0
\$\begingroup\$

Hey,

i just finished work on a Element based FiFo Buffer in C.

Layout of this question:

  • Intro
  • .h File
  • .c File
  • Usage Example
  • Intended Use-cases
  • Question

.h File

#ifndef Std_FiFo_LINEARBUFFER
#define Std_FiFo_LINEARBUFFER

#include <stdint.h> //Include for the UIntXX_t types

#ifdef __cplusplus
extern "C"
{
#endif

    /// <summary>
    /// Typedef used as handle to the Linear buffer entity used
    /// </summary>
    typedef volatile uint32_t t_Std_FiFo_LinearBuffer_BufferHandle;

    /// <summary>
    /// Typedef is used as a Handle for a single Element in the Buffer
    /// </summary>
    typedef volatile uint32_t t_Std_FiFo_LinearBuffer_ElementHandle;

    /// <summary>
    /// Used as nullpointer
    /// </summary>
    extern t_Std_FiFo_LinearBuffer_BufferHandle
        c_Std_FiFo_LinearBuffer_NullBufferHandle;

    /// <summary>
    /// Used as nullpointer
    /// </summary>
    extern t_Std_FiFo_LinearBuffer_ElementHandle
        c_Std_FiFo_LinearBuffer_NullElementHandle;

    /// <summary>
    /// Used as Nullpointer for Data elements or data fields in the buffer
    /// </summary>
    extern uint32_t* const c_Std_FiFo_LinearBuffer_NullDataPointer;

    /// <summary>
    /// Call to initiliaze the Buffer struct
    ///
    /// Configures the initial state of the object handle and the
    /// passed raw buffer. Must be called prior to any other calls.
    /// </summary>
    /// <param name="HandleForStruct">The pointer to the struct to
    /// initialize</param> <returns>1 if possible, 0 if not</returns>
    uint8_t Std_FiFo_LinearBuffer_Initiliaze(
        t_Std_FiFo_LinearBuffer_BufferHandle* const HandleForStruct);

    /// <summary>
    /// Call to invalidate the Buffer struct
    ///
    /// The pointer passed is invalidated aswell (0)
    /// </summary>
    /// <param name="HandleForStruct">The pointer to the struct to
    /// initialize</param> <returns>1 if possible, 0 if not</returns>
    uint8_t Std_FiFo_LinearBuffer_Invalidate(
        t_Std_FiFo_LinearBuffer_BufferHandle* const HandleForStruct);

    /// <summary>
    /// Checks if a number of data can be put into the buffer
    /// </summary>
    /// <param name="HandleForStruct">The pointer to the fifo struct</param>
    /// <param name="NumberOfU32s">The Number of U32s that should be put</param>
    /// <returns>0x01 if can put, 0x00 if not</returns>
    uint8_t Std_FiFo_LinearBuffer_CanPut(
        t_Std_FiFo_LinearBuffer_BufferHandle HandleForStruct,
        uint32_t NumberOfU32s);

    /// <summary>
    /// Checks if one can remove data from the buffer
    /// Does not allow to take locked head
    /// </summary>
    /// <param name="HandleForStruct">The pointer to the fifo struct</param>
    /// <returns>0x01 if can take, 0x00 if not</returns>
    uint8_t Std_FiFo_LinearBuffer_CanTake(
        t_Std_FiFo_LinearBuffer_BufferHandle HandleForStruct);

    /// <summary>
    /// Removes the first element from the buffer
    /// without copying the data
    /// </summary>
    /// <param name="HandleForStruct">The pointer to the fifo struct</param>
    /// <returns>0x01 if possible, 0x00 if not</returns>
    uint8_t Std_FiFo_LinearBuffer_Pop(
        t_Std_FiFo_LinearBuffer_BufferHandle HandleForStruct);

    /// <summary>
    /// Takes data from the buffer and removes the first element
    /// </summary>
    /// <param name="HandleForStruct">The pointer to the fifo struct</param>
    /// <param name="BufferToCopyTo">THe buffer to copy the data to</param>
    /// <param name="NumberOfU32s">The Number of U32s that should be taken. If 0,
    /// then entire buffer is copied</param> <returns>0x01 if possible, 0x00 if
    /// not</returns>
    uint8_t Std_FiFo_LinearBuffer_Take(
        t_Std_FiFo_LinearBuffer_BufferHandle HandleForStruct,
        uint32_t* const BufferToCopyTo, uint32_t NumberOfU32s);

    /// <summary>
    /// Checks if a number of data can be put into the buffer
    /// and the puts them into the buffer
    /// </summary>
    /// <param name="HandleForStruct">The pointer to the fifo struct</param>
    /// <param name="BufferToCopyFrom">THe buffer to copy the data from</param>
    /// <param name="NumberOfU32s">The Number of U32s that should be put</param>
    /// <returns>0x01 if possible, 0x00 if not</returns>
    uint8_t Std_FiFo_LinearBuffer_Put(
        t_Std_FiFo_LinearBuffer_BufferHandle HandleForStruct,
        uint32_t* const BufferToCopyFrom, uint32_t NumberOfU32s);
    /// <summary>
    /// Checks if a number of data can be put into the buffer
    /// and the puts them into the buffer
    ///
    /// Puts the Item to the Top of the buffer
    /// </summary>
    /// <param name="HandleForStruct">The pointer to the fifo struct</param>
    /// <param name="BufferToCopyFrom">THe buffer to copy the data from</param>
    /// <param name="NumberOfU32s">The Number of U32s that should be put</param>
    /// <returns>0x01 if possible, 0x00 if not</returns>
    uint8_t Std_FiFo_LinearBuffer_PutTop(
        t_Std_FiFo_LinearBuffer_BufferHandle HandleForStruct,
        uint32_t* const BufferToCopyFrom, uint32_t NumberOfU32s);

    /// <summary>
    /// Takes data from the buffer, but does not remove the element
    /// </summary>
    /// <param name="HandleForStruct">The pointer to the fifo struct</param>
    /// <param name="BufferToCopyTo">THe buffer to copy the data to</param>
    /// <param name="NumberOfU32s">The Number of U32s that should be taken. If 0,
    /// then entire buffer is copied</param> <returns>0x01 if possible, 0x00 if
    /// not</returns>
    uint8_t Std_FiFo_LinearBuffer_Peek(
        t_Std_FiFo_LinearBuffer_BufferHandle HandleForStruct,
        uint32_t* const BufferToCopyTo, uint32_t NumberOfU32s);

    /// <summary>
    /// Allocates a new element and returns a pointer to the raw data section
    /// User is responsible for writing too much data
    /// </summary>
    /// <param name="HandleForStruct">The pointer to the fifo struct</param>
    /// <param name="NumberOfU32s">The number of elements to feed. If to big or 0,
    /// *(0) is returned</param> <returns>Handle to the Element or
    /// c_Std_FiFo_LinearBuffer_NullElementHandle</returns>
    uint8_t Std_FiFo_LinearBuffer_PutElement(
        t_Std_FiFo_LinearBuffer_BufferHandle HandleForStruct,
        t_Std_FiFo_LinearBuffer_ElementHandle* const HandleToElement,
        uint32_t NumberOfU32s);

    /// <summary>
    /// Allocates a new element and returns a pointer to the raw data section
    /// User is responsible for writing too much data
    ///
    /// Puts the element to the top
    /// </summary>
    /// <param name="HandleForStruct">The pointer to the fifo struct</param>
    /// <param name="NumberOfU32s">The number of elements to feed. If to big or 0,
    /// *(0) is returned</param> <returns>Handle to the Element or
    /// c_Std_FiFo_LinearBuffer_NullElementHandle</returns>
    uint8_t Std_FiFo_LinearBuffer_PutTopElement(
        t_Std_FiFo_LinearBuffer_BufferHandle HandleForStruct,
        t_Std_FiFo_LinearBuffer_ElementHandle* const HandleToElement,
        uint32_t NumberOfU32s);

    /// <summary>
    /// Returns a pointer to the raw data of the first element
    /// Locks the element for take
    /// Must call release before take or Pop
    /// </summary>
    /// <param name="HandleForStruct">The pointer to the fifo struct</param>
    /// <param name="NumberOfU32s">THe number of U32s the user wants to use. If
    /// not possible (= or too big), return is *(0)</param> <returns>Handle to the
    /// Element or c_Std_FiFo_LinearBuffer_NullElementHandle</returns>
    uint8_t Std_FiFo_LinearBuffer_PeekElement(
        t_Std_FiFo_LinearBuffer_BufferHandle HandleForStruct,
        t_Std_FiFo_LinearBuffer_ElementHandle* const HandleToElement,
        uint32_t NumberOfU32s);

    /// <summary>
    /// Releases the Lock on the data
    ///
    /// The passed handle to the element is destroyed in the process
    /// </summary>
    /// <param name="HandleForStruct">The pointer to the element struct</param>
    /// <returns>1 if success, 0 if not</returns>
    uint8_t Std_FiFo_LinearBuffer_ReleaseElement(
        t_Std_FiFo_LinearBuffer_BufferHandle HandleForStruct,
        t_Std_FiFo_LinearBuffer_ElementHandle* const HandleToElement);

#ifdef __cplusplus
}
#endif
#endif

