Segmentation Fault with Circular Buffer Implemenation

76 views Asked by At

I am implementing a circular buffer but I am getting a confusing segmentation fault when I am testing my implementation. The code seems to produce a segfault right after I call a function to print out the contents of the circular buffer structure. This confuses me since I would have expected a segfault I am writing or reading from the buffer, but instead it seems to give the error right after returning from my print_cbuf() function.

Here is my circular buffer implementation:

#ifndef __CBUF_H__
#define __CBUF_H__

#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include <memory.h>

typedef struct
{
    char* buffer;       // buffer start
    char* write;        // write pointer in buffer
    char* read;         // read pointer in buffer
    size_t size;        // element size
    size_t curr_len;    // current lenth of circular buffer
    size_t max_len;     // maximum capacity of circular buffer
} cbuf_t;

/*
* new_circular_buffer()
* Initialize and returns a new circular buffer instance.
*/
cbuf_t* new_circular_buffer(size_t capacity, size_t size)
{
    cbuf_t* cbuf = (cbuf_t*) malloc(sizeof(cbuf_t));
    if (NULL == cbuf)
        return NULL;

    char* buffer = (char*) malloc(capacity * size);
    if (NULL == buffer)
    {
        free(cbuf);
        return NULL;
    }

    cbuf->buffer = buffer;
    cbuf->write = cbuf->buffer;
    cbuf->read = cbuf->buffer;
    cbuf->size = cbuf->size;
    cbuf->curr_len = 0;
    cbuf->max_len = capacity;

    return cbuf;
}

/*
* circular_buffer_free()
* Frees all memory the circular buffer has dynamically allocated.
*/
void circular_buffer_free(cbuf_t* cbuf)
{
    free(cbuf->buffer);
    free(cbuf);
}

/*
* empty()
* Returns True if the circular buffer is empty, False otherwise
*/
bool cbuf_empty(cbuf_t* cbuf)
{
    return (bool) (0 == cbuf->curr_len);
}

/* 
* full()
* Returns True if the circular buffer is full, False otherwise
*/
bool cbuf_full(cbuf_t* cbuf)
{
    return (bool) (cbuf->curr_len == cbuf->max_len);
}

/* 
* push()
* Pushes new data into the circular buffer. This will not overwrite data if buffer is full.
*/
bool cbuf_push(cbuf_t* cbuf, void* item)
{
    if (cbuf_full(cbuf))
    {
        return false;
    }

    memcpy(cbuf->write, item, cbuf->size);
    cbuf->write = cbuf->write + cbuf->size;
    if (cbuf->write == cbuf->buffer + (cbuf->max_len - 1) * sizeof(item))
    {
        cbuf->write = cbuf->buffer;
    }
    cbuf->curr_len++;
    return true;
}

/*
* pop()
* Pop data at read pointer from the circular buffer and increments read pointer to next position
*/
void* cbuf_pop(cbuf_t* cbuf)
{
    if (cbuf_empty(cbuf)) 
    {
        return NULL;
    }

    void* item;
    memcpy(item, cbuf->read, cbuf->size);
    cbuf->read = cbuf->read + cbuf->size;
    if (cbuf->read == cbuf->buffer + (cbuf->max_len - 1) * sizeof(item))
    {
        cbuf->read = cbuf->buffer;
    }
    cbuf->curr_len--;

    return item;
}

/*
* size()
* Returns the current size of the circular buffer
*/
size_t cbuf_size(cbuf_t* cbuf)
{
    return cbuf->curr_len;
}

/*
* peek()
* Peeks the data at the read pointer without incrementing the read pointer.
*/
void* cbuf_peek(cbuf_t* cbuf)
{
    return cbuf->read;
}

/*
* reset()
* Resets all fields in the circular buffer instance and clears the buffer.
*/
void cbuf_reset(cbuf_t* cbuf)
{
    memset(cbuf->buffer, 0, cbuf->max_len * cbuf->size);
    cbuf->write = cbuf->buffer;
    cbuf->read = cbuf->buffer;
    cbuf->curr_len = 0;
}

#endif

And here is where I test out the implementation:

#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <assert.h>
#include <stdbool.h>
#include <memory.h>
#include "cbuf.h"

void test_circular_buffer(void);
void print_cbuf(cbuf_t* cbuf);


void test_circular_buffer(void) 
{
    printf("Starting circular buffer API tests...\n");

    cbuf_t* cbuf = new_circular_buffer(64, sizeof(char));
    if (cbuf == NULL)
    {
        printf("Could not create circular buffer.\n");
        return;
    }
    printf("=== Circular buffer created. ===\n");
    print_cbuf(cbuf); // SEG FAULT HERE ??
    // CODE AFTER THIS POINT DOES NOT EXECUTE ?

    // Empty Test
    printf("cbuf size: %d" , (int) cbuf->curr_len);
    if (cbuf_empty(cbuf) == true) {
        printf("Empty Test passed!");
    } else {
        printf("Empty Test failed!");
        return;
    }
    
    // Push 1 element
    char* data;
    *data = 13;
    assert(cbuf_push(cbuf, data));
    printf("=== Data pushed into circular buffer. ===\n");

    printf("SUCCESS! All test cases passed!\n");
    return;
}

void print_cbuf(cbuf_t* cbuf)
{
    printf("Circular Buffer:\n");
    printf("Buffer: ");
    for (size_t i = 0; i < cbuf->max_len; i++)
    {
        printf("%d, " , *(char *)(cbuf->buffer+i));
    }
    printf("\n");
    printf("Buffer Start Address: %p\n" , cbuf->buffer);
    printf("Buffer Write Address: %p\n", cbuf->write);
    printf("Buffer Read Address: %p\n", cbuf->read);
    printf("Buffer Current Size: %d/%d\n", (int) cbuf->curr_len, (int) cbuf->max_len);
    return;
}

int main(void)
{
    test_circular_buffer();
    return 0;
}

Here is the error I receive:

Starting circular buffer API tests...
=== Circular buffer created. ===
Circular Buffer:
Buffer: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
Buffer Start Address: 0x149606750
Buffer Write Address: 0x149606750
Buffer Read Address: 0x149606750
Buffer Current Size: 0/64
zsh: segmentation fault  ./test_cbuf

If someone could shed some light on what is going on here as I am utterly confused and also give feedback on my circular buffer implementation that would be much appreciated. Thanks.

0

There are 0 answers