diff options
Diffstat (limited to 'third_party/rust/hashlink/src/lru_cache.rs')
-rw-r--r-- | third_party/rust/hashlink/src/lru_cache.rs | 292 |
1 files changed, 292 insertions, 0 deletions
diff --git a/third_party/rust/hashlink/src/lru_cache.rs b/third_party/rust/hashlink/src/lru_cache.rs new file mode 100644 index 0000000000..9e5740ea60 --- /dev/null +++ b/third_party/rust/hashlink/src/lru_cache.rs @@ -0,0 +1,292 @@ +use std::{ + borrow::Borrow, + fmt, + hash::{BuildHasher, Hash}, + usize, +}; + +use hashbrown::hash_map; + +use crate::linked_hash_map::{self, LinkedHashMap}; + +pub use crate::linked_hash_map::{ + Drain, Entry, IntoIter, Iter, IterMut, OccupiedEntry, RawEntryBuilder, RawEntryBuilderMut, + RawOccupiedEntryMut, RawVacantEntryMut, VacantEntry, +}; + +pub struct LruCache<K, V, S = hash_map::DefaultHashBuilder> { + map: LinkedHashMap<K, V, S>, + max_size: usize, +} + +impl<K: Eq + Hash, V> LruCache<K, V> { + #[inline] + pub fn new(capacity: usize) -> Self { + LruCache { + map: LinkedHashMap::new(), + max_size: capacity, + } + } + + /// Create a new unbounded `LruCache` that does not automatically evict entries. + /// + /// A simple convenience method that is equivalent to `LruCache::new(usize::MAX)` + #[inline] + pub fn new_unbounded() -> Self { + LruCache::new(usize::MAX) + } +} + +impl<K, V, S> LruCache<K, V, S> { + #[inline] + pub fn with_hasher(capacity: usize, hash_builder: S) -> Self { + LruCache { + map: LinkedHashMap::with_hasher(hash_builder), + max_size: capacity, + } + } + + #[inline] + pub fn capacity(&self) -> usize { + self.max_size + } + + #[inline] + pub fn len(&self) -> usize { + self.map.len() + } + + #[inline] + pub fn is_empty(&self) -> bool { + self.map.is_empty() + } + + #[inline] + pub fn clear(&mut self) { + self.map.clear(); + } + + #[inline] + pub fn iter(&self) -> Iter<K, V> { + self.map.iter() + } + + #[inline] + pub fn iter_mut(&mut self) -> IterMut<K, V> { + self.map.iter_mut() + } + + #[inline] + pub fn drain(&mut self) -> Drain<K, V> { + self.map.drain() + } +} + +impl<K: Eq + Hash, V, S> LruCache<K, V, S> +where + S: BuildHasher, +{ + #[inline] + pub fn contains_key<Q>(&mut self, key: &Q) -> bool + where + K: Borrow<Q>, + Q: Hash + Eq + ?Sized, + { + self.get_mut(key).is_some() + } + + /// Insert a new value into the `LruCache`. + /// + /// If necessary, will remove the value at the front of the LRU list to make room. + #[inline] + pub fn insert(&mut self, k: K, v: V) -> Option<V> { + let old_val = self.map.insert(k, v); + if self.len() > self.capacity() { + self.remove_lru(); + } + old_val + } + + /// Get the value for the given key, *without* marking the value as recently used and moving it + /// to the back of the LRU list. + #[inline] + pub fn peek<Q>(&self, k: &Q) -> Option<&V> + where + K: Borrow<Q>, + Q: Hash + Eq + ?Sized, + { + self.map.get(k) + } + + /// Get the value for the given key mutably, *without* marking the value as recently used and + /// moving it to the back of the LRU list. + #[inline] + pub fn peek_mut<Q>(&mut self, k: &Q) -> Option<&mut V> + where + K: Borrow<Q>, + Q: Hash + Eq + ?Sized, + { + self.map.get_mut(k) + } + + /// Retrieve the given key, marking it as recently used and moving it to the back of the LRU + /// list. + #[inline] + pub fn get<Q>(&mut self, k: &Q) -> Option<&V> + where + K: Borrow<Q>, + Q: Hash + Eq + ?Sized, + { + self.get_mut(k).map(|v| &*v) + } + + /// Retrieve the given key, marking it as recently used and moving it to the back of the LRU + /// list. + #[inline] + pub fn get_mut<Q>(&mut self, k: &Q) -> Option<&mut V> + where + K: Borrow<Q>, + Q: Hash + Eq + ?Sized, + { + match self.map.raw_entry_mut().from_key(k) { + linked_hash_map::RawEntryMut::Occupied(mut occupied) => { + occupied.to_back(); + Some(occupied.into_mut()) + } + linked_hash_map::RawEntryMut::Vacant(_) => None, + } + } + + /// If the returned entry is vacant, it will always have room to insert a single value. By + /// using the entry API, you can exceed the configured capacity by 1. + /// + /// The returned entry is not automatically moved to the back of the LRU list. By calling + /// `Entry::to_back` / `Entry::to_front` you can manually control the position of this entry in + /// the LRU list. + #[inline] + pub fn entry(&mut self, key: K) -> Entry<'_, K, V, S> { + if self.len() > self.capacity() { + self.remove_lru(); + } + self.map.entry(key) + } + + /// The constructed raw entry is never automatically moved to the back of the LRU list. By + /// calling `Entry::to_back` / `Entry::to_front` you can manually control the position of this + /// entry in the LRU list. + #[inline] + pub fn raw_entry(&self) -> RawEntryBuilder<'_, K, V, S> { + self.map.raw_entry() + } + + /// If the constructed raw entry is vacant, it will always have room to insert a single value. + /// By using the raw entry API, you can exceed the configured capacity by 1. + /// + /// The constructed raw entry is never automatically moved to the back of the LRU list. By + /// calling `Entry::to_back` / `Entry::to_front` you can manually control the position of this + /// entry in the LRU list. + #[inline] + pub fn raw_entry_mut(&mut self) -> RawEntryBuilderMut<'_, K, V, S> { + if self.len() > self.capacity() { + self.remove_lru(); + } + self.map.raw_entry_mut() + } + + #[inline] + pub fn remove<Q>(&mut self, k: &Q) -> Option<V> + where + K: Borrow<Q>, + Q: Hash + Eq + ?Sized, + { + self.map.remove(k) + } + + #[inline] + pub fn remove_entry<Q>(&mut self, k: &Q) -> Option<(K, V)> + where + K: Borrow<Q>, + Q: Hash + Eq + ?Sized, + { + self.map.remove_entry(k) + } + + /// Set the new cache capacity for the `LruCache`. + /// + /// If there are more entries in the `LruCache` than the new capacity will allow, they are + /// removed. + #[inline] + pub fn set_capacity(&mut self, capacity: usize) { + for _ in capacity..self.len() { + self.remove_lru(); + } + self.max_size = capacity; + } + + /// Remove the least recently used entry and return it. + /// + /// If the `LruCache` is empty this will return None. + #[inline] + pub fn remove_lru(&mut self) -> Option<(K, V)> { + self.map.pop_front() + } +} + +impl<K: Hash + Eq + Clone, V: Clone, S: BuildHasher + Clone> Clone for LruCache<K, V, S> { + #[inline] + fn clone(&self) -> Self { + LruCache { + map: self.map.clone(), + max_size: self.max_size, + } + } +} + +impl<K: Eq + Hash, V, S: BuildHasher> Extend<(K, V)> for LruCache<K, V, S> { + #[inline] + fn extend<I: IntoIterator<Item = (K, V)>>(&mut self, iter: I) { + for (k, v) in iter { + self.insert(k, v); + } + } +} + +impl<K, V, S> IntoIterator for LruCache<K, V, S> { + type Item = (K, V); + type IntoIter = IntoIter<K, V>; + + #[inline] + fn into_iter(self) -> IntoIter<K, V> { + self.map.into_iter() + } +} + +impl<'a, K, V, S> IntoIterator for &'a LruCache<K, V, S> { + type Item = (&'a K, &'a V); + type IntoIter = Iter<'a, K, V>; + + #[inline] + fn into_iter(self) -> Iter<'a, K, V> { + self.iter() + } +} + +impl<'a, K, V, S> IntoIterator for &'a mut LruCache<K, V, S> { + type Item = (&'a K, &'a mut V); + type IntoIter = IterMut<'a, K, V>; + + #[inline] + fn into_iter(self) -> IterMut<'a, K, V> { + self.iter_mut() + } +} + +impl<K, V, S> fmt::Debug for LruCache<K, V, S> +where + K: fmt::Debug, + V: fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_map().entries(self.iter().rev()).finish() + } +} |