diff --git a/.clang-format b/.clang-format index 0227352..33bb7d7 100644 --- a/.clang-format +++ b/.clang-format @@ -122,8 +122,8 @@ ObjCBlockIndentWidth: 2 ObjCBreakBeforeNestedBlockParam: true ObjCSpaceAfterProperty: false ObjCSpaceBeforeProtocolList: true -PenaltyBreakAssignment: 2 -PenaltyBreakBeforeFirstCallParameter: 19 +PenaltyBreakAssignment: 50 +PenaltyBreakBeforeFirstCallParameter: 9 PenaltyBreakComment: 300 PenaltyBreakFirstLessLess: 120 PenaltyBreakOpenParenthesis: 0 diff --git a/AUTHORS b/AUTHORS index f4df17b..77bc3df 100644 --- a/AUTHORS +++ b/AUTHORS @@ -1,9 +1,9 @@ Tilen Majerle +Tofik Sonono +Jackistang Tilen Majerle -Jaedeok Kim -Thomas Devoogdt -LinJieqiang <517503838@qq.com> -jnz86 Junde Yhi -Jackistang -Tofik Sonono \ No newline at end of file +jnz86 +LinJieqiang <517503838@qq.com> +Thomas Devoogdt +Jaedeok Kim \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index bc668b8..328a06c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,11 @@ ## Develop +## v3.2.0 + +- Add user argument option +- Improve the variable names to better conform *LLVM* tidy config + ## v3.1.0 - Preparation for `v3.1` @@ -17,7 +22,7 @@ - Add `lwrb_move` and `lwrb_overwrite` - Fix `lwrb_find` which failed to properly search for tokens at corner cases -## v3.1.0-RC1 +## v3.0.0-RC1 - Split CMakeLists.txt files between library and executable - Change license year to 2022 diff --git a/LICENSE b/LICENSE index 3702ca4..42c1b5c 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2023 Tilen MAJERLE +Copyright (c) 2024 Tilen MAJERLE Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/dev/main.c b/dev/main.c index 51de1b7..7eb798f 100644 --- a/dev/main.c +++ b/dev/main.c @@ -36,9 +36,9 @@ main() { #define RW_TEST(_w_exp_, _r_exp_, _rw_len_, _rw_exp_len_) \ do { \ - printf("W ptr: %u, R ptr: %u, R/W len: %u, as_expected: %u\r\n", (unsigned)buff.w, (unsigned)buff.r, \ + printf("W ptr: %u, R ptr: %u, R/W len: %u, as_expected: %u\r\n", (unsigned)buff.w_ptr, (unsigned)buff.r_ptr, \ (unsigned)(_rw_len_), \ - (unsigned)(buff.w == (_w_exp_) && buff.r == (_r_exp_) && (_rw_len_) == (_rw_exp_len_))); \ + (unsigned)(buff.w_ptr == (_w_exp_) && buff.r_ptr == (_r_exp_) && (_rw_len_) == (_rw_exp_len_))); \ } while (0) lwrb_reset(&buff); @@ -65,9 +65,9 @@ main() { #define RW_TEST(_w_exp_, _r_exp_, _success_, _rw_len_, _rw_exp_len_) \ do { \ - printf("W ptr: %u, R ptr: %u, R/W success: %u, R/W len: %u, as_expected: %u\r\n", (unsigned)buff.w, \ - (unsigned)buff.r, (unsigned)(_success_), (unsigned)(_rw_len_), \ - (unsigned)(buff.w == (_w_exp_) && buff.r == (_r_exp_) && (_rw_len_) == (_rw_exp_len_))); \ + printf("W ptr: %u, R ptr: %u, R/W success: %u, R/W len: %u, as_expected: %u\r\n", (unsigned)buff.w_ptr, \ + (unsigned)buff.r_ptr, (unsigned)(_success_), (unsigned)(_rw_len_), \ + (unsigned)(buff.w_ptr == (_w_exp_) && buff.r_ptr == (_r_exp_) && (_rw_len_) == (_rw_exp_len_))); \ } while (0) lwrb_reset(&buff); @@ -190,8 +190,8 @@ main() { FIND_TEST("678", 3, 0, 1); /* Restart by setting write and read as empty with offset */ - buff.w = 6; - buff.r = 6; + buff.w_ptr = 6; + buff.r_ptr = 6; lwrb_write(&buff, "12345678", 8); FIND_TEST("123", 3, 0, 1); /* Must find it */ @@ -200,8 +200,8 @@ main() { /* Restart by setting write and read as empty with offset */ /* This should generate data for search in overflow mode */ - buff.w = 8; - buff.r = 8; + buff.w_ptr = 8; + buff.r_ptr = 8; lwrb_write(&buff, "12345678", 8); FIND_TEST("1234", 3, 0, 1); /* Must find it */ diff --git a/docs/get-started/index.rst b/docs/get-started/index.rst index fb6293d..b54a2a0 100644 --- a/docs/get-started/index.rst +++ b/docs/get-started/index.rst @@ -57,22 +57,22 @@ Update cloned to latest version Add library to project ^^^^^^^^^^^^^^^^^^^^^^ -At this point it is assumed that you have successfully download library, either cloned it or from releases page. +At this point it is assumed that you have successfully download library, either with ``git clone`` command or with manual download from the library releases page. Next step is to add the library to the project, by means of source files to compiler inputs and header files in search path. *CMake* is the main supported build system. Package comes with the ``CMakeLists.txt`` and ``library.cmake`` files, both located in the ``lwrb`` directory: -* ``CMakeLists.txt``: Is a wrapper and only includes ``library.cmake`` file. It is used if target application uses ``add_subdirectory`` and then uses ``target_link_libraries`` to include the library in the project -* ``library.cmake``: It is a fully configured set of variables. User must use ``include(path/to/library.cmake)`` to include the library and must manually add files/includes to the final target +* ``library.cmake``: It is a fully configured set of variables and with library definition. User can include this file to the project file with ``include(path/to/library.cmake)`` and then manually use the variables provided by the file, such as list of source files, include paths or necessary compiler definitions. It is up to the user to properly use the this file on its own. +* ``CMakeLists.txt``: It is a wrapper-only file and includes ``library.cmake`` file. It is used for when user wants to include the library to the main project by simply calling ``add_subdirectory`` **CMake** command, followed by ``target_link_libraries`` to add library as an external library. .. tip:: - Open ``library.cmake`` file and manually analyze all the possible variables you can set for full functionality. + Open ``library.cmake`` and analyze the provided information. Among variables, you can also find list of all possible exposed libraries for the user. If you do not use the *CMake*, you can do the following: * Copy ``lwrb`` folder to your project, it contains library files * Add ``lwrb/src/include`` folder to `include path` of your toolchain. This is where `C/C++` compiler can find the files during compilation process. Usually using ``-I`` flag -* Add source files from ``lwrb/src/`` folder to toolchain build. These files are built by `C/C++` compiler. CMake configuration comes with the library, allows users to include library in the project as **subdirectory** and **library**.y +* Add source files from ``lwrb/src/`` folder to toolchain build. These files are built by `C/C++` compilery * Build the project Minimal example code diff --git a/library.json b/library.json index a89971a..440fc06 100644 --- a/library.json +++ b/library.json @@ -1,6 +1,6 @@ { "name": "LwRB", - "version": "3.1.0", + "version": "3.2.0", "description": "Lightweight general purpose ring buffer with optimizations for embedded systems", "keywords": "buffer, ring buffer, library, rb, cyclic", "repository": { diff --git a/lwrb/library.cmake b/lwrb/library.cmake index 3e14f7a..ee74a16 100644 --- a/lwrb/library.cmake +++ b/lwrb/library.cmake @@ -1,4 +1,6 @@ # +# LIB_PREFIX: LWRB +# # This file provides set of variables for end user # and also generates one (or more) libraries, that can be added to the project using target_link_libraries(...) # @@ -8,6 +10,9 @@ # LWRB_COMPILE_DEFINITIONS: If defined, it provides "-D" definitions to the library build # +# Custom include directory +set(LWRB_CUSTOM_INC_DIR ${CMAKE_CURRENT_BINARY_DIR}/lib_inc) + # Library core sources set(lwrb_core_SRCS ${CMAKE_CURRENT_LIST_DIR}/src/lwrb/lwrb.c @@ -21,6 +26,7 @@ set(lwrb_ex_SRCS # Setup include directories set(lwrb_include_DIRS ${CMAKE_CURRENT_LIST_DIR}/src/include + ${LWPKT_CUSTOM_INC_DIR} ) # Register library to the system diff --git a/lwrb/src/include/lwrb/lwrb.h b/lwrb/src/include/lwrb/lwrb.h index 6da7a20..524fa50 100644 --- a/lwrb/src/include/lwrb/lwrb.h +++ b/lwrb/src/include/lwrb/lwrb.h @@ -4,7 +4,7 @@ */ /* - * Copyright (c) 2023 Tilen MAJERLE + * Copyright (c) 2024 Tilen MAJERLE * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -29,7 +29,7 @@ * This file is part of LwRB - Lightweight ring buffer library. * * Author: Tilen MAJERLE - * Version: v3.1.0 + * Version: v3.2.0 */ #ifndef LWRB_HDR_H #define LWRB_HDR_H @@ -99,9 +99,12 @@ typedef void (*lwrb_evt_fn)(struct lwrb* buff, lwrb_evt_type_t evt, lwrb_sz_t bp typedef struct lwrb { uint8_t* buff; /*!< Pointer to buffer data. Buffer is considered initialized when `buff != NULL` and `size > 0` */ lwrb_sz_t size; /*!< Size of buffer data. Size of actual buffer is `1` byte less than value holds */ - lwrb_sz_atomic_t r; /*!< Next read pointer. Buffer is considered empty when `r == w` and full when `w == r - 1` */ - lwrb_sz_atomic_t w; /*!< Next write pointer. Buffer is considered empty when `r == w` and full when `w == r - 1` */ - lwrb_evt_fn evt_fn; /*!< Pointer to event callback function */ + lwrb_sz_atomic_t r_ptr; /*!< Next read pointer. + Buffer is considered empty when `r == w` and full when `w == r - 1` */ + lwrb_sz_atomic_t w_ptr; /*!< Next write pointer. + Buffer is considered empty when `r == w` and full when `w == r - 1` */ + lwrb_evt_fn evt_fn; /*!< Pointer to event callback function */ + void* arg; /*!< Event custom user argument */ } lwrb_t; uint8_t lwrb_init(lwrb_t* buff, void* buffdata, lwrb_sz_t size); @@ -109,6 +112,8 @@ uint8_t lwrb_is_ready(lwrb_t* buff); void lwrb_free(lwrb_t* buff); void lwrb_reset(lwrb_t* buff); void lwrb_set_evt_fn(lwrb_t* buff, lwrb_evt_fn fn); +void lwrb_set_arg(lwrb_t* buff, void* arg); +void* lwrb_get_arg(lwrb_t* buff); /* Read/Write functions */ lwrb_sz_t lwrb_write(lwrb_t* buff, const void* data, lwrb_sz_t btw); @@ -116,8 +121,8 @@ lwrb_sz_t lwrb_read(lwrb_t* buff, void* data, lwrb_sz_t btr); lwrb_sz_t lwrb_peek(const lwrb_t* buff, lwrb_sz_t skip_count, void* data, lwrb_sz_t btp); /* Extended read/write functions */ -uint8_t lwrb_write_ex(lwrb_t* buff, const void* data, lwrb_sz_t btw, lwrb_sz_t* bw, uint16_t flags); -uint8_t lwrb_read_ex(lwrb_t* buff, void* data, lwrb_sz_t btr, lwrb_sz_t* br, uint16_t flags); +uint8_t lwrb_write_ex(lwrb_t* buff, const void* data, lwrb_sz_t btw, lwrb_sz_t* bwritten, uint16_t flags); +uint8_t lwrb_read_ex(lwrb_t* buff, void* data, lwrb_sz_t btr, lwrb_sz_t* bread, uint16_t flags); /* Buffer size information */ lwrb_sz_t lwrb_get_free(const lwrb_t* buff); diff --git a/lwrb/src/lwrb/lwrb.c b/lwrb/src/lwrb/lwrb.c index 5e13468..6e724c8 100644 --- a/lwrb/src/lwrb/lwrb.c +++ b/lwrb/src/lwrb/lwrb.c @@ -4,7 +4,7 @@ */ /* - * Copyright (c) 2023 Tilen MAJERLE + * Copyright (c) 2024 Tilen MAJERLE * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -29,7 +29,7 @@ * This file is part of LwRB - Lightweight ring buffer library. * * Author: Tilen MAJERLE - * Version: v3.1.0 + * Version: v3.2.0 */ #include "lwrb/lwrb.h" @@ -75,8 +75,8 @@ lwrb_init(lwrb_t* buff, void* buffdata, lwrb_sz_t size) { buff->evt_fn = NULL; buff->size = size; buff->buff = buffdata; - LWRB_INIT(buff->w, 0); - LWRB_INIT(buff->r, 0); + LWRB_INIT(buff->w_ptr, 0); + LWRB_INIT(buff->r_ptr, 0); return 1; } @@ -115,9 +115,37 @@ lwrb_set_evt_fn(lwrb_t* buff, lwrb_evt_fn evt_fn) { } } +/** + * \brief Set custom buffer argument, that can be retrieved in the event function + * \param[in] buff: Ring buffer instance + * \param[in] arg: Custom user argument + */ +void +lwrb_set_arg(lwrb_t* buff, void* arg) { + if (BUF_IS_VALID(buff)) { + buff->arg = arg; + } +} + +/** + * \brief Get custom buffer argument, previously set with \ref lwrb_set_arg + * \param[in] buff: Ring buffer instance + * \return User argument, previously set with \ref lwrb_set_arg + */ +void* +lwrb_get_arg(lwrb_t* buff) { + return buff != NULL ? buff->arg : NULL; +} + /** * \brief Write data to buffer. - * Copies data from `data` array to buffer and marks buffer as full for maximum `btw` number of bytes + * Copies data from `data` array to buffer and advances the write pointer for a maximum of `btw` number of bytes. + * + * It copies less if there is less memory available in the buffer. + * User must check the return value of the function and compare it to + * the requested write length, to determine if everything has been written + * + * \note Use \ref lwrb_write_ex for more advanced usage * * \param[in] buff: Ring buffer instance * \param[in] data: Pointer to data to write into buffer @@ -142,16 +170,16 @@ lwrb_write(lwrb_t* buff, const void* data, lwrb_sz_t btw) { * \param buff: Ring buffer instance * \param data: Pointer to data to write into buffer * \param btw: Number of bytes to write - * \param bw: Output pointer to write number of bytes written + * \param bwritten: Output pointer to write number of bytes written into the buffer * \param flags: Optional flags. * \ref LWRB_FLAG_WRITE_ALL: Request to write all data (up to btw). * Will early return if no memory available * \return `1` if write operation OK, `0` otherwise */ uint8_t -lwrb_write_ex(lwrb_t* buff, const void* data, lwrb_sz_t btw, lwrb_sz_t* bw, uint16_t flags) { - lwrb_sz_t tocopy, free, buff_w_ptr; - const uint8_t* d = data; +lwrb_write_ex(lwrb_t* buff, const void* data, lwrb_sz_t btw, lwrb_sz_t* bwritten, uint16_t flags) { + lwrb_sz_t tocopy = 0, free = 0, w_ptr = 0; + const uint8_t* d_ptr = data; if (!BUF_IS_VALID(buff) || data == NULL || btw == 0) { return 0; @@ -160,45 +188,50 @@ lwrb_write_ex(lwrb_t* buff, const void* data, lwrb_sz_t btw, lwrb_sz_t* bw, uint /* Calculate maximum number of bytes available to write */ free = lwrb_get_free(buff); /* If no memory, or if user wants to write ALL data but no enough space, exit early */ - if (free == 0 || (free < btw && flags & LWRB_FLAG_WRITE_ALL)) { + if (free == 0 || (free < btw && (flags & LWRB_FLAG_WRITE_ALL))) { return 0; } btw = BUF_MIN(free, btw); - buff_w_ptr = LWRB_LOAD(buff->w, memory_order_acquire); + w_ptr = LWRB_LOAD(buff->w_ptr, memory_order_acquire); /* Step 1: Write data to linear part of buffer */ - tocopy = BUF_MIN(buff->size - buff_w_ptr, btw); - BUF_MEMCPY(&buff->buff[buff_w_ptr], d, tocopy); - buff_w_ptr += tocopy; + tocopy = BUF_MIN(buff->size - w_ptr, btw); + BUF_MEMCPY(&buff->buff[w_ptr], d_ptr, tocopy); + d_ptr += tocopy; + w_ptr += tocopy; btw -= tocopy; /* Step 2: Write data to beginning of buffer (overflow part) */ if (btw > 0) { - BUF_MEMCPY(buff->buff, &d[tocopy], btw); - buff_w_ptr = btw; + BUF_MEMCPY(buff->buff, d_ptr, btw); + w_ptr = btw; } /* Step 3: Check end of buffer */ - if (buff_w_ptr >= buff->size) { - buff_w_ptr = 0; + if (w_ptr >= buff->size) { + w_ptr = 0; } /* * Write final value to the actual running variable. * This is to ensure no read operation can access intermediate data */ - LWRB_STORE(buff->w, buff_w_ptr, memory_order_release); + LWRB_STORE(buff->w_ptr, w_ptr, memory_order_release); BUF_SEND_EVT(buff, LWRB_EVT_WRITE, tocopy + btw); - if (bw != NULL) { - *bw = tocopy + btw; + if (bwritten != NULL) { + *bwritten = tocopy + btw; } return 1; } /** * \brief Read data from buffer. - * Copies data from buffer to `data` array and marks buffer as free for maximum `btr` number of bytes + * Copies data from `data` array to buffer and advances the read pointer for a maximum of `btr` number of bytes. + * + * It copies less if there is less data available in the buffer. + * + * \note Use \ref lwrb_read_ex for more advanced usage * * \param[in] buff: Ring buffer instance * \param[out] data: Pointer to output memory to copy buffer data to @@ -216,21 +249,22 @@ lwrb_read(lwrb_t* buff, void* data, lwrb_sz_t btr) { } /** - * \brief Write extended functionality + * \brief Read extended functionality * * \param buff: Ring buffer instance * \param data: Pointer to memory to write read data from buffer * \param btr: Number of bytes to read - * \param br: Output pointer to write number of bytes read + * \param bread: Output pointer to write number of bytes read from buffer and written to the + * output `data` variable * \param flags: Optional flags * \ref LWRB_FLAG_READ_ALL: Request to read all data (up to btr). * Will early return if no enough bytes in the buffer * \return `1` if read operation OK, `0` otherwise */ uint8_t -lwrb_read_ex(lwrb_t* buff, void* data, lwrb_sz_t btr, lwrb_sz_t* br, uint16_t flags) { - lwrb_sz_t tocopy, full, buff_r_ptr; - uint8_t* d = data; +lwrb_read_ex(lwrb_t* buff, void* data, lwrb_sz_t btr, lwrb_sz_t* bread, uint16_t flags) { + lwrb_sz_t tocopy = 0, full = 0, r_ptr = 0; + uint8_t* d_ptr = data; if (!BUF_IS_VALID(buff) || data == NULL || btr == 0) { return 0; @@ -242,34 +276,35 @@ lwrb_read_ex(lwrb_t* buff, void* data, lwrb_sz_t btr, lwrb_sz_t* br, uint16_t fl return 0; } btr = BUF_MIN(full, btr); - buff_r_ptr = LWRB_LOAD(buff->r, memory_order_acquire); + r_ptr = LWRB_LOAD(buff->r_ptr, memory_order_acquire); /* Step 1: Read data from linear part of buffer */ - tocopy = BUF_MIN(buff->size - buff_r_ptr, btr); - BUF_MEMCPY(d, &buff->buff[buff_r_ptr], tocopy); - buff_r_ptr += tocopy; + tocopy = BUF_MIN(buff->size - r_ptr, btr); + BUF_MEMCPY(d_ptr, &buff->buff[r_ptr], tocopy); + d_ptr += tocopy; + r_ptr += tocopy; btr -= tocopy; /* Step 2: Read data from beginning of buffer (overflow part) */ if (btr > 0) { - BUF_MEMCPY(&d[tocopy], buff->buff, btr); - buff_r_ptr = btr; + BUF_MEMCPY(d_ptr, buff->buff, btr); + r_ptr = btr; } /* Step 3: Check end of buffer */ - if (buff_r_ptr >= buff->size) { - buff_r_ptr = 0; + if (r_ptr >= buff->size) { + r_ptr = 0; } /* * Write final value to the actual running variable. * This is to ensure no write operation can access intermediate data */ - LWRB_STORE(buff->r, buff_r_ptr, memory_order_release); + LWRB_STORE(buff->r_ptr, r_ptr, memory_order_release); BUF_SEND_EVT(buff, LWRB_EVT_READ, tocopy + btr); - if (br != NULL) { - *br = tocopy + btr; + if (bread != NULL) { + *bread = tocopy + btr; } return 1; } @@ -284,8 +319,8 @@ lwrb_read_ex(lwrb_t* buff, void* data, lwrb_sz_t btr, lwrb_sz_t* br, uint16_t fl */ lwrb_sz_t lwrb_peek(const lwrb_t* buff, lwrb_sz_t skip_count, void* data, lwrb_sz_t btp) { - lwrb_sz_t full, tocopy, r; - uint8_t* d = data; + lwrb_sz_t full = 0, tocopy = 0, r_ptr = 0; + uint8_t* d_ptr = data; if (!BUF_IS_VALID(buff) || data == NULL || btp == 0) { return 0; @@ -299,11 +334,11 @@ lwrb_peek(const lwrb_t* buff, lwrb_sz_t skip_count, void* data, lwrb_sz_t btp) { if (skip_count >= full) { return 0; } - r = LWRB_LOAD(buff->r, memory_order_relaxed); - r += skip_count; + r_ptr = LWRB_LOAD(buff->r_ptr, memory_order_relaxed); + r_ptr += skip_count; full -= skip_count; - if (r >= buff->size) { - r -= buff->size; + if (r_ptr >= buff->size) { + r_ptr -= buff->size; } /* Check maximum number of bytes available to read after skip */ @@ -313,13 +348,14 @@ lwrb_peek(const lwrb_t* buff, lwrb_sz_t skip_count, void* data, lwrb_sz_t btp) { } /* Step 1: Read data from linear part of buffer */ - tocopy = BUF_MIN(buff->size - r, btp); - BUF_MEMCPY(d, &buff->buff[r], tocopy); + tocopy = BUF_MIN(buff->size - r_ptr, btp); + BUF_MEMCPY(d_ptr, &buff->buff[r_ptr], tocopy); + d_ptr += tocopy; btp -= tocopy; /* Step 2: Read data from beginning of buffer (overflow part) */ if (btp > 0) { - BUF_MEMCPY(&d[tocopy], buff->buff, btp); + BUF_MEMCPY(d_ptr, buff->buff, btp); } return tocopy + btp; } @@ -331,7 +367,7 @@ lwrb_peek(const lwrb_t* buff, lwrb_sz_t skip_count, void* data, lwrb_sz_t btp) { */ lwrb_sz_t lwrb_get_free(const lwrb_t* buff) { - lwrb_sz_t size, w, r; + lwrb_sz_t size = 0, w_ptr = 0, r_ptr = 0; if (!BUF_IS_VALID(buff)) { return 0; @@ -347,7 +383,7 @@ lwrb_get_free(const lwrb_t* buff) { * even if buff->w or buff->r get changed during interrupt processing. * * They may change during load operation, important is that - * they do not change during if-elseif-else operations following these assignments. + * they do not change during if-else operations following these assignments. * * lwrb_get_free is only called for write purpose, and when in FIFO mode, then: * - buff->w pointer will not change by another process/interrupt because we are in write mode just now @@ -355,15 +391,13 @@ lwrb_get_free(const lwrb_t* buff) { * buffer will see "free size" less than it actually is. This is not a problem, application can * always try again to write more data to remaining free memory that was read just during copy operation */ - w = LWRB_LOAD(buff->w, memory_order_relaxed); - r = LWRB_LOAD(buff->r, memory_order_relaxed); + w_ptr = LWRB_LOAD(buff->w_ptr, memory_order_relaxed); + r_ptr = LWRB_LOAD(buff->r_ptr, memory_order_relaxed); - if (w == r) { - size = buff->size; - } else if (r > w) { - size = r - w; + if (w_ptr >= r_ptr) { + size = buff->size - (w_ptr - r_ptr); } else { - size = buff->size - (w - r); + size = r_ptr - w_ptr; } /* Buffer free size is always 1 less than actual size */ @@ -377,7 +411,7 @@ lwrb_get_free(const lwrb_t* buff) { */ lwrb_sz_t lwrb_get_full(const lwrb_t* buff) { - lwrb_sz_t size, w, r; + lwrb_sz_t size = 0, w_ptr = 0, r_ptr = 0; if (!BUF_IS_VALID(buff)) { return 0; @@ -393,7 +427,7 @@ lwrb_get_full(const lwrb_t* buff) { * even if buff->w or buff->r get changed during interrupt processing. * * They may change during load operation, important is that - * they do not change during if-elseif-else operations following these assignments. + * they do not change during if-else operations following these assignments. * * lwrb_get_full is only called for read purpose, and when in FIFO mode, then: * - buff->r pointer will not change by another process/interrupt because we are in read mode just now @@ -401,15 +435,13 @@ lwrb_get_full(const lwrb_t* buff) { * buffer will see "full size" less than it really is. This is not a problem, application can * always try again to read more data from remaining full memory that was written just during copy operation */ - w = LWRB_LOAD(buff->w, memory_order_relaxed); - r = LWRB_LOAD(buff->r, memory_order_relaxed); + w_ptr = LWRB_LOAD(buff->w_ptr, memory_order_relaxed); + r_ptr = LWRB_LOAD(buff->r_ptr, memory_order_relaxed); - if (w == r) { - size = 0; - } else if (w > r) { - size = w - r; + if (w_ptr >= r_ptr) { + size = w_ptr - r_ptr; } else { - size = buff->size - (r - w); + size = buff->size - (r_ptr - w_ptr); } return size; } @@ -423,8 +455,8 @@ lwrb_get_full(const lwrb_t* buff) { void lwrb_reset(lwrb_t* buff) { if (BUF_IS_VALID(buff)) { - LWRB_STORE(buff->w, 0, memory_order_release); - LWRB_STORE(buff->r, 0, memory_order_release); + LWRB_STORE(buff->w_ptr, 0, memory_order_release); + LWRB_STORE(buff->r_ptr, 0, memory_order_release); BUF_SEND_EVT(buff, LWRB_EVT_RESET, 0); } } @@ -436,10 +468,13 @@ lwrb_reset(lwrb_t* buff) { */ void* lwrb_get_linear_block_read_address(const lwrb_t* buff) { + lwrb_sz_t ptr = 0; + if (!BUF_IS_VALID(buff)) { return NULL; } - return &buff->buff[buff->r]; + ptr = LWRB_LOAD(buff->r_ptr, memory_order_relaxed); + return &buff->buff[ptr]; } /** @@ -449,7 +484,7 @@ lwrb_get_linear_block_read_address(const lwrb_t* buff) { */ lwrb_sz_t lwrb_get_linear_block_read_length(const lwrb_t* buff) { - lwrb_sz_t len, w, r; + lwrb_sz_t len = 0, w_ptr = 0, r_ptr = 0; if (!BUF_IS_VALID(buff)) { return 0; @@ -459,13 +494,13 @@ lwrb_get_linear_block_read_length(const lwrb_t* buff) { * Use temporary values in case they are changed during operations. * See lwrb_buff_free or lwrb_buff_full functions for more information why this is OK. */ - w = LWRB_LOAD(buff->w, memory_order_relaxed); - r = LWRB_LOAD(buff->r, memory_order_relaxed); + w_ptr = LWRB_LOAD(buff->w_ptr, memory_order_relaxed); + r_ptr = LWRB_LOAD(buff->r_ptr, memory_order_relaxed); - if (w > r) { - len = w - r; - } else if (r > w) { - len = buff->size - r; + if (w_ptr > r_ptr) { + len = w_ptr - r_ptr; + } else if (r_ptr > w_ptr) { + len = buff->size - r_ptr; } else { len = 0; } @@ -483,7 +518,7 @@ lwrb_get_linear_block_read_length(const lwrb_t* buff) { */ lwrb_sz_t lwrb_skip(lwrb_t* buff, lwrb_sz_t len) { - lwrb_sz_t full, r; + lwrb_sz_t full = 0, r_ptr = 0; if (!BUF_IS_VALID(buff) || len == 0) { return 0; @@ -491,12 +526,12 @@ lwrb_skip(lwrb_t* buff, lwrb_sz_t len) { full = lwrb_get_full(buff); len = BUF_MIN(len, full); - r = LWRB_LOAD(buff->r, memory_order_acquire); - r += len; - if (r >= buff->size) { - r -= buff->size; + r_ptr = LWRB_LOAD(buff->r_ptr, memory_order_acquire); + r_ptr += len; + if (r_ptr >= buff->size) { + r_ptr -= buff->size; } - LWRB_STORE(buff->r, r, memory_order_release); + LWRB_STORE(buff->r_ptr, r_ptr, memory_order_release); BUF_SEND_EVT(buff, LWRB_EVT_READ, len); return len; } @@ -508,10 +543,13 @@ lwrb_skip(lwrb_t* buff, lwrb_sz_t len) { */ void* lwrb_get_linear_block_write_address(const lwrb_t* buff) { + lwrb_sz_t ptr = 0; + if (!BUF_IS_VALID(buff)) { return NULL; } - return &buff->buff[buff->w]; + ptr = LWRB_LOAD(buff->w_ptr, memory_order_relaxed); + return &buff->buff[ptr]; } /** @@ -521,7 +559,7 @@ lwrb_get_linear_block_write_address(const lwrb_t* buff) { */ lwrb_sz_t lwrb_get_linear_block_write_length(const lwrb_t* buff) { - lwrb_sz_t len, w, r; + lwrb_sz_t len = 0, w_ptr = 0, r_ptr = 0; if (!BUF_IS_VALID(buff)) { return 0; @@ -531,17 +569,17 @@ lwrb_get_linear_block_write_length(const lwrb_t* buff) { * Use temporary values in case they are changed during operations. * See lwrb_buff_free or lwrb_buff_full functions for more information why this is OK. */ - w = LWRB_LOAD(buff->w, memory_order_relaxed); - r = LWRB_LOAD(buff->r, memory_order_relaxed); + w_ptr = LWRB_LOAD(buff->w_ptr, memory_order_relaxed); + r_ptr = LWRB_LOAD(buff->r_ptr, memory_order_relaxed); - if (w >= r) { - len = buff->size - w; + if (w_ptr >= r_ptr) { + len = buff->size - w_ptr; /* * When read pointer is 0, * maximal length is one less as if too many bytes * are written, buffer would be considered empty again (r == w) */ - if (r == 0) { + if (r_ptr == 0) { /* * Cannot overflow: * - If r is not 0, statement does not get called @@ -550,7 +588,7 @@ lwrb_get_linear_block_write_length(const lwrb_t* buff) { --len; } } else { - len = r - w - 1; + len = r_ptr - w_ptr - 1; } return len; } @@ -567,7 +605,7 @@ lwrb_get_linear_block_write_length(const lwrb_t* buff) { */ lwrb_sz_t lwrb_advance(lwrb_t* buff, lwrb_sz_t len) { - lwrb_sz_t free, w; + lwrb_sz_t free = 0, w_ptr = 0; if (!BUF_IS_VALID(buff) || len == 0) { return 0; @@ -576,12 +614,12 @@ lwrb_advance(lwrb_t* buff, lwrb_sz_t len) { /* Use local variables before writing back to main structure */ free = lwrb_get_free(buff); len = BUF_MIN(len, free); - w = LWRB_LOAD(buff->w, memory_order_acquire); - w += len; - if (w >= buff->size) { - w -= buff->size; + w_ptr = LWRB_LOAD(buff->w_ptr, memory_order_acquire); + w_ptr += len; + if (w_ptr >= buff->size) { + w_ptr -= buff->size; } - LWRB_STORE(buff->w, w, memory_order_release); + LWRB_STORE(buff->w_ptr, w_ptr, memory_order_release); BUF_SEND_EVT(buff, LWRB_EVT_WRITE, len); return len; } @@ -601,7 +639,7 @@ lwrb_advance(lwrb_t* buff, lwrb_sz_t len) { */ uint8_t lwrb_find(const lwrb_t* buff, const void* bts, lwrb_sz_t len, lwrb_sz_t start_offset, lwrb_sz_t* found_idx) { - lwrb_sz_t full, r, max_x; + lwrb_sz_t full = 0, r_ptr = 0, buff_r_ptr = 0, max_x = 0; uint8_t found = 0; const uint8_t* needle = bts; @@ -616,25 +654,28 @@ lwrb_find(const lwrb_t* buff, const void* bts, lwrb_sz_t len, lwrb_sz_t start_of return 0; } + /* Get actual buffer read pointer for this search */ + buff_r_ptr = LWRB_LOAD(buff->r_ptr, memory_order_relaxed); + /* Max number of for loops is buff_full - input_len - start_offset of buffer length */ max_x = full - len; for (lwrb_sz_t skip_x = start_offset; !found && skip_x <= max_x; ++skip_x) { found = 1; /* Found by default */ /* Prepare the starting point for reading */ - r = buff->r + skip_x; - if (r >= buff->size) { - r -= buff->size; + r_ptr = buff_r_ptr + skip_x; + if (r_ptr >= buff->size) { + r_ptr -= buff->size; } /* Search in the buffer */ - for (lwrb_sz_t i = 0; i < len; ++i) { - if (buff->buff[r] != needle[i]) { + for (lwrb_sz_t idx = 0; idx < len; ++idx) { + if (buff->buff[r_ptr] != needle[idx]) { found = 0; break; } - if (++r >= buff->size) { - r = 0; + if (++r_ptr >= buff->size) { + r_ptr = 0; } } if (found) { diff --git a/lwrb/src/lwrb/lwrb_ex.c b/lwrb/src/lwrb/lwrb_ex.c index 9893759..4d67017 100644 --- a/lwrb/src/lwrb/lwrb_ex.c +++ b/lwrb/src/lwrb/lwrb_ex.c @@ -4,7 +4,7 @@ */ /* - * Copyright (c) 2022 Tilen MAJERLE + * Copyright (c) 2024 Tilen MAJERLE * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -29,7 +29,7 @@ * This file is part of LwRB - Lightweight ring buffer library. * * Author: Tilen MAJERLE - * Version: v3.1.0 + * Version: v3.2.0 */ #include "lwrb/lwrb.h"