skrifa/outline/glyf/hint/
program.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
//! 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)
        }
    }
}