Coverage Report

Created: 2025-06-16 06:13

/src/ttf-parser/src/aat.rs
Line
Count
Source (jump to first uncovered line)
1
/*!
2
A collection of [Apple Advanced Typography](
3
https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6AATIntro.html)
4
related types.
5
*/
6
7
use core::num::NonZeroU16;
8
9
use crate::parser::{FromData, LazyArray16, NumFrom, Offset, Offset16, Offset32, Stream};
10
use crate::GlyphId;
11
12
/// Predefined states.
13
pub mod state {
14
    #![allow(missing_docs)]
15
    pub const START_OF_TEXT: u16 = 0;
16
}
17
18
/// Predefined classes.
19
///
20
/// Search for _Class Code_ in [Apple Advanced Typography Font Tables](
21
/// https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6Tables.html).
22
pub mod class {
23
    #![allow(missing_docs)]
24
    pub const END_OF_TEXT: u8 = 0;
25
    pub const OUT_OF_BOUNDS: u8 = 1;
26
    pub const DELETED_GLYPH: u8 = 2;
27
}
28
29
/// A State Table entry.
30
///
31
/// Used by legacy and extended tables.
32
#[derive(Clone, Copy, Debug)]
33
pub struct GenericStateEntry<T: FromData> {
34
    /// A new state.
35
    pub new_state: u16,
36
    /// Entry flags.
37
    pub flags: u16,
38
    /// Additional data.
39
    ///
40
    /// Use `()` if no data expected.
41
    pub extra: T,
42
}
43
44
impl<T: FromData> FromData for GenericStateEntry<T> {
45
    const SIZE: usize = 4 + T::SIZE;
46
47
    #[inline]
48
0
    fn parse(data: &[u8]) -> Option<Self> {
49
0
        let mut s = Stream::new(data);
50
0
        Some(GenericStateEntry {
51
0
            new_state: s.read::<u16>()?,
52
0
            flags: s.read::<u16>()?,
53
0
            extra: s.read::<T>()?,
54
        })
55
0
    }
56
}
57
58
impl<T: FromData> GenericStateEntry<T> {
59
    /// Checks that entry has an offset.
60
    #[inline]
61
0
    pub fn has_offset(&self) -> bool {
62
0
        self.flags & 0x3FFF != 0
63
0
    }
64
65
    /// Returns a value offset.
66
    ///
67
    /// Used by kern::format1 subtable.
68
    #[inline]
69
0
    pub fn value_offset(&self) -> ValueOffset {
70
0
        ValueOffset(self.flags & 0x3FFF)
71
0
    }
72
73
    /// If set, reset the kerning data (clear the stack).
74
    #[inline]
75
0
    pub fn has_reset(&self) -> bool {
76
0
        self.flags & 0x2000 != 0
77
0
    }
78
79
    /// If set, advance to the next glyph before going to the new state.
80
    #[inline]
81
0
    pub fn has_advance(&self) -> bool {
82
0
        self.flags & 0x4000 == 0
83
0
    }
84
85
    /// If set, push this glyph on the kerning stack.
86
    #[inline]
87
0
    pub fn has_push(&self) -> bool {
88
0
        self.flags & 0x8000 != 0
89
0
    }
90
91
    /// If set, remember this glyph as the marked glyph.
92
    ///
93
    /// Used by kerx::format4 subtable.
94
    ///
95
    /// Yes, the same as [`has_push`](Self::has_push).
96
    #[inline]
97
0
    pub fn has_mark(&self) -> bool {
98
0
        self.flags & 0x8000 != 0
99
0
    }
100
}
101
102
/// A legacy state entry used by [StateTable].
103
pub type StateEntry = GenericStateEntry<()>;
104
105
/// A type-safe wrapper for a kerning value offset.
106
#[derive(Clone, Copy, Debug)]
107
pub struct ValueOffset(u16);
108
109
impl ValueOffset {
110
    /// Returns the next offset.
111
    ///
112
    /// After reaching u16::MAX will start from 0.
113
    #[inline]
114
0
    pub fn next(self) -> Self {
115
0
        ValueOffset(self.0.wrapping_add(u16::SIZE as u16))
116
0
    }
117
}
118
119
/// A [State Table](
120
/// https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6Tables.html).
121
///
122
/// Also called `STHeader`.
123
///
124
/// Currently used by `kern` table.
125
#[derive(Clone)]
126
pub struct StateTable<'a> {
127
    number_of_classes: u16,
