blob: 1211901079f21d8695bf02ccd90fc548f4053e70 [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
public struct ForyAnyNullValue: Hashable, Serializer {
public typealias Target = Self
public init() {}
public static var staticTypeId: TypeId { .none }
public static var isNullableType: Bool { true }
public static func isNone(_: Self) -> Bool { true }
public static func defaultValue(_: ReadContext) throws -> Self {
Self()
}
public static func writeData(_: Self, _: WriteContext) throws {}
public static func readData(_: ReadContext) throws -> Self {
Self()
}
}
private protocol DynamicOptional {
static var dynamicNone: Any { get }
var dynamicWrapped: Any? { get }
}
extension Optional: DynamicOptional {
static var dynamicNone: Any { Self.none as Any }
var dynamicWrapped: Any? {
switch self {
case .some(let value):
return value
case .none:
return nil
}
}
}
@inline(__always)
private func unwrapDynamicTarget(_ value: Any) -> Any? {
if value is ForyAnyNullValue || value is NSNull {
return nil
}
if let optional = value as? any DynamicOptional {
guard let wrapped = optional.dynamicWrapped else {
return nil
}
return unwrapDynamicTarget(wrapped)
}
if Swift.type(of: value) == AnyHashable.self,
let anyHashable = value as? AnyHashable
{
return unwrapDynamicTarget(anyHashable.base)
}
return value
}
@inline(never)
private func unsupportedDynamicBody(_ operation: String) throws -> Never {
throw ForyError.invalidData(
"DynamicSerializer.\(operation) requires complete-value dynamic type information")
}
@inline(never)
private func missingDynamicDefault<T>(_ type: T.Type) throws -> Never {
throw ForyError.invalidData("dynamic target \(type) has no default value")
}
@inline(never)
private func missingDynamicTypeInfo() throws -> Never {
throw ForyError.invalidData("dynamic value requires concrete type information")
}
@inline(never)
private func nullDynamicWithoutEnvelope() throws -> Never {
throw ForyError.invalidData("null dynamic value cannot use RefMode.none")
}
@inline(never)
private func invalidDynamicRefFlag(_ rawFlag: Int8) throws -> Never {
throw ForyError.refError("invalid ref flag \(rawFlag)")
}
@inline(never)
private func dynamicTargetMismatch<T>(_ value: Any, _ type: T.Type) throws -> Never {
throw ForyError.invalidData(
"dynamic target \(Swift.type(of: value)) does not conform to requested type \(type)")
}
@inline(never)
private func valueTargetAsAnyObject(_ typeInfo: TypeInfo) throws -> Never {
throw ForyError.invalidData(
"dynamic value type \(typeInfo.typeID) cannot be materialized as AnyObject")
}
@inline(never)
private func valueTargetAsAnyObject(_ type: Any.Type) throws -> Never {
throw ForyError.invalidData(
"dynamic value type \(type) cannot be materialized as AnyObject")
}
@inline(__always)
private func dynamicDefault<T>(_ type: T.Type) throws -> T {
let value: Any
if type == Any.self {
value = ForyAnyNullValue()
} else if type == AnyObject.self {
value = NSNull()
} else if type == AnyHashable.self {
value = AnyHashable(ForyAnyNullValue())
} else if let optionalType = type as? any DynamicOptional.Type {
value = optionalType.dynamicNone
} else {
try missingDynamicDefault(type)
}
guard let typed = value as? T else {
try dynamicTargetMismatch(value, type)
}
return typed
}
@inline(__always)
private func castDynamicTarget<T>(
_ value: Any,
to type: T.Type,
typeInfo: TypeInfo? = nil
) throws -> T {
if type == AnyObject.self {
if let typeInfo {
if !typeInfo.isRefType {
try valueTargetAsAnyObject(typeInfo)
}
} else if !(Swift.type(of: value) is AnyObject.Type) {
try valueTargetAsAnyObject(Swift.type(of: value))
}
}
if type == AnyHashable.self {
let hashable = try toAnyHashable(value)
guard let typed = hashable as? T else {
try dynamicTargetMismatch(value, type)
}
return typed
}
guard let typed = value as? T else {
try dynamicTargetMismatch(value, type)
}
return typed
}
@inline(never)
private func toAnyHashable(_ value: Any) throws -> AnyHashable {
if let value = value as? AnyHashable {
return value
}
guard let value = value as? any Hashable else {
throw ForyError.invalidData(
"dynamic AnyHashable target must be Hashable, got \(Swift.type(of: value))")
}
return AnyHashable(value)
}
@usableFromInline
@inline(__always)
internal func writeDynamicTypeInfo<T>(
for value: T,
_ context: WriteContext
) throws -> TypeInfo {
guard let target = unwrapDynamicTarget(value) else {
try missingDynamicTypeInfo()
}
let typeInfo = try context.typeInfo(forTarget: Swift.type(of: target))
try typeInfo.writeTypeInfo(context)
return typeInfo
}
@usableFromInline
@inline(__always)
internal func writeDynamicValue<T>(
_ value: T,
typeInfo: TypeInfo,
_ context: WriteContext,
refMode: RefMode
) throws {
guard let target = unwrapDynamicTarget(value) else {
guard refMode != .none else {
try nullDynamicWithoutEnvelope()
}
context.buffer.writeInt8(RefFlag.null.rawValue)
return
}
if refMode != .none {
context.buffer.writeInt8(RefFlag.notNullValue.rawValue)
}
try context.enterDynamicAnyDepth()
defer { context.leaveDynamicAnyDepth() }
try typeInfo.writeDynamic(target, context)
}
@inline(__always)
private func readDynamicValue<S: Serializer>(
_ context: ReadContext,
as serializer: S.Type,
refMode: RefMode,
readTypeInfo: Bool
) throws -> S.Target {
let reservedRefID: UInt32?
if refMode != .none {
let rawFlag = try context.buffer.readInt8()
guard let flag = RefFlag(rawValue: rawFlag) else {
try invalidDynamicRefFlag(rawFlag)
}
switch flag {
case .null:
return try serializer.defaultValue(context)
case .ref:
let refID = try context.buffer.readVarUInt32()
let value = try context.refReader.readRefValue(refID)
return try castDynamicTarget(value, to: S.Target.self)
case .refValue:
reservedRefID = context.trackRef ? context.refReader.reserveRefID() : nil
case .notNullValue:
reservedRefID = nil
}
} else {
reservedRefID = nil
}
let typeInfo: TypeInfo
if readTypeInfo {
typeInfo = try context.readTypeInfo()
} else if let scoped = context.getTypeInfo(for: serializer) {
typeInfo = scoped
} else {
try missingDynamicTypeInfo()
}
if S.Target.self == AnyObject.self, !typeInfo.isRefType {
try valueTargetAsAnyObject(typeInfo)
}
let value = try typeInfo.readDynamic(context)
if let reservedRefID {
context.refReader.storeRef(value, at: reservedRefID)
}
return try castDynamicTarget(value, to: S.Target.self, typeInfo: typeInfo)
}
/// Serializes a dynamic value by resolving its registered concrete target type.
///
/// Select this serializer explicitly for `Any`, `AnyObject`, application
/// protocol existentials, and dynamic children of carrier serializers.
public enum DynamicSerializer<T>: Serializer {
public typealias Target = T
public static var staticTypeId: TypeId { .unknown }
public static var isNullableType: Bool { true }
public static var isRefType: Bool { true }
public static func isNone(_ value: T) -> Bool {
unwrapDynamicTarget(value) == nil
}
public static func defaultValue(_: ReadContext) throws -> T {
try dynamicDefault(T.self)
}
public static func writeData(_: T, _: WriteContext) throws {
try unsupportedDynamicBody("writeData")
}
public static func readData(_: ReadContext) throws -> T {
try unsupportedDynamicBody("readData")
}
public static func writeTypeInfo(_: WriteContext) throws {
try unsupportedDynamicBody("writeTypeInfo")
}
public static func readTypeInfo(_ context: ReadContext) throws -> TypeInfo? {
try context.readTypeInfo()
}
public static func write(
_ value: T,
_ context: WriteContext,
refMode: RefMode,
writeTypeInfo: Bool
) throws {
guard let target = unwrapDynamicTarget(value) else {
guard refMode != .none else {
try nullDynamicWithoutEnvelope()
}
context.buffer.writeInt8(RefFlag.null.rawValue)
return
}
if refMode != .none {
context.buffer.writeInt8(RefFlag.notNullValue.rawValue)
}
try context.enterDynamicAnyDepth()
defer { context.leaveDynamicAnyDepth() }
let typeInfo = try context.typeInfo(forTarget: Swift.type(of: target))
if writeTypeInfo {
try typeInfo.writeTypeInfo(context)
}
try typeInfo.writeDynamic(target, context)
}
public static func read(
_ context: ReadContext,
refMode: RefMode,
readTypeInfo: Bool
) throws -> T {
try readDynamicValue(
context,
as: Self.self,
refMode: refMode,
readTypeInfo: readTypeInfo
)
}
}
extension AnyHashable: Serializer {
public typealias Target = Self
public static var staticTypeId: TypeId { .unknown }
public static func defaultValue(_: ReadContext) throws -> Self {
AnyHashable(Int32(0))
}
public static func writeData(_: Self, _: WriteContext) throws {
try unsupportedDynamicBody("writeData")
}
public static func readData(_: ReadContext) throws -> Self {
try unsupportedDynamicBody("readData")
}
public static func writeTypeInfo(_: WriteContext) throws {
try unsupportedDynamicBody("writeTypeInfo")
}
public static func readTypeInfo(_ context: ReadContext) throws -> TypeInfo? {
try context.readTypeInfo()
}
public static func write(
_ value: Self,
_ context: WriteContext,
refMode: RefMode,
writeTypeInfo: Bool
) throws {
try DynamicSerializer<AnyHashable>.write(
value,
context,
refMode: refMode,
writeTypeInfo: writeTypeInfo
)
}
public static func read(
_ context: ReadContext,
refMode: RefMode,
readTypeInfo: Bool
) throws -> Self {
try readDynamicValue(
context,
as: Self.self,
refMode: refMode,
readTypeInfo: readTypeInfo
)
}
}