use std::collections::HashMap;
use std::mem::swap;
struct LRUHandle<T>(*mut LRUNode<T>);
struct LRUNode<T> {
next: Option<Box<LRUNode<T>>>, prev: Option<*mut LRUNode<T>>,
data: Option<T>, }
struct LRUList<T> {
head: LRUNode<T>,
count: usize,
}
impl<T> LRUList<T> {
fn new() -> LRUList<T> {
LRUList {
head: LRUNode {
data: None,
next: None,
prev: None,
},
count: 0,
}
}
fn insert(&mut self, elem: T) -> LRUHandle<T> {
self.count += 1;
if self.head.next.is_some() {
let mut new = Box::new(LRUNode {
data: Some(elem),
next: None,
prev: Some(&mut self.head as *mut LRUNode<T>),
});
let newp = new.as_mut() as *mut LRUNode<T>;
self.head.next.as_mut().unwrap().prev = Some(newp);
new.next = self.head.next.take();
self.head.next = Some(new);
LRUHandle(newp)
} else {
let mut new = Box::new(LRUNode {
data: Some(elem),
next: None,
prev: Some(&mut self.head as *mut LRUNode<T>),
});
let newp = new.as_mut() as *mut LRUNode<T>;
self.head.prev = Some(newp);
self.head.next = Some(new);
LRUHandle(newp)
}
}
fn remove_last(&mut self) -> Option<T> {
if self.count() == 0 {
return None;
}
let mut lasto = unsafe { (*((*self.head.prev.unwrap()).prev.unwrap())).next.take() };
assert!(lasto.is_some());
if let Some(ref mut last) = lasto {
assert!(last.prev.is_some());
assert!(self.head.prev.is_some());
self.head.prev = last.prev;
self.count -= 1;
last.data.take()
} else {
None
}
}
fn remove(&mut self, node_handle: LRUHandle<T>) -> T {
unsafe {
let d = (*node_handle.0).data.take().unwrap();
let mut current = (*(*node_handle.0).prev.unwrap()).next.take().unwrap();
let prev = current.prev.unwrap();
if current.next.is_some() {
current.next.as_mut().unwrap().prev = current.prev.take();
}
(*prev).next = current.next.take();
self.count -= 1;
d
}
}
fn reinsert_front(&mut self, node_handle: &LRUHandle<T>) {
unsafe {
let prevp = (*node_handle.0).prev.unwrap();
if let Some(next) = (*node_handle.0).next.as_mut() {
next.prev = Some(prevp);
} else {
self.head.prev = Some(prevp);
}
swap(&mut (*prevp).next, &mut (*node_handle.0).next);
swap(&mut (*node_handle.0).next, &mut self.head.next);
if let Some(ref mut newnext) = (*node_handle.0).next {
(*node_handle.0).prev = newnext.prev;
newnext.prev = Some(node_handle.0);
} else {
self.head.prev = Some(node_handle.0);
}
assert!(self.head.next.is_some());
assert!(self.head.prev.is_some());
}
}
fn count(&self) -> usize {
self.count
}
fn _testing_head_ref(&self) -> Option<&T> {
if let Some(ref first) = self.head.next {
first.data.as_ref()
} else {
None
}
}
}
pub type CacheKey = [u8; 16];
pub type CacheID = u64;
type CacheEntry<T> = (T, LRUHandle<CacheKey>);
pub struct Cache<T> {
list: LRUList<CacheKey>,
map: HashMap<CacheKey, CacheEntry<T>>,
cap: usize,
id: u64,
}
impl<T> Cache<T> {
pub fn new(capacity: usize) -> Cache<T> {
assert!(capacity > 0);
Cache {
list: LRUList::new(),
map: HashMap::with_capacity(1024),
cap: capacity,
id: 0,
}
}
pub fn new_cache_id(&mut self) -> CacheID {
self.id += 1;
self.id
}
pub fn count(&self) -> usize {
self.list.count()
}
pub fn cap(&self) -> usize {
self.cap
}
pub fn insert(&mut self, key: &CacheKey, elem: T) {
if self.list.count() >= self.cap {
if let Some(removed_key) = self.list.remove_last() {
assert!(self.map.remove(&removed_key).is_some());
} else {
panic!("could not remove_last(); bug!");
}
}
let lru_handle = self.list.insert(*key);
self.map.insert(*key, (elem, lru_handle));
}
pub fn get<'a>(&'a mut self, key: &CacheKey) -> Option<&'a T> {
match self.map.get(key) {
None => None,
Some((elem, lru_handle)) => {
self.list.reinsert_front(lru_handle);
Some(elem)
}
}
}
pub fn remove(&mut self, key: &CacheKey) -> Option<T> {
match self.map.remove(key) {
None => None,
Some((elem, lru_handle)) => {
self.list.remove(lru_handle);
Some(elem)
}
}
}
}
#[cfg(test)]
mod tests {
use super::LRUList;
use super::*;
fn make_key(a: u8, b: u8, c: u8) -> CacheKey {
[a, b, c, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
}
#[test]
fn test_blockcache_cache_add_rm() {
let mut cache = Cache::new(128);
let h_123 = make_key(1, 2, 3);
let h_521 = make_key(1, 2, 4);
let h_372 = make_key(3, 4, 5);
let h_332 = make_key(6, 3, 1);
let h_899 = make_key(8, 2, 1);
cache.insert(&h_123, 123);
cache.insert(&h_332, 332);
cache.insert(&h_521, 521);
cache.insert(&h_372, 372);
cache.insert(&h_899, 899);
assert_eq!(cache.count(), 5);
assert_eq!(cache.get(&h_123), Some(&123));
assert_eq!(cache.get(&h_372), Some(&372));
assert_eq!(cache.remove(&h_521), Some(521));
assert_eq!(cache.get(&h_521), None);
assert_eq!(cache.remove(&h_521), None);
assert_eq!(cache.count(), 4);
}
#[test]
fn test_blockcache_cache_capacity() {
let mut cache = Cache::new(3);
let h_123 = make_key(1, 2, 3);
let h_521 = make_key(1, 2, 4);
let h_372 = make_key(3, 4, 5);
let h_332 = make_key(6, 3, 1);
let h_899 = make_key(8, 2, 1);
cache.insert(&h_123, 123);
cache.insert(&h_332, 332);
cache.insert(&h_521, 521);
cache.insert(&h_372, 372);
cache.insert(&h_899, 899);
assert_eq!(cache.count(), 3);
assert_eq!(cache.get(&h_123), None);
assert_eq!(cache.get(&h_332), None);
assert_eq!(cache.get(&h_521), Some(&521));
assert_eq!(cache.get(&h_372), Some(&372));
assert_eq!(cache.get(&h_899), Some(&899));
}
#[test]
fn test_blockcache_lru_remove() {
let mut lru = LRUList::<usize>::new();
let h_56 = lru.insert(56);
lru.insert(22);
lru.insert(223);
let h_244 = lru.insert(244);
lru.insert(1111);
let h_12 = lru.insert(12);
assert_eq!(lru.count(), 6);
assert_eq!(244, lru.remove(h_244));
assert_eq!(lru.count(), 5);
assert_eq!(12, lru.remove(h_12));
assert_eq!(lru.count(), 4);
assert_eq!(56, lru.remove(h_56));
assert_eq!(lru.count(), 3);
}
#[test]
fn test_blockcache_lru_1() {
let mut lru = LRUList::<usize>::new();
lru.insert(56);
lru.insert(22);
lru.insert(244);
lru.insert(12);
assert_eq!(lru.count(), 4);
assert_eq!(Some(56), lru.remove_last());
assert_eq!(Some(22), lru.remove_last());
assert_eq!(Some(244), lru.remove_last());
assert_eq!(lru.count(), 1);
assert_eq!(Some(12), lru.remove_last());
assert_eq!(lru.count(), 0);
assert_eq!(None, lru.remove_last());
}
#[test]
fn test_blockcache_lru_reinsert() {
let mut lru = LRUList::<usize>::new();
let handle1 = lru.insert(56);
let handle2 = lru.insert(22);
let handle3 = lru.insert(244);
assert_eq!(lru._testing_head_ref().copied().unwrap(), 244);
lru.reinsert_front(&handle1);
assert_eq!(lru._testing_head_ref().copied().unwrap(), 56);
lru.reinsert_front(&handle3);
assert_eq!(lru._testing_head_ref().copied().unwrap(), 244);
lru.reinsert_front(&handle2);
assert_eq!(lru._testing_head_ref().copied().unwrap(), 22);
assert_eq!(lru.remove_last(), Some(56));
assert_eq!(lru.remove_last(), Some(244));
assert_eq!(lru.remove_last(), Some(22));
}
#[test]
fn test_blockcache_lru_reinsert_2() {
let mut lru = LRUList::<usize>::new();
let handles = [
lru.insert(0),
lru.insert(1),
lru.insert(2),
lru.insert(3),
lru.insert(4),
lru.insert(5),
lru.insert(6),
lru.insert(7),
lru.insert(8),
];
(0..9).for_each(|i| {
lru.reinsert_front(&handles[i]);
assert_eq!(lru._testing_head_ref().copied(), Some(i));
});
}
#[test]
fn test_blockcache_lru_edge_cases() {
let mut lru = LRUList::<usize>::new();
let handle = lru.insert(3);
lru.reinsert_front(&handle);
assert_eq!(lru._testing_head_ref().copied(), Some(3));
assert_eq!(lru.remove_last(), Some(3));
assert_eq!(lru.remove_last(), None);
assert_eq!(lru.remove_last(), None);
}
}