diff --git a/Sources/CDRCodable/Encoder/KeyedEncodingContainer.swift b/Sources/CDRCodable/Encoder/KeyedEncodingContainer.swift index 414f241..275069b 100644 --- a/Sources/CDRCodable/Encoder/KeyedEncodingContainer.swift +++ b/Sources/CDRCodable/Encoder/KeyedEncodingContainer.swift @@ -19,9 +19,19 @@ extension _CDREncoder { } extension _CDREncoder.KeyedContainer: KeyedEncodingContainerProtocol { - func encodeNil(forKey key: Key) throws { + @inline(__always) + private func encodeNumericArray(count: Int, size: Int, pointer: UnsafeRawBufferPointer) throws { + guard let uint32 = UInt32(exactly: count) else { + let context = EncodingError.Context(codingPath: self.codingPath, debugDescription: "Cannot encode data of length \(count).") + throw EncodingError.invalidValue(count, context) + } + write(value: uint32) + self.data.data.append(pointer.baseAddress!.assumingMemoryBound(to: UInt8.self), count: count * size) } + func encodeNil(forKey key: Key) throws { + } + func encode(_ value: Bool, forKey key: Key) throws { switch value { case false: @@ -61,20 +71,30 @@ extension _CDREncoder.KeyedContainer: KeyedEncodingContainerProtocol { } func encode(_ value: T, forKey key: Key) throws where T : Encodable { - if let value = value as? Data { - let length = value.count - if let uint32 = UInt32(exactly: length) { - write(value: uint32) - write(data: value) - } else { + switch value { + case let value as [Int]: try encodeNumericArray(count: value.count, size: MemoryLayout.size, pointer: value.withUnsafeBytes{ $0 }) + case let value as [Int8]: try encodeNumericArray(count: value.count, size: MemoryLayout.size, pointer: value.withUnsafeBytes{ $0 }) + case let value as [Int16]: try encodeNumericArray(count: value.count, size: MemoryLayout.size, pointer: value.withUnsafeBytes{ $0 }) + case let value as [Int32]: try encodeNumericArray(count: value.count, size: MemoryLayout.size, pointer: value.withUnsafeBytes{ $0 }) + case let value as [Int64]: try encodeNumericArray(count: value.count, size: MemoryLayout.size, pointer: value.withUnsafeBytes{ $0 }) + case let value as [UInt]: try encodeNumericArray(count: value.count, size: MemoryLayout.size, pointer: value.withUnsafeBytes{ $0 }) + case let value as [UInt8]: try encodeNumericArray(count: value.count, size: MemoryLayout.size, pointer: value.withUnsafeBytes{ $0 }) + case let value as [UInt16]: try encodeNumericArray(count: value.count, size: MemoryLayout.size, pointer: value.withUnsafeBytes{ $0 }) + case let value as [UInt32]: try encodeNumericArray(count: value.count, size: MemoryLayout.size, pointer: value.withUnsafeBytes{ $0 }) + case let value as [UInt64]: try encodeNumericArray(count: value.count, size: MemoryLayout.size, pointer: value.withUnsafeBytes{ $0 }) + case let value as [Float]: try encodeNumericArray(count: value.count, size: MemoryLayout.size, pointer: value.withUnsafeBytes{ $0 }) + case let value as [Double]: try encodeNumericArray(count: value.count, size: MemoryLayout.size, pointer: value.withUnsafeBytes{ $0 }) + case let value as Data: + guard let uint32 = UInt32(exactly: value.count) else { let context = EncodingError.Context(codingPath: self.codingPath, debugDescription: "Cannot encode data of length \(value.count).") - throw EncodingError.invalidValue(value, context) + throw EncodingError.invalidValue(value.count, context) } - return + write(value: uint32) + write(data: value) + default: + let encoder = _CDREncoder(data: self.data) + try value.encode(to: encoder) } - - let encoder = _CDREncoder(data: self.data) - try value.encode(to: encoder) } private func nestedSingleValueContainer(forKey key: Key) -> SingleValueEncodingContainer { diff --git a/Tests/CDRCodableTests/CDRCodableEncodingTests.swift b/Tests/CDRCodableTests/CDRCodableEncodingTests.swift index 078d93a..52f651b 100644 --- a/Tests/CDRCodableTests/CDRCodableEncodingTests.swift +++ b/Tests/CDRCodableTests/CDRCodableEncodingTests.swift @@ -54,4 +54,16 @@ class CDRCodableEncodingTests: XCTestCase { let value = try! encoder.encode(data) XCTAssertEqual(value, Data([5, 0, 0, 0, 0x68, 0x65, 0x6C, 0x6C, 0x6F, 0, 0, 0])) } + + func testEncodeStruct() { + struct TestStruct: Codable { + let i: Int16 + let s: String + let a: [Int16] + } + + let value = TestStruct(i: 0x55, s: "Test string", a: [1,2,3,4,5,6,7,8,9]) + let data = try! encoder.encode(value) + XCTAssertEqual(data, Data([0x55, 0, 0, 0, 0x0c, 0, 0, 0, 0x54, 0x65, 0x73, 0x74, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0, 0x09, 0, 0, 0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 0, 7, 0, 8, 0, 9, 0, 0, 0])) + } } diff --git a/Tests/CDRCodableTests/CDRCodablePerformanceTests.swift b/Tests/CDRCodableTests/CDRCodablePerformanceTests.swift index 3c3a818..3a55040 100644 --- a/Tests/CDRCodableTests/CDRCodablePerformanceTests.swift +++ b/Tests/CDRCodableTests/CDRCodablePerformanceTests.swift @@ -8,16 +8,16 @@ class CDRCodablePerformanceTests: XCTestCase { let key: String let stamp: UInt64 } - + let encoder = CDREncoder() + let decoder = CDRDecoder() + func testDataDecodeEncode() { let testData = Data(repeating: 1, count: 40 * 1024) let imageData = ImageData(image: testData, key: "Key", stamp: 123456789) - let encoder = CDREncoder() let cdrData = try! encoder.encode(imageData) XCTAssertEqual(cdrData.count, 40984) - let decoder = CDRDecoder() let decodedImage = try! decoder.decode(ImageData.self, from: cdrData) XCTAssertEqual(decodedImage.key, "Key") } @@ -26,13 +26,10 @@ class CDRCodablePerformanceTests: XCTestCase { let testData = Data(repeating: 1, count: 40 * 1024) let imageData = ImageData(image: testData, key: "Key", stamp: 123456789) - let encoder = CDREncoder() let cdrData = try! encoder.encode(imageData) - let decoder = CDRDecoder() - self.measure { - for _ in 1...100 { + for _ in 1...1000 { let decodedImage = try! decoder.decode(ImageData.self, from: cdrData) XCTAssertEqual(decodedImage.key, "Key") } @@ -43,14 +40,37 @@ class CDRCodablePerformanceTests: XCTestCase { let testData = Data(repeating: 1, count: 40 * 1024) let imageData = ImageData(image: testData, key: "Key", stamp: 123456789) - let encoder = CDREncoder() - self.measure { - for _ in 1...100 { + for _ in 1...1000 { let cdrData = try! encoder.encode(imageData) XCTAssertEqual(cdrData.count, 40984) } } } + + func testPerformanceUnkeyedArrayEncode() { + let testArray = [Int16].init(repeating: 55, count: 40 * 1024) + + self.measure { + for _ in 1...100 { + let cdrData = try! encoder.encode(testArray) + XCTAssertEqual(cdrData.count, 81924) + } + } + } + + func testPerformanceKeyedArrayEncode() { + struct TestStruct: Codable { + let a: [Int16] + } + let testStruct = TestStruct(a: .init(repeating: 55, count: 40 * 1024)) + + self.measure { + for _ in 1...100 { + let cdrData = try! encoder.encode(testStruct) + XCTAssertEqual(cdrData.count, 81924) + } + } + } }