Skip to content

Commit

Permalink
feat: add function for computing the postcard serialized size of a va…
Browse files Browse the repository at this point in the history
…lue. (#86)

* Add function for computing the postcard serialized size of a value

* add some doc comments

* cleanup api

* simplify and use a flavor

* cleanup visibility

* cleanup

* ...

* fixup docs

Co-authored-by: Lachlan Sneff <[email protected]>
  • Loading branch information
dignifiedquire and lachlansneff-parallel authored Jan 26, 2023
1 parent 7d07930 commit 08f18dc
Show file tree
Hide file tree
Showing 3 changed files with 103 additions and 23 deletions.
2 changes: 2 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ pub mod experimental {
pub use postcard_derive::MaxSize;
}

pub use crate::ser::serialized_size;

/// Compile time Schema generation
#[cfg(feature = "experimental-derive")]
pub mod schema {
Expand Down
36 changes: 36 additions & 0 deletions src/ser/flavors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -410,3 +410,39 @@ where
self.flav.finalize()
}
}

/// The `Size` flavor is a measurement flavor, which accumulates the number of bytes needed to
/// serialize the data.
///
/// ```
/// use postcard::{serialize_with_flavor, ser_flavors};
///
/// let value = false;
/// let size = serialize_with_flavor(&value, ser_flavors::Size::default()).unwrap();
///
/// assert_eq!(size, 1);
/// ```
#[derive(Default)]
pub struct Size {
size: usize,
}

impl Flavor for Size {
type Output = usize;

#[inline(always)]
fn try_push(&mut self, _b: u8) -> Result<()> {
self.size += 1;
Ok(())
}

#[inline(always)]
fn try_extend(&mut self, b: &[u8]) -> Result<()> {
self.size += b.len();
Ok(())
}

fn finalize(self) -> Result<Self::Output> {
Ok(self.size)
}
}
88 changes: 65 additions & 23 deletions src/ser/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,14 @@ where
.map_err(|_| Error::SerializeBufferFull)
}

/// Compute the size of the postcard serialization of `T`.
pub fn serialized_size<T>(value: &T) -> Result<usize>
where
T: Serialize + ?Sized,
{
serialize_with_flavor::<T, flavors::Size, usize>(value, flavors::Size::default())
}

#[cfg(feature = "heapless")]
#[cfg(test)]
mod test {
Expand All @@ -291,6 +299,7 @@ mod test {
fn ser_u8() {
let output: Vec<u8, 1> = to_vec(&0x05u8).unwrap();
assert!(&[5] == output.deref());
assert!(output.len() == serialized_size(&0x05u8).unwrap());
assert!(output.len() <= Vec::<u8, 1>::POSTCARD_MAX_SIZE);
}

Expand All @@ -299,6 +308,7 @@ mod test {
const SZ: usize = varint_max::<u16>();
let output: Vec<u8, SZ> = to_vec(&0xA5C7u16).unwrap();
assert_eq!(&[0xC7, 0xCB, 0x02], output.deref());
assert!(output.len() == serialized_size(&0xA5C7u16).unwrap());
assert!(output.len() <= Vec::<u8, SZ>::POSTCARD_MAX_SIZE);
}

Expand All @@ -307,6 +317,7 @@ mod test {
const SZ: usize = varint_max::<u32>();
let output: Vec<u8, SZ> = to_vec(&0xCDAB3412u32).unwrap();
assert_eq!(&[0x92, 0xE8, 0xAC, 0xED, 0x0C], output.deref());
assert!(output.len() == serialized_size(&0xCDAB3412u32).unwrap());
assert!(output.len() <= Vec::<u8, SZ>::POSTCARD_MAX_SIZE);
}

Expand All @@ -318,6 +329,7 @@ mod test {
&[0xEF, 0x9B, 0xAF, 0x85, 0x89, 0xCF, 0x95, 0x9A, 0x12],
output.deref()
);
assert!(output.len() == serialized_size(&0x1234_5678_90AB_CDEFu64).unwrap());
assert!(output.len() <= Vec::<u8, SZ>::POSTCARD_MAX_SIZE);
}

Expand All @@ -332,6 +344,10 @@ mod test {
],
output.deref()
);
assert!(
output.len()
== serialized_size(&0x1234_5678_90AB_CDEF_1234_5678_90AB_CDEFu128).unwrap()
);
assert!(output.len() <= Vec::<u8, SZ>::POSTCARD_MAX_SIZE);
}

