From 5eda964dd1a4d42748c20119a40578d50c3ffc49 Mon Sep 17 00:00:00 2001 From: Alsey Coleman Miller Date: Tue, 24 Dec 2019 10:48:50 -0500 Subject: [PATCH 1/9] Added `TLVOutputFormatting` --- Sources/Encoder.swift | 1 + Sources/Format.swift | 15 +++++++++++++++ 2 files changed, 16 insertions(+) diff --git a/Sources/Encoder.swift b/Sources/Encoder.swift index e3c0062..91c3267 100644 --- a/Sources/Encoder.swift +++ b/Sources/Encoder.swift @@ -21,6 +21,7 @@ public struct TLVEncoder { /// Format for numeric values. public var numericFormat: TLVNumericFormat = .littleEndian + public var outputFormatting: TLVOutputFormatting = .default /// Format for UUID values. public var uuidFormat: TLVUUIDFormat = .bytes diff --git a/Sources/Format.swift b/Sources/Format.swift index 21fefd5..78ed04a 100644 --- a/Sources/Format.swift +++ b/Sources/Format.swift @@ -10,6 +10,21 @@ import Foundation /// TLV Numeric Encoding Format public enum TLVNumericFormat: Equatable, Hashable { +/// The output formatting options that determine the readability, size, and element order of an encoded TLV object. +public struct TLVOutputFormatting: Equatable, Hashable { + + /// The output formatting option that sorts keys in numerical order. + public var sortedKeys: Bool +} + +public extension TLVOutputFormatting { + + /// The default TLV output formatting options. + static var `default`: TLVOutputFormatting { + return .init(sortedKeys: true) + } +} + case bigEndian case littleEndian From dbe54f916ec310681ee765c3c642f119aeb4fdd1 Mon Sep 17 00:00:00 2001 From: Alsey Coleman Miller Date: Tue, 24 Dec 2019 10:50:01 -0500 Subject: [PATCH 2/9] Renamed formatting options --- Sources/Decoder.swift | 17 ++++-- Sources/Encoder.swift | 68 +++++++++++++++++++---- Sources/Format.swift | 52 ++++++++++++----- Tests/TLVCodingTests/TLVCodingTests.swift | 10 ++-- 4 files changed, 112 insertions(+), 35 deletions(-) diff --git a/Sources/Decoder.swift b/Sources/Decoder.swift index 86032b2..f8b01e3 100644 --- a/Sources/Decoder.swift +++ b/Sources/Decoder.swift @@ -20,13 +20,13 @@ public struct TLVDecoder { public var log: ((String) -> ())? /// Format for numeric values. - public var numericFormat: TLVNumericFormat = .littleEndian + public var numericFormat: TLVNumericFormatting = .littleEndian /// Format for UUID values. - public var uuidFormat: TLVUUIDFormat = .bytes + public var uuidFormat: TLVUUIDFormatting = .bytes /// Format for Date values. - public var dateFormat: TLVDateFormat = .secondsSince1970 + public var dateFormat: TLVDateFormatting = .secondsSince1970 // MARK: - Initialization @@ -206,7 +206,14 @@ internal extension TLVDecoder { internal extension TLVDecoder.Decoder { - typealias Options = TLVOptions + struct Options { + + let numericFormat: TLVNumericFormatting + + let uuidFormat: TLVUUIDFormatting + + let dateFormat: TLVDateFormatting + } } // MARK: - Coding Key @@ -320,7 +327,7 @@ private extension TLVDecoder.Decoder { case .iso8601: guard #available(macOS 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *) else { fatalError("ISO8601DateFormatter is unavailable on this platform.") } - return try unboxDate(data, using: TLVDateFormat.iso8601Formatter) + return try unboxDate(data, using: TLVDateFormatting.iso8601Formatter) case let .formatted(formatter): return try unboxDate(data, using: formatter) } diff --git a/Sources/Encoder.swift b/Sources/Encoder.swift index 91c3267..e46f9b8 100644 --- a/Sources/Encoder.swift +++ b/Sources/Encoder.swift @@ -20,14 +20,16 @@ public struct TLVEncoder { public var log: ((String) -> ())? /// Format for numeric values. - public var numericFormat: TLVNumericFormat = .littleEndian public var outputFormatting: TLVOutputFormatting = .default + /// Format for numeric values. + public var numericFormatting: TLVNumericFormatting = .default + /// Format for UUID values. - public var uuidFormat: TLVUUIDFormat = .bytes + public var uuidFormatting: TLVUUIDFormatting = .default /// Format for Date values. - public var dateFormat: TLVDateFormat = .secondsSince1970 + public var dateFormatting: TLVDateFormatting = .default // MARK: - Initialization @@ -40,10 +42,12 @@ public struct TLVEncoder { log?("Will encode \(String(reflecting: T.self))") let options = Encoder.Options( - numericFormat: numericFormat, - uuidFormat: uuidFormat, - dateFormat: dateFormat + outputFormatting: outputFormatting, + numericFormatting: numericFormatting, + uuidFormatting: uuidFormatting, + dateFormatting: dateFormatting ) + let encoder = Encoder(userInfo: userInfo, log: log, options: options) try value.encode(to: encoder) assert(encoder.stack.containers.count == 1) @@ -64,6 +68,39 @@ public struct TLVEncoder { } } +// MARK: - Deprecated + +public extension TLVEncoder { + + @available(*, deprecated, message: "Renamed to numericFormatting") + var numericFormat: TLVNumericFormat { + get { return numericFormatting } + set { numericFormatting = newValue } + } + + @available(*, deprecated, message: "Renamed to uuidFormatting") + var uuidFormat: TLVUUIDFormat { + get { return uuidFormatting } + set { uuidFormatting = newValue } + } + + @available(*, deprecated, message: "Renamed to dateFormatting") + var dateFormat: TLVDateFormat { + get { return dateFormatting } + set { dateFormatting = newValue } + } +} + +// MARK: - Combine + +#if canImport(Combine) +import Combine + +extension TLVEncoder: TopLevelEncoder { } +#endif + +// MARK: - Encoder + internal extension TLVEncoder { final class Encoder: Swift.Encoder { @@ -135,7 +172,16 @@ internal extension TLVEncoder { internal extension TLVEncoder.Encoder { - typealias Options = TLVOptions + struct Options { + + public let outputFormatting: TLVOutputFormatting + + public let numericFormatting: TLVNumericFormatting + + public let uuidFormatting: TLVUUIDFormatting + + public let dateFormatting: TLVDateFormatting + } } internal extension TLVEncoder.Encoder { @@ -164,7 +210,7 @@ internal extension TLVEncoder.Encoder { func boxNumeric (_ value: T) -> Data { let numericValue: T - switch options.numericFormat { + switch options.numericFormatting { case .bigEndian: numericValue = value.bigEndian case .littleEndian: @@ -206,7 +252,7 @@ private extension TLVEncoder.Encoder { func boxUUID(_ uuid: UUID) -> Data { - switch options.uuidFormat { + switch options.uuidFormatting { case .bytes: return Data(uuid) case .string: @@ -216,7 +262,7 @@ private extension TLVEncoder.Encoder { func boxDate(_ date: Date) -> Data { - switch options.dateFormat { + switch options.dateFormatting { case .secondsSince1970: return boxDouble(date.timeIntervalSince1970) case .millisecondsSince1970: @@ -224,7 +270,7 @@ private extension TLVEncoder.Encoder { case .iso8601: guard #available(macOS 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *) else { fatalError("ISO8601DateFormatter is unavailable on this platform.") } - return boxDate(date, using: TLVDateFormat.iso8601Formatter) + return boxDate(date, using: TLVDateFormatting.iso8601Formatter) case let .formatted(formatter): return boxDate(date, using: formatter) } diff --git a/Sources/Format.swift b/Sources/Format.swift index 78ed04a..e94d19f 100644 --- a/Sources/Format.swift +++ b/Sources/Format.swift @@ -8,8 +8,6 @@ import Foundation -/// TLV Numeric Encoding Format -public enum TLVNumericFormat: Equatable, Hashable { /// The output formatting options that determine the readability, size, and element order of an encoded TLV object. public struct TLVOutputFormatting: Equatable, Hashable { @@ -25,20 +23,44 @@ public extension TLVOutputFormatting { } } +/// TLV number formatting (endianness). +public enum TLVNumericFormatting: Equatable, Hashable { - case bigEndian case littleEndian + case bigEndian +} + +public extension TLVNumericFormatting { + + /// The default TLV endianness for binary representation of numbers. + static var `default`: TLVNumericFormatting { + return .littleEndian + } } -/// TLV UUID Encoding Format -public enum TLVUUIDFormat: Equatable, Hashable { +@available(*, deprecated, message: "Renamed to TLVNumericFormatting") +public typealias TLVNumericFormat = TLVNumericFormatting + +/// TLV `UUID` Encoding Format +public enum TLVUUIDFormatting: Equatable, Hashable { case bytes case string } -/// TLV Date Encoding Format -public enum TLVDateFormat: Equatable { +public extension TLVUUIDFormatting { + + /// The default TLV `UUID` format. + static var `default`: TLVUUIDFormatting { + return .bytes + } +} + +@available(*, deprecated, message: "Renamed to TLVUUIDFormatting") +public typealias TLVUUIDFormat = TLVUUIDFormatting + +/// TLV `Date` Encoding Format +public enum TLVDateFormatting: Equatable { /// Encodes dates in terms of seconds since midnight UTC on January 1, 1970. case secondsSince1970 @@ -54,18 +76,20 @@ public enum TLVDateFormat: Equatable { case formatted(DateFormatter) } -internal struct TLVOptions { +public extension TLVDateFormatting { - let numericFormat: TLVNumericFormat - - let uuidFormat: TLVUUIDFormat - - let dateFormat: TLVDateFormat + /// The default TLV `Date` format. + static var `default`: TLVDateFormatting { + return .secondsSince1970 + } } +@available(*, deprecated, message: "Renamed to TLVDateFormatting") +public typealias TLVDateFormat = TLVDateFormatting + // MARK: - Formatters -internal extension TLVDateFormat { +internal extension TLVDateFormatting { @available(macOS 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *) static let iso8601Formatter: ISO8601DateFormatter = { diff --git a/Tests/TLVCodingTests/TLVCodingTests.swift b/Tests/TLVCodingTests/TLVCodingTests.swift index a3b9804..c631a57 100644 --- a/Tests/TLVCodingTests/TLVCodingTests.swift +++ b/Tests/TLVCodingTests/TLVCodingTests.swift @@ -210,7 +210,7 @@ final class TLVCodingTests: XCTestCase { func testUUID() { - let formats: [TLVUUIDFormat] = [.bytes, .string] + let formats: [TLVUUIDFormatting] = [.bytes, .string] for format in formats { @@ -223,7 +223,7 @@ final class TLVCodingTests: XCTestCase { var encodedData = Data() var encoder = TLVEncoder() - encoder.uuidFormat = format + encoder.uuidFormatting = format encoder.log = { print("Encoder:", $0) } do { encodedData = try encoder.encode(value) @@ -254,7 +254,7 @@ final class TLVCodingTests: XCTestCase { dateFormatter.calendar = Calendar(identifier: .gregorian) dateFormatter.timeZone = TimeZone(secondsFromGMT: 0) - var formats: [TLVDateFormat] = [.secondsSince1970, .millisecondsSince1970, .formatted(dateFormatter)] + var formats: [TLVDateFormatting] = [.secondsSince1970, .millisecondsSince1970, .formatted(dateFormatter)] if #available(macOS 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *) { formats.append(.iso8601) @@ -273,7 +273,7 @@ final class TLVCodingTests: XCTestCase { var encodedData = Data() var encoder = TLVEncoder() - encoder.dateFormat = format + encoder.dateFormatting = format encoder.log = { print("Encoder:", $0) } do { encodedData = try encoder.encode(value) @@ -313,7 +313,7 @@ final class TLVCodingTests: XCTestCase { ) var encoder = TLVEncoder() - encoder.dateFormat = .secondsSince1970 + encoder.dateFormatting = .secondsSince1970 encoder.log = { print("Encoder:", $0) } XCTAssertEqual(try encoder.encode(value), try encoder.encode(rawValue)) } From ef85683d86c0633c94facdf1433c201fef05d1d8 Mon Sep 17 00:00:00 2001 From: Alsey Coleman Miller Date: Tue, 24 Dec 2019 11:00:13 -0500 Subject: [PATCH 3/9] Renamed formatting options --- Sources/Decoder.swift | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/Sources/Decoder.swift b/Sources/Decoder.swift index f8b01e3..42d975a 100644 --- a/Sources/Decoder.swift +++ b/Sources/Decoder.swift @@ -110,6 +110,29 @@ public struct TLVDecoder { } } +// MARK: - Deprecated + +public extension TLVDecoder { + + @available(*, deprecated, message: "Renamed to numericFormatting") + var numericFormat: TLVNumericFormat { + get { return numericFormatting } + set { numericFormatting = newValue } + } + + @available(*, deprecated, message: "Renamed to uuidFormatting") + var uuidFormat: TLVUUIDFormat { + get { return uuidFormatting } + set { uuidFormatting = newValue } + } + + @available(*, deprecated, message: "Renamed to dateFormatting") + var dateFormat: TLVDateFormat { + get { return dateFormatting } + set { dateFormatting = newValue } + } +} + internal extension TLVDecoder { final class Decoder: Swift.Decoder { From 1b6d31bce4c4e4a3d2a768b31ec8ea45026281c0 Mon Sep 17 00:00:00 2001 From: Alsey Coleman Miller Date: Tue, 24 Dec 2019 11:02:40 -0500 Subject: [PATCH 4/9] Added Combine support --- Sources/Decoder.swift | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Sources/Decoder.swift b/Sources/Decoder.swift index 42d975a..459bb4d 100644 --- a/Sources/Decoder.swift +++ b/Sources/Decoder.swift @@ -133,6 +133,16 @@ public extension TLVDecoder { } } +// MARK: - Combine + +#if canImport(Combine) +import Combine + +extension TLVDecoder: TopLevelDecoder { } +#endif + +// MARK: - Decoder + internal extension TLVDecoder { final class Decoder: Swift.Decoder { From 8588ba35eb40e0adf95960e15022c0b8e0359715 Mon Sep 17 00:00:00 2001 From: Alsey Coleman Miller Date: Tue, 24 Dec 2019 11:02:52 -0500 Subject: [PATCH 5/9] Renamed formatting options --- Sources/Decoder.swift | 24 +++++++++++------------ Tests/TLVCodingTests/TLVCodingTests.swift | 4 ++-- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/Sources/Decoder.swift b/Sources/Decoder.swift index 459bb4d..926ee12 100644 --- a/Sources/Decoder.swift +++ b/Sources/Decoder.swift @@ -20,13 +20,13 @@ public struct TLVDecoder { public var log: ((String) -> ())? /// Format for numeric values. - public var numericFormat: TLVNumericFormatting = .littleEndian + public var numericFormatting: TLVNumericFormatting = .default /// Format for UUID values. - public var uuidFormat: TLVUUIDFormatting = .bytes + public var uuidFormatting: TLVUUIDFormatting = .default /// Format for Date values. - public var dateFormat: TLVDateFormatting = .secondsSince1970 + public var dateFormatting: TLVDateFormatting = .default // MARK: - Initialization @@ -41,9 +41,9 @@ public struct TLVDecoder { let items = try decode(data) let options = Decoder.Options( - numericFormat: numericFormat, - uuidFormat: uuidFormat, - dateFormat: dateFormat + numericFormatting: numericFormatting, + uuidFormatting: uuidFormatting, + dateFormatting: dateFormatting ) let decoder = Decoder(referencing: .items(items), @@ -241,11 +241,11 @@ internal extension TLVDecoder.Decoder { struct Options { - let numericFormat: TLVNumericFormatting + let numericFormatting: TLVNumericFormatting - let uuidFormat: TLVUUIDFormatting + let uuidFormatting: TLVUUIDFormatting - let dateFormat: TLVDateFormatting + let dateFormatting: TLVDateFormatting } } @@ -283,7 +283,7 @@ internal extension TLVDecoder.Decoder { func unboxNumeric (_ data: Data, as type: T.Type) throws -> T { var numericValue = try unbox(data, as: type) - switch options.numericFormat { + switch options.numericFormatting { case .bigEndian: numericValue = T.init(bigEndian: numericValue) case .littleEndian: @@ -331,7 +331,7 @@ private extension TLVDecoder.Decoder { func unboxUUID(_ data: Data) throws -> UUID { - switch options.uuidFormat { + switch options.uuidFormatting { case .bytes: guard data.count == MemoryLayout.size else { throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: codingPath, debugDescription: "Invalud number of bytes (\(data.count)) for UUID")) @@ -350,7 +350,7 @@ private extension TLVDecoder.Decoder { func unboxDate(_ data: Data) throws -> Date { - switch options.dateFormat { + switch options.dateFormatting { case .secondsSince1970: let timeInterval = try unboxDouble(data) return Date(timeIntervalSince1970: timeInterval) diff --git a/Tests/TLVCodingTests/TLVCodingTests.swift b/Tests/TLVCodingTests/TLVCodingTests.swift index c631a57..66562fe 100644 --- a/Tests/TLVCodingTests/TLVCodingTests.swift +++ b/Tests/TLVCodingTests/TLVCodingTests.swift @@ -234,7 +234,7 @@ final class TLVCodingTests: XCTestCase { } var decoder = TLVDecoder() - decoder.uuidFormat = format + decoder.uuidFormatting = format decoder.log = { print("Decoder:", $0) } do { let decodedValue = try decoder.decode(CustomEncodable.self, from: encodedData) @@ -284,7 +284,7 @@ final class TLVCodingTests: XCTestCase { } var decoder = TLVDecoder() - decoder.dateFormat = format + decoder.dateFormatting = format decoder.log = { print("Decoder:", $0) } do { let decodedValue = try decoder.decode(CustomEncodable.self, from: encodedData) From bbbd074f67a1384fd3b5e49905b52bb27e2c8940 Mon Sep 17 00:00:00 2001 From: Alsey Coleman Miller Date: Tue, 24 Dec 2019 11:17:12 -0500 Subject: [PATCH 6/9] Implemented sorting keys --- Sources/Encoder.swift | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/Sources/Encoder.swift b/Sources/Encoder.swift index e46f9b8..9b1df9e 100644 --- a/Sources/Encoder.swift +++ b/Sources/Encoder.swift @@ -327,13 +327,21 @@ internal extension TLVEncoder.Encoder { final class ItemsContainer { - var items = [TLVItem]() + private(set) var items = [TLVItem]() init() { } var data: Data { return Data(items) } + + @inline(__always) + func append(_ item: TLVItem, options: Options) { + items.append(item) + if options.outputFormatting.sortedKeys { + items.sort(by: { $0.type.rawValue < $1.type.rawValue }) + } + } } final class ItemContainer { @@ -504,7 +512,7 @@ internal final class TLVKeyedContainer : KeyedEncodingContainerP let type = try encoder.typeCode(for: key, value: value) let item = TLVItem(type: type, value: data) - self.container.items.append(item) + self.container.append(item, options: encoder.options) } } @@ -671,7 +679,7 @@ internal final class TLVUnkeyedEncodingContainer: UnkeyedEncodingContainer { let item = TLVItem(type: index, value: data) // write - self.container.items.append(item) + self.container.append(item, options: encoder.options) } } From e6098a35a16b86380db2eba9fcd59f37711811ab Mon Sep 17 00:00:00 2001 From: Alsey Coleman Miller Date: Tue, 24 Dec 2019 11:20:57 -0500 Subject: [PATCH 7/9] Updated documentation --- Sources/Item.swift | 10 +++------- Sources/TLVCodable.swift | 1 + 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/Sources/Item.swift b/Sources/Item.swift index 02f6ddf..6290a1b 100644 --- a/Sources/Item.swift +++ b/Sources/Item.swift @@ -13,12 +13,15 @@ import Foundation */ public struct TLVItem: Equatable, Hashable { + /// TLV code public var type: TLVTypeCode + /// TLV data payload public var value: Data public init(type: TLVTypeCode, value: Data) { + assert(value.count <= UInt8.max) self.type = type self.value = value } @@ -27,20 +30,13 @@ public struct TLVItem: Equatable, Hashable { public extension TLVItem { var length: UInt8 { - return UInt8(value.count) } } public extension TLVItem { - init?(data: Data) { - - fatalError() - } - var data: Data { - return Data(self) } } diff --git a/Sources/TLVCodable.swift b/Sources/TLVCodable.swift index b8290b8..aa0f982 100644 --- a/Sources/TLVCodable.swift +++ b/Sources/TLVCodable.swift @@ -17,6 +17,7 @@ public protocol TLVDecodable: Decodable { init?(tlvData: Data) } +/// TLV Encodable type public protocol TLVEncodable: Encodable { var tlvData: Data { get } From ec5ef860de653258e405288d74d73ff4c05aa012 Mon Sep 17 00:00:00 2001 From: Alsey Coleman Miller Date: Tue, 24 Dec 2019 22:44:37 -0500 Subject: [PATCH 8/9] Updated unit tests --- Tests/TLVCodingTests/TLVCodingTests.swift | 41 ++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/Tests/TLVCodingTests/TLVCodingTests.swift b/Tests/TLVCodingTests/TLVCodingTests.swift index 66562fe..79c95e4 100644 --- a/Tests/TLVCodingTests/TLVCodingTests.swift +++ b/Tests/TLVCodingTests/TLVCodingTests.swift @@ -17,7 +17,8 @@ final class TLVCodingTests: XCTestCase { ("testCodingKeys", testCodingKeys), ("testUUID", testUUID), ("testDate", testDate), - ("testDateSecondsSince1970", testDateSecondsSince1970) + ("testDateSecondsSince1970", testDateSecondsSince1970), + ("testOutputFormatting", testOutputFormatting) ] func testCodable() { @@ -317,6 +318,29 @@ final class TLVCodingTests: XCTestCase { encoder.log = { print("Encoder:", $0) } XCTAssertEqual(try encoder.encode(value), try encoder.encode(rawValue)) } + + func testOutputFormatting() { + + var encoder = TLVEncoder() + encoder.outputFormatting.sortedKeys = true + encoder.log = { print("Encoder:", $0) } + + let value = ProvisioningState( + state: .provisioning, + result: .success + ) + + let valueUnordered = ProvisioningStateUnordered( + result: value.result, + state: value.state + ) + + XCTAssertEqual(try encoder.encode(value), try encoder.encode(valueUnordered)) + + encoder.outputFormatting.sortedKeys = false + + XCTAssertNotEqual(try encoder.encode(value), try encoder.encode(valueUnordered)) + } } // MARK: - Supporting Types @@ -384,6 +408,21 @@ extension ProvisioningState.CodingKeys { } } +public struct ProvisioningStateUnordered: Codable, Equatable { + + typealias CodingKeys = ProvisioningState.CodingKeys + + public var result: ProvisioningState.Result + public var state: ProvisioningState.State + + public func encode(to encoder: Encoder) throws { + + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(result, forKey: .result) + try container.encode(state, forKey: .state) + } +} + public struct Profile: Codable, Equatable { public var person: Person From 79319dc75b62608f4a00e9d0d0ff517ea7f857fb Mon Sep 17 00:00:00 2001 From: Alsey Coleman Miller Date: Tue, 24 Dec 2019 22:52:40 -0500 Subject: [PATCH 9/9] Optimized encoding arrays --- Sources/Encoder.swift | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Sources/Encoder.swift b/Sources/Encoder.swift index 9b1df9e..874a4ac 100644 --- a/Sources/Encoder.swift +++ b/Sources/Encoder.swift @@ -342,6 +342,11 @@ internal extension TLVEncoder.Encoder { items.sort(by: { $0.type.rawValue < $1.type.rawValue }) } } + + @inline(__always) + fileprivate func append(_ item: TLVItem) { + items.append(item) + } } final class ItemContainer { @@ -679,7 +684,7 @@ internal final class TLVUnkeyedEncodingContainer: UnkeyedEncodingContainer { let item = TLVItem(type: index, value: data) // write - self.container.append(item, options: encoder.options) + self.container.append(item) // already sorted } }