Path: blob/main/extensions/copilot/src/util/common/cache.ts
13397 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import { IDisposable } from '../vs/base/common/lifecycle';67class Node<T> {8key: string;9value: T;10prev: Node<T> | null = null;11next: Node<T> | null = null;1213constructor(key: string, value: T) {14this.key = key;15this.value = value;16}17}1819export class LRUCache<T> {20private readonly _capacity: number;21private readonly _cache: Map<string, Node<T>>;22private readonly _head: Node<T>;23private readonly _tail: Node<T>;2425constructor(size = 10) {26if (size < 1) {27throw new Error('Cache size must be at least 1');28}29this._capacity = size;30this._cache = new Map<string, Node<T>>();31this._head = new Node<T>('', null as any);32this._tail = new Node<T>('', null as any);33this._head.next = this._tail;34this._tail.prev = this._head;35}3637private _addNode(node: Node<T>) {38node.prev = this._head;39node.next = this._head.next;40this._head.next!.prev = node;41this._head.next = node;42}4344private _removeNode(node: Node<T>) {45const prev = node.prev;46const next = node.next;47prev!.next = next;48next!.prev = prev;49}5051private _moveToHead(node: Node<T>) {52this._removeNode(node);53this._addNode(node);54}5556private _popTail(): Node<T> {57const res = this._tail.prev!;58this._removeNode(res);59return res;60}6162clear() {63this._cache.clear();64this._head.next = this._tail;65this._tail.prev = this._head;66}6768/**69* Deletes the cache entry for the given key, if it exists.70* @param key The key of the cache entry to delete.71* @returns The value of the deleted cache entry, or undefined if the key was not found.72*/73deleteKey(key: string): T | undefined {74const node = this._cache.get(key);75if (!node) {76return undefined;77}78this._removeNode(node);79this._cache.delete(key);80return node.value;81}8283get(key: string): T | undefined {84const node = this._cache.get(key);85if (!node) {86return undefined;87}88this._moveToHead(node);89return node.value;90}9192/**93* Return a copy of all the keys stored in the LRU cache, in LRU order.94*95* The returned array is safe to modify, as this call allocates a copy of a96* private array used to represent those keys.97*/98keys(): string[] {99const keys: string[] = [];100let current = this._head.next;101while (current !== this._tail) {102keys.push(current!.key);103current = current!.next;104}105return keys;106}107108getValues() {109const values: T[] = [];110let current = this._head.next;111while (current !== this._tail) {112values.push(current!.value);113current = current!.next;114}115return values;116}117118/** @returns the evicted [key, value] */119put(key: string, value: T): [string, T] | undefined {120let node = this._cache.get(key);121if (node) {122node.value = value;123this._moveToHead(node);124} else {125node = new Node<T>(key, value);126this._cache.set(key, node);127this._addNode(node);128129if (this._cache.size > this._capacity) {130const tail = this._popTail();131this._cache.delete(tail.key);132return [tail.key, tail.value] as const;133}134}135}136137entries(): Array<[string, T]> {138const entries: Array<[string, T]> = [];139let current = this._head.next;140while (current !== this._tail) {141entries.push([current!.key, current!.value]);142current = current!.next;143}144return entries;145}146}147148export class DisposablesLRUCache<T extends IDisposable> implements IDisposable {149private readonly actual: LRUCache<T>;150151constructor(size?: number) {152this.actual = new LRUCache<T>(size);153}154155dispose() {156this.clear();157}158159clear() {160const values = this.actual.getValues();161for (const value of values) {162value.dispose();163}164this.actual.clear();165}166167deleteKey(key: string): void {168const value = this.actual.deleteKey(key);169if (value) {170value.dispose();171}172}173174get(key: string): T | undefined {175return this.actual.get(key);176}177178keys(): string[] {179return this.actual.keys();180}181182getValues() {183return this.actual.getValues();184}185186put(key: string, value: T): void {187const evicted = this.actual.put(key, value);188if (evicted) {189evicted[1].dispose();190}191}192}193194195