How to Read 64-bit Integer from an ArrayBuffer / DataView in JavaScript

后端 未结 2 569
深忆病人
深忆病人 2020-12-17 06:21

Given a 64-bit (8-byte) little-endian ArrayBuffer of bytes, how can we read 64-bit integer values in JavaScript?

I experimented and came up with this, b

2条回答
  •  旧时难觅i
    2020-12-17 06:53

    Some browsers are starting to support the experimental BigInt global object:

    BigInt is a built-in object that provides a way to represent whole numbers larger than 253, which is the largest number JavaScript can reliably represent with the Number primitive.

    If you are only targeting these browsers then you can use this to get larger values than can be supported by a Number. Additionally, Chrome currently supports the DataView.getBigInt64( position, littleEndian ) and DataView.getBigUint64( position, littleEndian ) functions that return BigInt values.

    Read 64-bit unsigned values

    function getBigUint64( view, position, littleEndian = false )
    {
      if ( "getBigUint64" in DataView.prototype )
      {
        return view.getBigUint64( position, littleEndian );
      }
      else
      {
        const lsb = BigInt( view.getUint32( position + (littleEndian ? 0 : 4), littleEndian ) );
        const gsb = BigInt( view.getUint32( position + (littleEndian ? 4 : 0), littleEndian ) );
        return lsb + 4294967296n * gsb;
      }
    }
    

    Read 64-bit signed values:

    function getBigInt64( view, position, littleEndian = false )
    {
      if ( "getBigInt64" in DataView.prototype )
      {
        return view.getBigInt64( position, littleEndian );
      }
      else
      {
        let value = 0n;
        let isNegative = ( view.getUint8( position + ( littleEndian ? 7 : 0 ) ) & 0x80 ) > 0;
        let carrying = true;
        for ( let i = 0; i < 8; i++ )
        {
          let byte = view.getUint8( position + ( littleEndian ? i : 7 - i ) );
          if ( isNegative )
          {
            if ( carrying )
            {
              if ( byte != 0x00 )
              {
                byte = (~(byte - 1))&0xFF;
                carrying = false;
              }
            }
            else
            {
              byte = (~byte)&0xFF;
            }
          }
          value += BigInt(byte) * 256n**BigInt(i);
        }
        if ( isNegative )
        {
          value = -value;
        }
        return value;
      }
    }
    

    Tests:

    function getBigInt64( view, position, littleEndian = false )
    {
      if ( "getBigInt64" in DataView.prototype )
      {
        return view.getBigInt64( position, littleEndian );
      }
      else
      {
        let value = 0n;
        let isNegative = ( view.getUint8( position + ( littleEndian ? 7 : 0 ) ) & 0x80 ) > 0;
        let carrying = true;
        for ( let i = 0; i < 8; i++ )
        {
          let byte = view.getUint8( position + ( littleEndian ? i : 7 - i ) );
          if ( isNegative )
          {
            if ( carrying )
            {
              if ( byte != 0x00 )
              {
                byte = (~(byte - 1))&0xFF;
                carrying = false;
              }
            }
            else
            {
              byte = (~byte)&0xFF;
            }
          }
          value += BigInt(byte) * 256n**BigInt(i);
        }
        if ( isNegative )
        {
          value = -value;
        }
        return value;
      }
    }
    
    // [byteArray, littleEndian, expectedValue]
    const testValues = [
      // big-endian
      [new Uint8Array([0x00, 0x00, 0x00, 0x00,  0x00, 0x00, 0x00, 0xff]),  false, 255n], 
      [new Uint8Array([0x00, 0x00, 0x00, 0x00,  0x00, 0x00, 0xff, 0xff]),  false, 65535n],
      [new Uint8Array([0x00, 0x00, 0x00, 0x00,  0xff, 0xff, 0xff, 0xff]),  false, 4294967295n],
      [new Uint8Array([0x00, 0x00, 0x00, 0x01,  0x00, 0x00, 0x00, 0x00]),  false, 4294967296n],
      [new Uint8Array([0x00, 0x1f, 0xff, 0xff,  0xff, 0xff, 0xff, 0xff]),  false, 9007199254740991n],
      [new Uint8Array([0x00, 0x20, 0x00, 0x00,  0x00, 0x00, 0x00, 0x00]),  false, 9007199254740992n],
      [new Uint8Array([0x00, 0x20, 0x00, 0x00,  0x00, 0x00, 0x00, 0x01]),  false, 9007199254740993n],
      [new Uint8Array([0x7F, 0xFF, 0xFF, 0xFF,  0xFF, 0xFF, 0xFF, 0xFF]),  false, (2n**63n)-1n],
      [new Uint8Array([0xFF, 0xFF, 0xFF, 0xFF,  0xFF, 0xFF, 0xFF, 0xFF]),  false, -1n],
      [new Uint8Array([0xFF, 0xFF, 0xFF, 0xFF,  0xFF, 0xFF, 0xFF, 0x00]),  false, -256n],
      [new Uint8Array([0xFF, 0xFF, 0xFF, 0xFF,  0xFF, 0xFF, 0xFE, 0xFF]),  false, -257n],
      [new Uint8Array([0x80, 0x00, 0x00, 0x00,  0x00, 0x00, 0x00, 0x00]),  false, -(2n**63n)],
    
      // little-endian
      [new Uint8Array([0xff, 0x00, 0x00, 0x00,  0x00, 0x00, 0x00, 0x00]),  true, 255n], 
      [new Uint8Array([0xff, 0xff, 0x00, 0x00,  0x00, 0x00, 0x00, 0x00]),  true, 65535n],
      [new Uint8Array([0xff, 0xff, 0xff, 0xff,  0x00, 0x00, 0x00, 0x00]),  true, 4294967295n],
      [new Uint8Array([0x00, 0x00, 0x00, 0x00,  0x01, 0x00, 0x00, 0x00]),  true, 4294967296n],
      [new Uint8Array([0x00, 0x00, 0x00, 0x00,  0x00, 0x01, 0x00, 0x00]),  true, 1099511627776n],
      [new Uint8Array([0x00, 0x00, 0x00, 0x00,  0x00, 0x00, 0x01, 0x00]),  true, 281474976710656n],
      [new Uint8Array([0xff, 0xff, 0xff, 0xff,  0xff, 0xff, 0x1f, 0x00]),  true, 9007199254740991n],
      [new Uint8Array([0xFF, 0xFF, 0xFF, 0xFF,  0xFF, 0xFF, 0xFF, 0x7F]),  true, (2n**63n)-1n],
      [new Uint8Array([0xFF, 0xFF, 0xFF, 0xFF,  0xFF, 0xFF, 0xFF, 0xFF]),  true, -1n],
      [new Uint8Array([0x00, 0xFF, 0xFF, 0xFF,  0xFF, 0xFF, 0xFF, 0xFF]),  true, -256n],
      [new Uint8Array([0xFF, 0xFE, 0xFF, 0xFF,  0xFF, 0xFF, 0xFF, 0xFF]),  true, -257n],
      [new Uint8Array([0x00, 0x00, 0x00, 0x00,  0x00, 0x00, 0x00, 0x80]),  true, -(2n**63n)]
    ];
    
    testValues.forEach(
      function( [bytes, littleEndian, expectedValue] ) {
        const val = getBigInt64( new DataView(bytes.buffer), 0, littleEndian );
        console.log(
          val === expectedValue
                    ? 'pass'
                    : `FAIL. expected ${expectedValue}, received ${val}` );
      }
    );

提交回复
热议问题