skrifa/outline/glyf/hint/
definition.rs

1//! Management of function and instruction definitions.
2
3use core::ops::Range;
4
5use super::{error::HintErrorKind, program::Program};
6
7/// Code range and properties for a function or instruction definition.
8// Note: this type is designed to support allocation from user memory
9// so make sure the fields are all tightly packed and only use integral
10// types.
11// See <https://gitlab.freedesktop.org/freetype/freetype/-/blob/57617782464411201ce7bbc93b086c1b4d7d84a5/src/truetype/ttobjs.h#L158>
12#[derive(Copy, Clone, PartialEq, Eq, Default, Debug)]
13#[repr(C)]
14pub struct Definition {
15    start: u32,
16    end: u32,
17    /// The function number for an FDEF or opcode for an IDEF.
18    key: i32,
19    _pad: u16,
20    program: u8,
21    is_active: u8,
22}
23
24impl Definition {
25    /// Creates a new definition with the given program, code range and
26    /// key.
27    ///
28    /// The key is either a function number or opcode for function and
29    /// instruction definitions respectively.
30    pub fn new(program: Program, code_range: Range<usize>, key: i32) -> Self {
31        Self {
32            program: program as u8,
33            // Table sizes are specified in u32 so valid ranges will
34            // always fit.
35            start: code_range.start as u32,
36            end: code_range.end as u32,
37            key,
38            _pad: 0,
39            is_active: 1,
40        }
41    }
42
43    /// Returns the program that contains this definition.
44    pub fn program(&self) -> Program {
45        match self.program {
46            0 => Program::Font,
47            1 => Program::ControlValue,
48            _ => Program::Glyph,
49        }
50    }
51
52    /// Returns the function number or opcode.
53    #[cfg(test)]
54    pub fn key(&self) -> i32 {
55        self.key
56    }
57
58    /// Returns the byte range of the code for this definition in the source
59    /// program.
60    pub fn code_range(&self) -> Range<usize> {
61        self.start as usize..self.end as usize
62    }
63
64    /// Returns true if this definition entry has been defined by a program.
65    pub fn is_active(&self) -> bool {
66        self.is_active != 0
67    }
68}
69
70/// Map of function number or opcode to code definitions.
71///
72/// The `Ref` vs `Mut` distinction exists because these can be modified
73/// from the font and control value programs but not from a glyph program.
74/// In addition, hinting instance state is immutable once initialized so
75/// this captures that in a type safe way.
76pub enum DefinitionMap<'a> {
77    Ref(&'a [Definition]),
78    Mut(&'a mut [Definition]),
79}
80
81impl DefinitionMap<'_> {
82    /// Attempts to allocate a new definition entry with the given key.
83    ///
84    /// Overriding a definition is legal, so if an existing active entry
85    /// is found with the same key, that one will be returned. Otherwise,
86    /// an inactive entry will be chosen.
87    pub fn allocate(&mut self, key: i32) -> Result<&mut Definition, HintErrorKind> {
88        let Self::Mut(defs) = self else {
89            return Err(HintErrorKind::DefinitionInGlyphProgram);
90        };
91        // First, see if we can use key as an index.
92        //
93        // For function definitions in well-behaved fonts (that is, where
94        // function numbers fall within 0..max_function_defs) this will
95        // always work.
96        //
97        // For instruction definitions, this will likely never work
98        // because the number of instruction definitions is usually small
99        // (nearly always 0) and the available opcodes are in the higher
100        // ranges of u8 space.
101        let ix = if defs
102            .get(key as usize)
103            .filter(|def| !def.is_active() || def.key == key)
104            .is_some()
105        {
106            // If the entry is inactive or the key matches, we're good.
107            key as usize
108        } else {
109            // Otherwise, walk backward looking for an active entry with
110            // a matching key. Keep track of the inactive entry with the
111            // highest index.
112            let mut last_inactive_ix = None;
113            for (i, def) in defs.iter().enumerate().rev() {
114                if def.is_active() {
115                    if def.key == key {
116                        last_inactive_ix = Some(i);
117                        break;
118                    }
119                } else if last_inactive_ix.is_none() {
120                    last_inactive_ix = Some(i);
121                }
122            }
123            last_inactive_ix.ok_or(HintErrorKind::TooManyDefinitions)?
124        };
125        let def = defs.get_mut(ix).ok_or(HintErrorKind::TooManyDefinitions)?;
126        *def = Definition::new(Program::Font, 0..0, key);
127        Ok(def)
128    }
129
130    /// Returns the definition with the given key.
131    pub fn get(&self, key: i32) -> Result<&Definition, HintErrorKind> {
132        let defs = match self {
133            Self::Mut(defs) => *defs,
134            Self::Ref(defs) => *defs,
135        };
136        // Fast path, try to use key as index.
137        if let Some(def) = defs.get(key as usize) {
138            if def.is_active() && def.key == key {
139                return Ok(def);
140            }
141        }
142        // Otherwise, walk backward doing a linear search.
143        for def in defs.iter().rev() {
144            if def.is_active() && def.key == key {
145                return Ok(def);
146            }
147        }
148        Err(HintErrorKind::InvalidDefinition(key as _))
149    }
150
151    /// Returns a reference to the underlying definition slice.
152    #[cfg(test)]
153    fn as_slice(&self) -> &[Definition] {
154        match self {
155            Self::Ref(defs) => defs,
156            Self::Mut(defs) => defs,
157        }
158    }
159
160    /// If the map is mutable, resets all definitions to the default
161    /// value.
162    pub fn reset(&mut self) {
163        if let Self::Mut(defs) = self {
164            defs.fill(Default::default())
165        }
166    }
167}
168
169/// State containing font defined functions and instructions.
170pub struct DefinitionState<'a> {
171    pub functions: DefinitionMap<'a>,
172    pub instructions: DefinitionMap<'a>,
173}
174
175impl<'a> DefinitionState<'a> {
176    pub fn new(functions: DefinitionMap<'a>, instructions: DefinitionMap<'a>) -> Self {
177        Self {
178            functions,
179            instructions,
180        }
181    }
182}
183
184#[cfg(test)]
185mod tests {
186    use super::*;
187
188    #[test]
189    fn too_many_and_invalid() {
190        let mut buf = vec![Default::default(); 32];
191        let mut map = DefinitionMap::Mut(&mut buf);
192        for i in 0..32 {
193            map.allocate(i).unwrap();
194        }
195        assert!(matches!(
196            map.allocate(33),
197            Err(HintErrorKind::TooManyDefinitions)
198        ));
199        assert!(matches!(
200            map.get(33),
201            Err(HintErrorKind::InvalidDefinition(33))
202        ));
203    }
204
205    /// Test dense allocation where all keys map directly to indices. This is
206    /// the case for function definitions in well behaved fonts.
207    #[test]
208    fn allocate_dense() {
209        let mut buf = vec![Default::default(); 32];
210        let mut map = DefinitionMap::Mut(&mut buf);
211        for i in 0..32 {
212            map.allocate(i).unwrap();
213        }
214        for (i, def) in map.as_slice().iter().enumerate() {
215            let key = i as i32;
216            map.get(key).unwrap();
217            assert_eq!(def.key, key);
218        }
219    }
220
221    /// Test sparse allocation where keys never map to indices. This is
222    /// generally the case for instruction definitions and would apply
223    /// to fonts with function definition numbers that all fall outside
224    /// the range 0..max_function_defs.
225    #[test]
226    fn allocate_sparse() {
227        let mut buf = vec![Default::default(); 3];
228        let mut map = DefinitionMap::Mut(&mut buf);
229        let keys = [42, 88, 107];
230        for key in keys {
231            map.allocate(key).unwrap();
232        }
233        for key in keys {
234            assert_eq!(map.get(key).unwrap().key, key);
235        }
236    }
237
238    /// Test mixed allocation where some keys map to indices and others are
239    /// subject to fallback allocation. This would be the case for fonts
240    /// with function definition numbers where some fall inside the range
241    /// 0..max_function_defs but others don't.
242    #[test]
243    fn allocate_mixed() {
244        let mut buf = vec![Default::default(); 10];
245        let mut map = DefinitionMap::Mut(&mut buf);
246        let keys = [
247            0, 1, 2, 3, // Directly mapped to indices
248            123456, -42, -5555, // Fallback allocated
249            5,     // Also directly mapped
250            7,     // Would be direct but blocked by prior fallback
251        ];
252        for key in keys {
253            map.allocate(key).unwrap();
254        }
255        // Check backing store directly to ensure the expected allocation
256        // pattern.
257        let expected = [0, 1, 2, 3, 0, 5, 7, -5555, -42, 123456];
258        let mapped_keys: Vec<_> = map.as_slice().iter().map(|def| def.key).collect();
259        assert_eq!(&expected, mapped_keys.as_slice());
260        // Check that all keys are mapped
261        for key in keys {
262            assert_eq!(map.get(key).unwrap().key, key);
263        }
264    }
265}