unc_sdk/collections/
unordered_set.rs

1//! A set implemented on a trie. Unlike `std::collections::HashSet` the elements in this set are not
2//! hashed but are instead serialized.
3use crate::collections::{append, append_slice, Vector};
4use crate::{env, IntoStorageKey};
5use borsh::{to_vec, BorshDeserialize, BorshSerialize};
6use std::mem::size_of;
7use unc_sdk_macros::unc;
8
9const ERR_INCONSISTENT_STATE: &str = "The collection is an inconsistent state. Did previous smart contract execution terminate unexpectedly?";
10const ERR_ELEMENT_SERIALIZATION: &str = "Cannot serialize element with Borsh";
11
12/// An iterable implementation of a set that stores its content directly on the trie.
13#[unc(inside_uncsdk)]
14pub struct UnorderedSet<T> {
15    element_index_prefix: Vec<u8>,
16    // ser/de is independent of `T` ser/de, `BorshSerialize`/`BorshDeserialize`/`BorshSchema` bounds removed
17    #[cfg_attr(not(feature = "abi"), borsh(bound(serialize = "", deserialize = "")))]
18    #[cfg_attr(
19        feature = "abi",
20        borsh(bound(serialize = "", deserialize = ""), schema(params = ""))
21    )]
22    elements: Vector<T>,
23}
24
25impl<T> UnorderedSet<T> {
26    /// Returns the number of elements in the set, also referred to as its size.
27    ///
28    /// # Examples
29    ///
30    /// ```
31    /// use unc_sdk::collections::UnorderedSet;
32    ///
33    /// let mut set: UnorderedSet<u8> = UnorderedSet::new(b"s");
34    /// assert_eq!(set.len(), 0);
35    /// set.insert(&1);
36    /// set.insert(&2);
37    /// assert_eq!(set.len(), 2);
38    /// ```
39    pub fn len(&self) -> u64 {
40        self.elements.len()
41    }
42
43    /// Returns `true` if the set contains no elements.
44    pub fn is_empty(&self) -> bool {
45        self.elements.is_empty()
46    }
47
48    /// Create new map with zero elements. Use `id` as a unique identifier.
49    ///
50    /// # Examples
51    ///
52    /// ```
53    /// use unc_sdk::collections::UnorderedSet;
54    /// let mut set: UnorderedSet<u32> = UnorderedSet::new(b"s");
55    /// ```
56    pub fn new<S>(prefix: S) -> Self
57    where
58        S: IntoStorageKey,
59    {
60        let prefix = prefix.into_storage_key();
61        let element_index_prefix = append(&prefix, b'i');
62        let elements_prefix = append(&prefix, b'e');
63
64        Self { element_index_prefix, elements: Vector::new(elements_prefix) }
65    }
66
67    fn serialize_index(index: u64) -> [u8; size_of::<u64>()] {
68        index.to_le_bytes()
69    }
70
71    fn deserialize_index(raw_index: &[u8]) -> u64 {
72        let mut result = [0u8; size_of::<u64>()];
73        result.copy_from_slice(raw_index);
74        u64::from_le_bytes(result)
75    }
76
77    fn raw_element_to_index_lookup(&self, element_raw: &[u8]) -> Vec<u8> {
78        append_slice(&self.element_index_prefix, element_raw)
79    }
80
81    /// Returns true if the set contains a serialized element.
82    fn contains_raw(&self, element_raw: &[u8]) -> bool {
83        let index_lookup = self.raw_element_to_index_lookup(element_raw);
84        env::storage_has_key(&index_lookup)
85    }
86
87    /// Adds a value to the set.
88    /// If the set did not have this value present, `true` is returned.
89    /// If the set did have this value present, `false` is returned.
90    pub fn insert_raw(&mut self, element_raw: &[u8]) -> bool {
91        let index_lookup = self.raw_element_to_index_lookup(element_raw);
92        match env::storage_read(&index_lookup) {
93            Some(_index_raw) => false,
94            None => {
95                // The element does not exist yet.
96                let next_index = self.len();
97                let next_index_raw = Self::serialize_index(next_index);
98                env::storage_write(&index_lookup, &next_index_raw);
99                self.elements.push_raw(element_raw);
100                true
101            }
102        }
103    }
104
105    /// Removes a value from the set. Returns whether the value was present in the set.
106    pub fn remove_raw(&mut self, element_raw: &[u8]) -> bool {
107        let index_lookup = self.raw_element_to_index_lookup(element_raw);
108        match env::storage_read(&index_lookup) {
109            Some(index_raw) => {
110                #[allow(clippy::branches_sharing_code)]
111                if self.len() == 1 {
112                    // If there is only one element then swap remove simply removes it without
113                    // swapping with the last element.
114                    env::storage_remove(&index_lookup);
115                } else {
116                    // If there is more than one element then swap remove swaps it with the last
117                    // element.
118                    let last_element_raw = match self.elements.get_raw(self.len() - 1) {
119                        Some(x) => x,
120                        None => env::panic_str(ERR_INCONSISTENT_STATE),
121                    };
122                    env::storage_remove(&index_lookup);
123                    // If the removed element was the last element from keys, then we don't need to
124                    // reinsert the lookup back.
125                    if last_element_raw != element_raw {
126                        let last_lookup_element =
127                            self.raw_element_to_index_lookup(&last_element_raw);
128                        env::storage_write(&last_lookup_element, &index_raw);
129                    }
130                }
131                let index = Self::deserialize_index(&index_raw);
132                self.elements.swap_remove_raw(index);
133                true
134            }
135            None => false,
136        }
137    }
138}
139
140impl<T> UnorderedSet<T>
141where
142    T: BorshSerialize + BorshDeserialize,
143{
144    fn serialize_element(element: &T) -> Vec<u8> {
145        match to_vec(element) {
146            Ok(x) => x,
147            Err(_) => env::panic_str(ERR_ELEMENT_SERIALIZATION),
148        }
149    }
150
151    /// Returns true if the set contains an element.
152    ///
153    /// # Examples
154    ///
155    /// ```
156    /// use unc_sdk::collections::UnorderedSet;
157    ///
158    /// let mut set: UnorderedSet<u8> = UnorderedSet::new(b"s");
159    /// assert_eq!(set.contains(&1), false);
160    /// set.insert(&1);
161    /// assert_eq!(set.contains(&1), true);
162    /// ```
163    pub fn contains(&self, element: &T) -> bool {
164        self.contains_raw(&Self::serialize_element(element))
165    }
166
167    /// Removes a value from the set. Returns whether the value was present in the set.
168    ///
169    /// # Examples
170    ///
171    /// ```
172    /// use unc_sdk::collections::UnorderedSet;
173    ///
174    /// let mut set: UnorderedSet<u8> = UnorderedSet::new(b"s");
175    /// assert_eq!(set.remove(&1), false);
176    /// set.insert(&1);
177    /// assert_eq!(set.remove(&1), true);
178    /// assert_eq!(set.contains(&1), false);
179    /// ```
180    pub fn remove(&mut self, element: &T) -> bool {
181        self.remove_raw(&Self::serialize_element(element))
182    }
183
184    /// Adds a value to the set.
185    /// If the set did not have this value present, `true` is returned.
186    /// If the set did have this value present, `false` is returned.
187    ///
188    /// # Examples
189    ///
190    /// ```
191    /// use unc_sdk::collections::UnorderedSet;
192    ///
193    /// let mut set: UnorderedSet<u8> = UnorderedSet::new(b"s");
194    /// assert_eq!(set.insert(&1), true);
195    /// assert_eq!(set.insert(&1), false);
196    /// assert_eq!(set.contains(&1), true);
197    /// ```
198    pub fn insert(&mut self, element: &T) -> bool {
199        self.insert_raw(&Self::serialize_element(element))
200    }
201
202    /// Clears the map, removing all elements.
203    ///
204    /// # Examples
205    ///
206    /// ```
207    /// use unc_sdk::collections::UnorderedSet;
208    ///
209    /// let mut set: UnorderedSet<u8> = UnorderedSet::new(b"s");
210    /// set.insert(&1);
211    /// set.insert(&2);
212    /// set.clear();
213    /// assert_eq!(set.len(), 0);
214    /// ```
215    pub fn clear(&mut self) {
216        for raw_element in self.elements.iter_raw() {
217            let index_lookup = self.raw_element_to_index_lookup(&raw_element);
218            env::storage_remove(&index_lookup);
219        }
220        self.elements.clear();
221    }
222
223    /// Copies elements into an `std::vec::Vec`.
224    pub fn to_vec(&self) -> std::vec::Vec<T> {
225        self.iter().collect()
226    }
227
228    /// Iterate over deserialized elements.
229    pub fn iter(&self) -> impl Iterator<Item = T> + '_ {
230        self.elements.iter()
231    }
232
233    pub fn extend<IT: IntoIterator<Item = T>>(&mut self, iter: IT) {
234        for el in iter {
235            self.insert(&el);
236        }
237    }
238
239    /// Returns a view of elements as a vector.
240    /// It's sometimes useful to have random access to the elements.
241    pub fn as_vector(&self) -> &Vector<T> {
242        &self.elements
243    }
244}
245
246impl<T> std::fmt::Debug for UnorderedSet<T>
247where
248    T: std::fmt::Debug + BorshSerialize + BorshDeserialize,
249{
250    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
251        f.debug_struct("UnorderedSet")
252            .field("element_index_prefix", &self.element_index_prefix)
253            .field("elements", &self.elements)
254            .finish()
255    }
256}
257
258#[cfg(not(target_arch = "wasm32"))]
259#[cfg(test)]
260mod tests {
261    use crate::collections::UnorderedSet;
262    use rand::seq::SliceRandom;
263    use rand::{Rng, SeedableRng};
264    use std::collections::HashSet;
265    use std::iter::FromIterator;
266
267    #[test]
268    pub fn test_insert_one() {
269        let mut map = UnorderedSet::new(b"m");
270        assert!(map.insert(&1));
271        assert!(!map.insert(&1));
272    }
273
274    #[test]
275    pub fn test_insert() {
276        let mut set = UnorderedSet::new(b"s");
277        let mut rng = rand_xorshift::XorShiftRng::seed_from_u64(0);
278        for _ in 0..500 {
279            let key = rng.gen::<u64>();
280            set.insert(&key);
281        }
282    }
283
284    #[test]
285    pub fn test_insert_remove() {
286        let mut set = UnorderedSet::new(b"s");
287        let mut rng = rand_xorshift::XorShiftRng::seed_from_u64(1);
288        let mut keys = vec![];
289        for _ in 0..100 {
290            let key = rng.gen::<u64>();
291            keys.push(key);
292            set.insert(&key);
293        }
294        keys.shuffle(&mut rng);
295        for key in keys {
296            assert!(set.remove(&key));
297        }
298    }
299
300    #[test]
301    pub fn test_remove_last_reinsert() {
302        let mut set = UnorderedSet::new(b"s");
303        let key1 = 1u64;
304        set.insert(&key1);
305        let key2 = 2u64;
306        set.insert(&key2);
307
308        let actual = set.remove(&key2);
309        assert!(actual);
310
311        let actual_reinsert = set.insert(&key2);
312        assert!(actual_reinsert);
313    }
314
315    #[test]
316    pub fn test_insert_override_remove() {
317        let mut set = UnorderedSet::new(b"s");
318        let mut rng = rand_xorshift::XorShiftRng::seed_from_u64(2);
319        let mut keys = vec![];
320        for _ in 0..100 {
321            let key = rng.gen::<u64>();
322            keys.push(key);
323            set.insert(&key);
324        }
325        keys.shuffle(&mut rng);
326        for key in &keys {
327            assert!(!set.insert(key));
328        }
329        keys.shuffle(&mut rng);
330        for key in keys {
331            assert!(set.remove(&key));
332        }
333    }
334
335    #[test]
336    pub fn test_contains_non_existent() {
337        let mut set = UnorderedSet::new(b"s");
338        let mut rng = rand_xorshift::XorShiftRng::seed_from_u64(3);
339        let mut set_tmp = HashSet::new();
340        for _ in 0..500 {
341            let key = rng.gen::<u64>() % 20_000;
342            set_tmp.insert(key);
343            set.insert(&key);
344        }
345        for _ in 0..500 {
346            let key = rng.gen::<u64>() % 20_000;
347            assert_eq!(set.contains(&key), set_tmp.contains(&key));
348        }
349    }
350
351    #[test]
352    pub fn test_to_vec() {
353        let mut set = UnorderedSet::new(b"s");
354        let mut rng = rand_xorshift::XorShiftRng::seed_from_u64(4);
355        let mut keys = HashSet::new();
356        for _ in 0..500 {
357            let key = rng.gen::<u64>();
358            keys.insert(key);
359            set.insert(&key);
360        }
361        let actual = HashSet::from_iter(set.to_vec());
362        assert_eq!(actual, keys);
363    }
364
365    #[test]
366    pub fn test_clear() {
367        let mut set = UnorderedSet::new(b"s");
368        let mut rng = rand_xorshift::XorShiftRng::seed_from_u64(5);
369        for _ in 0..10 {
370            for _ in 0..=(rng.gen::<u64>() % 20 + 1) {
371                let key = rng.gen::<u64>();
372                set.insert(&key);
373            }
374            assert!(!set.to_vec().is_empty());
375            set.clear();
376            assert!(set.to_vec().is_empty());
377        }
378    }
379
380    #[test]
381    pub fn test_iter() {
382        let mut set = UnorderedSet::new(b"s");
383        let mut rng = rand_xorshift::XorShiftRng::seed_from_u64(4);
384        let mut keys = HashSet::new();
385        for _ in 0..500 {
386            let key = rng.gen::<u64>();
387            keys.insert(key);
388            set.insert(&key);
389        }
390        let actual: HashSet<u64> = set.iter().collect();
391        assert_eq!(actual, keys);
392    }
393
394    #[test]
395    pub fn test_extend() {
396        let mut set = UnorderedSet::new(b"s");
397        let mut rng = rand_xorshift::XorShiftRng::seed_from_u64(4);
398        let mut keys = HashSet::new();
399        for _ in 0..100 {
400            let key = rng.gen::<u64>();
401            keys.insert(key);
402            set.insert(&key);
403        }
404        for _ in 0..10 {
405            let mut tmp = vec![];
406            for _ in 0..=(rng.gen::<u64>() % 20 + 1) {
407                let key = rng.gen::<u64>();
408                tmp.push(key);
409            }
410            keys.extend(tmp.iter().cloned());
411            set.extend(tmp.iter().cloned());
412        }
413
414        let actual: HashSet<u64> = set.iter().collect();
415        assert_eq!(actual, keys);
416    }
417
418    #[test]
419    fn test_debug() {
420        let mut set = UnorderedSet::new(b"m");
421        set.insert(&1u64);
422        set.insert(&3u64);
423        set.insert(&2u64);
424
425        if cfg!(feature = "expensive-debug") {
426            assert_eq!(
427                format!("{:?}", set),
428                "UnorderedSet { element_index_prefix: [109, 105], elements: [1, 3, 2] }"
429            );
430        } else {
431            assert_eq!(
432                format!("{:?}", set),
433                "UnorderedSet { element_index_prefix: [109, 105], elements: Vector { len: 3, prefix: [109, 101] } }"
434            );
435        }
436    }
437}