So hängen Sie Binärdaten an einen Puffer in node.js an

83

Ich habe einen Puffer mit einigen Binärdaten:

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

und ich möchte anhängen 0x03.

Wie kann ich mehr Binärdaten anhängen? Ich suche in der Dokumentation, aber zum Anhängen von Daten muss es sich um eine Zeichenfolge handeln. Andernfalls tritt ein Fehler auf ( TypeError: Argument muss eine Zeichenfolge sein ):

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

Die einzige Lösung, die ich hier sehen kann, besteht darin, für alle angehängten Binärdaten einen neuen Puffer zu erstellen und ihn mit dem richtigen Versatz in den Hauptpuffer zu kopieren:

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>

Dies scheint jedoch etwas ineffizient zu sein, da ich für jedes Anhängen einen neuen Puffer instanziieren muss.

Kennen Sie einen besseren Weg zum Anhängen von Binärdaten?

BEARBEITEN

Ich habe einen BufferedWriter geschrieben , der mithilfe interner Puffer Bytes in eine Datei schreibt. Wie BufferedReader, jedoch zum Schreiben.

Ein kurzes Beispiel:

//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

LETZTES UPDATE

Verwenden Sie concat .

Gabriel Llamas
quelle
3
Es wäre klarer zu lesen, wenn die Mini-Antworten oben tatsächliche Antworten wären und die Frage hier für sich allein wäre.
Anko

Antworten:

140

Aktualisierte Antwort für Node.js ~> 0.8

Der Knoten kann jetzt Puffer selbstständig verketten .

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

Alte Antwort für Node.js ~ 0.6

Ich benutze ein Modul, um .concatunter anderem eine Funktion hinzuzufügen :

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

Ich weiß, dass es keine "reine" Lösung ist, aber es funktioniert sehr gut für meine Zwecke.

Brad
quelle
Die concatFunktion macht genau das, was ich gepostet habe :(. Sie berechnet die Gesamtlänge und kopiert dann die Daten aller Puffer, wobei der Versatz angepasst wird.
Gabriel Llamas
So muss es funktionieren. Wie @stewe hervorhob, werden Puffer aufgrund der Art und Weise, wie Speicher zugewiesen wird, auf eine feste Größe instanziiert.
Brad
2
In c haben wir jedoch die Realloc-Funktion, um den Speicher bei Bedarf dynamisch zu erweitern. Die node.js sollten dies wissen.
Gabriel Llamas
1
@GabrielLlamas, ich würde empfehlen, einen Patch an ihr Repository zu senden.
Brad
11
Ich habe herausgefunden, warum node.js keine dynamischen Puffer hat: markmail.org/message/vx2h3uslwgludu3y
Gabriel Llamas
10

Puffer haben immer eine feste Größe. Es gibt keine eingebaute Möglichkeit, ihre Größe dynamisch zu ändern. Daher ist Ihr Ansatz, sie in einen größeren Puffer zu kopieren, der einzige Weg.

Um jedoch effizienter zu sein, können Sie den Puffer größer als den ursprünglichen Inhalt machen, sodass er "freien" Speicherplatz enthält, in dem Sie Daten hinzufügen können, ohne den Puffer neu zuzuweisen. Auf diese Weise müssen Sie keinen neuen Puffer erstellen und den Inhalt bei jedem Anhängevorgang kopieren.

Stewe
quelle
8

Dies soll jedem helfen, der hierher kommt und nach einer Lösung sucht, die einen reinen Ansatz wünscht. Ich würde empfehlen, dieses Problem zu verstehen, da es nicht nur mit einem JS-Pufferobjekt an vielen verschiedenen Stellen auftreten kann. Wenn Sie verstehen, warum das Problem besteht und wie Sie es lösen können, verbessern Sie Ihre Fähigkeit, in Zukunft andere Probleme zu lösen, da dieses so grundlegend ist.

Für diejenigen von uns, die sich mit diesen Problemen in anderen Sprachen befassen müssen, ist es ganz natürlich, eine Lösung zu finden, aber es gibt Menschen, die möglicherweise nicht wissen, wie sie die Komplexität abstrahieren und einen allgemein effizienten dynamischen Puffer implementieren können. Der folgende Code kann möglicherweise weiter optimiert werden.

Ich habe die Lesemethode nicht implementiert, um das Beispiel klein zu halten.

Die reallocFunktion in C (oder einer Sprache, die sich mit intrinsischen Zuordnungen befasst) garantiert nicht, dass die Zuordnung vergrößert wird, ohne dass die vorhandenen Daten verschoben werden - obwohl dies manchmal möglich ist. Daher verwenden die meisten Anwendungen, wenn eine unbekannte Datenmenge gespeichert werden muss, eine Methode wie die folgende und werden nicht ständig neu zugewiesen, es sei denn, die Neuzuweisung ist sehr selten. Auf diese Weise handhaben die meisten Dateisysteme das Schreiben von Daten in eine Datei. Das Dateisystem weist einfach einen anderen Knoten zu und hält alle Knoten miteinander verbunden. Wenn Sie daraus lesen, wird die Komplexität abstrahiert, sodass die Datei / der Puffer als ein einziger zusammenhängender Puffer erscheint.

Für diejenigen unter Ihnen, die die Schwierigkeit verstehen möchten, einfach einen dynamischen Hochleistungspuffer bereitzustellen, müssen Sie nur den folgenden Code anzeigen und sich mit den Algorithmen für Speicherhaufen und der Funktionsweise des Speicherhaufens für Programme befassen.

Die meisten Sprachen bieten aus Leistungsgründen einen Puffer mit fester Größe und anschließend eine andere Version mit dynamischer Größe. Einige Sprachsysteme entscheiden sich für ein System von Drittanbietern, bei dem die Kernfunktionalität minimal gehalten wird (Kernverteilung) und Entwickler aufgefordert werden, Bibliotheken zu erstellen, um zusätzliche oder übergeordnete Probleme zu lösen. Aus diesem Grund können Sie sich fragen, warum eine Sprache einige Funktionen nicht bietet. Durch diese kleine Kernfunktionalität können die Kosten für die Wartung und Verbesserung der Sprache gesenkt werden. Am Ende müssen Sie jedoch Ihre eigenen Implementierungen schreiben oder sind von einem Drittanbieter abhängig.

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
quelle
Ich würde bei der Verwendung dieser Lösung äußerste Vorsicht walten lassen. Wenn der Grund für die Größe der Puffer die Leistung ist, verwenden Sie diese Option nicht . Jedes einzelne Byte, das in das Array mit veränderbarer Größe geschrieben wird, verursacht this.buffer_list[lastbuf][this.cur_size + x] = data[x + offset];unnötigerweise eine zusätzliche Hash-Suche, viele zusätzliche Array-Überprüfungen und zwei SMI-Ganzzahlprüfungen mit jedem einzelnen Byte. Wenn Sie Leistung wünschen, empfehle ich Ihnen dringend, diese Antwort nicht zu verwenden. Weisen Sie stattdessen ein neues Array mit der gewünschten Größe zu und kopieren Sie die Daten in das neue Array. Das macht Java und es ist sehr schnell.
Jack Giffin
0

Byte an einer bestimmten Stelle einfügen.

insertToArray(arr,index,item) {
   return Buffer.concat([arr.slice(0,index),Buffer.from(item,"utf-8"),arr.slice(index)]);
}
Havrylov Anton
quelle