#![feature(box_patterns)]
use std::mem;
use std::ptr;
use std::hash::{Hash, Hasher};
use std::collections::HashMap;
struct KeyRef<K> {
k: *const K,
}
impl<K: Hash> Hash for KeyRef<K> {
fn hash<H: Hasher>(&self, state: &mut H) {
unsafe { (*self.k).hash(state) }
}
}
impl<K: PartialEq> PartialEq for KeyRef<K> {
fn eq(&self, other: &KeyRef<K>) -> bool {
unsafe { (*self.k).eq(&*other.k) }
}
}
impl<K: Eq> Eq for KeyRef<K> {}
struct LruEntry<K, V> {
key: K,
val: V,
prev: *mut LruEntry<K, V>,
next: *mut LruEntry<K, V>,
}
impl<K, V> LruEntry<K, V> {
fn new(key: K, val: V) -> Self {
LruEntry {
key: key,
val: val,
prev: ptr::null_mut(),
next: ptr::null_mut(),
}
}
}
pub struct LruCache<K, V> {
map: HashMap<KeyRef<K>, Box<LruEntry<K, V>>>,
cap: usize,
head: *mut LruEntry<K, V>,
tail: *mut LruEntry<K, V>,
}
impl<K: Hash + Eq, V> LruCache<K, V> {
pub fn new(cap: usize) -> LruCache<K, V> {
let mut cache = LruCache {
map: HashMap::with_capacity(cap),
cap: cap,
head: unsafe { Box::into_raw(Box::new(mem::uninitialized::<LruEntry<K, V>>())) },
tail: unsafe { Box::into_raw(Box::new(mem::uninitialized::<LruEntry<K, V>>())) },
};
unsafe {
(*cache.head).next = cache.tail;
(*cache.tail).prev = cache.head;
}
cache
}
pub fn put(&mut self, k: K, v: V) {
let node_ptr = self.map.get_mut(&KeyRef { k: &k }).map(|node| {
let node_ptr: *mut LruEntry<K, V> = &mut **node;
node_ptr
});
match node_ptr {
Some(node_ptr) => {
unsafe { (*node_ptr).val = v };
self.detach(node_ptr);
self.attach(node_ptr);
}
None => {
let mut node = if self.len() == self.cap() {
let old_key = KeyRef { k: unsafe { &(*(*self.tail).prev).key } };
let mut old_node = self.map.remove(&old_key).unwrap();
old_node.key = k;
old_node.val = v;
let node_ptr: *mut LruEntry<K, V> = &mut *old_node;
self.detach(node_ptr);
old_node
} else {
Box::new(LruEntry::new(k, v))
};
let node_ptr: *mut LruEntry<K, V> = &mut *node;
self.attach(node_ptr);
let keyref = unsafe { &(*node_ptr).key };
self.map.insert(KeyRef { k: keyref }, node);
}
}
}
pub fn get<'a>(&'a mut self, k: &K) -> Option<&'a V> {
let key = KeyRef { k: k };
let (node_ptr, value) = match self.map.get_mut(&key) {
None => (None, None),
Some(node) => {
let node_ptr: *mut LruEntry<K, V> = &mut **node;
(Some(node_ptr), Some(unsafe { &(*node_ptr).val }))
}
};
match node_ptr {
None => (),
Some(node_ptr) => {
self.detach(node_ptr);
self.attach(node_ptr);
}
}
value
}
pub fn peek<'a>(&'a self, k: &K) -> Option<&'a V> {
let key = KeyRef { k: k };
match self.map.get(&key) {
None => None,
Some(node) => Some(&node.val),
}
}
pub fn contains(&self, k: &K) -> bool {
let key = KeyRef { k: k };
self.map.contains_key(&key)
}
pub fn pop(&mut self, k: &K) -> Option<V> {
let key = KeyRef { k: k };
match self.map.remove(&key) {
None => None,
Some(lru_entry) => Some(lru_entry.val),
}
}
pub fn len(&self) -> usize {
self.map.len()
}
pub fn cap(&self) -> usize {
self.cap
}
fn detach(&mut self, node: *mut LruEntry<K, V>) {
unsafe {
(*(*node).prev).next = (*node).next;
(*(*node).next).prev = (*node).prev;
}
}
#[inline]
fn attach(&mut self, node: *mut LruEntry<K, V>) {
unsafe {
(*node).next = (*self.head).next;
(*node).prev = self.head;
(*self.head).next = node;
(*(*node).next).prev = node;
}
}
}
impl<K, V> Drop for LruCache<K, V> {
fn drop(&mut self) {
unsafe {
let head = Box::from_raw(self.head);
let tail = Box::from_raw(self.tail);
let box internal_head = head;
let box internal_tail = tail;
let LruEntry { next: _, prev: _, key: head_key, val: head_val } = internal_head;
let LruEntry { next: _, prev: _, key: tail_key, val: tail_val } = internal_tail;
mem::forget(head_key);
mem::forget(head_val);
mem::forget(tail_key);
mem::forget(tail_val);
}
}
}
#[cfg(test)]
mod tests {
use std::fmt::Debug;
use super::LruCache;
fn assert_opt_eq<V: PartialEq + Debug>(opt: Option<&V>, v: V) {
assert!(opt.is_some());
assert_eq!(opt.unwrap(), &v);
}
#[test]
fn test_put_and_get() {
let mut cache: LruCache<String, String> = LruCache::new(2);
cache.put("apple".to_string(), "red".to_string());
cache.put("banana".to_string(), "yellow".to_string());
assert_eq!(cache.cap(), 2);
assert_eq!(cache.len(), 2);
assert_opt_eq(cache.get(&"apple".to_string()), "red".to_string());
assert_opt_eq(cache.get(&"banana".to_string()), "yellow".to_string());
}
#[test]
fn test_put_update() {
let mut cache = LruCache::new(1);
cache.put("apple".to_string(), "red".to_string());
cache.put("apple".to_string(), "green".to_string());
assert_eq!(cache.len(), 1);
assert_opt_eq(cache.get(&"apple".to_string()), "green".to_string());
}
#[test]
fn test_put_removes_oldest() {
let mut cache = LruCache::new(2);
cache.put("apple".to_string(), "red".to_string());
cache.put("banana".to_string(), "yellow".to_string());
cache.put("pear".to_string(), "green".to_string());
assert!(cache.get(&"apple".to_string()).is_none());
assert_opt_eq(cache.get(&"banana".to_string()), "yellow".to_string());
assert_opt_eq(cache.get(&"pear".to_string()), "green".to_string());
cache.put("banana".to_string(), "green".to_string());
cache.put("tomato".to_string(), "red".to_string());
assert!(cache.get(&"pear".to_string()).is_none());
assert_opt_eq(cache.get(&"banana".to_string()), "green".to_string());
assert_opt_eq(cache.get(&"tomato".to_string()), "red".to_string());
}
#[test]
fn test_peek() {
let mut cache: LruCache<String, String> = LruCache::new(2);
cache.put("apple".to_string(), "red".to_string());
cache.put("banana".to_string(), "yellow".to_string());
assert_opt_eq(cache.peek(&"banana".to_string()), "yellow".to_string());
assert_opt_eq(cache.peek(&"apple".to_string()), "red".to_string());
cache.put("pear".to_string(), "green".to_string());
assert!(cache.peek(&"apple".to_string()).is_none());
assert_opt_eq(cache.peek(&"banana".to_string()), "yellow".to_string());
assert_opt_eq(cache.peek(&"pear".to_string()), "green".to_string());
}
#[test]
fn test_contains() {
let mut cache = LruCache::new(2);
cache.put("apple".to_string(), "red".to_string());
cache.put("banana".to_string(), "yellow".to_string());
cache.put("pear".to_string(), "green".to_string());
assert!(!cache.contains(&"apple".to_string()));
assert!(cache.contains(&"banana".to_string()));
assert!(cache.contains(&"pear".to_string()));
}
#[test]
fn test_pop() {
let mut cache = LruCache::new(2);
cache.put("apple".to_string(), "red".to_string());
cache.put("banana".to_string(), "yellow".to_string());
assert_eq!(cache.len(), 2);
assert_opt_eq(cache.get(&"apple".to_string()), "red".to_string());
assert_opt_eq(cache.get(&"banana".to_string()), "yellow".to_string());
let popped = cache.pop(&"apple".to_string());
assert!(popped.is_some());
assert_eq!(popped.unwrap(), "red".to_string());
assert_eq!(cache.len(), 1);
assert!(cache.get(&"apple".to_string()).is_none());
assert_opt_eq(cache.get(&"banana".to_string()), "yellow".to_string());
}
}