serde_brief/
lib.rs

1//! # Serde-Brief
2//!
3//! Serde-Brief (German for letter) is a crate for encoding and decoding data into a binary format that is self-descriptive and [serde](https://docs.rs/serde/)-compatible.
4//!
5//! ## Design Goals
6//!
7//! Not necessarily in order of importance:
8//!
9//! - Convenient to use for developers: Integrates into the Rust ecosystem via `serde`, supporting
10//!   all of its features in its derived implementations (e.g. renaming, flattening, ..).
11//! - Compatibility: Easy to add or re-order fields/variants without breakage. Detects wrong data
12//!   types.
13//! - `#![no_std]` and std compatible.
14//! - Resource efficient: High performance, low memory usage.
15//! - Interoperability: Different architectures can communicate flawlessly.
16//!
17//! ## More Detailed Documentation
18//!
19//! See more detailed documentation in [the docs module](./docs/index.html). It also contains
20//! information on the binary representation format.
21//!
22//! ## Feature Flags
23//!
24//! This library is both no-std and std compatible. Additionally, there are some other features to
25//! enable additional functionality:
26//!
27//! | Feature Flag | Default | Description |
28//! | --- | --- | --- |
29//! | alloc | no | Enables the use of `alloc` types like serialization to a `Vec`. |
30//! | heapless | no | Enables serialization to a `heapless::Vec`. |
31//! | std | no | Enables the use of `std` types like serialization to a `Write`r and deserialization from a `Read`er. |
32//! | tracing | no | Enables tracing instrumentation. |
33//!
34//! ## Flavors / Modes
35//!
36//! By default, structs' field names and enums' variant names are encoded as strings. This can be
37//! configured to be encoded as unsigned integers of their indices instead. However, this has
38//! compatibility implications and some serde features do not work with the index representation.
39//! See the format specification for more info.
40//!
41//! ## Usage
42//!
43//! Add the library to your project with `cargo add serde-brief`. By default, no features are
44//! enabled (currently), so it is no-std by default. You can enable use of `Vec`s and such with
45//! features like `alloc` or `std`.
46//!
47//! ### Example Serialization/Deserialization
48//!
49//! The `heapless` feature was enabled for this example. It is similarly possible with `std`'s `Vec`
50//! or just slices.
51//!
52//! ```rust
53//! use heapless::Vec;
54//! use serde::{Deserialize, Serialize};
55//!
56//! #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
57//! struct MyBorrowedData<'a> {
58//! 	name: &'a str,
59//! 	age: u8,
60//! }
61//!
62//! let data = MyBorrowedData { name: "Holla", age: 21 };
63//! let mut output: Vec<u8, 22> = serde_brief::to_heapless_vec(&data).unwrap();
64//!
65//! assert_eq!(
66//! 	output,
67//! 	[
68//! 		17, 11, 4, b'n', b'a', b'm', b'e', 11, 5, b'H', b'o', b'l', b'l', b'a', 11, 3, b'a',
69//! 		b'g', b'e', 3, 21, 18
70//! 	]
71//! );
72//!
73//! let parsed: MyBorrowedData = serde_brief::from_slice(&output).unwrap();
74//! assert_eq!(parsed, data);
75//! ```
76//!
77//! ### Bytes Serialization/Deserialization
78//!
79//! Serde serializes byte arrays, such as `[u8; N]` or `Vec<u8>`, as sequences by default (due to
80//! missing specialization support in Rust). To serialize these types as proper bytes, making the
81//! format way more efficient, you can use `serde_bytes` or your own serde-trait-implementations.
82//!
83//! Example using `serde_bytes`:
84//!
85//! ```rust
86//! use serde::{Deserialize, Serialize};
87//! use serde_bytes::{ByteBuf, Bytes};
88//!
89//! #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
90//! struct MyData<'a> {
91//! 	owned_bytes: ByteBuf,
92//! 	#[serde(borrow)]
93//! 	borrowed_bytes: &'a Bytes,
94//! 	#[serde(with = "serde_bytes")]
95//! 	byte_vec: Vec<u8>,
96//! }
97//! ```
98//!
99//! ## Performance
100//!
101//! If you are interested in maximum performance, please take a look at the [PGO usage
102//! documentation](./docs/pgo/index.html).
103#![cfg_attr(not(feature = "std"), no_std)]
104
105#[cfg(feature = "alloc")]
106extern crate alloc;
107
108mod buffer;
109mod config;
110pub mod de;
111pub mod docs;
112mod error;
113mod format;
114mod io;
115pub mod ser;
116#[cfg(feature = "alloc")]
117pub mod value;
118
119#[allow(unused_imports, reason = "Different feature sets")]
120use ::serde::{Deserialize, Serialize, de::DeserializeOwned};
121#[cfg(feature = "std")]
122use ::std::io::{Read, Write};
123
124#[cfg(feature = "alloc")]
125pub use self::value::{from_value, from_value_with_config, to_value, to_value_with_config};
126pub use self::{config::Config, de::Deserializer, error::Error, ser::Serializer};
127
128/// `Result` type that uses the `serde-brief` error.
129pub type Result<T, E = Error> = ::core::result::Result<T, E>;
130
131/// Serialize a type into a slice of bytes using the given configuration. Returns the slice with the
132/// serialized data.
133#[cfg_attr(feature = "tracing", ::tracing::instrument(skip_all, fields(config)))]
134pub fn to_slice_with_config<'buf, T>(
135	value: &T,
136	buffer: &'buf mut [u8],
137	config: Config,
138) -> Result<&'buf mut [u8]>
139where
140	T: Serialize,
141{
142	let remaining = if let Some(max) = config.max_size {
143		let mut ser = Serializer::new(io::SizeLimit::new(&mut *buffer, max.into()))
144			.use_indices(config.use_indices);
145		value.serialize(&mut ser)?;
146		ser.into_output().into_inner().len()
147	} else {
148		let mut ser = Serializer::new(&mut *buffer).use_indices(config.use_indices);
149		value.serialize(&mut ser)?;
150		ser.into_output().len()
151	};
152
153	let used = buffer.len() - remaining;
154	Ok(buffer.split_at_mut(used).0)
155}
156
157/// Serialize a type into a slice of bytes. Returns the slice with the serialized data.
158pub fn to_slice<'buf, T>(value: &T, buffer: &'buf mut [u8]) -> Result<&'buf mut [u8]>
159where
160	T: Serialize,
161{
162	to_slice_with_config(value, buffer, Config::default())
163}
164
165/// Serialize a type into a [Vec] of bytes using the given configuration.
166#[cfg(feature = "alloc")]
167#[cfg_attr(feature = "tracing", ::tracing::instrument(skip_all, fields(config)))]
168pub fn to_vec_with_config<T>(value: &T, config: Config) -> Result<::alloc::vec::Vec<u8>>
169where
170	T: Serialize,
171{
172	if let Some(max) = config.max_size {
173		let mut ser = Serializer::new(io::SizeLimit::new(::alloc::vec::Vec::new(), max.into()))
174			.use_indices(config.use_indices);
175		value.serialize(&mut ser)?;
176		Ok(ser.into_output().into_inner())
177	} else {
178		let mut ser = Serializer::new(::alloc::vec::Vec::new()).use_indices(config.use_indices);
179		value.serialize(&mut ser)?;
180		Ok(ser.into_output())
181	}
182}
183
184/// Serialize a type into a [Vec] of bytes.
185#[cfg(feature = "alloc")]
186pub fn to_vec<T>(value: &T) -> Result<::alloc::vec::Vec<u8>>
187where
188	T: Serialize,
189{
190	to_vec_with_config(value, Config::default())
191}
192
193/// Serialize a type into a [`heapless::Vec`] of bytes using the given configuration.
194#[cfg(feature = "heapless")]
195#[cfg_attr(feature = "tracing", ::tracing::instrument(skip_all, fields(config)))]
196pub fn to_heapless_vec_with_config<const N: usize, T>(
197	value: &T,
198	config: Config,
199) -> Result<::heapless::Vec<u8, N>>
200where
201	T: Serialize,
202{
203	if let Some(max) = config.max_size {
204		let mut ser = Serializer::new(io::SizeLimit::new(::heapless::Vec::new(), max.into()))
205			.use_indices(config.use_indices);
206		value.serialize(&mut ser)?;
207		Ok(ser.into_output().into_inner())
208	} else {
209		let mut ser = Serializer::new(::heapless::Vec::new()).use_indices(config.use_indices);
210		value.serialize(&mut ser)?;
211		Ok(ser.into_output())
212	}
213}
214
215/// Serialize a type into a [`heapless::Vec`] of bytes.
216#[cfg(feature = "heapless")]
217pub fn to_heapless_vec<const N: usize, T>(value: &T) -> Result<::heapless::Vec<u8, N>>
218where
219	T: Serialize,
220{
221	to_heapless_vec_with_config(value, Config::default())
222}
223
224/// Serialize a type into a [Write]r using the given configuration.
225#[cfg(feature = "std")]
226#[cfg_attr(feature = "tracing", ::tracing::instrument(skip_all, fields(config)))]
227pub fn to_writer_with_config<T, W>(value: &T, writer: W, config: Config) -> Result<()>
228where
229	T: Serialize,
230	W: Write,
231{
232	if let Some(max) = config.max_size {
233		let mut ser = Serializer::new(io::SizeLimit::new(io::IoWriter::new(writer), max.into()))
234			.use_indices(config.use_indices);
235		value.serialize(&mut ser)?;
236	} else {
237		let mut ser = Serializer::new(io::IoWriter::new(writer)).use_indices(config.use_indices);
238		value.serialize(&mut ser)?;
239	}
240	Ok(())
241}
242
243/// Serialize a type into a [Write]r.
244#[cfg(feature = "std")]
245pub fn to_writer<T, W>(value: &T, writer: W) -> Result<()>
246where
247	T: Serialize,
248	W: Write,
249{
250	to_writer_with_config(value, writer, Config::default())
251}
252
253/// Deserialize a type from a slice of bytes using the given configuration.
254#[cfg_attr(feature = "tracing", ::tracing::instrument(skip_all, fields(config)))]
255pub fn from_slice_with_config<'de, T>(bytes: &'de [u8], config: Config) -> Result<T>
256where
257	T: Deserialize<'de>,
258{
259	let error_on_excess = config.error_on_excess_data;
260
261	let (value, peek) = if let Some(max) = config.max_size {
262		// The deserializer can parse both with and without `use_indices`.`
263		let mut de = Deserializer::new(io::SizeLimit::new(bytes, max.into()));
264		(T::deserialize(&mut de)?, io::Input::peek_byte(&mut de.into_input()))
265	} else {
266		// The deserializer can parse both with and without `use_indices`.`
267		let mut de = Deserializer::new(bytes);
268		(T::deserialize(&mut de)?, io::Input::peek_byte(&mut de.into_input()))
269	};
270
271	if error_on_excess && peek.is_ok() {
272		return Err(Error::ExcessData);
273	}
274
275	Ok(value)
276}
277
278/// Deserialize a type from a slice of bytes.
279pub fn from_slice<'de, T>(bytes: &'de [u8]) -> Result<T>
280where
281	T: Deserialize<'de>,
282{
283	from_slice_with_config(bytes, Config::default())
284}
285
286/// Deserialize a type from a [Read]er using the given configuration.
287#[cfg(feature = "std")]
288#[cfg_attr(feature = "tracing", ::tracing::instrument(skip_all, fields(config)))]
289pub fn from_reader_with_config<R, T>(reader: R, config: Config) -> Result<T>
290where
291	R: Read,
292	T: DeserializeOwned,
293{
294	let error_on_excess = config.error_on_excess_data;
295
296	let (value, peek) = if let Some(max) = config.max_size {
297		// The deserializer can parse both with and without `use_indices`.`
298		let mut de = Deserializer::new(io::SizeLimit::new(io::IoReader::new(reader), max.into()))
299			.with_buffer(Vec::new());
300		(T::deserialize(&mut de)?, io::Input::peek_byte(&mut de.into_input()))
301	} else {
302		// The deserializer can parse both with and without `use_indices`.`
303		let mut de = Deserializer::new(io::IoReader::new(reader)).with_buffer(Vec::new());
304		(T::deserialize(&mut de)?, io::Input::peek_byte(&mut de.into_input()))
305	};
306
307	if error_on_excess && peek.is_ok() {
308		return Err(Error::ExcessData);
309	}
310
311	Ok(value)
312}
313
314/// Deserialize a type from a [Read]er.
315#[cfg(feature = "std")]
316pub fn from_reader<R, T>(reader: R) -> Result<T>
317where
318	R: Read,
319	T: DeserializeOwned,
320{
321	from_reader_with_config(reader, Config::default())
322}
323
324#[cfg(test)]
325mod tests;