【问题标题】:Resize ArrayBuffer调整 ArrayBuffer 的大小
【发布时间】:2013-09-03 20:26:50
【问题描述】:

如果我想创建一个arraybuffer,我写:var buff = new ArrayBuffer(size)

但是如何调整现有缓冲区的大小呢?我的意思是,在缓冲区末尾添加更多字节。

【问题讨论】:

    标签: javascript arrays


    【解决方案1】:

    ArrayBuffer 本身没有设置。不过,TypedArray 上有 set。像这样使用:

    var oldBuffer = new ArrayBuffer(20);
    
    var newBuffer = new ArrayBuffer(40);
    new Uint8Array(newBuffer).set(oldBuffer);
    

    【讨论】:

    • set() 有效,但不要那样使用,因为 set 不返回值,您将没有指向新数组的指针
    • 给出的例子工作得很好。该代码不需要指向Uint8Array 的指针,因为它仅使用它来访问set 函数
    • 或者换句话说,如果您不需要视图,那么可以这样调用,否则您应该首先将视图分配给变量
    • 我不知道为什么,但在我的 Firefox 65 中提供的示例不起作用。在new Uint8Array(newBuffer).set(oldBuffer) newBuffer 之后包含零。为了解决这个问题,我必须向 set() 提供类型化数组:new Uint8Array(newBuffer).set(new Uint8Array(oldBuffer))——在这种情况下,它可以按预期工作。
    • 确保传递一个 ArrayBuffer 作为 set() 的参数。如果你通过另一个Uint8Array 并且它更大,你会得到一个RangeError
    【解决方案2】:

    这样做的方法是ArrayBuffer.transfer(oldBuffer, newByteLength),就像这样:

    var buffA = new ArrayBuffer(30);
    var buffB = ArrayBuffer.transfer(buffA, 40);
    
    // buffA is now an empty array buffer (`buffA.byteLength === 0`)
    // whereas buffB is a new buffer that starts with a's contents
    // and ends with 10 bytes that are 0s.
    
    // Much faster than manually copying. You could even just do:
    
    var buffA = new ArrayBuffer(30);
    buffA = ArrayBuffer.transfer(buffA, 40);
    
    // Or as a prototype method
    
    ArrayBuffer.prototype.resize = function(newByteLength) {
        return ArrayBuffer.transfer(this, newByteLength);
    }
    
    var buffA = new ArrayBuffer(30);
    buffA = buffA.resize(40);
    

    但是(截至 2017 年 10 月)浏览器支持率为 0%(甚至不支持 Node.js),甚至还没有起草。


    在此可用之前,您可以使用页面上给出的 polyfill,它一次复制 8 个字节,因此对于大型数组来说相对较快(尽管它不会清空给定的数组,这是不可能的)。

    这是一个使用 <em>TypeArray</em>.prototype.set 代替 for 循环的修改版本:

    if (!ArrayBuffer.transfer) {
      ArrayBuffer.transfer = function(oldBuffer, newByteLength) {
        var srcBuffer = Object(oldBuffer);
        var destBuffer = new ArrayBuffer(newByteLength);
        if (!(srcBuffer instanceof ArrayBuffer) || !(destBuffer instanceof ArrayBuffer)) {
          throw new TypeError('Source and destination must be ArrayBuffer instances');
        }
        var copylen = Math.min(srcBuffer.byteLength, destBuffer.byteLength);
    
        /* Copy 8 bytes at a time */
        var length = Math.trunc(copylen / 64);
        (new Float64Array(destBuffer, 0, length))
          .set(new Float64Array(srcBuffer, 0, length));
    
        /* Copy the remaining 0 to 7 bytes, 1 byte at a time */
        var offset = length * 64;
        length = copylen - offset;
        (new Uint8Array(srcBuffer, offset, length))
          .set(new Uint8Array(destBuffer, offset, length));
    
        return destBuffer;
      };
    }
    

    [编辑]

    上面的替代版本,不使用 ArrayBuffer 命名空间,并采用稍微不同的方法

              function arrayBufferTransfer(oldBuffer, newByteLength) {
                 const 
                 srcArray  = new Uint8Array(oldBuffer),
                 destArray = new Uint8Array(newByteLength),
                 copylen = Math.min(srcArray.buffer.byteLength, destArray.buffer.byteLength),
                 floatArrayLength   = Math.trunc(copylen / 8),
                 floatArraySource   = new Float64Array(srcArray.buffer,0,floatArrayLength),
                 floarArrayDest     = new Float64Array(destArray.buffer,0,floatArrayLength);
                 
                 floarArrayDest.set(floatArraySource);
                     
                 let bytesCopied = floatArrayLength * 8;
                 
             
                 // slowpoke copy up to 7 bytes.
                 while (bytesCopied < copylen ) {
                     destArray[bytesCopied]=srcArray[bytesCopied];
                     bytesCopied++;
                 }
                 
               
                 return destArray.buffer;
             }
    

    【讨论】:

    • 自从写了这个答案后,引用页面上列出的 polyfill 已经更新为没有循环的版本,因此值得快速查看。
    • 注意!if(!ArrayBuffer.transfer) { ...} 这样进行功能检查是一个非常糟糕的主意。您应该直接覆盖它而不进行检查。因为如果ArrayBuffer.transfer 方法被实现,但最终具有不同的功能(例如不同的参数,或以不同的顺序,或其他),那么当浏览器实现真正的ArrayBuffer.transfer 时,您的代码将突然中断。如果标准已经一成不变(只是还没有在运行时实现),那就没问题了。
    • 提案被撤回,所以这个答案已经死了
    【解决方案3】:

    您正在寻找的是“设置”方法。创建一个更大的新数组后,只需调用 set 函数并复制旧内容即可。

    【讨论】:

    • ArrayBuffer 上没有 .set() 方法。
    • 您必须在ArrayBuffer 上创建一个类型化数组(视图),然后调用set
    【解决方案4】:

    你不能。

    From the MDN

    ArrayBuffer 是一种用于表示泛型的数据类型, 固定长度二进制数据缓冲区。

    我不知道你想做什么,但显然你并没有按照它们的本意使用它们。

    This page 是学习如何在 JavaScript 中使用类型化数组的良好起点。

    【讨论】:

    【解决方案5】:
    var buff = new ArrayBuffer(32);
    buff[31] = 43;
    var newBuff = new ArrayBuffer(buff.byteLength*2);
    
    for (var i=0;i<buff.byteLength;i++){
        newBuff[i] = buff[i];
    }
    
    buff = newBuff;
    

    我已经像这样在 C++ 中完成了它。只需制作一个更大的数组并将内容复制过来,然后返回更大的数组并将其设置为原始数组。

    【讨论】:

    • 在 JS 中应该使用 TypedArray.prototype.set() 在数组之间进行复制,正如 Jon Lennart Aasenden 所建议的那样。
    • 我很困惑为什么接受这个。 ArrayBuffer 的内容不能直接使用[] 访问,而只能通过视图,例如类型化数组 和@ 987654324@s. 代码处理的是属性,而不是那些内容。你看内容:console.log(new Uint8Array(buf));.
    • set() 并不总是可以接受的,从共享缓冲区复制到普通数组缓冲区时速度很慢
    • 这永远行不通。上面的代码所做的就是在数组缓冲区上创建新属性。它实际上并没有复制内容。就像这样做:` var arr={}; arr[31] = 43;`
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-05-15
    • 2010-12-11
    • 2016-06-18
    • 1970-01-01
    相关资源
    最近更新 更多