map.ts 18.6 KB
Newer Older
1 2 3 4 5 6 7
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/

'use strict';

B
wip  
Benjamin Pasero 已提交
8 9
import URI from 'vs/base/common/uri';

S
Sandeep Somavarapu 已提交
10 11
export interface Entry<K, T> {
	key: K;
12 13 14
	value: T;
}

15 16 17
export function values<K, V>(map: Map<K, V>): V[] {
	const result: V[] = [];
	map.forEach(value => result.push(value));
B
Benjamin Pasero 已提交
18

19 20
	return result;
}
S
Sandeep Somavarapu 已提交
21

22 23 24
export function keys<K, V>(map: Map<K, V>): K[] {
	const result: K[] = [];
	map.forEach((value, key) => result.push(key));
B
Benjamin Pasero 已提交
25

26 27 28
	return result;
}

29 30 31 32 33
export function getOrSet<K, V>(map: Map<K, V>, key: K, value: V): V {
	let result = map.get(key);
	if (result === void 0) {
		result = value;
		map.set(key, result);
S
Sandeep Somavarapu 已提交
34
	}
B
Benjamin Pasero 已提交
35

36
	return result;
S
Sandeep Somavarapu 已提交
37 38
}

39 40 41 42
export interface ISerializedBoundedLinkedMap<T> {
	entries: { key: string; value: T }[];
}

B
Benjamin Pasero 已提交
43
interface LinkedEntry<K, T> extends Entry<K, T> {
44 45 46 47
	next?: LinkedEntry<K, T>;
	prev?: LinkedEntry<K, T>;
}

48 49
/**
 * A simple Map<T> that optionally allows to set a limit of entries to store. Once the limit is hit,
B
Benjamin Pasero 已提交
50 51
 * the cache will remove the entry that was last recently added. Or, if a ratio is provided below 1,
 * all elements will be removed until the ratio is full filled (e.g. 0.75 to remove 25% of old elements).
52
 */
53
export class BoundedMap<T> {
B
Benjamin Pasero 已提交
54
	private map: Map<string, LinkedEntry<string, T>>;
55 56 57

	private head: LinkedEntry<string, T>;
	private tail: LinkedEntry<string, T>;
B
Benjamin Pasero 已提交
58
	private ratio: number;
59

60
	constructor(private limit = Number.MAX_VALUE, ratio = 1, value?: ISerializedBoundedLinkedMap<T>) {
B
Benjamin Pasero 已提交
61
		this.map = new Map<string, LinkedEntry<string, T>>();
B
Benjamin Pasero 已提交
62
		this.ratio = limit * ratio;
63 64 65 66 67 68 69 70 71 72 73 74 75 76

		if (value) {
			value.entries.forEach(entry => {
				this.set(entry.key, entry.value);
			});
		}
	}

	public setLimit(limit: number): void {
		if (limit < 0) {
			return; // invalid limit
		}

		this.limit = limit;
B
Benjamin Pasero 已提交
77
		while (this.map.size > this.limit) {
78 79 80 81 82 83 84
			this.trim();
		}
	}

	public serialize(): ISerializedBoundedLinkedMap<T> {
		const serialized: ISerializedBoundedLinkedMap<T> = { entries: [] };

B
Benjamin Pasero 已提交
85 86 87
		this.map.forEach(entry => {
			serialized.entries.push({ key: entry.key, value: entry.value });
		});
88 89

		return serialized;
90 91 92
	}

	public get size(): number {
B
Benjamin Pasero 已提交
93
		return this.map.size;
94 95 96
	}

	public set(key: string, value: T): boolean {
B
Benjamin Pasero 已提交
97
		if (this.map.has(key)) {
98 99 100
			return false; // already present!
		}

101
		const entry: LinkedEntry<string, T> = { key, value };
B
Benjamin Pasero 已提交
102
		this.push(entry);
103

B
Benjamin Pasero 已提交
104
		if (this.size > this.limit) {
105 106 107 108 109 110 111
			this.trim();
		}

		return true;
	}

