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