Skip to content

Commit 3d8532f

Browse files
seishunmcollina
authored andcommitted
buffer: add {read|write}Big[U]Int64{BE|LE} methods
PR-URL: #19691 Reviewed-By: Ben Noordhuis <[email protected]> Reviewed-By: Tiancheng "Timothy" Gu <[email protected]> Reviewed-By: Joyee Cheung <[email protected]> Reviewed-By: James M Snell <[email protected]> Reviewed-By: Ruben Bridgewater <[email protected]> Reviewed-By: Tobias Nießen <[email protected]> Reviewed-By: Matteo Collina <[email protected]>
1 parent dd89a11 commit 3d8532f

File tree

5 files changed

+313
-0
lines changed

5 files changed

+313
-0
lines changed

benchmark/buffers/buffer-read.js

+4
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22
const common = require('../common.js');
33

44
const types = [
5+
'BigUInt64LE',
6+
'BigUInt64BE',
7+
'BigInt64LE',
8+
'BigInt64BE',
59
'UInt8',
610
'UInt16LE',
711
'UInt16BE',

benchmark/buffers/buffer-write.js

+21
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22
const common = require('../common.js');
33

44
const types = [
5+
'BigUInt64LE',
6+
'BigUInt64BE',
7+
'BigInt64LE',
8+
'BigInt64BE',
59
'UInt8',
610
'UInt16LE',
711
'UInt16BE',
@@ -32,11 +36,17 @@ const INT8 = 0x7f;
3236
const INT16 = 0x7fff;
3337
const INT32 = 0x7fffffff;
3438
const INT48 = 0x7fffffffffff;
39+
const INT64 = 0x7fffffffffffffffn;
3540
const UINT8 = 0xff;
3641
const UINT16 = 0xffff;
3742
const UINT32 = 0xffffffff;
43+
const UINT64 = 0xffffffffffffffffn;
3844

3945
const mod = {
46+
writeBigInt64BE: INT64,
47+
writeBigInt64LE: INT64,
48+
writeBigUInt64BE: UINT64,
49+
writeBigUInt64LE: UINT64,
4050
writeInt8: INT8,
4151
writeInt16BE: INT16,
4252
writeInt16LE: INT16,
@@ -67,12 +77,23 @@ function main({ n, buf, type }) {
6777

6878
if (!/\d/.test(fn))
6979
benchSpecialInt(buff, fn, n);
80+
else if (/BigU?Int/.test(fn))
81+
benchBigInt(buff, fn, BigInt(n));
7082
else if (/Int/.test(fn))
7183
benchInt(buff, fn, n);
7284
else
7385
benchFloat(buff, fn, n);
7486
}
7587

88+
function benchBigInt(buff, fn, n) {
89+
const m = mod[fn];
90+
bench.start();
91+
for (var i = 0n; i !== n; i++) {
92+
buff[fn](i & m, 0);
93+
}
94+
bench.end(Number(n));
95+
}
96+
7697
function benchInt(buff, fn, n) {
7798
const m = mod[fn];
7899
bench.start();

doc/api/buffer.md

+90
Original file line numberDiff line numberDiff line change
@@ -1546,6 +1546,46 @@ deprecated: v8.0.0
15461546
15471547
The `buf.parent` property is a deprecated alias for `buf.buffer`.
15481548

1549+
### buf.readBigInt64BE([offset])
1550+
### buf.readBigInt64LE([offset])
1551+
<!-- YAML
1552+
added: REPLACEME
1553+
-->
1554+
1555+
* `offset` {integer} Number of bytes to skip before starting to read. Must
1556+
satisfy: `0 <= offset <= buf.length - 8`. **Default:** `0`.
1557+
* Returns: {bigint}
1558+
1559+
Reads a signed 64-bit integer from `buf` at the specified `offset` with
1560+
the specified endian format (`readBigInt64BE()` returns big endian,
1561+
`readBigInt64LE()` returns little endian).
1562+
1563+
Integers read from a `Buffer` are interpreted as two's complement signed values.
1564+
1565+
### buf.readBigUInt64BE([offset])
1566+
### buf.readBigUInt64LE([offset])
1567+
<!-- YAML
1568+
added: REPLACEME
1569+
-->
1570+
1571+
* `offset` {integer} Number of bytes to skip before starting to read. Must
1572+
satisfy: `0 <= offset <= buf.length - 8`. **Default:** `0`.
1573+
* Returns: {bigint}
1574+
1575+
Reads an unsigned 64-bit integer from `buf` at the specified `offset` with
1576+
specified endian format (`readBigUInt64BE()` returns big endian,
1577+
`readBigUInt64LE()` returns little endian).
1578+
1579+
```js
1580+
const buf = Buffer.from([0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff]);
1581+
1582+
console.log(buf.readBigUInt64BE(0));
1583+
// Prints: 4294967295n
1584+
1585+
console.log(buf.readBigUInt64LE(0));
1586+
// Prints: 18446744069414584320n
1587+
```
1588+
15491589
### buf.readDoubleBE([offset])
15501590
### buf.readDoubleLE([offset])
15511591
<!-- YAML
@@ -2149,6 +2189,56 @@ console.log(`${len} bytes: ${buf.toString('utf8', 0, len)}`);
21492189
// Prints: 12 bytes: ½ + ¼ = ¾
21502190
```
21512191

2192+
### buf.writeBigInt64BE(value[, offset])
2193+
### buf.writeBigInt64LE(value[, offset])
2194+
<!-- YAML
2195+
added: REPLACEME
2196+
-->
2197+
2198+
* `value` {bigint} Number to be written to `buf`.
2199+
* `offset` {integer} Number of bytes to skip before starting to write. Must
2200+
satisfy: `0 <= offset <= buf.length - 8`. **Default:** `0`.
2201+
* Returns: {integer} `offset` plus the number of bytes written.
2202+
2203+
Writes `value` to `buf` at the specified `offset` with specified endian
2204+
format (`writeBigInt64BE()` writes big endian, `writeBigInt64LE()` writes little
2205+
endian).
2206+
2207+
`value` is interpreted and written as a two's complement signed integer.
2208+
2209+
```js
2210+
const buf = Buffer.allocUnsafe(8);
2211+
2212+
buf.writeBigInt64BE(0x0102030405060708n, 0);
2213+
2214+
console.log(buf);
2215+
// Prints: <Buffer 01 02 03 04 05 06 07 08>
2216+
```
2217+
2218+
### buf.writeBigUInt64BE(value[, offset])
2219+
### buf.writeBigUInt64LE(value[, offset])
2220+
<!-- YAML
2221+
added: REPLACEME
2222+
-->
2223+
2224+
* `value` {bigint} Number to be written to `buf`.
2225+
* `offset` {integer} Number of bytes to skip before starting to write. Must
2226+
satisfy: `0 <= offset <= buf.length - 8`. **Default:** `0`.
2227+
* Returns: {integer} `offset` plus the number of bytes written.
2228+
2229+
Writes `value` to `buf` at the specified `offset` with specified endian
2230+
format (`writeBigUInt64BE()` writes big endian, `writeBigUInt64LE()` writes
2231+
little endian).
2232+
2233+
```js
2234+
const buf = Buffer.allocUnsafe(8);
2235+
2236+
buf.writeBigUInt64LE(0xdecafafecacefaden, 0);
2237+
2238+
console.log(buf);
2239+
// Prints: <Buffer de fa ce ca fe fa ca de>
2240+
```
2241+
21522242
### buf.writeDoubleBE(value[, offset])
21532243
### buf.writeDoubleLE(value[, offset])
21542244
<!-- YAML

lib/internal/buffer.js

+147
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,82 @@ function boundsError(value, length, type) {
6363
}
6464

6565
// Read integers.
66+
function readBigUInt64LE(offset = 0) {
67+
validateNumber(offset, 'offset');
68+
const first = this[offset];
69+
const last = this[offset + 7];
70+
if (first === undefined || last === undefined)
71+
boundsError(offset, this.length - 8);
72+
73+
const lo = first +
74+
this[++offset] * 2 ** 8 +
75+
this[++offset] * 2 ** 16 +
76+
this[++offset] * 2 ** 24;
77+
78+
const hi = this[++offset] +
79+
this[++offset] * 2 ** 8 +
80+
this[++offset] * 2 ** 16 +
81+
last * 2 ** 24;
82+
83+
return BigInt(lo) + (BigInt(hi) << 32n);
84+
}
85+
86+
function readBigUInt64BE(offset = 0) {
87+
validateNumber(offset, 'offset');
88+
const first = this[offset];
89+
const last = this[offset + 7];
90+
if (first === undefined || last === undefined)
91+
boundsError(offset, this.length - 8);
92+
93+
const hi = first * 2 ** 24 +
94+
this[++offset] * 2 ** 16 +
95+
this[++offset] * 2 ** 8 +
96+
this[++offset];
97+
98+
const lo = this[++offset] * 2 ** 24 +
99+
this[++offset] * 2 ** 16 +
100+
this[++offset] * 2 ** 8 +
101+
last;
102+
103+
return (BigInt(hi) << 32n) + BigInt(lo);
104+
}
105+
106+
function readBigInt64LE(offset = 0) {
107+
validateNumber(offset, 'offset');
108+
const first = this[offset];
109+
const last = this[offset + 7];
110+
if (first === undefined || last === undefined)
111+
boundsError(offset, this.length - 8);
112+
113+
const val = this[offset + 4] +
114+
this[offset + 5] * 2 ** 8 +
115+
this[offset + 6] * 2 ** 16 +
116+
(last << 24); // Overflow
117+
return (BigInt(val) << 32n) +
118+
BigInt(first +
119+
this[++offset] * 2 ** 8 +
120+
this[++offset] * 2 ** 16 +
121+
this[++offset] * 2 ** 24);
122+
}
123+
124+
function readBigInt64BE(offset = 0) {
125+
validateNumber(offset, 'offset');
126+
const first = this[offset];
127+
const last = this[offset + 7];
128+
if (first === undefined || last === undefined)
129+
boundsError(offset, this.length - 8);
130+
131+
const val = (first << 24) + // Overflow
132+
this[++offset] * 2 ** 16 +
133+
this[++offset] * 2 ** 8 +
134+
this[++offset];
135+
return (BigInt(val) << 32n) +
136+
BigInt(this[++offset] * 2 ** 24 +
137+
this[++offset] * 2 ** 16 +
138+
this[++offset] * 2 ** 8 +
139+
last);
140+
}
141+
66142
function readUIntLE(offset, byteLength) {
67143
if (offset === undefined)
68144
throw new ERR_INVALID_ARG_TYPE('offset', 'number', offset);
@@ -473,6 +549,68 @@ function readDoubleForwards(offset = 0) {
473549
}
474550

475551
// Write integers.
552+
function writeBigU_Int64LE(buf, value, offset, min, max) {
553+
checkInt(value, min, max, buf, offset, 7);
554+
555+
let lo = Number(value & 0xffffffffn);
556+
buf[offset++] = lo;
557+
lo = lo >> 8;
558+
buf[offset++] = lo;
559+
lo = lo >> 8;
560+
buf[offset++] = lo;
561+
lo = lo >> 8;
562+
buf[offset++] = lo;
563+
let hi = Number(value >> 32n & 0xffffffffn);
564+
buf[offset++] = hi;
565+
hi = hi >> 8;
566+
buf[offset++] = hi;
567+
hi = hi >> 8;
568+
buf[offset++] = hi;
569+
hi = hi >> 8;
570+
buf[offset++] = hi;
571+
return offset;
572+
}
573+
574+
function writeBigUInt64LE(value, offset = 0) {
575+
return writeBigU_Int64LE(this, value, offset, 0n, 0xffffffffffffffffn);
576+
}
577+
578+
function writeBigU_Int64BE(buf, value, offset, min, max) {
579+
checkInt(value, min, max, buf, offset, 7);
580+
581+
let lo = Number(value & 0xffffffffn);
582+
buf[offset + 7] = lo;
583+
lo = lo >> 8;
584+
buf[offset + 6] = lo;
585+
lo = lo >> 8;
586+
buf[offset + 5] = lo;
587+
lo = lo >> 8;
588+
buf[offset + 4] = lo;
589+
let hi = Number(value >> 32n & 0xffffffffn);
590+
buf[offset + 3] = hi;
591+
hi = hi >> 8;
592+
buf[offset + 2] = hi;
593+
hi = hi >> 8;
594+
buf[offset + 1] = hi;
595+
hi = hi >> 8;
596+
buf[offset] = hi;
597+
return offset + 8;
598+
}
599+
600+
function writeBigUInt64BE(value, offset = 0) {
601+
return writeBigU_Int64BE(this, value, offset, 0n, 0xffffffffffffffffn);
602+
}
603+
604+
function writeBigInt64LE(value, offset = 0) {
605+
return writeBigU_Int64LE(
606+
this, value, offset, -0x8000000000000000n, 0x7fffffffffffffffn);
607+
}
608+
609+
function writeBigInt64BE(value, offset = 0) {
610+
return writeBigU_Int64BE(
611+
this, value, offset, -0x8000000000000000n, 0x7fffffffffffffffn);
612+
}
613+
476614
function writeUIntLE(value, offset, byteLength) {
477615
if (byteLength === 6)
478616
return writeU_Int48LE(this, value, offset, 0, 0xffffffffffff);
@@ -790,6 +928,15 @@ function writeFloatBackwards(val, offset = 0) {
790928
class FastBuffer extends Uint8Array {}
791929

792930
function addBufferPrototypeMethods(proto) {
931+
proto.readBigUInt64LE = readBigUInt64LE,
932+
proto.readBigUInt64BE = readBigUInt64BE,
933+
proto.readBigInt64LE = readBigInt64LE,
934+
proto.readBigInt64BE = readBigInt64BE,
935+
proto.writeBigUInt64LE = writeBigUInt64LE,
936+
proto.writeBigUInt64BE = writeBigUInt64BE,
937+
proto.writeBigInt64LE = writeBigInt64LE,
938+
proto.writeBigInt64BE = writeBigInt64BE,
939+
793940
proto.readUIntLE = readUIntLE;
794941
proto.readUInt32LE = readUInt32LE;
795942
proto.readUInt16LE = readUInt16LE;

test/parallel/test-buffer-bigint64.js

+51
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
'use strict';
2+
require('../common');
3+
const assert = require('assert');
4+
5+
const buf = Buffer.allocUnsafe(8);
6+
7+
['LE', 'BE'].forEach(function(endianness) {
8+
// Should allow simple BigInts to be written and read
9+
let val = 123456789n;
10+
buf['writeBigInt64' + endianness](val, 0);
11+
let rtn = buf['readBigInt64' + endianness](0);
12+
assert.strictEqual(val, rtn);
13+
14+
// Should allow INT64_MAX to be written and read
15+
val = 0x7fffffffffffffffn;
16+
buf['writeBigInt64' + endianness](val, 0);
17+
rtn = buf['readBigInt64' + endianness](0);
18+
assert.strictEqual(val, rtn);
19+
20+
// Should read and write a negative signed 64-bit integer
21+
val = -123456789n;
22+
buf['writeBigInt64' + endianness](val, 0);
23+
assert.strictEqual(val, buf['readBigInt64' + endianness](0));
24+
25+
// Should read and write an unsigned 64-bit integer
26+
val = 123456789n;
27+
buf['writeBigUInt64' + endianness](val, 0);
28+
assert.strictEqual(val, buf['readBigUInt64' + endianness](0));
29+
30+
// Should throw a RangeError upon INT64_MAX+1 being written
31+
assert.throws(function() {
32+
const val = 0x8000000000000000n;
33+
buf['writeBigInt64' + endianness](val, 0);
34+
}, RangeError);
35+
36+
// Should throw a RangeError upon UINT64_MAX+1 being written
37+
assert.throws(function() {
38+
const val = 0x10000000000000000n;
39+
buf['writeBigUInt64' + endianness](val, 0);
40+
}, RangeError);
41+
42+
// Should throw a TypeError upon invalid input
43+
assert.throws(function() {
44+
buf['writeBigInt64' + endianness]('bad', 0);
45+
}, TypeError);
46+
47+
// Should throw a TypeError upon invalid input
48+
assert.throws(function() {
49+
buf['writeBigUInt64' + endianness]('bad', 0);
50+
}, TypeError);
51+
});

0 commit comments

Comments
 (0)