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 { map: LinkedHashMap, max_size: usize, } impl LruCache { #[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 LruCache { #[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 { self.map.iter() } #[inline] pub fn iter_mut(&mut self) -> IterMut { self.map.iter_mut() } #[inline] pub fn drain(&mut self) -> Drain { self.map.drain() } } impl LruCache where S: BuildHasher, { #[inline] pub fn contains_key(&mut self, key: &Q) -> bool where K: Borrow, 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 { 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(&self, k: &Q) -> Option<&V> where K: Borrow, 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(&mut self, k: &Q) -> Option<&mut V> where K: Borrow, 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(&mut self, k: &Q) -> Option<&V> where K: Borrow, 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(&mut self, k: &Q) -> Option<&mut V> where K: Borrow, 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(&mut self, k: &Q) -> Option where K: Borrow, Q: Hash + Eq + ?Sized, { self.map.remove(k) } #[inline] pub fn remove_entry(&mut self, k: &Q) -> Option<(K, V)> where K: Borrow, 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 Clone for LruCache { #[inline] fn clone(&self) -> Self { LruCache { map: self.map.clone(), max_size: self.max_size, } } } impl Extend<(K, V)> for LruCache { #[inline] fn extend>(&mut self, iter: I) { for (k, v) in iter { self.insert(k, v); } } } impl IntoIterator for LruCache { type Item = (K, V); type IntoIter = IntoIter; #[inline] fn into_iter(self) -> IntoIter { self.map.into_iter() } } impl<'a, K, V, S> IntoIterator for &'a LruCache { 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 { 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 fmt::Debug for LruCache where K: fmt::Debug, V: fmt::Debug, { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_map().entries(self.iter().rev()).finish() } }