From 18cc5d3adfbed6360fb5eba00093ea371830d75f Mon Sep 17 00:00:00 2001 From: tickbh Date: Mon, 24 Jun 2024 14:16:12 +0800 Subject: [PATCH] add ttl --- Cargo.lock | 2 +- Cargo.toml | 5 +- src/arr/fix_vec.rs | 56 +++---- src/cache/arc.rs | 258 ++++++++++++++++++++++++++++--- src/cache/lfu.rs | 331 ++++++++++++++++++++++++++++++++++++++-- src/cache/lru.rs | 79 +++++++--- src/cache/lruk.rs | 368 ++++++++++++++++++++++++++++++++++++++++++--- src/cache/mod.rs | 7 +- 8 files changed, 987 insertions(+), 119 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b446593..a0c967c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -16,7 +16,7 @@ dependencies = [ [[package]] name = "algorithm" -version = "0.1.6" +version = "0.1.7" dependencies = [ "hashbrown", "lazy_static", diff --git a/Cargo.toml b/Cargo.toml index 6eae234..6ac2014 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,9 +1,9 @@ [package] name = "algorithm" -version = "0.1.6" +version = "0.1.7" edition = "2021" authors = ["tickbh "] -description = "about algorithm data structure, now has lru/lru-k/lfu/slab/rbtree/roaring_bitmap/timer_wheelss, 关于算法常用的数据结构" +description = "about algorithm data structure, now has ttl with lru/lru-k/lfu/arc and slab/rbtree/roaring_bitmap/timer_wheelss, 关于算法常用的数据结构" repository = "https://github.com/tickbh/algorithm-rs" license = "Apache-2.0" keywords = ["arc", "lru", "lfu", "timerwheel", "slab"] @@ -24,5 +24,6 @@ opt-level = 3 debug = true [features] +default = ["hashbrown", "ttl"] hashbrown=[] ttl=[] \ No newline at end of file diff --git a/src/arr/fix_vec.rs b/src/arr/fix_vec.rs index e9adb42..f4abeb0 100644 --- a/src/arr/fix_vec.rs +++ b/src/arr/fix_vec.rs @@ -1,5 +1,4 @@ use std::cmp::Ordering; -use std::ptr::NonNull; #[derive(Debug)] struct FixedVecNode { @@ -400,10 +399,16 @@ impl FixedVec { /// ``` #[inline] pub fn iter_mut(&mut self) -> FixedVecIterMut<'_, T> { - let head = self.head; - let tail = self.tail; - let len = self.len(); - FixedVecIterMut::new(&mut self.nodes, head, tail, len) + FixedVecIterMut { + head: self.head, + tail: self.tail, + len: self.len(), + list: self, + } + // let head = self.head; + // let tail = self.tail; + // let len = self.len(); + // FixedVecIterMut::new(self, head, tail, len) } fn reorder(&mut self) { @@ -670,46 +675,23 @@ impl<'a, T> DoubleEndedIterator for FixedVecIter<'a, T> { } } +#[derive(Debug)] pub struct FixedVecIterMut<'a, T> { - ptr: NonNull>>, + list: &'a mut FixedVec, head: usize, tail: usize, len: usize, - _marker: std::marker::PhantomData<&'a mut T>, -} - -impl<'a, T> FixedVecIterMut<'a, T> { - #[allow(unsafe_code)] - fn new( - slice: &'a mut [Option>], - head: usize, - tail: usize, - len: usize, - ) -> Self { - let ptr = slice.as_mut_ptr(); - Self { - ptr: unsafe { NonNull::new_unchecked(ptr) }, - head, - tail, - len, - _marker: std::marker::PhantomData, - } - } } impl<'a, T> Iterator for FixedVecIterMut<'a, T> { type Item = (usize, &'a mut T); - #[allow(unsafe_code)] fn next(&mut self) -> Option { if self.len > 0 { let head = self.head; - let node_ref = unsafe { - let ptr = NonNull::new_unchecked(self.ptr.as_ptr().add(head)).as_ptr(); - &mut *ptr + let node = unsafe { + core::mem::transmute::<&'_ mut FixedVecNode, &'a mut FixedVecNode>(self.list.node_mut(head).unwrap()) }; - - let node = node_ref.as_mut().unwrap(); self.head = node.next; self.len -= 1; Some((head, &mut node.data)) @@ -724,16 +706,12 @@ impl<'a, T> Iterator for FixedVecIterMut<'a, T> { } impl<'a, T> DoubleEndedIterator for FixedVecIterMut<'a, T> { - #[allow(unsafe_code)] fn next_back(&mut self) -> Option { if self.len > 0 { let tail = self.tail; - let node_ref = unsafe { - let ptr = NonNull::new_unchecked(self.ptr.as_ptr().add(tail)).as_ptr(); - &mut *ptr + let node = unsafe { + core::mem::transmute::<&'_ mut FixedVecNode, &'a mut FixedVecNode>(self.list.node_mut(tail).unwrap()) }; - - let node = node_ref.as_mut().unwrap(); self.tail = node.prev; self.len -= 1; Some((tail, &mut node.data)) @@ -741,4 +719,4 @@ impl<'a, T> DoubleEndedIterator for FixedVecIterMut<'a, T> { None } } -} +} \ No newline at end of file diff --git a/src/cache/arc.rs b/src/cache/arc.rs index 557011d..49ad4ea 100644 --- a/src/cache/arc.rs +++ b/src/cache/arc.rs @@ -22,6 +22,11 @@ use crate::{LfuCache, LruCache}; use super::{lfu, lru}; +#[cfg(feature = "ttl")] +use crate::get_milltimestamp; +#[cfg(feature = "ttl")] +const DEFAULT_CHECK_STEP: u64 = 120; + /// ARC(Adaptive Replacement Cache): 自适应缓存替换算法,它结合了LRU与LFU,来获得可用缓存的最佳使用。 /// 设置容量之后将最大保持该容量大小的数据 /// 后进的数据将会淘汰最久没有被访问的数据 @@ -51,6 +56,15 @@ pub struct ArcCache { ghost_lfu: LruCache, cap: usize, + /// 下一次检查的时间点,如果大于该时间点则全部检查是否过期 + #[cfg(feature = "ttl")] + check_next: u64, + /// 每次大检查点的时间间隔,如果不想启用该特性,可以将该值设成u64::MAX + #[cfg(feature = "ttl")] + check_step: u64, + /// 所有节点中是否存在带ttl的结点,如果均为普通的元素,则过期的将不进行检查 + #[cfg(feature = "ttl")] + has_ttl: bool, } impl Default for ArcCache { @@ -78,12 +92,37 @@ impl ArcCache { ghost_lfu: LruCache::with_hasher(cap, hash_builder), cap, + #[cfg(feature = "ttl")] + check_step: DEFAULT_CHECK_STEP, + #[cfg(feature = "ttl")] + check_next: get_milltimestamp()+DEFAULT_CHECK_STEP * 1000, + #[cfg(feature = "ttl")] + has_ttl: false, } } } impl ArcCache { + /// 获取当前检查lru的间隔 + #[cfg(feature="ttl")] + pub fn get_check_step(&self) -> u64 { + self.check_step + } + + /// 设置当前检查lru的间隔 + /// 单位为秒,意思就是每隔多少秒会清理一次数据 + /// 如果数据太大的话遍历一次可能会比较久的时长 + /// 一次清理时间复杂度O(n) + /// 仅仅在插入时触发检查,获取时仅检查当前元素 + #[cfg(feature="ttl")] + pub fn set_check_step(&mut self, check_step: u64) { + self.check_step = check_step; + self.check_next = get_milltimestamp() + self.check_step * 1000; + self.main_lru.set_check_step(check_step); + self.main_lfu.set_check_step(check_step); + } + /// 获取当前容量 pub fn capacity(&self) -> usize { self.cap @@ -245,24 +284,6 @@ impl ArcCache { } impl ArcCache { - // /// 排出当前数据 - // /// - // /// ``` - // /// use algorithm::ArcCache; - // /// fn main() { - // /// let mut arc = ArcCache::new(3); - // /// arc.insert("hello", "algorithm"); - // /// arc.insert("this", "arc"); - // /// { - // /// let mut drain = arc.drain(); - // /// assert!(drain.next()==Some(("hello", "algorithm"))); - // /// } - // /// assert!(arc.len() == 0); - // /// } - // /// ``` - // pub fn drain(&mut self) -> Drain<'_, K, V, S> { - // Drain { base: self } - // } /// 弹出栈顶上的数据, 最常使用的数据 /// @@ -428,6 +449,40 @@ impl ArcCache { self.get_mut_key_value(k).map(|(_, v)| v) } + #[cfg(feature="ttl")] + pub fn get_mut_key_value(&mut self, k: &Q) -> Option<(&K, &mut V)> + where + K: Borrow, + Q: Hash + Eq + ?Sized, + { + // { + // if let Some(v) = self.main_lfu.get_mut_key_value(k) { + // return Some(v) + // } + // } + if let Some((key, val, ttl)) = self.main_lru.remove_with_ttl(k) { + self.main_lfu.insert_with_ttl(key, val, ttl); + return self.main_lfu.get_mut_key_value(k); + } + + if let Some((key, val, ttl)) = self.ghost_lfu.remove_with_ttl(k) { + self.main_lfu.full_increase(); + self.main_lru.full_decrease(); + self.main_lfu.insert_with_ttl(key, val, ttl); + return self.main_lfu.get_mut_key_value(k); + } + + if let Some((key, val, ttl)) = self.ghost_lru.remove_with_ttl(k) { + self.main_lru.full_increase(); + self.main_lfu.full_decrease(); + self.main_lru.insert_with_ttl(key, val, ttl); + return self.main_lru.get_mut_key_value(k); + } + self.main_lfu.get_mut_key_value(k) + } + + + #[cfg(not(feature="ttl"))] pub fn get_mut_key_value(&mut self, k: &Q) -> Option<(&K, &mut V)> where K: Borrow, @@ -470,11 +525,50 @@ impl ArcCache { /// assert!(arc.insert("this", "arc good") == Some(&"arc")); /// } /// ``` + #[inline(always)] pub fn insert(&mut self, k: K, v: V) -> Option { self.capture_insert(k, v).map(|(_, v, _)| v) } + /// 插入带有生存时间的元素 + /// 每次获取像redis一样,并不会更新生存时间 + /// 如果需要更新则需要手动的进行重新设置 + #[cfg(feature="ttl")] + #[inline(always)] + pub fn insert_with_ttl(&mut self, k: K, v: V, ttl: u64) -> Option { + self.capture_insert_with_ttl(k, v, ttl).map(|(_, v, _)| v) + } + + #[inline(always)] pub fn capture_insert(&mut self, k: K, v: V) -> Option<(K, V, bool)> { + self._capture_insert_with_ttl(k, v, u64::MAX) + } + + #[cfg(feature = "ttl")] + #[inline(always)] + pub fn capture_insert_with_ttl(&mut self, k: K, v: V, ttl: u64) -> Option<(K, V, bool)> { + if ttl == 0 { return None }; + self.has_ttl = true; + self._capture_insert_with_ttl(k, v, ttl) + } + + #[cfg(feature = "ttl")] + #[allow(unused_variables)] + fn _capture_insert_with_ttl(&mut self, k: K, v: V, ttl: u64) -> Option<(K, V, bool)> { + if let Some((key, val, same)) = self.main_lru.capture_insert_with_ttl(k, v, ttl) { + if same { + Some((key, val, true)) + } else { + self.ghost_lru.capture_insert_with_ttl(key, val, ttl) + } + } else { + None + } + } + + #[cfg(not(feature = "ttl"))] + #[allow(unused_variables)] + fn _capture_insert_with_ttl(&mut self, k: K, v: V, ttl: u64) -> Option<(K, V, bool)> { if let Some((key, val, same)) = self.main_lru.capture_insert(k, v) { if same { Some((key, val, true)) @@ -528,6 +622,52 @@ impl ArcCache { } + #[cfg(feature="ttl")] + pub fn clear_expire(&mut self) { + if !self.has_ttl { + return; + } + let now = get_milltimestamp(); + if now < self.check_next { + return; + } + self.check_next = now + self.check_step; + self.main_lfu.clear_expire(); + self.main_lru.clear_expire(); + } + + #[cfg(feature="ttl")] + #[inline(always)] + pub fn del_ttl(&mut self, k: &Q) + where + K: Borrow, + Q: Hash + Eq + ?Sized, { + self.set_ttl(k, u64::MAX); + } + + #[cfg(feature="ttl")] + pub fn set_ttl(&mut self, k: &Q, expire: u64) -> bool + where + K: Borrow, + Q: Hash + Eq + ?Sized, { + + if self.main_lru.set_ttl(k, expire) { + return true + } + self.main_lfu.set_ttl(k, expire) + } + + #[cfg(feature="ttl")] + pub fn get_ttl(&mut self, k: &Q) -> Option + where + K: Borrow, + Q: Hash + Eq + ?Sized, { + if let Some(v) = self.main_lfu.get_ttl(k) { + return Some(v) + } + self.main_lru.get_ttl(k) + } + /// 移除元素 /// /// ``` @@ -596,6 +736,12 @@ impl Clone for ArcCache< ghost_lru: self.ghost_lru.clone(), ghost_lfu: self.ghost_lfu.clone(), cap: self.cap, + #[cfg(feature="ttl")] + check_next: self.check_next, + #[cfg(feature="ttl")] + check_step: self.check_step, + #[cfg(feature="ttl")] + has_ttl: self.has_ttl, } } } @@ -1225,4 +1371,80 @@ mod tests { assert!(handle.join().is_ok()); } + + + #[test] + #[cfg(feature="ttl")] + fn test_ttl_cache() { + let mut lru = ArcCache::new(3); + lru.insert_with_ttl("help", "ok", 1); + lru.insert_with_ttl("author", "tickbh", 2); + assert_eq!(lru.len(), 2); + std::thread::sleep(std::time::Duration::from_secs(1)); + assert_eq!(lru.get("help"), None); + std::thread::sleep(std::time::Duration::from_secs(1)); + assert_eq!(lru.get("author"), None); + assert_eq!(lru.len(), 0); + } + + #[test] + #[cfg(feature="ttl")] + fn test_ttl_check_cache() { + let mut lru = ArcCache::new(3); + lru.set_check_step(1); + lru.insert_with_ttl("help", "ok", 1); + lru.insert("now", "algorithm"); + assert_eq!(lru.len(), 2); + std::thread::sleep(std::time::Duration::from_secs(1)); + assert_eq!(lru.len(), 2); + lru.insert_with_ttl("author", "tickbh", 3); + assert_eq!(lru.len(), 2); + assert_eq!(lru.get("help"), None); + assert_eq!(lru.len(), 2); + } + + #[test] + #[cfg(feature="ttl")] + fn test_ttl_del() { + let mut lru = ArcCache::new(3); + lru.insert_with_ttl("help", "ok", 1); + lru.insert_with_ttl("author", "tickbh", 2); + assert_eq!(lru.len(), 2); + std::thread::sleep(std::time::Duration::from_secs(1)); + assert_eq!(lru.get("help"), None); + lru.del_ttl(&"author"); + std::thread::sleep(std::time::Duration::from_secs(1)); + assert_eq!(lru.get("author"), Some(&"tickbh")); + assert_eq!(lru.len(), 1); + } + + #[test] + #[cfg(feature="ttl")] + fn test_ttl_set() { + let mut lru = ArcCache::new(3); + lru.insert_with_ttl("help", "ok", 1); + lru.insert_with_ttl("author", "tickbh", 2); + lru.set_ttl(&"help", 3); + assert_eq!(lru.len(), 2); + std::thread::sleep(std::time::Duration::from_secs(1)); + assert_eq!(lru.get("help"), Some(&"ok")); + std::thread::sleep(std::time::Duration::from_secs(1)); + assert_eq!(lru.get("author"), None); + std::thread::sleep(std::time::Duration::from_secs(1)); + assert_eq!(lru.get("help"), None); + assert_eq!(lru.len(), 0); + } + + + #[test] + #[cfg(feature="ttl")] + fn test_ttl_get() { + let mut lru = ArcCache::new(3); + lru.insert_with_ttl("help", "ok", 1); + lru.insert_with_ttl("author", "tickbh", 2); + lru.insert("now", "algorithm"); + assert_eq!(lru.get_ttl(&"help"), Some(1)); + assert_eq!(lru.get_ttl(&"author"), Some(2)); + assert_eq!(lru.get_ttl(&"now"), Some(u64::MAX)); + } } diff --git a/src/cache/lfu.rs b/src/cache/lfu.rs index d8c3d72..4089a77 100644 --- a/src/cache/lfu.rs +++ b/src/cache/lfu.rs @@ -71,10 +71,19 @@ fn get_freq_by_times(times: usize) -> u8 { } } -struct LfuEntry { +#[cfg(feature = "ttl")] +use crate::get_milltimestamp; +#[cfg(feature = "ttl")] +const DEFAULT_CHECK_STEP: u64 = 120; + +pub(crate) struct LfuEntry { pub key: mem::MaybeUninit, pub val: mem::MaybeUninit, pub counter: usize, + /// 带ttl的过期时间,单位秒 + /// 如果为u64::MAX,则表示不过期 + #[cfg(feature = "ttl")] + pub expire: u64, } impl LfuEntry { @@ -83,14 +92,40 @@ impl LfuEntry { key: mem::MaybeUninit::new(k), val: mem::MaybeUninit::new(v), counter, + #[cfg(feature = "ttl")] + expire: u64::MAX, } } - pub fn key_ref(&self) -> KeyRef { + fn key_ref(&self) -> KeyRef { KeyRef { k: self.key.as_ptr(), } } + + + #[cfg(feature = "ttl")] + #[inline(always)] + pub fn is_expire(&self) -> bool { + get_milltimestamp() >= self.expire + } + + + #[cfg(feature = "ttl")] + #[inline(always)] + pub fn is_little(&self, time: &u64) -> bool { + time >= &self.expire + } + + #[cfg(feature = "ttl")] + #[inline(always)] + pub fn get_ttl(&self) -> u64 { + if self.expire == u64::MAX { + self.expire + } else { + self.expire.saturating_sub(get_milltimestamp()) / 1000 + } + } } /// 一个 lfu(least frequently used/最不经常使用页置换算法 ) 缓存的实现, 接口参照Hashmap保持一致 @@ -118,7 +153,7 @@ impl LfuEntry { /// ``` pub struct LfuCache { map: HashMap, NonNull>, S>, - /// 因为HashSet的pop耗时太长, 所以取LruCache暂时做为平替 + /// 因为HashSet的pop耗时太长, 所以取LfuCache暂时做为平替 times_map: HashMap, (), DefaultHasher>>, cap: usize, max_freq: u8, @@ -127,6 +162,16 @@ pub struct LfuCache { default_count: usize, reduce_count: usize, + + /// 下一次检查的时间点,如果大于该时间点则全部检查是否过期 + #[cfg(feature = "ttl")] + check_next: u64, + /// 每次大检查点的时间间隔,如果不想启用该特性,可以将该值设成u64::MAX + #[cfg(feature = "ttl")] + check_step: u64, + /// 所有节点中是否存在带ttl的结点,如果均为普通的元素,则过期的将不进行检查 + #[cfg(feature = "ttl")] + has_ttl: bool, } impl LfuCache { @@ -149,9 +194,32 @@ impl LfuCache { reduce_count: cap.saturating_mul(100), default_count: 4, cap, + #[cfg(feature = "ttl")] + check_step: DEFAULT_CHECK_STEP, + #[cfg(feature = "ttl")] + check_next: get_milltimestamp()+DEFAULT_CHECK_STEP * 1000, + #[cfg(feature = "ttl")] + has_ttl: false, } } + /// 获取当前检查lru的间隔 + #[cfg(feature="ttl")] + pub fn get_check_step(&self) -> u64 { + self.check_step + } + + /// 设置当前检查lru的间隔 + /// 单位为秒,意思就是每隔多少秒会清理一次数据 + /// 如果数据太大的话遍历一次可能会比较久的时长 + /// 一次清理时间复杂度O(n) + /// 仅仅在插入时触发检查,获取时仅检查当前元素 + #[cfg(feature="ttl")] + pub fn set_check_step(&mut self, check_step: u64) { + self.check_step = check_step; + self.check_next = get_milltimestamp() + self.check_step * 1000; + } + /// 设定初始进入列表中默认的访问次数,防止出现一进入就权重过低的情况 /// /// ``` @@ -631,9 +699,9 @@ impl LfuCache { /// 获取key值相对应的value值, 根据hash判定 /// /// ``` - /// use algorithm::LruCache; + /// use algorithm::LfuCache; /// fn main() { - /// let mut lru = LruCache::new(3); + /// let mut lru = LfuCache::new(3); /// lru.insert("hello", "algorithm"); /// lru.insert("this", "lru"); /// assert!(lru.raw_get(&"this") == Some(&"lru")); @@ -664,6 +732,7 @@ impl LfuCache { /// assert!(lru.get(&"this") == Some(&"lru")); /// } /// ``` + #[inline] pub fn get(&mut self, k: &Q) -> Option<&V> where K: Borrow, @@ -683,19 +752,13 @@ impl LfuCache { /// assert!(lru.get_key_value(&"this") == Some((&"this", &"lru"))); /// } /// ``` + #[inline] pub fn get_key_value(&mut self, k: &Q) -> Option<(&K, &V)> where K: Borrow, Q: Hash + Eq + ?Sized, { - match self.map.get(KeyWrapper::from_ref(k)) { - Some(l) => { - let node = l.as_ptr(); - self.try_fix_entry(node); - unsafe { Some((&*(*node).key.as_ptr(), &*(*node).val.as_ptr())) } - } - None => None, - } + self.get_mut_key_value(k).map(|(k, v)| (k, &*v)) } /// 获取key值相对应的value值, 根据hash判定, 可编辑被改变 @@ -710,6 +773,7 @@ impl LfuCache { /// assert!(lru.get_key_value(&"this") == Some((&"this", &"lru good".to_string()))); /// } /// ``` + #[inline] pub fn get_mut(&mut self, k: &Q) -> Option<&mut V> where K: Borrow, @@ -722,12 +786,36 @@ impl LfuCache { where K: Borrow, Q: Hash + Eq + ?Sized, + { + match self.get_node(k) { + Some(node) => { + unsafe { Some(( &*(*node).key.as_mut_ptr(), &mut *(*node).val.as_mut_ptr())) } + } + None => None, + } + } + + + pub(crate) fn get_node(&mut self, k: &Q) -> Option<*mut LfuEntry> + where + K: Borrow, + Q: Hash + Eq + ?Sized, { match self.map.get(KeyWrapper::from_ref(k)) { Some(l) => { let node = l.as_ptr(); + #[cfg(feature = "ttl")] + unsafe { + if self.has_ttl && (*node).is_expire() { + self.detach(node); + self.map.remove(KeyWrapper::from_ref(k)); + let _ = *Box::from_raw(node); + return None; + } + } + self.try_fix_entry(node); - unsafe { Some((&*(*node).key.as_ptr(), &mut *(*node).val.as_mut_ptr())) } + Some(node) } None => None, } @@ -748,7 +836,34 @@ impl LfuCache { self.capture_insert(k, v).map(|(_, v, _)| v) } - pub fn capture_insert(&mut self, k: K, mut v: V) -> Option<(K, V, bool)> { + #[inline(always)] + pub fn capture_insert(&mut self, k: K, v: V) -> Option<(K, V, bool)> { + self._capture_insert_with_ttl(k, v, u64::MAX) + } + + /// 插入带有生存时间的元素 + /// 每次获取像redis一样,并不会更新生存时间 + /// 如果需要更新则需要手动的进行重新设置 + #[cfg(feature="ttl")] + #[inline(always)] + pub fn insert_with_ttl(&mut self, k: K, v: V, ttl: u64) -> Option { + self.capture_insert_with_ttl(k, v, ttl).map(|(_, v, _)| v) + } + + #[cfg(feature = "ttl")] + #[inline(always)] + pub fn capture_insert_with_ttl(&mut self, k: K, v: V, ttl: u64) -> Option<(K, V, bool)> { + if ttl == 0 { return None }; + self.has_ttl = true; + self._capture_insert_with_ttl(k, v, ttl) + } + + + #[allow(unused_variables)] + fn _capture_insert_with_ttl(&mut self, k: K, mut v: V, ttl: u64) -> Option<(K, V, bool)> { + #[cfg(feature="ttl")] + self.clear_expire(); + let key = KeyRef::new(&k); match self.map.get_mut(&key) { Some(entry) => { @@ -756,6 +871,11 @@ impl LfuCache { unsafe { mem::swap(&mut *(*entry_ptr).val.as_mut_ptr(), &mut v); } + + #[cfg(feature="ttl")] + unsafe { + (*entry_ptr).expire = ttl.saturating_mul(1000).saturating_add(get_milltimestamp()); + } self.try_fix_entry(entry_ptr); Some((k, v, true)) @@ -764,6 +884,11 @@ impl LfuCache { let (val, entry) = self.replace_or_create_node(k, v); let entry_ptr = entry.as_ptr(); self.attach(entry_ptr); + + #[cfg(feature="ttl")] + unsafe { + (*entry_ptr).expire = ttl.saturating_mul(1000).saturating_add(get_milltimestamp()); + } unsafe { self.map .insert(KeyRef::new((*entry_ptr).key.as_ptr()), entry); @@ -803,6 +928,72 @@ impl LfuCache { } } + #[cfg(feature="ttl")] + pub fn clear_expire(&mut self) { + if !self.has_ttl { + return; + } + let now = get_milltimestamp(); + if now < self.check_next { + return; + } + self.check_next = now + self.check_step; + unsafe { + let mut expire_keys = vec![]; + for (k, v) in self.map.iter() { + if v.as_ref().is_little(&now) { + expire_keys.push((*k).clone()); + } + } + + for k in expire_keys.drain(..) { + self.remove(&*k.k); + } + } + } + + #[cfg(feature="ttl")] + #[inline(always)] + pub fn del_ttl(&mut self, k: &Q) + where + K: Borrow, + Q: Hash + Eq + ?Sized, { + self.set_ttl(k, u64::MAX); + } + + #[cfg(feature="ttl")] + pub fn set_ttl(&mut self, k: &Q, expire: u64) -> bool + where + K: Borrow, + Q: Hash + Eq + ?Sized, { + if let Some(v) = self.get_node(&k) { + self.has_ttl = true; + unsafe { + (*v).expire = get_milltimestamp().saturating_add(expire.saturating_mul(1000)); + } + true + } else { + false + } + } + + #[cfg(feature="ttl")] + pub fn get_ttl(&mut self, k: &Q) -> Option + where + K: Borrow, + Q: Hash + Eq + ?Sized, { + if let Some(v) = self.get_node(&k) { + unsafe { + if (*v).expire == u64::MAX { + Some((*v).expire) + } else { + Some((*v).expire.saturating_sub(get_milltimestamp()) / 1000) + } + } + } else { + None + } + } /// 移除元素 /// @@ -821,11 +1012,43 @@ impl LfuCache { K: Borrow, Q: Hash + Eq + ?Sized, { + if let Some(node) = self.remove_node(k) { + unsafe { + Some((node.key.assume_init(), node.val.assume_init())) + } + } else { + None + } + } + + + #[cfg(feature="ttl")] + pub fn remove_with_ttl(&mut self, k: &Q) -> Option<(K, V, u64)> + where + K: Borrow, + Q: Hash + Eq + ?Sized, + { + if let Some(node) = self.remove_node(k) { + unsafe { + let ttl = node.get_ttl(); + Some((node.key.assume_init(), node.val.assume_init(), ttl)) + } + } else { + None + } + } + + fn remove_node(&mut self, k: &Q) -> Option> + where + K: Borrow, + Q: Hash + Eq + ?Sized, + { + match self.map.remove(KeyWrapper::from_ref(k)) { Some(l) => unsafe { self.detach(l.as_ptr()); let node = *Box::from_raw(l.as_ptr()); - Some((node.key.assume_init(), node.val.assume_init())) + Some(node) }, None => None, } @@ -1717,4 +1940,80 @@ mod tests { assert!(handle.join().is_ok()); } + + + #[test] + #[cfg(feature="ttl")] + fn test_ttl_cache() { + let mut lru = LfuCache::new(3); + lru.insert_with_ttl("help", "ok", 1); + lru.insert_with_ttl("author", "tickbh", 2); + assert_eq!(lru.len(), 2); + std::thread::sleep(std::time::Duration::from_secs(1)); + assert_eq!(lru.get("help"), None); + std::thread::sleep(std::time::Duration::from_secs(1)); + assert_eq!(lru.get("author"), None); + assert_eq!(lru.len(), 0); + } + + #[test] + #[cfg(feature="ttl")] + fn test_ttl_check_cache() { + let mut lru = LfuCache::new(3); + lru.set_check_step(1); + lru.insert_with_ttl("help", "ok", 1); + lru.insert("now", "algorithm"); + assert_eq!(lru.len(), 2); + std::thread::sleep(std::time::Duration::from_secs(1)); + assert_eq!(lru.len(), 2); + lru.insert_with_ttl("author", "tickbh", 3); + assert_eq!(lru.len(), 2); + assert_eq!(lru.get("help"), None); + assert_eq!(lru.len(), 2); + } + + #[test] + #[cfg(feature="ttl")] + fn test_ttl_del() { + let mut lru = LfuCache::new(3); + lru.insert_with_ttl("help", "ok", 1); + lru.insert_with_ttl("author", "tickbh", 2); + assert_eq!(lru.len(), 2); + std::thread::sleep(std::time::Duration::from_secs(1)); + assert_eq!(lru.get("help"), None); + lru.del_ttl(&"author"); + std::thread::sleep(std::time::Duration::from_secs(1)); + assert_eq!(lru.get("author"), Some(&"tickbh")); + assert_eq!(lru.len(), 1); + } + + #[test] + #[cfg(feature="ttl")] + fn test_ttl_set() { + let mut lru = LfuCache::new(3); + lru.insert_with_ttl("help", "ok", 1); + lru.insert_with_ttl("author", "tickbh", 2); + lru.set_ttl(&"help", 3); + assert_eq!(lru.len(), 2); + std::thread::sleep(std::time::Duration::from_secs(1)); + assert_eq!(lru.get("help"), Some(&"ok")); + std::thread::sleep(std::time::Duration::from_secs(1)); + assert_eq!(lru.get("author"), None); + std::thread::sleep(std::time::Duration::from_secs(1)); + assert_eq!(lru.get("help"), None); + assert_eq!(lru.len(), 0); + } + + + #[test] + #[cfg(feature="ttl")] + fn test_ttl_get() { + let mut lru = LfuCache::new(3); + lru.insert_with_ttl("help", "ok", 1); + lru.insert_with_ttl("author", "tickbh", 2); + lru.insert("now", "algorithm"); + assert_eq!(lru.get_ttl(&"help"), Some(1)); + assert_eq!(lru.get_ttl(&"author"), Some(2)); + assert_eq!(lru.get_ttl(&"now"), Some(u64::MAX)); + } } diff --git a/src/cache/lru.rs b/src/cache/lru.rs index 73c2bd5..6a9b63a 100644 --- a/src/cache/lru.rs +++ b/src/cache/lru.rs @@ -18,7 +18,7 @@ use crate::{HashMap, DefaultHasher}; use super::{KeyRef, KeyWrapper}; #[cfg(feature = "ttl")] -use crate::{get_timestamp}; +use crate::get_milltimestamp; #[cfg(feature = "ttl")] const DEFAULT_CHECK_STEP: u64 = 120; /// Lru节点数据 @@ -74,7 +74,7 @@ impl LruEntry { #[cfg(feature = "ttl")] #[inline(always)] pub fn is_expire(&self) -> bool { - get_timestamp() >= self.expire + get_milltimestamp() >= self.expire } @@ -83,6 +83,16 @@ impl LruEntry { pub fn is_little(&self, time: &u64) -> bool { time >= &self.expire } + + #[cfg(feature = "ttl")] + #[inline(always)] + pub fn get_ttl(&self) -> u64 { + if self.expire == u64::MAX { + self.expire + } else { + self.expire.saturating_sub(get_milltimestamp()) / 1000 + } + } } @@ -158,7 +168,7 @@ impl LruCache { #[cfg(feature = "ttl")] check_step: DEFAULT_CHECK_STEP, #[cfg(feature = "ttl")] - check_next: get_timestamp()+DEFAULT_CHECK_STEP, + check_next: get_milltimestamp()+DEFAULT_CHECK_STEP * 1000, #[cfg(feature = "ttl")] has_ttl: false, } @@ -178,7 +188,7 @@ impl LruCache { #[cfg(feature="ttl")] pub fn set_check_step(&mut self, check_step: u64) { self.check_step = check_step; - self.check_next = get_timestamp() + self.check_step; + self.check_next = get_milltimestamp() + self.check_step * 1000; } /// 获取当前容量 @@ -647,8 +657,7 @@ impl LruCache { #[cfg(feature="ttl")] #[inline(always)] pub fn insert_with_ttl(&mut self, k: K, v: V, ttl: u64) -> Option { - self.has_ttl = true; - self._capture_insert_with_ttl(k, v, ttl).map(|(_, v, _)| v) + self.capture_insert_with_ttl(k, v, ttl).map(|(_, v, _)| v) } #[inline(always)] @@ -658,8 +667,10 @@ impl LruCache { #[cfg(feature = "ttl")] #[inline(always)] - pub fn capture_insert_with_ttl(&mut self, k: K, v: V) -> Option<(K, V, bool)> { - self._capture_insert_with_ttl(k, v, u64::MAX) + pub fn capture_insert_with_ttl(&mut self, k: K, v: V, ttl: u64) -> Option<(K, V, bool)> { + if ttl == 0 { return None }; + self.has_ttl = true; + self._capture_insert_with_ttl(k, v, ttl) } #[allow(unused_variables)] @@ -676,7 +687,7 @@ impl LruCache { } #[cfg(feature="ttl")] unsafe { - (*entry_ptr).expire = ttl.saturating_add(get_timestamp()); + (*entry_ptr).expire = ttl.saturating_mul(1000).saturating_add(get_milltimestamp()); } self.detach(entry_ptr); self.attach(entry_ptr); @@ -689,7 +700,7 @@ impl LruCache { self.attach(entry_ptr); #[cfg(feature="ttl")] unsafe { - (*entry_ptr).expire = ttl.saturating_add(get_timestamp()); + (*entry_ptr).expire = ttl.saturating_mul(1000).saturating_add(get_milltimestamp()); } unsafe { self.map @@ -732,7 +743,7 @@ impl LruCache { if !self.has_ttl { return; } - let now = get_timestamp(); + let now = get_milltimestamp(); if now < self.check_next { return; } @@ -763,15 +774,18 @@ impl LruCache { } #[cfg(feature="ttl")] - pub fn set_ttl(&mut self, k: &Q, expire: u64) + pub fn set_ttl(&mut self, k: &Q, expire: u64) -> bool where K: Borrow, Q: Hash + Eq + ?Sized, { if let Some(v) = self.get_node(&k) { self.has_ttl = true; unsafe { - (*v).expire = get_timestamp().saturating_add(expire); + (*v).expire = get_milltimestamp().saturating_add(expire.saturating_mul(1000)); } + true + } else { + false } } @@ -782,11 +796,7 @@ impl LruCache { Q: Hash + Eq + ?Sized, { if let Some(v) = self.get_node(&k) { unsafe { - if (*v).expire == u64::MAX { - Some((*v).expire) - } else { - Some((*v).expire.saturating_sub(get_timestamp())) - } + Some((*v).get_ttl()) } } else { None @@ -809,12 +819,43 @@ impl LruCache { where K: Borrow, Q: Hash + Eq + ?Sized, + { + if let Some(node) = self.remove_node(k) { + unsafe { + Some((node.key.assume_init(), node.val.assume_init())) + } + } else { + None + } + } + + + #[cfg(feature="ttl")] + pub fn remove_with_ttl(&mut self, k: &Q) -> Option<(K, V, u64)> + where + K: Borrow, + Q: Hash + Eq + ?Sized, + { + if let Some(node) = self.remove_node(k) { + unsafe { + let ttl = node.get_ttl(); + Some((node.key.assume_init(), node.val.assume_init(), ttl)) + } + } else { + None + } + } + + fn remove_node(&mut self, k: &Q) -> Option> + where + K: Borrow, + Q: Hash + Eq + ?Sized, { match self.map.remove(KeyWrapper::from_ref(k)) { Some(l) => unsafe { self.detach(l.as_ptr()); let node = *Box::from_raw(l.as_ptr()); - Some((node.key.assume_init(), node.val.assume_init())) + Some(node) }, None => None, } diff --git a/src/cache/lruk.rs b/src/cache/lruk.rs index 4385a3f..e267d6e 100644 --- a/src/cache/lruk.rs +++ b/src/cache/lruk.rs @@ -19,16 +19,24 @@ use std::{ use crate::{HashMap, DefaultHasher}; use super::{KeyRef, KeyWrapper}; +#[cfg(feature = "ttl")] +use crate::get_milltimestamp; +#[cfg(feature = "ttl")] +const DEFAULT_CHECK_STEP: u64 = 120; const DEFAULT_TIMESK: usize = 2; /// LruK节点数据 -struct LruKEntry { +pub(crate) struct LruKEntry { pub key: mem::MaybeUninit, pub val: mem::MaybeUninit, pub times: usize, pub prev: *mut LruKEntry, pub next: *mut LruKEntry, + /// 带ttl的过期时间,单位秒 + /// 如果为u64::MAX,则表示不过期 + #[cfg(feature = "ttl")] + pub expire: u64, } impl LruKEntry { @@ -39,6 +47,8 @@ impl LruKEntry { times: 0, prev: ptr::null_mut(), next: ptr::null_mut(), + #[cfg(feature = "ttl")] + expire: u64::MAX, } } @@ -49,6 +59,33 @@ impl LruKEntry { times: 0, prev: ptr::null_mut(), next: ptr::null_mut(), + #[cfg(feature = "ttl")] + expire: u64::MAX, + } + } + + + #[cfg(feature = "ttl")] + #[inline(always)] + pub fn is_expire(&self) -> bool { + get_milltimestamp() >= self.expire + } + + + #[cfg(feature = "ttl")] + #[inline(always)] + pub fn is_little(&self, time: &u64) -> bool { + time >= &self.expire + } + + + #[cfg(feature = "ttl")] + #[inline(always)] + pub fn get_ttl(&self) -> u64 { + if self.expire == u64::MAX { + self.expire + } else { + self.expire.saturating_sub(get_milltimestamp()) / 1000 } } } @@ -91,6 +128,16 @@ pub struct LruKCache { tail: *mut LruKEntry, /// 普通队列的长度 lru_count: usize, + + /// 下一次检查的时间点,如果大于该时间点则全部检查是否过期 + #[cfg(feature = "ttl")] + check_next: u64, + /// 每次大检查点的时间间隔,如果不想启用该特性,可以将该值设成u64::MAX + #[cfg(feature = "ttl")] + check_step: u64, + /// 所有节点中是否存在带ttl的结点,如果均为普通的元素,则过期的将不进行检查 + #[cfg(feature = "ttl")] + has_ttl: bool, } impl Default for LruKCache { @@ -135,9 +182,31 @@ impl LruKCache { head, tail, lru_count: 0, + #[cfg(feature = "ttl")] + check_step: DEFAULT_CHECK_STEP, + #[cfg(feature = "ttl")] + check_next: get_milltimestamp()+DEFAULT_CHECK_STEP * 1000, + #[cfg(feature = "ttl")] + has_ttl: false, } } + /// 获取当前检查lru的间隔 + #[cfg(feature="ttl")] + pub fn get_check_step(&self) -> u64 { + self.check_step + } + + /// 设置当前检查lru的间隔 + /// 单位为秒,意思就是每隔多少秒会清理一次数据 + /// 如果数据太大的话遍历一次可能会比较久的时长 + /// 一次清理时间复杂度O(n) + /// 仅仅在插入时触发检查,获取时仅检查当前元素 + #[cfg(feature="ttl")] + pub fn set_check_step(&mut self, check_step: u64) { + self.check_step = check_step; + self.check_next = get_milltimestamp() + self.check_step * 1000; + } /// 获取当前容量 pub fn capacity(&self) -> usize { self.cap @@ -530,20 +599,13 @@ impl LruKCache { /// assert!(lru.get(&"this") == Some(&"lru")); /// } /// ``` + #[inline] pub fn get(&mut self, k: &Q) -> Option<&V> where K: Borrow, Q: Hash + Eq + ?Sized, { - match self.map.get(KeyWrapper::from_ref(k)) { - Some(l) => { - let node = l.as_ptr(); - self.detach(node); - self.attach(node); - unsafe { Some(&*(*node).val.as_ptr()) } - } - None => None, - } + self.get_key_value(k).map(|(_, v)| v) } /// 获取key值相对应的key和value值, 根据hash判定 @@ -557,20 +619,13 @@ impl LruKCache { /// assert!(lru.get_key_value(&"this") == Some((&"this", &"lru"))); /// } /// ``` + #[inline] pub fn get_key_value(&mut self, k: &Q) -> Option<(&K, &V)> where K: Borrow, Q: Hash + Eq + ?Sized, { - match self.map.get(KeyWrapper::from_ref(k)) { - Some(l) => { - let node = l.as_ptr(); - self.detach(node); - self.attach(node); - unsafe { Some((&*(*node).key.as_ptr(), &*(*node).val.as_ptr())) } - } - None => None, - } + self.get_mut_key_value(k).map(|(k, v)| (k, &*v)) } /// 获取key值相对应的value值, 根据hash判定, 可编辑被改变 @@ -589,14 +644,57 @@ impl LruKCache { where K: Borrow, Q: Hash + Eq + ?Sized, + { + self.get_mut_key_value(k).map(|(_, v)| v) + } + + + /// 获取key值相对应的value值, 根据hash判定, 可编辑被改变 + /// + /// ``` + /// use algorithm::LruKCache; + /// fn main() { + /// let mut lru = LruKCache::new(3); + /// lru.insert("hello", "algorithm".to_string()); + /// lru.insert("this", "lru".to_string()); + /// lru.get_mut(&"this").unwrap().insert_str(3, " good"); + /// assert!(lru.get_key_value(&"this") == Some((&"this", &"lru good".to_string()))); + /// } + /// ``` + pub fn get_mut_key_value(&mut self, k: &Q) -> Option<(&K, &mut V)> + where + K: Borrow, + Q: Hash + Eq + ?Sized, + { + match self.get_node(k) { + Some(node) => { + unsafe { Some((&*(*node).key.as_ptr(), &mut *(*node).val.as_mut_ptr())) } + } + None => None, + } + } + + + pub(crate) fn get_node(&mut self, k: &Q) -> Option<*mut LruKEntry> + where + K: Borrow, + Q: Hash + Eq + ?Sized, { match self.map.get(KeyWrapper::from_ref(k)) { Some(l) => { let node = l.as_ptr(); - self.detach(node); + #[cfg(feature = "ttl")] + unsafe { + if self.has_ttl && (*node).is_expire() { + self.map.remove(KeyWrapper::from_ref(k)); + let _ = *Box::from_raw(node); + return None; + } + } + self.attach(node); - unsafe { Some(&mut *(*node).val.as_mut_ptr()) } + Some(node) } None => None, } @@ -616,8 +714,34 @@ impl LruKCache { pub fn insert(&mut self, k: K, v: V) -> Option { self.capture_insert(k, v).map(|(_, v, _)| v) } + + /// 插入带有生存时间的元素 + /// 每次获取像redis一样,并不会更新生存时间 + /// 如果需要更新则需要手动的进行重新设置 + #[cfg(feature="ttl")] + #[inline(always)] + pub fn insert_with_ttl(&mut self, k: K, v: V, ttl: u64) -> Option { + self.capture_insert_with_ttl(k, v, ttl).map(|(_, v, _)| v) + } + + #[inline(always)] + pub fn capture_insert(&mut self, k: K, v: V) -> Option<(K, V, bool)> { + self._capture_insert_with_ttl(k, v, u64::MAX) + } + + #[cfg(feature = "ttl")] + #[inline(always)] + pub fn capture_insert_with_ttl(&mut self, k: K, v: V, ttl: u64) -> Option<(K, V, bool)> { + if ttl == 0 { return None }; + self.has_ttl = true; + self._capture_insert_with_ttl(k, v, ttl) + } + + #[allow(unused_variables)] + fn _capture_insert_with_ttl(&mut self, k: K, mut v: V, ttl: u64) -> Option<(K, V, bool)> { + #[cfg(feature="ttl")] + self.clear_expire(); - pub fn capture_insert(&mut self, k: K, mut v: V) -> Option<(K, V, bool)> { let key = KeyRef::new(&k); match self.map.get_mut(&key) { Some(entry) => { @@ -625,6 +749,10 @@ impl LruKCache { unsafe { mem::swap(&mut *(*entry_ptr).val.as_mut_ptr(), &mut v); } + #[cfg(feature="ttl")] + unsafe { + (*entry_ptr).expire = ttl.saturating_mul(1000).saturating_add(get_milltimestamp()); + } self.detach(entry_ptr); self.attach(entry_ptr); @@ -634,6 +762,10 @@ impl LruKCache { let (val, entry) = self.replace_or_create_node(k, v); let entry_ptr = entry.as_ptr(); self.attach(entry_ptr); + #[cfg(feature="ttl")] + unsafe { + (*entry_ptr).expire = ttl.saturating_mul(1000).saturating_add(get_milltimestamp()); + } unsafe { self.map .insert(KeyRef::new((*entry_ptr).key.as_ptr()), entry); @@ -672,6 +804,89 @@ impl LruKCache { } } + + #[cfg(feature="ttl")] + pub fn clear_expire(&mut self) { + if !self.has_ttl { + return; + } + let now = get_milltimestamp(); + if now < self.check_next { + return; + } + self.check_next = now + self.check_step; + unsafe { + let mut ptr = self.tail; + while ptr != self.head { + if (*ptr).is_little(&now) { + let next = (*ptr).prev; + self.detach(ptr); + self.map.remove(&KeyRef::new(&*(*ptr).key.as_ptr())); + let _ = *Box::from_raw(ptr); + ptr = next; + } else { + ptr = (*ptr).prev; + } + } + + let mut ptr = self.tail_times; + while ptr != self.head_times { + if (*ptr).is_little(&now) { + let next = (*ptr).prev; + self.detach(ptr); + self.map.remove(&KeyRef::new(&*(*ptr).key.as_ptr())); + let _ = *Box::from_raw(ptr); + ptr = next; + } else { + ptr = (*ptr).prev; + } + } + } + } + + #[cfg(feature="ttl")] + #[inline(always)] + pub fn del_ttl(&mut self, k: &Q) + where + K: Borrow, + Q: Hash + Eq + ?Sized, { + self.set_ttl(k, u64::MAX); + } + + #[cfg(feature="ttl")] + pub fn set_ttl(&mut self, k: &Q, expire: u64) -> bool + where + K: Borrow, + Q: Hash + Eq + ?Sized, { + if let Some(v) = self.get_node(&k) { + self.has_ttl = true; + unsafe { + (*v).expire = get_milltimestamp().saturating_add(expire.saturating_mul(1000)); + } + true + } else { + false + } + } + + #[cfg(feature="ttl")] + pub fn get_ttl(&mut self, k: &Q) -> Option + where + K: Borrow, + Q: Hash + Eq + ?Sized, { + if let Some(v) = self.get_node(&k) { + unsafe { + if (*v).expire == u64::MAX { + Some((*v).expire) + } else { + Some((*v).expire.saturating_sub(get_milltimestamp()) / 1000) + } + } + } else { + None + } + } + /// 移除元素 /// /// ``` @@ -688,12 +903,43 @@ impl LruKCache { where K: Borrow, Q: Hash + Eq + ?Sized, + { + if let Some(node) = self.remove_node(k) { + unsafe { + Some((node.key.assume_init(), node.val.assume_init())) + } + } else { + None + } + } + + + #[cfg(feature="ttl")] + pub fn remove_with_ttl(&mut self, k: &Q) -> Option<(K, V, u64)> + where + K: Borrow, + Q: Hash + Eq + ?Sized, + { + if let Some(node) = self.remove_node(k) { + unsafe { + let ttl = node.get_ttl(); + Some((node.key.assume_init(), node.val.assume_init(), ttl)) + } + } else { + None + } + } + + fn remove_node(&mut self, k: &Q) -> Option> + where + K: Borrow, + Q: Hash + Eq + ?Sized, { match self.map.remove(KeyWrapper::from_ref(k)) { Some(l) => unsafe { self.detach(l.as_ptr()); let node = *Box::from_raw(l.as_ptr()); - Some((node.key.assume_init(), node.val.assume_init())) + Some(node) }, None => None, } @@ -1501,4 +1747,80 @@ mod tests { assert!(handle.join().is_ok()); } + + + #[test] + #[cfg(feature="ttl")] + fn test_ttl_cache() { + let mut lru = LruKCache::new(3); + lru.insert_with_ttl("help", "ok", 1); + lru.insert_with_ttl("author", "tickbh", 2); + assert_eq!(lru.len(), 2); + std::thread::sleep(std::time::Duration::from_secs(1)); + assert_eq!(lru.get("help"), None); + std::thread::sleep(std::time::Duration::from_secs(1)); + assert_eq!(lru.get("author"), None); + assert_eq!(lru.len(), 0); + } + + #[test] + #[cfg(feature="ttl")] + fn test_ttl_check_cache() { + let mut lru = LruKCache::new(3); + lru.set_check_step(1); + lru.insert_with_ttl("help", "ok", 1); + lru.insert("now", "algorithm"); + assert_eq!(lru.len(), 2); + std::thread::sleep(std::time::Duration::from_secs(1)); + assert_eq!(lru.len(), 2); + lru.insert_with_ttl("author", "tickbh", 3); + assert_eq!(lru.len(), 2); + assert_eq!(lru.get("help"), None); + assert_eq!(lru.len(), 2); + } + + #[test] + #[cfg(feature="ttl")] + fn test_ttl_del() { + let mut lru = LruKCache::new(3); + lru.insert_with_ttl("help", "ok", 1); + lru.insert_with_ttl("author", "tickbh", 2); + assert_eq!(lru.len(), 2); + std::thread::sleep(std::time::Duration::from_secs(1)); + assert_eq!(lru.get("help"), None); + lru.del_ttl(&"author"); + std::thread::sleep(std::time::Duration::from_secs(1)); + assert_eq!(lru.get("author"), Some(&"tickbh")); + assert_eq!(lru.len(), 1); + } + + #[test] + #[cfg(feature="ttl")] + fn test_ttl_set() { + let mut lru = LruKCache::new(3); + lru.insert_with_ttl("help", "ok", 1); + lru.insert_with_ttl("author", "tickbh", 2); + lru.set_ttl(&"help", 3); + assert_eq!(lru.len(), 2); + std::thread::sleep(std::time::Duration::from_secs(1)); + assert_eq!(lru.get("help"), Some(&"ok")); + std::thread::sleep(std::time::Duration::from_secs(1)); + assert_eq!(lru.get("author"), None); + std::thread::sleep(std::time::Duration::from_secs(1)); + assert_eq!(lru.get("help"), None); + assert_eq!(lru.len(), 0); + } + + + #[test] + #[cfg(feature="ttl")] + fn test_ttl_get() { + let mut lru = LruKCache::new(3); + lru.insert_with_ttl("help", "ok", 1); + lru.insert_with_ttl("author", "tickbh", 2); + lru.insert("now", "algorithm"); + assert_eq!(lru.get_ttl(&"help"), Some(1)); + assert_eq!(lru.get_ttl(&"author"), Some(2)); + assert_eq!(lru.get_ttl(&"now"), Some(u64::MAX)); + } } \ No newline at end of file diff --git a/src/cache/mod.rs b/src/cache/mod.rs index c03812c..ba32a5d 100644 --- a/src/cache/mod.rs +++ b/src/cache/mod.rs @@ -13,7 +13,6 @@ pub use lfu::LfuCache; pub use arc::ArcCache; pub use slab::{Slab, Reinit}; -#[derive(Clone)] struct KeyRef { pub k: *const K, } @@ -24,6 +23,12 @@ impl KeyRef { } } +impl Clone for KeyRef { + fn clone(&self) -> Self { + Self { k: self.k } + } +} + impl Hash for KeyRef { fn hash(&self, state: &mut H) { unsafe {