Skip to main content

smallset/
lib.rs

1// smallset: a Rust crate for small unordered sets of elements, built on top of
2// `smallvec`.
3//
4// Copyright (c) 2016 Chris Fallin <cfallin@c1f.net>. Released under the MIT license.
5//
6
7use std::fmt;
8use std::iter::{FromIterator, IntoIterator};
9use std::slice::Iter;
10
11extern crate smallvec;
12use smallvec::{Array, SmallVec};
13
14/// A `SmallSet` is an unordered set of elements. It is designed to work best
15/// for very small sets (no more than ten or so elements). In order to support
16/// small sets very efficiently, it stores elements in a simple unordered array.
17/// When the set is smaller than the size of the array `A`, all elements are
18/// stored inline, without heap allocation. This is accomplished by using a
19/// `smallvec::SmallVec`.
20///
21/// The insert, remove, and query methods on `SmallSet` have `O(n)` time
22/// complexity in the current set size: they perform a linear scan to determine
23/// if the element in question is present. This is inefficient for large sets,
24/// but fast and cache-friendly for small sets.
25///
26/// Example usage:
27///
28/// ```
29/// use smallset::SmallSet;
30///
31/// // `s` and its elements will be completely stack-allocated in this example.
32/// let mut s: SmallSet<[u32; 4]> = SmallSet::new();
33/// s.insert(1);
34/// s.insert(2);
35/// s.insert(3);
36/// assert!(s.len() == 3);
37/// assert!(s.contains(&1));
38/// ```
39pub struct SmallSet<A: Array>
40where
41    A::Item: PartialEq + Eq,
42{
43    elements: SmallVec<A>,
44}
45
46impl<A: Array> SmallSet<A>
47where
48    A::Item: PartialEq + Eq,
49{
50    /// Creates a new, empty `SmallSet`.
51    pub fn new() -> SmallSet<A> {
52        SmallSet {
53            elements: SmallVec::new(),
54        }
55    }
56
57    /// Inserts `elem` into the set if not yet present. Returns `true` if the
58    /// set did not have this element present, or `false` if it already had this
59    /// element present.
60    pub fn insert(&mut self, elem: A::Item) -> bool {
61        if !self.contains(&elem) {
62            self.elements.push(elem);
63            true
64        } else {
65            false
66        }
67    }
68
69    /// Removes `elem` from the set. Returns `true` if the element was removed,
70    /// or `false` if it was not found.
71    pub fn remove(&mut self, elem: &A::Item) -> bool {
72        if let Some(pos) = self.elements.iter().position(|e| *e == *elem) {
73            self.elements.remove(pos);
74            true
75        } else {
76            false
77        }
78    }
79
80    /// Tests whether `elem` is present. Returns `true` if it is present, or
81    /// `false` if not.
82    pub fn contains(&self, elem: &A::Item) -> bool {
83        self.elements.iter().any(|e| *e == *elem)
84    }
85
86    /// Returns an iterator over the set elements. Elements will be returned in
87    /// an arbitrary (unsorted) order.
88    pub fn iter(&self) -> Iter<A::Item> {
89        self.elements.iter()
90    }
91
92    /// Returns the current length of the set.
93    pub fn len(&self) -> usize {
94        self.elements.len()
95    }
96
97    /// Clears the set.
98    pub fn clear(&mut self) {
99        self.elements.clear();
100    }
101}
102
103impl<A: Array> Clone for SmallSet<A>
104where
105    A::Item: PartialEq + Eq + Clone,
106{
107    fn clone(&self) -> SmallSet<A> {
108        SmallSet {
109            elements: self.elements.clone(),
110        }
111    }
112}
113
114impl<A: Array> fmt::Debug for SmallSet<A>
115where
116    A::Item: PartialEq + Eq + fmt::Debug,
117{
118    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
119        self.elements.fmt(f)
120    }
121}
122
123impl<A: Array> FromIterator<A::Item> for SmallSet<A>
124where
125    A::Item: PartialEq + Eq,
126{
127    fn from_iter<T>(iter: T) -> Self
128    where
129        T: IntoIterator<Item = A::Item>,
130    {
131        SmallSet {
132            elements: SmallVec::from_iter(iter),
133        }
134    }
135}
136
137#[cfg(test)]
138mod test {
139    use super::*;
140    use std::fmt::Write;
141
142    #[test]
143    fn test_basic_set() {
144        let mut s: SmallSet<[u32; 2]> = SmallSet::new();
145        assert!(s.insert(1) == true);
146        assert!(s.insert(2) == true);
147        assert!(s.insert(2) == false);
148        assert!(s.insert(3) == true);
149        assert!(s.insert(2) == false);
150        assert!(s.insert(3) == false);
151        assert!(s.contains(&1));
152        assert!(s.contains(&2));
153        assert!(s.contains(&3));
154        assert!(!s.contains(&4));
155        assert!(s.len() == 3);
156        assert!(s.iter().map(|r| *r).collect::<Vec<u32>>() == vec![1, 2, 3]);
157        s.clear();
158        assert!(!s.contains(&1));
159    }
160
161    #[test]
162    fn test_remove() {
163        let mut s: SmallSet<[u32; 2]> = SmallSet::new();
164        assert!(s.insert(1) == true);
165        assert!(s.insert(2) == true);
166        assert!(s.len() == 2);
167        assert!(s.contains(&1));
168        assert!(s.remove(&1) == true);
169        assert!(s.remove(&1) == false);
170        assert!(s.len() == 1);
171        assert!(!s.contains(&1));
172        assert!(s.insert(1) == true);
173        assert!(s.iter().map(|r| *r).collect::<Vec<u32>>() == vec![2, 1]);
174    }
175
176    #[test]
177    fn test_clone() {
178        let mut s: SmallSet<[u32; 2]> = SmallSet::new();
179        s.insert(1);
180        s.insert(2);
181        let c = s.clone();
182        assert!(c.contains(&1));
183        assert!(c.contains(&2));
184        assert!(!c.contains(&3));
185    }
186
187    #[test]
188    fn test_debug() {
189        let mut s: SmallSet<[u32; 2]> = SmallSet::new();
190        s.insert(1);
191        s.insert(2);
192        let mut buf = String::new();
193        write!(buf, "{:?}", s).unwrap();
194        assert!(&buf == "[1, 2]");
195    }
196
197    #[test]
198    fn test_fromiter() {
199        let s: SmallSet<[usize; 4]> = vec![1, 2, 3, 4].into_iter().collect();
200        assert!(s.len() == 4);
201    }
202}