Skip to content

Commit 7d725a3

Browse files
committed
auto merge of #13618 : yuriks/rust/lru-cache, r=brson
Just a few space saving optimizations that end up making the code less cluttered too. I'd like to someone to review the last commit closely, I don't have much experience with writing unsafe code, I had someone walk me through how to use cast::forget in IRC.
2 parents e6c8c7c + 9faef77 commit 7d725a3

File tree

1 file changed

+35
-58
lines changed

1 file changed

+35
-58
lines changed

src/libcollections/lru_cache.rs

+35-58
Original file line numberDiff line numberDiff line change
@@ -41,25 +41,25 @@ use std::cast;
4141
use std::container::Container;
4242
use std::hash::Hash;
4343
use std::fmt;
44+
use std::mem;
4445
use std::ptr;
4546

4647
use HashMap;
4748

4849
struct KeyRef<K> { k: *K }
4950

5051
struct LruEntry<K, V> {
51-
key: Option<K>,
52-
value: Option<V>,
5352
next: *mut LruEntry<K, V>,
5453
prev: *mut LruEntry<K, V>,
54+
key: K,
55+
value: V,
5556
}
5657

5758
/// An LRU Cache.
5859
pub struct LruCache<K, V> {
5960
map: HashMap<KeyRef<K>, ~LruEntry<K, V>>,
6061
max_size: uint,
6162
head: *mut LruEntry<K, V>,
62-
tail: *mut LruEntry<K, V>,
6363
}
6464

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

