diff --git a/src/midi_RingBuffer.h b/src/midi_RingBuffer.h index da2266c..3627d5d 100644 --- a/src/midi_RingBuffer.h +++ b/src/midi_RingBuffer.h @@ -34,13 +34,16 @@ BEGIN_MIDI_NAMESPACE template class RingBuffer { +private: + static const int sMask = Size - 1; + public: RingBuffer(); ~RingBuffer(); public: - int getLength() const; - bool isEmpty() const; + inline int getLength() const; + inline bool isEmpty() const; public: void write(DataType inData); @@ -53,8 +56,9 @@ public: private: DataType mData[Size]; - DataType* mWriteHead; - DataType* mReadHead; + int mLength; + int mWriteHead; + int mReadHead; }; END_MIDI_NAMESPACE diff --git a/src/midi_RingBuffer.hpp b/src/midi_RingBuffer.hpp index 9c273c9..2dbdbbe 100644 --- a/src/midi_RingBuffer.hpp +++ b/src/midi_RingBuffer.hpp @@ -27,13 +27,27 @@ #pragma once +BEGIN_UNNAMED_NAMESPACE + +template +struct isPowerOfTwo +{ + static const bool value = N && !(N & (N - 1)); +}; + +END_UNNAMED_NAMESPACE + +// -- + BEGIN_MIDI_NAMESPACE template RingBuffer::RingBuffer() - : mWriteHead(mData) - , mReadHead(mData) + : mLength(0) + , mWriteHead(0) + , mReadHead(0) { + static_assert(isPowerOfTwo::value, "Size must be a power of two."); memset(mData, DataType(0), Size * sizeof(DataType)); } @@ -45,26 +59,15 @@ RingBuffer::~RingBuffer() // ----------------------------------------------------------------------------- template -int RingBuffer::getLength() const +inline int RingBuffer::getLength() const { - if (mReadHead == mWriteHead) - { - return 0; - } - else if (mWriteHead > mReadHead) - { - return int(mWriteHead - mReadHead); - } - else - { - return int(mWriteHead - mData) + Size - int(mReadHead - mData); - } + return mLength; } template -bool RingBuffer::isEmpty() const +inline bool RingBuffer::isEmpty() const { - return mReadHead == mWriteHead; + return mLength == 0; } // ----------------------------------------------------------------------------- @@ -72,10 +75,12 @@ bool RingBuffer::isEmpty() const template void RingBuffer::write(DataType inData) { - *mWriteHead++ = inData; - if (mWriteHead >= mData + Size) - { - mWriteHead = mData; + mData[mWriteHead] = inData; + mWriteHead = (mWriteHead + 1) & sMask; + mLength++; + if (mLength > Size) { + mLength = Size; + mReadHead = (mReadHead + 1) & sMask; } } @@ -92,8 +97,9 @@ template void RingBuffer::clear() { memset(mData, DataType(0), Size * sizeof(DataType)); - mReadHead = mData; - mWriteHead = mData; + mReadHead = 0; + mWriteHead = 0; + mLength = 0; } // ----------------------------------------------------------------------------- @@ -101,11 +107,13 @@ void RingBuffer::clear() template DataType RingBuffer::read() { - const DataType data = *mReadHead++; - if (mReadHead >= mData + Size) - { - mReadHead = mData; + mLength--; + if (mLength < 0) { + mLength = 0; + return 0; } + const DataType data = mData[mReadHead]; + mReadHead = (mReadHead + 1) & sMask; return data; } diff --git a/test/unit-tests/CMakeLists.txt b/test/unit-tests/CMakeLists.txt index 39f4bb3..a57b161 100644 --- a/test/unit-tests/CMakeLists.txt +++ b/test/unit-tests/CMakeLists.txt @@ -18,6 +18,7 @@ add_executable(unit-tests tests/unit-tests_Settings.cpp tests/unit-tests_Settings.h tests/unit-tests_SysExCodec.cpp + tests/unit-tests_RingBuffer.cpp tests/unit-tests_SerialMock.cpp tests/unit-tests_MidiInput.cpp tests/unit-tests_MidiInputCallbacks.cpp diff --git a/test/unit-tests/tests/unit-tests_RingBuffer.cpp b/test/unit-tests/tests/unit-tests_RingBuffer.cpp new file mode 100644 index 0000000..d33352d --- /dev/null +++ b/test/unit-tests/tests/unit-tests_RingBuffer.cpp @@ -0,0 +1,174 @@ +#include "unit-tests.h" +#include + +BEGIN_UNNAMED_NAMESPACE +using namespace testing; +using Buffer = midi::RingBuffer; + +TEST(RingBuffer, writeScalar) +{ + Buffer buffer; + EXPECT_EQ(buffer.isEmpty(), true); + EXPECT_EQ(buffer.getLength(), 0); + buffer.write(42); + buffer.write(47); + EXPECT_EQ(buffer.isEmpty(), false); + EXPECT_EQ(buffer.getLength(), 2); +} + +TEST(RingBuffer, readScalar) +{ + Buffer buffer; + buffer.write(42); + EXPECT_EQ(buffer.getLength(), 1); + buffer.write(47); + EXPECT_EQ(buffer.getLength(), 2); + EXPECT_EQ(buffer.read(), 42); + EXPECT_EQ(buffer.getLength(), 1); + EXPECT_EQ(buffer.read(), 47); + EXPECT_EQ(buffer.isEmpty(), true); + EXPECT_EQ(buffer.getLength(), 0); +} + +TEST(RingBuffer, clear) +{ + Buffer buffer; + buffer.write(42); + buffer.write(47); + buffer.clear(); + EXPECT_EQ(buffer.isEmpty(), true); + EXPECT_EQ(buffer.getLength(), 0); +} + +TEST(RingBuffer, writeArray) +{ + Buffer buffer; + const uint8_t input[4] = { + 1, 2, 3, 4 + }; + buffer.write(input, 4); + EXPECT_EQ(buffer.isEmpty(), false); + EXPECT_EQ(buffer.getLength(), 4); +} + +TEST(RingBuffer, writeOverflow) +{ + Buffer buffer; + + buffer.write(1); + EXPECT_EQ(buffer.getLength(), 1); + EXPECT_EQ(buffer.isEmpty(), false); + buffer.write(2); + EXPECT_EQ(buffer.getLength(), 2); + EXPECT_EQ(buffer.isEmpty(), false); + buffer.write(3); + EXPECT_EQ(buffer.getLength(), 3); + EXPECT_EQ(buffer.isEmpty(), false); + buffer.write(4); + EXPECT_EQ(buffer.getLength(), 4); + EXPECT_EQ(buffer.isEmpty(), false); + buffer.write(5); + EXPECT_EQ(buffer.getLength(), 5); + EXPECT_EQ(buffer.isEmpty(), false); + buffer.write(6); + EXPECT_EQ(buffer.getLength(), 6); + EXPECT_EQ(buffer.isEmpty(), false); + buffer.write(7); + EXPECT_EQ(buffer.getLength(), 7); + EXPECT_EQ(buffer.isEmpty(), false); + buffer.write(8); + EXPECT_EQ(buffer.getLength(), 8); + EXPECT_EQ(buffer.isEmpty(), false); + buffer.write(9); + EXPECT_EQ(buffer.getLength(), 8); + EXPECT_EQ(buffer.isEmpty(), false); + buffer.write(10); + EXPECT_EQ(buffer.getLength(), 8); + EXPECT_EQ(buffer.isEmpty(), false); + buffer.write(11); + EXPECT_EQ(buffer.getLength(), 8); + EXPECT_EQ(buffer.isEmpty(), false); + buffer.write(12); + EXPECT_EQ(buffer.getLength(), 8); + EXPECT_EQ(buffer.isEmpty(), false); +} + +TEST(RingBuffer, readOverflow) +{ + Buffer buffer; + + buffer.write(1); + buffer.write(2); + buffer.write(3); + buffer.write(4); + buffer.write(5); + buffer.write(6); + buffer.write(7); + buffer.write(8); + buffer.write(9); + buffer.write(10); + buffer.write(11); + buffer.write(12); + EXPECT_EQ(buffer.read(), 5); + EXPECT_EQ(buffer.getLength(), 7); + EXPECT_EQ(buffer.read(), 6); + EXPECT_EQ(buffer.getLength(), 6); + EXPECT_EQ(buffer.read(), 7); + EXPECT_EQ(buffer.getLength(), 5); + EXPECT_EQ(buffer.read(), 8); + EXPECT_EQ(buffer.getLength(), 4); + EXPECT_EQ(buffer.read(), 9); + EXPECT_EQ(buffer.getLength(), 3); + EXPECT_EQ(buffer.read(), 10); + EXPECT_EQ(buffer.getLength(), 2); + EXPECT_EQ(buffer.read(), 11); + EXPECT_EQ(buffer.getLength(), 1); + EXPECT_EQ(buffer.read(), 12); + EXPECT_EQ(buffer.getLength(), 0); + EXPECT_EQ(buffer.read(), 0); + EXPECT_EQ(buffer.getLength(), 0); +} + +TEST(RingBuffer, writeArrayOverflow) +{ + Buffer buffer; + const uint8_t input[12] = { + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, + }; + buffer.write(input, 12); + EXPECT_EQ(buffer.isEmpty(), false); + EXPECT_EQ(buffer.getLength(), 8); // Max size +} + +TEST(RingBuffer, readArray) +{ + Buffer buffer; + const uint8_t input[4] = { + 1, 2, 3, 4 + }; + uint8_t output[4] = { 0 }; + buffer.write(input, 4); + buffer.read(output, 4); + EXPECT_THAT(output, ContainerEq(input)); +} + +TEST(RingBuffer, readArrayOverflow) +{ + Buffer buffer; + const uint8_t input[12] = { + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, + }; + const uint8_t expected[8] = { + 5, 6, 7, 8, 9, 10, 11, 12, + }; + uint8_t output[8] = { 0 }; + + buffer.write(input, 12); + buffer.read(output, 8); + EXPECT_THAT(output, ContainerEq(expected)); + EXPECT_EQ(buffer.isEmpty(), true); + EXPECT_EQ(buffer.getLength(), 0); +} + +END_UNNAMED_NAMESPACE + diff --git a/test/unit-tests/tests/unit-tests_SerialMock.cpp b/test/unit-tests/tests/unit-tests_SerialMock.cpp index b3555c0..ae204fe 100644 --- a/test/unit-tests/tests/unit-tests_SerialMock.cpp +++ b/test/unit-tests/tests/unit-tests_SerialMock.cpp @@ -6,7 +6,7 @@ BEGIN_UNNAMED_NAMESPACE USING_NAMESPACE_TEST_MOCKS using namespace testing; -TEST(RingBuffer, initialState) +TEST(RingBufferMock, initialState) { typedef RingBuffer Buffer; Buffer buffer; @@ -17,7 +17,7 @@ TEST(RingBuffer, initialState) EXPECT_EQ(buffer.isEmpty(), true); } -TEST(RingBuffer, uint8) +TEST(RingBufferMock, uint8) { typedef RingBuffer Buffer; Buffer buffer; @@ -48,7 +48,7 @@ TEST(RingBuffer, uint8) EXPECT_EQ(buffer.isEmpty(), true); } -TEST(RingBuffer, uint32) +TEST(RingBufferMock, uint32) { typedef RingBuffer Buffer; Buffer buffer;