1//! TrueType program management.
23use raw::tables::glyf::bytecode::Decoder;
45use super::{
6 call_stack::{CallRecord, CallStack},
7 definition::Definition,
8 error::HintErrorKind,
9};
1011/// Describes the source for a piece of bytecode.
12#[derive(Copy, Clone, PartialEq, Eq, Default, Debug)]
13#[repr(u8)]
14pub enum Program {
15/// Program that initializes the function and instruction tables. Stored
16 /// in the `fpgm` table.
17#[default]
18Font = 0,
19/// Program that initializes CVT and storage based on font size and other
20 /// parameters. Stored in the `prep` table.
21ControlValue = 1,
22/// Glyph specified program. Stored per-glyph in the `glyf` table.
23Glyph = 2,
24}
2526/// State for managing active programs and decoding instructions.
27pub struct ProgramState<'a> {
28/// Bytecode for each of the three program types, indexed by `Program`.
29pub bytecode: [&'a [u8]; 3],
30/// The initial program when execution begins.
31pub initial: Program,
32/// The currently active program.
33pub current: Program,
34/// Instruction decoder for the currently active program.
35pub decoder: Decoder<'a>,
36/// Tracks nested function and instruction invocations.
37pub call_stack: CallStack,
38}
3940impl<'a> ProgramState<'a> {
41pub fn new(
42 font_code: &'a [u8],
43 cv_code: &'a [u8],
44 glyph_code: &'a [u8],
45 initial_program: Program,
46 ) -> Self {
47let bytecode = [font_code, cv_code, glyph_code];
48Self {
49 bytecode,
50 initial: initial_program,
51 current: initial_program,
52 decoder: Decoder::new(bytecode[initial_program as usize], 0),
53 call_stack: CallStack::default(),
54 }
55 }
5657/// Resets the state for execution of the given program.
58pub fn reset(&mut self, program: Program) {
59self.initial = program;
60self.current = program;
61self.decoder = Decoder::new(self.bytecode[program as usize], 0);
62self.call_stack.clear();
63 }
6465/// Jumps to the code in the given definition and sets it up for
66 /// execution `count` times.
67pub fn enter(&mut self, definition: Definition, count: u32) -> Result<(), HintErrorKind> {
68let program = definition.program();
69let pc = definition.code_range().start;
70let bytecode = self.bytecode[program as usize];
71self.call_stack.push(CallRecord {
72 caller_program: self.current,
73 return_pc: self.decoder.pc,
74 current_count: count,
75 definition,
76 })?;
77self.current = program;
78self.decoder = Decoder::new(bytecode, pc);
79Ok(())
80 }
8182/// Leaves the code from the definition on the top of the stack.
83 ///
84 /// If the top call record has a loop count greater than 1, restarts
85 /// execution from the beginning of the definition. Otherwise, resumes
86 /// execution at the previously active definition.
87pub fn leave(&mut self) -> Result<(), HintErrorKind> {
88let mut record = self.call_stack.pop()?;
89if record.current_count > 1 {
90// This is a loop call with some iterations remaining.
91record.current_count -= 1;
92self.decoder.pc = record.definition.code_range().start;
93self.call_stack.push(record)?;
94 } else {
95self.current = record.caller_program;
96// Reset the decoder to the calling program and program counter.
97self.decoder.bytecode = self.bytecode[record.caller_program as usize];
98self.decoder.pc = record.return_pc;
99 }
100Ok(())
101 }
102}
103104#[cfg(test)]
105mod tests {
106use super::*;
107108/// Test accounting of program, bytecode and program counter through
109 /// enter/leave cycles.
110#[test]
111fn accounting() {
112let font_code = &[0][..];
113let cv_code = &[1][..];
114let glyph_code = &[2][..];
115let mut state = ProgramState::new(font_code, cv_code, glyph_code, Program::Glyph);
116// We start at glyph code
117assert_eq!(state.active_state(), (Program::Glyph, glyph_code, 0));
118let font_def = Definition::new(Program::Font, 10..20, 0);
119let cv_def = Definition::new(Program::ControlValue, 33..111, 1);
120// Now move to CV code
121state.enter(cv_def, 1).unwrap();
122assert_eq!(state.active_state(), (Program::ControlValue, cv_code, 33));
123// Bump the program counter to test capture of return_pc
124state.decoder.pc += 20;
125// And to font code
126state.enter(font_def, 1).unwrap();
127assert_eq!(state.active_state(), (Program::Font, font_code, 10));
128// Back to CV code
129state.leave().unwrap();
130assert_eq!(state.active_state(), (Program::ControlValue, cv_code, 53));
131// And to the original glyph code
132state.leave().unwrap();
133assert_eq!(state.active_state(), (Program::Glyph, glyph_code, 0));
134 }
135136/// Ensure calls with a count of `n` require `n` leaves before returning
137 /// to previous frame. Also ensure program counter is reset to start of
138 /// definition at each leave.
139#[test]
140fn loop_call() {
141let font_code = &[0][..];
142let cv_code = &[1][..];
143let glyph_code = &[2][..];
144let mut state = ProgramState::new(font_code, cv_code, glyph_code, Program::Glyph);
145let font_def = Definition::new(Program::Font, 10..20, 0);
146// "Execute" font definition 3 times
147state.enter(font_def, 3).unwrap();
148for _ in 0..3 {
149assert_eq!(state.active_state(), (Program::Font, font_code, 10));
150// Modify program counter to ensure we reset on leave
151state.decoder.pc += 22;
152 state.leave().unwrap();
153 }
154// Should be back to glyph code
155assert_eq!(state.active_state(), (Program::Glyph, glyph_code, 0));
156 }
157158impl<'a> ProgramState<'a> {
159fn active_state(&self) -> (Program, &'a [u8], usize) {
160 (self.current, self.decoder.bytecode, self.decoder.pc)
161 }
162 }
163}