Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/editor/common/model/bracketPairsTextModelPart/bracketPairsTree/smallImmutableSet.ts
5222 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
const emptyArr: number[] = [];
7
8
/**
9
* Represents an immutable set that works best for a small number of elements (less than 32).
10
* It uses bits to encode element membership efficiently.
11
*/
12
export class SmallImmutableSet<T> {
13
// eslint-disable-next-line @typescript-eslint/no-explicit-any
14
private static cache = new Array<SmallImmutableSet<any>>(129);
15
16
private static create<T>(items: number, additionalItems: readonly number[]): SmallImmutableSet<T> {
17
if (items <= 128 && additionalItems.length === 0) {
18
// We create a cache of 128=2^7 elements to cover all sets with up to 7 (dense) elements.
19
let cached = SmallImmutableSet.cache[items];
20
if (!cached) {
21
cached = new SmallImmutableSet(items, additionalItems);
22
SmallImmutableSet.cache[items] = cached;
23
}
24
return cached;
25
}
26
27
return new SmallImmutableSet(items, additionalItems);
28
}
29
30
// eslint-disable-next-line @typescript-eslint/no-explicit-any
31
private static empty = SmallImmutableSet.create<any>(0, emptyArr);
32
public static getEmpty<T>(): SmallImmutableSet<T> {
33
return this.empty;
34
}
35
36
private constructor(
37
private readonly items: number,
38
private readonly additionalItems: readonly number[]
39
) {
40
}
41
42
public add(value: T, keyProvider: IDenseKeyProvider<T>): SmallImmutableSet<T> {
43
const key = keyProvider.getKey(value);
44
let idx = key >> 5; // divided by 32
45
if (idx === 0) {
46
// fast path
47
const newItem = (1 << key) | this.items;
48
if (newItem === this.items) {
49
return this;
50
}
51
return SmallImmutableSet.create(newItem, this.additionalItems);
52
}
53
idx--;
54
55
const newItems = this.additionalItems.slice(0);
56
while (newItems.length < idx) {
57
newItems.push(0);
58
}
59
newItems[idx] |= 1 << (key & 31);
60
61
return SmallImmutableSet.create(this.items, newItems);
62
}
63
64
public has(value: T, keyProvider: IDenseKeyProvider<T>): boolean {
65
const key = keyProvider.getKey(value);
66
let idx = key >> 5; // divided by 32
67
if (idx === 0) {
68
// fast path
69
return (this.items & (1 << key)) !== 0;
70
}
71
idx--;
72
73
return ((this.additionalItems[idx] || 0) & (1 << (key & 31))) !== 0;
74
}
75
76
public merge(other: SmallImmutableSet<T>): SmallImmutableSet<T> {
77
const merged = this.items | other.items;
78
79
if (this.additionalItems === emptyArr && other.additionalItems === emptyArr) {
80
// fast path
81
if (merged === this.items) {
82
return this;
83
}
84
if (merged === other.items) {
85
return other;
86
}
87
return SmallImmutableSet.create(merged, emptyArr);
88
}
89
90
// This can be optimized, but it's not a common case
91
const newItems: number[] = [];
92
for (let i = 0; i < Math.max(this.additionalItems.length, other.additionalItems.length); i++) {
93
const item1 = this.additionalItems[i] || 0;
94
const item2 = other.additionalItems[i] || 0;
95
newItems.push(item1 | item2);
96
}
97
98
return SmallImmutableSet.create(merged, newItems);
99
}
100
101
public intersects(other: SmallImmutableSet<T>): boolean {
102
if ((this.items & other.items) !== 0) {
103
return true;
104
}
105
106
for (let i = 0; i < Math.min(this.additionalItems.length, other.additionalItems.length); i++) {
107
if ((this.additionalItems[i] & other.additionalItems[i]) !== 0) {
108
return true;
109
}
110
}
111
112
return false;
113
}
114
115
public equals(other: SmallImmutableSet<T>): boolean {
116
if (this.items !== other.items) {
117
return false;
118
}
119
120
if (this.additionalItems.length !== other.additionalItems.length) {
121
return false;
122
}
123
124
for (let i = 0; i < this.additionalItems.length; i++) {
125
if (this.additionalItems[i] !== other.additionalItems[i]) {
126
return false;
127
}
128
}
129
130
return true;
131
}
132
}
133
134
export interface IDenseKeyProvider<T> {
135
getKey(value: T): number;
136
}
137
138
export const identityKeyProvider: IDenseKeyProvider<number> = {
139
getKey(value: number) {
140
return value;
141
}
142
};
143
144
/**
145
* Assigns values a unique incrementing key.
146
*/
147
export class DenseKeyProvider<T> {
148
private readonly items = new Map<T, number>();
149
150
getKey(value: T): number {
151
let existing = this.items.get(value);
152
if (existing === undefined) {
153
existing = this.items.size;
154
this.items.set(value, existing);
155
}
156
return existing;
157
}
158
159
reverseLookup(value: number): T | undefined {
160
return [...this.items].find(([_key, v]) => v === value)?.[0];
161
}
162
163
reverseLookupSet(set: SmallImmutableSet<T>): T[] {
164
const result: T[] = [];
165
for (const [key] of this.items) {
166
if (set.has(key, this)) {
167
result.push(key);
168
}
169
}
170
return result;
171
}
172
173
keys(): IterableIterator<T> {
174
return this.items.keys();
175
}
176
}
177
178