From e874fa92ffaa8791e019a8834c665dce7cc1fbba Mon Sep 17 00:00:00 2001 From: Ali <> Date: Thu, 29 Jul 2021 21:07:15 +0200 Subject: [PATCH] Temp --- .../Sources/ChatController.swift | 175 +++++++++++------ .../Sources/ChatInterfaceState.swift | 77 ++++++-- submodules/Postbox/Sources/Coding.swift | 180 ++++++++++++++++++ .../Sources/PeerChatInterfaceState.swift | 2 +- .../Utils/Decoder/AdaptedPostboxDecoder.swift | 47 +++++ ...AdaptedPostboxKeyedDecodingContainer.swift | 83 ++++++++ ...dPostboxSingleValueDecodingContainer.swift | 82 ++++++++ ...aptedPostboxUnkeyedDecodingContainer.swift | 15 ++ .../Utils/Encoder/AdaptedPostboxEncoder.swift | 48 +++++ ...AdaptedPostboxKeyedEncodingContainer.swift | 72 +++++++ ...dPostboxSingleValueEncodingContainer.swift | 85 +++++++++ ...aptedPostboxUnkeyedEncodingContainer.swift | 44 +++++ .../Sources/Utils/PostboxCodingAdapter.swift | 1 + .../Utils/StringCodingKey.swift | 24 +++ .../Sources/DeclareEncodables.swift | 1 - 15 files changed, 869 insertions(+), 67 deletions(-) create mode 100644 submodules/Postbox/Sources/Utils/Decoder/AdaptedPostboxDecoder.swift create mode 100644 submodules/Postbox/Sources/Utils/Decoder/AdaptedPostboxKeyedDecodingContainer.swift create mode 100644 submodules/Postbox/Sources/Utils/Decoder/AdaptedPostboxSingleValueDecodingContainer.swift create mode 100644 submodules/Postbox/Sources/Utils/Decoder/AdaptedPostboxUnkeyedDecodingContainer.swift create mode 100644 submodules/Postbox/Sources/Utils/Encoder/AdaptedPostboxEncoder.swift create mode 100644 submodules/Postbox/Sources/Utils/Encoder/AdaptedPostboxKeyedEncodingContainer.swift create mode 100644 submodules/Postbox/Sources/Utils/Encoder/AdaptedPostboxSingleValueEncodingContainer.swift create mode 100644 submodules/Postbox/Sources/Utils/Encoder/AdaptedPostboxUnkeyedEncodingContainer.swift create mode 100644 submodules/Postbox/Sources/Utils/PostboxCodingAdapter.swift create mode 100644 submodules/TelegramCore/Sources/TelegramEngine/Utils/StringCodingKey.swift diff --git a/submodules/AccountContext/Sources/ChatController.swift b/submodules/AccountContext/Sources/ChatController.swift index d0919530dd..2fb7014a77 100644 --- a/submodules/AccountContext/Sources/ChatController.swift +++ b/submodules/AccountContext/Sources/ChatController.swift @@ -130,7 +130,7 @@ public enum ChatControllerInteractionNavigateToPeer { case withBotStartPayload(ChatControllerInitialBotStart) } -public struct ChatTextInputState: PostboxCoding, Equatable { +public struct ChatTextInputState: Codable, Equatable { public let inputText: NSAttributedString public let selectionRange: Range @@ -153,28 +153,89 @@ public struct ChatTextInputState: PostboxCoding, Equatable { let length = inputText.length self.selectionRange = length ..< length } - - 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 init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: StringCodingKey.self) + self.inputText = ((try? container.decodeIfPresent(ChatTextInputStateText.self, forKey: "at")) ?? ChatTextInputStateText()).attributedText() + let rangeFrom = (try? container.decodeIfPresent(Int32.self, forKey: "as0")) ?? 0 + let rangeTo = (try? container.decodeIfPresent(Int32.self, forKey: "as1")) ?? 0 + if rangeFrom <= rangeTo { + self.selectionRange = Int(rangeFrom) ..< Int(rangeTo) + } else { + let length = self.inputText.length + self.selectionRange = length ..< length + } + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: StringCodingKey.self) + + try container.encode(ChatTextInputStateText(attributedText: self.inputText), forKey: "at") + try container.encode(Int32(self.selectionRange.lowerBound), forKey: "as0") + try container.encode(Int32(self.selectionRange.upperBound), forKey: "as1") } - public func encode(_ encoder: PostboxEncoder) { + /*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: PostboxCoding, Equatable { +public enum ChatTextInputStateTextAttributeType: Codable, Equatable { case bold case italic case monospace case textMention(EnginePeer.Id) case textUrl(String) + + public init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: StringCodingKey.self) + + switch (try? container.decodeIfPresent(Int32.self, forKey: "t")) ?? 0 { + case 0: + self = .bold + case 1: + self = .italic + case 2: + self = .monospace + case 3: + let peerId = (try? container.decode(Int64.self, forKey: "peerId")) ?? 0 + self = .textMention(EnginePeer.Id(peerId)) + case 4: + let url = (try? container.decode(String.self, forKey: "url")) ?? "" + self = .textUrl(url) + default: + assertionFailure() + self = .bold + } + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: StringCodingKey.self) + switch self { + case .bold: + try container.encode(0 as Int32, forKey: "t") + case .italic: + try container.encode(1 as Int32, forKey: "t") + case .monospace: + try container.encode(2 as Int32, forKey: "t") + case let .textMention(id): + try container.encode(3 as Int32, forKey: "t") + try container.encode(id.toInt64(), forKey: "peerId") + case let .textUrl(url): + try container.encode(4 as Int32, forKey: "t") + try container.encode(url, forKey: "url") + } + } - public init(decoder: PostboxDecoder) { + /*public init(decoder: PostboxDecoder) { switch decoder.decodeInt32ForKey("t", orElse: 0) { case 0: self = .bold @@ -207,45 +268,10 @@ public enum ChatTextInputStateTextAttributeType: PostboxCoding, Equatable { encoder.encodeInt32(4, forKey: "t") encoder.encodeString(url, forKey: "url") } - } - - public static func ==(lhs: ChatTextInputStateTextAttributeType, rhs: ChatTextInputStateTextAttributeType) -> Bool { - switch lhs { - case .bold: - if case .bold = rhs { - return true - } else { - return false - } - case .italic: - if case .italic = rhs { - return true - } else { - return false - } - case .monospace: - if case .monospace = rhs { - return true - } else { - return false - } - case let .textMention(id): - if case .textMention(id) = rhs { - return true - } else { - return false - } - case let .textUrl(url): - if case .textUrl(url) = rhs { - return true - } else { - return false - } - } - } + }*/ } -public struct ChatTextInputStateTextAttribute: PostboxCoding, Equatable { +public struct ChatTextInputStateTextAttribute: Codable, Equatable { public let type: ChatTextInputStateTextAttributeType public let range: Range @@ -253,8 +279,26 @@ public struct ChatTextInputStateTextAttribute: PostboxCoding, Equatable { self.type = type self.range = range } + + 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 + + self.range = Int(rangeFrom) ..< Int(rangeTo) + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: StringCodingKey.self) + + try container.encode(self.type, forKey: "type") + + try container.encode(Int32(self.range.lowerBound), forKey: "range0") + try container.encode(Int32(self.range.upperBound), forKey: "range1") + } - public init(decoder: PostboxDecoder) { + /*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)) } @@ -263,14 +307,14 @@ public struct ChatTextInputStateTextAttribute: PostboxCoding, Equatable { 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 } } -public struct ChatTextInputStateText: PostboxCoding, Equatable { +public struct ChatTextInputStateText: Codable, Equatable { public let text: String public let attributes: [ChatTextInputStateTextAttribute] @@ -304,8 +348,20 @@ public struct ChatTextInputStateText: PostboxCoding, Equatable { }) self.attributes = parsedAttributes } + + public init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: StringCodingKey.self) + self.text = (try? container.decodeIfPresent(String.self, forKey: "text")) ?? "" + self.attributes = (try? container.decodeIfPresent([ChatTextInputStateTextAttribute].self, forKey: "attributes")) ?? [] + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: StringCodingKey.self) + try container.encode(self.text, forKey: "text") + try container.encode(self.attributes, forKey: "attributes") + } - public init(decoder: PostboxDecoder) { + /*public init(decoder: PostboxDecoder) { self.text = decoder.decodeStringForKey("text", orElse: "") self.attributes = decoder.decodeObjectArrayWithDecoderForKey("attributes") } @@ -313,7 +369,7 @@ public struct ChatTextInputStateText: PostboxCoding, Equatable { 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 @@ -359,8 +415,21 @@ public final class ChatEmbeddedInterfaceState: PeerChatListEmbeddedInterfaceStat self.timestamp = timestamp self.text = text } + + 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() + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: StringCodingKey.self) + + try container.encode(self.timestamp, forKey: "d") + try container.encode(ChatTextInputStateText(attributedText: self.text), forKey: "at") + } - public init(decoder: PostboxDecoder) { + /*public init(decoder: PostboxDecoder) { self.timestamp = decoder.decodeInt32ForKey("d", orElse: 0) self.text = ((decoder.decodeObjectForKey("at", decoder: { ChatTextInputStateText(decoder: $0) }) as? ChatTextInputStateText) ?? ChatTextInputStateText()).attributedText() } @@ -368,7 +437,7 @@ public final class ChatEmbeddedInterfaceState: PeerChatListEmbeddedInterfaceStat 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 { diff --git a/submodules/ChatInterfaceState/Sources/ChatInterfaceState.swift b/submodules/ChatInterfaceState/Sources/ChatInterfaceState.swift index 2daae7aa39..9d14791fbc 100644 --- a/submodules/ChatInterfaceState/Sources/ChatInterfaceState.swift +++ b/submodules/ChatInterfaceState/Sources/ChatInterfaceState.swift @@ -10,7 +10,7 @@ public enum ChatTextInputMediaRecordingButtonMode: Int32 { case video = 1 } -public struct ChatInterfaceSelectionState: PostboxCoding, Equatable { +public struct ChatInterfaceSelectionState: Codable, Equatable { public let selectedIds: Set public static func ==(lhs: ChatInterfaceSelectionState, rhs: ChatInterfaceSelectionState) -> Bool { @@ -20,8 +20,28 @@ public struct ChatInterfaceSelectionState: PostboxCoding, Equatable { public init(selectedIds: Set) { self.selectedIds = selectedIds } + + public init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: StringCodingKey.self) + + if let data = try? container.decodeIfPresent(Data.self, forKey: "i") { + self.selectedIds = Set(MessageId.decodeArrayFromBuffer(ReadBuffer(data: data))) + } else { + self.selectedIds = Set() + } + } + + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: StringCodingKey.self) + + let buffer = WriteBuffer() + MessageId.encodeArrayToBuffer(Array(selectedIds), buffer: buffer) + + try container.encode(buffer.makeData(), forKey: "i") + } - public init(decoder: PostboxDecoder) { + /*public init(decoder: PostboxDecoder) { if let data = decoder.decodeBytesForKeyNoCopy("i") { self.selectedIds = Set(MessageId.decodeArrayFromBuffer(data)) } else { @@ -33,10 +53,10 @@ public struct ChatInterfaceSelectionState: PostboxCoding, Equatable { let buffer = WriteBuffer() MessageId.encodeArrayToBuffer(Array(selectedIds), buffer: buffer) encoder.encodeBytes(buffer, forKey: "i") - } + }*/ } -public struct ChatEditMessageState: PostboxCoding, Equatable { +public struct ChatEditMessageState: Codable, Equatable { public let messageId: MessageId public let inputState: ChatTextInputState public let disableUrlPreview: String? @@ -48,8 +68,41 @@ public struct ChatEditMessageState: PostboxCoding, Equatable { self.disableUrlPreview = disableUrlPreview self.inputTextMaxLength = inputTextMaxLength } + + public init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: StringCodingKey.self) + + self.messageId = MessageId( + peerId: PeerId((try? container.decode(Int64.self, forKey: "mp")) ?? 0), + namespace: (try? container.decode(Int32.self, forKey: "mn")) ?? 0, + id: (try? container.decode(Int32.self, forKey: "mi")) ?? 0 + ) + + if let inputState = try? container.decode(ChatTextInputState.self, forKey: "is") { + self.inputState = inputState + } else { + self.inputState = ChatTextInputState() + } + + self.disableUrlPreview = try? container.decodeIfPresent(String.self, forKey: "dup") + self.inputTextMaxLength = try? container.decodeIfPresent(Int32.self, forKey: "tl") + } + + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: StringCodingKey.self) + + try container.encode(self.messageId.peerId.toInt64(), forKey: "mp") + try container.encode(self.messageId.namespace, forKey: "mn") + try container.encode(self.messageId.id, forKey: "mi") + + try container.encode(self.inputState, forKey: "is") + + try container.encodeIfPresent(self.disableUrlPreview, forKey: "dup") + try container.encodeIfPresent(self.inputTextMaxLength, forKey: "tl") + } - public init(decoder: PostboxDecoder) { + /*public init(decoder: PostboxDecoder) { self.messageId = MessageId(peerId: PeerId(decoder.decodeInt64ForKey("mp", orElse: 0)), namespace: decoder.decodeInt32ForKey("mn", orElse: 0), id: decoder.decodeInt32ForKey("mi", orElse: 0)) if let inputState = decoder.decodeObjectForKey("is", decoder: { return ChatTextInputState(decoder: $0) }) as? ChatTextInputState { self.inputState = inputState @@ -75,7 +128,7 @@ public struct ChatEditMessageState: PostboxCoding, Equatable { } else { encoder.encodeNil(forKey: "ml") } - } + }*/ public static func ==(lhs: ChatEditMessageState, rhs: ChatEditMessageState) -> Bool { return lhs.messageId == rhs.messageId && lhs.inputState == rhs.inputState && lhs.disableUrlPreview == rhs.disableUrlPreview && lhs.inputTextMaxLength == rhs.inputTextMaxLength @@ -322,7 +375,7 @@ public final class ChatInterfaceState: SynchronizeableChatInterfaceState, Equata public init(decoder: PostboxDecoder) { self.timestamp = decoder.decodeInt32ForKey("ts", orElse: 0) - if let inputState = decoder.decodeObjectForKey("is", decoder: { return ChatTextInputState(decoder: $0) }) as? ChatTextInputState { + if let inputState = decoder.decode(ChatTextInputState.self, forKey: "is") { self.composeInputState = inputState } else { self.composeInputState = ChatTextInputState() @@ -345,12 +398,12 @@ public final class ChatInterfaceState: SynchronizeableChatInterfaceState, Equata } else { self.forwardMessageIds = nil } - if let editMessage = decoder.decodeObjectForKey("em", decoder: { ChatEditMessageState(decoder: $0) }) as? ChatEditMessageState { + if let editMessage = decoder.decode(ChatEditMessageState.self, forKey: "em") { self.editMessage = editMessage } else { self.editMessage = nil } - if let selectionState = decoder.decodeObjectForKey("ss", decoder: { return ChatInterfaceSelectionState(decoder: $0) }) as? ChatInterfaceSelectionState { + if let selectionState = decoder.decode(ChatInterfaceSelectionState.self, forKey: "ss") { self.selectionState = selectionState } else { self.selectionState = nil @@ -372,7 +425,7 @@ public final class ChatInterfaceState: SynchronizeableChatInterfaceState, Equata public func encode(_ encoder: PostboxEncoder) { encoder.encodeInt32(self.timestamp, forKey: "ts") - encoder.encodeObject(self.composeInputState, forKey: "is") + encoder.encode(self.composeInputState, forKey: "is") if let composeDisableUrlPreview = self.composeDisableUrlPreview { encoder.encodeString(composeDisableUrlPreview, forKey: "dup") } else { @@ -395,12 +448,12 @@ public final class ChatInterfaceState: SynchronizeableChatInterfaceState, Equata encoder.encodeNil(forKey: "fm") } if let editMessage = self.editMessage { - encoder.encodeObject(editMessage, forKey: "em") + encoder.encode(editMessage, forKey: "em") } else { encoder.encodeNil(forKey: "em") } if let selectionState = self.selectionState { - encoder.encodeObject(selectionState, forKey: "ss") + encoder.encode(selectionState, forKey: "ss") } else { encoder.encodeNil(forKey: "ss") } diff --git a/submodules/Postbox/Sources/Coding.swift b/submodules/Postbox/Sources/Coding.swift index 26de86c051..565e97f6a0 100644 --- a/submodules/Postbox/Sources/Coding.swift +++ b/submodules/Postbox/Sources/Coding.swift @@ -278,6 +278,12 @@ public final class PostboxEncoder { var type: Int8 = ValueType.Nil.rawValue self.buffer.write(&type, offset: 0, length: 1) } + + public func encodeNil(forKey key: String) { + self.encodeKey(key) + var type: Int8 = ValueType.Nil.rawValue + self.buffer.write(&type, offset: 0, length: 1) + } public func encodeInt32(_ value: Int32, forKey key: StaticString) { self.encodeKey(key) @@ -302,6 +308,14 @@ public final class PostboxEncoder { var v = value self.buffer.write(&v, offset: 0, length: 8) } + + public func encodeInt64(_ value: Int64, forKey key: String) { + self.encodeKey(key) + var type: Int8 = ValueType.Int64.rawValue + self.buffer.write(&type, offset: 0, length: 1) + var v = value + self.buffer.write(&v, offset: 0, length: 8) + } public func encodeBool(_ value: Bool, forKey key: StaticString) { self.encodeKey(key) @@ -310,6 +324,14 @@ public final class PostboxEncoder { var v: Int8 = value ? 1 : 0 self.buffer.write(&v, offset: 0, length: 1) } + + public func encodeBool(_ value: Bool, forKey key: String) { + self.encodeKey(key) + var type: Int8 = ValueType.Bool.rawValue + self.buffer.write(&type, offset: 0, length: 1) + var v: Int8 = value ? 1 : 0 + self.buffer.write(&v, offset: 0, length: 1) + } public func encodeDouble(_ value: Double, forKey key: StaticString) { self.encodeKey(key) @@ -318,6 +340,14 @@ public final class PostboxEncoder { var v = value self.buffer.write(&v, offset: 0, length: 8) } + + public func encodeDouble(_ value: Double, forKey key: String) { + self.encodeKey(key) + var type: Int8 = ValueType.Double.rawValue + self.buffer.write(&type, offset: 0, length: 1) + var v = value + self.buffer.write(&v, offset: 0, length: 8) + } public func encodeString(_ value: String, forKey key: StaticString) { self.encodeKey(key) @@ -332,6 +362,20 @@ public final class PostboxEncoder { self.buffer.write(&length, offset: 0, length: 4) } } + + public func encodeString(_ value: String, forKey key: String) { + self.encodeKey(key) + var type: Int8 = ValueType.String.rawValue + self.buffer.write(&type, offset: 0, length: 1) + if let data = value.data(using: .utf8, allowLossyConversion: true) { + var length: Int32 = Int32(data.count) + self.buffer.write(&length, offset: 0, length: 4) + self.buffer.write(data) + } else { + var length: Int32 = 0 + self.buffer.write(&length, offset: 0, length: 4) + } + } public func encodeRootObject(_ value: PostboxCoding) { self.encodeObject(value, forKey: "_") @@ -598,6 +642,38 @@ public final class PostboxEncoder { } } + public func encode(_ value: T, forKey key: String) { + let innerEncoder = AdaptedPostboxEncoder() + guard let innerData = try? innerEncoder.encode(value) else { + return + } + + self.encodeKey(key) + var t: Int8 = ValueType.Object.rawValue + self.buffer.write(&t, offset: 0, length: 1) + + let string = "\(type(of: value))" + var typeHash: Int32 = murMurHashString32(string) + self.buffer.write(&typeHash, offset: 0, length: 4) + + var length: Int32 = Int32(innerData.count) + self.buffer.write(&length, offset: 0, length: 4) + self.buffer.write(innerData) + } + + public func encodeInnerObjectData(_ value: Data, forKey key: String) { + self.encodeKey(key) + var t: Int8 = ValueType.Object.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) + } + public let sharedWriteBuffer = WriteBuffer() } @@ -718,6 +794,60 @@ public final class PostboxDecoder { return false } + private class func positionOnKey(_ rawBytes: UnsafeRawPointer, offset: inout Int, maxOffset: Int, length: Int, key: String, valueType: ValueType?, consumeKey: Bool = true) -> Bool + { + let bytes = rawBytes.assumingMemoryBound(to: Int8.self) + + let startOffset = offset + + let keyData = key.data(using: .utf8)! + + let keyLength: Int = keyData.count + while (offset < maxOffset) { + let keyOffset = offset + let readKeyLength = bytes[offset] + assert(readKeyLength >= 0) + offset += 1 + offset += Int(readKeyLength) + + let readValueType = bytes[offset] + offset += 1 + + if keyLength != Int(readKeyLength) { + skipValue(bytes, offset: &offset, length: length, valueType: ValueType(rawValue: readValueType)!) + continue + } + + if keyData.withUnsafeBytes({ keyBytes -> Bool in + return memcmp(bytes + (offset - Int(readKeyLength) - 1), keyBytes.baseAddress!, keyLength) == 0 + }) { + if let valueType = valueType { + if readValueType == valueType.rawValue { + return true + } else if readValueType == ValueType.Nil.rawValue { + return false + } else { + skipValue(bytes, offset: &offset, length: length, valueType: ValueType(rawValue: readValueType)!) + } + } else { + if !consumeKey { + offset = keyOffset + } + return true + } + } else { + skipValue(bytes, offset: &offset, length: length, valueType: ValueType(rawValue: readValueType)!) + } + } + + if (startOffset != 0) { + offset = 0 + return positionOnKey(bytes, offset: &offset, maxOffset: startOffset, length: length, key: key, valueType: valueType) + } + + return false + } + private class func positionOnStringKey(_ rawBytes: UnsafeRawPointer, offset: inout Int, maxOffset: Int, length: Int, key: String, valueType: ValueType) -> Bool { let bytes = rawBytes.assumingMemoryBound(to: Int8.self) @@ -789,6 +919,22 @@ public final class PostboxDecoder { return false } + + public func containsKey(_ key: String) -> Bool { + if PostboxDecoder.positionOnKey(self.buffer.memory, offset: &self.offset, maxOffset: self.buffer.length, length: self.buffer.length, key: key, valueType: nil, consumeKey: false) { + return true + } else { + return false + } + } + + public func decodeNilForKey(_ key: String) -> Bool { + if PostboxDecoder.positionOnKey(self.buffer.memory, offset: &self.offset, maxOffset: self.buffer.length, length: self.buffer.length, key: key, valueType: .Nil) { + return true + } else { + return false + } + } public func decodeInt32ForKey(_ key: StaticString, orElse: Int32) -> Int32 { if PostboxDecoder.positionOnKey(self.buffer.memory, offset: &self.offset, maxOffset: self.buffer.length, length: self.buffer.length, key: key, valueType: .Int32) { @@ -1520,4 +1666,38 @@ public final class PostboxDecoder { return nil } } + + public func decodeDataForKey(_ key: String) -> Data? { + if PostboxDecoder.positionOnKey(self.buffer.memory, offset: &self.offset, maxOffset: self.buffer.length, length: self.buffer.length, key: key, valueType: .Bytes) { + var length: Int32 = 0 + memcpy(&length, self.buffer.memory + self.offset, 4) + self.offset += 4 + Int(length) + var result = Data(count: Int(length)) + result.withUnsafeMutableBytes { (bytes: UnsafeMutablePointer) -> Void in + memcpy(bytes, self.buffer.memory.advanced(by: self.offset - Int(length)), Int(length)) + } + return result + } else { + return nil + } + } + + public func decode(_ type: T.Type, forKey key: String) -> T? { + if PostboxDecoder.positionOnKey(self.buffer.memory, offset: &self.offset, maxOffset: self.buffer.length, length: self.buffer.length, key: key, valueType: .Object) { + var typeHash: Int32 = 0 + memcpy(&typeHash, self.buffer.memory + self.offset, 4) + self.offset += 4 + + var length: Int32 = 0 + memcpy(&length, self.buffer.memory + self.offset, 4) + + let innerBuffer = ReadBuffer(memory: self.buffer.memory + (self.offset + 4), length: Int(length), freeWhenDone: false) + let innerData = innerBuffer.makeData() + self.offset += 4 + Int(length) + + return try? AdaptedPostboxDecoder().decode(T.self, from: innerData) + } else { + return nil + } + } } diff --git a/submodules/Postbox/Sources/PeerChatInterfaceState.swift b/submodules/Postbox/Sources/PeerChatInterfaceState.swift index eb395081d0..be04895995 100644 --- a/submodules/Postbox/Sources/PeerChatInterfaceState.swift +++ b/submodules/Postbox/Sources/PeerChatInterfaceState.swift @@ -1,5 +1,5 @@ -public protocol PeerChatListEmbeddedInterfaceState: PostboxCoding { +public protocol PeerChatListEmbeddedInterfaceState: Codable { var timestamp: Int32 { get } func isEqual(to: PeerChatListEmbeddedInterfaceState) -> Bool diff --git a/submodules/Postbox/Sources/Utils/Decoder/AdaptedPostboxDecoder.swift b/submodules/Postbox/Sources/Utils/Decoder/AdaptedPostboxDecoder.swift new file mode 100644 index 0000000000..f6e72f7e13 --- /dev/null +++ b/submodules/Postbox/Sources/Utils/Decoder/AdaptedPostboxDecoder.swift @@ -0,0 +1,47 @@ +import Foundation + +final public class AdaptedPostboxDecoder { + func decode(_ type: T.Type, from data: Data) throws -> T where T : Decodable { + let decoder = _AdaptedPostboxDecoder(data: data) + return try T(from: decoder) + } +} + +final class _AdaptedPostboxDecoder { + var codingPath: [CodingKey] = [] + + var userInfo: [CodingUserInfoKey : Any] = [:] + + var container: AdaptedPostboxDecodingContainer? + fileprivate var data: Data + + init(data: Data) { + self.data = data + } +} + +extension _AdaptedPostboxDecoder: Decoder { + fileprivate func assertCanCreateContainer() { + precondition(self.container == nil) + } + + func container(keyedBy type: Key.Type) -> KeyedDecodingContainer where Key : CodingKey { + assertCanCreateContainer() + + let container = KeyedContainer(data: self.data, codingPath: self.codingPath, userInfo: self.userInfo) + self.container = container + + return KeyedDecodingContainer(container) + } + + func unkeyedContainer() -> UnkeyedDecodingContainer { + preconditionFailure() + } + + func singleValueContainer() -> SingleValueDecodingContainer { + preconditionFailure() + } +} + +protocol AdaptedPostboxDecodingContainer: AnyObject { +} diff --git a/submodules/Postbox/Sources/Utils/Decoder/AdaptedPostboxKeyedDecodingContainer.swift b/submodules/Postbox/Sources/Utils/Decoder/AdaptedPostboxKeyedDecodingContainer.swift new file mode 100644 index 0000000000..49a5bf6dd0 --- /dev/null +++ b/submodules/Postbox/Sources/Utils/Decoder/AdaptedPostboxKeyedDecodingContainer.swift @@ -0,0 +1,83 @@ +import Foundation + +extension _AdaptedPostboxDecoder { + final class KeyedContainer where Key: CodingKey { + var codingPath: [CodingKey] + var userInfo: [CodingUserInfoKey: Any] + let decoder: PostboxDecoder + + init(data: Data, codingPath: [CodingKey], userInfo: [CodingUserInfoKey : Any]) { + self.codingPath = codingPath + self.userInfo = userInfo + self.decoder = PostboxDecoder(buffer: MemoryBuffer(data: data)) + } + } +} + +extension _AdaptedPostboxDecoder.KeyedContainer: KeyedDecodingContainerProtocol { + var allKeys: [Key] { + preconditionFailure() + } + + func contains(_ key: Key) -> Bool { + return self.decoder.containsKey(key.stringValue) + } + + func decodeNil(forKey key: Key) throws -> Bool { + return self.decoder.decodeNilForKey(key.stringValue) + } + + func decode(_ type: T.Type, forKey key: Key) throws -> T where T : Decodable { + preconditionFailure() + } + + func decode(_ type: Int32.Type, forKey key: Key) throws -> Int32 { + if let value = self.decoder.decodeOptionalInt32ForKey(key.stringValue) { + return value + } else { + throw DecodingError.keyNotFound(key, DecodingError.Context(codingPath: self.codingPath + [key], debugDescription: "")) + } + } + + func decode(_ type: Int64.Type, forKey key: Key) throws -> Int32 { + if let value = self.decoder.decodeOptionalInt64ForKey(key.stringValue) { + return value + } else { + throw DecodingError.keyNotFound(key, DecodingError.Context(codingPath: self.codingPath + [key], debugDescription: "")) + } + } + + func decode(_ type: Bool.Type, forKey key: Key) throws -> Int32 { + if let value = self.decoder.decodeOptionalBoolForKey(key.stringValue) { + return value + } else { + throw DecodingError.keyNotFound(key, DecodingError.Context(codingPath: self.codingPath + [key], debugDescription: "")) + } + } + + func decode(_ type: String.Type, forKey key: Key) throws -> Int32 { + if let value = self.decoder.decodeOptionalStringForKey(key.stringValue) { + return value + } else { + throw DecodingError.keyNotFound(key, DecodingError.Context(codingPath: self.codingPath + [key], debugDescription: "")) + } + } + + func nestedUnkeyedContainer(forKey key: Key) throws -> UnkeyedDecodingContainer { + preconditionFailure() + } + + func nestedContainer(keyedBy type: NestedKey.Type, forKey key: Key) throws -> KeyedDecodingContainer where NestedKey : CodingKey { + preconditionFailure() + } + + func superDecoder() throws -> Decoder { + preconditionFailure() + } + + func superDecoder(forKey key: Key) throws -> Decoder { + preconditionFailure() + } +} + +extension _AdaptedPostboxDecoder.KeyedContainer: AdaptedPostboxDecodingContainer {} diff --git a/submodules/Postbox/Sources/Utils/Decoder/AdaptedPostboxSingleValueDecodingContainer.swift b/submodules/Postbox/Sources/Utils/Decoder/AdaptedPostboxSingleValueDecodingContainer.swift new file mode 100644 index 0000000000..5f21bc88f7 --- /dev/null +++ b/submodules/Postbox/Sources/Utils/Decoder/AdaptedPostboxSingleValueDecodingContainer.swift @@ -0,0 +1,82 @@ +import Foundation + +extension _AdaptedPostboxDecoder { + final class SingleValueContainer { + var codingPath: [CodingKey] + var userInfo: [CodingUserInfoKey: Any] + + + init(codingPath: [CodingKey], userInfo: [CodingUserInfoKey : Any]) { + self.codingPath = codingPath + self.userInfo = userInfo + } + } +} + +extension _AdaptedPostboxDecoder.SingleValueContainer: SingleValueDecodingContainer { + func decodeNil() -> Bool { + preconditionFailure() + } + + func decode(_ type: Bool.Type) throws -> Bool { + preconditionFailure() + } + + func decode(_ type: String.Type) throws -> String { + preconditionFailure() + } + + func decode(_ type: Double.Type) throws -> Double { + preconditionFailure() + } + + func decode(_ type: Float.Type) throws -> Float { + preconditionFailure() + } + + func decode(_ type: Int.Type) throws -> Int { + preconditionFailure() + } + + func decode(_ type: Int8.Type) throws -> Int8 { + preconditionFailure() + } + + func decode(_ type: Int16.Type) throws -> Int16 { + preconditionFailure() + } + + func decode(_ type: Int32.Type) throws -> Int32 { + preconditionFailure() + } + + func decode(_ type: Int64.Type) throws -> Int64 { + preconditionFailure() + } + + func decode(_ type: UInt.Type) throws -> UInt { + preconditionFailure() + } + + func decode(_ type: UInt8.Type) throws -> UInt8 { + preconditionFailure() + } + + func decode(_ type: UInt16.Type) throws -> UInt16 { + preconditionFailure() + } + + func decode(_ type: UInt32.Type) throws -> UInt32 { + preconditionFailure() + } + + func decode(_ type: UInt64.Type) throws -> UInt64 { + preconditionFailure() + } + + func decode(_ type: T.Type) throws -> T where T : Decodable { + preconditionFailure() + } +} + +extension _AdaptedPostboxDecoder.SingleValueContainer: AdaptedPostboxDecodingContainer {} diff --git a/submodules/Postbox/Sources/Utils/Decoder/AdaptedPostboxUnkeyedDecodingContainer.swift b/submodules/Postbox/Sources/Utils/Decoder/AdaptedPostboxUnkeyedDecodingContainer.swift new file mode 100644 index 0000000000..4cf632bcab --- /dev/null +++ b/submodules/Postbox/Sources/Utils/Decoder/AdaptedPostboxUnkeyedDecodingContainer.swift @@ -0,0 +1,15 @@ +import Foundation + +extension _AdaptedPostboxDecoder { + final class UnkeyedContainer { + var codingPath: [CodingKey] + var userInfo: [CodingUserInfoKey: Any] + + init(data: Data, codingPath: [CodingKey], userInfo: [CodingUserInfoKey : Any]) { + self.codingPath = codingPath + self.userInfo = userInfo + } + } +} + + diff --git a/submodules/Postbox/Sources/Utils/Encoder/AdaptedPostboxEncoder.swift b/submodules/Postbox/Sources/Utils/Encoder/AdaptedPostboxEncoder.swift new file mode 100644 index 0000000000..437534f4b3 --- /dev/null +++ b/submodules/Postbox/Sources/Utils/Encoder/AdaptedPostboxEncoder.swift @@ -0,0 +1,48 @@ +import Foundation + +public class AdaptedPostboxEncoder { + func encode(_ value: Encodable) throws -> Data { + let encoder = _AdaptedPostboxEncoder() + try value.encode(to: encoder) + return encoder.data + } +} + +final class _AdaptedPostboxEncoder { + var codingPath: [CodingKey] = [] + + var userInfo: [CodingUserInfoKey : Any] = [:] + + fileprivate var container: AdaptedPostboxEncodingContainer? + + var data: Data { + return self.container!.makeData() + } +} + +extension _AdaptedPostboxEncoder: Encoder { + fileprivate func assertCanCreateContainer() { + precondition(self.container == nil) + } + + func container(keyedBy type: Key.Type) -> KeyedEncodingContainer where Key : CodingKey { + assertCanCreateContainer() + + let container = KeyedContainer(codingPath: self.codingPath, userInfo: self.userInfo) + self.container = container + + return KeyedEncodingContainer(container) + } + + func unkeyedContainer() -> UnkeyedEncodingContainer { + preconditionFailure() + } + + func singleValueContainer() -> SingleValueEncodingContainer { + preconditionFailure() + } +} + +protocol AdaptedPostboxEncodingContainer: AnyObject { + func makeData() -> Data +} diff --git a/submodules/Postbox/Sources/Utils/Encoder/AdaptedPostboxKeyedEncodingContainer.swift b/submodules/Postbox/Sources/Utils/Encoder/AdaptedPostboxKeyedEncodingContainer.swift new file mode 100644 index 0000000000..d37daf70e0 --- /dev/null +++ b/submodules/Postbox/Sources/Utils/Encoder/AdaptedPostboxKeyedEncodingContainer.swift @@ -0,0 +1,72 @@ +import Foundation + +extension _AdaptedPostboxEncoder { + final class KeyedContainer where Key: CodingKey { + var codingPath: [CodingKey] + var userInfo: [CodingUserInfoKey: Any] + + let encoder: PostboxEncoder + + init(codingPath: [CodingKey], userInfo: [CodingUserInfoKey : Any]) { + self.codingPath = codingPath + self.userInfo = userInfo + + self.encoder = PostboxEncoder() + } + + func makeData() -> Data { + return self.encoder.makeData() + } + } +} + +extension _AdaptedPostboxEncoder.KeyedContainer: KeyedEncodingContainerProtocol { + func encode(_ value: T, forKey key: Key) throws where T : Encodable { + let innerEncoder = _AdaptedPostboxEncoder() + try! value.encode(to: innerEncoder) + + self.encoder.encodeInnerObjectData(innerEncoder.data, forKey: key.stringValue) + } + + func encodeNil(forKey key: Key) throws { + self.encoder.encodeNil(forKey: key.stringValue) + } + + func encode(_ value: Int32, forKey key: Key) throws { + self.encoder.encodeInt32(value, forKey: key.stringValue) + } + + func encode(_ value: Int64, forKey key: Key) throws { + self.encoder.encodeInt64(value, forKey: key.stringValue) + } + + func encode(_ value: Bool, forKey key: Key) throws { + self.encoder.encodeBool(value, forKey: key.stringValue) + } + + func encode(_ value: Double, forKey key: Key) throws { + self.encoder.encodeDouble(value, forKey: key.stringValue) + } + + func encode(_ value: String, forKey key: Key) throws { + self.encoder.encodeString(value, forKey: key.stringValue) + } + + func nestedUnkeyedContainer(forKey key: Key) -> UnkeyedEncodingContainer { + preconditionFailure() + } + + func nestedContainer(keyedBy keyType: NestedKey.Type, forKey key: Key) -> KeyedEncodingContainer where NestedKey : CodingKey { + preconditionFailure() + } + + func superEncoder() -> Encoder { + preconditionFailure() + } + + func superEncoder(forKey key: Key) -> Encoder { + preconditionFailure() + } +} + +extension _AdaptedPostboxEncoder.KeyedContainer: AdaptedPostboxEncodingContainer {} diff --git a/submodules/Postbox/Sources/Utils/Encoder/AdaptedPostboxSingleValueEncodingContainer.swift b/submodules/Postbox/Sources/Utils/Encoder/AdaptedPostboxSingleValueEncodingContainer.swift new file mode 100644 index 0000000000..182231ff2d --- /dev/null +++ b/submodules/Postbox/Sources/Utils/Encoder/AdaptedPostboxSingleValueEncodingContainer.swift @@ -0,0 +1,85 @@ +import Foundation + +extension _AdaptedPostboxEncoder { + final class SingleValueContainer { + var codingPath: [CodingKey] + var userInfo: [CodingUserInfoKey: Any] + + init(codingPath: [CodingKey], userInfo: [CodingUserInfoKey : Any]) { + self.codingPath = codingPath + self.userInfo = userInfo + } + } +} + +extension _AdaptedPostboxEncoder.SingleValueContainer: SingleValueEncodingContainer { + func encodeNil() throws { + preconditionFailure() + } + + func encode(_ value: Bool) throws { + preconditionFailure() + } + + func encode(_ value: String) throws { + preconditionFailure() + } + + func encode(_ value: Double) throws { + preconditionFailure() + } + + func encode(_ value: Float) throws { + preconditionFailure() + } + + func encode(_ value: Int) throws { + preconditionFailure() + } + + func encode(_ value: Int8) throws { + preconditionFailure() + } + + func encode(_ value: Int16) throws { + preconditionFailure() + } + + func encode(_ value: Int32) throws { + preconditionFailure() + } + + func encode(_ value: Int64) throws { + preconditionFailure() + } + + func encode(_ value: UInt) throws { + preconditionFailure() + } + + func encode(_ value: UInt8) throws { + preconditionFailure() + } + + func encode(_ value: UInt16) throws { + preconditionFailure() + } + + func encode(_ value: UInt32) throws { + preconditionFailure() + } + + func encode(_ value: UInt64) throws { + preconditionFailure() + } + + func encode(_ value: T) throws where T : Encodable { + preconditionFailure() + } +} + +extension _AdaptedPostboxEncoder.SingleValueContainer: AdaptedPostboxEncodingContainer { + func makeData() -> Data { + preconditionFailure() + } +} diff --git a/submodules/Postbox/Sources/Utils/Encoder/AdaptedPostboxUnkeyedEncodingContainer.swift b/submodules/Postbox/Sources/Utils/Encoder/AdaptedPostboxUnkeyedEncodingContainer.swift new file mode 100644 index 0000000000..a11d351a0e --- /dev/null +++ b/submodules/Postbox/Sources/Utils/Encoder/AdaptedPostboxUnkeyedEncodingContainer.swift @@ -0,0 +1,44 @@ +import Foundation + +extension _AdaptedPostboxEncoder { + final class UnkeyedContainer { + var codingPath: [CodingKey] + var userInfo: [CodingUserInfoKey: Any] + var count: Int { + preconditionFailure() + } + + init(codingPath: [CodingKey], userInfo: [CodingUserInfoKey : Any]) { + self.codingPath = codingPath + self.userInfo = userInfo + } + } +} + +extension _AdaptedPostboxEncoder.UnkeyedContainer: UnkeyedEncodingContainer { + func encodeNil() throws { + preconditionFailure() + } + + func encode(_ value: T) throws where T : Encodable { + preconditionFailure() + } + + func nestedContainer(keyedBy keyType: NestedKey.Type) -> KeyedEncodingContainer where NestedKey : CodingKey { + preconditionFailure() + } + + func nestedUnkeyedContainer() -> UnkeyedEncodingContainer { + preconditionFailure() + } + + func superEncoder() -> Encoder { + preconditionFailure() + } +} + +extension _AdaptedPostboxEncoder.UnkeyedContainer: AdaptedPostboxEncodingContainer { + func makeData() -> Data { + preconditionFailure() + } +} diff --git a/submodules/Postbox/Sources/Utils/PostboxCodingAdapter.swift b/submodules/Postbox/Sources/Utils/PostboxCodingAdapter.swift new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/submodules/Postbox/Sources/Utils/PostboxCodingAdapter.swift @@ -0,0 +1 @@ + diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Utils/StringCodingKey.swift b/submodules/TelegramCore/Sources/TelegramEngine/Utils/StringCodingKey.swift new file mode 100644 index 0000000000..07cd9767c3 --- /dev/null +++ b/submodules/TelegramCore/Sources/TelegramEngine/Utils/StringCodingKey.swift @@ -0,0 +1,24 @@ + +public struct StringCodingKey: CodingKey, ExpressibleByStringLiteral { + public var stringValue: String + + public init?(stringValue: String) { + self.stringValue = stringValue + } + + public init(_ stringValue: String) { + self.stringValue = stringValue + } + + public init(stringLiteral: String) { + self.stringValue = stringLiteral + } + + public var intValue: Int? { + return nil + } + + public init?(intValue: Int) { + return nil + } +} diff --git a/submodules/TelegramUI/Sources/DeclareEncodables.swift b/submodules/TelegramUI/Sources/DeclareEncodables.swift index 2800e6a5f8..747c363eea 100644 --- a/submodules/TelegramUI/Sources/DeclareEncodables.swift +++ b/submodules/TelegramUI/Sources/DeclareEncodables.swift @@ -16,7 +16,6 @@ import ChatInterfaceState private var telegramUIDeclaredEncodables: Void = { declareEncodable(InAppNotificationSettings.self, f: { InAppNotificationSettings(decoder: $0) }) declareEncodable(ChatInterfaceState.self, f: { ChatInterfaceState(decoder: $0) }) - declareEncodable(ChatEmbeddedInterfaceState.self, f: { ChatEmbeddedInterfaceState(decoder: $0) }) declareEncodable(VideoLibraryMediaResource.self, f: { VideoLibraryMediaResource(decoder: $0) }) declareEncodable(LocalFileVideoMediaResource.self, f: { LocalFileVideoMediaResource(decoder: $0) }) declareEncodable(LocalFileGifMediaResource.self, f: { LocalFileGifMediaResource(decoder: $0) })