将二进制NodeJS缓冲区转换为JavaScript ArrayBuffer

时间:2011-12-22 20:21:07

标签: javascript node.js binary buffer arraybuffer

如何将NodeJS二进制缓冲区转换为JavaScript ArrayBuffer?

12 个答案:

答案 0 :(得分:96)

node.js 4.x及更高版本中的

Instances of Buffer are also instances of Uint8Array。因此,最有效的解决方案是根据https://stackoverflow.com/a/31394257/1375574直接访问buf.buffer属性。如果你需要转向另一个方向,Buffer构造函数也会获取一个ArrayBufferView参数。

请注意,这不会创建副本,这意味着对任何ArrayBufferView的写入将写入原始的Buffer实例。

<小时/> 在旧版本中,node.js将ArrayBuffer作为v8的一部分,但Buffer类提供了更灵活的API。为了读取或写入ArrayBuffer,您只需要创建一个视图并进行复制。

从Buffer到ArrayBuffer:

function toArrayBuffer(buf) {
    var ab = new ArrayBuffer(buf.length);
    var view = new Uint8Array(ab);
    for (var i = 0; i < buf.length; ++i) {
        view[i] = buf[i];
    }
    return ab;
}

从ArrayBuffer到Buffer:

function toBuffer(ab) {
    var buf = Buffer.alloc(ab.byteLength);
    var view = new Uint8Array(ab);
    for (var i = 0; i < buf.length; ++i) {
        buf[i] = view[i];
    }
    return buf;
}

答案 1 :(得分:49)

  • 无依赖关系,最快,节点4.x及更高版本

Buffers是Uint8Arrays,所以你只需要访问它的ArrayBuffer。这是 O(1)

 // node buffer
var b = new Buffer(512);
 // ArrayBuffer
var ab = b.buffer.slice(b.byteOffset, b.byteOffset + b.byteLength);
 // TypedArray
var ui32 = new Uint32Array(b.buffer, b.byteOffset, b.byteLength / Uint32Array.BYTES_PER_ELEMENT);

slice和偏移量是必需,因为小缓冲区(我认为<4096字节)是共享ArrayBuffer上的视图。如果没有它,您最终可能会得到一个包含来自另一个TypedArray的数据的ArrayBuffer。

  • 没有依赖关系,速度适中,任何版本的节点

使用在{em> O(n)时间内运行的Martin Thomson's answer。 (另请参阅我对他关于非优化的回答的评论的答复。使用DataView很慢。即使你需要翻转字节,也有更快的方法。)

  • 依赖关系,快速,任何版本的节点

您可以使用https://www.npmjs.com/package/memcpy向任一方向(Buffer to ArrayBuffer and back)。它比这里发布的其他答案更快,并且是一个写得很好的库。节点0.12到iojs 3.x需要ngossen的分支(见this)。

答案 2 :(得分:43)

“从ArrayBuffer到Buffer”可以这样做:

var buffer = Buffer.from( new Uint8Array(ab) );

答案 3 :(得分:25)

更快捷的写作方式

var arrayBuffer = new Uint8Array(nodeBuffer).buffer;

然而,在1024个元素的缓冲区上,这似乎比建议的toArrayBuffer函数慢大约4倍。

答案 4 :(得分:12)

使用以下优秀的npm包:to-arraybuffer

或者,您可以自己实施。如果您的缓冲区被称为buf,请执行以下操作:

buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength)

答案 5 :(得分:7)

1。 Buffer只是一个视图,用于查看ArrayBuffer

Buffer实际上是FastBufferextends(继承自)Uint8Array,这是一个八位字节视图(“部分访问者”)实际内存,ArrayBuffer

/lib/buffer.js#L65-L73 Node.js 9.4.0
class FastBuffer extends Uint8Array {
  constructor(arg1, arg2, arg3) {
    super(arg1, arg2, arg3);
  }
}
FastBuffer.prototype.constructor = Buffer;
internalBuffer.FastBuffer = FastBuffer;

Buffer.prototype = FastBuffer.prototype;

2。 ArrayBuffer的大小及其视图的大小可能会有所不同。

原因#1:Buffer.from(arrayBuffer[, byteOffset[, length]])

使用Buffer.from(arrayBuffer[, byteOffset[, length]]),您可以创建Buffer,并指定其基础ArrayBuffer以及视图的位置和大小。

const test_buffer = Buffer.from(new ArrayBuffer(50), 40, 10);
console.info(test_buffer.buffer.byteLength); // 50; the size of the memory.
console.info(test_buffer.length); // 10; the size of the view.