	public get(key: string): T {
B
Benjamin Pasero 已提交
112
		const entry = this.map.get(key);
113 114 115 116

		return entry ? entry.value : null;
	}

B
Benjamin Pasero 已提交
117 118 119 120 121 122 123 124 125 126 127
	public getOrSet(k: string, t: T): T {
		const res = this.get(k);
		if (res) {
			return res;
		}

		this.set(k, t);

		return t;
	}

B
Benjamin Pasero 已提交
128
	public delete(key: string): T {
B
Benjamin Pasero 已提交
129
		const entry = this.map.get(key);
130 131

		if (entry) {
B
Benjamin Pasero 已提交
132
			this.map.delete(key);
133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151

			if (entry.next) {
				entry.next.prev = entry.prev; // [A]<-[x]<-[C] = [A]<-[C]
			} else {
				this.head = entry.prev; // [A]-[x] = [A]
			}

			if (entry.prev) {
				entry.prev.next = entry.next; // [A]->[x]->[C] = [A]->[C]
			} else {
				this.tail = entry.next; // [x]-[A] = [A]
			}

			return entry.value;
		}

		return null;
	}

B
Benjamin Pasero 已提交
152
	public has(key: string): boolean {
B
Benjamin Pasero 已提交
153
		return this.map.has(key);
B
Benjamin Pasero 已提交
154 155
	}

156
	public clear(): void {
B
Benjamin Pasero 已提交
157
		this.map.clear();
158 159 160 161
		this.head = null;
		this.tail = null;
	}

B
Benjamin Pasero 已提交
162
	private push(entry: LinkedEntry<string, T>): void {
163 164 165 166 167 168 169 170 171 172 173 174
		if (this.head) {
			// [A]-[B] = [A]-[B]->[X]
			entry.prev = this.head;
			this.head.next = entry;
		}

		if (!this.tail) {
			this.tail = entry;
		}

		this.head = entry;

B
Benjamin Pasero 已提交
175
		this.map.set(entry.key, entry);
176 177 178 179 180
	}

	private trim(): void {
		if (this.tail) {

B
Benjamin Pasero 已提交
181 182 183 184 185 186 187
			// Remove all elements until ratio is reached
			if (this.ratio < this.limit) {
				let index = 0;
				let current = this.tail;
				while (current.next) {

					// Remove the entry
B
Benjamin Pasero 已提交
188
					this.map.delete(current.key);
B
Benjamin Pasero 已提交
189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206

					// if we reached the element that overflows our ratio condition
					// make its next element the new tail of the Map and adjust the size
					if (index === this.ratio) {
						this.tail = current.next;
						this.tail.prev = null;

						break;
					}

					// Move on
					current = current.next;
					index++;
				}
			}

			// Just remove the tail element
			else {
B
Benjamin Pasero 已提交
207
				this.map.delete(this.tail.key);
B
Benjamin Pasero 已提交
208 209 210

				// [x]-[B] = [B]
				this.tail = this.tail.next;
211 212 213
				if (this.tail) {
					this.tail.prev = null;
				}
B
Benjamin Pasero 已提交
214
			}
215 216
		}
	}
B
Benjamin Pasero 已提交
217 218
}

219
export interface IKeyIterator {
220
	reset(key: string): this;
221 222 223
	next(): this;
	join(parts: string[]): string;

J
Johannes Rieken 已提交
224
	hasNext(): boolean;
225 226
	cmp(a: string): number;
	value(): string;
227 228
}

229 230 231 232
export class StringIterator implements IKeyIterator {

	private _value: string = '';
	private _pos: number = 0;
233 234 235 236 237 238

	reset(key: string): this {
		this._value = key;
		this._pos = 0;
		return this;
	}
239 240 241 242

	next(): this {
		this._pos += 1;
		return this;
243
	}
244 245 246 247 248

	join(parts: string[]): string {
		return parts.join('');
	}

J
Johannes Rieken 已提交
249
	hasNext(): boolean {
250
		return this._pos < this._value.length - 1;
251
	}
252 253 254 255 256 257 258 259 260

	cmp(a: string): number {
		let aCode = a.charCodeAt(0);
		let thisCode = this._value.charCodeAt(this._pos);
		return aCode - thisCode;
	}

	value(): string {
		return this._value[this._pos];
261 262 263
	}
}

