Skip to content

Commit 8fb6847

Browse files
committed
Add serialization support for heapless v0.8 and v0.9
Add HVecV0_8 and HVecV0_9 flavors, convenience functions (to_vec_v0_8, to_vec_v0_9, to_vec_cobs_v0_8, to_vec_cobs_v0_9), CRC convenience functions (to_vec_crc32_v0_8, to_vec_crc32_v0_9), and re-exports in lib.rs. This allows users to fully migrate off heapless 0.7 (and its unmaintained atomic-polyfill dependency, RUSTSEC-2023-0089) by using default-features = false with heapless-v0_8 or heapless-v0_9 feature flags. Bump version to 1.2.0 (additive public API). Refs: #223
1 parent de18255 commit 8fb6847

3 files changed

Lines changed: 282 additions & 0 deletions

File tree

source/postcard/src/lib.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,12 @@ pub use ser::{serialize_with_flavor, serializer::Serializer, to_extend, to_slice
7575
#[cfg(feature = "heapless")]
7676
pub use ser::{to_vec, to_vec_cobs};
7777

78+
#[cfg(feature = "heapless-v0_8")]
79+
pub use ser::{to_vec_cobs_v0_8, to_vec_v0_8};
80+
81+
#[cfg(feature = "heapless-v0_9")]
82+
pub use ser::{to_vec_cobs_v0_9, to_vec_v0_9};
83+
7884
#[cfg(any(feature = "embedded-io-04", feature = "embedded-io-06"))]
7985
pub use ser::to_eio;
8086

@@ -99,6 +105,12 @@ pub use {
99105
#[cfg(all(feature = "use-crc", feature = "heapless"))]
100106
pub use ser::to_vec_crc32;
101107

108+
#[cfg(all(feature = "use-crc", feature = "heapless-v0_8"))]
109+
pub use ser::to_vec_crc32_v0_8;
110+
111+
#[cfg(all(feature = "use-crc", feature = "heapless-v0_9"))]
112+
pub use ser::to_vec_crc32_v0_9;
113+
102114
#[cfg(all(feature = "use-crc", feature = "use-std"))]
103115
pub use ser::to_stdvec_crc32;
104116

source/postcard/src/ser/flavors.rs

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,12 @@ use core::ops::IndexMut;
9595
#[cfg(feature = "heapless")]
9696
pub use heapless_vec::*;
9797

98+
#[cfg(feature = "heapless-v0_8")]
99+
pub use heapless_vec_v0_8::*;
100+
101+
#[cfg(feature = "heapless-v0_9")]
102+
pub use heapless_vec_v0_9::*;
103+
98104
#[cfg(feature = "use-std")]
99105
pub use std_vec::*;
100106

@@ -426,6 +432,72 @@ mod heapless_vec {
426432
}
427433
}
428434

435+
macro_rules! impl_heapless_vec_flavor {
436+
($feature:literal, $mod_name:ident, $name:ident, $heapless:ident, $version:literal) => {
437+
#[cfg(feature = $feature)]
438+
mod $mod_name {
439+
use super::Flavor;
440+
use super::Index;
441+
use super::IndexMut;
442+
use crate::{Error, Result};
443+
use $heapless::Vec;
444+
445+
#[doc = concat!("The `", stringify!($name), "` flavor is a wrapper type around a `heapless` ", $version, " `Vec`.")]
446+
/// This is a stack allocated data structure, with a fixed maximum size and
447+
/// variable amount of contents.
448+
#[derive(Default)]
449+
pub struct $name<const B: usize> {
450+
vec: Vec<u8, B>,
451+
}
452+
453+
impl<const B: usize> $name<B> {
454+
#[doc = concat!("Create a new, currently empty, `heapless` ", $version, " `Vec` to be used for")]
455+
/// storing serialized output data.
456+
pub fn new() -> Self {
457+
Self::default()
458+
}
459+
}
460+
461+
impl<const B: usize> Flavor for $name<B> {
462+
type Output = Vec<u8, B>;
463+
464+
#[inline(always)]
465+
fn try_extend(&mut self, data: &[u8]) -> Result<()> {
466+
self.vec
467+
.extend_from_slice(data)
468+
.map_err(|_| Error::SerializeBufferFull)
469+
}
470+
471+
#[inline(always)]
472+
fn try_push(&mut self, data: u8) -> Result<()> {
473+
self.vec.push(data).map_err(|_| Error::SerializeBufferFull)
474+
}
475+
476+
fn finalize(self) -> Result<Vec<u8, B>> {
477+
Ok(self.vec)
478+
}
479+
}
480+
481+
impl<const B: usize> Index<usize> for $name<B> {
482+
type Output = u8;
483+
484+
fn index(&self, idx: usize) -> &u8 {
485+
&self.vec[idx]
486+
}
487+
}
488+
489+
impl<const B: usize> IndexMut<usize> for $name<B> {
490+
fn index_mut(&mut self, idx: usize) -> &mut u8 {
491+
&mut self.vec[idx]
492+
}
493+
}
494+
}
495+
};
496+
}
497+
498+
impl_heapless_vec_flavor!("heapless-v0_8", heapless_vec_v0_8, HVecV0_8, heapless_v0_8, "0.8");
499+
impl_heapless_vec_flavor!("heapless-v0_9", heapless_vec_v0_9, HVecV0_9, heapless_v0_9, "0.9");
500+
429501
#[cfg(feature = "use-std")]
430502
mod std_vec {
431503
/// The `StdVec` flavor is a wrapper type around a `std::vec::Vec`.
@@ -690,6 +762,28 @@ pub mod crc {
690762
impl_flavor!(u32, to_slice_u32, to_vec_u32, to_allocvec_u32);
691763
impl_flavor!(u64, to_slice_u64, to_vec_u64, to_allocvec_u64);
692764
impl_flavor!(u128, to_slice_u128, to_vec_u128, to_allocvec_u128);
765+
766+
macro_rules! impl_crc_heapless_vec {
767+
($feature:literal, $fn_name:ident, $hvec:ident, $heapless:ident, $version:literal) => {
768+
#[doc = concat!("Serialize a `T` to a `heapless` ", $version, " `Vec<u8>`, with the `Vec` containing")]
769+
/// data followed by a CRC. The CRC bytes are included in the output `Vec`.
770+
#[cfg(feature = $feature)]
771+
#[cfg_attr(docsrs, doc(cfg(all(feature = "use-crc", feature = $feature))))]
772+
pub fn $fn_name<T, const B: usize>(
773+
value: &T,
774+
digest: Digest<'_, u32>,
775+
) -> Result<$heapless::Vec<u8, B>>
776+
where
777+
T: Serialize + ?Sized,
778+
{
779+
use super::$hvec;
780+
serialize_with_flavor(value, CrcModifier::new($hvec::default(), digest))
781+
}
782+
};
783+
}
784+
785+
impl_crc_heapless_vec!("heapless-v0_8", to_vec_u32_v0_8, HVecV0_8, heapless_v0_8, "0.8");
786+
impl_crc_heapless_vec!("heapless-v0_9", to_vec_u32_v0_9, HVecV0_9, heapless_v0_9, "0.9");
693787
}
694788

695789
/// The `Size` flavor is a measurement flavor, which accumulates the number of bytes needed to

source/postcard/src/ser/mod.rs

Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,12 @@ use crate::ser::flavors::HVec;
88
#[cfg(feature = "heapless")]
99
use heapless::Vec;
1010

11+
#[cfg(feature = "heapless-v0_8")]
12+
use crate::ser::flavors::HVecV0_8;
13+
14+
#[cfg(feature = "heapless-v0_9")]
15+
use crate::ser::flavors::HVecV0_9;
16+
1117
#[cfg(feature = "alloc")]
1218
use crate::ser::flavors::AllocVec;
1319

@@ -158,6 +164,38 @@ where
158164
serialize_with_flavor::<T, HVec<B>, Vec<u8, B>>(value, HVec::default())
159165
}
160166

167+
macro_rules! impl_heapless_ser_fns {
168+
($feature:literal, $to_vec:ident, $to_vec_cobs:ident, $hvec:ident, $heapless:ident, $version:literal) => {
169+
#[doc = concat!("Serialize a `T` to a `heapless` ", $version, " `Vec<u8>`.")]
170+
#[cfg(feature = $feature)]
171+
#[cfg_attr(docsrs, doc(cfg(feature = $feature)))]
172+
pub fn $to_vec<T, const B: usize>(value: &T) -> Result<$heapless::Vec<u8, B>>
173+
where
174+
T: Serialize + ?Sized,
175+
{
176+
serialize_with_flavor::<T, $hvec<B>, $heapless::Vec<u8, B>>(value, $hvec::default())
177+
}
178+
179+
#[doc = concat!("Serialize and COBS encode a `T` to a `heapless` ", $version, " `Vec<u8>`.")]
180+
///
181+
/// The terminating sentinel `0x00` byte is included in the output.
182+
#[cfg(feature = $feature)]
183+
#[cfg_attr(docsrs, doc(cfg(feature = $feature)))]
184+
pub fn $to_vec_cobs<T, const B: usize>(value: &T) -> Result<$heapless::Vec<u8, B>>
185+
where
186+
T: Serialize + ?Sized,
187+
{
188+
serialize_with_flavor::<T, Cobs<$hvec<B>>, $heapless::Vec<u8, B>>(
189+
value,
190+
Cobs::try_new($hvec::default())?,
191+
)
192+
}
193+
};
194+
}
195+
196+
impl_heapless_ser_fns!("heapless-v0_8", to_vec_v0_8, to_vec_cobs_v0_8, HVecV0_8, heapless_v0_8, "0.8");
197+
impl_heapless_ser_fns!("heapless-v0_9", to_vec_v0_9, to_vec_cobs_v0_9, HVecV0_9, heapless_v0_9, "0.9");
198+
161199
/// Serialize a `T` to a `std::vec::Vec<u8>`.
162200
///
163201
/// ## Example
@@ -447,6 +485,28 @@ where
447485
flavors::crc::to_allocvec_u32(value, digest)
448486
}
449487

