import Foundation import MurMurHash32 extension _AdaptedPostboxEncoder { final class UnkeyedContainer { fileprivate enum Item { case int32(Int32) case int64(Int64) case string(String) case object(Data) case data(Data) } let codingPath: [CodingKey] let userInfo: [CodingUserInfoKey: Any] fileprivate var items: [Item] = [] var count: Int { return self.items.count } init(codingPath: [CodingKey], userInfo: [CodingUserInfoKey : Any]) { self.codingPath = codingPath self.userInfo = userInfo } func makeData(addHeader: Bool, isDictionary: Bool) -> (Data, ValueType) { precondition(addHeader) if self.items.isEmpty { let buffer = WriteBuffer() var length: Int32 = Int32(self.items.count) buffer.write(&length, offset: 0, length: 4) return (buffer.makeData(), .Int32Array) } else if self.items.allSatisfy({ if case .int32 = $0 { return true } else { return false } }) { let buffer = WriteBuffer() var length: Int32 = Int32(self.items.count) buffer.write(&length, offset: 0, length: 4) for case .int32(var value) in self.items { buffer.write(&value, offset: 0, length: 4) } return (buffer.makeData(), .Int32Array) } else if self.items.allSatisfy({ if case .int64 = $0 { return true } else { return false } }) { let buffer = WriteBuffer() var length: Int32 = Int32(self.items.count) buffer.write(&length, offset: 0, length: 4) for case .int64(var value) in self.items { buffer.write(&value, offset: 0, length: 8) } return (buffer.makeData(), .Int64Array) } else if self.items.allSatisfy({ if case .string = $0 { return true } else { return false } }) { let buffer = WriteBuffer() var length: Int32 = Int32(self.items.count) buffer.write(&length, offset: 0, length: 4) for case .string(let value) in self.items { let data = value.data(using: .utf8, allowLossyConversion: true) ?? (String("").data(using: .utf8)!) var valueLength: Int32 = Int32(data.count) buffer.write(&valueLength, offset: 0, length: 4) buffer.write(data) } return (buffer.makeData(), .StringArray) } else if self.items.allSatisfy({ if case .object = $0 { return true } else { return false } }) { let buffer = WriteBuffer() var length: Int32 = Int32(self.items.count) if isDictionary { precondition(length % 2 == 0) length /= 2 } buffer.write(&length, offset: 0, length: 4) for case .object(let data) in self.items { buffer.write(data) } return (buffer.makeData(), isDictionary ? .ObjectDictionary : .ObjectArray) } else if self.items.allSatisfy({ if case .data = $0 { return true } else { return false } }) { let buffer = WriteBuffer() var length: Int32 = Int32(self.items.count) buffer.write(&length, offset: 0, length: 4) for case .data(let data) in self.items { var valueLength: Int32 = Int32(data.count) buffer.write(&valueLength, offset: 0, length: 4) buffer.write(data) } return (buffer.makeData(), .BytesArray) } else { preconditionFailure() } } } } extension _AdaptedPostboxEncoder.UnkeyedContainer: UnkeyedEncodingContainer { func encodeNil() throws { preconditionFailure() } func encode(_ value: T) throws where T : Encodable { if value is Int32 { try self.encode(value as! Int32) } else if value is Int64 { try self.encode(value as! Int64) } else if value is String { try self.encode(value as! String) } else if value is Data { try self.encode(value as! Data) } else if let value = value as? AdaptedPostboxEncoder.RawObjectData { let buffer = WriteBuffer() var typeHash: Int32 = value.typeHash buffer.write(&typeHash, offset: 0, length: 4) var length: Int32 = Int32(value.data.count) buffer.write(&length, offset: 0, length: 4) buffer.write(value.data) self.items.append(.object(buffer.makeData())) } else { let typeHash: Int32 = murMurHashString32("\(type(of: value))") let innerEncoder = _AdaptedPostboxEncoder(typeHash: typeHash) try! value.encode(to: innerEncoder) let (data, _) = innerEncoder.makeData(addHeader: true, isDictionary: false) let buffer = WriteBuffer() buffer.write(data) self.items.append(.object(buffer.makeData())) } } func encode(_ value: Int32) throws { self.items.append(.int32(value)) } func encode(_ value: Int64) throws { self.items.append(.int64(value)) } func encode(_ value: String) throws { self.items.append(.string(value)) } func nestedContainer(keyedBy keyType: NestedKey.Type) -> KeyedEncodingContainer where NestedKey : CodingKey { preconditionFailure() } func nestedUnkeyedContainer() -> UnkeyedEncodingContainer { preconditionFailure() } func superEncoder() -> Encoder { preconditionFailure() } } private extension _AdaptedPostboxEncoder.UnkeyedContainer { func encode(_ value: Data) throws { self.items.append(.data(value)) } } extension _AdaptedPostboxEncoder.UnkeyedContainer: AdaptedPostboxEncodingContainer { func makeData() -> Data { preconditionFailure() } }