492 lines
15 KiB
Rust
492 lines
15 KiB
Rust
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
|
|
// file at the top-level directory of this distribution and at
|
|
// http://rust-lang.org/COPYRIGHT.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
|
// option. This file may not be copied, modified, or distributed
|
|
// except according to those terms.
|
|
|
|
//! A cache that holds a limited number of key-value pairs. When the
|
|
//! capacity of the cache is exceeded, the least-recently-used
|
|
//! (where "used" means a look-up or putting the pair into the cache)
|
|
//! pair is automatically removed.
|
|
//!
|
|
//! # Examples
|
|
//!
|
|
//! ```
|
|
//! use lru_cache::LruCache;
|
|
//!
|
|
//! let mut cache = LruCache::new(2);
|
|
//!
|
|
//! cache.insert(1, 10);
|
|
//! cache.insert(2, 20);
|
|
//! cache.insert(3, 30);
|
|
//! assert!(cache.get_mut(&1).is_none());
|
|
//! assert_eq!(*cache.get_mut(&2).unwrap(), 20);
|
|
//! assert_eq!(*cache.get_mut(&3).unwrap(), 30);
|
|
//!
|
|
//! cache.insert(2, 22);
|
|
//! assert_eq!(*cache.get_mut(&2).unwrap(), 22);
|
|
//!
|
|
//! cache.insert(6, 60);
|
|
//! assert!(cache.get_mut(&3).is_none());
|
|
//!
|
|
//! cache.set_capacity(1);
|
|
//! assert!(cache.get_mut(&2).is_none());
|
|
//! ```
|
|
|
|
use std::collections::hash_map::RandomState;
|
|
use std::fmt;
|
|
use std::hash::{Hash, BuildHasher};
|
|
use std::borrow::Borrow;
|
|
|
|
use super::linked_hash_map::{self, LinkedHashMap};
|
|
|
|
// FIXME(conventions): implement indexing?
|
|
|
|
/// An LRU cache.
|
|
pub struct LruCache<K, V, S = RandomState> where K: Eq + Hash, S: BuildHasher {
|
|
map: LinkedHashMap<K, V, S>,
|
|
max_size: usize,
|
|
}
|
|
|
|
impl<K: Hash + Eq, V> LruCache<K, V> {
|
|
/// Creates an empty cache that can hold at most `capacity` items.
|
|
///
|
|
/// # Examples
|
|
///
|
|
/// ```
|
|
/// use lru_cache::LruCache;
|
|
/// let mut cache: LruCache<i32, &str> = LruCache::new(10);
|
|
/// ```
|
|
pub fn new(capacity: usize) -> LruCache<K, V> {
|
|
LruCache {
|
|
map: LinkedHashMap::new(),
|
|
max_size: capacity,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<K, V, S> LruCache<K, V, S> where K: Eq + Hash, S: BuildHasher {
|
|
/// Creates an empty cache that can hold at most `capacity` items with the given hash state.
|
|
pub fn with_hash_state(capacity: usize, hash_state: S) -> LruCache<K, V, S> {
|
|
LruCache { map: LinkedHashMap::with_hash_state(hash_state), max_size: capacity }
|
|
}
|
|
|
|
/// Checks if the map contains the given key.
|
|
///
|
|
/// # Examples
|
|
///
|
|
/// ```
|
|
/// use lru_cache::LruCache;
|
|
///
|
|
/// let mut cache = LruCache::new(1);
|
|
///
|
|
/// cache.insert(1, "a");
|
|
/// assert_eq!(cache.contains_key(&1), true);
|
|
/// ```
|
|
pub fn contains_key<Q: ?Sized>(&mut self, key: &Q) -> bool
|
|
where K: Borrow<Q>,
|
|
Q: Hash + Eq
|
|
{
|
|
self.get_mut(key).is_some()
|
|
}
|
|
|
|
/// Inserts a key-value pair into the cache. If the key already existed, the old value is
|
|
/// returned.
|
|
///
|
|
/// # Examples
|
|
///
|
|
/// ```
|
|
/// use lru_cache::LruCache;
|
|
///
|
|
/// let mut cache = LruCache::new(2);
|
|
///
|
|
/// cache.insert(1, "a");
|
|
/// cache.insert(2, "b");
|
|
/// assert_eq!(cache.get_mut(&1), Some(&mut "a"));
|
|
/// assert_eq!(cache.get_mut(&2), Some(&mut "b"));
|
|
/// ```
|
|
pub fn insert(&mut self, k: K, v: V) -> Option<V> {
|
|
let old_val = self.map.insert(k, v);
|
|
if self.len() > self.capacity() {
|
|
self.remove_lru();
|
|
}
|
|
old_val
|
|
}
|
|
|
|
/// Returns a mutable reference to the value corresponding to the given key in the cache, if
|
|
/// any.
|
|
///
|
|
/// # Examples
|
|
///
|
|
/// ```
|
|
/// use lru_cache::LruCache;
|
|
///
|
|
/// let mut cache = LruCache::new(2);
|
|
///
|
|
/// cache.insert(1, "a");
|
|
/// cache.insert(2, "b");
|
|
/// cache.insert(2, "c");
|
|
/// cache.insert(3, "d");
|
|
///
|
|
/// assert_eq!(cache.get_mut(&1), None);
|
|
/// assert_eq!(cache.get_mut(&2), Some(&mut "c"));
|
|
/// ```
|
|
pub fn get_mut<Q: ?Sized>(&mut self, k: &Q) -> Option<&mut V>
|
|
where K: Borrow<Q>,
|
|
Q: Hash + Eq
|
|
{
|
|
self.map.get_refresh(k)
|
|
}
|
|
|
|
/// Removes the given key from the cache and returns its corresponding value.
|
|
///
|
|
/// # Examples
|
|
///
|
|
/// ```
|
|
/// use lru_cache::LruCache;
|
|
///
|
|
/// let mut cache = LruCache::new(2);
|
|
///
|
|
/// cache.insert(2, "a");
|
|
///
|
|
/// assert_eq!(cache.remove(&1), None);
|
|
/// assert_eq!(cache.remove(&2), Some("a"));
|
|
/// assert_eq!(cache.remove(&2), None);
|
|
/// assert_eq!(cache.len(), 0);
|
|
/// ```
|
|
pub fn remove<Q: ?Sized>(&mut self, k: &Q) -> Option<V>
|
|
where K: Borrow<Q>,
|
|
Q: Hash + Eq
|
|
{
|
|
self.map.remove(k)
|
|
}
|
|
|
|
/// Returns the maximum number of key-value pairs the cache can hold.
|
|
///
|
|
/// # Examples
|
|
///
|
|
/// ```
|
|
/// use lru_cache::LruCache;
|
|
/// let mut cache: LruCache<i32, &str> = LruCache::new(2);
|
|
/// assert_eq!(cache.capacity(), 2);
|
|
/// ```
|
|
pub fn capacity(&self) -> usize {
|
|
self.max_size
|
|
}
|
|
|
|
/// Sets the number of key-value pairs the cache can hold. Removes
|
|
/// least-recently-used key-value pairs if necessary.
|
|
///
|
|
/// # Examples
|
|
///
|
|
/// ```
|
|
/// use lru_cache::LruCache;
|
|
///
|
|
/// let mut cache = LruCache::new(2);
|
|
///
|
|
/// cache.insert(1, "a");
|
|
/// cache.insert(2, "b");
|
|
/// cache.insert(3, "c");
|
|
///
|
|
/// assert_eq!(cache.get_mut(&1), None);
|
|
/// assert_eq!(cache.get_mut(&2), Some(&mut "b"));
|
|
/// assert_eq!(cache.get_mut(&3), Some(&mut "c"));
|
|
///
|
|
/// cache.set_capacity(3);
|
|
/// cache.insert(1, "a");
|
|
/// cache.insert(2, "b");
|
|
///
|
|
/// assert_eq!(cache.get_mut(&1), Some(&mut "a"));
|
|
/// assert_eq!(cache.get_mut(&2), Some(&mut "b"));
|
|
/// assert_eq!(cache.get_mut(&3), Some(&mut "c"));
|
|
///
|
|
/// cache.set_capacity(1);
|
|
///
|
|
/// assert_eq!(cache.get_mut(&1), None);
|
|
/// assert_eq!(cache.get_mut(&2), None);
|
|
/// assert_eq!(cache.get_mut(&3), Some(&mut "c"));
|
|
/// ```
|
|
pub fn set_capacity(&mut self, capacity: usize) {
|
|
for _ in capacity..self.len() {
|
|
self.remove_lru();
|
|
}
|
|
self.max_size = capacity;
|
|
}
|
|
|
|
#[inline]
|
|
fn remove_lru(&mut self) -> Option<(K, V)> {
|
|
self.map.pop_front()
|
|
}
|
|
|
|
/// Returns the number of key-value pairs in the cache.
|
|
pub fn len(&self) -> usize { self.map.len() }
|
|
|
|
/// Returns `true` if the cache contains no key-value pairs.
|
|
pub fn is_empty(&self) -> bool { self.map.is_empty() }
|
|
|
|
/// Removes all key-value pairs from the cache.
|
|
pub fn clear(&mut self) { self.map.clear(); }
|
|
|
|
/// Returns an iterator over the cache's key-value pairs in least- to most-recently-used order.
|
|
///
|
|
/// Accessing the cache through the iterator does _not_ affect the cache's LRU state.
|
|
///
|
|
/// # Examples
|
|
///
|
|
/// ```
|
|
/// use lru_cache::LruCache;
|
|
///
|
|
/// let mut cache = LruCache::new(2);
|
|
///
|
|
/// cache.insert(1, 10);
|
|
/// cache.insert(2, 20);
|
|
/// cache.insert(3, 30);
|
|
///
|
|
/// let kvs: Vec<_> = cache.iter().collect();
|
|
/// assert_eq!(kvs, [(&2, &20), (&3, &30)]);
|
|
/// ```
|
|
pub fn iter(&self) -> Iter<K, V> { Iter(self.map.iter()) }
|
|
|
|
/// Returns an iterator over the cache's key-value pairs in least- to most-recently-used order,
|
|
/// with mutable references to the values.
|
|
///
|
|
/// Accessing the cache through the iterator does _not_ affect the cache's LRU state.
|
|
///
|
|
/// # Examples
|
|
///
|
|
/// ```
|
|
/// use lru_cache::LruCache;
|
|
///
|
|
/// let mut cache = LruCache::new(2);
|
|
///
|
|
/// cache.insert(1, 10);
|
|
/// cache.insert(2, 20);
|
|
/// cache.insert(3, 30);
|
|
///
|
|
/// let mut n = 2;
|
|
///
|
|
/// for (k, v) in cache.iter_mut() {
|
|
/// assert_eq!(*k, n);
|
|
/// assert_eq!(*v, n * 10);
|
|
/// *v *= 10;
|
|
/// n += 1;
|
|
/// }
|
|
///
|
|
/// assert_eq!(n, 4);
|
|
/// assert_eq!(cache.get_mut(&2), Some(&mut 200));
|
|
/// assert_eq!(cache.get_mut(&3), Some(&mut 300));
|
|
/// ```
|
|
pub fn iter_mut(&mut self) -> IterMut<K, V> { IterMut(self.map.iter_mut()) }
|
|
}
|
|
|
|
impl<K: Hash + Eq, V, S: BuildHasher> Extend<(K, V)> for LruCache<K, V, S> {
|
|
fn extend<T: IntoIterator<Item=(K, V)>>(&mut self, iter: T) {
|
|
for (k, v) in iter {
|
|
self.insert(k, v);
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<A: fmt::Debug + Hash + Eq, B: fmt::Debug, S: BuildHasher> fmt::Debug for LruCache<A, B, S> {
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
f.debug_map().entries(self.iter().rev()).finish()
|
|
}
|
|
}
|
|
|
|
impl<'a, K, V, S> IntoIterator for &'a LruCache<K, V, S> where K: Eq + Hash, S: BuildHasher {
|
|
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, V, S> IntoIterator for &'a mut LruCache<K, V, S> where K: Eq + Hash, S: BuildHasher {
|
|
type Item = (&'a K, &'a mut V);
|
|
type IntoIter = IterMut<'a, K, V>;
|
|
fn into_iter(self) -> IterMut<'a, K, V> { self.iter_mut() }
|
|
}
|
|
|
|
impl<K, V> Clone for LruCache<K, V> where K: Clone + Eq + Hash, V: Clone {
|
|
fn clone(&self) -> LruCache<K, V> { LruCache { map: self.map.clone(), ..*self } }
|
|
}
|
|
|
|
/// An iterator over a cache's key-value pairs in least- to most-recently-used order.
|
|
///
|
|
/// Accessing a cache through the iterator does _not_ affect the cache's LRU state.
|
|
pub struct Iter<'a, K: 'a, V: 'a>(linked_hash_map::Iter<'a, K, V>);
|
|
|
|
impl<'a, K, V> Clone for Iter<'a, K, V> {
|
|
fn clone(&self) -> Iter<'a, K, V> { Iter(self.0.clone()) }
|
|
}
|
|
|
|
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)> { self.0.next() }
|
|
fn size_hint(&self) -> (usize, Option<usize>) { self.0.size_hint() }
|
|
}
|
|
|
|
impl<'a, K, V> DoubleEndedIterator for Iter<'a, K, V> {
|
|
fn next_back(&mut self) -> Option<(&'a K, &'a V)> { self.0.next_back() }
|
|
}
|
|
|
|
impl<'a, K, V> ExactSizeIterator for Iter<'a, K, V> {
|
|
fn len(&self) -> usize { self.0.len() }
|
|
}
|
|
|
|
/// An iterator over a cache's key-value pairs in least- to most-recently-used order with mutable
|
|
/// references to the values.
|
|
///
|
|
/// Accessing a cache through the iterator does _not_ affect the cache's LRU state.
|
|
pub struct IterMut<'a, K: 'a, V: 'a>(linked_hash_map::IterMut<'a, K, V>);
|
|
|
|
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)> { self.0.next() }
|
|
fn size_hint(&self) -> (usize, Option<usize>) { self.0.size_hint() }
|
|
}
|
|
|
|
impl<'a, K, V> DoubleEndedIterator for IterMut<'a, K, V> {
|
|
fn next_back(&mut self) -> Option<(&'a K, &'a mut V)> { self.0.next_back() }
|
|
}
|
|
|
|
impl<'a, K, V> ExactSizeIterator for IterMut<'a, K, V> {
|
|
fn len(&self) -> usize { self.0.len() }
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::LruCache;
|
|
|
|
#[test]
|
|
fn test_put_and_get() {
|
|
let mut cache = LruCache::new(2);
|
|
cache.insert(1, 10);
|
|
cache.insert(2, 20);
|
|
assert_eq!(cache.get_mut(&1), Some(&mut 10));
|
|
assert_eq!(cache.get_mut(&2), Some(&mut 20));
|
|
assert_eq!(cache.len(), 2);
|
|
}
|
|
|
|
#[test]
|
|
fn test_put_update() {
|
|
let mut cache = LruCache::new(1);
|
|
cache.insert("1", 10);
|
|
cache.insert("1", 19);
|
|
assert_eq!(cache.get_mut("1"), Some(&mut 19));
|
|
assert_eq!(cache.len(), 1);
|
|
}
|
|
|
|
#[test]
|
|
fn test_contains_key() {
|
|
let mut cache = LruCache::new(1);
|
|
cache.insert("1", 10);
|
|
assert_eq!(cache.contains_key("1"), true);
|
|
}
|
|
|
|
#[test]
|
|
fn test_expire_lru() {
|
|
let mut cache = LruCache::new(2);
|
|
cache.insert("foo1", "bar1");
|
|
cache.insert("foo2", "bar2");
|
|
cache.insert("foo3", "bar3");
|
|
assert!(cache.get_mut("foo1").is_none());
|
|
cache.insert("foo2", "bar2update");
|
|
cache.insert("foo4", "bar4");
|
|
assert!(cache.get_mut("foo3").is_none());
|
|
}
|
|
|
|
#[test]
|
|
fn test_pop() {
|
|
let mut cache = LruCache::new(2);
|
|
cache.insert(1, 10);
|
|
cache.insert(2, 20);
|
|
assert_eq!(cache.len(), 2);
|
|
let opt1 = cache.remove(&1);
|
|
assert!(opt1.is_some());
|
|
assert_eq!(opt1.unwrap(), 10);
|
|
assert!(cache.get_mut(&1).is_none());
|
|
assert_eq!(cache.len(), 1);
|
|
}
|
|
|
|
#[test]
|
|
fn test_change_capacity() {
|
|
let mut cache = LruCache::new(2);
|
|
assert_eq!(cache.capacity(), 2);
|
|
cache.insert(1, 10);
|
|
cache.insert(2, 20);
|
|
cache.set_capacity(1);
|
|
assert!(cache.get_mut(&1).is_none());
|
|
assert_eq!(cache.capacity(), 1);
|
|
}
|
|
|
|
#[test]
|
|
fn test_debug() {
|
|
let mut cache = LruCache::new(3);
|
|
cache.insert(1, 10);
|
|
cache.insert(2, 20);
|
|
cache.insert(3, 30);
|
|
assert_eq!(format!("{:?}", cache), "{3: 30, 2: 20, 1: 10}");
|
|
cache.insert(2, 22);
|
|
assert_eq!(format!("{:?}", cache), "{2: 22, 3: 30, 1: 10}");
|
|
cache.insert(6, 60);
|
|
assert_eq!(format!("{:?}", cache), "{6: 60, 2: 22, 3: 30}");
|
|
cache.get_mut(&3);
|
|
assert_eq!(format!("{:?}", cache), "{3: 30, 6: 60, 2: 22}");
|
|
cache.set_capacity(2);
|
|
assert_eq!(format!("{:?}", cache), "{3: 30, 6: 60}");
|
|
}
|
|
|
|
#[test]
|
|
fn test_remove() {
|
|
let mut cache = LruCache::new(3);
|
|
cache.insert(1, 10);
|
|
cache.insert(2, 20);
|
|
cache.insert(3, 30);
|
|
cache.insert(4, 40);
|
|
cache.insert(5, 50);
|
|
cache.remove(&3);
|
|
cache.remove(&4);
|
|
assert!(cache.get_mut(&3).is_none());
|
|
assert!(cache.get_mut(&4).is_none());
|
|
cache.insert(6, 60);
|
|
cache.insert(7, 70);
|
|
cache.insert(8, 80);
|
|
assert!(cache.get_mut(&5).is_none());
|
|
assert_eq!(cache.get_mut(&6), Some(&mut 60));
|
|
assert_eq!(cache.get_mut(&7), Some(&mut 70));
|
|
assert_eq!(cache.get_mut(&8), Some(&mut 80));
|
|
}
|
|
|
|
#[test]
|
|
fn test_clear() {
|
|
let mut cache = LruCache::new(2);
|
|
cache.insert(1, 10);
|
|
cache.insert(2, 20);
|
|
cache.clear();
|
|
assert!(cache.get_mut(&1).is_none());
|
|
assert!(cache.get_mut(&2).is_none());
|
|
assert_eq!(format!("{:?}", cache), "{}");
|
|
}
|
|
|
|
#[test]
|
|
fn test_iter() {
|
|
let mut cache = LruCache::new(3);
|
|
cache.insert(1, 10);
|
|
cache.insert(2, 20);
|
|
cache.insert(3, 30);
|
|
cache.insert(4, 40);
|
|
cache.insert(5, 50);
|
|
assert_eq!(cache.iter().collect::<Vec<_>>(),
|
|
[(&3, &30), (&4, &40), (&5, &50)]);
|
|
assert_eq!(cache.iter_mut().collect::<Vec<_>>(),
|
|
[(&3, &mut 30), (&4, &mut 40), (&5, &mut 50)]);
|
|
assert_eq!(cache.iter().rev().collect::<Vec<_>>(),
|
|
[(&5, &50), (&4, &40), (&3, &30)]);
|
|
assert_eq!(cache.iter_mut().rev().collect::<Vec<_>>(),
|
|
[(&5, &mut 50), (&4, &mut 40), (&3, &mut 30)]);
|
|
}
|
|
}
|