2013-10-31 19:58:15 -05:00
|
|
|
// Copyright 2013 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.
|
|
|
|
//!
|
|
|
|
//! # Example
|
|
|
|
//!
|
|
|
|
//! ```rust
|
2014-02-02 23:56:49 -06:00
|
|
|
//! use collections::LruCache;
|
2013-12-22 15:31:37 -06:00
|
|
|
//!
|
2013-10-31 19:58:15 -05:00
|
|
|
//! let mut cache: LruCache<int, int> = LruCache::new(2);
|
|
|
|
//! cache.put(1, 10);
|
|
|
|
//! cache.put(2, 20);
|
|
|
|
//! cache.put(3, 30);
|
|
|
|
//! assert!(cache.get(&1).is_none());
|
|
|
|
//! assert_eq!(*cache.get(&2).unwrap(), 20);
|
|
|
|
//! assert_eq!(*cache.get(&3).unwrap(), 30);
|
|
|
|
//!
|
|
|
|
//! cache.put(2, 22);
|
|
|
|
//! assert_eq!(*cache.get(&2).unwrap(), 22);
|
|
|
|
//!
|
|
|
|
//! cache.put(6, 60);
|
|
|
|
//! assert!(cache.get(&3).is_none());
|
|
|
|
//!
|
|
|
|
//! cache.change_capacity(1);
|
|
|
|
//! assert!(cache.get(&2).is_none());
|
|
|
|
//! ```
|
|
|
|
|
|
|
|
use std::container::Container;
|
2014-02-25 10:03:41 -06:00
|
|
|
use std::hash::Hash;
|
2014-02-19 20:56:33 -06:00
|
|
|
use std::fmt;
|
2014-04-18 06:44:30 -05:00
|
|
|
use std::mem;
|
2013-10-31 19:58:15 -05:00
|
|
|
use std::ptr;
|
|
|
|
|
2014-02-19 21:29:58 -06:00
|
|
|
use HashMap;
|
|
|
|
|
2014-01-24 13:02:03 -06:00
|
|
|
struct KeyRef<K> { k: *K }
|
2013-10-31 19:58:15 -05:00
|
|
|
|
|
|
|
struct LruEntry<K, V> {
|
2014-01-24 13:02:03 -06:00
|
|
|
next: *mut LruEntry<K, V>,
|
|
|
|
prev: *mut LruEntry<K, V>,
|
2014-04-18 06:44:30 -05:00
|
|
|
key: K,
|
|
|
|
value: V,
|
2013-10-31 19:58:15 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
/// An LRU Cache.
|
|
|
|
pub struct LruCache<K, V> {
|
2014-05-05 20:56:44 -05:00
|
|
|
map: HashMap<KeyRef<K>, Box<LruEntry<K, V>>>,
|
2014-03-27 17:10:04 -05:00
|
|
|
max_size: uint,
|
|
|
|
head: *mut LruEntry<K, V>,
|
2013-10-31 19:58:15 -05:00
|
|
|
}
|
|
|
|
|
2014-02-25 10:03:41 -06:00
|
|
|
impl<S, K: Hash<S>> Hash<S> for KeyRef<K> {
|
|
|
|
fn hash(&self, state: &mut S) {
|
|
|
|
unsafe { (*self.k).hash(state) }
|
2013-10-31 19:58:15 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<K: Eq> Eq for KeyRef<K> {
|
|
|
|
fn eq(&self, other: &KeyRef<K>) -> bool {
|
|
|
|
unsafe{ (*self.k).eq(&*other.k) }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-03-22 15:30:45 -05:00
|
|
|
impl<K: TotalEq> TotalEq for KeyRef<K> {}
|
|
|
|
|
2013-10-31 19:58:15 -05:00
|
|
|
impl<K, V> LruEntry<K, V> {
|
2014-04-18 06:44:30 -05:00
|
|
|
fn new(k: K, v: V) -> LruEntry<K, V> {
|
2013-10-31 19:58:15 -05:00
|
|
|
LruEntry {
|
2014-04-18 06:44:30 -05:00
|
|
|
key: k,
|
|
|
|
value: v,
|
2013-10-31 19:58:15 -05:00
|
|
|
next: ptr::mut_null(),
|
|
|
|
prev: ptr::mut_null(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-03-22 15:30:45 -05:00
|
|
|
impl<K: Hash + TotalEq, V> LruCache<K, V> {
|
2013-10-31 19:58:15 -05:00
|
|
|
/// Create an LRU Cache that holds at most `capacity` items.
|
|
|
|
pub fn new(capacity: uint) -> LruCache<K, V> {
|
|
|
|
let cache = LruCache {
|
|
|
|
map: HashMap::new(),
|
|
|
|
max_size: capacity,
|
2014-05-23 22:53:56 -05:00
|
|
|
head: unsafe{ mem::transmute(box mem::uninitialized::<LruEntry<K, V>>()) },
|
2013-10-31 19:58:15 -05:00
|
|
|
};
|
|
|
|
unsafe {
|
2014-04-18 05:23:36 -05:00
|
|
|
(*cache.head).next = cache.head;
|
|
|
|
(*cache.head).prev = cache.head;
|
2013-10-31 19:58:15 -05:00
|
|
|
}
|
|
|
|
return cache;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Put a key-value pair into cache.
|
|
|
|
pub fn put(&mut self, k: K, v: V) {
|
|
|
|
let (node_ptr, node_opt) = match self.map.find_mut(&KeyRef{k: &k}) {
|
|
|
|
Some(node) => {
|
2014-04-18 06:44:30 -05:00
|
|
|
node.value = v;
|
2013-10-31 19:58:15 -05:00
|
|
|
let node_ptr: *mut LruEntry<K, V> = &mut **node;
|
|
|
|
(node_ptr, None)
|
|
|
|
}
|
|
|
|
None => {
|
2014-04-25 03:08:02 -05:00
|
|
|
let mut node = box LruEntry::new(k, v);
|
2013-10-31 19:58:15 -05:00
|
|
|
let node_ptr: *mut LruEntry<K, V> = &mut *node;
|
|
|
|
(node_ptr, Some(node))
|
|
|
|
}
|
|
|
|
};
|
2014-04-18 01:39:25 -05:00
|
|
|
match node_opt {
|
|
|
|
None => {
|
2014-04-18 06:44:30 -05:00
|
|
|
// Existing node, just update LRU position
|
2014-04-18 01:39:25 -05:00
|
|
|
self.detach(node_ptr);
|
|
|
|
self.attach(node_ptr);
|
|
|
|
}
|
|
|
|
Some(node) => {
|
2014-04-18 06:44:30 -05:00
|
|
|
let keyref = unsafe { &(*node_ptr).key };
|
2014-04-18 01:39:25 -05:00
|
|
|
self.map.swap(KeyRef{k: keyref}, node);
|
|
|
|
self.attach(node_ptr);
|
|
|
|
if self.len() > self.capacity() {
|
|
|
|
self.remove_lru();
|
|
|
|
}
|
2013-10-31 19:58:15 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Return a value corresponding to the key in the cache.
|
|
|
|
pub fn get<'a>(&'a mut self, k: &K) -> Option<&'a V> {
|
|
|
|
let (value, node_ptr_opt) = match self.map.find_mut(&KeyRef{k: k}) {
|
|
|
|
None => (None, None),
|
|
|
|
Some(node) => {
|
|
|
|
let node_ptr: *mut LruEntry<K, V> = &mut **node;
|
2014-04-18 06:44:30 -05:00
|
|
|
(Some(unsafe { &(*node_ptr).value }), Some(node_ptr))
|
2013-10-31 19:58:15 -05:00
|
|
|
}
|
|
|
|
};
|
|
|
|
match node_ptr_opt {
|
|
|
|
None => (),
|
|
|
|
Some(node_ptr) => {
|
|
|
|
self.detach(node_ptr);
|
|
|
|
self.attach(node_ptr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Remove and return a value corresponding to the key from the cache.
|
|
|
|
pub fn pop(&mut self, k: &K) -> Option<V> {
|
|
|
|
match self.map.pop(&KeyRef{k: k}) {
|
|
|
|
None => None,
|
2014-04-18 06:44:30 -05:00
|
|
|
Some(lru_entry) => Some(lru_entry.value)
|
2013-10-31 19:58:15 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Return the maximum number of key-value pairs the cache can hold.
|
|
|
|
pub fn capacity(&self) -> uint {
|
|
|
|
self.max_size
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Change the number of key-value pairs the cache can hold. Remove
|
|
|
|
/// least-recently-used key-value pairs if necessary.
|
|
|
|
pub fn change_capacity(&mut self, capacity: uint) {
|
|
|
|
for _ in range(capacity, self.len()) {
|
|
|
|
self.remove_lru();
|
|
|
|
}
|
|
|
|
self.max_size = capacity;
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn remove_lru(&mut self) {
|
|
|
|
if self.len() > 0 {
|
2014-04-18 05:23:36 -05:00
|
|
|
let lru = unsafe { (*self.head).prev };
|
2013-10-31 19:58:15 -05:00
|
|
|
self.detach(lru);
|
2014-04-18 06:44:30 -05:00
|
|
|
self.map.pop(&KeyRef{k: unsafe { &(*lru).key }});
|
2013-10-31 19:58:15 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn detach(&mut self, node: *mut LruEntry<K, V>) {
|
|
|
|
unsafe {
|
|
|
|
(*(*node).prev).next = (*node).next;
|
|
|
|
(*(*node).next).prev = (*node).prev;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-03-22 15:30:45 -05:00
|
|
|
impl<A: fmt::Show + Hash + TotalEq, B: fmt::Show> fmt::Show for LruCache<A, B> {
|
2013-10-31 19:58:15 -05:00
|
|
|
/// Return a string that lists the key-value pairs from most-recently
|
|
|
|
/// used to least-recently used.
|
2014-02-19 20:56:33 -06:00
|
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
2014-05-10 16:05:06 -05:00
|
|
|
try!(write!(f, r"\{"));
|
2013-10-31 19:58:15 -05:00
|
|
|
let mut cur = self.head;
|
|
|
|
for i in range(0, self.len()) {
|
2014-05-10 16:05:06 -05:00
|
|
|
if i > 0 { try!(write!(f, ", ")) }
|
2013-10-31 19:58:15 -05:00
|
|
|
unsafe {
|
|
|
|
cur = (*cur).next;
|
2014-05-10 16:05:06 -05:00
|
|
|
try!(write!(f, "{}", (*cur).key));
|
2013-10-31 19:58:15 -05:00
|
|
|
}
|
2014-05-10 16:05:06 -05:00
|
|
|
try!(write!(f, ": "));
|
2013-10-31 19:58:15 -05:00
|
|
|
unsafe {
|
2014-05-10 16:05:06 -05:00
|
|
|
try!(write!(f, "{}", (*cur).value));
|
2013-10-31 19:58:15 -05:00
|
|
|
}
|
|
|
|
}
|
2014-05-10 16:05:06 -05:00
|
|
|
write!(f, r"\}")
|
2013-10-31 19:58:15 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-03-22 15:30:45 -05:00
|
|
|
impl<K: Hash + TotalEq, V> Container for LruCache<K, V> {
|
2013-10-31 19:58:15 -05:00
|
|
|
/// Return the number of key-value pairs in the cache.
|
|
|
|
fn len(&self) -> uint {
|
|
|
|
self.map.len()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-03-22 15:30:45 -05:00
|
|
|
impl<K: Hash + TotalEq, V> Mutable for LruCache<K, V> {
|
2013-10-31 19:58:15 -05:00
|
|
|
/// Clear the cache of all key-value pairs.
|
|
|
|
fn clear(&mut self) {
|
|
|
|
self.map.clear();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[unsafe_destructor]
|
|
|
|
impl<K, V> Drop for LruCache<K, V> {
|
|
|
|
fn drop(&mut self) {
|
|
|
|
unsafe {
|
core: Remove the cast module
This commit revisits the `cast` module in libcore and libstd, and scrutinizes
all functions inside of it. The result was to remove the `cast` module entirely,
folding all functionality into the `mem` module. Specifically, this is the fate
of each function in the `cast` module.
* transmute - This function was moved to `mem`, but it is now marked as
#[unstable]. This is due to planned changes to the `transmute`
function and how it can be invoked (see the #[unstable] comment).
For more information, see RFC 5 and #12898
* transmute_copy - This function was moved to `mem`, with clarification that is
is not an error to invoke it with T/U that are different
sizes, but rather that it is strongly discouraged. This
function is now #[stable]
* forget - This function was moved to `mem` and marked #[stable]
* bump_box_refcount - This function was removed due to the deprecation of
managed boxes as well as its questionable utility.
* transmute_mut - This function was previously deprecated, and removed as part
of this commit.
* transmute_mut_unsafe - This function doesn't serve much of a purpose when it
can be achieved with an `as` in safe code, so it was
removed.
* transmute_lifetime - This function was removed because it is likely a strong
indication that code is incorrect in the first place.
* transmute_mut_lifetime - This function was removed for the same reasons as
`transmute_lifetime`
* copy_lifetime - This function was moved to `mem`, but it is marked
`#[unstable]` now due to the likelihood of being removed in
the future if it is found to not be very useful.
* copy_mut_lifetime - This function was also moved to `mem`, but had the same
treatment as `copy_lifetime`.
* copy_lifetime_vec - This function was removed because it is not used today,
and its existence is not necessary with DST
(copy_lifetime will suffice).
In summary, the cast module was stripped down to these functions, and then the
functions were moved to the `mem` module.
transmute - #[unstable]
transmute_copy - #[stable]
forget - #[stable]
copy_lifetime - #[unstable]
copy_mut_lifetime - #[unstable]
[breaking-change]
2014-05-09 12:34:51 -05:00
|
|
|
let node: Box<LruEntry<K, V>> = mem::transmute(self.head);
|
2014-04-18 06:44:30 -05:00
|
|
|
// Prevent compiler from trying to drop the un-initialized field in the sigil node.
|
2014-05-05 20:56:44 -05:00
|
|
|
let box LruEntry { key: k, value: v, .. } = node;
|
core: Remove the cast module
This commit revisits the `cast` module in libcore and libstd, and scrutinizes
all functions inside of it. The result was to remove the `cast` module entirely,
folding all functionality into the `mem` module. Specifically, this is the fate
of each function in the `cast` module.
* transmute - This function was moved to `mem`, but it is now marked as
#[unstable]. This is due to planned changes to the `transmute`
function and how it can be invoked (see the #[unstable] comment).
For more information, see RFC 5 and #12898
* transmute_copy - This function was moved to `mem`, with clarification that is
is not an error to invoke it with T/U that are different
sizes, but rather that it is strongly discouraged. This
function is now #[stable]
* forget - This function was moved to `mem` and marked #[stable]
* bump_box_refcount - This function was removed due to the deprecation of
managed boxes as well as its questionable utility.
* transmute_mut - This function was previously deprecated, and removed as part
of this commit.
* transmute_mut_unsafe - This function doesn't serve much of a purpose when it
can be achieved with an `as` in safe code, so it was
removed.
* transmute_lifetime - This function was removed because it is likely a strong
indication that code is incorrect in the first place.
* transmute_mut_lifetime - This function was removed for the same reasons as
`transmute_lifetime`
* copy_lifetime - This function was moved to `mem`, but it is marked
`#[unstable]` now due to the likelihood of being removed in
the future if it is found to not be very useful.
* copy_mut_lifetime - This function was also moved to `mem`, but had the same
treatment as `copy_lifetime`.
* copy_lifetime_vec - This function was removed because it is not used today,
and its existence is not necessary with DST
(copy_lifetime will suffice).
In summary, the cast module was stripped down to these functions, and then the
functions were moved to the `mem` module.
transmute - #[unstable]
transmute_copy - #[stable]
forget - #[stable]
copy_lifetime - #[unstable]
copy_mut_lifetime - #[unstable]
[breaking-change]
2014-05-09 12:34:51 -05:00
|
|
|
mem::forget(k);
|
|
|
|
mem::forget(v);
|
2013-10-31 19:58:15 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use super::LruCache;
|
|
|
|
|
|
|
|
fn assert_opt_eq<V: Eq>(opt: Option<&V>, v: V) {
|
|
|
|
assert!(opt.is_some());
|
2014-02-28 03:23:06 -06:00
|
|
|
assert!(opt.unwrap() == &v);
|
2013-10-31 19:58:15 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_put_and_get() {
|
|
|
|
let mut cache: LruCache<int, int> = LruCache::new(2);
|
|
|
|
cache.put(1, 10);
|
|
|
|
cache.put(2, 20);
|
|
|
|
assert_opt_eq(cache.get(&1), 10);
|
|
|
|
assert_opt_eq(cache.get(&2), 20);
|
|
|
|
assert_eq!(cache.len(), 2);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_put_update() {
|
2014-05-22 18:57:53 -05:00
|
|
|
let mut cache: LruCache<String, Vec<u8>> = LruCache::new(1);
|
2014-05-13 20:01:54 -05:00
|
|
|
cache.put("1".to_strbuf(), vec![10, 10]);
|
|
|
|
cache.put("1".to_strbuf(), vec![10, 19]);
|
|
|
|
assert_opt_eq(cache.get(&"1".to_strbuf()), vec![10, 19]);
|
2013-10-31 19:58:15 -05:00
|
|
|
assert_eq!(cache.len(), 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_expire_lru() {
|
2014-05-22 18:57:53 -05:00
|
|
|
let mut cache: LruCache<String, String> = LruCache::new(2);
|
2014-05-13 20:01:54 -05:00
|
|
|
cache.put("foo1".to_strbuf(), "bar1".to_strbuf());
|
|
|
|
cache.put("foo2".to_strbuf(), "bar2".to_strbuf());
|
|
|
|
cache.put("foo3".to_strbuf(), "bar3".to_strbuf());
|
|
|
|
assert!(cache.get(&"foo1".to_strbuf()).is_none());
|
|
|
|
cache.put("foo2".to_strbuf(), "bar2update".to_strbuf());
|
|
|
|
cache.put("foo4".to_strbuf(), "bar4".to_strbuf());
|
|
|
|
assert!(cache.get(&"foo3".to_strbuf()).is_none());
|
2013-10-31 19:58:15 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_pop() {
|
|
|
|
let mut cache: LruCache<int, int> = LruCache::new(2);
|
|
|
|
cache.put(1, 10);
|
|
|
|
cache.put(2, 20);
|
|
|
|
assert_eq!(cache.len(), 2);
|
|
|
|
let opt1 = cache.pop(&1);
|
|
|
|
assert!(opt1.is_some());
|
|
|
|
assert_eq!(opt1.unwrap(), 10);
|
|
|
|
assert!(cache.get(&1).is_none());
|
|
|
|
assert_eq!(cache.len(), 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_change_capacity() {
|
|
|
|
let mut cache: LruCache<int, int> = LruCache::new(2);
|
|
|
|
assert_eq!(cache.capacity(), 2);
|
|
|
|
cache.put(1, 10);
|
|
|
|
cache.put(2, 20);
|
|
|
|
cache.change_capacity(1);
|
|
|
|
assert!(cache.get(&1).is_none());
|
|
|
|
assert_eq!(cache.capacity(), 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_to_str() {
|
|
|
|
let mut cache: LruCache<int, int> = LruCache::new(3);
|
|
|
|
cache.put(1, 10);
|
|
|
|
cache.put(2, 20);
|
|
|
|
cache.put(3, 30);
|
2014-05-25 05:10:11 -05:00
|
|
|
assert_eq!(cache.to_str(), "{3: 30, 2: 20, 1: 10}".to_string());
|
2013-10-31 19:58:15 -05:00
|
|
|
cache.put(2, 22);
|
2014-05-25 05:10:11 -05:00
|
|
|
assert_eq!(cache.to_str(), "{2: 22, 3: 30, 1: 10}".to_string());
|
2013-10-31 19:58:15 -05:00
|
|
|
cache.put(6, 60);
|
2014-05-25 05:10:11 -05:00
|
|
|
assert_eq!(cache.to_str(), "{6: 60, 2: 22, 3: 30}".to_string());
|
2013-10-31 19:58:15 -05:00
|
|
|
cache.get(&3);
|
2014-05-25 05:10:11 -05:00
|
|
|
assert_eq!(cache.to_str(), "{3: 30, 6: 60, 2: 22}".to_string());
|
2013-10-31 19:58:15 -05:00
|
|
|
cache.change_capacity(2);
|
2014-05-25 05:10:11 -05:00
|
|
|
assert_eq!(cache.to_str(), "{3: 30, 6: 60}".to_string());
|
2013-10-31 19:58:15 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_clear() {
|
|
|
|
let mut cache: LruCache<int, int> = LruCache::new(2);
|
|
|
|
cache.put(1, 10);
|
|
|
|
cache.put(2, 20);
|
|
|
|
cache.clear();
|
|
|
|
assert!(cache.get(&1).is_none());
|
|
|
|
assert!(cache.get(&2).is_none());
|
2014-05-25 05:10:11 -05:00
|
|
|
assert_eq!(cache.to_str(), "{}".to_string());
|
2013-10-31 19:58:15 -05:00
|
|
|
}
|
|
|
|
}
|