bincake_core/serialize.rs
1//! Provides the `Serializable` trait for handling serialization to/from a consistent, opinionated binary format.
2//!
3//! The binary format specified by this crate is used to emit RVM bytecode,
4//! and exhibits the following properties to ensure cross-platform compatibility:
5//!
6//! - Byte-aligned data
7//! - Little-endian encoding for numeric types
8//! - Strings encoded in UTF-8 with a `u32` length prefix (see `StrLen`)
9//! - Arrays encoded with a length prefix of varying size (e.g., `u8`, `u16`, `u32`), followed by the serialized elements
10//!
11//! This module also provides trivial implementations of `Serializable` for `bool` and `String`.
12
13use taped::Tape;
14
15use crate::{DecodeError, EncodeError, Read, Vec32, Write};
16
17/// Writes all provided data to the destination, taking each argument as a reference.
18///
19/// This macro is useful for streaming known sequences of values to a destination.
20#[macro_export]
21macro_rules! stream {
22 ($($data:expr),* $(,)? => $dest:expr) => {
23 {
24 let mut result;
25 'cases: {
26 $(
27 result = $dest.write(&$data);
28 if result.is_err() {
29 break 'cases;
30 }
31 )*
32 }
33 result
34 }
35 };
36}
37
38/// A value that can be serialized to and deserialized from a byte buffer.
39pub trait Serialize: Sized {
40 /// Encodes this value by appending it to the buffer.
41 fn encode(&self, dest: &mut Vec<u8>) -> Result<(), EncodeError>;
42
43 /// Decodes the value by consuming the next one in the buffer.
44 ///
45 /// Returns `bincake::DeserializeError` if the encoded data is malformed.
46 fn decode(src: &mut Tape<'_, u8>) -> Result<Self, DecodeError>;
47}
48
49impl Serialize for bool {
50 fn encode(&self, dest: &mut Vec<u8>) -> Result<(), EncodeError> {
51 dest.push(if *self { 1u8 } else { 0u8 });
52 Ok(())
53 }
54
55 fn decode(src: &mut Tape<'_, u8>) -> Result<Self, DecodeError> {
56 src.next()
57 .ok_or(DecodeError::Exhausted { pos: src.pos })
58 .map(|b| b == 1)
59 }
60}
61
62impl Serialize for String {
63 fn encode(&self, dest: &mut Vec<u8>) -> Result<(), EncodeError> {
64 let len = self.len();
65 #[cfg(target_pointer_width = "64")]
66 if len > u32::MAX as usize {
67 return Err(EncodeError::LengthExceedsPrefix {
68 prefix_size: 32,
69 len,
70 });
71 }
72 dest.write(&len)?;
73 dest.extend_from_slice(self.as_bytes());
74 Ok(())
75 }
76
77 fn decode(src: &mut Tape<'_, u8>) -> Result<Self, DecodeError> {
78 let start = src.pos;
79 let data = src.read::<Vec32<u8>>()?.into_inner();
80 String::from_utf8(data).map_err(|e| DecodeError::Other {
81 pos: start,
82 cause: format!("Invalid UTF-8 ({e})"),
83 })
84 }
85}