···11+import Foundation
22+import SwiftCBOR
33+import CBOR
44+55+@inline(never)
66+func blackhole<T>(_ value: T) { }
77+88+// Representative structs
99+struct Person: Codable {
1010+ let name: String
1111+ let age: Int
1212+ let email: String
1313+ let isActive: Bool
1414+ let tags: [String]
1515+}
1616+1717+struct Company: Codable {
1818+ let name: String
1919+ let founded: Int
2020+ let employees: [Person]
2121+ let metadata: [String: String]
2222+}
2323+2424+func measure(iterations: Int, block: () throws -> Void) throws -> (total: Double, avg: Double) {
2525+ var info = mach_timebase_info()
2626+ guard mach_timebase_info(&info) == KERN_SUCCESS else { fatalError() }
2727+ let start = mach_absolute_time()
2828+ for _ in 0..<iterations {
2929+ try block()
3030+ }
3131+ let end = mach_absolute_time()
3232+ let elapsed = end - start
3333+ let nanos = elapsed * UInt64(info.numer) / UInt64(info.denom)
3434+ let msec = TimeInterval(nanos) / TimeInterval(NSEC_PER_MSEC)
3535+ let avg = msec / Double(iterations)
3636+ return (msec, avg)
3737+}
3838+3939+@main
4040+struct Benchmarks {
4141+ // --- main logic ---
4242+ static func main() throws {
4343+ let person = Person(name: "Alice", age: 30, email: "alice@example.com", isActive: true, tags: ["swift", "cbor", "benchmark"])
4444+ let company = Company(
4545+ name: "Acme Corp",
4646+ founded: 1999,
4747+ employees: Array(repeating: person, count: 100),
4848+ metadata: ["industry": "tech", "location": "remote"]
4949+ )
5050+ let string = String(repeating: "a", count: 1000)
5151+ let stringSmall = "abc"
5252+ let data = Data(repeating: 0x42, count: 1024)
5353+ let dataSmall = Data([0x01, 0x02, 0x03])
5454+ let dict = ["key": "value", "foo": "bar", "baz": "qux"]
5555+ let dictSmall = ["a": "b"]
5656+ let array = Array(0..<1000)
5757+ let arraySmall = [1, 2, 3]
5858+ let intVal = 123456789
5959+ let intValSmall = 42
6060+ let boolVal = true
6161+6262+ let swiftCBOR = CodableCBOREncoder()
6363+ let cbor = CBOREncoder()
6464+ let iterations = 1000
6565+ print("--- Codable Encoding Benchmarks (\(iterations) iterations each) ---\n")
6666+6767+ let allBenchmarks: [Benchmark] = [
6868+ PersonBenchmark(person: person),
6969+ CompanyBenchmark(company: company),
7070+ StringBenchmark(string: string),
7171+ StringBenchmark(string: stringSmall, name: "String Small"),
7272+ DataBenchmark(data: data),
7373+ DataBenchmark(data: dataSmall, name: "Data Small"),
7474+ DictionaryBenchmark(dict: dict),
7575+ DictionaryBenchmark(dict: dictSmall, name: "Dictionary Small"),
7676+ ArrayBenchmark(array: array),
7777+ ArrayBenchmark(array: arraySmall, name: "Array Small"),
7878+ IntBenchmark(intVal: intVal),
7979+ IntBenchmark(intVal: intValSmall, name: "Int Small"),
8080+ BoolBenchmark(boolVal: boolVal)
8181+ ]
8282+8383+ let args = CommandLine.arguments
8484+ var only: String? = nil
8585+ if let onlyIdx = args.firstIndex(of: "--only"), onlyIdx + 1 < args.count {
8686+ only = args[onlyIdx + 1].lowercased()
8787+ }
8888+8989+ let benchmarksToRun: [Benchmark]
9090+ if let only = only {
9191+ benchmarksToRun = allBenchmarks.filter { $0.name.lowercased() == only }
9292+ if benchmarksToRun.isEmpty {
9393+ print("No benchmark found for --only \(only)")
9494+ return
9595+ }
9696+ } else {
9797+ benchmarksToRun = allBenchmarks
9898+ }
9999+100100+ for bench in benchmarksToRun {
101101+ try bench.run(swiftCBOR: swiftCBOR, cbor: cbor, iterations: iterations)
102102+ }
103103+ }
104104+}
105105+106106+func printComparison(type: String, swiftCBOR: (Double, Double), cbor: (Double, Double)) {
107107+ let absChange = cbor.1 - swiftCBOR.1
108108+ let percentChange = swiftCBOR.1 == 0 ? 0 : (absChange / swiftCBOR.1) * 100
109109+ print("Encoding: \(type)")
110110+ print(String(format: " SwiftCBOR: total=%.3fms, avg=%.6fms", swiftCBOR.0, swiftCBOR.1))
111111+ print(String(format: " CBOR: total=%.3fms, avg=%.6fms", cbor.0, cbor.1))
112112+ print(String(format: " Δ avg: %.6fms (%+.2f%%)", absChange, percentChange))
113113+ print("")
114114+}
115115+116116+117117+protocol Benchmark {
118118+ var name: String { get }
119119+ func run(swiftCBOR: CodableCBOREncoder, cbor: CBOREncoder, iterations: Int) throws
120120+}
121121+122122+struct PersonBenchmark: Benchmark {
123123+ let name: String
124124+ let person: Person
125125+ init(person: Person, name: String = "Person") { self.person = person; self.name = name }
126126+ func run(swiftCBOR: CodableCBOREncoder, cbor: CBOREncoder, iterations: Int) throws {
127127+ let swiftCBORResult = try measure(iterations: iterations) {
128128+ let encoded = try swiftCBOR.encode(person)
129129+ blackhole(encoded)
130130+ }
131131+ let cborResult = try measure(iterations: iterations) {
132132+ let encoded = try cbor.encode(person)
133133+ blackhole(encoded)
134134+ }
135135+ printComparison(type: name, swiftCBOR: swiftCBORResult, cbor: cborResult)
136136+ }
137137+}
138138+139139+struct CompanyBenchmark: Benchmark {
140140+ let name: String
141141+ let company: Company
142142+ init(company: Company, name: String = "Company") { self.company = company; self.name = name }
143143+ func run(swiftCBOR: CodableCBOREncoder, cbor: CBOREncoder, iterations: Int) throws {
144144+ let swiftCBORResult = try measure(iterations: iterations) {
145145+ let encoded = try swiftCBOR.encode(company)
146146+ blackhole(encoded)
147147+ }
148148+ let cborResult = try measure(iterations: iterations) {
149149+ let encoded = try cbor.encode(company)
150150+ blackhole(encoded)
151151+ }
152152+ printComparison(type: name, swiftCBOR: swiftCBORResult, cbor: cborResult)
153153+ }
154154+}
155155+156156+struct StringBenchmark: Benchmark {
157157+ let name: String
158158+ let string: String
159159+ init(string: String, name: String = "String") { self.string = string; self.name = name }
160160+ func run(swiftCBOR: CodableCBOREncoder, cbor: CBOREncoder, iterations: Int) throws {
161161+ let swiftCBORResult = try measure(iterations: iterations) {
162162+ let encoded = try swiftCBOR.encode(string)
163163+ blackhole(encoded)
164164+ }
165165+ let cborResult = try measure(iterations: iterations) {
166166+ let encoded = try cbor.encode(string)
167167+ blackhole(encoded)
168168+ }
169169+ printComparison(type: name, swiftCBOR: swiftCBORResult, cbor: cborResult)
170170+ }
171171+}
172172+173173+struct DataBenchmark: Benchmark {
174174+ let name: String
175175+ let data: Data
176176+ init(data: Data, name: String = "Data") { self.data = data; self.name = name }
177177+ func run(swiftCBOR: CodableCBOREncoder, cbor: CBOREncoder, iterations: Int) throws {
178178+ let swiftCBORResult = try measure(iterations: iterations) {
179179+ let encoded = try swiftCBOR.encode(data)
180180+ blackhole(encoded)
181181+ }
182182+ let cborResult = try measure(iterations: iterations) {
183183+ let encoded = try cbor.encode(data)
184184+ blackhole(encoded)
185185+ }
186186+ printComparison(type: name, swiftCBOR: swiftCBORResult, cbor: cborResult)
187187+ }
188188+}
189189+190190+struct DictionaryBenchmark: Benchmark {
191191+ let name: String
192192+ let dict: [String: String]
193193+ init(dict: [String: String], name: String = "Dictionary") { self.dict = dict; self.name = name }
194194+ func run(swiftCBOR: CodableCBOREncoder, cbor: CBOREncoder, iterations: Int) throws {
195195+ let swiftCBORResult = try measure(iterations: iterations) {
196196+ let encoded = try swiftCBOR.encode(dict)
197197+ blackhole(encoded)
198198+ }
199199+ let cborResult = try measure(iterations: iterations) {
200200+ let encoded = try cbor.encode(dict)
201201+ blackhole(encoded)
202202+ }
203203+ printComparison(type: name, swiftCBOR: swiftCBORResult, cbor: cborResult)
204204+ }
205205+}
206206+207207+struct ArrayBenchmark: Benchmark {
208208+ let name: String
209209+ let array: [Int]
210210+ init(array: [Int], name: String = "Array") { self.array = array; self.name = name }
211211+ func run(swiftCBOR: CodableCBOREncoder, cbor: CBOREncoder, iterations: Int) throws {
212212+ let swiftCBORResult = try measure(iterations: iterations) {
213213+ let encoded = try swiftCBOR.encode(array)
214214+ blackhole(encoded)
215215+ }
216216+ let cborResult = try measure(iterations: iterations) {
217217+ let encoded = try cbor.encode(array)
218218+ blackhole(encoded)
219219+ }
220220+ printComparison(type: name, swiftCBOR: swiftCBORResult, cbor: cborResult)
221221+ }
222222+}
223223+224224+struct IntBenchmark: Benchmark {
225225+ let name: String
226226+ let intVal: Int
227227+ init(intVal: Int, name: String = "Int") { self.intVal = intVal; self.name = name }
228228+ func run(swiftCBOR: CodableCBOREncoder, cbor: CBOREncoder, iterations: Int) throws {
229229+ let swiftCBORResult = try measure(iterations: iterations) {
230230+ let encoded = try swiftCBOR.encode(intVal)
231231+ blackhole(encoded)
232232+ }
233233+ let cborResult = try measure(iterations: iterations) {
234234+ let encoded = try cbor.encode(intVal)
235235+ blackhole(encoded)
236236+ }
237237+ printComparison(type: name, swiftCBOR: swiftCBORResult, cbor: cborResult)
238238+ }
239239+}
240240+241241+struct BoolBenchmark: Benchmark {
242242+ let name: String
243243+ let boolVal: Bool
244244+ init(boolVal: Bool, name: String = "Bool") { self.boolVal = boolVal; self.name = name }
245245+ func run(swiftCBOR: CodableCBOREncoder, cbor: CBOREncoder, iterations: Int) throws {
246246+ let swiftCBORResult = try measure(iterations: iterations) {
247247+ let encoded = try swiftCBOR.encode(boolVal)
248248+ blackhole(encoded)
249249+ }
250250+ let cborResult = try measure(iterations: iterations) {
251251+ let encoded = try cbor.encode(boolVal)
252252+ blackhole(encoded)
253253+ }
254254+ printComparison(type: name, swiftCBOR: swiftCBORResult, cbor: cborResult)
255255+ }
256256+}
+22
CODE_OF_CONDUCT.md
···11+# Contributor Code of Conduct
22+33+As contributors and maintainers of this project, and in the interest of fostering an open and welcoming community, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities.
44+55+We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, religion, or nationality.
66+77+Examples of unacceptable behavior by participants include:
88+99+* The use of sexualized language or imagery
1010+* Personal attacks
1111+* Trolling or insulting/derogatory comments
1212+* Public or private harassment
1313+* Publishing others' private information, such as physical or electronic addresses, without explicit permission
1414+* Other unethical or unprofessional conduct.
1515+1616+Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. By adopting this Code of Conduct, project maintainers commit themselves to fairly and consistently applying these principles to every aspect of managing this project. Project maintainers who do not follow or enforce the Code of Conduct may be permanently removed from the project team.
1717+1818+This code of conduct applies both within project spaces and in public spaces when an individual is representing the project or its community.
1919+2020+Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue or contacting one or more of the project maintainers.
2121+2222+This Code of Conduct is adapted from the [Contributor Covenant](http://contributor-covenant.org), version 1.2.0, available at [http://contributor-covenant.org/version/1/2/0/](http://contributor-covenant.org/version/1/2/0/)
···11+# SwiftCBOR
22+33+None of that now.
44+55+## Contributing
66+77+By participating in this project you agree to follow the [Contributor Code of Conduct](https://contributor-covenant.org/version/1/4/).
88+99+[The list of contributors is available on GitHub](https://github.com/thecoolwinter/SwiftCBOR/graphs/contributors).
+20
Sources/CBOR/Coding/CodingPath.swift
···11+//
22+// CodingPath.swift
33+// SwiftCBOR
44+//
55+// Created by Khan Winter on 8/17/25.
66+//
77+88+/// Reconstructible coding path.
99+enum CodingPath {
1010+ case root
1111+ indirect case child(key: CodingKey, parent: CodingPath)
1212+1313+ var expanded: [CodingKey] {
1414+ switch self {
1515+ case .root: return []
1616+ case let .child(key: key, parent: parent):
1717+ return parent.expanded + CollectionOfOne(key)
1818+ }
1919+ }
2020+}
+23
Sources/CBOR/Coding/UnkeyedCodingKey.swift
···11+//
22+// UnkeyedCodingKey.swift
33+// SwiftCBOR
44+//
55+// Created by Khan Winter on 8/17/25.
66+//
77+88+public struct UnkeyedCodingKey: CodingKey {
99+ var index: Int
1010+1111+ public var intValue: Int? { return index }
1212+ public var stringValue: String { return String(index) }
1313+1414+ public init(intValue: Int) {
1515+ index = intValue
1616+ }
1717+ public init?(stringValue: String) {
1818+ guard let value = Int(stringValue) else {
1919+ return nil
2020+ }
2121+ index = value
2222+ }
2323+}
+13
Sources/CBOR/CommonTags.swift
···11+//
22+// CommonTags.swift
33+// SwiftCBOR
44+//
55+// Created by Khan Winter on 8/17/25.
66+//
77+88+/// Some common tags for encoding/decoding specifically formatted data such as Dates and UUIDs.
99+enum CommonTags: UInt {
1010+ case stringDate = 0
1111+ case epochDate = 1
1212+ case uuid = 37
1313+}
+13
Sources/CBOR/Constants.swift
···11+//
22+// Constants.swift
33+// SwiftCBOR
44+//
55+// Created by Khan Winter on 8/17/25.
66+//
77+88+@usableFromInline
99+enum Constants {
1010+ // Values below this value can be encoded in the argument field.
1111+ @inline(__always)
1212+ @usableFromInline static let maxArgSize = 24
1313+}
+76
Sources/CBOR/Encoder/CBOREncoder.swift
···11+//
22+// CBOREncoder.swift
33+// SwiftCBOR
44+//
55+// Created by Khan Winter on 8/17/25.
66+//
77+88+#if canImport(FoundationEssentials)
99+import FoundationEssentials
1010+#else
1111+import Foundation
1212+#endif
1313+1414+/// An object that can serialize ``Codable`` objects into the CBOR serialization format.
1515+///
1616+/// To perform serialization, use the ``encode(_:)-6zhmp`` method to convert a Codable object to ``Data``. To
1717+/// configure encoding behavior, either pass customization options in with
1818+/// ``init(forceStringKeys:useStringDates:assumeUInt8IsByteString:)`` or modify ``options``.
1919+public struct CBOREncoder {
2020+ /// Options that determine the behavior of ``CBOREncoder``.
2121+ public var options: EncodingOptions
2222+2323+ /// Create a new CBOR encoder object.
2424+ /// - Parameters:
2525+ /// - forceStringKeys: See ``EncodingOptions/forceStringKeys``.
2626+ /// - useStringDates: See ``EncodingOptions/useStringDates``.
2727+ /// - assumeUInt8IsByteString: See ``EncodingOptions/assumeUInt8IsByteString``.
2828+ public init(forceStringKeys: Bool = false, useStringDates: Bool = false, assumeUInt8IsByteString: Bool = true) {
2929+ options = EncodingOptions(
3030+ forceStringKeys: forceStringKeys,
3131+ useStringDates: useStringDates,
3232+ assumeUInt8IsByteString: assumeUInt8IsByteString
3333+ )
3434+ }
3535+3636+ /// Returns a CBOR-encoded representation of the value you supply.
3737+ /// - Parameter value: The value to encode as CBOR data.
3838+ /// - Returns: The encoded CBOR data.
3939+ public func encode<T: Encodable>(_ value: T) throws -> Data {
4040+ let tempStorage = TopLevelTemporaryEncodingStorage()
4141+4242+ let encodingContext = EncodingContext(options: options)
4343+ let encoder = SingleValueCBOREncodingContainer(parent: tempStorage, context: encodingContext)
4444+ try value.encode(to: encoder)
4545+4646+ let dataSize = tempStorage.value.size
4747+ var data = Data(count: dataSize)
4848+ data.withUnsafeMutableBytes { ptr in
4949+ var slice = ptr[...]
5050+ tempStorage.value.write(to: &slice)
5151+ assert(slice.isEmpty)
5252+ }
5353+ return data
5454+ }
5555+5656+ /// Returns a CBOR-encoded representation of the value you supply.
5757+ /// - Note: This method is identical to ``encode(_:)-6zhmp``. This is a fast path included due to the lack of
5858+ /// ability to specialize Codable containers for specific types, such as byte strings.
5959+ /// - Parameter value: The value to encode as CBOR data.
6060+ /// - Returns: The encoded CBOR data.
6161+ public func encode(_ value: Data) throws -> Data {
6262+ // Fast path for plain data objects. See comments in ``UnkekedCBOREncodingContainer`` for why this can't be done
6363+ // via the real Codable APIs. Hate that we have to 'cheat' like this to get the performance I'd like for
6464+ // byte strings. >:(
6565+6666+ var optimizer = ByteStringOptimizer(value: value)
6767+ let dataSize = optimizer.size
6868+ var data = Data(count: dataSize)
6969+ data.withUnsafeMutableBytes { ptr in
7070+ var slice = ptr[...]
7171+ optimizer.write(to: &slice)
7272+ assert(slice.isEmpty)
7373+ }
7474+ return data
7575+ }
7676+}
···11+//
22+// File.swift
33+// SwiftCBOR
44+//
55+// Created by Khan Winter on 8/17/25.
66+//
77+88+struct UnkeyedCBOREncodingContainer<ParentStorage: TemporaryEncodingStorage>: UnkeyedEncodingContainer {
99+ private let storage: KeyBuffer
1010+ private let context: EncodingContext
1111+1212+ var codingPath: [CodingKey] { context.codingPath }
1313+ var count: Int { storage.count }
1414+1515+ init(parent: ParentStorage, context: EncodingContext) {
1616+ self.storage = KeyBuffer(parent: parent)
1717+ self.context = context
1818+ }
1919+2020+ private func nextContext() -> EncodingContext {
2121+ context.appending(UnkeyedCodingKey(intValue: count))
2222+ }
2323+2424+ func encode<T: Encodable>(_ value: T) throws {
2525+ try value.encode(
2626+ to: SingleValueCBOREncodingContainer(parent: storage.forAppending(), context: nextContext())
2727+ )
2828+ }
2929+3030+ func encode(_ value: UInt8) throws {
3131+ storage.data.append(value)
3232+ if !context.options.assumeUInt8IsByteString {
3333+ try value.encode(
3434+ to: SingleValueCBOREncodingContainer(parent: storage.forAppending(), context: nextContext())
3535+ )
3636+ }
3737+ }
3838+3939+ mutating func encodeNil() throws {
4040+ storage.forAppending().register(NilOptimizer())
4141+ }
4242+4343+ mutating func nestedContainer<NestedKey: CodingKey>(
4444+ keyedBy keyType: NestedKey.Type
4545+ ) -> KeyedEncodingContainer<NestedKey> {
4646+ KeyedEncodingContainer(KeyedCBOREncodingContainer(parent: storage.forAppending(), context: nextContext()))
4747+ }
4848+4949+ mutating func nestedUnkeyedContainer() -> any UnkeyedEncodingContainer {
5050+ UnkeyedCBOREncodingContainer<KeyBuffer.Indexed>(
5151+ parent: storage.forAppending(),
5252+ context: nextContext()
5353+ )
5454+ }
5555+5656+ mutating func superEncoder() -> any Encoder {
5757+ fatalError("Unimplemented")
5858+ }
5959+}
6060+6161+extension UnkeyedCBOREncodingContainer {
6262+ private final class KeyBuffer {
6363+ var count: Int { items.count }
6464+6565+ let parent: ParentStorage
6666+ var items: [EncodingOptimizer] = []
6767+ var data: [UInt8] = []
6868+6969+ init(parent: ParentStorage) {
7070+ self.parent = parent
7171+ self.items = []
7272+ }
7373+7474+ func forAppending() -> Indexed {
7575+ items.append(EmptyOptimizer())
7676+ return Indexed(parent: Unmanaged.passUnretained(self), index: items.count - 1)
7777+ }
7878+7979+ struct Indexed: TemporaryEncodingStorage {
8080+ let parent: Unmanaged<KeyBuffer>
8181+ let index: Int
8282+8383+ func register(_ optimizer: EncodingOptimizer) {
8484+ parent.takeUnretainedValue().items[index] = optimizer
8585+ }
8686+ }
8787+8888+ deinit {
8989+ // Swift doesn't give us a good way to detect a 'byte string'. So, we record both UInt8 values and
9090+ // some optimizers. At this point, we can check if we're encoding either a collection of multiple
9191+ // types (items.count > data.count), or a pure data collection (data.count == items.count).
9292+ // This is terrible in terms of memory use, but lets us encode byte strings using the most optimal
9393+ // encoding method.
9494+ // CBOR also mandates that an empty collection is by default an array, so we check if this is empty.
9595+ // Frankly, this blows and I wish Swift's Codable API was even a smidgen less fucked.
9696+9797+ if items.count <= data.count && !data.isEmpty {
9898+ parent.register(ByteStringOptimizer(value: data))
9999+ } else {
100100+ parent.register(UnkeyedOptimizer(value: items))
101101+ }
102102+ }
103103+ }
104104+105105+}
+33
Sources/CBOR/Encoder/EncodingContext.swift
···11+//
22+// EncodingContext.swift
33+// SwiftCBOR
44+//
55+// Created by Khan Winter on 8/17/25.
66+//
77+88+/// Encoding context, shared between all internal encoders, containers, etc. during encoding process.
99+struct EncodingContext {
1010+ fileprivate class Shared {
1111+ let options: EncodingOptions
1212+1313+ init(options: EncodingOptions) {
1414+ self.options = options
1515+ }
1616+ }
1717+1818+ fileprivate let shared: Shared
1919+ var path: CodingPath = .root
2020+2121+ var options: EncodingOptions { shared.options }
2222+ var codingPath: [CodingKey] { path.expanded }
2323+2424+ init(options: EncodingOptions) {
2525+ shared = Shared(options: options)
2626+ }
2727+2828+ func appending<Key: CodingKey>(_ key: Key) -> EncodingContext {
2929+ var temp = self
3030+ temp.path = .child(key: key, parent: path)
3131+ return temp
3232+ }
3333+}
+53
Sources/CBOR/Encoder/EncodingOptimizer.swift
···11+//
22+// EncodingOptimizer.swift
33+// SwiftCBOR
44+//
55+// Created by Khan Winter on 8/17/25.
66+//
77+88+@usableFromInline
99+protocol EncodingOptimizer {
1010+ var type: MajorType { get }
1111+ var argument: UInt8 { get }
1212+ var headerSize: Int { get }
1313+ var contentSize: Int { get }
1414+1515+ mutating func writeHeader(to data: inout Slice<UnsafeMutableRawBufferPointer>)
1616+ mutating func writePayload(to data: inout Slice<UnsafeMutableRawBufferPointer>)
1717+}
1818+1919+extension EncodingOptimizer {
2020+ @inline(__always)
2121+ @inlinable var headerSize: Int { 0 }
2222+2323+ var size: Int { 1 + headerSize + contentSize }
2424+2525+ mutating func write(to data: inout Slice<UnsafeMutableRawBufferPointer>) {
2626+ assert(data.count >= size)
2727+2828+ // Write the type & argument
2929+ assert(argument & 0b11100000 == 0)
3030+ assert(type.bits & 0b00011111 == 0)
3131+ assert(!data.isEmpty)
3232+3333+ data[data.startIndex] = type.bits | argument
3434+ data.removeFirst()
3535+3636+ writeHeader(to: &data)
3737+ writePayload(to: &data)
3838+ }
3939+4040+ @inlinable
4141+ func writeHeader(to data: inout Slice<UnsafeMutableRawBufferPointer>) {
4242+ // No-op by default
4343+ }
4444+}
4545+4646+struct EmptyOptimizer: EncodingOptimizer {
4747+ var type: MajorType { .uint }
4848+ var argument: UInt8 { 0 }
4949+ var contentSize: Int { 0 }
5050+5151+ func writeHeader(to data: inout Slice<UnsafeMutableRawBufferPointer>) { }
5252+ func writePayload(to data: inout Slice<UnsafeMutableRawBufferPointer>) { }
5353+}
+31
Sources/CBOR/Encoder/EncodingOptions.swift
···11+//
22+// EncodingOptions.swift
33+// SwiftCBOR
44+//
55+// Created by Khan Winter on 8/17/25.
66+//
77+88+/// Options that determine the behavior of ``CBOREncoder``.
99+public struct EncodingOptions {
1010+ /// Force encoded maps to use string keys even when integer keys are available.
1111+ public let forceStringKeys: Bool
1212+1313+ /// Encode dates as strings instead of epoch timestamps (Doubles)
1414+ public let useStringDates: Bool
1515+1616+ /// Codable can't tell us if we're encoding a Data or [UInt8] object. By default this library assumes that if it's
1717+ /// encoding an unkeyed container or UInt8 objects it's a byte string. Toggle this to false to disable this.
1818+ /// **This will slow down all Data encoding operations dramatically**.
1919+ public let assumeUInt8IsByteString: Bool
2020+2121+ /// Initialize new encoding options.
2222+ /// - Parameters:
2323+ /// - forceStringKeys: Force encoded maps to use string keys even when integer keys are available.
2424+ /// - useStringDates: Encode dates as strings instead of epoch timestamps (Doubles)
2525+ /// - assumeUInt8IsByteString: See ``assumeUInt8IsByteString``.
2626+ public init(forceStringKeys: Bool, useStringDates: Bool, assumeUInt8IsByteString: Bool) {
2727+ self.forceStringKeys = forceStringKeys
2828+ self.useStringDates = useStringDates
2929+ self.assumeUInt8IsByteString = assumeUInt8IsByteString
3030+ }
3131+}