use std::mem::MaybeUninit;
pub(crate) struct Stack<T, const N: usize> {
buf: [MaybeUninit<T>; N],
len: usize,
overflowed: bool,
}
impl<T, const N: usize> Stack<T, N> {
pub(crate) fn new() -> Self {
Stack {
buf: [const { MaybeUninit::uninit() }; N],
len: 0,
overflowed: false,
}
}
pub(crate) fn push(&mut self, v: T) {
match self.buf.get_mut(self.len) {
Some(slot) => {
slot.write(v);
self.len += 1;
}
None => self.overflowed = true,
}
}
pub(crate) fn pop(&mut self) -> Option<T> {
let len = self.len.checked_sub(1)?;
self.len = len;
Some(unsafe { self.buf[len].assume_init_read() })
}
pub(crate) fn truncate(&mut self, len: usize) {
let old = self.len;
let len = len.min(old);
self.len = len;
for slot in &mut self.buf[len..old] {
unsafe { slot.assume_init_drop() }
}
}
pub(crate) fn take_top(&mut self, n: usize) -> Vec<T> {
let old = self.len;
let base = old.saturating_sub(n);
self.len = base;
self.buf[base..old]
.iter()
.map(|slot| unsafe { slot.assume_init_read() })
.collect()
}
pub(crate) fn into_only(mut self) -> Option<T> {
if self.len == 1 { self.pop() } else { None }
}
pub(crate) fn overflowed(&self) -> bool {
self.overflowed
}
}
impl<T, const N: usize> Drop for Stack<T, N> {
fn drop(&mut self) {
self.truncate(0);
}
}
impl<T, const N: usize> std::ops::Deref for Stack<T, N> {
type Target = [T];
fn deref(&self) -> &[T] {
unsafe { std::slice::from_raw_parts(self.buf.as_ptr().cast::<T>(), self.len) }
}
}
impl<T, const N: usize> std::ops::DerefMut for Stack<T, N> {
fn deref_mut(&mut self) -> &mut [T] {
unsafe { std::slice::from_raw_parts_mut(self.buf.as_mut_ptr().cast::<T>(), self.len) }
}
}