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