/** * \file lwrb_ex.c * \brief Lightweight ring buffer - extended functions */ /* * 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 in the Software without restriction, * including without limitation the rights to use, copy, modify, merge, * publish, distribute, sublicense, and/or sell copies of the Software, * and to permit persons to whom the Software is furnished to do so, * subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. * * This file is part of LwRB - Lightweight ring buffer library. * * Author: Tilen MAJERLE * Version: v3.2.0 */ #include "lwrb.h" #define LWRB_DEV #if defined(LWRB_DEV) /* Do not build if development mode isn't enabled */ #define BUF_IS_VALID(b) ((b) != NULL && (b)->buff != NULL && (b)->size > 0) #define BUF_MIN(x, y) ((x) < (y) ? (x) : (y)) /** * \brief Writes data to buffer with overwrite function, if no enough space to hold * complete input data object. * \note Similar to \ref lwrb_write but overwrites * \param[in] buff: Buffer handle * \param[in] data: Data to write to ring buffer * \param[in] btw: Bytes To Write, length * \return Number of bytes written to buffer, will always return btw * \note Functionality is primary two parts, always writes some linear region, then * writes the wrap region if there is more data to write. The r indicator is advanced if w overtakes * it. This operation is a read op as well as a write op. For thread-safety mutexes may be desired, * see documentation. */ lwrb_sz_t lwrb_overwrite(lwrb_t* buff, const void* data, lwrb_sz_t btw) { lwrb_sz_t orig_btw = btw, max_cap; const uint8_t* d = data; if (!BUF_IS_VALID(buff) || data == NULL || btw == 0) { return 0; } /* Process complete input array */ max_cap = buff->size - 1; /* Maximum capacity buffer can hold */ if (btw > max_cap) { /* * When data to write is larger than max buffer capacity, * we can reset the buffer and simply write last part of * the input buffer. * * This is done here, by calculating remaining * length and then advancing to the end of input buffer */ d += btw - max_cap; /* Advance data */ btw = max_cap; /* Limit data to write */ lwrb_reset(buff); /* Reset buffer */ } else { /* * Bytes to write is less than capacity * We have to perform max one skip operation, * but only if free memory is less than * btw, otherwise we skip the operation * and only write the data. */ lwrb_sz_t f = lwrb_get_free(buff); if (f < btw) { lwrb_skip(buff, btw - f); } } lwrb_write(buff, d, btw); return orig_btw; } /** * \brief Move one ring buffer to another, up to the amount of data in the source, or amount * of data free in the destination. * \param[in] dest: Buffer handle that the copied data will be written to * \param[in] src: Buffer handle that the copied data will come from. * Source buffer will be effectively read upon operation. * \return Number of bytes written to destination buffer * \note This operation is a read op to the source, on success it will update the r index. * As well as a write op to the destination, and may update the w index. * For thread-safety mutexes may be desired, see documentation. */ lwrb_sz_t lwrb_move(lwrb_t* dest, lwrb_t* src) { lwrb_sz_t len_to_copy, len_to_copy_orig, src_full, dest_free; if (!BUF_IS_VALID(dest) || !BUF_IS_VALID(src)) { return 0; } src_full = lwrb_get_full(src); dest_free = lwrb_get_free(dest); len_to_copy = BUF_MIN(src_full, dest_free); len_to_copy_orig = len_to_copy; /* Calculations for available length to copy is done above. We safely assume operations inside loop will properly complete. */ while (len_to_copy > 0) { lwrb_sz_t max_seq_read, max_seq_write, op_len; const uint8_t* d_src; uint8_t* d_dst; /* Calculate data */ max_seq_read = lwrb_get_linear_block_read_length(src); max_seq_write = lwrb_get_linear_block_write_length(dest); op_len = BUF_MIN(max_seq_read, max_seq_write); op_len = BUF_MIN(len_to_copy, op_len); /* Get addresses */ d_src = lwrb_get_linear_block_read_address(src); d_dst = lwrb_get_linear_block_write_address(dest); /* Byte by byte copy */ for (lwrb_sz_t i = 0; i < op_len; ++i) { *d_dst++ = *d_src++; } lwrb_advance(dest, op_len); lwrb_skip(src, op_len); len_to_copy -= op_len; if (op_len == 0) { /* Hard error... */ return 0; } } return len_to_copy_orig; } #endif /* defined(LWRB_DEV) */