Ringbuffer item

This commit is contained in:
Erki 2021-03-14 15:19:04 +02:00
parent 42335b7c99
commit aba4741285
8 changed files with 555 additions and 0 deletions

7
.gitignore vendored
View File

@ -1,3 +1,10 @@
# ---> Clion
.idea/
# ---> Build files and folders
[Bb]uild/
cmake-build-*/
# ---> Eclipse # ---> Eclipse
.metadata .metadata
bin/ bin/

14
CMakeLists.txt Normal file
View File

@ -0,0 +1,14 @@
cmake_minimum_required(VERSION 3.14 FATAL_ERROR)
project(SkullC)
set(CMAKE_MODULE_PATH ${CMAKE_BINARY_DIR})
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra")
option(WITH_TESTS "Enable unit testing." OFF)
if(WITH_TESTS)
enable_testing()
endif()
add_subdirectory(Utility)

33
Utility/CMakeLists.txt Normal file
View File

@ -0,0 +1,33 @@
cmake_minimum_required(VERSION 3.14 FATAL_ERROR)
add_library(utility STATIC
Src/utility_logger.cpp
)
add_library(skullc::utility ALIAS utility)
target_include_directories(utility
PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/Inc
)
if(WITH_TESTS)
find_package(Catch2 REQUIRED)
add_executable(utility-tests
Tests/main.cpp
Tests/ringbuffer.cpp
)
target_link_libraries(utility-tests
PUBLIC
skullc::utility
Catch2::Catch2
)
include(CTest)
include(Catch)
catch_discover_tests(utility-tests)
endif()

View File

@ -0,0 +1,230 @@
/*
* utility_ringbuffer.hpp
*
* Created on: Mar 12, 2021
* Author: erki
*/
#ifndef UTILITY_RINGBUFFER_HPP_
#define UTILITY_RINGBUFFER_HPP_
#include <array>
#include <iterator>
#include <cstddef>
namespace Utility
{
template<typename T, size_t N>
class Ringbuffer
{
public:
using value_type = T;
using size_type = std::size_t;
using difference_type = std::ptrdiff_t;
using reference = value_type&;
using const_reference = const value_type&;
using pointer = value_type*;
using const_pointer = const value_type*;
struct iterator
{
using iterator_category = std::forward_iterator_tag;
using difference_type = std::ptrdiff_t;
using value_type = T;
using pointer = value_type*;
using reference = value_type&;
iterator(const pointer ptr, const pointer begin, const pointer end)
: _ptr(ptr)
, _arr_begin(begin)
, _arr_end(end)
{ }
iterator(const iterator&) = default;
iterator(iterator&&) noexcept = default;
iterator& operator=(const iterator&) = default;
reference operator*() const
{
return *_ptr;
}
pointer operator->()
{
return _ptr;
}
iterator& operator++()
{
_ptr++;
if (_ptr == _arr_end)
_ptr = _arr_begin;
return *this;
}
iterator operator++(int)
{
iterator tmp = *this;
++(*this);
return tmp;
}
iterator operator+(const difference_type n) const
{
const pointer naive = _ptr + n;
if (naive < _arr_end)
{
return iterator(naive, _arr_begin, _arr_end);
}
else
{
const pointer remainder = pointer(naive - _arr_end);
return iterator(_arr_begin + difference_type(remainder), _arr_begin, _arr_end);
}
}
iterator operator-(const difference_type n) const
{
const pointer naive = _ptr - n;
if (naive >= _arr_begin)
{
return iterator(naive, _arr_begin, _arr_end);
}
else
{
const pointer remainder = pointer(_arr_begin - naive);
return iterator(_arr_end - difference_type(remainder), _arr_begin, _arr_end);
}
}
friend bool operator==(const iterator& a, const iterator& b)
{
return a._ptr == b._ptr;
}
friend bool operator!=(const iterator& a, const iterator& b)
{
return a._ptr != b._ptr;
}
friend difference_type operator-(const iterator& a, const iterator& b)
{
return a._ptr - b._ptr;
}
friend difference_type operator+(const iterator& a, const iterator& b)
{
return a._ptr + b._ptr;
}
private:
pointer _ptr;
pointer _arr_begin;
pointer _arr_end;
};
Ringbuffer()
: _head(&_data[0], &_data[0], &_data[N])
, _tail(_head)
{ }
Ringbuffer(const Ringbuffer&) = delete;
Ringbuffer(Ringbuffer&&) noexcept = default;
iterator begin()
{
return _head;
}
iterator end()
{
return _tail;
}
void clear()
{
_head = _tail;
}
void push_back(const T& value)
{
if (size() == N)
return;
*_tail = value;
++_tail;
}
template<typename... Args>
void emplace_back(Args&&... args)
{
if (size() == N)
return;
new (&*_tail)T(std::forward<Args>(args)...);
return *(_tail++);
}
void pop_front()
{
if (empty())
return;
++_head;
}
reference front()
{
return *_head;
}
const_reference front() const
{
return *_head;
}
reference back()
{
return *(_tail - 1);
}
const_reference back() const
{
return *(_tail - 1);
}
size_type size() const
{
if (_head == _tail)
return 0;
const typename Ringbuffer<T, N>::iterator::difference_type distance = _tail - _head;
if (distance > 0)
{
return distance;
}
else
{
return _head - _tail;
}
}
bool empty() const
{
return size() == 0;
}
private:
std::array<T, N> _data;
iterator _head;
iterator _tail;
};
}
#endif /* UTILITY_RINGBUFFER_HPP_ */

