···1+#!/usr/bin/env python3
2+"""
3+Parse Swift Benchmark markdown (produced by `swift package benchmark ... --format markdown`) and produce compact summary
4+markdown tables for Decoding and Encoding using the Time (total CPU) p50 values.
5+6+Usage:
7+ - Read from stdin:
8+ swift package benchmark baseline compare swiftcbor --format markdown --no-progress | python3 bench_compare.py
9+ - Or read from file:
10+ python3 bench_compare.py benchmark.md
11+12+The script prints two markdown tables (Decoding and Encoding) to stdout.
13+"""
14+15+import sys
16+import re
17+from pathlib import Path
18+19+20+def parse_markdown(text):
21+ lines = text.splitlines()
22+ results = {"Decoding": {}, "Encoding": {}}
23+ mode = None
24+ i = 0
25+ while i < len(lines):
26+ line = lines[i]
27+ if line.strip().startswith('## Decoding'):
28+ mode = 'Decoding'
29+ i += 1
30+ continue
31+ if line.strip().startswith('## Encoding'):
32+ mode = 'Encoding'
33+ i += 1
34+ continue
35+36+ m = re.match(r"###\s+(.+?)\s+metrics", line)
37+ if m and mode:
38+ bench = m.group(1).strip()
39+ # look ahead for Time (total CPU) table
40+ j = i + 1
41+ while j < len(lines) and not lines[j].strip().startswith('###') and not lines[j].strip().startswith('## '):
42+ lh = lines[j]
43+ # find the section header line containing "Time (total CPU)"
44+ m2 = re.search(r"Time\s*\(total CPU\)\s*(?:\(([^)]+)\))?", lh)
45+ if m2:
46+ unit = m2.group(1) if m2.group(1) else ''
47+ # find the header row that contains p0/p25/p50 etc.
48+ k = j + 1
49+ while k < len(lines) and lines[k].strip() == '':
50+ k += 1
51+ header_line_index = None
52+ p50_idx = None
53+ for t in range(k, min(k + 60, len(lines))):
54+ if 'p50' in lines[t]:
55+ cols = [c.strip() for c in lines[t].split('|')][1:-1]
56+ # locate p50 column
57+ try:
58+ p50_idx = next(idx for idx, c in enumerate(cols) if c.startswith('p50'))
59+ except StopIteration:
60+ p50_idx = None
61+ header_line_index = t
62+ break
63+ swift_val = None
64+ curr_val = None
65+ if header_line_index is not None and p50_idx is not None:
66+ # parse following rows to find swiftcbor and Current_run
67+ for t in range(header_line_index + 1, header_line_index + 60):
68+ if t >= len(lines):
69+ break
70+ row = lines[t]
71+ if not row.strip().startswith('|'):
72+ continue
73+ cols = [c.strip() for c in row.split('|')][1:-1]
74+ if not cols:
75+ continue
76+ name = cols[0]
77+ # defensive check
78+ if p50_idx < len(cols):
79+ if 'swiftcbor' in name:
80+ swift_val = cols[p50_idx]
81+ if 'Current_run' in name:
82+ curr_val = cols[p50_idx]
83+ if swift_val and curr_val:
84+ break
85+ results[mode][bench] = (swift_val, curr_val, unit)
86+ break
87+ j += 1
88+ i += 1
89+ return results
90+91+92+def clean_num(s):
93+ if s is None:
94+ return None
95+ s = s.strip().replace(',', '')
96+ # find first numeric token
97+ m = re.search(r"([0-9]+(?:\.[0-9]+)?)", s)
98+ if not m:
99+ return None
100+ try:
101+ return float(m.group(1))
102+ except:
103+ return None
104+105+106+def fmt(n):
107+ if n is None:
108+ return ''
109+ if n >= 1000:
110+ return f"{int(round(n)):,}"
111+ if n == int(n):
112+ return str(int(n))
113+ return f"{n:.0f}"
114+115+116+def render_table_section(title, rows, preferred_order=None):
117+ print(f"### {title} (cpu time)\n")
118+ print("| Benchmark | SwiftCBOR (p50) | CBOR (p50) | % Improvement |")
119+ print("|---|---:|---:|---:|")
120+ keys = []
121+ if preferred_order:
122+ for k in preferred_order:
123+ if k in rows:
124+ keys.append(k)
125+ # append remaining in alphabetical order
126+ for k in sorted(rows.keys()):
127+ if k not in keys:
128+ keys.append(k)
129+ for b in keys:
130+ s_p, c_p, unit = rows.get(b, (None, None, ''))
131+ sval = clean_num(s_p)
132+ cval = clean_num(c_p)
133+ s_str = (fmt(sval) + (' ' + unit if unit else '')) if sval is not None else (s_p or '')
134+ c_str = (fmt(cval) + (' ' + unit if unit else '')) if cval is not None else (c_p or '')
135+ perc = ''
136+ if sval is not None and cval is not None and sval != 0:
137+ pct = round((sval - cval) / sval * 100)
138+ perc = f"**{pct}%**"
139+ print(f"| {b} | {s_str} | {c_str} | {perc} |")
140+ print("\n")
141+142+143+def main(argv):
144+ parser = __import__('argparse').ArgumentParser(description='Parse Swift benchmark markdown and print compact p50 tables')
145+ parser.add_argument('file', nargs='?', default='-', help='Path to markdown file, or - for stdin (default)')
146+ args = parser.parse_args(argv)
147+ if args.file == '-':
148+ text = sys.stdin.read()
149+ else:
150+ p = Path(args.file)
151+ text = p.read_text()
152+153+ results = parse_markdown(text)
154+155+ # preferred orders to match your example (best-effort)
156+ dec_order = ["Array","Complex Object","Date","Dictionary","Double","Float","Indeterminate String","Int","Int Small","Simple Object","String","String Small"]
157+ enc_order = ["Array","Array Small","Bool","Complex Codable Object","Data","Data Small","Dictionary","Dictionary Small","Int","Int Small","Simple Codable Object","String","String Small"]
158+159+ render_table_section('Decoding', results.get('Decoding', {}), preferred_order=dec_order)
160+ render_table_section('Encoding', results.get('Encoding', {}), preferred_order=enc_order)
161+162+163+if __name__ == '__main__':
164+ main(sys.argv[1:])
+13
Benchmarks/bench_compare.sh
···0000000000000
···1+#!/usr/bin/env bash
2+# Run the Swift benchmark compare command and pipe its markdown output into the Python parser.
3+# Usage: run this from the repository root.
4+5+set -euo pipefail
6+7+# If you want to pass additional args to the swift command, set SWIFT_ARGS environment variable.
8+SWIFT_ARGS=${SWIFT_ARGS:-"baseline compare swiftcbor --format markdown --no-progress"}
9+PY_SCRIPT="$(dirname "$0")/bench_compare.py"
10+11+swift -version
12+# Run the swift command and pipe
13+swift package benchmark $SWIFT_ARGS | python3 "$PY_SCRIPT"
···5First build the package in release mode with the fuzzer and address checkers on:
67```bash
8-swift build -c release --sanitize fuzzer,address
9```
1011Then run it:
···5First build the package in release mode with the fuzzer and address checkers on:
67```bash
8+swift build -c release --sanitize fuzzer
9```
1011Then run it:
···25- Customizable encoding options, such as date and key formatting.
26- Customizable decoding, with the ability to reject non-deterministic CBOR data.
27- Encoder creates deterministic CBOR data by default.
00028- A scan pass allows for decoding only the keys you need from an encoded object.
29- Supports decoding half precision floats (Float16) as a regular Float.
30- Runs on Linux, Android, and Windows using the swift-foundation project when available.
···32- Supports tagged items (will expand and add ability to inject your own tags in the future):
33 - Dates
34 - UUIDs
035- Flexible date parsing (tags `0` or `1` with support for any numeric value representation).
000003637## Usage
380039This library utilizes Swift's `Codable` API for all (de)serialization operations. It intentionally doesn't support streaming CBOR blobs. After [installing](#installation) the package using Swift Package Manager, use the `CBOREncoder` and `CBORDecoder` types to encode to and decode from CBOR blobs, respectively.
4041```swift
···48let decoder = CBORDecoder()
49let intValue = try decoder.decode(Int.self, from: Data([0])) // Result: 0
50// Result: ["AB": 1, "A": 2]
51-let mapValue = try decoder.decode([String: Int].self.self, from: Data([162, 98, 65, 66, 1, 97, 65, 2]))
52```
5354The encoder and decoders can be customized via the en/decoder initializers or via a `En/DecodingOptions` struct.
···70let decoder = CBORDecoder(options: options)
71```
7273-[Documentation]() is hosted on the Swift Package Index.
0000000000000000000000000000000000000000000000000007475## Installation
76
···25- Customizable encoding options, such as date and key formatting.
26- Customizable decoding, with the ability to reject non-deterministic CBOR data.
27- Encoder creates deterministic CBOR data by default.
28+ - Dictionaries are automatically sorted using a min heap for speed.
29+ - Duplicate map keys are removed.
30+ - Never creates non-deterministic collections (Strings, Byte Strings, Arrays, or Maps).
31- A scan pass allows for decoding only the keys you need from an encoded object.
32- Supports decoding half precision floats (Float16) as a regular Float.
33- Runs on Linux, Android, and Windows using the swift-foundation project when available.
···35- Supports tagged items (will expand and add ability to inject your own tags in the future):
36 - Dates
37 - UUIDs
38+ - [CIDs](https://github.com/multiformats/cid) (tag 42)
39- Flexible date parsing (tags `0` or `1` with support for any numeric value representation).
40+- Decoding multiple top-level objects using `decodeMultiple(_:from:)`.
41+- *NEW* IPLD compatible DAG-CBOR encoder for content addressable data.
42+- *NEW* Flexible date decoding for untagged date items encoded as strings, floating point values, or integers.
43+44+> 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.
4546## Usage
4748+### Standard CBOR
49+50This library utilizes Swift's `Codable` API for all (de)serialization operations. It intentionally doesn't support streaming CBOR blobs. After [installing](#installation) the package using Swift Package Manager, use the `CBOREncoder` and `CBORDecoder` types to encode to and decode from CBOR blobs, respectively.
5152```swift
···59let decoder = CBORDecoder()
60let intValue = try decoder.decode(Int.self, from: Data([0])) // Result: 0
61// Result: ["AB": 1, "A": 2]
62+let mapValue = try decoder.decode([String: Int].self, from: Data([162, 98, 65, 66, 1, 97, 65, 2]))
63```
6465The encoder and decoders can be customized via the en/decoder initializers or via a `En/DecodingOptions` struct.
···81let decoder = CBORDecoder(options: options)
82```
8384+### DAG-CBOR
85+86+This library also offers the ability to encode and decode DAG-CBOR data. DAG-CBOR is a superset of the CBOR spec, though very similar. This library provides a `DAGCBOREncoder` type that is automatically configured to produce compatible data. The flags it sets are also available through the `EncodingOptions` struct, but using the specialized type will ensure safety.
87+88+```swift
89+// !! SEE NOTE !!
90+let dagEncoder = DAGCBOREncoder(dateEncodingStrategy: .double)
91+```
92+93+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.
94+```swift
95+struct CID: CIDType {
96+ let data: Data
97+98+ init(data: Data) {
99+ self.data = data
100+ }
101+102+ init(from decoder: any Decoder) throws {
103+ let container = try decoder.singleValueContainer()
104+ var data = try container.decode(Data.self)
105+ data.removeFirst()
106+ self.data = data
107+ }
108+109+ func encode(to encoder: any Encoder) throws {
110+ var container = encoder.singleValueContainer()
111+ try container.encode(Data([0x0]) + data)
112+ }
113+}
114+```
115+> [!WARNING]
116+>
117+> 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.
118+119+Now, any time the encoder finds a `CID` type it will encode it using the correct tag.
120+```swift
121+let cid = CID(bytes: [0,1,2,3,4,5,6,7,8])
122+let data = try DAGCBOREncoder().encode(cid)
123+124+print(data.hexString())
125+// Output:
126+// D8 2A # tag(42)
127+// 4A # bytes(10)
128+// 00000102030405060708 # "\u0000\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007\b"
129+```
130+> [!NOTE]
131+> 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.
132+133+## Documentation
134+135+[Documentation](https://swiftpackageindex.com/thecoolwinter/CBOR/1.1.0/documentation/cbor) is hosted on the Swift Package Index.
136137## Installation
138
+1
Sources/CBOR/CommonTags.swift
···10 case stringDate = 0
11 case epochDate = 1
12 case uuid = 37
013}
···10 case stringDate = 0
11 case epochDate = 1
12 case uuid = 37
13+ case cid = 42
14}
+27
Sources/CBOR/CustomTags/TaggedCBORItem.swift
···000000000000000000000000000
···1+//
2+// TaggedCBORItem.swift
3+// CBOR
4+//
5+// Created by Khan Winter on 10/15/25.
6+//
7+8+/// Protocol for tagged item encoding. Conform types to this to have them encoded in a tag container.
9+///
10+/// Types that conform to this protocol must be `Codable` but are required to implement the method and initializer
11+/// required by this protocol to ensure extra containers are not created between the tag container and the real data.
12+/// This library implements this for Foundations ``Foundation/UUID`` type, Dates are handled as a special case.
13+///
14+/// For an example conformance, see ``CIDType``.
15+public protocol TaggedCBORItem: Codable {
16+ static var tag: UInt { get }
17+18+ init<Container: SingleValueDecodingContainer>(decodeTaggedDataUsing container: Container) throws
19+ func encodeTaggedData<Container: SingleValueEncodingContainer>(using container: inout Container) throws
20+}
21+22+/// Helper for getting the static member from an instance of the protocol. Otherwise sometimes has a crash at runtime
23+/// when trying to do `T as! TaggedCBORItem.self` even if `T` is a `TaggedCBORItem`. Probably due to some fuckery in
24+/// Foundation's stuff with UUID in particular. This fixes that though.
25+extension TaggedCBORItem {
26+ var __staticTagLookup: UInt { Self.tag }
27+}
+33
Sources/CBOR/CustomTags/UUID+TaggedCBORItem.swift
···000000000000000000000000000000000
···1+//
2+// UUID+TaggedCBORItem.swift
3+// CBOR
4+//
5+// Created by Khan Winter on 10/15/25.
6+//
7+8+#if canImport(FoundationEssentials)
9+import FoundationEssentials
10+#else
11+import Foundation
12+#endif
13+14+extension UUID: TaggedCBORItem {
15+ public static var tag: UInt { CommonTags.uuid.rawValue }
16+17+ public init<Container: SingleValueDecodingContainer>(decodeTaggedDataUsing container: Container) throws {
18+ let data = try container.decode(Data.self)
19+ guard data.count == 16 else { // UUID size
20+ throw DecodingError.dataCorruptedError(
21+ in: container,
22+ debugDescription: "Data decoded for UUID tag is not 16 bytes long."
23+ )
24+ }
25+ self = data.withUnsafeBytes { ptr in ptr.load(as: UUID.self) }
26+ }
27+28+ public func encodeTaggedData<Container: SingleValueEncodingContainer>(using encoder: inout Container) throws {
29+ try withUnsafeBytes(of: self) { ptr in
30+ try encoder.encode(Data(ptr))
31+ }
32+ }
33+}
···68 }
69 }
7071+ /// Decodes multiple instances of the given type from CBOR binary data.
72+ ///
73+ /// Some BLOBs are made up of multiple CBOR-encoded datas concatenated without valid CBOR dividers (eg in an array
74+ /// container). This method decodes that kind of data. It will attempt to decode an instance of the given type,
75+ /// once done, if there's more data, it will continue to attempt to decode more instances.
76+ ///
77+ /// - Parameters:
78+ /// - type: The decodable type to deserialize.
79+ /// - data: The CBOR data to decode from.
80+ /// - Returns: An instance of the decoded type.
81+ /// - Throws: A ``DecodingError`` with context and a debug description for a failed deserialization operation.
82+ public func decodeMultiple<T: Decodable>(_ type: T.Type, from data: Data) throws -> [T] {
83+ do {
84+ return try data.withUnsafeBytes {
85+ let data = $0[...]
86+ let reader = DataReader(data: data)
87+ let scanner = CBORScanner(data: reader, options: options)
88+ let results = try scanner.scan()
89+90+ guard !results.isEmpty else {
91+ throw ScanError.unexpectedEndOfData
92+ }
93+94+ let context = DecodingContext(options: options, results: results)
95+ var nextRegion: DataRegion? = results.load(at: 0)
96+97+ var accumulator: [T] = []
98+99+ while let region = nextRegion {
100+ let value = try SingleValueCBORDecodingContainer(context: context, data: region).decode(T.self)
101+ accumulator.append(value)
102+ let nextMapIndex = results.siblingIndex(region.mapOffset)
103+ if nextMapIndex < results.count {
104+ nextRegion = results.load(at: results.siblingIndex(region.mapOffset))
105+ } else {
106+ nextRegion = nil
107+ }
108+ }
109+110+ return accumulator
111+ }
112+ } catch {
113+ if let error = error as? ScanError {
114+ try throwScanError(error)
115+ } else {
116+ throw error
117+ }
118+ }
119+ }
120+121 private func throwScanError(_ error: ScanError) throws -> Never {
122 switch error {
123 case .unexpectedEndOfData:
+1-1
Sources/CBOR/Decoder/Containers/DataRegion.swift
···6667 func isNil() -> Bool {
68 // This shouldn't modify the data stack. We just peek here.
69- type == .simple && argument == 22
70 }
7172 /// Reads the next variable-sized integer off the data stack.
···6667 func isNil() -> Bool {
68 // This shouldn't modify the data stack. We just peek here.
69+ type == .simple && (argument == 22 || argument == 23)
70 }
7172 /// Reads the next variable-sized integer off the data stack.
···3738 /// Check the next type on the data stack.
39 /// - Parameter type: The type to check for.
40- func checkType(_ types: MajorType...) throws {
41 guard types.contains(data.type) else {
42- throw DecodingError.typeMismatch(Bool.self, context.error("Unexpected type found: \(data.type)."))
0000000000043 }
44 }
45}
···3738 /// Check the next type on the data stack.
39 /// - Parameter type: The type to check for.
40+ func checkType<T>(_ types: MajorType..., forType: T.Type) throws {
41 guard types.contains(data.type) else {
42+ throw DecodingError.typeMismatch(T.self, context.error("Unexpected type found: \(data.type)."))
43+ }
44+ }
45+46+ /// Check an available tag value by reading either the argument or the next int value.
47+ func checkTag<T>(_ tag: UInt, forType: T.Type) throws {
48+ let tagValue = try data.readInt(as: UInt.self)
49+ guard tagValue == tag else {
50+ throw DecodingError.typeMismatch(
51+ T.self,
52+ context.error("Unexpected tag found: \(tagValue), expected \(tag) for \(T.self)")
53+ )
54 }
55 }
56}
···11import Foundation
12#endif
1300000014@usableFromInline
15enum ScanError: Error {
16 case unexpectedEndOfData
···215 // MARK: - Scan Tagged
216217 private mutating func scanTagged(raw: UInt8) throws {
218+ // Scan the tag number (passing the raw value here makes it record a Tag rather than an Int)
219+ try scanInt(raw: raw)
000000000220221 guard let nextRaw = reader.peek(), let nextTag = MajorType(rawValue: nextRaw) else {
222 throw ScanError.unexpectedEndOfData
000000000223 }
224225 try scanType(type: nextTag, raw: nextRaw)
···1+//
2+// CIDType.swift
3+// CBOR
4+//
5+// Created by Khan Winter on 10/14/25.
6+//
7+8+/// A type that represents a [CID](https://github.com/multiformats/cid). When encoded using ``CBOREncoder`` or
9+/// ``DAGCBOREncoder``, uses the tag `42`. This is the only allowed tagged data type when using ``DAGCBOREncoder``.
10+///
11+/// To use, conform your internal CID type to ``CIDType``. Do not conform standard types like `String` or `Data` to
12+/// ``CIDType``, or the encoder will attempt to encode all of those data as tagged items.
13+/// ```swift
14+/// struct CID: CIDType {
15+/// let data: Data
16+///
17+/// init(data: Data) {
18+/// self.data = data
19+/// }
20+///
21+/// init<Container: SingleValueDecodingContainer>(decodeTaggedDataUsing container: Container) throws {
22+/// var data = try container.decode(Data.self)
23+/// data.removeFirst()
24+/// self.data = data
25+/// }
26+///
27+/// func encodeTaggedData<Container: SingleValueEncodingContainer>(using container: inout Container) throws {
28+/// try container.encode(Data([0x0]) + data)
29+/// }
30+/// }
31+/// ```
32+/// Note that you **need** to prefix your data with the `NULL` character once encoded. This library will
33+/// not handle that for you. It is invalid DAG-CBOR encoding to not include the prefixed byte.
34+public protocol CIDType: TaggedCBORItem {}
35+36+extension CIDType {
37+ /// The tag for all CID types is `42`.
38+ public static var tag: UInt { 42 }
39+}
···1+//
2+// DAGCBOREncoder.swift
3+// CBOR
4+//
5+// Created by Khan Winter on 10/14/25.
6+//
7+8+import Foundation
9+10+/// Serializes ``Encodable`` objects using the DAG-CBOR serialization format.
11+///
12+/// To perform serialization, use the ``encode`` method to convert a Codable object to ``Data``. To
13+/// configure encoding behavior, either pass customization options in with
14+/// ``init(dateEncodingStrategy:)`` or modify ``dateEncodingStrategy``.
15+///
16+/// This type has no performance differences from ``CBOREncoder``. Instead, it automatically configures the private
17+/// encoding options flags to generate always-valid DAG-CBOR encoded data.
18+///
19+/// - Warning: DAG-CBOR requires that implementations ***do not*** use tags for values such as dates. Because of this,
20+/// depending on the ``DAGCBOREncoder/dateEncodingStrategy``, date values will
21+/// be encoded without tag information as a different type.
22+public struct DAGCBOREncoder {
23+ /// Options that determine the behavior of ``DAGCBOREncoder``.
24+ public var dateEncodingStrategy: EncodingOptions.DateStrategy
25+26+ /// Create a new CBOR encoder.
27+ /// - Parameter dateEncodingStrategy: See ``EncodingOptions/dateEncodingStrategy``.
28+ public init(dateEncodingStrategy: EncodingOptions.DateStrategy = .double) {
29+ self.dateEncodingStrategy = dateEncodingStrategy
30+ }
31+32+ /// Returns a DAG-CBOR-encoded representation of the value you supply.
33+ /// - Parameter value: The value to encode as CBOR data.
34+ /// - Returns: The encoded CBOR data.
35+ public func encode<T: Encodable>(_ value: T) throws -> Data {
36+ // Required overrides for valid DAG-CBOR encoding
37+ let options = EncodingOptions.dag(dateEncodingStrategy: dateEncodingStrategy)
38+39+ let tempStorage = TopLevelTemporaryEncodingStorage()
40+41+ let encodingContext = EncodingContext(options: options)
42+ let encoder = SingleValueCBOREncodingContainer(parent: tempStorage, context: encodingContext)
43+ try encoder.encode(value)
44+45+ let dataSize = tempStorage.value.size
46+ var data = Data(count: dataSize)
47+ data.withUnsafeMutableBytes { ptr in
48+ var slice = ptr[...]
49+ tempStorage.value.write(to: &slice)
50+ assert(slice.isEmpty)
51+ }
52+ return data
53+ }
54+}
···12 var headerSize: Int { get }
13 var contentSize: Int { get }
1415- mutating func writeHeader(to data: inout Slice<UnsafeMutableRawBufferPointer>)
16- mutating func writePayload(to data: inout Slice<UnsafeMutableRawBufferPointer>)
17}
1819extension EncodingOptimizer {
···2223 var size: Int { 1 + headerSize + contentSize }
2425- mutating func write(to data: inout Slice<UnsafeMutableRawBufferPointer>) {
26 assert(data.count >= size)
2728 // Write the type & argument
···12 var headerSize: Int { get }
13 var contentSize: Int { get }
1415+ func writeHeader(to data: inout Slice<UnsafeMutableRawBufferPointer>)
16+ func writePayload(to data: inout Slice<UnsafeMutableRawBufferPointer>)
17}
1819extension EncodingOptimizer {
···2223 var size: Int { 1 + headerSize + contentSize }
2425+ func write(to data: inout Slice<UnsafeMutableRawBufferPointer>) {
26 assert(data.count >= size)
2728 // Write the type & argument
+42-1
Sources/CBOR/Encoder/EncodingOptions.swift
···25 /// Determine how to encode dates.
26 public let dateEncodingStrategy: DateStrategy
2700000000000000000028 /// Initialize new encoding options.
29 /// - Parameters:
30 /// - forceStringKeys: Force encoded maps to use string keys even when integer keys are available.
31 /// - useStringDates: See ``dateEncodingStrategy`` and ``DateStrategy``.
32- public init(forceStringKeys: Bool, dateEncodingStrategy: DateStrategy) {
000000000033 self.forceStringKeys = forceStringKeys
34 self.dateEncodingStrategy = dateEncodingStrategy
000000000000035 }
36}
···25 /// Determine how to encode dates.
26 public let dateEncodingStrategy: DateStrategy
2728+ /// Different strategies for encoding tagged items.
29+ public enum TagStrategy {
30+ /// Encode all tagged items. Default.
31+ case accept
32+ /// DAG mode option. Reject all tagged items (UUIDs). If possible, encoder uses an alternative encoding
33+ /// method. Otherwise, throws an encoding error.
34+ case dagMode
35+ }
36+37+ /// Determine how to encode tagged items.
38+ public let taggedItemsStrategy: TagStrategy
39+40+ /// Force the encoder to encode all floating point numbers as 64-bit double values.
41+ public let forceDoubleLengthEncoding: Bool
42+43+ /// DAG mode option. Reject all Infinity and NaN values for floating-point numbers (`Double`, `Float`).
44+ public let rejectInfAndNan: Bool
45+46 /// Initialize new encoding options.
47 /// - Parameters:
48 /// - forceStringKeys: Force encoded maps to use string keys even when integer keys are available.
49 /// - useStringDates: See ``dateEncodingStrategy`` and ``DateStrategy``.
50+ /// - taggedItemsStrategy: See ``taggedItemsStrategy`` and ``TagStrategy``.
51+ /// - forceDoubleLengthEncoding: Encode all floating point numbers as 64-bit double values.
52+ /// - rejectInfAndNan: DAG mode option. Reject all Infinity and NaN values for floating-point numbers (`Double`,
53+ /// `Float`).
54+ public init(
55+ forceStringKeys: Bool,
56+ dateEncodingStrategy: DateStrategy,
57+ taggedItemsStrategy: TagStrategy,
58+ forceDoubleLengthEncoding: Bool,
59+ rejectInfAndNan: Bool
60+ ) {
61 self.forceStringKeys = forceStringKeys
62 self.dateEncodingStrategy = dateEncodingStrategy
63+ self.taggedItemsStrategy = taggedItemsStrategy
64+ self.forceDoubleLengthEncoding = forceDoubleLengthEncoding
65+ self.rejectInfAndNan = rejectInfAndNan
66+ }
67+68+ static func dag(dateEncodingStrategy: DateStrategy) -> EncodingOptions {
69+ EncodingOptions(
70+ forceStringKeys: true,
71+ dateEncodingStrategy: dateEncodingStrategy,
72+ taggedItemsStrategy: .dagMode,
73+ forceDoubleLengthEncoding: true,
74+ rejectInfAndNan: true
75+ )
76 }
77}