Expand description
§zerocbor
A zero-copy, zero-dependency, no_std-compatible, extremely fast CBOR (RFC 8949) serializer for Rust.
§Overview
zerocbor is a fast CBOR serializer for Rust. It runs about 1.5–4.0 times faster than other crates and is implemented without depending on any libraries, including std.
zerocbor is based on the architecture of zerompk, with the serialization format switched to CBOR. zerompk is a fast MessagePack serializer characterized by high performance and a small code size compared with conventional serializers. See the zerompk README for details.
§Quick Start
use zerocbor::{FromCbor, ToCbor};
#[derive(FromCbor, ToCbor)]
pub struct Person {
pub name: String,
pub age: u32,
}
fn main() {
let person = Person {
name: "Alice".to_string(),
age: 18,
};
let cbor: Vec<u8> = zerocbor::to_cbor_vec(&person)
.unwrap();
let person: Person = zerocbor::from_cbor(&cbor)
.unwrap();
}§Format
The mapping between Rust types and CBOR types in zerocbor is as follows.
| Rust Type | CBOR Major Type |
|---|---|
bool | 7, simple value true / false |
u8, u16, u32, u64, usize | 0, unsigned integer |
i8, i16, i32, i64, isize | 0 or 1, unsigned or negative integer |
f32 | 7, float 16 or float 32, whichever is narrower |
f64 | 7, whichever of the three float widths is narrowest |
char | 3, one-character text string |
str, String | 3, text string |
&'a [u8] (decode) | 2, byte string |
&[u8], Vec<u8> (encode) | 4, array of integers — see the note below |
&[T], Vec<T>, VecDeque<T>, LinkedList<T>, BTreeSet<T>, BinaryHeap<T>, HashSet<T> | 4, array |
BTreeMap<K, V>, HashMap<K, V> | 5, map |
() | 7, simple value null |
Option<T> | 7, null (None) or T (Some(T)) |
Result<T, E> | 4, [true, T] (Ok) or [false, E] (Err) |
newtype struct W(T) | as T, optionally under a tag |
(T0, T1), (T0, T1, T2), … | 4, array |
Box<T>, Rc<T>, Arc<T> | as T |
PhantomData<T> | 7, simple value null |
| struct (default, array representation) | 4, array of the fields in declaration order |
struct (with #[cbor(map)]) | 5, map keyed by field name |
| enum, fieldless variants (default) | 0, the variant index |
enum, fieldless variants (with #[cbor(map)]) | 3, the variant name |
| enum with data (default) | 4, [index, fields...] |
enum with data (with #[cbor(map)]) | 5, {"Name": value} where value is the fields |
anything with #[cbor(tag = N)] | 6, the tag, then the value it applies to |
§derive
Enable the derive feature flag to implement FromCbor/ToCbor using derive macros.
use zerocbor::{FromCbor, ToCbor};
#[derive(FromCbor, ToCbor)]
pub struct Person {
pub name: String,
pub age: u32,
}You can also customize the serialization format using the #[cbor] attribute.
§array/map
You can choose array or map as the serialization format for structs and enums. For performance reasons, the default is array.
use zerocbor::{FromCbor, ToCbor};
#[derive(FromCbor, ToCbor)]
#[cbor(array)] // default
pub struct PersonArray {
pub name: String,
pub age: u32,
}
#[derive(FromCbor, ToCbor)]
#[cbor(map)]
pub struct PersonMap {
pub name: String,
pub age: u32,
}§key
You can override the index/key used for fields and enum variants. Integers can be used for arrays, and strings for maps. When the format is array and there are gaps in the indices, null is inserted automatically.
#[derive(FromCbor, ToCbor)]
#[cbor(map)]
pub struct Point {
#[cbor(key = "the-x")]
x: i32,
y: i32,
}[!NOTE] To improve versioning resilience, it is recommended to set keys explicitly whenever possible.
§ignore
Set ignore on fields that should be ignored during serialization/deserialization. When deserializing a struct that contains an ignore field, the field’s type must implement Default.
#[derive(FromCbor, ToCbor)]
pub struct Person {
pub name: String,
pub age: u32,
#[cbor(ignore)]
pub meta: Metadata,
}§c_enum
Adding #[cbor(c_enum)] to a C-style enum allows it to be serialized as an integer. The value is the discriminant of each variant.
#[derive(FromCbor, ToCbor)]
#[cbor(c_enum)]
#[repr(u8)]
pub enum Status {
Ok = 0,
NotFound = 4,
InternalServerError = 5,
}§as_bytes
You can specify whether a u8 array is serialized as binary data (major type 2). The default is true. This option can be applied to fields of type &[u8], Vec<u8>, or Cow<[u8]>.
use std::borrow::Cow;
use zerocbor::{FromCbor, ToCbor};
#[derive(Debug, PartialEq, ToCbor, FromCbor)]
struct Blob<'a> {
#[cbor(as_bytes = true)]
data: Cow<'a, [u8]>,
}
let value = Blob {
data: Cow::Borrowed(&[0x01, 0x02][..]),
};
let encoded = zerocbor::to_cbor_vec(&value).unwrap();
assert_eq!(encoded, vec![0x81, 0x42, 0x01, 0x02]);
assert_eq!(zerocbor::from_cbor::<Blob<'_>>(&encoded).unwrap(), value);§default
If a key is missing during deserialization, the corresponding field is replaced with a default value. A missing key is filled with Default::default(), or, if default = "path" is specified, with the result of the named function.
This is supported only with #[cbor(map)]. (Because arrays have no field names, missing values cannot be detected safely.)
fn default_age() -> u32 {
18
}
use zerocbor::{FromCbor, ToCbor};
#[derive(Debug, PartialEq, ToCbor, FromCbor)]
#[cbor(map)]
pub struct Person {
pub name: String,
#[cbor(default)]
pub nickname: Option<String>,
#[cbor(default = "default_age")]
pub age: u32,
}default applies only to missing keys. Unknown keys cause an error unless allow_unknown_fields is set.
§allow_unknown_fields
When an unknown key is encountered during deserialization, this changes the behavior to skip it instead of returning an error. This is effective only with #[cbor(map)].
use zerocbor::{FromCbor, ToCbor};
#[derive(Debug, PartialEq, ToCbor, FromCbor)]
#[cbor(map, allow_unknown_fields)]
pub struct Person {
pub name: String,
pub age: u32,
}To ensure full forward and backward compatibility, combine default and allow_unknown_fields.
#[derive(FromCbor, ToCbor)]
#[cbor(map, allow_unknown_fields)]
pub struct Person {
pub name: String,
#[cbor(default)]
pub age: u32,
}[!NOTE] These attributes are opt-in. By default, zerocbor requires an exact schema match.
§Benchmarks
Measured on macOS 26.4.1 (arm64) with
rustc 1.100.0-nightly.
§Serialize/Deserialize Struct (2 fields, array format)
| Crate | Serialize | Deserialize |
|---|---|---|
cbor4ii | 3.19 μs | 16.46 μs |
ciborium | 18.40 μs | 123.92 μs |
minicbor | 11.36 μs | 10.29 μs |
zerocbor | 1.54 μs | 5.03 μs |
§Serialize/Deserialize Struct (4 fields, map format, with a nested struct, an Option and a Vec)
| Crate | Serialize | Deserialize |
|---|---|---|
cbor4ii | 34.52 μs | 180.49 μs |
ciborium | 90.69 μs | 491.88 μs |
minicbor | 64.42 μs | 150.32 μs |
zerocbor | 24.74 μs | 122.83 μs |
§Serialize/Deserialize Struct (8 integer fields, one of every width)
| Crate | Serialize | Deserialize |
|---|---|---|
ciborium | 87.40 μs | 357.90 μs |
minicbor | 58.71 μs | 41.40 μs |
zerocbor | 8.54 μs | 27.15 μs |
§Serialize/Deserialize Array (a 2-field struct plus a 1000-element Vec<u64>)
| Crate | Serialize | Deserialize |
|---|---|---|
cbor4ii | 2,289.96 μs | 6,544.41 μs |
ciborium | 6,175.27 μs | 15,511.02 μs |
minicbor | 11,050.64 μs | 5,625.04 μs |
zerocbor | 820.06 μs | 2,376.10 μs |
§Serialize/Deserialize Struct (borrowed &str and byte string)
| Crate | Serialize | Deserialize |
|---|---|---|
cbor4ii | 11.74 μs | 27.33 μs |
ciborium | 23.84 μs | N/A |
minicbor | N/A | N/A |
zerocbor | 8.71 μs | 17.80 μs |
§Deserialize Array (1000 records) from a std::io::Read stream
| Crate | Deserialize |
|---|---|
cbor4ii | 124.05 μs |
ciborium | 96.32 μs |
zerocbor | 32.62 μs |
§Decode/Encode a dynamically typed document into and out of Value
| Crate | Decode | Encode |
|---|---|---|
ciborium | 2.83 μs | 496.22 μs |
zerocbor | 1.32 μs | 365.69 μs |
§License
This library is released under the MIT License.
Modules§
- tags
- The tag numbers RFC 8949 Section 3.4 and RFC 8746 assign meaning to.
Structs§
- Array
Iter - A trait for reading values from a CBOR-encoded input. A cursor over the elements of an array whose header has been read, so that both length forms are read by one loop. The reader is passed back in per step rather than borrowed, so it is never held across a call that needs it mutably.
- MapIter
- A cursor over the key-value pairs of a map whose header has been read. As
with
ArrayIter, the reader is passed in per step so both length forms are read by one loop. - Trusted
Size Hint - An encoded-size hint used for preallocation, never for memory safety.
Enums§
- Error
- Represents an error that can occur during CBOR encoding or decoding.
- Len
- The declared length of a container, which an indefinite-length one lacks.
- Value
- A dynamically-typed CBOR value.
Constants§
- MAX_
DEPTH - The maximum number of nested decoding scopes a decode accepts.
Traits§
- From
Cbor - A data structure that can be deserialized from CBOR format.
- From
Cbor Owned - A trait for types that can be deserialized from CBOR format without borrowing.
- Read
- A trait for reading values from a CBOR-encoded input.
- ToCbor
- A data structure that can be serialized into CBOR format.
- Write
- A trait for writing CBOR-encoded data.
Functions§
- check_
optional_ tag - Checks the tag a value arrived under against the one its type declares.
- from_
cbor - Deserializes a
Tfrom a CBOR-encoded byte slice. - read_
cbor - Deserializes a
Tfrom anstd::io::Read. - to_cbor
- Serializes a
Tintobuf, returning the bytes written. - to_
cbor_ vec - Serializes a
Tinto aVec<u8>. - write_
cbor - Serializes a
Tinto anstd::io::Write.
Type Aliases§
- Result
- The result of a CBOR encoding or decoding operation.