在Javascript中将Uint8Array转换为BigInt

fcg9iug3  于 2022-10-30  发布在  Java
关注(0)|答案(2)|浏览(371)

我找到了3种方法将Uint 8Array转换为BigInt,但由于某些原因,它们都给予了不同的结果。您能告诉我哪一种是正确的,我应该使用哪一种吗?
1.利用bigint-转换库.我们可以使用bigintConversion.bufToBigint()函数得到一个BigInt.实现如下:

export function bufToBigint (buf: ArrayBuffer|TypedArray|Buffer): bigint {
  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 as TypedArray|Buffer).values()) {
    const bi = BigInt(i)
    ret = (ret << bits) + bi
  }
  return ret
}

1.使用数据视图:

let view = new DataView(arr.buffer, 0);
let result = view.getBigUint64(0, true);

1.使用FOR循环:

let result = BigInt(0);
for (let i = arr.length - 1; i >= 0; i++) {
  result = result * BigInt(256) + BigInt(arr[i]);
}

老实说,我很困惑哪一个是正确的,因为它们都给予了不同的结果,但确实给出了结果。

f4t66c6m

f4t66c6m1#

我对BE或LE都没意见,但我只是想知道为什么这三种方法会给予不同的结果。
产生不同结果的一个原因是它们使用了不同的字节序。
让我们将您的代码片段转换为一个表单,以便执行和比较它们:

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));

请注意,这包括method 3的错误修复:你写for (let i = arr.length - 1; i >= 0; i++)的地方,你的意思很明显是在结尾。
对于“method 1”,将打印:ffeeddccbbaa998877665544332211
因为method 1是没有大小限制的big-endian转换(数组的第一个字节是结果的最高有效位部分)。
对于“method 2”,将打印:8899aabbccddeeff
因为method 2是一个限制为64位的小字节序转换(数组的第一个字节是结果的最低有效部分)。
如果将第二个getBigUint64参数从true切换为false,则会得到big-endian行为:ffeeddccbbaa9988 .
要消除大小限制,您必须添加一个循环:使用getBigUint64可以得到64位的块,可以使用类似于method 1和method 3的移位来组装这些块。
对于“method 3”,将打印:112233445566778899aabbccddeeff
因为method 3是一个没有大小限制的小字节序转换。如果你反转for-循环的方向,你会得到和method 1一样的大字节序行为:result * 256n给出与result << 8n相同的值;后者稍快一些。
(Side注意:BigInt(0)BigInt(256)是不必要冗余,只需编写0n256n即可。其他优点:123456789123456789n可以实现您所期望的功能,而BigInt(123456789123456789)却不能。)
那么,您应该使用哪种方法呢?这取决于:
(1)传入数组采用BE编码还是LE编码?
(2)您的BigInt限制为64位还是任意大?
(3)这是性能关键型代码,还是所有方法都“足够快”?
退一步说:如果您控制整个过程的两个部分(将BigInt转换为Uint 8Array,然后传输/存储它们,然后再转换回BigInt),请考虑直接使用十六进制字符串:更容易编码,更容易调试,而且速度更快。比如:

function serialize(bigint) {
  return "0x" + bigint.toString(16);
}
function deserialize(serialized_bigint) {
  return BigInt(serialized_bigint);
}
qyswt5oh

qyswt5oh2#

如果您需要存储不绑定到任何base64或128的真正大的整数,并且还需要保留负数,那么这是一个适合您的解决方案...
第一个

相关问题