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}