···2222 name: Testing CBOR (ubuntu)
2323 needs: swiftlint
2424 runs-on: ubuntu-latest
2525- container: swift:6.0-noble
2525+ container: swift:6.2-noble
2626 steps:
2727 - name: Checkout repository
2828 uses: actions/checkout@v5
2929- - uses: swift-actions/setup-swift@next
3030- with:
3131- swift-version: "6.0"
3229 - name: Restore .build
3330 id: "restore-build"
3431 uses: actions/cache/restore@v4
···5653 uses: actions/checkout@v5
5754 - uses: swift-actions/setup-swift@next
5855 with:
5959- swift-version: "6.0"
5656+ swift-version: "6.1"
6057 - name: Restore .build
6158 id: "restore-build"
6259 uses: actions/cache/restore@v4
···6562 key: "swiftpm-tests-build-${{ runner.os }}-${{ github.event.pull_request.base.sha || github.event.after }}"
6663 restore-keys: "swiftpm-tests-build-${{ runner.os }}-"
6764 - name: Building Package
6868- run: set -o pipefail && swift build --build-tests | xcbeautify
6565+ run: set -o pipefail && swift build --build-tests | xcbeautify --renderer github-actions
6966# run: set -o pipefail && swift build --build-tests // --sanitize fuzzer | xcbeautify
7067 - name: Cache .build
7168 if: steps.restore-build.outputs.cache-hit != 'true'
···7471 path: .build
7572 key: "swiftpm-tests-build-${{ runner.os }}-${{ github.event.pull_request.base.sha || github.event.after }}"
7673 - name: Testing Package
7777- run: set -o pipefail && swift test --skip-build | xcbeautify
7474+ run: set -o pipefail && swift test --skip-build | xcbeautify --renderer github-actions
7875# TODO: The Xcode version of the swift toolchain does not come with libfuzzer
7976# What needs to happen is we install the open source toolchain and then we can fuzz here...
8077# - name: Fuzzing For 15 Seconds
+12-13
README.md
···3232- Supports decoding half precision floats (Float16) as a regular Float.
3333- Runs on Linux, Android, and Windows using the swift-foundation project when available.
3434- Fuzz tested for reliability against crashes.
3535-- Supports tagged items (will expand and add ability to inject your own tags in the future):
3636- - Dates
3737- - UUIDs
3838- - [CIDs](https://github.com/multiformats/cid) (tag 42)
3535+- ***NEW*** Supports tagged items with custom tag injection.
3636+ - Dates are a special case, handled by the library.
3737+ - Contains UUID example implementation.
3938- Flexible date parsing (tags `0` or `1` with support for any numeric value representation).
4039- Decoding multiple top-level objects using `decodeMultiple(_:from:)`.
4141-- *NEW* IPLD compatible DAG-CBOR encoder for content addressable data.
4242-- *NEW* Flexible date decoding for untagged date items encoded as strings, floating point values, or integers.
4343-4444-> Note: This is not a valid CBOR/CDE encoder, it merely always outputs countable collections. CBOR/CDE should be implemented in the future as it's quite similar.
4040+- ***NEW*** IPLD compatible DAG-CBOR encoder for content addressable data.
4141+- ***NEW*** Flexible date decoding for untagged date items encoded as strings, floating point values, or integers.
45424643## Usage
4744···9087let dagEncoder = DAGCBOREncoder(dateEncodingStrategy: .double)
9188```
92899393-To use, conform your internal CID type to ``CIDType``. Do not conform standard types like `String` or `Data` to ``CIDType``, or the encoder will attempt to encode all of those data as tagged items.
9090+> [!NOTE]
9191+> DAG-CBOR does not allow tagged items (besides the CID item), and thus encoding dates must be done by encoding their 'raw' value directly. This is an application specific behavior, so ensure the encoder is using the correct date encoding behavior for compatibility. By default, the encoder will encode dates as an epoch `Double` timestamp.
9292+9393+To use with CIDs, conform your internal CID type to ``CIDType``. Do not conform standard types like `String` or `Data` to ``CIDType``, or the encoder may attempt to encode all of those data as tagged items.
9494```swift
9595struct CID: CIDType {
9696 let data: Data
···112112 }
113113}
114114```
115115+115116> [!WARNING]
116117>
117117-> You **need** to prefix your data with the `NULL` byte when encoding. This library will not handle that for you. It is invalid DAG-CBOR encoding to not include the prefixed byte.
118118+> It's *your* responsibility to correctly encode CIDs in the data container. That includes the `NULL` byte for raw binary CID encoding (which DAG-CBOR expects).
118119119120Now, any time the encoder finds a `CID` type it will encode it using the correct tag.
120121```swift
···127128// 4A # bytes(10)
128129// 00000102030405060708 # "\u0000\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007\b"
129130```
130130-> [!NOTE]
131131-> DAG-CBOR does not allow tagged items (besides the CID item), and thus encoding dates must be done by encoding their 'raw' value directly. This is an application specific behavior, so ensure the encoder is using the correct date encoding behavior for compatibility. By default, the encoder will encode dates as an epoch `Double` timestamp.
132131133132## Documentation
134133···140139141140```swift
142141dependencies: [
143143- .package(url: "https://github.com/thecoolwinter/CBOR.git", from: "1.0.0")
142142+ .package(url: "https://github.com/thecoolwinter/CBOR.git", from: "1.1.0")
144143]
145144```
146145
+24-51
Sources/CBOR/Decoder/CBORDecoder.swift
···2323 public var options: DecodingOptions
24242525 /// Create a new CBOR decoder.
2626- /// - Parameter rejectIndeterminateLengths: Set to `false` to allow indeterminate length objects to be decoded.
2727- /// Defaults to *rejecting* indeterminate length items (strings, bytes,
2828- /// maps, and arrays).
2929- public init(rejectIndeterminateLengths: Bool = true) {
3030- self.options = DecodingOptions(rejectIndeterminateLengths: rejectIndeterminateLengths)
2626+ ///
2727+ /// All parameters match flags in ``DecodingOptions``.
2828+ public init(
2929+ rejectIndeterminateLengths: Bool = true,
3030+ recursionDepth: Int = 50,
3131+ rejectIntKeys: Bool = false,
3232+ rejectUnorderedMap: Bool = false,
3333+ rejectUndefined: Bool = false,
3434+ rejectNaN: Bool = false,
3535+ rejectInf: Bool = false,
3636+ singleTopLevelItem: Bool = false
3737+ ) {
3838+ self.options = DecodingOptions(
3939+ rejectIndeterminateLengths: rejectIndeterminateLengths,
4040+ recursionDepth: recursionDepth,
4141+ rejectIntKeys: rejectIntKeys,
4242+ rejectUnorderedMap: rejectUnorderedMap,
4343+ rejectUndefined: rejectUndefined,
4444+ rejectNaN: rejectNaN,
4545+ rejectInf: rejectInf,
4646+ singleTopLevelItem: singleTopLevelItem
4747+ )
3148 }
32493350 /// Create a new CBOR decoder
···6178 }
6279 } catch {
6380 if let error = error as? ScanError {
6464- try throwScanError(error)
8181+ throw error.decodingError()
6582 } else {
6683 throw error
6784 }
···111128 }
112129 } catch {
113130 if let error = error as? ScanError {
114114- try throwScanError(error)
131131+ throw error.decodingError()
115132 } else {
116133 throw error
117134 }
118118- }
119119- }
120120-121121- private func throwScanError(_ error: ScanError) throws -> Never {
122122- switch error {
123123- case .unexpectedEndOfData:
124124- throw DecodingError.dataCorrupted(
125125- .init(codingPath: [], debugDescription: "Unexpected end of data.")
126126- )
127127- case let .invalidMajorType(byte, offset):
128128- throw DecodingError.dataCorrupted(.init(
129129- codingPath: [],
130130- debugDescription: "Unexpected major type: \(String(byte, radix: 2)) at offset \(offset)"
131131- ))
132132- case let .invalidSize(byte, offset):
133133- throw DecodingError.dataCorrupted(.init(
134134- codingPath: [],
135135- debugDescription: "Unexpected size argument: \(String(byte, radix: 2)) at offset \(offset)"
136136- ))
137137- case let .expectedMajorType(offset):
138138- throw DecodingError.dataCorrupted(.init(
139139- codingPath: [],
140140- debugDescription: "Expected major type at offset \(offset)"
141141- ))
142142- case let .typeInIndeterminateString(type, offset):
143143- throw DecodingError.dataCorrupted(.init(
144144- codingPath: [],
145145- debugDescription: "Unexpected major type in indeterminate \(type) at offset \(offset)"
146146- ))
147147- case let .rejectedIndeterminateLength(type, offset):
148148- throw DecodingError.dataCorrupted(.init(
149149- codingPath: [],
150150- debugDescription: "Rejected indeterminate length type \(type) at offset \(offset)"
151151- ))
152152- case let .cannotRepresentInt(max, found, offset):
153153- throw DecodingError.dataCorrupted(
154154- .init(
155155- codingPath: [],
156156- debugDescription: "Failed to decode integer with maximum \(max), "
157157- + "found \(found) at \(offset)"
158158- )
159159- )
160160- case .noTagInformation, .invalidMajorTypeForTaggedItem:
161161- throw error // rethrow these guys
162135 }
163136 }
164137}
···1616 let data: DataRegion
1717}
18181919+// MARK: - Decoder
2020+1921extension SingleValueCBORDecodingContainer: Decoder {
2022 func container<Key>(keyedBy type: Key.Type) throws -> KeyedDecodingContainer<Key> where Key: CodingKey {
2123 try KeyedDecodingContainer(KeyedCBORDecodingContainer(context: context, data: data))
···3133}
32343335extension SingleValueCBORDecodingContainer: SingleValueDecodingContainer {
3636+ // MARK: - Nil
3737+3438 func decodeNil() -> Bool {
3539 data.isNil()
3640 }
37414242+ // MARK: - Bool
4343+3844 func decode(_: Bool.Type) throws -> Bool {
3945 let argument = try checkType(.simple, arguments: 20, 21, as: Bool.self)
4046 return argument == 21
4147 }
42484949+ // MARK: - Floating Points
5050+5151+ /// Helper for checking floats against decoding flags.
5252+ @inline(__always)
5353+ private func checkFloatValidity<T: FloatingPoint>(_ value: T) throws -> T {
5454+ if context.options.rejectNaN && value.isNaN {
5555+ throw DecodingError.dataCorrupted(context.error("Found NaN \(T.self), configured to reject NaN values."))
5656+ }
5757+5858+ if context.options.rejectInf && value.isInfinite {
5959+ throw DecodingError.dataCorrupted(
6060+ context.error("Found \(value) \(T.self), configured to reject Infinite values.")
6161+ )
6262+ }
6363+6464+ return value
6565+ }
6666+4367 func decode(_: Float.Type) throws -> Float {
4468 let arg = try checkType(.simple, arguments: 25, 26, as: Float.self)
4569 if arg == 25 {
···4973 context.error("Could not decode half-precision float into Swift Float.")
5074 )
5175 }
5252- return value
7676+ return try checkFloatValidity(value)
5377 }
54785579 let floatRaw = try data.read(as: UInt32.self)
5656- return Float(bitPattern: floatRaw)
8080+ return try checkFloatValidity(Float(bitPattern: floatRaw))
5781 }
58825983 func decode(_: Double.Type) throws -> Double {
6084 try checkType(.simple, arguments: 27, as: Double.self)
6185 let doubleRaw = try data.read(as: UInt64.self).littleEndian
6262- return Double(bitPattern: doubleRaw)
8686+ return try checkFloatValidity(Double(bitPattern: doubleRaw))
6387 }
8888+8989+ // MARK: - Integers
64906591 func decode<T: Decodable & FixedWidthInteger>(_: T.Type) throws -> T {
6692 try checkType(.uint, .nint, forType: T.self)
···77103 return value
78104 }
79105106106+ @available(macOS 15.0, iOS 18.0, tvOS 18.0, watchOS 11.0, visionOS 2.0, *)
107107+ func decode(_ type: Int128.Type) throws -> Int128 {
108108+ try checkType(.uint, .nint, forType: Int128.self)
109109+ let value = try data.readInt(as: Int128.self)
110110+ if data.type == .nint {
111111+ return -1 - value
112112+ }
113113+ return value
114114+ }
115115+116116+ @available(macOS 15.0, iOS 18.0, tvOS 18.0, watchOS 11.0, visionOS 2.0, *)
117117+ func decode(_ type: UInt128.Type) throws -> UInt128 {
118118+ try checkType(.uint, .nint, forType: UInt128.self)
119119+ let value = try data.readInt(as: UInt128.self)
120120+ if data.type == .nint {
121121+ throw DecodingError.typeMismatch(
122122+ Int.self,
123123+ context.error("Found a negative integer while attempting to decode an unsigned int \(UInt128.self).")
124124+ )
125125+ }
126126+ return value
127127+ }
128128+129129+ // MARK: - String
130130+80131 func decode(_: String.Type) throws -> String {
81132 try checkType(.string, forType: String.self)
82133···128179 }
129180 return string
130181 }
182182+183183+ // MARK: - Date
131184132185 // Attempt first to decode a tagged date value, then move on and try decoding any of the following as a date:
133186 // - Int
···190243 }
191244 }
192245246246+ // MARK: - Data
247247+193248 private func _decode(_: Data.Type) throws -> Data {
194249 try checkType(.bytes, forType: Data.self)
195250···233288 }
234289 return string
235290 }
291291+292292+ // MARK: - Decode
236293237294 func decode<T: Decodable>(_ type: T.Type) throws -> T {
238295 // Unfortunate force unwraps here, but necessary
+49
Sources/CBOR/Decoder/DAGCBORDecoder.swift
···11+//
22+// DAGCBORDecoder.swift
33+// CBOR
44+//
55+// Created by Khan Winter on 10/20/25.
66+//
77+88+#if canImport(FoundationEssentials)
99+import FoundationEssentials
1010+#else
1111+import Foundation
1212+#endif
1313+1414+/// Decodes ``Decodable`` objects from DAG-CBOR data.
1515+///
1616+/// This is really just a wrapper for ``CBORDecoder``, which constant configuration flags that ensure valid DAG-CBOR
1717+/// data is decoded.
1818+///
1919+/// If this decoder is too strict, I'd suggest using ``CBORDecoder``, which will be much more flexible around decoding
2020+/// than this type. You can also loosen specific flags using the options member of that struct. For instance, this
2121+/// type will reject all unordered maps. If you find yourself needing to receive badly-defined DAG-CBOR, just
2222+/// use ``CBORDecoder``.
2323+public struct DAGCBORDecoder {
2424+ /// The options that determine decoding behavior.
2525+ let options: DecodingOptions
2626+2727+ /// Create a new DAG-CBOR decoder.
2828+ public init() {
2929+ self.options = DecodingOptions(
3030+ rejectIndeterminateLengths: true,
3131+ rejectIntKeys: true,
3232+ rejectUnorderedMap: true,
3333+ rejectUndefined: true,
3434+ rejectNaN: true,
3535+ rejectInf: true,
3636+ singleTopLevelItem: true
3737+ )
3838+ }
3939+4040+ /// Decodes the given type from DAG-CBOR binary data.
4141+ /// - Parameters:
4242+ /// - type: The decodable type to deserialize.
4343+ /// - data: The DAG-CBOR data to decode from.
4444+ /// - Returns: An instance of the decoded type.
4545+ /// - Throws: A ``DecodingError`` with context and a debug description for a failed deserialization operation.
4646+ public func decode<T: Decodable>(_ type: T.Type, from data: Data) throws -> T {
4747+ try CBORDecoder(options: options).decode(T.self, from: data)
4848+ }
4949+}
+40-1
Sources/CBOR/Decoder/DecodingOptions.swift
···1313 /// For deterministic encoding, this **must** be enabled.
1414 public var rejectIndeterminateLengths: Bool
15151616+ /// Maximum recursion depth.
1717+ public var recursionDepth: Int = 50
1818+1919+ /// Reject maps with non-string keys..
2020+ public var rejectIntKeys: Bool
2121+2222+ /// Reject maps whose keys are out of order.
2323+ public var rejectUnorderedMap: Bool
2424+2525+ /// Reject the `undefined` simple value (`23`).
2626+ public var rejectUndefined: Bool
2727+2828+ /// Enable to reject decoded `NaN` floating point values.
2929+ public var rejectNaN: Bool
3030+3131+ /// Enable to reject decoded infinite floating point values.
3232+ public var rejectInf: Bool
3333+3434+ /// Require that CBOR data encapsulates the *entire* data object being decoded.
3535+ /// When true, throws a decoding error if data is left over after scanning for valid CBOR structure, or if there
3636+ /// are multiple top-level objects.
3737+ public var singleTopLevelItem: Bool
3838+1639 /// Create a new options object.
1717- public init(rejectIndeterminateLengths: Bool = true) {
4040+ public init(
4141+ rejectIndeterminateLengths: Bool = true,
4242+ recursionDepth: Int = 50,
4343+ rejectIntKeys: Bool = false,
4444+ rejectUnorderedMap: Bool = false,
4545+ rejectUndefined: Bool = false,
4646+ rejectNaN: Bool = false,
4747+ rejectInf: Bool = false,
4848+ singleTopLevelItem: Bool = false
4949+ ) {
1850 self.rejectIndeterminateLengths = rejectIndeterminateLengths
5151+ self.recursionDepth = recursionDepth
5252+ self.rejectIntKeys = rejectIntKeys
5353+ self.rejectUnorderedMap = rejectUnorderedMap
5454+ self.rejectUndefined = rejectUndefined
5555+ self.rejectNaN = rejectNaN
5656+ self.rejectInf = rejectInf
5757+ self.singleTopLevelItem = singleTopLevelItem
1958 }
2059}
+33-29
Sources/CBOR/Decoder/Scanner/CBORScanner.swift
···1111import Foundation
1212#endif
13131414-@usableFromInline
1515-enum ScanError: Error {
1616- case unexpectedEndOfData
1717- case invalidMajorType(byte: UInt8, offset: Int)
1818- case invalidSize(byte: UInt8, offset: Int)
1919- case expectedMajorType(offset: Int)
2020- case typeInIndeterminateString(type: MajorType, offset: Int)
2121- case rejectedIndeterminateLength(type: MajorType, offset: Int)
2222- case cannotRepresentInt(max: UInt, found: UInt, offset: Int)
2323- case noTagInformation(tag: UInt, offset: Int)
2424- case invalidMajorTypeForTaggedItem(tag: UInt, expected: Set<MajorType>, found: MajorType, offset: Int)
2525-}
2626-2714/// # Why Scan?
2815/// I'd have loved to use a 'pop' method for decoding, where we only decode as data is requested. However, the way
2916/// Swift's decoding APIs work forces us to be able to be able to do random access for keys in maps, which requires
···52395340 consuming func scan() throws -> Results {
5441 while !reader.isEmpty {
4242+ if options.singleTopLevelItem && reader.index > 0 {
4343+ throw ScanError.unreadDataAfterEnd
4444+ }
4545+5546 let idx = reader.index
5656- try scanNext()
4747+ try scanNext(depth: 0)
5748 assert(idx < reader.index, "Scanner made no forward progress in scan")
5849 }
5050+5151+ if options.singleTopLevelItem && !reader.isEmpty {
5252+ throw ScanError.unreadDataAfterEnd
5353+ }
5454+5955 return results
6056 }
61576262- private mutating func scanNext() throws {
5858+ private mutating func scanNext(depth: Int) throws {
6359 guard let type = reader.peekType(), let raw = reader.peek() else {
6460 if reader.isEmpty {
6561 throw ScanError.unexpectedEndOfData
···6864 }
6965 }
70667171- try scanType(type: type, raw: raw)
6767+ guard depth < options.recursionDepth else {
6868+ throw ScanError.recursionLimit
6969+ }
7070+7171+ try scanType(type: type, raw: raw, depth: depth)
7272 }
73737474- private mutating func scanType(type: MajorType, raw: UInt8) throws {
7474+ private mutating func scanType(type: MajorType, raw: UInt8, depth: Int) throws {
7575 switch type {
7676 case .uint, .nint:
7777 try scanInt(raw: raw)
···8080 case .string:
8181 try scanBytesOrString(.string, raw: raw)
8282 case .array:
8383- try scanArray()
8383+ try scanArray(depth: depth)
8484 case .map:
8585- try scanMap()
8585+ try scanMap(depth: depth)
8686 case .simple:
8787 try scanSimple(raw: raw)
8888 case .tagged:
8989- try scanTagged(raw: raw)
8989+ try scanTagged(raw: raw, depth: depth)
9090 }
9191 }
9292···103103 // MARK: - Scan Simple
104104105105 private mutating func scanSimple(raw: UInt8) throws {
106106+ guard !(options.rejectUndefined && reader.peekArgument() == 23) else {
107107+ throw ScanError.rejectedUndefined
108108+ }
109109+106110 let idx = reader.index
107111 results.recordSimple(reader.pop(), currentByteIndex: idx)
108112 guard reader.canRead(raw.simpleLength()) else {
···150154151155 // MARK: - Scan Array
152156153153- private mutating func scanArray() throws {
157157+ private mutating func scanArray(depth: Int) throws {
154158 guard peekIsIndeterminate() else {
155159 let size = try reader.readNextInt(as: Int.self)
156160 let mapIdx = results.recordArrayStart(currentByteIndex: reader.index)
157161 for _ in 0..<size {
158158- try scanNext()
162162+ try scanNext(depth: depth + 1)
159163 }
160164 results.recordEnd(childCount: size, resultLocation: mapIdx, currentByteIndex: reader.index)
161165 return
···169173 reader.pop() // Pop type
170174 var count = 0
171175 while reader.peek() != Constants.breakCode {
172172- try scanNext()
176176+ try scanNext(depth: depth + 1)
173177 count += 1
174178 }
175179 // Pop the break byte
···179183180184 // MARK: - Scan Map
181185182182- private mutating func scanMap() throws {
186186+ private mutating func scanMap(depth: Int) throws {
183187 guard peekIsIndeterminate() else {
184188 let keyCount = try reader.readNextInt(as: Int.self)
185189 guard keyCount < Int.max / 2 else {
···189193 let size = keyCount * 2
190194 let mapIdx = results.recordMapStart(currentByteIndex: reader.index)
191195 for _ in 0..<size {
192192- try scanNext()
196196+ try scanNext(depth: depth + 1)
193197 }
194198 results.recordEnd(childCount: size, resultLocation: mapIdx, currentByteIndex: reader.index)
195199 return
···203207 reader.pop() // Pop type
204208 var count = 0
205209 while reader.peek() != Constants.breakCode {
206206- try scanNext() // Maps should always have a multiple of two values.
207207- try scanNext()
210210+ try scanNext(depth: depth + 1) // Maps should always have a multiple of two values.
211211+ try scanNext(depth: depth + 1)
208212 count += 2
209213 }
210214 // Pop the break byte
···214218215219 // MARK: - Scan Tagged
216220217217- private mutating func scanTagged(raw: UInt8) throws {
221221+ private mutating func scanTagged(raw: UInt8, depth: Int) throws {
218222 // Scan the tag number (passing the raw value here makes it record a Tag rather than an Int)
219223 try scanInt(raw: raw)
220224···222226 throw ScanError.unexpectedEndOfData
223227 }
224228225225- try scanType(type: nextTag, raw: nextRaw)
229229+ try scanType(type: nextTag, raw: nextRaw, depth: depth)
226230 }
227231}
228232
+103
Sources/CBOR/Decoder/Scanner/ScanError.swift
···11+//
22+// ScanError.swift
33+// CBOR
44+//
55+// Created by Khan Winter on 10/21/25.
66+//
77+88+@usableFromInline
99+enum ScanError: Error {
1010+ case unexpectedEndOfData
1111+ case invalidMajorType(byte: UInt8, offset: Int)
1212+ case invalidSize(byte: UInt8, offset: Int)
1313+ case expectedMajorType(offset: Int)
1414+ case typeInIndeterminateString(type: MajorType, offset: Int)
1515+ case rejectedIndeterminateLength(type: MajorType, offset: Int)
1616+ case cannotRepresentInt(max: UInt, found: UInt, offset: Int)
1717+ case recursionLimit
1818+ case unreadDataAfterEnd
1919+ case rejectedUndefined
2020+ case unnecessaryInt
2121+2222+ // swiftlint:disable:next cyclomatic_complexity function_body_length
2323+ func decodingError() -> DecodingError {
2424+ switch self {
2525+ case .unexpectedEndOfData:
2626+ return DecodingError.dataCorrupted(
2727+ .init(codingPath: [], debugDescription: "Unexpected end of data.", underlyingError: self)
2828+ )
2929+ case let .invalidMajorType(byte, offset):
3030+ return DecodingError.dataCorrupted(.init(
3131+ codingPath: [],
3232+ debugDescription: "Unexpected major type: \(String(byte, radix: 2)) at offset \(offset)",
3333+ underlyingError: self
3434+ ))
3535+ case let .invalidSize(byte, offset):
3636+ return DecodingError.dataCorrupted(.init(
3737+ codingPath: [],
3838+ debugDescription: "Unexpected size argument: \(String(byte, radix: 2)) at offset \(offset)",
3939+ underlyingError: self
4040+ ))
4141+ case let .expectedMajorType(offset):
4242+ return DecodingError.dataCorrupted(.init(
4343+ codingPath: [],
4444+ debugDescription: "Expected major type at offset \(offset)",
4545+ underlyingError: self
4646+ ))
4747+ case let .typeInIndeterminateString(type, offset):
4848+ return DecodingError.dataCorrupted(.init(
4949+ codingPath: [],
5050+ debugDescription: "Unexpected major type in indeterminate \(type) at offset \(offset)",
5151+ underlyingError: self
5252+ ))
5353+ case let .rejectedIndeterminateLength(type, offset):
5454+ return DecodingError.dataCorrupted(.init(
5555+ codingPath: [],
5656+ debugDescription: "Rejected indeterminate length type \(type) at offset \(offset)",
5757+ underlyingError: self
5858+ ))
5959+ case let .cannotRepresentInt(max, found, offset):
6060+ return DecodingError.dataCorrupted(
6161+ .init(
6262+ codingPath: [],
6363+ debugDescription: "Failed to decode integer with maximum \(max), "
6464+ + "found \(found) at \(offset)",
6565+ underlyingError: self
6666+ )
6767+ )
6868+ case .recursionLimit:
6969+ return DecodingError.dataCorrupted(
7070+ .init(
7171+ codingPath: [],
7272+ debugDescription: "Recursion depth limit exceeded (DecodingOptions.recursionDepth)",
7373+ underlyingError: self
7474+ )
7575+ )
7676+ case .unreadDataAfterEnd:
7777+ return DecodingError.dataCorrupted(
7878+ .init(
7979+ codingPath: [],
8080+ debugDescription: "Configured to reject CBOR data with trailing bytes (DecodingOptions."
8181+ + "singleTopLevelItem)",
8282+ underlyingError: self
8383+ )
8484+ )
8585+ case .rejectedUndefined:
8686+ return DecodingError.dataCorrupted(
8787+ .init(
8888+ codingPath: [],
8989+ debugDescription: "Configured to reject undefined values (DecodingOptions.rejectUndefined)",
9090+ underlyingError: self
9191+ )
9292+ )
9393+ case .unnecessaryInt:
9494+ return DecodingError.dataCorrupted(
9595+ .init(
9696+ codingPath: [],
9797+ debugDescription: "Found integer that was encoded in a larger data format than necessary",
9898+ underlyingError: self
9999+ )
100100+ )
101101+ }
102102+ }
103103+}
+1-1
Sources/CBOR/Encoder/CBOREncoder.swift
···5656 let tempStorage = TopLevelTemporaryEncodingStorage()
57575858 let encodingContext = EncodingContext(options: options)
5959- let encoder = SingleValueCBOREncodingContainer(parent: tempStorage, context: encodingContext)
5959+ let encoder = try SingleValueCBOREncodingContainer(parent: tempStorage, context: encodingContext)
6060 try encoder.encode(value)
61616262 let dataSize = tempStorage.value.size
···11+// From https://github.com/Flight-School/AnyCodable/blob/master/Sources/AnyCodable/AnyDecodable.swift
22+33+// swiftlint:disable cyclomatic_complexity
44+// swiftlint:disable line_length
55+// swiftlint:disable type_name
66+77+#if canImport(FoundationEssentials)
88+import FoundationEssentials
99+#else
1010+import Foundation
1111+#endif
1212+1313+/**
1414+ A type-erased `Decodable` value.
1515+1616+ The `AnyDecodable` type forwards decoding responsibilities
1717+ to an underlying value, hiding its specific underlying type.
1818+1919+ You can decode mixed-type values in dictionaries
2020+ and other collections that require `Decodable` conformance
2121+ by declaring their contained type to be `AnyDecodable`:
2222+2323+ let json = """
2424+ {
2525+ "boolean": true,
2626+ "integer": 42,
2727+ "double": 3.141592653589793,
2828+ "string": "string",
2929+ "array": [1, 2, 3],
3030+ "nested": {
3131+ "a": "alpha",
3232+ "b": "bravo",
3333+ "c": "charlie"
3434+ },
3535+ "null": null
3636+ }
3737+ """.data(using: .utf8)!
3838+3939+ let decoder = JSONDecoder()
4040+ let dictionary = try! decoder.decode([String: AnyDecodable].self, from: json)
4141+ */
4242+struct AnyDecodable: Decodable {
4343+ let value: Any
4444+4545+ init<T>(_ value: T?) {
4646+ self.value = value ?? ()
4747+ }
4848+}
4949+5050+@usableFromInline
5151+protocol _AnyDecodable {
5252+ var value: Any { get }
5353+ init<T>(_ value: T?)
5454+}
5555+5656+extension AnyDecodable: _AnyDecodable {}
5757+5858+extension _AnyDecodable {
5959+ init(from decoder: Decoder) throws {
6060+ let container = try decoder.singleValueContainer()
6161+6262+ if container.decodeNil() {
6363+ self.init(Optional<Self>.none)
6464+ } else if let bool = try? container.decode(Bool.self) {
6565+ self.init(bool)
6666+ } else if let int = try? container.decode(Int.self) {
6767+ self.init(int)
6868+ } else if let uint = try? container.decode(UInt.self) {
6969+ self.init(uint)
7070+ } else if let double = try? container.decode(Double.self) {
7171+ self.init(double)
7272+ } else if let string = try? container.decode(String.self) {
7373+ self.init(string)
7474+ } else if let array = try? container.decode([AnyDecodable].self) {
7575+ self.init(array.map { $0.value })
7676+ } else if let dictionary = try? container.decode([String: AnyDecodable].self) {
7777+ self.init(dictionary.mapValues { $0.value })
7878+ } else {
7979+ throw DecodingError.dataCorruptedError(in: container, debugDescription: "AnyDecodable value cannot be decoded")
8080+ }
8181+ }
8282+}
8383+8484+extension AnyDecodable: Equatable {
8585+ static func == (lhs: AnyDecodable, rhs: AnyDecodable) -> Bool {
8686+ switch (lhs.value, rhs.value) {
8787+ case (Optional<Self>.none, Optional<Self>.none), is (Void, Void):
8888+ return true
8989+ case let (lhs as Bool, rhs as Bool):
9090+ return lhs == rhs
9191+ case let (lhs as Int, rhs as Int):
9292+ return lhs == rhs
9393+ case let (lhs as Int8, rhs as Int8):
9494+ return lhs == rhs
9595+ case let (lhs as Int16, rhs as Int16):
9696+ return lhs == rhs
9797+ case let (lhs as Int32, rhs as Int32):
9898+ return lhs == rhs
9999+ case let (lhs as Int64, rhs as Int64):
100100+ return lhs == rhs
101101+ case let (lhs as UInt, rhs as UInt):
102102+ return lhs == rhs
103103+ case let (lhs as UInt8, rhs as UInt8):
104104+ return lhs == rhs
105105+ case let (lhs as UInt16, rhs as UInt16):
106106+ return lhs == rhs
107107+ case let (lhs as UInt32, rhs as UInt32):
108108+ return lhs == rhs
109109+ case let (lhs as UInt64, rhs as UInt64):
110110+ return lhs == rhs
111111+ case let (lhs as Float, rhs as Float):
112112+ return lhs == rhs
113113+ case let (lhs as Double, rhs as Double):
114114+ return lhs == rhs
115115+ case let (lhs as String, rhs as String):
116116+ return lhs == rhs
117117+ case let (lhs as [String: AnyDecodable], rhs as [String: AnyDecodable]):
118118+ return lhs == rhs
119119+ case let (lhs as [AnyDecodable], rhs as [AnyDecodable]):
120120+ return lhs == rhs
121121+ default:
122122+ return false
123123+ }
124124+ }
125125+}
126126+127127+extension AnyDecodable: CustomStringConvertible {
128128+ var description: String {
129129+ switch value {
130130+ case is Void:
131131+ return String(describing: nil as Any?)
132132+ case let value as CustomStringConvertible:
133133+ return value.description
134134+ default:
135135+ return String(describing: value)
136136+ }
137137+ }
138138+}
139139+140140+extension AnyDecodable: CustomDebugStringConvertible {
141141+ var debugDescription: String {
142142+ switch value {
143143+ case let value as CustomDebugStringConvertible:
144144+ return "AnyDecodable(\(value.debugDescription))"
145145+ default:
146146+ return "AnyDecodable(\(description))"
147147+ }
148148+ }
149149+}
150150+151151+extension AnyDecodable: Hashable {
152152+ func hash(into hasher: inout Hasher) {
153153+ switch value {
154154+ case let value as Bool:
155155+ hasher.combine(value)
156156+ case let value as Int:
157157+ hasher.combine(value)
158158+ case let value as Int8:
159159+ hasher.combine(value)
160160+ case let value as Int16:
161161+ hasher.combine(value)
162162+ case let value as Int32:
163163+ hasher.combine(value)
164164+ case let value as Int64:
165165+ hasher.combine(value)
166166+ case let value as UInt:
167167+ hasher.combine(value)
168168+ case let value as UInt8:
169169+ hasher.combine(value)
170170+ case let value as UInt16:
171171+ hasher.combine(value)
172172+ case let value as UInt32:
173173+ hasher.combine(value)
174174+ case let value as UInt64:
175175+ hasher.combine(value)
176176+ case let value as Float:
177177+ hasher.combine(value)
178178+ case let value as Double:
179179+ hasher.combine(value)
180180+ case let value as String:
181181+ hasher.combine(value)
182182+ case let value as [String: AnyDecodable]:
183183+ hasher.combine(value)
184184+ case let value as [AnyDecodable]:
185185+ hasher.combine(value)
186186+ default:
187187+ break
188188+ }
189189+ }
190190+}
191191+192192+// swiftlint:enable cyclomatic_complexity
193193+// swiftlint:enable line_length
194194+// swiftlint:enable type_name