Path: blob/main/extensions/copilot/src/util/vs/base/common/ternarySearchTree.ts
13405 views
//!!! DO NOT modify, this file was COPIED from 'microsoft/vscode'12/*---------------------------------------------------------------------------------------------3* Copyright (c) Microsoft Corporation. All rights reserved.4* Licensed under the MIT License. See License.txt in the project root for license information.5*--------------------------------------------------------------------------------------------*/67import { shuffle } from './arrays';8import { assert } from './assert';9import { CharCode } from './charCode';10import { compare, compareIgnoreCase, compareSubstring, compareSubstringIgnoreCase } from './strings';11import { URI } from './uri';1213export interface IKeyIterator<K> {14reset(key: K): this;15next(): this;1617hasNext(): boolean;18cmp(a: string): number;19value(): string;20}2122export class StringIterator implements IKeyIterator<string> {2324private _value: string = '';25private _pos: number = 0;2627reset(key: string): this {28this._value = key;29this._pos = 0;30return this;31}3233next(): this {34this._pos += 1;35return this;36}3738hasNext(): boolean {39return this._pos < this._value.length - 1;40}4142cmp(a: string): number {43const aCode = a.charCodeAt(0);44const thisCode = this._value.charCodeAt(this._pos);45return aCode - thisCode;46}4748value(): string {49return this._value[this._pos];50}51}5253export class ConfigKeysIterator implements IKeyIterator<string> {5455private _value!: string;56private _from!: number;57private _to!: number;5859constructor(60private readonly _caseSensitive: boolean = true61) { }6263reset(key: string): this {64this._value = key;65this._from = 0;66this._to = 0;67return this.next();68}6970hasNext(): boolean {71return this._to < this._value.length;72}7374next(): this {75// this._data = key.split(/[\\/]/).filter(s => !!s);76this._from = this._to;77let justSeps = true;78for (; this._to < this._value.length; this._to++) {79const ch = this._value.charCodeAt(this._to);80if (ch === CharCode.Period) {81if (justSeps) {82this._from++;83} else {84break;85}86} else {87justSeps = false;88}89}90return this;91}9293cmp(a: string): number {94return this._caseSensitive95? compareSubstring(a, this._value, 0, a.length, this._from, this._to)96: compareSubstringIgnoreCase(a, this._value, 0, a.length, this._from, this._to);97}9899value(): string {100return this._value.substring(this._from, this._to);101}102}103104export class PathIterator implements IKeyIterator<string> {105106private _value!: string;107private _valueLen!: number;108private _from!: number;109private _to!: number;110111constructor(112private readonly _splitOnBackslash: boolean = true,113private readonly _caseSensitive: boolean = true114) { }115116reset(key: string): this {117this._from = 0;118this._to = 0;119this._value = key;120this._valueLen = key.length;121for (let pos = key.length - 1; pos >= 0; pos--, this._valueLen--) {122const ch = this._value.charCodeAt(pos);123if (!(ch === CharCode.Slash || this._splitOnBackslash && ch === CharCode.Backslash)) {124break;125}126}127128return this.next();129}130131hasNext(): boolean {132return this._to < this._valueLen;133}134135next(): this {136// this._data = key.split(/[\\/]/).filter(s => !!s);137this._from = this._to;138let justSeps = true;139for (; this._to < this._valueLen; this._to++) {140const ch = this._value.charCodeAt(this._to);141if (ch === CharCode.Slash || this._splitOnBackslash && ch === CharCode.Backslash) {142if (justSeps) {143this._from++;144} else {145break;146}147} else {148justSeps = false;149}150}151return this;152}153154cmp(a: string): number {155return this._caseSensitive156? compareSubstring(a, this._value, 0, a.length, this._from, this._to)157: compareSubstringIgnoreCase(a, this._value, 0, a.length, this._from, this._to);158}159160value(): string {161return this._value.substring(this._from, this._to);162}163}164165const enum UriIteratorState {166Scheme = 1, Authority = 2, Path = 3, Query = 4, Fragment = 5167}168169export class UriIterator implements IKeyIterator<URI> {170171private _pathIterator!: PathIterator;172private _value!: URI;173private _states: UriIteratorState[] = [];174private _stateIdx: number = 0;175176constructor(177private readonly _ignorePathCasing: (uri: URI) => boolean,178private readonly _ignoreQueryAndFragment: (uri: URI) => boolean) { }179180reset(key: URI): this {181this._value = key;182this._states = [];183if (this._value.scheme) {184this._states.push(UriIteratorState.Scheme);185}186if (this._value.authority) {187this._states.push(UriIteratorState.Authority);188}189if (this._value.path) {190this._pathIterator = new PathIterator(false, !this._ignorePathCasing(key));191this._pathIterator.reset(key.path);192if (this._pathIterator.value()) {193this._states.push(UriIteratorState.Path);194}195}196if (!this._ignoreQueryAndFragment(key)) {197if (this._value.query) {198this._states.push(UriIteratorState.Query);199}200if (this._value.fragment) {201this._states.push(UriIteratorState.Fragment);202}203}204this._stateIdx = 0;205return this;206}207208next(): this {209if (this._states[this._stateIdx] === UriIteratorState.Path && this._pathIterator.hasNext()) {210this._pathIterator.next();211} else {212this._stateIdx += 1;213}214return this;215}216217hasNext(): boolean {218return (this._states[this._stateIdx] === UriIteratorState.Path && this._pathIterator.hasNext())219|| this._stateIdx < this._states.length - 1;220}221222cmp(a: string): number {223if (this._states[this._stateIdx] === UriIteratorState.Scheme) {224return compareIgnoreCase(a, this._value.scheme);225} else if (this._states[this._stateIdx] === UriIteratorState.Authority) {226return compareIgnoreCase(a, this._value.authority);227} else if (this._states[this._stateIdx] === UriIteratorState.Path) {228return this._pathIterator.cmp(a);229} else if (this._states[this._stateIdx] === UriIteratorState.Query) {230return compare(a, this._value.query);231} else if (this._states[this._stateIdx] === UriIteratorState.Fragment) {232return compare(a, this._value.fragment);233}234throw new Error();235}236237value(): string {238if (this._states[this._stateIdx] === UriIteratorState.Scheme) {239return this._value.scheme;240} else if (this._states[this._stateIdx] === UriIteratorState.Authority) {241return this._value.authority;242} else if (this._states[this._stateIdx] === UriIteratorState.Path) {243return this._pathIterator.value();244} else if (this._states[this._stateIdx] === UriIteratorState.Query) {245return this._value.query;246} else if (this._states[this._stateIdx] === UriIteratorState.Fragment) {247return this._value.fragment;248}249throw new Error();250}251}252253abstract class Undef {254255static readonly Val: unique symbol = Symbol('undefined_placeholder');256257static wrap<V>(value: V | undefined): V | typeof Undef.Val {258return value === undefined ? Undef.Val : value;259}260261static unwrap<V>(value: V | typeof Undef.Val): V | undefined {262return value === Undef.Val ? undefined : value;263}264}265266class TernarySearchTreeNode<K, V> {267height: number = 1;268segment!: string;269value: V | typeof Undef.Val | undefined = undefined;270key: K | undefined = undefined;271left: TernarySearchTreeNode<K, V> | undefined = undefined;272mid: TernarySearchTreeNode<K, V> | undefined = undefined;273right: TernarySearchTreeNode<K, V> | undefined = undefined;274275isEmpty(): boolean {276return !this.left && !this.mid && !this.right && this.value === undefined;277}278279rotateLeft() {280const tmp = this.right!;281this.right = tmp.left;282tmp.left = this;283this.updateHeight();284tmp.updateHeight();285return tmp;286}287288rotateRight() {289const tmp = this.left!;290this.left = tmp.right;291tmp.right = this;292this.updateHeight();293tmp.updateHeight();294return tmp;295}296297updateHeight() {298this.height = 1 + Math.max(this.heightLeft, this.heightRight);299}300301balanceFactor() {302return this.heightRight - this.heightLeft;303}304305get heightLeft() {306return this.left?.height ?? 0;307}308309get heightRight() {310return this.right?.height ?? 0;311}312}313314const enum Dir {315Left = -1,316Mid = 0,317Right = 1318}319320export class TernarySearchTree<K, V> {321322static forUris<E>(ignorePathCasing: (key: URI) => boolean = () => false, ignoreQueryAndFragment: (key: URI) => boolean = () => false): TernarySearchTree<URI, E> {323return new TernarySearchTree<URI, E>(new UriIterator(ignorePathCasing, ignoreQueryAndFragment));324}325326static forPaths<E>(ignorePathCasing = false): TernarySearchTree<string, E> {327return new TernarySearchTree<string, E>(new PathIterator(undefined, !ignorePathCasing));328}329330static forStrings<E>(): TernarySearchTree<string, E> {331return new TernarySearchTree<string, E>(new StringIterator());332}333334static forConfigKeys<E>(): TernarySearchTree<string, E> {335return new TernarySearchTree<string, E>(new ConfigKeysIterator());336}337338private _iter: IKeyIterator<K>;339private _root: TernarySearchTreeNode<K, V> | undefined;340341constructor(segments: IKeyIterator<K>) {342this._iter = segments;343}344345clear(): void {346this._root = undefined;347}348349/**350* Fill the tree with the same value of the given keys351*/352fill(element: V, keys: readonly K[]): void;353/**354* Fill the tree with given [key,value]-tuples355*/356fill(values: readonly [K, V][]): void;357fill(values: readonly [K, V][] | V, keys?: readonly K[]): void {358if (keys) {359const arr = keys.slice(0);360shuffle(arr);361for (const k of arr) {362this.set(k, (<V>values));363}364} else {365const arr = (<[K, V][]>values).slice(0);366shuffle(arr);367for (const entry of arr) {368this.set(entry[0], entry[1]);369}370}371}372373set(key: K, element: V): V | undefined {374const iter = this._iter.reset(key);375let node: TernarySearchTreeNode<K, V>;376377if (!this._root) {378this._root = new TernarySearchTreeNode<K, V>();379this._root.segment = iter.value();380}381const stack: [Dir, TernarySearchTreeNode<K, V>][] = [];382383// find insert_node384node = this._root;385while (true) {386const val = iter.cmp(node.segment);387if (val > 0) {388// left389if (!node.left) {390node.left = new TernarySearchTreeNode<K, V>();391node.left.segment = iter.value();392}393stack.push([Dir.Left, node]);394node = node.left;395396} else if (val < 0) {397// right398if (!node.right) {399node.right = new TernarySearchTreeNode<K, V>();400node.right.segment = iter.value();401}402stack.push([Dir.Right, node]);403node = node.right;404405} else if (iter.hasNext()) {406// mid407iter.next();408if (!node.mid) {409node.mid = new TernarySearchTreeNode<K, V>();410node.mid.segment = iter.value();411}412stack.push([Dir.Mid, node]);413node = node.mid;414} else {415break;416}417}418419// set value420const oldElement = Undef.unwrap(node.value);421node.value = Undef.wrap(element);422node.key = key;423424// balance425for (let i = stack.length - 1; i >= 0; i--) {426const node = stack[i][1];427428node.updateHeight();429const bf = node.balanceFactor();430431if (bf < -1 || bf > 1) {432// needs rotate433const d1 = stack[i][0];434const d2 = stack[i + 1][0];435436if (d1 === Dir.Right && d2 === Dir.Right) {437//right, right -> rotate left438stack[i][1] = node.rotateLeft();439440} else if (d1 === Dir.Left && d2 === Dir.Left) {441// left, left -> rotate right442stack[i][1] = node.rotateRight();443444} else if (d1 === Dir.Right && d2 === Dir.Left) {445// right, left -> double rotate right, left446node.right = stack[i + 1][1] = stack[i + 1][1].rotateRight();447stack[i][1] = node.rotateLeft();448449} else if (d1 === Dir.Left && d2 === Dir.Right) {450// left, right -> double rotate left, right451node.left = stack[i + 1][1] = stack[i + 1][1].rotateLeft();452stack[i][1] = node.rotateRight();453454} else {455throw new Error();456}457458// patch path to parent459if (i > 0) {460switch (stack[i - 1][0]) {461case Dir.Left:462stack[i - 1][1].left = stack[i][1];463break;464case Dir.Right:465stack[i - 1][1].right = stack[i][1];466break;467case Dir.Mid:468stack[i - 1][1].mid = stack[i][1];469break;470}471} else {472this._root = stack[0][1];473}474}475}476477return oldElement;478}479480get(key: K): V | undefined {481return Undef.unwrap(this._getNode(key)?.value);482}483484private _getNode(key: K) {485const iter = this._iter.reset(key);486let node = this._root;487while (node) {488const val = iter.cmp(node.segment);489if (val > 0) {490// left491node = node.left;492} else if (val < 0) {493// right494node = node.right;495} else if (iter.hasNext()) {496// mid497iter.next();498node = node.mid;499} else {500break;501}502}503return node;504}505506has(key: K): boolean {507const node = this._getNode(key);508return !(node?.value === undefined && node?.mid === undefined);509}510511delete(key: K): void {512return this._delete(key, false);513}514515deleteSuperstr(key: K): void {516return this._delete(key, true);517}518519private _delete(key: K, superStr: boolean): void {520const iter = this._iter.reset(key);521const stack: [Dir, TernarySearchTreeNode<K, V>][] = [];522let node = this._root;523524// find node525while (node) {526const val = iter.cmp(node.segment);527if (val > 0) {528// left529stack.push([Dir.Left, node]);530node = node.left;531} else if (val < 0) {532// right533stack.push([Dir.Right, node]);534node = node.right;535} else if (iter.hasNext()) {536// mid537iter.next();538stack.push([Dir.Mid, node]);539node = node.mid;540} else {541break;542}543}544545if (!node) {546// node not found547return;548}549550if (superStr) {551// removing children, reset height552node.left = undefined;553node.mid = undefined;554node.right = undefined;555node.height = 1;556} else {557// removing element558node.key = undefined;559node.value = undefined;560}561562// BST node removal563if (!node.mid && !node.value) {564if (node.left && node.right) {565// full node566// replace deleted-node with the min-node of the right branch.567// If there is no true min-node leave things as they are568const stack2: typeof stack = [[Dir.Right, node]];569const min = this._min(node.right, stack2);570571if (min.key) {572573node.key = min.key;574node.value = min.value;575node.segment = min.segment;576577// remove NODE (inorder successor can only have right child)578const newChild = min.right;579if (stack2.length > 1) {580const [dir, parent] = stack2[stack2.length - 1];581switch (dir) {582case Dir.Left: parent.left = newChild; break;583case Dir.Mid: assert(false);584case Dir.Right: assert(false);585}586} else {587node.right = newChild;588}589590// balance right branch and UPDATE parent pointer for stack591const newChild2 = this._balanceByStack(stack2)!;592if (stack.length > 0) {593const [dir, parent] = stack[stack.length - 1];594switch (dir) {595case Dir.Left: parent.left = newChild2; break;596case Dir.Mid: parent.mid = newChild2; break;597case Dir.Right: parent.right = newChild2; break;598}599} else {600this._root = newChild2;601}602}603604} else {605// empty or half empty606const newChild = node.left ?? node.right;607if (stack.length > 0) {608const [dir, parent] = stack[stack.length - 1];609switch (dir) {610case Dir.Left: parent.left = newChild; break;611case Dir.Mid: parent.mid = newChild; break;612case Dir.Right: parent.right = newChild; break;613}614} else {615this._root = newChild;616}617}618}619620// AVL balance621this._root = this._balanceByStack(stack) ?? this._root;622}623624private _min(node: TernarySearchTreeNode<K, V>, stack: [Dir, TernarySearchTreeNode<K, V>][]): TernarySearchTreeNode<K, V> {625while (node.left) {626stack.push([Dir.Left, node]);627node = node.left;628}629return node;630}631632private _balanceByStack(stack: [Dir, TernarySearchTreeNode<K, V>][]) {633634for (let i = stack.length - 1; i >= 0; i--) {635const node = stack[i][1];636637node.updateHeight();638const bf = node.balanceFactor();639if (bf > 1) {640// right heavy641if (node.right!.balanceFactor() >= 0) {642// right, right -> rotate left643stack[i][1] = node.rotateLeft();644} else {645// right, left -> double rotate646node.right = node.right!.rotateRight();647stack[i][1] = node.rotateLeft();648}649650} else if (bf < -1) {651// left heavy652if (node.left!.balanceFactor() <= 0) {653// left, left -> rotate right654stack[i][1] = node.rotateRight();655} else {656// left, right -> double rotate657node.left = node.left!.rotateLeft();658stack[i][1] = node.rotateRight();659}660}661662// patch path to parent663if (i > 0) {664switch (stack[i - 1][0]) {665case Dir.Left:666stack[i - 1][1].left = stack[i][1];667break;668case Dir.Right:669stack[i - 1][1].right = stack[i][1];670break;671case Dir.Mid:672stack[i - 1][1].mid = stack[i][1];673break;674}675} else {676return stack[0][1];677}678}679680return undefined;681}682683findSubstr(key: K): V | undefined {684const iter = this._iter.reset(key);685let node = this._root;686let candidate: V | undefined = undefined;687while (node) {688const val = iter.cmp(node.segment);689if (val > 0) {690// left691node = node.left;692} else if (val < 0) {693// right694node = node.right;695} else if (iter.hasNext()) {696// mid697iter.next();698candidate = Undef.unwrap(node.value) || candidate;699node = node.mid;700} else {701break;702}703}704return node && Undef.unwrap(node.value) || candidate;705}706707findSuperstr(key: K): IterableIterator<[K, V]> | undefined {708return this._findSuperstrOrElement(key, false);709}710711private _findSuperstrOrElement(key: K, allowValue: true): IterableIterator<[K, V]> | V | undefined;712private _findSuperstrOrElement(key: K, allowValue: false): IterableIterator<[K, V]> | undefined;713private _findSuperstrOrElement(key: K, allowValue: boolean): IterableIterator<[K, V]> | V | undefined {714const iter = this._iter.reset(key);715let node = this._root;716while (node) {717const val = iter.cmp(node.segment);718if (val > 0) {719// left720node = node.left;721} else if (val < 0) {722// right723node = node.right;724} else if (iter.hasNext()) {725// mid726iter.next();727node = node.mid;728} else {729// collect730if (!node.mid) {731if (allowValue) {732return Undef.unwrap(node.value);733} else {734return undefined;735}736} else {737return this._entries(node.mid);738}739}740}741return undefined;742}743744hasElementOrSubtree(key: K): boolean {745return this._findSuperstrOrElement(key, true) !== undefined;746}747748forEach(callback: (value: V, index: K) => unknown): void {749for (const [key, value] of this) {750callback(value, key);751}752}753754*[Symbol.iterator](): IterableIterator<[K, V]> {755yield* this._entries(this._root);756}757758private _entries(node: TernarySearchTreeNode<K, V> | undefined): IterableIterator<[K, V]> {759const result: [K, V][] = [];760this._dfsEntries(node, result);761return result[Symbol.iterator]();762}763764private _dfsEntries(node: TernarySearchTreeNode<K, V> | undefined, bucket: [K, V][]) {765// DFS766if (!node) {767return;768}769if (node.left) {770this._dfsEntries(node.left, bucket);771}772if (node.value !== undefined) {773bucket.push([node.key!, Undef.unwrap(node.value)!]);774}775if (node.mid) {776this._dfsEntries(node.mid, bucket);777}778if (node.right) {779this._dfsEntries(node.right, bucket);780}781}782783// for debug/testing784_isBalanced(): boolean {785const nodeIsBalanced = (node: TernarySearchTreeNode<unknown, unknown> | undefined): boolean => {786if (!node) {787return true;788}789const bf = node.balanceFactor();790if (bf < -1 || bf > 1) {791return false;792}793return nodeIsBalanced(node.left) && nodeIsBalanced(node.right);794};795return nodeIsBalanced(this._root);796}797}798799800