Jak dołączyć dane binarne do bufora w node.js.

84

Mam bufor z danymi binarnymi:

var b = new Buffer ([0x00, 0x01, 0x02]);

i chcę dołączyć 0x03.

Jak mogę dołączyć więcej danych binarnych? Szukam w dokumentacji, ale aby dołączyć dane, musi to być ciąg, jeśli nie, pojawia się błąd ( TypeError: Argument musi być stringiem ):

var b = new Buffer (256);
b.write ("hola");
console.log (b.toString ("utf8", 0, 4)); //hola
b.write (", adios", 4);
console.log (b.toString ("utf8", 0, 11)); //hola, adios

Wtedy jedynym rozwiązaniem, które widzę tutaj, jest utworzenie nowego bufora dla wszystkich dołączonych danych binarnych i skopiowanie go do głównego bufora z odpowiednim przesunięciem:

var b = new Buffer (4); //4 for having a nice printed buffer, but the size will be 16KB
new Buffer ([0x00, 0x01, 0x02]).copy (b);
console.log (b); //<Buffer 00 01 02 00>
new Buffer ([0x03]).copy (b, 3);
console.log (b); //<Buffer 00 01 02 03>

Ale wydaje się to trochę nieefektywne, ponieważ muszę utworzyć wystąpienie nowego buforu dla każdego dodawania.

Czy znasz lepszy sposób na dołączanie danych binarnych?

EDYTOWAĆ

Napisałem BufferedWriter, który zapisuje bajty do pliku przy użyciu wewnętrznych buforów. To samo co BufferedReader, ale do pisania.

Szybki przykład:

//The BufferedWriter truncates the file because append == false
new BufferedWriter ("file")
    .on ("error", function (error){
        console.log (error);
    })

    //From the beginning of the file:
    .write ([0x00, 0x01, 0x02], 0, 3) //Writes 0x00, 0x01, 0x02
    .write (new Buffer ([0x03, 0x04]), 1, 1) //Writes 0x04
    .write (0x05) //Writes 0x05
    .close (); //Closes the writer. A flush is implicitly done.

//The BufferedWriter appends content to the end of the file because append == true
new BufferedWriter ("file", true)
    .on ("error", function (error){
        console.log (error);
    })

    //From the end of the file:
    .write (0xFF) //Writes 0xFF
    .close (); //Closes the writer. A flush is implicitly done.

//The file contains: 0x00, 0x01, 0x02, 0x04, 0x05, 0xFF

OSTATNIA AKTUALIZACJA

Użyj concat .

Gabriel Llamas
źródło
3
Byłoby bardziej zrozumiałe, gdyby mini-odpowiedzi na górze były rzeczywistymi odpowiedziami, a pytanie było tutaj samo.
Anko

Odpowiedzi:

140

Zaktualizowana odpowiedź dla Node.js ~> 0.8

Węzeł może teraz samodzielnie łączyć bufory .

var newBuffer = Buffer.concat([buffer1, buffer2]);

Stara odpowiedź dla Node.js ~ 0.6

Korzystam z modułu do dodania .concatfunkcji m.in .:

https://github.com/coolaj86/node-bufferjs

Wiem, że nie jest to „czyste” rozwiązanie, ale do moich celów sprawdza się bardzo dobrze.

