use std::collections::hash_map::{
OccupiedEntry as InnerOccupiedEntry, VacantEntry as InnerVacantEntry,
};
use std::hash::Hash;
use std::num::NonZeroUsize;
use std::ptr::NonNull;
use std::rc::Rc;
use super::util::remove_entry_pointer;
use super::{FrequencyList, LfuEntry};
pub enum Entry<'a, Key: Hash + Eq, Value> {
Occupied(OccupiedEntry<'a, Key, Value>),
Vacant(VacantEntry<'a, Key, Value>),
}
#[allow(clippy::module_name_repetitions)]
pub struct OccupiedEntry<'a, Key: Hash + Eq, Value> {
inner: InnerOccupiedEntry<'a, Rc<Key>, NonNull<LfuEntry<Key, Value>>>,
len: &'a mut usize,
}
impl<'a, Key: Hash + Eq, Value> OccupiedEntry<'a, Key, Value> {
pub(super) fn new(
entry: InnerOccupiedEntry<'a, Rc<Key>, NonNull<LfuEntry<Key, Value>>>,
len: &'a mut usize,
) -> Self {
Self { inner: entry, len }
}
#[inline]
#[must_use]
pub fn key(&self) -> &Key {
self.inner.key()
}
#[must_use]
pub fn remove_entry(self) -> (Rc<Key>, Value) {
let (key, node) = self.inner.remove_entry();
let node = *unsafe { Box::from_raw(node.as_ptr()) };
let value = remove_entry_pointer(node, self.len);
(key, value)
}
#[inline]
#[must_use]
pub fn get(&self) -> &Value {
&unsafe { self.inner.get().as_ref() }.value
}
#[inline]
pub fn get_mut(&mut self) -> &mut Value {
&mut unsafe { self.inner.get_mut().as_mut() }.value
}
#[inline]
#[must_use]
pub fn into_mut(self) -> &'a mut Value {
&mut unsafe { self.inner.into_mut().as_mut() }.value
}
pub fn insert(&mut self, mut value: Value) -> Value {
let old_value = &mut unsafe { self.inner.get_mut().as_mut() }.value;
std::mem::swap(old_value, &mut value);
value
}
#[must_use]
pub fn remove(self) -> Value {
let node = self.inner.remove();
let node = *unsafe { Box::from_raw(node.as_ptr()) };
remove_entry_pointer(node, self.len)
}
}
#[allow(clippy::module_name_repetitions)]
pub struct VacantEntry<'a, Key: Hash + Eq, Value> {
inner: InnerVacantEntry<'a, Rc<Key>, NonNull<LfuEntry<Key, Value>>>,
key: Rc<Key>,
freq_list: &'a mut FrequencyList<Key, Value>,
cache_capacity: Option<NonZeroUsize>,
cache_len: &'a mut usize,
}
impl<'a, Key: Hash + Eq, Value> VacantEntry<'a, Key, Value> {
pub(super) fn new(
entry: InnerVacantEntry<'a, Rc<Key>, NonNull<LfuEntry<Key, Value>>>,
key: Rc<Key>,
freq_list: &'a mut FrequencyList<Key, Value>,
cache_capacity: Option<NonZeroUsize>,
cache_len: &'a mut usize,
) -> Self {
Self {
inner: entry,
key,
freq_list,
cache_capacity,
cache_len,
}
}
#[inline]
#[must_use]
pub fn key(&self) -> &Key {
self.key.as_ref()
}
#[inline]
#[must_use]
pub fn key_rc(&self) -> Rc<Key> {
Rc::clone(&self.key)
}
#[inline]
#[must_use]
pub fn into_key(self) -> Rc<Key> {
self.key
}
#[inline]
pub fn insert(self, value: Value) -> &'a mut Value {
if let Some(capacity) = self.cache_capacity {
if capacity.get() == *self.cache_len {
self.freq_list.pop_lfu();
}
} else {
*self.cache_len += 1;
}
&mut unsafe {
self.inner
.insert(self.freq_list.insert(self.key, value))
.as_mut()
}
.value
}
}
impl<'a, Key: Hash + Eq, Value> Entry<'a, Key, Value> {
#[inline]
pub fn or_insert(self, default: Value) -> &'a mut Value {
match self {
Entry::Occupied(entry) => &mut unsafe { entry.inner.into_mut().as_mut() }.value,
Entry::Vacant(entry) => entry.insert(default),
}
}
#[inline]
pub fn or_insert_with<F>(self, default: F) -> &'a mut Value
where
F: FnOnce() -> Value,
{
match self {
Entry::Occupied(entry) => &mut unsafe { entry.inner.into_mut().as_mut() }.value,
Entry::Vacant(entry) => entry.insert(default()),
}
}
#[inline]
pub fn or_insert_with_key<F>(self, default: F) -> &'a mut Value
where
F: FnOnce(&Key) -> Value,
{
match self {
Entry::Occupied(entry) => &mut unsafe { entry.inner.into_mut().as_mut() }.value,
Entry::Vacant(entry) => {
let value = default(&entry.key);
entry.insert(value)
}
}
}
#[inline]
#[must_use]
pub fn key(&self) -> &Key {
match self {
Entry::Occupied(entry) => entry.inner.key(),
Entry::Vacant(entry) => entry.key.as_ref(),
}
}
#[inline]
#[must_use]
pub fn key_rc(&self) -> Rc<Key> {
match self {
Entry::Occupied(entry) => Rc::clone(entry.inner.key()),
Entry::Vacant(entry) => Rc::clone(&entry.key),
}
}
#[inline]
#[must_use]
pub fn and_modify<F>(self, f: F) -> Self
where
F: FnOnce(&mut Value),
{
match self {
Self::Occupied(mut entry) => {
f(&mut unsafe { entry.inner.get_mut().as_mut() }.value);
Self::Occupied(entry)
}
Self::Vacant(entry) => Self::Vacant(entry),
}
}
}
impl<'a, Key: Hash + Eq, Value: Default> Entry<'a, Key, Value> {
#[inline]
#[must_use]
pub fn or_default(self) -> &'a mut Value {
match self {
Self::Occupied(entry) => &mut unsafe { entry.inner.into_mut().as_mut() }.value,
Self::Vacant(entry) => entry.insert(Value::default()),
}
}
}
#[cfg(test)]
mod entry {
use crate::LfuCache;
fn init_cache() -> LfuCache<i32, i32> {
LfuCache::unbounded()
}
#[test]
fn or_insert_empty_adds_value() {
let mut cache = init_cache();
let entry = cache.entry(1);
let v = entry.or_insert(2);
assert_eq!(*v, 2);
assert_eq!(cache.keys().copied().collect::<Vec<_>>(), vec![1]);
assert_eq!(cache.frequencies(), vec![0]);
assert_eq!(cache.get(&1), Some(&2));
assert_eq!(cache.len(), 1);
}
#[test]
fn or_insert_non_empty_does_nothing() {
let mut cache = init_cache();
cache.insert(1, 2);
let entry = cache.entry(1);
let v = entry.or_insert(3);
assert_eq!(*v, 2);
assert_eq!(cache.keys().copied().collect::<Vec<_>>(), vec![1]);
assert_eq!(cache.frequencies(), vec![1]);
assert_eq!(cache.get(&1), Some(&2));
assert_eq!(cache.len(), 1);
}
#[test]
fn or_insert_with_is_equiv_to_or_insert() {
let mut cache_0 = init_cache();
let res_0 = cache_0.entry(1).or_insert(2);
let mut cache_1 = init_cache();
let res_1 = cache_1.entry(1).or_insert_with(|| 2);
assert_eq!(res_0, res_1);
let mut cache_0 = init_cache();
cache_0.insert(1, 3);
let res_0 = cache_0.entry(1).or_insert(2);
let mut cache_1 = init_cache();
cache_1.insert(1, 3);
let res_1 = cache_1.entry(1).or_insert_with(|| 2);
assert_eq!(res_0, res_1);
}
#[test]
fn or_insert_with_key_is_equiv_to_or_insert() {
let mut cache_0 = init_cache();
let res_0 = cache_0.entry(1).or_insert(2);
let mut cache_1 = init_cache();
let res_1 = cache_1.entry(1).or_insert_with_key(|_| 2);
assert_eq!(res_0, res_1);
let mut cache_0 = init_cache();
cache_0.insert(1, 3);
let res_0 = cache_0.entry(1).or_insert(2);
let mut cache_1 = init_cache();
cache_1.insert(1, 3);
let res_1 = cache_1.entry(1).or_insert_with_key(|_| 2);
assert_eq!(res_0, res_1);
}
}