Node.js如何将64位无符号整数写入缓冲区?

13

我想以大端格式将一个64位(8字节)大整数存储到Node.js缓冲区对象中。

这个任务的问题在于,Node.js缓冲区只支持将32位整数作为最大值来写入(使用buf.write32UInt32BE(value, offset))。因此,我想,为什么我们不能将64位整数拆分呢?

var buf = new Buffer(8);

buf.fill(0) // clear all bytes of the buffer
console.log(buf); // outputs <Buffer 00 00 00 00 00 00 00 00>

var int = 0xffff; // as dezimal: 65535

buf.write32UInt32BE(0xff, 4); // right the first part of the int
console.log(buf); // outputs <Buffer 00 00 00 00 00 00 00 ff>

buf.write32UInt32BE(0xff, 0); // right the second part of the int
console.log(buf); // outputs <Buffer 00 00 00 ff 00 00 00 ff>

var bufInt = buf.read32UInt32BE(0) * buf.read32UInt32BE(4);
console.log(bufInt); // outputs 65025

正如您所见,这差不多是可行的。问题仅在于分割64位整数并在读取时找到缺失的510。是否有人能够展示这两个问题的解决方案?


2
自从最新的Node.js v12.0.0版本以来,您现在可以使用buf.writeBigInt64BE - mouse
7个回答

11

我认为你正在寻找的是:

var bufInt = (buf.readUInt32BE(0) << 8) + buf.readUInt32BE(4);

将第一个数字左移8位并加上(而不是乘以),得到65535


EDIT

另一种表达方式为:

var buf = new Buffer(8);
buf.fill(0);

var i = 0xCDEF; // 52719 in decimal

buf.writeUInt32BE(i >> 8, 0); //write the high order bits (shifted over)
buf.writeUInt32BE(i & 0x00ff, 4); //write the low order bits

console.log(buf); //displays: <Buffer 00 00 00 cd 00 00 00 ef>

var bufInt = (buf.readUInt32BE(0) << 8) + buf.readUInt32BE(4);
console.log(bufInt); //displays: 52719

是的,这将解决阅读问题,但如何将0xffff拆分为0xff、0xff呢? - bodokaiser
@kyogron 看看我的修改。记住,在 JavaScript 中移位是很奇怪的,它只适用于 32 位。我认为这不会适用于一个值无法由 32 位表示的数字。 - Chad
4
@Chad 这并不能适用于完整的64位数值。你的移位也有误。你需要进行32位的移位,而非8位,而这是JS无法实现的。 - loganfsmyth
@Chad 为了澄清,这将适用于他在问题中提出的16位值,但不适用于一般情况。使用8个字节的缓冲区存储2个字节也会浪费空间。 - loganfsmyth
@loganfsmyth,关于64位值,我已经说了很多,关于缓冲区空间,我完全同意,我只是按照他给出的例子来说。 - Chad
1
对数字进行位运算时,将数字转换为32位。 - Tracker1

2

阅读和编写小于Number.MAX_SAFE_INTEGER的UINT数字。

这仅适用于node.js,在浏览器端不可移植。

function uintToBase62(n) {
  if (n < 0) throw 'unsupported negative integer';

  let uintBuffer;
  if (n < 0x7FFFFFFF) {
    uintBuffer = new Buffer(4);
    uintBuffer.writeUInt32BE(n, 0);
  } else {
    // `~~` double bitwise operator
    // The most practical way of utilizing the power of this operator is to use it as a replacement
    // for Math.floor() function as double bitwise NOT performs the same operation a lot quicker.
    // You can use it, to convert any floating point number to a integer without performance overkill
    // that comes with Math.floor(). Additionally, when you care about minification of your code,
    // you end up using 2 characters (2 tildes) instead of 12.
    // http://rocha.la/JavaScript-bitwise-operators-in-practice
    const big = ~~(n / 0x0100000000);
    const low = (n % 0x0100000000);
    uintBuffer = new Buffer(8);
    uintBuffer.writeUInt32BE(big, 0);
    uintBuffer.writeUInt32BE(low, 4);
  }

  return uintBuffer.toString('hex');
}

将其转换

function uintFromBase62(uintBuffer) {
  const n = parseInt(uintBuffer.toString('hex'), 16);
  return n;
}

2

我感到困惑的是你的例子值0xFFFF只有16位,而不是64位。

请记住,JS number类型被指定为IEEE754浮点值,因此不能保证能够保存64位无符号值。如果您需要真正的64位整数支持,您需要使用提供它的模块,比如bignum。该自述文件中有读取和写入值到缓冲区的示例。

浮点值只能表示最多2^53 - 1的值,否则会失去精度。您可以在这个使用标准JS数字的例子中看到:

