Skip to content

Commit

Permalink
Add more tests. Fix end of data check on array decode.
Browse files Browse the repository at this point in the history
  • Loading branch information
DimaRU committed Mar 6, 2024
1 parent b1faa02 commit 31b6a69
Show file tree
Hide file tree
Showing 10 changed files with 324 additions and 24 deletions.
11 changes: 6 additions & 5 deletions Sources/CDRCodable/Decoder/CDRDecoder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -127,9 +127,9 @@ extension DataStore {
}

@inline(__always)
func readCheckBlockCount(of size: Int) throws -> Int {
func readCheckBlockCount() throws -> Int {
let length = Int(try read(UInt32.self))
try checkDataEnd(length * size)
try checkDataEnd(length)
return length
}

Expand All @@ -146,7 +146,7 @@ extension DataStore {

@inline(__always)
func readString() throws -> String {
let length = try readCheckBlockCount(of: 1)
let length = try readCheckBlockCount()

defer {
cursor = cursor.advanced(by: length)
Expand All @@ -160,7 +160,7 @@ extension DataStore {

@inline(__always)
func readData() throws -> Data {
let length = try readCheckBlockCount(of: 1)
let length = try readCheckBlockCount()
defer {
cursor = cursor.advanced(by: length)
}
Expand All @@ -170,8 +170,9 @@ extension DataStore {
@inline(__always)
func readArray<T>(_ type: T.Type) throws -> [T] where T: Numeric {
let size = MemoryLayout<T>.size
let count = try readCheckBlockCount(of: size)
let count = Int(try read(UInt32.self))
align(to: MemoryLayout<T>.alignment)
try checkDataEnd(count * size)
defer {
cursor = cursor.advanced(by: count * size)
}
Expand Down
2 changes: 1 addition & 1 deletion Sources/CDRCodable/Decoder/KeyedDecodingContainer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ extension _CDRDecoder.KeyedContainer: KeyedDecodingContainerProtocol {
stringArray.append(try dataStore.readString())
}
default:
let context = DecodingError.Context(codingPath: self.codingPath, debugDescription: "Non Array<Numberic> type as fixed array")
let context = DecodingError.Context(codingPath: self.codingPath, debugDescription: "Fixed array type is't Array<Numeric> or Array<String>")
throw DecodingError.typeMismatch(T.self, context)
}
}
Expand Down
13 changes: 3 additions & 10 deletions Sources/CDRCodable/Encoder/CDREncoder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -49,12 +49,7 @@ final public class CDREncoder {
}

// Final data aligment
let aligment = dataStore.data.count % 4
if aligment != 0 {
for _ in 0..<4-aligment {
dataStore.data.append(0)
}
}
dataStore.align(MemoryLayout<Int32>.alignment)
return dataStore.data
}
}
Expand Down Expand Up @@ -115,6 +110,7 @@ extension _CDREncoder: Encoder {
}

extension _CDREncoder.DataStore {
@inline(__always)
func align(_ alignment: Int) {
let offset = self.data.count % alignment
if offset != 0 {
Expand All @@ -135,10 +131,7 @@ extension _CDREncoder.DataStore {
@inline(__always)
func write<T>(value: T) where T: Numeric {
let alignment = MemoryLayout<T>.alignment
let offset = self.data.count % alignment
if offset != 0 {
self.data.append(contentsOf: Array(repeating: UInt8(0), count: alignment - offset))
}
align(alignment)
self.data.append(contentsOf: value.bytes)
}

Expand Down
2 changes: 1 addition & 1 deletion Sources/CDRCodable/Encoder/KeyedEncodingContainer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ extension _CDREncoder.KeyedContainer: KeyedEncodingContainerProtocol {
@inline(__always)
private func writeString(_ s: String) throws {
guard let data = s.data(using: .utf8) else {
let context = EncodingError.Context(codingPath: self.codingPath, debugDescription: "Cannot encode string using UTF-8 encoding.")
let context = EncodingError.Context(codingPath: self.codingPath, debugDescription: "Can't encode string using UTF-8 encoding.")
throw EncodingError.invalidValue(s, context)
}
let length = data.count + 1
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ extension _CDREncoder.SingleValueContainer: SingleValueEncodingContainer {

func encode(_ value: String) throws {
guard let data = value.data(using: .utf8) else {
let context = EncodingError.Context(codingPath: self.codingPath, debugDescription: "Cannot encode string using UTF-8 encoding.")
let context = EncodingError.Context(codingPath: self.codingPath, debugDescription: "Can't encode string using UTF-8 encoding.")
throw EncodingError.invalidValue(value, context)
}
let length = data.count + 1
Expand Down
103 changes: 103 additions & 0 deletions Tests/CDRCodableTests/CDRCodableDecodingArrayKeyedTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
/////
//// CDRCodableDecodingArrayKeyedTests.swift
/// Copyright © 2024 Dmitriy Borovikov. All rights reserved.
//

import XCTest
@testable import CDRCodable

class CDRCodableDecodingArrayKeyedTests: XCTestCase {
var decoder: CDRDecoder!

override func setUp() {
self.decoder = CDRDecoder()
}

func testDecodeData() {
struct TestStruct: Codable, Equatable {
let b: UInt8
let s: Data
}
let value = TestStruct(b: 0x55, s: "hello".data(using: .utf8)!)
let data = Data([0x55, 0, 0, 0, 5, 0, 0, 0, 0x68, 0x65, 0x6C, 0x6C, 0x6F, 0, 0, 0])
let decoded = try! decoder.decode(TestStruct.self, from: data)
XCTAssertEqual(value, decoded)
}

func testDecodeArray8() {
struct TestStruct: Codable, Equatable {
let b: UInt8
let a: [Int8]
}
let value = TestStruct(b: 0x55, a: [-1, 2, 3])
let data = Data([0x55, 0, 0, 0, 3, 0, 0, 0, 0xff, 2, 3, 0])
let decoded = try! decoder.decode(TestStruct.self, from: data)
XCTAssertEqual(value, decoded)
}

func testDecodeArray16() {
struct TestStruct: Codable, Equatable {
let b: UInt8
let a: [Int16]
}
let value = TestStruct(b: 0x55, a: [-1, 2, 3])
let data = Data([0x55, 0, 0, 0, 3, 0, 0, 0, 0xff, 0xff, 2, 0, 3, 0, 0, 0])
let decoded = try! decoder.decode(TestStruct.self, from: data)
XCTAssertEqual(value, decoded)
}

func testDecodeArray32() {
struct TestStruct: Codable, Equatable {
let b: UInt8
let a: [Int32]
}
let value = TestStruct(b: 0x55, a: [-1, 2, 3])
let data = Data([0x55, 0, 0, 0, 3, 0, 0, 0, 0xff, 0xff, 0xff, 0xff, 2, 0, 0, 0, 3, 0, 0, 0])
let decoded = try! decoder.decode(TestStruct.self, from: data)
XCTAssertEqual(value, decoded)
}

func testDecodeArray64() {
struct TestStruct: Codable, Equatable {
let a: [Int64]
}
let value = TestStruct(a: [-1, 2, 3])
let data = Data([3, 0, 0, 0,
0, 0, 0, 0,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
2, 0, 0, 0, 0, 0, 0, 0,
3, 0, 0, 0, 0, 0, 0, 0])
let decoded = try! decoder.decode(TestStruct.self, from: data)
XCTAssertEqual(value, decoded)
}

func testDecodeArrayFloat() {
struct TestStruct: Codable, Equatable {
let b: UInt8
let a: [Float]
}
let value = TestStruct(b: 0x55, a: [1, 2, 3])
let data = Data([0x55, 0, 0, 0,
3, 0, 0, 0,
0, 0, 0x80, 0x3f,
0, 0, 0, 0x40,
0, 0, 0x40, 0x40])
let decoded = try! decoder.decode(TestStruct.self, from: data)
XCTAssertEqual(value, decoded)
}

func testDecodeArrayDouble() {
struct TestStruct: Codable, Equatable {
let a: [Double]
}
let value = TestStruct(a: [1, 2, 3])
let data = Data([3, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0xf0, 0x3f,
0, 0, 0, 0, 0, 0, 0, 0x40,
0, 0, 0, 0, 0, 0, 8, 0x40])
let decoded = try! decoder.decode(TestStruct.self, from: data)
XCTAssertEqual(value, decoded)
}

}
2 changes: 1 addition & 1 deletion Tests/CDRCodableTests/CDRCodableDecodingArrayTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ class CDRCodableDecodingArrayTests: XCTestCase {
XCTAssertEqual(value, [1, 2, 3])
}

func testDecodeArrayDoble() {
func testDecodeArrayDouble() {
let data = Data([3, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0xf0, 0x3f,
Expand Down
108 changes: 108 additions & 0 deletions Tests/CDRCodableTests/CDRCodableDecodingErrorTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
/////
//// CDRCodableDecodingErrorTests.swift
/// Copyright © 2024 Dmitriy Borovikov. All rights reserved.
//

import XCTest
@testable import CDRCodable

class CDRCodableDecodingErrorTests: XCTestCase {
var decoder: CDRDecoder!

override func setUp() {
self.decoder = CDRDecoder()
}

func testDecodeInt32EOD() {
let data = Data([0, 0, 0])
XCTAssertThrowsError(try decoder.decode(Int32.self, from: data), "Unexpected end of data") {
guard case DecodingError.dataCorrupted = $0 else {
XCTFail("Wrong error type")
return
}
}
}

func testDecodeStringCountEOD() {
let data = Data([0, 0, 0])
XCTAssertThrowsError(try decoder.decode(String.self, from: data), "Unexpected end of data") {
guard case DecodingError.dataCorrupted = $0 else {
XCTFail("Wrong error type")
return
}
}
}

func testDecodeStringEOD() {
let data = Data([2, 0, 0, 0, 0x55])
XCTAssertThrowsError(try decoder.decode(String.self, from: data), "Unexpected end of data") {
guard case DecodingError.dataCorrupted = $0 else {
XCTFail("Wrong error type")
return
}
}
}

func testDecodeArrayEOD() {
let data = Data([1, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0])
XCTAssertThrowsError(try decoder.decode([Int64].self, from: data), "Unexpected end of data") {
guard case DecodingError.dataCorrupted = $0 else {
XCTFail("Wrong error type")
return
}
}
}

func testDecodeArrayStructEOD() {
let data = Data([1, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0])
struct TestStruct: Decodable {
let a: [Int64]
}
XCTAssertThrowsError(try decoder.decode(TestStruct.self, from: data), "Unexpected end of data") {
guard case DecodingError.dataCorrupted = $0 else {
XCTFail("Wrong error type")
return
}
}
}

func testDecodeFixedArrayEOD() {
let data = Data([0, 0, 0, 0,
0, 0, 0])
struct TestStruct: Decodable {
let b: UInt8
let a: [Int32]
enum CodingKeys: Int, CodingKey {
case b = 0
case a = 0x10001
}
}
XCTAssertThrowsError(try decoder.decode(TestStruct.self, from: data), "Unexpected end of data") {
guard case DecodingError.dataCorrupted = $0 else {
XCTFail("Wrong error type")
return
}
}
}

func testDecodeFixedArrayWrongDef() {
let data = Data([0, 0, 0, 0,
0, 0, 0, 0])
struct TestStruct: Decodable {
let a: [Data]
enum CodingKeys: Int, CodingKey {
case a = 0x10001
}
}
XCTAssertThrowsError(try decoder.decode(TestStruct.self, from: data), "Unexpected end of data") {
guard case DecodingError.typeMismatch = $0 else {
XCTFail("Wrong error type")
return
}
}
}
}
Loading

0 comments on commit 31b6a69

Please sign in to comment.