128
    first_glyph: GlyphId,
129
    class_table: &'a [u8],
130
    state_array_offset: u16,
131
    state_array: &'a [u8],
132
    entry_table: &'a [u8],
133
    actions: &'a [u8],
134
}
135
136
impl<'a> StateTable<'a> {
137
2
    pub(crate) fn parse(data: &'a [u8]) -> Option<Self> {
138
2
        let mut s = Stream::new(data);
139
140
2
        let number_of_classes: u16 = s.read()?;
141
        // Note that in format1 subtable, offsets are not from the subtable start,
142
        // but from subtable start + `header_size`.
143
        // So there is not need to subtract the `header_size`.
144
2
        let class_table_offset = s.read::<Offset16>()?.to_usize();
145
2
        let state_array_offset = s.read::<Offset16>()?.to_usize();
146
2
        let entry_table_offset = s.read::<Offset16>()?.to_usize();
147
        // Ignore `values_offset` since we don't use it.
148
149
        // Parse class subtable.
150
2
        let mut s = Stream::new_at(data, class_table_offset)?;
151
2
        let first_glyph: GlyphId = s.read()?;
152
2
        let number_of_glyphs: u16 = s.read()?;
153
        // The class table contains u8, so it's easier to use just a slice
154
        // instead of a LazyArray.
155
2
        let class_table = s.read_bytes(usize::from(number_of_glyphs))?;
156
157
        Some(StateTable {
158
2
            number_of_classes,
159
2
            first_glyph,
160
2
            class_table,
161
2
            state_array_offset: state_array_offset as u16,
162
2
            // We don't know the actual data size and it's kinda expensive to calculate.
163
2
            // So we are simply storing all the data past the offset.
164
2
            // Despite the fact that they may overlap.
165
2
            state_array: data.get(state_array_offset..)?,
166
2
            entry_table: data.get(entry_table_offset..)?,
167
            // `ValueOffset` defines an offset from the start of the subtable data.
168
            // We do not check that the provided offset is actually after `values_offset`.
169
2
            actions: data,
170
        })
171
2
    }
172
173
    /// Returns a glyph class.
174
    #[inline]
175
0
    pub fn class(&self, glyph_id: GlyphId) -> Option<u8> {
176
0
        if glyph_id.0 == 0xFFFF {
177
0
            return Some(class::DELETED_GLYPH);
178
0
        }
179
180
0
        let idx = glyph_id.0.checked_sub(self.first_glyph.0)?;
181
0
        self.class_table.get(usize::from(idx)).copied()
182
0
    }
183
184
    /// Returns a class entry.
185
    #[inline]
186
0
    pub fn entry(&self, state: u16, mut class: u8) -> Option<StateEntry> {
187
0
        if u16::from(class) >= self.number_of_classes {
188
0
            class = class::OUT_OF_BOUNDS;
189
0
        }
190
191
0
        let entry_idx = self
192
0
            .state_array
193
0
            .get(usize::from(state) * usize::from(self.number_of_classes) + usize::from(class))?;
194
195
0
        Stream::read_at(self.entry_table, usize::from(*entry_idx) * StateEntry::SIZE)
196
0
    }
197
198
    /// Returns kerning at offset.
199
    #[inline]
200
0
    pub fn kerning(&self, offset: ValueOffset) -> Option<i16> {
201
0
        Stream::read_at(self.actions, usize::from(offset.0))
202
0
    }
203
204
    /// Produces a new state.
205
    #[inline]
206
0
    pub fn new_state(&self, state: u16) -> u16 {
207
0
        let n = (i32::from(state) - i32::from(self.state_array_offset))
208
0
            / i32::from(self.number_of_classes);
209
210
        use core::convert::TryFrom;
211
0
        u16::try_from(n).unwrap_or(0)
212
0
    }
213
}
214
215
impl core::fmt::Debug for StateTable<'_> {
216
0
    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
