/ utils / CircularBuffer.ts
CircularBuffer.ts
 1  /**
 2   * A fixed-size circular buffer that automatically evicts the oldest items
 3   * when the buffer is full. Useful for maintaining a rolling window of data.
 4   */
 5  export class CircularBuffer<T> {
 6    private buffer: T[]
 7    private head = 0
 8    private size = 0
 9  
10    constructor(private capacity: number) {
11      this.buffer = new Array(capacity)
12    }
13  
14    /**
15     * Add an item to the buffer. If the buffer is full,
16     * the oldest item will be evicted.
17     */
18    add(item: T): void {
19      this.buffer[this.head] = item
20      this.head = (this.head + 1) % this.capacity
21      if (this.size < this.capacity) {
22        this.size++
23      }
24    }
25  
26    /**
27     * Add multiple items to the buffer at once.
28     */
29    addAll(items: T[]): void {
30      for (const item of items) {
31        this.add(item)
32      }
33    }
34  
35    /**
36     * Get the most recent N items from the buffer.
37     * Returns fewer items if the buffer contains less than N items.
38     */
39    getRecent(count: number): T[] {
40      const result: T[] = []
41      const start = this.size < this.capacity ? 0 : this.head
42      const available = Math.min(count, this.size)
43  
44      for (let i = 0; i < available; i++) {
45        const index = (start + this.size - available + i) % this.capacity
46        result.push(this.buffer[index]!)
47      }
48  
49      return result
50    }
51  
52    /**
53     * Get all items currently in the buffer, in order from oldest to newest.
54     */
55    toArray(): T[] {
56      if (this.size === 0) return []
57  
58      const result: T[] = []
59      const start = this.size < this.capacity ? 0 : this.head
60  
61      for (let i = 0; i < this.size; i++) {
62        const index = (start + i) % this.capacity
63        result.push(this.buffer[index]!)
64      }
65  
66      return result
67    }
68  
69    /**
70     * Clear all items from the buffer.
71     */
72    clear(): void {
73      this.buffer.length = 0
74      this.head = 0
75      this.size = 0
76    }
77  
78    /**
79     * Get the current number of items in the buffer.
80     */
81    length(): number {
82      return this.size
83    }
84  }