264
export class PathIterator implements IKeyIterator {
265 266 267 268 269

	private static _fwd = '/'.charCodeAt(0);
	private static _bwd = '\\'.charCodeAt(0);

	private _value: string;
270 271
	private _from: number;
	private _to: number;
272 273

	reset(key: string): this {
274 275 276 277
		this._value = key.replace(/\\$|\/$/, '');
		this._from = 0;
		this._to = 0;
		return this.next();
278
	}
279

J
Johannes Rieken 已提交
280
	hasNext(): boolean {
281 282 283 284 285
		return this._to < this._value.length;
	}

	join(parts: string[]): string {
		return parts.join('/');
286
	}
287 288

	next(): this {
289
		// this._data = key.split(/[\\/]/).filter(s => !!s);
290 291 292 293 294 295 296 297 298 299 300 301
		this._from = this._to;
		let justSeps = true;
		for (; this._to < this._value.length; this._to++) {
			const ch = this._value.charCodeAt(this._to);
			if (ch === PathIterator._fwd || ch === PathIterator._bwd) {
				if (justSeps) {
					this._from++;
				} else {
					break;
				}
			} else {
				justSeps = false;
302 303
			}
		}
304 305
		return this;
	}
306

307
	cmp(a: string): number {
308

309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325
		let aPos = 0;
		let aLen = a.length;
		let thisPos = this._from;

		while (aPos < aLen && thisPos < this._to) {
			let cmp = a.charCodeAt(aPos) - this._value.charCodeAt(thisPos);
			if (cmp !== 0) {
				return cmp;
			}
			aPos += 1;
			thisPos += 1;
		}

		if (aLen === this._to - this._from) {
			return 0;
		} else if (aPos < aLen) {
			return -1;
326
		} else {
327
			return 1;
328 329
		}
	}
330 331 332 333

	value(): string {
		return this._value.substring(this._from, this._to);
	}
334 335 336 337 338 339 340 341
}

class TernarySearchTreeNode<E> {
	str: string;
	element: E;
	left: TernarySearchTreeNode<E>;
	mid: TernarySearchTreeNode<E>;
	right: TernarySearchTreeNode<E>;
J
Johannes Rieken 已提交
342 343 344 345

	isEmpty(): boolean {
		return !this.left && !this.mid && !this.right && !this.element;
	}
346 347 348 349
}

export class TernarySearchTree<E> {

350
	static forPaths<E>(): TernarySearchTree<E> {
351
		return new TernarySearchTree<E>(new PathIterator());
352 353 354
	}

	static forStrings<E>(): TernarySearchTree<E> {
355
		return new TernarySearchTree<E>(new StringIterator());
356 357
	}

358
	private _iter: IKeyIterator;
359 360
	private _root: TernarySearchTreeNode<E>;

361 362
	constructor(segments: IKeyIterator) {
		this._iter = segments;
363 364
	}

J
Johannes Rieken 已提交
365 366 367 368
	clear(): void {
		this._root = undefined;
	}

369
	set(key: string, element: E): E {
370 371
		let iter = this._iter.reset(key);
		let node: TernarySearchTreeNode<E>;
372

373 374 375
		if (!this._root) {
			this._root = new TernarySearchTreeNode<E>();
			this._root.str = iter.value();
376 377
		}

378 379 380 381 382 383 384 385 386 387
		node = this._root;
		while (true) {
			let val = iter.cmp(node.str);
			if (val > 0) {
				// left
				if (!node.left) {
					node.left = new TernarySearchTreeNode<E>();
					node.left.str = iter.value();
				}
				node = node.left;
388

389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408
			} else if (val < 0) {
				// right
				if (!node.right) {
					node.right = new TernarySearchTreeNode<E>();
					node.right.str = iter.value();
				}
				node = node.right;

			} else if (iter.hasNext()) {
				// mid
				iter.next();
				if (!node.mid) {
					node.mid = new TernarySearchTreeNode<E>();
					node.mid.str = iter.value();
				}
				node = node.mid;
			} else {
				break;
			}
		}
409
		const oldElement = node.element;
410
		node.element = element;
411
		return oldElement;
412 413 414
	}

