CYCLIC

Description

Manages an already allocated buffer of uint8_t elements to perform a cyclic (also called circular or ring) buffer access to memory as if both buffer ends were connected. Please see the Wikipedia article on circular buffers for detailed information about this data structure.

The typical use case is buffering of data streams where a consumer reads data at a different pace than the producer. Elements are inserted to (Data IN) or extracted from (Data OUT) the CYCLIC instance.

Input sources (producers) are single octets, buffers, null-terminated strings, string with variable arguments (see VARIANT) and octets provided by streams (see STREAM). Output destinations (consumers) are single octets, buffers or streams. Produced input data can be discarded, peeked, overwritten or replaced before consumed. The figure below depicts producers, consumers and operations on buffered data.

_images/cyclic.drawio.svg

Note

Inserted data may overflow the available buffer capacity, overwriting older values not yet consumed; this is especially true when reading from a STREAM. That’s not a bug, but a feature™. The user must employ a buffer with an appropriate size for the intended application to avoid this data structure feature.

There are three CYCLIC buffer usage metrics:

Capacity

Total amount of uint8_t buffer elements. See CYCLIC_Capacity().

Elements

Number of inserted elements waiting to be consumed. See CYCLIC_Elements().

Available

Free space to insert further elements.

On a given time, the actual uint8_t linear memory buffer usage may look as in the following figure:

_images/cyclic_buffer.drawio.svg

Current IN and OUT buffer pointers are managed by the CYCLIC instance and are opaque to the user.

API guide

Status functions

Data IN

Data OUT

Peek, overwrite and replace contents

Design and development status

Feature-complete.

Changelog

Version

Date*

Author

Comment

1.0.0

2022.9.7

sgermino

Initial release.

* Date format is Year.Month.Day.

API reference

CYCLIC_IN_FromParsedString(_c, _m, _s, ...)

Automatically instances the parameters list and parameter count required to call CYCLIC_IN_FromParsedStringArgs().

Parameters
struct CYCLIC

The user should treat this as an opaque structure. No member should be directly accessed or modified.

void CYCLIC_Init(struct CYCLIC *const C, uint8_t *const Buffer, const uint32_t Capacity)

Initializes a CYCLIC instance.

Parameters
  • Buffer – An already allocated uint8_t buffer.

  • Capacity – Buffer capacity, in uint8_t elements.

Warning

Buffer capacity must be a power of two. This condition is asserted.

void CYCLIC_Reset(struct CYCLIC *const C)

Resets the state of a CYCLIC instance, keeping the uint8_t buffer pointer, its declared capacity and usage statistics.

void CYCLIC_Discard(struct CYCLIC *const C)

Discard elements inserted and not yet consumed.

uint32_t CYCLIC_Capacity(struct CYCLIC *const C)

Returns buffer capacity passed when initializing this CYCLIC instance.

Returns

Number of uint8_t elements, as declared at initialization.

uint32_t CYCLIC_Elements(struct CYCLIC *const C)

Return elements inserted and not yet consumed.

Returns

Number of uint8_t elements available to consume.

uint32_t CYCLIC_Available(struct CYCLIC *const C)

Returns space available to insert elements.

Returns

Number of uint8_t elements available.

void CYCLIC_IN_FromOctet(struct CYCLIC *const C, const uint8_t Octet)

[Data IN] Inserts a single uint8_t element.

Parameters
  • Octet – Element value to insert.

void CYCLIC_IN_FromBuffer(struct CYCLIC *const C, const uint8_t *const Data, const uint32_t Count)

[Data IN] Inserts an array of uint8_t elements.

Parameters
  • Data – Array of uint8_t elements.

  • Count – Number of uint8_t elements to insert.

void CYCLIC_IN_FromString(struct CYCLIC *const C, const char *const Str, const size_t MaxLen)

[Data IN] Inserts a null-terminated string.

Parameters
  • str – Pointer to a null-terminated string.

  • MaxLen – The null termination in str must be found before processing this maximum amount of char elements. This condition is asserted.

void CYCLIC_IN_FromParsedStringArgs(struct CYCLIC *const C, const size_t MaxOctets, const char *const Str, struct VARIANT *const ArgValues, const uint32_t ArgCount)

[Data IN] Inserts a null-terminated string with VARIANT argument substitution.

Parameters
  • Str – Pointer to a null-terminated string with indexed argument placeholders.

  • MaxOctets – The null termination in Str must be found before reaching MaxOctets; this condition is asserted.

  • ArgValues – Array of VARIANT elements whose values will replace the argument placeholders in Str.

  • ArgCount – Number of indexed elements in the VARIANT array.

void CYCLIC_IN_FromStream(struct CYCLIC *const C, struct STREAM *const S)

[Data IN] Reads a STREAM and insert each uint8_t element until the STREAM signals an end of file (EOF).

Parameters
  • SSTREAM to read and insert elements until EOF.

uint8_t CYCLIC_OUT_ToOctet(struct CYCLIC *const C)

[Data OUT] Consumes an octet and returns its value. The caller must have confirmed with CYCLIC_Elements() that there is at least one octet to consume; this condition is asserted.

Returns

The octet value.

void CYCLIC_OUT_ToBuffer(struct CYCLIC *const C, uint8_t *const Data, const uint32_t Count)

[Data OUT] Consumes a given amount of octets by storing them in an uint8_t array already allocated by the user. The user must have confirmed with CYCLIC_Elements() that the required amount of elements is available; this condition is asserted.

Parameters
  • Data – Already allocated uint8_t array.

  • Count – Number of octets to consume by storing them in the array.

