use std::collections::hash_map::RandomState; use std::hash::{BuildHasher, Hash}; #[cfg(not(feature = "abi_stable"))] mod basic_impl { pub type BoxImpl = Box; pub type HashMapImpl = std::collections::HashMap; pub type MutexImpl = std::sync::Mutex; pub type MutexGuardImpl<'a, T> = std::sync::MutexGuard<'a, T>; pub type IterImpl<'a, K, V> = std::collections::hash_map::Iter<'a, K, V>; pub type IntoIterImpl = std::collections::hash_map::IntoIter; pub fn box_into_inner_impl(b: BoxImpl) -> T { *b } pub fn mutex_lock_impl<'a, T>(m: &'a MutexImpl) -> MutexGuardImpl<'a, T> { m.lock().unwrap() } pub fn mutex_into_inner_impl(m: MutexImpl) -> T { m.into_inner().unwrap() } } #[cfg(not(feature = "abi_stable"))] use basic_impl::*; #[cfg(feature = "abi_stable")] mod abi_stable_impl { use abi_stable::{ external_types::RMutex, std_types::{RBox, RHashMap}, }; pub type BoxImpl = RBox; pub type HashMapImpl = RHashMap; pub type MutexImpl = RMutex; pub type MutexGuardImpl<'a, T> = abi_stable::external_types::parking_lot::mutex::RMutexGuard<'a, T>; pub type IterImpl<'a, K, V> = abi_stable::std_types::map::Iter<'a, K, V>; pub type IntoIterImpl = abi_stable::std_types::map::IntoIter; pub fn box_into_inner_impl(b: BoxImpl) -> T { RBox::into_inner(b) } pub fn mutex_lock_impl<'a, T>(m: &'a MutexImpl) -> MutexGuardImpl<'a, T> { m.lock() } pub fn mutex_into_inner_impl(m: MutexImpl) -> T { m.into_inner() } } #[cfg(feature = "abi_stable")] use abi_stable_impl::*; /// An insert-only map for caching the result of functions #[cfg_attr(feature = "abi_stable", derive(abi_stable::StableAbi))] #[cfg_attr(feature = "abi_stable", repr(C))] pub struct CacheMap { inner: MutexImpl, S>>, } impl Default for CacheMap { fn default() -> Self { CacheMap { inner: MutexImpl::new(Default::default()), } } } impl std::iter::FromIterator<(K, V)> for CacheMap { fn from_iter(iter: T) -> Self where T: IntoIterator, { CacheMap { inner: MutexImpl::new( iter.into_iter() .map(|(k, v)| (k, BoxImpl::new(v))) .collect(), ), } } } pub struct IntoIter(IntoIterImpl>); impl Iterator for IntoIter { type Item = (K, V); fn next(&mut self) -> Option { self.0.next().map(|t| (t.0, box_into_inner_impl(t.1))) } } impl IntoIterator for CacheMap { type Item = (K, V); type IntoIter = IntoIter; fn into_iter(self) -> Self::IntoIter { IntoIter(mutex_into_inner_impl(self.inner).into_iter()) } } pub struct Iter<'a, K, V, S> { iter: IterImpl<'a, K, BoxImpl>, _guard: MutexGuardImpl<'a, HashMapImpl, S>>, } impl<'a, K, V, S> Iterator for Iter<'a, K, V, S> { type Item = (&'a K, &'a V); fn next(&mut self) -> Option { self.iter.next().map(|t| (t.0, t.1.as_ref())) } } impl<'a, K, V, S> IntoIterator for &'a CacheMap { type Item = (&'a K, &'a V); type IntoIter = Iter<'a, K, V, S>; fn into_iter(self) -> Self::IntoIter { let guard = mutex_lock_impl(&self.inner); let iter = unsafe { std::mem::transmute::>, IterImpl>>(guard.iter()) }; Iter { iter, _guard: guard, } } } impl CacheMap { /// Fetch the value associated with the key, or run the provided function to insert one. /// /// # Example /// /// ``` /// use cachemap2::CacheMap; /// /// let m = CacheMap::new(); /// /// let fst = m.cache("key", || 5u32); /// let snd = m.cache("key", || 7u32); /// /// assert_eq!(*fst, *snd); /// assert_eq!(*fst, 5u32); /// ``` pub fn cache V>(&self, key: K, f: F) -> &V { let v = std::ptr::NonNull::from( mutex_lock_impl(&self.inner) .entry(key) .or_insert_with(|| BoxImpl::new(f())) .as_ref(), ); // Safety: We only support adding entries to the hashmap, and as long as a reference is // maintained the value will be present. unsafe { v.as_ref() } } /// Fetch the value associated with the key, or insert a default value. pub fn cache_default(&self, key: K) -> &V where V: Default, { self.cache(key, || Default::default()) } /// Return whether the map contains the given key. pub fn contains_key(&self, key: &Q) -> bool where K: std::borrow::Borrow, Q: Hash + Eq, { mutex_lock_impl(&self.inner).contains_key(key) } /// Return an iterator over the map. /// /// This iterator will lock the underlying map until it is dropped. pub fn iter(&self) -> Iter { self.into_iter() } } impl CacheMap { /// Creates a new CacheMap pub fn new() -> Self { Default::default() } } impl CacheMap { /// Creates a new CacheMap with the provided hasher pub fn with_hasher(hash_builder: S) -> Self { Self { inner: MutexImpl::new(HashMapImpl::with_hasher(hash_builder)), } } } #[cfg(test)] mod tests { use super::*; #[test] fn single_insert() { let m = CacheMap::new(); let a = m.cache("key", || 21u32); assert_eq!(21, *a); } #[test] fn contains_key() { let m = CacheMap::new(); m.cache("key", || 21u32); assert!(m.contains_key("key")); assert!(!m.contains_key("other")); } #[test] fn double_insert() { let m = CacheMap::new(); let a = m.cache("key", || 5u32); let b = m.cache("key", || 7u32); assert_eq!(*a, *b); assert_eq!(5, *a); } #[test] fn insert_two() { let m = CacheMap::new(); let a = m.cache("a", || 5u32); let b = m.cache("b", || 7u32); assert_eq!(5, *a); assert_eq!(7, *b); let c = m.cache("a", || 9u32); let d = m.cache("b", || 11u32); assert_eq!(*a, *c); assert_eq!(*b, *d); assert_eq!(5, *a); assert_eq!(7, *b); } #[test] fn iter() { use std::collections::HashMap; use std::iter::FromIterator; let m = CacheMap::new(); m.cache("a", || 5u32); m.cache("b", || 7u32); let mut expected = HashMap::<&'static str, u32>::from_iter([("a", 5u32), ("b", 7u32)]); for (k, v) in &m { assert!(expected.remove(k).expect("unexpected key") == *v); } assert!(expected.is_empty()); } }