swash/
cache.rs

1use super::FontRef;
2use alloc::vec::Vec;
3
4/// Uniquely generated value for identifying and caching fonts.
5#[derive(Copy, Clone, PartialOrd, Ord, PartialEq, Eq, Hash, Debug)]
6pub struct CacheKey(pub(crate) u64);
7
8impl CacheKey {
9    /// Generates a new cache key.
10    pub fn new() -> Self {
11        use core::sync::atomic::{AtomicUsize, Ordering};
12        static KEY: AtomicUsize = AtomicUsize::new(1);
13        Self(KEY.fetch_add(1, Ordering::Relaxed).try_into().unwrap())
14    }
15
16    /// Returns the underlying value of the key.
17    pub fn value(self) -> u64 {
18        self.0
19    }
20}
21
22impl Default for CacheKey {
23    fn default() -> Self {
24        Self::new()
25    }
26}
27
28pub struct FontCache<T> {
29    entries: Vec<Entry<T>>,
30    max_entries: usize,
31    epoch: u64,
32}
33
34impl<T> FontCache<T> {
35    pub fn new(max_entries: usize) -> Self {
36        Self {
37            entries: Vec::new(),
38            epoch: 0,
39            max_entries,
40        }
41    }
42
43    pub fn get<'a>(
44        &'a mut self,
45        font: &FontRef,
46        id_override: Option<[u64; 2]>,
47        mut f: impl FnMut(&FontRef) -> T,
48    ) -> ([u64; 2], &'a T) {
49        let id = id_override.unwrap_or([font.key.value(), u64::MAX]);
50        let (found, index) = self.find(id);
51        if found {
52            let entry = &mut self.entries[index];
53            entry.epoch = self.epoch;
54            (entry.id, &entry.data)
55        } else {
56            self.epoch += 1;
57            let data = f(font);
58            if index == self.entries.len() {
59                self.entries.push(Entry {
60                    epoch: self.epoch,
61                    id,
62                    data,
63                });
64                let entry = self.entries.last().unwrap();
65                (id, &entry.data)
66            } else {
67                let entry = &mut self.entries[index];
68                entry.epoch = self.epoch;
69                entry.id = id;
70                entry.data = data;
71                (id, &entry.data)
72            }
73        }
74    }
75
76    fn find(&self, id: [u64; 2]) -> (bool, usize) {
77        let mut lowest = 0;
78        let mut lowest_epoch = self.epoch;
79        for (i, entry) in self.entries.iter().enumerate() {
80            if entry.id == id {
81                return (true, i);
82            }
83            if entry.epoch < lowest_epoch {
84                lowest_epoch = entry.epoch;
85                lowest = i;
86            }
87        }
88        if self.entries.len() < self.max_entries {
89            (false, self.entries.len())
90        } else {
91            (false, lowest)
92        }
93    }
94}
95
96struct Entry<T> {
97    epoch: u64,
98    id: [u64; 2],
99    data: T,
100}