new - Convierta un búfer NodeJS binario en ArrayBuffer de JavaScript
nodejs base64 encode (11)
¿Cómo puedo convertir un búfer binario NodeJS en un ArrayBuffer de JavaScript?
1. Un Buffer
es solo una vista para mirar en un ArrayBuffer
.
Un Buffer
, de hecho, es un FastBuffer
, que se extends
(hereda de) Uint8Array
, que es una vista de unidad de octetos ("acceso parcial") de la memoria real, un 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. El tamaño de un ArrayBuffer
y el tamaño de su vista pueden variar.
Motivo n. ° 1: Buffer.from(arrayBuffer[, byteOffset[, length]])
.
Con Buffer.from(arrayBuffer[, byteOffset[, length]])
, puede crear un Buffer
especificando su ArrayBuffer
subyacente y la posición y el tamaño de la vista.
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.
Razón # 2: asignación de memoria de FastBuffer
.
Asigna la memoria de dos formas diferentes según el tamaño.
- Si el tamaño es menor que la mitad del tamaño de un grupo de memoria y no es 0 ("pequeño") : utiliza un grupo de memoria para preparar la memoria requerida.
- De lo contrario : crea un
ArrayBuffer
dedicado que se ajusta exactamente a la memoria requerida.
/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);
}
}
L /lib/buffer.js#L98-L100
Node.js 9.4.0
function createUnsafeBuffer(size) {
return new FastBuffer(createUnsafeArrayBuffer(size));
}
¿Qué quiere decir con un " grupo de memoria "?
Un grupo de memoria es un bloque de memoria preasignado de tamaño fijo para mantener trozos de memoria de tamaño pequeño para los Buffer
. Su uso mantiene los trozos de memoria de pequeño tamaño bien juntos, por lo que evita la fragmentation causada por la administración por separado (asignación y desasignación) de trozos de memoria de pequeño tamaño.
En este caso, los grupos de memoria son ArrayBuffer
s cuyo tamaño es 8 KiB por defecto, que se especifica en Buffer.poolSize
. Cuando se trata de proporcionar un fragmento de memoria de pequeño tamaño para un Buffer
, verifica si el último grupo de memoria tiene suficiente memoria disponible para manejar esto; si es así, crea un Buffer
que "visualiza" el fragmento parcial dado del grupo de memoria, de lo contrario, crea un nuevo grupo de memoria, etc.
Puede acceder al ArrayBuffer
subyacente de un Buffer
. La propiedad de buffer
del buffer
(es decir, heredada de Uint8Array
) lo tiene. Una propiedad de buffer
"pequeña" del Buffer
es un ArrayBuffer
que representa todo el grupo de memoria. Entonces, en este caso, el ArrayBuffer
y el Buffer
varían de tamaño.
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. Entonces necesitamos extraer la memoria que "ve" .
Un ArrayBuffer
se fija en tamaño, por lo que debemos extraerlo haciendo una copia de la pieza. Para hacer esto, utilizamos la developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… Buffer
y la developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… length
, que se heredan de Uint8Array
, y el método ArrayBuffer.prototype.slice
, que hace una copia de una parte de un ArrayBuffer
. El método de @ZachB slice()
está inspirado en @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. Mejora del rendimiento
Si va a utilizar los resultados como de solo lectura, o está bien modificar los contenidos del Buffer
entrada , puede evitar copias de memoria innecesarias.
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.buffer.subarray(buf.byteOffset, buf.byteOffset + 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
"De ArrayBuffer a Buffer" podría hacerse de esta manera:
var buffer = Buffer.from( new Uint8Array(ab) );
Este Proxy expondrá el buffer como cualquiera de las TypedArrays, sin ninguna copia. :
https://www.npmjs.com/package/node-buffer-as-typedarray
Solo funciona en LE, pero puede ser fácilmente portado a BE. Además, nunca llegó a probar realmente cuán eficiente es esto.
Intenté lo anterior para un Float64Array y simplemente no funcionó.
Terminé dándome cuenta de que realmente los datos debían leerse ''INTO'' en la vista en los fragmentos correctos. Esto significa leer 8 bytes a la vez del Buffer fuente.
De todos modos, esto es con lo que terminé ...
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++;
}
NodeJS, en un punto (creo que era v0.6.x), tenía compatibilidad con ArrayBuffer. Creé una pequeña biblioteca para codificación y decodificación base64 here , pero desde la actualización a v0.7, las pruebas (en NodeJS) fallan. Estoy pensando en crear algo que normalice esto, pero hasta entonces, supongo que se debe usar el Buffer
nativo de Node.
Puedes pensar en un ArrayBuffer
como un Buffer
tipeado.
ArrayBuffer
tanto, un ArrayBuffer
siempre necesita un tipo (la llamada "Vista de Buffer de Array"). Por lo general, la vista Buffer Buffer tiene un tipo de Uint8Array
o Uint16Array
.
Hay un buen artículo de Renato Mangini sobre la conversión entre un ArrayBuffer y un String .
He resumido las partes esenciales en un ejemplo de código (para Node.js). También muestra cómo convertir entre el ArrayBuffer
tipeado y el Buffer
tipo.
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!"
Una forma más rápida de escribirlo
var arrayBuffer = new Uint8Array(nodeBuffer).buffer;
Sin embargo, esto parece ejecutarse aproximadamente 4 veces más lento que la función sugerida para ArrayBuffer en un búfer con 1024 elementos.
Use el siguiente paquete excelente npm: to-arraybuffer
.
O bien, puede implementarlo usted mismo. Si su memoria intermedia se llama buf
, haga esto:
buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength)
Ya actualicé mi nodo a la Versión 5.0.0 Y trabajo con esto:
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
}
Lo uso para verificar la imagen de mi disco vhd.
Las instancias de Buffer
también son instancias de Uint8Array
en node.js 4.xy superiores. Por lo tanto, la solución más eficiente es acceder a la propiedad buf.buffer
directamente, según https://.com/a/31394257/1375574 . El constructor Buffer también toma un argumento ArrayBufferView si necesita ir en la otra dirección.
Tenga en cuenta que esto no creará una copia, lo que significa que las escrituras en cualquier ArrayBufferView se escribirán en la instancia original de Buffer.
En versiones anteriores, node.js tiene ArrayBuffer como parte de v8, pero la clase Buffer proporciona una API más flexible. Para leer o escribir en un ArrayBuffer, solo necesita crear una vista y copiarla.De Buffer a 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;
}
De ArrayBuffer a Buffer:
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;
}
- Sin dependencias, más rápido, nodo 4.xy posterior
Los búferes son Uint8Arrays, por lo que solo necesita acceder a su ArrayBuffer. Esto es 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);
La slice
y el desplazamiento son necesarios porque los pequeños búferes (<4096 bytes, creo) son vistas en un ArrayBuffer compartido. Sin él, podrías terminar con un ArrayBuffer que contiene datos de otro TypedArray.
- Sin dependencias, velocidad moderada, ninguna versión de nodo
Use la respuesta de Martin Thomson , que se ejecuta en O (n) tiempo. (Consulte también mis respuestas a los comentarios sobre su respuesta sobre las no optimizaciones. Usar DataView es lento. Incluso si necesita voltear los bytes, existen formas más rápidas de hacerlo).
- Dependencia, rápido, cualquier versión de nodo
Puede usar https://www.npmjs.com/package/memcpy para ir en cualquier dirección (Buffer a ArrayBuffer y viceversa). Es más rápido que las otras respuestas publicadas aquí y es una biblioteca bien escrita. El nodo 0.12 a iojs 3.x requiere la horquilla de ngossen (ver this ).