offheap 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/CI.yml +22 -1
- package/Cargo.toml +2 -2
- package/artifacts/bindings-aarch64-apple-darwin/index.d.ts +37 -25
- package/artifacts/bindings-aarch64-apple-darwin/index.js +165 -295
- package/artifacts/bindings-aarch64-unknown-linux-musl/index.d.ts +42 -0
- package/artifacts/bindings-aarch64-unknown-linux-musl/index.js +186 -0
- package/artifacts/bindings-x86_64-apple-darwin/index.d.ts +37 -25
- package/artifacts/bindings-x86_64-apple-darwin/index.js +165 -295
- package/artifacts/bindings-x86_64-pc-windows-msvc/index.d.ts +42 -30
- package/artifacts/bindings-x86_64-pc-windows-msvc/index.js +186 -316
- package/artifacts/bindings-x86_64-unknown-linux-gnu/index.d.ts +37 -25
- package/artifacts/bindings-x86_64-unknown-linux-gnu/index.js +165 -295
- package/artifacts/bindings-x86_64-unknown-linux-musl/index.d.ts +42 -0
- package/artifacts/bindings-x86_64-unknown-linux-musl/index.js +186 -0
- package/benchmarks/crossover_bench.js +139 -0
- package/docs/.vitepress/config.js +35 -0
- package/docs/guide/api.md +216 -0
- package/docs/guide/architecture.md +58 -0
- package/docs/guide/benchmarks.md +48 -0
- package/docs/guide/getting-started.md +74 -0
- package/docs/index.md +23 -0
- package/index.d.ts +37 -25
- package/index.js +165 -295
- package/package.json +21 -12
- package/src/algorithms/arc.rs +123 -25
- package/src/algorithms/lru.rs +94 -12
- package/src/algorithms/mod.rs +4 -0
- package/src/algorithms/tinylfu.rs +98 -35
- package/src/cache.rs +289 -94
- package/tests/index.test.js +134 -7
package/src/algorithms/arc.rs
CHANGED
|
@@ -35,6 +35,8 @@ impl ArcListMetadata {
|
|
|
35
35
|
|
|
36
36
|
pub struct ArcCache {
|
|
37
37
|
capacity: usize,
|
|
38
|
+
max_bytes: Option<usize>,
|
|
39
|
+
bytes_used: usize,
|
|
38
40
|
map: HashMap<String, usize>,
|
|
39
41
|
nodes: Vec<ArcNode>,
|
|
40
42
|
free_nodes: Vec<usize>,
|
|
@@ -51,9 +53,15 @@ pub struct ArcCache {
|
|
|
51
53
|
|
|
52
54
|
impl ArcCache {
|
|
53
55
|
pub fn new(capacity: usize) -> Self {
|
|
56
|
+
Self::new_with_max_bytes(capacity, None)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
pub fn new_with_max_bytes(capacity: usize, max_bytes: Option<usize>) -> Self {
|
|
54
60
|
Self {
|
|
55
61
|
capacity,
|
|
56
|
-
|
|
62
|
+
max_bytes,
|
|
63
|
+
bytes_used: 0,
|
|
64
|
+
map: HashMap::with_capacity(capacity * 2),
|
|
57
65
|
nodes: Vec::with_capacity(capacity * 2),
|
|
58
66
|
free_nodes: Vec::new(),
|
|
59
67
|
t1: ArcListMetadata::new(),
|
|
@@ -66,6 +74,15 @@ impl ArcCache {
|
|
|
66
74
|
}
|
|
67
75
|
}
|
|
68
76
|
|
|
77
|
+
fn node_bytes(&self, idx: usize) -> usize {
|
|
78
|
+
let node = &self.nodes[idx];
|
|
79
|
+
if let Some(ref entry) = node.value {
|
|
80
|
+
node.key.len() + entry.value.len()
|
|
81
|
+
} else {
|
|
82
|
+
0
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
69
86
|
fn detach(&mut self, idx: usize) {
|
|
70
87
|
let prev = self.nodes[idx].prev;
|
|
71
88
|
let next = self.nodes[idx].next;
|
|
@@ -118,6 +135,8 @@ impl ArcCache {
|
|
|
118
135
|
}
|
|
119
136
|
|
|
120
137
|
fn remove_completely(&mut self, idx: usize) -> String {
|
|
138
|
+
let bytes = self.node_bytes(idx);
|
|
139
|
+
self.bytes_used -= bytes;
|
|
121
140
|
self.detach(idx);
|
|
122
141
|
self.free_nodes.push(idx);
|
|
123
142
|
let key = std::mem::take(&mut self.nodes[idx].key);
|
|
@@ -128,12 +147,16 @@ impl ArcCache {
|
|
|
128
147
|
fn replace(&mut self, key_in_b2: bool) {
|
|
129
148
|
if self.t1.len > 0 && (self.t1.len > self.p || (key_in_b2 && self.t1.len == self.p)) {
|
|
130
149
|
if let Some(t1_tail) = self.t1.tail {
|
|
150
|
+
let bytes = self.node_bytes(t1_tail);
|
|
151
|
+
self.bytes_used -= bytes;
|
|
131
152
|
self.detach(t1_tail);
|
|
132
153
|
self.nodes[t1_tail].value = None; // Evict value from memory
|
|
133
154
|
self.attach_to_head(t1_tail, ArcList::B1);
|
|
134
155
|
}
|
|
135
156
|
} else {
|
|
136
157
|
if let Some(t2_tail) = self.t2.tail {
|
|
158
|
+
let bytes = self.node_bytes(t2_tail);
|
|
159
|
+
self.bytes_used -= bytes;
|
|
137
160
|
self.detach(t2_tail);
|
|
138
161
|
self.nodes[t2_tail].value = None; // Evict value from memory
|
|
139
162
|
self.attach_to_head(t2_tail, ArcList::B2);
|
|
@@ -148,12 +171,7 @@ impl CacheImpl for ArcCache {
|
|
|
148
171
|
let list = self.nodes[idx].list;
|
|
149
172
|
match list {
|
|
150
173
|
ArcList::T1 | ArcList::T2 => {
|
|
151
|
-
let expired =
|
|
152
|
-
entry.is_expired()
|
|
153
|
-
} else {
|
|
154
|
-
true
|
|
155
|
-
};
|
|
156
|
-
|
|
174
|
+
let expired = self.nodes[idx].value.as_ref().map_or(true, |e| e.is_expired());
|
|
157
175
|
if expired {
|
|
158
176
|
self.remove_completely(idx);
|
|
159
177
|
self.map.remove(key);
|
|
@@ -167,8 +185,6 @@ impl CacheImpl for ArcCache {
|
|
|
167
185
|
}
|
|
168
186
|
}
|
|
169
187
|
ArcList::B1 | ArcList::B2 => {
|
|
170
|
-
// History hit. We can't return value (evicted), so counts as miss for user,
|
|
171
|
-
// but we trigger adaptation.
|
|
172
188
|
self.misses += 1;
|
|
173
189
|
None
|
|
174
190
|
}
|
|
@@ -179,23 +195,74 @@ impl CacheImpl for ArcCache {
|
|
|
179
195
|
}
|
|
180
196
|
}
|
|
181
197
|
|
|
198
|
+
fn peek(&mut self, key: &str) -> Option<Vec<u8>> {
|
|
199
|
+
if let Some(&idx) = self.map.get(key) {
|
|
200
|
+
let list = self.nodes[idx].list;
|
|
201
|
+
match list {
|
|
202
|
+
ArcList::T1 | ArcList::T2 => {
|
|
203
|
+
let expired = self.nodes[idx].value.as_ref().map_or(true, |e| e.is_expired());
|
|
204
|
+
if expired {
|
|
205
|
+
self.remove_completely(idx);
|
|
206
|
+
self.map.remove(key);
|
|
207
|
+
self.misses += 1;
|
|
208
|
+
None
|
|
209
|
+
} else {
|
|
210
|
+
self.hits += 1;
|
|
211
|
+
self.nodes[idx].value.as_ref().map(|e| e.value.clone())
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
_ => {
|
|
215
|
+
self.misses += 1;
|
|
216
|
+
None
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
} else {
|
|
220
|
+
self.misses += 1;
|
|
221
|
+
None
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
fn has(&mut self, key: &str) -> bool {
|
|
226
|
+
if let Some(&idx) = self.map.get(key) {
|
|
227
|
+
let list = self.nodes[idx].list;
|
|
228
|
+
match list {
|
|
229
|
+
ArcList::T1 | ArcList::T2 => {
|
|
230
|
+
let expired = self.nodes[idx].value.as_ref().map_or(true, |e| e.is_expired());
|
|
231
|
+
if expired {
|
|
232
|
+
self.remove_completely(idx);
|
|
233
|
+
self.map.remove(key);
|
|
234
|
+
false
|
|
235
|
+
} else {
|
|
236
|
+
true
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
_ => false
|
|
240
|
+
}
|
|
241
|
+
} else {
|
|
242
|
+
false
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
182
246
|
fn set(&mut self, key: &str, value: Vec<u8>, ttl_ms: Option<u64>) -> Option<Vec<u8>> {
|
|
183
247
|
if self.capacity == 0 {
|
|
184
248
|
return None;
|
|
185
249
|
}
|
|
186
250
|
|
|
251
|
+
let new_entry = CacheEntry::new(value, ttl_ms);
|
|
252
|
+
let new_bytes = key.len() + new_entry.value.len();
|
|
253
|
+
let mut old_value = None;
|
|
254
|
+
|
|
187
255
|
if let Some(&idx) = self.map.get(key) {
|
|
188
256
|
let list = self.nodes[idx].list;
|
|
189
257
|
match list {
|
|
190
258
|
ArcList::T1 | ArcList::T2 => {
|
|
191
|
-
|
|
192
|
-
|
|
259
|
+
let old_bytes = self.node_bytes(idx);
|
|
260
|
+
old_value = self.nodes[idx].value.replace(new_entry).map(|e| e.value);
|
|
261
|
+
self.bytes_used = self.bytes_used + new_bytes - old_bytes;
|
|
193
262
|
self.detach(idx);
|
|
194
263
|
self.attach_to_head(idx, ArcList::T2);
|
|
195
|
-
old_value
|
|
196
264
|
}
|
|
197
265
|
ArcList::B1 => {
|
|
198
|
-
// Hit in B1: we need more recency
|
|
199
266
|
let b1_len = if self.b1.len == 0 { 1 } else { self.b1.len };
|
|
200
267
|
let delta = std::cmp::max(1, self.b2.len / b1_len);
|
|
201
268
|
self.p = std::cmp::min(self.p + delta, self.capacity);
|
|
@@ -203,12 +270,11 @@ impl CacheImpl for ArcCache {
|
|
|
203
270
|
self.replace(false);
|
|
204
271
|
|
|
205
272
|
self.detach(idx);
|
|
206
|
-
self.nodes[idx].value = Some(
|
|
273
|
+
self.nodes[idx].value = Some(new_entry);
|
|
274
|
+
self.bytes_used += new_bytes;
|
|
207
275
|
self.attach_to_head(idx, ArcList::T2);
|
|
208
|
-
None
|
|
209
276
|
}
|
|
210
277
|
ArcList::B2 => {
|
|
211
|
-
// Hit in B2: we need more frequency
|
|
212
278
|
let b2_len = if self.b2.len == 0 { 1 } else { self.b2.len };
|
|
213
279
|
let delta = std::cmp::max(1, self.b1.len / b2_len);
|
|
214
280
|
self.p = self.p.saturating_sub(delta);
|
|
@@ -216,13 +282,12 @@ impl CacheImpl for ArcCache {
|
|
|
216
282
|
self.replace(true);
|
|
217
283
|
|
|
218
284
|
self.detach(idx);
|
|
219
|
-
self.nodes[idx].value = Some(
|
|
285
|
+
self.nodes[idx].value = Some(new_entry);
|
|
286
|
+
self.bytes_used += new_bytes;
|
|
220
287
|
self.attach_to_head(idx, ArcList::T2);
|
|
221
|
-
None
|
|
222
288
|
}
|
|
223
289
|
}
|
|
224
290
|
} else {
|
|
225
|
-
// Cache Miss (not in cache, nor in history)
|
|
226
291
|
let l1_len = self.t1.len + self.b1.len;
|
|
227
292
|
let l2_len = self.t2.len + self.b2.len;
|
|
228
293
|
|
|
@@ -249,16 +314,15 @@ impl CacheImpl for ArcCache {
|
|
|
249
314
|
self.replace(false);
|
|
250
315
|
}
|
|
251
316
|
|
|
252
|
-
// Create new node
|
|
253
317
|
let idx = if let Some(free_idx) = self.free_nodes.pop() {
|
|
254
318
|
self.nodes[free_idx].key = key.to_string();
|
|
255
|
-
self.nodes[free_idx].value = Some(
|
|
319
|
+
self.nodes[free_idx].value = Some(new_entry);
|
|
256
320
|
free_idx
|
|
257
321
|
} else {
|
|
258
322
|
let free_idx = self.nodes.len();
|
|
259
323
|
self.nodes.push(ArcNode {
|
|
260
324
|
key: key.to_string(),
|
|
261
|
-
value: Some(
|
|
325
|
+
value: Some(new_entry),
|
|
262
326
|
list: ArcList::T1,
|
|
263
327
|
prev: None,
|
|
264
328
|
next: None,
|
|
@@ -266,9 +330,42 @@ impl CacheImpl for ArcCache {
|
|
|
266
330
|
free_idx
|
|
267
331
|
};
|
|
268
332
|
|
|
333
|
+
self.bytes_used += new_bytes;
|
|
269
334
|
self.attach_to_head(idx, ArcList::T1);
|
|
270
335
|
self.map.insert(key.to_string(), idx);
|
|
271
|
-
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
if let Some(max) = self.max_bytes {
|
|
339
|
+
while self.bytes_used > max && (self.t1.len > 0 || self.t2.len > 0) {
|
|
340
|
+
self.replace(false);
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
old_value
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
fn touch(&mut self, key: &str, ttl_ms: Option<u64>) -> bool {
|
|
348
|
+
if let Some(&idx) = self.map.get(key) {
|
|
349
|
+
let list = self.nodes[idx].list;
|
|
350
|
+
match list {
|
|
351
|
+
ArcList::T1 | ArcList::T2 => {
|
|
352
|
+
let expired = self.nodes[idx].value.as_ref().map_or(true, |e| e.is_expired());
|
|
353
|
+
if expired {
|
|
354
|
+
self.remove_completely(idx);
|
|
355
|
+
self.map.remove(key);
|
|
356
|
+
false
|
|
357
|
+
} else {
|
|
358
|
+
let val = self.nodes[idx].value.as_ref().unwrap().value.clone();
|
|
359
|
+
self.nodes[idx].value = Some(CacheEntry::new(val, ttl_ms));
|
|
360
|
+
self.detach(idx);
|
|
361
|
+
self.attach_to_head(idx, ArcList::T2);
|
|
362
|
+
true
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
_ => false
|
|
366
|
+
}
|
|
367
|
+
} else {
|
|
368
|
+
false
|
|
272
369
|
}
|
|
273
370
|
}
|
|
274
371
|
|
|
@@ -293,6 +390,7 @@ impl CacheImpl for ArcCache {
|
|
|
293
390
|
self.p = 0;
|
|
294
391
|
self.hits = 0;
|
|
295
392
|
self.misses = 0;
|
|
393
|
+
self.bytes_used = 0;
|
|
296
394
|
}
|
|
297
395
|
|
|
298
396
|
fn stats(&self) -> CacheStats {
|
|
@@ -301,12 +399,12 @@ impl CacheImpl for ArcCache {
|
|
|
301
399
|
misses: self.misses,
|
|
302
400
|
capacity: self.capacity,
|
|
303
401
|
size: self.t1.len + self.t2.len,
|
|
402
|
+
bytes_used: self.bytes_used,
|
|
304
403
|
}
|
|
305
404
|
}
|
|
306
405
|
|
|
307
406
|
fn keys(&self) -> Vec<String> {
|
|
308
407
|
let mut result = Vec::new();
|
|
309
|
-
// Return active keys in T1
|
|
310
408
|
let mut curr = self.t1.head;
|
|
311
409
|
while let Some(idx) = curr {
|
|
312
410
|
let node = &self.nodes[idx];
|
|
@@ -316,7 +414,6 @@ impl CacheImpl for ArcCache {
|
|
|
316
414
|
}
|
|
317
415
|
curr = node.next;
|
|
318
416
|
}
|
|
319
|
-
// Return active keys in T2
|
|
320
417
|
let mut curr = self.t2.head;
|
|
321
418
|
while let Some(idx) = curr {
|
|
322
419
|
let node = &self.nodes[idx];
|
|
@@ -330,6 +427,7 @@ impl CacheImpl for ArcCache {
|
|
|
330
427
|
}
|
|
331
428
|
}
|
|
332
429
|
|
|
430
|
+
|
|
333
431
|
#[cfg(test)]
|
|
334
432
|
mod tests {
|
|
335
433
|
use super::*;
|
package/src/algorithms/lru.rs
CHANGED
|
@@ -10,6 +10,8 @@ struct LruNode {
|
|
|
10
10
|
|
|
11
11
|
pub struct LruCache {
|
|
12
12
|
capacity: usize,
|
|
13
|
+
max_bytes: Option<usize>,
|
|
14
|
+
bytes_used: usize,
|
|
13
15
|
map: HashMap<String, usize>,
|
|
14
16
|
nodes: Vec<LruNode>,
|
|
15
17
|
free_nodes: Vec<usize>,
|
|
@@ -21,10 +23,16 @@ pub struct LruCache {
|
|
|
21
23
|
|
|
22
24
|
impl LruCache {
|
|
23
25
|
pub fn new(capacity: usize) -> Self {
|
|
26
|
+
Self::new_with_max_bytes(capacity, None)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
pub fn new_with_max_bytes(capacity: usize, max_bytes: Option<usize>) -> Self {
|
|
24
30
|
Self {
|
|
25
31
|
capacity,
|
|
26
|
-
|
|
27
|
-
|
|
32
|
+
max_bytes,
|
|
33
|
+
bytes_used: 0,
|
|
34
|
+
map: HashMap::with_capacity(capacity.min(1024)),
|
|
35
|
+
nodes: Vec::with_capacity(capacity.min(1024)),
|
|
28
36
|
free_nodes: Vec::new(),
|
|
29
37
|
head: None,
|
|
30
38
|
tail: None,
|
|
@@ -87,7 +95,8 @@ impl CacheImpl for LruCache {
|
|
|
87
95
|
fn get(&mut self, key: &str) -> Option<Vec<u8>> {
|
|
88
96
|
if let Some(&node_idx) = self.map.get(key) {
|
|
89
97
|
if self.nodes[node_idx].entry.is_expired() {
|
|
90
|
-
self.remove_node(node_idx);
|
|
98
|
+
let (evicted_key, evicted_entry) = self.remove_node(node_idx);
|
|
99
|
+
self.bytes_used -= evicted_key.len() + evicted_entry.value.len();
|
|
91
100
|
self.map.remove(key);
|
|
92
101
|
self.misses += 1;
|
|
93
102
|
None
|
|
@@ -102,25 +111,61 @@ impl CacheImpl for LruCache {
|
|
|
102
111
|
}
|
|
103
112
|
}
|
|
104
113
|
|
|
114
|
+
fn peek(&mut self, key: &str) -> Option<Vec<u8>> {
|
|
115
|
+
if let Some(&node_idx) = self.map.get(key) {
|
|
116
|
+
if self.nodes[node_idx].entry.is_expired() {
|
|
117
|
+
let (evicted_key, evicted_entry) = self.remove_node(node_idx);
|
|
118
|
+
self.bytes_used -= evicted_key.len() + evicted_entry.value.len();
|
|
119
|
+
self.map.remove(key);
|
|
120
|
+
self.misses += 1;
|
|
121
|
+
None
|
|
122
|
+
} else {
|
|
123
|
+
self.hits += 1;
|
|
124
|
+
Some(self.nodes[node_idx].entry.value.clone())
|
|
125
|
+
}
|
|
126
|
+
} else {
|
|
127
|
+
self.misses += 1;
|
|
128
|
+
None
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
fn has(&mut self, key: &str) -> bool {
|
|
133
|
+
if let Some(&node_idx) = self.map.get(key) {
|
|
134
|
+
if self.nodes[node_idx].entry.is_expired() {
|
|
135
|
+
let (evicted_key, evicted_entry) = self.remove_node(node_idx);
|
|
136
|
+
self.bytes_used -= evicted_key.len() + evicted_entry.value.len();
|
|
137
|
+
self.map.remove(key);
|
|
138
|
+
false
|
|
139
|
+
} else {
|
|
140
|
+
true
|
|
141
|
+
}
|
|
142
|
+
} else {
|
|
143
|
+
false
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
105
147
|
fn set(&mut self, key: &str, value: Vec<u8>, ttl_ms: Option<u64>) -> Option<Vec<u8>> {
|
|
148
|
+
let new_bytes = key.len() + value.len();
|
|
149
|
+
let mut old_value = None;
|
|
150
|
+
|
|
106
151
|
if let Some(&node_idx) = self.map.get(key) {
|
|
107
|
-
|
|
108
|
-
let old_value = std::mem::replace(
|
|
152
|
+
let old_entry = std::mem::replace(
|
|
109
153
|
&mut self.nodes[node_idx].entry,
|
|
110
154
|
CacheEntry::new(value, ttl_ms),
|
|
111
|
-
)
|
|
155
|
+
);
|
|
156
|
+
let old_bytes = key.len() + old_entry.value.len();
|
|
157
|
+
self.bytes_used = self.bytes_used + new_bytes - old_bytes;
|
|
112
158
|
self.move_to_head(node_idx);
|
|
113
|
-
Some(
|
|
159
|
+
old_value = Some(old_entry.value);
|
|
114
160
|
} else {
|
|
115
|
-
// If full, evict the LRU (tail)
|
|
116
161
|
if self.map.len() >= self.capacity && self.capacity > 0 {
|
|
117
162
|
if let Some(t_idx) = self.tail {
|
|
118
|
-
let (evicted_key,
|
|
163
|
+
let (evicted_key, evicted_entry) = self.remove_node(t_idx);
|
|
164
|
+
self.bytes_used -= evicted_key.len() + evicted_entry.value.len();
|
|
119
165
|
self.map.remove(&evicted_key);
|
|
120
166
|
}
|
|
121
167
|
}
|
|
122
168
|
|
|
123
|
-
// Insert new entry
|
|
124
169
|
let node_idx = if let Some(idx) = self.free_nodes.pop() {
|
|
125
170
|
self.nodes[idx].key = key.to_string();
|
|
126
171
|
self.nodes[idx].entry = CacheEntry::new(value, ttl_ms);
|
|
@@ -136,15 +181,50 @@ impl CacheImpl for LruCache {
|
|
|
136
181
|
idx
|
|
137
182
|
};
|
|
138
183
|
|
|
184
|
+
self.bytes_used += new_bytes;
|
|
139
185
|
self.attach_to_head(node_idx);
|
|
140
186
|
self.map.insert(key.to_string(), node_idx);
|
|
141
|
-
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
if let Some(max) = self.max_bytes {
|
|
190
|
+
while self.bytes_used > max && !self.map.is_empty() {
|
|
191
|
+
if let Some(t_idx) = self.tail {
|
|
192
|
+
let (evicted_key, evicted_entry) = self.remove_node(t_idx);
|
|
193
|
+
self.bytes_used -= evicted_key.len() + evicted_entry.value.len();
|
|
194
|
+
self.map.remove(&evicted_key);
|
|
195
|
+
} else {
|
|
196
|
+
break;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
old_value
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
fn touch(&mut self, key: &str, ttl_ms: Option<u64>) -> bool {
|
|
205
|
+
if let Some(&node_idx) = self.map.get(key) {
|
|
206
|
+
if self.nodes[node_idx].entry.is_expired() {
|
|
207
|
+
let (evicted_key, evicted_entry) = self.remove_node(node_idx);
|
|
208
|
+
self.bytes_used -= evicted_key.len() + evicted_entry.value.len();
|
|
209
|
+
self.map.remove(key);
|
|
210
|
+
false
|
|
211
|
+
} else {
|
|
212
|
+
self.nodes[node_idx].entry = CacheEntry::new(
|
|
213
|
+
self.nodes[node_idx].entry.value.clone(),
|
|
214
|
+
ttl_ms,
|
|
215
|
+
);
|
|
216
|
+
self.move_to_head(node_idx);
|
|
217
|
+
true
|
|
218
|
+
}
|
|
219
|
+
} else {
|
|
220
|
+
false
|
|
142
221
|
}
|
|
143
222
|
}
|
|
144
223
|
|
|
145
224
|
fn delete(&mut self, key: &str) -> bool {
|
|
146
225
|
if let Some(&node_idx) = self.map.get(key) {
|
|
147
|
-
self.remove_node(node_idx);
|
|
226
|
+
let (evicted_key, evicted_entry) = self.remove_node(node_idx);
|
|
227
|
+
self.bytes_used -= evicted_key.len() + evicted_entry.value.len();
|
|
148
228
|
self.map.remove(key);
|
|
149
229
|
true
|
|
150
230
|
} else {
|
|
@@ -160,6 +240,7 @@ impl CacheImpl for LruCache {
|
|
|
160
240
|
self.tail = None;
|
|
161
241
|
self.hits = 0;
|
|
162
242
|
self.misses = 0;
|
|
243
|
+
self.bytes_used = 0;
|
|
163
244
|
}
|
|
164
245
|
|
|
165
246
|
fn stats(&self) -> CacheStats {
|
|
@@ -168,6 +249,7 @@ impl CacheImpl for LruCache {
|
|
|
168
249
|
misses: self.misses,
|
|
169
250
|
capacity: self.capacity,
|
|
170
251
|
size: self.map.len(),
|
|
252
|
+
bytes_used: self.bytes_used,
|
|
171
253
|
}
|
|
172
254
|
}
|
|
173
255
|
|
package/src/algorithms/mod.rs
CHANGED
|
@@ -6,6 +6,7 @@ pub struct CacheStats {
|
|
|
6
6
|
pub misses: u64,
|
|
7
7
|
pub capacity: usize,
|
|
8
8
|
pub size: usize,
|
|
9
|
+
pub bytes_used: usize,
|
|
9
10
|
}
|
|
10
11
|
|
|
11
12
|
pub struct CacheEntry {
|
|
@@ -30,7 +31,10 @@ impl CacheEntry {
|
|
|
30
31
|
|
|
31
32
|
pub trait CacheImpl: Send + Sync {
|
|
32
33
|
fn get(&mut self, key: &str) -> Option<Vec<u8>>;
|
|
34
|
+
fn peek(&mut self, key: &str) -> Option<Vec<u8>>;
|
|
35
|
+
fn has(&mut self, key: &str) -> bool;
|
|
33
36
|
fn set(&mut self, key: &str, value: Vec<u8>, ttl_ms: Option<u64>) -> Option<Vec<u8>>;
|
|
37
|
+
fn touch(&mut self, key: &str, ttl_ms: Option<u64>) -> bool;
|
|
34
38
|
fn delete(&mut self, key: &str) -> bool;
|
|
35
39
|
fn clear(&mut self);
|
|
36
40
|
fn stats(&self) -> CacheStats;
|