Mercurial > lbo > hg > leveldb-rs
changeset 101:827cc92fd49a
Implement Cache
author | Lewin Bormann <lbo@spheniscida.de> |
---|---|
date | Sun, 02 Oct 2016 16:14:04 +0200 |
parents | f95c3a6be507 |
children | 3055c564cb2e |
files | src/block_cache.rs src/table_reader.rs |
diffstat | 2 files changed, 164 insertions(+), 8 deletions(-) [+] |
line wrap: on
line diff
--- a/src/block_cache.rs Sun Oct 02 13:25:56 2016 +0200 +++ b/src/block_cache.rs Sun Oct 02 16:14:04 2016 +0200 @@ -1,8 +1,7 @@ -use block::BlockContents; - use std::collections::HashMap; use std::mem::{swap, replace, transmute_copy}; +// No clone, no copy! That asserts that an LRUHandle exists only once. type LRUHandle<T> = *mut LRUNode<T>; struct LRUNode<T> { @@ -84,6 +83,25 @@ } } + fn remove(&mut self, node_handle: LRUHandle<T>) -> T { + unsafe { + // If has next + if let Some(ref mut nextp) = (*node_handle).next { + swap(&mut (**nextp).prev, &mut (*node_handle).prev); + } + // If has prev + if let Some(ref mut prevp) = (*node_handle).prev { + // swap prev.next + // (node_handle will own itself now) + swap(&mut (**prevp).next, &mut (*node_handle).next); + } + + self.count -= 1; + // node_handle now only has references/objects that point to itself, + // so it's safe to drop + replace(&mut (*node_handle).data, None).unwrap() + } + } /// Reinserts the referenced node at the front. fn reinsert_front(&mut self, node_handle: LRUHandle<T>) { @@ -131,21 +149,160 @@ } } -type CacheHandle = usize; +pub type CacheKey = Vec<u8>; +type CacheEntry<T> = (T, LRUHandle<CacheKey>); /// Implementation of `ShardedLRUCache`. /// Based on a HashMap; the elements are linked in order to support the LRU ordering. -pub struct BlockCache { - list: LRUList<CacheHandle>, - map: HashMap<CacheHandle, BlockContents>, - handle_counter: CacheHandle, +pub struct Cache<T> { + // note: CacheKeys (Vec<u8>) are duplicated between list and map. If this turns out to be a + // performance bottleneck, another layer of indirection™ can solve this by mapping the key to a + // numeric handle that keys both list and map. + list: LRUList<CacheKey>, + map: HashMap<CacheKey, CacheEntry<T>>, + cap: usize, +} + +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, + } + } + + /// How many the cache currently contains + pub fn count(&self) -> usize { + return self.list.count(); + } + + /// The capacity of this cache + pub fn cap(&self) -> usize { + return self.cap; + } + + /// Insert a new element into the cache. The returned `CacheHandle` can be used for further + /// operations on that element. + /// If the capacity has been reached, the least recently used element is removed from the + /// cache. + 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.clone()); + self.map.insert(key.clone(), (elem, lru_handle)); + } + + /// Retrieve an element from the cache. + /// If the element has been preempted from the cache in the meantime, this returns None. + pub fn get<'a>(&'a mut self, key: &CacheKey) -> Option<&'a T> { + match self.map.get(key) { + None => None, + Some(&(ref elem, ref lru_handle)) => { + self.list.reinsert_front(*lru_handle); + Some(elem) + } + } + } + + /// Remove an element from the cache (for invalidation). + 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::*; use super::LRUList; #[test] + fn test_blockcache_cache_add_rm() { + let mut cache = Cache::new(128); + + let h_123 = "aaa".as_bytes().to_vec(); + let h_521 = "aab".as_bytes().to_vec(); + let h_372 = "aac".as_bytes().to_vec(); + let h_332 = "aad".as_bytes().to_vec(); + let h_899 = "aae".as_bytes().to_vec(); + + 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 = "aaa".as_bytes().to_vec(); + let h_521 = "aab".as_bytes().to_vec(); + let h_372 = "aac".as_bytes().to_vec(); + let h_332 = "aad".as_bytes().to_vec(); + let h_899 = "aae".as_bytes().to_vec(); + + 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();