Search code examples
cmingwcircular-buffer

Issues with Ring buffer implementation using direct pointer manipulation


I have written below code for ring buffer. I wanted to use only pointer without any position indicator. But I am not getting expected result while writing. Though write happens, it does not stop after 1st entry. It continues further. Read operation seems to be correct. I tried to debug using debugger. But to my surprise, if(head>(myRing+AVAILABLE_SIZE*sizeof(myRing[0]))) is not getting executed in function writeToRing() function. Debugger is skipping this step. Also first time head++ is not getting executed in this function, instead code will first go to if(head==tail) and then come back to head++; Unable to find the reason. I am using Code::Blocks with MinGW

#define MAX_SIZE 2
#define AVAILABLE_SIZE (MAX_SIZE-1)

/*
Program to construct and access ring buffer without structure.
This program makes use of pointer. Even without pointer it is possible to manage
*/

int myRing[MAX_SIZE];
int *head=myRing;   // Initialize next element than tail
int *tail=myRing;

enum ERROR_LIST
{
    SUCCESS=0,
    BUFFER_FULL=-1,
    BUFFER_EMPTY=-2
    };

int writeToRing(int data)
{
    head++;
    if(head>(myRing+AVAILABLE_SIZE*sizeof(myRing[0])))
    {
        head=myRing;  //wraps over
    }
    else
    {
        // Do nothing
    }
    if(head==tail)
    {
        head--;
        if(head<myRing)     // In case head is less than starting address. assign max address
        {
            head=(myRing+AVAILABLE_SIZE*sizeof(myRing[0]));
        }
        printf("Buffer full\n");
        return(BUFFER_FULL);
    }
    else
    {
        *head=data;
    }
    return(SUCCESS);
}

 int readFromBuffer(int* data)
 {
     if(tail==head)
     {
         return(BUFFER_EMPTY);
     }
     else
     {
         tail++;
         if(tail>(myRing+AVAILABLE_SIZE*sizeof(myRing[0])))
         {
             tail=myRing;
         }
         *data=*tail;
         return(SUCCESS);
     }
 }

int main()
{
    int option;
    int data;
    while(1)
    {
    printf("Enter Your option. 1 for writing to buffer, 2 for reading from Buffer\n");
    scanf("%d",&option);
    if(option==1)
    {
        printf("Enter the data to be written\n");
        scanf("%d",&data);
        if(writeToRing(data))
        {
            printf("Buffer is Full. Remove the contents first\n");
        }
    }
    else if(option==2)
    {
        if(!readFromBuffer(&data))
        {
            printf("The data read = %d\n",data);
        }
        else
        {
            printf("Buffer is Empty\n");
        }
    }
    else
    {
        printf("Invalid Option\n");
    }

    }
    return(0);
}

Edit: Updated the code with another approach. In this code one data byte is not wasted. I tested and seems to be working. But in case there is any other issues, please let me know. There are two warnings for scanf with type specifier %u and need to see how to fix it. I basically want to read bytes in this case. If I go for integer read, there is no issue.

#include <stdio.h>
#include <stdint.h>
#define MAX_SIZE 3

uint8_t BUFFER_FULL=0;  // Initially buffer full flag is cleared
uint8_t BUFFER_EMPTY=1;   // Initially buffer empty flag is set

/*
Program to construct and access ring buffer without pointer.
Also this makes use of full buffer and checks for buffer empty or buffer full condition
before calling write or read functionality,
*/

uint8_t myRing[MAX_SIZE];
uint8_t head=0;   // Initialize the head
uint8_t tail=0;   // Initialize the tail
uint8_t maxPosition= MAX_SIZE-1;


void writeToRing(uint8_t data)
{
    head=head+1;
    if(head>maxPosition)
    {
        head=0;
    }
    else
    {
        // Do nothing
    }
    // Write the data to buffer
    myRing[head]=data;
    BUFFER_EMPTY=0;  // Indicate that buffer is not empty

    if(head==tail)
    {
        printf("Buffer full No further data can be written\n");
        BUFFER_FULL=1;
    }
}

void readFromRing(uint8_t* data)
{
    // Initially buffer is empty (BUFFER_EMPTY=1). At that point, calling portion cannot call this function.
    // Later when data is written, writeToRing() function will clear BUFFER_EMPTY flag.

    tail++;
    if(tail>maxPosition)
    {
        tail=0;
    }
    *data=myRing[tail];
    // Once reading is done, ensure that BUFFER_FULL flag is cleared.
    BUFFER_FULL=0;
    if(tail==head)
    {
        printf("Buffer is now Empty. No further reading possible\n");
        BUFFER_EMPTY=1;
    }
}

int main()
{
    uint8_t option;
    uint8_t data;
    while(1)
    {
        printf("Enter Your option. 1 for writing to buffer, 2 for reading from Buffer\n");
        scanf("%u",&option);
        if(option==1)
        {
            if(!BUFFER_FULL)
            {
                printf("Enter the data to be written\n");
                scanf("%u",&data);
                writeToRing(data);
            }
            else
            {
                // Nothing to be done in case buffer is FULL
                printf("Buffer should be cleared first\n");
            }
        }
        else if(option==2)
        {
            if(!BUFFER_EMPTY)
            {
                uint8_t data;
                readFromRing(&data);
                printf("The data read = %d\n",data);
            }
            else
            {
                printf("Buffer is Empty. Write something before you read\n");
            }
        }
        else
        {
            printf("Invalid Option\n");
        }
    }
    return(0);
}

Solution

  • The correct form would be:

    if(head>(myRing+AVAILABLE_SIZE))
    {
    ...
    }
    

    Since myRing is a pointer, "+" operator performs the multiplication by element size in itself.