Skip to content

Commit 64744a2

Browse files
GaryGSCBethGriggs
authored andcommittedFeb 25, 2020
buffer: add {read|write}Big[U]Int64{BE|LE} methods
Backport-PR-URL: #30361 PR-URL: #19691 Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl> Reviewed-By: Tiancheng "Timothy" Gu <timothygu99@gmail.com> Reviewed-By: Joyee Cheung <joyeec9h3@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de> Reviewed-By: Tobias Nießen <tniessen@tnie.de> Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
1 parent 1cfb457 commit 64744a2

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

+91
Original file line numberDiff line numberDiff line change
@@ -1529,6 +1529,47 @@ deprecated: v8.0.0
15291529
15301530
The `buf.parent` property is a deprecated alias for `buf.buffer`.
15311531

1532+
### buf.readBigInt64BE(offset)
1533+
### buf.readBigInt64LE(offset)
1534+
<!-- YAML
1535+
added: REPLACEME
1536+
-->
1537+
1538+
* `offset` {integer} Number of bytes to skip before starting to read. Must
1539+
satisfy: `0 <= offset <= buf.length - 8`. **Default:** `0`.
1540+
* Returns: {bigint}
1541+
1542+
Reads a signed 64-bit integer from `buf` at the specified `offset` with
1543+
the specified endian format (`readBigInt64BE()` returns big endian,
1544+
`readBigInt64LE()` returns little endian).
1545+
1546+
Integers read from a `Buffer` are interpreted as two's complement signed values.
1547+
1548+
### buf.readBigUInt64BE(offset)
1549+
### buf.readBigUInt64LE(offset)
1550+
<!-- YAML
1551+
added: REPLACEME
1552+
-->
1553+
1554+
* `offset` {integer} Number of bytes to skip before starting to read. Must
1555+
satisfy: `0 <= offset <= buf.length - 8`. **Default:** `0`.
1556+
* Returns: {bigint}
1557+
1558+
Reads an unsigned 64-bit integer from `buf` at the specified `offset` with
1559+
specified endian format (`readBigUInt64BE()` returns big endian,
1560+
`readBigUInt64LE()` returns little endian).
1561+
1562+
```js
1563+
const buf = Buffer.from([0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff]);
1564+
1565+
console.log(buf.readBigUInt64BE(0));
1566+
// Prints: 4294967295n
1567+
1568+
console.log(buf.readBigUInt64LE(0));
1569+
// Prints: 18446744069414584320n
1570+
```
1571+
1572+
15321573
### buf.readDoubleBE(offset)
15331574
### buf.readDoubleLE(offset)
15341575
<!-- YAML
@@ -2132,6 +2173,56 @@ console.log(`${len} bytes: ${buf.toString('utf8', 0, len)}`);
21322173
// Prints: 12 bytes: ½ + ¼ = ¾
21332174
```
21342175

2176+
### buf.writeBigInt64BE(value, offset)
2177+
### buf.writeBigInt64LE(value, offset)
2178+
<!-- YAML
2179+
added: REPLACEME
2180+
-->
2181+
2182+
* `value` {bigint} Number to be written to `buf`.
2183+
* `offset` {integer} Number of bytes to skip before starting to write. Must
2184+
satisfy: `0 <= offset <= buf.length - 8`. **Default:** `0`.
2185+
* Returns: {integer} `offset` plus the number of bytes written.
2186+
2187+
Writes `value` to `buf` at the specified `offset` with specified endian
2188+
format (`writeBigInt64BE()` writes big endian, `writeBigInt64LE()` writes little
2189+
endian).
2190+
2191+
`value` is interpreted and written as a two's complement signed integer.
2192+
2193+
```js
2194+
const buf = Buffer.allocUnsafe(8);
2195+
2196+
buf.writeBigInt64BE(0x0102030405060708n, 0);
2197+
2198+
console.log(buf);
2199+
// Prints: <Buffer 01 02 03 04 05 06 07 08>
2200+
```
2201+
2202+
### buf.writeBigUInt64BE(value, offset)
2203+
### buf.writeBigUInt64LE(value, offset)
2204+
<!-- YAML
2205+
added: REPLACEME
2206+
-->
2207+
2208+
* `value` {bigint} Number to be written to `buf`.
2209+
* `offset` {integer} Number of bytes to skip before starting to write. Must
2210+
satisfy: `0 <= offset <= buf.length - 8`. **Default:** `0`.
2211+
* Returns: {integer} `offset` plus the number of bytes written.
2212+
2213+
Writes `value` to `buf` at the specified `offset` with specified endian
2214+
format (`writeBigUInt64BE()` writes big endian, `writeBigUInt64LE()` writes
2215+
little endian).
2216+
2217+
```js
2218+
const buf = Buffer.allocUnsafe(8);
2219+
2220+
buf.writeBigUInt64LE(0xdecafafecacefaden, 0);
2221+
2222+
console.log(buf);
2223+
// Prints: <Buffer de fa ce ca fe fa ca de>
2224+
```
2225+
21352226
### buf.writeDoubleBE(value, offset)
21362227
### buf.writeDoubleLE(value, offset)
21372228
<!-- YAML

‎lib/internal/buffer.js

+146
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,82 @@ function boundsError(value, length, type) {
5252
}
5353

5454
// Read integers.
55+
function readBigUInt64LE(offset = 0) {
56+
validateNumber(offset, 'offset');
57+
const first = this[offset];
58+
const last = this[offset + 7];
59+
if (first === undefined || last === undefined)
60+
boundsError(offset, this.length - 8);
61+
62+
const lo = first +
63+
this[++offset] * 2 ** 8 +
64+
this[++offset] * 2 ** 16 +
65+
this[++offset] * 2 ** 24;
66+
67+
const hi = this[++offset] +
68+
this[++offset] * 2 ** 8 +
69+
this[++offset] * 2 ** 16 +
70+
last * 2 ** 24;
71+
72+
return BigInt(lo) + (BigInt(hi) << 32n);
73+
}
74+
75+
function readBigUInt64BE(offset = 0) {
76+
validateNumber(offset, 'offset');
77+
const first = this[offset];
78+
const last = this[offset + 7];
79+
if (first === undefined || last === undefined)
80+
boundsError(offset, this.length - 8);
81+
82+
const hi = first * 2 ** 24 +
83+
this[++offset] * 2 ** 16 +
84+
this[++offset] * 2 ** 8 +
85+
this[++offset];
86+
87+
const lo = this[++offset] * 2 ** 24 +
88+
this[++offset] * 2 ** 16 +
89+
this[++offset] * 2 ** 8 +
90+
last;
91+
92+
return (BigInt(hi) << 32n) + BigInt(lo);
93+
}
94+
95+
function readBigInt64LE(offset = 0) {
96+
validateNumber(offset, 'offset');
97+
const first = this[offset];
98+
const last = this[offset + 7];
99+
if (first === undefined || last === undefined)
100+
boundsError(offset, this.length - 8);
101+
102+
const val = this[offset + 4] +
103+
this[offset + 5] * 2 ** 8 +
104+
this[offset + 6] * 2 ** 16 +
105+
(last << 24); // Overflow
106+
return (BigInt(val) << 32n) +
107+
BigInt(first +
108+
this[++offset] * 2 ** 8 +
109+
this[++offset] * 2 ** 16 +
110+
this[++offset] * 2 ** 24);
111+
}
112+
113+
function readBigInt64BE(offset = 0) {
114+
validateNumber(offset, 'offset');
115+
const first = this[offset];
116+
const last = this[offset + 7];
117+
if (first === undefined || last === undefined)
118+
boundsError(offset, this.length - 8);
119+
120+
const val = (first << 24) + // Overflow
121+
this[++offset] * 2 ** 16 +
122+
this[++offset] * 2 ** 8 +
123+
this[++offset];
124+
return (BigInt(val) << 32n) +
125+
BigInt(this[++offset] * 2 ** 24 +
126+
this[++offset] * 2 ** 16 +
127+
this[++offset] * 2 ** 8 +
128+
last);
129+
}
130+
55131
function readUIntLE(offset, byteLength) {
56132
if (byteLength === 6)
57133
return readUInt48LE(this, offset);
@@ -454,6 +530,68 @@ function readDoubleForwards(offset = 0) {
454530
}
455531

456532
// Write integers.
533+
function writeBigU_Int64LE(buf, value, offset, min, max) {
534+
checkInt(value, min, max, buf, offset, 7);
535+
536+
let lo = Number(value & 0xffffffffn);
537+
buf[offset++] = lo;
538+
lo = lo >> 8;
539+
buf[offset++] = lo;
540+
lo = lo >> 8;
541+
buf[offset++] = lo;
542+
lo = lo >> 8;
543+
buf[offset++] = lo;
544+
let hi = Number(value >> 32n & 0xffffffffn);
545+
buf[offset++] = hi;
546+
hi = hi >> 8;
547+
buf[offset++] = hi;
548+
hi = hi >> 8;
549+
buf[offset++] = hi;
550+
hi = hi >> 8;
551+
buf[offset++] = hi;
552+
return offset;
553+
}
554+
555+
function writeBigUInt64LE(value, offset = 0) {
556+
return writeBigU_Int64LE(this, value, offset, 0n, 0xffffffffffffffffn);
557+
}
558+
559+
function writeBigU_Int64BE(buf, value, offset, min, max) {
560+
checkInt(value, min, max, buf, offset, 7);
561+
562+
let lo = Number(value & 0xffffffffn);
563+
buf[offset + 7] = lo;
564+
lo = lo >> 8;
565+
buf[offset + 6] = lo;
566+
lo = lo >> 8;
567+
buf[offset + 5] = lo;
568+
lo = lo >> 8;
569+
buf[offset + 4] = lo;
570+
let hi = Number(value >> 32n & 0xffffffffn);
571+
buf[offset + 3] = hi;
572+
hi = hi >> 8;
573+
buf[offset + 2] = hi;
574+
hi = hi >> 8;
575+
buf[offset + 1] = hi;
576+
hi = hi >> 8;
577+
buf[offset] = hi;
578+
return offset + 8;
579+
}
580+
581+
function writeBigUInt64BE(value, offset = 0) {
582+
return writeBigU_Int64BE(this, value, offset, 0n, 0xffffffffffffffffn);
583+
}
584+
585+
function writeBigInt64LE(value, offset = 0) {
586+
return writeBigU_Int64LE(
587+
this, value, offset, -0x8000000000000000n, 0x7fffffffffffffffn);
588+
}
589+
590+
function writeBigInt64BE(value, offset = 0) {
591+
return writeBigU_Int64BE(
592+
this, value, offset, -0x8000000000000000n, 0x7fffffffffffffffn);
593+
}
594+
457595
function writeUIntLE(value, offset = 0, byteLength) {
458596
if (byteLength === 6)
459597
return writeU_Int48LE(this, value, offset, 0, 0xffffffffffff);
@@ -773,6 +911,14 @@ module.exports = {
773911
setupBufferJS,
774912
// Container to export all read write functions.
775913
readWrites: {
914+
readBigUInt64LE,
915+
readBigUInt64BE,
916+
readBigInt64LE,
917+
readBigInt64BE,
918+
writeBigUInt64LE,
919+
writeBigUInt64BE,
920+
writeBigInt64LE,
921+
writeBigInt64BE,
776922
readUIntLE,
777923
readUInt32LE,
778924
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)
Please sign in to comment.