Skip to content
Open
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
63 changes: 50 additions & 13 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ extern crate linked_hash_map;

use std::borrow::Borrow;
use std::collections::hash_map::RandomState;
use std::fmt;
use std::fmt::{self, Debug};
use std::hash::{Hash, BuildHasher};

use linked_hash_map::LinkedHashMap;
Expand All @@ -51,6 +51,31 @@ mod heapsize;

// FIXME(conventions): implement indexing?

/// The type returned by the `insert` function.
#[derive(Clone)]
pub struct Insert<K, V> {
pub old_value: Option<V>,
pub lru_removed: Option<(K, V)>,
}

impl<K: Eq + Hash, V: PartialEq> PartialEq for Insert<K, V> {
fn eq(&self, other: &Insert<K, V>) -> bool {
self.old_value == other.old_value
&& self.lru_removed == self.lru_removed
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. There is a typo in this line where both sides of the equality reference self. The latter should be other`:
&& self.lru_removed == other.lru_removed
  1. Could we derive this form of PartialEq? It is an element-by-element check for eq, which is the default for #[derive(PartialEq)]

}
}

impl<K: Eq + Hash, V: Eq> Eq for Insert<K, V> { }

impl<K: Eq + Hash + Debug, V: Debug> Debug for Insert<K, V> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Insert")
.field("old_value", &self.old_value)
.field("lru_removed", &self.lru_removed)
.finish()
}
}

/// An LRU cache.
#[derive(Clone)]
pub struct LruCache<K: Eq + Hash, V, S: BuildHasher = RandomState> {
Expand Down Expand Up @@ -101,7 +126,7 @@ impl<K: Eq + Hash, V, S: BuildHasher> LruCache<K, V, S> {
}

/// Inserts a key-value pair into the cache. If the key already existed, the old value is
/// returned.
/// returned. Removes and returns the least recently used key-value pair if necessary.
///
/// # Examples
///
Expand All @@ -115,12 +140,16 @@ impl<K: Eq + Hash, V, S: BuildHasher> LruCache<K, V, S> {
/// assert_eq!(cache.get_mut(&1), Some(&mut "a"));
/// assert_eq!(cache.get_mut(&2), Some(&mut "b"));
/// ```
pub fn insert(&mut self, k: K, v: V) -> Option<V> {
let old_val = self.map.insert(k, v);
if self.len() > self.capacity() {
self.remove_lru();
}
old_val
pub fn insert(&mut self, k: K, v: V) -> Insert<K, V> {
let old_value = self.map.insert(k, v);

let lru_removed = if self.len() > self.capacity() {
self.remove_lru()
} else {
None
};

Insert { old_value, lru_removed }
}

/// Returns a mutable reference to the value corresponding to the given key in the cache, if
Expand Down Expand Up @@ -184,8 +213,10 @@ impl<K: Eq + Hash, V, S: BuildHasher> LruCache<K, V, S> {
self.max_size
}

/// Sets the number of key-value pairs the cache can hold. Removes
/// least-recently-used key-value pairs if necessary.
/// Sets the number of key-value pairs the cache can hold.
/// Removes and returns least-recently-used key-value pairs
/// in the least-to-most recently used order (least at the front),
/// if necessary.
///
/// # Examples
///
Expand Down Expand Up @@ -216,14 +247,20 @@ impl<K: Eq + Hash, V, S: BuildHasher> LruCache<K, V, S> {
/// assert_eq!(cache.get_mut(&2), None);
/// assert_eq!(cache.get_mut(&3), Some(&mut "c"));
/// ```
pub fn set_capacity(&mut self, capacity: usize) {
pub fn set_capacity(&mut self, capacity: usize) -> Vec<(K, V)> {
let cap = capacity.saturating_sub(self.len());
let mut removed_lrus = Vec::with_capacity(cap);

for _ in capacity..self.len() {
self.remove_lru();
let removed = self.remove_lru().unwrap();
removed_lrus.push(removed);
}
Comment on lines +251 to 257
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would using an iterator to create removed_lrus be as space efficient as your capacity specific vector?

I don't know how the efficiency compares, but I prefer reading the iterator version:

let removed_lrus = capacity..self.len()
    .filter_map(|_|: self.remove_lru())
    .collect();


self.max_size = capacity;
removed_lrus
}

/// Removes and returns the least recently used key-value pair as a tuple.
/// Removes and returns the least recently used key-value pair.
///
/// # Examples
///
Expand Down