.c File

#include "Std_FiFo_LinearBuffer.h"

#include <malloc.h>

#pragma region Static variables
/// <summary>
/// Variable used as internal NULLPTR
/// </summary>
static struct Std_FiFo_LinearBuffer
* const c_Std_FiFo_LinearBuffer_NullBufferHandlePointer
= (struct Std_FiFo_LinearBuffer* const)(0x00000000);

/// <summary>
/// Variable used as internal NULLPTR
/// </summary>
static struct Std_FiFo_LinearBuffer_Element
* const c_Std_FiFo_LinearBuffer_NullElementHandlePointer
= (struct Std_FiFo_LinearBuffer_Element* const)(0x00000000);

enum
{
    /// <summary>
    /// Configures the maximum number of elements in the buffer
    /// Each element can be the maximum of the buffer size or just a single data
    /// item However, no more than this number of elements are able to be managed
    /// </summary>
    c_Std_FiFo_LinearBuffer_MaxNumberOfElementsInBuffer = 16,

    /// <summary>
    /// Configures the number of U32s in the RAM Buffer
    /// This is the entire raw ram buffer used to store the data
    /// </summary>
    c_Std_FiFo_LinearBuffer_RamBufferSizeInU32 = 1024
};

/// <summary>
/// Used as Internal True
/// </summary>
static const uint8_t c_Std_FiFo_LinearBuffer_True = (uint8_t)(0x01);

/// <summary>
/// Used as Internal False
/// </summary>
static const uint8_t c_Std_FiFo_LinearBuffer_False = (uint8_t)(0x00);

/// <summary>
/// Used as nullpointer
/// </summary>
static t_Std_FiFo_LinearBuffer_BufferHandle
c_Std_FiFo_LinearBuffer_NullBufferHandle
= (t_Std_FiFo_LinearBuffer_BufferHandle)(0x00000000);

/// <summary>
/// Used as nullpointer
/// </summary>
static t_Std_FiFo_LinearBuffer_ElementHandle
c_Std_FiFo_LinearBuffer_NullElementHandle
= (t_Std_FiFo_LinearBuffer_ElementHandle)(0x00000000);

/// <summary>
/// Used as Nullpointer for Data elements or data fields in the buffer
/// </summary>
static uint32_t* const c_Std_FiFo_LinearBuffer_NullDataPointer
= (uint32_t* const)(0x00000000);
#pragma endregion

#pragma region Struct definition
/// <summary>
/// Stores the status of the fifo
/// </summary>
struct Std_FiFo_LinearBuffer_Status
{
    /// <summary>
    /// Is configured Flag for the Struct
    /// Is set in the initialization call.
    /// Can not Put to/take from the buffer
    /// unless the init call was performed
    /// </summary>
    uint32_t IsInitialized;
    /// <summary>
    /// The Current index in the circular Info struct allocation logic.
    /// This is a rollover direct index into the
    /// InfoElement Array. Everytime a new elemnt is allocated,
    /// this index is incremented and if required rolled over
    /// </summary>
    uint32_t InfoStructAllocationIndex;
    /// <summary>
    /// The Current Number of elements in the Buffer
    /// This indicates the number of elements currently in the buffer
    /// User can not put to the fifo, if threshold is reached
    /// </summary>
    uint32_t NumberOfElements;
    /// <summary>
    /// Stores the current number of U32s stored in the buffer
    /// This is the ammount of payload data currently residing in the buffer
    /// </summary>
    uint32_t NumberOfU32s;
    /// <summary>
    /// THe number of U32 slots left at the end of the buffer
    /// This number indicates, how many free data slots are available at the end
    /// of the buffer If all elements are taken from the buffer, this watermark is
    /// reset to represent the full buffer size again
    /// </summary>
    uint32_t TailSizeU32s;
    /// <summary>
    /// Current buffer for allocation in the linear ram buffer
    /// This pointer points to the first free slot at the end of the raw ram
    /// buffer It is used to write to the data buffer and is incremented on write.
    /// If all elements are taken, the pointer is reset to the buffer start
    /// </summary>
    uint32_t* CurrentAllocationPtr;
};

