| #![cfg(feature = "experimental_array_set")] | |
| // This was contributed by user `dhardy`! Big thanks. | |
| use super::{take, Array}; | |
| use core::{ | |
| borrow::Borrow, | |
| fmt, | |
| mem::swap, | |
| ops::{AddAssign, SubAssign}, | |
| }; | |
| /// Error resulting from attempting to insert into a full array | |
| #[derive(Copy, Clone, Debug, PartialEq, Eq)] | |
| pub struct InsertError; | |
| // TODO(when std): impl std::error::Error for InsertError {} | |
| impl fmt::Display for InsertError { | |
| fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | |
| write!(f, "ArraySet: insertion failed") | |
| } | |
| } | |
| /// An array-backed set | |
| /// | |
| /// This set supports `O(n)` operations and has a fixed size, thus may fail to | |
| /// insert items. The potential advantage is a *really* small size. | |
| /// | |
| /// The set is backed by an array of type `A` and indexed by type `L`. | |
| /// The item type must support `Default`. | |
| /// Due to restrictions, `L` may be only `u8` or `u16`. | |
| #[derive(Clone, Debug, Default)] | |
| pub struct ArraySet<A: Array, L> { | |
| arr: A, | |
| len: L, | |
| } | |
| impl<A: Array + Default, L: From<u8>> ArraySet<A, L> { | |
| /// Constructs a new, empty, set | |
| #[inline] | |
| pub fn new() -> Self { | |
| ArraySet { arr: Default::default(), len: 0.into() } | |
| } | |
| } | |
| impl<A: Array, L: Copy + Into<usize>> ArraySet<A, L> { | |
| /// Constructs a new set from given inputs | |
| /// | |
| /// Panics if `len> arr.len()`. | |
| #[inline] | |
| pub fn from(arr: A, len: L) -> Self { | |
| if len.into() > A::CAPACITY { | |
| panic!("ArraySet::from(array, len): len > array.len()"); | |
| } | |
| ArraySet { arr, len } | |
| } | |
| } | |
| impl<A: Array, L> ArraySet<A, L> | |
| where | |
| L: Copy + PartialEq + From<u8> + Into<usize>, | |
| { | |
| /// Returns the fixed capacity of the set | |
| #[inline] | |
| pub fn capacity(&self) -> usize { | |
| A::CAPACITY | |
| } | |
| /// Returns the number of elements in the set | |
| #[inline] | |
| pub fn len(&self) -> usize { | |
| self.len.into() | |
| } | |
| /// Returns true when the set contains no elements | |
| #[inline] | |
| pub fn is_empty(&self) -> bool { | |
| self.len == 0.into() | |
| } | |
| /// Removes all elements | |
| #[inline] | |
| pub fn clear(&mut self) { | |
| self.len = 0.into(); | |
| } | |
| /// Iterate over all contents | |
| #[inline] | |
| pub fn iter(&self) -> Iter<A::Item> { | |
| Iter { a: self.arr.as_slice(), i: 0 } | |
| } | |
| } | |
| impl<A: Array, L> ArraySet<A, L> | |
| where | |
| L: Copy + PartialOrd + AddAssign + SubAssign + From<u8> + Into<usize>, | |
| { | |
| /// Check whether the set contains `elt` | |
| #[inline] | |
| pub fn contains<Q: Eq + ?Sized>(&self, elt: &Q) -> bool | |
| where | |
| A::Item: Borrow<Q>, | |
| { | |
| self.get(elt).is_some() | |
| } | |
| /// Get a reference to a contained item matching `elt` | |
| pub fn get<Q: Eq + ?Sized>(&self, elt: &Q) -> Option<&A::Item> | |
| where | |
| A::Item: Borrow<Q>, | |
| { | |
| let len: usize = self.len.into(); | |
| let arr = self.arr.as_slice(); | |
| for i in 0..len { | |
| if arr[i].borrow() == elt { | |
| return Some(&arr[i]); | |
| } | |
| } | |
| None | |
| } | |
| /// Remove an item matching `elt`, if any | |
| pub fn remove<Q: Eq + ?Sized>(&mut self, elt: &Q) -> Option<A::Item> | |
| where | |
| A::Item: Borrow<Q>, | |
| { | |
| let len: usize = self.len.into(); | |
| let arr = self.arr.as_slice_mut(); | |
| for i in 0..len { | |
| if arr[i].borrow() == elt { | |
| let l1 = len - 1; | |
| if i < l1 { | |
| arr.swap(i, l1); | |
| } | |
| self.len -= L::from(1); | |
| return Some(take(&mut arr[l1])); | |
| } | |
| } | |
| None | |
| } | |
| /// Remove any items for which `f(item) == false` | |
| pub fn retain<F>(&mut self, mut f: F) | |
| where | |
| F: FnMut(&A::Item) -> bool, | |
| { | |
| let mut len = self.len; | |
| let arr = self.arr.as_slice_mut(); | |
| let mut i = 0; | |
| while i < len.into() { | |
| if !f(&arr[i]) { | |
| len -= L::from(1); | |
| if i < len.into() { | |
| arr.swap(i, len.into()); | |
| } | |
| } else { | |
| i += 1; | |
| } | |
| } | |
| self.len = len; | |
| } | |
| } | |
| impl<A: Array, L> ArraySet<A, L> | |
| where | |
| A::Item: Eq, | |
| L: Copy + PartialOrd + AddAssign + SubAssign + From<u8> + Into<usize>, | |
| { | |
| /// Insert an item | |
| /// | |
| /// Due to the fixed size of the backing array, insertion may fail. | |
| #[inline] | |
| pub fn insert(&mut self, elt: A::Item) -> Result<bool, InsertError> { | |
| if self.contains(&elt) { | |
| return Ok(false); | |
| } | |
| let len = self.len.into(); | |
| let arr = self.arr.as_slice_mut(); | |
| if len >= arr.len() { | |
| return Err(InsertError); | |
| } | |
| arr[len] = elt; | |
| self.len += L::from(1); | |
| Ok(true) | |
| } | |
| /* Hits borrow checker | |
| pub fn get_or_insert(&mut self, elt: A::Item) -> Result<&A::Item, InsertError> { | |
| if let Some(r) = self.get(&elt) { | |
| return Ok(r); | |
| } | |
| self.insert(elt)?; | |
| let len: usize = self.len.into(); | |
| Ok(&self.arr.as_slice()[len - 1]) | |
| } | |
| */ | |
| /// Replace an item matching `elt` with `elt`, or insert `elt` | |
| /// | |
| /// Returns the replaced item, if any. Fails when there is no matching item | |
| /// and the backing array is full, preventing insertion. | |
| pub fn replace( | |
| &mut self, | |
| mut elt: A::Item, | |
| ) -> Result<Option<A::Item>, InsertError> { | |
| let len: usize = self.len.into(); | |
| let arr = self.arr.as_slice_mut(); | |
| for i in 0..len { | |
| if arr[i] == elt { | |
| swap(&mut arr[i], &mut elt); | |
| return Ok(Some(elt)); | |
| } | |
| } | |
| if len >= arr.len() { | |
| return Err(InsertError); | |
| } | |
| arr[len] = elt; | |
| self.len += L::from(1); | |
| Ok(None) | |
| } | |
| } | |
| /// Type returned by [`ArraySet::iter`] | |
| pub struct Iter<'a, T> { | |
| a: &'a [T], | |
| i: usize, | |
| } | |
| impl<'a, T> ExactSizeIterator for Iter<'a, T> { | |
| #[inline] | |
| fn len(&self) -> usize { | |
| self.a.len() - self.i | |
| } | |
| } | |
| impl<'a, T> Iterator for Iter<'a, T> { | |
| type Item = &'a T; | |
| #[inline] | |
| fn next(&mut self) -> Option<Self::Item> { | |
| if self.i < self.a.len() { | |
| let i = self.i; | |
| self.i += 1; | |
| Some(&self.a[i]) | |
| } else { | |
| None | |
| } | |
| } | |
| #[inline] | |
| fn size_hint(&self) -> (usize, Option<usize>) { | |
| let len = self.len(); | |
| (len, Some(len)) | |
| } | |
| } | |
| #[cfg(test)] | |
| mod test { | |
| use super::*; | |
| use core::mem::size_of; | |
| #[test] | |
| fn test_size() { | |
| assert_eq!(size_of::<ArraySet<[i8; 7], u8>>(), 8); | |
| } | |
| #[test] | |
| fn test() { | |
| let mut set: ArraySet<[i8; 7], u8> = ArraySet::new(); | |
| assert_eq!(set.capacity(), 7); | |
| assert_eq!(set.insert(1), Ok(true)); | |
| assert_eq!(set.insert(5), Ok(true)); | |
| assert_eq!(set.insert(6), Ok(true)); | |
| assert_eq!(set.len(), 3); | |
| assert_eq!(set.insert(5), Ok(false)); | |
| assert_eq!(set.len(), 3); | |
| assert_eq!(set.replace(1), Ok(Some(1))); | |
| assert_eq!(set.replace(2), Ok(None)); | |
| assert_eq!(set.len(), 4); | |
| assert_eq!(set.insert(3), Ok(true)); | |
| assert_eq!(set.insert(4), Ok(true)); | |
| assert_eq!(set.insert(7), Ok(true)); | |
| assert_eq!(set.insert(8), Err(InsertError)); | |
| assert_eq!(set.len(), 7); | |
| assert_eq!(set.replace(9), Err(InsertError)); | |
| assert_eq!(set.remove(&3), Some(3)); | |
| assert_eq!(set.len(), 6); | |
| set.retain(|x| *x == 3 || *x == 6); | |
| assert_eq!(set.len(), 1); | |
| assert!(!set.contains(&3)); | |
| assert!(set.contains(&6)); | |
| } | |
| } |