72 lines
2.0 KiB
TypeScript
72 lines
2.0 KiB
TypeScript
import type {
|
|
BitPackerSchema,
|
|
PackingContext,
|
|
PackingStrategy,
|
|
} from "@suaveplan/bitpacker";
|
|
import { BITPACKER_CODES, BitpackerError } from "@suaveplan/bitpacker";
|
|
|
|
/**
|
|
* Variable-length integer encoding (similar to protobuf varint).
|
|
* Schema token: `"varint"` for unsigned, `"-varint"` for signed (zigzag).
|
|
*/
|
|
export class VarIntPackingStrategy implements PackingStrategy {
|
|
canHandle(schema: BitPackerSchema): boolean {
|
|
return schema === "varint" || schema === "-varint";
|
|
}
|
|
|
|
pack(
|
|
data: unknown,
|
|
schema: BitPackerSchema,
|
|
context: PackingContext
|
|
): void {
|
|
if (typeof data !== "number" || !Number.isInteger(data)) {
|
|
throw new BitpackerError(
|
|
BITPACKER_CODES.INVALID_SCHEMA,
|
|
`Expected integer for varint schema, got ${typeof data}`,
|
|
{ context: { schema, data } }
|
|
);
|
|
}
|
|
|
|
const signed = schema === "-varint";
|
|
let value = signed ? this.#zigzagEncode(data) : data;
|
|
|
|
if (value < 0) {
|
|
throw new BitpackerError(
|
|
BITPACKER_CODES.INVALID_SCHEMA,
|
|
`Unsigned varint cannot be negative: ${value}`,
|
|
{ context: { value } }
|
|
);
|
|
}
|
|
|
|
do {
|
|
let byte = value & 0x7f;
|
|
value >>>= 7;
|
|
if (value !== 0) byte |= 0x80;
|
|
context.writer.writeUint(byte, 8);
|
|
} while (value !== 0);
|
|
}
|
|
|
|
unpack(schema: BitPackerSchema, context: PackingContext): unknown {
|
|
const signed = schema === "-varint";
|
|
let value = 0;
|
|
let shift = 0;
|
|
let byte: number;
|
|
|
|
do {
|
|
byte = context.reader.readUint(8);
|
|
value |= (byte & 0x7f) << shift;
|
|
shift += 7;
|
|
} while ((byte & 0x80) !== 0);
|
|
|
|
return signed ? this.#zigzagDecode(value) : value;
|
|
}
|
|
|
|
#zigzagEncode(n: number): number {
|
|
return (n << 1) ^ (n >> 31);
|
|
}
|
|
|
|
#zigzagDecode(n: number): number {
|
|
return (n >>> 1) ^ -(n & 1);
|
|
}
|
|
}
|