14 releases (7 breaking)
0.7.0 | Aug 31, 2024 |
---|---|
0.5.2 | Aug 21, 2024 |
0.4.2 | Jul 7, 2024 |
#317 in Encoding
26 downloads per month
92KB
1.5K
SLoC
bzipper
bzipper is a binary (de)serialiser for the Rust language.
In contrast to Serde/Bincode, the primary 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 use streams – or 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 two traits for custom types (although this is only supported with enumerations and structures). Here, each field is chained according to declaration order:
use bzipper::{Buffer, Deserialise, Serialise};
#[derive(Debug, Deserialise, PartialEq, Serialise)]
struct IoRegister {
addr: u32,
value: u16,
}
let mut buf = Buffer::new();
buf.write(IoRegister { addr: 0x04000000, value: 0x0402 }).unwrap();
assert_eq!(buf.len(), 0x6);
assert_eq!(buf, [0x04, 0x00, 0x00, 0x00, 0x04, 0x02]);
assert_eq!(buf.read().unwrap(), IoRegister { addr: 0x04000000, value: 0x0402 });
Serialisation
To serialise an object implementing Serialise
, simply allocate a buffer for the serialisation and wrap it in an s-stream (serialisation stream) with the Sstream
type.
use bzipper::{Serialise, Sstream};
let mut buf = [Default::default(); char::MAX_SERIALISED_SIZE];
let mut stream = Sstream::new(&mut buf);
'Ж'.serialise(&mut stream).unwrap();
assert_eq!(stream, [0x00, 0x00, 0x04, 0x16]);
The maximum size of any given serialisation is specified by the MAX_SERIALISED_SIZE
constant.
We can also use streams to chain multiple elements together:
use bzipper::{Serialise, Sstream};
let mut buf = [Default::default(); char::MAX_SERIALISED_SIZE * 0x5];
let mut stream = Sstream::new(&mut buf);
// Note: For serialising multiple characters, the
// `FixedString` type is usually preferred.
'ل'.serialise(&mut stream).unwrap();
'ا'.serialise(&mut stream).unwrap();
'م'.serialise(&mut stream).unwrap();
'د'.serialise(&mut stream).unwrap();
'ا'.serialise(&mut stream).unwrap();
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 a similar syntax to serialisation.
D-streams (deserialisation streams) use the Dstream
type and are constructed in a manner similar to s-streams.
To deserialise a buffer, simply call the deserialise
method with the strema:
use bzipper::{Deserialise, Dstream};
let data = [0x45, 0x54];
let stream = Dstream::new(&data);
assert_eq!(u16::deserialise(&stream).unwrap(), 0x4554);
And just like s-streams, d-streams can also be used to handle chaining:
use bzipper::{Deserialise, Dstream};
let data = [0x45, 0x54];
let stream = Dstream::new(&data);
assert_eq!(u8::deserialise(&stream).unwrap(), 0x45);
assert_eq!(u8::deserialise(&stream).unwrap(), 0x54);
// The data can also be deserialised as a tuple (up
// to twelve elements).
let stream = Dstream::new(&data);
assert_eq!(<(u8, u8)>::deserialise(&stream).unwrap(), (0x45, 0x54));
Dependencies
~240–680KB
~16K SLoC