import type { BitPackerSchema, PackingContext, PackingStrategy, } from "@suaveplan/bitpacker"; import { BITPACKER_CODES, BitpackerError } from "@suaveplan/bitpacker"; function bitsRequired(maxValue: number): number { if (maxValue <= 0) return 0; return Math.floor(Math.log2(maxValue)) + 1; } /** * Bitset schema: `{ _bitset: { maxSize: number } }`. */ export class BitsetPackingStrategy implements PackingStrategy { canHandle(schema: BitPackerSchema): boolean { return ( typeof schema === "object" && schema !== null && "_bitset" in schema ); } pack( data: unknown, schema: BitPackerSchema, context: PackingContext ): void { const cfg = (schema as { _bitset: { maxSize: number } })._bitset; const set = data as Set; if (!(set instanceof Set)) { throw new BitpackerError( BITPACKER_CODES.INVALID_SCHEMA, `Expected Set for bitset schema, got ${typeof data}`, { context: { data } } ); } if (set.size > cfg.maxSize) { throw new BitpackerError( BITPACKER_CODES.VALUE_OUT_OF_RANGE, `Bitset size ${set.size} exceeds max ${cfg.maxSize}`, { context: { size: set.size, maxSize: cfg.maxSize } } ); } context.writer.writeUint(set.size, bitsRequired(cfg.maxSize)); for (const index of set) { context.writer.writeUint(index, bitsRequired(cfg.maxSize)); } } unpack(schema: BitPackerSchema, context: PackingContext): unknown { const cfg = (schema as { _bitset: { maxSize: number } })._bitset; const size = context.reader.readUint(bitsRequired(cfg.maxSize)); const result = new Set(); for (let i = 0; i < size; i++) { result.add(context.reader.readUint(bitsRequired(cfg.maxSize))); } return result; } }