pub trait Encode<Format = ()> {
// Required method
fn encode<W: Write>(&self, w: &mut Writer<W>) -> Result<(), EncodeError>;
}Expand description
Types which can be encoded to storekey format.
§Format
The storekey serialization format tries to preserve ordering, before and after serialization.
This means that in general when a < b then enc(a) < enc(b) when encoded with the storekey
format.
This need to preserve ordering means that storekey cannot store lengths of types which have a
size only known at runtime like Vec and other collections.
In order to be able to decode a type after encoding storekey needs some way to mark the end of
a runtime sized type. We cannot store the length itself as a prefix, as is common with
serialization formats, as that would not preserve ordering. Instead storekey uses a zero byte
to mark the end of a slice. This also means that the encoding format needs to escape zero bytes
if they occur when within the runtime size type. So for example if you have a Vec<u8> with
values [0,1] then the first 0 will be escaped with a 1, of course this means the the
second value also needs to be escaped resulting in the final encoding of 1,0,1,1,0 for the
given Vec.
§Implementing Encode.
Most of the time, when using storekey, you can rely on the derive macros to correctly implement storekey format for you. Encode is also implemented for a bunch of rust collections. However sometimes it is necessary to implement Encode yourself.
Most of the time implementing Encode is pretty straight forward. For a simple struct
implementing encode can be as simple as calling encode on all it’s fields in order.
struct MyStruct{
field_a: u32,
field_b: String,
}
impl Encode for MyStruct{
fn encode<W: Write>(&self, w: &mut Writer<W>) -> Result<(), EncodeError>{
Encode::<()>::encode(&self.field_a,w)?;
Encode::<()>::encode(&self.field_b,w)?;
Ok(())
}
}For enums the generall pattern is to first encode the discriminant and then encode the variant it’s fields.
enum MyEnum{
VariantA(u32),
VariantB(String),
}
impl Encode for MyEnum{
fn encode<W: Write>(&self, w: &mut Writer<W>) -> Result<(), EncodeError>{
match self {
MyEnum::VariantA(x) => {
// One good pattern is to avoid using 0 or 1 as a discriminant as these might need
// to be escaped
w.write_u8(2)?;
Encode::<()>::encode(x,w)?;
}
MyEnum::VariantB(x) => {
// One good pattern is to avoid using 0 or 1 as a discriminant as these might need
// to be escaped
w.write_u8(3)?;
Encode::<()>::encode(x,w)?;
}
}
Ok(())
}
}Finally for runtime sized types it you need to mark locations where the decoder might expect a terminator byte to happen. Doing so will cause the write to automatically escape a 0 byte if the next encoded byte happens to be one which is required to properly deserialize runtime sized types.
struct MyVec(Vec<u8>);
impl Encode for MyVec{
fn encode<W: Write>(&self, w: &mut Writer<W>) -> Result<(), EncodeError>{
for v in self.0.iter(){
// Before every entry in the vector it is possible that a terminator might happen
// as the vec could have been shorter. So we need to mark these spots so that the
// writer knows to escape the null byte.
w.mark_terminator();
Encode::<()>::encode(v,w)?;
}
// We have finished the list so we need to mark the end.
w.write_terminator()?;
Ok(())
}
}Required Methods§
Dyn Compatibility§
This trait is not dyn compatible.
In older versions of Rust, dyn compatibility was called "object safety", so this trait is not object safe.