skullc-peripherals/Utility/Inc/utility_ringbuffer.hpp
erki ea99a8a6ba
All checks were successful
continuous-integration/drone/push Build is passing
gitea/skullc-peripherals/pipeline/head This commit looks good
Other: fix ./clang-format to apply new-lines for else statements properly
2022-12-10 17:15:55 +02:00

229 lines
4.3 KiB
C++

/*
* utility_ringbuffer.hpp
*
* Created on: Mar 12, 2021
* Author: erki
*/
#ifndef SKULLC_UTILITY_RINGBUFFER_HPP_
#define SKULLC_UTILITY_RINGBUFFER_HPP_
#include <array>
#include <cstddef>
#include <iterator>
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_;
if (tail_ == head_)
is_full_ = true;
}
template<typename... Args>
void emplace_back(Args&&... args)
{
if (size() == N)
return;
new (&*tail_) T(std::forward<Args>(args)...);
++tail_;
}
void increment_tail()
{
if (is_full_)
++head_;
++tail_;
}
void pop_front()
{
if (empty())
return;
++head_;
is_full_ = false;
}
reference front() { return *head_; }
const_reference front() const { return *head_; }
reference back()
{
if (empty())
return *tail_;
else
return *(tail_ - 1);
}
const_reference back() const
{
if (empty())
return *tail_;
else
return *(tail_ - 1);
}
size_type size() const
{
if (head_ == tail_)
{
if (is_full_)
return N;
else
return 0;
}
const typename Ringbuffer<T, N>::iterator::difference_type distance =
tail_ - head_;
if (distance > 0)
{
return distance;
}
else
{
return head_ - tail_ + 1;
}
}
constexpr size_type max_size() const { return N; }
bool empty() const { return size() == 0; }
private:
std::array<T, N> data_;
bool is_full_ = false;
iterator head_;
iterator tail_;
};
}// namespace Utility
#endif /* SKULLC_UTILITY_RINGBUFFER_HPP_ */