Skip to main content

commonware_codec/types/
mod.rs

1//! Codec implementations for common types
2
3use crate::{Error, Read};
4use ::bytes::Buf;
5use core::cmp::Ordering;
6
7pub mod btree_map;
8pub mod btree_set;
9pub mod bytes;
10#[cfg(feature = "std")]
11pub mod hash_map;
12#[cfg(feature = "std")]
13pub mod hash_set;
14pub mod lazy;
15#[cfg(feature = "std")]
16pub mod net;
17pub mod primitives;
18pub mod tuple;
19pub mod vec;
20
21/// Read keyed items from [Buf] in ascending order.
22pub(crate) fn read_ordered_map<K, V, F>(
23    buf: &mut impl Buf,
24    len: usize,
25    k_cfg: &K::Cfg,
26    v_cfg: &V::Cfg,
27    mut insert: F,
28    map_type: &'static str,
29) -> Result<(), Error>
30where
31    K: Read + Ord,
32    V: Read,
33    F: FnMut(K, V) -> Option<V>,
34{
35    let mut last: Option<(K, V)> = None;
36    for _ in 0..len {
37        // Read key
38        let key = K::read_cfg(buf, k_cfg)?;
39
40        // Check if keys are in ascending order relative to the previous key
41        if let Some((ref last_key, _)) = last {
42            match key.cmp(last_key) {
43                Ordering::Equal => return Err(Error::Invalid(map_type, "Duplicate key")),
44                Ordering::Less => return Err(Error::Invalid(map_type, "Keys must ascend")),
45                _ => {}
46            }
47        }
48
49        // Read value
50        let value = V::read_cfg(buf, v_cfg)?;
51
52        // Add previous item, if exists
53        if let Some((last_key, last_value)) = last.take() {
54            insert(last_key, last_value);
55        }
56        last = Some((key, value));
57    }
58
59    // Add last item, if exists
60    if let Some((last_key, last_value)) = last {
61        insert(last_key, last_value);
62    }
63
64    Ok(())
65}
66
67/// Read items from [Buf] in ascending order.
68pub(crate) fn read_ordered_set<K, F>(
69    buf: &mut impl Buf,
70    len: usize,
71    cfg: &K::Cfg,
72    mut insert: F,
73    set_type: &'static str,
74) -> Result<(), Error>
75where
76    K: Read + Ord,
77    F: FnMut(K) -> bool,
78{
79    let mut last: Option<K> = None;
80    for _ in 0..len {
81        // Read item
82        let item = K::read_cfg(buf, cfg)?;
83
84        // Check if items are in ascending order
85        if let Some(ref last) = last {
86            match item.cmp(last) {
87                Ordering::Equal => return Err(Error::Invalid(set_type, "Duplicate item")),
88                Ordering::Less => return Err(Error::Invalid(set_type, "Items must ascend")),
89                _ => {}
90            }
91        }
92
93        // Add previous item, if exists
94        if let Some(last) = last.take() {
95            insert(last);
96        }
97        last = Some(item);
98    }
99
100    // Add last item, if exists
101    if let Some(last) = last {
102        insert(last);
103    }
104
105    Ok(())
106}
107
108#[cfg(test)]
109pub(crate) mod tests {
110    use crate::{BufsMut, Error, Read, Write};
111    use bytes::{Buf, BufMut, Bytes, BytesMut, buf::UninitSlice};
112
113    /// One-byte test type that uses the default aggregate hooks.
114    ///
115    /// This lets tests distinguish the generic per-element path from the
116    /// specialized `u8` path while keeping the same encoded representation.
117    #[derive(Debug, PartialEq, Eq)]
118    pub struct Byte(pub u8);
119
120    impl Write for Byte {
121        fn write(&self, buf: &mut impl BufMut) {
122            buf.put_u8(self.0);
123        }
124    }
125
126    impl Read for Byte {
127        type Cfg = ();
128
129        fn read_cfg(buf: &mut impl Buf, _: &()) -> Result<Self, Error> {
130            Ok(Self(<u8 as Read>::read_cfg(buf, &())?))
131        }
132    }
133
134    /// Test [`BufMut`] implementation that records how values are written.
135    ///
136    /// Specialization-selection tests use this to assert whether a container
137    /// wrote its payload with one aggregate [`BufMut::put_slice`] call or with
138    /// per-element [`BufMut::put_u8`] calls.
139    pub struct TrackingWriteBuf {
140        inner: BytesMut,
141        /// Number of aggregate slice writes.
142        pub put_slice_calls: usize,
143        /// Number of single-byte writes.
144        pub put_u8_calls: usize,
145        /// Number of externally pushed chunks.
146        pub push_calls: usize,
147    }
148
149    impl TrackingWriteBuf {
150        pub fn new() -> Self {
151            Self {
152                inner: BytesMut::new(),
153                put_slice_calls: 0,
154                put_u8_calls: 0,
155                push_calls: 0,
156            }
157        }
158
159        pub fn freeze(self) -> Bytes {
160            self.inner.freeze()
161        }
162    }
163
164    // SAFETY: `TrackingWriteBuf` delegates storage and cursor management to
165    // `BytesMut`, which upholds the `BufMut` invariants. The overridden write
166    // methods only count calls before forwarding.
167    unsafe impl BufMut for TrackingWriteBuf {
168        fn remaining_mut(&self) -> usize {
169            self.inner.remaining_mut()
170        }
171
172        fn chunk_mut(&mut self) -> &mut UninitSlice {
173            self.inner.chunk_mut()
174        }
175
176        unsafe fn advance_mut(&mut self, cnt: usize) {
177            // SAFETY: The caller guarantees that `cnt` bytes in the current
178            // chunk were initialized. `BytesMut` owns the cursor state and
179            // enforces the remaining invariants.
180            unsafe { self.inner.advance_mut(cnt) }
181        }
182
183        fn put_slice(&mut self, src: &[u8]) {
184            self.put_slice_calls += 1;
185            self.inner.put_slice(src);
186        }
187
188        fn put_u8(&mut self, n: u8) {
189            self.put_u8_calls += 1;
190            self.inner.put_u8(n);
191        }
192    }
193
194    impl BufsMut for TrackingWriteBuf {
195        fn push(&mut self, bytes: impl Into<Bytes>) {
196            let bytes = bytes.into();
197            self.push_calls += 1;
198            self.inner.extend_from_slice(&bytes);
199        }
200    }
201
202    /// Test [`Buf`] implementation that records how values are read.
203    ///
204    /// Specialization-selection tests use this to assert whether a container
205    /// read its payload with one aggregate [`Buf::copy_to_slice`] call or with
206    /// per-element [`Buf::get_u8`] calls.
207    pub struct TrackingReadBuf {
208        inner: Bytes,
209        /// Number of aggregate slice reads.
210        pub copy_to_slice_calls: usize,
211        /// Number of single-byte reads.
212        pub get_u8_calls: usize,
213    }
214
215    impl TrackingReadBuf {
216        pub fn new(bytes: &'static [u8]) -> Self {
217            Self {
218                inner: Bytes::from_static(bytes),
219                copy_to_slice_calls: 0,
220                get_u8_calls: 0,
221            }
222        }
223    }
224
225    impl Buf for TrackingReadBuf {
226        fn remaining(&self) -> usize {
227            self.inner.remaining()
228        }
229
230        fn chunk(&self) -> &[u8] {
231            self.inner.chunk()
232        }
233
234        fn advance(&mut self, cnt: usize) {
235            self.inner.advance(cnt)
236        }
237
238        fn copy_to_slice(&mut self, dst: &mut [u8]) {
239            self.copy_to_slice_calls += 1;
240            self.inner.copy_to_slice(dst);
241        }
242
243        fn get_u8(&mut self) -> u8 {
244            self.get_u8_calls += 1;
245            self.inner.get_u8()
246        }
247    }
248}