From efe06e423eb614bc685905737af8e6db56978a77 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Fri, 1 May 2020 14:45:21 +0100 Subject: [PATCH] Implement ArraySet --- src/arrayset.rs | 291 ++++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 3 + 2 files changed, 294 insertions(+) create mode 100644 src/arrayset.rs diff --git a/src/arrayset.rs b/src/arrayset.rs new file mode 100644 index 0000000..130bfab --- /dev/null +++ b/src/arrayset.rs @@ -0,0 +1,291 @@ +use core::borrow::Borrow; +use core::fmt; +use core::mem::swap; +use core::ops::{AddAssign, SubAssign}; +use super::Array; + +/// 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); + let mut r = Default::default(); + swap(&mut arr[l1], &mut r); + return Some(r); + } + } + 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)); + } +} diff --git a/src/lib.rs b/src/lib.rs index 31afc9d..f200109 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -88,6 +88,9 @@ pub extern crate alloc; mod array; pub use array::*; +mod arrayset; +pub use arrayset::*; + mod arrayvec; pub use arrayvec::*;