list-toolkit 2.2.6 → 2.3.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/README.md +40 -37
- package/llms-full.txt +743 -0
- package/llms.txt +100 -0
- package/package.json +34 -29
- package/src/cache/cache-fifo.d.ts +6 -0
- package/src/cache/cache-fifo.js +7 -4
- package/src/cache/cache-lfu.d.ts +18 -0
- package/src/cache/cache-lfu.js +18 -6
- package/src/cache/cache-lru.d.ts +74 -0
- package/src/cache/cache-lru.js +60 -5
- package/src/cache/cache-random.d.ts +20 -0
- package/src/cache/cache-random.js +17 -6
- package/src/cache/decorator.d.ts +46 -0
- package/src/cache/decorator.js +26 -2
- package/src/cache.d.ts +13 -0
- package/src/cache.js +7 -2
- package/src/ext-list.d.ts +3 -0
- package/src/ext-list.js +0 -2
- package/src/ext-slist.d.ts +3 -0
- package/src/ext-slist.js +0 -2
- package/src/ext-value-list.d.ts +3 -0
- package/src/ext-value-list.js +0 -2
- package/src/ext-value-slist.d.ts +3 -0
- package/src/ext-value-slist.js +0 -2
- package/src/heap/basics.d.ts +89 -0
- package/src/heap/basics.js +42 -5
- package/src/heap/leftist-heap.d.ts +107 -0
- package/src/heap/leftist-heap.js +54 -2
- package/src/heap/min-heap.d.ts +270 -0
- package/src/heap/min-heap.js +186 -2
- package/src/heap/skew-heap.d.ts +105 -0
- package/src/heap/skew-heap.js +54 -2
- package/src/heap.d.ts +3 -0
- package/src/heap.js +0 -2
- package/src/list/basics.d.ts +43 -0
- package/src/list/basics.js +26 -8
- package/src/list/core.d.ts +271 -0
- package/src/list/core.js +162 -7
- package/src/list/ext-value.d.ts +253 -0
- package/src/list/ext-value.js +40 -6
- package/src/list/ext.d.ts +242 -0
- package/src/list/ext.js +148 -10
- package/src/list/nodes.d.ts +336 -0
- package/src/list/nodes.js +141 -3
- package/src/list/ptr.d.ts +72 -0
- package/src/list/ptr.js +44 -2
- package/src/list/value.d.ts +292 -0
- package/src/list/value.js +47 -6
- package/src/list-helpers.d.ts +44 -0
- package/src/list-helpers.js +36 -3
- package/src/list-utils.d.ts +141 -0
- package/src/list-utils.js +89 -3
- package/src/list.d.ts +3 -0
- package/src/list.js +0 -2
- package/src/meta-utils.d.ts +212 -0
- package/src/meta-utils.js +152 -1
- package/src/nt-utils.d.ts +91 -0
- package/src/nt-utils.js +65 -4
- package/src/queue.d.ts +74 -0
- package/src/queue.js +28 -2
- package/src/slist/basics.d.ts +47 -0
- package/src/slist/basics.js +23 -8
- package/src/slist/core.d.ts +251 -0
- package/src/slist/core.js +151 -6
- package/src/slist/ext-value.d.ts +188 -0
- package/src/slist/ext-value.js +35 -6
- package/src/slist/ext.d.ts +182 -0
- package/src/slist/ext.js +114 -12
- package/src/slist/nodes.d.ts +361 -0
- package/src/slist/nodes.js +156 -3
- package/src/slist/ptr.d.ts +73 -0
- package/src/slist/ptr.js +45 -2
- package/src/slist/value.d.ts +246 -0
- package/src/slist/value.js +38 -6
- package/src/slist.d.ts +3 -0
- package/src/slist.js +0 -2
- package/src/stack.d.ts +59 -0
- package/src/stack.js +29 -3
- package/src/tree/splay-tree.d.ts +151 -0
- package/src/tree/splay-tree.js +94 -3
- package/src/value-list.d.ts +3 -0
- package/src/value-list.js +0 -2
- package/src/value-slist.d.ts +3 -0
- package/src/value-slist.js +0 -2
- package/cjs/cache/cache-fifo.js +0 -37
- package/cjs/cache/cache-lfu.js +0 -76
- package/cjs/cache/cache-lru.js +0 -100
- package/cjs/cache/cache-random.js +0 -77
- package/cjs/cache/decorator.js +0 -47
- package/cjs/cache.js +0 -27
- package/cjs/ext-list.js +0 -21
- package/cjs/ext-slist.js +0 -21
- package/cjs/ext-value-list.js +0 -21
- package/cjs/ext-value-slist.js +0 -21
- package/cjs/heap/basics.js +0 -63
- package/cjs/heap/leftist-heap.js +0 -124
- package/cjs/heap/min-heap.js +0 -294
- package/cjs/heap/skew-heap.js +0 -114
- package/cjs/heap.js +0 -21
- package/cjs/list/basics.js +0 -88
- package/cjs/list/core.js +0 -305
- package/cjs/list/ext-value.js +0 -88
- package/cjs/list/ext.js +0 -356
- package/cjs/list/nodes.js +0 -240
- package/cjs/list/ptr.js +0 -61
- package/cjs/list/value.js +0 -99
- package/cjs/list-helpers.js +0 -91
- package/cjs/list-utils.js +0 -141
- package/cjs/list.js +0 -21
- package/cjs/meta-utils.js +0 -171
- package/cjs/nt-utils.js +0 -132
- package/cjs/package.json +0 -1
- package/cjs/queue.js +0 -58
- package/cjs/slist/basics.js +0 -71
- package/cjs/slist/core.js +0 -362
- package/cjs/slist/ext-value.js +0 -82
- package/cjs/slist/ext.js +0 -336
- package/cjs/slist/nodes.js +0 -276
- package/cjs/slist/ptr.js +0 -87
- package/cjs/slist/value.js +0 -90
- package/cjs/slist.js +0 -21
- package/cjs/stack.js +0 -55
- package/cjs/tree/splay-tree.js +0 -362
- package/cjs/value-list.js +0 -21
- package/cjs/value-slist.js +0 -21
package/llms.txt
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
# list-toolkit
|
|
2
|
+
|
|
3
|
+
> Zero-dependency JavaScript library providing efficient list-based data structures: linked lists, caches, heaps, queues, stacks, and splay trees.
|
|
4
|
+
|
|
5
|
+
- NPM: https://npmjs.org/package/list-toolkit
|
|
6
|
+
- GitHub: https://github.com/uhop/list-toolkit
|
|
7
|
+
- Wiki: https://github.com/uhop/list-toolkit/wiki
|
|
8
|
+
- License: BSD-3-Clause
|
|
9
|
+
|
|
10
|
+
## Install
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
npm install list-toolkit
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Quick start
|
|
17
|
+
|
|
18
|
+
```js
|
|
19
|
+
import ValueList from 'list-toolkit/value-list.js';
|
|
20
|
+
|
|
21
|
+
const list = ValueList.from([1, 2, 3]);
|
|
22
|
+
for (const value of list) console.log(value); // 1, 2, 3
|
|
23
|
+
|
|
24
|
+
list.pushBack(4);
|
|
25
|
+
list.pushFront(0);
|
|
26
|
+
console.log(list.popFront()); // 0
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
```js
|
|
30
|
+
import List from 'list-toolkit/list.js';
|
|
31
|
+
|
|
32
|
+
// Node-based list: link properties are added directly to your objects
|
|
33
|
+
const a = {name: 'Alice'}, b = {name: 'Bob'};
|
|
34
|
+
const people = List.from([a, b]);
|
|
35
|
+
for (const node of people) console.log(node.name); // Alice, Bob
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Modules
|
|
39
|
+
|
|
40
|
+
### Linked lists (doubly linked, circular)
|
|
41
|
+
|
|
42
|
+
- **List** (`list-toolkit/list.js`) — hosted node-based DLL. Link properties (`next`/`prev`) are added to user objects. Customizable link names.
|
|
43
|
+
- **ValueList** (`list-toolkit/value-list.js`) — hosted value-based DLL. Wraps values in `ValueNode` containers.
|
|
44
|
+
- **ExtList** (`list-toolkit/ext-list.js`) — external (headless) node-based DLL. Points into an existing circular list.
|
|
45
|
+
- **ExtValueList** (`list-toolkit/ext-value-list.js`) — external (headless) value-based DLL.
|
|
46
|
+
|
|
47
|
+
### Linked lists (singly linked, circular)
|
|
48
|
+
|
|
49
|
+
- **SList** (`list-toolkit/slist.js`) — hosted node-based SLL.
|
|
50
|
+
- **ValueSList** (`list-toolkit/value-slist.js`) — hosted value-based SLL.
|
|
51
|
+
- **ExtSList** (`list-toolkit/ext-slist.js`) — external (headless) node-based SLL.
|
|
52
|
+
- **ExtValueSList** (`list-toolkit/ext-value-slist.js`) — external (headless) value-based SLL.
|
|
53
|
+
|
|
54
|
+
### Caches
|
|
55
|
+
|
|
56
|
+
- **CacheLRU** (`list-toolkit/cache.js`) — least recently used eviction. Default export.
|
|
57
|
+
- **CacheFIFO** (`list-toolkit/cache/cache-fifo.js`) — first in first out eviction.
|
|
58
|
+
- **CacheLFU** (`list-toolkit/cache/cache-lfu.js`) — least frequently used eviction.
|
|
59
|
+
- **CacheRandom** (`list-toolkit/cache/cache-random.js`) — random eviction.
|
|
60
|
+
- **cacheDecorator** (`list-toolkit/cache.js`) — decorator to cache function/method results.
|
|
61
|
+
|
|
62
|
+
All caches share the same API: `has(key)`, `find(key)`/`get(key)`, `register(key, value)`/`set(key, value)`, `remove(key)`/`delete(key)`, `clear()`.
|
|
63
|
+
|
|
64
|
+
### Heaps (priority queues)
|
|
65
|
+
|
|
66
|
+
- **MinHeap** (`list-toolkit/heap.js`) — array-based binary min-heap. Default export.
|
|
67
|
+
- **LeftistHeap** (`list-toolkit/heap/leftist-heap.js`) — merge-based leftist heap.
|
|
68
|
+
- **SkewHeap** (`list-toolkit/heap/skew-heap.js`) — merge-based skew heap.
|
|
69
|
+
|
|
70
|
+
All heaps support `less` or `compare` for ordering. Common API: `push(value)`, `pop()`, `top`/`peek()`, `merge()`, `has(value)`, `remove(value)`.
|
|
71
|
+
|
|
72
|
+
### Adapters
|
|
73
|
+
|
|
74
|
+
- **Queue** (`list-toolkit/queue.js`) — FIFO queue adapter. Methods: `add`/`push`/`enqueue`, `remove`/`pop`/`dequeue`, `peek`, `top`, `clear`.
|
|
75
|
+
- **Stack** (`list-toolkit/stack.js`) — LIFO stack adapter. Methods: `push`, `pop`, `peek`, `top`, `clear`.
|
|
76
|
+
|
|
77
|
+
### Trees
|
|
78
|
+
|
|
79
|
+
- **SplayTree** (`list-toolkit/tree/splay-tree.js`) — self-adjusting BST. Methods: `insert`, `find`, `remove`, `promote`, `split` (via `splitMaxTree`), `join`.
|
|
80
|
+
|
|
81
|
+
### Utilities
|
|
82
|
+
|
|
83
|
+
- **list-utils** (`list-toolkit/list-utils.js`) — push/append values, find/remove nodes by condition, validation.
|
|
84
|
+
- **nt-utils** (`list-toolkit/nt-utils.js`) — convert between null-terminated and circular lists.
|
|
85
|
+
- **meta-utils** (`list-toolkit/meta-utils.js`) — `addAlias`, `addAliases`, iterator helpers, `copyOptions`.
|
|
86
|
+
|
|
87
|
+
## Key concepts
|
|
88
|
+
|
|
89
|
+
- All lists are **circular** — the last node links back to the head.
|
|
90
|
+
- **Hosted lists** use a `HeadNode` sentinel. **External lists** are headless.
|
|
91
|
+
- **Value lists** wrap values in `ValueNode`. **Node lists** use link properties directly on user objects.
|
|
92
|
+
- Link names (`nextName`/`prevName`) are customizable, allowing objects to be in multiple lists.
|
|
93
|
+
- **Pointers** (`Ptr`) provide safe iteration with insert/remove during traversal.
|
|
94
|
+
- **TypeScript** declarations (`.d.ts`) are included for all modules.
|
|
95
|
+
|
|
96
|
+
## Links
|
|
97
|
+
|
|
98
|
+
- Docs: https://github.com/uhop/list-toolkit/wiki
|
|
99
|
+
- npm: https://www.npmjs.com/package/list-toolkit
|
|
100
|
+
- Full LLM reference: https://github.com/uhop/list-toolkit/blob/master/llms-full.txt
|
package/package.json
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "list-toolkit",
|
|
3
|
-
"version": "2.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "2.3.0",
|
|
4
|
+
"description": "Zero-dependency list-based data structures: linked lists (doubly/singly linked, circular), caches (LRU, LFU, FIFO), heaps, queues, stacks, splay trees.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
7
|
-
"./*":
|
|
8
|
-
"require": "./cjs/*",
|
|
9
|
-
"default": "./src/*"
|
|
10
|
-
}
|
|
7
|
+
"./*": "./src/*"
|
|
11
8
|
},
|
|
12
9
|
"scripts": {
|
|
13
|
-
"
|
|
14
|
-
"
|
|
15
|
-
"
|
|
10
|
+
"lint": "prettier --check .",
|
|
11
|
+
"lint:fix": "prettier --write .",
|
|
12
|
+
"ts-check": "tsc --noEmit",
|
|
13
|
+
"ts-test": "tape6 --flags FO '/ts-tests/test-*.*ts'",
|
|
14
|
+
"ts-test:bun": "tape6-bun --flags FO '/ts-tests/test-*.*ts'",
|
|
15
|
+
"ts-test:deno": "tape6-deno --flags FO '/ts-tests/test-*.*ts'",
|
|
16
16
|
"start": "tape6-server --trace",
|
|
17
17
|
"test": "tape6 --flags FO",
|
|
18
18
|
"test:bun": "tape6-bun --flags FO",
|
|
@@ -31,9 +31,23 @@
|
|
|
31
31
|
"keywords": [
|
|
32
32
|
"list",
|
|
33
33
|
"lists",
|
|
34
|
+
"linked-list",
|
|
35
|
+
"doubly-linked-list",
|
|
36
|
+
"singly-linked-list",
|
|
37
|
+
"circular-list",
|
|
34
38
|
"cache",
|
|
39
|
+
"lru-cache",
|
|
40
|
+
"lfu-cache",
|
|
41
|
+
"fifo-cache",
|
|
35
42
|
"heap",
|
|
36
|
-
"
|
|
43
|
+
"min-heap",
|
|
44
|
+
"priority-queue",
|
|
45
|
+
"queue",
|
|
46
|
+
"stack",
|
|
47
|
+
"splay-tree",
|
|
48
|
+
"data-structures",
|
|
49
|
+
"esm",
|
|
50
|
+
"zero-dependency"
|
|
37
51
|
],
|
|
38
52
|
"author": "Eugene Lazutkin <eugene.lazutkin@gmail.com> (http://www.lazutkin.com/)",
|
|
39
53
|
"funding": "https://github.com/sponsors/uhop",
|
|
@@ -41,31 +55,22 @@
|
|
|
41
55
|
"bugs": {
|
|
42
56
|
"url": "https://github.com/uhop/list-toolkit/issues"
|
|
43
57
|
},
|
|
58
|
+
"github": "http://github.com/uhop/list-toolkit",
|
|
44
59
|
"homepage": "https://github.com/uhop/list-toolkit#readme",
|
|
60
|
+
"llms": "https://raw.githubusercontent.com/uhop/list-toolkit/master/llms.txt",
|
|
61
|
+
"llmsFull": "https://raw.githubusercontent.com/uhop/list-toolkit/master/llms-full.txt",
|
|
45
62
|
"devDependencies": {
|
|
46
|
-
"
|
|
47
|
-
"
|
|
48
|
-
"
|
|
49
|
-
"
|
|
50
|
-
"
|
|
51
|
-
"tape-six-proc": "^1.2.2"
|
|
63
|
+
"nano-benchmark": "^1.0.10",
|
|
64
|
+
"prettier": "^3.8.1",
|
|
65
|
+
"tape-six": "^1.7.2",
|
|
66
|
+
"tape-six-proc": "^1.2.3",
|
|
67
|
+
"typescript": "^5.9.3"
|
|
52
68
|
},
|
|
53
69
|
"files": [
|
|
54
70
|
"/src",
|
|
55
|
-
"
|
|
71
|
+
"llms.txt",
|
|
72
|
+
"llms-full.txt"
|
|
56
73
|
],
|
|
57
|
-
"babel": {
|
|
58
|
-
"presets": [
|
|
59
|
-
[
|
|
60
|
-
"@babel/preset-env",
|
|
61
|
-
{
|
|
62
|
-
"targets": {
|
|
63
|
-
"node": "current"
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
]
|
|
67
|
-
]
|
|
68
|
-
},
|
|
69
74
|
"tape6": {
|
|
70
75
|
"tests": [
|
|
71
76
|
"/tests/test-*.*js"
|
package/src/cache/cache-fifo.js
CHANGED
|
@@ -1,19 +1,22 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
1
|
import CacheLRU from './cache-lru.js';
|
|
4
2
|
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
/**
|
|
4
|
+
* FIFO (First In First Out) cache. Evicts the oldest entry.
|
|
5
|
+
* Extends {@link CacheLRU} with FIFO eviction policy.
|
|
6
|
+
*/
|
|
7
7
|
export class CacheFIFO extends CacheLRU {
|
|
8
|
+
/** @override */
|
|
8
9
|
use(key) {
|
|
9
10
|
return this.dict.get(key);
|
|
10
11
|
}
|
|
12
|
+
/** @override */
|
|
11
13
|
addNew(key, value) {
|
|
12
14
|
this.list.pushBack({key, value});
|
|
13
15
|
const node = this.list.back;
|
|
14
16
|
this.dict.set(key, node);
|
|
15
17
|
return node;
|
|
16
18
|
}
|
|
19
|
+
/** @override */
|
|
17
20
|
evictAndReplace(key, value) {
|
|
18
21
|
const node = this.list.front;
|
|
19
22
|
this.list.moveToBack(node);
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import {CacheLRU} from './cache-lru.js';
|
|
2
|
+
|
|
3
|
+
/** LFU (Least Frequently Used) cache. Evicts the least frequently accessed entry. */
|
|
4
|
+
export class CacheLFU<K = unknown, V = unknown> extends CacheLRU<K, V> {
|
|
5
|
+
/**
|
|
6
|
+
* @param capacity - Maximum number of entries (default 10).
|
|
7
|
+
*/
|
|
8
|
+
constructor(capacity?: number);
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Reset all frequency counters to a given value.
|
|
12
|
+
* @param initialValue - Counter value to set (default 1).
|
|
13
|
+
* @returns `this` for chaining.
|
|
14
|
+
*/
|
|
15
|
+
resetCounters(initialValue?: number): this;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export default CacheLFU;
|
package/src/cache/cache-lfu.js
CHANGED
|
@@ -1,26 +1,30 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
1
|
import {addAlias} from '../meta-utils.js';
|
|
4
2
|
import MinHeap from '../heap/min-heap.js';
|
|
5
3
|
import CacheLRU from './cache-lru.js';
|
|
6
4
|
|
|
7
|
-
|
|
8
|
-
|
|
5
|
+
/**
|
|
6
|
+
* LFU (Least Frequently Used) cache. Evicts the least frequently accessed entry.
|
|
7
|
+
* Extends {@link CacheLRU} with a min-heap for frequency tracking.
|
|
8
|
+
*/
|
|
9
9
|
export class CacheLFU extends CacheLRU {
|
|
10
|
+
/** @param {number} [capacity=10] - Maximum number of entries. */
|
|
10
11
|
constructor(capacity = 10) {
|
|
11
12
|
super(capacity);
|
|
12
13
|
this.heap = new MinHeap({less: (a, b) => a.value.counter < b.value.counter});
|
|
13
14
|
}
|
|
15
|
+
/** @override */
|
|
14
16
|
use(key) {
|
|
15
17
|
const node = this.dict.get(key);
|
|
16
18
|
if (node) ++node.value.counter;
|
|
17
19
|
return node;
|
|
18
20
|
}
|
|
21
|
+
/** @override */
|
|
19
22
|
update(node, value) {
|
|
20
23
|
node.value.counter = 1;
|
|
21
24
|
node.value.value = value;
|
|
22
25
|
return this;
|
|
23
26
|
}
|
|
27
|
+
/** @override */
|
|
24
28
|
addNew(key, value) {
|
|
25
29
|
this.list.pushFront({key, value, counter: 1});
|
|
26
30
|
const node = this.list.front;
|
|
@@ -28,6 +32,7 @@ export class CacheLFU extends CacheLRU {
|
|
|
28
32
|
this.heap.push(node);
|
|
29
33
|
return node;
|
|
30
34
|
}
|
|
35
|
+
/** @override */
|
|
31
36
|
evictAndReplace(key, value) {
|
|
32
37
|
const node = this.heap.top;
|
|
33
38
|
this.dict.delete(node.value.key);
|
|
@@ -36,6 +41,7 @@ export class CacheLFU extends CacheLRU {
|
|
|
36
41
|
this.heap.updateTop();
|
|
37
42
|
return node;
|
|
38
43
|
}
|
|
44
|
+
/** @override */
|
|
39
45
|
remove(key) {
|
|
40
46
|
const node = this.dict.get(key);
|
|
41
47
|
if (node) {
|
|
@@ -45,14 +51,20 @@ export class CacheLFU extends CacheLRU {
|
|
|
45
51
|
}
|
|
46
52
|
return this;
|
|
47
53
|
}
|
|
54
|
+
/** @override */
|
|
48
55
|
clear() {
|
|
49
56
|
super.clear();
|
|
50
57
|
this.heap.clear();
|
|
51
58
|
return this;
|
|
52
59
|
}
|
|
60
|
+
/**
|
|
61
|
+
* Reset all frequency counters.
|
|
62
|
+
* @param {number} [initialValue=1] - Value to set all counters to.
|
|
63
|
+
* @returns {CacheLFU} `this` for chaining.
|
|
64
|
+
*/
|
|
53
65
|
resetCounters(initialValue = 1) {
|
|
54
|
-
for (const item of this.heap) {
|
|
55
|
-
item.counter = initialValue;
|
|
66
|
+
for (const item of this.heap.array) {
|
|
67
|
+
item.value.counter = initialValue;
|
|
56
68
|
}
|
|
57
69
|
return this;
|
|
58
70
|
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/** LRU (Least Recently Used) cache backed by a doubly linked value list and a Map. */
|
|
2
|
+
export class CacheLRU<K = unknown, V = unknown> {
|
|
3
|
+
/** Maximum number of entries. */
|
|
4
|
+
capacity: number;
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* @param capacity - Maximum number of entries (default 10).
|
|
8
|
+
*/
|
|
9
|
+
constructor(capacity?: number);
|
|
10
|
+
|
|
11
|
+
/** Whether the cache has no entries. */
|
|
12
|
+
get isEmpty(): boolean;
|
|
13
|
+
|
|
14
|
+
/** Number of entries currently stored. */
|
|
15
|
+
get size(): number;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Check whether a key exists in the cache.
|
|
19
|
+
* @param key - Key to look up.
|
|
20
|
+
* @returns `true` if the key is present.
|
|
21
|
+
*/
|
|
22
|
+
has(key: K): boolean;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Look up a value by key, promoting it as most recently used.
|
|
26
|
+
* @param key - Key to look up.
|
|
27
|
+
* @returns The value, or `undefined` if not found.
|
|
28
|
+
*/
|
|
29
|
+
find(key: K): V | undefined;
|
|
30
|
+
|
|
31
|
+
/** Alias for {@link find}. */
|
|
32
|
+
get(key: K): V | undefined;
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Remove an entry by key.
|
|
36
|
+
* @param key - Key to remove.
|
|
37
|
+
* @returns `this` for chaining.
|
|
38
|
+
*/
|
|
39
|
+
remove(key: K): this;
|
|
40
|
+
|
|
41
|
+
/** Alias for {@link remove}. */
|
|
42
|
+
delete(key: K): this;
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Add or update an entry. Evicts the least recently used entry if at capacity.
|
|
46
|
+
* @param key - Key to register.
|
|
47
|
+
* @param value - Value to associate.
|
|
48
|
+
* @returns `this` for chaining.
|
|
49
|
+
*/
|
|
50
|
+
register(key: K, value: V): this;
|
|
51
|
+
|
|
52
|
+
/** Alias for {@link register}. */
|
|
53
|
+
add(key: K, value: V): this;
|
|
54
|
+
|
|
55
|
+
/** Alias for {@link register}. */
|
|
56
|
+
set(key: K, value: V): this;
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Remove all entries.
|
|
60
|
+
* @returns `this` for chaining.
|
|
61
|
+
*/
|
|
62
|
+
clear(): this;
|
|
63
|
+
|
|
64
|
+
/** Iterate over `{key, value}` entry objects from most to least recently used. */
|
|
65
|
+
[Symbol.iterator](): IterableIterator<{key: K; value: V}>;
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Iterate over `{key, value}` entry objects from least to most recently used.
|
|
69
|
+
* @returns An iterable iterator.
|
|
70
|
+
*/
|
|
71
|
+
getReverseIterator(): IterableIterator<{key: K; value: V}>;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export default CacheLRU;
|
package/src/cache/cache-lru.js
CHANGED
|
@@ -1,30 +1,47 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
1
|
import ValueList from '../value-list.js';
|
|
4
2
|
import {addAliases} from '../meta-utils.js';
|
|
5
3
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
4
|
+
/**
|
|
5
|
+
* LRU (Least Recently Used) cache backed by a doubly linked value list.
|
|
6
|
+
* Evicts the least recently used item when capacity is exceeded.
|
|
7
|
+
*/
|
|
9
8
|
export class CacheLRU {
|
|
9
|
+
/** @param {number} [capacity=10] - Maximum number of entries. */
|
|
10
10
|
constructor(capacity = 10) {
|
|
11
11
|
this.capacity = capacity;
|
|
12
12
|
this.list = new ValueList();
|
|
13
13
|
this.dict = new Map();
|
|
14
14
|
}
|
|
15
|
+
/** Whether the cache has no entries. */
|
|
15
16
|
get isEmpty() {
|
|
16
17
|
return !this.dict.size;
|
|
17
18
|
}
|
|
19
|
+
/** The number of entries in the cache. */
|
|
18
20
|
get size() {
|
|
19
21
|
return this.dict.size;
|
|
20
22
|
}
|
|
23
|
+
/**
|
|
24
|
+
* Check whether a key exists.
|
|
25
|
+
* @param {*} key - Key to look up.
|
|
26
|
+
* @returns {boolean} `true` if the key exists.
|
|
27
|
+
*/
|
|
21
28
|
has(key) {
|
|
22
29
|
return this.dict.has(key);
|
|
23
30
|
}
|
|
31
|
+
/**
|
|
32
|
+
* Retrieve the value for a key, marking it as recently used.
|
|
33
|
+
* @param {*} key - Key to look up.
|
|
34
|
+
* @returns {*} The value, or `undefined` if not found.
|
|
35
|
+
*/
|
|
24
36
|
find(key) {
|
|
25
37
|
const node = this.use(key);
|
|
26
38
|
return node ? node.value.value : undefined;
|
|
27
39
|
}
|
|
40
|
+
/**
|
|
41
|
+
* Remove an entry by key.
|
|
42
|
+
* @param {*} key - Key to remove.
|
|
43
|
+
* @returns {CacheLRU} `this` for chaining.
|
|
44
|
+
*/
|
|
28
45
|
remove(key) {
|
|
29
46
|
const node = this.dict.get(key);
|
|
30
47
|
if (node) {
|
|
@@ -33,6 +50,12 @@ export class CacheLRU {
|
|
|
33
50
|
}
|
|
34
51
|
return this;
|
|
35
52
|
}
|
|
53
|
+
/**
|
|
54
|
+
* Add or update an entry. Evicts the LRU item if at capacity.
|
|
55
|
+
* @param {*} key - Key to register.
|
|
56
|
+
* @param {*} value - Value to store.
|
|
57
|
+
* @returns {CacheLRU} `this` for chaining.
|
|
58
|
+
*/
|
|
36
59
|
register(key, value) {
|
|
37
60
|
const node = this.use(key);
|
|
38
61
|
if (node) {
|
|
@@ -46,21 +69,44 @@ export class CacheLRU {
|
|
|
46
69
|
this.addNew(key, value);
|
|
47
70
|
return this;
|
|
48
71
|
}
|
|
72
|
+
/**
|
|
73
|
+
* Mark a key as recently used and return its node.
|
|
74
|
+
* @param {*} key - Key to look up.
|
|
75
|
+
* @returns {object|undefined} The internal node, or `undefined`.
|
|
76
|
+
*/
|
|
49
77
|
use(key) {
|
|
50
78
|
const node = this.dict.get(key);
|
|
51
79
|
if (node) this.list.moveToFront(node);
|
|
52
80
|
return node;
|
|
53
81
|
}
|
|
82
|
+
/**
|
|
83
|
+
* Update the value of an existing node.
|
|
84
|
+
* @param {object} node - Internal node to update.
|
|
85
|
+
* @param {*} value - New value.
|
|
86
|
+
* @returns {CacheLRU} `this` for chaining.
|
|
87
|
+
*/
|
|
54
88
|
update(node, value) {
|
|
55
89
|
node.value.value = value;
|
|
56
90
|
return this;
|
|
57
91
|
}
|
|
92
|
+
/**
|
|
93
|
+
* Add a new entry to the cache.
|
|
94
|
+
* @param {*} key - Key.
|
|
95
|
+
* @param {*} value - Value.
|
|
96
|
+
* @returns {object} The newly created node.
|
|
97
|
+
*/
|
|
58
98
|
addNew(key, value) {
|
|
59
99
|
this.list.pushFront({key, value});
|
|
60
100
|
const node = this.list.front;
|
|
61
101
|
this.dict.set(key, node);
|
|
62
102
|
return node;
|
|
63
103
|
}
|
|
104
|
+
/**
|
|
105
|
+
* Evict the LRU entry and replace it with a new key/value.
|
|
106
|
+
* @param {*} key - New key.
|
|
107
|
+
* @param {*} value - New value.
|
|
108
|
+
* @returns {object} The reused node.
|
|
109
|
+
*/
|
|
64
110
|
evictAndReplace(key, value) {
|
|
65
111
|
const node = this.list.back;
|
|
66
112
|
this.list.moveToFront(node);
|
|
@@ -69,14 +115,23 @@ export class CacheLRU {
|
|
|
69
115
|
node.value = {key, value};
|
|
70
116
|
return node;
|
|
71
117
|
}
|
|
118
|
+
/**
|
|
119
|
+
* Remove all entries.
|
|
120
|
+
* @returns {CacheLRU} `this` for chaining.
|
|
121
|
+
*/
|
|
72
122
|
clear() {
|
|
73
123
|
this.dict.clear();
|
|
74
124
|
this.list.clear();
|
|
75
125
|
return this;
|
|
76
126
|
}
|
|
127
|
+
/** Iterate over `{key, value}` entries from most to least recently used. */
|
|
77
128
|
[Symbol.iterator]() {
|
|
78
129
|
return this.list[Symbol.iterator]();
|
|
79
130
|
}
|
|
131
|
+
/**
|
|
132
|
+
* Get an iterable over entries in reverse (LRU-first) order.
|
|
133
|
+
* @returns {Iterable} An iterable iterator of `{key, value}` objects.
|
|
134
|
+
*/
|
|
80
135
|
getReverseIterator() {
|
|
81
136
|
return this.list.getReverseIterator();
|
|
82
137
|
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import {CacheLRU} from './cache-lru.js';
|
|
2
|
+
|
|
3
|
+
/** Random eviction cache. Evicts a randomly selected entry. */
|
|
4
|
+
export class CacheRandom<K = unknown, V = unknown> extends CacheLRU<K, V> {
|
|
5
|
+
/** Next ID counter for random ordering. */
|
|
6
|
+
nextId: number;
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* @param capacity - Maximum number of entries (default 10).
|
|
10
|
+
*/
|
|
11
|
+
constructor(capacity?: number);
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Reset all IDs and rebuild the internal heap.
|
|
15
|
+
* @returns `this` for chaining.
|
|
16
|
+
*/
|
|
17
|
+
resetIds(): this;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export default CacheRandom;
|
|
@@ -1,24 +1,28 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
1
|
import {addAlias} from '../meta-utils.js';
|
|
4
2
|
import MinHeap from '../heap/min-heap.js';
|
|
5
3
|
import CacheLRU from './cache-lru.js';
|
|
6
4
|
|
|
7
|
-
|
|
8
|
-
|
|
5
|
+
/**
|
|
6
|
+
* Random eviction cache. Evicts a randomly chosen entry.
|
|
7
|
+
* Extends {@link CacheLRU} with random eviction via a min-heap.
|
|
8
|
+
*/
|
|
9
9
|
export class CacheRandom extends CacheLRU {
|
|
10
|
+
/** @param {number} [capacity=10] - Maximum number of entries. */
|
|
10
11
|
constructor(capacity = 10) {
|
|
11
12
|
super(capacity);
|
|
12
13
|
this.heap = new MinHeap({less: (a, b) => a.value.id > b.value.id});
|
|
13
14
|
this.nextId = 0;
|
|
14
15
|
}
|
|
16
|
+
/** @override */
|
|
15
17
|
use(key) {
|
|
16
18
|
return this.dict.get(key);
|
|
17
19
|
}
|
|
20
|
+
/** @override */
|
|
18
21
|
update(node, value) {
|
|
19
22
|
node.value.value = value;
|
|
20
23
|
return this;
|
|
21
24
|
}
|
|
25
|
+
/** @override */
|
|
22
26
|
addNew(key, value) {
|
|
23
27
|
this.list.pushFront({key, value, id: this.nextId++});
|
|
24
28
|
const node = this.list.front;
|
|
@@ -26,6 +30,7 @@ export class CacheRandom extends CacheLRU {
|
|
|
26
30
|
this.heap.push(node);
|
|
27
31
|
return node;
|
|
28
32
|
}
|
|
33
|
+
/** @override */
|
|
29
34
|
evictAndReplace(key, value) {
|
|
30
35
|
const index = Math.floor(this.heap.length * Math.random());
|
|
31
36
|
|
|
@@ -42,6 +47,7 @@ export class CacheRandom extends CacheLRU {
|
|
|
42
47
|
|
|
43
48
|
return node;
|
|
44
49
|
}
|
|
50
|
+
/** @override */
|
|
45
51
|
remove(key) {
|
|
46
52
|
const node = this.dict.get(key);
|
|
47
53
|
if (node) {
|
|
@@ -51,16 +57,21 @@ export class CacheRandom extends CacheLRU {
|
|
|
51
57
|
}
|
|
52
58
|
return this;
|
|
53
59
|
}
|
|
60
|
+
/** @override */
|
|
54
61
|
clear() {
|
|
55
62
|
super.clear();
|
|
56
63
|
this.heap.clear();
|
|
57
64
|
this.nextId = 0;
|
|
58
65
|
return this;
|
|
59
66
|
}
|
|
67
|
+
/**
|
|
68
|
+
* Reset all internal IDs and rebuild the heap.
|
|
69
|
+
* @returns {CacheRandom} `this` for chaining.
|
|
70
|
+
*/
|
|
60
71
|
resetIds() {
|
|
61
72
|
this.nextId = 0;
|
|
62
|
-
for (const item of this.heap) {
|
|
63
|
-
item.id = this.nextId++;
|
|
73
|
+
for (const item of this.heap.array) {
|
|
74
|
+
item.value.id = this.nextId++;
|
|
64
75
|
}
|
|
65
76
|
const array = this.heap.array;
|
|
66
77
|
this.heap.clear().merge(array);
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import {CacheLRU} from './cache-lru.js';
|
|
2
|
+
|
|
3
|
+
/** A wrapped function with attached cache and original function reference. */
|
|
4
|
+
export interface WrappedFn<F extends (...args: any[]) => any, K = unknown, V = unknown> {
|
|
5
|
+
(...args: Parameters<F>): ReturnType<F>;
|
|
6
|
+
/** The original unwrapped function. */
|
|
7
|
+
fn: F;
|
|
8
|
+
/** The cache backing this wrapper. */
|
|
9
|
+
cache: CacheLRU<K, V>;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Wrap a function with caching. The first argument is used as the cache key.
|
|
14
|
+
* @param fn - Function to wrap.
|
|
15
|
+
* @param cache - Cache instance to use.
|
|
16
|
+
* @returns A wrapped function with `.fn` and `.cache` properties.
|
|
17
|
+
*/
|
|
18
|
+
export function decorateFn<F extends (...args: any[]) => any>(fn: F, cache: CacheLRU): WrappedFn<F>;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Decorate an own property descriptor's `value` with caching.
|
|
22
|
+
* @param object - Object owning the property.
|
|
23
|
+
* @param key - Property name.
|
|
24
|
+
* @param cache - Cache instance to use.
|
|
25
|
+
* @returns The wrapped function.
|
|
26
|
+
*/
|
|
27
|
+
export function decorate(object: object, key: PropertyKey, cache: CacheLRU): WrappedFn<any>;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Decorate a method on an object with caching (direct assignment).
|
|
31
|
+
* @param object - Object owning the method.
|
|
32
|
+
* @param key - Method name.
|
|
33
|
+
* @param cache - Cache instance to use.
|
|
34
|
+
* @returns The wrapped function.
|
|
35
|
+
*/
|
|
36
|
+
export function decorateMethod(object: object, key: PropertyKey, cache: CacheLRU): WrappedFn<any>;
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Retrieve the cache from a previously decorated property.
|
|
40
|
+
* @param object - Object owning the property.
|
|
41
|
+
* @param key - Property name.
|
|
42
|
+
* @returns The cache instance.
|
|
43
|
+
*/
|
|
44
|
+
export function getCache(object: object, key: PropertyKey): CacheLRU;
|
|
45
|
+
|
|
46
|
+
export default decorate;
|