zerialize 0.0.1

Zero-copy serialization
Documentation
  • Coverage
  • 65.85%
    27 out of 41 items documented2 out of 9 items with examples
  • Size
  • Source code size: 236.3 kB This is the summed size of all the files inside the crates.io package for this release.
  • Documentation size: 1.0 MB This is the summed size of all files generated by rustdoc for all configured targets
  • Ø build duration
  • this release: 7s Average build duration of successful builds.
  • all releases: 7s Average build duration of successful builds in releases after 2024-10-23.
  • Links
  • cberner/zerialize
    1 0 0
  • crates.io
  • Dependencies
  • Versions
  • Owners
  • cberner

zerialize

CI Crates.io Documentation License dependency status

Zerialize provides zero-cost deserialization and a natural interface that allows code to be written against the traits, structs, and enums in the codebase, without referencing any generated types.

Supported types

Primitives

  • bool
  • char
  • &str
  • &[u8]
  • u8, u16, u32, u64, u128
  • i8, i16, i32, i64, i128
  • f32, f64

Container types

  • Option<T>, over any other supported type
  • List<T>, over any other supported type

An optional field is absent on the wire when it is None, which is exactly what a reader sees of a field the writer did not have, so a field added as an optional one can be read out of messages written before it existed.

Containers may contain any primitive, value type, view trait, or view enum. An Option may contain a list; Option<Option<T>>, a list of Option<T>, and a list of lists are not supported.

Value types

Value types are copied, and may be structs or enums, without generic parameters beside the one lifetime their borrowed fields point into. They must implement Copy. A value enum's variants may carry fields of their own, each declaring its slot as a struct's fields do. For example:

#[derive(Zerializable, Copy, Clone)]
struct Date {
    #[n(0)]
    day: u8,
    #[n(1)]
    month: u8,
    #[n(2)]
    year: u16,
}
#[derive(Zerializable, Copy, Clone)]
enum Protocol {
    #[variant(0)]
    TCP {
        #[n(0)]
        port: u32,
    },
    #[variant(1)]
    UDP {
        #[n(0)]
        port: u32
    },
}

A value may hold &str and &[u8], which point into the buffer as the same fields of a view schema trait do. One that does declares the lifetime it points into, and is named with it wherever it is named, as a view enum that borrows is.

#[derive(Zerializable, Copy, Clone)]
struct Stamp<'a> {
    #[n(0)]
    code: &'a str,
    #[n(1)]
    grams: u32,
}

Only a value that borrows declares a lifetime, so a value that declares none holds nothing pointing into the buffer it was read from, and outlives it.

Value types may contain primitives, other value types, and an Option of either. They may not contain lists, view traits, or view enums.

A value type and a view enum are written alike where a field names one, Stamp<'_>, so which of the two a name stands for is which of them declared it.

View schema traits

View traits provide zero copy access to their data. A method that returns an impl Trait must be guarded with

where
    Self: Sized

which is what keeps dyn Person, the name of the schema, usable as one. A nested view trait, a view enum carrying one, and a list are each written as an impl Trait; a primitive or a value is not, and needs no guard.

For example:

#[zerializable]
trait Person {
    #[n(0)]
    fn name(&self) -> &str;

    #[n(1)]
    fn children(&self) -> impl List<Item = impl Person + '_> + '_
    where
        Self: Sized;
}

View schema traits may contain any primitive, container type, value type, view trait, or view enum.

View enums

View enums provide zero copy access to view traits behind a generic parameter. Their other fields are copied, except &str and &[u8], which point into the buffer as a view trait's do.

#[zerializable]
enum Mammal<'a, P: Person> {
    #[variant(0)]
    Cat {
        #[n(0)]
        name: &'a str,
    },
    #[variant(1)]
    Human(#[n(1)] P),
}

A variant's fields are its own, so two variants may declare the same slots, and either may declare one the other does not.

An enum with a borrowed field declares the lifetime it points into, and is named with it wherever it is named: Mammal<'_, dyn Person> is the schema, and decoding it gives Mammal<'buf, PersonView<'buf>>. An enum that borrows nothing declares no lifetime, and is named without one.

A list is a generic parameter as a nested view trait is, bound by what it holds: an element named outright as List<Item = u32>, or, where the list holds messages, the trait declaring them as List<Item: Person>.

#[zerializable]
enum Recipients<'a, P: List<Item: Person>, N: List<Item = &'a str>> {
    #[variant(0)]
    Everyone,
    #[variant(1)]
    People(#[n(0)] P),
    #[variant(2)]
    Addresses(#[n(0)] N),
}

A list is named with ListView wherever the enum is named: Recipients<'_, ListView<'_, dyn Person>, ListView<'_, str>> is the schema, and decoding it gives Recipients<'buf, ListView<'buf, dyn Person>, ListView<'buf, str>>.

A nested view enum is written over the enum's own parameters: an enum with a P: Person parameter carries the one above as Pet(#[n(0)] Mammal<'a, P>).

View enums may contain any primitive, container type, value type, view trait, or view enum, with one exception: a list a view enum carries may not hold a view enum. A view schema trait's list may.

Benchmarks

just bench measures size and speed against rkyv and flatbuffers over the same records. The results, and what is still left on the table, are in benchmarks/README.md.

License

Licensed under either of

at your option.

Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.