Skip to main content

airbender_codec/
lib.rs

1#![doc = include_str!("../README.md")]
2#![no_std]
3
4extern crate alloc;
5
6use alloc::vec::Vec;
7use core::fmt;
8
9#[cfg(test)]
10extern crate std;
11
12/// Stable codec version for host/guest communication.
13pub const AIRBENDER_CODEC_V0: u32 = 0;
14
15/// A stable, versioned serializer used by Airbender host and guest programs.
16pub trait AirbenderCodec {
17    /// Version identifier baked into manifests and tooling.
18    const VERSION: u32;
19
20    /// Serialize a value into a byte payload.
21    fn encode<T: serde::Serialize>(value: &T) -> Result<Vec<u8>, CodecError>;
22
23    /// Deserialize a value from a byte payload.
24    fn decode<T: serde::de::DeserializeOwned>(bytes: &[u8]) -> Result<T, CodecError>;
25}
26
27/// Initial codec based on `bincode` v2 with a fixed configuration.
28pub struct AirbenderCodecV0;
29
30impl AirbenderCodec for AirbenderCodecV0 {
31    const VERSION: u32 = AIRBENDER_CODEC_V0;
32
33    fn encode<T: serde::Serialize>(value: &T) -> Result<Vec<u8>, CodecError> {
34        bincode::serde::encode_to_vec(value, bincode::config::standard())
35            .map_err(CodecError::Encode)
36    }
37
38    fn decode<T: serde::de::DeserializeOwned>(bytes: &[u8]) -> Result<T, CodecError> {
39        let (decoded, read_len) =
40            bincode::serde::decode_from_slice(bytes, bincode::config::standard())
41                .map_err(CodecError::Decode)?;
42        if read_len != bytes.len() {
43            return Err(CodecError::TrailingBytes {
44                expected: bytes.len(),
45                read: read_len,
46            });
47        }
48        Ok(decoded)
49    }
50}
51
52#[derive(Debug)]
53pub enum CodecError {
54    Encode(bincode::error::EncodeError),
55    Decode(bincode::error::DecodeError),
56    TrailingBytes { expected: usize, read: usize },
57}
58
59impl fmt::Display for CodecError {
60    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
61        match self {
62            CodecError::Encode(_) => f.write_str("failed to encode value"),
63            CodecError::Decode(_) => f.write_str("failed to decode value"),
64            CodecError::TrailingBytes { expected, read } => {
65                write!(f, "decoded {read} bytes but expected {expected}")
66            }
67        }
68    }
69}
70
71#[cfg(test)]
72mod tests {
73    use super::*;
74    use alloc::vec;
75
76    #[derive(Debug, serde::Serialize, serde::Deserialize, PartialEq, Eq)]
77    struct Sample {
78        value: u32,
79        payload: alloc::vec::Vec<u8>,
80    }
81
82    #[test]
83    fn codec_roundtrip() {
84        let sample = Sample {
85            value: 42,
86            payload: vec![1u8, 2, 3, 4, 5],
87        };
88        let encoded = AirbenderCodecV0::encode(&sample).expect("encode");
89        let decoded: Sample = AirbenderCodecV0::decode(&encoded).expect("decode");
90        assert_eq!(decoded, sample);
91    }
92}