feat: add display UI with splash screen, dashboard, and RSSI sparkline

Implement full SSD1306 OLED display support:
- Splash screen on boot (1.5s) with project name, version, board
- Dashboard showing radio state, config, counters, RSSI/SNR
- RSSI history sparkline (64-sample ring buffer, 2px-wide bars)
- DisplayOn/DisplayOff host commands routed via dedicated channel
- Pure render functions against generic DrawTarget (no hardware coupling)
- Display on/off control bypasses radio task entirely

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
swaitsandClaude Opus 4.6 committed 2026-03-29 20:42:54 -06:00
1 parent 7064110293
commit 3102fc747a
17624 files changed
+4564928 -38

No files matched your search

+376
View File
@@ -0,0 +1,376 @@
//! An accumulator used to collect chunked COBS data and deserialize it.
use serde::Deserialize;
/// An accumulator used to collect chunked COBS data and deserialize it.
///
/// This is often useful when you receive "parts" of the message at a time, for example when draining
/// a serial port buffer that may not contain an entire uninterrupted message.
///
/// # Examples
///
/// Deserialize a struct by reading chunks from a [`Read`]er.
///
/// ```rust
/// use postcard::accumulator::{CobsAccumulator, FeedResult};
/// use serde::Deserialize;
/// use std::io::Read;
///
/// # let mut input_buf = [0u8; 256];
/// # #[derive(serde::Serialize, Deserialize, Debug, PartialEq, Eq)]
/// # struct MyData {
/// # a: u32,
/// # b: bool,
/// # c: [u8; 16],
/// # }
/// let input = /* Anything that implements the `Read` trait */
/// # postcard::to_slice_cobs(&MyData {
/// # a: 0xabcdef00,
/// # b: true,
/// # c: [0xab; 16],
/// # }, &mut input_buf).unwrap();
/// # let mut input = &input[..];
///
/// let mut raw_buf = [0u8; 32];
/// let mut cobs_buf: CobsAccumulator<256> = CobsAccumulator::new();
///
/// while let Ok(ct) = input.read(&mut raw_buf) {
/// // Finished reading input
/// if ct == 0 {
/// break;
/// }
///
/// let buf = &raw_buf[..ct];
/// let mut window = &buf[..];
///
/// 'cobs: while !window.is_empty() {
/// window = match cobs_buf.feed::<MyData>(&window) {
/// FeedResult::Consumed => break 'cobs,
/// FeedResult::OverFull(new_wind) => new_wind,
/// FeedResult::DeserError(new_wind) => new_wind,
/// FeedResult::Success { data, remaining } => {
/// // Do something with `data: MyData` here.
///
/// dbg!(data);
///
/// remaining
/// }
/// };
/// }
/// }
/// ```
///
/// [`Read`]: std::io::Read
#[cfg_attr(feature = "use-defmt", derive(defmt::Format))]
pub struct CobsAccumulator<const N: usize> {
buf: [u8; N],
idx: usize,
}
/// The result of feeding the accumulator.
#[cfg_attr(feature = "use-defmt", derive(defmt::Format))]
pub enum FeedResult<'a, T> {
/// Consumed all data, still pending.
Consumed,
/// Buffer was filled. Contains remaining section of input, if any.
OverFull(&'a [u8]),
/// Reached end of chunk, but deserialization failed. Contains remaining section of input, if any.
DeserError(&'a [u8]),
/// Deserialization complete. Contains deserialized data and remaining section of input, if any.
Success {
/// Deserialize data.
data: T,
/// Remaining data left in the buffer after deserializing.
remaining: &'a [u8],
},
}
impl<const N: usize> Default for CobsAccumulator<N> {
fn default() -> Self {
Self::new()
}
}
impl<const N: usize> CobsAccumulator<N> {
/// Create a new accumulator.
pub const fn new() -> Self {
CobsAccumulator {
buf: [0; N],
idx: 0,
}
}
/// Appends data to the internal buffer and attempts to deserialize the accumulated data into
/// `T`.
#[inline]
pub fn feed<'a, T>(&mut self, input: &'a [u8]) -> FeedResult<'a, T>
where
T: for<'de> Deserialize<'de>,
{
self.feed_ref(input)
}
/// Appends data to the internal buffer and attempts to deserialize the accumulated data into
/// `T`.
///
/// This differs from feed, as it allows the `T` to reference data within the internal buffer, but
/// mutably borrows the accumulator for the lifetime of the deserialization.
/// If `T` does not require the reference, the borrow of `self` ends at the end of the function.
pub fn feed_ref<'de, 'a, T>(&'de mut self, input: &'a [u8]) -> FeedResult<'a, T>
where
T: Deserialize<'de>,
{
if input.is_empty() {
return FeedResult::Consumed;
}
let zero_pos = input.iter().position(|&i| i == 0);
if let Some(n) = zero_pos {
// Yes! We have an end of message here.
// Add one to include the zero in the "take" portion
// of the buffer, rather than in "release".
let (take, release) = input.split_at(n + 1);
// Does it fit?
if (self.idx + take.len()) <= N {
// Aw yiss - add to array
self.extend_unchecked(take);
let retval = match crate::from_bytes_cobs::<T>(&mut self.buf[..self.idx]) {
Ok(t) => FeedResult::Success {
data: t,
remaining: release,
},
Err(_) => FeedResult::DeserError(release),
};
self.idx = 0;
retval
} else {
self.idx = 0;
FeedResult::OverFull(release)
}
} else {
// Does it fit?
if (self.idx + input.len()) > N {
// nope
let new_start = N - self.idx;
self.idx = 0;
FeedResult::OverFull(&input[new_start..])
} else {
// yup!
self.extend_unchecked(input);
FeedResult::Consumed
}
}
}
/// Extend the internal buffer with the given input.
///
/// # Panics
///
/// Will panic if the input does not fit in the internal buffer.
fn extend_unchecked(&mut self, input: &[u8]) {
let new_end = self.idx + input.len();
self.buf[self.idx..new_end].copy_from_slice(input);
self.idx = new_end;
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn loop_test() {
#[derive(serde::Serialize, Deserialize, Debug, PartialEq, Eq)]
struct Demo {
a: u32,
b: u8,
}
let mut raw_buf = [0u8; 64];
let mut cobs_buf: CobsAccumulator<64> = CobsAccumulator::new();
let ser = crate::to_slice_cobs(&Demo { a: 10, b: 20 }, &mut raw_buf).unwrap();
if let FeedResult::Success { data, remaining } = cobs_buf.feed(ser) {
assert_eq!(Demo { a: 10, b: 20 }, data);
assert_eq!(remaining.len(), 0);
} else {
panic!()
}
}
#[test]
#[cfg(feature = "heapless")]
fn double_loop_test() {
#[derive(serde::Serialize, Deserialize, Debug, PartialEq, Eq)]
struct Demo {
a: u32,
b: u8,
}
let mut cobs_buf: CobsAccumulator<64> = CobsAccumulator::new();
let mut ser = crate::to_vec_cobs::<_, 128>(&Demo { a: 10, b: 20 }).unwrap();
let ser2 = crate::to_vec_cobs::<_, 128>(&Demo {
a: 256854231,
b: 115,
})
.unwrap();
ser.extend(ser2);
let (demo1, ser) = if let FeedResult::Success { data, remaining } = cobs_buf.feed(&ser[..])
{
(data, remaining)
} else {
panic!()
};
assert_eq!(Demo { a: 10, b: 20 }, demo1);
let demo2 = if let FeedResult::Success { data, remaining } = cobs_buf.feed(ser) {
assert_eq!(remaining.len(), 0);
data
} else {
panic!()
};
assert_eq!(Demo { a: 10, b: 20 }, demo1);
assert_eq!(
Demo {
a: 256854231,
b: 115
},
demo2
);
}
#[test]
#[cfg(feature = "heapless")]
fn loop_test_ref() {
#[derive(serde::Serialize, Deserialize, Debug, PartialEq, Eq)]
struct Demo<'a> {
a: u32,
b: u8,
c: &'a str,
}
let mut cobs_buf: CobsAccumulator<64> = CobsAccumulator::new();
let ser = crate::to_vec_cobs::<_, 128>(&Demo {
a: 10,
b: 20,
c: "test",
})
.unwrap();
if let FeedResult::Success { data, remaining } = cobs_buf.feed_ref(&ser[..]) {
assert_eq!(
Demo {
a: 10,
b: 20,
c: "test"
},
data
);
assert_eq!(remaining.len(), 0);
} else {
panic!()
}
}
#[test]
#[cfg(feature = "heapless")]
fn double_loop_test_ref() {
#[derive(serde::Serialize, Deserialize, Debug, PartialEq, Eq)]
struct Demo<'a> {
a: u32,
b: u8,
c: &'a str,
}
let mut cobs_buf: CobsAccumulator<64> = CobsAccumulator::new();
let mut ser = crate::to_vec_cobs::<_, 128>(&Demo {
a: 10,
b: 20,
c: "test",
})
.unwrap();
let ser2 = crate::to_vec_cobs::<_, 128>(&Demo {
a: 256854231,
b: 115,
c: "different test",
})
.unwrap();
ser.extend(ser2);
let (data, ser) =
if let FeedResult::Success { data, remaining } = cobs_buf.feed_ref(&ser[..]) {
(data, remaining)
} else {
panic!()
};
assert!(
Demo {
a: 10,
b: 20,
c: "test"
} == data
);
let demo2 = if let FeedResult::Success { data, remaining } = cobs_buf.feed_ref(ser) {
assert!(remaining.is_empty());
data
} else {
panic!()
};
// Uncommenting the below line causes the test to no-longer compile, as cobs_buf would then be mutably borrowed twice
//assert!(Demo { a: 10, b: 20, c : "test" } == data);
assert!(
Demo {
a: 256854231,
b: 115,
c: "different test"
} == demo2
);
}
#[test]
#[cfg(feature = "heapless")]
fn extend_unchecked_in_bounds_test() {
// Test bug present in revision abcb407:
// extend_unchecked may be passed slice with size 1 greater than accumulator buffer causing panic
#[derive(serde::Serialize, Deserialize, Debug, PartialEq, Eq)]
struct Demo {
data: [u8; 10],
}
let data = crate::to_vec_cobs::<_, 128>(&Demo { data: [0xcc; 10] }).unwrap();
assert_eq!(data.len(), 12); // 1 byte for offset + 1 sentinel byte appended
// Accumulator has 1 byte less space than encoded message
let mut acc: CobsAccumulator<11> = CobsAccumulator::new();
assert!(matches!(
acc.feed::<Demo>(&data[..]),
FeedResult::OverFull(_)
));
// Accumulator is juuuuust right
let mut acc: CobsAccumulator<12> = CobsAccumulator::new();
assert!(matches!(
acc.feed::<Demo>(&data[..]),
FeedResult::Success { .. }
));
}
}
+602
View File
@@ -0,0 +1,602 @@
use serde::de::{self, DeserializeSeed, IntoDeserializer, Visitor};
use crate::de::flavors::{Flavor, Slice};
use crate::error::{Error, Result};
use crate::varint::{max_of_last_byte, varint_max};
use core::marker::PhantomData;
/// A `serde` compatible deserializer, generic over “Flavors” of deserializing plugins.
///
/// Please note that postcard messages are not self-describing and therefore incompatible with
/// [internally tagged enums](https://serde.rs/enum-representations.html#internally-tagged).
pub struct Deserializer<'de, F: Flavor<'de>> {
flavor: F,
_plt: PhantomData<&'de ()>,
}
impl<'de, F> Deserializer<'de, F>
where
F: Flavor<'de> + 'de,
{
/// Obtain a Deserializer from a slice of bytes
pub fn from_flavor(flavor: F) -> Self {
Deserializer {
flavor,
_plt: PhantomData,
}
}
/// Return the remaining (unused) bytes in the Deserializer along with any
/// additional data provided by the [`Flavor`]
pub fn finalize(self) -> Result<F::Remainder> {
self.flavor.finalize()
}
}
impl<'de> Deserializer<'de, Slice<'de>> {
/// Obtain a Deserializer from a slice of bytes
pub fn from_bytes(input: &'de [u8]) -> Self {
Deserializer {
flavor: Slice::new(input),
_plt: PhantomData,
}
}
}
impl<'de, F: Flavor<'de>> Deserializer<'de, F> {
#[cfg(target_pointer_width = "16")]
#[inline(always)]
fn try_take_varint_usize(&mut self) -> Result<usize> {
self.try_take_varint_u16().map(|u| u as usize)
}
#[cfg(target_pointer_width = "32")]
#[inline(always)]
fn try_take_varint_usize(&mut self) -> Result<usize> {
self.try_take_varint_u32().map(|u| u as usize)
}
#[cfg(target_pointer_width = "64")]
#[inline(always)]
fn try_take_varint_usize(&mut self) -> Result<usize> {
self.try_take_varint_u64().map(|u| u as usize)
}
#[inline]
fn try_take_varint_u16(&mut self) -> Result<u16> {
let mut out = 0;
for i in 0..varint_max::<u16>() {
let val = self.flavor.pop()?;
let carry = (val & 0x7F) as u16;
out |= carry << (7 * i);
if (val & 0x80) == 0 {
if i == varint_max::<u16>() - 1 && val > max_of_last_byte::<u16>() {
return Err(Error::DeserializeBadVarint);
} else {
return Ok(out);
}
}
}
Err(Error::DeserializeBadVarint)
}
#[inline]
fn try_take_varint_u32(&mut self) -> Result<u32> {
let mut out = 0;
for i in 0..varint_max::<u32>() {
let val = self.flavor.pop()?;
let carry = (val & 0x7F) as u32;
out |= carry << (7 * i);
if (val & 0x80) == 0 {
if i == varint_max::<u32>() - 1 && val > max_of_last_byte::<u32>() {
return Err(Error::DeserializeBadVarint);
} else {
return Ok(out);
}
}
}
Err(Error::DeserializeBadVarint)
}
#[inline]
fn try_take_varint_u64(&mut self) -> Result<u64> {
let mut out = 0;
for i in 0..varint_max::<u64>() {
let val = self.flavor.pop()?;
let carry = (val & 0x7F) as u64;
out |= carry << (7 * i);
if (val & 0x80) == 0 {
if i == varint_max::<u64>() - 1 && val > max_of_last_byte::<u64>() {
return Err(Error::DeserializeBadVarint);
} else {
return Ok(out);
}
}
}
Err(Error::DeserializeBadVarint)
}
#[inline]
fn try_take_varint_u128(&mut self) -> Result<u128> {
let mut out = 0;
for i in 0..varint_max::<u128>() {
let val = self.flavor.pop()?;
let carry = (val & 0x7F) as u128;
out |= carry << (7 * i);
if (val & 0x80) == 0 {
if i == varint_max::<u128>() - 1 && val > max_of_last_byte::<u128>() {
return Err(Error::DeserializeBadVarint);
} else {
return Ok(out);
}
}
}
Err(Error::DeserializeBadVarint)
}
}
struct SeqAccess<'a, 'b, F: Flavor<'b>> {
deserializer: &'a mut Deserializer<'b, F>,
len: usize,
}
impl<'a, 'b: 'a, F: Flavor<'b>> serde::de::SeqAccess<'b> for SeqAccess<'a, 'b, F> {
type Error = Error;
#[inline]
fn next_element_seed<V: DeserializeSeed<'b>>(&mut self, seed: V) -> Result<Option<V::Value>> {
if self.len > 0 {
self.len -= 1;
Ok(Some(DeserializeSeed::deserialize(
seed,
&mut *self.deserializer,
)?))
} else {
Ok(None)
}
}
#[inline]
fn size_hint(&self) -> Option<usize> {
match self.deserializer.flavor.size_hint() {
Some(size) if size < self.len => None,
_ => Some(self.len),
}
}
}
struct MapAccess<'a, 'b, F: Flavor<'b>> {
deserializer: &'a mut Deserializer<'b, F>,
len: usize,
}
impl<'a, 'b: 'a, F: Flavor<'b>> serde::de::MapAccess<'b> for MapAccess<'a, 'b, F> {
type Error = Error;
#[inline]
fn next_key_seed<K: DeserializeSeed<'b>>(&mut self, seed: K) -> Result<Option<K::Value>> {
if self.len > 0 {
self.len -= 1;
Ok(Some(DeserializeSeed::deserialize(
seed,
&mut *self.deserializer,
)?))
} else {
Ok(None)
}
}
#[inline]
fn next_value_seed<V: DeserializeSeed<'b>>(&mut self, seed: V) -> Result<V::Value> {
DeserializeSeed::deserialize(seed, &mut *self.deserializer)
}
#[inline]
fn size_hint(&self) -> Option<usize> {
Some(self.len)
}
}
impl<'de, F: Flavor<'de>> de::Deserializer<'de> for &mut Deserializer<'de, F> {
type Error = Error;
#[inline]
fn is_human_readable(&self) -> bool {
false
}
// Postcard does not support structures not known at compile time
#[inline]
fn deserialize_any<V>(self, _visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
// We wont ever support this.
Err(Error::WontImplement)
}
// Take a boolean encoded as a u8
#[inline]
fn deserialize_bool<V>(self, visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
let val = match self.flavor.pop()? {
0 => false,
1 => true,
_ => return Err(Error::DeserializeBadBool),
};
visitor.visit_bool(val)
}
#[inline]
fn deserialize_i8<V>(self, visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
visitor.visit_i8(self.flavor.pop()? as i8)
}
#[inline]
fn deserialize_i16<V>(self, visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
let v = self.try_take_varint_u16()?;
visitor.visit_i16(de_zig_zag_i16(v))
}
#[inline]
fn deserialize_i32<V>(self, visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
let v = self.try_take_varint_u32()?;
visitor.visit_i32(de_zig_zag_i32(v))
}
#[inline]
fn deserialize_i64<V>(self, visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
let v = self.try_take_varint_u64()?;
visitor.visit_i64(de_zig_zag_i64(v))
}
#[inline]
fn deserialize_i128<V>(self, visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
let v = self.try_take_varint_u128()?;
visitor.visit_i128(de_zig_zag_i128(v))
}
#[inline]
fn deserialize_u8<V>(self, visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
visitor.visit_u8(self.flavor.pop()?)
}
#[inline]
fn deserialize_u16<V>(self, visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
let v = self.try_take_varint_u16()?;
visitor.visit_u16(v)
}
#[inline]
fn deserialize_u32<V>(self, visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
let v = self.try_take_varint_u32()?;
visitor.visit_u32(v)
}
#[inline]
fn deserialize_u64<V>(self, visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
let v = self.try_take_varint_u64()?;
visitor.visit_u64(v)
}
#[inline]
fn deserialize_u128<V>(self, visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
let v = self.try_take_varint_u128()?;
visitor.visit_u128(v)
}
#[inline]
fn deserialize_f32<V>(self, visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
let bytes = self.flavor.try_take_n_temp(4)?;
let mut buf = [0u8; 4];
buf.copy_from_slice(bytes);
visitor.visit_f32(f32::from_bits(u32::from_le_bytes(buf)))
}
#[inline]
fn deserialize_f64<V>(self, visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
let bytes = self.flavor.try_take_n_temp(8)?;
let mut buf = [0u8; 8];
buf.copy_from_slice(bytes);
visitor.visit_f64(f64::from_bits(u64::from_le_bytes(buf)))
}
#[inline]
fn deserialize_char<V>(self, visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
let sz = self.try_take_varint_usize()?;
if sz > 4 {
return Err(Error::DeserializeBadChar);
}
let bytes: &[u8] = self.flavor.try_take_n_temp(sz)?;
// we pass the character through string conversion because
// this handles transforming the array of code units to a
// codepoint. we can't use char::from_u32() because it expects
// an already-processed codepoint.
let character = core::str::from_utf8(bytes)
.map_err(|_| Error::DeserializeBadChar)?
.chars()
.next()
.ok_or(Error::DeserializeBadChar)?;
visitor.visit_char(character)
}
#[inline]
fn deserialize_str<V>(self, visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
let sz = self.try_take_varint_usize()?;
let bytes: &'de [u8] = self.flavor.try_take_n(sz)?;
let str_sl = core::str::from_utf8(bytes).map_err(|_| Error::DeserializeBadUtf8)?;
visitor.visit_borrowed_str(str_sl)
}
#[inline]
fn deserialize_string<V>(self, visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
let sz = self.try_take_varint_usize()?;
let bytes: &[u8] = self.flavor.try_take_n_temp(sz)?;
let str_sl = core::str::from_utf8(bytes).map_err(|_| Error::DeserializeBadUtf8)?;
visitor.visit_str(str_sl)
}
#[inline]
fn deserialize_bytes<V>(self, visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
let sz = self.try_take_varint_usize()?;
let bytes: &'de [u8] = self.flavor.try_take_n(sz)?;
visitor.visit_borrowed_bytes(bytes)
}
#[inline]
fn deserialize_byte_buf<V>(self, visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
let sz = self.try_take_varint_usize()?;
let bytes: &[u8] = self.flavor.try_take_n_temp(sz)?;
visitor.visit_bytes(bytes)
}
#[inline]
fn deserialize_option<V>(self, visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
match self.flavor.pop()? {
0 => visitor.visit_none(),
1 => visitor.visit_some(self),
_ => Err(Error::DeserializeBadOption),
}
}
// In Serde, unit means an anonymous value containing no data.
// Unit is not actually encoded in Postcard.
#[inline]
fn deserialize_unit<V>(self, visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
visitor.visit_unit()
}
// Unit struct means a named value containing no data.
// Unit structs are not actually encoded in Postcard.
#[inline]
fn deserialize_unit_struct<V>(self, _name: &'static str, visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
self.deserialize_unit(visitor)
}
#[inline]
fn deserialize_newtype_struct<V>(self, _name: &'static str, visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
visitor.visit_newtype_struct(self)
}
#[inline]
fn deserialize_seq<V>(self, visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
let len = self.try_take_varint_usize()?;
visitor.visit_seq(SeqAccess {
deserializer: self,
len,
})
}
#[inline]
fn deserialize_tuple<V>(self, len: usize, visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
visitor.visit_seq(SeqAccess {
deserializer: self,
len,
})
}
#[inline]
fn deserialize_tuple_struct<V>(
self,
_name: &'static str,
len: usize,
visitor: V,
) -> Result<V::Value>
where
V: Visitor<'de>,
{
self.deserialize_tuple(len, visitor)
}
#[inline]
fn deserialize_map<V>(self, visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
let len = self.try_take_varint_usize()?;
visitor.visit_map(MapAccess {
deserializer: self,
len,
})
}
#[inline]
fn deserialize_struct<V>(
self,
_name: &'static str,
fields: &'static [&'static str],
visitor: V,
) -> Result<V::Value>
where
V: Visitor<'de>,
{
self.deserialize_tuple(fields.len(), visitor)
}
#[inline]
fn deserialize_enum<V>(
self,
_name: &'static str,
_variants: &'static [&'static str],
visitor: V,
) -> Result<V::Value>
where
V: Visitor<'de>,
{
visitor.visit_enum(self)
}
// As a binary format, Postcard does not encode identifiers
#[inline]
fn deserialize_identifier<V>(self, _visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
// Will not support
Err(Error::WontImplement)
}
#[inline]
fn deserialize_ignored_any<V>(self, _visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
// Will not support
Err(Error::WontImplement)
}
}
impl<'de, F: Flavor<'de>> serde::de::VariantAccess<'de> for &mut Deserializer<'de, F> {
type Error = Error;
#[inline]
fn unit_variant(self) -> Result<()> {
Ok(())
}
#[inline]
fn newtype_variant_seed<V: DeserializeSeed<'de>>(self, seed: V) -> Result<V::Value> {
DeserializeSeed::deserialize(seed, self)
}
#[inline]
fn tuple_variant<V: Visitor<'de>>(self, len: usize, visitor: V) -> Result<V::Value> {
serde::de::Deserializer::deserialize_tuple(self, len, visitor)
}
#[inline]
fn struct_variant<V: Visitor<'de>>(
self,
fields: &'static [&'static str],
visitor: V,
) -> Result<V::Value> {
serde::de::Deserializer::deserialize_tuple(self, fields.len(), visitor)
}
}
impl<'de, F: Flavor<'de>> serde::de::EnumAccess<'de> for &mut Deserializer<'de, F> {
type Error = Error;
type Variant = Self;
#[inline]
fn variant_seed<V: DeserializeSeed<'de>>(self, seed: V) -> Result<(V::Value, Self)> {
let varint = self.try_take_varint_u32()?;
let v = DeserializeSeed::deserialize(seed, varint.into_deserializer())?;
Ok((v, self))
}
}
fn de_zig_zag_i16(n: u16) -> i16 {
((n >> 1) as i16) ^ (-((n & 0b1) as i16))
}
fn de_zig_zag_i32(n: u32) -> i32 {
((n >> 1) as i32) ^ (-((n & 0b1) as i32))
}
fn de_zig_zag_i64(n: u64) -> i64 {
((n >> 1) as i64) ^ (-((n & 0b1) as i64))
}
fn de_zig_zag_i128(n: u128) -> i128 {
((n >> 1) as i128) ^ (-((n & 0b1) as i128))
}
+642
View File
@@ -0,0 +1,642 @@
//! # Deserialization Flavors
//!
//! "Flavors" in `postcard` are used as modifiers to the serialization or deserialization
//! process. Flavors typically modify one or both of the following:
//!
//! 1. The source medium of the deserialization, e.g. whether the data is serialized from a `[u8]` slice, or some other container
//! 2. The format of the deserialization, such as if the original data is encoded in a COBS format, contains a CRC32 checksum
//! appended to the message, etc.
//!
//! Flavors are implemented using the [`Flavor`] trait, which acts as a "middleware" for retrieving the bytes before they
//! are passed to `serde` for deserialization
//!
//! Multiple flavors may be combined to obtain a desired combination of behavior and storage.
//! When flavors are combined, it is expected that the storage flavor (such as [`Slice`]) is the innermost flavor.
//!
//! Custom flavors may be defined by users of the `postcard` crate, however some commonly useful flavors have been provided in
//! this module. If you think your custom flavor would be useful to others, PRs adding flavors are very welcome!
//!
//! ## Usability
//!
//! Flavors may not always be convenient to use directly, as they may expose some implementation details of how the
//! inner workings of the flavor behaves. It is typical to provide a convenience method for using a flavor, to prevent
//! the user from having to specify generic parameters, setting correct initialization values, or handling the output of
//! the flavor correctly. See `postcard::from_bytes()` for an example of this.
//!
//! ## When to use (multiple) flavors
//!
//! Combining flavors are nice for convenience, as they perform potentially multiple steps of
//! serialization at one time.
//!
//! This can often be more memory efficient, as intermediate buffers are not typically required.
//!
//! ## When NOT to use (multiple) flavors
//!
//! The downside of passing deserialization through multiple steps is that it is typically slower than
//! performing each step serially. Said simply, "cobs decoding while deserializing" is often slower
//! than "cobs decode then deserialize", due to the ability to handle longer "runs" of data in each
//! stage. The downside is that if these stages can not be performed in-place on the buffer, you
//! will need additional buffers for each stage.
//!
//! Additionally, deserializating flavors can be more restrictive or difficult to work with than
//! serialization flavors, as deserialization may require that the deserialized types borrow some
//! portion of the original message.
//!
//! ## Examples
//!
//! ### Using a single flavor
//!
//! In the first example, we use the `Slice` flavor, to retrieve the serialized output from a `[u8]` slice.
//! No other modification is made to the serialization process.
//!
//! ```rust
//! use postcard::{
//! de_flavors::Slice,
//! Deserializer,
//! };
//! use serde::Deserialize;
//!
//! #[derive(Deserialize, Debug, PartialEq)]
//! struct Tup(u8, u8, u8);
//!
//! let msg = [0x04, 0x00, 0x04, 0x01, 0x02, 0x03];
//! let slice = Slice::new(&msg);
//! let mut deserializer = Deserializer::from_flavor(slice);
//! let t = Tup::deserialize(&mut deserializer).unwrap();
//! assert_eq!(t, Tup(4, 0, 4));
//! let remainder = deserializer.finalize().unwrap();
//! assert_eq!(remainder, &[1, 2, 3]);
//! ```
use crate::{Error, Result};
use core::marker::PhantomData;
/// The deserialization Flavor trait
///
/// This is used as the primary way to decode serialized data from some kind of buffer,
/// or modify that data in a middleware style pattern.
///
/// See the module level docs for an example of how flavors are used.
pub trait Flavor<'de>: 'de {
/// The remaining data of this flavor after deserializing has completed.
///
/// Typically, this includes the remaining buffer that was not used for
/// deserialization, and in cases of more complex flavors, any additional
/// information that was decoded or otherwise calculated during
/// the deserialization process.
type Remainder: 'de;
/// The source of data retrieved for deserialization.
///
/// This is typically some sort of data buffer, or another Flavor, when
/// chained behavior is desired
type Source: 'de;
/// Obtain the next byte for deserialization
fn pop(&mut self) -> Result<u8>;
/// Returns the number of bytes remaining in the message, if known.
///
/// # Implementation notes
///
/// It is not enforced that this number is exactly correct.
/// A flavor may yield less or more bytes than the what is hinted at by
/// this function.
///
/// `size_hint()` is primarily intended to be used for optimizations such as
/// reserving space for deserialized items, but must not be trusted to
/// e.g., omit bounds checks in unsafe code. An incorrect implementation of
/// `size_hint()` should not lead to memory safety violations.
///
/// That said, the implementation should provide a correct estimation,
/// because otherwise it would be a violation of the trait’s protocol.
///
/// The default implementation returns `None` which is correct for any flavor.
fn size_hint(&self) -> Option<usize> {
None
}
/// Attempt to take the next `ct` bytes from the serialized message.
///
/// This variant borrows the data from the input for zero-copy deserialization. If zero-copy
/// deserialization is not necessary, prefer to use `try_take_n_temp` instead.
fn try_take_n(&mut self, ct: usize) -> Result<&'de [u8]>;
/// Attempt to take the next `ct` bytes from the serialized message.
///
/// This variant does not guarantee that the returned value is borrowed from the input, so it
/// cannot be used for zero-copy deserialization, but it also avoids needing to potentially
/// allocate a data in a temporary buffer.
///
/// This variant should be used instead of `try_take_n`
/// if zero-copy deserialization is not necessary.
///
/// It is only necessary to implement this method if the flavor requires storing data in a
/// temporary buffer in order to implement the borrow semantics, e.g. the `std::io::Read`
/// flavor.
fn try_take_n_temp<'a>(&'a mut self, ct: usize) -> Result<&'a [u8]>
where
'de: 'a,
{
self.try_take_n(ct)
}
/// Complete the deserialization process.
///
/// This is typically called separately, after the `serde` deserialization
/// has completed.
fn finalize(self) -> Result<Self::Remainder>;
}
/// A simple [`Flavor`] representing the deserialization from a borrowed slice
pub struct Slice<'de> {
// This string starts with the input data and characters are truncated off
// the beginning as data is parsed.
pub(crate) cursor: *const u8,
pub(crate) end: *const u8,
pub(crate) _pl: PhantomData<&'de [u8]>,
}
impl<'de> Slice<'de> {
/// Create a new [Slice] from the given buffer
pub fn new(sli: &'de [u8]) -> Self {
let range = sli.as_ptr_range();
Self {
cursor: range.start,
end: range.end,
_pl: PhantomData,
}
}
}
impl<'de> Flavor<'de> for Slice<'de> {
type Remainder = &'de [u8];
type Source = &'de [u8];
#[inline]
fn pop(&mut self) -> Result<u8> {
if self.cursor == self.end {
Err(Error::DeserializeUnexpectedEnd)
} else {
// SAFETY: `self.cursor` is in-bounds and won't be incremented past `self.end` as we
// have checked above.
unsafe {
let res = Ok(*self.cursor);
self.cursor = self.cursor.add(1);
res
}
}
}
#[inline]
fn size_hint(&self) -> Option<usize> {
Some((self.end as usize) - (self.cursor as usize))
}
#[inline]
fn try_take_n(&mut self, ct: usize) -> Result<&'de [u8]> {
let remain = (self.end as usize) - (self.cursor as usize);
if remain < ct {
Err(Error::DeserializeUnexpectedEnd)
} else {
// SAFETY: `self.cursor` is valid for `ct` elements and won't be incremented past `self.end` as we
// have checked above.
unsafe {
let sli = core::slice::from_raw_parts(self.cursor, ct);
self.cursor = self.cursor.add(ct);
Ok(sli)
}
}
}
/// Return the remaining (unused) bytes in the Deserializer
fn finalize(self) -> Result<&'de [u8]> {
let remain = (self.end as usize) - (self.cursor as usize);
// SAFETY: `self.cursor` is valid for `remain` elements
unsafe { Ok(core::slice::from_raw_parts(self.cursor, remain)) }
}
}
/// Support for [`std::io`] or `embedded-io` traits
#[cfg(any(
feature = "embedded-io-04",
feature = "embedded-io-06",
feature = "use-std"
))]
pub mod io {
use crate::{Error, Result};
use core::marker::PhantomData;
struct SlidingBuffer<'de> {
cursor: *mut u8,
end: *const u8,
_pl: PhantomData<&'de [u8]>,
}
impl<'de> SlidingBuffer<'de> {
pub fn new(sli: &'de mut [u8]) -> Self {
let range = sli.as_mut_ptr_range();
Self {
cursor: range.start,
end: range.end,
_pl: PhantomData,
}
}
#[inline]
fn take_n(&mut self, ct: usize) -> Result<&'de mut [u8]> {
let remain = (self.end as usize) - (self.cursor as usize);
let buff = if remain < ct {
return Err(Error::DeserializeUnexpectedEnd);
} else {
// SAFETY: `self.cursor` is valid for `ct` elements and won't be incremented
// past `self.end` as we have checked above.
unsafe {
let sli = core::slice::from_raw_parts_mut(self.cursor, ct);
self.cursor = self.cursor.add(ct);
sli
}
};
Ok(buff)
}
#[inline]
fn take_n_temp(&mut self, ct: usize) -> Result<&mut [u8]> {
let remain = (self.end as usize) - (self.cursor as usize);
let buff = if remain < ct {
return Err(Error::DeserializeUnexpectedEnd);
} else {
unsafe {
let sli = core::slice::from_raw_parts_mut(self.cursor, ct);
sli
}
};
Ok(buff)
}
fn complete(self) -> Result<&'de mut [u8]> {
let remain = (self.end as usize) - (self.cursor as usize);
// SAFETY: `self.cursor` is valid for `remain` elements
unsafe { Ok(core::slice::from_raw_parts_mut(self.cursor, remain)) }
}
}
/// Support for [`embedded_io`](crate::eio::embedded_io) traits
#[cfg(any(feature = "embedded-io-04", feature = "embedded-io-06"))]
pub mod eio {
use super::super::Flavor;
use super::SlidingBuffer;
use crate::{Error, Result};
/// Wrapper over a [`embedded_io`](crate::eio::embedded_io)::[`Read`](crate::eio::Read) and a sliding buffer to implement the [`Flavor`] trait
pub struct EIOReader<'de, T>
where
T: crate::eio::Read,
{
reader: T,
buff: SlidingBuffer<'de>,
}
impl<'de, T> EIOReader<'de, T>
where
T: crate::eio::Read,
{
/// Create a new [`EIOReader`] from a reader and a buffer.
///
/// `buff` must have enough space to hold all data read during the deserialisation.
pub fn new(reader: T, buff: &'de mut [u8]) -> Self {
Self {
reader,
buff: SlidingBuffer::new(buff),
}
}
}
impl<'de, T> Flavor<'de> for EIOReader<'de, T>
where
T: crate::eio::Read + 'de,
{
type Remainder = (T, &'de mut [u8]);
type Source = &'de [u8];
#[inline]
fn pop(&mut self) -> Result<u8> {
let mut val = [0; 1];
self.reader
.read_exact(&mut val)
.map_err(|_| Error::DeserializeUnexpectedEnd)?;
Ok(val[0])
}
#[inline]
fn size_hint(&self) -> Option<usize> {
None
}
#[inline]
fn try_take_n(&mut self, ct: usize) -> Result<&'de [u8]> {
let buff = self.buff.take_n(ct)?;
self.reader
.read_exact(buff)
.map_err(|_| Error::DeserializeUnexpectedEnd)?;
Ok(buff)
}
#[inline]
fn try_take_n_temp<'a>(&'a mut self, ct: usize) -> Result<&'a [u8]>
where
'de: 'a,
{
let buff = self.buff.take_n_temp(ct)?;
self.reader
.read_exact(buff)
.map_err(|_| Error::DeserializeUnexpectedEnd)?;
Ok(buff)
}
/// Return the remaining (unused) bytes in the Deserializer
fn finalize(self) -> Result<(T, &'de mut [u8])> {
let buf = self.buff.complete()?;
Ok((self.reader, buf))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_pop() {
let mut reader = EIOReader::new(&[0xAA, 0xBB, 0xCC][..], &mut []);
assert_eq!(reader.pop(), Ok(0xAA));
assert_eq!(reader.pop(), Ok(0xBB));
assert_eq!(reader.pop(), Ok(0xCC));
assert_eq!(reader.pop(), Err(Error::DeserializeUnexpectedEnd));
}
#[test]
fn test_try_take_n() {
let mut buf = [0; 8];
let mut reader = EIOReader::new(&[0xAA, 0xBB, 0xCC, 0xDD, 0xEE][..], &mut buf);
assert_eq!(reader.try_take_n(2), Ok(&[0xAA, 0xBB][..]));
assert_eq!(reader.try_take_n(2), Ok(&[0xCC, 0xDD][..]));
assert_eq!(reader.try_take_n(2), Err(Error::DeserializeUnexpectedEnd));
}
}
}
/// Support for [`std::io`] traits
#[allow(clippy::module_inception)]
#[cfg(feature = "use-std")]
pub mod io {
use super::super::Flavor;
use super::SlidingBuffer;
use crate::{Error, Result};
/// Wrapper over a [`std::io::Read`] and a sliding buffer to implement the [Flavor] trait
pub struct IOReader<'de, T>
where
T: std::io::Read,
{
reader: T,
buff: SlidingBuffer<'de>,
}
impl<'de, T> IOReader<'de, T>
where
T: std::io::Read,
{
/// Create a new [`IOReader`] from a reader and a buffer.
///
/// `buff` must have enough space to hold all data read during the deserialisation.
pub fn new(reader: T, buff: &'de mut [u8]) -> Self {
Self {
reader,
buff: SlidingBuffer::new(buff),
}
}
}
impl<'de, T> Flavor<'de> for IOReader<'de, T>
where
T: std::io::Read + 'de,
{
type Remainder = (T, &'de mut [u8]);
type Source = &'de [u8];
#[inline]
fn pop(&mut self) -> Result<u8> {
let mut val = [0; 1];
self.reader
.read_exact(&mut val)
.map_err(|_| Error::DeserializeUnexpectedEnd)?;
Ok(val[0])
}
#[inline]
fn size_hint(&self) -> Option<usize> {
None
}
#[inline]
fn try_take_n(&mut self, ct: usize) -> Result<&'de [u8]> {
let buff = self.buff.take_n(ct)?;
self.reader
.read_exact(buff)
.map_err(|_| Error::DeserializeUnexpectedEnd)?;
Ok(buff)
}
#[inline]
fn try_take_n_temp<'a>(&'a mut self, ct: usize) -> Result<&'a [u8]>
where
'de: 'a,
{
let buff = self.buff.take_n_temp(ct)?;
self.reader
.read_exact(buff)
.map_err(|_| Error::DeserializeUnexpectedEnd)?;
Ok(buff)
}
/// Return the remaining (unused) bytes in the Deserializer
fn finalize(self) -> Result<(T, &'de mut [u8])> {
let buf = self.buff.complete()?;
Ok((self.reader, buf))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_pop() {
let mut reader = IOReader::new(&[0xAA, 0xBB, 0xCC][..], &mut []);
assert_eq!(reader.pop(), Ok(0xAA));
assert_eq!(reader.pop(), Ok(0xBB));
assert_eq!(reader.pop(), Ok(0xCC));
assert_eq!(reader.pop(), Err(Error::DeserializeUnexpectedEnd));
}
#[test]
fn test_try_take_n() {
let mut buf = [0; 8];
let mut reader = IOReader::new(&[0xAA, 0xBB, 0xCC, 0xDD, 0xEE][..], &mut buf);
assert_eq!(reader.try_take_n(2), Ok(&[0xAA, 0xBB][..]));
assert_eq!(reader.try_take_n(2), Ok(&[0xCC, 0xDD][..]));
assert_eq!(reader.try_take_n(2), Err(Error::DeserializeUnexpectedEnd));
}
}
}
}
////////////////////////////////////////
// CRC
////////////////////////////////////////
/// This Cyclic Redundancy Check flavor applies [the CRC crate's `Algorithm`](https://docs.rs/crc/latest/crc/struct.Algorithm.html) struct on
/// the serialized data.
///
/// The flavor will check the CRC assuming that it has been appended to the bytes.
/// CRCs are used for error detection when reading data back.
/// Requires the `crc` feature.
///
/// More on CRCs: <https://en.wikipedia.org/wiki/Cyclic_redundancy_check>.
#[cfg(feature = "use-crc")]
#[cfg_attr(docsrs, doc(cfg(feature = "use-crc")))]
pub mod crc {
use core::convert::TryInto;
use crc::Digest;
use crc::Width;
use serde::Deserialize;
use super::Flavor;
use super::Slice;
use crate::Deserializer;
use crate::Error;
use crate::Result;
/// Manages CRC modifications as a flavor.
pub struct CrcModifier<'de, B, W>
where
B: Flavor<'de>,
W: Width,
{
flav: B,
digest: Digest<'de, W>,
}
impl<'de, B, W> CrcModifier<'de, B, W>
where
B: Flavor<'de>,
W: Width,
{
/// Create a new Crc modifier Flavor.
pub fn new(bee: B, digest: Digest<'de, W>) -> Self {
Self { flav: bee, digest }
}
}
macro_rules! impl_flavor {
($int:ty, $from_bytes:ident, $take_from_bytes:ident) => {
impl<'de, B> Flavor<'de> for CrcModifier<'de, B, $int>
where
B: Flavor<'de>,
{
type Remainder = B::Remainder;
type Source = B::Source;
#[inline]
fn pop(&mut self) -> Result<u8> {
match self.flav.pop() {
Ok(byte) => {
self.digest.update(&[byte]);
Ok(byte)
}
e @ Err(_) => e,
}
}
#[inline]
fn size_hint(&self) -> Option<usize> {
self.flav.size_hint()
}
#[inline]
fn try_take_n(&mut self, ct: usize) -> Result<&'de [u8]> {
match self.flav.try_take_n(ct) {
Ok(bytes) => {
self.digest.update(bytes);
Ok(bytes)
}
e @ Err(_) => e,
}
}
fn finalize(mut self) -> Result<Self::Remainder> {
match self.flav.try_take_n(core::mem::size_of::<$int>()) {
Ok(prev_crc_bytes) => match self.flav.finalize() {
Ok(remainder) => {
let crc = self.digest.finalize();
let le_bytes = prev_crc_bytes
.try_into()
.map_err(|_| Error::DeserializeBadEncoding)?;
let prev_crc = <$int>::from_le_bytes(le_bytes);
if crc == prev_crc {
Ok(remainder)
} else {
Err(Error::DeserializeBadCrc)
}
}
e @ Err(_) => e,
},
Err(e) => Err(e),
}
}
}
/// Deserialize a message of type `T` from a byte slice with a Crc. The unused portion (if any)
/// of the byte slice is not returned.
pub fn $from_bytes<'a, T>(s: &'a [u8], digest: Digest<'a, $int>) -> Result<T>
where
T: Deserialize<'a>,
{
let flav = CrcModifier::new(Slice::new(s), digest);
let mut deserializer = Deserializer::from_flavor(flav);
let r = T::deserialize(&mut deserializer)?;
let _ = deserializer.finalize()?;
Ok(r)
}
/// Deserialize a message of type `T` from a byte slice with a Crc. The unused portion (if any)
/// of the byte slice is returned for further usage
pub fn $take_from_bytes<'a, T>(
s: &'a [u8],
digest: Digest<'a, $int>,
) -> Result<(T, &'a [u8])>
where
T: Deserialize<'a>,
{
let flav = CrcModifier::new(Slice::new(s), digest);
let mut deserializer = Deserializer::from_flavor(flav);
let t = T::deserialize(&mut deserializer)?;
Ok((t, deserializer.finalize()?))
}
};
}
impl_flavor!(u8, from_bytes_u8, take_from_bytes_u8);
impl_flavor!(u16, from_bytes_u16, take_from_bytes_u16);
impl_flavor!(u32, from_bytes_u32, take_from_bytes_u32);
impl_flavor!(u64, from_bytes_u64, take_from_bytes_u64);
impl_flavor!(u128, from_bytes_u128, take_from_bytes_u128);
}
+612
View File
@@ -0,0 +1,612 @@
use cobs::{decode_in_place, decode_in_place_report};
use serde::Deserialize;
pub(crate) mod deserializer;
pub mod flavors;
use crate::error::{Error, Result};
use deserializer::Deserializer;
/// Deserialize a message of type `T` from a byte slice. The unused portion (if any)
/// of the byte slice is not returned.
pub fn from_bytes<'a, T>(s: &'a [u8]) -> Result<T>
where
T: Deserialize<'a>,
{
let mut deserializer = Deserializer::from_bytes(s);
let t = T::deserialize(&mut deserializer)?;
Ok(t)
}
/// Deserialize a message of type `T` from a cobs-encoded byte slice.
///
/// The unused portion (if any) of the byte slice is not returned.
/// The used portion of the input slice is modified during deserialization (even if an error is returned).
/// Therefore, if this is not desired, pass a clone of the original slice.
pub fn from_bytes_cobs<'a, T>(s: &'a mut [u8]) -> Result<T>
where
T: Deserialize<'a>,
{
let sz = decode_in_place(s).map_err(|_| Error::DeserializeBadEncoding)?;
from_bytes::<T>(&s[..sz])
}
/// 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.
/// The used portion of the input slice is modified during deserialization (even if an error is returned).
/// Therefore, if this is not desired, pass a clone of the original slice.
pub fn take_from_bytes_cobs<'a, T>(s: &'a mut [u8]) -> Result<(T, &'a mut [u8])>
where
T: Deserialize<'a>,
{
let mut report = decode_in_place_report(s).map_err(|_| Error::DeserializeBadEncoding)?;
// The report does not include terminator bytes. If there is one in the
// buffer right AFTER the message, also include it.
if s.get(report.src_used) == Some(&0) {
report.src_used += 1;
}
// First split off the amount used for the "destination", which includes our now
// decoded message to deserialize
let (dst_used, dst_unused) = s.split_at_mut(report.dst_used);
// Then create a slice that includes the unused bytes, but DON'T include the
// excess bytes that were "shrunk" away from the original message
let (_unused, src_unused) = dst_unused.split_at_mut(report.src_used - report.dst_used);
Ok((from_bytes::<T>(dst_used)?, src_unused))
}
/// Deserialize a message of type `T` from a byte slice. The unused portion (if any)
/// of the byte slice is returned for further usage
pub fn take_from_bytes<'a, T>(s: &'a [u8]) -> Result<(T, &'a [u8])>
where
T: Deserialize<'a>,
{
let mut deserializer = Deserializer::from_bytes(s);
let t = T::deserialize(&mut deserializer)?;
Ok((t, deserializer.finalize()?))
}
/// Deserialize a message of type `T` from a [`embedded_io`](crate::eio::embedded_io)::[`Read`](crate::eio::Read).
#[cfg(any(feature = "embedded-io-04", feature = "embedded-io-06"))]
pub fn from_eio<'a, T, R>(val: (R, &'a mut [u8])) -> Result<(T, (R, &'a mut [u8]))>
where
T: Deserialize<'a>,
R: crate::eio::Read + 'a,
{
let flavor = flavors::io::eio::EIOReader::new(val.0, val.1);
let mut deserializer = Deserializer::from_flavor(flavor);
let t = T::deserialize(&mut deserializer)?;
Ok((t, deserializer.finalize()?))
}
/// Deserialize a message of type `T` from a [`std::io::Read`].
#[cfg(feature = "use-std")]
pub fn from_io<'a, T, R>(val: (R, &'a mut [u8])) -> Result<(T, (R, &'a mut [u8]))>
where
T: Deserialize<'a>,
R: std::io::Read + 'a,
{
let flavor = flavors::io::io::IOReader::new(val.0, val.1);
let mut deserializer = Deserializer::from_flavor(flavor);
let t = T::deserialize(&mut deserializer)?;
Ok((t, deserializer.finalize()?))
}
/// Conveniently deserialize a message of type `T` from a byte slice with a Crc. The unused portion (if any)
/// of the byte slice is not returned.
///
/// See the `de_flavors::crc` module for the complete set of functions.
#[cfg(feature = "use-crc")]
#[cfg_attr(docsrs, doc(cfg(feature = "use-crc")))]
#[inline]
pub fn from_bytes_crc32<'a, T>(s: &'a [u8], digest: crc::Digest<'a, u32>) -> Result<T>
where
T: Deserialize<'a>,
{
flavors::crc::from_bytes_u32(s, digest)
}
/// Conveniently deserialize a message of type `T` from a byte slice with a Crc. The unused portion (if any)
/// of the byte slice is returned for further usage
///
/// See the `de_flavors::crc` module for the complete set of functions.
#[cfg(feature = "use-crc")]
#[cfg_attr(docsrs, doc(cfg(feature = "use-crc")))]
#[inline]
pub fn take_from_bytes_crc32<'a, T>(
s: &'a [u8],
digest: crc::Digest<'a, u32>,
) -> Result<(T, &'a [u8])>
where
T: Deserialize<'a>,
{
flavors::crc::take_from_bytes_u32(s, digest)
}
////////////////////////////////////////////////////////////////////////////////
#[cfg(feature = "heapless")]
#[cfg(test)]
mod test_heapless {
use super::*;
use crate::{ser::to_vec, to_vec_cobs, varint::varint_max};
use core::fmt::Write;
use core::ops::Deref;
use heapless::{FnvIndexMap, String, Vec};
use serde::{Deserialize, Deserializer, Serialize, Serializer};
#[test]
fn de_u8() {
let output: Vec<u8, 1> = to_vec(&0x05u8).unwrap();
assert_eq!(&[5], output.deref());
let out: u8 = from_bytes(output.deref()).unwrap();
assert_eq!(out, 0x05);
}
#[test]
fn de_u16() {
let output: Vec<u8, { varint_max::<u16>() }> = to_vec(&0xA5C7u16).unwrap();
assert_eq!(&[0xC7, 0xCB, 0x02], output.deref());
let out: u16 = from_bytes(output.deref()).unwrap();
assert_eq!(out, 0xA5C7);
}
#[test]
fn de_u32() {
let output: Vec<u8, { varint_max::<u32>() }> = to_vec(&0xCDAB3412u32).unwrap();
assert_eq!(&[0x92, 0xE8, 0xAC, 0xED, 0x0C], output.deref());
let out: u32 = from_bytes(output.deref()).unwrap();
assert_eq!(out, 0xCDAB3412u32);
}
#[test]
fn de_u64() {
let output: Vec<u8, { varint_max::<u64>() }> = to_vec(&0x1234_5678_90AB_CDEFu64).unwrap();
assert_eq!(
&[0xEF, 0x9B, 0xAF, 0x85, 0x89, 0xCF, 0x95, 0x9A, 0x12],
output.deref()
);
let out: u64 = from_bytes(output.deref()).unwrap();
assert_eq!(out, 0x1234_5678_90AB_CDEFu64);
}
#[test]
fn de_u128() {
let output: Vec<u8, { varint_max::<u128>() }> =
to_vec(&0x1234_5678_90AB_CDEF_1234_5678_90AB_CDEFu128).unwrap();
assert_eq!(
&[
0xEF, 0x9B, 0xAF, 0x85, 0x89, 0xCF, 0x95, 0x9A, 0x92, 0xDE, 0xB7, 0xDE, 0x8A, 0x92,
0x9E, 0xAB, 0xB4, 0x24,
],
output.deref()
);
let out: u128 = from_bytes(output.deref()).unwrap();
assert_eq!(out, 0x1234_5678_90AB_CDEF_1234_5678_90AB_CDEFu128);
}
#[derive(Debug, Serialize, Deserialize, Eq, PartialEq)]
struct BasicU8S {
st: u16,
ei: u8,
ote: u128,
sf: u64,
tt: u32,
}
#[test]
fn de_struct_unsigned() {
let data = BasicU8S {
st: 0xABCD,
ei: 0xFE,
ote: 0x1234_4321_ABCD_DCBA_1234_4321_ABCD_DCBA,
sf: 0x1234_4321_ABCD_DCBA,
tt: 0xACAC_ACAC,
};
const SZ: usize = varint_max::<u16>()
+ 1
+ varint_max::<u128>()
+ varint_max::<u64>()
+ varint_max::<u32>();
let output: Vec<u8, SZ> = to_vec(&data).unwrap();
assert_eq!(
&[
0xCD, 0xD7, 0x02, 0xFE, 0xBA, 0xB9, 0xB7, 0xDE, 0x9A, 0xE4, 0x90, 0x9A, 0x92, 0xF4,
0xF2, 0xEE, 0xBC, 0xB5, 0xC8, 0xA1, 0xB4, 0x24, 0xBA, 0xB9, 0xB7, 0xDE, 0x9A, 0xE4,
0x90, 0x9A, 0x12, 0xAC, 0xD9, 0xB2, 0xE5, 0x0A
],
output.deref()
);
let out: BasicU8S = from_bytes(output.deref()).unwrap();
assert_eq!(out, data);
}
#[test]
fn de_byte_slice() {
let input: &[u8] = &[1u8, 2, 3, 4, 5, 6, 7, 8];
let output: Vec<u8, 9> = to_vec(input).unwrap();
assert_eq!(
&[0x08, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08],
output.deref()
);
let out: Vec<u8, 128> = from_bytes(output.deref()).unwrap();
assert_eq!(input, out.deref());
let mut input: Vec<u8, 1024> = Vec::new();
for i in 0..1024 {
input.push((i & 0xFF) as u8).unwrap();
}
let output: Vec<u8, 2048> = to_vec(input.deref()).unwrap();
assert_eq!(&[0x80, 0x08], &output.deref()[..2]);
assert_eq!(output.len(), 1026);
for (i, val) in output.deref()[2..].iter().enumerate() {
assert_eq!((i & 0xFF) as u8, *val);
}
let de: Vec<u8, 1024> = from_bytes(output.deref()).unwrap();
assert_eq!(input.deref(), de.deref());
}
#[test]
fn de_str() {
let input: &str = "hello, postcard!";
let output: Vec<u8, 17> = to_vec(input).unwrap();
assert_eq!(0x10, output.deref()[0]);
assert_eq!(input.as_bytes(), &output.deref()[1..]);
let mut input: String<1024> = String::new();
for _ in 0..256 {
write!(&mut input, "abcd").unwrap();
}
let output: Vec<u8, 2048> = to_vec(input.deref()).unwrap();
assert_eq!(&[0x80, 0x08], &output.deref()[..2]);
assert_eq!(output.len(), 1026);
for ch in output.deref()[2..].chunks(4) {
assert_eq!("abcd", core::str::from_utf8(ch).unwrap());
}
let de: String<1024> = from_bytes(output.deref()).unwrap();
assert_eq!(input.deref(), de.deref());
}
#[allow(dead_code)]
#[derive(Serialize, Deserialize, Debug, Eq, PartialEq)]
enum BasicEnum {
Bib,
Bim,
Bap,
}
#[derive(Serialize, Deserialize, Debug, Eq, PartialEq)]
struct EnumStruct {
eight: u8,
sixt: u16,
}
#[derive(Serialize, Deserialize, Debug, Eq, PartialEq)]
enum DataEnum {
Bib(u16),
Bim(u64),
Bap(u8),
Kim(EnumStruct),
Chi { a: u8, b: u32 },
Sho(u16, u8),
}
#[test]
fn enums() {
let output: Vec<u8, 1> = to_vec(&BasicEnum::Bim).unwrap();
assert_eq!(&[0x01], output.deref());
let out: BasicEnum = from_bytes(output.deref()).unwrap();
assert_eq!(out, BasicEnum::Bim);
let output: Vec<u8, { 1 + varint_max::<u64>() }> =
to_vec(&DataEnum::Bim(u64::MAX)).unwrap();
assert_eq!(
&[0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01],
output.deref()
);
let output: Vec<u8, { 1 + varint_max::<u16>() }> =
to_vec(&DataEnum::Bib(u16::MAX)).unwrap();
assert_eq!(&[0x00, 0xFF, 0xFF, 0x03], output.deref());
let out: DataEnum = from_bytes(output.deref()).unwrap();
assert_eq!(out, DataEnum::Bib(u16::MAX));
let output: Vec<u8, 2> = to_vec(&DataEnum::Bap(u8::MAX)).unwrap();
assert_eq!(&[0x02, 0xFF], output.deref());
let out: DataEnum = from_bytes(output.deref()).unwrap();
assert_eq!(out, DataEnum::Bap(u8::MAX));
let output: Vec<u8, 8> = to_vec(&DataEnum::Kim(EnumStruct {
eight: 0xF0,
sixt: 0xACAC,
}))
.unwrap();
assert_eq!(&[0x03, 0xF0, 0xAC, 0xD9, 0x02], output.deref());
let out: DataEnum = from_bytes(output.deref()).unwrap();
assert_eq!(
out,
DataEnum::Kim(EnumStruct {
eight: 0xF0,
sixt: 0xACAC
})
);
let output: Vec<u8, 8> = to_vec(&DataEnum::Chi {
a: 0x0F,
b: 0xC7C7C7C7,
})
.unwrap();
assert_eq!(&[0x04, 0x0F, 0xC7, 0x8F, 0x9F, 0xBE, 0x0C], output.deref());
let out: DataEnum = from_bytes(output.deref()).unwrap();
assert_eq!(
out,
DataEnum::Chi {
a: 0x0F,
b: 0xC7C7C7C7
}
);
let output: Vec<u8, 8> = to_vec(&DataEnum::Sho(0x6969, 0x07)).unwrap();
assert_eq!(&[0x05, 0xE9, 0xD2, 0x01, 0x07], output.deref());
let out: DataEnum = from_bytes(output.deref()).unwrap();
assert_eq!(out, DataEnum::Sho(0x6969, 0x07));
}
#[test]
fn tuples() {
let output: Vec<u8, 128> = to_vec(&(1u8, 10u32, "Hello!")).unwrap();
assert_eq!(
&[1u8, 0x0A, 0x06, b'H', b'e', b'l', b'l', b'o', b'!'],
output.deref()
);
let out: (u8, u32, &str) = from_bytes(output.deref()).unwrap();
assert_eq!(out, (1u8, 10u32, "Hello!"));
}
#[derive(Debug, Eq, PartialEq)]
pub struct ByteSliceStruct<'a> {
bytes: &'a [u8],
}
impl Serialize for ByteSliceStruct<'_> {
fn serialize<S>(&self, serializer: S) -> core::result::Result<S::Ok, S::Error>
where
S: Serializer,
{
// Serialization is generic for all slice types, so the default serialization of byte
// slices does not use `Serializer::serialize_bytes`.
serializer.serialize_bytes(self.bytes)
}
}
impl<'a, 'de> Deserialize<'de> for ByteSliceStruct<'a>
where
'de: 'a,
{
fn deserialize<D>(deserializer: D) -> core::result::Result<Self, D::Error>
where
D: Deserializer<'de>,
{
// Deserialization of byte slices is specialized for byte slices, so the default
// deserialization will call `Deserializer::deserialize_bytes`.
Ok(Self {
bytes: Deserialize::deserialize(deserializer)?,
})
}
}
#[test]
fn bytes() {
let x: &[u8; 32] = &[0u8; 32];
let output: Vec<u8, 128> = to_vec(x).unwrap();
assert_eq!(output.len(), 32);
let out: [u8; 32] = from_bytes(output.deref()).unwrap();
assert_eq!(out, [0u8; 32]);
let x: &[u8] = &[0u8; 32];
let output: Vec<u8, 128> = to_vec(x).unwrap();
assert_eq!(output.len(), 33);
let out: &[u8] = from_bytes(output.deref()).unwrap();
assert_eq!(out, [0u8; 32]);
let x = ByteSliceStruct { bytes: &[0u8; 32] };
let output: Vec<u8, 128> = to_vec(&x).unwrap();
assert_eq!(output.len(), 33);
let out: ByteSliceStruct<'_> = from_bytes(output.deref()).unwrap();
assert_eq!(out, ByteSliceStruct { bytes: &[0u8; 32] });
}
#[test]
fn chars() {
let x: char = 'a';
let output: Vec<u8, 5> = to_vec(&x).unwrap();
assert_eq!(output.len(), 2);
let out: char = from_bytes(output.deref()).unwrap();
assert_eq!(out, 'a');
}
#[derive(Serialize, Deserialize, Debug, Eq, PartialEq)]
pub struct NewTypeStruct(u32);
#[derive(Serialize, Deserialize, Debug, Eq, PartialEq)]
pub struct TupleStruct((u8, u16));
#[derive(Serialize, Deserialize, Debug, Eq, PartialEq)]
pub struct DualTupleStruct(u8, u16);
#[test]
fn structs() {
let output: Vec<u8, 4> = to_vec(&NewTypeStruct(5)).unwrap();
assert_eq!(&[0x05], output.deref());
let out: NewTypeStruct = from_bytes(output.deref()).unwrap();
assert_eq!(out, NewTypeStruct(5));
let output: Vec<u8, 3> = to_vec(&TupleStruct((0xA0, 0x1234))).unwrap();
assert_eq!(&[0xA0, 0xB4, 0x24], output.deref());
let out: TupleStruct = from_bytes(output.deref()).unwrap();
assert_eq!(out, TupleStruct((0xA0, 0x1234)));
let output: Vec<u8, 3> = to_vec(&DualTupleStruct(0xA0, 0x1234)).unwrap();
assert_eq!(&[0xA0, 0xB4, 0x24], output.deref());
let out: DualTupleStruct = from_bytes(output.deref()).unwrap();
assert_eq!(out, DualTupleStruct(0xA0, 0x1234));
}
#[derive(Serialize, Deserialize, Debug, Eq, PartialEq)]
struct RefStruct<'a> {
bytes: &'a [u8],
str_s: &'a str,
}
#[test]
fn ref_struct() {
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,
}
);
}
#[test]
fn unit() {
let output: Vec<u8, 1> = to_vec(&()).unwrap();
assert_eq!(output.len(), 0);
let _: () = from_bytes(output.deref()).unwrap();
}
#[test]
fn heapless_data() {
let mut input: Vec<u8, 4> = Vec::new();
input.extend_from_slice(&[0x01, 0x02, 0x03, 0x04]).unwrap();
let output: Vec<u8, 5> = to_vec(&input).unwrap();
assert_eq!(&[0x04, 0x01, 0x02, 0x03, 0x04], output.deref());
let out: Vec<u8, 4> = from_bytes(output.deref()).unwrap();
assert_eq!(out, input);
let mut input: String<8> = String::new();
write!(&mut input, "helLO!").unwrap();
let output: Vec<u8, 7> = to_vec(&input).unwrap();
assert_eq!(&[0x06, b'h', b'e', b'l', b'L', b'O', b'!'], output.deref());
let out: String<8> = from_bytes(output.deref()).unwrap();
assert_eq!(input, out);
let mut input: FnvIndexMap<u8, u8, 4> = FnvIndexMap::new();
input.insert(0x01, 0x05).unwrap();
input.insert(0x02, 0x06).unwrap();
input.insert(0x03, 0x07).unwrap();
input.insert(0x04, 0x08).unwrap();
let output: Vec<u8, 100> = to_vec(&input).unwrap();
assert_eq!(
&[0x04, 0x01, 0x05, 0x02, 0x06, 0x03, 0x07, 0x04, 0x08],
output.deref()
);
let out: FnvIndexMap<u8, u8, 4> = from_bytes(output.deref()).unwrap();
assert_eq!(input, out);
}
#[test]
fn cobs_test() {
let message = "hElLo";
let bytes = [0x01, 0x00, 0x02, 0x20];
let input = RefStruct {
bytes: &bytes,
str_s: message,
};
let output: Vec<u8, 11> = to_vec(&input).unwrap();
let mut encode_buf = [0u8; 32];
let sz = cobs::encode(output.deref(), &mut encode_buf);
let out = from_bytes_cobs::<RefStruct<'_>>(&mut encode_buf[..sz]).unwrap();
assert_eq!(input, out);
}
#[test]
fn take_from_includes_terminator() {
// With the null terminator
let mut output: Vec<u8, 32> = to_vec_cobs(&(4i32, 0u8, 4u64)).unwrap();
let (val, remain) = take_from_bytes_cobs::<(i32, u8, u64)>(&mut output).unwrap();
assert_eq!((4, 0, 4), val);
assert_eq!(remain.len(), 0);
// without the null terminator
let mut output: Vec<u8, 32> = to_vec_cobs(&(4i32, 0u8, 4u64)).unwrap();
assert_eq!(output.pop(), Some(0));
let (val, remain) = take_from_bytes_cobs::<(i32, u8, u64)>(&mut output).unwrap();
assert_eq!((4, 0, 4), val);
assert_eq!(remain.len(), 0);
}
}
#[cfg(any(feature = "alloc", feature = "use-std"))]
#[cfg(test)]
mod test_alloc {
extern crate alloc;
use super::*;
use alloc::vec;
use serde::Deserialize;
#[derive(Debug, Deserialize, PartialEq)]
struct ZSTStruct;
#[test]
fn zst_vec() {
assert_eq!(from_bytes(&[3]), Ok(vec![ZSTStruct, ZSTStruct, ZSTStruct]));
assert_eq!(
from_bytes(&[4]),
Ok(vec![ZSTStruct, ZSTStruct, ZSTStruct, ZSTStruct])
);
}
#[test]
fn vec() {
assert_eq!(
from_bytes::<Vec<u8>>(&[8, 255, 255, 255, 0, 0, 0, 0, 0]),
Ok(vec![255, 255, 255, 0, 0, 0, 0, 0])
);
// This won't actually prove anything since tests will likely always be
// run on devices with larger amounts of memory, but it can't hurt.
assert_eq!(
from_bytes::<Vec<u8>>(&[(1 << 7) | 8, 255, 255, 255, 0, 0, 0, 0, 0]),
Err(Error::DeserializeUnexpectedEnd)
);
}
}
+20
View File
@@ -0,0 +1,20 @@
// We disable all embedded-io versions but the most recent in docs.rs, because we use
// --all-features which doesn't work with non-additive features.
#[cfg(all(feature = "embedded-io-04", feature = "embedded-io-06", not(docsrs)))]
compile_error!("Only one version of `embedded-io` must be enabled through features");
#[cfg(all(feature = "embedded-io-04", not(docsrs)))]
mod version_impl {
pub use embedded_io_04 as embedded_io;
pub use embedded_io_04::blocking::{Read, Write};
}
#[cfg(feature = "embedded-io-06")]
mod version_impl {
pub use embedded_io_06 as embedded_io;
pub use embedded_io_06::{Read, Write};
}
// All versions should export the appropriate items
#[cfg(any(feature = "embedded-io-04", feature = "embedded-io-06"))]
pub use version_impl::{embedded_io, Read, Write};
+96
View File
@@ -0,0 +1,96 @@
use core::fmt::{Display, Formatter};
use serde::{Deserialize, Serialize};
/// This is the error type used by Postcard
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
#[cfg_attr(feature = "use-defmt", derive(defmt::Format))]
#[non_exhaustive]
pub enum Error {
/// This is a feature that postcard will never implement
WontImplement,
/// This is a feature that postcard intends to support, but does not yet
NotYetImplemented,
/// The serialize buffer is full
SerializeBufferFull,
/// The length of a sequence must be known
SerializeSeqLengthUnknown,
/// Hit the end of buffer, expected more data
DeserializeUnexpectedEnd,
/// Found a varint that didn't terminate. Is the usize too big for this platform?
DeserializeBadVarint,
/// Found a bool that wasn't 0 or 1
DeserializeBadBool,
/// Found an invalid unicode char
DeserializeBadChar,
/// Tried to parse invalid utf-8
DeserializeBadUtf8,
/// Found an Option discriminant that wasn't 0 or 1
DeserializeBadOption,
/// Found an enum discriminant that was > `u32::MAX`
DeserializeBadEnum,
/// The original data was not well encoded
DeserializeBadEncoding,
/// Bad CRC while deserializing
DeserializeBadCrc,
/// Serde Serialization Error
SerdeSerCustom,
/// Serde Deserialization Error
SerdeDeCustom,
/// Error while processing `collect_str` during serialization
CollectStrError,
}
impl Display for Error {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
use Error::*;
write!(
f,
"{}",
match self {
WontImplement => "This is a feature that PostCard will never implement",
NotYetImplemented => {
"This is a feature that Postcard intends to support, but does not yet"
}
SerializeBufferFull => "The serialize buffer is full",
SerializeSeqLengthUnknown => "The length of a sequence must be known",
DeserializeUnexpectedEnd => "Hit the end of buffer, expected more data",
DeserializeBadVarint => {
"Found a varint that didn't terminate. Is the usize too big for this platform?"
}
DeserializeBadBool => "Found a bool that wasn't 0 or 1",
DeserializeBadChar => "Found an invalid unicode char",
DeserializeBadUtf8 => "Tried to parse invalid utf-8",
DeserializeBadOption => "Found an Option discriminant that wasn't 0 or 1",
DeserializeBadEnum => "Found an enum discriminant that was > u32::max_value()",
DeserializeBadEncoding => "The original data was not well encoded",
DeserializeBadCrc => "Bad CRC while deserializing",
SerdeSerCustom => "Serde Serialization Error",
SerdeDeCustom => "Serde Deserialization Error",
CollectStrError => "Error while processing `collect_str` during serialization",
}
)
}
}
/// This is the Result type used by Postcard.
pub type Result<T> = ::core::result::Result<T, Error>;
impl serde::ser::Error for Error {
fn custom<T>(_msg: T) -> Self
where
T: Display,
{
Error::SerdeSerCustom
}
}
impl serde::de::Error for Error {
fn custom<T>(_msg: T) -> Self
where
T: Display,
{
Error::SerdeDeCustom
}
}
impl serde::ser::StdError for Error {}
+191
View File
@@ -0,0 +1,191 @@
//! # Fixed Size Integers
//!
//! In some cases, the use of variably length encoded data may not be
//! preferable. These modules, for use with `#[serde(with = ...)]`
//! "opt out" of variable length encoding.
//!
//! Support explicitly not provided for `usize` or `isize`, as
//! these types would not be portable between systems of different
//! pointer widths.
//!
//! Although all data in Postcard is typically encoded in little-endian
//! order, these modules provide a choice to the user to encode the data
//! in either little or big endian form, which may be useful for zero-copy
//! applications.
use serde::{Deserialize, Serialize, Serializer};
/// Use with the `#[serde(with = "postcard::fixint::le")]` field attribute.
///
/// Disables varint serialization/deserialization for the specified integer
/// field. The integer will always be serialized in the same way as a fixed
/// size array, in **Little Endian** order on the wire.
///
/// ```rust
/// # use serde::Serialize;
/// #[derive(Serialize)]
/// pub struct DefinitelyLittleEndian {
/// #[serde(with = "postcard::fixint::le")]
/// x: u16,
/// }
/// ```
pub mod le {
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use super::LE;
/// Serialize the integer value as a little-endian fixed-size array.
pub fn serialize<S, T>(val: &T, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
T: Copy,
LE<T>: Serialize,
{
LE(*val).serialize(serializer)
}
/// Deserialize the integer value from a little-endian fixed-size array.
pub fn deserialize<'de, D, T>(deserializer: D) -> Result<T, D::Error>
where
D: Deserializer<'de>,
LE<T>: Deserialize<'de>,
{
LE::<T>::deserialize(deserializer).map(|x| x.0)
}
}
/// Disables varint serialization/deserialization for the specified integer field.
///
/// Use with the `#[serde(with = "postcard::fixint::be")]` field attribute.
/// The integer will always be serialized in the same way as a fixed
/// size array, in **Big Endian** order on the wire.
///
/// ```rust
/// # use serde::Serialize;
/// #[derive(Serialize)]
/// pub struct DefinitelyBigEndian {
/// #[serde(with = "postcard::fixint::be")]
/// x: u16,
/// }
/// ```
pub mod be {
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use super::BE;
/// Serialize the integer value as a big-endian fixed-size array.
pub fn serialize<S, T>(val: &T, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
T: Copy,
BE<T>: Serialize,
{
BE(*val).serialize(serializer)
}
/// Deserialize the integer value from a big-endian fixed-size array.
pub fn deserialize<'de, D, T>(deserializer: D) -> Result<T, D::Error>
where
D: Deserializer<'de>,
BE<T>: Deserialize<'de>,
{
BE::<T>::deserialize(deserializer).map(|x| x.0)
}
}
#[doc(hidden)]
pub struct LE<T>(T);
#[doc(hidden)]
pub struct BE<T>(T);
macro_rules! impl_fixint {
($( $int:ty ),*) => {
$(
impl Serialize for LE<$int> {
#[inline]
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
self.0.to_le_bytes().serialize(serializer)
}
}
impl<'de> Deserialize<'de> for LE<$int> {
#[inline]
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
<_ as Deserialize>::deserialize(deserializer)
.map(<$int>::from_le_bytes)
.map(Self)
}
}
impl Serialize for BE<$int> {
#[inline]
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
self.0.to_be_bytes().serialize(serializer)
}
}
impl<'de> Deserialize<'de> for BE<$int> {
#[inline]
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
<_ as Deserialize>::deserialize(deserializer)
.map(<$int>::from_be_bytes)
.map(Self)
}
}
)*
};
}
impl_fixint![i16, i32, i64, i128, u16, u32, u64, u128];
#[cfg(test)]
mod tests {
use serde::{Deserialize, Serialize};
#[test]
fn test_little_endian() {
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
pub struct DefinitelyLE {
#[serde(with = "crate::fixint::le")]
x: u16,
}
let input = DefinitelyLE { x: 0xABCD };
let mut buf = [0; 32];
let serialized = crate::to_slice(&input, &mut buf).unwrap();
assert_eq!(serialized, &[0xCD, 0xAB]);
let deserialized: DefinitelyLE = crate::from_bytes(serialized).unwrap();
assert_eq!(deserialized, input);
}
#[test]
fn test_big_endian() {
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
pub struct DefinitelyBE {
#[serde(with = "crate::fixint::be")]
x: u16,
}
let input = DefinitelyBE { x: 0xABCD };
let mut buf = [0; 32];
let serialized = crate::to_slice(&input, &mut buf).unwrap();
assert_eq!(serialized, &[0xAB, 0xCD]);
let deserialized: DefinitelyBE = crate::from_bytes(serialized).unwrap();
assert_eq!(deserialized, input);
}
}
+125
View File
@@ -0,0 +1,125 @@
#![cfg_attr(not(any(test, feature = "use-std")), no_std)]
#![warn(missing_docs)]
#![cfg_attr(not(doctest), doc = include_str!("../README.md"))]
#![cfg_attr(docsrs, feature(doc_cfg))]
pub mod accumulator;
mod de;
mod eio;
mod error;
pub mod fixint;
mod ser;
mod varint;
// Still experimental! Don't make pub pub.
pub(crate) mod max_size;
/// # Experimental Postcard Features
///
/// Items inside this module require various feature flags, and are not
/// subject to SemVer stability. Items may be removed or deprecated at
/// any point.
///
/// ## Derive
///
/// The `experimental-derive` feature enables one experimental feature:
///
/// * Max size calculation
///
/// ### Max Size Calculation
///
/// This features enables calculation of the Max serialized size of a message as
/// an associated `usize` constant called `POSTCARD_MAX_SIZE`. It also provides a
/// `#[derive(MaxSize)]` macro that can be used for calculating user types.
///
/// This is useful for determining the maximum buffer size needed when receiving
/// or sending a message that has been serialized.
///
/// NOTE: This only covers the size of "plain" flavored messages, e.g. not with COBS
/// or any other Flavors applied. The overhead for these flavors must be calculated
/// separately.
///
/// Please report any missing types, or any incorrectly calculated values.
///
/// ### Message Schema Generation
///
/// This now lives in the `postcard-schema` crate.
pub mod experimental {
/// Compile time max-serialization size calculation
#[cfg(feature = "experimental-derive")]
#[cfg_attr(docsrs, doc(cfg(feature = "experimental-derive")))]
pub mod max_size {
// NOTE: This is the trait...
pub use crate::max_size::MaxSize;
// NOTE: ...and this is the derive macro
pub use postcard_derive::MaxSize;
}
pub use crate::ser::serialized_size;
}
pub use de::deserializer::Deserializer;
pub use de::flavors as de_flavors;
pub use de::{from_bytes, from_bytes_cobs, take_from_bytes, take_from_bytes_cobs};
pub use error::{Error, Result};
pub use ser::flavors as ser_flavors;
pub use ser::{serialize_with_flavor, serializer::Serializer, to_extend, to_slice, to_slice_cobs};
#[cfg(feature = "heapless")]
pub use ser::{to_vec, to_vec_cobs};
#[cfg(any(feature = "embedded-io-04", feature = "embedded-io-06"))]
pub use ser::to_eio;
#[cfg(any(feature = "embedded-io-04", feature = "embedded-io-06"))]
pub use de::from_eio;
#[cfg(feature = "use-std")]
pub use ser::{to_io, to_stdvec, to_stdvec_cobs};
#[cfg(feature = "use-std")]
pub use de::from_io;
#[cfg(feature = "alloc")]
pub use ser::{to_allocvec, to_allocvec_cobs};
#[cfg(feature = "use-crc")]
pub use {
de::{from_bytes_crc32, take_from_bytes_crc32},
ser::to_slice_crc32,
};
#[cfg(all(feature = "use-crc", feature = "heapless"))]
pub use ser::to_vec_crc32;
#[cfg(all(feature = "use-crc", feature = "use-std"))]
pub use ser::to_stdvec_crc32;
#[cfg(all(feature = "use-crc", feature = "alloc"))]
pub use ser::to_allocvec_crc32;
#[cfg(test)]
mod test {
#[test]
fn varint_boundary_canon() {
let x = u32::MAX;
let mut buf = [0u8; 5];
let used = crate::to_slice(&x, &mut buf).unwrap();
let deser: u32 = crate::from_bytes(used).unwrap();
assert_eq!(deser, u32::MAX);
assert_eq!(used, &mut [0xFF, 0xFF, 0xFF, 0xFF, 0x0F]);
let deser: Result<u32, crate::Error> = crate::from_bytes(&[0xFF, 0xFF, 0xFF, 0xFF, 0x1F]);
assert_eq!(deser, Err(crate::Error::DeserializeBadVarint));
}
#[test]
fn signed_int128() {
let x = -19490127978232325886905073712831_i128;
let mut buf = [0u8; 32];
let used = crate::to_slice(&x, &mut buf).unwrap();
let deser: i128 = crate::from_bytes(used).unwrap();
assert_eq!(deser, x);
}
}
+354
View File
@@ -0,0 +1,354 @@
#[cfg(feature = "alloc")]
extern crate alloc;
#[cfg(feature = "alloc")]
use alloc::{boxed::Box, rc::Rc};
#[cfg(all(feature = "alloc", target_has_atomic = "ptr"))]
use alloc::sync::Arc;
use crate::varint::varint_max;
use core::{
marker::PhantomData,
num::{
NonZeroI128, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI8, NonZeroIsize, NonZeroU128,
NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU8, NonZeroUsize,
},
ops::{Range, RangeFrom, RangeInclusive, RangeTo},
time::Duration,
};
/// This trait is used to enforce the maximum size required to
/// store the serialization of a given type.
pub trait MaxSize {
/// The maximum possible size that the serialization of this
/// type can have, in bytes.
const POSTCARD_MAX_SIZE: usize;
}
impl MaxSize for bool {
const POSTCARD_MAX_SIZE: usize = 1;
}
impl MaxSize for i8 {
const POSTCARD_MAX_SIZE: usize = 1;
}
impl MaxSize for i16 {
const POSTCARD_MAX_SIZE: usize = varint_max::<Self>();
}
impl MaxSize for i32 {
const POSTCARD_MAX_SIZE: usize = varint_max::<Self>();
}
impl MaxSize for i64 {
const POSTCARD_MAX_SIZE: usize = varint_max::<Self>();
}
impl MaxSize for i128 {
const POSTCARD_MAX_SIZE: usize = varint_max::<Self>();
}
impl MaxSize for isize {
const POSTCARD_MAX_SIZE: usize = varint_max::<Self>();
}
impl MaxSize for u8 {
const POSTCARD_MAX_SIZE: usize = 1;
}
impl MaxSize for u16 {
const POSTCARD_MAX_SIZE: usize = varint_max::<Self>();
}
impl MaxSize for u32 {
const POSTCARD_MAX_SIZE: usize = varint_max::<Self>();
}
impl MaxSize for u64 {
const POSTCARD_MAX_SIZE: usize = varint_max::<Self>();
}
impl MaxSize for u128 {
const POSTCARD_MAX_SIZE: usize = varint_max::<Self>();
}
impl MaxSize for usize {
const POSTCARD_MAX_SIZE: usize = varint_max::<Self>();
}
impl MaxSize for f32 {
const POSTCARD_MAX_SIZE: usize = 4;
}
impl MaxSize for f64 {
const POSTCARD_MAX_SIZE: usize = 8;
}
impl MaxSize for char {
const POSTCARD_MAX_SIZE: usize = 5;
}
impl<T: MaxSize> MaxSize for Option<T> {
const POSTCARD_MAX_SIZE: usize = T::POSTCARD_MAX_SIZE + 1;
}
impl<T: MaxSize, E: MaxSize> MaxSize for Result<T, E> {
const POSTCARD_MAX_SIZE: usize = max(T::POSTCARD_MAX_SIZE, E::POSTCARD_MAX_SIZE) + 1;
}
impl MaxSize for () {
const POSTCARD_MAX_SIZE: usize = 0;
}
impl<T: MaxSize, const N: usize> MaxSize for [T; N] {
const POSTCARD_MAX_SIZE: usize = T::POSTCARD_MAX_SIZE * N;
}
impl<T: MaxSize> MaxSize for &'_ T {
const POSTCARD_MAX_SIZE: usize = T::POSTCARD_MAX_SIZE;
}
impl<T: MaxSize> MaxSize for &'_ mut T {
const POSTCARD_MAX_SIZE: usize = T::POSTCARD_MAX_SIZE;
}
impl MaxSize for NonZeroI8 {
const POSTCARD_MAX_SIZE: usize = i8::POSTCARD_MAX_SIZE;
}
impl MaxSize for NonZeroI16 {
const POSTCARD_MAX_SIZE: usize = i16::POSTCARD_MAX_SIZE;
}
impl MaxSize for NonZeroI32 {
const POSTCARD_MAX_SIZE: usize = i32::POSTCARD_MAX_SIZE;
}
impl MaxSize for NonZeroI64 {
const POSTCARD_MAX_SIZE: usize = i64::POSTCARD_MAX_SIZE;
}
impl MaxSize for NonZeroI128 {
const POSTCARD_MAX_SIZE: usize = i128::POSTCARD_MAX_SIZE;
}
impl MaxSize for NonZeroIsize {
const POSTCARD_MAX_SIZE: usize = isize::POSTCARD_MAX_SIZE;
}
impl MaxSize for NonZeroU8 {
const POSTCARD_MAX_SIZE: usize = u8::POSTCARD_MAX_SIZE;
}
impl MaxSize for NonZeroU16 {
const POSTCARD_MAX_SIZE: usize = u16::POSTCARD_MAX_SIZE;
}
impl MaxSize for NonZeroU32 {
const POSTCARD_MAX_SIZE: usize = u32::POSTCARD_MAX_SIZE;
}
impl MaxSize for NonZeroU64 {
const POSTCARD_MAX_SIZE: usize = u64::POSTCARD_MAX_SIZE;
}
impl MaxSize for NonZeroU128 {
const POSTCARD_MAX_SIZE: usize = u128::POSTCARD_MAX_SIZE;
}
impl MaxSize for NonZeroUsize {
const POSTCARD_MAX_SIZE: usize = usize::POSTCARD_MAX_SIZE;
}
impl MaxSize for Duration {
const POSTCARD_MAX_SIZE: usize = u64::POSTCARD_MAX_SIZE + u32::POSTCARD_MAX_SIZE;
}
impl<T> MaxSize for PhantomData<T> {
const POSTCARD_MAX_SIZE: usize = 0;
}
impl<A: MaxSize> MaxSize for (A,) {
const POSTCARD_MAX_SIZE: usize = A::POSTCARD_MAX_SIZE;
}
impl<A: MaxSize, B: MaxSize> MaxSize for (A, B) {
const POSTCARD_MAX_SIZE: usize = A::POSTCARD_MAX_SIZE + B::POSTCARD_MAX_SIZE;
}
impl<A: MaxSize, B: MaxSize, C: MaxSize> MaxSize for (A, B, C) {
const POSTCARD_MAX_SIZE: usize =
A::POSTCARD_MAX_SIZE + B::POSTCARD_MAX_SIZE + C::POSTCARD_MAX_SIZE;
}
impl<A: MaxSize, B: MaxSize, C: MaxSize, D: MaxSize> MaxSize for (A, B, C, D) {
const POSTCARD_MAX_SIZE: usize =
A::POSTCARD_MAX_SIZE + B::POSTCARD_MAX_SIZE + C::POSTCARD_MAX_SIZE + D::POSTCARD_MAX_SIZE;
}
impl<A: MaxSize, B: MaxSize, C: MaxSize, D: MaxSize, E: MaxSize> MaxSize for (A, B, C, D, E) {
const POSTCARD_MAX_SIZE: usize = A::POSTCARD_MAX_SIZE
+ B::POSTCARD_MAX_SIZE
+ C::POSTCARD_MAX_SIZE
+ D::POSTCARD_MAX_SIZE
+ E::POSTCARD_MAX_SIZE;
}
impl<A: MaxSize, B: MaxSize, C: MaxSize, D: MaxSize, E: MaxSize, F: MaxSize> MaxSize
for (A, B, C, D, E, F)
{
const POSTCARD_MAX_SIZE: usize = A::POSTCARD_MAX_SIZE
+ B::POSTCARD_MAX_SIZE
+ C::POSTCARD_MAX_SIZE
+ D::POSTCARD_MAX_SIZE
+ E::POSTCARD_MAX_SIZE
+ F::POSTCARD_MAX_SIZE;
}
impl<T: MaxSize> MaxSize for Range<T> {
const POSTCARD_MAX_SIZE: usize = T::POSTCARD_MAX_SIZE * 2;
}
impl<T: MaxSize> MaxSize for RangeInclusive<T> {
const POSTCARD_MAX_SIZE: usize = T::POSTCARD_MAX_SIZE * 2;
}
impl<T: MaxSize> MaxSize for RangeFrom<T> {
const POSTCARD_MAX_SIZE: usize = T::POSTCARD_MAX_SIZE;
}
impl<T: MaxSize> MaxSize for RangeTo<T> {
const POSTCARD_MAX_SIZE: usize = T::POSTCARD_MAX_SIZE;
}
impl<T: MaxSize> MaxSize for core::num::Wrapping<T> {
const POSTCARD_MAX_SIZE: usize = T::POSTCARD_MAX_SIZE;
}
#[cfg(all(feature = "core-num-saturating", feature = "experimental-derive"))]
#[cfg_attr(docsrs, doc(cfg(feature = "core-num-saturating")))]
impl<T: MaxSize> MaxSize for core::num::Saturating<T> {
const POSTCARD_MAX_SIZE: usize = T::POSTCARD_MAX_SIZE;
}
#[cfg(feature = "alloc")]
#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
impl<T: MaxSize> MaxSize for Box<T> {
const POSTCARD_MAX_SIZE: usize = T::POSTCARD_MAX_SIZE;
}
#[cfg(all(feature = "alloc", target_has_atomic = "ptr"))]
#[cfg_attr(docsrs, doc(cfg(all(feature = "alloc", target_has_atomic = "ptr"))))]
impl<T: MaxSize> MaxSize for Arc<T> {
const POSTCARD_MAX_SIZE: usize = T::POSTCARD_MAX_SIZE;
}
#[cfg(feature = "alloc")]
#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
impl<T: MaxSize> MaxSize for Rc<T> {
const POSTCARD_MAX_SIZE: usize = T::POSTCARD_MAX_SIZE;
}
#[cfg(feature = "heapless")]
#[cfg_attr(docsrs, doc(cfg(feature = "heapless")))]
impl<T: MaxSize, const N: usize> MaxSize for heapless::Vec<T, N> {
const POSTCARD_MAX_SIZE: usize = <[T; N]>::POSTCARD_MAX_SIZE + varint_size(N);
}
#[cfg(feature = "heapless")]
#[cfg_attr(docsrs, doc(cfg(feature = "heapless")))]
impl<const N: usize> MaxSize for heapless::String<N> {
const POSTCARD_MAX_SIZE: usize = <[u8; N]>::POSTCARD_MAX_SIZE + varint_size(N);
}
#[cfg(all(feature = "nalgebra-v0_33", feature = "experimental-derive"))]
#[cfg_attr(docsrs, doc(cfg(feature = "nalgebra-v0_33")))]
impl<T, const R: usize, const C: usize> MaxSize
for nalgebra_v0_33::Matrix<
T,
nalgebra_v0_33::Const<R>,
nalgebra_v0_33::Const<C>,
nalgebra_v0_33::ArrayStorage<T, R, C>,
>
where
T: MaxSize + nalgebra_v0_33::Scalar,
{
const POSTCARD_MAX_SIZE: usize = T::POSTCARD_MAX_SIZE * R * C;
}
#[cfg(all(feature = "nalgebra-v0_33", feature = "experimental-derive"))]
#[cfg_attr(docsrs, doc(cfg(feature = "nalgebra-v0_33")))]
impl<T: MaxSize> MaxSize for nalgebra_v0_33::Unit<T> {
const POSTCARD_MAX_SIZE: usize = T::POSTCARD_MAX_SIZE;
}
#[cfg(all(feature = "nalgebra-v0_33", feature = "experimental-derive"))]
#[cfg_attr(docsrs, doc(cfg(feature = "nalgebra-v0_33")))]
impl<T: MaxSize + nalgebra_v0_33::Scalar> MaxSize for nalgebra_v0_33::Quaternion<T> {
const POSTCARD_MAX_SIZE: usize = nalgebra_v0_33::Vector4::<T>::POSTCARD_MAX_SIZE;
}
#[cfg(feature = "heapless")]
const fn varint_size(max_n: usize) -> usize {
const BITS_PER_BYTE: usize = 8;
const BITS_PER_VARINT_BYTE: usize = 7;
if max_n == 0 {
return 1;
}
// How many data bits do we need for `max_n`.
let bits = core::mem::size_of::<usize>() * BITS_PER_BYTE - max_n.leading_zeros() as usize;
// We add (BITS_PER_BYTE - 1), to ensure any integer divisions
// with a remainder will always add exactly one full byte, but
// an evenly divided number of bits will be the same
let roundup_bits = bits + (BITS_PER_VARINT_BYTE - 1);
// Apply division, using normal "round down" integer division
roundup_bits / BITS_PER_VARINT_BYTE
}
const fn max(lhs: usize, rhs: usize) -> usize {
if lhs > rhs {
lhs
} else {
rhs
}
}
#[cfg(any(feature = "alloc", feature = "use-std"))]
#[cfg(test)]
mod tests {
extern crate alloc;
use super::*;
use alloc::rc::Rc;
#[cfg(target_has_atomic = "ptr")]
use alloc::sync::Arc;
#[test]
fn box_max_size() {
assert_eq!(Box::<u8>::POSTCARD_MAX_SIZE, 1);
assert_eq!(Box::<u32>::POSTCARD_MAX_SIZE, 5);
assert_eq!(Box::<(u128, [u8; 8])>::POSTCARD_MAX_SIZE, 27);
}
#[test]
#[cfg(target_has_atomic = "ptr")]
fn arc_max_size() {
assert_eq!(Arc::<u8>::POSTCARD_MAX_SIZE, 1);
assert_eq!(Arc::<u32>::POSTCARD_MAX_SIZE, 5);
assert_eq!(Arc::<(u128, [u8; 8])>::POSTCARD_MAX_SIZE, 27);
}
#[test]
fn rc_max_size() {
assert_eq!(Rc::<u8>::POSTCARD_MAX_SIZE, 1);
assert_eq!(Rc::<u32>::POSTCARD_MAX_SIZE, 5);
assert_eq!(Rc::<(u128, [u8; 8])>::POSTCARD_MAX_SIZE, 27);
}
}
+729
View File
@@ -0,0 +1,729 @@
//! # Serialization Flavors
//!
//! "Flavors" in `postcard` are used as modifiers to the serialization or deserialization
//! process. Flavors typically modify one or both of the following:
//!
//! 1. The output medium of the serialization, e.g. whether the data is serialized to a `[u8]` slice, or a `heapless::Vec`.
//! 2. The format of the serialization, such as encoding the serialized output in a COBS format, performing CRC32 checksumming while serializing, etc.
//!
//! Flavors are implemented using the [`Flavor`] trait, which acts as a "middleware" for receiving the bytes as serialized by `serde`.
//! Multiple flavors may be combined to obtain a desired combination of behavior and storage.
//! When flavors are combined, it is expected that the storage flavor (such as `Slice` or `HVec`) is the innermost flavor.
//!
//! Custom flavors may be defined by users of the `postcard` crate, however some commonly useful flavors have been provided in
//! this module. If you think your custom flavor would be useful to others, PRs adding flavors are very welcome!
//!
//! ## Usability
//!
//! Flavors may not always be convenient to use directly, as they may expose some implementation details of how the
//! inner workings of the flavor behaves. It is typical to provide a convenience method for using a flavor, to prevent
//! the user from having to specify generic parameters, setting correct initialization values, or handling the output of
//! the flavor correctly. See `postcard::to_vec()` for an example of this.
//!
//! It is recommended to use the [`serialize_with_flavor()`](../fn.serialize_with_flavor.html) method for serialization. See it's documentation for information
//! regarding usage and generic type parameters.
//!
//! ## When to use (multiple) flavors
//!
//! Combining flavors are nice for convenience, as they perform potentially multiple steps of
//! serialization at one time.
//!
//! This can often be more memory efficient, as intermediate buffers are not typically required.
//!
//! ## When NOT to use (multiple) flavors
//!
//! The downside of passing serialization through multiple steps is that it is typically slower than
//! performing each step serially. Said simply, "cobs encoding while serializing" is often slower
//! than "serialize then cobs encode", due to the ability to handle longer "runs" of data in each
//! stage. The downside is that if these stages can not be performed in-place on the buffer, you
//! will need additional buffers for each stage.
//!
//! ## Examples
//!
//! ### Using a single flavor
//!
//! In the first example, we use the `Slice` flavor, to store the serialized output into a mutable `[u8]` slice.
//! No other modification is made to the serialization process.
//!
//! ```rust
//! use postcard::{
//! serialize_with_flavor,
//! ser_flavors::Slice,
//! };
//!
//! let mut buf = [0u8; 32];
//!
//! let data: &[u8] = &[0x01, 0x00, 0x20, 0x30];
//! let buffer = &mut [0u8; 32];
//! let res = serialize_with_flavor::<[u8], Slice, &mut [u8]>(
//! data,
//! Slice::new(buffer)
//! ).unwrap();
//!
//! assert_eq!(res, &[0x04, 0x01, 0x00, 0x20, 0x30]);
//! ```
//!
//! ### Using combined flavors
//!
//! In the second example, we mix `Slice` with `Cobs`, to cobs encode the output while
//! the data is serialized. Notice how `Slice` (the storage flavor) is the innermost flavor used.
//!
//! ```rust
//! 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]);
//! ```
use crate::error::{Error, Result};
use cobs::{EncoderState, PushResult};
use core::marker::PhantomData;
use core::ops::Index;
use core::ops::IndexMut;
#[cfg(feature = "heapless")]
pub use heapless_vec::*;
#[cfg(feature = "use-std")]
pub use std_vec::*;
#[cfg(feature = "alloc")]
pub use alloc_vec::*;
#[cfg(feature = "alloc")]
extern crate alloc;
/// The serialization Flavor trait
///
/// This is used as the primary way to encode serialized data into some kind of buffer,
/// or modify that data in a middleware style pattern.
///
/// See the module level docs for an example of how flavors are used.
pub trait Flavor {
/// The `Output` type is what this storage "resolves" to when the serialization is complete,
/// such as a slice or a Vec of some sort.
type Output;
/// Override this method when you want to customize processing
/// multiple bytes at once, such as copying a slice to the output,
/// rather than iterating over one byte at a time.
#[inline]
fn try_extend(&mut self, data: &[u8]) -> Result<()> {
data.iter().try_for_each(|d| self.try_push(*d))
}
/// Push a single byte to be modified and/or stored.
fn try_push(&mut self, data: u8) -> Result<()>;
/// Finalize the serialization process.
fn finalize(self) -> Result<Self::Output>;
}
////////////////////////////////////////
// Slice
////////////////////////////////////////
/// The `Slice` flavor is a storage flavor, storing the serialized (or otherwise modified) bytes into a plain
/// `[u8]` slice. The `Slice` flavor resolves into a sub-slice of the original slice buffer.
pub struct Slice<'a> {
start: *mut u8,
cursor: *mut u8,
end: *mut u8,
_pl: PhantomData<&'a [u8]>,
}
impl<'a> Slice<'a> {
/// Create a new `Slice` flavor from a given backing buffer
pub fn new(buf: &'a mut [u8]) -> Self {
let ptr = buf.as_mut_ptr_range();
Slice {
start: ptr.start,
cursor: ptr.start,
end: ptr.end,
_pl: PhantomData,
}
}
}
impl<'a> Flavor for Slice<'a> {
type Output = &'a mut [u8];
#[inline(always)]
fn try_push(&mut self, b: u8) -> Result<()> {
if self.cursor == self.end {
Err(Error::SerializeBufferFull)
} else {
// SAFETY: `self.cursor` is in-bounds and won't be incremented past `self.end` as we
// have checked above.
unsafe {
self.cursor.write(b);
self.cursor = self.cursor.add(1);
}
Ok(())
}
}
#[inline(always)]
fn try_extend(&mut self, b: &[u8]) -> Result<()> {
let remain = (self.end as usize) - (self.cursor as usize);
let blen = b.len();
if blen > remain {
Err(Error::SerializeBufferFull)
} else {
// SAFETY: `self.cursor` is in-bounds for `blen` elements and won't be incremented past
// `self.end` as we have checked above.
unsafe {
core::ptr::copy_nonoverlapping(b.as_ptr(), self.cursor, blen);
self.cursor = self.cursor.add(blen);
}
Ok(())
}
}
fn finalize(self) -> Result<Self::Output> {
let used = (self.cursor as usize) - (self.start as usize);
// SAFETY: `self.cursor` is in-bounds for `used` elements
let sli = unsafe { core::slice::from_raw_parts_mut(self.start, used) };
Ok(sli)
}
}
impl Index<usize> for Slice<'_> {
type Output = u8;
fn index(&self, idx: usize) -> &u8 {
let len = (self.end as usize) - (self.start as usize);
assert!(idx < len);
// SAFETY: `self.start` is in-bounds at `idx`
unsafe { &*self.start.add(idx) }
}
}
impl IndexMut<usize> for Slice<'_> {
fn index_mut(&mut self, idx: usize) -> &mut u8 {
let len = (self.end as usize) - (self.start as usize);
assert!(idx < len);
// SAFETY: `self.start` is in-bounds at `idx`
unsafe { &mut *self.start.add(idx) }
}
}
/// Wrapper over a [`core::iter::Extend<u8>`] that implements the flavor trait
pub struct ExtendFlavor<T> {
iter: T,
}
impl<T> ExtendFlavor<T>
where
T: core::iter::Extend<u8>,
{
/// Create a new [`Self`] flavor from a given [`core::iter::Extend<u8>`]
pub fn new(iter: T) -> Self {
Self { iter }
}
}
impl<T> Flavor for ExtendFlavor<T>
where
T: core::iter::Extend<u8>,
{
type Output = T;
#[inline(always)]
fn try_push(&mut self, data: u8) -> Result<()> {
self.iter.extend([data]);
Ok(())
}
#[inline(always)]
fn try_extend(&mut self, b: &[u8]) -> Result<()> {
self.iter.extend(b.iter().copied());
Ok(())
}
fn finalize(self) -> Result<Self::Output> {
Ok(self.iter)
}
}
/// Support for the [`embedded-io`](crate::eio::embedded_io) traits
#[cfg(any(feature = "embedded-io-04", feature = "embedded-io-06"))]
pub mod eio {
use super::Flavor;
use crate::{Error, Result};
/// Wrapper over a [`embedded_io Write`](crate::eio::Write) that implements the flavor trait
pub struct WriteFlavor<T> {
writer: T,
}
impl<T> WriteFlavor<T>
where
T: crate::eio::Write,
{
/// Create a new [`Self`] flavor from a given [`embedded_io Write`](crate::eio::Write)
pub fn new(writer: T) -> Self {
Self { writer }
}
}
impl<T> Flavor for WriteFlavor<T>
where
T: crate::eio::Write,
{
type Output = T;
#[inline(always)]
fn try_push(&mut self, data: u8) -> Result<()> {
self.writer
.write_all(&[data])
.map_err(|_| Error::SerializeBufferFull)?;
Ok(())
}
#[inline(always)]
fn try_extend(&mut self, b: &[u8]) -> Result<()> {
self.writer
.write_all(b)
.map_err(|_| Error::SerializeBufferFull)?;
Ok(())
}
fn finalize(mut self) -> Result<Self::Output> {
self.writer
.flush()
.map_err(|_| Error::SerializeBufferFull)?;
Ok(self.writer)
}
}
}
/// Support for the [`std::io`] traits
#[cfg(feature = "use-std")]
pub mod io {
use super::Flavor;
use crate::{Error, Result};
/// Wrapper over a [`std::io::Write`] that implements the flavor trait
pub struct WriteFlavor<T> {
writer: T,
}
impl<T> WriteFlavor<T>
where
T: std::io::Write,
{
/// Create a new [`Self`] flavor from a given [`std::io::Write`]
pub fn new(writer: T) -> Self {
Self { writer }
}
}
impl<T> Flavor for WriteFlavor<T>
where
T: std::io::Write,
{
type Output = T;
#[inline(always)]
fn try_push(&mut self, data: u8) -> Result<()> {
self.writer
.write_all(&[data])
.map_err(|_| Error::SerializeBufferFull)?;
Ok(())
}
#[inline(always)]
fn try_extend(&mut self, b: &[u8]) -> Result<()> {
self.writer
.write_all(b)
.map_err(|_| Error::SerializeBufferFull)?;
Ok(())
}
fn finalize(mut self) -> Result<Self::Output> {
self.writer
.flush()
.map_err(|_| Error::SerializeBufferFull)?;
Ok(self.writer)
}
}
}
#[cfg(feature = "heapless")]
mod heapless_vec {
use super::Flavor;
use super::Index;
use super::IndexMut;
use crate::{Error, Result};
use heapless::Vec;
////////////////////////////////////////
// HVec
////////////////////////////////////////
/// The `HVec` flavor is a wrapper type around a `heapless::Vec`. This is a stack
/// allocated data structure, with a fixed maximum size and variable amount of contents.
#[derive(Default)]
pub struct HVec<const B: usize> {
/// the contained data buffer
vec: Vec<u8, B>,
}
impl<const B: usize> HVec<B> {
/// Create a new, currently empty, [`heapless::Vec`] to be used for storing serialized
/// output data.
pub fn new() -> Self {
Self::default()
}
}
impl<const B: usize> Flavor for HVec<B> {
type Output = Vec<u8, B>;
#[inline(always)]
fn try_extend(&mut self, data: &[u8]) -> Result<()> {
self.vec
.extend_from_slice(data)
.map_err(|_| Error::SerializeBufferFull)
}
#[inline(always)]
fn try_push(&mut self, data: u8) -> Result<()> {
self.vec.push(data).map_err(|_| Error::SerializeBufferFull)
}
fn finalize(self) -> Result<Vec<u8, B>> {
Ok(self.vec)
}
}
impl<const B: usize> Index<usize> for HVec<B> {
type Output = u8;
fn index(&self, idx: usize) -> &u8 {
&self.vec[idx]
}
}
impl<const B: usize> IndexMut<usize> for HVec<B> {
fn index_mut(&mut self, idx: usize) -> &mut u8 {
&mut self.vec[idx]
}
}
}
#[cfg(feature = "use-std")]
mod std_vec {
/// The `StdVec` flavor is a wrapper type around a `std::vec::Vec`.
///
/// This type is only available when the (non-default) `use-std` feature is active
pub type StdVec = super::alloc_vec::AllocVec;
}
#[cfg(feature = "alloc")]
mod alloc_vec {
extern crate alloc;
use super::Flavor;
use super::Index;
use super::IndexMut;
use crate::Result;
use alloc::vec::Vec;
/// The `AllocVec` flavor is a wrapper type around an [`alloc::vec::Vec`].
///
/// This type is only available when the (non-default) `alloc` feature is active
#[derive(Default)]
pub struct AllocVec {
/// The vec to be used for serialization
vec: Vec<u8>,
}
impl AllocVec {
/// Create a new, currently empty, [`alloc::vec::Vec`] to be used for storing serialized
/// output data.
pub fn new() -> Self {
Self::default()
}
}
impl Flavor for AllocVec {
type Output = Vec<u8>;
#[inline(always)]
fn try_extend(&mut self, data: &[u8]) -> Result<()> {
self.vec.extend_from_slice(data);
Ok(())
}
#[inline(always)]
fn try_push(&mut self, data: u8) -> Result<()> {
self.vec.push(data);
Ok(())
}
fn finalize(self) -> Result<Self::Output> {
Ok(self.vec)
}
}
impl Index<usize> for AllocVec {
type Output = u8;
#[inline]
fn index(&self, idx: usize) -> &u8 {
&self.vec[idx]
}
}
impl IndexMut<usize> for AllocVec {
#[inline]
fn index_mut(&mut self, idx: usize) -> &mut u8 {
&mut self.vec[idx]
}
}
}
////////////////////////////////////////////////////////////////////////////////
// Modification Flavors
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////
// COBS
////////////////////////////////////////
/// The `Cobs` flavor implements [Consistent Overhead Byte Stuffing] on
/// the serialized data. The output of this flavor includes the termination/sentinel
/// byte of `0x00`.
///
/// This protocol is useful when sending data over a serial interface without framing such as a UART
///
/// [Consistent Overhead Byte Stuffing]: https://en.wikipedia.org/wiki/Consistent_Overhead_Byte_Stuffing
pub struct Cobs<B>
where
B: Flavor + IndexMut<usize, Output = u8>,
{
flav: B,
cobs: EncoderState,
}
impl<B> Cobs<B>
where
B: Flavor + IndexMut<usize, Output = u8>,
{
/// Create a new Cobs modifier Flavor. If there is insufficient space
/// to push the leading header byte, the method will return an Error
pub fn try_new(mut bee: B) -> Result<Self> {
bee.try_push(0).map_err(|_| Error::SerializeBufferFull)?;
Ok(Self {
flav: bee,
cobs: EncoderState::default(),
})
}
}
impl<B> Flavor for Cobs<B>
where
B: Flavor + IndexMut<usize, Output = u8>,
{
type Output = <B as Flavor>::Output;
#[inline(always)]
fn try_push(&mut self, data: u8) -> Result<()> {
use PushResult::*;
match self.cobs.push(data) {
AddSingle(n) => self.flav.try_push(n),
ModifyFromStartAndSkip((idx, mval)) => {
self.flav[idx] = mval;
self.flav.try_push(0)
}
ModifyFromStartAndPushAndSkip((idx, mval, nval)) => {
self.flav[idx] = mval;
self.flav.try_push(nval)?;
self.flav.try_push(0)
}
}
}
fn finalize(mut self) -> Result<Self::Output> {
let (idx, mval) = self.cobs.finalize();
self.flav[idx] = mval;
self.flav.try_push(0)?;
self.flav.finalize()
}
}
////////////////////////////////////////
// CRC
////////////////////////////////////////
/// This Cyclic Redundancy Check flavor applies [the CRC crate's `Algorithm`](https://docs.rs/crc/latest/crc/struct.Algorithm.html) struct on
/// the serialized data.
///
/// The output of this flavor receives the CRC appended to the bytes.
/// CRCs are used for error detection when reading data back.
/// Requires the `crc` feature.
///
/// More on CRCs: <https://en.wikipedia.org/wiki/Cyclic_redundancy_check>.
#[cfg(feature = "use-crc")]
#[cfg_attr(docsrs, doc(cfg(feature = "use-crc")))]
pub mod crc {
use crc::Digest;
use crc::Width;
use serde::Serialize;
#[cfg(feature = "alloc")]
use super::alloc;
use super::Flavor;
use super::Slice;
use crate::serialize_with_flavor;
use crate::Result;
/// Manages CRC modifications as a flavor.
pub struct CrcModifier<'a, B, W>
where
B: Flavor,
W: Width,
{
flav: B,
digest: Digest<'a, W>,
}
impl<'a, B, W> CrcModifier<'a, B, W>
where
B: Flavor,
W: Width,
{
/// Create a new CRC modifier Flavor.
pub fn new(bee: B, digest: Digest<'a, W>) -> Self {
Self { flav: bee, digest }
}
}
macro_rules! impl_flavor {
($int:ty, $to_slice:ident, $to_vec:ident, $to_allocvec:ident) => {
impl<'a, B> Flavor for CrcModifier<'a, B, $int>
where
B: Flavor,
{
type Output = <B as Flavor>::Output;
#[inline(always)]
fn try_push(&mut self, data: u8) -> Result<()> {
self.digest.update(&[data]);
self.flav.try_push(data)
}
fn finalize(mut self) -> Result<Self::Output> {
let crc = self.digest.finalize();
for byte in crc.to_le_bytes() {
self.flav.try_push(byte)?;
}
self.flav.finalize()
}
}
/// Serialize a `T` to the given slice, with the resulting slice containing
/// data followed by a CRC. The CRC bytes are included in the output buffer.
///
/// When successful, this function returns the slice containing the
/// serialized and encoded message.
pub fn $to_slice<'a, T>(
value: &T,
buf: &'a mut [u8],
digest: Digest<'_, $int>,
) -> Result<&'a mut [u8]>
where
T: Serialize + ?Sized,
{
serialize_with_flavor(value, CrcModifier::new(Slice::new(buf), digest))
}
/// Serialize a `T` to a `heapless::Vec<u8>`, with the `Vec` containing
/// data followed by a CRC. The CRC bytes are included in the output `Vec`.
#[cfg(feature = "heapless")]
#[cfg_attr(docsrs, doc(cfg(feature = "heapless")))]
pub fn $to_vec<T, const B: usize>(
value: &T,
digest: Digest<'_, $int>,
) -> Result<heapless::Vec<u8, B>>
where
T: Serialize + ?Sized,
{
use super::HVec;
serialize_with_flavor(value, CrcModifier::new(HVec::default(), digest))
}
/// Serialize a `T` to a `heapless::Vec<u8>`, with the `Vec` containing
/// data followed by a CRC. The CRC bytes are included in the output `Vec`.
#[cfg(feature = "alloc")]
#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
pub fn $to_allocvec<T>(
value: &T,
digest: Digest<'_, $int>,
) -> Result<alloc::vec::Vec<u8>>
where
T: Serialize + ?Sized,
{
use super::AllocVec;
serialize_with_flavor(value, CrcModifier::new(AllocVec::new(), digest))
}
};
}
impl_flavor!(u8, to_slice_u8, to_vec_u8, to_allocvec_u8);
impl_flavor!(u16, to_slice_u16, to_vec_u16, to_allocvec_u16);
impl_flavor!(u32, to_slice_u32, to_vec_u32, to_allocvec_u32);
impl_flavor!(u64, to_slice_u64, to_vec_u64, to_allocvec_u64);
impl_flavor!(u128, to_slice_u128, to_vec_u128, to_allocvec_u128);
}
/// The `Size` flavor is a measurement flavor, which accumulates the number of bytes needed to
/// serialize the data.
///
/// ```
/// use postcard::{serialize_with_flavor, ser_flavors};
///
/// let value = false;
/// let size = serialize_with_flavor(&value, ser_flavors::Size::default()).unwrap();
///
/// assert_eq!(size, 1);
/// ```
#[derive(Default)]
pub struct Size {
size: usize,
}
impl Flavor for Size {
type Output = usize;
#[inline(always)]
fn try_push(&mut self, _b: u8) -> Result<()> {
self.size += 1;
Ok(())
}
#[inline(always)]
fn try_extend(&mut self, b: &[u8]) -> Result<()> {
self.size += b.len();
Ok(())
}
fn finalize(self) -> Result<Self::Output> {
Ok(self.size)
}
}
+863
View File
@@ -0,0 +1,863 @@
use crate::error::{Error, Result};
use crate::ser::flavors::{Cobs, Flavor, Slice};
use serde::Serialize;
#[cfg(feature = "heapless")]
use crate::ser::flavors::HVec;
#[cfg(feature = "heapless")]
use heapless::Vec;
#[cfg(feature = "alloc")]
use crate::ser::flavors::AllocVec;
#[cfg(feature = "alloc")]
extern crate alloc;
use crate::ser::serializer::Serializer;
pub mod flavors;
pub(crate) mod serializer;
/// 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.
///
/// When successful, this function returns the slice containing the
/// serialized and encoded message.
///
/// ## Example
///
/// ```rust
/// use postcard::to_slice_cobs;
/// let mut buf = [0u8; 32];
///
/// let used = to_slice_cobs(&false, &mut buf).unwrap();
/// assert_eq!(used, &[0x01, 0x01, 0x00]);
///
/// let used = to_slice_cobs("1", &mut buf).unwrap();
/// assert_eq!(used, &[0x03, 0x01, b'1', 0x00]);
///
/// let used = to_slice_cobs("Hi!", &mut buf).unwrap();
/// assert_eq!(used, &[0x05, 0x03, b'H', b'i', b'!', 0x00]);
///
/// let data: &[u8] = &[0x01u8, 0x00, 0x20, 0x30];
/// let used = to_slice_cobs(data, &mut buf).unwrap();
/// assert_eq!(used, &[0x03, 0x04, 0x01, 0x03, 0x20, 0x30, 0x00]);
/// ```
pub fn to_slice_cobs<'a, 'b, T>(value: &'b T, buf: &'a mut [u8]) -> Result<&'a mut [u8]>
where
T: Serialize + ?Sized,
{
serialize_with_flavor::<T, Cobs<Slice<'a>>, &'a mut [u8]>(
value,
Cobs::try_new(Slice::new(buf))?,
)
}
/// Serialize a `T` to the given slice, with the resulting slice containing
/// data in a serialized format.
///
/// When successful, this function returns the slice containing the
/// serialized message
///
/// ## Example
///
/// ```rust
/// use postcard::to_slice;
/// let mut buf = [0u8; 32];
///
/// let used = to_slice(&true, &mut buf).unwrap();
/// assert_eq!(used, &[0x01]);
///
/// let used = to_slice("Hi!", &mut buf).unwrap();
/// assert_eq!(used, &[0x03, b'H', b'i', b'!']);
///
/// // NOTE: postcard handles `&[u8]` and `&[u8; N]` differently.
/// let data: &[u8] = &[0x01u8, 0x00, 0x20, 0x30];
/// let used = to_slice(data, &mut buf).unwrap();
/// assert_eq!(used, &[0x04, 0x01, 0x00, 0x20, 0x30]);
///
/// let data: &[u8; 4] = &[0x01u8, 0x00, 0x20, 0x30];
/// let used = to_slice(data, &mut buf).unwrap();
/// assert_eq!(used, &[0x01, 0x00, 0x20, 0x30]);
/// ```
pub fn to_slice<'a, 'b, T>(value: &'b T, buf: &'a mut [u8]) -> Result<&'a mut [u8]>
where
T: Serialize + ?Sized,
{
serialize_with_flavor::<T, Slice<'a>, &'a mut [u8]>(value, Slice::new(buf))
}
/// 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`.
///
/// ## Example
///
/// ```rust
/// use postcard::to_vec_cobs;
/// use heapless::Vec;
/// use core::ops::Deref;
///
/// let ser: Vec<u8, 32> = to_vec_cobs(&false).unwrap();
/// assert_eq!(ser.deref(), &[0x01, 0x01, 0x00]);
///
/// let ser: Vec<u8, 32> = to_vec_cobs("Hi!").unwrap();
/// assert_eq!(ser.deref(), &[0x05, 0x03, b'H', b'i', b'!', 0x00]);
///
/// // NOTE: postcard handles `&[u8]` and `&[u8; N]` differently.
/// let data: &[u8] = &[0x01u8, 0x00, 0x20, 0x30];
/// let ser: Vec<u8, 32> = to_vec_cobs(data).unwrap();
/// assert_eq!(ser.deref(), &[0x03, 0x04, 0x01, 0x03, 0x20, 0x30, 0x00]);
///
/// let data: &[u8; 4] = &[0x01u8, 0x00, 0x20, 0x30];
/// let ser: Vec<u8, 32> = to_vec_cobs(data).unwrap();
/// assert_eq!(ser.deref(), &[0x02, 0x01, 0x03, 0x20, 0x30, 0x00]);
/// ```
#[cfg(feature = "heapless")]
#[cfg_attr(docsrs, doc(cfg(feature = "heapless")))]
pub fn to_vec_cobs<T, const B: usize>(value: &T) -> Result<Vec<u8, B>>
where
T: Serialize + ?Sized,
{
serialize_with_flavor::<T, Cobs<HVec<B>>, Vec<u8, B>>(value, Cobs::try_new(HVec::default())?)
}
/// Serialize a `T` to a `heapless::Vec<u8>`, with the `Vec` containing
/// data in a serialized format.
///
/// ## Example
///
/// ```rust
/// use postcard::to_vec;
/// use heapless::Vec;
/// use core::ops::Deref;
///
/// let ser: Vec<u8, 32> = to_vec(&true).unwrap();
/// assert_eq!(ser.deref(), &[0x01]);
///
/// let ser: Vec<u8, 32> = to_vec("Hi!").unwrap();
/// assert_eq!(ser.deref(), &[0x03, b'H', b'i', b'!']);
///
/// // NOTE: postcard handles `&[u8]` and `&[u8; N]` differently.
/// let data: &[u8] = &[0x01u8, 0x00, 0x20, 0x30];
/// let ser: Vec<u8, 32> = to_vec(data).unwrap();
/// assert_eq!(ser.deref(), &[0x04, 0x01, 0x00, 0x20, 0x30]);
///
/// let data: &[u8; 4] = &[0x01u8, 0x00, 0x20, 0x30];
/// let ser: Vec<u8, 32> = to_vec(data).unwrap();
/// assert_eq!(ser.deref(), &[0x01, 0x00, 0x20, 0x30]);
/// ```
#[cfg(feature = "heapless")]
#[cfg_attr(docsrs, doc(cfg(feature = "heapless")))]
pub fn to_vec<T, const B: usize>(value: &T) -> Result<Vec<u8, B>>
where
T: Serialize + ?Sized,
{
serialize_with_flavor::<T, HVec<B>, Vec<u8, B>>(value, HVec::default())
}
/// Serialize a `T` to a `std::vec::Vec<u8>`.
///
/// ## Example
///
/// ```rust
/// use postcard::to_stdvec;
///
/// let ser: Vec<u8> = to_stdvec(&true).unwrap();
/// assert_eq!(ser.as_slice(), &[0x01]);
///
/// let ser: Vec<u8> = to_stdvec("Hi!").unwrap();
/// assert_eq!(ser.as_slice(), &[0x03, b'H', b'i', b'!']);
/// ```
#[cfg(feature = "use-std")]
#[cfg_attr(docsrs, doc(cfg(feature = "use-std")))]
#[inline]
pub fn to_stdvec<T>(value: &T) -> Result<std::vec::Vec<u8>>
where
T: Serialize + ?Sized,
{
to_allocvec(value)
}
/// Serialize and COBS encode a `T` to a `std::vec::Vec<u8>`.
///
/// The terminating sentinel `0x00` byte is included in the output.
///
/// ## Example
///
/// ```rust
/// use postcard::to_stdvec_cobs;
///
/// let ser: Vec<u8> = to_stdvec_cobs(&true).unwrap();
/// assert_eq!(ser.as_slice(), &[0x02, 0x01, 0x00]);
///
/// let ser: Vec<u8> = to_stdvec_cobs("Hi!").unwrap();
/// assert_eq!(ser.as_slice(), &[0x05, 0x03, b'H', b'i', b'!', 0x00]);
/// ```
#[cfg(feature = "use-std")]
#[cfg_attr(docsrs, doc(cfg(feature = "use-std")))]
#[inline]
pub fn to_stdvec_cobs<T>(value: &T) -> Result<std::vec::Vec<u8>>
where
T: Serialize + ?Sized,
{
to_allocvec_cobs(value)
}
/// Serialize a `T` to an `alloc::vec::Vec<u8>`.
///
/// ## Example
///
/// ```rust
/// use postcard::to_allocvec;
///
/// let ser: Vec<u8> = to_allocvec(&true).unwrap();
/// assert_eq!(ser.as_slice(), &[0x01]);
///
/// let ser: Vec<u8> = to_allocvec("Hi!").unwrap();
/// assert_eq!(ser.as_slice(), &[0x03, b'H', b'i', b'!']);
/// ```
#[cfg(feature = "alloc")]
#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
pub fn to_allocvec<T>(value: &T) -> Result<alloc::vec::Vec<u8>>
where
T: Serialize + ?Sized,
{
serialize_with_flavor::<T, AllocVec, alloc::vec::Vec<u8>>(value, AllocVec::new())
}
/// Serialize and COBS encode a `T` to an `alloc::vec::Vec<u8>`.
///
/// The terminating sentinel `0x00` byte is included in the output.
///
/// ## Example
///
/// ```rust
/// use postcard::to_allocvec_cobs;
///
/// let ser: Vec<u8> = to_allocvec_cobs(&true).unwrap();
/// assert_eq!(ser.as_slice(), &[0x02, 0x01, 0x00]);
///
/// let ser: Vec<u8> = to_allocvec_cobs("Hi!").unwrap();
/// assert_eq!(ser.as_slice(), &[0x05, 0x03, b'H', b'i', b'!', 0x00]);
/// ```
#[cfg(feature = "alloc")]
#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
pub fn to_allocvec_cobs<T>(value: &T) -> Result<alloc::vec::Vec<u8>>
where
T: Serialize + ?Sized,
{
serialize_with_flavor::<T, Cobs<AllocVec>, alloc::vec::Vec<u8>>(
value,
Cobs::try_new(AllocVec::new())?,
)
}
/// Serialize a `T` to a [`core::iter::Extend`],
/// ## Example
///
/// ```rust
/// use postcard::to_extend;
/// let mut vec = Vec::new();
///
/// let ser = to_extend(&true, vec).unwrap();
/// let vec = to_extend("Hi!", ser).unwrap();
/// assert_eq!(&vec[0..5], &[0x01, 0x03, b'H', b'i', b'!']);
/// ```
pub fn to_extend<T, W>(value: &T, writer: W) -> Result<W>
where
T: Serialize + ?Sized,
W: core::iter::Extend<u8>,
{
serialize_with_flavor::<T, _, _>(value, flavors::ExtendFlavor::new(writer))
}
/// Serialize a `T` to an [`embedded_io Write`](crate::eio::Write),
/// ## Example
///
/// ```rust
/// use postcard::to_eio;
/// let mut buf: [u8; 32] = [0; 32];
/// let mut writer: &mut [u8] = &mut buf;
///
/// let ser = to_eio(&true, &mut writer).unwrap();
/// to_eio("Hi!", ser).unwrap();
/// assert_eq!(&buf[0..5], &[0x01, 0x03, b'H', b'i', b'!']);
/// ```
#[cfg(any(feature = "embedded-io-04", feature = "embedded-io-06"))]
pub fn to_eio<T, W>(value: &T, writer: W) -> Result<W>
where
T: Serialize + ?Sized,
W: crate::eio::Write,
{
serialize_with_flavor::<T, _, _>(value, flavors::eio::WriteFlavor::new(writer))
}
/// Serialize a `T` to a [`std::io::Write`],
/// ## Example
///
/// ```rust
/// use postcard::to_io;
/// let mut buf: [u8; 32] = [0; 32];
/// let mut writer: &mut [u8] = &mut buf;
///
/// let ser = to_io(&true, &mut writer).unwrap();
/// to_io("Hi!", ser).unwrap();
/// assert_eq!(&buf[0..5], &[0x01, 0x03, b'H', b'i', b'!']);
/// ```
#[cfg(feature = "use-std")]
pub fn to_io<T, W>(value: &T, writer: W) -> Result<W>
where
T: Serialize + ?Sized,
W: std::io::Write,
{
serialize_with_flavor::<T, _, _>(value, flavors::io::WriteFlavor::new(writer))
}
/// Conveniently serialize a `T` to the given slice, with the resulting slice containing
/// data followed by a 32-bit CRC. The CRC bytes are included in the output buffer.
///
/// When successful, this function returns the slice containing the
/// serialized and encoded message.
///
/// ## Example
///
/// ```rust
/// use crc::{Crc, CRC_32_ISCSI};
///
/// let mut buf = [0; 9];
///
/// let data: &[u8] = &[0x01, 0x00, 0x20, 0x30];
/// let crc = Crc::<u32>::new(&CRC_32_ISCSI);
/// let used = postcard::to_slice_crc32(data, &mut buf, crc.digest()).unwrap();
/// assert_eq!(used, &[0x04, 0x01, 0x00, 0x20, 0x30, 0x8E, 0xC8, 0x1A, 0x37]);
/// ```
///
/// See the `ser_flavors::crc` module for the complete set of functions.
#[cfg(feature = "use-crc")]
#[cfg_attr(docsrs, doc(cfg(feature = "use-crc")))]
#[inline]
pub fn to_slice_crc32<'a, T>(
value: &T,
buf: &'a mut [u8],
digest: crc::Digest<'_, u32>,
) -> Result<&'a mut [u8]>
where
T: Serialize + ?Sized,
{
flavors::crc::to_slice_u32(value, buf, digest)
}
/// Conveniently serialize a `T` to a `heapless::Vec<u8>`, with the `Vec` containing
/// data followed by a 32-bit CRC. The CRC bytes are included in the output `Vec`.
///
/// ## Example
///
/// ```rust
/// use crc::{Crc, CRC_32_ISCSI};
/// use heapless::Vec;
/// use core::ops::Deref;
///
/// // NOTE: postcard handles `&[u8]` and `&[u8; N]` differently.
/// let data: &[u8] = &[0x01u8, 0x00, 0x20, 0x30];
/// let crc = Crc::<u32>::new(&CRC_32_ISCSI);
/// let ser: Vec<u8, 32> = postcard::to_vec_crc32(data, crc.digest()).unwrap();
/// assert_eq!(ser.deref(), &[0x04, 0x01, 0x00, 0x20, 0x30, 0x8E, 0xC8, 0x1A, 0x37]);
///
/// let data: &[u8; 4] = &[0x01u8, 0x00, 0x20, 0x30];
/// let ser: Vec<u8, 32> = postcard::to_vec_crc32(data, crc.digest()).unwrap();
/// assert_eq!(ser.deref(), &[0x01, 0x00, 0x20, 0x30, 0xCC, 0x4B, 0x4A, 0xDA]);
/// ```
///
/// See the `ser_flavors::crc` module for the complete set of functions.
#[cfg(all(feature = "use-crc", feature = "heapless"))]
#[cfg_attr(docsrs, doc(cfg(all(feature = "use-crc", feature = "heapless"))))]
#[inline]
pub fn to_vec_crc32<T, const B: usize>(
value: &T,
digest: crc::Digest<'_, u32>,
) -> Result<heapless::Vec<u8, B>>
where
T: Serialize + ?Sized,
{
flavors::crc::to_vec_u32(value, digest)
}
/// Conveniently serialize a `T` to a `heapless::Vec<u8>`, with the `Vec` containing
/// data followed by a 32-bit CRC. The CRC bytes are included in the output `Vec`.
///
/// ## Example
///
/// ```rust
/// use crc::{Crc, CRC_32_ISCSI};
/// use core::ops::Deref;
///
/// // NOTE: postcard handles `&[u8]` and `&[u8; N]` differently.
/// let data: &[u8] = &[0x01u8, 0x00, 0x20, 0x30];
/// let crc = Crc::<u32>::new(&CRC_32_ISCSI);
/// let ser: Vec<u8> = postcard::to_stdvec_crc32(data, crc.digest()).unwrap();
/// assert_eq!(ser.deref(), &[0x04, 0x01, 0x00, 0x20, 0x30, 0x8E, 0xC8, 0x1A, 0x37]);
///
/// let data: &[u8; 4] = &[0x01u8, 0x00, 0x20, 0x30];
/// let ser: Vec<u8> = postcard::to_stdvec_crc32(data, crc.digest()).unwrap();
/// assert_eq!(ser.deref(), &[0x01, 0x00, 0x20, 0x30, 0xCC, 0x4B, 0x4A, 0xDA]);
/// ```
///
/// See the `ser_flavors::crc` module for the complete set of functions.
#[cfg(all(feature = "use-crc", feature = "use-std"))]
#[cfg_attr(docsrs, doc(cfg(all(feature = "use-crc", feature = "use-std"))))]
#[inline]
pub fn to_stdvec_crc32<T>(value: &T, digest: crc::Digest<'_, u32>) -> Result<std::vec::Vec<u8>>
where
T: Serialize + ?Sized,
{
flavors::crc::to_allocvec_u32(value, digest)
}
/// Conveniently serialize a `T` to a `heapless::Vec<u8>`, with the `Vec` containing
/// data followed by a 32-bit CRC. The CRC bytes are included in the output `Vec`.
///
/// ## Example
///
/// ```rust
/// use crc::{Crc, CRC_32_ISCSI};
/// use core::ops::Deref;
///
/// // NOTE: postcard handles `&[u8]` and `&[u8; N]` differently.
/// let data: &[u8] = &[0x01u8, 0x00, 0x20, 0x30];
/// let crc = Crc::<u32>::new(&CRC_32_ISCSI);
/// let ser: Vec<u8> = postcard::to_allocvec_crc32(data, crc.digest()).unwrap();
/// assert_eq!(ser.deref(), &[0x04, 0x01, 0x00, 0x20, 0x30, 0x8E, 0xC8, 0x1A, 0x37]);
///
/// let data: &[u8; 4] = &[0x01u8, 0x00, 0x20, 0x30];
/// let ser: Vec<u8> = postcard::to_allocvec_crc32(data, crc.digest()).unwrap();
/// assert_eq!(ser.deref(), &[0x01, 0x00, 0x20, 0x30, 0xCC, 0x4B, 0x4A, 0xDA]);
/// ```
///
/// See the `ser_flavors::crc` module for the complete set of functions.
#[cfg(all(feature = "use-crc", feature = "alloc"))]
#[cfg_attr(docsrs, doc(cfg(all(feature = "use-crc", feature = "alloc"))))]
#[inline]
pub fn to_allocvec_crc32<T>(value: &T, digest: crc::Digest<'_, u32>) -> Result<alloc::vec::Vec<u8>>
where
T: Serialize + ?Sized,
{
flavors::crc::to_allocvec_u32(value, digest)
}
/// `serialize_with_flavor()` has three generic parameters, `T, F, O`.
///
/// * `T`: This is the type that is being serialized
/// * `S`: This is the Storage that is used during serialization
/// * `O`: This is the resulting storage type that is returned containing the serialized data
///
/// For more information about how Flavors work, please see the
/// [`flavors` module documentation](./flavors/index.html).
///
/// ```rust
/// 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]);
/// ```
pub fn serialize_with_flavor<T, S, O>(value: &T, storage: S) -> Result<O>
where
T: Serialize + ?Sized,
S: Flavor<Output = O>,
{
let mut serializer = Serializer { output: storage };
value.serialize(&mut serializer)?;
serializer
.output
.finalize()
.map_err(|_| Error::SerializeBufferFull)
}
/// Compute the size of the postcard serialization of `T`.
pub fn serialized_size<T>(value: &T) -> Result<usize>
where
T: Serialize + ?Sized,
{
serialize_with_flavor::<T, flavors::Size, usize>(value, flavors::Size::default())
}
#[cfg(feature = "heapless")]
#[cfg(test)]
mod test {
use super::*;
use crate::max_size::MaxSize;
use crate::varint::{varint_max, varint_usize};
use core::fmt::Write;
use core::ops::{Deref, DerefMut};
use heapless::{FnvIndexMap, String};
use serde::Deserialize;
#[test]
fn ser_u8() {
let output: Vec<u8, 1> = to_vec(&0x05u8).unwrap();
assert_eq!(&[5], output.deref());
assert!(output.len() == serialized_size(&0x05u8).unwrap());
assert!(output.len() <= Vec::<u8, 1>::POSTCARD_MAX_SIZE);
}
#[test]
fn ser_u16() {
const SZ: usize = varint_max::<u16>();
let output: Vec<u8, SZ> = to_vec(&0xA5C7u16).unwrap();
assert_eq!(&[0xC7, 0xCB, 0x02], output.deref());
assert!(output.len() == serialized_size(&0xA5C7u16).unwrap());
assert!(output.len() <= Vec::<u8, SZ>::POSTCARD_MAX_SIZE);
}
#[test]
fn ser_u32() {
const SZ: usize = varint_max::<u32>();
let output: Vec<u8, SZ> = to_vec(&0xCDAB3412u32).unwrap();
assert_eq!(&[0x92, 0xE8, 0xAC, 0xED, 0x0C], output.deref());
assert!(output.len() == serialized_size(&0xCDAB3412u32).unwrap());
assert!(output.len() <= Vec::<u8, SZ>::POSTCARD_MAX_SIZE);
}
#[test]
fn ser_u64() {
const SZ: usize = varint_max::<u64>();
let output: Vec<u8, SZ> = to_vec(&0x1234_5678_90AB_CDEFu64).unwrap();
assert_eq!(
&[0xEF, 0x9B, 0xAF, 0x85, 0x89, 0xCF, 0x95, 0x9A, 0x12],
output.deref()
);
assert!(output.len() == serialized_size(&0x1234_5678_90AB_CDEFu64).unwrap());
assert!(output.len() <= Vec::<u8, SZ>::POSTCARD_MAX_SIZE);
}
#[test]
fn ser_u128() {
const SZ: usize = varint_max::<u128>();
let output: Vec<u8, SZ> = to_vec(&0x1234_5678_90AB_CDEF_1234_5678_90AB_CDEFu128).unwrap();
assert_eq!(
&[
0xEF, 0x9B, 0xAF, 0x85, 0x89, 0xCF, 0x95, 0x9A, 0x92, 0xDE, 0xB7, 0xDE, 0x8A, 0x92,
0x9E, 0xAB, 0xB4, 0x24,
],
output.deref()
);
assert!(
output.len()
== serialized_size(&0x1234_5678_90AB_CDEF_1234_5678_90AB_CDEFu128).unwrap()
);
assert!(output.len() <= Vec::<u8, SZ>::POSTCARD_MAX_SIZE);
}
#[derive(Serialize)]
struct BasicU8S {
st: u16,
ei: u8,
ote: u128,
sf: u64,
tt: u32,
}
impl MaxSize for BasicU8S {
const POSTCARD_MAX_SIZE: usize = {
u16::POSTCARD_MAX_SIZE
+ u8::POSTCARD_MAX_SIZE
+ u128::POSTCARD_MAX_SIZE
+ u64::POSTCARD_MAX_SIZE
+ u32::POSTCARD_MAX_SIZE
};
}
#[test]
fn ser_struct_unsigned() {
const SZ: usize = BasicU8S::POSTCARD_MAX_SIZE;
let input = BasicU8S {
st: 0xABCD,
ei: 0xFE,
ote: 0x1234_4321_ABCD_DCBA_1234_4321_ABCD_DCBA,
sf: 0x1234_4321_ABCD_DCBA,
tt: 0xACAC_ACAC,
};
let output: Vec<u8, SZ> = to_vec(&input).unwrap();
assert_eq!(
&[
0xCD, 0xD7, 0x02, 0xFE, 0xBA, 0xB9, 0xB7, 0xDE, 0x9A, 0xE4, 0x90, 0x9A, 0x92, 0xF4,
0xF2, 0xEE, 0xBC, 0xB5, 0xC8, 0xA1, 0xB4, 0x24, 0xBA, 0xB9, 0xB7, 0xDE, 0x9A, 0xE4,
0x90, 0x9A, 0x12, 0xAC, 0xD9, 0xB2, 0xE5, 0x0A
],
output.deref()
);
assert!(output.len() == serialized_size(&input).unwrap());
assert!(output.len() <= BasicU8S::POSTCARD_MAX_SIZE);
}
#[test]
fn ser_byte_slice() {
let input: &[u8] = &[1u8, 2, 3, 4, 5, 6, 7, 8];
let output: Vec<u8, 9> = to_vec(input).unwrap();
assert_eq!(
&[0x08, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08],
output.deref()
);
assert!(output.len() == serialized_size(&input).unwrap());
let mut input: Vec<u8, 1024> = Vec::new();
for i in 0..1024 {
input.push((i & 0xFF) as u8).unwrap();
}
let output: Vec<u8, 2048> = to_vec(input.deref()).unwrap();
assert_eq!(&[0x80, 0x08], &output.deref()[..2]);
assert_eq!(output.len(), 1026);
for (i, val) in output.deref()[2..].iter().enumerate() {
assert_eq!((i & 0xFF) as u8, *val);
}
}
#[test]
fn ser_str() {
let input: &str = "hello, postcard!";
let output: Vec<u8, 17> = to_vec(input).unwrap();
assert_eq!(0x10, output.deref()[0]);
assert_eq!(input.as_bytes(), &output.deref()[1..]);
assert!(output.len() == serialized_size(&input).unwrap());
let mut input: String<1024> = String::new();
for _ in 0..256 {
write!(&mut input, "abcd").unwrap();
}
let output: Vec<u8, 2048> = to_vec(input.deref()).unwrap();
assert_eq!(&[0x80, 0x08], &output.deref()[..2]);
assert!(String::<1024>::POSTCARD_MAX_SIZE <= output.len());
assert_eq!(output.len(), 1026);
for ch in output.deref()[2..].chunks(4) {
assert_eq!("abcd", core::str::from_utf8(ch).unwrap());
}
}
#[test]
fn usize_varint_encode() {
let mut buf = [0; varint_max::<usize>()];
let res = varint_usize(1, &mut buf);
assert_eq!(&[1], res);
let res = varint_usize(usize::MAX, &mut buf);
//
if varint_max::<usize>() == varint_max::<u32>() {
assert_eq!(&[0xFF, 0xFF, 0xFF, 0xFF, 0x0F], res);
} else if varint_max::<usize>() == varint_max::<u64>() {
assert_eq!(
&[0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01],
res
);
} else {
panic!("Update this test for 16/128 bit targets!");
}
}
#[allow(dead_code)]
#[derive(Serialize)]
enum BasicEnum {
Bib,
Bim,
Bap,
}
#[derive(Serialize)]
struct EnumStruct {
eight: u8,
sixt: u16,
}
#[derive(Serialize)]
enum DataEnum {
Bib(u16),
Bim(u64),
Bap(u8),
Kim(EnumStruct),
Chi { a: u8, b: u32 },
Sho(u16, u8),
}
#[test]
fn enums() {
let input = BasicEnum::Bim;
let output: Vec<u8, 1> = to_vec(&input).unwrap();
assert_eq!(&[0x01], output.deref());
assert!(output.len() == serialized_size(&input).unwrap());
let input = DataEnum::Bim(u64::MAX);
let output: Vec<u8, { 1 + varint_max::<u64>() }> = to_vec(&input).unwrap();
assert_eq!(
&[0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01],
output.deref()
);
assert!(output.len() == serialized_size(&input).unwrap());
let input = DataEnum::Bib(u16::MAX);
let output: Vec<u8, { 1 + varint_max::<u16>() }> = to_vec(&input).unwrap();
assert_eq!(&[0x00, 0xFF, 0xFF, 0x03], output.deref());
assert!(output.len() == serialized_size(&input).unwrap());
let input = DataEnum::Bap(u8::MAX);
let output: Vec<u8, 2> = to_vec(&input).unwrap();
assert_eq!(&[0x02, 0xFF], output.deref());
assert!(output.len() == serialized_size(&input).unwrap());
let input = DataEnum::Kim(EnumStruct {
eight: 0xF0,
sixt: 0xACAC,
});
let output: Vec<u8, 8> = to_vec(&input).unwrap();
assert_eq!(&[0x03, 0xF0, 0xAC, 0xD9, 0x02], output.deref());
assert!(output.len() == serialized_size(&input).unwrap());
let input = DataEnum::Chi {
a: 0x0F,
b: 0xC7C7C7C7,
};
let output: Vec<u8, 8> = to_vec(&input).unwrap();
assert_eq!(&[0x04, 0x0F, 0xC7, 0x8F, 0x9F, 0xBE, 0x0C], output.deref());
assert!(output.len() == serialized_size(&input).unwrap());
let input = DataEnum::Sho(0x6969, 0x07);
let output: Vec<u8, 8> = to_vec(&input).unwrap();
assert_eq!(&[0x05, 0xE9, 0xD2, 0x01, 0x07], output.deref());
assert!(output.len() == serialized_size(&input).unwrap());
}
#[test]
fn tuples() {
let input = (1u8, 10u32, "Hello!");
let output: Vec<u8, 128> = to_vec(&input).unwrap();
assert_eq!(
&[1u8, 0x0A, 0x06, b'H', b'e', b'l', b'l', b'o', b'!'],
output.deref()
);
assert!(output.len() == serialized_size(&input).unwrap());
}
#[test]
fn bytes() {
let x: &[u8; 32] = &[0u8; 32];
let output: Vec<u8, 128> = to_vec(x).unwrap();
assert_eq!(output.len(), 32);
assert!(output.len() == serialized_size(&x).unwrap());
assert!(<[u8; 32] as MaxSize>::POSTCARD_MAX_SIZE <= output.len());
let x: &[u8] = &[0u8; 32];
let output: Vec<u8, 128> = to_vec(x).unwrap();
assert_eq!(output.len(), 33);
assert!(output.len() == serialized_size(&x).unwrap());
}
#[derive(Serialize)]
pub struct NewTypeStruct(u32);
#[derive(Serialize)]
pub struct TupleStruct((u8, u16));
#[test]
fn structs() {
let input = NewTypeStruct(5);
let output: Vec<u8, 1> = to_vec(&input).unwrap();
assert_eq!(&[0x05], output.deref());
assert!(output.len() == serialized_size(&input).unwrap());
let input = TupleStruct((0xA0, 0x1234));
let output: Vec<u8, 3> = to_vec(&input).unwrap();
assert_eq!(&[0xA0, 0xB4, 0x24], output.deref());
assert!(output.len() == serialized_size(&input).unwrap());
}
#[derive(Serialize, Deserialize, Eq, PartialEq, Debug)]
struct RefStruct<'a> {
bytes: &'a [u8],
str_s: &'a str,
}
#[test]
fn ref_struct() {
let message = "hElLo";
let bytes = [0x01, 0x10, 0x02, 0x20];
let input = RefStruct {
bytes: &bytes,
str_s: message,
};
let output: Vec<u8, 11> = to_vec(&input).unwrap();
assert_eq!(
&[0x04, 0x01, 0x10, 0x02, 0x20, 0x05, b'h', b'E', b'l', b'L', b'o',],
output.deref()
);
assert!(output.len() == serialized_size(&input).unwrap());
}
#[test]
fn unit() {
let output: Vec<u8, 1> = to_vec(&()).unwrap();
assert_eq!(output.len(), 0);
assert!(output.len() == serialized_size(&()).unwrap());
}
#[test]
fn heapless_data() {
let mut input: Vec<u8, 4> = Vec::new();
input.extend_from_slice(&[0x01, 0x02, 0x03, 0x04]).unwrap();
let output: Vec<u8, 5> = to_vec(&input).unwrap();
assert_eq!(&[0x04, 0x01, 0x02, 0x03, 0x04], output.deref());
assert!(output.len() == serialized_size(&input).unwrap());
let mut input: String<8> = String::new();
write!(&mut input, "helLO!").unwrap();
let output: Vec<u8, 7> = to_vec(&input).unwrap();
assert_eq!(&[0x06, b'h', b'e', b'l', b'L', b'O', b'!'], output.deref());
assert!(output.len() == serialized_size(&input).unwrap());
let mut input: FnvIndexMap<u8, u8, 4> = FnvIndexMap::new();
input.insert(0x01, 0x05).unwrap();
input.insert(0x02, 0x06).unwrap();
input.insert(0x03, 0x07).unwrap();
input.insert(0x04, 0x08).unwrap();
let output: Vec<u8, 100> = to_vec(&input).unwrap();
assert_eq!(
&[0x04, 0x01, 0x05, 0x02, 0x06, 0x03, 0x07, 0x04, 0x08],
output.deref()
);
assert!(output.len() == serialized_size(&input).unwrap());
}
#[test]
fn cobs_test() {
let message = "hElLo";
let bytes = [0x01, 0x00, 0x02, 0x20];
let input = RefStruct {
bytes: &bytes,
str_s: message,
};
let mut output: Vec<u8, 13> = to_vec_cobs(&input).unwrap();
let sz = cobs::decode_in_place(output.deref_mut()).unwrap();
let x = crate::from_bytes::<RefStruct<'_>>(&output.deref_mut()[..sz]).unwrap();
assert_eq!(input, x);
}
}
+564
View File
@@ -0,0 +1,564 @@
use serde::{ser, Serialize};
use crate::error::{Error, Result};
use crate::ser::flavors::Flavor;
use crate::varint::*;
/// A `serde` compatible serializer, generic over "Flavors" of serializing plugins.
///
/// It should rarely be necessary to directly use this type unless you are implementing your
/// own [`SerFlavor`].
///
/// See the docs for [`SerFlavor`] for more information about "flavors" of serialization
///
/// [`SerFlavor`]: crate::ser_flavors::Flavor
pub struct Serializer<F>
where
F: Flavor,
{
/// This is the Flavor(s) that will be used to modify or store any bytes generated
/// by serialization
pub output: F,
}
impl<F: Flavor> Serializer<F> {
/// Attempt to push a variably encoded [usize] into the output data stream
#[inline]
pub(crate) fn try_push_varint_usize(&mut self, data: usize) -> Result<()> {
let mut buf = [0u8; varint_max::<usize>()];
let used_buf = varint_usize(data, &mut buf);
self.output.try_extend(used_buf)
}
/// Attempt to push a variably encoded [u128] into the output data stream
#[inline]
pub(crate) fn try_push_varint_u128(&mut self, data: u128) -> Result<()> {
let mut buf = [0u8; varint_max::<u128>()];
let used_buf = varint_u128(data, &mut buf);
self.output.try_extend(used_buf)
}
/// Attempt to push a variably encoded [u64] into the output data stream
#[inline]
pub(crate) fn try_push_varint_u64(&mut self, data: u64) -> Result<()> {
let mut buf = [0u8; varint_max::<u64>()];
let used_buf = varint_u64(data, &mut buf);
self.output.try_extend(used_buf)
}
/// Attempt to push a variably encoded [u32] into the output data stream
#[inline]
pub(crate) fn try_push_varint_u32(&mut self, data: u32) -> Result<()> {
let mut buf = [0u8; varint_max::<u32>()];
let used_buf = varint_u32(data, &mut buf);
self.output.try_extend(used_buf)
}
/// Attempt to push a variably encoded [u16] into the output data stream
#[inline]
pub(crate) fn try_push_varint_u16(&mut self, data: u16) -> Result<()> {
let mut buf = [0u8; varint_max::<u16>()];
let used_buf = varint_u16(data, &mut buf);
self.output.try_extend(used_buf)
}
}
impl<F> ser::Serializer for &mut Serializer<F>
where
F: Flavor,
{
type Ok = ();
type Error = Error;
// Associated types for keeping track of additional state while serializing
// compound data structures like sequences and maps. In this case no
// additional state is required beyond what is already stored in the
// Serializer struct.
type SerializeSeq = Self;
type SerializeTuple = Self;
type SerializeTupleStruct = Self;
type SerializeTupleVariant = Self;
type SerializeMap = Self;
type SerializeStruct = Self;
type SerializeStructVariant = Self;
#[inline]
fn is_human_readable(&self) -> bool {
false
}
#[inline]
fn serialize_bool(self, v: bool) -> Result<()> {
self.serialize_u8(if v { 1 } else { 0 })
}
#[inline]
fn serialize_i8(self, v: i8) -> Result<()> {
self.serialize_u8(v.to_le_bytes()[0])
}
#[inline]
fn serialize_i16(self, v: i16) -> Result<()> {
let zzv = zig_zag_i16(v);
self.try_push_varint_u16(zzv)
.map_err(|_| Error::SerializeBufferFull)
}
#[inline]
fn serialize_i32(self, v: i32) -> Result<()> {
let zzv = zig_zag_i32(v);
self.try_push_varint_u32(zzv)
.map_err(|_| Error::SerializeBufferFull)
}
#[inline]
fn serialize_i64(self, v: i64) -> Result<()> {
let zzv = zig_zag_i64(v);
self.try_push_varint_u64(zzv)
.map_err(|_| Error::SerializeBufferFull)
}
#[inline]
fn serialize_i128(self, v: i128) -> Result<()> {
let zzv = zig_zag_i128(v);
self.try_push_varint_u128(zzv)
.map_err(|_| Error::SerializeBufferFull)
}
#[inline]
fn serialize_u8(self, v: u8) -> Result<()> {
self.output
.try_push(v)
.map_err(|_| Error::SerializeBufferFull)
}
#[inline]
fn serialize_u16(self, v: u16) -> Result<()> {
self.try_push_varint_u16(v)
.map_err(|_| Error::SerializeBufferFull)
}
#[inline]
fn serialize_u32(self, v: u32) -> Result<()> {
self.try_push_varint_u32(v)
.map_err(|_| Error::SerializeBufferFull)
}
#[inline]
fn serialize_u64(self, v: u64) -> Result<()> {
self.try_push_varint_u64(v)
.map_err(|_| Error::SerializeBufferFull)
}
#[inline]
fn serialize_u128(self, v: u128) -> Result<()> {
self.try_push_varint_u128(v)
.map_err(|_| Error::SerializeBufferFull)
}
#[inline]
fn serialize_f32(self, v: f32) -> Result<()> {
let buf = v.to_bits().to_le_bytes();
self.output
.try_extend(&buf)
.map_err(|_| Error::SerializeBufferFull)
}
#[inline]
fn serialize_f64(self, v: f64) -> Result<()> {
let buf = v.to_bits().to_le_bytes();
self.output
.try_extend(&buf)
.map_err(|_| Error::SerializeBufferFull)
}
#[inline]
fn serialize_char(self, v: char) -> Result<()> {
let mut buf = [0u8; 4];
let strsl = v.encode_utf8(&mut buf);
strsl.serialize(self)
}
#[inline]
fn serialize_str(self, v: &str) -> Result<()> {
self.try_push_varint_usize(v.len())
.map_err(|_| Error::SerializeBufferFull)?;
self.output
.try_extend(v.as_bytes())
.map_err(|_| Error::SerializeBufferFull)?;
Ok(())
}
#[inline]
fn serialize_bytes(self, v: &[u8]) -> Result<()> {
self.try_push_varint_usize(v.len())
.map_err(|_| Error::SerializeBufferFull)?;
self.output
.try_extend(v)
.map_err(|_| Error::SerializeBufferFull)
}
#[inline]
fn serialize_none(self) -> Result<()> {
self.serialize_u8(0)
}
#[inline]
fn serialize_some<T>(self, value: &T) -> Result<()>
where
T: ?Sized + Serialize,
{
self.serialize_u8(1)?;
value.serialize(self)
}
#[inline]
fn serialize_unit(self) -> Result<()> {
Ok(())
}
#[inline]
fn serialize_unit_struct(self, _name: &'static str) -> Result<()> {
Ok(())
}
#[inline]
fn serialize_unit_variant(
self,
_name: &'static str,
variant_index: u32,
_variant: &'static str,
) -> Result<()> {
self.try_push_varint_u32(variant_index)
.map_err(|_| Error::SerializeBufferFull)
}
#[inline]
fn serialize_newtype_struct<T>(self, _name: &'static str, value: &T) -> Result<()>
where
T: ?Sized + Serialize,
{
value.serialize(self)
}
#[inline]
fn serialize_newtype_variant<T>(
self,
_name: &'static str,
variant_index: u32,
_variant: &'static str,
value: &T,
) -> Result<()>
where
T: ?Sized + Serialize,
{
self.try_push_varint_u32(variant_index)
.map_err(|_| Error::SerializeBufferFull)?;
value.serialize(self)
}
#[inline]
fn serialize_seq(self, len: Option<usize>) -> Result<Self::SerializeSeq> {
self.try_push_varint_usize(len.ok_or(Error::SerializeSeqLengthUnknown)?)
.map_err(|_| Error::SerializeBufferFull)?;
Ok(self)
}
#[inline]
fn serialize_tuple(self, _len: usize) -> Result<Self::SerializeTuple> {
Ok(self)
}
#[inline]
fn serialize_tuple_struct(
self,
_name: &'static str,
_len: usize,
) -> Result<Self::SerializeTupleStruct> {
Ok(self)
}
#[inline]
fn serialize_tuple_variant(
self,
_name: &'static str,
variant_index: u32,
_variant: &'static str,
_len: usize,
) -> Result<Self::SerializeTupleVariant> {
self.try_push_varint_u32(variant_index)
.map_err(|_| Error::SerializeBufferFull)?;
Ok(self)
}
#[inline]
fn serialize_map(self, len: Option<usize>) -> Result<Self::SerializeMap> {
self.try_push_varint_usize(len.ok_or(Error::SerializeSeqLengthUnknown)?)
.map_err(|_| Error::SerializeBufferFull)?;
Ok(self)
}
#[inline]
fn serialize_struct(self, _name: &'static str, _len: usize) -> Result<Self::SerializeStruct> {
Ok(self)
}
#[inline]
fn serialize_struct_variant(
self,
_name: &'static str,
variant_index: u32,
_variant: &'static str,
_len: usize,
) -> Result<Self::SerializeStructVariant> {
self.try_push_varint_u32(variant_index)
.map_err(|_| Error::SerializeBufferFull)?;
Ok(self)
}
#[inline]
fn collect_str<T>(self, value: &T) -> Result<Self::Ok>
where
T: core::fmt::Display + ?Sized,
{
use core::fmt::Write;
// Unfortunately, we need to know the size of the serialized data before
// we can place it into the output. In order to do this, we run the formatting
// of the output data TWICE, the first time to determine the length, the
// second time to actually format the data
//
// There are potentially other ways to do this, such as:
//
// * Reserving a fixed max size, such as 5 bytes, for the length field, and
// leaving non-canonical trailing zeroes at the end. This would work up
// to some reasonable length, but might have some portability vs max size
// tradeoffs, e.g. 64KiB if we pick 3 bytes, or 4GiB if we pick 5 bytes
// * Expose some kind of "memmove" capability to flavors, to allow us to
// format into the buffer, then "scoot over" that many times.
//
// Despite the current approaches downside in speed, it is likely flexible
// enough for the rare-ish case where formatting a Debug impl is necessary.
// This is better than the previous panicking behavior, and can be improved
// in the future.
struct CountWriter {
ct: usize,
}
impl Write for CountWriter {
fn write_str(&mut self, s: &str) -> core::result::Result<(), core::fmt::Error> {
self.ct += s.len();
Ok(())
}
}
let mut ctr = CountWriter { ct: 0 };
// This is the first pass through, where we just count the length of the
// data that we are given
write!(&mut ctr, "{value}").map_err(|_| Error::CollectStrError)?;
let len = ctr.ct;
self.try_push_varint_usize(len)
.map_err(|_| Error::SerializeBufferFull)?;
struct FmtWriter<'a, IF>
where
IF: Flavor,
{
output: &'a mut IF,
}
impl<IF> Write for FmtWriter<'_, IF>
where
IF: Flavor,
{
fn write_str(&mut self, s: &str) -> core::result::Result<(), core::fmt::Error> {
self.output
.try_extend(s.as_bytes())
.map_err(|_| core::fmt::Error)
}
}
// This second pass actually inserts the data.
let mut fw = FmtWriter {
output: &mut self.output,
};
write!(&mut fw, "{value}").map_err(|_| Error::CollectStrError)?;
Ok(())
}
}
impl<F> ser::SerializeSeq for &mut Serializer<F>
where
F: Flavor,
{
// Must match the `Ok` type of the serializer.
type Ok = ();
// Must match the `Error` type of the serializer.
type Error = Error;
// Serialize a single element of the sequence.
#[inline]
fn serialize_element<T>(&mut self, value: &T) -> Result<()>
where
T: ?Sized + Serialize,
{
value.serialize(&mut **self)
}
// Close the sequence.
#[inline]
fn end(self) -> Result<()> {
Ok(())
}
}
impl<F> ser::SerializeTuple for &mut Serializer<F>
where
F: Flavor,
{
type Ok = ();
type Error = Error;
#[inline]
fn serialize_element<T>(&mut self, value: &T) -> Result<()>
where
T: ?Sized + Serialize,
{
value.serialize(&mut **self)
}
#[inline]
fn end(self) -> Result<()> {
Ok(())
}
}
impl<F> ser::SerializeTupleStruct for &mut Serializer<F>
where
F: Flavor,
{
type Ok = ();
type Error = Error;
#[inline]
fn serialize_field<T>(&mut self, value: &T) -> Result<()>
where
T: ?Sized + Serialize,
{
value.serialize(&mut **self)
}
#[inline]
fn end(self) -> Result<()> {
Ok(())
}
}
impl<F> ser::SerializeTupleVariant for &mut Serializer<F>
where
F: Flavor,
{
type Ok = ();
type Error = Error;
#[inline]
fn serialize_field<T>(&mut self, value: &T) -> Result<()>
where
T: ?Sized + Serialize,
{
value.serialize(&mut **self)
}
#[inline]
fn end(self) -> Result<()> {
Ok(())
}
}
impl<F> ser::SerializeMap for &mut Serializer<F>
where
F: Flavor,
{
type Ok = ();
type Error = Error;
#[inline]
fn serialize_key<T>(&mut self, key: &T) -> Result<()>
where
T: ?Sized + Serialize,
{
key.serialize(&mut **self)
}
#[inline]
fn serialize_value<T>(&mut self, value: &T) -> Result<()>
where
T: ?Sized + Serialize,
{
value.serialize(&mut **self)
}
#[inline]
fn end(self) -> Result<()> {
Ok(())
}
}
impl<F> ser::SerializeStruct for &mut Serializer<F>
where
F: Flavor,
{
type Ok = ();
type Error = Error;
#[inline]
fn serialize_field<T>(&mut self, _key: &'static str, value: &T) -> Result<()>
where
T: ?Sized + Serialize,
{
value.serialize(&mut **self)
}
#[inline]
fn end(self) -> Result<()> {
Ok(())
}
}
impl<F> ser::SerializeStructVariant for &mut Serializer<F>
where
F: Flavor,
{
type Ok = ();
type Error = Error;
#[inline]
fn serialize_field<T>(&mut self, _key: &'static str, value: &T) -> Result<()>
where
T: ?Sized + Serialize,
{
value.serialize(&mut **self)
}
#[inline]
fn end(self) -> Result<()> {
Ok(())
}
}
fn zig_zag_i16(n: i16) -> u16 {
((n << 1) ^ (n >> 15)) as u16
}
fn zig_zag_i32(n: i32) -> u32 {
((n << 1) ^ (n >> 31)) as u32
}
fn zig_zag_i64(n: i64) -> u64 {
((n << 1) ^ (n >> 63)) as u64
}
fn zig_zag_i128(n: i128) -> u128 {
((n << 1) ^ (n >> 127)) as u128
}
+103
View File
@@ -0,0 +1,103 @@
/// Returns the maximum number of bytes required to encode T.
pub const fn varint_max<T: Sized>() -> usize {
const BITS_PER_BYTE: usize = 8;
const BITS_PER_VARINT_BYTE: usize = 7;
// How many data bits do we need for this type?
let bits = core::mem::size_of::<T>() * BITS_PER_BYTE;
// We add (BITS_PER_VARINT_BYTE - 1), to ensure any integer divisions
// with a remainder will always add exactly one full byte, but
// an evenly divided number of bits will be the same
let roundup_bits = bits + (BITS_PER_VARINT_BYTE - 1);
// Apply division, using normal "round down" integer division
roundup_bits / BITS_PER_VARINT_BYTE
}
/// Returns the maximum value stored in the last encoded byte.
pub const fn max_of_last_byte<T: Sized>() -> u8 {
let max_bits = core::mem::size_of::<T>() * 8;
let extra_bits = max_bits % 7;
(1 << extra_bits) - 1
}
#[inline]
pub fn varint_usize(n: usize, out: &mut [u8; varint_max::<usize>()]) -> &mut [u8] {
let mut value = n;
for i in 0..varint_max::<usize>() {
out[i] = value.to_le_bytes()[0];
if value < 128 {
return &mut out[..=i];
}
out[i] |= 0x80;
value >>= 7;
}
debug_assert_eq!(value, 0);
&mut out[..]
}
#[inline]
pub fn varint_u16(n: u16, out: &mut [u8; varint_max::<u16>()]) -> &mut [u8] {
let mut value = n;
for i in 0..varint_max::<u16>() {
out[i] = value.to_le_bytes()[0];
if value < 128 {
return &mut out[..=i];
}
out[i] |= 0x80;
value >>= 7;
}
debug_assert_eq!(value, 0);
&mut out[..]
}
#[inline]
pub fn varint_u32(n: u32, out: &mut [u8; varint_max::<u32>()]) -> &mut [u8] {
let mut value = n;
for i in 0..varint_max::<u32>() {
out[i] = value.to_le_bytes()[0];
if value < 128 {
return &mut out[..=i];
}
out[i] |= 0x80;
value >>= 7;
}
debug_assert_eq!(value, 0);
&mut out[..]
}
#[inline]
pub fn varint_u64(n: u64, out: &mut [u8; varint_max::<u64>()]) -> &mut [u8] {
let mut value = n;
for i in 0..varint_max::<u64>() {
out[i] = value.to_le_bytes()[0];
if value < 128 {
return &mut out[..=i];
}
out[i] |= 0x80;
value >>= 7;
}
debug_assert_eq!(value, 0);
&mut out[..]
}
#[inline]
pub fn varint_u128(n: u128, out: &mut [u8; varint_max::<u128>()]) -> &mut [u8] {
let mut value = n;
for i in 0..varint_max::<u128>() {
out[i] = value.to_le_bytes()[0];
if value < 128 {
return &mut out[..=i];
}
out[i] |= 0x80;
value >>= 7;
}
debug_assert_eq!(value, 0);
&mut out[..]
}