How do you Design a Circular FIFO Buffer in C?

in #programming8 years ago (edited)

I was recently being asked by this question: How do you Design a Circular FIFO Buffer in C? You need to implement two methods: fifoRead and fifoWrite, which reads or writes a byte to the buffer respectively.

  1. C Implementation Only.
  2. Circular Buffer
  3. First-In-First-Out
  4. fifoRead will read a byte from the buffer, if the buffer is empty, it should return a EMPTY error code.
  5. fifoWrite will write a byte to the buffer, if the buffer is full, it should return a FULL error code.
  6. The size of the buffer is fixed (not changing dynamically runtime)

It is fixed size so the best data structure is the static array that we can use to represent the buffer or queue.

#define BUFFER_SIZE 100
#define ERROR_EMPTY 0
#define ERROR_FULL 0xFF

char buffer[BUFFER_SIZE];

We can then have two index-pointers head and tail pointing to the beginning and the end of the buffer, default to zeros.

int head = 0, tail = 0;

image.png

When a byte is to insert into the buffer, we move the head and on the other hand, when a byte is about to be read from the buffer we move the tail.

If we all move the head and tail in clock-wise direction (moving to the right), we also need to rewind the pointers when they reach the end of the array i.e. head = (head + 1) % BUFFER_SIZE and tail = (tail + 1) % BUFFER_SIZE

We can use a counter variable to record the number of stored-bytes in the buffer. Therefore, the fifoRead and fifoWrite can be implemented as follows:

int count = 0; 

// reads a byte from the buffer and return ERROR_EMPTY if buffer empty
char fifoRead() {
   if (0 == count) return ERROR_EMPTY;
   count --;
   tail = (tail + 1) % BUFFER_SIZE;
   return buffer[tail];
}

// writes a byte to the buffer if not ERROR_FULL
char fifoWrite(chat val) {
   if (BUFFER_SIZE == count) return ERROR_FULL;
   count ++;
   head = (head + 1) % BUFFER_SIZE;
   return buffer[head];
}

We don't like global variables in this case counter. When you do counter -- or counter ++, what computer does is to copy the value from memory counter to register, increment or decrement the register, and copy the value from register back to the memory. In case of hardware interrupts (similar to multithreading), the value of counter may be incorrectly updated.

So, if we get rid of the counter, how do we check if the buffer is empty or full? The answer is in the head and tail itself.

When the buffer if empty, the head == tail is true. And when the buffer is about to be full, the head + 1 == tail is true.

// reads a byte from the buffer and return ERROR_EMPTY if buffer empty
char fifoRead() {
   if (head == tail) return ERROR_EMPTY;
   tail = (tail + 1) % BUFFER_SIZE;
   return buffer[tail];
}

// writes a byte to the buffer if not ERROR_FULL
char fifoWrite(chat val) {
   if (head + 1 == tail) return ERROR_FULL;
   head = (head + 1) % BUFFER_SIZE;
   return buffer[head];
}

In this case, we can only store BUFFER_SIZE - 1 elements in the buffer because we have to distinguish between BUFFER emtpy and BUFFER full.

Reposted to my blog: https://helloacm.com/how-do-you-design-a-circular-fifo-buffer-queue-in-c/

Support me and my work as a witness - witness thread by

  1. voting me here, or
  2. voting me as a proxy.

Thank you! Some of My Contributions: SteemIt Tutorials, Robots, Tools and APIs

Coin Marketplace

STEEM 0.04
TRX 0.33
JST 0.081
BTC 61242.42
ETH 1634.45
USDT 1.00
SBD 0.42