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}