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
parent
43a423f299
commit
477539c791
|
|
@ -3,6 +3,61 @@
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stddef.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
|
/// A generic circular buffer class
|
||||||
/// Can hold up to (size-1) elements
|
/// Can hold up to (size-1) elements
|
||||||
/// @param T data type of stored 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>
|
template <typename T = uint8_t, typename index_t = uint_fast8_t, size_t size = 16>
|
||||||
class CircularBuffer {
|
class CircularBuffer {
|
||||||
public:
|
public:
|
||||||
constexpr inline CircularBuffer()
|
|
||||||
: tail(0)
|
|
||||||
, head(0) {}
|
|
||||||
|
|
||||||
inline bool empty() const {
|
inline bool empty() const {
|
||||||
return tail == head;
|
return index.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool full() const {
|
bool full() const {
|
||||||
return next(head) == tail;
|
return index.full();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Insert an element into the buffer.
|
/// Insert an element into the buffer.
|
||||||
|
|
@ -32,15 +83,15 @@ public:
|
||||||
bool push(T elem) {
|
bool push(T elem) {
|
||||||
if (full())
|
if (full())
|
||||||
return false;
|
return false;
|
||||||
data[head] = elem;
|
data[index.back()] = elem;
|
||||||
head = next(head);
|
index.push();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @returns peeks the current element to extract from the buffer, however the element is left in the buffer
|
/// @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
|
/// Does not perform any range checks for performance reasons, should be preceeded by if(!empty()) in the user code
|
||||||
inline T front() const {
|
inline T front() const {
|
||||||
return data[tail];
|
return data[index.front()];
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Extracts the current element from the buffer
|
/// Extracts the current element from the buffer
|
||||||
|
|
@ -49,15 +100,11 @@ public:
|
||||||
if (empty())
|
if (empty())
|
||||||
return false;
|
return false;
|
||||||
elem = front();
|
elem = front();
|
||||||
tail = next(tail);
|
index.pop();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
T data[size]; ///< array of stored elements
|
T data[size]; ///< array of stored elements
|
||||||
index_t tail; ///< index of element to read (pop/extract) from the buffer
|
CircularIndex<index_t, size> index; ///< circular index
|
||||||
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; }
|
|
||||||
};
|
};
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue