Skip to content

LruCache clean ups and optimizations #13618

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Apr 22, 2014
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 35 additions & 58 deletions src/libcollections/lru_cache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,25 +41,25 @@ use std::cast;
use std::container::Container;
use std::hash::Hash;
use std::fmt;
use std::mem;
use std::ptr;

use HashMap;

struct KeyRef<K> { k: *K }

struct LruEntry<K, V> {
key: Option<K>,
value: Option<V>,
next: *mut LruEntry<K, V>,
prev: *mut LruEntry<K, V>,
key: K,
value: V,
}

/// An LRU Cache.
pub struct LruCache<K, V> {
map: HashMap<KeyRef<K>, ~LruEntry<K, V>>,
max_size: uint,
head: *mut LruEntry<K, V>,
tail: *mut LruEntry<K, V>,
}

impl<S, K: Hash<S>> Hash<S> for KeyRef<K> {
Expand All @@ -77,19 +77,10 @@ impl<K: Eq> Eq for KeyRef<K> {
impl<K: TotalEq> TotalEq for KeyRef<K> {}

impl<K, V> LruEntry<K, V> {
fn new() -> LruEntry<K, V> {
fn new(k: K, v: V) -> LruEntry<K, V> {
LruEntry {
key: None,
value: None,
next: ptr::mut_null(),
prev: ptr::mut_null(),
}
}

fn with_key_value(k: K, v: V) -> LruEntry<K, V> {
LruEntry {
key: Some(k),
value: Some(v),
key: k,
value: v,
next: ptr::mut_null(),
prev: ptr::mut_null(),
}
Expand All @@ -102,41 +93,42 @@ impl<K: Hash + TotalEq, V> LruCache<K, V> {
let cache = LruCache {
map: HashMap::new(),
max_size: capacity,
head: unsafe{ cast::transmute(~LruEntry::<K, V>::new()) },
tail: unsafe{ cast::transmute(~LruEntry::<K, V>::new()) },
head: unsafe{ cast::transmute(~mem::uninit::<LruEntry<K, V>>()) },
};
unsafe {
(*cache.head).next = cache.tail;
(*cache.tail).prev = cache.head;
(*cache.head).next = cache.head;
(*cache.head).prev = cache.head;
}
return cache;
}

/// Put a key-value pair into cache.
pub fn put(&mut self, k: K, v: V) {
let mut key_existed = false;
let (node_ptr, node_opt) = match self.map.find_mut(&KeyRef{k: &k}) {
Some(node) => {
key_existed = true;
node.value = Some(v);
node.value = v;
let node_ptr: *mut LruEntry<K, V> = &mut **node;
(node_ptr, None)
}
None => {
let mut node = ~LruEntry::with_key_value(k, v);
let mut node = ~LruEntry::new(k, v);
let node_ptr: *mut LruEntry<K, V> = &mut *node;
(node_ptr, Some(node))
}
};
if key_existed {
self.detach(node_ptr);
self.attach(node_ptr);
} else {
let keyref = unsafe { (*node_ptr).key.as_ref().unwrap() };
self.map.swap(KeyRef{k: keyref}, node_opt.unwrap());
self.attach(node_ptr);
if self.len() > self.capacity() {
self.remove_lru();
match node_opt {
None => {
// Existing node, just update LRU position
self.detach(node_ptr);
self.attach(node_ptr);
}
Some(node) => {
let keyref = unsafe { &(*node_ptr).key };
self.map.swap(KeyRef{k: keyref}, node);
self.attach(node_ptr);
if self.len() > self.capacity() {
self.remove_lru();
}
}
}
}
Expand All @@ -147,12 +139,7 @@ impl<K: Hash + TotalEq, V> LruCache<K, V> {
None => (None, None),
Some(node) => {
let node_ptr: *mut LruEntry<K, V> = &mut **node;
unsafe {
match (*node_ptr).value {
None => (None, None),
Some(ref value) => (Some(value), Some(node_ptr))
}
}
(Some(unsafe { &(*node_ptr).value }), Some(node_ptr))
}
};
match node_ptr_opt {
Expand All @@ -169,7 +156,7 @@ impl<K: Hash + TotalEq, V> LruCache<K, V> {
pub fn pop(&mut self, k: &K) -> Option<V> {
match self.map.pop(&KeyRef{k: k}) {
None => None,
Some(lru_entry) => lru_entry.value
Some(lru_entry) => Some(lru_entry.value)
}
}

Expand All @@ -190,14 +177,9 @@ impl<K: Hash + TotalEq, V> LruCache<K, V> {
#[inline]
fn remove_lru(&mut self) {
if self.len() > 0 {
let lru = unsafe { (*self.tail).prev };
let lru = unsafe { (*self.head).prev };
self.detach(lru);
unsafe {
match (*lru).key {
None => (),
Some(ref k) => { self.map.pop(&KeyRef{k: k}); }
}
}
self.map.pop(&KeyRef{k: unsafe { &(*lru).key }});
}
}

Expand Down Expand Up @@ -230,19 +212,11 @@ impl<A: fmt::Show + Hash + TotalEq, B: fmt::Show> fmt::Show for LruCache<A, B> {
if i > 0 { try!(write!(f.buf, ", ")) }
unsafe {
cur = (*cur).next;
match (*cur).key {
// should never print nil
None => try!(write!(f.buf, "nil")),
Some(ref k) => try!(write!(f.buf, "{}", *k)),
}
try!(write!(f.buf, "{}", (*cur).key));
}
try!(write!(f.buf, ": "));
unsafe {
match (*cur).value {
// should never print nil
None => try!(write!(f.buf, "nil")),
Some(ref value) => try!(write!(f.buf, "{}", *value)),
}
try!(write!(f.buf, "{}", (*cur).value));
}
}
write!(f.buf, r"\}")
Expand All @@ -267,8 +241,11 @@ impl<K: Hash + TotalEq, V> Mutable for LruCache<K, V> {
impl<K, V> Drop for LruCache<K, V> {
fn drop(&mut self) {
unsafe {
let _: ~LruEntry<K, V> = cast::transmute(self.head);
let _: ~LruEntry<K, V> = cast::transmute(self.tail);
let node: ~LruEntry<K, V> = cast::transmute(self.head);
// Prevent compiler from trying to drop the un-initialized field in the sigil node.
let ~LruEntry { key: k, value: v, .. } = node;
cast::forget(k);
cast::forget(v);
}
}
}
Expand Down