Expand description
§Series of compact encoding schemes for building small and fast parsers and serializers
Binary compatible with the original JavaScript compact-encoding library.
§Usage
The simplest way to encoded and decode a some data is using the to_encoded_bytes
and
map_decode
macros:
use compact_encoding::{map_decode, to_encoded_bytes};
let number = 41_u32;
let word = "hi";
let encoded_buffer = to_encoded_bytes!(number, word);
let ((number_dec, word_dec), remaining_buffer) = map_decode!(&encoded_buffer, [u32, String]);
assert!(remaining_buffer.is_empty());
assert_eq!(number_dec, number);
assert_eq!(word_dec, word);
§Manual encoding and decoding
When more fine-grained control of encoding and decoding is needed you manually do each step of
encoding and decoding like in the following example, where we want to use a fixed width
encoding for number
(instead of the default variable width encoding). It shows how to
manually calculate the needed buffer size, create the buffer, encode data, and decode it.
use compact_encoding::{CompactEncoding, FixedWidthEncoding, FixedWidthU32};
let number = 41_u32;
let word = "hi";
// Use `encoded_size` to figure out how big a buffer should be
let size = number.as_fixed_width().encoded_size()? + word.encoded_size()?;
// Create a buffer with the calculated size
let mut buffer = vec![0; size];
assert_eq!(buffer.len(), 4 + 1 + 2);
// Then actually encode the values
let mut remaining_buffer = number.as_fixed_width().encode(&mut buffer)?;
remaining_buffer = word.encode(remaining_buffer)?;
assert!(remaining_buffer.is_empty());
assert_eq!(buffer.to_vec(), vec![41_u8, 0, 0, 0, 2_u8, b'h', b'i']);
// `buffer` now contains all the encoded data, and we can decode from it
let (number_dec, remaining_buffer) = FixedWidthU32::decode(&buffer)?;
let (word_dec, remaining_buffer) = String::decode(remaining_buffer)?;
assert!(remaining_buffer.is_empty());
assert_eq!(number_dec, number);
assert_eq!(word_dec, word);
§Implementing CompactEncoding for custom types
Here we demonstrate how to implement CompactEncoding
for a custom struct.
use compact_encoding::{
map_decode, map_encode, sum_encoded_size, to_encoded_bytes, CompactEncoding, EncodingError,
};
#[derive(Debug, PartialEq)]
struct MyStruct {
some_flag: bool,
values: Option<Vec<[u8; 32]>>,
other: String,
stuff: u64,
}
impl CompactEncoding for MyStruct {
fn encoded_size(&self) -> Result<usize, EncodingError> {
Ok(1 /* flags */ + {
/* handle option values */
if let Some(values) = &self.values {
values.encoded_size()?
} else {
0
}
} + sum_encoded_size!(&self.other, &self.stuff))
}
fn encode<'a>(&self, buffer: &'a mut [u8]) -> Result<&'a mut [u8], EncodingError> {
let mut flags: u8 = 0;
if self.some_flag {
flags |= 1;
}
if self.values.is_some() {
flags |= 2;
}
let mut rest = flags.encode(buffer)?;
if let Some(values) = &self.values {
rest = values.encode(rest)?;
}
Ok(map_encode!(rest, self.other, self.stuff))
}
fn decode(buffer: &[u8]) -> Result<(Self, &[u8]), EncodingError> {
let (flags, rest) = u8::decode(buffer)?;
let some_flag: bool = flags & 1 != 0;
let (values, rest) = if flags & 2 != 0 {
let (vec, rest) = <Vec<[u8; 32]>>::decode(rest)?;
(Some(vec), rest)
} else {
(None, rest)
};
let ((other, stuff), rest) = map_decode!(rest, [String, u64]);
Ok((
Self {
some_flag,
values,
other,
stuff,
},
rest,
))
}
}
// Test values
let foo = MyStruct {
some_flag: false,
values: None,
other: "hi".to_string(),
stuff: 42,
};
let bar = MyStruct {
some_flag: true,
values: Some(vec![[1; 32], [2; 32]]),
other: "yo".to_string(),
stuff: 0,
};
// Encode `foo` and `bar` to a buffer
let buffer = to_encoded_bytes!(&foo, &bar);
// With the above use of a flags byte, the empty value encodes to only one byte
assert_eq!(
buffer.len(),
// flags + string + u64
(1 + 3 + 1) +
// "" + (values.len().encoded_size() + (values.len() * <[u8;32]>::encoded_size()) + ""
(1 + (1 + (2 * 32)) + 3 + 1)
);
// And decode directly to your own struct
let (foo_dec, rest) = MyStruct::decode(&buffer)?;
let (bar_dec, rest) = MyStruct::decode(&rest)?;
// Ensure all bytes were used
assert!(rest.is_empty());
assert_eq!(foo_dec, foo);
assert_eq!(bar_dec, bar);
Macros§
- create_
buffer - Given a list of
CompactEncoding
things, create a zeroed buffer of the correct size for encoding. Note this is macro is useful when your arguments have differing types. - map_
decode - Decode a buffer to the list of types provided, returning the remaining buffer.
It takes as arguments:
(&buffer, [type1, type2, type3, ...])
And returns:((decoded_type1, decoded_type2, ...), remaining_buffer)
- map_
encode - Given a buffer and a list of
CompactEncoding
things, encode the arguments to the buffer. Note this is macro is useful when your arguments have differing types. - sum_
encoded_ size - Given a list of
CompactEncoding
things, sum the result of callingCompactEncoding::encoded_size
on all of them. Note this is macro is useful when your arguments have differing types. - to_
encoded_ bytes - Given a list of
CompactEncoding
things, encode the arguments to the buffer. Note this is macro is useful when your arguments have differing types.
Structs§
- Encoding
Error - Encoding/decoding error.
- Fixed
Width Uint - A fixed width encodable unsigned integer
Enums§
- Encoding
Error Kind - Specific type EncodingError
Constants§
- U16_
SIGNIFIER - indicates a variable width unsigned integer fits in u16
- U32_
SIGNIFIER - indicates a variable width unsigned integer fits in u32
- U64_
SIGNIFIER - indicates a variable width unsigned integer fits in u64
Traits§
- Boxed
Slice Encodable - Define this trait for
T
to getimpl Box<[T]> for CompactEncoding
- Compact
Encoding - A trait for building small and fast parsers and serializers.
- Fixed
Width Encoding - Implents functionality needed to encode unisegned integrer in a fixed width way with
CompactEncoding
- VecEncodable
- Implement this for type
T
to haveCompactEncoding
implemented forVec<T>
Functions§
- as_
array - Get a slice as an array of size
N
. Errors whenslice.len() != N
. - as_
array_ mut - Get a slice as a mutable array of size
N
. Errors whenslice.len() != N
. - bytes_
fixed_ from_ vec - Helper to convert a vec to an array, and fail with an encoding error when needed
- decode_
bytes_ fixed - Decode a fixed sized array from a buffer. Return the array and the remainder of the buffer.
Errors when
buffer.len() < N
; - decode_
usize - Decode a
usize
frombuffer
and return the remaining bytes. This will fail, when we are decoding ausize
on a usize = u32 machine for data that was originally encoded on ausize = u64
machine whenever the value is overu32::MAX
. - encode_
bytes_ fixed - Encoded a fixed sized array to a buffer, return the remainder of the buffer.
Errors when
value.len() > buffer.len()
; - encode_
var_ u64 - Write
uint
to the start ofbuffer
and return the remaining part ofbuffer
. - encoded_
size_ usize - The number of bytes required to encode this number. Note this is always variable width.
- encoded_
size_ var_ u64 - The number of bytes required to encode this number. We only need this for u64 because all other uints can be converted to usize reliably.
- get_
slices_ checked - Split a slice in two at
mid
. Returns encoding error whenmid
is out of bounds. - get_
slices_ mut_ checked - Split a mutable slice into two mutable slices around
mid
. Returns encoding error whenmid
is out of bounds. - take_
array - split the first
N
bytes ofbuffer
off and return them - take_
array_ mut - split the first
N
bytes ofbuffer
off and return them - write_
array - Write
source
tobuffer
and return the remainder ofbuffer
. Errors whenN < buffer.len()
- write_
slice - write
source
tobuffer
and return remaining buffer
Type Aliases§
- Fixed
Width U32 - A wrapper around
u32
to let us encoded/decode to/from a fixed width - Fixed
Width U64 - A wrapper around
u64
to let us encoded/decode to/from a fixed width