diff --git a/src/hal/circular_buffer.h b/src/hal/circular_buffer.h index 6ef1c6d..d833428 100644 --- a/src/hal/circular_buffer.h +++ b/src/hal/circular_buffer.h @@ -3,6 +3,61 @@ #include #include +/// 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 +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 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; ///< circular index };