aboutsummaryrefslogtreecommitdiff
path: root/src/cdp/record-store.ts
blob: 04b71fff9af38159dd7e8c16698f5edfaf8d03b7 (plain)
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
export type DrainPage<T> = {
  readonly records: T[];
  readonly total: number;
  readonly bufferOverflowed: boolean;
};

export type RecordStore<K, T> = {
  set(key: K, rec: T): void;
  get(key: K): T | undefined;
  delete(key: K): void;
  values(): IterableIterator<T>;
  // Takes the overflow flag as it pages, so each drain reports only the loss since the previous one.
  page(matched: T[], limit: number | undefined): DrainPage<T>;
  clear(): void;
};

const MAX_LIMIT = 500;
const DEFAULT_LIMIT = 50;

export const createRecordStore = <K, T>(capacity: number): RecordStore<K, T> => {
  const records = new Map<K, T>();
  let overflowed = false;

  const evictOldestIfFull = (): void => {
    while (records.size >= capacity) {
      const oldest = records.keys().next();
      if (oldest.done) return;
      records.delete(oldest.value);
      overflowed = true;
    }
  };

  return {
    set(key, rec) {
      evictOldestIfFull();
      records.set(key, rec);
    },
    get(key) {
      return records.get(key);
    },
    delete(key) {
      records.delete(key);
    },
    values() {
      return records.values();
    },
    page(matched, limit) {
      const bufferOverflowed = overflowed;
      overflowed = false;
      return {
        records: matched.slice(-Math.min(limit ?? DEFAULT_LIMIT, MAX_LIMIT)),
        total: matched.length,
        bufferOverflowed,
      };
    },
    clear() {
      records.clear();
      overflowed = false;
    },
  };
};