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}