var b = new Buffer([0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF])
var firstHalf = b.readUInt32BE(0); // 4294967295
var secondHalf = b.readUInt32BE(4); // 4294967295

var val = firstHalf * 0x100000000 + secondHalf; // 18446744073709552000

这个结果应该是18446744073709551615,但由于某些原因导致最终结果为18446744073709552000
var bignum = require('bignum');

var b = new Buffer([0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF])
var val = bignum.fromBuffer(b);

这将得到一个值为18446744073709551615BigNum对象。

另外,为了详细说明您的示例代码,您使用的值仅为16位,并尝试使用32位函数进行操作。您可以这样做:

var buf = new Buffer(2);

buf.fill(0) // clear all bytes of the buffer
console.log(buf); // outputs <Buffer 00 00>

var int = 0xffff; // as decimal: 65535

// Write it with a standard 16-bit function calls.
buf.writeUInt16BE(int);

// OR write it with 2 8-bit function calls.
buf.writeUInt8(int & 0xff, 0); // right the first part of the int
buf.writeUInt8((int >> 8) & 0xFF, 1); // right the second part of the int
console.log(buf); // outputs <Buffer ff ff>

// Read it as a 16-bit value.
var bufInt = buf.readUInt16BE(0);
console.log(bufInt);

// OR read it as two 8-bit values.
var bufInt = (buf.readUInt8(1) << 8) + buf.readUInt8(0);

1

读取/写入64位值:

const int64 = Date.now()   // 1456909977176 (00 00 01 53 36 9a 06 58)
const b = new Buffer(8)
const MAX_UINT32 = 0xFFFFFFFF

// write
const big = ~~(int64 / MAX_UINT32)
const low = (int64 % MAX_UINT32) - big

b.writeUInt32BE(big, 0)  // 00 00 01 53 00 00 00 00
b.writeUInt32BE(low, 4)  // 00 00 01 53 36 9a 06 58

// read
var time = parseInt(b.toString('hex'), 16)
time == int64 // true

我使用此代码而无需任何特殊模块。

更新

仅适用于数字<= Number.MAX_SAFE_INTEGER


1
const low = int64 & MAX_UINT32 稍微简短一点 ^_^ - evilive
1
给定以下数字:18446744073709552000。~~(18446744073709552000 / 0xFFFFFFFF) 的结果为1,(18446744073709552000 % 0xFFFFFFFF) - 1的结果为0。这段代码有问题。 - Warty
@Warty 是的,你是对的(这是一种适用于某些数字的小“hack”。我很快就会修复我的代码。谢谢)使用 bignum - ReklatsMasters
1
我认为你应该使用0x0100000000进行除法和取模运算。 考虑int64 = 0xFFFFFFFF (=4294967295)的情况: high应该= 0,low应该= 4294967295,buffer应该= <00 00 00 00 ff ff ff ff> 但在你的情况下,high = 1,low = -1,buffer = <00 00 00 01 00 00 00 00>这是正确的代码:const high = Math.trunc(int64 / 0x0100000000); const low = int64 % 0x0100000000;每当最低的int32为FFFFFFFF时,这也将是错误的。 如果您正在使用它来转换日期,则可能会在49.7天后出现错误。 - boinged

0

JavaScript/EcmaScript中的位运算将强制将数字(或任何值)转换为32位整数。

如果您需要特定精度或处理具有大于32位值的缓冲区,则确实需要使用bignum来处理更大的值。

var bignum = require('bignum');

//max safe integer to big number
var num = bignum(Number.MAX_SAFE_INTEGER.toString());
var buf = num.toBuffer({endian:'big',size:8 /*8-byte / 64-bit*/});

console.log(buf); // 

上面的示例使用了Number.MAX_SAFE_INTEGER,它等于Math.pow(2,53)-1。如果您需要使用更大的数字,则应将它们作为大数字符串访问... 如果您可以在JS中保持整数范围内,则可以保留它们并按上述方式进行转换。

0
// sending time
var sending_time = new Date().getTime();
buffer.writeInt32LE(parseInt(sending_time & 0xffffffff, 10), 16);
buffer.writeInt32LE(parseInt(sending_time / 0xffffffff, 10), 20);

0

这不是问题的确切答案。
扩展问题,这里我展示一个假设将64位值转换为缓冲区值的方法。

  • 写入缓冲区
buf = Buffer.alloc(8)
view = new DataView(buf.buffer)
view.setBigInt64(0, BigInt(0x1826d1705e0), false) // true when little endian
console.log(buf)
// Output: <Buffer 00 00 01 82 6d 17 05 e0>
  • 从缓冲区读取
view = new DataView(buf.buffer)
n = Number(view.getBigUint64(0, false))
console.log(n.toString(16))
// Output: 1826d1705e0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接