Подтвердить что ты не робот

Преобразование двоичного буфера NodeJS в JavaScript ArrayBuffer

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

4b9b3361

Ответ 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.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 памяти FastBuffer.

Он выделяет память двумя различными способами в зависимости от размера.

  • Если размер меньше половины размера пула памяти и не равен 0 ("маленький"): он использует пул памяти для подготовки требуемой памяти.
  • Else: он создает выделенный 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));
}

Что вы подразумеваете под "пулом памяти"?

Пул памяти представляет собой предварительно выделенный блок памяти фиксированного размера для хранения небольших блоков памяти для 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.