Expand All @@ -357,14 +373,14 @@ mod test {
#[test]
fn ser_struct_unsigned() {
const SZ: usize = BasicU8S::POSTCARD_MAX_SIZE;
let output: Vec<u8, SZ> = to_vec(&BasicU8S {
let input = BasicU8S {
st: 0xABCD,
ei: 0xFE,
ote: 0x1234_4321_ABCD_DCBA_1234_4321_ABCD_DCBA,
sf: 0x1234_4321_ABCD_DCBA,
tt: 0xACAC_ACAC,
})
.unwrap();
};
let output: Vec<u8, SZ> = to_vec(&input).unwrap();

assert_eq!(
&[
Expand All @@ -374,6 +390,7 @@ mod test {
],
output.deref()
);
assert!(output.len() == serialized_size(&input).unwrap());
assert!(output.len() <= BasicU8S::POSTCARD_MAX_SIZE);
}

Expand All @@ -385,6 +402,7 @@ mod test {
&[0x08, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08],
output.deref()
);
assert!(output.len() == serialized_size(&input).unwrap());

let mut input: Vec<u8, 1024> = Vec::new();
for i in 0..1024 {
Expand All @@ -405,6 +423,7 @@ mod test {
let output: Vec<u8, 17> = to_vec(input).unwrap();
assert_eq!(0x10, output.deref()[0]);
assert_eq!(input.as_bytes(), &output.deref()[1..]);
assert!(output.len() == serialized_size(&input).unwrap());

let mut input: String<1024> = String::new();
for _ in 0..256 {
Expand Down Expand Up @@ -468,60 +487,74 @@ mod test {

#[test]
fn enums() {
let output: Vec<u8, 1> = to_vec(&BasicEnum::Bim).unwrap();
let input = BasicEnum::Bim;
let output: Vec<u8, 1> = to_vec(&input).unwrap();
assert_eq!(&[0x01], output.deref());
assert!(output.len() == serialized_size(&input).unwrap());

let output: Vec<u8, { 1 + varint_max::<u64>() }> =
to_vec(&DataEnum::Bim(u64::max_value())).unwrap();
let input = DataEnum::Bim(u64::max_value());
let output: Vec<u8, { 1 + varint_max::<u64>() }> = to_vec(&input).unwrap();
assert_eq!(
&[0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01],
output.deref()
);
assert!(output.len() == serialized_size(&input).unwrap());

let output: Vec<u8, { 1 + varint_max::<u16>() }> =
to_vec(&DataEnum::Bib(u16::max_value())).unwrap();
let input = DataEnum::Bib(u16::max_value());
let output: Vec<u8, { 1 + varint_max::<u16>() }> = to_vec(&input).unwrap();
assert_eq!(&[0x00, 0xFF, 0xFF, 0x03], output.deref());
assert!(output.len() == serialized_size(&input).unwrap());

let output: Vec<u8, 2> = to_vec(&DataEnum::Bap(u8::max_value())).unwrap();
let input = DataEnum::Bap(u8::max_value());
let output: Vec<u8, 2> = to_vec(&input).unwrap();
assert_eq!(&[0x02, 0xFF], output.deref());
assert!(output.len() == serialized_size(&input).unwrap());

let output: Vec<u8, 8> = to_vec(&DataEnum::Kim(EnumStruct {
let input = DataEnum::Kim(EnumStruct {
eight: 0xF0,
sixt: 0xACAC,
}))
.unwrap();
});
let output: Vec<u8, 8> = to_vec(&input).unwrap();
assert_eq!(&[0x03, 0xF0, 0xAC, 0xD9, 0x02], output.deref());
assert!(output.len() == serialized_size(&input).unwrap());

let output: Vec<u8, 8> = to_vec(&DataEnum::Chi {
let input = DataEnum::Chi {
a: 0x0F,
b: 0xC7C7C7C7,
})
.unwrap();
};
let output: Vec<u8, 8> = to_vec(&input).unwrap();
assert_eq!(&[0x04, 0x0F, 0xC7, 0x8F, 0x9F, 0xBE, 0x0C], output.deref());
assert!(output.len() == serialized_size(&input).unwrap());

let output: Vec<u8, 8> = to_vec(&DataEnum::Sho(0x6969, 0x07)).unwrap();
let input = DataEnum::Sho(0x6969, 0x07);
let output: Vec<u8, 8> = to_vec(&input).unwrap();
assert_eq!(&[0x05, 0xE9, 0xD2, 0x01, 0x07], output.deref());
assert!(output.len() == serialized_size(&input).unwrap());
}

#[test]
fn tuples() {
let output: Vec<u8, 128> = to_vec(&(1u8, 10u32, "Hello!")).unwrap();
let input = (1u8, 10u32, "Hello!");
let output: Vec<u8, 128> = to_vec(&input).unwrap();
assert_eq!(
&[1u8, 0x0A, 0x06, b'H', b'e', b'l', b'l', b'o', b'!'],
output.deref()
)
);
assert!(output.len() == serialized_size(&input).unwrap());
}

#[test]
fn bytes() {
let x: &[u8; 32] = &[0u8; 32];
let output: Vec<u8, 128> = to_vec(x).unwrap();
assert_eq!(output.len(), 32);
assert!(output.len() == serialized_size(&x).unwrap());
assert!(<[u8; 32] as MaxSize>::POSTCARD_MAX_SIZE <= output.len());

let x: &[u8] = &[0u8; 32];
let output: Vec<u8, 128> = to_vec(x).unwrap();
assert_eq!(output.len(), 33);
assert!(output.len() == serialized_size(&x).unwrap());
}

#[derive(Serialize)]
Expand All @@ -532,11 +565,15 @@ mod test {

#[test]
fn structs() {
let output: Vec<u8, 1> = to_vec(&NewTypeStruct(5)).unwrap();
let input = NewTypeStruct(5);
let output: Vec<u8, 1> = to_vec(&input).unwrap();
assert_eq!(&[0x05], output.deref());
assert!(output.len() == serialized_size(&input).unwrap());

let output: Vec<u8, 3> = to_vec(&TupleStruct((0xA0, 0x1234))).unwrap();
let input = TupleStruct((0xA0, 0x1234));
let output: Vec<u8, 3> = to_vec(&input).unwrap();
assert_eq!(&[0xA0, 0xB4, 0x24], output.deref());
assert!(output.len() == serialized_size(&input).unwrap());
}

#[derive(Serialize, Deserialize, Eq, PartialEq, Debug)]
Expand All @@ -549,22 +586,24 @@ mod test {
fn ref_struct() {
let message = "hElLo";
let bytes = [0x01, 0x10, 0x02, 0x20];
let output: Vec<u8, 11> = to_vec(&RefStruct {
let input = RefStruct {
bytes: &bytes,
str_s: message,
})
.unwrap();
};
let output: Vec<u8, 11> = to_vec(&input).unwrap();

assert_eq!(
&[0x04, 0x01, 0x10, 0x02, 0x20, 0x05, b'h', b'E', b'l', b'L', b'o',],
output.deref()
);
assert!(output.len() == serialized_size(&input).unwrap());
}

#[test]
fn unit() {
let output: Vec<u8, 1> = to_vec(&()).unwrap();
assert_eq!(output.len(), 0);
assert!(output.len() == serialized_size(&()).unwrap());
}

#[test]
Expand All @@ -573,11 +612,13 @@ mod test {
input.extend_from_slice(&[0x01, 0x02, 0x03, 0x04]).unwrap();
let output: Vec<u8, 5> = to_vec(&input).unwrap();
assert_eq!(&[0x04, 0x01, 0x02, 0x03, 0x04], output.deref());
assert!(output.len() == serialized_size(&input).unwrap());

let mut input: String<8> = String::new();
write!(&mut input, "helLO!").unwrap();
let output: Vec<u8, 7> = to_vec(&input).unwrap();
assert_eq!(&[0x06, b'h', b'e', b'l', b'L', b'O', b'!'], output.deref());
assert!(output.len() == serialized_size(&input).unwrap());

let mut input: FnvIndexMap<u8, u8, 4> = FnvIndexMap::new();
input.insert(0x01, 0x05).unwrap();
Expand All @@ -589,6 +630,7 @@ mod test {
&[0x04, 0x01, 0x05, 0x02, 0x06, 0x03, 0x07, 0x04, 0x08],
output.deref()
);
assert!(output.len() == serialized_size(&input).unwrap());
}

#[test]
Expand Down

0 comments on commit 08f18dc

Please sign in to comment.