【问题标题】:Store typed array in ArrayBuffer将类型化数组存储在 ArrayBuffer 中
【发布时间】:2015-06-05 21:35:30
【问题描述】:

我有一个由 Uint8 组成的 ArrayBuffer 数据。每个都是网络字节顺序(大端)。我的目标是将每个 Uint8 转换为 little-endian 顺序,然后将它们放回 ArrayBuffer 中。

我知道我可以使用类型化数组轻松分隔各个 Uint,如下所示:

var uintArr = new Uint8Array(ArrayBuffer);

从那里我可以交换每个项目的字节序,并拥有一个小字节序 Uint8 数组。

我不知道如何将该数组重新放入 ArrayBuffer。

有没有办法在 Javascript 中做到这一点?

【问题讨论】:

    标签: javascript endianness arraybuffer typed-arrays


    【解决方案1】:

    ArrayBuffer 是所有视图的通用字节数组。类型化数组只是意味着数组上有一个关联的类型,例如 uint8、int16 等。所有 Uin8Array、Int32Array 等都是 ArrayBuffer 顶部的 视图,以启用它们所代表的类型的读取和写入。

    (U)int8 数组没有字节顺序,因为它们是单字节(即没有可重新排序的内容)。字节需要表示更广泛的内容,例如 (u)int-16 或 -32(在 ES7 之前不支持 64 位整数,但您确实有 32 位和 64 位 IEEE 浮点数)。

    您通过视图读取或写入的任何内容最终都会备份到它们指向的同一个 ArrayBuffer 中 - 您甚至可以为同一个缓冲区拥有多个视图。

    要交换 16 位的顺序,您可以简单地使用 Uint16Array 视图读取 ArrayBuffer 并手动交换:

    var buffer16 = new Uint16Array(buffer); // use same buffer as for the Uin8Array view
    
    for(var i = 0, v; i < buffer16.length; i++) {
      v = buffer16[i];
      buffer16[i] = ((v & 0xff) << 8) | ((v & 0xff00) >>> 8);  // mask and shift the two bytes
    }
    

    如果您有相同缓冲区的 8 位视图,您现在可以使用新顺序读取单个字节。

    对于 32 位,您会这样做:

    var buffer32 = new Uint32Array(buffer);
    
    for(var i = 0, v; i < buffer32.length; i++) {
      v = buffer32[i];
      buffer32[i] = ((v & 0xff) << 24) |       // mask, move byte 0 to 3
                    ((v & 0xff00) << 8) |      // mask, move byte 1 to 2
                    ((v & 0xff0000) >>> 8) |   // mask, move byte 2 to 1 unsigned
                    ((v & 0xff000000) >>> 24); // mask, move byte 3 to 0 unsigned
    }
    

    但是,这些视图需要对齐的缓冲区,这意味着 (u)int16 的缓冲区长度必须对齐为偶数长度,而 32 位必须对齐到 4 个字节。

    如果缓冲区不是,您可以改为使用 DataView 在任何位置读取和写入任意长度,但会牺牲一些性能:

    var view = new DataView(buffer);
    var alignedLength = ((buffer.length / 2)|0) * 2;  // aligned to 16-bit
    
    // 16-bit
    for(var i = 0, v; i < alignedLength; i += 2) {
      v = view.getUint16(i, false);  // read as big-endian
      view.setUint16(i, v, true);    // write as little-endian
    }
    

    对于 32 位,请改用 getUint32/setUint32,将 i 增加 4。请注意,如果 ArrayBuffer 未与 2 或 4 字节对齐(通常您会对齐 buffer.length 并在循环中使用它,如图所示,使用 4 表示 32 位)。

    如果您的源缓冲区包含混合长度(例如,如果它包含原始二进制文件),您将必须根据文件格式规范解析每个值。为此,请使用 DataView。

    示例

    var buffer = new ArrayBuffer(4),
        b8 = new Uint8Array(buffer),
        b16 = new Uint16Array(buffer),
        b32 = new Uint32Array(buffer),
        view = new DataView(buffer);
    
    setData();
    show("Original unsigned big-endian");
    
    
    // swap the value using 16-bit array
    for(var i = 0, v; i < b16.length; i++) {
      v = b16[i];
      b16[i] = ((v & 0xff) << 8) | ((v & 0xff00) >>> 8);
    }
    show("Byte-order swapped 16-bits");
    
    setData();
    for(var i = 0, v; i < b32.length; i++) {
      v = b32[i];
      b32[i] = ((v & 0xff) << 24) |
               ((v & 0xff00) << 8) |
               ((v & 0xff0000) >>> 8) |
               ((v & 0xff000000) >>> 24);
    }
    show("Byte-order swapped 32-bits");
    
    setData();
    for(var i = 0, v; i < buffer.byteLength; i += 2) {
      v = view.getUint16(i, false);  // big-endian
      view.setUint16(i, v, true);    // little-endian
    }
    show("Byte-order swapped 16-bit using DataView");
    
    setData();
    for(var i = 0, v; i < buffer.byteLength; i += 4) {
      v = view.getUint32(i, false);  // big-endian
      view.setUint32(i, v, true);    // little-endian
    }
    show("Byte-order swapped 32-bit using DataView");
    
    
    function valToHex(v) {
      return (v>>>0).toString(16)
    }
    
    function setData() {
      b8[0] = 255;   // "network" order / big.endian 0xff804020
      b8[1] = 128;
      b8[2] = 64;
      b8[3] = 32;
    }
    
    function show(pre) {
      document.write(pre + ": ");
      document.write("0x" + valToHex(b8[0]));
      document.write(valToHex(b8[1]));
      document.write(valToHex(b8[2]));
      document.write(valToHex(b8[3]) + "<br>");
    }
    body {font:16px monospace}

    【讨论】:

    • 大声笑我真的没想到。当然 Uint8s 只是单字节。感谢您详细而彻底的回答。
    猜你喜欢
    • 2014-04-01
    • 2017-07-13
    • 2012-12-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-06-01
    • 2018-11-17
    • 2016-01-02
    相关资源
    最近更新 更多