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
3296 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
private static cache = new Array<SmallImmutableSet<any>>(129);
14
15
private static create<T>(items: number, additionalItems: readonly number[]): SmallImmutableSet<T> {
16
if (items <= 128 && additionalItems.length === 0) {
17
// We create a cache of 128=2^7 elements to cover all sets with up to 7 (dense) elements.
18
let cached = SmallImmutableSet.cache[items];
19
if (!cached) {
20
cached = new SmallImmutableSet(items, additionalItems);
21
SmallImmutableSet.cache[items] = cached;
22
}
23
return cached;
24
}
25
26
return new SmallImmutableSet(items, additionalItems);
27
}
28
29
private static empty = SmallImmutableSet.create<any>(0, emptyArr);
30
public static getEmpty<T>(): SmallImmutableSet<T> {
31
return this.empty;
32
}
33
34
private constructor(
35
private readonly items: number,
36
private readonly additionalItems: readonly number[]
37
) {
38
}
39
40
public add(value: T, keyProvider: IDenseKeyProvider<T>): SmallImmutableSet<T> {
41
const key = keyProvider.getKey(value);
42
let idx = key >> 5; // divided by 32
43
if (idx === 0) {
44
// fast path
45
const newItem = (1 << key) | this.items;
46
if (newItem === this.items) {
47
return this;
48
}
49
return SmallImmutableSet.create(newItem, this.additionalItems);
50
}
51
idx--;
52
53
const newItems = this.additionalItems.slice(0);
54
while (newItems.length < idx) {
55
newItems.push(0);
56
}
57
newItems[idx] |= 1 << (key & 31);
58
59
return SmallImmutableSet.create(this.items, newItems);
60
}
61
62
public has(value: T, keyProvider: IDenseKeyProvider<T>): boolean {
63
const key = keyProvider.getKey(value);
64
let idx = key >> 5; // divided by 32
65
if (idx === 0) {
66
// fast path
67
return (this.items & (1 << key)) !== 0;
68
}
69
idx--;
70
71
return ((this.additionalItems[idx] || 0) & (1 << (key & 31))) !== 0;
72
}
73
74
public merge(other: SmallImmutableSet<T>): SmallImmutableSet<T> {
75
const merged = this.items | other.items;
76
77
if (this.additionalItems === emptyArr && other.additionalItems === emptyArr) {
78
// fast path
79
if (merged === this.items) {
80
return this;
81
}
82
if (merged === other.items) {
83
return other;
84
}
85
return SmallImmutableSet.create(merged, emptyArr);
86
}
87
88
// This can be optimized, but it's not a common case
89
const newItems: number[] = [];
90
for (let i = 0; i < Math.max(this.additionalItems.length, other.additionalItems.length); i++) {
91
const item1 = this.additionalItems[i] || 0;
92
const item2 = other.additionalItems[i] || 0;
93
newItems.push(item1 | item2);
94
}
95
96
return SmallImmutableSet.create(merged, newItems);
97
}
98
99
public intersects(other: SmallImmutableSet<T>): boolean {
100
if ((this.items & other.items) !== 0) {
101
return true;
102
}
103
104
for (let i = 0; i < Math.min(this.additionalItems.length, other.additionalItems.length); i++) {
105
if ((this.additionalItems[i] & other.additionalItems[i]) !== 0) {
106
return true;
107
}
108
}
109
110
return false;
111
}
112
113
public equals(other: SmallImmutableSet<T>): boolean {
114
if (this.items !== other.items) {
115
return false;
116
}
117
118
if (this.additionalItems.length !== other.additionalItems.length) {
119
return false;
120
}
121
122
for (let i = 0; i < this.additionalItems.length; i++) {
123
if (this.additionalItems[i] !== other.additionalItems[i]) {
124
return false;
125
}
126
}
127
128
return true;
129
}
130
}
131
132
export interface IDenseKeyProvider<T> {
133
getKey(value: T): number;
134
}
135
136
export const identityKeyProvider: IDenseKeyProvider<number> = {
137
getKey(value: number) {
138
return value;
139
}
140
};
141
142
/**
143
* Assigns values a unique incrementing key.
144
*/
145
export class DenseKeyProvider<T> {
146
private readonly items = new Map<T, number>();
147
148
getKey(value: T): number {
149
let existing = this.items.get(value);
150
if (existing === undefined) {
151
existing = this.items.size;
152
this.items.set(value, existing);
153
}
154
return existing;
155
}
156
157
reverseLookup(value: number): T | undefined {
158
return [...this.items].find(([_key, v]) => v === value)?.[0];
159
}
160
161
reverseLookupSet(set: SmallImmutableSet<T>): T[] {
162
const result: T[] = [];
163
for (const [key] of this.items) {
164
if (set.has(key, this)) {
165
result.push(key);
166
}
167
}
168
return result;
169
}
170
171
keys(): IterableIterator<T> {
172
return this.items.keys();
173
}
174
}
175
176