#![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)); | |
} | |
} |