/// <summary>
/// Struct used to store information about the individual elements in the buffer
/// </summary>
struct Std_FiFo_LinearBuffer_Element
{
    /// <summary>
    /// POinter to the data start address of the data section occupied by the
    /// element
    /// </summary>
    uint32_t* AbsStartAdr;
    /// <summary>
    /// Indicates if the current Element is locked (e.g. handle is available)
    /// </summary>
    uint32_t IsLocked;
    /// <summary>
    /// Number of U32s in the Buffer space for this element
    /// </summary>
    uint32_t NumElements;
};

/// <summary>
/// Struct represents the datatype used for an 4KB buffer with 16 Elements
/// </summary>
struct Std_FiFo_LinearBuffer
{
    /// <summary>
    /// Stores the status of the fifo
    /// </summary>
    struct Std_FiFo_LinearBuffer_Status Info;
    /// <summary>
    /// The Infos on the actual Elements
    /// </summary>
    struct Std_FiFo_LinearBuffer_Element
        ElementInfos[c_Std_FiFo_LinearBuffer_MaxNumberOfElementsInBuffer];
    /// <summary>
    /// Stores the pointer mapping table for the elements
    /// </summary>
    struct Std_FiFo_LinearBuffer_Element
        * ElementMapping[c_Std_FiFo_LinearBuffer_MaxNumberOfElementsInBuffer];
    /// <summary>
    /// The Actual RAM buffer used for the FIFO
    /// </summary>
    uint32_t RamBuffer[c_Std_FiFo_LinearBuffer_RamBufferSizeInU32];
};
#pragma endregion

#pragma region PrivateHelpers
/// <summary>
/// Creates a pointer to a struct for a given U32 Address
/// Checks if address is 0 or 0XF...F to make sure pointer is valid
/// </summary>
/// <param name="address">The absolute address to point to</param>
/// <returns>Returns (0) if not valid, otherwise the pointer to the
/// struct</returns>
static struct Std_FiFo_LinearBuffer* const
priv_GetBufferHandlePtrFromU32AddressAndValidate(
    t_Std_FiFo_LinearBuffer_BufferHandle address)
{
    if (address != 0 && address != 0xFFFFFFFF)
        return (struct Std_FiFo_LinearBuffer* const)(address);
    else
        return c_Std_FiFo_LinearBuffer_NullBufferHandlePointer;
}

/// <summary>
/// Creates a pointer to a struct for a given U32 Address
/// Checks if address is 0 or 0XF...F to make sure pointer is valid
/// </summary>
/// <param name="address">The absolute address to point to</param>
/// <returns>Returns (0) if not valid, otherwise the pointer to the
/// struct</returns>
static uint8_t
priv_GetElementHandlePtrFromU32AddressAndValidate(
    struct Std_FiFo_LinearBuffer* const ptr,
    struct Std_FiFo_LinearBuffer_Element** ptrEle,
    t_Std_FiFo_LinearBuffer_ElementHandle* HandleToElement)
{
    // Loop all Elements and check if one with a fitting start address can be
    // found If so, return Success - if not return error
    for (uint32_t i = 0; i < ptr->Info.NumberOfElements; i++)
    {
        if ((uint32_t)ptr->ElementMapping[i]->AbsStartAdr == *HandleToElement)
        {
            // Set pointer and return success
            *ptrEle = ptr->ElementMapping[i];
            return c_Std_FiFo_LinearBuffer_True;
        }
    }
    return c_Std_FiFo_LinearBuffer_False;
}

/// <summary>
/// Copies data from src to dest
/// Does not alter the pointers
/// </summary>
/// <param name="src">src pointer</param>
/// <param name="dst">dst pointer</param>
/// <param name="cnt">number of elements</param>
static void
priv_CopyDataFromTo(uint32_t* const src, uint32_t* const dst, uint32_t cnt)
{
    // Make local copy, so the arguments are not altered
    uint32_t* srcPtr = src;
    uint32_t* destPtr = dst;
    for (uint32_t i = 0; i < cnt; i++)
    {
        *destPtr = *srcPtr;
        destPtr++;
        srcPtr++;
    }
}

/// <summary>
/// Trashes the first most info element and advances the mapping chain
///
/// First, the Number of ElementsIndex and the NumberOfU32s are decremented
/// Then, the Current Info struct is trashed (See buildflag)
/// Afterwards - if there are still elements in the buffer -
/// the buffer mapping chain is advanced
/// If there are no elements left, the internal logic is reset
/// (Head meets tail)
/// </summary>
/// <param name="ptr">The pointer to the fifo struct</param>
static void
priv_TrashInfoStructAndAdvanceMappingForHead(
    struct Std_FiFo_LinearBuffer* const ptr)
{
    // Update Info struct - always remove all data from data counter
    ptr->Info.NumberOfElements--;
    ptr->Info.NumberOfU32s -= ptr->ElementMapping[0]->NumElements;
    // trash info struct
    ptr->ElementMapping[0]->AbsStartAdr = c_Std_FiFo_LinearBuffer_NullDataPointer;
    ptr->ElementMapping[0]->NumElements = 0;
    ptr->ElementMapping[0]->IsLocked = c_Std_FiFo_LinearBuffer_False;
    ptr->ElementMapping[0] = 0;
    // Check if Head reached tail - if so, reset logic
    if (ptr->Info.NumberOfElements == 0)
    {
        ptr->Info.NumberOfElements = 0;
        ptr->Info.CurrentAllocationPtr = &ptr->RamBuffer[0];
        ptr->Info.NumberOfU32s = 0;
        ptr->Info.InfoStructAllocationIndex = 0;
        ptr->Info.TailSizeU32s = c_Std_FiFo_LinearBuffer_RamBufferSizeInU32;
        ptr->ElementMapping[0] = 0;
    }
    else
    {
        // Advance info mapping
        for (uint32_t i = 0; i < ptr->Info.NumberOfElements; i++)
        {
            ptr->ElementMapping[i] = ptr->ElementMapping[i + 1];
        }
        ptr->ElementMapping[ptr->Info.NumberOfElements] = 0;
    }
}

