first commit
This commit is contained in:
@@ -0,0 +1,90 @@
|
||||
# `@suaveplan/license` — codec/bitpack
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
|
||||
## Purpose
|
||||
|
||||
Bit-precise pack/unpack primitives used to serialise the 72-bit license
|
||||
payload into 9 bytes. Plain `DataView` / typed-array slicing can only
|
||||
write whole bytes, so we walk the buffer bit-by-bit. Two thin classes
|
||||
hold the cursor: `SimpleBitPacker` writes MSB-first, `SimpleBitUnpacker`
|
||||
reads MSB-first. Both round-trip identically to upstream's
|
||||
`@suaveplan/license` bitpacker.
|
||||
|
||||
## Features
|
||||
|
||||
- `packUint(value, bits)` — writes an unsigned integer in `[0, 2^bits)`
|
||||
using `bits` bits; raises `LicenseError(LICENSE.PAYLOAD_OUT_OF_RANGE)`
|
||||
when the value exceeds the declared width or is negative.
|
||||
- `unpackUint(bits)` — reads the next `bits`-wide unsigned integer;
|
||||
raises `LicenseError(LICENSE.MALFORMED)` when the read would underflow
|
||||
the buffer.
|
||||
- `getBytes()` / `getBitCount()` / `getBitPosition()` introspection
|
||||
helpers used by the spec-conformance tests.
|
||||
|
||||
## Usage: Basic
|
||||
|
||||
```ts
|
||||
import {
|
||||
SimpleBitPacker,
|
||||
SimpleBitUnpacker,
|
||||
} from "@suaveplan/license/codec/bitpack/bitpack";
|
||||
|
||||
const p = new SimpleBitPacker();
|
||||
p.packUint(2, 3); // 3-bit version
|
||||
p.packUint(17, 5); // 5-bit signingKeyId
|
||||
const bytes = p.getBytes();
|
||||
|
||||
const u = new SimpleBitUnpacker(bytes);
|
||||
const version = u.unpackUint(3); // 2
|
||||
const keyId = u.unpackUint(5); // 17
|
||||
```
|
||||
|
||||
## Usage: Advanced
|
||||
|
||||
Round-tripping a full 72-bit payload:
|
||||
|
||||
```ts
|
||||
import { FIELD_BITS } from "@suaveplan/license/constants/constants";
|
||||
import {
|
||||
SimpleBitPacker,
|
||||
SimpleBitUnpacker,
|
||||
} from "@suaveplan/license/codec/bitpack/bitpack";
|
||||
|
||||
const p = new SimpleBitPacker();
|
||||
for (const [_field, bits] of Object.entries(FIELD_BITS)) {
|
||||
p.packUint(0, bits);
|
||||
}
|
||||
const bytes = p.getBytes(); // 9 bytes
|
||||
|
||||
const u = new SimpleBitUnpacker(bytes);
|
||||
for (const [_field, bits] of Object.entries(FIELD_BITS)) {
|
||||
u.unpackUint(bits); // === 0
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `class SimpleBitPacker`
|
||||
|
||||
- `packUint(value: number, bits: number): void` — write an unsigned
|
||||
integer; throws on overflow / negative.
|
||||
- `getBytes(): Uint8Array` — snapshot the packed bytes.
|
||||
- `getBitCount(): number` — total bits written.
|
||||
|
||||
### `class SimpleBitUnpacker`
|
||||
|
||||
- `constructor(buffer: Uint8Array)` — wrap a byte buffer.
|
||||
- `unpackUint(bits: number): number` — read the next integer; throws
|
||||
`MALFORMED` on underflow.
|
||||
- `getBitPosition(): number` — current read cursor.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
The packer grows its internal `number[]` buffer one byte at a time
|
||||
because the upstream layout is small (9 bytes) and pre-sizing would
|
||||
require knowing the total bit count in advance. The `1 << bits` overflow
|
||||
check is safe for the bit widths used by the license schema (≤19 bits);
|
||||
for wider integers, callers should use a dedicated bigint bitpacker.
|
||||
Reference in New Issue
Block a user