#![allow(deprecated)]
mod impls;
mod iter;
pub use self::iter::{Difference, Drain, Intersection, Iter, SymmetricDifference, Union};
use super::{FreeList, LookupMap, ERR_INCONSISTENT_STATE};
use crate::store::free_list::FreeListIndex;
use crate::store::key::{Sha256, ToKey};
use crate::{env, IntoStorageKey};
use borsh::{BorshDeserialize, BorshSerialize};
use std::borrow::Borrow;
use std::fmt;
#[derive(BorshDeserialize, BorshSerialize)]
#[deprecated(
since = "2.0.0",
note = "Suboptimal iteration performance. See performance considerations doc for details."
)]
pub struct UnorderedSet<T, H = Sha256>
where
T: BorshSerialize + Ord,
H: ToKey,
{
#[borsh(bound(serialize = "", deserialize = ""))]
elements: FreeList<T>,
#[borsh(bound(serialize = "", deserialize = ""))]
index: LookupMap<T, FreeListIndex, H>,
}
impl<T, H> Drop for UnorderedSet<T, H>
where
T: BorshSerialize + Ord,
H: ToKey,
{
fn drop(&mut self) {
self.flush()
}
}
impl<T, H> fmt::Debug for UnorderedSet<T, H>
where
T: BorshSerialize + Ord + BorshDeserialize + fmt::Debug,
H: ToKey,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("UnorderedSet")
.field("elements", &self.elements)
.field("index", &self.index)
.finish()
}
}
impl<T> UnorderedSet<T, Sha256>
where
T: BorshSerialize + Ord,
{
#[inline]
pub fn new<S>(prefix: S) -> Self
where
S: IntoStorageKey,
{
Self::with_hasher(prefix)
}
}
impl<T, H> UnorderedSet<T, H>
where
T: BorshSerialize + Ord,
H: ToKey,
{
pub fn with_hasher<S>(prefix: S) -> Self
where
S: IntoStorageKey,
{
let mut vec_key = prefix.into_storage_key();
let map_key = [vec_key.as_slice(), b"m"].concat();
vec_key.push(b'v');
Self { elements: FreeList::new(vec_key), index: LookupMap::with_hasher(map_key) }
}
pub fn len(&self) -> u32 {
self.elements.len()
}
pub fn is_empty(&self) -> bool {
self.elements.is_empty()
}
pub fn clear(&mut self)
where
T: BorshDeserialize + Clone,
{
for e in self.elements.drain() {
self.index.set(e, None);
}
}
pub fn difference<'a>(&'a self, other: &'a UnorderedSet<T, H>) -> Difference<'a, T, H>
where
T: BorshDeserialize,
{
Difference::new(self, other)
}
pub fn symmetric_difference<'a>(
&'a self,
other: &'a UnorderedSet<T, H>,
) -> SymmetricDifference<'a, T, H>
where
T: BorshDeserialize + Clone,
{
SymmetricDifference::new(self, other)
}
pub fn intersection<'a>(&'a self, other: &'a UnorderedSet<T, H>) -> Intersection<'a, T, H>
where
T: BorshDeserialize,
{
Intersection::new(self, other)
}
pub fn union<'a>(&'a self, other: &'a UnorderedSet<T, H>) -> Union<'a, T, H>
where
T: BorshDeserialize + Clone,
{
Union::new(self, other)
}
pub fn is_disjoint(&self, other: &UnorderedSet<T, H>) -> bool
where
T: BorshDeserialize + Clone,
{
if self.len() <= other.len() {
self.iter().all(|v| !other.contains(v))
} else {
other.iter().all(|v| !self.contains(v))
}
}
pub fn is_subset(&self, other: &UnorderedSet<T, H>) -> bool
where
T: BorshDeserialize + Clone,
{
if self.len() <= other.len() {
self.iter().all(|v| other.contains(v))
} else {
false
}
}
pub fn is_superset(&self, other: &UnorderedSet<T, H>) -> bool
where
T: BorshDeserialize + Clone,
{
other.is_subset(self)
}
pub fn iter(&self) -> Iter<T>
where
T: BorshDeserialize,
{
Iter::new(self)
}
pub fn drain(&mut self) -> Drain<T, H>
where
T: BorshDeserialize,
{
Drain::new(self)
}
pub fn contains<Q: ?Sized>(&self, value: &Q) -> bool
where
T: Borrow<Q>,
Q: BorshSerialize + ToOwned<Owned = T> + Ord,
{
self.index.contains_key(value)
}
pub fn insert(&mut self, value: T) -> bool
where
T: Clone + BorshDeserialize,
{
let entry = self.index.get_mut_inner(&value);
if entry.value_mut().is_some() {
false
} else {
let element_index = self.elements.insert(value);
entry.replace(Some(element_index));
true
}
}
pub fn remove<Q: ?Sized>(&mut self, value: &Q) -> bool
where
T: Borrow<Q> + BorshDeserialize,
Q: BorshSerialize + ToOwned<Owned = T> + Ord,
{
match self.index.remove(value) {
Some(element_index) => {
self.elements
.remove(element_index)
.unwrap_or_else(|| env::panic_str(ERR_INCONSISTENT_STATE));
true
}
None => false,
}
}
pub fn flush(&mut self) {
self.elements.flush();
self.index.flush();
}
}
impl<T, H> UnorderedSet<T, H>
where
T: BorshSerialize + BorshDeserialize + Ord,
H: ToKey,
{
pub fn defrag(&mut self) {
self.elements.defrag(|_, _| {});
}
}
#[cfg(not(target_arch = "wasm32"))]
#[cfg(test)]
mod tests {
use crate::store::free_list::FreeListIndex;
use crate::store::UnorderedSet;
use crate::test_utils::test_env::setup_free;
use arbitrary::{Arbitrary, Unstructured};
use borsh::{to_vec, BorshDeserialize};
use rand::RngCore;
use rand::SeedableRng;
use std::collections::HashSet;
#[test]
fn basic_functionality() {
let mut set = UnorderedSet::new(b"b");
assert!(set.is_empty());
assert!(set.insert("test".to_string()));
assert!(set.contains("test"));
assert_eq!(set.len(), 1);
assert!(set.remove("test"));
assert_eq!(set.len(), 0);
}
#[test]
fn set_iterator() {
let mut set = UnorderedSet::new(b"b");
set.insert(0u8);
set.insert(1);
set.insert(2);
set.insert(3);
set.remove(&1);
let iter = set.iter();
assert_eq!(iter.len(), 3);
assert_eq!(iter.collect::<Vec<_>>(), [(&0), (&2), (&3)]);
let mut iter = set.iter();
assert_eq!(iter.nth(2), Some(&3));
assert_eq!(iter.next(), None);
assert_eq!(set.drain().collect::<Vec<_>>(), [0, 2, 3]);
assert!(set.is_empty());
}
#[test]
fn test_drain() {
let mut s = UnorderedSet::new(b"m");
s.extend(1..100);
for _ in 0..20 {
assert_eq!(s.len(), 99);
for _ in s.drain() {}
#[allow(clippy::never_loop)]
for _ in &s {
panic!("s should be empty!");
}
assert_eq!(s.len(), 0);
assert!(s.is_empty());
s.extend(1..100);
}
}
#[test]
fn test_extend() {
let mut a = UnorderedSet::<u64>::new(b"m");
a.insert(1);
a.extend([2, 3, 4]);
assert_eq!(a.len(), 4);
assert!(a.contains(&1));
assert!(a.contains(&2));
assert!(a.contains(&3));
assert!(a.contains(&4));
}
#[test]
fn test_difference() {
let mut set1 = UnorderedSet::new(b"m");
set1.insert("a".to_string());
set1.insert("b".to_string());
set1.insert("c".to_string());
set1.insert("d".to_string());
let mut set2 = UnorderedSet::new(b"n");
set2.insert("b".to_string());
set2.insert("c".to_string());
set2.insert("e".to_string());
assert_eq!(
set1.difference(&set2).collect::<HashSet<_>>(),
["a".to_string(), "d".to_string()].iter().collect::<HashSet<_>>()
);
assert_eq!(
set2.difference(&set1).collect::<HashSet<_>>(),
["e".to_string()].iter().collect::<HashSet<_>>()
);
assert!(set1.difference(&set2).nth(1).is_some());
assert!(set1.difference(&set2).nth(2).is_none());
}
#[test]
fn test_difference_empty() {
let mut set1 = UnorderedSet::new(b"m");
set1.insert(1);
set1.insert(2);
set1.insert(3);
let mut set2 = UnorderedSet::new(b"n");
set2.insert(3);
set2.insert(1);
set2.insert(2);
set2.insert(4);
assert_eq!(set1.difference(&set2).collect::<HashSet<_>>(), HashSet::new());
}
#[test]
fn test_symmetric_difference() {
let mut set1 = UnorderedSet::new(b"m");
set1.insert("a".to_string());
set1.insert("b".to_string());
set1.insert("c".to_string());
let mut set2 = UnorderedSet::new(b"n");
set2.insert("b".to_string());
set2.insert("c".to_string());
set2.insert("d".to_string());
assert_eq!(
set1.symmetric_difference(&set2).collect::<HashSet<_>>(),
["a".to_string(), "d".to_string()].iter().collect::<HashSet<_>>()
);
assert_eq!(
set2.symmetric_difference(&set1).collect::<HashSet<_>>(),
["a".to_string(), "d".to_string()].iter().collect::<HashSet<_>>()
);
}
#[test]
fn test_symmetric_difference_empty() {
let mut set1 = UnorderedSet::new(b"m");
set1.insert(1);
set1.insert(2);
set1.insert(3);
let mut set2 = UnorderedSet::new(b"n");
set2.insert(3);
set2.insert(1);
set2.insert(2);
assert_eq!(set1.symmetric_difference(&set2).collect::<HashSet<_>>(), HashSet::new());
}
#[test]
fn test_intersection() {
let mut set1 = UnorderedSet::new(b"m");
set1.insert("a".to_string());
set1.insert("b".to_string());
set1.insert("c".to_string());
let mut set2 = UnorderedSet::new(b"n");
set2.insert("b".to_string());
set2.insert("c".to_string());
set2.insert("d".to_string());
assert_eq!(
set1.intersection(&set2).collect::<HashSet<_>>(),
["b".to_string(), "c".to_string()].iter().collect::<HashSet<_>>()
);
assert_eq!(
set2.intersection(&set1).collect::<HashSet<_>>(),
["b".to_string(), "c".to_string()].iter().collect::<HashSet<_>>()
);
assert!(set1.intersection(&set2).nth(1).is_some());
assert!(set1.intersection(&set2).nth(2).is_none());
}
#[test]
fn test_intersection_empty() {
let mut set1 = UnorderedSet::new(b"m");
set1.insert(1);
set1.insert(2);
set1.insert(3);
let mut set2 = UnorderedSet::new(b"n");
set2.insert(4);
set2.insert(6);
set2.insert(5);
assert_eq!(set1.intersection(&set2).collect::<HashSet<_>>(), HashSet::new());
}
#[test]
fn test_union() {
let mut set1 = UnorderedSet::new(b"m");
set1.insert("a".to_string());
set1.insert("b".to_string());
set1.insert("c".to_string());
let mut set2 = UnorderedSet::new(b"n");
set2.insert("b".to_string());
set2.insert("c".to_string());
set2.insert("d".to_string());
assert_eq!(
set1.union(&set2).collect::<HashSet<_>>(),
["a".to_string(), "b".to_string(), "c".to_string(), "d".to_string()]
.iter()
.collect::<HashSet<_>>()
);
assert_eq!(
set2.union(&set1).collect::<HashSet<_>>(),
["a".to_string(), "b".to_string(), "c".to_string(), "d".to_string()]
.iter()
.collect::<HashSet<_>>()
);
}
#[test]
fn test_union_empty() {
let set1 = UnorderedSet::<u64>::new(b"m");
let set2 = UnorderedSet::<u64>::new(b"n");
assert_eq!(set1.union(&set2).collect::<HashSet<_>>(), HashSet::new());
}
#[test]
fn test_subset_and_superset() {
let mut a = UnorderedSet::new(b"m");
assert!(a.insert(0));
assert!(a.insert(50));
assert!(a.insert(110));
assert!(a.insert(70));
let mut b = UnorderedSet::new(b"n");
assert!(b.insert(0));
assert!(b.insert(70));
assert!(b.insert(190));
assert!(b.insert(2500));
assert!(b.insert(110));
assert!(b.insert(2000));
assert!(!a.is_subset(&b));
assert!(!a.is_superset(&b));
assert!(!b.is_subset(&a));
assert!(!b.is_superset(&a));
assert!(b.insert(50));
assert!(a.is_subset(&b));
assert!(!a.is_superset(&b));
assert!(!b.is_subset(&a));
assert!(b.is_superset(&a));
}
#[test]
fn test_disjoint() {
let mut xs = UnorderedSet::new(b"m");
let mut ys = UnorderedSet::new(b"n");
assert!(xs.is_disjoint(&ys));
assert!(ys.is_disjoint(&xs));
assert!(xs.insert(50));
assert!(ys.insert(110));
assert!(xs.is_disjoint(&ys));
assert!(ys.is_disjoint(&xs));
assert!(xs.insert(70));
assert!(xs.insert(190));
assert!(xs.insert(40));
assert!(ys.insert(20));
assert!(ys.insert(-110));
assert!(xs.is_disjoint(&ys));
assert!(ys.is_disjoint(&xs));
assert!(ys.insert(70));
assert!(!xs.is_disjoint(&ys));
assert!(!ys.is_disjoint(&xs));
}
#[derive(Arbitrary, Debug)]
enum Op {
Insert(u8),
Remove(u8),
Flush,
Restore,
Contains(u8),
}
#[test]
fn arbitrary() {
setup_free();
let mut rng = rand_xorshift::XorShiftRng::seed_from_u64(0);
let mut buf = vec![0; 4096];
for _ in 0..512 {
crate::mock::with_mocked_blockchain(|b| b.take_storage());
rng.fill_bytes(&mut buf);
let mut us = UnorderedSet::new(b"l");
let mut hs = HashSet::new();
let u = Unstructured::new(&buf);
if let Ok(ops) = Vec::<Op>::arbitrary_take_rest(u) {
for op in ops {
match op {
Op::Insert(v) => {
let r1 = us.insert(v);
let r2 = hs.insert(v);
assert_eq!(r1, r2)
}
Op::Remove(v) => {
let r1 = us.remove(&v);
let r2 = hs.remove(&v);
assert_eq!(r1, r2)
}
Op::Flush => {
us.flush();
}
Op::Restore => {
let serialized = to_vec(&us).unwrap();
us = UnorderedSet::deserialize(&mut serialized.as_slice()).unwrap();
}
Op::Contains(v) => {
let r1 = us.contains(&v);
let r2 = hs.contains(&v);
assert_eq!(r1, r2)
}
}
}
}
}
}
#[test]
fn defrag() {
let mut set = UnorderedSet::new(b"b");
let all_values = 0..=8;
for i in all_values {
set.insert(i);
}
let removed = [2, 4, 6];
let existing = [0, 1, 3, 5, 7, 8];
for id in removed {
set.remove(&id);
}
set.defrag();
for i in removed {
assert!(!set.contains(&i));
}
for i in existing {
assert!(set.contains(&i));
}
assert_eq!(*set.elements.get(FreeListIndex(2)).unwrap(), 8);
assert_eq!(*set.elements.get(FreeListIndex(4)).unwrap(), 7);
assert_eq!(set.elements.get(FreeListIndex(6)), None);
}
}