Add ringbuffer

This commit is contained in:
Attila Body 2025-05-31 21:38:09 +02:00
parent 45141798d8
commit 6f911d717e
Signed by: abody
GPG key ID: BD0C6214E68FB5CF
3 changed files with 233 additions and 0 deletions

View file

@ -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

View file

@ -0,0 +1,70 @@
/*
* ringbuffer.h
*
* Created on: Sep 14, 2021
* Author: Attila Body
*/
#pragma once
#include <stdbool.h>
#include <stdint.h>
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

View file

@ -0,0 +1,162 @@
/*
* 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>
#include <stm32_assert.h>
#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 buffer_used(uint16_t size, uint16_t head, uint16_t tail)
{
return head >= tail ? head - tail : size - tail + head;
}
uint16_t buffer_free(uint16_t size, uint16_t head, uint16_t tail)
{
return size - buffer_used(size, head, tail);
}
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();
}
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()
{
return buffer_used(m_size, m_tail, m_head);
}
uint16_t ringbuffer::uncommited()
{
return buffer_used(m_size, m_head_shadow, m_head);
}
void ringbuffer::discard()
{
m_head_shadow = m_head;
}
} // namespace