···11-struct AnyCodingKey: CodingKey, Equatable {
22- var stringValue: String {
33- get { self._stringValue ?? "_do_not_use_this_string_value_use_the_int_value_instead_" }
44- }
55-66- var _stringValue: String?
77- var intValue: Int?
88-99- init(stringValue: String) {
1010- self._stringValue = stringValue
1111- self.intValue = nil
1212- }
1313-1414- init(intValue: Int) {
1515- self.intValue = intValue
1616- self._stringValue = nil
1717- }
1818-1919- init<Key: CodingKey>(_ base: Key, useStringKey: Bool = false) {
2020- if !useStringKey, let intValue = base.intValue {
2121- self.init(intValue: intValue)
2222- } else {
2323- self.init(stringValue: base.stringValue)
2424- }
2525- }
2626-2727- func key<K: CodingKey>() -> K {
2828- if let intValue = self.intValue {
2929- return K(intValue: intValue)!
3030- } else if let stringValue = self._stringValue {
3131- return K(stringValue: stringValue)!
3232- } else {
3333- fatalError("AnyCodingKey created without a string or int value")
3434- }
3535- }
3636-}
3737-3838-extension AnyCodingKey: Hashable {
3939- public func hash(into hasher: inout Hasher) {
4040- self.intValue?.hash(into: &hasher) ?? self._stringValue?.hash(into: &hasher)
4141- }
4242-}
4343-4444-extension AnyCodingKey: Encodable {
4545- func encode(to encoder: Encoder) throws {
4646- var container = encoder.singleValueContainer()
4747- if let intValue = self.intValue {
4848- try container.encode(intValue)
4949- } else if let stringValue = self._stringValue {
5050- try container.encode(stringValue)
5151- } else {
5252- fatalError("AnyCodingKey created without a string or int value")
5353- }
5454- }
5555-}
5656-5757-extension AnyCodingKey: Decodable {
5858- init(from decoder: Decoder) throws {
5959- let value = try decoder.singleValueContainer()
6060- if let intValue = try? value.decode(Int.self) {
6161- self._stringValue = nil
6262- self.intValue = intValue
6363- } else {
6464- self._stringValue = try! value.decode(String.self)
6565- self.intValue = nil
6666- }
6767- }
6868-}
-179
Old/CBOR.swift
···11-#if canImport(Foundation)
22-import Foundation
33-#endif
44-55-public indirect enum CBOR : Equatable, Hashable,
66- ExpressibleByNilLiteral, ExpressibleByIntegerLiteral, ExpressibleByStringLiteral,
77- ExpressibleByArrayLiteral, ExpressibleByDictionaryLiteral, ExpressibleByBooleanLiteral,
88- ExpressibleByFloatLiteral {
99-1010- case unsignedInt(UInt64)
1111- case negativeInt(UInt64)
1212- case byteString([UInt8])
1313- case utf8String(String)
1414- case array([CBOR])
1515- case map([CBOR : CBOR])
1616- case tagged(Tag, CBOR)
1717- case simple(UInt8)
1818- case boolean(Bool)
1919- case null
2020- case undefined
2121- case half(Float32)
2222- case float(Float32)
2323- case double(Float64)
2424- case `break`
2525- #if canImport(Foundation)
2626- case date(Date)
2727- #endif
2828-2929- public func hash(into hasher: inout Hasher) {
3030- switch self {
3131- case let .unsignedInt(l): l.hash(into: &hasher)
3232- case let .negativeInt(l): l.hash(into: &hasher)
3333- case let .byteString(l): Util.djb2Hash(l.map { Int($0) }).hash(into: &hasher)
3434- case let .utf8String(l): l.hash(into: &hasher)
3535- case let .array(l): Util.djb2Hash(l.map { $0.hashValue }).hash(into: &hasher)
3636- case let .map(l): Util.djb2Hash(l.map { $0.hashValue &+ $1.hashValue }).hash(into: &hasher)
3737- case let .tagged(t, l): t.hash(into: &hasher)
3838- l.hash(into: &hasher)
3939- case let .simple(l): l.hash(into: &hasher)
4040- case let .boolean(l): l.hash(into: &hasher)
4141- case .null: (-1).hash(into: &hasher)
4242- case .undefined: (-2).hash(into: &hasher)
4343- case let .half(l): l.hash(into: &hasher)
4444- case let .float(l): l.hash(into: &hasher)
4545- case let .double(l): l.hash(into: &hasher)
4646- #if canImport(Foundation)
4747- case let .date(l): l.hash(into: &hasher)
4848- #endif
4949- case .break: Int.min.hash(into: &hasher)
5050- }
5151- }
5252-5353- public subscript(position: CBOR) -> CBOR? {
5454- get {
5555- switch (self, position) {
5656- case (let .array(l), let .unsignedInt(i)): return l[Int(i)]
5757- case (let .map(l), let i): return l[i]
5858- default: return nil
5959- }
6060- }
6161- set(x) {
6262- switch (self, position) {
6363- case (var .array(l), let .unsignedInt(i)):
6464- l[Int(i)] = x!
6565- self = .array(l)
6666- case (var .map(l), let i):
6767- l[i] = x!
6868- self = .map(l)
6969- default: break
7070- }
7171- }
7272- }
7373-7474- public init(nilLiteral: ()) { self = .null }
7575- public init(integerLiteral value: Int) {
7676- if value < 0 {
7777- self = .negativeInt(~UInt64(bitPattern: Int64(value)))
7878- } else {
7979- self = .unsignedInt(UInt64(value))
8080- }
8181- }
8282- public init(extendedGraphemeClusterLiteral value: String) { self = .utf8String(value) }
8383- public init(unicodeScalarLiteral value: String) { self = .utf8String(value) }
8484- public init(stringLiteral value: String) { self = .utf8String(value) }
8585- public init(arrayLiteral elements: CBOR...) { self = .array(elements) }
8686- public init(dictionaryLiteral elements: (CBOR, CBOR)...) {
8787- var result = [CBOR : CBOR]()
8888- for (key, value) in elements {
8989- result[key] = value
9090- }
9191- self = .map(result)
9292- }
9393- public init(booleanLiteral value: Bool) { self = .boolean(value) }
9494- public init(floatLiteral value: Float32) { self = .float(value) }
9595-9696- public static func ==(lhs: CBOR, rhs: CBOR) -> Bool {
9797- switch (lhs, rhs) {
9898- case (let .unsignedInt(l), let .unsignedInt(r)): return l == r
9999- case (let .negativeInt(l), let .negativeInt(r)): return l == r
100100- case (let .byteString(l), let .byteString(r)): return l == r
101101- case (let .utf8String(l), let .utf8String(r)): return l == r
102102- case (let .array(l), let .array(r)): return l == r
103103- case (let .map(l), let .map(r)): return l == r
104104- case (let .tagged(tl, l), let .tagged(tr, r)): return tl == tr && l == r
105105- case (let .simple(l), let .simple(r)): return l == r
106106- case (let .boolean(l), let .boolean(r)): return l == r
107107- case (.null, .null): return true
108108- case (.undefined, .undefined): return true
109109- case (let .half(l), let .half(r)): return l == r
110110- case (let .float(l), let .float(r)): return l == r
111111- case (let .double(l), let .double(r)): return l == r
112112- #if canImport(Foundation)
113113- case (let .date(l), let .date(r)): return l == r
114114- #endif
115115- case (.break, .break): return true
116116- case (.unsignedInt, _): return false
117117- case (.negativeInt, _): return false
118118- case (.byteString, _): return false
119119- case (.utf8String, _): return false
120120- case (.array, _): return false
121121- case (.map, _): return false
122122- case (.tagged, _): return false
123123- case (.simple, _): return false
124124- case (.boolean, _): return false
125125- case (.null, _): return false
126126- case (.undefined, _): return false
127127- case (.half, _): return false
128128- case (.float, _): return false
129129- case (.double, _): return false
130130- case (.break, _): return false
131131- default: return false
132132- }
133133- }
134134-135135- public struct Tag: RawRepresentable, Hashable, Sendable {
136136- public let rawValue: UInt64
137137-138138- public init(rawValue: UInt64) {
139139- self.rawValue = rawValue
140140- }
141141-142142- public var hashValue : Int {
143143- return rawValue.hashValue
144144- }
145145- }
146146-}
147147-148148-extension CBOR.Tag {
149149- public static let standardDateTimeString = CBOR.Tag(rawValue: 0)
150150- public static let epochBasedDateTime = CBOR.Tag(rawValue: 1)
151151- public static let positiveBignum = CBOR.Tag(rawValue: 2)
152152- public static let negativeBignum = CBOR.Tag(rawValue: 3)
153153- public static let decimalFraction = CBOR.Tag(rawValue: 4)
154154- public static let bigfloat = CBOR.Tag(rawValue: 5)
155155-156156- // 6...20 unassigned
157157-158158- public static let expectedConversionToBase64URLEncoding = CBOR.Tag(rawValue: 21)
159159- public static let expectedConversionToBase64Encoding = CBOR.Tag(rawValue: 22)
160160- public static let expectedConversionToBase16Encoding = CBOR.Tag(rawValue: 23)
161161- public static let encodedCBORDataItem = CBOR.Tag(rawValue: 24)
162162-163163- // 25...31 unassigned
164164-165165- public static let uri = CBOR.Tag(rawValue: 32)
166166- public static let base64Url = CBOR.Tag(rawValue: 33)
167167- public static let base64 = CBOR.Tag(rawValue: 34)
168168- public static let regularExpression = CBOR.Tag(rawValue: 35)
169169- public static let mimeMessage = CBOR.Tag(rawValue: 36)
170170- public static let uuid = CBOR.Tag(rawValue: 37)
171171-172172- // 38...55798 unassigned
173173-174174- public static let selfDescribeCBOR = CBOR.Tag(rawValue: 55799)
175175-}
176176-177177-#if os(Linux) || os(Windows) || os(Android)
178178-let NSEC_PER_SEC: UInt64 = 1_000_000_000
179179-#endif
-235
Old/CBORDecoder.swift
···11-#if canImport(Foundation)
22-import Foundation
33-#endif
44-55-public enum CBORError : Error {
66- case unfinishedSequence
77- case wrongTypeInsideSequence
88- case tooLongSequence
99- case incorrectUTF8String
1010- case maximumDepthExceeded
1111-}
1212-1313-extension CBOR {
1414- static public func decode(_ input: [UInt8], options: CBOROptions = CBOROptions()) throws -> CBOR? {
1515- return try CBORDecoder(input: input, options: options).decodeItem()
1616- }
1717-}
1818-1919-public class CBORDecoder {
2020- private var istream : CBORInputStream
2121- public var options: CBOROptions
2222- private var currentDepth = 0
2323-2424- public init(stream: CBORInputStream, options: CBOROptions = CBOROptions()) {
2525- self.istream = stream
2626- self.options = options
2727- }
2828-2929- public init(input: ArraySlice<UInt8>, options: CBOROptions = CBOROptions()) {
3030- self.istream = ArraySliceUInt8(slice: input)
3131- self.options = options
3232- }
3333-3434- public init(input: [UInt8], options: CBOROptions = CBOROptions()) {
3535- self.istream = ArrayUInt8(array: ArraySlice(input))
3636- self.options = options
3737- }
3838-3939- func readBinaryNumber<T>(_ type: T.Type) throws -> T {
4040- Array(try self.istream.popBytes(MemoryLayout<T>.size).reversed()).withUnsafeBytes { ptr in
4141- return ptr.load(as: T.self)
4242- }
4343- }
4444-4545- func readVarUInt(_ v: UInt8, base: UInt8) throws -> UInt64 {
4646- guard v > base + 0x17 else { return UInt64(v - base) }
4747-4848- switch VarUIntSize(rawValue: v) {
4949- case .uint8: return UInt64(try readBinaryNumber(UInt8.self))
5050- case .uint16: return UInt64(try readBinaryNumber(UInt16.self))
5151- case .uint32: return UInt64(try readBinaryNumber(UInt32.self))
5252- case .uint64: return UInt64(try readBinaryNumber(UInt64.self))
5353- }
5454- }
5555-5656- func readLength(_ v: UInt8, base: UInt8) throws -> Int {
5757- let n = try readVarUInt(v, base: base)
5858-5959- guard n <= Int.max else {
6060- throw CBORError.tooLongSequence
6161- }
6262-6363- return Int(n)
6464- }
6565-6666- private func readN(_ n: Int) throws -> [CBOR] {
6767- return try (0..<n).map { _ in
6868- guard let r = try decodeItem() else { throw CBORError.unfinishedSequence }
6969- return r
7070- }
7171- }
7272-7373- func readUntilBreak() throws -> [CBOR] {
7474- var result: [CBOR] = []
7575- var cur = try decodeItem()
7676- while cur != CBOR.break {
7777- guard let curr = cur else { throw CBORError.unfinishedSequence }
7878- result.append(curr)
7979- cur = try decodeItem()
8080- }
8181- return result
8282- }
8383-8484- private func readNPairs(_ n: Int) throws -> [CBOR : CBOR] {
8585- var result: [CBOR: CBOR] = [:]
8686- for _ in (0..<n) {
8787- guard let key = try decodeItem() else { throw CBORError.unfinishedSequence }
8888- guard let val = try decodeItem() else { throw CBORError.unfinishedSequence }
8989- result[key] = val
9090- }
9191- return result
9292- }
9393-9494- func readPairsUntilBreak() throws -> [CBOR : CBOR] {
9595- var result: [CBOR: CBOR] = [:]
9696- var key = try decodeItem()
9797- if key == CBOR.break {
9898- return result
9999- }
100100- var val = try decodeItem()
101101- while key != CBOR.break {
102102- guard let okey = key else { throw CBORError.unfinishedSequence }
103103- guard let oval = val else { throw CBORError.unfinishedSequence }
104104- result[okey] = oval
105105- do { key = try decodeItem() } catch CBORError.unfinishedSequence { key = nil }
106106- guard (key != CBOR.break) else { break } // don't eat the val after the break!
107107- do { val = try decodeItem() } catch CBORError.unfinishedSequence { val = nil }
108108- }
109109- return result
110110- }
111111-112112- public func decodeItem() throws -> CBOR? {
113113- guard currentDepth <= options.maximumDepth
114114- else { throw CBORError.maximumDepthExceeded }
115115-116116- currentDepth += 1
117117- defer { currentDepth -= 1 }
118118- let b = try istream.popByte()
119119-120120- switch b {
121121- // positive integers
122122- case 0x00...0x1b:
123123- return CBOR.unsignedInt(try readVarUInt(b, base: 0x00))
124124-125125- // negative integers
126126- case 0x20...0x3b:
127127- return CBOR.negativeInt(try readVarUInt(b, base: 0x20))
128128-129129- // byte strings
130130- case 0x40...0x5b:
131131- let numBytes = try readLength(b, base: 0x40)
132132- return CBOR.byteString(Array(try istream.popBytes(numBytes)))
133133- case 0x5f:
134134- return CBOR.byteString(try readUntilBreak().flatMap { x -> [UInt8] in
135135- guard case .byteString(let r) = x else { throw CBORError.wrongTypeInsideSequence }
136136- return r
137137- })
138138-139139- // utf-8 strings
140140- case 0x60...0x7b:
141141- let numBytes = try readLength(b, base: 0x60)
142142- return CBOR.utf8String(try Util.decodeUtf8(try istream.popBytes(numBytes)))
143143- case 0x7f:
144144- return CBOR.utf8String(try readUntilBreak().map { x -> String in
145145- guard case .utf8String(let r) = x else { throw CBORError.wrongTypeInsideSequence }
146146- return r
147147- }.joined(separator: ""))
148148-149149- // arrays
150150- case 0x80...0x9b:
151151- let numBytes = try readLength(b, base: 0x80)
152152- return CBOR.array(try readN(numBytes))
153153- case 0x9f:
154154- return CBOR.array(try readUntilBreak())
155155-156156- // pairs
157157- case 0xa0...0xbb:
158158- let numBytes = try readLength(b, base: 0xa0)
159159- let pairs = try readNPairs(numBytes)
160160- if self.options.dateStrategy == .annotatedMap {
161161- if let annotatedType = pairs[CBOR.utf8String(AnnotatedMapDateStrategy.typeKey)],
162162- annotatedType == CBOR.utf8String(AnnotatedMapDateStrategy.typeValue),
163163- let dateEpochTimestampCBOR = pairs[CBOR.utf8String(AnnotatedMapDateStrategy.valueKey)],
164164- let date = try? getDateFromTimestamp(dateEpochTimestampCBOR)
165165- {
166166- return CBOR.date(date)
167167- }
168168- }
169169- return CBOR.map(pairs)
170170- case 0xbf:
171171- return CBOR.map(try readPairsUntilBreak())
172172-173173- // tagged values
174174- case 0xc0...0xdb:
175175- let tag = try readVarUInt(b, base: 0xc0)
176176- guard let item = try decodeItem() else { throw CBORError.unfinishedSequence }
177177- #if canImport(Foundation)
178178- if tag == 1 {
179179- let date = try getDateFromTimestamp(item)
180180- return CBOR.date(date)
181181- }
182182- #endif
183183- return CBOR.tagged(CBOR.Tag(rawValue: tag), item)
184184-185185- case 0xe0...0xf3: return CBOR.simple(b - 0xe0)
186186- case 0xf4: return CBOR.boolean(false)
187187- case 0xf5: return CBOR.boolean(true)
188188- case 0xf6: return CBOR.null
189189- case 0xf7: return CBOR.undefined
190190- case 0xf8: return CBOR.simple(try istream.popByte())
191191-192192- case 0xf9:
193193- return CBOR.half(Util.readFloat16(x: try readBinaryNumber(UInt16.self)))
194194- case 0xfa:
195195- return CBOR.float(try readBinaryNumber(Float32.self))
196196- case 0xfb:
197197- return CBOR.double(try readBinaryNumber(Float64.self))
198198-199199- case 0xff: return CBOR.break
200200- default: return nil
201201- }
202202- }
203203-}
204204-205205-func getDateFromTimestamp(_ item: CBOR) throws -> Date {
206206- switch item {
207207- case .double(let d):
208208- return Date(timeIntervalSince1970: TimeInterval(d))
209209- case .negativeInt(let n):
210210- return Date(timeIntervalSince1970: TimeInterval(-1 - Double(n)))
211211- case .float(let f):
212212- return Date(timeIntervalSince1970: TimeInterval(f))
213213- case .unsignedInt(let u):
214214- return Date(timeIntervalSince1970: TimeInterval(u))
215215- default:
216216- throw CBORError.wrongTypeInsideSequence
217217- }
218218-}
219219-220220-private enum VarUIntSize: UInt8 {
221221- case uint8 = 0
222222- case uint16 = 1
223223- case uint32 = 2
224224- case uint64 = 3
225225-226226- init(rawValue: UInt8) {
227227- switch rawValue & 0b11 {
228228- case 0: self = .uint8
229229- case 1: self = .uint16
230230- case 2: self = .uint32
231231- case 3: self = .uint64
232232- default: fatalError() // mask only allows values from 0-3
233233- }
234234- }
235235-}
···11+//
22+// CBORScanner+Utils.swift
33+// CBOR
44+//
55+// Created by Khan Winter on 8/30/25.
66+//
77+88+extension CBORScanner {
99+ func popByteCount() throws -> Int {
1010+ let byteCount = reader.popArgument()
1111+ return switch byteCount {
1212+ case let value where value < Constants.maxArgSize: 0
1313+ case 24: 1
1414+ case 25: 2
1515+ case 26: 4
1616+ case 27: 8
1717+ default:
1818+ throw ScanError.invalidSize(byte: byteCount, offset: reader.index - 1)
1919+ }
2020+ }
2121+2222+ func peekIsIndeterminate() -> Bool {
2323+ (reader.peekArgument() ?? 0) == 0b1_1111
2424+ }
2525+}
+27-67
Sources/CBOR/Decoder/Scanner/CBORScanner.swift
···7788import Foundation
991010+@usableFromInline
1111+enum ScanError: Error {
1212+ case unexpectedEndOfData
1313+ case invalidMajorType(byte: UInt8, offset: Int)
1414+ case invalidSize(byte: UInt8, offset: Int)
1515+ case expectedMajorType(offset: Int)
1616+ case typeInIndeterminateString(type: MajorType, offset: Int)
1717+ case rejectedIndeterminateLength(type: MajorType, offset: Int)
1818+ case cannotRepresentInt(max: UInt, found: UInt, offset: Int)
1919+}
2020+1021/// # Why Scan?
1122/// I'd have loved to use a 'pop' method for decoding, where we only decode as data is requested. However, the way
1223/// Swift's decoding APIs work forces us to be able to be able to do random access for keys in maps, which requires
···2132/// take either indeterminate or specific lengths and decode them.
2233@usableFromInline
2334final class CBORScanner {
2424- @usableFromInline
2525- enum ScanError: Error {
2626- case unexpectedEndOfData
2727- case invalidMajorType(byte: UInt8, offset: Int)
2828- case invalidSize(byte: UInt8, offset: Int)
2929- case expectedMajorType(offset: Int)
3030- case typeInIndeterminateString(type: MajorType, offset: Int)
3131- case rejectedIndeterminateLength(type: MajorType, offset: Int)
3232- }
3333-3435 // MARK: - Results
35363637 /// After the scanner scans, this contains a map that allows the CBOR data to be scanned for values at arbitrary
···9697 var results: Results
9798 let options: DecodingOptions
9899100100+ var isEmpty: Bool {
101101+ results.map.isEmpty
102102+ }
103103+99104 init(data: DataReader, options: DecodingOptions = DecodingOptions()) {
100105 self.reader = data
101106 self.results = Results(dataCount: data.count)
···199204 case .map:
200205 try scanMap()
201206 case .simple:
202202- scanSimple(raw: raw)
207207+ try scanSimple(raw: raw)
203208 case .tagged:
204204- fatalError()
209209+ throw ScanError.expectedMajorType(offset: 0)
205210 }
206211 }
207212···217222218223 // MARK: - Scan Simple
219224220220- private func scanSimple(raw: UInt8) {
225225+ private func scanSimple(raw: UInt8) throws {
221226 let idx = reader.index
222227 results.recordSimple(reader.pop(), currentByteIndex: idx)
228228+ guard reader.canRead(simpleLength(raw)) else {
229229+ throw ScanError.unexpectedEndOfData
230230+ }
223231 reader.pop(simpleLength(raw))
224232 }
225233···308316309317 private func scanMap() throws {
310318 guard peekIsIndeterminate() else {
311311- let size = try reader.readNextInt(as: Int.self) * 2
319319+ let keyCount = try reader.readNextInt(as: Int.self)
320320+ guard keyCount < Int.max / 2 else {
321321+ throw ScanError.cannotRepresentInt(max: UInt(Int.max), found: UInt(keyCount) * 2, offset: reader.index)
322322+ }
323323+324324+ let size = keyCount * 2
312325 let mapIdx = results.recordMapStart(currentByteIndex: reader.index)
313326 for _ in 0..<size {
314327 try scanNext()
···334347 results.recordEnd(childCount: count, resultLocation: mapIdx, currentByteIndex: reader.index)
335348 }
336349}
337337-338338-// MARK: - Utils
339339-340340-extension CBORScanner {
341341- func popByteCount() throws -> Int {
342342- let byteCount = reader.popArgument()
343343- return switch byteCount {
344344- case let value where value < Constants.maxArgSize: 0
345345- case 24: 1
346346- case 25: 2
347347- case 26: 4
348348- case 27: 8
349349- default:
350350- throw ScanError.invalidSize(byte: byteCount, offset: reader.index - 1)
351351- }
352352- }
353353-354354- func peekIsIndeterminate() -> Bool {
355355- (reader.peekArgument() ?? 0) == 0b1_1111
356356- }
357357-}
358358-359359-// MARK: - Debug Description
360360-361361-#if DEBUG
362362-extension CBORScanner: CustomDebugStringConvertible {
363363- @usableFromInline var debugDescription: String {
364364- var string = ""
365365- func indent(_ other: String, d: Int) { string += String(repeating: " ", count: d * 2) + other + "\n" }
366366-367367- func gen(_ idx: Int, depth: Int) {
368368- let value = load(at: idx)
369369- switch value.type {
370370- case .map, .array:
371371- indent(
372372- "\(value.type), mapIdx: \(value.mapOffset), children: \(value.childCount!), bytes: \(value.count)",
373373- d: depth
374374- )
375375- var idx = firstChildIndex(idx)
376376- for _ in 0..<value.childCount! {
377377- gen(idx, depth: depth + 1)
378378- idx = siblingIndex(idx)
379379- }
380380- default:
381381- indent("\(value.type), mapIdx: \(value.mapOffset), arg: \(value.argument)", d: depth)
382382- }
383383- }
384384-385385- gen(0, depth: 0)
386386- return string
387387- }
388388-}
389389-#endif
+5-27
Sources/CBOR/Decoder/Scanner/DataReader.swift
···8686 peekType() == .simple && peekArgument() == 22
8787 }
88888989- @inline(__always)
9090- mutating func readInt<F: FixedWidthInteger>(as: F.Type) throws -> F {
9191- guard canRead(F.byteCount) else {
9292- throw CBORScanner.ScanError.unexpectedEndOfData
9393- }
9494- var val = F.zero
9595- for idx in 0..<(F.byteCount) {
9696- let shift = (F.byteCount - idx - 1) * 8
9797- val |= F(pop()) << shift
9898- }
9999- return F(val)
100100- }
101101-10289 /// Reads the next variable-sized integer off the data stack.
10390 @inlinable
10491 mutating func readNextInt<T: FixedWidthInteger>(as: T.Type) throws -> T {
105105- let byteCount = popArgument()
106106- return switch byteCount {
107107- case let value where value < Constants.maxArgSize:
108108- T(value)
109109- case 24:
110110- T(try readInt(as: UInt8.self))
111111- case 25:
112112- T(try readInt(as: UInt16.self))
113113- case 26:
114114- T(try readInt(as: UInt32.self))
115115- case 27:
116116- T(try readInt(as: UInt64.self))
117117- default:
118118- throw CBORScanner.ScanError.invalidSize(byte: byteCount, offset: index - 1)
9292+ let arg = popArgument()
9393+ let value = try data.readInt(as: T.self, argument: arg, from: data.startIndex + index)
9494+ if let byteCount = arg.byteCount(), byteCount > 0 {
9595+ pop(Int(byteCount))
11996 }
9797+ return value
12098 }
12199122100 @inlinable
+198
Sources/Fuzzing/AnyDecodable.swift
···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(Foundation)
88+import Foundation
99+#endif
1010+1111+/**
1212+ A type-erased `Decodable` value.
1313+1414+ The `AnyDecodable` type forwards decoding responsibilities
1515+ to an underlying value, hiding its specific underlying type.
1616+1717+ You can decode mixed-type values in dictionaries
1818+ and other collections that require `Decodable` conformance
1919+ by declaring their contained type to be `AnyDecodable`:
2020+2121+ let json = """
2222+ {
2323+ "boolean": true,
2424+ "integer": 42,
2525+ "double": 3.141592653589793,
2626+ "string": "string",
2727+ "array": [1, 2, 3],
2828+ "nested": {
2929+ "a": "alpha",
3030+ "b": "bravo",
3131+ "c": "charlie"
3232+ },
3333+ "null": null
3434+ }
3535+ """.data(using: .utf8)!
3636+3737+ let decoder = JSONDecoder()
3838+ let dictionary = try! decoder.decode([String: AnyDecodable].self, from: json)
3939+ */
4040+struct AnyDecodable: Decodable {
4141+ let value: Any
4242+4343+ init<T>(_ value: T?) {
4444+ self.value = value ?? ()
4545+ }
4646+}
4747+4848+@usableFromInline
4949+protocol _AnyDecodable {
5050+ var value: Any { get }
5151+ init<T>(_ value: T?)
5252+}
5353+5454+extension AnyDecodable: _AnyDecodable {}
5555+5656+extension _AnyDecodable {
5757+ init(from decoder: Decoder) throws {
5858+ let container = try decoder.singleValueContainer()
5959+6060+ if container.decodeNil() {
6161+ #if canImport(Foundation)
6262+ self.init(NSNull())
6363+ #else
6464+ self.init(Optional<Self>.none)
6565+ #endif
6666+ } else if let bool = try? container.decode(Bool.self) {
6767+ self.init(bool)
6868+ } else if let int = try? container.decode(Int.self) {
6969+ self.init(int)
7070+ } else if let uint = try? container.decode(UInt.self) {
7171+ self.init(uint)
7272+ } else if let double = try? container.decode(Double.self) {
7373+ self.init(double)
7474+ } else if let string = try? container.decode(String.self) {
7575+ self.init(string)
7676+ } else if let array = try? container.decode([AnyDecodable].self) {
7777+ self.init(array.map { $0.value })
7878+ } else if let dictionary = try? container.decode([String: AnyDecodable].self) {
7979+ self.init(dictionary.mapValues { $0.value })
8080+ } else {
8181+ throw DecodingError.dataCorruptedError(in: container, debugDescription: "AnyDecodable value cannot be decoded")
8282+ }
8383+ }
8484+}
8585+8686+extension AnyDecodable: Equatable {
8787+ static func == (lhs: AnyDecodable, rhs: AnyDecodable) -> Bool {
8888+ switch (lhs.value, rhs.value) {
8989+#if canImport(Foundation)
9090+ case is (NSNull, NSNull), is (Void, Void):
9191+ return true
9292+#endif
9393+ case let (lhs as Bool, rhs as Bool):
9494+ return lhs == rhs
9595+ case let (lhs as Int, rhs as Int):
9696+ return lhs == rhs
9797+ case let (lhs as Int8, rhs as Int8):
9898+ return lhs == rhs
9999+ case let (lhs as Int16, rhs as Int16):
100100+ return lhs == rhs
101101+ case let (lhs as Int32, rhs as Int32):
102102+ return lhs == rhs
103103+ case let (lhs as Int64, rhs as Int64):
104104+ return lhs == rhs
105105+ case let (lhs as UInt, rhs as UInt):
106106+ return lhs == rhs
107107+ case let (lhs as UInt8, rhs as UInt8):
108108+ return lhs == rhs
109109+ case let (lhs as UInt16, rhs as UInt16):
110110+ return lhs == rhs
111111+ case let (lhs as UInt32, rhs as UInt32):
112112+ return lhs == rhs
113113+ case let (lhs as UInt64, rhs as UInt64):
114114+ return lhs == rhs
115115+ case let (lhs as Float, rhs as Float):
116116+ return lhs == rhs
117117+ case let (lhs as Double, rhs as Double):
118118+ return lhs == rhs
119119+ case let (lhs as String, rhs as String):
120120+ return lhs == rhs
121121+ case let (lhs as [String: AnyDecodable], rhs as [String: AnyDecodable]):
122122+ return lhs == rhs
123123+ case let (lhs as [AnyDecodable], rhs as [AnyDecodable]):
124124+ return lhs == rhs
125125+ default:
126126+ return false
127127+ }
128128+ }
129129+}
130130+131131+extension AnyDecodable: CustomStringConvertible {
132132+ var description: String {
133133+ switch value {
134134+ case is Void:
135135+ return String(describing: nil as Any?)
136136+ case let value as CustomStringConvertible:
137137+ return value.description
138138+ default:
139139+ return String(describing: value)
140140+ }
141141+ }
142142+}
143143+144144+extension AnyDecodable: CustomDebugStringConvertible {
145145+ var debugDescription: String {
146146+ switch value {
147147+ case let value as CustomDebugStringConvertible:
148148+ return "AnyDecodable(\(value.debugDescription))"
149149+ default:
150150+ return "AnyDecodable(\(description))"
151151+ }
152152+ }
153153+}
154154+155155+extension AnyDecodable: Hashable {
156156+ func hash(into hasher: inout Hasher) {
157157+ switch value {
158158+ case let value as Bool:
159159+ hasher.combine(value)
160160+ case let value as Int:
161161+ hasher.combine(value)
162162+ case let value as Int8:
163163+ hasher.combine(value)
164164+ case let value as Int16:
165165+ hasher.combine(value)
166166+ case let value as Int32:
167167+ hasher.combine(value)
168168+ case let value as Int64:
169169+ hasher.combine(value)
170170+ case let value as UInt:
171171+ hasher.combine(value)
172172+ case let value as UInt8:
173173+ hasher.combine(value)
174174+ case let value as UInt16:
175175+ hasher.combine(value)
176176+ case let value as UInt32:
177177+ hasher.combine(value)
178178+ case let value as UInt64:
179179+ hasher.combine(value)
180180+ case let value as Float:
181181+ hasher.combine(value)
182182+ case let value as Double:
183183+ hasher.combine(value)
184184+ case let value as String:
185185+ hasher.combine(value)
186186+ case let value as [String: AnyDecodable]:
187187+ hasher.combine(value)
188188+ case let value as [AnyDecodable]:
189189+ hasher.combine(value)
190190+ default:
191191+ break
192192+ }
193193+ }
194194+}
195195+196196+// swiftlint:enable cyclomatic_complexity
197197+// swiftlint:enable line_length
198198+// swiftlint:enable type_name
+67
Sources/Fuzzing/main.swift
···11+//
22+// fuzz.swift
33+// CBOR
44+//
55+// Created by Khan Winter on 8/29/25.
66+//
77+88+import CBOR
99+import Foundation
1010+1111+@_optimize(none)
1212+func blackhole(_ val: some Any) { }
1313+1414+/// Fuzzing entry point
1515+@_cdecl("LLVMFuzzerTestOneInput")
1616+public func fuzz(_ start: UnsafeRawPointer, _ count: Int) -> CInt {
1717+ let bytes = UnsafeRawBufferPointer(start: start, count: count)
1818+ let data = Data(bytes)
1919+2020+ // Try decoding against a bunch of standard types.
2121+ // Each attempt is independent — errors are ignored, crashes are what fuzzing is for.
2222+ func tryDecode<T: Decodable>(_ type: T.Type) {
2323+ do {
2424+ blackhole(try CBORDecoder(rejectIndeterminateLengths: false).decode(T.self, from: data))
2525+ } catch {
2626+ // ignore decode errors
2727+ }
2828+ }
2929+3030+ // Scalars
3131+ tryDecode(Bool.self)
3232+ tryDecode(Int.self)
3333+ tryDecode(Int8.self)
3434+ tryDecode(Int16.self)
3535+ tryDecode(Int32.self)
3636+ tryDecode(Int64.self)
3737+ tryDecode(UInt.self)
3838+ tryDecode(UInt8.self)
3939+ tryDecode(UInt16.self)
4040+ tryDecode(UInt32.self)
4141+ tryDecode(UInt64.self)
4242+ tryDecode(Float.self)
4343+ tryDecode(Double.self)
4444+ tryDecode(String.self)
4545+ tryDecode(Data.self)
4646+4747+ // Optionals
4848+ tryDecode(Optional<Int>.self)
4949+ tryDecode(Optional<String>.self)
5050+ tryDecode(Optional<Data>.self)
5151+5252+ // Collections
5353+ tryDecode([Int].self)
5454+ tryDecode([String].self)
5555+ tryDecode([Data].self)
5656+ tryDecode([String: Int].self)
5757+ tryDecode([String: String].self)
5858+5959+ // Nested combinations
6060+ tryDecode([[Int]].self)
6161+ tryDecode([String: [String: Int]].self)
6262+6363+ // Any decodable
6464+ tryDecode(AnyDecodable.self)
6565+6666+ return 0
6767+}
+27-26
Tests/CBORTests/DecodableTests.swift
···1616struct DecodableTests {
1717 @Test
1818 func uint8() throws {
1919- var value = try CBORDecoder().decode(UInt8.self, from: [0])
2020- #expect(value == 0)
2121- value = try CBORDecoder().decode(UInt8.self, from: [1])
2222- #expect(value == 1)
2323- // Just below max arg size
2424- value = try CBORDecoder().decode(UInt8.self, from: [23])
2525- #expect(value == 23)
1919+ var value: UInt8 = 0 // try CBORDecoder().decode(UInt8.self, from: [0])
2020+// #expect(value == 0)
2121+// value = try CBORDecoder().decode(UInt8.self, from: [1])
2222+// #expect(value == 1)
2323+// // Just below max arg size
2424+// value = try CBORDecoder().decode(UInt8.self, from: [23])
2525+// #expect(value == 23)
2626 // Just above max arg size
2727 value = try CBORDecoder().decode(UInt8.self, from: [24, 24])
2828 #expect(value == 24)
2929 // Max Int
3030- value = try CBORDecoder().decode(UInt8.self, from: [24, UInt8.max])
3131- #expect(value == UInt8.max)
3030+// value = try CBORDecoder().decode(UInt8.self, from: [24, UInt8.max])
3131+// #expect(value == UInt8.max)
3232+//
3333+// #expect(throws: DecodingError.self) { try CBORDecoder().decode(UInt8.self, from: [128]) }
3434+ }
32353333- #expect(throws: DecodingError.self) { try CBORDecoder().decode(UInt8.self, from: [128]) }
3636+ @Test
3737+ func int8() throws {
3838+ #expect(throws: DecodingError.self) { try CBORDecoder().decode(Int8.self, from: [24, 255]) }
3439 }
35403641 @Test
···182187 ])
183188 func indeterminateString(data: String, expected: String) throws {
184189 let data = data.asHexData()
185185- let string = try CBORDecoder(options: DecodingOptions(rejectIndeterminateLengthStrings: false))
190190+ let string = try CBORDecoder(rejectIndeterminateLengths: false)
186191 .decode(String.self, from: data)
187192 #expect(string == expected)
188193 }
···207212 try data.withUnsafeBytes {
208213 let data = $0[...]
209214 let reader = DataReader(data: data)
210210- let scanner = CBORScanner(data: reader, options: DecodingOptions(rejectIndeterminateLengthArrays: false))
215215+ let scanner = CBORScanner(data: reader, options: DecodingOptions(rejectIndeterminateLengths: false))
211216 try scanner.scan()
212217213218 let context = DecodingContext(scanner: scanner)
···239244240245 @Test
241246 func indeterminateArray() throws {
242242-// let array = "9F0203FF".asHexData()
243243- let options = DecodingOptions(rejectIndeterminateLengthArrays: false)
244244-// #expect(try CBORDecoder(options: options).decode([Int].self, from: array) == [2, 3])
247247+ let array = "9F0203FF".asHexData()
248248+ #expect(try CBORDecoder(rejectIndeterminateLengths: false).decode([Int].self, from: array) == [2, 3])
245249246250 let twodArray = "9F9F0203FF9F0405FFFF".asHexData()
247247- try twodArray.withUnsafeBytes {
248248- let data = $0[...]
249249- let reader = DataReader(data: data)
250250- let scanner = CBORScanner(data: reader, options: DecodingOptions(rejectIndeterminateLengthArrays: false))
251251- try scanner.scan()
252252- print(scanner.debugDescription)
253253- dump(scanner.results.map)
254254- }
255255- let result = try CBORDecoder(options: options).decode([[Int]].self, from: twodArray)
251251+ let result = try CBORDecoder(rejectIndeterminateLengths: false).decode([[Int]].self, from: twodArray)
256252 #expect(result == [[2, 3], [4, 5]])
257253 }
258254259255 @Test
260256 func rejectsIndeterminateArrayWhenConfigured() throws {
261257 let array = "9FFF".asHexData()
262262- let options = DecodingOptions(rejectIndeterminateLengthArrays: true)
263258 #expect(throws: DecodingError.self) {
264264- try CBORDecoder(options: options).decode([Int].self, from: array)
259259+ try CBORDecoder(rejectIndeterminateLengths: true).decode([Int].self, from: array)
265260 }
266261262262+ }
263263+264264+ @Test
265265+ func emptyData() throws {
266266+ let data = Data()
267267+ #expect(throws: DecodingError.self) { try CBORDecoder().decode(Data.self, from: data) }
267268 }
268269}
+60
Tests/CBORTests/RoundTripTests.swift
···5353 #expect(value == result)
5454 }
5555 }
5656+5757+ @Test
5858+ func data() throws {
5959+ let value = Data([0xde, 0xad, 0xbe, 0xef])
6060+ let encoded = try CBOREncoder().encode(value)
6161+ let decoded = try CBORDecoder().decode(Data.self, from: encoded)
6262+ #expect(value == decoded)
6363+ }
6464+6565+ @Test
6666+ func emptyData() throws {
6767+ let value = Data()
6868+ let encoded = try CBOREncoder().encode(value)
6969+ let decoded = try CBORDecoder().decode(Data.self, from: encoded)
7070+ #expect(value == decoded)
7171+ }
7272+7373+ @Test
7474+ func string() throws {
7575+ let value = "Hello, CBOR 👋"
7676+ let encoded = try CBOREncoder().encode(value)
7777+ let decoded = try CBORDecoder().decode(String.self, from: encoded)
7878+ #expect(value == decoded)
7979+ }
8080+8181+ @Test
8282+ func emptyString() throws {
8383+ let value = ""
8484+ let encoded = try CBOREncoder().encode(value)
8585+ let decoded = try CBORDecoder().decode(String.self, from: encoded)
8686+ #expect(value == decoded)
8787+ }
8888+8989+ @Test
9090+ func person() throws {
9191+ let value = Person.mock
9292+ let encoded = try CBOREncoder().encode(value)
9393+ let decoded = try CBORDecoder().decode(Person.self, from: encoded)
9494+ #expect(value.name == decoded.name)
9595+ #expect(value.age == decoded.age)
9696+ #expect(value.email == decoded.email)
9797+ #expect(value.isActive == decoded.isActive)
9898+ #expect(value.tags == decoded.tags)
9999+ }
100100+101101+ @Test
102102+ func company() throws {
103103+ let value = Company.mock
104104+ let encoded = try CBOREncoder().encode(value)
105105+ let decoded = try CBORDecoder().decode(Company.self, from: encoded)
106106+ #expect(value.name == decoded.name)
107107+ #expect(value.founded == decoded.founded)
108108+ #expect(value.employees.count == decoded.employees.count)
109109+ #expect(value.metadata == decoded.metadata)
110110+111111+ // sanity check one employee too
112112+ if let first = decoded.employees.first {
113113+ #expect(first.name == Person.mock.name)
114114+ }
115115+ }
56116}
+4-4
Tests/CBORTests/ScannerTests.swift
···3434 let data = Array(repeating: UInt8.zero, count: count)
3535 Data([argument] + data).withUnsafeBytes {
3636 let data = $0[...]
3737- #expect(throws: CBORScanner.ScanError.self) {
3737+ #expect(throws: ScanError.self) {
3838 try CBORScanner(data: DataReader(data: data)).scan()
3939 }
4040 }
···5959 let data = Data([MajorType.nint.bits | argument] + Array(repeating: UInt8.zero, count: count))
6060 data.withUnsafeBytes {
6161 let data = $0[...]
6262- #expect(throws: CBORScanner.ScanError.self) {
6262+ #expect(throws: ScanError.self) {
6363 try CBORScanner(data: DataReader(data: data)).scan()
6464 }
6565 }
···7777 let data = data.asHexData()
7878 try data.withUnsafeBytes {
7979 let data = $0[...]
8080- let options = DecodingOptions(rejectIndeterminateLengthMaps: false)
8080+ let options = DecodingOptions(rejectIndeterminateLengths: false)
8181 let scanner = CBORScanner(data: DataReader(data: data), options: options)
8282 try scanner.scan()
8383 let map = scanner.results.map
···114114 let data = "9F9F0203FF9F0405FFFF".asHexData()
115115 try data.withUnsafeBytes {
116116 let data = $0[...]
117117- let options = DecodingOptions(rejectIndeterminateLengthArrays: false)
117117+ let options = DecodingOptions(rejectIndeterminateLengths: false)
118118 let scanner = CBORScanner(data: DataReader(data: data), options: options)
119119 try scanner.scan()
120120 let map = scanner.results.map