1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
use std::{
collections::{BTreeMap, HashMap},
time::{Duration, SystemTime},
};
struct ValueInfo<V> {
value: V,
ttl: SystemTime,
}
pub struct TtlCache<K, V> {
capacity: usize,
default_timeout: Duration,
data: HashMap<K, ValueInfo<V>>,
ttl_index: BTreeMap<SystemTime, K>,
}
impl<K, V> TtlCache<K, V>
where
K: std::cmp::Eq + std::hash::Hash + std::clone::Clone,
{
pub fn new(capacity: usize, default_timeout: Duration) -> Self {
Self {
capacity,
default_timeout,
data: HashMap::new(),
ttl_index: BTreeMap::new(),
}
}
pub fn get(&self, key: &K) -> Option<&V> {
self.data.get(key).map(|v| &v.value)
}
pub fn insert(&mut self, key: K, value: V) {
match self.data.get(&key) {
Some(info) => {
self.ttl_index.remove(&info.ttl);
}
None => {
if self.data.len() == self.capacity {
let first_entry = self.ttl_index.keys().next().cloned();
if let Some(tst) = first_entry {
if let Some(key) = self.ttl_index.remove(&tst) {
self.data.remove(&key);
}
}
}
}
}
if let Some(expiration_time) = SystemTime::now().checked_add(self.default_timeout) {
self.ttl_index.insert(expiration_time, key.clone());
let value_info = ValueInfo {
value,
ttl: expiration_time,
};
self.data.insert(key, value_info);
}
}
pub fn remove(&mut self, key: &K) -> Option<V> {
match self.data.remove(key) {
Some(info) => {
self.ttl_index.remove(&info.ttl);
Some(info.value)
}
None => None,
}
}
pub fn gc(&mut self, gc_time: SystemTime) {
let mut active = self.ttl_index.split_off(&gc_time);
for key in self.ttl_index.values() {
self.data.remove(key);
}
self.ttl_index.clear();
self.ttl_index.append(&mut active);
}
#[cfg(test)]
pub fn size(&self) -> usize {
self.data.len()
}
}