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;
},
};
};
|