binutils64 0.1.3 → 0.2.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 +56 -0
- package/LICENSE +21 -0
- package/README.md +269 -163
- package/binutils.d.ts +83 -0
- package/binutils.js +23 -21
- package/package.json +54 -14
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
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.2.0] - 2026-07-02
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- MIT license (`LICENSE` file and `license` field in `package.json`).
|
|
13
|
+
- Bundled TypeScript type definitions (`binutils.d.ts`).
|
|
14
|
+
- `CHANGELOG.md` (this file).
|
|
15
|
+
- ESLint configuration that locks `binutils.js` to ES5 syntax, an `npm run lint`
|
|
16
|
+
script, and a CI lint job.
|
|
17
|
+
- GitHub Actions publish workflow that releases to npm with provenance.
|
|
18
|
+
- Package metadata: `files` whitelist, `exports` map, `keywords`, `bugs`, `homepage`.
|
|
19
|
+
- `.editorconfig`.
|
|
20
|
+
|
|
21
|
+
### Changed
|
|
22
|
+
|
|
23
|
+
- Raised the declared Node.js floor (`engines.node`) from `>=0.12` to `>=12`,
|
|
24
|
+
matching the actual requirement of the 64-bit `BigInt` methods.
|
|
25
|
+
- Replaced the deprecated `new Buffer(...)` constructor with
|
|
26
|
+
`Buffer.alloc()`/`Buffer.from()`; the `DEP0005` deprecation warning is gone.
|
|
27
|
+
No behavior change.
|
|
28
|
+
- `repository.url` now uses `git+https://` instead of the retired `git://` protocol.
|
|
29
|
+
|
|
30
|
+
## [0.1.4] - 2026-05-28
|
|
31
|
+
|
|
32
|
+
### Added
|
|
33
|
+
|
|
34
|
+
- Test suite using the built-in `node:test` runner: reader, writer, round-trip,
|
|
35
|
+
edge-case and README-example coverage.
|
|
36
|
+
- GitHub Actions CI running the tests on Node.js 20, 22 and 24.
|
|
37
|
+
|
|
38
|
+
## [0.1.3] - 2024-11-01
|
|
39
|
+
|
|
40
|
+
### Fixed
|
|
41
|
+
|
|
42
|
+
- `ReadInt64` returning incorrect values.
|
|
43
|
+
|
|
44
|
+
## [0.1.2] - 2018-06-15
|
|
45
|
+
|
|
46
|
+
### Changed
|
|
47
|
+
|
|
48
|
+
- README updates.
|
|
49
|
+
|
|
50
|
+
## [0.1.1] - 2018-06-15
|
|
51
|
+
|
|
52
|
+
### Added
|
|
53
|
+
|
|
54
|
+
- First release of `binutils64`: `BinaryReader` and `BinaryWriter` with selectable
|
|
55
|
+
endianness, signed/unsigned 8/16/32-bit integers, floats, doubles, raw byte runs,
|
|
56
|
+
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
|
@@ -1,163 +1,269 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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://www.npmjs.com/package/binutils64)
|
|
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)
|
|
12
|
+
|
|
13
|
+
`binutils64` provides two small classes — `BinaryReader` and `BinaryWriter` — that
|
|
14
|
+
make it easy to parse and produce binary data sequentially, with an API modelled on
|
|
15
|
+
the corresponding .NET classes. Both classes let you choose the byte order
|
|
16
|
+
(`big`- or `little`-endian) and support 8-, 16-, 32- and 64-bit integers, floats,
|
|
17
|
+
doubles, and raw byte runs.
|
|
18
|
+
|
|
19
|
+
## Table of contents
|
|
20
|
+
|
|
21
|
+
- [Features](#features)
|
|
22
|
+
- [Installation](#installation)
|
|
23
|
+
- [Quick start](#quick-start)
|
|
24
|
+
- [API reference](#api-reference)
|
|
25
|
+
- [BinaryReader](#binaryreader)
|
|
26
|
+
- [BinaryWriter](#binarywriter)
|
|
27
|
+
- [Behavior and best practices](#behavior-and-best-practices)
|
|
28
|
+
- [Examples](#examples)
|
|
29
|
+
- [Requirements and compatibility](#requirements-and-compatibility)
|
|
30
|
+
- [Testing](#testing)
|
|
31
|
+
- [Contributing](#contributing)
|
|
32
|
+
- [License](#license)
|
|
33
|
+
|
|
34
|
+
## Features
|
|
35
|
+
|
|
36
|
+
- Sequential `Read*` / `Write*` methods for every common fixed-width type.
|
|
37
|
+
- Per-instance endianness (`big` by default, or `little`).
|
|
38
|
+
- 64-bit integers via JavaScript `BigInt`.
|
|
39
|
+
- Signed and unsigned integers, IEEE-754 `float` and `double`, and raw byte runs.
|
|
40
|
+
- Zero runtime dependencies.
|
|
41
|
+
- Bundled TypeScript type definitions.
|
|
42
|
+
|
|
43
|
+
## Installation
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
npm install binutils64
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Then require it:
|
|
50
|
+
|
|
51
|
+
```javascript
|
|
52
|
+
const binutils = require('binutils64');
|
|
53
|
+
// const { BinaryReader, BinaryWriter } = require('binutils64');
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Quick start
|
|
57
|
+
|
|
58
|
+
```javascript
|
|
59
|
+
const { BinaryReader, BinaryWriter } = require('binutils64');
|
|
60
|
+
|
|
61
|
+
// --- Writing ---
|
|
62
|
+
const writer = new BinaryWriter(); // big-endian by default
|
|
63
|
+
writer.WriteUInt16(65535);
|
|
64
|
+
writer.WriteUInt32(0);
|
|
65
|
+
writer.WriteInt32(-1);
|
|
66
|
+
writer.WriteBytes([5, 4, 3, 2, 1]);
|
|
67
|
+
|
|
68
|
+
console.log(writer.ByteBuffer);
|
|
69
|
+
// <Buffer ff ff 00 00 00 00 ff ff ff ff 05 04 03 02 01>
|
|
70
|
+
console.log(writer.Length); // 15
|
|
71
|
+
|
|
72
|
+
// --- Reading ---
|
|
73
|
+
const reader = new BinaryReader(writer.ByteBuffer);
|
|
74
|
+
console.log(reader.ReadUInt16()); // 65535
|
|
75
|
+
console.log(reader.ReadUInt32()); // 0
|
|
76
|
+
console.log(reader.ReadInt32()); // -1
|
|
77
|
+
console.log(reader.ReadBytes(5)); // <Buffer 05 04 03 02 01>
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## API reference
|
|
81
|
+
|
|
82
|
+
### BinaryReader
|
|
83
|
+
|
|
84
|
+
A reader wraps an immutable copy of the input data and consumes it from the front as
|
|
85
|
+
you read.
|
|
86
|
+
|
|
87
|
+
#### `new BinaryReader(input, [endianness], [encoding])`
|
|
88
|
+
|
|
89
|
+
| Parameter | Type | Default | Description |
|
|
90
|
+
| ------------ | ----------------------------- | ------- | ------------------------------------------------------------------ |
|
|
91
|
+
| `input` | `Buffer` \| `number[]` \| `string` | — | The data to read. A `Buffer` is **copied** so the source is never mutated. |
|
|
92
|
+
| `endianness` | `'big'` \| `'little'` | `'big'` | Byte order used by all multi-byte reads. |
|
|
93
|
+
| `encoding` | `string` | `'ascii'` | Used only when `input` is a `string`, to turn it into bytes. |
|
|
94
|
+
|
|
95
|
+
Throws `Error` if `input` is not a `Buffer`, array, or string.
|
|
96
|
+
|
|
97
|
+
#### Read methods
|
|
98
|
+
|
|
99
|
+
| Method | Bytes | Returns | Notes |
|
|
100
|
+
| ----------------- | ----- | --------- | ---------------------------------------------- |
|
|
101
|
+
| `ReadUInt8()` | 1 | `number` | Unsigned. |
|
|
102
|
+
| `ReadInt8()` | 1 | `number` | Signed. |
|
|
103
|
+
| `ReadUInt16()` | 2 | `number` | Honors endianness. |
|
|
104
|
+
| `ReadInt16()` | 2 | `number` | Honors endianness. |
|
|
105
|
+
| `ReadUInt32()` | 4 | `number` | Honors endianness. |
|
|
106
|
+
| `ReadInt32()` | 4 | `number` | Honors endianness. |
|
|
107
|
+
| `ReadUInt64()` | 8 | `BigInt` | Honors endianness. Returns a `BigInt`. |
|
|
108
|
+
| `ReadInt64()` | 8 | `BigInt` | Honors endianness. Returns a `BigInt`. |
|
|
109
|
+
| `ReadFloat()` | 4 | `number` | IEEE-754 single precision. |
|
|
110
|
+
| `ReadDouble()` | 8 | `number` | IEEE-754 double precision. |
|
|
111
|
+
| `ReadBytes(count)`| `count` | `Buffer`| Copies `count` bytes into a new `Buffer`. |
|
|
112
|
+
|
|
113
|
+
If fewer than the required number of bytes remain, integer/float reads return `0`
|
|
114
|
+
(or `0.0`), and `ReadBytes` returns an empty `Buffer` — **without** throwing or
|
|
115
|
+
advancing the position. See [Behavior and best practices](#behavior-and-best-practices).
|
|
116
|
+
|
|
117
|
+
#### Reader properties
|
|
118
|
+
|
|
119
|
+
| Property | Type | Description |
|
|
120
|
+
| ------------ | -------- | ------------------------------------------------------------------- |
|
|
121
|
+
| `ByteBuffer` | `Buffer` | The **remaining** unread data. Shrinks as you read. |
|
|
122
|
+
| `Position` | `number` | Number of bytes consumed so far (starts at `0`). |
|
|
123
|
+
| `Length` | `number` | The length of the original input. Does **not** change as you read. |
|
|
124
|
+
| `Endianness` | `string` | `'big'` or `'little'`. |
|
|
125
|
+
| `Encoding` | `string` | The encoding passed to the constructor. |
|
|
126
|
+
|
|
127
|
+
### BinaryWriter
|
|
128
|
+
|
|
129
|
+
A writer accumulates bytes in an internal buffer that grows with every write.
|
|
130
|
+
|
|
131
|
+
#### `new BinaryWriter([endianness], [encoding])`
|
|
132
|
+
|
|
133
|
+
| Parameter | Type | Default | Description |
|
|
134
|
+
| ------------ | --------------------- | --------- | ---------------------------------------- |
|
|
135
|
+
| `endianness` | `'big'` \| `'little'` | `'big'` | Byte order used by all multi-byte writes.|
|
|
136
|
+
| `encoding` | `string` | `'ascii'` | Stored on the instance; reserved. |
|
|
137
|
+
|
|
138
|
+
#### Write methods
|
|
139
|
+
|
|
140
|
+
| Method | Bytes | Accepts | Notes |
|
|
141
|
+
| --------------------- | ----- | ------------------- | -------------------------------------------------- |
|
|
142
|
+
| `WriteUInt8(value)` | 1 | `number` | Unsigned. |
|
|
143
|
+
| `WriteInt8(value)` | 1 | `number` | Signed. |
|
|
144
|
+
| `WriteUInt16(value)` | 2 | `number` | Honors endianness. |
|
|
145
|
+
| `WriteInt16(value)` | 2 | `number` | Honors endianness. |
|
|
146
|
+
| `WriteUInt32(value)` | 4 | `number` | Honors endianness. |
|
|
147
|
+
| `WriteInt32(value)` | 4 | `number` | Honors endianness. |
|
|
148
|
+
| `WriteUInt64(value)` | 8 | `number` \| `BigInt`| Coerced with `BigInt(value)`; honors endianness. |
|
|
149
|
+
| `WriteInt64(value)` | 8 | `number` \| `BigInt`| Coerced with `BigInt(value)`; honors endianness. |
|
|
150
|
+
| `WriteFloat(value)` | 4 | `number` | IEEE-754 single precision. |
|
|
151
|
+
| `WriteDouble(value)` | 8 | `number` | IEEE-754 double precision. |
|
|
152
|
+
| `WriteBytes(value)` | varies| `Buffer` \| `number[]` \| `string` | Strings are written as one byte per character code. Throws on any other type. |
|
|
153
|
+
|
|
154
|
+
Writing a value outside the target type's range throws a `RangeError` (the standard
|
|
155
|
+
Node.js `Buffer` write behavior) — e.g. `WriteUInt8(256)`.
|
|
156
|
+
|
|
157
|
+
#### Writer properties
|
|
158
|
+
|
|
159
|
+
| Property | Type | Description |
|
|
160
|
+
| ------------ | -------- | -------------------------------------------- |
|
|
161
|
+
| `ByteBuffer` | `Buffer` | All bytes written so far. |
|
|
162
|
+
| `Length` | `number` | The current length of `ByteBuffer`. |
|
|
163
|
+
| `Endianness` | `string` | `'big'` or `'little'`. |
|
|
164
|
+
| `Encoding` | `string` | The encoding passed to the constructor. |
|
|
165
|
+
|
|
166
|
+
## Behavior and best practices
|
|
167
|
+
|
|
168
|
+
- **Reads are destructive.** Each `Read*` call consumes bytes from the front of
|
|
169
|
+
`ByteBuffer` and advances `Position`. If you need the original bytes again, keep
|
|
170
|
+
your own copy before reading. Create a fresh `BinaryReader` to start over.
|
|
171
|
+
- **`Length` vs. `Position`.** On a reader, `Length` is the original size and never
|
|
172
|
+
changes; `Position` tracks how much you have consumed. The bytes left to read are
|
|
173
|
+
`Length - Position` (also `ByteBuffer.length`).
|
|
174
|
+
- **Out-of-range reads do not throw.** Reading past the end returns `0` / `0.0` /
|
|
175
|
+
an empty `Buffer` and leaves `Position` unchanged. Check the remaining length
|
|
176
|
+
yourself if a short buffer should be treated as an error:
|
|
177
|
+
|
|
178
|
+
```javascript
|
|
179
|
+
if (reader.ByteBuffer.length < 4) {
|
|
180
|
+
throw new Error('truncated record');
|
|
181
|
+
}
|
|
182
|
+
const value = reader.ReadUInt32();
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
- **64-bit values are `BigInt`s.** `ReadUInt64`/`ReadInt64` always return a `BigInt`.
|
|
186
|
+
When writing, pass a `BigInt` for any value above `Number.MAX_SAFE_INTEGER`
|
|
187
|
+
(`2^53 - 1`) to avoid silent precision loss; smaller `number`s are accepted and
|
|
188
|
+
coerced automatically.
|
|
189
|
+
- **Set endianness once, at construction.** All multi-byte methods follow the
|
|
190
|
+
instance's `Endianness`; reader and writer must agree to round-trip correctly.
|
|
191
|
+
- **Constructing a reader from a string?** Pass the encoding explicitly
|
|
192
|
+
(e.g. `new BinaryReader(text, 'big', 'utf8')`) so the bytes are interpreted the
|
|
193
|
+
way you expect.
|
|
194
|
+
|
|
195
|
+
## Examples
|
|
196
|
+
|
|
197
|
+
### Round-tripping a 64-bit integer
|
|
198
|
+
|
|
199
|
+
```javascript
|
|
200
|
+
const { BinaryReader, BinaryWriter } = require('binutils64');
|
|
201
|
+
|
|
202
|
+
const writer = new BinaryWriter(); // big-endian
|
|
203
|
+
writer.WriteUInt64(0x1234567890ABCDEFn); // pass a BigInt for large values
|
|
204
|
+
console.log(writer.ByteBuffer); // <Buffer 12 34 56 78 90 ab cd ef>
|
|
205
|
+
|
|
206
|
+
const reader = new BinaryReader(writer.ByteBuffer);
|
|
207
|
+
console.log(reader.ReadUInt64()); // 1311768467294899695n
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
### Little-endian
|
|
211
|
+
|
|
212
|
+
```javascript
|
|
213
|
+
const { BinaryReader, BinaryWriter } = require('binutils64');
|
|
214
|
+
|
|
215
|
+
const writer = new BinaryWriter('little');
|
|
216
|
+
writer.WriteUInt32(0x01020304);
|
|
217
|
+
console.log(writer.ByteBuffer); // <Buffer 04 03 02 01>
|
|
218
|
+
|
|
219
|
+
const reader = new BinaryReader(writer.ByteBuffer, 'little');
|
|
220
|
+
console.log(reader.ReadUInt32().toString(16)); // "1020304"
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
### Parsing a structured record
|
|
224
|
+
|
|
225
|
+
```javascript
|
|
226
|
+
const { BinaryReader } = require('binutils64');
|
|
227
|
+
|
|
228
|
+
// type (u8), id (u32, big-endian), payload (6 bytes)
|
|
229
|
+
const reader = new BinaryReader(Buffer.from([1, 0, 0, 0, 3, 1, 2, 3, 4, 5, 6]));
|
|
230
|
+
|
|
231
|
+
const record = {
|
|
232
|
+
type: reader.ReadUInt8(), // 1
|
|
233
|
+
id: reader.ReadUInt32(), // 3
|
|
234
|
+
payload: reader.ReadBytes(6), // <Buffer 01 02 03 04 05 06>
|
|
235
|
+
};
|
|
236
|
+
|
|
237
|
+
console.log(record, 'read', reader.Position, 'of', reader.Length, 'bytes');
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
## Requirements and compatibility
|
|
241
|
+
|
|
242
|
+
- **Node.js 12 or newer** (declared in `package.json` `engines`). The 64-bit methods
|
|
243
|
+
(`ReadUInt64`, `ReadInt64`, `WriteUInt64`, `WriteInt64`) rely on `BigInt` and the
|
|
244
|
+
`Buffer` big-integer methods introduced in Node.js 12.
|
|
245
|
+
- **TypeScript typings are bundled** (`binutils.d.ts`) — no separate `@types`
|
|
246
|
+
package is needed.
|
|
247
|
+
- Running the test suite uses the built-in `node:test` runner, which requires
|
|
248
|
+
**Node.js 18 or newer**.
|
|
249
|
+
|
|
250
|
+
## Testing
|
|
251
|
+
|
|
252
|
+
```bash
|
|
253
|
+
npm test # runs `node --test`
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
The suite covers every reader/writer method, round-trips across both endiannesses,
|
|
257
|
+
edge cases (out-of-range reads, constructor input types, buffer-copy isolation), and
|
|
258
|
+
the documented examples. Continuous integration runs it on Node.js 20, 22 and 24.
|
|
259
|
+
|
|
260
|
+
## Contributing
|
|
261
|
+
|
|
262
|
+
Issues and pull requests are welcome. Please add or update tests for any behavioral
|
|
263
|
+
change, note it in `CHANGELOG.md` under *Unreleased*, and make sure `npm test` and
|
|
264
|
+
`npm run lint` pass before opening a pull request (run `npm ci` once to install the
|
|
265
|
+
linter).
|
|
266
|
+
|
|
267
|
+
## License
|
|
268
|
+
|
|
269
|
+
[MIT](LICENSE)
|
package/binutils.d.ts
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
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. */
|
|
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
|
+
}
|
|
54
|
+
|
|
55
|
+
class BinaryWriter {
|
|
56
|
+
/** Accumulates bytes in an internal buffer that grows with every write. */
|
|
57
|
+
constructor(endianness?: Endianness, encoding?: BufferEncoding);
|
|
58
|
+
|
|
59
|
+
/** All bytes written so far. */
|
|
60
|
+
ByteBuffer: Buffer;
|
|
61
|
+
/** Byte order used by all multi-byte writes. */
|
|
62
|
+
Endianness: Endianness;
|
|
63
|
+
/** Stored on the instance; reserved. */
|
|
64
|
+
Encoding: BufferEncoding;
|
|
65
|
+
/** The current length of `ByteBuffer`. */
|
|
66
|
+
Length: number;
|
|
67
|
+
|
|
68
|
+
WriteUInt8(value: number): void;
|
|
69
|
+
WriteUInt16(value: number): void;
|
|
70
|
+
WriteUInt32(value: number): void;
|
|
71
|
+
/** Accepts a number or BigInt; the value is coerced with `BigInt(value)`. */
|
|
72
|
+
WriteUInt64(value: number | bigint): void;
|
|
73
|
+
WriteInt8(value: number): void;
|
|
74
|
+
WriteInt16(value: number): void;
|
|
75
|
+
WriteInt32(value: number): void;
|
|
76
|
+
/** Accepts a number or BigInt; the value is coerced with `BigInt(value)`. */
|
|
77
|
+
WriteInt64(value: number | bigint): void;
|
|
78
|
+
WriteFloat(value: number): void;
|
|
79
|
+
WriteDouble(value: number): void;
|
|
80
|
+
/** Strings are written as one byte per character code. Throws on any other input type. */
|
|
81
|
+
WriteBytes(value: Buffer | ReadonlyArray<number> | string): void;
|
|
82
|
+
}
|
|
83
|
+
}
|
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,10 +134,10 @@ 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);
|
|
@@ -151,7 +151,7 @@ BinaryReader.prototype = {
|
|
|
151
151
|
|
|
152
152
|
var BinaryWriter = function(p_Endianness, p_Encoding) {
|
|
153
153
|
// Instantiate the buffer
|
|
154
|
-
this.ByteBuffer =
|
|
154
|
+
this.ByteBuffer = Buffer.alloc(0);
|
|
155
155
|
|
|
156
156
|
// Set the endianness
|
|
157
157
|
this.Endianness = p_Endianness || 'big';
|
|
@@ -165,14 +165,14 @@ var BinaryWriter = function(p_Endianness, p_Encoding) {
|
|
|
165
165
|
|
|
166
166
|
BinaryWriter.prototype = {
|
|
167
167
|
WriteUInt8: function(p_Value) {
|
|
168
|
-
var s_TempBuffer =
|
|
168
|
+
var s_TempBuffer = Buffer.alloc(1);
|
|
169
169
|
s_TempBuffer.writeUInt8(p_Value, 0);
|
|
170
170
|
this.Length += 1;
|
|
171
171
|
this.ByteBuffer = Buffer.concat([this.ByteBuffer, s_TempBuffer], this.Length);
|
|
172
172
|
},
|
|
173
173
|
|
|
174
174
|
WriteUInt16: function(p_Value) {
|
|
175
|
-
var s_TempBuffer =
|
|
175
|
+
var s_TempBuffer = Buffer.alloc(2);
|
|
176
176
|
if (this.Endianness == 'little') {
|
|
177
177
|
s_TempBuffer.writeUInt16LE(p_Value, 0);
|
|
178
178
|
} else {
|
|
@@ -183,7 +183,7 @@ BinaryWriter.prototype = {
|
|
|
183
183
|
},
|
|
184
184
|
|
|
185
185
|
WriteUInt32: function(p_Value) {
|
|
186
|
-
var s_TempBuffer =
|
|
186
|
+
var s_TempBuffer = Buffer.alloc(4);
|
|
187
187
|
if (this.Endianness == 'little') {
|
|
188
188
|
s_TempBuffer.writeUInt32LE(p_Value, 0);
|
|
189
189
|
} else {
|
|
@@ -194,25 +194,26 @@ BinaryWriter.prototype = {
|
|
|
194
194
|
},
|
|
195
195
|
|
|
196
196
|
WriteUInt64: function(p_Value) {
|
|
197
|
-
var s_TempBuffer =
|
|
197
|
+
var s_TempBuffer = Buffer.alloc(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);
|
|
205
206
|
},
|
|
206
207
|
|
|
207
208
|
WriteInt8: function(p_Value) {
|
|
208
|
-
var s_TempBuffer =
|
|
209
|
+
var s_TempBuffer = Buffer.alloc(1);
|
|
209
210
|
s_TempBuffer.writeInt8(p_Value, 0);
|
|
210
211
|
this.Length += 1;
|
|
211
212
|
this.ByteBuffer = Buffer.concat([this.ByteBuffer, s_TempBuffer], this.Length);
|
|
212
213
|
},
|
|
213
214
|
|
|
214
215
|
WriteInt16: function(p_Value) {
|
|
215
|
-
var s_TempBuffer =
|
|
216
|
+
var s_TempBuffer = Buffer.alloc(2);
|
|
216
217
|
if (this.Endianness == 'little') {
|
|
217
218
|
s_TempBuffer.writeInt16LE(p_Value, 0);
|
|
218
219
|
} else {
|
|
@@ -223,7 +224,7 @@ BinaryWriter.prototype = {
|
|
|
223
224
|
},
|
|
224
225
|
|
|
225
226
|
WriteInt32: function(p_Value) {
|
|
226
|
-
var s_TempBuffer =
|
|
227
|
+
var s_TempBuffer = Buffer.alloc(4);
|
|
227
228
|
if (this.Endianness == 'little') {
|
|
228
229
|
s_TempBuffer.writeInt32LE(p_Value, 0);
|
|
229
230
|
} else {
|
|
@@ -234,18 +235,19 @@ BinaryWriter.prototype = {
|
|
|
234
235
|
},
|
|
235
236
|
|
|
236
237
|
WriteInt64: function(p_Value) {
|
|
237
|
-
var s_TempBuffer =
|
|
238
|
+
var s_TempBuffer = Buffer.alloc(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);
|
|
245
247
|
},
|
|
246
248
|
|
|
247
249
|
WriteFloat: function(p_Value) {
|
|
248
|
-
var s_TempBuffer =
|
|
250
|
+
var s_TempBuffer = Buffer.alloc(4);
|
|
249
251
|
if (this.Endianness == 'little') {
|
|
250
252
|
s_TempBuffer.writeFloatLE(p_Value, 0);
|
|
251
253
|
} else {
|
|
@@ -256,7 +258,7 @@ BinaryWriter.prototype = {
|
|
|
256
258
|
},
|
|
257
259
|
|
|
258
260
|
WriteDouble: function(p_Value) {
|
|
259
|
-
var s_TempBuffer =
|
|
261
|
+
var s_TempBuffer = Buffer.alloc(8);
|
|
260
262
|
if (this.Endianness == 'little') {
|
|
261
263
|
s_TempBuffer.writeDoubleLE(p_Value, 0);
|
|
262
264
|
} else {
|
|
@@ -279,11 +281,11 @@ 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
|
|
|
286
|
-
var s_TempBuffer = (p_Value instanceof Buffer) ? p_Value :
|
|
288
|
+
var s_TempBuffer = (p_Value instanceof Buffer) ? p_Value : Buffer.from(p_Value);
|
|
287
289
|
|
|
288
290
|
this.Length += s_TempBuffer.length;
|
|
289
291
|
this.ByteBuffer = Buffer.concat([this.ByteBuffer, s_TempBuffer], this.Length);
|
package/package.json
CHANGED
|
@@ -1,14 +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
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "binutils64",
|
|
3
|
+
"version": "0.2.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
|
+
}
|