Fixed-Size Circular Buffer
Store the most recent items in a fixed-capacity circular buffer with overwrite eviction, indexed access, iteration, and clearing.
Code
export class CircularBuffer<T> {
private readonly items: Array<T | undefined>;
private head = 0;
private count = 0;
public constructor(public readonly capacity: number) {
if (!Number.isInteger(capacity) || capacity < 1) {
throw new RangeError("capacity must be a positive integer");
}
this.items = new Array<T | undefined>(capacity);
}
public get size(): number {
return this.count;
}
public get full(): boolean {
return this.count === this.capacity;
}
public push(value: T): T | undefined {
const evicted =
this.count === this.capacity ? this.items[this.head] : undefined;
this.items[this.head] = value;
this.head = (this.head + 1) % this.capacity;
if (this.count < this.capacity) this.count += 1;
return evicted;
}
public at(index: number): T {
if (!Number.isInteger(index) || index < 0 || index >= this.count) {
throw new RangeError(`index out of range: ${index}`);
}
const start =
(this.head - this.count + this.capacity) % this.capacity;
return this.items[(start + index) % this.capacity] as T;
}
public toArray(): T[] {
const output: T[] = [];
for (let index = 0; index < this.count; index += 1) {
output.push(this.at(index));
}
return output;
}
public clear(): void {
this.items.fill(undefined);
this.head = 0;
this.count = 0;
}
public *[Symbol.iterator](): IterableIterator<T> {
for (let index = 0; index < this.count; index += 1) {
yield this.at(index);
}
}
}
const buffer = new CircularBuffer<number>(3);
console.log(buffer.push(1), buffer.push(2), buffer.push(3));
console.log(buffer.push(4));
console.log(buffer.toArray(), buffer.at(0), buffer.at(2), buffer.full);
console.log([...buffer]);
buffer.clear();
console.log(buffer.size, buffer.toArray());
try {
buffer.at(0);
} catch (error) {
console.log(error instanceof Error ? error.message : String(error));
}Report a problem with this exampleOpens a pre-filled issue on GitHub.