/// <summary>
/// Allocates a new element info item
///
/// First, the Element mapping is linked and initialized and the number of
/// elements is incremented Afterwards, the allocation index is incremented and
/// rolled over Then, the NumberOfU32s and the tail size are updated
/// </summary>
/// <param name="ptr">The pointer to the fifo struct</param>
/// <param name="NumberOfU32s">Number of data itmems to allocate for</param>
/// <param name="OnTop">If Flag is 1, the element gets allocated on top</param>
/// <param name="LockedFlag">If to lock element to prevent take. Default is
/// Unlocked</param> <returns>The Alllocation pointer to write the data
/// too</returns>
static uint32_t*
priv_AllocateNewInfoStructElement(struct Std_FiFo_LinearBuffer* const ptr,
    uint32_t NumberOfU32s, uint8_t OnTop,
    uint8_t LockedFlag)
{
    // Check if to allocate on Top
    // If not, then allocate the last element in chain
    // Otherwise copy chain and then allocate top
    if (OnTop == 0)
    {
        // Allocate current info element
        // and put start values
        ptr->ElementMapping[ptr->Info.NumberOfElements]
            = &ptr->ElementInfos[ptr->Info.InfoStructAllocationIndex];
        ptr->ElementMapping[ptr->Info.NumberOfElements]->NumElements
            = NumberOfU32s;
        ptr->ElementMapping[ptr->Info.NumberOfElements]->AbsStartAdr
            = ptr->Info.CurrentAllocationPtr;
        ptr->ElementMapping[ptr->Info.NumberOfElements]->IsLocked = LockedFlag;
    }
    else
    {
        // Map elements backwards
        for (uint32_t i = ptr->Info.NumberOfElements; i > 0; i--)
        {
            ptr->ElementMapping[i] = ptr->ElementMapping[i - 1];
        }
        // Allocate current info element
        // and put start values
        ptr->ElementMapping[0]
            = &ptr->ElementInfos[ptr->Info.InfoStructAllocationIndex];
        ptr->ElementMapping[0]->NumElements = NumberOfU32s;
        ptr->ElementMapping[0]->AbsStartAdr = ptr->Info.CurrentAllocationPtr;
        ptr->ElementMapping[0]->IsLocked = LockedFlag;
    }
    // Always increment elements index
    uint32_t* ret = ptr->Info.CurrentAllocationPtr;
    ptr->Info.NumberOfElements++;
    // Increment and rollover Allocation Index
    ptr->Info.InfoStructAllocationIndex++;
    if (ptr->Info.InfoStructAllocationIndex
        >= c_Std_FiFo_LinearBuffer_MaxNumberOfElementsInBuffer)
    {
        ptr->Info.InfoStructAllocationIndex = 0;
    }
    // Update Info struct
    ptr->Info.NumberOfU32s += NumberOfU32s;
    ptr->Info.TailSizeU32s -= NumberOfU32s;
    ptr->Info.CurrentAllocationPtr += NumberOfU32s;
    return ret;
}

/// <summary>
/// Computes the maximum allowed number of elements to copy during take
/// </summary>
/// <param name="ptr">The pointer to the fifo struct</param>
/// <param name="NumberOfU32s">The proposed number of elements</param>
/// <returns>IF arg == 0, the maximum number pr info struct is returned
/// Otherwise, The bigger of the two is returned</returns>
static uint32_t
priv_GetMaxNumOfValidDataForCopyOnTake(struct Std_FiFo_LinearBuffer* const ptr,
    uint32_t NumberOfU32s)
{
    return (NumberOfU32s == 0)
        ? (ptr->ElementMapping[0]->NumElements)
        : ((NumberOfU32s > ptr->ElementMapping[0]->NumElements)
            ? ptr->ElementMapping[0]->NumElements
            : NumberOfU32s);
}

#pragma endregion

uint8_t
Std_FiFo_LinearBuffer_Initiliaze(
    t_Std_FiFo_LinearBuffer_BufferHandle* const HandleForStruct)
{
    // Check if valid pointer
    // if so initialize
    // Otherwise return error
    struct Std_FiFo_LinearBuffer* const ptr
        = (struct Std_FiFo_LinearBuffer* const)(malloc(
            sizeof(struct Std_FiFo_LinearBuffer)));
    if (ptr == c_Std_FiFo_LinearBuffer_NullBufferHandlePointer)
        return c_Std_FiFo_LinearBuffer_False;
    else
    {
        // Set Info struct as not-initialized
        ptr->Info.IsInitialized = c_Std_FiFo_LinearBuffer_False;
        ptr->Info.InfoStructAllocationIndex = 0;
        ptr->Info.NumberOfElements = 0;
        ptr->Info.NumberOfU32s = 0;
        ptr->Info.TailSizeU32s = c_Std_FiFo_LinearBuffer_RamBufferSizeInU32;
        ptr->Info.CurrentAllocationPtr = &ptr->RamBuffer[0];
        // Rebuild mapping table
        for (uint32_t i = 0;
            i < c_Std_FiFo_LinearBuffer_MaxNumberOfElementsInBuffer; i++)
        {
            ptr->ElementInfos[i].AbsStartAdr
                = c_Std_FiFo_LinearBuffer_NullDataPointer;
            ptr->ElementInfos[i].NumElements = 0;
            ptr->ElementInfos[i].IsLocked = c_Std_FiFo_LinearBuffer_False;
            ptr->ElementMapping[i] = (struct Std_FiFo_LinearBuffer_Element*)
                c_Std_FiFo_LinearBuffer_NullElementHandle;
        }
        // zerorize Buffer
        for (uint32_t i = 0; i < c_Std_FiFo_LinearBuffer_RamBufferSizeInU32; i++)
        {
            ptr->RamBuffer[i] = 0;
        }
        // Set as initialized, set handle value and return
        ptr->Info.IsInitialized = c_Std_FiFo_LinearBuffer_True;
        *HandleForStruct = (t_Std_FiFo_LinearBuffer_BufferHandle)(ptr);
        return c_Std_FiFo_LinearBuffer_True;
    }
}

