Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/extensions/copilot/src/util/common/cache.ts
13397 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import { IDisposable } from '../vs/base/common/lifecycle';
7
8
class Node<T> {
9
key: string;
10
value: T;
11
prev: Node<T> | null = null;
12
next: Node<T> | null = null;
13
14
constructor(key: string, value: T) {
15
this.key = key;
16
this.value = value;
17
}
18
}
19
20
export class LRUCache<T> {
21
private readonly _capacity: number;
22
private readonly _cache: Map<string, Node<T>>;
23
private readonly _head: Node<T>;
24
private readonly _tail: Node<T>;
25
26
constructor(size = 10) {
27
if (size < 1) {
28
throw new Error('Cache size must be at least 1');
29
}
30
this._capacity = size;
31
this._cache = new Map<string, Node<T>>();
32
this._head = new Node<T>('', null as any);
33
this._tail = new Node<T>('', null as any);
34
this._head.next = this._tail;
35
this._tail.prev = this._head;
36
}
37
38
private _addNode(node: Node<T>) {
39
node.prev = this._head;
40
node.next = this._head.next;
41
this._head.next!.prev = node;
42
this._head.next = node;
43
}
44
45
private _removeNode(node: Node<T>) {
46
const prev = node.prev;
47
const next = node.next;
48
prev!.next = next;
49
next!.prev = prev;
50
}
51
52
private _moveToHead(node: Node<T>) {
53
this._removeNode(node);
54
this._addNode(node);
55
}
56
57
private _popTail(): Node<T> {
58
const res = this._tail.prev!;
59
this._removeNode(res);
60
return res;
61
}
62
63
clear() {
64
this._cache.clear();
65
this._head.next = this._tail;
66
this._tail.prev = this._head;
67
}
68
69
/**
70
* Deletes the cache entry for the given key, if it exists.
71
* @param key The key of the cache entry to delete.
72
* @returns The value of the deleted cache entry, or undefined if the key was not found.
73
*/
74
deleteKey(key: string): T | undefined {
75
const node = this._cache.get(key);
76
if (!node) {
77
return undefined;
78
}
79
this._removeNode(node);
80
this._cache.delete(key);
81
return node.value;
82
}
83
84
get(key: string): T | undefined {
85
const node = this._cache.get(key);
86
if (!node) {
87
return undefined;
88
}
89
this._moveToHead(node);
90
return node.value;
91
}
92
93
/**
94
* Return a copy of all the keys stored in the LRU cache, in LRU order.
95
*
96
* The returned array is safe to modify, as this call allocates a copy of a
97
* private array used to represent those keys.
98
*/
99
keys(): string[] {
100
const keys: string[] = [];
101
let current = this._head.next;
102
while (current !== this._tail) {
103
keys.push(current!.key);
104
current = current!.next;
105
}
106
return keys;
107
}
108
109
getValues() {
110
const values: T[] = [];
111
let current = this._head.next;
112
while (current !== this._tail) {
113
values.push(current!.value);
114
current = current!.next;
115
}
116
return values;
117
}
118
119
/** @returns the evicted [key, value] */
120
put(key: string, value: T): [string, T] | undefined {
121
let node = this._cache.get(key);
122
if (node) {
123
node.value = value;
124
this._moveToHead(node);
125
} else {
126
node = new Node<T>(key, value);
127
this._cache.set(key, node);
128
this._addNode(node);
129
130
if (this._cache.size > this._capacity) {
131
const tail = this._popTail();
132
this._cache.delete(tail.key);
133
return [tail.key, tail.value] as const;
134
}
135
}
136
}
137
138
entries(): Array<[string, T]> {
139
const entries: Array<[string, T]> = [];
140
let current = this._head.next;
141
while (current !== this._tail) {
142
entries.push([current!.key, current!.value]);
143
current = current!.next;
144
}
145
return entries;
146
}
147
}
148
149
export class DisposablesLRUCache<T extends IDisposable> implements IDisposable {
150
private readonly actual: LRUCache<T>;
151
152
constructor(size?: number) {
153
this.actual = new LRUCache<T>(size);
154
}
155
156
dispose() {
157
this.clear();
158
}
159
160
clear() {
161
const values = this.actual.getValues();
162
for (const value of values) {
163
value.dispose();
164
}
165
this.actual.clear();
166
}
167
168
deleteKey(key: string): void {
169
const value = this.actual.deleteKey(key);
170
if (value) {
171
value.dispose();
172
}
173
}
174
175
get(key: string): T | undefined {
176
return this.actual.get(key);
177
}
178
179
keys(): string[] {
180
return this.actual.keys();
181
}
182
183
getValues() {
184
return this.actual.getValues();
185
}
186
187
put(key: string, value: T): void {
188
const evicted = this.actual.put(key, value);
189
if (evicted) {
190
evicted[1].dispose();
191
}
192
}
193
}
194
195