Skip to main content

Crate zerocbor

Crate zerocbor 

Source
Expand description

§zerocbor

A zero-copy, zero-dependency, no_std-compatible, extremely fast CBOR (RFC 8949) serializer for Rust.

Crates.io version docs.rs docs

§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 TypeCBOR Major Type
bool7, simple value true / false
u8, u16, u32, u64, usize0, unsigned integer
i8, i16, i32, i64, isize0 or 1, unsigned or negative integer
f327, float 16 or float 32, whichever is narrower
f647, whichever of the three float widths is narrowest
char3, one-character text string
str, String3, 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)

CrateSerializeDeserialize
cbor4ii3.19 μs16.46 μs
ciborium18.40 μs123.92 μs
minicbor11.36 μs10.29 μs
zerocbor1.54 μs5.03 μs

§Serialize/Deserialize Struct (4 fields, map format, with a nested struct, an Option and a Vec)

CrateSerializeDeserialize
cbor4ii34.52 μs180.49 μs
ciborium90.69 μs491.88 μs
minicbor64.42 μs150.32 μs
zerocbor24.74 μs122.83 μs

§Serialize/Deserialize Struct (8 integer fields, one of every width)

CrateSerializeDeserialize
ciborium87.40 μs357.90 μs
minicbor58.71 μs41.40 μs
zerocbor8.54 μs27.15 μs

§Serialize/Deserialize Array (a 2-field struct plus a 1000-element Vec<u64>)

CrateSerializeDeserialize
cbor4ii2,289.96 μs6,544.41 μs
ciborium6,175.27 μs15,511.02 μs
minicbor11,050.64 μs5,625.04 μs
zerocbor820.06 μs2,376.10 μs

§Serialize/Deserialize Struct (borrowed &str and byte string)

CrateSerializeDeserialize
cbor4ii11.74 μs27.33 μs
ciborium23.84 μsN/A
minicborN/AN/A
zerocbor8.71 μs17.80 μs

§Deserialize Array (1000 records) from a std::io::Read stream

CrateDeserialize
cbor4ii124.05 μs
ciborium96.32 μs
zerocbor32.62 μs

§Decode/Encode a dynamically typed document into and out of Value

CrateDecodeEncode
ciborium2.83 μs496.22 μs
zerocbor1.32 μs365.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§

ArrayIter
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.
TrustedSizeHint
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§

FromCbor
A data structure that can be deserialized from CBOR format.
FromCborOwned
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 T from a CBOR-encoded byte slice.
read_cbor
Deserializes a T from an std::io::Read.
to_cbor
Serializes a T into buf, returning the bytes written.
to_cbor_vec
Serializes a T into a Vec<u8>.
write_cbor
Serializes a T into an std::io::Write.

Type Aliases§

Result
The result of a CBOR encoding or decoding operation.