blob: 9e11ac40ec8062ca9aafe67746e9cf3195f8dae9 [file] [log] [blame] [edit]
// MIT License
// Copyright (c) 2016 Jerome Froelich
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//! An implementation of a LRU cache. The cache supports `get`, `get_mut`, `put`,
//! and `pop` operations, all of which are O(1). This crate was heavily influenced
//! by the [LRU Cache implementation in an earlier version of Rust's std::collections crate](https://doc.rust-lang.org/0.12.0/std/collections/lru_cache/struct.LruCache.html).
//!
//! ## Example
//!
//! ```rust
//! extern crate lru;
//!
//! use lru::LruCache;
//! use std::num::NonZeroUsize;
//!
//! fn main() {
//! let mut cache = LruCache::new(NonZeroUsize::new(2).unwrap());
//! cache.put("apple", 3);
//! cache.put("banana", 2);
//!
//! assert_eq!(*cache.get(&"apple").unwrap(), 3);
//! assert_eq!(*cache.get(&"banana").unwrap(), 2);
//! assert!(cache.get(&"pear").is_none());
//!
//! assert_eq!(cache.put("banana", 4), Some(2));
//! assert_eq!(cache.put("pear", 5), None);
//!
//! assert_eq!(*cache.get(&"pear").unwrap(), 5);
//! assert_eq!(*cache.get(&"banana").unwrap(), 4);
//! assert!(cache.get(&"apple").is_none());
//!
//! {
//! let v = cache.get_mut(&"banana").unwrap();
//! *v = 6;
//! }
//!
//! assert_eq!(*cache.get(&"banana").unwrap(), 6);
//! }
//! ```
#![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};
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;
// Struct used to hold a reference to a key
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> {}
// This type exists to allow a "blanket" Borrow impl for KeyRef without conflicting with the
// stdlib blanket impl
#[repr(transparent)]
struct KeyWrapper<K: ?Sized>(K);
impl<K: ?Sized> KeyWrapper<K> {
fn from_ref(key: &K) -> &Self {
// safety: KeyWrapper is transparent, so casting the ref like this is allowable
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> {
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 used to hold a key value pair. Also contains references to previous and next entries
// so we can maintain the entries in a linked list ordered by their use.
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;
/// An LRU Cache
pub struct LruCache<K, V, S = DefaultHasher> {
map: HashMap<KeyRef<K>, NonNull<LruEntry<K, V>>, S>,
cap: NonZeroUsize,
// head and tail are sigil nodes to facilitate inserting entries
head: *mut LruEntry<K, V>,
tail: *mut LruEntry<K, V>,
}
impl<K: Hash + Eq, V> LruCache<K, V> {
/// Creates a new LRU Cache that holds at most `cap` items.
///
/// # Example
///
/// ```
/// use lru::LruCache;
/// use std::num::NonZeroUsize;
/// let mut cache: LruCache<isize, &str> = LruCache::new(NonZeroUsize::new(10).unwrap());
/// ```
pub fn new(cap: NonZeroUsize) -> LruCache<K, V> {
LruCache::construct(cap, HashMap::with_capacity(cap.get()))
}
/// Creates a new LRU Cache that never automatically evicts items.
///
/// # Example
///
/// ```
/// use lru::LruCache;
/// use std::num::NonZeroUsize;
/// let mut cache: LruCache<isize, &str> = LruCache::unbounded();
/// ```
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> {
/// Creates a new LRU Cache that holds at most `cap` items and
/// uses the provided hash builder to hash keys.
///
/// # Example
///
/// ```
/// use lru::{LruCache, DefaultHasher};
/// use std::num::NonZeroUsize;
///
/// let s = DefaultHasher::default();
/// let mut cache: LruCache<isize, &str> = LruCache::with_hasher(NonZeroUsize::new(10).unwrap(), s);
/// ```
pub fn with_hasher(cap: NonZeroUsize, hash_builder: S) -> LruCache<K, V, S> {
LruCache::construct(
cap,
HashMap::with_capacity_and_hasher(cap.into(), hash_builder),
)
}
/// Creates a new LRU Cache that never automatically evicts items and
/// uses the provided hash builder to hash keys.
///
/// # Example
///
/// ```
/// use lru::{LruCache, DefaultHasher};
///
/// let s = DefaultHasher::default();
/// let mut cache: LruCache<isize, &str> = LruCache::unbounded_with_hasher(s);
/// ```
pub fn unbounded_with_hasher(hash_builder: S) -> LruCache<K, V, S> {
LruCache::construct(
NonZeroUsize::new(usize::MAX).unwrap(),
HashMap::with_hasher(hash_builder),
)
}
/// Creates a new LRU Cache with the given capacity.
fn construct(
cap: NonZeroUsize,
map: HashMap<KeyRef<K>, NonNull<LruEntry<K, V>>, S>,
) -> LruCache<K, V, S> {
// NB: The compiler warns that cache does not need to be marked as mutable if we
// declare it as such since we only mutate it inside the unsafe block.
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
}
/// Puts a key-value pair into cache. If the key already exists in the cache, then it updates
/// the key's value and returns the old value. Otherwise, `None` is returned.
///
/// # Example
///
/// ```
/// use lru::LruCache;
/// use std::num::NonZeroUsize;
/// let mut cache = LruCache::new(NonZeroUsize::new(2).unwrap());
///
/// assert_eq!(None, cache.put(1, "a"));
/// assert_eq!(None, cache.put(2, "b"));
/// assert_eq!(Some("b"), cache.put(2, "beta"));
///
/// assert_eq!(cache.get(&1), Some(&"a"));
/// assert_eq!(cache.get(&2), Some(&"beta"));
/// ```
pub fn put(&mut self, k: K, v: V) -> Option<V> {
self.capturing_put(k, v, false).map(|(_, v)| v)
}
/// Pushes a key-value pair into the cache. If an entry with key `k` already exists in
/// the cache or another cache entry is removed (due to the lru's capacity),
/// then it returns the old entry's key-value pair. Otherwise, returns `None`.
///
/// # Example
///
/// ```
/// use lru::LruCache;
/// use std::num::NonZeroUsize;
/// let mut cache = LruCache::new(NonZeroUsize::new(2).unwrap());
///
/// assert_eq!(None, cache.push(1, "a"));
/// assert_eq!(None, cache.push(2, "b"));
///
/// // This push call returns (2, "b") because that was previously 2's entry in the cache.
/// assert_eq!(Some((2, "b")), cache.push(2, "beta"));
///
/// // This push call returns (1, "a") because the cache is at capacity and 1's entry was the lru entry.
/// assert_eq!(Some((1, "a")), cache.push(3, "alpha"));
///
/// assert_eq!(cache.get(&1), None);
/// assert_eq!(cache.get(&2), Some(&"beta"));
/// assert_eq!(cache.get(&3), Some(&"alpha"));
/// ```
pub fn push(&mut self, k: K, v: V) -> Option<(K, V)> {
self.capturing_put(k, v, true)
}
// Used internally by `put` and `push` to add a new entry to the lru.
// Takes ownership of and returns entries replaced due to the cache's capacity
// when `capture` is true.
fn capturing_put(&mut self, k: K, mut v: V, capture: bool) -> Option<(K, V)> {
let node_ref = self.map.get_mut(&KeyRef { k: &k });
match node_ref {
Some(node_ref) => {
// if the key is already in the cache just update its value and move it to the
// front of the list
let node_ptr: *mut LruEntry<K, V> = node_ref.as_ptr();
// gets a reference to the node to perform a swap and drops it right after
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);
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)
}
}
}
// Used internally to swap out a node if the cache is full or to create a new node if space
// is available. Shared between `put`, `push`, `get_or_insert`, and `get_or_insert_mut`.
#[allow(clippy::type_complexity)]
fn replace_or_create_node(&mut self, k: K, v: V) -> (Option<(K, V)>, NonNull<LruEntry<K, V>>) {
if self.len() == self.cap().get() {
// if the cache is full, remove the last entry so we can use it for the new key
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();
// read out the node's old key and value and then replace it
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.detach(node_ptr);
(Some(replaced), old_node)
} else {
// if the cache is not full allocate a new LruEntry
// Safety: We allocate, turn into raw, and get NonNull all in one step.
(None, unsafe {
NonNull::new_unchecked(Box::into_raw(Box::new(LruEntry::new(k, v))))
})
}
}
/// Returns a reference to the value of the key in the cache or `None` if it is not
/// present in the cache. Moves the key to the head of the LRU list if it exists.
///
/// # Example
///
/// ```
/// use lru::LruCache;
/// use std::num::NonZeroUsize;
/// let mut cache = LruCache::new(NonZeroUsize::new(2).unwrap());
///
/// cache.put(1, "a");
/// cache.put(2, "b");
/// cache.put(2, "c");
/// cache.put(3, "d");
///
/// assert_eq!(cache.get(&1), None);
/// assert_eq!(cache.get(&2), Some(&"c"));
/// assert_eq!(cache.get(&3), Some(&"d"));
/// ```
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
}
}
/// Returns a mutable reference to the value of the key in the cache or `None` if it
/// is not present in the cache. Moves the key to the head of the LRU list if it exists.
///
/// # Example
///
/// ```
/// use lru::LruCache;
/// use std::num::NonZeroUsize;
/// let mut cache = LruCache::new(NonZeroUsize::new(2).unwrap());
///
/// cache.put("apple", 8);
/// cache.put("banana", 4);
/// cache.put("banana", 6);
/// cache.put("pear", 2);
///
/// assert_eq!(cache.get_mut(&"apple"), None);
/// assert_eq!(cache.get_mut(&"banana"), Some(&mut 6));
/// assert_eq!(cache.get_mut(&"pear"), Some(&mut 2));
/// ```
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
}
}
/// Returns a reference to the value of the key in the cache if it is
/// present in the cache and moves the key to the head of the LRU list.
/// If the key does not exist the provided `FnOnce` is used to populate
/// the list and a reference is returned.
///
/// # Example
///
/// ```
/// use lru::LruCache;
/// use std::num::NonZeroUsize;
/// let mut cache = LruCache::new(NonZeroUsize::new(2).unwrap());
///
/// cache.put(1, "a");
/// cache.put(2, "b");
/// cache.put(2, "c");
/// cache.put(3, "d");
///
/// assert_eq!(cache.get_or_insert(2, ||"a"), &"c");
/// assert_eq!(cache.get_or_insert(3, ||"a"), &"d");
/// assert_eq!(cache.get_or_insert(1, ||"a"), &"a");
/// assert_eq!(cache.get_or_insert(1, ||"b"), &"a");
/// ```
pub fn get_or_insert<'a, F>(&'a mut self, k: K, f: F) -> &'a V
where
F: FnOnce() -> V,
{
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 = f();
let (_, node) = self.replace_or_create_node(k, v);
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() }
}
}
/// Returns a reference to the value of the key in the cache if it is
/// present in the cache and moves the key to the head of the LRU list.
/// If the key does not exist the provided `FnOnce` is used to populate
/// the list and a reference is returned. If `FnOnce` returns `Err`,
/// returns the `Err`.
///
/// # Example
///
/// ```
/// use lru::LruCache;
/// use std::num::NonZeroUsize;
/// let mut cache = LruCache::new(NonZeroUsize::new(2).unwrap());
///
/// cache.put(1, "a");
/// cache.put(2, "b");
/// cache.put(2, "c");
/// cache.put(3, "d");
///
/// let f = ||->Result<&str, String> {Err("failed".to_owned())};
/// let a = ||->Result<&str, String> {Ok("a")};
/// let b = ||->Result<&str, String> {Ok("b")};
/// assert_eq!(cache.try_get_or_insert(2, a), Ok(&"c"));
/// assert_eq!(cache.try_get_or_insert(3, a), Ok(&"d"));
/// assert_eq!(cache.try_get_or_insert(4, f), Err("failed".to_owned()));
/// assert_eq!(cache.try_get_or_insert(5, b), Ok(&"b"));
/// assert_eq!(cache.try_get_or_insert(5, a), Ok(&"b"));
/// ```
pub fn try_get_or_insert<F, E>(&mut self, k: K, f: F) -> Result<&V, E>
where
F: FnOnce() -> Result<V, 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 = f()?;
let (_, node) = self.replace_or_create_node(k, v);
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() })
}
}
/// Returns a mutable reference to the value of the key in the cache if it is
/// present in the cache and moves the key to the head of the LRU list.
/// If the key does not exist the provided `FnOnce` is used to populate
/// the list and a mutable reference is returned.
///
/// # Example
///
/// ```
/// use lru::LruCache;
/// use std::num::NonZeroUsize;
/// let mut cache = LruCache::new(NonZeroUsize::new(2).unwrap());
///
/// cache.put(1, "a");
/// cache.put(2, "b");
///
/// let v = cache.get_or_insert_mut(2, ||"c");
/// assert_eq!(v, &"b");
/// *v = "d";
/// assert_eq!(cache.get_or_insert_mut(2, ||"e"), &mut "d");
/// assert_eq!(cache.get_or_insert_mut(3, ||"f"), &mut "f");
/// assert_eq!(cache.get_or_insert_mut(3, ||"e"), &mut "f");
/// ```
pub fn get_or_insert_mut<'a, F>(&'a mut self, k: K, f: F) -> &'a mut V
where
F: FnOnce() -> V,
{
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 = f();
let (_, node) = self.replace_or_create_node(k, v);
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() }
}
}
/// Returns a mutable reference to the value of the key in the cache if it is
/// present in the cache and moves the key to the head of the LRU list.
/// If the key does not exist the provided `FnOnce` is used to populate
/// the list and a mutable reference is returned. If `FnOnce` returns `Err`,
/// returns the `Err`.
///
/// # Example
///
/// ```
/// use lru::LruCache;
/// use std::num::NonZeroUsize;
/// let mut cache = LruCache::new(NonZeroUsize::new(2).unwrap());
///
/// cache.put(1, "a");
/// cache.put(2, "b");
/// cache.put(2, "c");
///
/// let f = ||->Result<&str, String> {Err("failed".to_owned())};
/// let a = ||->Result<&str, String> {Ok("a")};
/// let b = ||->Result<&str, String> {Ok("b")};
/// 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".to_owned()));
/// 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"));
/// ```
pub fn try_get_or_insert_mut<'a, F, E>(&'a mut self, k: K, f: F) -> Result<&'a mut V, E>
where
F: FnOnce() -> Result<V, 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 = f()?;
let (_, node) = self.replace_or_create_node(k, v);
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()) }
}
}
/// Returns a reference to the value corresponding to the key in the cache or `None` if it is
/// not present in the cache. Unlike `get`, `peek` does not update the LRU list so the key's
/// position will be unchanged.
///
/// # Example
///
/// ```
/// use lru::LruCache;
/// use std::num::NonZeroUsize;
/// let mut cache = LruCache::new(NonZeroUsize::new(2).unwrap());
///
/// cache.put(1, "a");
/// cache.put(2, "b");
///
/// assert_eq!(cache.peek(&1), Some(&"a"));
/// assert_eq!(cache.peek(&2), Some(&"b"));
/// ```
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() })
}
/// Returns a mutable reference to the value corresponding to the key in the cache or `None`
/// if it is not present in the cache. Unlike `get_mut`, `peek_mut` does not update the LRU
/// list so the key's position will be unchanged.
///
/// # Example
///
/// ```
/// use lru::LruCache;
/// use std::num::NonZeroUsize;
/// let mut cache = LruCache::new(NonZeroUsize::new(2).unwrap());
///
/// cache.put(1, "a");
/// cache.put(2, "b");
///
/// assert_eq!(cache.peek_mut(&1), Some(&mut "a"));
/// assert_eq!(cache.peek_mut(&2), Some(&mut "b"));
/// ```
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() }),
}
}
/// Returns the value corresponding to the least recently used item or `None` if the
/// cache is empty. Like `peek`, `peek_lru` does not update the LRU list so the item's
/// position will be unchanged.
///
/// # Example
///
/// ```
/// use lru::LruCache;
/// use std::num::NonZeroUsize;
/// let mut cache = LruCache::new(NonZeroUsize::new(2).unwrap());
///
/// cache.put(1, "a");
/// cache.put(2, "b");
///
/// assert_eq!(cache.peek_lru(), Some((&1, &"a")));
/// ```
pub fn peek_lru<'a>(&'a self) -> Option<(&'a K, &'a 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))
}
/// Returns a bool indicating whether the given key is in the cache. Does not update the
/// LRU list.
///
/// # Example
///
/// ```
/// use lru::LruCache;
/// use std::num::NonZeroUsize;
/// let mut cache = LruCache::new(NonZeroUsize::new(2).unwrap());
///
/// cache.put(1, "a");
/// cache.put(2, "b");
/// cache.put(3, "c");
///
/// assert!(!cache.contains(&1));
/// assert!(cache.contains(&2));
/// assert!(cache.contains(&3));
/// ```
pub fn contains<Q>(&self, k: &Q) -> bool
where
K: Borrow<Q>,
Q: Hash + Eq + ?Sized,
{
self.map.contains_key(KeyWrapper::from_ref(k))
}
/// Removes and returns the value corresponding to the key from the cache or
/// `None` if it does not exist.
///
/// # Example
///
/// ```
/// use lru::LruCache;
/// use std::num::NonZeroUsize;
/// let mut cache = LruCache::new(NonZeroUsize::new(2).unwrap());
///
/// cache.put(2, "a");
///
/// assert_eq!(cache.pop(&1), None);
/// assert_eq!(cache.pop(&2), Some("a"));
/// assert_eq!(cache.pop(&2), None);
/// assert_eq!(cache.len(), 0);
/// ```
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());
old_node
};
self.detach(&mut old_node);
let LruEntry { key: _, val, .. } = old_node;
unsafe { Some(val.assume_init()) }
}
}
}
/// Removes and returns the key and the value corresponding to the key from the cache or
/// `None` if it does not exist.
///
/// # Example
///
/// ```
/// use lru::LruCache;
/// use std::num::NonZeroUsize;
/// let mut cache = LruCache::new(NonZeroUsize::new(2).unwrap());
///
/// cache.put(1, "a");
/// cache.put(2, "a");
///
/// assert_eq!(cache.pop(&1), Some("a"));
/// assert_eq!(cache.pop_entry(&2), Some((2, "a")));
/// assert_eq!(cache.pop(&1), None);
/// assert_eq!(cache.pop_entry(&2), None);
/// assert_eq!(cache.len(), 0);
/// ```
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())) }
}
}
}
/// Removes and returns the key and value corresponding to the least recently
/// used item or `None` if the cache is empty.
///
/// # Example
///
/// ```
/// use lru::LruCache;
/// use std::num::NonZeroUsize;
/// let mut cache = LruCache::new(NonZeroUsize::new(2).unwrap());
///
/// cache.put(2, "a");
/// cache.put(3, "b");
/// cache.put(4, "c");
/// cache.get(&3);
///
/// assert_eq!(cache.pop_lru(), Some((4, "c")));
/// assert_eq!(cache.pop_lru(), Some((3, "b")));
/// assert_eq!(cache.pop_lru(), None);
/// assert_eq!(cache.len(), 0);
/// ```
pub fn pop_lru(&mut self) -> Option<(K, V)> {
let node = self.remove_last()?;
// N.B.: Can't destructure directly because of https://github.com/rust-lang/rust/issues/28536
let node = *node;
let LruEntry { key, val, .. } = node;
unsafe { Some((key.assume_init(), val.assume_init())) }
}
/// Marks the key as the most recently used one.
///
/// # Example
///
/// ```
/// use lru::LruCache;
/// use std::num::NonZeroUsize;
/// let mut cache = LruCache::new(NonZeroUsize::new(3).unwrap());
///
/// cache.put(1, "a");
/// cache.put(2, "b");
/// cache.put(3, "c");
/// cache.get(&1);
/// cache.get(&2);
///
/// // If we do `pop_lru` now, we would pop 3.
/// // assert_eq!(cache.pop_lru(), Some((3, "c")));
///
/// // By promoting 3, we make sure it isn't popped.
/// cache.promote(&3);
/// assert_eq!(cache.pop_lru(), Some((1, "a")));
/// ```
pub fn promote<'a, Q>(&'a 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);
}
}
/// Marks the key as the least recently used one.
///
/// # Example
///
/// ```
/// use lru::LruCache;
/// use std::num::NonZeroUsize;
/// let mut cache = LruCache::new(NonZeroUsize::new(3).unwrap());
///
/// cache.put(1, "a");
/// cache.put(2, "b");
/// cache.put(3, "c");
/// cache.get(&1);
/// cache.get(&2);
///
/// // If we do `pop_lru` now, we would pop 3.
/// // assert_eq!(cache.pop_lru(), Some((3, "c")));
///
/// // By demoting 1 and 2, we make sure those are popped first.
/// cache.demote(&2);
/// cache.demote(&1);
/// assert_eq!(cache.pop_lru(), Some((1, "a")));
/// assert_eq!(cache.pop_lru(), Some((2, "b")));
/// ```
pub fn demote<'a, Q>(&'a 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);
}
}
/// Returns the number of key-value pairs that are currently in the the cache.
///
/// # Example
///
/// ```
/// use lru::LruCache;
/// use std::num::NonZeroUsize;
/// let mut cache = LruCache::new(NonZeroUsize::new(2).unwrap());
/// assert_eq!(cache.len(), 0);
///
/// cache.put(1, "a");
/// assert_eq!(cache.len(), 1);
///
/// cache.put(2, "b");
/// assert_eq!(cache.len(), 2);
///
/// cache.put(3, "c");
/// assert_eq!(cache.len(), 2);
/// ```
pub fn len(&self) -> usize {
self.map.len()
}
/// Returns a bool indicating whether the cache is empty or not.
///
/// # Example
///
/// ```
/// use lru::LruCache;
/// use std::num::NonZeroUsize;
/// let mut cache = LruCache::new(NonZeroUsize::new(2).unwrap());
/// assert!(cache.is_empty());
///
/// cache.put(1, "a");
/// assert!(!cache.is_empty());
/// ```
pub fn is_empty(&self) -> bool {
self.map.len() == 0
}
/// Returns the maximum number of key-value pairs the cache can hold.
///
/// # Example
///
/// ```
/// use lru::LruCache;
/// use std::num::NonZeroUsize;
/// let mut cache: LruCache<isize, &str> = LruCache::new(NonZeroUsize::new(2).unwrap());
/// assert_eq!(cache.cap().get(), 2);
/// ```
pub fn cap(&self) -> NonZeroUsize {
self.cap
}
/// Resizes the cache. If the new capacity is smaller than the size of the current
/// cache any entries past the new capacity are discarded.
///
/// # Example
///
/// ```
/// use lru::LruCache;
/// use std::num::NonZeroUsize;
/// let mut cache: LruCache<isize, &str> = LruCache::new(NonZeroUsize::new(2).unwrap());
///
/// cache.put(1, "a");
/// cache.put(2, "b");
/// cache.resize(NonZeroUsize::new(4).unwrap());
/// 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"));
/// ```
pub fn resize(&mut self, cap: NonZeroUsize) {
// return early if capacity doesn't change
if cap == self.cap {
return;
}
while self.map.len() > cap.get() {
self.pop_lru();
}
self.map.shrink_to_fit();
self.cap = cap;
}
/// Clears the contents of the cache.
///
/// # Example
///
/// ```
/// use lru::LruCache;
/// use std::num::NonZeroUsize;
/// let mut cache: LruCache<isize, &str> = LruCache::new(NonZeroUsize::new(2).unwrap());
/// assert_eq!(cache.len(), 0);
///
/// cache.put(1, "a");
/// assert_eq!(cache.len(), 1);
///
/// cache.put(2, "b");
/// assert_eq!(cache.len(), 2);
///
/// cache.clear();
/// assert_eq!(cache.len(), 0);
/// ```
pub fn clear(&mut self) {
while self.pop_lru().is_some() {}
}
/// An iterator visiting all entries in most-recently used order. The iterator element type is
/// `(&K, &V)`.
///
/// # Examples
///
/// ```
/// use lru::LruCache;
/// use std::num::NonZeroUsize;
///
/// let mut cache = LruCache::new(NonZeroUsize::new(3).unwrap());
/// cache.put("a", 1);
/// cache.put("b", 2);
/// cache.put("c", 3);
///
/// for (key, val) in cache.iter() {
/// println!("key: {} val: {}", key, val);
/// }
/// ```
pub fn iter(&self) -> Iter<'_, K, V> {
Iter {
len: self.len(),
ptr: unsafe { (*self.head).next },
end: unsafe { (*self.tail).prev },
phantom: PhantomData,
}
}
/// An iterator visiting all entries in most-recently-used order, giving a mutable reference on
/// V. The iterator element type is `(&K, &mut V)`.
///
/// # Examples
///
/// ```
/// use lru::LruCache;
/// use std::num::NonZeroUsize;
///
/// struct HddBlock {
/// dirty: bool,
/// data: [u8; 512]
/// }
///
/// let mut cache = LruCache::new(NonZeroUsize::new(3).unwrap());
/// cache.put(0, HddBlock { dirty: false, data: [0x00; 512]});
/// cache.put(1, HddBlock { dirty: true, data: [0x55; 512]});
/// cache.put(2, HddBlock { dirty: true, data: [0x77; 512]});
///
/// // write dirty blocks to disk.
/// for (block_id, block) in cache.iter_mut() {
/// if block.dirty {
/// // write block to disk
/// block.dirty = false
/// }
/// }
/// ```
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_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.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;
}
}
// Attaches `node` after the sigil `self.head` node.
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;
}
}
// Attaches `node` before the sigil `self.tail` 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());
});
// We rebox the head/tail, and because these are maybe-uninit
// they do not have the absent k/v dropped.
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()
}
}
// The compiler does not automatically derive Send and Sync for LruCache because it contains
// raw pointers. The raw pointers are safely encapsulated by LruCache though so we can
// implement Send and Sync for it below.
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()
}
}
/// An iterator over the entries of a `LruCache`.
///
/// This `struct` is created by the [`iter`] method on [`LruCache`][`LruCache`]. See its
/// documentation for more.
///
/// [`iter`]: struct.LruCache.html#method.iter
/// [`LruCache`]: struct.LruCache.html
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,
}
}
}
// The compiler does not automatically derive Send and Sync for Iter because it contains
// raw pointers.
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> {}
/// An iterator over mutables entries of a `LruCache`.
///
/// This `struct` is created by the [`iter_mut`] method on [`LruCache`][`LruCache`]. See its
/// documentation for more.
///
/// [`iter_mut`]: struct.LruCache.html#method.iter_mut
/// [`LruCache`]: struct.LruCache.html
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> {}
// The compiler does not automatically derive Send and Sync for Iter because it contains
// raw pointers.
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> {}
/// An iterator that moves out of a `LruCache`.
///
/// This `struct` is created by the [`into_iter`] method on [`LruCache`][`LruCache`]. See its
/// documentation for more.
///
/// [`into_iter`]: struct.LruCache.html#method.into_iter
/// [`LruCache`]: struct.LruCache.html
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 core::{fmt::Debug, num::NonZeroUsize};
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 core::num::NonZeroUsize;
use hashbrown::hash_map::DefaultHashBuilder;
let s = DefaultHashBuilder::default();
let mut cache = LruCache::with_hasher(NonZeroUsize::new(16).unwrap(), 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(NonZeroUsize::new(2).unwrap());
assert!(cache.is_empty());
assert_eq!(cache.put("apple", "red"), None);
assert_eq!(cache.put("banana", "yellow"), 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"), None);
assert_eq!(cache.put("banana", "yellow"), None);
assert_eq!(cache.cap().get(), 2);
assert_eq!(cache.len(), 2);
assert!(!cache.is_empty());
assert_eq!(cache.get_or_insert("apple", || "orange"), &"red");
assert_eq!(cache.get_or_insert("banana", || "orange"), &"yellow");
assert_eq!(cache.get_or_insert("lemon", || "orange"), &"orange");
assert_eq!(cache.get_or_insert("lemon", || "red"), &"orange");
}
#[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("red")),
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("orange")),
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_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"), None);
assert_eq!(cache.put("banana", "yellow"), None);
assert_eq!(cache.cap().get(), 2);
assert_eq!(cache.len(), 2);
let v = cache.get_or_insert_mut("apple", || "orange");
assert_eq!(v, &"red");
*v = "blue";
assert_eq!(cache.get_or_insert_mut("apple", || "orange"), &"blue");
assert_eq!(cache.get_or_insert_mut("banana", || "orange"), &"yellow");
assert_eq!(cache.get_or_insert_mut("lemon", || "orange"), &"orange");
assert_eq!(cache.get_or_insert_mut("lemon", || "red"), &"orange");
}
#[test]
fn test_try_get_or_insert_mut() {
let mut cache = LruCache::new(NonZeroUsize::new(2).unwrap());
cache.put(1, "a");
cache.put(2, "b");
cache.put(2, "c");
let f = || -> Result<&str, &str> { Err("failed") };
let a = || -> Result<&str, &str> { Ok("a") };
let b = || -> Result<&str, &str> { Ok("b") };
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_put_and_get_mut() {
let mut cache = LruCache::new(NonZeroUsize::new(2).unwrap());
cache.put("apple", "red");
cache.put("banana", "yellow");
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);
cache.put("banana", 3);
{
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"), 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(NonZeroUsize::new(2).unwrap());
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");
// Even though we inserted "apple" into the cache earlier it has since been removed from
// the cache so there is no current value for `put` to return.
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(NonZeroUsize::new(2).unwrap());
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(NonZeroUsize::new(2).unwrap());
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(NonZeroUsize::new(2).unwrap());
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(NonZeroUsize::new(2).unwrap());
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(NonZeroUsize::new(2).unwrap());
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_entry() {
let mut cache = LruCache::new(NonZeroUsize::new(2).unwrap());
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_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");
}
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(NonZeroUsize::new(2).unwrap());
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(NonZeroUsize::new(2).unwrap());
cache.put(1, "a");
cache.put(2, "b");
cache.resize(NonZeroUsize::new(4).unwrap());
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(NonZeroUsize::new(4).unwrap());
cache.put(1, "a");
cache.put(2, "b");
cache.put(3, "c");
cache.put(4, "d");
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");
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");
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);
cache.put("b", 2);
cache.put("c", 3);
{
// iter const
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);
}
{
// iter mut
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);
cache.put("b", 2);
cache.put("c", 3);
{
// iter const
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);
}
{
// iter mut
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);
cache.put("b", 2);
cache.put("c", 3);
{
// iter const
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);
}
{
// iter mut
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);
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(NonZeroUsize::new(3).unwrap());
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_into_iter() {
let mut cache = LruCache::new(NonZeroUsize::new(3).unwrap());
cache.put("a", 1);
cache.put("b", 2);
cache.put("c", 3);
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);
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]
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");
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");
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 {});
}
}
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 {});
}
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 {});
}
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);
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);
}
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);
}
}
/// Doctests for what should *not* compile
///
/// ```compile_fail
/// let mut cache = lru::LruCache::<u32, u32>::unbounded();
/// let _: &'static u32 = cache.get_or_insert(0, || 92);
/// ```
///
/// ```compile_fail
/// let mut cache = lru::LruCache::<u32, u32>::unbounded();
/// let _: Option<(&'static u32, _)> = cache.peek_lru();
/// let _: Option<(_, &'static u32)> = cache.peek_lru();
/// ```
fn _test_lifetimes() {}