7979
impl<K, V> LruEntry<K, V> {
80-
fn new() -> LruEntry<K, V> {
80+
fn new(k: K, v: V) -> LruEntry<K, V> {
8181
LruEntry {
82-
key: None,
83-
value: None,
84-
next: ptr::mut_null(),
85-
prev: ptr::mut_null(),
86-
}
87-
}
88-
89-
fn with_key_value(k: K, v: V) -> LruEntry<K, V> {
90-
LruEntry {
91-
key: Some(k),
92-
value: Some(v),
82+
key: k,
83+
value: v,
9384
next: ptr::mut_null(),
9485
prev: ptr::mut_null(),
9586
}
@@ -102,41 +93,42 @@ impl<K: Hash + TotalEq, V> LruCache<K, V> {
10293
let cache = LruCache {
10394
map: HashMap::new(),
10495
max_size: capacity,
105-
head: unsafe{ cast::transmute(~LruEntry::<K, V>::new()) },
106-
tail: unsafe{ cast::transmute(~LruEntry::<K, V>::new()) },
96+
head: unsafe{ cast::transmute(~mem::uninit::<LruEntry<K, V>>()) },
10797
};
10898
unsafe {
109-
(*cache.head).next = cache.tail;
110-
(*cache.tail).prev = cache.head;
99+
(*cache.head).next = cache.head;
100+
(*cache.head).prev = cache.head;
111101
}
112102
return cache;
113103
}
114104

115105
/// Put a key-value pair into cache.
116106
pub fn put(&mut self, k: K, v: V) {
117-
let mut key_existed = false;
118107
let (node_ptr, node_opt) = match self.map.find_mut(&KeyRef{k: &k}) {
119108
Some(node) => {
120-
key_existed = true;
121-
node.value = Some(v);
109+
node.value = v;
122110
let node_ptr: *mut LruEntry<K, V> = &mut **node;
123111
(node_ptr, None)
124112
}
125113
None => {
126-
let mut node = ~LruEntry::with_key_value(k, v);
114+
let mut node = ~LruEntry::new(k, v);
127115
let node_ptr: *mut LruEntry<K, V> = &mut *node;
128116
(node_ptr, Some(node))
129117
}
130118
};
131-
if key_existed {
132-
self.detach(node_ptr);
133-
self.attach(node_ptr);
134-
} else {
135-
let keyref = unsafe { (*node_ptr).key.as_ref().unwrap() };
136-
self.map.swap(KeyRef{k: keyref}, node_opt.unwrap());
137-
self.attach(node_ptr);
138-
if self.len() > self.capacity() {
139-
self.remove_lru();
119+
match node_opt {
120+
None => {
121+
// Existing node, just update LRU position
122+
self.detach(node_ptr);
123+
self.attach(node_ptr);
124+
}
125+
Some(node) => {
126+
let keyref = unsafe { &(*node_ptr).key };
127+
self.map.swap(KeyRef{k: keyref}, node);
128+
self.attach(node_ptr);
129+
if self.len() > self.capacity() {
130+
self.remove_lru();
131+
}
140132
}
141133
}
142134
}
@@ -147,12 +139,7 @@ impl<K: Hash + TotalEq, V> LruCache<K, V> {
147139
None => (None, None),
148140
Some(node) => {
149141
let node_ptr: *mut LruEntry<K, V> = &mut **node;
150-
unsafe {
151-
match (*node_ptr).value {
152-
None => (None, None),
153-
Some(ref value) => (Some(value), Some(node_ptr))
154-
}
155-
}
142+
(Some(unsafe { &(*node_ptr).value }), Some(node_ptr))
156143
}
157144
};
158145
match node_ptr_opt {
@@ -169,7 +156,7 @@ impl<K: Hash + TotalEq, V> LruCache<K, V> {
169156
pub fn pop(&mut self, k: &K) -> Option<V> {
170157
match self.map.pop(&KeyRef{k: k}) {
171158
None => None,
172-
Some(lru_entry) => lru_entry.value
159+
Some(lru_entry) => Some(lru_entry.value)
173160
}
174161
}
175162

@@ -190,14 +177,9 @@ impl<K: Hash + TotalEq, V> LruCache<K, V> {
190177
#[inline]
191178
fn remove_lru(&mut self) {
192179
if self.len() > 0 {
193-
let lru = unsafe { (*self.tail).prev };
180+
let lru = unsafe { (*self.head).prev };
194181
self.detach(lru);
195-
unsafe {
196-
match (*lru).key {
197-
None => (),
198-
Some(ref k) => { self.map.pop(&KeyRef{k: k}); }
199-
}
200-
}
182+
self.map.pop(&KeyRef{k: unsafe { &(*lru).key }});
201183
}
202184
}
203185

@@ -230,19 +212,11 @@ impl<A: fmt::Show + Hash + TotalEq, B: fmt::Show> fmt::Show for LruCache<A, B> {
230212
if i > 0 { try!(write!(f.buf, ", ")) }
231213
unsafe {
232214
cur = (*cur).next;
233-
match (*cur).key {
234-
// should never print nil
235-
None => try!(write!(f.buf, "nil")),
236-
Some(ref k) => try!(write!(f.buf, "{}", *k)),
237-
}
215+
try!(write!(f.buf, "{}", (*cur).key));
238216
}
239217
try!(write!(f.buf, ": "));
240218
unsafe {
241-
match (*cur).value {
242-
// should never print nil
243-
None => try!(write!(f.buf, "nil")),
244-
Some(ref value) => try!(write!(f.buf, "{}", *value)),
245-
}
219+
try!(write!(f.buf, "{}", (*cur).value));
246220
}
247221
}
248222
write!(f.buf, r"\}")
@@ -267,8 +241,11 @@ impl<K: Hash + TotalEq, V> Mutable for LruCache<K, V> {
267241
impl<K, V> Drop for LruCache<K, V> {
268242
fn drop(&mut self) {
269243
unsafe {
270-
let _: ~LruEntry<K, V> = cast::transmute(self.head);
271-
let _: ~LruEntry<K, V> = cast::transmute(self.tail);
244+
let node: ~LruEntry<K, V> = cast::transmute(self.head);
245+
// Prevent compiler from trying to drop the un-initialized field in the sigil node.
246+
let ~LruEntry { key: k, value: v, .. } = node;
247+
cast::forget(k);
248+
cast::forget(v);
272249
}
273250
}
274251
}

0 commit comments

Comments
 (0)