#![no_std]
#![cfg_attr(feature = "nightly", feature(negative_impls, auto_traits))]
#[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::ptr;
use core::usize;
#[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;
#[doc(hidden)]
pub 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> {
fn eq(&self, other: &KeyRef<K>) -> bool {
unsafe { (*self.k).eq(&*other.k) }
}
}
impl<K: Eq> Eq for KeyRef<K> {}
#[cfg(feature = "nightly")]
#[doc(hidden)]
pub auto trait NotKeyRef {}
#[cfg(feature = "nightly")]
impl<K> !NotKeyRef for KeyRef<K> {}
#[cfg(feature = "nightly")]
impl<K, D> Borrow<D> for KeyRef<K>
where
K: Borrow<D>,
D: NotKeyRef + ?Sized,
{
fn borrow(&self) -> &D {
unsafe { &*self.k }.borrow()
}
}
#[cfg(not(feature = "nightly"))]
impl<K> Borrow<K> for KeyRef<K> {
fn borrow(&self) -> &K {
unsafe { &*self.k }
}
}
struct LruEntry<K, V> {
key: mem::MaybeUninit<K>,
val: mem::MaybeUninit<V>,
prev: *mut LruEntry<K, V>,
next: *mut LruEntry<K, V>,
}
impl<K, V> LruEntry<K, V> {
fn new(key: K, val: V) -> Self {
LruEntry {
key: mem::MaybeUninit::new(key),
val: mem::MaybeUninit::new(val),
prev: ptr::null_mut(),
next: ptr::null_mut(),
}
}
fn new_sigil() -> Self {
LruEntry {
key: mem::MaybeUninit::uninit(),
val: mem::MaybeUninit::uninit(),
prev: ptr::null_mut(),
next: ptr::null_mut(),
}
}
}
#[cfg(feature = "hashbrown")]
pub type DefaultHasher = hashbrown::hash_map::DefaultHashBuilder;
#[cfg(not(feature = "hashbrown"))]
pub type DefaultHasher = std::collections::hash_map::RandomState;
pub struct LruCache<K, V, S = DefaultHasher> {
map: HashMap<KeyRef<K>, Box<LruEntry<K, V>>, S>,
cap: usize,
head: *mut LruEntry<K, V>,
tail: *mut LruEntry<K, V>,
}
impl<K: Hash + Eq, V> LruCache<K, V> {
pub fn new(cap: usize) -> LruCache<K, V> {
LruCache::construct(cap, HashMap::with_capacity(cap))
}
pub fn unbounded() -> LruCache<K, V> {
LruCache::construct(usize::MAX, HashMap::default())
}
}
impl<K: Hash + Eq, V, S: BuildHasher> LruCache<K, V, S> {
pub fn with_hasher(cap: usize, hash_builder: S) -> LruCache<K, V, S> {
LruCache::construct(cap, HashMap::with_capacity_and_hasher(cap, hash_builder))
}
pub fn unbounded_with_hasher(hash_builder: S) -> LruCache<K, V, S> {
LruCache::construct(usize::MAX, HashMap::with_hasher(hash_builder))
}
fn construct(cap: usize, map: HashMap<KeyRef<K>, Box<LruEntry<K, V>>, S>) -> LruCache<K, V, S> {
let cache = LruCache {
map,
cap,
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, mut v: V) -> Option<V> {
let node_ptr = self.map.get_mut(&KeyRef { k: &k }).map(|node| {
let node_ptr: *mut LruEntry<K, V> = &mut **node;
node_ptr
});
match node_ptr {
Some(node_ptr) => {
unsafe { mem::swap(&mut v, &mut (*(*node_ptr).val.as_mut_ptr()) as &mut V) }
self.detach(node_ptr);
self.attach(node_ptr);
Some(v)
}
None => {
if self.cap() == 0 {
return None;
}
let mut node = if self.len() == self.cap() {
let old_key = KeyRef {
k: unsafe { &(*(*(*self.tail).prev).key.as_ptr()) },
};
let mut old_node = self.map.remove(&old_key).unwrap();
unsafe {
ptr::drop_in_place(old_node.key.as_mut_ptr());
ptr::drop_in_place(old_node.val.as_mut_ptr());
}
old_node.key = mem::MaybeUninit::new(k);
old_node.val = mem::MaybeUninit::new(v);
let node_ptr: *mut LruEntry<K, V> = &mut *old_node;
self.detach(node_ptr);
old_node
} else {
Box::new(LruEntry::new(k, v))
};
let node_ptr: *mut LruEntry<K, V> = &mut *node;
self.attach(node_ptr);
let keyref = unsafe { (*node_ptr).key.as_ptr() };
self.map.insert(KeyRef { k: keyref }, node);
None
}
}
}
pub fn get<'a, Q>(&'a mut self, k: &Q) -> Option<&'a V>
where
KeyRef<K>: Borrow<Q>,
Q: Hash + Eq + ?Sized,
{
if let Some(node) = self.map.get_mut(k) {
let node_ptr: *mut LruEntry<K, V> = &mut **node;
self.detach(node_ptr);
self.attach(node_ptr);
Some(unsafe { &(*(*node_ptr).val.as_ptr()) as &V })
} else {
None
}
}
pub fn get_mut<'a, Q>(&'a mut self, k: &Q) -> Option<&'a mut V>
where
KeyRef<K>: Borrow<Q>,
Q: Hash + Eq + ?Sized,
{
if let Some(node) = self.map.get_mut(k) {
let node_ptr: *mut LruEntry<K, V> = &mut **node;
self.detach(node_ptr);
self.attach(node_ptr);
Some(unsafe { &mut (*(*node_ptr).val.as_mut_ptr()) as &mut V })
} else {
None
}
}
pub fn peek<'a, Q>(&'a self, k: &Q) -> Option<&'a V>
where
KeyRef<K>: Borrow<Q>,
Q: Hash + Eq + ?Sized,
{
match self.map.get(k) {
None => None,
Some(node) => Some(unsafe { &(*(*node).val.as_ptr()) as &V }),
}
}
pub fn peek_mut<'a, Q>(&'a mut self, k: &Q) -> Option<&'a mut V>
where
KeyRef<K>: Borrow<Q>,
Q: Hash + Eq + ?Sized,
{
match self.map.get_mut(k) {
None => None,
Some(node) => Some(unsafe { &mut (*(*node).val.as_mut_ptr()) as &mut V }),
}
}
pub fn peek_lru<'a>(&'a self) -> Option<(&'a K, &'a V)> {
if self.len() == 0 {
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 contains<Q>(&self, k: &Q) -> bool
where
KeyRef<K>: Borrow<Q>,
Q: Hash + Eq + ?Sized,
{
self.map.contains_key(k)
}
pub fn pop<Q>(&mut self, k: &Q) -> Option<V>
where
KeyRef<K>: Borrow<Q>,
Q: Hash + Eq + ?Sized,
{
match self.map.remove(&k) {
None => None,
Some(mut old_node) => {
unsafe {
ptr::drop_in_place(old_node.key.as_mut_ptr());
}
let node_ptr: *mut LruEntry<K, V> = &mut *old_node;
self.detach(node_ptr);
unsafe { Some(old_node.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 len(&self) -> usize {
self.map.len()
}
pub fn is_empty(&self) -> bool {
self.map.len() == 0
}
pub fn cap(&self) -> usize {
self.cap
}
pub fn resize(&mut self, cap: usize) {
if cap == self.cap {
return;
}
while self.map.len() > cap {
self.pop_lru();
}
self.map.shrink_to_fit();
self.cap = cap;
}
pub fn clear(&mut self) {
loop {
match self.pop_lru() {
Some(_) => (),
None => break,
}
}
}
pub fn iter<'a>(&'a self) -> Iter<'a, K, V> {
Iter {
len: self.len(),
ptr: unsafe { (*self.head).next },
end: unsafe { (*self.tail).prev },
phantom: PhantomData,
}
}
pub fn iter_mut<'a>(&'a mut self) -> IterMut<'a, K, V> {
IterMut {
len: self.len(),
ptr: unsafe { (*self.head).next },
end: unsafe { (*self.tail).prev },
phantom: PhantomData,
}
}
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 mut old_node = self.map.remove(&old_key).unwrap();
let node_ptr: *mut LruEntry<K, V> = &mut *old_node;
self.detach(node_ptr);
Some(old_node)
} 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;
}
}
}
impl<K, V, S> Drop for LruCache<K, V, S> {
fn drop(&mut self) {
self.map.values_mut().for_each(|e| unsafe {
ptr::drop_in_place(e.key.as_mut_ptr());
ptr::drop_in_place(e.val.as_mut_ptr());
});
unsafe {
let _head = *Box::from_raw(self.head);
let _tail = *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> fmt::Debug for LruCache<K, V> {
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> {}
#[cfg(test)]
mod tests {
use super::LruCache;
use core::fmt::Debug;
use scoped_threadpool::Pool;
use std::sync::atomic::{AtomicUsize, Ordering};
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, ());
}
assert_eq!(cache.len(), 13370);
}
#[test]
#[cfg(feature = "hashbrown")]
fn test_with_hasher() {
use hashbrown::hash_map::DefaultHashBuilder;
let s = DefaultHashBuilder::default();
let mut cache = LruCache::with_hasher(16, s);
for i in 0..13370 {
cache.put(i, ());
}
assert_eq!(cache.len(), 16);
}
#[test]
fn test_put_and_get() {
let mut cache = LruCache::new(2);
assert!(cache.is_empty());
assert_eq!(cache.put("apple", "red"), None);
assert_eq!(cache.put("banana", "yellow"), None);
assert_eq!(cache.cap(), 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_mut() {
let mut cache = LruCache::new(2);
cache.put("apple", "red");
cache.put("banana", "yellow");
assert_eq!(cache.cap(), 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(2);
cache.put("apple", 1);
cache.put("banana", 3);
{
let v = cache.get_mut(&"apple").unwrap();
*v = 4;
}
assert_eq!(cache.cap(), 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(1);
assert_eq!(cache.put("apple", "red"), None);
assert_eq!(cache.put("apple", "green"), 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(2);
assert_eq!(cache.put("apple", "red"), None);
assert_eq!(cache.put("banana", "yellow"), None);
assert_eq!(cache.put("pear", "green"), 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"), None);
assert_eq!(cache.put("tomato", "red"), 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(2);
cache.put("apple", "red");
cache.put("banana", "yellow");
assert_opt_eq(cache.peek(&"banana"), "yellow");
assert_opt_eq(cache.peek(&"apple"), "red");
cache.put("pear", "green");
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(2);
cache.put("apple", "red");
cache.put("banana", "yellow");
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");
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(2);
assert!(cache.peek_lru().is_none());
cache.put("apple", "red");
cache.put("banana", "yellow");
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_contains() {
let mut cache = LruCache::new(2);
cache.put("apple", "red");
cache.put("banana", "yellow");
cache.put("pear", "green");
assert!(!cache.contains(&"apple"));
assert!(cache.contains(&"banana"));
assert!(cache.contains(&"pear"));
}
#[test]
fn test_pop() {
let mut cache = LruCache::new(2);
cache.put("apple", "red");
cache.put("banana", "yellow");
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_lru() {
let mut cache = LruCache::new(200);
for i in 0..75 {
cache.put(i, "A");
}
for i in 0..75 {
cache.put(i + 100, "B");
}
for i in 0..75 {
cache.put(i + 200, "C");
}
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_clear() {
let mut cache = LruCache::new(2);
cache.put("apple", "red");
cache.put("banana", "yellow");
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(2);
cache.put(1, "a");
cache.put(2, "b");
cache.resize(4);
cache.put(3, "c");
cache.put(4, "d");
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(4);
cache.put(1, "a");
cache.put(2, "b");
cache.put(3, "c");
cache.put(4, "d");
cache.resize(2);
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(4);
cache.put(1, "a");
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(4);
cache.put(1, "a");
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(3);
cache.put("a", 1);
cache.put("b", 2);
cache.put("c", 3);
{
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(3);
cache.put("a", 1);
cache.put("b", 2);
cache.put("c", 3);
{
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(3);
cache.put("a", 1);
cache.put("b", 2);
cache.put("c", 3);
{
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(3);
cache.put("a", 1);
cache.put("b", 2);
cache.put("c", 3);
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(3);
cache.put("a", 1);
cache.put("b", 2);
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_that_pop_actually_detaches_node() {
let mut cache = LruCache::new(5);
cache.put("a", 1);
cache.put("b", 2);
cache.put("c", 3);
cache.put("d", 4);
cache.put("e", 5);
assert_eq!(cache.pop(&"c"), Some(3));
cache.put("f", 6);
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]
#[cfg(feature = "nightly")]
fn test_get_with_borrow() {
use alloc::string::String;
let mut cache = LruCache::new(2);
let key = String::from("apple");
cache.put(key, "red");
assert_opt_eq(cache.get("apple"), "red");
}
#[test]
#[cfg(feature = "nightly")]
fn test_get_mut_with_borrow() {
use alloc::string::String;
let mut cache = LruCache::new(2);
let key = String::from("apple");
cache.put(key, "red");
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(1);
for i in 0..n {
cache.put(i, DropCounter {});
}
}
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(1);
for i in 0..n {
cache.put(i, DropCounter {});
}
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(1);
for i in 0..n {
cache.put(i, DropCounter {});
}
cache.resize(0);
}
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(1);
for i in 0..100 {
cache.put(KeyDropCounter(i), i);
cache.pop(&KeyDropCounter(i));
}
}
assert_eq!(DROP_COUNT.load(Ordering::SeqCst), n * n * 2);
}
#[test]
fn test_zero_cap_no_crash() {
let mut cache = LruCache::new(0);
cache.put("reizeiin", "tohka");
}
}