290 lines
8.4 KiB
C++
290 lines
8.4 KiB
C++
/*
|
||
Copyright (c) 2026 - SD-Innovation S.A.S. - FRANCE
|
||
*/
|
||
|
||
/*
|
||
ver: 2.x.x - build: 2026-04-28
|
||
*/
|
||
|
||
/*
|
||
The zlib License
|
||
|
||
Copyright (c) 2026 SD-Innovation S.A.S.
|
||
|
||
This software is provided ‘as-is’, without any express or implied
|
||
warranty. In no event will the authors be held liable for any damages
|
||
arising from the use of this software.
|
||
|
||
Permission is granted to anyone to use this software for any purpose,
|
||
including commercial applications, and to alter it and redistribute it
|
||
freely, subject to the following restrictions:
|
||
|
||
1. The origin of this software must not be misrepresented; you must not
|
||
claim that you wrote the original software. If you use this software
|
||
in a product, an acknowledgment in the product documentation would be
|
||
appreciated but is not required.
|
||
|
||
2. Altered source versions must be plainly marked as such, and must not be
|
||
misrepresented as being the original software.
|
||
|
||
3. This notice may not be removed or altered from any source
|
||
distribution.
|
||
*/
|
||
|
||
#pragma once
|
||
|
||
#include "ringBuffer.h"
|
||
|
||
namespace sdi_toolBox::common::utils
|
||
{
|
||
//--------------------------------------------------------------
|
||
/**
|
||
* @brief Fixed-size circular buffer with compile-time capacity
|
||
* and no-overwrite behavior.
|
||
*
|
||
* This template wraps a RingBuffer and prevents new elements from being
|
||
* written when the buffer is full. Existing data is never overwritten;
|
||
* push() simply fails and returns false when capacity is reached.
|
||
*
|
||
* @tparam T Element type stored in the buffer.
|
||
* @tparam CAPACITY Compile-time buffer capacity. Must be > 0.
|
||
*
|
||
* @note The class provides both optional-returning and out-parameter
|
||
* overloads for pop/front/back to suit different runtime constraints.
|
||
* @warning Instantiating with CAPACITY == 0 is forbidden (static_assert).
|
||
*
|
||
* @code{.cpp}
|
||
* CircularBuffer<int, 8> cb;
|
||
* if (cb.push(42))
|
||
* {
|
||
* // element was inserted
|
||
* }
|
||
* else
|
||
* {
|
||
* // buffer was full, element was discarded
|
||
* }
|
||
* @endcode
|
||
*/
|
||
template<class T, std::size_t CAPACITY>
|
||
class CircularBuffer
|
||
{
|
||
static_assert(CAPACITY > 0, "CAPACITY must be > 0");
|
||
|
||
public:
|
||
///@name Write operations
|
||
///@{
|
||
|
||
/**
|
||
* @brief Try to append a value to the buffer.
|
||
*
|
||
* If the buffer is full, the value is discarded and no overwrite occurs.
|
||
* @param value Value to append (copied).
|
||
*
|
||
* @return true if the value was inserted, false if the buffer was full.
|
||
*/
|
||
bool push(const T &value);
|
||
|
||
///@}
|
||
///@name Read operations
|
||
///@{
|
||
|
||
/**
|
||
* @brief Remove and return the oldest element from the buffer.
|
||
*
|
||
* @return std::optional<T> The oldest element if present, std::nullopt if empty.
|
||
*/
|
||
|
||
std::optional<T> pop();
|
||
/**
|
||
* @brief Remove the oldest element and store it in the provided reference.
|
||
*
|
||
* @param value Output reference that receives the removed element.
|
||
*
|
||
* @return true if an element was removed, false if the buffer was empty.
|
||
*/
|
||
bool pop(T &value);
|
||
|
||
/**
|
||
* @brief Return (without removing) the oldest element.
|
||
*
|
||
* @return std::optional<T> The oldest element if present, std::nullopt if empty.
|
||
*/
|
||
[[nodiscard]] std::optional<T> front() const;
|
||
|
||
/**
|
||
* @brief Copy the oldest element into the provided reference without removing it.
|
||
*
|
||
* @param value Output reference that receives the element.
|
||
*
|
||
* @return true if the element was copied, false if the buffer is empty.
|
||
*/
|
||
[[nodiscard]] bool front(T &value) const;
|
||
|
||
/**
|
||
* @brief Return (without removing) the newest element.
|
||
*
|
||
* @return std::optional<T> The newest element if present, std::nullopt if empty.
|
||
*/
|
||
[[nodiscard]] std::optional<T> back() const;
|
||
|
||
/**
|
||
* @brief Copy the newest element into the provided reference without removing it.
|
||
*
|
||
* @param value Output reference that receives the element.
|
||
*
|
||
* @return true if the element was copied, false if the buffer is empty.
|
||
*/
|
||
[[nodiscard]] bool back(T &value) const;
|
||
|
||
///@}
|
||
///@name State queries
|
||
///@{
|
||
|
||
/**
|
||
* @brief Check whether the buffer is empty.
|
||
*
|
||
* @return true if empty, false otherwise.
|
||
*/
|
||
[[nodiscard]] bool empty() const;
|
||
|
||
/**
|
||
* @brief Check whether the buffer is full.
|
||
*
|
||
* @return true if full, false otherwise.
|
||
*/
|
||
[[nodiscard]] bool full() const;
|
||
|
||
/**
|
||
* @brief Number of elements currently stored in the buffer.
|
||
*
|
||
* @return Current size (0 .. CAPACITY).
|
||
*/
|
||
[[nodiscard]] std::size_t size() const;
|
||
|
||
/**
|
||
* @brief Compile-time capacity of the buffer.
|
||
*
|
||
* @return The maximum number of elements the buffer can hold.
|
||
*/
|
||
[[nodiscard]] constexpr std::size_t capacity() const;
|
||
|
||
///@}
|
||
///@name Modifiers
|
||
///@{
|
||
|
||
/**
|
||
* @brief Clear the buffer and reset internal indices.
|
||
*
|
||
* After calling clear(), empty() returns true and size() returns 0.
|
||
*/
|
||
void clear();
|
||
|
||
///@}
|
||
|
||
private:
|
||
///@name Data members
|
||
///@{
|
||
|
||
RingBuffer<T, CAPACITY> m_buffer{}; ///< Underlying ring buffer
|
||
|
||
///@}
|
||
};
|
||
//--------------------------------------------------------------
|
||
|
||
//--------------------------------------------------------------
|
||
/* Try to append a value to the buffer. If the buffer is full,
|
||
* the value is discarded. Return true if the value was inserted,
|
||
* false if the buffer was full. */
|
||
template<class T, std::size_t CAPACITY>
|
||
bool CircularBuffer<T, CAPACITY>::push(const T &value)
|
||
{
|
||
if (m_buffer.full())
|
||
return false;
|
||
return m_buffer.push(value);
|
||
}
|
||
//--------------------------------------------------------------
|
||
/* Remove and return the oldest value from the buffer. If the
|
||
* buffer is empty, return std::nullopt */
|
||
template<class T, std::size_t CAPACITY>
|
||
std::optional<T> CircularBuffer<T, CAPACITY>::pop()
|
||
{
|
||
return m_buffer.pop();
|
||
}
|
||
//--------------------------------------------------------------
|
||
/* Remove the oldest value from the buffer and store it in
|
||
* 'value'. Return true if successful, false if the buffer is empty */
|
||
template<class T, std::size_t CAPACITY>
|
||
bool CircularBuffer<T, CAPACITY>::pop(T &value)
|
||
{
|
||
return m_buffer.pop(value);
|
||
}
|
||
//--------------------------------------------------------------
|
||
/* Return the oldest value without removing it. If the buffer is
|
||
* empty, return std::nullopt */
|
||
template<class T, std::size_t CAPACITY>
|
||
std::optional<T> CircularBuffer<T, CAPACITY>::front() const
|
||
{
|
||
return m_buffer.front();
|
||
}
|
||
//--------------------------------------------------------------
|
||
/* Return the oldest value without removing it and store it in
|
||
* 'value'. Return true if successful, false if the buffer is empty */
|
||
template<class T, std::size_t CAPACITY>
|
||
bool CircularBuffer<T, CAPACITY>::front(T &value) const
|
||
{
|
||
return m_buffer.front(value);
|
||
}
|
||
//--------------------------------------------------------------
|
||
/* Return the newest value without removing it. If the buffer is
|
||
* empty, return std::nullopt */
|
||
template<class T, std::size_t CAPACITY>
|
||
std::optional<T> CircularBuffer<T, CAPACITY>::back() const
|
||
{
|
||
return m_buffer.back();
|
||
}
|
||
//--------------------------------------------------------------
|
||
/* Return the newest value without removing it and store it in
|
||
* 'value'. Return true if successful, false if the buffer is empty */
|
||
template<class T, std::size_t CAPACITY>
|
||
bool CircularBuffer<T, CAPACITY>::back(T &value) const
|
||
{
|
||
return m_buffer.back(value);
|
||
}
|
||
//--------------------------------------------------------------
|
||
/* Check if the buffer is empty */
|
||
template<class T, std::size_t CAPACITY>
|
||
bool CircularBuffer<T, CAPACITY>::empty() const
|
||
{
|
||
return m_buffer.empty();
|
||
}
|
||
//--------------------------------------------------------------
|
||
/* Check if the buffer is full */
|
||
template<class T, std::size_t CAPACITY>
|
||
bool CircularBuffer<T, CAPACITY>::full() const
|
||
{
|
||
return m_buffer.full();
|
||
}
|
||
//--------------------------------------------------------------
|
||
/* Get the number of elements currently in the buffer */
|
||
template<class T, std::size_t CAPACITY>
|
||
std::size_t CircularBuffer<T, CAPACITY>::size() const
|
||
{
|
||
return m_buffer.size();
|
||
}
|
||
//--------------------------------------------------------------
|
||
/* Get the maximum capacity of the buffer */
|
||
template<class T, std::size_t CAPACITY>
|
||
[[nodiscard]] constexpr std::size_t CircularBuffer<T, CAPACITY>::capacity() const
|
||
{
|
||
return CAPACITY;
|
||
}
|
||
//--------------------------------------------------------------
|
||
/* Clear the buffer, resetting it to an empty state */
|
||
template<class T, std::size_t CAPACITY>
|
||
void CircularBuffer<T, CAPACITY>::clear()
|
||
{
|
||
m_buffer.clear();
|
||
}
|
||
//--------------------------------------------------------------
|
||
} // namespace sdi_toolBox::common::utils
|