buffer: add {read|write}Big[U]Int64{BE|LE} methods
PR-URL: https://github.com/nodejs/node/pull/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>
This commit is contained in:
parent
dd89a1182f
commit
3d8532f851
@ -2,6 +2,10 @@
|
|||||||
const common = require('../common.js');
|
const common = require('../common.js');
|
||||||
|
|
||||||
const types = [
|
const types = [
|
||||||
|
'BigUInt64LE',
|
||||||
|
'BigUInt64BE',
|
||||||
|
'BigInt64LE',
|
||||||
|
'BigInt64BE',
|
||||||
'UInt8',
|
'UInt8',
|
||||||
'UInt16LE',
|
'UInt16LE',
|
||||||
'UInt16BE',
|
'UInt16BE',
|
||||||
|
@ -2,6 +2,10 @@
|
|||||||
const common = require('../common.js');
|
const common = require('../common.js');
|
||||||
|
|
||||||
const types = [
|
const types = [
|
||||||
|
'BigUInt64LE',
|
||||||
|
'BigUInt64BE',
|
||||||
|
'BigInt64LE',
|
||||||
|
'BigInt64BE',
|
||||||
'UInt8',
|
'UInt8',
|
||||||
'UInt16LE',
|
'UInt16LE',
|
||||||
'UInt16BE',
|
'UInt16BE',
|
||||||
@ -32,11 +36,17 @@ const INT8 = 0x7f;
|
|||||||
const INT16 = 0x7fff;
|
const INT16 = 0x7fff;
|
||||||
const INT32 = 0x7fffffff;
|
const INT32 = 0x7fffffff;
|
||||||
const INT48 = 0x7fffffffffff;
|
const INT48 = 0x7fffffffffff;
|
||||||
|
const INT64 = 0x7fffffffffffffffn;
|
||||||
const UINT8 = 0xff;
|
const UINT8 = 0xff;
|
||||||
const UINT16 = 0xffff;
|
const UINT16 = 0xffff;
|
||||||
const UINT32 = 0xffffffff;
|
const UINT32 = 0xffffffff;
|
||||||
|
const UINT64 = 0xffffffffffffffffn;
|
||||||
|
|
||||||
const mod = {
|
const mod = {
|
||||||
|
writeBigInt64BE: INT64,
|
||||||
|
writeBigInt64LE: INT64,
|
||||||
|
writeBigUInt64BE: UINT64,
|
||||||
|
writeBigUInt64LE: UINT64,
|
||||||
writeInt8: INT8,
|
writeInt8: INT8,
|
||||||
writeInt16BE: INT16,
|
writeInt16BE: INT16,
|
||||||
writeInt16LE: INT16,
|
writeInt16LE: INT16,
|
||||||
@ -67,12 +77,23 @@ function main({ n, buf, type }) {
|
|||||||
|
|
||||||
if (!/\d/.test(fn))
|
if (!/\d/.test(fn))
|
||||||
benchSpecialInt(buff, fn, n);
|
benchSpecialInt(buff, fn, n);
|
||||||
|
else if (/BigU?Int/.test(fn))
|
||||||
|
benchBigInt(buff, fn, BigInt(n));
|
||||||
else if (/Int/.test(fn))
|
else if (/Int/.test(fn))
|
||||||
benchInt(buff, fn, n);
|
benchInt(buff, fn, n);
|
||||||
else
|
else
|
||||||
benchFloat(buff, fn, n);
|
benchFloat(buff, fn, n);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function benchBigInt(buff, fn, n) {
|
||||||
|
const m = mod[fn];
|
||||||
|
bench.start();
|
||||||
|
for (var i = 0n; i !== n; i++) {
|
||||||
|
buff[fn](i & m, 0);
|
||||||
|
}
|
||||||
|
bench.end(Number(n));
|
||||||
|
}
|
||||||
|
|
||||||
function benchInt(buff, fn, n) {
|
function benchInt(buff, fn, n) {
|
||||||
const m = mod[fn];
|
const m = mod[fn];
|
||||||
bench.start();
|
bench.start();
|
||||||
|
@ -1546,6 +1546,46 @@ deprecated: v8.0.0
|
|||||||
|
|
||||||
The `buf.parent` property is a deprecated alias for `buf.buffer`.
|
The `buf.parent` property is a deprecated alias for `buf.buffer`.
|
||||||
|
|
||||||
|
### buf.readBigInt64BE([offset])
|
||||||
|
### buf.readBigInt64LE([offset])
|
||||||
|
<!-- YAML
|
||||||
|
added: REPLACEME
|
||||||
|
-->
|
||||||
|
|
||||||
|
* `offset` {integer} Number of bytes to skip before starting to read. Must
|
||||||
|
satisfy: `0 <= offset <= buf.length - 8`. **Default:** `0`.
|
||||||
|
* Returns: {bigint}
|
||||||
|
|
||||||
|
Reads a signed 64-bit integer from `buf` at the specified `offset` with
|
||||||
|
the specified endian format (`readBigInt64BE()` returns big endian,
|
||||||
|
`readBigInt64LE()` returns little endian).
|
||||||
|
|
||||||
|
Integers read from a `Buffer` are interpreted as two's complement signed values.
|
||||||
|
|
||||||
|
### buf.readBigUInt64BE([offset])
|
||||||
|
### buf.readBigUInt64LE([offset])
|
||||||
|
<!-- YAML
|
||||||
|
added: REPLACEME
|
||||||
|
-->
|
||||||
|
|
||||||
|
* `offset` {integer} Number of bytes to skip before starting to read. Must
|
||||||
|
satisfy: `0 <= offset <= buf.length - 8`. **Default:** `0`.
|
||||||
|
* Returns: {bigint}
|
||||||
|
|
||||||
|
Reads an unsigned 64-bit integer from `buf` at the specified `offset` with
|
||||||
|
specified endian format (`readBigUInt64BE()` returns big endian,
|
||||||
|
`readBigUInt64LE()` returns little endian).
|
||||||
|
|
||||||
|
```js
|
||||||
|
const buf = Buffer.from([0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff]);
|
||||||
|
|
||||||
|
console.log(buf.readBigUInt64BE(0));
|
||||||
|
// Prints: 4294967295n
|
||||||
|
|
||||||
|
console.log(buf.readBigUInt64LE(0));
|
||||||
|
// Prints: 18446744069414584320n
|
||||||
|
```
|
||||||
|
|
||||||
### buf.readDoubleBE([offset])
|
### buf.readDoubleBE([offset])
|
||||||
### buf.readDoubleLE([offset])
|
### buf.readDoubleLE([offset])
|
||||||
<!-- YAML
|
<!-- YAML
|
||||||
@ -2149,6 +2189,56 @@ console.log(`${len} bytes: ${buf.toString('utf8', 0, len)}`);
|
|||||||
// Prints: 12 bytes: ½ + ¼ = ¾
|
// Prints: 12 bytes: ½ + ¼ = ¾
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### buf.writeBigInt64BE(value[, offset])
|
||||||
|
### buf.writeBigInt64LE(value[, offset])
|
||||||
|
<!-- YAML
|
||||||
|
added: REPLACEME
|
||||||
|
-->
|
||||||
|
|
||||||
|
* `value` {bigint} Number to be written to `buf`.
|
||||||
|
* `offset` {integer} Number of bytes to skip before starting to write. Must
|
||||||
|
satisfy: `0 <= offset <= buf.length - 8`. **Default:** `0`.
|
||||||
|
* Returns: {integer} `offset` plus the number of bytes written.
|
||||||
|
|
||||||
|
Writes `value` to `buf` at the specified `offset` with specified endian
|
||||||
|
format (`writeBigInt64BE()` writes big endian, `writeBigInt64LE()` writes little
|
||||||
|
endian).
|
||||||
|
|
||||||
|
`value` is interpreted and written as a two's complement signed integer.
|
||||||
|
|
||||||
|
```js
|
||||||
|
const buf = Buffer.allocUnsafe(8);
|
||||||
|
|
||||||
|
buf.writeBigInt64BE(0x0102030405060708n, 0);
|
||||||
|
|
||||||
|
console.log(buf);
|
||||||
|
// Prints: <Buffer 01 02 03 04 05 06 07 08>
|
||||||
|
```
|
||||||
|
|
||||||
|
### buf.writeBigUInt64BE(value[, offset])
|
||||||
|
### buf.writeBigUInt64LE(value[, offset])
|
||||||
|
<!-- YAML
|
||||||
|
added: REPLACEME
|
||||||
|
-->
|
||||||
|
|
||||||
|
* `value` {bigint} Number to be written to `buf`.
|
||||||
|
* `offset` {integer} Number of bytes to skip before starting to write. Must
|
||||||
|
satisfy: `0 <= offset <= buf.length - 8`. **Default:** `0`.
|
||||||
|
* Returns: {integer} `offset` plus the number of bytes written.
|
||||||
|
|
||||||
|
Writes `value` to `buf` at the specified `offset` with specified endian
|
||||||
|
format (`writeBigUInt64BE()` writes big endian, `writeBigUInt64LE()` writes
|
||||||
|
little endian).
|
||||||
|
|
||||||
|
```js
|
||||||
|
const buf = Buffer.allocUnsafe(8);
|
||||||
|
|
||||||
|
buf.writeBigUInt64LE(0xdecafafecacefaden, 0);
|
||||||
|
|
||||||
|
console.log(buf);
|
||||||
|
// Prints: <Buffer de fa ce ca fe fa ca de>
|
||||||
|
```
|
||||||
|
|
||||||
### buf.writeDoubleBE(value[, offset])
|
### buf.writeDoubleBE(value[, offset])
|
||||||
### buf.writeDoubleLE(value[, offset])
|
### buf.writeDoubleLE(value[, offset])
|
||||||
<!-- YAML
|
<!-- YAML
|
||||||
|
@ -63,6 +63,82 @@ function boundsError(value, length, type) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Read integers.
|
// Read integers.
|
||||||
|
function readBigUInt64LE(offset = 0) {
|
||||||
|
validateNumber(offset, 'offset');
|
||||||
|
const first = this[offset];
|
||||||
|
const last = this[offset + 7];
|
||||||
|
if (first === undefined || last === undefined)
|
||||||
|
boundsError(offset, this.length - 8);
|
||||||
|
|
||||||
|
const lo = first +
|
||||||
|
this[++offset] * 2 ** 8 +
|
||||||
|
this[++offset] * 2 ** 16 +
|
||||||
|
this[++offset] * 2 ** 24;
|
||||||
|
|
||||||
|
const hi = this[++offset] +
|
||||||
|
this[++offset] * 2 ** 8 +
|
||||||
|
this[++offset] * 2 ** 16 +
|
||||||
|
last * 2 ** 24;
|
||||||
|
|
||||||
|
return BigInt(lo) + (BigInt(hi) << 32n);
|
||||||
|
}
|
||||||
|
|
||||||
|
function readBigUInt64BE(offset = 0) {
|
||||||
|
validateNumber(offset, 'offset');
|
||||||
|
const first = this[offset];
|
||||||
|
const last = this[offset + 7];
|
||||||
|
if (first === undefined || last === undefined)
|
||||||
|
boundsError(offset, this.length - 8);
|
||||||
|
|
||||||
|
const hi = first * 2 ** 24 +
|
||||||
|
this[++offset] * 2 ** 16 +
|
||||||
|
this[++offset] * 2 ** 8 +
|
||||||
|
this[++offset];
|
||||||
|
|
||||||
|
const lo = this[++offset] * 2 ** 24 +
|
||||||
|
this[++offset] * 2 ** 16 +
|
||||||
|
this[++offset] * 2 ** 8 +
|
||||||
|
last;
|
||||||
|
|
||||||
|
return (BigInt(hi) << 32n) + BigInt(lo);
|
||||||
|
}
|
||||||
|
|
||||||
|
function readBigInt64LE(offset = 0) {
|
||||||
|
validateNumber(offset, 'offset');
|
||||||
|
const first = this[offset];
|
||||||
|
const last = this[offset + 7];
|
||||||
|
if (first === undefined || last === undefined)
|
||||||
|
boundsError(offset, this.length - 8);
|
||||||
|
|
||||||
|
const val = this[offset + 4] +
|
||||||
|
this[offset + 5] * 2 ** 8 +
|
||||||
|
this[offset + 6] * 2 ** 16 +
|
||||||
|
(last << 24); // Overflow
|
||||||
|
return (BigInt(val) << 32n) +
|
||||||
|
BigInt(first +
|
||||||
|
this[++offset] * 2 ** 8 +
|
||||||
|
this[++offset] * 2 ** 16 +
|
||||||
|
this[++offset] * 2 ** 24);
|
||||||
|
}
|
||||||
|
|
||||||
|
function readBigInt64BE(offset = 0) {
|
||||||
|
validateNumber(offset, 'offset');
|
||||||
|
const first = this[offset];
|
||||||
|
const last = this[offset + 7];
|
||||||
|
if (first === undefined || last === undefined)
|
||||||
|
boundsError(offset, this.length - 8);
|
||||||
|
|
||||||
|
const val = (first << 24) + // Overflow
|
||||||
|
this[++offset] * 2 ** 16 +
|
||||||
|
this[++offset] * 2 ** 8 +
|
||||||
|
this[++offset];
|
||||||
|
return (BigInt(val) << 32n) +
|
||||||
|
BigInt(this[++offset] * 2 ** 24 +
|
||||||
|
this[++offset] * 2 ** 16 +
|
||||||
|
this[++offset] * 2 ** 8 +
|
||||||
|
last);
|
||||||
|
}
|
||||||
|
|
||||||
function readUIntLE(offset, byteLength) {
|
function readUIntLE(offset, byteLength) {
|
||||||
if (offset === undefined)
|
if (offset === undefined)
|
||||||
throw new ERR_INVALID_ARG_TYPE('offset', 'number', offset);
|
throw new ERR_INVALID_ARG_TYPE('offset', 'number', offset);
|
||||||
@ -473,6 +549,68 @@ function readDoubleForwards(offset = 0) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Write integers.
|
// Write integers.
|
||||||
|
function writeBigU_Int64LE(buf, value, offset, min, max) {
|
||||||
|
checkInt(value, min, max, buf, offset, 7);
|
||||||
|
|
||||||
|
let lo = Number(value & 0xffffffffn);
|
||||||
|
buf[offset++] = lo;
|
||||||
|
lo = lo >> 8;
|
||||||
|
buf[offset++] = lo;
|
||||||
|
lo = lo >> 8;
|
||||||
|
buf[offset++] = lo;
|
||||||
|
lo = lo >> 8;
|
||||||
|
buf[offset++] = lo;
|
||||||
|
let hi = Number(value >> 32n & 0xffffffffn);
|
||||||
|
buf[offset++] = hi;
|
||||||
|
hi = hi >> 8;
|
||||||
|
buf[offset++] = hi;
|
||||||
|
hi = hi >> 8;
|
||||||
|
buf[offset++] = hi;
|
||||||
|
hi = hi >> 8;
|
||||||
|
buf[offset++] = hi;
|
||||||
|
return offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
function writeBigUInt64LE(value, offset = 0) {
|
||||||
|
return writeBigU_Int64LE(this, value, offset, 0n, 0xffffffffffffffffn);
|
||||||
|
}
|
||||||
|
|
||||||
|
function writeBigU_Int64BE(buf, value, offset, min, max) {
|
||||||
|
checkInt(value, min, max, buf, offset, 7);
|
||||||
|
|
||||||
|
let lo = Number(value & 0xffffffffn);
|
||||||
|
buf[offset + 7] = lo;
|
||||||
|
lo = lo >> 8;
|
||||||
|
buf[offset + 6] = lo;
|
||||||
|
lo = lo >> 8;
|
||||||
|
buf[offset + 5] = lo;
|
||||||
|
lo = lo >> 8;
|
||||||
|
buf[offset + 4] = lo;
|
||||||
|
let hi = Number(value >> 32n & 0xffffffffn);
|
||||||
|
buf[offset + 3] = hi;
|
||||||
|
hi = hi >> 8;
|
||||||
|
buf[offset + 2] = hi;
|
||||||
|
hi = hi >> 8;
|
||||||
|
buf[offset + 1] = hi;
|
||||||
|
hi = hi >> 8;
|
||||||
|
buf[offset] = hi;
|
||||||
|
return offset + 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
function writeBigUInt64BE(value, offset = 0) {
|
||||||
|
return writeBigU_Int64BE(this, value, offset, 0n, 0xffffffffffffffffn);
|
||||||
|
}
|
||||||
|
|
||||||
|
function writeBigInt64LE(value, offset = 0) {
|
||||||
|
return writeBigU_Int64LE(
|
||||||
|
this, value, offset, -0x8000000000000000n, 0x7fffffffffffffffn);
|
||||||
|
}
|
||||||
|
|
||||||
|
function writeBigInt64BE(value, offset = 0) {
|
||||||
|
return writeBigU_Int64BE(
|
||||||
|
this, value, offset, -0x8000000000000000n, 0x7fffffffffffffffn);
|
||||||
|
}
|
||||||
|
|
||||||
function writeUIntLE(value, offset, byteLength) {
|
function writeUIntLE(value, offset, byteLength) {
|
||||||
if (byteLength === 6)
|
if (byteLength === 6)
|
||||||
return writeU_Int48LE(this, value, offset, 0, 0xffffffffffff);
|
return writeU_Int48LE(this, value, offset, 0, 0xffffffffffff);
|
||||||
@ -790,6 +928,15 @@ function writeFloatBackwards(val, offset = 0) {
|
|||||||
class FastBuffer extends Uint8Array {}
|
class FastBuffer extends Uint8Array {}
|
||||||
|
|
||||||
function addBufferPrototypeMethods(proto) {
|
function addBufferPrototypeMethods(proto) {
|
||||||
|
proto.readBigUInt64LE = readBigUInt64LE,
|
||||||
|
proto.readBigUInt64BE = readBigUInt64BE,
|
||||||
|
proto.readBigInt64LE = readBigInt64LE,
|
||||||
|
proto.readBigInt64BE = readBigInt64BE,
|
||||||
|
proto.writeBigUInt64LE = writeBigUInt64LE,
|
||||||
|
proto.writeBigUInt64BE = writeBigUInt64BE,
|
||||||
|
proto.writeBigInt64LE = writeBigInt64LE,
|
||||||
|
proto.writeBigInt64BE = writeBigInt64BE,
|
||||||
|
|
||||||
proto.readUIntLE = readUIntLE;
|
proto.readUIntLE = readUIntLE;
|
||||||
proto.readUInt32LE = readUInt32LE;
|
proto.readUInt32LE = readUInt32LE;
|
||||||
proto.readUInt16LE = readUInt16LE;
|
proto.readUInt16LE = readUInt16LE;
|
||||||
|
51
test/parallel/test-buffer-bigint64.js
Normal file
51
test/parallel/test-buffer-bigint64.js
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
'use strict';
|
||||||
|
require('../common');
|
||||||
|
const assert = require('assert');
|
||||||
|
|
||||||
|
const buf = Buffer.allocUnsafe(8);
|
||||||
|
|
||||||
|
['LE', 'BE'].forEach(function(endianness) {
|
||||||
|
// Should allow simple BigInts to be written and read
|
||||||
|
let val = 123456789n;
|
||||||
|
buf['writeBigInt64' + endianness](val, 0);
|
||||||
|
let rtn = buf['readBigInt64' + endianness](0);
|
||||||
|
assert.strictEqual(val, rtn);
|
||||||
|
|
||||||
|
// Should allow INT64_MAX to be written and read
|
||||||
|
val = 0x7fffffffffffffffn;
|
||||||
|
buf['writeBigInt64' + endianness](val, 0);
|
||||||
|
rtn = buf['readBigInt64' + endianness](0);
|
||||||
|
assert.strictEqual(val, rtn);
|
||||||
|
|
||||||
|
// Should read and write a negative signed 64-bit integer
|
||||||
|
val = -123456789n;
|
||||||
|
buf['writeBigInt64' + endianness](val, 0);
|
||||||
|
assert.strictEqual(val, buf['readBigInt64' + endianness](0));
|
||||||
|
|
||||||
|
// Should read and write an unsigned 64-bit integer
|
||||||
|
val = 123456789n;
|
||||||
|
buf['writeBigUInt64' + endianness](val, 0);
|
||||||
|
assert.strictEqual(val, buf['readBigUInt64' + endianness](0));
|
||||||
|
|
||||||
|
// Should throw a RangeError upon INT64_MAX+1 being written
|
||||||
|
assert.throws(function() {
|
||||||
|
const val = 0x8000000000000000n;
|
||||||
|
buf['writeBigInt64' + endianness](val, 0);
|
||||||
|
}, RangeError);
|
||||||
|
|
||||||
|
// Should throw a RangeError upon UINT64_MAX+1 being written
|
||||||
|
assert.throws(function() {
|
||||||
|
const val = 0x10000000000000000n;
|
||||||
|
buf['writeBigUInt64' + endianness](val, 0);
|
||||||
|
}, RangeError);
|
||||||
|
|
||||||
|
// Should throw a TypeError upon invalid input
|
||||||
|
assert.throws(function() {
|
||||||
|
buf['writeBigInt64' + endianness]('bad', 0);
|
||||||
|
}, TypeError);
|
||||||
|
|
||||||
|
// Should throw a TypeError upon invalid input
|
||||||
|
assert.throws(function() {
|
||||||
|
buf['writeBigUInt64' + endianness]('bad', 0);
|
||||||
|
}, TypeError);
|
||||||
|
});
|
Loading…
x
Reference in New Issue
Block a user