Skip to content

Commit 16048d8

Browse files
authored
Merge pull request #206 from traceflight/mru
Add peek_mru pop_mru
2 parents 2d18d2d + 066d9bf commit 16048d8

File tree

1 file changed

+127
-0
lines changed

1 file changed

+127
-0
lines changed

src/lib.rs

+127
Original file line numberDiff line numberDiff line change
@@ -1063,6 +1063,37 @@ impl<K: Hash + Eq, V, S: BuildHasher> LruCache<K, V, S> {
10631063
Some((key, val))
10641064
}
10651065

1066+
/// Returns the value corresponding to the most recently used item or `None` if the
1067+
/// cache is empty. Like `peek`, `peek_mru` does not update the LRU list so the item's
1068+
/// position will be unchanged.
1069+
///
1070+
/// # Example
1071+
///
1072+
/// ```
1073+
/// use lru::LruCache;
1074+
/// use std::num::NonZeroUsize;
1075+
/// let mut cache = LruCache::new(NonZeroUsize::new(2).unwrap());
1076+
///
1077+
/// cache.put(1, "a");
1078+
/// cache.put(2, "b");
1079+
///
1080+
/// assert_eq!(cache.peek_mru(), Some((&2, &"b")));
1081+
/// ```
1082+
pub fn peek_mru(&self) -> Option<(&K, &V)> {
1083+
if self.is_empty() {
1084+
return None;
1085+
}
1086+
1087+
let (key, val);
1088+
unsafe {
1089+
let node: *mut LruEntry<K, V> = (*self.head).next;
1090+
key = &(*(*node).key.as_ptr()) as &K;
1091+
val = &(*(*node).val.as_ptr()) as &V;
1092+
}
1093+
1094+
Some((key, val))
1095+
}
1096+
10661097
/// Returns a bool indicating whether the given key is in the cache. Does not update the
10671098
/// LRU list.
10681099
///
@@ -1194,6 +1225,34 @@ impl<K: Hash + Eq, V, S: BuildHasher> LruCache<K, V, S> {
11941225
unsafe { Some((key.assume_init(), val.assume_init())) }
11951226
}
11961227

1228+
/// Removes and returns the key and value corresponding to the most recently
1229+
/// used item or `None` if the cache is empty.
1230+
///
1231+
/// # Example
1232+
///
1233+
/// ```
1234+
/// use lru::LruCache;
1235+
/// use std::num::NonZeroUsize;
1236+
/// let mut cache = LruCache::new(NonZeroUsize::new(2).unwrap());
1237+
///
1238+
/// cache.put(2, "a");
1239+
/// cache.put(3, "b");
1240+
/// cache.put(4, "c");
1241+
/// cache.get(&3);
1242+
///
1243+
/// assert_eq!(cache.pop_mru(), Some((3, "b")));
1244+
/// assert_eq!(cache.pop_mru(), Some((4, "c")));
1245+
/// assert_eq!(cache.pop_mru(), None);
1246+
/// assert_eq!(cache.len(), 0);
1247+
/// ```
1248+
pub fn pop_mru(&mut self) -> Option<(K, V)> {
1249+
let node = self.remove_first()?;
1250+
// N.B.: Can't destructure directly because of https://github.com/rust-lang/rust/issues/28536
1251+
let node = *node;
1252+
let LruEntry { key, val, .. } = node;
1253+
unsafe { Some((key.assume_init(), val.assume_init())) }
1254+
}
1255+
11971256
/// Marks the key as the most recently used one.
11981257
///
11991258
/// # Example
@@ -1440,6 +1499,22 @@ impl<K: Hash + Eq, V, S: BuildHasher> LruCache<K, V, S> {
14401499
}
14411500
}
14421501

1502+
fn remove_first(&mut self) -> Option<Box<LruEntry<K, V>>> {
1503+
let next;
1504+
unsafe { next = (*self.head).next }
1505+
if next != self.tail {
1506+
let old_key = KeyRef {
1507+
k: unsafe { &(*(*(*self.head).next).key.as_ptr()) },
1508+
};
1509+
let old_node = self.map.remove(&old_key).unwrap();
1510+
let node_ptr: *mut LruEntry<K, V> = old_node.as_ptr();
1511+
self.detach(node_ptr);
1512+
unsafe { Some(Box::from_raw(node_ptr)) }
1513+
} else {
1514+
None
1515+
}
1516+
}
1517+
14431518
fn remove_last(&mut self) -> Option<Box<LruEntry<K, V>>> {
14441519
let prev;
14451520
unsafe { prev = (*self.tail).prev }
@@ -2095,6 +2170,23 @@ mod tests {
20952170
assert!(cache.peek_lru().is_none());
20962171
}
20972172

2173+
#[test]
2174+
fn test_peek_mru() {
2175+
let mut cache = LruCache::new(NonZeroUsize::new(2).unwrap());
2176+
2177+
assert!(cache.peek_mru().is_none());
2178+
2179+
cache.put("apple", "red");
2180+
cache.put("banana", "yellow");
2181+
assert_opt_eq_tuple(cache.peek_mru(), ("banana", "yellow"));
2182+
2183+
cache.get(&"apple");
2184+
assert_opt_eq_tuple(cache.peek_mru(), ("apple", "red"));
2185+
2186+
cache.clear();
2187+
assert!(cache.peek_mru().is_none());
2188+
}
2189+
20982190
#[test]
20992191
fn test_contains() {
21002192
let mut cache = LruCache::new(NonZeroUsize::new(2).unwrap());
@@ -2180,6 +2272,41 @@ mod tests {
21802272
}
21812273
}
21822274

2275+
#[test]
2276+
fn test_pop_mru() {
2277+
let mut cache = LruCache::new(NonZeroUsize::new(200).unwrap());
2278+
2279+
for i in 0..75 {
2280+
cache.put(i, "A");
2281+
}
2282+
for i in 0..75 {
2283+
cache.put(i + 100, "B");
2284+
}
2285+
for i in 0..75 {
2286+
cache.put(i + 200, "C");
2287+
}
2288+
assert_eq!(cache.len(), 200);
2289+
2290+
for i in 0..75 {
2291+
assert_opt_eq(cache.get(&(74 - i + 100)), "B");
2292+
}
2293+
assert_opt_eq(cache.get(&25), "A");
2294+
2295+
assert_eq!(cache.pop_mru(), Some((25, "A")));
2296+
for i in 0..75 {
2297+
assert_eq!(cache.pop_mru(), Some((i + 100, "B")));
2298+
}
2299+
for i in 0..75 {
2300+
assert_eq!(cache.pop_mru(), Some((74 - i + 200, "C")));
2301+
}
2302+
for i in (26..75).into_iter().rev() {
2303+
assert_eq!(cache.pop_mru(), Some((i, "A")));
2304+
}
2305+
for _ in 0..50 {
2306+
assert_eq!(cache.pop_mru(), None);
2307+
}
2308+
}
2309+
21832310
#[test]
21842311
fn test_clear() {
21852312
let mut cache = LruCache::new(NonZeroUsize::new(2).unwrap());

0 commit comments

Comments
 (0)