authorgravatar for git@paperclover.netclover caruso <git@paperclover.net> 2026-02-03 03:26:50-08:00
committergravatar for git@paperclover.netclover caruso <git@paperclover.net> 2026-02-03 03:44:47-08:00
logfb973bb80728e2a703b326d99d295ad881e11d96
tree4ef94d2282cc24f44f0eb0134c2b7aa9cb89dff2
parent6249c9f2acd13afd89ef2ee201902c3567c8d29e
signature Signed by SSH key SHA256:xbd+BjjhyBfwk7GVoURf9Yx0gzDerHbvYv7SddNWmAs

fix: lru revival must revive all state

resolves #44 (tho cant reproduce it's exact error in a test). coverage on this module is really high so its probably a revive bug.

2 files changed, 24 insertions(+), 2 deletions(-)

lib/Lru.test.ts+16
...@@ -23,6 +23,7 @@ test("lru basic", () => {...@@ -23,6 +23,7 @@ test("lru basic", () => {
23 ["c", 5, 1],23 ["c", 5, 1],
24 ]);24 ]);
25 lru.capacity = 10;25 lru.capacity = 10;
26 lru.set("a", -1);
26 lru.set("a", 0);27 lru.set("a", 0);
27 lru.set("z", 1);28 lru.set("z", 1);
28 lru.set("x", 1);29 lru.set("x", 1);
...@@ -132,6 +133,21 @@ test("throws on evicting a locked item", () => {...@@ -132,6 +133,21 @@ test("throws on evicting a locked item", () => {
132 void lru.lock("e");133 void lru.lock("e");
133 assert.throws(() => lru.ensureUnusedCapacity(3), RangeError);134 assert.throws(() => lru.ensureUnusedCapacity(3), RangeError);
134});135});
136test("revival", () => {
137 let lru = new Lru<string, number>({ capacity: 3, sizeFn: (v) => 1 });
138 lru.set("a", 1);
139 lru.set("b", 2);
140 lru.set("c", 3);
141 lru.set("d", 4);
142 const state = lru.serialize();
143 lru = new Lru({ capacity: 3, sizeFn: (v) => 1 });
144 lru.revive(state);
145 assert.equal(lru.get("c"), 3);
146 assert.equal(lru.get("a"), undefined);
147 lru.set("e", 5);
148 assert.equal(lru.get("d"), 4);
149 assert.equal(lru.get("e"), 5);
150});
135151
136import assert from "node:assert";152import assert from "node:assert";
137import { test } from "node:test";153import { test } from "node:test";
lib/Lru.ts+8-2
...@@ -232,7 +232,8 @@ export class Lru<K, V> extends Map<K, V> {...@@ -232,7 +232,8 @@ export class Lru<K, V> extends Map<K, V> {
232 const order = this.#order;232 const order = this.#order;
233 let prev: Order<K> | null = null;233 let prev: Order<K> | null = null;
234 let pk!: K;234 let pk!: K;
235 for (let i = entries.length - 1; i > 0; i -= 1) {235 let totalSize = 0;
236 for (let i = entries.length - 1; i >= 0; i -= 1) {
236 const [ik, iv, size] = UNWRAP(entries[i]);237 const [ik, iv, size] = UNWRAP(entries[i]);
237 const kv = map(ik, iv);238 const kv = map(ik, iv);
238 if (!kv) continue;239 if (!kv) continue;
...@@ -240,9 +241,14 @@ export class Lru<K, V> extends Map<K, V> {...@@ -240,9 +241,14 @@ export class Lru<K, V> extends Map<K, V> {
240 super.set(k, v);241 super.set(k, v);
241 const current: Order<K> = [size, none, prev ? pk : none, 0];242 const current: Order<K> = [size, none, prev ? pk : none, 0];
242 if (prev) prev[1] = k;243 if (prev) prev[1] = k;
243 order.set(k, prev = current);244 else this.#tail = k;
245 order.set(k, current);
246 prev = current;
244 pk = k;247 pk = k;
248 totalSize += size;
245 }249 }
250 if (prev) this.#head = pk;
251 this.#used = totalSize;
246 }252 }
247}253}
248254