From 5135cdca0c2a3bccfd97660afaf0b3299f0ea999 Mon Sep 17 00:00:00 2001 From: Attila Body Date: Sat, 31 May 2025 21:38:09 +0200 Subject: [PATCH] Add ringbuffer --- components/f1ll/CMakeLists.txt | 1 + components/f1ll/inc/f1ll/ringbuffer.h | 70 +++++++++++ components/f1ll/src/ringbuffer.cpp | 160 ++++++++++++++++++++++++++ 3 files changed, 231 insertions(+) create mode 100644 components/f1ll/inc/f1ll/ringbuffer.h create mode 100644 components/f1ll/src/ringbuffer.cpp diff --git a/components/f1ll/CMakeLists.txt b/components/f1ll/CMakeLists.txt index e1f412c..b155cf7 100644 --- a/components/f1ll/CMakeLists.txt +++ b/components/f1ll/CMakeLists.txt @@ -3,6 +3,7 @@ add_library(f1ll STATIC src/dma_helper.cpp src/str_util.cpp src/console_handler.cpp + src/ringbuffer.cpp ) target_include_directories(f1ll PUBLIC diff --git a/components/f1ll/inc/f1ll/ringbuffer.h b/components/f1ll/inc/f1ll/ringbuffer.h new file mode 100644 index 0000000..ccdaab2 --- /dev/null +++ b/components/f1ll/inc/f1ll/ringbuffer.h @@ -0,0 +1,70 @@ +/* + * ringbuffer.h + * + * Created on: Sep 14, 2021 + * Author: Attila Body + */ + +#pragma once + +#include +#include + +namespace f1ll { + +class ringbuffer +{ +public: + ringbuffer(uint8_t *buffer, uint16_t size); + + /// @brief Copies data to the ring buffer (without committing it) The amount of the committed data in the buffer + /// should not exceed the size of the buffer. + /// @param data Pointer to the data to copy + /// @param len Length of the data to copy + uint16_t put(uint8_t const *data_buffer, uint16_t len); + + /// @brief Commits the data already placed into the buffer and notifies the consumer about it's availability + void commit(); + + /// @brief Waits until all the data from the ring buffer gets consumed. + // void flush(); + + /// @brief Gets a pointer to the next chunk of committed data in the buffer without registering the consumption. + /// The caller should also call report_consumption using the returned chunk length after + /// it finished processing the data. + /// @param[in] len_requested Length of the data requested from the buffer. The length of the actual data provided + /// might be actually smaller (because either reaching the end of the buffer or not enough data in the + /// buffer). + /// @param[out] data Receives a pointer to the first byte of the available 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 + bool get_chunk(uint16_t len_requested, uint8_t **data, uint16_t *len); + + /// @brief Marks the chunk returned by ringbuffer_GetChunk as available + /// @param consumed The length of the chunk as returned by ringbuffer_GetChunk(..., len) + void report_consumption(uint16_t consumed); + + /// @brief Returns the number of uncommited bytes in the ring buffer + uint16_t uncommited() const; + + /// @brief Returns the number of commited bytes in the ring buffer + uint16_t commited() const; + + /// @brief Discards the uncommited data in the ring buffer + void discard(); + +private: + uint8_t *m_buffer; //!< Pointer to the phisical memory bufer + uint16_t m_size; //!< Size of the memory buffer in bytes + 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); + static uint16_t buffer_free(uint16_t size, uint16_t head, uint16_t tail); + + uint16_t buffer_used() const; + uint16_t buffer_free() const; +}; + +} // namespace \ No newline at end of file diff --git a/components/f1ll/src/ringbuffer.cpp b/components/f1ll/src/ringbuffer.cpp new file mode 100644 index 0000000..d2c42b1 --- /dev/null +++ b/components/f1ll/src/ringbuffer.cpp @@ -0,0 +1,160 @@ +/* + * ringbuffer.c + * + * Created on: Sep 14, 2021 + * Author: Attila Body + */ + +#include +// #include "macro_utils.h" +// #include "print_string.h" +// #include "taskregistry.h" + +#include + +#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 \ No newline at end of file