mutating func withAbbreviatedRecord()

in Sources/TSCUtility/BitstreamReader.swift [155:219]


  mutating func withAbbreviatedRecord(
    _ abbrev: Bitstream.Abbreviation,
    body: (BitcodeElement.Record) throws -> Void
  ) throws {
    let code = try readSingleAbbreviatedRecordOperand(abbrev.operands.first!)

    let lastOperand = abbrev.operands.last!
    let lastRegularOperandIndex: Int = abbrev.operands.endIndex - (lastOperand.isPayload ? 1 : 0)

    // Safety: `lastRegularOperandIndex` is always at least 1. An abbreviation
    // is required by the format to contain at least one operand. If that last
    // operand is a payload (and thus we subtracted one from the total number of
    // operands above), then that must mean it is either a trailing array
    // or trailing blob. Both of these are preceded by their length field.
    let fields = UnsafeMutableBufferPointer<UInt64>.allocate(capacity: lastRegularOperandIndex - 1)
    defer { fields.deallocate() }

    for (idx, op) in abbrev.operands[1..<lastRegularOperandIndex].enumerated() {
      fields[idx] = try readSingleAbbreviatedRecordOperand(op)
    }

    let payload: BitcodeElement.Record.Payload
    if !lastOperand.isPayload {
      payload = .none
    } else {
      switch lastOperand {
      case .array(let element):
        let length = try cursor.readVBR(6)
        if case .char6 = element {
          // FIXME: Once the minimum deployment target bumps to macOS 11, use
          // the more ergonomic stdlib API everywhere.
          if #available(macOS 11.0, iOS 14.0, watchOS 7.0, tvOS 14.0, *) {
            payload = try .char6String(String(unsafeUninitializedCapacity: Int(length)) { buffer in
              for i in 0..<Int(length) {
                buffer[i] = try UInt8(readSingleAbbreviatedRecordOperand(element))
              }
              return Int(length)
            })
          } else {
            let buffer = UnsafeMutableBufferPointer<UInt8>.allocate(capacity: Int(length))
            defer { buffer.deallocate() }
            for i in 0..<Int(length) {
              buffer[i] = try UInt8(readSingleAbbreviatedRecordOperand(element))
            }
            payload = .char6String(String(decoding: buffer, as: UTF8.self))
          }
        } else {
          var elements = [UInt64]()
          for _ in 0..<length {
            elements.append(try readSingleAbbreviatedRecordOperand(element))
          }
          payload = .array(elements)
        }
      case .blob:
        let length = Int(try cursor.readVBR(6))
        try cursor.advance(toBitAlignment: 32)
        payload = .blob(try cursor.read(bytes: length))
        try cursor.advance(toBitAlignment: 32)
      default:
        fatalError()
      }
    }

    return try body(.init(id: code, fields: UnsafeBufferPointer(fields), payload: payload))
  }