pub(crate) const MAX_DEPTH: usize = 1024;
#[derive(Debug, Clone)]
pub(crate) struct GraphicsStateStack<T> {
entries: Vec<T>,
dropped: usize,
}
impl<T> Default for GraphicsStateStack<T> {
fn default() -> Self {
Self {
entries: Vec::new(),
dropped: 0,
}
}
}
impl<T> GraphicsStateStack<T> {
pub(crate) fn push_with(&mut self, capture: impl FnOnce() -> T) {
if self.entries.len() < MAX_DEPTH {
self.entries.push(capture());
} else {
self.dropped = self.dropped.saturating_add(1);
}
}
pub(crate) fn pop(&mut self) -> Option<T> {
if self.dropped > 0 {
self.dropped -= 1;
return None;
}
self.entries.pop()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn a_balanced_unwind_past_the_cap_pairs_every_push_with_its_own_pop() {
let overflow = 7;
let mut stack = GraphicsStateStack::default();
for level in 0..MAX_DEPTH + overflow {
stack.push_with(|| level);
}
for _ in 0..overflow {
assert_eq!(
stack.pop(),
None,
"a pop closing a dropped push must restore nothing"
);
}
for level in (0..MAX_DEPTH).rev() {
assert_eq!(stack.pop(), Some(level));
}
assert_eq!(stack.pop(), None, "the stack is empty after a full unwind");
}
#[test]
fn extra_pops_are_ignored_and_do_not_corrupt_the_count() {
let mut stack = GraphicsStateStack::default();
stack.push_with(|| 1);
for _ in 0..MAX_DEPTH + 3 {
let _ = stack.pop();
}
stack.push_with(|| 2);
assert_eq!(
stack.pop(),
Some(2),
"after over-popping, a fresh push must still be the next thing restored"
);
}
#[test]
fn a_refused_push_never_builds_its_snapshot() {
let mut captures = 0usize;
let mut stack = GraphicsStateStack::default();
for _ in 0..MAX_DEPTH + 500 {
stack.push_with(|| {
captures += 1;
});
}
assert_eq!(
captures, MAX_DEPTH,
"only the pushes that fit may build a snapshot"
);
}
}