devpage_design/tiles/
cache.rs1use std::collections::{BTreeMap, HashMap};
7
8use kurbo::Rect as KRect;
9
10use crate::resolve::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) {
120 continue;
121 }
122 let r = k.key().rect(origin);
123 if rects
124 .iter()
125 .any(|d| d.x0 < r.x1 && d.x1 > r.x0 && d.y0 < r.y1 && d.y1 > r.y0)
126 {
127 e.stale = true;
128 }
129 }
130 }
131
132 pub fn damage_all(&mut self, theme: Theme) {
134 self.damage_all_where(|k| k.key().theme == theme);
135 }
136
137 pub fn damage_all_where(&mut self, which: impl Fn(&K) -> bool) {
139 for (k, e) in &mut self.entries {
140 if which(k) {
141 e.stale = true;
142 }
143 }
144 }
145
146 pub fn forget(&mut self, which: impl Fn(&K) -> bool) {
148 let gone: Vec<K> = self.entries.keys().filter(|k| which(k)).copied().collect();
149 for k in gone {
150 self.remove(&k);
151 }
152 }
153
154 pub fn iter(&self) -> impl Iterator<Item = (&K, &T, bool)> {
156 self.entries.iter().map(|(k, e)| (k, &e.value, e.stale))
157 }
158
159 pub fn clear(&mut self) -> Vec<T> {
161 self.by_use.clear();
162 self.cost = 0;
163 self.entries.drain().map(|(_, e)| e.value).collect()
164 }
165
166 pub fn len(&self) -> usize {
167 self.entries.len()
168 }
169
170 pub fn is_empty(&self) -> bool {
171 self.entries.is_empty()
172 }
173
174 pub fn cost(&self) -> usize {
176 self.cost
177 }
178}
179
180#[cfg(test)]
181mod tests;