原因#2:FastBuffer的内存分配。

它根据大小以两种不同的方式分配内存。

  • 如果大小小于内存池大小的一半且不是0(“小”):它使用内存池以准备所需的内存。
  • 其他:它会创建一个完全符合所需内存的专用ArrayBuffer
/lib/buffer.js#L306-L320 Node.js 9.4.0
function allocate(size) {
  if (size <= 0) {
    return new FastBuffer();
  }
  if (size < (Buffer.poolSize >>> 1)) {
    if (size > (poolSize - poolOffset))
      createPool();
    var b = new FastBuffer(allocPool, poolOffset, size);
    poolOffset += size;
    alignPool();
    return b;
  } else {
    return createUnsafeBuffer(size);
  }
}
/lib/buffer.js#L98-L100 Node.js 9.4.0
function createUnsafeBuffer(size) {
  return new FastBuffer(createUnsafeArrayBuffer(size));
}

内存池”是什么意思?

memory pool是一个固定大小的预分配内存块,用于保存Buffer的小型内存块。使用它可以将小型内存块紧密地保持在一起,因此可以防止由小型内存块的单独管理(分配和释放)引起的fragmentation

在这种情况下,内存池是ArrayBuffer,默认大小为8 KiB,在Buffer.poolSize中指定。当它为Buffer提供一个小型内存块时,它会检查最后一个内存池是否有足够的可用内存来处理这个问题。如果是这样,它会创建一个Buffer “查看”内存池的给定部分块,否则,它会创建一个新的内存池,依此类推。

您可以访问ArrayBuffer的基础BufferBuffer的{​​{3}}(即继承自Uint8Array)拥有它。 “small” Buffer的{​​{1}}属性是buffer,代表整个内存池。所以在这种情况下, ArrayBufferArrayBuffer的大小不尽相同。

Buffer

3。所以我们需要提取内存“ views 。”

const zero_sized_buffer = Buffer.allocUnsafe(0); const small_buffer = Buffer.from([0xC0, 0xFF, 0xEE]); const big_buffer = Buffer.allocUnsafe(Buffer.poolSize >>> 1); // A `Buffer`'s `length` property holds the size, in octets, of the view. // An `ArrayBuffer`'s `byteLength` property holds the size, in octets, of its data. console.info(zero_sized_buffer.length); /// 0; the view's size. console.info(zero_sized_buffer.buffer.byteLength); /// 0; the memory..'s size. console.info(Buffer.poolSize); /// 8192; a memory pool's size. console.info(small_buffer.length); /// 3; the view's size. console.info(small_buffer.buffer.byteLength); /// 8192; the memory pool's size. console.info(Buffer.poolSize); /// 8192; a memory pool's size. console.info(big_buffer.length); /// 4096; the view's size. console.info(big_buffer.buffer.byteLength); /// 4096; the memory's size. console.info(Buffer.poolSize); /// 8192; a memory pool's size. 的大小是固定的,因此我们需要通过复制该部分来提取它。为此,我们使用ArrayBuffer的{​​{3}}和buffer property,它们是从Buffer继承的,而byteOffset property则是Uint8Array的一部分。 ArrayBuffer。此处slice()方法的灵感来自length property

const test_buffer = Buffer.from(new ArrayBuffer(10));
const zero_sized_buffer = Buffer.allocUnsafe(0);
const small_buffer = Buffer.from([0xC0, 0xFF, 0xEE]);
const big_buffer = Buffer.allocUnsafe(Buffer.poolSize >>> 1);

function extract_arraybuffer(buf)
{
    // You may use the `byteLength` property instead of the `length` one.
    return buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.length);
}

// A copy -
const test_arraybuffer = extract_arraybuffer(test_buffer); // of the memory.
const zero_sized_arraybuffer = extract_arraybuffer(zero_sized_buffer); // of the... void.
const small_arraybuffer = extract_arraybuffer(small_buffer); // of the part of the memory.
const big_arraybuffer = extract_arraybuffer(big_buffer); // of the memory.

console.info(test_arraybuffer.byteLength); // 10
console.info(zero_sized_arraybuffer.byteLength); // 0
console.info(small_arraybuffer.byteLength); // 3
console.info(big_arraybuffer.byteLength); // 4096

4。绩效改进

如果您要将结果用作只读,或者可以修改输入Buffer s'内容,则可以避免不必要的内存复制。

