web-dev-qa-db-fra.com

Quelle est la façon la plus simple de copier un objet ArrayBuffer?

Je travaille avec des objets ArrayBuffer et je voudrais les dupliquer. Bien que ce soit assez facile avec les pointeurs réels et memcpy, je n'ai trouvé aucun moyen simple de le faire en Javascript.

En ce moment, voici comment je copie mon ArrayBuffers:

function copy(buffer)
{
    var bytes = new Uint8Array(buffer);
    var output = new ArrayBuffer(buffer.byteLength);
    var outputBytes = new Uint8Array(output);
    for (var i = 0; i < bytes.length; i++)
        outputBytes[i] = bytes[i];
    return output;
}

Existe-t-il une manière plus jolie?

24
zneak

ArrayBuffer est censé prendre en charge slice (http://www.khronos.org/registry/typedarray/specs/latest/) afin que vous puissiez essayer,

buffer.slice(0);

qui fonctionne dans Chrome 18 mais pas Firefox 10 ou 11. Quant à Firefox, vous devez le copier manuellement. Vous pouvez patcher singe la slice() dans Firefox car la Chrome slice() surpassera une copie manuelle. Cela ressemblerait à quelque chose,

if (!ArrayBuffer.prototype.slice)
    ArrayBuffer.prototype.slice = function (start, end) {
        var that = new Uint8Array(this);
        if (end == undefined) end = that.length;
        var result = new ArrayBuffer(end - start);
        var resultArray = new Uint8Array(result);
        for (var i = 0; i < resultArray.length; i++)
           resultArray[i] = that[i + start];
        return result;
    }

Ensuite, vous pouvez appeler,

buffer.slice(0);

pour copier le tableau à la fois dans Chrome et Firefox.

26
chuckj

Je préfère la méthode suivante

function copy(src)  {
    var dst = new ArrayBuffer(src.byteLength);
    new Uint8Array(dst).set(new Uint8Array(src));
    return dst;
}
41
Gleno

Il semble que le simple passage de la vue de données source effectue une copie:

var a = new Uint8Array([2,3,4,5]);
var b = new Uint8Array(a);
a[0] = 6;
console.log(a); // [6, 3, 4, 5]
console.log(b); // [2, 3, 4, 5]

Testé en FF 33 et Chrome 36.

23
Andrew

Hmmm ... si c'est le Uint8Array que vous souhaitez découper (ce qui devrait logiquement être le cas), cela peut fonctionner.

 if (!Uint8Array.prototype.slice && 'subarray' in Uint8Array.prototype)
     Uint8Array.prototype.slice = Uint8Array.prototype.subarray;
2
Orwellophile

Version plus rapide et légèrement plus compliquée de la réponse de chuckj. Doit utiliser environ 8 fois moins d'opérations de copie sur de grands tableaux typés. Fondamentalement, nous copions autant de morceaux de 8 octets que possible, puis copions les 0 à 7 octets restants. Ceci est particulièrement utile dans la version actuelle d'IE, car il n'a pas de méthode de tranche implémentée pour ArrayBuffer.

if (!ArrayBuffer.prototype.slice)
    ArrayBuffer.prototype.slice = function (start, end) {
    if (end == undefined) end = that.length;
    var length = end - start;
    var lengthDouble = Math.floor(length / Float64Array.BYTES_PER_ELEMENT); 
    // ArrayBuffer that will be returned
    var result = new ArrayBuffer(length);

    var that = new Float64Array(this, start, lengthDouble)
    var resultArray = new Float64Array(result, 0, lengthDouble);

    for (var i = 0; i < resultArray.length; i++)
       resultArray[i] = that[i];

    // copying over the remaining bytes
    that = new Uint8Array(this, start + lengthDouble * Float64Array.BYTES_PER_ELEMENT)
    resultArray = new Uint8Array(result, lengthDouble * Float64Array.BYTES_PER_ELEMENT);

    for (var i = 0; i < resultArray.length; i++)
       resultArray[i] = that[i];

    return result;
}
2
xmichaelx

Dans certains cas (comme les audiobuffers webaudio), vous ne disposez que d'une référence aux 2 baies.
Donc, si vous avez array1 comme float32Array et array2 comme float32Array,
vous devez faire une copie élément par élément.

Pour ce faire, vous pouvez utiliser différentes méthodes.

            var ib=z.inputBuffer.getChannelData(0);
            var ob=z.outputBuffer.getChannelData(0);

ce

            ib.forEach((chd,i)=>ob[i]=chd);

ou c'est plus agréable et probablement plus rapide

            ob.set(ib);

En effet, Array.set remplit un tableau existant avec plusieurs données (même à partir d'un autre tableau)

0
Zibri