¿Cómo puedo convertir un búfer binario NodeJS en un ArrayBuffer de JavaScript?
fuente
¿Cómo puedo convertir un búfer binario NodeJS en un ArrayBuffer de JavaScript?
Las instancias de Buffer
también son instancias deUint8Array
en node.js 4.xy superior. Por lo tanto, la solución más eficiente es acceder a la buf.buffer
propiedad directamente, según https://stackoverflow.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.
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 = Buffer.alloc(ab.byteLength);
var view = new Uint8Array(ab);
for (var i = 0; i < buf.length; ++i) {
buf[i] = view[i];
}
return buf;
}
size&0xfffffffe
, copie enteros de 32 bits, luego, si queda 1 byte, copie un entero de 8 bits, si tiene 2 bytes, copie un entero de 16 bits, y si tiene 3 bytes, copie un entero de 16 bits y 8 bits.
ab
devuelve? No hay nada hecho con ab
? Siempre consigo {}
como resultado.
slice()
método devuelve un nuevo ArrayBuffer
cuyo contenido es una copia de ArrayBuffer
los bytes de este desde inicio, inclusivo, hasta el final, exclusivo' - MDNArrayBuffer.prototype.slice()
Buffer
s son Uint8Array
s, por lo que solo necesita cortar (copiar) su región del respaldo 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);
El material slice
y el desplazamiento son necesarios porque los pequeños Buffer
s (menos de 4 kB por defecto, la mitad del tamaño del grupo ) pueden ser vistas en un espacio compartido ArrayBuffer
. Sin segmentar, puede terminar con ArrayBuffer
datos que contienen de otro Buffer
. Ver explicación en los documentos .
Si finalmente necesita un TypedArray
, puede crear uno sin copiar los datos:
// Create a new view of the ArrayBuffer without copying
let ui32 = new Uint32Array(b.buffer, b.byteOffset, b.byteLength / Uint32Array.BYTES_PER_ELEMENT);
Use la respuesta de Martin Thomson , que se ejecuta en O (n) . (Consulte también mis respuestas a los comentarios sobre su respuesta sobre las no optimizaciones. El uso de un DataView es lento. Incluso si necesita voltear bytes, hay formas más rápidas de hacerlo).
Puede usar https://www.npmjs.com/package/memcpy para ir en cualquier dirección (Buffer to ArrayBuffer y viceversa). Es más rápido que las otras respuestas publicadas aquí y es una biblioteca bien escrita. Los nodos 0.12 a iojs 3.x requieren la bifurcación de ngossen (vea esto ).
.byteLength
y .byteOffset
documentados?
var ab = b.buffer.slice(b.byteOffset, b.byteOffset + b.byteLength);
me salvó el día
"De ArrayBuffer a Buffer" podría hacerse de esta manera:
var buffer = Buffer.from( new Uint8Array(ab) );
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 toArrayBuffer sugerida en un búfer con 1024 elementos.
Buffer
también son instancias de Uint8Array
Node.js 4.xy superior. Para versiones inferiores de Node.js, debe implementar una toArrayBuffer
función.
Buffer
es solo una vista para mirar un ArrayBuffer
.A Buffer
, de hecho, es un FastBuffer
, que extends
(hereda de) Uint8Array
, que es una vista de unidad de octeto ("acceso parcial") de la memoria real, unArrayBuffer
.
/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;
ArrayBuffer
y el tamaño de su vista pueden variar.Buffer.from(arrayBuffer[, byteOffset[, length]])
.Con Buffer.from(arrayBuffer[, byteOffset[, length]])
, puede crear un Buffer
con especificar su subyacente ArrayBuffer
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.
FastBuffer
asignación de memoria de.Asigna la memoria de dos formas diferentes según el tamaño.
ArrayBuffer
que se ajusta exactamente a la memoria requerida./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));
}
Un grupo de memoria es un bloque de memoria preasignado de tamaño fijo para mantener fragmentos de memoria de tamaño pequeño durante Buffer
s. Su uso mantiene los fragmentos de memoria de pequeño tamaño muy juntos, por lo que evita la fragmentación. causada por la administración separada (asignación y desasignación) de fragmentos de memoria de pequeño tamaño.
En este caso, las agrupaciones de memoria son ArrayBuffer
s cuyo tamaño es de 8 KiB de forma predeterminada, que se especifica en Buffer.poolSize
. Cuando se trata de proporcionar un fragmento de memoria de tamaño pequeño para un Buffer
, comprueba si el último grupo de memoria tiene suficiente memoria disponible para manejar esto; si es así, crea un Buffer
que "ve" el fragmento parcial dado del grupo de memoria, de lo contrario, crea un nuevo grupo de memoria y así sucesivamente.
Puede acceder al subyacente ArrayBuffer
de a Buffer
. El Buffer
's buffer
de propiedad (es decir, heredado de Uint8Array
) la sostiene. Un “pequeño” Buffer
's buffer
establecimiento es el ArrayBuffer
que representa la totalidad del bloque de memoria. Entonces, en este caso, el ArrayBuffer
y el Buffer
varía en 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.
Un ArrayBuffer
tamaño es fijo, por lo que debemos extraerlo haciendo una copia de la pieza. Para hacer esto, utilizamos Buffer
la byteOffset
propiedad y la length
propiedad , que se heredan Uint8Array
, y el ArrayBuffer.prototype.slice
método , que hace una copia de una parte de un ArrayBuffer
. El slice()
método -ing aquí fue inspirado por @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
Si va a utilizar los resultados como de solo lectura, o está bien modificar el Buffer
contenido de la entrada , puede evitar la copia innecesaria de la memoria.
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
obtain_arraybuffer
: buf.buffer.subarray
no parece existir. ¿Quisiste decir buf.buffer.slice
aquí?
ArrayBuffer.prototype.slice
y luego lo modifiqué Uint8Array.prototype.subarray
. Ah, y lo hice mal. Probablemente se confundió un poco en ese entonces. Todo está bien ahora gracias a ti.
Utiliza el siguiente paquete excelente NPM: to-arraybuffer
.
O bien, puede implementarlo usted mismo. Si se llama a su búfer buf
, haga esto:
buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength)
Puedes pensar en un ArrayBuffer
como mecanografiado Buffer
.
Una ArrayBuffer
, por tanto, siempre tiene un tipo (el denominado "Matriz de búfer Ver"). Normalmente, la Vista de búfer de matriz tiene un tipo de Uint8Array
o Uint16Array
.
Hay un buen artículo de Renato Mangini sobre la conversión entre un ArrayBuffer y una Cadena .
He resumido las partes esenciales en un ejemplo de código (para Node.js). También muestra cómo convertir entre los mecanografiados ArrayBuffer
y los no tipificados 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!"
Intenté lo anterior para un Float64Array y simplemente no funcionó.
Terminé dándome cuenta de que realmente los datos debían leerse 'EN' la vista en fragmentos correctos. Esto significa leer 8 bytes a la vez desde el Buffer de origen.
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++;
}
Buffer.read*
métodos también son lentos.
Este proxy expondrá el búfer como cualquiera de los TypedArrays, sin ninguna copia. :
https://www.npmjs.com/package/node-buffer-as-typedarray
Solo funciona en LE, pero se puede portar fácilmente a BE. Además, nunca pude probar qué tan eficiente es esto.
Ahora hay un paquete npm muy útil para esto: buffer
https://github.com/feross/buffer
Intenta proporcionar una API que sea 100% idéntica a la API de búfer del nodo y permita:
y pocos mas.
NodeJS, en un momento (creo que fue v0.6.x) tenía soporte para ArrayBuffer. Creé una pequeña biblioteca para la codificación y decodificación base64 aquí , 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 Buffer
debe usar el nativo de Node .
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.
toArrayBuffer(new Buffer([1,2,3]))
-> ['01', '02', '03']
- esto devuelve una matriz de cadenas, no enteros / bytes.
Array
. Por lo tanto, para almacenar muchos flotadores, necesitaFloat32Array
4 bytes. Y si desea una serialización rápida de esos flotadores en un archivo, necesita unBuffer
, ya que la serialización a JSON lleva años.