Skip to content

Commit

Permalink
Introduces CRCs
Browse files Browse the repository at this point in the history
A `crc` feature has been added that brings in the `crc` crate and a new `CrcModifier` flavour. This modifier flavour is similar to the COBS implementation in how it wraps another flavour.
  • Loading branch information
huntc committed Apr 4, 2023
1 parent 62c0547 commit 170d1fe
Show file tree
Hide file tree
Showing 4 changed files with 170 additions and 1 deletion.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/target
**/*.rs.bk
Cargo.lock
Cargo.lock
.vscode
5 changes: 5 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -52,13 +52,18 @@ path = "./postcard-derive"
version = "0.1.1"
optional = true

[dependencies.crc]
version = "3.0.1"
optional = true

[features]
default = ["heapless-cas"]

use-std = ["serde/std", "alloc"]
heapless-cas = ["heapless", "heapless/cas"]
alloc = ["serde/alloc"]
use-defmt = ["defmt"]
use-crc = ["crc"]

# Experimental features!
#
Expand Down
146 changes: 146 additions & 0 deletions src/ser/flavors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -411,6 +411,152 @@ where
}
}

////////////////////////////////////////
// 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: https://en.wikipedia.org/wiki/Cyclic_redundancy_check.
#[cfg(feature = "crc")]
pub mod crc {
use crc::Digest;
use crc::Width;

use super::Flavor;
use super::IndexMut;
use crate::Result;

/// Manages CRC modifications as a flavor.
pub struct CrcModifier<'a, B, W>
where
B: Flavor + IndexMut<usize, Output = u8>,
W: Width,
{
flav: B,
digest: Digest<'a, W>,
}

impl<'a, B, W> CrcModifier<'a, B, W>
where
B: Flavor + IndexMut<usize, Output = u8>,
W: Width,
{
/// 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 + IndexMut<usize, Output = u8>,
{
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, u16>
where
B: Flavor + IndexMut<usize, Output = u8>,
{
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 + IndexMut<usize, Output = u8>,
{
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 + IndexMut<usize, Output = u8>,
{
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 + IndexMut<usize, Output = u8>,
{
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()
}
}
}

/// The `Size` flavor is a measurement flavor, which accumulates the number of bytes needed to
/// serialize the data.
///
Expand Down
17 changes: 17 additions & 0 deletions tests/crc.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#[test]
#[cfg(feature = "crc")]
fn test_crc() {
use crc::{Crc, CRC_32_ISCSI};
use postcard::{
ser_flavors::{crc::CrcModifier, Slice},
serialize_with_flavor,
};

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();

assert_eq!(res, &[0x04, 0x01, 0x00, 0x20, 0x30, 0x8E, 0xC8, 0x1A, 0x37]);
}

0 comments on commit 170d1fe

Please sign in to comment.