/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ var _a, _b; import { compare, compareIgnoreCase, compareSubstring, compareSubstringIgnoreCase } from './strings.js'; import { URI } from './uri.js'; export class StringIterator { constructor() { this._value = ''; this._pos = 0; } reset(key) { this._value = key; this._pos = 0; return this; } next() { this._pos += 1; return this; } hasNext() { return this._pos < this._value.length - 1; } cmp(a) { const aCode = a.charCodeAt(0); const thisCode = this._value.charCodeAt(this._pos); return aCode - thisCode; } value() { return this._value[this._pos]; } } export class ConfigKeysIterator { constructor(_caseSensitive = true) { this._caseSensitive = _caseSensitive; } reset(key) { this._value = key; this._from = 0; this._to = 0; return this.next(); } hasNext() { return this._to < this._value.length; } next() { // this._data = key.split(/[\\/]/).filter(s => !!s); this._from = this._to; let justSeps = true; for (; this._to < this._value.length; this._to++) { const ch = this._value.charCodeAt(this._to); if (ch === 46 /* Period */) { if (justSeps) { this._from++; } else { break; } } else { justSeps = false; } } return this; } cmp(a) { return this._caseSensitive ? compareSubstring(a, this._value, 0, a.length, this._from, this._to) : compareSubstringIgnoreCase(a, this._value, 0, a.length, this._from, this._to); } value() { return this._value.substring(this._from, this._to); } } export class PathIterator { constructor(_splitOnBackslash = true, _caseSensitive = true) { this._splitOnBackslash = _splitOnBackslash; this._caseSensitive = _caseSensitive; } reset(key) { this._value = key.replace(/\\$|\/$/, ''); this._from = 0; this._to = 0; return this.next(); } hasNext() { return this._to < this._value.length; } next() { // this._data = key.split(/[\\/]/).filter(s => !!s); this._from = this._to; let justSeps = true; for (; this._to < this._value.length; this._to++) { const ch = this._value.charCodeAt(this._to); if (ch === 47 /* Slash */ || this._splitOnBackslash && ch === 92 /* Backslash */) { if (justSeps) { this._from++; } else { break; } } else { justSeps = false; } } return this; } cmp(a) { return this._caseSensitive ? compareSubstring(a, this._value, 0, a.length, this._from, this._to) : compareSubstringIgnoreCase(a, this._value, 0, a.length, this._from, this._to); } value() { return this._value.substring(this._from, this._to); } } export class UriIterator { constructor(_ignorePathCasing) { this._ignorePathCasing = _ignorePathCasing; this._states = []; this._stateIdx = 0; } reset(key) { this._value = key; this._states = []; if (this._value.scheme) { this._states.push(1 /* Scheme */); } if (this._value.authority) { this._states.push(2 /* Authority */); } if (this._value.path) { this._pathIterator = new PathIterator(false, !this._ignorePathCasing(key)); this._pathIterator.reset(key.path); if (this._pathIterator.value()) { this._states.push(3 /* Path */); } } if (this._value.query) { this._states.push(4 /* Query */); } if (this._value.fragment) { this._states.push(5 /* Fragment */); } this._stateIdx = 0; return this; } next() { if (this._states[this._stateIdx] === 3 /* Path */ && this._pathIterator.hasNext()) { this._pathIterator.next(); } else { this._stateIdx += 1; } return this; } hasNext() { return (this._states[this._stateIdx] === 3 /* Path */ && this._pathIterator.hasNext()) || this._stateIdx < this._states.length - 1; } cmp(a) { if (this._states[this._stateIdx] === 1 /* Scheme */) { return compareIgnoreCase(a, this._value.scheme); } else if (this._states[this._stateIdx] === 2 /* Authority */) { return compareIgnoreCase(a, this._value.authority); } else if (this._states[this._stateIdx] === 3 /* Path */) { return this._pathIterator.cmp(a); } else if (this._states[this._stateIdx] === 4 /* Query */) { return compare(a, this._value.query); } else if (this._states[this._stateIdx] === 5 /* Fragment */) { return compare(a, this._value.fragment); } throw new Error(); } value() { if (this._states[this._stateIdx] === 1 /* Scheme */) { return this._value.scheme; } else if (this._states[this._stateIdx] === 2 /* Authority */) { return this._value.authority; } else if (this._states[this._stateIdx] === 3 /* Path */) { return this._pathIterator.value(); } else if (this._states[this._stateIdx] === 4 /* Query */) { return this._value.query; } else if (this._states[this._stateIdx] === 5 /* Fragment */) { return this._value.fragment; } throw new Error(); } } class TernarySearchTreeNode { isEmpty() { return !this.left && !this.mid && !this.right && !this.value; } } export class TernarySearchTree { constructor(segments) { this._iter = segments; } static forUris(ignorePathCasing = () => false) { return new TernarySearchTree(new UriIterator(ignorePathCasing)); } static forStrings() { return new TernarySearchTree(new StringIterator()); } static forConfigKeys() { return new TernarySearchTree(new ConfigKeysIterator()); } clear() { this._root = undefined; } set(key, element) { const iter = this._iter.reset(key); let node; if (!this._root) { this._root = new TernarySearchTreeNode(); this._root.segment = iter.value(); } node = this._root; while (true) { const val = iter.cmp(node.segment); if (val > 0) { // left if (!node.left) { node.left = new TernarySearchTreeNode(); node.left.segment = iter.value(); } node = node.left; } else if (val < 0) { // right if (!node.right) { node.right = new TernarySearchTreeNode(); node.right.segment = iter.value(); } node = node.right; } else if (iter.hasNext()) { // mid iter.next(); if (!node.mid) { node.mid = new TernarySearchTreeNode(); node.mid.segment = iter.value(); } node = node.mid; } else { break; } } const oldElement = node.value; node.value = element; node.key = key; return oldElement; } get(key) { var _c; return (_c = this._getNode(key)) === null || _c === void 0 ? void 0 : _c.value; } _getNode(key) { const iter = this._iter.reset(key); let node = this._root; while (node) { const val = iter.cmp(node.segment); 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; } } return node; } has(key) { const node = this._getNode(key); return !((node === null || node === void 0 ? void 0 : node.value) === undefined && (node === null || node === void 0 ? void 0 : node.mid) === undefined); } delete(key) { return this._delete(key, false); } deleteSuperstr(key) { return this._delete(key, true); } _delete(key, superStr) { const iter = this._iter.reset(key); const stack = []; let node = this._root; // find and unset node while (node) { const val = iter.cmp(node.segment); 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 { if (superStr) { // remove children node.left = undefined; node.mid = undefined; node.right = undefined; } else { // remove element node.value = undefined; } // 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; } } } findSubstr(key) { const iter = this._iter.reset(key); let node = this._root; let candidate = undefined; while (node) { const val = iter.cmp(node.segment); if (val > 0) { // left node = node.left; } else if (val < 0) { // right node = node.right; } else if (iter.hasNext()) { // mid iter.next(); candidate = node.value || candidate; node = node.mid; } else { break; } } return node && node.value || candidate; } findSuperstr(key) { const iter = this._iter.reset(key); let node = this._root; while (node) { const val = iter.cmp(node.segment); 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; } else { return this._entries(node.mid); } } } return undefined; } forEach(callback) { for (const [key, value] of this) { callback(value, key); } } *[Symbol.iterator]() { yield* this._entries(this._root); } *_entries(node) { // DFS if (!node) { return; } const stack = [node]; while (stack.length > 0) { const node = stack.pop(); if (node) { if (node.value) { yield [node.key, node.value]; } if (node.left) { stack.push(node.left); } if (node.mid) { stack.push(node.mid); } if (node.right) { stack.push(node.right); } } } } } export class ResourceMap { constructor(mapOrKeyFn, toKey) { this[_a] = 'ResourceMap'; if (mapOrKeyFn instanceof ResourceMap) { this.map = new Map(mapOrKeyFn.map); this.toKey = toKey !== null && toKey !== void 0 ? toKey : ResourceMap.defaultToKey; } else { this.map = new Map(); this.toKey = mapOrKeyFn !== null && mapOrKeyFn !== void 0 ? mapOrKeyFn : ResourceMap.defaultToKey; } } set(resource, value) { this.map.set(this.toKey(resource), value); return this; } get(resource) { return this.map.get(this.toKey(resource)); } has(resource) { return this.map.has(this.toKey(resource)); } get size() { return this.map.size; } clear() { this.map.clear(); } delete(resource) { return this.map.delete(this.toKey(resource)); } forEach(clb, thisArg) { if (typeof thisArg !== 'undefined') { clb = clb.bind(thisArg); } for (let [index, value] of this.map) { clb(value, URI.parse(index), this); } } values() { return this.map.values(); } *keys() { for (let key of this.map.keys()) { yield URI.parse(key); } } *entries() { for (let tuple of this.map.entries()) { yield [URI.parse(tuple[0]), tuple[1]]; } } *[(_a = Symbol.toStringTag, Symbol.iterator)]() { for (let item of this.map) { yield [URI.parse(item[0]), item[1]]; } } } ResourceMap.defaultToKey = (resource) => resource.toString(); export class LinkedMap { constructor() { this[_b] = 'LinkedMap'; this._map = new Map(); this._head = undefined; this._tail = undefined; this._size = 0; this._state = 0; } clear() { this._map.clear(); this._head = undefined; this._tail = undefined; this._size = 0; this._state++; } isEmpty() { return !this._head && !this._tail; } get size() { return this._size; } get first() { var _c; return (_c = this._head) === null || _c === void 0 ? void 0 : _c.value; } get last() { var _c; return (_c = this._tail) === null || _c === void 0 ? void 0 : _c.value; } has(key) { return this._map.has(key); } get(key, touch = 0 /* None */) { const item = this._map.get(key); if (!item) { return undefined; } if (touch !== 0 /* None */) { this.touch(item, touch); } return item.value; } set(key, value, touch = 0 /* None */) { let item = this._map.get(key); if (item) { item.value = value; if (touch !== 0 /* None */) { this.touch(item, touch); } } else { item = { key, value, next: undefined, previous: undefined }; switch (touch) { case 0 /* None */: this.addItemLast(item); break; case 1 /* AsOld */: this.addItemFirst(item); break; case 2 /* AsNew */: this.addItemLast(item); break; default: this.addItemLast(item); break; } this._map.set(key, item); this._size++; } return this; } delete(key) { return !!this.remove(key); } remove(key) { const item = this._map.get(key); if (!item) { return undefined; } this._map.delete(key); this.removeItem(item); this._size--; return item.value; } shift() { 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; } forEach(callbackfn, thisArg) { const state = this._state; let current = this._head; while (current) { if (thisArg) { callbackfn.bind(thisArg)(current.value, current.key, this); } else { callbackfn(current.value, current.key, this); } if (this._state !== state) { throw new Error(`LinkedMap got modified during iteration.`); } current = current.next; } } keys() { const map = this; const state = this._state; let current = this._head; const iterator = { [Symbol.iterator]() { return iterator; }, next() { if (map._state !== state) { throw new Error(`LinkedMap got modified during iteration.`); } if (current) { const result = { value: current.key, done: false }; current = current.next; return result; } else { return { value: undefined, done: true }; } } }; return iterator; } values() { const map = this; const state = this._state; let current = this._head; const iterator = { [Symbol.iterator]() { return iterator; }, next() { if (map._state !== state) { throw new Error(`LinkedMap got modified during iteration.`); } if (current) { const result = { value: current.value, done: false }; current = current.next; return result; } else { return { value: undefined, done: true }; } } }; return iterator; } entries() { const map = this; const state = this._state; let current = this._head; const iterator = { [Symbol.iterator]() { return iterator; }, next() { if (map._state !== state) { throw new Error(`LinkedMap got modified during iteration.`); } if (current) { const result = { value: [current.key, current.value], done: false }; current = current.next; return result; } else { return { value: undefined, done: true }; } } }; return iterator; } [(_b = Symbol.toStringTag, Symbol.iterator)]() { return this.entries(); } trimOld(newSize) { if (newSize >= this.size) { return; } if (newSize === 0) { this.clear(); return; } let current = this._head; let currentSize = this.size; while (current && currentSize > newSize) { this._map.delete(current.key); current = current.next; currentSize--; } this._head = current; this._size = currentSize; if (current) { current.previous = undefined; } this._state++; } addItemFirst(item) { // 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; this._state++; } addItemLast(item) { // 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; this._state++; } removeItem(item) { if (item === this._head && item === this._tail) { this._head = undefined; this._tail = undefined; } else if (item === this._head) { // This can only happen if size === 1 which is handled // by the case above. if (!item.next) { throw new Error('Invalid list'); } item.next.previous = undefined; this._head = item.next; } else if (item === this._tail) { // This can only happen if size === 1 which is handled // by the case above. if (!item.previous) { throw new Error('Invalid list'); } item.previous.next = undefined; 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; } item.next = undefined; item.previous = undefined; this._state++; } touch(item, touch) { if (!this._head || !this._tail) { throw new Error('Invalid list'); } if ((touch !== 1 /* AsOld */ && touch !== 2 /* AsNew */)) { return; } if (touch === 1 /* AsOld */) { 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; this._state++; } else if (touch === 2 /* AsNew */) { 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; this._state++; } } toJSON() { const data = []; this.forEach((value, key) => { data.push([key, value]); }); return data; } fromJSON(data) { this.clear(); for (const [key, value] of data) { this.set(key, value); } } } export class LRUCache extends LinkedMap { constructor(limit, ratio = 1) { super(); this._limit = limit; this._ratio = Math.min(Math.max(0, ratio), 1); } get limit() { return this._limit; } set limit(limit) { this._limit = limit; this.checkTrim(); } get(key, touch = 2 /* AsNew */) { return super.get(key, touch); } peek(key) { return super.get(key, 0 /* None */); } set(key, value) { super.set(key, value, 2 /* AsNew */); this.checkTrim(); return this; } checkTrim() { if (this.size > this._limit) { this.trimOld(Math.round(this._limit * this._ratio)); } } }