postcard/ser/
flavors.rs

1//! # Serialization Flavors
2//!
3//! "Flavors" in `postcard` are used as modifiers to the serialization or deserialization
4//! process. Flavors typically modify one or both of the following:
5//!
6//! 1. The output medium of the serialization, e.g. whether the data is serialized to a `[u8]` slice, or a `heapless::Vec`.
7//! 2. The format of the serialization, such as encoding the serialized output in a COBS format, performing CRC32 checksumming while serializing, etc.
8//!
9//! Flavors are implemented using the [`Flavor`] trait, which acts as a "middleware" for receiving the bytes as serialized by `serde`.
10//! Multiple flavors may be combined to obtain a desired combination of behavior and storage.
11//! When flavors are combined, it is expected that the storage flavor (such as `Slice` or `HVec`) is the innermost flavor.
12//!
13//! Custom flavors may be defined by users of the `postcard` crate, however some commonly useful flavors have been provided in
14//! this module. If you think your custom flavor would be useful to others, PRs adding flavors are very welcome!
15//!
16//! ## Usability
17//!
18//! Flavors may not always be convenient to use directly, as they may expose some implementation details of how the
19//! inner workings of the flavor behaves. It is typical to provide a convenience method for using a flavor, to prevent
20//! the user from having to specify generic parameters, setting correct initialization values, or handling the output of
21//! the flavor correctly. See `postcard::to_vec()` for an example of this.
22//!
23//! It is recommended to use the [`serialize_with_flavor()`](../fn.serialize_with_flavor.html) method for serialization. See it's documentation for information
24//! regarding usage and generic type parameters.
25//!
26//! ## When to use (multiple) flavors
27//!
28//! Combining flavors are nice for convenience, as they perform potentially multiple steps of
29//! serialization at one time.
30//!
31//! This can often be more memory efficient, as intermediate buffers are not typically required.
32//!
33//! ## When NOT to use (multiple) flavors
34//!
35//! The downside of passing serialization through multiple steps is that it is typically slower than
36//! performing each step serially. Said simply, "cobs encoding while serializing" is often slower
37//! than "serialize then cobs encode", due to the ability to handle longer "runs" of data in each
38//! stage. The downside is that if these stages can not be performed in-place on the buffer, you
39//! will need additional buffers for each stage.
40//!
41//! ## Examples
42//!
43//! ### Using a single flavor
44//!
45//! In the first example, we use the `Slice` flavor, to store the serialized output into a mutable `[u8]` slice.
46//! No other modification is made to the serialization process.
47//!
48//! ```rust
49//! use postcard::{
50//!     serialize_with_flavor,
51//!     ser_flavors::Slice,
52//! };
53//!
54//! let mut buf = [0u8; 32];
55//!
56//! let data: &[u8] = &[0x01, 0x00, 0x20, 0x30];
57//! let buffer = &mut [0u8; 32];
58//! let res = serialize_with_flavor::<[u8], Slice, &mut [u8]>(
59//!     data,
60//!     Slice::new(buffer)
61//! ).unwrap();
62//!
63//! assert_eq!(res, &[0x04, 0x01, 0x00, 0x20, 0x30]);
64//! ```
65//!
66//! ### Using combined flavors
67//!
68//! In the second example, we mix `Slice` with `Cobs`, to cobs encode the output while
69//! the data is serialized. Notice how `Slice` (the storage flavor) is the innermost flavor used.
70//!
71//! ```rust
72//! use postcard::{
73//!     serialize_with_flavor,
74//!     ser_flavors::{Cobs, Slice},
75//! };
76//!
77//! let mut buf = [0u8; 32];
78//!
79//! let data: &[u8] = &[0x01, 0x00, 0x20, 0x30];
80//! let buffer = &mut [0u8; 32];
81//! let res = serialize_with_flavor::<[u8], Cobs<Slice>, &mut [u8]>(
82//!     data,
83//!     Cobs::try_new(Slice::new(buffer)).unwrap(),
84//! ).unwrap();
85//!
86//! assert_eq!(res, &[0x03, 0x04, 0x01, 0x03, 0x20, 0x30, 0x00]);
87//! ```
88
89use crate::error::{Error, Result};
90use cobs::{EncoderState, PushResult};
91use core::marker::PhantomData;
92use core::ops::Index;
93use core::ops::IndexMut;
94
95#[cfg(feature = "heapless")]
96pub use heapless_vec::*;
97
98#[cfg(feature = "use-std")]
99pub use std_vec::*;
100
101#[cfg(feature = "alloc")]
102pub use alloc_vec::*;
103
104#[cfg(feature = "alloc")]
105extern crate alloc;
106
107/// The serialization Flavor trait
108///
109/// This is used as the primary way to encode serialized data into some kind of buffer,
110/// or modify that data in a middleware style pattern.
111///
112/// See the module level docs for an example of how flavors are used.
113pub trait Flavor {
114    /// The `Output` type is what this storage "resolves" to when the serialization is complete,
115    /// such as a slice or a Vec of some sort.
116    type Output;
117
118    /// Override this method when you want to customize processing
119    /// multiple bytes at once, such as copying a slice to the output,
120    /// rather than iterating over one byte at a time.
121    #[inline]
122    fn try_extend(&mut self, data: &[u8]) -> Result<()> {
123        data.iter().try_for_each(|d| self.try_push(*d))
124    }
125
126    /// Push a single byte to be modified and/or stored.
127    fn try_push(&mut self, data: u8) -> Result<()>;
128
129    /// Finalize the serialization process.
130    fn finalize(self) -> Result<Self::Output>;
131}
132
133////////////////////////////////////////
134// Slice
135////////////////////////////////////////
136
137/// The `Slice` flavor is a storage flavor, storing the serialized (or otherwise modified) bytes into a plain
138/// `[u8]` slice. The `Slice` flavor resolves into a sub-slice of the original slice buffer.
139pub struct Slice<'a> {
140    start: *mut u8,
141    cursor: *mut u8,
142    end: *mut u8,
143    _pl: PhantomData<&'a [u8]>,
144}
145
146impl<'a> Slice<'a> {
147    /// Create a new `Slice` flavor from a given backing buffer
148    pub fn new(buf: &'a mut [u8]) -> Self {
149        let ptr = buf.as_mut_ptr_range();
150        Slice {
151            start: ptr.start,
152            cursor: ptr.start,
153            end: ptr.end,
154            _pl: PhantomData,
155        }
156    }
157}
158
159impl<'a> Flavor for Slice<'a> {
160    type Output = &'a mut [u8];
161
162    #[inline(always)]
163    fn try_push(&mut self, b: u8) -> Result<()> {
164        if self.cursor == self.end {
165            Err(Error::SerializeBufferFull)
166        } else {
167            // SAFETY: `self.cursor` is in-bounds and won't be incremented past `self.end` as we
168            // have checked above.
169            unsafe {
170                self.cursor.write(b);
171                self.cursor = self.cursor.add(1);
172            }
173            Ok(())
174        }
175    }
176
177    #[inline(always)]
178    fn try_extend(&mut self, b: &[u8]) -> Result<()> {
179        let remain = (self.end as usize) - (self.cursor as usize);
180        let blen = b.len();
181        if blen > remain {
182            Err(Error::SerializeBufferFull)
183        } else {
184            // SAFETY: `self.cursor` is in-bounds for `blen` elements and won't be incremented past
185            // `self.end` as we have checked above.
186            unsafe {
187                core::ptr::copy_nonoverlapping(b.as_ptr(), self.cursor, blen);
188                self.cursor = self.cursor.add(blen);
189            }
190            Ok(())
191        }
192    }
193
194    fn finalize(self) -> Result<Self::Output> {
195        let used = (self.cursor as usize) - (self.start as usize);
196        // SAFETY: `self.cursor` is in-bounds for `used` elements
197        let sli = unsafe { core::slice::from_raw_parts_mut(self.start, used) };
198        Ok(sli)
199    }
200}
201
202impl Index<usize> for Slice<'_> {
203    type Output = u8;
204
205    fn index(&self, idx: usize) -> &u8 {
206        let len = (self.end as usize) - (self.start as usize);
207        assert!(idx < len);
208        // SAFETY: `self.start` is in-bounds at `idx`
209        unsafe { &*self.start.add(idx) }
210    }
211}
212
213impl IndexMut<usize> for Slice<'_> {
214    fn index_mut(&mut self, idx: usize) -> &mut u8 {
215        let len = (self.end as usize) - (self.start as usize);
216        assert!(idx < len);
217        // SAFETY: `self.start` is in-bounds at `idx`
218        unsafe { &mut *self.start.add(idx) }
219    }
220}
221
222/// Wrapper over a [`core::iter::Extend<u8>`] that implements the flavor trait
223pub struct ExtendFlavor<T> {
224    iter: T,
225}
226
227impl<T> ExtendFlavor<T>
228where
229    T: core::iter::Extend<u8>,
230{
231    /// Create a new [`Self`] flavor from a given [`core::iter::Extend<u8>`]
232    pub fn new(iter: T) -> Self {
233        Self { iter }
234    }
235}
236
237impl<T> Flavor for ExtendFlavor<T>
238where
239    T: core::iter::Extend<u8>,
240{
241    type Output = T;
242
243    #[inline(always)]
244    fn try_push(&mut self, data: u8) -> Result<()> {
245        self.iter.extend([data]);
246        Ok(())
247    }
248
249    #[inline(always)]
250    fn try_extend(&mut self, b: &[u8]) -> Result<()> {
251        self.iter.extend(b.iter().copied());
252        Ok(())
253    }
254
255    fn finalize(self) -> Result<Self::Output> {
256        Ok(self.iter)
257    }
258}
259
260/// Support for the [`embedded-io`](crate::eio::embedded_io) traits
261#[cfg(any(feature = "embedded-io-04", feature = "embedded-io-06"))]
262pub mod eio {
263
264    use super::Flavor;
265    use crate::{Error, Result};
266
267    /// Wrapper over a [`embedded_io Write`](crate::eio::Write) that implements the flavor trait
268    pub struct WriteFlavor<T> {
269        writer: T,
270    }
271
272    impl<T> WriteFlavor<T>
273    where
274        T: crate::eio::Write,
275    {
276        /// Create a new [`Self`] flavor from a given [`embedded_io Write`](crate::eio::Write)
277        pub fn new(writer: T) -> Self {
278            Self { writer }
279        }
280    }
281
282    impl<T> Flavor for WriteFlavor<T>
283    where
284        T: crate::eio::Write,
285    {
286        type Output = T;
287
288        #[inline(always)]
289        fn try_push(&mut self, data: u8) -> Result<()> {
290            self.writer
291                .write_all(&[data])
292                .map_err(|_| Error::SerializeBufferFull)?;
293            Ok(())
294        }
295
296        #[inline(always)]
297        fn try_extend(&mut self, b: &[u8]) -> Result<()> {
298            self.writer
299                .write_all(b)
300                .map_err(|_| Error::SerializeBufferFull)?;
301            Ok(())
302        }
303
304        fn finalize(mut self) -> Result<Self::Output> {
305            self.writer
306                .flush()
307                .map_err(|_| Error::SerializeBufferFull)?;
308            Ok(self.writer)
309        }
310    }
311}
312
313/// Support for the [`std::io`] traits
314#[cfg(feature = "use-std")]
315pub mod io {
316
317    use super::Flavor;
318    use crate::{Error, Result};
319
320    /// Wrapper over a [`std::io::Write`] that implements the flavor trait
321    pub struct WriteFlavor<T> {
322        writer: T,
323    }
324
325    impl<T> WriteFlavor<T>
326    where
327        T: std::io::Write,
328    {
329        /// Create a new [`Self`] flavor from a given [`std::io::Write`]
330        pub fn new(writer: T) -> Self {
331            Self { writer }
332        }
333    }
334
335    impl<T> Flavor for WriteFlavor<T>
336    where
337        T: std::io::Write,
338    {
339        type Output = T;
340
341        #[inline(always)]
342        fn try_push(&mut self, data: u8) -> Result<()> {
343            self.writer
344                .write_all(&[data])
345                .map_err(|_| Error::SerializeBufferFull)?;
346            Ok(())
347        }
348
349        #[inline(always)]
350        fn try_extend(&mut self, b: &[u8]) -> Result<()> {
351            self.writer
352                .write_all(b)
353                .map_err(|_| Error::SerializeBufferFull)?;
354            Ok(())
355        }
356
357        fn finalize(mut self) -> Result<Self::Output> {
358            self.writer
359                .flush()
360                .map_err(|_| Error::SerializeBufferFull)?;
361            Ok(self.writer)
362        }
363    }
364}
365
366#[cfg(feature = "heapless")]
367mod heapless_vec {
368    use super::Flavor;
369    use super::Index;
370    use super::IndexMut;
371    use crate::{Error, Result};
372    use heapless::Vec;
373
374    ////////////////////////////////////////
375    // HVec
376    ////////////////////////////////////////
377
378    /// The `HVec` flavor is a wrapper type around a `heapless::Vec`. This is a stack
379    /// allocated data structure, with a fixed maximum size and variable amount of contents.
380    #[derive(Default)]
381    pub struct HVec<const B: usize> {
382        /// the contained data buffer
383        vec: Vec<u8, B>,
384    }
385
386    impl<const B: usize> HVec<B> {
387        /// Create a new, currently empty, [`heapless::Vec`] to be used for storing serialized
388        /// output data.
389        pub fn new() -> Self {
390            Self::default()
391        }
392    }
393
394    impl<const B: usize> Flavor for HVec<B> {
395        type Output = Vec<u8, B>;
396
397        #[inline(always)]
398        fn try_extend(&mut self, data: &[u8]) -> Result<()> {
399            self.vec
400                .extend_from_slice(data)
401                .map_err(|_| Error::SerializeBufferFull)
402        }
403
404        #[inline(always)]
405        fn try_push(&mut self, data: u8) -> Result<()> {
406            self.vec.push(data).map_err(|_| Error::SerializeBufferFull)
407        }
408
409        fn finalize(self) -> Result<Vec<u8, B>> {
410            Ok(self.vec)
411        }
412    }
413
414    impl<const B: usize> Index<usize> for HVec<B> {
415        type Output = u8;
416
417        fn index(&self, idx: usize) -> &u8 {
418            &self.vec[idx]
419        }
420    }
421
422    impl<const B: usize> IndexMut<usize> for HVec<B> {
423        fn index_mut(&mut self, idx: usize) -> &mut u8 {
424            &mut self.vec[idx]
425        }
426    }
427}
428
429#[cfg(feature = "use-std")]
430mod std_vec {
431    /// The `StdVec` flavor is a wrapper type around a `std::vec::Vec`.
432    ///
433    /// This type is only available when the (non-default) `use-std` feature is active
434    pub type StdVec = super::alloc_vec::AllocVec;
435}
436
437#[cfg(feature = "alloc")]
438mod alloc_vec {
439    extern crate alloc;
440    use super::Flavor;
441    use super::Index;
442    use super::IndexMut;
443    use crate::Result;
444    use alloc::vec::Vec;
445
446    /// The `AllocVec` flavor is a wrapper type around an [`alloc::vec::Vec`].
447    ///
448    /// This type is only available when the (non-default) `alloc` feature is active
449    #[derive(Default)]
450    pub struct AllocVec {
451        /// The vec to be used for serialization
452        vec: Vec<u8>,
453    }
454
455    impl AllocVec {
456        /// Create a new, currently empty, [`alloc::vec::Vec`] to be used for storing serialized
457        /// output data.
458        pub fn new() -> Self {
459            Self::default()
460        }
461    }
462
463    impl Flavor for AllocVec {
464        type Output = Vec<u8>;
465
466        #[inline(always)]
467        fn try_extend(&mut self, data: &[u8]) -> Result<()> {
468            self.vec.extend_from_slice(data);
469            Ok(())
470        }
471
472        #[inline(always)]
473        fn try_push(&mut self, data: u8) -> Result<()> {
474            self.vec.push(data);
475            Ok(())
476        }
477
478        fn finalize(self) -> Result<Self::Output> {
479            Ok(self.vec)
480        }
481    }
482
483    impl Index<usize> for AllocVec {
484        type Output = u8;
485
486        #[inline]
487        fn index(&self, idx: usize) -> &u8 {
488            &self.vec[idx]
489        }
490    }
491
492    impl IndexMut<usize> for AllocVec {
493        #[inline]
494        fn index_mut(&mut self, idx: usize) -> &mut u8 {
495            &mut self.vec[idx]
496        }
497    }
498}
499
500////////////////////////////////////////////////////////////////////////////////
501// Modification Flavors
502////////////////////////////////////////////////////////////////////////////////
503
504////////////////////////////////////////
505// COBS
506////////////////////////////////////////
507
508/// The `Cobs` flavor implements [Consistent Overhead Byte Stuffing] on
509/// the serialized data. The output of this flavor includes the termination/sentinel
510/// byte of `0x00`.
511///
512/// This protocol is useful when sending data over a serial interface without framing such as a UART
513///
514/// [Consistent Overhead Byte Stuffing]: https://en.wikipedia.org/wiki/Consistent_Overhead_Byte_Stuffing
515pub struct Cobs<B>
516where
517    B: Flavor + IndexMut<usize, Output = u8>,
518{
519    flav: B,
520    cobs: EncoderState,
521}
522
523impl<B> Cobs<B>
524where
525    B: Flavor + IndexMut<usize, Output = u8>,
526{
527    /// Create a new Cobs modifier Flavor. If there is insufficient space
528    /// to push the leading header byte, the method will return an Error
529    pub fn try_new(mut bee: B) -> Result<Self> {
530        bee.try_push(0).map_err(|_| Error::SerializeBufferFull)?;
531        Ok(Self {
532            flav: bee,
533            cobs: EncoderState::default(),
534        })
535    }
536}
537
538impl<B> Flavor for Cobs<B>
539where
540    B: Flavor + IndexMut<usize, Output = u8>,
541{
542    type Output = <B as Flavor>::Output;
543
544    #[inline(always)]
545    fn try_push(&mut self, data: u8) -> Result<()> {
546        use PushResult::*;
547        match self.cobs.push(data) {
548            AddSingle(n) => self.flav.try_push(n),
549            ModifyFromStartAndSkip((idx, mval)) => {
550                self.flav[idx] = mval;
551                self.flav.try_push(0)
552            }
553            ModifyFromStartAndPushAndSkip((idx, mval, nval)) => {
554                self.flav[idx] = mval;
555                self.flav.try_push(nval)?;
556                self.flav.try_push(0)
557            }
558        }
559    }
560
561    fn finalize(mut self) -> Result<Self::Output> {
562        let (idx, mval) = self.cobs.finalize();
563        self.flav[idx] = mval;
564        self.flav.try_push(0)?;
565        self.flav.finalize()
566    }
567}
568
569////////////////////////////////////////
570// CRC
571////////////////////////////////////////
572
573/// This Cyclic Redundancy Check flavor applies [the CRC crate's `Algorithm`](https://docs.rs/crc/latest/crc/struct.Algorithm.html) struct on
574/// the serialized data.
575///
576/// The output of this flavor receives the CRC appended to the bytes.
577/// CRCs are used for error detection when reading data back.
578/// Requires the `crc` feature.
579///
580/// More on CRCs: <https://en.wikipedia.org/wiki/Cyclic_redundancy_check>.
581#[cfg(feature = "use-crc")]
582#[cfg_attr(docsrs, doc(cfg(feature = "use-crc")))]
583pub mod crc {
584    use crc::Digest;
585    use crc::Width;
586    use serde::Serialize;
587
588    #[cfg(feature = "alloc")]
589    use super::alloc;
590    use super::Flavor;
591    use super::Slice;
592
593    use crate::serialize_with_flavor;
594    use crate::Result;
595
596    /// Manages CRC modifications as a flavor.
597    pub struct CrcModifier<'a, B, W>
598    where
599        B: Flavor,
600        W: Width,
601    {
602        flav: B,
603        digest: Digest<'a, W>,
604    }
605
606    impl<'a, B, W> CrcModifier<'a, B, W>
607    where
608        B: Flavor,
609        W: Width,
610    {
611        /// Create a new CRC modifier Flavor.
612        pub fn new(bee: B, digest: Digest<'a, W>) -> Self {
613            Self { flav: bee, digest }
614        }
615    }
616
617    macro_rules! impl_flavor {
618        ($int:ty, $to_slice:ident, $to_vec:ident, $to_allocvec:ident) => {
619            impl<'a, B> Flavor for CrcModifier<'a, B, $int>
620            where
621                B: Flavor,
622            {
623                type Output = <B as Flavor>::Output;
624
625                #[inline(always)]
626                fn try_push(&mut self, data: u8) -> Result<()> {
627                    self.digest.update(&[data]);
628                    self.flav.try_push(data)
629                }
630
631                fn finalize(mut self) -> Result<Self::Output> {
632                    let crc = self.digest.finalize();
633                    for byte in crc.to_le_bytes() {
634                        self.flav.try_push(byte)?;
635                    }
636                    self.flav.finalize()
637                }
638            }
639
640            /// Serialize a `T` to the given slice, with the resulting slice containing
641            /// data followed by a CRC. The CRC bytes are included in the output buffer.
642            ///
643            /// When successful, this function returns the slice containing the
644            /// serialized and encoded message.
645            pub fn $to_slice<'a, T>(
646                value: &T,
647                buf: &'a mut [u8],
648                digest: Digest<'_, $int>,
649            ) -> Result<&'a mut [u8]>
650            where
651                T: Serialize + ?Sized,
652            {
653                serialize_with_flavor(value, CrcModifier::new(Slice::new(buf), digest))
654            }
655
656            /// Serialize a `T` to a `heapless::Vec<u8>`, with the `Vec` containing
657            /// data followed by a CRC. The CRC bytes are included in the output `Vec`.
658            #[cfg(feature = "heapless")]
659            #[cfg_attr(docsrs, doc(cfg(feature = "heapless")))]
660            pub fn $to_vec<T, const B: usize>(
661                value: &T,
662                digest: Digest<'_, $int>,
663            ) -> Result<heapless::Vec<u8, B>>
664            where
665                T: Serialize + ?Sized,
666            {
667                use super::HVec;
668                serialize_with_flavor(value, CrcModifier::new(HVec::default(), digest))
669            }
670
671            /// Serialize a `T` to a `heapless::Vec<u8>`, with the `Vec` containing
672            /// data followed by a CRC. The CRC bytes are included in the output `Vec`.
673            #[cfg(feature = "alloc")]
674            #[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
675            pub fn $to_allocvec<T>(
676                value: &T,
677                digest: Digest<'_, $int>,
678            ) -> Result<alloc::vec::Vec<u8>>
679            where
680                T: Serialize + ?Sized,
681            {
682                use super::AllocVec;
683                serialize_with_flavor(value, CrcModifier::new(AllocVec::new(), digest))
684            }
685        };
686    }
687
688    impl_flavor!(u8, to_slice_u8, to_vec_u8, to_allocvec_u8);
689    impl_flavor!(u16, to_slice_u16, to_vec_u16, to_allocvec_u16);
690    impl_flavor!(u32, to_slice_u32, to_vec_u32, to_allocvec_u32);
691    impl_flavor!(u64, to_slice_u64, to_vec_u64, to_allocvec_u64);
692    impl_flavor!(u128, to_slice_u128, to_vec_u128, to_allocvec_u128);
693}
694
695/// The `Size` flavor is a measurement flavor, which accumulates the number of bytes needed to
696/// serialize the data.
697///
698/// ```
699/// use postcard::{serialize_with_flavor, ser_flavors};
700///
701/// let value = false;
702/// let size = serialize_with_flavor(&value, ser_flavors::Size::default()).unwrap();
703///
704/// assert_eq!(size, 1);
705/// ```
706#[derive(Default)]
707pub struct Size {
708    size: usize,
709}
710
711impl Flavor for Size {
712    type Output = usize;
713
714    #[inline(always)]
715    fn try_push(&mut self, _b: u8) -> Result<()> {
716        self.size += 1;
717        Ok(())
718    }
719
720    #[inline(always)]
721    fn try_extend(&mut self, b: &[u8]) -> Result<()> {
722        self.size += b.len();
723        Ok(())
724    }
725
726    fn finalize(self) -> Result<Self::Output> {
727        Ok(self.size)
728    }
729}