ferroui_base/utilities/
weak_hash_list.rs1use std::any::{Any, TypeId};
2use std::cell::{Cell, RefCell};
3use std::collections::HashMap;
4use std::hash::{Hash, Hasher};
5use std::rc::{Rc, Weak};
6
7pub struct WeakHashList<T: ?Sized + 'static> {
13 dic: Option<OrderedDic<T>>,
14 arr: Option<Vec<Option<Weak<T>>>>,
15 arr_count: usize,
16 need_compact: Cell<bool>,
17}
18
19struct Key<T: ?Sized> {
22 weak: Option<Weak<T>>,
23 strong: Option<Rc<T>>,
24 hash_code: usize,
25}
26
27impl<T: ?Sized> Key<T> {
28 fn make_strong(r: &Rc<T>) -> Self {
29 Self { hash_code: address(Rc::as_ptr(r)), weak: None, strong: Some(r.clone()) }
30 }
31
32 fn make_weak(r: &Rc<T>) -> Self {
33 Self { hash_code: address(Rc::as_ptr(r)), weak: Some(Rc::downgrade(r)), strong: None }
34 }
35}
36
37impl<T: ?Sized> Clone for Key<T> {
38 fn clone(&self) -> Self {
39 Self { weak: self.weak.clone(), strong: self.strong.clone(), hash_code: self.hash_code }
40 }
41}
42
43struct OrderedDic<T: ?Sized> {
47 entries: Vec<Option<(Key<T>, i32)>>,
48 free: Vec<usize>,
49 map: HashMap<Key<T>, usize>,
50}
51
52impl<T: ?Sized> OrderedDic<T> {
53 fn new() -> Self {
54 Self { entries: Vec::new(), free: Vec::new(), map: HashMap::new() }
55 }
56
57 fn is_empty(&self) -> bool {
58 self.map.is_empty()
59 }
60
61 fn get(&self, key: &Key<T>) -> Option<i32> {
62 self.map.get(key).and_then(|&slot| self.entries[slot].as_ref()).map(|(_, count)| *count)
63 }
64
65 fn set(&mut self, key: Key<T>, count: i32) {
68 if let Some(&slot) = self.map.get(&key) {
69 if let Some(entry) = &mut self.entries[slot] {
70 entry.1 = count;
71 }
72 return;
73 }
74 let slot = match self.free.pop() {
75 Some(slot) => {
76 self.entries[slot] = Some((key.clone(), count));
77 slot
78 }
79 None => {
80 self.entries.push(Some((key.clone(), count)));
81 self.entries.len() - 1
82 }
83 };
84 self.map.insert(key, slot);
85 }
86
87 fn remove(&mut self, key: &Key<T>) {
88 if let Some(slot) = self.map.remove(key) {
89 self.entries[slot] = None;
90 self.free.push(slot);
91 }
92 }
93
94 fn keys(&self) -> impl Iterator<Item = &Key<T>> {
96 self.entries.iter().flatten().map(|(key, _)| key)
97 }
98}
99
100impl<T: ?Sized> Hash for Key<T> {
101 fn hash<H: Hasher>(&self, state: &mut H) {
102 self.hash_code.hash(state);
103 }
104}
105
106impl<T: ?Sized> PartialEq for Key<T> {
108 fn eq(&self, y: &Self) -> bool {
109 let x = self;
110 if x.hash_code != y.hash_code {
111 return false;
112 }
113 if let Some(x_strong) = &x.strong {
114 if let Some(y_strong) = &y.strong {
115 return Rc::ptr_eq(x_strong, y_strong);
116 }
117 let Some(y_weak) = &y.weak else { return false };
118 return y_weak.upgrade().is_some_and(|weak_target| Rc::ptr_eq(&weak_target, x_strong));
119 } else if let Some(y_strong) = &y.strong {
120 let Some(x_weak) = &x.weak else { return false };
121 return x_weak.upgrade().is_some_and(|weak_target| Rc::ptr_eq(&weak_target, y_strong));
122 }
123 match x.weak.as_ref().and_then(Weak::upgrade) {
124 None => y.weak.as_ref().and_then(Weak::upgrade).is_none(),
125 Some(x_target) => y.weak.as_ref().and_then(Weak::upgrade).is_some_and(|y_target| Rc::ptr_eq(&x_target, &y_target)),
126 }
127 }
128}
129
130impl<T: ?Sized> Eq for Key<T> {}
131
132fn address<T: ?Sized>(ptr: *const T) -> usize {
133 ptr as *const () as usize
134}
135
136crate::ferro_thread_local! {
137 static LIST_POOLS: RefCell<HashMap<TypeId, Box<dyn Any>>> = RefCell::new(HashMap::new());
139}
140
141impl<T: ?Sized + 'static> Default for WeakHashList<T> {
142 fn default() -> Self {
143 Self::new()
144 }
145}
146
147impl<T: ?Sized + 'static> WeakHashList<T> {
148 pub const DEFAULT_ARRAY_SIZE: usize = 8;
149
150 pub fn new() -> Self {
151 Self { dic: None, arr: None, arr_count: 0, need_compact: Cell::new(false) }
152 }
153
154 pub fn is_empty(&self) -> bool {
155 match &self.dic {
156 Some(dic) => dic.is_empty(),
157 None => self.arr_count == 0,
158 }
159 }
160
161 pub fn need_compact(&self) -> bool {
162 self.need_compact.get()
163 }
164
165 pub fn add(&mut self, item: &Rc<T>) {
166 if let Some(dic) = &mut self.dic {
167 let strong_key = Key::make_strong(item);
168 if let Some(cnt) = dic.get(&strong_key) {
169 dic.set(strong_key, cnt + 1);
170 } else {
171 dic.set(Key::make_weak(item), 1);
172 }
173 return;
174 }
175
176 let arr = self.arr.get_or_insert_with(|| vec![None; Self::DEFAULT_ARRAY_SIZE]);
177
178 if self.arr_count < arr.len() {
179 arr[self.arr_count] = Some(Rc::downgrade(item));
180 self.arr_count += 1;
181 return;
182 }
183
184 for c in 0..self.arr_count {
186 if arr[c].as_ref().and_then(Weak::upgrade).is_none() {
187 arr[c] = Some(Rc::downgrade(item));
188 return;
189 }
190 }
191
192 let existing: Vec<Rc<T>> = arr.iter().filter_map(|r| r.as_ref().and_then(Weak::upgrade)).collect();
193 self.dic = Some(OrderedDic::new());
194 for target in &existing {
195 self.add(target);
196 }
197
198 self.add(item);
199
200 self.arr = None;
201 self.arr_count = 0;
202 }
203
204 pub fn remove(&mut self, item: &Rc<T>) {
205 if let Some(arr) = &mut self.arr {
206 for c in 0..self.arr_count {
207 if arr[c].as_ref().and_then(Weak::upgrade).is_some_and(|target| Rc::ptr_eq(&target, item)) {
208 arr[c] = None;
209 self.arr_compact();
210 return;
211 }
212 }
213 } else if let Some(dic) = &mut self.dic {
214 let strong_key = Key::make_strong(item);
215
216 if let Some(cnt) = dic.get(&strong_key) {
217 if cnt > 1 {
218 dic.set(strong_key, cnt - 1);
219 return;
220 }
221 }
222
223 dic.remove(&strong_key);
224 }
225 }
226
227 fn arr_compact(&mut self) {
228 if let Some(arr) = &mut self.arr {
229 let mut empty: Option<usize> = None;
230 for c in 0..self.arr_count {
231 let r = arr[c].clone();
232 if r.is_none() && empty.is_none() {
234 empty = Some(c);
235 }
236 if let (Some(_), Some(e)) = (&r, empty) {
238 arr[c] = None;
239 arr[e] = r;
240 empty = Some(e + 1);
241 }
242 }
243
244 if let Some(empty) = empty {
245 self.arr_count = empty;
246 }
247 }
248 }
249
250 pub fn compact(&mut self) {
251 if let Some(dic) = &mut self.dic {
252 let to_remove: Vec<Key<T>> = dic
253 .keys()
254 .filter(|key| key.weak.as_ref().and_then(Weak::upgrade).is_none())
255 .map(|key| Key { weak: key.weak.clone(), strong: None, hash_code: key.hash_code })
256 .collect();
257
258 for k in &to_remove {
259 dic.remove(k);
260 }
261 }
262 }
263
264 pub fn return_to_shared_pool(mut list: Vec<Rc<T>>) {
266 list.clear();
267 LIST_POOLS.with(|pools| {
268 pools
269 .borrow_mut()
270 .entry(TypeId::of::<T>())
271 .or_insert_with(|| Box::new(Vec::<Vec<Rc<T>>>::new()))
272 .downcast_mut::<Vec<Vec<Rc<T>>>>()
273 .expect("the pool of an item type holds lists of that type")
274 .push(list);
275 });
276 }
277
278 fn rent_from_shared_pool() -> Vec<Rc<T>> {
279 LIST_POOLS
280 .with(|pools| {
281 pools
282 .borrow_mut()
283 .get_mut(&TypeId::of::<T>())
284 .and_then(|pool| pool.downcast_mut::<Vec<Vec<Rc<T>>>>())
285 .and_then(Vec::pop)
286 })
287 .unwrap_or_default()
288 }
289
290 pub fn get_alive(&mut self, factory: Option<&dyn Fn() -> Vec<Rc<T>>>) -> Option<Vec<Rc<T>>> {
293 let mut pooled: Option<Vec<Rc<T>>> = None;
294 let mut new_list = || match factory {
295 Some(factory) => factory(),
296 None => Self::rent_from_shared_pool(),
297 };
298 if let Some(arr) = &mut self.arr {
299 let mut need_compact = false;
300 for c in 0..self.arr_count {
301 match arr[c].as_ref().and_then(Weak::upgrade) {
302 Some(target) => pooled.get_or_insert_with(&mut new_list).push(target),
303 None => {
304 arr[c] = None;
305 need_compact = true;
306 }
307 }
308 }
309 if need_compact {
310 self.arr_compact();
311 }
312 return pooled;
313 }
314 if let Some(dic) = &self.dic {
315 for key in dic.keys() {
316 match key.weak.as_ref().and_then(Weak::upgrade) {
317 Some(target) => pooled.get_or_insert_with(&mut new_list).push(target),
318 None => self.need_compact.set(true),
319 }
320 }
321 }
322
323 pooled
324 }
325}
326
327#[cfg(test)]
328mod tests {
329 use super::*;
330
331 fn strings(count: usize) -> Vec<Rc<str>> {
335 (0..count).map(|i| Rc::from(i.to_string())).collect()
336 }
337
338 #[test]
339 fn is_empty_works() {
340 let mut target = WeakHashList::<str>::new();
341 let one: Rc<str> = Rc::from("1");
342
343 assert!(target.is_empty());
344
345 target.add(&one);
346
347 assert!(!target.is_empty());
348
349 target.remove(&one);
350
351 assert!(target.is_empty());
352
353 let arr_max_size = WeakHashList::<str>::DEFAULT_ARRAY_SIZE;
355 let items = strings(arr_max_size + 1);
356
357 for item in items.iter().take(arr_max_size) {
358 target.add(item);
359 }
360
361 assert!(!target.is_empty());
362
363 target.add(&items[arr_max_size]);
365
366 assert!(!target.is_empty());
367
368 for item in items.iter().take(arr_max_size + 1) {
370 target.remove(item);
371 }
372
373 assert!(target.is_empty());
374 }
375
376 #[test]
377 fn array_compact_after_remove_works() {
378 let mut target = WeakHashList::<str>::new();
379
380 let arr_max_size = WeakHashList::<str>::DEFAULT_ARRAY_SIZE;
382 let items = strings(arr_max_size);
383
384 for item in &items {
385 target.add(item);
386 }
387
388 target.remove(&items[3]);
390
391 let forty_two: Rc<str> = Rc::from("42");
393 target.add(&forty_two);
394 }
395
396 #[test]
400 fn dictionary_storage_keeps_insertion_order() {
401 let mut target = WeakHashList::<str>::new();
402 let items = strings(WeakHashList::<str>::DEFAULT_ARRAY_SIZE + 4);
403 for item in &items {
404 target.add(item);
405 }
406
407 target.remove(&items[2]);
408 let added: Rc<str> = Rc::from("added");
409 target.add(&added);
410
411 let mut expected = items.clone();
412 expected[2] = added.clone();
413 let alive = target.get_alive(None).unwrap();
414 assert_eq!(alive.len(), expected.len());
415 assert!(alive.iter().zip(&expected).all(|(a, e)| Rc::ptr_eq(a, e)));
416 }
417}