217
0
        write!(f, "StateTable {{ ... }}")
218
0
    }
219
}
220
221
/// An [Extended State Table](
222
/// https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6Tables.html).
223
///
224
/// Also called `STXHeader`.
225
///
226
/// Currently used by `kerx` and `morx` tables.
227
#[derive(Clone)]
228
pub struct ExtendedStateTable<'a, T> {
229
    number_of_classes: u32,
230
    lookup: Lookup<'a>,
231
    state_array: &'a [u8],
232
    entry_table: &'a [u8],
233
    entry_type: core::marker::PhantomData<T>,
234
}
235
236
impl<'a, T: FromData> ExtendedStateTable<'a, T> {
237
    // TODO: make private
238
    /// Parses an Extended State Table from a stream.
239
    ///
240
    /// `number_of_glyphs` is from the `maxp` table.
241
2
    pub fn parse(number_of_glyphs: NonZeroU16, s: &mut Stream<'a>) -> Option<Self> {
242
2
        let data = s.tail()?;
243
244
2
        let number_of_classes = s.read::<u32>()?;
245
        // Note that offsets are not from the subtable start,
246
        // but from subtable start + `header_size`.
247
        // So there is not need to subtract the `header_size`.
248
2
        let lookup_table_offset = s.read::<Offset32>()?.to_usize();
249
2
        let state_array_offset = s.read::<Offset32>()?.to_usize();
250
2
        let entry_table_offset = s.read::<Offset32>()?.to_usize();
251
2
252
2
        Some(ExtendedStateTable {
253
2
            number_of_classes,
254
2
            lookup: Lookup::parse(number_of_glyphs, data.get(lookup_table_offset..)?)?,
255
            // We don't know the actual data size and it's kinda expensive to calculate.
256
            // So we are simply storing all the data past the offset.
257
            // Despite the fact that they may overlap.
258
1
            state_array: data.get(state_array_offset..)?,
259
1
            entry_table: data.get(entry_table_offset..)?,
260
1
            entry_type: core::marker::PhantomData,
261
        })
262
2
    }
<ttf_parser::aat::ExtendedStateTable<ttf_parser::tables::kerx::EntryData>>::parse
Line
Count
Source
241
2
    pub fn parse(number_of_glyphs: NonZeroU16, s: &mut Stream<'a>) -> Option<Self> {
242
2
        let data = s.tail()?;
243
244
2
        let number_of_classes = s.read::<u32>()?;
245
        // Note that offsets are not from the subtable start,
246
        // but from subtable start + `header_size`.
247
        // So there is not need to subtract the `header_size`.
248
2
        let lookup_table_offset = s.read::<Offset32>()?.to_usize();
249
2
        let state_array_offset = s.read::<Offset32>()?.to_usize();
250
2
        let entry_table_offset = s.read::<Offset32>()?.to_usize();
251
2
252
2
        Some(ExtendedStateTable {
253
2
            number_of_classes,
254
2
            lookup: Lookup::parse(number_of_glyphs, data.get(lookup_table_offset..)?)?,
255
            // We don't know the actual data size and it's kinda expensive to calculate.
256
            // So we are simply storing all the data past the offset.
257
            // Despite the fact that they may overlap.
258
1
            state_array: data.get(state_array_offset..)?,
259
1
            entry_table: data.get(entry_table_offset..)?,
260
1
            entry_type: core::marker::PhantomData,
261
        })
262
2
    }
Unexecuted instantiation: <ttf_parser::aat::ExtendedStateTable<ttf_parser::tables::morx::InsertionEntryData>>::parse
Unexecuted instantiation: <ttf_parser::aat::ExtendedStateTable<ttf_parser::tables::morx::ContextualEntryData>>::parse
Unexecuted instantiation: <ttf_parser::aat::ExtendedStateTable<u16>>::parse
Unexecuted instantiation: <ttf_parser::aat::ExtendedStateTable<()>>::parse
263
264
    /// Returns a glyph class.
265
    #[inline]
266
0
    pub fn class(&self, glyph_id: GlyphId) -> Option<u16> {
267
0
        if glyph_id.0 == 0xFFFF {
268
0
            return Some(u16::from(class::DELETED_GLYPH));
269
0
        }
270
0
271
0
        self.lookup.value(glyph_id)
272
0
    }
273
274
    /// Returns a class entry.
275
    #[inline]
276
0
    pub fn entry(&self, state: u16, mut class: u16) -> Option<GenericStateEntry<T>> {
277
0
        if u32::from(class) >= self.number_of_classes {
278
0
            class = u16::from(class::OUT_OF_BOUNDS);
279
0
        }
280
281
0
        let state_idx =
282
0
            usize::from(state) * usize::num_from(self.number_of_classes) + usize::from(class);
283
284
0
        let entry_idx: u16 = Stream::read_at(self.state_array, state_idx * u16::SIZE)?;
285
0
        Stream::read_at(
286
0
            self.entry_table,
287
0
            usize::from(entry_idx) * GenericStateEntry::<T>::SIZE,
288
0
        )
289
0
    }
290
}
291
292
impl<T> core::fmt::Debug for ExtendedStateTable<'_, T> {
293
0
    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
294
0
        write!(f, "ExtendedStateTable {{ ... }}")
295
0
    }
296
}
297
298
/// A [lookup table](
299
/// https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6Tables.html).
300
///
301
/// u32 values in Format10 tables will be truncated to u16.
302
/// u64 values in Format10 tables are not supported.
303
#[derive(Clone)]
304
pub struct Lookup<'a> {
305
    data: LookupInner<'a>,
306
}
307
308
impl<'a> Lookup<'a> {
309
    /// Parses a lookup table from raw data.
310
    ///
311
    /// `number_of_glyphs` is from the `maxp` table.
312
    #[inline]
313
2.14k
    pub fn parse(number_of_glyphs: NonZeroU16, data: &'a [u8]) -> Option<Self> {
314
2.14k
        LookupInner::parse(number_of_glyphs, data).map(|data| Self { data })
315
2.14k
    }
<ttf_parser::aat::Lookup>::parse
Line
Count
Source
313
169
    pub fn parse(number_of_glyphs: NonZeroU16, data: &'a [u8]) -> Option<Self> {
314
169
        LookupInner::parse(number_of_glyphs, data).map(|data| Self { data })
315
169
    }
<ttf_parser::aat::Lookup>::parse
Line
Count
Source
313
1.97k
    pub fn parse(number_of_glyphs: NonZeroU16, data: &'a [u8]) -> Option<Self> {
314
1.97k
        LookupInner::parse(number_of_glyphs, data).map(|data| Self { data })
315
1.97k
    }
316
317
    /// Returns a value associated with the specified glyph.
318
    #[inline]
319
1.34k
    pub fn value(&self, glyph_id: GlyphId) -> Option<u16> {
320
1.34k
        self.data.value(glyph_id)
321
1.34k
    }
<ttf_parser::aat::Lookup>::value
Line
Count
Source
319
22
    pub fn value(&self, glyph_id: GlyphId) -> Option<u16> {
320
22
        self.data.value(glyph_id)
321
22
    }
<ttf_parser::aat::Lookup>::value
Line
Count
Source
319
1.32k
    pub fn value(&self, glyph_id: GlyphId) -> Option<u16> {
320
1.32k
        self.data.value(glyph_id)
321
1.32k
    }
322
}
323
324
impl core::fmt::Debug for Lookup<'_> {
325
0
    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
326
0
        write!(f, "Lookup {{ ... }}")
327
0
    }
