【问题标题】:nodejs write 64bit unsigned integer to buffernodejs将64位无符号整数写入缓冲区
【发布时间】:2013-02-06 14:03:49
【问题描述】:

我想以大端格式将 64 位(8 字节)大整数存储到 nodejs 缓冲区对象。

关于这个任务的问题是 nodejs 缓冲区只支持写入 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。有人介意展示这两个问题的解决方案吗?

【问题讨论】:

  • 从最新的Node.js v12.0.0开始,现在可以使用buf.writeBigInt64BE

标签: node.js integer buffer byte


【解决方案1】:

读取和写入 UINT 数,最高可达 Number.MAX_SAFE_INTEGER

这仅适用于 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】:

    我认为您正在寻找的是:

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

    将第一个数字移动 8 位并相加(而不是相乘),返回 65535


    编辑

    另一种写法是:

    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?
    • @kyogron 查看我的编辑。请记住,在 javascript 中移位很奇怪,它只适用于 32 位。我认为这不适用于值不能用 32 位表示的数字。
    • @Chad 这不适用于完整的 64 位值。你的班次也关闭了。您需要移动 32,而不是 8,这是 JS 无法做到的。
    • @Chad 澄清一下,这将适用于他在问题中提出的 16 位值,但不适用于一般情况。使用 8 个字节的 Buffer 来存储 2 个字节也是浪费空间。
    • 对数字的按位运算将数字转换为 32 位。
    【解决方案3】:

    我很困惑,因为您的 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);
    

    这会产生一个BigNum 对象,其值为18446744073709551615

    另外,为了详细说明您的示例代码,您使用的值只有 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);
    

    【讨论】:

      【解决方案4】:

      读取/写入 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
      

      我使用这段代码没有任何特殊模块。

      更新

      仅适用于号码&lt;= Number.MAX_SAFE_INTEGER

      【讨论】:

      • const low = int64 &amp; MAX_UINT32 短一点^_^
      • 从下面给定号码:18446744073709552000。~~(18446744073709552000 / 0xFFFFFFFF) 为 1,(18446744073709552000 % 0xFFFFFFFF) - 1 为 0。此代码已损坏。
      • @Warty 是的,你是对的(这是一个适用于一些数字的小“黑客”。我会尽快修复我的代码。谢谢)使用bignum
      • 我认为您应该改为除以 0x0100000000 并取模。考虑 int64 = 0xFFFFFFFF (=4294967295): high should = 0, low should = 4294967295, buffer should = 但在你的情况下 high = 1, low = -1, buffer = 这是正确的代码:const high = Math.trunc(int64 / 0x0100000000); const low = int64 % 0x0100000000; 每次最低 int32 为 FFFFFFFF 时,这也是错误的。如果您使用它来转换日期,则可能每 49.7 天出现一次错误。
      【解决方案5】:
      // sending time
      var sending_time = new Date().getTime();
      buffer.writeInt32LE(parseInt(sending_time & 0xffffffff, 10), 16);
      buffer.writeInt32LE(parseInt(sending_time / 0xffffffff, 10), 20);
      

      【讨论】:

        【解决方案6】:

        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。如果你需要使用更大的数字,它们应该作为 bignum 字符串来访问...如果你可以保持在 JS 中的 Integers 范围内,你可以保留它们并按照上面的方法进行转换。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2013-06-21
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-11-05
          • 2012-10-03
          • 2011-11-10
          相关资源
          最近更新 更多