MultiReaderBuffer

EmbeddedUtilities/MultiReaderBuffer.h

#include “EmbeddedUtilities/MultiReaderBuffer.h”

Defines

MULTI_READER_BUFFER_SIZE(word_size, max_elements, max_readers)

Expands to the number of bytes required to create a circular buffer with the provided parameters.

MULTI_READER_BUFFER_SIZE

This macro should be used to obtain the correct number of bytes to be allocated for creating a circular buffer. One buffer position is “wasted” for distinguishing whether the buffer is full or empty.

Parameters
  • word_size: The size in byte of the individual data items that should be managed in the buffer

  • max_elements: The maximum number of elements that the buffer should be able to hold at once

  • max_readers: The maximum number of buffer readers that are allowed to exist in parallel

Typedefs

typedef struct Buffer Buffer
typedef struct MultiReaderBuffer MultiReaderBuffer

Enums

enum [anonymous]

Values:

enumerator BUFFER_UNDERRUN_EXCEPTION

More items than existent have been tried to be read by a reader.

enumerator BUFFER_OVERRUN_EXCEPTION

More items have been written than could have been read by a reader, i.e. unread items have been overwritten.

enumerator BUFFER_NO_FREE_READER_SLOTS_EXCEPTION

The number of reader slots is exhausted.

enumerator BUFFER_INVALID_READER_EXCEPTION

It has been tried to execute an operation with an invalid reader.

enum BufferReaderState

Indicates the different states a reader can have.

A reader is invalid as long as getNewBufferReaderDescriptor returns a descriptor for this particular reader to use for read operations.

Values:

enumerator BUFFER_READER_VALID

A reader is valid and can be used for read operations.

enumerator BUFFER_READER_OVERRUN

A valid read pointer has been overwritten by the write pointer, i.e. elements are lost.

enumerator BUFFER_READER_INVALID

A reader is invalid.

Functions

void pushToBuffer(Buffer *self, const void *data)

Writes new data into the buffer.

As with the nature of a circular buffer, the buffer space virtually never “ends” since when space is used up the buffer is written from the start again, overwriting the oldest entries.

Parameters
  • self: A pointer to the circular buffer, addressed by its generalized type

  • data: The data that should be written into the buffer

uint8_t getNewBufferReaderDescriptor(Buffer *self)

Returns a descriptor for a new buffer reader.

The descriptor shall be used with functions deleteBufferReaderDescriptor, readableItemExistsForReader, popFromBufferWithReader and peekAtBufferWithReader.

Return

A descriptor for the newly allocated reader, represented by an integer

Parameters
  • self: A pointer to the circular buffer, addressed by its generalized type

Exceptions
  • BUFFER_NO_FREE_READER_SLOTS_EXCEPTION: An exception is thrown when more readers have been tried to be allocated than are allowed; use deleteBufferReaderDescriptor to free unused readers.

void deleteBufferReaderDescriptor(Buffer *self, uint8_t reader_descriptor)

Flags the provided reader descriptor as invalid.

Parameters
  • self: A pointer to the circular buffer, addressed by its generalized type

  • reader_descriptor: The descriptor of the reader with which the data should be retrieved

Exceptions
  • BUFFER_INVALID_READER_EXCEPTION: An exception is thrown if the descriptor does not correspond to an actual reader slot

bool readableItemExistsForReader(const Buffer *self, uint8_t reader_descriptor)

Returns whether there is at least one unread data item left that could be retrieved via popFromBufferWithReader or peekAtBufferWithReader.

Return

true if the reader descriptor is valid and there are still elements left to be read; false if the reader descriptor is invalid or there are no elements left to be read

Parameters
  • self: A pointer to the circular buffer, addressed by its generalized type

  • reader_descriptor: The descriptor of the reader with which the data should be retrieved

const void *popFromBufferWithReader(Buffer *self, uint8_t reader_descriptor)

Reads from the buffer including “removal” of the element that has been read.

