From 61dcffe849af0d47aef2fe0fa26e5c7891b8c462 Mon Sep 17 00:00:00 2001 From: Sebastien Metrot Date: Thu, 23 Sep 2021 01:51:22 +0200 Subject: [PATCH] initial commit for supporting multiple CodingKeys in a class inheritance context --- .../Decoder/MessagePackDecoder.swift | 13 +++-- .../Encoder/KeyedEncodingContainer.swift | 58 +++++++++++++------ .../Encoder/MessagePackEncoder.swift | 22 ++++--- .../SingleValueEncodingContainer.swift | 21 ++++--- .../Encoder/UnkeyedEncodingContainer.swift | 26 +++++---- 5 files changed, 92 insertions(+), 48 deletions(-) diff --git a/Sources/MessagePack/Decoder/MessagePackDecoder.swift b/Sources/MessagePack/Decoder/MessagePackDecoder.swift index 46e4b05..931bfca 100644 --- a/Sources/MessagePack/Decoder/MessagePackDecoder.swift +++ b/Sources/MessagePack/Decoder/MessagePackDecoder.swift @@ -78,8 +78,9 @@ final class _MessagePackDecoder { var codingPath: [CodingKey] = [] var userInfo: [CodingUserInfoKey : Any] = [:] - - var container: MessagePackDecodingContainer? + + var containers: [MessagePackDecodingContainer] = [] + var container: MessagePackDecodingContainer? { containers.last } fileprivate var data: Data init(data: Data) { @@ -89,14 +90,14 @@ final class _MessagePackDecoder { extension _MessagePackDecoder: Decoder { fileprivate func assertCanCreateContainer() { - precondition(self.container == nil) +// 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 + self.containers.append(container) return KeyedDecodingContainer(container) } @@ -105,7 +106,7 @@ extension _MessagePackDecoder: Decoder { assertCanCreateContainer() let container = UnkeyedContainer(data: self.data, codingPath: self.codingPath, userInfo: self.userInfo) - self.container = container + self.containers.append(container) return container } @@ -114,7 +115,7 @@ extension _MessagePackDecoder: Decoder { assertCanCreateContainer() let container = SingleValueContainer(data: self.data, codingPath: self.codingPath, userInfo: self.userInfo) - self.container = container + self.containers.append(container) return container } diff --git a/Sources/MessagePack/Encoder/KeyedEncodingContainer.swift b/Sources/MessagePack/Encoder/KeyedEncodingContainer.swift index a657455..73cfe62 100644 --- a/Sources/MessagePack/Encoder/KeyedEncodingContainer.swift +++ b/Sources/MessagePack/Encoder/KeyedEncodingContainer.swift @@ -1,19 +1,41 @@ import Foundation +protocol KeyedStorage { + var keyStorage: KeyStorage { get } +} + +class KeyStorage { + var values: [AnyCodingKey: _MessagePackEncodingContainer] = [:] + + var codingPath: [CodingKey] = [] + var userInfo: [CodingUserInfoKey: Any] = [:] +} + extension _MessagePackEncoder { - final class KeyedContainer where Key: CodingKey { - private var storage: [AnyCodingKey: _MessagePackEncodingContainer] = [:] - - var codingPath: [CodingKey] - var userInfo: [CodingUserInfoKey: Any] - + final class KeyedContainer: KeyedStorage where Key: CodingKey { + internal var keyStorage = KeyStorage() + var codingPath: [CodingKey] { + get { + keyStorage.codingPath + } + + set { + keyStorage.codingPath = newValue + } + } + func nestedCodingPath(forKey key: CodingKey) -> [CodingKey] { - return self.codingPath + [key] + return self.keyStorage.codingPath + [key] } + init(codingPath: [CodingKey], userInfo: [CodingUserInfoKey : Any]) { - self.codingPath = codingPath - self.userInfo = userInfo + self.keyStorage.codingPath = codingPath + self.keyStorage.userInfo = userInfo + } + + init(keyStorage: KeyStorage) { + self.keyStorage = keyStorage } } } @@ -30,21 +52,21 @@ extension _MessagePackEncoder.KeyedContainer: KeyedEncodingContainerProtocol { } private func nestedSingleValueContainer(forKey key: Key) -> SingleValueEncodingContainer { - let container = _MessagePackEncoder.SingleValueContainer(codingPath: self.nestedCodingPath(forKey: key), userInfo: self.userInfo) - self.storage[AnyCodingKey(key)] = container + let container = _MessagePackEncoder.SingleValueContainer(codingPath: self.nestedCodingPath(forKey: key), userInfo: self.keyStorage.userInfo) + self.keyStorage.values[AnyCodingKey(key)] = container return container } func nestedUnkeyedContainer(forKey key: Key) -> UnkeyedEncodingContainer { - let container = _MessagePackEncoder.UnkeyedContainer(codingPath: self.nestedCodingPath(forKey: key), userInfo: self.userInfo) - self.storage[AnyCodingKey(key)] = container + let container = _MessagePackEncoder.UnkeyedContainer(codingPath: self.nestedCodingPath(forKey: key), userInfo: self.keyStorage.userInfo) + self.keyStorage.values[AnyCodingKey(key)] = container return container } func nestedContainer(keyedBy keyType: NestedKey.Type, forKey key: Key) -> KeyedEncodingContainer where NestedKey : CodingKey { - let container = _MessagePackEncoder.KeyedContainer(codingPath: self.nestedCodingPath(forKey: key), userInfo: self.userInfo) - self.storage[AnyCodingKey(key)] = container + let container = _MessagePackEncoder.KeyedContainer(codingPath: self.nestedCodingPath(forKey: key), userInfo: self.keyStorage.userInfo) + self.keyStorage.values[AnyCodingKey(key)] = container return KeyedEncodingContainer(container) } @@ -62,7 +84,7 @@ extension _MessagePackEncoder.KeyedContainer: _MessagePackEncodingContainer { var data: Data { var data = Data() - let length = storage.count + let length = keyStorage.values.count if let uint16 = UInt16(exactly: length) { if length <= 15 { data.append(0x80 + UInt8(length)) @@ -77,8 +99,8 @@ extension _MessagePackEncoder.KeyedContainer: _MessagePackEncodingContainer { fatalError() } - for (key, container) in self.storage { - let keyContainer = _MessagePackEncoder.SingleValueContainer(codingPath: self.codingPath, userInfo: self.userInfo) + for (key, container) in self.keyStorage.values { + let keyContainer = _MessagePackEncoder.SingleValueContainer(codingPath: self.codingPath, userInfo: self.keyStorage.userInfo) try! keyContainer.encode(key.stringValue) data.append(keyContainer.data) diff --git a/Sources/MessagePack/Encoder/MessagePackEncoder.swift b/Sources/MessagePack/Encoder/MessagePackEncoder.swift index d2b6a08..31cd29a 100644 --- a/Sources/MessagePack/Encoder/MessagePackEncoder.swift +++ b/Sources/MessagePack/Encoder/MessagePackEncoder.swift @@ -49,7 +49,7 @@ extension MessagePackEncoder: TopLevelEncoder { // MARK: - -protocol _MessagePackEncodingContainer { +protocol _MessagePackEncodingContainer: KeyedStorage { var data: Data { get } } @@ -71,12 +71,20 @@ extension _MessagePackEncoder: Encoder { } 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) +// assertCanCreateContainer() + + guard let container = container else { + let container = KeyedContainer(codingPath: self.codingPath, userInfo: self.userInfo) + self.container = container + + return KeyedEncodingContainer(container) + } + + let newContainer = KeyedContainer(keyStorage: container.keyStorage) + self.container = newContainer + + + return KeyedEncodingContainer(newContainer) } func unkeyedContainer() -> UnkeyedEncodingContainer { diff --git a/Sources/MessagePack/Encoder/SingleValueEncodingContainer.swift b/Sources/MessagePack/Encoder/SingleValueEncodingContainer.swift index 9006b3e..8635d65 100644 --- a/Sources/MessagePack/Encoder/SingleValueEncodingContainer.swift +++ b/Sources/MessagePack/Encoder/SingleValueEncodingContainer.swift @@ -3,21 +3,28 @@ import Foundation extension _MessagePackEncoder { final class SingleValueContainer { private var storage: Data = Data() - + var keyStorage: KeyStorage = KeyStorage() + var codingPath: [CodingKey] { + get { + keyStorage.codingPath + } + + set { + keyStorage.codingPath = newValue + } + } + fileprivate var canEncodeNewValue = true fileprivate func checkCanEncode(value: Any?) throws { guard self.canEncodeNewValue else { - let context = EncodingError.Context(codingPath: self.codingPath, debugDescription: "Attempt to encode value through single value container when previously value already encoded.") + let context = EncodingError.Context(codingPath: self.keyStorage.codingPath, debugDescription: "Attempt to encode value through single value container when previously value already encoded.") throw EncodingError.invalidValue(value as Any, context) } } - var codingPath: [CodingKey] - var userInfo: [CodingUserInfoKey: Any] - init(codingPath: [CodingKey], userInfo: [CodingUserInfoKey : Any]) { - self.codingPath = codingPath - self.userInfo = userInfo + self.keyStorage.codingPath = codingPath + self.keyStorage.userInfo = userInfo } } } diff --git a/Sources/MessagePack/Encoder/UnkeyedEncodingContainer.swift b/Sources/MessagePack/Encoder/UnkeyedEncodingContainer.swift index 46477ed..df6f14c 100644 --- a/Sources/MessagePack/Encoder/UnkeyedEncodingContainer.swift +++ b/Sources/MessagePack/Encoder/UnkeyedEncodingContainer.swift @@ -3,22 +3,28 @@ import Foundation extension _MessagePackEncoder { final class UnkeyedContainer { private var storage: [_MessagePackEncodingContainer] = [] - + internal var keyStorage = KeyStorage() + var codingPath: [CodingKey] { + get { + keyStorage.codingPath + } + + set { + keyStorage.codingPath = newValue + } + } + var count: Int { return storage.count } - var codingPath: [CodingKey] - var nestedCodingPath: [CodingKey] { return self.codingPath + [AnyCodingKey(intValue: self.count)!] } - var userInfo: [CodingUserInfoKey: Any] - init(codingPath: [CodingKey], userInfo: [CodingUserInfoKey : Any]) { - self.codingPath = codingPath - self.userInfo = userInfo + self.keyStorage.codingPath = codingPath + self.keyStorage.userInfo = userInfo } } } @@ -35,21 +41,21 @@ extension _MessagePackEncoder.UnkeyedContainer: UnkeyedEncodingContainer { } private func nestedSingleValueContainer() -> SingleValueEncodingContainer { - let container = _MessagePackEncoder.SingleValueContainer(codingPath: self.nestedCodingPath, userInfo: self.userInfo) + let container = _MessagePackEncoder.SingleValueContainer(codingPath: self.nestedCodingPath, userInfo: self.keyStorage.userInfo) self.storage.append(container) return container } func nestedContainer(keyedBy keyType: NestedKey.Type) -> KeyedEncodingContainer where NestedKey : CodingKey { - let container = _MessagePackEncoder.KeyedContainer(codingPath: self.nestedCodingPath, userInfo: self.userInfo) + let container = _MessagePackEncoder.KeyedContainer(codingPath: self.nestedCodingPath, userInfo: self.keyStorage.userInfo) self.storage.append(container) return KeyedEncodingContainer(container) } func nestedUnkeyedContainer() -> UnkeyedEncodingContainer { - let container = _MessagePackEncoder.UnkeyedContainer(codingPath: self.nestedCodingPath, userInfo: self.userInfo) + let container = _MessagePackEncoder.UnkeyedContainer(codingPath: self.nestedCodingPath, userInfo: self.keyStorage.userInfo) self.storage.append(container) return container