//! TrueType program management.
use raw::tables::glyf::bytecode::Decoder;
use super::{
call_stack::{CallRecord, CallStack},
definition::Definition,
error::HintErrorKind,
};
/// Describes the source for a piece of bytecode.
#[derive(Copy, Clone, PartialEq, Eq, Default, Debug)]
#[repr(u8)]
pub enum Program {
/// Program that initializes the function and instruction tables. Stored
/// in the `fpgm` table.
#[default]
Font = 0,
/// Program that initializes CVT and storage based on font size and other
/// parameters. Stored in the `prep` table.
ControlValue = 1,
/// Glyph specified program. Stored per-glyph in the `glyf` table.
Glyph = 2,
}
/// State for managing active programs and decoding instructions.
pub struct ProgramState<'a> {
/// Bytecode for each of the three program types, indexed by `Program`.
pub bytecode: [&'a [u8]; 3],
/// The initial program when execution begins.
pub initial: Program,
/// The currently active program.
pub current: Program,
/// Instruction decoder for the currently active program.
pub decoder: Decoder<'a>,
/// Tracks nested function and instruction invocations.
pub call_stack: CallStack,
}
impl<'a> ProgramState<'a> {
pub fn new(
font_code: &'a [u8],
cv_code: &'a [u8],
glyph_code: &'a [u8],
initial_program: Program,
) -> Self {
let bytecode = [font_code, cv_code, glyph_code];
Self {
bytecode,
initial: initial_program,
current: initial_program,
decoder: Decoder::new(bytecode[initial_program as usize], 0),
call_stack: CallStack::default(),
}
}
/// Resets the state for execution of the given program.
pub fn reset(&mut self, program: Program) {
self.initial = program;
self.current = program;
self.decoder = Decoder::new(self.bytecode[program as usize], 0);
self.call_stack.clear();
}
/// Jumps to the code in the given definition and sets it up for
/// execution `count` times.
pub fn enter(&mut self, definition: Definition, count: u32) -> Result<(), HintErrorKind> {
let program = definition.program();
let pc = definition.code_range().start;
let bytecode = self.bytecode[program as usize];
self.call_stack.push(CallRecord {
caller_program: self.current,
return_pc: self.decoder.pc,
current_count: count,
definition,
})?;
self.current = program;
self.decoder = Decoder::new(bytecode, pc);
Ok(())
}
/// Leaves the code from the definition on the top of the stack.
///
/// If the top call record has a loop count greater than 1, restarts
/// execution from the beginning of the definition. Otherwise, resumes
/// execution at the previously active definition.
pub fn leave(&mut self) -> Result<(), HintErrorKind> {
let mut record = self.call_stack.pop()?;
if record.current_count > 1 {
// This is a loop call with some iterations remaining.
record.current_count -= 1;
self.decoder.pc = record.definition.code_range().start;
self.call_stack.push(record)?;
} else {
self.current = record.caller_program;
// Reset the decoder to the calling program and program counter.
self.decoder.bytecode = self.bytecode[record.caller_program as usize];
self.decoder.pc = record.return_pc;
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
/// Test accounting of program, bytecode and program counter through
/// enter/leave cycles.
#[test]
fn accounting() {
let font_code = &[0][..];
let cv_code = &[1][..];
let glyph_code = &[2][..];
let mut state = ProgramState::new(font_code, cv_code, glyph_code, Program::Glyph);
// We start at glyph code
assert_eq!(state.active_state(), (Program::Glyph, glyph_code, 0));
let font_def = Definition::new(Program::Font, 10..20, 0);
let cv_def = Definition::new(Program::ControlValue, 33..111, 1);
// Now move to CV code
state.enter(cv_def, 1).unwrap();
assert_eq!(state.active_state(), (Program::ControlValue, cv_code, 33));
// Bump the program counter to test capture of return_pc
state.decoder.pc += 20;
// And to font code
state.enter(font_def, 1).unwrap();
assert_eq!(state.active_state(), (Program::Font, font_code, 10));
// Back to CV code
state.leave().unwrap();
assert_eq!(state.active_state(), (Program::ControlValue, cv_code, 53));
// And to the original glyph code
state.leave().unwrap();
assert_eq!(state.active_state(), (Program::Glyph, glyph_code, 0));
}
/// Ensure calls with a count of `n` require `n` leaves before returning
/// to previous frame. Also ensure program counter is reset to start of
/// definition at each leave.
#[test]
fn loop_call() {
let font_code = &[0][..];
let cv_code = &[1][..];
let glyph_code = &[2][..];
let mut state = ProgramState::new(font_code, cv_code, glyph_code, Program::Glyph);
let font_def = Definition::new(Program::Font, 10..20, 0);
// "Execute" font definition 3 times
state.enter(font_def, 3).unwrap();
for _ in 0..3 {
assert_eq!(state.active_state(), (Program::Font, font_code, 10));
// Modify program counter to ensure we reset on leave
state.decoder.pc += 22;
state.leave().unwrap();
}
// Should be back to glyph code
assert_eq!(state.active_state(), (Program::Glyph, glyph_code, 0));
}
impl<'a> ProgramState<'a> {
fn active_state(&self) -> (Program, &'a [u8], usize) {
(self.current, self.decoder.bytecode, self.decoder.pc)
}
}
}