Speed up binary I/O with zero-copy performance, automatic offset management, and a set of advanced tools.
Working with binary data in JavaScript and TypeScript has always been a chore. Manually tracking offsets, worrying about endianness, and constantly copying memory—it's easy to get bogged down in boilerplate code. Existing libraries only address some of the issues, but they either tie to Node.js Buffer or don't support modern features like SharedArrayBuffer.
Today I'm excited to introduce Flash-Buffer: a lightning-fast binary data library that makes manipulating ArrayBuffers as easy as working with JSON.
🤔 Problem: Binary data is complex and slow
Whether you're parsing a custom network protocol, reading media file headers, or serializing game state for transmission over WebSocket, you'll encounter the same inconveniences:
-
Manual offset management. Every read or write requires updating the
offsetvariable. This is a source of infinite per-unit errors. - Data copying. Standard methods often create new buffers instead of working with existing ones. This kills performance on large data sets.
-
Lack of convenient abstractions. You want to write a string or a floating-point number, but you have to fiddle with
DataViewandTextEncoder.
Flash-Buffer solves these problems by providing an intuitive API that remains incredibly fast.
🚀 Key Features
-
Zero-Copy. Reading data returns a
Uint8Array, which is a direct "view" of the memory location. No unnecessary copying just pure performance. -
Smart memory management. Automatic buffer expansion on write (
exact,powerOfTwo,fixedstrategies), built-in buffer pool to reduce GC load, nativeresize()support forArrayBuffer. -
Cross-platform. Works in browsers, Node.js, Deno, and Bun. Supports
SharedArrayBufferfor efficient data exchange between threads without copying. - Advanced data formats:
- - VarInt (LEB128) with ZigZag encoding-like Protobuf.
- Bitwise operations (BitBuffer) for flags, compressed data, and cryptography.
- C-strings (null-terminated).
- Stream adapters for the Web Streams API.
-
Schematic serialization. Use TypeScript decorators (
@field) to describe the class structure, and the library will automatically serialize it to and from a binary buffer.
💻 Usage examples
Basic reading and writing:
import { FlashBuffer } from 'flash-buffer';
// Create a buffer (auto-growing, little-endian by default)
const buf = new FlashBuffer({ endianness: 'little' });
// Write data
buf.writeUint32(0xDEADBEEF);
buf.writeString('Hello, Flash!', 'utf-8', true); // true = prefix length as uint32
// Reset the offset for reading
buf.reset();
// Read data back
const value = buf.readUint32();
const strLength = buf.readUint32();
const str = buf.readString(strLength);
console.log(value.toString(16)); // 'deadbeef'
console.log(str); // 'Hello, Flash!'
Working with VarInt (Variable-Length Integers):
import { FlashBuffer } from 'flash-buffer';
const buf = new FlashBuffer();
// Write a large number (less than 128) in one byte instead of four
buf.writeVarUint(127);
// Write a negative number efficiently using ZigZag encoding
buf.writeVarInt(-15);
buf.reset();
console.log(buf.readVarUint()); // 127
console.log(buf.readVarInt()); // -15
console.log(buf.offset); // 3 (1 byte for 127 + 2 bytes for -15)
Bit-level operations:
import { FlashBuffer } from 'flash-buffer';
const buf = new FlashBuffer();
const bits = buf.bit();
// Write a sequence of bits that may cross byte boundaries
bits.writeBits(0b11111, 5);
bits.writeBits(0b10101, 5);
bits.flush(); // Important: flush to finalize the byte and align the offset
buf.reset();
const readBits = buf.bit();
console.log(readBits.readBits(5)); // 0b11111 (31)
console.log(readBits.readBits(5)); // 0b10101 (21)
Serializing objects using decorators:
import { FlashBufferSchema, field } from 'flash-buffer';
/* Create serializable class (Player) */
class Player {
@field({ type: 'uint32' }) id: number = 0;
@field({ type: 'string' }) name: string = '';
@field({ type: 'float32' }) x: number = 0;
@field({ type: 'float32' }) y: number = 0;
}
/* Create new Player */
const player = new Player();
player.id = 1;
player.name = 'John';
player.x = 100.5;
player.y = 200.5;
/* Serialize and Restore Object */
const buf = FlashBufferSchema.serialize(player);
const restored = FlashBufferSchema.deserialize(Player, buf.reset());
📊 Comparison with similar libraries
Several excellent libraries exist for binary data manipulation. Flash Buffer was designed to combine their strengths and offer unique capabilities.
| Feature | flash-buffer | smart-buffer | @hazae41/binary | @jsonjoy.com/buffers |
|---|---|---|---|---|
| Zero-Copy | ✅ | ❌ | ✅ | ❌ |
**SharedArrayBuffer** |
✅ | ❌ | ❌ | ❌ |
| Automatic Growth | ✅ | ✅ | ❌ | ✅ |
| Streaming (Web Streams) | ✅ | ❌ | ❌ | ✅ |
| Bit-Level Operations | ✅ | ❌ | ❌ | ❌ |
| VarInt (LEB128) | ✅ | ❌ | ❌ | ❌ |
| C-Strings | ✅ | ✅ | ❌ | ❌ |
| Schema Serialization | ✅ | ❌ | ✅ | ❌ |
| Buffer Pool | ✅ | ❌ | ❌ | ❌ |
Unique advantages of Flash Buffer:
-
Full support for
SharedArrayBufferthe key to copy-free multithreading. - Modern
DataView-style API intuitive and concise. - A wide range of out-of-the-box tools: from VarInt to Serialization and streams.
📦 Installation and getting started
Just install via NPM (zero-dependency library):
npm install flash-buffer
Or clone from GitHub:
https://github.com/devsdaddy/flash-buffer/
💎 Conclusion
Flash-Buffer was created to free you from the pain of manually handling binary data. It combines the performance of zero-copy, the convenience of high-level abstractions, and support for modern web standards. If you write network protocols, parse files, or develop high-load services, try Flash-Buffer and you'll be surprised at how simple binary code can be.
I welcome stars, issues, and pull requests on GitHub. Share your experiences or questions in the comments!
Top comments (0)