328
}
329
330
#[derive(Clone)]
331
enum LookupInner<'a> {
332
    Format1(LazyArray16<'a, u16>),
333
    Format2(BinarySearchTable<'a, LookupSegment>),
334
    Format4(BinarySearchTable<'a, LookupSegment>, &'a [u8]),
335
    Format6(BinarySearchTable<'a, LookupSingle>),
336
    Format8 {
337
        first_glyph: u16,
338
        values: LazyArray16<'a, u16>,
339
    },
340
    Format10 {
341
        value_size: u16,
342
        first_glyph: u16,
343
        glyph_count: u16,
344
        data: &'a [u8],
345
    },
346
}
347
348
impl<'a> LookupInner<'a> {
349
2.14k
    fn parse(number_of_glyphs: NonZeroU16, data: &'a [u8]) -> Option<Self> {
350
2.14k
        let mut s = Stream::new(data);
351
2.14k
        let format = s.read::<u16>()?;
352
2.14k
        match format {
353
            0 => {
354
887
                let values = s.read_array16::<u16>(number_of_glyphs.get())?;
355
884
                Some(Self::Format1(values))
356
            }
357
            2 => {
358
815
                let bsearch = BinarySearchTable::<LookupSegment>::parse(s.tail()?)?;
359
445
                Some(Self::Format2(bsearch))
360
            }
361
            4 => {
362
120
                let bsearch = BinarySearchTable::<LookupSegment>::parse(s.tail()?)?;
363
41
                Some(Self::Format4(bsearch, data))
364
            }
365
            6 => {
366
75
                let bsearch = BinarySearchTable::<LookupSingle>::parse(s.tail()?)?;
367
16
                Some(Self::Format6(bsearch))
368
            }
369
            8 => {
370
81
                let first_glyph = s.read::<u16>()?;
371
79
                let glyph_count = s.read::<u16>()?;
372
77
                let values = s.read_array16::<u16>(glyph_count)?;
373
17
                Some(Self::Format8 {
374
17
                    first_glyph,
375
17
                    values,
376
17
                })
377
            }
378
            10 => {
379
37
                let value_size = s.read::<u16>()?;
380
36
                let first_glyph = s.read::<u16>()?;
381
34
                let glyph_count = s.read::<u16>()?;
382
                Some(Self::Format10 {
383
10
                    value_size,
384
10
                    first_glyph,
385
10
                    glyph_count,
386
10
                    data: s.tail()?,
387
                })
388
            }
389
128
            _ => None,
390
        }
391
2.14k
    }
392
393
1.34k
    fn value(&self, glyph_id: GlyphId) -> Option<u16> {
394
1.34k
        match self {
395
883
            Self::Format1(values) => values.get(glyph_id.0),
396
434
            Self::Format2(ref bsearch) => bsearch.get(glyph_id).map(|v| v.value),
397
4
            Self::Format4(ref bsearch, data) => {
398
                // In format 4, LookupSegment contains an offset to a list of u16 values.
399
                // One value for each glyph in the LookupSegment range.
400
4
                let segment = bsearch.get(glyph_id)?;
401
4
                let index = glyph_id.0.checked_sub(segment.first_glyph)?;
402
4
                let offset = usize::from(segment.value) + u16::SIZE * usize::from(index);
403
4
                Stream::read_at::<u16>(data, offset)
404
            }
405
2
            Self::Format6(ref bsearch) => bsearch.get(glyph_id).map(|v| v.value),
406
13
            Self::Format8 {
407
13
                first_glyph,
408
13
                values,
409
13
            } => {
410
13
                let idx = glyph_id.0.checked_sub(*first_glyph)?;
411
12
                values.get(idx)
412
            }
413
6
            Self::Format10 {
414
6
                value_size,
415
6
                first_glyph,
416
6
                glyph_count,
417
6
                data,
418
6
            } => {
419
6
                let idx = glyph_id.0.checked_sub(*first_glyph)?;
420
6
                let mut s = Stream::new(data);
421
6
                match value_size {
422
4
                    1 => s.read_array16::<u8>(*glyph_count)?.get(idx).map(u16::from),
423
0
                    2 => s.read_array16::<u16>(*glyph_count)?.get(idx),
424
                    // TODO: we should return u32 here, but this is not supported yet
425
2
                    4 => s
426
2
                        .read_array16::<u32>(*glyph_count)?
427
2
                        .get(idx)
428
2
                        .map(|n| n as u16),
429
0
                    _ => None, // 8 is also supported
430
                }
431
            }
432
        }
433
1.34k
    }
434
}
435
436
/// A binary searching table as defined at
437
/// https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6Tables.html
438
#[derive(Clone)]
439
struct BinarySearchTable<'a, T: BinarySearchValue> {
440
    values: LazyArray16<'a, T>,
441
    len: NonZeroU16, // values length excluding termination segment
442
}
443
444
impl<'a, T: BinarySearchValue + core::fmt::Debug> BinarySearchTable<'a, T> {
445
    #[inline(never)]
446
1.01k
    fn parse(data: &'a [u8]) -> Option<Self> {
447
1.01k
        let mut s = Stream::new(data);
448
1.01k
        let segment_size = s.read::<u16>()?;
449
1.00k
        let number_of_segments = s.read::<u16>()?;
450
982
        s.advance(6); // search_range + entry_selector + range_shift
451
982
452
982
        if usize::from(segment_size) != T::SIZE {
453
431
            return None;
454
551
        }
455
551
456
551
        if number_of_segments == 0 {
457
2
            return None;
458
549
        }
459
460
549
        let values = s.read_array16::<T>(number_of_segments)?;
461
462
        // 'The number of termination values that need to be included is table-specific.
463
        // The value that indicates binary search termination is 0xFFFF.'
464
504
        let mut len = number_of_segments;
465
504
        if values.last()?.is_termination() {
466
329
            len = len.checked_sub(1)?;
467
175
        }
468
469
        Some(BinarySearchTable {
470
504
            len: NonZeroU16::new(len)?,
471
502
            values,
472
        })
473
1.01k
    }
<ttf_parser::aat::BinarySearchTable<ttf_parser::aat::LookupSingle>>::parse
Line
Count
Source
446
75
    fn parse(data: &'a [u8]) -> Option<Self> {
447
75
        let mut s = Stream::new(data);
448
75
        let segment_size = s.read::<u16>()?;
449
73
        let number_of_segments = s.read::<u16>()?;
450
49
        s.advance(6); // search_range + entry_selector + range_shift
451
49
452
49
        if usize::from(segment_size) != T::SIZE {
453
13
            return None;
454
36
        }
455
36
456
36
        if number_of_segments == 0 {
457
1
            return None;
458
35
        }
459
460
35
        let values = s.read_array16::<T>(number_of_segments)?;
461
462
        // 'The number of termination values that need to be included is table-specific.
463
        // The value that indicates binary search termination is 0xFFFF.'
464
17
        let mut len = number_of_segments;
465
17
        if values.last()?.is_termination() {
466
13
            len = len.checked_sub(1)?;
467
4
        }
468
469
        Some(BinarySearchTable {
470
17
            len: NonZeroU16::new(len)?,
471
16
            values,
472
        })
473
75
    }
<ttf_parser::aat::BinarySearchTable<ttf_parser::aat::LookupSegment>>::parse
Line
Count
Source
446
935
    fn parse(data: &'a [u8]) -> Option<Self> {
447
935
        let mut s = Stream::new(data);
448
935
        let segment_size = s.read::<u16>()?;
449
934
        let number_of_segments = s.read::<u16>()?;
450
933
        s.advance(6); // search_range + entry_selector + range_shift
451
933
452
933
        if usize::from(segment_size) != T::SIZE {
453
418
            return None;
454
515
        }
455
515
456
515
        if number_of_segments == 0 {
457
1
            return None;
458
514
        }
459
460
514
        let values = s.read_array16::<T>(number_of_segments)?;
461
462
        // 'The number of termination values that need to be included is table-specific.
463
        // The value that indicates binary search termination is 0xFFFF.'
464
487
        let mut len = number_of_segments;
465
487
        if values.last()?.is_termination() {
466
316
            len = len.checked_sub(1)?;
467
171
        }
468
469
        Some(BinarySearchTable {
470
487
            len: NonZeroU16::new(len)?,
471
486
            values,
472
        })
473
935
    }
474
475
440
    fn get(&self, key: GlyphId) -> Option<T> {
476
440
        let mut min = 0;
477
440
        let mut max = (self.len.get() as isize) - 1;
478
836
        while min <= max {
479
715
            let mid = (min + max) / 2;
480
715
            let v = self.values.get(mid as u16)?;
481
715
            match v.contains(key) {
482
237
                core::cmp::Ordering::Less => max = mid - 1,
483
159
                core::cmp::Ordering::Greater => min = mid + 1,
484
319
                core::cmp::Ordering::Equal => return Some(v),
485
            }
486
        }
487
488
121
        None
489
440
    }
<ttf_parser::aat::BinarySearchTable<ttf_parser::aat::LookupSingle>>::get
Line
Count
Source
475
2
    fn get(&self, key: GlyphId) -> Option<T> {
476
2
        let mut min = 0;
477
2
        let mut max = (self.len.get() as isize) - 1;
478
5
        while min <= max {
479
4
            let mid = (min + max) / 2;
480
4
            let v = self.values.get(mid as u16)?;
481
4
            match v.contains(key) {
482
3
                core::cmp::Ordering::Less => max = mid - 1,
483
0
                core::cmp::Ordering::Greater => min = mid + 1,
484
1
                core::cmp::Ordering::Equal => return Some(v),
485
            }
486
        }
487
488
1
        None
489
2
    }
<ttf_parser::aat::BinarySearchTable<ttf_parser::aat::LookupSegment>>::get
Line
Count
Source
475
438
    fn get(&self, key: GlyphId) -> Option<T> {
476
438
        let mut min = 0;
477
438
        let mut max = (self.len.get() as isize) - 1;
478
831
        while min <= max {
479
711
            let mid = (min + max) / 2;
480
711
            let v = self.values.get(mid as u16)?;
481
711
            match v.contains(key) {
482
234
                core::cmp::Ordering::Less => max = mid - 1,
483
159
                core::cmp::Ordering::Greater => min = mid + 1,
484
318
                core::cmp::Ordering::Equal => return Some(v),
485
            }
486
        }
487
488
120
        None
489
438
    }
490
}
491
492
trait BinarySearchValue: FromData {
493
    fn is_termination(&self) -> bool;
494
    fn contains(&self, glyph_id: GlyphId) -> core::cmp::Ordering;
495
}
496
497
#[derive(Clone, Copy, Debug)]
498
struct LookupSegment {
499
    last_glyph: u16,
500
    first_glyph: u16,
501
    value: u16,
502
}
503
504
impl FromData for LookupSegment {
505
    const SIZE: usize = 6;
506
507
    #[inline]
508
1.19k
    fn parse(data: &[u8]) -> Option<Self> {
509
1.19k
        let mut s = Stream::new(data);
510
1.19k
        Some(LookupSegment {
511
1.19k
            last_glyph: s.read::<u16>()?,
512
1.19k
            first_glyph: s.read::<u16>()?,
513
1.19k
            value: s.read::<u16>()?,
514
        })
515
1.19k
    }
516
}
517
518
impl BinarySearchValue for LookupSegment {
519
    #[inline]
520
487
    fn is_termination(&self) -> bool {
521
487
        self.last_glyph == 0xFFFF && self.first_glyph == 0xFFFF
522
487
    }
523
524
    #[inline]
525
711
    fn contains(&self, id: GlyphId) -> core::cmp::Ordering {
526
711
        if id.0 < self.first_glyph {
527
234
            core::cmp::Ordering::Less
528
477
        } else if id.0 <= self.last_glyph {
529
318
            core::cmp::Ordering::Equal
530
        } else {
531
159
            core::cmp::Ordering::Greater
532
        }
533
711
    }
534
}
535
536
#[derive(Clone, Copy, Debug)]
537
struct LookupSingle {
538
    glyph: u16,
539
    value: u16,
540
}
541
542
impl FromData for LookupSingle {
543
    const SIZE: usize = 4;
544
545
    #[inline]
546
21
    fn parse(data: &[u8]) -> Option<Self> {
547
21
        let mut s = Stream::new(data);
548
21
        Some(LookupSingle {
549
21
            glyph: s.read::<u16>()?,
550
21
            value: s.read::<u16>()?,
551
        })
552
21
    }
553
}
554
555
impl BinarySearchValue for LookupSingle {
556
    #[inline]
557
17
    fn is_termination(&self) -> bool {
558
17
        self.glyph == 0xFFFF
559
17
    }
560
561
    #[inline]
562
4
    fn contains(&self, id: GlyphId) -> core::cmp::Ordering {
563
4
        id.0.cmp(&self.glyph)
564
4
    }
565
}