diff --git a/CMakeLists.txt b/CMakeLists.txt index fc3909d..6428fa4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,7 +14,7 @@ set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) # Add your executable -add_executable(${PROJECT_NAME} main.cpp ringbuffer.cpp) +add_executable(${PROJECT_NAME} main.cpp) # Add include directories for your header files target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) diff --git a/f1ll/ringbuffer.h b/f1ll/ringbuffer.h index ccdaab2..8bc10a5 100644 --- a/f1ll/ringbuffer.h +++ b/f1ll/ringbuffer.h @@ -1,70 +1,224 @@ /* * ringbuffer.h * - * Created on: Sep 14, 2021 + * Created on: May 31, 2025 * Author: Attila Body */ #pragma once -#include -#include +#include +#include +#include namespace f1ll { -class ringbuffer +class iringbuffer { public: - ringbuffer(uint8_t *buffer, uint16_t size); + using size_type = size_t; - /// @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. + /// @brief Copies data to the ring buffer (without committing it) /// @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); + /// @retval Length of the data copied (differs when not all data can fit) + virtual size_type put(uint8_t const *data, size_type len) = 0; - /// @brief Commits the data already placed into the buffer and notifies the consumer about it's availability - void commit(); + /// @brief Commits the data already placed into the buffer and notifies the + /// consumer about it's availability + virtual void commit() = 0; /// @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 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. + virtual bool get_chunk(size_type len_requested, uint8_t *&data, size_type &len) = 0; - /// @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 Marks the chunk returned by ringbuffer_GetChunk as available. + /// @param consumed The length of the chunk as returned by + /// ringbuffer_GetChunk(..., len) + virtual void consumed(size_type len) = 0; - /// @brief Returns the number of uncommited bytes in the ring buffer - uint16_t uncommited() const; + /// @brief Returns the number of uncommited bytes in the ring buffer. + virtual size_type uncommited() const = 0; - /// @brief Returns the number of commited bytes in the ring buffer - uint16_t commited() const; + /// @brief Returns the number of commited bytes in the ring buffer. + virtual size_type commited() const = 0; - /// @brief Discards the uncommited data in the ring buffer - void discard(); + /// @brief Discards the uncommited data in the ring buffer. + virtual void discard() = 0; -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 + /// @brief Returns the size of the internal buffer. + /// One byte in the buffer is always used as boundary + virtual size_type size() const = 0; - 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); + /// @brief Returns the size of the used place in the buffer + // (including non-commited data) + virtual size_type used() const = 0; - uint16_t buffer_used() const; - uint16_t buffer_free() const; + /// @brief Returns the free storage capacity of the buffer + virtual size_type unused() const = 0; }; -} // namespace \ No newline at end of file +template class ringbuffer : public iringbuffer +{ +public: + ringbuffer() = default; + + size_type put(uint8_t const *data, size_type len) override; + void commit() override; + bool get_chunk(size_type len_requested, uint8_t *&data, size_type &len) override; + void consumed(size_type len) override; + size_type uncommited() const override; + size_type commited() const override; + void discard() override; + size_type size() const override; + size_type used() const override; + size_type unused() const override; + +private: + uint8_t m_buffer[SZ]; //!< Data bufer + size_type m_head = 0; //!< Write position + size_type m_head_shadow = 0; //!< Shadowed write position for collecting data + //!< before committing it + size_type m_tail = 0; //!< Read position + + size_type marker_diff(size_type m1, size_type m2) const; +}; + +// +template iringbuffer::size_type ringbuffer::put(uint8_t const *data, size_type len) +{ + size_type chunk1 = 0; + size_type chunk2 = 0; + + if (!data || !len) { + return 0; + } + + size_type max_len = unused(); + len = len < max_len ? len : max_len; + + chunk1 = sizeof(m_buffer) - m_head_shadow; + if (chunk1 >= len) { + chunk1 = len; + } else { + chunk2 = len - chunk1; + } + + std::memcpy(m_buffer + m_head_shadow, data, chunk1); + m_head_shadow += chunk1; + if (m_head_shadow == sizeof(m_buffer)) { + m_head_shadow = 0; + } + + if (chunk2) { + std::memcpy(m_buffer, data + chunk1, chunk2); + m_head_shadow += chunk2; + if (m_head_shadow == sizeof(m_buffer)) { + m_head_shadow = 0; + } + } + + return len; +} + +template void ringbuffer::commit() +{ + iringbuffer::size_type uncommitted = marker_diff(m_head_shadow, m_head); + + if (!uncommitted) { + return; + } + + m_head = m_head_shadow; +}; + +template bool ringbuffer::get_chunk(size_type len_requested, uint8_t *&data, size_type &len) +{ + if (!len_requested) { + return false; + } + + size_type head = m_head; + size_type tail = m_tail; + size_type chunk_size = head >= tail ? head - tail : sizeof(m_buffer) - 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 == sizeof(m_buffer)) { + tail = 0; + } + + return tail != head; +} + +template void ringbuffer::consumed(size_type len) +{ + if (!len) { + return; + } + m_tail += len; + if (m_tail == sizeof(m_buffer)) { + m_tail = 0; + } +} + +template iringbuffer::size_type ringbuffer::uncommited() const +{ + return marker_diff(m_head_shadow, m_head); +} + +template iringbuffer::size_type ringbuffer::commited() const +{ + return marker_diff(m_head, m_tail); +} + +template void ringbuffer::discard() +{ + m_head_shadow = m_head; +} + +template iringbuffer::size_type ringbuffer::size() const +{ + return SZ; +} + +template iringbuffer::size_type ringbuffer::used() const +{ + return marker_diff(m_head_shadow, m_tail); +} + +template iringbuffer::size_type ringbuffer::unused() const +{ + return marker_diff(m_tail, m_head_shadow) - 1; +} + +template iringbuffer::size_type ringbuffer::marker_diff(size_type m1, size_type m2) const +{ + return m1 >= m2 ? m1 - m2 : sizeof(m_buffer) - m2 + m1; +} + +} // namespace f1ll \ No newline at end of file diff --git a/main.cpp b/main.cpp index 006d0a2..f01435a 100644 --- a/main.cpp +++ b/main.cpp @@ -6,21 +6,19 @@ 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<8> rb; + uint8_t dst_buffer[7]; + uint8_t data1[] = {0, 1, 2, 3}; + uint8_t data2[] = {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; + 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; } diff --git a/ringbuffer.cpp b/ringbuffer.cpp deleted file mode 100644 index d2c42b1..0000000 --- a/ringbuffer.cpp +++ /dev/null @@ -1,160 +0,0 @@ -/* - * ringbuffer.c - * - * Created on: Sep 14, 2021 - * Author: Attila Body - */ - -#include -// #include "macro_utils.h" -// #include "print_string.h" -// #include "taskregistry.h" - -#include - -#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 \ No newline at end of file