use super::{NonEmptyStack, Stack};
pub struct SparseStack<T> {
has_values: NonEmptyStack<bool>,
values: Stack<T>,
}
impl<T> Default for SparseStack<T> {
fn default() -> Self {
Self::new()
}
}
impl<T> SparseStack<T> {
pub const MAX_FILLED_CAPACITY: usize = Stack::<T>::MAX_CAPACITY;
pub const MAX_TOTAL_CAPACITY: usize = NonEmptyStack::<bool>::MAX_CAPACITY;
pub fn new() -> Self {
Self { has_values: NonEmptyStack::new(false), values: Stack::new() }
}
#[inline]
pub fn with_capacity(total_capacity: usize, filled_capacity: usize) -> Self {
Self {
has_values: NonEmptyStack::with_capacity(total_capacity, false),
values: Stack::with_capacity(filled_capacity),
}
}
#[inline]
pub fn push(&mut self, value: Option<T>) {
let has_value = match value {
Some(value) => {
self.values.push(value);
true
}
_ => false,
};
self.has_values.push(has_value);
}
#[inline]
pub fn pop(&mut self) -> Option<T> {
let has_value = self.has_values.pop();
if has_value {
debug_assert!(!self.values.is_empty());
let value = unsafe { self.values.pop_unchecked() };
Some(value)
} else {
None
}
}
#[inline]
pub fn first(&self) -> Option<&T> {
let has_value = *self.has_values.first();
if has_value {
debug_assert!(!self.values.is_empty());
let value = unsafe { self.values.get_unchecked(0) };
Some(value)
} else {
None
}
}
#[inline]
pub fn first_mut(&mut self) -> Option<&mut T> {
let has_value = *self.has_values.first();
if has_value {
debug_assert!(!self.values.is_empty());
let value = unsafe { self.values.get_unchecked_mut(0) };
Some(value)
} else {
None
}
}
#[inline]
pub fn last(&self) -> Option<&T> {
let has_value = *self.has_values.last();
if has_value {
debug_assert!(!self.values.is_empty());
let value = unsafe { self.values.last_unchecked() };
Some(value)
} else {
None
}
}
#[inline]
pub fn last_mut(&mut self) -> Option<&mut T> {
let has_value = *self.has_values.last();
if has_value {
debug_assert!(!self.values.is_empty());
let value = unsafe { self.values.last_unchecked_mut() };
Some(value)
} else {
None
}
}
#[inline]
pub fn take_last(&mut self) -> Option<T> {
let has_value = self.has_values.last_mut();
if *has_value {
*has_value = false;
debug_assert!(!self.values.is_empty());
let value = unsafe { self.values.pop_unchecked() };
Some(value)
} else {
None
}
}
#[inline]
pub fn last_or_init<I: FnOnce() -> T>(&mut self, init: I) -> &T {
let has_value = self.has_values.last_mut();
if !*has_value {
*has_value = true;
self.values.push(init());
}
debug_assert!(!self.values.is_empty());
unsafe { self.values.last_unchecked() }
}
#[inline]
pub fn last_mut_or_init<I: FnOnce() -> T>(&mut self, init: I) -> &mut T {
let has_value = self.has_values.last_mut();
if !*has_value {
*has_value = true;
self.values.push(init());
}
debug_assert!(!self.values.is_empty());
unsafe { self.values.last_unchecked_mut() }
}
#[inline]
#[expect(clippy::len_without_is_empty)] pub fn len(&self) -> usize {
self.has_values.len()
}
#[inline]
pub fn is_exhausted(&self) -> bool {
self.has_values.is_exhausted()
}
#[inline]
pub fn filled_len(&self) -> usize {
self.values.len()
}
#[inline]
pub fn total_capacity(&self) -> usize {
self.has_values.capacity()
}
#[inline]
pub fn filled_capacity(&self) -> usize {
self.values.capacity()
}
#[inline]
pub fn as_slice(&self) -> &[T] {
self.values.as_slice()
}
#[inline]
pub fn as_mut_slice(&mut self) -> &mut [T] {
self.values.as_mut_slice()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn is_exhausted() {
let mut stack = SparseStack::<u64>::new();
assert!(stack.is_exhausted());
stack.push(None);
assert!(!stack.is_exhausted());
stack.push(None);
assert!(!stack.is_exhausted());
stack.pop();
assert!(!stack.is_exhausted());
stack.pop();
assert!(stack.is_exhausted());
stack.push(Some(10));
assert!(!stack.is_exhausted());
stack.push(Some(20));
assert!(!stack.is_exhausted());
stack.pop();
assert!(!stack.is_exhausted());
stack.pop();
assert!(stack.is_exhausted());
}
}