compressible_map/
local_cache.rs1use core::hash::{BuildHasher, Hash};
2use std::cell::UnsafeCell;
3use std::collections::{hash_map, HashMap};
4use std::pin::Pin;
5
6#[derive(Default)]
13pub struct LocalCache<K, V, H> {
14 accesses: UnsafeCell<HashMap<K, LocalAccess<Pin<Box<V>>>, H>>,
15}
16
17pub enum LocalAccess<V> {
18 Cached,
20 Missed(V),
24}
25
26impl<V> LocalAccess<V> {
27 fn unwrap_ref(&self) -> &V {
28 match self {
29 LocalAccess::Cached => panic!("Tried to unwrap access without value"),
30 LocalAccess::Missed(value) => &value,
31 }
32 }
33
34 fn map<T>(self, f: impl FnOnce(V) -> T) -> LocalAccess<T> {
35 match self {
36 LocalAccess::Cached => LocalAccess::Cached,
37 LocalAccess::Missed(value) => LocalAccess::Missed(f(value)),
38 }
39 }
40}
41
42impl<K, V, H> LocalCache<K, V, H>
43where
44 K: Eq + Hash,
45 H: Default + BuildHasher,
46{
47 pub fn new() -> Self {
48 LocalCache {
49 accesses: UnsafeCell::new(HashMap::with_hasher(Default::default())),
50 }
51 }
52
53 pub fn remember_cached_access(&self, key: K) {
61 let mut_accesses = unsafe { &mut *self.accesses.get() };
62 mut_accesses.entry(key).or_insert(LocalAccess::Cached);
63 }
64
65 pub fn get_or_insert_with(&self, key: K, f: impl FnOnce() -> V) -> &V {
66 let mut_accesses = unsafe { &mut *self.accesses.get() };
67 match mut_accesses.entry(key) {
68 hash_map::Entry::Occupied(occupied) => {
69 let access_ref = occupied.into_mut();
70 match access_ref {
71 LocalAccess::Cached => {
72 *access_ref = LocalAccess::Missed(Box::pin(f()));
73
74 access_ref.unwrap_ref()
75 }
76 LocalAccess::Missed(value) => value,
77 }
78 }
79 hash_map::Entry::Vacant(vacant) => {
80 let access_ref = vacant.insert(LocalAccess::Missed(Box::pin(f())));
81
82 access_ref.unwrap_ref()
83 }
84 }
85 }
86
87 pub fn into_iter(self) -> impl Iterator<Item = (K, LocalAccess<V>)> {
88 self.accesses.into_inner().into_iter().map(|(k, access)| {
89 (
90 k,
91 access.map(|value| unsafe { *Pin::into_inner_unchecked(value) }),
92 )
93 })
94 }
95}