CBOR

[CBOR](https://cbor.io/) is a flexible data format that emphasizes extremely small code size, small message size, and extendability. This library provides a Swift API for encoding and decoding Swift types using the CBOR serialization format. The motivation for this library over existing implementations is twofold: performance, and reliability. Existing community implementations do not make correct use of Swift features like `Optional` and memory safety. This library aims to be safe while still outperforming other implementations. To that end, this library has been extensively [fuzz tested](./Fuzz.md) to ensure your application never crashes due to malicious input. At the same time, this library boasts up to a *74% faster* encoding and up to *89% faster* decoding than existing implementations. See [benchmarks](#benchmarks) for more details. ## Features - `Codable` API for familiar Swift project integration. - Customizable encoding options, such as date and key formatting. - Customizable decoding, with the ability to reject non-deterministic CBOR data. - Encoder creates deterministic CBOR data by default. - Dictionaries are automatically sorted using a min heap for speed. - Duplicate map keys are removed. - Never creates non-deterministic collections (Strings, Byte Strings, Arrays, or Maps). - A scan pass allows for decoding only the keys you need from an encoded object. - Supports decoding half precision floats (Float16) as a regular Float. - Runs on Linux, Android, and Windows using the swift-foundation project when available. - Fuzz tested for reliability against crashes. - Supports tagged items (will expand and add ability to inject your own tags in the future): - Dates - UUIDs - Flexible date parsing (tags `0` or `1` with support for any numeric value representation). > 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. ## Usage This 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. ```swift // Encode any `Encodable` value. let encoder = CBOREncoder() try encoder.encode(0) // Result: 0x00 try encoder.encode([404: "Not Found"]) // Result: 0xA1190194694E6F7420466F756E64 // Decode any `Decodable` type. let decoder = CBORDecoder() let intValue = try decoder.decode(Int.self, from: Data([0])) // Result: 0 // Result: ["AB": 1, "A": 2] let mapValue = try decoder.decode([String: Int].self.self, from: Data([162, 98, 65, 66, 1, 97, 65, 2])) ``` The encoder and decoders can be customized via the en/decoder initializers or via a `En/DecodingOptions` struct. ```swift // Customize encoder behavior. let encoder = CBOREncoder( forceStringKeys: Bool = false, dateEncodingStrategy: EncodingOptions.DateStrategy = .double ) // or via the options struct let options = EncodingOptions(/* ... */) let encoder = CBOREncoder(options: options) // Customize the decoder behavior, such as enforcing deterministic CBOR. let decoder = CBORDecoder(rejectIndeterminateLengths: Bool = false) // or via the options struct let options = DecodingOptions(/* ... */) let decoder = CBORDecoder(options: options) ``` [Documentation](https://swiftpackageindex.com/thecoolwinter/CBOR/1.0.0/documentation/cbor) is hosted on the Swift Package Index. ## Installation You can use the Swift Package Manager to download and import the library into your project: ```swift dependencies: [ .package(url: "https://github.com/thecoolwinter/CBOR.git", from: "1.0.0") ] ``` Then under `targets`: ```swift targets: [ .target( // ... dependencies: [ .product(name: "CBOR", package: "CBOR") ] ) ] ``` ## Benchmarks These benchmarks range from simple to complex encoding and decoding operations compared to the [SwiftCBOR](https://github.com/valpackett/SwiftCBOR) library. [Benchmarks source](./Benchmarks). Benchmarks are run on 10,000 samples and the p50 values are compared here. ### Decoding (cpu time) | Benchmark | SwiftCBOR (ns, p50) | CBOR (ns, p50) | % Improvement | |-----------|----------------|-----------|------------| | Array | 23 | 7 | **70%** | | Complex Object | 703 μs | 75 μs | **89%** | | Date | 5,251 | 1,083 | **79%** | | Dictionary | 17 | 5 | **71%** | | Double | 5,295 | 1,001 | **81%** | | Float | 5,295 | 1,000 | **81%** | | Indeterminate String | 6,251 | 1,417 | **77%** | | Int | 5,211 | 1,125 | **78%** | | Int Small | 5,211 | 1,083 | **79%** | | Simple Object | 36 | 8 | **78%** | | String | 5,459 | 1,292 | **76%** | | String Small | 5,291 | 1,126 | **79%** | ### Encoding (cpu time) | Benchmark | SwiftCBOR (ns, p50) | CBOR (ns, p50) | % Improvement | |-----------|----------------|-----------|------------| | Array | 669 μs | 471 μs | **30%** | | Array Small | 7,043 | 2,917 | **59%** | | Bool | 3,169 | 1,125 | **64%** | | Complex Codable Object | 124 μs | 92 μs | **26%** | | Data | 5,335 | 1,250 | **77%** | | Data Small | 3,959 | 1000 | **75%** | | Dictionary | 11 | 5 | **55%** | | Dictionary Small | 7,459 | 2,959 | **60%** | | Int | 4,001 | 1,292 | **68%** | | Int Small | 3,833 | 1,208 | **68%** | | Simple Codable Object | 18 | 9 | **50%** | | String | 5,583 | 1,291 | **77%** | | String Small | 4,041 | 1,125 | **72%** | ## Contributing By participating in this project you agree to follow the [Contributor Code of Conduct](https://contributor-covenant.org/version/1/4/). Pull requests and issues are welcome! ## Sponsor This project has been developed in my spare time. To keep me motivated to continue to maintain it into the future, consider [supporting my work](https://github.com/sponsors/thecoolwinter)!