I am open to both BE and LE, but I would like to understand why these three methods yield different outcomes.
The discrepancies in the results stem from their usage of varying endianness.
Let's transform your code snippets into an executable format for comparison:
let source_array = new Uint8Array([
0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88,
0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11]);
let buffer = source_array.buffer;
function method1(buf) {
let bits = 8n
if (ArrayBuffer.isView(buf)) {
bits = BigInt(buf.BYTES_PER_ELEMENT * 8)
} else {
buf = new Uint8Array(buf)
}
let ret = 0n
for (const i of buf.values()) {
const bi = BigInt(i)
ret = (ret << bits) + bi
}
return ret
}
function method2(buf) {
let view = new DataView(buf, 0);
return view.getBigUint64(0, true);
}
function method3(buf) {
let arr = new Uint8Array(buf);
let result = BigInt(0);
for (let i = arr.length - 1; i >= 0; i--) {
result = result * BigInt(256) + BigInt(arr[i]);
}
return result;
}
console.log(method1(buffer).toString(16));
console.log(method2(buffer).toString(16));
console.log(method3(buffer).toString(16));
Please note that a bug fix has been applied to "method3": change i++)
to i--
at the end of your loop statement.
Here are the outputs of each method:
"method1" output: ffeeddccbbaa998877665544332211
. This method represents big-endian conversion without size limitations.
"method2" output: 8899aabbccddeeff
. This method involves little-endian conversion limited to 64 bits. Changing the second argument in getBigUint64
can switch between little-endian and big-endian behavior.
"method3" output: 112233445566778899aabbccddeeff
. This method depicts little-endian conversion without size restrictions. Adjusting the direction of the loop will yield big-endian behavior similar to method1.
To select the appropriate method, consider factors such as the endianness of incoming arrays, size limits on BigInts, and performance requirements.
If you have control over the entire process of converting BigInts to Uint8Arrays and vice versa, using hexadecimal strings could be a simpler and faster alternative:
function serialize(bigint) {
return "0x" + bigint.toString(16);
}
function deserialize(serialized_bigint) {
return BigInt(serialized_bigint);
}