data-structures · intermediate · ~15 min

Generic Ring Buffer Queue

Implement circular ring buffer queues with wrap-around head and tail indices.

Challenge

Circular ring buffers provide bounded FIFO queuing with constant time O(1) push and pop operations without shifting memory elements.

Your Task

Given the ring buffer structure:

typedef struct {
    int *data;
    size_t capacity;
    size_t head;
    size_t tail;
    size_t count;
} RingBuffer;

Implement:

int rb_push(RingBuffer *rb, int val);
int rb_pop(RingBuffer *rb, int *out);
int rb_peek(const RingBuffer *rb, int *out);

Rules

  1. In rb_push:
    • If rb == NULL or rb->count == rb->capacity, return -1 (buffer full).
    • Store val into rb->data[rb->tail].
    • Advance rb->tail = (rb->tail + 1) % rb->capacity.
    • Increment rb->count and return 0.
  2. In rb_pop:
    • If rb == NULL or out == NULL or rb->count == 0, return -1 (buffer empty).
    • Read value from rb->data[rb->head] into *out.
    • Advance rb->head = (rb->head + 1) % rb->capacity.
    • Decrement rb->count and return 0.
  3. In rb_peek:
    • If rb == NULL or out == NULL or rb->count == 0, return -1.
    • Read value from rb->data[rb->head] into *out without modifying head or count.
    • Return 0.

Example

int store[3];
RingBuffer rb = { .data = store, .capacity = 3, .head = 0, .tail = 0, .count = 0 };
rb_push(&rb, 10); rb_push(&rb, 20);
int v;
rb_pop(&rb, &v); // v == 10

Input format

rb: pointer to RingBuffer; val: int; out: pointer to int destination.

Output format

Returns 0 on success, -1 on buffer full/empty or NULL pointer.

Constraints

O(1) operations. Use modular arithmetic modulo capacity.

Starter code

#include <stddef.h>

typedef struct {
    int *data;
    size_t capacity;
    size_t head;
    size_t tail;
    size_t count;
} RingBuffer;

int rb_push(RingBuffer *rb, int val) {
    (void)rb; (void)val;
    return -1;
}
int rb_pop(RingBuffer *rb, int *out) {
    (void)rb; (void)out;
    return -1;
}
int rb_peek(const RingBuffer *rb, int *out) {
    (void)rb; (void)out;
    return -1;
}

Common mistakes

Forgetting modulo capacity wrap-around; forgetting to decrement count on pop.

Edge cases to handle

Pushing to full buffer fails; popping from empty buffer fails; head wraps around capacity.

Background lessons

Solve this exercise in the browser editor — compile and run against the test harness, no setup required.