feat: fully implement circular buffer semantic
authorJérôme Benoit <jerome.benoit@piment-noir.org>
Fri, 24 May 2024 04:27:04 +0000 (06:27 +0200)
committerJérôme Benoit <jerome.benoit@piment-noir.org>
Fri, 24 May 2024 04:27:04 +0000 (06:27 +0200)
Signed-off-by: Jérôme Benoit <jerome.benoit@piment-noir.org>
src/circular-buffer.ts
tests/circular-buffer.test.mjs

index bd59b25cd8377be9d54d183af8947f2244099a22..46f6df5f2883433d8516890b075d267d7d0f1250 100644 (file)
@@ -10,10 +10,11 @@ export const defaultBufferSize = 2048
  * @internal
  */
 export class CircularBuffer<T> {
-  private readonly readIdx: number
+  private readIdx: number
   private writeIdx: number
   private items: Array<T | undefined>
   private readonly maxArrayIdx: number
+  public size: number
 
   /**
    * @param size - Buffer size. @defaultValue defaultBufferSize
@@ -24,9 +25,28 @@ export class CircularBuffer<T> {
     this.readIdx = 0
     this.writeIdx = 0
     this.maxArrayIdx = size - 1
+    this.size = 0
     this.items = new Array<T | undefined>(size)
   }
 
+  /**
+   * Checks whether the buffer is empty.
+   *
+   * @returns Whether the buffer is empty.
+   */
+  public empty (): boolean {
+    return this.size === 0
+  }
+
+  /**
+   * Checks whether the buffer is full.
+   *
+   * @returns Whether the buffer is full.
+   */
+  public full (): boolean {
+    return this.size === this.items.length
+  }
+
   /**
    * Puts data into buffer.
    *
@@ -35,6 +55,25 @@ export class CircularBuffer<T> {
   public put (data: T): void {
     this.items[this.writeIdx] = data
     this.writeIdx = this.writeIdx === this.maxArrayIdx ? 0 : this.writeIdx + 1
+    if (this.size < this.items.length) {
+      ++this.size
+    }
+  }
+
+  /**
+   * Gets data from buffer.
+   *
+   * @returns Data from buffer.
+   */
+  public get (): T | undefined {
+    const data = this.items[this.readIdx]
+    if (data == null) {
+      return
+    }
+    this.items[this.readIdx] = undefined
+    this.readIdx = this.readIdx === this.maxArrayIdx ? 0 : this.readIdx + 1
+    --this.size
+    return data
   }
 
   /**
index 804bb587962aca4c66f9808e923c33d2dd1eef93..c6351eb7128ea956a817def1be81ccd63d7a03c6 100644 (file)
@@ -9,15 +9,17 @@ describe('Circular buffer test suite', t => {
     expect(circularBuffer.readIdx).toBe(0)
     expect(circularBuffer.writeIdx).toBe(0)
     expect(circularBuffer.maxArrayIdx).toBe(defaultBufferSize - 1)
+    expect(circularBuffer.size).toBe(0)
     expect(circularBuffer.items).toBeInstanceOf(Array)
     expect(circularBuffer.items.length).toBe(defaultBufferSize)
   })
 
   it('Verify that circular buffer size can be set at instance creation', () => {
-    const circularBuffer = new CircularBuffer(1000)
-    expect(circularBuffer.maxArrayIdx).toBe(999)
+    const size = 1000
+    const circularBuffer = new CircularBuffer(size)
+    expect(circularBuffer.maxArrayIdx).toBe(size - 1)
     expect(circularBuffer.items).toBeInstanceOf(Array)
-    expect(circularBuffer.items.length).toBe(1000)
+    expect(circularBuffer.items.length).toBe(size)
   })
 
   it('Verify that circular buffer size is valid at instance creation', () => {
@@ -41,21 +43,105 @@ describe('Circular buffer test suite', t => {
     circularBuffer.put(1)
     expect(circularBuffer.items).toMatchObject([1])
     expect(circularBuffer.writeIdx).toBe(1)
+    expect(circularBuffer.size).toBe(1)
     circularBuffer.put(2)
     expect(circularBuffer.items).toMatchObject([1, 2])
     expect(circularBuffer.writeIdx).toBe(2)
+    expect(circularBuffer.size).toBe(2)
     circularBuffer.put(3)
     expect(circularBuffer.items).toMatchObject([1, 2, 3])
     expect(circularBuffer.writeIdx).toBe(3)
+    expect(circularBuffer.size).toBe(3)
     circularBuffer.put(4)
     expect(circularBuffer.items).toMatchObject([1, 2, 3, 4])
     expect(circularBuffer.writeIdx).toBe(0)
+    expect(circularBuffer.size).toBe(4)
     circularBuffer.put(5)
     expect(circularBuffer.items).toMatchObject([5, 2, 3, 4])
     expect(circularBuffer.writeIdx).toBe(1)
+    expect(circularBuffer.size).toBe(4)
     circularBuffer.put(6)
     expect(circularBuffer.items).toMatchObject([5, 6, 3, 4])
     expect(circularBuffer.writeIdx).toBe(2)
+    expect(circularBuffer.size).toBe(4)
+  })
+
+  it('Verify that circular buffer get() works as intended', () => {
+    const circularBuffer = new CircularBuffer(4)
+    circularBuffer.put(1)
+    circularBuffer.put(2)
+    circularBuffer.put(3)
+    circularBuffer.put(4)
+    expect(circularBuffer.get()).toBe(1)
+    expect(circularBuffer.readIdx).toBe(1)
+    expect(circularBuffer.size).toBe(3)
+    expect(circularBuffer.get()).toBe(2)
+    expect(circularBuffer.readIdx).toBe(2)
+    expect(circularBuffer.size).toBe(2)
+    circularBuffer.put(5)
+    circularBuffer.put(6)
+    expect(circularBuffer.get()).toBe(3)
+    expect(circularBuffer.readIdx).toBe(3)
+    expect(circularBuffer.size).toBe(3)
+    expect(circularBuffer.get()).toBe(4)
+    expect(circularBuffer.readIdx).toBe(0)
+    expect(circularBuffer.size).toBe(2)
+    expect(circularBuffer.get()).toBe(5)
+    expect(circularBuffer.readIdx).toBe(1)
+    expect(circularBuffer.size).toBe(1)
+    expect(circularBuffer.get()).toBe(6)
+    expect(circularBuffer.readIdx).toBe(2)
+    expect(circularBuffer.size).toBe(0)
+    expect(circularBuffer.get()).toBe(undefined)
+    expect(circularBuffer.readIdx).toBe(2)
+    expect(circularBuffer.size).toBe(0)
+  })
+
+  it('Verify that circular buffer empty() works as intended', () => {
+    const circularBuffer = new CircularBuffer(4)
+    expect(circularBuffer.empty()).toBe(true)
+    circularBuffer.put(1)
+    expect(circularBuffer.empty()).toBe(false)
+    circularBuffer.put(2)
+    expect(circularBuffer.empty()).toBe(false)
+    circularBuffer.put(3)
+    expect(circularBuffer.empty()).toBe(false)
+    circularBuffer.put(4)
+    expect(circularBuffer.empty()).toBe(false)
+    circularBuffer.get()
+    expect(circularBuffer.empty()).toBe(false)
+    circularBuffer.get()
+    expect(circularBuffer.empty()).toBe(false)
+    circularBuffer.get()
+    expect(circularBuffer.empty()).toBe(false)
+    circularBuffer.get()
+    expect(circularBuffer.empty()).toBe(true)
+  })
+
+  it('Verify that circular buffer full() works as intended', () => {
+    const circularBuffer = new CircularBuffer(4)
+    expect(circularBuffer.full()).toBe(false)
+    circularBuffer.put(1)
+    expect(circularBuffer.full()).toBe(false)
+    circularBuffer.put(2)
+    expect(circularBuffer.full()).toBe(false)
+    circularBuffer.put(3)
+    expect(circularBuffer.full()).toBe(false)
+    circularBuffer.put(4)
+    expect(circularBuffer.full()).toBe(true)
+    circularBuffer.get()
+    expect(circularBuffer.full()).toBe(false)
+    circularBuffer.put(5)
+    expect(circularBuffer.full()).toBe(true)
+    circularBuffer.get()
+    expect(circularBuffer.full()).toBe(false)
+    circularBuffer.get()
+    expect(circularBuffer.full()).toBe(false)
+    circularBuffer.get()
+    expect(circularBuffer.full()).toBe(false)
+    circularBuffer.get()
+    expect(circularBuffer.full()).toBe(false)
+    expect(circularBuffer.empty()).toBe(true)
   })
 
   it('Verify that circular buffer toArray() works as intended', () => {