binutils64 0.1.3 → 0.1.4
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/.claude/agents/binutils-reviewer.md +58 -0
- package/.claude/settings.local.json +21 -0
- package/.claude/skills/add-binary-type/SKILL.md +93 -0
- package/.claude/skills/test-binutils/SKILL.md +91 -0
- package/.github/workflows/test.yml +22 -0
- package/CLAUDE.md +54 -0
- package/README.md +256 -163
- package/binutils.js +7 -5
- package/package.json +4 -1
- package/test/edge-cases.test.js +76 -0
- package/test/reader.test.js +106 -0
- package/test/readme-examples.test.js +33 -0
- package/test/roundtrip.test.js +120 -0
- package/test/writer.test.js +115 -0
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: binutils-reviewer
|
|
3
|
+
description: Reviews changes to the binutils64 library (binutils.js, README.md, tests) for binary-correctness bugs and convention adherence. Use proactively after any edit to binutils.js or when asked to review a Read/Write change, a new type, or a PR touching this library.
|
|
4
|
+
tools: Read, Grep, Glob, Bash
|
|
5
|
+
model: sonnet
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
You are a focused code reviewer for **binutils64**, a single-file ES5 CommonJS
|
|
9
|
+
library (`binutils.js`) providing .NET-style `BinaryReader` and `BinaryWriter`
|
|
10
|
+
with selectable endianness. Your job is to catch binary-correctness bugs and
|
|
11
|
+
convention drift in changes to this library. Be precise and skeptical — this code
|
|
12
|
+
already shipped a real width-mismatch bug (the 64-bit writers emit only 32 bits).
|
|
13
|
+
|
|
14
|
+
## What to review
|
|
15
|
+
|
|
16
|
+
Read `binutils.js`, the relevant `README.md` sections, and any `test/` files. Use
|
|
17
|
+
`git diff` (via Bash) to scope the change when reviewing edits. If a round-trip is
|
|
18
|
+
in doubt, actually run it: `node -e "..."` or `node --test`.
|
|
19
|
+
|
|
20
|
+
## Correctness checklist (highest priority)
|
|
21
|
+
|
|
22
|
+
1. **Write width == read width.** For every `WriteX`/`ReadX` pair, the number of
|
|
23
|
+
bytes written must equal the bytes read and the declared width `N`. Flag any
|
|
24
|
+
writer using a narrower method than its buffer size (e.g. `writeUInt32LE` into
|
|
25
|
+
an 8-byte buffer — this was the original `WriteUInt64`/`WriteInt64` bug, since
|
|
26
|
+
fixed). 64-bit writers use `writeBigUInt64*`/`writeBigInt64*` and coerce with
|
|
27
|
+
`BigInt(p_Value)`. Prefer confirming with a real round-trip.
|
|
28
|
+
2. **Endianness branches** present and correct for every multi-byte method: the
|
|
29
|
+
`'little'` branch uses the `*LE` variant, the else branch uses `*BE`. Single-byte
|
|
30
|
+
methods (`*UInt8`/`*Int8`) must NOT have an endianness branch.
|
|
31
|
+
3. **Reader is destructive and consistent**: each `ReadX` slices exactly `N` bytes
|
|
32
|
+
off `this.ByteBuffer` and advances `this.Position` by exactly `N`. `this.Length`
|
|
33
|
+
must NOT change on reads (it reflects the original buffer length).
|
|
34
|
+
4. **Writer accounting**: each `WriteX` increases `this.Length` by exactly `N` and
|
|
35
|
+
passes the updated `this.Length` as the `Buffer.concat` total-length argument.
|
|
36
|
+
5. **Out-of-range reads return the zero value** (`0`, `0.0`, or empty `Buffer`) and
|
|
37
|
+
never throw. Verify the length guard matches the width.
|
|
38
|
+
6. **Signed/unsigned and BigInt**: signed types use the signed Buffer methods;
|
|
39
|
+
64-bit reads return `bigint` (`readBig*64*`). Watch for sign/precision errors.
|
|
40
|
+
|
|
41
|
+
## Convention checklist
|
|
42
|
+
|
|
43
|
+
- ES5 only: `var`, prototype assignment. No `let`/`const`/classes/arrow functions.
|
|
44
|
+
- `PascalCase` method and property names; parameters prefixed `p_`; locals `s_`.
|
|
45
|
+
- `new Buffer(...)` is intentional for `node >=0.12`. Do NOT recommend
|
|
46
|
+
`Buffer.alloc`/`Buffer.from` unless the change also raises `engines.node`.
|
|
47
|
+
- New/changed public methods must have matching `README.md` entries with the
|
|
48
|
+
existing phrasing, and a round-trip test.
|
|
49
|
+
- Methods placed beside their family in the file, ordered consistently.
|
|
50
|
+
|
|
51
|
+
## Output format
|
|
52
|
+
|
|
53
|
+
Report findings grouped by severity. For each: a one-line title, the
|
|
54
|
+
`binutils.js:line` reference, why it is wrong, and the concrete fix. Lead with
|
|
55
|
+
**Correctness** issues, then **Conventions**, then **Docs/Tests**. State a
|
|
56
|
+
confidence level and only raise convention nits if no correctness issue is
|
|
57
|
+
outstanding. If you ran a round-trip to confirm, show the command and result. If
|
|
58
|
+
nothing is wrong, say so plainly — do not invent issues.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"permissions": {
|
|
3
|
+
"allow": [
|
|
4
|
+
"Bash(git checkout *)",
|
|
5
|
+
"Bash(node *)",
|
|
6
|
+
"Bash(echo \"exit code: $?\")",
|
|
7
|
+
"Read(//tmp/**)",
|
|
8
|
+
"Bash(echo \"exit: $?\")",
|
|
9
|
+
"mcp__claude_ai_Context7__resolve-library-id",
|
|
10
|
+
"mcp__claude_ai_Context7__query-docs",
|
|
11
|
+
"Bash(python3 -c \"import yaml,sys; yaml.safe_load\\(open\\('.github/workflows/test.yml'\\)\\); print\\('YAML OK'\\)\")",
|
|
12
|
+
"Bash(git add *)",
|
|
13
|
+
"Bash(git commit -m 'fix 64-bit writers and WriteBytes type guard *)",
|
|
14
|
+
"Bash(git commit -m 'add test suite using node:test *)",
|
|
15
|
+
"Bash(git commit -m 'add github actions workflow to run tests *)",
|
|
16
|
+
"Bash(git commit -m 'add project docs and claude tooling *)",
|
|
17
|
+
"Bash(git push *)",
|
|
18
|
+
"Bash(git commit -m 'rewrite readme with full api reference and examples *)"
|
|
19
|
+
]
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: add-binary-type
|
|
3
|
+
description: Add a new matched Read/Write method pair to binutils.js following the library's exact ES5 conventions, then update README.md and add a round-trip test. Use when asked to support a new numeric/binary type (e.g. a new width, a string type, a boolean) in the BinaryReader/BinaryWriter classes.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Adding a binary type to binutils64
|
|
7
|
+
|
|
8
|
+
This library exposes mirrored methods on `BinaryReader` and `BinaryWriter` in the
|
|
9
|
+
single file `binutils.js`. Every supported type has a `ReadX` and a matching
|
|
10
|
+
`WriteX`. Adding a type means editing both prototypes, updating `README.md`, and
|
|
11
|
+
adding a round-trip test. Follow the templates below **exactly** — style
|
|
12
|
+
deviations and width mismatches are the main source of bugs here (this library
|
|
13
|
+
previously shipped a low-32-bit width bug in the 64-bit writers — don't repeat it).
|
|
14
|
+
|
|
15
|
+
## Hard rules (match existing code)
|
|
16
|
+
|
|
17
|
+
- ES5 only: `var`, prototype assignment. No `let`/`const`/classes/arrow functions.
|
|
18
|
+
- Method names are `PascalCase`: `ReadUInt24`, `WriteBool`, etc.
|
|
19
|
+
- Parameters are prefixed `p_` (`p_Value`); locals are prefixed `s_` (`s_Val`,
|
|
20
|
+
`s_TempBuffer`).
|
|
21
|
+
- Keep `new Buffer(...)` — the package targets `node >=0.12`. Do not switch to
|
|
22
|
+
`Buffer.alloc`/`Buffer.from` unless `package.json` `engines.node` is also raised.
|
|
23
|
+
- **The write width MUST equal the read width.** When the type is wider than 32
|
|
24
|
+
bits, use the correct wide Buffer method (`writeBigUInt64LE`, etc.), NOT
|
|
25
|
+
`writeUInt32LE` — using a 32-bit write into an 8-byte buffer was the original
|
|
26
|
+
64-bit writer bug (now fixed). 64-bit values are `BigInt`; coerce with
|
|
27
|
+
`BigInt(p_Value)` so both `Number` and `BigInt` arguments work.
|
|
28
|
+
|
|
29
|
+
## Reader method template
|
|
30
|
+
|
|
31
|
+
Out-of-range reads return `0` (or `0.0` / empty `Buffer`) — they do not throw.
|
|
32
|
+
Reads are destructive: slice the consumed bytes off `this.ByteBuffer` and advance
|
|
33
|
+
`this.Position` by the byte width `N`.
|
|
34
|
+
|
|
35
|
+
```javascript
|
|
36
|
+
ReadTYPE: function() {
|
|
37
|
+
if (this.ByteBuffer.length < N) {
|
|
38
|
+
return 0;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
var s_Val = (this.Endianness == 'little') ? this.ByteBuffer.readTYPELE(0) : this.ByteBuffer.readTYPEBE(0);
|
|
42
|
+
this.ByteBuffer = this.ByteBuffer.slice(N);
|
|
43
|
+
this.Position += N;
|
|
44
|
+
return s_Val;
|
|
45
|
+
},
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
For a single-byte type there is no endianness branch (see `ReadUInt8`/`ReadInt8`),
|
|
49
|
+
and use `++this.Position;`.
|
|
50
|
+
|
|
51
|
+
## Writer method template
|
|
52
|
+
|
|
53
|
+
Allocate an `N`-byte temp buffer, write with the correct-width method honoring
|
|
54
|
+
endianness, grow `this.Length` by `N`, and concat.
|
|
55
|
+
|
|
56
|
+
```javascript
|
|
57
|
+
WriteTYPE: function(p_Value) {
|
|
58
|
+
var s_TempBuffer = new Buffer(N);
|
|
59
|
+
if (this.Endianness == 'little') {
|
|
60
|
+
s_TempBuffer.writeTYPELE(p_Value, 0);
|
|
61
|
+
} else {
|
|
62
|
+
s_TempBuffer.writeTYPEBE(p_Value, 0);
|
|
63
|
+
}
|
|
64
|
+
this.Length += N;
|
|
65
|
+
this.ByteBuffer = Buffer.concat([this.ByteBuffer, s_TempBuffer], this.Length);
|
|
66
|
+
},
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
For a single-byte type, drop the endianness branch (see `WriteUInt8`/`WriteInt8`).
|
|
70
|
+
|
|
71
|
+
## Placement
|
|
72
|
+
|
|
73
|
+
Keep the read/write methods grouped by family and ordered as in the existing file
|
|
74
|
+
(unsigned ascending width, then signed ascending width, then float/double, then
|
|
75
|
+
bytes). Add the new method next to its siblings, not at the end.
|
|
76
|
+
|
|
77
|
+
## After editing binutils.js
|
|
78
|
+
|
|
79
|
+
1. **README.md** — add a `### ReadX(...)` and `### WriteX(value)` entry in the
|
|
80
|
+
matching BinaryReader / BinaryWriter sections, mirroring the existing phrasing
|
|
81
|
+
("Reads/Writes a … and advances the current position by N bytes").
|
|
82
|
+
2. **Test** — add a round-trip test (see the `test-binutils` skill): write a value
|
|
83
|
+
in both `'big'` and `'little'`, read it back, assert equality. Include a min/max
|
|
84
|
+
or negative boundary value for signed/wide types. For 64-bit values assert the
|
|
85
|
+
returned type is `bigint`.
|
|
86
|
+
3. Run the tests: `node --test`.
|
|
87
|
+
|
|
88
|
+
## Verify before finishing
|
|
89
|
+
|
|
90
|
+
- Write width == read width (round-trip test passes for both endiannesses).
|
|
91
|
+
- `Position`/`Length` advance by exactly `N`.
|
|
92
|
+
- Out-of-range read returns the documented zero value, not a throw.
|
|
93
|
+
- README entries added; method placed beside its family.
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: test-binutils
|
|
3
|
+
description: Author and run tests for the binutils64 library using node:test and node:assert. Use when asked to add test coverage, write tests for a Read/Write method, verify a change, or set up the test suite for this project.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Testing binutils64
|
|
7
|
+
|
|
8
|
+
The library ships with no test framework today. Use the built-in `node:test`
|
|
9
|
+
runner + `node:assert/strict` — zero dependencies. (The library's runtime floor
|
|
10
|
+
stays `node >=0.12`; `node:test` only affects the dev/test environment, which
|
|
11
|
+
needs Node >= 18.)
|
|
12
|
+
|
|
13
|
+
## Layout & running
|
|
14
|
+
|
|
15
|
+
- Put tests in `test/`, named `*.test.js`.
|
|
16
|
+
- Run the whole suite: `node --test`.
|
|
17
|
+
- Add to `package.json` if missing:
|
|
18
|
+
```json
|
|
19
|
+
"scripts": { "test": "node --test" }
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Test file skeleton
|
|
23
|
+
|
|
24
|
+
```javascript
|
|
25
|
+
var test = require('node:test');
|
|
26
|
+
var assert = require('node:assert/strict');
|
|
27
|
+
var binutils = require('../binutils.js');
|
|
28
|
+
|
|
29
|
+
test('UInt32 round-trips both endiannesses', function() {
|
|
30
|
+
['big', 'little'].forEach(function(endian) {
|
|
31
|
+
var w = new binutils.BinaryWriter(endian);
|
|
32
|
+
w.WriteUInt32(0xDEADBEEF);
|
|
33
|
+
var r = new binutils.BinaryReader(w.ByteBuffer, endian);
|
|
34
|
+
assert.equal(r.ReadUInt32(), 0xDEADBEEF);
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Keep test code ES5-compatible in spirit (`var`, `function`) to match repo style,
|
|
40
|
+
though the test runner itself requires modern Node.
|
|
41
|
+
|
|
42
|
+
## Coverage priorities (highest value first)
|
|
43
|
+
|
|
44
|
+
### 1. Round-trips — write then read back
|
|
45
|
+
For every type, both `'big'` and `'little'`: write a value, feed `writer.ByteBuffer`
|
|
46
|
+
into a reader, assert the read value equals the written one.
|
|
47
|
+
- `UInt8/16/32`, `Int8/16/32`, `Float`, `Double`, `Bytes`.
|
|
48
|
+
- `UInt64/Int64`: assert against the correct BigInt value. The writers accept a
|
|
49
|
+
`Number` or a `BigInt` and emit all 8 bytes; the reader returns a `BigInt`, so
|
|
50
|
+
compare against a `BigInt` literal (`123n`), not a `Number` (`123n !== 123` under
|
|
51
|
+
strict equality).
|
|
52
|
+
- Signed boundaries: `Int32` min `-2147483648` and max `2147483647`, negatives for
|
|
53
|
+
`Int8/16`.
|
|
54
|
+
- `Float`: compare with tolerance (`assert.ok(Math.abs(got - want) < 1e-6)`) due to
|
|
55
|
+
precision loss. `Double` is exact for representable values.
|
|
56
|
+
|
|
57
|
+
### 2. BinaryReader behavior
|
|
58
|
+
- **Endianness**: identical bytes read as `'big'` vs `'little'` produce the expected
|
|
59
|
+
swapped values; default (no arg) is `'big'`.
|
|
60
|
+
- **Invariants**: after reads, `Position` advanced by the right byte count, `Length`
|
|
61
|
+
unchanged at the original size, `ByteBuffer` shrank by the consumed bytes.
|
|
62
|
+
- **Out-of-range** reads return `0` / `0.0` / empty `Buffer` (never throw) — read
|
|
63
|
+
past the end for each method.
|
|
64
|
+
- **Constructor inputs**: `Buffer`, `Array`, and `string` (+ encoding) all build;
|
|
65
|
+
an invalid input (e.g. a number) throws.
|
|
66
|
+
- **Constructor copies input**: mutate the caller's original buffer after
|
|
67
|
+
constructing and confirm reader output is unaffected.
|
|
68
|
+
- `ReadUInt64`/`ReadInt64` return a `bigint` (`assert.equal(typeof v, 'bigint')`).
|
|
69
|
+
- README reader example: bytes `[1,0,2,0,0,0,3,1,2,3,4,5,6]` yield `1`, `2`, `3`,
|
|
70
|
+
a 6-byte buffer, then `Position` and `Length` both `13`.
|
|
71
|
+
|
|
72
|
+
### 3. BinaryWriter behavior
|
|
73
|
+
- Defaults: endianness `'big'`, encoding `'ascii'`.
|
|
74
|
+
- `Length` increments by the correct width per write; emitted bytes match expected
|
|
75
|
+
hex for both endiannesses.
|
|
76
|
+
- `WriteBytes` accepts `string`, `Array`, and `Buffer`, producing identical bytes,
|
|
77
|
+
and throws on any other input type.
|
|
78
|
+
- README writer example yields `<Buffer ff ff 00 00 00 00 ff ff ff ff 05 04 03 02 01>`,
|
|
79
|
+
`Length` 15.
|
|
80
|
+
|
|
81
|
+
### 4. Maintaining bug-documentation tests
|
|
82
|
+
There are no open known bugs at the moment. When you discover one, pin its current
|
|
83
|
+
behavior with a clearly-commented test so regressions are noticed. When a bug is
|
|
84
|
+
fixed, delete its pinned test and convert any matching TODO round-trip into a normal
|
|
85
|
+
assertion — this is how the former 64-bit writer bug (low-32-bit truncation) and the
|
|
86
|
+
`WriteBytes` guard bug (`!p_Value instanceof Buffer` mis-parsing, which let invalid
|
|
87
|
+
input through) were retired.
|
|
88
|
+
|
|
89
|
+
## Verify before finishing
|
|
90
|
+
- `node --test` passes (except intentionally-`todo` bug tests).
|
|
91
|
+
- New types added via the `add-binary-type` skill have a matching round-trip test.
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
name: Tests
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [master]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
test:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
strategy:
|
|
12
|
+
fail-fast: false
|
|
13
|
+
matrix:
|
|
14
|
+
node: [20, 22, 24]
|
|
15
|
+
name: Node ${{ matrix.node }}
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@v6
|
|
18
|
+
- uses: actions/setup-node@v6
|
|
19
|
+
with:
|
|
20
|
+
node-version: ${{ matrix.node }}
|
|
21
|
+
# No install step: the package has zero dependencies and no lockfile.
|
|
22
|
+
- run: npm test
|
package/CLAUDE.md
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
Guidance for working in this repository.
|
|
4
|
+
|
|
5
|
+
## What this is
|
|
6
|
+
|
|
7
|
+
`binutils64` — a small npm package providing .NET-style `BinaryReader` and
|
|
8
|
+
`BinaryWriter` classes for Node.js, with selectable endianness (`'big'` default
|
|
9
|
+
or `'little'`). All logic lives in a single file: `binutils.js`. Published to npm
|
|
10
|
+
as `binutils64`.
|
|
11
|
+
|
|
12
|
+
## Layout
|
|
13
|
+
|
|
14
|
+
- `binutils.js` — the entire library. Two ES5 prototype-based "classes"
|
|
15
|
+
(`BinaryReader`, `BinaryWriter`) exported via `module.exports`.
|
|
16
|
+
- `README.md` — the public API reference (keep in sync when adding/changing methods).
|
|
17
|
+
- No `src/`, no build step, no dependencies, no test suite.
|
|
18
|
+
|
|
19
|
+
## Running / testing
|
|
20
|
+
|
|
21
|
+
There is no test framework, lint config, or build. To verify a change, write an
|
|
22
|
+
ad-hoc script and run it with node, e.g.:
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
node -e "var b=require('./binutils.js'); var w=new b.BinaryWriter('little'); w.WriteUInt32(3); console.log(w.ByteBuffer);"
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
When you add or change a public method, update the matching section in `README.md`.
|
|
29
|
+
|
|
30
|
+
## Code conventions (match the existing style exactly)
|
|
31
|
+
|
|
32
|
+
- ES5 only: `var`, prototype assignment, no classes/arrow functions/`let`/`const`.
|
|
33
|
+
- Method and property names are `PascalCase` (`ReadUInt32`, `ByteBuffer`, `Position`).
|
|
34
|
+
- Function parameters are prefixed `p_` (`p_InputBuffer`, `p_Value`).
|
|
35
|
+
- Local variables are prefixed `s_` (`s_Val`, `s_TempBuffer`).
|
|
36
|
+
- Targets `node >=0.12`, so `new Buffer(...)` is used deliberately despite being
|
|
37
|
+
deprecated in modern Node. Don't "fix" it to `Buffer.alloc`/`Buffer.from` unless
|
|
38
|
+
the engines floor is also raised.
|
|
39
|
+
- Commit messages are short, lowercase, imperative (e.g. `add read/write 64 bit values`,
|
|
40
|
+
`fix read int64`). No AI/Claude attribution.
|
|
41
|
+
|
|
42
|
+
## Behavior to preserve
|
|
43
|
+
|
|
44
|
+
- **Reads are destructive.** Each `Read*` slices the consumed bytes off
|
|
45
|
+
`this.ByteBuffer` and advances `this.Position`. `Length` stays at the original
|
|
46
|
+
buffer length; `ByteBuffer` shrinks as you read.
|
|
47
|
+
- **Out-of-range reads return `0` (or `0.0`/empty Buffer), they do not throw.**
|
|
48
|
+
- `ReadUInt64`/`ReadInt64` return a `BigInt` (via `readBigUInt64*`/`readBigInt64*`).
|
|
49
|
+
- The constructor copies the input buffer (`new Buffer(p_InputBuffer)`) so the
|
|
50
|
+
caller's buffer is not mutated — preserve this.
|
|
51
|
+
- `WriteUInt64`/`WriteInt64` accept a `Number` or `BigInt` (coerced via
|
|
52
|
+
`BigInt(p_Value)`) and emit all 8 bytes via `writeBigUInt64*`/`writeBigInt64*`.
|
|
53
|
+
- `WriteBytes` accepts a `Buffer`, `Array`, or `string`, and throws on any other
|
|
54
|
+
input type.
|
package/README.md
CHANGED
|
@@ -1,163 +1,256 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
BinaryReader
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
###
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
1
|
+
# binutils64
|
|
2
|
+
|
|
3
|
+
> A .NET-style `BinaryReader` and `BinaryWriter` for Node.js, with selectable endianness.
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/binutils64)
|
|
6
|
+
[](https://github.com/mihailShumilov/node-binutils/actions/workflows/test.yml)
|
|
7
|
+
|
|
8
|
+
`binutils64` provides two small classes — `BinaryReader` and `BinaryWriter` — that
|
|
9
|
+
make it easy to parse and produce binary data sequentially, with an API modelled on
|
|
10
|
+
the corresponding .NET classes. Both classes let you choose the byte order
|
|
11
|
+
(`big`- or `little`-endian) and support 8-, 16-, 32- and 64-bit integers, floats,
|
|
12
|
+
doubles, and raw byte runs.
|
|
13
|
+
|
|
14
|
+
## Table of contents
|
|
15
|
+
|
|
16
|
+
- [Features](#features)
|
|
17
|
+
- [Installation](#installation)
|
|
18
|
+
- [Quick start](#quick-start)
|
|
19
|
+
- [API reference](#api-reference)
|
|
20
|
+
- [BinaryReader](#binaryreader)
|
|
21
|
+
- [BinaryWriter](#binarywriter)
|
|
22
|
+
- [Behavior and best practices](#behavior-and-best-practices)
|
|
23
|
+
- [Examples](#examples)
|
|
24
|
+
- [Requirements and compatibility](#requirements-and-compatibility)
|
|
25
|
+
- [Testing](#testing)
|
|
26
|
+
- [Contributing](#contributing)
|
|
27
|
+
|
|
28
|
+
## Features
|
|
29
|
+
|
|
30
|
+
- Sequential `Read*` / `Write*` methods for every common fixed-width type.
|
|
31
|
+
- Per-instance endianness (`big` by default, or `little`).
|
|
32
|
+
- 64-bit integers via JavaScript `BigInt`.
|
|
33
|
+
- Signed and unsigned integers, IEEE-754 `float` and `double`, and raw byte runs.
|
|
34
|
+
- Zero runtime dependencies.
|
|
35
|
+
|
|
36
|
+
## Installation
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
npm install binutils64
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Then require it:
|
|
43
|
+
|
|
44
|
+
```javascript
|
|
45
|
+
const binutils = require('binutils64');
|
|
46
|
+
// const { BinaryReader, BinaryWriter } = require('binutils64');
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Quick start
|
|
50
|
+
|
|
51
|
+
```javascript
|
|
52
|
+
const { BinaryReader, BinaryWriter } = require('binutils64');
|
|
53
|
+
|
|
54
|
+
// --- Writing ---
|
|
55
|
+
const writer = new BinaryWriter(); // big-endian by default
|
|
56
|
+
writer.WriteUInt16(65535);
|
|
57
|
+
writer.WriteUInt32(0);
|
|
58
|
+
writer.WriteInt32(-1);
|
|
59
|
+
writer.WriteBytes([5, 4, 3, 2, 1]);
|
|
60
|
+
|
|
61
|
+
console.log(writer.ByteBuffer);
|
|
62
|
+
// <Buffer ff ff 00 00 00 00 ff ff ff ff 05 04 03 02 01>
|
|
63
|
+
console.log(writer.Length); // 15
|
|
64
|
+
|
|
65
|
+
// --- Reading ---
|
|
66
|
+
const reader = new BinaryReader(writer.ByteBuffer);
|
|
67
|
+
console.log(reader.ReadUInt16()); // 65535
|
|
68
|
+
console.log(reader.ReadUInt32()); // 0
|
|
69
|
+
console.log(reader.ReadInt32()); // -1
|
|
70
|
+
console.log(reader.ReadBytes(5)); // <Buffer 05 04 03 02 01>
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## API reference
|
|
74
|
+
|
|
75
|
+
### BinaryReader
|
|
76
|
+
|
|
77
|
+
A reader wraps an immutable copy of the input data and consumes it from the front as
|
|
78
|
+
you read.
|
|
79
|
+
|
|
80
|
+
#### `new BinaryReader(input, [endianness], [encoding])`
|
|
81
|
+
|
|
82
|
+
| Parameter | Type | Default | Description |
|
|
83
|
+
| ------------ | ----------------------------- | ------- | ------------------------------------------------------------------ |
|
|
84
|
+
| `input` | `Buffer` \| `number[]` \| `string` | — | The data to read. A `Buffer` is **copied** so the source is never mutated. |
|
|
85
|
+
| `endianness` | `'big'` \| `'little'` | `'big'` | Byte order used by all multi-byte reads. |
|
|
86
|
+
| `encoding` | `string` | `'ascii'` | Used only when `input` is a `string`, to turn it into bytes. |
|
|
87
|
+
|
|
88
|
+
Throws `Error` if `input` is not a `Buffer`, array, or string.
|
|
89
|
+
|
|
90
|
+
#### Read methods
|
|
91
|
+
|
|
92
|
+
| Method | Bytes | Returns | Notes |
|
|
93
|
+
| ----------------- | ----- | --------- | ---------------------------------------------- |
|
|
94
|
+
| `ReadUInt8()` | 1 | `number` | Unsigned. |
|
|
95
|
+
| `ReadInt8()` | 1 | `number` | Signed. |
|
|
96
|
+
| `ReadUInt16()` | 2 | `number` | Honors endianness. |
|
|
97
|
+
| `ReadInt16()` | 2 | `number` | Honors endianness. |
|
|
98
|
+
| `ReadUInt32()` | 4 | `number` | Honors endianness. |
|
|
99
|
+
| `ReadInt32()` | 4 | `number` | Honors endianness. |
|
|
100
|
+
| `ReadUInt64()` | 8 | `BigInt` | Honors endianness. Returns a `BigInt`. |
|
|
101
|
+
| `ReadInt64()` | 8 | `BigInt` | Honors endianness. Returns a `BigInt`. |
|
|
102
|
+
| `ReadFloat()` | 4 | `number` | IEEE-754 single precision. |
|
|
103
|
+
| `ReadDouble()` | 8 | `number` | IEEE-754 double precision. |
|
|
104
|
+
| `ReadBytes(count)`| `count` | `Buffer`| Copies `count` bytes into a new `Buffer`. |
|
|
105
|
+
|
|
106
|
+
If fewer than the required number of bytes remain, integer/float reads return `0`
|
|
107
|
+
(or `0.0`), and `ReadBytes` returns an empty `Buffer` — **without** throwing or
|
|
108
|
+
advancing the position. See [Behavior and best practices](#behavior-and-best-practices).
|
|
109
|
+
|
|
110
|
+
#### Reader properties
|
|
111
|
+
|
|
112
|
+
| Property | Type | Description |
|
|
113
|
+
| ------------ | -------- | ------------------------------------------------------------------- |
|
|
114
|
+
| `ByteBuffer` | `Buffer` | The **remaining** unread data. Shrinks as you read. |
|
|
115
|
+
| `Position` | `number` | Number of bytes consumed so far (starts at `0`). |
|
|
116
|
+
| `Length` | `number` | The length of the original input. Does **not** change as you read. |
|
|
117
|
+
| `Endianness` | `string` | `'big'` or `'little'`. |
|
|
118
|
+
| `Encoding` | `string` | The encoding passed to the constructor. |
|
|
119
|
+
|
|
120
|
+
### BinaryWriter
|
|
121
|
+
|
|
122
|
+
A writer accumulates bytes in an internal buffer that grows with every write.
|
|
123
|
+
|
|
124
|
+
#### `new BinaryWriter([endianness], [encoding])`
|
|
125
|
+
|
|
126
|
+
| Parameter | Type | Default | Description |
|
|
127
|
+
| ------------ | --------------------- | --------- | ---------------------------------------- |
|
|
128
|
+
| `endianness` | `'big'` \| `'little'` | `'big'` | Byte order used by all multi-byte writes.|
|
|
129
|
+
| `encoding` | `string` | `'ascii'` | Stored on the instance; reserved. |
|
|
130
|
+
|
|
131
|
+
#### Write methods
|
|
132
|
+
|
|
133
|
+
| Method | Bytes | Accepts | Notes |
|
|
134
|
+
| --------------------- | ----- | ------------------- | -------------------------------------------------- |
|
|
135
|
+
| `WriteUInt8(value)` | 1 | `number` | Unsigned. |
|
|
136
|
+
| `WriteInt8(value)` | 1 | `number` | Signed. |
|
|
137
|
+
| `WriteUInt16(value)` | 2 | `number` | Honors endianness. |
|
|
138
|
+
| `WriteInt16(value)` | 2 | `number` | Honors endianness. |
|
|
139
|
+
| `WriteUInt32(value)` | 4 | `number` | Honors endianness. |
|
|
140
|
+
| `WriteInt32(value)` | 4 | `number` | Honors endianness. |
|
|
141
|
+
| `WriteUInt64(value)` | 8 | `number` \| `BigInt`| Coerced with `BigInt(value)`; honors endianness. |
|
|
142
|
+
| `WriteInt64(value)` | 8 | `number` \| `BigInt`| Coerced with `BigInt(value)`; honors endianness. |
|
|
143
|
+
| `WriteFloat(value)` | 4 | `number` | IEEE-754 single precision. |
|
|
144
|
+
| `WriteDouble(value)` | 8 | `number` | IEEE-754 double precision. |
|
|
145
|
+
| `WriteBytes(value)` | varies| `Buffer` \| `number[]` \| `string` | Strings are written as one byte per character code. Throws on any other type. |
|
|
146
|
+
|
|
147
|
+
Writing a value outside the target type's range throws a `RangeError` (the standard
|
|
148
|
+
Node.js `Buffer` write behavior) — e.g. `WriteUInt8(256)`.
|
|
149
|
+
|
|
150
|
+
#### Writer properties
|
|
151
|
+
|
|
152
|
+
| Property | Type | Description |
|
|
153
|
+
| ------------ | -------- | -------------------------------------------- |
|
|
154
|
+
| `ByteBuffer` | `Buffer` | All bytes written so far. |
|
|
155
|
+
| `Length` | `number` | The current length of `ByteBuffer`. |
|
|
156
|
+
| `Endianness` | `string` | `'big'` or `'little'`. |
|
|
157
|
+
| `Encoding` | `string` | The encoding passed to the constructor. |
|
|
158
|
+
|
|
159
|
+
## Behavior and best practices
|
|
160
|
+
|
|
161
|
+
- **Reads are destructive.** Each `Read*` call consumes bytes from the front of
|
|
162
|
+
`ByteBuffer` and advances `Position`. If you need the original bytes again, keep
|
|
163
|
+
your own copy before reading. Create a fresh `BinaryReader` to start over.
|
|
164
|
+
- **`Length` vs. `Position`.** On a reader, `Length` is the original size and never
|
|
165
|
+
changes; `Position` tracks how much you have consumed. The bytes left to read are
|
|
166
|
+
`Length - Position` (also `ByteBuffer.length`).
|
|
167
|
+
- **Out-of-range reads do not throw.** Reading past the end returns `0` / `0.0` /
|
|
168
|
+
an empty `Buffer` and leaves `Position` unchanged. Check the remaining length
|
|
169
|
+
yourself if a short buffer should be treated as an error:
|
|
170
|
+
|
|
171
|
+
```javascript
|
|
172
|
+
if (reader.ByteBuffer.length < 4) {
|
|
173
|
+
throw new Error('truncated record');
|
|
174
|
+
}
|
|
175
|
+
const value = reader.ReadUInt32();
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
- **64-bit values are `BigInt`s.** `ReadUInt64`/`ReadInt64` always return a `BigInt`.
|
|
179
|
+
When writing, pass a `BigInt` for any value above `Number.MAX_SAFE_INTEGER`
|
|
180
|
+
(`2^53 - 1`) to avoid silent precision loss; smaller `number`s are accepted and
|
|
181
|
+
coerced automatically.
|
|
182
|
+
- **Set endianness once, at construction.** All multi-byte methods follow the
|
|
183
|
+
instance's `Endianness`; reader and writer must agree to round-trip correctly.
|
|
184
|
+
- **Constructing a reader from a string?** Pass the encoding explicitly
|
|
185
|
+
(e.g. `new BinaryReader(text, 'big', 'utf8')`) so the bytes are interpreted the
|
|
186
|
+
way you expect.
|
|
187
|
+
|
|
188
|
+
## Examples
|
|
189
|
+
|
|
190
|
+
### Round-tripping a 64-bit integer
|
|
191
|
+
|
|
192
|
+
```javascript
|
|
193
|
+
const { BinaryReader, BinaryWriter } = require('binutils64');
|
|
194
|
+
|
|
195
|
+
const writer = new BinaryWriter(); // big-endian
|
|
196
|
+
writer.WriteUInt64(0x1234567890ABCDEFn); // pass a BigInt for large values
|
|
197
|
+
console.log(writer.ByteBuffer); // <Buffer 12 34 56 78 90 ab cd ef>
|
|
198
|
+
|
|
199
|
+
const reader = new BinaryReader(writer.ByteBuffer);
|
|
200
|
+
console.log(reader.ReadUInt64()); // 1311768467294899695n
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
### Little-endian
|
|
204
|
+
|
|
205
|
+
```javascript
|
|
206
|
+
const { BinaryReader, BinaryWriter } = require('binutils64');
|
|
207
|
+
|
|
208
|
+
const writer = new BinaryWriter('little');
|
|
209
|
+
writer.WriteUInt32(0x01020304);
|
|
210
|
+
console.log(writer.ByteBuffer); // <Buffer 04 03 02 01>
|
|
211
|
+
|
|
212
|
+
const reader = new BinaryReader(writer.ByteBuffer, 'little');
|
|
213
|
+
console.log(reader.ReadUInt32().toString(16)); // "1020304"
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
### Parsing a structured record
|
|
217
|
+
|
|
218
|
+
```javascript
|
|
219
|
+
const { BinaryReader } = require('binutils64');
|
|
220
|
+
|
|
221
|
+
// type (u8), id (u32, big-endian), payload (6 bytes)
|
|
222
|
+
const reader = new BinaryReader(Buffer.from([1, 0, 0, 0, 3, 1, 2, 3, 4, 5, 6]));
|
|
223
|
+
|
|
224
|
+
const record = {
|
|
225
|
+
type: reader.ReadUInt8(), // 1
|
|
226
|
+
id: reader.ReadUInt32(), // 3
|
|
227
|
+
payload: reader.ReadBytes(6), // <Buffer 01 02 03 04 05 06>
|
|
228
|
+
};
|
|
229
|
+
|
|
230
|
+
console.log(record, 'read', reader.Position, 'of', reader.Length, 'bytes');
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
## Requirements and compatibility
|
|
234
|
+
|
|
235
|
+
- **Node.js.** The 8-, 16- and 32-bit methods run on very old Node.js versions, but
|
|
236
|
+
the 64-bit methods (`ReadUInt64`, `ReadInt64`, `WriteUInt64`, `WriteInt64`) rely on
|
|
237
|
+
`BigInt` and the `Buffer` big-integer methods, which require **Node.js 12 or newer**.
|
|
238
|
+
- The library uses the legacy `Buffer` constructor internally; on modern Node.js you
|
|
239
|
+
may see a one-time `DEP0005` deprecation warning. This does not affect behavior.
|
|
240
|
+
- Running the test suite uses the built-in `node:test` runner, which requires
|
|
241
|
+
**Node.js 18 or newer**.
|
|
242
|
+
|
|
243
|
+
## Testing
|
|
244
|
+
|
|
245
|
+
```bash
|
|
246
|
+
npm test # runs `node --test`
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
The suite covers every reader/writer method, round-trips across both endiannesses,
|
|
250
|
+
edge cases (out-of-range reads, constructor input types, buffer-copy isolation), and
|
|
251
|
+
the documented examples. Continuous integration runs it on Node.js 20, 22 and 24.
|
|
252
|
+
|
|
253
|
+
## Contributing
|
|
254
|
+
|
|
255
|
+
Issues and pull requests are welcome. Please add or update tests for any behavioral
|
|
256
|
+
change and make sure `npm test` passes before opening a pull request.
|
package/binutils.js
CHANGED
|
@@ -195,10 +195,11 @@ BinaryWriter.prototype = {
|
|
|
195
195
|
|
|
196
196
|
WriteUInt64: function(p_Value) {
|
|
197
197
|
var s_TempBuffer = new Buffer(8);
|
|
198
|
+
var s_Value = BigInt(p_Value);
|
|
198
199
|
if (this.Endianness == 'little') {
|
|
199
|
-
s_TempBuffer.
|
|
200
|
+
s_TempBuffer.writeBigUInt64LE(s_Value, 0);
|
|
200
201
|
} else {
|
|
201
|
-
s_TempBuffer.
|
|
202
|
+
s_TempBuffer.writeBigUInt64BE(s_Value, 0);
|
|
202
203
|
}
|
|
203
204
|
this.Length += 8;
|
|
204
205
|
this.ByteBuffer = Buffer.concat([this.ByteBuffer, s_TempBuffer], this.Length);
|
|
@@ -235,10 +236,11 @@ BinaryWriter.prototype = {
|
|
|
235
236
|
|
|
236
237
|
WriteInt64: function(p_Value) {
|
|
237
238
|
var s_TempBuffer = new Buffer(8);
|
|
239
|
+
var s_Value = BigInt(p_Value);
|
|
238
240
|
if (this.Endianness == 'little') {
|
|
239
|
-
s_TempBuffer.
|
|
241
|
+
s_TempBuffer.writeBigInt64LE(s_Value, 0);
|
|
240
242
|
} else {
|
|
241
|
-
s_TempBuffer.
|
|
243
|
+
s_TempBuffer.writeBigInt64BE(s_Value, 0);
|
|
242
244
|
}
|
|
243
245
|
this.Length += 8;
|
|
244
246
|
this.ByteBuffer = Buffer.concat([this.ByteBuffer, s_TempBuffer], this.Length);
|
|
@@ -279,7 +281,7 @@ BinaryWriter.prototype = {
|
|
|
279
281
|
p_Value = s_BytesArray;
|
|
280
282
|
}
|
|
281
283
|
|
|
282
|
-
if (!p_Value instanceof Buffer && !p_Value instanceof Array) {
|
|
284
|
+
if (!(p_Value instanceof Buffer) && !(p_Value instanceof Array)) {
|
|
283
285
|
throw new Error("Invalid Buffer object provided.");
|
|
284
286
|
}
|
|
285
287
|
|
package/package.json
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "binutils64",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
4
4
|
"author": "Mihail Shumilov <mschumilow@gmail.com>",
|
|
5
5
|
"description": "A .NET-like BinaryReader and BinaryWriter with endianness support.",
|
|
6
6
|
"main": "./binutils.js",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"test": "node --test"
|
|
9
|
+
},
|
|
7
10
|
"engines": {
|
|
8
11
|
"node": ">=0.12"
|
|
9
12
|
},
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
var test = require('node:test');
|
|
2
|
+
var assert = require('node:assert/strict');
|
|
3
|
+
var binutils = require('../binutils.js');
|
|
4
|
+
var BinaryReader = binutils.BinaryReader;
|
|
5
|
+
|
|
6
|
+
test('constructor accepts a Buffer', function() {
|
|
7
|
+
var r = new BinaryReader(Buffer.from([1, 2, 3]));
|
|
8
|
+
assert.equal(r.Length, 3);
|
|
9
|
+
assert.equal(r.ReadUInt8(), 1);
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
test('constructor accepts an Array', function() {
|
|
13
|
+
var r = new BinaryReader([1, 2, 3]);
|
|
14
|
+
assert.equal(r.Length, 3);
|
|
15
|
+
assert.equal(r.ReadUInt8(), 1);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
test('constructor accepts a string with encoding', function() {
|
|
19
|
+
var r = new BinaryReader('ABC', 'big', 'ascii');
|
|
20
|
+
assert.equal(r.Length, 3);
|
|
21
|
+
assert.equal(r.ReadUInt8(), 65);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
test('constructor rejects invalid input types', function() {
|
|
25
|
+
assert.throws(function() { new BinaryReader(42); }, /Invalid buffer input/);
|
|
26
|
+
assert.throws(function() { new BinaryReader({}); }, /Invalid buffer input/);
|
|
27
|
+
assert.throws(function() { new BinaryReader(null); }, /Invalid buffer input/);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
test('constructor copies the input buffer (no aliasing)', function() {
|
|
31
|
+
var src = Buffer.from([1, 2, 3]);
|
|
32
|
+
var r = new BinaryReader(src);
|
|
33
|
+
src[0] = 99; // mutate the caller's buffer after construction
|
|
34
|
+
assert.equal(r.ReadUInt8(), 1, 'reader is unaffected by later mutation of the source');
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
test('out-of-range integer reads return 0 without advancing Position', function() {
|
|
38
|
+
var r = new BinaryReader([0x01], 'big'); // only 1 byte available
|
|
39
|
+
assert.equal(r.ReadUInt16(), 0);
|
|
40
|
+
assert.equal(r.ReadUInt32(), 0);
|
|
41
|
+
assert.equal(r.ReadUInt64(), 0);
|
|
42
|
+
assert.equal(r.ReadInt16(), 0);
|
|
43
|
+
assert.equal(r.ReadInt32(), 0);
|
|
44
|
+
assert.equal(r.ReadInt64(), 0);
|
|
45
|
+
assert.equal(r.Position, 0, 'failed reads do not advance Position');
|
|
46
|
+
assert.equal(r.ByteBuffer.length, 1, 'failed reads do not consume bytes');
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
test('out-of-range float/double reads return 0', function() {
|
|
50
|
+
var r = new BinaryReader([0x01], 'big');
|
|
51
|
+
assert.equal(r.ReadFloat(), 0);
|
|
52
|
+
assert.equal(r.ReadDouble(), 0);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
test('ReadUInt8 on an empty buffer returns 0', function() {
|
|
56
|
+
var r = new BinaryReader([], 'big');
|
|
57
|
+
assert.equal(r.Length, 0);
|
|
58
|
+
assert.equal(r.ReadUInt8(), 0);
|
|
59
|
+
assert.equal(r.ReadInt8(), 0);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
test('ReadBytes returns an empty Buffer when count exceeds remaining', function() {
|
|
63
|
+
var r = new BinaryReader([1, 2, 3], 'big');
|
|
64
|
+
var out = r.ReadBytes(5);
|
|
65
|
+
assert.ok(Buffer.isBuffer(out));
|
|
66
|
+
assert.equal(out.length, 0);
|
|
67
|
+
assert.equal(r.Position, 0, 'over-long ReadBytes does not advance');
|
|
68
|
+
assert.equal(r.ByteBuffer.length, 3);
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
test('ReadBytes reading exactly the remaining length succeeds', function() {
|
|
72
|
+
var r = new BinaryReader([1, 2, 3], 'big');
|
|
73
|
+
assert.deepEqual(Array.from(r.ReadBytes(3)), [1, 2, 3]);
|
|
74
|
+
assert.equal(r.Position, 3);
|
|
75
|
+
assert.equal(r.ByteBuffer.length, 0);
|
|
76
|
+
});
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
var test = require('node:test');
|
|
2
|
+
var assert = require('node:assert/strict');
|
|
3
|
+
var binutils = require('../binutils.js');
|
|
4
|
+
var BinaryReader = binutils.BinaryReader;
|
|
5
|
+
|
|
6
|
+
function reader(arr, endian) {
|
|
7
|
+
return new BinaryReader(Buffer.from(arr), endian);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
test('BinaryReader: constructor defaults', function() {
|
|
11
|
+
var r = reader([1, 2, 3]);
|
|
12
|
+
assert.equal(r.Endianness, 'big', 'defaults to big-endian');
|
|
13
|
+
assert.equal(r.Encoding, 'ascii', 'defaults to ascii encoding');
|
|
14
|
+
assert.equal(r.Length, 3, 'Length is the original buffer length');
|
|
15
|
+
assert.equal(r.Position, 0, 'Position starts at 0');
|
|
16
|
+
assert.equal(r.ByteBuffer.length, 3);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
test('BinaryReader: ReadUInt8', function() {
|
|
20
|
+
var r = reader([200, 5]);
|
|
21
|
+
assert.equal(r.ReadUInt8(), 200);
|
|
22
|
+
assert.equal(r.ReadUInt8(), 5);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
test('BinaryReader: ReadInt8 (signed)', function() {
|
|
26
|
+
var r = reader([0xFF, 0x80, 0x7F]);
|
|
27
|
+
assert.equal(r.ReadInt8(), -1);
|
|
28
|
+
assert.equal(r.ReadInt8(), -128);
|
|
29
|
+
assert.equal(r.ReadInt8(), 127);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
test('BinaryReader: ReadUInt16 honors endianness', function() {
|
|
33
|
+
assert.equal(reader([0x12, 0x34], 'big').ReadUInt16(), 0x1234);
|
|
34
|
+
assert.equal(reader([0x12, 0x34], 'little').ReadUInt16(), 0x3412);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
test('BinaryReader: ReadInt16 (signed)', function() {
|
|
38
|
+
assert.equal(reader([0xFF, 0xFF], 'big').ReadInt16(), -1);
|
|
39
|
+
assert.equal(reader([0x80, 0x00], 'big').ReadInt16(), -32768);
|
|
40
|
+
assert.equal(reader([0x00, 0x80], 'little').ReadInt16(), -32768);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
test('BinaryReader: ReadUInt32 honors endianness', function() {
|
|
44
|
+
assert.equal(reader([0x12, 0x34, 0x56, 0x78], 'big').ReadUInt32(), 0x12345678);
|
|
45
|
+
assert.equal(reader([0x78, 0x56, 0x34, 0x12], 'little').ReadUInt32(), 0x12345678);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
test('BinaryReader: ReadInt32 (signed)', function() {
|
|
49
|
+
assert.equal(reader([0xFF, 0xFF, 0xFF, 0xFF], 'big').ReadInt32(), -1);
|
|
50
|
+
assert.equal(reader([0x80, 0x00, 0x00, 0x00], 'big').ReadInt32(), -2147483648);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
test('BinaryReader: ReadUInt64 returns a BigInt', function() {
|
|
54
|
+
var r = reader([0, 0, 0, 0, 0, 0, 0, 5], 'big');
|
|
55
|
+
var v = r.ReadUInt64();
|
|
56
|
+
assert.equal(typeof v, 'bigint');
|
|
57
|
+
assert.equal(v, 5n);
|
|
58
|
+
assert.equal(reader([5, 0, 0, 0, 0, 0, 0, 0], 'little').ReadUInt64(), 5n);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
test('BinaryReader: ReadInt64 returns a signed BigInt', function() {
|
|
62
|
+
assert.equal(reader([0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF], 'big').ReadInt64(), -1n);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
test('BinaryReader: ReadFloat', function() {
|
|
66
|
+
// 1.5 as IEEE-754 single precision = 0x3FC00000
|
|
67
|
+
assert.equal(reader([0x3F, 0xC0, 0x00, 0x00], 'big').ReadFloat(), 1.5);
|
|
68
|
+
assert.equal(reader([0x00, 0x00, 0xC0, 0x3F], 'little').ReadFloat(), 1.5);
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
test('BinaryReader: ReadDouble', function() {
|
|
72
|
+
// 1.5 as IEEE-754 double precision = 0x3FF8000000000000
|
|
73
|
+
assert.equal(reader([0x3F, 0xF8, 0, 0, 0, 0, 0, 0], 'big').ReadDouble(), 1.5);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
test('BinaryReader: ReadBytes returns a Buffer of the requested length', function() {
|
|
77
|
+
var r = reader([1, 2, 3, 4, 5, 6]);
|
|
78
|
+
var out = r.ReadBytes(4);
|
|
79
|
+
assert.ok(Buffer.isBuffer(out));
|
|
80
|
+
assert.deepEqual(Array.from(out), [1, 2, 3, 4]);
|
|
81
|
+
assert.equal(r.Position, 4);
|
|
82
|
+
assert.equal(r.ByteBuffer.length, 2);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
test('BinaryReader: Position advances and Length stays constant; ByteBuffer shrinks', function() {
|
|
86
|
+
var r = reader([1, 2, 3, 4, 5, 6, 7, 8], 'big');
|
|
87
|
+
|
|
88
|
+
assert.equal(r.ReadUInt8(), 1);
|
|
89
|
+
assert.equal(r.Position, 1);
|
|
90
|
+
assert.equal(r.Length, 8, 'Length never changes on reads');
|
|
91
|
+
assert.equal(r.ByteBuffer.length, 7, 'ByteBuffer shrinks by consumed bytes');
|
|
92
|
+
|
|
93
|
+
assert.equal(r.ReadUInt16(), 0x0203);
|
|
94
|
+
assert.equal(r.Position, 3);
|
|
95
|
+
assert.equal(r.ByteBuffer.length, 5);
|
|
96
|
+
|
|
97
|
+
assert.equal(r.ReadUInt32(), 0x04050607);
|
|
98
|
+
assert.equal(r.Position, 7);
|
|
99
|
+
assert.equal(r.ByteBuffer.length, 1);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
test('BinaryReader: same bytes decode differently per endianness', function() {
|
|
103
|
+
var b = [0x00, 0x00, 0x00, 0x01];
|
|
104
|
+
assert.equal(reader(b, 'big').ReadUInt32(), 1);
|
|
105
|
+
assert.equal(reader(b, 'little').ReadUInt32(), 0x01000000);
|
|
106
|
+
});
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
var test = require('node:test');
|
|
2
|
+
var assert = require('node:assert/strict');
|
|
3
|
+
var binutils = require('../binutils.js');
|
|
4
|
+
|
|
5
|
+
// These mirror the worked examples in README.md. If the documented output ever
|
|
6
|
+
// changes, the docs and these tests must change together.
|
|
7
|
+
|
|
8
|
+
test('README reader example', function() {
|
|
9
|
+
var buffer = Buffer.from([1, 0, 2, 0, 0, 0, 3, 1, 2, 3, 4, 5, 6]);
|
|
10
|
+
var reader = new binutils.BinaryReader(buffer);
|
|
11
|
+
|
|
12
|
+
assert.equal(reader.ReadUInt8(), 1);
|
|
13
|
+
assert.equal(reader.ReadUInt16(), 2);
|
|
14
|
+
assert.equal(reader.ReadUInt32(), 3);
|
|
15
|
+
assert.deepEqual(Array.from(reader.ReadBytes(6)), [1, 2, 3, 4, 5, 6]);
|
|
16
|
+
assert.equal(reader.Position, 13);
|
|
17
|
+
assert.equal(reader.Length, 13);
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
test('README writer example', function() {
|
|
21
|
+
var writer = new binutils.BinaryWriter();
|
|
22
|
+
|
|
23
|
+
writer.WriteUInt16(65535);
|
|
24
|
+
writer.WriteUInt32(0);
|
|
25
|
+
writer.WriteInt32(-1);
|
|
26
|
+
writer.WriteBytes([5, 4, 3, 2, 1]);
|
|
27
|
+
|
|
28
|
+
assert.deepEqual(
|
|
29
|
+
Array.from(writer.ByteBuffer),
|
|
30
|
+
[0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 5, 4, 3, 2, 1]
|
|
31
|
+
);
|
|
32
|
+
assert.equal(writer.Length, 15);
|
|
33
|
+
});
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
var test = require('node:test');
|
|
2
|
+
var assert = require('node:assert/strict');
|
|
3
|
+
var binutils = require('../binutils.js');
|
|
4
|
+
var BinaryReader = binutils.BinaryReader;
|
|
5
|
+
var BinaryWriter = binutils.BinaryWriter;
|
|
6
|
+
|
|
7
|
+
// Write a value with WriteMethod, then read it back with ReadMethod, asserting
|
|
8
|
+
// equality across both endiannesses.
|
|
9
|
+
function roundTrip(writeMethod, readMethod, value, compare) {
|
|
10
|
+
['big', 'little'].forEach(function(endian) {
|
|
11
|
+
var w = new BinaryWriter(endian);
|
|
12
|
+
w[writeMethod](value);
|
|
13
|
+
var r = new BinaryReader(w.ByteBuffer, endian);
|
|
14
|
+
var got = r[readMethod]();
|
|
15
|
+
if (compare) {
|
|
16
|
+
compare(got, value, endian);
|
|
17
|
+
} else {
|
|
18
|
+
assert.equal(got, value, writeMethod + '/' + readMethod + ' (' + endian + ')');
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
test('round-trip: UInt8', function() {
|
|
24
|
+
roundTrip('WriteUInt8', 'ReadUInt8', 0);
|
|
25
|
+
roundTrip('WriteUInt8', 'ReadUInt8', 200);
|
|
26
|
+
roundTrip('WriteUInt8', 'ReadUInt8', 255);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
test('round-trip: Int8', function() {
|
|
30
|
+
roundTrip('WriteInt8', 'ReadInt8', -128);
|
|
31
|
+
roundTrip('WriteInt8', 'ReadInt8', -1);
|
|
32
|
+
roundTrip('WriteInt8', 'ReadInt8', 127);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
test('round-trip: UInt16', function() {
|
|
36
|
+
roundTrip('WriteUInt16', 'ReadUInt16', 0);
|
|
37
|
+
roundTrip('WriteUInt16', 'ReadUInt16', 0xBEEF);
|
|
38
|
+
roundTrip('WriteUInt16', 'ReadUInt16', 0xFFFF);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
test('round-trip: Int16', function() {
|
|
42
|
+
roundTrip('WriteInt16', 'ReadInt16', -32768);
|
|
43
|
+
roundTrip('WriteInt16', 'ReadInt16', -1);
|
|
44
|
+
roundTrip('WriteInt16', 'ReadInt16', 32767);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
test('round-trip: UInt32', function() {
|
|
48
|
+
roundTrip('WriteUInt32', 'ReadUInt32', 0);
|
|
49
|
+
roundTrip('WriteUInt32', 'ReadUInt32', 0xDEADBEEF);
|
|
50
|
+
roundTrip('WriteUInt32', 'ReadUInt32', 0xFFFFFFFF);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
test('round-trip: Int32', function() {
|
|
54
|
+
roundTrip('WriteInt32', 'ReadInt32', -2147483648);
|
|
55
|
+
roundTrip('WriteInt32', 'ReadInt32', -1);
|
|
56
|
+
roundTrip('WriteInt32', 'ReadInt32', 2147483647);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
test('round-trip: Float (within single-precision tolerance)', function() {
|
|
60
|
+
roundTrip('WriteFloat', 'ReadFloat', 1.5);
|
|
61
|
+
roundTrip('WriteFloat', 'ReadFloat', 3.14, function(got, want, endian) {
|
|
62
|
+
assert.ok(Math.abs(got - want) < 1e-5, 'Float ~3.14 (' + endian + '), got ' + got);
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
test('round-trip: Double (exact)', function() {
|
|
67
|
+
roundTrip('WriteDouble', 'ReadDouble', Math.PI);
|
|
68
|
+
roundTrip('WriteDouble', 'ReadDouble', -123456.789);
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
test('round-trip: Bytes', function() {
|
|
72
|
+
['big', 'little'].forEach(function(endian) {
|
|
73
|
+
var payload = [1, 2, 3, 4, 5];
|
|
74
|
+
var w = new BinaryWriter(endian);
|
|
75
|
+
w.WriteBytes(payload);
|
|
76
|
+
var r = new BinaryReader(w.ByteBuffer, endian);
|
|
77
|
+
assert.deepEqual(Array.from(r.ReadBytes(payload.length)), payload);
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
// 64-bit round-trips. The writers accept a Number or a BigInt and emit all 8
|
|
82
|
+
// bytes; the reader always returns a BigInt.
|
|
83
|
+
|
|
84
|
+
test('round-trip: UInt64 (BigInt argument)', function() {
|
|
85
|
+
var value = 0x1234567890ABCDEFn;
|
|
86
|
+
['big', 'little'].forEach(function(endian) {
|
|
87
|
+
var w = new BinaryWriter(endian);
|
|
88
|
+
w.WriteUInt64(value);
|
|
89
|
+
var r = new BinaryReader(w.ByteBuffer, endian);
|
|
90
|
+
assert.equal(r.ReadUInt64(), value);
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
test('round-trip: UInt64 (Number argument is coerced)', function() {
|
|
95
|
+
['big', 'little'].forEach(function(endian) {
|
|
96
|
+
var w = new BinaryWriter(endian);
|
|
97
|
+
w.WriteUInt64(123);
|
|
98
|
+
var r = new BinaryReader(w.ByteBuffer, endian);
|
|
99
|
+
assert.equal(r.ReadUInt64(), 123n);
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
test('round-trip: Int64 (BigInt argument)', function() {
|
|
104
|
+
var value = -81985529216486896n;
|
|
105
|
+
['big', 'little'].forEach(function(endian) {
|
|
106
|
+
var w = new BinaryWriter(endian);
|
|
107
|
+
w.WriteInt64(value);
|
|
108
|
+
var r = new BinaryReader(w.ByteBuffer, endian);
|
|
109
|
+
assert.equal(r.ReadInt64(), value);
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
test('round-trip: Int64 (negative Number argument is coerced)', function() {
|
|
114
|
+
['big', 'little'].forEach(function(endian) {
|
|
115
|
+
var w = new BinaryWriter(endian);
|
|
116
|
+
w.WriteInt64(-123);
|
|
117
|
+
var r = new BinaryReader(w.ByteBuffer, endian);
|
|
118
|
+
assert.equal(r.ReadInt64(), -123n);
|
|
119
|
+
});
|
|
120
|
+
});
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
var test = require('node:test');
|
|
2
|
+
var assert = require('node:assert/strict');
|
|
3
|
+
var binutils = require('../binutils.js');
|
|
4
|
+
var BinaryWriter = binutils.BinaryWriter;
|
|
5
|
+
|
|
6
|
+
function bytes(writer) {
|
|
7
|
+
return Array.from(writer.ByteBuffer);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
test('BinaryWriter: constructor defaults', function() {
|
|
11
|
+
var w = new BinaryWriter();
|
|
12
|
+
assert.equal(w.Endianness, 'big');
|
|
13
|
+
assert.equal(w.Encoding, 'ascii');
|
|
14
|
+
assert.equal(w.Length, 0);
|
|
15
|
+
assert.equal(w.ByteBuffer.length, 0);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
test('BinaryWriter: WriteUInt8', function() {
|
|
19
|
+
var w = new BinaryWriter();
|
|
20
|
+
w.WriteUInt8(200);
|
|
21
|
+
assert.deepEqual(bytes(w), [200]);
|
|
22
|
+
assert.equal(w.Length, 1);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
test('BinaryWriter: WriteInt8 (signed)', function() {
|
|
26
|
+
var w = new BinaryWriter();
|
|
27
|
+
w.WriteInt8(-1);
|
|
28
|
+
assert.deepEqual(bytes(w), [0xFF]);
|
|
29
|
+
assert.equal(w.Length, 1);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
test('BinaryWriter: WriteUInt16 honors endianness', function() {
|
|
33
|
+
var big = new BinaryWriter('big');
|
|
34
|
+
big.WriteUInt16(0x1234);
|
|
35
|
+
assert.deepEqual(bytes(big), [0x12, 0x34]);
|
|
36
|
+
assert.equal(big.Length, 2);
|
|
37
|
+
|
|
38
|
+
var little = new BinaryWriter('little');
|
|
39
|
+
little.WriteUInt16(0x1234);
|
|
40
|
+
assert.deepEqual(bytes(little), [0x34, 0x12]);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
test('BinaryWriter: WriteUInt32 honors endianness', function() {
|
|
44
|
+
var big = new BinaryWriter('big');
|
|
45
|
+
big.WriteUInt32(0x12345678);
|
|
46
|
+
assert.deepEqual(bytes(big), [0x12, 0x34, 0x56, 0x78]);
|
|
47
|
+
assert.equal(big.Length, 4);
|
|
48
|
+
|
|
49
|
+
var little = new BinaryWriter('little');
|
|
50
|
+
little.WriteUInt32(0x12345678);
|
|
51
|
+
assert.deepEqual(bytes(little), [0x78, 0x56, 0x34, 0x12]);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
test('BinaryWriter: WriteInt16 / WriteInt32 (signed)', function() {
|
|
55
|
+
var w16 = new BinaryWriter('big');
|
|
56
|
+
w16.WriteInt16(-2);
|
|
57
|
+
assert.deepEqual(bytes(w16), [0xFF, 0xFE]);
|
|
58
|
+
|
|
59
|
+
var w32 = new BinaryWriter('little');
|
|
60
|
+
w32.WriteInt32(-1);
|
|
61
|
+
assert.deepEqual(bytes(w32), [0xFF, 0xFF, 0xFF, 0xFF]);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
test('BinaryWriter: WriteFloat', function() {
|
|
65
|
+
var big = new BinaryWriter('big');
|
|
66
|
+
big.WriteFloat(1.5);
|
|
67
|
+
assert.deepEqual(bytes(big), [0x3F, 0xC0, 0x00, 0x00]);
|
|
68
|
+
assert.equal(big.Length, 4);
|
|
69
|
+
|
|
70
|
+
var little = new BinaryWriter('little');
|
|
71
|
+
little.WriteFloat(1.5);
|
|
72
|
+
assert.deepEqual(bytes(little), [0x00, 0x00, 0xC0, 0x3F]);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
test('BinaryWriter: WriteDouble', function() {
|
|
76
|
+
var w = new BinaryWriter('big');
|
|
77
|
+
w.WriteDouble(1.5);
|
|
78
|
+
assert.deepEqual(bytes(w), [0x3F, 0xF8, 0, 0, 0, 0, 0, 0]);
|
|
79
|
+
assert.equal(w.Length, 8);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
test('BinaryWriter: WriteBytes accepts an array', function() {
|
|
83
|
+
var w = new BinaryWriter();
|
|
84
|
+
w.WriteBytes([1, 2, 3]);
|
|
85
|
+
assert.deepEqual(bytes(w), [1, 2, 3]);
|
|
86
|
+
assert.equal(w.Length, 3);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
test('BinaryWriter: WriteBytes accepts a Buffer', function() {
|
|
90
|
+
var w = new BinaryWriter();
|
|
91
|
+
w.WriteBytes(Buffer.from([9, 8, 7]));
|
|
92
|
+
assert.deepEqual(bytes(w), [9, 8, 7]);
|
|
93
|
+
assert.equal(w.Length, 3);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
test('BinaryWriter: WriteBytes accepts a string (per-char codes)', function() {
|
|
97
|
+
var w = new BinaryWriter();
|
|
98
|
+
w.WriteBytes('ABC');
|
|
99
|
+
assert.deepEqual(bytes(w), [65, 66, 67]);
|
|
100
|
+
assert.equal(w.Length, 3);
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
test('BinaryWriter: WriteBytes rejects non-Buffer/non-Array input', function() {
|
|
104
|
+
assert.throws(function() { new BinaryWriter().WriteBytes(4); }, /Invalid Buffer object/);
|
|
105
|
+
assert.throws(function() { new BinaryWriter().WriteBytes({}); }, /Invalid Buffer object/);
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
test('BinaryWriter: multiple writes accumulate and track Length', function() {
|
|
109
|
+
var w = new BinaryWriter('big');
|
|
110
|
+
w.WriteUInt8(1);
|
|
111
|
+
w.WriteUInt16(0x0203);
|
|
112
|
+
w.WriteUInt32(0x04050607);
|
|
113
|
+
assert.deepEqual(bytes(w), [1, 2, 3, 4, 5, 6, 7]);
|
|
114
|
+
assert.equal(w.Length, 7);
|
|
115
|
+
});
|