#![doc(html_logo_url =
"https://raw.githubusercontent.com/maidsafe/QA/master/Images/maidsafe_logo.png",
html_favicon_url = "http://maidsafe.net/img/favicon.ico",
html_root_url = "http://maidsafe.github.io/lru_time_cache")]
#![forbid(bad_style, exceeding_bitshifts, mutable_transmutes, no_mangle_const_items,
unknown_crate_types, warnings)]
#![deny(deprecated, drop_with_repr_extern, improper_ctypes, missing_docs,
non_shorthand_field_patterns, overflowing_literals, plugin_as_library,
private_no_mangle_fns, private_no_mangle_statics, stable_features, unconditional_recursion,
unknown_lints, unsafe_code, unused, unused_allocation, unused_attributes,
unused_comparisons, unused_features, unused_parens, while_true)]
#![warn(trivial_casts, trivial_numeric_casts, unused_extern_crates, unused_import_braces,
unused_qualifications, unused_results)]
#![allow(box_pointers, fat_ptr_transmutes, missing_copy_implementations,
missing_debug_implementations, variant_size_differences)]
#![cfg_attr(feature="clippy", feature(plugin))]
#![cfg_attr(feature="clippy", plugin(clippy))]
#![cfg_attr(feature="clippy", deny(clippy, clippy_pedantic))]
#![cfg_attr(feature="clippy", allow(use_debug))]
#[cfg(test)]
extern crate rand;
use std::collections::{BTreeMap, VecDeque};
use std::time::{Instant, Duration};
pub enum Entry<'a, Key: 'a, Value: 'a> {
Vacant(VacantEntry<'a, Key, Value>),
Occupied(OccupiedEntry<'a, Value>),
}
pub struct VacantEntry<'a, Key: 'a, Value: 'a> {
key: Key,
cache: &'a mut LruCache<Key, Value>,
}
pub struct OccupiedEntry<'a, Value: 'a> {
value: &'a mut Value,
}
pub struct LruCache<Key, Value> {
map: BTreeMap<Key, (Value, Instant)>,
list: VecDeque<Key>,
capacity: usize,
time_to_live: Duration,
}
impl<Key, Value> LruCache<Key, Value>
where Key: PartialOrd + Ord + Clone
{
pub fn with_capacity(capacity: usize) -> LruCache<Key, Value> {
LruCache {
map: BTreeMap::new(),
list: VecDeque::new(),
capacity: capacity,
time_to_live: Duration::new(std::u64::MAX, 999_999_999),
}
}
pub fn with_expiry_duration(time_to_live: Duration) -> LruCache<Key, Value> {
LruCache {
map: BTreeMap::new(),
list: VecDeque::new(),
capacity: ::std::usize::MAX,
time_to_live: time_to_live,
}
}
pub fn with_expiry_duration_and_capacity(time_to_live: Duration,
capacity: usize)
-> LruCache<Key, Value> {
LruCache {
map: BTreeMap::new(),
list: VecDeque::new(),
capacity: capacity,
time_to_live: time_to_live,
}
}
pub fn insert(&mut self, key: Key, value: Value) -> Option<Value> {
if self.map.contains_key(&key) {
Self::update_key(&mut self.list, &key);
} else {
while self.check_time_expired() || self.map.len() == self.capacity {
self.remove_oldest_element();
}
self.list.push_back(key.clone());
}
self.map.insert(key, (value, Instant::now())).map(|pair| pair.0)
}
pub fn remove(&mut self, key: &Key) -> Option<Value> {
self.list.retain(|k| *k < *key || *k > *key);
self.map.remove(key).map(|(value, _)| value)
}
pub fn get(&mut self, key: &Key) -> Option<&Value> {
self.remove_expired();
let list = &mut self.list;
self.map.get_mut(key).map(|result| {
Self::update_key(list, key);
result.1 = Instant::now();
&result.0
})
}
pub fn get_mut(&mut self, key: &Key) -> Option<&mut Value> {
self.remove_expired();
let list = &mut self.list;
self.map.get_mut(key).map(|result| {
Self::update_key(list, key);
result.1 = Instant::now();
&mut result.0
})
}
pub fn contains_key(&self, key: &Key) -> bool {
self.map.contains_key(key) && !self.expired(key)
}
pub fn len(&self) -> usize {
self.map.len() - self.list.iter().take_while(|key| self.expired(key)).count()
}
pub fn is_empty(&self) -> bool {
self.list.iter().all(|key| self.expired(key))
}
pub fn entry(&mut self, key: Key) -> Entry<Key, Value> {
if self.contains_key(&key) {
Entry::Occupied(OccupiedEntry { value: self.get_mut(&key).unwrap() })
} else {
Entry::Vacant(VacantEntry {
key: key,
cache: self,
})
}
}
fn has_expiry(&self) -> bool {
self.time_to_live != Duration::new(std::u64::MAX, 999_999_999)
}
fn expired(&self, key: &Key) -> bool {
let now = Instant::now();
self.has_expiry() && self.map.get(key).map_or(false, |v| v.1 + self.time_to_live < now)
}
fn remove_oldest_element(&mut self) {
let _ = self.list.pop_front().map(|key| assert!(self.map.remove(&key).is_some()));
}
fn check_time_expired(&self) -> bool {
self.has_expiry() && self.list.front().map_or(false, |key| self.expired(key))
}
fn update_key(list: &mut VecDeque<Key>, key: &Key) {
list.retain(|k| *k < *key || *k > *key);
list.push_back(key.clone());
}
fn remove_expired(&mut self) {
while self.check_time_expired() {
self.remove_oldest_element();
}
}
}
impl<Key: PartialOrd + Ord + Clone, Value: Clone> LruCache<Key, Value> {
pub fn retrieve_all(&mut self) -> Vec<(Key, Value)> {
self.remove_expired();
let now = Instant::now();
let mut result = Vec::<(Key, Value)>::with_capacity(self.map.len());
self.map.iter_mut().all(|a| {
result.push((a.0.clone(), (a.1).0.clone()));
(a.1).1 = now;
true
});
result
}
pub fn retrieve_all_ordered(&mut self) -> Vec<(Key, Value)> {
self.remove_expired();
let now = Instant::now();
let mut result = Vec::<(Key, Value)>::with_capacity(self.list.len());
for key in self.list.iter().rev() {
if let Some(value) = self.map.get_mut(key) {
result.push((key.clone(), value.0.clone()));
value.1 = now;
}
}
result
}
}
impl<Key, Value> Clone for LruCache<Key, Value>
where Key: Clone,
Value: Clone
{
fn clone(&self) -> LruCache<Key, Value> {
LruCache {
map: self.map.clone(),
list: self.list.clone(),
capacity: self.capacity,
time_to_live: self.time_to_live,
}
}
}
impl<'a, Key: PartialOrd + Ord + Clone, Value> VacantEntry<'a, Key, Value> {
pub fn insert(self, value: Value) -> &'a mut Value {
let _ = self.cache.insert(self.key.clone(), value);
self.cache.get_mut(&self.key).unwrap()
}
}
impl<'a, Value> OccupiedEntry<'a, Value> {
pub fn into_mut(self) -> &'a mut Value {
self.value
}
}
impl<'a, Key: PartialOrd + Ord + Clone, Value> Entry<'a, Key, Value> {
pub fn or_insert(self, default: Value) -> &'a mut Value {
match self {
Entry::Occupied(entry) => entry.into_mut(),
Entry::Vacant(entry) => entry.insert(default),
}
}
pub fn or_insert_with<F: FnOnce() -> Value>(self, default: F) -> &'a mut Value {
match self {
Entry::Occupied(entry) => entry.into_mut(),
Entry::Vacant(entry) => entry.insert(default()),
}
}
}
#[cfg(test)]
mod test {
use std::time::Duration;
fn generate_random_vec<T>(len: usize) -> Vec<T>
where T: ::rand::Rand
{
let mut vec = Vec::<T>::with_capacity(len);
for _ in 0..len {
vec.push(::rand::random::<T>());
}
vec
}
#[test]
fn size_only() {
let size = 10usize;
let mut lru_cache = super::LruCache::<usize, usize>::with_capacity(size);
for i in 0..10 {
assert_eq!(lru_cache.len(), i);
let _ = lru_cache.insert(i, i);
assert_eq!(lru_cache.len(), i + 1);
}
for i in 10..1000 {
let _ = lru_cache.insert(i, i);
assert_eq!(lru_cache.len(), size);
}
for _ in (0..1000).rev() {
assert!(lru_cache.contains_key(&(1000 - 1)));
assert!(lru_cache.get(&(1000 - 1)).is_some());
assert_eq!(*lru_cache.get(&(1000 - 1)).unwrap(), 1000 - 1);
}
}
#[test]
fn time_only() {
let time_to_live = Duration::from_millis(100);
let mut lru_cache = super::LruCache::<usize, usize>::with_expiry_duration(time_to_live);
for i in 0..10 {
assert_eq!(lru_cache.len(), i);
let _ = lru_cache.insert(i, i);
assert_eq!(lru_cache.len(), i + 1);
}
let duration = Duration::from_millis(100);
::std::thread::sleep(duration);
let _ = lru_cache.insert(11, 11);
assert_eq!(lru_cache.len(), 1);
for i in 0..10 {
assert!(!lru_cache.is_empty());
assert_eq!(lru_cache.len(), i + 1);
let _ = lru_cache.insert(i, i);
assert_eq!(lru_cache.len(), i + 2);
}
::std::thread::sleep(duration);
assert_eq!(0, lru_cache.len());
assert!(lru_cache.is_empty());
}
#[test]
fn time_only_check() {
let time_to_live = Duration::from_millis(50);
let mut lru_cache = super::LruCache::<usize, usize>::with_expiry_duration(time_to_live);
assert_eq!(lru_cache.len(), 0);
let _ = lru_cache.insert(0, 0);
assert_eq!(lru_cache.len(), 1);
let duration = Duration::from_millis(100);
::std::thread::sleep(duration);
assert!(!lru_cache.contains_key(&0));
assert_eq!(lru_cache.len(), 0);
}
#[test]
fn time_and_size() {
let size = 10usize;
let time_to_live = Duration::from_millis(100);
let mut lru_cache =
super::LruCache::<usize, usize>::with_expiry_duration_and_capacity(time_to_live, size);
for i in 0..1000 {
if i < size {
assert_eq!(lru_cache.len(), i);
}
let _ = lru_cache.insert(i, i);
if i < size {
assert_eq!(lru_cache.len(), i + 1);
} else {
assert_eq!(lru_cache.len(), size);
}
}
let duration = Duration::from_millis(100);
::std::thread::sleep(duration);
let _ = lru_cache.insert(1, 1);
assert_eq!(lru_cache.len(), 1);
}
#[test]
fn time_size_struct_value() {
let size = 100usize;
let time_to_live = Duration::from_millis(100);
#[derive(PartialEq, PartialOrd, Ord, Clone, Eq)]
struct Temp {
id: Vec<u8>,
}
let mut lru_cache =
super::LruCache::<Temp, usize>::with_expiry_duration_and_capacity(time_to_live, size);
for i in 0..1000 {
if i < size {
assert_eq!(lru_cache.len(), i);
}
let _ = lru_cache.insert(Temp { id: generate_random_vec::<u8>(64) }, i);
if i < size {
assert_eq!(lru_cache.len(), i + 1);
} else {
assert_eq!(lru_cache.len(), size);
}
}
let duration = Duration::from_millis(100);
::std::thread::sleep(duration);
let _ = lru_cache.insert(Temp { id: generate_random_vec::<u8>(64) }, 1);
assert_eq!(lru_cache.len(), 1);
}
#[test]
fn retrieve_all() {
let size = 10usize;
let mut lru_cache = super::LruCache::<usize, usize>::with_capacity(size);
for i in 0..10 {
let _ = lru_cache.insert(i, i);
}
let all = lru_cache.retrieve_all();
assert_eq!(all.len(), lru_cache.map.len());
assert!(all.iter()
.all(|a| lru_cache.contains_key(&a.0) && *lru_cache.get(&a.0).unwrap() == a.1));
}
#[test]
fn retrieve_all_ordered() {
let size = 10usize;
let mut lru_cache = super::LruCache::<usize, usize>::with_capacity(size);
for i in 0..10 {
let _ = lru_cache.insert(i, i);
}
let all = lru_cache.retrieve_all_ordered();
assert_eq!(all.len(), lru_cache.map.len());
for i in all.iter().rev() {
lru_cache.remove_oldest_element();
assert!(!lru_cache.contains_key(&i.0) && lru_cache.get(&i.0).is_none());
}
}
#[test]
fn update_time_check() {
let time_to_live = Duration::from_millis(50);
let mut lru_cache = super::LruCache::<usize, usize>::with_expiry_duration(time_to_live);
assert_eq!(lru_cache.len(), 0);
let _ = lru_cache.insert(0, 0);
assert_eq!(lru_cache.len(), 1);
let duration = Duration::from_millis(30);
::std::thread::sleep(duration);
{
let result = lru_cache.get(&0);
assert!(result.is_some());
let value = result.unwrap();
assert_eq!(*value, 0);
}
::std::thread::sleep(duration);
{
let result = lru_cache.get(&0);
assert!(result.is_some());
let value = result.unwrap();
assert_eq!(*value, 0);
}
}
}