commonware_codec/lib.rs
1//! Serialize structured data.
2//!
3//! # Overview
4//!
5//! Provides traits and implementations for efficient and safe binary serialization and
6//! deserialization of structured data. The library focuses on:
7//!
8//! - **Performance:** Uses the [`bytes`] crate and aims to minimize allocations.
9//! - **Safety:** Deserialization of untrusted data is made safer via the [`Config`] system for the
10//! [`Read`] trait, allowing users to impose limits (like maximum lengths) or other strict
11//! constraints on the data.
12//! - **Ease of Use:** Provides implementations for common Rust types and uses extension traits
13//! ([`ReadExt`], [`DecodeExt`], etc.) for ergonomic usage.
14//!
15//! # Core Concepts
16//!
17//! The library revolves around a few core traits:
18//!
19//! - [`Write`]: Implement this to define how your type is written to a byte buffer.
20//! - [`Read<Cfg>`]: Implement this to define how your type is read from a byte buffer.
21//! It takes a configuration `Cfg` parameter, primarily used to enforce constraints
22//! (e.g., size limits) when reading untrusted data. Use `()` if no config is needed.
23//! - [`EncodeSize`]: Implement this to calculate the exact encoded byte size of a value.
24//! Required for efficient buffer pre-allocation.
25//! - [`FixedSize`]: Marker trait for types whose encoded size is constant. Automatically
26//! implements [`EncodeSize`].
27//!
28//! Helper traits combine these for convenience:
29//!
30//! - [`Encode`]: Combines [`Write`] + [`EncodeSize`]. Provides [`Encode::encode()`] method.
31//! - [`Decode<Cfg>`]: Requires [`Read<Cfg>`]. Provides [`Decode::decode_cfg()`] method that ensures
32//! that the entire buffer is consumed.
33//! - [`Codec<Cfg>`]: Combines [`Encode`] + [`Decode<Cfg>`].
34//!
35//! # Supported Types
36//!
37//! Natively supports encoding/decoding for:
38//! - Primitives: [`bool`],
39//! [`u8`], [`u16`], [`u32`], [`u64`], [`u128`],
40//! [`i8`], [`i16`], [`i32`], [`i64`], [`i128`],
41//! [`f32`], [`f64`], `[u8; N]`
42//! - Collections: [`Vec<T>`], [`Option<T>`]
43//! - Tuples: `(T1, T2, ...)` (up to 12 elements)
44//! - Networking:
45//! [`Ipv4Addr`](`std::net::Ipv4Addr`),
46//! [`Ipv6Addr`](`std::net::Ipv6Addr`),
47//! [`SocketAddrV4`](`std::net::SocketAddrV4`),
48//! [`SocketAddrV6`](`std::net::SocketAddrV6`),
49//! [`SocketAddr`](`std::net::SocketAddr`)
50//! - Common External Types: [`bytes::Bytes`]
51//!
52//! # Implementing for Custom Types
53//!
54//! You typically need to implement [`Write`], [`EncodeSize`] (unless [`FixedSize`]), and [`Read<Cfg>`]
55//! for your custom structs and enums.
56//!
57//! ## Example (Fixed Size Type)
58//!
59//! ```
60//! use bytes::{Buf, BufMut};
61//! use commonware_codec::{Error, FixedSize, Read, ReadExt, Write, Encode, DecodeExt};
62//!
63//! // Define a custom struct
64//! #[derive(Debug, Clone, PartialEq)]
65//! struct Point {
66//! x: u32, // FixedSize
67//! y: u32, // FixedSize
68//! }
69//!
70//! // 1. Implement Write: How to serialize the struct
71//! impl Write for Point {
72//! fn write(&self, buf: &mut impl BufMut) {
73//! // u32 implements Write
74//! self.x.write(buf);
75//! self.y.write(buf);
76//! }
77//! }
78//!
79//! // 2. Implement FixedSize (provides EncodeSize automatically)
80//! impl FixedSize for Point {
81//! // u32 implements FixedSize
82//! const SIZE: usize = u32::SIZE + u32::SIZE;
83//! }
84//!
85//! // 3. Implement Read: How to deserialize the struct (uses default Cfg = ())
86//! impl Read for Point {
87//! fn read_cfg(buf: &mut impl Buf, _: &()) -> Result<Self, Error> {
88//! // Use ReadExt::read for ergonomic reading when Cfg is ()
89//! let x = u32::read(buf)?;
90//! let y = u32::read(buf)?;
91//! Ok(Self { x, y })
92//! }
93//! }
94//!
95//! // Point now automatically implements Encode, Decode, Codec
96//! let point = Point { x: 1, y: 2 };
97//!
98//! // Encode is available via FixedSize + Write
99//! let bytes = point.encode();
100//! assert_eq!(bytes.len(), Point::SIZE);
101//!
102//! // Decode is available via Read<()>, use DecodeExt
103//! let decoded_point = Point::decode(bytes).unwrap();
104//! assert_eq!(point, decoded_point);
105//! ```
106//!
107//! ## Example (Variable Size Type)
108//!
109//! ```
110//! use bytes::{Buf, BufMut};
111//! use commonware_codec::{
112//! Config, Decode, Encode, EncodeSize, Error, FixedSize, Read, ReadExt,
113//! ReadRangeExt, Write, RangeConfig
114//! };
115//! use std::ops::RangeInclusive; // Example RangeConfig
116//!
117//! // Define a simple configuration for reading Item
118//! // Here, it just specifies the maximum allowed metadata length.
119//! #[derive(Clone)]
120//! pub struct ItemConfig {
121//! max_metadata_len: usize,
122//! }
123//!
124//! // Define a custom struct
125//! #[derive(Debug, Clone, PartialEq)]
126//! struct Item {
127//! id: u64, // FixedSize
128//! name: Option<u32>, // EncodeSize (depends on Option)
129//! metadata: Vec<u8>, // EncodeSize (variable)
130//! }
131//!
132//! // 1. Implement Write
133//! impl Write for Item {
134//! fn write(&self, buf: &mut impl BufMut) {
135//! self.id.write(buf); // u64 implements Write
136//! self.name.write(buf); // Option<u32> implements Write
137//! self.metadata.write(buf); // Vec<u8> implements Write
138//! }
139//! }
140//!
141//! // 2. Implement EncodeSize
142//! impl EncodeSize for Item {
143//! fn encode_size(&self) -> usize {
144//! // Sum the sizes of the parts
145//! self.id.encode_size() // u64 implements EncodeSize (via FixedSize)
146//! + self.name.encode_size() // Option<u32> implements EncodeSize
147//! + self.metadata.encode_size() // Vec<u8> implements EncodeSize
148//! }
149//! }
150//!
151//! // 3. Implement Read<Cfg>
152//! impl Read<ItemConfig> for Item {
153//! // Use the config Cfg = ItemConfig
154//! fn read_cfg(buf: &mut impl Buf, cfg: &ItemConfig) -> Result<Self, Error> {
155//! // u64 requires Cfg = (), uses ReadExt::read
156//! let id = <u64>::read(buf)?;
157//!
158//! // Option<u32> requires Cfg = (), uses ReadExt::read
159//! let name = <Option<u32>>::read(buf)?;
160//!
161//! // For Vec<u8>, the required config is (RangeConfig, InnerConfig)
162//! // InnerConfig for u8 is (), so we need (RangeConfig, ())
163//! // We use ReadRangeExt::read_range which handles the () for us.
164//! // The RangeConfig limits the vector length using our ItemConfig.
165//! let metadata_range = 0..=cfg.max_metadata_len; // Create the RangeConfig
166//! let metadata = <Vec<u8>>::read_range(buf, metadata_range)?;
167//!
168//! Ok(Self { id, name, metadata })
169//! }
170//! }
171//!
172//! // Now you can use Encode and Decode:
173//! let item = Item { id: 101, name: None, metadata: vec![1, 2, 3] };
174//! let config = ItemConfig { max_metadata_len: 1024 };
175//!
176//! // Encode the item (uses Write + EncodeSize)
177//! let bytes = item.encode(); // Returns BytesMut
178//!
179//! // Decode the item (uses Read<ItemConfig>)
180//! // decode_cfg ensures all bytes are consumed.
181//! let decoded_item = Item::decode_cfg(bytes, &config).unwrap();
182//! assert_eq!(item, decoded_item);
183//! ```
184
185pub mod codec;
186pub mod error;
187pub mod extensions;
188pub mod types;
189pub mod util;
190pub mod varint;
191
192// Re-export main types and traits
193pub use codec::*;
194pub use error::Error;
195pub use extensions::*;
196pub use types::{net, primitives};