skrifa/outline/glyf/hint/engine/storage.rs
1//! Managing the storage area.
2//!
3//! Implements 2 instructions.
4//!
5//! See <https://learn.microsoft.com/en-us/typography/opentype/spec/tt_instructions#managing-the-storage-area>
6
7use super::{Engine, OpResult};
8
9impl Engine<'_> {
10 /// Read store.
11 ///
12 /// RS[] (0x43)
13 ///
14 /// Pops: location: Storage Area location
15 /// Pushes: value: Storage Area value
16 ///
17 /// This instruction reads a 32 bit value from the Storage Area location
18 /// popped from the stack and pushes the value read onto the stack. It pops
19 /// an address from the stack and pushes the value found in that Storage
20 /// Area location to the top of the stack. The number of available storage
21 /// locations is specified in the maxProfile table in the font file.
22 ///
23 /// See <https://learn.microsoft.com/en-us/typography/opentype/spec/tt_instructions#read-store>
24 /// and <https://gitlab.freedesktop.org/freetype/freetype/-/blob/57617782464411201ce7bbc93b086c1b4d7d84a5/src/truetype/ttinterp.c#L2975>
25 pub(super) fn op_rs(&mut self) -> OpResult {
26 let location = self.value_stack.pop_usize()?;
27 let maybe_value = self.storage.get(location);
28 let value = if self.graphics.is_pedantic {
29 maybe_value?
30 } else {
31 maybe_value.unwrap_or(0)
32 };
33 self.value_stack.push(value)
34 }
35
36 /// Write store.
37 ///
38 /// WS[] (0x42)
39 ///
40 /// Pops: value: Storage Area value,
41 /// location: Storage Area location
42 ///
43 /// This instruction writes a 32 bit value into the storage location
44 /// indexed by locations. It works by popping a value and then a location
45 /// from the stack. The value is placed in the Storage Area location
46 /// specified by that address. The number of storage locations is specified
47 /// in the maxProfile table in the font file.
48 ///
49 /// See <https://learn.microsoft.com/en-us/typography/opentype/spec/tt_instructions#write-store>
50 /// and <https://gitlab.freedesktop.org/freetype/freetype/-/blob/57617782464411201ce7bbc93b086c1b4d7d84a5/src/truetype/ttinterp.c#L3000>
51 pub(super) fn op_ws(&mut self) -> OpResult {
52 let value = self.value_stack.pop()?;
53 let location = self.value_stack.pop_usize()?;
54 let result = self.storage.set(location, value);
55 if self.graphics.is_pedantic {
56 result
57 } else {
58 Ok(())
59 }
60 }
61}
62
63#[cfg(test)]
64mod tests {
65 use super::super::{HintErrorKind, MockEngine};
66
67 #[test]
68 fn write_read() {
69 let mut mock = MockEngine::new();
70 let mut engine = mock.engine();
71 for i in 0..8 {
72 engine.value_stack.push(i).unwrap();
73 engine.value_stack.push(i * 2).unwrap();
74 engine.op_ws().unwrap();
75 }
76 for i in 0..8 {
77 engine.value_stack.push(i).unwrap();
78 engine.op_rs().unwrap();
79 assert_eq!(engine.value_stack.pop().unwrap(), i * 2);
80 }
81 }
82
83 #[test]
84 fn pedantry() {
85 let mut mock = MockEngine::new();
86 let mut engine = mock.engine();
87 let oob_index = 1000;
88 // Disable pedantic mode: OOB writes are ignored, OOB reads
89 // push 0
90 engine.graphics.is_pedantic = false;
91 engine.value_stack.push(oob_index).unwrap();
92 engine.value_stack.push(0).unwrap();
93 engine.op_ws().unwrap();
94 engine.value_stack.push(oob_index).unwrap();
95 engine.op_rs().unwrap();
96 // Enable pedantic mode: OOB reads/writes error
97 engine.graphics.is_pedantic = true;
98 engine.value_stack.push(oob_index).unwrap();
99 engine.value_stack.push(0).unwrap();
100 assert_eq!(
101 engine.op_ws(),
102 Err(HintErrorKind::InvalidStorageIndex(oob_index as _))
103 );
104 engine.value_stack.push(oob_index).unwrap();
105 assert_eq!(
106 engine.op_rs(),
107 Err(HintErrorKind::InvalidStorageIndex(oob_index as _))
108 );
109 }
110}