66 lines
2.0 KiB
TypeScript
66 lines
2.0 KiB
TypeScript
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<number>;
|
|
|
|
if (!(set instanceof Set)) {
|
|
throw new BitpackerError(
|
|
BITPACKER_CODES.INVALID_SCHEMA,
|
|
`Expected Set<number> 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<number>();
|
|
|
|
for (let i = 0; i < size; i++) {
|
|
result.add(context.reader.readUint(bitsRequired(cfg.maxSize)));
|
|
}
|
|
|
|
return result;
|
|
}
|
|
}
|