skrifa/outline/glyf/hint/
definition.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
//! Management of function and instruction definitions.

use core::ops::Range;

use super::{error::HintErrorKind, program::Program};

/// Code range and properties for a function or instruction definition.
// Note: this type is designed to support allocation from user memory
// so make sure the fields are all tightly packed and only use integral
// types.
// See <https://gitlab.freedesktop.org/freetype/freetype/-/blob/57617782464411201ce7bbc93b086c1b4d7d84a5/src/truetype/ttobjs.h#L158>
#[derive(Copy, Clone, PartialEq, Eq, Default, Debug)]
#[repr(C)]
pub struct Definition {
    start: u32,
    end: u32,
    /// The function number for an FDEF or opcode for an IDEF.
    key: i32,
    _pad: u16,
    program: u8,
    is_active: u8,
}

impl Definition {
    /// Creates a new definition with the given program, code range and
    /// key.
    ///
    /// The key is either a function number or opcode for function and
    /// instruction definitions respectively.
    pub fn new(program: Program, code_range: Range<usize>, key: i32) -> Self {
        Self {
            program: program as u8,
            // Table sizes are specified in u32 so valid ranges will
            // always fit.
            start: code_range.start as u32,
            end: code_range.end as u32,
            key,
            _pad: 0,
            is_active: 1,
        }
    }

    /// Returns the program that contains this definition.
    pub fn program(&self) -> Program {
        match self.program {
            0 => Program::Font,
            1 => Program::ControlValue,
            _ => Program::Glyph,
        }
    }

    /// Returns the function number or opcode.
    #[cfg(test)]
    pub fn key(&self) -> i32 {
        self.key
    }

    /// Returns the byte range of the code for this definition in the source
    /// program.
    pub fn code_range(&self) -> Range<usize> {
        self.start as usize..self.end as usize
    }

    /// Returns true if this definition entry has been defined by a program.
    pub fn is_active(&self) -> bool {
        self.is_active != 0
    }
}

/// Map of function number or opcode to code definitions.
///
/// The `Ref` vs `Mut` distinction exists because these can be modified
/// from the font and control value programs but not from a glyph program.
/// In addition, hinting instance state is immutable once initialized so
/// this captures that in a type safe way.
pub enum DefinitionMap<'a> {
    Ref(&'a [Definition]),
    Mut(&'a mut [Definition]),
}

impl<'a> DefinitionMap<'a> {
    /// Attempts to allocate a new definition entry with the given key.
    ///
    /// Overriding a definition is legal, so if an existing active entry
    /// is found with the same key, that one will be returned. Otherwise,
    /// an inactive entry will be chosen.
    pub fn allocate(&mut self, key: i32) -> Result<&mut Definition, HintErrorKind> {
        let Self::Mut(defs) = self else {
            return Err(HintErrorKind::DefinitionInGlyphProgram);
        };
        // First, see if we can use key as an index.
        //
        // For function definitions in well-behaved fonts (that is, where
        // function numbers fall within 0..max_function_defs) this will
        // always work.
        //
        // For instruction definitions, this will likely never work
        // because the number of instruction definitions is usually small
        // (nearly always 0) and the available opcodes are in the higher
        // ranges of u8 space.
        let ix = if defs
            .get(key as usize)
            .filter(|def| !def.is_active() || def.key == key)
            .is_some()
        {
            // If the entry is inactive or the key matches, we're good.
            key as usize
        } else {
            // Otherwise, walk backward looking for an active entry with
            // a matching key. Keep track of the inactive entry with the
            // highest index.
            let mut last_inactive_ix = None;
            for (i, def) in defs.iter().enumerate().rev() {
                if def.is_active() {
                    if def.key == key {
                        last_inactive_ix = Some(i);
                        break;
                    }
                } else if last_inactive_ix.is_none() {
                    last_inactive_ix = Some(i);
                }
            }
            last_inactive_ix.ok_or(HintErrorKind::TooManyDefinitions)?
        };
        let def = defs.get_mut(ix).ok_or(HintErrorKind::TooManyDefinitions)?;
        *def = Definition::new(Program::Font, 0..0, key);
        Ok(def)
    }

    /// Returns the definition with the given key.
    pub fn get(&self, key: i32) -> Result<&Definition, HintErrorKind> {
        let defs = match self {
            Self::Mut(defs) => *defs,
            Self::Ref(defs) => *defs,
        };
        // Fast path, try to use key as index.
        if let Some(def) = defs.get(key as usize) {
            if def.is_active() && def.key == key {
                return Ok(def);
            }
        }
        // Otherwise, walk backward doing a linear search.
        for def in defs.iter().rev() {
            if def.is_active() && def.key == key {
                return Ok(def);
            }
        }
        Err(HintErrorKind::InvalidDefinition(key as _))
    }

    /// Returns a reference to the underlying definition slice.
    #[cfg(test)]
    fn as_slice(&self) -> &[Definition] {
        match self {
            Self::Ref(defs) => defs,
            Self::Mut(defs) => defs,
        }
    }

    /// If the map is mutable, resets all definitions to the default
    /// value.
    pub fn reset(&mut self) {
        if let Self::Mut(defs) = self {
            defs.fill(Default::default())
        }
    }
}

/// State containing font defined functions and instructions.
pub struct DefinitionState<'a> {
    pub functions: DefinitionMap<'a>,
    pub instructions: DefinitionMap<'a>,
}

