swash/shape/
cache.rs
1use super::at::FeatureStore;
2use super::engine::EngineMetadata;
3use super::internal::var::Fvar;
4use crate::{charmap::CharmapProxy, metrics::MetricsProxy, FontRef};
5
6use alloc::vec::Vec;
7
8pub type Epoch = u64;
9
10pub struct FontEntry {
11 pub metrics: MetricsProxy,
12 pub charmap: CharmapProxy,
13 pub coord_count: u16,
14 pub metadata: EngineMetadata,
15}
16
17impl FontEntry {
18 pub fn new(font: &FontRef) -> Self {
19 Self {
20 metrics: MetricsProxy::from_font(font),
21 charmap: CharmapProxy::from_font(font),
22 coord_count: Fvar::from_font(font)
23 .map(|fvar| fvar.axis_count())
24 .unwrap_or(0),
25 metadata: EngineMetadata::from_font(font),
26 }
27 }
28}
29
30pub struct FeatureEntry {
31 pub epoch: Epoch,
32 pub id: [u64; 2],
33 pub coords: Vec<i16>,
34 pub tags: [u32; 4],
35 pub store: FeatureStore,
36}
37
38pub struct FeatureCache {
39 entries: Vec<FeatureEntry>,
40 epoch: Epoch,
41 max_entries: usize,
42}
43
44pub enum FeatureCacheEntry<'a> {
45 New(&'a mut FeatureStore),
46 Present(&'a mut FeatureStore),
47}
48
49impl FeatureCache {
50 pub fn new(max_entries: usize) -> Self {
51 Self {
52 entries: Default::default(),
53 epoch: 0,
54 max_entries,
55 }
56 }
57
58 pub fn entry<'a>(
59 &'a mut self,
60 id: [u64; 2],
61 coords: &[i16],
62 has_feature_vars: bool,
63 tags: &[u32; 4],
64 ) -> FeatureCacheEntry<'a> {
65 match self.find_entry(id, coords, has_feature_vars, tags) {
66 (true, index) => {
67 let entry = &mut self.entries[index];
68 entry.epoch = self.epoch;
69 FeatureCacheEntry::Present(&mut entry.store)
70 }
71 (false, index) => {
72 self.epoch += 1;
73 let entry = &mut self.entries[index];
74 entry.epoch = self.epoch;
75 FeatureCacheEntry::New(&mut entry.store)
76 }
77 }
78 }
79
80 fn find_entry(
81 &mut self,
82 id: [u64; 2],
83 coords: &[i16],
84 has_feature_vars: bool,
85 tags: &[u32; 4],
86 ) -> (bool, usize) {
87 let epoch = self.epoch;
88 let mut lowest_serial = epoch;
89 let mut lowest_index = 0;
90 for (i, entry) in self.entries.iter().enumerate() {
91 if entry.id == id && &entry.tags == tags {
92 if has_feature_vars && coords != &entry.coords[..] {
93 continue;
94 }
95 return (true, i);
96 }
97 if entry.epoch < lowest_serial {
98 lowest_serial = entry.epoch;
99 lowest_index = i;
100 }
101 }
102 if self.entries.len() < self.max_entries {
103 lowest_index = self.entries.len();
104 self.entries.push(FeatureEntry {
105 epoch,
106 id,
107 coords: Vec::from(coords),
108 store: FeatureStore::default(),
109 tags: *tags,
110 });
111 } else {
112 let entry = &mut self.entries[lowest_index];
113 entry.epoch = epoch;
114 entry.id = id;
115 entry.coords.clear();
116 entry.coords.extend_from_slice(coords);
117 entry.store.clear();
118 entry.tags = *tags;
119 }
120 (false, lowest_index)
121 }
122}