|
| 1 | +/// This comes from Mike Ash's work on implementing a Codeable implemention of a binary encoder/decoder |
| 2 | +/// https://github.com/mikeash/BinaryCoder |
| 3 | +/// https://www.mikeash.com/pyblog/friday-qa-2017-07-28-a-binary-coder-for-swift.html |
| 4 | + |
| 5 | +import CoreFoundation |
| 6 | + |
| 7 | + |
| 8 | +/// A protocol for types which can be encoded to binary. |
| 9 | +public protocol BinaryEncodable: Encodable { |
| 10 | + func binaryEncode(to encoder: BinaryEncoder) throws |
| 11 | +} |
| 12 | + |
| 13 | +/// This is used when an atom has children that need to be prefixed by their size |
| 14 | +public protocol BinarySizedEncodable: BinaryEncodable { } |
| 15 | + |
| 16 | +/// Provide a default implementation which calls through to `Encodable`. This |
| 17 | +/// allows `BinaryEncodable` to use the `Encodable` implementation generated by the |
| 18 | +/// compiler. |
| 19 | +public extension BinaryEncodable { |
| 20 | + func binaryEncode(to encoder: BinaryEncoder) throws { |
| 21 | + try self.encode(to: encoder) |
| 22 | + } |
| 23 | +} |
| 24 | + |
| 25 | +/// The actual binary encoder class. |
| 26 | +public class BinaryEncoder { |
| 27 | + internal var data: [UInt8] = [] |
| 28 | + |
| 29 | + public init() {} |
| 30 | +} |
| 31 | + |
| 32 | +/// A convenience function for creating an encoder, encoding a value, and |
| 33 | +/// extracting the resulting data. |
| 34 | +public extension BinaryEncoder { |
| 35 | + static func encode(_ value: BinaryEncodable) throws -> [UInt8] { |
| 36 | + let encoder = BinaryEncoder() |
| 37 | + try value.binaryEncode(to: encoder) |
| 38 | + |
| 39 | + // KRAD: We need to prepend the size of what we've encoded |
| 40 | + return byteArray(from: UInt32(encoder.data.count + 4)) + encoder.data |
| 41 | + } |
| 42 | +} |
| 43 | + |
| 44 | +/// The error type. |
| 45 | +public extension BinaryEncoder { |
| 46 | + /// All errors which `BinaryEncoder` itself can throw. |
| 47 | + enum Error: Swift.Error { |
| 48 | + /// Attempted to encode a type which is `Encodable`, but not `BinaryEncodable`. (We |
| 49 | + /// require `BinaryEncodable` because `BinaryEncoder` doesn't support full keyed |
| 50 | + /// coding functionality.) |
| 51 | + case typeNotConformingToBinaryEncodable(Encodable.Type) |
| 52 | + |
| 53 | + /// Attempted to encode a type which is not `Encodable`. |
| 54 | + case typeNotConformingToEncodable(Any.Type) |
| 55 | + } |
| 56 | +} |
| 57 | + |
| 58 | +/// Methods for encoding various types. |
| 59 | +public extension BinaryEncoder { |
| 60 | + func encode(_ value: Bool) throws { |
| 61 | + try encode(value ? 1 as UInt8 : 0 as UInt8) |
| 62 | + } |
| 63 | + |
| 64 | + func encode(_ value: Float) { |
| 65 | + appendBytes(of: CFConvertFloatHostToSwapped(value)) |
| 66 | + } |
| 67 | + |
| 68 | + func encode(_ value: Double) { |
| 69 | + appendBytes(of: CFConvertDoubleHostToSwapped(value)) |
| 70 | + } |
| 71 | + |
| 72 | + func encode(_ encodable: Encodable) throws { |
| 73 | + switch encodable { |
| 74 | + case let v as Int: |
| 75 | + try encode(Int64(v)) |
| 76 | + case let v as UInt: |
| 77 | + try encode(UInt64(v)) |
| 78 | + case let v as Float: |
| 79 | + encode(v) |
| 80 | + case let v as Double: |
| 81 | + encode(v) |
| 82 | + case let v as Bool: |
| 83 | + try encode(v) |
| 84 | + case let binary as BinaryEncodable: |
| 85 | + try binary.binaryEncode(to: self) |
| 86 | + default: |
| 87 | + throw Error.typeNotConformingToBinaryEncodable(type(of: encodable)) |
| 88 | + } |
| 89 | + } |
| 90 | + |
| 91 | + /// Append the raw bytes of the parameter to the encoder's data. No byte-swapping |
| 92 | + /// or other encoding is done. |
| 93 | + func appendBytes<T>(of: T) { |
| 94 | + var target = of |
| 95 | + withUnsafeBytes(of: &target) { |
| 96 | + data.append(contentsOf: $0) |
| 97 | + } |
| 98 | + } |
| 99 | +} |
| 100 | + |
| 101 | +extension BinaryEncoder: Encoder { |
| 102 | + public var codingPath: [CodingKey] { return [] } |
| 103 | + |
| 104 | + public var userInfo: [CodingUserInfoKey : Any] { return [:] } |
| 105 | + |
| 106 | + public func container<Key>(keyedBy type: Key.Type) -> KeyedEncodingContainer<Key> where Key : CodingKey { |
| 107 | + return KeyedEncodingContainer(KeyedContainer<Key>(encoder: self)) |
| 108 | + } |
| 109 | + |
| 110 | + public func unkeyedContainer() -> UnkeyedEncodingContainer { |
| 111 | + return UnkeyedContanier(encoder: self) |
| 112 | + } |
| 113 | + |
| 114 | + public func singleValueContainer() -> SingleValueEncodingContainer { |
| 115 | + return UnkeyedContanier(encoder: self) |
| 116 | + } |
| 117 | + |
| 118 | + private struct KeyedContainer<Key: CodingKey>: KeyedEncodingContainerProtocol { |
| 119 | + var encoder: BinaryEncoder |
| 120 | + |
| 121 | + var codingPath: [CodingKey] { return [] } |
| 122 | + |
| 123 | + func encode<T>(_ value: T, forKey key: Key) throws where T : Encodable { |
| 124 | + try encoder.encode(value) |
| 125 | + } |
| 126 | + |
| 127 | + func encodeNil(forKey key: Key) throws {} |
| 128 | + |
| 129 | + func nestedContainer<NestedKey>(keyedBy keyType: NestedKey.Type, forKey key: Key) -> KeyedEncodingContainer<NestedKey> where NestedKey : CodingKey { |
| 130 | + return encoder.container(keyedBy: keyType) |
| 131 | + } |
| 132 | + |
| 133 | + func nestedUnkeyedContainer(forKey key: Key) -> UnkeyedEncodingContainer { |
| 134 | + return encoder.unkeyedContainer() |
| 135 | + } |
| 136 | + |
| 137 | + func superEncoder() -> Encoder { |
| 138 | + return encoder |
| 139 | + } |
| 140 | + |
| 141 | + func superEncoder(forKey key: Key) -> Encoder { |
| 142 | + return encoder |
| 143 | + } |
| 144 | + } |
| 145 | + |
| 146 | + private struct UnkeyedContanier: UnkeyedEncodingContainer, SingleValueEncodingContainer { |
| 147 | + var encoder: BinaryEncoder |
| 148 | + |
| 149 | + var codingPath: [CodingKey] { return [] } |
| 150 | + |
| 151 | + var count: Int { return 0 } |
| 152 | + |
| 153 | + func nestedContainer<NestedKey>(keyedBy keyType: NestedKey.Type) -> KeyedEncodingContainer<NestedKey> where NestedKey : CodingKey { |
| 154 | + return encoder.container(keyedBy: keyType) |
| 155 | + } |
| 156 | + |
| 157 | + func nestedUnkeyedContainer() -> UnkeyedEncodingContainer { |
| 158 | + return self |
| 159 | + } |
| 160 | + |
| 161 | + func superEncoder() -> Encoder { |
| 162 | + return encoder |
| 163 | + } |
| 164 | + |
| 165 | + func encodeNil() throws {} |
| 166 | + |
| 167 | + func encode<T>(_ value: T) throws where T : Encodable { |
| 168 | + try encoder.encode(value) |
| 169 | + } |
| 170 | + } |
| 171 | +} |
0 commit comments