unc_sdk/collections/
unordered_set.rs1use 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#[unc(inside_uncsdk)]
14pub struct UnorderedSet<T> {
15 element_index_prefix: Vec<u8>,
16 #[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 pub fn len(&self) -> u64 {
40 self.elements.len()
41 }
42
43 pub fn is_empty(&self) -> bool {
45 self.elements.is_empty()
46 }
47
48 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 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 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 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 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 env::storage_remove(&index_lookup);
115 } else {
116 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 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 pub fn contains(&self, element: &T) -> bool {
164 self.contains_raw(&Self::serialize_element(element))
165 }
166
167 pub fn remove(&mut self, element: &T) -> bool {
181 self.remove_raw(&Self::serialize_element(element))
182 }
183
184 pub fn insert(&mut self, element: &T) -> bool {
199 self.insert_raw(&Self::serialize_element(element))
200 }
201
202 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 pub fn to_vec(&self) -> std::vec::Vec<T> {
225 self.iter().collect()
226 }
227
228 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 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}