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}
142
143impl core::fmt::Display for BeBytesError {
144    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
145        match self {
146            Self::EmptyBuffer => write!(f, "No bytes provided"),
147            Self::InsufficientData { expected, actual } => {
148                write!(f, "Not enough bytes: expected {expected}, got {actual}")
149            }
150            Self::InvalidDiscriminant { value, type_name } => {
151                write!(f, "Invalid discriminant {value} for type {type_name}")
152            }
153            Self::InvalidDiscriminantLarge { value, type_name } => {
154                write!(f, "Invalid discriminant {value} for type {type_name}")
155            }
156            Self::InvalidBitField { value, max, field } => {
157                write!(f, "Value {value} exceeds maximum {max} for field {field}")
158            }
159            Self::InvalidUtf8 { field } => {
160                write!(f, "Invalid UTF-8 sequence in field '{field}'")
161            }
162            Self::MarkerNotFound { marker, field } => {
163                write!(f, "Marker byte 0x{marker:02X} not found in field '{field}'")
164            }
165        }
166    }
167}
168
169#[cfg(feature = "std")]
170impl std::error::Error for BeBytesError {}
171
172// Note: core::error::Error is stable since Rust 1.81
173// We could add this when we update MSRV:
174// #[cfg(not(feature = "std"))]
175// impl core::error::Error for BeBytesError {}
176
177pub trait BeBytes {
178    fn field_size() -> usize;
179
180    // Big-endian methods
181    #[cfg(feature = "std")]
182    fn to_be_bytes(&self) -> std::vec::Vec<u8>;
183
184    #[cfg(not(feature = "std"))]
185    fn to_be_bytes(&self) -> alloc::vec::Vec<u8>;
186
187    /// Try to parse a struct from big-endian bytes
188    ///
189    /// # Errors
190    ///
191    /// Returns `BeBytesError::EmptyBuffer` if the input slice is empty
192    /// Returns `BeBytesError::InsufficientData` if there aren't enough bytes to parse all fields
193    /// Returns `BeBytesError::InvalidDiscriminant` if an enum field has an invalid value
194    /// Returns `BeBytesError::InvalidBitField` if a bit field value exceeds its maximum
195    fn try_from_be_bytes(bytes: &'_ [u8]) -> core::result::Result<(Self, usize), BeBytesError>
196    where
197        Self: Sized;
198
199    // Little-endian methods
200    #[cfg(feature = "std")]
201    fn to_le_bytes(&self) -> std::vec::Vec<u8>;
202
203    #[cfg(not(feature = "std"))]
204    fn to_le_bytes(&self) -> alloc::vec::Vec<u8>;
205
206    /// Convert to big-endian bytes as a Bytes buffer
207    ///
208    /// Returns a Bytes buffer (now a simple Vec wrapper without reference counting)
209    fn to_be_bytes_buf(&self) -> Bytes {
210        // Default implementation for backward compatibility
211        Bytes::from(self.to_be_bytes())
212    }
213
214    /// Convert to little-endian bytes as a Bytes buffer
215    ///
216    /// Returns a Bytes buffer (now a simple Vec wrapper without reference counting)
217    fn to_le_bytes_buf(&self) -> Bytes {
218        // Default implementation for backward compatibility
219        Bytes::from(self.to_le_bytes())
220    }
221
222    /// Try to parse a struct from little-endian bytes
223    ///
224    /// # Errors
225    ///
226    /// Returns `BeBytesError::EmptyBuffer` if the input slice is empty
227    /// Returns `BeBytesError::InsufficientData` if there aren't enough bytes to parse all fields
228    /// Returns `BeBytesError::InvalidDiscriminant` if an enum field has an invalid value
229    /// Returns `BeBytesError::InvalidBitField` if a bit field value exceeds its maximum
230    fn try_from_le_bytes(bytes: &'_ [u8]) -> core::result::Result<(Self, usize), BeBytesError>
231    where
232        Self: Sized;
233
234    /// Encode directly to a buffer in big-endian format
235    ///
236    /// Writes struct data directly to the provided buffer without intermediate
237    /// allocations.
238    ///
239    /// # Errors
240    ///
241    /// Returns an error if the buffer doesn't have enough capacity
242    fn encode_be_to<B: BufMut>(&self, buf: &mut B) -> core::result::Result<(), BeBytesError> {
243        // Default implementation using to_be_bytes for backward compatibility
244        let bytes = self.to_be_bytes();
245        buf.put_slice(&bytes);
246        Ok(())
247    }
248
249    /// Encode directly to a buffer in little-endian format
250    ///
251    /// Writes struct data directly to the provided buffer without intermediate
252    /// allocations.
253    ///
254    /// # Errors
255    ///
256    /// Returns an error if the buffer doesn't have enough capacity
257    fn encode_le_to<B: BufMut>(&self, buf: &mut B) -> core::result::Result<(), BeBytesError> {
258        // Default implementation using to_le_bytes for backward compatibility
259        let bytes = self.to_le_bytes();
260        buf.put_slice(&bytes);
261        Ok(())
262    }
263}