#![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 { arr: A, len: L, } impl> ArraySet { /// Constructs a new, empty, set #[inline] pub fn new() -> Self { ArraySet { arr: Default::default(), len: 0.into() } } } impl> ArraySet { /// 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 ArraySet where L: Copy + PartialEq + From + Into, { /// 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 { Iter { a: self.arr.as_slice(), i: 0 } } } impl ArraySet where L: Copy + PartialOrd + AddAssign + SubAssign + From + Into, { /// Check whether the set contains `elt` #[inline] pub fn contains(&self, elt: &Q) -> bool where A::Item: Borrow, { self.get(elt).is_some() } /// Get a reference to a contained item matching `elt` pub fn get(&self, elt: &Q) -> Option<&A::Item> where A::Item: Borrow, { 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(&mut self, elt: &Q) -> Option where A::Item: Borrow, { 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(&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 ArraySet where A::Item: Eq, L: Copy + PartialOrd + AddAssign + SubAssign + From + Into, { /// 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 { 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, 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 { 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) { 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::>(), 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)); } }