#![no_std]
#[cfg(feature = "hashbrown")]
extern crate hashbrown;
#[cfg(test)]
extern crate scoped_threadpool;
use alloc::borrow::Borrow;
use alloc::boxed::Box;
use core::fmt;
use core::hash::{BuildHasher, Hash, Hasher};
use core::iter::FusedIterator;
use core::marker::PhantomData;
use core::mem;
use core::num::NonZeroUsize;
use core::ptr::{self, NonNull};
#[cfg(any(test, not(feature = "hashbrown")))]
extern crate std;
#[cfg(feature = "hashbrown")]
use hashbrown::HashMap;
#[cfg(not(feature = "hashbrown"))]
use std::collections::HashMap;
extern crate alloc;
struct KeyRef<K> {
k: *const K,
}
impl<K: Hash> Hash for KeyRef<K> {
fn hash<H: Hasher>(&self, state: &mut H) {
unsafe { (*self.k).hash(state) }
}
}
impl<K: PartialEq> PartialEq for KeyRef<K> {
#![allow(unknown_lints)]
#[allow(clippy::unconditional_recursion)]
fn eq(&self, other: &KeyRef<K>) -> bool {
unsafe { (*self.k).eq(&*other.k) }
}
}
impl<K: Eq> Eq for KeyRef<K> {}
#[repr(transparent)]
struct KeyWrapper<K: ?Sized>(K);
impl<K: ?Sized> KeyWrapper<K> {
fn from_ref(key: &K) -> &Self {
unsafe { &*(key as *const K as *const KeyWrapper<K>) }
}
}
impl<K: ?Sized + Hash> Hash for KeyWrapper<K> {
fn hash<H: Hasher>(&self, state: &mut H) {
self.0.hash(state)
}
}
impl<K: ?Sized + PartialEq> PartialEq for KeyWrapper<K> {
#![allow(unknown_lints)]
#[allow(clippy::unconditional_recursion)]
fn eq(&self, other: &Self) -> bool {
self.0.eq(&other.0)
}
}
impl<K: ?Sized + Eq> Eq for KeyWrapper<K> {}
impl<K, Q> Borrow<KeyWrapper<Q>> for KeyRef<K>
where
K: Borrow<Q>,
Q: ?Sized,
{
fn borrow(&self) -> &KeyWrapper<Q> {
let key = unsafe { &*self.k }.borrow();
KeyWrapper::from_ref(key)
}
}
struct LruEntry<K, V> {
key: mem::MaybeUninit<K>,
val: mem::MaybeUninit<V>,
size: NonZeroUsize,
prev: *mut LruEntry<K, V>,
next: *mut LruEntry<K, V>,
}
impl<K, V> LruEntry<K, V> {
fn new(key: K, val: V, size: NonZeroUsize) -> Self {
LruEntry {
key: mem::MaybeUninit::new(key),
val: mem::MaybeUninit::new(val),
size,
prev: ptr::null_mut(),
next: ptr::null_mut(),
}
}
fn new_sigil() -> Self {
LruEntry {
key: mem::MaybeUninit::uninit(),
val: mem::MaybeUninit::uninit(),
size: NonZeroUsize::new(usize::MAX).unwrap(),
prev: ptr::null_mut(),
next: ptr::null_mut(),
}
}
}
#[cfg(feature = "hashbrown")]
pub type DefaultHasher = hashbrown::DefaultHashBuilder;
#[cfg(not(feature = "hashbrown"))]
pub type DefaultHasher = std::collections::hash_map::RandomState;
pub struct LruCache<K, V, S = DefaultHasher> {
map: HashMap<KeyRef<K>, NonNull<LruEntry<K, V>>, S>,
cap: NonZeroUsize,
current_size: usize,
head: *mut LruEntry<K, V>,
tail: *mut LruEntry<K, V>,
}
impl<K, V> Clone for LruCache<K, V>
where
K: Hash + PartialEq + Eq + Clone,
V: Clone,
{
fn clone(&self) -> Self {
let mut new_lru = LruCache::new(self.cap());
for (key, value) in self.iter().rev() {
let old_key = KeyRef {
k: unsafe { &(*(*(*self.tail).prev).key.as_ptr()) },
};
let old_node = self.map.get(&old_key).unwrap();
let node_ptr: *mut LruEntry<K, V> = old_node.as_ptr();
let size = unsafe { &mut (*node_ptr).size };
new_lru.push(key.clone(), value.clone(), *size);
}
new_lru
}
}
impl<K: Hash + Eq, V> LruCache<K, V> {
pub fn new(cap: NonZeroUsize) -> LruCache<K, V> {
LruCache::construct(cap, HashMap::new())
}
pub fn unbounded() -> LruCache<K, V> {
LruCache::construct(NonZeroUsize::new(usize::MAX).unwrap(), HashMap::default())
}
}
impl<K: Hash + Eq, V, S: BuildHasher> LruCache<K, V, S> {
pub fn with_hasher(cap: NonZeroUsize, hash_builder: S) -> LruCache<K, V, S> {
LruCache::construct(cap, HashMap::with_hasher(hash_builder))
}
pub fn unbounded_with_hasher(hash_builder: S) -> LruCache<K, V, S> {
LruCache::construct(
NonZeroUsize::new(usize::MAX).unwrap(),
HashMap::with_hasher(hash_builder),
)
}
fn construct(
cap: NonZeroUsize,
map: HashMap<KeyRef<K>, NonNull<LruEntry<K, V>>, S>,
) -> LruCache<K, V, S> {
let cache = LruCache {
map,
cap,
current_size: 0,
head: Box::into_raw(Box::new(LruEntry::new_sigil())),
tail: Box::into_raw(Box::new(LruEntry::new_sigil())),
};
unsafe {
(*cache.head).next = cache.tail;
(*cache.tail).prev = cache.head;
}
cache
}
pub fn put(&mut self, k: K, v: V, size: NonZeroUsize) -> Option<V> {
self.capturing_put(k, v, size, false).map(|(_, v)| v)
}
pub fn push(&mut self, k: K, v: V, size: NonZeroUsize) -> Option<(K, V)> {
self.capturing_put(k, v, size, true)
}
fn update_size(&mut self, node_ptr: *mut LruEntry<K, V>, size: NonZeroUsize) -> NonZeroUsize {
let prev_size = unsafe { mem::replace(&mut (*node_ptr).size, size) };
self.current_size -= prev_size.get();
self.current_size += size.get();
prev_size
}
fn capturing_put(
&mut self,
k: K,
mut v: V,
size: NonZeroUsize,
capture: bool,
) -> Option<(K, V)> {
let node_ref = self.map.get_mut(&KeyRef { k: &k });
match node_ref {
Some(node_ref) => {
let node_ptr: *mut LruEntry<K, V> = node_ref.as_ptr();
self.update_size(node_ptr, size);
let node_ref = unsafe { &mut (*(*node_ptr).val.as_mut_ptr()) };
mem::swap(&mut v, node_ref);
let _ = node_ref;
self.detach(node_ptr);
self.attach(node_ptr);
Some((k, v))
}
None => {
let (replaced, node) = self.replace_or_create_node(k, v, size);
let node_ptr: *mut LruEntry<K, V> = node.as_ptr();
self.attach(node_ptr);
let keyref = unsafe { (*node_ptr).key.as_ptr() };
self.map.insert(KeyRef { k: keyref }, node);
replaced.filter(|_| capture)
}
}
}
fn tail_size(&self) -> usize {
let prev;
unsafe { prev = (*self.tail).prev }
if prev != self.head {
unsafe { (*(*self.tail).prev).size }.get()
} else {
0
}
}
pub fn update_node_size(&mut self, k: K, size: NonZeroUsize) -> Option<NonZeroUsize> {
let node_ref = self.map.get_mut(&KeyRef { k: &k });
match node_ref {
None => None,
Some(node) => {
let node_ptr = node.as_ptr();
Some(self.update_size(node_ptr, size))
}
}
}
pub fn update_node<'a, Q, F>(&'a mut self, k: &'_ Q, f: F) -> Option<V>
where
K: Borrow<Q>,
Q: Hash + Eq + ?Sized + alloc::borrow::ToOwned<Owned = K>,
F: FnOnce(&'a V) -> (V, NonZeroUsize),
{
if let Some(old_node) = self.map.get_mut(KeyWrapper::from_ref(k)) {
let node_ptr: *mut LruEntry<K, V> = old_node.as_ptr();
let old_value = unsafe { &mut *(*node_ptr).val.as_mut_ptr() };
let (v, size) = f(old_value);
let old_value = unsafe {
mem::replace(&mut (*node_ptr).val, mem::MaybeUninit::new(v)).assume_init()
};
self.update_size(node_ptr, size);
Some(old_value)
} else {
None
}
}
#[allow(clippy::type_complexity)]
fn replace_or_create_node(
&mut self,
k: K,
v: V,
size: NonZeroUsize,
) -> (Option<(K, V)>, NonNull<LruEntry<K, V>>) {
let mut tail_size = self.tail_size();
while self.current_size + size.get() - tail_size > self.cap().get() {
if self.current_size == 0 {
break;
}
let _ = self.pop_lru();
tail_size = self.tail_size();
}
if self.current_size + size.get() > self.cap().get() && self.current_size > 0 {
let old_key = KeyRef {
k: unsafe { &(*(*(*self.tail).prev).key.as_ptr()) },
};
let old_node = self.map.remove(&old_key).unwrap();
let node_ptr: *mut LruEntry<K, V> = old_node.as_ptr();
let replaced = unsafe {
(
mem::replace(&mut (*node_ptr).key, mem::MaybeUninit::new(k)).assume_init(),
mem::replace(&mut (*node_ptr).val, mem::MaybeUninit::new(v)).assume_init(),
)
};
self.update_size(node_ptr, size);
self.detach(node_ptr);
(Some(replaced), old_node)
} else {
self.current_size += size.get();
(None, unsafe {
NonNull::new_unchecked(Box::into_raw(Box::new(LruEntry::new(k, v, size))))
})
}
}
pub fn get<'a, Q>(&'a mut self, k: &Q) -> Option<&'a V>
where
K: Borrow<Q>,
Q: Hash + Eq + ?Sized,
{
if let Some(node) = self.map.get_mut(KeyWrapper::from_ref(k)) {
let node_ptr: *mut LruEntry<K, V> = node.as_ptr();
self.detach(node_ptr);
self.attach(node_ptr);
Some(unsafe { &*(*node_ptr).val.as_ptr() })
} else {
None
}
}
pub fn get_mut<'a, Q>(&'a mut self, k: &Q) -> Option<&'a mut V>
where
K: Borrow<Q>,
Q: Hash + Eq + ?Sized,
{
if let Some(node) = self.map.get_mut(KeyWrapper::from_ref(k)) {
let node_ptr: *mut LruEntry<K, V> = node.as_ptr();
self.detach(node_ptr);
self.attach(node_ptr);
Some(unsafe { &mut *(*node_ptr).val.as_mut_ptr() })
} else {
None
}
}
pub fn get_key_value<'a, Q>(&'a mut self, k: &Q) -> Option<(&'a K, &'a V)>
where
K: Borrow<Q>,
Q: Hash + Eq + ?Sized,
{
if let Some(node) = self.map.get_mut(KeyWrapper::from_ref(k)) {
let node_ptr: *mut LruEntry<K, V> = node.as_ptr();
self.detach(node_ptr);
self.attach(node_ptr);
Some(unsafe { (&*(*node_ptr).key.as_ptr(), &*(*node_ptr).val.as_ptr()) })
} else {
None
}
}
pub fn get_key_value_mut<'a, Q>(&'a mut self, k: &Q) -> Option<(&'a K, &'a mut V)>
where
K: Borrow<Q>,
Q: Hash + Eq + ?Sized,
{
if let Some(node) = self.map.get_mut(KeyWrapper::from_ref(k)) {
let node_ptr: *mut LruEntry<K, V> = node.as_ptr();
self.detach(node_ptr);
self.attach(node_ptr);
Some(unsafe {
(
&*(*node_ptr).key.as_ptr(),
&mut *(*node_ptr).val.as_mut_ptr(),
)
})
} else {
None
}
}
pub fn get_or_insert<F>(&mut self, k: K, f: F) -> &V
where
F: FnOnce() -> (V, NonZeroUsize),
{
if let Some(node) = self.map.get_mut(&KeyRef { k: &k }) {
let node_ptr: *mut LruEntry<K, V> = node.as_ptr();
self.detach(node_ptr);
self.attach(node_ptr);
unsafe { &*(*node_ptr).val.as_ptr() }
} else {
let (v, size) = f();
let (_, node) = self.replace_or_create_node(k, v, size);
let node_ptr: *mut LruEntry<K, V> = node.as_ptr();
self.attach(node_ptr);
let keyref = unsafe { (*node_ptr).key.as_ptr() };
self.map.insert(KeyRef { k: keyref }, node);
unsafe { &*(*node_ptr).val.as_ptr() }
}
}
pub fn get_or_insert_ref<'a, Q, F>(&'a mut self, k: &'_ Q, f: F) -> &'a V
where
K: Borrow<Q>,
Q: Hash + Eq + ?Sized + alloc::borrow::ToOwned<Owned = K>,
F: FnOnce() -> (V, NonZeroUsize),
{
if let Some(node) = self.map.get_mut(KeyWrapper::from_ref(k)) {
let node_ptr: *mut LruEntry<K, V> = node.as_ptr();
self.detach(node_ptr);
self.attach(node_ptr);
unsafe { &*(*node_ptr).val.as_ptr() }
} else {
let (v, size) = f();
let (_, node) = self.replace_or_create_node(k.to_owned(), v, size);
let node_ptr: *mut LruEntry<K, V> = node.as_ptr();
self.attach(node_ptr);
let keyref = unsafe { (*node_ptr).key.as_ptr() };
self.map.insert(KeyRef { k: keyref }, node);
unsafe { &*(*node_ptr).val.as_ptr() }
}
}
pub fn try_get_or_insert<F, E>(&mut self, k: K, f: F) -> Result<&V, E>
where
F: FnOnce() -> Result<(V, NonZeroUsize), E>,
{
if let Some(node) = self.map.get_mut(&KeyRef { k: &k }) {
let node_ptr: *mut LruEntry<K, V> = node.as_ptr();
self.detach(node_ptr);
self.attach(node_ptr);
unsafe { Ok(&*(*node_ptr).val.as_ptr()) }
} else {
let (v, size) = f()?;
let (_, node) = self.replace_or_create_node(k, v, size);
let node_ptr: *mut LruEntry<K, V> = node.as_ptr();
self.attach(node_ptr);
let keyref = unsafe { (*node_ptr).key.as_ptr() };
self.map.insert(KeyRef { k: keyref }, node);
Ok(unsafe { &*(*node_ptr).val.as_ptr() })
}
}
pub fn try_get_or_insert_ref<'a, Q, F, E>(&'a mut self, k: &'_ Q, f: F) -> Result<&'a V, E>
where
K: Borrow<Q>,
Q: Hash + Eq + ?Sized + alloc::borrow::ToOwned<Owned = K>,
F: FnOnce() -> Result<(V, NonZeroUsize), E>,
{
if let Some(node) = self.map.get_mut(KeyWrapper::from_ref(k)) {
let node_ptr: *mut LruEntry<K, V> = node.as_ptr();
self.detach(node_ptr);
self.attach(node_ptr);
unsafe { Ok(&*(*node_ptr).val.as_ptr()) }
} else {
let (v, size) = f()?;
let (_, node) = self.replace_or_create_node(k.to_owned(), v, size);
let node_ptr: *mut LruEntry<K, V> = node.as_ptr();
self.attach(node_ptr);
let keyref = unsafe { (*node_ptr).key.as_ptr() };
self.map.insert(KeyRef { k: keyref }, node);
Ok(unsafe { &*(*node_ptr).val.as_ptr() })
}
}
pub fn get_or_insert_mut<F>(&mut self, k: K, f: F) -> &mut V
where
F: FnOnce() -> (V, NonZeroUsize),
{
if let Some(node) = self.map.get_mut(&KeyRef { k: &k }) {
let node_ptr: *mut LruEntry<K, V> = node.as_ptr();
self.detach(node_ptr);
self.attach(node_ptr);
unsafe { &mut *(*node_ptr).val.as_mut_ptr() }
} else {
let (v, size) = f();
let (_, node) = self.replace_or_create_node(k, v, size);
let node_ptr: *mut LruEntry<K, V> = node.as_ptr();
self.attach(node_ptr);
let keyref = unsafe { (*node_ptr).key.as_ptr() };
self.map.insert(KeyRef { k: keyref }, node);
unsafe { &mut *(*node_ptr).val.as_mut_ptr() }
}
}
pub fn get_or_insert_mut_ref<'a, Q, F>(&mut self, k: &'_ Q, f: F) -> &'a mut V
where
K: Borrow<Q>,
Q: Hash + Eq + ?Sized + alloc::borrow::ToOwned<Owned = K>,
F: FnOnce() -> (V, NonZeroUsize),
{
if let Some(node) = self.map.get_mut(KeyWrapper::from_ref(k)) {
let node_ptr: *mut LruEntry<K, V> = node.as_ptr();
self.detach(node_ptr);
self.attach(node_ptr);
unsafe { &mut *(*node_ptr).val.as_mut_ptr() }
} else {
let (v, size) = f();
let (_, node) = self.replace_or_create_node(k.to_owned(), v, size);
let node_ptr: *mut LruEntry<K, V> = node.as_ptr();
self.attach(node_ptr);
let keyref = unsafe { (*node_ptr).key.as_ptr() };
self.map.insert(KeyRef { k: keyref }, node);
unsafe { &mut *(*node_ptr).val.as_mut_ptr() }
}
}
pub fn try_get_or_insert_mut<F, E>(&mut self, k: K, f: F) -> Result<&mut V, E>
where
F: FnOnce() -> Result<(V, NonZeroUsize), E>,
{
if let Some(node) = self.map.get_mut(&KeyRef { k: &k }) {
let node_ptr: *mut LruEntry<K, V> = node.as_ptr();
self.detach(node_ptr);
self.attach(node_ptr);
unsafe { Ok(&mut *(*node_ptr).val.as_mut_ptr()) }
} else {
let (v, size) = f()?;
let (_, node) = self.replace_or_create_node(k, v, size);
let node_ptr: *mut LruEntry<K, V> = node.as_ptr();
self.attach(node_ptr);
let keyref = unsafe { (*node_ptr).key.as_ptr() };
self.map.insert(KeyRef { k: keyref }, node);
unsafe { Ok(&mut *(*node_ptr).val.as_mut_ptr()) }
}
}
pub fn try_get_or_insert_mut_ref<'a, Q, F, E>(
&'a mut self,
k: &'_ Q,
f: F,
) -> Result<&'a mut V, E>
where
K: Borrow<Q>,
Q: Hash + Eq + ?Sized + alloc::borrow::ToOwned<Owned = K>,
F: FnOnce() -> Result<(V, NonZeroUsize), E>,
{
if let Some(node) = self.map.get_mut(KeyWrapper::from_ref(k)) {
let node_ptr: *mut LruEntry<K, V> = node.as_ptr();
self.detach(node_ptr);
self.attach(node_ptr);
unsafe { Ok(&mut *(*node_ptr).val.as_mut_ptr()) }
} else {
let (v, size) = f()?;
let (_, node) = self.replace_or_create_node(k.to_owned(), v, size);
let node_ptr: *mut LruEntry<K, V> = node.as_ptr();
self.attach(node_ptr);
let keyref = unsafe { (*node_ptr).key.as_ptr() };
self.map.insert(KeyRef { k: keyref }, node);
unsafe { Ok(&mut *(*node_ptr).val.as_mut_ptr()) }
}
}
pub fn peek<'a, Q>(&'a self, k: &Q) -> Option<&'a V>
where
K: Borrow<Q>,
Q: Hash + Eq + ?Sized,
{
self.map
.get(KeyWrapper::from_ref(k))
.map(|node| unsafe { &*node.as_ref().val.as_ptr() })
}
pub fn peek_mut<'a, Q>(&'a mut self, k: &Q) -> Option<&'a mut V>
where
K: Borrow<Q>,
Q: Hash + Eq + ?Sized,
{
match self.map.get_mut(KeyWrapper::from_ref(k)) {
None => None,
Some(node) => Some(unsafe { &mut *(*node.as_ptr()).val.as_mut_ptr() }),
}
}
pub fn peek_lru(&self) -> Option<(&K, &V)> {
if self.is_empty() {
return None;
}
let (key, val);
unsafe {
let node = (*self.tail).prev;
key = &(*(*node).key.as_ptr()) as &K;
val = &(*(*node).val.as_ptr()) as &V;
}
Some((key, val))
}
pub fn peek_mru(&self) -> Option<(&K, &V)> {
if self.is_empty() {
return None;
}
let (key, val);
unsafe {
let node: *mut LruEntry<K, V> = (*self.head).next;
key = &(*(*node).key.as_ptr()) as &K;
val = &(*(*node).val.as_ptr()) as &V;
}
Some((key, val))
}
pub fn contains<Q>(&self, k: &Q) -> bool
where
K: Borrow<Q>,
Q: Hash + Eq + ?Sized,
{
self.map.contains_key(KeyWrapper::from_ref(k))
}
pub fn pop<Q>(&mut self, k: &Q) -> Option<V>
where
K: Borrow<Q>,
Q: Hash + Eq + ?Sized,
{
match self.map.remove(KeyWrapper::from_ref(k)) {
None => None,
Some(old_node) => {
let mut old_node = unsafe {
let mut old_node = *Box::from_raw(old_node.as_ptr());
ptr::drop_in_place(old_node.key.as_mut_ptr());
self.current_size -= old_node.size.get();
old_node
};
self.detach(&mut old_node);
let LruEntry { key: _, val, .. } = old_node;
unsafe { Some(val.assume_init()) }
}
}
}
pub fn pop_entry<Q>(&mut self, k: &Q) -> Option<(K, V)>
where
K: Borrow<Q>,
Q: Hash + Eq + ?Sized,
{
match self.map.remove(KeyWrapper::from_ref(k)) {
None => None,
Some(old_node) => {
let mut old_node = unsafe { *Box::from_raw(old_node.as_ptr()) };
self.detach(&mut old_node);
let LruEntry { key, val, .. } = old_node;
unsafe { Some((key.assume_init(), val.assume_init())) }
}
}
}
pub fn pop_lru(&mut self) -> Option<(K, V)> {
let node = self.remove_last()?;
let node = *node;
let LruEntry { key, val, .. } = node;
unsafe { Some((key.assume_init(), val.assume_init())) }
}
pub fn pop_mru(&mut self) -> Option<(K, V)> {
let node = self.remove_first()?;
let node = *node;
let LruEntry { key, val, .. } = node;
unsafe { Some((key.assume_init(), val.assume_init())) }
}
pub fn promote<Q>(&mut self, k: &Q)
where
K: Borrow<Q>,
Q: Hash + Eq + ?Sized,
{
if let Some(node) = self.map.get_mut(KeyWrapper::from_ref(k)) {
let node_ptr: *mut LruEntry<K, V> = node.as_ptr();
self.detach(node_ptr);
self.attach(node_ptr);
}
}
pub fn demote<Q>(&mut self, k: &Q)
where
K: Borrow<Q>,
Q: Hash + Eq + ?Sized,
{
if let Some(node) = self.map.get_mut(KeyWrapper::from_ref(k)) {
let node_ptr: *mut LruEntry<K, V> = node.as_ptr();
self.detach(node_ptr);
self.attach_last(node_ptr);
}
}
pub fn len(&self) -> usize {
self.map.len()
}
pub fn size(&self) -> usize {
self.current_size
}
pub fn is_empty(&self) -> bool {
self.map.len() == 0
}
pub fn cap(&self) -> NonZeroUsize {
self.cap
}
pub fn resize(&mut self, cap: NonZeroUsize) {
if cap == self.cap {
return;
}
while self.size() > cap.get() {
self.pop_lru();
}
self.map.shrink_to_fit();
self.cap = cap;
}
pub fn clear(&mut self) {
while self.pop_lru().is_some() {}
}
pub fn iter(&self) -> Iter<'_, K, V> {
Iter {
len: self.len(),
ptr: unsafe { (*self.head).next },
end: unsafe { (*self.tail).prev },
phantom: PhantomData,
}
}
pub fn iter_mut(&mut self) -> IterMut<'_, K, V> {
IterMut {
len: self.len(),
ptr: unsafe { (*self.head).next },
end: unsafe { (*self.tail).prev },
phantom: PhantomData,
}
}
fn remove_first(&mut self) -> Option<Box<LruEntry<K, V>>> {
let next;
unsafe { next = (*self.head).next }
if next != self.tail {
let old_key = KeyRef {
k: unsafe { &(*(*(*self.head).next).key.as_ptr()) },
};
let old_node = self.map.remove(&old_key).unwrap();
let node_ptr: *mut LruEntry<K, V> = old_node.as_ptr();
self.current_size -= unsafe { (*node_ptr).size.get() };
self.detach(node_ptr);
unsafe { Some(Box::from_raw(node_ptr)) }
} else {
None
}
}
fn remove_last(&mut self) -> Option<Box<LruEntry<K, V>>> {
let prev;
unsafe { prev = (*self.tail).prev }
if prev != self.head {
let old_key = KeyRef {
k: unsafe { &(*(*(*self.tail).prev).key.as_ptr()) },
};
let old_node = self.map.remove(&old_key).unwrap();
let node_ptr: *mut LruEntry<K, V> = old_node.as_ptr();
self.current_size -= unsafe { (*node_ptr).size.get() };
self.detach(node_ptr);
unsafe { Some(Box::from_raw(node_ptr)) }
} else {
None
}
}
fn detach(&mut self, node: *mut LruEntry<K, V>) {
unsafe {
(*(*node).prev).next = (*node).next;
(*(*node).next).prev = (*node).prev;
}
}
fn attach(&mut self, node: *mut LruEntry<K, V>) {
unsafe {
(*node).next = (*self.head).next;
(*node).prev = self.head;
(*self.head).next = node;
(*(*node).next).prev = node;
}
}
fn attach_last(&mut self, node: *mut LruEntry<K, V>) {
unsafe {
(*node).next = self.tail;
(*node).prev = (*self.tail).prev;
(*self.tail).prev = node;
(*(*node).prev).next = node;
}
}
}
impl<K, V, S> Drop for LruCache<K, V, S> {
fn drop(&mut self) {
self.map.drain().for_each(|(_, node)| unsafe {
let mut node = *Box::from_raw(node.as_ptr());
ptr::drop_in_place((node).key.as_mut_ptr());
ptr::drop_in_place((node).val.as_mut_ptr());
});
let _head = unsafe { *Box::from_raw(self.head) };
let _tail = unsafe { *Box::from_raw(self.tail) };
}
}
impl<'a, K: Hash + Eq, V, S: BuildHasher> IntoIterator for &'a LruCache<K, V, S> {
type Item = (&'a K, &'a V);
type IntoIter = Iter<'a, K, V>;
fn into_iter(self) -> Iter<'a, K, V> {
self.iter()
}
}
impl<'a, K: Hash + Eq, V, S: BuildHasher> IntoIterator for &'a mut LruCache<K, V, S> {
type Item = (&'a K, &'a mut V);
type IntoIter = IterMut<'a, K, V>;
fn into_iter(self) -> IterMut<'a, K, V> {
self.iter_mut()
}
}
unsafe impl<K: Send, V: Send, S: Send> Send for LruCache<K, V, S> {}
unsafe impl<K: Sync, V: Sync, S: Sync> Sync for LruCache<K, V, S> {}
impl<K: Hash + Eq, V, S: BuildHasher> fmt::Debug for LruCache<K, V, S> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("LruCache")
.field("len", &self.len())
.field("cap", &self.cap())
.finish()
}
}
pub struct Iter<'a, K: 'a, V: 'a> {
len: usize,
ptr: *const LruEntry<K, V>,
end: *const LruEntry<K, V>,
phantom: PhantomData<&'a K>,
}
impl<'a, K, V> Iterator for Iter<'a, K, V> {
type Item = (&'a K, &'a V);
fn next(&mut self) -> Option<(&'a K, &'a V)> {
if self.len == 0 {
return None;
}
let key = unsafe { &(*(*self.ptr).key.as_ptr()) as &K };
let val = unsafe { &(*(*self.ptr).val.as_ptr()) as &V };
self.len -= 1;
self.ptr = unsafe { (*self.ptr).next };
Some((key, val))
}
fn size_hint(&self) -> (usize, Option<usize>) {
(self.len, Some(self.len))
}
fn count(self) -> usize {
self.len
}
}
impl<'a, K, V> DoubleEndedIterator for Iter<'a, K, V> {
fn next_back(&mut self) -> Option<(&'a K, &'a V)> {
if self.len == 0 {
return None;
}
let key = unsafe { &(*(*self.end).key.as_ptr()) as &K };
let val = unsafe { &(*(*self.end).val.as_ptr()) as &V };
self.len -= 1;
self.end = unsafe { (*self.end).prev };
Some((key, val))
}
}
impl<'a, K, V> ExactSizeIterator for Iter<'a, K, V> {}
impl<'a, K, V> FusedIterator for Iter<'a, K, V> {}
impl<'a, K, V> Clone for Iter<'a, K, V> {
fn clone(&self) -> Iter<'a, K, V> {
Iter {
len: self.len,
ptr: self.ptr,
end: self.end,
phantom: PhantomData,
}
}
}
unsafe impl<'a, K: Send, V: Send> Send for Iter<'a, K, V> {}
unsafe impl<'a, K: Sync, V: Sync> Sync for Iter<'a, K, V> {}
pub struct IterMut<'a, K: 'a, V: 'a> {
len: usize,
ptr: *mut LruEntry<K, V>,
end: *mut LruEntry<K, V>,
phantom: PhantomData<&'a K>,
}
impl<'a, K, V> Iterator for IterMut<'a, K, V> {
type Item = (&'a K, &'a mut V);
fn next(&mut self) -> Option<(&'a K, &'a mut V)> {
if self.len == 0 {
return None;
}
let key = unsafe { &mut (*(*self.ptr).key.as_mut_ptr()) as &mut K };
let val = unsafe { &mut (*(*self.ptr).val.as_mut_ptr()) as &mut V };
self.len -= 1;
self.ptr = unsafe { (*self.ptr).next };
Some((key, val))
}
fn size_hint(&self) -> (usize, Option<usize>) {
(self.len, Some(self.len))
}
fn count(self) -> usize {
self.len
}
}
impl<'a, K, V> DoubleEndedIterator for IterMut<'a, K, V> {
fn next_back(&mut self) -> Option<(&'a K, &'a mut V)> {
if self.len == 0 {
return None;
}
let key = unsafe { &mut (*(*self.end).key.as_mut_ptr()) as &mut K };
let val = unsafe { &mut (*(*self.end).val.as_mut_ptr()) as &mut V };
self.len -= 1;
self.end = unsafe { (*self.end).prev };
Some((key, val))
}
}
impl<'a, K, V> ExactSizeIterator for IterMut<'a, K, V> {}
impl<'a, K, V> FusedIterator for IterMut<'a, K, V> {}
unsafe impl<'a, K: Send, V: Send> Send for IterMut<'a, K, V> {}
unsafe impl<'a, K: Sync, V: Sync> Sync for IterMut<'a, K, V> {}
pub struct IntoIter<K, V>
where
K: Hash + Eq,
{
cache: LruCache<K, V>,
}
impl<K, V> Iterator for IntoIter<K, V>
where
K: Hash + Eq,
{
type Item = (K, V);
fn next(&mut self) -> Option<(K, V)> {
self.cache.pop_lru()
}
fn size_hint(&self) -> (usize, Option<usize>) {
let len = self.cache.len();
(len, Some(len))
}
fn count(self) -> usize {
self.cache.len()
}
}
impl<K, V> ExactSizeIterator for IntoIter<K, V> where K: Hash + Eq {}
impl<K, V> FusedIterator for IntoIter<K, V> where K: Hash + Eq {}
impl<K: Hash + Eq, V> IntoIterator for LruCache<K, V> {
type Item = (K, V);
type IntoIter = IntoIter<K, V>;
fn into_iter(self) -> IntoIter<K, V> {
IntoIter { cache: self }
}
}
#[cfg(test)]
mod tests {
use super::LruCache;
use alloc::rc::Rc;
use core::{fmt::Debug, num::NonZeroUsize};
use scoped_threadpool::Pool;
use std::sync::atomic::{AtomicUsize, Ordering};
fn build_value<T>(v: T, size: usize) -> (T, NonZeroUsize) {
(v, NonZeroUsize::new(size).unwrap())
}
fn assert_opt_eq<V: PartialEq + Debug>(opt: Option<&V>, v: V) {
assert!(opt.is_some());
assert_eq!(opt.unwrap(), &v);
}
fn assert_opt_eq_mut<V: PartialEq + Debug>(opt: Option<&mut V>, v: V) {
assert!(opt.is_some());
assert_eq!(opt.unwrap(), &v);
}
fn assert_opt_eq_tuple<K: PartialEq + Debug, V: PartialEq + Debug>(
opt: Option<(&K, &V)>,
kv: (K, V),
) {
assert!(opt.is_some());
let res = opt.unwrap();
assert_eq!(res.0, &kv.0);
assert_eq!(res.1, &kv.1);
}
fn assert_opt_eq_mut_tuple<K: PartialEq + Debug, V: PartialEq + Debug>(
opt: Option<(&K, &mut V)>,
kv: (K, V),
) {
assert!(opt.is_some());
let res = opt.unwrap();
assert_eq!(res.0, &kv.0);
assert_eq!(res.1, &kv.1);
}
#[test]
fn test_unbounded() {
let mut cache = LruCache::unbounded();
for i in 0..13370 {
cache.put(i, (), NonZeroUsize::new(1).unwrap());
}
assert_eq!(cache.len(), 13370);
assert_eq!(cache.size(), 13370);
}
#[test]
#[cfg(feature = "hashbrown")]
fn test_with_hasher() {
use core::num::NonZeroUsize;
use hashbrown::DefaultHashBuilder;
let s = DefaultHashBuilder::default();
let mut cache = LruCache::with_hasher(NonZeroUsize::new(16).unwrap(), s);
for i in 0..13370 {
cache.put(i, (), NonZeroUsize::new(1).unwrap());
}
assert_eq!(cache.len(), 16);
assert_eq!(cache.size(), 16);
}
#[test]
fn test_put_and_get() {
let mut cache = LruCache::new(NonZeroUsize::new(2).unwrap());
assert!(cache.is_empty());
assert_eq!(
cache.put("apple", "red", NonZeroUsize::new(1).unwrap()),
None
);
assert_eq!(
cache.put("banana", "yellow", NonZeroUsize::new(1).unwrap()),
None
);
assert_eq!(cache.cap().get(), 2);
assert_eq!(cache.len(), 2);
assert!(!cache.is_empty());
assert_opt_eq(cache.get(&"apple"), "red");
assert_opt_eq(cache.get(&"banana"), "yellow");
}
#[test]
fn test_put_and_get_or_insert() {
let mut cache = LruCache::new(NonZeroUsize::new(2).unwrap());
assert!(cache.is_empty());
assert_eq!(
cache.put("apple", "red", NonZeroUsize::new(1).unwrap()),
None
);
assert_eq!(
cache.put("banana", "yellow", NonZeroUsize::new(1).unwrap()),
None
);
assert_eq!(cache.cap().get(), 2);
assert_eq!(cache.len(), 2);
assert!(!cache.is_empty());
assert_eq!(
cache.get_or_insert("apple", || ("orange", NonZeroUsize::new(1).unwrap())),
&"red"
);
assert_eq!(
cache.get_or_insert("banana", || ("orange", NonZeroUsize::new(1).unwrap())),
&"yellow"
);
assert_eq!(
cache.get_or_insert("lemon", || ("orange", NonZeroUsize::new(1).unwrap())),
&"orange"
);
assert_eq!(
cache.get_or_insert("lemon", || ("red", NonZeroUsize::new(1).unwrap())),
&"orange"
);
}
#[test]
fn test_get_or_insert_ref() {
use alloc::borrow::ToOwned;
use alloc::string::String;
let key1 = Rc::new("1".to_owned());
let key2 = Rc::new("2".to_owned());
let mut cache = LruCache::<Rc<String>, String>::new(NonZeroUsize::new(2).unwrap());
assert!(cache.is_empty());
assert_eq!(
cache.get_or_insert_ref(&key1, || ("One".to_owned(), NonZeroUsize::new(1).unwrap())),
"One"
);
assert_eq!(
cache.get_or_insert_ref(&key2, || ("Two".to_owned(), NonZeroUsize::new(1).unwrap())),
"Two"
);
assert_eq!(cache.len(), 2);
assert!(!cache.is_empty());
assert_eq!(
cache.get_or_insert_ref(&key2, || (
"Not two".to_owned(),
NonZeroUsize::new(1).unwrap()
)),
"Two"
);
assert_eq!(
cache.get_or_insert_ref(&key2, || (
"Again not two".to_owned(),
NonZeroUsize::new(1).unwrap()
)),
"Two"
);
assert_eq!(Rc::strong_count(&key1), 2);
assert_eq!(Rc::strong_count(&key2), 2);
}
#[test]
fn test_try_get_or_insert() {
let mut cache = LruCache::new(NonZeroUsize::new(2).unwrap());
assert_eq!(
cache.try_get_or_insert::<_, &str>("apple", || Ok(build_value("red", 1))),
Ok(&"red")
);
assert_eq!(
cache.try_get_or_insert::<_, &str>("apple", || Err("failed")),
Ok(&"red")
);
assert_eq!(
cache.try_get_or_insert::<_, &str>("banana", || Ok(build_value("orange", 1))),
Ok(&"orange")
);
assert_eq!(
cache.try_get_or_insert::<_, &str>("lemon", || Err("failed")),
Err("failed")
);
assert_eq!(
cache.try_get_or_insert::<_, &str>("banana", || Err("failed")),
Ok(&"orange")
);
}
#[test]
fn test_try_get_or_insert_ref() {
use alloc::borrow::ToOwned;
use alloc::string::String;
let key1 = Rc::new("1".to_owned());
let key2 = Rc::new("2".to_owned());
let mut cache = LruCache::<Rc<String>, String>::new(NonZeroUsize::new(2).unwrap());
let f = || -> Result<(String, NonZeroUsize), ()> { Err(()) };
let a = || -> Result<(String, NonZeroUsize), ()> { Ok(build_value("One".to_owned(), 1)) };
let b = || -> Result<(String, NonZeroUsize), ()> { Ok(build_value("Two".to_owned(), 1)) };
assert_eq!(cache.try_get_or_insert_ref(&key1, a), Ok(&"One".to_owned()));
assert_eq!(cache.try_get_or_insert_ref(&key2, f), Err(()));
assert_eq!(cache.try_get_or_insert_ref(&key2, b), Ok(&"Two".to_owned()));
assert_eq!(cache.try_get_or_insert_ref(&key2, a), Ok(&"Two".to_owned()));
assert_eq!(cache.len(), 2);
assert_eq!(Rc::strong_count(&key1), 2);
assert_eq!(Rc::strong_count(&key2), 2);
}
#[test]
fn test_put_and_get_or_insert_mut() {
let mut cache = LruCache::new(NonZeroUsize::new(2).unwrap());
assert!(cache.is_empty());
assert_eq!(
cache.put("apple", "red", NonZeroUsize::new(1).unwrap()),
None
);
assert_eq!(
cache.put("banana", "yellow", NonZeroUsize::new(1).unwrap()),
None
);
assert_eq!(cache.cap().get(), 2);
assert_eq!(cache.len(), 2);
let v = cache.get_or_insert_mut("apple", || build_value("orange", 1));
assert_eq!(v, &"red");
*v = "blue";
assert_eq!(
cache.get_or_insert_mut("apple", || build_value("orange", 1)),
&"blue"
);
assert_eq!(
cache.get_or_insert_mut("banana", || build_value("orange", 1)),
&"yellow"
);
assert_eq!(
cache.get_or_insert_mut("lemon", || build_value("orange", 1)),
&"orange"
);
assert_eq!(
cache.get_or_insert_mut("lemon", || build_value("red", 1)),
&"orange"
);
}
#[test]
fn test_get_or_insert_mut_ref() {
use alloc::borrow::ToOwned;
use alloc::string::String;
let key1 = Rc::new("1".to_owned());
let key2 = Rc::new("2".to_owned());
let mut cache = LruCache::<Rc<String>, &'static str>::new(NonZeroUsize::new(2).unwrap());
assert_eq!(
cache.get_or_insert_mut_ref(&key1, || build_value("One", 1)),
&mut "One"
);
let v = cache.get_or_insert_mut_ref(&key2, || build_value("Two", 1));
*v = "New two";
assert_eq!(
cache.get_or_insert_mut_ref(&key2, || build_value("Two", 1)),
&mut "New two"
);
assert_eq!(Rc::strong_count(&key1), 2);
assert_eq!(Rc::strong_count(&key2), 2);
}
#[test]
fn test_try_get_or_insert_mut() {
let mut cache = LruCache::new(NonZeroUsize::new(2).unwrap());
cache.put(1, "a", NonZeroUsize::new(1).unwrap());
cache.put(2, "b", NonZeroUsize::new(1).unwrap());
cache.put(2, "c", NonZeroUsize::new(1).unwrap());
let f = || -> Result<(&str, NonZeroUsize), &str> { Err("failed") };
let a = || -> Result<(&str, NonZeroUsize), &str> { Ok(build_value("a", 1)) };
let b = || -> Result<(&str, NonZeroUsize), &str> { Ok(build_value("b", 1)) };
if let Ok(v) = cache.try_get_or_insert_mut(2, a) {
*v = "d";
}
assert_eq!(cache.try_get_or_insert_mut(2, a), Ok(&mut "d"));
assert_eq!(cache.try_get_or_insert_mut(3, f), Err("failed"));
assert_eq!(cache.try_get_or_insert_mut(4, b), Ok(&mut "b"));
assert_eq!(cache.try_get_or_insert_mut(4, a), Ok(&mut "b"));
}
#[test]
fn test_try_get_or_insert_mut_ref() {
use alloc::borrow::ToOwned;
use alloc::string::String;
let key1 = Rc::new("1".to_owned());
let key2 = Rc::new("2".to_owned());
let mut cache = LruCache::<Rc<String>, String>::new(NonZeroUsize::new(2).unwrap());
let f = || -> Result<(String, NonZeroUsize), ()> { Err(()) };
let a = || -> Result<(String, NonZeroUsize), ()> { Ok(build_value("One".to_owned(), 1)) };
let b = || -> Result<(String, NonZeroUsize), ()> { Ok(build_value("Two".to_owned(), 1)) };
assert_eq!(
cache.try_get_or_insert_mut_ref(&key1, a),
Ok(&mut "One".to_owned())
);
assert_eq!(cache.try_get_or_insert_mut_ref(&key2, f), Err(()));
if let Ok(v) = cache.try_get_or_insert_mut_ref(&key2, b) {
assert_eq!(v, &mut "Two");
*v = "New two".to_owned();
}
assert_eq!(
cache.try_get_or_insert_mut_ref(&key2, a),
Ok(&mut "New two".to_owned())
);
assert_eq!(Rc::strong_count(&key1), 2);
assert_eq!(Rc::strong_count(&key2), 2);
}
#[test]
fn test_put_and_get_mut() {
let mut cache = LruCache::new(NonZeroUsize::new(2).unwrap());
cache.put("apple", "red", NonZeroUsize::new(1).unwrap());
cache.put("banana", "yellow", NonZeroUsize::new(1).unwrap());
assert_eq!(cache.cap().get(), 2);
assert_eq!(cache.len(), 2);
assert_opt_eq_mut(cache.get_mut(&"apple"), "red");
assert_opt_eq_mut(cache.get_mut(&"banana"), "yellow");
}
#[test]
fn test_get_mut_and_update() {
let mut cache = LruCache::new(NonZeroUsize::new(2).unwrap());
cache.put("apple", 1, NonZeroUsize::new(1).unwrap());
cache.put("banana", 3, NonZeroUsize::new(1).unwrap());
{
let v = cache.get_mut(&"apple").unwrap();
*v = 4;
}
assert_eq!(cache.cap().get(), 2);
assert_eq!(cache.len(), 2);
assert_opt_eq_mut(cache.get_mut(&"apple"), 4);
assert_opt_eq_mut(cache.get_mut(&"banana"), 3);
}
#[test]
fn test_put_update() {
let mut cache = LruCache::new(NonZeroUsize::new(2).unwrap());
assert_eq!(
cache.put("apple", "red", NonZeroUsize::new(1).unwrap()),
None
);
assert_eq!(
cache.put("apple", "green", NonZeroUsize::new(1).unwrap()),
Some("red")
);
assert_eq!(cache.len(), 1);
assert_opt_eq(cache.get(&"apple"), "green");
}
#[test]
fn test_put_removes_oldest() {
let mut cache = LruCache::new(NonZeroUsize::new(2).unwrap());
assert_eq!(
cache.put("apple", "red", NonZeroUsize::new(1).unwrap()),
None
);
assert_eq!(
cache.put("banana", "yellow", NonZeroUsize::new(1).unwrap()),
None
);
assert_eq!(
cache.put("pear", "green", NonZeroUsize::new(1).unwrap()),
None
);
assert!(cache.get(&"apple").is_none());
assert_opt_eq(cache.get(&"banana"), "yellow");
assert_opt_eq(cache.get(&"pear"), "green");
assert_eq!(
cache.put("apple", "green", NonZeroUsize::new(1).unwrap()),
None
);
assert_eq!(
cache.put("tomato", "red", NonZeroUsize::new(1).unwrap()),
None
);
assert!(cache.get(&"pear").is_none());
assert_opt_eq(cache.get(&"apple"), "green");
assert_opt_eq(cache.get(&"tomato"), "red");
}
#[test]
fn test_peek() {
let mut cache = LruCache::new(NonZeroUsize::new(2).unwrap());
cache.put("apple", "red", NonZeroUsize::new(1).unwrap());
cache.put("banana", "yellow", NonZeroUsize::new(1).unwrap());
assert_opt_eq(cache.peek(&"banana"), "yellow");
assert_opt_eq(cache.peek(&"apple"), "red");
cache.put("pear", "green", NonZeroUsize::new(1).unwrap());
assert!(cache.peek(&"apple").is_none());
assert_opt_eq(cache.peek(&"banana"), "yellow");
assert_opt_eq(cache.peek(&"pear"), "green");
}
#[test]
fn test_peek_mut() {
let mut cache = LruCache::new(NonZeroUsize::new(2).unwrap());
cache.put("apple", "red", NonZeroUsize::new(1).unwrap());
cache.put("banana", "yellow", NonZeroUsize::new(1).unwrap());
assert_opt_eq_mut(cache.peek_mut(&"banana"), "yellow");
assert_opt_eq_mut(cache.peek_mut(&"apple"), "red");
assert!(cache.peek_mut(&"pear").is_none());
cache.put("pear", "green", NonZeroUsize::new(1).unwrap());
assert!(cache.peek_mut(&"apple").is_none());
assert_opt_eq_mut(cache.peek_mut(&"banana"), "yellow");
assert_opt_eq_mut(cache.peek_mut(&"pear"), "green");
{
let v = cache.peek_mut(&"banana").unwrap();
*v = "green";
}
assert_opt_eq_mut(cache.peek_mut(&"banana"), "green");
}
#[test]
fn test_peek_lru() {
let mut cache = LruCache::new(NonZeroUsize::new(2).unwrap());
assert!(cache.peek_lru().is_none());
cache.put("apple", "red", NonZeroUsize::new(1).unwrap());
cache.put("banana", "yellow", NonZeroUsize::new(1).unwrap());
assert_opt_eq_tuple(cache.peek_lru(), ("apple", "red"));
cache.get(&"apple");
assert_opt_eq_tuple(cache.peek_lru(), ("banana", "yellow"));
cache.clear();
assert!(cache.peek_lru().is_none());
}
#[test]
fn test_peek_mru() {
let mut cache = LruCache::new(NonZeroUsize::new(2).unwrap());
assert!(cache.peek_mru().is_none());
cache.put("apple", "red", NonZeroUsize::new(1).unwrap());
cache.put("banana", "yellow", NonZeroUsize::new(1).unwrap());
assert_opt_eq_tuple(cache.peek_mru(), ("banana", "yellow"));
cache.get(&"apple");
assert_opt_eq_tuple(cache.peek_mru(), ("apple", "red"));
cache.clear();
assert!(cache.peek_mru().is_none());
}
#[test]
fn test_contains() {
let mut cache = LruCache::new(NonZeroUsize::new(2).unwrap());
cache.put("apple", "red", NonZeroUsize::new(1).unwrap());
cache.put("banana", "yellow", NonZeroUsize::new(1).unwrap());
cache.put("pear", "green", NonZeroUsize::new(1).unwrap());
assert!(!cache.contains(&"apple"));
assert!(cache.contains(&"banana"));
assert!(cache.contains(&"pear"));
}
#[test]
fn test_pop() {
let mut cache = LruCache::new(NonZeroUsize::new(2).unwrap());
cache.put("apple", "red", NonZeroUsize::new(1).unwrap());
cache.put("banana", "yellow", NonZeroUsize::new(1).unwrap());
assert_eq!(cache.len(), 2);
assert_opt_eq(cache.get(&"apple"), "red");
assert_opt_eq(cache.get(&"banana"), "yellow");
let popped = cache.pop(&"apple");
assert!(popped.is_some());
assert_eq!(popped.unwrap(), "red");
assert_eq!(cache.len(), 1);
assert!(cache.get(&"apple").is_none());
assert_opt_eq(cache.get(&"banana"), "yellow");
}
#[test]
fn test_pop_entry() {
let mut cache = LruCache::new(NonZeroUsize::new(2).unwrap());
cache.put("apple", "red", NonZeroUsize::new(1).unwrap());
cache.put("banana", "yellow", NonZeroUsize::new(1).unwrap());
assert_eq!(cache.len(), 2);
assert_opt_eq(cache.get(&"apple"), "red");
assert_opt_eq(cache.get(&"banana"), "yellow");
let popped = cache.pop_entry(&"apple");
assert!(popped.is_some());
assert_eq!(popped.unwrap(), ("apple", "red"));
assert_eq!(cache.len(), 1);
assert!(cache.get(&"apple").is_none());
assert_opt_eq(cache.get(&"banana"), "yellow");
}
#[test]
fn test_pop_lru() {
let mut cache = LruCache::new(NonZeroUsize::new(200).unwrap());
for i in 0..75 {
cache.put(i, "A", NonZeroUsize::new(1).unwrap());
}
for i in 0..75 {
cache.put(i + 100, "B", NonZeroUsize::new(1).unwrap());
}
for i in 0..75 {
cache.put(i + 200, "C", NonZeroUsize::new(1).unwrap());
}
assert_eq!(cache.len(), 200);
for i in 0..75 {
assert_opt_eq(cache.get(&(74 - i + 100)), "B");
}
assert_opt_eq(cache.get(&25), "A");
for i in 26..75 {
assert_eq!(cache.pop_lru(), Some((i, "A")));
}
for i in 0..75 {
assert_eq!(cache.pop_lru(), Some((i + 200, "C")));
}
for i in 0..75 {
assert_eq!(cache.pop_lru(), Some((74 - i + 100, "B")));
}
assert_eq!(cache.pop_lru(), Some((25, "A")));
for _ in 0..50 {
assert_eq!(cache.pop_lru(), None);
}
}
#[test]
fn test_pop_mru() {
let mut cache = LruCache::new(NonZeroUsize::new(200).unwrap());
for i in 0..75 {
cache.put(i, "A", NonZeroUsize::new(1).unwrap());
}
for i in 0..75 {
cache.put(i + 100, "B", NonZeroUsize::new(1).unwrap());
}
for i in 0..75 {
cache.put(i + 200, "C", NonZeroUsize::new(1).unwrap());
}
assert_eq!(cache.len(), 200);
for i in 0..75 {
assert_opt_eq(cache.get(&(74 - i + 100)), "B");
}
assert_opt_eq(cache.get(&25), "A");
assert_eq!(cache.pop_mru(), Some((25, "A")));
for i in 0..75 {
assert_eq!(cache.pop_mru(), Some((i + 100, "B")));
}
for i in 0..75 {
assert_eq!(cache.pop_mru(), Some((74 - i + 200, "C")));
}
for i in (26..75).into_iter().rev() {
assert_eq!(cache.pop_mru(), Some((i, "A")));
}
for _ in 0..50 {
assert_eq!(cache.pop_mru(), None);
}
}
#[test]
fn test_clear() {
let mut cache = LruCache::new(NonZeroUsize::new(2).unwrap());
cache.put("apple", "red", NonZeroUsize::new(1).unwrap());
cache.put("banana", "yellow", NonZeroUsize::new(1).unwrap());
assert_eq!(cache.len(), 2);
assert_opt_eq(cache.get(&"apple"), "red");
assert_opt_eq(cache.get(&"banana"), "yellow");
cache.clear();
assert_eq!(cache.len(), 0);
}
#[test]
fn test_resize_larger() {
let mut cache = LruCache::new(NonZeroUsize::new(2).unwrap());
cache.put(1, "a", NonZeroUsize::new(1).unwrap());
cache.put(2, "b", NonZeroUsize::new(1).unwrap());
cache.resize(NonZeroUsize::new(4).unwrap());
cache.put(3, "c", NonZeroUsize::new(1).unwrap());
cache.put(4, "d", NonZeroUsize::new(1).unwrap());
assert_eq!(cache.len(), 4);
assert_eq!(cache.get(&1), Some(&"a"));
assert_eq!(cache.get(&2), Some(&"b"));
assert_eq!(cache.get(&3), Some(&"c"));
assert_eq!(cache.get(&4), Some(&"d"));
}
#[test]
fn test_resize_smaller() {
let mut cache = LruCache::new(NonZeroUsize::new(4).unwrap());
cache.put(1, "a", NonZeroUsize::new(1).unwrap());
cache.put(2, "b", NonZeroUsize::new(1).unwrap());
cache.put(3, "c", NonZeroUsize::new(1).unwrap());
cache.put(4, "d", NonZeroUsize::new(1).unwrap());
cache.resize(NonZeroUsize::new(2).unwrap());
assert_eq!(cache.len(), 2);
assert!(cache.get(&1).is_none());
assert!(cache.get(&2).is_none());
assert_eq!(cache.get(&3), Some(&"c"));
assert_eq!(cache.get(&4), Some(&"d"));
}
#[test]
fn test_send() {
use std::thread;
let mut cache = LruCache::new(NonZeroUsize::new(4).unwrap());
cache.put(1, "a", NonZeroUsize::new(1).unwrap());
let handle = thread::spawn(move || {
assert_eq!(cache.get(&1), Some(&"a"));
});
assert!(handle.join().is_ok());
}
#[test]
fn test_multiple_threads() {
let mut pool = Pool::new(1);
let mut cache = LruCache::new(NonZeroUsize::new(4).unwrap());
cache.put(1, "a", NonZeroUsize::new(1).unwrap());
let cache_ref = &cache;
pool.scoped(|scoped| {
scoped.execute(move || {
assert_eq!(cache_ref.peek(&1), Some(&"a"));
});
});
assert_eq!((cache_ref).peek(&1), Some(&"a"));
}
#[test]
fn test_iter_forwards() {
let mut cache = LruCache::new(NonZeroUsize::new(3).unwrap());
cache.put("a", 1, NonZeroUsize::new(1).unwrap());
cache.put("b", 2, NonZeroUsize::new(1).unwrap());
cache.put("c", 3, NonZeroUsize::new(1).unwrap());
{
let mut iter = cache.iter();
assert_eq!(iter.len(), 3);
assert_opt_eq_tuple(iter.next(), ("c", 3));
assert_eq!(iter.len(), 2);
assert_opt_eq_tuple(iter.next(), ("b", 2));
assert_eq!(iter.len(), 1);
assert_opt_eq_tuple(iter.next(), ("a", 1));
assert_eq!(iter.len(), 0);
assert_eq!(iter.next(), None);
}
{
let mut iter = cache.iter_mut();
assert_eq!(iter.len(), 3);
assert_opt_eq_mut_tuple(iter.next(), ("c", 3));
assert_eq!(iter.len(), 2);
assert_opt_eq_mut_tuple(iter.next(), ("b", 2));
assert_eq!(iter.len(), 1);
assert_opt_eq_mut_tuple(iter.next(), ("a", 1));
assert_eq!(iter.len(), 0);
assert_eq!(iter.next(), None);
}
}
#[test]
fn test_iter_backwards() {
let mut cache = LruCache::new(NonZeroUsize::new(3).unwrap());
cache.put("a", 1, NonZeroUsize::new(1).unwrap());
cache.put("b", 2, NonZeroUsize::new(1).unwrap());
cache.put("c", 3, NonZeroUsize::new(1).unwrap());
{
let mut iter = cache.iter();
assert_eq!(iter.len(), 3);
assert_opt_eq_tuple(iter.next_back(), ("a", 1));
assert_eq!(iter.len(), 2);
assert_opt_eq_tuple(iter.next_back(), ("b", 2));
assert_eq!(iter.len(), 1);
assert_opt_eq_tuple(iter.next_back(), ("c", 3));
assert_eq!(iter.len(), 0);
assert_eq!(iter.next_back(), None);
}
{
let mut iter = cache.iter_mut();
assert_eq!(iter.len(), 3);
assert_opt_eq_mut_tuple(iter.next_back(), ("a", 1));
assert_eq!(iter.len(), 2);
assert_opt_eq_mut_tuple(iter.next_back(), ("b", 2));
assert_eq!(iter.len(), 1);
assert_opt_eq_mut_tuple(iter.next_back(), ("c", 3));
assert_eq!(iter.len(), 0);
assert_eq!(iter.next_back(), None);
}
}
#[test]
fn test_iter_forwards_and_backwards() {
let mut cache = LruCache::new(NonZeroUsize::new(3).unwrap());
cache.put("a", 1, NonZeroUsize::new(1).unwrap());
cache.put("b", 2, NonZeroUsize::new(1).unwrap());
cache.put("c", 3, NonZeroUsize::new(1).unwrap());
{
let mut iter = cache.iter();
assert_eq!(iter.len(), 3);
assert_opt_eq_tuple(iter.next(), ("c", 3));
assert_eq!(iter.len(), 2);
assert_opt_eq_tuple(iter.next_back(), ("a", 1));
assert_eq!(iter.len(), 1);
assert_opt_eq_tuple(iter.next(), ("b", 2));
assert_eq!(iter.len(), 0);
assert_eq!(iter.next_back(), None);
}
{
let mut iter = cache.iter_mut();
assert_eq!(iter.len(), 3);
assert_opt_eq_mut_tuple(iter.next(), ("c", 3));
assert_eq!(iter.len(), 2);
assert_opt_eq_mut_tuple(iter.next_back(), ("a", 1));
assert_eq!(iter.len(), 1);
assert_opt_eq_mut_tuple(iter.next(), ("b", 2));
assert_eq!(iter.len(), 0);
assert_eq!(iter.next_back(), None);
}
}
#[test]
fn test_iter_multiple_threads() {
let mut pool = Pool::new(1);
let mut cache = LruCache::new(NonZeroUsize::new(3).unwrap());
cache.put("a", 1, NonZeroUsize::new(1).unwrap());
cache.put("b", 2, NonZeroUsize::new(1).unwrap());
cache.put("c", 3, NonZeroUsize::new(1).unwrap());
let mut iter = cache.iter();
assert_eq!(iter.len(), 3);
assert_opt_eq_tuple(iter.next(), ("c", 3));
{
let iter_ref = &mut iter;
pool.scoped(|scoped| {
scoped.execute(move || {
assert_eq!(iter_ref.len(), 2);
assert_opt_eq_tuple(iter_ref.next(), ("b", 2));
});
});
}
assert_eq!(iter.len(), 1);
assert_opt_eq_tuple(iter.next(), ("a", 1));
assert_eq!(iter.len(), 0);
assert_eq!(iter.next(), None);
}
#[test]
fn test_iter_clone() {
let mut cache = LruCache::new(NonZeroUsize::new(3).unwrap());
cache.put("a", 1, NonZeroUsize::new(1).unwrap());
cache.put("b", 2, NonZeroUsize::new(1).unwrap());
let mut iter = cache.iter();
let mut iter_clone = iter.clone();
assert_eq!(iter.len(), 2);
assert_opt_eq_tuple(iter.next(), ("b", 2));
assert_eq!(iter_clone.len(), 2);
assert_opt_eq_tuple(iter_clone.next(), ("b", 2));
assert_eq!(iter.len(), 1);
assert_opt_eq_tuple(iter.next(), ("a", 1));
assert_eq!(iter_clone.len(), 1);
assert_opt_eq_tuple(iter_clone.next(), ("a", 1));
assert_eq!(iter.len(), 0);
assert_eq!(iter.next(), None);
assert_eq!(iter_clone.len(), 0);
assert_eq!(iter_clone.next(), None);
}
#[test]
fn test_into_iter() {
let mut cache = LruCache::new(NonZeroUsize::new(3).unwrap());
cache.put("a", 1, NonZeroUsize::new(1).unwrap());
cache.put("b", 2, NonZeroUsize::new(1).unwrap());
cache.put("c", 3, NonZeroUsize::new(1).unwrap());
let mut iter = cache.into_iter();
assert_eq!(iter.len(), 3);
assert_eq!(iter.next(), Some(("a", 1)));
assert_eq!(iter.len(), 2);
assert_eq!(iter.next(), Some(("b", 2)));
assert_eq!(iter.len(), 1);
assert_eq!(iter.next(), Some(("c", 3)));
assert_eq!(iter.len(), 0);
assert_eq!(iter.next(), None);
}
#[test]
fn test_that_pop_actually_detaches_node() {
let mut cache = LruCache::new(NonZeroUsize::new(5).unwrap());
cache.put("a", 1, NonZeroUsize::new(1).unwrap());
cache.put("b", 2, NonZeroUsize::new(1).unwrap());
cache.put("c", 3, NonZeroUsize::new(1).unwrap());
cache.put("d", 4, NonZeroUsize::new(1).unwrap());
cache.put("e", 5, NonZeroUsize::new(1).unwrap());
assert_eq!(cache.pop(&"c"), Some(3));
cache.put("f", 6, NonZeroUsize::new(1).unwrap());
let mut iter = cache.iter();
assert_opt_eq_tuple(iter.next(), ("f", 6));
assert_opt_eq_tuple(iter.next(), ("e", 5));
assert_opt_eq_tuple(iter.next(), ("d", 4));
assert_opt_eq_tuple(iter.next(), ("b", 2));
assert_opt_eq_tuple(iter.next(), ("a", 1));
assert!(iter.next().is_none());
}
#[test]
fn test_get_with_borrow() {
use alloc::string::String;
let mut cache = LruCache::new(NonZeroUsize::new(2).unwrap());
let key = String::from("apple");
cache.put(key, "red", NonZeroUsize::new(1).unwrap());
assert_opt_eq(cache.get("apple"), "red");
}
#[test]
fn test_get_mut_with_borrow() {
use alloc::string::String;
let mut cache = LruCache::new(NonZeroUsize::new(2).unwrap());
let key = String::from("apple");
cache.put(key, "red", NonZeroUsize::new(1).unwrap());
assert_opt_eq_mut(cache.get_mut("apple"), "red");
}
#[test]
fn test_no_memory_leaks() {
static DROP_COUNT: AtomicUsize = AtomicUsize::new(0);
struct DropCounter;
impl Drop for DropCounter {
fn drop(&mut self) {
DROP_COUNT.fetch_add(1, Ordering::SeqCst);
}
}
let n = 100;
for _ in 0..n {
let mut cache = LruCache::new(NonZeroUsize::new(1).unwrap());
for i in 0..n {
cache.put(i, DropCounter {}, NonZeroUsize::new(1).unwrap());
}
}
assert_eq!(DROP_COUNT.load(Ordering::SeqCst), n * n);
}
#[test]
fn test_no_memory_leaks_with_clear() {
static DROP_COUNT: AtomicUsize = AtomicUsize::new(0);
struct DropCounter;
impl Drop for DropCounter {
fn drop(&mut self) {
DROP_COUNT.fetch_add(1, Ordering::SeqCst);
}
}
let n = 100;
for _ in 0..n {
let mut cache = LruCache::new(NonZeroUsize::new(1).unwrap());
for i in 0..n {
cache.put(i, DropCounter {}, NonZeroUsize::new(1).unwrap());
}
cache.clear();
}
assert_eq!(DROP_COUNT.load(Ordering::SeqCst), n * n);
}
#[test]
fn test_no_memory_leaks_with_resize() {
static DROP_COUNT: AtomicUsize = AtomicUsize::new(0);
struct DropCounter;
impl Drop for DropCounter {
fn drop(&mut self) {
DROP_COUNT.fetch_add(1, Ordering::SeqCst);
}
}
let n = 100;
for _ in 0..n {
let mut cache = LruCache::new(NonZeroUsize::new(1).unwrap());
for i in 0..n {
cache.put(i, DropCounter {}, NonZeroUsize::new(1).unwrap());
}
cache.clear();
}
assert_eq!(DROP_COUNT.load(Ordering::SeqCst), n * n);
}
#[test]
fn test_no_memory_leaks_with_pop() {
static DROP_COUNT: AtomicUsize = AtomicUsize::new(0);
#[derive(Hash, Eq)]
struct KeyDropCounter(usize);
impl PartialEq for KeyDropCounter {
fn eq(&self, other: &Self) -> bool {
self.0.eq(&other.0)
}
}
impl Drop for KeyDropCounter {
fn drop(&mut self) {
DROP_COUNT.fetch_add(1, Ordering::SeqCst);
}
}
let n = 100;
for _ in 0..n {
let mut cache = LruCache::new(NonZeroUsize::new(1).unwrap());
for i in 0..100 {
cache.put(KeyDropCounter(i), i, NonZeroUsize::new(1).unwrap());
cache.pop(&KeyDropCounter(i));
}
}
assert_eq!(DROP_COUNT.load(Ordering::SeqCst), n * n * 2);
}
#[test]
fn test_promote_and_demote() {
let mut cache = LruCache::new(NonZeroUsize::new(5).unwrap());
for i in 0..5 {
cache.push(i, i, NonZeroUsize::new(1).unwrap());
}
cache.promote(&1);
cache.promote(&0);
cache.demote(&3);
cache.demote(&4);
assert_eq!(cache.pop_lru(), Some((4, 4)));
assert_eq!(cache.pop_lru(), Some((3, 3)));
assert_eq!(cache.pop_lru(), Some((2, 2)));
assert_eq!(cache.pop_lru(), Some((1, 1)));
assert_eq!(cache.pop_lru(), Some((0, 0)));
assert_eq!(cache.pop_lru(), None);
}
#[test]
fn test_get_key_value() {
use alloc::string::String;
let mut cache = LruCache::new(NonZeroUsize::new(2).unwrap());
let key = String::from("apple");
cache.put(key, "red", NonZeroUsize::new(1).unwrap());
assert_eq!(
cache.get_key_value("apple"),
Some((&String::from("apple"), &"red"))
);
assert_eq!(cache.get_key_value("banana"), None);
}
#[test]
fn test_get_key_value_mut() {
use alloc::string::String;
let mut cache = LruCache::new(NonZeroUsize::new(2).unwrap());
let key = String::from("apple");
cache.put(key, "red", NonZeroUsize::new(1).unwrap());
let (k, v) = cache.get_key_value_mut("apple").unwrap();
assert_eq!(k, &String::from("apple"));
assert_eq!(v, &mut "red");
*v = "green";
assert_eq!(
cache.get_key_value("apple"),
Some((&String::from("apple"), &"green"))
);
assert_eq!(cache.get_key_value("banana"), None);
}
#[test]
fn test_clone() {
let mut cache = LruCache::new(NonZeroUsize::new(3).unwrap());
cache.put("a", 1, NonZeroUsize::new(1).unwrap());
cache.put("b", 2, NonZeroUsize::new(1).unwrap());
cache.put("c", 3, NonZeroUsize::new(1).unwrap());
let mut cloned = cache.clone();
assert_eq!(cloned.size(), cache.size());
assert_eq!(cloned.len(), cache.len());
assert_eq!(cache.pop_lru(), Some(("a", 1)));
assert_eq!(cloned.pop_lru(), Some(("a", 1)));
assert_eq!(cache.pop_lru(), Some(("b", 2)));
assert_eq!(cloned.pop_lru(), Some(("b", 2)));
assert_eq!(cache.pop_lru(), Some(("c", 3)));
assert_eq!(cloned.pop_lru(), Some(("c", 3)));
assert_eq!(cache.pop_lru(), None);
assert_eq!(cloned.pop_lru(), None);
}
#[test]
fn test_cap() {
let mut cache = LruCache::new(NonZeroUsize::new(10).unwrap());
cache.put(1, 1, NonZeroUsize::new(1).unwrap());
cache.put(2, 2, NonZeroUsize::new(2).unwrap());
cache.put(3, 3, NonZeroUsize::new(3).unwrap());
cache.put(4, 4, NonZeroUsize::new(4).unwrap());
assert_eq!(NonZeroUsize::new(cache.size()).unwrap(), cache.cap());
cache.put(5, 5, NonZeroUsize::new(5).unwrap());
let mut iter = cache.iter();
assert_eq!(iter.len(), 2);
assert_opt_eq_tuple(iter.next(), (5, 5));
assert_opt_eq_tuple(iter.next(), (4, 4));
assert_eq!(cache.size(), 9);
}
#[test]
fn test_push() {
let mut cache = LruCache::new(NonZeroUsize::new(10).unwrap());
cache.push(1, 1, NonZeroUsize::new(5).unwrap());
cache.push(2, 2, NonZeroUsize::new(5).unwrap());
assert_eq!(cache.size(), 10);
assert_eq!(cache.len(), 2);
cache.push(3, 3, NonZeroUsize::new(5).unwrap());
assert_eq!(cache.size(), 10);
assert_eq!(cache.len(), 2);
let mut iter = cache.iter();
assert_eq!(iter.len(), 2);
assert_opt_eq_tuple(iter.next(), (3, 3));
assert_opt_eq_tuple(iter.next(), (2, 2));
}
#[test]
fn test_replace_push() {
let mut cache = LruCache::new(NonZeroUsize::new(10).unwrap());
cache.push(1, 1, NonZeroUsize::new(4).unwrap());
cache.push(2, 2, NonZeroUsize::new(4).unwrap());
assert_eq!(cache.size(), 8);
cache.push(1, 2, NonZeroUsize::new(3).unwrap());
assert_eq!(cache.size(), 7);
assert_eq!(cache.len(), 2);
let mut iter = cache.iter();
assert_opt_eq_tuple(iter.next(), (1, 2)); assert_opt_eq_tuple(iter.next(), (2, 2));
cache.put(2, 3, NonZeroUsize::new(10).unwrap());
assert_eq!(cache.size(), 13);
assert_eq!(cache.len(), 2);
let mut iter = cache.iter();
assert_opt_eq_tuple(iter.next(), (2, 3)); assert_opt_eq_tuple(iter.next(), (1, 2)); }
#[test]
fn test_over_cap() {
let mut cache = LruCache::new(NonZeroUsize::new(10).unwrap());
cache.put(1, 1, NonZeroUsize::new(1).unwrap());
cache.put(2, 2, NonZeroUsize::new(2).unwrap());
cache.put(3, 3, NonZeroUsize::new(3).unwrap());
cache.put(4, 4, NonZeroUsize::new(4).unwrap());
assert_eq!(NonZeroUsize::new(cache.size()).unwrap(), cache.cap());
cache.put(2, 5, NonZeroUsize::new(5).unwrap());
let mut iter = cache.iter();
assert_eq!(iter.len(), 4);
assert_opt_eq_tuple(iter.next(), (2, 5)); assert_opt_eq_tuple(iter.next(), (4, 4)); assert_opt_eq_tuple(iter.next(), (3, 3)); assert_opt_eq_tuple(iter.next(), (1, 1)); assert_eq!(cache.size(), 13);
cache.put(5, 5, NonZeroUsize::new(1).unwrap());
assert_eq!(cache.size(), 10);
let mut iter = cache.iter();
assert_eq!(iter.len(), 3);
assert_opt_eq_tuple(iter.next(), (5, 5)); assert_opt_eq_tuple(iter.next(), (2, 5)); assert_opt_eq_tuple(iter.next(), (4, 4));
cache.put(6, 6, NonZeroUsize::new(6).unwrap());
assert_eq!(cache.size(), 7);
let mut iter = cache.iter();
assert_eq!(iter.len(), 2);
assert_opt_eq_tuple(iter.next(), (6, 6)); assert_opt_eq_tuple(iter.next(), (5, 5)); }
#[test]
fn test_replace_node() {
let mut cache = LruCache::new(NonZeroUsize::new(10).unwrap());
cache.put(1, 1, NonZeroUsize::new(5).unwrap());
let old_value = cache.update_node(&1, |v| (*v + 1, NonZeroUsize::new(2).unwrap()));
assert_opt_eq(old_value.as_ref(), 1);
assert_eq!(cache.len(), 1);
assert_eq!(cache.size(), 2);
assert_opt_eq(cache.peek(&1), 2);
}
#[test]
fn test_overflow_node() {
let mut cache = LruCache::new(NonZeroUsize::new(3).unwrap());
cache.put(1, 1, NonZeroUsize::new(4).unwrap());
assert_eq!(cache.size(), 4);
assert_eq!(cache.len(), 1);
cache.remove_last();
cache.push(1, 1, NonZeroUsize::new(1).unwrap());
assert_eq!(cache.size(), 1);
assert_eq!(cache.len(), 1);
cache.push(2, 2, NonZeroUsize::new(1).unwrap());
cache.push(3, 3, NonZeroUsize::new(1).unwrap());
assert_eq!(cache.size(), 3);
assert_eq!(cache.len(), 3);
cache.push(4, 4, NonZeroUsize::new(4).unwrap());
assert_eq!(cache.size(), 4);
assert_eq!(cache.len(), 1);
}
}
fn _test_lifetimes() {}