1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
use super::FontRef;

/// Uniquely generated value for identifying and caching fonts.
#[derive(Copy, Clone, PartialOrd, Ord, PartialEq, Eq, Hash, Debug)]
pub struct CacheKey(pub(crate) u64);

impl CacheKey {
    /// Generates a new cache key.
    pub fn new() -> Self {
        use core::sync::atomic::{AtomicU64, Ordering};
        static KEY: AtomicU64 = AtomicU64::new(1);
        Self(KEY.fetch_add(1, Ordering::Relaxed))
    }

    /// Returns the underlying value of the key.
    pub fn value(self) -> u64 {
        self.0
    }
}

impl Default for CacheKey {
    fn default() -> Self {
        Self::new()
    }
}

pub struct FontCache<T> {
    entries: Vec<Entry<T>>,
    max_entries: usize,
    epoch: u64,
}

impl<T> FontCache<T> {
    pub fn new(max_entries: usize) -> Self {
        Self {
            entries: Vec::new(),
            epoch: 0,
            max_entries,
        }
    }

    pub fn get<'a>(
        &'a mut self,
        font: &FontRef,
        id_override: Option<[u64; 2]>,
        mut f: impl FnMut(&FontRef) -> T,
    ) -> ([u64; 2], &'a T) {
        let id = id_override.unwrap_or([font.key.value(), u64::MAX]);
        let (found, index) = self.find(id);
        if found {
            let entry = &mut self.entries[index];
            entry.epoch = self.epoch;
            (entry.id, &entry.data)
        } else {
            self.epoch += 1;
            let data = f(font);
            if index == self.entries.len() {
                self.entries.push(Entry {
                    epoch: self.epoch,
                    id,
                    data,
                });
                let entry = self.entries.last().unwrap();
                (id, &entry.data)
            } else {
                let entry = &mut self.entries[index];
                entry.epoch = self.epoch;
                entry.id = id;
                entry.data = data;
                (id, &entry.data)
            }
        }
    }

    fn find(&self, id: [u64; 2]) -> (bool, usize) {
        let mut lowest = 0;
        let mut lowest_epoch = self.epoch;
        for (i, entry) in self.entries.iter().enumerate() {
            if entry.id == id {
                return (true, i);
            }
            if entry.epoch < lowest_epoch {
                lowest_epoch = entry.epoch;
                lowest = i;
            }
        }
        if self.entries.len() < self.max_entries {
            (false, self.entries.len())
        } else {
            (false, lowest)
        }
    }
}

struct Entry<T> {
    epoch: u64,
    id: [u64; 2],
    data: T,
}