Expand description
Postcard
Postcard is a #![no_std]
focused serializer and deserializer for Serde.
Postcard aims to be convenient for developers in constrained environments, while allowing for flexibility to customize behavior as needed.
Run to 1.0
Postcard will be reaching v1.0.0 in June 2022! Read the announcement blog post for more details!
You can also see the preview specification.
Work towards the Postcard Specification and portions of the Postcard 1.0 Release has been sponsored by Mozilla Corporation.
Design Goals
- Design primarily for
#![no_std]
usage, in embedded or other constrained contexts - Support a maximal set of
serde
features, sopostcard
can be used as a drop in replacement - Avoid special differences in code between communication code written for a microcontroller or a desktop/server PC
- Be resource efficient - memory usage, code size, developer time, and CPU time; in that order
- Allow library users to customize the serialization and deserialization behavior to fit their bespoke needs
Variable Length Data
Variable length data (such as slices) are prefixed by their length.
Length is encoded as a Varint. This is done for two reasons: to minimize wasted bytes
on the wire when sending slices with items less than 127 items (typical for embedded),
and to reduce compatibility issues between 32-bit and 64-bit targets due to differing sizes
of usize
.
Similarly, enum
descriminants are encoded as varints, meaning that any enum with less than
127 variants will encode its discriminant as a single byte (rather than a u32
).
Varints in postcard
have a maximum value of the usize for that platform. In practice, this
means that 64-bit targets should not send messages with slices containing (1 << 32) - 1
items
to 32-bit targets, which is uncommon in practice. Enum discriminants already have a fixed
maximum value of (1 << 32) - 1
as currently defined in Rust. Varints larger than the current platform’s
usize
will cause the deserialization process to return an Err
.
Example - Serialization/Deserialization
Postcard can serialize and deserialize messages similar to other serde
formats.
Using the default heapless
feature to serialize to a heapless::Vec<u8>
:
use core::ops::Deref;
use serde::{Serialize, Deserialize};
use postcard::{from_bytes, to_vec};
use heapless::Vec;
#[derive(Serialize, Deserialize, Debug, Eq, PartialEq)]
struct RefStruct<'a> {
bytes: &'a [u8],
str_s: &'a str,
}
let message = "hElLo";
let bytes = [0x01, 0x10, 0x02, 0x20];
let output: Vec<u8, 11> = to_vec(&RefStruct {
bytes: &bytes,
str_s: message,
}).unwrap();
assert_eq!(
&[0x04, 0x01, 0x10, 0x02, 0x20, 0x05, b'h', b'E', b'l', b'L', b'o',],
output.deref()
);
let out: RefStruct = from_bytes(output.deref()).unwrap();
assert_eq!(
out,
RefStruct {
bytes: &bytes,
str_s: message,
}
);
Or the optional alloc
feature to serialize to an alloc::vec::Vec<u8>
:
use core::ops::Deref;
use serde::{Serialize, Deserialize};
use postcard::{from_bytes, to_allocvec};
extern crate alloc;
use alloc::vec::Vec;
#[derive(Serialize, Deserialize, Debug, Eq, PartialEq)]
struct RefStruct<'a> {
bytes: &'a [u8],
str_s: &'a str,
}
let message = "hElLo";
let bytes = [0x01, 0x10, 0x02, 0x20];
let output: Vec<u8> = to_allocvec(&RefStruct {
bytes: &bytes,
str_s: message,
}).unwrap();
assert_eq!(
&[0x04, 0x01, 0x10, 0x02, 0x20, 0x05, b'h', b'E', b'l', b'L', b'o',],
output.deref()
);
let out: RefStruct = from_bytes(output.deref()).unwrap();
assert_eq!(
out,
RefStruct {
bytes: &bytes,
str_s: message,
}
);
Example - Flavors
postcard
supports a system called Flavors
, which are used to modify the way
postcard serializes or processes serialized data. These flavors act as “plugins” or “middlewares”
during the serialization process, and can be combined to obtain complex protocol formats.
Here, we serialize the given data, while simultaneously encoding it using COBS (a “modification flavor”), and placing the output in a byte slice (a “storage flavor”).
Users of postcard
can define their own Flavors that can be combined with existing Flavors.
use postcard::{
serialize_with_flavor,
ser_flavors::{Cobs, Slice},
};
let mut buf = [0u8; 32];
let data: &[u8] = &[0x01, 0x00, 0x20, 0x30];
let buffer = &mut [0u8; 32];
let res = serialize_with_flavor::<[u8], Cobs<Slice>, &mut [u8]>(
data,
Cobs::try_new(Slice::new(buffer)).unwrap(),
).unwrap();
assert_eq!(res, &[0x03, 0x04, 0x01, 0x03, 0x20, 0x30, 0x00]);
Setup - Cargo.toml
Don’t forget to add the no-std
subset of serde
along with postcard
to the [dependencies]
section of your Cargo.toml
!
[dependencies]
postcard = "0.7.2"
serde = { version = "1.0.*", default-features = false }
License
Licensed under either of
- Apache License, Version 2.0 (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
- MIT license (LICENSE-MIT or http://opensource.org/licenses/MIT)
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.
Modules
Deserialization flavors
Flavors - Plugins for postcard
Structs
An accumulator used to collect chunked COBS data and deserialize it.
A structure for deserializing a postcard message. For now, Deserializer does not implement the same Flavor interface as the serializer does, as messages are typically easier to deserialize in place. This may change in the future for consistency, or to support items that cannot be deserialized in-place, such as compressed message types
A serde
compatible serializer, generic over “Flavors” of serializing plugins.
Enums
This is the error type used by Postcard
The result of feeding the accumulator.
Functions
Deserialize a message of type T
from a byte slice. The unused portion (if any)
of the byte slice is not returned.
Deserialize a message of type T
from a cobs-encoded byte slice. The
unused portion (if any) of the byte slice is not returned.
serialize_with_flavor()
has three generic parameters, T, F, O
.
Deserialize a message of type T
from a byte slice. The unused portion (if any)
of the byte slice is returned for further usage
Deserialize a message of type T
from a cobs-encoded byte slice. The
unused portion (if any) of the byte slice is returned for further usage
Serialize a T
to an alloc::vec::Vec<u8>
. Requires the alloc
feature.
Serialize and COBS encode a T
to an alloc::vec::Vec<u8>
. Requires the alloc
feature.
Serialize a T
to the given slice, with the resulting slice containing
data in a serialized format.
Serialize a T
to the given slice, with the resulting slice containing
data in a serialized then COBS encoded format. The terminating sentinel 0x00
byte is included
in the output buffer.
Serialize a T
to an alloc::vec::Vec<u8>
. Requires the alloc
feature.
Serialize and COBS encode a T
to an alloc::vec::Vec<u8>
. Requires the alloc
feature.
Serialize a T
to a heapless::Vec<u8>
, with the Vec
containing
data in a serialized format. Requires the (default) heapless
feature.
Serialize a T
to a heapless::Vec<u8>
, with the Vec
containing
data in a serialized then COBS encoded format. The terminating sentinel
0x00
byte is included in the output Vec
. Requires the (default) heapless
feature.
Type Definitions
This is the Result type used by Postcard.