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:
17624 files changed
+4564928
-38
No files matched your search
Vendored
+376
@@ -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
@@ -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))
|
||||
}
|
||||
Vendored
+642
@@ -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);
|
||||
}
|
||||
Vendored
+612
@@ -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)
|
||||
);
|
||||
}
|
||||
}
|
||||
Vendored
+20
@@ -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};
|
||||
Vendored
+96
@@ -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 {}
|
||||
Vendored
+191
@@ -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);
|
||||
}
|
||||
}
|
||||
Vendored
+125
@@ -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);
|
||||
}
|
||||
}
|
||||
Vendored
+354
@@ -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);
|
||||
}
|
||||
}
|
||||
Vendored
+729
@@ -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)
|
||||
}
|
||||
}
|
||||
Vendored
+863
@@ -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
@@ -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
|
||||
}
|
||||
Vendored
+103
@@ -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[..]
|
||||
}
|
||||
Reference in new issue
Block a user