如何在JavaScript中将整数编码为Uint8Array并返回整数



我需要为我的项目添加压缩,我决定使用LZJB算法,它速度快,代码小。找到此库https://github.com/copy/jslzjb-k

但是API不是很好,因为要解压缩文件,需要输入缓冲区长度(因为Uint8Array不是动态的,所以需要分配一些数据)。因此,我想将输入缓冲区的长度保存为Uint8Array的前几个字节,这样我就可以提取该值并基于该整数值创建输出Uint8Array。

我希望从integer返回Uint8Array的函数是泛型的,也许可以将字节的长度保存到第一个字节中,这样你就知道需要提取多少数据才能读取整数。我想我需要提取这些字节,并使用一些位偏移来获得原始数字。但我不知道该怎么做。

那么,我如何编写一个通用函数,将整数转换为可以嵌入到更大数组中的Uint8Array,然后提取该数字呢?

以下是工作函数(基于将javascript Integer转换为字节数组并返回)


function numberToBytes(number) {
// you can use constant number of bytes by using 8 or 4
const len = Math.ceil(Math.log2(number) / 8);
const byteArray = new Uint8Array(len);
for (let index = 0; index < byteArray.length; index++) {
const byte = number & 0xff;
byteArray[index] = byte;
number = (number - byte) / 256;
}
return byteArray;
}
function bytesToNumber(byteArray) {
let result = 0;
for (let i = byteArray.length - 1; i >= 0; i--) {
result = (result * 256) + byteArray[i];
}
return result;
}

通过使用CCD_ 1,该阵列仅具有所需的字节。如果想要固定大小,可以使用常量84。在我的例子中,我只是在第一个字节中保存了字节的长度。

一般答案

这些函数允许任何整数(它在内部使用BigInts,但可以接受Number参数)被编码到Uint8Array的任何部分中,并从中解码。这有点过头了,但我想学习如何在JS中处理任意大小的整数。

// n can be a bigint or a number
// bs is an optional Uint8Array of sufficient size
//   if unspecified, a large-enough Uint8Array will be allocated
// start (optional) is the offset 
//   where the length-prefixed number will be written
// returns the resulting Uint8Array
function writePrefixedNum(n, bs, start) {
start = start || 0;
let len = start+2; // start, length, and 1 byte min
for (let i=0x100n; i<n; i<<=8n, len ++) /* increment length */;
if (bs === undefined) {  
bs = new Uint8Array(len);
} else if (bs.length < len) {
throw `byte array too small; ${bs.length} < ${len}`;
}
let r = BigInt(n);
for (let pos = start+1; pos < len; pos++) {
bs[pos] = Number(r & 0xffn); 
r >>= 8n;
}
bs[start] = len-start-1; // write byte-count to start byte
return bs;
}
// bs must be a Uint8Array from where the number will be read
// start (optional, defaults to 0)
//    is where the length-prefixed number can be found
// returns a bigint, which can be coerced to int using Number()
function readPrefixedNum(bs, start) {
start = start || 0;
let size = bs[start]; // read byte-count from start byte
let n = 0n;
if (bs.length < start+size) {
throw `byte array too small; ${bs.length} < ${start+size}`;
}    
for (let pos = start+size; pos >= start+1; pos --) {
n <<= 8n;
n |= BigInt(bs[pos])
}
return n;
}
function test(n) {
const array = undefined;
const offset = 2;
let bs = writePrefixedNum(n, undefined, offset);
console.log(bs);
let result = readPrefixedNum(bs, offset);
console.log(n, result, "correct?", n == result)
}
test(0)
test(0x1020304050607080n)
test(0x0807060504030201n)


简单的4字节答案

此答案对往返于Uint8Arrays的4字节整数进行编码。

function intToArray(i) {
return Uint8Array.of(
(i&0xff000000)>>24,
(i&0x00ff0000)>>16,
(i&0x0000ff00)>> 8,
(i&0x000000ff)>> 0);
}
function arrayToInt(bs, start) {
start = start || 0;
const bytes = bs.subarray(start, start+4); 
let n = 0;
for (const byte of bytes.values()) {       
n = (n<<8)|byte;
}
return n;
}
for (let v of [123, 123<<8, 123<<16, 123<<24]) {
let a = intToArray(v);
let r = arrayToInt(a, 0);
console.log(v, a, r);
}

发布这一行,以防对任何希望使用2^53以下数字的人有用。这严格使用按位运算,不需要定义除输入之外的常量或值。

export const encodeUvarint = (n: number): Uint8Array => n >= 0x80 
? Uint8Array.from([(n & 0x7f) | 0x80, ...encodeUvarint(n >> 7)]) 
: Uint8Array.from([n & 0xff]);

相关内容

  • 没有找到相关文章

最新更新