Convert ringbuffer to a template
This commit is contained in:
parent
9fc63b3e00
commit
fdb2cbcff0
4 changed files with 209 additions and 217 deletions
|
@ -14,7 +14,7 @@ set(CMAKE_CXX_STANDARD 17)
|
||||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
|
|
||||||
# Add your executable
|
# Add your executable
|
||||||
add_executable(${PROJECT_NAME} main.cpp ringbuffer.cpp)
|
add_executable(${PROJECT_NAME} main.cpp)
|
||||||
|
|
||||||
# Add include directories for your header files
|
# Add include directories for your header files
|
||||||
target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
|
target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
|
||||||
|
|
|
@ -1,70 +1,224 @@
|
||||||
/*
|
/*
|
||||||
* ringbuffer.h
|
* ringbuffer.h
|
||||||
*
|
*
|
||||||
* Created on: Sep 14, 2021
|
* Created on: May 31, 2025
|
||||||
* Author: Attila Body
|
* Author: Attila Body
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <stdbool.h>
|
#include <cstddef>
|
||||||
#include <stdint.h>
|
#include <cstdint>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
namespace f1ll {
|
namespace f1ll {
|
||||||
|
|
||||||
class ringbuffer
|
class iringbuffer
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
ringbuffer(uint8_t *buffer, uint16_t size);
|
using size_type = size_t;
|
||||||
|
|
||||||
/// @brief Copies data to the ring buffer (without committing it) The amount of the committed data in the buffer
|
/// @brief Copies data to the ring buffer (without committing it)
|
||||||
/// should not exceed the size of the buffer.
|
|
||||||
/// @param data Pointer to the data to copy
|
/// @param data Pointer to the data to copy
|
||||||
/// @param len Length of the data to copy
|
/// @param len Length of the data to copy
|
||||||
uint16_t put(uint8_t const *data_buffer, uint16_t len);
|
/// @retval Length of the data copied (differs when not all data can fit)
|
||||||
|
virtual size_type put(uint8_t const *data, size_type len) = 0;
|
||||||
|
|
||||||
/// @brief Commits the data already placed into the buffer and notifies the consumer about it's availability
|
/// @brief Commits the data already placed into the buffer and notifies the
|
||||||
void commit();
|
/// consumer about it's availability
|
||||||
|
virtual void commit() = 0;
|
||||||
|
|
||||||
/// @brief Waits until all the data from the ring buffer gets consumed.
|
/// @brief Waits until all the data from the ring buffer gets consumed.
|
||||||
// void flush();
|
// void flush();
|
||||||
|
|
||||||
/// @brief Gets a pointer to the next chunk of committed data in the buffer without registering the consumption.
|
/// @brief Gets a pointer to the next chunk of committed data in the buffer
|
||||||
/// The caller should also call report_consumption using the returned chunk length after
|
/// without registering the consumption.
|
||||||
/// it finished processing the data.
|
/// The caller should also call report_consumption using the returned
|
||||||
/// @param[in] len_requested Length of the data requested from the buffer. The length of the actual data provided
|
/// chunk length after it finished processing the data.
|
||||||
/// might be actually smaller (because either reaching the end of the buffer or not enough data in the
|
/// @param[in] len_requested Length of the data requested from the buffer.
|
||||||
/// buffer).
|
/// The length of the actual data provided
|
||||||
/// @param[out] data Receives a pointer to the first byte of the available data in the buffer
|
/// might be actually smaller (because either reaching the end of
|
||||||
/// @param[out] len Receives the length of the chunk available in the buffer. Will not exceed len_requested.
|
/// the buffer or not enough data in the buffer).
|
||||||
/// @retval true if the buffer has more available data, false otherwise
|
/// @param[out] data Receives a pointer to the first byte of the available
|
||||||
bool get_chunk(uint16_t len_requested, uint8_t **data, uint16_t *len);
|
/// data in the buffer
|
||||||
|
/// @param[out] len Receives the length of the chunk available in the buffer.
|
||||||
|
/// Will not exceed len_requested.
|
||||||
|
/// @retval true if the buffer has more available data, false otherwise.
|
||||||
|
virtual bool get_chunk(size_type len_requested, uint8_t *&data, size_type &len) = 0;
|
||||||
|
|
||||||
/// @brief Marks the chunk returned by ringbuffer_GetChunk as available
|
/// @brief Marks the chunk returned by ringbuffer_GetChunk as available.
|
||||||
/// @param consumed The length of the chunk as returned by ringbuffer_GetChunk(..., len)
|
/// @param consumed The length of the chunk as returned by
|
||||||
void report_consumption(uint16_t consumed);
|
/// ringbuffer_GetChunk(..., len)
|
||||||
|
virtual void consumed(size_type len) = 0;
|
||||||
|
|
||||||
/// @brief Returns the number of uncommited bytes in the ring buffer
|
/// @brief Returns the number of uncommited bytes in the ring buffer.
|
||||||
uint16_t uncommited() const;
|
virtual size_type uncommited() const = 0;
|
||||||
|
|
||||||
/// @brief Returns the number of commited bytes in the ring buffer
|
/// @brief Returns the number of commited bytes in the ring buffer.
|
||||||
uint16_t commited() const;
|
virtual size_type commited() const = 0;
|
||||||
|
|
||||||
/// @brief Discards the uncommited data in the ring buffer
|
/// @brief Discards the uncommited data in the ring buffer.
|
||||||
void discard();
|
virtual void discard() = 0;
|
||||||
|
|
||||||
private:
|
/// @brief Returns the size of the internal buffer.
|
||||||
uint8_t *m_buffer; //!< Pointer to the phisical memory bufer
|
/// One byte in the buffer is always used as boundary
|
||||||
uint16_t m_size; //!< Size of the memory buffer in bytes
|
virtual size_type size() const = 0;
|
||||||
uint16_t m_head; //!< Write position
|
|
||||||
uint16_t m_head_shadow; //!< Shadowed write position for collecting data before committing it
|
|
||||||
uint16_t m_tail; //!< Read position
|
|
||||||
|
|
||||||
static uint16_t buffer_used(uint16_t size, uint16_t head, uint16_t tail);
|
/// @brief Returns the size of the used place in the buffer
|
||||||
static uint16_t buffer_free(uint16_t size, uint16_t head, uint16_t tail);
|
// (including non-commited data)
|
||||||
|
virtual size_type used() const = 0;
|
||||||
|
|
||||||
uint16_t buffer_used() const;
|
/// @brief Returns the free storage capacity of the buffer
|
||||||
uint16_t buffer_free() const;
|
virtual size_type unused() const = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace
|
template <iringbuffer::size_type SZ> class ringbuffer : public iringbuffer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ringbuffer() = default;
|
||||||
|
|
||||||
|
size_type put(uint8_t const *data, size_type len) override;
|
||||||
|
void commit() override;
|
||||||
|
bool get_chunk(size_type len_requested, uint8_t *&data, size_type &len) override;
|
||||||
|
void consumed(size_type len) override;
|
||||||
|
size_type uncommited() const override;
|
||||||
|
size_type commited() const override;
|
||||||
|
void discard() override;
|
||||||
|
size_type size() const override;
|
||||||
|
size_type used() const override;
|
||||||
|
size_type unused() const override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
uint8_t m_buffer[SZ]; //!< Data bufer
|
||||||
|
size_type m_head = 0; //!< Write position
|
||||||
|
size_type m_head_shadow = 0; //!< Shadowed write position for collecting data
|
||||||
|
//!< before committing it
|
||||||
|
size_type m_tail = 0; //!< Read position
|
||||||
|
|
||||||
|
size_type marker_diff(size_type m1, size_type m2) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
template <iringbuffer::size_type SZ> iringbuffer::size_type ringbuffer<SZ>::put(uint8_t const *data, size_type len)
|
||||||
|
{
|
||||||
|
size_type chunk1 = 0;
|
||||||
|
size_type chunk2 = 0;
|
||||||
|
|
||||||
|
if (!data || !len) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_type max_len = unused();
|
||||||
|
len = len < max_len ? len : max_len;
|
||||||
|
|
||||||
|
chunk1 = sizeof(m_buffer) - m_head_shadow;
|
||||||
|
if (chunk1 >= len) {
|
||||||
|
chunk1 = len;
|
||||||
|
} else {
|
||||||
|
chunk2 = len - chunk1;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::memcpy(m_buffer + m_head_shadow, data, chunk1);
|
||||||
|
m_head_shadow += chunk1;
|
||||||
|
if (m_head_shadow == sizeof(m_buffer)) {
|
||||||
|
m_head_shadow = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (chunk2) {
|
||||||
|
std::memcpy(m_buffer, data + chunk1, chunk2);
|
||||||
|
m_head_shadow += chunk2;
|
||||||
|
if (m_head_shadow == sizeof(m_buffer)) {
|
||||||
|
m_head_shadow = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <iringbuffer::size_type SZ> void ringbuffer<SZ>::commit()
|
||||||
|
{
|
||||||
|
iringbuffer::size_type uncommitted = marker_diff(m_head_shadow, m_head);
|
||||||
|
|
||||||
|
if (!uncommitted) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_head = m_head_shadow;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <iringbuffer::size_type SZ> bool ringbuffer<SZ>::get_chunk(size_type len_requested, uint8_t *&data, size_type &len)
|
||||||
|
{
|
||||||
|
if (!len_requested) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_type head = m_head;
|
||||||
|
size_type tail = m_tail;
|
||||||
|
size_type chunk_size = head >= tail ? head - tail : sizeof(m_buffer) - tail;
|
||||||
|
|
||||||
|
if (!chunk_size) {
|
||||||
|
len = 0;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (chunk_size > len_requested) {
|
||||||
|
chunk_size = len_requested;
|
||||||
|
}
|
||||||
|
data = m_buffer + tail;
|
||||||
|
len = chunk_size;
|
||||||
|
|
||||||
|
tail += chunk_size;
|
||||||
|
if (tail == sizeof(m_buffer)) {
|
||||||
|
tail = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return tail != head;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <iringbuffer::size_type SZ> void ringbuffer<SZ>::consumed(size_type len)
|
||||||
|
{
|
||||||
|
if (!len) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
m_tail += len;
|
||||||
|
if (m_tail == sizeof(m_buffer)) {
|
||||||
|
m_tail = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <iringbuffer::size_type SZ> iringbuffer::size_type ringbuffer<SZ>::uncommited() const
|
||||||
|
{
|
||||||
|
return marker_diff(m_head_shadow, m_head);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <iringbuffer::size_type SZ> iringbuffer::size_type ringbuffer<SZ>::commited() const
|
||||||
|
{
|
||||||
|
return marker_diff(m_head, m_tail);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <iringbuffer::size_type SZ> void ringbuffer<SZ>::discard()
|
||||||
|
{
|
||||||
|
m_head_shadow = m_head;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <iringbuffer::size_type SZ> iringbuffer::size_type ringbuffer<SZ>::size() const
|
||||||
|
{
|
||||||
|
return SZ;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <iringbuffer::size_type SZ> iringbuffer::size_type ringbuffer<SZ>::used() const
|
||||||
|
{
|
||||||
|
return marker_diff(m_head_shadow, m_tail);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <iringbuffer::size_type SZ> iringbuffer::size_type ringbuffer<SZ>::unused() const
|
||||||
|
{
|
||||||
|
return marker_diff(m_tail, m_head_shadow) - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <iringbuffer::size_type SZ> iringbuffer::size_type ringbuffer<SZ>::marker_diff(size_type m1, size_type m2) const
|
||||||
|
{
|
||||||
|
return m1 >= m2 ? m1 - m2 : sizeof(m_buffer) - m2 + m1;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace f1ll
|
10
main.cpp
10
main.cpp
|
@ -6,12 +6,10 @@ using namespace std;
|
||||||
|
|
||||||
int main()
|
int main()
|
||||||
{
|
{
|
||||||
uint8_t rb_buffer[8];
|
f1ll::ringbuffer<8> rb;
|
||||||
uint8_t dst_buffer[sizeof(rb_buffer) - 1];
|
uint8_t dst_buffer[7];
|
||||||
uint8_t data1[4] = {0, 1, 2, 3};
|
uint8_t data1[] = {0, 1, 2, 3};
|
||||||
uint8_t data2[4] = {4, 5, 6, 7};
|
uint8_t data2[] = {4, 5, 6, 7};
|
||||||
|
|
||||||
f1ll::ringbuffer rb(rb_buffer, sizeof(rb_buffer));
|
|
||||||
|
|
||||||
int16_t added = rb.put(data1, sizeof(data1));
|
int16_t added = rb.put(data1, sizeof(data1));
|
||||||
cout << "Added " << added << " bytes to the buffer. It reports " << rb.uncommited() << " uncommited and " << rb.commited()
|
cout << "Added " << added << " bytes to the buffer. It reports " << rb.uncommited() << " uncommited and " << rb.commited()
|
||||||
|
|
160
ringbuffer.cpp
160
ringbuffer.cpp
|
@ -1,160 +0,0 @@
|
||||||
/*
|
|
||||||
* ringbuffer.c
|
|
||||||
*
|
|
||||||
* Created on: Sep 14, 2021
|
|
||||||
* Author: Attila Body
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <f1ll/ringbuffer.h>
|
|
||||||
// #include "macro_utils.h"
|
|
||||||
// #include "print_string.h"
|
|
||||||
// #include "taskregistry.h"
|
|
||||||
|
|
||||||
#include <cstring>
|
|
||||||
|
|
||||||
#define __ASSERT(x) assert_param(x)
|
|
||||||
|
|
||||||
namespace f1ll {
|
|
||||||
|
|
||||||
ringbuffer::ringbuffer(uint8_t *buffer, uint16_t size)
|
|
||||||
: m_buffer(buffer),
|
|
||||||
m_size(size),
|
|
||||||
m_head(0),
|
|
||||||
m_head_shadow(0),
|
|
||||||
m_tail(0)
|
|
||||||
{
|
|
||||||
// __ASSERT(handle);
|
|
||||||
// __ASSERT(buffer);
|
|
||||||
// __ASSERT(buffer_size);
|
|
||||||
}
|
|
||||||
|
|
||||||
uint16_t ringbuffer::buffer_used(uint16_t size, uint16_t head, uint16_t tail)
|
|
||||||
{
|
|
||||||
return head >= tail ? head - tail : size - tail + head;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint16_t ringbuffer::buffer_free(uint16_t size, uint16_t head, uint16_t tail)
|
|
||||||
{
|
|
||||||
return size - buffer_used(size, head, tail) - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint16_t ringbuffer::buffer_used() const
|
|
||||||
{
|
|
||||||
return m_head_shadow >= m_tail ? m_head_shadow - m_tail : m_size - m_tail + m_head_shadow;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint16_t ringbuffer::buffer_free() const
|
|
||||||
{
|
|
||||||
return m_size - buffer_used() - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint16_t ringbuffer::put(uint8_t const *data_buffer, uint16_t len)
|
|
||||||
{
|
|
||||||
uint16_t chunk1 = 0;
|
|
||||||
uint16_t chunk2 = 0;
|
|
||||||
|
|
||||||
if (!data_buffer || !len) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint16_t max_len = buffer_free(m_size, m_head_shadow, m_tail);
|
|
||||||
len = len < max_len ? len : max_len;
|
|
||||||
|
|
||||||
chunk1 = m_size - m_head_shadow;
|
|
||||||
if (chunk1 >= len) {
|
|
||||||
chunk1 = len;
|
|
||||||
} else {
|
|
||||||
chunk2 = len - chunk1;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::memcpy(m_buffer + m_head_shadow, data_buffer, chunk1);
|
|
||||||
m_head_shadow += chunk1;
|
|
||||||
if (m_head_shadow == m_size) {
|
|
||||||
m_head_shadow = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (chunk2) {
|
|
||||||
std::memcpy(m_buffer, data_buffer + chunk1, chunk2);
|
|
||||||
m_head_shadow += chunk2;
|
|
||||||
if (m_head_shadow == m_size) {
|
|
||||||
m_head_shadow = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return len;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ringbuffer::commit()
|
|
||||||
{
|
|
||||||
|
|
||||||
uint16_t uncommitted = buffer_used(m_size, m_head_shadow, m_head);
|
|
||||||
|
|
||||||
if (!uncommitted) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_head = m_head_shadow;
|
|
||||||
}
|
|
||||||
|
|
||||||
// void ringbuffe::flush()
|
|
||||||
// {
|
|
||||||
// while (handle->m_head != handle->m_tail) {
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
bool ringbuffer::get_chunk(uint16_t len_requested, uint8_t **data, uint16_t *len)
|
|
||||||
{
|
|
||||||
if (!len_requested || !data || !*data || !len) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint16_t head = m_head;
|
|
||||||
uint16_t tail = m_tail;
|
|
||||||
uint16_t chunk_size = head >= tail ? head - tail : m_size - tail;
|
|
||||||
|
|
||||||
if (!chunk_size) {
|
|
||||||
*len = 0;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (chunk_size > len_requested) {
|
|
||||||
chunk_size = len_requested;
|
|
||||||
}
|
|
||||||
*data = m_buffer + tail;
|
|
||||||
*len = chunk_size;
|
|
||||||
|
|
||||||
tail += chunk_size;
|
|
||||||
if (tail == m_size) {
|
|
||||||
tail = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return tail != head;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ringbuffer::report_consumption(uint16_t consumed)
|
|
||||||
{
|
|
||||||
if (!consumed) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
m_tail += consumed;
|
|
||||||
if (m_tail == m_size) {
|
|
||||||
m_tail = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
uint16_t ringbuffer::commited() const
|
|
||||||
{
|
|
||||||
return buffer_used(m_size, m_head, m_tail);
|
|
||||||
}
|
|
||||||
|
|
||||||
uint16_t ringbuffer::uncommited() const
|
|
||||||
{
|
|
||||||
return buffer_used(m_size, m_head_shadow, m_head);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ringbuffer::discard()
|
|
||||||
{
|
|
||||||
m_head_shadow = m_head;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace f1ll
|
|
Loading…
Add table
Add a link
Reference in a new issue