← Questions

Fixed-capacity circular buffer with O(1) push and pop

0
Applied IntuitionAmazonSnowflake+1CodingLast asked Aug 2026

Implement a generic first-in, first-out buffer with a fixed capacity. Its storage is allocated once, when the buffer is created, and never grows; pushing and popping are constant time. Then discuss how the buffer should report full and empty, and how one producer and one consumer could share it.

Interface

Unless you agree on a different interface with the interviewer, assume (shown in Python; a C++ template RingBuffer<T> is equally fine):

  • RingBuffer(capacity: int): allocates storage for capacity items; capacity >= 1.
  • push(item) -> bool: appends at the back; returns False and changes nothing when full.
  • pop() -> item | None: removes and returns the front item; None when empty.
  • peek() -> item | None, size() -> int, is_empty() -> bool, is_full() -> bool.

Example

b = RingBuffer(3)
b.push(1), b.push(2), b.push(3)   -> True, True, True
b.push(4)                         -> False     # full
b.pop()                           -> 1
b.push(4)                         -> True      # wraps into the freed slot
b.pop(), b.pop(), b.pop()         -> 2, 3, 4
b.pop()                           -> None      # empty

Constraints

  • No allocation after construction; every operation O(1).
  • Items are of any type; the buffer must not keep references to popped items alive.

Clarifying questions

  • When full, should push fail, overwrite the oldest item, or block?
  • Should errors be return values, exceptions, or optional types?
  • Single-threaded, or one producer and one consumer, or many of each?
  • In C++, must items be default-constructible, and who destroys them?

What a strong answer covers

  • A fixed array with head and tail indices that wrap with modulo (or a power-of-two mask), plus a count, or one slot left empty, to tell full from empty; the trade-off between the two.
  • Clearing a popped slot so the object can be freed; in C++, placement new and explicit destruction with raw storage so T needs no default constructor.
  • Edge cases: capacity 1, many wrap-arounds, alternating push and pop at the boundary.
  • For one producer and one consumer: a lock-free design where only the producer writes the tail and only the consumer writes the head, with acquire/release ordering; a mutex and condition variables for blocking or many producers.
  • Tests that wrap many times and compare against a reference queue.

Follow-ups

  • Add an overwrite-oldest mode for streaming sensor data.
  • Make it safe for one producer and one consumer without locks. Which memory orderings do you need, and why?
  • Add push_many and pop_many that copy in at most two contiguous chunks.
Write and test your solution herePython in your browser, the question's tests level by level, every submission kept for you.Sign in to start coding