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