Skip to main content

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