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
Some browsers are starting to support the experimental BigInt global object:
BigIntis 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}` );
}
);