如何将 NodeJS 二进制缓冲区转换为 JavaScript ArrayBuffer?
将二进制 NodeJS 缓冲区转换为 JavaScript ArrayBuffer
的实例Buffer
也是Uint8Array
node.js 4.x 及更高版本中的实例。因此,最有效的解决方案是buf.buffer
直接访问该属性,如https://stackoverflow.com/a/31394257/1375574。如果您需要向另一个方向移动,则 Buffer 构造函数也接受一个 ArrayBufferView 参数。
请注意,这不会创建副本,这意味着写入任何 ArrayBufferView 都将写入原始 Buffer 实例。
在旧版本中,node.js 将 ArrayBuffer 作为 v8 的一部分,但 Buffer 类提供了更灵活的 API。为了读取或写入 ArrayBuffer,您只需要创建一个视图并进行复制。
从缓冲区到数组缓冲区:
function toArrayBuffer(buf) {
const ab = new ArrayBuffer(buf.length);
const view = new Uint8Array(ab);
for (let i = 0; i < buf.length; ++i) {
view[i] = buf[i];
}
return ab;
}
从 ArrayBuffer 到 Buffer:
function toBuffer(ab) {
const buf = Buffer.alloc(ab.byteLength);
const view = new Uint8Array(ab);
for (let i = 0; i < buf.length; ++i) {
buf[i] = view[i];
}
return buf;
}
无依赖,最快,Node.js 4.x 及更高版本
Buffer
s 是Uint8Array
s,所以你只需要切片(复制)它的 backing 区域ArrayBuffer
。
// Original Buffer
let b = Buffer.alloc(512);
// Slice (copy) its segment of the underlying ArrayBuffer
let ab = b.buffer.slice(b.byteOffset, b.byteOffset + b.byteLength);
在slice
和偏移的东西是需要,因为小Buffer
秒(小于4kB的默认状态下,一半的池大小)可以是在共享视图ArrayBuffer
。如果不进行切片,您最终会得到一个ArrayBuffer
包含来自另一个Buffer
. 请参阅文档中的说明。
如果您最终需要一个TypedArray
,您可以在不复制数据的情况下创建一个:
// Create a new view of the ArrayBuffer without copying
let ui32 = new Uint32Array(b.buffer, b.byteOffset, b.byteLength / Uint32Array.BYTES_PER_ELEMENT);
无依赖,中等速度,任意版本的 Node.js
使用Martin Thomson 的答案,它在O(n)时间内运行。(另请参阅我对他关于非优化的回答的评论的回复。使用 DataView 很慢。即使您需要翻转字节,也有更快的方法。)
依赖,快,Node.js ≤ 0.12 或 iojs 3.x
您可以使用https://www.npmjs.com/package/memcpy向任一方向前进(从 Buffer 到 ArrayBuffer 再返回)。它比此处发布的其他答案更快,并且是一个编写良好的库。Node 0.12 到 iojs 3.x 需要 ngossen 的 fork(请参阅此)。
“从 ArrayBuffer 到 Buffer”可以这样完成:
var buffer = Buffer.from( new Uint8Array(ab) );
一种更快的书写方式
var arrayBuffer = new Uint8Array(nodeBuffer).buffer;
但是,在具有 1024 个元素的缓冲区上,这似乎比建议的 toArrayBuffer 函数慢 4 倍。
1.一种Buffer
仅仅是一个视图用于寻找到的ArrayBuffer
。
A Buffer
,实际上是一个FastBuffer
,它extends
(继承自)Uint8Array
,它是实际内存的一个八位字节单元视图(“部分访问器”),一个ArrayBuffer
.
/lib/buffer.js#L65-L73
class FastBuffer extends Uint8Array {
constructor(arg1, arg2, arg3) {
super(arg1, arg2, arg3);
}
}
FastBuffer.prototype.constructor = Buffer;
internalBuffer.FastBuffer = FastBuffer;
Buffer.prototype = FastBuffer.prototype;
2. anArrayBuffer
的大小及其视图的大小可能会有所不同。
理由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
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);
}
}
📜 Node.js 9.4.0/lib/buffer.js#L98-L100
function createUnsafeBuffer(size) {
return new FastBuffer(createUnsafeArrayBuffer(size));
}
“内存池”是什么意思?
甲存储器池是一个固定大小的预先分配用于保持小尺寸存储块用于存储块Buffer
秒。使用它可以将小内存块紧密地结合在一起,从而防止因单独管理(分配和释放)小内存块而导致碎片化。
在这种情况下,内存池是ArrayBuffer
s,默认大小为 8 KiB,在Buffer.poolSize
. 当它要为 a 提供一个小内存块时Buffer
,它会检查最后一个内存池是否有足够的可用内存来处理这个;如果是的话,它会创建一个Buffer
该“意见”的内存池的给定的部分块,否则,将创建一个新的内存池等。
您可以访问底层ArrayBuffer
的Buffer
。该Buffer
的buffer
财产(即从继承Uint8Array
)持有它。一个“小” Buffer
的buffer
属性是ArrayBuffer
代表整个内存池。所以在这种情况下,ArrayBuffer
和 的Buffer
大小不同。
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.
3. 所以我们需要提取它“查看”的内存。
An 的ArrayBuffer
大小是固定的,因此我们需要通过制作零件的副本来提取它。要做到这一点,我们使用Buffer
的byteOffset
财产和length
财产,这是从继承Uint8Array
,并且该ArrayBuffer.prototype.slice
方法,这使得一个部分的副本ArrayBuffer
。这里的slice()
-ing 方法受到@ZachB 的启发。
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