offheap 0.1.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/src/manager.rs ADDED
@@ -0,0 +1,50 @@
1
+ use std::collections::HashMap;
2
+ use napi::{Result, Error, Status};
3
+ use super::cache::{Cache, CacheConfig};
4
+
5
+ #[napi]
6
+ pub struct CacheManager {
7
+ caches: parking_lot::RwLock<HashMap<String, Cache>>,
8
+ }
9
+
10
+ #[napi]
11
+ impl CacheManager {
12
+ #[napi(constructor)]
13
+ pub fn new() -> Self {
14
+ Self {
15
+ caches: parking_lot::RwLock::new(HashMap::new()),
16
+ }
17
+ }
18
+
19
+ #[napi]
20
+ pub fn create_cache(&self, name: String, config: CacheConfig) -> Result<Cache> {
21
+ let mut lock = self.caches.write();
22
+ if lock.contains_key(&name) {
23
+ return Err(Error::new(
24
+ Status::InvalidArg,
25
+ format!("Cache '{}' already exists", name),
26
+ ));
27
+ }
28
+ let cache = Cache::from_config(config);
29
+ lock.insert(name, cache.clone());
30
+ Ok(cache)
31
+ }
32
+
33
+ #[napi]
34
+ pub fn get_cache(&self, name: String) -> Option<Cache> {
35
+ let lock = self.caches.read();
36
+ lock.get(&name).cloned()
37
+ }
38
+
39
+ #[napi]
40
+ pub fn delete_cache(&self, name: String) -> bool {
41
+ let mut lock = self.caches.write();
42
+ lock.remove(&name).is_some()
43
+ }
44
+
45
+ #[napi]
46
+ pub fn clear(&self) {
47
+ let mut lock = self.caches.write();
48
+ lock.clear();
49
+ }
50
+ }
@@ -0,0 +1,135 @@
1
+ const test = require('node:test');
2
+ const assert = require('node:assert');
3
+ const { CacheManager } = require('../index.js'); // NAPI generated file
4
+
5
+ test('CacheManager - isolated caches', () => {
6
+ const manager = new CacheManager();
7
+
8
+ const cache1 = manager.createCache('lru-1', { policy: 'lru', capacity: 10 });
9
+ const cache2 = manager.createCache('lru-2', { policy: 'lru', capacity: 10 });
10
+
11
+ cache1.set('key', 'value-1');
12
+ cache2.set('key', 'value-2');
13
+
14
+ assert.strictEqual(cache1.get('key'), 'value-1');
15
+ assert.strictEqual(cache2.get('key'), 'value-2');
16
+
17
+ assert.deepStrictEqual(manager.getCache('lru-1').stats(), cache1.stats());
18
+
19
+ manager.deleteCache('lru-1');
20
+ assert.strictEqual(manager.getCache('lru-1'), null);
21
+ assert.ok(manager.getCache('lru-2') !== null);
22
+ });
23
+
24
+ test('Cache - DataType Preservation (String, Buffer, JSON Object)', () => {
25
+ const manager = new CacheManager();
26
+ const cache = manager.createCache('lru-types', { policy: 'lru', capacity: 10 });
27
+
28
+ // String
29
+ cache.set('str', 'hello world');
30
+ assert.strictEqual(cache.get('str'), 'hello world');
31
+
32
+ // Buffer
33
+ const buf = Buffer.from([1, 2, 3, 4]);
34
+ cache.set('buf', buf);
35
+ const retrievedBuf = cache.get('buf');
36
+ assert.ok(Buffer.isBuffer(retrievedBuf));
37
+ assert.deepStrictEqual(retrievedBuf, buf);
38
+
39
+ // JSON Object
40
+ const obj = { x: 42, y: 'test', z: [1, 2] };
41
+ cache.set('json', obj);
42
+ assert.deepStrictEqual(cache.get('json'), obj);
43
+ });
44
+
45
+ test('Cache Policies - LRU eviction', () => {
46
+ const manager = new CacheManager();
47
+ const cache = manager.createCache('lru-evict', { policy: 'lru', capacity: 3 });
48
+
49
+ cache.set('a', 1);
50
+ cache.set('b', 2);
51
+ cache.set('c', 3);
52
+
53
+ // Access a to make it most recently used, b becomes oldest
54
+ cache.get('a');
55
+
56
+ // Insert d, which should evict b
57
+ cache.set('d', 4);
58
+
59
+ assert.strictEqual(cache.get('b'), undefined);
60
+ assert.strictEqual(cache.get('a'), 1);
61
+ assert.strictEqual(cache.get('c'), 3);
62
+ assert.strictEqual(cache.get('d'), 4);
63
+ });
64
+
65
+ test('Cache Policies - ARC adaptation & eviction', () => {
66
+ const manager = new CacheManager();
67
+ const cache = manager.createCache('arc-evict', { policy: 'arc', capacity: 4 });
68
+
69
+ // Warm up ARC
70
+ cache.set('a', 1);
71
+ cache.set('b', 2);
72
+ cache.set('c', 3);
73
+ cache.set('d', 4);
74
+
75
+ // Retrieve some keys to establish frequency (moving them to T2)
76
+ cache.get('a');
77
+ cache.get('b');
78
+
79
+ // Now a and b are in T2 (frequent), c and d are in T1 (recent)
80
+ // Insert e, which triggers replace. Since |T1| > p, it evicts from T1 (evicting c or d)
81
+ cache.set('e', 5);
82
+
83
+ // Check stats
84
+ const stats = cache.stats();
85
+ assert.strictEqual(stats.size, 4);
86
+ });
87
+
88
+ test('Cache Policies - W-TinyLFU eviction competition', () => {
89
+ const manager = new CacheManager();
90
+ // W-TinyLFU capacity 10
91
+ const cache = manager.createCache('tinylfu-evict', { policy: 'tinylfu', capacity: 10 });
92
+
93
+ // Fill cache
94
+ for (let i = 0; i < 12; i++) {
95
+ cache.set(`key-${i}`, i);
96
+ }
97
+
98
+ // Check that size is limited to capacity
99
+ const stats = cache.stats();
100
+ assert.strictEqual(stats.capacity, 10);
101
+ assert.ok(stats.size <= 10);
102
+ });
103
+
104
+ test('Cache - TTL (Time To Live)', async () => {
105
+ const manager = new CacheManager();
106
+ const cache = manager.createCache('lru-ttl', { policy: 'lru', capacity: 10 });
107
+
108
+ cache.set('short-lived', 'expire-me', 10); // 10ms TTL
109
+ cache.set('long-lived', 'keep-me', 1000); // 1000ms TTL
110
+
111
+ assert.strictEqual(cache.get('short-lived'), 'expire-me');
112
+ assert.strictEqual(cache.get('long-lived'), 'keep-me');
113
+
114
+ // Wait 30ms for short-lived key to expire
115
+ await new Promise((resolve) => setTimeout(resolve, 30));
116
+
117
+ assert.strictEqual(cache.get('short-lived'), undefined);
118
+ assert.strictEqual(cache.get('long-lived'), 'keep-me');
119
+ });
120
+
121
+ test('Cache - keys() and delete()', () => {
122
+ const manager = new CacheManager();
123
+ const cache = manager.createCache('lru-ops', { policy: 'lru', capacity: 5 });
124
+
125
+ cache.set('a', 1);
126
+ cache.set('b', 2);
127
+ cache.set('c', 3);
128
+
129
+ assert.deepStrictEqual(cache.keys().sort(), ['a', 'b', 'c']);
130
+
131
+ assert.strictEqual(cache.delete('b'), true);
132
+ assert.strictEqual(cache.delete('non-existent'), false);
133
+
134
+ assert.deepStrictEqual(cache.keys().sort(), ['a', 'c']);
135
+ });