From f9abcf437a261e91eca8be60a44d326aed66c9e4 Mon Sep 17 00:00:00 2001 From: Rados Vreljakovic Date: Wed, 22 Apr 2020 21:30:40 +0200 Subject: [PATCH] Added circual buffer implementation Signed-off-by: Rados Vreljakovic --- CMakeLists.txt | 9 ++ README.md | 29 ++++++ circular_buffer.cpp | 235 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 273 insertions(+) create mode 100644 CMakeLists.txt create mode 100644 circular_buffer.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..63cd970 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,9 @@ +cmake_minimum_required(VERSION 3.10) + +project(circ-buff) + +SET(CMAKE_CXX_STANDARD 11) + +add_executable(${PROJECT_NAME} + circular_buffer.cpp +) diff --git a/README.md b/README.md index 7fb2175..5a3ef76 100644 --- a/README.md +++ b/README.md @@ -17,3 +17,32 @@ 3. Generic data type (e.g. uint8_t) 4. Two put(data) overflow behaviors: overwrite and discard data. 2. Write tests showing your circular buffer in action. + + +## Circular buffer + +Circular buffer for different data types created as a templated class with fixed-size buffer. Two different behaviour added for circular buffer: + +1) Overwrite on full +2) Discard data when full + +To choose overwrite set `bool overwrite = true`, for discard data set `bool overwrite = false` + +Buffer instanced compile-time for fixed static size. Size of the buffer resolved +in compile time trough `int size` argument of templated class + +### How to build + +Dependencies: + - Cmake + - Ninja/make + - gcc/g++ compiler + +1) Clone this repository `cd ` +2) Navigate to build directory `cd build` +3) Execute `cmake .. -G Ninja` or `cmake .. -G make` +4) In build directory run `ninja` or `make` +5) Run example with `./circ-buff` + + + diff --git a/circular_buffer.cpp b/circular_buffer.cpp new file mode 100644 index 0000000..2dcc5bb --- /dev/null +++ b/circular_buffer.cpp @@ -0,0 +1,235 @@ +#include +#include +#include + +namespace cb_defines +{ + constexpr size_t cb_size = 4; +} + + +/** + * Universal circual buffer implementation for different data types and + * structures. Type and size solved in compile time + * + * @tparam T the type of data stored in buffer + * @tparam size Size of the buffer + */ +template +class CircularBuffer +{ +public: + + /** + * Class contstructor, always call default without any arguments as size is + * resolved in compile time + */ + CircularBuffer() = default; + ~CircularBuffer() = default; + + + CircularBuffer(const CircularBuffer &) = delete; + CircularBuffer &operator=(const CircularBuffer &) = delete; + CircularBuffer(CircularBuffer &&) = delete; + CircularBuffer &operator=(CircularBuffer &&) = delete; + + /** + * @brief Get buffer element from circular buffer + * + * @return item from head of buffer + */ + T dequeue() + { + // local lock_guard to lock mtx for guaranted destruction/exception + std::lock_guard lock(mtx); + + if (is_empty()) + { + throw std::runtime_error("Buffer is empty"); + } + + T item = m_cb_data[head]; + + T tmp; + m_cb_data[head] = tmp; + + head = (head + 1) % m_cb_size; + + num_of_elements--; + + return item; + } + + /** + * @brief Put data element into circular buffer + * + * @param item new data inserted into buffer + */ + void enqueue(T item) + { + // local lock_guard to lock mtx for guaranted destruction/exception + std::lock_guard lock(mtx); + + if (is_full()) + { + if (overwrite) + { + m_cb_data[(head + head_overwrite) % m_cb_size] = item; + head_overwrite = (head_overwrite + 1) % m_cb_size; + //std::cout << "Overwrite" << std::endl; + } + else + { + //std::cout << "Data discarded due cb full" << std::endl; + } + } + else + { + m_cb_data[tail] = item; + tail = (tail + 1) % m_cb_size; + head_overwrite = 0; + num_of_elements++; + } + } + /** + * Print current content of data and relevent infomations + */ + void print_buffer() + { + std::cout << "Current head " << head << ", tail " << tail << std::endl; + for (auto x : m_cb_data) + std::cout << x << " "; + + std::cout << std::endl; + } + + + /** + * Get item from front of buffer pointed by head + * + * @return buffer element from front + */ + T front() + { + // local lock_guard to lock mtx for guaranted destruction/exception + std::lock_guard lock(mtx); + + if (not is_empty()) + { + return m_cb_data[head]; + } + } + + /** + * Get item from back of buffer pointed by tail + * + * @return buffer element from back + */ + T back() + { + // local lock_guard to lock mtx for guaranted destruction/exception + std::lock_guard lock(mtx); + + if (not is_empty()) + { + return m_cb_data[tail]; + } + } + + /** + * Check if buffer is full + * + * @return true if buffer is full, false if not + */ + bool is_full() + { + return (num_of_elements >= m_cb_size); + } + + /** + * Check if buffer is empty + * + * @return true if buffer is empty, false if not + */ + bool is_empty() + { + return (num_of_elements == 0); + } + + /** + * Get current buffer size + * + * @return size of buffer + */ + size_t cb_size() + { + if (tail >= head) + return tail - head; + + return this->cb_size - head - tail; + } + +private: + CircularBuffer(uint32_t cb_size) = delete; + //CircularBuffer(CircBuf&&) = delete; + + /** If overwrite mode is used set to true, if discard data is required + * after buffer is full set overwrite to false + */ + bool overwrite = true; + + int m_cb_size = size; + int num_of_elements = 0; + + std::array m_cb_data; + + int head = 0; + int head_overwrite = 0; + int tail = 0; + + std::mutex mtx; + +}; + +int main() +{ + CircularBuffer cb; + + try + { + cb.enqueue(1); + cb.print_buffer(); + cb.enqueue(2); + cb.print_buffer(); + cb.enqueue(3); + cb.print_buffer(); + cb.enqueue(4); + cb.print_buffer(); + cb.enqueue(5); + cb.print_buffer(); + cb.enqueue(6); + cb.print_buffer(); + cb.enqueue(7); + cb.print_buffer(); + cb.enqueue(8); + cb.print_buffer(); + cb.enqueue(9); + cb.print_buffer(); + + std::cout << cb.dequeue() << std::endl; + cb.enqueue(1); + cb.print_buffer(); + std::cout << cb.dequeue() << std::endl; + cb.enqueue(2); + cb.print_buffer(); + std::cout << cb.dequeue() << std::endl; + cb.enqueue(3); + cb.print_buffer(); + } + catch(const std::exception& e) + { + std::cerr << "Exception: " << e.what() << '\n'; + } + + return 0; +}