impl<'a> DefinitionState<'a> {
    pub fn new(functions: DefinitionMap<'a>, instructions: DefinitionMap<'a>) -> Self {
        Self {
            functions,
            instructions,
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn too_many_and_invalid() {
        let mut buf = vec![Default::default(); 32];
        let mut map = DefinitionMap::Mut(&mut buf);
        for i in 0..32 {
            map.allocate(i).unwrap();
        }
        assert!(matches!(
            map.allocate(33),
            Err(HintErrorKind::TooManyDefinitions)
        ));
        assert!(matches!(
            map.get(33),
            Err(HintErrorKind::InvalidDefinition(33))
        ));
    }

    /// Test dense allocation where all keys map directly to indices. This is
    /// the case for function definitions in well behaved fonts.
    #[test]
    fn allocate_dense() {
        let mut buf = vec![Default::default(); 32];
        let mut map = DefinitionMap::Mut(&mut buf);
        for i in 0..32 {
            map.allocate(i).unwrap();
        }
        for (i, def) in map.as_slice().iter().enumerate() {
            let key = i as i32;
            map.get(key).unwrap();
            assert_eq!(def.key, key);
        }
    }

    /// Test sparse allocation where keys never map to indices. This is
    /// generally the case for instruction definitions and would apply
    /// to fonts with function definition numbers that all fall outside
    /// the range 0..max_function_defs.
    #[test]
    fn allocate_sparse() {
        let mut buf = vec![Default::default(); 3];
        let mut map = DefinitionMap::Mut(&mut buf);
        let keys = [42, 88, 107];
        for key in keys {
            map.allocate(key).unwrap();
        }
        for key in keys {
            assert_eq!(map.get(key).unwrap().key, key);
        }
    }

    /// Test mixed allocation where some keys map to indices and others are
    /// subject to fallback allocation. This would be the case for fonts
    /// with function definition numbers where some fall inside the range
    /// 0..max_function_defs but others don't.
    #[test]
    fn allocate_mixed() {
        let mut buf = vec![Default::default(); 10];
        let mut map = DefinitionMap::Mut(&mut buf);
        let keys = [
            0, 1, 2, 3, // Directly mapped to indices
            123456, -42, -5555, // Fallback allocated
            5,     // Also directly mapped
            7,     // Would be direct but blocked by prior fallback
        ];
        for key in keys {
            map.allocate(key).unwrap();
        }
        // Check backing store directly to ensure the expected allocation
        // pattern.
        let expected = [0, 1, 2, 3, 0, 5, 7, -5555, -42, 123456];
        let mapped_keys: Vec<_> = map.as_slice().iter().map(|def| def.key).collect();
        assert_eq!(&expected, mapped_keys.as_slice());
        // Check that all keys are mapped
        for key in keys {
            assert_eq!(map.get(key).unwrap().key, key);
        }
    }
}