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}