interface Lengthwise { length: number } export class RingBuffer { private capacity: number private maxItems: number private usage: number = 0 private items: Array = [] private start: number = 0 private end: number = 0 constructor(capacity: number, maxItems = Infinity, ringBuffer?: RingBuffer) { this.capacity = capacity this.maxItems = maxItems if (ringBuffer) { this.items = ringBuffer.toArray() this.end = this.items.length this.usage = ringBuffer.usage } } private enforceCapacityConstraints(addedItemSize: number) { const remainingSize = this.capacity - (this.usage + addedItemSize) if (remainingSize < 0) { this.freeSomeSpace(Math.abs(remainingSize)) } while (this.end - this.start >= this.maxItems) { this.dropFirst() } } private freeSpace(): number { return this.capacity - this.usage } private isEmpty(): boolean { return this.usage === 0 } private freeSomeSpace(requiredSpace: number) { while (this.freeSpace() < requiredSpace && !this.isEmpty()) { this.dropFirst() } } private dropFirst() { const firstItem = this.items[this.start] delete this.items[this.start] this.start += 1 const freedSpace = firstItem.length this.usage -= freedSpace } public setCapacity(items: number, bytes: number) { this.maxItems = items this.capacity = bytes } public clone(): RingBuffer { return new RingBuffer(this.capacity, this.maxItems, this) } public toArray() { return this.items.slice(this.start, this.end) } public count() { return this.end - this.start } public add(item: T) { const size = item.length this.enforceCapacityConstraints(size) this.usage += size this.items[this.end] = item this.end += 1 } }