blob: 5285cfe1dc49b814258ffeacd383b4f09ddc145d [file]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
import Foundation
private let fieldReferenceBytes = 4
@inline(__always)
private func fieldElementBytes<ElementCodec: FieldCodec>(_ codec: ElementCodec.Type) -> Int {
codec.isRefType ? fieldReferenceBytes : max(1, MemoryLayout<ElementCodec.Value>.stride)
}
@inline(__always)
private func serializerElementBytes<Element: Serializer>(_ type: Element.Type) -> Int {
type.isRefType ? fieldReferenceBytes : max(1, MemoryLayout<Element>.stride)
}
@inline(__always)
private func fieldOwnerBytes<T>(_ type: T.Type) -> Int {
max(1, MemoryLayout<T>.stride)
}
@inline(__always)
private func reserveFieldStorage(
_ context: ReadContext,
ownerBytes: Int,
count: Int,
elementBytes: Int
) throws {
if ownerBytes < 0 || count < 0 || elementBytes < 0 {
throw ForyError.invalidData("graph memory estimate overflows")
}
let (storageBytes, overflow) = count.multipliedReportingOverflow(by: elementBytes)
if overflow {
throw ForyError.invalidData("graph memory estimate overflows")
}
let (bytes, addOverflow) = ownerBytes.addingReportingOverflow(storageBytes)
if addOverflow {
throw ForyError.invalidData("graph memory estimate overflows")
}
try context.reserveGraphMemory(bytes)
}
@inline(__always)
private func reserveFieldArrayStorage<ElementCodec: FieldCodec>(
_ context: ReadContext,
_ codec: ElementCodec.Type,
ownerBytes: Int,
count: Int
) throws {
try reserveFieldStorage(
context, ownerBytes: ownerBytes, count: count, elementBytes: fieldElementBytes(codec))
}
@inline(__always)
private func reserveSerializerArrayMemory<Element: Serializer>(
_ context: ReadContext,
_ type: Element.Type,
ownerBytes: Int,
count: Int
) throws {
try reserveFieldStorage(
context, ownerBytes: ownerBytes, count: count, elementBytes: serializerElementBytes(type))
}
@inline(__always)
private func reserveFieldMapStorage<KeyCodec: FieldCodec, ValueCodec: FieldCodec>(
_ context: ReadContext,
key: KeyCodec.Type,
value: ValueCodec.Type,
ownerBytes: Int,
count: Int
) throws {
let keyBytes = fieldElementBytes(key)
let valueBytes = fieldElementBytes(value)
let (elementBytes, overflow) = keyBytes.addingReportingOverflow(valueBytes)
if overflow {
throw ForyError.invalidData("graph memory estimate overflows")
}
try reserveFieldStorage(context, ownerBytes: ownerBytes, count: count, elementBytes: elementBytes)
}
public protocol FieldCodec {
associatedtype Value
static var typeId: TypeId { get }
static var defaultValue: Value { get }
static var isNullableType: Bool { get }
static var isRefType: Bool { get }
static func isNone(_ value: Value) -> Bool
static func fieldType(nullable: Bool, trackRef: Bool) -> TypeMeta.FieldType
static func writePayload(_ value: Value, _ context: WriteContext) throws
static func readPayload(_ context: ReadContext) throws -> Value
static func writeStaticTypeInfo(_ context: WriteContext) throws
static func readTypeInfo(_ context: ReadContext) throws -> TypeInfo?
static func withTypeInfo<R>(
_ typeInfo: TypeInfo?, _ context: ReadContext, _ body: () throws -> R
)
rethrows -> R
static func readCompatibleField(
_ context: ReadContext,
remoteFieldType: TypeMeta.FieldType,
refMode: RefMode
) throws -> Value
}
extension FieldCodec {
public static var isNullableType: Bool { false }
public static var isRefType: Bool { false }
public static func isNone(_: Value) -> Bool { false }
public static func fieldType(nullable: Bool, trackRef: Bool) -> TypeMeta.FieldType {
TypeMeta.FieldType(typeID: typeId.rawValue, nullable: nullable, trackRef: trackRef)
}
public static func writeStaticTypeInfo(_ context: WriteContext) throws {
context.writeStaticTypeInfo(typeId)
}
public static func readTypeInfo(_ context: ReadContext) throws -> TypeInfo? {
try context.readStaticTypeInfo(typeId)
}
public static func withTypeInfo<R>(
_ typeInfo: TypeInfo?,
_ context: ReadContext,
_ body: () throws -> R
) rethrows -> R {
_ = typeInfo
_ = context
return try body()
}
public static func readCompatibleField(
_ context: ReadContext,
remoteFieldType: TypeMeta.FieldType,
refMode: RefMode
) throws -> Value {
try read(
context,
refMode: refMode,
readTypeInfo: TypeId.needsTypeInfoForField(
TypeId(rawValue: remoteFieldType.typeID) ?? .unknown)
)
}
public static func write(
_ value: Value,
_ context: WriteContext,
refMode: RefMode,
writeTypeInfo: Bool
) throws {
if refMode != .none {
if refMode == .tracking, isRefType, let object = value as AnyObject? {
if context.refWriter.tryWriteRef(buffer: context.buffer, object: object) {
return
}
} else {
context.buffer.writeInt8(RefFlag.notNullValue.rawValue)
}
}
if writeTypeInfo {
try writeStaticTypeInfo(context)
}
try writePayload(value, context)
}
public static func read(
_ context: ReadContext,
refMode: RefMode,
readTypeInfo: Bool
) throws -> Value {
switch refMode {
case .none:
if readTypeInfo {
let typeInfo = try Self.readTypeInfo(context)
return try withTypeInfo(typeInfo, context) {
try readPayload(context)
}
}
return try readPayload(context)
case .nullOnly:
let rawFlag = try context.buffer.readInt8()
switch rawFlag {
case RefFlag.null.rawValue:
return defaultValue
case RefFlag.notNullValue.rawValue:
if readTypeInfo {
let typeInfo = try Self.readTypeInfo(context)
return try withTypeInfo(typeInfo, context) {
try readPayload(context)
}
}
return try readPayload(context)
case RefFlag.refValue.rawValue:
if context.trackRef {
let reservedRefID = context.refReader.reserveRefID()
let value = try readPayloadAfterTypeInfo(context, readTypeInfo: readTypeInfo)
context.refReader.storeRef(value, at: reservedRefID)
return value
}
return try readPayloadAfterTypeInfo(context, readTypeInfo: readTypeInfo)
case RefFlag.ref.rawValue:
let refID = try context.buffer.readVarUInt32()
return try context.refReader.readRef(refID, as: Value.self)
default:
throw ForyError.refError("invalid ref flag \(rawFlag)")
}
case .tracking:
let rawFlag = try context.buffer.readInt8()
guard let flag = RefFlag(rawValue: rawFlag) else {
throw ForyError.refError("invalid ref flag \(rawFlag)")
}
switch flag {
case .null:
return defaultValue
case .ref:
let refID = try context.buffer.readVarUInt32()
return try context.refReader.readRef(refID, as: Value.self)
case .refValue:
let reservedRefID = context.trackRef ? context.refReader.reserveRefID() : nil
let value = try readPayloadAfterTypeInfo(context, readTypeInfo: readTypeInfo)
if let reservedRefID {
context.refReader.storeRef(value, at: reservedRefID)
}
return value
case .notNullValue:
return try readPayloadAfterTypeInfo(context, readTypeInfo: readTypeInfo)
}
}
}
private static func readPayloadAfterTypeInfo(
_ context: ReadContext,
readTypeInfo: Bool
) throws -> Value {
if readTypeInfo {
let typeInfo = try Self.readTypeInfo(context)
return try withTypeInfo(typeInfo, context) {
try readPayload(context)
}
}
return try readPayload(context)
}
}
private enum FieldCodecDefault {
static func readCompatibleField<Codec: FieldCodec>(
codec _: Codec.Type,
_ context: ReadContext,
remoteFieldType: TypeMeta.FieldType,
refMode: RefMode
) throws -> Codec.Value {
try Codec.read(
context,
refMode: refMode,
readTypeInfo: TypeId.needsTypeInfoForField(
TypeId(rawValue: remoteFieldType.typeID) ?? .unknown)
)
}
}
public enum SerializerCodec<T: Serializer>: FieldCodec {
public typealias Value = T
public static var typeId: TypeId { T.staticTypeId }
public static var defaultValue: T { T.foryDefault() }
public static var isNullableType: Bool { T.isNullableType }
public static var isRefType: Bool { T.isRefType }
public static func isNone(_ value: T) -> Bool {
value.foryIsNone
}
public static func fieldType(nullable: Bool, trackRef: Bool) -> TypeMeta.FieldType {
let fieldTypeID =
T.staticTypeId == .structType ? TypeId.compatibleStruct.rawValue : T.staticTypeId.rawValue
return TypeMeta.FieldType(typeID: fieldTypeID, nullable: nullable, trackRef: trackRef)
}
public static func writePayload(_ value: T, _ context: WriteContext) throws {
try value.foryWriteData(context, hasGenerics: false)
}
public static func readPayload(_ context: ReadContext) throws -> T {
try T.foryReadPayload(context, readTypeInfo: false)
}
public static func writeStaticTypeInfo(_ context: WriteContext) throws {
try T.foryWriteStaticTypeInfo(context)
}
public static func readTypeInfo(_ context: ReadContext) throws -> TypeInfo? {
try T.foryReadTypeInfo(context)
}
public static func withTypeInfo<R>(
_ typeInfo: TypeInfo?,
_ context: ReadContext,
_ body: () throws -> R
) rethrows -> R {
try context.withTypeInfo(typeInfo, for: T.self, body)
}
public static func write(
_ value: T,
_ context: WriteContext,
refMode: RefMode,
writeTypeInfo: Bool
) throws {
try value.foryWrite(context, refMode: refMode, writeTypeInfo: writeTypeInfo, hasGenerics: false)
}
public static func read(
_ context: ReadContext,
refMode: RefMode,
readTypeInfo: Bool
) throws -> T {
try T.foryRead(context, refMode: refMode, readTypeInfo: readTypeInfo)
}
}
public enum OptionalFieldCodec<WrappedCodec: FieldCodec>: FieldCodec {
public typealias Value = WrappedCodec.Value?
public static var typeId: TypeId { WrappedCodec.typeId }
public static var defaultValue: Value { nil }
public static var isNullableType: Bool { true }
public static var isRefType: Bool { WrappedCodec.isRefType }
public static func isNone(_ value: Value) -> Bool {
value == nil
}
public static func fieldType(nullable: Bool, trackRef: Bool) -> TypeMeta.FieldType {
WrappedCodec.fieldType(nullable: nullable, trackRef: trackRef)
}
public static func writePayload(_ value: Value, _ context: WriteContext) throws {
guard let value else {
throw ForyError.invalidData("Option.none cannot write raw payload")
}
try WrappedCodec.writePayload(value, context)
}
public static func readPayload(_ context: ReadContext) throws -> Value {
try WrappedCodec.readPayload(context)
}
public static func writeStaticTypeInfo(_ context: WriteContext) throws {
try WrappedCodec.writeStaticTypeInfo(context)
}
public static func readTypeInfo(_ context: ReadContext) throws -> TypeInfo? {
try WrappedCodec.readTypeInfo(context)
}
public static func withTypeInfo<R>(
_ typeInfo: TypeInfo?,
_ context: ReadContext,
_ body: () throws -> R
) rethrows -> R {
try WrappedCodec.withTypeInfo(typeInfo, context, body)
}
public static func write(
_ value: Value,
_ context: WriteContext,
refMode: RefMode,
writeTypeInfo: Bool
) throws {
switch refMode {
case .none:
guard let value else {
throw ForyError.invalidData("Option.none with RefMode.none")
}
try WrappedCodec.write(value, context, refMode: .none, writeTypeInfo: writeTypeInfo)
case .nullOnly:
guard let value else {
context.buffer.writeInt8(RefFlag.null.rawValue)
return
}
context.buffer.writeInt8(RefFlag.notNullValue.rawValue)
try WrappedCodec.write(value, context, refMode: .none, writeTypeInfo: writeTypeInfo)
case .tracking:
guard let value else {
context.buffer.writeInt8(RefFlag.null.rawValue)
return
}
try WrappedCodec.write(value, context, refMode: .tracking, writeTypeInfo: writeTypeInfo)
}
}
public static func read(
_ context: ReadContext,
refMode: RefMode,
readTypeInfo: Bool
) throws -> Value {
switch refMode {
case .none:
return try WrappedCodec.read(context, refMode: .none, readTypeInfo: readTypeInfo)
case .nullOnly:
let refFlag = try context.buffer.readInt8()
if refFlag == RefFlag.null.rawValue {
return nil
}
return try WrappedCodec.read(context, refMode: .none, readTypeInfo: readTypeInfo)
case .tracking:
let refFlag = try context.buffer.readInt8()
if refFlag == RefFlag.null.rawValue {
return nil
}
context.buffer.moveBack(1)
return try WrappedCodec.read(context, refMode: .tracking, readTypeInfo: readTypeInfo)
}
}
}
public enum BoolCodec: FieldCodec {
public static let typeId: TypeId = .bool
public static let defaultValue = false
public static func writePayload(_ value: Bool, _ context: WriteContext) {
context.buffer.writeUInt8(value ? 1 : 0)
}
public static func readPayload(_ context: ReadContext) throws -> Bool {
try context.buffer.readUInt8() != 0
}
}
public enum Int8Codec: FieldCodec {
public static let typeId: TypeId = .int8
public static let defaultValue = Int8(0)
public static func writePayload(_ value: Int8, _ context: WriteContext) {
context.buffer.writeInt8(value)
}
public static func readPayload(_ context: ReadContext) throws -> Int8 {
try context.buffer.readInt8()
}
}
public enum Int16Codec: FieldCodec {
public static let typeId: TypeId = .int16
public static let defaultValue = Int16(0)
public static func writePayload(_ value: Int16, _ context: WriteContext) {
context.buffer.writeInt16(value)
}
public static func readPayload(_ context: ReadContext) throws -> Int16 {
try context.buffer.readInt16()
}
}
public enum Int32VarintCodec: FieldCodec {
public static let typeId: TypeId = .varint32
public static let defaultValue = Int32(0)
public static func writePayload(_ value: Int32, _ context: WriteContext) {
context.buffer.writeVarInt32(value)
}
public static func readPayload(_ context: ReadContext) throws -> Int32 {
try context.buffer.readVarInt32()
}
}
public enum Int32FixedCodec: FieldCodec {
public static let typeId: TypeId = .int32
public static let defaultValue = Int32(0)
public static func writePayload(_ value: Int32, _ context: WriteContext) {
context.buffer.writeInt32(value)
}
public static func readPayload(_ context: ReadContext) throws -> Int32 {
try context.buffer.readInt32()
}
}
public enum Int64VarintCodec: FieldCodec {
public static let typeId: TypeId = .varint64
public static let defaultValue = Int64(0)
public static func writePayload(_ value: Int64, _ context: WriteContext) {
context.buffer.writeVarInt64(value)
}
public static func readPayload(_ context: ReadContext) throws -> Int64 {
try context.buffer.readVarInt64()
}
}
public enum Int64FixedCodec: FieldCodec {
public static let typeId: TypeId = .int64
public static let defaultValue = Int64(0)
public static func writePayload(_ value: Int64, _ context: WriteContext) {
context.buffer.writeInt64(value)
}
public static func readPayload(_ context: ReadContext) throws -> Int64 {
try context.buffer.readInt64()
}
}
public enum Int64TaggedCodec: FieldCodec {
public static let typeId: TypeId = .taggedInt64
public static let defaultValue = Int64(0)
public static func writePayload(_ value: Int64, _ context: WriteContext) {
context.buffer.writeTaggedInt64(value)
}
public static func readPayload(_ context: ReadContext) throws -> Int64 {
try context.buffer.readTaggedInt64()
}
}
public enum UInt8Codec: FieldCodec {
public static let typeId: TypeId = .uint8
public static let defaultValue = UInt8(0)
public static func writePayload(_ value: UInt8, _ context: WriteContext) {
context.buffer.writeUInt8(value)
}
public static func readPayload(_ context: ReadContext) throws -> UInt8 {
try context.buffer.readUInt8()
}
}
public enum UInt16Codec: FieldCodec {
public static let typeId: TypeId = .uint16
public static let defaultValue = UInt16(0)
public static func writePayload(_ value: UInt16, _ context: WriteContext) {
context.buffer.writeUInt16(value)
}
public static func readPayload(_ context: ReadContext) throws -> UInt16 {
try context.buffer.readUInt16()
}
}
public enum UInt32VarintCodec: FieldCodec {
public static let typeId: TypeId = .varUInt32
public static let defaultValue = UInt32(0)
public static func writePayload(_ value: UInt32, _ context: WriteContext) {
context.buffer.writeVarUInt32(value)
}
public static func readPayload(_ context: ReadContext) throws -> UInt32 {
try context.buffer.readVarUInt32()
}
}
public enum UInt32FixedCodec: FieldCodec {
public static let typeId: TypeId = .uint32
public static let defaultValue = UInt32(0)
public static func writePayload(_ value: UInt32, _ context: WriteContext) {
context.buffer.writeUInt32(value)
}
public static func readPayload(_ context: ReadContext) throws -> UInt32 {
try context.buffer.readUInt32()
}
}
public enum UInt64VarintCodec: FieldCodec {
public static let typeId: TypeId = .varUInt64
public static let defaultValue = UInt64(0)
public static func writePayload(_ value: UInt64, _ context: WriteContext) {
context.buffer.writeVarUInt64(value)
}
public static func readPayload(_ context: ReadContext) throws -> UInt64 {
try context.buffer.readVarUInt64()
}
}
public enum UInt64FixedCodec: FieldCodec {
public static let typeId: TypeId = .uint64
public static let defaultValue = UInt64(0)
public static func writePayload(_ value: UInt64, _ context: WriteContext) {
context.buffer.writeUInt64(value)
}
public static func readPayload(_ context: ReadContext) throws -> UInt64 {
try context.buffer.readUInt64()
}
}
public enum UInt64TaggedCodec: FieldCodec {
public static let typeId: TypeId = .taggedUInt64
public static let defaultValue = UInt64(0)
public static func writePayload(_ value: UInt64, _ context: WriteContext) {
context.buffer.writeTaggedUInt64(value)
}
public static func readPayload(_ context: ReadContext) throws -> UInt64 {
try context.buffer.readTaggedUInt64()
}
}
public enum IntVarintCodec: FieldCodec {
public static let typeId: TypeId = .varint64
public static let defaultValue = Int(0)
public static func writePayload(_ value: Int, _ context: WriteContext) {
context.buffer.writeVarInt64(Int64(value))
}
public static func readPayload(_ context: ReadContext) throws -> Int {
Int(try context.buffer.readVarInt64())
}
}
public enum IntFixedCodec: FieldCodec {
public static let typeId: TypeId = .int64
public static let defaultValue = Int(0)
public static func writePayload(_ value: Int, _ context: WriteContext) {
context.buffer.writeInt64(Int64(value))
}
public static func readPayload(_ context: ReadContext) throws -> Int {
Int(try context.buffer.readInt64())
}
}
public enum IntTaggedCodec: FieldCodec {
public static let typeId: TypeId = .taggedInt64
public static let defaultValue = Int(0)
public static func writePayload(_ value: Int, _ context: WriteContext) {
context.buffer.writeTaggedInt64(Int64(value))
}
public static func readPayload(_ context: ReadContext) throws -> Int {
Int(try context.buffer.readTaggedInt64())
}
}
public enum UIntVarintCodec: FieldCodec {
public static let typeId: TypeId = .varUInt64
public static let defaultValue = UInt(0)
public static func writePayload(_ value: UInt, _ context: WriteContext) {
context.buffer.writeVarUInt64(UInt64(value))
}
public static func readPayload(_ context: ReadContext) throws -> UInt {
UInt(try context.buffer.readVarUInt64())
}
}
public enum UIntFixedCodec: FieldCodec {
public static let typeId: TypeId = .uint64
public static let defaultValue = UInt(0)
public static func writePayload(_ value: UInt, _ context: WriteContext) {
context.buffer.writeUInt64(UInt64(value))
}
public static func readPayload(_ context: ReadContext) throws -> UInt {
UInt(try context.buffer.readUInt64())
}
}
public enum UIntTaggedCodec: FieldCodec {
public static let typeId: TypeId = .taggedUInt64
public static let defaultValue = UInt(0)
public static func writePayload(_ value: UInt, _ context: WriteContext) {
context.buffer.writeTaggedUInt64(UInt64(value))
}
public static func readPayload(_ context: ReadContext) throws -> UInt {
UInt(try context.buffer.readTaggedUInt64())
}
}
public enum Float16Codec: FieldCodec {
public static let typeId: TypeId = .float16
public static let defaultValue = Float16(0)
public static func writePayload(_ value: Float16, _ context: WriteContext) {
context.buffer.writeUInt16(value.bitPattern)
}
public static func readPayload(_ context: ReadContext) throws -> Float16 {
Float16(bitPattern: try context.buffer.readUInt16())
}
}
public enum BFloat16Codec: FieldCodec {
public static let typeId: TypeId = .bfloat16
public static let defaultValue = BFloat16()
public static func writePayload(_ value: BFloat16, _ context: WriteContext) {
context.buffer.writeUInt16(value.rawValue)
}
public static func readPayload(_ context: ReadContext) throws -> BFloat16 {
BFloat16(rawValue: try context.buffer.readUInt16())
}
}
public enum FloatCodec: FieldCodec {
public static let typeId: TypeId = .float32
public static let defaultValue = Float(0)
public static func writePayload(_ value: Float, _ context: WriteContext) {
context.buffer.writeFloat32(value)
}
public static func readPayload(_ context: ReadContext) throws -> Float {
try context.buffer.readFloat32()
}
}
public enum DoubleCodec: FieldCodec {
public static let typeId: TypeId = .float64
public static let defaultValue = Double(0)
public static func writePayload(_ value: Double, _ context: WriteContext) {
context.buffer.writeFloat64(value)
}
public static func readPayload(_ context: ReadContext) throws -> Double {
try context.buffer.readFloat64()
}
}
public typealias StringCodec = SerializerCodec<String>
public typealias DurationCodec = SerializerCodec<Duration>
public typealias TimestampCodec = SerializerCodec<Date>
public typealias LocalDateCodec = SerializerCodec<LocalDate>
public typealias DecimalCodec = SerializerCodec<Decimal>
public typealias DataCodec = SerializerCodec<Data>
public enum ListFieldCodec<ElementCodec: FieldCodec>: FieldCodec {
public typealias Value = [ElementCodec.Value]
public static var typeId: TypeId { .list }
public static var defaultValue: Value { [] }
public static func fieldType(nullable: Bool, trackRef: Bool) -> TypeMeta.FieldType {
return TypeMeta.FieldType(
typeID: TypeId.list.rawValue,
nullable: nullable,
trackRef: trackRef,
generics: [
ElementCodec.fieldType(
nullable: ElementCodec.isNullableType,
trackRef: trackRef && ElementCodec.isRefType)
]
)
}
public static func writePayload(_ value: Value, _ context: WriteContext) throws {
try writeCollectionPayload(value, context, elementCodec: ElementCodec.self)
}
public static func readPayload(_ context: ReadContext) throws -> Value {
return try readCollectionPayload(
context,
elementCodec: ElementCodec.self,
ownerBytes: fieldOwnerBytes([ElementCodec.Value].self)
)
}
public static func readCompatibleField(
_ context: ReadContext,
remoteFieldType: TypeMeta.FieldType,
refMode: RefMode
) throws -> Value {
if isCompatiblePackedArrayTypeID(remoteFieldType.typeID, elementCodec: ElementCodec.self) {
return try readCompatiblePackedArrayField(
context, refMode: refMode, elementCodec: ElementCodec.self)
}
return try FieldCodecDefault.readCompatibleField(
codec: Self.self,
context,
remoteFieldType: remoteFieldType,
refMode: refMode
)
}
}
public enum ArrayFieldCodec<ElementCodec: FieldCodec>: FieldCodec {
public typealias Value = [ElementCodec.Value]
public static var typeId: TypeId {
guard let typeID = packedArrayTypeID(for: ElementCodec.self) else {
preconditionFailure("ArrayFieldCodec requires a non-null numeric or bool element codec")
}
return typeID
}
public static var defaultValue: Value { [] }
public static func fieldType(nullable: Bool, trackRef: Bool) -> TypeMeta.FieldType {
TypeMeta.FieldType(typeID: typeId.rawValue, nullable: nullable, trackRef: trackRef)
}
public static func writePayload(_ value: Value, _ context: WriteContext) throws {
if try writePackedArrayPayload(value, context, elementCodec: ElementCodec.self) {
return
}
throw ForyError.invalidData("unsupported array field element codec \(ElementCodec.self)")
}
public static func readPayload(_ context: ReadContext) throws -> Value {
if let value = try readPackedArrayPayload(context, elementCodec: ElementCodec.self) {
return value
}
throw ForyError.invalidData("unsupported array field element codec \(ElementCodec.self)")
}
public static func readCompatibleField(
_ context: ReadContext,
remoteFieldType: TypeMeta.FieldType,
refMode: RefMode
) throws -> Value {
if remoteFieldType.typeID == TypeId.list.rawValue,
let element = remoteFieldType.generics.first,
let localArrayTypeID = packedArrayTypeID(for: ElementCodec.self),
TypeId.listElementTypeID(element.typeID, matchesDenseArrayTypeID: localArrayTypeID.rawValue)
{
return try readListPayloadAsArray(
context,
refMode: refMode,
elementCodec: ElementCodec.self,
remoteElementTypeID: element.typeID
)
}
return try FieldCodecDefault.readCompatibleField(
codec: Self.self,
context,
remoteFieldType: remoteFieldType,
refMode: refMode
)
}
public static func write(
_ value: Value,
_ context: WriteContext,
refMode: RefMode,
writeTypeInfo: Bool
) throws {
if refMode == .none, !writeTypeInfo {
try writePayload(value, context)
return
}
if refMode != .none {
context.buffer.writeInt8(RefFlag.notNullValue.rawValue)
}
if writeTypeInfo {
try writeStaticTypeInfo(context)
}
try writePayload(value, context)
}
public static func read(
_ context: ReadContext,
refMode: RefMode,
readTypeInfo: Bool
) throws -> Value {
switch refMode {
case .none:
return try readPayloadAfterTypeInfo(context, readTypeInfo: readTypeInfo)
case .nullOnly:
let rawFlag = try context.buffer.readInt8()
switch rawFlag {
case RefFlag.null.rawValue:
return defaultValue
case RefFlag.notNullValue.rawValue:
return try readPayloadAfterTypeInfo(context, readTypeInfo: readTypeInfo)
case RefFlag.refValue.rawValue:
if context.trackRef {
let reservedRefID = context.refReader.reserveRefID()
let value = try readPayloadAfterTypeInfo(context, readTypeInfo: readTypeInfo)
context.refReader.storeRef(value, at: reservedRefID)
return value
}
return try readPayloadAfterTypeInfo(context, readTypeInfo: readTypeInfo)
case RefFlag.ref.rawValue:
let refID = try context.buffer.readVarUInt32()
return try context.refReader.readRef(refID, as: Value.self)
default:
throw ForyError.refError("invalid ref flag \(rawFlag)")
}
case .tracking:
let rawFlag = try context.buffer.readInt8()
guard let flag = RefFlag(rawValue: rawFlag) else {
throw ForyError.refError("invalid ref flag \(rawFlag)")
}
switch flag {
case .null:
return defaultValue
case .ref:
let refID = try context.buffer.readVarUInt32()
return try context.refReader.readRef(refID, as: Value.self)
case .refValue:
let reservedRefID = context.trackRef ? context.refReader.reserveRefID() : nil
let value = try readPayloadAfterTypeInfo(context, readTypeInfo: readTypeInfo)
if let reservedRefID {
context.refReader.storeRef(value, at: reservedRefID)
}
return value
case .notNullValue:
return try readPayloadAfterTypeInfo(context, readTypeInfo: readTypeInfo)
}
}
}
private static func readPayloadAfterTypeInfo(
_ context: ReadContext,
readTypeInfo: Bool
) throws -> Value {
if readTypeInfo {
let typeInfo = try Self.readTypeInfo(context)
return try withTypeInfo(typeInfo, context) {
try readPayload(context)
}
}
return try readPayload(context)
}
}
public enum SetFieldCodec<ElementCodec: FieldCodec>: FieldCodec where ElementCodec.Value: Hashable {
public typealias Value = Set<ElementCodec.Value>
public static var typeId: TypeId { .set }
public static var defaultValue: Value { [] }
public static func fieldType(nullable: Bool, trackRef: Bool) -> TypeMeta.FieldType {
TypeMeta.FieldType(
typeID: TypeId.set.rawValue,
nullable: nullable,
trackRef: trackRef,
generics: [
ElementCodec.fieldType(
nullable: ElementCodec.isNullableType,
trackRef: trackRef && ElementCodec.isRefType)
]
)
}
public static func writePayload(_ value: Value, _ context: WriteContext) throws {
try writeCollectionPayload(Array(value), context, elementCodec: ElementCodec.self)
}
public static func readPayload(_ context: ReadContext) throws -> Value {
let values = try readCollectionPayload(
context,
elementCodec: ElementCodec.self,
ownerBytes: 0
)
try reserveFieldArrayStorage(
context,
ElementCodec.self,
ownerBytes: fieldOwnerBytes(Set<ElementCodec.Value>.self),
count: values.count
)
return Set(values)
}
}
public enum MapFieldCodec<KeyCodec: FieldCodec, ValueCodec: FieldCodec>: FieldCodec
where KeyCodec.Value: Hashable {
public typealias Value = [KeyCodec.Value: ValueCodec.Value]
private struct MapEntryWriteOptions {
var trackKeyRef: Bool
var trackValueRef: Bool
var keyDeclared: Bool
var valueDeclared: Bool
var keyDynamicType: Bool
var valueDynamicType: Bool
var keyIsNil: Bool
var valueIsNil: Bool
}
public static var typeId: TypeId { .map }
public static var defaultValue: Value { [:] }
public static func fieldType(nullable: Bool, trackRef: Bool) -> TypeMeta.FieldType {
TypeMeta.FieldType(
typeID: TypeId.map.rawValue,
nullable: nullable,
trackRef: trackRef,
generics: [
KeyCodec.fieldType(
nullable: KeyCodec.isNullableType,
trackRef: trackRef && KeyCodec.isRefType),
ValueCodec.fieldType(
nullable: ValueCodec.isNullableType,
trackRef: trackRef && ValueCodec.isRefType)
]
)
}
public static func writePayload(_ value: Value, _ context: WriteContext) throws {
context.buffer.writeVarUInt32(UInt32(value.count))
if value.isEmpty {
return
}
let trackKeyRef = context.trackRef && KeyCodec.isRefType
let trackValueRef = context.trackRef && ValueCodec.isRefType
let keyDeclared = !TypeId.needsTypeInfoForField(KeyCodec.typeId)
let valueDeclared = !TypeId.needsTypeInfoForField(ValueCodec.typeId)
let keyDynamicType = KeyCodec.typeId == .unknown
let valueDynamicType = ValueCodec.typeId == .unknown
let commonOptions = MapEntryWriteOptions(
trackKeyRef: trackKeyRef,
trackValueRef: trackValueRef,
keyDeclared: keyDeclared,
valueDeclared: valueDeclared,
keyDynamicType: keyDynamicType,
valueDynamicType: valueDynamicType,
keyIsNil: false,
valueIsNil: false
)
var iterator = value.makeIterator()
var pendingPair = iterator.next()
while let pair = pendingPair {
let keyIsNil = KeyCodec.isNone(pair.key)
let valueIsNil = ValueCodec.isNone(pair.value)
if keyDynamicType || valueDynamicType || keyIsNil || valueIsNil {
var options = commonOptions
options.keyIsNil = keyIsNil
options.valueIsNil = valueIsNil
try writeMapEntry(
pair,
context,
options: options
)
pendingPair = iterator.next()
continue
}
var header: UInt8 = 0
if trackKeyRef { header |= MapHeader.trackingKeyRef }
if trackValueRef { header |= MapHeader.trackingValueRef }
if keyDeclared { header |= MapHeader.declaredKeyType }
if valueDeclared { header |= MapHeader.declaredValueType }
context.buffer.writeUInt8(header)
let chunkSizeOffset = context.buffer.count
context.buffer.writeUInt8(0)
if !keyDeclared {
try KeyCodec.writeStaticTypeInfo(context)
}
if !valueDeclared {
try ValueCodec.writeStaticTypeInfo(context)
}
var chunkSize: UInt8 = 0
while chunkSize < UInt8.max, let current = pendingPair {
if KeyCodec.isNone(current.key) || ValueCodec.isNone(current.value) {
break
}
try writeMapPayload(
current,
context,
trackKeyRef: trackKeyRef,
trackValueRef: trackValueRef
)
chunkSize &+= 1
pendingPair = iterator.next()
}
context.buffer.setByte(at: chunkSizeOffset, to: chunkSize)
}
}
public static func readPayload(_ context: ReadContext) throws -> Value {
let totalLength = Int(try context.buffer.readVarUInt32())
try context.ensureCollectionLength(totalLength, label: "map")
let ownerBytes = fieldOwnerBytes(Dictionary<KeyCodec.Value, ValueCodec.Value>.self)
if totalLength == 0 {
try reserveFieldMapStorage(
context, key: KeyCodec.self, value: ValueCodec.self, ownerBytes: ownerBytes,
count: totalLength)
return [:]
}
try reserveFieldMapStorage(
context, key: KeyCodec.self, value: ValueCodec.self, ownerBytes: ownerBytes,
count: totalLength)
try context.ensureRemainingBytes(totalLength, label: "map")
var map: Value = [:]
map.reserveCapacity(totalLength)
var readCount = 0
while readCount < totalLength {
let header = try context.buffer.readUInt8()
// IMPORTANT: map readers must obey the sender-written key/value ref
// bits in this header. Local Swift field metadata must not
// override that decision while reading. Shared xlang tests
// intentionally deserialize one ref policy and then serialize
// another local payload. DO NOT REMOVE this comment.
let trackKeyRef = (header & MapHeader.trackingKeyRef) != 0
let keyNull = (header & MapHeader.keyNull) != 0
let keyDeclared = (header & MapHeader.declaredKeyType) != 0
let trackValueRef = (header & MapHeader.trackingValueRef) != 0
let valueNull = (header & MapHeader.valueNull) != 0
let valueDeclared = (header & MapHeader.declaredValueType) != 0
if keyNull && valueNull {
map[KeyCodec.defaultValue] = ValueCodec.defaultValue
readCount += 1
continue
}
if keyNull {
let value = try readMapValue(
context,
declared: valueDeclared,
trackRef: trackValueRef
)
map[KeyCodec.defaultValue] = value
readCount += 1
continue
}
if valueNull {
let key = try readMapKey(
context,
declared: keyDeclared,
trackRef: trackKeyRef
)
map[key] = ValueCodec.defaultValue
readCount += 1
continue
}
let chunkSize = Int(try context.buffer.readUInt8())
if chunkSize > (totalLength - readCount) {
throw ForyError.invalidData("map chunk size exceeds remaining entries")
}
let keyTypeInfo = keyDeclared ? nil : try KeyCodec.readTypeInfo(context)
let valueTypeInfo = valueDeclared ? nil : try ValueCodec.readTypeInfo(context)
for _ in 0..<chunkSize {
let key = try KeyCodec.withTypeInfo(keyTypeInfo, context) {
try KeyCodec.read(
context,
refMode: trackKeyRef ? .tracking : .none,
readTypeInfo: false
)
}
let value = try ValueCodec.withTypeInfo(valueTypeInfo, context) {
try ValueCodec.read(
context,
refMode: trackValueRef ? .tracking : .none,
readTypeInfo: false
)
}
map[key] = value
}
readCount += chunkSize
}
return map
}
private static func writeMapEntry(
_ pair: Dictionary<KeyCodec.Value, ValueCodec.Value>.Element,
_ context: WriteContext,
options: MapEntryWriteOptions
) throws {
var header: UInt8 = 0
if options.trackKeyRef { header |= MapHeader.trackingKeyRef }
if options.trackValueRef { header |= MapHeader.trackingValueRef }
if options.keyIsNil {
header |= MapHeader.keyNull
} else if !options.keyDynamicType && options.keyDeclared {
header |= MapHeader.declaredKeyType
}
if options.valueIsNil {
header |= MapHeader.valueNull
} else if !options.valueDynamicType && options.valueDeclared {
header |= MapHeader.declaredValueType
}
context.buffer.writeUInt8(header)
if !options.keyIsNil {
if !options.keyDeclared {
try KeyCodec.writeStaticTypeInfo(context)
}
try KeyCodec.write(
pair.key,
context,
refMode: options.trackKeyRef ? .tracking : .none,
writeTypeInfo: false
)
}
if !options.valueIsNil {
if !options.valueDeclared {
try ValueCodec.writeStaticTypeInfo(context)
}
try ValueCodec.write(
pair.value,
context,
refMode: options.trackValueRef ? .tracking : .none,
writeTypeInfo: false
)
}
}
private static func writeMapPayload(
_ pair: Dictionary<KeyCodec.Value, ValueCodec.Value>.Element,
_ context: WriteContext,
trackKeyRef: Bool,
trackValueRef: Bool
) throws {
try KeyCodec.write(
pair.key,
context,
refMode: trackKeyRef ? .tracking : .none,
writeTypeInfo: false
)
try ValueCodec.write(
pair.value,
context,
refMode: trackValueRef ? .tracking : .none,
writeTypeInfo: false
)
}
private static func readMapKey(
_ context: ReadContext,
declared: Bool,
trackRef: Bool
) throws -> KeyCodec.Value {
let typeInfo = declared ? nil : try KeyCodec.readTypeInfo(context)
return try KeyCodec.withTypeInfo(typeInfo, context) {
try KeyCodec.read(context, refMode: trackRef ? .tracking : .none, readTypeInfo: false)
}
}
private static func readMapValue(
_ context: ReadContext,
declared: Bool,
trackRef: Bool
) throws -> ValueCodec.Value {
let typeInfo = declared ? nil : try ValueCodec.readTypeInfo(context)
return try ValueCodec.withTypeInfo(typeInfo, context) {
try ValueCodec.read(context, refMode: trackRef ? .tracking : .none, readTypeInfo: false)
}
}
}
@inline(__always)
private func uncheckedPackedArrayCast<From, To>(_ array: [From], to _: To.Type) -> [To] {
assert(From.self == To.self)
return unsafeBitCast(array, to: [To].self)
}
@inline(__always)
private func uncheckedScalarCast<From, To>(_ value: From, to _: To.Type) -> To {
assert(From.self == To.self)
return unsafeBitCast(value, to: To.self)
}
private func packedArrayTypeID<ElementCodec: FieldCodec>(for _: ElementCodec.Type) -> TypeId? {
if ElementCodec.isNullableType {
return nil
}
if ElementCodec.self == BoolCodec.self {
return .boolArray
}
if ElementCodec.self == Int8Codec.self {
return .int8Array
}
if ElementCodec.self == Int16Codec.self {
return .int16Array
}
if ElementCodec.self == Int32FixedCodec.self {
return .int32Array
}
if ElementCodec.self == Int64FixedCodec.self || ElementCodec.self == IntFixedCodec.self {
return .int64Array
}
if ElementCodec.self == UInt8Codec.self {
return .uint8Array
}
if ElementCodec.self == UInt16Codec.self {
return .uint16Array
}
if ElementCodec.self == UInt32FixedCodec.self {
return .uint32Array
}
if ElementCodec.self == UInt64FixedCodec.self || ElementCodec.self == UIntFixedCodec.self {
return .uint64Array
}
if ElementCodec.self == Float16Codec.self {
return .float16Array
}
if ElementCodec.self == BFloat16Codec.self {
return .bfloat16Array
}
if ElementCodec.self == FloatCodec.self {
return .float32Array
}
if ElementCodec.self == DoubleCodec.self {
return .float64Array
}
return nil
}
private func isCompatiblePackedArrayTypeID<ElementCodec: FieldCodec>(
_ typeID: UInt32,
elementCodec _: ElementCodec.Type
) -> Bool {
TypeId.listElementTypeID(ElementCodec.typeId.rawValue, matchesDenseArrayTypeID: typeID)
}
private func writePackedArrayPayload<ElementCodec: FieldCodec>(
_ value: [ElementCodec.Value],
_ context: WriteContext,
elementCodec _: ElementCodec.Type
) throws -> Bool {
if ElementCodec.self == BoolCodec.self {
writePrimitiveArray(uncheckedPackedArrayCast(value, to: Bool.self), context: context)
return true
}
if ElementCodec.self == Int8Codec.self {
writePrimitiveArray(uncheckedPackedArrayCast(value, to: Int8.self), context: context)
return true
}
if ElementCodec.self == Int16Codec.self {
writePrimitiveArray(uncheckedPackedArrayCast(value, to: Int16.self), context: context)
return true
}
if ElementCodec.self == Int32FixedCodec.self {
writePrimitiveArray(uncheckedPackedArrayCast(value, to: Int32.self), context: context)
return true
}
if ElementCodec.self == Int64FixedCodec.self {
writePrimitiveArray(uncheckedPackedArrayCast(value, to: Int64.self), context: context)
return true
}
if ElementCodec.self == IntFixedCodec.self {
writeIntArrayPayload(uncheckedPackedArrayCast(value, to: Int.self), context)
return true
}
if ElementCodec.self == UInt8Codec.self {
writePrimitiveArray(uncheckedPackedArrayCast(value, to: UInt8.self), context: context)
return true
}
if ElementCodec.self == UInt16Codec.self {
writePrimitiveArray(uncheckedPackedArrayCast(value, to: UInt16.self), context: context)
return true
}
if ElementCodec.self == UInt32FixedCodec.self {
writePrimitiveArray(uncheckedPackedArrayCast(value, to: UInt32.self), context: context)
return true
}
if ElementCodec.self == UInt64FixedCodec.self {
writePrimitiveArray(uncheckedPackedArrayCast(value, to: UInt64.self), context: context)
return true
}
if ElementCodec.self == UIntFixedCodec.self {
writeUIntArrayPayload(uncheckedPackedArrayCast(value, to: UInt.self), context)
return true
}
if ElementCodec.self == Float16Codec.self {
writePrimitiveArray(uncheckedPackedArrayCast(value, to: Float16.self), context: context)
return true
}
if ElementCodec.self == BFloat16Codec.self {
writePrimitiveArray(uncheckedPackedArrayCast(value, to: BFloat16.self), context: context)
return true
}
if ElementCodec.self == FloatCodec.self {
writePrimitiveArray(uncheckedPackedArrayCast(value, to: Float.self), context: context)
return true
}
if ElementCodec.self == DoubleCodec.self {
writePrimitiveArray(uncheckedPackedArrayCast(value, to: Double.self), context: context)
return true
}
return false
}
private func readPackedArrayPayload<ElementCodec: FieldCodec>(
_ context: ReadContext,
elementCodec _: ElementCodec.Type
) throws -> [ElementCodec.Value]? {
if ElementCodec.self == BoolCodec.self {
return uncheckedPackedArrayCast(
try readPrimitiveArray(context) as [Bool], to: ElementCodec.Value.self)
}
if ElementCodec.self == Int8Codec.self {
return uncheckedPackedArrayCast(
try readPrimitiveArray(context) as [Int8], to: ElementCodec.Value.self)
}
if ElementCodec.self == Int16Codec.self {
return uncheckedPackedArrayCast(
try readPrimitiveArray(context) as [Int16], to: ElementCodec.Value.self)
}
if ElementCodec.self == Int32FixedCodec.self {
return uncheckedPackedArrayCast(
try readPrimitiveArray(context) as [Int32], to: ElementCodec.Value.self)
}
if ElementCodec.self == Int64FixedCodec.self {
return uncheckedPackedArrayCast(
try readPrimitiveArray(context) as [Int64], to: ElementCodec.Value.self)
}
if ElementCodec.self == IntFixedCodec.self {
return uncheckedPackedArrayCast(try readIntArrayPayload(context), to: ElementCodec.Value.self)
}
if ElementCodec.self == UInt8Codec.self {
return uncheckedPackedArrayCast(
try readPrimitiveArray(context) as [UInt8], to: ElementCodec.Value.self)
}
if ElementCodec.self == UInt16Codec.self {
return uncheckedPackedArrayCast(
try readPrimitiveArray(context) as [UInt16], to: ElementCodec.Value.self)
}
if ElementCodec.self == UInt32FixedCodec.self {
return uncheckedPackedArrayCast(
try readPrimitiveArray(context) as [UInt32], to: ElementCodec.Value.self)
}
if ElementCodec.self == UInt64FixedCodec.self {
return uncheckedPackedArrayCast(
try readPrimitiveArray(context) as [UInt64], to: ElementCodec.Value.self)
}
if ElementCodec.self == UIntFixedCodec.self {
return uncheckedPackedArrayCast(try readUIntArrayPayload(context), to: ElementCodec.Value.self)
}
if ElementCodec.self == Float16Codec.self {
return uncheckedPackedArrayCast(
try readPrimitiveArray(context) as [Float16], to: ElementCodec.Value.self)
}
if ElementCodec.self == BFloat16Codec.self {
return uncheckedPackedArrayCast(
try readPrimitiveArray(context) as [BFloat16], to: ElementCodec.Value.self)
}
if ElementCodec.self == FloatCodec.self {
return uncheckedPackedArrayCast(
try readPrimitiveArray(context) as [Float], to: ElementCodec.Value.self)
}
if ElementCodec.self == DoubleCodec.self {
return uncheckedPackedArrayCast(
try readPrimitiveArray(context) as [Double], to: ElementCodec.Value.self)
}
return nil
}
private func writeIntArrayPayload(_ value: [Int], _ context: WriteContext) {
context.buffer.writeVarUInt32(UInt32(value.count * 8))
for item in value {
context.buffer.writeInt64(Int64(item))
}
}
private func writeUIntArrayPayload(_ value: [UInt], _ context: WriteContext) {
context.buffer.writeVarUInt32(UInt32(value.count * 8))
for item in value {
context.buffer.writeUInt64(UInt64(item))
}
}
private func readIntArrayPayload(
_ context: ReadContext, reserveGraphStorage: Bool = false
) throws
-> [Int]
{
let count = try readPackedArrayElementCount(context, width: 8, label: "int64_array")
if reserveGraphStorage {
try reserveSerializerArrayMemory(
context, Int.self, ownerBytes: fieldOwnerBytes([Int].self), count: count)
}
var values: [Int] = []
values.reserveCapacity(count)
for _ in 0..<count {
values.append(Int(try context.buffer.readInt64()))
}
return values
}
private func readUIntArrayPayload(
_ context: ReadContext, reserveGraphStorage: Bool = false
) throws
-> [UInt]
{
let count = try readPackedArrayElementCount(context, width: 8, label: "uint64_array")
if reserveGraphStorage {
try reserveSerializerArrayMemory(
context, UInt.self, ownerBytes: fieldOwnerBytes([UInt].self), count: count)
}
var values: [UInt] = []
values.reserveCapacity(count)
for _ in 0..<count {
values.append(UInt(try context.buffer.readUInt64()))
}
return values
}
@inline(never)
private func readCompatiblePackedArrayField<ElementCodec: FieldCodec>(
_ context: ReadContext,
refMode: RefMode,
elementCodec _: ElementCodec.Type
) throws -> [ElementCodec.Value] {
switch refMode {
case .none:
return try readCompatiblePackedArrayPayload(context, elementCodec: ElementCodec.self)
case .nullOnly, .tracking:
let rawFlag = try context.buffer.readInt8()
guard rawFlag != RefFlag.null.rawValue else {
return []
}
if rawFlag == RefFlag.ref.rawValue {
let refID = try context.buffer.readVarUInt32()
return try context.refReader.readRef(refID, as: [ElementCodec.Value].self)
}
let reservedRefID =
(rawFlag == RefFlag.refValue.rawValue && context.trackRef)
? context.refReader.reserveRefID()
: nil
let value = try readCompatiblePackedArrayPayload(context, elementCodec: ElementCodec.self)
if let reservedRefID {
context.refReader.storeRef(value, at: reservedRefID)
}
return value
}
}
private func readCompatiblePackedArrayPayload<ElementCodec: FieldCodec>(
_ context: ReadContext,
elementCodec _: ElementCodec.Type
) throws -> [ElementCodec.Value] {
if ElementCodec.self == BoolCodec.self {
return uncheckedPackedArrayCast(
try readPrimitiveArray(context, reserveGraphStorage: true) as [Bool],
to: ElementCodec.Value.self)
}
if ElementCodec.self == Int8Codec.self {
return uncheckedPackedArrayCast(
try readPrimitiveArray(context, reserveGraphStorage: true) as [Int8],
to: ElementCodec.Value.self)
}
if ElementCodec.self == Int16Codec.self {
return uncheckedPackedArrayCast(
try readPrimitiveArray(context, reserveGraphStorage: true) as [Int16],
to: ElementCodec.Value.self)
}
if ElementCodec.self == Int32FixedCodec.self || ElementCodec.self == Int32VarintCodec.self {
return uncheckedPackedArrayCast(
try readPrimitiveArray(context, reserveGraphStorage: true) as [Int32],
to: ElementCodec.Value.self)
}
if ElementCodec.self == Int64FixedCodec.self || ElementCodec.self == Int64VarintCodec.self
|| ElementCodec.self == Int64TaggedCodec.self
{
return uncheckedPackedArrayCast(
try readPrimitiveArray(context, reserveGraphStorage: true) as [Int64],
to: ElementCodec.Value.self)
}
if ElementCodec.self == IntFixedCodec.self || ElementCodec.self == IntVarintCodec.self
|| ElementCodec.self == IntTaggedCodec.self
{
return uncheckedPackedArrayCast(
try readIntArrayPayload(context, reserveGraphStorage: true), to: ElementCodec.Value.self)
}
if ElementCodec.self == UInt8Codec.self {
return uncheckedPackedArrayCast(
try readPrimitiveArray(context, reserveGraphStorage: true) as [UInt8],
to: ElementCodec.Value.self)
}
if ElementCodec.self == UInt16Codec.self {
return uncheckedPackedArrayCast(
try readPrimitiveArray(context, reserveGraphStorage: true) as [UInt16],
to: ElementCodec.Value.self)
}
if ElementCodec.self == UInt32FixedCodec.self || ElementCodec.self == UInt32VarintCodec.self {
return uncheckedPackedArrayCast(
try readPrimitiveArray(context, reserveGraphStorage: true) as [UInt32],
to: ElementCodec.Value.self)
}
if ElementCodec.self == UInt64FixedCodec.self || ElementCodec.self == UInt64VarintCodec.self
|| ElementCodec.self == UInt64TaggedCodec.self
{
return uncheckedPackedArrayCast(
try readPrimitiveArray(context, reserveGraphStorage: true) as [UInt64],
to: ElementCodec.Value.self)
}
if ElementCodec.self == UIntFixedCodec.self || ElementCodec.self == UIntVarintCodec.self
|| ElementCodec.self == UIntTaggedCodec.self
{
return uncheckedPackedArrayCast(
try readUIntArrayPayload(context, reserveGraphStorage: true), to: ElementCodec.Value.self)
}
if ElementCodec.self == Float16Codec.self {
return uncheckedPackedArrayCast(
try readPrimitiveArray(context, reserveGraphStorage: true) as [Float16],
to: ElementCodec.Value.self)
}
if ElementCodec.self == BFloat16Codec.self {
return uncheckedPackedArrayCast(
try readPrimitiveArray(context, reserveGraphStorage: true) as [BFloat16],
to: ElementCodec.Value.self)
}
if ElementCodec.self == FloatCodec.self {
return uncheckedPackedArrayCast(
try readPrimitiveArray(context, reserveGraphStorage: true) as [Float],
to: ElementCodec.Value.self)
}
if ElementCodec.self == DoubleCodec.self {
return uncheckedPackedArrayCast(
try readPrimitiveArray(context, reserveGraphStorage: true) as [Double],
to: ElementCodec.Value.self)
}
throw ForyError.invalidData(
"unsupported compatible array-to-list field element codec \(ElementCodec.self)")
}
private func readCompatibleElementPayload<ElementCodec: FieldCodec>(
_ context: ReadContext,
elementCodec _: ElementCodec.Type,
remoteElementTypeID: UInt32?
) throws -> ElementCodec.Value {
guard let remoteElementTypeID,
remoteElementTypeID != ElementCodec.typeId.rawValue,
let remoteTypeID = TypeId(rawValue: remoteElementTypeID)
else {
return try ElementCodec.readPayload(context)
}
if ElementCodec.self == Int32FixedCodec.self || ElementCodec.self == Int32VarintCodec.self {
switch remoteTypeID {
case .int32:
return uncheckedScalarCast(
try context.buffer.readInt32() as Int32, to: ElementCodec.Value.self)
case .varint32:
return uncheckedScalarCast(
try context.buffer.readVarInt32() as Int32, to: ElementCodec.Value.self)
default:
break
}
}
if ElementCodec.self == Int64FixedCodec.self || ElementCodec.self == Int64VarintCodec.self
|| ElementCodec.self == Int64TaggedCodec.self
{
switch remoteTypeID {
case .int64:
return uncheckedScalarCast(
try context.buffer.readInt64() as Int64, to: ElementCodec.Value.self)
case .varint64:
return uncheckedScalarCast(
try context.buffer.readVarInt64() as Int64, to: ElementCodec.Value.self)
case .taggedInt64:
return uncheckedScalarCast(
try context.buffer.readTaggedInt64() as Int64, to: ElementCodec.Value.self)
default:
break
}
}
if ElementCodec.self == IntFixedCodec.self || ElementCodec.self == IntVarintCodec.self
|| ElementCodec.self == IntTaggedCodec.self
{
switch remoteTypeID {
case .int64:
return uncheckedScalarCast(Int(try context.buffer.readInt64()), to: ElementCodec.Value.self)
case .varint64:
return uncheckedScalarCast(
Int(try context.buffer.readVarInt64()), to: ElementCodec.Value.self)
case .taggedInt64:
return uncheckedScalarCast(
Int(try context.buffer.readTaggedInt64()), to: ElementCodec.Value.self)
default:
break
}
}
if ElementCodec.self == UInt32FixedCodec.self || ElementCodec.self == UInt32VarintCodec.self {
switch remoteTypeID {
case .uint32:
return uncheckedScalarCast(
try context.buffer.readUInt32() as UInt32, to: ElementCodec.Value.self)
case .varUInt32:
return uncheckedScalarCast(
try context.buffer.readVarUInt32() as UInt32, to: ElementCodec.Value.self)
default:
break
}
}
if ElementCodec.self == UInt64FixedCodec.self || ElementCodec.self == UInt64VarintCodec.self
|| ElementCodec.self == UInt64TaggedCodec.self
{
switch remoteTypeID {
case .uint64:
return uncheckedScalarCast(
try context.buffer.readUInt64() as UInt64, to: ElementCodec.Value.self)
case .varUInt64:
return uncheckedScalarCast(
try context.buffer.readVarUInt64() as UInt64, to: ElementCodec.Value.self)
case .taggedUInt64:
return uncheckedScalarCast(
try context.buffer.readTaggedUInt64() as UInt64, to: ElementCodec.Value.self)
default:
break
}
}
if ElementCodec.self == UIntFixedCodec.self || ElementCodec.self == UIntVarintCodec.self
|| ElementCodec.self == UIntTaggedCodec.self
{
switch remoteTypeID {
case .uint64:
return uncheckedScalarCast(UInt(try context.buffer.readUInt64()), to: ElementCodec.Value.self)
case .varUInt64:
return uncheckedScalarCast(
UInt(try context.buffer.readVarUInt64()), to: ElementCodec.Value.self)
case .taggedUInt64:
return uncheckedScalarCast(
UInt(try context.buffer.readTaggedUInt64()), to: ElementCodec.Value.self)
default:
break
}
}
throw ForyError.typeMismatch(expected: ElementCodec.typeId.rawValue, actual: remoteElementTypeID)
}
private func readPackedArrayElementCount(
_ context: ReadContext,
width: Int,
label: String
) throws -> Int {
let byteSize = Int(try context.buffer.readVarUInt32())
try context.ensureRemainingBytes(byteSize, label: "primitive_array_bytes")
if byteSize % width != 0 {
throw ForyError.invalidData("\(label) byte size mismatch")
}
let count = byteSize / width
try context.ensureCollectionLength(count, label: label)
return count
}
private func writeCollectionPayload<ElementCodec: FieldCodec>(
_ value: [ElementCodec.Value],
_ context: WriteContext,
elementCodec _: ElementCodec.Type
) throws {
let buffer = context.buffer
buffer.writeVarUInt32(UInt32(value.count))
if value.isEmpty {
return
}
let hasNull = ElementCodec.isNullableType && value.contains(where: ElementCodec.isNone)
let trackRef = context.trackRef && ElementCodec.isRefType
let declaredElementType = !TypeId.needsTypeInfoForField(ElementCodec.typeId)
let dynamicElementType = ElementCodec.typeId == .unknown
var header: UInt8 = dynamicElementType ? 0 : CollectionHeader.sameType
if trackRef {
header |= CollectionHeader.trackingRef
}
if hasNull {
header |= CollectionHeader.hasNull
}
if declaredElementType {
header |= CollectionHeader.declaredElementType
}
buffer.writeUInt8(header)
if !dynamicElementType && !declaredElementType {
try ElementCodec.writeStaticTypeInfo(context)
}
if dynamicElementType {
let refMode = RefMode.from(nullable: hasNull, trackRef: trackRef)
for element in value {
try ElementCodec.write(element, context, refMode: refMode, writeTypeInfo: true)
}
return
}
if trackRef {
for element in value {
try ElementCodec.write(element, context, refMode: .tracking, writeTypeInfo: false)
}
} else if hasNull {
for element in value {
if ElementCodec.isNone(element) {
buffer.writeInt8(RefFlag.null.rawValue)
} else {
buffer.writeInt8(RefFlag.notNullValue.rawValue)
try ElementCodec.writePayload(element, context)
}
}
} else {
for element in value {
try ElementCodec.writePayload(element, context)
}
}
}
private func readCollectionPayload<ElementCodec: FieldCodec>(
_ context: ReadContext,
elementCodec _: ElementCodec.Type,
ownerBytes: Int
) throws -> [ElementCodec.Value] {
let buffer = context.buffer
let length = Int(try buffer.readVarUInt32())
try context.ensureCollectionLength(length, label: "array")
if length == 0 {
try reserveFieldArrayStorage(
context, ElementCodec.self, ownerBytes: ownerBytes, count: length)
return []
}
let header = try buffer.readUInt8()
// IMPORTANT: collection readers must obey the ref/null bits written on the
// wire, not the local Swift element metadata that may imply a different
// ref policy. Shared xlang tests intentionally deserialize one ref policy
// and then serialize another local payload. DO NOT REMOVE this comment.
let trackRef = (header & CollectionHeader.trackingRef) != 0
let hasNull = (header & CollectionHeader.hasNull) != 0
let declared = (header & CollectionHeader.declaredElementType) != 0
let sameType = (header & CollectionHeader.sameType) != 0
var result: [ElementCodec.Value] = []
try reserveFieldArrayStorage(context, ElementCodec.self, ownerBytes: ownerBytes, count: length)
try context.ensureRemainingBytes(length, label: "array")
result.reserveCapacity(length)
if !sameType {
let refMode = RefMode.from(nullable: hasNull, trackRef: trackRef)
for _ in 0..<length {
result.append(try ElementCodec.read(context, refMode: refMode, readTypeInfo: true))
}
return result
}
let elementTypeInfo = declared ? nil : try ElementCodec.readTypeInfo(context)
return try ElementCodec.withTypeInfo(elementTypeInfo, context) {
if trackRef {
for _ in 0..<length {
result.append(try ElementCodec.read(context, refMode: .tracking, readTypeInfo: false))
}
} else if hasNull {
for _ in 0..<length {
let refFlag = try buffer.readInt8()
if refFlag == RefFlag.null.rawValue {
result.append(ElementCodec.defaultValue)
} else if refFlag == RefFlag.notNullValue.rawValue {
result.append(try ElementCodec.readPayload(context))
} else {
throw ForyError.refError("invalid nullability flag \(refFlag)")
}
}
} else {
for _ in 0..<length {
result.append(try ElementCodec.readPayload(context))
}
}
return result
}
}
@inline(never)
private func readListPayloadAsArray<ElementCodec: FieldCodec>(
_ context: ReadContext,
refMode: RefMode,
elementCodec _: ElementCodec.Type,
remoteElementTypeID: UInt32
) throws -> [ElementCodec.Value] {
switch refMode {
case .none:
return try readListPayloadAsArrayPayload(
context,
elementCodec: ElementCodec.self,
remoteElementTypeID: remoteElementTypeID
)
case .nullOnly, .tracking:
let rawFlag = try context.buffer.readInt8()
guard rawFlag != RefFlag.null.rawValue else {
return []
}
if rawFlag == RefFlag.ref.rawValue {
let refID = try context.buffer.readVarUInt32()
return try context.refReader.readRef(refID, as: [ElementCodec.Value].self)
}
let reservedRefID =
(rawFlag == RefFlag.refValue.rawValue && context.trackRef)
? context.refReader.reserveRefID()
: nil
let value = try readListPayloadAsArrayPayload(
context,
elementCodec: ElementCodec.self,
remoteElementTypeID: remoteElementTypeID
)
if let reservedRefID {
context.refReader.storeRef(value, at: reservedRefID)
}
return value
}
}
private func readListPayloadAsArrayPayload<ElementCodec: FieldCodec>(
_ context: ReadContext,
elementCodec _: ElementCodec.Type,
remoteElementTypeID: UInt32
) throws -> [ElementCodec.Value] {
let buffer = context.buffer
let length = Int(try buffer.readVarUInt32())
try context.ensureCollectionLength(length, label: "array")
if length == 0 {
return []
}
let header = try buffer.readUInt8()
let trackRef = (header & CollectionHeader.trackingRef) != 0
let hasNull = (header & CollectionHeader.hasNull) != 0
if hasNull {
throw ForyError.invalidData("compatible list-to-array field cannot read nullable elements")
}
let declared = (header & CollectionHeader.declaredElementType) != 0
let sameType = (header & CollectionHeader.sameType) != 0
if !sameType {
throw ForyError.invalidData("compatible list-to-array field requires same-type elements")
}
if trackRef {
throw ForyError.invalidData("compatible list-to-array field cannot read ref-tracked elements")
}
let elementTypeInfo: TypeInfo?
if declared {
elementTypeInfo = nil
} else {
throw ForyError.invalidData("compatible list-to-array field requires declared elements")
}
try context.ensureRemainingBytes(length, label: "array")
var result: [ElementCodec.Value] = []
result.reserveCapacity(length)
return try ElementCodec.withTypeInfo(elementTypeInfo, context) {
for _ in 0..<length {
result.append(
try readCompatibleElementPayload(
context,
elementCodec: ElementCodec.self,
remoteElementTypeID: remoteElementTypeID
))
}
return result
}
}