From 74a75c8f0d3701f3d1413afbc4c31b7ec05adbac Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 19 May 2026 16:03:03 +0000 Subject: [PATCH 1/8] fix: propagate LFBB_MULTICORE_HOSTED value and expose configuration as PUBLIC MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The previous CMake code added a bare `-DLFBB_MULTICORE_HOSTED` regardless of the value the user passed, so `cmake -DLFBB_MULTICORE_HOSTED=false` would still enable multicore mode (the bare macro expands to 1). The defines were also PRIVATE, which meant the struct layout in the header differed between the library build and consumer builds — a silent ABI mismatch when either option was set. Both defines are now PUBLIC and forward the user-supplied value. --- CMakeLists.txt | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c73a7a1..6bd72bc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,12 +12,15 @@ add_subdirectory(lfbb) # Library configuration if (DEFINED LFBB_MULTICORE_HOSTED) - target_compile_definitions(${PROJECT_NAME} PRIVATE LFBB_MULTICORE_HOSTED) + target_compile_definitions(${PROJECT_NAME} + PUBLIC + LFBB_MULTICORE_HOSTED=${LFBB_MULTICORE_HOSTED} + ) endif() if (DEFINED LFBB_CACHELINE_LENGTH) target_compile_definitions(${PROJECT_NAME} - PRIVATE + PUBLIC LFBB_CACHELINE_LENGTH=${LFBB_CACHELINE_LENGTH} ) endif() From 9cfad4db65323345f67183f9b6216d0af7734e34 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 19 May 2026 16:03:13 +0000 Subject: [PATCH 2/8] fix: assert read amount stays within buffer in LFBB_ReadRelease LFBB_WriteRelease already asserts `w + written <= inst->size` to catch callers releasing more than they acquired. Add the symmetric check on the read side so misuse is caught in debug builds rather than silently corrupting the read index. --- lfbb/src/lfbb.c | 1 + 1 file changed, 1 insertion(+) diff --git a/lfbb/src/lfbb.c b/lfbb/src/lfbb.c index 1b94f4a..6b6183f 100755 --- a/lfbb/src/lfbb.c +++ b/lfbb/src/lfbb.c @@ -182,6 +182,7 @@ void LFBB_ReadRelease(LFBB_Inst_Type *inst, const size_t read) { } /* Increment the read index and wrap to 0 if needed */ + assert(r + read <= inst->size); r += read; if (r == inst->size) { r = 0U; From 0bfcdad6cf77d5b4f918ac13eedd2d45ebeeb910 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 19 May 2026 16:03:22 +0000 Subject: [PATCH 3/8] fix: reject single-byte buffers in LFBB_Init A size of 1 produces a buffer with zero usable capacity because the implementation always reserves one slot to distinguish empty from full. Tighten the assert from `size != 0` to `size > 1` so the degenerate case is caught at init time. --- lfbb/src/lfbb.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lfbb/src/lfbb.c b/lfbb/src/lfbb.c index 6b6183f..da6990f 100755 --- a/lfbb/src/lfbb.c +++ b/lfbb/src/lfbb.c @@ -58,7 +58,9 @@ static size_t CalcFree(size_t w, size_t r, size_t size); void LFBB_Init(LFBB_Inst_Type *inst, uint8_t *data_array, const size_t size) { assert(inst != NULL); assert(data_array != NULL); - assert(size != 0U); + /* One slot is always reserved to distinguish empty from full, so the + * minimum useful buffer size is 2. */ + assert(size > 1U); inst->data = data_array; inst->size = size; From 8037531c05ae99bdf1268314994c56621c2a7177 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 19 May 2026 16:04:22 +0000 Subject: [PATCH 4/8] docs: fix README, header, changelog and test documentation issues - README: correct the malformed CI badge URL (duplicated path segment) - README: fix syntax error in the producer code example (missing ')') - README: replace inline triple-backtick spans with single backticks - README: hyphenate "single-producer, single-consumer" - README: spell "phenomenon" correctly - CHANGELOG: spell "inferring" correctly - tests: spell "overflow" correctly in comments - lfbb.h: name parameters in @param tags so Doxygen can link them - lfbb.h: document the NULL return path for LFBB_WriteAcquire and LFBB_ReadAcquire --- CHANGELOG.md | 2 +- README.md | 16 ++++++++-------- lfbb/inc/lfbb.h | 28 +++++++++++++++------------- tests/tests.cpp | 4 ++-- 4 files changed, 26 insertions(+), 24 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c0f36e0..62d8e45 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -37,7 +37,7 @@ ## 1.2.2 -- Improved performance by using a write_wrapped flag instead of infering write wraps +- Improved performance by using a write_wrapped flag instead of inferring write wraps ## 1.2.1 diff --git a/README.md b/README.md index 230ca63..c80b5a0 100755 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # LFBB - Lock Free Bipartite Buffer -![CMake](https://github.com/DNedic/lfbb/actions/workflows/.github/workflows/cmake.yml/badge.svg) +![CMake](https://github.com/DNedic/lfbb/actions/workflows/cmake.yml/badge.svg) -LFBB is a bipartite buffer implementation written in standard C11, suitable for all platforms, from deeply embedded to HPC uses. It is lock-free for single consumer single producer scenarios making it incredibly performant and easy to use. +LFBB is a bipartite buffer implementation written in standard C11, suitable for all platforms, from deeply embedded to HPC uses. It is lock-free for single-producer, single-consumer scenarios making it incredibly performant and easy to use. ## What is a bipartite buffer @@ -16,7 +16,7 @@ A bipartite buffer should be used everywhere a ring buffer is used if you want: ## Features * Written in standard C11, compatible with all platforms supporting it -* Lock free thread and multicore safe in single producer single consumer scenarios +* Lock free thread and multicore safe in single-producer, single-consumer scenarios * No dynamic allocation * Optimized for high performance * MIT Licensed @@ -49,7 +49,7 @@ if (!write_started) { write_started = true; } } else { - if (ADC_PollDmaComplete(&adc_dma_h) { + if (ADC_PollDmaComplete(&adc_dma_h)) { LFBB_WriteRelease(&lfbb_adc, sizeof(data)); write_started = false; } @@ -57,13 +57,13 @@ if (!write_started) { ``` ## Configuration -The library offers two configuration defines ```LFBB_MULTICORE_HOSTED``` and ```LFBB_CACHELINE_LENGTH``` that can be passed by the build system or defined before including the library if the configuration isn't suitable. +The library offers two configuration defines `LFBB_MULTICORE_HOSTED` and `LFBB_CACHELINE_LENGTH` that can be passed by the build system or defined before including the library if the configuration isn't suitable. -On embedded systems it is usually required to do manual cache synchronization, so ```LFBB_MULTICORE_HOSTED``` should be left as ```false``` to avoid wasting space on padding for cacheline alignment of indexes. +On embedded systems it is usually required to do manual cache synchronization, so `LFBB_MULTICORE_HOSTED` should be left as `false` to avoid wasting space on padding for cacheline alignment of indexes. -For hosted systems the [False Sharing](https://en.wikipedia.org/wiki/False_sharing) phenomenom can reduce performance to some extent which is why passing ```LFBB_MULTICORE_HOSTED``` as ```true``` is advisable. This aligns the indexes to the system cacheline size, ```64``` by default. +For hosted systems the [False Sharing](https://en.wikipedia.org/wiki/False_sharing) phenomenon can reduce performance to some extent which is why passing `LFBB_MULTICORE_HOSTED` as `true` is advisable. This aligns the indexes to the system cacheline size, `64` by default. -Some systems have a non-typical cacheline length (for instance the apple M1/M2 CPUs have a cacheline length of 128 bytes), and ```LFBB_CACHELINE_LENGTH``` should be set accordingly in those cases. +Some systems have a non-typical cacheline length (for instance the apple M1/M2 CPUs have a cacheline length of 128 bytes), and `LFBB_CACHELINE_LENGTH` should be set accordingly in those cases. ## How it works The Bipartite Buffer uses the same base principle as the [ring buffer data structure](https://en.wikipedia.org/wiki/Circular_buffer), however its ability to provide contiguous space for writing and reading requires modifying the approach slightly. diff --git a/lfbb/inc/lfbb.h b/lfbb/inc/lfbb.h index 30921a6..c91c51f 100644 --- a/lfbb/inc/lfbb.h +++ b/lfbb/inc/lfbb.h @@ -90,41 +90,43 @@ typedef struct { /** * @brief Initializes a bipartite buffer instance - * @param[in] Instance pointer - * @param[in] Data array pointer - * @param[in] Size of data array + * @param[in] inst Instance pointer + * @param[in] data_array Data array pointer + * @param[in] size Size of data array (must be greater than 1) * @retval None */ void LFBB_Init(LFBB_Inst_Type *inst, uint8_t *data_array, size_t size); /** * @brief Acquires a linear region in the bipartite buffer for writing - * @param[in] Instance pointer - * @param[in] Free linear space in the buffer required - * @retval Pointer to the beginning of the linear space + * @param[in] inst Instance pointer + * @param[in] free_required Free linear space in the buffer required + * @retval Pointer to the beginning of the linear space, or NULL if a + * contiguous region of the requested size is not available */ uint8_t *LFBB_WriteAcquire(LFBB_Inst_Type *inst, size_t free_required); /** * @brief Releases the bipartite buffer after a write - * @param[in] Instance pointer - * @param[in] Bytes written to the linear space + * @param[in] inst Instance pointer + * @param[in] written Bytes written to the linear space * @retval None */ void LFBB_WriteRelease(LFBB_Inst_Type *inst, size_t written); /** * @brief Acquires a linear region in the bipartite buffer for reading - * @param[in] Instance pointer - * @param[out] Available linear data in the buffer - * @retval Pointer to the beginning of the data + * @param[in] inst Instance pointer + * @param[out] available Available linear data in the buffer + * @retval Pointer to the beginning of the data, or NULL if the buffer is + * empty (in which case \p available is set to 0) */ uint8_t *LFBB_ReadAcquire(LFBB_Inst_Type *inst, size_t *available); /** * @brief Releases the bipartite buffer after a read - * @param[in] Instance pointer - * @param[in] Bytes read from the linear region + * @param[in] inst Instance pointer + * @param[in] read Bytes read from the linear region * @retval None */ void LFBB_ReadRelease(LFBB_Inst_Type *inst, size_t read); diff --git a/tests/tests.cpp b/tests/tests.cpp index 7d37f0e..dd5be7d 100644 --- a/tests/tests.cpp +++ b/tests/tests.cpp @@ -62,7 +62,7 @@ TEST_CASE("Write with overflow condition", "[write_overflow]") { uint8_t *read_location = LFBB_ReadAcquire(&lfbb, &read_available); LFBB_ReadRelease(&lfbb, sizeof(test_data)); - /* Write again, this time the oveflow will trigger and should give us the + /* Write again, this time the overflow will trigger and should give us the * beginning again */ const uint8_t test_data2[240] = {0xA3U}; write_location = LFBB_WriteAcquire(&lfbb, sizeof(test_data2)); @@ -93,7 +93,7 @@ TEST_CASE("Read data written after overflow condition write", uint8_t *read_location = LFBB_ReadAcquire(&lfbb, &read_available); LFBB_ReadRelease(&lfbb, sizeof(test_data)); - /* Write again, this time the oveflow will trigger and should give us the + /* Write again, this time the overflow will trigger and should give us the * beginning again */ const uint8_t test_data2[240] = {0xA3U}; write_location = LFBB_WriteAcquire(&lfbb, sizeof(test_data2)); From 25ddf3dde8cab6f6be956e688f0ad0b324d7993f Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 19 May 2026 16:04:31 +0000 Subject: [PATCH 5/8] style: use 0U for size_t literal in LFBB_ReadAcquire The rest of the file consistently uses the `U` suffix for size_t literals; this assignment to `*available` was the lone exception. --- lfbb/src/lfbb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lfbb/src/lfbb.c b/lfbb/src/lfbb.c index da6990f..fd3e74b 100755 --- a/lfbb/src/lfbb.c +++ b/lfbb/src/lfbb.c @@ -147,7 +147,7 @@ uint8_t *LFBB_ReadAcquire(LFBB_Inst_Type *inst, size_t *available) { /* When read and write indexes are equal, the buffer is empty */ if (r == w) { - *available = 0; + *available = 0U; return NULL; } From f3cbb6608848cc5ceac2b8b81e4f87b9089558bb Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 19 May 2026 16:04:43 +0000 Subject: [PATCH 6/8] chore: drop executable bit on README.md and CHANGELOG.md These are plain markdown files; the executable bit was meaningless and caused them to show up oddly in file managers and `ls -l` output. --- CHANGELOG.md | 0 README.md | 0 2 files changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 CHANGELOG.md mode change 100755 => 100644 README.md diff --git a/CHANGELOG.md b/CHANGELOG.md old mode 100755 new mode 100644 diff --git a/README.md b/README.md old mode 100755 new mode 100644 From 058d51337ea5ea8450a36d18c2e9fa98f0d8a64f Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 19 May 2026 16:05:00 +0000 Subject: [PATCH 7/8] chore: bump pre-commit clang-format mirror from v15.0.7 to v19.1.7 v15 dates from early 2023. Move to a current stable release so the pre-commit hook tracks a modern clang-format with recent fixes. --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index a020f20..8b7ce16 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,5 +1,5 @@ repos: - repo: https://github.com/pre-commit/mirrors-clang-format - rev: v15.0.7 + rev: v19.1.7 hooks: - id: clang-format From 2a02b7783973e88ff53e8018c70774f4db02878a Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 19 May 2026 19:56:12 +0000 Subject: [PATCH 8/8] Release 1.3.9 --- CHANGELOG.md | 6 ++++++ CMakeLists.txt | 2 +- lfbb/inc/lfbb.h | 2 +- lfbb/src/lfbb.c | 2 +- 4 files changed, 9 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 62d8e45..ebcfdb0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## 1.3.9 + +- Fixed `LFBB_MULTICORE_HOSTED` not being forwarded as a value through CMake and being exposed as `PRIVATE`, which silently ignored the user-supplied value and caused a struct layout mismatch between the library build and consumers +- Tightened debug-time asserts: `LFBB_Init` now rejects single-byte buffers, `LFBB_ReadRelease` asserts the read amount stays within the buffer +- Documentation, style and tooling fixes + ## 1.3.8 - Avoided potential false sharing due to wrapping indexes for performance diff --git a/CMakeLists.txt b/CMakeLists.txt index 6bd72bc..4872e63 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,7 +4,7 @@ cmake_minimum_required(VERSION 3.16) project(lfbb - VERSION 1.3.8 + VERSION 1.3.9 LANGUAGES C CXX ) diff --git a/lfbb/inc/lfbb.h b/lfbb/inc/lfbb.h index c91c51f..2b9653d 100644 --- a/lfbb/inc/lfbb.h +++ b/lfbb/inc/lfbb.h @@ -34,7 +34,7 @@ * This file is part of LFBB - Lock Free Bipartite Buffer * * Author: Djordje Nedic - * Version: 1.3.8 + * Version: 1.3.9 **************************************************************/ /************************** INCLUDE ***************************/ diff --git a/lfbb/src/lfbb.c b/lfbb/src/lfbb.c index fd3e74b..5e7a47b 100755 --- a/lfbb/src/lfbb.c +++ b/lfbb/src/lfbb.c @@ -34,7 +34,7 @@ * This file is part of LFBB - Lock Free Bipartite Buffer * * Author: Djordje Nedic - * Version: 1.3.8 + * Version: 1.3.9 **************************************************************/ /************************** INCLUDE ***************************/