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