uint8_t
Std_FiFo_LinearBuffer_Invalidate(
    t_Std_FiFo_LinearBuffer_BufferHandle* const HandleForStruct)
{
    // Check if pointer exists
    // if not return error
    // If so, initialize to defaults and reset init flag
    // Then invalidate the ptr
    struct Std_FiFo_LinearBuffer* const ptr
        = priv_GetBufferHandlePtrFromU32AddressAndValidate(*HandleForStruct);
    if (ptr == c_Std_FiFo_LinearBuffer_NullBufferHandlePointer)
        return c_Std_FiFo_LinearBuffer_False;
    else
    {
        ptr->Info.IsInitialized = c_Std_FiFo_LinearBuffer_False;
        ptr->Info.InfoStructAllocationIndex = 0;
        ptr->Info.NumberOfElements = 0;
        ptr->Info.NumberOfU32s = 0;
        ptr->Info.TailSizeU32s = 0;
        ptr->Info.CurrentAllocationPtr = c_Std_FiFo_LinearBuffer_NullDataPointer;
        // Rebuild mapping table
        for (uint32_t i = 0;
            i < c_Std_FiFo_LinearBuffer_MaxNumberOfElementsInBuffer; i++)
        {
            ptr->ElementInfos[i].AbsStartAdr
                = c_Std_FiFo_LinearBuffer_NullDataPointer;
            ptr->ElementInfos[i].NumElements = 0;
            ptr->ElementInfos[i].IsLocked = c_Std_FiFo_LinearBuffer_False;
            ptr->ElementMapping[i] = (struct Std_FiFo_LinearBuffer_Element*)
                c_Std_FiFo_LinearBuffer_NullElementHandle;
        }
        // zerorize Buffer
        for (uint32_t i = 0; i < c_Std_FiFo_LinearBuffer_RamBufferSizeInU32; i++)
        {
            ptr->RamBuffer[i] = 0;
        }
        // Invalidate Pointer and return success
        *HandleForStruct
            = (t_Std_FiFo_LinearBuffer_BufferHandle)(c_Std_FiFo_LinearBuffer_NullDataPointer);
        free(ptr);
        return c_Std_FiFo_LinearBuffer_True;
    }
}

uint8_t
Std_FiFo_LinearBuffer_CanPut(
    t_Std_FiFo_LinearBuffer_BufferHandle HandleForStruct, uint32_t NumberOfU32s)
{
    // Check if struct is intialized and can take elements
    struct Std_FiFo_LinearBuffer* const ptr
        = priv_GetBufferHandlePtrFromU32AddressAndValidate(HandleForStruct);
    return ((ptr != c_Std_FiFo_LinearBuffer_NullBufferHandlePointer)
        && (ptr->Info.IsInitialized == c_Std_FiFo_LinearBuffer_True)
        && (ptr->Info.NumberOfElements
            < c_Std_FiFo_LinearBuffer_MaxNumberOfElementsInBuffer)
        && (ptr->Info.TailSizeU32s > NumberOfU32s))
        ? c_Std_FiFo_LinearBuffer_True
        : c_Std_FiFo_LinearBuffer_False;
}

uint8_t
Std_FiFo_LinearBuffer_CanTake(
    t_Std_FiFo_LinearBuffer_BufferHandle HandleForStruct)
{
    // Check if struct is initialized and has elements
    // Also check if Element is Locked
    struct Std_FiFo_LinearBuffer* const ptr
        = priv_GetBufferHandlePtrFromU32AddressAndValidate(HandleForStruct);
    return ((ptr != c_Std_FiFo_LinearBuffer_NullBufferHandlePointer)
        && (ptr->Info.IsInitialized == c_Std_FiFo_LinearBuffer_True)
        && (ptr->Info.NumberOfElements > 0)
        && (ptr->ElementMapping[0]->IsLocked == c_Std_FiFo_LinearBuffer_False)
        // Safe to access as Number of elements != 0
        )
        ? c_Std_FiFo_LinearBuffer_True
        : c_Std_FiFo_LinearBuffer_False;
}

uint8_t
Std_FiFo_LinearBuffer_Pop(t_Std_FiFo_LinearBuffer_BufferHandle HandleForStruct)
{
    // Get pointer to work with and check that is valid
    // Then check if buffer has data. If not, just return error
    // If has data, then trash the Head and return success
    struct Std_FiFo_LinearBuffer* const ptr
        = priv_GetBufferHandlePtrFromU32AddressAndValidate(HandleForStruct);
    if (ptr == c_Std_FiFo_LinearBuffer_NullBufferHandlePointer)
        return c_Std_FiFo_LinearBuffer_False;
    else if (!Std_FiFo_LinearBuffer_CanTake(HandleForStruct))
        return c_Std_FiFo_LinearBuffer_False;
    else
    {
        // Trash info struct
        priv_TrashInfoStructAndAdvanceMappingForHead(ptr);
        // Return success;
        return c_Std_FiFo_LinearBuffer_True;
    }
}

uint8_t
Std_FiFo_LinearBuffer_Take(
    t_Std_FiFo_LinearBuffer_BufferHandle HandleForStruct,
    uint32_t* const BufferToCopyTo, uint32_t NumberOfU32s)
{
    // Get pointer to work with and check that is valid
    // Then check if buffer has data. If not, just return error
    // If has data, determine number of U32s to Take and copy data
    // Then Update metrics and trash the Head
    struct Std_FiFo_LinearBuffer* const ptr
        = priv_GetBufferHandlePtrFromU32AddressAndValidate(HandleForStruct);
    if (ptr == c_Std_FiFo_LinearBuffer_NullBufferHandlePointer)
        return c_Std_FiFo_LinearBuffer_False;
    else if (!Std_FiFo_LinearBuffer_CanTake(HandleForStruct))
        return c_Std_FiFo_LinearBuffer_False;
    else
    {
        // Has data, so copy data from first element in buffer
        // If Argument Len is 0, copy all data as per configuration
        // otherwise copy the number given per parameter, but a maximum as per
        // buffer info
        priv_CopyDataFromTo(
            ptr->ElementMapping[0]->AbsStartAdr, BufferToCopyTo,
            priv_GetMaxNumOfValidDataForCopyOnTake(ptr, NumberOfU32s));
        // Trash info struct
        priv_TrashInfoStructAndAdvanceMappingForHead(ptr);
        // return success
        return c_Std_FiFo_LinearBuffer_True;
    }
}