The element is returned by reference, which needs to be casted into the actual data type (that the user must be aware of) and then dereferenced to retrieve the data item’s value. The reader pointer will have proceeded by one data word after this function has been called.

If the reader pointer has been overrun by the write pointer, the reader pointer will be repositioned to the oldest entry in the buffer before an exception will be thrown. Therefore, a subsequent read operation will succeed, given no elements have been written in the meantime.

Return

An “untyped” pointer to the current element the reader pointer is positioned at

Parameters
  • self: A pointer to the circular buffer, addressed by its generalized type

  • reader_descriptor: The descriptor of the reader with which the data should be retrieved

Exceptions
  • BUFFER_INVALID_READER_EXCEPTION: An exception is thrown if the reader descriptor is invalid

  • BUFFER_OVERRUN_EXCEPTION: An exception is thrown if the reader has been overrun by the write pointer, i.e. elements have been lost

  • BUFFER_UNDERRUN_EXCEPTION: An exception is thrown if there are no elements left to be read for this reader, i.e. the buffer is “empty”

const void *peekAtBufferWithReader(const Buffer *self, uint8_t reader_descriptor)

Reads from the buffer without “removing” the element that has been read.

The element is returned by reference, which needs to be casted into the actual data type (that the user must be aware of) and then dereferenced to retrieve the data item’s value. The reader pointer will not have proceeded after this function has been called.

If the reader pointer has been overrun by the write pointer, the reader pointer will be repositioned to the oldest entry in the buffer before an exception will be thrown. Therefore, a subsequent read operation will succeed, given no elements have been written in the meantime.

Return

An “untyped” pointer to the current element the reader pointer is positioned at

Parameters
  • self: A pointer to the circular buffer, addressed by its generalized type

  • reader_descriptor: The descriptor of the reader with which the data should be retrieved

Exceptions
  • BUFFER_INVALID_READER_EXCEPTION: An exception is thrown if the reader descriptor is invalid

  • BUFFER_OVERRUN_EXCEPTION: An exception is thrown if the reader has been overrun by the write pointer, i.e. elements have been lost

  • BUFFER_UNDERRUN_EXCEPTION: An exception is thrown if there are no elements left to be read for this reader, i.e. the buffer is “empty”

void initMultiReaderBuffer(MultiReaderBuffer *self, uint8_t word_size, size_t max_elements, size_t max_readers)

Initializes a multi reader buffer.

This function creates all the necessary structures (see MultiReaderBuffer) to use the provided memory as a multi reader buffer and needs to be called first in order to use all the other functions provided by this header.

The memory passed via the first parameter needs to be large enough to hold the entire multi reader buffer given the set of customization parameters, i.e. the memory should be created using the MULTI_READER_BUFFER_SIZE with the same set of parameters.

Parameters
  • self: Pointer to the memory that should be used for the circular buffer

  • word_size: The size in byte of the individual data items that should be managed in the buffer

  • max_elements: The maximum number of elements that the buffer should be able to hold at once

  • max_readers: The maximum number of buffer readers that are allowed to exist in parallel

Variables

enum [anonymous] MultiReaderBufferException
struct MultiReaderBuffer
#include <MultiReaderBuffer.h>

Defines the structure of the circular buffer implementation.

This circular buffer implementation allows to use multiple buffer readers, to provide a mechanism for implementing 1-to-n producer-consumer relationships in an efficient manner as often needed in the realm of embedded systems. It is utilized as part of the data processing pipeline implemented by the edge device.

Public Members

uint8_t word_size_in_byte

Stores the size in byte of the individual data items stored.

size_t max_elements

Stores the maximum number of elements.

size_t max_readers

Stores the maximum number of readers allowed in parallel.

void *start

Stores the pointer to the start position of the buffer.

void *write

Stores the writer pointer, which always points to the next position it would write to.

void **readers

Holds the array of readers, where each reader is represented by a pointer into the buffer memory.

BufferReaderState *reader_state_indicators

For each reader slot this array stores the current reader state (see BufferReaderState)