Implement a Fixed-Capacity Ring Buffer in C for Firmware

Quick Overview

An embedded-software question that asks for a fixed-capacity ring buffer in C with put, get, count, empty and full operations. It tests wrap-around indexing, telling a full buffer from an empty one, the policy when the buffer is full, and safe use between an interrupt handler and the main loop.

Implement a Fixed-Capacity Ring Buffer in C for Firmware

Company: Amazon

Role: Software Engineer

Category: Software Engineering Fundamentals

Difficulty: medium

Interview Round: Technical Screen

Implement a fixed-capacity ring buffer (circular buffer) in C. Its storage is set up once, and the write and read positions wrap back to the start of the storage when they reach its end, so elements come out in the order they went in. Assume byte elements, storage supplied by the caller, and an interface like the one below unless the interviewer specifies otherwise. What a put into a full buffer should do is a policy to settle with the interviewer. ```c #include <stdbool.h> #include <stddef.h> #include <stdint.h> typedef struct ring_buffer ring_buffer_t; /* you define its fields */ bool rb_init(ring_buffer_t *rb, uint8_t *storage, size_t capacity); bool rb_put(ring_buffer_t *rb, uint8_t value); /* true if the value was stored */ bool rb_get(ring_buffer_t *rb, uint8_t *out); /* true if a value was read */ size_t rb_count(ring_buffer_t *rb); bool rb_is_empty(ring_buffer_t *rb); bool rb_is_full(ring_buffer_t *rb); ``` ```hint Full or empty? When the read position and the write position are equal, the buffer could be empty or full. Decide how your structure tells the two apart. ``` ```hint Wrapping cheaply Consider how each position wraps past the end of the storage, and what that costs on a small microcontroller without a hardware divider. ``` ### Clarifying Questions - When the buffer is full, should a put fail and report it, or overwrite the oldest element? - Do the producer and the consumer run in different execution contexts, such as an interrupt handler and the main loop, or two cores, and is there exactly one of each? - What element type and capacity are needed, and may the capacity be required to be a power of two? - Is dynamic memory allowed, or must the storage be static? - Are bulk operations that put or get several elements at once needed? ### What a Strong Answer Covers - Correct wrap-around indexing and a clear rule that distinguishes full from empty - A defined behavior when putting into a full buffer and getting from an empty one - Constant-time operations without dynamic allocation - Safety when the producer and the consumer run in different contexts: which side writes which variable, ordering of the data write and the index update, atomic access - Edge cases: capacity 1, positions wrapping many times, counter overflow - How the buffer is tested ### Follow-up Questions - The producer is a UART receive interrupt and the consumer is the main loop. What makes your buffer safe without disabling interrupts? - How would you add bulk reads and writes that copy contiguous blocks, for example to feed a DMA transfer? - If the positions are free-running counters that are never wrapped, what happens when they overflow, and why can that still be correct? - How would you extend the buffer to several producers?

Overview: An embedded-software question that asks for a fixed-capacity ring buffer in C with put, get, count, empty and full operations. It tests wrap-around indexing, telling a full buffer from an empty one, the policy when the buffer is full, and safe use between an interrupt handler and the main loop.

Amazon logo
Amazon
Oct 31, 2026
mediumSoftware EngineerTechnical ScreenSoftware Engineering Fundamentals
0
0

Implement a fixed-capacity ring buffer (circular buffer) in C. Its storage is set up once, and the write and read positions wrap back to the start of the storage when they reach its end, so elements come out in the order they went in. Assume byte elements, storage supplied by the caller, and an interface like the one below unless the interviewer specifies otherwise. What a put into a full buffer should do is a policy to settle with the interviewer.

#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>

typedef struct ring_buffer ring_buffer_t;   /* you define its fields */

bool   rb_init(ring_buffer_t *rb, uint8_t *storage, size_t capacity);
bool   rb_put(ring_buffer_t *rb, uint8_t value);   /* true if the value was stored */
bool   rb_get(ring_buffer_t *rb, uint8_t *out);    /* true if a value was read */
size_t rb_count(ring_buffer_t *rb);
bool   rb_is_empty(ring_buffer_t *rb);
bool   rb_is_full(ring_buffer_t *rb);

Clarifying Questions Guidance

  • When the buffer is full, should a put fail and report it, or overwrite the oldest element?
  • Do the producer and the consumer run in different execution contexts, such as an interrupt handler and the main loop, or two cores, and is there exactly one of each?
  • What element type and capacity are needed, and may the capacity be required to be a power of two?
  • Is dynamic memory allowed, or must the storage be static?
  • Are bulk operations that put or get several elements at once needed?

What a Strong Answer Covers Guidance

  • Correct wrap-around indexing and a clear rule that distinguishes full from empty
  • A defined behavior when putting into a full buffer and getting from an empty one
  • Constant-time operations without dynamic allocation
  • Safety when the producer and the consumer run in different contexts: which side writes which variable, ordering of the data write and the index update, atomic access
  • Edge cases: capacity 1, positions wrapping many times, counter overflow
  • How the buffer is tested

Follow-up Questions Guidance

  • The producer is a UART receive interrupt and the consumer is the main loop. What makes your buffer safe without disabling interrupts?
  • How would you add bulk reads and writes that copy contiguous blocks, for example to feed a DMA transfer?
  • If the positions are free-running counters that are never wrapped, what happens when they overflow, and why can that still be correct?
  • How would you extend the buffer to several producers?