Skip to main content

primitives/utils/codec/
bincode_io.rs

1//! Thin wrappers around `bincode`'s free (de)serialization functions that avoid
2//! [`bincode::serialize`]'s built-in serialized-size pre-pass.
3//!
4//! `bincode::serialize` (and `Options::serialize`, which it's built on) always calls
5//! `serialized_size` — a full `Serialize::serialize` pass against a `SizeChecker` — before encoding
6//! for real into a correctly pre-sized `Vec` (see `bincode`'s internal `serialize` function). For
7//! most `#[derive(Serialize)]` types that's cheap: both passes just walk fields, and the size pass
8//! does no real work. But `Serialize` impls that build their output eagerly regardless of which
9//! serializer is asking — most notably `HeapArray`'s (the [`InPlaceCodec`] fast path), whose
10//! `serialize` calls `self.to_inplace_bytes()` unconditionally — end up doing that work twice: once
11//! to be measured and thrown away by the `SizeChecker` pass, once for the real encode.
12//!
13//! [`serialize`] encodes exactly once, writing directly into a growable `Vec` via
14//! `bincode::serialize_into` (the same fix already used by `network`'s `IoSink::send`).
15//! [`deserialize`] is a plain passthrough to `bincode::deserialize` — bincode's decode path has no
16//! equivalent double pass — kept alongside [`serialize`] so callers use one consistent
17//! (de)serialization entry point instead of mixing direct `bincode::*` calls with this module.
18//!
19//! [`InPlaceCodec`]: super::InPlaceCodec
20
21use serde::{de::DeserializeOwned, Serialize};
22
23/// Serialize `value` with `bincode`, without `bincode::serialize`'s serialized-size pre-pass.
24pub fn serialize<T: ?Sized + Serialize>(value: &T) -> bincode::Result<Vec<u8>> {
25    let mut bytes = Vec::new();
26    bincode::serialize_into(&mut bytes, value)?;
27    Ok(bytes)
28}
29
30/// Deserialize a `T` with `bincode`. A thin passthrough to `bincode::deserialize`, kept alongside
31/// [`serialize`] so callers use one consistent (de)serialization entry point.
32pub fn deserialize<T: DeserializeOwned>(bytes: &[u8]) -> bincode::Result<T> {
33    bincode::deserialize(bytes)
34}
35
36#[cfg(test)]
37mod tests {
38    use super::*;
39
40    #[test]
41    fn roundtrips_and_matches_bincode_serialize() {
42        let value = (1u64, "hello".to_string(), vec![1u8, 2, 3]);
43
44        let bytes = serialize(&value).unwrap();
45        // Same bytes `bincode::serialize` would produce — this is purely a faster encode path,
46        // not a different wire format.
47        assert_eq!(bytes, bincode::serialize(&value).unwrap());
48
49        let restored: (u64, String, Vec<u8>) = deserialize(&bytes).unwrap();
50        assert_eq!(restored, value);
51    }
52}