488+
macro_rules! impl_heapless_crc_wrapper {
489+
($feature:literal, $pub_fn:ident, $internal_fn:ident, $heapless:ident, $version:literal) => {
490+
#[doc = concat!("Conveniently serialize a `T` to a `heapless` ", $version, " `Vec<u8>`, with the `Vec` containing")]
491+
/// data followed by a 32-bit CRC. The CRC bytes are included in the output `Vec`.
492+
#[cfg(all(feature = "use-crc", feature = $feature))]
493+
#[cfg_attr(docsrs, doc(cfg(all(feature = "use-crc", feature = $feature))))]
494+
#[inline]
495+
pub fn $pub_fn<T, const B: usize>(
496+
value: &T,
497+
digest: crc::Digest<'_, u32>,
498+
) -> Result<$heapless::Vec<u8, B>>
499+
where
500+
T: Serialize + ?Sized,
501+
{
502+
flavors::crc::$internal_fn(value, digest)
503+
}
504+
};
505+
}
506+
507+
impl_heapless_crc_wrapper!("heapless-v0_8", to_vec_crc32_v0_8, to_vec_u32_v0_8, heapless_v0_8, "0.8");
508+
impl_heapless_crc_wrapper!("heapless-v0_9", to_vec_crc32_v0_9, to_vec_u32_v0_9, heapless_v0_9, "0.9");
509+
450510
/// `serialize_with_flavor()` has three generic parameters, `T, F, O`.
451511
///
452512
/// * `T`: This is the type that is being serialized
@@ -861,3 +921,119 @@ mod test {
861921
assert_eq!(input, x);
862922
}
863923
}
924+
925+
macro_rules! impl_heapless_ser_tests {
926+
($feature:literal, $mod_name:ident, $to_vec:ident, $to_vec_cobs:ident, $heapless:ident) => {
927+
#[cfg(feature = $feature)]
928+
#[cfg(test)]
929+
mod $mod_name {
930+
use super::*;
931+
use core::ops::{Deref, DerefMut};
932+
use serde::Deserialize;
933+
934+
#[test]
935+
fn ser_u8() {
936+
let output: $heapless::Vec<u8, 1> = $to_vec(&0x05u8).unwrap();
937+
assert_eq!(&[5], output.deref());
938+
}
939+
940+
#[test]
941+
fn ser_u16() {
942+
let output: $heapless::Vec<u8, 3> = $to_vec(&0xA5C7u16).unwrap();
943+
assert_eq!(&[0xC7, 0xCB, 0x02], output.deref());
944+
}
945+
946+
#[derive(Serialize, Deserialize, Eq, PartialEq, Debug)]
947+
struct TestStruct {
948+
a: u8,
949+
b: u32,
950+
}
951+
952+
#[test]
953+
fn ser_struct() {
954+
let input = TestStruct { a: 0x05, b: 0x1234 };
955+
let output: $heapless::Vec<u8, 8> = $to_vec(&input).unwrap();
956+
assert_eq!(&[0x05, 0xB4, 0x24], output.deref());
957+
}
958+
959+
#[derive(Serialize, Deserialize, Eq, PartialEq, Debug)]
960+
enum TestEnum {
961+
A,
962+
B(u8),
963+
}
964+
965+
#[test]
966+
fn ser_enum() {
967+
let output: $heapless::Vec<u8, 1> = $to_vec(&TestEnum::A).unwrap();
968+
assert_eq!(&[0x00], output.deref());
969+
970+
let output: $heapless::Vec<u8, 2> = $to_vec(&TestEnum::B(0xFF)).unwrap();
971+
assert_eq!(&[0x01, 0xFF], output.deref());
972+
}
973+
974+
#[test]
975+
fn ser_str() {
976+
let input: &str = "Hi!";
977+
let output: $heapless::Vec<u8, 8> = $to_vec(input).unwrap();
978+
assert_eq!(&[0x03, b'H', b'i', b'!'], output.deref());
979+
}
980+
981+
#[test]
982+
fn ser_byte_slice() {
983+
let input: &[u8] = &[1u8, 2, 3, 4];
984+
let output: $heapless::Vec<u8, 8> = $to_vec(input).unwrap();
985+
assert_eq!(&[0x04, 0x01, 0x02, 0x03, 0x04], output.deref());
986+
}
987+
988+
#[test]
989+
fn buffer_full() {
990+
let input: &[u8] = &[0u8; 32];
991+
let result: Result<$heapless::Vec<u8, 4>> = $to_vec(input);
992+
assert_eq!(result, Err(Error::SerializeBufferFull));
993+
}
994+
995+
#[derive(Serialize, Deserialize, Eq, PartialEq, Debug)]
996+
struct RefStruct<'a> {
997+
bytes: &'a [u8],
998+
str_s: &'a str,
999+
}
1000+
1001+
#[test]
1002+
fn cobs_roundtrip() {
1003+
let message = "hElLo";
1004+
let bytes = [0x01, 0x00, 0x02, 0x20];
1005+
let input = RefStruct {
1006+
bytes: &bytes,
1007+
str_s: message,
1008+
};
1009+
1010+
let mut output: $heapless::Vec<u8, 13> = $to_vec_cobs(&input).unwrap();
1011+
1012+
let sz = cobs::decode_in_place(output.deref_mut()).unwrap();
1013+
1014+
let x = crate::from_bytes::<RefStruct<'_>>(&output.deref_mut()[..sz]).unwrap();
1015+
1016+
assert_eq!(input, x);
1017+
}
1018+
1019+
#[test]
1020+
fn deser_roundtrip() {
1021+
let input = TestStruct { a: 0xAB, b: 0xCDEF };
1022+
let bytes: $heapless::Vec<u8, 8> = $to_vec(&input).unwrap();
1023+
let output: TestStruct = crate::from_bytes(bytes.deref()).unwrap();
1024+
assert_eq!(input, output);
1025+
}
1026+
1027+
#[test]
1028+
fn deser_enum_roundtrip() {
1029+
let input = TestEnum::B(0x42);
1030+
let bytes: $heapless::Vec<u8, 4> = $to_vec(&input).unwrap();
1031+
let output: TestEnum = crate::from_bytes(bytes.deref()).unwrap();
1032+
assert_eq!(input, output);
1033+
}
1034+
}
1035+
};
1036+
}
1037+
1038+
impl_heapless_ser_tests!("heapless-v0_8", test_v0_8, to_vec_v0_8, to_vec_cobs_v0_8, heapless_v0_8);
1039+
impl_heapless_ser_tests!("heapless-v0_9", test_v0_9, to_vec_v0_9, to_vec_cobs_v0_9, heapless_v0_9);

0 commit comments

Comments
 (0)