Created
June 1, 2025 13:10
-
-
Save Be1zebub/7af10197ba93d2c87f6ac2e6a8e6d8f5 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class BitMask { | |
constructor(initialValue = 0, useBigInt = false) { | |
this.useBigInt = useBigInt | |
this.mask = useBigInt ? BigInt(initialValue) : initialValue | |
} | |
static fromArray(values, useBigInt = false) { | |
const instance = new BitMask(0, useBigInt) | |
values.forEach((value) => instance.add(value)) | |
return instance | |
} | |
static fromBinary(binaryString, useBigInt = false) { | |
if (!/^[01]+$/.test(binaryString)) { | |
throw new Error("Invalid binary string") | |
} | |
const value = useBigInt | |
? BigInt(`0b${binaryString}`) | |
: parseInt(binaryString, 2) | |
return new BitMask(value, useBigInt) | |
} | |
add(bit) { | |
this._validateBit(bit) | |
const bitValue = this.useBigInt ? BigInt(bit) : bit | |
this.mask = this.mask | bitValue | |
return this | |
} | |
remove(bit) { | |
this._validateBit(bit) | |
const bitValue = this.useBigInt ? BigInt(bit) : bit | |
this.mask = this.mask & ~bitValue | |
return this | |
} | |
toggle(bit) { | |
this._validateBit(bit) | |
const bitValue = this.useBigInt ? BigInt(bit) : bit | |
this.mask = this.mask ^ bitValue | |
return this | |
} | |
has(bit) { | |
this._validateBit(bit) | |
const bitValue = this.useBigInt ? BigInt(bit) : bit | |
return (this.mask & bitValue) !== (this.useBigInt ? 0n : 0) | |
} | |
hasAll(bits) { | |
if (!Array.isArray(bits)) { | |
throw new Error("bits must be an array") | |
} | |
return bits.every((bit) => this.has(bit)) | |
} | |
hasAny(bits) { | |
if (!Array.isArray(bits)) { | |
throw new Error("bits must be an array") | |
} | |
return bits.some((bit) => this.has(bit)) | |
} | |
clear() { | |
this.mask = this.useBigInt ? 0n : 0 | |
return this | |
} | |
isEmpty() { | |
return this.mask === (this.useBigInt ? 0n : 0) | |
} | |
union(other) { | |
const otherMask = this._convertToMask(other) | |
this.mask = this.mask | otherMask | |
return this | |
} | |
intersection(other) { | |
const otherMask = this._convertToMask(other) | |
this.mask = this.mask & otherMask | |
return this | |
} | |
xor(other) { | |
const otherMask = this._convertToMask(other) | |
this.mask = this.mask ^ otherMask | |
return this | |
} | |
invert(maxBits = 32) { | |
if (maxBits <= 0) { | |
throw new Error("maxBits must be positive") | |
} | |
if (this.useBigInt) { | |
const mask = (1n << BigInt(maxBits)) - 1n | |
this.mask = this.mask ^ mask | |
} else { | |
const mask = (1 << maxBits) - 1 | |
this.mask = (this.mask ^ mask) >>> 0 | |
} | |
return this | |
} | |
toArray() { | |
const result = [] | |
let tempMask = this.mask | |
let bit = this.useBigInt ? 1n : 1 | |
while (tempMask > (this.useBigInt ? 0n : 0)) { | |
if ((tempMask & bit) !== (this.useBigInt ? 0n : 0)) { | |
if (this.useBigInt && bit > Number.MAX_SAFE_INTEGER) { | |
result.push(bit.toString()) | |
} else { | |
result.push(Number(bit)) | |
} | |
} | |
tempMask = tempMask >> (this.useBigInt ? 1n : 1) | |
bit = bit << (this.useBigInt ? 1n : 1) | |
} | |
return result | |
} | |
toIndexArray() { | |
const result = [] | |
let tempMask = this.mask | |
let index = 0 | |
while (tempMask > (this.useBigInt ? 0n : 0)) { | |
if ( | |
(tempMask & (this.useBigInt ? 1n : 1)) !== | |
(this.useBigInt ? 0n : 0) | |
) { | |
result.push(index) | |
} | |
tempMask = tempMask >> (this.useBigInt ? 1n : 1) | |
index++ | |
} | |
return result | |
} | |
count() { | |
let count = 0 | |
let tempMask = this.mask | |
while (tempMask > (this.useBigInt ? 0n : 0)) { | |
tempMask = tempMask & (tempMask - (this.useBigInt ? 1n : 1)) | |
count++ | |
} | |
return count | |
} | |
toBinary() { | |
return this.mask.toString(2) | |
} | |
getValue() { | |
return this.mask | |
} | |
setValue(value) { | |
if (value < 0) { | |
throw new Error("Value cannot be negative") | |
} | |
this.mask = this.useBigInt ? BigInt(value) : value | |
return this | |
} | |
clone() { | |
return new BitMask(this.mask, this.useBigInt) | |
} | |
equals(other) { | |
const otherMask = this._convertToMask(other) | |
return this.mask === otherMask | |
} | |
toString() { | |
return `BitMask(${this.mask}, binary: ${this.toBinary()})` | |
} | |
toJSON() { | |
return this.useBigInt ? this.mask.toString() : this.mask | |
} | |
_validateBit(bit) { | |
if (typeof bit !== "number" || bit < 0 || !Number.isInteger(bit)) { | |
throw new Error("Bit must be a non-negative integer") | |
} | |
if (!this.useBigInt && bit > Number.MAX_SAFE_INTEGER) { | |
throw new Error( | |
"Bit value too large for regular numbers, use BigInt mode" | |
) | |
} | |
} | |
_convertToMask(other) { | |
if (other instanceof BitMask) { | |
return other.mask | |
} | |
if (typeof other === "number" || typeof other === "bigint") { | |
return this.useBigInt ? BigInt(other) : Number(other) | |
} | |
throw new Error("Invalid mask type") | |
} | |
} | |
export default BitMask |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment