64 lines
1.8 KiB
TypeScript
64 lines
1.8 KiB
TypeScript
import type {
|
|
BitPackerSchema,
|
|
PackingContext,
|
|
PackingStrategy,
|
|
} from "@suaveplan/bitpacker";
|
|
import {
|
|
BITPACKER_CODES,
|
|
BitpackerError,
|
|
isEnumSchema,
|
|
} from "@suaveplan/bitpacker";
|
|
|
|
function bitsRequired(maxValue: number): number {
|
|
if (maxValue <= 0) return 0;
|
|
return Math.floor(Math.log2(maxValue)) + 1;
|
|
}
|
|
|
|
export class EnumPackingStrategy implements PackingStrategy {
|
|
canHandle(schema: BitPackerSchema): boolean {
|
|
return isEnumSchema(schema);
|
|
}
|
|
|
|
pack(
|
|
data: unknown,
|
|
schema: BitPackerSchema,
|
|
context: PackingContext
|
|
): void {
|
|
const { _enum: values } = schema as { _enum: readonly string[] };
|
|
|
|
if (typeof data !== "string") {
|
|
throw new BitpackerError(
|
|
BITPACKER_CODES.INVALID_SCHEMA,
|
|
`Enum value must be a string, got ${typeof data}`,
|
|
{ context: { data, values } }
|
|
);
|
|
}
|
|
|
|
const index = values.indexOf(data);
|
|
if (index === -1) {
|
|
throw new BitpackerError(
|
|
BITPACKER_CODES.VALUE_OUT_OF_RANGE,
|
|
`Enum value "${data}" not in [${values.join(", ")}]`,
|
|
{ context: { data, values } }
|
|
);
|
|
}
|
|
|
|
context.writer.writeUint(index, bitsRequired(values.length - 1));
|
|
}
|
|
|
|
unpack(schema: BitPackerSchema, context: PackingContext): unknown {
|
|
const { _enum: values } = schema as { _enum: readonly string[] };
|
|
const index = context.reader.readUint(bitsRequired(values.length - 1));
|
|
|
|
if (index >= values.length) {
|
|
throw new BitpackerError(
|
|
BITPACKER_CODES.UNDERFLOW,
|
|
`Enum index ${index} out of range (max ${values.length - 1})`,
|
|
{ context: { index, values } }
|
|
);
|
|
}
|
|
|
|
return values[index];
|
|
}
|
|
}
|