I have a ring buffer example code that I'm practicing and for some odd reason, it adds an extra element with no apparent reason. The behavior I am coding is that it drops incoming data if buffer is full.
Here is the code:
#include <stdio.h>
#include <stdint.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdbool.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
typedef struct {
void *buf;
ssize_t ri; //read index
ssize_t wi; //write index
ssize_t capacity; // total size of buffer in bytes
size_t max_count;
size_t count;
ssize_t sz; // size of individual elements in bytes
bool full;
} r_buffer_t;
r_buffer_t* ring_buffer_init(unsigned int count, ssize_t dsize) {
r_buffer_t *buffer = malloc(sizeof(r_buffer_t));
if(buffer != NULL){
buffer->buf = malloc(count * sizeof(dsize));
if(buffer->buf != NULL) {
buffer->max_count = count;
buffer->sz = dsize;
buffer->count = 0;
buffer->ri = 0;
buffer->wi = 0;
buffer->capacity = count * dsize;
buffer->full = false;
return buffer;
}
free(buffer);
}
return NULL;
}
int put_item(r_buffer_t *buffer, void *data) {
assert(buffer && data); // exception catch on stderr;
if(!buffer->full) {
printf("\nw-memcpy");
memcpy((buffer->buf + (buffer->wi * buffer->sz)), data, sizeof(buffer->sz));
printf("\n\t\tret\tdata\tcount\tfull\twi");
//printf("\n-------------------------------------------------\n");
printf("\nAdding data :\t%d\t%d\t%ld\t%d\t%ld", 0, *((int*)data), buffer->count, buffer->full, buffer->wi);
buffer->count++;
buffer->wi++;
buffer->wi %= buffer->max_count;
//if((buffer->wi == buffer->ri) && (buffer->count >= (buffer->max_count))) {
if(buffer->count >= buffer->max_count) {
printf("\n>>>Full for count = %ld, buffer-full = %d, index ri = %ld, wi=%ld",
buffer->count, buffer->full, buffer->ri, buffer->wi);
buffer->full = true;
}
return 0;
}
printf("\n>>>Dropping data. Buffer Full for count = %ld, buffer-full = %d, index ri = %ld, wi=%ld",
buffer->count, buffer->full, buffer->ri, buffer->wi);
//printf("\n%d\t%d\t%ld\t%d", -1, *((int *)data), buffer->count, buffer->full);
return -1;
}
int get_item(r_buffer_t *buffer, void *data) {
assert(buffer && data);
if(buffer->count > 0) {
printf("r-memcpy");
memcpy(data, (buffer->buf + (buffer->ri * buffer->sz)), buffer->sz);
memset((buffer->buf + (buffer->ri * buffer->sz)), 0, buffer->sz);
//printf("\n%d\t%d\t%ld\t%d\t%ld", 0, *((int*)data), buffer->count, buffer->full, buffer->ri);
buffer->count--;
buffer->ri++;
buffer->ri %= buffer->max_count;
buffer->full = (buffer->count >= buffer->max_count);
return 0;
}
//printf("\n%d\t%d\t%ld\t%d\t%ld", -1, *((int*)data), buffer->count, buffer->full, buffer->ri);
return -1;
}
void print_buffer(r_buffer_t *buffer) {
int i = 0;
printf("\n*************Buffer***********\n");
printf("\ncount = %ld, full = %d, ri = %ld, wi = %ld",
buffer->count, buffer->full, buffer->ri, buffer->wi);
printf("\n\t");
for(i =0; i < buffer->max_count; i++){
if(buffer->ri == i)
printf("R ");
else
printf(" ");
}
printf("\n\t");
for(i =0; i < buffer->max_count; i++)
printf("%d |", *(int *)(buffer->buf + i * buffer->sz));
printf("\n\t");
for(i =0; i < buffer->max_count; i++){
if(buffer->wi == i)
printf("W ");
else
printf(" ");
}
printf("\n*********************************\n");
}
int main() {
int i = 0;
int d1[] = {1,2,3,4,5,6,7,8,9,10};
int rd = 0;
int ret = 0;
r_buffer_t *buf = ring_buffer_init(5, sizeof(unsigned int));
print_buffer(buf);
printf("\nAdding 4 items to buffer");
for(i = 0; i < 4; i++) {
printf("\nCalling put for %d", d1[i]);
ret = put_item(buf, (void *)&d1[i]);
}
print_buffer(buf);
printf("\nRemoving and printing 2");
for(i = 0; i < 2; i++){
get_item(buf, &rd);
}
print_buffer(buf);
printf("\nAdding 4 more items to buffer");
for(i = 4; i < 9; i++) {
ret = put_item(buf, &d1[i]);
}
print_buffer(buf);
return 0;
}
The output makes no sense:
*************Buffer***********
count = 0, full = 0, ri = 0, wi = 0
R
0 |0 |0 |0 |0 |
W
*********************************
Adding 4 items to buffer
Calling put for 1
w-memcpy
ret data count full wi
Adding data : 0 1 0 0 0
Calling put for 2
w-memcpy
ret data count full wi
Adding data : 0 2 1 0 1
Calling put for 3
w-memcpy
ret data count full wi
Adding data : 0 3 2 0 2
Calling put for 4
w-memcpy
ret data count full wi
Adding data : 0 4 3 0 3
*************Buffer***********
count = 4, full = 0, ri = 0, wi = 4
R
1 |2 |3 |4 |5 |
W
*********************************
How is there an extra '5' in the buffer ?
memcpy((buffer->buf + (buffer->wi * buffer->sz)), data, sizeof(buffer->sz));
The above line in put_item()
would copy a number of sizeof(buffer->sz)
bytes from data
to (buffer->buf + (buffer->wi * buffer->sz))
.
The sizeof(buffer->sz)
equals to sizeof(ssize_t)
for the type of buffer->sz
is ssize_t
in type r_buffer_t
.
In many system, ssize_t
would be signed long
while size_t
would be unsigned long
, and they would be double the sizeof(int)
bytes in general. It is possible that the above code line actually copy double the sizeof(int)
bytes from the source to destination, then at the fourth call to put_item()
the fifth element in array d1[]
would always be copied into the ring buffer.
You might want to change it as the following line.
memcpy((buffer->buf + (buffer->wi * buffer->sz)), data, buffer->sz);