	get(key: string): E {
415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431
		let iter = this._iter.reset(key);
		let node = this._root;
		while (node) {
			let val = iter.cmp(node.str);
			if (val > 0) {
				// left
				node = node.left;
			} else if (val < 0) {
				// right
				node = node.right;
			} else if (iter.hasNext()) {
				// mid
				iter.next();
				node = node.mid;
			} else {
				break;
			}
432
		}
433
		return node ? node.element : undefined;
434 435
	}

J
Johannes Rieken 已提交
436 437
	delete(key: string): void {

J
Johannes Rieken 已提交
438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460
		let iter = this._iter.reset(key);
		let stack: [-1 | 0 | 1, TernarySearchTreeNode<E>][] = [];
		let node = this._root;

		// find and unset node
		while (node) {
			let val = iter.cmp(node.str);
			if (val > 0) {
				// left
				stack.push([1, node]);
				node = node.left;
			} else if (val < 0) {
				// right
				stack.push([-1, node]);
				node = node.right;
			} else if (iter.hasNext()) {
				// mid
				iter.next();
				stack.push([0, node]);
				node = node.mid;
			} else {
				// remove element
				node.element = undefined;
J
Johannes Rieken 已提交
461

J
Johannes Rieken 已提交
462 463 464 465 466 467 468 469 470 471 472
				// clean up empty nodes
				while (stack.length > 0 && node.isEmpty()) {
					let [dir, parent] = stack.pop();
					switch (dir) {
						case 1: parent.left = undefined; break;
						case 0: parent.mid = undefined; break;
						case -1: parent.right = undefined; break;
					}
					node = parent;
				}
				break;
J
Johannes Rieken 已提交
473 474
			}
		}
J
Johannes Rieken 已提交
475 476
	}

477
	findSubstr(key: string): E {
478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496
		let iter = this._iter.reset(key);
		let node = this._root;
		let candidate: E;
		while (node) {
			let val = iter.cmp(node.str);
			if (val > 0) {
				// left
				node = node.left;
			} else if (val < 0) {
				// right
				node = node.right;
			} else if (iter.hasNext()) {
				// mid
				iter.next();
				candidate = node.element || candidate;
				node = node.mid;
			} else {
				break;
			}
497
		}
498
		return node && node.element || candidate;
499 500
	}

501
	findSuperstr(key: string): TernarySearchTree<E> {
502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523
		let iter = this._iter.reset(key);
		let node = this._root;
		while (node) {
			let val = iter.cmp(node.str);
			if (val > 0) {
				// left
				node = node.left;
			} else if (val < 0) {
				// right
				node = node.right;
			} else if (iter.hasNext()) {
				// mid
				iter.next();
				node = node.mid;
			} else {
				// collect
				if (!node.mid) {
					return undefined;
				}
				let ret = new TernarySearchTree<E>(this._iter);
				ret._root = node.mid;
				return ret;
J
Johannes Rieken 已提交
524 525
			}
		}
526
		return undefined;
J
Johannes Rieken 已提交
527 528
	}

529
	forEach(callback: (value: E, index: string) => any) {
530 531 532
		this._forEach(this._root, [], callback);
	}

533
	private _forEach(node: TernarySearchTreeNode<E>, parts: string[], callback: (value: E, index: string) => any) {
534 535 536 537 538 539 540 541 542 543 544 545 546 547 548
		if (node) {
			// left
			this._forEach(node.left, parts, callback);

			// node
			parts.push(node.str);
			if (node.element) {
				callback(node.element, this._iter.join(parts));
			}
			// mid
			this._forEach(node.mid, parts, callback);
			parts.pop();

			// right
			this._forEach(node.right, parts, callback);
549 550 551 552
		}
	}
}

B
wip  
Benjamin Pasero 已提交
553
export class ResourceMap<T> {
554 555

	protected map: Map<string, T>;
B
wip  
Benjamin Pasero 已提交
556

557
	constructor(private ignoreCase?: boolean) {
B
wip  
Benjamin Pasero 已提交
558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588
		this.map = new Map<string, T>();
	}

	public set(resource: URI, value: T): void {
		this.map.set(this.toKey(resource), value);
	}

	public get(resource: URI): T {
		return this.map.get(this.toKey(resource));
	}