void CYCLIC_OUT_ToStream(struct CYCLIC *const C, struct STREAM *const S)

[Data OUT] Tries to consume all elements transferring them to a STREAM. The user must have confirmed that there is at least one element to consume; this condition is asserted. After the call, the user must not assume all elements were effectively consumed. The operation may stop prematurely if the stream is unable to receive more items. Depending on the actual STREAM implementation, the user may need to wait some time and repeatedly call this function until all elements are consumed. See CYCLIC_Elements() and STREAM_IN_Count().

Below is an usage example:

// "s" is an instance of a hypothetical STREAM implementation to handle a
// particular hardware UART. Suppose this UART has a limited baud rate and
// TX buffering, so it may not send all cyclic elements at once. That is why
// CYCLIC_OUT_ToStream() may return before consuming every element.
//
// "retries" is the maximum number of times to keep calling
// CYCLIC_OUT_ToStream() after a STREAM EOF.

uint32_t retries = 10;

while (retries --)
{
    // Consume elements from the cyclic instance "c" and send them to the
    // STREAM "s".
    CYCLIC_OUT_ToStream (&c, &s);
    // Call returned.
    
    // Check for STREAM EOF
    if (!STREAM_EOF (&s))
    {
        // Not an EOF. CYCLIC_OUT_ToStream should have consumed and sent all
        // elements.
        // TEST: Assert that all elements have been consumed.
        BOARD_AssertState (CYCLIC_Elements(&c) == 0);
        // Do not retry anymore.
        break;
    }
    else
    {
        // STREAM EOF. Keep trying.
    }
}

// TEST: invalid states.
// 1) Not a STREAM EOF and no more retries.
// 2) STREAM EOF and all elements consumed.
BOARD_AssertState (!STREAM_EOF(&s) && !retries);
BOARD_AssertState (STREAM_EOF(&s) && CYCLIC_Elements(&c) == 0);

// There are two valid states:
// A) STREAM EOF, run out of retries and not all elements consumed.
// B) not a STREAM EOF and all elements consumed.
Parameters
  • SSTREAM instance to transfer consumed elements. The STREAM may abort the operation at any time.

uint8_t CYCLIC_Peek(struct CYCLIC *const C, const uint32_t Index)

Peeks an inserted element before consuming it. The user must check that there are enough inserted elements for the requested Index; otherwise, the return value will be invalid.

Parameters
  • Index – A Zero-based Index selects which element to peek; zero is the first element consumed.

Returns

A copy of the element.

void CYCLIC_PeekToBuffer(struct CYCLIC *const C, const uint32_t Index, uint8_t *const Data, const uint32_t Count)

Peeks inserted elements before consuming them by copying their values to an already allocated array of uint8_t. The user must check that there are enough inserted elements for the requested Index plus Count; otherwise, the return value will be invalid.

Parameters
  • Index – A Zero-based Index selects from which element it starts to peek; zero is the first element consumed.

  • Datauint8_t array to copy element values.

  • Count – Number of elements to peek, starting from Index.

void CYCLIC_PeekToCyclic(struct CYCLIC *const C, const uint32_t Index, const uint32_t Count, struct CYCLIC *const D)

Peek inserted elements before consuming them by inserting a copy of their values to another c:struct:CYCLIC instance. The user must check that there are enough inserted elements for the requested Index plus Count; otherwise, the return value will be invalid.

Parameters
  • Index – A Zero-based Index selects from which element it starts to peek; zero is the first element consumed.

  • Count – Number of elements to peek, starting from Index.

  • D – c:struct:CYCLIC instance to store a copy of the elements.

void CYCLIC_Overwrite(struct CYCLIC *const C, const uint32_t Index, const uint8_t Octet)

Overwrites an inserted element. The user must check that there are enough inserted elements for the requested Index; this condition is asserted.

Parameters
  • Index – A Zero-based Index selects which element to overwrite; zero is the first element consumed.

  • Octet – Overwrite the inserted element with this value.

void CYCLIC_OverwriteFromBuffer(struct CYCLIC *const C, const uint32_t Index, const uint8_t *const Data, const uint32_t Count)

Overwrites inserted elements by replacing their values with the ones in an array of uint8_t. The user must check that there are enough inserted elements for the requested Index plus Count; this condition is asserted.

Parameters
  • Index – A Zero-based Index selects from which element it starts to overwrite; zero is the first element consumed.

  • Data – Overwrite the inserted elements with the ones from this uint8_t array.

  • Count – Number of elements to overwrite starting from Index.

void CYCLIC_Replace(struct CYCLIC *const C, const uint32_t Index, const uint32_t Count, const uint8_t Match, const uint8_t Replace)

Replaces an inserted element that is equal to a given value. The user must check that there are enough inserted elements for the requested Index plus Count; this condition is asserted.

Parameters
  • Index – A Zero-based Index selects from which element it starts to check; zero is the first element consumed.

  • Count – Number of elements to check, starting from Index.

  • Match – Value to check for equality.

  • Replace – If equal, replace with this value.

void CYCLIC_ReplaceAll(struct CYCLIC *const C, const uint8_t Match, const uint8_t Replace)

Replaces all inserted elements that are equal to a given value. The user must check that there is at least one element; this condition is asserted.

Parameters
  • Match – Value to check for equality.

  • Replace – If equal, replace with this value.

uint32_t CYCLIC_IN_Count(struct CYCLIC *const C)

Returns number of insertions from the last [Data IN] operation.

Returns

Number of insertions.

uint32_t CYCLIC_OUT_Count(struct CYCLIC *const C)

Returns number of consumptions from the last [Data OUT] operation.

Returns

Number of consumptions.