web-dev-qa-db-fra.com

Comment ajouter des données binaires à un tampon dans node.js

J'ai un tampon avec des données binaires:

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

et je veux ajouter 0x03.

Comment puis-je ajouter plus de données binaires? Je cherche dans la documentation, mais pour l'ajout de données, il doit s'agir d'une chaîne. Sinon, une erreur se produit ( TypeError: l'argument doit être une chaîne):

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

Ensuite, la seule solution que je peux voir ici est de créer un nouveau tampon pour chaque donnée binaire ajoutée et de le copier dans le tampon principal avec le bon décalage:

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>

Mais cela semble un peu inefficace car je dois instancier un nouveau tampon pour chaque ajout.

Connaissez-vous un meilleur moyen d’ajouter des données binaires?

[~ # ~] éditer [~ # ~]

J'ai écrit un BufferedWriter qui écrit des octets dans un fichier en utilisant des tampons internes. Identique à BufferedReader mais pour l'écriture.

Un exemple rapide:

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

DERNIÈRE MISE À JOUR

Utilisez concat .

63
Gabriel Llamas

Réponse mise à jour pour Node.js ~> 0.8

Le nœud est capable de concaténer des tampons seul.

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

Ancienne réponse pour Node.js ~ 0.6

J'utilise un module pour ajouter un .concat fonction, entre autres:

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

Je sais que ce n'est pas une solution "pure", mais cela fonctionne très bien pour mes besoins.

113
Brad

Les tampons ont toujours une taille fixe, il n’existe pas de méthode intégrée pour les redimensionner de manière dynamique. Vous ne pouvez donc les copier que dans un tampon plus grand.

Toutefois, pour être plus efficace, vous pouvez augmenter la taille de la mémoire tampon par rapport au contenu d'origine afin qu'elle contienne un espace "libre" dans lequel vous pouvez ajouter des données sans réaffecter la mémoire tampon. De cette façon, vous n'avez pas besoin de créer un nouveau tampon et de copier le contenu à chaque opération d'ajout.

8
stewe

Ceci est destiné à aider tous ceux qui viennent ici à la recherche d'une solution qui veut une approche pure. Je recommanderais de comprendre ce problème car cela peut se produire dans beaucoup d'endroits différents, pas seulement avec un objet JS Buffer. En comprenant pourquoi le problème existe et comment le résoudre, vous améliorerez votre capacité à résoudre d’autres problèmes à l’avenir, celui-ci étant si fondamental.

Pour ceux d'entre nous qui doivent traiter ces problèmes dans d'autres langues, il est tout à fait naturel de trouver une solution, mais il y a des personnes qui ne savent peut-être pas comment résoudre les problèmes complexes et mettre en œuvre un tampon dynamique généralement efficace. Le code ci-dessous peut encore être optimisé.

J'ai laissé la méthode de lecture non implémentée pour garder l'exemple de petite taille.

La fonction realloc en C (ou tout langage traitant des allocations intrinsèques) ne garantit pas que la taille de l'allocation sera étendue sans déplacer les données existantes - bien que cela soit parfois possible. Par conséquent, la plupart des applications qui ont besoin de stocker une quantité inconnue de données utilisent une méthode comme celle décrite ci-dessous et ne réallouent pas en permanence, sauf si la réaffectation est très peu fréquente. C’est essentiellement ainsi que la plupart des systèmes de fichiers gèrent l’écriture de données dans un fichier. Le système de fichiers alloue simplement un autre nœud et garde tous les nœuds liés entre eux. Lorsque vous en lisez un extrait, la complexité est résumée, de sorte que le fichier/tampon semble être un seul tampon contigu.

Pour ceux d'entre vous qui souhaitent comprendre la difficulté de simplement fournir un tampon dynamique hautes performances, il vous suffit de visualiser le code ci-dessous et de faire des recherches sur les algorithmes de mémoire mémoire et sur le fonctionnement de la mémoire mémoire pour les programmes.

La plupart des langues fournissent un tampon de taille fixe pour des raisons de performances, puis une autre version dynamique. Certains systèmes linguistiques optent pour un système tiers dans lequel ils minimisent les fonctionnalités principales (distribution centrale) et encouragent les développeurs à créer des bibliothèques pour résoudre des problèmes supplémentaires ou de niveau supérieur. C'est pourquoi vous pouvez vous demander pourquoi une langue ne fournit pas certaines fonctionnalités. Cette petite fonctionnalité centrale permet de réduire les coûts liés à la maintenance et à l'amélioration du langage. Cependant, vous finissez par écrire vos propres implémentations ou par une tierce partie.

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;
};
6
kmcguire