How To Read 64-bit Integer From An Arraybuffer / Dataview In Javascript
Solution 1:
Based on the original experiment and Sebastian Speitel
's suggestion/fix, this function returns a 64-bit value until precision is lost after Number.MAX_SAFE_INTEGER
DataView.prototype.getUint64 = function(byteOffset, littleEndian) {
// split 64-bit number into two 32-bit partsconst left = this.getUint32(byteOffset, littleEndian);
const right = this.getUint32(byteOffset+4, littleEndian);
// combine the two 32-bit valuesconst combined = littleEndian? left + 2**32*right : 2**32*left + right;
if (!Number.isSafeInteger(combined))
console.warn(combined, 'exceeds MAX_SAFE_INTEGER. Precision may be lost');
return combined;
}
Tested below:
// [byteArray, littleEndian, expectedValue]const testValues = [
// big-endian
[newUint8Array([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff]), false, 255],
[newUint8Array([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff]), false, 65535],
[newUint8Array([0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff]), false, 4294967295],
[newUint8Array([0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00]), false, 4294967296],
[newUint8Array([0x00, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]), false, 9007199254740991], // maximum precision
[newUint8Array([0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]), false, 9007199254740992], // precision lost
[newUint8Array([0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01]), false, 9007199254740992], // precision lost// little-endian
[newUint8Array([0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]), true, 255],
[newUint8Array([0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]), true, 65535],
[newUint8Array([0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00]), true, 4294967295],
[newUint8Array([0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00]), true, 4294967296],
[newUint8Array([0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00]), true, 1099511627776],
[newUint8Array([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00]), true, 281474976710656],
[newUint8Array([0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1f, 0x00]), true, 9007199254740991], // maximum precision
];
testValues.forEach(testGetUint64);
functiontestGetUint64([bytes, littleEndian, expectedValue]) {
const val = newDataView(bytes.buffer).getUint64(0, littleEndian);
console.log(val === expectedValue? 'pass' : 'FAIL. expected '+expectedValue+', received '+val);
}
Solution 2:
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 2, 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
functiongetBigUint64( view, position, littleEndian = false)
{
if ( "getBigUint64"inDataView.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
{
letvalue = 0n;
let isNegative = ( view.getUint8( position + ( littleEndian ? 7 : 0 ) ) & 0x80 ) > 0;
let carrying = true;
for ( let i = 0; i < 8; i++ )
{
letbyte = 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;
}
returnvalue;
}
}
Tests:
functiongetBigInt64( view, position, littleEndian = false)
{
if ( "getBigInt64"inDataView.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
[newUint8Array([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff]), false, 255n],
[newUint8Array([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff]), false, 65535n],
[newUint8Array([0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff]), false, 4294967295n],
[newUint8Array([0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00]), false, 4294967296n],
[newUint8Array([0x00, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]), false, 9007199254740991n],
[newUint8Array([0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]), false, 9007199254740992n],
[newUint8Array([0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01]), false, 9007199254740993n],
[newUint8Array([0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]), false, (2n**63n)-1n],
[newUint8Array([0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]), false, -1n],
[newUint8Array([0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00]), false, -256n],
[newUint8Array([0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF]), false, -257n],
[newUint8Array([0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]), false, -(2n**63n)],
// little-endian
[newUint8Array([0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]), true, 255n],
[newUint8Array([0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]), true, 65535n],
[newUint8Array([0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00]), true, 4294967295n],
[newUint8Array([0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00]), true, 4294967296n],
[newUint8Array([0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00]), true, 1099511627776n],
[newUint8Array([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00]), true, 281474976710656n],
[newUint8Array([0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1f, 0x00]), true, 9007199254740991n],
[newUint8Array([0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F]), true, (2n**63n)-1n],
[newUint8Array([0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]), true, -1n],
[newUint8Array([0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]), true, -256n],
[newUint8Array([0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]), true, -257n],
[newUint8Array([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80]), true, -(2n**63n)]
];
testValues.forEach(
function( [bytes, littleEndian, expectedValue] ) {
const val = getBigInt64( newDataView(bytes.buffer), 0, littleEndian );
console.log(
val === expectedValue
? 'pass'
: `FAIL. expected ${expectedValue}, received ${val}` );
}
);
Post a Comment for "How To Read 64-bit Integer From An Arraybuffer / Dataview In Javascript"