ferroui_skia/
two_level_cache.rs1type KeyComparer<TKey> = Box<dyn Fn(&TKey, &TKey) -> bool + Send>;
3
4type EvictionAction<TValue> = Box<dyn Fn(&TValue) + Send>;
6
7pub struct TwoLevelCache<TKey, TValue: Clone> {
16 secondary_size: usize,
17 primary: Option<(TKey, TValue)>,
18 secondary: Option<Vec<(TKey, TValue)>>,
19 eviction_action: Option<EvictionAction<TValue>>,
20 comparer: KeyComparer<TKey>,
21}
22
23impl<TKey: PartialEq + Clone + 'static, TValue: Clone> TwoLevelCache<TKey, TValue> {
24 pub fn new() -> Self {
27 Self::with_options(3, None, None)
28 }
29
30 pub fn with_secondary_size(secondary_size: i32) -> Self {
37 Self::with_options(secondary_size, None, None)
38 }
39
40 pub fn with_options(
48 secondary_size: i32,
49 eviction_action: Option<EvictionAction<TValue>>,
50 comparer: Option<KeyComparer<TKey>>,
51 ) -> Self {
52 let Ok(secondary_size) = usize::try_from(secondary_size) else {
53 panic!("secondary_size is out of range: {secondary_size}");
54 };
55
56 Self {
57 secondary_size,
58 primary: None,
59 secondary: None,
60 eviction_action,
61 comparer: comparer.unwrap_or_else(|| Box::new(|a, b| a == b)),
62 }
63 }
64}
65
66impl<TKey: PartialEq + Clone + 'static, TValue: Clone> Default for TwoLevelCache<TKey, TValue> {
67 fn default() -> Self {
68 Self::new()
69 }
70}
71
72impl<TKey: Clone, TValue: Clone> TwoLevelCache<TKey, TValue> {
73 pub fn try_get(&self, key: &TKey) -> Option<TValue> {
75 if let Some((primary_key, primary_value)) = &self.primary {
76 if (self.comparer)(primary_key, key) {
77 return Some(primary_value.clone());
78 }
79 }
80
81 if let Some(secondary) = &self.secondary {
82 for (secondary_key, value) in secondary {
83 if (self.comparer)(secondary_key, key) {
84 return Some(value.clone());
85 }
86 }
87 }
88
89 None
90 }
91
92 pub fn get_or_add(&mut self, key: TKey, factory: impl FnOnce(&TKey) -> TValue) -> TValue {
95 if let Some(existing) = self.try_get(&key) {
97 return existing;
98 }
99
100 let value = factory(&key);
102
103 if self.primary.is_none() {
105 self.primary = Some((key, value.clone()));
106 return value;
107 }
108
109 if self.secondary_size == 0 {
111 if let Some((_, evicted)) = self.primary.replace((key, value.clone())) {
112 self.evict(&evicted);
113 }
114 return value;
115 }
116
117 let secondary_size = self.secondary_size;
120 let secondary = self.secondary.get_or_insert_with(|| Vec::with_capacity(secondary_size));
121 let evicted = if secondary.len() == secondary_size { secondary.pop() } else { None };
122 secondary.insert(0, (key, value.clone()));
123
124 if let Some((_, evicted)) = evicted {
125 self.evict(&evicted);
126 }
127
128 value
129 }
130
131 pub fn clear_and_dispose(&mut self) {
133 if let Some((_, value)) = self.primary.take() {
134 self.evict(&value);
135 }
136
137 if let Some(secondary) = self.secondary.take() {
138 for (_, value) in &secondary {
139 self.evict(value);
140 }
141 }
142 }
143
144 fn evict(&self, value: &TValue) {
145 if let Some(action) = &self.eviction_action {
146 action(value);
147 }
148 }
149}