bebytes/
lib.rs

1//! # `BeBytes` - Binary Serialization
2//!
3//! `BeBytes` is a Rust library that provides procedural macros for generating
4//! serialization and deserialization methods for network structs. It supports both
5//! big-endian and little-endian byte orders and includes features like bit fields,
6//! marker-delimited fields, and WebAssembly compatibility.
7//!
8//! ## Key Features
9//!
10//! - **Bit Fields**: Pack multiple fields into bytes with `#[bits(N)]` attribute
11//! - **Marker Attributes**: Handle variable-length sections with `#[UntilMarker]` and `#[AfterMarker]`
12//! - **Size Control**: Dynamic field sizing with `#[FromField]` and `#[With(size())]`
13//! - **WebAssembly Support**: Full `no_std` compatibility for WASM targets
14//! - **Type Support**: Primitives, strings, arrays, vectors, enums, and nested structs
15//!
16//! ## Quick Start
17//!
18//! ```rust
19//! use bebytes::BeBytes;
20//!
21//! #[derive(BeBytes, Debug, PartialEq)]
22//! struct NetworkMessage {
23//!     #[bits(4)]
24//!     version: u8,
25//!     #[bits(4)]
26//!     msg_type: u8,
27//!     sender_id: u32,
28//!     content_len: u8,
29//!     #[FromField(content_len)]
30//!     content: Vec<u8>,
31//! }
32//!
33//! let message = NetworkMessage {
34//!     version: 1,
35//!     msg_type: 2,
36//!     sender_id: 0x12345678,
37//!     content_len: 5,
38//!     content: b"hello".to_vec(),
39//! };
40//!
41//! // Serialize
42//! let bytes = message.to_be_bytes();
43//!
44//! // Deserialize
45//! let (parsed, bytes_consumed) = NetworkMessage::try_from_be_bytes(&bytes).unwrap();
46//! assert_eq!(parsed, message);
47//! ```
48//!
49//! ## Marker-Delimited Fields
50//!
51//! Handle protocols with variable-length sections separated by marker bytes:
52//!
53//! ```rust
54//! use bebytes::BeBytes;
55//!
56//! #[derive(BeBytes, Debug, PartialEq)]
57//! struct Protocol {
58//!     header: u32,
59//!     #[UntilMarker(0xFF)]
60//!     data: Vec<u8>,  // Reads until 0xFF marker
61//!     footer: u16,
62//! }
63//!
64//! // For multiple delimited sections:
65//! #[derive(BeBytes, Debug, PartialEq)]
66//! struct MultiSection {
67//!     section_count: u8,
68//!     #[FromField(section_count)]
69//!     #[UntilMarker(0xFF)]
70//!     sections: Vec<Vec<u8>>,  // Multiple sections, each ending with 0xFF
71//! }
72//! ```
73//!
74//! ## Error Handling
75//!
76//! All parsing operations return `Result<(T, usize), BeBytesError>` where:
77//! - `T` is the parsed struct
78//! - `usize` is the number of bytes consumed
79//! - `BeBytesError` provides detailed error information
80
81#![cfg_attr(not(feature = "std"), no_std)]
82
83#[cfg(feature = "std")]
84extern crate std;
85
86#[cfg(not(feature = "std"))]
87extern crate alloc;
88#[cfg(not(feature = "std"))]
89pub use alloc::borrow::ToOwned;
90
91// Re-export Vec for use in generated code
92#[cfg(not(feature = "std"))]
93pub use alloc::vec::Vec;
94#[cfg(feature = "std")]
95pub use std::vec::Vec;
96
97#[cfg(not(feature = "bytes_backend"))]
98pub mod buffer;
99pub mod interpreter;
100
101pub use bebytes_derive::BeBytes;
102#[doc(hidden)]
103pub use interpreter::{StringInterpreter, Utf8};
104
105#[cfg(feature = "bytes_backend")]
106pub use bytes::buf::BufMut;
107#[cfg(feature = "bytes_backend")]
108pub use bytes::{Bytes, BytesMut};
109
110#[cfg(not(feature = "bytes_backend"))]
111pub use buffer::{BufMut, Bytes, BytesMut};
112
113/// Error type for `BeBytes` operations
114#[derive(Debug, Clone, PartialEq, Eq)]
115pub enum BeBytesError {
116    EmptyBuffer,
117    InsufficientData {
118        expected: usize,
119        actual: usize,
120    },
121    InvalidDiscriminant {
122        value: u8,
123        type_name: &'static str,
124    },
125    InvalidDiscriminantLarge {
126        value: u128,
127        type_name: &'static str,
128    },
129    InvalidBitField {
130        value: u128,
131        max: u128,
132        field: &'static str,
133    },
134    InvalidUtf8 {
135        field: &'static str,
136    },
137    MarkerNotFound {
138        marker: u8,
139        field: &'static str,
140    },
141    InvalidChar {
142        value: u32,
143    },
144}
145
146impl core::fmt::Display for BeBytesError {
147    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
148        match self {
149            Self::EmptyBuffer => write!(f, "No bytes provided"),
150            Self::InsufficientData { expected, actual } => {
151                write!(f, "Not enough bytes: expected {expected}, got {actual}")
152            }
153            Self::InvalidDiscriminant { value, type_name } => {
154                write!(f, "Invalid discriminant {value} for type {type_name}")
155            }
156            Self::InvalidDiscriminantLarge { value, type_name } => {
157                write!(f, "Invalid discriminant {value} for type {type_name}")
158            }
159            Self::InvalidBitField { value, max, field } => {
160                write!(f, "Value {value} exceeds maximum {max} for field {field}")
161            }
162            Self::InvalidUtf8 { field } => {
163                write!(f, "Invalid UTF-8 sequence in field '{field}'")
164            }
165            Self::MarkerNotFound { marker, field } => {
166                write!(f, "Marker byte 0x{marker:02X} not found in field '{field}'")
167            }
168            Self::InvalidChar { value } => {
169                write!(f, "Invalid Unicode code point: 0x{value:08X}")
170            }
171        }
172    }
173}
174
175#[cfg(feature = "std")]
176impl std::error::Error for BeBytesError {}
177
178// Note: core::error::Error is stable since Rust 1.81
179// We could add this when we update MSRV:
180// #[cfg(not(feature = "std"))]
181// impl core::error::Error for BeBytesError {}
182
183pub trait BeBytes {
184    fn field_size() -> usize;
185
186    // Big-endian methods
187    #[cfg(feature = "std")]
188    fn to_be_bytes(&self) -> std::vec::Vec<u8>;
189
190    #[cfg(not(feature = "std"))]
191    fn to_be_bytes(&self) -> alloc::vec::Vec<u8>;
192
193    /// Try to parse a struct from big-endian bytes
194    ///
195    /// # Errors
196    ///
197    /// Returns `BeBytesError::EmptyBuffer` if the input slice is empty
198    /// Returns `BeBytesError::InsufficientData` if there aren't enough bytes to parse all fields
199    /// Returns `BeBytesError::InvalidDiscriminant` if an enum field has an invalid value
200    /// Returns `BeBytesError::InvalidBitField` if a bit field value exceeds its maximum
201    fn try_from_be_bytes(bytes: &'_ [u8]) -> core::result::Result<(Self, usize), BeBytesError>
202    where
203        Self: Sized;
204
205    // Little-endian methods
206    #[cfg(feature = "std")]
207    fn to_le_bytes(&self) -> std::vec::Vec<u8>;
208
209    #[cfg(not(feature = "std"))]
210    fn to_le_bytes(&self) -> alloc::vec::Vec<u8>;
211
212    /// Convert to big-endian bytes as a Bytes buffer
213    ///
214    /// Returns a Bytes buffer (now a simple Vec wrapper without reference counting)
215    fn to_be_bytes_buf(&self) -> Bytes {
216        // Default implementation for backward compatibility
217        Bytes::from(self.to_be_bytes())
218    }
219
220    /// Convert to little-endian bytes as a Bytes buffer
221    ///
222    /// Returns a Bytes buffer (now a simple Vec wrapper without reference counting)
223    fn to_le_bytes_buf(&self) -> Bytes {
224        // Default implementation for backward compatibility
225        Bytes::from(self.to_le_bytes())
226    }
227
228    /// Try to parse a struct from little-endian bytes
229    ///
230    /// # Errors
231    ///
232    /// Returns `BeBytesError::EmptyBuffer` if the input slice is empty
233    /// Returns `BeBytesError::InsufficientData` if there aren't enough bytes to parse all fields
234    /// Returns `BeBytesError::InvalidDiscriminant` if an enum field has an invalid value
235    /// Returns `BeBytesError::InvalidBitField` if a bit field value exceeds its maximum
236    fn try_from_le_bytes(bytes: &'_ [u8]) -> core::result::Result<(Self, usize), BeBytesError>
237    where
238        Self: Sized;
239
240    /// Encode directly to a buffer in big-endian format
241    ///
242    /// Writes struct data directly to the provided buffer without intermediate
243    /// allocations.
244    ///
245    /// # Errors
246    ///
247    /// Returns an error if the buffer doesn't have enough capacity
248    fn encode_be_to<B: BufMut>(&self, buf: &mut B) -> core::result::Result<(), BeBytesError> {
249        // Default implementation using to_be_bytes for backward compatibility
250        let bytes = self.to_be_bytes();
251        buf.put_slice(&bytes);
252        Ok(())
253    }
254
255    /// Encode directly to a buffer in little-endian format
256    ///
257    /// Writes struct data directly to the provided buffer without intermediate
258    /// allocations.
259    ///
260    /// # Errors
261    ///
262    /// Returns an error if the buffer doesn't have enough capacity
263    fn encode_le_to<B: BufMut>(&self, buf: &mut B) -> core::result::Result<(), BeBytesError> {
264        // Default implementation using to_le_bytes for backward compatibility
265        let bytes = self.to_le_bytes();
266        buf.put_slice(&bytes);
267        Ok(())
268    }
269}