JavaScript 合并 ArrayBuffer 部分

5
我需要将两个现有的arrayBuffer的两个部分合并成一个新的arrayBuffer。
我正在构建一个解析器,数据以随机大小的arrayBuffer形式出现,数据会溢出一个数组缓冲区的末尾,并进入另一个数组缓冲区的开头。因此,我需要创建一个新的输出缓冲区,并复制一个缓冲区末尾的一部分和另一个缓冲区开头的一部分。输出将只是一个Arraybuffer。
从这个演示开始,我打算使用一些偏移量创建Uint8Arrays,然后使用set,但问题是某些组合会抛出“Invalid typed array length”的错误。我事先不知道每个数组或偏移量的长度。
var buffer1 = new ArrayBuffer(8);
var buffer2 = new ArrayBuffer(8);
var buffer3 = new ArrayBuffer(8);

var uint8_1 = new Uint8Array(buffer1);
var uint8_2 = new Uint8Array(buffer2);
var uint8_3 = new Uint8Array(buffer3);

uint8_1.fill(1);
uint8_2.fill(2);

var uint8_1_slice = new Uint8Array(buffer1 , 0 , 3);
var uint8_2_slice = new Uint8Array(buffer2 , 4, 7);

为了这个演示,需要将buffer3设置为1,1,1,1,2,2,2,2。

无法使用切片

2个回答

4

我看到有些人只使用array.length。如果数组每个元素只占1个字节,那么这样做是可以的。如果其他类型的数组被填满了,也可以这样做,但在这个例子中a2没有被填满。因此最好使用byteLength,这也是Blob构造函数连接部分的方式。

// Concatenate a mix of typed arrays
function concatenate(...arrays) {
  // Calculate byteSize from all arrays
  let size = arrays.reduce((a,b) => a + b.byteLength, 0)
  // Allcolate a new buffer
  let result = new Uint8Array(size)

  // Build the new array
  let offset = 0
  for (let arr of arrays) {
    result.set(arr, offset)
    offset += arr.byteLength
  }

  return result
}

// the total length of 1-3 = 5
// the total byteLength of 1-3 = 6
let a1 = Uint8Array.of(1, 2) // [1, 2]
let a2 = Uint16Array.of(3) // [3] just for the fun of it 16 takes up 2 bytes
let a3 = Uint8Array.of(4, 5) // [4, 5]

concatenate(a1, a2, a3) // [1, 2, 3, 0, 4, 5]

/********/
var blob = new Blob([a1, a2, a3])
var res = new Response(blob)
res.arrayBuffer().then(buffer => console.log(new Uint8Array(buffer)))
// [1, 2, 3, 0, 4, 5]

注意:此“concatenate”函数仅支持Uint8Array参数。例如,concatenate(new Uint16Array([1,2,3]))返回[1, 2, 3, 0, 0, 0]而不是[1, 0, 2, 0, 3, 0]。问题在于Uint8Array.set()遍历给定类型数组的元素而不是字节。解决方法是将result.set(arr, offset)更改为result.set(new Uint8Array(arr.buffer, arr.byteOffset, arr.byteLength), offset)。(请注意,对ArrayBuffer参数的支持需要额外的逻辑。) - tom

1

在这个演示中,需要将buffer3设置为1,1,1,1,2,2,2,2。

您可以使用for循环,如果变量n小于uint8_1.byteLength / 2,则将uint8_3设置为uint8_1的值,否则将uint8_3设置为uint8_2上的值。

var len = 8;

var buffer1 = new ArrayBuffer(len);
var buffer2 = new ArrayBuffer(len);
var buffer3 = new ArrayBuffer(len);

var uint8_1 = new Uint8Array(buffer1);
var uint8_2 = new Uint8Array(buffer2);
var uint8_3 = new Uint8Array(buffer3);

uint8_1.fill(1);
uint8_2.fill(2);
// `len` : uint8_1.byteLength / 2 + uint8_2.byteLength / 2
for (var n = 0; n < len; n++) {
  uint8_3[n] = n < len / 2 ? uint8_1[n] : uint8_2[n];
}

console.log(uint8_3);


这是正确的方向,但应该使用set,因为如果不这样做,你每次只复制1个字节。 - brianxautumn
实际上很抱歉,不能使用切片,它会返回一个新数组。 - brianxautumn
@brianxautumn “实际上很抱歉,不能使用切片,因为它会返回一个新的数组。” 不确定你的意思是什么?是否要求不创建新的数组? - guest271314
是的,在这种情况下,速度和内存使用非常非常重要。 - brianxautumn
@brianxautumn:“更好了,但问题是它一次只复制1个字节,而它可以一次复制多达8个字节。” 是的,要求是不使用.slice(),“不能使用slice,因为它会返回一个新数组”?.set()不需要第三个参数。.subarray也会返回一个新数组。 - guest271314
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接