postcard-1.0.10/.cargo_vcs_info.json0000644000000001550000000000100127330ustar { "git": { "sha1": "f878536b80bfc2cce1ff0016f2c850740c99ae7b" }, "path_in_vcs": "source/postcard" }postcard-1.0.10/Cargo.toml0000644000000047310000000000100107350ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2018" name = "postcard" version = "1.0.10" authors = ["James Munns "] build = false autobins = false autoexamples = false autotests = false autobenches = false description = "A no_std + serde compatible message library for Rust" documentation = "https://docs.rs/postcard/" readme = "README.md" keywords = [ "serde", "cobs", "framing", ] categories = [ "embedded", "no-std", ] license = "MIT OR Apache-2.0" repository = "https://github.com/jamesmunns/postcard" resolver = "2" [package.metadata.docs.rs] all-features = true rustdoc-args = [ "--cfg", "docsrs", ] [lib] name = "postcard" path = "src/lib.rs" [[test]] name = "accumulator" path = "tests/accumulator.rs" [[test]] name = "crc" path = "tests/crc.rs" [[test]] name = "loopback" path = "tests/loopback.rs" [[test]] name = "max_size" path = "tests/max_size.rs" [[test]] name = "schema" path = "tests/schema.rs" [dependencies.cobs] version = "0.2.3" default-features = false [dependencies.crc] version = "3.0.1" optional = true [dependencies.defmt] version = "0.3" optional = true [dependencies.embedded-io-04] version = "0.4" optional = true package = "embedded-io" [dependencies.embedded-io-06] version = "0.6" optional = true package = "embedded-io" [dependencies.heapless] version = "0.7.0" features = ["serde"] optional = true default-features = false [dependencies.paste] version = "1.0.12" optional = true [dependencies.postcard-derive] version = "0.1.2" optional = true [dependencies.serde] version = "1.0.100" features = ["derive"] default-features = false [features] alloc = [ "serde/alloc", "embedded-io-04?/alloc", "embedded-io-06?/alloc", ] default = ["heapless-cas"] embedded-io = ["dep:embedded-io-04"] embedded-io-04 = ["dep:embedded-io-04"] embedded-io-06 = ["dep:embedded-io-06"] experimental-derive = ["postcard-derive"] heapless-cas = [ "heapless", "heapless/cas", ] use-crc = [ "crc", "paste", ] use-defmt = ["defmt"] use-std = [ "serde/std", "alloc", ] postcard-1.0.10/Cargo.toml.orig000064400000000000000000000035441046102023000144170ustar 00000000000000[package] name = "postcard" version = "1.0.10" authors = ["James Munns "] edition = "2018" readme = "README.md" repository = "https://github.com/jamesmunns/postcard" description = "A no_std + serde compatible message library for Rust" license = "MIT OR Apache-2.0" categories = [ "embedded", "no-std", ] keywords = [ "serde", "cobs", "framing", ] documentation = "https://docs.rs/postcard/" [package.metadata.docs.rs] all-features = true rustdoc-args = ["--cfg", "docsrs"] [dependencies] [dependencies.heapless] version = "0.7.0" default-features = false features = ["serde"] optional = true [dependencies.serde] version = "1.0.100" default-features = false features = ["derive"] [dependencies.cobs] version = "0.2.3" default-features = false [dependencies.defmt] version = "0.3" optional = true [dependencies.embedded-io-04] package = "embedded-io" version = "0.4" optional = true [dependencies.embedded-io-06] package = "embedded-io" version = "0.6" optional = true [dependencies.postcard-derive] path = "../postcard-derive" version = "0.1.2" optional = true [dependencies.crc] version = "3.0.1" optional = true [dependencies.paste] version = "1.0.12" optional = true [features] default = ["heapless-cas"] # Enables support for `embedded-io` traits # This feature will track the latest `embedded-io` when major releases are made embedded-io = ["dep:embedded-io-04"] # Specific versions of `embedded-io` can be selected through the features below embedded-io-04 = ["dep:embedded-io-04"] embedded-io-06 = ["dep:embedded-io-06"] use-std = ["serde/std", "alloc"] heapless-cas = ["heapless", "heapless/cas"] alloc = ["serde/alloc", "embedded-io-04?/alloc", "embedded-io-06?/alloc"] use-defmt = ["defmt"] use-crc = ["crc", "paste"] # Experimental features! # # NOT subject to SemVer guarantees! experimental-derive = ["postcard-derive"] postcard-1.0.10/README.md000064400000000000000000000106621046102023000130060ustar 00000000000000# Postcard [![Documentation](https://docs.rs/postcard/badge.svg)](https://docs.rs/postcard) Postcard is a `#![no_std]` focused serializer and deserializer for Serde. Postcard aims to be convenient for developers in constrained environments, while allowing for flexibility to customize behavior as needed. ## Design Goals 1. Design primarily for `#![no_std]` usage, in embedded or other constrained contexts 2. Support a maximal set of `serde` features, so `postcard` can be used as a drop in replacement 3. Avoid special differences in code between communication code written for a microcontroller or a desktop/server PC 4. Be resource efficient - memory usage, code size, developer time, and CPU time; in that order 5. Allow library users to customize the serialization and deserialization behavior to fit their bespoke needs ## Format Stability As of v1.0.0, `postcard` has a documented and stable wire format. More information about this wire format can be found in the `spec/` folder of the Postcard repository, or viewed online at . Work towards the Postcard Specification and portions of the Postcard 1.0 Release were sponsored by Mozilla Corporation. ## Variable Length Data All signed and unsigned integers larger than eight bits are encoded using a [Varint]. This includes the length of array slices, as well as the discriminant of `enums`. For more information, see the [Varint] chapter of the wire specification. [Varint]: https://postcard.jamesmunns.com/wire-format.html#varint-encoded-integers ## Example - Serialization/Deserialization Postcard can serialize and deserialize messages similar to other `serde` formats. Using the default `heapless` feature to serialize to a `heapless::Vec`: ```rust use core::ops::Deref; use serde::{Serialize, Deserialize}; use postcard::{from_bytes, to_vec}; use heapless::Vec; #[derive(Serialize, Deserialize, Debug, Eq, PartialEq)] struct RefStruct<'a> { bytes: &'a [u8], str_s: &'a str, } let message = "hElLo"; let bytes = [0x01, 0x10, 0x02, 0x20]; let output: Vec = to_vec(&RefStruct { bytes: &bytes, str_s: message, }).unwrap(); assert_eq!( &[0x04, 0x01, 0x10, 0x02, 0x20, 0x05, b'h', b'E', b'l', b'L', b'o',], output.deref() ); let out: RefStruct = from_bytes(output.deref()).unwrap(); assert_eq!( out, RefStruct { bytes: &bytes, str_s: message, } ); ``` Or the optional `alloc` feature to serialize to an `alloc::vec::Vec`: ```rust use core::ops::Deref; use serde::{Serialize, Deserialize}; use postcard::{from_bytes, to_allocvec}; extern crate alloc; use alloc::vec::Vec; #[derive(Serialize, Deserialize, Debug, Eq, PartialEq)] struct RefStruct<'a> { bytes: &'a [u8], str_s: &'a str, } let message = "hElLo"; let bytes = [0x01, 0x10, 0x02, 0x20]; let output: Vec = to_allocvec(&RefStruct { bytes: &bytes, str_s: message, }).unwrap(); assert_eq!( &[0x04, 0x01, 0x10, 0x02, 0x20, 0x05, b'h', b'E', b'l', b'L', b'o',], output.deref() ); let out: RefStruct = from_bytes(output.deref()).unwrap(); assert_eq!( out, RefStruct { bytes: &bytes, str_s: message, } ); ``` ## Flavors `postcard` supports a system called `Flavors`, which are used to modify the way postcard serializes or processes serialized data. These flavors act as "plugins" or "middlewares" during the serialization or deserialization process, and can be combined to obtain complex protocol formats. See the documentation of the `ser_flavors` or `de_flavors` modules for more information on usage. ## Setup - `Cargo.toml` Don't forget to add [the `no-std` subset](https://serde.rs/no-std.html) of `serde` along with `postcard` to the `[dependencies]` section of your `Cargo.toml`! ```toml [dependencies] postcard = "1.0.0" # By default, `serde` has the `std` feature enabled, which makes it unsuitable for embedded targets # disabling default-features fixes this serde = { version = "1.0.*", default-features = false } ``` ## License Licensed under either of - Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or ) - MIT license ([LICENSE-MIT](LICENSE-MIT) or ) at your option. ### Contribution Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. postcard-1.0.10/src/accumulator.rs000064400000000000000000000250601046102023000152010ustar 00000000000000//! 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::(&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 { 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 Default for CobsAccumulator { fn default() -> Self { Self::new() } } impl CobsAccumulator { /// 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::(&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] 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] 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] 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] 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::(&data[..]), FeedResult::OverFull(_) )); // Accumulator is juuuuust right let mut acc: CobsAccumulator<12> = CobsAccumulator::new(); assert!(matches!( acc.feed::(&data[..]), FeedResult::Success { .. } )); } } postcard-1.0.10/src/de/deserializer.rs000064400000000000000000000367171046102023000157470ustar 00000000000000use 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 { 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 { 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 { 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 { self.try_take_varint_u64().map(|u| u as usize) } #[inline] fn try_take_varint_u16(&mut self) -> Result { let mut out = 0; for i in 0..varint_max::() { let val = self.flavor.pop()?; let carry = (val & 0x7F) as u16; out |= carry << (7 * i); if (val & 0x80) == 0 { if i == varint_max::() - 1 && val > max_of_last_byte::() { return Err(Error::DeserializeBadVarint); } else { return Ok(out); } } } Err(Error::DeserializeBadVarint) } #[inline] fn try_take_varint_u32(&mut self) -> Result { let mut out = 0; for i in 0..varint_max::() { let val = self.flavor.pop()?; let carry = (val & 0x7F) as u32; out |= carry << (7 * i); if (val & 0x80) == 0 { if i == varint_max::() - 1 && val > max_of_last_byte::() { return Err(Error::DeserializeBadVarint); } else { return Ok(out); } } } Err(Error::DeserializeBadVarint) } #[inline] fn try_take_varint_u64(&mut self) -> Result { let mut out = 0; for i in 0..varint_max::() { let val = self.flavor.pop()?; let carry = (val & 0x7F) as u64; out |= carry << (7 * i); if (val & 0x80) == 0 { if i == varint_max::() - 1 && val > max_of_last_byte::() { return Err(Error::DeserializeBadVarint); } else { return Ok(out); } } } Err(Error::DeserializeBadVarint) } #[inline] fn try_take_varint_u128(&mut self) -> Result { let mut out = 0; for i in 0..varint_max::() { let val = self.flavor.pop()?; let carry = (val & 0x7F) as u128; out |= carry << (7 * i); if (val & 0x80) == 0 { if i == varint_max::() - 1 && val > max_of_last_byte::() { return Err(Error::DeserializeBadVarint); } else { return Ok(out); } } } Err(Error::DeserializeBadVarint) } } struct SeqAccess<'a, 'b: 'a, 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>(&mut self, seed: V) -> Result> { if self.len > 0 { self.len -= 1; Ok(Some(DeserializeSeed::deserialize( seed, &mut *self.deserializer, )?)) } else { Ok(None) } } #[inline] fn size_hint(&self) -> Option { match self.deserializer.flavor.size_hint() { Some(size) if size < self.len => None, _ => Some(self.len), } } } struct MapAccess<'a, 'b: 'a, 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>(&mut self, seed: K) -> Result> { if self.len > 0 { self.len -= 1; Ok(Some(DeserializeSeed::deserialize( seed, &mut *self.deserializer, )?)) } else { Ok(None) } } #[inline] fn next_value_seed>(&mut self, seed: V) -> Result { DeserializeSeed::deserialize(seed, &mut *self.deserializer) } #[inline] fn size_hint(&self) -> Option { Some(self.len) } } impl<'de, 'a, F: Flavor<'de>> de::Deserializer<'de> for &'a 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(self, _visitor: V) -> Result where V: Visitor<'de>, { // We wont ever support this. Err(Error::WontImplement) } // Take a boolean encoded as a u8 #[inline] fn deserialize_bool(self, visitor: V) -> Result 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(self, visitor: V) -> Result where V: Visitor<'de>, { visitor.visit_i8(self.flavor.pop()? as i8) } #[inline] fn deserialize_i16(self, visitor: V) -> Result where V: Visitor<'de>, { let v = self.try_take_varint_u16()?; visitor.visit_i16(de_zig_zag_i16(v)) } #[inline] fn deserialize_i32(self, visitor: V) -> Result where V: Visitor<'de>, { let v = self.try_take_varint_u32()?; visitor.visit_i32(de_zig_zag_i32(v)) } #[inline] fn deserialize_i64(self, visitor: V) -> Result where V: Visitor<'de>, { let v = self.try_take_varint_u64()?; visitor.visit_i64(de_zig_zag_i64(v)) } #[inline] fn deserialize_i128(self, visitor: V) -> Result where V: Visitor<'de>, { let v = self.try_take_varint_u128()?; visitor.visit_i128(de_zig_zag_i128(v)) } #[inline] fn deserialize_u8(self, visitor: V) -> Result where V: Visitor<'de>, { visitor.visit_u8(self.flavor.pop()?) } #[inline] fn deserialize_u16(self, visitor: V) -> Result where V: Visitor<'de>, { let v = self.try_take_varint_u16()?; visitor.visit_u16(v) } #[inline] fn deserialize_u32(self, visitor: V) -> Result where V: Visitor<'de>, { let v = self.try_take_varint_u32()?; visitor.visit_u32(v) } #[inline] fn deserialize_u64(self, visitor: V) -> Result where V: Visitor<'de>, { let v = self.try_take_varint_u64()?; visitor.visit_u64(v) } #[inline] fn deserialize_u128(self, visitor: V) -> Result where V: Visitor<'de>, { let v = self.try_take_varint_u128()?; visitor.visit_u128(v) } #[inline] fn deserialize_f32(self, visitor: V) -> Result where V: Visitor<'de>, { let bytes = self.flavor.try_take_n(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(self, visitor: V) -> Result where V: Visitor<'de>, { let bytes = self.flavor.try_take_n(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(self, visitor: V) -> Result where V: Visitor<'de>, { let sz = self.try_take_varint_usize()?; if sz > 4 { return Err(Error::DeserializeBadChar); } let bytes: &'de [u8] = self.flavor.try_take_n(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(self, visitor: V) -> Result 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(self, visitor: V) -> Result where V: Visitor<'de>, { self.deserialize_str(visitor) } #[inline] fn deserialize_bytes(self, visitor: V) -> Result 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(self, visitor: V) -> Result where V: Visitor<'de>, { self.deserialize_bytes(visitor) } #[inline] fn deserialize_option(self, visitor: V) -> Result 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(self, visitor: V) -> Result 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(self, _name: &'static str, visitor: V) -> Result where V: Visitor<'de>, { self.deserialize_unit(visitor) } #[inline] fn deserialize_newtype_struct(self, _name: &'static str, visitor: V) -> Result where V: Visitor<'de>, { visitor.visit_newtype_struct(self) } #[inline] fn deserialize_seq(self, visitor: V) -> Result where V: Visitor<'de>, { let len = self.try_take_varint_usize()?; visitor.visit_seq(SeqAccess { deserializer: self, len, }) } #[inline] fn deserialize_tuple(self, len: usize, visitor: V) -> Result where V: Visitor<'de>, { visitor.visit_seq(SeqAccess { deserializer: self, len, }) } #[inline] fn deserialize_tuple_struct( self, _name: &'static str, len: usize, visitor: V, ) -> Result where V: Visitor<'de>, { self.deserialize_tuple(len, visitor) } #[inline] fn deserialize_map(self, visitor: V) -> Result where V: Visitor<'de>, { let len = self.try_take_varint_usize()?; visitor.visit_map(MapAccess { deserializer: self, len, }) } #[inline] fn deserialize_struct( self, _name: &'static str, fields: &'static [&'static str], visitor: V, ) -> Result where V: Visitor<'de>, { self.deserialize_tuple(fields.len(), visitor) } #[inline] fn deserialize_enum( self, _name: &'static str, _variants: &'static [&'static str], visitor: V, ) -> Result where V: Visitor<'de>, { visitor.visit_enum(self) } // As a binary format, Postcard does not encode identifiers #[inline] fn deserialize_identifier(self, _visitor: V) -> Result where V: Visitor<'de>, { // Will not support Err(Error::WontImplement) } #[inline] fn deserialize_ignored_any(self, _visitor: V) -> Result where V: Visitor<'de>, { // Will not support Err(Error::WontImplement) } } impl<'de, 'a, F: Flavor<'de>> serde::de::VariantAccess<'de> for &'a mut Deserializer<'de, F> { type Error = Error; #[inline] fn unit_variant(self) -> Result<()> { Ok(()) } #[inline] fn newtype_variant_seed>(self, seed: V) -> Result { DeserializeSeed::deserialize(seed, self) } #[inline] fn tuple_variant>(self, len: usize, visitor: V) -> Result { serde::de::Deserializer::deserialize_tuple(self, len, visitor) } #[inline] fn struct_variant>( self, fields: &'static [&'static str], visitor: V, ) -> Result { serde::de::Deserializer::deserialize_tuple(self, fields.len(), visitor) } } impl<'de, 'a, F: Flavor<'de>> serde::de::EnumAccess<'de> for &'a mut Deserializer<'de, F> { type Error = Error; type Variant = Self; #[inline] fn variant_seed>(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)) } postcard-1.0.10/src/de/flavors.rs000064400000000000000000000441461046102023000147340ustar 00000000000000//! # 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; /// 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 { None } /// Attempt to take the next `ct` bytes from the serialized message fn try_take_n(&mut self, ct: usize) -> Result<&'de [u8]>; /// Complete the deserialization process. /// /// This is typically called separately, after the `serde` deserialization /// has completed. fn finalize(self) -> Result; } /// 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 { Self { cursor: sli.as_ptr(), end: unsafe { sli.as_ptr().add(sli.len()) }, _pl: PhantomData, } } } impl<'de> Flavor<'de> for Slice<'de> { type Remainder = &'de [u8]; type Source = &'de [u8]; #[inline] fn pop(&mut self) -> Result { if self.cursor == self.end { Err(Error::DeserializeUnexpectedEnd) } else { unsafe { let res = Ok(*self.cursor); self.cursor = self.cursor.add(1); res } } } #[inline] fn size_hint(&self) -> Option { 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 { 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); 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 { Self { cursor: sli.as_mut_ptr(), end: unsafe { sli.as_ptr().add(sli.len()) }, _pl: PhantomData, } } #[inline] fn size(&self) -> usize { (self.end as usize) - (self.cursor as usize) } #[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 { unsafe { let sli = core::slice::from_raw_parts_mut(self.cursor, ct); self.cursor = self.cursor.add(ct); sli } }; Ok(buff) } fn complete(self) -> Result<&'de mut [u8]> { let remain = (self.end as usize) - (self.cursor as usize); 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, { pub(crate) 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 { let mut val = [0; 1]; self.reader .read(&mut val) .map_err(|_| Error::DeserializeUnexpectedEnd)?; Ok(val[0]) } #[inline] fn size_hint(&self) -> Option { Some(self.buff.size()) } #[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) } /// 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)) } } } /// 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, { pub(crate) 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 { let mut val = [0; 1]; self.reader .read(&mut val) .map_err(|_| Error::DeserializeUnexpectedEnd)?; Ok(val[0]) } #[inline] fn size_hint(&self) -> Option { Some(self.buff.size()) } #[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) } /// 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)) } } } } //////////////////////////////////////// // 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. /// /// The `crc` feature requires enabling to use this module. /// /// More on CRCs: . #[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; use paste::paste; /// 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 ),*) => { $( paste! { 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 { match self.flav.pop() { Ok(byte) => { self.digest.update(&[byte]); Ok(byte) } e @ Err(_) => e, } } #[inline] fn size_hint(&self) -> Option { 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 { 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 []<'a, T>(s: &'a [u8], digest: Digest<'a, $int>) -> Result 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 []<'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, u16, u32, u64, u128]; } postcard-1.0.10/src/de/mod.rs000064400000000000000000000472751046102023000140450ustar 00000000000000use 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 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 where T: Deserialize<'a>, { let sz = decode_in_place(s).map_err(|_| Error::DeserializeBadEncoding)?; from_bytes::(&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::(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 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 = 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() }> = 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() }> = 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() }> = 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() }> = 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::() + 1 + varint_max::() + varint_max::() + varint_max::(); let output: Vec = 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 = to_vec(input).unwrap(); assert_eq!( &[0x08, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08], output.deref() ); let out: Vec = from_bytes(output.deref()).unwrap(); assert_eq!(input, out.deref()); let mut input: Vec = Vec::new(); for i in 0..1024 { input.push((i & 0xFF) as u8).unwrap(); } let output: Vec = 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 = from_bytes(output.deref()).unwrap(); assert_eq!(input.deref(), de.deref()); } #[test] fn de_str() { let input: &str = "hello, postcard!"; let output: Vec = 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 = 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 = 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() }> = 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() }> = 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 = 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 = 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 = 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 = 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 = 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<'a> Serialize for ByteSliceStruct<'a> { fn serialize(&self, serializer: S) -> core::result::Result 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(deserializer: D) -> core::result::Result 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = to_vec(&()).unwrap(); assert_eq!(output.len(), 0); let _: () = from_bytes(output.deref()).unwrap(); } #[test] fn heapless_data() { let mut input: Vec = Vec::new(); input.extend_from_slice(&[0x01, 0x02, 0x03, 0x04]).unwrap(); let output: Vec = to_vec(&input).unwrap(); assert_eq!(&[0x04, 0x01, 0x02, 0x03, 0x04], output.deref()); let out: Vec = from_bytes(output.deref()).unwrap(); assert_eq!(out, input); let mut input: String<8> = String::new(); write!(&mut input, "helLO!").unwrap(); let output: Vec = 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 = 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 = to_vec(&input).unwrap(); assert_eq!( &[0x04, 0x01, 0x05, 0x02, 0x06, 0x03, 0x07, 0x04, 0x08], output.deref() ); let out: FnvIndexMap = 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 = to_vec(&input).unwrap(); let mut encode_buf = [0u8; 32]; let sz = cobs::encode(output.deref(), &mut encode_buf); let out = from_bytes_cobs::(&mut encode_buf[..sz]).unwrap(); assert_eq!(input, out); } #[test] fn take_from_includes_terminator() { // With the null terminator let mut output: Vec = 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 = 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::>(&[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::>(&[(1 << 7) | 8, 255, 255, 255, 0, 0, 0, 0, 0]), Err(Error::DeserializeUnexpectedEnd) ); } } postcard-1.0.10/src/eio.rs000064400000000000000000000014431046102023000134350ustar 00000000000000// 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}; postcard-1.0.10/src/error.rs000064400000000000000000000066241046102023000140200ustar 00000000000000use core::fmt::{Display, Formatter}; /// This is the error type used by Postcard #[derive(Clone, Debug, Eq, PartialEq)] #[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_value() 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 = ::core::result::Result; impl serde::ser::Error for Error { fn custom(_msg: T) -> Self where T: Display, { Error::SerdeSerCustom } } impl serde::de::Error for Error { fn custom(_msg: T) -> Self where T: Display, { Error::SerdeDeCustom } } impl serde::ser::StdError for Error {} postcard-1.0.10/src/fixint.rs000064400000000000000000000132641046102023000141660ustar 00000000000000//! # Fixed Size Integers //! //! In some cases, the use of variably length encoded data may not be //! preferrable. 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(val: &T, serializer: S) -> Result where S: Serializer, T: Copy, LE: 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 where D: Deserializer<'de>, LE: Deserialize<'de>, { LE::::deserialize(deserializer).map(|x| x.0) } } /// Use with the `#[serde(with = "postcard::fixint::be")]` 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 **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(val: &T, serializer: S) -> Result where S: Serializer, T: Copy, BE: 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 where D: Deserializer<'de>, BE: Deserialize<'de>, { BE::::deserialize(deserializer).map(|x| x.0) } } #[doc(hidden)] pub struct LE(T); #[doc(hidden)] pub struct BE(T); macro_rules! impl_fixint { ($( $int:ty ),*) => { $( impl Serialize for LE<$int> { #[inline] fn serialize(&self, serializer: S) -> Result where S: Serializer, { self.0.to_le_bytes().serialize(serializer) } } impl<'de> Deserialize<'de> for LE<$int> { #[inline] fn deserialize(deserializer: D) -> Result where D: serde::Deserializer<'de>, { <_ as Deserialize>::deserialize(deserializer) .map(<$int>::from_le_bytes) .map(Self) } } impl Serialize for BE<$int> { #[inline] fn serialize(&self, serializer: S) -> Result where S: Serializer, { self.0.to_be_bytes().serialize(serializer) } } impl<'de> Deserialize<'de> for BE<$int> { #[inline] fn deserialize(deserializer: D) -> Result 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); } } postcard-1.0.10/src/lib.rs000064400000000000000000000115071046102023000134310ustar 00000000000000#![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; /// The schema types and macros. #[cfg(feature = "experimental-derive")] pub(crate) mod schema; /// # 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 two experimental features: /// /// * Max size calculation /// * Message schema generation /// /// ### 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 recieving /// 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 feature enables the generation of a schema of a given message at compile /// time. At the moment, this is only exposed as a [`NamedType`](crate::experimental::schema::NamedType) /// which is a recursive data structure describing the schema. In the future, it is planned /// to provide formatting functions that emit this as a human or machine readable schema. /// /// NOTE: This only covers the schema of "plain" flavored messages, e.g. not with COBS /// or any other Flavors applied. The format of these flavors must be calculated /// separately. /// /// Please report any missing types, or any incorrectly calculated schemas. 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; /// Compile time Schema generation #[cfg(feature = "experimental-derive")] #[cfg_attr(docsrs, doc(cfg(feature = "experimental-derive")))] pub mod schema { // NOTE: This is the trait... pub use crate::schema::{NamedType, NamedValue, NamedVariant, Schema, SdmTy, Varint}; // NOTE: ...and this is the derive macro pub use postcard_derive::Schema; } } 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 = 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); } } postcard-1.0.10/src/max_size.rs000064400000000000000000000172151046102023000145040ustar 00000000000000#[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, }, }; /// 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::(); } impl MaxSize for i32 { const POSTCARD_MAX_SIZE: usize = varint_max::(); } impl MaxSize for i64 { const POSTCARD_MAX_SIZE: usize = varint_max::(); } impl MaxSize for i128 { const POSTCARD_MAX_SIZE: usize = varint_max::(); } impl MaxSize for isize { const POSTCARD_MAX_SIZE: usize = varint_max::(); } impl MaxSize for u8 { const POSTCARD_MAX_SIZE: usize = 1; } impl MaxSize for u16 { const POSTCARD_MAX_SIZE: usize = varint_max::(); } impl MaxSize for u32 { const POSTCARD_MAX_SIZE: usize = varint_max::(); } impl MaxSize for u64 { const POSTCARD_MAX_SIZE: usize = varint_max::(); } impl MaxSize for u128 { const POSTCARD_MAX_SIZE: usize = varint_max::(); } impl MaxSize for usize { const POSTCARD_MAX_SIZE: usize = varint_max::(); } 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 MaxSize for Option { const POSTCARD_MAX_SIZE: usize = T::POSTCARD_MAX_SIZE + 1; } impl MaxSize for Result { 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 MaxSize for [T; N] { const POSTCARD_MAX_SIZE: usize = T::POSTCARD_MAX_SIZE * N; } impl MaxSize for &'_ T { const POSTCARD_MAX_SIZE: usize = T::POSTCARD_MAX_SIZE; } impl 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 PhantomData { const POSTCARD_MAX_SIZE: usize = 0; } impl MaxSize for (A,) { const POSTCARD_MAX_SIZE: usize = A::POSTCARD_MAX_SIZE; } impl MaxSize for (A, B) { const POSTCARD_MAX_SIZE: usize = A::POSTCARD_MAX_SIZE + B::POSTCARD_MAX_SIZE; } impl MaxSize for (A, B, C) { const POSTCARD_MAX_SIZE: usize = A::POSTCARD_MAX_SIZE + B::POSTCARD_MAX_SIZE + C::POSTCARD_MAX_SIZE; } impl 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 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 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; } #[cfg(feature = "alloc")] #[cfg_attr(docsrs, doc(cfg(feature = "alloc")))] impl MaxSize for Box { 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 MaxSize for Arc { const POSTCARD_MAX_SIZE: usize = T::POSTCARD_MAX_SIZE; } #[cfg(feature = "alloc")] #[cfg_attr(docsrs, doc(cfg(feature = "alloc")))] impl MaxSize for Rc { const POSTCARD_MAX_SIZE: usize = T::POSTCARD_MAX_SIZE; } #[cfg(feature = "heapless")] #[cfg_attr(docsrs, doc(cfg(feature = "heapless")))] impl MaxSize for heapless::Vec { const POSTCARD_MAX_SIZE: usize = <[T; N]>::POSTCARD_MAX_SIZE + varint_size(N); } #[cfg(feature = "heapless")] #[cfg_attr(docsrs, doc(cfg(feature = "heapless")))] impl MaxSize for heapless::String { const POSTCARD_MAX_SIZE: usize = <[u8; N]>::POSTCARD_MAX_SIZE + varint_size(N); } #[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::() * 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::::POSTCARD_MAX_SIZE, 1); assert_eq!(Box::::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::::POSTCARD_MAX_SIZE, 1); assert_eq!(Arc::::POSTCARD_MAX_SIZE, 5); assert_eq!(Arc::<(u128, [u8; 8])>::POSTCARD_MAX_SIZE, 27); } #[test] fn rc_max_size() { assert_eq!(Rc::::POSTCARD_MAX_SIZE, 1); assert_eq!(Rc::::POSTCARD_MAX_SIZE, 5); assert_eq!(Rc::<(u128, [u8; 8])>::POSTCARD_MAX_SIZE, 27); } } postcard-1.0.10/src/schema.rs000064400000000000000000000163021046102023000141210ustar 00000000000000use serde::Serialize; /// A schema type representing a variably encoded integer #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize)] pub enum Varint { /// A variably encoded i16 I16, /// A variably encoded i32 I32, /// A variably encoded i64 I64, /// A variably encoded i128 I128, /// A variably encoded u16 U16, /// A variably encoded u32 U32, /// A variably encoded u64 U64, /// A variably encoded u128 U128, /// A variably encoded usize Usize, /// A variably encoded isize Isize, } /// Serde Data Model Types (and friends) #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize)] pub enum SdmTy { /// The `bool` Serde Data Model Type Bool, /// The `i8` Serde Data Model Type I8, /// The `u8` Serde Data Model Type U8, /// The Serde Data Model Type for variably length encoded integers Varint(Varint), /// The `f32` Serde Data Model Type F32, /// The `f64 Serde Data Model Type F64, /// The `char` Serde Data Model Type Char, /// The `String` Serde Data Model Type String, /// The `[u8; N]` Serde Data Model Type ByteArray, /// The `Option` Serde Data Model Type Option(&'static NamedType), /// The `()` Serde Data Model Type Unit, /// The "unit struct" Serde Data Model Type UnitStruct, /// The "unit variant" Serde Data Model Type UnitVariant, /// The "newtype struct" Serde Data Model Type NewtypeStruct(&'static NamedType), /// The "newtype variant" Serde Data Model Type NewtypeVariant(&'static NamedType), /// The "Sequence" Serde Data Model Type Seq(&'static NamedType), /// The "Tuple" Serde Data Model Type Tuple(&'static [&'static NamedType]), /// The "Tuple Struct" Serde Data Model Type TupleStruct(&'static [&'static NamedType]), /// The "Tuple Variant" Serde Data Model Type TupleVariant(&'static [&'static NamedType]), /// The "Map" Serde Data Model Type Map { /// The map "Key" type key: &'static NamedType, /// The map "Value" type val: &'static NamedType, }, /// The "Struct" Serde Data Model Type Struct(&'static [&'static NamedValue]), /// The "Struct Variant" Serde Data Model Type StructVariant(&'static [&'static NamedValue]), /// The "Enum" Serde Data Model Type (which contains any of the "Variant" types) Enum(&'static [&'static NamedVariant]), } /// A data type with a name - e.g. a field of a Struct #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize)] pub struct NamedValue { /// The name of this value pub name: &'static str, /// The type of this value pub ty: &'static NamedType, } /// A data type - e.g. a custom `struct Foo{ ... }` type #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize)] pub struct NamedType { /// The name of this type pub name: &'static str, /// The type pub ty: &'static SdmTy, } /// An enum variant with a name, e.g. `T::Bar(...)` #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize)] pub struct NamedVariant { /// The name of this variant pub name: &'static str, /// The type of this variant pub ty: &'static SdmTy, } /// A trait that represents a compile time calculated schema pub trait Schema { /// A recursive data structure that describes the schema of the given /// type. const SCHEMA: &'static NamedType; } macro_rules! impl_schema { ($($t:ty: $sdm:expr),*) => { $( impl Schema for $t { const SCHEMA: &'static NamedType = &NamedType { name: stringify!($t), ty: &$sdm, }; } )* }; (varint => [$($t:ty: $varint:expr),*]) => { impl_schema!($($t: SdmTy::Varint($varint)),*); }; (tuple => [$(($($generic:ident),*)),*]) => { $( impl<$($generic: Schema),*> Schema for ($($generic,)*) { const SCHEMA: &'static NamedType = &NamedType { name: stringify!(($($generic,)*)), ty: &SdmTy::Tuple(&[$($generic::SCHEMA),*]), }; } )* }; } impl_schema![ u8: SdmTy::U8, i8: SdmTy::I8, bool: SdmTy::Bool, f32: SdmTy::F32, f64: SdmTy::F64, char: SdmTy::Char, str: SdmTy::String, (): SdmTy::Unit ]; impl_schema!(varint => [ i16: Varint::I16, i32: Varint::I32, i64: Varint::I64, i128: Varint::I128, u16: Varint::U16, u32: Varint::U32, u64: Varint::U64, u128: Varint::U128 ]); impl_schema!(tuple => [ (A), (A, B), (A, B, C), (A, B, C, D), (A, B, C, D, E), (A, B, C, D, E, F) ]); impl Schema for Option { const SCHEMA: &'static NamedType = &NamedType { name: "Option", ty: &SdmTy::Option(T::SCHEMA), }; } impl Schema for Result { const SCHEMA: &'static NamedType = &NamedType { name: "Result", ty: &SdmTy::Enum(&[ &NamedVariant { name: "Ok", ty: &SdmTy::TupleVariant(&[T::SCHEMA]), }, &NamedVariant { name: "Err", ty: &SdmTy::TupleVariant(&[E::SCHEMA]), }, ]), }; } impl Schema for &'_ T { const SCHEMA: &'static NamedType = T::SCHEMA; } impl Schema for [T] { const SCHEMA: &'static NamedType = &NamedType { name: "&[T]", ty: &SdmTy::Seq(T::SCHEMA), }; } impl Schema for [T; N] { const SCHEMA: &'static NamedType = &NamedType { name: "[T; N]", ty: &SdmTy::Tuple(&[T::SCHEMA; N]), }; } #[cfg(feature = "heapless")] #[cfg_attr(docsrs, doc(cfg(feature = "heapless")))] impl Schema for heapless::Vec { const SCHEMA: &'static NamedType = &NamedType { name: "heapless::Vec", ty: &SdmTy::Seq(T::SCHEMA), }; } #[cfg(feature = "heapless")] #[cfg_attr(docsrs, doc(cfg(feature = "heapless")))] impl Schema for heapless::String { const SCHEMA: &'static NamedType = &NamedType { name: "heapless::String", ty: &SdmTy::String, }; } #[cfg(feature = "use-std")] #[cfg_attr(docsrs, doc(cfg(any(feature = "alloc", feature = "use-std"))))] impl Schema for std::vec::Vec { const SCHEMA: &'static NamedType = &NamedType { name: "Vec", ty: &SdmTy::Seq(T::SCHEMA), }; } #[cfg(feature = "use-std")] #[cfg_attr(docsrs, doc(cfg(any(feature = "alloc", feature = "use-std"))))] impl Schema for std::string::String { const SCHEMA: &'static NamedType = &NamedType { name: "String", ty: &SdmTy::String, }; } #[cfg(all(not(feature = "use-std"), feature = "alloc"))] extern crate alloc; #[cfg(all(not(feature = "use-std"), feature = "alloc"))] impl Schema for alloc::vec::Vec { const SCHEMA: &'static NamedType = &NamedType { name: "Vec", ty: &SdmTy::Seq(T::SCHEMA), }; } #[cfg(all(not(feature = "use-std"), feature = "alloc"))] impl Schema for alloc::string::String { const SCHEMA: &'static NamedType = &NamedType { name: "String", ty: &SdmTy::String, }; } postcard-1.0.10/src/ser/flavors.rs000064400000000000000000000526271046102023000151400ustar 00000000000000//! # 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, &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; /// The try_extend() trait method can be implemented when there is a more efficient way of 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)) } /// The try_push() trait method can be used to 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; } //////////////////////////////////////// // 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(); Slice { start: ptr, cursor: ptr, end: unsafe { ptr.add(buf.len()) }, _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 { 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 { unsafe { core::ptr::copy_nonoverlapping(b.as_ptr(), self.cursor, blen); self.cursor = self.cursor.add(blen); } Ok(()) } } fn finalize(self) -> Result { let used = (self.cursor as usize) - (self.start as usize); let sli = unsafe { core::slice::from_raw_parts_mut(self.start, used) }; Ok(sli) } } impl<'a> Index for Slice<'a> { type Output = u8; fn index(&self, idx: usize) -> &u8 { let len = (self.end as usize) - (self.start as usize); assert!(idx < len); unsafe { &*self.start.add(idx) } } } impl<'a> IndexMut for Slice<'a> { fn index_mut(&mut self, idx: usize) -> &mut u8 { let len = (self.end as usize) - (self.start as usize); assert!(idx < len); unsafe { &mut *self.start.add(idx) } } } /// Wrapper over a [`std::iter::Extend`] that implements the flavor trait pub struct ExtendFlavor { iter: T, } impl ExtendFlavor where T: core::iter::Extend, { /// Create a new [Self] flavor from a given [`std::iter::Extend`] pub fn new(iter: T) -> Self { Self { iter } } } impl Flavor for ExtendFlavor where T: core::iter::Extend, { 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().cloned()); Ok(()) } fn finalize(self) -> Result { 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 { writer: T, } impl WriteFlavor 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 Flavor for WriteFlavor 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.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 { writer: T, } impl WriteFlavor 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 Flavor for WriteFlavor 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.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 { /// the contained data buffer vec: Vec, } impl HVec { /// Create a new, currently empty, [heapless::Vec] to be used for storing serialized /// output data. pub fn new() -> Self { Self::default() } } impl Flavor for HVec { type Output = Vec; #[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> { Ok(self.vec) } } impl Index for HVec { type Output = u8; fn index(&self, idx: usize) -> &u8 { &self.vec[idx] } } impl IndexMut for HVec { 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, } 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; #[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 { Ok(self.vec) } } impl Index for AllocVec { type Output = u8; #[inline] fn index(&self, idx: usize) -> &u8 { &self.vec[idx] } } impl IndexMut 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 where B: Flavor + IndexMut, { flav: B, cobs: EncoderState, } impl Cobs where B: Flavor + IndexMut, { /// 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 { bee.try_push(0).map_err(|_| Error::SerializeBufferFull)?; Ok(Self { flav: bee, cobs: EncoderState::default(), }) } } impl Flavor for Cobs where B: Flavor + IndexMut, { type Output = ::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 { 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. /// /// The `crc` feature requires enabling to use this module. /// /// More on CRCs: . #[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; use paste::paste; /// 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 ),*) => { $( paste! { impl<'a, B> Flavor for CrcModifier<'a, B, $int> where B: Flavor, { type Output = ::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 { 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 []<'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`, 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 []( value: &T, digest: Digest<'_, $int>, ) -> Result> where T: Serialize + ?Sized, { use super::HVec; serialize_with_flavor(value, CrcModifier::new(HVec::default(), digest)) } /// Serialize a `T` to a `heapless::Vec`, 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 [](value: &T, digest: Digest<'_, $int>) -> Result> where T: Serialize + ?Sized, { use super::AllocVec; serialize_with_flavor(value, CrcModifier::new(AllocVec::new(), digest)) } } )* }; } impl_flavor![u8, u16, u32, u64, 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 { Ok(self.size) } } postcard-1.0.10/src/ser/mod.rs000064400000000000000000000656671046102023000142530ustar 00000000000000use 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::>, &'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::, &'a mut [u8]>(value, Slice::new(buf)) } /// Serialize a `T` to a `heapless::Vec`, 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 = to_vec_cobs(&false).unwrap(); /// assert_eq!(ser.deref(), &[0x01, 0x01, 0x00]); /// /// let ser: Vec = 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 = 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 = 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(value: &T) -> Result> where T: Serialize + ?Sized, { serialize_with_flavor::>, Vec>(value, Cobs::try_new(HVec::default())?) } /// Serialize a `T` to a `heapless::Vec`, 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 = to_vec(&true).unwrap(); /// assert_eq!(ser.deref(), &[0x01]); /// /// let ser: Vec = 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 = to_vec(data).unwrap(); /// assert_eq!(ser.deref(), &[0x04, 0x01, 0x00, 0x20, 0x30]); /// /// let data: &[u8; 4] = &[0x01u8, 0x00, 0x20, 0x30]; /// let ser: Vec = 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(value: &T) -> Result> where T: Serialize + ?Sized, { serialize_with_flavor::, Vec>(value, HVec::default()) } /// Serialize a `T` to a `std::vec::Vec`. /// /// ## Example /// /// ```rust /// use postcard::to_stdvec; /// /// let ser: Vec = to_stdvec(&true).unwrap(); /// assert_eq!(ser.as_slice(), &[0x01]); /// /// let ser: Vec = 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(value: &T) -> Result> where T: Serialize + ?Sized, { to_allocvec(value) } /// Serialize and COBS encode a `T` to a `std::vec::Vec`. /// /// The terminating sentinel `0x00` byte is included in the output. /// /// ## Example /// /// ```rust /// use postcard::to_stdvec_cobs; /// /// let ser: Vec = to_stdvec_cobs(&true).unwrap(); /// assert_eq!(ser.as_slice(), &[0x02, 0x01, 0x00]); /// /// let ser: Vec = 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(value: &T) -> Result> where T: Serialize + ?Sized, { to_allocvec_cobs(value) } /// Serialize a `T` to an `alloc::vec::Vec`. /// /// ## Example /// /// ```rust /// use postcard::to_allocvec; /// /// let ser: Vec = to_allocvec(&true).unwrap(); /// assert_eq!(ser.as_slice(), &[0x01]); /// /// let ser: Vec = 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(value: &T) -> Result> where T: Serialize + ?Sized, { serialize_with_flavor::>(value, AllocVec::new()) } /// Serialize and COBS encode a `T` to an `alloc::vec::Vec`. /// /// The terminating sentinel `0x00` byte is included in the output. /// /// ## Example /// /// ```rust /// use postcard::to_allocvec_cobs; /// /// let ser: Vec = to_allocvec_cobs(&true).unwrap(); /// assert_eq!(ser.as_slice(), &[0x02, 0x01, 0x00]); /// /// let ser: Vec = 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(value: &T) -> Result> where T: Serialize + ?Sized, { serialize_with_flavor::, alloc::vec::Vec>( 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(value: &T, writer: W) -> Result where T: Serialize + ?Sized, W: core::iter::Extend, { serialize_with_flavor::(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<'b, T, W>(value: &'b T, writer: W) -> Result where T: Serialize + ?Sized, W: crate::eio::Write, { serialize_with_flavor::(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(value: &T, writer: W) -> Result where T: Serialize + ?Sized, W: std::io::Write, { serialize_with_flavor::(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::::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`, 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::::new(&CRC_32_ISCSI); /// let ser: Vec = 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 = 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( value: &T, digest: crc::Digest<'_, u32>, ) -> Result> where T: Serialize + ?Sized, { flavors::crc::to_vec_u32(value, digest) } /// Conveniently serialize a `T` to a `heapless::Vec`, 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::::new(&CRC_32_ISCSI); /// let ser: Vec = 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 = 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(value: &T, digest: crc::Digest<'_, u32>) -> Result> where T: Serialize + ?Sized, { flavors::crc::to_allocvec_u32(value, digest) } /// Conveniently serialize a `T` to a `heapless::Vec`, 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::::new(&CRC_32_ISCSI); /// let ser: Vec = 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 = 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(value: &T, digest: crc::Digest<'_, u32>) -> Result> 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, &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(value: &T, storage: S) -> Result where T: Serialize + ?Sized, S: Flavor, { 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(value: &T) -> Result where T: Serialize + ?Sized, { serialize_with_flavor::(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 = to_vec(&0x05u8).unwrap(); assert_eq!(&[5], output.deref()); assert!(output.len() == serialized_size(&0x05u8).unwrap()); assert!(output.len() <= Vec::::POSTCARD_MAX_SIZE); } #[test] fn ser_u16() { const SZ: usize = varint_max::(); let output: Vec = to_vec(&0xA5C7u16).unwrap(); assert_eq!(&[0xC7, 0xCB, 0x02], output.deref()); assert!(output.len() == serialized_size(&0xA5C7u16).unwrap()); assert!(output.len() <= Vec::::POSTCARD_MAX_SIZE); } #[test] fn ser_u32() { const SZ: usize = varint_max::(); let output: Vec = to_vec(&0xCDAB3412u32).unwrap(); assert_eq!(&[0x92, 0xE8, 0xAC, 0xED, 0x0C], output.deref()); assert!(output.len() == serialized_size(&0xCDAB3412u32).unwrap()); assert!(output.len() <= Vec::::POSTCARD_MAX_SIZE); } #[test] fn ser_u64() { const SZ: usize = varint_max::(); let output: Vec = 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::::POSTCARD_MAX_SIZE); } #[test] fn ser_u128() { const SZ: usize = varint_max::(); let output: Vec = 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::::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 = 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 = 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 = Vec::new(); for i in 0..1024 { input.push((i & 0xFF) as u8).unwrap(); } let output: Vec = 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 = 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 = 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::()]; let res = varint_usize(1, &mut buf); assert_eq!(&[1], res); let res = varint_usize(usize::MAX, &mut buf); // if varint_max::() == varint_max::() { assert_eq!(&[0xFF, 0xFF, 0xFF, 0xFF, 0x0F], res); } else if varint_max::() == varint_max::() { 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 = 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() }> = 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() }> = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = to_vec(&input).unwrap(); assert_eq!(&[0x05], output.deref()); assert!(output.len() == serialized_size(&input).unwrap()); let input = TupleStruct((0xA0, 0x1234)); let output: Vec = 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 = 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 = to_vec(&()).unwrap(); assert_eq!(output.len(), 0); assert!(output.len() == serialized_size(&()).unwrap()); } #[test] fn heapless_data() { let mut input: Vec = Vec::new(); input.extend_from_slice(&[0x01, 0x02, 0x03, 0x04]).unwrap(); let output: Vec = 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 = 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 = 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 = 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 = to_vec_cobs(&input).unwrap(); let sz = cobs::decode_in_place(output.deref_mut()).unwrap(); let x = crate::from_bytes::(&output.deref_mut()[..sz]).unwrap(); assert_eq!(input, x); } } postcard-1.0.10/src/ser/serializer.rs000064400000000000000000000347661046102023000156410ustar 00000000000000use 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`]: trait.SerFlavor.html pub struct Serializer 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 Serializer { /// 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::()]; 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::()]; 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::()]; 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::()]; 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::()]; let used_buf = varint_u16(data, &mut buf); self.output.try_extend(used_buf) } } impl<'a, F> ser::Serializer for &'a mut Serializer 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(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(self, _name: &'static str, value: &T) -> Result<()> where T: ?Sized + Serialize, { value.serialize(self) } #[inline] fn serialize_newtype_variant( 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) -> Result { self.try_push_varint_usize(len.ok_or(Error::SerializeSeqLengthUnknown)?) .map_err(|_| Error::SerializeBufferFull)?; Ok(self) } #[inline] fn serialize_tuple(self, _len: usize) -> Result { Ok(self) } #[inline] fn serialize_tuple_struct( self, _name: &'static str, _len: usize, ) -> Result { Ok(self) } #[inline] fn serialize_tuple_variant( self, _name: &'static str, variant_index: u32, _variant: &'static str, _len: usize, ) -> Result { self.try_push_varint_u32(variant_index) .map_err(|_| Error::SerializeBufferFull)?; Ok(self) } #[inline] fn serialize_map(self, len: Option) -> Result { 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 { Ok(self) } #[inline] fn serialize_struct_variant( self, _name: &'static str, variant_index: u32, _variant: &'static str, _len: usize, ) -> Result { self.try_push_varint_u32(variant_index) .map_err(|_| Error::SerializeBufferFull)?; Ok(self) } #[inline] fn collect_str(self, value: &T) -> Result 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.as_bytes().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<'a, IF> Write for FmtWriter<'a, 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<'a, F> ser::SerializeSeq for &'a mut Serializer 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(&mut self, value: &T) -> Result<()> where T: ?Sized + Serialize, { value.serialize(&mut **self) } // Close the sequence. #[inline] fn end(self) -> Result<()> { Ok(()) } } impl<'a, F> ser::SerializeTuple for &'a mut Serializer where F: Flavor, { type Ok = (); type Error = Error; #[inline] fn serialize_element(&mut self, value: &T) -> Result<()> where T: ?Sized + Serialize, { value.serialize(&mut **self) } #[inline] fn end(self) -> Result<()> { Ok(()) } } impl<'a, F> ser::SerializeTupleStruct for &'a mut Serializer where F: Flavor, { type Ok = (); type Error = Error; #[inline] fn serialize_field(&mut self, value: &T) -> Result<()> where T: ?Sized + Serialize, { value.serialize(&mut **self) } #[inline] fn end(self) -> Result<()> { Ok(()) } } impl<'a, F> ser::SerializeTupleVariant for &'a mut Serializer where F: Flavor, { type Ok = (); type Error = Error; #[inline] fn serialize_field(&mut self, value: &T) -> Result<()> where T: ?Sized + Serialize, { value.serialize(&mut **self) } #[inline] fn end(self) -> Result<()> { Ok(()) } } impl<'a, F> ser::SerializeMap for &'a mut Serializer where F: Flavor, { type Ok = (); type Error = Error; #[inline] fn serialize_key(&mut self, key: &T) -> Result<()> where T: ?Sized + Serialize, { key.serialize(&mut **self) } #[inline] fn serialize_value(&mut self, value: &T) -> Result<()> where T: ?Sized + Serialize, { value.serialize(&mut **self) } #[inline] fn end(self) -> Result<()> { Ok(()) } } impl<'a, F> ser::SerializeStruct for &'a mut Serializer where F: Flavor, { type Ok = (); type Error = Error; #[inline] fn serialize_field(&mut self, _key: &'static str, value: &T) -> Result<()> where T: ?Sized + Serialize, { value.serialize(&mut **self) } #[inline] fn end(self) -> Result<()> { Ok(()) } } impl<'a, F> ser::SerializeStructVariant for &'a mut Serializer where F: Flavor, { type Ok = (); type Error = Error; #[inline] fn serialize_field(&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 } postcard-1.0.10/src/varint.rs000064400000000000000000000052441046102023000141670ustar 00000000000000/// Returns the maximum number of bytes required to encode T. pub const fn varint_max() -> 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::() * 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() -> u8 { let max_bits = core::mem::size_of::() * 8; let extra_bits = max_bits % 7; (1 << extra_bits) - 1 } #[inline] pub fn varint_usize(n: usize, out: &mut [u8; varint_max::()]) -> &mut [u8] { let mut value = n; for i in 0..varint_max::() { 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::()]) -> &mut [u8] { let mut value = n; for i in 0..varint_max::() { 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::()]) -> &mut [u8] { let mut value = n; for i in 0..varint_max::() { 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::()]) -> &mut [u8] { let mut value = n; for i in 0..varint_max::() { 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::()]) -> &mut [u8] { let mut value = n; for i in 0..varint_max::() { 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[..] } postcard-1.0.10/tests/accumulator.rs000064400000000000000000000032261046102023000155540ustar 00000000000000use postcard::accumulator::{CobsAccumulator, FeedResult}; use serde::{Deserialize, Serialize}; use std::io::Read; // Read a "huge" serialized struct in 32 byte chunks into a 256 byte buffer and deserialize it. #[test] fn reader() { let mut raw_buf = [0u8; 32]; let mut input_buf = [0u8; 256]; let mut cobs_buf: CobsAccumulator<256> = CobsAccumulator::new(); #[derive(Serialize, Deserialize, Debug, PartialEq, Eq)] struct Huge { a: u32, b: [u32; 32], d: u64, } let expected = Huge { a: 0xabcdef00, b: [0x01234567; 32], d: u64::MAX, }; let input = postcard::to_slice_cobs(&expected, &mut input_buf).unwrap(); // TODO(https://github.com/rust-lang/rust-clippy/issues/12751): Remove once fixed. #[allow(clippy::redundant_slicing)] let mut input = &input[..]; // Magic number from serializing struct and printing length assert_eq!(input.len(), 145); let mut output = None; 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::(window) { FeedResult::Consumed => break 'cobs, FeedResult::OverFull(new_wind) => new_wind, FeedResult::DeserError(new_wind) => new_wind, FeedResult::Success { data, remaining } => { output = Some(data); remaining } }; } } assert_eq!(output.unwrap(), expected); } postcard-1.0.10/tests/crc.rs000064400000000000000000000050031046102023000137770ustar 00000000000000#[test] #[cfg(feature = "use-crc")] fn test_crc() { use crc::{Crc, CRC_32_ISCSI}; let data: &[u8] = &[0x01, 0x00, 0x20, 0x30]; let buffer = &mut [0u8; 32]; let crc = Crc::::new(&CRC_32_ISCSI); let digest = crc.digest(); let res = postcard::to_slice_crc32(data, buffer, digest).unwrap(); assert_eq!(res, &[0x04, 0x01, 0x00, 0x20, 0x30, 0x8E, 0xC8, 0x1A, 0x37]); let digest = crc.digest(); let res = postcard::take_from_bytes_crc32::<[u8; 5]>(res, digest).unwrap(); let expected_bytes = [0x04, 0x01, 0x00, 0x20, 0x30]; let remaining_bytes = []; assert_eq!(res, (expected_bytes, remaining_bytes.as_slice())); } #[test] #[cfg(feature = "use-crc")] fn test_crc_8() { use crc::{Crc, CRC_8_SMBUS}; let data: &[u8] = &[0x01, 0x00, 0x20, 0x30]; let buffer = &mut [0u8; 32]; let crc = Crc::::new(&CRC_8_SMBUS); let digest = crc.digest(); let res = postcard::ser_flavors::crc::to_slice_u8(data, buffer, digest).unwrap(); assert_eq!(res, &[0x04, 0x01, 0x00, 0x20, 0x30, 167]); let digest = crc.digest(); let res = postcard::de_flavors::crc::take_from_bytes_u8::<[u8; 5]>(res, digest).unwrap(); let expected_bytes = [0x04, 0x01, 0x00, 0x20, 0x30]; let remaining_bytes = []; assert_eq!(res, (expected_bytes, remaining_bytes.as_slice())); } #[test] #[cfg(feature = "use-crc")] fn test_crc_error() { use crc::{Crc, CRC_32_ISCSI}; let data: &[u8] = &[0x01, 0x00, 0x20, 0x30]; let buffer = &mut [0u8; 32]; let crc = Crc::::new(&CRC_32_ISCSI); let digest = crc.digest(); let res = postcard::to_slice_crc32(data, buffer, digest).unwrap(); // intentionally corrupt the crc let last = res.len() - 1; res[last] = 0; let digest = crc.digest(); let res = postcard::take_from_bytes_crc32::<[u8; 5]>(res, digest); assert_eq!(res, Err(postcard::Error::DeserializeBadCrc)); } #[test] #[cfg(feature = "use-crc")] fn test_crc_in_method() { use crc::{Crc, CRC_32_ISCSI}; use postcard::{to_slice_crc32, Result}; use serde::Serialize; #[derive(Debug, Serialize)] pub struct Thing { value: u32, } impl Thing { pub fn to_bytes<'a>(&self, buf: &'a mut [u8]) -> Result<&'a mut [u8]> { let crc = Crc::::new(&CRC_32_ISCSI); to_slice_crc32(self, buf, crc.digest()) } } let buffer = &mut [0u8; 5]; let thing = Thing { value: 42 }; let slice = thing.to_bytes(buffer).unwrap(); assert_eq!(slice, &[0x2A, 0xB7, 0xF5, 0x22, 0x19]); } postcard-1.0.10/tests/loopback.rs000064400000000000000000000152101046102023000150230ustar 00000000000000use core::fmt::Debug; use core::fmt::Write; use core::ops::Deref; #[cfg(feature = "heapless")] use heapless::{FnvIndexMap, String, Vec}; #[cfg(feature = "heapless")] use postcard::to_vec; use postcard::from_bytes; use serde::de::DeserializeOwned; use serde::{Deserialize, Serialize}; #[derive(Debug, Serialize, Deserialize, Eq, PartialEq)] struct BasicU8S { st: u16, ei: u8, sf: u64, tt: u32, } #[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), } #[derive(Serialize, Deserialize, Debug, Eq, PartialEq)] struct NewTypeStruct(u32); #[derive(Serialize, Deserialize, Debug, Eq, PartialEq)] struct TupleStruct((u8, u16)); #[derive(Serialize, Deserialize, Debug, Eq, PartialEq)] struct RefStruct<'a> { bytes: &'a [u8], str_s: &'a str, } #[cfg(feature = "heapless")] #[test] fn loopback() { // Basic types test_one((), &[]); test_one(false, &[0x00]); test_one(true, &[0x01]); test_one(5u8, &[0x05]); test_one(0xA5C7u16, &[0xC7, 0xCB, 0x02]); test_one(0xCDAB3412u32, &[0x92, 0xE8, 0xAC, 0xED, 0x0C]); test_one( 0x1234_5678_90AB_CDEFu64, &[0xEF, 0x9B, 0xAF, 0x85, 0x89, 0xCF, 0x95, 0x9A, 0x12], ); // https://github.com/jamesmunns/postcard/pull/83 test_one(32767i16, &[0xFE, 0xFF, 0x03]); test_one(-32768i16, &[0xFF, 0xFF, 0x03]); // chars test_one('z', &[0x01, 0x7a]); test_one('¢', &[0x02, 0xc2, 0xa2]); test_one('𐍈', &[0x04, 0xF0, 0x90, 0x8D, 0x88]); test_one('🥺', &[0x04, 0xF0, 0x9F, 0xA5, 0xBA]); // Structs test_one( BasicU8S { st: 0xABCD, ei: 0xFE, sf: 0x1234_4321_ABCD_DCBA, tt: 0xACAC_ACAC, }, &[ 0xCD, 0xD7, 0x02, 0xFE, 0xBA, 0xB9, 0xB7, 0xDE, 0x9A, 0xE4, 0x90, 0x9A, 0x12, 0xAC, 0xD9, 0xB2, 0xE5, 0x0A, ], ); // Enums! test_one(BasicEnum::Bim, &[0x01]); test_one( DataEnum::Bim(u64::MAX), &[ 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, ], ); test_one(DataEnum::Bib(u16::MAX), &[0x00, 0xFF, 0xFF, 0x03]); test_one(DataEnum::Bap(u8::MAX), &[0x02, 0xFF]); test_one( DataEnum::Kim(EnumStruct { eight: 0xF0, sixt: 0xACAC, }), &[0x03, 0xF0, 0xAC, 0xD9, 0x02], ); test_one( DataEnum::Chi { a: 0x0F, b: 0xC7C7C7C7, }, &[0x04, 0x0F, 0xC7, 0x8F, 0x9F, 0xBE, 0x0C], ); test_one(DataEnum::Sho(0x6969, 0x07), &[0x05, 0xE9, 0xD2, 0x01, 0x07]); // Tuples! test_one((0x12u8, 0xC7A5u16), &[0x12, 0xA5, 0x8F, 0x03]); // Structs! test_one(NewTypeStruct(5), &[0x05]); test_one(TupleStruct((0xA0, 0x1234)), &[0xA0, 0xB4, 0x24]); let mut input: Vec = Vec::new(); input.extend_from_slice(&[0x01, 0x02, 0x03, 0x04]).unwrap(); test_one(input, &[0x04, 0x01, 0x02, 0x03, 0x04]); let mut input: String<8> = String::new(); write!(&mut input, "helLO!").unwrap(); test_one(input, &[0x06, b'h', b'e', b'l', b'L', b'O', b'!']); let mut input: FnvIndexMap = FnvIndexMap::new(); input.insert(0x01, 0x05).unwrap(); input.insert(0x02, 0x06).unwrap(); input.insert(0x03, 0x07).unwrap(); input.insert(0x04, 0x08).unwrap(); test_one( input, &[0x04, 0x01, 0x05, 0x02, 0x06, 0x03, 0x07, 0x04, 0x08], ); // `CString` (uses `serialize_bytes`/`deserialize_byte_buf`) #[cfg(feature = "use-std")] test_one( std::ffi::CString::new("heLlo").unwrap(), &[0x05, b'h', b'e', b'L', b'l', b'o'], ); } #[cfg(feature = "heapless")] #[track_caller] fn test_one(data: T, ser_rep: &[u8]) where T: Serialize + DeserializeOwned + Eq + PartialEq + Debug, { let serialized: Vec = to_vec(&data).unwrap(); assert_eq!(serialized.len(), ser_rep.len()); let mut x: ::std::vec::Vec = vec![]; x.extend(serialized.deref().iter().cloned()); // let bysl: &'de [u8] = serialized.deref(); assert_eq!(x, ser_rep); { // let deserialized: T = from_bytes(serialized.deref()).unwrap(); let deserialized: T = from_bytes(&x).unwrap(); assert_eq!(data, deserialized); } } #[cfg(feature = "use-std")] #[test] fn std_io_loopback() { use postcard::from_io; use postcard::to_io; fn test_io(data: T, ser_rep: &[u8]) where T: Serialize + DeserializeOwned + Eq + PartialEq + Debug, { let serialized: ::std::vec::Vec = vec![]; let ser = to_io(&data, serialized).unwrap(); assert_eq!(ser.len(), ser_rep.len()); assert_eq!(ser, ser_rep); { let mut buff = [0; 2048]; let x = ser.clone(); let deserialized: T = from_io((x.as_slice(), &mut buff)).unwrap().0; assert_eq!(data, deserialized); } } test_io(DataEnum::Sho(0x6969, 0x07), &[0x05, 0xE9, 0xD2, 0x01, 0x07]); test_io( BasicU8S { st: 0xABCD, ei: 0xFE, sf: 0x1234_4321_ABCD_DCBA, tt: 0xACAC_ACAC, }, &[ 0xCD, 0xD7, 0x02, 0xFE, 0xBA, 0xB9, 0xB7, 0xDE, 0x9A, 0xE4, 0x90, 0x9A, 0x12, 0xAC, 0xD9, 0xB2, 0xE5, 0x0A, ], ); } #[cfg(all( any(feature = "embedded-io-04", feature = "embedded-io-06"), feature = "alloc" ))] #[test] fn std_eio_loopback() { use postcard::from_eio; use postcard::to_eio; fn test_io<'a, 'de, T>(data: T, ser_rep: &'a [u8]) where T: Serialize + DeserializeOwned + Eq + PartialEq + Debug, { let serialized: ::std::vec::Vec = vec![]; let ser = to_eio(&data, serialized).unwrap(); assert_eq!(ser.len(), ser_rep.len()); assert_eq!(ser, ser_rep); { let mut buff = [0; 2048]; let x = ser.clone(); let deserialized: T = from_eio((x.as_slice(), &mut buff)).unwrap().0; assert_eq!(data, deserialized); } } test_io(DataEnum::Sho(0x6969, 0x07), &[0x05, 0xE9, 0xD2, 0x01, 0x07]); test_io( BasicU8S { st: 0xABCD, ei: 0xFE, sf: 0x1234_4321_ABCD_DCBA, tt: 0xACAC_ACAC, }, &[ 0xCD, 0xD7, 0x02, 0xFE, 0xBA, 0xB9, 0xB7, 0xDE, 0x9A, 0xE4, 0x90, 0x9A, 0x12, 0xAC, 0xD9, 0xB2, 0xE5, 0x0A, ], ); } postcard-1.0.10/tests/max_size.rs000064400000000000000000000045311046102023000150540ustar 00000000000000#![allow(unused_imports)] #[cfg(feature = "experimental-derive")] mod tests { use postcard::experimental::max_size::MaxSize; use postcard::to_slice; use serde::Serialize; #[test] fn test_struct_max_size() { #[derive(MaxSize)] struct Foo { _a: u16, _b: Option, } assert_eq!(Foo::POSTCARD_MAX_SIZE, 5); } #[test] fn test_enum_max_size() { #[allow(dead_code)] #[derive(MaxSize, Serialize)] enum Bar { A(u16), B(u8), } assert_eq!(Bar::POSTCARD_MAX_SIZE, 4); let mut buf = [0u8; 128]; let used = to_slice(&Bar::A(0xFFFF), &mut buf).unwrap(); assert!( used.len() <= Bar::POSTCARD_MAX_SIZE, "FAIL {} > {}", used.len(), Bar::POSTCARD_MAX_SIZE ); #[derive(MaxSize)] enum Baz {} assert_eq!(Baz::POSTCARD_MAX_SIZE, 0); } #[test] fn test_ref() { #[allow(dead_code)] #[derive(MaxSize)] struct Foo { a: &'static u32, } } #[cfg(feature = "heapless")] #[test] fn test_vec_edge_cases() { #[track_caller] fn test_equals(buf: &mut [u8]) { let mut v = heapless::Vec::::new(); for _ in 0..N { v.push(0).unwrap(); } let serialized = postcard::to_slice(&v, buf).unwrap(); assert_eq!(heapless::Vec::::POSTCARD_MAX_SIZE, serialized.len()); } let mut buf = [0; 16400]; test_equals::<1>(&mut buf); test_equals::<2>(&mut buf); test_equals::<127>(&mut buf); test_equals::<128>(&mut buf); test_equals::<129>(&mut buf); test_equals::<16383>(&mut buf); test_equals::<16384>(&mut buf); test_equals::<16385>(&mut buf); } // #[cfg(feature = "experimental-derive")] // #[test] // fn test_union_max_size() { // #[derive(postcard::MaxSize)] // union Foo { // a: u16, // b: Option, // } // } // #[cfg(feature = "experimental-derive")] // #[test] // fn test_not_implemented() { // #[derive(postcard::MaxSize)] // struct Foo { // a: &'static str, // } // } } postcard-1.0.10/tests/schema.rs000064400000000000000000000100011046102023000144620ustar 00000000000000#![cfg(feature = "experimental-derive")] use postcard::experimental::schema::{NamedType, NamedValue, NamedVariant, Schema, SdmTy, Varint}; const U8_SCHEMA: NamedType = NamedType { name: "u8", ty: &SdmTy::U8, }; const U32_SCHEMA: NamedType = NamedType { name: "u32", ty: &SdmTy::Varint(Varint::U32), }; const U64_SCHEMA: NamedType = NamedType { name: "u64", ty: &SdmTy::Varint(Varint::U64), }; const I16_SCHEMA: NamedType = NamedType { name: "i16", ty: &SdmTy::Varint(Varint::I16), }; const I32_SCHEMA: NamedType = NamedType { name: "i32", ty: &SdmTy::Varint(Varint::I32), }; #[allow(unused)] #[derive(Schema)] enum Inner { Alpha, Beta, Gamma, Delta(i32, i16), Epsilon { zeta: f32, eta: bool }, } #[allow(unused)] #[derive(Schema)] struct Outer<'a> { a: u32, b: u64, c: u8, d: Inner, e: [u8; 10], f: &'a [u8], } #[allow(unused)] #[derive(Schema)] struct Slice<'a> { x: &'a [u8], } #[test] fn test_enum_serialize() { assert_eq!( &NamedType { name: "Inner", ty: &SdmTy::Enum(&[ &NamedVariant { name: "Alpha", ty: &SdmTy::UnitVariant }, &NamedVariant { name: "Beta", ty: &SdmTy::UnitVariant }, &NamedVariant { name: "Gamma", ty: &SdmTy::UnitVariant }, &NamedVariant { name: "Delta", ty: &SdmTy::TupleVariant(&[&I32_SCHEMA, &I16_SCHEMA,]) }, &NamedVariant { name: "Epsilon", ty: &SdmTy::StructVariant(&[ &NamedValue { name: "zeta", ty: &NamedType { name: "f32", ty: &SdmTy::F32 }, }, &NamedValue { name: "eta", ty: &NamedType { name: "bool", ty: &SdmTy::Bool }, } ]), } ]), }, Inner::SCHEMA ); } #[test] fn test_struct_serialize() { const TEN_BYTES_SCHEMA: &[&NamedType] = &[&U8_SCHEMA; 10]; assert_eq!( Outer::SCHEMA, &NamedType { name: "Outer", ty: &SdmTy::Struct(&[ &NamedValue { name: "a", ty: &U32_SCHEMA }, &NamedValue { name: "b", ty: &U64_SCHEMA }, &NamedValue { name: "c", ty: &U8_SCHEMA }, &NamedValue { name: "d", ty: Inner::SCHEMA }, &NamedValue { name: "e", ty: &NamedType { name: "[T; N]", ty: &SdmTy::Tuple(TEN_BYTES_SCHEMA), } }, &NamedValue { name: "f", ty: &NamedType { name: "&[T]", ty: &SdmTy::Seq(&NamedType { name: "u8", ty: &SdmTy::U8 }) } }, ]), } ); } #[test] fn test_slice_serialize() { assert_eq!( &NamedType { name: "Slice", ty: &SdmTy::Struct(&[&NamedValue { name: "x", ty: &NamedType { name: "&[T]", ty: &SdmTy::Seq(&U8_SCHEMA) } },]), }, Slice::SCHEMA ); }