Double-Ended Queue Implementation

A growable ring-buffer deque with amortized O(1) pushes and pops at both ends, plus indexed snapshot access.

JavascriptData Integrity

Code

snippet
// Double-ended queue backed by a growable ring buffer.
export class Deque {
  #buffer = new Array(8);
  #head = 0;
  #size = 0;

  pushBack(value) {
    this.#ensure(this.#size + 1);
    this.#buffer[(this.#head + this.#size) % this.#buffer.length] = value;
    this.#size += 1;
    return this;
  }

  pushFront(value) {
    this.#ensure(this.#size + 1);
    this.#head = (this.#head - 1 + this.#buffer.length) % this.#buffer.length;
    this.#buffer[this.#head] = value;
    this.#size += 1;
    return this;
  }

  popFront() {
    if (this.#size === 0) return undefined;
    const value = this.#buffer[this.#head];
    this.#buffer[this.#head] = undefined;
    this.#head = (this.#head + 1) % this.#buffer.length;
    this.#size -= 1;
    return value;
  }

  popBack() {
    if (this.#size === 0) return undefined;
    this.#size -= 1;
    const index = (this.#head + this.#size) % this.#buffer.length;
    const value = this.#buffer[index];
    this.#buffer[index] = undefined;
    return value;
  }

  peekFront() {
    return this.#size > 0 ? this.#buffer[this.#head] : undefined;
  }

  peekBack() {
    return this.#size > 0
      ? this.#buffer[(this.#head + this.#size - 1) % this.#buffer.length]
      : undefined;
  }

  get size() {
    return this.#size;
  }

  toArray() {
    const result = [];
    for (let index = 0; index < this.#size; index += 1) {
      result.push(this.#buffer[(this.#head + index) % this.#buffer.length]);
    }
    return result;
  }

  #ensure(capacity) {
    if (capacity <= this.#buffer.length) return;
    const next = new Array(this.#buffer.length * 2);
    for (let index = 0; index < this.#size; index += 1) {
      next[index] = this.#buffer[(this.#head + index) % this.#buffer.length];
    }
    this.#buffer = next;
    this.#head = 0;
  }
}