Преобразовать двоичный буфер NodeJS в JavaScript ArrayBuffer - PullRequest
107 голосов
/ 23 декабря 2011

Как я могу преобразовать двоичный буфер NodeJS в ArrayBuffer JavaScript?

Ответы [ 11 ]

105 голосов
/ 24 августа 2012

Экземпляры 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, вам нужно только создать представление и скопировать его.

От буфера до 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 = Buffer.alloc(ab.byteLength);
    var view = new Uint8Array(ab);
    for (var i = 0; i < buf.length; ++i) {
        buf[i] = view[i];
    }
    return buf;
}
51 голосов
/ 14 июля 2015
  • Без зависимостей, самый быстрый, узел 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. </p>

  • Нет зависимостей, средняя скорость, любая версия узла

Используйте Ответ Мартина Томсона , который выполняется за O (n) времени. (См. Также мои ответы на комментарии к его ответу о неоптимизации. Использование DataView идет медленно. Даже если вам нужно перевернуть байты, есть более быстрые способы сделать это.)

  • Зависимость, быстрая, любая версия узла

Вы можете использовать https://www.npmjs.com/package/memcpy, чтобы идти в любом направлении (от буфера до ArrayBuffer и обратно). Это быстрее, чем другие ответы, опубликованные здесь, и это хорошо написанная библиотека. Узлам от 0.12 до iojs 3.x требуется вилка ngossen (см. this ).

45 голосов
/ 12 июня 2013

«От ArrayBuffer до Buffer» можно сделать следующим образом:

var buffer = Buffer.from( new Uint8Array(ab) );
25 голосов
/ 23 октября 2013

Более быстрый способ записи

var arrayBuffer = new Uint8Array(nodeBuffer).buffer;

Однако, похоже, что он работает примерно в 4 раза медленнее, чем предлагаемая функция toArrayBuffer в буфере с 1024 элементами.

12 голосов
/ 04 марта 2014

Используйте следующий превосходный пакет npm: to-arraybuffer.

Или вы можете реализовать это самостоятельно. Если ваш буфер называется buf, сделайте это:

buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength)
9 голосов
/ 24 января 2018

1.A Buffer - это просто представление для просмотра ArrayBuffer.

A Buffer, на самом деле это FastBuffer, которое extends (наследует от)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));
}

Что вы подразумеваете под « пул памяти *»1085 *? ”

A пул памяти - фиксированный размер предварительно выделенный meБлок памяти для хранения небольших блоков памяти в течение Buffer с.Его использование тесно связывает блоки памяти небольшого размера, предотвращая фрагментацию , вызванную раздельным управлением (выделением и освобождением) блоков памяти малого размера.

Inв этом случае пулы памяти имеют размер ArrayBuffer с, размер которого по умолчанию составляет 8 КиБ, что указано в Buffer.poolSize.Когда он предназначен для предоставления блока памяти небольшого размера для Buffer, он проверяет, достаточно ли у последнего пула памяти доступной памяти для обработки этого;если это так, он создает Buffer, который «просматривает» данный частичный фрагмент пула памяти, в противном случае он создает новый пул памяти и т. д.


Youможет получить доступ к базовому ArrayBuffer из Buffer.Свойство Buffer buffer (то есть наследуемое от Uint8Array) содержит его. A «small» Buffer s 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.Поэтому нам нужно извлечь память, которую он « views

. 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
1 голос
/ 30 августа 2017

Вы можете думать о ArrayBuffer как о напечатанном Buffer.

Поэтому ArrayBuffer всегда нужен тип (так называемый «просмотр массива»). Как правило, Просмотр массива имеет тип Uint8Array или Uint16Array.

Есть хорошая статья Ренато Манджини о преобразовании между ArrayBuffer и 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!"
0 голосов
/ 20 февраля 2017

Этот прокси будет представлять буфер как любой из TypedArrays, без какой-либо копии.:

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

Работает только на LE, но легко переносится на BE.Кроме того, никогда не проверял, насколько это эффективно.

0 голосов
/ 09 ноября 2015

Я уже обновил свой узел до версии 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-диска.

0 голосов
/ 06 июля 2015

Я попробовал выше для 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++;
}
...