use ordered_hash_map::ordered_map::DefaultHashBuilder;
use ordered_hash_map::OrderedHashMap;
use core::borrow::Borrow;
use core::fmt;
use core::hash::Hash;
use core::ops::Fn;
mod iter;
pub use iter::{Iter, IterMut};
pub struct EvictingCacheMap<K, V, const N: usize, F, S = DefaultHashBuilder>
where
F: Fn(K, V),
{
inner: OrderedHashMap<K, V, S>,
func: F,
}
impl<K, V, const N: usize, F, S> fmt::Debug for EvictingCacheMap<K, V, N, F, S>
where
K: fmt::Debug,
V: fmt::Debug,
F: Fn(K, V),
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.inner.fmt(f)
}
}
impl<K, V, const N: usize> Default
for EvictingCacheMap<K, V, N, fn(K, V) -> (), DefaultHashBuilder>
{
fn default() -> Self {
Self::new()
}
}
impl<K, V, const N: usize> EvictingCacheMap<K, V, N, fn(K, V) -> (), DefaultHashBuilder> {
pub fn new() -> Self {
Self::with_prune_hook(|_, _| ())
}
}
impl<K, V, const N: usize, F> EvictingCacheMap<K, V, N, F, DefaultHashBuilder>
where
F: Fn(K, V),
{
pub fn with_prune_hook(func: F) -> Self {
Self {
inner: OrderedHashMap::with_capacity(N),
func,
}
}
}
impl<K, V, const N: usize, S> EvictingCacheMap<K, V, N, fn(K, V) -> (), S> {
pub fn with_hasher(hash_builder: S) -> Self {
Self::with_hasher_and_prune_hook(hash_builder, |_, _| ())
}
}
impl<K, V, const N: usize, F, S> EvictingCacheMap<K, V, N, F, S>
where
F: Fn(K, V),
{
pub fn with_hasher_and_prune_hook(hash_builder: S, func: F) -> Self {
Self {
inner: OrderedHashMap::with_capacity_and_hasher(N, hash_builder),
func,
}
}
}
impl<K, V, const N: usize, F> EvictingCacheMap<K, V, N, F>
where
K: Eq + Hash,
F: Fn(K, V),
{
#[inline(always)]
fn prune_impl(&mut self) {
if let Some((k, v)) = self.inner.pop_front_entry() {
(self.func)(k, v);
}
}
#[inline(always)]
fn prune_with_impl(&mut self, f: &dyn Fn(K, V)) {
if let Some((k, v)) = self.inner.pop_front_entry() {
(f)(k, v);
}
}
#[inline]
fn prune_one(&mut self) {
if self.inner.len() == N {
self.prune_impl();
}
}
#[inline]
fn prune_one_key(&mut self, key: &K) {
if self.inner.len() == N {
if !self.inner.contains_key(key) {
self.prune_impl();
} else if let Some((k, v)) = self.inner.remove_entry(key) {
(self.func)(k, v);
}
}
}
#[inline]
pub fn prune_by(&mut self, len: usize) {
for _ in 0..if len < self.len() { len } else { self.len() } {
self.prune_impl();
}
}
#[inline]
pub fn prune_by_with(&mut self, len: usize, f: impl Fn(K, V)) {
for _ in 0..if len < self.len() { len } else { self.len() } {
self.prune_with_impl(&f);
}
}
#[inline]
pub fn prune_to(&mut self, len: usize) {
self.prune_by(self.len().saturating_sub(len));
}
#[inline]
pub fn prune_to_with(&mut self, len: usize, f: impl Fn(K, V)) {
self.prune_by_with(self.len().saturating_sub(len), f);
}
#[inline]
pub fn promote<Q>(&mut self, key: &Q)
where
K: Borrow<Q>,
Q: Hash + Eq + ?Sized,
{
self.inner.move_to_back(key);
}
pub fn contains_key<Q>(&self, key: &Q) -> bool
where
K: Borrow<Q>,
Q: Hash + Eq + ?Sized,
{
self.inner.contains_key(key)
}
#[inline]
pub fn insert(&mut self, key: K, value: V) {
self.prune_one_key(&key);
self.inner.insert(key, value);
}
#[inline]
pub fn get<Q>(&mut self, key: &Q) -> Option<&V>
where
K: Borrow<Q>,
Q: Hash + Eq + ?Sized,
{
self.promote(key);
self.get_no_promote(key)
}
#[inline]
pub fn get_no_promote<Q>(&self, key: &Q) -> Option<&V>
where
K: Borrow<Q>,
Q: Hash + Eq + ?Sized,
{
self.inner.get(key)
}
#[inline]
pub fn get_mut<Q>(&mut self, key: &Q) -> Option<&mut V>
where
K: Borrow<Q>,
Q: Hash + Eq + ?Sized,
{
self.promote(key);
self.get_mut_no_promote(key)
}
#[inline]
pub fn get_mut_no_promote<Q>(&mut self, key: &Q) -> Option<&mut V>
where
K: Borrow<Q>,
Q: Hash + Eq + ?Sized,
{
self.inner.get_mut(key)
}
#[inline]
pub fn get_key_value<Q>(&mut self, key: &Q) -> Option<(&K, &V)>
where
K: Borrow<Q>,
Q: Hash + Eq + ?Sized,
{
self.promote(key);
self.get_key_value_no_promote(key)
}
#[inline]
pub fn get_key_value_no_promote<Q>(&self, key: &Q) -> Option<(&K, &V)>
where
K: Borrow<Q>,
Q: Hash + Eq + ?Sized,
{
self.inner.get_key_value(key)
}
#[inline]
pub fn get_key_value_mut<Q>(&mut self, key: &Q) -> Option<(&K, &mut V)>
where
K: Borrow<Q>,
Q: Hash + Eq + ?Sized,
{
self.promote(key);
self.get_key_value_mut_no_promote(key)
}
#[inline]
pub fn get_key_value_mut_no_promote<Q>(&mut self, key: &Q) -> Option<(&K, &mut V)>
where
K: Borrow<Q>,
Q: Hash + Eq + ?Sized,
{
self.inner.get_key_value_mut(key)
}
pub fn remove<Q>(&mut self, key: &Q) -> Option<V>
where
K: Borrow<Q>,
Q: Hash + Eq + ?Sized,
{
self.inner.remove(key)
}
pub fn remove_entry<Q>(&mut self, key: &Q) -> Option<(K, V)>
where
K: Borrow<Q>,
Q: Hash + Eq + ?Sized,
{
self.inner.remove_entry(key)
}
pub fn len(&self) -> usize {
self.inner.len()
}
pub fn is_empty(&self) -> bool {
self.inner.is_empty()
}
pub fn clear(&mut self) {
self.inner.drain().for_each(|(k, v)| (self.func)(k, v));
}
pub fn iter(&self) -> Iter<'_, K, V> {
self.into_iter()
}
pub fn iter_mut(&mut self) -> IterMut<'_, K, V> {
self.into_iter()
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::sync::mpsc;
#[test]
fn basic_operation() {
let mut map: EvictingCacheMap<String, u8, 4, _> = EvictingCacheMap::default();
map.insert("One".to_string(), 1);
map.insert("Two".to_string(), 2);
map.insert("Three".to_string(), 3);
map.insert("Four".to_string(), 4);
map.insert("Five".to_string(), 5);
assert!(!map.contains_key("One"));
assert!(map.contains_key("Two"));
assert!(map.contains_key("Three"));
assert!(map.contains_key("Four"));
assert!(map.contains_key("Five"));
}
#[test]
fn promote() {
let (tx, rx) = mpsc::channel();
let mut map: EvictingCacheMap<String, u8, 2, _> =
EvictingCacheMap::with_prune_hook(|k, v| tx.send((k, v)).unwrap());
map.insert("One".into(), 1);
map.insert("Two".into(), 2);
map.promote("One");
map.insert("Three".into(), 3);
assert!(map.contains_key("One"));
assert!(!map.contains_key("Two"));
assert!(map.contains_key("Three"));
drop(map);
drop(tx); assert_eq!(rx.recv(), Ok(("Two".into(), 2)));
assert!(rx.recv().is_err());
}
#[test]
fn clear() {
let (tx, rx) = mpsc::channel();
let mut map: EvictingCacheMap<String, u8, 2, _> =
EvictingCacheMap::with_prune_hook(|k, v| tx.send((k, v)).unwrap());
map.insert("A".into(), 1);
map.clear();
assert_eq!(rx.recv(), Ok(("A".into(), 1)));
}
#[test]
fn prune() {
let (tx, rx) = mpsc::channel();
let mut map: EvictingCacheMap<String, u8, 4, _> =
EvictingCacheMap::with_prune_hook(|k, v| tx.send((k, v)).unwrap());
map.insert("A".into(), 1);
map.insert("B".into(), 2);
map.insert("C".into(), 3);
map.insert("D".into(), 4);
map.prune_to(3);
assert_eq!(rx.recv(), Ok(("A".into(), 1)));
assert_eq!(rx.try_recv(), Err(mpsc::TryRecvError::Empty));
map.prune_by(1);
assert_eq!(rx.recv(), Ok(("B".into(), 2)));
assert_eq!(rx.try_recv(), Err(mpsc::TryRecvError::Empty));
}
#[test]
fn prune_with() {
let (tx, rx) = mpsc::channel();
let mut map: EvictingCacheMap<String, u8, 4, _> =
EvictingCacheMap::with_prune_hook(|k, v| tx.send((k, v)).unwrap());
map.insert("A".into(), 1);
map.insert("B".into(), 2);
map.insert("C".into(), 3);
map.insert("D".into(), 4);
map.prune_to_with(3, |k, v| assert_eq!((k, v), ("A".into(), 1)));
assert!(rx.try_recv().is_err()); map.prune_by_with(1, |k, v| assert_eq!((k, v), ("B".into(), 2)));
assert!(rx.try_recv().is_err());
}
#[test]
fn get_ops() {
let (tx, rx) = mpsc::channel();
let mut map: EvictingCacheMap<i32, i32, 4, _> =
EvictingCacheMap::with_prune_hook(|k, v| tx.send((k, v)).unwrap());
map.insert(1, 1);
map.insert(2, 2);
map.insert(3, 3);
map.insert(4, 4);
assert!(!map.is_empty());
map.get(&1);
map.prune_by(1);
assert_eq!(rx.recv(), Ok((2, 2)));
map.get_no_promote(&3);
map.prune_by(1);
assert_eq!(rx.recv(), Ok((3, 3)));
if let Some(v) = map.get_mut(&4) {
*v += 1i32;
};
map.prune_by(2);
assert_eq!(rx.recv(), Ok((1, 1)));
assert_eq!(rx.recv(), Ok((4, 5)));
assert!(map.is_empty());
}
#[test]
fn iterators() {
let mut map: EvictingCacheMap<i32, i32, 4, _> = EvictingCacheMap::new();
map.insert(1, 1);
map.insert(2, 2);
map.insert(3, 3);
map.insert(4, 4);
for (_, v) in map.iter_mut() {
*v += 1;
}
map.iter().for_each(|(k, v)| assert_eq!(k + 1, *v));
}
}