Как преобразовать двоичный буфер NodeJS в JavaScript ArrayBuffer?
Преобразование двоичного буфера NodeJS в JavaScript ArrayBuffer
Ответ 1
Экземпляры Buffer
также являются экземплярами Uint8Array
в node.js 4.x и выше. Таким образом, наиболее эффективным решением является доступ к свойству buf.buffer
напрямую, согласно fooobar.com/questions/77260/.... Конструктор буфера также принимает аргумент ArrayBufferView, если вам нужно идти в другом направлении.
Обратите внимание, что это не создаст копию, а это значит, что запись в любой ArrayBufferView будет записываться в исходный экземпляр Buffer.
В более старых версиях node.js имеет как ArrayBuffer как часть v8, но класс Buffer предоставляет более гибкий API. Чтобы читать или писать в ArrayBuffer, вам нужно только создать представление и скопировать его.
От Buffer to 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 до буфера:
function toBuffer(ab) {
var buf = new Buffer(ab.byteLength);
var view = new Uint8Array(ab);
for (var i = 0; i < buf.length; ++i) {
buf[i] = view[i];
}
return buf;
}
Ответ 2
"От ArrayBuffer to Buffer" можно сделать следующим образом:
var buffer = Buffer.from( new Uint8Array(ab) );
Ответ 3
- Нет зависимостей, быстрее, узел 4.x и более поздних версий
Буферы - это 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. Без этого вы можете получить ArrayBuffer, содержащий данные из другого TypedArray.
- Никаких зависимостей, умеренной скорости, любой версии узла
Используйте ответ Мартина Томсона, который работает в O (n) времени. (См. Также мои ответы на комментарии к его ответу о не оптимизации. Использование DataView выполняется медленно. Даже если вам нужно перевернуть байты, есть более быстрые способы сделать это.)
- Зависимость, быстрая, любая версия узла
Вы можете использовать https://www.npmjs.com/package/memcpy для перехода в любом направлении (от буфера до ArrayBuffer и обратно). Это быстрее, чем другие ответы, размещенные здесь, и хорошо написанная библиотека. Узел 0.12 через iojs 3.x требует ngossen fork (см. Это).
Ответ 4
Более быстрый способ записать его
var arrayBuffer = new Uint8Array(nodeBuffer).buffer;
Однако, похоже, это работает примерно в 4 раза медленнее, чем предлагаемая функция toArrayBuffer в буфере с 1024 элементами.
Ответ 5
Используйте следующий отличный пакет npm: to-arraybuffer
.
Или вы можете реализовать его самостоятельно. Если ваш буфер называется buf
, сделайте следующее:
buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength)
Ответ 6
1. Buffer
- это просто взгляд на поиск ArrayBuffer
.
Фактически, Buffer
является FastBuffer
, который extends
(наследует) Uint8Array
, который представляет собой представление октетного блока ("частичный аксессор") фактической памяти, ArrayBuffer
.
/lib/buffer.js#L65-L73
Node.js 9.4.0class 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
памяти FastBuffer
.
Он выделяет память двумя различными способами в зависимости от размера.
- Если размер меньше половины размера пула памяти и не равен 0 ("маленький"): он использует пул памяти для подготовки требуемой памяти.
- Else: он создает выделенный
ArrayBuffer
который точно соответствует требуемой памяти.
/lib/buffer.js#L306-L320
Node.js 9.4.0function 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.0function createUnsafeBuffer(size) {
return new FastBuffer(createUnsafeArrayBuffer(size));
}
Что вы подразумеваете под "пулом памяти"?
Пул памяти представляет собой предварительно выделенный блок памяти фиксированного размера для хранения небольших блоков памяти для Buffer
s. Используя его, плотно сжимаются мелкие куски памяти, поэтому предотвращает фрагментацию, вызванную отдельным управлением (выделение и освобождение) мелких фрагментов памяти.
В этом случае пулами памяти являются ArrayBuffer
, размер по умолчанию 8 KiB, который указан в Buffer.poolSize
. Когда нужно предоставить небольшой блок памяти для 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' 'length' property holds the size, in octets, of the view.
// An 'ArrayBuffer' 'byteLength' property holds the size, in octets, of its data.
console.info(zero_sized_buffer.length); /// 0; the view size.
console.info(zero_sized_buffer.buffer.byteLength); /// 0; the memory.. size.
console.info(Buffer.poolSize); /// 8192; a memory pool size.
console.info(small_buffer.length); /// 3; the view size.
console.info(small_buffer.buffer.byteLength); /// 8192; the memory pool size.
console.info(Buffer.poolSize); /// 8192; a memory pool size.
console.info(big_buffer.length); /// 4096; the view size.
console.info(big_buffer.buffer.byteLength); /// 4096; the memory size.
console.info(Buffer.poolSize); /// 8192; a memory pool size.
3. Поэтому нам нужно извлечь память, которую она "просматривает".
ArrayBuffer
имеет фиксированный размер, поэтому нам нужно извлечь его, сделав копию части. Для этого мы используем byteOffset
свойства 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
, вы можете избежать ненужного копирования памяти.
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
Ответ 7
Вы можете придумать ArrayBuffer
как типизированный Buffer
.
An ArrayBuffer
поэтому всегда нуждается в типе (так называемый "Просмотр буфера массива" ). Обычно Просмотр буфера массива имеет тип Uint8Array
или Uint16Array
.
Существует хорошая статья из Renato Mangini на преобразование между ArrayBuffer и строкой.
Я обобщил основные части в примере кода (для 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!"
Ответ 8
Я попробовал выше для Float64Array, и он просто не работал.
В итоге я понял, что действительно данные должны быть прочитаны "INTO" в виде правильных фрагментов. Это означает чтение 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++;
}
Ответ 9
Этот прокси будет выставлять буфер как любой из TypedArrays без какой-либо копии.:
https://www.npmjs.com/package/node-buffer-as-typedarray
Он работает только на LE, но его можно легко портировать в BE. Кроме того, никогда не было на самом деле проверить, насколько это эффективно.
Ответ 10
NodeJS, в какой-то момент (думаю, он был v0.6.x) поддерживал ArrayBuffer. Я создал небольшую библиотеку для кодирования и декодирования base64 здесь, но с момента обновления до версии v0.7 тесты (на NodeJS) терпят неудачу. Я думаю о создании чего-то, что нормализует это, но до тех пор я полагаю, что Node native Buffer
должен использоваться.
Ответ 11
Я уже обновил свой node до версии 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.