https://github.com/dlenroc/binary-decoder.js
Incremental binary data parser powered by generators.
https://github.com/dlenroc/binary-decoder.js
Last synced: 4 months ago
JSON representation
Incremental binary data parser powered by generators.
- Host: GitHub
- URL: https://github.com/dlenroc/binary-decoder.js
- Owner: dlenroc
- License: mit
- Created: 2024-09-04T20:04:46.000Z (almost 2 years ago)
- Default Branch: main
- Last Pushed: 2025-01-02T10:21:46.000Z (over 1 year ago)
- Last Synced: 2025-03-25T10:01:56.083Z (about 1 year ago)
- Language: TypeScript
- Homepage:
- Size: 24.4 KB
- Stars: 0
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# @dlenroc/binary-decoder · [](https://www.npmjs.com/package/@dlenroc/binary-decoder)
A library for implementing incremental binary data parsers using generators.
## Installation
```sh
npm install @dlenroc/binary-decoder
```
## Usage
The `BinaryDecoder` class enables streaming binary decoding using a generator
function. It leverages `yield` to read bytes, return unused data, and produce
the decoded result.
```ts
import { BinaryDecoder } from '@dlenroc/binary-decoder';
const decoder = new BinaryDecoder(function* () {
// ✨ N ≥ 0 | Read exactly N bytes.
const fixedLengthBytes: Uint8Array = yield 2;
// ✨ N < 0 | Read at most |N| bytes, at least 1 byte.
const variableLengthBytes: Uint8Array = yield -2;
// ✨ ArrayBufferView | Pushback bytes to the internal buffer.
yield Uint8Array.of(40, 50);
// ✨ Other | Enqueue parsed data.
yield { fixedLengthBytes, variableLengthBytes, extraBytes: yield -Infinity };
});
console.log([...decoder.decode(Uint8Array.of(10, 20, 30))]);
// [
// {
// fixedLengthBytes: Uint8Array(2) [ 10, 20 ],
// variableLengthBytes: Uint8Array(1) [ 30 ],
// extraBytes: Uint8Array(2) [ 40, 50 ]
// }
// ]
```
## Examples
### Stateful Parsing
Suppose we need to parse a simple protocol defined as follows:
| No. of bytes | Type [Value] | Description |
| ------------ | ------------ | ----------- |
| 4 | U32 | length |
| length | U8 array | text |
```ts
import type { Decoder } from '@dlenroc/binary-decoder';
import { BinaryDecoder, getUint32 } from '@dlenroc/binary-decoder';
function* parse(): Decoder {
while (true) {
const length = yield* getUint32();
const bytes = yield length;
yield new TextDecoder().decode(bytes);
}
}
const decoder = new BinaryDecoder(parse);
// create a message
const textBytes = new TextEncoder().encode('Hello, World!');
const chunk = new Uint8Array([...new Uint8Array(4), ...textBytes]);
new DataView(chunk.buffer).setUint32(0, textBytes.byteLength);
// [1] Parse a message
console.log([...decoder.decode(chunk)]);
// [ 'Hello, World!' ]
// [2] Parse multiple messages
console.log([...decoder.decode(Uint8Array.of(...chunk, ...chunk))]);
// [ 'Hello, World!', 'Hello, World!' ]
// [3] Parse a message split across chunks
console.log([...decoder.decode(chunk.subarray(0, 3))]);
// []
console.log([...decoder.decode(chunk.subarray(3, 5))]);
// []
console.log([...decoder.decode(chunk.subarray(5))]);
// [ 'Hello, World!' ]
```
### Handling Large Messages
Update the decoder to stream decoded chunks directly, avoiding buffering.
```ts
import { getUint32, streamBytes } from '@dlenroc/binary-decoder';
function* parse(): Decoder {
while (true) {
const length = yield* getUint32();
const stream = new TextDecoderStream();
const writer = stream.writable.getWriter();
try {
yield stream;
yield* streamBytes(length, (chunk) => writer.write(chunk));
} finally {
writer.close();
}
}
}
// ... 👀 See previous example
// [3] Parse a message split across chunks (streaming output)
console.log([...decoder.decode(chunk.subarray(0, 3))]);
// []
console.log([...decoder.decode(chunk.subarray(3, 5))]);
// [ TextDecoderStream { ... } ]
console.log([...decoder.decode(chunk.subarray(5))]);
// []
```