Program Listing for File ring_buffer.hpp¶
↰ Return to documentation for file (include/util/ring_buffer.hpp)
// SPDX-FileCopyrightText: 2021 Christian Göhring <mostsig@gmail.com>
// SPDX-License-Identifier: MIT
#ifndef THAT_THIS_UTIL_RING_BUFFER_HEADER_IS_ALREADY_INCLUDED
#define THAT_THIS_UTIL_RING_BUFFER_HEADER_IS_ALREADY_INCLUDED
#include <algorithm>
#include <array>
#include <cstddef>
#include <iterator>
#include <stdexcept>
#include <utility>
#ifdef UTIL_ASSERT
#include "assert.hpp"
#endif
namespace util {
namespace detail {
template <bool IsConst, class T, std::size_t N>
class ring_buffer_iterator;
} // namespace detail
template <class T, std::size_t N>
class ring_buffer {
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*;
using iterator = detail::ring_buffer_iterator<false, T, N>;
using const_iterator = detail::ring_buffer_iterator<true, T, N>;
using reverse_iterator = std::reverse_iterator<iterator>;
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
ring_buffer() = default;
~ring_buffer() = default;
ring_buffer(ring_buffer&&) noexcept = default;
ring_buffer(const ring_buffer&) noexcept = default;
auto operator=(ring_buffer&&) noexcept -> ring_buffer& = default;
auto operator=(const ring_buffer&) noexcept -> ring_buffer& = default;
constexpr ring_buffer(std::initializer_list<T> ilist);
constexpr auto at(size_type pos) -> reference;
constexpr auto at(size_type pos) const -> const_reference;
constexpr auto operator[](size_type pos) -> reference;
constexpr auto operator[](size_type pos) const -> const_reference;
constexpr auto front() -> reference;
constexpr auto front() const -> const_reference;
constexpr auto back() -> reference;
constexpr auto back() const -> const_reference;
constexpr auto data() noexcept -> pointer;
constexpr auto data() const noexcept -> const_pointer;
constexpr auto empty() const noexcept -> bool;
constexpr auto size() const noexcept -> size_type;
constexpr auto max_size() const noexcept -> size_type;
constexpr void push_front(const T& value);
constexpr void push_back(const T& value);
private:
std::array<T, N> elements;
std::size_t first = 0; // points to the beginning of the elements
std::size_t last = 0; // points to the last of the elements
void inc() noexcept;
void dec() noexcept;
};
template <class T, std::size_t N>
constexpr ring_buffer<T, N>::ring_buffer(std::initializer_list<T> ilist) {
if (ilist.size() > N) {
std::copy_n(ilist.end() - N, N, elements.begin());
last = N - 1;
} else {
std::copy_n(ilist.begin(), ilist.size(), elements.begin());
last = ilist.size() - 1;
}
}
template <class T, std::size_t N>
constexpr auto ring_buffer<T, N>::at(size_type pos) -> reference {
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast) @see Effective C++ by Scott Meyers
return const_cast<reference>(const_cast<const ring_buffer<T, N>*>(this)->at(pos));
}
template <class T, std::size_t N>
constexpr auto ring_buffer<T, N>::at(size_type pos) const -> const_reference {
if (pos >= size()) {
throw std::out_of_range{"pos is out of range"};
}
return operator[](pos);
}
template <class T, std::size_t N>
constexpr auto ring_buffer<T, N>::operator[](size_type pos) -> reference {
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast) @see Effective C++ by Scott Meyers
return const_cast<reference>(const_cast<const ring_buffer<T, N>*>(this)->operator[](pos));
}
template <class T, std::size_t N>
constexpr auto ring_buffer<T, N>::operator[](size_type pos) const -> const_reference {
#ifdef UTIL_ASSERT
util_assert(pos < size());
#endif
return elements[(first + pos) % N];
}
template <class T, std::size_t N>
constexpr auto ring_buffer<T, N>::front() -> reference {
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast) @see Effective C++ by Scott Meyers
return const_cast<reference>(const_cast<const ring_buffer<T, N>*>(this)->front());
}
template <class T, std::size_t N>
constexpr auto ring_buffer<T, N>::front() const -> const_reference {
return elements[first];
}
template <class T, std::size_t N>
constexpr auto ring_buffer<T, N>::back() -> reference {
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast) @see Effective C++ by Scott Meyers
return const_cast<reference>(const_cast<const ring_buffer<T, N>*>(this)->back());
}
template <class T, std::size_t N>
constexpr auto ring_buffer<T, N>::back() const -> const_reference {
return elements[last];
}
template <class T, std::size_t N>
constexpr auto ring_buffer<T, N>::data() noexcept -> pointer {
return elements.data();
}
template <class T, std::size_t N>
constexpr auto ring_buffer<T, N>::data() const noexcept -> const_pointer {
return elements.data();
}
template <class T, std::size_t N>
constexpr auto ring_buffer<T, N>::empty() const noexcept -> bool {
return first == 0 && last == 0;
}
template <class T, std::size_t N>
constexpr auto ring_buffer<T, N>::size() const noexcept -> size_type {
if (first == 0 && last < N - 1) {
return last - first;
}
return N;
}
template <class T, std::size_t N>
constexpr auto ring_buffer<T, N>::max_size() const noexcept -> size_type {
return elements.max_size();
}
template <class T, std::size_t N>
constexpr void ring_buffer<T, N>::push_front(const T& value) {
dec();
elements[first] = value;
}
template <class T, std::size_t N>
constexpr void ring_buffer<T, N>::push_back(const T& value) {
inc();
elements[last] = value;
}
template <class T, std::size_t N>
void ring_buffer<T, N>::inc() noexcept {
if (++first >= N) {
first = 0;
}
if (++last >= N) {
last = 0;
}
}
template <class T, std::size_t N>
void ring_buffer<T, N>::dec() noexcept {
if (first == 0) {
first = N - 1;
} else {
first--;
}
if (last == 0) {
last = N - 1;
} else {
last--;
}
}
} // namespace util
#endif // THAT_THIS_UTIL_RING_BUFFER_HEADER_IS_ALREADY_INCLUDED