View File

@ -0,0 +1,10 @@
/*
* utility_logger.cpp
*
* Created on: Mar 13, 2021
* Author: erki
*/

6
Utility/Tests/main.cpp Normal file
View File

@ -0,0 +1,6 @@
//
// Created by erki on 13.03.21.
//
#define CATCH_CONFIG_MAIN // This tells Catch to provide a main() - only do this in one cpp file
#include <catch2/catch.hpp>

View File

@ -0,0 +1,250 @@
//
// Created by erki on 13.03.21.
//
#include <catch2/catch.hpp>
#include "utility_ringbuffer.hpp"
template<size_t N>
using Ringbuffer = Utility::Ringbuffer<int, N>;
TEST_CASE("Ringbuffer iterator", "[ringbuffer],[iterator]")
{
using iterator = Ringbuffer<10>::iterator;
const auto begin = iterator::pointer(10);
const auto end = iterator::pointer(21);
iterator it = iterator(iterator::pointer(15), begin, end);
SECTION("Incrementing postfix increases its pointer and returns initial.")
{
iterator it_second = it++;
REQUIRE(it - it_second == iterator::difference_type(1));
}
SECTION("Incrementing prefix increases its pointer and returns new.")
{
const iterator original = it;
iterator it_second = ++it;
REQUIRE(it_second == it);
REQUIRE(it - original == iterator::difference_type(1));
}
SECTION("Subtracting 1 decreases its pointer.")
{
iterator it_second = it - 1;
REQUIRE(it - it_second == iterator::difference_type(1));
}
SECTION("Adding 1 increases its pointer.")
{
iterator it_second = it + 1;
REQUIRE(it_second - it == iterator::difference_type(1));
}
}
TEST_CASE("Ringbuffer iterator at the end", "[ringbuffer],[iterator]")
{
using iterator = Ringbuffer<10>::iterator;
const auto begin = iterator::pointer(10);
const auto end = iterator::pointer(21);
iterator it = iterator(end - 1, begin, end);
const auto it_begin = iterator(begin, begin, end);
SECTION("Incrementing postfix sets the pointer to begin().")
{
it++;
REQUIRE(it == it_begin);
}
SECTION("Incrementing prefix sets the pointer to begin().")
{
++it;
REQUIRE(it == it_begin);
}
SECTION("Adding 1 sets the pointer to begin().")
{
const iterator it_second = it + 1;
REQUIRE(it_second == it_begin);
}
}
TEST_CASE("Ringbuffer iterator at the beginning", "[ringbuffer],[iterator]")
{
using iterator = Ringbuffer<10>::iterator;
const auto begin = iterator::pointer(10);
const auto end = iterator::pointer(21);
iterator it = iterator(begin, begin, end);
const auto it_last = iterator(end - 1, begin, end);
SECTION("Subtracting 1 sets the pointer to last element.")
{
const iterator it_second = it - 1;
REQUIRE(it_last == it_second);
}
}
TEST_CASE("Constructed buffer is empty.", "[ringbuffer]")
{
Ringbuffer<10> buffer;
REQUIRE(buffer.begin() == buffer.end());
REQUIRE(buffer.size() == 0);
REQUIRE(buffer.empty());
}
TEST_CASE("Adding single element.", "[ringbuffer]")
{
Ringbuffer<10> buffer;
const auto old_end = buffer.end();
const auto old_begin = buffer.begin();
buffer.push_back(1);
SECTION("Increases size and empty appropriately.")
{
REQUIRE(buffer.size() == 1);
REQUIRE(!buffer.empty());
}
SECTION("Updates end() appropriately.")
{
REQUIRE(old_end != buffer.end());
}
SECTION("begin() remains the same.")
{
REQUIRE(old_begin == buffer.begin());
}
SECTION("Makes begin() refer to the inserted member.")
{
REQUIRE(*old_begin == 1);
REQUIRE(*buffer.begin() == 1);
}
SECTION("Distance between begin and end is 1.")
{
REQUIRE(buffer.end() - buffer.begin() == 1);
}
SECTION("Makes back() refer to the first element.")
{
REQUIRE(buffer.back() == 1);
}
SECTION("Makes front() refer to the first element.")
{
REQUIRE(buffer.front() == 1);
}
SECTION("Makes front() and back() refer to the same memory address.")
{
REQUIRE(&buffer.front() == &buffer.back());
}
}
TEST_CASE("Adding multiple elements.", "[ringbuffer]")
{
Ringbuffer<10> buffer;
const auto old_begin = buffer.begin();
buffer.push_back(1);
buffer.push_back(2);
buffer.push_back(3);
SECTION("Increases size and empty appropriately.")
{
REQUIRE(buffer.size() == 3);
REQUIRE(!buffer.empty());
}
SECTION("Makes front() refer to the first element.")
{
REQUIRE(buffer.front() == 1);
}
SECTION("Makes back() refer to the last element.")
{
REQUIRE(buffer.back() == 3);
}
SECTION("Updates begin() and end() appropriately.")
{
REQUIRE(old_begin == buffer.begin());
REQUIRE(buffer.end() - buffer.begin() == 3);
}
}
TEST_CASE("Removing elements from the ringbuffer.", "[ringbuffer]")
{
Ringbuffer<10> buffer;
const auto old_begin = buffer.begin();
buffer.push_back(1);
buffer.push_back(2);
buffer.push_back(3);
buffer.pop_front();
SECTION("Updates the front() appropriately.")
{
REQUIRE(buffer.front() == 2);
}
SECTION("Updates begin() appropriately.")
{
REQUIRE(buffer.begin() - old_begin == 1);
}
SECTION("Updates size() appropriately.")
{
REQUIRE(buffer.size() == 2);
}
SECTION("Erasing remaining elements")
{
buffer.pop_front();
buffer.pop_front();
SECTION("Updates empty() appropriately.")
{
REQUIRE(buffer.empty());
}
SECTION("Updates begin() and end() appropriately.")
{
REQUIRE(buffer.begin() == buffer.end());
}
SECTION("Updates size() appropriately.")
{
REQUIRE(buffer.size() == 0);
}
}
}
TEST_CASE("Clearing a ringbuffer works.", "[ringbuffer]")
{
Ringbuffer<10> buffer;
buffer.push_back(1);
buffer.push_back(2);
buffer.push_back(3);
buffer.clear();
SECTION("Updates size() and empty() appropriately.")
{
REQUIRE(buffer.empty());
REQUIRE(buffer.size() == 0);
}
SECTION("Sets begin() and end() pointers appropriately.")
{
REQUIRE(buffer.begin() == buffer.end());
}
}

5
conanfile.txt Normal file
View File

@ -0,0 +1,5 @@
[requires]
catch2/[>=2.0.0,<3.0.0]
[generators]
cmake_find_package