/ shared / utils / src / lru-map.ts
lru-map.ts
 1  /**
 2   * LRU Map implementation storing key/values up to a provided size limit. Beyond that
 3   * size limit, the least recently used entry is evicted.
 4   *
 5   * @see https://github.pie.apple.com/isao/lru-map
 6   */
 7  export class LruMap<K, V> extends Map<K, V> {
 8      private sizeLimit: number;
 9  
10      constructor(sizeLimit: number) {
11          super();
12          this.setSizeLimit(sizeLimit);
13          // Needed to convince TS that this is set (it's actually handled by setSizeLimit)
14          this.sizeLimit = sizeLimit;
15      }
16  
17      /**
18       * Retrieve a value from the map with a given key.
19       * @param key The key for the entry
20       * @return value The entry's value (or undefined if non existent)
21       */
22      get(key: K): V | undefined {
23          let value: V | undefined;
24  
25          if (this.has(key)) {
26              value = super.get(key);
27  
28              // Map entries are always in insertion order. So
29              // readding, pushes this entry to the top of the LRU.
30              this.delete(key);
31              super.set(key, value!);
32          }
33  
34          return value;
35      }
36  
37      set(key: K, value: V): this {
38          super.set(key, value);
39          this.prune();
40          return this;
41      }
42  
43      setSizeLimit(newSizeLimit: number): void {
44          if (newSizeLimit < 0 || !isFinite(newSizeLimit)) {
45              throw new Error(
46                  `setSizeLimit expects finite positive number, got: ${newSizeLimit}`,
47              );
48          }
49  
50          this.sizeLimit = newSizeLimit;
51          this.prune();
52      }
53  
54      private prune(): void {
55          while (this.size > this.sizeLimit) {
56              const leastRecentlyUsedKey = this.keys().next().value;
57              this.delete(leastRecentlyUsedKey);
58          }
59      }
60  }