bzipper

bzipper is a binary (de)serialiser for the Rust language.


Contrary to Serde/Bincode, the goal of bzipper is to serialise with a known size constraint. Therefore, this crate may be more suited for networking or other cases where a fixed-sized buffer is needed.


Keep in mind that this project is still work-in-progress.


This crate is compatible with no_std.

Data model

Most primitive types serialise losslessly, with the exception being usize and isize. These serialise as u32 and i32, respectively, for portability reasons.


Unsized types, such as str and slices, are not supported. Instead, arrays should be used. For strings, the FixedString type is also provided.

Usage

This crate revolves around the Serialise and Deserialise traits, both of which are commonly used in conjunction with streams (more specifically, s-streams and d-streams).


Many core types come implemented with bzipper, including primitives as well as some standard library types such as Option and Result.


It is recommended in most cases to just derive these traits for custom types (enumerations and structures only). Here, each field is chained in declaration order:


use bzipper::{Deserialise, Serialise};

#[derive(Debug, Deserialise, PartialEq, Serialise)]
struct IoRegister {
    addr:  u32,
    value: u16,
}

let mut buf: [u8; IoRegister::SERIALISED_SIZE] = Default::default();
IoRegister { addr: 0x04000000, value: 0x0402 }.serialise(&mut buf).unwrap();

assert_eq!(buf, [0x04, 0x00, 0x00, 0x00, 0x04, 0x02]);

assert_eq!(IoRegister::deserialise(&buf).unwrap(), IoRegister { addr: 0x04000000, value: 0x0402 });

Serialisation

To serialise an object implementing Serialise, simply allocate a buffer for the serialisation. The required size of any given serialisation is specified by the SERIALISED_SIZE constant:


use bzipper::Serialise;

let mut buf: [u8; char::SERIALISED_SIZE] = Default::default();
'Ж'.serialise(&mut buf).unwrap();

assert_eq!(buf, [0x00, 0x00, 0x04, 0x16]);


The only special requirement of the serialise method is that the provided byte slice has an element count of exactly SERIALISED_SIZE.


We can also use streams to chain multiple elements together:


use bzipper::Serialise;

let mut buf: [u8; char::SERIALISED_SIZE * 5] = Default::default();
let mut stream = bzipper::Sstream::new(&mut buf);

stream.append(&'ل');
stream.append(&'ا');
stream.append(&'م');
stream.append(&'د');
stream.append(&'ا');

assert_eq!(buf, [0x00, 0x00, 0x06, 0x44, 0x00, 0x00, 0x06, 0x27, 0x00, 0x00, 0x06, 0x45, 0x00, 0x00, 0x06, 0x2F, 0x00, 0x00, 0x06, 0x27]);


When serialising primitives, the resulting byte stream is in big endian (a.k.a. network endian). It is recommended for implementors to adhere to this convention as well.

Deserialisation

Deserialisation works with an almost identical syntax to serialisation.


To deserialise a buffer, simply call the deserialise method:


use bzipper::Deserialise;

let data = [0x45, 0x54];
assert_eq!(::deserialise(&data).unwrap(), 0x4554);


Just like with serialisations, the Dstream can be used to deserialise chained elements:


use bzipper::Deserialise;

let data = [0x45, 0x54];
let stream = bzipper::Dstream::new(&data);

assert_eq!(stream.take::().unwrap(), 0x45);
assert_eq!(stream.take::().unwrap(), 0x54);

Docs

Documentation is written in-source. See Docs.rs for a rendered instance.