skrifa/outline/glyf/hint/engine/
stack.rs

1//! Managing the stack and pushing data onto the interpreter stack.
2//!
3//! Implements 26 instructions.
4//!
5//! See <https://learn.microsoft.com/en-us/typography/opentype/spec/tt_instructions#managing-the-stack>
6//! and <https://learn.microsoft.com/en-us/typography/opentype/spec/tt_instructions#pushing-data-onto-the-interpreter-stack>
7
8use read_fonts::tables::glyf::bytecode::InlineOperands;
9
10use super::{Engine, OpResult};
11
12impl Engine<'_> {
13    /// Duplicate top stack element.
14    ///
15    /// DUP[] (0x20)
16    ///
17    /// Pops: e
18    /// Pushes: e, e
19    ///
20    /// Duplicates the element at the top of the stack.
21    ///
22    /// See <https://learn.microsoft.com/en-us/typography/opentype/spec/tt_instructions#duplicate-top-stack-element>
23    /// and <https://gitlab.freedesktop.org/freetype/freetype/-/blob/57617782464411201ce7bbc93b086c1b4d7d84a5/src/truetype/ttinterp.c#L2650>
24    pub(super) fn op_dup(&mut self) -> OpResult {
25        self.value_stack.dup()
26    }
27
28    /// Pop top stack element.
29    ///
30    /// POP[] (0x21)
31    ///
32    /// Pops: e
33    ///
34    /// Pops the top element of the stack.
35    ///
36    /// See <https://learn.microsoft.com/en-us/typography/opentype/spec/tt_instructions#pop-top-stack-element>
37    /// and <https://gitlab.freedesktop.org/freetype/freetype/-/blob/57617782464411201ce7bbc93b086c1b4d7d84a5/src/truetype/ttinterp.c#L2663>
38    pub(super) fn op_pop(&mut self) -> OpResult {
39        self.value_stack.pop()?;
40        Ok(())
41    }
42
43    /// Clear the entire stack.
44    ///
45    /// CLEAR[] (0x22)
46    ///
47    /// Pops: all the items on the stack
48    ///
49    /// Clears all elements from the stack.
50    ///
51    /// See <https://learn.microsoft.com/en-us/typography/opentype/spec/tt_instructions#clear-the-entire-stack>
52    /// and <https://gitlab.freedesktop.org/freetype/freetype/-/blob/57617782464411201ce7bbc93b086c1b4d7d84a5/src/truetype/ttinterp.c#L2676>
53    pub(super) fn op_clear(&mut self) -> OpResult {
54        self.value_stack.clear();
55        Ok(())
56    }
57
58    /// Swap the top two elements on the stack.
59    ///
60    /// SWAP[] (0x23)
61    ///
62    /// Pops: e2, e1
63    /// Pushes: e1, e2
64    ///
65    /// Swaps the top two elements of the stack making the old top element the
66    /// second from the top and the old second element the top element.
67    ///
68    /// See <https://learn.microsoft.com/en-us/typography/opentype/spec/tt_instructions#swap-the-top-two-elements-on-the-stack>
69    /// and <https://gitlab.freedesktop.org/freetype/freetype/-/blob/57617782464411201ce7bbc93b086c1b4d7d84a5/src/truetype/ttinterp.c#L2689>
70    pub(super) fn op_swap(&mut self) -> OpResult {
71        self.value_stack.swap()
72    }
73
74    /// Returns the depth of the stack.
75    ///
76    /// DEPTH[] (0x24)
77    ///
78    /// Pushes: n; number of elements
79    ///
80    /// Pushes n, the number of elements currently in the stack onto the stack.
81    ///
82    /// See <https://learn.microsoft.com/en-us/typography/opentype/spec/tt_instructions#returns-the-depth-of-the-stack>
83    /// and <https://gitlab.freedesktop.org/freetype/freetype/-/blob/57617782464411201ce7bbc93b086c1b4d7d84a5/src/truetype/ttinterp.c#L2707>
84    pub(super) fn op_depth(&mut self) -> OpResult {
85        let n = self.value_stack.len();
86        self.value_stack.push(n as i32)
87    }
88
89    /// Copy the indexed element to the top of the stack.
90    ///
91    /// CINDEX[] (0x25)
92    ///
93    /// Pops: k: stack element number
94    /// Pushes: ek: indexed element
95    ///
96    /// Puts a copy of the kth stack element on the top of the stack.
97    ///
98    /// See <https://learn.microsoft.com/en-us/typography/opentype/spec/tt_instructions#copy-the-indexed-element-to-the-top-of-the-stack>
99    /// and <https://gitlab.freedesktop.org/freetype/freetype/-/blob/57617782464411201ce7bbc93b086c1b4d7d84a5/src/truetype/ttinterp.c#L3232>
100    pub(super) fn op_cindex(&mut self) -> OpResult {
101        self.value_stack.copy_index()
102    }
103
104    /// Move the indexed element to the top of the stack.
105    ///
106    /// MINDEX[] (0x26)
107    ///
108    /// Pops: k: stack element number
109    /// Pushes: ek: indexed element
110    ///
111    /// Moves the indexed element to the top of the stack.
112    ///
113    /// See <https://learn.microsoft.com/en-us/typography/opentype/spec/tt_instructions#move-the-indexed-element-to-the-top-of-the-stack>
114    /// and <https://gitlab.freedesktop.org/freetype/freetype/-/blob/57617782464411201ce7bbc93b086c1b4d7d84a5/src/truetype/ttinterp.c#L3199>
115    pub(super) fn op_mindex(&mut self) -> OpResult {
116        self.value_stack.move_index()
117    }
118
119    /// Roll the top three stack elements.
120    ///
121    /// ROLL[] (0x8a)
122    ///
123    /// Pops: a, b, c (top three stack elements)
124    /// Pushes: b, a, c (elements reordered)
125    ///
126    /// Performs a circular shift of the top three objects on the stack with
127    /// the effect being to move the third element to the top of the stack
128    /// and to move the first two elements down one position. ROLL is
129    /// equivalent to MINDEX[] 3.
130    ///
131    /// See <https://learn.microsoft.com/en-us/typography/opentype/spec/tt_instructions#roll-the-top-three-stack-elements>
132    /// and <https://gitlab.freedesktop.org/freetype/freetype/-/blob/57617782464411201ce7bbc93b086c1b4d7d84a5/src/truetype/ttinterp.c#L3258>
133    pub(super) fn op_roll(&mut self) -> OpResult {
134        self.value_stack.roll()
135    }
136
137    /// Push data onto the interpreter stack.
138    ///
139    /// NPUSHB[] (0x8a)
140    ///
141    /// Takes n unsigned bytes from the instruction stream, where n is an
142    /// unsigned integer in the range (0..255), and pushes them onto the stack.
143    /// n itself is not pushed onto the stack.
144    ///
145    /// NPUSHW[] (0x41)
146    ///
147    /// Takes n 16-bit signed words from the instruction stream, where n is an
148    /// unsigned integer in the range (0..255), and pushes them onto the stack.
149    /// n itself is not pushed onto the stack.
150    ///
151    /// PUSHB\[abc\] (0xB0 - 0xB7)
152    ///
153    /// Takes the specified number of bytes from the instruction stream and
154    /// pushes them onto the interpreter stack.
155    /// The variables a, b, and c are binary digits representing numbers from
156    /// 000 to 111 (0-7 in binary). Because the actual number of bytes (n) is
157    /// from 1 to 8, 1 is automatically added to the ABC figure to obtain the
158    /// actual number of bytes pushed.
159    ///
160    /// PUSHW\[abc\] (0xB8 - 0xBF)
161    ///
162    /// Takes the specified number of words from the instruction stream and
163    /// pushes them onto the interpreter stack.
164    /// The variables a, b, and c are binary digits representing numbers from
165    /// 000 to 111 (0-7 binary). Because the actual number of bytes (n) is from
166    /// 1 to 8, 1 is automatically added to the abc figure to obtain the actual
167    /// number of bytes pushed.
168    ///
169    /// See <https://learn.microsoft.com/en-us/typography/opentype/spec/tt_instructions#pushing-data-onto-the-interpreter-stack>
170    /// and <https://gitlab.freedesktop.org/freetype/freetype/-/blob/57617782464411201ce7bbc93b086c1b4d7d84a5/src/truetype/ttinterp.c#L3858>
171    pub(super) fn op_push(&mut self, operands: &InlineOperands) -> OpResult {
172        self.value_stack.push_inline_operands(operands)
173    }
174}
175
176#[cfg(test)]
177mod tests {
178    use super::super::MockEngine;
179    use read_fonts::tables::glyf::bytecode::MockInlineOperands;
180
181    #[test]
182    fn stack_ops() {
183        let mut mock = MockEngine::new();
184        let mut engine = mock.engine();
185        let byte_args = MockInlineOperands::from_bytes(&[2, 4, 6, 8]);
186        let word_args = MockInlineOperands::from_words(&[-2000, 4000, -6000, 8000]);
187        let initial_stack = byte_args
188            .operands()
189            .values()
190            .chain(word_args.operands().values())
191            .collect::<Vec<_>>();
192        // Push instructions
193        engine.op_push(&byte_args.operands()).unwrap();
194        engine.op_push(&word_args.operands()).unwrap();
195        assert_eq!(engine.value_stack.values(), initial_stack);
196        // DEPTH[]
197        engine.op_depth().unwrap();
198        assert_eq!(
199            engine.value_stack.pop().ok(),
200            Some(initial_stack.len() as i32)
201        );
202        // POP[]
203        engine.op_pop().unwrap();
204        engine.op_pop().unwrap();
205        assert_eq!(
206            engine.value_stack.values(),
207            &initial_stack[..initial_stack.len() - 2]
208        );
209        // SWAP[]
210        engine.op_swap().unwrap();
211        assert_eq!(&engine.value_stack.values()[4..], &[4000, -2000]);
212        // ROLL[]
213        engine.op_roll().unwrap();
214        assert_eq!(&engine.value_stack.values()[3..], &[4000, -2000, 8]);
215        // CINDEX[]
216        engine.value_stack.push(4).unwrap();
217        engine.op_cindex().unwrap();
218        assert_eq!(engine.value_stack.peek(), Some(6));
219        // MINDEX[]
220        engine.value_stack.push(3).unwrap();
221        engine.op_mindex().unwrap();
222        assert_eq!(engine.value_stack.peek(), Some(-2000));
223    }
224}