From c703041f4bf3c1471d028bdf2fd79b0c4107a612 Mon Sep 17 00:00:00 2001 From: "hanif.ariffin.4326@gmail.com" Date: Fri, 7 Aug 2020 14:30:16 -0400 Subject: [PATCH 1/6] Rewrite the entire library. Implemented some helper functions Partially implemented some of the easier functions from here https://github.com/servo/rust-smallvec/issues/220#issuecomment-624303063 Implemented `PartialEq` for SmallSet Added tests for `replace`. nitpick from Intellij Rust SmallSet is now a wrapper over enum of SmallVec and HashSet Insertion now moves to a heap allocated HashSet when capacity. Fixed compilation error from previous commit Might want to merge this two later. Cleared now changes to stack. Implemented drain Moved tests and updated them. Updated tests. Fixed set equality. --- .gitignore | 1 + Cargo.toml | 2 +- src/lib.rs | 542 +++++++++++++++++++++++++++++++++++++++++--------- tests/test.rs | 230 +++++++++++++++++++++ 4 files changed, 680 insertions(+), 95 deletions(-) create mode 100644 tests/test.rs diff --git a/.gitignore b/.gitignore index a9d37c5..4a1e9db 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ target Cargo.lock +.idea/ diff --git a/Cargo.toml b/Cargo.toml index d2a231a..7431145 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,4 +8,4 @@ documentation = "https://cfallin.github.io/rust-smallset/smallset/" license = "MIT" [dependencies] -smallvec = "0.1" +smallvec = "1.4.2" diff --git a/src/lib.rs b/src/lib.rs index 2cbcf20..d36f9ba 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,12 +4,14 @@ // Copyright (c) 2016 Chris Fallin . Released under the MIT license. // +extern crate smallvec; + use std::fmt; use std::iter::{FromIterator, IntoIterator}; -use std::slice::Iter; -extern crate smallvec; use smallvec::{Array, SmallVec}; +use std::collections::HashSet; +use std::hash::Hash; /// A `SmallSet` is an unordered set of elements. It is designed to work best /// for very small sets (no more than ten or so elements). In order to support @@ -33,70 +35,456 @@ use smallvec::{Array, SmallVec}; /// s.insert(1); /// s.insert(2); /// s.insert(3); -/// assert!(s.len() == 3); +/// assert_eq!(s.len(), 3); /// assert!(s.contains(&1)); /// ``` + pub struct SmallSet where A::Item: PartialEq + Eq, { - elements: SmallVec, + inner: InnerSmallVec, } -impl SmallSet +impl Default for SmallSet +where + A::Item: PartialEq + Eq + Hash, +{ + fn default() -> Self { + SmallSet::new() + } +} + +pub enum InnerSmallVec +where + A::Item: PartialEq + Eq, +{ + Stack(SmallVec), + Heap(std::collections::HashSet), +} + +impl Default for InnerSmallVec where A::Item: PartialEq + Eq, +{ + fn default() -> Self { + InnerSmallVec::Stack(SmallVec::new()) + } +} + +impl Clone for InnerSmallVec +where + A::Item: PartialEq + Eq + Clone, +{ + fn clone(&self) -> Self { + match &self { + InnerSmallVec::Stack(elements) => InnerSmallVec::Stack(elements.clone()), + InnerSmallVec::Heap(elements) => InnerSmallVec::Heap(elements.clone()), + } + } +} + +impl PartialEq for SmallSet +where + A::Item: Eq + PartialEq + Hash, +{ + fn eq(&self, other: &Self) -> bool { + fn set_same(stack: &SmallVec, heap: &HashSet) -> bool + where + A::Item: Eq + PartialEq, + { + stack.len() == heap.len() && heap.iter().all(|x| stack.contains(x)) + } + + match (&self.inner, &other.inner) { + (InnerSmallVec::Stack(lhs), InnerSmallVec::Stack(rhs)) => lhs.eq(rhs), + (InnerSmallVec::Heap(lhs), InnerSmallVec::Heap(rhs)) => lhs.eq(rhs), + (InnerSmallVec::Stack(stack), InnerSmallVec::Heap(heap)) => set_same(stack, heap), + (InnerSmallVec::Heap(heap), InnerSmallVec::Stack(stack)) => set_same(stack, heap), + } + } +} + +impl SmallSet +where + A::Item: PartialEq + Eq + Hash, { /// Creates a new, empty `SmallSet`. pub fn new() -> SmallSet { SmallSet { - elements: SmallVec::new(), + inner: InnerSmallVec::Stack(SmallVec::new()), } } + pub fn is_empty(&self) -> bool { + self.len() == 0 + } + /// Inserts `elem` into the set if not yet present. Returns `true` if the /// set did not have this element present, or `false` if it already had this /// element present. pub fn insert(&mut self, elem: A::Item) -> bool { - if !self.contains(&elem) { - self.elements.push(elem); - true - } else { - false + match &mut self.inner { + InnerSmallVec::Stack(ref mut elements) => { + if elements.contains(&elem) { + false + } else { + if elements.len() + 1 <= A::size() { + elements.push(elem); + } else { + let mut ee = HashSet::::with_capacity(elements.len() + 1); + while !elements.is_empty() { + ee.insert(elements.remove(0)); + } + ee.insert(elem); + self.inner = InnerSmallVec::Heap(ee); + } + true + } + } + InnerSmallVec::Heap(ref mut elements) => elements.insert(elem), } } /// Removes `elem` from the set. Returns `true` if the element was removed, /// or `false` if it was not found. pub fn remove(&mut self, elem: &A::Item) -> bool { - if let Some(pos) = self.elements.iter().position(|e| *e == *elem) { - self.elements.remove(pos); - true - } else { - false + match &mut self.inner { + InnerSmallVec::Stack(ref mut elements) => { + if let Some(pos) = elements.iter().position(|e| *e == *elem) { + elements.remove(pos); + true + } else { + false + } + } + InnerSmallVec::Heap(ref mut elements) => elements.remove(elem), } } /// Tests whether `elem` is present. Returns `true` if it is present, or /// `false` if not. pub fn contains(&self, elem: &A::Item) -> bool { - self.elements.iter().any(|e| *e == *elem) + match &self.inner { + InnerSmallVec::Stack(ref elements) => elements.iter().any(|e| *e == *elem), + InnerSmallVec::Heap(ref elements) => elements.contains(elem), + } } /// Returns an iterator over the set elements. Elements will be returned in /// an arbitrary (unsorted) order. - pub fn iter(&self) -> Iter { - self.elements.iter() + pub fn iter(&self) -> SmallIter { + match &self.inner { + InnerSmallVec::Stack(element) => SmallIter { + inner: InnerSmallIter::Stack(element.iter()), + }, + InnerSmallVec::Heap(element) => SmallIter { + inner: InnerSmallIter::Heap(element.iter()), + }, + } } /// Returns the current length of the set. pub fn len(&self) -> usize { - self.elements.len() + match &self.inner { + InnerSmallVec::Stack(elements) => elements.len(), + InnerSmallVec::Heap(elements) => elements.len(), + } } /// Clears the set. pub fn clear(&mut self) { - self.elements.clear(); + match &mut self.inner { + InnerSmallVec::Stack(ref mut elements) => elements.clear(), + InnerSmallVec::Heap(ref mut elements) => { + elements.clear(); + self.inner = Default::default(); + } + } + } + + // + pub fn get(&self, value: &A::Item) -> Option<&A::Item> { + match &self.inner { + InnerSmallVec::Stack(elements) => elements.iter().find(|x| (value).eq(&x)), + InnerSmallVec::Heap(elements) => elements.iter().find(|x| (value).eq(&x)), + } + } + + pub fn take(&mut self, value: &A::Item) -> Option { + match &mut self.inner { + InnerSmallVec::Stack(ref mut elements) => { + if let Some(pos) = elements.iter().position(|e| *e == *value) { + let result = elements.remove(pos); + Some(result) + } else { + None + } + } + InnerSmallVec::Heap(ref mut elements) => elements.take(value), + } + } + + // Adds a value to the set, replacing the existing value, if any, that is equal to the given one. Returns the replaced value. + pub fn replace(&mut self, value: A::Item) -> Option { + match &mut self.inner { + InnerSmallVec::Stack(ref mut elements) => { + if let Some(pos) = elements.iter().position(|e| *e == value) { + let result = elements.remove(pos); + elements.insert(pos, value); + Some(result) + } else { + None + } + } + InnerSmallVec::Heap(ref mut elements) => elements.replace(value), + } + } + + pub fn drain(&mut self) -> SmallDrain { + match &mut self.inner { + InnerSmallVec::Stack(ref mut elements) => { + // TODO: Clean up this garbage... + let mut ee = Vec::::with_capacity(elements.len() + 1); + while !elements.is_empty() { + ee.push(elements.remove(0)); + } + SmallDrain { data: ee, index: 0 } + } + InnerSmallVec::Heap(ref mut elements) => { + let drain = elements.drain().collect::>(); + SmallDrain { + data: drain, + index: 0, + } + } + } + } + + pub fn retain(&mut self, f: F) + where + F: FnMut(&mut A::Item) -> bool + for<'r> FnMut(&'r ::Item) -> bool, + { + match &mut self.inner { + InnerSmallVec::Stack(ref mut elements) => elements.retain(f), + InnerSmallVec::Heap(ref mut elements) => elements.retain(f), + } + } + + pub fn intersection<'a>(&'a self, other: &'a Self) -> SmallIntersection<'a, A::Item> { + match &self.inner { + InnerSmallVec::Stack(ref elements) => { + let result = elements + .iter() + .filter(|x| other.contains(x)) + .collect::>(); + SmallIntersection { + data: result, + index: 0, + } + } + + InnerSmallVec::Heap(ref elements) => { + let result = elements + .iter() + .filter(|x| other.contains(x)) + .collect::>(); + SmallIntersection { + data: result, + index: 0, + } + } + } + } + + pub fn union<'a>(&'a self, other: &'a Self) -> SmallUnion<'a, A::Item> { + match &self.inner { + InnerSmallVec::Stack(ref elements) => { + let mut lhs = elements.iter().collect::>(); + let mut rhs = other + .iter() + .filter(|x| !lhs.contains(x)) + .collect::>(); + lhs.append(&mut rhs); + SmallUnion { + data: lhs, + index: 0, + } + } + + InnerSmallVec::Heap(ref elements) => { + let mut lhs = elements.iter().collect::>(); + let mut rhs = other + .iter() + .filter(|x| !lhs.contains(x)) + .collect::>(); + lhs.append(&mut rhs); + SmallUnion { + data: rhs, + index: 0, + } + } + } + } + + pub fn difference<'a>(&'a self, other: &'a Self) -> SmallDifference<'a, A::Item> { + match &self.inner { + InnerSmallVec::Stack(ref elements) => { + let lhs = elements + .iter() + .filter(|x| !other.contains(x)) + .collect::>(); + SmallDifference { + data: lhs, + index: 0, + } + } + + InnerSmallVec::Heap(ref elements) => { + let lhs = elements + .iter() + .filter(|x| !other.contains(x)) + .collect::>(); + SmallDifference { + data: lhs, + index: 0, + } + } + } + } + + pub fn symmetric_difference<'a>( + &'a self, + other: &'a Self, + ) -> SmallSymmetricDifference<'a, A::Item> { + match &self.inner { + InnerSmallVec::Stack(ref elements) => { + let mut lhs = elements + .iter() + .filter(|x| !other.contains(x)) + .collect::>(); + let mut rhs = other + .iter() + .filter(|x| !elements.contains(x)) + .collect::>(); + lhs.append(&mut rhs); + SmallSymmetricDifference { + data: lhs, + index: 0, + } + } + + InnerSmallVec::Heap(ref elements) => { + let mut lhs = elements + .iter() + .filter(|x| other.contains(x)) + .collect::>(); + let mut rhs = other + .iter() + .filter(|x| elements.contains(x)) + .collect::>(); + lhs.append(&mut rhs); + SmallSymmetricDifference { + data: lhs, + index: 0, + } + } + } + } +} + +pub struct SmallDrain { + data: Vec, + index: usize, +} + +impl Iterator for SmallDrain { + type Item = T; + + fn next(&mut self) -> Option { + if self.index == self.data.len() { + None + } else { + let ptr = self.data.as_ptr(); + self.index += 1; + unsafe { Some(std::ptr::read(ptr.add(self.index - 1))) } + } + } +} + +pub struct SmallIntersection<'a, T> { + data: Vec<&'a T>, + index: usize, +} + +impl<'a, T> Iterator for SmallIntersection<'a, T> { + type Item = &'a T; + + fn next(&mut self) -> Option { + if self.index == self.data.len() { + None + } else { + let ptr = self.data.as_ptr(); + self.index += 1; + unsafe { Some(std::ptr::read(ptr.add(self.index - 1))) } + } + } +} + +pub struct SmallUnion<'a, T> { + data: Vec<&'a T>, + index: usize, +} + +impl<'a, T> Iterator for SmallUnion<'a, T> { + type Item = &'a T; + + fn next(&mut self) -> Option { + if self.index == self.data.len() { + None + } else { + let ptr = self.data.as_ptr(); + self.index += 1; + unsafe { Some(std::ptr::read(ptr.add(self.index - 1))) } + } + } +} + +pub struct SmallDifference<'a, T> { + data: Vec<&'a T>, + index: usize, +} + +impl<'a, T> Iterator for SmallDifference<'a, T> { + type Item = &'a T; + + fn next(&mut self) -> Option { + if self.index == self.data.len() { + None + } else { + let ptr = self.data.as_ptr(); + self.index += 1; + unsafe { Some(std::ptr::read(ptr.add(self.index - 1))) } + } + } +} + +pub struct SmallSymmetricDifference<'a, T> { + data: Vec<&'a T>, + index: usize, +} + +impl<'a, T> Iterator for SmallSymmetricDifference<'a, T> { + type Item = &'a T; + + fn next(&mut self) -> Option { + if self.index == self.data.len() { + None + } else { + let ptr = self.data.as_ptr(); + self.index += 1; + unsafe { Some(std::ptr::read(ptr.add(self.index - 1))) } + } } } @@ -106,7 +494,7 @@ where { fn clone(&self) -> SmallSet { SmallSet { - elements: self.elements.clone(), + inner: self.inner.clone(), } } } @@ -116,87 +504,53 @@ where A::Item: PartialEq + Eq + fmt::Debug, { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - self.elements.fmt(f) + match &self.inner { + InnerSmallVec::Stack(elements) => write!(f, "{:?}", elements.as_slice()), + InnerSmallVec::Heap(elements) => write!(f, "{:?}", elements), + } } } impl FromIterator for SmallSet where - A::Item: PartialEq + Eq, + A::Item: PartialEq + Eq + Hash, { fn from_iter(iter: T) -> Self where T: IntoIterator, { - SmallSet { - elements: SmallVec::from_iter(iter), - } - } -} - -#[cfg(test)] -mod test { - use super::*; - use std::fmt::Write; - - #[test] - fn test_basic_set() { - let mut s: SmallSet<[u32; 2]> = SmallSet::new(); - assert!(s.insert(1) == true); - assert!(s.insert(2) == true); - assert!(s.insert(2) == false); - assert!(s.insert(3) == true); - assert!(s.insert(2) == false); - assert!(s.insert(3) == false); - assert!(s.contains(&1)); - assert!(s.contains(&2)); - assert!(s.contains(&3)); - assert!(!s.contains(&4)); - assert!(s.len() == 3); - assert!(s.iter().map(|r| *r).collect::>() == vec![1, 2, 3]); - s.clear(); - assert!(!s.contains(&1)); - } - - #[test] - fn test_remove() { - let mut s: SmallSet<[u32; 2]> = SmallSet::new(); - assert!(s.insert(1) == true); - assert!(s.insert(2) == true); - assert!(s.len() == 2); - assert!(s.contains(&1)); - assert!(s.remove(&1) == true); - assert!(s.remove(&1) == false); - assert!(s.len() == 1); - assert!(!s.contains(&1)); - assert!(s.insert(1) == true); - assert!(s.iter().map(|r| *r).collect::>() == vec![2, 1]); - } - - #[test] - fn test_clone() { - let mut s: SmallSet<[u32; 2]> = SmallSet::new(); - s.insert(1); - s.insert(2); - let c = s.clone(); - assert!(c.contains(&1)); - assert!(c.contains(&2)); - assert!(!c.contains(&3)); - } - - #[test] - fn test_debug() { - let mut s: SmallSet<[u32; 2]> = SmallSet::new(); - s.insert(1); - s.insert(2); - let mut buf = String::new(); - write!(buf, "{:?}", s).unwrap(); - assert!(&buf == "[1, 2]"); - } - - #[test] - fn test_fromiter() { - let s: SmallSet<[usize; 4]> = vec![1, 2, 3, 4].into_iter().collect(); - assert!(s.len() == 4); + iter.into_iter().fold(SmallSet::new(), |mut acc, x| { + acc.insert(x); + acc + }) + } +} + +pub struct SmallIter<'a, A: Array> +where + A::Item: PartialEq + Eq + Hash + 'a, +{ + inner: InnerSmallIter<'a, A>, +} + +pub enum InnerSmallIter<'a, A: Array> +where + A::Item: PartialEq + Eq + Hash + 'a, +{ + Stack(std::slice::Iter<'a, A::Item>), + Heap(std::collections::hash_set::Iter<'a, A::Item>), +} + +impl<'a, A: Array> Iterator for SmallIter<'a, A> +where + A::Item: PartialEq + Eq + Hash + 'a, +{ + type Item = &'a A::Item; + + fn next(&mut self) -> Option { + match &mut self.inner { + InnerSmallIter::Stack(ref mut iter) => iter.next(), + InnerSmallIter::Heap(ref mut iter) => iter.next(), + } } } diff --git a/tests/test.rs b/tests/test.rs new file mode 100644 index 0000000..7d23d08 --- /dev/null +++ b/tests/test.rs @@ -0,0 +1,230 @@ +extern crate smallset; + +use smallset::SmallSet; +use std::fmt::Write; +use std::hash::{Hash, Hasher}; + +#[test] +fn test_basic_set() { + let mut s: SmallSet<[u32; 2]> = SmallSet::new(); + assert_eq!(s.insert(1), true); + assert_eq!(s.insert(2), true); + assert_eq!(s.insert(2), false); + assert_eq!(s.insert(3), true); + assert_eq!(s.insert(2), false); + assert_eq!(s.insert(3), false); + assert!(s.contains(&1)); + assert!(s.contains(&2)); + assert!(s.contains(&3)); + assert!(!s.contains(&4)); + assert_eq!(s.len(), 3); + assert!(s + .iter() + .map(|r| *r) + .collect::>() + .iter() + .all(|x| vec![1, 2, 3].contains(x))); + s.clear(); + assert!(!s.contains(&1)); +} + +#[test] +fn test_remove() { + let mut s: SmallSet<[u32; 2]> = SmallSet::new(); + assert_eq!(s.insert(1), true); + assert_eq!(s.insert(2), true); + assert_eq!(s.len(), 2); + assert!(s.contains(&1)); + assert_eq!(s.remove(&1), true); + assert_eq!(s.remove(&1), false); + assert_eq!(s.len(), 1); + assert!(!s.contains(&1)); + assert_eq!(s.insert(1), true); + assert!(s + .iter() + .map(|r| *r) + .collect::>() + .iter() + .all(|x| vec![1, 2, 3].contains(x))); +} + +#[test] +fn test_clone() { + let mut s: SmallSet<[u32; 2]> = SmallSet::new(); + s.insert(1); + s.insert(2); + let c = s.clone(); + assert!(c.contains(&1)); + assert!(c.contains(&2)); + assert!(!c.contains(&3)); +} + +#[test] +fn test_debug_small() { + let mut s: SmallSet<[u32; 2]> = SmallSet::new(); + s.insert(1); + s.insert(2); + let mut buf = String::new(); + write!(buf, "{:?}", s).unwrap(); + assert_eq!(&buf, "[1, 2]"); +} + +#[test] +fn test_from_iter() { + let s: SmallSet<[usize; 4]> = vec![1, 2, 3, 4].into_iter().collect(); + assert_eq!(s.len(), 4); +} + +#[test] +fn test_replace() { + struct RingOf7 { + pub value: u32, + } + + impl PartialEq for RingOf7 { + fn eq(&self, other: &Self) -> bool { + self.value % 7 == other.value % 7 + } + + fn ne(&self, other: &Self) -> bool { + self.value % 7 != other.value % 7 + } + } + + impl From for u32 { + fn from(value: RingOf7) -> Self { + value.value + } + } + + impl Hash for RingOf7 { + fn hash(&self, state: &mut H) { + self.value.hash(state) + } + } + + impl Eq for RingOf7 {} + + let mut lhs = SmallSet::<[RingOf7; 4]>::new(); + lhs.insert(RingOf7 { value: 1 }); + lhs.insert(RingOf7 { value: 2 }); + lhs.insert(RingOf7 { value: 3 }); + lhs.insert(RingOf7 { value: 4 }); + + lhs.replace(RingOf7 { value: 8 }); + lhs.replace(RingOf7 { value: 9 }); + lhs.replace(RingOf7 { value: 10 }); + lhs.replace(RingOf7 { value: 11 }); + + let expected = vec![8, 9, 10, 11]; + assert!(lhs + .iter() + .map(|x| x.value) + .collect::>() + .iter() + .zip(expected.iter()) + .all(|(lhs, rhs)| lhs == rhs)); +} + +#[test] +fn test_eq() { + let mut lhs = SmallSet::<[u32; 4]>::new(); + lhs.insert(1); + lhs.insert(2); + + let mut rhs = SmallSet::<[u32; 4]>::new(); + rhs.insert(1); + rhs.insert(2); + + assert_eq!(lhs, rhs); +} + +#[test] +fn test_intersection() { + let mut lhs = SmallSet::<[u32; 4]>::new(); + lhs.insert(1); + lhs.insert(3); + lhs.insert(5); + lhs.insert(4); + lhs.insert(8); + lhs.insert(10); + + let mut rhs = SmallSet::<[u32; 4]>::new(); + rhs.insert(4); + rhs.insert(8); + rhs.insert(10); + + assert!(lhs.intersection(&rhs).all(|x| x % 2 == 0)); +} + +#[test] +fn test_union() { + let mut lhs = SmallSet::<[u32; 4]>::new(); + lhs.insert(1); + lhs.insert(2); + lhs.insert(3); + lhs.insert(4); + + let mut rhs = SmallSet::<[u32; 4]>::new(); + rhs.insert(3); + rhs.insert(4); + rhs.insert(5); + rhs.insert(6); + + let union = lhs.union(&rhs).collect::>(); + let expected = vec![1, 2, 3, 4, 5, 6]; + assert_eq!(union.len(), expected.len()); + assert!(expected + .iter() + .collect::>() + .iter() + .all(|x| union.contains(x))); +} + +#[test] +fn test_difference() { + let mut lhs = SmallSet::<[u32; 4]>::new(); + lhs.insert(1); + lhs.insert(2); + lhs.insert(3); + lhs.insert(4); + + let mut rhs = SmallSet::<[u32; 4]>::new(); + rhs.insert(3); + rhs.insert(4); + rhs.insert(5); + rhs.insert(6); + + let union = lhs.difference(&rhs).collect::>(); + let expected = vec![1, 2]; + assert_eq!(union.len(), expected.len()); + assert!(expected + .iter() + .collect::>() + .iter() + .all(|x| union.contains(x))); +} + +#[test] +fn test_symmetric_difference() { + let mut lhs = SmallSet::<[u32; 4]>::new(); + lhs.insert(1); + lhs.insert(2); + lhs.insert(3); + lhs.insert(4); + + let mut rhs = SmallSet::<[u32; 4]>::new(); + rhs.insert(3); + rhs.insert(4); + rhs.insert(5); + rhs.insert(6); + + let symmetric_difference = lhs.symmetric_difference(&rhs).collect::>(); + let expected = vec![1, 2, 5, 6]; + assert_eq!(symmetric_difference.len(), expected.len()); + assert!(expected + .iter() + .collect::>() + .iter() + .all(|x| { symmetric_difference.contains(x) })); +} From b142cca411c4d08a8057eac5d5778115c5535af1 Mon Sep 17 00:00:00 2001 From: Hanif Bin Ariffin Date: Wed, 19 Aug 2020 07:20:14 -0400 Subject: [PATCH 2/6] Added documentation --- src/lib.rs | 30 ++++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index d36f9ba..4d5d345 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -38,7 +38,6 @@ use std::hash::Hash; /// assert_eq!(s.len(), 3); /// assert!(s.contains(&1)); /// ``` - pub struct SmallSet where A::Item: PartialEq + Eq, @@ -55,6 +54,8 @@ where } } +/// Internal (and true) representation of the `SmallSet`. +/// Created so that user are not aware of the sum type. pub enum InnerSmallVec where A::Item: PartialEq + Eq, @@ -116,6 +117,7 @@ where } } + /// Returns the number of elements in this set. pub fn is_empty(&self) -> bool { self.len() == 0 } @@ -203,14 +205,17 @@ where } } - // - pub fn get(&self, value: &A::Item) -> Option<&A::Item> { + /// If the given `elem` exists in the set, returns the reference to the value inside the set. + /// Where they are equal (in the case where the set is in stack mode) or they hash equally (if the set is in heap mode). + pub fn get(&self, elem: &A::Item) -> Option<&A::Item> { match &self.inner { - InnerSmallVec::Stack(elements) => elements.iter().find(|x| (value).eq(&x)), - InnerSmallVec::Heap(elements) => elements.iter().find(|x| (value).eq(&x)), + InnerSmallVec::Stack(elements) => elements.iter().find(|x| (elem).eq(&x)), + InnerSmallVec::Heap(elements) => elements.iter().find(|x| (elem).eq(&x)), } } + /// If the given `elem` exists in the set, returns the value inside the set where they are either equal or hash equally. + /// Then, remove that value from the set. pub fn take(&mut self, value: &A::Item) -> Option { match &mut self.inner { InnerSmallVec::Stack(ref mut elements) => { @@ -225,7 +230,7 @@ where } } - // Adds a value to the set, replacing the existing value, if any, that is equal to the given one. Returns the replaced value. + /// Adds a value to the set, replacing the existing value, if any, that is equal to the given one. Returns the replaced value. pub fn replace(&mut self, value: A::Item) -> Option { match &mut self.inner { InnerSmallVec::Stack(ref mut elements) => { @@ -241,6 +246,7 @@ where } } + /// Empties the set and returns an iterator over it. pub fn drain(&mut self) -> SmallDrain { match &mut self.inner { InnerSmallVec::Stack(ref mut elements) => { @@ -261,6 +267,7 @@ where } } + /// Removes all elements in the set that does not satisfy the given predicate `f`. pub fn retain(&mut self, f: F) where F: FnMut(&mut A::Item) -> bool + for<'r> FnMut(&'r ::Item) -> bool, @@ -271,6 +278,7 @@ where } } + /// Returns an iterator over the intersection of the 2 sets. pub fn intersection<'a>(&'a self, other: &'a Self) -> SmallIntersection<'a, A::Item> { match &self.inner { InnerSmallVec::Stack(ref elements) => { @@ -297,6 +305,7 @@ where } } + /// Returns an iterator over the union of the 2 sets. pub fn union<'a>(&'a self, other: &'a Self) -> SmallUnion<'a, A::Item> { match &self.inner { InnerSmallVec::Stack(ref elements) => { @@ -327,6 +336,7 @@ where } } + /// Returns an iterator over the difference of the 2 sets. pub fn difference<'a>(&'a self, other: &'a Self) -> SmallDifference<'a, A::Item> { match &self.inner { InnerSmallVec::Stack(ref elements) => { @@ -353,6 +363,7 @@ where } } + /// Returns an iterator over the symmetric difference of the 2 sets. pub fn symmetric_difference<'a>( &'a self, other: &'a Self, @@ -393,6 +404,7 @@ where } } +/// Iterator returned upon calling `drain`. pub struct SmallDrain { data: Vec, index: usize, @@ -412,6 +424,7 @@ impl Iterator for SmallDrain { } } +/// Iterator returned upon calling `intersection`. pub struct SmallIntersection<'a, T> { data: Vec<&'a T>, index: usize, @@ -431,6 +444,7 @@ impl<'a, T> Iterator for SmallIntersection<'a, T> { } } +/// Iterator returned upon calling `union`. pub struct SmallUnion<'a, T> { data: Vec<&'a T>, index: usize, @@ -450,6 +464,7 @@ impl<'a, T> Iterator for SmallUnion<'a, T> { } } +/// Iterator returned upon calling `difference`. pub struct SmallDifference<'a, T> { data: Vec<&'a T>, index: usize, @@ -469,6 +484,7 @@ impl<'a, T> Iterator for SmallDifference<'a, T> { } } +/// Iterator returned upon calling `symmteric_difference`. pub struct SmallSymmetricDifference<'a, T> { data: Vec<&'a T>, index: usize, @@ -526,6 +542,8 @@ where } } +/// Iterator of the set returned upon calling `iter`. +/// This is required to be an abstraction over the enum. pub struct SmallIter<'a, A: Array> where A::Item: PartialEq + Eq + Hash + 'a, From 8b47bff8e8f7b82bec9af55d9440b7da6e179f99 Mon Sep 17 00:00:00 2001 From: Hanif Ariffin Date: Wed, 19 Aug 2020 07:22:41 -0400 Subject: [PATCH 3/6] Create rust.yml --- .github/workflows/rust.yml | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 .github/workflows/rust.yml diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml new file mode 100644 index 0000000..3c13d1b --- /dev/null +++ b/.github/workflows/rust.yml @@ -0,0 +1,22 @@ +name: Rust + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +env: + CARGO_TERM_COLOR: always + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: Build + run: cargo build --verbose + - name: Run tests + run: cargo test --verbose From 2ecb06eb4532c1dda20a19dba2dc7accb7eae7fc Mon Sep 17 00:00:00 2001 From: Hanif Bin Ariffin Date: Wed, 19 Aug 2020 07:31:18 -0400 Subject: [PATCH 4/6] Updated Cargo.toml and the name of this fork. --- Cargo.toml | 14 ++--- src/lib.rs | 146 +++++++++++++++++++++++++------------------------- tests/test.rs | 36 ++++++------- 3 files changed, 99 insertions(+), 97 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 7431145..89df8af 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,10 +1,12 @@ [package] -name = "smallset" -version = "0.1.1" -authors = ["Chris Fallin "] -description = "An unordered set of elements optimized for small sizes" -repository = "https://github.com/cfallin/rust-smallset" -documentation = "https://cfallin.github.io/rust-smallset/smallset/" +name = "smolset" +version = "1.0.0" +authors = ["Chris Fallin ", "Hanif Bin Ariffin "] +description = """" +An unordered set of elements optimized for small sizes. +This is a fork of the original library with overhauled internals, better fallback perforamance (O(1) insert and find) and more features! +""" +repository = "https://github.com/hbina/smolset" license = "MIT" [dependencies] diff --git a/src/lib.rs b/src/lib.rs index 4d5d345..196e333 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -13,14 +13,14 @@ use smallvec::{Array, SmallVec}; use std::collections::HashSet; use std::hash::Hash; -/// A `SmallSet` is an unordered set of elements. It is designed to work best +/// A `SmolSet` is an unordered set of elements. It is designed to work best /// for very small sets (no more than ten or so elements). In order to support /// small sets very efficiently, it stores elements in a simple unordered array. /// When the set is smaller than the size of the array `A`, all elements are /// stored inline, without heap allocation. This is accomplished by using a /// `smallvec::SmallVec`. /// -/// The insert, remove, and query methods on `SmallSet` have `O(n)` time +/// The insert, remove, and query methods on `SmolSet` have `O(n)` time /// complexity in the current set size: they perform a linear scan to determine /// if the element in question is present. This is inefficient for large sets, /// but fast and cache-friendly for small sets. @@ -28,35 +28,35 @@ use std::hash::Hash; /// Example usage: /// /// ``` -/// use smallset::SmallSet; +/// use smallset::SmolSet; /// /// // `s` and its elements will be completely stack-allocated in this example. -/// let mut s: SmallSet<[u32; 4]> = SmallSet::new(); +/// let mut s: SmolSet<[u32; 4]> = SmolSet::new(); /// s.insert(1); /// s.insert(2); /// s.insert(3); /// assert_eq!(s.len(), 3); /// assert!(s.contains(&1)); /// ``` -pub struct SmallSet +pub struct SmolSet where A::Item: PartialEq + Eq, { - inner: InnerSmallVec, + inner: InnerSmolSet, } -impl Default for SmallSet +impl Default for SmolSet where A::Item: PartialEq + Eq + Hash, { fn default() -> Self { - SmallSet::new() + SmolSet::new() } } -/// Internal (and true) representation of the `SmallSet`. +/// Internal (and true) representation of the `SmolSet`. /// Created so that user are not aware of the sum type. -pub enum InnerSmallVec +pub enum InnerSmolSet where A::Item: PartialEq + Eq, { @@ -64,28 +64,28 @@ where Heap(std::collections::HashSet), } -impl Default for InnerSmallVec +impl Default for InnerSmolSet where A::Item: PartialEq + Eq, { fn default() -> Self { - InnerSmallVec::Stack(SmallVec::new()) + InnerSmolSet::Stack(SmallVec::new()) } } -impl Clone for InnerSmallVec +impl Clone for InnerSmolSet where A::Item: PartialEq + Eq + Clone, { fn clone(&self) -> Self { match &self { - InnerSmallVec::Stack(elements) => InnerSmallVec::Stack(elements.clone()), - InnerSmallVec::Heap(elements) => InnerSmallVec::Heap(elements.clone()), + InnerSmolSet::Stack(elements) => InnerSmolSet::Stack(elements.clone()), + InnerSmolSet::Heap(elements) => InnerSmolSet::Heap(elements.clone()), } } } -impl PartialEq for SmallSet +impl PartialEq for SmolSet where A::Item: Eq + PartialEq + Hash, { @@ -98,22 +98,22 @@ where } match (&self.inner, &other.inner) { - (InnerSmallVec::Stack(lhs), InnerSmallVec::Stack(rhs)) => lhs.eq(rhs), - (InnerSmallVec::Heap(lhs), InnerSmallVec::Heap(rhs)) => lhs.eq(rhs), - (InnerSmallVec::Stack(stack), InnerSmallVec::Heap(heap)) => set_same(stack, heap), - (InnerSmallVec::Heap(heap), InnerSmallVec::Stack(stack)) => set_same(stack, heap), + (InnerSmolSet::Stack(lhs), InnerSmolSet::Stack(rhs)) => lhs.eq(rhs), + (InnerSmolSet::Heap(lhs), InnerSmolSet::Heap(rhs)) => lhs.eq(rhs), + (InnerSmolSet::Stack(stack), InnerSmolSet::Heap(heap)) => set_same(stack, heap), + (InnerSmolSet::Heap(heap), InnerSmolSet::Stack(stack)) => set_same(stack, heap), } } } -impl SmallSet +impl SmolSet where A::Item: PartialEq + Eq + Hash, { - /// Creates a new, empty `SmallSet`. - pub fn new() -> SmallSet { - SmallSet { - inner: InnerSmallVec::Stack(SmallVec::new()), + /// Creates a new, empty `SmolSet`. + pub fn new() -> SmolSet { + SmolSet { + inner: InnerSmolSet::Stack(SmallVec::new()), } } @@ -127,7 +127,7 @@ where /// element present. pub fn insert(&mut self, elem: A::Item) -> bool { match &mut self.inner { - InnerSmallVec::Stack(ref mut elements) => { + InnerSmolSet::Stack(ref mut elements) => { if elements.contains(&elem) { false } else { @@ -139,12 +139,12 @@ where ee.insert(elements.remove(0)); } ee.insert(elem); - self.inner = InnerSmallVec::Heap(ee); + self.inner = InnerSmolSet::Heap(ee); } true } } - InnerSmallVec::Heap(ref mut elements) => elements.insert(elem), + InnerSmolSet::Heap(ref mut elements) => elements.insert(elem), } } @@ -152,7 +152,7 @@ where /// or `false` if it was not found. pub fn remove(&mut self, elem: &A::Item) -> bool { match &mut self.inner { - InnerSmallVec::Stack(ref mut elements) => { + InnerSmolSet::Stack(ref mut elements) => { if let Some(pos) = elements.iter().position(|e| *e == *elem) { elements.remove(pos); true @@ -160,7 +160,7 @@ where false } } - InnerSmallVec::Heap(ref mut elements) => elements.remove(elem), + InnerSmolSet::Heap(ref mut elements) => elements.remove(elem), } } @@ -168,20 +168,20 @@ where /// `false` if not. pub fn contains(&self, elem: &A::Item) -> bool { match &self.inner { - InnerSmallVec::Stack(ref elements) => elements.iter().any(|e| *e == *elem), - InnerSmallVec::Heap(ref elements) => elements.contains(elem), + InnerSmolSet::Stack(ref elements) => elements.iter().any(|e| *e == *elem), + InnerSmolSet::Heap(ref elements) => elements.contains(elem), } } /// Returns an iterator over the set elements. Elements will be returned in /// an arbitrary (unsorted) order. - pub fn iter(&self) -> SmallIter { + pub fn iter(&self) -> SmolSetIter { match &self.inner { - InnerSmallVec::Stack(element) => SmallIter { - inner: InnerSmallIter::Stack(element.iter()), + InnerSmolSet::Stack(element) => SmolSetIter { + inner: InnerSmolSetIter::Stack(element.iter()), }, - InnerSmallVec::Heap(element) => SmallIter { - inner: InnerSmallIter::Heap(element.iter()), + InnerSmolSet::Heap(element) => SmolSetIter { + inner: InnerSmolSetIter::Heap(element.iter()), }, } } @@ -189,16 +189,16 @@ where /// Returns the current length of the set. pub fn len(&self) -> usize { match &self.inner { - InnerSmallVec::Stack(elements) => elements.len(), - InnerSmallVec::Heap(elements) => elements.len(), + InnerSmolSet::Stack(elements) => elements.len(), + InnerSmolSet::Heap(elements) => elements.len(), } } /// Clears the set. pub fn clear(&mut self) { match &mut self.inner { - InnerSmallVec::Stack(ref mut elements) => elements.clear(), - InnerSmallVec::Heap(ref mut elements) => { + InnerSmolSet::Stack(ref mut elements) => elements.clear(), + InnerSmolSet::Heap(ref mut elements) => { elements.clear(); self.inner = Default::default(); } @@ -209,8 +209,8 @@ where /// Where they are equal (in the case where the set is in stack mode) or they hash equally (if the set is in heap mode). pub fn get(&self, elem: &A::Item) -> Option<&A::Item> { match &self.inner { - InnerSmallVec::Stack(elements) => elements.iter().find(|x| (elem).eq(&x)), - InnerSmallVec::Heap(elements) => elements.iter().find(|x| (elem).eq(&x)), + InnerSmolSet::Stack(elements) => elements.iter().find(|x| (elem).eq(&x)), + InnerSmolSet::Heap(elements) => elements.iter().find(|x| (elem).eq(&x)), } } @@ -218,7 +218,7 @@ where /// Then, remove that value from the set. pub fn take(&mut self, value: &A::Item) -> Option { match &mut self.inner { - InnerSmallVec::Stack(ref mut elements) => { + InnerSmolSet::Stack(ref mut elements) => { if let Some(pos) = elements.iter().position(|e| *e == *value) { let result = elements.remove(pos); Some(result) @@ -226,14 +226,14 @@ where None } } - InnerSmallVec::Heap(ref mut elements) => elements.take(value), + InnerSmolSet::Heap(ref mut elements) => elements.take(value), } } /// Adds a value to the set, replacing the existing value, if any, that is equal to the given one. Returns the replaced value. pub fn replace(&mut self, value: A::Item) -> Option { match &mut self.inner { - InnerSmallVec::Stack(ref mut elements) => { + InnerSmolSet::Stack(ref mut elements) => { if let Some(pos) = elements.iter().position(|e| *e == value) { let result = elements.remove(pos); elements.insert(pos, value); @@ -242,14 +242,14 @@ where None } } - InnerSmallVec::Heap(ref mut elements) => elements.replace(value), + InnerSmolSet::Heap(ref mut elements) => elements.replace(value), } } /// Empties the set and returns an iterator over it. pub fn drain(&mut self) -> SmallDrain { match &mut self.inner { - InnerSmallVec::Stack(ref mut elements) => { + InnerSmolSet::Stack(ref mut elements) => { // TODO: Clean up this garbage... let mut ee = Vec::::with_capacity(elements.len() + 1); while !elements.is_empty() { @@ -257,7 +257,7 @@ where } SmallDrain { data: ee, index: 0 } } - InnerSmallVec::Heap(ref mut elements) => { + InnerSmolSet::Heap(ref mut elements) => { let drain = elements.drain().collect::>(); SmallDrain { data: drain, @@ -273,15 +273,15 @@ where F: FnMut(&mut A::Item) -> bool + for<'r> FnMut(&'r ::Item) -> bool, { match &mut self.inner { - InnerSmallVec::Stack(ref mut elements) => elements.retain(f), - InnerSmallVec::Heap(ref mut elements) => elements.retain(f), + InnerSmolSet::Stack(ref mut elements) => elements.retain(f), + InnerSmolSet::Heap(ref mut elements) => elements.retain(f), } } /// Returns an iterator over the intersection of the 2 sets. pub fn intersection<'a>(&'a self, other: &'a Self) -> SmallIntersection<'a, A::Item> { match &self.inner { - InnerSmallVec::Stack(ref elements) => { + InnerSmolSet::Stack(ref elements) => { let result = elements .iter() .filter(|x| other.contains(x)) @@ -292,7 +292,7 @@ where } } - InnerSmallVec::Heap(ref elements) => { + InnerSmolSet::Heap(ref elements) => { let result = elements .iter() .filter(|x| other.contains(x)) @@ -308,7 +308,7 @@ where /// Returns an iterator over the union of the 2 sets. pub fn union<'a>(&'a self, other: &'a Self) -> SmallUnion<'a, A::Item> { match &self.inner { - InnerSmallVec::Stack(ref elements) => { + InnerSmolSet::Stack(ref elements) => { let mut lhs = elements.iter().collect::>(); let mut rhs = other .iter() @@ -321,7 +321,7 @@ where } } - InnerSmallVec::Heap(ref elements) => { + InnerSmolSet::Heap(ref elements) => { let mut lhs = elements.iter().collect::>(); let mut rhs = other .iter() @@ -339,7 +339,7 @@ where /// Returns an iterator over the difference of the 2 sets. pub fn difference<'a>(&'a self, other: &'a Self) -> SmallDifference<'a, A::Item> { match &self.inner { - InnerSmallVec::Stack(ref elements) => { + InnerSmolSet::Stack(ref elements) => { let lhs = elements .iter() .filter(|x| !other.contains(x)) @@ -350,7 +350,7 @@ where } } - InnerSmallVec::Heap(ref elements) => { + InnerSmolSet::Heap(ref elements) => { let lhs = elements .iter() .filter(|x| !other.contains(x)) @@ -369,7 +369,7 @@ where other: &'a Self, ) -> SmallSymmetricDifference<'a, A::Item> { match &self.inner { - InnerSmallVec::Stack(ref elements) => { + InnerSmolSet::Stack(ref elements) => { let mut lhs = elements .iter() .filter(|x| !other.contains(x)) @@ -385,7 +385,7 @@ where } } - InnerSmallVec::Heap(ref elements) => { + InnerSmolSet::Heap(ref elements) => { let mut lhs = elements .iter() .filter(|x| other.contains(x)) @@ -504,30 +504,30 @@ impl<'a, T> Iterator for SmallSymmetricDifference<'a, T> { } } -impl Clone for SmallSet +impl Clone for SmolSet where A::Item: PartialEq + Eq + Clone, { - fn clone(&self) -> SmallSet { - SmallSet { + fn clone(&self) -> SmolSet { + SmolSet { inner: self.inner.clone(), } } } -impl fmt::Debug for SmallSet +impl fmt::Debug for SmolSet where A::Item: PartialEq + Eq + fmt::Debug, { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match &self.inner { - InnerSmallVec::Stack(elements) => write!(f, "{:?}", elements.as_slice()), - InnerSmallVec::Heap(elements) => write!(f, "{:?}", elements), + InnerSmolSet::Stack(elements) => write!(f, "{:?}", elements.as_slice()), + InnerSmolSet::Heap(elements) => write!(f, "{:?}", elements), } } } -impl FromIterator for SmallSet +impl FromIterator for SmolSet where A::Item: PartialEq + Eq + Hash, { @@ -535,7 +535,7 @@ where where T: IntoIterator, { - iter.into_iter().fold(SmallSet::new(), |mut acc, x| { + iter.into_iter().fold(SmolSet::new(), |mut acc, x| { acc.insert(x); acc }) @@ -544,14 +544,14 @@ where /// Iterator of the set returned upon calling `iter`. /// This is required to be an abstraction over the enum. -pub struct SmallIter<'a, A: Array> +pub struct SmolSetIter<'a, A: Array> where A::Item: PartialEq + Eq + Hash + 'a, { - inner: InnerSmallIter<'a, A>, + inner: InnerSmolSetIter<'a, A>, } -pub enum InnerSmallIter<'a, A: Array> +pub enum InnerSmolSetIter<'a, A: Array> where A::Item: PartialEq + Eq + Hash + 'a, { @@ -559,7 +559,7 @@ where Heap(std::collections::hash_set::Iter<'a, A::Item>), } -impl<'a, A: Array> Iterator for SmallIter<'a, A> +impl<'a, A: Array> Iterator for SmolSetIter<'a, A> where A::Item: PartialEq + Eq + Hash + 'a, { @@ -567,8 +567,8 @@ where fn next(&mut self) -> Option { match &mut self.inner { - InnerSmallIter::Stack(ref mut iter) => iter.next(), - InnerSmallIter::Heap(ref mut iter) => iter.next(), + InnerSmolSetIter::Stack(ref mut iter) => iter.next(), + InnerSmolSetIter::Heap(ref mut iter) => iter.next(), } } } diff --git a/tests/test.rs b/tests/test.rs index 7d23d08..24d68c0 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -1,12 +1,12 @@ -extern crate smallset; +extern crate smolset; -use smallset::SmallSet; +use smolset::SmolSet; use std::fmt::Write; use std::hash::{Hash, Hasher}; #[test] fn test_basic_set() { - let mut s: SmallSet<[u32; 2]> = SmallSet::new(); + let mut s: SmolSet<[u32; 2]> = SmolSet::new(); assert_eq!(s.insert(1), true); assert_eq!(s.insert(2), true); assert_eq!(s.insert(2), false); @@ -30,7 +30,7 @@ fn test_basic_set() { #[test] fn test_remove() { - let mut s: SmallSet<[u32; 2]> = SmallSet::new(); + let mut s: SmolSet<[u32; 2]> = SmolSet::new(); assert_eq!(s.insert(1), true); assert_eq!(s.insert(2), true); assert_eq!(s.len(), 2); @@ -50,7 +50,7 @@ fn test_remove() { #[test] fn test_clone() { - let mut s: SmallSet<[u32; 2]> = SmallSet::new(); + let mut s: SmolSet<[u32; 2]> = SmolSet::new(); s.insert(1); s.insert(2); let c = s.clone(); @@ -61,7 +61,7 @@ fn test_clone() { #[test] fn test_debug_small() { - let mut s: SmallSet<[u32; 2]> = SmallSet::new(); + let mut s: SmolSet<[u32; 2]> = SmolSet::new(); s.insert(1); s.insert(2); let mut buf = String::new(); @@ -71,7 +71,7 @@ fn test_debug_small() { #[test] fn test_from_iter() { - let s: SmallSet<[usize; 4]> = vec![1, 2, 3, 4].into_iter().collect(); + let s: SmolSet<[usize; 4]> = vec![1, 2, 3, 4].into_iter().collect(); assert_eq!(s.len(), 4); } @@ -105,7 +105,7 @@ fn test_replace() { impl Eq for RingOf7 {} - let mut lhs = SmallSet::<[RingOf7; 4]>::new(); + let mut lhs = SmolSet::<[RingOf7; 4]>::new(); lhs.insert(RingOf7 { value: 1 }); lhs.insert(RingOf7 { value: 2 }); lhs.insert(RingOf7 { value: 3 }); @@ -128,11 +128,11 @@ fn test_replace() { #[test] fn test_eq() { - let mut lhs = SmallSet::<[u32; 4]>::new(); + let mut lhs = SmolSet::<[u32; 4]>::new(); lhs.insert(1); lhs.insert(2); - let mut rhs = SmallSet::<[u32; 4]>::new(); + let mut rhs = SmolSet::<[u32; 4]>::new(); rhs.insert(1); rhs.insert(2); @@ -141,7 +141,7 @@ fn test_eq() { #[test] fn test_intersection() { - let mut lhs = SmallSet::<[u32; 4]>::new(); + let mut lhs = SmolSet::<[u32; 4]>::new(); lhs.insert(1); lhs.insert(3); lhs.insert(5); @@ -149,7 +149,7 @@ fn test_intersection() { lhs.insert(8); lhs.insert(10); - let mut rhs = SmallSet::<[u32; 4]>::new(); + let mut rhs = SmolSet::<[u32; 4]>::new(); rhs.insert(4); rhs.insert(8); rhs.insert(10); @@ -159,13 +159,13 @@ fn test_intersection() { #[test] fn test_union() { - let mut lhs = SmallSet::<[u32; 4]>::new(); + let mut lhs = SmolSet::<[u32; 4]>::new(); lhs.insert(1); lhs.insert(2); lhs.insert(3); lhs.insert(4); - let mut rhs = SmallSet::<[u32; 4]>::new(); + let mut rhs = SmolSet::<[u32; 4]>::new(); rhs.insert(3); rhs.insert(4); rhs.insert(5); @@ -183,13 +183,13 @@ fn test_union() { #[test] fn test_difference() { - let mut lhs = SmallSet::<[u32; 4]>::new(); + let mut lhs = SmolSet::<[u32; 4]>::new(); lhs.insert(1); lhs.insert(2); lhs.insert(3); lhs.insert(4); - let mut rhs = SmallSet::<[u32; 4]>::new(); + let mut rhs = SmolSet::<[u32; 4]>::new(); rhs.insert(3); rhs.insert(4); rhs.insert(5); @@ -207,13 +207,13 @@ fn test_difference() { #[test] fn test_symmetric_difference() { - let mut lhs = SmallSet::<[u32; 4]>::new(); + let mut lhs = SmolSet::<[u32; 4]>::new(); lhs.insert(1); lhs.insert(2); lhs.insert(3); lhs.insert(4); - let mut rhs = SmallSet::<[u32; 4]>::new(); + let mut rhs = SmolSet::<[u32; 4]>::new(); rhs.insert(3); rhs.insert(4); rhs.insert(5); From dbf76ee8793bf2ae5ab9ee640b1e6c7dccbf5c3d Mon Sep 17 00:00:00 2001 From: Hanif Bin Ariffin Date: Wed, 19 Aug 2020 07:36:33 -0400 Subject: [PATCH 5/6] Updated README.md and fixed documentation code. Also bumped version to be able to push. --- Cargo.toml | 2 +- README.md | 31 +++++++++++++------------------ src/lib.rs | 2 +- 3 files changed, 15 insertions(+), 20 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 89df8af..9da0399 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "smolset" -version = "1.0.0" +version = "1.0.1" authors = ["Chris Fallin ", "Hanif Bin Ariffin "] description = """" An unordered set of elements optimized for small sizes. diff --git a/README.md b/README.md index 05c628a..5f55d41 100644 --- a/README.md +++ b/README.md @@ -1,23 +1,18 @@ -`smallset`: a small unordered set -================================= +# SmolSet -[![Build Status](https://travis-ci.org/cfallin/boolean_expression.svg?branch=master)](https://travis-ci.org/cfallin/rust-smallset) +[![Crate](https://img.shields.io/crates/v/smolset.svg)](https://crates.io/crates/smolset) -[crates.io](https://crates.io/crates/smallset/) +This crate implements a small unordered-set data structure implemented using +[smallvec](https://crates.io/crates/smallvec/). +It initially stores set elements in a simple unordered array. +When the set is smaller than a parameterizable size, no allocations will be performed. +The data structure is thus very space-efficient for sets of only a few elements, much more so than a tree-based or hash-table-based set data structure. +It is also fast when the set is small: queries and inserts perform a linear scan, which is more cache-friendly than a pointer-chasing search through a tree. -[Documentation](https://cfallin.github.io/rust-smallset/smallset/) +However, as the set grows, it will transform internally into a `std::collections::HashSet`. -This crate implements a small unordered-set data structure implemented using -[smallvec](https://crates.io/crates/smallvec/). It stores set elements in a -simple unordered array, and when the set is smaller than a parameterizable -size, the elements are stored completely inline (i.e., with zero heap -allocations). The data structure is thus very space-efficient for sets of only -a few elements, much more so than a tree-based or hash-table-based set data -structure. It is also fast when the set is small: queries and inserts perform -a linear scan, which is more cache-friendly than a pointer-chasing search -through a tree. -`smallset` should be used where minimizing heap allocations is of primary -importance and where it is expected that no more than a few elements will be -present. If the set grows large, then it will exhibit poor (`O(n)` queries and -inserts) performance. +## Note + +This is a fork of the original library here: [rust-smallset](https://github.com/cfallin/rust-smallset). +I have rewritten the internals completely to not have such a bad fallback mode and added more features (and their tests and documentations). diff --git a/src/lib.rs b/src/lib.rs index 196e333..3bab968 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -28,7 +28,7 @@ use std::hash::Hash; /// Example usage: /// /// ``` -/// use smallset::SmolSet; +/// use smolset::SmolSet; /// /// // `s` and its elements will be completely stack-allocated in this example. /// let mut s: SmolSet<[u32; 4]> = SmolSet::new(); From 7b8ecfdaa687dc07ca49f3be124a32eb907db9a5 Mon Sep 17 00:00:00 2001 From: Hanif Bin Ariffin Date: Wed, 19 Aug 2020 16:08:24 -0400 Subject: [PATCH 6/6] Added tests and ability to inquire current mode. --- Cargo.toml | 2 +- src/lib.rs | 16 ++++++++++++++++ tests/test.rs | 42 +++++++++++++++++++++++++++++++++++------- 3 files changed, 52 insertions(+), 8 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 9da0399..a3c72c9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "smolset" -version = "1.0.1" +version = "1.1.0" authors = ["Chris Fallin ", "Hanif Bin Ariffin "] description = """" An unordered set of elements optimized for small sizes. diff --git a/src/lib.rs b/src/lib.rs index 3bab968..13eb17b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -38,6 +38,9 @@ use std::hash::Hash; /// assert_eq!(s.len(), 3); /// assert!(s.contains(&1)); /// ``` +/// +/// TODO: Add the ability to switch modes explicitly. +/// pub struct SmolSet where A::Item: PartialEq + Eq, @@ -106,6 +109,12 @@ where } } +#[derive(PartialEq, Debug)] +pub enum SetMode { + Stack, + Heap, +} + impl SmolSet where A::Item: PartialEq + Eq + Hash, @@ -117,6 +126,13 @@ where } } + pub fn mode(&self) -> SetMode { + match self.inner { + InnerSmolSet::Stack(_) => SetMode::Stack, + InnerSmolSet::Heap(_) => SetMode::Heap, + } + } + /// Returns the number of elements in this set. pub fn is_empty(&self) -> bool { self.len() == 0 diff --git a/tests/test.rs b/tests/test.rs index 24d68c0..ed41160 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -1,8 +1,9 @@ extern crate smolset; -use smolset::SmolSet; +use smolset::{SetMode, SmolSet}; use std::fmt::Write; use std::hash::{Hash, Hasher}; +use std::iter::FromIterator; #[test] fn test_basic_set() { @@ -17,13 +18,14 @@ fn test_basic_set() { assert!(s.contains(&2)); assert!(s.contains(&3)); assert!(!s.contains(&4)); - assert_eq!(s.len(), 3); + let expected = vec![1, 2, 3]; + assert_eq!(s.len(), expected.len()); assert!(s .iter() .map(|r| *r) .collect::>() .iter() - .all(|x| vec![1, 2, 3].contains(x))); + .all(|x| expected.contains(x))); s.clear(); assert!(!s.contains(&1)); } @@ -40,12 +42,14 @@ fn test_remove() { assert_eq!(s.len(), 1); assert!(!s.contains(&1)); assert_eq!(s.insert(1), true); + let expected = vec![1, 2, 3]; + assert_eq!(s.len(), expected.len()); assert!(s .iter() .map(|r| *r) .collect::>() .iter() - .all(|x| vec![1, 2, 3].contains(x))); + .all(|x| expected.contains(x))); } #[test] @@ -117,17 +121,17 @@ fn test_replace() { lhs.replace(RingOf7 { value: 11 }); let expected = vec![8, 9, 10, 11]; + assert_eq!(lhs.len(), expected.len()); assert!(lhs .iter() .map(|x| x.value) .collect::>() .iter() - .zip(expected.iter()) - .all(|(lhs, rhs)| lhs == rhs)); + .all(|x| expected.contains(x))); } #[test] -fn test_eq() { +fn test_eq_both_stack() { let mut lhs = SmolSet::<[u32; 4]>::new(); lhs.insert(1); lhs.insert(2); @@ -139,6 +143,30 @@ fn test_eq() { assert_eq!(lhs, rhs); } +#[test] +fn test_eq_both_heap() { + let expected = (0..100).collect::>(); + let lhs = SmolSet::<[u32; 4]>::from_iter(expected.clone()); + let rhs = SmolSet::<[u32; 4]>::from_iter(expected.clone()); + + assert_eq!(lhs, rhs); +} + +#[test] +fn test_eq_stack_heap() { + let expected = (0..5).collect::>(); + let mut lhs = SmolSet::<[u32; 10]>::from_iter(expected.clone()); + let rhs = SmolSet::<[u32; 10]>::from_iter(expected.clone()); + + (100..200).for_each(|x| assert!(lhs.insert(x))); + (100..200).for_each(|x| assert!(lhs.remove(&x))); + + assert_eq!(lhs.mode(), SetMode::Heap); + assert_eq!(rhs.mode(), SetMode::Stack); + + assert_eq!(lhs, rhs); +} + #[test] fn test_intersection() { let mut lhs = SmolSet::<[u32; 4]>::new();