postcard/de/
flavors.rs

1//! # Deserialization 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 source medium of the deserialization, e.g. whether the data is serialized from a `[u8]` slice, or some other container
7//! 2. The format of the deserialization, such as if the original data is encoded in a COBS format, contains a CRC32 checksum
8//!    appended to the message, etc.
9//!
10//! Flavors are implemented using the [`Flavor`] trait, which acts as a "middleware" for retrieving the bytes before they
11//! are passed to `serde` for deserialization
12//!
13//! Multiple flavors may be combined to obtain a desired combination of behavior and storage.
14//! When flavors are combined, it is expected that the storage flavor (such as [`Slice`]) is the innermost flavor.
15//!
16//! Custom flavors may be defined by users of the `postcard` crate, however some commonly useful flavors have been provided in
17//! this module. If you think your custom flavor would be useful to others, PRs adding flavors are very welcome!
18//!
19//! ## Usability
20//!
21//! Flavors may not always be convenient to use directly, as they may expose some implementation details of how the
22//! inner workings of the flavor behaves. It is typical to provide a convenience method for using a flavor, to prevent
23//! the user from having to specify generic parameters, setting correct initialization values, or handling the output of
24//! the flavor correctly. See `postcard::from_bytes()` for an example of this.
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 deserialization through multiple steps is that it is typically slower than
36//! performing each step serially. Said simply, "cobs decoding while deserializing" is often slower
37//! than "cobs decode then deserialize", 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//! Additionally, deserializating flavors can be more restrictive or difficult to work with than
42//! serialization flavors, as deserialization may require that the deserialized types borrow some
43//! portion of the original message.
44//!
45//! ## Examples
46//!
47//! ### Using a single flavor
48//!
49//! In the first example, we use the `Slice` flavor, to retrieve the serialized output from a `[u8]` slice.
50//! No other modification is made to the serialization process.
51//!
52//! ```rust
53//! use postcard::{
54//!     de_flavors::Slice,
55//!     Deserializer,
56//! };
57//! use serde::Deserialize;
58//!
59//! #[derive(Deserialize, Debug, PartialEq)]
60//! struct Tup(u8, u8, u8);
61//!
62//! let msg = [0x04, 0x00, 0x04, 0x01, 0x02, 0x03];
63//! let slice = Slice::new(&msg);
64//! let mut deserializer = Deserializer::from_flavor(slice);
65//! let t = Tup::deserialize(&mut deserializer).unwrap();
66//! assert_eq!(t, Tup(4, 0, 4));
67//! let remainder = deserializer.finalize().unwrap();
68//! assert_eq!(remainder, &[1, 2, 3]);
69//! ```
70
71use crate::{Error, Result};
72use core::marker::PhantomData;
73
74/// The deserialization Flavor trait
75///
76/// This is used as the primary way to decode serialized data from some kind of buffer,
77/// or modify that data in a middleware style pattern.
78///
79/// See the module level docs for an example of how flavors are used.
80pub trait Flavor<'de>: 'de {
81    /// The remaining data of this flavor after deserializing has completed.
82    ///
83    /// Typically, this includes the remaining buffer that was not used for
84    /// deserialization, and in cases of more complex flavors, any additional
85    /// information that was decoded or otherwise calculated during
86    /// the deserialization process.
87    type Remainder: 'de;
88
89    /// The source of data retrieved for deserialization.
90    ///
91    /// This is typically some sort of data buffer, or another Flavor, when
92    /// chained behavior is desired
93    type Source: 'de;
94
95    /// Obtain the next byte for deserialization
96    fn pop(&mut self) -> Result<u8>;
97
98    /// Returns the number of bytes remaining in the message, if known.
99    ///
100    /// # Implementation notes
101    ///
102    /// It is not enforced that this number is exactly correct.
103    /// A flavor may yield less or more bytes than the what is hinted at by
104    /// this function.
105    ///
106    /// `size_hint()` is primarily intended to be used for optimizations such as
107    /// reserving space for deserialized items, but must not be trusted to
108    /// e.g., omit bounds checks in unsafe code. An incorrect implementation of
109    /// `size_hint()` should not lead to memory safety violations.
110    ///
111    /// That said, the implementation should provide a correct estimation,
112    /// because otherwise it would be a violation of the trait’s protocol.
113    ///
114    /// The default implementation returns `None` which is correct for any flavor.
115    fn size_hint(&self) -> Option<usize> {
116        None
117    }
118
119    /// Attempt to take the next `ct` bytes from the serialized message.
120    ///
121    /// This variant borrows the data from the input for zero-copy deserialization. If zero-copy
122    /// deserialization is not necessary, prefer to use `try_take_n_temp` instead.
123    fn try_take_n(&mut self, ct: usize) -> Result<&'de [u8]>;
124
125    /// Attempt to take the next `ct` bytes from the serialized message.
126    ///
127    /// This variant does not guarantee that the returned value is borrowed from the input, so it
128    /// cannot be used for zero-copy deserialization, but it also avoids needing to potentially
129    /// allocate a data in a temporary buffer.
130    ///
131    /// This variant should be used instead of `try_take_n`
132    /// if zero-copy deserialization is not necessary.
133    ///
134    /// It is only necessary to implement this method if the flavor requires storing data in a
135    /// temporary buffer in order to implement the borrow semantics, e.g. the `std::io::Read`
136    /// flavor.
137    fn try_take_n_temp<'a>(&'a mut self, ct: usize) -> Result<&'a [u8]>
138    where
139        'de: 'a,
140    {
141        self.try_take_n(ct)
142    }
143
144    /// Complete the deserialization process.
145    ///
146    /// This is typically called separately, after the `serde` deserialization
147    /// has completed.
148    fn finalize(self) -> Result<Self::Remainder>;
149}
150
151/// A simple [`Flavor`] representing the deserialization from a borrowed slice
152pub struct Slice<'de> {
153    // This string starts with the input data and characters are truncated off
154    // the beginning as data is parsed.
155    pub(crate) cursor: *const u8,
156    pub(crate) end: *const u8,
157    pub(crate) _pl: PhantomData<&'de [u8]>,
158}
159
160impl<'de> Slice<'de> {
161    /// Create a new [Slice] from the given buffer
162    pub fn new(sli: &'de [u8]) -> Self {
163        let range = sli.as_ptr_range();
164        Self {
165            cursor: range.start,
166            end: range.end,
167            _pl: PhantomData,
168        }
169    }
170}
171
172impl<'de> Flavor<'de> for Slice<'de> {
173    type Remainder = &'de [u8];
174    type Source = &'de [u8];
175
176    #[inline]
177    fn pop(&mut self) -> Result<u8> {
178        if self.cursor == self.end {
179            Err(Error::DeserializeUnexpectedEnd)
180        } else {
181            // SAFETY: `self.cursor` is in-bounds and won't be incremented past `self.end` as we
182            // have checked above.
183            unsafe {
184                let res = Ok(*self.cursor);
185                self.cursor = self.cursor.add(1);
186                res
187            }
188        }
189    }
190
191    #[inline]
192    fn size_hint(&self) -> Option<usize> {
193        Some((self.end as usize) - (self.cursor as usize))
194    }
195
196    #[inline]
197    fn try_take_n(&mut self, ct: usize) -> Result<&'de [u8]> {
198        let remain = (self.end as usize) - (self.cursor as usize);
199        if remain < ct {
200            Err(Error::DeserializeUnexpectedEnd)
201        } else {
202            // SAFETY: `self.cursor` is valid for `ct` elements and won't be incremented past `self.end` as we
203            // have checked above.
204            unsafe {
205                let sli = core::slice::from_raw_parts(self.cursor, ct);
206                self.cursor = self.cursor.add(ct);
207                Ok(sli)
208            }
209        }
210    }
211
212    /// Return the remaining (unused) bytes in the Deserializer
213    fn finalize(self) -> Result<&'de [u8]> {
214        let remain = (self.end as usize) - (self.cursor as usize);
215        // SAFETY: `self.cursor` is valid for `remain` elements
216        unsafe { Ok(core::slice::from_raw_parts(self.cursor, remain)) }
217    }
218}
219
220/// Support for [`std::io`] or `embedded-io` traits
221#[cfg(any(
222    feature = "embedded-io-04",
223    feature = "embedded-io-06",
224    feature = "use-std"
225))]
226pub mod io {
227    use crate::{Error, Result};
228    use core::marker::PhantomData;
229
230    struct SlidingBuffer<'de> {
231        cursor: *mut u8,
232        end: *const u8,
233        _pl: PhantomData<&'de [u8]>,
234    }
235
236    impl<'de> SlidingBuffer<'de> {
237        pub fn new(sli: &'de mut [u8]) -> Self {
238            let range = sli.as_mut_ptr_range();
239            Self {
240                cursor: range.start,
241                end: range.end,
242                _pl: PhantomData,
243            }
244        }
245
246        #[inline]
247        fn take_n(&mut self, ct: usize) -> Result<&'de mut [u8]> {
248            let remain = (self.end as usize) - (self.cursor as usize);
249            let buff = if remain < ct {
250                return Err(Error::DeserializeUnexpectedEnd);
251            } else {
252                // SAFETY: `self.cursor` is valid for `ct` elements and won't be incremented
253                // past `self.end` as we have checked above.
254                unsafe {
255                    let sli = core::slice::from_raw_parts_mut(self.cursor, ct);
256                    self.cursor = self.cursor.add(ct);
257                    sli
258                }
259            };
260
261            Ok(buff)
262        }
263
264        #[inline]
265        fn take_n_temp(&mut self, ct: usize) -> Result<&mut [u8]> {
266            let remain = (self.end as usize) - (self.cursor as usize);
267            let buff = if remain < ct {
268                return Err(Error::DeserializeUnexpectedEnd);
269            } else {
270                unsafe {
271                    let sli = core::slice::from_raw_parts_mut(self.cursor, ct);
272                    sli
273                }
274            };
275
276            Ok(buff)
277        }
278
279        fn complete(self) -> Result<&'de mut [u8]> {
280            let remain = (self.end as usize) - (self.cursor as usize);
281            // SAFETY: `self.cursor` is valid for `remain` elements
282            unsafe { Ok(core::slice::from_raw_parts_mut(self.cursor, remain)) }
283        }
284    }
285
286    /// Support for [`embedded_io`](crate::eio::embedded_io) traits
287    #[cfg(any(feature = "embedded-io-04", feature = "embedded-io-06"))]
288    pub mod eio {
289        use super::super::Flavor;
290        use super::SlidingBuffer;
291        use crate::{Error, Result};
292
293        /// Wrapper over a [`embedded_io`](crate::eio::embedded_io)::[`Read`](crate::eio::Read) and a sliding buffer to implement the [`Flavor`] trait
294        pub struct EIOReader<'de, T>
295        where
296            T: crate::eio::Read,
297        {
298            reader: T,
299            buff: SlidingBuffer<'de>,
300        }
301
302        impl<'de, T> EIOReader<'de, T>
303        where
304            T: crate::eio::Read,
305        {
306            /// Create a new [`EIOReader`] from a reader and a buffer.
307            ///
308            /// `buff` must have enough space to hold all data read during the deserialisation.
309            pub fn new(reader: T, buff: &'de mut [u8]) -> Self {
310                Self {
311                    reader,
312                    buff: SlidingBuffer::new(buff),
313                }
314            }
315        }
316
317        impl<'de, T> Flavor<'de> for EIOReader<'de, T>
318        where
319            T: crate::eio::Read + 'de,
320        {
321            type Remainder = (T, &'de mut [u8]);
322            type Source = &'de [u8];
323
324            #[inline]
325            fn pop(&mut self) -> Result<u8> {
326                let mut val = [0; 1];
327                self.reader
328                    .read_exact(&mut val)
329                    .map_err(|_| Error::DeserializeUnexpectedEnd)?;
330                Ok(val[0])
331            }
332
333            #[inline]
334            fn size_hint(&self) -> Option<usize> {
335                None
336            }
337
338            #[inline]
339            fn try_take_n(&mut self, ct: usize) -> Result<&'de [u8]> {
340                let buff = self.buff.take_n(ct)?;
341                self.reader
342                    .read_exact(buff)
343                    .map_err(|_| Error::DeserializeUnexpectedEnd)?;
344                Ok(buff)
345            }
346
347            #[inline]
348            fn try_take_n_temp<'a>(&'a mut self, ct: usize) -> Result<&'a [u8]>
349            where
350                'de: 'a,
351            {
352                let buff = self.buff.take_n_temp(ct)?;
353                self.reader
354                    .read_exact(buff)
355                    .map_err(|_| Error::DeserializeUnexpectedEnd)?;
356                Ok(buff)
357            }
358
359            /// Return the remaining (unused) bytes in the Deserializer
360            fn finalize(self) -> Result<(T, &'de mut [u8])> {
361                let buf = self.buff.complete()?;
362                Ok((self.reader, buf))
363            }
364        }
365
366        #[cfg(test)]
367        mod tests {
368            use super::*;
369
370            #[test]
371            fn test_pop() {
372                let mut reader = EIOReader::new(&[0xAA, 0xBB, 0xCC][..], &mut []);
373
374                assert_eq!(reader.pop(), Ok(0xAA));
375                assert_eq!(reader.pop(), Ok(0xBB));
376                assert_eq!(reader.pop(), Ok(0xCC));
377                assert_eq!(reader.pop(), Err(Error::DeserializeUnexpectedEnd));
378            }
379
380            #[test]
381            fn test_try_take_n() {
382                let mut buf = [0; 8];
383                let mut reader = EIOReader::new(&[0xAA, 0xBB, 0xCC, 0xDD, 0xEE][..], &mut buf);
384
385                assert_eq!(reader.try_take_n(2), Ok(&[0xAA, 0xBB][..]));
386                assert_eq!(reader.try_take_n(2), Ok(&[0xCC, 0xDD][..]));
387                assert_eq!(reader.try_take_n(2), Err(Error::DeserializeUnexpectedEnd));
388            }
389        }
390    }
391
392    /// Support for [`std::io`] traits
393    #[allow(clippy::module_inception)]
394    #[cfg(feature = "use-std")]
395    pub mod io {
396        use super::super::Flavor;
397        use super::SlidingBuffer;
398        use crate::{Error, Result};
399
400        /// Wrapper over a [`std::io::Read`] and a sliding buffer to implement the [Flavor] trait
401        pub struct IOReader<'de, T>
402        where
403            T: std::io::Read,
404        {
405            reader: T,
406            buff: SlidingBuffer<'de>,
407        }
408
409        impl<'de, T> IOReader<'de, T>
410        where
411            T: std::io::Read,
412        {
413            /// Create a new [`IOReader`] from a reader and a buffer.
414            ///
415            /// `buff` must have enough space to hold all data read during the deserialisation.
416            pub fn new(reader: T, buff: &'de mut [u8]) -> Self {
417                Self {
418                    reader,
419                    buff: SlidingBuffer::new(buff),
420                }
421            }
422        }
423
424        impl<'de, T> Flavor<'de> for IOReader<'de, T>
425        where
426            T: std::io::Read + 'de,
427        {
428            type Remainder = (T, &'de mut [u8]);
429            type Source = &'de [u8];
430
431            #[inline]
432            fn pop(&mut self) -> Result<u8> {
433                let mut val = [0; 1];
434                self.reader
435                    .read_exact(&mut val)
436                    .map_err(|_| Error::DeserializeUnexpectedEnd)?;
437                Ok(val[0])
438            }
439
440            #[inline]
441            fn size_hint(&self) -> Option<usize> {
442                None
443            }
444
445            #[inline]
446            fn try_take_n(&mut self, ct: usize) -> Result<&'de [u8]> {
447                let buff = self.buff.take_n(ct)?;
448                self.reader
449                    .read_exact(buff)
450                    .map_err(|_| Error::DeserializeUnexpectedEnd)?;
451                Ok(buff)
452            }
453
454            #[inline]
455            fn try_take_n_temp<'a>(&'a mut self, ct: usize) -> Result<&'a [u8]>
456            where
457                'de: 'a,
458            {
459                let buff = self.buff.take_n_temp(ct)?;
460                self.reader
461                    .read_exact(buff)
462                    .map_err(|_| Error::DeserializeUnexpectedEnd)?;
463                Ok(buff)
464            }
465
466            /// Return the remaining (unused) bytes in the Deserializer
467            fn finalize(self) -> Result<(T, &'de mut [u8])> {
468                let buf = self.buff.complete()?;
469                Ok((self.reader, buf))
470            }
471        }
472
473        #[cfg(test)]
474        mod tests {
475            use super::*;
476
477            #[test]
478            fn test_pop() {
479                let mut reader = IOReader::new(&[0xAA, 0xBB, 0xCC][..], &mut []);
480
481                assert_eq!(reader.pop(), Ok(0xAA));
482                assert_eq!(reader.pop(), Ok(0xBB));
483                assert_eq!(reader.pop(), Ok(0xCC));
484                assert_eq!(reader.pop(), Err(Error::DeserializeUnexpectedEnd));
485            }
486
487            #[test]
488            fn test_try_take_n() {
489                let mut buf = [0; 8];
490                let mut reader = IOReader::new(&[0xAA, 0xBB, 0xCC, 0xDD, 0xEE][..], &mut buf);
491
492                assert_eq!(reader.try_take_n(2), Ok(&[0xAA, 0xBB][..]));
493                assert_eq!(reader.try_take_n(2), Ok(&[0xCC, 0xDD][..]));
494                assert_eq!(reader.try_take_n(2), Err(Error::DeserializeUnexpectedEnd));
495            }
496        }
497    }
498}
499
500////////////////////////////////////////
501// CRC
502////////////////////////////////////////
503
504/// This Cyclic Redundancy Check flavor applies [the CRC crate's `Algorithm`](https://docs.rs/crc/latest/crc/struct.Algorithm.html) struct on
505/// the serialized data.
506///
507/// The flavor will check the CRC assuming that it has been appended to the bytes.
508/// CRCs are used for error detection when reading data back.
509/// Requires the `crc` feature.
510///
511/// More on CRCs: <https://en.wikipedia.org/wiki/Cyclic_redundancy_check>.
512#[cfg(feature = "use-crc")]
513#[cfg_attr(docsrs, doc(cfg(feature = "use-crc")))]
514pub mod crc {
515    use core::convert::TryInto;
516
517    use crc::Digest;
518    use crc::Width;
519    use serde::Deserialize;
520
521    use super::Flavor;
522    use super::Slice;
523
524    use crate::Deserializer;
525    use crate::Error;
526    use crate::Result;
527
528    /// Manages CRC modifications as a flavor.
529    pub struct CrcModifier<'de, B, W>
530    where
531        B: Flavor<'de>,
532        W: Width,
533    {
534        flav: B,
535        digest: Digest<'de, W>,
536    }
537
538    impl<'de, B, W> CrcModifier<'de, B, W>
539    where
540        B: Flavor<'de>,
541        W: Width,
542    {
543        /// Create a new Crc modifier Flavor.
544        pub fn new(bee: B, digest: Digest<'de, W>) -> Self {
545            Self { flav: bee, digest }
546        }
547    }
548
549    macro_rules! impl_flavor {
550        ($int:ty, $from_bytes:ident, $take_from_bytes:ident) => {
551            impl<'de, B> Flavor<'de> for CrcModifier<'de, B, $int>
552            where
553                B: Flavor<'de>,
554            {
555                type Remainder = B::Remainder;
556                type Source = B::Source;
557
558                #[inline]
559                fn pop(&mut self) -> Result<u8> {
560                    match self.flav.pop() {
561                        Ok(byte) => {
562                            self.digest.update(&[byte]);
563                            Ok(byte)
564                        }
565                        e @ Err(_) => e,
566                    }
567                }
568
569                #[inline]
570                fn size_hint(&self) -> Option<usize> {
571                    self.flav.size_hint()
572                }
573
574                #[inline]
575                fn try_take_n(&mut self, ct: usize) -> Result<&'de [u8]> {
576                    match self.flav.try_take_n(ct) {
577                        Ok(bytes) => {
578                            self.digest.update(bytes);
579                            Ok(bytes)
580                        }
581                        e @ Err(_) => e,
582                    }
583                }
584
585                fn finalize(mut self) -> Result<Self::Remainder> {
586                    match self.flav.try_take_n(core::mem::size_of::<$int>()) {
587                        Ok(prev_crc_bytes) => match self.flav.finalize() {
588                            Ok(remainder) => {
589                                let crc = self.digest.finalize();
590                                let le_bytes = prev_crc_bytes
591                                    .try_into()
592                                    .map_err(|_| Error::DeserializeBadEncoding)?;
593                                let prev_crc = <$int>::from_le_bytes(le_bytes);
594                                if crc == prev_crc {
595                                    Ok(remainder)
596                                } else {
597                                    Err(Error::DeserializeBadCrc)
598                                }
599                            }
600                            e @ Err(_) => e,
601                        },
602                        Err(e) => Err(e),
603                    }
604                }
605            }
606
607            /// Deserialize a message of type `T` from a byte slice with a Crc. The unused portion (if any)
608            /// of the byte slice is not returned.
609            pub fn $from_bytes<'a, T>(s: &'a [u8], digest: Digest<'a, $int>) -> Result<T>
610            where
611                T: Deserialize<'a>,
612            {
613                let flav = CrcModifier::new(Slice::new(s), digest);
614                let mut deserializer = Deserializer::from_flavor(flav);
615                let r = T::deserialize(&mut deserializer)?;
616                let _ = deserializer.finalize()?;
617                Ok(r)
618            }
619
620            /// Deserialize a message of type `T` from a byte slice with a Crc. The unused portion (if any)
621            /// of the byte slice is returned for further usage
622            pub fn $take_from_bytes<'a, T>(
623                s: &'a [u8],
624                digest: Digest<'a, $int>,
625            ) -> Result<(T, &'a [u8])>
626            where
627                T: Deserialize<'a>,
628            {
629                let flav = CrcModifier::new(Slice::new(s), digest);
630                let mut deserializer = Deserializer::from_flavor(flav);
631                let t = T::deserialize(&mut deserializer)?;
632                Ok((t, deserializer.finalize()?))
633            }
634        };
635    }
636
637    impl_flavor!(u8, from_bytes_u8, take_from_bytes_u8);
638    impl_flavor!(u16, from_bytes_u16, take_from_bytes_u16);
639    impl_flavor!(u32, from_bytes_u32, take_from_bytes_u32);
640    impl_flavor!(u64, from_bytes_u64, take_from_bytes_u64);
641    impl_flavor!(u128, from_bytes_u128, take_from_bytes_u128);
642}