	public has(resource: URI): boolean {
		return this.map.has(this.toKey(resource));
	}

	public get size(): number {
		return this.map.size;
	}

	public clear(): void {
		this.map.clear();
	}

	public delete(resource: URI): boolean {
		return this.map.delete(this.toKey(resource));
	}

	public forEach(clb: (value: T) => void): void {
		this.map.forEach(clb);
	}

B
Benjamin Pasero 已提交
589
	public values(): T[] {
590
		return values(this.map);
B
Benjamin Pasero 已提交
591 592
	}

B
wip  
Benjamin Pasero 已提交
593
	private toKey(resource: URI): string {
594
		let key = resource.toString();
595 596 597 598
		if (this.ignoreCase) {
			key = key.toLowerCase();
		}

B
wip  
Benjamin Pasero 已提交
599 600
		return key;
	}
D
Dirk Baeumer 已提交
601 602
}

603 604 605 606 607 608 609 610 611 612 613 614
export class StrictResourceMap<T> extends ResourceMap<T> {

	constructor() {
		super();
	}

	public keys(): URI[] {
		return keys(this.map).map(key => URI.parse(key));
	}

}

D
Dirk Baeumer 已提交
615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701
// We should fold BoundedMap and LinkedMap. See https://github.com/Microsoft/vscode/issues/28496

interface Item<K, V> {
	previous: Item<K, V> | undefined;
	next: Item<K, V> | undefined;
	key: K;
	value: V;
}

export namespace Touch {
	export const None: 0 = 0;
	export const First: 1 = 1;
	export const Last: 2 = 2;
}

export type Touch = 0 | 1 | 2;

export class LinkedMap<K, V> {

	private _map: Map<K, Item<K, V>>;
	private _head: Item<K, V> | undefined;
	private _tail: Item<K, V> | undefined;
	private _size: number;

	constructor() {
		this._map = new Map<K, Item<K, V>>();
		this._head = undefined;
		this._tail = undefined;
		this._size = 0;
	}

	public clear(): void {
		this._map.clear();
		this._head = undefined;
		this._tail = undefined;
		this._size = 0;
	}

	public isEmpty(): boolean {
		return !this._head && !this._tail;
	}

	public get size(): number {
		return this._size;
	}

	public has(key: K): boolean {
		return this._map.has(key);
	}

	public get(key: K): V | undefined {
		const item = this._map.get(key);
		if (!item) {
			return undefined;
		}
		return item.value;
	}

	public set(key: K, value: V, touch: Touch = Touch.None): void {
		let item = this._map.get(key);
		if (item) {
			item.value = value;
			if (touch !== Touch.None) {
				this.touch(item, touch);
			}
		} else {
			item = { key, value, next: undefined, previous: undefined };
			switch (touch) {
				case Touch.None:
					this.addItemLast(item);
					break;
				case Touch.First:
					this.addItemFirst(item);
					break;
				case Touch.Last:
					this.addItemLast(item);
					break;
				default:
					this.addItemLast(item);
					break;
			}
			this._map.set(key, item);
			this._size++;
		}
	}

	public delete(key: K): boolean {
702 703 704 705
		return !!this.remove(key);
	}

	public remove(key: K): V | undefined {
D
Dirk Baeumer 已提交
706 707
		const item = this._map.get(key);
		if (!item) {
708
			return undefined;
D
Dirk Baeumer 已提交
709 710 711 712
		}
		this._map.delete(key);
		this.removeItem(item);
		this._size--;
713
		return item.value;
D
Dirk Baeumer 已提交
714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920
	}

	public shift(): V | undefined {
		if (!this._head && !this._tail) {
			return undefined;
		}
		if (!this._head || !this._tail) {
			throw new Error('Invalid list');
		}
		const item = this._head;
		this._map.delete(item.key);
		this.removeItem(item);
		this._size--;
		return item.value;
	}

	public forEach(callbackfn: (value: V, key: K, map: LinkedMap<K, V>) => void, thisArg?: any): void {
		let current = this._head;
		while (current) {
			if (thisArg) {
				callbackfn.bind(thisArg)(current.value, current.key, this);
			} else {
				callbackfn(current.value, current.key, this);
			}
			current = current.next;
		}
	}

