Як додати двійкові дані до буфера в node.js


84

У мене є буфер з деякими двійковими даними:

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>

Але це здається трохи неефективним, оскільки мені доводиться створювати новий буфер для кожного додавання.

Чи знаєте ви кращий спосіб додавання двійкових даних?

РЕДАГУВАТИ

Я написав 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

ОСТАННЄ ОНОВЛЕННЯ

Використовуйте concat .


3
Було б зрозуміліше читати, якби міні-відповіді вгорі були фактичними відповідями, і тут питання було саме по собі.
Anko

Відповіді:


140

Оновлена ​​відповідь для Node.js ~> 0,8

Node може самостійно об'єднати буфери .

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

Стара відповідь для Node.js ~ 0,6

Я використовую модуль для додавання .concatфункції, серед іншого:

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

Я знаю, що це не "чисте" рішення, але воно дуже добре працює для моїх цілей.


concatФункція робить саме те , що я писав :( Він обчислює від загальної довжини і потім копіює дані з усіх буферів регулювальних зміщення ..
Gabriel Лами

Ось як це має працювати. Як зазначив @stewe, буфери створюються за допомогою фіксованого розміру завдяки способу виділення пам'яті.
Бред,

2
Але в c ми маємо функцію realloc для динамічного розширення пам'яті, коли це необхідно. Node.js повинен це знати.
Габріель Ламас,

1
@GabrielLlamas, я б рекомендував надіслати патч до їхнього сховища.
Бред,

11
Я виявив, чому у node.js немає динамічних буферів: markmail.org/message/vx2h3uslwgludu3y
Габріель Ламас

10

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

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


8

Це допоможе кожному, хто прийде сюди, шукаючи рішення, яке хоче чистого підходу. Я б порадив зрозуміти цю проблему, оскільки вона може траплятися у багатьох різних місцях, не тільки з об'єктом JS Buffer. Розуміючи, чому проблема існує та як її вирішити, ви вдосконалите свою здатність вирішувати інші проблеми в майбутньому, оскільки ця проблема є настільки фундаментальною.

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

Я залишив метод читання нереалізованим, щоб приклад мав невеликі розміри.

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

Тим з вас, хто хоче зрозуміти труднощі просто у забезпеченні високопродуктивного динамічного буфера, вам потрібно лише переглянути код нижче, а також провести деякі дослідження алгоритмів купи пам’яті та того, як ця пам’ять працює для програм.

Більшість мов забезпечують буфер фіксованого розміру з міркувань продуктивності, а потім надають іншу версію, динамічну за розміром. Деякі мовні системи вибирають сторонні системи, де вони зберігають мінімальну базову функціональність (основний розподіл) та заохочують розробників створювати бібліотеки для вирішення додаткових або більш високих проблем. Ось чому ви можете поставити запитання, чому мова не надає певних функціональних можливостей. Ця невелика основна функціональність дозволяє зменшити витрати на підтримку та вдосконалення мови, однак вам в кінцевому підсумку доведеться писати власні реалізації або в залежності від третьої сторони.

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

Я б порадив бути гранично обережними під час використання цього розчину. Якщо причиною, через яку ви хочете змінювати розмір буферів, є продуктивність, не використовуйте це . Кожен окремий байт, записаний у масштабований масив, породжується this.buffer_list[lastbuf][this.cur_size + x] = data[x + offset];, що без потреби вводить додатковий хеш-пошук, безліч додаткових перевірок масивів та дві цілі перевірки SMI з кожним байтом. Якщо ви бажаєте ефективності, я настійно закликаю вас не використовувати цю відповідь. Натомість виділіть новий масив бажаного розміру та скопіюйте дані до нового масиву. Це те, що робить Java, і це дуже швидко.
Джек Гіффін,

0

вставити байт у певне місце.

insertToArray(arr,index,item) {
   return Buffer.concat([arr.slice(0,index),Buffer.from(item,"utf-8"),arr.slice(index)]);
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.