diff --git a/src/lib.rs b/src/lib.rs index 419f2f5..31ffe85 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -132,7 +132,7 @@ //! //! let data: &[u8] = &[0x01, 0x00, 0x20, 0x30]; //! let buffer = &mut [0u8; 32]; -//! let res = serialize_with_flavor::<[u8], Cobs, &mut [u8]>( +//! let res = serialize_with_flavor( //! data, //! Cobs::try_new(Slice::new(buffer)).unwrap(), //! ).unwrap(); @@ -167,9 +167,7 @@ mod varint; pub use de::deserializer::Deserializer; pub use de::{from_bytes, from_bytes_cobs, take_from_bytes, take_from_bytes_cobs}; pub use error::{Error, Result}; -pub use ser::{ - flavors, serialize_with_flavor, serializer::Serializer, to_slice, to_slice_cobs, -}; +pub use ser::{flavors, serialize_with_flavor, serializer::Serializer, to_slice, to_slice_cobs}; #[cfg(feature = "heapless")] pub use ser::{to_vec, to_vec_cobs}; diff --git a/src/ser/flavors.rs b/src/ser/flavors.rs index 4b1eb0e..8d4011c 100644 --- a/src/ser/flavors.rs +++ b/src/ser/flavors.rs @@ -63,7 +63,7 @@ //! //! let data: &[u8] = &[0x01, 0x00, 0x20, 0x30]; //! let buffer = &mut [0u8; 32]; -//! let res = serialize_with_flavor::<[u8], Cobs, &mut [u8]>( +//! let res = serialize_with_flavor( //! data, //! Cobs::try_new(Slice::new(buffer)).unwrap(), //! ).unwrap(); @@ -183,10 +183,10 @@ impl<'a> IndexMut for Slice<'a> { #[cfg(feature = "heapless")] mod heapless_vec { - use heapless::{ArrayLength, Vec}; - use super::SerFlavor; use super::Index; use super::IndexMut; + use super::SerFlavor; + use heapless::{ArrayLength, Vec}; //////////////////////////////////////// // HVec @@ -241,10 +241,10 @@ mod heapless_vec { #[cfg(feature = "use-std")] mod std_vec { extern crate std; - use std::vec::Vec; - use super::SerFlavor; use super::Index; use super::IndexMut; + use super::SerFlavor; + use std::vec::Vec; /// The `StdVec` flavor is a wrapper type around a `std::vec::Vec`. /// @@ -289,10 +289,10 @@ mod std_vec { #[cfg(feature = "alloc")] mod alloc_vec { extern crate alloc; - use alloc::vec::Vec; - use super::SerFlavor; use super::Index; use super::IndexMut; + use super::SerFlavor; + use alloc::vec::Vec; /// The `AllocVec` flavor is a wrapper type around an `alloc::vec::Vec`. /// @@ -338,6 +338,35 @@ mod alloc_vec { // Modification Flavors //////////////////////////////////////////////////////////////////////////////// +use core::convert::AsRef; + +/// The SerFlavor trait acts as a layer to generate footers in modification flavors, this can +/// commonly be a hash or CRC in packets. +pub trait FooterGenerator> { + /// This feeds the generator with data to generate a footer + fn feed(&mut self, data: u8); + /// This returns the final footer + fn finalize(&mut self) -> T; +} + +#[doc(hidden)] +pub struct NoFooter; + +impl NoFooter { + fn new() -> Self { + NoFooter {} + } +} + +impl FooterGenerator<[u8; 0]> for NoFooter { + #[inline(always)] + fn feed(&mut self, _data: u8) {} + + fn finalize(&mut self) -> [u8; 0] { + [0; 0] + } +} + //////////////////////////////////////// // COBS //////////////////////////////////////// @@ -349,15 +378,19 @@ mod alloc_vec { /// This protocol is useful when sending data over a serial interface without framing such as a UART /// /// [Consistent Overhead Byte Stuffing]: https://en.wikipedia.org/wiki/Consistent_Overhead_Byte_Stuffing -pub struct Cobs +pub struct Cobs where B: SerFlavor + IndexMut, + F: FooterGenerator, + T: AsRef<[u8]>, { flav: B, + footer: F, cobs: EncoderState, + _t: core::marker::PhantomData, } -impl Cobs +impl Cobs where B: SerFlavor + IndexMut, { @@ -367,20 +400,44 @@ where bee.try_push(0).map_err(|_| Error::SerializeBufferFull)?; Ok(Self { flav: bee, + footer: NoFooter::new(), cobs: EncoderState::default(), + _t: core::marker::PhantomData, }) } } -impl<'a, B> SerFlavor for Cobs +impl Cobs where B: SerFlavor + IndexMut, + F: FooterGenerator, + T: AsRef<[u8]>, +{ + /// Create a new Cobs modifier Flavor. If there is insufficient space + /// to push the leading header byte, the method will return an Error + pub fn try_new_with_footer(mut bee: B, footer: F) -> Result { + bee.try_push(0).map_err(|_| Error::SerializeBufferFull)?; + Ok(Self { + flav: bee, + footer, + cobs: EncoderState::default(), + _t: core::marker::PhantomData, + }) + } +} + +impl<'a, B, F, T> SerFlavor for Cobs +where + B: SerFlavor + IndexMut, + F: FooterGenerator, + T: AsRef<[u8]>, { type Output = ::Output; #[inline(always)] fn try_push(&mut self, data: u8) -> core::result::Result<(), ()> { use PushResult::*; + self.footer.feed(data); match self.cobs.push(data) { AddSingle(n) => self.flav.try_push(n), ModifyFromStartAndSkip((idx, mval)) => { @@ -396,6 +453,9 @@ where } fn release(mut self) -> core::result::Result { + for b in self.footer.finalize().as_ref() { + self.try_push(*b)?; + } let (idx, mval) = self.cobs.finalize(); self.flav[idx] = mval; self.flav.try_push(0)?; diff --git a/src/ser/mod.rs b/src/ser/mod.rs index 5d21f58..0c28758 100644 --- a/src/ser/mod.rs +++ b/src/ser/mod.rs @@ -1,6 +1,6 @@ -use serde::Serialize; use crate::error::{Error, Result}; use crate::ser::flavors::{Cobs, SerFlavor, Slice}; +use serde::Serialize; #[cfg(feature = "heapless")] use crate::ser::flavors::HVec; @@ -54,10 +54,7 @@ pub fn to_slice_cobs<'a, 'b, T>(value: &'b T, buf: &'a mut [u8]) -> Result<&'a m where T: Serialize + ?Sized, { - serialize_with_flavor::>, &'a mut [u8]>( - value, - Cobs::try_new(Slice::new(buf))?, - ) + serialize_with_flavor(value, Cobs::try_new(Slice::new(buf))?) } /// Serialize a `T` to the given slice, with the resulting slice containing @@ -128,7 +125,7 @@ where T: Serialize + ?Sized, B: ArrayLength, { - serialize_with_flavor::>, Vec>(value, Cobs::try_new(HVec::default())?) + serialize_with_flavor(value, Cobs::try_new(HVec::default())?) } /// Serialize a `T` to a `heapless::Vec`, with the `Vec` containing @@ -230,7 +227,10 @@ pub fn to_allocvec(value: &T) -> Result> where T: Serialize + ?Sized, { - serialize_with_flavor::>(value, AllocVec(alloc::vec::Vec::new())) + serialize_with_flavor::>( + value, + AllocVec(alloc::vec::Vec::new()), + ) } /// Serialize and COBS encode a `T` to an `alloc::vec::Vec`. Requires the `alloc` feature. @@ -278,7 +278,7 @@ where /// /// let data: &[u8] = &[0x01, 0x00, 0x20, 0x30]; /// let buffer = &mut [0u8; 32]; -/// let res = serialize_with_flavor::<[u8], Cobs, &mut [u8]>( +/// let res = serialize_with_flavor( /// data, /// Cobs::try_new(Slice::new(buffer)).unwrap(), /// ).unwrap(); @@ -302,6 +302,7 @@ where #[cfg(test)] mod test { use super::*; + use crate::from_bytes_cobs; use crate::varint::VarintUsize; use core::fmt::Write; use core::ops::{Deref, DerefMut}; @@ -587,4 +588,36 @@ mod test { assert_eq!(input, x); } + + struct MyFooter { + state: u16, + } + + impl crate::flavors::FooterGenerator<[u8; 2]> for MyFooter { + fn feed(&mut self, data: u8) { + self.state += data as u16; + } + + fn finalize(&mut self) -> [u8; 2] { + self.state.to_le_bytes() + } + } + + #[test] + fn cobs_footer_test() { + let data: [u8; 4] = [0x01, 0x00, 0x20, 0x30]; + let buffer = &mut [0; 32]; + let res = serialize_with_flavor( + &data, + Cobs::try_new_with_footer(Slice::new(buffer), MyFooter { state: 0 }).unwrap(), + ) + .unwrap(); + + assert_eq!(res, &[0x02, 0x01, 0x04, 0x20, 0x30, 0x51, 0x01, 0x00]); + + let res: ([u8; 4], [u8; 2]) = from_bytes_cobs(res).unwrap(); + + assert_eq!(res.0, data); + assert_eq!(res.1, (data.iter().sum::() as u16).to_le_bytes()); + } }