diff --git a/README.md b/README.md index b929ffa..ff755e8 100644 --- a/README.md +++ b/README.md @@ -4,13 +4,14 @@ A simple and easy to use ring buffer library for Arduino. Interrupt safe functio ## Changelog +- 1.0.3 Changed the way templates are instanciated. Now, a size greater than 255 is allowed and leads to a uint16_t datatype used for size and index. In addition, wrong size are detected and a compilation error is emited. Added example ```BigBuffer``` with a size over 255. - 1.0.2 Changed the name of the template from RingBuffer to RingBuf in order to avoid a name conflict with and internal RingBuffer class used in the ARM version of the Arduino core. - 1.0.1 Fix a mistake in pop documentation - 1.0 Initial release. ## Limitation -The size of the ring buffer is limited to 255 elements. The compiler will not prevent you from declaring a buffer size 0 but a size 0 is not supported (and otherwise silly). Among the quirks with a size of 0 is the fact that the buffer is both empty and full. +The size of the ring buffer is limited to 65535 elements. The compiler will emit an error is the buffer size is 0 or if the buffer size is greated than 65535. ## Using the library @@ -26,7 +27,7 @@ Instantiate a ring buffer by using the following syntax: RingBuf myRingBuffer; ``` -```type``` is the type name of each element of the ring buffer. ```size``` is the size, from 1 to 255, of the ring buffer. For instance the declaration shown below instantiate a ring buffer where each element is a ```byte``` with a size of 20. +```type``` is the type name of each element of the ring buffer. ```size``` is the size, from 1 to 65535, of the ring buffer. For instance the declaration shown below instantiate a ring buffer where each element is a ```byte``` with a size of 20. ``` RingBuf aBuffer; @@ -44,11 +45,11 @@ The following functions are available to manage the ring buffer. ### maxSize() -```maxSize()``` returns an ```uint8_t``` which is the maximum size of the ring buffer. It is the value set when the ring buffer has been instantiated. +```maxSize()``` returns an ```uint8_t``` for buffers of size lower or equal to 255 and an ```uint16_t``` for buffers of size in the [256, 65535] interval. It is the value set when the ring buffer has been instantiated. ### size() -```size()``` returns an ```uint8_t``` which is the current size of the ring buffer. It is between 0 and ```maxSize()```. +```size()``` returns an ```uint8_t``` for buffers of size lower or equal to 255 and an ```uint16_t``` for buffers of size in the [256, 65535] interval, which is the current size of the ring buffer. It is between 0 and ```maxSize()```. ### clear() diff --git a/examples/BigBuffer/BigBuffer.ino b/examples/BigBuffer/BigBuffer.ino new file mode 100644 index 0000000..9009d50 --- /dev/null +++ b/examples/BigBuffer/BigBuffer.ino @@ -0,0 +1,66 @@ +/* + * Test of a big buffer (greater than 255) + * + * Output should be + * Filled with 300 values + * Buffer agrees ! + * Checking 300 values... 0 error(s) found + * Popping the values... popped 300 values, 0 wrong + */ + +#include + +RingBuf myBuffer; + +void setup() +{ + Serial.begin(115200); + + uint16_t i = 0; + + /* fill the buffer */ + while (! myBuffer.isFull()) { + myBuffer.push(i++); + } + Serial.print("Filled with "); + Serial.print(i); + Serial.println(" values"); + + /* Check the buffer agrees */ + if (myBuffer.size() == i) { + Serial.println("Buffer agrees !"); + } + else { + Serial.println("*** Error: Buffer does not agree !"); + } + + /* Check the values */ + Serial.print("Checking "); + Serial.print(myBuffer.size()); + Serial.print(" values... "); + uint16_t errorCount = 0; + for (uint16_t j = 0; j < myBuffer.size(); j++) { + if (myBuffer[j] != j) errorCount++; + } + Serial.print(errorCount); + Serial.println(" error(s) found"); + + /* Pop the values */ + Serial.print("Popping the values... "); + errorCount = 0; + uint16_t value; + i = 0; + while (myBuffer.pop(value)) { + if (value != i++) errorCount++; + } + Serial.print("popped "); + Serial.print(i); + Serial.print(" values, "); + Serial.print(errorCount); + Serial.println(" wrong"); +} + +void loop() +{ + +} diff --git a/examples/README.md b/examples/README.md index e69de29..d0d64a6 100644 --- a/examples/README.md +++ b/examples/README.md @@ -0,0 +1 @@ +# Examples of RingBuffer library \ No newline at end of file diff --git a/library.properties b/library.properties index 412bd41..ef13659 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=RingBuffer -version=1.0.2 +version=1.0.3 author=Jean-Luc - Locoduino maintainer=Jean-Luc - Locoduino sentence=This library allows to use ring buffer with and without interrupts. diff --git a/src/RingBuf.h b/src/RingBuf.h index 2688a06..f8bec56 100644 --- a/src/RingBuf.h +++ b/src/RingBuf.h @@ -47,62 +47,103 @@ #include -template +/* + * Set the integer size used to store the size of the buffer according of + * the size given in the template instanciation. Thanks to Niklas Gürtler + * to share his knowledge of C++ template meta programming. + * https://niklas-guertler.de/ + * + * If Index argument is true, the ring buffer has a size and an index + * stored in an uint8_t (Type below) because its size is within [1,255]. + * Intermediate computation may need an uint16_t (BiggerType below). + * If Index argument is false, the ring buffer has a size and an index + * stored in an uint16_t (Type below) because its size is within [256,65535]. + * Intermediate computation may need an uint32_t (BiggerType below). + */ + +namespace RingBufHelper { + template struct Index { + using Type = uint16_t; /* index of the buffer */ + using BiggerType = uint32_t; /* for intermediate calculation */ + }; + template<> struct Index { + using Type = uint8_t; /* index of the buffer */ + using BiggerType = uint16_t; /* for intermediate calculation */ + }; +} + +template < + typename ET, + size_t S, + typename IT = typename RingBufHelper::Index<(S > 255)>::Type, + typename BT = typename RingBufHelper::Index<(S > 255)>::BiggerType +> class RingBuf { + /* + * check the size is greater than 0, otherwise emit a compile time error + */ + static_assert(S > 0, "RingBuf with size 0 are forbidden"); + + /* + * check the size is lower or equal to the maximum uint16_t value, + * otherwise emit a compile time error + */ + static_assert(S <= UINT16_MAX, "RingBuf with size greater than 65535 are forbidden"); + private: - rg_element_t mBuffer[__maxSize__]; - uint8_t mReadIndex; - uint8_t mSize; + ET mBuffer[S]; + IT mReadIndex; + IT mSize; - uint8_t writeIndex(); + IT writeIndex(); public: /* Constructor. Init mReadIndex to 0 and mSize to 0 */ RingBuf(); /* Push a data at the end of the buffer */ - bool push(const rg_element_t inElement) __attribute__ ((noinline)); + bool push(const ET inElement) __attribute__ ((noinline)); /* Push a data at the end of the buffer. Copy it from its pointer */ - bool push(const rg_element_t * const inElement) __attribute__ ((noinline)); + bool push(const ET * const inElement) __attribute__ ((noinline)); /* Push a data at the end of the buffer with interrupts disabled */ - bool lockedPush(const rg_element_t inElement); + bool lockedPush(const ET inElement); /* Push a data at the end of the buffer with interrupts disabled. Copy it from its pointer */ - bool lockedPush(const rg_element_t * const inElement); + bool lockedPush(const ET * const inElement); /* Pop the data at the beginning of the buffer */ - bool pop(rg_element_t &outElement) __attribute__ ((noinline)); + bool pop(ET &outElement) __attribute__ ((noinline)); /* Pop the data at the beginning of the buffer with interrupt disabled */ - bool lockedPop(rg_element_t &outElement); + bool lockedPop(ET &outElement); /* Return true if the buffer is full */ - bool isFull() { return mSize == __maxSize__; } + bool isFull() { return mSize == S; } /* Return true if the buffer is empty */ bool isEmpty() { return mSize == 0; } /* Reset the buffer to an empty state */ void clear() { mSize = 0; } /* return the size of the buffer */ - uint8_t size() { return mSize; } + IT size() { return mSize; } /* return the maximum size of the buffer */ - uint8_t maxSize() { return __maxSize__; } + IT maxSize() { return S; } /* access the buffer using array syntax, not interrupt safe */ - rg_element_t &operator[](uint8_t inIndex); + ET &operator[](IT inIndex); }; -template -uint8_t RingBuf::writeIndex() +template +IT RingBuf::writeIndex() { - uint16_t wi = (uint16_t)mReadIndex + (uint16_t)mSize; - if (wi >= (uint16_t)__maxSize__) wi -= (uint16_t)__maxSize__; - return (uint8_t)wi; + BT wi = (BT)mReadIndex + (BT)mSize; + if (wi >= (BT)S) wi -= (BT)S; + return (IT)wi; } -template -RingBuf::RingBuf() : +template +RingBuf::RingBuf() : mReadIndex(0), mSize(0) { } -template -bool RingBuf::push(const rg_element_t inElement) +template +bool RingBuf::push(const ET inElement) { if (isFull()) return false; mBuffer[writeIndex()] = inElement; @@ -110,8 +151,8 @@ bool RingBuf::push(const rg_element_t inElement) return true; } -template -bool RingBuf::push(const rg_element_t * const inElement) +template +bool RingBuf::push(const ET * const inElement) { if (isFull()) return false; mBuffer[writeIndex()] = *inElement; @@ -119,8 +160,8 @@ bool RingBuf::push(const rg_element_t * const inEleme return true; } -template -bool RingBuf::lockedPush(const rg_element_t inElement) +template +bool RingBuf::lockedPush(const ET inElement) { noInterrupts(); bool result = push(inElement); @@ -128,8 +169,8 @@ bool RingBuf::lockedPush(const rg_element_t inElement return result; } -template -bool RingBuf::lockedPush(const rg_element_t * const inElement) +template +bool RingBuf::lockedPush(const ET * const inElement) { noInterrupts(); bool result = push(inElement); @@ -137,19 +178,19 @@ bool RingBuf::lockedPush(const rg_element_t * const i return result; } -template -bool RingBuf::pop(rg_element_t &outElement) +template +bool RingBuf::pop(ET &outElement) { if (isEmpty()) return false; outElement = mBuffer[mReadIndex]; mReadIndex++; mSize--; - if (mReadIndex == __maxSize__) mReadIndex = 0; + if (mReadIndex == S) mReadIndex = 0; return true; } -template -bool RingBuf::lockedPop(rg_element_t &outElement) +template +bool RingBuf::lockedPop(ET &outElement) { noInterrupts(); bool result = pop(outElement); @@ -157,13 +198,13 @@ bool RingBuf::lockedPop(rg_element_t &outElement) return result; } -template -rg_element_t &RingBuf::operator[](uint8_t inIndex) +template +ET &RingBuf::operator[](IT inIndex) { if (inIndex >= mSize) return mBuffer[0]; - uint16_t index = (uint16_t)mReadIndex + (uint16_t)inIndex; - if (index >= (uint16_t)__maxSize__) index -= (uint16_t)__maxSize__; - return mBuffer[(uint8_t)index]; + BT index = (BT)mReadIndex + (BT)inIndex; + if (index >= (BT)S) index -= (BT)S; + return mBuffer[(IT)index]; } #endif /* __RINGBUF_H__ */