commonware_codec/types/
btree_set.rs

1//! Codec implementations for no_std compatible set types.
2//!
3//! For portability and consistency between architectures,
4//! the size of the set must fit within a [u32].
5
6extern crate alloc;
7
8use crate::{
9    codec::{EncodeSize, Read, Write},
10    error::Error,
11    RangeCfg,
12};
13use alloc::collections::BTreeSet;
14use bytes::{Buf, BufMut};
15use core::cmp::Ordering;
16
17const BTREESET_TYPE: &str = "BTreeSet";
18
19/// Read items from [Buf] in ascending order.
20fn read_ordered_set<K, F>(
21    buf: &mut impl Buf,
22    len: usize,
23    cfg: &K::Cfg,
24    mut insert: F,
25    set_type: &'static str,
26) -> Result<(), Error>
27where
28    K: Read + Ord,
29    F: FnMut(K) -> bool,
30{
31    let mut last: Option<K> = None;
32    for _ in 0..len {
33        // Read item
34        let item = K::read_cfg(buf, cfg)?;
35
36        // Check if items are in ascending order
37        if let Some(ref last) = last {
38            match item.cmp(last) {
39                Ordering::Equal => return Err(Error::Invalid(set_type, "Duplicate item")),
40                Ordering::Less => return Err(Error::Invalid(set_type, "Items must ascend")),
41                _ => {}
42            }
43        }
44
45        // Add previous item, if exists
46        if let Some(last) = last.take() {
47            insert(last);
48        }
49        last = Some(item);
50    }
51
52    // Add last item, if exists
53    if let Some(last) = last {
54        insert(last);
55    }
56
57    Ok(())
58}
59
60// ---------- BTreeSet ----------
61
62impl<K: Ord + Eq + Write> Write for BTreeSet<K> {
63    fn write(&self, buf: &mut impl BufMut) {
64        self.len().write(buf);
65
66        // Items are already sorted in BTreeSet, so we can iterate directly
67        for item in self {
68            item.write(buf);
69        }
70    }
71}
72
73impl<K: Ord + Eq + EncodeSize> EncodeSize for BTreeSet<K> {
74    fn encode_size(&self) -> usize {
75        let mut size = self.len().encode_size();
76        for item in self {
77            size += item.encode_size();
78        }
79        size
80    }
81}
82
83impl<K: Read + Clone + Ord + Eq> Read for BTreeSet<K> {
84    type Cfg = (RangeCfg, K::Cfg);
85
86    fn read_cfg(buf: &mut impl Buf, (range, cfg): &Self::Cfg) -> Result<Self, Error> {
87        // Read and validate the length prefix
88        let len = usize::read_cfg(buf, range)?;
89        let mut set = BTreeSet::new();
90
91        // Read items in ascending order
92        read_ordered_set(buf, len, cfg, |item| set.insert(item), BTREESET_TYPE)?;
93
94        Ok(set)
95    }
96}
97
98#[cfg(test)]
99mod tests {
100    use super::*;
101    use crate::{
102        codec::{Decode, Encode},
103        FixedSize,
104    };
105    use bytes::{Bytes, BytesMut};
106    use core::fmt::Debug;
107
108    // Generic round trip test function for BTreeSet
109    fn round_trip_btree<K>(set: &BTreeSet<K>, range_cfg: RangeCfg, item_cfg: K::Cfg)
110    where
111        K: Write + EncodeSize + Read + Clone + Ord + Eq + Debug + PartialEq,
112        BTreeSet<K>: Read<Cfg = (RangeCfg, K::Cfg)>
113            + Decode<Cfg = (RangeCfg, K::Cfg)>
114            + Debug
115            + PartialEq
116            + Write
117            + EncodeSize,
118    {
119        let encoded = set.encode();
120        assert_eq!(set.encode_size(), encoded.len());
121        let config_tuple = (range_cfg, item_cfg);
122        let decoded = BTreeSet::<K>::decode_cfg(encoded, &config_tuple).expect("decode_cfg failed");
123        assert_eq!(set, &decoded);
124    }
125
126    #[test]
127    fn test_empty_btreeset() {
128        let set = BTreeSet::<u32>::new();
129        round_trip_btree(&set, (..).into(), ());
130        assert_eq!(set.encode_size(), 1); // varint 0
131        let encoded = set.encode();
132        assert_eq!(encoded, Bytes::from_static(&[0]));
133    }
134
135    #[test]
136    fn test_simple_btreeset_u32() {
137        let mut set = BTreeSet::new();
138        set.insert(1u32);
139        set.insert(5u32);
140        set.insert(2u32);
141        round_trip_btree(&set, (..).into(), ());
142        assert_eq!(set.encode_size(), 1 + 3 * u32::SIZE);
143    }
144
145    #[test]
146    fn test_large_btreeset() {
147        // Fixed-size items
148        let set: BTreeSet<_> = (0..1000u16).collect();
149        round_trip_btree(&set, (1000..=1000).into(), ());
150
151        // Variable-size items
152        let set: BTreeSet<_> = (0..1000usize).collect();
153        round_trip_btree(&set, (1000..=1000).into(), (..=1000).into());
154    }
155
156    #[test]
157    fn test_btreeset_decode_length_limit_exceeded() {
158        let mut set = BTreeSet::new();
159        set.insert(1u32);
160        set.insert(5u32);
161        let encoded = set.encode();
162
163        let config_tuple = ((0..=1).into(), ());
164        let result = BTreeSet::<u32>::decode_cfg(encoded, &config_tuple);
165        assert!(matches!(result, Err(Error::InvalidLength(2))));
166    }
167
168    #[test]
169    fn test_btreeset_decode_invalid_item_order() {
170        let mut encoded = BytesMut::new();
171        2usize.write(&mut encoded); // Set length = 2
172        5u32.write(&mut encoded); // Item 5
173        2u32.write(&mut encoded); // Item 2 (out of order)
174
175        let config_tuple = ((..).into(), ());
176        let result = BTreeSet::<u32>::decode_cfg(encoded, &config_tuple);
177        assert!(matches!(
178            result,
179            Err(Error::Invalid("BTreeSet", "Items must ascend"))
180        ));
181    }
182
183    #[test]
184    fn test_btreeset_decode_duplicate_item() {
185        let mut encoded = BytesMut::new();
186        2usize.write(&mut encoded); // Set length = 2
187        1u32.write(&mut encoded); // Item 1
188        1u32.write(&mut encoded); // Duplicate Item 1
189
190        let config_tuple = ((..).into(), ());
191        let result = BTreeSet::<u32>::decode_cfg(encoded, &config_tuple);
192        assert!(matches!(
193            result,
194            Err(Error::Invalid("BTreeSet", "Duplicate item"))
195        ));
196    }
197
198    #[test]
199    fn test_btreeset_deterministic_encoding() {
200        let mut set1 = BTreeSet::new();
201        (0..1000u32).for_each(|i| {
202            set1.insert(i);
203        });
204
205        let mut set2 = BTreeSet::new();
206        (0..1000u32).rev().for_each(|i| {
207            set2.insert(i);
208        });
209
210        assert_eq!(set1.encode(), set2.encode());
211    }
212}