read_fonts/tables/glyf/bytecode/
instruction.rs

1/// Decoded representation of a TrueType instruction.
2use super::Opcode;
3
4/// Decoded TrueType instruction.
5#[derive(Copy, Clone, Debug)]
6pub struct Instruction<'a> {
7    /// Operation code.
8    pub opcode: Opcode,
9    /// Instruction operands that were decoded from the bytecode.
10    pub inline_operands: InlineOperands<'a>,
11    /// Program counter -- offset into the bytecode where this
12    /// instruction was decoded.
13    pub pc: usize,
14}
15
16impl std::fmt::Display for Instruction<'_> {
17    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
18        write!(f, "{}", self.opcode.name())?;
19        for value in self.inline_operands.values() {
20            write!(f, " {value}")?;
21        }
22        Ok(())
23    }
24}
25
26/// Sequence of instruction operands that are encoded directly in the bytecode.
27///
28/// This is only used for push instructions.
29#[derive(Copy, Clone, Default, Debug)]
30pub struct InlineOperands<'a> {
31    pub(super) bytes: &'a [u8],
32    pub(super) is_words: bool,
33}
34
35impl<'a> InlineOperands<'a> {
36    /// Returns the number of operands.
37    #[inline]
38    pub fn len(&self) -> usize {
39        if self.is_words {
40            self.bytes.len() / 2
41        } else {
42            self.bytes.len()
43        }
44    }
45
46    /// Returns true if there are no operands.
47    pub fn is_empty(&self) -> bool {
48        self.bytes.is_empty()
49    }
50
51    /// Returns an iterator over the operand values.
52    #[inline]
53    pub fn values(&self) -> impl Iterator<Item = i32> + 'a + Clone {
54        let (bytes, words) = if self.is_words {
55            (&[][..], self.bytes)
56        } else {
57            (self.bytes, &[][..])
58        };
59        bytes
60            .iter()
61            .map(|byte| *byte as u32 as i32)
62            .chain(words.chunks_exact(2).map(|chunk| {
63                let word = ((chunk[0] as u16) << 8) | chunk[1] as u16;
64                // Double cast to ensure sign extension
65                word as i16 as i32
66            }))
67    }
68}
69
70/// Mock for testing inline operands.
71#[cfg(any(test, feature = "scaler_test"))]
72pub struct MockInlineOperands {
73    bytes: Vec<u8>,
74    is_words: bool,
75}
76
77#[cfg(any(test, feature = "scaler_test"))]
78impl MockInlineOperands {
79    pub fn from_bytes(bytes: &[u8]) -> Self {
80        Self {
81            bytes: bytes.into(),
82            is_words: false,
83        }
84    }
85
86    pub fn from_words(words: &[i16]) -> Self {
87        Self {
88            bytes: words
89                .iter()
90                .map(|word| *word as u16)
91                .flat_map(|word| vec![(word >> 8) as u8, word as u8])
92                .collect(),
93            is_words: true,
94        }
95    }
96
97    pub fn operands(&self) -> InlineOperands {
98        InlineOperands {
99            bytes: &self.bytes,
100            is_words: self.is_words,
101        }
102    }
103}
104
105#[cfg(test)]
106mod tests {
107    use super::MockInlineOperands;
108
109    #[test]
110    fn byte_operands() {
111        let values = [5, 2, 85, 92, 26, 42, u8::MIN, u8::MAX];
112        let mock = MockInlineOperands::from_bytes(&values);
113        let decoded = mock.operands().values().collect::<Vec<_>>();
114        assert!(values.iter().map(|x| *x as i32).eq(decoded.iter().copied()));
115    }
116
117    #[test]
118    fn word_operands() {
119        let values = [-5, 2, 2845, 92, -26, 42, i16::MIN, i16::MAX];
120        let mock = MockInlineOperands::from_words(&values);
121        let decoded = mock.operands().values().collect::<Vec<_>>();
122        assert!(values.iter().map(|x| *x as i32).eq(decoded.iter().copied()));
123    }
124}