diff --git a/README.md b/README.md index 02b20e1..acef24a 100644 --- a/README.md +++ b/README.md @@ -92,7 +92,6 @@ do { ```swift import CBOR -import Foundation // Define your data structures struct Person: Codable { diff --git a/Sources/CBOR/CBOR.swift b/Sources/CBOR/CBOR.swift index 56305c4..16ee984 100644 --- a/Sources/CBOR/CBOR.swift +++ b/Sources/CBOR/CBOR.swift @@ -1,13 +1,9 @@ -#if canImport(FoundationEssentials) -import FoundationEssentials -#else -import Foundation -#endif - #if canImport(Darwin) import Darwin #elseif canImport(Glibc) import Glibc +#elseif canImport(Musl) +import Musl #elseif os(Windows) import ucrt #endif @@ -15,7 +11,7 @@ import ucrt // MARK: - CBOR Type /// A CBOR value -public indirect enum CBOR: Equatable { +public enum CBOR: Equatable, Sendable { /// A positive unsigned integer case unsignedInt(UInt64) /// A negative integer @@ -29,7 +25,7 @@ public indirect enum CBOR: Equatable { /// A map of CBOR key-value pairs case map([CBORMapPair]) /// A tagged CBOR value - case tagged(UInt64, CBOR) + indirect case tagged(UInt64, CBOR) /// A simple value case simple(UInt8) /// A boolean value @@ -42,14 +38,18 @@ public indirect enum CBOR: Equatable { case float(Double) /// Encodes the CBOR value to bytes + public func encode(into output: inout [UInt8]) { + _encode(self, into: &output) + } + public func encode() -> [UInt8] { var output: [UInt8] = [] - _encode(self, into: &output) + encode(into: &output) return output } /// Decodes a CBOR value from bytes - public static func decode(_ bytes: [UInt8]) throws -> CBOR { + public static func decode(_ bytes: [UInt8]) throws(CBORError) -> CBOR { var reader = CBORReader(data: bytes) let value = try _decode(reader: &reader) @@ -67,7 +67,7 @@ public indirect enum CBOR: Equatable { /// - Parameters: /// - key: The key of the pair /// - value: The value of the pair -public struct CBORMapPair: Equatable { +public struct CBORMapPair: Equatable, Sendable { public let key: CBOR public let value: CBOR @@ -84,6 +84,7 @@ public struct CBORMapPair: Equatable { /// - Parameters: /// - value: The CBOR value to encode /// - output: The output buffer to write the encoded bytes to +@inline(__always) private func _encode(_ value: CBOR, into output: inout [UInt8]) { switch value { case .unsignedInt(let u): @@ -103,11 +104,9 @@ private func _encode(_ value: CBOR, into output: inout [UInt8]) { encodeUnsigned(major: 2, value: UInt64(bytes.count), into: &output) output.append(contentsOf: bytes) case .textString(let string): - if let utf8 = string.data(using: .utf8) { - let bytes = [UInt8](utf8) - encodeUnsigned(major: 3, value: UInt64(bytes.count), into: &output) - output.append(contentsOf: bytes) - } + let bytes = [UInt8](string.utf8) + encodeUnsigned(major: 3, value: UInt64(bytes.count), into: &output) + output.append(contentsOf: bytes) case .array(let array): encodeUnsigned(major: 4, value: UInt64(array.count), into: &output) for item in array { @@ -138,8 +137,7 @@ private func _encode(_ value: CBOR, into output: inout [UInt8]) { case .float(let f): // Encode as IEEE 754 double-precision float output.append(0xfb) - var value = f - withUnsafeBytes(of: &value) { bytes in + withUnsafeBytes(of: f) { bytes in // Append bytes in big-endian order for i in (0..<8).reversed() { output.append(bytes[i]) @@ -192,7 +190,7 @@ private func encodeUnsigned(major: UInt8, value: UInt64, into output: inout [UIn /// - reader: The reader to decode from /// - Returns: The decoded CBOR value /// - Throws: A `CBORError` if the decoding fails -private func _decode(reader: inout CBORReader) throws -> CBOR { +private func _decode(reader: inout CBORReader) throws(CBORError) -> CBOR { let initial = try reader.readByte() // Check for break marker (0xff) @@ -214,16 +212,16 @@ private func _decode(reader: inout CBORReader) throws -> CBOR { case 2: // byte string let length = try readUIntValue(additional: additional, reader: &reader) - guard length <= UInt64(Int.max) else { - throw CBORError.lengthTooLarge(length) + guard length <= reader.maximumStringLength else { + throw CBORError.lengthTooLarge(length, maximum: reader.maximumStringLength) } - return .byteString(try reader.readBytes(Int(length))) + return .byteString(Array(try reader.readBytes(Int(length)))) case 3: // text string let length = try readUIntValue(additional: additional, reader: &reader) - guard length <= UInt64(Int.max) else { - throw CBORError.lengthTooLarge(length) + guard length <= reader.maximumStringLength else { + throw CBORError.lengthTooLarge(length, maximum: reader.maximumStringLength) } let bytes = try reader.readBytes(Int(length)) @@ -236,12 +234,12 @@ private func _decode(reader: inout CBORReader) throws -> CBOR { case 4: // array let count = try readUIntValue(additional: additional, reader: &reader) - guard count <= UInt64(Int.max) else { - throw CBORError.lengthTooLarge(count) + guard count <= reader.maximumElementCount else { + throw CBORError.lengthTooLarge(count, maximum: reader.maximumElementCount) } var items: [CBOR] = [] - for _ in 0.. CBOR { case 5: // map let count = try readUIntValue(additional: additional, reader: &reader) - guard count <= UInt64(Int.max) else { - throw CBORError.lengthTooLarge(count) + guard count <= reader.maximumElementCount else { + throw CBORError.lengthTooLarge(count, maximum: reader.maximumElementCount) } var pairs: [CBORMapPair] = [] - for _ in 0.. CBOR { let simple = try reader.readByte() return .simple(simple) case 25: // IEEE 754 Half-Precision Float (16 bits) - let bytes = try reader.readBytes(2) - let bits = UInt16(bytes[0]) << 8 | UInt16(bytes[1]) + let bits = try reader.readBigEndianInteger(UInt16.self) // Convert half-precision to double let sign = (bits & 0x8000) != 0 let exponent = Int((bits & 0x7C00) >> 10) @@ -296,15 +293,12 @@ private func _decode(reader: inout CBORReader) throws -> CBOR { return .float(sign ? -value : value) case 26: // IEEE 754 Single-Precision Float (32 bits) - let bytes = try reader.readBytes(4) - let bits = UInt32(bytes[0]) << 24 | UInt32(bytes[1]) << 16 | UInt32(bytes[2]) << 8 | UInt32(bytes[3]) + let bits = try reader.readBigEndianInteger(UInt32.self) let float = Float(bitPattern: bits) return .float(Double(float)) case 27: // IEEE 754 Double-Precision Float (64 bits) - let bytes = try reader.readBytes(8) - let bits = UInt64(bytes[0]) << 56 | UInt64(bytes[1]) << 48 | UInt64(bytes[2]) << 40 | UInt64(bytes[3]) << 32 | - UInt64(bytes[4]) << 24 | UInt64(bytes[5]) << 16 | UInt64(bytes[6]) << 8 | UInt64(bytes[7]) + let bits = try reader.readBigEndianInteger(UInt64.self) let double = Double(bitPattern: bits) return .float(double) @@ -321,27 +315,21 @@ private func _decode(reader: inout CBORReader) throws -> CBOR { } /// Reads an unsigned integer value based on the additional information. -private func readUIntValue(additional: UInt8, reader: inout CBORReader) throws -> UInt64 { - // Check for indefinite length first - if additional == 31 { - throw CBORError.indefiniteLengthNotSupported - } - - if additional < 24 { +private func readUIntValue(additional: UInt8, reader: inout CBORReader) throws(CBORError) -> UInt64 { + switch additional { + case 0...23: return UInt64(additional) - } else if additional == 24 { + case 24: return UInt64(try reader.readByte()) - } else if additional == 25 { - let bytes = try reader.readBytes(2) - return UInt64(bytes[0]) << 8 | UInt64(bytes[1]) - } else if additional == 26 { - let bytes = try reader.readBytes(4) - return UInt64(bytes[0]) << 24 | UInt64(bytes[1]) << 16 | UInt64(bytes[2]) << 8 | UInt64(bytes[3]) - } else if additional == 27 { - let bytes = try reader.readBytes(8) - return UInt64(bytes[0]) << 56 | UInt64(bytes[1]) << 48 | UInt64(bytes[2]) << 40 | UInt64(bytes[3]) << 32 | - UInt64(bytes[4]) << 24 | UInt64(bytes[5]) << 16 | UInt64(bytes[6]) << 8 | UInt64(bytes[7]) - } else { + case 25: + return try UInt64(reader.readBigEndianInteger(UInt16.self)) + case 26: + return try UInt64(reader.readBigEndianInteger(UInt32.self)) + case 27: + return try reader.readBigEndianInteger(UInt64.self) + case 31: + throw CBORError.indefiniteLengthNotSupported + default: throw CBORError.invalidInitialByte(additional) } } \ No newline at end of file diff --git a/Sources/CBOR/CBORError.swift b/Sources/CBOR/CBORError.swift index 7cb74c5..78ee218 100644 --- a/Sources/CBOR/CBORError.swift +++ b/Sources/CBOR/CBORError.swift @@ -1,9 +1,3 @@ -#if canImport(FoundationEssentials) -import FoundationEssentials -#elseif canImport(Foundation) -import Foundation -#endif - // MARK: - Error Types /// Errors that can occur during CBOR encoding and decoding. @@ -11,7 +5,7 @@ import Foundation /// These errors provide detailed information about what went wrong during /// CBOR processing operations, helping developers diagnose and fix issues /// in their CBOR data or usage of the CBOR API. -public enum CBORError: Error { +public enum CBORError: Error, Equatable, Sendable { /// The input data is not valid CBOR. /// /// This error occurs when the decoder encounters data that doesn't conform to @@ -19,57 +13,12 @@ public enum CBORError: Error { /// incomplete data, or data encoded with a different format entirely. case invalidCBOR - /// Expected a specific type but found another. - /// - /// This error occurs when trying to decode a CBOR value as a specific type, - /// but the actual type of the value doesn't match the expected type. - /// - Parameters: - /// - expected: The type that was expected (e.g., "String", "Int", "Array") - /// - actual: The actual type that was found in the CBOR data - case typeMismatch(expected: String, actual: String) - - /// Array index out of bounds. - /// - /// This error occurs when attempting to access an element in a CBOR array - /// using an index that is outside the valid range for the array. - /// - Parameters: - /// - index: The requested index that was attempted to be accessed - /// - count: The actual number of elements in the array (valid indices are 0.. UInt8 { + mutating func readByte() throws(CBORError) -> UInt8 { guard index < data.count else { throw CBORError.prematureEnd } @@ -25,14 +21,25 @@ struct CBORReader { } /// Read a specified number of bytes from the input - mutating func readBytes(_ count: Int) throws -> [UInt8] { + mutating func readBytes(_ count: Int) throws(CBORError) -> ArraySlice { guard index + count <= data.count else { throw CBORError.prematureEnd } - let result = Array(data[index..(_ type: F.Type) throws(CBORError) -> F { + let bytes = try readBytes(MemoryLayout.size) + var value: F = 0 + return bytes.withUnsafeBytes { buffer in + withUnsafeMutableBytes(of: &value) { valuePtr in + valuePtr.copyMemory(from: buffer) + } + return value.bigEndian + } + } /// Check if there are more bytes to read var hasMoreBytes: Bool { @@ -48,12 +55,4 @@ struct CBORReader { var totalBytes: Int { return data.count } - - /// Skip a specified number of bytes - mutating func skip(_ count: Int) throws { - guard index + count <= data.count else { - throw CBORError.prematureEnd - } - index += count - } } diff --git a/Sources/CBOR/CBORCodable.swift b/Sources/CBOR/Codable/CBORCodable.swift similarity index 94% rename from Sources/CBOR/CBORCodable.swift rename to Sources/CBOR/Codable/CBORCodable.swift index 3b8ec46..06946bb 100644 --- a/Sources/CBOR/CBORCodable.swift +++ b/Sources/CBOR/Codable/CBORCodable.swift @@ -1,3 +1,4 @@ +#if !hasFeature(Embedded) #if canImport(FoundationEssentials) import FoundationEssentials #elseif canImport(Foundation) @@ -9,6 +10,11 @@ import Foundation extension CBOR: Encodable { public func encode(to encoder: Encoder) throws { var container = encoder.singleValueContainer() + + if let container = container as? CBOREncoderSingleValueContainer { + container.encoder.push(self) + return + } switch self { case .unsignedInt(let value): @@ -62,6 +68,11 @@ extension CBOR: Encodable { extension CBOR: Decodable { public init(from decoder: Decoder) throws { let container = try decoder.singleValueContainer() + + if let container = container as? CBORSingleValueDecodingContainer { + self = container.cbor + return + } if container.decodeNil() { self = .null @@ -155,3 +166,4 @@ struct CBORKey: CodingKey { self.intValue = index } } +#endif \ No newline at end of file diff --git a/Sources/CBOR/CBORDecoder.swift b/Sources/CBOR/Codable/CBORDecoder.swift similarity index 99% rename from Sources/CBOR/CBORDecoder.swift rename to Sources/CBOR/Codable/CBORDecoder.swift index 814b9fd..61429ee 100644 --- a/Sources/CBOR/CBORDecoder.swift +++ b/Sources/CBOR/Codable/CBORDecoder.swift @@ -1,3 +1,4 @@ +#if !hasFeature(Embedded) #if canImport(FoundationEssentials) import FoundationEssentials #elseif canImport(Foundation) @@ -7,7 +8,7 @@ import Foundation // MARK: - CBOR Decoder /// A decoder that converts CBOR data to Swift values -public class CBORDecoder: Decoder { +public final class CBORDecoder: Decoder { private var cbor: CBOR public var codingPath: [CodingKey] public var userInfo: [CodingUserInfoKey: Any] = [:] @@ -17,18 +18,16 @@ public class CBORDecoder: Decoder { self.codingPath = [] } - public func decode(_ type: T.Type, from data: Data) throws -> T where T: Decodable { + public func decode(_ type: T.Type, from data: [UInt8]) throws -> T where T: Decodable { // First decode the CBOR value from the data - let cbor = try CBOR.decode([UInt8](data)) + let cbor = try CBOR.decode(data) // Special case for arrays - if type == [Data].self { - if case .byteString = cbor { - // If we're trying to decode a byteString as an array of Data, - // wrap it in an array with a single element - let dataArray = [Data([UInt8](data))] - return dataArray as! T - } + if type == [Data].self, case .byteString = cbor { + // If we're trying to decode a byteString as an array of Data, + // wrap it in an array with a single element + let dataArray = [Data(data)] + return dataArray as! T } // Then use the regular decoder to decode the value @@ -1537,9 +1536,9 @@ private struct CBORUnkeyedDecodingContainer: UnkeyedDecodingContainer { // MARK: - CBOR Single Value Decoding Container -private struct CBORSingleValueDecodingContainer: SingleValueDecodingContainer { +internal struct CBORSingleValueDecodingContainer: SingleValueDecodingContainer { var codingPath: [CodingKey] - private let cbor: CBOR + internal let cbor: CBOR init(cbor: CBOR, codingPath: [CodingKey]) { self.cbor = cbor @@ -1917,3 +1916,4 @@ private struct CBORSingleValueDecodingContainer: SingleValueDecodingContainer { return try T(from: decoder) } } +#endif \ No newline at end of file diff --git a/Sources/CBOR/CBOREncoder.swift b/Sources/CBOR/Codable/CBOREncoder.swift similarity index 85% rename from Sources/CBOR/CBOREncoder.swift rename to Sources/CBOR/Codable/CBOREncoder.swift index 20f91e6..53e4cd0 100644 --- a/Sources/CBOR/CBOREncoder.swift +++ b/Sources/CBOR/Codable/CBOREncoder.swift @@ -1,3 +1,4 @@ +#if !hasFeature(Embedded) #if canImport(FoundationEssentials) import FoundationEssentials #elseif canImport(Foundation) @@ -6,7 +7,7 @@ import Foundation // MARK: - CBOR Encoder -public class CBOREncoder { +public final class CBOREncoder { // MARK: - Storage private class Storage { @@ -39,121 +40,71 @@ public class CBOREncoder { public init() {} - public func encode(_ value: T) throws -> Data { - // Special case for Data - if let data = value as? Data { - let cbor = CBOR.byteString(Array(data)) - return Data(cbor.encode()) - } - - // Special case for Date - if let date = value as? Date { - let cbor = CBOR.tagged(1, CBOR.float(date.timeIntervalSince1970)) - return Data(cbor.encode()) - } - - // Special case for URL - if let url = value as? URL { - let cbor = CBOR.textString(url.absoluteString) - return Data(cbor.encode()) - } - - // Special case for arrays of primitive types - if let array = value as? [Int] { - let cbor = CBOR.array(array.map { - if $0 < 0 { - return CBOR.negativeInt(Int64(-1 - $0)) - } else { - return CBOR.unsignedInt(UInt64($0)) - } - }) - return Data(cbor.encode()) - } - if let array = value as? [String] { - let cbor = CBOR.array(array.map { CBOR.textString($0) }) - return Data(cbor.encode()) - } - if let array = value as? [Bool] { - let cbor = CBOR.array(array.map { CBOR.bool($0) }) - return Data(cbor.encode()) - } - if let array = value as? [Double] { - let cbor = CBOR.array(array.map { CBOR.float($0) }) - return Data(cbor.encode()) - } - if let array = value as? [Float] { - let cbor = CBOR.array(array.map { CBOR.float(Double($0)) }) - return Data(cbor.encode()) - } - if let array = value as? [Data] { - let cbor = CBOR.array(array.map { CBOR.byteString(Array($0)) }) - return Data(cbor.encode()) - } - - // For other types, use the Encodable protocol - storage = Storage() // Reset storage - try value.encode(to: self) - - // Get the encoded CBOR value and convert it to Data - let cbor = storage.topValue - return Data(cbor.encode()) + public func encode(_ value: T) throws -> [UInt8] { + let cbor: CBOR + switch value { + case let data as Data: + cbor = CBOR.byteString(Array(data)) + case let date as Date: + cbor = CBOR.tagged(1, CBOR.float(date.timeIntervalSince1970)) + case let url as URL: + cbor = CBOR.textString(url.absoluteString) + case let array as [Int]: + cbor = CBOR.array(array.map { $0 < 0 ? CBOR.negativeInt(Int64(-1 - $0)) : CBOR.unsignedInt(UInt64($0)) }) + case let array as [String]: + cbor = CBOR.array(array.map { CBOR.textString($0) }) + case let array as [Bool]: + cbor = CBOR.array(array.map { CBOR.bool($0) }) + case let array as [Double]: + cbor = CBOR.array(array.map { CBOR.float($0) }) + case let array as [Float]: + cbor = CBOR.array(array.map { CBOR.float(Double($0)) }) + case let array as [Data]: + cbor = CBOR.array(array.map { CBOR.byteString(Array($0)) }) + default: + storage = Storage() + try value.encode(to: self) + cbor = storage.topValue + } + return cbor.encode() } // MARK: - Internal API - fileprivate func push(_ value: CBOR) { + @usableFromInline + internal func push(_ value: CBOR) { storage.push(value) } // Implementation of the encodeCBOR method that's referenced in the code fileprivate func encodeCBOR(_ value: T) throws -> CBOR { - // Special case for Data - if let data = value as? Data { + switch value { + case let data as Data: return CBOR.byteString(Array(data)) - } - - // Special case for Date - if let date = value as? Date { + case let date as Date: return CBOR.tagged(1, CBOR.float(date.timeIntervalSince1970)) - } - - // Special case for URL - if let url = value as? URL { + case let url as URL: return CBOR.textString(url.absoluteString) - } - - // Special case for arrays of primitive types - if let array = value as? [Int] { - return CBOR.array(array.map { - if $0 < 0 { - return CBOR.negativeInt(Int64(-1 - $0)) - } else { - return CBOR.unsignedInt(UInt64($0)) - } - }) - } - if let array = value as? [String] { + case let array as [Int]: + return CBOR.array(array.map { $0 < 0 ? CBOR.negativeInt(Int64(-1 - $0)) : CBOR.unsignedInt(UInt64($0)) }) + case let array as [String]: return CBOR.array(array.map { CBOR.textString($0) }) - } - if let array = value as? [Bool] { + case let array as [Bool]: return CBOR.array(array.map { CBOR.bool($0) }) - } - if let array = value as? [Double] { + case let array as [Double]: return CBOR.array(array.map { CBOR.float($0) }) - } - if let array = value as? [Float] { + case let array as [Float]: return CBOR.array(array.map { CBOR.float(Double($0)) }) - } - if let array = value as? [Data] { + case let array as [Data]: return CBOR.array(array.map { CBOR.byteString(Array($0)) }) + default: + // For other types, use the Encodable protocol + let tempEncoder = CBOREncoder() + try value.encode(to: tempEncoder) + + // Get the encoded CBOR value + return tempEncoder.storage.topValue } - - // For other types, use the Encodable protocol - let tempEncoder = CBOREncoder() - try value.encode(to: tempEncoder) - - // Get the encoded CBOR value - return tempEncoder.storage.topValue } } @@ -690,9 +641,9 @@ private struct AnyCodingKey: CodingKey { } } -private struct CBOREncoderSingleValueContainer: SingleValueEncodingContainer { +internal struct CBOREncoderSingleValueContainer: SingleValueEncodingContainer { let codingPath: [CodingKey] - private let encoder: CBOREncoder + internal let encoder: CBOREncoder init(codingPath: [CodingKey], encoder: CBOREncoder) { self.codingPath = codingPath @@ -771,3 +722,4 @@ private struct CBOREncoderSingleValueContainer: SingleValueEncodingContainer { try encoder.push(encoder.encodeCBOR(value)) } } +#endif \ No newline at end of file diff --git a/Tests/CBORTests/CBORCodableTests.swift b/Tests/CBORTests/CBORCodableTests.swift index 2b722a1..4caca5e 100644 --- a/Tests/CBORTests/CBORCodableTests.swift +++ b/Tests/CBORTests/CBORCodableTests.swift @@ -336,7 +336,7 @@ struct CBORCodableTests { // Test decoding invalid data do { - let invalidData = Data([0xFF, 0xFF, 0xFF]) // Invalid CBOR data + let invalidData: [UInt8] = [0xFF, 0xFF, 0xFF] // Invalid CBOR data let decoder = CBORDecoder() #expect(throws: CBORError.self) { try decoder.decode(Person.self, from: invalidData) diff --git a/Tests/CBORTests/CBORErrorTests.swift b/Tests/CBORTests/CBORErrorTests.swift index 4e199cf..577e175 100644 --- a/Tests/CBORTests/CBORErrorTests.swift +++ b/Tests/CBORTests/CBORErrorTests.swift @@ -16,7 +16,7 @@ struct CBORErrorTests { do { let _ = try CBOR.decode(invalidData) Issue.record("Expected decoding to fail with CBORError") - } catch is CBORError { + } catch CBORError.invalidInitialByte(0xFF) { // This is the expected error } catch { Issue.record("Expected CBORError but got \(error)") @@ -34,7 +34,7 @@ struct CBORErrorTests { do { let _ = try CBOR.decode(incompleteData) Issue.record("Expected decoding to fail with CBORError") - } catch is CBORError { + } catch CBORError.prematureEnd { // This is the expected error } catch { Issue.record("Expected CBORError but got \(error)") @@ -52,7 +52,7 @@ struct CBORErrorTests { do { let _ = try CBOR.decode(dataWithExtra) Issue.record("Expected decoding to fail with CBORError") - } catch is CBORError { + } catch CBORError.extraDataFound { // This is the expected error } catch { Issue.record("Expected CBORError but got \(error)") @@ -74,7 +74,7 @@ struct CBORErrorTests { do { let _ = try CBOR.decode(simpleInvalidUTF8) Issue.record("Expected decoding to fail with CBORError for simple invalid UTF-8") - } catch is CBORError { + } catch CBORError.invalidUTF8 { // This is the expected error } catch { Issue.record("Expected CBORError but got \(error) for simple invalid UTF-8") @@ -91,7 +91,7 @@ struct CBORErrorTests { do { let _ = try CBOR.decode(overlongUTF8) Issue.record("Expected decoding to fail with CBORError for overlong UTF-8") - } catch is CBORError { + } catch CBORError.invalidUTF8 { // This is the expected error } catch { Issue.record("Expected CBORError but got \(error) for overlong UTF-8") @@ -106,7 +106,7 @@ struct CBORErrorTests { do { let _ = try CBOR.decode(overlongUTF8_2) Issue.record("Expected decoding to fail with CBORError for 3-byte overlong UTF-8") - } catch is CBORError { + } catch CBORError.invalidUTF8 { // This is the expected error } catch { Issue.record("Expected CBORError but got \(error) for 3-byte overlong UTF-8") @@ -124,7 +124,7 @@ struct CBORErrorTests { do { let _ = try CBOR.decode(surrogateUTF8) Issue.record("Expected decoding to fail with CBORError for surrogate code point") - } catch is CBORError { + } catch CBORError.invalidUTF8 { // This is the expected error } catch { Issue.record("Expected CBORError but got \(error) for surrogate code point") @@ -139,7 +139,7 @@ struct CBORErrorTests { do { let _ = try CBOR.decode(lowSurrogateUTF8) Issue.record("Expected decoding to fail with CBORError for low surrogate code point") - } catch is CBORError { + } catch CBORError.invalidUTF8 { // This is the expected error } catch { Issue.record("Expected CBORError but got \(error) for low surrogate code point") @@ -155,7 +155,7 @@ struct CBORErrorTests { do { let _ = try CBOR.decode(incompleteUTF8) Issue.record("Expected decoding to fail with CBORError for incomplete UTF-8 sequence") - } catch is CBORError { + } catch CBORError.invalidUTF8 { // This is the expected error } catch { Issue.record("Expected CBORError but got \(error) for incomplete UTF-8 sequence") @@ -171,7 +171,7 @@ struct CBORErrorTests { do { let _ = try CBOR.decode(invalidContinuationUTF8) Issue.record("Expected decoding to fail with CBORError for invalid continuation byte") - } catch is CBORError { + } catch CBORError.invalidUTF8 { // This is the expected error } catch { Issue.record("Expected CBORError but got \(error) for invalid continuation byte") @@ -188,7 +188,7 @@ struct CBORErrorTests { do { let _ = try CBOR.decode(beyondMaxUTF8) Issue.record("Expected decoding to fail with CBORError for code point beyond U+10FFFF") - } catch is CBORError { + } catch CBORError.invalidUTF8 { // This is the expected error } catch { Issue.record("Expected CBORError but got \(error) for code point beyond U+10FFFF") @@ -206,7 +206,7 @@ struct CBORErrorTests { do { let _ = try CBOR.decode(mixedUTF8) Issue.record("Expected decoding to fail with CBORError for mixed valid/invalid UTF-8") - } catch is CBORError { + } catch CBORError.invalidUTF8 { // This is the expected error } catch { Issue.record("Expected CBORError but got \(error) for mixed valid/invalid UTF-8") @@ -223,7 +223,7 @@ struct CBORErrorTests { do { let _ = try CBOR.decode(unexpectedContinuationUTF8) Issue.record("Expected decoding to fail with CBORError for unexpected continuation bytes") - } catch is CBORError { + } catch CBORError.invalidUTF8 { // This is the expected error } catch { Issue.record("Expected CBORError but got \(error) for unexpected continuation bytes") @@ -241,9 +241,9 @@ struct CBORErrorTests { // Try to decode it as Int (should fail with overflow) let decoder = CBORDecoder() do { - let _ = try decoder.decode(Int.self, from: Data(encoded)) + let _ = try decoder.decode(Int.self, from: encoded) Issue.record("Expected decoding to fail with DecodingError") - } catch is DecodingError { + } catch DecodingError.dataCorrupted { // This is the expected error } catch { Issue.record("Expected DecodingError but got \(error)") @@ -259,9 +259,9 @@ struct CBORErrorTests { // Try to decode it as Int (should fail with type mismatch) let decoder = CBORDecoder() do { - let _ = try decoder.decode(Int.self, from: Data(encoded)) + let _ = try decoder.decode(Int.self, from: encoded) Issue.record("Expected decoding to fail with DecodingError") - } catch is DecodingError { + } catch DecodingError.typeMismatch { // This is the expected error } catch { Issue.record("Expected DecodingError but got \(error)") @@ -281,9 +281,9 @@ struct CBORErrorTests { // Try to decode it (should fail with key not found) let decoder = CBORDecoder() do { - let _ = try decoder.decode(RequiredKeyStruct.self, from: Data(encoded)) + let _ = try decoder.decode(RequiredKeyStruct.self, from: encoded) Issue.record("Expected decoding to fail with DecodingError") - } catch is DecodingError { + } catch DecodingError.keyNotFound { // This is the expected error } catch { Issue.record("Expected DecodingError but got \(error)") @@ -299,9 +299,9 @@ struct CBORErrorTests { // Try to decode it as URL (should fail with data corrupted) let decoder = CBORDecoder() do { - let _ = try decoder.decode(URL.self, from: Data(encoded)) + let _ = try decoder.decode(URL.self, from: encoded) Issue.record("Expected decoding to fail with DecodingError") - } catch is DecodingError { + } catch DecodingError.dataCorrupted { // This is the expected error } catch { Issue.record("Expected DecodingError but got \(error)") @@ -320,7 +320,7 @@ struct CBORErrorTests { do { let _ = try CBOR.decode(incompleteData) Issue.record("Expected decoding to fail with CBORError") - } catch is CBORError { + } catch CBORError.prematureEnd { // This is the expected error } catch { Issue.record("Expected CBORError but got \(error)") @@ -335,7 +335,7 @@ struct CBORErrorTests { do { let _ = try CBOR.decode(dataWithExtra) Issue.record("Expected decoding to fail with CBORError") - } catch is CBORError { + } catch CBORError.extraDataFound { // This is the expected error } catch { Issue.record("Expected CBORError but got \(error)") @@ -349,16 +349,10 @@ struct CBORErrorTests { // Test that all CBORError cases have meaningful descriptions let errors: [CBORError] = [ .invalidCBOR, - .typeMismatch(expected: "String", actual: "Int"), - .outOfBounds(index: 5, count: 3), - .missingKey("requiredKey"), - .valueConversionFailed("Could not convert to Int"), .invalidUTF8, - .integerOverflow, - .unsupportedTag(123), .prematureEnd, .invalidInitialByte(0xFF), - .lengthTooLarge(UInt64.max), + .lengthTooLarge(UInt64.max, maximum: 16_384), .indefiniteLengthNotSupported, .extraDataFound ] @@ -417,7 +411,7 @@ struct CBORErrorTests { do { let _ = try shortReader.readByte() // This should fail (no more data) Issue.record("Expected reading to fail with CBORError") - } catch is CBORError { + } catch CBORError.prematureEnd { // This is the expected error } catch { Issue.record("Expected CBORError but got \(error)") @@ -437,7 +431,7 @@ struct CBORErrorTests { do { let _ = try multiByteReader.readByte() // Should fail after reading all bytes Issue.record("Expected reading to fail with CBORError") - } catch is CBORError { + } catch CBORError.prematureEnd { // This is the expected error } catch { Issue.record("Expected CBORError but got \(error)") @@ -462,7 +456,7 @@ struct CBORErrorTests { do { let _ = try CBOR.decode(invalidArrayItem) Issue.record("Expected decoding to fail with CBORError") - } catch is CBORError { + } catch CBORError.indefiniteLengthNotSupported { // This is the expected error } catch { Issue.record("Expected CBORError but got \(error)") @@ -478,7 +472,7 @@ struct CBORErrorTests { do { let _ = try CBOR.decode(invalidMapValue) Issue.record("Expected decoding to fail with CBORError") - } catch is CBORError { + } catch CBORError.indefiniteLengthNotSupported { // This is the expected error } catch { Issue.record("Expected CBORError but got \(error)") @@ -522,7 +516,7 @@ struct CBORErrorTests { do { let _ = try CBOR.decode(invalidUnsignedIntAdditionalInfo) Issue.record("Expected decoding to fail with CBORError for invalid unsigned int additional info") - } catch is CBORError { + } catch CBORError.indefiniteLengthNotSupported { // This is the expected error } catch { Issue.record("Expected CBORError but got \(error)") @@ -539,7 +533,7 @@ struct CBORErrorTests { do { let _ = try CBOR.decode(unexpectedBreak) Issue.record("Expected decoding to fail with CBORError for unexpected break code") - } catch is CBORError { + } catch CBORError.invalidInitialByte(0xFF) { // This is the expected error } catch { Issue.record("Expected CBORError but got \(error)") @@ -556,7 +550,7 @@ struct CBORErrorTests { do { let _ = try CBOR.decode(unexpectedBreakInArray) Issue.record("Expected decoding to fail with CBORError for unexpected break in array") - } catch is CBORError { + } catch CBORError.invalidInitialByte(0xFF) { // This is the expected error } catch { Issue.record("Expected CBORError but got \(error)") @@ -580,7 +574,7 @@ struct CBORErrorTests { // Try to decode it (should fail with key not found) let decoder = CBORDecoder() do { - let _ = try decoder.decode(RequiredKeyStruct.self, from: Data(encoded)) + let _ = try decoder.decode(RequiredKeyStruct.self, from: encoded) Issue.record("Expected decoding to fail with DecodingError.keyNotFound") } catch let error as DecodingError { switch error { @@ -606,10 +600,8 @@ struct CBORErrorTests { do { let _ = try CBOR.decode(lengthTooLarge) Issue.record("Expected decoding to fail with CBORError for length too large") - } catch is CBORError { - // This is the expected error } catch { - Issue.record("Expected CBORError but got \(error)") + // This is the expected error } // Test array with length that exceeds available memory @@ -621,7 +613,7 @@ struct CBORErrorTests { do { let _ = try CBOR.decode(arrayTooLarge) Issue.record("Expected decoding to fail with CBORError for array length too large") - } catch is CBORError { + } catch CBORError.lengthTooLarge(UInt64.max, maximum: _) { // This is the expected error } catch { Issue.record("Expected CBORError but got \(error)") @@ -657,7 +649,7 @@ struct CBORErrorTests { let decoder = CBORDecoder() do { - let _ = try decoder.decode(Int.self, from: Data(encoded)) + let _ = try decoder.decode(Int.self, from: encoded) Issue.record("Expected decoding a string as Int to fail") } catch is DecodingError { // This is the expected error