harmony/src/utils/bitfield.ts

119 lines
3.2 KiB
TypeScript

// Ported from https://github.com/discordjs/discord.js/blob/master/src/util/BitField.js
export type BitFieldResolvable =
| number
| number[]
| BitField
| string
| string[]
| BitField[]
| bigint
| Array<bigint>
/** Bit Field utility to work with Bits and Flags */
export class BitField {
#flags: { [name: string]: number | bigint } = {}
bitfield: bigint
constructor(
flags: { [name: string]: number | bigint },
bits: BitFieldResolvable
) {
this.#flags = flags
this.bitfield = BitField.resolve(this.#flags, bits)
}
any(bit: BitFieldResolvable): boolean {
return (this.bitfield & BitField.resolve(this.#flags, bit)) !== 0n
}
equals(bit: BitFieldResolvable): boolean {
return this.bitfield === BitField.resolve(this.#flags, bit)
}
has(bit: BitFieldResolvable, ...args: any[]): boolean {
if (Array.isArray(bit)) return (bit.every as any)((p: any) => this.has(p))
bit = BitField.resolve(this.#flags, bit)
return (this.bitfield & bit) === bit
}
missing(bits: any, ...hasParams: any[]): string[] {
if (!Array.isArray(bits))
bits = new BitField(this.#flags, bits).toArray(false)
return bits.filter((p: any) => !this.has(p, ...hasParams))
}
freeze(): Readonly<BitField> {
return Object.freeze(this)
}
add(...bits: BitFieldResolvable[]): BitField {
let total = 0n
for (const bit of bits) {
total |= BitField.resolve(this.#flags, bit)
}
if (Object.isFrozen(this))
return new BitField(this.#flags, this.bitfield | total)
this.bitfield |= total
return this
}
remove(...bits: BitFieldResolvable[]): BitField {
let total = 0n
for (const bit of bits) {
total |= BitField.resolve(this.#flags, bit)
}
if (Object.isFrozen(this))
return new BitField(this.#flags, this.bitfield & ~total)
this.bitfield &= ~total
return this
}
flags(): { [name: string]: bigint | number } {
return this.#flags
}
serialize(...hasParams: any[]): { [key: string]: boolean } {
const serialized: { [key: string]: boolean } = {}
for (const [flag, bit] of Object.entries(this.#flags))
serialized[flag] = this.has(
BitField.resolve(this.#flags, bit),
...hasParams
)
return serialized
}
toArray(...hasParams: any[]): string[] {
return Object.keys(this.#flags).filter((bit) =>
this.has(BitField.resolve(this.#flags, bit), ...hasParams)
)
}
toJSON(): string {
return this.bitfield.toString()
}
valueOf(): bigint {
return this.bitfield
}
*[Symbol.iterator](): Iterator<string> {
yield* this.toArray()
}
static resolve(flags: any, bit: BitFieldResolvable = 0n): bigint {
if (typeof bit === 'bigint') return bit
if (typeof bit === 'string' && !isNaN(parseInt(bit))) return BigInt(bit)
if (typeof bit === 'number' && bit >= 0) return BigInt(bit)
if (bit instanceof BitField) return this.resolve(flags, bit.bitfield)
if (Array.isArray(bit))
return (bit.map as any)((p: any) => this.resolve(flags, p)).reduce(
(prev: bigint, p: bigint) => prev | p,
0n
)
if (typeof bit === 'string' && typeof flags[bit] !== 'undefined')
return BigInt(flags[bit])
const error = new RangeError('BITFIELD_INVALID')
throw error
}
}