Jak przekonwertować bufor binarny NodeJS na JavaScript ArrayBuffer?
javascript
node.js
binary
buffer
arraybuffer
Drake Amara
źródło
źródło
Array
. Tak więc, aby zapisać wiele zmiennych, potrzebujeszFloat32Array
4 bajty. A jeśli chcesz szybko serializować te elementy zmiennoprzecinkowe do pliku, potrzebujesz plikuBuffer
, ponieważ serializacja do formatu JSON zajmuje wieki.Odpowiedzi:
Instancje
Buffer
są również instancjamiUint8Array
w node.js 4.xi nowszych. Dlatego najbardziej wydajnym rozwiązaniem jestbuf.buffer
bezpośredni dostęp do nieruchomości, zgodnie z https://stackoverflow.com/a/31394257/1375574 . Konstruktor Buffer przyjmuje również argument ArrayBufferView, jeśli chcesz pójść w innym kierunku.Zauważ, że nie utworzy to kopii, co oznacza, że zapis do dowolnego ArrayBufferView będzie zapisywać do oryginalnej instancji Buffer.
W starszych wersjach node.js zawiera zarówno ArrayBuffer jako część v8, ale klasa Buffer zapewnia bardziej elastyczne API. Aby czytać lub zapisywać w ArrayBuffer, wystarczy utworzyć widok i skopiować.
Od bufora do 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; }
Od ArrayBuffer do 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; }
źródło
size&0xfffffffe
skopiowania 32-bitowych liczb całkowitych, a następnie, jeśli pozostał 1 bajt, skopiuj 8-bitową liczbę całkowitą, jeśli 2 bajty, skopiuj 16-bitową liczbę całkowitą, a jeśli 3 bajty, skopiuj 16-bitową i 8-bitową liczbę całkowitą.ab
wraca? Nic się nie stałoab
? W{}
rezultacie zawsze otrzymuję .slice()
Metoda zwraca nowy,ArrayBuffer
którego zawartość jest kopią tegoArrayBuffer
bajtów od początku włącznie, do końca, z wyłączeniem”. - MDNArrayBuffer.prototype.slice()
Bez zależności, najszybszy, Node.js 4.xi nowszy
Buffer
s toUint8Array
s, więc wystarczy wyciąć (skopiować) jego region podkładuArrayBuffer
.// 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);
Elementy
slice
i offset są wymagane, ponieważ małe plikiBuffer
(domyślnie mniejsze niż 4 kB, połowa rozmiaru puli ) mogą być widokami na udostępnionymArrayBuffer
. Bez dzielenia na plasterki możesz otrzymaćArrayBuffer
dane z innegoBuffer
. Zobacz wyjaśnienie w dokumentacji .Jeśli ostatecznie potrzebujesz pliku
TypedArray
, możesz go utworzyć bez kopiowania danych:// Create a new view of the ArrayBuffer without copying let ui32 = new Uint32Array(b.buffer, b.byteOffset, b.byteLength / Uint32Array.BYTES_PER_ELEMENT);
Brak zależności, umiarkowana prędkość, dowolna wersja Node.js.
Użyj odpowiedzi Martina Thomsona , która działa w czasie O (n) . (Zobacz także moje odpowiedzi na komentarze dotyczące jego odpowiedzi na temat braku optymalizacji. Korzystanie z DataView jest powolne. Nawet jeśli musisz przerzucić bajty, są na to szybsze sposoby).
Zależność, szybki, Node.js ≤ 0,12 lub iojs 3.x
Możesz użyć https://www.npmjs.com/package/memcpy, aby przejść w dowolnym kierunku (Buffer to ArrayBuffer iz powrotem). Jest szybszy niż inne zamieszczone tutaj odpowiedzi i jest dobrze napisaną biblioteką. Węzły od 0.12 do iojs 3.x wymagają rozwidlenia ngossen (zobacz to ).
źródło
.byteLength
i.byteOffset
udokumentowane?var ab = b.buffer.slice(b.byteOffset, b.byteOffset + b.byteLength);
uratował mi dzień„From ArrayBuffer to Buffer” można zrobić w ten sposób:
var buffer = Buffer.from( new Uint8Array(ab) );
źródło
Szybszy sposób na napisanie tego
var arrayBuffer = new Uint8Array(nodeBuffer).buffer;
Wydaje się jednak, że działa to około 4 razy wolniej niż sugerowana funkcja toArrayBuffer na buforze z 1024 elementami.
źródło
Buffer
są również instancjamiUint8Array
w Node.js 4.xi nowszych. W przypadku niższych wersji Node.js musisz zaimplementowaćtoArrayBuffer
funkcję.1. A
Buffer
to tylko widok do spojrzenia na plikArrayBuffer
.A
📜 Node.js 9.4.0Buffer
w rzeczywistości jest aFastBuffer
, któryextends
(dziedziczy po)Uint8Array
, który jest widokiem jednostki oktetu („częściowy akcesor”) rzeczywistej pamięci, anArrayBuffer
./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;
2. Rozmiar
ArrayBuffer
i rozmiar widoku mogą się różnić.Powód # 1:
Buffer.from(arrayBuffer[, byteOffset[, length]])
.Za pomocą
Buffer.from(arrayBuffer[, byteOffset[, length]])
można utworzyćBuffer
z określeniem jego podstawyArrayBuffer
oraz położenia i rozmiaru widoku.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.
Powód 2:
FastBuffer
alokacja pamięci.Alokuje pamięć na dwa różne sposoby w zależności od rozmiaru.
ArrayBuffer
która dokładnie pasuje do wymaganej pamięci./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
function createUnsafeBuffer(size) { return new FastBuffer(createUnsafeArrayBuffer(size)); }
Co masz na myśli mówiąc „ pula pamięci ”?
Puli pamięci jest stałym rozmiarze wstępnie przyznane blok pamięci do przechowywania małych rozmiarów kawałki pamięci dla
Buffer
s. Użycie go utrzymuje małe fragmenty pamięci ściśle ze sobą, dzięki czemu zapobiega fragmentacji spowodowanej oddzielnym zarządzaniem (alokacją i zwalnianiem) małych fragmentów pamięci.W tym przypadku pule pamięci to
ArrayBuffer
s, których rozmiar domyślnie wynosi 8 KB, który jest określony wBuffer.poolSize
. Kiedy ma zapewnić niewielki fragment pamięci dla aBuffer
, sprawdza, czy ostatnia pula pamięci ma wystarczającą ilość dostępnej pamięci, aby to obsłużyć; jeśli tak, to tworzyBuffer
, że „poglądy” dana częściowy klocek puli pamięci, w przeciwnym razie tworzy nową pulę pamięci i tak dalej.Możesz uzyskać dostęp do podstawy
ArrayBuffer
plikuBuffer
. WBuffer
„sbuffer
nieruchomość (czyli odziedziczoneUint8Array
) trzyma go. „Mały” jest nieruchomość jest że reprezentuje całą pulę pamięci. Więc w tym przypadku, i różnią się rozmiarem.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`'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. Więc musimy wyodrębnić pamięci „ widoki ”.
ArrayBuffer
Jest ustalona w wielkości, więc trzeba wyodrębnić ją poprzez kopię strony. Aby to zrobić, używamyBuffer
„sbyteOffset
własności ilength
właściwości , które są dziedziczoneUint8Array
, a tenArrayBuffer.prototype.slice
sposób , co sprawia, kopię część kontroliArrayBuffer
. Przedstawionaslice()
tutaj metoda -ing została zainspirowana @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. Poprawa wydajności
Jeśli jesteś wykorzystanie wyników jako tylko do odczytu, czy to jest w porządku, aby zmodyfikować wprowadzić
Buffer
S'zawartość , można uniknąć niepotrzebnego kopiowania pamięci.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
źródło
obtain_arraybuffer
:buf.buffer.subarray
nie wydaje się istnieć. Miałeś na myślibuf.buffer.slice
tutaj?ArrayBuffer.prototype.slice
a później zmodyfikowałem naUint8Array.prototype.subarray
. Aha, i zrobiłem to źle. Prawdopodobnie był wtedy trochę zdezorientowany. Dzięki tobie wszystko jest w porządku.Użyj następującego doskonały pakiet npm:
to-arraybuffer
.Lub możesz to zaimplementować samodzielnie. Jeśli wywoływany jest twój bufor
buf
, zrób to:źródło
Możesz myśleć o
ArrayBuffer
typieBuffer
.ArrayBuffer
Dlatego zawsze potrzebuje typ (tak zwane „Array Buffer View”). Zazwyczaj widok bufora tablicy ma typUint8Array
lubUint16Array
.Renato Mangini ma dobry artykuł na temat konwersji między ArrayBuffer a String .
Podsumowałem najważniejsze części w przykładzie kodu (dla Node.js). Pokazuje również, jak konwertować między typem
ArrayBuffer
a nietypowymBuffer
.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!"
źródło
Wypróbowałem powyższe dla Float64Array i po prostu nie działało.
Skończyło się na tym, że zdałem sobie sprawę, że naprawdę dane muszą być odczytywane „DO” widoku w odpowiednich fragmentach. Oznacza to jednoczesne odczytywanie 8 bajtów z bufora źródłowego.
W każdym razie na tym skończyłem ...
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++; }
źródło
Buffer.read*
metody są również powolne.Ten serwer proxy ujawni bufor jako dowolną z TypedArrays, bez żadnej kopii. :
https://www.npmjs.com/package/node-buffer-as-typedarray
Działa tylko na LE, ale można go łatwo przenieść do BE. Ponadto nigdy nie udało mi się przetestować, jak wydajne jest to.
źródło
Teraz jest do tego bardzo przydatny pakiet npm:
buffer
https://github.com/feross/bufferPróbuje zapewnić interfejs API, który jest w 100% identyczny z interfejsem Buffer API węzła i umożliwia:
i kilka innych.
źródło
NodeJS w pewnym momencie (myślę, że to była wersja 0.6.x) miał obsługę ArrayBuffer. Stworzyłem małą bibliotekę dla base64 kodowania i dekodowania tutaj , ale od aktualizacji do v0.7, testy (na NodeJS) niepowodzeniem. Myślę o stworzeniu czegoś, co normalizowałoby to, ale przypuszczam, że do tego czasu
Buffer
powinien być używany natywny Node .źródło
Zaktualizowałem już mój węzeł do wersji 5.0.0 I pracuję z tym:
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 }
Używam go do sprawdzenia obrazu dysku vhd.
źródło
toArrayBuffer(new Buffer([1,2,3]))
->['01', '02', '03']
- to zwraca tablicę ciągów, a nie liczb całkowitych / bajtów.