Ćwiek
źródło
concatFunkcja robi dokładnie to, co ja wysłana :( oblicza de łącznej długości a następnie kopiuje dane z wszystkich buforów Regulacja offsetu..
Gabriel lamy
Tak to ma działać. Jak zauważył @stewe, instancje buforów mają stały rozmiar, ze względu na sposób przydzielania pamięci.
Brad
2
Ale w c mamy funkcję realoc, która dynamicznie rozszerza pamięć, gdy jest to konieczne. Node.js powinien to wiedzieć.
Gabriel Llamas
1
@GabrielLlamas, polecam przesłanie łatki do ich repozytorium.
Brad
11
Dowiedziałem się, dlaczego node.js nie ma dynamicznych buforów: markmail.org/message/vx2h3uslwgludu3y
Gabriel Llamas
10

Bufory mają zawsze stały rozmiar, nie ma możliwości dynamicznej zmiany ich rozmiaru, więc jedynym sposobem jest skopiowanie ich do większego bufora.

Jednak, aby być bardziej wydajnym, można zwiększyć rozmiar bufora niż oryginalna zawartość, aby zawierał trochę „wolnego” miejsca, do którego można dodawać dane bez ponownego przydzielania bufora. W ten sposób nie musisz tworzyć nowego bufora i kopiować zawartości przy każdej operacji dołączania.

gulasz
źródło
8

Ma to pomóc każdemu, kto przyjeżdża tutaj i szuka rozwiązania, które pragnie czystego podejścia. Polecam zrozumienie tego problemu, ponieważ może się on zdarzyć w wielu różnych miejscach, nie tylko w przypadku obiektu JS Buffer. Dzięki zrozumieniu, dlaczego problem istnieje i jak go rozwiązać, poprawisz swoją zdolność rozwiązywania innych problemów w przyszłości, ponieważ ten jest tak fundamentalny.

Dla tych z nas, którzy mają do czynienia z tymi problemami w innych językach, znalezienie rozwiązania jest całkiem naturalne, ale są ludzie, którzy mogą nie zdawać sobie sprawy, jak odciąć się od złożoności i wdrożyć ogólnie skuteczny bufor dynamiczny. Poniższy kod może być dalej optymalizowany.

Pozostawiłem metodę odczytu niezaimplementowaną, aby przykład był niewielki.

reallocFunkcja w C (lub dowolny język czynienia z nieodłącznych alokacji) nie gwarantuje, że alokacja zostanie poszerzona w rozmiarze z zewnątrz przeniesienie istniejących danych - choć czasem jest to możliwe. Dlatego większość aplikacji, które muszą przechowywać nieznaną ilość danych, będzie korzystać z metody podobnej do poniższej i nie będzie dokonywać ich ciągłej ponownej alokacji, chyba że ponowna alokacja jest bardzo rzadka. Zasadniczo jest to sposób, w jaki większość systemów plików obsługuje zapisywanie danych do pliku. System plików po prostu przydziela inny węzeł i utrzymuje wszystkie węzły połączone razem, a kiedy czytasz z niego, złożoność jest wyabstrahowana, tak że plik / bufor wydaje się być pojedynczym ciągłym buforem.

Dla tych z Was, którzy chcą zrozumieć trudność związaną z zapewnieniem po prostu wysokowydajnego bufora dynamicznego, wystarczy przejrzeć poniższy kod, a także przeprowadzić badania nad algorytmami stosu pamięci i sposobem działania sterty pamięci dla programów.

Większość języków zapewnia bufor o stałym rozmiarze ze względu na wydajność, a następnie udostępnia inną wersję o dynamicznym rozmiarze. Niektóre systemy językowe wybierają system innej firmy, w którym podstawowe funkcje są minimalne (dystrybucja podstawowa) i zachęcają programistów do tworzenia bibliotek w celu rozwiązywania problemów dodatkowych lub wyższego poziomu. Dlatego możesz zapytać, dlaczego język nie zapewnia pewnych funkcji. Ta niewielka podstawowa funkcjonalność pozwala obniżyć koszty utrzymania i ulepszania języka, jednak ostatecznie musisz napisać własne implementacje lub polegać na stronie trzeciej.

var Buffer_A1 = function (chunk_size) {
    this.buffer_list = [];
    this.total_size = 0;
    this.cur_size = 0;
    this.cur_buffer = [];
    this.chunk_size = chunk_size || 4096;

    this.buffer_list.push(new Buffer(this.chunk_size));
};

Buffer_A1.prototype.writeByteArrayLimited = function (data, offset, length) {
    var can_write = length > (this.chunk_size - this.cur_size) ? (this.chunk_size - this.cur_size) : length;

    var lastbuf = this.buffer_list.length - 1;

    for (var x = 0; x < can_write; ++x) {
        this.buffer_list[lastbuf][this.cur_size + x] = data[x + offset];
    }

    this.cur_size += can_write;
    this.total_size += can_write;

    if (this.cur_size == this.chunk_size) {
        this.buffer_list.push(new Buffer(this.chunk_size));
        this.cur_size = 0;
    }

    return can_write;
};

/*
    The `data` parameter can be anything that is array like. It just must
    support indexing and a length and produce an acceptable value to be
    used with Buffer.
*/
Buffer_A1.prototype.writeByteArray = function (data, offset, length) {
    offset = offset == undefined ? 0 : offset;
    length = length == undefined ? data.length : length;

    var rem = length;
    while (rem > 0) {
        rem -= this.writeByteArrayLimited(data, length - rem, rem);
    }
};

Buffer_A1.prototype.readByteArray = function (data, offset, length) {
    /*
        If you really wanted to implement some read functionality
        then you would have to deal with unaligned reads which could
        span two buffers.
    */
};

Buffer_A1.prototype.getSingleBuffer = function () {
    var obuf = new Buffer(this.total_size);
    var cur_off = 0;
    var x;

    for (x = 0; x < this.buffer_list.length - 1; ++x) {
        this.buffer_list[x].copy(obuf, cur_off);
        cur_off += this.buffer_list[x].length;
    }

    this.buffer_list[x].copy(obuf, cur_off, 0, this.cur_size);

    return obuf;
};
kmcguire
źródło
Radziłbym zachować szczególną ostrożność podczas korzystania z tego rozwiązania. Jeśli powodem, dla którego chcesz zmienić rozmiar buforów, jest wydajność, nie używaj tego . Każdy bajt zapisany w tablicy o zmiennym rozmiarze powoduje powstanie this.buffer_list[lastbuf][this.cur_size + x] = data[x + offset];, co niepotrzebnie wprowadza dodatkowe wyszukiwanie skrótu, wiele dodatkowych sprawdzeń tablicy i dwa sprawdzenia liczb całkowitych SMI z każdym bajtem. Jeśli oczekujesz wydajności, gorąco zachęcam, abyś nie korzystał z tej odpowiedzi. Zamiast tego przydziel nową tablicę o żądanym rozmiarze i skopiuj dane do nowej tablicy. To właśnie robi Java i jest naprawdę szybki.
Jack Giffin
0

wstaw bajt w określone miejsce.

insertToArray(arr,index,item) {
   return Buffer.concat([arr.slice(0,index),Buffer.from(item,"utf-8"),arr.slice(index)]);
}
Havrylov Anton
źródło