Skip to main content

commonware_codec/types/
mod.rs

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