Eliminate the need for Options in LruEntry.

LruEntry nodes previously used Option to encapsulate the key and value
fields. This was used merely as a way avoid having values for the sigil
node. Apart from wasting a few bytes for the discriminant, this
cluttered the rest of the code, since these fields always contained
Some on regular nodes as a class invariant.

The Option wrapping was removed, and the values in the sigil field are
initialized using mem::init, so that they don't contain any real data.
This commit is contained in:
Yuri Kunde Schlesner 2014-04-18 08:44:30 -03:00
parent ad4062e8af
commit 9faef77b23
1 changed files with 21 additions and 42 deletions

View File

@ -41,6 +41,7 @@ use std::cast;
use std::container::Container;
use std::hash::Hash;
use std::fmt;
use std::mem;
use std::ptr;
use HashMap;
@ -48,10 +49,10 @@ 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.
@ -76,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(),
}
@ -101,7 +93,7 @@ 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()) },
head: unsafe{ cast::transmute(~mem::uninit::<LruEntry<K, V>>()) },
};
unsafe {
(*cache.head).next = cache.head;
@ -114,23 +106,24 @@ impl<K: Hash + TotalEq, V> LruCache<K, V> {
pub fn put(&mut self, k: K, v: V) {
let (node_ptr, node_opt) = match self.map.find_mut(&KeyRef{k: &k}) {
Some(node) => {
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))
}
};
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.as_ref().unwrap() };
let keyref = unsafe { &(*node_ptr).key };
self.map.swap(KeyRef{k: keyref}, node);
self.attach(node_ptr);
if self.len() > self.capacity() {
@ -146,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 {
@ -168,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)
}
}
@ -191,12 +179,7 @@ impl<K: Hash + TotalEq, V> LruCache<K, V> {
if self.len() > 0 {
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 }});
}
}
@ -229,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"\}")
@ -266,7 +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 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);
}
}
}