Skip to main content

read_fonts/tables/
variations.rs

1//! OpenType font variations common tables.
2
3include!("../../generated/generated_variations.rs");
4
5use super::{
6    glyf::{PointCoord, PointFlags, PointMarker},
7    gvar::GlyphDelta,
8};
9
10pub const NO_VARIATION_INDEX: u32 = 0xFFFFFFFF;
11/// Outer and inner indices for reading from an [ItemVariationStore].
12#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
13pub struct DeltaSetIndex {
14    /// Outer delta set index.
15    pub outer: u16,
16    /// Inner delta set index.
17    pub inner: u16,
18}
19
20impl DeltaSetIndex {
21    pub const NO_VARIATION_INDEX: Self = Self {
22        outer: (NO_VARIATION_INDEX >> 16) as u16,
23        inner: (NO_VARIATION_INDEX & 0xFFFF) as u16,
24    };
25}
26
27#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
28#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
29pub struct TupleIndex(u16);
30
31impl TupleIndex {
32    /// Flag indicating that this tuple variation header includes an embedded
33    /// peak tuple record, immediately after the tupleIndex field.
34    ///
35    /// If set, the low 12 bits of the tupleIndex value are ignored.
36    ///
37    /// Note that this must always be set within the 'cvar' table.
38    pub const EMBEDDED_PEAK_TUPLE: u16 = 0x8000;
39
40    /// Flag indicating that this tuple variation table applies to an
41    /// intermediate region within the variation space.
42    ///
43    /// If set, the header includes the two intermediate-region, start and end
44    /// tuple records, immediately after the peak tuple record (if present).
45    pub const INTERMEDIATE_REGION: u16 = 0x4000;
46    /// Flag indicating that the serialized data for this tuple variation table
47    /// includes packed “point” number data.
48    ///
49    /// If set, this tuple variation table uses that number data; if clear,
50    /// this tuple variation table uses shared number data found at the start
51    /// of the serialized data for this glyph variation data or 'cvar' table.
52    pub const PRIVATE_POINT_NUMBERS: u16 = 0x2000;
53    //0x1000	Reserved	Reserved for future use — set to 0.
54    //
55    /// Mask for the low 12 bits to give the shared tuple records index.
56    pub const TUPLE_INDEX_MASK: u16 = 0x0FFF;
57
58    #[inline(always)]
59    fn tuple_len(self, axis_count: u16, flag: usize) -> usize {
60        if flag == 0 {
61            self.embedded_peak_tuple() as usize * axis_count as usize
62        } else {
63            self.intermediate_region() as usize * axis_count as usize
64        }
65    }
66
67    pub fn bits(self) -> u16 {
68        self.0
69    }
70
71    pub fn from_bits(bits: u16) -> Self {
72        TupleIndex(bits)
73    }
74
75    /// `true` if the header includes an embedded peak tuple.
76    pub fn embedded_peak_tuple(self) -> bool {
77        (self.0 & Self::EMBEDDED_PEAK_TUPLE) != 0
78    }
79
80    /// `true` if the header includes the two intermediate region tuple records.
81    pub fn intermediate_region(self) -> bool {
82        (self.0 & Self::INTERMEDIATE_REGION) != 0
83    }
84
85    /// `true` if the data for this table includes packed point number data.
86    pub fn private_point_numbers(self) -> bool {
87        (self.0 & Self::PRIVATE_POINT_NUMBERS) != 0
88    }
89
90    pub fn tuple_records_index(self) -> Option<u16> {
91        (!self.embedded_peak_tuple()).then_some(self.0 & Self::TUPLE_INDEX_MASK)
92    }
93}
94
95impl types::Scalar for TupleIndex {
96    type Raw = <u16 as types::Scalar>::Raw;
97    fn to_raw(self) -> Self::Raw {
98        self.0.to_raw()
99    }
100    fn from_raw(raw: Self::Raw) -> Self {
101        let t = <u16>::from_raw(raw);
102        Self(t)
103    }
104}
105
106/// The 'tupleVariationCount' field of the [Tuple Variation Store Header][header]
107///
108/// The high 4 bits are flags, and the low 12 bits are the number of tuple
109/// variation tables for this glyph. The count can be any number between 1 and 4095.
110///
111/// [header]: https://learn.microsoft.com/en-us/typography/opentype/spec/otvarcommonformats#tuple-variation-store-header
112#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
113#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
114pub struct TupleVariationCount(u16);
115
116impl TupleVariationCount {
117    /// Flag indicating that some or all tuple variation tables reference a
118    /// shared set of “point” numbers.
119    ///
120    /// These shared numbers are represented as packed point number data at the
121    /// start of the serialized data.
122    pub const SHARED_POINT_NUMBERS: u16 = 0x8000;
123
124    /// Mask for the low 12 bits to give the shared tuple records index.
125    pub const COUNT_MASK: u16 = 0x0FFF;
126
127    pub fn bits(self) -> u16 {
128        self.0
129    }
130
131    pub fn from_bits(bits: u16) -> Self {
132        Self(bits)
133    }
134
135    /// `true` if any tables reference a shared set of point numbers
136    pub fn shared_point_numbers(self) -> bool {
137        (self.0 & Self::SHARED_POINT_NUMBERS) != 0
138    }
139
140    pub fn count(self) -> u16 {
141        self.0 & Self::COUNT_MASK
142    }
143}
144
145impl types::Scalar for TupleVariationCount {
146    type Raw = <u16 as types::Scalar>::Raw;
147    fn to_raw(self) -> Self::Raw {
148        self.0.to_raw()
149    }
150    fn from_raw(raw: Self::Raw) -> Self {
151        let t = <u16>::from_raw(raw);
152        Self(t)
153    }
154}
155
156impl<'a> TupleVariationHeader<'a> {
157    /// Peak tuple record for this tuple variation table — optional,
158    /// determined by flags in the tupleIndex value.  Note that this
159    /// must always be included in the 'cvar' table.
160    #[inline(always)]
161    pub fn peak_tuple(&self) -> Option<Tuple<'a>> {
162        self.tuple_index().embedded_peak_tuple().then(|| {
163            let range = self.peak_tuple_byte_range();
164            Tuple {
165                values: self.data.read_array(range).unwrap(),
166            }
167        })
168    }
169
170    /// Intermediate start tuple record for this tuple variation table
171    /// — optional, determined by flags in the tupleIndex value.
172    #[inline(always)]
173    pub fn intermediate_start_tuple(&self) -> Option<Tuple<'a>> {
174        self.tuple_index().intermediate_region().then(|| {
175            let range = self.intermediate_start_tuple_byte_range();
176            Tuple {
177                values: self.data.read_array(range).unwrap(),
178            }
179        })
180    }
181
182    /// Intermediate end tuple record for this tuple variation table
183    /// — optional, determined by flags in the tupleIndex value.
184    #[inline(always)]
185    pub fn intermediate_end_tuple(&self) -> Option<Tuple<'a>> {
186        self.tuple_index().intermediate_region().then(|| {
187            let range = self.intermediate_end_tuple_byte_range();
188            Tuple {
189                values: self.data.read_array(range).unwrap(),
190            }
191        })
192    }
193
194    /// Intermediate tuple records for this tuple variation table
195    /// — optional, determined by flags in the tupleIndex value.
196    #[inline(always)]
197    pub fn intermediate_tuples(&self) -> Option<(Tuple<'a>, Tuple<'a>)> {
198        self.tuple_index().intermediate_region().then(|| {
199            let start_range = self.intermediate_start_tuple_byte_range();
200            let end_range = self.intermediate_end_tuple_byte_range();
201            (
202                Tuple {
203                    values: self.data.read_array(start_range).unwrap(),
204                },
205                Tuple {
206                    values: self.data.read_array(end_range).unwrap(),
207                },
208            )
209        })
210    }
211
212    /// Compute the actual length of this table in bytes
213    #[inline(always)]
214    fn byte_len(&self, axis_count: u16) -> usize {
215        const FIXED_LEN: usize = u16::RAW_BYTE_LEN + TupleIndex::RAW_BYTE_LEN;
216        let tuple_byte_len = F2Dot14::RAW_BYTE_LEN * axis_count as usize;
217        let index = self.tuple_index();
218        FIXED_LEN
219            + if index.embedded_peak_tuple() {
220                tuple_byte_len
221            } else {
222                Default::default()
223            }
224            + if index.intermediate_region() {
225                tuple_byte_len * 2
226            } else {
227                Default::default()
228            }
229    }
230}
231
232impl Tuple<'_> {
233    pub fn len(&self) -> usize {
234        self.values().len()
235    }
236
237    pub fn is_empty(&self) -> bool {
238        self.values.is_empty()
239    }
240
241    #[inline(always)]
242    pub fn get(&self, idx: usize) -> Option<F2Dot14> {
243        self.values.get(idx).map(BigEndian::get)
244    }
245}
246
247//FIXME: add an #[extra_traits(..)] attribute!
248#[allow(clippy::derivable_impls)]
249impl Default for Tuple<'_> {
250    fn default() -> Self {
251        Self {
252            values: Default::default(),
253        }
254    }
255}
256
257/// [Packed "Point" Numbers](https://learn.microsoft.com/en-us/typography/opentype/spec/otvarcommonformats#packed-point-numbers)
258#[derive(Clone, Default, Debug)]
259pub struct PackedPointNumbers<'a> {
260    data: FontData<'a>,
261}
262
263impl<'a> PackedPointNumbers<'a> {
264    /// read point numbers off the front of this data, returning the remaining data
265    pub fn split_off_front(data: FontData<'a>) -> (Self, FontData<'a>) {
266        let this = PackedPointNumbers { data };
267        let total_len = this.total_len();
268        let remainder = data.split_off(total_len).unwrap_or_default();
269        (this, remainder)
270    }
271
272    /// The number of points in this set
273    pub fn count(&self) -> u16 {
274        self.count_and_count_bytes().0
275    }
276
277    /// compute the count, and the number of bytes used to store it
278    fn count_and_count_bytes(&self) -> (u16, usize) {
279        match self.data.read_at::<u8>(0).unwrap_or(0) {
280            0 => (0, 1),
281            count @ 1..=127 => (count as u16, 1),
282            _ => {
283                // "If the high bit of the first byte is set, then a second byte is used.
284                // The count is read from interpreting the two bytes as a big-endian
285                // uint16 value with the high-order bit masked out."
286
287                let count = self.data.read_at::<u16>(0).unwrap_or_default() & 0x7FFF;
288                // a weird case where I'm following fonttools: if the 'use words' bit
289                // is set, but the total count is still 0, treat it like 0 first byte
290                if count == 0 {
291                    (0, 2)
292                } else {
293                    (count & 0x7FFF, 2)
294                }
295            }
296        }
297    }
298
299    /// the number of bytes to encode the packed point numbers
300    #[inline(never)]
301    fn total_len(&self) -> usize {
302        let (n_points, mut n_bytes) = self.count_and_count_bytes();
303        if n_points == 0 {
304            return n_bytes;
305        }
306        let mut cursor = self.data.cursor();
307        cursor.advance_by(n_bytes);
308
309        let mut n_seen = 0;
310        while n_seen < n_points {
311            let Some((count, two_bytes)) = read_control_byte(&mut cursor) else {
312                return n_bytes;
313            };
314            let word_size = 1 + usize::from(two_bytes);
315            let run_size = word_size * count as usize;
316            n_bytes += run_size + 1; // plus the control byte;
317            cursor.advance_by(run_size);
318            n_seen += count as u16;
319        }
320
321        n_bytes
322    }
323
324    /// Iterate over the packed points
325    pub fn iter(&self) -> PackedPointNumbersIter<'a> {
326        let (count, n_bytes) = self.count_and_count_bytes();
327        let mut cursor = self.data.cursor();
328        cursor.advance_by(n_bytes);
329        PackedPointNumbersIter::new(count, cursor)
330    }
331}
332
333/// An iterator over the packed point numbers data.
334#[derive(Clone, Debug)]
335pub struct PackedPointNumbersIter<'a> {
336    count: u16,
337    seen: u16,
338    last_val: u16,
339    current_run: PointRunIter<'a>,
340}
341
342impl<'a> PackedPointNumbersIter<'a> {
343    fn new(count: u16, cursor: Cursor<'a>) -> Self {
344        PackedPointNumbersIter {
345            count,
346            seen: 0,
347            last_val: 0,
348            current_run: PointRunIter {
349                remaining: 0,
350                two_bytes: false,
351                cursor,
352            },
353        }
354    }
355}
356
357/// Implements the logic for iterating over the individual runs
358#[derive(Clone, Debug)]
359struct PointRunIter<'a> {
360    remaining: u8,
361    two_bytes: bool,
362    cursor: Cursor<'a>,
363}
364
365impl Iterator for PointRunIter<'_> {
366    type Item = u16;
367
368    fn next(&mut self) -> Option<Self::Item> {
369        // if no items remain in this run, start the next one.
370        while self.remaining == 0 {
371            (self.remaining, self.two_bytes) = read_control_byte(&mut self.cursor)?;
372        }
373
374        self.remaining -= 1;
375        if self.two_bytes {
376            self.cursor.read().ok()
377        } else {
378            self.cursor.read::<u8>().ok().map(|v| v as u16)
379        }
380    }
381}
382
383/// returns the count and the 'uses_two_bytes' flag from the control byte
384fn read_control_byte(cursor: &mut Cursor) -> Option<(u8, bool)> {
385    let control: u8 = cursor.read().ok()?;
386    let two_bytes = (control & 0x80) != 0;
387    let count = (control & 0x7F) + 1;
388    Some((count, two_bytes))
389}
390
391impl Iterator for PackedPointNumbersIter<'_> {
392    type Item = u16;
393
394    fn next(&mut self) -> Option<Self::Item> {
395        // if our count is zero, we keep incrementing forever
396        if self.count == 0 {
397            let result = self.last_val;
398            self.last_val = self.last_val.checked_add(1)?;
399            return Some(result);
400        }
401
402        if self.count == self.seen {
403            return None;
404        }
405        self.seen += 1;
406        self.last_val = self.last_val.checked_add(self.current_run.next()?)?;
407        Some(self.last_val)
408    }
409
410    fn size_hint(&self) -> (usize, Option<usize>) {
411        (self.count as usize, Some(self.count as usize))
412    }
413}
414
415// completely unnecessary?
416impl ExactSizeIterator for PackedPointNumbersIter<'_> {}
417
418/// [Packed Deltas](https://learn.microsoft.com/en-us/typography/opentype/spec/otvarcommonformats#packed-deltas)
419#[derive(Clone, Debug)]
420pub struct PackedDeltas<'a> {
421    data: FontData<'a>,
422    // How many values we expect
423    count: Option<usize>,
424}
425
426impl<'a> PackedDeltas<'a> {
427    pub(crate) fn new(data: FontData<'a>, count: usize) -> Self {
428        Self {
429            data,
430            count: Some(count),
431        }
432    }
433
434    /// NOTE: this is unbounded, and assumes all of data is deltas.
435    #[doc(hidden)] // used by tests in write-fonts
436    pub fn consume_all(data: FontData<'a>) -> Self {
437        Self { data, count: None }
438    }
439
440    pub fn count(&self) -> Option<usize> {
441        self.count
442    }
443
444    pub fn count_or_compute(&self) -> usize {
445        self.count.unwrap_or_else(|| count_all_deltas(self.data))
446    }
447
448    pub fn iter(&self) -> DeltaRunIter<'a> {
449        DeltaRunIter::new(self.data.cursor(), self.count)
450    }
451
452    pub fn fetcher(&self) -> PackedDeltaFetcher<'a> {
453        PackedDeltaFetcher::new(self.data.as_bytes(), self.count)
454    }
455
456    fn x_deltas(&self) -> DeltaRunIter<'a> {
457        let count = self.count_or_compute() / 2;
458        DeltaRunIter::new(self.data.cursor(), Some(count))
459    }
460
461    fn y_deltas(&self) -> DeltaRunIter<'a> {
462        let count = self.count_or_compute();
463        DeltaRunIter::new(self.data.cursor(), Some(count)).skip_fast(count / 2)
464    }
465}
466
467/// Flag indicating that this run contains no data,
468/// and that the deltas for this run are all zero.
469const DELTAS_ARE_ZERO: u8 = 0x80;
470/// Flag indicating the data type for delta values in the run.
471const DELTAS_ARE_WORDS: u8 = 0x40;
472/// Mask for the low 6 bits to provide the number of delta values in the run, minus one.
473const DELTA_RUN_COUNT_MASK: u8 = 0x3F;
474
475/// The type of values for a given delta run (influences the number of bytes per delta)
476///
477/// The variants are intentionally set to the byte size of the type to allow usage
478/// as a multiplier when computing offsets.
479#[derive(Clone, Copy, Debug, PartialEq)]
480pub enum DeltaRunType {
481    Zero = 0,
482    I8 = 1,
483    I16 = 2,
484    I32 = 4,
485}
486
487impl DeltaRunType {
488    /// The run type for a given control byte
489    pub fn new(control: u8) -> Self {
490        // if the top two bits of the control byte (DELTAS_ARE_ZERO and DELTAS_ARE_WORDS) are both set,
491        // then the following values are 32-bit.
492        // <https://github.com/harfbuzz/boring-expansion-spec/blob/main/VARC.md#tuplevalues>
493        let are_zero = (control & DELTAS_ARE_ZERO) != 0;
494        let are_words = (control & DELTAS_ARE_WORDS) != 0;
495        match (are_zero, are_words) {
496            (false, false) => Self::I8,
497            (false, true) => Self::I16,
498            (true, false) => Self::Zero,
499            (true, true) => Self::I32,
500        }
501    }
502}
503
504/// Implements the logic for iterating over the individual runs
505#[derive(Clone, Debug)]
506pub struct DeltaRunIter<'a> {
507    limit: Option<usize>, // when None, consume all available data
508    remaining_in_run: u8,
509    value_type: DeltaRunType,
510    cursor: Cursor<'a>,
511}
512
513/// A decoding helper that adds packed deltas directly to an output slice.
514pub struct PackedDeltaFetcher<'a> {
515    data: &'a [u8],
516    pos: usize,
517    end: usize,
518    run_count: usize,
519    value_type: DeltaRunType,
520    remaining_total: Option<usize>,
521}
522
523impl<'a> PackedDeltaFetcher<'a> {
524    fn new(data: &'a [u8], count: Option<usize>) -> Self {
525        Self {
526            data,
527            pos: 0,
528            end: data.len(),
529            run_count: 0,
530            value_type: DeltaRunType::I8,
531            remaining_total: count,
532        }
533    }
534
535    #[inline(always)]
536    fn ensure_run(&mut self) -> Result<(), ReadError> {
537        if self.run_count > 0 {
538            return Ok(());
539        }
540        if self.pos >= self.end {
541            return Err(ReadError::OutOfBounds);
542        }
543        let control = self.data[self.pos];
544        self.pos += 1;
545        self.run_count = (control & DELTA_RUN_COUNT_MASK) as usize + 1;
546        self.value_type = DeltaRunType::new(control);
547        let width = self.value_type as usize;
548        let needed = self.run_count * width;
549        if self.pos + needed > self.end {
550            return Err(ReadError::OutOfBounds);
551        }
552        Ok(())
553    }
554
555    pub fn skip(&mut self, mut n: usize) -> Result<(), ReadError> {
556        if let Some(remaining_total) = self.remaining_total {
557            if n > remaining_total {
558                return Err(ReadError::OutOfBounds);
559            }
560            self.remaining_total = Some(remaining_total - n);
561        }
562        while n > 0 {
563            self.ensure_run()?;
564            let take = n.min(self.run_count);
565            let width = self.value_type as usize;
566            self.pos += take * width;
567            self.run_count -= take;
568            n -= take;
569        }
570        Ok(())
571    }
572
573    pub fn add_to_f32_scaled(&mut self, out: &mut [f32], scale: f32) -> Result<(), ReadError> {
574        self.add_to_scaled(out, scale, |delta| delta as f32)
575    }
576
577    /// Adds packed deltas without losing precision from 32-bit integer values.
578    pub fn add_to_f64_scaled(&mut self, out: &mut [f64], scale: f64) -> Result<(), ReadError> {
579        self.add_to_scaled(out, scale, |delta| delta as f64)
580    }
581
582    fn add_to_scaled<T>(
583        &mut self,
584        out: &mut [T],
585        scale: T,
586        convert: impl Fn(i32) -> T,
587    ) -> Result<(), ReadError>
588    where
589        T: Copy + core::ops::AddAssign + core::ops::Mul<Output = T>,
590    {
591        let mut remaining = out.len();
592        if let Some(remaining_total) = self.remaining_total {
593            if remaining > remaining_total {
594                return Err(ReadError::OutOfBounds);
595            }
596            self.remaining_total = Some(remaining_total - remaining);
597        }
598        let mut idx = 0usize;
599        while remaining > 0 {
600            self.ensure_run()?;
601            let take = remaining.min(self.run_count);
602            match self.value_type {
603                DeltaRunType::Zero => {
604                    // nothing to add
605                    idx += take;
606                }
607                DeltaRunType::I8 => {
608                    let bytes = &self.data[self.pos..self.pos + take];
609                    for &b in bytes {
610                        out[idx] += convert(b as i8 as i32) * scale;
611                        idx += 1;
612                    }
613                    self.pos += take;
614                }
615                DeltaRunType::I16 => {
616                    let bytes = &self.data[self.pos..self.pos + take * 2];
617                    for chunk in bytes.chunks_exact(2) {
618                        let delta = i16::from_be_bytes([chunk[0], chunk[1]]) as i32;
619                        out[idx] += convert(delta) * scale;
620                        idx += 1;
621                    }
622                    self.pos += take * 2;
623                }
624                DeltaRunType::I32 => {
625                    let bytes = &self.data[self.pos..self.pos + take * 4];
626                    for chunk in bytes.chunks_exact(4) {
627                        let delta = i32::from_be_bytes([chunk[0], chunk[1], chunk[2], chunk[3]]);
628                        out[idx] += convert(delta) * scale;
629                        idx += 1;
630                    }
631                    self.pos += take * 4;
632                }
633            }
634            self.run_count -= take;
635            remaining -= take;
636        }
637        Ok(())
638    }
639}
640
641/// Counts the number of deltas available in the given data, avoiding
642/// excessive reads.
643fn count_all_deltas(data: FontData) -> usize {
644    let mut count = 0;
645    let mut offset = 0;
646    while let Ok(control) = data.read_at::<u8>(offset) {
647        let run_count = (control & DELTA_RUN_COUNT_MASK) as usize + 1;
648        count += run_count;
649        offset += run_count * DeltaRunType::new(control) as usize + 1;
650    }
651    count
652}
653
654impl<'a> DeltaRunIter<'a> {
655    fn new(cursor: Cursor<'a>, limit: Option<usize>) -> Self {
656        DeltaRunIter {
657            limit,
658            remaining_in_run: 0,
659            value_type: DeltaRunType::I8,
660            cursor,
661        }
662    }
663
664    pub(crate) fn end(mut self) -> Cursor<'a> {
665        if let Some(limit) = self.limit {
666            return self.skip_fast(limit).cursor;
667        }
668        // No limit: jump over runs without decoding values.
669        if self.remaining_in_run != 0 {
670            if self.value_type != DeltaRunType::Zero {
671                self.cursor
672                    .advance_by(self.remaining_in_run as usize * self.value_type as usize);
673            }
674            self.remaining_in_run = 0;
675        }
676        while self.read_next_control().is_some() {
677            if self.value_type != DeltaRunType::Zero {
678                self.cursor
679                    .advance_by(self.remaining_in_run as usize * self.value_type as usize);
680            }
681            self.remaining_in_run = 0;
682        }
683        self.cursor
684    }
685
686    /// Skips `n` deltas without reading the actual delta values.
687    #[inline(always)]
688    pub fn skip_fast(mut self, n: usize) -> Self {
689        let mut wanted = n;
690        let mut remaining = self.remaining_in_run as usize;
691        let mut value_type = self.value_type;
692        loop {
693            if wanted > remaining {
694                // Consume the rest of this run and move to the next.
695                self.cursor.advance_by(remaining * value_type as usize);
696                wanted -= remaining;
697                if self.read_next_control().is_none() {
698                    self.limit = Some(0);
699                    break;
700                }
701                remaining = self.remaining_in_run as usize;
702                value_type = self.value_type;
703                continue;
704            }
705            let consumed = wanted;
706            self.remaining_in_run -= consumed as u8;
707            self.cursor.advance_by(consumed * value_type as usize);
708            if let Some(limit) = self.limit.as_mut() {
709                *limit = limit.saturating_sub(n);
710            }
711            break;
712        }
713        self
714    }
715
716    #[inline(always)]
717    fn read_next_control(&mut self) -> Option<()> {
718        self.remaining_in_run = 0;
719        let control: u8 = self.cursor.read().ok()?;
720        self.value_type = DeltaRunType::new(control);
721        self.remaining_in_run = (control & DELTA_RUN_COUNT_MASK) + 1;
722        Some(())
723    }
724}
725
726impl Iterator for DeltaRunIter<'_> {
727    type Item = i32;
728
729    #[inline(always)]
730    fn next(&mut self) -> Option<Self::Item> {
731        if let Some(limit) = self.limit {
732            if limit == 0 {
733                return None;
734            }
735            self.limit = Some(limit - 1);
736        }
737        if self.remaining_in_run == 0 {
738            self.read_next_control()?;
739        }
740        self.remaining_in_run -= 1;
741        match self.value_type {
742            DeltaRunType::Zero => Some(0),
743            DeltaRunType::I8 => self.cursor.read::<i8>().ok().map(|v| v as i32),
744            DeltaRunType::I16 => self.cursor.read::<i16>().ok().map(|v| v as i32),
745            DeltaRunType::I32 => self.cursor.read::<i32>().ok(),
746        }
747    }
748}
749
750/// A helper type for iterating over [`TupleVariationHeader`]s.
751pub struct TupleVariationHeaderIter<'a> {
752    data: FontData<'a>,
753    n_headers: usize,
754    current: usize,
755    axis_count: u16,
756}
757
758impl<'a> TupleVariationHeaderIter<'a> {
759    pub(crate) fn new(data: FontData<'a>, n_headers: usize, axis_count: u16) -> Self {
760        Self {
761            data,
762            n_headers,
763            current: 0,
764            axis_count,
765        }
766    }
767}
768
769impl<'a> Iterator for TupleVariationHeaderIter<'a> {
770    type Item = Result<TupleVariationHeader<'a>, ReadError>;
771
772    #[inline(always)]
773    fn next(&mut self) -> Option<Self::Item> {
774        if self.current == self.n_headers {
775            return None;
776        }
777        self.current += 1;
778        let next = TupleVariationHeader::read(self.data, self.axis_count);
779
780        let next_len = next
781            .as_ref()
782            .map(|table| table.byte_len(self.axis_count))
783            .unwrap_or(0);
784        self.data = self.data.split_off(next_len)?;
785        Some(next)
786    }
787}
788
789#[derive(Clone)]
790pub struct TupleVariationData<'a, T> {
791    pub(crate) axis_count: u16,
792    pub(crate) shared_tuples: Option<ComputedArray<'a, Tuple<'a>>>,
793    pub(crate) shared_point_numbers: Option<PackedPointNumbers<'a>>,
794    pub(crate) tuple_count: TupleVariationCount,
795    // the data for all the tuple variation headers
796    pub(crate) header_data: FontData<'a>,
797    // the data for all the tuple bodies
798    pub(crate) serialized_data: FontData<'a>,
799    pub(crate) _marker: std::marker::PhantomData<fn() -> T>,
800}
801
802impl<'a, T> TupleVariationData<'a, T>
803where
804    T: TupleDelta,
805{
806    pub fn tuples(&self) -> TupleVariationIter<'a, T> {
807        TupleVariationIter {
808            current: 0,
809            parent: self.clone(),
810            header_iter: TupleVariationHeaderIter::new(
811                self.header_data,
812                self.tuple_count.count() as usize,
813                self.axis_count,
814            ),
815            serialized_data: self.serialized_data,
816            _marker: std::marker::PhantomData,
817        }
818    }
819
820    /// Returns an iterator over all of the pairs of (variation tuple, scalar)
821    /// for this glyph that are active for the given set of normalized
822    /// coordinates.
823    pub fn active_tuples_at<'b>(
824        &self,
825        coords: &'b [F2Dot14],
826    ) -> impl Iterator<Item = (TupleVariation<'a, T>, Fixed)> + 'b
827    where
828        'a: 'b,
829    {
830        self.active_tuples_at_with_scalars(coords, &[])
831    }
832
833    /// Returns an iterator over the tuples that apply at `coords`, reusing
834    /// `scalars`.
835    ///
836    /// `scalars` holds one entry per shared tuple, from
837    /// [`Gvar::compute_scalars`]. A tuple that names a shared peak and no
838    /// range of its own has the same scalar everywhere, so it is read from
839    /// there. A tuple with its own range, or one `scalars` does not reach, is
840    /// computed here.
841    ///
842    /// [`Gvar::compute_scalars`]: crate::tables::gvar::Gvar::compute_scalars
843    pub fn active_tuples_at_with_scalars<'b>(
844        &self,
845        coords: &'b [F2Dot14],
846        scalars: &'b [Fixed],
847    ) -> impl Iterator<Item = (TupleVariation<'a, T>, Fixed)> + 'b
848    where
849        'a: 'b,
850    {
851        ActiveTupleVariationIter {
852            coords,
853            scalars,
854            parent: self.clone(),
855            header_iter: TupleVariationHeaderIter::new(
856                self.header_data,
857                self.tuple_count.count() as usize,
858                self.axis_count,
859            ),
860            serialized_data: self.serialized_data,
861            data_offset: 0,
862            _marker: std::marker::PhantomData,
863        }
864    }
865
866    pub(crate) fn tuple_count(&self) -> usize {
867        self.tuple_count.count() as usize
868    }
869}
870
871/// An iterator over the [`TupleVariation`]s for a specific glyph.
872pub struct TupleVariationIter<'a, T> {
873    current: usize,
874    parent: TupleVariationData<'a, T>,
875    header_iter: TupleVariationHeaderIter<'a>,
876    serialized_data: FontData<'a>,
877    _marker: std::marker::PhantomData<fn() -> T>,
878}
879
880impl<'a, T> TupleVariationIter<'a, T>
881where
882    T: TupleDelta,
883{
884    #[inline(always)]
885    fn next_tuple(&mut self) -> Option<TupleVariation<'a, T>> {
886        if self.parent.tuple_count() == self.current {
887            return None;
888        }
889        self.current += 1;
890
891        // FIXME: is it okay to discard an error here?
892        let header = self.header_iter.next()?.ok()?;
893        let data_len = header.variation_data_size() as usize;
894        let var_data = self.serialized_data.take_up_to(data_len)?;
895
896        Some(TupleVariation {
897            axis_count: self.parent.axis_count,
898            header,
899            shared_tuples: self.parent.shared_tuples.clone(),
900            serialized_data: var_data,
901            shared_point_numbers: self.parent.shared_point_numbers.clone(),
902            _marker: std::marker::PhantomData,
903        })
904    }
905}
906
907impl<'a, T> Iterator for TupleVariationIter<'a, T>
908where
909    T: TupleDelta,
910{
911    type Item = TupleVariation<'a, T>;
912
913    #[inline(always)]
914    fn next(&mut self) -> Option<Self::Item> {
915        self.next_tuple()
916    }
917}
918
919/// An iterator over the active [`TupleVariation`]s for a specific glyph
920/// for a given set of coordinates.
921struct ActiveTupleVariationIter<'a, 'b, T> {
922    coords: &'b [F2Dot14],
923    scalars: &'b [Fixed],
924    parent: TupleVariationData<'a, T>,
925    header_iter: TupleVariationHeaderIter<'a>,
926    serialized_data: FontData<'a>,
927    data_offset: usize,
928    _marker: std::marker::PhantomData<fn() -> T>,
929}
930
931impl<'a, T> Iterator for ActiveTupleVariationIter<'a, '_, T>
932where
933    T: TupleDelta,
934{
935    type Item = (TupleVariation<'a, T>, Fixed);
936
937    #[inline(always)]
938    fn next(&mut self) -> Option<Self::Item> {
939        loop {
940            let header = self.header_iter.next()?.ok()?;
941            let data_len = header.variation_data_size() as usize;
942            let data_start = self.data_offset;
943            let data_end = data_start.checked_add(data_len)?;
944            self.data_offset = data_end;
945            if let Some(scalar) = compute_scalar(
946                &header,
947                self.parent.axis_count as usize,
948                &self.parent.shared_tuples,
949                self.coords,
950                self.scalars,
951            ) {
952                let var_data = self.serialized_data.slice(data_start..data_end)?;
953                return Some((
954                    TupleVariation {
955                        axis_count: self.parent.axis_count,
956                        header,
957                        shared_tuples: self.parent.shared_tuples.clone(),
958                        serialized_data: var_data,
959                        shared_point_numbers: self.parent.shared_point_numbers.clone(),
960                        _marker: std::marker::PhantomData,
961                    },
962                    scalar,
963                ));
964            }
965        }
966    }
967}
968
969/// Marks a shared tuple scalar that was never computed.
970///
971/// A glyph names shared tuples by index and need not name them all, so a
972/// slice of scalars can carry gaps. Variation store regions are dense and
973/// need no such mark.
974///
975/// No computed scalar can equal this: a scalar is a product of ratios, each
976/// held between zero and one.
977pub const NOT_COMPUTED: Fixed = Fixed::MAX;
978
979/// A single set of tuple variation data
980#[derive(Clone)]
981pub struct TupleVariation<'a, T> {
982    axis_count: u16,
983    header: TupleVariationHeader<'a>,
984    shared_tuples: Option<ComputedArray<'a, Tuple<'a>>>,
985    serialized_data: FontData<'a>,
986    shared_point_numbers: Option<PackedPointNumbers<'a>>,
987    _marker: std::marker::PhantomData<fn() -> T>,
988}
989
990impl<'a, T> TupleVariation<'a, T>
991where
992    T: TupleDelta,
993{
994    /// Returns true if this tuple provides deltas for all points in a glyph.
995    pub fn has_deltas_for_all_points(&self) -> bool {
996        if self.header.tuple_index().private_point_numbers() {
997            PackedPointNumbers {
998                data: self.serialized_data,
999            }
1000            .count()
1001                == 0
1002        } else if let Some(shared) = &self.shared_point_numbers {
1003            shared.count() == 0
1004        } else {
1005            false
1006        }
1007    }
1008
1009    pub fn point_numbers(&self) -> PackedPointNumbersIter<'a> {
1010        let (point_numbers, _) = self.point_numbers_and_packed_deltas();
1011        point_numbers.iter()
1012    }
1013
1014    /// Returns the 'peak' tuple for this variation
1015    pub fn peak(&self) -> Tuple<'a> {
1016        self.header
1017            .tuple_index()
1018            .tuple_records_index()
1019            .and_then(|idx| self.shared_tuples.as_ref()?.get(idx as usize).ok())
1020            .or_else(|| self.header.peak_tuple())
1021            .unwrap_or_default()
1022    }
1023
1024    pub fn intermediate_start(&self) -> Option<Tuple<'a>> {
1025        self.header.intermediate_start_tuple()
1026    }
1027
1028    pub fn intermediate_end(&self) -> Option<Tuple<'a>> {
1029        self.header.intermediate_end_tuple()
1030    }
1031
1032    /// Compute the fixed point scalar for this tuple at the given location in
1033    /// variation space.
1034    ///
1035    /// The `coords` slice must be of lesser or equal length to the number of
1036    /// axes. If it is less, missing (trailing) axes will be assumed to have
1037    /// zero values.
1038    ///
1039    /// Returns `None` if this tuple is not applicable at the provided
1040    /// coordinates (e.g. if the resulting scalar is zero).
1041    pub fn compute_scalar(&self, coords: &[F2Dot14]) -> Option<Fixed> {
1042        compute_scalar(
1043            &self.header,
1044            self.axis_count as usize,
1045            &self.shared_tuples,
1046            coords,
1047            &[],
1048        )
1049    }
1050
1051    /// Compute the floating point scalar for this tuple at the given location
1052    /// in variation space.
1053    ///
1054    /// The `coords` slice must be of lesser or equal length to the number of
1055    /// axes. If it is less, missing (trailing) axes will be assumed to have
1056    /// zero values.
1057    ///
1058    /// Returns `None` if this tuple is not applicable at the provided
1059    /// coordinates (e.g. if the resulting scalar is zero).
1060    pub fn compute_scalar_f32(&self, coords: &[F2Dot14]) -> Option<f32> {
1061        let mut scalar = 1.0;
1062        let peak = self.peak();
1063        let inter_start = self.header.intermediate_start_tuple();
1064        let inter_end = self.header.intermediate_end_tuple();
1065        if peak.len() != self.axis_count as usize {
1066            return None;
1067        }
1068        for i in 0..self.axis_count {
1069            let i = i as usize;
1070            let coord = coords.get(i).copied().unwrap_or_default().to_bits() as i32;
1071            let peak = peak.get(i).unwrap_or_default().to_bits() as i32;
1072            if peak == 0 || peak == coord {
1073                continue;
1074            }
1075            if coord == 0 {
1076                return None;
1077            }
1078            if let (Some(inter_start), Some(inter_end)) = (&inter_start, &inter_end) {
1079                let start = inter_start.get(i).unwrap_or_default().to_bits() as i32;
1080                let end = inter_end.get(i).unwrap_or_default().to_bits() as i32;
1081                if start > peak || peak > end || (start < 0 && end > 0 && peak != 0) {
1082                    continue;
1083                }
1084                if coord < start || coord > end {
1085                    return None;
1086                }
1087                if coord < peak {
1088                    if peak != start {
1089                        scalar *= (coord - start) as f32 / (peak - start) as f32;
1090                    }
1091                } else if peak != end {
1092                    scalar *= (end - coord) as f32 / (end - peak) as f32;
1093                }
1094            } else {
1095                if coord < peak.min(0) || coord > peak.max(0) {
1096                    return None;
1097                }
1098                scalar *= coord as f32 / peak as f32;
1099            }
1100        }
1101        Some(scalar)
1102    }
1103
1104    /// Iterate over the deltas for this tuple.
1105    ///
1106    /// This does not account for scaling. Returns only explicitly encoded
1107    /// deltas, e.g. an omission by IUP will not be present.
1108    pub fn deltas(&self) -> TupleDeltaIter<'a, T> {
1109        let (point_numbers, packed_deltas) = self.point_numbers_and_packed_deltas();
1110        let count = point_numbers.count() as usize;
1111        let packed_deltas = if count == 0 {
1112            PackedDeltas::consume_all(packed_deltas)
1113        } else {
1114            PackedDeltas::new(packed_deltas, if T::is_point() { count * 2 } else { count })
1115        };
1116        TupleDeltaIter::new(&point_numbers, packed_deltas)
1117    }
1118
1119    fn point_numbers_and_packed_deltas(&self) -> (PackedPointNumbers<'a>, FontData<'a>) {
1120        if self.header.tuple_index().private_point_numbers() {
1121            PackedPointNumbers::split_off_front(self.serialized_data)
1122        } else {
1123            (
1124                self.shared_point_numbers.clone().unwrap_or_default(),
1125                self.serialized_data,
1126            )
1127        }
1128    }
1129}
1130
1131impl TupleVariation<'_, GlyphDelta> {
1132    /// Reads the set of deltas from this tuple variation.
1133    ///
1134    /// This is significantly faster than using the [`Self::deltas`]
1135    /// method but requires preallocated memory to store deltas and
1136    /// flags.
1137    ///
1138    /// This method should only be used when the tuple variation is dense,
1139    /// that is, [`Self::has_deltas_for_all_points`] returns true.
1140    ///
1141    /// The size of `deltas` must be the same as the target value set to
1142    /// which the variation is applied. For simple outlines, this is
1143    /// `num_points + 4` and for composites it is `num_components + 4`
1144    /// (where the `+ 4` is to accommodate phantom points).
1145    ///
1146    /// The `deltas` slice will not be zeroed before accumulation and each
1147    /// delta will be multiplied by the given `scalar`.
1148    pub fn accumulate_dense_deltas<D: PointCoord>(
1149        &self,
1150        deltas: &mut [Point<D>],
1151        scalar: Fixed,
1152    ) -> Result<(), ReadError> {
1153        let (_, packed_deltas) = self.point_numbers_and_packed_deltas();
1154        let mut cursor = packed_deltas.cursor();
1155        if scalar == Fixed::ONE {
1156            // scalar of 1.0 is common so avoid the costly conversions and
1157            // multiplications per coord
1158            read_dense_deltas(&mut cursor, deltas, |delta, new_delta| {
1159                delta.x += D::from_i32(new_delta);
1160            })?;
1161            read_dense_deltas(&mut cursor, deltas, |delta, new_delta| {
1162                delta.y += D::from_i32(new_delta);
1163            })?;
1164        } else {
1165            read_dense_deltas(&mut cursor, deltas, |delta, new_delta| {
1166                delta.x += D::from_fixed(Fixed::from_i32(new_delta) * scalar);
1167            })?;
1168            read_dense_deltas(&mut cursor, deltas, |delta, new_delta| {
1169                delta.y += D::from_fixed(Fixed::from_i32(new_delta) * scalar);
1170            })?;
1171        }
1172        Ok(())
1173    }
1174
1175    /// Reads the set of deltas from this tuple variation.
1176    ///
1177    /// This is significantly faster than using the [`Self::deltas`]
1178    /// method but requires preallocated memory to store deltas and
1179    /// flags.
1180    ///
1181    /// This method should only be used when the tuple variation is sparse,
1182    /// that is, [`Self::has_deltas_for_all_points`] returns false.
1183    ///
1184    /// The size of `deltas` must be the same as the target value set to
1185    /// which the variation is applied. For simple outlines, this is
1186    /// `num_points + 4` and for composites it is `num_components + 4`
1187    /// (where the `+ 4` is to accommodate phantom points).
1188    ///
1189    /// The `deltas` and `flags` slices must be the same size. Modifications
1190    /// to `deltas` will be sparse and for each entry that is modified, the
1191    /// [PointMarker::HAS_DELTA] marker will be set for the corresponding
1192    /// entry in the `flags` slice.
1193    ///
1194    /// The `deltas` slice will not be zeroed before accumulation and each
1195    /// delta will be multiplied by the given `scalar`.
1196    pub fn accumulate_sparse_deltas<D: PointCoord>(
1197        &self,
1198        deltas: &mut [Point<D>],
1199        flags: &mut [PointFlags],
1200        scalar: Fixed,
1201    ) -> Result<(), ReadError> {
1202        let (point_numbers, packed_deltas) = self.point_numbers_and_packed_deltas();
1203        let mut cursor = packed_deltas.cursor();
1204        let count = point_numbers.count() as usize;
1205        if scalar == Fixed::ONE {
1206            // scalar of 1.0 is common so avoid the costly conversions and
1207            // multiplications per coord
1208            read_sparse_deltas(&mut cursor, &point_numbers, count, |ix, new_delta| {
1209                if let Some((delta, flag)) = deltas.get_mut(ix).zip(flags.get_mut(ix)) {
1210                    delta.x += D::from_i32(new_delta);
1211                    flag.set_marker(PointMarker::HAS_DELTA);
1212                }
1213            })?;
1214            read_sparse_deltas(&mut cursor, &point_numbers, count, |ix, new_delta| {
1215                if let Some(delta) = deltas.get_mut(ix) {
1216                    delta.y += D::from_i32(new_delta);
1217                }
1218            })?;
1219        } else {
1220            read_sparse_deltas(&mut cursor, &point_numbers, count, |ix, new_delta| {
1221                if let Some((delta, flag)) = deltas.get_mut(ix).zip(flags.get_mut(ix)) {
1222                    delta.x += D::from_fixed(Fixed::from_i32(new_delta) * scalar);
1223                    flag.set_marker(PointMarker::HAS_DELTA);
1224                }
1225            })?;
1226            read_sparse_deltas(&mut cursor, &point_numbers, count, |ix, new_delta| {
1227                if let Some(delta) = deltas.get_mut(ix) {
1228                    delta.y += D::from_fixed(Fixed::from_i32(new_delta) * scalar);
1229                }
1230            })?;
1231        }
1232        Ok(())
1233    }
1234}
1235
1236/// This is basically a manually applied loop unswitching optimization
1237/// for reading deltas. It reads each typed run into a slice for processing
1238/// instead of handling each delta individually with all the necessary
1239/// branching that implies.
1240fn read_dense_deltas<T>(
1241    cursor: &mut Cursor,
1242    deltas: &mut [T],
1243    mut f: impl FnMut(&mut T, i32),
1244) -> Result<(), ReadError> {
1245    let count = deltas.len();
1246    let mut cur = 0;
1247    while cur < count {
1248        let control: u8 = cursor.read()?;
1249        let value_type = DeltaRunType::new(control);
1250        let run_count = ((control & DELTA_RUN_COUNT_MASK) + 1) as usize;
1251        let dest = deltas
1252            .get_mut(cur..cur + run_count)
1253            .ok_or(ReadError::OutOfBounds)?;
1254        match value_type {
1255            DeltaRunType::Zero => {}
1256            DeltaRunType::I8 => {
1257                let packed_deltas = cursor.read_array::<i8>(run_count)?;
1258                for (delta, new_delta) in dest.iter_mut().zip(packed_deltas) {
1259                    f(delta, *new_delta as i32);
1260                }
1261            }
1262            DeltaRunType::I16 => {
1263                let packed_deltas = cursor.read_array::<BigEndian<i16>>(run_count)?;
1264                for (delta, new_delta) in dest.iter_mut().zip(packed_deltas) {
1265                    f(delta, new_delta.get() as i32);
1266                }
1267            }
1268            DeltaRunType::I32 => {
1269                let packed_deltas = cursor.read_array::<BigEndian<i32>>(run_count)?;
1270                for (delta, new_delta) in dest.iter_mut().zip(packed_deltas) {
1271                    f(delta, new_delta.get());
1272                }
1273            }
1274        }
1275        cur += run_count;
1276    }
1277    Ok(())
1278}
1279
1280/// See [read_dense_deltas] docs.
1281fn read_sparse_deltas(
1282    cursor: &mut Cursor,
1283    point_numbers: &PackedPointNumbers,
1284    count: usize,
1285    mut f: impl FnMut(usize, i32),
1286) -> Result<(), ReadError> {
1287    let mut cur = 0;
1288    let mut points_iter = point_numbers.iter().map(|ix| ix as usize);
1289    while cur < count {
1290        let control: u8 = cursor.read()?;
1291        let value_type = DeltaRunType::new(control);
1292        let run_count = ((control & DELTA_RUN_COUNT_MASK) + 1) as usize;
1293        match value_type {
1294            DeltaRunType::Zero => {
1295                for _ in 0..run_count {
1296                    let point_ix = points_iter.next().ok_or(ReadError::OutOfBounds)?;
1297                    f(point_ix, 0);
1298                }
1299            }
1300            DeltaRunType::I8 => {
1301                let packed_deltas = cursor.read_array::<i8>(run_count)?;
1302                for (new_delta, point_ix) in packed_deltas.iter().zip(points_iter.by_ref()) {
1303                    f(point_ix, *new_delta as i32);
1304                }
1305            }
1306            DeltaRunType::I16 => {
1307                let packed_deltas = cursor.read_array::<BigEndian<i16>>(run_count)?;
1308                for (new_delta, point_ix) in packed_deltas.iter().zip(points_iter.by_ref()) {
1309                    f(point_ix, new_delta.get() as i32);
1310                }
1311            }
1312            DeltaRunType::I32 => {
1313                let packed_deltas = cursor.read_array::<BigEndian<i32>>(run_count)?;
1314                for (new_delta, point_ix) in packed_deltas.iter().zip(points_iter.by_ref()) {
1315                    f(point_ix, new_delta.get());
1316                }
1317            }
1318        }
1319        cur += run_count;
1320    }
1321    Ok(())
1322}
1323
1324/// Compute the fixed point scalar for this tuple at the given location in
1325/// variation space.
1326///
1327/// The `coords` slice must be of lesser or equal length to the number of
1328/// axes. If it is less, missing (trailing) axes will be assumed to have
1329/// zero values.
1330///
1331/// Returns `None` if this tuple is not applicable at the provided
1332/// coordinates (e.g. if the resulting scalar is zero).
1333#[inline(always)]
1334fn compute_scalar<'a>(
1335    header: &TupleVariationHeader,
1336    axis_count: usize,
1337    shared_tuples: &Option<ComputedArray<'a, Tuple<'a>>>,
1338    coords: &[F2Dot14],
1339    scalars: &[Fixed],
1340) -> Option<Fixed> {
1341    let tuple_idx = header.tuple_index();
1342    let intermediate = header.intermediate_tuples();
1343    let peak = if let Some(shared_index) = tuple_idx.tuple_records_index() {
1344        // A shared peak with no range of its own has the same scalar
1345        // wherever it is named, so a cached value applies.
1346        if intermediate.is_none() {
1347            match scalars.get(shared_index as usize) {
1348                Some(scalar) if *scalar != NOT_COMPUTED => {
1349                    return (*scalar != Fixed::ZERO).then_some(*scalar);
1350                }
1351                _ => {}
1352            }
1353        }
1354        shared_tuples.as_ref()?.get(shared_index as usize).ok()?
1355    } else {
1356        header.peak_tuple()?
1357    };
1358    if peak.len() != axis_count {
1359        return None;
1360    }
1361    scalar_for(&peak, intermediate, coords)
1362}
1363
1364/// Computes the scalar for a peak at `coords`, interpolating over the tuple's
1365/// range if it states one.
1366///
1367/// `None` where the tuple does not apply, which a scalar of zero also
1368/// expresses.
1369pub(crate) fn scalar_for(
1370    peak: &Tuple,
1371    intermediate: Option<(Tuple, Tuple)>,
1372    coords: &[F2Dot14],
1373) -> Option<Fixed> {
1374    let mut scalar = Fixed::ONE;
1375    for (i, peak) in peak
1376        .values
1377        .iter()
1378        .enumerate()
1379        .filter(|(_, peak)| peak.get() != F2Dot14::ZERO)
1380    {
1381        let coord = coords.get(i).copied().unwrap_or_default();
1382        if coord == F2Dot14::ZERO {
1383            return None;
1384        }
1385        let peak = peak.get();
1386        if peak == coord {
1387            continue;
1388        }
1389        if let Some((inter_start, inter_end)) = &intermediate {
1390            let start = inter_start.get(i).unwrap_or_default();
1391            let end = inter_end.get(i).unwrap_or_default();
1392            // A region that does not enclose its own peak, or that straddles
1393            // the default, is invalid and contributes a factor of one. This
1394            // mirrors the OpenType algorithm and the sibling scalar
1395            // computations (`compute_scalar_f32`, `VariationRegion::compute_scalar`);
1396            // without it a malformed region interpolates where it should be
1397            // skipped.
1398            if start > peak || peak > end || (start < F2Dot14::ZERO && end > F2Dot14::ZERO) {
1399                continue;
1400            }
1401            if coord <= start || coord >= end {
1402                return None;
1403            }
1404            let coord = coord.to_fixed();
1405            let peak = peak.to_fixed();
1406            if coord < peak {
1407                let start = start.to_fixed();
1408                scalar = scalar.mul_div(coord - start, peak - start);
1409            } else {
1410                let end = end.to_fixed();
1411                scalar = scalar.mul_div(end - coord, end - peak);
1412            }
1413        } else {
1414            if coord < peak.min(F2Dot14::ZERO) || coord > peak.max(F2Dot14::ZERO) {
1415                return None;
1416            }
1417            let coord = coord.to_fixed();
1418            let peak = peak.to_fixed();
1419            scalar = scalar.mul_div(coord, peak);
1420        }
1421    }
1422    (scalar != Fixed::ZERO).then_some(scalar)
1423}
1424
1425#[derive(Clone, Debug)]
1426enum TupleDeltaValues<'a> {
1427    // Point deltas have separate runs for x and y coordinates.
1428    Points(DeltaRunIter<'a>, DeltaRunIter<'a>),
1429    Scalars(DeltaRunIter<'a>),
1430}
1431
1432/// An iterator over the deltas for a glyph.
1433#[derive(Clone, Debug)]
1434pub struct TupleDeltaIter<'a, T> {
1435    pub cur: usize,
1436    // if None all points get deltas, if Some specifies subset of points that do
1437    points: Option<PackedPointNumbersIter<'a>>,
1438    next_point: usize,
1439    values: TupleDeltaValues<'a>,
1440    _marker: std::marker::PhantomData<fn() -> T>,
1441}
1442
1443impl<'a, T> TupleDeltaIter<'a, T>
1444where
1445    T: TupleDelta,
1446{
1447    fn new(points: &PackedPointNumbers<'a>, deltas: PackedDeltas<'a>) -> TupleDeltaIter<'a, T> {
1448        let mut points = points.iter();
1449        let next_point = points.next();
1450        let values = if T::is_point() {
1451            TupleDeltaValues::Points(deltas.x_deltas(), deltas.y_deltas())
1452        } else {
1453            TupleDeltaValues::Scalars(deltas.iter())
1454        };
1455        TupleDeltaIter {
1456            cur: 0,
1457            points: next_point.map(|_| points),
1458            next_point: next_point.unwrap_or_default() as usize,
1459            values,
1460            _marker: std::marker::PhantomData,
1461        }
1462    }
1463}
1464
1465/// Trait for deltas that are computed in a tuple variation store.
1466pub trait TupleDelta: Sized + Copy + 'static {
1467    /// Returns true if the delta is a point and requires reading two values
1468    /// from the packed delta stream.
1469    fn is_point() -> bool;
1470
1471    /// Creates a new delta for the given position and coordinates. If
1472    /// the delta is not a point, the y value will always be zero.
1473    fn new(position: u16, x: i32, y: i32) -> Self;
1474}
1475
1476impl<T> Iterator for TupleDeltaIter<'_, T>
1477where
1478    T: TupleDelta,
1479{
1480    type Item = T;
1481
1482    fn next(&mut self) -> Option<Self::Item> {
1483        let (position, dx, dy) = loop {
1484            let position = if let Some(points) = &mut self.points {
1485                // if we have points then result is sparse; only some points have deltas
1486                if self.cur > self.next_point {
1487                    self.next_point = points.next()? as usize;
1488                }
1489                self.next_point
1490            } else {
1491                // no points, every point has a delta. Just take the next one.
1492                self.cur
1493            };
1494            if position == self.cur {
1495                let (dx, dy) = match &mut self.values {
1496                    TupleDeltaValues::Points(x, y) => (x.next()?, y.next()?),
1497                    TupleDeltaValues::Scalars(scalars) => (scalars.next()?, 0),
1498                };
1499                break (position, dx, dy);
1500            }
1501            self.cur += 1;
1502        };
1503        self.cur += 1;
1504        Some(T::new(position as u16, dx, dy))
1505    }
1506}
1507
1508impl EntryFormat {
1509    pub fn entry_size(self) -> u8 {
1510        ((self.bits() & Self::MAP_ENTRY_SIZE_MASK.bits()) >> 4) + 1
1511    }
1512
1513    pub fn bit_count(self) -> u8 {
1514        (self.bits() & Self::INNER_INDEX_BIT_COUNT_MASK.bits()) + 1
1515    }
1516
1517    // called from codegen
1518    pub(crate) fn map_size(self, map_count: impl Into<u32>) -> usize {
1519        self.entry_size() as usize * map_count.into() as usize
1520    }
1521}
1522
1523impl DeltaSetIndexMap<'_> {
1524    /// Returns the delta set index for the specified value.
1525    pub fn get(&self, index: u32) -> Result<DeltaSetIndex, ReadError> {
1526        let (entry_format, map_count, data) = match self {
1527            Self::Format0(fmt) => (fmt.entry_format(), fmt.map_count() as u32, fmt.map_data()),
1528            Self::Format1(fmt) => (fmt.entry_format(), fmt.map_count(), fmt.map_data()),
1529        };
1530        if map_count == 0 {
1531            return Ok(DeltaSetIndex {
1532                outer: (index >> 16) as u16,
1533                inner: index as u16,
1534            });
1535        }
1536        let entry_size = entry_format.entry_size();
1537        let data = FontData::new(data);
1538        // "if an index into the mapping array is used that is greater than or equal to
1539        // mapCount, then the last logical entry of the mapping array is used."
1540        // https://learn.microsoft.com/en-us/typography/opentype/spec/otvarcommonformats
1541        // #associating-target-items-to-variation-data
1542        let index = index.min(map_count.saturating_sub(1));
1543        let offset = index as usize * entry_size as usize;
1544        let entry = match entry_size {
1545            1 => data.read_at::<u8>(offset)? as u32,
1546            2 => data.read_at::<u16>(offset)? as u32,
1547            3 => data.read_at::<Uint24>(offset)?.into(),
1548            4 => data.read_at::<u32>(offset)?,
1549            _ => {
1550                return Err(ReadError::MalformedData(
1551                    "invalid entry size in DeltaSetIndexMap",
1552                ))
1553            }
1554        };
1555        let bit_count = entry_format.bit_count();
1556        Ok(DeltaSetIndex {
1557            outer: (entry >> bit_count) as u16,
1558            inner: (entry & ((1 << bit_count) - 1)) as u16,
1559        })
1560    }
1561}
1562
1563impl ItemVariationStore<'_> {
1564    /// Computes the delta value for the specified index and set of
1565    /// normalized variation coordinates.
1566    ///
1567    /// Each region's scalar is computed in 16.16 and multiplied by its raw
1568    /// delta, and the products are summed exactly -- 48.16 holds the largest
1569    /// possible sum with room to spare. Use [`F48Dot16::to_i32`] for the
1570    /// classic integer delta, or apply the value unrounded to targets that
1571    /// take fractional deltas.
1572    pub fn compute_delta(&self, index: DeltaSetIndex, coords: &[F2Dot14]) -> Option<F48Dot16> {
1573        self.compute_delta_with_scalars(index, coords, &[])
1574    }
1575
1576    /// Computes the delta for `index` at `coords`, reusing `scalars`.
1577    ///
1578    /// `scalars` holds one entry per variation region, from
1579    /// [`compute_scalars`](Self::compute_scalars). It may be empty or cover
1580    /// only some of them; the rest are computed here.
1581    pub fn compute_delta_with_scalars(
1582        &self,
1583        index: DeltaSetIndex,
1584        coords: &[F2Dot14],
1585        scalars: &[Fixed],
1586    ) -> Option<F48Dot16> {
1587        if coords.is_empty() || index == DeltaSetIndex::NO_VARIATION_INDEX {
1588            return Some(F48Dot16::ZERO);
1589        }
1590        let data = match self.item_variation_data().get(index.outer as usize) {
1591            Some(data) => data.ok()?,
1592            None => return Some(F48Dot16::ZERO),
1593        };
1594        let region_indices = data.region_indexes();
1595        // Read only when a scalar has to be computed. A run that finds all
1596        // of them in `scalars` never touches the region list.
1597        let mut regions = None;
1598        // Compute deltas with 64-bit precision.
1599        // See <https://gitlab.freedesktop.org/freetype/freetype/-/blob/7ab541a2/src/truetype/ttgxvar.c#L1094>
1600        let mut accum = F48Dot16::ZERO;
1601        // The deltas and the region indices are parallel arrays sized by the
1602        // same header field, so they are walked together.
1603        for (region_index, region_delta) in region_indices.iter().zip(data.delta_set(index.inner)) {
1604            let region_index = region_index.get() as usize;
1605            let scalar = match scalars.get(region_index) {
1606                Some(scalar) => *scalar,
1607                None => {
1608                    let regions = match &regions {
1609                        Some(regions) => regions,
1610                        None => {
1611                            regions.insert(self.variation_region_list().ok()?.variation_regions())
1612                        }
1613                    };
1614                    regions.get(region_index).ok()?.compute_scalar(coords)
1615                }
1616            };
1617            // The sum cannot overflow, even for hostile data: a scalar is a
1618            // product of ratios that the range guards keep at most one, so
1619            // each term is under 2^47, and at most 2^16 - 1 regions bounds
1620            // the total below 2^63.
1621            accum += scalar.mul_i32(region_delta);
1622        }
1623        Some(accum)
1624    }
1625
1626    /// Computes the scalar for each variation region at `coords`, in store
1627    /// order, and returns how many were written.
1628    ///
1629    /// A region that does not apply at `coords` is written as zero, which is
1630    /// the value it contributes.
1631    ///
1632    /// `out` may be shorter than the store has regions, and an unreadable
1633    /// store writes nothing.
1634    /// [`compute_delta_with_scalars`](Self::compute_delta_with_scalars)
1635    /// computes whatever is missing.
1636    ///
1637    /// Pass back only the entries this wrote. Regions are dense, so the
1638    /// length of the slice is what identifies them.
1639    pub fn compute_scalars(&self, coords: &[F2Dot14], out: &mut [Fixed]) -> usize {
1640        let Ok(list) = self.variation_region_list() else {
1641            return 0;
1642        };
1643        let regions = list.variation_regions();
1644        let count = out.len().min(regions.len());
1645        for (i, out) in out[..count].iter_mut().enumerate() {
1646            // An unreadable region ends the run. Entries already written
1647            // stay valid; the caller computes the rest.
1648            let Ok(region) = regions.get(i) else {
1649                return i;
1650            };
1651            *out = region.compute_scalar(coords);
1652        }
1653        count
1654    }
1655}
1656
1657impl<'a> VariationRegion<'a> {
1658    /// Computes a scalar value for this region and the specified
1659    /// normalized variation coordinates.
1660    pub fn compute_scalar(&self, coords: &[F2Dot14]) -> Fixed {
1661        const ZERO: Fixed = Fixed::ZERO;
1662        let mut scalar = Fixed::ONE;
1663        for (i, peak, axis_coords) in self.active_region_axes() {
1664            // A coordinate pinned to the peak contributes a factor of one,
1665            // whatever the rest of the axis says: if the axis is invalid or
1666            // the peak sits at an edge, the outcome is `continue` on every
1667            // path below. Checking it first, in raw 2.14, skips the start
1668            // and end reads and their conversions for the common case of an
1669            // instance sitting on a region's corner.
1670            let raw_coord = coords.get(i).copied().unwrap_or_default();
1671            if raw_coord == peak {
1672                continue;
1673            }
1674            let peak = peak.to_fixed();
1675            let start = axis_coords.start_coord.get().to_fixed();
1676            let end = axis_coords.end_coord.get().to_fixed();
1677            if start > peak || peak > end || start < ZERO && end > ZERO {
1678                continue;
1679            }
1680            let coord = raw_coord.to_fixed();
1681            if coord < start || coord > end {
1682                return ZERO;
1683            } else if coord < peak {
1684                scalar = scalar.mul_div(coord - start, peak - start);
1685            } else {
1686                scalar = scalar.mul_div(end - coord, end - peak);
1687            }
1688        }
1689        scalar
1690    }
1691
1692    fn active_region_axes(
1693        &self,
1694    ) -> impl Iterator<Item = (usize, F2Dot14, &'a RegionAxisCoordinates)> {
1695        self.region_axes()
1696            .iter()
1697            .enumerate()
1698            .filter_map(|(i, axis_coords)| {
1699                let peak = axis_coords.peak_coord();
1700                if peak != F2Dot14::ZERO {
1701                    Some((i, peak, axis_coords))
1702                } else {
1703                    None
1704                }
1705            })
1706    }
1707}
1708
1709impl<'a> ItemVariationData<'a> {
1710    /// Returns an iterator over the per-region delta values for the specified
1711    /// inner index.
1712    pub fn delta_set(&self, inner_index: u16) -> impl Iterator<Item = i32> + 'a + Clone {
1713        let word_delta_count = self.word_delta_count();
1714        let region_count = self.region_index_count();
1715        let bytes_per_row = Self::delta_row_len(word_delta_count, region_count);
1716        let long_words = word_delta_count & 0x8000 != 0;
1717        let word_delta_count = word_delta_count & 0x7FFF;
1718
1719        let offset = bytes_per_row * inner_index as usize;
1720        ItemDeltas {
1721            bytes: self.delta_sets().get(offset..).unwrap_or_default().iter(),
1722            word_delta_count,
1723            long_words,
1724            len: region_count,
1725            pos: 0,
1726        }
1727    }
1728
1729    pub fn get_delta_row_len(&self) -> usize {
1730        let word_delta_count = self.word_delta_count();
1731        let region_count = self.region_index_count();
1732        Self::delta_row_len(word_delta_count, region_count)
1733    }
1734
1735    /// the length of one delta set
1736    pub fn delta_row_len(word_delta_count: u16, region_index_count: u16) -> usize {
1737        let region_count = region_index_count as usize;
1738        let long_words = word_delta_count & 0x8000 != 0;
1739        let (word_size, small_size) = if long_words { (4, 2) } else { (2, 1) };
1740        let long_delta_count = (word_delta_count & 0x7FFF) as usize;
1741        let short_delta_count = region_count.saturating_sub(long_delta_count);
1742        long_delta_count * word_size + short_delta_count * small_size
1743    }
1744
1745    // called from generated code: compute the length in bytes of the delta_sets data
1746    pub fn delta_sets_len(
1747        item_count: u16,
1748        word_delta_count: u16,
1749        region_index_count: u16,
1750    ) -> usize {
1751        let bytes_per_row = Self::delta_row_len(word_delta_count, region_index_count);
1752        bytes_per_row * item_count as usize
1753    }
1754}
1755
1756#[derive(Clone)]
1757struct ItemDeltas<'a> {
1758    bytes: core::slice::Iter<'a, u8>,
1759    word_delta_count: u16,
1760    long_words: bool,
1761    len: u16,
1762    pos: u16,
1763}
1764
1765impl Iterator for ItemDeltas<'_> {
1766    type Item = i32;
1767
1768    fn next(&mut self) -> Option<Self::Item> {
1769        if self.pos >= self.len {
1770            return None;
1771        }
1772        let pos = self.pos;
1773        self.pos += 1;
1774        let mut byte = || self.bytes.next().copied();
1775        let value = match (pos >= self.word_delta_count, self.long_words) {
1776            (true, true) | (false, false) => i16::from_be_bytes([byte()?, byte()?]) as i32,
1777            (true, false) => byte()? as i8 as i32,
1778            (false, true) => i32::from_be_bytes([byte()?, byte()?, byte()?, byte()?]),
1779        };
1780        Some(value)
1781    }
1782}
1783
1784/// The delta for a glyph's advance, reusing `scalars`.
1785///
1786/// Keeps every bit the variation store computed. Rounding it to a whole
1787/// design unit is left to a caller, and implementations differ on how.
1788pub(crate) fn advance_delta_with_scalars(
1789    dsim: Option<Result<DeltaSetIndexMap, ReadError>>,
1790    ivs: Result<ItemVariationStore, ReadError>,
1791    glyph_id: GlyphId,
1792    coords: &[F2Dot14],
1793    scalars: &[Fixed],
1794) -> Option<F48Dot16> {
1795    if coords.is_empty() {
1796        return Some(F48Dot16::ZERO);
1797    }
1798    let gid = glyph_id.to_u32();
1799    let ix = match dsim {
1800        Some(Ok(dsim)) => dsim.get(gid).ok()?,
1801        // Implicit mappings use outer index zero; an oversized glyph ID
1802        // cannot address an inner row and must not wrap to a different glyph.
1803        _ if gid > u16::MAX as u32 => return Some(F48Dot16::ZERO),
1804        _ => DeltaSetIndex {
1805            outer: 0,
1806            inner: gid as _,
1807        },
1808    };
1809    ivs.ok()?.compute_delta_with_scalars(ix, coords, scalars)
1810}
1811
1812/// The delta for an item, reusing `scalars`.
1813///
1814/// As [`advance_delta_with_scalars`], but for mappings that require an index
1815/// map instead of falling back to the glyph id.
1816pub(crate) fn item_delta_with_scalars(
1817    dsim: Option<Result<DeltaSetIndexMap, ReadError>>,
1818    ivs: Result<ItemVariationStore, ReadError>,
1819    glyph_id: GlyphId,
1820    coords: &[F2Dot14],
1821    scalars: &[Fixed],
1822) -> Option<F48Dot16> {
1823    if coords.is_empty() {
1824        return Some(F48Dot16::ZERO);
1825    }
1826    let gid = glyph_id.to_u32();
1827    let ix = match dsim {
1828        Some(Ok(dsim)) => dsim.get(gid).ok()?,
1829        _ => return None,
1830    };
1831    ivs.ok()?.compute_delta_with_scalars(ix, coords, scalars)
1832}
1833
1834#[cfg(test)]
1835mod tests {
1836    use font_test_data::bebuffer::BeBuffer;
1837
1838    use super::*;
1839    use crate::{FontRef, TableProvider};
1840
1841    #[test]
1842    fn implicit_advance_indices_do_not_truncate_glyph_ids() {
1843        let bytes = BeBuffer::new()
1844            .push(1u16) // ItemVariationStore format
1845            .push(12u32) // region list offset
1846            .push(1u16) // variation data count
1847            .push(22u32) // variation data offset
1848            .push(1u16) // axis count
1849            .push(1u16) // region count
1850            .extend([F2Dot14::ZERO, F2Dot14::ONE, F2Dot14::ONE])
1851            .push(2u16) // item count
1852            .push(1u16) // word delta count
1853            .push(1u16) // region index count
1854            .push(0u16) // region index
1855            .extend([10i16, 100i16]);
1856        let store = ItemVariationStore::read(FontData::new(&bytes)).unwrap();
1857        let mapping = DeltaSetIndexMap::read(FontData::new(&[0, 0, 0, 1, 0])).unwrap();
1858        let coords = [F2Dot14::from_f32(0.75)];
1859        let mut scalars = [Fixed::ZERO; 32];
1860        let scalar_count = store.compute_scalars(&coords, &mut scalars);
1861        for scalars in [&[][..], &scalars[..scalar_count]] {
1862            let delta = advance_delta_with_scalars(
1863                None,
1864                Ok(store.clone()),
1865                GlyphId::new(1),
1866                &coords,
1867                scalars,
1868            );
1869            assert!(delta.is_some_and(|delta| delta != F48Dot16::ZERO));
1870            for gid in [0xFFFF, 0x10000, 0x10001, 0xFFFF0001] {
1871                assert_eq!(
1872                    advance_delta_with_scalars(
1873                        None,
1874                        Ok(store.clone()),
1875                        GlyphId::new(gid),
1876                        &coords,
1877                        scalars,
1878                    ),
1879                    Some(F48Dot16::ZERO),
1880                    "implicit advance mapping wrapped glyph ID {gid}"
1881                );
1882            }
1883            // Explicit mappings keep their last-entry repetition for high IDs.
1884            for gid in [0x10000, 0x10001] {
1885                assert_eq!(
1886                    advance_delta_with_scalars(
1887                        Some(Ok(mapping.clone())),
1888                        Ok(store.clone()),
1889                        GlyphId::new(gid),
1890                        &coords,
1891                        scalars,
1892                    ),
1893                    Some(F48Dot16::from_f64(7.5))
1894                );
1895            }
1896        }
1897    }
1898
1899    #[test]
1900    fn ivs_regions() {
1901        let font = FontRef::new(font_test_data::VAZIRMATN_VAR).unwrap();
1902        let hvar = font.hvar().expect("missing HVAR table");
1903        let ivs = hvar
1904            .item_variation_store()
1905            .expect("missing item variation store in HVAR");
1906        let region_list = ivs.variation_region_list().expect("missing region list!");
1907        let regions = region_list.variation_regions();
1908        let expected = &[
1909            // start_coord, peak_coord, end_coord
1910            vec![[-1.0f32, -1.0, 0.0]],
1911            vec![[0.0, 1.0, 1.0]],
1912        ][..];
1913        let region_coords = regions
1914            .iter()
1915            .map(|region| {
1916                region
1917                    .unwrap()
1918                    .region_axes()
1919                    .iter()
1920                    .map(|coords| {
1921                        [
1922                            coords.start_coord().to_f32(),
1923                            coords.peak_coord().to_f32(),
1924                            coords.end_coord().to_f32(),
1925                        ]
1926                    })
1927                    .collect::<Vec<_>>()
1928            })
1929            .collect::<Vec<_>>();
1930        assert_eq!(expected, &region_coords);
1931    }
1932
1933    // A gvar/cvar intermediate region that does not enclose its own peak, or
1934    // that straddles the default, must be ignored (contribute a factor of one)
1935    // rather than interpolated. This matches the OpenType algorithm and the
1936    // sibling `compute_scalar_f32` / `VariationRegion::compute_scalar`, which
1937    // already apply the guard.
1938    #[test]
1939    fn scalar_for_ignores_invalid_intermediate_region() {
1940        fn tuple(values: &[BigEndian<F2Dot14>]) -> Tuple<'_> {
1941            Tuple { values }
1942        }
1943        let f = F2Dot14::from_f32;
1944
1945        // start > peak: the coord sits inside (start, end) but the region does
1946        // not contain the peak, so the axis is skipped. Before the guard this
1947        // interpolated to (end - coord) / (end - peak) = 0.2.
1948        let peak: [BigEndian<F2Dot14>; 1] = [f(0.5).into()];
1949        let start: [BigEndian<F2Dot14>; 1] = [f(0.8).into()];
1950        let end: [BigEndian<F2Dot14>; 1] = [f(1.0).into()];
1951        let coords = [f(0.9)];
1952        assert_eq!(
1953            scalar_for(&tuple(&peak), Some((tuple(&start), tuple(&end))), &coords),
1954            Some(Fixed::ONE)
1955        );
1956
1957        // peak > end.
1958        let peak2: [BigEndian<F2Dot14>; 1] = [f(1.0).into()];
1959        let start2: [BigEndian<F2Dot14>; 1] = [f(0.0).into()];
1960        let end2: [BigEndian<F2Dot14>; 1] = [f(0.5).into()];
1961        let coords2 = [f(0.25)];
1962        assert_eq!(
1963            scalar_for(
1964                &tuple(&peak2),
1965                Some((tuple(&start2), tuple(&end2))),
1966                &coords2
1967            ),
1968            Some(Fixed::ONE)
1969        );
1970
1971        // Region straddling the default (start < 0 < end).
1972        let start3: [BigEndian<F2Dot14>; 1] = [f(-0.5).into()];
1973        let end3: [BigEndian<F2Dot14>; 1] = [f(1.0).into()];
1974        let coords3 = [f(0.25)];
1975        assert_eq!(
1976            scalar_for(
1977                &tuple(&peak),
1978                Some((tuple(&start3), tuple(&end3))),
1979                &coords3
1980            ),
1981            Some(Fixed::ONE)
1982        );
1983
1984        // A well-formed region (start <= peak <= end) still interpolates.
1985        let start4: [BigEndian<F2Dot14>; 1] = [f(0.0).into()];
1986        let end4: [BigEndian<F2Dot14>; 1] = [f(1.0).into()];
1987        let coords4 = [f(0.25)];
1988        let scalar = scalar_for(
1989            &tuple(&peak),
1990            Some((tuple(&start4), tuple(&end4))),
1991            &coords4,
1992        )
1993        .unwrap();
1994        assert!((scalar.to_f32() - 0.5).abs() < 1e-4);
1995    }
1996
1997    // adapted from https://github.com/fonttools/fonttools/blob/f73220816264fc383b8a75f2146e8d69e455d398/Tests/ttLib/tables/TupleVariation_test.py#L492
1998    #[test]
1999    fn packed_points() {
2000        fn decode_points(bytes: &[u8]) -> Option<Vec<u16>> {
2001            let data = FontData::new(bytes);
2002            let packed = PackedPointNumbers { data };
2003            if packed.count() == 0 {
2004                None
2005            } else {
2006                Some(packed.iter().collect())
2007            }
2008        }
2009
2010        assert_eq!(decode_points(&[0]), None);
2011        // all points in glyph (in overly verbose encoding, not explicitly prohibited by spec)
2012        assert_eq!(decode_points(&[0x80, 0]), None);
2013        // 2 points; first run: [9, 9+6]
2014        assert_eq!(decode_points(&[0x02, 0x01, 0x09, 0x06]), Some(vec![9, 15]));
2015        // 2 points; first run: [0xBEEF, 0xCAFE]. (0x0C0F = 0xCAFE - 0xBEEF)
2016        assert_eq!(
2017            decode_points(&[0x02, 0x81, 0xbe, 0xef, 0x0c, 0x0f]),
2018            Some(vec![0xbeef, 0xcafe])
2019        );
2020        // 1 point; first run: [7]
2021        assert_eq!(decode_points(&[0x01, 0, 0x07]), Some(vec![7]));
2022        // 1 point; first run: [7] in overly verbose encoding
2023        assert_eq!(decode_points(&[0x01, 0x80, 0, 0x07]), Some(vec![7]));
2024        // 1 point; first run: [65535]; requires words to be treated as unsigned numbers
2025        assert_eq!(decode_points(&[0x01, 0x80, 0xff, 0xff]), Some(vec![65535]));
2026        // 4 points; first run: [7, 8]; second run: [255, 257]. 257 is stored in delta-encoded bytes (0xFF + 2).
2027        assert_eq!(
2028            decode_points(&[0x04, 1, 7, 1, 1, 0xff, 2]),
2029            Some(vec![7, 8, 263, 265])
2030        );
2031    }
2032
2033    #[test]
2034    fn packed_point_byte_len() {
2035        fn count_bytes(bytes: &[u8]) -> usize {
2036            let packed = PackedPointNumbers {
2037                data: FontData::new(bytes),
2038            };
2039            packed.total_len()
2040        }
2041
2042        static CASES: &[&[u8]] = &[
2043            &[0],
2044            &[0x80, 0],
2045            &[0x02, 0x01, 0x09, 0x06],
2046            &[0x02, 0x81, 0xbe, 0xef, 0x0c, 0x0f],
2047            &[0x01, 0, 0x07],
2048            &[0x01, 0x80, 0, 0x07],
2049            &[0x01, 0x80, 0xff, 0xff],
2050            &[0x04, 1, 7, 1, 1, 0xff, 2],
2051        ];
2052
2053        for case in CASES {
2054            assert_eq!(count_bytes(case), case.len(), "{case:?}");
2055        }
2056    }
2057
2058    // https://github.com/fonttools/fonttools/blob/c30a6355ffdf7f09d31e7719975b4b59bac410af/Tests/ttLib/tables/TupleVariation_test.py#L670
2059    #[test]
2060    fn packed_deltas() {
2061        static INPUT: FontData = FontData::new(&[0x83, 0x40, 0x01, 0x02, 0x01, 0x81, 0x80]);
2062
2063        let deltas = PackedDeltas::consume_all(INPUT);
2064        assert_eq!(deltas.count_or_compute(), 7);
2065        assert_eq!(
2066            deltas.iter().collect::<Vec<_>>(),
2067            &[0, 0, 0, 0, 258, -127, -128]
2068        );
2069
2070        assert_eq!(
2071            PackedDeltas::consume_all(FontData::new(&[0x81]))
2072                .iter()
2073                .collect::<Vec<_>>(),
2074            &[0, 0,]
2075        );
2076    }
2077
2078    // https://learn.microsoft.com/en-us/typography/opentype/spec/otvarcommonformats#packed-deltas
2079    #[test]
2080    fn packed_deltas_spec() {
2081        static INPUT: FontData = FontData::new(&[
2082            0x03, 0x0A, 0x97, 0x00, 0xC6, 0x87, 0x41, 0x10, 0x22, 0xFB, 0x34,
2083        ]);
2084        static EXPECTED: &[i32] = &[10, -105, 0, -58, 0, 0, 0, 0, 0, 0, 0, 0, 4130, -1228];
2085
2086        let deltas = PackedDeltas::consume_all(INPUT);
2087        assert_eq!(deltas.count_or_compute(), EXPECTED.len());
2088        assert_eq!(deltas.iter().collect::<Vec<_>>(), EXPECTED);
2089    }
2090
2091    #[test]
2092    fn packed_delta_fetcher_skip_matches_iterator_suffix() {
2093        static INPUT: FontData = FontData::new(&[
2094            0x03, 0x0A, 0x97, 0x00, 0xC6, 0x87, 0x41, 0x10, 0x22, 0xFB, 0x34,
2095        ]);
2096        let deltas = PackedDeltas::consume_all(INPUT);
2097        let expected = deltas.iter().collect::<Vec<_>>();
2098
2099        for skip in 0..=expected.len() {
2100            let mut fetcher = deltas.fetcher();
2101            fetcher.skip(skip).unwrap();
2102            let mut out = vec![0.0; expected.len() - skip];
2103            fetcher.add_to_f32_scaled(&mut out, 1.0).unwrap();
2104            let got = out.into_iter().map(|v| v as i32).collect::<Vec<_>>();
2105            assert_eq!(&got[..], &expected[skip..], "skip={skip}");
2106        }
2107
2108        let mut fetcher = deltas.fetcher();
2109        assert!(matches!(
2110            fetcher.skip(expected.len() + 1),
2111            Err(ReadError::OutOfBounds)
2112        ));
2113    }
2114
2115    #[test]
2116    fn packed_delta_fetcher_scaled_add_and_exhaustion() {
2117        static INPUT: FontData = FontData::new(&[
2118            0x03, 0x0A, 0x97, 0x00, 0xC6, 0x87, 0x41, 0x10, 0x22, 0xFB, 0x34,
2119        ]);
2120        // First four deltas are [10, -105, 0, -58].
2121        let deltas = PackedDeltas::new(INPUT, 4);
2122        let mut fetcher = deltas.fetcher();
2123        let mut out = [1.0f32; 4];
2124        fetcher.add_to_f32_scaled(&mut out, 0.5).unwrap();
2125        assert_eq!(out, [6.0, -51.5, 1.0, -28.0]);
2126
2127        // Bounded fetcher should now be exhausted.
2128        let mut extra = [0.0f32; 1];
2129        assert!(matches!(
2130            fetcher.add_to_f32_scaled(&mut extra, 1.0),
2131            Err(ReadError::OutOfBounds)
2132        ));
2133    }
2134
2135    #[test]
2136    fn packed_delta_fetcher_f64_preserves_i32_residuals() {
2137        static INPUT: FontData =
2138            FontData::new(&[0xC1, 0x01, 0x00, 0x00, 0x01, 0xFF, 0x00, 0x00, 0x00]);
2139        let mut fetcher = PackedDeltas::new(INPUT, 2).fetcher();
2140        let mut out = [0.0f64];
2141        fetcher.add_to_f64_scaled(&mut out, 0.5).unwrap();
2142        fetcher.add_to_f64_scaled(&mut out, 0.5).unwrap();
2143        assert_eq!(out, [0.5]);
2144        assert!(matches!(
2145            fetcher.add_to_f64_scaled(&mut out, 1.0),
2146            Err(ReadError::OutOfBounds)
2147        ));
2148    }
2149
2150    #[test]
2151    fn packed_delta_fetcher_skip_then_add_bounded() {
2152        static INPUT: FontData = FontData::new(&[0x83, 0x40, 0x01, 0x02, 0x01, 0x81, 0x80]);
2153        // Full decoded stream: [0, 0, 0, 0, 258, -127, -128]
2154        let deltas = PackedDeltas::new(INPUT, 7);
2155        let mut fetcher = deltas.fetcher();
2156        fetcher.skip(3).unwrap();
2157        let mut out = [0.0f32; 4];
2158        fetcher.add_to_f32_scaled(&mut out, 1.0).unwrap();
2159        assert_eq!(out, [0.0, 258.0, -127.0, -128.0]);
2160    }
2161
2162    #[test]
2163    fn delta_run_iter_end_exhausts_unbounded_data() {
2164        static INPUT: FontData = FontData::new(&[0x83, 0x40, 0x01, 0x02, 0x01, 0x81, 0x80]);
2165        let deltas = PackedDeltas::consume_all(INPUT);
2166        let end = deltas.iter().end();
2167        assert_eq!(end.remaining_bytes(), 0);
2168    }
2169
2170    #[test]
2171    fn delta_run_iter_end_respects_bounded_count() {
2172        static INPUT: FontData = FontData::new(&[0x83, 0x40, 0x01, 0x02, 0x01, 0x81, 0x80]);
2173        // Count is exactly the first run only (4 zeros), so end() should not consume past
2174        // the run header byte.
2175        let deltas = PackedDeltas::new(INPUT, 4);
2176        let end = deltas.iter().end();
2177        assert_eq!(end.remaining_bytes(), INPUT.len() - 1);
2178
2179        let end_via_skip = deltas.iter().skip_fast(4).cursor;
2180        assert_eq!(end_via_skip.remaining_bytes(), INPUT.len() - 1);
2181    }
2182
2183    #[test]
2184    fn delta_run_iter_end_matches_manual_iteration_for_bounded_data() {
2185        static INPUT: FontData = FontData::new(&[
2186            0x03, 0x0A, 0x97, 0x00, 0xC6, 0x87, 0x41, 0x10, 0x22, 0xFB, 0x34,
2187        ]);
2188        let deltas = PackedDeltas::new(INPUT, 6);
2189
2190        let iter_collected = deltas.iter().collect::<Vec<_>>();
2191        assert_eq!(iter_collected.len(), 6);
2192
2193        let end = deltas.iter().end();
2194        let end_via_skip = deltas.iter().skip_fast(6).cursor;
2195        assert_eq!(end.remaining_bytes(), end_via_skip.remaining_bytes());
2196    }
2197
2198    fn lcg_next(state: &mut u32) -> u32 {
2199        *state = state.wrapping_mul(1664525).wrapping_add(1013904223);
2200        *state
2201    }
2202
2203    fn generated_delta_stream(seed: u32) -> (Vec<u8>, Vec<i32>) {
2204        let mut state = seed;
2205        let mut bytes = Vec::new();
2206        let mut expected = Vec::new();
2207        let run_count = (lcg_next(&mut state) % 6 + 1) as usize;
2208        for _ in 0..run_count {
2209            let run_type = (lcg_next(&mut state) % 4) as usize;
2210            let len = (lcg_next(&mut state) % 8 + 1) as usize;
2211            let control = match run_type {
2212                0 => (len - 1) as u8,        // i8
2213                1 => 0x40 | (len - 1) as u8, // i16
2214                2 => 0x80 | (len - 1) as u8, // zero
2215                _ => 0xC0 | (len - 1) as u8, // i32
2216            };
2217            bytes.push(control);
2218            match run_type {
2219                0 => {
2220                    for _ in 0..len {
2221                        let v = ((lcg_next(&mut state) % 255) as i32 - 127) as i8;
2222                        bytes.push(v as u8);
2223                        expected.push(v as i32);
2224                    }
2225                }
2226                1 => {
2227                    for _ in 0..len {
2228                        let v = ((lcg_next(&mut state) % 65535) as i32 - 32767) as i16;
2229                        bytes.extend(v.to_be_bytes());
2230                        expected.push(v as i32);
2231                    }
2232                }
2233                2 => {
2234                    expected.resize(expected.len() + len, 0);
2235                }
2236                _ => {
2237                    for _ in 0..len {
2238                        let v = (lcg_next(&mut state) % 2_000_001) as i32 - 1_000_000;
2239                        bytes.extend(v.to_be_bytes());
2240                        expected.push(v);
2241                    }
2242                }
2243            }
2244        }
2245        (bytes, expected)
2246    }
2247
2248    #[test]
2249    fn generated_packed_deltas_iter_matches_expected() {
2250        for seed in 1..=64 {
2251            let (bytes, expected) = generated_delta_stream(seed);
2252            let data = FontData::new(&bytes);
2253            let deltas = PackedDeltas::consume_all(data);
2254            assert_eq!(deltas.count_or_compute(), expected.len(), "seed={seed}");
2255            assert_eq!(deltas.iter().collect::<Vec<_>>(), expected, "seed={seed}");
2256        }
2257    }
2258
2259    #[test]
2260    fn generated_fetcher_skip_scaled_matches_expected() {
2261        for seed in 1..=64 {
2262            let (bytes, expected) = generated_delta_stream(seed);
2263            let data = FontData::new(&bytes);
2264            let deltas = PackedDeltas::new(data, expected.len());
2265            let mut fetcher = deltas.fetcher();
2266            let skip = (seed as usize * 7) % (expected.len() + 1);
2267            fetcher.skip(skip).unwrap();
2268
2269            let scale = if seed % 2 == 0 { 0.25 } else { -0.5 };
2270            let mut out = vec![10.0f32; expected.len() - skip];
2271            fetcher.add_to_f32_scaled(&mut out, scale).unwrap();
2272            for (i, got) in out.iter().copied().enumerate() {
2273                let want = 10.0 + expected[skip + i] as f32 * scale;
2274                assert!(
2275                    (got - want).abs() <= 1e-6,
2276                    "seed={seed} i={i} got={got} want={want}"
2277                );
2278            }
2279
2280            // Bounded fetcher should be exhausted after consuming all remaining entries.
2281            let mut extra = [0.0f32; 1];
2282            assert!(matches!(
2283                fetcher.add_to_f32_scaled(&mut extra, 1.0),
2284                Err(ReadError::OutOfBounds)
2285            ));
2286        }
2287    }
2288
2289    #[test]
2290    fn packed_point_split() {
2291        static INPUT: FontData =
2292            FontData::new(&[2, 1, 1, 2, 1, 205, 143, 1, 8, 0, 1, 202, 59, 1, 255, 0]);
2293        let (points, data) = PackedPointNumbers::split_off_front(INPUT);
2294        assert_eq!(points.count(), 2);
2295        assert_eq!(points.iter().collect::<Vec<_>>(), &[1, 3]);
2296        assert_eq!(points.total_len(), 4);
2297        assert_eq!(data.len(), INPUT.len() - 4);
2298    }
2299
2300    #[test]
2301    fn packed_points_dont_panic() {
2302        // a single '0' byte means that there are deltas for all points
2303        static ALL_POINTS: FontData = FontData::new(&[0]);
2304        let (all_points, _) = PackedPointNumbers::split_off_front(ALL_POINTS);
2305        // in which case the iterator just keeps incrementing until u16::MAX
2306        assert_eq!(all_points.iter().count(), u16::MAX as usize);
2307    }
2308
2309    /// Test that we split properly when the coordinate boundary doesn't align
2310    /// with a packed run boundary
2311    #[test]
2312    fn packed_delta_run_crosses_coord_boundary() {
2313        // 8 deltas with values 0..=7 with a run broken after the first 6; the
2314        // coordinate boundary occurs after the first 4
2315        static INPUT: FontData = FontData::new(&[
2316            // first run: 6 deltas as bytes
2317            5,
2318            0,
2319            1,
2320            2,
2321            3,
2322            // coordinate boundary is here
2323            4,
2324            5,
2325            // second run: 2 deltas as words
2326            1 | DELTAS_ARE_WORDS,
2327            0,
2328            6,
2329            0,
2330            7,
2331        ]);
2332        let deltas = PackedDeltas::consume_all(INPUT);
2333        assert_eq!(deltas.count_or_compute(), 8);
2334        let x_deltas = deltas.x_deltas().collect::<Vec<_>>();
2335        let y_deltas = deltas.y_deltas().collect::<Vec<_>>();
2336        assert_eq!(x_deltas, [0, 1, 2, 3]);
2337        assert_eq!(y_deltas, [4, 5, 6, 7]);
2338    }
2339
2340    /// We don't have a reference for our float delta computation, so this is
2341    /// a sanity test to ensure that floating point deltas are within a
2342    /// reasonable margin of the same in fixed point.
2343    #[test]
2344    fn ivs_float_deltas_nearly_match_fixed_deltas() {
2345        let font = FontRef::new(font_test_data::COLRV0V1_VARIABLE).unwrap();
2346        let axis_count = font.fvar().unwrap().axis_count() as usize;
2347        let colr = font.colr().unwrap();
2348        let ivs = colr.item_variation_store().unwrap().unwrap();
2349        // Generate a set of coords from -1 to 1 in 0.1 increments
2350        for coord in (0..=20).map(|x| F2Dot14::from_f32((x as f32) / 10.0 - 1.0)) {
2351            // For testing purposes, just splat the coord to all axes
2352            let coords = vec![coord; axis_count];
2353            for (outer_ix, data) in ivs.item_variation_data().iter().enumerate() {
2354                let outer_ix = outer_ix as u16;
2355                let Some(Ok(data)) = data else {
2356                    continue;
2357                };
2358                for inner_ix in 0..data.item_count() {
2359                    let delta_ix = DeltaSetIndex {
2360                        outer: outer_ix,
2361                        inner: inner_ix,
2362                    };
2363                    // Check the deltas against all possible target values
2364                    let delta = ivs.compute_delta(delta_ix, &coords).unwrap();
2365                    let orig_delta = delta.to_i32();
2366                    let float_delta = delta.to_f64();
2367
2368                    // For font unit types, we need to accept both rounding and
2369                    // truncation to account for the additional accumulation of
2370                    // fractional bits in floating point
2371                    assert!(
2372                        orig_delta == float_delta.round() as i32
2373                            || orig_delta == float_delta.trunc() as i32
2374                    );
2375                    // For the fixed point types, check with an epsilon
2376                    const EPSILON: f32 = 1e12;
2377                    let fixed_delta = Fixed::ZERO.apply_delta(delta);
2378                    assert!((Fixed::from_bits(orig_delta).to_f32() - fixed_delta).abs() < EPSILON);
2379                    let f2dot14_delta = F2Dot14::ZERO.apply_delta(delta);
2380                    assert!(
2381                        (F2Dot14::from_bits(orig_delta as i16).to_f32() - f2dot14_delta).abs()
2382                            < EPSILON
2383                    );
2384                }
2385            }
2386        }
2387    }
2388
2389    #[test]
2390    fn ivs_data_len_short() {
2391        let data = BeBuffer::new()
2392            .push(2u16) // item_count
2393            .push(3u16) // word_delta_count
2394            .push(5u16) // region_index_count
2395            .extend([0u16, 1, 2, 3, 4]) // region_indices
2396            .extend([1u8; 128]); // this is much more data than we need!
2397
2398        let ivs = ItemVariationData::read(data.data().into()).unwrap();
2399        let row_len = (3 * u16::RAW_BYTE_LEN) + (2 * u8::RAW_BYTE_LEN); // 3 word deltas, 2 byte deltas
2400        let expected_len = 2 * row_len;
2401        assert_eq!(ivs.delta_sets().len(), expected_len);
2402    }
2403
2404    #[test]
2405    fn ivs_data_len_long() {
2406        let data = BeBuffer::new()
2407            .push(2u16) // item_count
2408            .push(2u16 | 0x8000) // word_delta_count, long deltas
2409            .push(4u16) // region_index_count
2410            .extend([0u16, 1, 2]) // region_indices
2411            .extend([1u8; 128]); // this is much more data than we need!
2412
2413        let ivs = ItemVariationData::read(data.data().into()).unwrap();
2414        let row_len = (2 * u32::RAW_BYTE_LEN) + (2 * u16::RAW_BYTE_LEN); // 1 word (4-byte) delta, 2 short (2-byte)
2415        let expected_len = 2 * row_len;
2416        assert_eq!(ivs.delta_sets().len(), expected_len);
2417    }
2418
2419    // Add with overflow when accumulating packed point numbers
2420    // https://issues.oss-fuzz.com/issues/378159154
2421    #[test]
2422    fn packed_point_numbers_avoid_overflow() {
2423        // Lots of 1 bits triggers the behavior quite nicely
2424        let buf = vec![0xFF; 0xFFFF];
2425        let iter = PackedPointNumbersIter::new(0xFFFF, FontData::new(&buf).cursor());
2426        // Don't panic!
2427        let _ = iter.count();
2428    }
2429
2430    // Dense accumulator should match iterator
2431    #[test]
2432    fn accumulate_dense() {
2433        let font = FontRef::new(font_test_data::VAZIRMATN_VAR).unwrap();
2434        let gvar = font.gvar().unwrap();
2435        let gvar_data = gvar.glyph_variation_data(GlyphId::new(1)).unwrap().unwrap();
2436        let mut count = 0;
2437        for tuple in gvar_data.tuples() {
2438            if !tuple.has_deltas_for_all_points() {
2439                continue;
2440            }
2441            let iter_deltas = tuple
2442                .deltas()
2443                .map(|delta| (delta.x_delta, delta.y_delta))
2444                .collect::<Vec<_>>();
2445            let mut delta_buf = vec![Point::broadcast(Fixed::ZERO); iter_deltas.len()];
2446            tuple
2447                .accumulate_dense_deltas(&mut delta_buf, Fixed::ONE)
2448                .unwrap();
2449            let accum_deltas = delta_buf
2450                .iter()
2451                .map(|delta| (delta.x.to_i32(), delta.y.to_i32()))
2452                .collect::<Vec<_>>();
2453            assert_eq!(iter_deltas, accum_deltas);
2454            count += iter_deltas.len();
2455        }
2456        assert!(count != 0);
2457    }
2458
2459    // Sparse accumulator should match iterator
2460    #[test]
2461    fn accumulate_sparse() {
2462        let font = FontRef::new(font_test_data::VAZIRMATN_VAR).unwrap();
2463        let gvar = font.gvar().unwrap();
2464        let gvar_data = gvar.glyph_variation_data(GlyphId::new(2)).unwrap().unwrap();
2465        let mut count = 0;
2466        for tuple in gvar_data.tuples() {
2467            if tuple.has_deltas_for_all_points() {
2468                continue;
2469            }
2470            let iter_deltas = tuple.deltas().collect::<Vec<_>>();
2471            let max_modified_point = iter_deltas
2472                .iter()
2473                .max_by_key(|delta| delta.position)
2474                .unwrap()
2475                .position as usize;
2476            let mut delta_buf = vec![Point::broadcast(Fixed::ZERO); max_modified_point + 1];
2477            let mut flags = vec![PointFlags::default(); delta_buf.len()];
2478            tuple
2479                .accumulate_sparse_deltas(&mut delta_buf, &mut flags, Fixed::ONE)
2480                .unwrap();
2481            let mut accum_deltas = vec![];
2482            for (i, (delta, flag)) in delta_buf.iter().zip(flags).enumerate() {
2483                if flag.has_marker(PointMarker::HAS_DELTA) {
2484                    accum_deltas.push(GlyphDelta::new(
2485                        i as u16,
2486                        delta.x.to_i32(),
2487                        delta.y.to_i32(),
2488                    ));
2489                }
2490            }
2491            assert_eq!(iter_deltas, accum_deltas);
2492            count += iter_deltas.len();
2493        }
2494        assert!(count != 0);
2495    }
2496
2497    #[test]
2498    fn delta_set_index_map_empty_is_identity() {
2499        let data = BeBuffer::new()
2500            .push(0u8) // format 0
2501            .push(EntryFormat::empty())
2502            .push(0u16); // map_count
2503        let map = DeltaSetIndexMap::read(data.data().into()).unwrap();
2504        assert_eq!(
2505            map.get(0x0001_0002).unwrap(),
2506            DeltaSetIndex { outer: 1, inner: 2 }
2507        );
2508    }
2509}