skrifa/outline/glyf/hint/
call_stack.rsuse super::{definition::Definition, error::HintErrorKind, program::Program};
const MAX_DEPTH: usize = 32;
#[derive(Copy, Clone, Default)]
pub struct CallRecord {
pub caller_program: Program,
pub return_pc: usize,
pub current_count: u32,
pub definition: Definition,
}
#[derive(Default)]
pub struct CallStack {
records: [CallRecord; MAX_DEPTH],
len: usize,
}
impl CallStack {
pub fn clear(&mut self) {
self.len = 0;
}
pub fn push(&mut self, record: CallRecord) -> Result<(), HintErrorKind> {
let top = self
.records
.get_mut(self.len)
.ok_or(HintErrorKind::CallStackOverflow)?;
*top = record;
self.len += 1;
Ok(())
}
pub fn peek(&self) -> Option<&CallRecord> {
self.records.get(self.len.checked_sub(1)?)
}
pub fn pop(&mut self) -> Result<CallRecord, HintErrorKind> {
let record = *self.peek().ok_or(HintErrorKind::CallStackUnderflow)?;
self.len -= 1;
Ok(record)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn stack_overflow() {
let mut stack = CallStack::default();
for i in 0..MAX_DEPTH {
stack.push(record_with_key(i)).unwrap();
}
assert!(matches!(
stack.push(CallRecord::default()),
Err(HintErrorKind::CallStackOverflow)
));
}
#[test]
fn stack_underflow() {
assert!(matches!(
CallStack::default().pop(),
Err(HintErrorKind::CallStackUnderflow)
));
}
#[test]
fn stack_push_pop() {
let mut stack = CallStack::default();
for i in 0..MAX_DEPTH {
stack.push(record_with_key(i)).unwrap();
}
for i in (0..MAX_DEPTH).rev() {
assert_eq!(stack.pop().unwrap().definition.key(), i as i32);
}
}
fn record_with_key(key: usize) -> CallRecord {
CallRecord {
caller_program: Program::Glyph,
return_pc: 0,
current_count: 1,
definition: Definition::new(Program::Font, 0..0, key as i32),
}
}
}