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}