|
|
- 'use strict';
- import { Iterables } from './iterable';
- import { Strings } from './string';
-
- // Code stolen from https://github.com/Microsoft/vscode/blob/b3e6d5bb039a4a9362b52a2c8726267ca68cf64e/src/vs/base/common/map.ts#L352
-
- const FIN = { done: true, value: undefined };
-
- // eslint-disable-next-line @typescript-eslint/interface-name-prefix
- export interface IKeyIterator {
- reset(key: string): this;
- next(): this;
-
- hasNext(): boolean;
- cmp(a: string): number;
- value(): string;
- }
-
- export class StringIterator implements IKeyIterator {
- private _value: string = '';
- private _pos: number = 0;
-
- reset(key: string): this {
- this._value = key;
- this._pos = 0;
- return this;
- }
-
- next(): this {
- this._pos += 1;
- return this;
- }
-
- hasNext(): boolean {
- return this._pos < this._value.length - 1;
- }
-
- cmp(a: string): number {
- const aCode = a.charCodeAt(0);
- const thisCode = this._value.charCodeAt(this._pos);
- return aCode - thisCode;
- }
-
- value(): string {
- return this._value[this._pos];
- }
- }
-
- export class PathIterator implements IKeyIterator {
- private _value!: string;
- private _from!: number;
- private _to!: number;
-
- reset(key: string): this {
- this._value = key.replace(/\\$|\/$/, '');
- this._from = 0;
- this._to = 0;
- return this.next();
- }
-
- hasNext(): boolean {
- return this._to < this._value.length;
- }
-
- next(): this {
- // 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 === Strings.CharCode.Slash || ch === Strings.CharCode.Backslash) {
- if (justSeps) {
- this._from++;
- } else {
- break;
- }
- } else {
- justSeps = false;
- }
- }
- return this;
- }
-
- cmp(a: string): number {
- let aPos = 0;
- const aLen = a.length;
- let thisPos = this._from;
-
- while (aPos < aLen && thisPos < this._to) {
- const 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;
- }
-
- if (aPos < aLen) {
- return -1;
- }
-
- return 1;
- }
-
- value(): string {
- return this._value.substring(this._from, this._to);
- }
- }
-
- class TernarySearchTreeNode<E> {
- segment!: string;
- value: E | undefined;
- key!: string;
- left: TernarySearchTreeNode<E> | undefined;
- mid: TernarySearchTreeNode<E> | undefined;
- right: TernarySearchTreeNode<E> | undefined;
-
- isEmpty(): boolean {
- return !this.left && !this.mid && !this.right && !this.value;
- }
- }
-
- export class TernarySearchTree<E> {
- static forPaths<E>(): TernarySearchTree<E> {
- return new TernarySearchTree<E>(new PathIterator());
- }
-
- static forStrings<E>(): TernarySearchTree<E> {
- return new TernarySearchTree<E>(new StringIterator());
- }
-
- private _iter: IKeyIterator;
- private _root: TernarySearchTreeNode<E> | undefined;
-
- constructor(segments: IKeyIterator) {
- this._iter = segments;
- }
-
- clear(): void {
- this._root = undefined;
- }
-
- set(key: string, element: E): E | undefined {
- const iter = this._iter.reset(key);
- let node: TernarySearchTreeNode<E>;
-
- if (!this._root) {
- this._root = new TernarySearchTreeNode<E>();
- 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<E>();
- node.left.segment = iter.value();
- }
- node = node.left;
- } else if (val < 0) {
- // right
- if (!node.right) {
- node.right = new TernarySearchTreeNode<E>();
- node.right.segment = iter.value();
- }
- node = node.right;
- } else if (iter.hasNext()) {
- // mid
- iter.next();
- if (!node.mid) {
- node.mid = new TernarySearchTreeNode<E>();
- node.mid.segment = iter.value();
- }
- node = node.mid;
- } else {
- break;
- }
- }
- const oldElement = node.value;
- node.value = element;
- node.key = key;
- return oldElement;
- }
-
- get(key: string): E | undefined {
- 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 ? node.value : undefined;
- }
-
- delete(key: string): void {
- const iter = this._iter.reset(key);
- const stack: [-1 | 0 | 1, TernarySearchTreeNode<E>][] = [];
- 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 {
- // remove element
- node.value = undefined;
-
- // clean up empty nodes
- while (stack.length > 0 && node.isEmpty()) {
- const [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: string): E | undefined {
- const iter = this._iter.reset(key);
- let node = this._root;
- let candidate: E | 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: string, limit: boolean = false): Iterable<E> | undefined {
- 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;
- }
-
- node = node.mid;
- return {
- // eslint-disable-next-line no-loop-func
- [Symbol.iterator]: () => this._nodeIterator(node!, limit)
- };
- }
- }
- return undefined;
- }
-
- private _nodeIterator(node: TernarySearchTreeNode<E>, limit: boolean = false): Iterator<E> {
- let res: { done: false; value: E };
- let idx: number;
- let data: E[];
- const next = (): IteratorResult<E> => {
- if (!data) {
- // lazy till first invocation
- data = [];
- idx = 0;
- this._forEach(node, value => data.push(value), limit);
- }
- if (idx >= data.length) {
- return (FIN as unknown) as IteratorResult<E>;
- }
-
- if (!res) {
- res = { done: false, value: data[idx++] };
- } else {
- res.value = data[idx++];
- }
- return res;
- };
- return { next: next };
- }
-
- forEach(callback: (value: E, index: string) => any) {
- this._forEach(this._root, callback);
- }
-
- private _forEach(
- node: TernarySearchTreeNode<E> | undefined,
- callback: (value: E, index: string) => any,
- limit: boolean = false
- ) {
- if (node === undefined) return;
-
- // left
- this._forEach(node.left, callback, limit);
-
- // node
- if (node.value) {
- callback(node.value, node.key);
- }
-
- if (!limit) {
- // mid
- this._forEach(node.mid, callback, limit);
- }
-
- // right
- this._forEach(node.right, callback, limit);
- }
-
- any(): boolean {
- return this._root !== undefined && !this._root.isEmpty();
- }
-
- count(predicate?: (entry: E) => boolean): number {
- if (this._root === undefined || this._root.isEmpty()) return 0;
-
- return Iterables.count(this.entries(), predicate === undefined ? undefined : ([e]) => predicate(e));
- }
-
- entries(): Iterable<[E, string]> {
- return this._iterator(this._root);
- }
-
- values(): Iterable<E> {
- return Iterables.map(this.entries(), ([e]) => e);
- }
-
- highlander(): [E, string] | undefined {
- if (this._root === undefined || this._root.isEmpty()) return undefined;
-
- const entries = this.entries() as IterableIterator<[E, string]>;
-
- let count = 0;
- let next: IteratorResult<[E, string]>;
- let value: [E, string] | undefined;
-
- while (true) {
- next = entries.next();
- if (next.done) break;
-
- value = next.value;
-
- count++;
- if (count > 1) return undefined;
- }
-
- return value;
- }
-
- some(predicate: (entry: E) => boolean): boolean {
- if (this._root === undefined || this._root.isEmpty()) return false;
-
- return Iterables.some(this.entries(), ([e]) => predicate(e));
- }
-
- private *_iterator(node: TernarySearchTreeNode<E> | undefined): IterableIterator<[E, string]> {
- if (node !== undefined) {
- // left
- yield* this._iterator(node.left);
-
- // node
- if (node.value) {
- yield [node.value, node.key];
- }
-
- // mid
- yield* this._iterator(node.mid);
-
- // right
- yield* this._iterator(node.right);
- }
- }
- }
|