如何在JavaScript中将字节、多个字节和缓冲区附加到ArrayBuffer?

33

Javascript的ArrayBuffer或者TypedArrays没有任何类型的appendByte()、appendBytes()或者appendBuffer()方法。那么如果我想一次一个值地填充一个ArrayBuffer,我该怎么做呢?

var firstVal = 0xAB;              // 1 byte
var secondVal = 0x3D7F            // 2 bytes
var anotherUint8Array = someArr;

var buffer = new ArrayBuffer();   // I don't know the length yet
var bufferArr = new UInt8Array(buffer);

// following methods do not exist. What are the alternatives for each??
bufferArr.appendByte(firstVal);
bufferArr.appendBytes(secondVal);
bufferArr.appendBuffer(anotherUint8Array);

这是一个数组,请使用数组语法r[i]=x,例如:https://github.com/rndme/download/blob/master/download.js#L69。同时请查看Uint8Array构造函数的语法:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array#Syntax - 你需要在数组缓冲区中指定大小才能像那样使用它... - dandavis
1
创建缓冲区后,无法修改其大小。 - Paul S.
2个回答

37
您可以使用新的ArrayBuffer创建一个新的TypedArray,但您无法更改现有缓冲区的大小。
function concatTypedArrays(a, b) { // a, b TypedArray of same type
    var c = new (a.constructor)(a.length + b.length);
    c.set(a, 0);
    c.set(b, a.length);
    return c;
}

现在可以做

var a = new Uint8Array(2),
    b = new Uint8Array(3);
a[0] = 1; a[1] = 2;
b[0] = 3; b[1] = 4;
concatTypedArrays(a, b); // [1, 2, 3, 4, 0] Uint8Array length 5
如果您想使用不同类型,请通过Uint8Array进行操作,因为最小的单位是字节
function concatBuffers(a, b) {
    return concatTypedArrays(
        new Uint8Array(a.buffer || a), 
        new Uint8Array(b.buffer || b)
    ).buffer;
}
这意味着.length将按预期工作,现在您可以将其转换为所选的类型化数组(确保它是一种接受缓冲区.byteLength的类型)。从这里开始,您现在可以实现任何您想要的用于连接数据的方法,例如:
function concatBytes(ui8a, byte) {
    var b = new Uint8Array(1);
    b[0] = byte;
    return concatTypedArrays(ui8a, b);
}

var u8 = new Uint8Array(0);
u8 = concatBytes(u8, 0x80); // [128]

谢谢 @Paul S. 对于这样基本的功能来说,如果它们是规范的一部分并且可以原生实现就更好了,但是你的解决方案非常不错。我还有一个问题,如果要将一个多字节值(比如一个4字节的值)追加到一个TypedArray中,应该怎么做呢?我们可以循环调用你建议的concatBytes函数多次,每次传入一个单字节,但是有没有更好的方法来完成这个任务呢? - codneto
@codneto,你的多字节存储怎么样?例如,如果你正在使用整数,你如何区分0x3D7F0x00003D7F之间的差异?是的,我同意应该有一些本地的concat,但我不认为应该有一个本地的push或_length_更改 - 这不是类型化数组的工作方式。 - Paul S.
我有一些返回4字节值的函数,我需要将它们添加到缓冲区中。我还从流中读取一些字节并附加到缓冲区中,有时基于一些标志,我必须读取1、2或4字节值,所有这些都需要附加到缓冲区中。我应该如何将这些多字节值添加到缓冲区? - codneto
@codneto 把它写成一个循环。最好的循环方式是创建一个长度为 nUint8Array 用于新数据,并在循环中设置其值,然后再与之前的数据连接起来。 - Paul S.
根据您的建议,我将 concatBytes 更改为以下内容。我认为它会起作用。**function concatBytes(ui8a, bytes) { var b = new Uint8Array(bytes.length); bytes.forEach(function (byte, index) { b[index] = byte; }); return concatTypedArrays(ui8a, b); } var u8 = new Uint8Array(0); //添加 0x80C83B u8 = concatBytes(u8, [0x80, 0xC8, 0x3B]); // 128, 200, 59]** - codneto
无法在评论中发布格式化代码,因此上面的评论中的代码不可读。因此在这里创建了一个 jsfiddle:http://jsfiddle.net/pLb3haqr/ - codneto

9

Paul的答案允许您将一个TypedArray连接到现有的TypedArray。在ES6中,您可以使用以下函数来连接多个TypedArrays:

function concatenate(resultConstructor, ...arrays) {
    let totalLength = 0;
    for (const arr of arrays) {
        totalLength += arr.length;
    }
    const result = new resultConstructor(totalLength);
    let offset = 0;
    for (const arr of arrays) {
        result.set(arr, offset);
        offset += arr.length;
    }
    return result;
}

const ta = concatenate(Uint8Array,
    Uint8Array.of(1, 2), Uint8Array.of(3, 4));
console.log(ta); // Uint8Array [1, 2, 3, 4]
console.log(ta.buffer.byteLength); // 4

添加一个新字节的方法是:
const byte = 3;
concatenate(Uint8Array, Uint8Array.of(1, 2), Uint8Array.of(byte));

这个方法可以在ExploringJS中找到。


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