Skip to content

Commit

Permalink
Provides deserialisation
Browse files Browse the repository at this point in the history
Not quite there yet...
  • Loading branch information
huntc committed Apr 5, 2023
1 parent a8abacf commit c068ce8
Show file tree
Hide file tree
Showing 3 changed files with 145 additions and 104 deletions.
105 changes: 105 additions & 0 deletions src/de/flavors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<u8> {
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<Self::Remainder> {
match self.flav.try_take_n(core::mem::size_of::<$int>()) {
Ok(prev_crc_bytes) => match self.flav.finalize() {
Ok(remainder) => {
let crc = self.digest.finalize();
let le_bytes = prev_crc_bytes
.try_into()
.map_err(|_| Error::DeserializeBadEncoding)?;
let prev_crc = <$int>::from_le_bytes(le_bytes);
if crc == prev_crc {
Ok(remainder)
} else {
Err(Error::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.
Expand Down
128 changes: 27 additions & 101 deletions src/ser/flavors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 = <B as Flavor>::Output;

#[inline(always)]
fn try_push(&mut self, data: u8) -> Result<()> {
self.digest.update(&[data]);
self.flav.try_push(data)
}

fn finalize(mut self) -> Result<Self::Output> {
let crc = self.digest.finalize();
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 = <B as Flavor>::Output;

#[inline(always)]
fn try_push(&mut self, data: u8) -> Result<()> {
self.digest.update(&[data]);
self.flav.try_push(data)
}

fn finalize(mut self) -> Result<Self::Output> {
let crc = self.digest.finalize();
for byte in crc.to_le_bytes() {
self.flav.try_push(byte)?;
}
self.flav.finalize()
}
}
)*
};
}

impl<'a, B> Flavor for CrcModifier<'a, B, u16>
where
B: Flavor,
{
type Output = <B as Flavor>::Output;

#[inline(always)]
fn try_push(&mut self, data: u8) -> Result<()> {
self.digest.update(&[data]);
self.flav.try_push(data)
}

fn finalize(mut self) -> Result<Self::Output> {
let crc = self.digest.finalize();
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 = <B as Flavor>::Output;

#[inline(always)]
fn try_push(&mut self, data: u8) -> Result<()> {
self.digest.update(&[data]);
self.flav.try_push(data)
}

fn finalize(mut self) -> Result<Self::Output> {
let crc = self.digest.finalize();
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 = <B as Flavor>::Output;

#[inline(always)]
fn try_push(&mut self, data: u8) -> Result<()> {
self.digest.update(&[data]);
self.flav.try_push(data)
}

fn finalize(mut self) -> Result<Self::Output> {
let crc = self.digest.finalize();
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 = <B as Flavor>::Output;

#[inline(always)]
fn try_push(&mut self, data: u8) -> Result<()> {
self.digest.update(&[data]);
self.flav.try_push(data)
}

fn finalize(mut self) -> Result<Self::Output> {
let crc = self.digest.finalize();
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
Expand Down
16 changes: 13 additions & 3 deletions tests/crc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::<u32>::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]);
}

0 comments on commit c068ce8

Please sign in to comment.