binutils64 0.1.4 → 0.3.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/CHANGELOG.md +65 -0
- package/LICENSE +21 -0
- package/README.md +48 -12
- package/binutils.d.ts +87 -0
- package/binutils.js +37 -16
- package/package.json +54 -17
- package/.claude/agents/binutils-reviewer.md +0 -58
- package/.claude/settings.local.json +0 -21
- package/.claude/skills/add-binary-type/SKILL.md +0 -93
- package/.claude/skills/test-binutils/SKILL.md +0 -91
- package/.github/workflows/test.yml +0 -22
- package/CLAUDE.md +0 -54
- package/test/edge-cases.test.js +0 -76
- package/test/reader.test.js +0 -106
- package/test/readme-examples.test.js +0 -33
- package/test/roundtrip.test.js +0 -120
- package/test/writer.test.js +0 -115
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [0.3.0] - 2026-07-04
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- `BinaryReader.ReadString(count)` and `BinaryWriter.WriteString(value)`: read and
|
|
13
|
+
write strings using the instance's `Encoding` (`'ascii'` by default). Reading
|
|
14
|
+
past the end returns an empty string without advancing; writing a non-string
|
|
15
|
+
throws.
|
|
16
|
+
|
|
17
|
+
## [0.2.0] - 2026-07-02
|
|
18
|
+
|
|
19
|
+
### Added
|
|
20
|
+
|
|
21
|
+
- MIT license (`LICENSE` file and `license` field in `package.json`).
|
|
22
|
+
- Bundled TypeScript type definitions (`binutils.d.ts`).
|
|
23
|
+
- `CHANGELOG.md` (this file).
|
|
24
|
+
- ESLint configuration that locks `binutils.js` to ES5 syntax, an `npm run lint`
|
|
25
|
+
script, and a CI lint job.
|
|
26
|
+
- GitHub Actions publish workflow that releases to npm with provenance.
|
|
27
|
+
- Package metadata: `files` whitelist, `exports` map, `keywords`, `bugs`, `homepage`.
|
|
28
|
+
- `.editorconfig`.
|
|
29
|
+
|
|
30
|
+
### Changed
|
|
31
|
+
|
|
32
|
+
- Raised the declared Node.js floor (`engines.node`) from `>=0.12` to `>=12`,
|
|
33
|
+
matching the actual requirement of the 64-bit `BigInt` methods.
|
|
34
|
+
- Replaced the deprecated `new Buffer(...)` constructor with
|
|
35
|
+
`Buffer.alloc()`/`Buffer.from()`; the `DEP0005` deprecation warning is gone.
|
|
36
|
+
No behavior change.
|
|
37
|
+
- `repository.url` now uses `git+https://` instead of the retired `git://` protocol.
|
|
38
|
+
|
|
39
|
+
## [0.1.4] - 2026-05-28
|
|
40
|
+
|
|
41
|
+
### Added
|
|
42
|
+
|
|
43
|
+
- Test suite using the built-in `node:test` runner: reader, writer, round-trip,
|
|
44
|
+
edge-case and README-example coverage.
|
|
45
|
+
- GitHub Actions CI running the tests on Node.js 20, 22 and 24.
|
|
46
|
+
|
|
47
|
+
## [0.1.3] - 2024-11-01
|
|
48
|
+
|
|
49
|
+
### Fixed
|
|
50
|
+
|
|
51
|
+
- `ReadInt64` returning incorrect values.
|
|
52
|
+
|
|
53
|
+
## [0.1.2] - 2018-06-15
|
|
54
|
+
|
|
55
|
+
### Changed
|
|
56
|
+
|
|
57
|
+
- README updates.
|
|
58
|
+
|
|
59
|
+
## [0.1.1] - 2018-06-15
|
|
60
|
+
|
|
61
|
+
### Added
|
|
62
|
+
|
|
63
|
+
- First release of `binutils64`: `BinaryReader` and `BinaryWriter` with selectable
|
|
64
|
+
endianness, signed/unsigned 8/16/32-bit integers, floats, doubles, raw byte runs,
|
|
65
|
+
and 64-bit integers via `BigInt`.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2013-2026 Mihail Shumilov
|
|
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
CHANGED
|
@@ -3,13 +3,18 @@
|
|
|
3
3
|
> A .NET-style `BinaryReader` and `BinaryWriter` for Node.js, with selectable endianness.
|
|
4
4
|
|
|
5
5
|
[](https://www.npmjs.com/package/binutils64)
|
|
6
|
+
[](https://www.npmjs.com/package/binutils64)
|
|
6
7
|
[](https://github.com/mihailShumilov/node-binutils/actions/workflows/test.yml)
|
|
8
|
+
[](https://www.npmjs.com/package/binutils64)
|
|
9
|
+
[](https://github.com/mihailShumilov/node-binutils/blob/master/LICENSE)
|
|
10
|
+
[](https://www.npmjs.com/package/binutils64)
|
|
11
|
+
[](https://packagephobia.com/result?p=binutils64)
|
|
7
12
|
|
|
8
13
|
`binutils64` provides two small classes — `BinaryReader` and `BinaryWriter` — that
|
|
9
14
|
make it easy to parse and produce binary data sequentially, with an API modelled on
|
|
10
15
|
the corresponding .NET classes. Both classes let you choose the byte order
|
|
11
16
|
(`big`- or `little`-endian) and support 8-, 16-, 32- and 64-bit integers, floats,
|
|
12
|
-
doubles,
|
|
17
|
+
doubles, raw byte runs, and encoded strings.
|
|
13
18
|
|
|
14
19
|
## Table of contents
|
|
15
20
|
|
|
@@ -24,14 +29,17 @@ doubles, and raw byte runs.
|
|
|
24
29
|
- [Requirements and compatibility](#requirements-and-compatibility)
|
|
25
30
|
- [Testing](#testing)
|
|
26
31
|
- [Contributing](#contributing)
|
|
32
|
+
- [License](#license)
|
|
27
33
|
|
|
28
34
|
## Features
|
|
29
35
|
|
|
30
36
|
- Sequential `Read*` / `Write*` methods for every common fixed-width type.
|
|
31
37
|
- Per-instance endianness (`big` by default, or `little`).
|
|
32
38
|
- 64-bit integers via JavaScript `BigInt`.
|
|
33
|
-
- Signed and unsigned integers, IEEE-754 `float` and `double`,
|
|
39
|
+
- Signed and unsigned integers, IEEE-754 `float` and `double`, raw byte runs, and
|
|
40
|
+
strings in any `Buffer` encoding.
|
|
34
41
|
- Zero runtime dependencies.
|
|
42
|
+
- Bundled TypeScript type definitions.
|
|
35
43
|
|
|
36
44
|
## Installation
|
|
37
45
|
|
|
@@ -83,7 +91,7 @@ you read.
|
|
|
83
91
|
| ------------ | ----------------------------- | ------- | ------------------------------------------------------------------ |
|
|
84
92
|
| `input` | `Buffer` \| `number[]` \| `string` | — | The data to read. A `Buffer` is **copied** so the source is never mutated. |
|
|
85
93
|
| `endianness` | `'big'` \| `'little'` | `'big'` | Byte order used by all multi-byte reads. |
|
|
86
|
-
| `encoding` | `string` | `'ascii'` | Used
|
|
94
|
+
| `encoding` | `string` | `'ascii'` | Used to turn a `string` input into bytes, and by `ReadString`. |
|
|
87
95
|
|
|
88
96
|
Throws `Error` if `input` is not a `Buffer`, array, or string.
|
|
89
97
|
|
|
@@ -102,10 +110,11 @@ Throws `Error` if `input` is not a `Buffer`, array, or string.
|
|
|
102
110
|
| `ReadFloat()` | 4 | `number` | IEEE-754 single precision. |
|
|
103
111
|
| `ReadDouble()` | 8 | `number` | IEEE-754 double precision. |
|
|
104
112
|
| `ReadBytes(count)`| `count` | `Buffer`| Copies `count` bytes into a new `Buffer`. |
|
|
113
|
+
| `ReadString(count)`| `count` | `string`| Decodes `count` bytes using the instance's `Encoding`. |
|
|
105
114
|
|
|
106
115
|
If fewer than the required number of bytes remain, integer/float reads return `0`
|
|
107
|
-
(or `0.0`), and `ReadBytes`
|
|
108
|
-
advancing the position. See [Behavior and best practices](#behavior-and-best-practices).
|
|
116
|
+
(or `0.0`), and `ReadBytes`/`ReadString` return an empty `Buffer`/`string` — **without**
|
|
117
|
+
throwing or advancing the position. See [Behavior and best practices](#behavior-and-best-practices).
|
|
109
118
|
|
|
110
119
|
#### Reader properties
|
|
111
120
|
|
|
@@ -126,7 +135,7 @@ A writer accumulates bytes in an internal buffer that grows with every write.
|
|
|
126
135
|
| Parameter | Type | Default | Description |
|
|
127
136
|
| ------------ | --------------------- | --------- | ---------------------------------------- |
|
|
128
137
|
| `endianness` | `'big'` \| `'little'` | `'big'` | Byte order used by all multi-byte writes.|
|
|
129
|
-
| `encoding` | `string` | `'ascii'` |
|
|
138
|
+
| `encoding` | `string` | `'ascii'` | Used by `WriteString` to encode strings. |
|
|
130
139
|
|
|
131
140
|
#### Write methods
|
|
132
141
|
|
|
@@ -143,6 +152,7 @@ A writer accumulates bytes in an internal buffer that grows with every write.
|
|
|
143
152
|
| `WriteFloat(value)` | 4 | `number` | IEEE-754 single precision. |
|
|
144
153
|
| `WriteDouble(value)` | 8 | `number` | IEEE-754 double precision. |
|
|
145
154
|
| `WriteBytes(value)` | varies| `Buffer` \| `number[]` \| `string` | Strings are written as one byte per character code. Throws on any other type. |
|
|
155
|
+
| `WriteString(value)` | varies| `string` | Encodes the string using the instance's `Encoding`. Throws on any other type. |
|
|
146
156
|
|
|
147
157
|
Writing a value outside the target type's range throws a `RangeError` (the standard
|
|
148
158
|
Node.js `Buffer` write behavior) — e.g. `WriteUInt8(256)`.
|
|
@@ -184,6 +194,11 @@ Node.js `Buffer` write behavior) — e.g. `WriteUInt8(256)`.
|
|
|
184
194
|
- **Constructing a reader from a string?** Pass the encoding explicitly
|
|
185
195
|
(e.g. `new BinaryReader(text, 'big', 'utf8')`) so the bytes are interpreted the
|
|
186
196
|
way you expect.
|
|
197
|
+
- **The default `'ascii'` encoding is 7-bit only.** Node.js clears the high bit of
|
|
198
|
+
every byte when decoding `'ascii'`, so text containing characters above `0x7F`
|
|
199
|
+
does **not** round-trip through `WriteString`/`ReadString` with the default
|
|
200
|
+
encoding. Pass `'utf8'` (or `'latin1'`) to both constructors when handling
|
|
201
|
+
non-ASCII text.
|
|
187
202
|
|
|
188
203
|
## Examples
|
|
189
204
|
|
|
@@ -213,6 +228,21 @@ const reader = new BinaryReader(writer.ByteBuffer, 'little');
|
|
|
213
228
|
console.log(reader.ReadUInt32().toString(16)); // "1020304"
|
|
214
229
|
```
|
|
215
230
|
|
|
231
|
+
### Writing and reading strings
|
|
232
|
+
|
|
233
|
+
```javascript
|
|
234
|
+
const { BinaryReader, BinaryWriter } = require('binutils64');
|
|
235
|
+
|
|
236
|
+
const text = 'héllo';
|
|
237
|
+
const writer = new BinaryWriter('big', 'utf8');
|
|
238
|
+
writer.WriteUInt8(Buffer.byteLength(text, 'utf8')); // length prefix: 6
|
|
239
|
+
writer.WriteString(text);
|
|
240
|
+
|
|
241
|
+
const reader = new BinaryReader(writer.ByteBuffer, 'big', 'utf8');
|
|
242
|
+
const length = reader.ReadUInt8(); // 6
|
|
243
|
+
console.log(reader.ReadString(length)); // "héllo"
|
|
244
|
+
```
|
|
245
|
+
|
|
216
246
|
### Parsing a structured record
|
|
217
247
|
|
|
218
248
|
```javascript
|
|
@@ -232,11 +262,11 @@ console.log(record, 'read', reader.Position, 'of', reader.Length, 'bytes');
|
|
|
232
262
|
|
|
233
263
|
## Requirements and compatibility
|
|
234
264
|
|
|
235
|
-
- **Node.js
|
|
236
|
-
|
|
237
|
-
`
|
|
238
|
-
-
|
|
239
|
-
|
|
265
|
+
- **Node.js 12 or newer** (declared in `package.json` `engines`). The 64-bit methods
|
|
266
|
+
(`ReadUInt64`, `ReadInt64`, `WriteUInt64`, `WriteInt64`) rely on `BigInt` and the
|
|
267
|
+
`Buffer` big-integer methods introduced in Node.js 12.
|
|
268
|
+
- **TypeScript typings are bundled** (`binutils.d.ts`) — no separate `@types`
|
|
269
|
+
package is needed.
|
|
240
270
|
- Running the test suite uses the built-in `node:test` runner, which requires
|
|
241
271
|
**Node.js 18 or newer**.
|
|
242
272
|
|
|
@@ -253,4 +283,10 @@ the documented examples. Continuous integration runs it on Node.js 20, 22 and 24
|
|
|
253
283
|
## Contributing
|
|
254
284
|
|
|
255
285
|
Issues and pull requests are welcome. Please add or update tests for any behavioral
|
|
256
|
-
change and make sure `npm test`
|
|
286
|
+
change, note it in `CHANGELOG.md` under *Unreleased*, and make sure `npm test` and
|
|
287
|
+
`npm run lint` pass before opening a pull request (run `npm ci` once to install the
|
|
288
|
+
linter).
|
|
289
|
+
|
|
290
|
+
## License
|
|
291
|
+
|
|
292
|
+
[MIT](LICENSE)
|
package/binutils.d.ts
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
|
|
3
|
+
export = binutils;
|
|
4
|
+
|
|
5
|
+
declare namespace binutils {
|
|
6
|
+
type Endianness = 'big' | 'little';
|
|
7
|
+
|
|
8
|
+
class BinaryReader {
|
|
9
|
+
/**
|
|
10
|
+
* Wraps a copy of the input data and consumes it from the front as you read.
|
|
11
|
+
* A `Buffer` input is copied, so the source is never mutated.
|
|
12
|
+
* Throws if `input` is not a `Buffer`, array, or string.
|
|
13
|
+
*/
|
|
14
|
+
constructor(
|
|
15
|
+
input: Buffer | ReadonlyArray<number> | string,
|
|
16
|
+
endianness?: Endianness,
|
|
17
|
+
encoding?: BufferEncoding
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
/** The remaining unread bytes. Shrinks as `Read*` methods consume it. */
|
|
21
|
+
ByteBuffer: Buffer;
|
|
22
|
+
/** Byte order used by all multi-byte reads. */
|
|
23
|
+
Endianness: Endianness;
|
|
24
|
+
/** Encoding used to turn a string input into bytes and to decode `ReadString`. */
|
|
25
|
+
Encoding: BufferEncoding;
|
|
26
|
+
/** Length of the original input in bytes. Never changes as you read. */
|
|
27
|
+
Length: number;
|
|
28
|
+
/** Number of bytes consumed so far. */
|
|
29
|
+
Position: number;
|
|
30
|
+
|
|
31
|
+
/** Reads 1 byte. Returns 0 (without advancing) if no bytes remain. */
|
|
32
|
+
ReadUInt8(): number;
|
|
33
|
+
/** Reads 2 bytes. Returns 0 (without advancing) if fewer than 2 bytes remain. */
|
|
34
|
+
ReadUInt16(): number;
|
|
35
|
+
/** Reads 4 bytes. Returns 0 (without advancing) if fewer than 4 bytes remain. */
|
|
36
|
+
ReadUInt32(): number;
|
|
37
|
+
/** Reads 8 bytes as an unsigned BigInt. Returns the number 0 (without advancing) if fewer than 8 bytes remain. */
|
|
38
|
+
ReadUInt64(): bigint | 0;
|
|
39
|
+
/** Reads 1 byte, signed. Returns 0 (without advancing) if no bytes remain. */
|
|
40
|
+
ReadInt8(): number;
|
|
41
|
+
/** Reads 2 bytes, signed. Returns 0 (without advancing) if fewer than 2 bytes remain. */
|
|
42
|
+
ReadInt16(): number;
|
|
43
|
+
/** Reads 4 bytes, signed. Returns 0 (without advancing) if fewer than 4 bytes remain. */
|
|
44
|
+
ReadInt32(): number;
|
|
45
|
+
/** Reads 8 bytes as a signed BigInt. Returns the number 0 (without advancing) if fewer than 8 bytes remain. */
|
|
46
|
+
ReadInt64(): bigint | 0;
|
|
47
|
+
/** Reads 4 bytes as an IEEE-754 single. Returns 0 (without advancing) if fewer than 4 bytes remain. */
|
|
48
|
+
ReadFloat(): number;
|
|
49
|
+
/** Reads 8 bytes as an IEEE-754 double. Returns 0 (without advancing) if fewer than 8 bytes remain. */
|
|
50
|
+
ReadDouble(): number;
|
|
51
|
+
/** Copies `count` bytes into a new Buffer. Returns an empty Buffer (without advancing) if fewer than `count` bytes remain. */
|
|
52
|
+
ReadBytes(count: number): Buffer;
|
|
53
|
+
/** Decodes `count` bytes as a string using the instance's `Encoding`. Returns an empty string (without advancing) if fewer than `count` bytes remain. */
|
|
54
|
+
ReadString(count: number): string;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
class BinaryWriter {
|
|
58
|
+
/** Accumulates bytes in an internal buffer that grows with every write. */
|
|
59
|
+
constructor(endianness?: Endianness, encoding?: BufferEncoding);
|
|
60
|
+
|
|
61
|
+
/** All bytes written so far. */
|
|
62
|
+
ByteBuffer: Buffer;
|
|
63
|
+
/** Byte order used by all multi-byte writes. */
|
|
64
|
+
Endianness: Endianness;
|
|
65
|
+
/** Encoding used by `WriteString`. */
|
|
66
|
+
Encoding: BufferEncoding;
|
|
67
|
+
/** The current length of `ByteBuffer`. */
|
|
68
|
+
Length: number;
|
|
69
|
+
|
|
70
|
+
WriteUInt8(value: number): void;
|
|
71
|
+
WriteUInt16(value: number): void;
|
|
72
|
+
WriteUInt32(value: number): void;
|
|
73
|
+
/** Accepts a number or BigInt; the value is coerced with `BigInt(value)`. */
|
|
74
|
+
WriteUInt64(value: number | bigint): void;
|
|
75
|
+
WriteInt8(value: number): void;
|
|
76
|
+
WriteInt16(value: number): void;
|
|
77
|
+
WriteInt32(value: number): void;
|
|
78
|
+
/** Accepts a number or BigInt; the value is coerced with `BigInt(value)`. */
|
|
79
|
+
WriteInt64(value: number | bigint): void;
|
|
80
|
+
WriteFloat(value: number): void;
|
|
81
|
+
WriteDouble(value: number): void;
|
|
82
|
+
/** Strings are written as one byte per character code. Throws on any other input type. */
|
|
83
|
+
WriteBytes(value: Buffer | ReadonlyArray<number> | string): void;
|
|
84
|
+
/** Encodes the string using the instance's `Encoding`. Throws on any other input type. */
|
|
85
|
+
WriteString(value: string): void;
|
|
86
|
+
}
|
|
87
|
+
}
|
package/binutils.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
var BinaryReader = function(p_InputBuffer, p_Endianness, p_Encoding) {
|
|
2
2
|
// Instantiate the buffer (if needed)
|
|
3
3
|
if (p_InputBuffer instanceof Buffer) {
|
|
4
|
-
this.ByteBuffer =
|
|
4
|
+
this.ByteBuffer = Buffer.from(p_InputBuffer);
|
|
5
5
|
} else if (p_InputBuffer instanceof Array || typeof p_InputBuffer == 'string') {
|
|
6
|
-
this.ByteBuffer =
|
|
6
|
+
this.ByteBuffer = Buffer.from(p_InputBuffer, p_Encoding);
|
|
7
7
|
} else {
|
|
8
8
|
throw new Error('Invalid buffer input for BinaryReader (' + typeof p_InputBuffer + ')');
|
|
9
9
|
}
|
|
@@ -134,14 +134,25 @@ BinaryReader.prototype = {
|
|
|
134
134
|
|
|
135
135
|
ReadBytes: function(p_Count) {
|
|
136
136
|
if (p_Count > this.ByteBuffer.length) {
|
|
137
|
-
return
|
|
137
|
+
return Buffer.alloc(0);
|
|
138
138
|
}
|
|
139
139
|
|
|
140
|
-
var s_Val =
|
|
140
|
+
var s_Val = Buffer.alloc(p_Count);
|
|
141
141
|
this.ByteBuffer.copy(s_Val, 0, 0, p_Count);
|
|
142
142
|
|
|
143
143
|
this.ByteBuffer = this.ByteBuffer.slice(p_Count);
|
|
144
144
|
|
|
145
|
+
this.Position += p_Count;
|
|
146
|
+
return s_Val;
|
|
147
|
+
},
|
|
148
|
+
|
|
149
|
+
ReadString: function(p_Count) {
|
|
150
|
+
if (p_Count <= 0 || p_Count > this.ByteBuffer.length) {
|
|
151
|
+
return '';
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
var s_Val = this.ByteBuffer.toString(this.Encoding, 0, p_Count);
|
|
155
|
+
this.ByteBuffer = this.ByteBuffer.slice(p_Count);
|
|
145
156
|
this.Position += p_Count;
|
|
146
157
|
return s_Val;
|
|
147
158
|
}
|
|
@@ -151,7 +162,7 @@ BinaryReader.prototype = {
|
|
|
151
162
|
|
|
152
163
|
var BinaryWriter = function(p_Endianness, p_Encoding) {
|
|
153
164
|
// Instantiate the buffer
|
|
154
|
-
this.ByteBuffer =
|
|
165
|
+
this.ByteBuffer = Buffer.alloc(0);
|
|
155
166
|
|
|
156
167
|
// Set the endianness
|
|
157
168
|
this.Endianness = p_Endianness || 'big';
|
|
@@ -165,14 +176,14 @@ var BinaryWriter = function(p_Endianness, p_Encoding) {
|
|
|
165
176
|
|
|
166
177
|
BinaryWriter.prototype = {
|
|
167
178
|
WriteUInt8: function(p_Value) {
|
|
168
|
-
var s_TempBuffer =
|
|
179
|
+
var s_TempBuffer = Buffer.alloc(1);
|
|
169
180
|
s_TempBuffer.writeUInt8(p_Value, 0);
|
|
170
181
|
this.Length += 1;
|
|
171
182
|
this.ByteBuffer = Buffer.concat([this.ByteBuffer, s_TempBuffer], this.Length);
|
|
172
183
|
},
|
|
173
184
|
|
|
174
185
|
WriteUInt16: function(p_Value) {
|
|
175
|
-
var s_TempBuffer =
|
|
186
|
+
var s_TempBuffer = Buffer.alloc(2);
|
|
176
187
|
if (this.Endianness == 'little') {
|
|
177
188
|
s_TempBuffer.writeUInt16LE(p_Value, 0);
|
|
178
189
|
} else {
|
|
@@ -183,7 +194,7 @@ BinaryWriter.prototype = {
|
|
|
183
194
|
},
|
|
184
195
|
|
|
185
196
|
WriteUInt32: function(p_Value) {
|
|
186
|
-
var s_TempBuffer =
|
|
197
|
+
var s_TempBuffer = Buffer.alloc(4);
|
|
187
198
|
if (this.Endianness == 'little') {
|
|
188
199
|
s_TempBuffer.writeUInt32LE(p_Value, 0);
|
|
189
200
|
} else {
|
|
@@ -194,7 +205,7 @@ BinaryWriter.prototype = {
|
|
|
194
205
|
},
|
|
195
206
|
|
|
196
207
|
WriteUInt64: function(p_Value) {
|
|
197
|
-
var s_TempBuffer =
|
|
208
|
+
var s_TempBuffer = Buffer.alloc(8);
|
|
198
209
|
var s_Value = BigInt(p_Value);
|
|
199
210
|
if (this.Endianness == 'little') {
|
|
200
211
|
s_TempBuffer.writeBigUInt64LE(s_Value, 0);
|
|
@@ -206,14 +217,14 @@ BinaryWriter.prototype = {
|
|
|
206
217
|
},
|
|
207
218
|
|
|
208
219
|
WriteInt8: function(p_Value) {
|
|
209
|
-
var s_TempBuffer =
|
|
220
|
+
var s_TempBuffer = Buffer.alloc(1);
|
|
210
221
|
s_TempBuffer.writeInt8(p_Value, 0);
|
|
211
222
|
this.Length += 1;
|
|
212
223
|
this.ByteBuffer = Buffer.concat([this.ByteBuffer, s_TempBuffer], this.Length);
|
|
213
224
|
},
|
|
214
225
|
|
|
215
226
|
WriteInt16: function(p_Value) {
|
|
216
|
-
var s_TempBuffer =
|
|
227
|
+
var s_TempBuffer = Buffer.alloc(2);
|
|
217
228
|
if (this.Endianness == 'little') {
|
|
218
229
|
s_TempBuffer.writeInt16LE(p_Value, 0);
|
|
219
230
|
} else {
|
|
@@ -224,7 +235,7 @@ BinaryWriter.prototype = {
|
|
|
224
235
|
},
|
|
225
236
|
|
|
226
237
|
WriteInt32: function(p_Value) {
|
|
227
|
-
var s_TempBuffer =
|
|
238
|
+
var s_TempBuffer = Buffer.alloc(4);
|
|
228
239
|
if (this.Endianness == 'little') {
|
|
229
240
|
s_TempBuffer.writeInt32LE(p_Value, 0);
|
|
230
241
|
} else {
|
|
@@ -235,7 +246,7 @@ BinaryWriter.prototype = {
|
|
|
235
246
|
},
|
|
236
247
|
|
|
237
248
|
WriteInt64: function(p_Value) {
|
|
238
|
-
var s_TempBuffer =
|
|
249
|
+
var s_TempBuffer = Buffer.alloc(8);
|
|
239
250
|
var s_Value = BigInt(p_Value);
|
|
240
251
|
if (this.Endianness == 'little') {
|
|
241
252
|
s_TempBuffer.writeBigInt64LE(s_Value, 0);
|
|
@@ -247,7 +258,7 @@ BinaryWriter.prototype = {
|
|
|
247
258
|
},
|
|
248
259
|
|
|
249
260
|
WriteFloat: function(p_Value) {
|
|
250
|
-
var s_TempBuffer =
|
|
261
|
+
var s_TempBuffer = Buffer.alloc(4);
|
|
251
262
|
if (this.Endianness == 'little') {
|
|
252
263
|
s_TempBuffer.writeFloatLE(p_Value, 0);
|
|
253
264
|
} else {
|
|
@@ -258,7 +269,7 @@ BinaryWriter.prototype = {
|
|
|
258
269
|
},
|
|
259
270
|
|
|
260
271
|
WriteDouble: function(p_Value) {
|
|
261
|
-
var s_TempBuffer =
|
|
272
|
+
var s_TempBuffer = Buffer.alloc(8);
|
|
262
273
|
if (this.Endianness == 'little') {
|
|
263
274
|
s_TempBuffer.writeDoubleLE(p_Value, 0);
|
|
264
275
|
} else {
|
|
@@ -285,8 +296,18 @@ BinaryWriter.prototype = {
|
|
|
285
296
|
throw new Error("Invalid Buffer object provided.");
|
|
286
297
|
}
|
|
287
298
|
|
|
288
|
-
var s_TempBuffer = (p_Value instanceof Buffer) ? p_Value :
|
|
299
|
+
var s_TempBuffer = (p_Value instanceof Buffer) ? p_Value : Buffer.from(p_Value);
|
|
300
|
+
|
|
301
|
+
this.Length += s_TempBuffer.length;
|
|
302
|
+
this.ByteBuffer = Buffer.concat([this.ByteBuffer, s_TempBuffer], this.Length);
|
|
303
|
+
},
|
|
304
|
+
|
|
305
|
+
WriteString: function(p_Value) {
|
|
306
|
+
if (typeof p_Value != 'string') {
|
|
307
|
+
throw new Error("Invalid string provided.");
|
|
308
|
+
}
|
|
289
309
|
|
|
310
|
+
var s_TempBuffer = Buffer.from(p_Value, this.Encoding);
|
|
290
311
|
this.Length += s_TempBuffer.length;
|
|
291
312
|
this.ByteBuffer = Buffer.concat([this.ByteBuffer, s_TempBuffer], this.Length);
|
|
292
313
|
}
|
package/package.json
CHANGED
|
@@ -1,17 +1,54 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "binutils64",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"author": "Mihail Shumilov <mschumilow@gmail.com>",
|
|
5
|
-
"description": "A .NET-like BinaryReader and BinaryWriter with endianness support.",
|
|
6
|
-
"
|
|
7
|
-
"
|
|
8
|
-
"
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
"
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
"
|
|
15
|
-
"
|
|
16
|
-
|
|
17
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "binutils64",
|
|
3
|
+
"version": "0.3.0",
|
|
4
|
+
"author": "Mihail Shumilov <mschumilow@gmail.com>",
|
|
5
|
+
"description": "A .NET-like BinaryReader and BinaryWriter with endianness support.",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"keywords": [
|
|
8
|
+
"binary",
|
|
9
|
+
"buffer",
|
|
10
|
+
"binaryreader",
|
|
11
|
+
"binarywriter",
|
|
12
|
+
"reader",
|
|
13
|
+
"writer",
|
|
14
|
+
"endianness",
|
|
15
|
+
"endian",
|
|
16
|
+
"bigint",
|
|
17
|
+
"int64",
|
|
18
|
+
"uint64",
|
|
19
|
+
"parse",
|
|
20
|
+
"serialize"
|
|
21
|
+
],
|
|
22
|
+
"main": "./binutils.js",
|
|
23
|
+
"types": "./binutils.d.ts",
|
|
24
|
+
"exports": {
|
|
25
|
+
".": {
|
|
26
|
+
"types": "./binutils.d.ts",
|
|
27
|
+
"default": "./binutils.js"
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
"files": [
|
|
31
|
+
"binutils.js",
|
|
32
|
+
"binutils.d.ts",
|
|
33
|
+
"CHANGELOG.md"
|
|
34
|
+
],
|
|
35
|
+
"scripts": {
|
|
36
|
+
"test": "node --test",
|
|
37
|
+
"lint": "eslint ."
|
|
38
|
+
},
|
|
39
|
+
"engines": {
|
|
40
|
+
"node": ">=12"
|
|
41
|
+
},
|
|
42
|
+
"repository": {
|
|
43
|
+
"type": "git",
|
|
44
|
+
"url": "git+https://github.com/mihailShumilov/node-binutils.git"
|
|
45
|
+
},
|
|
46
|
+
"bugs": {
|
|
47
|
+
"url": "https://github.com/mihailShumilov/node-binutils/issues"
|
|
48
|
+
},
|
|
49
|
+
"homepage": "https://github.com/mihailShumilov/node-binutils#readme",
|
|
50
|
+
"devDependencies": {
|
|
51
|
+
"@eslint/js": "^10.0.1",
|
|
52
|
+
"eslint": "^10.6.0"
|
|
53
|
+
}
|
|
54
|
+
}
|
|
@@ -1,58 +0,0 @@
|
|
|
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.
|
|
@@ -1,21 +0,0 @@
|
|
|
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
|
-
}
|