evicting-cache 1.0.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/LICENSE +12 -0
- package/README.md +2 -0
- package/package.json +46 -0
- package/src/evicting-cache.js +136 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
Copyright (c) 2023, Jason DiMeo
|
|
2
|
+
|
|
3
|
+
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided
|
|
4
|
+
that the above copyright notice and this permission notice appear in all copies.
|
|
5
|
+
|
|
6
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE
|
|
7
|
+
INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE
|
|
8
|
+
FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
9
|
+
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
|
|
10
|
+
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
11
|
+
|
|
12
|
+
Source: http://opensource.org/licenses/ISC
|
package/README.md
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "evicting-cache",
|
|
3
|
+
"author": "D1g1talEntr0py",
|
|
4
|
+
"version": "1.0.0",
|
|
5
|
+
"license": "ISC",
|
|
6
|
+
"description": "Cache implementation with an LRU evicting policy",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"exports": "./src/evicting-cache.js",
|
|
9
|
+
"files": [
|
|
10
|
+
"src/"
|
|
11
|
+
],
|
|
12
|
+
"packageManager": "pnpm@8.13.0+sha256.fbcf256db6d06bc189e31df34b3ed61220f3ba9f78a2ca8fe7be0fce4670dbd3",
|
|
13
|
+
"keywords": [],
|
|
14
|
+
"dependencies": {
|
|
15
|
+
"@d1g1tal/collections": "^1.0.0"
|
|
16
|
+
},
|
|
17
|
+
"devDependencies": {
|
|
18
|
+
"esbuild": "^0.19.9",
|
|
19
|
+
"esbuild-library": "^1.0.5",
|
|
20
|
+
"eslint": "^8.56.0",
|
|
21
|
+
"eslint-plugin-compat": "^4.2.0",
|
|
22
|
+
"eslint-plugin-jsdoc": "^46.9.1",
|
|
23
|
+
"jest": "^29.7.0"
|
|
24
|
+
},
|
|
25
|
+
"browserslist": [
|
|
26
|
+
"defaults",
|
|
27
|
+
"not ios_saf < 15",
|
|
28
|
+
"not op_mini all"
|
|
29
|
+
],
|
|
30
|
+
"jest": {
|
|
31
|
+
"verbose": true,
|
|
32
|
+
"transform": {},
|
|
33
|
+
"coverageDirectory": "./tests/coverage/",
|
|
34
|
+
"coveragePathIgnorePatterns": [
|
|
35
|
+
"/node_modules/"
|
|
36
|
+
],
|
|
37
|
+
"collectCoverage": true,
|
|
38
|
+
"collectCoverageFrom": [
|
|
39
|
+
"src/**/*"
|
|
40
|
+
]
|
|
41
|
+
},
|
|
42
|
+
"scripts": {
|
|
43
|
+
"build": "node ./esbuild.js",
|
|
44
|
+
"test": "node --no-warnings --experimental-vm-modules node_modules/jest/bin/jest.js"
|
|
45
|
+
}
|
|
46
|
+
}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import LinkedMap from '@d1g1tal/collections/dist/linked-map.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* JavaScript implementation of a Least Recently Used(LRU) Cache using a doubly linked list.
|
|
5
|
+
*
|
|
6
|
+
* @template K
|
|
7
|
+
* @template V
|
|
8
|
+
* @type {EvictingCache<K, V>}
|
|
9
|
+
*/
|
|
10
|
+
export default class EvictingCache {
|
|
11
|
+
/** @type {number} */
|
|
12
|
+
#capacity;
|
|
13
|
+
/** @type {LinkedMap<K, V>} */
|
|
14
|
+
#cache;
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Creates a new Evicting Cache with the given capacity.
|
|
18
|
+
*
|
|
19
|
+
* @param {number} [capacity=100] The maximum number of key-value pairs the cache can hold.
|
|
20
|
+
*/
|
|
21
|
+
constructor(capacity = 100) {
|
|
22
|
+
if (capacity < 1) { throw new RangeError('capacity must be greater than 0') }
|
|
23
|
+
if (!Number.isInteger(capacity)) { throw new RangeError('capacity must be an integer') }
|
|
24
|
+
|
|
25
|
+
this.#capacity = capacity;
|
|
26
|
+
this.#cache = new LinkedMap();
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Returns the value associated with the given key from the cache and updates the LRU order.
|
|
31
|
+
*
|
|
32
|
+
* @param {K} key The key to get the value for.
|
|
33
|
+
* @returns {V|null} The associated value if the key is in the cache, or null otherwise.
|
|
34
|
+
*/
|
|
35
|
+
get(key) {
|
|
36
|
+
const value = this.#cache.get(key);
|
|
37
|
+
if (!value) { return null }
|
|
38
|
+
|
|
39
|
+
this.#cache.moveToFirst(key);
|
|
40
|
+
|
|
41
|
+
return value;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Returns true if the given key is in the cache, false otherwise.
|
|
46
|
+
*
|
|
47
|
+
* @param {K} key The key to check.
|
|
48
|
+
* @returns {boolean} True if the key is in the cache, false otherwise.
|
|
49
|
+
*/
|
|
50
|
+
has(key) {
|
|
51
|
+
return this.#cache.has(key);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Adds a new key-value pair to the cache and updates the LRU order.
|
|
56
|
+
* If adding the new pair will exceed the capacity, removes the least recently used pair from the cache.
|
|
57
|
+
*
|
|
58
|
+
* @param {K} key The key to add.
|
|
59
|
+
* @param {V} value The value to add.
|
|
60
|
+
* @returns {void}
|
|
61
|
+
*/
|
|
62
|
+
put(key, value) {
|
|
63
|
+
this.#put(key, value);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Returns the value for the key if it exists in the cache. If not, put the key-value pair into the cache and return the value.
|
|
68
|
+
* @param {K} key The key.
|
|
69
|
+
* @param {function(): V} producer The value to put if the key does not exist in the cache.
|
|
70
|
+
* @returns {V} The value corresponding to the key.
|
|
71
|
+
*/
|
|
72
|
+
getOrPut(key, producer) {
|
|
73
|
+
return this.get(key) ?? this.#put(key, producer());
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Removes the least recently used key-value pair from the cache.
|
|
78
|
+
*
|
|
79
|
+
* @returns {boolean} True if an item was removed, false otherwise.
|
|
80
|
+
*/
|
|
81
|
+
evict() {
|
|
82
|
+
return this.#cache.size ? this.#cache.removeLast() : false;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Clears the cache and the LRU list.
|
|
87
|
+
*
|
|
88
|
+
* @returns {void}
|
|
89
|
+
*/
|
|
90
|
+
clear() {
|
|
91
|
+
this.#cache.clear();
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Gets the capacity of the cache.
|
|
96
|
+
* This is the maximum number of key-value pairs the cache can hold.
|
|
97
|
+
* This is not the number of key-value pairs in the cache.
|
|
98
|
+
*
|
|
99
|
+
* @readonly
|
|
100
|
+
* @returns {number} The capacity of the cache.
|
|
101
|
+
*/
|
|
102
|
+
get capacity() {
|
|
103
|
+
return this.#capacity;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Gets the size of the cache.
|
|
108
|
+
* This is the number of key-value pairs in the cache.
|
|
109
|
+
* This is not the capacity of the cache.
|
|
110
|
+
* The capacity is the maximum number of key-value pairs the cache can hold.
|
|
111
|
+
* The size is the number of key-value pairs currently in the cache.
|
|
112
|
+
* The size will be less than or equal to the capacity.
|
|
113
|
+
*
|
|
114
|
+
* @returns {number} The size of the cache.
|
|
115
|
+
*/
|
|
116
|
+
get size() {
|
|
117
|
+
return this.#cache.size;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Gets the description of the object.
|
|
122
|
+
*
|
|
123
|
+
* @override
|
|
124
|
+
* @returns {string} The description of the object.
|
|
125
|
+
*/
|
|
126
|
+
get [Symbol.toStringTag]() {
|
|
127
|
+
return 'EvictingCache';
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
#put(key, value) {
|
|
131
|
+
this.#cache.addFirst(key, value);
|
|
132
|
+
if (this.#cache.size > this.#capacity) { this.evict() }
|
|
133
|
+
|
|
134
|
+
return value;
|
|
135
|
+
}
|
|
136
|
+
}
|