src/vs/base/common/map.ts

1016 LOC · 873 covered · 143 uncovered · 293 ranges · 20961 concepts · 118 introducers · 12741 tests

File neighbourhood

The centred file is linked to every concept that introduces one of its ranges, every test that runs code from the file, and the gray connector concepts standing between those tests and the file's own introducer concepts. Undirected links join concepts to every file where they introduce source and concepts to the tests they introduce; arrows show specialization between the displayed concepts and bridge only concepts omitted from this view. Concept colors match the source ranges below; connector concepts have no source color and are shown in gray.

Focused file, its introducer and connector concepts, their introduced files, and tests that run code from the file

In the embedded map, ordinary wheel input scrolls the page; use the visible controls to zoom and drag to pan. Open the full-screen map for canvas navigation: wheel pans, Ctrl/Command plus wheel zooms, and arrow keys pan when this region is focused. On touch screens, open the full-screen map to pan or pinch. If JavaScript or WebGL is unavailable, use the related-file, concept, and source links on this page.

Graph controls are ready.

Interactive rendering requires JavaScript and WebGL. Use the related-file, concept, and source links on this page while the interactive map is unavailable.

1 > /*--------------------------------------------------------------------------------------------- map.ts ×97
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 { URI } from './uri.js';
7 >
8 > export function getOrSet<K, V>(map: Map<K, V>, key: K, value: V): V {
9 > let result = map.get(key); map.ts ×1
10 > if (result === undefined) {
11 > result = value;
12 > map.set(key, result);
13 > }
14 >
15 > return result;
16 > }
18 > export function mapToString<K, V>(map: Map<K, V>): string {
19 const entries: string[] = [];
20 map.forEach((value, key) => {
21 entries.push(`${key} => ${value}`);
22 });
23
24 return `Map(${map.size}) {${entries.join(', ')}}`;
25 }
27 > export function setToString<K>(set: Set<K>): string {
28 const entries: K[] = [];
29 set.forEach(value => {
30 entries.push(value);
31 });
32
33 return `Set(${set.size}) {${entries.join(', ')}}`;
34 }
36 > interface ResourceMapKeyFn {
37 > (resource: URI): string;
38 > }
39 >
40 > class ResourceMapEntry<T> {
41 > constructor(readonly uri: URI, readonly value: T) { }
42 > }
43 >
44 > function isEntries<T>(arg: ResourceMap<T> | ResourceMapKeyFn | readonly (readonly [URI, T])[] | undefined): arg is readonly (readonly [URI, T])[] { map.ts ×4
45 > return Array.isArray(arg);
46 > }
48 > export class ResourceMap<T> implements Map<URI, T> {
49 >
50 > private static readonly defaultToKey = (resource: URI) => resource.toString();
51 >
52 > readonly [Symbol.toStringTag] = 'ResourceMap';
53 >
54 > private readonly map: Map<string, ResourceMapEntry<T>>;
55 > private readonly toKey: ResourceMapKeyFn;
56 >
57 > /**
58 > *
59 > * @param toKey Custom uri identity function, e.g use an existing `IExtUri#getComparison`-util
60 > */
61 > constructor(toKey?: ResourceMapKeyFn);
62 >
63 > /**
64 > *
65 > * @param other Another resource which this maps is created from
66 > * @param toKey Custom uri identity function, e.g use an existing `IExtUri#getComparison`-util
67 > */
68 > constructor(other?: ResourceMap<T>, toKey?: ResourceMapKeyFn);
69 >
70 > /**
71 > *
72 > * @param other Another resource which this maps is created from
73 > * @param toKey Custom uri identity function, e.g use an existing `IExtUri#getComparison`-util
74 > */
75 > constructor(entries?: readonly (readonly [URI, T])[], toKey?: ResourceMapKeyFn);
76 >
77 > constructor(arg?: ResourceMap<T> | ResourceMapKeyFn | readonly (readonly [URI, T])[], toKey?: ResourceMapKeyFn) {
78 > if (arg instanceof ResourceMap) { map.ts ×4
79 this.map = new Map(arg.map);
80 this.toKey = toKey ?? ResourceMap.defaultToKey;
81 > } else if (isEntries(arg)) { map.ts ×4
82 this.map = new Map();
83 this.toKey = toKey ?? ResourceMap.defaultToKey;
84
85 for (const [resource, value] of arg) {
86 this.set(resource, value);
87 }
88 > } else { map.ts ×4
89 > this.map = new Map();
90 > this.toKey = arg ?? ResourceMap.defaultToKey;
91 > }
92 > }
94 > set(resource: URI, value: T): this {
95 > this.map.set(this.toKey(resource), new ResourceMapEntry(resource, value)); map.ts ×1
96 > return this;
97 > }
99 > get(resource: URI): T | undefined {
100 > return this.map.get(this.toKey(resource))?.value; map.ts ×1
101 > }
102 > map.ts ×97
103 > has(resource: URI): boolean {
104 > return this.map.has(this.toKey(resource)); map.ts ×1
105 > }
106 > map.ts ×97
107 > get size(): number {
108 > return this.map.size; map.ts ×1
109 > }
110 > map.ts ×97
111 > clear(): void {
112 > this.map.clear(); map.ts ×1
113 > }
114 > map.ts ×97
115 > delete(resource: URI): boolean {
116 > return this.map.delete(this.toKey(resource)); map.ts ×1
117 > }
118 > map.ts ×97
119 > forEach(clb: (value: T, key: URI, map: Map<URI, T>) => void, thisArg?: object): void {
120 > if (typeof thisArg !== 'undefined') { map.ts ×3
121 clb = clb.bind(thisArg);
122 }
123 > for (const [_, entry] of this.map) { map.ts ×3
124 > clb(entry.value, entry.uri, this); map.ts ×1
125 > }
126 > } map.ts ×3
127 > map.ts ×97
128 > *values(): MapIterator<T> {
129 > for (const entry of this.map.values()) { map.ts ×2
130 > yield entry.value; map.ts ×1
131 > }
132 > } map.ts ×2
133 > map.ts ×97
134 > *keys(): MapIterator<URI> {
135 > for (const entry of this.map.values()) { map.ts ×2
136 > yield entry.uri; map.ts ×1
137 > } map.ts ×1
138 > } map.ts ×2
139 > map.ts ×97
140 > *entries(): MapIterator<[URI, T]> {
141 > for (const entry of this.map.values()) { sessionCustomizationDiscovery.ts ×16
142 > yield [entry.uri, entry.value]; sessionCustomizationDiscovery.ts ×6
143 > }
145 > map.ts ×97
146 > *[Symbol.iterator](): MapIterator<[URI, T]> {
147 > for (const [, entry] of this.map) { map.ts ×2
148 > yield [entry.uri, entry.value]; map.ts ×1
149 > }
150 > } map.ts ×2
151 > } map.ts ×97
152 >
153 > export class ResourceSet implements Set<URI> {
154 >
155 > readonly [Symbol.toStringTag]: string = 'ResourceSet';
156 >
157 > private readonly _map: ResourceMap<URI>;
158 >
159 > constructor(toKey?: ResourceMapKeyFn);
160 > constructor(entries: readonly URI[], toKey?: ResourceMapKeyFn);
161 > constructor(entriesOrKey?: readonly URI[] | ResourceMapKeyFn, toKey?: ResourceMapKeyFn) {
162 > if (!entriesOrKey || typeof entriesOrKey === 'function') { map.ts ×3
163 > this._map = new ResourceMap(entriesOrKey); map.ts ×1
164 > } else { map.ts ×3
165 > this._map = new ResourceMap(toKey); map.ts ×1
166 > entriesOrKey.forEach(this.add, this);
167 > }
168 > } map.ts ×3
169 > map.ts ×97
170 >
171 > get size(): number {
172 > return this._map.size; map.ts ×1
173 > }
174 > map.ts ×97
175 > add(value: URI): this {
176 > this._map.set(value, value); map.ts ×1
177 > return this;
178 > }
179 > map.ts ×97
180 > clear(): void {
181 > this._map.clear(); map.ts ×1
182 > }
183 > map.ts ×97
184 > delete(value: URI): boolean {
185 return this._map.delete(value);
186 }
187 > map.ts ×97
188 > forEach(callbackfn: (value: URI, value2: URI, set: Set<URI>) => void, thisArg?: unknown): void {
189 this._map.forEach((_value, key) => callbackfn.call(thisArg, key, key, this));
190 }
191 > map.ts ×97
192 > has(value: URI): boolean {
193 > return this._map.has(value); map.ts ×1
194 > }
195 > map.ts ×97
196 > entries(): SetIterator<[URI, URI]> {
197 return this._map.entries() as unknown as SetIterator<[URI, URI]>;
198 }
199 > map.ts ×97
200 > keys(): SetIterator<URI> {
201 > return this._map.keys() as unknown as SetIterator<URI>; map.ts ×2
202 > }
203 > map.ts ×97
204 > values(): SetIterator<URI> {
205 return this._map.keys() as unknown as SetIterator<URI>;
206 }
207 > map.ts ×97
208 > [Symbol.iterator](): SetIterator<URI> {
209 > return this.keys(); map.ts ×2
210 > }
211 > } map.ts ×97
212 >
213 >
214 > interface Item<K, V> {
215 > previous: Item<K, V> | undefined;
216 > next: Item<K, V> | undefined;
217 > key: K;
218 > value: V;
219 > }
220 >
221 > export const enum Touch {
222 > None = 0,
223 > AsOld = 1,
224 > AsNew = 2
225 > }
226 >
227 > export class LinkedMap<K, V> implements Map<K, V> {
228 >
229 > readonly [Symbol.toStringTag] = 'LinkedMap';
230 >
231 > private _map: Map<K, Item<K, V>>;
232 > private _head: Item<K, V> | undefined;
233 > private _tail: Item<K, V> | undefined;
234 > private _size: number;
235 >
236 > private _state: number;
237 >
238 > constructor() {
239 > this._map = new Map<K, Item<K, V>>(); map.ts ×1
240 > this._head = undefined;
241 > this._tail = undefined;
242 > this._size = 0;
243 > this._state = 0;
244 > }
245 > map.ts ×97
246 > clear(): void {
247 > this._map.clear(); map.ts ×1
248 > this._head = undefined;
249 > this._tail = undefined;
250 > this._size = 0;
251 > this._state++;
252 > }
253 > map.ts ×97
254 > isEmpty(): boolean {
255 return !this._head && !this._tail;
256 }
257 > map.ts ×97
258 > get size(): number {
259 > return this._size; map.ts ×1
260 > }
261 > map.ts ×97
262 > get first(): V | undefined {
263 > return this._head?.value; map.ts ×2
264 > }
265 > map.ts ×97
266 > get last(): V | undefined {
267 > return this._tail?.value; map.ts ×2
268 > }
269 > map.ts ×97
270 > has(key: K): boolean {
271 > return this._map.has(key); map.ts ×1
272 > }
273 > map.ts ×97
274 > get(key: K, touch: Touch = Touch.None): V | undefined {
275 > const item = this._map.get(key); map.ts ×2
276 > if (!item) {
277 > return undefined; map.ts ×1
278 > }
279 > if (touch !== Touch.None) { map.ts ×2
280 > this.touch(item, touch); map.ts ×1
281 > }
282 > return item.value; map.ts ×2
283 > } map.ts ×2
284 > map.ts ×97
285 > set(key: K, value: V, touch: Touch = Touch.None): this {
286 > let item = this._map.get(key); map.ts ×8
287 > if (item) {
288 > item.value = value; map.ts ×1
289 > if (touch !== Touch.None) {
290 > this.touch(item, touch);
291 > }
292 > } else { map.ts ×8
293 > item = { key, value, next: undefined, previous: undefined };
294 > switch (touch) {
295 > case Touch.None:
296 > this.addItemLast(item); map.ts ×1
297 > break;
298 > case Touch.AsOld: map.ts ×8
299 this.addItemFirst(item);
300 break;
301 > case Touch.AsNew: map.ts ×8
302 > this.addItemLast(item); map.ts ×1
303 > break;
304 > default: map.ts ×8
305 this.addItemLast(item);
306 break;
307 > } map.ts ×8
308 > this._map.set(key, item);
309 > this._size++;
310 > }
311 > return this;
312 > }
313 > map.ts ×97
314 > delete(key: K): boolean {
315 > return !!this.remove(key); map.ts ×5
316 > }
317 > map.ts ×97
318 > remove(key: K): V | undefined {
319 > const item = this._map.get(key); map.ts ×5
320 > if (!item) {
321 > return undefined; map.ts ×1
322 > }
323 > this._map.delete(key); map.ts ×5
324 > this.removeItem(item);
325 > this._size--;
326 > return item.value;
327 > }
328 > map.ts ×97
329 > shift(): V | undefined {
330 if (!this._head && !this._tail) {
331 return undefined;
332 }
333 if (!this._head || !this._tail) {
334 throw new Error('Invalid list');
335 }
336 const item = this._head;
337 this._map.delete(item.key);
338 this.removeItem(item);
339 this._size--;
340 return item.value;
341 }
342 > map.ts ×97
343 > forEach(callbackfn: (value: V, key: K, map: Map<K, V>) => void, thisArg?: unknown): void {
344 > const state = this._state; map.ts ×3
345 > let current = this._head;
346 > while (current) {
347 > if (thisArg) {
348 callbackfn.bind(thisArg)(current.value, current.key, this);
349 > } else { map.ts ×3
350 > callbackfn(current.value, current.key, this);
351 > }
352 > if (this._state !== state) {
353 throw new Error(`LinkedMap got modified during iteration.`);
354 }
355 > current = current.next; map.ts ×3
356 > }
357 > }
358 > map.ts ×97
359 > keys(): MapIterator<K> {
360 > const map = this; map.ts ×3
361 > const state = this._state;
362 > let current = this._head;
363 > const iterator: MapIterator<K> = {
364 > [Symbol.iterator]() {
365 > return iterator;
366 > },
367 > [Symbol.dispose]() { /* no-op */ },
368 > next(): IteratorResult<K> {
369 > if (map._state !== state) {
370 > throw new Error(`LinkedMap got modified during iteration.`); map.ts ×3
371 > }
372 > if (current) { map.ts ×3
373 > const result = { value: current.key, done: false }; map.ts ×1
374 > current = current.next;
375 > return result;
376 > } else { map.ts ×3
377 > return { value: undefined, done: true };
378 > }
379 > }
380 > };
381 > return iterator;
382 > }
383 > map.ts ×97
384 > values(): MapIterator<V> {
385 > const map = this; map.ts ×2
386 > const state = this._state;
387 > let current = this._head;
388 > const iterator: MapIterator<V> = {
389 > [Symbol.iterator]() {
390 > return iterator;
391 > },
392 > [Symbol.dispose]() { /* no-op */ },
393 > next(): IteratorResult<V> {
394 > if (map._state !== state) {
395 > throw new Error(`LinkedMap got modified during iteration.`); map.ts ×3
396 > }
397 > if (current) { map.ts ×2
398 > const result = { value: current.value, done: false };
399 > current = current.next;
400 > return result;
401 > } else {
402 > return { value: undefined, done: true };
403 > }
404 > }
405 > };
406 > return iterator;
407 > }
408 > map.ts ×97
409 > entries(): MapIterator<[K, V]> {
410 > const map = this; map.ts ×2
411 > const state = this._state;
412 > let current = this._head;
413 > const iterator: MapIterator<[K, V]> = {
414 > [Symbol.iterator]() {
415 > return iterator;
416 > },
417 > [Symbol.dispose]() { /* no-op */ },
418 > next(): IteratorResult<[K, V]> {
419 > if (map._state !== state) {
420 > throw new Error(`LinkedMap got modified during iteration.`); map.ts ×3
421 > }
422 > if (current) { map.ts ×2
423 > const result: IteratorResult<[K, V]> = { value: [current.key, current.value], done: false };
424 > current = current.next;
425 > return result;
426 > } else {
427 > return { value: undefined, done: true };
428 > }
429 > }
430 > };
431 > return iterator;
432 > }
433 > map.ts ×97
434 > [Symbol.iterator](): MapIterator<[K, V]> {
435 return this.entries();
436 }
437 > map.ts ×97
438 > protected trimOld(newSize: number) {
439 > if (newSize >= this.size) { map.ts ×5
440 return;
441 }
442 > if (newSize === 0) { map.ts ×5
443 this.clear();
444 return;
445 }
446 > let current = this._head; map.ts ×5
447 > let currentSize = this.size;
448 > while (current && currentSize > newSize) {
449 > this._map.delete(current.key);
450 > current = current.next;
451 > currentSize--;
452 > }
453 > this._head = current;
454 > this._size = currentSize;
455 > if (current) {
456 > current.previous = undefined;
457 > }
458 > this._state++;
459 > }
460 > map.ts ×97
461 > protected trimNew(newSize: number) {
462 > if (newSize >= this.size) { map.ts ×5
463 return;
464 }
465 > if (newSize === 0) { map.ts ×5
466 this.clear();
467 return;
468 }
469 > let current = this._tail; map.ts ×5
470 > let currentSize = this.size;
471 > while (current && currentSize > newSize) {
472 > this._map.delete(current.key);
473 > current = current.previous;
474 > currentSize--;
475 > }
476 > this._tail = current;
477 > this._size = currentSize;
478 > if (current) {
479 > current.next = undefined;
480 > }
481 > this._state++;
482 > }
483 > map.ts ×97
484 > private addItemFirst(item: Item<K, V>): void {
485 // First time Insert
486 if (!this._head && !this._tail) {
487 this._tail = item;
488 } else if (!this._head) {
489 throw new Error('Invalid list');
490 } else {
491 item.next = this._head;
492 this._head.previous = item;
493 }
494 this._head = item;
495 this._state++;
496 }
497 > map.ts ×97
498 > private addItemLast(item: Item<K, V>): void {
499 > // First time Insert map.ts ×8
500 > if (!this._head && !this._tail) {
501 > this._head = item;
502 > } else if (!this._tail) {
503 throw new Error('Invalid list');
504 > } else { map.ts ×1
505 > item.previous = this._tail;
506 > this._tail.next = item;
507 > }
508 > this._tail = item; map.ts ×8
509 > this._state++;
510 > }
511 > map.ts ×97
512 > private removeItem(item: Item<K, V>): void {
513 > if (item === this._head && item === this._tail) { map.ts ×5
514 > this._head = undefined; map.ts ×1
515 > this._tail = undefined;
516 > }
517 > else if (item === this._head) { map.ts ×1
518 > // This can only happen if size === 1 which is handled map.ts ×2
519 > // by the case above.
520 > if (!item.next) {
521 throw new Error('Invalid list');
522 }
523 > item.next.previous = undefined; map.ts ×2
524 > this._head = item.next;
525 > }
526 > else if (item === this._tail) { map.ts ×2
527 > // This can only happen if size === 1 which is handled
528 > // by the case above.
529 > if (!item.previous) {
530 throw new Error('Invalid list');
531 }
532 > item.previous.next = undefined; map.ts ×2
533 > this._tail = item.previous;
534 > }
535 else {
536 const next = item.next;
537 const previous = item.previous;
538 if (!next || !previous) {
539 throw new Error('Invalid list');
540 }
541 next.previous = previous;
542 previous.next = next;
543 }
544 > item.next = undefined; map.ts ×5
545 > item.previous = undefined;
546 > this._state++;
547 > }
548 > map.ts ×97
549 > private touch(item: Item<K, V>, touch: Touch): void {
550 > if (!this._head || !this._tail) { map.ts ×4
551 throw new Error('Invalid list');
552 }
553 > if ((touch !== Touch.AsOld && touch !== Touch.AsNew)) { map.ts ×4
554 return;
555 }
556 > map.ts ×4
557 > if (touch === Touch.AsOld) {
558 > if (item === this._head) { map.ts ×1
559 > return; map.ts ×1
560 > }
561 > map.ts ×2
562 > const next = item.next;
563 > const previous = item.previous;
564 >
565 > // Unlink the item
566 > if (item === this._tail) {
567 > // previous must be defined since item was not head but is tail map.ts ×1
568 > // So there are more than on item in the map
569 > previous!.next = undefined;
570 > this._tail = previous;
571 > }
572 > else { map.ts ×1
573 > // Both next and previous are not undefined since item was neither head nor tail.
574 > next!.previous = previous;
575 > previous!.next = next;
576 > }
577 > map.ts ×2
578 > // Insert the node at head
579 > item.previous = undefined;
580 > item.next = this._head;
581 > this._head.previous = item;
582 > this._head = item;
583 > this._state++;
584 > } else if (touch === Touch.AsNew) { map.ts ×1
585 > if (item === this._tail) { map.ts ×1
586 > return; map.ts ×1
587 > }
588 > map.ts ×3
589 > const next = item.next;
590 > const previous = item.previous;
591 >
592 > // Unlink the item.
593 > if (item === this._head) {
594 > // next must be defined since item was not tail but is head map.ts ×1
595 > // So there are more than on item in the map
596 > next!.previous = undefined;
597 > this._head = next;
598 > } else { map.ts ×3
599 > // Both next and previous are not undefined since item was neither head nor tail. map.ts ×1
600 > next!.previous = previous;
601 > previous!.next = next;
602 > }
603 > item.next = undefined; map.ts ×3
604 > item.previous = this._tail;
605 > this._tail.next = item;
606 > this._tail = item;
607 > this._state++;
608 > }
609 > } map.ts ×4
610 > map.ts ×97
611 > toJSON(): [K, V][] {
612 > const data: [K, V][] = []; map.ts ×2
613 >
614 > this.forEach((value, key) => {
615 > data.push([key, value]);
616 > });
617 >
618 > return data;
619 > }
620 > map.ts ×97
621 > fromJSON(data: [K, V][]): void {
622 > this.clear(); map.ts ×2
623 >
624 > for (const [key, value] of data) {
625 > this.set(key, value);
626 > }
627 > }
628 > } map.ts ×97
629 >
630 > abstract class Cache<K, V> extends LinkedMap<K, V> {
631 >
632 > protected _limit: number;
633 > protected _ratio: number;
634 >
635 > constructor(limit: number, ratio: number = 1) {
636 > super(); map.ts ×1
637 > this._limit = limit;
638 > this._ratio = Math.min(Math.max(0, ratio), 1);
639 > }
640 > map.ts ×97
641 > get limit(): number {
642 return this._limit;
643 }
644 > map.ts ×97
645 > set limit(limit: number) {
646 > this._limit = limit; map.ts ×1
647 > this.checkTrim();
648 > }
649 > map.ts ×97
650 > get ratio(): number {
651 return this._ratio;
652 }
653 > map.ts ×97
654 > set ratio(ratio: number) {
655 this._ratio = Math.min(Math.max(0, ratio), 1);
656 this.checkTrim();
657 }
658 > map.ts ×97
659 > override get(key: K, touch: Touch = Touch.AsNew): V | undefined {
660 > return super.get(key, touch); map.ts ×1
661 > }
662 > map.ts ×97
663 > peek(key: K): V | undefined {
664 > return super.get(key, Touch.None); map.ts ×1
665 > }
666 > map.ts ×97
667 > override set(key: K, value: V): this {
668 > super.set(key, value, Touch.AsNew); map.ts ×1
669 > return this;
670 > }
671 > map.ts ×97
672 > protected checkTrim() {
673 > if (this.size > this._limit) { map.ts ×3
674 > this.trim(Math.round(this._limit * this._ratio)); map.ts ×5
675 > }
676 > } map.ts ×3
677 > map.ts ×97
678 > protected abstract trim(newSize: number): void;
679 > }
680 >
681 > export class LRUCache<K, V> extends Cache<K, V> {
682 >
683 > constructor(limit: number, ratio: number = 1) {
684 > super(limit, ratio); map.ts ×1
685 > }
686 > map.ts ×97
687 > protected override trim(newSize: number) {
688 > this.trimOld(newSize); map.ts ×5
689 > }
690 > map.ts ×97
691 > override set(key: K, value: V): this {
692 > super.set(key, value); map.ts ×3
693 > this.checkTrim();
694 > return this;
695 > }
696 > } map.ts ×97
697 >
698 > export class MRUCache<K, V> extends Cache<K, V> {
699 >
700 > constructor(limit: number, ratio: number = 1) {
701 > super(limit, ratio); map.ts ×3
702 > }
703 > map.ts ×97
704 > protected override trim(newSize: number) {
705 > this.trimNew(newSize); map.ts ×5
706 > }
707 > map.ts ×97
708 > override set(key: K, value: V): this {
709 > if (this._limit <= this.size && !this.has(key)) { map.ts ×3
710 > this.trim(Math.round(this._limit * this._ratio) - 1); map.ts ×5
711 > }
712 > map.ts ×3
713 > super.set(key, value);
714 > return this;
715 > }
716 > } map.ts ×97
717 >
718 > export class CounterSet<T> {
719
720 private map = new Map<T, number>();
721 > map.ts ×97
722 > add(value: T): CounterSet<T> {
723 this.map.set(value, (this.map.get(value) || 0) + 1);
724 return this;
725 }
726 > map.ts ×97
727 > delete(value: T): boolean {
728 let counter = this.map.get(value) || 0;
729
730 if (counter === 0) {
731 return false;
732 }
733
734 counter--;
735
736 if (counter === 0) {
737 this.map.delete(value);
738 } else {
739 this.map.set(value, counter);
740 }
741
742 return true;
743 }
744 > map.ts ×97
745 > has(value: T): boolean {
746 return this.map.has(value);
747 }
748 > } map.ts ×97
749 >
750 > /**
751 > * A map that allows access both by keys and values.
752 > * **NOTE**: values need to be unique.
753 > */
754 > export class BidirectionalMap<K, V> {
755 >
756 > private readonly _m1 = new Map<K, V>();
757 > private readonly _m2 = new Map<V, K>();
758 >
759 > constructor(entries?: readonly (readonly [K, V])[]) {
760 > if (entries) { map.ts ×3
761 for (const [key, value] of entries) {
762 this.set(key, value);
763 }
764 }
765 > } map.ts ×3
766 > map.ts ×97
767 > clear(): void {
768 > this._m1.clear(); map.ts ×1
769 > this._m2.clear();
770 > }
771 > map.ts ×97
772 > set(key: K, value: V): void {
773 > this._m1.set(key, value); map.ts ×3
774 > this._m2.set(value, key);
775 > }
776 > map.ts ×97
777 > get(key: K): V | undefined {
778 > return this._m1.get(key); map.ts ×1
779 > }
780 > map.ts ×97
781 > getKey(value: V): K | undefined {
782 > return this._m2.get(value); map.ts ×1
783 > }
784 > map.ts ×97
785 > delete(key: K): boolean {
786 > const value = this._m1.get(key); map.ts ×2
787 > if (value === undefined) {
788 > return false; map.ts ×1
789 > }
790 > this._m1.delete(key); map.ts ×1
791 > this._m2.delete(value);
792 > return true;
793 > } map.ts ×2
794 > map.ts ×97
795 > forEach(callbackfn: (value: V, key: K, map: BidirectionalMap<K, V>) => void, thisArg?: unknown): void {
796 > this._m1.forEach((value, key) => { map.ts ×1
797 > callbackfn.call(thisArg, value, key, this);
798 > });
799 > }
800 > map.ts ×97
801 > keys(): IterableIterator<K> {
802 return this._m1.keys();
803 }
804 > map.ts ×97
805 > values(): IterableIterator<V> {
806 return this._m1.values();
807 }
808 > } map.ts ×97
809 >
810 > export class SetMap<K, V> {
811 > map.ts ×1
812 > private map = new Map<K, Set<V>>();
813 > map.ts ×97
814 > add(key: K, value: V): void {
815 > let values = this.map.get(key); map.ts ×1
816 >
817 > if (!values) {
818 > values = new Set<V>();
819 > this.map.set(key, values);
820 > }
821 >
822 > values.add(value);
823 > }
824 > map.ts ×97
825 > delete(key: K, value: V): void {
826 > const values = this.map.get(key); map.ts ×2
827 >
828 > if (!values) {
829 return;
830 }
831 > map.ts ×2
832 > values.delete(value);
833 >
834 > if (values.size === 0) {
835 > this.map.delete(key);
836 > }
837 > }
838 > map.ts ×97
839 > forEach(key: K, fn: (value: V) => void): void {
840 > const values = this.map.get(key); map.ts ×2
841 >
842 > if (!values) {
843 > return; computeMovedLines.ts ×2
844 > }
845 > map.ts ×1
846 > values.forEach(fn);
847 > } map.ts ×2
848 > map.ts ×97
849 > get(key: K): ReadonlySet<V> {
850 > const values = this.map.get(key); map.ts ×2
851 > if (!values) {
852 > return new Set<V>(); map.ts ×1
853 > }
854 > return values; map.ts ×1
855 > } map.ts ×2
856 > } map.ts ×97
857 >
858 > export function mapsStrictEqualIgnoreOrder(a: Map<unknown, unknown>, b: Map<unknown, unknown>): boolean {
859 > if (a === b) { map.ts ×4
860 return true;
861 }
862 > map.ts ×4
863 > if (a.size !== b.size) {
864 > return false;
865 > }
866 >
867 > for (const [key, value] of a) {
868 > if (!b.has(key) || b.get(key) !== value) {
869 return false;
870 }
871 > } map.ts ×4
872 >
873 > for (const [key] of b) {
874 > if (!a.has(key)) {
875 return false;
876 }
877 > } map.ts ×4
878 >
879 > return true;
880 > }
881 > map.ts ×97
882 > /**
883 > * A map that is addressable with an arbitrary number of keys. This is useful in high performance
884 > * scenarios where creating a composite key whenever the data is accessed is too expensive. For
885 > * example for a very hot function, constructing a string like `first-second-third` for every call
886 > * will cause a significant hit to performance.
887 > */
888 > export class NKeyMap<TValue, TKeys extends (string | boolean | number)[]> {
889 > private _data: Map<any, any> = new Map(); map.ts ×1
890 > map.ts ×97
891 > /**
892 > * Sets a value on the map. Note that unlike a standard `Map`, the first argument is the value.
893 > * This is because the spread operator is used for the keys and must be last..
894 > * @param value The value to set.
895 > * @param keys The keys for the value.
896 > */
897 > public set(value: TValue, ...keys: [...TKeys]): void {
898 > let currentMap = this._data; map.ts ×1
899 > for (let i = 0; i < keys.length - 1; i++) {
900 > let nextMap = currentMap.get(keys[i]);
901 > if (nextMap === undefined) {
902 > nextMap = new Map();
903 > currentMap.set(keys[i], nextMap);
904 > }
905 > currentMap = nextMap;
906 > }
907 > currentMap.set(keys[keys.length - 1], value);
908 > }
909 > map.ts ×97
910 > public get(...keys: [...TKeys]): TValue | undefined {
911 > let currentMap = this._data; map.ts ×2
912 > for (let i = 0; i < keys.length - 1; i++) {
913 > const nextMap = currentMap.get(keys[i]);
914 > if (nextMap === undefined) {
915 > return undefined; map.ts ×1
916 > }
917 > currentMap = nextMap; map.ts ×1
918 > }
919 > return currentMap.get(keys[keys.length - 1]);
920 > } map.ts ×2
921 > map.ts ×97
922 > public delete(...keys: [...TKeys]): boolean {
923 > const maps: Map<any, any>[] = [this._data]; map.ts ×3
924 > let currentMap = this._data;
925 > for (let i = 0; i < keys.length - 1; i++) {
926 > const nextMap = currentMap.get(keys[i]);
927 > if (nextMap === undefined) {
928 > return false; map.ts ×1
929 > }
930 > currentMap = nextMap; map.ts ×3
931 > maps.push(currentMap);
932 > }
933 > const deleted = currentMap.delete(keys[keys.length - 1]);
934 > for (let i = keys.length - 2; deleted && i >= 0; i--) { map.ts ×3
935 > if (maps[i + 1].size === 0) { map.ts ×3
936 > maps[i].delete(keys[i]); map.ts ×1
937 > }
938 > } map.ts ×3
939 > return deleted;
940 > } map.ts ×3
941 > map.ts ×97
942 > public deleteAll(...keys: Partial<TKeys>): boolean {
943 > if (keys.length === 0) { map.ts ×4
944 > const hadData = this._data.size > 0; map.ts ×1
945 > this._data.clear();
946 > return hadData;
947 > }
948 > const maps: Map<any, any>[] = [this._data]; map.ts ×4
949 > let currentMap = this._data;
950 > for (let i = 0; i < keys.length - 1; i++) {
951 > const nextMap = currentMap.get(keys[i]); map.ts ×4
952 > if (nextMap === undefined) {
953 return false;
954 }
955 > currentMap = nextMap; map.ts ×4
956 > maps.push(currentMap);
957 > }
958 > const deleted = currentMap.delete(keys[keys.length - 1]); map.ts ×4
959 > for (let i = keys.length - 2; deleted && i >= 0; i--) {
960 > if (maps[i + 1].size === 0) { map.ts ×4
961 > maps[i].delete(keys[i]); map.ts ×1
962 > }
963 > } map.ts ×4
964 > return deleted; map.ts ×4
965 > }
966 > map.ts ×97
967 > public clear(): void {
968 > this._data.clear(); map.ts ×1
969 > }
970 > map.ts ×97
971 > public *getAll(...keys: Partial<TKeys>): IterableIterator<TValue> {
972 > let currentMap = this._data; map.ts ×2
973 > for (const key of keys) {
974 > const nextMap = currentMap.get(key);
975 > if (nextMap === undefined) {
976 > return; map.ts ×1
977 > }
978 > currentMap = nextMap; map.ts ×1
979 > }
980 > yield* this._values(currentMap);
981 > } map.ts ×2
982 > map.ts ×97
983 > public *values(): IterableIterator<TValue> {
984 > yield* this._values(this._data); map.ts ×1
985 > }
986 > map.ts ×97
987 > private *_values(map: Map<any, any>): IterableIterator<TValue> {
988 > for (const value of map.values()) { map.ts ×2
989 > if (value instanceof Map) { map.ts ×3
990 > yield* this._values(value); map.ts ×1
991 > } else { map.ts ×3
992 > yield value;
993 > } map.ts ×1
994 > } map.ts ×3
995 > } map.ts ×2
996 > map.ts ×97
997 > /**
998 > * Get a textual representation of the map for debugging purposes.
999 > */
1000 > public toString(): string {
1001 > const printMap = (map: Map<any, any>, depth: number): string => { map.ts ×1
1002 > let result = '';
1003 > for (const [key, value] of map) {
1004 > result += `${' '.repeat(depth)}${key}: `;
1005 > if (value instanceof Map) {
1006 > result += '\n' + printMap(value, depth + 1);
1007 > } else {
1008 > result += `${value}\n`;
1009 > }
1010 > }
1011 > return result;
1012 > };
1013 >
1014 > return printMap(this._data, 0);
1015 > }
1016 > } map.ts ×97