devpage_design/tiles/
cache.rs1use std::collections::{BTreeMap, HashMap};
7
8use kurbo::Rect as KRect;
9
10use crate::scene::Theme;
11
12use super::*;
13
14struct Entry<T> {
17 value: T,
18 cost: usize,
19 stale: bool,
20 used: u64,
21}
22
23pub struct Cache<T, K: Tiled = Key> {
29 cap: usize,
30 cost: usize,
31 clock: u64,
32 entries: HashMap<K, Entry<T>>,
33 by_use: BTreeMap<u64, K>,
34}
35
36impl<T, K: Tiled> Cache<T, K> {
37 pub fn new(cap: usize) -> Cache<T, K> {
38 Cache {
39 cap,
40 cost: 0,
41 clock: 0,
42 entries: HashMap::new(),
43 by_use: BTreeMap::new(),
44 }
45 }
46
47 pub fn get(&mut self, key: &K) -> Option<(&T, bool)> {
49 self.clock += 1;
50 let e = self.entries.get_mut(key)?;
51 self.by_use.remove(&e.used);
52 e.used = self.clock;
53 self.by_use.insert(self.clock, *key);
54 Some((&e.value, e.stale))
55 }
56
57 pub fn peek(&self, key: &K) -> Option<(&T, bool)> {
59 self.entries.get(key).map(|e| (&e.value, e.stale))
60 }
61
62 pub fn insert(&mut self, key: K, value: T, cost: usize) -> Vec<(K, T)> {
66 self.remove(&key);
68 let mut dropped = Vec::new();
69 self.clock += 1;
70 self.entries.insert(
71 key,
72 Entry {
73 value,
74 cost,
75 stale: false,
76 used: self.clock,
77 },
78 );
79 self.by_use.insert(self.clock, key);
80 self.cost += cost;
81 while self.cost > self.cap {
82 let Some((_, oldest)) = self.by_use.iter().next().map(|(u, k)| (*u, *k)) else {
83 break;
84 };
85 if oldest == key {
86 break;
87 }
88 if let Some(v) = self.remove(&oldest) {
89 dropped.push((oldest, v));
90 }
91 }
92 dropped
93 }
94
95 pub fn remove(&mut self, key: &K) -> Option<T> {
97 let e = self.entries.remove(key)?;
98 self.by_use.remove(&e.used);
99 self.cost -= e.cost;
100 Some(e.value)
101 }
102
103 pub fn damage(&mut self, theme: Theme, origin: (f64, f64), rects: &[KRect]) {
107 self.damage_where(|k| k.key().theme == theme, origin, rects);
108 }
109
110 pub fn damage_where(
113 &mut self,
114 which: impl Fn(&K) -> bool,
115 origin: (f64, f64),
116 rects: &[KRect],
117 ) {
118 for (k, e) in &mut self.entries {
119 if !e.stale && which(k) && k.key().overlaps(origin, rects) {
120 e.stale = true;
121 }
122 }
123 }
124
125 pub fn damage_all(&mut self, theme: Theme) {
127 self.damage_all_where(|k| k.key().theme == theme);
128 }
129
130 pub fn damage_all_where(&mut self, which: impl Fn(&K) -> bool) {
132 for (k, e) in &mut self.entries {
133 if which(k) {
134 e.stale = true;
135 }
136 }
137 }
138
139 pub fn forget(&mut self, which: impl Fn(&K) -> bool) {
141 let gone: Vec<K> = self.entries.keys().filter(|k| which(k)).copied().collect();
142 for k in gone {
143 self.remove(&k);
144 }
145 }
146
147 pub fn iter(&self) -> impl Iterator<Item = (&K, &T, bool)> {
149 self.entries.iter().map(|(k, e)| (k, &e.value, e.stale))
150 }
151
152 pub fn clear(&mut self) -> Vec<T> {
154 self.by_use.clear();
155 self.cost = 0;
156 self.entries.drain().map(|(_, e)| e.value).collect()
157 }
158
159 pub fn len(&self) -> usize {
160 self.entries.len()
161 }
162
163 pub fn is_empty(&self) -> bool {
164 self.entries.is_empty()
165 }
166
167 pub fn cost(&self) -> usize {
169 self.cost
170 }
171}
172
173#[cfg(test)]
174mod tests;