| ... | @@ -1,4 +1,5 @@ | ... | @@ -1,4 +1,5 @@ |
| 1 | /** | 1 | /** |
| | 2 | * Least-recently-used cache. |
| 2 | * This module is intended to be imported via the main class. | 3 | * This module is intended to be imported via the main class. |
| 3 | * | 4 | * |
| 4 | * import { Lru } from '@clo/lib/Lru'; | 5 | * import { Lru } from '@clo/lib/Lru'; |
| ... | @@ -12,19 +13,19 @@ | ... | @@ -12,19 +13,19 @@ |
| 12 | interface Options<K, V> { | 13 | interface Options<K, V> { |
| 13 | capacity: number; | 14 | capacity: number; |
| 14 | /** | 15 | /** |
| 15 | * Decide how many units of size each entry takes. | 16 | * decide how many units of size each entry takes. |
| 16 | * For example, accessing `.byteLength` of a Buffer. | 17 | * For example, accessing `.byteLength` of a Buffer. |
| 17 | */ | 18 | */ |
| 18 | sizeFn?: (value: V, key: K) => number; | 19 | sizeFn?: (value: V, key: K) => number; |
| 19 | /** Called when `set` or `ensureUnusedCapacity` removes items. */ | 20 | /** called when `set` or `ensureUnusedCapacity` removes items. */ |
| 20 | evict?: (value: V, key: K) => void; | 21 | evict?: (value: V, key: K) => void; |
| 21 | /** Called when anything removes items. */ | 22 | /** called when anything removes items. */ |
| 22 | delete?: (value: V, key: K) => void; | 23 | delete?: (value: V, key: K) => void; |
| 23 | } | 24 | } |
| 24 | | 25 | |
| 25 | /** | 26 | /** |
| 26 | * A Least-recently-used cache has a set capacity, removing the oldest item | 27 | * a Least-recently-used cache has a set capacity, removing the oldest item |
| 27 | * when trying to insert an item that would exceed such capacity. This | 28 | * when trying to insert an item that would exceed such capacity. this |
| 28 | * implementation supports variable sized items, eviction callbacks, entry | 29 | * implementation supports variable sized items, eviction callbacks, entry |
| 29 | * locking, and JSON-compatible serialization. | 30 | * locking, and JSON-compatible serialization. |
| 30 | * | 31 | * |
| ... | @@ -32,9 +33,9 @@ interface Options<K, V> { | ... | @@ -32,9 +33,9 @@ interface Options<K, V> { |
| 32 | */ | 33 | */ |
| 33 | export class Lru<K, V> extends Map<K, V> { | 34 | export class Lru<K, V> extends Map<K, V> { |
| 34 | #order = new Map<K, Order<K>>(); | 35 | #order = new Map<K, Order<K>>(); |
| 35 | /** The most recently used */ | 36 | /** the most recently used */ |
| 36 | #head: K | none = none; | 37 | #head: K | none = none; |
| 37 | /** The least recently used */ | 38 | /** the least recently used */ |
| 38 | #tail: K | none = none; | 39 | #tail: K | none = none; |
| 39 | #used: number = 0; | 40 | #used: number = 0; |
| 40 | #locked: number = 0; | 41 | #locked: number = 0; |
| ... | @@ -119,7 +120,7 @@ export class Lru<K, V> extends Map<K, V> { | ... | @@ -119,7 +120,7 @@ export class Lru<K, V> extends Map<K, V> { |
| 119 | return this; | 120 | return this; |
| 120 | } | 121 | } |
| 121 | | 122 | |
| 122 | /** Prevent an item from being automatically evicted */ | 123 | /** prevent an item from being automatically evicted */ |
| 123 | lock(key: K): ts.Dispose { | 124 | lock(key: K): ts.Dispose { |
| 124 | const order = this.#order.get(key); | 125 | const order = this.#order.get(key); |
| 125 | if (!order) throw new Error(`Key ${key} not in Lru`); | 126 | if (!order) throw new Error(`Key ${key} not in Lru`); |
| ... | @@ -131,7 +132,7 @@ export class Lru<K, V> extends Map<K, V> { | ... | @@ -131,7 +132,7 @@ export class Lru<K, V> extends Map<K, V> { |
| 131 | }); | 132 | }); |
| 132 | } | 133 | } |
| 133 | | 134 | |
| 134 | /** Evict items until there are `unused` capacity slots. */ | 135 | /** evict items until there are `unused` capacity slots. */ |
| 135 | ensureUnusedCapacity(unused: number) { | 136 | ensureUnusedCapacity(unused: number) { |
| 136 | const uc = this.unlockedCapacity; | 137 | const uc = this.unlockedCapacity; |
| 137 | if (unused > uc) { | 138 | if (unused > uc) { |
| ... | @@ -171,7 +172,7 @@ export class Lru<K, V> extends Map<K, V> { | ... | @@ -171,7 +172,7 @@ export class Lru<K, V> extends Map<K, V> { |
| 171 | this.#used -= needed - remain; | 172 | this.#used -= needed - remain; |
| 172 | } | 173 | } |
| 173 | | 174 | |
| 174 | /** Unlink an entry without removing it. */ | 175 | /** unlink an entry without removing it. */ |
| 175 | #evict(key: K) { | 176 | #evict(key: K) { |
| 176 | const entry = this.#order.get(key); | 177 | const entry = this.#order.get(key); |
| 177 | if (!entry) return null; | 178 | if (!entry) return null; |
| ... | @@ -243,7 +244,7 @@ export class Lru<K, V> extends Map<K, V> { | ... | @@ -243,7 +244,7 @@ export class Lru<K, V> extends Map<K, V> { |
| 243 | } | 244 | } |
| 244 | } | 245 | } |
| 245 | | 246 | |
| 246 | /** The first item is the most recently used. */ | 247 | /** the first item is the most recently used. */ |
| 247 | type Serialized<K, V> = Array<[K, V, number]>; | 248 | type Serialized<K, V> = Array<[K, V, number]>; |
| 248 | | 249 | |
| 249 | interface SerializeOptions<IK, IV, OK = IK, OV = IV> { | 250 | interface SerializeOptions<IK, IV, OK = IK, OV = IV> { |
| ... | @@ -252,13 +253,13 @@ interface SerializeOptions<IK, IV, OK = IK, OV = IV> { | ... | @@ -252,13 +253,13 @@ interface SerializeOptions<IK, IV, OK = IK, OV = IV> { |
| 252 | } | 253 | } |
| 253 | | 254 | |
| 254 | type Order<K> = [ | 255 | type Order<K> = [ |
| 255 | /** Number of units computed at insertion time. */ | 256 | /** number of units computed at insertion time. */ |
| 256 | size: number, | 257 | size: number, |
| 257 | /** Towards most recently used */ | 258 | /** towards most recently used */ |
| 258 | before: K | none, | 259 | before: K | none, |
| 259 | /** Towards least recently used */ | 260 | /** towards least recently used */ |
| 260 | after: K | none, | 261 | after: K | none, |
| 261 | /** Locking prevents automatic eviction */ | 262 | /** locking prevents automatic eviction */ |
| 262 | locks: number, | 263 | locks: number, |
| 263 | ]; | 264 | ]; |
| 264 | | 265 | |