uint8_t
Std_FiFo_LinearBuffer_Put(t_Std_FiFo_LinearBuffer_BufferHandle HandleForStruct,
    uint32_t* const BufferToCopyFrom,
    uint32_t NumberOfU32s)
{
    // Get pointer to work with and check that is valid
    // Then check if buffer has data. If not, just return error
    // If has data, allocate new element and copy data
    // Then increment allocation counter and Update metrics
    struct Std_FiFo_LinearBuffer* const ptr
        = priv_GetBufferHandlePtrFromU32AddressAndValidate(HandleForStruct);
    if (ptr == c_Std_FiFo_LinearBuffer_NullBufferHandlePointer)
        return c_Std_FiFo_LinearBuffer_False;
    else if (!Std_FiFo_LinearBuffer_CanPut(HandleForStruct, NumberOfU32s))
        return c_Std_FiFo_LinearBuffer_False;
    else
    {
        // Copy data and increment pointer
        priv_CopyDataFromTo(BufferToCopyFrom,
            priv_AllocateNewInfoStructElement(
                ptr, NumberOfU32s, c_Std_FiFo_LinearBuffer_False,
                c_Std_FiFo_LinearBuffer_False),
            NumberOfU32s);
        // Return success
        return c_Std_FiFo_LinearBuffer_True;
    }
}

uint8_t
Std_FiFo_LinearBuffer_PutTop(
    t_Std_FiFo_LinearBuffer_BufferHandle HandleForStruct,
    uint32_t* const BufferToCopyFrom, uint32_t NumberOfU32s)
{
    // Get pointer to work with and check that is valid
    // Then check if buffer has data. If not, just return error
    // If has data, allocate new element and copy data
    // Then increment allocation counter and Update metrics
    struct Std_FiFo_LinearBuffer* const ptr
        = priv_GetBufferHandlePtrFromU32AddressAndValidate(HandleForStruct);
    if (ptr == c_Std_FiFo_LinearBuffer_NullBufferHandlePointer)
        return c_Std_FiFo_LinearBuffer_False;
    else if (!Std_FiFo_LinearBuffer_CanPut(HandleForStruct, NumberOfU32s))
        return c_Std_FiFo_LinearBuffer_False;
    else
    {
        // Copy data and increment pointer
        priv_CopyDataFromTo(BufferToCopyFrom,
            priv_AllocateNewInfoStructElement(
                ptr, NumberOfU32s, c_Std_FiFo_LinearBuffer_True,
                c_Std_FiFo_LinearBuffer_False),
            NumberOfU32s);
        // Return success
        return c_Std_FiFo_LinearBuffer_True;
    }
}

uint8_t
Std_FiFo_LinearBuffer_Peek(
    t_Std_FiFo_LinearBuffer_BufferHandle HandleForStruct,
    uint32_t* const BufferToCopyTo, uint32_t NumberOfU32s)
{
    // Get pointer to work with and check that is valid
    // Then check if buffer has data. If not, just return error
    // If has data, determine number of U32s to copy
    // Then copy data and update metrics
    // To not Trash head
    struct Std_FiFo_LinearBuffer* const ptr
        = priv_GetBufferHandlePtrFromU32AddressAndValidate(HandleForStruct);
    if (ptr == c_Std_FiFo_LinearBuffer_NullBufferHandlePointer)
        return c_Std_FiFo_LinearBuffer_False;
    else if (!Std_FiFo_LinearBuffer_CanTake(HandleForStruct))
        return c_Std_FiFo_LinearBuffer_False;
    else
    {
        // Has data, so copy data from first element in buffer
        // If Argument Len is 0, copy all data as per configuration
        // otherwise copy the number given per parameter, but a maximum as per
        // buffer info
        priv_CopyDataFromTo(
            ptr->ElementMapping[0]->AbsStartAdr, BufferToCopyTo,
            priv_GetMaxNumOfValidDataForCopyOnTake(ptr, NumberOfU32s));
        // return success
        return c_Std_FiFo_LinearBuffer_True;
    }
}

uint8_t
Std_FiFo_LinearBuffer_PutElement(
    t_Std_FiFo_LinearBuffer_BufferHandle HandleForStruct,
    t_Std_FiFo_LinearBuffer_ElementHandle* const HandleToElement,
    uint32_t NumberOfU32s)
{
    // Get pointer to work with and check that is valid
    // Then check if buffer has data. If not, just return error
    // If has data allocate new info struct and return the handle
    // advance the access pointer and update metrics
    struct Std_FiFo_LinearBuffer* const ptr
        = priv_GetBufferHandlePtrFromU32AddressAndValidate(HandleForStruct);
    if (ptr == c_Std_FiFo_LinearBuffer_NullBufferHandlePointer)
        return c_Std_FiFo_LinearBuffer_False;
    else if (!Std_FiFo_LinearBuffer_CanPut(HandleForStruct, NumberOfU32s))
        return c_Std_FiFo_LinearBuffer_False;
    else
    {
        // Get new info struct
        priv_AllocateNewInfoStructElement(ptr, NumberOfU32s,
            c_Std_FiFo_LinearBuffer_False,
            c_Std_FiFo_LinearBuffer_True);
        // Return Handle.... -1 safe here, as element was created before hand
        // is necessary as index was incremented
        *HandleToElement
            = (t_Std_FiFo_LinearBuffer_ElementHandle)(ptr->ElementMapping
                [ptr->Info
                .NumberOfElements
                - 1]
        ->AbsStartAdr);
        return c_Std_FiFo_LinearBuffer_True;
    }
}

uint8_t
Std_FiFo_LinearBuffer_PutTopElement(
    t_Std_FiFo_LinearBuffer_BufferHandle HandleForStruct,
    t_Std_FiFo_LinearBuffer_ElementHandle* const HandleToElement,
    uint32_t NumberOfU32s)
{
    // Get pointer to work with and check that is valid
    // Then check if buffer has data. If not, just return error
    // If has data allocate new info struct and return the handle
    // advance the access pointer and update metrics
    struct Std_FiFo_LinearBuffer* const ptr
        = priv_GetBufferHandlePtrFromU32AddressAndValidate(HandleForStruct);
    if (ptr == c_Std_FiFo_LinearBuffer_NullBufferHandlePointer)
        return c_Std_FiFo_LinearBuffer_False;
    else if (!Std_FiFo_LinearBuffer_CanPut(HandleForStruct, NumberOfU32s))
        return c_Std_FiFo_LinearBuffer_False;
    else
    {
        // Get new info struct
        priv_AllocateNewInfoStructElement(ptr, NumberOfU32s,
            c_Std_FiFo_LinearBuffer_True,
            c_Std_FiFo_LinearBuffer_True);
        // Return handle - Element was put to the Top sor eturn top element
        *HandleToElement
            = (t_Std_FiFo_LinearBuffer_ElementHandle)(ptr->ElementMapping[0]
                ->AbsStartAdr);
        return c_Std_FiFo_LinearBuffer_True;
    }
}

