Browse Source

Added methods AbstractFifo::read() and AbstractFifo::write() which return a scope-based object to make it easy to hold, release and iterate blocks of items.

tags/2021-05-28
jules 7 years ago
parent
commit
e2688eec0a
2 changed files with 202 additions and 41 deletions
  1. +100
    -40
      modules/juce_core/containers/juce_AbstractFifo.cpp
  2. +102
    -1
      modules/juce_core/containers/juce_AbstractFifo.h

+ 100
- 40
modules/juce_core/containers/juce_AbstractFifo.cpp View File

@@ -23,8 +23,7 @@
namespace juce namespace juce
{ {
AbstractFifo::AbstractFifo (const int capacity) noexcept
: bufferSize (capacity)
AbstractFifo::AbstractFifo (int capacity) noexcept : bufferSize (capacity)
{ {
jassert (bufferSize > 0); jassert (bufferSize > 0);
} }
@@ -36,8 +35,8 @@ int AbstractFifo::getFreeSpace() const noexcept { return bufferSize -
int AbstractFifo::getNumReady() const noexcept int AbstractFifo::getNumReady() const noexcept
{ {
const int vs = validStart.get();
const int ve = validEnd.get();
auto vs = validStart.get();
auto ve = validEnd.get();
return ve >= vs ? (ve - vs) : (bufferSize - (vs - ve)); return ve >= vs ? (ve - vs) : (bufferSize - (vs - ve));
} }
@@ -57,10 +56,10 @@ void AbstractFifo::setTotalSize (int newSize) noexcept
//============================================================================== //==============================================================================
void AbstractFifo::prepareToWrite (int numToWrite, int& startIndex1, int& blockSize1, int& startIndex2, int& blockSize2) const noexcept void AbstractFifo::prepareToWrite (int numToWrite, int& startIndex1, int& blockSize1, int& startIndex2, int& blockSize2) const noexcept
{ {
const int vs = validStart.get();
const int ve = validEnd.get();
auto vs = validStart.get();
auto ve = validEnd.get();
const int freeSpace = ve >= vs ? (bufferSize - (ve - vs)) : (vs - ve);
auto freeSpace = ve >= vs ? (bufferSize - (ve - vs)) : (vs - ve);
numToWrite = jmin (numToWrite, freeSpace - 1); numToWrite = jmin (numToWrite, freeSpace - 1);
if (numToWrite <= 0) if (numToWrite <= 0)
@@ -84,7 +83,7 @@ void AbstractFifo::finishedWrite (int numWritten) noexcept
{ {
jassert (numWritten >= 0 && numWritten < bufferSize); jassert (numWritten >= 0 && numWritten < bufferSize);
int newEnd = validEnd.get() + numWritten;
auto newEnd = validEnd.get() + numWritten;
if (newEnd >= bufferSize) if (newEnd >= bufferSize)
newEnd -= bufferSize; newEnd -= bufferSize;
@@ -94,10 +93,10 @@ void AbstractFifo::finishedWrite (int numWritten) noexcept
void AbstractFifo::prepareToRead (int numWanted, int& startIndex1, int& blockSize1, int& startIndex2, int& blockSize2) const noexcept void AbstractFifo::prepareToRead (int numWanted, int& startIndex1, int& blockSize1, int& startIndex2, int& blockSize2) const noexcept
{ {
const int vs = validStart.get();
const int ve = validEnd.get();
auto vs = validStart.get();
auto ve = validEnd.get();
const int numReady = ve >= vs ? (ve - vs) : (bufferSize - (vs - ve));
auto numReady = ve >= vs ? (ve - vs) : (bufferSize - (vs - ve));
numWanted = jmin (numWanted, numReady); numWanted = jmin (numWanted, numReady);
if (numWanted <= 0) if (numWanted <= 0)
@@ -121,7 +120,7 @@ void AbstractFifo::finishedRead (int numRead) noexcept
{ {
jassert (numRead >= 0 && numRead <= bufferSize); jassert (numRead >= 0 && numRead <= bufferSize);
int newStart = validStart.get() + numRead;
auto newStart = validStart.get() + numRead;
if (newStart >= bufferSize) if (newStart >= bufferSize)
newStart -= bufferSize; newStart -= bufferSize;
@@ -129,6 +128,76 @@ void AbstractFifo::finishedRead (int numRead) noexcept
validStart = newStart; validStart = newStart;
} }
//==============================================================================
template <AbstractFifo::ReadOrWrite mode>
AbstractFifo::ScopedReadWrite<mode>::ScopedReadWrite (AbstractFifo& f, int num) noexcept
: fifo (&f)
{
prepare (*fifo, num);
}
template <AbstractFifo::ReadOrWrite mode>
AbstractFifo::ScopedReadWrite<mode>::ScopedReadWrite (ScopedReadWrite&& other) noexcept
: startIndex1 (other.startIndex1),
blockSize1 (other.blockSize1),
startIndex2 (other.startIndex2),
blockSize2 (other.blockSize2)
{
swap (other);
}
template <AbstractFifo::ReadOrWrite mode>
AbstractFifo::ScopedReadWrite<mode>&
AbstractFifo::ScopedReadWrite<mode>::operator= (ScopedReadWrite&& other) noexcept
{
swap (other);
return *this;
}
template <AbstractFifo::ReadOrWrite mode>
AbstractFifo::ScopedReadWrite<mode>::~ScopedReadWrite() noexcept
{
if (fifo != nullptr)
finish (*fifo, blockSize1 + blockSize2);
}
template <AbstractFifo::ReadOrWrite mode>
void AbstractFifo::ScopedReadWrite<mode>::swap (ScopedReadWrite& other) noexcept
{
std::swap (other.fifo, fifo);
std::swap (other.startIndex1, startIndex1);
std::swap (other.blockSize1, blockSize1);
std::swap (other.startIndex2, startIndex2);
std::swap (other.blockSize2, blockSize2);
}
template<>
void AbstractFifo::ScopedReadWrite<AbstractFifo::ReadOrWrite::read>::prepare (AbstractFifo& f, int num) noexcept
{
f.prepareToRead (num, startIndex1, blockSize1, startIndex2, blockSize2);
}
template<>
void AbstractFifo::ScopedReadWrite<AbstractFifo::ReadOrWrite::write>::prepare (AbstractFifo& f, int num) noexcept
{
f.prepareToWrite (num, startIndex1, blockSize1, startIndex2, blockSize2);
}
template<>
void AbstractFifo::ScopedReadWrite<AbstractFifo::ReadOrWrite::read>::finish (AbstractFifo& f, int num) noexcept
{
f.finishedRead (num);
}
template<>
void AbstractFifo::ScopedReadWrite<AbstractFifo::ReadOrWrite::write>::finish (AbstractFifo& f, int num) noexcept
{
f.finishedWrite (num);
}
template class AbstractFifo::ScopedReadWrite<AbstractFifo::ReadOrWrite::read>;
template class AbstractFifo::ScopedReadWrite<AbstractFifo::ReadOrWrite::write>;
//============================================================================== //==============================================================================
#if JUCE_UNIT_TESTS #if JUCE_UNIT_TESTS
@@ -137,9 +206,8 @@ class AbstractFifoTests : public UnitTest
public: public:
AbstractFifoTests() : UnitTest ("Abstract Fifo", "Containers") {} AbstractFifoTests() : UnitTest ("Abstract Fifo", "Containers") {}
class WriteThread : public Thread
struct WriteThread : public Thread
{ {
public:
WriteThread (AbstractFifo& f, int* b, Random rng) WriteThread (AbstractFifo& f, int* b, Random rng)
: Thread ("fifo writer"), fifo (f), buffer (b), random (rng) : Thread ("fifo writer"), fifo (f), buffer (b), random (rng)
{ {
@@ -159,24 +227,18 @@ public:
{ {
int num = random.nextInt (2000) + 1; int num = random.nextInt (2000) + 1;
int start1, size1, start2, size2;
fifo.prepareToWrite (num, start1, size1, start2, size2);
jassert (size1 >= 0 && size2 >= 0);
jassert (size1 == 0 || (start1 >= 0 && start1 < fifo.getTotalSize()));
jassert (size2 == 0 || (start2 >= 0 && start2 < fifo.getTotalSize()));
auto writer = fifo.write (num);
for (int i = 0; i < size1; ++i)
buffer [start1 + i] = n++;
jassert (writer.blockSize1 >= 0 && writer.blockSize2 >= 0);
jassert (writer.blockSize1 == 0
|| (writer.startIndex1 >= 0 && writer.startIndex1 < fifo.getTotalSize()));
jassert (writer.blockSize2 == 0
|| (writer.startIndex2 >= 0 && writer.startIndex2 < fifo.getTotalSize()));
for (int i = 0; i < size2; ++i)
buffer [start2 + i] = n++;
fifo.finishedWrite (size1 + size2);
writer.forEach ([this, &n] (int index) { this->buffer[index] = n++; });
} }
} }
private:
AbstractFifo& fifo; AbstractFifo& fifo;
int* buffer; int* buffer;
Random random; Random random;
@@ -186,7 +248,7 @@ public:
{ {
beginTest ("AbstractFifo"); beginTest ("AbstractFifo");
int buffer [5000];
int buffer[5000];
AbstractFifo fifo (numElementsInArray (buffer)); AbstractFifo fifo (numElementsInArray (buffer));
WriteThread writer (fifo, buffer, getRandom()); WriteThread writer (fifo, buffer, getRandom());
@@ -199,12 +261,13 @@ public:
{ {
int num = r.nextInt (6000) + 1; int num = r.nextInt (6000) + 1;
int start1, size1, start2, size2;
fifo.prepareToRead (num, start1, size1, start2, size2);
auto reader = fifo.read (num);
if (! (size1 >= 0 && size2 >= 0)
&& (size1 == 0 || (start1 >= 0 && start1 < fifo.getTotalSize()))
&& (size2 == 0 || (start2 >= 0 && start2 < fifo.getTotalSize())))
if (! (reader.blockSize1 >= 0 && reader.blockSize2 >= 0)
&& (reader.blockSize1 == 0
|| (reader.startIndex1 >= 0 && reader.startIndex1 < fifo.getTotalSize()))
&& (reader.blockSize2 == 0
|| (reader.startIndex2 >= 0 && reader.startIndex2 < fifo.getTotalSize())))
{ {
expect (false, "prepareToRead returned -ve values"); expect (false, "prepareToRead returned -ve values");
break; break;
@@ -212,19 +275,16 @@ public:
bool failed = false; bool failed = false;
for (int i = 0; i < size1; ++i)
failed = (buffer [start1 + i] != n++) || failed;
for (int i = 0; i < size2; ++i)
failed = (buffer [start2 + i] != n++) || failed;
reader.forEach ([this, &failed, &buffer, &n] (int index)
{
failed = (buffer[index] != n++) || failed;
});
if (failed) if (failed)
{ {
expect (false, "read values were incorrect"); expect (false, "read values were incorrect");
break; break;
} }
fifo.finishedRead (size1 + size2);
} }
} }
}; };


+ 102
- 1
modules/juce_core/containers/juce_AbstractFifo.h View File

@@ -200,11 +200,112 @@ public:
*/ */
void finishedRead (int numRead) noexcept; void finishedRead (int numRead) noexcept;
//==============================================================================
private:
enum class ReadOrWrite
{
read,
write
};
public:
template <ReadOrWrite mode>
class ScopedReadWrite final
{
public:
/** Construct an unassigned reader/writer. Doesn't do anything upon destruction. */
ScopedReadWrite() = default;
/** Construct a reader/writer and immediately call prepareRead/prepareWrite
on the abstractFifo which was passed in.
This object will hold a pointer back to the fifo, so make sure that
the fifo outlives this object.
*/
ScopedReadWrite (AbstractFifo&, int num) noexcept;
ScopedReadWrite (const ScopedReadWrite&) = delete;
ScopedReadWrite (ScopedReadWrite&&) noexcept;
ScopedReadWrite& operator= (const ScopedReadWrite&) = delete;
ScopedReadWrite& operator= (ScopedReadWrite&&) noexcept;
/** Calls finishedRead or finishedWrite if this is a non-null scoped
reader/writer.
*/
~ScopedReadWrite() noexcept;
/** Calls the passed function with each index that was deemed valid
for the current read/write operation.
*/
template <typename FunctionToApply>
void forEach (FunctionToApply&& func) const
{
for (auto i = startIndex1, e = startIndex1 + blockSize1; i != e; ++i) func (i);
for (auto i = startIndex2, e = startIndex2 + blockSize2; i != e; ++i) func (i);
}
int startIndex1, blockSize1, startIndex2, blockSize2;
private:
void prepare (AbstractFifo&, int) noexcept;
static void finish (AbstractFifo&, int) noexcept;
void swap (ScopedReadWrite&) noexcept;
AbstractFifo* fifo = nullptr;
};
using ScopedRead = ScopedReadWrite<ReadOrWrite::read>;
using ScopedWrite = ScopedReadWrite<ReadOrWrite::write>;
/** Replaces prepareToRead/finishedRead with a single function.
This function returns an object which contains the start indices and
block sizes, and also automatically finishes the read operation when
it goes out of scope.
@code
{
auto readHandle = fifo.read (4);
for (auto i = 0; i != readHandle.blockSize1; ++i)
{
// read the item at index readHandle.startIndex1 + i
}
for (auto i = 0; i != readHandle.blockSize2; ++i)
{
// read the item at index readHandle.startIndex2 + i
}
} // readHandle goes out of scope here, finishing the read operation
@endcode
*/
ScopedRead read (int numToRead) noexcept { return { *this, numToRead }; }
/** Replaces prepareToWrite/finishedWrite with a single function.
This function returns an object which contains the start indices and
block sizes, and also automatically finishes the write operation when
it goes out of scope.
@code
{
auto writeHandle = fifo.write (5);
for (auto i = 0; i != writeHandle.blockSize1; ++i)
{
// write the item at index writeHandle.startIndex1 + i
}
for (auto i = 0; i != writeHandle.blockSize2; ++i)
{
// write the item at index writeHandle.startIndex2 + i
}
} // writeHandle goes out of scope here, finishing the write operation
@endcode
*/
ScopedWrite write (int numToWrite) noexcept { return { *this, numToWrite }; }
private: private:
//============================================================================== //==============================================================================
int bufferSize; int bufferSize;
Atomic <int> validStart, validEnd;
Atomic<int> validStart, validEnd;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AbstractFifo) JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AbstractFifo)
}; };


Loading…
Cancel
Save