diff --git a/submodules/AccountContext/Sources/ChatController.swift b/submodules/AccountContext/Sources/ChatController.swift index 393a656d7c..af7c095c8e 100644 --- a/submodules/AccountContext/Sources/ChatController.swift +++ b/submodules/AccountContext/Sources/ChatController.swift @@ -174,18 +174,6 @@ public struct ChatTextInputState: Codable, Equatable { try container.encode(Int32(self.selectionRange.lowerBound), forKey: "as0") try container.encode(Int32(self.selectionRange.upperBound), forKey: "as1") } - - /*public init(decoder: PostboxDecoder) { - self.inputText = ((decoder.decodeObjectForKey("at", decoder: { ChatTextInputStateText(decoder: $0) }) as? ChatTextInputStateText) ?? ChatTextInputStateText()).attributedText() - self.selectionRange = Int(decoder.decodeInt32ForKey("as0", orElse: 0)) ..< Int(decoder.decodeInt32ForKey("as1", orElse: 0)) - }*/ - - /*public func encode(_ encoder: PostboxEncoder) { - encoder.encodeObject(ChatTextInputStateText(attributedText: self.inputText), forKey: "at") - - encoder.encodeInt32(Int32(self.selectionRange.lowerBound), forKey: "as0") - encoder.encodeInt32(Int32(self.selectionRange.upperBound), forKey: "as1") - }*/ } public enum ChatTextInputStateTextAttributeType: Codable, Equatable { @@ -198,7 +186,7 @@ public enum ChatTextInputStateTextAttributeType: Codable, Equatable { public init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: StringCodingKey.self) - switch (try? container.decodeIfPresent(Int32.self, forKey: "t")) ?? 0 { + switch (try? container.decode(Int32.self, forKey: "t")) ?? 0 { case 0: self = .bold case 1: @@ -234,41 +222,6 @@ public enum ChatTextInputStateTextAttributeType: Codable, Equatable { try container.encode(url, forKey: "url") } } - - /*public init(decoder: PostboxDecoder) { - switch decoder.decodeInt32ForKey("t", orElse: 0) { - case 0: - self = .bold - case 1: - self = .italic - case 2: - self = .monospace - case 3: - self = .textMention(EnginePeer.Id(decoder.decodeInt64ForKey("peerId", orElse: 0))) - case 4: - self = .textUrl(decoder.decodeStringForKey("url", orElse: "")) - default: - assertionFailure() - self = .bold - } - } - - public func encode(_ encoder: PostboxEncoder) { - switch self { - case .bold: - encoder.encodeInt32(0, forKey: "t") - case .italic: - encoder.encodeInt32(1, forKey: "t") - case .monospace: - encoder.encodeInt32(2, forKey: "t") - case let .textMention(id): - encoder.encodeInt32(3, forKey: "t") - encoder.encodeInt64(id.toInt64(), forKey: "peerId") - case let .textUrl(url): - encoder.encodeInt32(4, forKey: "t") - encoder.encodeString(url, forKey: "url") - } - }*/ } public struct ChatTextInputStateTextAttribute: Codable, Equatable { @@ -283,8 +236,8 @@ public struct ChatTextInputStateTextAttribute: Codable, Equatable { public init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: StringCodingKey.self) self.type = try container.decode(ChatTextInputStateTextAttributeType.self, forKey: "type") - let rangeFrom = (try? container.decodeIfPresent(Int32.self, forKey: "range0")) ?? 0 - let rangeTo = (try? container.decodeIfPresent(Int32.self, forKey: "range1")) ?? 0 + let rangeFrom = (try? container.decode(Int32.self, forKey: "range0")) ?? 0 + let rangeTo = (try? container.decode(Int32.self, forKey: "range1")) ?? 0 self.range = Int(rangeFrom) ..< Int(rangeTo) } @@ -298,17 +251,6 @@ public struct ChatTextInputStateTextAttribute: Codable, Equatable { try container.encode(Int32(self.range.upperBound), forKey: "range1") } - /*public init(decoder: PostboxDecoder) { - self.type = decoder.decodeObjectForKey("type", decoder: { ChatTextInputStateTextAttributeType(decoder: $0) }) as! ChatTextInputStateTextAttributeType - self.range = Int(decoder.decodeInt32ForKey("range0", orElse: 0)) ..< Int(decoder.decodeInt32ForKey("range1", orElse: 0)) - } - - public func encode(_ encoder: PostboxEncoder) { - encoder.encodeObject(self.type, forKey: "type") - encoder.encodeInt32(Int32(self.range.lowerBound), forKey: "range0") - encoder.encodeInt32(Int32(self.range.upperBound), forKey: "range1") - }*/ - public static func ==(lhs: ChatTextInputStateTextAttribute, rhs: ChatTextInputStateTextAttribute) -> Bool { return lhs.type == rhs.type && lhs.range == rhs.range } @@ -361,16 +303,6 @@ public struct ChatTextInputStateText: Codable, Equatable { try container.encode(self.attributes, forKey: "attributes") } - /*public init(decoder: PostboxDecoder) { - self.text = decoder.decodeStringForKey("text", orElse: "") - self.attributes = decoder.decodeObjectArrayWithDecoderForKey("attributes") - } - - public func encode(_ encoder: PostboxEncoder) { - encoder.encodeString(self.text, forKey: "text") - encoder.encodeObjectArray(self.attributes, forKey: "attributes") - }*/ - static public func ==(lhs: ChatTextInputStateText, rhs: ChatTextInputStateText) -> Bool { return lhs.text == rhs.text && lhs.attributes == rhs.attributes } @@ -419,7 +351,7 @@ public final class ChatEmbeddedInterfaceState: PeerChatListEmbeddedInterfaceStat public init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: StringCodingKey.self) self.timestamp = (try? container.decode(Int32.self, forKey: "d")) ?? 0 - self.text = ((try? container.decodeIfPresent(ChatTextInputStateText.self, forKey: "at")) ?? ChatTextInputStateText()).attributedText() + self.text = ((try? container.decode(ChatTextInputStateText.self, forKey: "at")) ?? ChatTextInputStateText()).attributedText() } public func encode(to encoder: Encoder) throws { @@ -429,16 +361,6 @@ public final class ChatEmbeddedInterfaceState: PeerChatListEmbeddedInterfaceStat try container.encode(ChatTextInputStateText(attributedText: self.text), forKey: "at") } - /*public init(decoder: PostboxDecoder) { - self.timestamp = decoder.decodeInt32ForKey("d", orElse: 0) - self.text = ((decoder.decodeObjectForKey("at", decoder: { ChatTextInputStateText(decoder: $0) }) as? ChatTextInputStateText) ?? ChatTextInputStateText()).attributedText() - } - - public func encode(_ encoder: PostboxEncoder) { - encoder.encodeInt32(self.timestamp, forKey: "d") - encoder.encodeObject(ChatTextInputStateText(attributedText: self.text), forKey: "at") - }*/ - public func isEqual(to: PeerChatListEmbeddedInterfaceState) -> Bool { if let to = to as? ChatEmbeddedInterfaceState { return self.timestamp == to.timestamp && self.text.isEqual(to: to.text) diff --git a/submodules/Postbox/Sources/Coding.swift b/submodules/Postbox/Sources/Coding.swift index cb29125b22..37a5c14d86 100644 --- a/submodules/Postbox/Sources/Coding.swift +++ b/submodules/Postbox/Sources/Coding.swift @@ -603,16 +603,12 @@ public final class PostboxEncoder { self.buffer.write(innerData) } - public func encodeInnerObjectData(_ value: Data, forKey key: String) { + func encodeInnerObjectData(_ value: Data, valueType: ValueType, forKey key: String) { self.encodeKey(key) - var t: Int8 = ValueType.Object.rawValue + + var t: Int8 = valueType.rawValue self.buffer.write(&t, offset: 0, length: 1) - - var typeHash: Int32 = 0 - self.buffer.write(&typeHash, offset: 0, length: 4) - - var length: Int32 = Int32(value.count) - self.buffer.write(&length, offset: 0, length: 4) + self.buffer.write(value) } @@ -1008,7 +1004,7 @@ public final class PostboxDecoder { let initialOffset = self.offset PostboxDecoder.skipValue(self.buffer.memory.assumingMemoryBound(to: Int8.self), offset: &self.offset, length: self.buffer.length, valueType: actualValueType) - let data = ReadBuffer(memory: UnsafeMutableRawPointer(mutating: self.buffer.memory.advanced(by: self.offset)), length: self.offset - initialOffset, freeWhenDone: false).makeData() + let data = ReadBuffer(memory: UnsafeMutableRawPointer(mutating: self.buffer.memory.advanced(by: initialOffset)), length: self.offset - initialOffset, freeWhenDone: false).makeData() return (data, actualValueType) } @@ -1179,7 +1175,16 @@ public final class PostboxDecoder { return array } else { - return nil + if PostboxDecoder.positionOnKey(self.buffer.memory, offset: &self.offset, maxOffset: self.buffer.length, length: self.buffer.length, key: key, valueType: .Int32Array) { + let array = decodeInt32ArrayRaw() + if array.isEmpty { + return [] + } else { + return nil + } + } else { + return nil + } } } diff --git a/submodules/Postbox/Sources/Utils/Decoder/AdaptedPostboxKeyedDecodingContainer.swift b/submodules/Postbox/Sources/Utils/Decoder/AdaptedPostboxKeyedDecodingContainer.swift index 6ef1103632..6d4f5e9faa 100644 --- a/submodules/Postbox/Sources/Utils/Decoder/AdaptedPostboxKeyedDecodingContainer.swift +++ b/submodules/Postbox/Sources/Utils/Decoder/AdaptedPostboxKeyedDecodingContainer.swift @@ -14,6 +14,12 @@ extension _AdaptedPostboxDecoder { } } +private func decodingErrorBreakpoint() { + #if DEBUG + print("Decoding error. Install a breakpoint at decodingErrorBreakpoint to debug.") + #endif +} + extension _AdaptedPostboxDecoder.KeyedContainer: KeyedDecodingContainerProtocol { var allKeys: [Key] { preconditionFailure() @@ -32,9 +38,11 @@ extension _AdaptedPostboxDecoder.KeyedContainer: KeyedDecodingContainerProtocol if let mappedType = AdaptedPostboxDecoder.ContentType(valueType: valueType) { return try AdaptedPostboxDecoder().decode(T.self, from: data, contentType: mappedType) } else { + decodingErrorBreakpoint() throw DecodingError.typeMismatch(T.self, DecodingError.Context(codingPath: self.codingPath + [key], debugDescription: "")) } } else { + decodingErrorBreakpoint() throw DecodingError.keyNotFound(key, DecodingError.Context(codingPath: self.codingPath + [key], debugDescription: "")) } } @@ -43,6 +51,7 @@ extension _AdaptedPostboxDecoder.KeyedContainer: KeyedDecodingContainerProtocol if let value = self.decoder.decodeOptionalInt32ForKey(key.stringValue) { return value } else { + decodingErrorBreakpoint() throw DecodingError.keyNotFound(key, DecodingError.Context(codingPath: self.codingPath + [key], debugDescription: "")) } } @@ -51,6 +60,7 @@ extension _AdaptedPostboxDecoder.KeyedContainer: KeyedDecodingContainerProtocol if let value = self.decoder.decodeOptionalInt64ForKey(key.stringValue) { return value } else { + decodingErrorBreakpoint() throw DecodingError.keyNotFound(key, DecodingError.Context(codingPath: self.codingPath + [key], debugDescription: "")) } } @@ -59,6 +69,7 @@ extension _AdaptedPostboxDecoder.KeyedContainer: KeyedDecodingContainerProtocol if let value = self.decoder.decodeOptionalBoolForKey(key.stringValue) { return value } else { + decodingErrorBreakpoint() throw DecodingError.keyNotFound(key, DecodingError.Context(codingPath: self.codingPath + [key], debugDescription: "")) } } @@ -67,6 +78,7 @@ extension _AdaptedPostboxDecoder.KeyedContainer: KeyedDecodingContainerProtocol if let value = self.decoder.decodeOptionalStringForKey(key.stringValue) { return value } else { + decodingErrorBreakpoint() throw DecodingError.keyNotFound(key, DecodingError.Context(codingPath: self.codingPath + [key], debugDescription: "")) } } diff --git a/submodules/Postbox/Sources/Utils/Encoder/AdaptedPostboxEncoder.swift b/submodules/Postbox/Sources/Utils/Encoder/AdaptedPostboxEncoder.swift index 437534f4b3..fefd6bb851 100644 --- a/submodules/Postbox/Sources/Utils/Encoder/AdaptedPostboxEncoder.swift +++ b/submodules/Postbox/Sources/Utils/Encoder/AdaptedPostboxEncoder.swift @@ -4,7 +4,7 @@ public class AdaptedPostboxEncoder { func encode(_ value: Encodable) throws -> Data { let encoder = _AdaptedPostboxEncoder() try value.encode(to: encoder) - return encoder.data + return encoder.makeData().0 } } @@ -15,7 +15,7 @@ final class _AdaptedPostboxEncoder { fileprivate var container: AdaptedPostboxEncodingContainer? - var data: Data { + func makeData() -> (Data, ValueType) { return self.container!.makeData() } } @@ -35,7 +35,12 @@ extension _AdaptedPostboxEncoder: Encoder { } func unkeyedContainer() -> UnkeyedEncodingContainer { - preconditionFailure() + assertCanCreateContainer() + + let container = UnkeyedContainer(codingPath: self.codingPath, userInfo: self.userInfo) + self.container = container + + return container } func singleValueContainer() -> SingleValueEncodingContainer { @@ -44,5 +49,5 @@ extension _AdaptedPostboxEncoder: Encoder { } protocol AdaptedPostboxEncodingContainer: AnyObject { - func makeData() -> Data + func makeData() -> (Data, ValueType) } diff --git a/submodules/Postbox/Sources/Utils/Encoder/AdaptedPostboxKeyedEncodingContainer.swift b/submodules/Postbox/Sources/Utils/Encoder/AdaptedPostboxKeyedEncodingContainer.swift index d37daf70e0..cd840a9580 100644 --- a/submodules/Postbox/Sources/Utils/Encoder/AdaptedPostboxKeyedEncodingContainer.swift +++ b/submodules/Postbox/Sources/Utils/Encoder/AdaptedPostboxKeyedEncodingContainer.swift @@ -14,8 +14,19 @@ extension _AdaptedPostboxEncoder { self.encoder = PostboxEncoder() } - func makeData() -> Data { - return self.encoder.makeData() + func makeData() -> (Data, ValueType) { + let buffer = WriteBuffer() + + var typeHash: Int32 = 0 + buffer.write(&typeHash, offset: 0, length: 4) + + let data = self.encoder.makeData() + + var length: Int32 = Int32(data.count) + buffer.write(&length, offset: 0, length: 4) + buffer.write(data) + + return (buffer.makeData(), .Object) } } } @@ -25,7 +36,8 @@ extension _AdaptedPostboxEncoder.KeyedContainer: KeyedEncodingContainerProtocol let innerEncoder = _AdaptedPostboxEncoder() try! value.encode(to: innerEncoder) - self.encoder.encodeInnerObjectData(innerEncoder.data, forKey: key.stringValue) + let (data, valueType) = innerEncoder.makeData() + self.encoder.encodeInnerObjectData(data, valueType: valueType, forKey: key.stringValue) } func encodeNil(forKey key: Key) throws { diff --git a/submodules/Postbox/Sources/Utils/Encoder/AdaptedPostboxSingleValueEncodingContainer.swift b/submodules/Postbox/Sources/Utils/Encoder/AdaptedPostboxSingleValueEncodingContainer.swift index 182231ff2d..7dd39a4ffa 100644 --- a/submodules/Postbox/Sources/Utils/Encoder/AdaptedPostboxSingleValueEncodingContainer.swift +++ b/submodules/Postbox/Sources/Utils/Encoder/AdaptedPostboxSingleValueEncodingContainer.swift @@ -79,7 +79,7 @@ extension _AdaptedPostboxEncoder.SingleValueContainer: SingleValueEncodingContai } extension _AdaptedPostboxEncoder.SingleValueContainer: AdaptedPostboxEncodingContainer { - func makeData() -> Data { + func makeData() -> (Data, ValueType) { preconditionFailure() } } diff --git a/submodules/Postbox/Sources/Utils/Encoder/AdaptedPostboxUnkeyedEncodingContainer.swift b/submodules/Postbox/Sources/Utils/Encoder/AdaptedPostboxUnkeyedEncodingContainer.swift index a11d351a0e..c546133b44 100644 --- a/submodules/Postbox/Sources/Utils/Encoder/AdaptedPostboxUnkeyedEncodingContainer.swift +++ b/submodules/Postbox/Sources/Utils/Encoder/AdaptedPostboxUnkeyedEncodingContainer.swift @@ -1,17 +1,102 @@ import Foundation +import MurMurHash32 extension _AdaptedPostboxEncoder { final class UnkeyedContainer { - var codingPath: [CodingKey] - var userInfo: [CodingUserInfoKey: Any] + 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 { - preconditionFailure() + return self.items.count } init(codingPath: [CodingKey], userInfo: [CodingUserInfoKey : Any]) { self.codingPath = codingPath self.userInfo = userInfo } + + func makeData() -> (Data, ValueType) { + 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: 4) + } + + 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) + buffer.write(&length, offset: 0, length: 4) + + for case .object(let data) in self.items { + buffer.write(data) + } + + return (buffer.makeData(), .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() + } + } } } @@ -21,7 +106,37 @@ extension _AdaptedPostboxEncoder.UnkeyedContainer: UnkeyedEncodingContainer { } func encode(_ value: T) throws where T : Encodable { - preconditionFailure() + let innerEncoder = _AdaptedPostboxEncoder() + try! value.encode(to: innerEncoder) + + let (data, _) = innerEncoder.makeData() + + let buffer = WriteBuffer() + var typeHash: Int32 = murMurHashString32("\(type(of: value))") + buffer.write(&typeHash, offset: 0, length: 4) + + var length: Int32 = Int32(data.count) + buffer.write(&length, offset: 0, length: 4) + + 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 encode(_ value: Data) throws { + self.items.append(.data(value)) } func nestedContainer(keyedBy keyType: NestedKey.Type) -> KeyedEncodingContainer where NestedKey : CodingKey {