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:
Родитель
780436005f
Коммит
d49d17abcd
|
@ -2,6 +2,10 @@
|
|||
const common = require('../common.js');
|
||||
|
||||
const types = [
|
||||
'BigUInt64LE',
|
||||
'BigUInt64BE',
|
||||
'BigInt64LE',
|
||||
'BigInt64BE',
|
||||
'UInt8',
|
||||
'UInt16LE',
|
||||
'UInt16BE',
|
||||
|
|
|
@ -2,6 +2,10 @@
|
|||
const common = require('../common.js');
|
||||
|
||||
const types = [
|
||||
'BigUInt64LE',
|
||||
'BigUInt64BE',
|
||||
'BigInt64LE',
|
||||
'BigInt64BE',
|
||||
'UInt8',
|
||||
'UInt16LE',
|
||||
'UInt16BE',
|
||||
|
@ -32,11 +36,17 @@ const INT8 = 0x7f;
|
|||
const INT16 = 0x7fff;
|
||||
const INT32 = 0x7fffffff;
|
||||
const INT48 = 0x7fffffffffff;
|
||||
const INT64 = 0x7fffffffffffffffn;
|
||||
const UINT8 = 0xff;
|
||||
const UINT16 = 0xffff;
|
||||
const UINT32 = 0xffffffff;
|
||||
const UINT64 = 0xffffffffffffffffn;
|
||||
|
||||
const mod = {
|
||||
writeBigInt64BE: INT64,
|
||||
writeBigInt64LE: INT64,
|
||||
writeBigUInt64BE: UINT64,
|
||||
writeBigUInt64LE: UINT64,
|
||||
writeInt8: INT8,
|
||||
writeInt16BE: INT16,
|
||||
writeInt16LE: INT16,
|
||||
|
@ -67,12 +77,23 @@ function main({ n, buf, type }) {
|
|||
|
||||
if (!/\d/.test(fn))
|
||||
benchSpecialInt(buff, fn, n);
|
||||
else if (/BigU?Int/.test(fn))
|
||||
benchBigInt(buff, fn, BigInt(n));
|
||||
else if (/Int/.test(fn))
|
||||
benchInt(buff, fn, n);
|
||||
else
|
||||
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) {
|
||||
const m = mod[fn];
|
||||
bench.start();
|
||||
|
|
|
@ -1546,6 +1546,46 @@ deprecated: v8.0.0
|
|||
|
||||
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.readDoubleLE([offset])
|
||||
<!-- YAML
|
||||
|
@ -2149,6 +2189,56 @@ console.log(`${len} bytes: ${buf.toString('utf8', 0, len)}`);
|
|||
// 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.writeDoubleLE(value[, offset])
|
||||
<!-- YAML
|
||||
|
|
|
@ -63,6 +63,82 @@ function boundsError(value, length, type) {
|
|||
}
|
||||
|
||||
// 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) {
|
||||
if (offset === undefined)
|
||||
throw new ERR_INVALID_ARG_TYPE('offset', 'number', offset);
|
||||
|
@ -473,6 +549,68 @@ function readDoubleForwards(offset = 0) {
|
|||
}
|
||||
|
||||
// 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) {
|
||||
if (byteLength === 6)
|
||||
return writeU_Int48LE(this, value, offset, 0, 0xffffffffffff);
|
||||
|
@ -790,6 +928,15 @@ function writeFloatBackwards(val, offset = 0) {
|
|||
class FastBuffer extends Uint8Array {}
|
||||
|
||||
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.readUInt32LE = readUInt32LE;
|
||||
proto.readUInt16LE = readUInt16LE;
|
||||
|
|
|
@ -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);
|
||||
});
|
Загрузка…
Ссылка в новой задаче