use std::collections::HashMap;
pub const DEFAULT_MAX_KEYS: usize = 50_000;
const EVICT_FRACTION: usize = 16;
#[derive(Debug)]
pub(crate) struct BoundedLru<K, V> {
entries: HashMap<K, V>,
max_keys: usize,
}
impl<K: std::hash::Hash + Eq + Clone, V> BoundedLru<K, V> {
pub(crate) fn with_capacity(max_keys: usize) -> Self {
BoundedLru {
entries: HashMap::new(),
max_keys: max_keys.max(1),
}
}
pub(crate) fn max_keys(&self) -> usize {
self.max_keys
}
pub(crate) fn admit<R, F>(&mut self, mut recency: F) -> usize
where
R: Ord,
F: FnMut(&V) -> R,
{
if self.entries.len() < self.max_keys {
return 0;
}
let target = self.max_keys - (self.max_keys / EVICT_FRACTION).max(1);
let mut seen: Vec<(R, K)> = self
.entries
.iter()
.map(|(k, v)| (recency(v), k.clone()))
.collect();
seen.sort_unstable_by(|(a, _), (b, _)| a.cmp(b));
let doomed = self.entries.len() - target;
let mut dropped = 0;
for (_, key) in seen.into_iter().take(doomed) {
if self.entries.remove(&key).is_some() {
dropped += 1;
}
}
dropped
}
pub(crate) fn insert(&mut self, key: K, value: V) -> Option<V> {
self.entries.insert(key, value)
}
pub(crate) fn len(&self) -> usize {
self.entries.len()
}
pub(crate) fn is_empty(&self) -> bool {
self.entries.is_empty()
}
pub(crate) fn clear(&mut self) {
self.entries.clear();
}
pub(crate) fn keys(&self) -> impl Iterator<Item = &K> {
self.entries.keys()
}
pub(crate) fn iter(&self) -> impl Iterator<Item = (&K, &V)> {
self.entries.iter()
}
pub(crate) fn values(&self) -> impl Iterator<Item = &V> {
self.entries.values()
}
pub(crate) fn values_mut(&mut self) -> impl Iterator<Item = &mut V> {
self.entries.values_mut()
}
pub(crate) fn get<Q>(&self, key: &Q) -> Option<&V>
where
K: std::borrow::Borrow<Q>,
Q: std::hash::Hash + Eq + ?Sized,
{
self.entries.get(key)
}
pub(crate) fn get_mut<Q>(&mut self, key: &Q) -> Option<&mut V>
where
K: std::borrow::Borrow<Q>,
Q: std::hash::Hash + Eq + ?Sized,
{
self.entries.get_mut(key)
}
pub(crate) fn remove<Q>(&mut self, key: &Q) -> Option<V>
where
K: std::borrow::Borrow<Q>,
Q: std::hash::Hash + Eq + ?Sized,
{
self.entries.remove(key)
}
}