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 `Cfg` associated type in
10//!   the [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]: Implement this to define how your type is read from a byte buffer.
21//!   It has an associated `Cfg` type, primarily used to enforce constraints (e.g., size limits)
22//!   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]: Requires [Read]. Provides [Decode::decode_cfg()] method that ensures
32//!   that the entire buffer is consumed.
33//! - [Codec]: Combines [Encode] + [Decode].
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//!   and [usize] (must fit within a [u32] for cross-platform compatibility).
43//! - Collections: [`Vec`], [`Option`], `BTreeMap`, `BTreeSet`
44//! - Tuples: `(T1, T2, ...)` (up to 12 elements)
45//! - Common External Types: [::bytes::Bytes]
46//!
47//! With the `std` feature (enabled by default):
48//! - Networking:
49//!   [`std::net::Ipv4Addr`],
50//!   [`std::net::Ipv6Addr`],
51//!   [`std::net::SocketAddrV4`],
52//!   [`std::net::SocketAddrV6`],
53//!   [`std::net::IpAddr`],
54//!   [`std::net::SocketAddr`]
55//! - Collections:
56//!   [`std::collections::HashMap`],
57//!   [`std::collections::HashSet`]
58//!
59//! # Implementing for Custom Types
60//!
61//! You typically need to implement [Write], [EncodeSize] (unless [FixedSize]), and [Read]
62//! for your custom structs and enums.
63//!
64//! ## Example 1. Fixed-Size Type
65//!
66//! ```
67//! use bytes::{Buf, BufMut};
68//! use commonware_codec::{Error, FixedSize, Read, ReadExt, Write, Encode, DecodeExt};
69//!
70//! // Define a custom struct
71//! #[derive(Debug, Clone, PartialEq)]
72//! struct Point {
73//!     x: u32, // FixedSize
74//!     y: u32, // FixedSize
75//! }
76//!
77//! // 1. Implement Write: How to serialize the struct
78//! impl Write for Point {
79//!     fn write(&self, buf: &mut impl BufMut) {
80//!         // u32 implements Write
81//!         self.x.write(buf);
82//!         self.y.write(buf);
83//!     }
84//! }
85//!
86//! // 2. Implement FixedSize (provides EncodeSize automatically)
87//! impl FixedSize for Point {
88//!     // u32 implements FixedSize
89//!     const SIZE: usize = u32::SIZE + u32::SIZE;
90//! }
91//!
92//! // 3. Implement Read: How to deserialize the struct (uses default Cfg = ())
93//! impl Read for Point {
94//!     type Cfg = ();
95//!     fn read_cfg(buf: &mut impl Buf, _: &()) -> Result<Self, Error> {
96//!         // Use ReadExt::read for ergonomic reading when Cfg is ()
97//!         let x = u32::read(buf)?;
98//!         let y = u32::read(buf)?;
99//!         Ok(Self { x, y })
100//!     }
101//! }
102//!
103//! // Point now automatically implements Encode, Decode, Codec
104//! let point = Point { x: 1, y: 2 };
105//!
106//! // Encode is available via FixedSize + Write
107//! let bytes = point.encode();
108//! assert_eq!(bytes.len(), Point::SIZE);
109//!
110//! // Decode is available via Read, use DecodeExt
111//! let decoded_point = Point::decode(bytes).unwrap();
112//! assert_eq!(point, decoded_point);
113//! ```
114//!
115//! ## Example 2. Variable-Size Type
116//!
117//! ```
118//! use bytes::{Buf, BufMut};
119//! use commonware_codec::{
120//!     Decode, Encode, EncodeSize, Error, FixedSize, Read, ReadExt,
121//!     ReadRangeExt, Write, RangeCfg
122//! };
123//! use core::ops::RangeInclusive; // Example RangeCfg
124//!
125//! // Define a simple configuration for reading Item
126//! // Here, it just specifies the maximum allowed metadata length.
127//! #[derive(Clone)]
128//! pub struct ItemConfig {
129//!     max_metadata_len: usize,
130//! }
131//!
132//! // Define a custom struct
133//! #[derive(Debug, Clone, PartialEq)]
134//! struct Item {
135//!     id: u64,           // FixedSize
136//!     name: Option<u32>, // EncodeSize (depends on Option)
137//!     metadata: Vec<u8>, // EncodeSize (variable)
138//! }
139//!
140//! // 1. Implement Write
141//! impl Write for Item {
142//!     fn write(&self, buf: &mut impl BufMut) {
143//!         self.id.write(buf);       // u64 implements Write
144//!         self.name.write(buf);     // Option<u32> implements Write
145//!         self.metadata.write(buf); // Vec<u8> implements Write
146//!     }
147//! }
148//!
149//! // 2. Implement EncodeSize
150//! impl EncodeSize for Item {
151//!     fn encode_size(&self) -> usize {
152//!         // Sum the sizes of the parts
153//!         self.id.encode_size()         // u64 implements EncodeSize (via FixedSize)
154//!         + self.name.encode_size()     // Option<u32> implements EncodeSize
155//!         + self.metadata.encode_size() // Vec<u8> implements EncodeSize
156//!     }
157//! }
158//!
159//! // 3. Implement Read
160//! impl Read for Item {
161//!     type Cfg = ItemConfig;
162//!     fn read_cfg(buf: &mut impl Buf, cfg: &ItemConfig) -> Result<Self, Error> {
163//!         // u64 requires Cfg = (), uses ReadExt::read
164//!         let id = <u64>::read(buf)?;
165//!
166//!         // Option<u32> requires Cfg = (), uses ReadExt::read
167//!         let name = <Option<u32>>::read(buf)?;
168//!
169//!         // For Vec<u8>, the required config is (RangeCfg, InnerConfig)
170//!         // InnerConfig for u8 is (), so we need (RangeCfg, ())
171//!         // We use ReadRangeExt::read_range which handles the () for us.
172//!         // The RangeCfg limits the vector length using our ItemConfig.
173//!         let metadata_range = 0..=cfg.max_metadata_len; // Create the RangeCfg
174//!         let metadata = <Vec<u8>>::read_range(buf, metadata_range)?;
175//!
176//!         Ok(Self { id, name, metadata })
177//!     }
178//! }
179//!
180//! // Now you can use Encode and Decode:
181//! let item = Item { id: 101, name: None, metadata: vec![1, 2, 3] };
182//! let config = ItemConfig { max_metadata_len: 1024 };
183//!
184//! // Encode the item (uses Write + EncodeSize)
185//! let bytes = item.encode(); // Returns BytesMut
186//!
187//! // Decode the item
188//! // decode_cfg ensures all bytes are consumed.
189//! let decoded_item = Item::decode_cfg(bytes, &config).unwrap();
190//! assert_eq!(item, decoded_item);
191//! ```
192
193#![doc(
194    html_logo_url = "https://commonware.xyz/imgs/rustdoc_logo.svg",
195    html_favicon_url = "https://commonware.xyz/favicon.ico"
196)]
197#![cfg_attr(not(feature = "std"), no_std)]
198
199#[cfg(not(feature = "std"))]
200extern crate alloc;
201
202pub mod codec;
203pub mod config;
204pub mod error;
205pub mod extensions;
206pub mod types;
207pub mod util;
208pub mod varint;
209
210// Re-export main types and traits
211pub use codec::*;
212pub use config::RangeCfg;
213pub use error::Error;
214pub use extensions::*;
215
216#[cfg(feature = "arbitrary")]
217pub mod conformance;
218
219// Re-export paste for use in conformance macros
220#[cfg(feature = "arbitrary")]
221#[doc(hidden)]
222pub use paste;