Initial commit
This commit is contained in:
commit
5ee5c5cb2e
9 changed files with 461 additions and 0 deletions
33
.clang-format
Normal file
33
.clang-format
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
BasedOnStyle: LLVM
|
||||||
|
UseTab: Never
|
||||||
|
IndentWidth: 2
|
||||||
|
TabWidth: 2
|
||||||
|
BreakBeforeBraces: Custom
|
||||||
|
AllowShortFunctionsOnASingleLine: Empty
|
||||||
|
AllowShortIfStatementsOnASingleLine: false
|
||||||
|
AllowShortLambdasOnASingleLine: true
|
||||||
|
AllowAllArgumentsOnNextLine: true
|
||||||
|
IndentCaseLabels: true
|
||||||
|
AccessModifierOffset: -2
|
||||||
|
NamespaceIndentation: None
|
||||||
|
FixNamespaceComments: false
|
||||||
|
PackConstructorInitializers: Never
|
||||||
|
AlignAfterOpenBracket: AlwaysBreak
|
||||||
|
InsertBraces: true
|
||||||
|
BraceWrapping:
|
||||||
|
AfterClass: true # false
|
||||||
|
AfterControlStatement: false
|
||||||
|
AfterEnum: true # false
|
||||||
|
AfterFunction: true # false
|
||||||
|
AfterNamespace: false
|
||||||
|
AfterObjCDeclaration: true # false
|
||||||
|
AfterStruct: true # false
|
||||||
|
AfterUnion: true # false
|
||||||
|
AfterExternBlock: false
|
||||||
|
BeforeCatch: false
|
||||||
|
BeforeElse: false
|
||||||
|
IndentBraces: false
|
||||||
|
SplitEmptyFunction: true
|
||||||
|
SplitEmptyRecord: true
|
||||||
|
SplitEmptyNamespace: true
|
||||||
|
ColumnLimit: 140
|
6
.gitignore
vendored
Normal file
6
.gitignore
vendored
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
/build/
|
||||||
|
/.cache/
|
||||||
|
CMakeFiles/
|
||||||
|
CMakeCache.txt
|
||||||
|
cmake_install.cmake
|
||||||
|
build.ninja
|
25
.vscode/launch.json
vendored
Normal file
25
.vscode/launch.json
vendored
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
{
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"name": "Debug RingBufferTest",
|
||||||
|
"type": "cppdbg",
|
||||||
|
"request": "launch",
|
||||||
|
"program": "${workspaceFolder}/build/ringbuffer_test", // Adjust path if your build directory is different
|
||||||
|
"args": [],
|
||||||
|
"stopAtEntry": false,
|
||||||
|
"cwd": "${workspaceFolder}/build",
|
||||||
|
"environment": [],
|
||||||
|
"externalConsole": false,
|
||||||
|
"MIMode": "gdb", // Or "lldb" if you're on macOS/Linux and prefer lldb
|
||||||
|
"setupCommands": [
|
||||||
|
{
|
||||||
|
"description": "Enable pretty-printing for gdb",
|
||||||
|
"text": "-enable-pretty-printing",
|
||||||
|
"ignoreFailures": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"preLaunchTask": "build_debug" // This task will build your project before debugging
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
58
.vscode/tasks.json
vendored
Normal file
58
.vscode/tasks.json
vendored
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
{
|
||||||
|
"version": "2.0.0",
|
||||||
|
"tasks": [
|
||||||
|
{
|
||||||
|
"label": "configure_debug",
|
||||||
|
"type": "shell",
|
||||||
|
"command": "cmake -B ${workspaceFolder}/build -S ${workspaceFolder} -DCMAKE_BUILD_TYPE=Debug",
|
||||||
|
"group": "build",
|
||||||
|
"presentation": {
|
||||||
|
"reveal": "silent",
|
||||||
|
"panel": "shared"
|
||||||
|
},
|
||||||
|
"problemMatcher": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "build_debug",
|
||||||
|
"type": "shell",
|
||||||
|
"command": "cmake --build ${workspaceFolder}/build --config Debug",
|
||||||
|
"group": {
|
||||||
|
"kind": "build",
|
||||||
|
"isDefault": true
|
||||||
|
},
|
||||||
|
"dependsOn": "configure_debug",
|
||||||
|
"presentation": {
|
||||||
|
"reveal": "always",
|
||||||
|
"panel": "shared"
|
||||||
|
},
|
||||||
|
"problemMatcher": [
|
||||||
|
"$gcc" // Or "$msCompile" for MSVC
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "configure_release",
|
||||||
|
"type": "shell",
|
||||||
|
"command": "cmake -B ${workspaceFolder}/build -S ${workspaceFolder} -DCMAKE_BUILD_TYPE=Release",
|
||||||
|
"group": "build",
|
||||||
|
"presentation": {
|
||||||
|
"reveal": "silent",
|
||||||
|
"panel": "shared"
|
||||||
|
},
|
||||||
|
"problemMatcher": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "build_release",
|
||||||
|
"type": "shell",
|
||||||
|
"command": "cmake --build ${workspaceFolder}/build --config Release",
|
||||||
|
"group": "build",
|
||||||
|
"dependsOn": "configure_release",
|
||||||
|
"presentation": {
|
||||||
|
"reveal": "always",
|
||||||
|
"panel": "shared"
|
||||||
|
},
|
||||||
|
"problemMatcher": [
|
||||||
|
"$gcc"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
18
CMakeLists.txt
Normal file
18
CMakeLists.txt
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
cmake_minimum_required(VERSION 3.10) # Or a newer version if you prefer
|
||||||
|
|
||||||
|
project(RingBufferTest CXX)
|
||||||
|
|
||||||
|
# Specify the C++ standard to use (e.g., C++17)
|
||||||
|
set(CMAKE_CXX_STANDARD 17)
|
||||||
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
|
|
||||||
|
# Add your executable
|
||||||
|
add_executable(ringbuffer_test main.cpp ringbuffer.cpp)
|
||||||
|
|
||||||
|
# Add include directories for your header files
|
||||||
|
target_include_directories(ringbuffer_test PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
|
||||||
|
|
||||||
|
# Add debugging flags (important for debugging)
|
||||||
|
if (CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||||
|
target_compile_options(ringbuffer_test PRIVATE -g)
|
||||||
|
endif()
|
65
CMakePresets.json
Normal file
65
CMakePresets.json
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
{
|
||||||
|
"version": 6,
|
||||||
|
"cmakeMinimumRequired": {
|
||||||
|
"major": 3,
|
||||||
|
"minor": 23,
|
||||||
|
"patch": 0
|
||||||
|
},
|
||||||
|
"configurePresets": [
|
||||||
|
{
|
||||||
|
"name": "base",
|
||||||
|
"hidden": true,
|
||||||
|
"generator": "Ninja", // Or "Unix Makefiles", "MinGW Makefiles", etc.
|
||||||
|
"binaryDir": "${sourceDir}/build/${presetName}",
|
||||||
|
"cacheVariables": {
|
||||||
|
"CMAKE_CXX_STANDARD": "17",
|
||||||
|
"CMAKE_CXX_STANDARD_REQUIRED": "ON",
|
||||||
|
"CMAKE_EXPORT_COMPILE_COMMANDS": "ON"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "debug",
|
||||||
|
"displayName": "Debug Build",
|
||||||
|
"inherits": "base",
|
||||||
|
"cacheVariables": {
|
||||||
|
"CMAKE_BUILD_TYPE": "Debug"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "release",
|
||||||
|
"displayName": "Release Build",
|
||||||
|
"inherits": "base",
|
||||||
|
"cacheVariables": {
|
||||||
|
"CMAKE_BUILD_TYPE": "Release"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"buildPresets": [
|
||||||
|
{
|
||||||
|
"name": "debug",
|
||||||
|
"displayName": "Debug Build",
|
||||||
|
"configurePreset": "debug",
|
||||||
|
"targets": ["ringbuffer_test"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "release",
|
||||||
|
"displayName": "Release Build",
|
||||||
|
"configurePreset": "release",
|
||||||
|
"targets": ["ringbuffer_test"]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"testPresets": [
|
||||||
|
{
|
||||||
|
"name": "default",
|
||||||
|
"displayName": "Run Tests",
|
||||||
|
"configurePreset": "debug", // Use the debug configuration for running tests
|
||||||
|
"output": {
|
||||||
|
"outputOnFailure": true
|
||||||
|
},
|
||||||
|
"execution": {
|
||||||
|
"noTestsAction": "error",
|
||||||
|
"stopOnFailure": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
70
f1ll/ringbuffer.h
Normal file
70
f1ll/ringbuffer.h
Normal 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
|
26
main.cpp
Normal file
26
main.cpp
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
#include <f1ll/ringbuffer.h>
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
uint8_t rb_buffer[8];
|
||||||
|
uint8_t dst_buffer[sizeof(rb_buffer) - 1];
|
||||||
|
uint8_t data1[4] = {0, 1, 2, 3};
|
||||||
|
uint8_t data2[4] = {4, 5, 6, 7};
|
||||||
|
|
||||||
|
f1ll::ringbuffer rb(rb_buffer, sizeof(rb_buffer));
|
||||||
|
|
||||||
|
int16_t added = rb.put(data1, sizeof(data1));
|
||||||
|
cout << "Added " << added << " bytes to the buffer. It reports " << rb.uncommited() << " uncommited and " << rb.commited()
|
||||||
|
<< " commited bytes." << endl;
|
||||||
|
rb.commit();
|
||||||
|
cout << "After commit, there are " << rb.commited() << " commited and " << rb.uncommited() << " uncommited bytes in the buffer" << endl;
|
||||||
|
added = rb.put(data1, sizeof(data1));
|
||||||
|
cout << "Added " << added << " bytes to the buffer. It reports " << rb.uncommited() << " uncommited and " << rb.commited()
|
||||||
|
<< " commited bytes." << endl;
|
||||||
|
rb.commit();
|
||||||
|
cout << "After commit, there are " << rb.commited() << " commited and " << rb.uncommited() << " uncommited bytes in the buffer" << endl;
|
||||||
|
}
|
160
ringbuffer.cpp
Normal file
160
ringbuffer.cpp
Normal file
|
@ -0,0 +1,160 @@
|
||||||
|
/*
|
||||||
|
* 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