Ringbuffer item
This commit is contained in:
parent
42335b7c99
commit
aba4741285
7
.gitignore
vendored
7
.gitignore
vendored
@ -1,3 +1,10 @@
|
||||
# ---> Clion
|
||||
.idea/
|
||||
|
||||
# ---> Build files and folders
|
||||
[Bb]uild/
|
||||
cmake-build-*/
|
||||
|
||||
# ---> Eclipse
|
||||
.metadata
|
||||
bin/
|
||||
|
||||
14
CMakeLists.txt
Normal file
14
CMakeLists.txt
Normal 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
33
Utility/CMakeLists.txt
Normal 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()
|
||||
230
Utility/Inc/utility_ringbuffer.hpp
Normal file
230
Utility/Inc/utility_ringbuffer.hpp
Normal 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_ */
|
||||
10
Utility/Src/utility_logger.cpp
Normal file
10
Utility/Src/utility_logger.cpp
Normal file
@ -0,0 +1,10 @@
|
||||
/*
|
||||
* utility_logger.cpp
|
||||
*
|
||||
* Created on: Mar 13, 2021
|
||||
* Author: erki
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
6
Utility/Tests/main.cpp
Normal file
6
Utility/Tests/main.cpp
Normal 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>
|
||||
250
Utility/Tests/ringbuffer.cpp
Normal file
250
Utility/Tests/ringbuffer.cpp
Normal 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
5
conanfile.txt
Normal file
@ -0,0 +1,5 @@
|
||||
[requires]
|
||||
catch2/[>=2.0.0,<3.0.0]
|
||||
|
||||
[generators]
|
||||
cmake_find_package
|
||||
Loading…
x
Reference in New Issue
Block a user