Skip to main content

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] and [MaxEncodeSize].
27//! - [MaxEncodeSize]: Provides an upper bound on encoded size that does not depend on
28//!   caller-supplied limits.
29//!
30//! Helper traits combine these for convenience:
31//!
32//! - [Encode]: Combines [Write] + [EncodeSize]. Provides [Encode::encode()] method.
33//! - [Decode]: Requires [Read]. Provides [Decode::decode_cfg()] method that ensures
34//!   that the entire buffer is consumed.
35//! - [Codec]: Combines [Encode] + [Decode].
36//!
37//! # Size Bounds
38//!
39//! Bounds reserve each scalar's maximum encoded type width, even when validation restricts its
40//! values. For example, a [`UInt<u64>`](varint::UInt) field contributes ten bytes even when its
41//! valid values fit in one. A bound therefore need not be attained by any valid value.
42//!
43//! Size functions accept caller-supplied limits for variable contents, such as item counts or
44//! byte lengths. Length prefixes still contribute their type's maximum width. For example,
45//! [`util::max_items_size`] gives a 133-byte bound for a `Vec<u8>` limited to 128 bytes: 128
46//! bytes of data and a five-byte [`UInt<u32>`](varint::UInt) length prefix.
47//!
48//! [FixedSize] types contribute their fixed size. Composite types sum their fields' bounds,
49//! and enums add their tag's bound to the largest variant bound. A size helper belongs with
50//! the encoding it describes, so enclosing types can reuse its result.
51//!
52//! # Decode Inputs
53//!
54//! Readers accept [Buf] inputs so decoded byte fields can share the input allocation. Pass owned
55//! buffers such as [::bytes::Bytes] directly, or borrow [::bytes::Bytes] through [Viewing] for an
56//! independent cursor without cloning the handle. Both preserve shared storage for retained byte
57//! fields. Decoders also accept owned [`Vec<u8>`] values through [Input] and take ownership of
58//! their allocation without copying the payload.
59//!
60//! Borrowed slices require [Copying]. Creating this adapter does not allocate, so scalar
61//! and byte-array reads can use reusable scratch storage. Fields that retain bytes copy
62//! their contents when decoded through the adapter.
63//!
64//! Use [Buf] for generic readers of serialized values and every helper on that read path,
65//! including fixed-size reads and length or padding validation. Use [Input] at entry
66//! points that convert owned inputs into readable buffers, then preserve [Buf] internally.
67//!
68//! Use [::bytes::Buf] for raw buffer implementations and byte-stream inputs to I/O,
69//! encoding, or hashing. When its cursor methods must be in scope alongside [Buf],
70//! import it as `use bytes::Buf as _;`.
71//!
72//! # Specialization
73//!
74//! Byte-oriented container paths use hidden trait hooks on [Write], [Read], and [EncodeSize] to
75//! select bulk-copy implementations while keeping generic fallbacks. Container implementations
76//! call hooks such as `T::write_slice`, `T::read_vec`, and `T::encode_size_slice`. The default
77//! methods preserve element-by-element behavior, while concrete element implementations can
78//! override only the paths they can make faster.
79//!
80//! Encoding specialization has two parts: aggregate sizing and aggregate writing. For example,
81//! `Vec<u8>::encode()` first asks for the output size, then writes the bytes. The [EncodeSize]
82//! slice hooks let fixed-size elements compute `SIZE * len` without scanning every element, while
83//! the [Write] slice hooks let byte containers write the payload with one bulk copy.
84//!
85//! These hooks keep the container code generic: a container like `Vec<T>` calls one element-level
86//! method for sizing, writing, or reading, and the element implementation decides whether the
87//! default element-by-element behavior or a bulk path applies.
88//!
89//! # Supported Types
90//!
91//! Natively supports encoding/decoding for:
92//! - Primitives: [bool],
93//!   [u8], [u16], [u32], [u64], [u128],
94//!   [i8], [i16], [i32], [i64], [i128],
95//!   [f32], [f64], and [usize] (must fit within a [u32] for cross-platform compatibility).
96//! - Arrays: `[T; N]` supports [Write] and [Read] when `T` does, and supports [FixedSize],
97//!   [Encode], [Codec], [EncodeFixed], and [CodecFixed] when `T: FixedSize`.
98//! - Collections: [`Vec`], [`Option`], `BTreeMap`, `BTreeSet`
99//! - Tuples: `(T1, T2, ...)` (up to 12 elements)
100//! - Common External Types: [::bytes::Bytes], `Arc<T>` (delegates to `T`)
101//!
102//! With the `std` feature (enabled by default):
103//! - Networking:
104//!   [`std::net::Ipv4Addr`],
105//!   [`std::net::Ipv6Addr`],
106//!   [`std::net::SocketAddrV4`],
107//!   [`std::net::SocketAddrV6`],
108//!   [`std::net::IpAddr`],
109//!   [`std::net::SocketAddr`]
110//! - Collections:
111//!   [`std::collections::HashMap`],
112//!   [`std::collections::HashSet`]
113//!
114//! # Implementing for Custom Types
115//!
116//! You typically need to implement [Write], [EncodeSize] (unless [FixedSize]), and [Read]
117//! for your custom structs and enums.
118//!
119//! ## Example 1. Fixed-Size Type
120//!
121//! ```
122//! use bytes::BufMut;
123//! use commonware_codec::{Buf, Error, FixedSize, Read, ReadExt, Write, Encode, DecodeExt};
124//!
125//! // Define a custom struct
126//! #[derive(Debug, Clone, PartialEq)]
127//! struct Point {
128//!     x: u32, // FixedSize
129//!     y: u32, // FixedSize
130//! }
131//!
132//! // 1. Implement Write: How to serialize the struct
133//! impl Write for Point {
134//!     fn write(&self, buf: &mut impl BufMut) {
135//!         // u32 implements Write
136//!         self.x.write(buf);
137//!         self.y.write(buf);
138//!     }
139//! }
140//!
141//! // 2. Implement FixedSize (provides EncodeSize automatically)
142//! impl FixedSize for Point {
143//!     // u32 implements FixedSize
144//!     const SIZE: usize = u32::SIZE + u32::SIZE;
145//! }
146//!
147//! // 3. Implement Read: How to deserialize the struct (uses default Cfg = ())
148//! impl Read for Point {
149//!     type Cfg = ();
150//!     fn read_cfg(buf: &mut impl Buf, _: &()) -> Result<Self, Error> {
151//!         // Use ReadExt::read for ergonomic reading when Cfg is ()
152//!         let x = u32::read(buf)?;
153//!         let y = u32::read(buf)?;
154//!         Ok(Self { x, y })
155//!     }
156//! }
157//!
158//! // Point now automatically implements Encode, Decode, Codec
159//! let point = Point { x: 1, y: 2 };
160//!
161//! // Encode is available via FixedSize + Write
162//! let bytes = point.encode();
163//! assert_eq!(bytes.len(), Point::SIZE);
164//!
165//! // Decode is available via Read, use DecodeExt
166//! let decoded_point = Point::decode(bytes).unwrap();
167//! assert_eq!(point, decoded_point);
168//! ```
169//!
170//! ## Example 2. Variable-Size Type
171//!
172//! ```
173//! use bytes::BufMut;
174//! use commonware_codec::{Buf,
175//!     Decode, Encode, EncodeSize, Error, FixedSize, Read, ReadExt,
176//!     ReadRangeExt, Write, RangeCfg
177//! };
178//! use core::ops::RangeInclusive; // Example RangeCfg
179//!
180//! // Define a simple configuration for reading Item
181//! // Here, it just specifies the maximum allowed metadata length.
182//! #[derive(Clone)]
183//! pub struct ItemConfig {
184//!     max_metadata_len: usize,
185//! }
186//!
187//! // Define a custom struct
188//! #[derive(Debug, Clone, PartialEq)]
189//! struct Item {
190//!     id: u64,           // FixedSize
191//!     name: Option<u32>, // EncodeSize (depends on Option)
192//!     metadata: Vec<u8>, // EncodeSize (variable)
193//! }
194//!
195//! // 1. Implement Write
196//! impl Write for Item {
197//!     fn write(&self, buf: &mut impl BufMut) {
198//!         self.id.write(buf);       // u64 implements Write
199//!         self.name.write(buf);     // Option<u32> implements Write
200//!         self.metadata.write(buf); // Vec<u8> implements Write
201//!     }
202//! }
203//!
204//! // 2. Implement EncodeSize
205//! impl EncodeSize for Item {
206//!     fn encode_size(&self) -> usize {
207//!         // Sum the sizes of the parts
208//!         self.id.encode_size()         // u64 implements EncodeSize (via FixedSize)
209//!         + self.name.encode_size()     // Option<u32> implements EncodeSize
210//!         + self.metadata.encode_size() // Vec<u8> implements EncodeSize
211//!     }
212//! }
213//!
214//! // 3. Implement Read
215//! impl Read for Item {
216//!     type Cfg = ItemConfig;
217//!     fn read_cfg(buf: &mut impl Buf, cfg: &ItemConfig) -> Result<Self, Error> {
218//!         // u64 requires Cfg = (), uses ReadExt::read
219//!         let id = <u64>::read(buf)?;
220//!
221//!         // Option<u32> requires Cfg = (), uses ReadExt::read
222//!         let name = <Option<u32>>::read(buf)?;
223//!
224//!         // For Vec<u8>, the required config is (RangeCfg, InnerConfig)
225//!         // InnerConfig for u8 is (), so we need (RangeCfg, ())
226//!         // We use ReadRangeExt::read_range which handles the () for us.
227//!         // The RangeCfg limits the vector length using our ItemConfig.
228//!         let metadata_range = 0..=cfg.max_metadata_len; // Create the RangeCfg
229//!         let metadata = <Vec<u8>>::read_range(buf, metadata_range)?;
230//!
231//!         Ok(Self { id, name, metadata })
232//!     }
233//! }
234//!
235//! // Now you can use Encode and Decode:
236//! let item = Item { id: 101, name: None, metadata: vec![1, 2, 3] };
237//! let config = ItemConfig { max_metadata_len: 1024 };
238//!
239//! // Encode the item (uses Write + EncodeSize)
240//! let bytes = item.encode(); // Returns BytesMut
241//!
242//! // Decode the item
243//! // decode_cfg ensures all bytes are consumed.
244//! let decoded_item = Item::decode_cfg(bytes, &config).unwrap();
245//! assert_eq!(item, decoded_item);
246//! ```
247
248#![doc(
249    html_logo_url = "https://commonware.xyz/imgs/rustdoc_logo.svg",
250    html_favicon_url = "https://commonware.xyz/favicon.ico"
251)]
252#![cfg_attr(not(any(feature = "std", test)), no_std)]
253
254commonware_macros::stability_scope!(BETA {
255    #[cfg(not(feature = "std"))]
256    extern crate alloc;
257
258    mod buf;
259    pub use buf::{Buf, Copying, Input, OwnedBytes, Viewing};
260
261    pub mod codec;
262    pub mod config;
263    pub mod error;
264    pub mod extensions;
265    pub mod mode;
266    pub mod types;
267    pub mod util;
268    pub mod varint;
269
270    // Re-export main types and traits
271    pub use codec::*;
272    pub use commonware_codec_macros::FixedArray;
273    pub use config::RangeCfg;
274    pub use error::Error;
275    pub use extensions::*;
276    pub use mode::{InvalidMode, Mode, Modes};
277});
278
279commonware_macros::stability_scope!(ALPHA {
280    #[cfg(feature = "arbitrary")]
281    pub mod conformance;
282
283    // Re-export paste for use in conformance macros
284    #[cfg(feature = "arbitrary")]
285    #[doc(hidden)]
286    pub use paste;
287});