RingBud
A lightweight, high-performance Ring Buffer for streaming data using JavaScript
TypedArray
s.
Features
- 🧠 Frame-based buffering (configurable frame size)
- ⚡ Zero dependencies
- 🧵 Supports all major TypedArrays (e.g.
Float32Array
,Uint8Array
, etc.) - 📦 Memory efficient with optional frame trimming
- 🔁 Sync & async iteration support
- ✅ Fully tested and predictable behavior
- 🧰 Customizable preallocation and cache options
Installation
npm install ringbud
Quick Start
import { RingBufferU8 } from "ringbud";
// Create a ring buffer with frame size of 100
const rb = new RingBufferU8(100);
// Write 50 bytes (not enough for a full frame)
rb.write(new Uint8Array(50).fill(1));
console.log(rb.empty()); // true
// Write 50 more bytes (now we have a complete frame)
rb.write(new Uint8Array(50).fill(1));
console.log(rb.empty()); // false
// Read one frame of 100 bytes
const frame = rb.read();
console.log(frame); // Uint8Array(100)
// After reading, it becomes empty again
console.log(rb.empty()); // true
Supported Types
You can instantiate ring buffers for:
Uint8Array
→RingBufferU8
Uint16Array
→RingBufferU16
Float32Array
→RingBufferF32
Each subclass wraps the base RingBufferBase
with preconfigured types.
Configuration Options
All constructors accept:
{
frameSize: number, // Number of elements per frame (required)
preallocateFrameCount?: number, // Default: 10
frameCacheSize?: number // Default: 0 (no trim)
}
Frame Cache Size (Clamping)
When frameCacheSize > 0
, the ring buffer trims memory usage by shifting unread bytes after every .read()
. This reduces buffer growth at the cost of additional memory copying.
API Reference
Constructor
new RingBufferU8(frameSize: number, options?: {
preallocateFrameCount?: number;
frameCacheSize?: number;
});
Methods
Method | Description |
---|---|
write(data) |
Appends a TypedArray to the buffer |
read() |
Returns the next full frame, or null |
drain() |
Returns remaining incomplete data |
peek() |
Returns the entire buffer content (not a copy) |
empty() |
true if no full frame is available |
remainingFrames() |
Number of full frames available to read |
rewind() |
Resets read offset so frames can be re-read |
Symbol.iterator() |
Enables for (const frame of buffer) |
Symbol.asyncIterator() |
Enables for await (const frame of buffer) |
Example: Iteration
for (const frame of rb) {
console.log(frame); // each is a complete frame
}
// or async
for await (const frame of rb) {
await process(frame);
}
Example: Auto-Trimming
const rb = new RingBufferU8(100, { frameCacheSize: 1 });
rb.write(new Uint8Array(300)); // 3 frames
rb.read(); // returns 1st frame
// Buffer automatically shifts remaining frames to the front
rb.peek().subarray(0, 200); // contains frame 2 and 3
Validations & Safety
frameSize
must be an integer ≥ 1preallocateFrameCount
must be ≥ 1 (if set)- Partial frames are never returned from
.read()
or iterators - Trimming only occurs after reads when
frameCacheSize > 0
- If iteration is used, all frames are consumed as if
.read()
was called repeatedly - Frames can be shared or copied depending on cache config
TypedArray Support
Internally, the base class accepts any TypedArray
constructor:
new RingBufferBase({
frameSize: 256,
TypedArrayConstructor: Uint16Array
});
Built-in classes like RingBufferF32
are wrappers over this API.
Examples
Draining Partial Data
const rb = new RingBufferU8(100);
rb.write(new Uint8Array(230));
rb.read(); // reads 1 frame (100 bytes)
rb.read(); // reads 1 more frame (100 bytes)
rb.read(); // null (30 bytes left)
rb.drain(); // returns 30 bytes
Rewind
rb.rewind(); // enables re-reading all written frames
for (const frame of rb) {
console.log(frame);
}