Skip to content

Commit d902e71

Browse files
committed
Add lru_cache to libextra
1 parent 8a3b35f commit d902e71

File tree

2 files changed

+366
-0
lines changed

2 files changed

+366
-0
lines changed

src/libextra/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ pub mod sort;
7171
pub mod dlist;
7272
pub mod treemap;
7373
pub mod btree;
74+
pub mod lru_cache;
7475

7576
// And ... other stuff
7677

src/libextra/lru_cache.rs

+365
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,365 @@
1+
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
12+
//! A cache that holds a limited number of key-value pairs. When the
13+
//! capacity of the cache is exceeded, the least-recently-used
14+
//! (where "used" means a look-up or putting the pair into the cache)
15+
//! pair is automatically removed.
16+
//!
17+
//! # Example
18+
//!
19+
//! ```rust
20+
//! let mut cache: LruCache<int, int> = LruCache::new(2);
21+
//! cache.put(1, 10);
22+
//! cache.put(2, 20);
23+
//! cache.put(3, 30);
24+
//! assert!(cache.get(&1).is_none());
25+
//! assert_eq!(*cache.get(&2).unwrap(), 20);
26+
//! assert_eq!(*cache.get(&3).unwrap(), 30);
27+
//!
28+
//! cache.put(2, 22);
29+
//! assert_eq!(*cache.get(&2).unwrap(), 22);
30+
//!
31+
//! cache.put(6, 60);
32+
//! assert!(cache.get(&3).is_none());
33+
//!
34+
//! cache.change_capacity(1);
35+
//! assert!(cache.get(&2).is_none());
36+
//! ```
37+
38+
use std::container::Container;
39+
use std::hashmap::HashMap;
40+
use std::to_bytes::Cb;
41+
use std::ptr;
42+
use std::cast;
43+
44+
struct KeyRef<K> { priv k: *K }
45+
46+
struct LruEntry<K, V> {
47+
priv key: Option<K>,
48+
priv value: Option<V>,
49+
priv next: *mut LruEntry<K, V>,
50+
priv prev: *mut LruEntry<K, V>,
51+
}
52+
53+
/// An LRU Cache.
54+
pub struct LruCache<K, V> {
55+
priv map: HashMap<KeyRef<K>, ~LruEntry<K, V>>,
56+
priv max_size: uint,
57+
priv head: *mut LruEntry<K, V>,
58+
priv tail: *mut LruEntry<K, V>,
59+
}
60+
61+
impl<K: IterBytes> IterBytes for KeyRef<K> {
62+
fn iter_bytes(&self, lsb0: bool, f: Cb) -> bool {
63+
unsafe{ (*self.k).iter_bytes(lsb0, f) }
64+
}
65+
}
66+
67+
impl<K: Eq> Eq for KeyRef<K> {
68+
fn eq(&self, other: &KeyRef<K>) -> bool {
69+
unsafe{ (*self.k).eq(&*other.k) }
70+
}
71+
}
72+
73+
impl<K, V> LruEntry<K, V> {
74+
fn new() -> LruEntry<K, V> {
75+
LruEntry {
76+
key: None,
77+
value: None,
78+
next: ptr::mut_null(),
79+
prev: ptr::mut_null(),
80+
}
81+
}
82+
83+
fn with_key_value(k: K, v: V) -> LruEntry<K, V> {
84+
LruEntry {
85+
key: Some(k),
86+
value: Some(v),
87+
next: ptr::mut_null(),
88+
prev: ptr::mut_null(),
89+
}
90+
}
91+
}
92+
93+
impl<K: IterBytes + Eq, V> LruCache<K, V> {
94+
/// Create an LRU Cache that holds at most `capacity` items.
95+
pub fn new(capacity: uint) -> LruCache<K, V> {
96+
let cache = LruCache {
97+
map: HashMap::new(),
98+
max_size: capacity,
99+
head: unsafe{ cast::transmute(~LruEntry::<K, V>::new()) },
100+
tail: unsafe{ cast::transmute(~LruEntry::<K, V>::new()) },
101+
};
102+
unsafe {
103+
(*cache.head).next = cache.tail;
104+
(*cache.tail).prev = cache.head;
105+
}
106+
return cache;
107+
}
108+
109+
/// Put a key-value pair into cache.
110+
pub fn put(&mut self, k: K, v: V) {
111+
let mut key_existed = false;
112+
let (node_ptr, node_opt) = match self.map.find_mut(&KeyRef{k: &k}) {
113+
Some(node) => {
114+
key_existed = true;
115+
node.value = Some(v);
116+
let node_ptr: *mut LruEntry<K, V> = &mut **node;
117+
(node_ptr, None)
118+
}
119+
None => {
120+
let mut node = ~LruEntry::with_key_value(k, v);
121+
let node_ptr: *mut LruEntry<K, V> = &mut *node;
122+
(node_ptr, Some(node))
123+
}
124+
};
125+
if key_existed {
126+
self.detach(node_ptr);
127+
self.attach(node_ptr);
128+
} else {
129+
let keyref = unsafe { (*node_ptr).key.as_ref().unwrap() };
130+
self.map.swap(KeyRef{k: keyref}, node_opt.unwrap());
131+
self.attach(node_ptr);
132+
if self.len() > self.capacity() {
133+
self.remove_lru();
134+
}
135+
}
136+
}
137+
138+
/// Return a value corresponding to the key in the cache.
139+
pub fn get<'a>(&'a mut self, k: &K) -> Option<&'a V> {
140+
let (value, node_ptr_opt) = match self.map.find_mut(&KeyRef{k: k}) {
141+
None => (None, None),
142+
Some(node) => {
143+
let node_ptr: *mut LruEntry<K, V> = &mut **node;
144+
unsafe {
145+
match (*node_ptr).value {
146+
None => (None, None),
147+
Some(ref value) => (Some(value), Some(node_ptr))
148+
}
149+
}
150+
}
151+
};
152+
match node_ptr_opt {
153+
None => (),
154+
Some(node_ptr) => {
155+
self.detach(node_ptr);
156+
self.attach(node_ptr);
157+
}
158+
}
159+
return value;
160+
}
161+
162+
/// Remove and return a value corresponding to the key from the cache.
163+
pub fn pop(&mut self, k: &K) -> Option<V> {
164+
match self.map.pop(&KeyRef{k: k}) {
165+
None => None,
166+
Some(lru_entry) => lru_entry.value
167+
}
168+
}
169+
170+
/// Return the maximum number of key-value pairs the cache can hold.
171+
pub fn capacity(&self) -> uint {
172+
self.max_size
173+
}
174+
175+
/// Change the number of key-value pairs the cache can hold. Remove
176+
/// least-recently-used key-value pairs if necessary.
177+
pub fn change_capacity(&mut self, capacity: uint) {
178+
for _ in range(capacity, self.len()) {
179+
self.remove_lru();
180+
}
181+
self.max_size = capacity;
182+
}
183+
184+
#[inline]
185+
fn remove_lru(&mut self) {
186+
if self.len() > 0 {
187+
let lru = unsafe { (*self.tail).prev };
188+
self.detach(lru);
189+
unsafe {
190+
match (*lru).key {
191+
None => (),
192+
Some(ref k) => { self.map.pop(&KeyRef{k: k}); }
193+
}
194+
}
195+
}
196+
}
197+
198+
#[inline]
199+
fn detach(&mut self, node: *mut LruEntry<K, V>) {
200+
unsafe {
201+
(*(*node).prev).next = (*node).next;
202+
(*(*node).next).prev = (*node).prev;
203+
}
204+
}
205+
206+
#[inline]
207+
fn attach(&mut self, node: *mut LruEntry<K, V>) {
208+
unsafe {
209+
(*node).next = (*self.head).next;
210+
(*node).prev = self.head;
211+
(*self.head).next = node;
212+
(*(*node).next).prev = node;
213+
}
214+
}
215+
}
216+
217+
impl<A: ToStr + IterBytes + Eq, B: ToStr> ToStr for LruCache<A, B> {
218+
/// Return a string that lists the key-value pairs from most-recently
219+
/// used to least-recently used.
220+
#[inline]
221+
fn to_str(&self) -> ~str {
222+
let mut acc = ~"{";
223+
let mut cur = self.head;
224+
for i in range(0, self.len()) {
225+
if i > 0 {
226+
acc.push_str(", ");
227+
}
228+
unsafe {
229+
cur = (*cur).next;
230+
match (*cur).key {
231+
// should never print nil
232+
None => acc.push_str("nil"),
233+
Some(ref k) => acc.push_str(k.to_str())
234+
}
235+
}
236+
acc.push_str(": ");
237+
unsafe {
238+
match (*cur).value {
239+
// should never print nil
240+
None => acc.push_str("nil"),
241+
Some(ref value) => acc.push_str(value.to_str())
242+
}
243+
}
244+
}
245+
acc.push_char('}');
246+
acc
247+
}
248+
}
249+
250+
impl<K: IterBytes + Eq, V> Container for LruCache<K, V> {
251+
/// Return the number of key-value pairs in the cache.
252+
fn len(&self) -> uint {
253+
self.map.len()
254+
}
255+
}
256+
257+
impl<K: IterBytes + Eq, V> Mutable for LruCache<K, V> {
258+
/// Clear the cache of all key-value pairs.
259+
fn clear(&mut self) {
260+
self.map.clear();
261+
}
262+
}
263+
264+
#[unsafe_destructor]
265+
impl<K, V> Drop for LruCache<K, V> {
266+
fn drop(&mut self) {
267+
unsafe {
268+
let _: ~LruEntry<K, V> = cast::transmute(self.head);
269+
let _: ~LruEntry<K, V> = cast::transmute(self.tail);
270+
}
271+
}
272+
}
273+
274+
#[cfg(test)]
275+
mod tests {
276+
use super::LruCache;
277+
278+
fn assert_opt_eq<V: Eq>(opt: Option<&V>, v: V) {
279+
assert!(opt.is_some());
280+
assert_eq!(opt.unwrap(), &v);
281+
}
282+
283+
#[test]
284+
fn test_put_and_get() {
285+
let mut cache: LruCache<int, int> = LruCache::new(2);
286+
cache.put(1, 10);
287+
cache.put(2, 20);
288+
assert_opt_eq(cache.get(&1), 10);
289+
assert_opt_eq(cache.get(&2), 20);
290+
assert_eq!(cache.len(), 2);
291+
}
292+
293+
#[test]
294+
fn test_put_update() {
295+
let mut cache: LruCache<~str, ~[u8]> = LruCache::new(1);
296+
cache.put(~"1", ~[10, 10]);
297+
cache.put(~"1", ~[10, 19]);
298+
assert_opt_eq(cache.get(&~"1"), ~[10, 19]);
299+
assert_eq!(cache.len(), 1);
300+
}
301+
302+
#[test]
303+
fn test_expire_lru() {
304+
let mut cache: LruCache<~str, ~str> = LruCache::new(2);
305+
cache.put(~"foo1", ~"bar1");
306+
cache.put(~"foo2", ~"bar2");
307+
cache.put(~"foo3", ~"bar3");
308+
assert!(cache.get(&~"foo1").is_none());
309+
cache.put(~"foo2", ~"bar2update");
310+
cache.put(~"foo4", ~"bar4");
311+
assert!(cache.get(&~"foo3").is_none());
312+
}
313+
314+
#[test]
315+
fn test_pop() {
316+
let mut cache: LruCache<int, int> = LruCache::new(2);
317+
cache.put(1, 10);
318+
cache.put(2, 20);
319+
assert_eq!(cache.len(), 2);
320+
let opt1 = cache.pop(&1);
321+
assert!(opt1.is_some());
322+
assert_eq!(opt1.unwrap(), 10);
323+
assert!(cache.get(&1).is_none());
324+
assert_eq!(cache.len(), 1);
325+
}
326+
327+
#[test]
328+
fn test_change_capacity() {
329+
let mut cache: LruCache<int, int> = LruCache::new(2);
330+
assert_eq!(cache.capacity(), 2);
331+
cache.put(1, 10);
332+
cache.put(2, 20);
333+
cache.change_capacity(1);
334+
assert!(cache.get(&1).is_none());
335+
assert_eq!(cache.capacity(), 1);
336+
}
337+
338+
#[test]
339+
fn test_to_str() {
340+
let mut cache: LruCache<int, int> = LruCache::new(3);
341+
cache.put(1, 10);
342+
cache.put(2, 20);
343+
cache.put(3, 30);
344+
assert_eq!(cache.to_str(), ~"{3: 30, 2: 20, 1: 10}");
345+
cache.put(2, 22);
346+
assert_eq!(cache.to_str(), ~"{2: 22, 3: 30, 1: 10}");
347+
cache.put(6, 60);
348+
assert_eq!(cache.to_str(), ~"{6: 60, 2: 22, 3: 30}");
349+
cache.get(&3);
350+
assert_eq!(cache.to_str(), ~"{3: 30, 6: 60, 2: 22}");
351+
cache.change_capacity(2);
352+
assert_eq!(cache.to_str(), ~"{3: 30, 6: 60}");
353+
}
354+
355+
#[test]
356+
fn test_clear() {
357+
let mut cache: LruCache<int, int> = LruCache::new(2);
358+
cache.put(1, 10);
359+
cache.put(2, 20);
360+
cache.clear();
361+
assert!(cache.get(&1).is_none());
362+
assert!(cache.get(&2).is_none());
363+
assert_eq!(cache.to_str(), ~"{}");
364+
}
365+
}

0 commit comments

Comments
 (0)