--- /dev/null
+/**
+ * Copyright (c) 2017 The xterm.js authors. All rights reserved.
+ * @license MIT
+ */
+
+interface ILinkedListNode<T> {
+ prev: ILinkedListNode<T> | null;
+ next: ILinkedListNode<T> | null;
+ key: number | null;
+ value: T | null;
+}
+
+export class LRUMap<T> {
+ private _map: { [key: number]: ILinkedListNode<T> } = {};
+ private _head: ILinkedListNode<T> | null = null;
+ private _tail: ILinkedListNode<T> | null = null;
+ private _nodePool: ILinkedListNode<T>[] = [];
+ public size: number = 0;
+
+ constructor(public capacity: number) { }
+
+ private _unlinkNode(node: ILinkedListNode<T>): void {
+ const prev = node.prev;
+ const next = node.next;
+ if (node === this._head) {
+ this._head = next;
+ }
+ if (node === this._tail) {
+ this._tail = prev;
+ }
+ if (prev !== null) {
+ prev.next = next;
+ }
+ if (next !== null) {
+ next.prev = prev;
+ }
+ }
+
+ private _appendNode(node: ILinkedListNode<T>): void {
+ const tail = this._tail;
+ if (tail !== null) {
+ tail.next = node;
+ }
+ node.prev = tail;
+ node.next = null;
+ this._tail = node;
+ if (this._head === null) {
+ this._head = node;
+ }
+ }
+
+ /**
+ * Preallocate a bunch of linked-list nodes. Allocating these nodes ahead of time means that
+ * they're more likely to live next to each other in memory, which seems to improve performance.
+ *
+ * Each empty object only consumes about 60 bytes of memory, so this is pretty cheap, even for
+ * large maps.
+ */
+ public prealloc(count: number): void {
+ const nodePool = this._nodePool;
+ for (let i = 0; i < count; i++) {
+ nodePool.push({
+ prev: null,
+ next: null,
+ key: null,
+ value: null
+ });
+ }
+ }
+
+ public get(key: number): T | null {
+ // This is unsafe: We're assuming our keyspace doesn't overlap with Object.prototype. However,
+ // it's faster than calling hasOwnProperty, and in our case, it would never overlap.
+ const node = this._map[key];
+ if (node !== undefined) {
+ this._unlinkNode(node);
+ this._appendNode(node);
+ return node.value;
+ }
+ return null;
+ }
+
+ /**
+ * Gets a value from a key without marking it as the most recently used item.
+ */
+ public peekValue(key: number): T | null {
+ const node = this._map[key];
+ if (node !== undefined) {
+ return node.value;
+ }
+ return null;
+ }
+
+ public peek(): T | null {
+ const head = this._head;
+ return head === null ? null : head.value;
+ }
+
+ public set(key: number, value: T): void {
+ // This is unsafe: See note above.
+ let node = this._map[key];
+ if (node !== undefined) {
+ // already exists, we just need to mutate it and move it to the end of the list
+ node = this._map[key];
+ this._unlinkNode(node);
+ node.value = value;
+ } else if (this.size >= this.capacity) {
+ // we're out of space: recycle the head node, move it to the tail
+ node = this._head!;
+ this._unlinkNode(node);
+ delete this._map[node.key!];
+ node.key = key;
+ node.value = value;
+ this._map[key] = node;
+ } else {
+ // make a new element
+ const nodePool = this._nodePool;
+ if (nodePool.length > 0) {
+ // use a preallocated node if we can
+ node = nodePool.pop()!;
+ node.key = key;
+ node.value = value;
+ } else {
+ node = {
+ prev: null,
+ next: null,
+ key,
+ value
+ };
+ }
+ this._map[key] = node;
+ this.size++;
+ }
+ this._appendNode(node);
+ }
+}