All files / qr-code/src cache.ts

100% Statements 19/19
94.73% Branches 18/19
100% Functions 4/4
100% Lines 18/18

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67                                3x 3x       168x 168x                                 168x 168x 168x   32x 32x 32x     136x 136x 136x 2x 2x   126x         5x         55x    
import type { EncodeOptions, QRMatrix } from "./types";
import { encodeQR } from "./encode/encodeQR";
 
/**
 * A small LRU in front of the encoder.
 *
 * Encoding is fast, but a React tree re-renders for reasons that have nothing
 * to do with the QR code, and a component that re-encodes on every parent
 * render allocates a fresh matrix each time for no benefit. Keying on the
 * encode inputs makes repeat renders of the same value free.
 *
 * Only *encoding* is cached. Styling is not: it changes far more often (theme
 * toggles, hover states) and is cheap to redo, so caching it would evict
 * useful entries to store nothing of value.
 */
 
const CAPACITY = 32;
const cache = new Map<string, QRMatrix>();
 
function cacheKey(data: string | Uint8Array, options: EncodeOptions): string {
  const payload =
    typeof data === "string" ? `s${data}` : `b${Array.prototype.join.call(data, ",")}`;
  return [
    options.level ?? "M",
    options.version ?? "",
    options.minVersion ?? "",
    options.mask ?? "",
    options.eci === false ? "0" : "1",
    payload,
  ].join("");
}
 
/**
 * {@link encodeQR}, memoized on its inputs.
 *
 * Failures are never cached: a `QRCapacityError` is cheap to reproduce, and
 * storing one would waste a slot that a working code could use.
 */
export function encodeCached(data: string | Uint8Array, options: EncodeOptions = {}): QRMatrix {
  const key = cacheKey(data, options);
  const hit = cache.get(key);
  if (hit) {
    // Refresh recency: delete + set moves the entry to the end of the Map.
    cache.delete(key);
    cache.set(key, hit);
    return hit;
  }
 
  const matrix = encodeQR(data, options);
  cache.set(key, matrix);
  if (cache.size > CAPACITY) {
    const oldest = cache.keys().next();
    Eif (!oldest.done) cache.delete(oldest.value);
  }
  return matrix;
}
 
/** Number of cached symbols. Testing seam. */
export function cacheSize(): number {
  return cache.size;
}
 
/** Empty the cache. Testing seam. */
export function clearCache(): void {
  cache.clear();
}