uint8_t
Std_FiFo_LinearBuffer_PeekElement(
    t_Std_FiFo_LinearBuffer_BufferHandle HandleForStruct,
    t_Std_FiFo_LinearBuffer_ElementHandle* const HandleToElement,
    uint32_t NumberOfU32s)
{
    // Get pointer to work with and check that is valid
    // Then check if buffer has data. If not, just return error
    // If has data return the data pointer
    // Do not trash the head
    // But lock the element
    struct Std_FiFo_LinearBuffer* const ptr
        = priv_GetBufferHandlePtrFromU32AddressAndValidate(HandleForStruct);
    if (ptr == c_Std_FiFo_LinearBuffer_NullBufferHandlePointer)
        return c_Std_FiFo_LinearBuffer_False;
    else if (!Std_FiFo_LinearBuffer_CanTake(HandleForStruct))
        return c_Std_FiFo_LinearBuffer_False;
    else if (ptr->ElementMapping[0]->NumElements >= NumberOfU32s)
        return c_Std_FiFo_LinearBuffer_False;
    else
    {
        // Lock head element
        ptr->ElementMapping[0]->IsLocked = c_Std_FiFo_LinearBuffer_True;
        // Return handle
        *HandleToElement
            = (t_Std_FiFo_LinearBuffer_ElementHandle)(ptr->ElementMapping[0]
                ->AbsStartAdr);
        return c_Std_FiFo_LinearBuffer_True;
    }
}

uint8_t
Std_FiFo_LinearBuffer_ReleaseElement(
    t_Std_FiFo_LinearBuffer_BufferHandle HandleForStruct,
    t_Std_FiFo_LinearBuffer_ElementHandle* const HandleToElement)
{
    // Get the Pointer to the Element
    // and check if exists. If not return error
    // If exists, release the lock and invalidate the pointer
    struct Std_FiFo_LinearBuffer* const ptr
        = priv_GetBufferHandlePtrFromU32AddressAndValidate(HandleForStruct);
    struct Std_FiFo_LinearBuffer_Element* ptrEle
        = (struct Std_FiFo_LinearBuffer_Element*)(0);
    if (ptr == c_Std_FiFo_LinearBuffer_NullBufferHandlePointer)
        return c_Std_FiFo_LinearBuffer_False;
    else if (!priv_GetElementHandlePtrFromU32AddressAndValidate(ptr, &ptrEle,
        HandleToElement))
        return c_Std_FiFo_LinearBuffer_False;
    else
    {
        // Unlokc and invalidate the handle
        ptrEle->IsLocked = c_Std_FiFo_LinearBuffer_False;
        *HandleToElement
            = (t_Std_FiFo_LinearBuffer_ElementHandle)(c_Std_FiFo_LinearBuffer_NullDataPointer);
        return c_Std_FiFo_LinearBuffer_True;
    }
}

Example

t_Std_FiFo_LinearBuffer_BufferHandle dsd;
if (Std_FiFo_LinearBuffer_Initiliaze(&dsd)) {

    uint32_t buffer[32];
    for (int i = 0; i < 8; i++) {
        for (int d = 0; d < 32; d++) buffer[d] = d + i;
        Std_FiFo_LinearBuffer_Put(dsd, buffer, 32);
    }

    for (int i = 0; i < 8; i++) {
        t_Std_FiFo_LinearBuffer_ElementHandle h;
        if (Std_FiFo_LinearBuffer_PutElement(dsd, &h, 32)) {
            uint32_t* g = (uint32_t*)(h);
            for (int d = 0; d < 32; d++) g[d] = d + i;
            Std_FiFo_LinearBuffer_ReleaseElement(dsd,&h);
        }
    }
    /* Do other Work */
}
Std_FiFo_LinearBuffer_Invalidate(&dsd);

Use-Cases

This library is itended to be used for embedded applications. My focus is one:

  • Doucpling e.g. communication peripherals from the application code
  • Buffering ADC/DAC samples
  • Buffering data that is streamed through the system
  • you get the idea...

Most of the times, one of these two scenarios will be present:

  • ISR allocates element and fills is (e.g. receiving a frame via SPI). Once complete, it releases the element and the "main" grabs it via "take".
  • The "main" both "puts" and "takes" - from different modules.

I do not require the code to be ISR/Thread-Safe/Reentrant. This will be done on the user-level.

Question

Now, i have multiple questions:

As Context: I'm currently working on improving the documentation. There still are "copy-paste residues" and incomplete explanations. So it is TBD.

  1. At the top of the .c file i declare multiple constants for use throughout the code. I did this with e.g. static const uint8_t c_Std_FiFo_LinearBuffer_False = (uint8_t)(0x00); instead of #define c_Std_FiFo_LinearBuffer_False (uint8_t)(0) to avoid polluting the codebase with defines. However, i think that this will place the constants into flash - which is not required. What would be a better approach for these constants only required in the .c file shown?

  2. In the static struct Std_FiFo_LinearBuffer* const priv_GetBufferHandlePtrFromU32AddressAndValidate(t_Std_FiFo_LinearBuffer_BufferHandle address); i try to check, if the absolute address provided to the call can be used as a valid pointer to the a struct Std_FiFo_LinearBuffer - However, only checking if the address is not 0 or 0xF..F seems a little useless to me. I could of course adapt the ranges to reflect the memory ranges of the specific controller i'm using, but this would make the library non-reuseable. How would i make sure, that the address provided is indeed pointing to a struct in of said type in memory? Should i include a magic-number in the struct and check against it?

  3. I would like to hear your suggestions on how to further refactor the code, to make it more maintainable and re-useable.

  4. Which additional features are a must to implement?

  5. To "hide" the internal structs from the user, i choose to make the interface available only through a "handle". This handle is essentially a u32 storing the memory address of the memory instance to be used. I want to avoid the user to be able to access the inner workings of the data structure through the "user-space" reference. Is the way i choose practical or completly pointless? How could it be improved?

\$\endgroup\$
1
  • \$\begingroup\$ Look up opaque pointers in C. Point 5 in context is describing them in so many words \$\endgroup\$ Commented Dec 9, 2023 at 0:51

1 Answer 1

4
\$\begingroup\$

Naming things

