//! Tracking function call state. use super::{definition::Definition, error::HintErrorKind, program::Program}; // FreeType provides a call stack with a depth of 32. // See const MAX_DEPTH: usize = 32; /// Record of an active invocation of a function or instruction /// definition. /// /// See #[derive(Copy, Clone, Default)] pub struct CallRecord { pub caller_program: Program, pub return_pc: usize, pub current_count: u32, pub definition: Definition, } /// Tracker for nested active function or instruction calls. #[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 { 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), } } }