diff --git a/Sources/CDRCodable/Decoder/CDRDecoder.swift b/Sources/CDRCodable/Decoder/CDRDecoder.swift index 84ddebb..406ee29 100644 --- a/Sources/CDRCodable/Decoder/CDRDecoder.swift +++ b/Sources/CDRCodable/Decoder/CDRDecoder.swift @@ -1,8 +1,6 @@ import Foundation -/** - An object that decodes instances of a data type from CDRCodable objects. - */ +/// An object that decodes instances of a data type from Common Data Representation binary. final public class CDRDecoder { public init() {} @@ -12,45 +10,59 @@ final public class CDRDecoder { */ public var userInfo: [CodingUserInfoKey : Any] = [:] - /** - Returns a value of the type you specify, - decoded from a CDRCodable object. - - - Parameters: - - type: The type of the value to decode - from the supplied CDRCodable object. - - data: The CDRCodable object to decode. - - Throws: `DecodingError.dataCorrupted(_:)` - if the data is not valid CDRCodable. - */ + /// Returns a value of the type you specify, decoded from a Common Data Representation binary data + /// - Parameters: + /// - type: The type of the value to decode + /// - data: Common Data Representation binary data, little endian + /// - Returns: decoded object + /// - Throws: `DecodingError.dataCorrupted(_:)` if the data is not valid public func decode(_ type: T.Type, from data: Data) throws -> T where T : Decodable { - let decoder = _CDRDecoder(data: _CDRDecoder.DataStore(data: data)) - decoder.userInfo = self.userInfo - - return try T(from: decoder) + let dataStore = DataStore(data: data) + switch T.self { + case is [Double].Type: return try dataStore.readArray(Double.self) as! T + case is [Float].Type: return try dataStore.readArray(Float.self) as! T + case is [Int].Type: return try dataStore.readArray(Int.self) as! T + case is [Int8].Type: return try dataStore.readArray(Int8.self) as! T + case is [Int16].Type: return try dataStore.readArray(Int16.self) as! T + case is [Int32].Type: return try dataStore.readArray(Int32.self) as! T + case is [Int64].Type: return try dataStore.readArray(Int64.self) as! T + case is [UInt].Type: return try dataStore.readArray(UInt.self) as! T + case is [UInt8].Type: return try dataStore.readArray(UInt8.self) as! T + case is [UInt16].Type: return try dataStore.readArray(UInt16.self) as! T + case is [UInt32].Type: return try dataStore.readArray(UInt32.self) as! T + case is [UInt64].Type: return try dataStore.readArray(UInt64.self) as! T + case is Data.Type: + return try dataStore.readData() as! T + default: + let decoder = _CDRDecoder(dataStore: dataStore, userInfo: userInfo) + return try T(from: decoder) + } } } // MARK: - -final class _CDRDecoder { - - final class DataStore { - let data: Data - var index: Data.Index - init(data: Data) { - self.data = data - self.index = self.data.startIndex - } +final class DataStore { + let data: Data + var index: Data.Index + var getCodingPath: () -> [CodingKey] + init(data: Data) { + self.data = data + self.index = self.data.startIndex + self.getCodingPath = { [] } } +} + +final class _CDRDecoder { var codingPath: [CodingKey] = [] - var userInfo: [CodingUserInfoKey : Any] = [:] + let userInfo: [CodingUserInfoKey : Any] var container: _CDRDecodingContainer? var dataStore: DataStore - init(data: DataStore) { - self.dataStore = data + init(dataStore: DataStore, userInfo: [CodingUserInfoKey : Any]) { + self.dataStore = dataStore + self.userInfo = userInfo } } @@ -58,7 +70,7 @@ extension _CDRDecoder: Decoder { func container(keyedBy type: Key.Type) -> KeyedDecodingContainer where Key : CodingKey { precondition(self.container == nil) - let container = KeyedContainer(data: self.dataStore, codingPath: self.codingPath, userInfo: self.userInfo) + let container = KeyedContainer(dataStore: self.dataStore, codingPath: self.codingPath, userInfo: self.userInfo) self.container = container return KeyedDecodingContainer(container) @@ -67,7 +79,7 @@ extension _CDRDecoder: Decoder { func unkeyedContainer() throws -> UnkeyedDecodingContainer { precondition(self.container == nil) - let container = UnkeyedContainer(data: self.dataStore, codingPath: self.codingPath, userInfo: self.userInfo) + let container = try UnkeyedContainer(data: self.dataStore, codingPath: self.codingPath, userInfo: self.userInfo) self.container = container return container @@ -86,23 +98,23 @@ extension _CDRDecoder: Decoder { protocol _CDRDecodingContainer { var codingPath: [CodingKey] { get set } var userInfo: [CodingUserInfoKey : Any] { get } - var dataStore: _CDRDecoder.DataStore { get } + var dataStore: DataStore { get } } -extension _CDRDecodingContainer { +extension DataStore { @inline(__always) func align(to aligment: Int) { - let offset = self.dataStore.index % aligment + let offset = index % aligment if offset != 0 { - self.dataStore.index = self.dataStore.index.advanced(by: aligment - offset) + index = index.advanced(by: aligment - offset) } } @inline(__always) func checkDataEnd(_ length: Int) throws { - let nextIndex = self.dataStore.index.advanced(by: length) - guard nextIndex <= self.dataStore.data.endIndex else { - let context = DecodingError.Context(codingPath: self.codingPath, debugDescription: "Unexpected end of data") + let nextIndex = index.advanced(by: length) + guard nextIndex <= data.endIndex else { + let context = DecodingError.Context(codingPath: getCodingPath(), debugDescription: "Unexpected end of data") throw DecodingError.dataCorrupted(context) } } @@ -117,16 +129,16 @@ extension _CDRDecodingContainer { @inline(__always) func read(_ type: T.Type) throws -> T where T : Numeric { let aligment = MemoryLayout.alignment - let offset = self.dataStore.index % aligment + let offset = index % aligment if offset != 0 { - self.dataStore.index = self.dataStore.index.advanced(by: aligment - offset) + index = index.advanced(by: aligment - offset) } let stride = MemoryLayout.stride try checkDataEnd(stride) defer { - dataStore.index = dataStore.index.advanced(by: stride) + index = index.advanced(by: stride) } - return dataStore.data.withUnsafeBytes{ $0.load(fromByteOffset: dataStore.index, as: T.self) } + return data.withUnsafeBytes{ $0.load(fromByteOffset: index, as: T.self) } } @inline(__always) @@ -134,10 +146,10 @@ extension _CDRDecodingContainer { let size = MemoryLayout.size let count = try readCheckBlockCount(of: size) defer { - dataStore.index = dataStore.index.advanced(by: count * size) + index = index.advanced(by: count * size) } return Array.init(unsafeUninitializedCapacity: count) { - dataStore.data.copyBytes(to: $0, from: dataStore.index...) + data.copyBytes(to: $0, from: index...) $1 = count } } @@ -147,10 +159,10 @@ extension _CDRDecodingContainer { let length = try readCheckBlockCount(of: 1) defer { - dataStore.index = dataStore.index.advanced(by: length) + index = index.advanced(by: length) } - guard let string = String(data: dataStore.data[dataStore.index.. Data { let length = try readCheckBlockCount(of: 1) defer { - dataStore.index = dataStore.index.advanced(by: length) + index = index.advanced(by: length) } - return dataStore.data.subdata(in: dataStore.index.. Bool { - try read(UInt8.self) != 0 + try dataStore.read(UInt8.self) != 0 } func decode(_ type: String.Type, forKey key: Key) throws -> String { - try readString() + try dataStore.readString() } func decode(_ type: T.Type, forKey key: Key) throws -> T where T : Numeric & Decodable { - try read(T.self) + try dataStore.read(T.self) } func decode(_ type: T.Type, forKey key: Key) throws -> T where T : Decodable { switch T.self { - case is [Double].Type: return try readArray(Double.self) as! T - case is [Float].Type: return try readArray(Float.self) as! T - case is [Int].Type: return try readArray(Int.self) as! T - case is [Int8].Type: return try readArray(Int8.self) as! T - case is [Int16].Type: return try readArray(Int16.self) as! T - case is [Int32].Type: return try readArray(Int32.self) as! T - case is [Int64].Type: return try readArray(Int64.self) as! T - case is [UInt].Type: return try readArray(UInt.self) as! T - case is [UInt8].Type: return try readArray(UInt8.self) as! T - case is [UInt16].Type: return try readArray(UInt16.self) as! T - case is [UInt32].Type: return try readArray(UInt32.self) as! T - case is [UInt64].Type: return try readArray(UInt64.self) as! T + case is [Double].Type: return try dataStore.readArray(Double.self) as! T + case is [Float].Type: return try dataStore.readArray(Float.self) as! T + case is [Int].Type: return try dataStore.readArray(Int.self) as! T + case is [Int8].Type: return try dataStore.readArray(Int8.self) as! T + case is [Int16].Type: return try dataStore.readArray(Int16.self) as! T + case is [Int32].Type: return try dataStore.readArray(Int32.self) as! T + case is [Int64].Type: return try dataStore.readArray(Int64.self) as! T + case is [UInt].Type: return try dataStore.readArray(UInt.self) as! T + case is [UInt8].Type: return try dataStore.readArray(UInt8.self) as! T + case is [UInt16].Type: return try dataStore.readArray(UInt16.self) as! T + case is [UInt32].Type: return try dataStore.readArray(UInt32.self) as! T + case is [UInt64].Type: return try dataStore.readArray(UInt64.self) as! T case is Data.Type: - return try readData() as! T + return try dataStore.readData() as! T default: - let decoder = _CDRDecoder(data: self.dataStore) + let decoder = _CDRDecoder(dataStore: dataStore, userInfo: userInfo) return try T(from: decoder) } } func nestedUnkeyedContainer(forKey key: Key) throws -> UnkeyedDecodingContainer { - let context = DecodingError.Context(codingPath: self.codingPath, debugDescription: "Nested unsupported") + let context = DecodingError.Context(codingPath: codingPath, debugDescription: "Nested unsupported") throw DecodingError.dataCorrupted(context) } func nestedContainer(keyedBy type: NestedKey.Type, forKey key: Key) throws -> KeyedDecodingContainer where NestedKey : CodingKey { - let context = DecodingError.Context(codingPath: self.codingPath, debugDescription: "Nested unsupported") + let context = DecodingError.Context(codingPath: codingPath, debugDescription: "Nested unsupported") throw DecodingError.dataCorrupted(context) } func superDecoder() throws -> Decoder { - return _CDRDecoder(data: self.dataStore) + return _CDRDecoder(dataStore: dataStore, userInfo: userInfo) } func superDecoder(forKey key: Key) throws -> Decoder { - let decoder = _CDRDecoder(data: self.dataStore) + let decoder = _CDRDecoder(dataStore: dataStore, userInfo: userInfo) decoder.codingPath = [key] return decoder } diff --git a/Sources/CDRCodable/Decoder/SingleValueDecodingContainer.swift b/Sources/CDRCodable/Decoder/SingleValueDecodingContainer.swift index c8575ba..8f16428 100644 --- a/Sources/CDRCodable/Decoder/SingleValueDecodingContainer.swift +++ b/Sources/CDRCodable/Decoder/SingleValueDecodingContainer.swift @@ -10,6 +10,9 @@ extension _CDRDecoder { self.codingPath = codingPath self.userInfo = userInfo self.dataStore = data + self.dataStore.getCodingPath = { + self.codingPath + } } } } @@ -20,23 +23,23 @@ extension _CDRDecoder.SingleValueContainer: SingleValueDecodingContainer { } func decode(_ type: Bool.Type) throws -> Bool { - try read(UInt8.self) != 0 + try dataStore.read(UInt8.self) != 0 } func decode(_ type: String.Type) throws -> String { - try readString() + try dataStore.readString() } func decode(_ type: T.Type) throws -> T where T : Numeric & Decodable { - try read(T.self) + try dataStore.read(T.self) } func decode(_ type: T.Type) throws -> T where T : Decodable { switch type { case is Data.Type: - return try readData() as! T + return try dataStore.readData() as! T default: - let decoder = _CDRDecoder(data: self.dataStore) + let decoder = _CDRDecoder(dataStore: dataStore, userInfo: userInfo) return try T(from: decoder) } } diff --git a/Sources/CDRCodable/Decoder/UnkeyedDecodingContainer.swift b/Sources/CDRCodable/Decoder/UnkeyedDecodingContainer.swift index 25070b6..9949161 100644 --- a/Sources/CDRCodable/Decoder/UnkeyedDecodingContainer.swift +++ b/Sources/CDRCodable/Decoder/UnkeyedDecodingContainer.swift @@ -6,28 +6,20 @@ extension _CDRDecoder { var userInfo: [CodingUserInfoKey: Any] var dataStore: DataStore - lazy var count: Int? = { - do { - return Int(try read(UInt32.self)) - } catch { - return nil - } - } () - - var currentIndex: Int = 0 - - - init(data: DataStore, codingPath: [CodingKey], userInfo: [CodingUserInfoKey : Any]) { + init(data: DataStore, codingPath: [CodingKey], userInfo: [CodingUserInfoKey : Any]) throws { self.codingPath = codingPath self.userInfo = userInfo self.dataStore = data + self.dataStore.getCodingPath = { + self.codingPath + } + count = Int(try dataStore.read(UInt32.self)) } - + + var count: Int? + var currentIndex: Int = 0 var isAtEnd: Bool { - guard let count = self.count else { - return true - } - return currentIndex >= count + currentIndex >= count! } } } @@ -39,7 +31,7 @@ extension _CDRDecoder.UnkeyedContainer: UnkeyedDecodingContainer { func decode(_ type: T.Type) throws -> T where T : Decodable { defer { self.currentIndex += 1 } - let decoder = _CDRDecoder(data: self.dataStore) + let decoder = _CDRDecoder(dataStore: self.dataStore, userInfo: userInfo) let value = try T(from: decoder) return value } @@ -56,7 +48,7 @@ extension _CDRDecoder.UnkeyedContainer: UnkeyedDecodingContainer { } func superDecoder() throws -> Decoder { - return _CDRDecoder(data: self.dataStore) + return _CDRDecoder(dataStore: dataStore, userInfo: userInfo) } }