Files
2026-02-02 04:50:13 +01:00

216 lines
4.2 KiB
C++

/**
* @file
*/
#pragma once
#include "common/util.h"
#include <stddef.h>
namespace TwinE {
/**
* @brief Non allocating buffer class holds a maximum of given entries and allows an endless insert
* by overrinding previous elements in the buffer
*/
template<typename TYPE, size_t SIZE = 64u>
class RingBuffer {
protected:
size_t _size;
size_t _front;
size_t _back;
TYPE _buffer[SIZE];
public:
using value_type = TYPE;
RingBuffer() : _size(0u), _front(0u), _back(SIZE - 1u) {
}
class iterator {
private:
RingBuffer *_ringBuffer;
size_t _idx;
public:
iterator(RingBuffer *ringBuffer, size_t idx) : _ringBuffer(ringBuffer), _idx(idx) {
}
inline const TYPE &operator*() const {
return _ringBuffer->_buffer[_idx % _ringBuffer->capacity()];
}
inline TYPE &operator*() {
return _ringBuffer->_buffer[_idx % _ringBuffer->capacity()];
}
inline const TYPE &operator()() const {
return _ringBuffer->_buffer[_idx % _ringBuffer->capacity()];
}
inline TYPE &operator()() {
return _ringBuffer->_buffer[_idx % _ringBuffer->capacity()];
}
iterator &operator++() {
++_idx;
return *this;
}
inline bool operator!=(const iterator &rhs) const {
return _ringBuffer != rhs._ringBuffer || _idx != rhs._idx;
}
inline bool operator==(const iterator &rhs) const {
return _ringBuffer == rhs._ringBuffer && _idx == rhs._idx;
}
inline const TYPE *operator->() const {
return &_ringBuffer->_buffer[_idx % _ringBuffer->capacity()];
}
};
iterator begin() {
return iterator(this, _front);
}
iterator end() {
return iterator(this, _front + _size);
}
iterator begin() const {
return iterator(const_cast<RingBuffer *>(this), _front);
}
iterator end() const {
return iterator(const_cast<RingBuffer *>(this), _front + _size);
}
inline size_t size() const {
return _size;
}
constexpr size_t capacity() const {
return SIZE;
}
inline bool empty() const {
return _size == 0;
}
/**
* @brief Access to the first element in the buffer
* @note This is not the same as accessing the index 0 element
*/
const TYPE &front() const {
return _buffer[_front];
}
/**
* @brief Access to the first element in the buffer
* @note This is not the same as accessing the index 0 element
*/
TYPE &front() {
return _buffer[_front];
}
/**
* @brief Access to the last element in the buffer
* @note This is not the same as accessing the index @c size-1 element
*/
const TYPE &back() const {
return _buffer[_back];
}
/**
* @brief Access to the last element in the buffer
* @note This is not the same as accessing the index @c size-1 element
*/
TYPE &back() {
return _buffer[_back];
}
/**
* @brief Clears the whole ring buffer
* @note Does not call any destructors - they are called when the buffer itself gets destroyed
*/
void clear() {
_front = 0u;
_back = SIZE - 1u;
_size = 0u;
}
/**
* @brief Pushes an element to the end of the buffer
*/
void push_back(const TYPE &x) {
_back = (_back + 1u) % SIZE;
if (_size == SIZE) {
_front = (_front + 1u) % SIZE;
} else {
++_size;
}
_buffer[_back] = x;
}
/**
* @brief Pushes an elements to the end of the buffer
* @note Performs a move operation
*/
template<typename... _Args>
void emplace_back(_Args &&...args) {
_back = (_back + 1u) % SIZE;
if (_size == SIZE) {
_front = (_front + 1u) % SIZE;
} else {
++_size;
}
_buffer[_back] = Common::move(TYPE{Common::forward<_Args>(args)...});
}
/**
* @brief Removes element from the beginning of the buffer
*/
void pop() {
if (_size == 0) {
return;
}
--_size;
_front = (_front + 1u) % SIZE;
}
/**
* @brief Erase the given amount of elements from the end of the buffer
*/
void erase_back(const size_t n) {
if (n >= _size) {
clear();
return;
}
_size -= n;
_back = (_front + _size - 1u) % SIZE;
}
/**
* @brief Erase the given amount of elements from the beginning of the buffer
*/
void erase_front(const size_t n) {
if (n >= _size) {
clear();
return;
}
_front = (_front + n) % SIZE;
_size -= n;
}
inline TYPE &operator[](size_t i) {
return _buffer[(_front + i) % SIZE];
}
inline const TYPE &operator[](size_t i) const {
return _buffer[(_front + i) % SIZE];
}
};
} // namespace TwinE