【问题标题】:What's the most straightforward way to copy an ArrayBuffer object?复制 ArrayBuffer 对象最直接的方法是什么?
【发布时间】:2022-02-15 20:08:06
【问题描述】:

我正在使用ArrayBuffer 对象,我想复制它们。虽然使用实际指针和memcpy 这很容易,但我找不到任何直接的方法在 Javascript 中执行此操作。

现在,这就是我复制 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;
}

有没有更漂亮的方法?

【问题讨论】:

    标签: javascript typed-arrays


    【解决方案1】:

    我更喜欢下面的方法

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

    【讨论】:

    • 看来你的答案是fastest than the chosen one
    • @Viclib,请注意您可能需要为 IE 提供后备。
    • 另外请注意,不同类型的性能也可能有所不同;据我所知,Uint8 的复制速度比 Float32 快。我认为最快的是 Int32;但是你必须正确填充数据。 YMMV。
    • @Viclib Afaik,所有人。
    • @DanNissenbaum 因为 IE 什么都做不了!
    【解决方案2】:

    看来只需传入源数据视图就可以执行复制:

    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]
    

    在 FF 33 和 Chrome 36 中测试。

    【讨论】:

    • 这是一个很好的答案,而且似乎跨浏览器兼容。谢谢!
    • 确实,这比公认的答案要好得多。谢谢!
    • 注意事项:new Uint8Array(a.buffer) 确实复制缓冲区。根据您的需要,这可能很有用。当使用 ArrayBuffer 调用时,它还接受可选的 byteOffsetlength 参数。这与 Node v6 中 NodeJS 的 Buffer.from(Buffer, byteOffset, length) 非常相似。
    • 目前这是最快的方法,非常感谢!谁想看我做了个板凳jsben.ch/rkCpx(禁用慢例)
    【解决方案3】:

    ArrayBuffer 应该支持slice (http://www.khronos.org/registry/typedarray/specs/latest/) 所以你可以试试,

    buffer.slice(0);
    

    适用于 Chrome 18 但不适用于 Firefox 10 或 11。至于 Firefox,您需要手动复制它。您可以在 Firefox 中修补 slice(),因为 Chrome 的 slice() 将优于手动复制。这看起来像,

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

    那你就可以打电话了,

    buffer.slice(0);
    

    在 Chrome 和 Firefox 中复制数组。

    【讨论】:

    • 在 Chrome 中(发表此评论时为 29),ArrayBuffer 没有名为 .slice 的方法,而是 .subarray(start [, end]) 。不知道 FF 中的情况如何。
    • 自从我提出答案后,规格似乎发生了变化。我会努力更新它。 subarray() 在新标准中代替 slice()
    • 再次查看规范,ArrayBuffer 应该有slice()。类型化数组(例如Uint8Array)应该有subarray()。以上对ArrayBuffer 是正确的。
    • 请注意,subarray() 在现有缓冲区上返回一个新视图,而不是实际复制缓冲区。
    • 注意slice()的begin参数是可选的,所以你可以直接buffer.slice()
    【解决方案4】:

    嗯...如果它是您要切片的 Uint8Array(从逻辑上讲,它应该是),这可能会起作用。

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

    【讨论】:

      【解决方案5】:

      chuckj 答案的更快且稍微复杂的版本。应该在大型类型化数组上使用少约 8 倍的复制操作。基本上我们复制尽可能多的 8 字节块,然后复制剩余的 0-7 字节。这在当前版本的 IE 中特别有用,因为它没有为 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;
      }
      

      【讨论】:

      • 这会在 Android 股票浏览器中导致ArrayBuffer length minus the byteOffset is not a multiple of the element size.
      【解决方案6】:

      Buffer 包裹在ArrayBuffer 周围。这是共享内存,不会复制。然后从包装 Buffer 创建一个新的 Buffer。这将复制数据。最后得到一个新的Buffer的引用ArrayBuffer

      这是我能找到的最直接的方法。最有效率?也许吧。

      const wrappingBuffer = Buffer.from(arrayBuffer)
      const copiedBuffer = Buffer.from(wrappingBuffer)
      const copiedArrayBuffer = copiedBuffer.buffer
      

      【讨论】:

        【解决方案7】:

        在某些情况下(例如 webaudio Audiobuffers),您只能引用 2 个数组。
        因此,如果您将 array1 作为 float32Array,将 array2 作为 float32Array,
        您必须逐个元素地复制。

        为此,您可以使用不同的方法。

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

        这个

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

        或者这个更好,可能更快

                    ob.set(ib);
        

        这是因为 Array.set 用多个数据(甚至来自另一个数组)填充现有数组

        【讨论】:

          【解决方案8】:

          上面的一些操作只做“浅”拷贝。在使用 worker 和可转移数组时,您需要进行深拷贝。

          function copyTypedArray(original, deep){
              var copy;
              var kon = original.constructor;
              if(deep){
                  var len = original.length;
                  copy = new kon(len);
                  for (var k=len; --k;) {
                      copy[k] = original[k];
                  }
              } else {
                  var sBuf = original.buffer;
                  copy = new kon(sBuf);
                  copy.set(original);
              }
              return copy;
          }
          

          提示(对于困惑的人):类型化数组包含一个 ArrayBuffer,可以通过“buffer”属性获得。

          var arr = new Float32Array(8);
          arr.buffer <-- this is an ArrayBuffer
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2011-12-16
            • 2012-07-13
            • 1970-01-01
            • 2011-08-04
            • 1970-01-01
            • 1970-01-01
            • 2018-02-07
            相关资源
            最近更新 更多