use std::marker::PhantomData;
use sim_lib_control::WorkLimit;
use crate::ValueWidthPolicy;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum StackError {
Overflow {
depth: usize,
width: usize,
limit: usize,
},
Underflow {
depth: usize,
},
ZeroWidth {
depth: usize,
},
}
pub struct UnitStack<P: ValueWidthPolicy> {
pub(crate) values: Vec<P::Value>,
pub(crate) depth: usize,
pub(crate) limit: WorkLimit,
_policy: PhantomData<P>,
}
impl<P: ValueWidthPolicy> UnitStack<P> {
pub fn new(limit: WorkLimit) -> Self {
Self {
values: Vec::new(),
depth: 0,
limit,
_policy: PhantomData,
}
}
pub fn depth(&self) -> usize {
self.depth
}
pub fn is_empty(&self) -> bool {
self.values.is_empty()
}
pub fn top(&self) -> Result<&P::Value, StackError> {
self.values
.last()
.ok_or(StackError::Underflow { depth: self.depth })
}
pub fn visit_values(&self, mut visit: impl FnMut(&P::Value)) {
for value in &self.values {
visit(value);
}
}
pub fn push(&mut self, value: P::Value) -> Result<(), StackError> {
let width = P::width(&value);
if width == 0 {
return Err(StackError::ZeroWidth { depth: self.depth });
}
let next = self
.depth
.checked_add(width)
.filter(|next| *next <= self.limit.0)
.ok_or(StackError::Overflow {
depth: self.depth,
width,
limit: self.limit.0,
})?;
self.values.push(value);
self.depth = next;
Ok(())
}
pub fn pop(&mut self) -> Result<P::Value, StackError> {
let value = self
.values
.pop()
.ok_or(StackError::Underflow { depth: self.depth })?;
self.depth -= P::width(&value);
Ok(value)
}
pub fn clear(&mut self) {
while self.pop().is_ok() {}
}
}