Path: blob/main/src/vs/editor/common/model/bracketPairsTextModelPart/bracketPairsTree/smallImmutableSet.ts
3296 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> {12private static cache = new Array<SmallImmutableSet<any>>(129);1314private static create<T>(items: number, additionalItems: readonly number[]): SmallImmutableSet<T> {15if (items <= 128 && additionalItems.length === 0) {16// We create a cache of 128=2^7 elements to cover all sets with up to 7 (dense) elements.17let cached = SmallImmutableSet.cache[items];18if (!cached) {19cached = new SmallImmutableSet(items, additionalItems);20SmallImmutableSet.cache[items] = cached;21}22return cached;23}2425return new SmallImmutableSet(items, additionalItems);26}2728private static empty = SmallImmutableSet.create<any>(0, emptyArr);29public static getEmpty<T>(): SmallImmutableSet<T> {30return this.empty;31}3233private constructor(34private readonly items: number,35private readonly additionalItems: readonly number[]36) {37}3839public add(value: T, keyProvider: IDenseKeyProvider<T>): SmallImmutableSet<T> {40const key = keyProvider.getKey(value);41let idx = key >> 5; // divided by 3242if (idx === 0) {43// fast path44const newItem = (1 << key) | this.items;45if (newItem === this.items) {46return this;47}48return SmallImmutableSet.create(newItem, this.additionalItems);49}50idx--;5152const newItems = this.additionalItems.slice(0);53while (newItems.length < idx) {54newItems.push(0);55}56newItems[idx] |= 1 << (key & 31);5758return SmallImmutableSet.create(this.items, newItems);59}6061public has(value: T, keyProvider: IDenseKeyProvider<T>): boolean {62const key = keyProvider.getKey(value);63let idx = key >> 5; // divided by 3264if (idx === 0) {65// fast path66return (this.items & (1 << key)) !== 0;67}68idx--;6970return ((this.additionalItems[idx] || 0) & (1 << (key & 31))) !== 0;71}7273public merge(other: SmallImmutableSet<T>): SmallImmutableSet<T> {74const merged = this.items | other.items;7576if (this.additionalItems === emptyArr && other.additionalItems === emptyArr) {77// fast path78if (merged === this.items) {79return this;80}81if (merged === other.items) {82return other;83}84return SmallImmutableSet.create(merged, emptyArr);85}8687// This can be optimized, but it's not a common case88const newItems: number[] = [];89for (let i = 0; i < Math.max(this.additionalItems.length, other.additionalItems.length); i++) {90const item1 = this.additionalItems[i] || 0;91const item2 = other.additionalItems[i] || 0;92newItems.push(item1 | item2);93}9495return SmallImmutableSet.create(merged, newItems);96}9798public intersects(other: SmallImmutableSet<T>): boolean {99if ((this.items & other.items) !== 0) {100return true;101}102103for (let i = 0; i < Math.min(this.additionalItems.length, other.additionalItems.length); i++) {104if ((this.additionalItems[i] & other.additionalItems[i]) !== 0) {105return true;106}107}108109return false;110}111112public equals(other: SmallImmutableSet<T>): boolean {113if (this.items !== other.items) {114return false;115}116117if (this.additionalItems.length !== other.additionalItems.length) {118return false;119}120121for (let i = 0; i < this.additionalItems.length; i++) {122if (this.additionalItems[i] !== other.additionalItems[i]) {123return false;124}125}126127return true;128}129}130131export interface IDenseKeyProvider<T> {132getKey(value: T): number;133}134135export const identityKeyProvider: IDenseKeyProvider<number> = {136getKey(value: number) {137return value;138}139};140141/**142* Assigns values a unique incrementing key.143*/144export class DenseKeyProvider<T> {145private readonly items = new Map<T, number>();146147getKey(value: T): number {148let existing = this.items.get(value);149if (existing === undefined) {150existing = this.items.size;151this.items.set(value, existing);152}153return existing;154}155156reverseLookup(value: number): T | undefined {157return [...this.items].find(([_key, v]) => v === value)?.[0];158}159160reverseLookupSet(set: SmallImmutableSet<T>): T[] {161const result: T[] = [];162for (const [key] of this.items) {163if (set.has(key, this)) {164result.push(key);165}166}167return result;168}169170keys(): IterableIterator<T> {171return this.items.keys();172}173}174175176