1use std::{
4 cmp::Reverse,
5 collections::{BinaryHeap, HashMap, HashSet},
6 fmt::{Debug, Display},
7};
8
9use crate::tables::{
10 layout::VariationIndex,
11 variations::{
12 common_builder::{TemporaryDeltaSetId, VarStoreRemapping},
13 ItemVariationData, ItemVariationStore, VariationRegion, VariationRegionList,
14 },
15};
16use indexmap::IndexMap;
17
18pub type VariationIndexRemapping = VarStoreRemapping<VariationIndex>;
19
20#[derive(Clone, Debug)]
25pub struct VariationStoreBuilder {
26 all_regions: HashMap<VariationRegion, usize>,
28 delta_sets: DeltaSetStorage,
29 axis_count: u16,
32}
33
34#[derive(Clone, Debug)]
36enum DeltaSetStorage {
37 Direct(Vec<DeltaSet>),
39 Deduplicated(IndexMap<DeltaSet, TemporaryDeltaSetId>),
41}
42
43#[derive(Clone, Debug, Default, Hash, PartialEq, Eq)]
47struct DeltaSet(Vec<(u16, i32)>);
48
49impl VariationStoreBuilder {
50 pub fn new(axis_count: u16) -> Self {
58 Self {
59 axis_count,
60 delta_sets: DeltaSetStorage::Deduplicated(Default::default()),
61 all_regions: Default::default(),
62 }
63 }
64
65 pub fn is_empty(&self) -> bool {
67 match &self.delta_sets {
68 DeltaSetStorage::Direct(val) => val.is_empty(),
69 DeltaSetStorage::Deduplicated(val) => val.is_empty(),
70 }
71 }
72
73 pub fn new_with_implicit_indices(axis_count: u16) -> Self {
79 VariationStoreBuilder {
80 axis_count,
81 all_regions: Default::default(),
82 delta_sets: DeltaSetStorage::Direct(Default::default()),
83 }
84 }
85
86 pub fn add_deltas<T: Into<i32>>(
87 &mut self,
88 deltas: Vec<(VariationRegion, T)>,
89 ) -> TemporaryDeltaSetId {
90 let mut delta_set = Vec::with_capacity(deltas.len());
91 for (region, delta) in deltas {
92 let region_idx = self.canonical_index_for_region(region) as u16;
93 delta_set.push((region_idx, delta.into()));
94 }
95 delta_set.retain(|(_, delta)| *delta != 0);
110 delta_set.sort_unstable();
111 self.delta_sets.add(DeltaSet(delta_set))
112 }
113
114 fn canonical_index_for_region(&mut self, region: VariationRegion) -> usize {
115 let next_idx = self.all_regions.len();
116 *self.all_regions.entry(region).or_insert(next_idx)
117 }
118
119 fn make_region_list(&self, subtables: &mut [Option<ItemVariationData>]) -> VariationRegionList {
120 let used_regions = subtables
122 .iter()
123 .flatten()
124 .flat_map(|var_data| var_data.region_indexes.iter())
125 .map(|idx| *idx as usize)
126 .collect::<HashSet<_>>();
127 let mut region_list = self
129 .all_regions
130 .iter()
131 .filter_map(|(reg, idx)| {
132 if used_regions.contains(idx) {
133 Some((idx, reg.to_owned()))
134 } else {
135 None
136 }
137 })
138 .collect::<Vec<_>>();
139 region_list.sort_unstable();
140 let mut new_regions = Vec::new();
141 let mut region_map = HashMap::new();
142 for (old_idx, reg) in region_list.into_iter() {
143 region_map.insert(*old_idx as u16, new_regions.len() as u16);
144 new_regions.push(reg);
145 }
146 for var_data in subtables.iter_mut().flatten() {
148 var_data.region_indexes = var_data
149 .region_indexes
150 .iter()
151 .map(|idx| region_map[idx])
152 .collect();
153 }
154 VariationRegionList::new(self.axis_count, new_regions)
155 }
156
157 fn encoder(&self) -> Encoder<'_> {
158 Encoder::new(&self.delta_sets, self.all_regions.len() as u16)
159 }
160
161 pub fn build(self) -> (ItemVariationStore, VariationIndexRemapping) {
166 let mut key_map = VariationIndexRemapping::default();
167 let mut subtables = if matches!(self.delta_sets, DeltaSetStorage::Direct(_)) {
168 vec![self.build_unoptimized(&mut key_map)]
169 } else {
170 let mut encoder = self.encoder();
171 encoder.optimize();
172 encoder.encode(&mut key_map)
173 };
174 let region_list = self.make_region_list(&mut subtables);
175 (ItemVariationStore::new(region_list, subtables), key_map)
176 }
177
178 fn build_unoptimized(
180 &self,
181 key_map: &mut VariationIndexRemapping,
182 ) -> Option<ItemVariationData> {
183 let n_regions = self.all_regions.len() as u16;
185 let mut shape = RowShape(vec![ColumnBits::None; n_regions as usize]);
186 let mut temp = RowShape::default();
187
188 for (delta, _) in self.delta_sets.iter() {
189 temp.reuse(delta, n_regions);
190 if !shape.can_cover(&temp) {
191 shape = shape.merge(&temp);
192 }
193 }
194
195 let encoding = Encoding {
197 shape,
198 deltas: self.delta_sets.iter().collect(),
199 };
200 debug_assert!(
201 encoding.deltas.len() <= u16::MAX as usize,
202 "unmapped variation store supports at most u16::MAX items"
203 );
204 encoding.encode(key_map, 0)
205 }
206}
207
208struct Encoder<'a> {
212 encodings: Vec<Encoding<'a>>,
213}
214
215struct Encoding<'a> {
217 shape: RowShape,
218 deltas: Vec<(&'a DeltaSet, TemporaryDeltaSetId)>,
219}
220
221struct RegionMap {
226 regions_to_columns: Vec<(u16, ColumnBits)>,
233 n_active_regions: u16,
234 n_long_regions: u16,
235 long_words: bool,
236}
237
238#[derive(Debug, Clone, Default, PartialEq, Eq, Hash, PartialOrd, Ord)]
245struct RowShape(Vec<ColumnBits>);
246
247#[repr(u8)]
251#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
252enum ColumnBits {
253 None = 0,
255 One = 1,
257 Two = 2,
259 Four = 4,
261}
262
263impl<'a> Encoder<'a> {
264 fn new(delta_map: &'a DeltaSetStorage, total_regions: u16) -> Self {
265 let mut shape = RowShape::default();
266 let mut encodings: IndexMap<_, Vec<_>> = Default::default();
267
268 for (delta, idx) in delta_map.iter() {
269 shape.reuse(delta, total_regions);
270 match encodings.get_mut(&shape) {
271 Some(items) => items.push((delta, idx)),
272 None => {
273 encodings.insert(shape.clone(), vec![(delta, idx)]);
274 }
275 }
276 }
277 let encodings = encodings
278 .into_iter()
279 .map(|(shape, deltas)| Encoding { shape, deltas })
280 .collect();
281
282 Encoder { encodings }
283 }
284
285 fn cost(&self) -> usize {
286 self.encodings.iter().map(Encoding::cost).sum()
287 }
288
289 fn optimize(&mut self) {
296 let cost = self.cost();
297 log::trace!("optimizing {} encodings, {cost}B", self.encodings.len(),);
298 struct DebugTodoList<'a>(&'a [Option<Encoding<'a>>]);
300 impl Debug for DebugTodoList<'_> {
301 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
302 write!(f, "Todo({} items", self.0.len())?;
303 for (i, enc) in self.0.iter().enumerate() {
304 if let Some(enc) = enc {
305 write!(f, "\n {i:>4}: {enc:?}")?;
306 }
307 }
308 writeln!(f, ")")
309 }
310 }
311 let mut to_process = std::mem::take(&mut self.encodings);
313 to_process.sort_unstable_by(Encoding::ord_matching_fonttools);
315 let mut to_process = to_process.into_iter().map(Option::Some).collect::<Vec<_>>();
318
319 let mut queue = BinaryHeap::with_capacity(to_process.len());
322
323 for (i, red) in to_process.iter().enumerate() {
324 for (j, blue) in to_process.iter().enumerate().skip(i + 1) {
325 let gain = red.as_ref().unwrap().compute_gain(blue.as_ref().unwrap());
326 if gain > 0 {
327 log::trace!("adding ({i}, {j} ({gain})) to queue");
328 queue.push(Reverse((-gain, i, j)));
331 }
332 }
333 }
334
335 while let Some(Reverse((_gain, i, j))) = queue.pop() {
337 if to_process[i].is_none() || to_process[j].is_none() {
338 continue;
339 }
340 let (Some(mut to_update), Some(to_add)) = (
343 to_process.get_mut(i).and_then(Option::take),
344 to_process.get_mut(j).and_then(Option::take),
345 ) else {
346 unreachable!("checked above")
347 };
348 log::trace!(
349 "combining {i}+{j} ({}, {} {_gain})",
350 to_update.shape,
351 to_add.shape
352 );
353
354 to_update.merge_with(to_add);
358 let n = to_process.len(); let mut maybe_existing_encoding = None;
360 for (ii, opt_encoding) in to_process.iter_mut().enumerate() {
361 let Some(encoding) = opt_encoding.take() else {
364 continue;
365 };
366
367 if encoding.shape == to_update.shape {
368 maybe_existing_encoding = Some(encoding);
373 continue;
374 }
375 let gain = to_update.compute_gain(&encoding);
376 if gain > 0 {
377 log::trace!("adding ({n}, {ii} ({gain})) to queue");
378 queue.push(Reverse((-gain, ii, n)));
379 }
380 *opt_encoding = Some(encoding);
381 }
382 if let Some(existing) = maybe_existing_encoding.take() {
383 to_update.deltas.extend(existing.deltas);
384 }
385 to_process.push(Some(to_update));
386 log::trace!("{:?}", DebugTodoList(&to_process));
387 }
388 self.encodings = to_process.into_iter().flatten().collect();
389 self.encodings
391 .iter_mut()
392 .for_each(|enc| enc.deltas.sort_unstable());
393 self.encodings
396 .sort_unstable_by(Encoding::ord_matching_fonttools);
397 log::trace!(
398 "optimized {} encodings, {}B, ({}B saved)",
399 self.encodings.len(),
400 self.cost(),
401 cost.saturating_sub(self.cost()),
402 );
403 }
404
405 fn encode(self, key_map: &mut VariationIndexRemapping) -> Vec<Option<ItemVariationData>> {
412 self.encodings
413 .into_iter()
414 .flat_map(Encoding::iter_split_into_table_size_chunks)
415 .enumerate()
416 .map(|(i, encoding)| encoding.encode(key_map, i as u16))
417 .collect()
418 }
419}
420
421impl ColumnBits {
422 fn for_val(val: i32) -> Self {
423 if val == 0 {
424 Self::None
425 } else if i8::try_from(val).is_ok() {
426 Self::One
427 } else if i16::try_from(val).is_ok() {
428 Self::Two
429 } else {
430 Self::Four
431 }
432 }
433
434 fn cost(self) -> usize {
436 self as u8 as _
437 }
438}
439
440impl RowShape {
441 fn reuse(&mut self, deltas: &DeltaSet, n_regions: u16) {
448 self.0.clear();
449 self.0.resize(n_regions as _, ColumnBits::None);
450 for (region, delta) in &deltas.0 {
451 self.0[*region as usize] = ColumnBits::for_val(*delta);
452 }
453 }
454
455 fn merge(&self, other: &Self) -> Self {
459 Self(
460 self.0
461 .iter()
462 .zip(other.0.iter())
463 .map(|(us, them)| *us.max(them))
464 .collect(),
465 )
466 }
467
468 fn can_cover(&self, other: &Self) -> bool {
470 debug_assert_eq!(self.0.len(), other.0.len());
471 self.0
472 .iter()
473 .zip(other.0.iter())
474 .all(|(us, them)| us >= them)
475 }
476
477 fn row_cost(&self) -> usize {
479 self.0.iter().copied().map(ColumnBits::cost).sum()
480 }
481
482 fn overhead(&self) -> usize {
483 const SUBTABLE_FIXED_COST: usize = 10;
485 const COST_PER_REGION: usize = 2;
486 SUBTABLE_FIXED_COST + (self.n_non_zero_regions() * COST_PER_REGION)
487 }
488
489 fn n_non_zero_regions(&self) -> usize {
490 self.0.iter().map(|x| (*x as u8).min(1) as usize).sum()
491 }
492
493 fn count_lengths(&self) -> (u16, u16, u16) {
495 self.0
496 .iter()
497 .fold((0, 0, 0), |(byte, short, long), this| match this {
498 ColumnBits::One => (byte + 1, short, long),
499 ColumnBits::Two => (byte, short + 1, long),
500 ColumnBits::Four => (byte, short, long + 1),
501 _ => (byte, short, long),
502 })
503 }
504
505 fn region_map(&self) -> RegionMap {
508 let mut with_idx = self.0.iter().copied().enumerate().collect::<Vec<_>>();
509 with_idx.sort_unstable_by_key(|(idx, bit)| (std::cmp::Reverse(*bit), *idx));
511 let mut map = vec![(0u16, ColumnBits::None); with_idx.len()];
513 for (new_idx, (canonical_idx, bits)) in with_idx.iter().enumerate() {
514 map[*canonical_idx] = (new_idx as _, *bits);
515 }
516
517 let (count_8, count_16, count_32) = self.count_lengths();
518 let long_words = count_32 > 0;
519 let n_long_regions = if long_words { count_32 } else { count_16 };
520 let n_active_regions = count_8 + count_16 + count_32;
521 RegionMap {
522 regions_to_columns: map,
523 n_active_regions,
524 n_long_regions,
525 long_words,
526 }
527 }
528
529 #[cfg(test)]
532 fn to_fonttools_repr(&self) -> u128 {
533 assert!(
534 self.0.len() <= u128::BITS as usize / 4,
535 "we can only pack 128 bits"
536 );
537
538 let has_long_word = self.0.iter().any(|bits| *bits == ColumnBits::Four);
539 let mut chars = 0;
540 let mut i = 1;
541
542 if !has_long_word {
543 for v in &self.0 {
544 if *v != ColumnBits::None {
545 chars += i;
546 }
547 if *v == ColumnBits::Two {
548 chars += i * 0b0010;
549 }
550 i <<= 4;
551 }
552 } else {
553 for v in &self.0 {
554 if *v != ColumnBits::None {
555 chars += i * 0b0011;
556 }
557 if *v == ColumnBits::Four {
558 chars += i * 0b1100;
559 }
560 i <<= 4;
561 }
562 }
563 chars
564 }
565}
566
567impl<'a> Encoding<'a> {
568 fn cost(&self) -> usize {
569 self.shape.overhead() + (self.shape.row_cost() * self.deltas.len())
570 }
571
572 fn compute_gain(&self, other: &Encoding) -> i64 {
573 let current_cost = self.cost() + other.cost();
574
575 let combined = self.shape.merge(&other.shape);
576 let combined_cost =
577 combined.overhead() + (combined.row_cost() * (self.deltas.len() + other.deltas.len()));
578 current_cost as i64 - combined_cost as i64
579 }
580
581 fn merge_with(&mut self, other: Encoding<'a>) {
582 self.shape = self.shape.merge(&other.shape);
583 self.deltas.extend(other.deltas);
584 }
585
586 fn iter_split_into_table_size_chunks(self) -> impl Iterator<Item = Encoding<'a>> {
591 let mut next = Some(self);
592 std::iter::from_fn(move || {
593 let mut this = next.take()?;
594 next = this.split_off_back();
595 Some(this)
596 })
597 }
598
599 fn split_off_back(&mut self) -> Option<Self> {
603 const MAX_ITEMS: usize = 0xFFFF;
604 if self.deltas.len() <= MAX_ITEMS {
605 return None;
606 }
607 let deltas = self.deltas.split_off(MAX_ITEMS);
608 Some(Self {
609 shape: self.shape.clone(),
610 deltas,
611 })
612 }
613
614 fn encode(
615 self,
616 key_map: &mut VariationIndexRemapping,
617 subtable_idx: u16,
618 ) -> Option<ItemVariationData> {
619 log::trace!(
620 "encoding subtable {subtable_idx} ({} rows, {}B)",
621 self.deltas.len(),
622 self.cost()
623 );
624 assert!(self.deltas.len() <= 0xffff, "call split_off_back first");
625 let item_count = self.deltas.len() as u16;
626 if item_count == 0 {
627 return None;
629 }
630
631 let region_map = self.shape.region_map();
632 let n_regions = self.shape.n_non_zero_regions();
633 let total_n_delta_values = self.deltas.len() * n_regions;
634 let mut raw_deltas = vec![0i32; total_n_delta_values];
635
636 for (i, (delta, raw_key)) in self.deltas.iter().enumerate() {
639 let pos = i * n_regions;
640 for (region, val) in &delta.0 {
641 let Some(column_idx) = region_map.column_index_for_region(*region) else {
642 continue;
643 };
644 let idx = pos + column_idx as usize;
645 raw_deltas[idx] = *val;
646 }
647 let final_key = VariationIndex::new(subtable_idx, i as u16);
648 key_map.set(*raw_key, final_key);
649 }
650
651 let delta_sets = region_map.encode_raw_delta_values(raw_deltas);
654 let word_delta_count = region_map.word_delta_count();
655 let region_indexes = region_map.indices();
656
657 Some(ItemVariationData::new(
658 item_count,
659 word_delta_count,
660 region_indexes,
661 delta_sets,
662 ))
663 }
664
665 fn ord_matching_fonttools(&self, other: &Self) -> std::cmp::Ordering {
672 let cost_ord = self.shape.row_cost().cmp(&other.shape.row_cost());
674 if cost_ord != std::cmp::Ordering::Equal {
675 return cost_ord;
676 }
677
678 debug_assert_eq!(
679 self.shape.0.len(),
680 other.shape.0.len(),
681 "all shapes have same # of regions"
682 );
683
684 for (a, b) in self.shape.0.iter().rev().zip(other.shape.0.iter().rev()) {
686 match a.cmp(b) {
687 std::cmp::Ordering::Equal => (), not_eq => return not_eq,
689 }
690 }
691 std::cmp::Ordering::Equal
692 }
693}
694
695impl RegionMap {
696 fn encode_raw_delta_values(&self, raw_deltas: Vec<i32>) -> Vec<u8> {
704 fn encode_words<'a>(
706 long: &'a [i32],
707 short: &'a [i32],
708 long_words: bool,
709 ) -> impl Iterator<Item = u8> + 'a {
710 let left = long_words.then(|| {
713 long.iter()
714 .flat_map(|x| x.to_be_bytes().into_iter())
715 .chain(short.iter().flat_map(|x| (*x as i16).to_be_bytes()))
716 });
717 let right = (!long_words).then(|| {
718 long.iter()
719 .flat_map(|x| (*x as i16).to_be_bytes().into_iter())
720 .chain(short.iter().flat_map(|x| (*x as i8).to_be_bytes()))
721 });
722
723 left.into_iter()
725 .flatten()
726 .chain(right.into_iter().flatten())
727 }
728
729 if self.n_active_regions == 0 {
730 return Default::default();
731 }
732
733 raw_deltas
734 .chunks(self.n_active_regions as usize)
735 .flat_map(|delta_set| {
736 let (long, short) = delta_set.split_at(self.n_long_regions as usize);
737 encode_words(long, short, self.long_words)
738 })
739 .collect()
740 }
741
742 fn word_delta_count(&self) -> u16 {
747 let long_flag = if self.long_words { 0x8000 } else { 0 };
748 self.n_long_regions | long_flag
749 }
750
751 fn column_index_for_region(&self, region: u16) -> Option<u16> {
754 let (column, bits) = self.regions_to_columns[region as usize];
755 (bits != ColumnBits::None).then_some(column)
756 }
757
758 fn indices(&self) -> Vec<u16> {
760 let mut result: Vec<_> = self
761 .regions_to_columns
762 .iter()
763 .enumerate()
764 .filter_map(|(i, (_, bits))| (*bits as u8 > 0).then_some(i as _))
765 .collect();
766 result.sort_unstable_by_key(|region_idx| {
768 self.regions_to_columns
769 .get(*region_idx as usize)
770 .map(|(column, _)| *column)
771 .unwrap_or(u16::MAX)
775 });
776 result
777 }
778}
779
780impl VariationIndexRemapping {
781 #[cfg(test)]
783 fn get_raw(&self, from: TemporaryDeltaSetId) -> Option<(u16, u16)> {
784 self.map
785 .get(&from)
786 .map(|var| (var.delta_set_outer_index, var.delta_set_inner_index))
787 }
788}
789
790impl DeltaSetStorage {
791 fn add(&mut self, delta_set: DeltaSet) -> TemporaryDeltaSetId {
792 match self {
793 DeltaSetStorage::Direct(deltas) => {
794 let next_id = deltas.len() as u32;
795 deltas.push(delta_set);
796 next_id
797 }
798 DeltaSetStorage::Deduplicated(deltas) => {
799 let next_id = deltas.len() as u32;
800 *deltas.entry(delta_set).or_insert(next_id)
801 }
802 }
803 }
804
805 fn iter(&self) -> impl Iterator<Item = (&DeltaSet, TemporaryDeltaSetId)> + '_ {
806 let (a_vec, a_map) = match self {
809 DeltaSetStorage::Direct(deltas) => (Some(deltas), None),
810 DeltaSetStorage::Deduplicated(deltas) => (None, Some(deltas)),
811 };
812 a_vec
813 .into_iter()
814 .flat_map(|x| x.iter().enumerate().map(|(i, val)| (val, i as u32)))
815 .chain(
816 a_map
817 .into_iter()
818 .flat_map(|map| map.iter().map(|(val, idx)| (val, *idx))),
819 )
820 }
821}
822
823impl PartialOrd for DeltaSet {
836 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
837 Some(self.cmp(other))
838 }
839}
840
841impl Ord for DeltaSet {
842 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
843 let max_region_idx = self
844 .0
845 .iter()
846 .chain(other.0.iter())
847 .map(|(idx, _)| *idx)
848 .max()
849 .unwrap_or(0);
850
851 let left = DenseDeltaIter::new(&self.0, max_region_idx);
852 let right = DenseDeltaIter::new(&other.0, max_region_idx);
853
854 for (l, r) in left.zip(right) {
855 match l.cmp(&r) {
856 std::cmp::Ordering::Equal => (),
857 non_eq => return non_eq,
858 }
859 }
860 std::cmp::Ordering::Equal
861 }
862}
863
864struct DenseDeltaIter<'a> {
869 total_len: u16,
870 cur_pos: u16,
871 deltas: &'a [(u16, i32)],
872}
873
874impl<'a> DenseDeltaIter<'a> {
875 fn new(deltas: &'a [(u16, i32)], max_idx: u16) -> Self {
876 DenseDeltaIter {
877 total_len: max_idx,
878 deltas,
879 cur_pos: 0,
880 }
881 }
882}
883
884impl Iterator for DenseDeltaIter<'_> {
885 type Item = i32;
886
887 fn next(&mut self) -> Option<Self::Item> {
888 if self.cur_pos > self.total_len {
889 return None;
890 }
891 let result = if self.deltas.first().map(|(idx, _)| *idx) == Some(self.cur_pos) {
892 let result = self.deltas.first().unwrap().1;
893 self.deltas = &self.deltas[1..];
894 result
895 } else {
896 0
897 };
898 self.cur_pos += 1;
899 Some(result)
900 }
901}
902
903impl Default for DeltaSetStorage {
904 fn default() -> Self {
905 Self::Deduplicated(Default::default())
906 }
907}
908
909impl Display for RowShape {
910 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
911 for col in &self.0 {
912 match col {
913 ColumnBits::None => write!(f, "-"),
914 ColumnBits::One => write!(f, "B"),
915 ColumnBits::Two => write!(f, "S"),
916 ColumnBits::Four => write!(f, "L"),
917 }?
918 }
919 Ok(())
920 }
921}
922
923impl Debug for Encoding<'_> {
924 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
925 write!(
926 f,
927 "Encoding({}, {} items {} bytes)",
928 self.shape,
929 self.deltas.len(),
930 self.cost()
931 )
932 }
933}
934
935impl Debug for Encoder<'_> {
936 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
937 f.debug_struct("Encoder")
938 .field("encodings", &self.encodings)
939 .finish_non_exhaustive()
940 }
941}
942
943#[cfg(test)]
944mod tests {
945 use crate::tables::variations::RegionAxisCoordinates;
946 use font_types::F2Dot14;
947 use read_fonts::{FontData, FontRead};
948
949 use super::*;
950
951 fn reg_coords(min: f32, default: f32, max: f32) -> RegionAxisCoordinates {
952 RegionAxisCoordinates {
953 start_coord: F2Dot14::from_f32(min),
954 peak_coord: F2Dot14::from_f32(default),
955 end_coord: F2Dot14::from_f32(max),
956 }
957 }
958
959 fn test_regions() -> [VariationRegion; 3] {
960 [
961 VariationRegion::new(vec![reg_coords(0.0, 0.2, 1.0), reg_coords(0.0, 0.0, 1.0)]),
962 VariationRegion::new(vec![reg_coords(0.0, 0.1, 0.3), reg_coords(0.0, 0.1, 0.3)]),
963 VariationRegion::new(vec![reg_coords(0.0, 0.1, 0.5), reg_coords(0.0, 0.1, 0.3)]),
964 ]
965 }
966
967 #[test]
968 #[allow(clippy::redundant_clone)]
969 fn smoke_test() {
970 let [r1, r2, r3] = test_regions();
971
972 let mut builder = VariationStoreBuilder::new(2);
973 builder.add_deltas(vec![(r1.clone(), 512), (r2, 266), (r3.clone(), 1115)]);
974 builder.add_deltas(vec![(r3.clone(), 20)]);
975 builder.add_deltas(vec![(r3.clone(), 21)]);
976 builder.add_deltas(vec![(r3, 22)]);
977
978 let (store, _) = builder.build();
980 assert_eq!(store.variation_region_list.variation_regions.len(), 3);
981 assert_eq!(store.item_variation_data.len(), 2);
982 assert_eq!(
983 store.item_variation_data[0]
984 .as_ref()
985 .unwrap()
986 .region_indexes,
987 vec![2]
988 );
989 assert_eq!(
990 store.item_variation_data[1]
991 .as_ref()
992 .unwrap()
993 .region_indexes,
994 vec![0, 1, 2]
995 );
996 }
997
998 #[test]
999 fn key_mapping() {
1000 let [r1, r2, r3] = test_regions();
1001
1002 let mut builder = VariationStoreBuilder::new(2);
1003 let k1 = builder.add_deltas(vec![(r1.clone(), 5), (r2, 1000), (r3.clone(), 1500)]);
1004 let k2 = builder.add_deltas(vec![(r1.clone(), -3), (r3.clone(), 20)]);
1005 let k3 = builder.add_deltas(vec![(r1.clone(), -12), (r3.clone(), 7)]);
1006 let _ = builder.add_deltas(vec![(r1.clone(), -10), (r3.clone(), 7)]);
1008 let _ = builder.add_deltas(vec![(r1.clone(), -9), (r3.clone(), 7)]);
1009 let _ = builder.add_deltas(vec![(r1, -11), (r3, 7)]);
1010
1011 let (_, key_lookup) = builder.build();
1015
1016 assert_eq!(key_lookup.get_raw(k1).unwrap(), (1, 0),);
1019 assert_eq!(key_lookup.get_raw(k2).unwrap(), (0, 4),); assert_eq!(key_lookup.get_raw(k3).unwrap(), (0, 0),); assert_eq!(key_lookup.map.len(), 6);
1025 }
1026
1027 #[test]
1029 fn fontools_rowshape_repr() {
1030 use ColumnBits as C;
1031 let shape1 = RowShape(vec![C::None, C::One, C::One, C::Two]);
1032 assert_eq!(shape1.to_fonttools_repr(), 0b0011_0001_0001_0000);
1033 let shape2 = RowShape(vec![C::Two, C::One, C::One, C::None]);
1034 assert_eq!(shape2.to_fonttools_repr(), 0b0000_0001_0001_0011);
1035
1036 assert!(shape1.to_fonttools_repr() > shape2.to_fonttools_repr());
1037 }
1038
1039 #[test]
1040 fn encoding_sort_order() {
1041 let _ = env_logger::builder().is_test(true).try_init();
1042 let [r1, r2, r3] = test_regions();
1043
1044 let mut builder = VariationStoreBuilder::new(2);
1047 builder.add_deltas(vec![(r1.clone(), 1000), (r2.clone(), 5)]);
1049 builder.add_deltas(vec![(r1.clone(), 1013), (r2.clone(), 20)]);
1050 builder.add_deltas(vec![(r1.clone(), 1014), (r2.clone(), 21)]);
1051 builder.add_deltas(vec![(r2.clone(), 1212), (r3.clone(), 7)]);
1053 builder.add_deltas(vec![(r2.clone(), 1213), (r3.clone(), 8)]);
1054 builder.add_deltas(vec![(r2.clone(), 1214), (r3.clone(), 8)]);
1055
1056 builder.add_deltas(vec![(r1.clone(), 12), (r3.clone(), 7)]);
1058 builder.add_deltas(vec![(r1.clone(), 13), (r3.clone(), 9)]);
1059 builder.add_deltas(vec![(r1.clone(), 14), (r3.clone(), 10)]);
1060 builder.add_deltas(vec![(r1.clone(), 15), (r3.clone(), 11)]);
1061 builder.add_deltas(vec![(r1.clone(), 16), (r3.clone(), 12)]);
1062
1063 let (var_store, key_lookup) = builder.build();
1064 assert_eq!(var_store.item_variation_data.len(), 3);
1065 assert_eq!(key_lookup.map.len(), 11);
1066
1067 assert_eq!(
1069 var_store.item_variation_data[0]
1070 .as_ref()
1071 .unwrap()
1072 .region_indexes,
1073 vec![0, 2]
1074 );
1075
1076 assert_eq!(
1079 var_store.item_variation_data[1]
1080 .as_ref()
1081 .unwrap()
1082 .region_indexes,
1083 vec![0, 1]
1084 );
1085 }
1086
1087 #[test]
1088 #[allow(clippy::redundant_clone)]
1089 fn to_binary() {
1090 let [r1, r2, r3] = test_regions();
1091
1092 let mut builder = VariationStoreBuilder::new(2);
1093 builder.add_deltas(vec![(r1.clone(), 512), (r2, 1000), (r3.clone(), 265)]);
1094 builder.add_deltas(vec![(r1.clone(), -3), (r3.clone(), 20)]);
1095 builder.add_deltas(vec![(r1.clone(), -12), (r3.clone(), 7)]);
1096 builder.add_deltas(vec![(r1.clone(), -11), (r3.clone(), 8)]);
1097 builder.add_deltas(vec![(r1.clone(), -10), (r3.clone(), 9)]);
1098 let (table, _) = builder.build();
1099 let bytes = crate::dump_table(&table).unwrap();
1100 let data = FontData::new(&bytes);
1101
1102 let reloaded = read_fonts::tables::variations::ItemVariationStore::read(data).unwrap();
1103
1104 assert_eq!(reloaded.item_variation_data_count(), 2);
1105 let var_data_array = reloaded.item_variation_data();
1106
1107 let var_data = var_data_array.get(0).unwrap().unwrap();
1108 assert_eq!(var_data.region_indexes(), &[0, 2]);
1109 assert_eq!(var_data.item_count(), 4);
1110 assert_eq!(var_data.delta_set(0).collect::<Vec<_>>(), vec![-12, 7]);
1111 assert_eq!(var_data.delta_set(1).collect::<Vec<_>>(), vec![-11, 8]);
1112 assert_eq!(var_data.delta_set(2).collect::<Vec<_>>(), vec![-10, 9]);
1113 assert_eq!(var_data.delta_set(3).collect::<Vec<_>>(), vec![-3, 20]);
1114
1115 let var_data = var_data_array.get(1).unwrap().unwrap();
1116 assert_eq!(var_data.region_indexes(), &[0, 1, 2]);
1117 assert_eq!(var_data.item_count(), 1);
1118 assert_eq!(
1119 var_data.delta_set(0).collect::<Vec<_>>(),
1120 vec![512, 1000, 265]
1121 );
1122 }
1123
1124 #[test]
1125 fn reuse_identical_variation_data() {
1126 let _ = env_logger::builder().is_test(true).try_init();
1127 let [r1, r2, r3] = test_regions();
1128
1129 let mut builder = VariationStoreBuilder::new(2);
1130 let k1 = builder.add_deltas(vec![(r1.clone(), 5), (r2, 10), (r3.clone(), 15)]);
1131 let k2 = builder.add_deltas(vec![(r1.clone(), -12), (r3.clone(), 7)]);
1132 let k3 = builder.add_deltas(vec![(r1.clone(), -12), (r3.clone(), 7)]);
1133 let k4 = builder.add_deltas(vec![(r1, 322), (r3, 532)]);
1134
1135 let (_, key_lookup) = builder.build();
1137 assert_eq!(k2, k3);
1138 assert_ne!(k1, k2);
1139 assert_ne!(k1, k4);
1140 assert_eq!(key_lookup.map.len(), 3);
1141 }
1142
1143 #[test]
1147 #[allow(clippy::redundant_clone)]
1148 fn long_deltas_split() {
1149 let [r1, r2, _] = test_regions();
1150 let mut builder = VariationStoreBuilder::new(2);
1151 builder.add_deltas(vec![(r1.clone(), 1), (r2.clone(), 2)]);
1153 builder.add_deltas(vec![(r1.clone(), 3), (r2.clone(), 4)]);
1154 builder.add_deltas(vec![(r1.clone(), 5), (r2.clone(), 6)]);
1155 builder.add_deltas(vec![(r1.clone(), 0xffff + 1), (r2.clone(), 0xffff + 2)]);
1157 let mut encoder = builder.encoder();
1158 assert_eq!(encoder.encodings.len(), 2);
1159 encoder.optimize();
1160 assert_eq!(encoder.encodings.len(), 2);
1161 }
1162
1163 #[test]
1165 #[allow(clippy::redundant_clone)]
1166 fn long_deltas_combine() {
1167 let [r1, r2, _] = test_regions();
1168 let mut builder = VariationStoreBuilder::new(2);
1169 builder.add_deltas(vec![(r1.clone(), 1), (r2.clone(), 2)]);
1171 builder.add_deltas(vec![(r1.clone(), 3), (r2.clone(), 4)]);
1172 builder.add_deltas(vec![(r1.clone(), 0xffff + 1), (r2.clone(), 0xffff + 2)]);
1174
1175 let mut encoder = builder.encoder();
1176 assert_eq!(encoder.encodings.len(), 2);
1177 assert_eq!(encoder.encodings[0].shape.overhead(), 14); assert_eq!(encoder.encodings[0].cost(), 14 + 4); assert_eq!(encoder.encodings[1].shape.overhead(), 14);
1180 assert_eq!(encoder.encodings[1].cost(), 14 + 8); encoder.optimize();
1182 assert_eq!(encoder.encodings.len(), 1);
1183 }
1184
1185 #[test]
1187 #[allow(clippy::redundant_clone)]
1188 fn combine_many_shapes() {
1189 let _ = env_logger::builder().is_test(true).try_init();
1190 let [r1, r2, r3] = test_regions();
1191 let mut builder = VariationStoreBuilder::new(2);
1192 builder.add_deltas(vec![(r1.clone(), 0xffff + 5)]); builder.add_deltas(vec![(r1.clone(), 2)]); builder.add_deltas(vec![(r1.clone(), 300)]); builder.add_deltas(vec![(r2.clone(), 0xffff + 5)]); builder.add_deltas(vec![(r2.clone(), 2)]); builder.add_deltas(vec![(r2.clone(), 300)]); builder.add_deltas(vec![(r3.clone(), 0xffff + 5)]); builder.add_deltas(vec![(r3.clone(), 2)]); builder.add_deltas(vec![(r3.clone(), 300)]); let mut encoder = builder.encoder();
1204 encoder.optimize();
1205 assert_eq!(encoder.encodings.len(), 3);
1207 assert!(encoder.encodings[0]
1208 .compute_gain(&encoder.encodings[1])
1209 .is_negative());
1210 }
1211
1212 #[test]
1213 #[allow(clippy::redundant_clone)]
1214 fn combine_two_big_fellas() {
1215 let _ = env_logger::builder().is_test(true).try_init();
1216 let [r1, r2, r3] = test_regions();
1217 let mut builder = VariationStoreBuilder::new(2);
1218 builder.add_deltas(vec![(r1.clone(), 0xffff + 5)]); builder.add_deltas(vec![(r2.clone(), 0xffff + 5)]); builder.add_deltas(vec![(r3.clone(), 0xffff + 5)]); let mut encoder = builder.encoder();
1225 assert_eq!(encoder.encodings[0].cost(), 16);
1226 let merge_cost = 2 + 4 + 8; assert_eq!(
1230 encoder.encodings[0].compute_gain(&encoder.encodings[1]),
1231 16 - merge_cost
1232 );
1233 encoder.optimize();
1234
1235 let next_merge_cost = 2
1237 + 2 * 4 + 12; assert_eq!(encoder.encodings.len(), 2);
1240 assert_eq!(encoder.encodings[0].cost(), 16);
1241 assert_eq!(
1242 encoder.encodings[0].compute_gain(&encoder.encodings[1]),
1243 16 - next_merge_cost
1244 );
1245 }
1246
1247 #[test]
1250 fn ensure_zero_deltas_dont_write() {
1251 let _ = env_logger::builder().is_test(true).try_init();
1252 let [r1, r2, _] = test_regions();
1253 let mut builder = VariationStoreBuilder::new(2);
1254 builder.add_deltas(vec![(r1.clone(), 0), (r2.clone(), 4)]);
1255 let _ = builder.build();
1256 }
1257
1258 #[test]
1261 fn ensure_all_zeros_dont_write() {
1262 let _ = env_logger::builder().is_test(true).try_init();
1263 let [r1, r2, _] = test_regions();
1264 let mut builder = VariationStoreBuilder::new(2);
1265 builder.add_deltas(vec![(r1.clone(), 0), (r2.clone(), 0)]);
1266 let _ = builder.build();
1267 }
1268
1269 #[test]
1270 fn vardata_region_indices_order() {
1271 let r0 = VariationRegion::new(vec![reg_coords(0.0, 0.5, 1.0)]);
1272 let r1 = VariationRegion::new(vec![reg_coords(0.5, 1.0, 1.0)]);
1273
1274 let mut builder = VariationStoreBuilder::new(1);
1275 builder.add_deltas(vec![(r0.clone(), 1), (r1.clone(), 2)]);
1276 builder.add_deltas(vec![(r0.clone(), 3), (r1.clone(), 256)]);
1279
1280 let (store, _varidx_map) = builder.build();
1281
1282 assert_eq!(store.variation_region_list.variation_regions.len(), 2);
1283 assert_eq!(store.item_variation_data.len(), 1);
1284
1285 let var_data = store.item_variation_data[0].as_ref().unwrap();
1286
1287 assert_eq!(var_data.item_count, 2);
1288 assert_eq!(var_data.word_delta_count, 1);
1289 assert_eq!(var_data.region_indexes, vec![1, 0]);
1295 assert_eq!(
1296 var_data.delta_sets,
1297 vec![
1302 0, 2, 1, 1, 0, 3, ],
1309 );
1310 }
1311
1312 #[test]
1313 fn unoptimized_version() {
1314 let r0 = VariationRegion::new(vec![reg_coords(0.0, 0.5, 1.0)]);
1315 let r1 = VariationRegion::new(vec![reg_coords(0.5, 1.0, 1.0)]);
1316
1317 let mut builder = VariationStoreBuilder::new_with_implicit_indices(1);
1318 builder.add_deltas(vec![(r0.clone(), 1), (r1.clone(), 2)]);
1319 builder.add_deltas(vec![(r0.clone(), 1), (r1.clone(), 2)]);
1322 builder.add_deltas(vec![(r0.clone(), 3), (r1.clone(), 256)]);
1323 builder.add_deltas(vec![(r0.clone(), 3), (r1.clone(), 256)]);
1324
1325 let (ivs, key_map) = builder.build();
1326 assert_eq!(ivs.item_variation_data.len(), 1);
1328
1329 let var_data = ivs.item_variation_data[0].as_ref().unwrap();
1330 assert_eq!(var_data.item_count, 4);
1331 assert_eq!(var_data.region_indexes, vec![1, 0]);
1332
1333 assert_eq!(
1334 var_data.delta_sets,
1335 &[
1336 0x0, 0x2, 0x1, 0x0, 0x2, 0x1, 0x1, 0x0, 0x3, 0x1, 0x0, 0x3, ]
1345 );
1346
1347 assert_eq!(key_map.map.len(), 4);
1349 assert!(key_map
1350 .map
1351 .iter()
1352 .all(|(key, idx)| *key == idx.delta_set_inner_index as u32))
1353 }
1354
1355 #[test]
1356 fn delta_set_ordering() {
1357 let left = DeltaSet(vec![(0, 1), (1, 2), (2, -11)]);
1358 let right = DeltaSet(vec![(0, 1), (1, 2)]);
1359
1360 assert!(left.0 > right.0);
1363 assert!(left < right);
1365
1366 let left = DeltaSet(vec![(0, 1), (1, 2), (2, 11)]);
1368 let right = DeltaSet(vec![(0, 1), (1, 2)]);
1369
1370 assert!(left > right);
1371 let left = DeltaSet(vec![(0, 1), (1, 2), (2, -11)]);
1372 let right = DeltaSet(vec![(0, 1), (1, 2), (3, 0)]);
1373
1374 assert!(left < right);
1375
1376 let left = DeltaSet(vec![(0, 1), (1, -2), (2, -11)]);
1378 let right = DeltaSet(vec![(0, 1), (2, -11)]);
1379 assert!(left < right)
1380 }
1381
1382 #[test]
1383 fn no_duplicate_zero_delta_sets() {
1384 let r0 = VariationRegion::new(vec![reg_coords(0.0, 5.0, 1.0)]);
1385 let r1 = VariationRegion::new(vec![reg_coords(0.5, 1.0, 1.0)]);
1386 let mut builder = VariationStoreBuilder::new(1);
1387 let varidxes = vec![
1388 builder.add_deltas::<i32>(Vec::new()),
1392 builder.add_deltas(vec![(r0.clone(), 50), (r1.clone(), 100)]),
1393 builder.add_deltas(vec![(r0.clone(), 0), (r1.clone(), 0)]),
1396 builder.add_deltas(vec![(r0.clone(), 50), (r1.clone(), 100)]),
1399 builder.add_deltas(vec![(r0.clone(), 0), (r1.clone(), 100)]),
1402 ];
1403 let (store, key_map) = builder.build();
1404
1405 let varidx_map: Vec<u32> = varidxes
1406 .into_iter()
1407 .map(|idx| key_map.get(idx).unwrap().into())
1408 .collect::<Vec<_>>();
1409
1410 assert_eq!(store.variation_region_list.variation_regions.len(), 2);
1411 assert_eq!(store.item_variation_data.len(), 1);
1412
1413 let var_data = store.item_variation_data[0].as_ref().unwrap();
1414
1415 assert_eq!(var_data.item_count, 3);
1416 assert_eq!(var_data.word_delta_count, 0);
1417 assert_eq!(var_data.region_indexes, vec![0, 1]);
1418 assert_eq!(var_data.delta_sets, vec![0, 0, 0, 100, 50, 100],);
1419 assert_eq!(varidx_map, vec![0, 2, 0, 2, 1]);
1423 }
1424
1425 #[test]
1426 fn zero_deltas_deduplicate_with_absent_regions() {
1427 let r0 = VariationRegion::new(vec![reg_coords(0.0, 0.5, 1.0)]);
1435 let r1 = VariationRegion::new(vec![reg_coords(0.5, 1.0, 1.0)]);
1436 let r_intermediate = VariationRegion::new(vec![reg_coords(0.0, 0.7, 1.0)]);
1438
1439 let mut builder = VariationStoreBuilder::new(1);
1440
1441 let idx_a = builder.add_deltas(vec![
1445 (r0.clone(), -87_i16),
1446 (r1.clone(), 20_i16),
1447 (r_intermediate.clone(), 0_i16),
1448 ]);
1449 let idx_b = builder.add_deltas(vec![(r0.clone(), -87_i16), (r1.clone(), 20_i16)]);
1451
1452 let (store, key_map) = builder.build();
1453
1454 assert_eq!(
1456 key_map.get(idx_a),
1457 key_map.get(idx_b),
1458 "explicit zero delta should deduplicate with absent region"
1459 );
1460
1461 assert_eq!(store.variation_region_list.variation_regions.len(), 2);
1464 }
1465
1466 #[test]
1467 fn prune_unused_regions() {
1468 let r0 = VariationRegion::new(vec![reg_coords(-1.0, -0.5, 0.0)]);
1470 let r1 = VariationRegion::new(vec![reg_coords(-1.0, -1.0, 0.0)]);
1471 let r2 = VariationRegion::new(vec![reg_coords(0.0, 0.5, 1.0)]);
1472 let r3 = VariationRegion::new(vec![reg_coords(0.0, 1.0, 1.0)]);
1473 let mut builder = VariationStoreBuilder::new(1);
1474 builder.add_deltas(vec![
1475 (r0.clone(), 0),
1476 (r1.clone(), 50),
1477 (r2.clone(), 0),
1478 (r3.clone(), 100),
1479 ]);
1480 let (store, _) = builder.build();
1481
1482 assert_eq!(store.variation_region_list.variation_regions.len(), 2);
1484 assert_eq!(store.item_variation_data.len(), 1);
1485
1486 let var_data = store.item_variation_data[0].as_ref().unwrap();
1487 assert_eq!(var_data.item_count, 1);
1488 assert_eq!(var_data.word_delta_count, 0);
1489 assert_eq!(var_data.region_indexes, vec![0, 1]); assert_eq!(var_data.delta_sets, vec![50, 100]);
1491 }
1492
1493 #[test]
1494 fn we_match_fonttools_stable_order() {
1495 use rand::{seq::SliceRandom, thread_rng};
1496
1497 let mut builder = VariationStoreBuilder::new(1);
1498 let r1 = VariationRegion::new(vec![reg_coords(-1.0, -1.0, 0.0)]);
1499 let r2 = VariationRegion::new(vec![reg_coords(0.0, 1.0, 1.0)]);
1500
1501 let mut delta_sets = vec![
1502 vec![(r1.clone(), 1), (r2.clone(), 2)],
1504 vec![(r1.clone(), 3), (r2.clone(), 4)],
1505 vec![(r1.clone(), 5), (r2.clone(), 6)],
1506 vec![(r1.clone(), 7), (r2.clone(), 8)],
1507 vec![(r1.clone(), 9), (r2.clone(), 10)],
1508 vec![(r1.clone(), 11), (r2.clone(), 12)],
1509 vec![(r1.clone(), 13), (r2.clone(), 14)],
1510 vec![(r1.clone(), 0), (r2.clone(), -130)],
1512 vec![(r1.clone(), 0), (r2.clone(), -129)],
1513 vec![(r1.clone(), 0), (r2.clone(), 128)],
1514 vec![(r1.clone(), 0), (r2.clone(), 129)],
1515 vec![(r1.clone(), 0), (r2.clone(), -1)],
1524 ];
1525
1526 let mut rng = thread_rng();
1528 delta_sets.shuffle(&mut rng);
1529 for deltas in delta_sets {
1530 builder.add_deltas(deltas);
1531 }
1532
1533 let (store, _) = builder.build();
1534
1535 let bytes = crate::dump_table(&store).unwrap();
1536 let data = FontData::new(&bytes);
1537 let reloaded = read_fonts::tables::variations::ItemVariationStore::read(data).unwrap();
1538
1539 assert_eq!(reloaded.item_variation_data_count(), 2);
1540 let var_data_array = reloaded.item_variation_data();
1541
1542 let var_data = var_data_array.get(0).unwrap().unwrap();
1543 assert_eq!(var_data.region_indexes(), &[0, 1]);
1544 assert_eq!(var_data.item_count(), 8);
1546 assert_eq!(var_data.delta_set(0).collect::<Vec<_>>(), vec![0, -1]);
1547 assert_eq!(var_data.delta_set(1).collect::<Vec<_>>(), vec![1, 2]);
1548 assert_eq!(var_data.delta_set(2).collect::<Vec<_>>(), vec![3, 4]);
1549 assert_eq!(var_data.delta_set(3).collect::<Vec<_>>(), vec![5, 6]);
1550 assert_eq!(var_data.delta_set(4).collect::<Vec<_>>(), vec![7, 8]);
1551 assert_eq!(var_data.delta_set(5).collect::<Vec<_>>(), vec![9, 10]);
1552 assert_eq!(var_data.delta_set(6).collect::<Vec<_>>(), vec![11, 12]);
1553 assert_eq!(var_data.delta_set(7).collect::<Vec<_>>(), vec![13, 14]);
1554
1555 let var_data = var_data_array.get(1).unwrap().unwrap();
1556 assert_eq!(var_data.region_indexes(), &[1]);
1557 assert_eq!(var_data.item_count(), 4);
1558 assert_eq!(var_data.delta_set(0).collect::<Vec<_>>(), vec![-130]);
1560 assert_eq!(var_data.delta_set(1).collect::<Vec<_>>(), vec![-129]);
1561 assert_eq!(var_data.delta_set(2).collect::<Vec<_>>(), vec![128]);
1562 assert_eq!(var_data.delta_set(3).collect::<Vec<_>>(), vec![129]);
1563 }
1564}