The names you give to types and functions are very long. Maybe if you have an editor or IDE that allows tab completion it is not so bad, but having to type the 48 characters of priv_GetBufferHandlePtrFromU32AddressAndValidate is a bit much. It also makes lines very long, requiring frequent line breaks to keep the line lengths in check, making functions look longer than they are.

Having long common prefixes also makes things more difficult to read. For exampe, the difference between Std_FiFo_LinearBuffer_PutElement and Std_FiFo_LinearBuffer_ReleaseElement is quite small to the whole name. While the prefix avoid name conflicts with other libraries, try to come up with something shorter that still is unique enough:

  • Why is there Std in the name? It makes it look like this indicates this is a standard library of some sorts, but it's not. So I would drop this.
  • Is LinearBuffer really adding anything useful? I think the user of this code just cares about it being a FIFO, how it is implemented behind the scenes is less important. So unless you want to implement different types of FIFOs, I would drop this as well.
  • Prefixes like t_ and c_ are really not that useful. The compiler does not care about them. It might be nice for the programmer, but only if you are very consistent. I see lots of non-const variables having a c_ prefix, which tells me you are not applying these prefixes consistently, which makes it worse than useless.

Spelling

There are several spelling mistakes in your code. Consider using a spell checker to fix them. Maybe your code editor or IDE has a built-in spell checking function, otherwise you can use external tools like codespell to do it for you.

Handles

Your handles are 32-bit integers, but you are actually just storing an address in them. But what if you run this code on a 64-bit device? You have a check for it not being 0 or 0xFFFFFFFF, but you can do a NULL-pointer check on regular pointers, and since Std_FiFo_LinearBuffer is larger than a byte, you can never have 0xFFFFFFFF as the address to a buffer anyway. Apart from these two values, the caller could pass you any other integer value, and your will blindly accept it. So I thing the validation has very little value.

I recommend that you make the handles pointers instead of integers. You can do this in the header file:

struct Std_FiFo_LinearBuffer; // forward declare the struct
typedef Std_FiFo_LinearBuffer* Std_FiFo_LinearBuffer_BufferHandle;

However, the typedef itself also doesn't add much here. I would just keep the forward declaration of the struct, and just write Std_FiFo_LinearBuffer*. The implementation of Std_FiFo_LinearBuffer will still be hidden from the user.

Return values

Instead of using uint8_t for returning success or failure, use bool instead. You also don't need to create any constants, C provides true and false for you.

The function that creates a new handle could just return a pointer to that handle, and NULL can then signify failure. The function that invalidates a handle can return void; the caller can't do anything useful with a failure anyway.

Sizes

Instead of using uint32_t to store the sizes, counts and indices, use size_t instead. The latter is guaranteed to be large enough to handle everything that can fit into memory on the platform you are on.

Use of volatile

You should not use volatile in this code. volatile variables are not atomic, but they do have a performance impact. Since you wrote that you do not require thread-safety, remove volatile everywhere.

Use of const

You use const in several places where it is not very useful, and you forgot it in places where it would actually make a big difference. Consider:

uint8_t Std_FiFo_LinearBuffer_Put(…, uint32_t* const BufferToCopyFrom, …);

Here you declare BufferToCopyFrom to be a constant pointer to an array of non-constant uint32_ts. Making the pointer value itself constant has little effect; inside Std_FiFo_LinearBuffer_Put() you can just make a non-const copy of the pointer value.

By pointing to a non-const array, you are telling the caller that the values in this array might potentially be overwritten. That's not something that should be done (the put operation should only copy from that array), and it also prevents some compiler optimizations. So you should make it point to a const array instead:

uint8_t Std_FiFo_LinearBuffer_Put(…, const uint32_t* BufferToCopyFrom, …);

Hardcoding parameters

You have hardcoded the size of the FIFO to 16 elements of 64 bytes each. Maybe you really only need that size FIFO? But if you want this to be something you can reuse for different purposes, I think it would make more sense to have the size of the FIFO configurable. This just needs a few extra variables in struct Std_FiFo_LinearBuffer.

Example refactored code

The header file:

#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>

struct FiFo;

FiFo* FiFo_Initialize(size_t maxElements, size_t bufferSize);
void FiFo_Invalidate(FiFo* fifo);
void FiFo_Put(FiFo* fifo, const uint32_t* buffer, size_t size);
void FiFo_Take(FiFo* fifo, uint32_t *buffer, size_t size);
void FiFo_Pop(FiFo* fifo);
…

In the source file:

#include "FiFo.h"

#include <stdlib.h>

struct Element {
    …
};

struct FiFo {
    …
    size_t MaxNumberOfElements;
    size_t BufferSize;

    struct Element* Elements;
    struct Element** ElementMapping;
    uint32_t *Buffer;
};

FiFo* FiFo_Initialize(size_t maxElements, size_t bufferSize) {
    FiFo* fifo = calloc(1, sizeof *fifo);
    if (!fifo) {
        return NULL;
    }

    fifo->MaxNumberOfElements = maxElements;
    fifo->BufferSize = bufferSize;
    …
}

static bool CanPut(FiFo *fifo, size_t size) {
    …
}

static void CopyDataFromTo(…) {
    …
}

bool FiFo_Put(FiFo *fifo, const uint32_t *buffer, size_t size) {
    if (!fifo || !CanPut(fifo, size)) {
        return false;
    }

    CopyDataFromTo(buffer, AllocateNewElement(fifo, size), size);
    return true;
}

Note how the code looks a lot cleaner, just from having shorter names and no need for casting handles to pointers.

Other thoughts

Why is the buffer an array of uint32_ts? Why not make it uint8_t and allow any odd number of bytes to be stored?

Why are there so many public functions? Why have "take" and "pop" instead of a single function that does both? Why have a set of functions that take pointers to uint32_ts and a set that takes element handles? Why allow putting things on the top? That doesn't sound very FIFO-like to me. If you want a double-ended queue, call it that instead of calling it a FIFO.

There are other issues with this code. It seems like you are making this more complicated than necessary, with the expectation that you might need that in the future. Don't do that, instead follow the YAGNI principle.

\$\endgroup\$
1
  • \$\begingroup\$ I was about to review this code and was so thankful that someone else slogged though it. It covers most of the points I saw too. It is the /// <summary> ... style of documentation that lost it for me. I am certain it follows some tool's requirements to extract documentation, yet it makes the .h file drowned with text a human does not need. I look forward to OP's next version. \$\endgroup\$
    – chux
    Commented Nov 19, 2023 at 12:46

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.