Подтвердить что ты не робот

Как добавить двоичные данные в буфер в node.js

ПОСЛЕДНЕЕ ОБНОВЛЕНИЕ

Используйте concat.

ИЗМЕНИТЬ

Я написал BufferedWriter, который записывает байты в файл с использованием внутренних буферов. То же, что BufferedReader, но для записи.

Быстрый пример:

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

ОРИГИНАЛЬНЫЙ ВОПРОС

У меня есть буфер с некоторыми двоичными данными:

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

и я хочу добавить 0x03.

Как добавить дополнительные двоичные данные? Я ищу в документации, но для добавления данных она должна быть строкой, если нет, возникает ошибка (TypeError: Аргумент должен быть строкой):

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

Тогда единственное решение, которое я вижу здесь, - создать новый буфер для каждого добавленного двоичного файла и скопировать его в основной буфер с правильным смещением:

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>

Но это кажется немного неэффективным, потому что я должен создать новый буфер для каждого добавления.

Знаете ли вы, что лучший способ для добавления двоичных данных?

4b9b3361

Ответ 1

Обновлен ответ для Node.js ~ > 0.8

Node может объединить буферы самостоятельно.

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

Старый ответ для Node.js ~ 0.6

Я использую модуль для добавления функции .concat, среди прочего:

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

Я знаю, что это не "чистое" решение, но оно отлично работает для моих целей.

Ответ 2

Буферы всегда имеют фиксированный размер, нет встроенного способа динамического изменения размера, поэтому ваш подход к копированию в буфер большего размера является единственным способом.

Однако, чтобы быть более эффективным, вы можете сделать Buffer больше, чем исходное содержимое, поэтому оно содержит некоторое "свободное" пространство, где вы можете добавлять данные без перераспределения буфера. Таким образом, вам не нужно создавать новый буфер и копировать содержимое в каждую операцию добавления.

Ответ 3

Это поможет любому, кто приходит сюда искать решение, которое хочет использовать чистый подход. Я бы рекомендовал понять эту проблему, потому что это может произойти в самых разных местах не только с помощью объекта JS Buffer. Понимая, почему существует проблема и как ее решить, вы улучшите свою способность решать другие проблемы в будущем, так как это настолько фундаментально.

Для тех из нас, кто имеет дело с этими проблемами на других языках, вполне естественно придумать решение, но есть люди, которые могут не понимать, как абстрагироваться от сложностей и внедрить в целом эффективный динамический буфер. Ниже приведен код, который может быть оптимизирован.

Я оставил метод чтения нереализованным, чтобы сохранить пример небольшого размера.

Функция realloc в C (или любом языке, относящемся к внутренним распределениям) не гарантирует, что распределение будет расширено по размеру, не перемещая существующие данные, хотя иногда это возможно. Поэтому большинство приложений, когда требуется хранить неизвестный объем данных, будет использовать метод, как показано ниже, а не постоянно перераспределять, если перераспределение не очень редко. По сути, это то, как большинство файловых систем обрабатывают запись данных в файл. Файловая система просто выделяет еще один node и связывает все узлы, и когда вы читаете их, сложность абстрагируется, так что файл/буфер представляется единственным непрерывным буфером.

Для тех из вас, кто хочет понять трудность просто просто предоставления динамического буфера с высокой производительностью, вам нужно только просмотреть приведенный ниже код, а также провести некоторое исследование алгоритмов кучи памяти и как куча памяти работает для программ.

Большинство языков будут предоставлять буфер фиксированного размера по соображениям производительности, а затем предоставить другую версию с динамическим размером. Некоторые языковые системы выбирают стороннюю систему, где они сохраняют базовую функциональность минимальной (основной дистрибутив) и поощряют разработчиков создавать библиотеки для решения дополнительных или более сложных проблем. Вот почему вы можете задаться вопросом, почему язык не предоставляет некоторых функций. Эта небольшая базовая функциональность позволяет снизить затраты при сохранении и улучшении языка, однако в конечном итоге вам приходится писать свои собственные реализации или в зависимости от сторонних разработчиков.

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