├── .clang-format ├── SConstruct ├── SConscript ├── .gitignore ├── test ├── SConscript └── circularbuffer-gtest.cc ├── LICENSE ├── gen_test_report.sh ├── README.md ├── circularbuffer.c └── circularbuffer.h /.clang-format: -------------------------------------------------------------------------------- 1 | # Run manually to reformat a file: 2 | # clang-format -i --style=file 3 | --- 4 | Language: Cpp 5 | BasedOnStyle: Google 6 | UseTab: Never 7 | IndentWidth: 4 8 | ColumnLimit: 100 9 | ... 10 | -------------------------------------------------------------------------------- /SConstruct: -------------------------------------------------------------------------------- 1 | # Use environment 2 | env = Environment() 3 | 4 | # Add compiler flags 5 | env.Append(CCFLAGS = ['-Wextra', '-Wall', '-Wpedantic', '-Werror']) 6 | 7 | # Google test program 8 | test = [] 9 | 10 | test += SConscript('test/SConscript', variant_dir='build', duplicate=0, exports='env') 11 | 12 | # Depend on the runner to ensure that it's built before running it - Note: using abspath. 13 | test_alias = Alias('test', [test], test[0].abspath) 14 | 15 | # Simply required. Without it, 'test' is never considered out of date. 16 | AlwaysBuild(test_alias) 17 | 18 | # Alias for building all programs 19 | Alias('all', [test]) 20 | -------------------------------------------------------------------------------- /SConscript: -------------------------------------------------------------------------------- 1 | # Import environment 2 | Import('env', 'mode') 3 | 4 | # Add include file absolute path 5 | env.Append(CPPPATH = [env.Dir('.').abspath]) 6 | 7 | # Clone environment 8 | env = env.Clone() 9 | 10 | # Add specific compiler flags 11 | env.AppendUnique(CCFLAGS = ['-std=c99']) 12 | 13 | if mode == 'test': 14 | env.AppendUnique(CCFLAGS = ['--coverage']) 15 | 16 | # Point out all source files 17 | src = env.Split(''' 18 | circularbuffer.c 19 | ''') 20 | 21 | obj = [] 22 | obj += env.Object(src) 23 | 24 | # Build a library 25 | lib = env.Library('circularbuffer', obj) 26 | 27 | # Return result to SConstruct 28 | Return('lib') 29 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Object files 5 | *.o 6 | *.ko 7 | *.obj 8 | *.elf 9 | 10 | # Linker output 11 | *.ilk 12 | *.map 13 | *.exp 14 | 15 | # Precompiled Headers 16 | *.gch 17 | *.pch 18 | 19 | # Libraries 20 | *.lib 21 | *.a 22 | *.la 23 | *.lo 24 | 25 | # Shared objects (inc. Windows DLLs) 26 | *.dll 27 | *.so 28 | *.so.* 29 | *.dylib 30 | 31 | # Executables 32 | *.exe 33 | *.out 34 | *.app 35 | *.i*86 36 | *.x86_64 37 | *.hex 38 | 39 | # Debug files 40 | *.dSYM/ 41 | *.su 42 | *.idb 43 | *.pdb 44 | 45 | # Kernel Module Compile Results 46 | *.mod* 47 | *.cmd 48 | .tmp_versions/ 49 | modules.order 50 | Module.symvers 51 | Mkfile.old 52 | dkms.conf 53 | 54 | # SCons files 55 | .sconsign.dblite 56 | 57 | # Build folder 58 | /build* 59 | -------------------------------------------------------------------------------- /test/SConscript: -------------------------------------------------------------------------------- 1 | # Import environment 2 | Import('env') 3 | 4 | # Clone environment 5 | env = env.Clone() 6 | 7 | # Add compiler flags 8 | env.AppendUnique(CCFLAGS = ['-O0', '-g']) 9 | 10 | # Add test mode 11 | mode = 'test' 12 | 13 | # Build the test object 14 | lib = [] 15 | 16 | lib += SConscript('../SConscript', variant_dir='libcbuf', duplicate=0, exports=['env', 'mode']) 17 | 18 | # Link needed libraries 19 | env.Prepend(LIBS = [lib, 'gtest', 'gcov', 'pthread'], LIBPATH = ['../../build_gtest/lib/']) 20 | 21 | # Add include path for the Google test 22 | env.Append(CCFLAGS=('-isystem', '../googletest/googletest/include')) 23 | 24 | # Build the unittest 25 | program = env.Program('circularbuffer-gtest', 'circularbuffer-gtest.cc') 26 | 27 | # Return result to SConstruct 28 | Return('program') 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Kristian Kinderlöv 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /gen_test_report.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | BUILD_DIR='build' 4 | 5 | function gen_test_report { 6 | if [ -d "$BUILD_DIR" ]; then 7 | # Generate report 8 | lcov -c --directory $BUILD_DIR --output-file $BUILD_DIR/gtest_coverage.info 9 | 10 | # Convert to html 11 | genhtml $BUILD_DIR/gtest_coverage.info --output-directory $BUILD_DIR/out 12 | else 13 | echo "No build folder found!" 14 | fi 15 | } 16 | 17 | function rem_test_report { 18 | if [ -d "$BUILD_DIR" ]; then 19 | if [ -f "$BUILD_DIR/gtest_coverage.info" ]; then 20 | rm $BUILD_DIR/gtest_coverage.info 21 | fi 22 | if [ -d "$BUILD_DIR/out" ]; then 23 | rm -rf $BUILD_DIR/out 24 | fi 25 | fi 26 | } 27 | 28 | function usage { 29 | echo "Usage: gen_test_report.sh [options]" 30 | echo " -g Generate" 31 | echo " -c Clean" 32 | echo " -h Help" 33 | } 34 | 35 | if [ $# -eq 0 ]; then 36 | gen_test_report 37 | exit 1; 38 | fi 39 | 40 | while getopts "gch" opt; do 41 | case "${opt}" in 42 | g) 43 | gen_test_report 44 | ;; 45 | c) 46 | rem_test_report 47 | ;; 48 | h) 49 | usage 50 | ;; 51 | esac 52 | done 53 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Circular Buffer 2 | This repository contains a circular buffer or a ring buffer implementation in C code suitable for embedded systems. The implementation using a single empty cell to detect the buffer "full" case; for supporting a single producer thread and single consumer thread without OS lock primitive. For use of efficient algorithms the maximum number of elements in the circular buffer must be a power of 2. The code follows the Google C++ Style Guide with one exception that instead of 2 spaces it use 4 spaces. 3 | 4 | ## Unittest 5 | 6 | The added unittest uses the googletest framework and the SCons build system. 7 | 8 | ### Follow the steps below for building the unittest 9 | 10 | 1. Install the scons, if you run on an Ubuntu machine: 11 | 12 | ```sudo apt install scons``` 13 | 14 | 2. Go to https://github.com/google/googletest and clone the git repo besides the circularbuffer repo. 15 | 16 | 3. Create a directory called *build_gtest* beside the circularbuffer and googletest repos. 17 | 18 | 4. Now you shall have the following directory structure: 19 | ``` 20 | 21 | | build_gtest 22 | | circularbuffer 23 | | googletest 24 | ``` 25 | 5. Change directory to the new directory ```build_gtest```. 26 | 27 | 6. Build the google test framework: 28 | 29 | ```/build_gtest$ cmake ../googletest/``` 30 | 31 | ```/build_gtest$ make``` 32 | 33 | 7. Change directory to ```/circularbuffer```. 34 | 35 | 8. Build, run and clean the units test with the scons command: 36 | 37 | ```/circularbuffer$ scons test``` 38 | 39 | ```/circularbuffer$ scons test -c``` 40 | 41 | 9. Optional build, run and clean the units test with the commands: 42 | 43 | ```/circularbuffer$ scons``` 44 | 45 | ```/circularbuffer$ ./build/circularbuffer-gtest``` 46 | 47 | ```/circularbuffer$ scons -c``` 48 | 49 | ### Follow the steps below for generate code coverage report 50 | 51 | 1. Install the lcov, if you run on an Ubuntu machine: 52 | 53 | ```sudo apt install lcov``` 54 | 55 | 2. Generate report: 56 | 57 | ```/circularbuffer$ ./gen_test_report.sh``` 58 | 59 | 3. Html output result is found here 60 | 61 | ```/circularbuffer/build/out$``` 62 | 63 | 4. Delete report: 64 | 65 | ```/circularbuffer$ ./gen_test_report.sh -c``` 66 | -------------------------------------------------------------------------------- /circularbuffer.c: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License (MIT) 3 | * 4 | * Copyright (c) 2019 Kristian Kinderlöv 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | /** 26 | * @addtogroup CIRCULARBUFFER 27 | * @{ 28 | * 29 | * @file circularbuffer.c 30 | * 31 | * @brief A circular buffer implementation. 32 | * 33 | * @} 34 | */ 35 | 36 | /* **** Includes **** */ 37 | 38 | #include "circularbuffer.h" 39 | 40 | #include 41 | #include 42 | 43 | /* **** Defines **** */ 44 | 45 | #define ASSERT(expr) assert(expr) 46 | 47 | /* **** Function Definitions **** */ 48 | 49 | void CircularBufferInit(CircularBufferContext *ctx, void *buf, size_t buf_size, 50 | size_t element_size) { 51 | ASSERT(ctx); 52 | ASSERT(buf); 53 | ASSERT(buf_size); 54 | ASSERT(element_size); 55 | 56 | const size_t size = buf_size / element_size; 57 | 58 | ASSERT((size & (size - 1u)) == 0u); 59 | 60 | ctx->buf = buf; 61 | ctx->write_pos = 0u; 62 | ctx->read_pos = 0u; 63 | ctx->element_size = element_size; 64 | ctx->max_size = size - 1; 65 | } 66 | 67 | void CircularBufferClear(CircularBufferContext *ctx) { 68 | ASSERT(ctx); 69 | 70 | ctx->write_pos = 0u; 71 | ctx->read_pos = 0u; 72 | } 73 | 74 | int32_t CircularBufferPushBack(CircularBufferContext *ctx, const void *val) { 75 | ASSERT(ctx); 76 | ASSERT(val); 77 | 78 | int32_t retval = 0; 79 | const size_t write_pos = (ctx->write_pos + 1) & ctx->max_size; 80 | 81 | // Check if full 82 | if (write_pos == ctx->read_pos) { 83 | goto fail; 84 | } 85 | 86 | memcpy(&ctx->buf[ctx->write_pos * ctx->element_size], val, ctx->element_size); 87 | ctx->write_pos = write_pos; 88 | 89 | goto success; 90 | 91 | fail: 92 | retval = -1; 93 | success: 94 | return retval; 95 | } 96 | 97 | int32_t CircularBufferPopFront(CircularBufferContext *ctx, void *val) { 98 | ASSERT(ctx); 99 | ASSERT(val); 100 | 101 | int32_t retval = 0; 102 | 103 | // Check if empty 104 | if (ctx->read_pos == ctx->write_pos) { 105 | goto fail; 106 | } 107 | 108 | memcpy(val, &ctx->buf[ctx->read_pos * ctx->element_size], ctx->element_size); 109 | ctx->read_pos = (ctx->read_pos + 1) & ctx->max_size; 110 | 111 | goto success; 112 | 113 | fail: 114 | retval = -1; 115 | success: 116 | return retval; 117 | } 118 | 119 | int32_t CircularBufferPeek(const CircularBufferContext *ctx, size_t num, void **elem) { 120 | ASSERT(ctx); 121 | 122 | int32_t retval = 0; 123 | const size_t write_pos = ctx->write_pos; 124 | const size_t read_pos = ctx->read_pos; 125 | const size_t size = ((write_pos - read_pos) & ctx->max_size); 126 | 127 | // Check that the buffer isn't empty and 128 | // that num is less than number of added elements 129 | if ((size == 0) || (size <= num)) { 130 | goto fail; 131 | } 132 | 133 | const size_t element_pos = ((read_pos + num) & ctx->max_size); 134 | *elem = &ctx->buf[element_pos * ctx->element_size]; 135 | 136 | goto success; 137 | 138 | fail: 139 | retval = -1; 140 | success: 141 | return retval; 142 | } 143 | 144 | size_t CircularBufferSize(const CircularBufferContext *ctx) { 145 | ASSERT(ctx); 146 | 147 | return ((ctx->write_pos - ctx->read_pos) & ctx->max_size); 148 | } 149 | 150 | size_t CircularBufferSpace(const CircularBufferContext *ctx) { 151 | ASSERT(ctx); 152 | 153 | return (ctx->max_size - CircularBufferSize(ctx)); 154 | } 155 | 156 | bool CircularBufferEmpty(const CircularBufferContext *ctx) { 157 | ASSERT(ctx); 158 | 159 | return (ctx->read_pos == ctx->write_pos); 160 | } 161 | -------------------------------------------------------------------------------- /test/circularbuffer-gtest.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Unit test for the circular buffer 3 | */ 4 | 5 | #include "circularbuffer.h" 6 | #include "gtest/gtest.h" 7 | 8 | namespace { 9 | 10 | #define BUF_SIZE 7u 11 | 12 | // The fixture for testing class Foo. 13 | class CircularBufferTest : public ::testing::Test { 14 | protected: 15 | // You can remove any or all of the following functions if its body 16 | // is empty. 17 | 18 | CircularBufferTest() { 19 | // You can do set-up work for each test here. 20 | } 21 | 22 | ~CircularBufferTest() override { 23 | // You can do clean-up work that doesn't throw exceptions here. 24 | } 25 | 26 | // If the constructor and destructor are not enough for setting up 27 | // and cleaning up each test, you can define the following methods: 28 | 29 | void SetUp() override { 30 | // Code here will be called immediately after the constructor (right 31 | // before each test). 32 | } 33 | 34 | void TearDown() override { 35 | // Code here will be called immediately after each test (right 36 | // before the destructor). 37 | } 38 | 39 | // Objects declared here can be used by all tests in the test case for 40 | // CircularBuffer. 41 | CircularBufferContext cb; 42 | uint32_t buf[BUF_SIZE + 1]; 43 | }; 44 | 45 | // Tests that the CircularBufferInit does the intialalization. 46 | TEST_F(CircularBufferTest, Init) { 47 | CircularBufferInit(&cb, buf, sizeof(buf), sizeof(buf[0])); 48 | 49 | ASSERT_EQ(cb.buf, (uint8_t *)buf); 50 | ASSERT_EQ(cb.read_pos, cb.write_pos); 51 | ASSERT_EQ(cb.element_size, sizeof(uint32_t)); 52 | ASSERT_EQ(cb.max_size, BUF_SIZE); 53 | } 54 | 55 | // Tests that CircularBufferPushBack add correct number of elements. 56 | TEST_F(CircularBufferTest, PushBack) { 57 | for (uint32_t i = 0; i < BUF_SIZE; i++) { 58 | ASSERT_EQ(CircularBufferPushBack(&cb, &i), 0u); 59 | } 60 | 61 | ASSERT_EQ(CircularBufferSize(&cb), BUF_SIZE); 62 | } 63 | 64 | // Tests that CircularBufferPushBack when buffer is full. 65 | TEST_F(CircularBufferTest, PushBackOutOfBoundary) { 66 | const uint32_t data = 0; 67 | 68 | ASSERT_EQ(CircularBufferPushBack(&cb, &data), -1); 69 | ASSERT_EQ(CircularBufferSize(&cb), BUF_SIZE); 70 | } 71 | 72 | // Tests that CircularBufferPopFront pop correct value and 73 | // correct number of elements. 74 | TEST_F(CircularBufferTest, PopFront) { 75 | uint32_t data = 0; 76 | 77 | for (uint8_t i = 0; i < BUF_SIZE; i++) { 78 | ASSERT_EQ(CircularBufferPopFront(&cb, &data), 0u); 79 | ASSERT_EQ(data, i); 80 | } 81 | 82 | ASSERT_EQ(CircularBufferSize(&cb), 0u); 83 | } 84 | 85 | // Tests that CircularBufferPopFront when buffer is empty. 86 | TEST_F(CircularBufferTest, PopFronOutOfBoundary) { 87 | uint32_t data = 0; 88 | 89 | ASSERT_EQ(CircularBufferPopFront(&cb, &data), -1); 90 | ASSERT_EQ(CircularBufferSize(&cb), 0u); 91 | } 92 | 93 | // Tests that CircularBufferPeek return correct pointer. 94 | TEST_F(CircularBufferTest, Peek) { 95 | uint32_t data[BUF_SIZE] = {0}; 96 | 97 | // Fill the buffer 98 | for (uint32_t i = 0; i < BUF_SIZE; i++) { 99 | data[i] = 10 + i; 100 | ASSERT_EQ(CircularBufferPushBack(&cb, &data[i]), 0u); 101 | } 102 | 103 | // Test to peek all elements 104 | for (uint32_t i = 0; i < BUF_SIZE; i++) { 105 | uint32_t *peek; 106 | ASSERT_EQ(CircularBufferPeek(&cb, i, (void **)&peek), 0u); 107 | ASSERT_EQ(*peek, data[i]); 108 | } 109 | 110 | // Remove 2 elements and add 1 element 111 | for (uint32_t i = 0; i < 2; i++) { 112 | ASSERT_EQ(CircularBufferPopFront(&cb, &data[i]), 0u); 113 | data[i] = data[i + 2]; 114 | } 115 | for (uint32_t i = 0; i < BUF_SIZE - 2; i++) { 116 | data[i] = data[i + 2]; 117 | } 118 | for (uint32_t i = BUF_SIZE - 2; i < BUF_SIZE - 1; i++) { 119 | data[i] = 20 + i; 120 | ASSERT_EQ(CircularBufferPushBack(&cb, &data[i]), 0u); 121 | } 122 | 123 | // Test to peek all elements 124 | for (uint32_t i = 0; i < BUF_SIZE - 1; i++) { 125 | uint32_t *peek; 126 | ASSERT_EQ(CircularBufferPeek(&cb, i, (void **)&peek), 0u); 127 | ASSERT_EQ(*peek, data[i]); 128 | } 129 | 130 | // Fill the buffer again 131 | for (uint32_t i = BUF_SIZE - 1; i < BUF_SIZE; i++) { 132 | data[i] = 20 + i; 133 | ASSERT_EQ(CircularBufferPushBack(&cb, &data[i]), 0u); 134 | } 135 | 136 | // Test to peek all elements 137 | for (uint32_t i = 0; i < BUF_SIZE; i++) { 138 | uint32_t *peek; 139 | ASSERT_EQ(CircularBufferPeek(&cb, i, (void **)&peek), 0u); 140 | ASSERT_EQ(*peek, data[i]); 141 | } 142 | 143 | // Test out of boundary 144 | uint32_t *peek = NULL; 145 | ASSERT_EQ(CircularBufferPeek(&cb, BUF_SIZE, (void **)&peek), -1); 146 | ASSERT_TRUE(peek == NULL); 147 | } 148 | 149 | // Tests that CircularBufferSpace return correct value. 150 | TEST_F(CircularBufferTest, Size) { 151 | ASSERT_EQ(CircularBufferSize(&cb), BUF_SIZE); 152 | } 153 | 154 | // Tests that CircularBufferSpace return correct value. 155 | TEST_F(CircularBufferTest, Space) { 156 | uint32_t data = 0; 157 | 158 | for (uint32_t i = 0; i < BUF_SIZE; i++) { 159 | ASSERT_EQ(CircularBufferSpace(&cb), i); 160 | ASSERT_EQ(CircularBufferPopFront(&cb, &data), 0u); 161 | } 162 | 163 | ASSERT_EQ(CircularBufferSpace(&cb), BUF_SIZE); 164 | } 165 | 166 | // Tests that CircularBufferClear clears the circular buffer 167 | TEST_F(CircularBufferTest, Clear) { 168 | uint32_t data = 10u; 169 | 170 | for (uint32_t i = 0; i < BUF_SIZE; i++) { 171 | ASSERT_EQ(CircularBufferPushBack(&cb, &i), 0u); 172 | } 173 | 174 | CircularBufferClear(&cb); 175 | 176 | ASSERT_EQ(CircularBufferSize(&cb), 0u); 177 | ASSERT_EQ(CircularBufferPushBack(&cb, &data), 0u); 178 | ASSERT_EQ(CircularBufferSize(&cb), 1u); 179 | } 180 | 181 | // Tests that CircularBufferClear clears the circular buffer 182 | TEST_F(CircularBufferTest, Empty) { 183 | uint32_t data = 10u; 184 | 185 | CircularBufferClear(&cb); 186 | 187 | ASSERT_EQ(CircularBufferEmpty(&cb), true); 188 | ASSERT_EQ(CircularBufferPushBack(&cb, &data), 0u); 189 | ASSERT_EQ(CircularBufferEmpty(&cb), false); 190 | ASSERT_EQ(CircularBufferPopFront(&cb, &data), 0u); 191 | ASSERT_EQ(CircularBufferEmpty(&cb), true); 192 | } 193 | 194 | } // namespace 195 | 196 | int main(int argc, char **argv) { 197 | ::testing::InitGoogleTest(&argc, argv); 198 | return RUN_ALL_TESTS(); 199 | } 200 | -------------------------------------------------------------------------------- /circularbuffer.h: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License (MIT) 3 | * 4 | * Copyright (c) 2019 Kristian Kinderlöv 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | /** 26 | * @addtogroup CIRCULARBUFFER 27 | * @ingroup MISC 28 | * @{ 29 | * 30 | * @file circularbuffer.h 31 | * 32 | * @brief Circular buffer public interface. 33 | * 34 | * This circular buffer implementation uses a single empty cell to detect the 35 | * "full" case for supporting thread safety, meaning it support one producer and 36 | * one consumer without a lock. The circular buffer therefore hold as most 37 | * ((buf_size / element_size) - 1) elements. For use of efficient algorithms the 38 | * maximum number of elements in the circular buffer must be a power of 2. 39 | */ 40 | 41 | #ifndef CIRCULARBUFFER_H_ 42 | #define CIRCULARBUFFER_H_ 43 | 44 | #ifdef __cplusplus 45 | extern "C" { 46 | #endif 47 | 48 | /* **** Includes **** */ 49 | 50 | #include 51 | #include 52 | #include 53 | 54 | /* **** Typedefs **** */ 55 | 56 | typedef struct { 57 | uint8_t *buf; // Pointer to the buffer 58 | size_t write_pos; // Write position 59 | size_t read_pos; // Read position 60 | size_t element_size; // Size of a element in the buffer 61 | size_t max_size; // Max number of elements in the buffer 62 | } CircularBufferContext; 63 | 64 | /* **** Function Declarations **** */ 65 | 66 | /** 67 | * @brief Initializes the circular buffer context. 68 | * 69 | * The max number of elements in the circular buffer is 70 | * (buf_size / element_size) - 1. 71 | * 72 | * Asserts if: 73 | * 'ctx' is NULL. 74 | * 'buf' is NULL. 75 | * 'buf_size' is 0. 76 | * 'element_size' is 0. 77 | * 'buf_size / element_size' is not power of 2. 78 | * 79 | * @param[out] *ctx Pointer to the circular buffer context. 80 | * @param[in] buf Pointer to a buffer. 81 | * @param[in] buf_size The buffer size. 82 | * @param[in] element_size The size of an element in the buffer. 83 | */ 84 | void CircularBufferInit(CircularBufferContext *ctx, void *buf, size_t buf_size, 85 | size_t element_size); 86 | 87 | /** 88 | * @brief Removes all elements from the circular buffer. 89 | * 90 | * Asserts if: 91 | * 'ctx' is NULL. 92 | * 93 | * @param[out] *ctx Pointer to the circular buffer context. 94 | */ 95 | void CircularBufferClear(CircularBufferContext *ctx); 96 | 97 | /** 98 | * @brief Adds an new element to the end of the buffer. The "val" content is 99 | * copied to the element. 100 | * 101 | * Asserts if: 102 | * 'ctx' is NULL. 103 | * 'val' is NULL. 104 | * 105 | * @param[out] *ctx Pointer to the circular buffer context. 106 | * @param[in] val Pointer to the source to be copied. 107 | * @return 0 if success, -1 if the buffer is full. 108 | */ 109 | int32_t CircularBufferPushBack(CircularBufferContext *ctx, const void *val); 110 | 111 | /** 112 | * @brief Removes the first element from the buffer. Copies the element content 113 | * to the "val" destination. 114 | * 115 | * Asserts if: 116 | * 'ctx' is NULL. 117 | * 'val' is NULL. 118 | * 119 | * @param[out] *ctx Pointer to the circular buffer context. 120 | * @param[out] *val Pointer to the destination where the data is to 121 | * be stored. 122 | * @return 0 if success, -1 if the buffer is 123 | * empty. 124 | */ 125 | int32_t CircularBufferPopFront(CircularBufferContext *ctx, void *val); 126 | 127 | /** 128 | * @brief Peeks the "num" element from the buffer. 129 | * 130 | * The "num" argument shall be less than the number of elements added to the 131 | * buffer. 132 | * 133 | * Asserts if: 134 | * 'ctx' is NULL. 135 | * 136 | * @param[in] *ctx Pointer to the circular buffer context. 137 | * @param[in] num The number of the element to peek. 138 | * @param[out] elem Pointer to the 'num' element. 139 | * @return 0 if success, -1 or NULL buffer is empty or the 140 | * 'num' is out of bounds. 141 | */ 142 | int32_t CircularBufferPeek(const CircularBufferContext *ctx, size_t num, void **elem); 143 | 144 | /** 145 | * @brief Gets the number of added elements in the buffer. 146 | * 147 | * Asserts if: 148 | * 'ctx' is NULL. 149 | * 150 | * @param[in] *ctx Pointer to the circular buffer context. 151 | * @return The number of added elements. 152 | */ 153 | size_t CircularBufferSize(const CircularBufferContext *ctx); 154 | 155 | /** 156 | * @brief Gets the number of free elements in the buffer. 157 | * 158 | * Asserts if: 159 | * 'ctx' is NULL. 160 | * 161 | * @param[in] *ctx Pointer to the circular buffer context. 162 | * @return The number of free elements. 163 | */ 164 | size_t CircularBufferSpace(const CircularBufferContext *ctx); 165 | 166 | /** 167 | * @brief Checks if the buffer is empty. 168 | * 169 | * Asserts if: 170 | * 'ctx' is NULL. 171 | * 172 | * @param[in] *ctx Pointer to the circular buffer context. 173 | * @return true if the buffer is empty otherwise false. 174 | */ 175 | bool CircularBufferEmpty(const CircularBufferContext *ctx); 176 | 177 | #ifdef __cplusplus 178 | } 179 | #endif 180 | 181 | #endif /* CIRCULARBUFFER_H_ */ 182 | 183 | /** @} */ 184 | --------------------------------------------------------------------------------