Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/extensions/copilot/src/util/vs/base/common/ternarySearchTree.ts
13405 views
1
//!!! DO NOT modify, this file was COPIED from 'microsoft/vscode'
2
3
/*---------------------------------------------------------------------------------------------
4
* Copyright (c) Microsoft Corporation. All rights reserved.
5
* Licensed under the MIT License. See License.txt in the project root for license information.
6
*--------------------------------------------------------------------------------------------*/
7
8
import { shuffle } from './arrays';
9
import { assert } from './assert';
10
import { CharCode } from './charCode';
11
import { compare, compareIgnoreCase, compareSubstring, compareSubstringIgnoreCase } from './strings';
12
import { URI } from './uri';
13
14
export interface IKeyIterator<K> {
15
reset(key: K): this;
16
next(): this;
17
18
hasNext(): boolean;
19
cmp(a: string): number;
20
value(): string;
21
}
22
23
export class StringIterator implements IKeyIterator<string> {
24
25
private _value: string = '';
26
private _pos: number = 0;
27
28
reset(key: string): this {
29
this._value = key;
30
this._pos = 0;
31
return this;
32
}
33
34
next(): this {
35
this._pos += 1;
36
return this;
37
}
38
39
hasNext(): boolean {
40
return this._pos < this._value.length - 1;
41
}
42
43
cmp(a: string): number {
44
const aCode = a.charCodeAt(0);
45
const thisCode = this._value.charCodeAt(this._pos);
46
return aCode - thisCode;
47
}
48
49
value(): string {
50
return this._value[this._pos];
51
}
52
}
53
54
export class ConfigKeysIterator implements IKeyIterator<string> {
55
56
private _value!: string;
57
private _from!: number;
58
private _to!: number;
59
60
constructor(
61
private readonly _caseSensitive: boolean = true
62
) { }
63
64
reset(key: string): this {
65
this._value = key;
66
this._from = 0;
67
this._to = 0;
68
return this.next();
69
}
70
71
hasNext(): boolean {
72
return this._to < this._value.length;
73
}
74
75
next(): this {
76
// this._data = key.split(/[\\/]/).filter(s => !!s);
77
this._from = this._to;
78
let justSeps = true;
79
for (; this._to < this._value.length; this._to++) {
80
const ch = this._value.charCodeAt(this._to);
81
if (ch === CharCode.Period) {
82
if (justSeps) {
83
this._from++;
84
} else {
85
break;
86
}
87
} else {
88
justSeps = false;
89
}
90
}
91
return this;
92
}
93
94
cmp(a: string): number {
95
return this._caseSensitive
96
? compareSubstring(a, this._value, 0, a.length, this._from, this._to)
97
: compareSubstringIgnoreCase(a, this._value, 0, a.length, this._from, this._to);
98
}
99
100
value(): string {
101
return this._value.substring(this._from, this._to);
102
}
103
}
104
105
export class PathIterator implements IKeyIterator<string> {
106
107
private _value!: string;
108
private _valueLen!: number;
109
private _from!: number;
110
private _to!: number;
111
112
constructor(
113
private readonly _splitOnBackslash: boolean = true,
114
private readonly _caseSensitive: boolean = true
115
) { }
116
117
reset(key: string): this {
118
this._from = 0;
119
this._to = 0;
120
this._value = key;
121
this._valueLen = key.length;
122
for (let pos = key.length - 1; pos >= 0; pos--, this._valueLen--) {
123
const ch = this._value.charCodeAt(pos);
124
if (!(ch === CharCode.Slash || this._splitOnBackslash && ch === CharCode.Backslash)) {
125
break;
126
}
127
}
128
129
return this.next();
130
}
131
132
hasNext(): boolean {
133
return this._to < this._valueLen;
134
}
135
136
next(): this {
137
// this._data = key.split(/[\\/]/).filter(s => !!s);
138
this._from = this._to;
139
let justSeps = true;
140
for (; this._to < this._valueLen; this._to++) {
141
const ch = this._value.charCodeAt(this._to);
142
if (ch === CharCode.Slash || this._splitOnBackslash && ch === CharCode.Backslash) {
143
if (justSeps) {
144
this._from++;
145
} else {
146
break;
147
}
148
} else {
149
justSeps = false;
150
}
151
}
152
return this;
153
}
154
155
cmp(a: string): number {
156
return this._caseSensitive
157
? compareSubstring(a, this._value, 0, a.length, this._from, this._to)
158
: compareSubstringIgnoreCase(a, this._value, 0, a.length, this._from, this._to);
159
}
160
161
value(): string {
162
return this._value.substring(this._from, this._to);
163
}
164
}
165
166
const enum UriIteratorState {
167
Scheme = 1, Authority = 2, Path = 3, Query = 4, Fragment = 5
168
}
169
170
export class UriIterator implements IKeyIterator<URI> {
171
172
private _pathIterator!: PathIterator;
173
private _value!: URI;
174
private _states: UriIteratorState[] = [];
175
private _stateIdx: number = 0;
176
177
constructor(
178
private readonly _ignorePathCasing: (uri: URI) => boolean,
179
private readonly _ignoreQueryAndFragment: (uri: URI) => boolean) { }
180
181
reset(key: URI): this {
182
this._value = key;
183
this._states = [];
184
if (this._value.scheme) {
185
this._states.push(UriIteratorState.Scheme);
186
}
187
if (this._value.authority) {
188
this._states.push(UriIteratorState.Authority);
189
}
190
if (this._value.path) {
191
this._pathIterator = new PathIterator(false, !this._ignorePathCasing(key));
192
this._pathIterator.reset(key.path);
193
if (this._pathIterator.value()) {
194
this._states.push(UriIteratorState.Path);
195
}
196
}
197
if (!this._ignoreQueryAndFragment(key)) {
198
if (this._value.query) {
199
this._states.push(UriIteratorState.Query);
200
}
201
if (this._value.fragment) {
202
this._states.push(UriIteratorState.Fragment);
203
}
204
}
205
this._stateIdx = 0;
206
return this;
207
}
208
209
next(): this {
210
if (this._states[this._stateIdx] === UriIteratorState.Path && this._pathIterator.hasNext()) {
211
this._pathIterator.next();
212
} else {
213
this._stateIdx += 1;
214
}
215
return this;
216
}
217
218
hasNext(): boolean {
219
return (this._states[this._stateIdx] === UriIteratorState.Path && this._pathIterator.hasNext())
220
|| this._stateIdx < this._states.length - 1;
221
}
222
223
cmp(a: string): number {
224
if (this._states[this._stateIdx] === UriIteratorState.Scheme) {
225
return compareIgnoreCase(a, this._value.scheme);
226
} else if (this._states[this._stateIdx] === UriIteratorState.Authority) {
227
return compareIgnoreCase(a, this._value.authority);
228
} else if (this._states[this._stateIdx] === UriIteratorState.Path) {
229
return this._pathIterator.cmp(a);
230
} else if (this._states[this._stateIdx] === UriIteratorState.Query) {
231
return compare(a, this._value.query);
232
} else if (this._states[this._stateIdx] === UriIteratorState.Fragment) {
233
return compare(a, this._value.fragment);
234
}
235
throw new Error();
236
}
237
238
value(): string {
239
if (this._states[this._stateIdx] === UriIteratorState.Scheme) {
240
return this._value.scheme;
241
} else if (this._states[this._stateIdx] === UriIteratorState.Authority) {
242
return this._value.authority;
243
} else if (this._states[this._stateIdx] === UriIteratorState.Path) {
244
return this._pathIterator.value();
245
} else if (this._states[this._stateIdx] === UriIteratorState.Query) {
246
return this._value.query;
247
} else if (this._states[this._stateIdx] === UriIteratorState.Fragment) {
248
return this._value.fragment;
249
}
250
throw new Error();
251
}
252
}
253
254
abstract class Undef {
255
256
static readonly Val: unique symbol = Symbol('undefined_placeholder');
257
258
static wrap<V>(value: V | undefined): V | typeof Undef.Val {
259
return value === undefined ? Undef.Val : value;
260
}
261
262
static unwrap<V>(value: V | typeof Undef.Val): V | undefined {
263
return value === Undef.Val ? undefined : value;
264
}
265
}
266
267
class TernarySearchTreeNode<K, V> {
268
height: number = 1;
269
segment!: string;
270
value: V | typeof Undef.Val | undefined = undefined;
271
key: K | undefined = undefined;
272
left: TernarySearchTreeNode<K, V> | undefined = undefined;
273
mid: TernarySearchTreeNode<K, V> | undefined = undefined;
274
right: TernarySearchTreeNode<K, V> | undefined = undefined;
275
276
isEmpty(): boolean {
277
return !this.left && !this.mid && !this.right && this.value === undefined;
278
}
279
280
rotateLeft() {
281
const tmp = this.right!;
282
this.right = tmp.left;
283
tmp.left = this;
284
this.updateHeight();
285
tmp.updateHeight();
286
return tmp;
287
}
288
289
rotateRight() {
290
const tmp = this.left!;
291
this.left = tmp.right;
292
tmp.right = this;
293
this.updateHeight();
294
tmp.updateHeight();
295
return tmp;
296
}
297
298
updateHeight() {
299
this.height = 1 + Math.max(this.heightLeft, this.heightRight);
300
}
301
302
balanceFactor() {
303
return this.heightRight - this.heightLeft;
304
}
305
306
get heightLeft() {
307
return this.left?.height ?? 0;
308
}
309
310
get heightRight() {
311
return this.right?.height ?? 0;
312
}
313
}
314
315
const enum Dir {
316
Left = -1,
317
Mid = 0,
318
Right = 1
319
}
320
321
export class TernarySearchTree<K, V> {
322
323
static forUris<E>(ignorePathCasing: (key: URI) => boolean = () => false, ignoreQueryAndFragment: (key: URI) => boolean = () => false): TernarySearchTree<URI, E> {
324
return new TernarySearchTree<URI, E>(new UriIterator(ignorePathCasing, ignoreQueryAndFragment));
325
}
326
327
static forPaths<E>(ignorePathCasing = false): TernarySearchTree<string, E> {
328
return new TernarySearchTree<string, E>(new PathIterator(undefined, !ignorePathCasing));
329
}
330
331
static forStrings<E>(): TernarySearchTree<string, E> {
332
return new TernarySearchTree<string, E>(new StringIterator());
333
}
334
335
static forConfigKeys<E>(): TernarySearchTree<string, E> {
336
return new TernarySearchTree<string, E>(new ConfigKeysIterator());
337
}
338
339
private _iter: IKeyIterator<K>;
340
private _root: TernarySearchTreeNode<K, V> | undefined;
341
342
constructor(segments: IKeyIterator<K>) {
343
this._iter = segments;
344
}
345
346
clear(): void {
347
this._root = undefined;
348
}
349
350
/**
351
* Fill the tree with the same value of the given keys
352
*/
353
fill(element: V, keys: readonly K[]): void;
354
/**
355
* Fill the tree with given [key,value]-tuples
356
*/
357
fill(values: readonly [K, V][]): void;
358
fill(values: readonly [K, V][] | V, keys?: readonly K[]): void {
359
if (keys) {
360
const arr = keys.slice(0);
361
shuffle(arr);
362
for (const k of arr) {
363
this.set(k, (<V>values));
364
}
365
} else {
366
const arr = (<[K, V][]>values).slice(0);
367
shuffle(arr);
368
for (const entry of arr) {
369
this.set(entry[0], entry[1]);
370
}
371
}
372
}
373
374
set(key: K, element: V): V | undefined {
375
const iter = this._iter.reset(key);
376
let node: TernarySearchTreeNode<K, V>;
377
378
if (!this._root) {
379
this._root = new TernarySearchTreeNode<K, V>();
380
this._root.segment = iter.value();
381
}
382
const stack: [Dir, TernarySearchTreeNode<K, V>][] = [];
383
384
// find insert_node
385
node = this._root;
386
while (true) {
387
const val = iter.cmp(node.segment);
388
if (val > 0) {
389
// left
390
if (!node.left) {
391
node.left = new TernarySearchTreeNode<K, V>();
392
node.left.segment = iter.value();
393
}
394
stack.push([Dir.Left, node]);
395
node = node.left;
396
397
} else if (val < 0) {
398
// right
399
if (!node.right) {
400
node.right = new TernarySearchTreeNode<K, V>();
401
node.right.segment = iter.value();
402
}
403
stack.push([Dir.Right, node]);
404
node = node.right;
405
406
} else if (iter.hasNext()) {
407
// mid
408
iter.next();
409
if (!node.mid) {
410
node.mid = new TernarySearchTreeNode<K, V>();
411
node.mid.segment = iter.value();
412
}
413
stack.push([Dir.Mid, node]);
414
node = node.mid;
415
} else {
416
break;
417
}
418
}
419
420
// set value
421
const oldElement = Undef.unwrap(node.value);
422
node.value = Undef.wrap(element);
423
node.key = key;
424
425
// balance
426
for (let i = stack.length - 1; i >= 0; i--) {
427
const node = stack[i][1];
428
429
node.updateHeight();
430
const bf = node.balanceFactor();
431
432
if (bf < -1 || bf > 1) {
433
// needs rotate
434
const d1 = stack[i][0];
435
const d2 = stack[i + 1][0];
436
437
if (d1 === Dir.Right && d2 === Dir.Right) {
438
//right, right -> rotate left
439
stack[i][1] = node.rotateLeft();
440
441
} else if (d1 === Dir.Left && d2 === Dir.Left) {
442
// left, left -> rotate right
443
stack[i][1] = node.rotateRight();
444
445
} else if (d1 === Dir.Right && d2 === Dir.Left) {
446
// right, left -> double rotate right, left
447
node.right = stack[i + 1][1] = stack[i + 1][1].rotateRight();
448
stack[i][1] = node.rotateLeft();
449
450
} else if (d1 === Dir.Left && d2 === Dir.Right) {
451
// left, right -> double rotate left, right
452
node.left = stack[i + 1][1] = stack[i + 1][1].rotateLeft();
453
stack[i][1] = node.rotateRight();
454
455
} else {
456
throw new Error();
457
}
458
459
// patch path to parent
460
if (i > 0) {
461
switch (stack[i - 1][0]) {
462
case Dir.Left:
463
stack[i - 1][1].left = stack[i][1];
464
break;
465
case Dir.Right:
466
stack[i - 1][1].right = stack[i][1];
467
break;
468
case Dir.Mid:
469
stack[i - 1][1].mid = stack[i][1];
470
break;
471
}
472
} else {
473
this._root = stack[0][1];
474
}
475
}
476
}
477
478
return oldElement;
479
}
480
481
get(key: K): V | undefined {
482
return Undef.unwrap(this._getNode(key)?.value);
483
}
484
485
private _getNode(key: K) {
486
const iter = this._iter.reset(key);
487
let node = this._root;
488
while (node) {
489
const val = iter.cmp(node.segment);
490
if (val > 0) {
491
// left
492
node = node.left;
493
} else if (val < 0) {
494
// right
495
node = node.right;
496
} else if (iter.hasNext()) {
497
// mid
498
iter.next();
499
node = node.mid;
500
} else {
501
break;
502
}
503
}
504
return node;
505
}
506
507
has(key: K): boolean {
508
const node = this._getNode(key);
509
return !(node?.value === undefined && node?.mid === undefined);
510
}
511
512
delete(key: K): void {
513
return this._delete(key, false);
514
}
515
516
deleteSuperstr(key: K): void {
517
return this._delete(key, true);
518
}
519
520
private _delete(key: K, superStr: boolean): void {
521
const iter = this._iter.reset(key);
522
const stack: [Dir, TernarySearchTreeNode<K, V>][] = [];
523
let node = this._root;
524
525
// find node
526
while (node) {
527
const val = iter.cmp(node.segment);
528
if (val > 0) {
529
// left
530
stack.push([Dir.Left, node]);
531
node = node.left;
532
} else if (val < 0) {
533
// right
534
stack.push([Dir.Right, node]);
535
node = node.right;
536
} else if (iter.hasNext()) {
537
// mid
538
iter.next();
539
stack.push([Dir.Mid, node]);
540
node = node.mid;
541
} else {
542
break;
543
}
544
}
545
546
if (!node) {
547
// node not found
548
return;
549
}
550
551
if (superStr) {
552
// removing children, reset height
553
node.left = undefined;
554
node.mid = undefined;
555
node.right = undefined;
556
node.height = 1;
557
} else {
558
// removing element
559
node.key = undefined;
560
node.value = undefined;
561
}
562
563
// BST node removal
564
if (!node.mid && !node.value) {
565
if (node.left && node.right) {
566
// full node
567
// replace deleted-node with the min-node of the right branch.
568
// If there is no true min-node leave things as they are
569
const stack2: typeof stack = [[Dir.Right, node]];
570
const min = this._min(node.right, stack2);
571
572
if (min.key) {
573
574
node.key = min.key;
575
node.value = min.value;
576
node.segment = min.segment;
577
578
// remove NODE (inorder successor can only have right child)
579
const newChild = min.right;
580
if (stack2.length > 1) {
581
const [dir, parent] = stack2[stack2.length - 1];
582
switch (dir) {
583
case Dir.Left: parent.left = newChild; break;
584
case Dir.Mid: assert(false);
585
case Dir.Right: assert(false);
586
}
587
} else {
588
node.right = newChild;
589
}
590
591
// balance right branch and UPDATE parent pointer for stack
592
const newChild2 = this._balanceByStack(stack2)!;
593
if (stack.length > 0) {
594
const [dir, parent] = stack[stack.length - 1];
595
switch (dir) {
596
case Dir.Left: parent.left = newChild2; break;
597
case Dir.Mid: parent.mid = newChild2; break;
598
case Dir.Right: parent.right = newChild2; break;
599
}
600
} else {
601
this._root = newChild2;
602
}
603
}
604
605
} else {
606
// empty or half empty
607
const newChild = node.left ?? node.right;
608
if (stack.length > 0) {
609
const [dir, parent] = stack[stack.length - 1];
610
switch (dir) {
611
case Dir.Left: parent.left = newChild; break;
612
case Dir.Mid: parent.mid = newChild; break;
613
case Dir.Right: parent.right = newChild; break;
614
}
615
} else {
616
this._root = newChild;
617
}
618
}
619
}
620
621
// AVL balance
622
this._root = this._balanceByStack(stack) ?? this._root;
623
}
624
625
private _min(node: TernarySearchTreeNode<K, V>, stack: [Dir, TernarySearchTreeNode<K, V>][]): TernarySearchTreeNode<K, V> {
626
while (node.left) {
627
stack.push([Dir.Left, node]);
628
node = node.left;
629
}
630
return node;
631
}
632
633
private _balanceByStack(stack: [Dir, TernarySearchTreeNode<K, V>][]) {
634
635
for (let i = stack.length - 1; i >= 0; i--) {
636
const node = stack[i][1];
637
638
node.updateHeight();
639
const bf = node.balanceFactor();
640
if (bf > 1) {
641
// right heavy
642
if (node.right!.balanceFactor() >= 0) {
643
// right, right -> rotate left
644
stack[i][1] = node.rotateLeft();
645
} else {
646
// right, left -> double rotate
647
node.right = node.right!.rotateRight();
648
stack[i][1] = node.rotateLeft();
649
}
650
651
} else if (bf < -1) {
652
// left heavy
653
if (node.left!.balanceFactor() <= 0) {
654
// left, left -> rotate right
655
stack[i][1] = node.rotateRight();
656
} else {
657
// left, right -> double rotate
658
node.left = node.left!.rotateLeft();
659
stack[i][1] = node.rotateRight();
660
}
661
}
662
663
// patch path to parent
664
if (i > 0) {
665
switch (stack[i - 1][0]) {
666
case Dir.Left:
667
stack[i - 1][1].left = stack[i][1];
668
break;
669
case Dir.Right:
670
stack[i - 1][1].right = stack[i][1];
671
break;
672
case Dir.Mid:
673
stack[i - 1][1].mid = stack[i][1];
674
break;
675
}
676
} else {
677
return stack[0][1];
678
}
679
}
680
681
return undefined;
682
}
683
684
findSubstr(key: K): V | undefined {
685
const iter = this._iter.reset(key);
686
let node = this._root;
687
let candidate: V | undefined = undefined;
688
while (node) {
689
const val = iter.cmp(node.segment);
690
if (val > 0) {
691
// left
692
node = node.left;
693
} else if (val < 0) {
694
// right
695
node = node.right;
696
} else if (iter.hasNext()) {
697
// mid
698
iter.next();
699
candidate = Undef.unwrap(node.value) || candidate;
700
node = node.mid;
701
} else {
702
break;
703
}
704
}
705
return node && Undef.unwrap(node.value) || candidate;
706
}
707
708
findSuperstr(key: K): IterableIterator<[K, V]> | undefined {
709
return this._findSuperstrOrElement(key, false);
710
}
711
712
private _findSuperstrOrElement(key: K, allowValue: true): IterableIterator<[K, V]> | V | undefined;
713
private _findSuperstrOrElement(key: K, allowValue: false): IterableIterator<[K, V]> | undefined;
714
private _findSuperstrOrElement(key: K, allowValue: boolean): IterableIterator<[K, V]> | V | undefined {
715
const iter = this._iter.reset(key);
716
let node = this._root;
717
while (node) {
718
const val = iter.cmp(node.segment);
719
if (val > 0) {
720
// left
721
node = node.left;
722
} else if (val < 0) {
723
// right
724
node = node.right;
725
} else if (iter.hasNext()) {
726
// mid
727
iter.next();
728
node = node.mid;
729
} else {
730
// collect
731
if (!node.mid) {
732
if (allowValue) {
733
return Undef.unwrap(node.value);
734
} else {
735
return undefined;
736
}
737
} else {
738
return this._entries(node.mid);
739
}
740
}
741
}
742
return undefined;
743
}
744
745
hasElementOrSubtree(key: K): boolean {
746
return this._findSuperstrOrElement(key, true) !== undefined;
747
}
748
749
forEach(callback: (value: V, index: K) => unknown): void {
750
for (const [key, value] of this) {
751
callback(value, key);
752
}
753
}
754
755
*[Symbol.iterator](): IterableIterator<[K, V]> {
756
yield* this._entries(this._root);
757
}
758
759
private _entries(node: TernarySearchTreeNode<K, V> | undefined): IterableIterator<[K, V]> {
760
const result: [K, V][] = [];
761
this._dfsEntries(node, result);
762
return result[Symbol.iterator]();
763
}
764
765
private _dfsEntries(node: TernarySearchTreeNode<K, V> | undefined, bucket: [K, V][]) {
766
// DFS
767
if (!node) {
768
return;
769
}
770
if (node.left) {
771
this._dfsEntries(node.left, bucket);
772
}
773
if (node.value !== undefined) {
774
bucket.push([node.key!, Undef.unwrap(node.value)!]);
775
}
776
if (node.mid) {
777
this._dfsEntries(node.mid, bucket);
778
}
779
if (node.right) {
780
this._dfsEntries(node.right, bucket);
781
}
782
}
783
784
// for debug/testing
785
_isBalanced(): boolean {
786
const nodeIsBalanced = (node: TernarySearchTreeNode<unknown, unknown> | undefined): boolean => {
787
if (!node) {
788
return true;
789
}
790
const bf = node.balanceFactor();
791
if (bf < -1 || bf > 1) {
792
return false;
793
}
794
return nodeIsBalanced(node.left) && nodeIsBalanced(node.right);
795
};
796
return nodeIsBalanced(this._root);
797
}
798
}
799
800