Path: blob/main/src/vs/editor/common/model/bracketPairsTextModelPart/bracketPairsTree/smallImmutableSet.ts
5222 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45const emptyArr: number[] = [];67/**8* Represents an immutable set that works best for a small number of elements (less than 32).9* It uses bits to encode element membership efficiently.10*/11export class SmallImmutableSet<T> {12// eslint-disable-next-line @typescript-eslint/no-explicit-any13private static cache = new Array<SmallImmutableSet<any>>(129);1415private static create<T>(items: number, additionalItems: readonly number[]): SmallImmutableSet<T> {16if (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.18let cached = SmallImmutableSet.cache[items];19if (!cached) {20cached = new SmallImmutableSet(items, additionalItems);21SmallImmutableSet.cache[items] = cached;22}23return cached;24}2526return new SmallImmutableSet(items, additionalItems);27}2829// eslint-disable-next-line @typescript-eslint/no-explicit-any30private static empty = SmallImmutableSet.create<any>(0, emptyArr);31public static getEmpty<T>(): SmallImmutableSet<T> {32return this.empty;33}3435private constructor(36private readonly items: number,37private readonly additionalItems: readonly number[]38) {39}4041public add(value: T, keyProvider: IDenseKeyProvider<T>): SmallImmutableSet<T> {42const key = keyProvider.getKey(value);43let idx = key >> 5; // divided by 3244if (idx === 0) {45// fast path46const newItem = (1 << key) | this.items;47if (newItem === this.items) {48return this;49}50return SmallImmutableSet.create(newItem, this.additionalItems);51}52idx--;5354const newItems = this.additionalItems.slice(0);55while (newItems.length < idx) {56newItems.push(0);57}58newItems[idx] |= 1 << (key & 31);5960return SmallImmutableSet.create(this.items, newItems);61}6263public has(value: T, keyProvider: IDenseKeyProvider<T>): boolean {64const key = keyProvider.getKey(value);65let idx = key >> 5; // divided by 3266if (idx === 0) {67// fast path68return (this.items & (1 << key)) !== 0;69}70idx--;7172return ((this.additionalItems[idx] || 0) & (1 << (key & 31))) !== 0;73}7475public merge(other: SmallImmutableSet<T>): SmallImmutableSet<T> {76const merged = this.items | other.items;7778if (this.additionalItems === emptyArr && other.additionalItems === emptyArr) {79// fast path80if (merged === this.items) {81return this;82}83if (merged === other.items) {84return other;85}86return SmallImmutableSet.create(merged, emptyArr);87}8889// This can be optimized, but it's not a common case90const newItems: number[] = [];91for (let i = 0; i < Math.max(this.additionalItems.length, other.additionalItems.length); i++) {92const item1 = this.additionalItems[i] || 0;93const item2 = other.additionalItems[i] || 0;94newItems.push(item1 | item2);95}9697return SmallImmutableSet.create(merged, newItems);98}99100public intersects(other: SmallImmutableSet<T>): boolean {101if ((this.items & other.items) !== 0) {102return true;103}104105for (let i = 0; i < Math.min(this.additionalItems.length, other.additionalItems.length); i++) {106if ((this.additionalItems[i] & other.additionalItems[i]) !== 0) {107return true;108}109}110111return false;112}113114public equals(other: SmallImmutableSet<T>): boolean {115if (this.items !== other.items) {116return false;117}118119if (this.additionalItems.length !== other.additionalItems.length) {120return false;121}122123for (let i = 0; i < this.additionalItems.length; i++) {124if (this.additionalItems[i] !== other.additionalItems[i]) {125return false;126}127}128129return true;130}131}132133export interface IDenseKeyProvider<T> {134getKey(value: T): number;135}136137export const identityKeyProvider: IDenseKeyProvider<number> = {138getKey(value: number) {139return value;140}141};142143/**144* Assigns values a unique incrementing key.145*/146export class DenseKeyProvider<T> {147private readonly items = new Map<T, number>();148149getKey(value: T): number {150let existing = this.items.get(value);151if (existing === undefined) {152existing = this.items.size;153this.items.set(value, existing);154}155return existing;156}157158reverseLookup(value: number): T | undefined {159return [...this.items].find(([_key, v]) => v === value)?.[0];160}161162reverseLookupSet(set: SmallImmutableSet<T>): T[] {163const result: T[] = [];164for (const [key] of this.items) {165if (set.has(key, this)) {166result.push(key);167}168}169return result;170}171172keys(): IterableIterator<T> {173return this.items.keys();174}175}176177178