	public forEachReverse(callbackfn: (value: V, key: K, map: LinkedMap<K, V>) => void, thisArg?: any): void {
		let current = this._tail;
		while (current) {
			if (thisArg) {
				callbackfn.bind(thisArg)(current.value, current.key, this);
			} else {
				callbackfn(current.value, current.key, this);
			}
			current = current.previous;
		}
	}

	public values(): V[] {
		let result: V[] = [];
		let current = this._head;
		while (current) {
			result.push(current.value);
			current = current.next;
		}
		return result;
	}

	public keys(): K[] {
		let result: K[] = [];
		let current = this._head;
		while (current) {
			result.push(current.key);
			current = current.next;
		}
		return result;
	}

	/* VS Code / Monaco editor runs on es5 which has no Symbol.iterator
	public keys(): IterableIterator<K> {
		let current = this._head;
		let iterator: IterableIterator<K> = {
			[Symbol.iterator]() {
				return iterator;
			},
			next():IteratorResult<K> {
				if (current) {
					let result = { value: current.key, done: false };
					current = current.next;
					return result;
				} else {
					return { value: undefined, done: true };
				}
			}
		};
		return iterator;
	}

	public values(): IterableIterator<V> {
		let current = this._head;
		let iterator: IterableIterator<V> = {
			[Symbol.iterator]() {
				return iterator;
			},
			next():IteratorResult<V> {
				if (current) {
					let result = { value: current.value, done: false };
					current = current.next;
					return result;
				} else {
					return { value: undefined, done: true };
				}
			}
		};
		return iterator;
	}
	*/

	private addItemFirst(item: Item<K, V>): void {
		// First time Insert
		if (!this._head && !this._tail) {
			this._tail = item;
		} else if (!this._head) {
			throw new Error('Invalid list');
		} else {
			item.next = this._head;
			this._head.previous = item;
		}
		this._head = item;
	}

	private addItemLast(item: Item<K, V>): void {
		// First time Insert
		if (!this._head && !this._tail) {
			this._head = item;
		} else if (!this._tail) {
			throw new Error('Invalid list');
		} else {
			item.previous = this._tail;
			this._tail.next = item;
		}
		this._tail = item;
	}

	private removeItem(item: Item<K, V>): void {
		if (item === this._head && item === this._tail) {
			this._head = undefined;
			this._tail = undefined;
		}
		else if (item === this._head) {
			this._head = item.next;
		}
		else if (item === this._tail) {
			this._tail = item.previous;
		}
		else {
			const next = item.next;
			const previous = item.previous;
			if (!next || !previous) {
				throw new Error('Invalid list');
			}
			next.previous = previous;
			previous.next = next;
		}
	}

	private touch(item: Item<K, V>, touch: Touch): void {
		if (!this._head || !this._tail) {
			throw new Error('Invalid list');
		}
		if ((touch !== Touch.First && touch !== Touch.Last)) {
			return;
		}

		if (touch === Touch.First) {
			if (item === this._head) {
				return;
			}

			const next = item.next;
			const previous = item.previous;

			// Unlink the item
			if (item === this._tail) {
				// previous must be defined since item was not head but is tail
				// So there are more than on item in the map
				previous!.next = undefined;
				this._tail = previous;
			}
			else {
				// Both next and previous are not undefined since item was neither head nor tail.
				next!.previous = previous;
				previous!.next = next;
			}

			// Insert the node at head
			item.previous = undefined;
			item.next = this._head;
			this._head.previous = item;
			this._head = item;
		} else if (touch === Touch.Last) {
			if (item === this._tail) {
				return;
			}

			const next = item.next;
			const previous = item.previous;

			// Unlink the item.
			if (item === this._head) {
				// next must be defined since item was not tail but is head
				// So there are more than on item in the map
				next!.previous = undefined;
				this._head = next;
			} else {
				// Both next and previous are not undefined since item was neither head nor tail.
				next!.previous = previous;
				previous!.next = next;
			}
			item.next = undefined;
			item.previous = this._tail;
			this._tail.next = item;
			this._tail = item;
		}
	}
921
}