diff --git a/src/de/flavors.rs b/src/de/flavors.rs index a81fdb2..3eefd03 100644 --- a/src/de/flavors.rs +++ b/src/de/flavors.rs @@ -163,6 +163,111 @@ impl<'de> Flavor<'de> for Slice<'de> { } } +//////////////////////////////////////// +// 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: https://en.wikipedia.org/wiki/Cyclic_redundancy_check. +#[cfg(feature = "crc")] +pub mod crc { + use core::convert::TryInto; + + use crc::Digest; + use crc::Width; + + use super::Flavor; + use crate::Error; + use crate::Result; + + /// Manages CRC modifications as a flavor. + pub struct CrcModifier<'de, B, W> + where + B: Flavor<'de>, + W: Width, + { + flav: B, + digest: Digest<'de, W>, + } + + impl<'de, B, W> CrcModifier<'de, B, W> + where + B: Flavor<'de>, + W: Width, + { + /// Create a new Crc modifier Flavor. + pub fn new(bee: B, digest: Digest<'de, W>) -> Self { + Self { flav: bee, digest } + } + } + + macro_rules! impl_flavor { + ($( $int:ty ),*) => { + $( + 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 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::DeserializeBadEncoding) + } + } + e @ Err(_) => e, + }, + Err(e) => Err(e), + } + } + } + )* + }; + } + + impl_flavor![u8, u16, u32, u64, u128]; +} + // This is a terrible checksum implementation to make sure that we can effectively // use the deserialization flavor. This is kept as a test (and not published) // because an 8-bit checksum is not ACTUALLY useful for almost anything. diff --git a/src/ser/flavors.rs b/src/ser/flavors.rs index b55af1a..2339538 100644 --- a/src/ser/flavors.rs +++ b/src/ser/flavors.rs @@ -446,114 +446,40 @@ pub mod crc { B: Flavor, W: Width, { - /// Create a new Crc modifier Flavor. + /// Create a new CRC modifier Flavor. pub fn new(bee: B, digest: Digest<'a, W>) -> Self { Self { flav: bee, digest } } - - #[inline(always)] - fn try_push_crc(flav: &mut B, crc_bytes: &[u8]) -> Result<()> { - for byte in crc_bytes { - flav.try_push(*byte)?; - } - Ok(()) - } } - impl<'a, B> Flavor for CrcModifier<'a, B, u8> - 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(); - Self::try_push_crc(&mut self.flav, &crc.to_le_bytes())?; - self.flav.finalize() - } + 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) + } + + 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() + } + } + )* + }; } - impl<'a, B> Flavor for CrcModifier<'a, B, u16> - 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(); - Self::try_push_crc(&mut self.flav, &crc.to_le_bytes())?; - self.flav.finalize() - } - } - - impl<'a, B> Flavor for CrcModifier<'a, B, u32> - 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(); - Self::try_push_crc(&mut self.flav, &crc.to_le_bytes())?; - self.flav.finalize() - } - } - - impl<'a, B> Flavor for CrcModifier<'a, B, u64> - 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(); - Self::try_push_crc(&mut self.flav, &crc.to_le_bytes())?; - self.flav.finalize() - } - } - - impl<'a, B> Flavor for CrcModifier<'a, B, u128> - 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(); - Self::try_push_crc(&mut self.flav, &crc.to_le_bytes())?; - self.flav.finalize() - } - } + impl_flavor![u8, u16, u32, u64, u128]; } /// The `Size` flavor is a measurement flavor, which accumulates the number of bytes needed to diff --git a/tests/crc.rs b/tests/crc.rs index 4c6d978..83ff456 100644 --- a/tests/crc.rs +++ b/tests/crc.rs @@ -3,15 +3,25 @@ fn test_crc() { use crc::{Crc, CRC_32_ISCSI}; use postcard::{ - ser_flavors::{crc::CrcModifier, Slice}, - serialize_with_flavor, + 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, CrcModifier::new(Slice::new(buffer), digest)).unwrap(); + let res = + serialize_with_flavor(data, SerCrcModifier::new(SerSlice::new(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 r = <[u8; 5]>::deserialize(&mut deserializer).unwrap(); + + assert_eq!(res, &[0x01, 0x00, 0x20, 0x30]); }