Search code examples
ccircular-buffercircular-list

Using circular list with variable datatypes in c


I searched the net for over two days to make a circular list that could be used for different data types. I use c language, so I mean I am looking for some code that can manage buffers with char, short, int, signed, or unsigned definitions. what I have found more useful is these 2 links

Embedded artistry

StackOverflow

The problem I have is that I am working on embedded boards with an external ram and since I do have not enough memory inside the processor chip, I have to use external memory, and in this case, malloc function would not work correctly.

So I am looking for some code or enhancing the embedded artistry code to be flexible to different data types.

What I had done with no good result was to change this code into two parts. one for char type and one for short type.

struct circular_buf_t {
    uint8_t *buffer;
    size_t head;
    size_t tail;
    size_t max; // of the buffer
    bool full;
};

struct circular_buf_short_t {
    void *buffer;
    size_t head;
    size_t tail;
    size_t max; // of the buffer
    bool full;
};

cbuf_handle_t circular_buf_init(uint8_t *buffer, size_t size) {
    cbuf_handle_t cbuf = malloc(sizeof(circular_buf_t));
    cbuf->buffer = buffer;
    cbuf->max = size;
    circular_buf_reset(cbuf);
    return cbuf;
}

cbuf_handle_short_t circular_buf_short_init(uint16_t *buffer, size_t size) {
    cbuf_handle_short_t cbuf = malloc(sizeof(cbuf_handle_short_t));
    circular_buf_short_reset(cbuf);
    cbuf->buffer = buffer;
    cbuf->max = size;
    return cbuf;
}

int circular_buf_try_put(cbuf_handle_t me, uint8_t data) {
    int r = -1;
    if (!circular_buf_full(me)) {
        me->buffer[me->head] = data;
        advance_head_pointer(me);
        r = 0;
    }
    return r;
}

int circular_buf_short_try_put(cbuf_handle_short_t me, uint16_t data) {
    int r = -1;
    if (!circular_buf_short_full(me)) {
        me->buffer[me->head] = data;
        advance_head_pointer_short(me);
        r = 0;
    }
    return r;
}

Solution

  • For your purpose, you could use a generic circular buffer structure that you initialize with an array of the type needed (eg: 8, 16 or 32 bit integers) along with the element size and element count.

    The ancillary functions work the same for all types, and the get and put functions handle the specific type with a test.

    If you know the type of element for a given buffer, you can use the specific get/put function.

    Here is a sample implementation:

    #include <stdbool.h>
    #include <stdint.h>
    #include <stdlib.h>
    
    typedef struct circular_buf_t {
        void *buffer;
        size_t head;  // you can make these uint16_t to save memory
        size_t tail;
        size_t element_count;   // number of elements in the buffer
        uint8_t element_size;
        bool full;
    } circular_buf_t;
    
    circular_buf_t *circular_buf_reset(circular_buf_t *cbuf) {
        cbuf->head = cbuf->tail = 0;
        cbuf->full = false;
        return cbuf;
    }
    
    circular_buf_t *circular_buf_init(void *buffer, size_t element_size, size_t element_count) {
        if (element_size == 1 || element_size == 2 || element_size == 4) {
            circular_buf_t *cbuf = malloc(sizeof(*cbuf));
            cbuf->buffer = buffer;
            cbuf->element_count = element_count;
            cbuf->element_size = (uint8_t)element_size;
            return circular_buf_reset(cbuf);
        } else {
            return NULL;
        }
    }
    
    bool circular_buf_full(circular_buf_t *me) {
        return me->full;
    }
    
    bool circular_buf_empty(circular_buf_t *me) {
        return me->head == me->tail && !me->full;
    }
    
    int circular_buf_advance_head_pointer(circular_buf_t *me) {
        if (++me->head == me->element_count)
            me->head = 0;
        me->full = (me->head == me->tail);
        return 0;
    }
    
    int circular_buf_advance_tail_pointer(circular_buf_t *me) {
        if (++me->tail == me->element_count)
            me->tail = 0;
        me->full = 0;
        return 0;
    }
    
    int circular_buf_try_put(circular_buf_t *me, int32_t data) {
        if (circular_buf_full(me)) {
            return -1;
        } else {
            switch (me->element_size) {
            case 1: ((uint8_t  *)me->buffer)[me->head] = (uint8_t)data; break;
            case 2: ((uint16_t *)me->buffer)[me->head] = (uint16_t)data; break;
            case 4: ((int32_t  *)me->buffer)[me->head] = data; break;
            }
            return circular_buf_advance_head_pointer(me);
        }
    }
    
    int circular_buf_try_put8(circular_buf_t *me, uint8_t data) {
        if (circular_buf_full(me) || me->element_size != sizeof(data)) {
            return -1;
        } else {
            ((uint8_t *)me->buffer)[me->head] = data;
            return circular_buf_advance_head_pointer(me);
        }
    }
    
    int circular_buf_try_put16(circular_buf_t *me, uint16_t data) {
        if (circular_buf_full(me) || me->element_size != sizeof(data)) {
            return -1;
        } else {
            ((uint16_t *)me->buffer)[me->head] = data;
            return circular_buf_advance_head_pointer(me);
        }
    }
    
    int circular_buf_try_put32(circular_buf_t *me, int32_t data) {
        if (circular_buf_full(me) || me->element_size != sizeof(data)) {
            return -1;
        } else {
            ((int32_t *)me->buffer)[me->head] = data;
            return circular_buf_advance_head_pointer(me);
        }
    }
    
    int32_t circular_buf_get(circular_buf_t *me) {
        int32_t res = -1;
        if (!circular_buf_empty(me)) {
            switch (me->element_size) {
            case 1: res = ((uint8_t  *)me->buffer)[me->head]; break;
            case 2: res = ((uint16_t *)me->buffer)[me->head]; break;
            case 4: res = ((int32_t  *)me->buffer)[me->head]; break;
            }
            circular_buf_advance_tail_pointer(me);
        }
        return res;
    }
    
    int32_t circular_buf_try_get8(circular_buf_t *me) {
        int32_t res = -1;
        if (!circular_buf_empty(me) && me->element_size == 1) {
            res = ((uint8_t *)me->buffer)[me->tail];
            circular_buf_advance_tail_pointer(me);
        }
        return res;
    }
    
    int32_t circular_buf_try_get16(circular_buf_t *me) {
        int32_t res = -1;
        if (!circular_buf_empty(me) && me->element_size == 2) {
            res = ((uint16_t *)me->buffer)[me->tail];
            circular_buf_advance_tail_pointer(me);
        }
        return res;
    }
    
    int32_t circular_buf_try_get32(circular_buf_t *me) {
        int32_t res = -1;
        if (!circular_buf_empty(me) && me->element_size == 4) {
            res = ((int32_t *)me->buffer)[me->tail];
            circular_buf_advance_tail_pointer(me);
        }
        return res;
    }