extern crate linked_hash_map;
use linked_hash_map::LinkedHashMap;
use std::hash::Hash;
use std::sync::{Mutex, Arc};
use std::fmt;
struct MultiCacheItem<V> {
val: V,
bytes: usize,
}
impl<V> MultiCacheItem<V> {
pub fn new(val: Arc<V>, bytes: usize) -> MultiCacheItem<Arc<V>> {
MultiCacheItem {
val: val,
bytes: bytes,
}
}
}
struct MultiCacheParts<K,V> {
hash: LinkedHashMap<K,MultiCacheItem<Arc<V>>>,
totalsize: usize,
maxsize: usize,
}
impl<K,V> fmt::Debug for MultiCacheParts<K,V> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{{ {} totalsize, {} maxsize }}",
self.totalsize, self.maxsize)
}
}
#[derive(Debug)]
pub struct MultiCache<K,V> {
parts: Mutex<MultiCacheParts<K,V>>,
}
impl<K,V> MultiCache<K,V> {
pub fn new(bytesize: usize) -> MultiCache<K,V>
where K: Hash+Eq {
MultiCache {
parts: Mutex::new(MultiCacheParts{
hash: LinkedHashMap::new(),
totalsize: 0,
maxsize: bytesize,
}),
}
}
pub fn put(&self, key: K, value: V, bytes: usize)
where K: Hash+Eq {
self.put_arc(key, Arc::new(value), bytes)
}
pub fn put_arc(&self, key: K, value: Arc<V>, bytes: usize)
where K: Hash+Eq {
self.remove(&key);
let mut mparts = self.parts.lock().unwrap();
while mparts.totalsize + bytes > mparts.maxsize {
match mparts.hash.pop_front() {
None => break, Some(val) => {
mparts.totalsize -= val.1.bytes;
}
}
}
(*mparts).hash.insert(key, MultiCacheItem::new(value,bytes));
mparts.totalsize += bytes;
}
pub fn get(&self, key: &K) -> Option<Arc<V>>
where K: Hash+Eq {
let mparts = &mut *(self.parts.lock().unwrap());
if let Some(val) = mparts.hash.get_refresh(key) {
return Some(val.val.clone())
}
None
}
pub fn remove(&self, key: &K) -> Option<Arc<V>>
where K: Hash+Eq {
let mut mparts = self.parts.lock().unwrap();
if let Some(val) = (*mparts).hash.remove(&key) {
mparts.totalsize -= val.bytes;
Some(val.val)
} else {
None
}
}
pub fn contains_key(&self, key: &K) -> bool
where K: Hash+Eq {
let mparts = self.parts.lock().unwrap();
if (*mparts).hash.contains_key(&key) {
return true
}
false
}
}
#[cfg(test)]
mod tests {
use super::MultiCache;
use std::sync::Arc;
#[test]
fn evicts() {
let cache = MultiCache::new(200);
cache.put(0, 0, 100);
cache.put(1, 1, 100);
cache.put(2, 2, 100);
assert_eq!(cache.get(&2), Some(Arc::new(2)));
assert_eq!(cache.get(&1), Some(Arc::new(1)));
assert_eq!(cache.get(&0), None);
}
#[test]
fn evicts_no_repeats() {
let cache = MultiCache::new(200);
cache.put(0, 0, 100);
cache.put(1, 1, 100);
cache.put(1, 2, 100);
cache.put(1, 3, 100);
assert_eq!(cache.get(&1), Some(Arc::new(3)));
assert_eq!(cache.get(&0), Some(Arc::new(0)));
}
#[test]
fn get_refreshes() {
let cache = MultiCache::new(200);
cache.put(0, 0, 100);
cache.put(1, 1, 100);
cache.get(&0);
cache.put(2, 2, 100);
assert_eq!(cache.get(&0), Some(Arc::new(0)));
assert_eq!(cache.get(&1), None);
assert_eq!(cache.get(&2), Some(Arc::new(2)));
}
#[test]
fn contains() {
let cache = MultiCache::new(100);
cache.put(0, 0, 100);
assert_eq!(cache.contains_key(&0), true);
assert_eq!(cache.contains_key(&2), false);
cache.put(2, 2, 100);
assert_eq!(cache.contains_key(&0), false);
assert_eq!(cache.contains_key(&2), true);
}
#[test]
fn puts() {
let cache = MultiCache::new(200);
cache.put(0, 0, 100);
cache.put_arc(1, Arc::new(1), 100);
assert_eq!(cache.get(&0), Some(Arc::new(0)));
assert_eq!(cache.get(&1), Some(Arc::new(1)));
}
#[test]
fn removes() {
let cache = MultiCache::new(200);
cache.put(0, 0, 100);
assert_eq!(cache.remove(&0), Some(Arc::new(0)));
assert_eq!(cache.remove(&0), None);
assert_eq!(cache.get(&0), None);
}
}