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