zstdify 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +95 -0
- package/dist/bitstream/bitReader.d.ts +26 -0
- package/dist/bitstream/bitReader.js +86 -0
- package/dist/bitstream/bitReader.js.map +1 -0
- package/dist/bitstream/bitReader.test.d.ts +1 -0
- package/dist/bitstream/bitReader.test.js +47 -0
- package/dist/bitstream/bitReader.test.js.map +1 -0
- package/dist/bitstream/bitReaderReverse.d.ts +23 -0
- package/dist/bitstream/bitReaderReverse.js +84 -0
- package/dist/bitstream/bitReaderReverse.js.map +1 -0
- package/dist/bitstream/bitReaderReverse.test.d.ts +1 -0
- package/dist/bitstream/bitReaderReverse.test.js +49 -0
- package/dist/bitstream/bitReaderReverse.test.js.map +1 -0
- package/dist/bitstream/bitWriter.d.ts +15 -0
- package/dist/bitstream/bitWriter.js +47 -0
- package/dist/bitstream/bitWriter.js.map +1 -0
- package/dist/bitstream/index.d.ts +4 -0
- package/dist/bitstream/index.js +5 -0
- package/dist/bitstream/index.js.map +1 -0
- package/dist/bitstream/littleEndian.d.ts +7 -0
- package/dist/bitstream/littleEndian.js +45 -0
- package/dist/bitstream/littleEndian.js.map +1 -0
- package/dist/bitstream/littleEndian.test.d.ts +1 -0
- package/dist/bitstream/littleEndian.test.js +21 -0
- package/dist/bitstream/littleEndian.test.js.map +1 -0
- package/dist/bitstream/varint.d.ts +9 -0
- package/dist/bitstream/varint.js +40 -0
- package/dist/bitstream/varint.js.map +1 -0
- package/dist/bitstream/varint.test.d.ts +1 -0
- package/dist/bitstream/varint.test.js +25 -0
- package/dist/bitstream/varint.test.js.map +1 -0
- package/dist/compress.d.ts +9 -0
- package/dist/compress.js +74 -0
- package/dist/compress.js.map +1 -0
- package/dist/decode/block.d.ts +11 -0
- package/dist/decode/block.js +28 -0
- package/dist/decode/block.js.map +1 -0
- package/dist/decode/block.test.d.ts +1 -0
- package/dist/decode/block.test.js +26 -0
- package/dist/decode/block.test.js.map +1 -0
- package/dist/decode/decompressFrame.d.ts +9 -0
- package/dist/decode/decompressFrame.js +147 -0
- package/dist/decode/decompressFrame.js.map +1 -0
- package/dist/decode/literals.corruption.test.d.ts +1 -0
- package/dist/decode/literals.corruption.test.js +26 -0
- package/dist/decode/literals.corruption.test.js.map +1 -0
- package/dist/decode/literals.d.ts +49 -0
- package/dist/decode/literals.js +300 -0
- package/dist/decode/literals.js.map +1 -0
- package/dist/decode/literals.test.d.ts +1 -0
- package/dist/decode/literals.test.js +52 -0
- package/dist/decode/literals.test.js.map +1 -0
- package/dist/decode/reconstruct.d.ts +13 -0
- package/dist/decode/reconstruct.js +80 -0
- package/dist/decode/reconstruct.js.map +1 -0
- package/dist/decode/reconstruct.test.d.ts +1 -0
- package/dist/decode/reconstruct.test.js +42 -0
- package/dist/decode/reconstruct.test.js.map +1 -0
- package/dist/decode/sequences.corruption.test.d.ts +1 -0
- package/dist/decode/sequences.corruption.test.js +32 -0
- package/dist/decode/sequences.corruption.test.js.map +1 -0
- package/dist/decode/sequences.d.ts +21 -0
- package/dist/decode/sequences.js +222 -0
- package/dist/decode/sequences.js.map +1 -0
- package/dist/decode/sequences.level1.test.d.ts +1 -0
- package/dist/decode/sequences.level1.test.js +35 -0
- package/dist/decode/sequences.level1.test.js.map +1 -0
- package/dist/decompress.d.ts +11 -0
- package/dist/decompress.js +59 -0
- package/dist/decompress.js.map +1 -0
- package/dist/dictionary/decoderDictionary.d.ts +15 -0
- package/dist/dictionary/decoderDictionary.js +116 -0
- package/dist/dictionary/decoderDictionary.js.map +1 -0
- package/dist/dictionary/decoderDictionary.test.d.ts +1 -0
- package/dist/dictionary/decoderDictionary.test.js +87 -0
- package/dist/dictionary/decoderDictionary.test.js.map +1 -0
- package/dist/encode/blockWriter.d.ts +5 -0
- package/dist/encode/blockWriter.js +27 -0
- package/dist/encode/blockWriter.js.map +1 -0
- package/dist/encode/blockWriter.test.d.ts +1 -0
- package/dist/encode/blockWriter.test.js +31 -0
- package/dist/encode/blockWriter.test.js.map +1 -0
- package/dist/encode/compressedBlock.d.ts +3 -0
- package/dist/encode/compressedBlock.js +449 -0
- package/dist/encode/compressedBlock.js.map +1 -0
- package/dist/encode/compressedBlock.test.d.ts +1 -0
- package/dist/encode/compressedBlock.test.js +63 -0
- package/dist/encode/compressedBlock.test.js.map +1 -0
- package/dist/encode/frameWriter.d.ts +4 -0
- package/dist/encode/frameWriter.js +34 -0
- package/dist/encode/frameWriter.js.map +1 -0
- package/dist/encode/frameWriter.test.d.ts +1 -0
- package/dist/encode/frameWriter.test.js +38 -0
- package/dist/encode/frameWriter.test.js.map +1 -0
- package/dist/encode/greedySequences.d.ts +7 -0
- package/dist/encode/greedySequences.js +82 -0
- package/dist/encode/greedySequences.js.map +1 -0
- package/dist/encode/greedySequences.test.d.ts +1 -0
- package/dist/encode/greedySequences.test.js +33 -0
- package/dist/encode/greedySequences.test.js.map +1 -0
- package/dist/entropy/fse.d.ts +33 -0
- package/dist/entropy/fse.js +217 -0
- package/dist/entropy/fse.js.map +1 -0
- package/dist/entropy/fse.test.d.ts +1 -0
- package/dist/entropy/fse.test.js +41 -0
- package/dist/entropy/fse.test.js.map +1 -0
- package/dist/entropy/huffman.d.ts +24 -0
- package/dist/entropy/huffman.js +70 -0
- package/dist/entropy/huffman.js.map +1 -0
- package/dist/entropy/huffman.test.d.ts +1 -0
- package/dist/entropy/huffman.test.js +22 -0
- package/dist/entropy/huffman.test.js.map +1 -0
- package/dist/entropy/index.d.ts +6 -0
- package/dist/entropy/index.js +5 -0
- package/dist/entropy/index.js.map +1 -0
- package/dist/entropy/predefined.d.ts +10 -0
- package/dist/entropy/predefined.js +18 -0
- package/dist/entropy/predefined.js.map +1 -0
- package/dist/entropy/weights.d.ts +20 -0
- package/dist/entropy/weights.js +108 -0
- package/dist/entropy/weights.js.map +1 -0
- package/dist/entropy/weights.test.d.ts +1 -0
- package/dist/entropy/weights.test.js +38 -0
- package/dist/entropy/weights.test.js.map +1 -0
- package/dist/errors.d.ts +7 -0
- package/dist/errors.js +13 -0
- package/dist/errors.js.map +1 -0
- package/dist/errors.test.d.ts +1 -0
- package/dist/errors.test.js +16 -0
- package/dist/errors.test.js.map +1 -0
- package/dist/frame/checksum.d.ts +17 -0
- package/dist/frame/checksum.js +94 -0
- package/dist/frame/checksum.js.map +1 -0
- package/dist/frame/checksum.test.d.ts +1 -0
- package/dist/frame/checksum.test.js +28 -0
- package/dist/frame/checksum.test.js.map +1 -0
- package/dist/frame/frameHeader.d.ts +26 -0
- package/dist/frame/frameHeader.js +127 -0
- package/dist/frame/frameHeader.js.map +1 -0
- package/dist/frame/frameHeader.test.d.ts +1 -0
- package/dist/frame/frameHeader.test.js +83 -0
- package/dist/frame/frameHeader.test.js.map +1 -0
- package/dist/frame/index.d.ts +4 -0
- package/dist/frame/index.js +4 -0
- package/dist/frame/index.js.map +1 -0
- package/dist/frame/skippable.d.ts +12 -0
- package/dist/frame/skippable.js +27 -0
- package/dist/frame/skippable.js.map +1 -0
- package/dist/frame/skippable.test.d.ts +1 -0
- package/dist/frame/skippable.test.js +35 -0
- package/dist/frame/skippable.test.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/package.json +38 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Ben Houston
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# zstdify
|
|
2
|
+
|
|
3
|
+
[![NPM Package][npm]][npm-url]
|
|
4
|
+
[![NPM Downloads][npm-downloads]][npmtrends-url]
|
|
5
|
+
[![Tests][tests-badge]][tests-url]
|
|
6
|
+
[![Coverage][coverage-badge]][coverage-url]
|
|
7
|
+
|
|
8
|
+
Pure TypeScript zstd compression library. No native dependencies, works in Node.js and browsers.
|
|
9
|
+
|
|
10
|
+
## Status
|
|
11
|
+
|
|
12
|
+
- **Decoder**: Raw and RLE blocks supported. Compressed blocks (Huffman/FSE) planned.
|
|
13
|
+
- **Encoder**: Raw baseline, RLE blocks for repeated-byte chunks (`level > 0`), plus an initial compressed-block path (`level > 1`) for selected single-sequence blocks with raw fallback.
|
|
14
|
+
- **Format**: RFC 8878 compliant for supported features.
|
|
15
|
+
|
|
16
|
+
## Usage
|
|
17
|
+
|
|
18
|
+
```ts
|
|
19
|
+
import { compress, decompress } from 'zstdify';
|
|
20
|
+
|
|
21
|
+
const data = new TextEncoder().encode('hello world');
|
|
22
|
+
const compressed = compress(data);
|
|
23
|
+
const restored = decompress(compressed);
|
|
24
|
+
// restored equals data
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## API
|
|
28
|
+
|
|
29
|
+
- `compress(input: Uint8Array, options?: { level?: number }): Uint8Array`
|
|
30
|
+
- `decompress(input: Uint8Array, options?: { maxSize?: number }): Uint8Array`
|
|
31
|
+
|
|
32
|
+
## CLI Tool
|
|
33
|
+
|
|
34
|
+
The **zstdify-cli** package is a command-line tool for compressing and decompressing files with zstd. Install from npm:
|
|
35
|
+
|
|
36
|
+
```sh
|
|
37
|
+
pnpm add -g zstdify-cli
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
zstdify compress input.txt output.zst
|
|
42
|
+
zstdify extract output.zst restored.txt
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
See [packages/cli/README.md](packages/cli/README.md) for full CLI documentation.
|
|
46
|
+
|
|
47
|
+
## Development
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
pnpm install
|
|
51
|
+
pnpm build
|
|
52
|
+
pnpm test
|
|
53
|
+
pnpm check
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### How we validate
|
|
57
|
+
|
|
58
|
+
- **Round-trip**: `decompress(compress(x)) === x` for a variety of payloads and levels (see [packages/zstdify-tests/src/roundtrip/](packages/zstdify-tests/src/roundtrip/)), plus property-based tests with [fast-check](https://fast-check.dev/).
|
|
59
|
+
- **Conformance fixtures**: Pre-generated `.zst` files from the official zstd CLI; we decompress and compare. Legacy fixtures are documented in [packages/zstdify-tests/fixtures/README.md](packages/zstdify-tests/fixtures/README.md). A decodecorpus-style **corpus** (multiple sizes/levels, manifest with hashes) is generated by `pnpm --filter zstdify-tests run generate:corpus` and tested in `conformance/corpus-manifest.test.ts`.
|
|
60
|
+
- **Differential (zstd ↔ zstdify)**: With the zstd CLI installed, we test zstd compress → zstdify decompress and zstdify compress → zstd decompress across payloads and levels.
|
|
61
|
+
- **Corruption**: Truncation, checksum mismatch, invalid header bits, and related error paths are tested in [conformance/corruption.test.ts](packages/zstdify-tests/src/conformance/corruption.test.ts).
|
|
62
|
+
- **Compression regression**: `pnpm --filter zstdify-tests run regression:compression` checks that compressed sizes for fixed payloads do not increase (ratio stability).
|
|
63
|
+
- **Fuzzing**: A decompress harness (`pnpm --filter zstdify-tests run fuzz:decompress`) reads bytes from stdin and calls `decompress`; use with a fuzzer or corpus to find crashes. See [upstream zstd TESTING.md](https://github.com/facebook/zstd/blob/dev/TESTING.md) and decodecorpus for comparison.
|
|
64
|
+
|
|
65
|
+
## Publishing
|
|
66
|
+
|
|
67
|
+
Publish the npm packages (library first, then CLI so it gets the correct `zstdify` version):
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
pnpm make-release:zstdify
|
|
71
|
+
pnpm make-release:cli
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Project structure
|
|
75
|
+
|
|
76
|
+
- `packages/zstdify` - Core library
|
|
77
|
+
- `packages/cli` - CLI tool (`zstdify-cli` on npm)
|
|
78
|
+
- `packages/zstdify-tests` - Integration tests
|
|
79
|
+
|
|
80
|
+
# License
|
|
81
|
+
|
|
82
|
+
MIT
|
|
83
|
+
|
|
84
|
+
## Author
|
|
85
|
+
|
|
86
|
+
[Ben Houston](https://benhouston3d.com), Sponsored by [Land of Assets](https://landofassets.com)
|
|
87
|
+
|
|
88
|
+
[npm]: https://img.shields.io/npm/v/zstdify
|
|
89
|
+
[npm-url]: https://www.npmjs.com/package/zstdify
|
|
90
|
+
[npm-downloads]: https://img.shields.io/npm/dw/zstdify
|
|
91
|
+
[npmtrends-url]: https://www.npmtrends.com/zstdify
|
|
92
|
+
[tests-badge]: https://github.com/bhouston/zstdify/workflows/Tests/badge.svg
|
|
93
|
+
[tests-url]: https://github.com/bhouston/zstdify/actions/workflows/test.yml
|
|
94
|
+
[coverage-badge]: https://codecov.io/gh/bhouston/zstdify/branch/main/graph/badge.svg
|
|
95
|
+
[coverage-url]: https://codecov.io/gh/bhouston/zstdify
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Little-endian bit reader with bounds-safe cursor.
|
|
3
|
+
* Reads bits LSB-first within bytes, bytes in forward order.
|
|
4
|
+
*/
|
|
5
|
+
export declare class BitReader {
|
|
6
|
+
private data;
|
|
7
|
+
private byteOffset;
|
|
8
|
+
private bitOffset;
|
|
9
|
+
constructor(data: Uint8Array, byteOffset?: number);
|
|
10
|
+
/** Current byte position (after last fully consumed byte) */
|
|
11
|
+
get position(): number;
|
|
12
|
+
/** Total bits consumed */
|
|
13
|
+
get bitsConsumed(): number;
|
|
14
|
+
/** True if no more bits available */
|
|
15
|
+
get atEnd(): boolean;
|
|
16
|
+
/** Ensure at least n bits are available. Throws if not. */
|
|
17
|
+
ensure(n: number): void;
|
|
18
|
+
/** Read n bits (1-32), LSB first */
|
|
19
|
+
readBits(n: number): number;
|
|
20
|
+
/** Align to next byte boundary (skip remaining bits in current byte) */
|
|
21
|
+
align(): void;
|
|
22
|
+
/** Read a full byte (convenience, must be aligned or will read across boundary) */
|
|
23
|
+
readByte(): number;
|
|
24
|
+
/** Slice remaining bytes from current position (after aligning) */
|
|
25
|
+
readRemainingBytes(): Uint8Array;
|
|
26
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Little-endian bit reader with bounds-safe cursor.
|
|
3
|
+
* Reads bits LSB-first within bytes, bytes in forward order.
|
|
4
|
+
*/
|
|
5
|
+
export class BitReader {
|
|
6
|
+
data;
|
|
7
|
+
byteOffset;
|
|
8
|
+
bitOffset; // 0-7, bits consumed in current byte
|
|
9
|
+
constructor(data, byteOffset = 0) {
|
|
10
|
+
this.data = data;
|
|
11
|
+
this.byteOffset = byteOffset;
|
|
12
|
+
this.bitOffset = 0;
|
|
13
|
+
}
|
|
14
|
+
/** Current byte position (after last fully consumed byte) */
|
|
15
|
+
get position() {
|
|
16
|
+
return this.byteOffset;
|
|
17
|
+
}
|
|
18
|
+
/** Total bits consumed */
|
|
19
|
+
get bitsConsumed() {
|
|
20
|
+
return this.byteOffset * 8 + this.bitOffset;
|
|
21
|
+
}
|
|
22
|
+
/** True if no more bits available */
|
|
23
|
+
get atEnd() {
|
|
24
|
+
return this.byteOffset >= this.data.length;
|
|
25
|
+
}
|
|
26
|
+
/** Ensure at least n bits are available. Throws if not. */
|
|
27
|
+
ensure(n) {
|
|
28
|
+
const bitsAvailable = (this.data.length - this.byteOffset) * 8 - this.bitOffset;
|
|
29
|
+
if (bitsAvailable < n) {
|
|
30
|
+
throw new RangeError(`BitReader: requested ${n} bits, only ${bitsAvailable} available`);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
/** Read n bits (1-32), LSB first */
|
|
34
|
+
readBits(n) {
|
|
35
|
+
if (n < 1 || n > 32) {
|
|
36
|
+
throw new RangeError(`BitReader.readBits: n must be 1-32, got ${n}`);
|
|
37
|
+
}
|
|
38
|
+
this.ensure(n);
|
|
39
|
+
let value = 0;
|
|
40
|
+
let bitsLeft = n;
|
|
41
|
+
while (bitsLeft > 0) {
|
|
42
|
+
const byte = this.data[this.byteOffset] ?? 0;
|
|
43
|
+
const bitsInByte = 8 - this.bitOffset;
|
|
44
|
+
const take = Math.min(bitsLeft, bitsInByte);
|
|
45
|
+
const mask = (1 << take) - 1;
|
|
46
|
+
const shift = this.bitOffset;
|
|
47
|
+
value |= ((byte >> shift) & mask) << (n - bitsLeft);
|
|
48
|
+
this.bitOffset += take;
|
|
49
|
+
bitsLeft -= take;
|
|
50
|
+
if (this.bitOffset >= 8) {
|
|
51
|
+
this.byteOffset++;
|
|
52
|
+
this.bitOffset = 0;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return value;
|
|
56
|
+
}
|
|
57
|
+
/** Align to next byte boundary (skip remaining bits in current byte) */
|
|
58
|
+
align() {
|
|
59
|
+
if (this.bitOffset !== 0) {
|
|
60
|
+
this.bitOffset = 0;
|
|
61
|
+
this.byteOffset++;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
/** Read a full byte (convenience, must be aligned or will read across boundary) */
|
|
65
|
+
readByte() {
|
|
66
|
+
if (this.bitOffset === 0) {
|
|
67
|
+
if (this.byteOffset >= this.data.length) {
|
|
68
|
+
throw new RangeError('BitReader: no more bytes');
|
|
69
|
+
}
|
|
70
|
+
const v = this.data[this.byteOffset++];
|
|
71
|
+
if (v === undefined)
|
|
72
|
+
throw new RangeError('BitReader: no more bytes');
|
|
73
|
+
return v;
|
|
74
|
+
}
|
|
75
|
+
return this.readBits(8);
|
|
76
|
+
}
|
|
77
|
+
/** Slice remaining bytes from current position (after aligning) */
|
|
78
|
+
readRemainingBytes() {
|
|
79
|
+
this.align();
|
|
80
|
+
if (this.byteOffset >= this.data.length) {
|
|
81
|
+
return new Uint8Array(0);
|
|
82
|
+
}
|
|
83
|
+
return this.data.subarray(this.byteOffset);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
//# sourceMappingURL=bitReader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bitReader.js","sourceRoot":"","sources":["../../src/bitstream/bitReader.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { BitReader } from './bitReader.js';
|
|
3
|
+
describe('BitReader', () => {
|
|
4
|
+
it('reads single bits', () => {
|
|
5
|
+
// 0b10110100 = 0xB4
|
|
6
|
+
const data = new Uint8Array([0xb4]);
|
|
7
|
+
const r = new BitReader(data);
|
|
8
|
+
expect(r.readBits(1)).toBe(0);
|
|
9
|
+
expect(r.readBits(1)).toBe(0);
|
|
10
|
+
expect(r.readBits(1)).toBe(1);
|
|
11
|
+
expect(r.readBits(1)).toBe(0);
|
|
12
|
+
expect(r.readBits(1)).toBe(1);
|
|
13
|
+
expect(r.readBits(1)).toBe(1);
|
|
14
|
+
expect(r.readBits(1)).toBe(0);
|
|
15
|
+
expect(r.readBits(1)).toBe(1);
|
|
16
|
+
expect(r.atEnd).toBe(true);
|
|
17
|
+
});
|
|
18
|
+
it('reads multi-bit values', () => {
|
|
19
|
+
const data = new Uint8Array([0xff, 0x00]); // 11111111 00000000
|
|
20
|
+
const r = new BitReader(data);
|
|
21
|
+
expect(r.readBits(8)).toBe(0xff);
|
|
22
|
+
expect(r.readBits(8)).toBe(0x00);
|
|
23
|
+
});
|
|
24
|
+
it('reads across byte boundaries', () => {
|
|
25
|
+
// byte0: 1111 (low) 0000 (high), byte1: 1111 (low) 0000 (high)
|
|
26
|
+
const data = new Uint8Array([0b00001111, 0b00001111]);
|
|
27
|
+
const r = new BitReader(data);
|
|
28
|
+
expect(r.readBits(4)).toBe(0b1111); // low 4 of byte0
|
|
29
|
+
expect(r.readBits(8)).toBe(0b11110000); // high 4 of byte0 + low 4 of byte1
|
|
30
|
+
expect(r.readBits(4)).toBe(0); // high 4 of byte1
|
|
31
|
+
});
|
|
32
|
+
it('align works', () => {
|
|
33
|
+
const data = new Uint8Array([0xff, 0xab, 0xcd]);
|
|
34
|
+
const r = new BitReader(data);
|
|
35
|
+
r.readBits(3);
|
|
36
|
+
r.align();
|
|
37
|
+
expect(r.position).toBe(1);
|
|
38
|
+
expect(r.readByte()).toBe(0xab);
|
|
39
|
+
});
|
|
40
|
+
it('throws on out of bounds', () => {
|
|
41
|
+
const data = new Uint8Array([0xff]);
|
|
42
|
+
const r = new BitReader(data);
|
|
43
|
+
r.readBits(8);
|
|
44
|
+
expect(() => r.readBits(1)).toThrow();
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
//# sourceMappingURL=bitReader.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bitReader.test.js","sourceRoot":"","sources":["../../src/bitstream/bitReader.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reverse (backward) bit reader for FSE/Huffman streams in zstd.
|
|
3
|
+
* Modeled after zstd's reverse stream semantics:
|
|
4
|
+
* - compute an absolute bit offset from stream start,
|
|
5
|
+
* - consume bits by decrementing that offset,
|
|
6
|
+
* - extract bits in little-endian bit order.
|
|
7
|
+
*/
|
|
8
|
+
export declare class BitReaderReverse {
|
|
9
|
+
private readonly data;
|
|
10
|
+
private readonly startBit;
|
|
11
|
+
private readonly endBit;
|
|
12
|
+
private bitOffset;
|
|
13
|
+
constructor(data: Uint8Array, startByteOffset: number, lengthBytes: number, skipBitsAtStart?: number);
|
|
14
|
+
/** Read n bits (1-32), LSB first from current position (reading backward) */
|
|
15
|
+
readBits(n: number): number;
|
|
16
|
+
/** Skip trailing zero padding and end-mark bit from the stream tail. */
|
|
17
|
+
skipPadding(): void;
|
|
18
|
+
get position(): number;
|
|
19
|
+
/** Skip the first n bits at the logical start (the end of the buffer when reading backward). */
|
|
20
|
+
skipBitsAtEnd(n: number): void;
|
|
21
|
+
/** Undo a previous readBits() by pushing the cursor forward. */
|
|
22
|
+
unreadBits(n: number): void;
|
|
23
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reverse (backward) bit reader for FSE/Huffman streams in zstd.
|
|
3
|
+
* Modeled after zstd's reverse stream semantics:
|
|
4
|
+
* - compute an absolute bit offset from stream start,
|
|
5
|
+
* - consume bits by decrementing that offset,
|
|
6
|
+
* - extract bits in little-endian bit order.
|
|
7
|
+
*/
|
|
8
|
+
export class BitReaderReverse {
|
|
9
|
+
data;
|
|
10
|
+
startBit;
|
|
11
|
+
endBit;
|
|
12
|
+
bitOffset;
|
|
13
|
+
constructor(data, startByteOffset, lengthBytes, skipBitsAtStart = 0) {
|
|
14
|
+
if (lengthBytes < 0) {
|
|
15
|
+
throw new RangeError(`BitReaderReverse: negative length ${lengthBytes}`);
|
|
16
|
+
}
|
|
17
|
+
this.data = data;
|
|
18
|
+
this.startBit = startByteOffset * 8 + skipBitsAtStart;
|
|
19
|
+
this.endBit = (startByteOffset + lengthBytes) * 8;
|
|
20
|
+
this.bitOffset = this.endBit;
|
|
21
|
+
}
|
|
22
|
+
/** Read n bits (1-32), LSB first from current position (reading backward) */
|
|
23
|
+
readBits(n) {
|
|
24
|
+
if (n < 1 || n > 32) {
|
|
25
|
+
throw new RangeError(`BitReaderReverse.readBits: n must be 1-32, got ${n}`);
|
|
26
|
+
}
|
|
27
|
+
const requestedStart = this.bitOffset - n;
|
|
28
|
+
this.bitOffset = Math.max(this.startBit, requestedStart);
|
|
29
|
+
let value = 0;
|
|
30
|
+
for (let i = 0; i < n; i++) {
|
|
31
|
+
const absoluteBit = requestedStart + i;
|
|
32
|
+
if (absoluteBit < this.startBit) {
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
const byteIndex = absoluteBit >>> 3;
|
|
36
|
+
const bitInByte = absoluteBit & 7;
|
|
37
|
+
const bit = ((this.data[byteIndex] ?? 0) >>> bitInByte) & 1;
|
|
38
|
+
value |= bit << i;
|
|
39
|
+
}
|
|
40
|
+
return value;
|
|
41
|
+
}
|
|
42
|
+
/** Skip trailing zero padding and end-mark bit from the stream tail. */
|
|
43
|
+
skipPadding() {
|
|
44
|
+
if (this.endBit <= this.startBit) {
|
|
45
|
+
throw new RangeError('BitReaderReverse: empty stream');
|
|
46
|
+
}
|
|
47
|
+
const lastByteIndex = (this.endBit >>> 3) - 1;
|
|
48
|
+
const lastByte = this.data[lastByteIndex] ?? 0;
|
|
49
|
+
if (lastByte === 0) {
|
|
50
|
+
throw new RangeError('BitReaderReverse: invalid end marker');
|
|
51
|
+
}
|
|
52
|
+
const highestSetBit = 31 - Math.clz32(lastByte);
|
|
53
|
+
const paddingBits = 8 - highestSetBit; // includes end-mark + zero padding bits.
|
|
54
|
+
this.bitOffset = this.endBit - paddingBits;
|
|
55
|
+
if (this.bitOffset < this.startBit) {
|
|
56
|
+
throw new RangeError('BitReaderReverse: invalid padding');
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
get position() {
|
|
60
|
+
if (this.bitOffset <= this.startBit) {
|
|
61
|
+
return this.startBit >>> 3;
|
|
62
|
+
}
|
|
63
|
+
return (this.bitOffset - 1) >>> 3;
|
|
64
|
+
}
|
|
65
|
+
/** Skip the first n bits at the logical start (the end of the buffer when reading backward). */
|
|
66
|
+
skipBitsAtEnd(n) {
|
|
67
|
+
if (n <= 0)
|
|
68
|
+
return;
|
|
69
|
+
this.bitOffset -= n;
|
|
70
|
+
if (this.bitOffset < this.startBit) {
|
|
71
|
+
throw new RangeError('BitReaderReverse: buffer underflow');
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
/** Undo a previous readBits() by pushing the cursor forward. */
|
|
75
|
+
unreadBits(n) {
|
|
76
|
+
if (n <= 0)
|
|
77
|
+
return;
|
|
78
|
+
this.bitOffset += n;
|
|
79
|
+
if (this.bitOffset > this.endBit) {
|
|
80
|
+
throw new RangeError('BitReaderReverse: unread overflow');
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=bitReaderReverse.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bitReaderReverse.js","sourceRoot":"","sources":["../../src/bitstream/bitReaderReverse.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { BitReaderReverse } from './bitReaderReverse.js';
|
|
3
|
+
describe('BitReaderReverse', () => {
|
|
4
|
+
it('supports unreadBits to rollback over-read', () => {
|
|
5
|
+
const reader = new BitReaderReverse(new Uint8Array([0xa0, 0x01]), 0, 2);
|
|
6
|
+
reader.skipPadding();
|
|
7
|
+
const first = reader.readBits(2);
|
|
8
|
+
expect(first).toBe(2);
|
|
9
|
+
reader.unreadBits(1);
|
|
10
|
+
expect(reader.readBits(1)).toBe(0);
|
|
11
|
+
});
|
|
12
|
+
it('zero-fills when reading past stream start', () => {
|
|
13
|
+
const reader = new BitReaderReverse(new Uint8Array([0x80]), 0, 1);
|
|
14
|
+
reader.skipPadding();
|
|
15
|
+
// No payload bits remain after padding; reading still succeeds with zeros.
|
|
16
|
+
expect(reader.readBits(4)).toBe(0);
|
|
17
|
+
});
|
|
18
|
+
it('position returns start byte when bitOffset <= startBit', () => {
|
|
19
|
+
const reader = new BitReaderReverse(new Uint8Array([0x80]), 0, 1);
|
|
20
|
+
reader.skipPadding();
|
|
21
|
+
reader.readBits(4); // consume remaining (zeros)
|
|
22
|
+
expect(reader.position).toBe(0);
|
|
23
|
+
});
|
|
24
|
+
it('position returns correct byte after reading bits', () => {
|
|
25
|
+
const reader = new BitReaderReverse(new Uint8Array([0xa0, 0x01]), 0, 2);
|
|
26
|
+
reader.skipPadding();
|
|
27
|
+
reader.readBits(2);
|
|
28
|
+
expect(reader.position).toBeGreaterThanOrEqual(0);
|
|
29
|
+
expect(reader.position).toBeLessThanOrEqual(2);
|
|
30
|
+
});
|
|
31
|
+
it('skipBitsAtEnd advances logical position', () => {
|
|
32
|
+
const reader = new BitReaderReverse(new Uint8Array([0x80]), 0, 1);
|
|
33
|
+
reader.skipPadding();
|
|
34
|
+
reader.skipBitsAtEnd(1);
|
|
35
|
+
expect(reader.readBits(1)).toBe(0);
|
|
36
|
+
});
|
|
37
|
+
it('skipBitsAtEnd throws on buffer underflow', () => {
|
|
38
|
+
const reader = new BitReaderReverse(new Uint8Array([0x80]), 0, 1);
|
|
39
|
+
reader.skipPadding();
|
|
40
|
+
expect(() => reader.skipBitsAtEnd(100)).toThrow(/underflow|RangeError/i);
|
|
41
|
+
});
|
|
42
|
+
it('unreadBits throws on overflow', () => {
|
|
43
|
+
const reader = new BitReaderReverse(new Uint8Array([0xa0, 0x01]), 0, 2);
|
|
44
|
+
reader.skipPadding();
|
|
45
|
+
reader.readBits(2);
|
|
46
|
+
expect(() => reader.unreadBits(20)).toThrow(/overflow|RangeError/i);
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
//# sourceMappingURL=bitReaderReverse.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bitReaderReverse.test.js","sourceRoot":"","sources":["../../src/bitstream/bitReaderReverse.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Little-endian bit writer.
|
|
3
|
+
* Writes bits LSB-first within bytes.
|
|
4
|
+
*/
|
|
5
|
+
export declare class BitWriter {
|
|
6
|
+
private chunks;
|
|
7
|
+
private currentByte;
|
|
8
|
+
private bitOffset;
|
|
9
|
+
/** Write n bits (1-32), LSB first */
|
|
10
|
+
writeBits(n: number, bits: number): void;
|
|
11
|
+
/** Flush remaining bits to output. Call when done writing. */
|
|
12
|
+
flush(): Uint8Array;
|
|
13
|
+
/** Reset writer for reuse */
|
|
14
|
+
reset(): void;
|
|
15
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Little-endian bit writer.
|
|
3
|
+
* Writes bits LSB-first within bytes.
|
|
4
|
+
*/
|
|
5
|
+
export class BitWriter {
|
|
6
|
+
chunks = [];
|
|
7
|
+
currentByte = 0;
|
|
8
|
+
bitOffset = 0; // 0-7, bits written in current byte
|
|
9
|
+
/** Write n bits (1-32), LSB first */
|
|
10
|
+
writeBits(n, bits) {
|
|
11
|
+
if (n < 1 || n > 32) {
|
|
12
|
+
throw new RangeError(`BitWriter.writeBits: n must be 1-32, got ${n}`);
|
|
13
|
+
}
|
|
14
|
+
const mask = n === 32 ? 0xffff_ffff : (1 << n) - 1;
|
|
15
|
+
let value = (bits >>> 0) & mask;
|
|
16
|
+
let bitsLeft = n;
|
|
17
|
+
while (bitsLeft > 0) {
|
|
18
|
+
const spaceInByte = 8 - this.bitOffset;
|
|
19
|
+
const take = Math.min(bitsLeft, spaceInByte);
|
|
20
|
+
const maskTake = (1 << take) - 1;
|
|
21
|
+
this.currentByte |= (value & maskTake) << this.bitOffset;
|
|
22
|
+
this.bitOffset += take;
|
|
23
|
+
bitsLeft -= take;
|
|
24
|
+
value >>= take;
|
|
25
|
+
if (this.bitOffset >= 8) {
|
|
26
|
+
this.chunks.push(this.currentByte & 0xff);
|
|
27
|
+
this.currentByte = 0;
|
|
28
|
+
this.bitOffset = 0;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
/** Flush remaining bits to output. Call when done writing. */
|
|
33
|
+
flush() {
|
|
34
|
+
const result = [...this.chunks];
|
|
35
|
+
if (this.bitOffset > 0) {
|
|
36
|
+
result.push(this.currentByte & 0xff);
|
|
37
|
+
}
|
|
38
|
+
return new Uint8Array(result);
|
|
39
|
+
}
|
|
40
|
+
/** Reset writer for reuse */
|
|
41
|
+
reset() {
|
|
42
|
+
this.chunks = [];
|
|
43
|
+
this.currentByte = 0;
|
|
44
|
+
this.bitOffset = 0;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=bitWriter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bitWriter.js","sourceRoot":"","sources":["../../src/bitstream/bitWriter.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,OAAO,SAAS;IACZ,MAAM,GAAa,EAAE,CAAC;IACtB,WAAW,GAAG,CAAC,CAAC;IAChB,SAAS,GAAG,CAAC,CAAC,CAAC,oCAAoC;IAE3D,qCAAqC;IACrC,SAAS,CAAC,CAAS,EAAE,IAAY,EAAQ;QACvC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC;YACpB,MAAM,IAAI,UAAU,CAAC,4CAA4C,CAAC,EAAE,CAAC,CAAC;QACxE,CAAC;QACD,MAAM,IAAI,GAAG,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC;QACnD,IAAI,KAAK,GAAG,CAAC,IAAI,KAAK,CAAC,CAAC,GAAG,IAAI,CAAC;QAChC,IAAI,QAAQ,GAAG,CAAC,CAAC;QAEjB,OAAO,QAAQ,GAAG,CAAC,EAAE,CAAC;YACpB,MAAM,WAAW,GAAG,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC;YACvC,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,WAAW,CAAC,CAAC;YAC7C,MAAM,QAAQ,GAAG,CAAC,CAAC,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC;YACjC,IAAI,CAAC,WAAW,IAAI,CAAC,KAAK,GAAG,QAAQ,CAAC,IAAI,IAAI,CAAC,SAAS,CAAC;YAEzD,IAAI,CAAC,SAAS,IAAI,IAAI,CAAC;YACvB,QAAQ,IAAI,IAAI,CAAC;YACjB,KAAK,KAAK,IAAI,CAAC;YAEf,IAAI,IAAI,CAAC,SAAS,IAAI,CAAC,EAAE,CAAC;gBACxB,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;gBAC1C,IAAI,CAAC,WAAW,GAAG,CAAC,CAAC;gBACrB,IAAI,CAAC,SAAS,GAAG,CAAC,CAAC;YACrB,CAAC;QACH,CAAC;IAAA,CACF;IAED,8DAA8D;IAC9D,KAAK,GAAe;QAClB,MAAM,MAAM,GAAa,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC;QAC1C,IAAI,IAAI,CAAC,SAAS,GAAG,CAAC,EAAE,CAAC;YACvB,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;QACvC,CAAC;QACD,OAAO,IAAI,UAAU,CAAC,MAAM,CAAC,CAAC;IAAA,CAC/B;IAED,6BAA6B;IAC7B,KAAK,GAAS;QACZ,IAAI,CAAC,MAAM,GAAG,EAAE,CAAC;QACjB,IAAI,CAAC,WAAW,GAAG,CAAC,CAAC;QACrB,IAAI,CAAC,SAAS,GAAG,CAAC,CAAC;IAAA,CACpB;CACF"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/bitstream/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AACpE,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Little-endian read helpers for Uint8Array.
|
|
3
|
+
* All reads are bounds-checked.
|
|
4
|
+
*/
|
|
5
|
+
export declare function readU16LE(data: Uint8Array, offset: number): number;
|
|
6
|
+
export declare function readU32LE(data: Uint8Array, offset: number): number;
|
|
7
|
+
export declare function readU64LE(data: Uint8Array, offset: number): bigint;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Little-endian read helpers for Uint8Array.
|
|
3
|
+
* All reads are bounds-checked.
|
|
4
|
+
*/
|
|
5
|
+
export function readU16LE(data, offset) {
|
|
6
|
+
if (offset + 2 > data.length) {
|
|
7
|
+
throw new RangeError(`readU16LE: offset ${offset} + 2 exceeds length ${data.length}`);
|
|
8
|
+
}
|
|
9
|
+
const a = data[offset];
|
|
10
|
+
const b = data[offset + 1];
|
|
11
|
+
if (a === undefined || b === undefined)
|
|
12
|
+
throw new Error('unreachable');
|
|
13
|
+
return a | (b << 8);
|
|
14
|
+
}
|
|
15
|
+
export function readU32LE(data, offset) {
|
|
16
|
+
if (offset + 4 > data.length) {
|
|
17
|
+
throw new RangeError(`readU32LE: offset ${offset} + 4 exceeds length ${data.length}`);
|
|
18
|
+
}
|
|
19
|
+
const a = data[offset];
|
|
20
|
+
const b = data[offset + 1];
|
|
21
|
+
const c = data[offset + 2];
|
|
22
|
+
const d = data[offset + 3];
|
|
23
|
+
if (a === undefined || b === undefined || c === undefined || d === undefined)
|
|
24
|
+
throw new Error('unreachable');
|
|
25
|
+
return (a | (b << 8) | (c << 16) | (d << 24)) >>> 0;
|
|
26
|
+
}
|
|
27
|
+
export function readU64LE(data, offset) {
|
|
28
|
+
if (offset + 8 > data.length) {
|
|
29
|
+
throw new RangeError(`readU64LE: offset ${offset} + 8 exceeds length ${data.length}`);
|
|
30
|
+
}
|
|
31
|
+
const b0 = data[offset];
|
|
32
|
+
const b1 = data[offset + 1];
|
|
33
|
+
const b2 = data[offset + 2];
|
|
34
|
+
const b3 = data[offset + 3];
|
|
35
|
+
const b4 = data[offset + 4];
|
|
36
|
+
const b5 = data[offset + 5];
|
|
37
|
+
const b6 = data[offset + 6];
|
|
38
|
+
const b7 = data[offset + 7];
|
|
39
|
+
if ([b0, b1, b2, b3, b4, b5, b6, b7].some((x) => x === undefined))
|
|
40
|
+
throw new Error('unreachable');
|
|
41
|
+
const lo = (b0 | (b1 << 8) | (b2 << 16) | (b3 << 24)) >>> 0;
|
|
42
|
+
const hi = (b4 | (b5 << 8) | (b6 << 16) | (b7 << 24)) >>> 0;
|
|
43
|
+
return BigInt(lo) | (BigInt(hi) << 32n);
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=littleEndian.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"littleEndian.js","sourceRoot":"","sources":["../../src/bitstream/littleEndian.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { readU16LE, readU32LE, readU64LE } from './littleEndian.js';
|
|
3
|
+
describe('littleEndian', () => {
|
|
4
|
+
it('readU16LE', () => {
|
|
5
|
+
const data = new Uint8Array([0x34, 0x12]); // 0x1234
|
|
6
|
+
expect(readU16LE(data, 0)).toBe(0x1234);
|
|
7
|
+
});
|
|
8
|
+
it('readU32LE', () => {
|
|
9
|
+
const data = new Uint8Array([0x78, 0x56, 0x34, 0x12]);
|
|
10
|
+
expect(readU32LE(data, 0)).toBe(0x12345678);
|
|
11
|
+
});
|
|
12
|
+
it('readU64LE', () => {
|
|
13
|
+
const data = new Uint8Array([0xef, 0xcd, 0xab, 0x89, 0x67, 0x45, 0x23, 0x01]);
|
|
14
|
+
expect(readU64LE(data, 0)).toBe(0x0123456789abcdefn);
|
|
15
|
+
});
|
|
16
|
+
it('throws on out of bounds', () => {
|
|
17
|
+
const data = new Uint8Array([1, 2, 3]);
|
|
18
|
+
expect(() => readU32LE(data, 0)).toThrow();
|
|
19
|
+
});
|
|
20
|
+
});
|
|
21
|
+
//# sourceMappingURL=littleEndian.test.js.map
|