Split CircularBuffer into CircularIndex+CircularBuffer

Allow the lower-level index to be used without an actual container by
splitting off the index management into CircularIndex.

Rebuild CircularBuffer using CircularIndex itself.
pull/49/head
Yuri D'Elia 2021-07-05 21:07:53 +02:00 committed by DRracer
parent 43a423f299
commit 477539c791
1 changed files with 62 additions and 15 deletions

View File

@ -3,6 +3,61 @@
#include <stdint.h>
#include <stddef.h>
/// A generic circular index class which can be used to build circular buffers
/// Can hold up to (size-1) elements
/// @param index_t data type of indices into array of elements
/// (recommended to keep uint8_fast8_t as single byte operations are atomical on the AVR)
/// @param size number of index positions + 1.
/// It is recommended to keep a power of 2 to allow for optimal code generation on the AVR (there is no HW modulo instruction)
template <typename index_t = uint_fast8_t, size_t size = 16>
class CircularIndex {
public:
constexpr inline CircularIndex()
: tail(0)
, head(0) {}
/// @returns true if empty
inline bool empty() const {
return tail == head;
}
/// @returns true if full
inline bool full() const {
return next(head) == tail;
}
/// Advance the head index of the buffer.
/// No checks are performed. full() needs to be queried beforehand.
inline void push() {
head = next(head);
}
/// Advance the tail index from the buffer.
/// No checks are performed. empty() needs to be queried beforehand.
inline void pop() {
tail = next(tail);
}
/// @returns return the tail index from the buffer.
/// Does not perform any range checks for performance reasons, should be preceeded by if(!empty()) in the user code
inline index_t front() const {
return tail;
}
/// @returns return the head index from the buffer.
/// Does not perform any range checks for performance reasons, should be preceeded by if(!empty()) in the user code
inline index_t back() const {
return head;
}
protected:
index_t tail; ///< index of element to read (pop/extract) from the buffer
index_t head; ///< index of an empty spot or element insertion (write)
/// @returns next index wrapped past the end of the array of elements
static index_t next(index_t index) { return (index + 1) % size; }
};
/// A generic circular buffer class
/// Can hold up to (size-1) elements
/// @param T data type of stored elements
@ -13,16 +68,12 @@
template <typename T = uint8_t, typename index_t = uint_fast8_t, size_t size = 16>
class CircularBuffer {
public:
constexpr inline CircularBuffer()
: tail(0)
, head(0) {}
inline bool empty() const {
return tail == head;
return index.empty();
}
bool full() const {
return next(head) == tail;
return index.full();
}
/// Insert an element into the buffer.
@ -32,15 +83,15 @@ public:
bool push(T elem) {
if (full())
return false;
data[head] = elem;
head = next(head);
data[index.back()] = elem;
index.push();
return true;
}
/// @returns peeks the current element to extract from the buffer, however the element is left in the buffer
/// Does not perform any range checks for performance reasons, should be preceeded by if(!empty()) in the user code
inline T front() const {
return data[tail];
return data[index.front()];
}
/// Extracts the current element from the buffer
@ -49,15 +100,11 @@ public:
if (empty())
return false;
elem = front();
tail = next(tail);
index.pop();
return true;
}
protected:
T data[size]; ///< array of stored elements
index_t tail; ///< index of element to read (pop/extract) from the buffer
index_t head; ///< index of an empty spot or element insertion (write)
/// @returns next index wrapped past the end of the array of elements
static index_t next(index_t index) { return (index + 1) % size; }
CircularIndex<index_t, size> index; ///< circular index
};