const test_buffer = Buffer.from(new ArrayBuffer(10));
const zero_sized_buffer = Buffer.allocUnsafe(0);
const small_buffer = Buffer.from([0xC0, 0xFF, 0xEE]);
const big_buffer = Buffer.allocUnsafe(Buffer.poolSize >>> 1);

function obtain_arraybuffer(buf)
{
    if(buf.length === buf.buffer.byteLength)
    {
        return buf.buffer;
    } // else:
    // You may use the `byteLength` property instead of the `length` one.
    return buf.subarray(0, buf.length);
}

// Its underlying `ArrayBuffer`.
const test_arraybuffer = obtain_arraybuffer(test_buffer);
// Just a zero-sized `ArrayBuffer`.
const zero_sized_arraybuffer = obtain_arraybuffer(zero_sized_buffer);
// A copy of the part of the memory.
const small_arraybuffer = obtain_arraybuffer(small_buffer);
// Its underlying `ArrayBuffer`.
const big_arraybuffer = obtain_arraybuffer(big_buffer);

console.info(test_arraybuffer.byteLength); // 10
console.info(zero_sized_arraybuffer.byteLength); // 0
console.info(small_arraybuffer.byteLength); // 3
console.info(big_arraybuffer.byteLength); // 4096

答案 6 :(得分:1)

您可以将ArrayBuffer视为已键入的Buffer

ArrayBuffer因此总是需要一个类型(所谓的&#34;数组缓冲区视图&#34;)。通常,数组缓冲区视图的类型为Uint8ArrayUint16Array

Renato Mangini在converting between an ArrayBuffer and a String上发表了一篇很好的文章。

我总结了代码示例中的基本部分(对于Node.js)。它还显示了如何在键入的ArrayBuffer和无类型Buffer之间进行转换。

function stringToArrayBuffer(string) {
  const arrayBuffer = new ArrayBuffer(string.length);
  const arrayBufferView = new Uint8Array(arrayBuffer);
  for (let i = 0; i < string.length; i++) {
    arrayBufferView[i] = string.charCodeAt(i);
  }
  return arrayBuffer;
}

function arrayBufferToString(buffer) {
  return String.fromCharCode.apply(null, new Uint8Array(buffer));
}

const helloWorld = stringToArrayBuffer('Hello, World!'); // "ArrayBuffer" (Uint8Array)
const encodedString = new Buffer(helloWorld).toString('base64'); // "string"
const decodedBuffer = Buffer.from(encodedString, 'base64'); // "Buffer"
const decodedArrayBuffer = new Uint8Array(decodedBuffer).buffer; // "ArrayBuffer" (Uint8Array)

console.log(arrayBufferToString(decodedArrayBuffer)); // prints "Hello, World!"

答案 7 :(得分:0)

我尝试了上面的Float64Array,它只是没有用。

我最终意识到确实需要以正确的块“读取”视图中的数据。这意味着从源缓冲区一次读取8个字节。

无论如何,这就是我最终的结果......

var buff = new Buffer("40100000000000004014000000000000", "hex");
var ab = new ArrayBuffer(buff.length);
var view = new Float64Array(ab);

var viewIndex = 0;
for (var bufferIndex=0;bufferIndex<buff.length;bufferIndex=bufferIndex+8)            {

    view[viewIndex] = buff.readDoubleLE(bufferIndex);
    viewIndex++;
}

答案 8 :(得分:0)

此代理将公开缓冲区作为任何TypedArrays,而不进行任何复制。 :

https://www.npmjs.com/package/node-buffer-as-typedarray

它仅适用于LE,但可以轻松移植到BE。 此外,从来没有真正测试过它的效率。

答案 9 :(得分:0)

现在有一个非常有用的npm软件包:buffer https://github.com/feross/buffer

它试图提供与节点的Buffer API 100%相同的API,并允许:

等等。

答案 10 :(得分:-1)

NodeJS,有一点(我认为它是v0.6.x)有ArrayBuffer支持。我为base64编码和解码here创建了一个小型库,但自从更新到v0.7后,测试(在NodeJS上)失败了。我正在考虑创建一些规范化的东西,但在此之前,我认为应该使用Node的原生Buffer

答案 11 :(得分:-6)

我已经将我的节点更新到5.0.0版 我的工作是:

function toArrayBuffer(buffer){
    var array = [];
    var json = buffer.toJSON();
    var list = json.data

    for(var key in list){
        array.push(fixcode(list[key].toString(16)))
    }

    function fixcode(key){
        if(key.length==1){
            return '0'+key.toUpperCase()
        }else{
            return key.toUpperCase()
        }
    }

    return array
}

我用它来查看我的vhd磁盘映像。