arcium-primitives 0.8.5

Arcium primitives
Documentation
//! Thin wrappers around `bincode`'s free (de)serialization functions that avoid
//! [`bincode::serialize`]'s built-in serialized-size pre-pass.
//!
//! `bincode::serialize` (and `Options::serialize`, which it's built on) always calls
//! `serialized_size` before encoding for real into a correctly pre-sized `Vec` (see `bincode`'s
//! internal `serialize` function). `serialized_size` is a full `Serialize::serialize` pass against
//! a `SizeChecker`. For most `#[derive(Serialize)]` types that's cheap: both passes just walk
//! fields, and the size pass does no real work. But `Serialize` impls that build their output
//! eagerly regardless of which serializer is asking end up doing that work twice: once to be
//! measured and thrown away by the `SizeChecker` pass, once for the real encode. Most notably,
//! `HeapArray`'s impl (the [`InPlaceCodec`] fast path) calls `self.to_inplace_bytes()`
//! unconditionally.
//!
//! [`serialize`] encodes exactly once, writing directly into a growable `Vec` via
//! `bincode::serialize_into` (the same fix already used by `network`'s `IoSink::send`).
//! [`deserialize`] is a plain passthrough to `bincode::deserialize`: bincode's decode path has no
//! equivalent double pass. It is kept alongside [`serialize`] so callers use one consistent
//! (de)serialization entry point instead of mixing direct `bincode::*` calls with this module.
//!
//! [`InPlaceCodec`]: super::InPlaceCodec

use serde::{de::DeserializeOwned, Serialize};

/// Serialize `value` with `bincode`, without `bincode::serialize`'s serialized-size pre-pass.
pub fn serialize<T: ?Sized + Serialize>(value: &T) -> bincode::Result<Vec<u8>> {
    let mut bytes = Vec::new();
    bincode::serialize_into(&mut bytes, value)?;
    Ok(bytes)
}

/// Deserialize a `T` with `bincode`. A thin passthrough to `bincode::deserialize`, kept alongside
/// [`serialize`] so callers use one consistent (de)serialization entry point.
pub fn deserialize<T: DeserializeOwned>(bytes: &[u8]) -> bincode::Result<T> {
    bincode::deserialize(bytes)
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn roundtrips_and_matches_bincode_serialize() {
        let value = (1u64, "hello".to_string(), vec![1u8, 2, 3]);

        let bytes = serialize(&value).unwrap();
        // Same bytes `bincode::serialize` would produce. This is purely a faster encode path,
        // not a different wire format.
        assert_eq!(bytes, bincode::serialize(&value).unwrap());

        let restored: (u64, String, Vec<u8>) = deserialize(&bytes).unwrap();
        assert_eq!(restored, value);
    }
}