Data Structures
Table Of Contents
Ring (Circular) Buffer
A ring buffer is a fixed-size array treated as if the end wraps back to the beginning. • Two moving indices: • head (read position) • tail (write position) • Indices advance modulo the buffer capacity.
Used when you want: • bounded memory • constant-time enqueue/dequeue • predictable performance (no allocations)
Core Properties
Fixed Capacity • Size is decided up front. • When full, you must decide a policy: • reject writes • overwrite old data • block until space is available
Wrap-Around
Instead of shifting data, indices wrap:
index = (index + 1) % capacity
This is the entire trick.
Empty vs Full Problem
Because head == tail can mean either empty or full, common solutions are:
-
Keep a Count • Track size • Empty: size == 0 • Full: size == capacity
-
Leave One Slot Empty (Classic) • Capacity N, usable slots = N - 1 • Empty: head == tail • Full: (tail + 1) % N == head
-
Use a Full Flag • Boolean full • Cleared on read, set on write when tail catches head
Basic Operations
Enqueue (Write) 1. Check if full 2. Write at tail 3. Advance tail
Dequeue (Read) 1. Check if empty 2. Read at head 3. Advance head
All O(1).
Cache & Performance Characteristics
• Excellent cache locality
• No reallocations
• Very predictable latency
• Common in:
• audio buffers
• network stacks
• logging pipelines
• telemetry ingestion
• real-time systemsPower-of-Two Optimization
If capacity is a power of two:
index = index & (capacity - 1)
Instead of modulo: • Faster • Very common in high-performance code
This is why you’ll often see capacities like 1024, 4096, etc.
Concurrency Considerations
Single Producer / Single Consumer (SPSC) • Can be lock-free • Use atomic loads/stores for indices • No contention on same variable
Multiple Producers / Consumers • Usually requires: • mutex • CAS loops • sequence counters • Much harder to get right