TGViewer
Настоящий JavaScript Настоящий JavaScript @true_js · 5.89K subscribers
Post #3592 304
⁣Buffer и Uint8Array: грабли, которые стоят часов дебага

Одна из самых коварных ловушек в Node.js — смешивание Buffer и Uint8Array при парсинге бинарных протоколов. На первый взгляд они взаимозаменяемы: Buffer наследует от Uint8Array. Но под капотом — разные контракты, и "тихая" конвертация данных может убить zero-copy или вызвать TypeError.

Проблема: slice vs subarray

Допустим, ты парсишь TCP-поток с zero-copy, работая с подмассивами:

// Ошибка: buffer.slice() копирует данные в Node <20
function parsePacket(raw: Buffer): number {
const header = raw.slice(0, 4);
return header.readUInt32BE(); // работает, но копия
}

// Правильно: subarray() делает zero-copy view
function parsePacket(raw: Buffer): number {
const header = raw.subarray(0, 4); // Uint8Array
// TypeError: header.readUInt32BE is not a function
}


Ключевые грабли

* Buffer.prototype.slice() до Node 20 копирует данные, убивая zero-copy.
* Buffer.prototype.subarray() возвращает Uint8Array, а не Buffer. Методы .readUInt* недоступны.
* Обратная конвертация (передача Uint8Array в Buffer API) — тихая, но с сюрпризом: методы Buffer не работают.

Правильный подход: DataView

Для кросс-платформенного zero-copy парсинга используй DataView поверх ArrayBuffer:

class PacketParser {
private view: DataView;

constructor(private buffer: ArrayBuffer | Buffer) {
this.view = new DataView(
buffer instanceof Buffer ? buffer.buffer : buffer
);
}

readUint32(offset: number): number {
return this.view.getUint32(offset, false); // big-endian
}

readSlice(offset: number, length: number): ArrayBuffer {
return this.buffer.slice(offset, offset + length);
}
}

// Zero-copy: subarray, затем DataView
const raw = Buffer.from([0, 0, 0, 42]);
const firstPacket = raw.subarray(0, 4); // Uint8Array
const parser = new PacketParser(firstPacket.buffer);
console.log(parser.readUint32(0)); // 42


Практические советы

* Не используй Buffer.read* при zero-copy — они не работают с Uint8Array.
* Для кросс-платформенного парсинга (браузер + Node) — только DataView.
* Buffer.concat возвращает Buffer и копирует данные — явная конвертация.
* Если нужен гибридный парсер, оберни все в new Uint8Array(bufferOrView) и используй TextDecoder или DataView.

Предупреждение
Неявное смешивание типов — главный источник багов производительности и логики. Zero-copy теряется, как только ты вызываешь .slice() или передаешь Buffer в API браузера (например, WebSocket.send).

Вывод: Для надежного zero-copy парсинга бинарных протоколов используй DataView поверх subarray — это единственный способ избежать тихих конвертаций и сохранить производительность в production.
More from @true_js
  1. Oct 3, 2026Post #3963
  2. Oct 2, 2026😅 Айтишник отправил в одну компанию три одинаковых резюме и только одно дошло до финала О…
  3. Oct 2, 2026Post #3961
  4. Oct 2, 2026🤣 Правильно расставленные приоритеты в моей жизни би лайк: 💥 xCode Journal
  5. Oct 1, 2026🤯 Люди взбунтовались против «пыточной для ИИ» На GitHub заметили открытый проект AI Tortu…
  6. Oct 1, 2026День в Заонежье начинается ещё в дороге. Мы встретим вас в аэропорту или на вокзале. Дальш…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →