You are here: Foswiki>Notes Web>RingBuffer (29 Feb 2020, KoenMartens)Edit Attach
/*
 * Copyright (c) 2020, Koen Martens
 * 
 * License: public domain.
 *
 * Comes with no warranties.
 *
 */

#ifndef _RING_BUFFER_H
#define _RING_BUFFER_H

/*
 * Declare a static ring buffer variable. This defines a circular buffer that
 * can be used with the other macro's in this file.
 *
 * @param name: name of the static variable
 * @param type: type of the buffer elements
 * @param size: number of elements (usable elements is one less than this parameter)
 */
#define RING_BUFFER(name, type, size)  typedef struct ringBuffer_##name      {  \
    size_t start;                                                               \
    size_t end;                                                                 \
    type data[size];                                                            \
} name##_t;                                                                     \
static name##_t name = {0};

/*
 * Check whether ring buffer is full.
 *
 * @param buffer: ring buffer declared with RING_BUFFER
 * @returns: true if the buffer is full, false otherwise
 */
#define RING_BUFFER_FULL(buffer)      ( ((buffer.end + 1) %                     \
                        number_of_elements(buffer.data)) == buffer.start )

/*
 * Check whether ring buffer is empty.
 *
 * @param buffer: ring buffer
 */
#define RING_BUFFER_EMPTY(buffer)     ( buffer.end == buffer.start )

/*
 * Remove least-recently added item from ring buffer.
 *
 * @param buffer: ring buffer declared with RING_BUFFER
 */
#define RING_BUFFER_TAKE(buffer)      do {                                      \
    if (!RING_BUFFER_EMPTY(buffer))                                             \
    {                                                                           \
        buffer.start = (buffer.start + 1) % number_of_elements(buffer.data);    \
    }                                                                           \
} while(false)

/*
 * Add (copy) an element in the ring buffer (if not full)
 *
 * @param buffer: ring buffer declared with RING_BUFFER
 * @param element: element to copy into buffer
 */
#define RING_BUFFER_PUT(buffer, element)   do {                                 \
    if (!RING_BUFFER_FULL(buffer))                                              \
    {                                                                           \
        buffer.data[buffer.end] = element;                                      \
        buffer.end = (buffer.end + 1) % number_of_elements(buffer.data);        \
    }                                                                           \
} while(false)

/*
 * Advance the ring buffer last item index (if not full).
 *
 * This advances the ring buffer end index, and is intended to be used
 * in cases where the caller wants to set the element directly, such as:
 *
 * RING_BUFFER_NEW(buffer).foo = 42;
 * RING_BUFFER_ADVANCE(buffer)
 *
 * The above code could be written less efficiently as:
 *
 * myElementType element;
 * element.foo = 42;
 * RING_BUFFER_PUT(buffer, element);
 *
 * @param buffer: ring buffer declared with RING_BUFFER
 */
#define RING_BUFFER_ADVANCE(buffer)    do {                                     \
    if (!RING_BUFFER_FULL(buffer))                                              \
    {                                                                           \
        buffer.end = (buffer.end + 1) % number_of_elements(buffer.data);        \
    }                                                                           \
} while(false)

/*
 * Access the head of the ring buffer (the least recently added element).
 *
 * @param buffer: ring buffer declared with RING_BUFFER
 */
#define RING_BUFFER_HEAD(buffer)    buffer.data[buffer.start]

/*
 * Access the element that would be added by calling RING_BUFFER_PUT.
 * Care must be taken not to write to this element if the buffer is full.
 *
 * @param buffer: ring buffer declared with RING_BUFFER
 */
#define RING_BUFFER_NEW(buffer)    buffer.data[buffer.end]

/*
 * Reset ring buffer. After this, the ring buffer is empty.
 *
 * @param buffer: ring buffer declared with RING_BUFFER
 */
#define RING_BUFFER_RESET(buffer)  do {                                         \
    buffer.start = buffer.end = 0;                                              \
} while(false)

#endif /* _RING_BUFFER_H */

-- KoenMartens - 29 Feb 2020
Topic revision: r1 - 29 Feb 2020, KoenMartens
This site is powered by FoswikiCopyright © by the contributing authors. All material on this collaboration platform is the property of the contributing authors.
Ideas, requests, problems regarding Foswiki? Send feedback