diff --git a/Cargo.toml b/Cargo.toml index 313fa77..604b8b5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -56,6 +56,9 @@ optional = true version = "3.0.1" optional = true +[dependencies.paste] +version = "1.0.12" + [features] default = ["heapless-cas"] diff --git a/src/de/flavors.rs b/src/de/flavors.rs index 5237749..d4121a0 100644 --- a/src/de/flavors.rs +++ b/src/de/flavors.rs @@ -174,17 +174,22 @@ impl<'de> Flavor<'de> for Slice<'de> { /// /// The `crc` feature requires enabling to use this module. /// -/// More on CRCs: https://en.wikipedia.org/wiki/Cyclic_redundancy_check. -#[cfg(feature = "crc")] +/// More on CRCs: . +#[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> @@ -210,56 +215,83 @@ pub mod crc { macro_rules! impl_flavor { ($( $int:ty ),*) => { $( - impl<'de, B> Flavor<'de> for CrcModifier<'de, B, $int> - where - B: Flavor<'de>, - { - type Remainder = B::Remainder; + paste! { + impl<'de, B> Flavor<'de> for CrcModifier<'de, B, $int> + where + B: Flavor<'de>, + { + type Remainder = B::Remainder; - type Source = B::Source; + type Source = B::Source; - #[inline] - fn pop(&mut self) -> Result { - match self.flav.pop() { - Ok(byte) => { - self.digest.update(&[byte]); - Ok(byte) + #[inline] + fn pop(&mut self) -> Result { + match self.flav.pop() { + Ok(byte) => { + self.digest.update(&[byte]); + Ok(byte) + } + e @ Err(_) => e, } - e @ Err(_) => e, } - } - #[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) + #[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, } - 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::DeserializeBadEncoding) + 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::DeserializeBadEncoding) + } } - } - e @ Err(_) => e, - }, - Err(e) => Err(e), + 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, u32>) -> 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, u32>) -> 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()?)) + } } )* }; diff --git a/src/de/mod.rs b/src/de/mod.rs index c17751b..a30c9ce 100644 --- a/src/de/mod.rs +++ b/src/de/mod.rs @@ -20,7 +20,7 @@ where /// 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). +/// 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 @@ -32,7 +32,7 @@ where /// 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). +/// 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 @@ -67,6 +67,22 @@ where 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")] +pub use flavors::crc::from_bytes_u32 as from_bytes_crc32; + +/// 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")] +pub use flavors::crc::take_from_bytes_u32 as take_from_bytes_crc32; + //////////////////////////////////////////////////////////////////////////////// #[cfg(feature = "heapless")] diff --git a/src/lib.rs b/src/lib.rs index 96c6422..3688e37 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -91,6 +91,21 @@ pub use ser::{to_stdvec, to_stdvec_cobs}; #[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] diff --git a/src/ser/flavors.rs b/src/ser/flavors.rs index aec695f..7be3250 100644 --- a/src/ser/flavors.rs +++ b/src/ser/flavors.rs @@ -101,6 +101,9 @@ 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, @@ -422,14 +425,21 @@ where /// /// The `crc` feature requires enabling to use this module. /// -/// More on CRCs: https://en.wikipedia.org/wiki/Cyclic_redundancy_check. -#[cfg(feature = "crc")] +/// More on CRCs: . +#[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> @@ -455,24 +465,70 @@ pub mod crc { macro_rules! impl_flavor { ($( $int:ty ),*) => { $( - 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) - } + 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)?; + 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() } - 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, 'b, T>( + value: &'b T, + buf: &'a mut [u8], + digest: Digest<'a, u32>, + ) -> 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`. + /// Requires the (default) `heapless` feature. + #[cfg(feature = "heapless")] + pub fn []<'a, T, const B: usize>( + value: &T, + digest: Digest<'a, u32>, + ) -> 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")] + pub fn []<'a, T>(value: &T, digest: Digest<'a, u32>) -> Result> + where + T: Serialize + ?Sized, + { + use super::AllocVec; + + serialize_with_flavor(value, CrcModifier::new(AllocVec::new(), digest)) } } )* diff --git a/src/ser/mod.rs b/src/ser/mod.rs index 95d330e..7d37a83 100644 --- a/src/ser/mod.rs +++ b/src/ser/mod.rs @@ -237,6 +237,107 @@ where ) } +/// 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")] +pub use flavors::crc::to_slice_u32 as to_slice_crc32; + +/// 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`. +/// Requires the (default) `heapless` feature. +/// +/// ## 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"))] +pub use flavors::crc::to_vec_u32 as to_vec_crc32; + +/// 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"))] +pub use flavors::crc::to_allocvec_u32 as to_stdvec_crc32; + +/// 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"))] +pub use flavors::crc::to_allocvec_u32 as to_allocvec_crc32; + /// `serialize_with_flavor()` has three generic parameters, `T, F, O`. /// /// * `T`: This is the type that is being serialized diff --git a/tests/crc.rs b/tests/crc.rs index 27e6acc..9b93118 100644 --- a/tests/crc.rs +++ b/tests/crc.rs @@ -1,29 +1,19 @@ #[test] -#[cfg(feature = "crc")] +#[cfg(feature = "use-crc")] fn test_crc() { use crc::{Crc, CRC_32_ISCSI}; - use postcard::{ - de_flavors::{crc::CrcModifier as DeCrcModifier, Slice as DeSlice}, - ser_flavors::{crc::CrcModifier as SerCrcModifier, Slice as SerSlice}, - serialize_with_flavor, Deserializer, - }; - use serde::de::Deserialize; 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 = - serialize_with_flavor(data, SerCrcModifier::new(SerSlice::new(buffer), digest)).unwrap(); - + 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 flav = DeCrcModifier::new(DeSlice::new(&res), digest); - let mut deserializer = Deserializer::from_flavor(flav); - let res = <[u8; 5]>::deserialize(&mut deserializer).unwrap(); - - assert_eq!(res, [0x04, 0x01, 0x00, 0x20, 0x30]); + let res = postcard::take_from_bytes_crc32::<[u8; 5]>(&res, digest).unwrap(); - assert_eq!(deserializer.finalize().unwrap(), &[0u8; 0]); + let expected_bytes = [0x04, 0x01, 0x00, 0x20, 0x30]; + let remaining_bytes = []; + assert_eq!(res, (expected_bytes, remaining_bytes.as_slice())); }