Skip to content

Commit

Permalink
Convenience functions
Browse files Browse the repository at this point in the history
Convenience functions for serde usage
  • Loading branch information
huntc committed Apr 6, 2023
1 parent 81582a2 commit 9f7539e
Show file tree
Hide file tree
Showing 4 changed files with 198 additions and 70 deletions.
3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,9 @@ optional = true
version = "3.0.1"
optional = true

[dependencies.paste]
version = "1.0.12"

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

Expand Down
110 changes: 71 additions & 39 deletions src/de/flavors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -181,10 +181,15 @@ pub mod crc {

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>
Expand All @@ -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<u8> {
match self.flav.pop() {
Ok(byte) => {
self.digest.update(&[byte]);
Ok(byte)
#[inline]
fn pop(&mut self) -> Result<u8> {
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<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)
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),
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 [<from_bytes_ $int>]<'a, T>(s: &'a [u8], digest: Digest<'a, u32>) -> Result<T>
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 [<take_from_bytes_ $int>]<'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()?))
}
}
)*
};
Expand Down
135 changes: 119 additions & 16 deletions src/ser/flavors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -427,9 +430,15 @@ where
pub mod crc {
use crc::Digest;
use crc::Width;
use serde::Serialize;

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>
Expand All @@ -455,24 +464,118 @@ pub mod crc {
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)
}
paste! {
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)?;
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()
}
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.
///
/// ## Example
///
/// ```rust
/// use crc::{Crc, CRC_32_ISCSI};
///
/// let mut buf = [0; 9];
///
/// let data: &[u8] = &[0x01, 0x00, 0x20, 0x30];
/// let crc = Crc::<u32>::new(&CRC_32_ISCSI);
/// let used = postcard::ser_flavors::crc::[<to_slice_ $int>](data, &mut buf, crc.digest()).unwrap();
/// assert_eq!(used, &[0x04, 0x01, 0x00, 0x20, 0x30, 0x8E, 0xC8, 0x1A, 0x37]);
/// ```
pub fn [<to_slice_ $int>]<'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<u8>`, with the `Vec` containing
/// data followed by a 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::<u32>::new(&CRC_32_ISCSI);
/// let ser: Vec<u8, 32> = postcard::ser_flavors::crc::[<to_vec_ $int>](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<u8, 32> = postcard::ser_flavors::crc::[<to_vec $int>](data, crc.digest()).unwrap();
/// assert_eq!(ser.deref(), &[0x01, 0x00, 0x20, 0x30, 0xCC, 0x4B, 0x4A, 0xDA]);
/// ```
#[cfg(feature = "heapless")]
pub fn [<to_vec $int>]<'a, T, const B: usize>(
value: &T,
digest: Digest<'a, u32>,
) -> Result<heapless::Vec<u8, B>>
where
T: Serialize + ?Sized,
{
use super::HVec;

serialize_with_flavor(value, CrcModifier::new(HVec::default(), digest))
}

/// Serialize a `T` to a `heapless::Vec<u8>`, with the `Vec` containing
/// data followed by a 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::<u32>::new(&CRC_32_ISCSI);
/// let ser: Vec<u8> = postcard::ser_flavors::crc::[<to_allocvec $int>(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<u8> = postcard::ser_flavors::crc::[<to_allocvec $int>(data, crc.digest()).unwrap();
/// assert_eq!(ser.deref(), &[0x01, 0x00, 0x20, 0x30, 0xCC, 0x4B, 0x4A, 0xDA]);
/// ```
#[cfg(feature = "alloc")]
pub fn [<to_allocvec $int>]<'a, T>(value: &T, digest: Digest<'a, u32>) -> Result<alloc::vec::Vec<u8>>
where
T: Serialize + ?Sized,
{
use super::AllocVec;

serialize_with_flavor(value, CrcModifier::new(AllocVec::new(), digest))
}
}
)*
Expand Down
20 changes: 5 additions & 15 deletions tests/crc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,18 @@
#[cfg(feature = "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::<u32>::new(&CRC_32_ISCSI);
let digest = crc.digest();
let res =
serialize_with_flavor(data, SerCrcModifier::new(SerSlice::new(buffer), digest)).unwrap();

let res = postcard::ser_flavors::crc::to_slice_u32(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::de_flavors::crc::take_from_bytes_u32::<[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()));
}

0 comments on commit 9f7539e

Please sign in to comment.