migrating to the latest JUCE version
This commit is contained in:
@ -1,271 +1,269 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
AbstractFifo::AbstractFifo (int capacity) noexcept : bufferSize (capacity)
|
||||
{
|
||||
jassert (bufferSize > 0);
|
||||
}
|
||||
|
||||
AbstractFifo::~AbstractFifo() {}
|
||||
|
||||
int AbstractFifo::getTotalSize() const noexcept { return bufferSize; }
|
||||
int AbstractFifo::getFreeSpace() const noexcept { return bufferSize - getNumReady() - 1; }
|
||||
|
||||
int AbstractFifo::getNumReady() const noexcept
|
||||
{
|
||||
auto vs = validStart.get();
|
||||
auto ve = validEnd.get();
|
||||
return ve >= vs ? (ve - vs) : (bufferSize - (vs - ve));
|
||||
}
|
||||
|
||||
void AbstractFifo::reset() noexcept
|
||||
{
|
||||
validEnd = 0;
|
||||
validStart = 0;
|
||||
}
|
||||
|
||||
void AbstractFifo::setTotalSize (int newSize) noexcept
|
||||
{
|
||||
jassert (newSize > 0);
|
||||
reset();
|
||||
bufferSize = newSize;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void AbstractFifo::prepareToWrite (int numToWrite, int& startIndex1, int& blockSize1,
|
||||
int& startIndex2, int& blockSize2) const noexcept
|
||||
{
|
||||
auto vs = validStart.get();
|
||||
auto ve = validEnd.get();
|
||||
|
||||
auto freeSpace = ve >= vs ? (bufferSize - (ve - vs)) : (vs - ve);
|
||||
numToWrite = jmin (numToWrite, freeSpace - 1);
|
||||
|
||||
if (numToWrite <= 0)
|
||||
{
|
||||
startIndex1 = 0;
|
||||
startIndex2 = 0;
|
||||
blockSize1 = 0;
|
||||
blockSize2 = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
startIndex1 = ve;
|
||||
startIndex2 = 0;
|
||||
blockSize1 = jmin (bufferSize - ve, numToWrite);
|
||||
numToWrite -= blockSize1;
|
||||
blockSize2 = numToWrite <= 0 ? 0 : jmin (numToWrite, vs);
|
||||
}
|
||||
}
|
||||
|
||||
void AbstractFifo::finishedWrite (int numWritten) noexcept
|
||||
{
|
||||
jassert (numWritten >= 0 && numWritten < bufferSize);
|
||||
|
||||
auto newEnd = validEnd.get() + numWritten;
|
||||
|
||||
if (newEnd >= bufferSize)
|
||||
newEnd -= bufferSize;
|
||||
|
||||
validEnd = newEnd;
|
||||
}
|
||||
|
||||
void AbstractFifo::prepareToRead (int numWanted, int& startIndex1, int& blockSize1,
|
||||
int& startIndex2, int& blockSize2) const noexcept
|
||||
{
|
||||
auto vs = validStart.get();
|
||||
auto ve = validEnd.get();
|
||||
|
||||
auto numReady = ve >= vs ? (ve - vs) : (bufferSize - (vs - ve));
|
||||
numWanted = jmin (numWanted, numReady);
|
||||
|
||||
if (numWanted <= 0)
|
||||
{
|
||||
startIndex1 = 0;
|
||||
startIndex2 = 0;
|
||||
blockSize1 = 0;
|
||||
blockSize2 = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
startIndex1 = vs;
|
||||
startIndex2 = 0;
|
||||
blockSize1 = jmin (bufferSize - vs, numWanted);
|
||||
numWanted -= blockSize1;
|
||||
blockSize2 = numWanted <= 0 ? 0 : jmin (numWanted, ve);
|
||||
}
|
||||
}
|
||||
|
||||
void AbstractFifo::finishedRead (int numRead) noexcept
|
||||
{
|
||||
jassert (numRead >= 0 && numRead <= bufferSize);
|
||||
|
||||
auto newStart = validStart.get() + numRead;
|
||||
|
||||
if (newStart >= bufferSize)
|
||||
newStart -= bufferSize;
|
||||
|
||||
validStart = newStart;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
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>
|
||||
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 class AbstractFifo::ScopedReadWrite<AbstractFifo::ReadOrWrite::read>;
|
||||
template class AbstractFifo::ScopedReadWrite<AbstractFifo::ReadOrWrite::write>;
|
||||
|
||||
AbstractFifo::ScopedRead AbstractFifo::read (int numToRead) noexcept { return { *this, numToRead }; }
|
||||
AbstractFifo::ScopedWrite AbstractFifo::write (int numToWrite) noexcept { return { *this, numToWrite }; }
|
||||
|
||||
|
||||
//==============================================================================
|
||||
//==============================================================================
|
||||
#if JUCE_UNIT_TESTS
|
||||
|
||||
class AbstractFifoTests : public UnitTest
|
||||
{
|
||||
public:
|
||||
AbstractFifoTests()
|
||||
: UnitTest ("Abstract Fifo", UnitTestCategories::containers)
|
||||
{}
|
||||
|
||||
struct WriteThread : public Thread
|
||||
{
|
||||
WriteThread (AbstractFifo& f, int* b, Random rng)
|
||||
: Thread ("fifo writer"), fifo (f), buffer (b), random (rng)
|
||||
{
|
||||
startThread();
|
||||
}
|
||||
|
||||
~WriteThread()
|
||||
{
|
||||
stopThread (5000);
|
||||
}
|
||||
|
||||
void run()
|
||||
{
|
||||
int n = 0;
|
||||
|
||||
while (! threadShouldExit())
|
||||
{
|
||||
int num = random.nextInt (2000) + 1;
|
||||
|
||||
auto writer = fifo.write (num);
|
||||
|
||||
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()));
|
||||
|
||||
writer.forEach ([this, &n] (int index) { this->buffer[index] = n++; });
|
||||
}
|
||||
}
|
||||
|
||||
AbstractFifo& fifo;
|
||||
int* buffer;
|
||||
Random random;
|
||||
};
|
||||
|
||||
JUCE_BEGIN_IGNORE_WARNINGS_MSVC (6262)
|
||||
|
||||
void runTest() override
|
||||
{
|
||||
beginTest ("AbstractFifo");
|
||||
|
||||
int buffer[5000];
|
||||
AbstractFifo fifo (numElementsInArray (buffer));
|
||||
|
||||
WriteThread writer (fifo, buffer, getRandom());
|
||||
|
||||
int n = 0;
|
||||
Random r = getRandom();
|
||||
r.combineSeed (12345);
|
||||
|
||||
for (int count = 100000; --count >= 0;)
|
||||
{
|
||||
int num = r.nextInt (6000) + 1;
|
||||
|
||||
auto reader = fifo.read (num);
|
||||
|
||||
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");
|
||||
break;
|
||||
}
|
||||
|
||||
bool failed = false;
|
||||
|
||||
reader.forEach ([&failed, &buffer, &n] (int index)
|
||||
{
|
||||
failed = (buffer[index] != n++) || failed;
|
||||
});
|
||||
|
||||
if (failed)
|
||||
{
|
||||
expect (false, "read values were incorrect");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
JUCE_END_IGNORE_WARNINGS_MSVC
|
||||
};
|
||||
|
||||
static AbstractFifoTests fifoUnitTests;
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace juce
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
AbstractFifo::AbstractFifo (int capacity) noexcept : bufferSize (capacity)
|
||||
{
|
||||
jassert (bufferSize > 0);
|
||||
}
|
||||
|
||||
int AbstractFifo::getTotalSize() const noexcept { return bufferSize; }
|
||||
int AbstractFifo::getFreeSpace() const noexcept { return bufferSize - getNumReady() - 1; }
|
||||
|
||||
int AbstractFifo::getNumReady() const noexcept
|
||||
{
|
||||
auto vs = validStart.get();
|
||||
auto ve = validEnd.get();
|
||||
return ve >= vs ? (ve - vs) : (bufferSize - (vs - ve));
|
||||
}
|
||||
|
||||
void AbstractFifo::reset() noexcept
|
||||
{
|
||||
validEnd = 0;
|
||||
validStart = 0;
|
||||
}
|
||||
|
||||
void AbstractFifo::setTotalSize (int newSize) noexcept
|
||||
{
|
||||
jassert (newSize > 0);
|
||||
reset();
|
||||
bufferSize = newSize;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void AbstractFifo::prepareToWrite (int numToWrite, int& startIndex1, int& blockSize1,
|
||||
int& startIndex2, int& blockSize2) const noexcept
|
||||
{
|
||||
auto vs = validStart.get();
|
||||
auto ve = validEnd.get();
|
||||
|
||||
auto freeSpace = ve >= vs ? (bufferSize - (ve - vs)) : (vs - ve);
|
||||
numToWrite = jmin (numToWrite, freeSpace - 1);
|
||||
|
||||
if (numToWrite <= 0)
|
||||
{
|
||||
startIndex1 = 0;
|
||||
startIndex2 = 0;
|
||||
blockSize1 = 0;
|
||||
blockSize2 = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
startIndex1 = ve;
|
||||
startIndex2 = 0;
|
||||
blockSize1 = jmin (bufferSize - ve, numToWrite);
|
||||
numToWrite -= blockSize1;
|
||||
blockSize2 = numToWrite <= 0 ? 0 : jmin (numToWrite, vs);
|
||||
}
|
||||
}
|
||||
|
||||
void AbstractFifo::finishedWrite (int numWritten) noexcept
|
||||
{
|
||||
jassert (numWritten >= 0 && numWritten < bufferSize);
|
||||
|
||||
auto newEnd = validEnd.get() + numWritten;
|
||||
|
||||
if (newEnd >= bufferSize)
|
||||
newEnd -= bufferSize;
|
||||
|
||||
validEnd = newEnd;
|
||||
}
|
||||
|
||||
void AbstractFifo::prepareToRead (int numWanted, int& startIndex1, int& blockSize1,
|
||||
int& startIndex2, int& blockSize2) const noexcept
|
||||
{
|
||||
auto vs = validStart.get();
|
||||
auto ve = validEnd.get();
|
||||
|
||||
auto numReady = ve >= vs ? (ve - vs) : (bufferSize - (vs - ve));
|
||||
numWanted = jmin (numWanted, numReady);
|
||||
|
||||
if (numWanted <= 0)
|
||||
{
|
||||
startIndex1 = 0;
|
||||
startIndex2 = 0;
|
||||
blockSize1 = 0;
|
||||
blockSize2 = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
startIndex1 = vs;
|
||||
startIndex2 = 0;
|
||||
blockSize1 = jmin (bufferSize - vs, numWanted);
|
||||
numWanted -= blockSize1;
|
||||
blockSize2 = numWanted <= 0 ? 0 : jmin (numWanted, ve);
|
||||
}
|
||||
}
|
||||
|
||||
void AbstractFifo::finishedRead (int numRead) noexcept
|
||||
{
|
||||
jassert (numRead >= 0 && numRead <= bufferSize);
|
||||
|
||||
auto newStart = validStart.get() + numRead;
|
||||
|
||||
if (newStart >= bufferSize)
|
||||
newStart -= bufferSize;
|
||||
|
||||
validStart = newStart;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
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>
|
||||
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 class AbstractFifo::ScopedReadWrite<AbstractFifo::ReadOrWrite::read>;
|
||||
template class AbstractFifo::ScopedReadWrite<AbstractFifo::ReadOrWrite::write>;
|
||||
|
||||
AbstractFifo::ScopedRead AbstractFifo::read (int numToRead) noexcept { return { *this, numToRead }; }
|
||||
AbstractFifo::ScopedWrite AbstractFifo::write (int numToWrite) noexcept { return { *this, numToWrite }; }
|
||||
|
||||
|
||||
//==============================================================================
|
||||
//==============================================================================
|
||||
#if JUCE_UNIT_TESTS
|
||||
|
||||
class AbstractFifoTests : public UnitTest
|
||||
{
|
||||
public:
|
||||
AbstractFifoTests()
|
||||
: UnitTest ("Abstract Fifo", UnitTestCategories::containers)
|
||||
{}
|
||||
|
||||
struct WriteThread : public Thread
|
||||
{
|
||||
WriteThread (AbstractFifo& f, int* b, Random rng)
|
||||
: Thread ("fifo writer"), fifo (f), buffer (b), random (rng)
|
||||
{
|
||||
startThread();
|
||||
}
|
||||
|
||||
~WriteThread()
|
||||
{
|
||||
stopThread (5000);
|
||||
}
|
||||
|
||||
void run()
|
||||
{
|
||||
int n = 0;
|
||||
|
||||
while (! threadShouldExit())
|
||||
{
|
||||
int num = random.nextInt (2000) + 1;
|
||||
|
||||
auto writer = fifo.write (num);
|
||||
|
||||
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()));
|
||||
|
||||
writer.forEach ([this, &n] (int index) { this->buffer[index] = n++; });
|
||||
}
|
||||
}
|
||||
|
||||
AbstractFifo& fifo;
|
||||
int* buffer;
|
||||
Random random;
|
||||
};
|
||||
|
||||
JUCE_BEGIN_IGNORE_WARNINGS_MSVC (6262)
|
||||
|
||||
void runTest() override
|
||||
{
|
||||
beginTest ("AbstractFifo");
|
||||
|
||||
int buffer[5000];
|
||||
AbstractFifo fifo (numElementsInArray (buffer));
|
||||
|
||||
WriteThread writer (fifo, buffer, getRandom());
|
||||
|
||||
int n = 0;
|
||||
Random r = getRandom();
|
||||
r.combineSeed (12345);
|
||||
|
||||
for (int count = 100000; --count >= 0;)
|
||||
{
|
||||
int num = r.nextInt (6000) + 1;
|
||||
|
||||
auto reader = fifo.read (num);
|
||||
|
||||
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");
|
||||
break;
|
||||
}
|
||||
|
||||
bool failed = false;
|
||||
|
||||
reader.forEach ([&failed, &buffer, &n] (int index)
|
||||
{
|
||||
failed = (buffer[index] != n++) || failed;
|
||||
});
|
||||
|
||||
if (failed)
|
||||
{
|
||||
expect (false, "read values were incorrect");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
JUCE_END_IGNORE_WARNINGS_MSVC
|
||||
};
|
||||
|
||||
static AbstractFifoTests fifoUnitTests;
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace juce
|
||||
|
@ -1,336 +1,333 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Encapsulates the logic required to implement a lock-free FIFO.
|
||||
|
||||
This class handles the logic needed when building a single-reader, single-writer FIFO.
|
||||
|
||||
It doesn't actually hold any data itself, but your FIFO class can use one of these to manage
|
||||
its position and status when reading or writing to it.
|
||||
|
||||
To use it, you can call prepareToWrite() to determine the position within your own buffer that
|
||||
an incoming block of data should be stored, and prepareToRead() to find out when the next
|
||||
outgoing block should be read from.
|
||||
|
||||
e.g.
|
||||
@code
|
||||
struct MyFifo
|
||||
{
|
||||
void addToFifo (const int* someData, int numItems)
|
||||
{
|
||||
const auto scope = abstractFifo.write (numItems);
|
||||
|
||||
if (scope.blockSize1 > 0)
|
||||
copySomeData (myBuffer + scope.startIndex1, someData, scope.blockSize1);
|
||||
|
||||
if (scope.blockSize2 > 0)
|
||||
copySomeData (myBuffer + scope.startIndex2, someData, scope.blockSize2);
|
||||
}
|
||||
|
||||
void readFromFifo (int* someData, int numItems)
|
||||
{
|
||||
const auto scope = abstractFifo.read (numItems);
|
||||
|
||||
if (scope.blockSize1 > 0)
|
||||
copySomeData (someData, myBuffer + scope.startIndex1, scope.blockSize1);
|
||||
|
||||
if (scope.blockSize2 > 0)
|
||||
copySomeData (someData + scope.blockSize1, myBuffer + scope.startIndex2, scope.blockSize2);
|
||||
}
|
||||
|
||||
AbstractFifo abstractFifo { 1024 };
|
||||
int myBuffer[1024];
|
||||
};
|
||||
@endcode
|
||||
|
||||
@tags{Core}
|
||||
*/
|
||||
class JUCE_API AbstractFifo
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates a FIFO to manage a buffer with the specified capacity. */
|
||||
AbstractFifo (int capacity) noexcept;
|
||||
|
||||
/** Destructor */
|
||||
~AbstractFifo();
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the total size of the buffer being managed. */
|
||||
int getTotalSize() const noexcept;
|
||||
|
||||
/** Returns the number of items that can currently be added to the buffer without it overflowing. */
|
||||
int getFreeSpace() const noexcept;
|
||||
|
||||
/** Returns the number of items that can currently be read from the buffer. */
|
||||
int getNumReady() const noexcept;
|
||||
|
||||
/** Clears the buffer positions, so that it appears empty. */
|
||||
void reset() noexcept;
|
||||
|
||||
/** Changes the buffer's total size.
|
||||
Note that this isn't thread-safe, so don't call it if there's any danger that it
|
||||
might overlap with a call to any other method in this class!
|
||||
*/
|
||||
void setTotalSize (int newSize) noexcept;
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the location within the buffer at which an incoming block of data should be written.
|
||||
|
||||
Because the section of data that you want to add to the buffer may overlap the end
|
||||
and wrap around to the start, two blocks within your buffer are returned, and you
|
||||
should copy your data into the first one, with any remaining data spilling over into
|
||||
the second.
|
||||
|
||||
If the number of items you ask for is too large to fit within the buffer's free space, then
|
||||
blockSize1 + blockSize2 may add up to a lower value than numToWrite. If this happens, you
|
||||
may decide to keep waiting and re-trying the method until there's enough space available.
|
||||
|
||||
After calling this method, if you choose to write your data into the blocks returned, you
|
||||
must call finishedWrite() to tell the FIFO how much data you actually added.
|
||||
|
||||
e.g.
|
||||
@code
|
||||
void addToFifo (const int* someData, int numItems)
|
||||
{
|
||||
int start1, size1, start2, size2;
|
||||
prepareToWrite (numItems, start1, size1, start2, size2);
|
||||
|
||||
if (size1 > 0)
|
||||
copySomeData (myBuffer + start1, someData, size1);
|
||||
|
||||
if (size2 > 0)
|
||||
copySomeData (myBuffer + start2, someData + size1, size2);
|
||||
|
||||
finishedWrite (size1 + size2);
|
||||
}
|
||||
@endcode
|
||||
|
||||
@param numToWrite indicates how many items you'd like to add to the buffer
|
||||
@param startIndex1 on exit, this will contain the start index in your buffer at which your data should be written
|
||||
@param blockSize1 on exit, this indicates how many items can be written to the block starting at startIndex1
|
||||
@param startIndex2 on exit, this will contain the start index in your buffer at which any data that didn't fit into
|
||||
the first block should be written
|
||||
@param blockSize2 on exit, this indicates how many items can be written to the block starting at startIndex2
|
||||
@see finishedWrite
|
||||
*/
|
||||
void prepareToWrite (int numToWrite, int& startIndex1, int& blockSize1, int& startIndex2, int& blockSize2) const noexcept;
|
||||
|
||||
/** Called after writing from the FIFO, to indicate that this many items have been added.
|
||||
@see prepareToWrite
|
||||
*/
|
||||
void finishedWrite (int numWritten) noexcept;
|
||||
|
||||
/** Returns the location within the buffer from which the next block of data should be read.
|
||||
|
||||
Because the section of data that you want to read from the buffer may overlap the end
|
||||
and wrap around to the start, two blocks within your buffer are returned, and you
|
||||
should read from both of them.
|
||||
|
||||
If the number of items you ask for is greater than the amount of data available, then
|
||||
blockSize1 + blockSize2 may add up to a lower value than numWanted. If this happens, you
|
||||
may decide to keep waiting and re-trying the method until there's enough data available.
|
||||
|
||||
After calling this method, if you choose to read the data, you must call finishedRead() to
|
||||
tell the FIFO how much data you have consumed.
|
||||
|
||||
e.g.
|
||||
@code
|
||||
void readFromFifo (int* someData, int numItems)
|
||||
{
|
||||
int start1, size1, start2, size2;
|
||||
prepareToRead (numSamples, start1, size1, start2, size2);
|
||||
|
||||
if (size1 > 0)
|
||||
copySomeData (someData, myBuffer + start1, size1);
|
||||
|
||||
if (size2 > 0)
|
||||
copySomeData (someData + size1, myBuffer + start2, size2);
|
||||
|
||||
finishedRead (size1 + size2);
|
||||
}
|
||||
@endcode
|
||||
|
||||
@param numWanted indicates how many items you'd like to add to the buffer
|
||||
@param startIndex1 on exit, this will contain the start index in your buffer at which your data should be written
|
||||
@param blockSize1 on exit, this indicates how many items can be written to the block starting at startIndex1
|
||||
@param startIndex2 on exit, this will contain the start index in your buffer at which any data that didn't fit into
|
||||
the first block should be written
|
||||
@param blockSize2 on exit, this indicates how many items can be written to the block starting at startIndex2
|
||||
@see finishedRead
|
||||
*/
|
||||
void prepareToRead (int numWanted, int& startIndex1, int& blockSize1, int& startIndex2, int& blockSize2) const noexcept;
|
||||
|
||||
/** Called after reading from the FIFO, to indicate that this many items have now been consumed.
|
||||
@see prepareToRead
|
||||
*/
|
||||
void finishedRead (int numRead) noexcept;
|
||||
|
||||
//==============================================================================
|
||||
|
||||
private:
|
||||
enum class ReadOrWrite
|
||||
{
|
||||
read,
|
||||
write
|
||||
};
|
||||
|
||||
public:
|
||||
/** Class for a scoped reader/writer */
|
||||
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& f, int num) noexcept : fifo (&f)
|
||||
{
|
||||
prepare (*fifo, num);
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
if (fifo != nullptr)
|
||||
finish (*fifo, blockSize1 + blockSize2);
|
||||
}
|
||||
|
||||
/** 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;
|
||||
|
||||
/** 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;
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
int bufferSize;
|
||||
Atomic<int> validStart, validEnd;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AbstractFifo)
|
||||
};
|
||||
|
||||
template <>
|
||||
inline void AbstractFifo::ScopedReadWrite<AbstractFifo::ReadOrWrite::read>::finish (AbstractFifo& f, int num) noexcept
|
||||
{
|
||||
f.finishedRead (num);
|
||||
}
|
||||
|
||||
template <>
|
||||
inline void AbstractFifo::ScopedReadWrite<AbstractFifo::ReadOrWrite::write>::finish (AbstractFifo& f, int num) noexcept
|
||||
{
|
||||
f.finishedWrite (num);
|
||||
}
|
||||
|
||||
template <>
|
||||
inline void AbstractFifo::ScopedReadWrite<AbstractFifo::ReadOrWrite::read>::prepare (AbstractFifo& f, int num) noexcept
|
||||
{
|
||||
f.prepareToRead (num, startIndex1, blockSize1, startIndex2, blockSize2);
|
||||
}
|
||||
|
||||
template <>
|
||||
inline void AbstractFifo::ScopedReadWrite<AbstractFifo::ReadOrWrite::write>::prepare (AbstractFifo& f, int num) noexcept
|
||||
{
|
||||
f.prepareToWrite (num, startIndex1, blockSize1, startIndex2, blockSize2);
|
||||
}
|
||||
|
||||
|
||||
} // namespace juce
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Encapsulates the logic required to implement a lock-free FIFO.
|
||||
|
||||
This class handles the logic needed when building a single-reader, single-writer FIFO.
|
||||
|
||||
It doesn't actually hold any data itself, but your FIFO class can use one of these to manage
|
||||
its position and status when reading or writing to it.
|
||||
|
||||
To use it, you can call prepareToWrite() to determine the position within your own buffer that
|
||||
an incoming block of data should be stored, and prepareToRead() to find out when the next
|
||||
outgoing block should be read from.
|
||||
|
||||
e.g.
|
||||
@code
|
||||
struct MyFifo
|
||||
{
|
||||
void addToFifo (const int* someData, int numItems)
|
||||
{
|
||||
const auto scope = abstractFifo.write (numItems);
|
||||
|
||||
if (scope.blockSize1 > 0)
|
||||
copySomeData (myBuffer + scope.startIndex1, someData, scope.blockSize1);
|
||||
|
||||
if (scope.blockSize2 > 0)
|
||||
copySomeData (myBuffer + scope.startIndex2, someData, scope.blockSize2);
|
||||
}
|
||||
|
||||
void readFromFifo (int* someData, int numItems)
|
||||
{
|
||||
const auto scope = abstractFifo.read (numItems);
|
||||
|
||||
if (scope.blockSize1 > 0)
|
||||
copySomeData (someData, myBuffer + scope.startIndex1, scope.blockSize1);
|
||||
|
||||
if (scope.blockSize2 > 0)
|
||||
copySomeData (someData + scope.blockSize1, myBuffer + scope.startIndex2, scope.blockSize2);
|
||||
}
|
||||
|
||||
AbstractFifo abstractFifo { 1024 };
|
||||
int myBuffer[1024];
|
||||
};
|
||||
@endcode
|
||||
|
||||
@tags{Core}
|
||||
*/
|
||||
class JUCE_API AbstractFifo
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates a FIFO to manage a buffer with the specified capacity. */
|
||||
AbstractFifo (int capacity) noexcept;
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the total size of the buffer being managed. */
|
||||
int getTotalSize() const noexcept;
|
||||
|
||||
/** Returns the number of items that can currently be added to the buffer without it overflowing. */
|
||||
int getFreeSpace() const noexcept;
|
||||
|
||||
/** Returns the number of items that can currently be read from the buffer. */
|
||||
int getNumReady() const noexcept;
|
||||
|
||||
/** Clears the buffer positions, so that it appears empty. */
|
||||
void reset() noexcept;
|
||||
|
||||
/** Changes the buffer's total size.
|
||||
Note that this isn't thread-safe, so don't call it if there's any danger that it
|
||||
might overlap with a call to any other method in this class!
|
||||
*/
|
||||
void setTotalSize (int newSize) noexcept;
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the location within the buffer at which an incoming block of data should be written.
|
||||
|
||||
Because the section of data that you want to add to the buffer may overlap the end
|
||||
and wrap around to the start, two blocks within your buffer are returned, and you
|
||||
should copy your data into the first one, with any remaining data spilling over into
|
||||
the second.
|
||||
|
||||
If the number of items you ask for is too large to fit within the buffer's free space, then
|
||||
blockSize1 + blockSize2 may add up to a lower value than numToWrite. If this happens, you
|
||||
may decide to keep waiting and re-trying the method until there's enough space available.
|
||||
|
||||
After calling this method, if you choose to write your data into the blocks returned, you
|
||||
must call finishedWrite() to tell the FIFO how much data you actually added.
|
||||
|
||||
e.g.
|
||||
@code
|
||||
void addToFifo (const int* someData, int numItems)
|
||||
{
|
||||
int start1, size1, start2, size2;
|
||||
prepareToWrite (numItems, start1, size1, start2, size2);
|
||||
|
||||
if (size1 > 0)
|
||||
copySomeData (myBuffer + start1, someData, size1);
|
||||
|
||||
if (size2 > 0)
|
||||
copySomeData (myBuffer + start2, someData + size1, size2);
|
||||
|
||||
finishedWrite (size1 + size2);
|
||||
}
|
||||
@endcode
|
||||
|
||||
@param numToWrite indicates how many items you'd like to add to the buffer
|
||||
@param startIndex1 on exit, this will contain the start index in your buffer at which your data should be written
|
||||
@param blockSize1 on exit, this indicates how many items can be written to the block starting at startIndex1
|
||||
@param startIndex2 on exit, this will contain the start index in your buffer at which any data that didn't fit into
|
||||
the first block should be written
|
||||
@param blockSize2 on exit, this indicates how many items can be written to the block starting at startIndex2
|
||||
@see finishedWrite
|
||||
*/
|
||||
void prepareToWrite (int numToWrite, int& startIndex1, int& blockSize1, int& startIndex2, int& blockSize2) const noexcept;
|
||||
|
||||
/** Called after writing from the FIFO, to indicate that this many items have been added.
|
||||
@see prepareToWrite
|
||||
*/
|
||||
void finishedWrite (int numWritten) noexcept;
|
||||
|
||||
/** Returns the location within the buffer from which the next block of data should be read.
|
||||
|
||||
Because the section of data that you want to read from the buffer may overlap the end
|
||||
and wrap around to the start, two blocks within your buffer are returned, and you
|
||||
should read from both of them.
|
||||
|
||||
If the number of items you ask for is greater than the amount of data available, then
|
||||
blockSize1 + blockSize2 may add up to a lower value than numWanted. If this happens, you
|
||||
may decide to keep waiting and re-trying the method until there's enough data available.
|
||||
|
||||
After calling this method, if you choose to read the data, you must call finishedRead() to
|
||||
tell the FIFO how much data you have consumed.
|
||||
|
||||
e.g.
|
||||
@code
|
||||
void readFromFifo (int* someData, int numItems)
|
||||
{
|
||||
int start1, size1, start2, size2;
|
||||
prepareToRead (numSamples, start1, size1, start2, size2);
|
||||
|
||||
if (size1 > 0)
|
||||
copySomeData (someData, myBuffer + start1, size1);
|
||||
|
||||
if (size2 > 0)
|
||||
copySomeData (someData + size1, myBuffer + start2, size2);
|
||||
|
||||
finishedRead (size1 + size2);
|
||||
}
|
||||
@endcode
|
||||
|
||||
@param numWanted indicates how many items you'd like to add to the buffer
|
||||
@param startIndex1 on exit, this will contain the start index in your buffer at which your data should be written
|
||||
@param blockSize1 on exit, this indicates how many items can be written to the block starting at startIndex1
|
||||
@param startIndex2 on exit, this will contain the start index in your buffer at which any data that didn't fit into
|
||||
the first block should be written
|
||||
@param blockSize2 on exit, this indicates how many items can be written to the block starting at startIndex2
|
||||
@see finishedRead
|
||||
*/
|
||||
void prepareToRead (int numWanted, int& startIndex1, int& blockSize1, int& startIndex2, int& blockSize2) const noexcept;
|
||||
|
||||
/** Called after reading from the FIFO, to indicate that this many items have now been consumed.
|
||||
@see prepareToRead
|
||||
*/
|
||||
void finishedRead (int numRead) noexcept;
|
||||
|
||||
//==============================================================================
|
||||
|
||||
private:
|
||||
enum class ReadOrWrite
|
||||
{
|
||||
read,
|
||||
write
|
||||
};
|
||||
|
||||
public:
|
||||
/** Class for a scoped reader/writer */
|
||||
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& f, int num) noexcept : fifo (&f)
|
||||
{
|
||||
prepare (*fifo, num);
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
if (fifo != nullptr)
|
||||
finish (*fifo, blockSize1 + blockSize2);
|
||||
}
|
||||
|
||||
/** 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;
|
||||
|
||||
/** 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;
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
int bufferSize;
|
||||
Atomic<int> validStart, validEnd;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AbstractFifo)
|
||||
};
|
||||
|
||||
template <>
|
||||
inline void AbstractFifo::ScopedReadWrite<AbstractFifo::ReadOrWrite::read>::finish (AbstractFifo& f, int num) noexcept
|
||||
{
|
||||
f.finishedRead (num);
|
||||
}
|
||||
|
||||
template <>
|
||||
inline void AbstractFifo::ScopedReadWrite<AbstractFifo::ReadOrWrite::write>::finish (AbstractFifo& f, int num) noexcept
|
||||
{
|
||||
f.finishedWrite (num);
|
||||
}
|
||||
|
||||
template <>
|
||||
inline void AbstractFifo::ScopedReadWrite<AbstractFifo::ReadOrWrite::read>::prepare (AbstractFifo& f, int num) noexcept
|
||||
{
|
||||
f.prepareToRead (num, startIndex1, blockSize1, startIndex2, blockSize2);
|
||||
}
|
||||
|
||||
template <>
|
||||
inline void AbstractFifo::ScopedReadWrite<AbstractFifo::ReadOrWrite::write>::prepare (AbstractFifo& f, int num) noexcept
|
||||
{
|
||||
f.prepareToWrite (num, startIndex1, blockSize1, startIndex2, blockSize2);
|
||||
}
|
||||
|
||||
|
||||
} // namespace juce
|
||||
|
2303
deps/juce/modules/juce_core/containers/juce_Array.h
vendored
2303
deps/juce/modules/juce_core/containers/juce_Array.h
vendored
File diff suppressed because it is too large
Load Diff
@ -1,121 +1,121 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Implements some basic array storage allocation functions.
|
||||
|
||||
This class isn't really for public use - it used to be part of the
|
||||
container classes but has since been superseded by ArrayBase. Eventually
|
||||
it will be removed from the API.
|
||||
|
||||
@tags{Core}
|
||||
*/
|
||||
template <class ElementType, class TypeOfCriticalSectionToUse>
|
||||
class ArrayAllocationBase : public TypeOfCriticalSectionToUse
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates an empty array. */
|
||||
ArrayAllocationBase() = default;
|
||||
|
||||
/** Destructor. */
|
||||
~ArrayAllocationBase() = default;
|
||||
|
||||
ArrayAllocationBase (ArrayAllocationBase&& other) noexcept
|
||||
: elements (std::move (other.elements)),
|
||||
numAllocated (other.numAllocated)
|
||||
{
|
||||
}
|
||||
|
||||
ArrayAllocationBase& operator= (ArrayAllocationBase&& other) noexcept
|
||||
{
|
||||
elements = std::move (other.elements);
|
||||
numAllocated = other.numAllocated;
|
||||
return *this;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Changes the amount of storage allocated.
|
||||
|
||||
This will retain any data currently held in the array, and either add or
|
||||
remove extra space at the end.
|
||||
|
||||
@param numElements the number of elements that are needed
|
||||
*/
|
||||
void setAllocatedSize (int numElements)
|
||||
{
|
||||
if (numAllocated != numElements)
|
||||
{
|
||||
if (numElements > 0)
|
||||
elements.realloc ((size_t) numElements);
|
||||
else
|
||||
elements.free();
|
||||
|
||||
numAllocated = numElements;
|
||||
}
|
||||
}
|
||||
|
||||
/** Increases the amount of storage allocated if it is less than a given amount.
|
||||
|
||||
This will retain any data currently held in the array, but will add
|
||||
extra space at the end to make sure there it's at least as big as the size
|
||||
passed in. If it's already bigger, no action is taken.
|
||||
|
||||
@param minNumElements the minimum number of elements that are needed
|
||||
*/
|
||||
void ensureAllocatedSize (int minNumElements)
|
||||
{
|
||||
if (minNumElements > numAllocated)
|
||||
setAllocatedSize ((minNumElements + minNumElements / 2 + 8) & ~7);
|
||||
|
||||
jassert (numAllocated <= 0 || elements != nullptr);
|
||||
}
|
||||
|
||||
/** Minimises the amount of storage allocated so that it's no more than
|
||||
the given number of elements.
|
||||
*/
|
||||
void shrinkToNoMoreThan (int maxNumElements)
|
||||
{
|
||||
if (maxNumElements < numAllocated)
|
||||
setAllocatedSize (maxNumElements);
|
||||
}
|
||||
|
||||
/** Swap the contents of two objects. */
|
||||
void swapWith (ArrayAllocationBase& other) noexcept
|
||||
{
|
||||
elements.swapWith (other.elements);
|
||||
std::swap (numAllocated, other.numAllocated);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
HeapBlock<ElementType> elements;
|
||||
int numAllocated = 0;
|
||||
|
||||
private:
|
||||
JUCE_DECLARE_NON_COPYABLE (ArrayAllocationBase)
|
||||
};
|
||||
|
||||
} // namespace juce
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Implements some basic array storage allocation functions.
|
||||
|
||||
This class isn't really for public use - it used to be part of the
|
||||
container classes but has since been superseded by ArrayBase. Eventually
|
||||
it will be removed from the API.
|
||||
|
||||
@tags{Core}
|
||||
*/
|
||||
template <class ElementType, class TypeOfCriticalSectionToUse>
|
||||
class ArrayAllocationBase : public TypeOfCriticalSectionToUse
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates an empty array. */
|
||||
ArrayAllocationBase() = default;
|
||||
|
||||
/** Destructor. */
|
||||
~ArrayAllocationBase() = default;
|
||||
|
||||
ArrayAllocationBase (ArrayAllocationBase&& other) noexcept
|
||||
: elements (std::move (other.elements)),
|
||||
numAllocated (other.numAllocated)
|
||||
{
|
||||
}
|
||||
|
||||
ArrayAllocationBase& operator= (ArrayAllocationBase&& other) noexcept
|
||||
{
|
||||
elements = std::move (other.elements);
|
||||
numAllocated = other.numAllocated;
|
||||
return *this;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Changes the amount of storage allocated.
|
||||
|
||||
This will retain any data currently held in the array, and either add or
|
||||
remove extra space at the end.
|
||||
|
||||
@param numElements the number of elements that are needed
|
||||
*/
|
||||
void setAllocatedSize (int numElements)
|
||||
{
|
||||
if (numAllocated != numElements)
|
||||
{
|
||||
if (numElements > 0)
|
||||
elements.realloc ((size_t) numElements);
|
||||
else
|
||||
elements.free();
|
||||
|
||||
numAllocated = numElements;
|
||||
}
|
||||
}
|
||||
|
||||
/** Increases the amount of storage allocated if it is less than a given amount.
|
||||
|
||||
This will retain any data currently held in the array, but will add
|
||||
extra space at the end to make sure there it's at least as big as the size
|
||||
passed in. If it's already bigger, no action is taken.
|
||||
|
||||
@param minNumElements the minimum number of elements that are needed
|
||||
*/
|
||||
void ensureAllocatedSize (int minNumElements)
|
||||
{
|
||||
if (minNumElements > numAllocated)
|
||||
setAllocatedSize ((minNumElements + minNumElements / 2 + 8) & ~7);
|
||||
|
||||
jassert (numAllocated <= 0 || elements != nullptr);
|
||||
}
|
||||
|
||||
/** Minimises the amount of storage allocated so that it's no more than
|
||||
the given number of elements.
|
||||
*/
|
||||
void shrinkToNoMoreThan (int maxNumElements)
|
||||
{
|
||||
if (maxNumElements < numAllocated)
|
||||
setAllocatedSize (maxNumElements);
|
||||
}
|
||||
|
||||
/** Swap the contents of two objects. */
|
||||
void swapWith (ArrayAllocationBase& other) noexcept
|
||||
{
|
||||
elements.swapWith (other.elements);
|
||||
std::swap (numAllocated, other.numAllocated);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
HeapBlock<ElementType> elements;
|
||||
int numAllocated = 0;
|
||||
|
||||
private:
|
||||
JUCE_DECLARE_NON_COPYABLE (ArrayAllocationBase)
|
||||
};
|
||||
|
||||
} // namespace juce
|
||||
|
File diff suppressed because it is too large
Load Diff
1214
deps/juce/modules/juce_core/containers/juce_ArrayBase.h
vendored
1214
deps/juce/modules/juce_core/containers/juce_ArrayBase.h
vendored
File diff suppressed because it is too large
Load Diff
@ -1,132 +1,132 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
DynamicObject::DynamicObject()
|
||||
{
|
||||
}
|
||||
|
||||
DynamicObject::DynamicObject (const DynamicObject& other)
|
||||
: ReferenceCountedObject(), properties (other.properties)
|
||||
{
|
||||
}
|
||||
|
||||
DynamicObject::~DynamicObject()
|
||||
{
|
||||
}
|
||||
|
||||
bool DynamicObject::hasProperty (const Identifier& propertyName) const
|
||||
{
|
||||
const var* const v = properties.getVarPointer (propertyName);
|
||||
return v != nullptr && ! v->isMethod();
|
||||
}
|
||||
|
||||
const var& DynamicObject::getProperty (const Identifier& propertyName) const
|
||||
{
|
||||
return properties [propertyName];
|
||||
}
|
||||
|
||||
void DynamicObject::setProperty (const Identifier& propertyName, const var& newValue)
|
||||
{
|
||||
properties.set (propertyName, newValue);
|
||||
}
|
||||
|
||||
void DynamicObject::removeProperty (const Identifier& propertyName)
|
||||
{
|
||||
properties.remove (propertyName);
|
||||
}
|
||||
|
||||
bool DynamicObject::hasMethod (const Identifier& methodName) const
|
||||
{
|
||||
return getProperty (methodName).isMethod();
|
||||
}
|
||||
|
||||
var DynamicObject::invokeMethod (Identifier method, const var::NativeFunctionArgs& args)
|
||||
{
|
||||
if (auto function = properties [method].getNativeFunction())
|
||||
return function (args);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
void DynamicObject::setMethod (Identifier name, var::NativeFunction function)
|
||||
{
|
||||
properties.set (name, var (function));
|
||||
}
|
||||
|
||||
void DynamicObject::clear()
|
||||
{
|
||||
properties.clear();
|
||||
}
|
||||
|
||||
void DynamicObject::cloneAllProperties()
|
||||
{
|
||||
for (int i = properties.size(); --i >= 0;)
|
||||
if (auto* v = properties.getVarPointerAt (i))
|
||||
*v = v->clone();
|
||||
}
|
||||
|
||||
DynamicObject::Ptr DynamicObject::clone()
|
||||
{
|
||||
Ptr d (new DynamicObject (*this));
|
||||
d->cloneAllProperties();
|
||||
return d;
|
||||
}
|
||||
|
||||
void DynamicObject::writeAsJSON (OutputStream& out, const int indentLevel, const bool allOnOneLine, int maximumDecimalPlaces)
|
||||
{
|
||||
out << '{';
|
||||
if (! allOnOneLine)
|
||||
out << newLine;
|
||||
|
||||
const int numValues = properties.size();
|
||||
|
||||
for (int i = 0; i < numValues; ++i)
|
||||
{
|
||||
if (! allOnOneLine)
|
||||
JSONFormatter::writeSpaces (out, indentLevel + JSONFormatter::indentSize);
|
||||
|
||||
out << '"';
|
||||
JSONFormatter::writeString (out, properties.getName (i));
|
||||
out << "\": ";
|
||||
JSONFormatter::write (out, properties.getValueAt (i), indentLevel + JSONFormatter::indentSize, allOnOneLine, maximumDecimalPlaces);
|
||||
|
||||
if (i < numValues - 1)
|
||||
{
|
||||
if (allOnOneLine)
|
||||
out << ", ";
|
||||
else
|
||||
out << ',' << newLine;
|
||||
}
|
||||
else if (! allOnOneLine)
|
||||
out << newLine;
|
||||
}
|
||||
|
||||
if (! allOnOneLine)
|
||||
JSONFormatter::writeSpaces (out, indentLevel);
|
||||
|
||||
out << '}';
|
||||
}
|
||||
|
||||
} // namespace juce
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
DynamicObject::DynamicObject()
|
||||
{
|
||||
}
|
||||
|
||||
DynamicObject::DynamicObject (const DynamicObject& other)
|
||||
: ReferenceCountedObject(), properties (other.properties)
|
||||
{
|
||||
}
|
||||
|
||||
DynamicObject::~DynamicObject()
|
||||
{
|
||||
}
|
||||
|
||||
bool DynamicObject::hasProperty (const Identifier& propertyName) const
|
||||
{
|
||||
const var* const v = properties.getVarPointer (propertyName);
|
||||
return v != nullptr && ! v->isMethod();
|
||||
}
|
||||
|
||||
const var& DynamicObject::getProperty (const Identifier& propertyName) const
|
||||
{
|
||||
return properties [propertyName];
|
||||
}
|
||||
|
||||
void DynamicObject::setProperty (const Identifier& propertyName, const var& newValue)
|
||||
{
|
||||
properties.set (propertyName, newValue);
|
||||
}
|
||||
|
||||
void DynamicObject::removeProperty (const Identifier& propertyName)
|
||||
{
|
||||
properties.remove (propertyName);
|
||||
}
|
||||
|
||||
bool DynamicObject::hasMethod (const Identifier& methodName) const
|
||||
{
|
||||
return getProperty (methodName).isMethod();
|
||||
}
|
||||
|
||||
var DynamicObject::invokeMethod (Identifier method, const var::NativeFunctionArgs& args)
|
||||
{
|
||||
if (auto function = properties [method].getNativeFunction())
|
||||
return function (args);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
void DynamicObject::setMethod (Identifier name, var::NativeFunction function)
|
||||
{
|
||||
properties.set (name, var (function));
|
||||
}
|
||||
|
||||
void DynamicObject::clear()
|
||||
{
|
||||
properties.clear();
|
||||
}
|
||||
|
||||
void DynamicObject::cloneAllProperties()
|
||||
{
|
||||
for (int i = properties.size(); --i >= 0;)
|
||||
if (auto* v = properties.getVarPointerAt (i))
|
||||
*v = v->clone();
|
||||
}
|
||||
|
||||
DynamicObject::Ptr DynamicObject::clone()
|
||||
{
|
||||
Ptr d (new DynamicObject (*this));
|
||||
d->cloneAllProperties();
|
||||
return d;
|
||||
}
|
||||
|
||||
void DynamicObject::writeAsJSON (OutputStream& out, const int indentLevel, const bool allOnOneLine, int maximumDecimalPlaces)
|
||||
{
|
||||
out << '{';
|
||||
if (! allOnOneLine)
|
||||
out << newLine;
|
||||
|
||||
const int numValues = properties.size();
|
||||
|
||||
for (int i = 0; i < numValues; ++i)
|
||||
{
|
||||
if (! allOnOneLine)
|
||||
JSONFormatter::writeSpaces (out, indentLevel + JSONFormatter::indentSize);
|
||||
|
||||
out << '"';
|
||||
JSONFormatter::writeString (out, properties.getName (i));
|
||||
out << "\": ";
|
||||
JSONFormatter::write (out, properties.getValueAt (i), indentLevel + JSONFormatter::indentSize, allOnOneLine, maximumDecimalPlaces);
|
||||
|
||||
if (i < numValues - 1)
|
||||
{
|
||||
if (allOnOneLine)
|
||||
out << ", ";
|
||||
else
|
||||
out << ',' << newLine;
|
||||
}
|
||||
else if (! allOnOneLine)
|
||||
out << newLine;
|
||||
}
|
||||
|
||||
if (! allOnOneLine)
|
||||
JSONFormatter::writeSpaces (out, indentLevel);
|
||||
|
||||
out << '}';
|
||||
}
|
||||
|
||||
} // namespace juce
|
||||
|
@ -1,127 +1,127 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Represents a dynamically implemented object.
|
||||
|
||||
This class is primarily intended for wrapping scripting language objects,
|
||||
but could be used for other purposes.
|
||||
|
||||
An instance of a DynamicObject can be used to store named properties, and
|
||||
by subclassing hasMethod() and invokeMethod(), you can give your object
|
||||
methods.
|
||||
|
||||
@tags{Core}
|
||||
*/
|
||||
class JUCE_API DynamicObject : public ReferenceCountedObject
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
DynamicObject();
|
||||
DynamicObject (const DynamicObject&);
|
||||
~DynamicObject() override;
|
||||
|
||||
using Ptr = ReferenceCountedObjectPtr<DynamicObject>;
|
||||
|
||||
//==============================================================================
|
||||
/** Returns true if the object has a property with this name.
|
||||
Note that if the property is actually a method, this will return false.
|
||||
*/
|
||||
virtual bool hasProperty (const Identifier& propertyName) const;
|
||||
|
||||
/** Returns a named property.
|
||||
This returns var() if no such property exists.
|
||||
*/
|
||||
virtual const var& getProperty (const Identifier& propertyName) const;
|
||||
|
||||
/** Sets a named property. */
|
||||
virtual void setProperty (const Identifier& propertyName, const var& newValue);
|
||||
|
||||
/** Removes a named property. */
|
||||
virtual void removeProperty (const Identifier& propertyName);
|
||||
|
||||
//==============================================================================
|
||||
/** Checks whether this object has the specified method.
|
||||
|
||||
The default implementation of this just checks whether there's a property
|
||||
with this name that's actually a method, but this can be overridden for
|
||||
building objects with dynamic invocation.
|
||||
*/
|
||||
virtual bool hasMethod (const Identifier& methodName) const;
|
||||
|
||||
/** Invokes a named method on this object.
|
||||
|
||||
The default implementation looks up the named property, and if it's a method
|
||||
call, then it invokes it.
|
||||
|
||||
This method is virtual to allow more dynamic invocation to used for objects
|
||||
where the methods may not already be set as properties.
|
||||
*/
|
||||
virtual var invokeMethod (Identifier methodName,
|
||||
const var::NativeFunctionArgs& args);
|
||||
|
||||
/** Adds a method to the class.
|
||||
|
||||
This is basically the same as calling setProperty (methodName, (var::NativeFunction) myFunction), but
|
||||
helps to avoid accidentally invoking the wrong type of var constructor. It also makes
|
||||
the code easier to read.
|
||||
*/
|
||||
void setMethod (Identifier methodName, var::NativeFunction function);
|
||||
|
||||
//==============================================================================
|
||||
/** Removes all properties and methods from the object. */
|
||||
void clear();
|
||||
|
||||
/** Returns the NamedValueSet that holds the object's properties. */
|
||||
NamedValueSet& getProperties() noexcept { return properties; }
|
||||
|
||||
/** Calls var::clone() on all the properties that this object contains. */
|
||||
void cloneAllProperties();
|
||||
|
||||
//==============================================================================
|
||||
/** Returns a clone of this object.
|
||||
The default implementation of this method just returns a new DynamicObject
|
||||
with a (deep) copy of all of its properties. Subclasses can override this to
|
||||
implement their own custom copy routines.
|
||||
*/
|
||||
virtual Ptr clone();
|
||||
|
||||
//==============================================================================
|
||||
/** Writes this object to a text stream in JSON format.
|
||||
This method is used by JSON::toString and JSON::writeToStream, and you should
|
||||
never need to call it directly, but it's virtual so that custom object types
|
||||
can stringify themselves appropriately.
|
||||
*/
|
||||
virtual void writeAsJSON (OutputStream&, int indentLevel, bool allOnOneLine, int maximumDecimalPlaces);
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
NamedValueSet properties;
|
||||
|
||||
JUCE_LEAK_DETECTOR (DynamicObject)
|
||||
};
|
||||
|
||||
} // namespace juce
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Represents a dynamically implemented object.
|
||||
|
||||
This class is primarily intended for wrapping scripting language objects,
|
||||
but could be used for other purposes.
|
||||
|
||||
An instance of a DynamicObject can be used to store named properties, and
|
||||
by subclassing hasMethod() and invokeMethod(), you can give your object
|
||||
methods.
|
||||
|
||||
@tags{Core}
|
||||
*/
|
||||
class JUCE_API DynamicObject : public ReferenceCountedObject
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
DynamicObject();
|
||||
DynamicObject (const DynamicObject&);
|
||||
~DynamicObject() override;
|
||||
|
||||
using Ptr = ReferenceCountedObjectPtr<DynamicObject>;
|
||||
|
||||
//==============================================================================
|
||||
/** Returns true if the object has a property with this name.
|
||||
Note that if the property is actually a method, this will return false.
|
||||
*/
|
||||
virtual bool hasProperty (const Identifier& propertyName) const;
|
||||
|
||||
/** Returns a named property.
|
||||
This returns var() if no such property exists.
|
||||
*/
|
||||
virtual const var& getProperty (const Identifier& propertyName) const;
|
||||
|
||||
/** Sets a named property. */
|
||||
virtual void setProperty (const Identifier& propertyName, const var& newValue);
|
||||
|
||||
/** Removes a named property. */
|
||||
virtual void removeProperty (const Identifier& propertyName);
|
||||
|
||||
//==============================================================================
|
||||
/** Checks whether this object has the specified method.
|
||||
|
||||
The default implementation of this just checks whether there's a property
|
||||
with this name that's actually a method, but this can be overridden for
|
||||
building objects with dynamic invocation.
|
||||
*/
|
||||
virtual bool hasMethod (const Identifier& methodName) const;
|
||||
|
||||
/** Invokes a named method on this object.
|
||||
|
||||
The default implementation looks up the named property, and if it's a method
|
||||
call, then it invokes it.
|
||||
|
||||
This method is virtual to allow more dynamic invocation to used for objects
|
||||
where the methods may not already be set as properties.
|
||||
*/
|
||||
virtual var invokeMethod (Identifier methodName,
|
||||
const var::NativeFunctionArgs& args);
|
||||
|
||||
/** Adds a method to the class.
|
||||
|
||||
This is basically the same as calling setProperty (methodName, (var::NativeFunction) myFunction), but
|
||||
helps to avoid accidentally invoking the wrong type of var constructor. It also makes
|
||||
the code easier to read.
|
||||
*/
|
||||
void setMethod (Identifier methodName, var::NativeFunction function);
|
||||
|
||||
//==============================================================================
|
||||
/** Removes all properties and methods from the object. */
|
||||
void clear();
|
||||
|
||||
/** Returns the NamedValueSet that holds the object's properties. */
|
||||
NamedValueSet& getProperties() noexcept { return properties; }
|
||||
|
||||
/** Calls var::clone() on all the properties that this object contains. */
|
||||
void cloneAllProperties();
|
||||
|
||||
//==============================================================================
|
||||
/** Returns a clone of this object.
|
||||
The default implementation of this method just returns a new DynamicObject
|
||||
with a (deep) copy of all of its properties. Subclasses can override this to
|
||||
implement their own custom copy routines.
|
||||
*/
|
||||
virtual Ptr clone();
|
||||
|
||||
//==============================================================================
|
||||
/** Writes this object to a text stream in JSON format.
|
||||
This method is used by JSON::toString and JSON::writeToStream, and you should
|
||||
never need to call it directly, but it's virtual so that custom object types
|
||||
can stringify themselves appropriately.
|
||||
*/
|
||||
virtual void writeAsJSON (OutputStream&, int indentLevel, bool allOnOneLine, int maximumDecimalPlaces);
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
NamedValueSet properties;
|
||||
|
||||
JUCE_LEAK_DETECTOR (DynamicObject)
|
||||
};
|
||||
|
||||
} // namespace juce
|
||||
|
@ -1,197 +1,199 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
#ifndef DOXYGEN
|
||||
|
||||
/** This is an internal helper class which converts a juce ElementComparator style
|
||||
class (using a "compareElements" method) into a class that's compatible with
|
||||
std::sort (i.e. using an operator() to compare the elements)
|
||||
|
||||
@tags{Core}
|
||||
*/
|
||||
template <typename ElementComparator>
|
||||
struct SortFunctionConverter
|
||||
{
|
||||
SortFunctionConverter (ElementComparator& e) : comparator (e) {}
|
||||
|
||||
template <typename Type>
|
||||
bool operator() (Type a, Type b) { return comparator.compareElements (a, b) < 0; }
|
||||
|
||||
private:
|
||||
ElementComparator& comparator;
|
||||
SortFunctionConverter& operator= (const SortFunctionConverter&) = delete;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Sorts a range of elements in an array.
|
||||
|
||||
The comparator object that is passed-in must define a public method with the following
|
||||
signature:
|
||||
@code
|
||||
int compareElements (ElementType first, ElementType second);
|
||||
@endcode
|
||||
|
||||
..and this method must return:
|
||||
- a value of < 0 if the first comes before the second
|
||||
- a value of 0 if the two objects are equivalent
|
||||
- a value of > 0 if the second comes before the first
|
||||
|
||||
To improve performance, the compareElements() method can be declared as static or const.
|
||||
|
||||
@param comparator an object which defines a compareElements() method
|
||||
@param array the array to sort
|
||||
@param firstElement the index of the first element of the range to be sorted
|
||||
@param lastElement the index of the last element in the range that needs
|
||||
sorting (this is inclusive)
|
||||
@param retainOrderOfEquivalentItems if true, the order of items that the
|
||||
comparator deems the same will be maintained - this will be
|
||||
a slower algorithm than if they are allowed to be moved around.
|
||||
|
||||
@see sortArrayRetainingOrder
|
||||
*/
|
||||
template <class ElementType, class ElementComparator>
|
||||
static void sortArray (ElementComparator& comparator,
|
||||
ElementType* const array,
|
||||
int firstElement,
|
||||
int lastElement,
|
||||
const bool retainOrderOfEquivalentItems)
|
||||
{
|
||||
jassert (firstElement >= 0);
|
||||
|
||||
if (lastElement > firstElement)
|
||||
{
|
||||
SortFunctionConverter<ElementComparator> converter (comparator);
|
||||
|
||||
if (retainOrderOfEquivalentItems)
|
||||
std::stable_sort (array + firstElement, array + lastElement + 1, converter);
|
||||
else
|
||||
std::sort (array + firstElement, array + lastElement + 1, converter);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Searches a sorted array of elements, looking for the index at which a specified value
|
||||
should be inserted for it to be in the correct order.
|
||||
|
||||
The comparator object that is passed-in must define a public method with the following
|
||||
signature:
|
||||
@code
|
||||
int compareElements (ElementType first, ElementType second);
|
||||
@endcode
|
||||
|
||||
..and this method must return:
|
||||
- a value of < 0 if the first comes before the second
|
||||
- a value of 0 if the two objects are equivalent
|
||||
- a value of > 0 if the second comes before the first
|
||||
|
||||
To improve performance, the compareElements() method can be declared as static or const.
|
||||
|
||||
@param comparator an object which defines a compareElements() method
|
||||
@param array the array to search
|
||||
@param newElement the value that is going to be inserted
|
||||
@param firstElement the index of the first element to search
|
||||
@param lastElement the index of the last element in the range (this is non-inclusive)
|
||||
*/
|
||||
template <class ElementType, class ElementComparator>
|
||||
static int findInsertIndexInSortedArray (ElementComparator& comparator,
|
||||
ElementType* const array,
|
||||
const ElementType newElement,
|
||||
int firstElement,
|
||||
int lastElement)
|
||||
{
|
||||
jassert (firstElement <= lastElement);
|
||||
|
||||
ignoreUnused (comparator); // if you pass in an object with a static compareElements() method, this
|
||||
// avoids getting warning messages about the parameter being unused
|
||||
|
||||
while (firstElement < lastElement)
|
||||
{
|
||||
if (comparator.compareElements (newElement, array [firstElement]) == 0)
|
||||
{
|
||||
++firstElement;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
const int halfway = (firstElement + lastElement) >> 1;
|
||||
|
||||
if (halfway == firstElement)
|
||||
{
|
||||
if (comparator.compareElements (newElement, array [halfway]) >= 0)
|
||||
++firstElement;
|
||||
|
||||
break;
|
||||
}
|
||||
else if (comparator.compareElements (newElement, array [halfway]) >= 0)
|
||||
{
|
||||
firstElement = halfway;
|
||||
}
|
||||
else
|
||||
{
|
||||
lastElement = halfway;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return firstElement;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A simple ElementComparator class that can be used to sort an array of
|
||||
objects that support the '<' operator.
|
||||
|
||||
This will work for primitive types and objects that implement operator<().
|
||||
|
||||
Example: @code
|
||||
Array<int> myArray;
|
||||
DefaultElementComparator<int> sorter;
|
||||
myArray.sort (sorter);
|
||||
@endcode
|
||||
|
||||
@see ElementComparator
|
||||
|
||||
@tags{Core}
|
||||
*/
|
||||
template <class ElementType>
|
||||
class DefaultElementComparator
|
||||
{
|
||||
private:
|
||||
using ParameterType = typename TypeHelpers::ParameterType<ElementType>::type;
|
||||
|
||||
public:
|
||||
static int compareElements (ParameterType first, ParameterType second)
|
||||
{
|
||||
return (first < second) ? -1 : ((second < first) ? 1 : 0);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace juce
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
#ifndef DOXYGEN
|
||||
|
||||
/** This is an internal helper class which converts a juce ElementComparator style
|
||||
class (using a "compareElements" method) into a class that's compatible with
|
||||
std::sort (i.e. using an operator() to compare the elements)
|
||||
|
||||
@tags{Core}
|
||||
*/
|
||||
template <typename ElementComparator>
|
||||
struct SortFunctionConverter
|
||||
{
|
||||
SortFunctionConverter (ElementComparator& e) : comparator (e) {}
|
||||
SortFunctionConverter (const SortFunctionConverter&) = default;
|
||||
|
||||
template <typename Type>
|
||||
bool operator() (Type a, Type b) { return comparator.compareElements (a, b) < 0; }
|
||||
|
||||
private:
|
||||
ElementComparator& comparator;
|
||||
|
||||
SortFunctionConverter& operator= (const SortFunctionConverter&) = delete;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Sorts a range of elements in an array.
|
||||
|
||||
The comparator object that is passed-in must define a public method with the following
|
||||
signature:
|
||||
@code
|
||||
int compareElements (ElementType first, ElementType second);
|
||||
@endcode
|
||||
|
||||
..and this method must return:
|
||||
- a value of < 0 if the first comes before the second
|
||||
- a value of 0 if the two objects are equivalent
|
||||
- a value of > 0 if the second comes before the first
|
||||
|
||||
To improve performance, the compareElements() method can be declared as static or const.
|
||||
|
||||
@param comparator an object which defines a compareElements() method
|
||||
@param array the array to sort
|
||||
@param firstElement the index of the first element of the range to be sorted
|
||||
@param lastElement the index of the last element in the range that needs
|
||||
sorting (this is inclusive)
|
||||
@param retainOrderOfEquivalentItems if true, the order of items that the
|
||||
comparator deems the same will be maintained - this will be
|
||||
a slower algorithm than if they are allowed to be moved around.
|
||||
|
||||
@see sortArrayRetainingOrder
|
||||
*/
|
||||
template <class ElementType, class ElementComparator>
|
||||
static void sortArray (ElementComparator& comparator,
|
||||
ElementType* const array,
|
||||
int firstElement,
|
||||
int lastElement,
|
||||
const bool retainOrderOfEquivalentItems)
|
||||
{
|
||||
jassert (firstElement >= 0);
|
||||
|
||||
if (lastElement > firstElement)
|
||||
{
|
||||
SortFunctionConverter<ElementComparator> converter (comparator);
|
||||
|
||||
if (retainOrderOfEquivalentItems)
|
||||
std::stable_sort (array + firstElement, array + lastElement + 1, converter);
|
||||
else
|
||||
std::sort (array + firstElement, array + lastElement + 1, converter);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Searches a sorted array of elements, looking for the index at which a specified value
|
||||
should be inserted for it to be in the correct order.
|
||||
|
||||
The comparator object that is passed-in must define a public method with the following
|
||||
signature:
|
||||
@code
|
||||
int compareElements (ElementType first, ElementType second);
|
||||
@endcode
|
||||
|
||||
..and this method must return:
|
||||
- a value of < 0 if the first comes before the second
|
||||
- a value of 0 if the two objects are equivalent
|
||||
- a value of > 0 if the second comes before the first
|
||||
|
||||
To improve performance, the compareElements() method can be declared as static or const.
|
||||
|
||||
@param comparator an object which defines a compareElements() method
|
||||
@param array the array to search
|
||||
@param newElement the value that is going to be inserted
|
||||
@param firstElement the index of the first element to search
|
||||
@param lastElement the index of the last element in the range (this is non-inclusive)
|
||||
*/
|
||||
template <class ElementType, class ElementComparator>
|
||||
static int findInsertIndexInSortedArray (ElementComparator& comparator,
|
||||
ElementType* const array,
|
||||
const ElementType newElement,
|
||||
int firstElement,
|
||||
int lastElement)
|
||||
{
|
||||
jassert (firstElement <= lastElement);
|
||||
|
||||
ignoreUnused (comparator); // if you pass in an object with a static compareElements() method, this
|
||||
// avoids getting warning messages about the parameter being unused
|
||||
|
||||
while (firstElement < lastElement)
|
||||
{
|
||||
if (comparator.compareElements (newElement, array [firstElement]) == 0)
|
||||
{
|
||||
++firstElement;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
const int halfway = (firstElement + lastElement) >> 1;
|
||||
|
||||
if (halfway == firstElement)
|
||||
{
|
||||
if (comparator.compareElements (newElement, array [halfway]) >= 0)
|
||||
++firstElement;
|
||||
|
||||
break;
|
||||
}
|
||||
else if (comparator.compareElements (newElement, array [halfway]) >= 0)
|
||||
{
|
||||
firstElement = halfway;
|
||||
}
|
||||
else
|
||||
{
|
||||
lastElement = halfway;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return firstElement;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A simple ElementComparator class that can be used to sort an array of
|
||||
objects that support the '<' operator.
|
||||
|
||||
This will work for primitive types and objects that implement operator<().
|
||||
|
||||
Example: @code
|
||||
Array<int> myArray;
|
||||
DefaultElementComparator<int> sorter;
|
||||
myArray.sort (sorter);
|
||||
@endcode
|
||||
|
||||
@see ElementComparator
|
||||
|
||||
@tags{Core}
|
||||
*/
|
||||
template <class ElementType>
|
||||
class DefaultElementComparator
|
||||
{
|
||||
private:
|
||||
using ParameterType = typename TypeHelpers::ParameterType<ElementType>::type;
|
||||
|
||||
public:
|
||||
static int compareElements (ParameterType first, ParameterType second)
|
||||
{
|
||||
return (first < second) ? -1 : ((second < first) ? 1 : 0);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace juce
|
||||
|
1012
deps/juce/modules/juce_core/containers/juce_HashMap.h
vendored
1012
deps/juce/modules/juce_core/containers/juce_HashMap.h
vendored
File diff suppressed because it is too large
Load Diff
@ -1,278 +1,278 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
struct HashMapTest : public UnitTest
|
||||
{
|
||||
HashMapTest()
|
||||
: UnitTest ("HashMap", UnitTestCategories::containers)
|
||||
{}
|
||||
|
||||
void runTest() override
|
||||
{
|
||||
doTest<AddElementsTest> ("AddElementsTest");
|
||||
doTest<AccessTest> ("AccessTest");
|
||||
doTest<RemoveTest> ("RemoveTest");
|
||||
doTest<PersistantMemoryLocationOfValues> ("PersistantMemoryLocationOfValues");
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
struct AddElementsTest
|
||||
{
|
||||
template <typename KeyType>
|
||||
static void run (UnitTest& u)
|
||||
{
|
||||
AssociativeMap<KeyType, int> groundTruth;
|
||||
HashMap<KeyType, int> hashMap;
|
||||
|
||||
RandomKeys<KeyType> keyOracle (300, 3827829);
|
||||
Random valueOracle (48735);
|
||||
|
||||
int totalValues = 0;
|
||||
for (int i = 0; i < 10000; ++i)
|
||||
{
|
||||
auto key = keyOracle.next();
|
||||
auto value = valueOracle.nextInt();
|
||||
|
||||
bool contains = (groundTruth.find (key) != nullptr);
|
||||
u.expectEquals ((int) contains, (int) hashMap.contains (key));
|
||||
|
||||
groundTruth.add (key, value);
|
||||
hashMap.set (key, value);
|
||||
|
||||
if (! contains) totalValues++;
|
||||
|
||||
u.expectEquals (hashMap.size(), totalValues);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct AccessTest
|
||||
{
|
||||
template <typename KeyType>
|
||||
static void run (UnitTest& u)
|
||||
{
|
||||
AssociativeMap<KeyType, int> groundTruth;
|
||||
HashMap<KeyType, int> hashMap;
|
||||
|
||||
fillWithRandomValues (hashMap, groundTruth);
|
||||
|
||||
for (auto pair : groundTruth.pairs)
|
||||
u.expectEquals (hashMap[pair.key], pair.value);
|
||||
}
|
||||
};
|
||||
|
||||
struct RemoveTest
|
||||
{
|
||||
template <typename KeyType>
|
||||
static void run (UnitTest& u)
|
||||
{
|
||||
AssociativeMap<KeyType, int> groundTruth;
|
||||
HashMap<KeyType, int> hashMap;
|
||||
|
||||
fillWithRandomValues (hashMap, groundTruth);
|
||||
auto n = groundTruth.size();
|
||||
|
||||
Random r (3827387);
|
||||
|
||||
for (int i = 0; i < 100; ++i)
|
||||
{
|
||||
auto idx = r.nextInt (n-- - 1);
|
||||
auto key = groundTruth.pairs.getReference (idx).key;
|
||||
|
||||
groundTruth.pairs.remove (idx);
|
||||
hashMap.remove (key);
|
||||
|
||||
u.expect (! hashMap.contains (key));
|
||||
|
||||
for (auto pair : groundTruth.pairs)
|
||||
u.expectEquals (hashMap[pair.key], pair.value);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// ensure that the addresses of object references don't change
|
||||
struct PersistantMemoryLocationOfValues
|
||||
{
|
||||
struct AddressAndValue { int value; const int* valueAddress; };
|
||||
|
||||
template <typename KeyType>
|
||||
static void run (UnitTest& u)
|
||||
{
|
||||
AssociativeMap<KeyType, AddressAndValue> groundTruth;
|
||||
HashMap<KeyType, int> hashMap;
|
||||
|
||||
RandomKeys<KeyType> keyOracle (300, 3827829);
|
||||
Random valueOracle (48735);
|
||||
|
||||
for (int i = 0; i < 1000; ++i)
|
||||
{
|
||||
auto key = keyOracle.next();
|
||||
auto value = valueOracle.nextInt();
|
||||
|
||||
hashMap.set (key, value);
|
||||
|
||||
if (auto* existing = groundTruth.find (key))
|
||||
{
|
||||
// don't change the address: only the value
|
||||
existing->value = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
groundTruth.add (key, { value, &hashMap.getReference (key) });
|
||||
}
|
||||
|
||||
for (auto pair : groundTruth.pairs)
|
||||
{
|
||||
const auto& hashMapValue = hashMap.getReference (pair.key);
|
||||
|
||||
u.expectEquals (hashMapValue, pair.value.value);
|
||||
u.expect (&hashMapValue == pair.value.valueAddress);
|
||||
}
|
||||
}
|
||||
|
||||
auto n = groundTruth.size();
|
||||
Random r (3827387);
|
||||
|
||||
for (int i = 0; i < 100; ++i)
|
||||
{
|
||||
auto idx = r.nextInt (n-- - 1);
|
||||
auto key = groundTruth.pairs.getReference (idx).key;
|
||||
|
||||
groundTruth.pairs.remove (idx);
|
||||
hashMap.remove (key);
|
||||
|
||||
for (auto pair : groundTruth.pairs)
|
||||
{
|
||||
const auto& hashMapValue = hashMap.getReference (pair.key);
|
||||
|
||||
u.expectEquals (hashMapValue, pair.value.value);
|
||||
u.expect (&hashMapValue == pair.value.valueAddress);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
template <class Test>
|
||||
void doTest (const String& testName)
|
||||
{
|
||||
beginTest (testName);
|
||||
|
||||
Test::template run<int> (*this);
|
||||
Test::template run<void*> (*this);
|
||||
Test::template run<String> (*this);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
template <typename KeyType, typename ValueType>
|
||||
struct AssociativeMap
|
||||
{
|
||||
struct KeyValuePair { KeyType key; ValueType value; };
|
||||
|
||||
ValueType* find (KeyType key)
|
||||
{
|
||||
auto n = pairs.size();
|
||||
|
||||
for (int i = 0; i < n; ++i)
|
||||
{
|
||||
auto& pair = pairs.getReference (i);
|
||||
|
||||
if (pair.key == key)
|
||||
return &pair.value;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void add (KeyType key, ValueType value)
|
||||
{
|
||||
if (ValueType* v = find (key))
|
||||
*v = value;
|
||||
else
|
||||
pairs.add ({key, value});
|
||||
}
|
||||
|
||||
int size() const { return pairs.size(); }
|
||||
|
||||
Array<KeyValuePair> pairs;
|
||||
};
|
||||
|
||||
template <typename KeyType, typename ValueType>
|
||||
static void fillWithRandomValues (HashMap<KeyType, int>& hashMap, AssociativeMap<KeyType, ValueType>& groundTruth)
|
||||
{
|
||||
RandomKeys<KeyType> keyOracle (300, 3827829);
|
||||
Random valueOracle (48735);
|
||||
|
||||
for (int i = 0; i < 10000; ++i)
|
||||
{
|
||||
auto key = keyOracle.next();
|
||||
auto value = valueOracle.nextInt();
|
||||
|
||||
groundTruth.add (key, value);
|
||||
hashMap.set (key, value);
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
template <typename KeyType>
|
||||
class RandomKeys
|
||||
{
|
||||
public:
|
||||
RandomKeys (int maxUniqueKeys, int seed) : r (seed)
|
||||
{
|
||||
for (int i = 0; i < maxUniqueKeys; ++i)
|
||||
keys.add (generateRandomKey (r));
|
||||
}
|
||||
|
||||
const KeyType& next()
|
||||
{
|
||||
int i = r.nextInt (keys.size() - 1);
|
||||
return keys.getReference (i);
|
||||
}
|
||||
private:
|
||||
static KeyType generateRandomKey (Random&);
|
||||
|
||||
Random r;
|
||||
Array<KeyType> keys;
|
||||
};
|
||||
};
|
||||
|
||||
template <> int HashMapTest::RandomKeys<int> ::generateRandomKey (Random& rnd) { return rnd.nextInt(); }
|
||||
template <> void* HashMapTest::RandomKeys<void*>::generateRandomKey (Random& rnd) { return reinterpret_cast<void*> (rnd.nextInt64()); }
|
||||
|
||||
template <> String HashMapTest::RandomKeys<String>::generateRandomKey (Random& rnd)
|
||||
{
|
||||
String str;
|
||||
|
||||
int len = rnd.nextInt (8)+1;
|
||||
for (int i = 0; i < len; ++i)
|
||||
str += static_cast<char> (rnd.nextInt (95) + 32);
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
static HashMapTest hashMapTest;
|
||||
|
||||
} // namespace juce
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
struct HashMapTest : public UnitTest
|
||||
{
|
||||
HashMapTest()
|
||||
: UnitTest ("HashMap", UnitTestCategories::containers)
|
||||
{}
|
||||
|
||||
void runTest() override
|
||||
{
|
||||
doTest<AddElementsTest> ("AddElementsTest");
|
||||
doTest<AccessTest> ("AccessTest");
|
||||
doTest<RemoveTest> ("RemoveTest");
|
||||
doTest<PersistantMemoryLocationOfValues> ("PersistantMemoryLocationOfValues");
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
struct AddElementsTest
|
||||
{
|
||||
template <typename KeyType>
|
||||
static void run (UnitTest& u)
|
||||
{
|
||||
AssociativeMap<KeyType, int> groundTruth;
|
||||
HashMap<KeyType, int> hashMap;
|
||||
|
||||
RandomKeys<KeyType> keyOracle (300, 3827829);
|
||||
Random valueOracle (48735);
|
||||
|
||||
int totalValues = 0;
|
||||
for (int i = 0; i < 10000; ++i)
|
||||
{
|
||||
auto key = keyOracle.next();
|
||||
auto value = valueOracle.nextInt();
|
||||
|
||||
bool contains = (groundTruth.find (key) != nullptr);
|
||||
u.expectEquals ((int) contains, (int) hashMap.contains (key));
|
||||
|
||||
groundTruth.add (key, value);
|
||||
hashMap.set (key, value);
|
||||
|
||||
if (! contains) totalValues++;
|
||||
|
||||
u.expectEquals (hashMap.size(), totalValues);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct AccessTest
|
||||
{
|
||||
template <typename KeyType>
|
||||
static void run (UnitTest& u)
|
||||
{
|
||||
AssociativeMap<KeyType, int> groundTruth;
|
||||
HashMap<KeyType, int> hashMap;
|
||||
|
||||
fillWithRandomValues (hashMap, groundTruth);
|
||||
|
||||
for (auto pair : groundTruth.pairs)
|
||||
u.expectEquals (hashMap[pair.key], pair.value);
|
||||
}
|
||||
};
|
||||
|
||||
struct RemoveTest
|
||||
{
|
||||
template <typename KeyType>
|
||||
static void run (UnitTest& u)
|
||||
{
|
||||
AssociativeMap<KeyType, int> groundTruth;
|
||||
HashMap<KeyType, int> hashMap;
|
||||
|
||||
fillWithRandomValues (hashMap, groundTruth);
|
||||
auto n = groundTruth.size();
|
||||
|
||||
Random r (3827387);
|
||||
|
||||
for (int i = 0; i < 100; ++i)
|
||||
{
|
||||
auto idx = r.nextInt (n-- - 1);
|
||||
auto key = groundTruth.pairs.getReference (idx).key;
|
||||
|
||||
groundTruth.pairs.remove (idx);
|
||||
hashMap.remove (key);
|
||||
|
||||
u.expect (! hashMap.contains (key));
|
||||
|
||||
for (auto pair : groundTruth.pairs)
|
||||
u.expectEquals (hashMap[pair.key], pair.value);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// ensure that the addresses of object references don't change
|
||||
struct PersistantMemoryLocationOfValues
|
||||
{
|
||||
struct AddressAndValue { int value; const int* valueAddress; };
|
||||
|
||||
template <typename KeyType>
|
||||
static void run (UnitTest& u)
|
||||
{
|
||||
AssociativeMap<KeyType, AddressAndValue> groundTruth;
|
||||
HashMap<KeyType, int> hashMap;
|
||||
|
||||
RandomKeys<KeyType> keyOracle (300, 3827829);
|
||||
Random valueOracle (48735);
|
||||
|
||||
for (int i = 0; i < 1000; ++i)
|
||||
{
|
||||
auto key = keyOracle.next();
|
||||
auto value = valueOracle.nextInt();
|
||||
|
||||
hashMap.set (key, value);
|
||||
|
||||
if (auto* existing = groundTruth.find (key))
|
||||
{
|
||||
// don't change the address: only the value
|
||||
existing->value = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
groundTruth.add (key, { value, &hashMap.getReference (key) });
|
||||
}
|
||||
|
||||
for (auto pair : groundTruth.pairs)
|
||||
{
|
||||
const auto& hashMapValue = hashMap.getReference (pair.key);
|
||||
|
||||
u.expectEquals (hashMapValue, pair.value.value);
|
||||
u.expect (&hashMapValue == pair.value.valueAddress);
|
||||
}
|
||||
}
|
||||
|
||||
auto n = groundTruth.size();
|
||||
Random r (3827387);
|
||||
|
||||
for (int i = 0; i < 100; ++i)
|
||||
{
|
||||
auto idx = r.nextInt (n-- - 1);
|
||||
auto key = groundTruth.pairs.getReference (idx).key;
|
||||
|
||||
groundTruth.pairs.remove (idx);
|
||||
hashMap.remove (key);
|
||||
|
||||
for (auto pair : groundTruth.pairs)
|
||||
{
|
||||
const auto& hashMapValue = hashMap.getReference (pair.key);
|
||||
|
||||
u.expectEquals (hashMapValue, pair.value.value);
|
||||
u.expect (&hashMapValue == pair.value.valueAddress);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
template <class Test>
|
||||
void doTest (const String& testName)
|
||||
{
|
||||
beginTest (testName);
|
||||
|
||||
Test::template run<int> (*this);
|
||||
Test::template run<void*> (*this);
|
||||
Test::template run<String> (*this);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
template <typename KeyType, typename ValueType>
|
||||
struct AssociativeMap
|
||||
{
|
||||
struct KeyValuePair { KeyType key; ValueType value; };
|
||||
|
||||
ValueType* find (KeyType key)
|
||||
{
|
||||
auto n = pairs.size();
|
||||
|
||||
for (int i = 0; i < n; ++i)
|
||||
{
|
||||
auto& pair = pairs.getReference (i);
|
||||
|
||||
if (pair.key == key)
|
||||
return &pair.value;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void add (KeyType key, ValueType value)
|
||||
{
|
||||
if (ValueType* v = find (key))
|
||||
*v = value;
|
||||
else
|
||||
pairs.add ({key, value});
|
||||
}
|
||||
|
||||
int size() const { return pairs.size(); }
|
||||
|
||||
Array<KeyValuePair> pairs;
|
||||
};
|
||||
|
||||
template <typename KeyType, typename ValueType>
|
||||
static void fillWithRandomValues (HashMap<KeyType, int>& hashMap, AssociativeMap<KeyType, ValueType>& groundTruth)
|
||||
{
|
||||
RandomKeys<KeyType> keyOracle (300, 3827829);
|
||||
Random valueOracle (48735);
|
||||
|
||||
for (int i = 0; i < 10000; ++i)
|
||||
{
|
||||
auto key = keyOracle.next();
|
||||
auto value = valueOracle.nextInt();
|
||||
|
||||
groundTruth.add (key, value);
|
||||
hashMap.set (key, value);
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
template <typename KeyType>
|
||||
class RandomKeys
|
||||
{
|
||||
public:
|
||||
RandomKeys (int maxUniqueKeys, int seed) : r (seed)
|
||||
{
|
||||
for (int i = 0; i < maxUniqueKeys; ++i)
|
||||
keys.add (generateRandomKey (r));
|
||||
}
|
||||
|
||||
const KeyType& next()
|
||||
{
|
||||
int i = r.nextInt (keys.size() - 1);
|
||||
return keys.getReference (i);
|
||||
}
|
||||
private:
|
||||
static KeyType generateRandomKey (Random&);
|
||||
|
||||
Random r;
|
||||
Array<KeyType> keys;
|
||||
};
|
||||
};
|
||||
|
||||
template <> int HashMapTest::RandomKeys<int> ::generateRandomKey (Random& rnd) { return rnd.nextInt(); }
|
||||
template <> void* HashMapTest::RandomKeys<void*>::generateRandomKey (Random& rnd) { return reinterpret_cast<void*> (rnd.nextInt64()); }
|
||||
|
||||
template <> String HashMapTest::RandomKeys<String>::generateRandomKey (Random& rnd)
|
||||
{
|
||||
String str;
|
||||
|
||||
int len = rnd.nextInt (8)+1;
|
||||
for (int i = 0; i < len; ++i)
|
||||
str += static_cast<char> (rnd.nextInt (95) + 32);
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
static HashMapTest hashMapTest;
|
||||
|
||||
} // namespace juce
|
||||
|
@ -1,370 +1,370 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Helps to manipulate singly-linked lists of objects.
|
||||
|
||||
For objects that are designed to contain a pointer to the subsequent item in the
|
||||
list, this class contains methods to deal with the list. To use it, the ObjectType
|
||||
class that it points to must contain a LinkedListPointer called nextListItem, e.g.
|
||||
|
||||
@code
|
||||
struct MyObject
|
||||
{
|
||||
int x, y, z;
|
||||
|
||||
// A linkable object must contain a member with this name and type, which must be
|
||||
// accessible by the LinkedListPointer class. (This doesn't mean it has to be public -
|
||||
// you could make your class a friend of a LinkedListPointer<MyObject> instead).
|
||||
LinkedListPointer<MyObject> nextListItem;
|
||||
};
|
||||
|
||||
LinkedListPointer<MyObject> myList;
|
||||
myList.append (new MyObject());
|
||||
myList.append (new MyObject());
|
||||
|
||||
int numItems = myList.size(); // returns 2
|
||||
MyObject* lastInList = myList.getLast();
|
||||
@endcode
|
||||
|
||||
@tags{Core}
|
||||
*/
|
||||
template <class ObjectType>
|
||||
class LinkedListPointer
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates a null pointer to an empty list. */
|
||||
LinkedListPointer() noexcept
|
||||
: item (nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
/** Creates a pointer to a list whose head is the item provided. */
|
||||
explicit LinkedListPointer (ObjectType* const headItem) noexcept
|
||||
: item (headItem)
|
||||
{
|
||||
}
|
||||
|
||||
/** Sets this pointer to point to a new list. */
|
||||
LinkedListPointer& operator= (ObjectType* const newItem) noexcept
|
||||
{
|
||||
item = newItem;
|
||||
return *this;
|
||||
}
|
||||
|
||||
LinkedListPointer (LinkedListPointer&& other) noexcept
|
||||
: item (other.item)
|
||||
{
|
||||
other.item = nullptr;
|
||||
}
|
||||
|
||||
LinkedListPointer& operator= (LinkedListPointer&& other) noexcept
|
||||
{
|
||||
jassert (this != &other); // hopefully the compiler should make this situation impossible!
|
||||
|
||||
item = other.item;
|
||||
other.item = nullptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the item which this pointer points to. */
|
||||
inline operator ObjectType*() const noexcept
|
||||
{
|
||||
return item;
|
||||
}
|
||||
|
||||
/** Returns the item which this pointer points to. */
|
||||
inline ObjectType* get() const noexcept
|
||||
{
|
||||
return item;
|
||||
}
|
||||
|
||||
/** Returns the last item in the list which this pointer points to.
|
||||
This will iterate the list and return the last item found. Obviously the speed
|
||||
of this operation will be proportional to the size of the list. If the list is
|
||||
empty the return value will be this object.
|
||||
If you're planning on appending a number of items to your list, it's much more
|
||||
efficient to use the Appender class than to repeatedly call getLast() to find the end.
|
||||
*/
|
||||
LinkedListPointer& getLast() noexcept
|
||||
{
|
||||
auto* l = this;
|
||||
|
||||
while (l->item != nullptr)
|
||||
l = &(l->item->nextListItem);
|
||||
|
||||
return *l;
|
||||
}
|
||||
|
||||
/** Returns the number of items in the list.
|
||||
Obviously with a simple linked list, getting the size involves iterating the list, so
|
||||
this can be a lengthy operation - be careful when using this method in your code.
|
||||
*/
|
||||
int size() const noexcept
|
||||
{
|
||||
int total = 0;
|
||||
|
||||
for (auto* i = item; i != nullptr; i = i->nextListItem)
|
||||
++total;
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
/** Returns the item at a given index in the list.
|
||||
Since the only way to find an item is to iterate the list, this operation can obviously
|
||||
be slow, depending on its size, so you should be careful when using this in algorithms.
|
||||
*/
|
||||
LinkedListPointer& operator[] (int index) noexcept
|
||||
{
|
||||
auto* l = this;
|
||||
|
||||
while (--index >= 0 && l->item != nullptr)
|
||||
l = &(l->item->nextListItem);
|
||||
|
||||
return *l;
|
||||
}
|
||||
|
||||
/** Returns the item at a given index in the list.
|
||||
Since the only way to find an item is to iterate the list, this operation can obviously
|
||||
be slow, depending on its size, so you should be careful when using this in algorithms.
|
||||
*/
|
||||
const LinkedListPointer& operator[] (int index) const noexcept
|
||||
{
|
||||
auto* l = this;
|
||||
|
||||
while (--index >= 0 && l->item != nullptr)
|
||||
l = &(l->item->nextListItem);
|
||||
|
||||
return *l;
|
||||
}
|
||||
|
||||
/** Returns true if the list contains the given item. */
|
||||
bool contains (const ObjectType* const itemToLookFor) const noexcept
|
||||
{
|
||||
for (auto* i = item; i != nullptr; i = i->nextListItem)
|
||||
if (itemToLookFor == i)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Inserts an item into the list, placing it before the item that this pointer
|
||||
currently points to.
|
||||
*/
|
||||
void insertNext (ObjectType* const newItem)
|
||||
{
|
||||
JUCE_BEGIN_IGNORE_WARNINGS_MSVC (6011)
|
||||
jassert (newItem != nullptr);
|
||||
jassert (newItem->nextListItem == nullptr);
|
||||
newItem->nextListItem = item;
|
||||
item = newItem;
|
||||
JUCE_END_IGNORE_WARNINGS_MSVC
|
||||
}
|
||||
|
||||
/** Inserts an item at a numeric index in the list.
|
||||
Obviously this will involve iterating the list to find the item at the given index,
|
||||
so be careful about the impact this may have on execution time.
|
||||
*/
|
||||
void insertAtIndex (int index, ObjectType* newItem)
|
||||
{
|
||||
jassert (newItem != nullptr);
|
||||
auto* l = this;
|
||||
|
||||
while (index != 0 && l->item != nullptr)
|
||||
{
|
||||
l = &(l->item->nextListItem);
|
||||
--index;
|
||||
}
|
||||
|
||||
l->insertNext (newItem);
|
||||
}
|
||||
|
||||
/** Replaces the object that this pointer points to, appending the rest of the list to
|
||||
the new object, and returning the old one.
|
||||
*/
|
||||
ObjectType* replaceNext (ObjectType* const newItem) noexcept
|
||||
{
|
||||
JUCE_BEGIN_IGNORE_WARNINGS_MSVC (6011 28182)
|
||||
jassert (newItem != nullptr);
|
||||
jassert (newItem->nextListItem == nullptr);
|
||||
|
||||
auto oldItem = item;
|
||||
item = newItem;
|
||||
item->nextListItem = oldItem->nextListItem.item;
|
||||
oldItem->nextListItem.item = nullptr;
|
||||
return oldItem;
|
||||
JUCE_END_IGNORE_WARNINGS_MSVC
|
||||
}
|
||||
|
||||
/** Adds an item to the end of the list.
|
||||
|
||||
This operation involves iterating the whole list, so can be slow - if you need to
|
||||
append a number of items to your list, it's much more efficient to use the Appender
|
||||
class than to repeatedly call append().
|
||||
*/
|
||||
void append (ObjectType* const newItem)
|
||||
{
|
||||
getLast().item = newItem;
|
||||
}
|
||||
|
||||
/** Creates copies of all the items in another list and adds them to this one.
|
||||
This will use the ObjectType's copy constructor to try to create copies of each
|
||||
item in the other list, and appends them to this list.
|
||||
*/
|
||||
void addCopyOfList (const LinkedListPointer& other)
|
||||
{
|
||||
auto* insertPoint = this;
|
||||
|
||||
for (auto* i = other.item; i != nullptr; i = i->nextListItem)
|
||||
{
|
||||
insertPoint->insertNext (new ObjectType (*i));
|
||||
insertPoint = &(insertPoint->item->nextListItem);
|
||||
}
|
||||
}
|
||||
|
||||
/** Removes the head item from the list.
|
||||
This won't delete the object that is removed, but returns it, so the caller can
|
||||
delete it if necessary.
|
||||
*/
|
||||
ObjectType* removeNext() noexcept
|
||||
{
|
||||
auto oldItem = item;
|
||||
|
||||
if (oldItem != nullptr)
|
||||
{
|
||||
item = oldItem->nextListItem;
|
||||
oldItem->nextListItem.item = nullptr;
|
||||
}
|
||||
|
||||
return oldItem;
|
||||
}
|
||||
|
||||
/** Removes a specific item from the list.
|
||||
Note that this will not delete the item, it simply unlinks it from the list.
|
||||
*/
|
||||
void remove (ObjectType* const itemToRemove)
|
||||
{
|
||||
if (auto* l = findPointerTo (itemToRemove))
|
||||
l->removeNext();
|
||||
}
|
||||
|
||||
/** Iterates the list, calling the delete operator on all of its elements and
|
||||
leaving this pointer empty.
|
||||
*/
|
||||
void deleteAll()
|
||||
{
|
||||
while (item != nullptr)
|
||||
{
|
||||
auto oldItem = item;
|
||||
item = oldItem->nextListItem;
|
||||
delete oldItem;
|
||||
}
|
||||
}
|
||||
|
||||
/** Finds a pointer to a given item.
|
||||
If the item is found in the list, this returns the pointer that points to it. If
|
||||
the item isn't found, this returns null.
|
||||
*/
|
||||
LinkedListPointer* findPointerTo (ObjectType* const itemToLookFor) noexcept
|
||||
{
|
||||
auto* l = this;
|
||||
|
||||
while (l->item != nullptr)
|
||||
{
|
||||
if (l->item == itemToLookFor)
|
||||
return l;
|
||||
|
||||
l = &(l->item->nextListItem);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/** Copies the items in the list to an array.
|
||||
The destArray must contain enough elements to hold the entire list - no checks are
|
||||
made for this!
|
||||
*/
|
||||
void copyToArray (ObjectType** destArray) const noexcept
|
||||
{
|
||||
JUCE_BEGIN_IGNORE_WARNINGS_MSVC (6011)
|
||||
jassert (destArray != nullptr);
|
||||
|
||||
for (auto* i = item; i != nullptr; i = i->nextListItem)
|
||||
*destArray++ = i;
|
||||
|
||||
JUCE_END_IGNORE_WARNINGS_MSVC
|
||||
}
|
||||
|
||||
/** Swaps this pointer with another one */
|
||||
void swapWith (LinkedListPointer& other) noexcept
|
||||
{
|
||||
std::swap (item, other.item);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Allows efficient repeated insertions into a list.
|
||||
|
||||
You can create an Appender object which points to the last element in your
|
||||
list, and then repeatedly call Appender::append() to add items to the end
|
||||
of the list in O(1) time.
|
||||
*/
|
||||
class Appender
|
||||
{
|
||||
public:
|
||||
/** Creates an appender which will add items to the given list.
|
||||
*/
|
||||
Appender (LinkedListPointer& endOfListPointer) noexcept
|
||||
: endOfList (&endOfListPointer)
|
||||
{
|
||||
// This can only be used to add to the end of a list.
|
||||
jassert (endOfListPointer.item == nullptr);
|
||||
}
|
||||
|
||||
/** Appends an item to the list. */
|
||||
void append (ObjectType* const newItem) noexcept
|
||||
{
|
||||
*endOfList = newItem;
|
||||
endOfList = &(newItem->nextListItem);
|
||||
}
|
||||
|
||||
private:
|
||||
LinkedListPointer* endOfList;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE (Appender)
|
||||
};
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
ObjectType* item;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE (LinkedListPointer)
|
||||
};
|
||||
|
||||
} // namespace juce
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Helps to manipulate singly-linked lists of objects.
|
||||
|
||||
For objects that are designed to contain a pointer to the subsequent item in the
|
||||
list, this class contains methods to deal with the list. To use it, the ObjectType
|
||||
class that it points to must contain a LinkedListPointer called nextListItem, e.g.
|
||||
|
||||
@code
|
||||
struct MyObject
|
||||
{
|
||||
int x, y, z;
|
||||
|
||||
// A linkable object must contain a member with this name and type, which must be
|
||||
// accessible by the LinkedListPointer class. (This doesn't mean it has to be public -
|
||||
// you could make your class a friend of a LinkedListPointer<MyObject> instead).
|
||||
LinkedListPointer<MyObject> nextListItem;
|
||||
};
|
||||
|
||||
LinkedListPointer<MyObject> myList;
|
||||
myList.append (new MyObject());
|
||||
myList.append (new MyObject());
|
||||
|
||||
int numItems = myList.size(); // returns 2
|
||||
MyObject* lastInList = myList.getLast();
|
||||
@endcode
|
||||
|
||||
@tags{Core}
|
||||
*/
|
||||
template <class ObjectType>
|
||||
class LinkedListPointer
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates a null pointer to an empty list. */
|
||||
LinkedListPointer() noexcept
|
||||
: item (nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
/** Creates a pointer to a list whose head is the item provided. */
|
||||
explicit LinkedListPointer (ObjectType* const headItem) noexcept
|
||||
: item (headItem)
|
||||
{
|
||||
}
|
||||
|
||||
/** Sets this pointer to point to a new list. */
|
||||
LinkedListPointer& operator= (ObjectType* const newItem) noexcept
|
||||
{
|
||||
item = newItem;
|
||||
return *this;
|
||||
}
|
||||
|
||||
LinkedListPointer (LinkedListPointer&& other) noexcept
|
||||
: item (other.item)
|
||||
{
|
||||
other.item = nullptr;
|
||||
}
|
||||
|
||||
LinkedListPointer& operator= (LinkedListPointer&& other) noexcept
|
||||
{
|
||||
jassert (this != &other); // hopefully the compiler should make this situation impossible!
|
||||
|
||||
item = other.item;
|
||||
other.item = nullptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the item which this pointer points to. */
|
||||
inline operator ObjectType*() const noexcept
|
||||
{
|
||||
return item;
|
||||
}
|
||||
|
||||
/** Returns the item which this pointer points to. */
|
||||
inline ObjectType* get() const noexcept
|
||||
{
|
||||
return item;
|
||||
}
|
||||
|
||||
/** Returns the last item in the list which this pointer points to.
|
||||
This will iterate the list and return the last item found. Obviously the speed
|
||||
of this operation will be proportional to the size of the list. If the list is
|
||||
empty the return value will be this object.
|
||||
If you're planning on appending a number of items to your list, it's much more
|
||||
efficient to use the Appender class than to repeatedly call getLast() to find the end.
|
||||
*/
|
||||
LinkedListPointer& getLast() noexcept
|
||||
{
|
||||
auto* l = this;
|
||||
|
||||
while (l->item != nullptr)
|
||||
l = &(l->item->nextListItem);
|
||||
|
||||
return *l;
|
||||
}
|
||||
|
||||
/** Returns the number of items in the list.
|
||||
Obviously with a simple linked list, getting the size involves iterating the list, so
|
||||
this can be a lengthy operation - be careful when using this method in your code.
|
||||
*/
|
||||
int size() const noexcept
|
||||
{
|
||||
int total = 0;
|
||||
|
||||
for (auto* i = item; i != nullptr; i = i->nextListItem)
|
||||
++total;
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
/** Returns the item at a given index in the list.
|
||||
Since the only way to find an item is to iterate the list, this operation can obviously
|
||||
be slow, depending on its size, so you should be careful when using this in algorithms.
|
||||
*/
|
||||
LinkedListPointer& operator[] (int index) noexcept
|
||||
{
|
||||
auto* l = this;
|
||||
|
||||
while (--index >= 0 && l->item != nullptr)
|
||||
l = &(l->item->nextListItem);
|
||||
|
||||
return *l;
|
||||
}
|
||||
|
||||
/** Returns the item at a given index in the list.
|
||||
Since the only way to find an item is to iterate the list, this operation can obviously
|
||||
be slow, depending on its size, so you should be careful when using this in algorithms.
|
||||
*/
|
||||
const LinkedListPointer& operator[] (int index) const noexcept
|
||||
{
|
||||
auto* l = this;
|
||||
|
||||
while (--index >= 0 && l->item != nullptr)
|
||||
l = &(l->item->nextListItem);
|
||||
|
||||
return *l;
|
||||
}
|
||||
|
||||
/** Returns true if the list contains the given item. */
|
||||
bool contains (const ObjectType* const itemToLookFor) const noexcept
|
||||
{
|
||||
for (auto* i = item; i != nullptr; i = i->nextListItem)
|
||||
if (itemToLookFor == i)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Inserts an item into the list, placing it before the item that this pointer
|
||||
currently points to.
|
||||
*/
|
||||
void insertNext (ObjectType* const newItem)
|
||||
{
|
||||
JUCE_BEGIN_IGNORE_WARNINGS_MSVC (6011)
|
||||
jassert (newItem != nullptr);
|
||||
jassert (newItem->nextListItem == nullptr);
|
||||
newItem->nextListItem = item;
|
||||
item = newItem;
|
||||
JUCE_END_IGNORE_WARNINGS_MSVC
|
||||
}
|
||||
|
||||
/** Inserts an item at a numeric index in the list.
|
||||
Obviously this will involve iterating the list to find the item at the given index,
|
||||
so be careful about the impact this may have on execution time.
|
||||
*/
|
||||
void insertAtIndex (int index, ObjectType* newItem)
|
||||
{
|
||||
jassert (newItem != nullptr);
|
||||
auto* l = this;
|
||||
|
||||
while (index != 0 && l->item != nullptr)
|
||||
{
|
||||
l = &(l->item->nextListItem);
|
||||
--index;
|
||||
}
|
||||
|
||||
l->insertNext (newItem);
|
||||
}
|
||||
|
||||
/** Replaces the object that this pointer points to, appending the rest of the list to
|
||||
the new object, and returning the old one.
|
||||
*/
|
||||
ObjectType* replaceNext (ObjectType* const newItem) noexcept
|
||||
{
|
||||
JUCE_BEGIN_IGNORE_WARNINGS_MSVC (6011 28182)
|
||||
jassert (newItem != nullptr);
|
||||
jassert (newItem->nextListItem == nullptr);
|
||||
|
||||
auto oldItem = item;
|
||||
item = newItem;
|
||||
item->nextListItem = oldItem->nextListItem.item;
|
||||
oldItem->nextListItem.item = nullptr;
|
||||
return oldItem;
|
||||
JUCE_END_IGNORE_WARNINGS_MSVC
|
||||
}
|
||||
|
||||
/** Adds an item to the end of the list.
|
||||
|
||||
This operation involves iterating the whole list, so can be slow - if you need to
|
||||
append a number of items to your list, it's much more efficient to use the Appender
|
||||
class than to repeatedly call append().
|
||||
*/
|
||||
void append (ObjectType* const newItem)
|
||||
{
|
||||
getLast().item = newItem;
|
||||
}
|
||||
|
||||
/** Creates copies of all the items in another list and adds them to this one.
|
||||
This will use the ObjectType's copy constructor to try to create copies of each
|
||||
item in the other list, and appends them to this list.
|
||||
*/
|
||||
void addCopyOfList (const LinkedListPointer& other)
|
||||
{
|
||||
auto* insertPoint = this;
|
||||
|
||||
for (auto* i = other.item; i != nullptr; i = i->nextListItem)
|
||||
{
|
||||
insertPoint->insertNext (new ObjectType (*i));
|
||||
insertPoint = &(insertPoint->item->nextListItem);
|
||||
}
|
||||
}
|
||||
|
||||
/** Removes the head item from the list.
|
||||
This won't delete the object that is removed, but returns it, so the caller can
|
||||
delete it if necessary.
|
||||
*/
|
||||
ObjectType* removeNext() noexcept
|
||||
{
|
||||
auto oldItem = item;
|
||||
|
||||
if (oldItem != nullptr)
|
||||
{
|
||||
item = oldItem->nextListItem;
|
||||
oldItem->nextListItem.item = nullptr;
|
||||
}
|
||||
|
||||
return oldItem;
|
||||
}
|
||||
|
||||
/** Removes a specific item from the list.
|
||||
Note that this will not delete the item, it simply unlinks it from the list.
|
||||
*/
|
||||
void remove (ObjectType* const itemToRemove)
|
||||
{
|
||||
if (auto* l = findPointerTo (itemToRemove))
|
||||
l->removeNext();
|
||||
}
|
||||
|
||||
/** Iterates the list, calling the delete operator on all of its elements and
|
||||
leaving this pointer empty.
|
||||
*/
|
||||
void deleteAll()
|
||||
{
|
||||
while (item != nullptr)
|
||||
{
|
||||
auto oldItem = item;
|
||||
item = oldItem->nextListItem;
|
||||
delete oldItem;
|
||||
}
|
||||
}
|
||||
|
||||
/** Finds a pointer to a given item.
|
||||
If the item is found in the list, this returns the pointer that points to it. If
|
||||
the item isn't found, this returns null.
|
||||
*/
|
||||
LinkedListPointer* findPointerTo (ObjectType* const itemToLookFor) noexcept
|
||||
{
|
||||
auto* l = this;
|
||||
|
||||
while (l->item != nullptr)
|
||||
{
|
||||
if (l->item == itemToLookFor)
|
||||
return l;
|
||||
|
||||
l = &(l->item->nextListItem);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/** Copies the items in the list to an array.
|
||||
The destArray must contain enough elements to hold the entire list - no checks are
|
||||
made for this!
|
||||
*/
|
||||
void copyToArray (ObjectType** destArray) const noexcept
|
||||
{
|
||||
JUCE_BEGIN_IGNORE_WARNINGS_MSVC (6011)
|
||||
jassert (destArray != nullptr);
|
||||
|
||||
for (auto* i = item; i != nullptr; i = i->nextListItem)
|
||||
*destArray++ = i;
|
||||
|
||||
JUCE_END_IGNORE_WARNINGS_MSVC
|
||||
}
|
||||
|
||||
/** Swaps this pointer with another one */
|
||||
void swapWith (LinkedListPointer& other) noexcept
|
||||
{
|
||||
std::swap (item, other.item);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Allows efficient repeated insertions into a list.
|
||||
|
||||
You can create an Appender object which points to the last element in your
|
||||
list, and then repeatedly call Appender::append() to add items to the end
|
||||
of the list in O(1) time.
|
||||
*/
|
||||
class Appender
|
||||
{
|
||||
public:
|
||||
/** Creates an appender which will add items to the given list.
|
||||
*/
|
||||
Appender (LinkedListPointer& endOfListPointer) noexcept
|
||||
: endOfList (&endOfListPointer)
|
||||
{
|
||||
// This can only be used to add to the end of a list.
|
||||
jassert (endOfListPointer.item == nullptr);
|
||||
}
|
||||
|
||||
/** Appends an item to the list. */
|
||||
void append (ObjectType* const newItem) noexcept
|
||||
{
|
||||
*endOfList = newItem;
|
||||
endOfList = &(newItem->nextListItem);
|
||||
}
|
||||
|
||||
private:
|
||||
LinkedListPointer* endOfList;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE (Appender)
|
||||
};
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
ObjectType* item;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE (LinkedListPointer)
|
||||
};
|
||||
|
||||
} // namespace juce
|
||||
|
321
deps/juce/modules/juce_core/containers/juce_ListenerList.cpp
vendored
Normal file
321
deps/juce/modules/juce_core/containers/juce_ListenerList.cpp
vendored
Normal file
@ -0,0 +1,321 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
#if JUCE_UNIT_TESTS
|
||||
|
||||
class ListenerListTests : public UnitTest
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
class TestListener
|
||||
{
|
||||
public:
|
||||
explicit TestListener (std::function<void()> cb) : callback (std::move (cb)) {}
|
||||
|
||||
void doCallback()
|
||||
{
|
||||
++numCalls;
|
||||
callback();
|
||||
}
|
||||
|
||||
int getNumCalls() const { return numCalls; }
|
||||
|
||||
private:
|
||||
int numCalls = 0;
|
||||
std::function<void()> callback;
|
||||
};
|
||||
|
||||
class TestObject
|
||||
{
|
||||
public:
|
||||
void addListener (std::function<void()> cb)
|
||||
{
|
||||
listeners.push_back (std::make_unique<TestListener> (std::move (cb)));
|
||||
listenerList.add (listeners.back().get());
|
||||
}
|
||||
|
||||
void removeListener (int i) { listenerList.remove (listeners[(size_t) i].get()); }
|
||||
|
||||
void callListeners()
|
||||
{
|
||||
++callLevel;
|
||||
listenerList.call ([] (auto& l) { l.doCallback(); });
|
||||
--callLevel;
|
||||
}
|
||||
|
||||
int getNumListeners() const { return (int) listeners.size(); }
|
||||
|
||||
auto& getListener (int i) { return *listeners[(size_t) i]; }
|
||||
|
||||
int getCallLevel() const
|
||||
{
|
||||
return callLevel;
|
||||
}
|
||||
|
||||
bool wereAllNonRemovedListenersCalled (int numCalls) const
|
||||
{
|
||||
return std::all_of (std::begin (listeners),
|
||||
std::end (listeners),
|
||||
[&] (auto& listener)
|
||||
{
|
||||
return (! listenerList.contains (listener.get())) || listener->getNumCalls() == numCalls;
|
||||
});
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<std::unique_ptr<TestListener>> listeners;
|
||||
ListenerList<TestListener> listenerList;
|
||||
int callLevel = 0;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
ListenerListTests() : UnitTest ("ListenerList", UnitTestCategories::containers) {}
|
||||
|
||||
void runTest() override
|
||||
{
|
||||
// This is a test that the pre-iterator adjustment implementation should pass too
|
||||
beginTest ("All non-removed listeners should be called - removing an already called listener");
|
||||
{
|
||||
TestObject test;
|
||||
|
||||
for (int i = 0; i < 20; ++i)
|
||||
{
|
||||
test.addListener ([i, &test]
|
||||
{
|
||||
if (i == 5)
|
||||
test.removeListener (6);
|
||||
});
|
||||
}
|
||||
|
||||
test.callListeners();
|
||||
expect (test.wereAllNonRemovedListenersCalled (1));
|
||||
}
|
||||
|
||||
// Iterator adjustment is necessary for passing this
|
||||
beginTest ("All non-removed listeners should be called - removing a yet uncalled listener");
|
||||
{
|
||||
TestObject test;
|
||||
|
||||
for (int i = 0; i < 20; ++i)
|
||||
{
|
||||
test.addListener ([i, &test]
|
||||
{
|
||||
if (i == 5)
|
||||
test.removeListener (4);
|
||||
});
|
||||
}
|
||||
|
||||
test.callListeners();
|
||||
expect (test.wereAllNonRemovedListenersCalled (1));
|
||||
}
|
||||
|
||||
// This test case demonstrates why we have to call --it.index instead of it.next()
|
||||
beginTest ("All non-removed listeners should be called - one callback removes multiple listeners");
|
||||
{
|
||||
TestObject test;
|
||||
|
||||
for (int i = 0; i < 20; ++i)
|
||||
{
|
||||
test.addListener ([i, &test]
|
||||
{
|
||||
if (i == 19)
|
||||
{
|
||||
test.removeListener (19);
|
||||
test.removeListener (0);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
test.callListeners();
|
||||
expect (test.wereAllNonRemovedListenersCalled (1));
|
||||
}
|
||||
|
||||
beginTest ("All non-removed listeners should be called - removing listeners randomly");
|
||||
{
|
||||
auto random = getRandom();
|
||||
|
||||
for (auto run = 0; run < 10; ++run)
|
||||
{
|
||||
const auto numListeners = random.nextInt ({ 10, 100 });
|
||||
const auto listenersThatRemoveListeners = chooseUnique (random,
|
||||
numListeners,
|
||||
random.nextInt ({ 0, numListeners / 2 }));
|
||||
|
||||
// The listener in position [key] should remove listeners in [value]
|
||||
std::map<int, std::set<int>> removals;
|
||||
|
||||
for (auto i : listenersThatRemoveListeners)
|
||||
{
|
||||
// Random::nextInt ({1, 1}); triggers an assertion
|
||||
removals[i] = chooseUnique (random,
|
||||
numListeners,
|
||||
random.nextInt ({ 1, std::max (2, numListeners / 10) }));
|
||||
}
|
||||
|
||||
TestObject test;
|
||||
|
||||
for (int i = 0; i < numListeners; ++i)
|
||||
{
|
||||
test.addListener ([i, &removals, &test]
|
||||
{
|
||||
const auto iter = removals.find (i);
|
||||
|
||||
if (iter == removals.end())
|
||||
return;
|
||||
|
||||
for (auto j : iter->second)
|
||||
{
|
||||
test.removeListener (j);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
test.callListeners();
|
||||
expect (test.wereAllNonRemovedListenersCalled (1));
|
||||
}
|
||||
}
|
||||
|
||||
// Iterator adjustment is not necessary for passing this
|
||||
beginTest ("All non-removed listeners should be called - add listener during iteration");
|
||||
{
|
||||
TestObject test;
|
||||
const auto numStartingListeners = 20;
|
||||
|
||||
for (int i = 0; i < numStartingListeners; ++i)
|
||||
{
|
||||
test.addListener ([i, &test]
|
||||
{
|
||||
if (i == 5 || i == 6)
|
||||
test.addListener ([] {});
|
||||
});
|
||||
}
|
||||
|
||||
test.callListeners();
|
||||
|
||||
// Only the Listeners added before the test can be expected to have been called
|
||||
bool success = true;
|
||||
|
||||
for (int i = 0; i < numStartingListeners; ++i)
|
||||
success = success && test.getListener (i).getNumCalls() == 1;
|
||||
|
||||
// Listeners added during the iteration must not be called in that iteration
|
||||
for (int i = numStartingListeners; i < test.getNumListeners(); ++i)
|
||||
success = success && test.getListener (i).getNumCalls() == 0;
|
||||
|
||||
expect (success);
|
||||
}
|
||||
|
||||
beginTest ("All non-removed listeners should be called - nested ListenerList::call()");
|
||||
{
|
||||
TestObject test;
|
||||
|
||||
for (int i = 0; i < 20; ++i)
|
||||
{
|
||||
test.addListener ([i, &test]
|
||||
{
|
||||
const auto callLevel = test.getCallLevel();
|
||||
|
||||
if (i == 6 && callLevel == 1)
|
||||
{
|
||||
test.callListeners();
|
||||
}
|
||||
|
||||
if (i == 5)
|
||||
{
|
||||
if (callLevel == 1)
|
||||
test.removeListener (4);
|
||||
else if (callLevel == 2)
|
||||
test.removeListener (6);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
test.callListeners();
|
||||
expect (test.wereAllNonRemovedListenersCalled (2));
|
||||
}
|
||||
|
||||
beginTest ("All non-removed listeners should be called - random ListenerList::call()");
|
||||
{
|
||||
const auto numListeners = 20;
|
||||
auto random = getRandom();
|
||||
|
||||
for (int run = 0; run < 10; ++run)
|
||||
{
|
||||
TestObject test;
|
||||
auto numCalls = 0;
|
||||
|
||||
auto listenersToRemove = chooseUnique (random, numListeners, numListeners / 2);
|
||||
|
||||
for (int i = 0; i < numListeners; ++i)
|
||||
{
|
||||
// Capturing numListeners is a warning on MacOS, not capturing it is an error on Windows
|
||||
test.addListener ([&]
|
||||
{
|
||||
const auto callLevel = test.getCallLevel();
|
||||
|
||||
if (callLevel < 4 && random.nextFloat() < 0.05f)
|
||||
{
|
||||
++numCalls;
|
||||
test.callListeners();
|
||||
}
|
||||
|
||||
if (random.nextFloat() < 0.5f)
|
||||
{
|
||||
const auto listenerToRemove = random.nextInt ({ 0, numListeners });
|
||||
|
||||
if (listenersToRemove.erase (listenerToRemove) > 0)
|
||||
test.removeListener (listenerToRemove);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
while (listenersToRemove.size() > 0)
|
||||
{
|
||||
test.callListeners();
|
||||
++numCalls;
|
||||
}
|
||||
|
||||
expect (test.wereAllNonRemovedListenersCalled (numCalls));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
static std::set<int> chooseUnique (Random& random, int max, int numChosen)
|
||||
{
|
||||
std::set<int> result;
|
||||
|
||||
while ((int) result.size() < numChosen)
|
||||
result.insert (random.nextInt ({ 0, max }));
|
||||
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
static ListenerListTests listenerListTests;
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace juce
|
@ -1,313 +1,362 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Holds a set of objects and can invoke a member function callback on each object
|
||||
in the set with a single call.
|
||||
|
||||
Use a ListenerList to manage a set of objects which need a callback, and you
|
||||
can invoke a member function by simply calling call() or callChecked().
|
||||
|
||||
E.g.
|
||||
@code
|
||||
class MyListenerType
|
||||
{
|
||||
public:
|
||||
void myCallbackMethod (int foo, bool bar);
|
||||
};
|
||||
|
||||
ListenerList<MyListenerType> listeners;
|
||||
listeners.add (someCallbackObjects...);
|
||||
|
||||
// This will invoke myCallbackMethod (1234, true) on each of the objects
|
||||
// in the list...
|
||||
listeners.call ([] (MyListenerType& l) { l.myCallbackMethod (1234, true); });
|
||||
@endcode
|
||||
|
||||
If you add or remove listeners from the list during one of the callbacks - i.e. while
|
||||
it's in the middle of iterating the listeners, then it's guaranteed that no listeners
|
||||
will be mistakenly called after they've been removed, but it may mean that some of the
|
||||
listeners could be called more than once, or not at all, depending on the list's order.
|
||||
|
||||
Sometimes, there's a chance that invoking one of the callbacks might result in the
|
||||
list itself being deleted while it's still iterating - to survive this situation, you can
|
||||
use callChecked() instead of call(), passing it a local object to act as a "BailOutChecker".
|
||||
The BailOutChecker must implement a method of the form "bool shouldBailOut()", and
|
||||
the list will check this after each callback to determine whether it should abort the
|
||||
operation. For an example of a bail-out checker, see the Component::BailOutChecker class,
|
||||
which can be used to check when a Component has been deleted. See also
|
||||
ListenerList::DummyBailOutChecker, which is a dummy checker that always returns false.
|
||||
|
||||
@tags{Core}
|
||||
*/
|
||||
template <class ListenerClass,
|
||||
class ArrayType = Array<ListenerClass*>>
|
||||
class ListenerList
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates an empty list. */
|
||||
ListenerList() = default;
|
||||
|
||||
/** Destructor. */
|
||||
~ListenerList() = default;
|
||||
|
||||
//==============================================================================
|
||||
/** Adds a listener to the list.
|
||||
A listener can only be added once, so if the listener is already in the list,
|
||||
this method has no effect.
|
||||
@see remove
|
||||
*/
|
||||
void add (ListenerClass* listenerToAdd)
|
||||
{
|
||||
if (listenerToAdd != nullptr)
|
||||
listeners.addIfNotAlreadyThere (listenerToAdd);
|
||||
else
|
||||
jassertfalse; // Listeners can't be null pointers!
|
||||
}
|
||||
|
||||
/** Removes a listener from the list.
|
||||
If the listener wasn't in the list, this has no effect.
|
||||
*/
|
||||
void remove (ListenerClass* listenerToRemove)
|
||||
{
|
||||
jassert (listenerToRemove != nullptr); // Listeners can't be null pointers!
|
||||
listeners.removeFirstMatchingValue (listenerToRemove);
|
||||
}
|
||||
|
||||
/** Returns the number of registered listeners. */
|
||||
int size() const noexcept { return listeners.size(); }
|
||||
|
||||
/** Returns true if no listeners are registered, false otherwise. */
|
||||
bool isEmpty() const noexcept { return listeners.isEmpty(); }
|
||||
|
||||
/** Clears the list. */
|
||||
void clear() { listeners.clear(); }
|
||||
|
||||
/** Returns true if the specified listener has been added to the list. */
|
||||
bool contains (ListenerClass* listener) const noexcept { return listeners.contains (listener); }
|
||||
|
||||
/** Returns the raw array of listeners. */
|
||||
const ArrayType& getListeners() const noexcept { return listeners; }
|
||||
|
||||
//==============================================================================
|
||||
/** Calls a member function on each listener in the list, with multiple parameters. */
|
||||
template <typename Callback>
|
||||
void call (Callback&& callback)
|
||||
{
|
||||
typename ArrayType::ScopedLockType lock (listeners.getLock());
|
||||
|
||||
for (Iterator<DummyBailOutChecker, ThisType> iter (*this); iter.next();)
|
||||
callback (*iter.getListener());
|
||||
}
|
||||
|
||||
/** Calls a member function with 1 parameter, on all but the specified listener in the list.
|
||||
This can be useful if the caller is also a listener and needs to exclude itself.
|
||||
*/
|
||||
template <typename Callback>
|
||||
void callExcluding (ListenerClass* listenerToExclude, Callback&& callback)
|
||||
{
|
||||
typename ArrayType::ScopedLockType lock (listeners.getLock());
|
||||
|
||||
for (Iterator<DummyBailOutChecker, ThisType> iter (*this); iter.next();)
|
||||
{
|
||||
auto* l = iter.getListener();
|
||||
|
||||
if (l != listenerToExclude)
|
||||
callback (*l);
|
||||
}
|
||||
}
|
||||
|
||||
/** Calls a member function on each listener in the list, with 1 parameter and a bail-out-checker.
|
||||
See the class description for info about writing a bail-out checker.
|
||||
*/
|
||||
template <typename Callback, typename BailOutCheckerType>
|
||||
void callChecked (const BailOutCheckerType& bailOutChecker, Callback&& callback)
|
||||
{
|
||||
typename ArrayType::ScopedLockType lock (listeners.getLock());
|
||||
|
||||
for (Iterator<BailOutCheckerType, ThisType> iter (*this); iter.next (bailOutChecker);)
|
||||
callback (*iter.getListener());
|
||||
}
|
||||
|
||||
/** Calls a member function, with 1 parameter, on all but the specified listener in the list
|
||||
with a bail-out-checker. This can be useful if the caller is also a listener and needs to
|
||||
exclude itself. See the class description for info about writing a bail-out checker.
|
||||
*/
|
||||
template <typename Callback, typename BailOutCheckerType>
|
||||
void callCheckedExcluding (ListenerClass* listenerToExclude,
|
||||
const BailOutCheckerType& bailOutChecker,
|
||||
Callback&& callback)
|
||||
{
|
||||
typename ArrayType::ScopedLockType lock (listeners.getLock());
|
||||
|
||||
for (Iterator<BailOutCheckerType, ThisType> iter (*this); iter.next (bailOutChecker);)
|
||||
{
|
||||
auto* l = iter.getListener();
|
||||
|
||||
if (l != listenerToExclude)
|
||||
callback (*l);
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** A dummy bail-out checker that always returns false.
|
||||
See the ListenerList notes for more info about bail-out checkers.
|
||||
*/
|
||||
struct DummyBailOutChecker
|
||||
{
|
||||
bool shouldBailOut() const noexcept { return false; }
|
||||
};
|
||||
|
||||
using ThisType = ListenerList<ListenerClass, ArrayType>;
|
||||
using ListenerType = ListenerClass;
|
||||
|
||||
//==============================================================================
|
||||
/** Iterates the listeners in a ListenerList. */
|
||||
template <class BailOutCheckerType, class ListType>
|
||||
struct Iterator
|
||||
{
|
||||
Iterator (const ListType& listToIterate) noexcept
|
||||
: list (listToIterate), index (listToIterate.size())
|
||||
{}
|
||||
|
||||
~Iterator() = default;
|
||||
|
||||
//==============================================================================
|
||||
bool next() noexcept
|
||||
{
|
||||
if (index <= 0)
|
||||
return false;
|
||||
|
||||
auto listSize = list.size();
|
||||
|
||||
if (--index < listSize)
|
||||
return true;
|
||||
|
||||
index = listSize - 1;
|
||||
return index >= 0;
|
||||
}
|
||||
|
||||
bool next (const BailOutCheckerType& bailOutChecker) noexcept
|
||||
{
|
||||
return (! bailOutChecker.shouldBailOut()) && next();
|
||||
}
|
||||
|
||||
typename ListType::ListenerType* getListener() const noexcept
|
||||
{
|
||||
return list.getListeners().getUnchecked (index);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
private:
|
||||
const ListType& list;
|
||||
int index;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE (Iterator)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
#ifndef DOXYGEN
|
||||
// There are now lambda-based call functions that can be used to replace these old method-based versions.
|
||||
// We'll eventually deprecate these old ones, so please begin moving your code to use lambdas!
|
||||
void call (void (ListenerClass::*callbackFunction) ())
|
||||
{
|
||||
call ([=] (ListenerClass& l) { (l.*callbackFunction)(); });
|
||||
}
|
||||
|
||||
void callExcluding (ListenerClass* listenerToExclude, void (ListenerClass::*callbackFunction) ())
|
||||
{
|
||||
callExcluding (listenerToExclude, [=] (ListenerClass& l) { (l.*callbackFunction)(); });
|
||||
}
|
||||
|
||||
template <class BailOutCheckerType>
|
||||
void callChecked (const BailOutCheckerType& bailOutChecker, void (ListenerClass::*callbackFunction) ())
|
||||
{
|
||||
callChecked (bailOutChecker, [=] (ListenerClass& l) { (l.*callbackFunction)(); });
|
||||
}
|
||||
|
||||
template <class BailOutCheckerType>
|
||||
void callCheckedExcluding (ListenerClass* listenerToExclude,
|
||||
const BailOutCheckerType& bailOutChecker,
|
||||
void (ListenerClass::*callbackFunction) ())
|
||||
{
|
||||
callCheckedExcluding (listenerToExclude, bailOutChecker, [=] (ListenerClass& l) { (l.*callbackFunction)(); });
|
||||
}
|
||||
|
||||
template <typename... MethodArgs, typename... Args>
|
||||
void call (void (ListenerClass::*callbackFunction) (MethodArgs...), Args&&... args)
|
||||
{
|
||||
typename ArrayType::ScopedLockType lock (listeners.getLock());
|
||||
|
||||
for (Iterator<DummyBailOutChecker, ThisType> iter (*this); iter.next();)
|
||||
(iter.getListener()->*callbackFunction) (static_cast<typename TypeHelpers::ParameterType<Args>::type> (args)...);
|
||||
}
|
||||
|
||||
template <typename... MethodArgs, typename... Args>
|
||||
void callExcluding (ListenerClass* listenerToExclude,
|
||||
void (ListenerClass::*callbackFunction) (MethodArgs...),
|
||||
Args&&... args)
|
||||
{
|
||||
typename ArrayType::ScopedLockType lock (listeners.getLock());
|
||||
|
||||
for (Iterator<DummyBailOutChecker, ThisType> iter (*this); iter.next();)
|
||||
if (iter.getListener() != listenerToExclude)
|
||||
(iter.getListener()->*callbackFunction) (static_cast<typename TypeHelpers::ParameterType<Args>::type> (args)...);
|
||||
}
|
||||
|
||||
template <typename BailOutCheckerType, typename... MethodArgs, typename... Args>
|
||||
void callChecked (const BailOutCheckerType& bailOutChecker,
|
||||
void (ListenerClass::*callbackFunction) (MethodArgs...),
|
||||
Args&&... args)
|
||||
{
|
||||
typename ArrayType::ScopedLockType lock (listeners.getLock());
|
||||
|
||||
for (Iterator<BailOutCheckerType, ThisType> iter (*this); iter.next (bailOutChecker);)
|
||||
(iter.getListener()->*callbackFunction) (static_cast<typename TypeHelpers::ParameterType<Args>::type> (args)...);
|
||||
}
|
||||
|
||||
template <typename BailOutCheckerType, typename... MethodArgs, typename... Args>
|
||||
void callCheckedExcluding (ListenerClass* listenerToExclude,
|
||||
const BailOutCheckerType& bailOutChecker,
|
||||
void (ListenerClass::*callbackFunction) (MethodArgs...),
|
||||
Args&&... args)
|
||||
{
|
||||
typename ArrayType::ScopedLockType lock (listeners.getLock());
|
||||
|
||||
for (Iterator<BailOutCheckerType, ThisType> iter (*this); iter.next (bailOutChecker);)
|
||||
if (iter.getListener() != listenerToExclude)
|
||||
(iter.getListener()->*callbackFunction) (static_cast<typename TypeHelpers::ParameterType<Args>::type> (args)...);
|
||||
}
|
||||
#endif
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
ArrayType listeners;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE (ListenerList)
|
||||
};
|
||||
|
||||
} // namespace juce
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Holds a set of objects and can invoke a member function callback on each object
|
||||
in the set with a single call.
|
||||
|
||||
Use a ListenerList to manage a set of objects which need a callback, and you
|
||||
can invoke a member function by simply calling call() or callChecked().
|
||||
|
||||
E.g.
|
||||
@code
|
||||
class MyListenerType
|
||||
{
|
||||
public:
|
||||
void myCallbackMethod (int foo, bool bar);
|
||||
};
|
||||
|
||||
ListenerList<MyListenerType> listeners;
|
||||
listeners.add (someCallbackObjects...);
|
||||
|
||||
// This will invoke myCallbackMethod (1234, true) on each of the objects
|
||||
// in the list...
|
||||
listeners.call ([] (MyListenerType& l) { l.myCallbackMethod (1234, true); });
|
||||
@endcode
|
||||
|
||||
It is guaranteed that every Listener is called during an iteration if it's inside the
|
||||
ListenerList before the iteration starts and isn't removed until its end. This guarantee
|
||||
holds even if some Listeners are removed or new ones are added during the iteration.
|
||||
|
||||
Listeners added during an iteration are guaranteed to be not called in that iteration.
|
||||
|
||||
Sometimes, there's a chance that invoking one of the callbacks might result in the
|
||||
list itself being deleted while it's still iterating - to survive this situation, you can
|
||||
use callChecked() instead of call(), passing it a local object to act as a "BailOutChecker".
|
||||
The BailOutChecker must implement a method of the form "bool shouldBailOut()", and
|
||||
the list will check this after each callback to determine whether it should abort the
|
||||
operation. For an example of a bail-out checker, see the Component::BailOutChecker class,
|
||||
which can be used to check when a Component has been deleted. See also
|
||||
ListenerList::DummyBailOutChecker, which is a dummy checker that always returns false.
|
||||
|
||||
@tags{Core}
|
||||
*/
|
||||
template <class ListenerClass,
|
||||
class ArrayType = Array<ListenerClass*>>
|
||||
class ListenerList
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates an empty list. */
|
||||
ListenerList() = default;
|
||||
|
||||
/** Destructor. */
|
||||
~ListenerList()
|
||||
{
|
||||
WrappedIterator::forEach (activeIterators, [&] (auto& iter)
|
||||
{
|
||||
iter.invalidate();
|
||||
});
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Adds a listener to the list.
|
||||
A listener can only be added once, so if the listener is already in the list,
|
||||
this method has no effect.
|
||||
@see remove
|
||||
*/
|
||||
void add (ListenerClass* listenerToAdd)
|
||||
{
|
||||
if (listenerToAdd != nullptr)
|
||||
listeners.addIfNotAlreadyThere (listenerToAdd);
|
||||
else
|
||||
jassertfalse; // Listeners can't be null pointers!
|
||||
}
|
||||
|
||||
/** Removes a listener from the list.
|
||||
If the listener wasn't in the list, this has no effect.
|
||||
*/
|
||||
void remove (ListenerClass* listenerToRemove)
|
||||
{
|
||||
jassert (listenerToRemove != nullptr); // Listeners can't be null pointers!
|
||||
|
||||
typename ArrayType::ScopedLockType lock (listeners.getLock());
|
||||
|
||||
const auto index = listeners.removeFirstMatchingValue (listenerToRemove);
|
||||
|
||||
WrappedIterator::forEach (activeIterators, [&] (auto& iter)
|
||||
{
|
||||
if (0 <= index && index < iter.get().index)
|
||||
--iter.get().index;
|
||||
});
|
||||
}
|
||||
|
||||
/** Returns the number of registered listeners. */
|
||||
int size() const noexcept { return listeners.size(); }
|
||||
|
||||
/** Returns true if no listeners are registered, false otherwise. */
|
||||
bool isEmpty() const noexcept { return listeners.isEmpty(); }
|
||||
|
||||
/** Clears the list. */
|
||||
void clear() { listeners.clear(); }
|
||||
|
||||
/** Returns true if the specified listener has been added to the list. */
|
||||
bool contains (ListenerClass* listener) const noexcept { return listeners.contains (listener); }
|
||||
|
||||
/** Returns the raw array of listeners. */
|
||||
const ArrayType& getListeners() const noexcept { return listeners; }
|
||||
|
||||
//==============================================================================
|
||||
/** Calls a member function on each listener in the list, with multiple parameters. */
|
||||
template <typename Callback>
|
||||
void call (Callback&& callback)
|
||||
{
|
||||
typename ArrayType::ScopedLockType lock (listeners.getLock());
|
||||
|
||||
for (WrappedIterator iter (*this, activeIterators); iter.get().next();)
|
||||
callback (*iter.get().getListener());
|
||||
}
|
||||
|
||||
/** Calls a member function with 1 parameter, on all but the specified listener in the list.
|
||||
This can be useful if the caller is also a listener and needs to exclude itself.
|
||||
*/
|
||||
template <typename Callback>
|
||||
void callExcluding (ListenerClass* listenerToExclude, Callback&& callback)
|
||||
{
|
||||
typename ArrayType::ScopedLockType lock (listeners.getLock());
|
||||
|
||||
for (WrappedIterator iter (*this, activeIterators); iter.get().next();)
|
||||
{
|
||||
auto* l = iter.get().getListener();
|
||||
|
||||
if (l != listenerToExclude)
|
||||
callback (*l);
|
||||
}
|
||||
}
|
||||
|
||||
/** Calls a member function on each listener in the list, with 1 parameter and a bail-out-checker.
|
||||
See the class description for info about writing a bail-out checker.
|
||||
*/
|
||||
template <typename Callback, typename BailOutCheckerType>
|
||||
void callChecked (const BailOutCheckerType& bailOutChecker, Callback&& callback)
|
||||
{
|
||||
typename ArrayType::ScopedLockType lock (listeners.getLock());
|
||||
|
||||
for (WrappedIterator iter (*this, activeIterators); iter.get().next (bailOutChecker);)
|
||||
{
|
||||
callback (*iter.get().getListener());
|
||||
}
|
||||
}
|
||||
|
||||
/** Calls a member function, with 1 parameter, on all but the specified listener in the list
|
||||
with a bail-out-checker. This can be useful if the caller is also a listener and needs to
|
||||
exclude itself. See the class description for info about writing a bail-out checker.
|
||||
*/
|
||||
template <typename Callback, typename BailOutCheckerType>
|
||||
void callCheckedExcluding (ListenerClass* listenerToExclude,
|
||||
const BailOutCheckerType& bailOutChecker,
|
||||
Callback&& callback)
|
||||
{
|
||||
typename ArrayType::ScopedLockType lock (listeners.getLock());
|
||||
|
||||
for (WrappedIterator iter (*this, activeIterators); iter.get().next (bailOutChecker);)
|
||||
{
|
||||
auto* l = iter.get().getListener();
|
||||
|
||||
if (l != listenerToExclude)
|
||||
callback (*l);
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** A dummy bail-out checker that always returns false.
|
||||
See the ListenerList notes for more info about bail-out checkers.
|
||||
*/
|
||||
struct DummyBailOutChecker
|
||||
{
|
||||
bool shouldBailOut() const noexcept { return false; }
|
||||
};
|
||||
|
||||
using ThisType = ListenerList<ListenerClass, ArrayType>;
|
||||
using ListenerType = ListenerClass;
|
||||
|
||||
//==============================================================================
|
||||
/** Iterates the listeners in a ListenerList. */
|
||||
struct Iterator
|
||||
{
|
||||
explicit Iterator (const ListenerList& listToIterate) noexcept
|
||||
: list (listToIterate), index (listToIterate.size())
|
||||
{}
|
||||
|
||||
//==============================================================================
|
||||
bool next() noexcept
|
||||
{
|
||||
if (index <= 0)
|
||||
return false;
|
||||
|
||||
auto listSize = list.size();
|
||||
|
||||
if (--index < listSize)
|
||||
return true;
|
||||
|
||||
index = listSize - 1;
|
||||
return index >= 0;
|
||||
}
|
||||
|
||||
template <class BailOutCheckerType>
|
||||
bool next (const BailOutCheckerType& bailOutChecker) noexcept
|
||||
{
|
||||
return (! bailOutChecker.shouldBailOut()) && next();
|
||||
}
|
||||
|
||||
ListenerClass* getListener() const noexcept
|
||||
{
|
||||
return list.getListeners().getUnchecked (index);
|
||||
}
|
||||
|
||||
private:
|
||||
const ListenerList& list;
|
||||
int index;
|
||||
|
||||
friend ListenerList;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE (Iterator)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
#ifndef DOXYGEN
|
||||
void call (void (ListenerClass::*callbackFunction) ())
|
||||
{
|
||||
call ([=] (ListenerClass& l) { (l.*callbackFunction)(); });
|
||||
}
|
||||
|
||||
void callExcluding (ListenerClass* listenerToExclude, void (ListenerClass::*callbackFunction) ())
|
||||
{
|
||||
callExcluding (listenerToExclude, [=] (ListenerClass& l) { (l.*callbackFunction)(); });
|
||||
}
|
||||
|
||||
template <class BailOutCheckerType>
|
||||
void callChecked (const BailOutCheckerType& bailOutChecker, void (ListenerClass::*callbackFunction) ())
|
||||
{
|
||||
callChecked (bailOutChecker, [=] (ListenerClass& l) { (l.*callbackFunction)(); });
|
||||
}
|
||||
|
||||
template <class BailOutCheckerType>
|
||||
void callCheckedExcluding (ListenerClass* listenerToExclude,
|
||||
const BailOutCheckerType& bailOutChecker,
|
||||
void (ListenerClass::*callbackFunction) ())
|
||||
{
|
||||
callCheckedExcluding (listenerToExclude, bailOutChecker, [=] (ListenerClass& l) { (l.*callbackFunction)(); });
|
||||
}
|
||||
|
||||
template <typename... MethodArgs, typename... Args>
|
||||
void call (void (ListenerClass::*callbackFunction) (MethodArgs...), Args&&... args)
|
||||
{
|
||||
typename ArrayType::ScopedLockType lock (listeners.getLock());
|
||||
|
||||
for (Iterator iter (*this); iter.next();)
|
||||
(iter.getListener()->*callbackFunction) (static_cast<typename TypeHelpers::ParameterType<Args>::type> (args)...);
|
||||
}
|
||||
|
||||
template <typename... MethodArgs, typename... Args>
|
||||
void callExcluding (ListenerClass* listenerToExclude,
|
||||
void (ListenerClass::*callbackFunction) (MethodArgs...),
|
||||
Args&&... args)
|
||||
{
|
||||
typename ArrayType::ScopedLockType lock (listeners.getLock());
|
||||
|
||||
for (Iterator iter (*this); iter.next();)
|
||||
if (iter.getListener() != listenerToExclude)
|
||||
(iter.getListener()->*callbackFunction) (static_cast<typename TypeHelpers::ParameterType<Args>::type> (args)...);
|
||||
}
|
||||
|
||||
template <typename BailOutCheckerType, typename... MethodArgs, typename... Args>
|
||||
void callChecked (const BailOutCheckerType& bailOutChecker,
|
||||
void (ListenerClass::*callbackFunction) (MethodArgs...),
|
||||
Args&&... args)
|
||||
{
|
||||
typename ArrayType::ScopedLockType lock (listeners.getLock());
|
||||
|
||||
for (Iterator iter (*this); iter.next (bailOutChecker);)
|
||||
(iter.getListener()->*callbackFunction) (static_cast<typename TypeHelpers::ParameterType<Args>::type> (args)...);
|
||||
}
|
||||
|
||||
template <typename BailOutCheckerType, typename... MethodArgs, typename... Args>
|
||||
void callCheckedExcluding (ListenerClass* listenerToExclude,
|
||||
const BailOutCheckerType& bailOutChecker,
|
||||
void (ListenerClass::*callbackFunction) (MethodArgs...),
|
||||
Args&&... args)
|
||||
{
|
||||
typename ArrayType::ScopedLockType lock (listeners.getLock());
|
||||
|
||||
for (Iterator iter (*this); iter.next (bailOutChecker);)
|
||||
if (iter.getListener() != listenerToExclude)
|
||||
(iter.getListener()->*callbackFunction) (static_cast<typename TypeHelpers::ParameterType<Args>::type> (args)...);
|
||||
}
|
||||
#endif
|
||||
|
||||
private:
|
||||
class WrappedIterator
|
||||
{
|
||||
public:
|
||||
WrappedIterator (const ListenerList& listToIterate, WrappedIterator*& listHeadIn)
|
||||
: it (listToIterate), listHead (listHeadIn), next (listHead)
|
||||
{
|
||||
listHead = this;
|
||||
}
|
||||
|
||||
~WrappedIterator()
|
||||
{
|
||||
if (valid)
|
||||
listHead = next;
|
||||
}
|
||||
|
||||
auto& get() noexcept { return it; }
|
||||
|
||||
template <typename Callback>
|
||||
static void forEach (WrappedIterator* wrapped, Callback&& cb)
|
||||
{
|
||||
for (auto* p = wrapped; p != nullptr; p = p->next)
|
||||
cb (*p);
|
||||
}
|
||||
|
||||
void invalidate() noexcept { valid = false; }
|
||||
|
||||
private:
|
||||
Iterator it;
|
||||
WrappedIterator*& listHead;
|
||||
WrappedIterator* next = nullptr;
|
||||
bool valid = true;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
ArrayType listeners;
|
||||
WrappedIterator* activeIterators = nullptr;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE (ListenerList)
|
||||
};
|
||||
|
||||
} // namespace juce
|
||||
|
@ -1,306 +1,306 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
NamedValueSet::NamedValue::NamedValue() noexcept {}
|
||||
NamedValueSet::NamedValue::~NamedValue() noexcept {}
|
||||
|
||||
NamedValueSet::NamedValue::NamedValue (const Identifier& n, const var& v) : name (n), value (v) {}
|
||||
NamedValueSet::NamedValue::NamedValue (const NamedValue& other) : NamedValue (other.name, other.value) {}
|
||||
|
||||
NamedValueSet::NamedValue::NamedValue (NamedValue&& other) noexcept
|
||||
: NamedValue (std::move (other.name),
|
||||
std::move (other.value))
|
||||
{}
|
||||
|
||||
NamedValueSet::NamedValue::NamedValue (const Identifier& n, var&& v) noexcept
|
||||
: name (n), value (std::move (v))
|
||||
{
|
||||
}
|
||||
|
||||
NamedValueSet::NamedValue::NamedValue (Identifier&& n, var&& v) noexcept
|
||||
: name (std::move (n)),
|
||||
value (std::move (v))
|
||||
{}
|
||||
|
||||
NamedValueSet::NamedValue& NamedValueSet::NamedValue::operator= (NamedValue&& other) noexcept
|
||||
{
|
||||
name = std::move (other.name);
|
||||
value = std::move (other.value);
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool NamedValueSet::NamedValue::operator== (const NamedValue& other) const noexcept { return name == other.name && value == other.value; }
|
||||
bool NamedValueSet::NamedValue::operator!= (const NamedValue& other) const noexcept { return ! operator== (other); }
|
||||
|
||||
//==============================================================================
|
||||
NamedValueSet::NamedValueSet() noexcept {}
|
||||
NamedValueSet::~NamedValueSet() noexcept {}
|
||||
|
||||
NamedValueSet::NamedValueSet (const NamedValueSet& other) : values (other.values) {}
|
||||
|
||||
NamedValueSet::NamedValueSet (NamedValueSet&& other) noexcept
|
||||
: values (std::move (other.values)) {}
|
||||
|
||||
NamedValueSet::NamedValueSet (std::initializer_list<NamedValue> list)
|
||||
: values (std::move (list))
|
||||
{
|
||||
}
|
||||
|
||||
NamedValueSet& NamedValueSet::operator= (const NamedValueSet& other)
|
||||
{
|
||||
clear();
|
||||
values = other.values;
|
||||
return *this;
|
||||
}
|
||||
|
||||
NamedValueSet& NamedValueSet::operator= (NamedValueSet&& other) noexcept
|
||||
{
|
||||
other.values.swapWith (values);
|
||||
return *this;
|
||||
}
|
||||
|
||||
void NamedValueSet::clear()
|
||||
{
|
||||
values.clear();
|
||||
}
|
||||
|
||||
bool NamedValueSet::operator== (const NamedValueSet& other) const noexcept
|
||||
{
|
||||
auto num = values.size();
|
||||
|
||||
if (num != other.values.size())
|
||||
return false;
|
||||
|
||||
for (int i = 0; i < num; ++i)
|
||||
{
|
||||
// optimise for the case where the keys are in the same order
|
||||
if (values.getReference(i).name == other.values.getReference(i).name)
|
||||
{
|
||||
if (values.getReference(i).value != other.values.getReference(i).value)
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// if we encounter keys that are in a different order, search remaining items by brute force..
|
||||
for (int j = i; j < num; ++j)
|
||||
{
|
||||
if (auto* otherVal = other.getVarPointer (values.getReference(j).name))
|
||||
if (values.getReference(j).value == *otherVal)
|
||||
continue;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NamedValueSet::operator!= (const NamedValueSet& other) const noexcept { return ! operator== (other); }
|
||||
|
||||
int NamedValueSet::size() const noexcept { return values.size(); }
|
||||
bool NamedValueSet::isEmpty() const noexcept { return values.isEmpty(); }
|
||||
|
||||
static const var& getNullVarRef() noexcept
|
||||
{
|
||||
static var nullVar;
|
||||
return nullVar;
|
||||
}
|
||||
|
||||
const var& NamedValueSet::operator[] (const Identifier& name) const noexcept
|
||||
{
|
||||
if (auto* v = getVarPointer (name))
|
||||
return *v;
|
||||
|
||||
return getNullVarRef();
|
||||
}
|
||||
|
||||
var NamedValueSet::getWithDefault (const Identifier& name, const var& defaultReturnValue) const
|
||||
{
|
||||
if (auto* v = getVarPointer (name))
|
||||
return *v;
|
||||
|
||||
return defaultReturnValue;
|
||||
}
|
||||
|
||||
var* NamedValueSet::getVarPointer (const Identifier& name) noexcept
|
||||
{
|
||||
for (auto& i : values)
|
||||
if (i.name == name)
|
||||
return &(i.value);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
const var* NamedValueSet::getVarPointer (const Identifier& name) const noexcept
|
||||
{
|
||||
for (auto& i : values)
|
||||
if (i.name == name)
|
||||
return &(i.value);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
bool NamedValueSet::set (const Identifier& name, var&& newValue)
|
||||
{
|
||||
if (auto* v = getVarPointer (name))
|
||||
{
|
||||
if (v->equalsWithSameType (newValue))
|
||||
return false;
|
||||
|
||||
*v = std::move (newValue);
|
||||
return true;
|
||||
}
|
||||
|
||||
values.add ({ name, std::move (newValue) });
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NamedValueSet::set (const Identifier& name, const var& newValue)
|
||||
{
|
||||
if (auto* v = getVarPointer (name))
|
||||
{
|
||||
if (v->equalsWithSameType (newValue))
|
||||
return false;
|
||||
|
||||
*v = newValue;
|
||||
return true;
|
||||
}
|
||||
|
||||
values.add ({ name, newValue });
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NamedValueSet::contains (const Identifier& name) const noexcept
|
||||
{
|
||||
return getVarPointer (name) != nullptr;
|
||||
}
|
||||
|
||||
int NamedValueSet::indexOf (const Identifier& name) const noexcept
|
||||
{
|
||||
auto numValues = values.size();
|
||||
|
||||
for (int i = 0; i < numValues; ++i)
|
||||
if (values.getReference(i).name == name)
|
||||
return i;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool NamedValueSet::remove (const Identifier& name)
|
||||
{
|
||||
auto numValues = values.size();
|
||||
|
||||
for (int i = 0; i < numValues; ++i)
|
||||
{
|
||||
if (values.getReference(i).name == name)
|
||||
{
|
||||
values.remove (i);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Identifier NamedValueSet::getName (const int index) const noexcept
|
||||
{
|
||||
if (isPositiveAndBelow (index, values.size()))
|
||||
return values.getReference (index).name;
|
||||
|
||||
jassertfalse;
|
||||
return {};
|
||||
}
|
||||
|
||||
const var& NamedValueSet::getValueAt (const int index) const noexcept
|
||||
{
|
||||
if (isPositiveAndBelow (index, values.size()))
|
||||
return values.getReference (index).value;
|
||||
|
||||
jassertfalse;
|
||||
return getNullVarRef();
|
||||
}
|
||||
|
||||
var* NamedValueSet::getVarPointerAt (int index) noexcept
|
||||
{
|
||||
if (isPositiveAndBelow (index, values.size()))
|
||||
return &(values.getReference (index).value);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
const var* NamedValueSet::getVarPointerAt (int index) const noexcept
|
||||
{
|
||||
if (isPositiveAndBelow (index, values.size()))
|
||||
return &(values.getReference (index).value);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
void NamedValueSet::setFromXmlAttributes (const XmlElement& xml)
|
||||
{
|
||||
values.clearQuick();
|
||||
|
||||
for (auto* att = xml.attributes.get(); att != nullptr; att = att->nextListItem)
|
||||
{
|
||||
if (att->name.toString().startsWith ("base64:"))
|
||||
{
|
||||
MemoryBlock mb;
|
||||
|
||||
if (mb.fromBase64Encoding (att->value))
|
||||
{
|
||||
values.add ({ att->name.toString().substring (7), var (mb) });
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
values.add ({ att->name, var (att->value) });
|
||||
}
|
||||
}
|
||||
|
||||
void NamedValueSet::copyToXmlAttributes (XmlElement& xml) const
|
||||
{
|
||||
for (auto& i : values)
|
||||
{
|
||||
if (auto* mb = i.value.getBinaryData())
|
||||
{
|
||||
xml.setAttribute ("base64:" + i.name.toString(), mb->toBase64Encoding());
|
||||
}
|
||||
else
|
||||
{
|
||||
// These types can't be stored as XML!
|
||||
jassert (! i.value.isObject());
|
||||
jassert (! i.value.isMethod());
|
||||
jassert (! i.value.isArray());
|
||||
|
||||
xml.setAttribute (i.name.toString(),
|
||||
i.value.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace juce
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
NamedValueSet::NamedValue::NamedValue() noexcept {}
|
||||
NamedValueSet::NamedValue::~NamedValue() noexcept {}
|
||||
|
||||
NamedValueSet::NamedValue::NamedValue (const Identifier& n, const var& v) : name (n), value (v) {}
|
||||
NamedValueSet::NamedValue::NamedValue (const NamedValue& other) : NamedValue (other.name, other.value) {}
|
||||
|
||||
NamedValueSet::NamedValue::NamedValue (NamedValue&& other) noexcept
|
||||
: NamedValue (std::move (other.name),
|
||||
std::move (other.value))
|
||||
{}
|
||||
|
||||
NamedValueSet::NamedValue::NamedValue (const Identifier& n, var&& v) noexcept
|
||||
: name (n), value (std::move (v))
|
||||
{
|
||||
}
|
||||
|
||||
NamedValueSet::NamedValue::NamedValue (Identifier&& n, var&& v) noexcept
|
||||
: name (std::move (n)),
|
||||
value (std::move (v))
|
||||
{}
|
||||
|
||||
NamedValueSet::NamedValue& NamedValueSet::NamedValue::operator= (NamedValue&& other) noexcept
|
||||
{
|
||||
name = std::move (other.name);
|
||||
value = std::move (other.value);
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool NamedValueSet::NamedValue::operator== (const NamedValue& other) const noexcept { return name == other.name && value == other.value; }
|
||||
bool NamedValueSet::NamedValue::operator!= (const NamedValue& other) const noexcept { return ! operator== (other); }
|
||||
|
||||
//==============================================================================
|
||||
NamedValueSet::NamedValueSet() noexcept {}
|
||||
NamedValueSet::~NamedValueSet() noexcept {}
|
||||
|
||||
NamedValueSet::NamedValueSet (const NamedValueSet& other) : values (other.values) {}
|
||||
|
||||
NamedValueSet::NamedValueSet (NamedValueSet&& other) noexcept
|
||||
: values (std::move (other.values)) {}
|
||||
|
||||
NamedValueSet::NamedValueSet (std::initializer_list<NamedValue> list)
|
||||
: values (std::move (list))
|
||||
{
|
||||
}
|
||||
|
||||
NamedValueSet& NamedValueSet::operator= (const NamedValueSet& other)
|
||||
{
|
||||
clear();
|
||||
values = other.values;
|
||||
return *this;
|
||||
}
|
||||
|
||||
NamedValueSet& NamedValueSet::operator= (NamedValueSet&& other) noexcept
|
||||
{
|
||||
other.values.swapWith (values);
|
||||
return *this;
|
||||
}
|
||||
|
||||
void NamedValueSet::clear()
|
||||
{
|
||||
values.clear();
|
||||
}
|
||||
|
||||
bool NamedValueSet::operator== (const NamedValueSet& other) const noexcept
|
||||
{
|
||||
auto num = values.size();
|
||||
|
||||
if (num != other.values.size())
|
||||
return false;
|
||||
|
||||
for (int i = 0; i < num; ++i)
|
||||
{
|
||||
// optimise for the case where the keys are in the same order
|
||||
if (values.getReference(i).name == other.values.getReference(i).name)
|
||||
{
|
||||
if (values.getReference(i).value != other.values.getReference(i).value)
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// if we encounter keys that are in a different order, search remaining items by brute force..
|
||||
for (int j = i; j < num; ++j)
|
||||
{
|
||||
if (auto* otherVal = other.getVarPointer (values.getReference(j).name))
|
||||
if (values.getReference(j).value == *otherVal)
|
||||
continue;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NamedValueSet::operator!= (const NamedValueSet& other) const noexcept { return ! operator== (other); }
|
||||
|
||||
int NamedValueSet::size() const noexcept { return values.size(); }
|
||||
bool NamedValueSet::isEmpty() const noexcept { return values.isEmpty(); }
|
||||
|
||||
static const var& getNullVarRef() noexcept
|
||||
{
|
||||
static var nullVar;
|
||||
return nullVar;
|
||||
}
|
||||
|
||||
const var& NamedValueSet::operator[] (const Identifier& name) const noexcept
|
||||
{
|
||||
if (auto* v = getVarPointer (name))
|
||||
return *v;
|
||||
|
||||
return getNullVarRef();
|
||||
}
|
||||
|
||||
var NamedValueSet::getWithDefault (const Identifier& name, const var& defaultReturnValue) const
|
||||
{
|
||||
if (auto* v = getVarPointer (name))
|
||||
return *v;
|
||||
|
||||
return defaultReturnValue;
|
||||
}
|
||||
|
||||
var* NamedValueSet::getVarPointer (const Identifier& name) noexcept
|
||||
{
|
||||
for (auto& i : values)
|
||||
if (i.name == name)
|
||||
return &(i.value);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
const var* NamedValueSet::getVarPointer (const Identifier& name) const noexcept
|
||||
{
|
||||
for (auto& i : values)
|
||||
if (i.name == name)
|
||||
return &(i.value);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
bool NamedValueSet::set (const Identifier& name, var&& newValue)
|
||||
{
|
||||
if (auto* v = getVarPointer (name))
|
||||
{
|
||||
if (v->equalsWithSameType (newValue))
|
||||
return false;
|
||||
|
||||
*v = std::move (newValue);
|
||||
return true;
|
||||
}
|
||||
|
||||
values.add ({ name, std::move (newValue) });
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NamedValueSet::set (const Identifier& name, const var& newValue)
|
||||
{
|
||||
if (auto* v = getVarPointer (name))
|
||||
{
|
||||
if (v->equalsWithSameType (newValue))
|
||||
return false;
|
||||
|
||||
*v = newValue;
|
||||
return true;
|
||||
}
|
||||
|
||||
values.add ({ name, newValue });
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NamedValueSet::contains (const Identifier& name) const noexcept
|
||||
{
|
||||
return getVarPointer (name) != nullptr;
|
||||
}
|
||||
|
||||
int NamedValueSet::indexOf (const Identifier& name) const noexcept
|
||||
{
|
||||
auto numValues = values.size();
|
||||
|
||||
for (int i = 0; i < numValues; ++i)
|
||||
if (values.getReference(i).name == name)
|
||||
return i;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool NamedValueSet::remove (const Identifier& name)
|
||||
{
|
||||
auto numValues = values.size();
|
||||
|
||||
for (int i = 0; i < numValues; ++i)
|
||||
{
|
||||
if (values.getReference(i).name == name)
|
||||
{
|
||||
values.remove (i);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Identifier NamedValueSet::getName (const int index) const noexcept
|
||||
{
|
||||
if (isPositiveAndBelow (index, values.size()))
|
||||
return values.getReference (index).name;
|
||||
|
||||
jassertfalse;
|
||||
return {};
|
||||
}
|
||||
|
||||
const var& NamedValueSet::getValueAt (const int index) const noexcept
|
||||
{
|
||||
if (isPositiveAndBelow (index, values.size()))
|
||||
return values.getReference (index).value;
|
||||
|
||||
jassertfalse;
|
||||
return getNullVarRef();
|
||||
}
|
||||
|
||||
var* NamedValueSet::getVarPointerAt (int index) noexcept
|
||||
{
|
||||
if (isPositiveAndBelow (index, values.size()))
|
||||
return &(values.getReference (index).value);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
const var* NamedValueSet::getVarPointerAt (int index) const noexcept
|
||||
{
|
||||
if (isPositiveAndBelow (index, values.size()))
|
||||
return &(values.getReference (index).value);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
void NamedValueSet::setFromXmlAttributes (const XmlElement& xml)
|
||||
{
|
||||
values.clearQuick();
|
||||
|
||||
for (auto* att = xml.attributes.get(); att != nullptr; att = att->nextListItem)
|
||||
{
|
||||
if (att->name.toString().startsWith ("base64:"))
|
||||
{
|
||||
MemoryBlock mb;
|
||||
|
||||
if (mb.fromBase64Encoding (att->value))
|
||||
{
|
||||
values.add ({ att->name.toString().substring (7), var (mb) });
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
values.add ({ att->name, var (att->value) });
|
||||
}
|
||||
}
|
||||
|
||||
void NamedValueSet::copyToXmlAttributes (XmlElement& xml) const
|
||||
{
|
||||
for (auto& i : values)
|
||||
{
|
||||
if (auto* mb = i.value.getBinaryData())
|
||||
{
|
||||
xml.setAttribute ("base64:" + i.name.toString(), mb->toBase64Encoding());
|
||||
}
|
||||
else
|
||||
{
|
||||
// These types can't be stored as XML!
|
||||
jassert (! i.value.isObject());
|
||||
jassert (! i.value.isMethod());
|
||||
jassert (! i.value.isArray());
|
||||
|
||||
xml.setAttribute (i.name.toString(),
|
||||
i.value.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace juce
|
||||
|
@ -1,185 +1,185 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/** Holds a set of named var objects.
|
||||
|
||||
This can be used as a basic structure to hold a set of var object, which can
|
||||
be retrieved by using their identifier.
|
||||
|
||||
@tags{Core}
|
||||
*/
|
||||
class JUCE_API NamedValueSet
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Structure for a named var object */
|
||||
struct JUCE_API NamedValue
|
||||
{
|
||||
NamedValue() noexcept;
|
||||
~NamedValue() noexcept;
|
||||
|
||||
NamedValue (const Identifier& name, const var& value);
|
||||
NamedValue (const Identifier& name, var&& value) noexcept;
|
||||
NamedValue (Identifier&& name, var&& value) noexcept;
|
||||
|
||||
NamedValue (const NamedValue&);
|
||||
NamedValue (NamedValue&&) noexcept;
|
||||
NamedValue& operator= (NamedValue&&) noexcept;
|
||||
|
||||
bool operator== (const NamedValue&) const noexcept;
|
||||
bool operator!= (const NamedValue&) const noexcept;
|
||||
|
||||
Identifier name;
|
||||
var value;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** Creates an empty set. */
|
||||
NamedValueSet() noexcept;
|
||||
|
||||
NamedValueSet (const NamedValueSet&);
|
||||
NamedValueSet (NamedValueSet&&) noexcept;
|
||||
NamedValueSet& operator= (const NamedValueSet&);
|
||||
NamedValueSet& operator= (NamedValueSet&&) noexcept;
|
||||
|
||||
/** Creates a NamedValueSet from a list of names and properties. */
|
||||
NamedValueSet (std::initializer_list<NamedValue>);
|
||||
|
||||
/** Destructor. */
|
||||
~NamedValueSet() noexcept;
|
||||
|
||||
/** Two NamedValueSets are considered equal if they contain all the same key/value
|
||||
pairs, regardless of the order.
|
||||
*/
|
||||
bool operator== (const NamedValueSet&) const noexcept;
|
||||
bool operator!= (const NamedValueSet&) const noexcept;
|
||||
|
||||
const NamedValueSet::NamedValue* begin() const noexcept { return values.begin(); }
|
||||
const NamedValueSet::NamedValue* end() const noexcept { return values.end(); }
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the total number of values that the set contains. */
|
||||
int size() const noexcept;
|
||||
|
||||
/** Returns true if the set is empty. */
|
||||
bool isEmpty() const noexcept;
|
||||
|
||||
/** Returns the value of a named item.
|
||||
If the name isn't found, this will return a void variant.
|
||||
*/
|
||||
const var& operator[] (const Identifier& name) const noexcept;
|
||||
|
||||
/** Tries to return the named value, but if no such value is found, this will
|
||||
instead return the supplied default value.
|
||||
*/
|
||||
var getWithDefault (const Identifier& name, const var& defaultReturnValue) const;
|
||||
|
||||
/** Changes or adds a named value.
|
||||
@returns true if a value was changed or added; false if the
|
||||
value was already set the value passed-in.
|
||||
*/
|
||||
bool set (const Identifier& name, const var& newValue);
|
||||
|
||||
/** Changes or adds a named value.
|
||||
@returns true if a value was changed or added; false if the
|
||||
value was already set the value passed-in.
|
||||
*/
|
||||
bool set (const Identifier& name, var&& newValue);
|
||||
|
||||
/** Returns true if the set contains an item with the specified name. */
|
||||
bool contains (const Identifier& name) const noexcept;
|
||||
|
||||
/** Removes a value from the set.
|
||||
@returns true if a value was removed; false if there was no value
|
||||
with the name that was given.
|
||||
*/
|
||||
bool remove (const Identifier& name);
|
||||
|
||||
/** Returns the name of the value at a given index.
|
||||
The index must be between 0 and size() - 1.
|
||||
*/
|
||||
Identifier getName (int index) const noexcept;
|
||||
|
||||
/** Returns a pointer to the var that holds a named value, or null if there is
|
||||
no value with this name.
|
||||
|
||||
Do not use this method unless you really need access to the internal var object
|
||||
for some reason - for normal reading and writing always prefer operator[]() and set().
|
||||
Also note that the pointer returned may become invalid as soon as any subsequent
|
||||
methods are called on the NamedValueSet.
|
||||
*/
|
||||
var* getVarPointer (const Identifier& name) noexcept;
|
||||
|
||||
/** Returns a pointer to the var that holds a named value, or null if there is
|
||||
no value with this name.
|
||||
|
||||
Do not use this method unless you really need access to the internal var object
|
||||
for some reason - for normal reading and writing always prefer operator[]() and set().
|
||||
Also note that the pointer returned may become invalid as soon as any subsequent
|
||||
methods are called on the NamedValueSet.
|
||||
*/
|
||||
const var* getVarPointer (const Identifier& name) const noexcept;
|
||||
|
||||
/** Returns the value of the item at a given index.
|
||||
The index must be between 0 and size() - 1.
|
||||
*/
|
||||
const var& getValueAt (int index) const noexcept;
|
||||
|
||||
/** Returns the value of the item at a given index.
|
||||
The index must be between 0 and size() - 1, or this will return a nullptr
|
||||
Also note that the pointer returned may become invalid as soon as any subsequent
|
||||
methods are called on the NamedValueSet.
|
||||
*/
|
||||
var* getVarPointerAt (int index) noexcept;
|
||||
|
||||
/** Returns the value of the item at a given index.
|
||||
The index must be between 0 and size() - 1, or this will return a nullptr
|
||||
Also note that the pointer returned may become invalid as soon as any subsequent
|
||||
methods are called on the NamedValueSet.
|
||||
*/
|
||||
const var* getVarPointerAt (int index) const noexcept;
|
||||
|
||||
/** Returns the index of the given name, or -1 if it's not found. */
|
||||
int indexOf (const Identifier& name) const noexcept;
|
||||
|
||||
/** Removes all values. */
|
||||
void clear();
|
||||
|
||||
//==============================================================================
|
||||
/** Sets properties to the values of all of an XML element's attributes. */
|
||||
void setFromXmlAttributes (const XmlElement& xml);
|
||||
|
||||
/** Sets attributes in an XML element corresponding to each of this object's
|
||||
properties.
|
||||
*/
|
||||
void copyToXmlAttributes (XmlElement& xml) const;
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
Array<NamedValue> values;
|
||||
};
|
||||
|
||||
} // namespace juce
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/** Holds a set of named var objects.
|
||||
|
||||
This can be used as a basic structure to hold a set of var object, which can
|
||||
be retrieved by using their identifier.
|
||||
|
||||
@tags{Core}
|
||||
*/
|
||||
class JUCE_API NamedValueSet
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Structure for a named var object */
|
||||
struct JUCE_API NamedValue
|
||||
{
|
||||
NamedValue() noexcept;
|
||||
~NamedValue() noexcept;
|
||||
|
||||
NamedValue (const Identifier& name, const var& value);
|
||||
NamedValue (const Identifier& name, var&& value) noexcept;
|
||||
NamedValue (Identifier&& name, var&& value) noexcept;
|
||||
|
||||
NamedValue (const NamedValue&);
|
||||
NamedValue (NamedValue&&) noexcept;
|
||||
NamedValue& operator= (NamedValue&&) noexcept;
|
||||
|
||||
bool operator== (const NamedValue&) const noexcept;
|
||||
bool operator!= (const NamedValue&) const noexcept;
|
||||
|
||||
Identifier name;
|
||||
var value;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** Creates an empty set. */
|
||||
NamedValueSet() noexcept;
|
||||
|
||||
NamedValueSet (const NamedValueSet&);
|
||||
NamedValueSet (NamedValueSet&&) noexcept;
|
||||
NamedValueSet& operator= (const NamedValueSet&);
|
||||
NamedValueSet& operator= (NamedValueSet&&) noexcept;
|
||||
|
||||
/** Creates a NamedValueSet from a list of names and properties. */
|
||||
NamedValueSet (std::initializer_list<NamedValue>);
|
||||
|
||||
/** Destructor. */
|
||||
~NamedValueSet() noexcept;
|
||||
|
||||
/** Two NamedValueSets are considered equal if they contain all the same key/value
|
||||
pairs, regardless of the order.
|
||||
*/
|
||||
bool operator== (const NamedValueSet&) const noexcept;
|
||||
bool operator!= (const NamedValueSet&) const noexcept;
|
||||
|
||||
const NamedValueSet::NamedValue* begin() const noexcept { return values.begin(); }
|
||||
const NamedValueSet::NamedValue* end() const noexcept { return values.end(); }
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the total number of values that the set contains. */
|
||||
int size() const noexcept;
|
||||
|
||||
/** Returns true if the set is empty. */
|
||||
bool isEmpty() const noexcept;
|
||||
|
||||
/** Returns the value of a named item.
|
||||
If the name isn't found, this will return a void variant.
|
||||
*/
|
||||
const var& operator[] (const Identifier& name) const noexcept;
|
||||
|
||||
/** Tries to return the named value, but if no such value is found, this will
|
||||
instead return the supplied default value.
|
||||
*/
|
||||
var getWithDefault (const Identifier& name, const var& defaultReturnValue) const;
|
||||
|
||||
/** Changes or adds a named value.
|
||||
@returns true if a value was changed or added; false if the
|
||||
value was already set the value passed-in.
|
||||
*/
|
||||
bool set (const Identifier& name, const var& newValue);
|
||||
|
||||
/** Changes or adds a named value.
|
||||
@returns true if a value was changed or added; false if the
|
||||
value was already set the value passed-in.
|
||||
*/
|
||||
bool set (const Identifier& name, var&& newValue);
|
||||
|
||||
/** Returns true if the set contains an item with the specified name. */
|
||||
bool contains (const Identifier& name) const noexcept;
|
||||
|
||||
/** Removes a value from the set.
|
||||
@returns true if a value was removed; false if there was no value
|
||||
with the name that was given.
|
||||
*/
|
||||
bool remove (const Identifier& name);
|
||||
|
||||
/** Returns the name of the value at a given index.
|
||||
The index must be between 0 and size() - 1.
|
||||
*/
|
||||
Identifier getName (int index) const noexcept;
|
||||
|
||||
/** Returns a pointer to the var that holds a named value, or null if there is
|
||||
no value with this name.
|
||||
|
||||
Do not use this method unless you really need access to the internal var object
|
||||
for some reason - for normal reading and writing always prefer operator[]() and set().
|
||||
Also note that the pointer returned may become invalid as soon as any subsequent
|
||||
methods are called on the NamedValueSet.
|
||||
*/
|
||||
var* getVarPointer (const Identifier& name) noexcept;
|
||||
|
||||
/** Returns a pointer to the var that holds a named value, or null if there is
|
||||
no value with this name.
|
||||
|
||||
Do not use this method unless you really need access to the internal var object
|
||||
for some reason - for normal reading and writing always prefer operator[]() and set().
|
||||
Also note that the pointer returned may become invalid as soon as any subsequent
|
||||
methods are called on the NamedValueSet.
|
||||
*/
|
||||
const var* getVarPointer (const Identifier& name) const noexcept;
|
||||
|
||||
/** Returns the value of the item at a given index.
|
||||
The index must be between 0 and size() - 1.
|
||||
*/
|
||||
const var& getValueAt (int index) const noexcept;
|
||||
|
||||
/** Returns the value of the item at a given index.
|
||||
The index must be between 0 and size() - 1, or this will return a nullptr
|
||||
Also note that the pointer returned may become invalid as soon as any subsequent
|
||||
methods are called on the NamedValueSet.
|
||||
*/
|
||||
var* getVarPointerAt (int index) noexcept;
|
||||
|
||||
/** Returns the value of the item at a given index.
|
||||
The index must be between 0 and size() - 1, or this will return a nullptr
|
||||
Also note that the pointer returned may become invalid as soon as any subsequent
|
||||
methods are called on the NamedValueSet.
|
||||
*/
|
||||
const var* getVarPointerAt (int index) const noexcept;
|
||||
|
||||
/** Returns the index of the given name, or -1 if it's not found. */
|
||||
int indexOf (const Identifier& name) const noexcept;
|
||||
|
||||
/** Removes all values. */
|
||||
void clear();
|
||||
|
||||
//==============================================================================
|
||||
/** Sets properties to the values of all of an XML element's attributes. */
|
||||
void setFromXmlAttributes (const XmlElement& xml);
|
||||
|
||||
/** Sets attributes in an XML element corresponding to each of this object's
|
||||
properties.
|
||||
*/
|
||||
void copyToXmlAttributes (XmlElement& xml) const;
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
Array<NamedValue> values;
|
||||
};
|
||||
|
||||
} // namespace juce
|
||||
|
401
deps/juce/modules/juce_core/containers/juce_Optional.h
vendored
Normal file
401
deps/juce/modules/juce_core/containers/juce_Optional.h
vendored
Normal file
@ -0,0 +1,401 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
namespace detail
|
||||
{
|
||||
namespace adlSwap
|
||||
{
|
||||
using std::swap;
|
||||
|
||||
template <typename T>
|
||||
constexpr auto isNothrowSwappable = noexcept (swap (std::declval<T&>(), std::declval<T&>()));
|
||||
} // namespace adlSwap
|
||||
} // namespace detail
|
||||
|
||||
/** A type representing the null state of an Optional.
|
||||
Similar to std::nullopt_t.
|
||||
*/
|
||||
struct Nullopt
|
||||
{
|
||||
explicit constexpr Nullopt (int) {}
|
||||
};
|
||||
|
||||
/** An object that can be used when constructing and comparing Optional instances.
|
||||
Similar to std::nullopt.
|
||||
*/
|
||||
constexpr Nullopt nullopt { 0 };
|
||||
|
||||
// Without this, our tests can emit "unreachable code" warnings during
|
||||
// link time code generation.
|
||||
JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4702)
|
||||
|
||||
/**
|
||||
A simple optional type.
|
||||
|
||||
Has similar (not necessarily identical!) semantics to std::optional.
|
||||
|
||||
This is intended to stand-in for std::optional while JUCE's minimum
|
||||
supported language standard is lower than C++17. When the minimum language
|
||||
standard moves to C++17, this class will probably be deprecated, in much
|
||||
the same way that juce::ScopedPointer was deprecated in favour of
|
||||
std::unique_ptr after C++11.
|
||||
|
||||
This isn't really intended to be used by JUCE clients. Instead, it's to be
|
||||
used internally in JUCE code, with an API close-enough to std::optional
|
||||
that the types can be swapped with fairly minor disruption at some point in
|
||||
the future, but *without breaking any public APIs*.
|
||||
|
||||
@tags{Core}
|
||||
*/
|
||||
template <typename Value>
|
||||
class Optional
|
||||
{
|
||||
template <typename T, typename U>
|
||||
struct NotConstructibleFromSimilarType
|
||||
{
|
||||
static constexpr auto value = ! std::is_constructible<T, Optional<U>&>::value
|
||||
&& ! std::is_constructible<T, const Optional<U>&>::value
|
||||
&& ! std::is_constructible<T, Optional<U>&&>::value
|
||||
&& ! std::is_constructible<T, const Optional<U>&&>::value
|
||||
&& ! std::is_convertible<Optional<U>&, T>::value
|
||||
&& ! std::is_convertible<const Optional<U>&, T>::value
|
||||
&& ! std::is_convertible<Optional<U>&&, T>::value
|
||||
&& ! std::is_convertible<const Optional<U>&&, T>::value;
|
||||
};
|
||||
|
||||
template <typename T, typename U>
|
||||
using OptionalCopyConstructorEnabled = std::enable_if_t<std::is_constructible<T, const U&>::value && NotConstructibleFromSimilarType<T, U>::value>;
|
||||
|
||||
template <typename T, typename U>
|
||||
using OptionalMoveConstructorEnabled = std::enable_if_t<std::is_constructible<T, U&&>::value && NotConstructibleFromSimilarType<T, U>::value>;
|
||||
|
||||
template <typename T, typename U>
|
||||
static auto notAssignableFromSimilarType = NotConstructibleFromSimilarType<T, U>::value
|
||||
&& ! std::is_assignable<T&, Optional<U>&>::value
|
||||
&& ! std::is_assignable<T&, const Optional<U>&>::value
|
||||
&& ! std::is_assignable<T&, Optional<U>&&>::value
|
||||
&& ! std::is_assignable<T&, const Optional<U>&&>::value;
|
||||
|
||||
template <typename T, typename U>
|
||||
using OptionalCopyAssignmentEnabled = std::enable_if_t<std::is_constructible<T, const U&>::value
|
||||
&& std::is_assignable<T&, const U&>::value
|
||||
&& NotConstructibleFromSimilarType<T, U>::value>;
|
||||
|
||||
template <typename T, typename U>
|
||||
using OptionalMoveAssignmentEnabled = std::enable_if_t<std::is_constructible<T, U>::value
|
||||
&& std::is_nothrow_assignable<T&, U>::value
|
||||
&& NotConstructibleFromSimilarType<T, U>::value>;
|
||||
|
||||
public:
|
||||
Optional() : placeholder() {}
|
||||
|
||||
Optional (Nullopt) noexcept : placeholder() {}
|
||||
|
||||
template <typename U = Value,
|
||||
typename = std::enable_if_t<std::is_constructible<Value, U&&>::value
|
||||
&& ! std::is_same<std::decay_t<U>, Optional>::value>>
|
||||
Optional (U&& value) noexcept (noexcept (Value (std::forward<U> (value))))
|
||||
: storage (std::forward<U> (value)), valid (true)
|
||||
{
|
||||
}
|
||||
|
||||
Optional (Optional&& other) noexcept (noexcept (std::declval<Optional>().constructFrom (other)))
|
||||
: placeholder()
|
||||
{
|
||||
constructFrom (other);
|
||||
}
|
||||
|
||||
Optional (const Optional& other)
|
||||
: placeholder(), valid (other.valid)
|
||||
{
|
||||
if (valid)
|
||||
new (&storage) Value (*other);
|
||||
}
|
||||
|
||||
template <typename Other, typename = OptionalMoveConstructorEnabled<Value, Other>>
|
||||
Optional (Optional<Other>&& other) noexcept (noexcept (std::declval<Optional>().constructFrom (other)))
|
||||
: placeholder()
|
||||
{
|
||||
constructFrom (other);
|
||||
}
|
||||
|
||||
template <typename Other, typename = OptionalCopyConstructorEnabled<Value, Other>>
|
||||
Optional (const Optional<Other>& other)
|
||||
: placeholder(), valid (other.hasValue())
|
||||
{
|
||||
if (valid)
|
||||
new (&storage) Value (*other);
|
||||
}
|
||||
|
||||
Optional& operator= (Nullopt) noexcept
|
||||
{
|
||||
reset();
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename U = Value,
|
||||
typename = std::enable_if_t<std::is_nothrow_move_constructible<U>::value
|
||||
&& std::is_nothrow_move_assignable<U>::value>>
|
||||
Optional& operator= (Optional&& other) noexcept (noexcept (std::declval<Optional>().assign (std::declval<Optional&>())))
|
||||
{
|
||||
assign (other);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename U = Value,
|
||||
typename = std::enable_if_t<! std::is_same<std::decay_t<U>, Optional>::value
|
||||
&& std::is_constructible<Value, U>::value
|
||||
&& std::is_assignable<Value&, U>::value
|
||||
&& (! std::is_scalar<Value>::value || ! std::is_same<std::decay_t<U>, Value>::value)>>
|
||||
Optional& operator= (U&& value)
|
||||
{
|
||||
if (valid)
|
||||
**this = std::forward<U> (value);
|
||||
else
|
||||
new (&storage) Value (std::forward<U> (value));
|
||||
|
||||
valid = true;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** Maintains the strong exception safety guarantee. */
|
||||
Optional& operator= (const Optional& other)
|
||||
{
|
||||
auto copy = other;
|
||||
assign (copy);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename Other, typename = OptionalMoveAssignmentEnabled<Value, Other>>
|
||||
Optional& operator= (Optional<Other>&& other) noexcept (noexcept (std::declval<Optional>().assign (other)))
|
||||
{
|
||||
assign (other);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** Maintains the strong exception safety guarantee. */
|
||||
template <typename Other, typename = OptionalCopyAssignmentEnabled<Value, Other>>
|
||||
Optional& operator= (const Optional<Other>& other)
|
||||
{
|
||||
auto copy = other;
|
||||
assign (copy);
|
||||
return *this;
|
||||
}
|
||||
|
||||
~Optional() noexcept
|
||||
{
|
||||
reset();
|
||||
}
|
||||
|
||||
Value* operator->() noexcept { return reinterpret_cast< Value*> (&storage); }
|
||||
const Value* operator->() const noexcept { return reinterpret_cast<const Value*> (&storage); }
|
||||
|
||||
Value& operator*() noexcept { return *operator->(); }
|
||||
const Value& operator*() const noexcept { return *operator->(); }
|
||||
|
||||
explicit operator bool() const noexcept { return valid; }
|
||||
bool hasValue() const noexcept { return valid; }
|
||||
|
||||
void reset()
|
||||
{
|
||||
if (std::exchange (valid, false))
|
||||
operator*().~Value();
|
||||
}
|
||||
|
||||
/** Like std::optional::value_or */
|
||||
template <typename U>
|
||||
Value orFallback (U&& fallback) const { return *this ? **this : std::forward<U> (fallback); }
|
||||
|
||||
template <typename... Args>
|
||||
Value& emplace (Args&&... args)
|
||||
{
|
||||
reset();
|
||||
new (&storage) Value (std::forward<Args> (args)...);
|
||||
valid = true;
|
||||
return **this;
|
||||
}
|
||||
|
||||
void swap (Optional& other) noexcept (std::is_nothrow_move_constructible<Value>::value
|
||||
&& detail::adlSwap::isNothrowSwappable<Value>)
|
||||
{
|
||||
if (hasValue() && other.hasValue())
|
||||
{
|
||||
using std::swap;
|
||||
swap (**this, *other);
|
||||
}
|
||||
else if (hasValue() || other.hasValue())
|
||||
{
|
||||
(hasValue() ? other : *this).constructFrom (hasValue() ? *this : other);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
template <typename Other>
|
||||
void constructFrom (Optional<Other>& other) noexcept (noexcept (Value (std::move (*other))))
|
||||
{
|
||||
if (! other.hasValue())
|
||||
return;
|
||||
|
||||
new (&storage) Value (std::move (*other));
|
||||
valid = true;
|
||||
other.reset();
|
||||
}
|
||||
|
||||
template <typename Other>
|
||||
void assign (Optional<Other>& other) noexcept (noexcept (std::declval<Value&>() = std::move (*other)) && noexcept (std::declval<Optional>().constructFrom (other)))
|
||||
{
|
||||
if (valid)
|
||||
{
|
||||
if (other.hasValue())
|
||||
{
|
||||
**this = std::move (*other);
|
||||
other.reset();
|
||||
}
|
||||
else
|
||||
{
|
||||
reset();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
constructFrom (other);
|
||||
}
|
||||
}
|
||||
|
||||
union
|
||||
{
|
||||
char placeholder;
|
||||
Value storage;
|
||||
};
|
||||
bool valid = false;
|
||||
};
|
||||
|
||||
JUCE_END_IGNORE_WARNINGS_MSVC
|
||||
|
||||
template <typename Value>
|
||||
Optional<std::decay_t<Value>> makeOptional (Value&& v)
|
||||
{
|
||||
return std::forward<Value> (v);
|
||||
}
|
||||
|
||||
template <class T, class U>
|
||||
bool operator== (const Optional<T>& lhs, const Optional<U>& rhs)
|
||||
{
|
||||
if (lhs.hasValue() != rhs.hasValue()) return false;
|
||||
if (! lhs.hasValue()) return true;
|
||||
return *lhs == *rhs;
|
||||
}
|
||||
|
||||
template <class T, class U>
|
||||
bool operator!= (const Optional<T>& lhs, const Optional<U>& rhs)
|
||||
{
|
||||
if (lhs.hasValue() != rhs.hasValue()) return true;
|
||||
if (! lhs.hasValue()) return false;
|
||||
return *lhs != *rhs;
|
||||
}
|
||||
|
||||
template <class T, class U>
|
||||
bool operator< (const Optional<T>& lhs, const Optional<U>& rhs)
|
||||
{
|
||||
if (! rhs.hasValue()) return false;
|
||||
if (! lhs.hasValue()) return true;
|
||||
return *lhs < *rhs;
|
||||
}
|
||||
|
||||
template <class T, class U>
|
||||
bool operator<= (const Optional<T>& lhs, const Optional<U>& rhs)
|
||||
{
|
||||
if (! lhs.hasValue()) return true;
|
||||
if (! rhs.hasValue()) return false;
|
||||
return *lhs <= *rhs;
|
||||
}
|
||||
|
||||
template <class T, class U>
|
||||
bool operator> (const Optional<T>& lhs, const Optional<U>& rhs)
|
||||
{
|
||||
if (! lhs.hasValue()) return false;
|
||||
if (! rhs.hasValue()) return true;
|
||||
return *lhs > *rhs;
|
||||
}
|
||||
|
||||
template <class T, class U>
|
||||
bool operator>= (const Optional<T>& lhs, const Optional<U>& rhs)
|
||||
{
|
||||
if (! rhs.hasValue()) return true;
|
||||
if (! lhs.hasValue()) return false;
|
||||
return *lhs >= *rhs;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
bool operator== (const Optional<T>& opt, Nullopt) noexcept { return ! opt.hasValue(); }
|
||||
template <class T>
|
||||
bool operator== (Nullopt, const Optional<T>& opt) noexcept { return ! opt.hasValue(); }
|
||||
template <class T>
|
||||
bool operator!= (const Optional<T>& opt, Nullopt) noexcept { return opt.hasValue(); }
|
||||
template <class T>
|
||||
bool operator!= (Nullopt, const Optional<T>& opt) noexcept { return opt.hasValue(); }
|
||||
template <class T>
|
||||
bool operator< (const Optional<T>&, Nullopt) noexcept { return false; }
|
||||
template <class T>
|
||||
bool operator< (Nullopt, const Optional<T>& opt) noexcept { return opt.hasValue(); }
|
||||
template <class T>
|
||||
bool operator<= (const Optional<T>& opt, Nullopt) noexcept { return ! opt.hasValue(); }
|
||||
template <class T>
|
||||
bool operator<= (Nullopt, const Optional<T>&) noexcept { return true; }
|
||||
template <class T>
|
||||
bool operator> (const Optional<T>& opt, Nullopt) noexcept { return opt.hasValue(); }
|
||||
template <class T>
|
||||
bool operator> (Nullopt, const Optional<T>&) noexcept { return false; }
|
||||
template <class T>
|
||||
bool operator>= (const Optional<T>&, Nullopt) noexcept { return true; }
|
||||
template <class T>
|
||||
bool operator>= (Nullopt, const Optional<T>& opt) noexcept { return ! opt.hasValue(); }
|
||||
|
||||
template <class T, class U>
|
||||
bool operator== (const Optional<T>& opt, const U& value) { return opt.hasValue() ? *opt == value : false; }
|
||||
template <class T, class U>
|
||||
bool operator== (const T& value, const Optional<U>& opt) { return opt.hasValue() ? value == *opt : false; }
|
||||
template <class T, class U>
|
||||
bool operator!= (const Optional<T>& opt, const U& value) { return opt.hasValue() ? *opt != value : true; }
|
||||
template <class T, class U>
|
||||
bool operator!= (const T& value, const Optional<U>& opt) { return opt.hasValue() ? value != *opt : true; }
|
||||
template <class T, class U>
|
||||
bool operator< (const Optional<T>& opt, const U& value) { return opt.hasValue() ? *opt < value : true; }
|
||||
template <class T, class U>
|
||||
bool operator< (const T& value, const Optional<U>& opt) { return opt.hasValue() ? value < *opt : false; }
|
||||
template <class T, class U>
|
||||
bool operator<= (const Optional<T>& opt, const U& value) { return opt.hasValue() ? *opt <= value : true; }
|
||||
template <class T, class U>
|
||||
bool operator<= (const T& value, const Optional<U>& opt) { return opt.hasValue() ? value <= *opt : false; }
|
||||
template <class T, class U>
|
||||
bool operator> (const Optional<T>& opt, const U& value) { return opt.hasValue() ? *opt > value : false; }
|
||||
template <class T, class U>
|
||||
bool operator> (const T& value, const Optional<U>& opt) { return opt.hasValue() ? value > *opt : true; }
|
||||
template <class T, class U>
|
||||
bool operator>= (const Optional<T>& opt, const U& value) { return opt.hasValue() ? *opt >= value : false; }
|
||||
template <class T, class U>
|
||||
bool operator>= (const T& value, const Optional<U>& opt) { return opt.hasValue() ? value >= *opt : true; }
|
||||
|
||||
} // namespace juce
|
627
deps/juce/modules/juce_core/containers/juce_Optional_test.cpp
vendored
Normal file
627
deps/juce/modules/juce_core/containers/juce_Optional_test.cpp
vendored
Normal file
@ -0,0 +1,627 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
/* Not nested, so that ADL works for the swap function. */
|
||||
struct ThrowOnMoveOrSwap
|
||||
{
|
||||
ThrowOnMoveOrSwap() = default;
|
||||
ThrowOnMoveOrSwap (ThrowOnMoveOrSwap&&) { throw std::bad_alloc{}; }
|
||||
};
|
||||
static void swap (ThrowOnMoveOrSwap&, ThrowOnMoveOrSwap&) { throw std::bad_alloc{}; }
|
||||
|
||||
class OptionalUnitTest : public UnitTest
|
||||
{
|
||||
public:
|
||||
OptionalUnitTest() : UnitTest ("Optional", UnitTestCategories::containers) {}
|
||||
|
||||
void runTest() override
|
||||
{
|
||||
beginTest ("Default-constructed optional is invalid");
|
||||
{
|
||||
Optional<int> o;
|
||||
expect (! o.hasValue());
|
||||
}
|
||||
|
||||
beginTest ("Constructing from Nullopt is invalid");
|
||||
{
|
||||
Optional<int> o (nullopt);
|
||||
expect (! o.hasValue());
|
||||
}
|
||||
|
||||
beginTest ("Optional constructed from value is valid");
|
||||
{
|
||||
Optional<int> o = 5;
|
||||
expect (o.hasValue());
|
||||
expectEquals (*o, 5);
|
||||
}
|
||||
|
||||
using Ptr = std::shared_ptr<int>;
|
||||
const auto makePtr = [] { return std::make_shared<int>(); };
|
||||
|
||||
beginTest ("Constructing from a moved optional calls appropriate member functions");
|
||||
{
|
||||
auto ptr = makePtr();
|
||||
Optional<Ptr> original (ptr);
|
||||
expect (ptr.use_count() == 2);
|
||||
auto other = std::move (original);
|
||||
expect (! original.hasValue());
|
||||
expect (other.hasValue());
|
||||
expect (ptr.use_count() == 2);
|
||||
}
|
||||
|
||||
beginTest ("Moving an empty optional to a populated one destroys the instance");
|
||||
{
|
||||
auto ptr = makePtr();
|
||||
Optional<Ptr> original (ptr);
|
||||
expect (ptr.use_count() == 2);
|
||||
original = Optional<Ptr>();
|
||||
expect (ptr.use_count() == 1);
|
||||
}
|
||||
|
||||
beginTest ("Copying an empty optional to a populated one destroys the instance");
|
||||
{
|
||||
auto ptr = makePtr();
|
||||
Optional<Ptr> original (ptr);
|
||||
expect (ptr.use_count() == 2);
|
||||
Optional<Ptr> empty;
|
||||
original = empty;
|
||||
expect (ptr.use_count() == 1);
|
||||
}
|
||||
|
||||
beginTest ("Moving a populated optional calls appropriate member functions");
|
||||
{
|
||||
auto a = makePtr();
|
||||
auto b = makePtr();
|
||||
|
||||
Optional<Ptr> aOpt (a);
|
||||
Optional<Ptr> bOpt (b);
|
||||
|
||||
expect (a.use_count() == 2);
|
||||
expect (b.use_count() == 2);
|
||||
|
||||
aOpt = std::move (bOpt);
|
||||
|
||||
expect (aOpt.hasValue());
|
||||
expect (! bOpt.hasValue());
|
||||
|
||||
expect (a.use_count() == 1);
|
||||
expect (b.use_count() == 2);
|
||||
}
|
||||
|
||||
beginTest ("Copying a populated optional calls appropriate member functions");
|
||||
{
|
||||
auto a = makePtr();
|
||||
auto b = makePtr();
|
||||
|
||||
Optional<Ptr> aOpt (a);
|
||||
Optional<Ptr> bOpt (b);
|
||||
|
||||
expect (a.use_count() == 2);
|
||||
expect (b.use_count() == 2);
|
||||
|
||||
aOpt = bOpt;
|
||||
|
||||
expect (aOpt.hasValue());
|
||||
expect (bOpt.hasValue());
|
||||
|
||||
expect (a.use_count() == 1);
|
||||
expect (b.use_count() == 3);
|
||||
}
|
||||
|
||||
beginTest ("Moving an empty optional to an empty one does nothing");
|
||||
{
|
||||
Optional<Ptr> original;
|
||||
original = Optional<Ptr>();
|
||||
expect (! original.hasValue());
|
||||
}
|
||||
|
||||
beginTest ("Copying an empty optional to an empty one does nothing");
|
||||
{
|
||||
Optional<Ptr> original;
|
||||
Optional<Ptr> empty;
|
||||
original = empty;
|
||||
expect (! original.hasValue());
|
||||
expect (! empty.hasValue());
|
||||
}
|
||||
|
||||
beginTest ("Moving a populated optional calls appropriate member functions");
|
||||
{
|
||||
auto a = makePtr();
|
||||
|
||||
Optional<Ptr> aOpt (a);
|
||||
Optional<Ptr> empty;
|
||||
|
||||
expect (a.use_count() == 2);
|
||||
|
||||
empty = std::move (aOpt);
|
||||
|
||||
expect (empty.hasValue());
|
||||
expect (! aOpt.hasValue());
|
||||
|
||||
expect (a.use_count() == 2);
|
||||
}
|
||||
|
||||
beginTest ("Copying a populated optional calls appropriate member functions");
|
||||
{
|
||||
auto a = makePtr();
|
||||
|
||||
Optional<Ptr> aOpt (a);
|
||||
Optional<Ptr> empty;
|
||||
|
||||
expect (a.use_count() == 2);
|
||||
|
||||
empty = aOpt;
|
||||
|
||||
expect (aOpt.hasValue());
|
||||
expect (empty.hasValue());
|
||||
|
||||
expect (a.use_count() == 3);
|
||||
}
|
||||
|
||||
struct ThrowOnCopy
|
||||
{
|
||||
ThrowOnCopy() = default;
|
||||
|
||||
// Put into an invalid state and throw
|
||||
ThrowOnCopy (const ThrowOnCopy&)
|
||||
{
|
||||
value = -100;
|
||||
throw std::bad_alloc{};
|
||||
}
|
||||
|
||||
// Put into an invalid state and throw
|
||||
ThrowOnCopy& operator= (const ThrowOnCopy&)
|
||||
{
|
||||
value = -100;
|
||||
throw std::bad_alloc{};
|
||||
}
|
||||
|
||||
ThrowOnCopy (ThrowOnCopy&&) noexcept = default;
|
||||
ThrowOnCopy& operator= (ThrowOnCopy&&) noexcept = default;
|
||||
|
||||
~ThrowOnCopy() = default;
|
||||
|
||||
int value = 0;
|
||||
};
|
||||
|
||||
beginTest ("Strong exception safety is maintained when forwarding over empty object");
|
||||
{
|
||||
bool threw = false;
|
||||
Optional<ThrowOnCopy> a;
|
||||
|
||||
try
|
||||
{
|
||||
ThrowOnCopy t;
|
||||
a = t;
|
||||
}
|
||||
catch (const std::bad_alloc&)
|
||||
{
|
||||
threw = true;
|
||||
}
|
||||
|
||||
expect (threw);
|
||||
expect (! a.hasValue()); // If construction failed, this object should still be well-formed but empty
|
||||
}
|
||||
|
||||
beginTest ("Weak exception safety is maintained when forwarding over populated object");
|
||||
{
|
||||
bool threw = false;
|
||||
Optional<ThrowOnCopy> a = ThrowOnCopy();
|
||||
a->value = 5;
|
||||
|
||||
try
|
||||
{
|
||||
ThrowOnCopy t;
|
||||
a = t;
|
||||
}
|
||||
catch (const std::bad_alloc&)
|
||||
{
|
||||
threw = true;
|
||||
}
|
||||
|
||||
expect (threw);
|
||||
expect (a.hasValue());
|
||||
expect (a->value == -100); // If we assign to an extant object, it's up to that object to provide an exception guarantee
|
||||
}
|
||||
|
||||
beginTest ("Strong exception safety is maintained when copying over empty object");
|
||||
{
|
||||
bool threw = false;
|
||||
Optional<ThrowOnCopy> a;
|
||||
|
||||
try
|
||||
{
|
||||
Optional<ThrowOnCopy> t = ThrowOnCopy{};
|
||||
a = t;
|
||||
}
|
||||
catch (const std::bad_alloc&)
|
||||
{
|
||||
threw = true;
|
||||
}
|
||||
|
||||
expect (threw);
|
||||
expect (! a.hasValue());
|
||||
}
|
||||
|
||||
beginTest ("Strong exception safety is maintained when copying over populated object");
|
||||
{
|
||||
bool threw = false;
|
||||
Optional<ThrowOnCopy> a = ThrowOnCopy();
|
||||
a->value = 5;
|
||||
|
||||
try
|
||||
{
|
||||
Optional<ThrowOnCopy> t = ThrowOnCopy{};
|
||||
a = t;
|
||||
}
|
||||
catch (const std::bad_alloc&)
|
||||
{
|
||||
threw = true;
|
||||
}
|
||||
|
||||
expect (threw);
|
||||
expect (a.hasValue());
|
||||
expect (a->value == 5);
|
||||
}
|
||||
|
||||
beginTest ("Assigning from nullopt clears the instance");
|
||||
{
|
||||
auto ptr = makePtr();
|
||||
Optional<Ptr> a (ptr);
|
||||
expect (ptr.use_count() == 2);
|
||||
a = nullopt;
|
||||
expect (ptr.use_count() == 1);
|
||||
}
|
||||
|
||||
struct Foo {};
|
||||
struct Bar : Foo {};
|
||||
|
||||
beginTest ("Can be constructed from compatible type");
|
||||
{
|
||||
Optional<std::shared_ptr<Foo>> opt { std::make_shared<Bar>() };
|
||||
}
|
||||
|
||||
beginTest ("Can be assigned from compatible type");
|
||||
{
|
||||
Optional<std::shared_ptr<Foo>> opt;
|
||||
opt = std::make_shared<Bar>();
|
||||
}
|
||||
|
||||
beginTest ("Can copy from compatible type");
|
||||
{
|
||||
auto ptr = std::make_shared<Bar>();
|
||||
Optional<std::shared_ptr<Bar>> bar (ptr);
|
||||
Optional<std::shared_ptr<Foo>> foo (bar);
|
||||
expect (ptr.use_count() == 3);
|
||||
}
|
||||
|
||||
beginTest ("Can move from compatible type");
|
||||
{
|
||||
auto ptr = std::make_shared<Bar>();
|
||||
Optional<std::shared_ptr<Foo>> foo (Optional<std::shared_ptr<Bar>> { ptr });
|
||||
expect (ptr.use_count() == 2);
|
||||
}
|
||||
|
||||
beginTest ("Can copy assign from compatible type");
|
||||
{
|
||||
auto ptr = std::make_shared<Bar>();
|
||||
Optional<std::shared_ptr<Bar>> bar (ptr);
|
||||
Optional<std::shared_ptr<Foo>> foo;
|
||||
foo = bar;
|
||||
expect (ptr.use_count() == 3);
|
||||
}
|
||||
|
||||
beginTest ("Can move assign from compatible type");
|
||||
{
|
||||
auto ptr = std::make_shared<Bar>();
|
||||
Optional<std::shared_ptr<Foo>> foo;
|
||||
foo = Optional<std::shared_ptr<Bar>> (ptr);
|
||||
expect (ptr.use_count() == 2);
|
||||
}
|
||||
|
||||
beginTest ("An exception thrown during emplace leaves the optional without a value");
|
||||
{
|
||||
Optional<ThrowOnCopy> opt { ThrowOnCopy{} };
|
||||
bool threw = false;
|
||||
|
||||
try
|
||||
{
|
||||
ThrowOnCopy t;
|
||||
opt.emplace (t);
|
||||
}
|
||||
catch (const std::bad_alloc&)
|
||||
{
|
||||
threw = true;
|
||||
}
|
||||
|
||||
expect (threw);
|
||||
expect (! opt.hasValue());
|
||||
}
|
||||
|
||||
beginTest ("Swap does nothing to two empty optionals");
|
||||
{
|
||||
Optional<Ptr> a, b;
|
||||
expect (! a.hasValue());
|
||||
expect (! b.hasValue());
|
||||
|
||||
a.swap (b);
|
||||
|
||||
expect (! a.hasValue());
|
||||
expect (! b.hasValue());
|
||||
}
|
||||
|
||||
beginTest ("Swap transfers ownership if one optional contains a value");
|
||||
{
|
||||
{
|
||||
Ptr ptr = makePtr();
|
||||
Optional<Ptr> a, b = ptr;
|
||||
expect (! a.hasValue());
|
||||
expect (b.hasValue());
|
||||
expect (ptr.use_count() == 2);
|
||||
|
||||
a.swap (b);
|
||||
|
||||
expect (a.hasValue());
|
||||
expect (! b.hasValue());
|
||||
expect (ptr.use_count() == 2);
|
||||
}
|
||||
|
||||
{
|
||||
auto ptr = makePtr();
|
||||
Optional<Ptr> a = ptr, b;
|
||||
expect (a.hasValue());
|
||||
expect (! b.hasValue());
|
||||
expect (ptr.use_count() == 2);
|
||||
|
||||
a.swap (b);
|
||||
|
||||
expect (! a.hasValue());
|
||||
expect (b.hasValue());
|
||||
expect (ptr.use_count() == 2);
|
||||
}
|
||||
}
|
||||
|
||||
beginTest ("Swap calls std::swap to swap two populated optionals");
|
||||
{
|
||||
auto x = makePtr(), y = makePtr();
|
||||
Optional<Ptr> a = x, b = y;
|
||||
expect (a.hasValue());
|
||||
expect (b.hasValue());
|
||||
expect (x.use_count() == 2);
|
||||
expect (y.use_count() == 2);
|
||||
expect (*a == x);
|
||||
expect (*b == y);
|
||||
|
||||
a.swap (b);
|
||||
|
||||
expect (a.hasValue());
|
||||
expect (b.hasValue());
|
||||
expect (x.use_count() == 2);
|
||||
expect (y.use_count() == 2);
|
||||
expect (*a == y);
|
||||
expect (*b == x);
|
||||
}
|
||||
|
||||
beginTest ("An exception thrown during a swap leaves both objects in the previous populated state");
|
||||
{
|
||||
{
|
||||
Optional<ThrowOnMoveOrSwap> a, b;
|
||||
a.emplace();
|
||||
|
||||
expect (a.hasValue());
|
||||
expect (! b.hasValue());
|
||||
|
||||
bool threw = false;
|
||||
|
||||
try
|
||||
{
|
||||
a.swap (b);
|
||||
}
|
||||
catch (const std::bad_alloc&)
|
||||
{
|
||||
threw = true;
|
||||
}
|
||||
|
||||
expect (threw);
|
||||
expect (a.hasValue());
|
||||
expect (! b.hasValue());
|
||||
}
|
||||
|
||||
{
|
||||
Optional<ThrowOnMoveOrSwap> a, b;
|
||||
b.emplace();
|
||||
|
||||
expect (! a.hasValue());
|
||||
expect (b.hasValue());
|
||||
|
||||
bool threw = false;
|
||||
|
||||
try
|
||||
{
|
||||
a.swap (b);
|
||||
}
|
||||
catch (const std::bad_alloc&)
|
||||
{
|
||||
threw = true;
|
||||
}
|
||||
|
||||
expect (threw);
|
||||
expect (! a.hasValue());
|
||||
expect (b.hasValue());
|
||||
}
|
||||
|
||||
{
|
||||
Optional<ThrowOnMoveOrSwap> a, b;
|
||||
a.emplace();
|
||||
b.emplace();
|
||||
|
||||
expect (a.hasValue());
|
||||
expect (b.hasValue());
|
||||
|
||||
bool threw = false;
|
||||
|
||||
try
|
||||
{
|
||||
a.swap (b);
|
||||
}
|
||||
catch (const std::bad_alloc&)
|
||||
{
|
||||
threw = true;
|
||||
}
|
||||
|
||||
expect (threw);
|
||||
expect (a.hasValue());
|
||||
expect (b.hasValue());
|
||||
}
|
||||
}
|
||||
|
||||
beginTest ("Relational tests");
|
||||
{
|
||||
expect (Optional<int> (1) == Optional<int> (1));
|
||||
expect (Optional<int>() == Optional<int>());
|
||||
expect (! (Optional<int> (1) == Optional<int>()));
|
||||
expect (! (Optional<int>() == Optional<int> (1)));
|
||||
expect (! (Optional<int> (1) == Optional<int> (2)));
|
||||
|
||||
expect (Optional<int> (1) != Optional<int> (2));
|
||||
expect (! (Optional<int>() != Optional<int>()));
|
||||
expect (Optional<int> (1) != Optional<int>());
|
||||
expect (Optional<int>() != Optional<int> (1));
|
||||
expect (! (Optional<int> (1) != Optional<int> (1)));
|
||||
|
||||
expect (Optional<int>() < Optional<int> (1));
|
||||
expect (! (Optional<int> (1) < Optional<int>()));
|
||||
expect (! (Optional<int>() < Optional<int>()));
|
||||
expect (Optional<int> (1) < Optional<int> (2));
|
||||
|
||||
expect (Optional<int>() <= Optional<int> (1));
|
||||
expect (! (Optional<int> (1) <= Optional<int>()));
|
||||
expect (Optional<int>() <= Optional<int>());
|
||||
expect (Optional<int> (1) <= Optional<int> (2));
|
||||
|
||||
expect (! (Optional<int>() > Optional<int> (1)));
|
||||
expect (Optional<int> (1) > Optional<int>());
|
||||
expect (! (Optional<int>() > Optional<int>()));
|
||||
expect (! (Optional<int> (1) > Optional<int> (2)));
|
||||
|
||||
expect (! (Optional<int>() >= Optional<int> (1)));
|
||||
expect (Optional<int> (1) >= Optional<int>());
|
||||
expect (Optional<int>() >= Optional<int>());
|
||||
expect (! (Optional<int> (1) >= Optional<int> (2)));
|
||||
|
||||
expect (Optional<int>() == nullopt);
|
||||
expect (! (Optional<int> (1) == nullopt));
|
||||
expect (nullopt == Optional<int>());
|
||||
expect (! (nullopt == Optional<int> (1)));
|
||||
|
||||
expect (! (Optional<int>() != nullopt));
|
||||
expect (Optional<int> (1) != nullopt);
|
||||
expect (! (nullopt != Optional<int>()));
|
||||
expect (nullopt != Optional<int> (1));
|
||||
|
||||
expect (! (Optional<int>() < nullopt));
|
||||
expect (! (Optional<int> (1) < nullopt));
|
||||
|
||||
expect (! (nullopt < Optional<int>()));
|
||||
expect (nullopt < Optional<int> (1));
|
||||
|
||||
expect (Optional<int>() <= nullopt);
|
||||
expect (! (Optional<int> (1) <= nullopt));
|
||||
|
||||
expect (nullopt <= Optional<int>());
|
||||
expect (nullopt <= Optional<int> (1));
|
||||
|
||||
expect (! (Optional<int>() > nullopt));
|
||||
expect (Optional<int> (1) > nullopt);
|
||||
|
||||
expect (! (nullopt > Optional<int>()));
|
||||
expect (! (nullopt > Optional<int> (1)));
|
||||
|
||||
expect (Optional<int>() >= nullopt);
|
||||
expect (Optional<int> (1) >= nullopt);
|
||||
|
||||
expect (nullopt >= Optional<int>());
|
||||
expect (! (nullopt >= Optional<int> (1)));
|
||||
|
||||
expect (! (Optional<int>() == 5));
|
||||
expect (! (Optional<int> (1) == 5));
|
||||
expect (Optional<int> (1) == 1);
|
||||
expect (! (5 == Optional<int>()));
|
||||
expect (! (5 == Optional<int> (1)));
|
||||
expect (1 == Optional<int> (1));
|
||||
|
||||
expect (Optional<int>() != 5);
|
||||
expect (Optional<int> (1) != 5);
|
||||
expect (! (Optional<int> (1) != 1));
|
||||
expect (5 != Optional<int>());
|
||||
expect (5 != Optional<int> (1));
|
||||
expect (! (1 != Optional<int> (1)));
|
||||
|
||||
expect (Optional<int>() < 5);
|
||||
expect (Optional<int> (1) < 5);
|
||||
expect (! (Optional<int> (1) < 1));
|
||||
expect (! (Optional<int> (1) < 0));
|
||||
|
||||
expect (! (5 < Optional<int>()));
|
||||
expect (! (5 < Optional<int> (1)));
|
||||
expect (! (1 < Optional<int> (1)));
|
||||
expect (0 < Optional<int> (1));
|
||||
|
||||
expect (Optional<int>() <= 5);
|
||||
expect (Optional<int> (1) <= 5);
|
||||
expect (Optional<int> (1) <= 1);
|
||||
expect (! (Optional<int> (1) <= 0));
|
||||
|
||||
expect (! (5 <= Optional<int>()));
|
||||
expect (! (5 <= Optional<int> (1)));
|
||||
expect (1 <= Optional<int> (1));
|
||||
expect (0 <= Optional<int> (1));
|
||||
|
||||
expect (! (Optional<int>() > 5));
|
||||
expect (! (Optional<int> (1) > 5));
|
||||
expect (! (Optional<int> (1) > 1));
|
||||
expect (Optional<int> (1) > 0);
|
||||
|
||||
expect (5 > Optional<int>());
|
||||
expect (5 > Optional<int> (1));
|
||||
expect (! (1 > Optional<int> (1)));
|
||||
expect (! (0 > Optional<int> (1)));
|
||||
|
||||
expect (! (Optional<int>() >= 5));
|
||||
expect (! (Optional<int> (1) >= 5));
|
||||
expect (Optional<int> (1) >= 1);
|
||||
expect (Optional<int> (1) >= 0);
|
||||
|
||||
expect (5 >= Optional<int>());
|
||||
expect (5 >= Optional<int> (1));
|
||||
expect (1 >= Optional<int> (1));
|
||||
expect (! (0 >= Optional<int> (1)));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static OptionalUnitTest optionalUnitTest;
|
||||
|
||||
} // namespace juce
|
@ -1,130 +1,130 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
#if JUCE_UNIT_TESTS
|
||||
|
||||
static struct OwnedArrayTest : public UnitTest
|
||||
{
|
||||
struct Base
|
||||
{
|
||||
Base() = default;
|
||||
virtual ~Base() = default;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Base)
|
||||
};
|
||||
|
||||
struct Derived : Base
|
||||
{
|
||||
Derived() = default;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Derived)
|
||||
};
|
||||
|
||||
struct DestructorObj
|
||||
{
|
||||
DestructorObj (OwnedArrayTest& p,
|
||||
OwnedArray<DestructorObj>& arr)
|
||||
: parent (p), objectArray (arr)
|
||||
{}
|
||||
|
||||
~DestructorObj()
|
||||
{
|
||||
data = 0;
|
||||
|
||||
for (auto* o : objectArray)
|
||||
{
|
||||
parent.expect (o != nullptr);
|
||||
parent.expect (o != this);
|
||||
|
||||
if (o != nullptr)
|
||||
parent.expectEquals (o->data, 956);
|
||||
}
|
||||
}
|
||||
|
||||
OwnedArrayTest& parent;
|
||||
OwnedArray<DestructorObj>& objectArray;
|
||||
int data = 956;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DestructorObj)
|
||||
};
|
||||
|
||||
OwnedArrayTest()
|
||||
: UnitTest ("OwnedArray", UnitTestCategories::containers)
|
||||
{}
|
||||
|
||||
void runTest() override
|
||||
{
|
||||
beginTest ("After converting move construction, ownership is transferred");
|
||||
{
|
||||
OwnedArray<Derived> derived { new Derived{}, new Derived{}, new Derived{} };
|
||||
|
||||
OwnedArray<Base> base { std::move (derived) };
|
||||
|
||||
expectEquals (base.size(), 3);
|
||||
expectEquals (derived.size(), 0);
|
||||
}
|
||||
|
||||
beginTest ("After converting move assignment, ownership is transferred");
|
||||
{
|
||||
OwnedArray<Base> base;
|
||||
|
||||
base = OwnedArray<Derived> { new Derived{}, new Derived{}, new Derived{} };
|
||||
|
||||
expectEquals (base.size(), 3);
|
||||
}
|
||||
|
||||
beginTest ("Iterate in destructor");
|
||||
{
|
||||
{
|
||||
OwnedArray<DestructorObj> arr;
|
||||
|
||||
for (int i = 0; i < 2; ++i)
|
||||
arr.add (new DestructorObj (*this, arr));
|
||||
}
|
||||
|
||||
OwnedArray<DestructorObj> arr;
|
||||
|
||||
for (int i = 0; i < 1025; ++i)
|
||||
arr.add (new DestructorObj (*this, arr));
|
||||
|
||||
while (! arr.isEmpty())
|
||||
arr.remove (0);
|
||||
|
||||
for (int i = 0; i < 1025; ++i)
|
||||
arr.add (new DestructorObj (*this, arr));
|
||||
|
||||
arr.removeRange (1, arr.size() - 3);
|
||||
|
||||
for (int i = 0; i < 1025; ++i)
|
||||
arr.add (new DestructorObj (*this, arr));
|
||||
|
||||
arr.set (500, new DestructorObj (*this, arr));
|
||||
}
|
||||
}
|
||||
} ownedArrayTest;
|
||||
|
||||
#endif
|
||||
|
||||
}
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
#if JUCE_UNIT_TESTS
|
||||
|
||||
static struct OwnedArrayTest : public UnitTest
|
||||
{
|
||||
struct Base
|
||||
{
|
||||
Base() = default;
|
||||
virtual ~Base() = default;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Base)
|
||||
};
|
||||
|
||||
struct Derived : Base
|
||||
{
|
||||
Derived() = default;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Derived)
|
||||
};
|
||||
|
||||
struct DestructorObj
|
||||
{
|
||||
DestructorObj (OwnedArrayTest& p,
|
||||
OwnedArray<DestructorObj>& arr)
|
||||
: parent (p), objectArray (arr)
|
||||
{}
|
||||
|
||||
~DestructorObj()
|
||||
{
|
||||
data = 0;
|
||||
|
||||
for (auto* o : objectArray)
|
||||
{
|
||||
parent.expect (o != nullptr);
|
||||
parent.expect (o != this);
|
||||
|
||||
if (o != nullptr)
|
||||
parent.expectEquals (o->data, 956);
|
||||
}
|
||||
}
|
||||
|
||||
OwnedArrayTest& parent;
|
||||
OwnedArray<DestructorObj>& objectArray;
|
||||
int data = 956;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DestructorObj)
|
||||
};
|
||||
|
||||
OwnedArrayTest()
|
||||
: UnitTest ("OwnedArray", UnitTestCategories::containers)
|
||||
{}
|
||||
|
||||
void runTest() override
|
||||
{
|
||||
beginTest ("After converting move construction, ownership is transferred");
|
||||
{
|
||||
OwnedArray<Derived> derived { new Derived{}, new Derived{}, new Derived{} };
|
||||
|
||||
OwnedArray<Base> base { std::move (derived) };
|
||||
|
||||
expectEquals (base.size(), 3);
|
||||
expectEquals (derived.size(), 0);
|
||||
}
|
||||
|
||||
beginTest ("After converting move assignment, ownership is transferred");
|
||||
{
|
||||
OwnedArray<Base> base;
|
||||
|
||||
base = OwnedArray<Derived> { new Derived{}, new Derived{}, new Derived{} };
|
||||
|
||||
expectEquals (base.size(), 3);
|
||||
}
|
||||
|
||||
beginTest ("Iterate in destructor");
|
||||
{
|
||||
{
|
||||
OwnedArray<DestructorObj> arr;
|
||||
|
||||
for (int i = 0; i < 2; ++i)
|
||||
arr.add (new DestructorObj (*this, arr));
|
||||
}
|
||||
|
||||
OwnedArray<DestructorObj> arr;
|
||||
|
||||
for (int i = 0; i < 1025; ++i)
|
||||
arr.add (new DestructorObj (*this, arr));
|
||||
|
||||
while (! arr.isEmpty())
|
||||
arr.remove (0);
|
||||
|
||||
for (int i = 0; i < 1025; ++i)
|
||||
arr.add (new DestructorObj (*this, arr));
|
||||
|
||||
arr.removeRange (1, arr.size() - 3);
|
||||
|
||||
for (int i = 0; i < 1025; ++i)
|
||||
arr.add (new DestructorObj (*this, arr));
|
||||
|
||||
arr.set (500, new DestructorObj (*this, arr));
|
||||
}
|
||||
}
|
||||
} ownedArrayTest;
|
||||
|
||||
#endif
|
||||
|
||||
}
|
||||
|
1746
deps/juce/modules/juce_core/containers/juce_OwnedArray.h
vendored
1746
deps/juce/modules/juce_core/containers/juce_OwnedArray.h
vendored
File diff suppressed because it is too large
Load Diff
@ -1,217 +1,217 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
PropertySet::PropertySet (bool ignoreCaseOfKeyNames)
|
||||
: properties (ignoreCaseOfKeyNames),
|
||||
fallbackProperties (nullptr),
|
||||
ignoreCaseOfKeys (ignoreCaseOfKeyNames)
|
||||
{
|
||||
}
|
||||
|
||||
PropertySet::PropertySet (const PropertySet& other)
|
||||
: properties (other.properties),
|
||||
fallbackProperties (other.fallbackProperties),
|
||||
ignoreCaseOfKeys (other.ignoreCaseOfKeys)
|
||||
{
|
||||
}
|
||||
|
||||
PropertySet& PropertySet::operator= (const PropertySet& other)
|
||||
{
|
||||
properties = other.properties;
|
||||
fallbackProperties = other.fallbackProperties;
|
||||
ignoreCaseOfKeys = other.ignoreCaseOfKeys;
|
||||
|
||||
propertyChanged();
|
||||
return *this;
|
||||
}
|
||||
|
||||
PropertySet::~PropertySet()
|
||||
{
|
||||
}
|
||||
|
||||
void PropertySet::clear()
|
||||
{
|
||||
const ScopedLock sl (lock);
|
||||
|
||||
if (properties.size() > 0)
|
||||
{
|
||||
properties.clear();
|
||||
propertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
String PropertySet::getValue (StringRef keyName, const String& defaultValue) const noexcept
|
||||
{
|
||||
const ScopedLock sl (lock);
|
||||
auto index = properties.getAllKeys().indexOf (keyName, ignoreCaseOfKeys);
|
||||
|
||||
if (index >= 0)
|
||||
return properties.getAllValues() [index];
|
||||
|
||||
return fallbackProperties != nullptr ? fallbackProperties->getValue (keyName, defaultValue)
|
||||
: defaultValue;
|
||||
}
|
||||
|
||||
int PropertySet::getIntValue (StringRef keyName, int defaultValue) const noexcept
|
||||
{
|
||||
const ScopedLock sl (lock);
|
||||
auto index = properties.getAllKeys().indexOf (keyName, ignoreCaseOfKeys);
|
||||
|
||||
if (index >= 0)
|
||||
return properties.getAllValues() [index].getIntValue();
|
||||
|
||||
return fallbackProperties != nullptr ? fallbackProperties->getIntValue (keyName, defaultValue)
|
||||
: defaultValue;
|
||||
}
|
||||
|
||||
double PropertySet::getDoubleValue (StringRef keyName, double defaultValue) const noexcept
|
||||
{
|
||||
const ScopedLock sl (lock);
|
||||
auto index = properties.getAllKeys().indexOf (keyName, ignoreCaseOfKeys);
|
||||
|
||||
if (index >= 0)
|
||||
return properties.getAllValues()[index].getDoubleValue();
|
||||
|
||||
return fallbackProperties != nullptr ? fallbackProperties->getDoubleValue (keyName, defaultValue)
|
||||
: defaultValue;
|
||||
}
|
||||
|
||||
bool PropertySet::getBoolValue (StringRef keyName, bool defaultValue) const noexcept
|
||||
{
|
||||
const ScopedLock sl (lock);
|
||||
auto index = properties.getAllKeys().indexOf (keyName, ignoreCaseOfKeys);
|
||||
|
||||
if (index >= 0)
|
||||
return properties.getAllValues() [index].getIntValue() != 0;
|
||||
|
||||
return fallbackProperties != nullptr ? fallbackProperties->getBoolValue (keyName, defaultValue)
|
||||
: defaultValue;
|
||||
}
|
||||
|
||||
std::unique_ptr<XmlElement> PropertySet::getXmlValue (StringRef keyName) const
|
||||
{
|
||||
return parseXML (getValue (keyName));
|
||||
}
|
||||
|
||||
void PropertySet::setValue (StringRef keyName, const var& v)
|
||||
{
|
||||
jassert (keyName.isNotEmpty()); // shouldn't use an empty key name!
|
||||
|
||||
if (keyName.isNotEmpty())
|
||||
{
|
||||
auto value = v.toString();
|
||||
const ScopedLock sl (lock);
|
||||
auto index = properties.getAllKeys().indexOf (keyName, ignoreCaseOfKeys);
|
||||
|
||||
if (index < 0 || properties.getAllValues() [index] != value)
|
||||
{
|
||||
properties.set (keyName, value);
|
||||
propertyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PropertySet::removeValue (StringRef keyName)
|
||||
{
|
||||
if (keyName.isNotEmpty())
|
||||
{
|
||||
const ScopedLock sl (lock);
|
||||
auto index = properties.getAllKeys().indexOf (keyName, ignoreCaseOfKeys);
|
||||
|
||||
if (index >= 0)
|
||||
{
|
||||
properties.remove (keyName);
|
||||
propertyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PropertySet::setValue (StringRef keyName, const XmlElement* xml)
|
||||
{
|
||||
setValue (keyName, xml == nullptr ? var()
|
||||
: var (xml->toString (XmlElement::TextFormat().singleLine().withoutHeader())));
|
||||
}
|
||||
|
||||
bool PropertySet::containsKey (StringRef keyName) const noexcept
|
||||
{
|
||||
const ScopedLock sl (lock);
|
||||
return properties.getAllKeys().contains (keyName, ignoreCaseOfKeys);
|
||||
}
|
||||
|
||||
void PropertySet::addAllPropertiesFrom (const PropertySet& source)
|
||||
{
|
||||
const ScopedLock sl (source.getLock());
|
||||
|
||||
for (int i = 0; i < source.properties.size(); ++i)
|
||||
setValue (source.properties.getAllKeys() [i],
|
||||
source.properties.getAllValues() [i]);
|
||||
}
|
||||
|
||||
void PropertySet::setFallbackPropertySet (PropertySet* fallbackProperties_) noexcept
|
||||
{
|
||||
const ScopedLock sl (lock);
|
||||
fallbackProperties = fallbackProperties_;
|
||||
}
|
||||
|
||||
std::unique_ptr<XmlElement> PropertySet::createXml (const String& nodeName) const
|
||||
{
|
||||
auto xml = std::make_unique<XmlElement> (nodeName);
|
||||
|
||||
const ScopedLock sl (lock);
|
||||
|
||||
for (int i = 0; i < properties.getAllKeys().size(); ++i)
|
||||
{
|
||||
auto e = xml->createNewChildElement ("VALUE");
|
||||
e->setAttribute ("name", properties.getAllKeys()[i]);
|
||||
e->setAttribute ("val", properties.getAllValues()[i]);
|
||||
}
|
||||
|
||||
return xml;
|
||||
}
|
||||
|
||||
void PropertySet::restoreFromXml (const XmlElement& xml)
|
||||
{
|
||||
const ScopedLock sl (lock);
|
||||
clear();
|
||||
|
||||
for (auto* e : xml.getChildWithTagNameIterator ("VALUE"))
|
||||
{
|
||||
if (e->hasAttribute ("name")
|
||||
&& e->hasAttribute ("val"))
|
||||
{
|
||||
properties.set (e->getStringAttribute ("name"),
|
||||
e->getStringAttribute ("val"));
|
||||
}
|
||||
}
|
||||
|
||||
if (properties.size() > 0)
|
||||
propertyChanged();
|
||||
}
|
||||
|
||||
void PropertySet::propertyChanged()
|
||||
{
|
||||
}
|
||||
|
||||
} // namespace juce
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
PropertySet::PropertySet (bool ignoreCaseOfKeyNames)
|
||||
: properties (ignoreCaseOfKeyNames),
|
||||
fallbackProperties (nullptr),
|
||||
ignoreCaseOfKeys (ignoreCaseOfKeyNames)
|
||||
{
|
||||
}
|
||||
|
||||
PropertySet::PropertySet (const PropertySet& other)
|
||||
: properties (other.properties),
|
||||
fallbackProperties (other.fallbackProperties),
|
||||
ignoreCaseOfKeys (other.ignoreCaseOfKeys)
|
||||
{
|
||||
}
|
||||
|
||||
PropertySet& PropertySet::operator= (const PropertySet& other)
|
||||
{
|
||||
properties = other.properties;
|
||||
fallbackProperties = other.fallbackProperties;
|
||||
ignoreCaseOfKeys = other.ignoreCaseOfKeys;
|
||||
|
||||
propertyChanged();
|
||||
return *this;
|
||||
}
|
||||
|
||||
PropertySet::~PropertySet()
|
||||
{
|
||||
}
|
||||
|
||||
void PropertySet::clear()
|
||||
{
|
||||
const ScopedLock sl (lock);
|
||||
|
||||
if (properties.size() > 0)
|
||||
{
|
||||
properties.clear();
|
||||
propertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
String PropertySet::getValue (StringRef keyName, const String& defaultValue) const noexcept
|
||||
{
|
||||
const ScopedLock sl (lock);
|
||||
auto index = properties.getAllKeys().indexOf (keyName, ignoreCaseOfKeys);
|
||||
|
||||
if (index >= 0)
|
||||
return properties.getAllValues() [index];
|
||||
|
||||
return fallbackProperties != nullptr ? fallbackProperties->getValue (keyName, defaultValue)
|
||||
: defaultValue;
|
||||
}
|
||||
|
||||
int PropertySet::getIntValue (StringRef keyName, int defaultValue) const noexcept
|
||||
{
|
||||
const ScopedLock sl (lock);
|
||||
auto index = properties.getAllKeys().indexOf (keyName, ignoreCaseOfKeys);
|
||||
|
||||
if (index >= 0)
|
||||
return properties.getAllValues() [index].getIntValue();
|
||||
|
||||
return fallbackProperties != nullptr ? fallbackProperties->getIntValue (keyName, defaultValue)
|
||||
: defaultValue;
|
||||
}
|
||||
|
||||
double PropertySet::getDoubleValue (StringRef keyName, double defaultValue) const noexcept
|
||||
{
|
||||
const ScopedLock sl (lock);
|
||||
auto index = properties.getAllKeys().indexOf (keyName, ignoreCaseOfKeys);
|
||||
|
||||
if (index >= 0)
|
||||
return properties.getAllValues()[index].getDoubleValue();
|
||||
|
||||
return fallbackProperties != nullptr ? fallbackProperties->getDoubleValue (keyName, defaultValue)
|
||||
: defaultValue;
|
||||
}
|
||||
|
||||
bool PropertySet::getBoolValue (StringRef keyName, bool defaultValue) const noexcept
|
||||
{
|
||||
const ScopedLock sl (lock);
|
||||
auto index = properties.getAllKeys().indexOf (keyName, ignoreCaseOfKeys);
|
||||
|
||||
if (index >= 0)
|
||||
return properties.getAllValues() [index].getIntValue() != 0;
|
||||
|
||||
return fallbackProperties != nullptr ? fallbackProperties->getBoolValue (keyName, defaultValue)
|
||||
: defaultValue;
|
||||
}
|
||||
|
||||
std::unique_ptr<XmlElement> PropertySet::getXmlValue (StringRef keyName) const
|
||||
{
|
||||
return parseXML (getValue (keyName));
|
||||
}
|
||||
|
||||
void PropertySet::setValue (StringRef keyName, const var& v)
|
||||
{
|
||||
jassert (keyName.isNotEmpty()); // shouldn't use an empty key name!
|
||||
|
||||
if (keyName.isNotEmpty())
|
||||
{
|
||||
auto value = v.toString();
|
||||
const ScopedLock sl (lock);
|
||||
auto index = properties.getAllKeys().indexOf (keyName, ignoreCaseOfKeys);
|
||||
|
||||
if (index < 0 || properties.getAllValues() [index] != value)
|
||||
{
|
||||
properties.set (keyName, value);
|
||||
propertyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PropertySet::removeValue (StringRef keyName)
|
||||
{
|
||||
if (keyName.isNotEmpty())
|
||||
{
|
||||
const ScopedLock sl (lock);
|
||||
auto index = properties.getAllKeys().indexOf (keyName, ignoreCaseOfKeys);
|
||||
|
||||
if (index >= 0)
|
||||
{
|
||||
properties.remove (keyName);
|
||||
propertyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PropertySet::setValue (StringRef keyName, const XmlElement* xml)
|
||||
{
|
||||
setValue (keyName, xml == nullptr ? var()
|
||||
: var (xml->toString (XmlElement::TextFormat().singleLine().withoutHeader())));
|
||||
}
|
||||
|
||||
bool PropertySet::containsKey (StringRef keyName) const noexcept
|
||||
{
|
||||
const ScopedLock sl (lock);
|
||||
return properties.getAllKeys().contains (keyName, ignoreCaseOfKeys);
|
||||
}
|
||||
|
||||
void PropertySet::addAllPropertiesFrom (const PropertySet& source)
|
||||
{
|
||||
const ScopedLock sl (source.getLock());
|
||||
|
||||
for (int i = 0; i < source.properties.size(); ++i)
|
||||
setValue (source.properties.getAllKeys() [i],
|
||||
source.properties.getAllValues() [i]);
|
||||
}
|
||||
|
||||
void PropertySet::setFallbackPropertySet (PropertySet* fallbackProperties_) noexcept
|
||||
{
|
||||
const ScopedLock sl (lock);
|
||||
fallbackProperties = fallbackProperties_;
|
||||
}
|
||||
|
||||
std::unique_ptr<XmlElement> PropertySet::createXml (const String& nodeName) const
|
||||
{
|
||||
auto xml = std::make_unique<XmlElement> (nodeName);
|
||||
|
||||
const ScopedLock sl (lock);
|
||||
|
||||
for (int i = 0; i < properties.getAllKeys().size(); ++i)
|
||||
{
|
||||
auto e = xml->createNewChildElement ("VALUE");
|
||||
e->setAttribute ("name", properties.getAllKeys()[i]);
|
||||
e->setAttribute ("val", properties.getAllValues()[i]);
|
||||
}
|
||||
|
||||
return xml;
|
||||
}
|
||||
|
||||
void PropertySet::restoreFromXml (const XmlElement& xml)
|
||||
{
|
||||
const ScopedLock sl (lock);
|
||||
clear();
|
||||
|
||||
for (auto* e : xml.getChildWithTagNameIterator ("VALUE"))
|
||||
{
|
||||
if (e->hasAttribute ("name")
|
||||
&& e->hasAttribute ("val"))
|
||||
{
|
||||
properties.set (e->getStringAttribute ("name"),
|
||||
e->getStringAttribute ("val"));
|
||||
}
|
||||
}
|
||||
|
||||
if (properties.size() > 0)
|
||||
propertyChanged();
|
||||
}
|
||||
|
||||
void PropertySet::propertyChanged()
|
||||
{
|
||||
}
|
||||
|
||||
} // namespace juce
|
||||
|
@ -1,205 +1,205 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A set of named property values, which can be strings, integers, floating point, etc.
|
||||
|
||||
Effectively, this just wraps a StringPairArray in an interface that makes it easier
|
||||
to load and save types other than strings.
|
||||
|
||||
See the PropertiesFile class for a subclass of this, which automatically broadcasts change
|
||||
messages and saves/loads the list from a file.
|
||||
|
||||
@tags{Core}
|
||||
*/
|
||||
class JUCE_API PropertySet
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates an empty PropertySet.
|
||||
@param ignoreCaseOfKeyNames if true, the names of properties are compared in a
|
||||
case-insensitive way
|
||||
*/
|
||||
PropertySet (bool ignoreCaseOfKeyNames = false);
|
||||
|
||||
/** Creates a copy of another PropertySet. */
|
||||
PropertySet (const PropertySet& other);
|
||||
|
||||
/** Copies another PropertySet over this one. */
|
||||
PropertySet& operator= (const PropertySet& other);
|
||||
|
||||
/** Destructor. */
|
||||
virtual ~PropertySet();
|
||||
|
||||
//==============================================================================
|
||||
/** Returns one of the properties as a string.
|
||||
|
||||
If the value isn't found in this set, then this will look for it in a fallback
|
||||
property set (if you've specified one with the setFallbackPropertySet() method),
|
||||
and if it can't find one there, it'll return the default value passed-in.
|
||||
|
||||
@param keyName the name of the property to retrieve
|
||||
@param defaultReturnValue a value to return if the named property doesn't actually exist
|
||||
*/
|
||||
String getValue (StringRef keyName, const String& defaultReturnValue = String()) const noexcept;
|
||||
|
||||
/** Returns one of the properties as an integer.
|
||||
|
||||
If the value isn't found in this set, then this will look for it in a fallback
|
||||
property set (if you've specified one with the setFallbackPropertySet() method),
|
||||
and if it can't find one there, it'll return the default value passed-in.
|
||||
|
||||
@param keyName the name of the property to retrieve
|
||||
@param defaultReturnValue a value to return if the named property doesn't actually exist
|
||||
*/
|
||||
int getIntValue (StringRef keyName, int defaultReturnValue = 0) const noexcept;
|
||||
|
||||
/** Returns one of the properties as an double.
|
||||
|
||||
If the value isn't found in this set, then this will look for it in a fallback
|
||||
property set (if you've specified one with the setFallbackPropertySet() method),
|
||||
and if it can't find one there, it'll return the default value passed-in.
|
||||
|
||||
@param keyName the name of the property to retrieve
|
||||
@param defaultReturnValue a value to return if the named property doesn't actually exist
|
||||
*/
|
||||
double getDoubleValue (StringRef keyName, double defaultReturnValue = 0.0) const noexcept;
|
||||
|
||||
/** Returns one of the properties as an boolean.
|
||||
|
||||
The result will be true if the string found for this key name can be parsed as a non-zero
|
||||
integer.
|
||||
|
||||
If the value isn't found in this set, then this will look for it in a fallback
|
||||
property set (if you've specified one with the setFallbackPropertySet() method),
|
||||
and if it can't find one there, it'll return the default value passed-in.
|
||||
|
||||
@param keyName the name of the property to retrieve
|
||||
@param defaultReturnValue a value to return if the named property doesn't actually exist
|
||||
*/
|
||||
bool getBoolValue (StringRef keyName, bool defaultReturnValue = false) const noexcept;
|
||||
|
||||
/** Returns one of the properties as an XML element.
|
||||
|
||||
The result will a new XMLElement object. It may return nullptr if the key isn't found,
|
||||
or if the entry contains an string that isn't valid XML.
|
||||
|
||||
If the value isn't found in this set, then this will look for it in a fallback
|
||||
property set (if you've specified one with the setFallbackPropertySet() method),
|
||||
and if it can't find one there, it'll return the default value passed-in.
|
||||
|
||||
@param keyName the name of the property to retrieve
|
||||
*/
|
||||
std::unique_ptr<XmlElement> getXmlValue (StringRef keyName) const;
|
||||
|
||||
//==============================================================================
|
||||
/** Sets a named property.
|
||||
|
||||
@param keyName the name of the property to set. (This mustn't be an empty string)
|
||||
@param value the new value to set it to
|
||||
*/
|
||||
void setValue (StringRef keyName, const var& value);
|
||||
|
||||
/** Sets a named property to an XML element.
|
||||
|
||||
@param keyName the name of the property to set. (This mustn't be an empty string)
|
||||
@param xml the new element to set it to. If this is a nullptr, the value will
|
||||
be set to an empty string
|
||||
@see getXmlValue
|
||||
*/
|
||||
void setValue (StringRef keyName, const XmlElement* xml);
|
||||
|
||||
/** This copies all the values from a source PropertySet to this one.
|
||||
This won't remove any existing settings, it just adds any that it finds in the source set.
|
||||
*/
|
||||
void addAllPropertiesFrom (const PropertySet& source);
|
||||
|
||||
//==============================================================================
|
||||
/** Deletes a property.
|
||||
@param keyName the name of the property to delete. (This mustn't be an empty string)
|
||||
*/
|
||||
void removeValue (StringRef keyName);
|
||||
|
||||
/** Returns true if the properties include the given key. */
|
||||
bool containsKey (StringRef keyName) const noexcept;
|
||||
|
||||
/** Removes all values. */
|
||||
void clear();
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the keys/value pair array containing all the properties. */
|
||||
StringPairArray& getAllProperties() noexcept { return properties; }
|
||||
|
||||
/** Returns the lock used when reading or writing to this set */
|
||||
const CriticalSection& getLock() const noexcept { return lock; }
|
||||
|
||||
//==============================================================================
|
||||
/** Returns an XML element which encapsulates all the items in this property set.
|
||||
The string parameter is the tag name that should be used for the node.
|
||||
@see restoreFromXml
|
||||
*/
|
||||
std::unique_ptr<XmlElement> createXml (const String& nodeName) const;
|
||||
|
||||
/** Reloads a set of properties that were previously stored as XML.
|
||||
The node passed in must have been created by the createXml() method.
|
||||
@see createXml
|
||||
*/
|
||||
void restoreFromXml (const XmlElement& xml);
|
||||
|
||||
//==============================================================================
|
||||
/** Sets up a second PopertySet that will be used to look up any values that aren't
|
||||
set in this one.
|
||||
|
||||
If you set this up to be a pointer to a second property set, then whenever one
|
||||
of the getValue() methods fails to find an entry in this set, it will look up that
|
||||
value in the fallback set, and if it finds it, it will return that.
|
||||
|
||||
Make sure that you don't delete the fallback set while it's still being used by
|
||||
another set! To remove the fallback set, just call this method with a null pointer.
|
||||
|
||||
@see getFallbackPropertySet
|
||||
*/
|
||||
void setFallbackPropertySet (PropertySet* fallbackProperties) noexcept;
|
||||
|
||||
/** Returns the fallback property set.
|
||||
@see setFallbackPropertySet
|
||||
*/
|
||||
PropertySet* getFallbackPropertySet() const noexcept { return fallbackProperties; }
|
||||
|
||||
protected:
|
||||
/** Subclasses can override this to be told when one of the properties has been changed. */
|
||||
virtual void propertyChanged();
|
||||
|
||||
private:
|
||||
StringPairArray properties;
|
||||
PropertySet* fallbackProperties;
|
||||
CriticalSection lock;
|
||||
bool ignoreCaseOfKeys;
|
||||
|
||||
JUCE_LEAK_DETECTOR (PropertySet)
|
||||
};
|
||||
|
||||
} // namespace juce
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A set of named property values, which can be strings, integers, floating point, etc.
|
||||
|
||||
Effectively, this just wraps a StringPairArray in an interface that makes it easier
|
||||
to load and save types other than strings.
|
||||
|
||||
See the PropertiesFile class for a subclass of this, which automatically broadcasts change
|
||||
messages and saves/loads the list from a file.
|
||||
|
||||
@tags{Core}
|
||||
*/
|
||||
class JUCE_API PropertySet
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates an empty PropertySet.
|
||||
@param ignoreCaseOfKeyNames if true, the names of properties are compared in a
|
||||
case-insensitive way
|
||||
*/
|
||||
PropertySet (bool ignoreCaseOfKeyNames = false);
|
||||
|
||||
/** Creates a copy of another PropertySet. */
|
||||
PropertySet (const PropertySet& other);
|
||||
|
||||
/** Copies another PropertySet over this one. */
|
||||
PropertySet& operator= (const PropertySet& other);
|
||||
|
||||
/** Destructor. */
|
||||
virtual ~PropertySet();
|
||||
|
||||
//==============================================================================
|
||||
/** Returns one of the properties as a string.
|
||||
|
||||
If the value isn't found in this set, then this will look for it in a fallback
|
||||
property set (if you've specified one with the setFallbackPropertySet() method),
|
||||
and if it can't find one there, it'll return the default value passed-in.
|
||||
|
||||
@param keyName the name of the property to retrieve
|
||||
@param defaultReturnValue a value to return if the named property doesn't actually exist
|
||||
*/
|
||||
String getValue (StringRef keyName, const String& defaultReturnValue = String()) const noexcept;
|
||||
|
||||
/** Returns one of the properties as an integer.
|
||||
|
||||
If the value isn't found in this set, then this will look for it in a fallback
|
||||
property set (if you've specified one with the setFallbackPropertySet() method),
|
||||
and if it can't find one there, it'll return the default value passed-in.
|
||||
|
||||
@param keyName the name of the property to retrieve
|
||||
@param defaultReturnValue a value to return if the named property doesn't actually exist
|
||||
*/
|
||||
int getIntValue (StringRef keyName, int defaultReturnValue = 0) const noexcept;
|
||||
|
||||
/** Returns one of the properties as an double.
|
||||
|
||||
If the value isn't found in this set, then this will look for it in a fallback
|
||||
property set (if you've specified one with the setFallbackPropertySet() method),
|
||||
and if it can't find one there, it'll return the default value passed-in.
|
||||
|
||||
@param keyName the name of the property to retrieve
|
||||
@param defaultReturnValue a value to return if the named property doesn't actually exist
|
||||
*/
|
||||
double getDoubleValue (StringRef keyName, double defaultReturnValue = 0.0) const noexcept;
|
||||
|
||||
/** Returns one of the properties as an boolean.
|
||||
|
||||
The result will be true if the string found for this key name can be parsed as a non-zero
|
||||
integer.
|
||||
|
||||
If the value isn't found in this set, then this will look for it in a fallback
|
||||
property set (if you've specified one with the setFallbackPropertySet() method),
|
||||
and if it can't find one there, it'll return the default value passed-in.
|
||||
|
||||
@param keyName the name of the property to retrieve
|
||||
@param defaultReturnValue a value to return if the named property doesn't actually exist
|
||||
*/
|
||||
bool getBoolValue (StringRef keyName, bool defaultReturnValue = false) const noexcept;
|
||||
|
||||
/** Returns one of the properties as an XML element.
|
||||
|
||||
The result will a new XMLElement object. It may return nullptr if the key isn't found,
|
||||
or if the entry contains an string that isn't valid XML.
|
||||
|
||||
If the value isn't found in this set, then this will look for it in a fallback
|
||||
property set (if you've specified one with the setFallbackPropertySet() method),
|
||||
and if it can't find one there, it'll return the default value passed-in.
|
||||
|
||||
@param keyName the name of the property to retrieve
|
||||
*/
|
||||
std::unique_ptr<XmlElement> getXmlValue (StringRef keyName) const;
|
||||
|
||||
//==============================================================================
|
||||
/** Sets a named property.
|
||||
|
||||
@param keyName the name of the property to set. (This mustn't be an empty string)
|
||||
@param value the new value to set it to
|
||||
*/
|
||||
void setValue (StringRef keyName, const var& value);
|
||||
|
||||
/** Sets a named property to an XML element.
|
||||
|
||||
@param keyName the name of the property to set. (This mustn't be an empty string)
|
||||
@param xml the new element to set it to. If this is a nullptr, the value will
|
||||
be set to an empty string
|
||||
@see getXmlValue
|
||||
*/
|
||||
void setValue (StringRef keyName, const XmlElement* xml);
|
||||
|
||||
/** This copies all the values from a source PropertySet to this one.
|
||||
This won't remove any existing settings, it just adds any that it finds in the source set.
|
||||
*/
|
||||
void addAllPropertiesFrom (const PropertySet& source);
|
||||
|
||||
//==============================================================================
|
||||
/** Deletes a property.
|
||||
@param keyName the name of the property to delete. (This mustn't be an empty string)
|
||||
*/
|
||||
void removeValue (StringRef keyName);
|
||||
|
||||
/** Returns true if the properties include the given key. */
|
||||
bool containsKey (StringRef keyName) const noexcept;
|
||||
|
||||
/** Removes all values. */
|
||||
void clear();
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the keys/value pair array containing all the properties. */
|
||||
StringPairArray& getAllProperties() noexcept { return properties; }
|
||||
|
||||
/** Returns the lock used when reading or writing to this set */
|
||||
const CriticalSection& getLock() const noexcept { return lock; }
|
||||
|
||||
//==============================================================================
|
||||
/** Returns an XML element which encapsulates all the items in this property set.
|
||||
The string parameter is the tag name that should be used for the node.
|
||||
@see restoreFromXml
|
||||
*/
|
||||
std::unique_ptr<XmlElement> createXml (const String& nodeName) const;
|
||||
|
||||
/** Reloads a set of properties that were previously stored as XML.
|
||||
The node passed in must have been created by the createXml() method.
|
||||
@see createXml
|
||||
*/
|
||||
void restoreFromXml (const XmlElement& xml);
|
||||
|
||||
//==============================================================================
|
||||
/** Sets up a second PopertySet that will be used to look up any values that aren't
|
||||
set in this one.
|
||||
|
||||
If you set this up to be a pointer to a second property set, then whenever one
|
||||
of the getValue() methods fails to find an entry in this set, it will look up that
|
||||
value in the fallback set, and if it finds it, it will return that.
|
||||
|
||||
Make sure that you don't delete the fallback set while it's still being used by
|
||||
another set! To remove the fallback set, just call this method with a null pointer.
|
||||
|
||||
@see getFallbackPropertySet
|
||||
*/
|
||||
void setFallbackPropertySet (PropertySet* fallbackProperties) noexcept;
|
||||
|
||||
/** Returns the fallback property set.
|
||||
@see setFallbackPropertySet
|
||||
*/
|
||||
PropertySet* getFallbackPropertySet() const noexcept { return fallbackProperties; }
|
||||
|
||||
protected:
|
||||
/** Subclasses can override this to be told when one of the properties has been changed. */
|
||||
virtual void propertyChanged();
|
||||
|
||||
private:
|
||||
StringPairArray properties;
|
||||
PropertySet* fallbackProperties;
|
||||
CriticalSection lock;
|
||||
bool ignoreCaseOfKeys;
|
||||
|
||||
JUCE_LEAK_DETECTOR (PropertySet)
|
||||
};
|
||||
|
||||
} // namespace juce
|
||||
|
@ -1,179 +1,179 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
#if JUCE_UNIT_TESTS
|
||||
|
||||
class ReferenceCountedArrayTests : public UnitTest
|
||||
{
|
||||
public:
|
||||
ReferenceCountedArrayTests()
|
||||
: UnitTest ("ReferenceCountedArray", UnitTestCategories::containers)
|
||||
{}
|
||||
|
||||
//==============================================================================
|
||||
void runTest() override
|
||||
{
|
||||
beginTest ("Add derived objects");
|
||||
{
|
||||
ReferenceCountedArray<TestDerivedObj> derivedArray;
|
||||
derivedArray.add (static_cast<TestDerivedObj*> (new TestBaseObj()));
|
||||
expectEquals (derivedArray.size(), 1);
|
||||
expectEquals (derivedArray.getObjectPointer (0)->getReferenceCount(), 1);
|
||||
expectEquals (derivedArray[0]->getReferenceCount(), 2);
|
||||
|
||||
for (auto o : derivedArray)
|
||||
expectEquals (o->getReferenceCount(), 1);
|
||||
|
||||
ReferenceCountedArray<TestBaseObj> baseArray;
|
||||
baseArray.addArray (derivedArray);
|
||||
|
||||
for (auto o : baseArray)
|
||||
expectEquals (o->getReferenceCount(), 2);
|
||||
|
||||
derivedArray.clearQuick();
|
||||
baseArray.clearQuick();
|
||||
|
||||
auto* baseObject = new TestBaseObj();
|
||||
TestBaseObj::Ptr baseObjectPtr = baseObject;
|
||||
expectEquals (baseObject->getReferenceCount(), 1);
|
||||
|
||||
auto* derivedObject = new TestDerivedObj();
|
||||
TestDerivedObj::Ptr derivedObjectPtr = derivedObject;
|
||||
expectEquals (derivedObject->getReferenceCount(), 1);
|
||||
|
||||
baseArray.add (baseObject);
|
||||
baseArray.add (derivedObject);
|
||||
|
||||
for (auto o : baseArray)
|
||||
expectEquals (o->getReferenceCount(), 2);
|
||||
|
||||
expectEquals (baseObject->getReferenceCount(), 2);
|
||||
expectEquals (derivedObject->getReferenceCount(), 2);
|
||||
|
||||
derivedArray.add (derivedObject);
|
||||
|
||||
for (auto o : derivedArray)
|
||||
expectEquals (o->getReferenceCount(), 3);
|
||||
|
||||
derivedArray.clearQuick();
|
||||
baseArray.clearQuick();
|
||||
|
||||
expectEquals (baseObject->getReferenceCount(), 1);
|
||||
expectEquals (derivedObject->getReferenceCount(), 1);
|
||||
|
||||
baseArray.add (baseObjectPtr);
|
||||
baseArray.add (derivedObjectPtr.get());
|
||||
|
||||
for (auto o : baseArray)
|
||||
expectEquals (o->getReferenceCount(), 2);
|
||||
|
||||
derivedArray.add (derivedObjectPtr);
|
||||
|
||||
for (auto o : derivedArray)
|
||||
expectEquals (o->getReferenceCount(), 3);
|
||||
}
|
||||
|
||||
beginTest ("Iterate in destructor");
|
||||
{
|
||||
{
|
||||
ReferenceCountedArray<DestructorObj> arr;
|
||||
|
||||
for (int i = 0; i < 2; ++i)
|
||||
arr.add (new DestructorObj (*this, arr));
|
||||
}
|
||||
|
||||
ReferenceCountedArray<DestructorObj> arr;
|
||||
|
||||
for (int i = 0; i < 1025; ++i)
|
||||
arr.add (new DestructorObj (*this, arr));
|
||||
|
||||
while (! arr.isEmpty())
|
||||
arr.remove (0);
|
||||
|
||||
for (int i = 0; i < 1025; ++i)
|
||||
arr.add (new DestructorObj (*this, arr));
|
||||
|
||||
arr.removeRange (1, arr.size() - 3);
|
||||
|
||||
for (int i = 0; i < 1025; ++i)
|
||||
arr.add (new DestructorObj (*this, arr));
|
||||
|
||||
arr.set (500, new DestructorObj (*this, arr));
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
struct TestBaseObj : public ReferenceCountedObject
|
||||
{
|
||||
using Ptr = ReferenceCountedObjectPtr<TestBaseObj>;
|
||||
|
||||
TestBaseObj() = default;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TestBaseObj)
|
||||
};
|
||||
|
||||
struct TestDerivedObj : public TestBaseObj
|
||||
{
|
||||
using Ptr = ReferenceCountedObjectPtr<TestDerivedObj>;
|
||||
|
||||
TestDerivedObj() = default;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TestDerivedObj)
|
||||
};
|
||||
|
||||
struct DestructorObj : public ReferenceCountedObject
|
||||
{
|
||||
DestructorObj (ReferenceCountedArrayTests& p,
|
||||
ReferenceCountedArray<DestructorObj>& arr)
|
||||
: parent (p), objectArray (arr)
|
||||
{}
|
||||
|
||||
~DestructorObj()
|
||||
{
|
||||
data = 0;
|
||||
|
||||
for (auto* o : objectArray)
|
||||
{
|
||||
parent.expect (o != nullptr);
|
||||
parent.expect (o != this);
|
||||
|
||||
if (o != nullptr)
|
||||
parent.expectEquals (o->data, 374);
|
||||
}
|
||||
}
|
||||
|
||||
ReferenceCountedArrayTests& parent;
|
||||
ReferenceCountedArray<DestructorObj>& objectArray;
|
||||
int data = 374;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DestructorObj)
|
||||
};
|
||||
};
|
||||
|
||||
static ReferenceCountedArrayTests referenceCountedArrayTests;
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace juce
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
#if JUCE_UNIT_TESTS
|
||||
|
||||
class ReferenceCountedArrayTests : public UnitTest
|
||||
{
|
||||
public:
|
||||
ReferenceCountedArrayTests()
|
||||
: UnitTest ("ReferenceCountedArray", UnitTestCategories::containers)
|
||||
{}
|
||||
|
||||
//==============================================================================
|
||||
void runTest() override
|
||||
{
|
||||
beginTest ("Add derived objects");
|
||||
{
|
||||
ReferenceCountedArray<TestDerivedObj> derivedArray;
|
||||
derivedArray.add (static_cast<TestDerivedObj*> (new TestBaseObj()));
|
||||
expectEquals (derivedArray.size(), 1);
|
||||
expectEquals (derivedArray.getObjectPointer (0)->getReferenceCount(), 1);
|
||||
expectEquals (derivedArray[0]->getReferenceCount(), 2);
|
||||
|
||||
for (auto o : derivedArray)
|
||||
expectEquals (o->getReferenceCount(), 1);
|
||||
|
||||
ReferenceCountedArray<TestBaseObj> baseArray;
|
||||
baseArray.addArray (derivedArray);
|
||||
|
||||
for (auto o : baseArray)
|
||||
expectEquals (o->getReferenceCount(), 2);
|
||||
|
||||
derivedArray.clearQuick();
|
||||
baseArray.clearQuick();
|
||||
|
||||
auto* baseObject = new TestBaseObj();
|
||||
TestBaseObj::Ptr baseObjectPtr = baseObject;
|
||||
expectEquals (baseObject->getReferenceCount(), 1);
|
||||
|
||||
auto* derivedObject = new TestDerivedObj();
|
||||
TestDerivedObj::Ptr derivedObjectPtr = derivedObject;
|
||||
expectEquals (derivedObject->getReferenceCount(), 1);
|
||||
|
||||
baseArray.add (baseObject);
|
||||
baseArray.add (derivedObject);
|
||||
|
||||
for (auto o : baseArray)
|
||||
expectEquals (o->getReferenceCount(), 2);
|
||||
|
||||
expectEquals (baseObject->getReferenceCount(), 2);
|
||||
expectEquals (derivedObject->getReferenceCount(), 2);
|
||||
|
||||
derivedArray.add (derivedObject);
|
||||
|
||||
for (auto o : derivedArray)
|
||||
expectEquals (o->getReferenceCount(), 3);
|
||||
|
||||
derivedArray.clearQuick();
|
||||
baseArray.clearQuick();
|
||||
|
||||
expectEquals (baseObject->getReferenceCount(), 1);
|
||||
expectEquals (derivedObject->getReferenceCount(), 1);
|
||||
|
||||
baseArray.add (baseObjectPtr);
|
||||
baseArray.add (derivedObjectPtr.get());
|
||||
|
||||
for (auto o : baseArray)
|
||||
expectEquals (o->getReferenceCount(), 2);
|
||||
|
||||
derivedArray.add (derivedObjectPtr);
|
||||
|
||||
for (auto o : derivedArray)
|
||||
expectEquals (o->getReferenceCount(), 3);
|
||||
}
|
||||
|
||||
beginTest ("Iterate in destructor");
|
||||
{
|
||||
{
|
||||
ReferenceCountedArray<DestructorObj> arr;
|
||||
|
||||
for (int i = 0; i < 2; ++i)
|
||||
arr.add (new DestructorObj (*this, arr));
|
||||
}
|
||||
|
||||
ReferenceCountedArray<DestructorObj> arr;
|
||||
|
||||
for (int i = 0; i < 1025; ++i)
|
||||
arr.add (new DestructorObj (*this, arr));
|
||||
|
||||
while (! arr.isEmpty())
|
||||
arr.remove (0);
|
||||
|
||||
for (int i = 0; i < 1025; ++i)
|
||||
arr.add (new DestructorObj (*this, arr));
|
||||
|
||||
arr.removeRange (1, arr.size() - 3);
|
||||
|
||||
for (int i = 0; i < 1025; ++i)
|
||||
arr.add (new DestructorObj (*this, arr));
|
||||
|
||||
arr.set (500, new DestructorObj (*this, arr));
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
struct TestBaseObj : public ReferenceCountedObject
|
||||
{
|
||||
using Ptr = ReferenceCountedObjectPtr<TestBaseObj>;
|
||||
|
||||
TestBaseObj() = default;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TestBaseObj)
|
||||
};
|
||||
|
||||
struct TestDerivedObj : public TestBaseObj
|
||||
{
|
||||
using Ptr = ReferenceCountedObjectPtr<TestDerivedObj>;
|
||||
|
||||
TestDerivedObj() = default;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TestDerivedObj)
|
||||
};
|
||||
|
||||
struct DestructorObj : public ReferenceCountedObject
|
||||
{
|
||||
DestructorObj (ReferenceCountedArrayTests& p,
|
||||
ReferenceCountedArray<DestructorObj>& arr)
|
||||
: parent (p), objectArray (arr)
|
||||
{}
|
||||
|
||||
~DestructorObj()
|
||||
{
|
||||
data = 0;
|
||||
|
||||
for (auto* o : objectArray)
|
||||
{
|
||||
parent.expect (o != nullptr);
|
||||
parent.expect (o != this);
|
||||
|
||||
if (o != nullptr)
|
||||
parent.expectEquals (o->data, 374);
|
||||
}
|
||||
}
|
||||
|
||||
ReferenceCountedArrayTests& parent;
|
||||
ReferenceCountedArray<DestructorObj>& objectArray;
|
||||
int data = 374;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DestructorObj)
|
||||
};
|
||||
};
|
||||
|
||||
static ReferenceCountedArrayTests referenceCountedArrayTests;
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace juce
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,93 +1,93 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Helper class providing an RAII-based mechanism for temporarily setting and
|
||||
then re-setting a value.
|
||||
|
||||
E.g. @code
|
||||
int x = 1;
|
||||
|
||||
{
|
||||
ScopedValueSetter setter (x, 2);
|
||||
|
||||
// x is now 2
|
||||
}
|
||||
|
||||
// x is now 1 again
|
||||
|
||||
{
|
||||
ScopedValueSetter setter (x, 3, 4);
|
||||
|
||||
// x is now 3
|
||||
}
|
||||
|
||||
// x is now 4
|
||||
@endcode
|
||||
|
||||
@tags{Core}
|
||||
*/
|
||||
template <typename ValueType>
|
||||
class ScopedValueSetter
|
||||
{
|
||||
public:
|
||||
/** Creates a ScopedValueSetter that will immediately change the specified value to the
|
||||
given new value, and will then reset it to its original value when this object is deleted.
|
||||
*/
|
||||
ScopedValueSetter (ValueType& valueToSet,
|
||||
ValueType newValue)
|
||||
: value (valueToSet),
|
||||
originalValue (valueToSet)
|
||||
{
|
||||
valueToSet = newValue;
|
||||
}
|
||||
|
||||
/** Creates a ScopedValueSetter that will immediately change the specified value to the
|
||||
given new value, and will then reset it to be valueWhenDeleted when this object is deleted.
|
||||
*/
|
||||
ScopedValueSetter (ValueType& valueToSet,
|
||||
ValueType newValue,
|
||||
ValueType valueWhenDeleted)
|
||||
: value (valueToSet),
|
||||
originalValue (valueWhenDeleted)
|
||||
{
|
||||
valueToSet = newValue;
|
||||
}
|
||||
|
||||
~ScopedValueSetter()
|
||||
{
|
||||
value = originalValue;
|
||||
}
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
ValueType& value;
|
||||
const ValueType originalValue;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE (ScopedValueSetter)
|
||||
};
|
||||
|
||||
} // namespace juce
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Helper class providing an RAII-based mechanism for temporarily setting and
|
||||
then re-setting a value.
|
||||
|
||||
E.g. @code
|
||||
int x = 1;
|
||||
|
||||
{
|
||||
ScopedValueSetter setter (x, 2);
|
||||
|
||||
// x is now 2
|
||||
}
|
||||
|
||||
// x is now 1 again
|
||||
|
||||
{
|
||||
ScopedValueSetter setter (x, 3, 4);
|
||||
|
||||
// x is now 3
|
||||
}
|
||||
|
||||
// x is now 4
|
||||
@endcode
|
||||
|
||||
@tags{Core}
|
||||
*/
|
||||
template <typename ValueType>
|
||||
class ScopedValueSetter
|
||||
{
|
||||
public:
|
||||
/** Creates a ScopedValueSetter that will immediately change the specified value to the
|
||||
given new value, and will then reset it to its original value when this object is deleted.
|
||||
*/
|
||||
ScopedValueSetter (ValueType& valueToSet,
|
||||
ValueType newValue)
|
||||
: value (valueToSet),
|
||||
originalValue (valueToSet)
|
||||
{
|
||||
valueToSet = newValue;
|
||||
}
|
||||
|
||||
/** Creates a ScopedValueSetter that will immediately change the specified value to the
|
||||
given new value, and will then reset it to be valueWhenDeleted when this object is deleted.
|
||||
*/
|
||||
ScopedValueSetter (ValueType& valueToSet,
|
||||
ValueType newValue,
|
||||
ValueType valueWhenDeleted)
|
||||
: value (valueToSet),
|
||||
originalValue (valueWhenDeleted)
|
||||
{
|
||||
valueToSet = newValue;
|
||||
}
|
||||
|
||||
~ScopedValueSetter()
|
||||
{
|
||||
value = originalValue;
|
||||
}
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
ValueType& value;
|
||||
const ValueType originalValue;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE (ScopedValueSetter)
|
||||
};
|
||||
|
||||
} // namespace juce
|
||||
|
@ -1,126 +1,126 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Encapsulates the logic for a single-threaded FIFO.
|
||||
|
||||
This might be useful for building buffers which can be written and read in
|
||||
blocks of different sizes. For example, in an audio effect we might wish to
|
||||
run some processing on fixed-size blocks of audio input, but the host may
|
||||
provide input blocks of varying sizes. In this situation, we might want to
|
||||
store the previous input in a buffer, and extract a fixed-size block
|
||||
whenever there are enough samples available. The SingleThreadedAbstractFifo
|
||||
implements logic suitable for this use-case.
|
||||
|
||||
This class is quite similar to AbstractFifo, in that it only keeps track of
|
||||
the current read/write locations. The user is responsible for providing the
|
||||
actual buffer that will be read/written.
|
||||
|
||||
The intended usage of this class is as follows:
|
||||
- Create some backing storage in a vector, AudioBuffer etc.
|
||||
- Construct a SingleThreadedAbstractFifo to manage the buffer, passing the
|
||||
number of items in the buffer.
|
||||
- Each time new input is ready, call write(), passing the number of items
|
||||
you wish to write into the buffer. This function returns a pair of ranges
|
||||
describing which indices in the backing storage should be written.
|
||||
- Call getNumReadable() to find out how many items are ready to read from
|
||||
the buffer.
|
||||
- If there are enough items ready to read, call read(), passing the number
|
||||
of items you require. This function returns a pair of ranges describing
|
||||
which indices in the backing storage may be read.
|
||||
|
||||
Unlike AbstractFifo, the SingleThreadedAbstractFifo is intended for use
|
||||
from a single thread. It is not safe to call any non-const member function
|
||||
of SingleThreadedAbstractFifo concurrently with any other member function.
|
||||
|
||||
@see AbstractFifo
|
||||
|
||||
@tags{Core}
|
||||
*/
|
||||
class SingleThreadedAbstractFifo
|
||||
{
|
||||
public:
|
||||
/** Creates a SingleThreadedAbstractFifo with no size. */
|
||||
SingleThreadedAbstractFifo() = default;
|
||||
|
||||
/** Creates a SingleThreadedAbstractFifo that can manage a buffer of the specified size. */
|
||||
explicit SingleThreadedAbstractFifo (int sizeIn)
|
||||
: size (sizeIn)
|
||||
{
|
||||
// This class only works properly when the size is a power of two.
|
||||
// Use nextPowerOfTwo() to find a good size, and ensure that your
|
||||
// backing storage is the same size.
|
||||
jassert (isPowerOfTwo (sizeIn));
|
||||
}
|
||||
|
||||
/** Returns the number of unused elements present in the buffer. */
|
||||
int getRemainingSpace() const { return size - numReadable; }
|
||||
|
||||
/** Returns the number of pending elements present in the buffer. */
|
||||
int getNumReadable() const { return numReadable; }
|
||||
|
||||
/** Returns the size of the managed buffer. */
|
||||
int getSize() const { return size; }
|
||||
|
||||
/** Returns two blocks in the buffer where new items may be written.
|
||||
|
||||
Note that if the buffer is running low on free space, the sum of the lengths of
|
||||
the returned ranges may be less than num!
|
||||
*/
|
||||
std::array<Range<int>, 2> write (int num)
|
||||
{
|
||||
const auto startPos = (readPos + numReadable) & (size - 1);
|
||||
const auto maxToWrite = jmin (getRemainingSpace(), num);
|
||||
const auto firstBlockSize = jmin (maxToWrite, size - startPos);
|
||||
|
||||
numReadable += maxToWrite;
|
||||
|
||||
return { { { startPos, startPos + firstBlockSize }, { 0, maxToWrite - firstBlockSize } } };
|
||||
}
|
||||
|
||||
/** Returns two blocks in the buffer from which new items may be read.
|
||||
|
||||
Note that if the buffer doesn't have the requested number of items available,
|
||||
the sum of the lengths of the returned ranges may be less than num!
|
||||
*/
|
||||
std::array<Range<int>, 2> read (int num)
|
||||
{
|
||||
const auto startPos = readPos;
|
||||
const auto maxToRead = jmin (numReadable, num);
|
||||
const auto firstBlockSize = jmin (maxToRead, size - startPos);
|
||||
|
||||
readPos = (startPos + maxToRead) & (size - 1);
|
||||
numReadable -= maxToRead;
|
||||
|
||||
return { { { startPos, startPos + firstBlockSize }, { 0, maxToRead - firstBlockSize } } };
|
||||
}
|
||||
|
||||
private:
|
||||
int size = 0, readPos = 0, numReadable = 0;
|
||||
};
|
||||
|
||||
|
||||
} // namespace juce
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Encapsulates the logic for a single-threaded FIFO.
|
||||
|
||||
This might be useful for building buffers which can be written and read in
|
||||
blocks of different sizes. For example, in an audio effect we might wish to
|
||||
run some processing on fixed-size blocks of audio input, but the host may
|
||||
provide input blocks of varying sizes. In this situation, we might want to
|
||||
store the previous input in a buffer, and extract a fixed-size block
|
||||
whenever there are enough samples available. The SingleThreadedAbstractFifo
|
||||
implements logic suitable for this use-case.
|
||||
|
||||
This class is quite similar to AbstractFifo, in that it only keeps track of
|
||||
the current read/write locations. The user is responsible for providing the
|
||||
actual buffer that will be read/written.
|
||||
|
||||
The intended usage of this class is as follows:
|
||||
- Create some backing storage in a vector, AudioBuffer etc.
|
||||
- Construct a SingleThreadedAbstractFifo to manage the buffer, passing the
|
||||
number of items in the buffer.
|
||||
- Each time new input is ready, call write(), passing the number of items
|
||||
you wish to write into the buffer. This function returns a pair of ranges
|
||||
describing which indices in the backing storage should be written.
|
||||
- Call getNumReadable() to find out how many items are ready to read from
|
||||
the buffer.
|
||||
- If there are enough items ready to read, call read(), passing the number
|
||||
of items you require. This function returns a pair of ranges describing
|
||||
which indices in the backing storage may be read.
|
||||
|
||||
Unlike AbstractFifo, the SingleThreadedAbstractFifo is intended for use
|
||||
from a single thread. It is not safe to call any non-const member function
|
||||
of SingleThreadedAbstractFifo concurrently with any other member function.
|
||||
|
||||
@see AbstractFifo
|
||||
|
||||
@tags{Core}
|
||||
*/
|
||||
class SingleThreadedAbstractFifo
|
||||
{
|
||||
public:
|
||||
/** Creates a SingleThreadedAbstractFifo with no size. */
|
||||
SingleThreadedAbstractFifo() = default;
|
||||
|
||||
/** Creates a SingleThreadedAbstractFifo that can manage a buffer of the specified size. */
|
||||
explicit SingleThreadedAbstractFifo (int sizeIn)
|
||||
: size (sizeIn)
|
||||
{
|
||||
// This class only works properly when the size is a power of two.
|
||||
// Use nextPowerOfTwo() to find a good size, and ensure that your
|
||||
// backing storage is the same size.
|
||||
jassert (isPowerOfTwo (sizeIn));
|
||||
}
|
||||
|
||||
/** Returns the number of unused elements present in the buffer. */
|
||||
int getRemainingSpace() const { return size - numReadable; }
|
||||
|
||||
/** Returns the number of pending elements present in the buffer. */
|
||||
int getNumReadable() const { return numReadable; }
|
||||
|
||||
/** Returns the size of the managed buffer. */
|
||||
int getSize() const { return size; }
|
||||
|
||||
/** Returns two blocks in the buffer where new items may be written.
|
||||
|
||||
Note that if the buffer is running low on free space, the sum of the lengths of
|
||||
the returned ranges may be less than num!
|
||||
*/
|
||||
std::array<Range<int>, 2> write (int num)
|
||||
{
|
||||
const auto startPos = (readPos + numReadable) & (size - 1);
|
||||
const auto maxToWrite = jmin (getRemainingSpace(), num);
|
||||
const auto firstBlockSize = jmin (maxToWrite, size - startPos);
|
||||
|
||||
numReadable += maxToWrite;
|
||||
|
||||
return { { { startPos, startPos + firstBlockSize }, { 0, maxToWrite - firstBlockSize } } };
|
||||
}
|
||||
|
||||
/** Returns two blocks in the buffer from which new items may be read.
|
||||
|
||||
Note that if the buffer doesn't have the requested number of items available,
|
||||
the sum of the lengths of the returned ranges may be less than num!
|
||||
*/
|
||||
std::array<Range<int>, 2> read (int num)
|
||||
{
|
||||
const auto startPos = readPos;
|
||||
const auto maxToRead = jmin (numReadable, num);
|
||||
const auto firstBlockSize = jmin (maxToRead, size - startPos);
|
||||
|
||||
readPos = (startPos + maxToRead) & (size - 1);
|
||||
numReadable -= maxToRead;
|
||||
|
||||
return { { { startPos, startPos + firstBlockSize }, { 0, maxToRead - firstBlockSize } } };
|
||||
}
|
||||
|
||||
private:
|
||||
int size = 0, readPos = 0, numReadable = 0;
|
||||
};
|
||||
|
||||
|
||||
} // namespace juce
|
||||
|
@ -1,489 +1,489 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4512)
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Holds a set of unique primitive objects, such as ints or doubles.
|
||||
|
||||
A set can only hold one item with a given value, so if for example it's a
|
||||
set of integers, attempting to add the same integer twice will do nothing
|
||||
the second time.
|
||||
|
||||
Internally, the list of items is kept sorted (which means that whatever
|
||||
kind of primitive type is used must support the ==, <, >, <= and >= operators
|
||||
to determine the order), and searching the set for known values is very fast
|
||||
because it uses a binary-chop method.
|
||||
|
||||
Note that if you're using a class or struct as the element type, it must be
|
||||
capable of being copied or moved with a straightforward memcpy, rather than
|
||||
needing construction and destruction code.
|
||||
|
||||
To make all the set's methods thread-safe, pass in "CriticalSection" as the templated
|
||||
TypeOfCriticalSectionToUse parameter, instead of the default DummyCriticalSection.
|
||||
|
||||
@see Array, OwnedArray, ReferenceCountedArray, StringArray, CriticalSection
|
||||
|
||||
@tags{Core}
|
||||
*/
|
||||
template <class ElementType, class TypeOfCriticalSectionToUse = DummyCriticalSection>
|
||||
class SortedSet
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates an empty set. */
|
||||
SortedSet() = default;
|
||||
|
||||
/** Creates a copy of another set. */
|
||||
SortedSet (const SortedSet&) = default;
|
||||
|
||||
/** Creates a copy of another set. */
|
||||
SortedSet (SortedSet&&) noexcept = default;
|
||||
|
||||
/** Makes a copy of another set. */
|
||||
SortedSet& operator= (const SortedSet&) = default;
|
||||
|
||||
/** Makes a copy of another set. */
|
||||
SortedSet& operator= (SortedSet&&) noexcept = default;
|
||||
|
||||
/** Destructor. */
|
||||
~SortedSet() = default;
|
||||
|
||||
//==============================================================================
|
||||
/** Compares this set to another one.
|
||||
Two sets are considered equal if they both contain the same set of elements.
|
||||
@param other the other set to compare with
|
||||
*/
|
||||
bool operator== (const SortedSet<ElementType>& other) const noexcept
|
||||
{
|
||||
return data == other.data;
|
||||
}
|
||||
|
||||
/** Compares this set to another one.
|
||||
Two sets are considered equal if they both contain the same set of elements.
|
||||
@param other the other set to compare with
|
||||
*/
|
||||
bool operator!= (const SortedSet<ElementType>& other) const noexcept
|
||||
{
|
||||
return ! operator== (other);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Removes all elements from the set.
|
||||
|
||||
This will remove all the elements, and free any storage that the set is
|
||||
using. To clear it without freeing the storage, use the clearQuick()
|
||||
method instead.
|
||||
|
||||
@see clearQuick
|
||||
*/
|
||||
void clear() noexcept
|
||||
{
|
||||
data.clear();
|
||||
}
|
||||
|
||||
/** Removes all elements from the set without freeing the array's allocated storage.
|
||||
@see clear
|
||||
*/
|
||||
void clearQuick() noexcept
|
||||
{
|
||||
data.clearQuick();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the current number of elements in the set. */
|
||||
inline int size() const noexcept
|
||||
{
|
||||
return data.size();
|
||||
}
|
||||
|
||||
/** Returns true if the set is empty, false otherwise. */
|
||||
inline bool isEmpty() const noexcept
|
||||
{
|
||||
return size() == 0;
|
||||
}
|
||||
|
||||
/** Returns one of the elements in the set.
|
||||
|
||||
If the index passed in is beyond the range of valid elements, this
|
||||
will return zero.
|
||||
|
||||
If you're certain that the index will always be a valid element, you
|
||||
can call getUnchecked() instead, which is faster.
|
||||
|
||||
@param index the index of the element being requested (0 is the first element in the set)
|
||||
@see getUnchecked, getFirst, getLast
|
||||
*/
|
||||
inline ElementType operator[] (const int index) const noexcept
|
||||
{
|
||||
return data [index];
|
||||
}
|
||||
|
||||
/** Returns one of the elements in the set, without checking the index passed in.
|
||||
Unlike the operator[] method, this will try to return an element without
|
||||
checking that the index is within the bounds of the set, so should only
|
||||
be used when you're confident that it will always be a valid index.
|
||||
|
||||
@param index the index of the element being requested (0 is the first element in the set)
|
||||
@see operator[], getFirst, getLast
|
||||
*/
|
||||
inline ElementType getUnchecked (const int index) const noexcept
|
||||
{
|
||||
return data.getUnchecked (index);
|
||||
}
|
||||
|
||||
/** Returns a direct reference to one of the elements in the set, without checking the index passed in.
|
||||
|
||||
This is like getUnchecked, but returns a direct reference to the element, so that
|
||||
you can alter it directly. Obviously this can be dangerous, so only use it when
|
||||
absolutely necessary.
|
||||
|
||||
@param index the index of the element being requested (0 is the first element in the array)
|
||||
*/
|
||||
inline ElementType& getReference (const int index) noexcept
|
||||
{
|
||||
return data.getReference (index);
|
||||
}
|
||||
|
||||
/** Returns a direct reference to one of the elements in the set, without checking the index passed in.
|
||||
|
||||
@param index the index of the element being requested (0 is the first element in the array)
|
||||
*/
|
||||
inline const ElementType& getReference (const int index) const noexcept
|
||||
{
|
||||
return data.getReference (index);
|
||||
}
|
||||
|
||||
/** Returns the first element in the set, or 0 if the set is empty.
|
||||
@see operator[], getUnchecked, getLast
|
||||
*/
|
||||
inline ElementType getFirst() const noexcept
|
||||
{
|
||||
return data.getFirst();
|
||||
}
|
||||
|
||||
/** Returns the last element in the set, or 0 if the set is empty.
|
||||
@see operator[], getUnchecked, getFirst
|
||||
*/
|
||||
inline ElementType getLast() const noexcept
|
||||
{
|
||||
return data.getLast();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Returns a pointer to the first element in the set.
|
||||
This method is provided for compatibility with standard C++ iteration mechanisms.
|
||||
*/
|
||||
inline const ElementType* begin() const noexcept
|
||||
{
|
||||
return data.begin();
|
||||
}
|
||||
|
||||
/** Returns a pointer to the element which follows the last element in the set.
|
||||
This method is provided for compatibility with standard C++ iteration mechanisms.
|
||||
*/
|
||||
inline const ElementType* end() const noexcept
|
||||
{
|
||||
return data.end();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Finds the index of the first element which matches the value passed in.
|
||||
|
||||
This will search the set for the given object, and return the index
|
||||
of its first occurrence. If the object isn't found, the method will return -1.
|
||||
|
||||
@param elementToLookFor the value or object to look for
|
||||
@returns the index of the object, or -1 if it's not found
|
||||
*/
|
||||
int indexOf (const ElementType& elementToLookFor) const noexcept
|
||||
{
|
||||
const ScopedLockType lock (data.getLock());
|
||||
|
||||
int s = 0;
|
||||
int e = data.size();
|
||||
|
||||
for (;;)
|
||||
{
|
||||
if (s >= e)
|
||||
return -1;
|
||||
|
||||
if (elementToLookFor == data.getReference (s))
|
||||
return s;
|
||||
|
||||
auto halfway = (s + e) / 2;
|
||||
|
||||
if (halfway == s)
|
||||
return -1;
|
||||
|
||||
if (elementToLookFor < data.getReference (halfway))
|
||||
e = halfway;
|
||||
else
|
||||
s = halfway;
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns true if the set contains at least one occurrence of an object.
|
||||
|
||||
@param elementToLookFor the value or object to look for
|
||||
@returns true if the item is found
|
||||
*/
|
||||
bool contains (const ElementType& elementToLookFor) const noexcept
|
||||
{
|
||||
return indexOf (elementToLookFor) >= 0;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Adds a new element to the set, (as long as it's not already in there).
|
||||
|
||||
Note that if a matching element already exists, the new value will be assigned
|
||||
to the existing one using operator=, so that if there are any differences between
|
||||
the objects which were not recognised by the object's operator==, then the
|
||||
set will always contain a copy of the most recently added one.
|
||||
|
||||
@param newElement the new object to add to the set
|
||||
@returns true if the value was added, or false if it already existed
|
||||
@see set, insert, addIfNotAlreadyThere, addSorted, addSet, addArray
|
||||
*/
|
||||
bool add (const ElementType& newElement) noexcept
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
|
||||
int s = 0;
|
||||
int e = data.size();
|
||||
|
||||
while (s < e)
|
||||
{
|
||||
auto& elem = data.getReference (s);
|
||||
|
||||
if (newElement == elem)
|
||||
{
|
||||
elem = newElement; // force an update in case operator== permits differences.
|
||||
return false;
|
||||
}
|
||||
|
||||
auto halfway = (s + e) / 2;
|
||||
bool isBeforeHalfway = (newElement < data.getReference (halfway));
|
||||
|
||||
if (halfway == s)
|
||||
{
|
||||
if (! isBeforeHalfway)
|
||||
++s;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (isBeforeHalfway)
|
||||
e = halfway;
|
||||
else
|
||||
s = halfway;
|
||||
}
|
||||
|
||||
data.insert (s, newElement);
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Adds elements from an array to this set.
|
||||
|
||||
@param elementsToAdd the array of elements to add
|
||||
@param numElementsToAdd how many elements are in this other array
|
||||
@see add
|
||||
*/
|
||||
void addArray (const ElementType* elementsToAdd,
|
||||
int numElementsToAdd) noexcept
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
|
||||
while (--numElementsToAdd >= 0)
|
||||
add (*elementsToAdd++);
|
||||
}
|
||||
|
||||
/** Adds elements from another set to this one.
|
||||
|
||||
@param setToAddFrom the set from which to copy the elements
|
||||
@param startIndex the first element of the other set to start copying from
|
||||
@param numElementsToAdd how many elements to add from the other set. If this
|
||||
value is negative or greater than the number of available elements,
|
||||
all available elements will be copied.
|
||||
@see add
|
||||
*/
|
||||
template <class OtherSetType>
|
||||
void addSet (const OtherSetType& setToAddFrom,
|
||||
int startIndex = 0,
|
||||
int numElementsToAdd = -1) noexcept
|
||||
{
|
||||
const typename OtherSetType::ScopedLockType lock1 (setToAddFrom.getLock());
|
||||
const ScopedLockType lock2 (getLock());
|
||||
jassert (this != &setToAddFrom);
|
||||
|
||||
if (this != &setToAddFrom)
|
||||
{
|
||||
if (startIndex < 0)
|
||||
{
|
||||
jassertfalse;
|
||||
startIndex = 0;
|
||||
}
|
||||
|
||||
if (numElementsToAdd < 0 || startIndex + numElementsToAdd > setToAddFrom.size())
|
||||
numElementsToAdd = setToAddFrom.size() - startIndex;
|
||||
|
||||
if (numElementsToAdd > 0)
|
||||
addArray (&setToAddFrom.data.getReference (startIndex), numElementsToAdd);
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Removes an element from the set.
|
||||
|
||||
This will remove the element at a given index.
|
||||
If the index passed in is out-of-range, nothing will happen.
|
||||
|
||||
@param indexToRemove the index of the element to remove
|
||||
@returns the element that has been removed
|
||||
@see removeValue, removeRange
|
||||
*/
|
||||
ElementType remove (const int indexToRemove) noexcept
|
||||
{
|
||||
return data.removeAndReturn (indexToRemove);
|
||||
}
|
||||
|
||||
/** Removes an item from the set.
|
||||
|
||||
This will remove the given element from the set, if it's there.
|
||||
|
||||
@param valueToRemove the object to try to remove
|
||||
@see remove, removeRange
|
||||
*/
|
||||
void removeValue (const ElementType& valueToRemove) noexcept
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
data.remove (indexOf (valueToRemove));
|
||||
}
|
||||
|
||||
/** Removes any elements which are also in another set.
|
||||
|
||||
@param otherSet the other set in which to look for elements to remove
|
||||
@see removeValuesNotIn, remove, removeValue, removeRange
|
||||
*/
|
||||
template <class OtherSetType>
|
||||
void removeValuesIn (const OtherSetType& otherSet) noexcept
|
||||
{
|
||||
const typename OtherSetType::ScopedLockType lock1 (otherSet.getLock());
|
||||
const ScopedLockType lock2 (getLock());
|
||||
|
||||
if (this == &otherSet)
|
||||
{
|
||||
clear();
|
||||
}
|
||||
else if (! otherSet.isEmpty())
|
||||
{
|
||||
for (int i = data.size(); --i >= 0;)
|
||||
if (otherSet.contains (data.getReference (i)))
|
||||
remove (i);
|
||||
}
|
||||
}
|
||||
|
||||
/** Removes any elements which are not found in another set.
|
||||
|
||||
Only elements which occur in this other set will be retained.
|
||||
|
||||
@param otherSet the set in which to look for elements NOT to remove
|
||||
@see removeValuesIn, remove, removeValue, removeRange
|
||||
*/
|
||||
template <class OtherSetType>
|
||||
void removeValuesNotIn (const OtherSetType& otherSet) noexcept
|
||||
{
|
||||
const typename OtherSetType::ScopedLockType lock1 (otherSet.getLock());
|
||||
const ScopedLockType lock2 (getLock());
|
||||
|
||||
if (this != &otherSet)
|
||||
{
|
||||
if (otherSet.isEmpty())
|
||||
{
|
||||
clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = data.size(); --i >= 0;)
|
||||
if (! otherSet.contains (data.getReference (i)))
|
||||
remove (i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** This swaps the contents of this array with those of another array.
|
||||
|
||||
If you need to exchange two arrays, this is vastly quicker than using copy-by-value
|
||||
because it just swaps their internal pointers.
|
||||
*/
|
||||
template <class OtherSetType>
|
||||
void swapWith (OtherSetType& otherSet) noexcept
|
||||
{
|
||||
data.swapWith (otherSet.data);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Reduces the amount of storage being used by the set.
|
||||
|
||||
Sets typically allocate slightly more storage than they need, and after
|
||||
removing elements, they may have quite a lot of unused space allocated.
|
||||
This method will reduce the amount of allocated storage to a minimum.
|
||||
*/
|
||||
void minimiseStorageOverheads() noexcept
|
||||
{
|
||||
data.minimiseStorageOverheads();
|
||||
}
|
||||
|
||||
/** Increases the set's internal storage to hold a minimum number of elements.
|
||||
|
||||
Calling this before adding a large known number of elements means that
|
||||
the set won't have to keep dynamically resizing itself as the elements
|
||||
are added, and it'll therefore be more efficient.
|
||||
*/
|
||||
void ensureStorageAllocated (const int minNumElements)
|
||||
{
|
||||
data.ensureStorageAllocated (minNumElements);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the CriticalSection that locks this array.
|
||||
To lock, you can call getLock().enter() and getLock().exit(), or preferably use
|
||||
an object of ScopedLockType as an RAII lock for it.
|
||||
*/
|
||||
inline const TypeOfCriticalSectionToUse& getLock() const noexcept { return data.getLock(); }
|
||||
|
||||
/** Returns the type of scoped lock to use for locking this array */
|
||||
using ScopedLockType = typename TypeOfCriticalSectionToUse::ScopedLockType;
|
||||
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
Array<ElementType, TypeOfCriticalSectionToUse> data;
|
||||
};
|
||||
|
||||
JUCE_END_IGNORE_WARNINGS_MSVC
|
||||
|
||||
} // namespace juce
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4512)
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Holds a set of unique primitive objects, such as ints or doubles.
|
||||
|
||||
A set can only hold one item with a given value, so if for example it's a
|
||||
set of integers, attempting to add the same integer twice will do nothing
|
||||
the second time.
|
||||
|
||||
Internally, the list of items is kept sorted (which means that whatever
|
||||
kind of primitive type is used must support the ==, <, >, <= and >= operators
|
||||
to determine the order), and searching the set for known values is very fast
|
||||
because it uses a binary-chop method.
|
||||
|
||||
Note that if you're using a class or struct as the element type, it must be
|
||||
capable of being copied or moved with a straightforward memcpy, rather than
|
||||
needing construction and destruction code.
|
||||
|
||||
To make all the set's methods thread-safe, pass in "CriticalSection" as the templated
|
||||
TypeOfCriticalSectionToUse parameter, instead of the default DummyCriticalSection.
|
||||
|
||||
@see Array, OwnedArray, ReferenceCountedArray, StringArray, CriticalSection
|
||||
|
||||
@tags{Core}
|
||||
*/
|
||||
template <class ElementType, class TypeOfCriticalSectionToUse = DummyCriticalSection>
|
||||
class SortedSet
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates an empty set. */
|
||||
SortedSet() = default;
|
||||
|
||||
/** Creates a copy of another set. */
|
||||
SortedSet (const SortedSet&) = default;
|
||||
|
||||
/** Creates a copy of another set. */
|
||||
SortedSet (SortedSet&&) noexcept = default;
|
||||
|
||||
/** Makes a copy of another set. */
|
||||
SortedSet& operator= (const SortedSet&) = default;
|
||||
|
||||
/** Makes a copy of another set. */
|
||||
SortedSet& operator= (SortedSet&&) noexcept = default;
|
||||
|
||||
/** Destructor. */
|
||||
~SortedSet() = default;
|
||||
|
||||
//==============================================================================
|
||||
/** Compares this set to another one.
|
||||
Two sets are considered equal if they both contain the same set of elements.
|
||||
@param other the other set to compare with
|
||||
*/
|
||||
bool operator== (const SortedSet<ElementType>& other) const noexcept
|
||||
{
|
||||
return data == other.data;
|
||||
}
|
||||
|
||||
/** Compares this set to another one.
|
||||
Two sets are considered equal if they both contain the same set of elements.
|
||||
@param other the other set to compare with
|
||||
*/
|
||||
bool operator!= (const SortedSet<ElementType>& other) const noexcept
|
||||
{
|
||||
return ! operator== (other);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Removes all elements from the set.
|
||||
|
||||
This will remove all the elements, and free any storage that the set is
|
||||
using. To clear it without freeing the storage, use the clearQuick()
|
||||
method instead.
|
||||
|
||||
@see clearQuick
|
||||
*/
|
||||
void clear() noexcept
|
||||
{
|
||||
data.clear();
|
||||
}
|
||||
|
||||
/** Removes all elements from the set without freeing the array's allocated storage.
|
||||
@see clear
|
||||
*/
|
||||
void clearQuick() noexcept
|
||||
{
|
||||
data.clearQuick();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the current number of elements in the set. */
|
||||
inline int size() const noexcept
|
||||
{
|
||||
return data.size();
|
||||
}
|
||||
|
||||
/** Returns true if the set is empty, false otherwise. */
|
||||
inline bool isEmpty() const noexcept
|
||||
{
|
||||
return size() == 0;
|
||||
}
|
||||
|
||||
/** Returns one of the elements in the set.
|
||||
|
||||
If the index passed in is beyond the range of valid elements, this
|
||||
will return zero.
|
||||
|
||||
If you're certain that the index will always be a valid element, you
|
||||
can call getUnchecked() instead, which is faster.
|
||||
|
||||
@param index the index of the element being requested (0 is the first element in the set)
|
||||
@see getUnchecked, getFirst, getLast
|
||||
*/
|
||||
inline ElementType operator[] (const int index) const noexcept
|
||||
{
|
||||
return data [index];
|
||||
}
|
||||
|
||||
/** Returns one of the elements in the set, without checking the index passed in.
|
||||
Unlike the operator[] method, this will try to return an element without
|
||||
checking that the index is within the bounds of the set, so should only
|
||||
be used when you're confident that it will always be a valid index.
|
||||
|
||||
@param index the index of the element being requested (0 is the first element in the set)
|
||||
@see operator[], getFirst, getLast
|
||||
*/
|
||||
inline ElementType getUnchecked (const int index) const noexcept
|
||||
{
|
||||
return data.getUnchecked (index);
|
||||
}
|
||||
|
||||
/** Returns a direct reference to one of the elements in the set, without checking the index passed in.
|
||||
|
||||
This is like getUnchecked, but returns a direct reference to the element, so that
|
||||
you can alter it directly. Obviously this can be dangerous, so only use it when
|
||||
absolutely necessary.
|
||||
|
||||
@param index the index of the element being requested (0 is the first element in the array)
|
||||
*/
|
||||
inline ElementType& getReference (const int index) noexcept
|
||||
{
|
||||
return data.getReference (index);
|
||||
}
|
||||
|
||||
/** Returns a direct reference to one of the elements in the set, without checking the index passed in.
|
||||
|
||||
@param index the index of the element being requested (0 is the first element in the array)
|
||||
*/
|
||||
inline const ElementType& getReference (const int index) const noexcept
|
||||
{
|
||||
return data.getReference (index);
|
||||
}
|
||||
|
||||
/** Returns the first element in the set, or 0 if the set is empty.
|
||||
@see operator[], getUnchecked, getLast
|
||||
*/
|
||||
inline ElementType getFirst() const noexcept
|
||||
{
|
||||
return data.getFirst();
|
||||
}
|
||||
|
||||
/** Returns the last element in the set, or 0 if the set is empty.
|
||||
@see operator[], getUnchecked, getFirst
|
||||
*/
|
||||
inline ElementType getLast() const noexcept
|
||||
{
|
||||
return data.getLast();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Returns a pointer to the first element in the set.
|
||||
This method is provided for compatibility with standard C++ iteration mechanisms.
|
||||
*/
|
||||
inline const ElementType* begin() const noexcept
|
||||
{
|
||||
return data.begin();
|
||||
}
|
||||
|
||||
/** Returns a pointer to the element which follows the last element in the set.
|
||||
This method is provided for compatibility with standard C++ iteration mechanisms.
|
||||
*/
|
||||
inline const ElementType* end() const noexcept
|
||||
{
|
||||
return data.end();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Finds the index of the first element which matches the value passed in.
|
||||
|
||||
This will search the set for the given object, and return the index
|
||||
of its first occurrence. If the object isn't found, the method will return -1.
|
||||
|
||||
@param elementToLookFor the value or object to look for
|
||||
@returns the index of the object, or -1 if it's not found
|
||||
*/
|
||||
int indexOf (const ElementType& elementToLookFor) const noexcept
|
||||
{
|
||||
const ScopedLockType lock (data.getLock());
|
||||
|
||||
int s = 0;
|
||||
int e = data.size();
|
||||
|
||||
for (;;)
|
||||
{
|
||||
if (s >= e)
|
||||
return -1;
|
||||
|
||||
if (elementToLookFor == data.getReference (s))
|
||||
return s;
|
||||
|
||||
auto halfway = (s + e) / 2;
|
||||
|
||||
if (halfway == s)
|
||||
return -1;
|
||||
|
||||
if (elementToLookFor < data.getReference (halfway))
|
||||
e = halfway;
|
||||
else
|
||||
s = halfway;
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns true if the set contains at least one occurrence of an object.
|
||||
|
||||
@param elementToLookFor the value or object to look for
|
||||
@returns true if the item is found
|
||||
*/
|
||||
bool contains (const ElementType& elementToLookFor) const noexcept
|
||||
{
|
||||
return indexOf (elementToLookFor) >= 0;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Adds a new element to the set, (as long as it's not already in there).
|
||||
|
||||
Note that if a matching element already exists, the new value will be assigned
|
||||
to the existing one using operator=, so that if there are any differences between
|
||||
the objects which were not recognised by the object's operator==, then the
|
||||
set will always contain a copy of the most recently added one.
|
||||
|
||||
@param newElement the new object to add to the set
|
||||
@returns true if the value was added, or false if it already existed
|
||||
@see set, insert, addIfNotAlreadyThere, addSorted, addSet, addArray
|
||||
*/
|
||||
bool add (const ElementType& newElement) noexcept
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
|
||||
int s = 0;
|
||||
int e = data.size();
|
||||
|
||||
while (s < e)
|
||||
{
|
||||
auto& elem = data.getReference (s);
|
||||
|
||||
if (newElement == elem)
|
||||
{
|
||||
elem = newElement; // force an update in case operator== permits differences.
|
||||
return false;
|
||||
}
|
||||
|
||||
auto halfway = (s + e) / 2;
|
||||
bool isBeforeHalfway = (newElement < data.getReference (halfway));
|
||||
|
||||
if (halfway == s)
|
||||
{
|
||||
if (! isBeforeHalfway)
|
||||
++s;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (isBeforeHalfway)
|
||||
e = halfway;
|
||||
else
|
||||
s = halfway;
|
||||
}
|
||||
|
||||
data.insert (s, newElement);
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Adds elements from an array to this set.
|
||||
|
||||
@param elementsToAdd the array of elements to add
|
||||
@param numElementsToAdd how many elements are in this other array
|
||||
@see add
|
||||
*/
|
||||
void addArray (const ElementType* elementsToAdd,
|
||||
int numElementsToAdd) noexcept
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
|
||||
while (--numElementsToAdd >= 0)
|
||||
add (*elementsToAdd++);
|
||||
}
|
||||
|
||||
/** Adds elements from another set to this one.
|
||||
|
||||
@param setToAddFrom the set from which to copy the elements
|
||||
@param startIndex the first element of the other set to start copying from
|
||||
@param numElementsToAdd how many elements to add from the other set. If this
|
||||
value is negative or greater than the number of available elements,
|
||||
all available elements will be copied.
|
||||
@see add
|
||||
*/
|
||||
template <class OtherSetType>
|
||||
void addSet (const OtherSetType& setToAddFrom,
|
||||
int startIndex = 0,
|
||||
int numElementsToAdd = -1) noexcept
|
||||
{
|
||||
const typename OtherSetType::ScopedLockType lock1 (setToAddFrom.getLock());
|
||||
const ScopedLockType lock2 (getLock());
|
||||
jassert (this != &setToAddFrom);
|
||||
|
||||
if (this != &setToAddFrom)
|
||||
{
|
||||
if (startIndex < 0)
|
||||
{
|
||||
jassertfalse;
|
||||
startIndex = 0;
|
||||
}
|
||||
|
||||
if (numElementsToAdd < 0 || startIndex + numElementsToAdd > setToAddFrom.size())
|
||||
numElementsToAdd = setToAddFrom.size() - startIndex;
|
||||
|
||||
if (numElementsToAdd > 0)
|
||||
addArray (&setToAddFrom.data.getReference (startIndex), numElementsToAdd);
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Removes an element from the set.
|
||||
|
||||
This will remove the element at a given index.
|
||||
If the index passed in is out-of-range, nothing will happen.
|
||||
|
||||
@param indexToRemove the index of the element to remove
|
||||
@returns the element that has been removed
|
||||
@see removeValue, removeRange
|
||||
*/
|
||||
ElementType remove (const int indexToRemove) noexcept
|
||||
{
|
||||
return data.removeAndReturn (indexToRemove);
|
||||
}
|
||||
|
||||
/** Removes an item from the set.
|
||||
|
||||
This will remove the given element from the set, if it's there.
|
||||
|
||||
@param valueToRemove the object to try to remove
|
||||
@see remove, removeRange
|
||||
*/
|
||||
void removeValue (const ElementType& valueToRemove) noexcept
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
data.remove (indexOf (valueToRemove));
|
||||
}
|
||||
|
||||
/** Removes any elements which are also in another set.
|
||||
|
||||
@param otherSet the other set in which to look for elements to remove
|
||||
@see removeValuesNotIn, remove, removeValue, removeRange
|
||||
*/
|
||||
template <class OtherSetType>
|
||||
void removeValuesIn (const OtherSetType& otherSet) noexcept
|
||||
{
|
||||
const typename OtherSetType::ScopedLockType lock1 (otherSet.getLock());
|
||||
const ScopedLockType lock2 (getLock());
|
||||
|
||||
if (this == &otherSet)
|
||||
{
|
||||
clear();
|
||||
}
|
||||
else if (! otherSet.isEmpty())
|
||||
{
|
||||
for (int i = data.size(); --i >= 0;)
|
||||
if (otherSet.contains (data.getReference (i)))
|
||||
remove (i);
|
||||
}
|
||||
}
|
||||
|
||||
/** Removes any elements which are not found in another set.
|
||||
|
||||
Only elements which occur in this other set will be retained.
|
||||
|
||||
@param otherSet the set in which to look for elements NOT to remove
|
||||
@see removeValuesIn, remove, removeValue, removeRange
|
||||
*/
|
||||
template <class OtherSetType>
|
||||
void removeValuesNotIn (const OtherSetType& otherSet) noexcept
|
||||
{
|
||||
const typename OtherSetType::ScopedLockType lock1 (otherSet.getLock());
|
||||
const ScopedLockType lock2 (getLock());
|
||||
|
||||
if (this != &otherSet)
|
||||
{
|
||||
if (otherSet.isEmpty())
|
||||
{
|
||||
clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = data.size(); --i >= 0;)
|
||||
if (! otherSet.contains (data.getReference (i)))
|
||||
remove (i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** This swaps the contents of this array with those of another array.
|
||||
|
||||
If you need to exchange two arrays, this is vastly quicker than using copy-by-value
|
||||
because it just swaps their internal pointers.
|
||||
*/
|
||||
template <class OtherSetType>
|
||||
void swapWith (OtherSetType& otherSet) noexcept
|
||||
{
|
||||
data.swapWith (otherSet.data);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Reduces the amount of storage being used by the set.
|
||||
|
||||
Sets typically allocate slightly more storage than they need, and after
|
||||
removing elements, they may have quite a lot of unused space allocated.
|
||||
This method will reduce the amount of allocated storage to a minimum.
|
||||
*/
|
||||
void minimiseStorageOverheads() noexcept
|
||||
{
|
||||
data.minimiseStorageOverheads();
|
||||
}
|
||||
|
||||
/** Increases the set's internal storage to hold a minimum number of elements.
|
||||
|
||||
Calling this before adding a large known number of elements means that
|
||||
the set won't have to keep dynamically resizing itself as the elements
|
||||
are added, and it'll therefore be more efficient.
|
||||
*/
|
||||
void ensureStorageAllocated (const int minNumElements)
|
||||
{
|
||||
data.ensureStorageAllocated (minNumElements);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the CriticalSection that locks this array.
|
||||
To lock, you can call getLock().enter() and getLock().exit(), or preferably use
|
||||
an object of ScopedLockType as an RAII lock for it.
|
||||
*/
|
||||
inline const TypeOfCriticalSectionToUse& getLock() const noexcept { return data.getLock(); }
|
||||
|
||||
/** Returns the type of scoped lock to use for locking this array */
|
||||
using ScopedLockType = typename TypeOfCriticalSectionToUse::ScopedLockType;
|
||||
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
Array<ElementType, TypeOfCriticalSectionToUse> data;
|
||||
};
|
||||
|
||||
JUCE_END_IGNORE_WARNINGS_MSVC
|
||||
|
||||
} // namespace juce
|
||||
|
@ -1,206 +1,206 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
#if JUCE_UNIT_TESTS
|
||||
|
||||
class SparseSetTests : public UnitTest
|
||||
{
|
||||
public:
|
||||
SparseSetTests()
|
||||
: UnitTest ("SparseSet class", UnitTestCategories::containers)
|
||||
{}
|
||||
|
||||
void runTest() override
|
||||
{
|
||||
beginTest ("basic operations");
|
||||
{
|
||||
SparseSet<int> set;
|
||||
|
||||
expect (set.isEmpty());
|
||||
expectEquals (set.size(), 0);
|
||||
expectEquals (set.getNumRanges(), 0);
|
||||
expect (set.getTotalRange().isEmpty());
|
||||
|
||||
set.addRange ({0, 10});
|
||||
expect (! set.isEmpty());
|
||||
expectEquals (set.size(), 10);
|
||||
expectEquals (set.getNumRanges(), 1);
|
||||
expect (! set.getTotalRange().isEmpty());
|
||||
expect (set.getRange (0) == Range<int> (0, 10));
|
||||
|
||||
expectEquals (set[0], 0);
|
||||
expectEquals (set[5], 5);
|
||||
expectEquals (set[9], 9);
|
||||
// Index out of range yields a default value for a type
|
||||
expectEquals (set[10], 0);
|
||||
expect (set.contains (0));
|
||||
expect (set.contains (9));
|
||||
expect (! set.contains (10));
|
||||
}
|
||||
|
||||
beginTest ("adding ranges");
|
||||
{
|
||||
SparseSet<int> set;
|
||||
|
||||
// Adding same range twice should yield just a single range
|
||||
set.addRange ({0, 10});
|
||||
set.addRange ({0, 10});
|
||||
expectEquals (set.getNumRanges(), 1);
|
||||
expect (set.getRange (0) == Range<int> (0, 10));
|
||||
|
||||
// Adding already included range does not increase num ranges
|
||||
set.addRange ({0, 2});
|
||||
expectEquals (set.getNumRanges(), 1);
|
||||
set.addRange ({8, 10});
|
||||
expectEquals (set.getNumRanges(), 1);
|
||||
set.addRange ({2, 5});
|
||||
expectEquals (set.getNumRanges(), 1);
|
||||
|
||||
// Adding non adjacent range includes total number of ranges
|
||||
set.addRange ({-10, -5});
|
||||
expectEquals (set.getNumRanges(), 2);
|
||||
expect (set.getRange (0) == Range<int> (-10, -5));
|
||||
expect (set.getRange (1) == Range<int> (0, 10));
|
||||
expect (set.getTotalRange() == Range<int> (-10, 10));
|
||||
|
||||
set.addRange ({15, 20});
|
||||
expectEquals (set.getNumRanges(), 3);
|
||||
expect (set.getRange (0) == Range<int> (-10, -5));
|
||||
expect (set.getRange (1) == Range<int> (0, 10));
|
||||
expect (set.getRange (2) == Range<int> (15, 20));
|
||||
expect (set.getTotalRange() == Range<int> (-10, 20));
|
||||
|
||||
// Adding adjacent ranges merges them.
|
||||
set.addRange ({-5, -3});
|
||||
expectEquals (set.getNumRanges(), 3);
|
||||
expect (set.getRange (0) == Range<int> (-10, -3));
|
||||
expect (set.getRange (1) == Range<int> (0, 10));
|
||||
expect (set.getRange (2) == Range<int> (15, 20));
|
||||
expect (set.getTotalRange() == Range<int> (-10, 20));
|
||||
|
||||
set.addRange ({20, 25});
|
||||
expectEquals (set.getNumRanges(), 3);
|
||||
expect (set.getRange (0) == Range<int> (-10, -3));
|
||||
expect (set.getRange (1) == Range<int> (0, 10));
|
||||
expect (set.getRange (2) == Range<int> (15, 25));
|
||||
expect (set.getTotalRange() == Range<int> (-10, 25));
|
||||
|
||||
// Adding range containing other ranges merges them
|
||||
set.addRange ({-50, 50});
|
||||
expectEquals (set.getNumRanges(), 1);
|
||||
expect (set.getRange (0) == Range<int> (-50, 50));
|
||||
expect (set.getTotalRange() == Range<int> (-50, 50));
|
||||
}
|
||||
|
||||
beginTest ("removing ranges");
|
||||
{
|
||||
SparseSet<int> set;
|
||||
|
||||
set.addRange ({-20, -10});
|
||||
set.addRange ({0, 10});
|
||||
set.addRange ({20, 30});
|
||||
expectEquals (set.getNumRanges(), 3);
|
||||
|
||||
// Removing ranges not included in the set has no effect
|
||||
set.removeRange ({-5, 5});
|
||||
expectEquals (set.getNumRanges(), 3);
|
||||
|
||||
// Removing partially overlapping range
|
||||
set.removeRange ({-15, 5});
|
||||
expectEquals (set.getNumRanges(), 3);
|
||||
expect (set.getRange (0) == Range<int> (-20, -15));
|
||||
expect (set.getRange (1) == Range<int> (5, 10));
|
||||
expect (set.getRange (2) == Range<int> (20, 30));
|
||||
|
||||
// Removing subrange of existing range
|
||||
set.removeRange ({20, 22});
|
||||
expectEquals (set.getNumRanges(), 3);
|
||||
expect (set.getRange (2) == Range<int> (22, 30));
|
||||
|
||||
set.removeRange ({28, 30});
|
||||
expectEquals (set.getNumRanges(), 3);
|
||||
expect (set.getRange (2) == Range<int> (22, 28));
|
||||
|
||||
set.removeRange ({24, 26});
|
||||
expectEquals (set.getNumRanges(), 4);
|
||||
expect (set.getRange (0) == Range<int> (-20, -15));
|
||||
expect (set.getRange (1) == Range<int> (5, 10));
|
||||
expect (set.getRange (2) == Range<int> (22, 24));
|
||||
expect (set.getRange (3) == Range<int> (26, 28));
|
||||
}
|
||||
|
||||
beginTest ("XORing ranges");
|
||||
{
|
||||
SparseSet<int> set;
|
||||
set.addRange ({0, 10});
|
||||
|
||||
set.invertRange ({0, 10});
|
||||
expectEquals (set.getNumRanges(), 0);
|
||||
set.invertRange ({0, 10});
|
||||
expectEquals (set.getNumRanges(), 1);
|
||||
|
||||
set.invertRange ({4, 6});
|
||||
expectEquals (set.getNumRanges(), 2);
|
||||
expect (set.getRange (0) == Range<int> (0, 4));
|
||||
expect (set.getRange (1) == Range<int> (6, 10));
|
||||
|
||||
set.invertRange ({-2, 2});
|
||||
expectEquals (set.getNumRanges(), 3);
|
||||
expect (set.getRange (0) == Range<int> (-2, 0));
|
||||
expect (set.getRange (1) == Range<int> (2, 4));
|
||||
expect (set.getRange (2) == Range<int> (6, 10));
|
||||
}
|
||||
|
||||
beginTest ("range contains & overlaps checks");
|
||||
{
|
||||
SparseSet<int> set;
|
||||
set.addRange ({0, 10});
|
||||
|
||||
expect (set.containsRange (Range<int> (0, 2)));
|
||||
expect (set.containsRange (Range<int> (8, 10)));
|
||||
expect (set.containsRange (Range<int> (0, 10)));
|
||||
|
||||
expect (! set.containsRange (Range<int> (-2, 0)));
|
||||
expect (! set.containsRange (Range<int> (-2, 10)));
|
||||
expect (! set.containsRange (Range<int> (10, 12)));
|
||||
expect (! set.containsRange (Range<int> (0, 12)));
|
||||
|
||||
expect (set.overlapsRange (Range<int> (0, 2)));
|
||||
expect (set.overlapsRange (Range<int> (8, 10)));
|
||||
expect (set.overlapsRange (Range<int> (0, 10)));
|
||||
|
||||
expect (! set.overlapsRange (Range<int> (-2, 0)));
|
||||
expect ( set.overlapsRange (Range<int> (-2, 10)));
|
||||
expect (! set.overlapsRange (Range<int> (10, 12)));
|
||||
expect ( set.overlapsRange (Range<int> (0, 12)));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static SparseSetTests sparseSetTests;
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace juce
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
#if JUCE_UNIT_TESTS
|
||||
|
||||
class SparseSetTests : public UnitTest
|
||||
{
|
||||
public:
|
||||
SparseSetTests()
|
||||
: UnitTest ("SparseSet class", UnitTestCategories::containers)
|
||||
{}
|
||||
|
||||
void runTest() override
|
||||
{
|
||||
beginTest ("basic operations");
|
||||
{
|
||||
SparseSet<int> set;
|
||||
|
||||
expect (set.isEmpty());
|
||||
expectEquals (set.size(), 0);
|
||||
expectEquals (set.getNumRanges(), 0);
|
||||
expect (set.getTotalRange().isEmpty());
|
||||
|
||||
set.addRange ({0, 10});
|
||||
expect (! set.isEmpty());
|
||||
expectEquals (set.size(), 10);
|
||||
expectEquals (set.getNumRanges(), 1);
|
||||
expect (! set.getTotalRange().isEmpty());
|
||||
expect (set.getRange (0) == Range<int> (0, 10));
|
||||
|
||||
expectEquals (set[0], 0);
|
||||
expectEquals (set[5], 5);
|
||||
expectEquals (set[9], 9);
|
||||
// Index out of range yields a default value for a type
|
||||
expectEquals (set[10], 0);
|
||||
expect (set.contains (0));
|
||||
expect (set.contains (9));
|
||||
expect (! set.contains (10));
|
||||
}
|
||||
|
||||
beginTest ("adding ranges");
|
||||
{
|
||||
SparseSet<int> set;
|
||||
|
||||
// Adding same range twice should yield just a single range
|
||||
set.addRange ({0, 10});
|
||||
set.addRange ({0, 10});
|
||||
expectEquals (set.getNumRanges(), 1);
|
||||
expect (set.getRange (0) == Range<int> (0, 10));
|
||||
|
||||
// Adding already included range does not increase num ranges
|
||||
set.addRange ({0, 2});
|
||||
expectEquals (set.getNumRanges(), 1);
|
||||
set.addRange ({8, 10});
|
||||
expectEquals (set.getNumRanges(), 1);
|
||||
set.addRange ({2, 5});
|
||||
expectEquals (set.getNumRanges(), 1);
|
||||
|
||||
// Adding non adjacent range includes total number of ranges
|
||||
set.addRange ({-10, -5});
|
||||
expectEquals (set.getNumRanges(), 2);
|
||||
expect (set.getRange (0) == Range<int> (-10, -5));
|
||||
expect (set.getRange (1) == Range<int> (0, 10));
|
||||
expect (set.getTotalRange() == Range<int> (-10, 10));
|
||||
|
||||
set.addRange ({15, 20});
|
||||
expectEquals (set.getNumRanges(), 3);
|
||||
expect (set.getRange (0) == Range<int> (-10, -5));
|
||||
expect (set.getRange (1) == Range<int> (0, 10));
|
||||
expect (set.getRange (2) == Range<int> (15, 20));
|
||||
expect (set.getTotalRange() == Range<int> (-10, 20));
|
||||
|
||||
// Adding adjacent ranges merges them.
|
||||
set.addRange ({-5, -3});
|
||||
expectEquals (set.getNumRanges(), 3);
|
||||
expect (set.getRange (0) == Range<int> (-10, -3));
|
||||
expect (set.getRange (1) == Range<int> (0, 10));
|
||||
expect (set.getRange (2) == Range<int> (15, 20));
|
||||
expect (set.getTotalRange() == Range<int> (-10, 20));
|
||||
|
||||
set.addRange ({20, 25});
|
||||
expectEquals (set.getNumRanges(), 3);
|
||||
expect (set.getRange (0) == Range<int> (-10, -3));
|
||||
expect (set.getRange (1) == Range<int> (0, 10));
|
||||
expect (set.getRange (2) == Range<int> (15, 25));
|
||||
expect (set.getTotalRange() == Range<int> (-10, 25));
|
||||
|
||||
// Adding range containing other ranges merges them
|
||||
set.addRange ({-50, 50});
|
||||
expectEquals (set.getNumRanges(), 1);
|
||||
expect (set.getRange (0) == Range<int> (-50, 50));
|
||||
expect (set.getTotalRange() == Range<int> (-50, 50));
|
||||
}
|
||||
|
||||
beginTest ("removing ranges");
|
||||
{
|
||||
SparseSet<int> set;
|
||||
|
||||
set.addRange ({-20, -10});
|
||||
set.addRange ({0, 10});
|
||||
set.addRange ({20, 30});
|
||||
expectEquals (set.getNumRanges(), 3);
|
||||
|
||||
// Removing ranges not included in the set has no effect
|
||||
set.removeRange ({-5, 5});
|
||||
expectEquals (set.getNumRanges(), 3);
|
||||
|
||||
// Removing partially overlapping range
|
||||
set.removeRange ({-15, 5});
|
||||
expectEquals (set.getNumRanges(), 3);
|
||||
expect (set.getRange (0) == Range<int> (-20, -15));
|
||||
expect (set.getRange (1) == Range<int> (5, 10));
|
||||
expect (set.getRange (2) == Range<int> (20, 30));
|
||||
|
||||
// Removing subrange of existing range
|
||||
set.removeRange ({20, 22});
|
||||
expectEquals (set.getNumRanges(), 3);
|
||||
expect (set.getRange (2) == Range<int> (22, 30));
|
||||
|
||||
set.removeRange ({28, 30});
|
||||
expectEquals (set.getNumRanges(), 3);
|
||||
expect (set.getRange (2) == Range<int> (22, 28));
|
||||
|
||||
set.removeRange ({24, 26});
|
||||
expectEquals (set.getNumRanges(), 4);
|
||||
expect (set.getRange (0) == Range<int> (-20, -15));
|
||||
expect (set.getRange (1) == Range<int> (5, 10));
|
||||
expect (set.getRange (2) == Range<int> (22, 24));
|
||||
expect (set.getRange (3) == Range<int> (26, 28));
|
||||
}
|
||||
|
||||
beginTest ("XORing ranges");
|
||||
{
|
||||
SparseSet<int> set;
|
||||
set.addRange ({0, 10});
|
||||
|
||||
set.invertRange ({0, 10});
|
||||
expectEquals (set.getNumRanges(), 0);
|
||||
set.invertRange ({0, 10});
|
||||
expectEquals (set.getNumRanges(), 1);
|
||||
|
||||
set.invertRange ({4, 6});
|
||||
expectEquals (set.getNumRanges(), 2);
|
||||
expect (set.getRange (0) == Range<int> (0, 4));
|
||||
expect (set.getRange (1) == Range<int> (6, 10));
|
||||
|
||||
set.invertRange ({-2, 2});
|
||||
expectEquals (set.getNumRanges(), 3);
|
||||
expect (set.getRange (0) == Range<int> (-2, 0));
|
||||
expect (set.getRange (1) == Range<int> (2, 4));
|
||||
expect (set.getRange (2) == Range<int> (6, 10));
|
||||
}
|
||||
|
||||
beginTest ("range contains & overlaps checks");
|
||||
{
|
||||
SparseSet<int> set;
|
||||
set.addRange ({0, 10});
|
||||
|
||||
expect (set.containsRange (Range<int> (0, 2)));
|
||||
expect (set.containsRange (Range<int> (8, 10)));
|
||||
expect (set.containsRange (Range<int> (0, 10)));
|
||||
|
||||
expect (! set.containsRange (Range<int> (-2, 0)));
|
||||
expect (! set.containsRange (Range<int> (-2, 10)));
|
||||
expect (! set.containsRange (Range<int> (10, 12)));
|
||||
expect (! set.containsRange (Range<int> (0, 12)));
|
||||
|
||||
expect (set.overlapsRange (Range<int> (0, 2)));
|
||||
expect (set.overlapsRange (Range<int> (8, 10)));
|
||||
expect (set.overlapsRange (Range<int> (0, 10)));
|
||||
|
||||
expect (! set.overlapsRange (Range<int> (-2, 0)));
|
||||
expect ( set.overlapsRange (Range<int> (-2, 10)));
|
||||
expect (! set.overlapsRange (Range<int> (10, 12)));
|
||||
expect ( set.overlapsRange (Range<int> (0, 12)));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static SparseSetTests sparseSetTests;
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace juce
|
||||
|
@ -1,268 +1,268 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Holds a set of primitive values, storing them as a set of ranges.
|
||||
|
||||
This container acts like an array, but can efficiently hold large contiguous
|
||||
ranges of values. It's quite a specialised class, mostly useful for things
|
||||
like keeping the set of selected rows in a listbox.
|
||||
|
||||
The type used as a template parameter must be an integer type, such as int, short,
|
||||
int64, etc.
|
||||
|
||||
@tags{Core}
|
||||
*/
|
||||
template <class Type>
|
||||
class SparseSet
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
SparseSet() = default;
|
||||
|
||||
SparseSet (const SparseSet&) = default;
|
||||
SparseSet& operator= (const SparseSet&) = default;
|
||||
|
||||
SparseSet (SparseSet&& other) noexcept : ranges (std::move (other.ranges)) {}
|
||||
SparseSet& operator= (SparseSet&& other) noexcept { ranges = std::move (other.ranges); return *this; }
|
||||
|
||||
//==============================================================================
|
||||
/** Clears the set. */
|
||||
void clear() { ranges.clear(); }
|
||||
|
||||
/** Checks whether the set is empty.
|
||||
This is much quicker than using (size() == 0).
|
||||
*/
|
||||
bool isEmpty() const noexcept { return ranges.isEmpty(); }
|
||||
|
||||
/** Returns the number of values in the set.
|
||||
|
||||
Because of the way the data is stored, this method can take longer if there
|
||||
are a lot of items in the set. Use isEmpty() for a quick test of whether there
|
||||
are any items.
|
||||
*/
|
||||
Type size() const noexcept
|
||||
{
|
||||
Type total = {};
|
||||
|
||||
for (auto& r : ranges)
|
||||
total += r.getLength();
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
/** Returns one of the values in the set.
|
||||
|
||||
@param index the index of the value to retrieve, in the range 0 to (size() - 1).
|
||||
@returns the value at this index, or 0 if it's out-of-range
|
||||
*/
|
||||
Type operator[] (Type index) const noexcept
|
||||
{
|
||||
Type total = {};
|
||||
|
||||
for (auto& r : ranges)
|
||||
{
|
||||
auto end = total + r.getLength();
|
||||
|
||||
if (index < end)
|
||||
return r.getStart() + (index - total);
|
||||
|
||||
total = end;
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
/** Checks whether a particular value is in the set. */
|
||||
bool contains (Type valueToLookFor) const noexcept
|
||||
{
|
||||
for (auto& r : ranges)
|
||||
{
|
||||
if (r.getStart() > valueToLookFor)
|
||||
break;
|
||||
|
||||
if (r.getEnd() > valueToLookFor)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the number of contiguous blocks of values.
|
||||
@see getRange
|
||||
*/
|
||||
int getNumRanges() const noexcept { return ranges.size(); }
|
||||
|
||||
/** Returns one of the contiguous ranges of values stored.
|
||||
@param rangeIndex the index of the range to look up, between 0
|
||||
and (getNumRanges() - 1)
|
||||
@see getTotalRange
|
||||
*/
|
||||
Range<Type> getRange (int rangeIndex) const noexcept { return ranges[rangeIndex]; }
|
||||
|
||||
/** Returns the range between the lowest and highest values in the set.
|
||||
@see getRange
|
||||
*/
|
||||
Range<Type> getTotalRange() const noexcept
|
||||
{
|
||||
if (ranges.isEmpty())
|
||||
return {};
|
||||
|
||||
return { ranges.getFirst().getStart(),
|
||||
ranges.getLast().getEnd() };
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Adds a range of contiguous values to the set.
|
||||
e.g. addRange (Range \<int\> (10, 14)) will add (10, 11, 12, 13) to the set.
|
||||
*/
|
||||
void addRange (Range<Type> range)
|
||||
{
|
||||
if (! range.isEmpty())
|
||||
{
|
||||
removeRange (range);
|
||||
ranges.add (range);
|
||||
std::sort (ranges.begin(), ranges.end(),
|
||||
[] (Range<Type> a, Range<Type> b) { return a.getStart() < b.getStart(); });
|
||||
simplify();
|
||||
}
|
||||
}
|
||||
|
||||
/** Removes a range of values from the set.
|
||||
e.g. removeRange (Range\<int\> (10, 14)) will remove (10, 11, 12, 13) from the set.
|
||||
*/
|
||||
void removeRange (Range<Type> rangeToRemove)
|
||||
{
|
||||
if (getTotalRange().intersects (rangeToRemove) && ! rangeToRemove.isEmpty())
|
||||
{
|
||||
for (int i = ranges.size(); --i >= 0;)
|
||||
{
|
||||
auto& r = ranges.getReference(i);
|
||||
|
||||
if (r.getEnd() <= rangeToRemove.getStart())
|
||||
break;
|
||||
|
||||
if (r.getStart() >= rangeToRemove.getEnd())
|
||||
continue;
|
||||
|
||||
if (rangeToRemove.contains (r))
|
||||
{
|
||||
ranges.remove (i);
|
||||
}
|
||||
else if (r.contains (rangeToRemove))
|
||||
{
|
||||
auto r1 = r.withEnd (rangeToRemove.getStart());
|
||||
auto r2 = r.withStart (rangeToRemove.getEnd());
|
||||
|
||||
// this should be covered in if (rangeToRemove.contains (r))
|
||||
jassert (! r1.isEmpty() || ! r2.isEmpty());
|
||||
|
||||
r = r1;
|
||||
|
||||
if (r.isEmpty())
|
||||
r = r2;
|
||||
|
||||
if (! r1.isEmpty() && ! r2.isEmpty())
|
||||
ranges.insert (i + 1, r2);
|
||||
}
|
||||
else if (rangeToRemove.getEnd() > r.getEnd())
|
||||
{
|
||||
r.setEnd (rangeToRemove.getStart());
|
||||
}
|
||||
else
|
||||
{
|
||||
r.setStart (rangeToRemove.getEnd());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Does an XOR of the values in a given range. */
|
||||
void invertRange (Range<Type> range)
|
||||
{
|
||||
SparseSet newItems;
|
||||
newItems.addRange (range);
|
||||
|
||||
for (auto& r : ranges)
|
||||
newItems.removeRange (r);
|
||||
|
||||
removeRange (range);
|
||||
|
||||
for (auto& r : newItems.ranges)
|
||||
addRange (r);
|
||||
}
|
||||
|
||||
/** Checks whether any part of a given range overlaps any part of this set. */
|
||||
bool overlapsRange (Range<Type> range) const noexcept
|
||||
{
|
||||
if (! range.isEmpty())
|
||||
for (auto& r : ranges)
|
||||
if (r.intersects (range))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Checks whether the whole of a given range is contained within this one. */
|
||||
bool containsRange (Range<Type> range) const noexcept
|
||||
{
|
||||
if (! range.isEmpty())
|
||||
for (auto& r : ranges)
|
||||
if (r.contains (range))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Returns the set as a list of ranges, which you may want to iterate over. */
|
||||
const Array<Range<Type>>& getRanges() const noexcept { return ranges; }
|
||||
|
||||
//==============================================================================
|
||||
bool operator== (const SparseSet& other) const noexcept { return ranges == other.ranges; }
|
||||
bool operator!= (const SparseSet& other) const noexcept { return ranges != other.ranges; }
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
Array<Range<Type>> ranges;
|
||||
|
||||
void simplify()
|
||||
{
|
||||
for (int i = ranges.size(); --i > 0;)
|
||||
{
|
||||
auto& r1 = ranges.getReference (i - 1);
|
||||
auto& r2 = ranges.getReference (i);
|
||||
|
||||
if (r1.getEnd() == r2.getStart())
|
||||
{
|
||||
r1.setEnd (r2.getEnd());
|
||||
ranges.remove (i);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace juce
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Holds a set of primitive values, storing them as a set of ranges.
|
||||
|
||||
This container acts like an array, but can efficiently hold large contiguous
|
||||
ranges of values. It's quite a specialised class, mostly useful for things
|
||||
like keeping the set of selected rows in a listbox.
|
||||
|
||||
The type used as a template parameter must be an integer type, such as int, short,
|
||||
int64, etc.
|
||||
|
||||
@tags{Core}
|
||||
*/
|
||||
template <class Type>
|
||||
class SparseSet
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
SparseSet() = default;
|
||||
|
||||
SparseSet (const SparseSet&) = default;
|
||||
SparseSet& operator= (const SparseSet&) = default;
|
||||
|
||||
SparseSet (SparseSet&& other) noexcept : ranges (std::move (other.ranges)) {}
|
||||
SparseSet& operator= (SparseSet&& other) noexcept { ranges = std::move (other.ranges); return *this; }
|
||||
|
||||
//==============================================================================
|
||||
/** Clears the set. */
|
||||
void clear() { ranges.clear(); }
|
||||
|
||||
/** Checks whether the set is empty.
|
||||
This is much quicker than using (size() == 0).
|
||||
*/
|
||||
bool isEmpty() const noexcept { return ranges.isEmpty(); }
|
||||
|
||||
/** Returns the number of values in the set.
|
||||
|
||||
Because of the way the data is stored, this method can take longer if there
|
||||
are a lot of items in the set. Use isEmpty() for a quick test of whether there
|
||||
are any items.
|
||||
*/
|
||||
Type size() const noexcept
|
||||
{
|
||||
Type total = {};
|
||||
|
||||
for (auto& r : ranges)
|
||||
total += r.getLength();
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
/** Returns one of the values in the set.
|
||||
|
||||
@param index the index of the value to retrieve, in the range 0 to (size() - 1).
|
||||
@returns the value at this index, or 0 if it's out-of-range
|
||||
*/
|
||||
Type operator[] (Type index) const noexcept
|
||||
{
|
||||
Type total = {};
|
||||
|
||||
for (auto& r : ranges)
|
||||
{
|
||||
auto end = total + r.getLength();
|
||||
|
||||
if (index < end)
|
||||
return r.getStart() + (index - total);
|
||||
|
||||
total = end;
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
/** Checks whether a particular value is in the set. */
|
||||
bool contains (Type valueToLookFor) const noexcept
|
||||
{
|
||||
for (auto& r : ranges)
|
||||
{
|
||||
if (r.getStart() > valueToLookFor)
|
||||
break;
|
||||
|
||||
if (r.getEnd() > valueToLookFor)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the number of contiguous blocks of values.
|
||||
@see getRange
|
||||
*/
|
||||
int getNumRanges() const noexcept { return ranges.size(); }
|
||||
|
||||
/** Returns one of the contiguous ranges of values stored.
|
||||
@param rangeIndex the index of the range to look up, between 0
|
||||
and (getNumRanges() - 1)
|
||||
@see getTotalRange
|
||||
*/
|
||||
Range<Type> getRange (int rangeIndex) const noexcept { return ranges[rangeIndex]; }
|
||||
|
||||
/** Returns the range between the lowest and highest values in the set.
|
||||
@see getRange
|
||||
*/
|
||||
Range<Type> getTotalRange() const noexcept
|
||||
{
|
||||
if (ranges.isEmpty())
|
||||
return {};
|
||||
|
||||
return { ranges.getFirst().getStart(),
|
||||
ranges.getLast().getEnd() };
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Adds a range of contiguous values to the set.
|
||||
e.g. addRange (Range \<int\> (10, 14)) will add (10, 11, 12, 13) to the set.
|
||||
*/
|
||||
void addRange (Range<Type> range)
|
||||
{
|
||||
if (! range.isEmpty())
|
||||
{
|
||||
removeRange (range);
|
||||
ranges.add (range);
|
||||
std::sort (ranges.begin(), ranges.end(),
|
||||
[] (Range<Type> a, Range<Type> b) { return a.getStart() < b.getStart(); });
|
||||
simplify();
|
||||
}
|
||||
}
|
||||
|
||||
/** Removes a range of values from the set.
|
||||
e.g. removeRange (Range\<int\> (10, 14)) will remove (10, 11, 12, 13) from the set.
|
||||
*/
|
||||
void removeRange (Range<Type> rangeToRemove)
|
||||
{
|
||||
if (getTotalRange().intersects (rangeToRemove) && ! rangeToRemove.isEmpty())
|
||||
{
|
||||
for (int i = ranges.size(); --i >= 0;)
|
||||
{
|
||||
auto& r = ranges.getReference(i);
|
||||
|
||||
if (r.getEnd() <= rangeToRemove.getStart())
|
||||
break;
|
||||
|
||||
if (r.getStart() >= rangeToRemove.getEnd())
|
||||
continue;
|
||||
|
||||
if (rangeToRemove.contains (r))
|
||||
{
|
||||
ranges.remove (i);
|
||||
}
|
||||
else if (r.contains (rangeToRemove))
|
||||
{
|
||||
auto r1 = r.withEnd (rangeToRemove.getStart());
|
||||
auto r2 = r.withStart (rangeToRemove.getEnd());
|
||||
|
||||
// this should be covered in if (rangeToRemove.contains (r))
|
||||
jassert (! r1.isEmpty() || ! r2.isEmpty());
|
||||
|
||||
r = r1;
|
||||
|
||||
if (r.isEmpty())
|
||||
r = r2;
|
||||
|
||||
if (! r1.isEmpty() && ! r2.isEmpty())
|
||||
ranges.insert (i + 1, r2);
|
||||
}
|
||||
else if (rangeToRemove.getEnd() > r.getEnd())
|
||||
{
|
||||
r.setEnd (rangeToRemove.getStart());
|
||||
}
|
||||
else
|
||||
{
|
||||
r.setStart (rangeToRemove.getEnd());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Does an XOR of the values in a given range. */
|
||||
void invertRange (Range<Type> range)
|
||||
{
|
||||
SparseSet newItems;
|
||||
newItems.addRange (range);
|
||||
|
||||
for (auto& r : ranges)
|
||||
newItems.removeRange (r);
|
||||
|
||||
removeRange (range);
|
||||
|
||||
for (auto& r : newItems.ranges)
|
||||
addRange (r);
|
||||
}
|
||||
|
||||
/** Checks whether any part of a given range overlaps any part of this set. */
|
||||
bool overlapsRange (Range<Type> range) const noexcept
|
||||
{
|
||||
if (! range.isEmpty())
|
||||
for (auto& r : ranges)
|
||||
if (r.intersects (range))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Checks whether the whole of a given range is contained within this one. */
|
||||
bool containsRange (Range<Type> range) const noexcept
|
||||
{
|
||||
if (! range.isEmpty())
|
||||
for (auto& r : ranges)
|
||||
if (r.contains (range))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Returns the set as a list of ranges, which you may want to iterate over. */
|
||||
const Array<Range<Type>>& getRanges() const noexcept { return ranges; }
|
||||
|
||||
//==============================================================================
|
||||
bool operator== (const SparseSet& other) const noexcept { return ranges == other.ranges; }
|
||||
bool operator!= (const SparseSet& other) const noexcept { return ranges != other.ranges; }
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
Array<Range<Type>> ranges;
|
||||
|
||||
void simplify()
|
||||
{
|
||||
for (int i = ranges.size(); --i > 0;)
|
||||
{
|
||||
auto& r1 = ranges.getReference (i - 1);
|
||||
auto& r2 = ranges.getReference (i);
|
||||
|
||||
if (r1.getEnd() == r2.getStart())
|
||||
{
|
||||
r1.setEnd (r2.getEnd());
|
||||
ranges.remove (i);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace juce
|
||||
|
1818
deps/juce/modules/juce_core/containers/juce_Variant.cpp
vendored
1818
deps/juce/modules/juce_core/containers/juce_Variant.cpp
vendored
File diff suppressed because it is too large
Load Diff
@ -1,354 +1,365 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A variant class, that can be used to hold a range of primitive values.
|
||||
|
||||
A var object can hold a range of simple primitive values, strings, or
|
||||
any kind of ReferenceCountedObject. The var class is intended to act like
|
||||
the kind of values used in dynamic scripting languages.
|
||||
|
||||
You can save/load var objects either in a small, proprietary binary format
|
||||
using writeToStream()/readFromStream(), or as JSON by using the JSON class.
|
||||
|
||||
@see JSON, DynamicObject
|
||||
|
||||
@tags{Core}
|
||||
*/
|
||||
class JUCE_API var
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** This structure is passed to a NativeFunction callback, and contains invocation
|
||||
details about the function's arguments and context.
|
||||
*/
|
||||
struct JUCE_API NativeFunctionArgs
|
||||
{
|
||||
NativeFunctionArgs (const var& thisObject, const var* args, int numArgs) noexcept;
|
||||
|
||||
const var& thisObject;
|
||||
const var* arguments;
|
||||
int numArguments;
|
||||
};
|
||||
|
||||
using NativeFunction = std::function<var (const NativeFunctionArgs&)>;
|
||||
|
||||
//==============================================================================
|
||||
/** Creates a void variant. */
|
||||
var() noexcept;
|
||||
|
||||
/** Destructor. */
|
||||
~var() noexcept;
|
||||
|
||||
var (const var& valueToCopy);
|
||||
var (int value) noexcept;
|
||||
var (int64 value) noexcept;
|
||||
var (bool value) noexcept;
|
||||
var (double value) noexcept;
|
||||
var (const char* value);
|
||||
var (const wchar_t* value);
|
||||
var (const String& value);
|
||||
var (const Array<var>& value);
|
||||
var (const StringArray& value);
|
||||
var (ReferenceCountedObject* object);
|
||||
var (NativeFunction method) noexcept;
|
||||
var (const void* binaryData, size_t dataSize);
|
||||
var (const MemoryBlock& binaryData);
|
||||
|
||||
var& operator= (const var& valueToCopy);
|
||||
var& operator= (int value);
|
||||
var& operator= (int64 value);
|
||||
var& operator= (bool value);
|
||||
var& operator= (double value);
|
||||
var& operator= (const char* value);
|
||||
var& operator= (const wchar_t* value);
|
||||
var& operator= (const String& value);
|
||||
var& operator= (const MemoryBlock& value);
|
||||
var& operator= (const Array<var>& value);
|
||||
var& operator= (ReferenceCountedObject* object);
|
||||
var& operator= (NativeFunction method);
|
||||
|
||||
var (var&&) noexcept;
|
||||
var (String&&);
|
||||
var (MemoryBlock&&);
|
||||
var (Array<var>&&);
|
||||
var& operator= (var&&) noexcept;
|
||||
var& operator= (String&&);
|
||||
|
||||
void swapWith (var& other) noexcept;
|
||||
|
||||
/** Returns a var object that can be used where you need the javascript "undefined" value. */
|
||||
static var undefined() noexcept;
|
||||
|
||||
//==============================================================================
|
||||
operator int() const noexcept;
|
||||
operator int64() const noexcept;
|
||||
operator bool() const noexcept;
|
||||
operator float() const noexcept;
|
||||
operator double() const noexcept;
|
||||
operator String() const;
|
||||
String toString() const;
|
||||
|
||||
/** If this variant holds an array, this provides access to it.
|
||||
NOTE: Beware when you use this - the array pointer is only valid for the lifetime
|
||||
of the variant that returned it, so be very careful not to call this method on temporary
|
||||
var objects that are the return-value of a function, and which may go out of scope before
|
||||
you use the array!
|
||||
*/
|
||||
Array<var>* getArray() const noexcept;
|
||||
|
||||
/** If this variant holds a memory block, this provides access to it.
|
||||
NOTE: Beware when you use this - the MemoryBlock pointer is only valid for the lifetime
|
||||
of the variant that returned it, so be very careful not to call this method on temporary
|
||||
var objects that are the return-value of a function, and which may go out of scope before
|
||||
you use the MemoryBlock!
|
||||
*/
|
||||
MemoryBlock* getBinaryData() const noexcept;
|
||||
|
||||
ReferenceCountedObject* getObject() const noexcept;
|
||||
DynamicObject* getDynamicObject() const noexcept;
|
||||
|
||||
//==============================================================================
|
||||
bool isVoid() const noexcept;
|
||||
bool isUndefined() const noexcept;
|
||||
bool isInt() const noexcept;
|
||||
bool isInt64() const noexcept;
|
||||
bool isBool() const noexcept;
|
||||
bool isDouble() const noexcept;
|
||||
bool isString() const noexcept;
|
||||
bool isObject() const noexcept;
|
||||
bool isArray() const noexcept;
|
||||
bool isBinaryData() const noexcept;
|
||||
bool isMethod() const noexcept;
|
||||
|
||||
/** Returns true if this var has the same value as the one supplied.
|
||||
Note that this ignores the type, so a string var "123" and an integer var with the
|
||||
value 123 are considered to be equal.
|
||||
@see equalsWithSameType
|
||||
*/
|
||||
bool equals (const var& other) const noexcept;
|
||||
|
||||
/** Returns true if this var has the same value and type as the one supplied.
|
||||
This differs from equals() because e.g. "123" and 123 will be considered different.
|
||||
@see equals
|
||||
*/
|
||||
bool equalsWithSameType (const var& other) const noexcept;
|
||||
|
||||
/** Returns true if this var has the same type as the one supplied. */
|
||||
bool hasSameTypeAs (const var& other) const noexcept;
|
||||
|
||||
/** Returns a deep copy of this object.
|
||||
For simple types this just returns a copy, but if the object contains any arrays
|
||||
or DynamicObjects, they will be cloned (recursively).
|
||||
*/
|
||||
var clone() const noexcept;
|
||||
|
||||
//==============================================================================
|
||||
/** If the var is an array, this returns the number of elements.
|
||||
If the var isn't actually an array, this will return 0.
|
||||
*/
|
||||
int size() const;
|
||||
|
||||
/** If the var is an array, this can be used to return one of its elements.
|
||||
To call this method, you must make sure that the var is actually an array, and
|
||||
that the index is a valid number. If these conditions aren't met, behaviour is
|
||||
undefined.
|
||||
For more control over the array's contents, you can call getArray() and manipulate
|
||||
it directly as an Array\<var\>.
|
||||
*/
|
||||
const var& operator[] (int arrayIndex) const;
|
||||
|
||||
/** If the var is an array, this can be used to return one of its elements.
|
||||
To call this method, you must make sure that the var is actually an array, and
|
||||
that the index is a valid number. If these conditions aren't met, behaviour is
|
||||
undefined.
|
||||
For more control over the array's contents, you can call getArray() and manipulate
|
||||
it directly as an Array\<var\>.
|
||||
*/
|
||||
var& operator[] (int arrayIndex);
|
||||
|
||||
/** Appends an element to the var, converting it to an array if it isn't already one.
|
||||
If the var isn't an array, it will be converted to one, and if its value was non-void,
|
||||
this value will be kept as the first element of the new array. The parameter value
|
||||
will then be appended to it.
|
||||
For more control over the array's contents, you can call getArray() and manipulate
|
||||
it directly as an Array\<var\>.
|
||||
*/
|
||||
void append (const var& valueToAppend);
|
||||
|
||||
/** Inserts an element to the var, converting it to an array if it isn't already one.
|
||||
If the var isn't an array, it will be converted to one, and if its value was non-void,
|
||||
this value will be kept as the first element of the new array. The parameter value
|
||||
will then be inserted into it.
|
||||
For more control over the array's contents, you can call getArray() and manipulate
|
||||
it directly as an Array\<var\>.
|
||||
*/
|
||||
void insert (int index, const var& value);
|
||||
|
||||
/** If the var is an array, this removes one of its elements.
|
||||
If the index is out-of-range or the var isn't an array, nothing will be done.
|
||||
For more control over the array's contents, you can call getArray() and manipulate
|
||||
it directly as an Array\<var\>.
|
||||
*/
|
||||
void remove (int index);
|
||||
|
||||
/** Treating the var as an array, this resizes it to contain the specified number of elements.
|
||||
If the var isn't an array, it will be converted to one, and if its value was non-void,
|
||||
this value will be kept as the first element of the new array before resizing.
|
||||
For more control over the array's contents, you can call getArray() and manipulate
|
||||
it directly as an Array\<var\>.
|
||||
*/
|
||||
void resize (int numArrayElementsWanted);
|
||||
|
||||
/** If the var is an array, this searches it for the first occurrence of the specified value,
|
||||
and returns its index.
|
||||
If the var isn't an array, or if the value isn't found, this returns -1.
|
||||
*/
|
||||
int indexOf (const var& value) const;
|
||||
|
||||
//==============================================================================
|
||||
/** If this variant is an object, this returns one of its properties. */
|
||||
const var& operator[] (const Identifier& propertyName) const;
|
||||
/** If this variant is an object, this returns one of its properties. */
|
||||
const var& operator[] (const char* propertyName) const;
|
||||
/** If this variant is an object, this returns one of its properties, or a default
|
||||
fallback value if the property is not set. */
|
||||
var getProperty (const Identifier& propertyName, const var& defaultReturnValue) const;
|
||||
/** Returns true if this variant is an object and if it has the given property. */
|
||||
bool hasProperty (const Identifier& propertyName) const noexcept;
|
||||
|
||||
/** Invokes a named method call with no arguments. */
|
||||
var call (const Identifier& method) const;
|
||||
/** Invokes a named method call with one argument. */
|
||||
var call (const Identifier& method, const var& arg1) const;
|
||||
/** Invokes a named method call with 2 arguments. */
|
||||
var call (const Identifier& method, const var& arg1, const var& arg2) const;
|
||||
/** Invokes a named method call with 3 arguments. */
|
||||
var call (const Identifier& method, const var& arg1, const var& arg2, const var& arg3);
|
||||
/** Invokes a named method call with 4 arguments. */
|
||||
var call (const Identifier& method, const var& arg1, const var& arg2, const var& arg3, const var& arg4) const;
|
||||
/** Invokes a named method call with 5 arguments. */
|
||||
var call (const Identifier& method, const var& arg1, const var& arg2, const var& arg3, const var& arg4, const var& arg5) const;
|
||||
/** Invokes a named method call with a list of arguments. */
|
||||
var invoke (const Identifier& method, const var* arguments, int numArguments) const;
|
||||
/** If this object is a method, this returns the function pointer. */
|
||||
NativeFunction getNativeFunction() const;
|
||||
|
||||
//==============================================================================
|
||||
/** Writes a binary representation of this value to a stream.
|
||||
The data can be read back later using readFromStream().
|
||||
@see JSON
|
||||
*/
|
||||
void writeToStream (OutputStream& output) const;
|
||||
|
||||
/** Reads back a stored binary representation of a value.
|
||||
The data in the stream must have been written using writeToStream(), or this
|
||||
will have unpredictable results.
|
||||
@see JSON
|
||||
*/
|
||||
static var readFromStream (InputStream& input);
|
||||
|
||||
//==============================================================================
|
||||
#if JUCE_ALLOW_STATIC_NULL_VARIABLES && ! defined (DOXYGEN)
|
||||
[[deprecated ("This was a static empty var object, but is now deprecated as it's too easy to accidentally "
|
||||
"use it indirectly during a static constructor leading to hard-to-find order-of-initialisation "
|
||||
"problems. Use var() or {} instead. For returning an empty var from a function by reference, "
|
||||
"use a function-local static var and return that.")]]
|
||||
static const var null;
|
||||
#endif
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
struct VariantType;
|
||||
struct Instance;
|
||||
|
||||
union ValueUnion
|
||||
{
|
||||
int intValue;
|
||||
int64 int64Value;
|
||||
bool boolValue;
|
||||
double doubleValue;
|
||||
char stringValue[sizeof (String)];
|
||||
ReferenceCountedObject* objectValue;
|
||||
MemoryBlock* binaryValue;
|
||||
NativeFunction* methodValue;
|
||||
};
|
||||
|
||||
friend bool canCompare (const var&, const var&);
|
||||
|
||||
const VariantType* type;
|
||||
ValueUnion value;
|
||||
|
||||
Array<var>* convertToArray();
|
||||
var (const VariantType&) noexcept;
|
||||
|
||||
// This is needed to prevent the wrong constructor/operator being called
|
||||
var (const ReferenceCountedObject*) = delete;
|
||||
var& operator= (const ReferenceCountedObject*) = delete;
|
||||
var (const void*) = delete;
|
||||
var& operator= (const void*) = delete;
|
||||
};
|
||||
|
||||
/** Compares the values of two var objects, using the var::equals() comparison. */
|
||||
JUCE_API bool operator== (const var&, const var&);
|
||||
/** Compares the values of two var objects, using the var::equals() comparison. */
|
||||
JUCE_API bool operator!= (const var&, const var&);
|
||||
/** Compares the values of two var objects, using the var::equals() comparison. */
|
||||
JUCE_API bool operator< (const var&, const var&);
|
||||
/** Compares the values of two var objects, using the var::equals() comparison. */
|
||||
JUCE_API bool operator<= (const var&, const var&);
|
||||
/** Compares the values of two var objects, using the var::equals() comparison. */
|
||||
JUCE_API bool operator> (const var&, const var&);
|
||||
/** Compares the values of two var objects, using the var::equals() comparison. */
|
||||
JUCE_API bool operator>= (const var&, const var&);
|
||||
|
||||
JUCE_API bool operator== (const var&, const String&);
|
||||
JUCE_API bool operator!= (const var&, const String&);
|
||||
JUCE_API bool operator== (const var&, const char*);
|
||||
JUCE_API bool operator!= (const var&, const char*);
|
||||
|
||||
//==============================================================================
|
||||
/** This template-overloaded class can be used to convert between var and custom types.
|
||||
|
||||
@tags{Core}
|
||||
*/
|
||||
template <typename Type>
|
||||
struct VariantConverter
|
||||
{
|
||||
static Type fromVar (const var& v) { return static_cast<Type> (v); }
|
||||
static var toVar (const Type& t) { return t; }
|
||||
};
|
||||
|
||||
#ifndef DOXYGEN
|
||||
template <>
|
||||
struct VariantConverter<String>
|
||||
{
|
||||
static String fromVar (const var& v) { return v.toString(); }
|
||||
static var toVar (const String& s) { return s; }
|
||||
};
|
||||
#endif
|
||||
|
||||
} // namespace juce
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A variant class, that can be used to hold a range of primitive values.
|
||||
|
||||
A var object can hold a range of simple primitive values, strings, or
|
||||
any kind of ReferenceCountedObject. The var class is intended to act like
|
||||
the kind of values used in dynamic scripting languages.
|
||||
|
||||
You can save/load var objects either in a small, proprietary binary format
|
||||
using writeToStream()/readFromStream(), or as JSON by using the JSON class.
|
||||
|
||||
@see JSON, DynamicObject
|
||||
|
||||
@tags{Core}
|
||||
*/
|
||||
class JUCE_API var
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** This structure is passed to a NativeFunction callback, and contains invocation
|
||||
details about the function's arguments and context.
|
||||
*/
|
||||
struct JUCE_API NativeFunctionArgs
|
||||
{
|
||||
NativeFunctionArgs (const var& thisObject, const var* args, int numArgs) noexcept;
|
||||
|
||||
const var& thisObject;
|
||||
const var* arguments;
|
||||
int numArguments;
|
||||
};
|
||||
|
||||
using NativeFunction = std::function<var (const NativeFunctionArgs&)>;
|
||||
|
||||
//==============================================================================
|
||||
/** Creates a void variant. */
|
||||
var() noexcept;
|
||||
|
||||
/** Destructor. */
|
||||
~var() noexcept;
|
||||
|
||||
var (const var& valueToCopy);
|
||||
var (int value) noexcept;
|
||||
var (int64 value) noexcept;
|
||||
var (bool value) noexcept;
|
||||
var (double value) noexcept;
|
||||
var (const char* value);
|
||||
var (const wchar_t* value);
|
||||
var (const String& value);
|
||||
var (const Array<var>& value);
|
||||
var (const StringArray& value);
|
||||
var (ReferenceCountedObject* object);
|
||||
var (NativeFunction method) noexcept;
|
||||
var (const void* binaryData, size_t dataSize);
|
||||
var (const MemoryBlock& binaryData);
|
||||
|
||||
var& operator= (const var& valueToCopy);
|
||||
var& operator= (int value);
|
||||
var& operator= (int64 value);
|
||||
var& operator= (bool value);
|
||||
var& operator= (double value);
|
||||
var& operator= (const char* value);
|
||||
var& operator= (const wchar_t* value);
|
||||
var& operator= (const String& value);
|
||||
var& operator= (const MemoryBlock& value);
|
||||
var& operator= (const Array<var>& value);
|
||||
var& operator= (ReferenceCountedObject* object);
|
||||
var& operator= (NativeFunction method);
|
||||
|
||||
var (var&&) noexcept;
|
||||
var (String&&);
|
||||
var (MemoryBlock&&);
|
||||
var (Array<var>&&);
|
||||
var& operator= (var&&) noexcept;
|
||||
var& operator= (String&&);
|
||||
|
||||
void swapWith (var& other) noexcept;
|
||||
|
||||
/** Returns a var object that can be used where you need the javascript "undefined" value. */
|
||||
static var undefined() noexcept;
|
||||
|
||||
//==============================================================================
|
||||
operator int() const noexcept;
|
||||
operator int64() const noexcept;
|
||||
operator bool() const noexcept;
|
||||
operator float() const noexcept;
|
||||
operator double() const noexcept;
|
||||
operator String() const;
|
||||
String toString() const;
|
||||
|
||||
/** If this variant holds an array, this provides access to it.
|
||||
NOTE: Beware when you use this - the array pointer is only valid for the lifetime
|
||||
of the variant that returned it, so be very careful not to call this method on temporary
|
||||
var objects that are the return-value of a function, and which may go out of scope before
|
||||
you use the array!
|
||||
*/
|
||||
Array<var>* getArray() const noexcept;
|
||||
|
||||
/** If this variant holds a memory block, this provides access to it.
|
||||
NOTE: Beware when you use this - the MemoryBlock pointer is only valid for the lifetime
|
||||
of the variant that returned it, so be very careful not to call this method on temporary
|
||||
var objects that are the return-value of a function, and which may go out of scope before
|
||||
you use the MemoryBlock!
|
||||
*/
|
||||
MemoryBlock* getBinaryData() const noexcept;
|
||||
|
||||
ReferenceCountedObject* getObject() const noexcept;
|
||||
DynamicObject* getDynamicObject() const noexcept;
|
||||
|
||||
//==============================================================================
|
||||
bool isVoid() const noexcept;
|
||||
bool isUndefined() const noexcept;
|
||||
bool isInt() const noexcept;
|
||||
bool isInt64() const noexcept;
|
||||
bool isBool() const noexcept;
|
||||
bool isDouble() const noexcept;
|
||||
bool isString() const noexcept;
|
||||
bool isObject() const noexcept;
|
||||
bool isArray() const noexcept;
|
||||
bool isBinaryData() const noexcept;
|
||||
bool isMethod() const noexcept;
|
||||
|
||||
/** Returns true if this var has the same value as the one supplied.
|
||||
Note that this ignores the type, so a string var "123" and an integer var with the
|
||||
value 123 are considered to be equal.
|
||||
|
||||
Note that equality checking depends on the "wrapped" type of the object on which
|
||||
equals() is called. That means the following code will convert the right-hand-side
|
||||
argument to a string and compare the string values, because the object on the
|
||||
left-hand-side was initialised from a string:
|
||||
@code var ("123").equals (var (123)) @endcode
|
||||
However, the following code will convert the right-hand-side argument to a double
|
||||
and compare the values as doubles, because the object on the left-hand-side was
|
||||
initialised from a double:
|
||||
@code var (45.6).equals ("45.6000") @endcode
|
||||
|
||||
@see equalsWithSameType
|
||||
*/
|
||||
bool equals (const var& other) const noexcept;
|
||||
|
||||
/** Returns true if this var has the same value and type as the one supplied.
|
||||
This differs from equals() because e.g. "123" and 123 will be considered different.
|
||||
@see equals
|
||||
*/
|
||||
bool equalsWithSameType (const var& other) const noexcept;
|
||||
|
||||
/** Returns true if this var has the same type as the one supplied. */
|
||||
bool hasSameTypeAs (const var& other) const noexcept;
|
||||
|
||||
/** Returns a deep copy of this object.
|
||||
For simple types this just returns a copy, but if the object contains any arrays
|
||||
or DynamicObjects, they will be cloned (recursively).
|
||||
*/
|
||||
var clone() const noexcept;
|
||||
|
||||
//==============================================================================
|
||||
/** If the var is an array, this returns the number of elements.
|
||||
If the var isn't actually an array, this will return 0.
|
||||
*/
|
||||
int size() const;
|
||||
|
||||
/** If the var is an array, this can be used to return one of its elements.
|
||||
To call this method, you must make sure that the var is actually an array, and
|
||||
that the index is a valid number. If these conditions aren't met, behaviour is
|
||||
undefined.
|
||||
For more control over the array's contents, you can call getArray() and manipulate
|
||||
it directly as an Array\<var\>.
|
||||
*/
|
||||
const var& operator[] (int arrayIndex) const;
|
||||
|
||||
/** If the var is an array, this can be used to return one of its elements.
|
||||
To call this method, you must make sure that the var is actually an array, and
|
||||
that the index is a valid number. If these conditions aren't met, behaviour is
|
||||
undefined.
|
||||
For more control over the array's contents, you can call getArray() and manipulate
|
||||
it directly as an Array\<var\>.
|
||||
*/
|
||||
var& operator[] (int arrayIndex);
|
||||
|
||||
/** Appends an element to the var, converting it to an array if it isn't already one.
|
||||
If the var isn't an array, it will be converted to one, and if its value was non-void,
|
||||
this value will be kept as the first element of the new array. The parameter value
|
||||
will then be appended to it.
|
||||
For more control over the array's contents, you can call getArray() and manipulate
|
||||
it directly as an Array\<var\>.
|
||||
*/
|
||||
void append (const var& valueToAppend);
|
||||
|
||||
/** Inserts an element to the var, converting it to an array if it isn't already one.
|
||||
If the var isn't an array, it will be converted to one, and if its value was non-void,
|
||||
this value will be kept as the first element of the new array. The parameter value
|
||||
will then be inserted into it.
|
||||
For more control over the array's contents, you can call getArray() and manipulate
|
||||
it directly as an Array\<var\>.
|
||||
*/
|
||||
void insert (int index, const var& value);
|
||||
|
||||
/** If the var is an array, this removes one of its elements.
|
||||
If the index is out-of-range or the var isn't an array, nothing will be done.
|
||||
For more control over the array's contents, you can call getArray() and manipulate
|
||||
it directly as an Array\<var\>.
|
||||
*/
|
||||
void remove (int index);
|
||||
|
||||
/** Treating the var as an array, this resizes it to contain the specified number of elements.
|
||||
If the var isn't an array, it will be converted to one, and if its value was non-void,
|
||||
this value will be kept as the first element of the new array before resizing.
|
||||
For more control over the array's contents, you can call getArray() and manipulate
|
||||
it directly as an Array\<var\>.
|
||||
*/
|
||||
void resize (int numArrayElementsWanted);
|
||||
|
||||
/** If the var is an array, this searches it for the first occurrence of the specified value,
|
||||
and returns its index.
|
||||
If the var isn't an array, or if the value isn't found, this returns -1.
|
||||
*/
|
||||
int indexOf (const var& value) const;
|
||||
|
||||
//==============================================================================
|
||||
/** If this variant is an object, this returns one of its properties. */
|
||||
const var& operator[] (const Identifier& propertyName) const;
|
||||
/** If this variant is an object, this returns one of its properties. */
|
||||
const var& operator[] (const char* propertyName) const;
|
||||
/** If this variant is an object, this returns one of its properties, or a default
|
||||
fallback value if the property is not set. */
|
||||
var getProperty (const Identifier& propertyName, const var& defaultReturnValue) const;
|
||||
/** Returns true if this variant is an object and if it has the given property. */
|
||||
bool hasProperty (const Identifier& propertyName) const noexcept;
|
||||
|
||||
/** Invokes a named method call with no arguments. */
|
||||
var call (const Identifier& method) const;
|
||||
/** Invokes a named method call with one argument. */
|
||||
var call (const Identifier& method, const var& arg1) const;
|
||||
/** Invokes a named method call with 2 arguments. */
|
||||
var call (const Identifier& method, const var& arg1, const var& arg2) const;
|
||||
/** Invokes a named method call with 3 arguments. */
|
||||
var call (const Identifier& method, const var& arg1, const var& arg2, const var& arg3);
|
||||
/** Invokes a named method call with 4 arguments. */
|
||||
var call (const Identifier& method, const var& arg1, const var& arg2, const var& arg3, const var& arg4) const;
|
||||
/** Invokes a named method call with 5 arguments. */
|
||||
var call (const Identifier& method, const var& arg1, const var& arg2, const var& arg3, const var& arg4, const var& arg5) const;
|
||||
/** Invokes a named method call with a list of arguments. */
|
||||
var invoke (const Identifier& method, const var* arguments, int numArguments) const;
|
||||
/** If this object is a method, this returns the function pointer. */
|
||||
NativeFunction getNativeFunction() const;
|
||||
|
||||
//==============================================================================
|
||||
/** Writes a binary representation of this value to a stream.
|
||||
The data can be read back later using readFromStream().
|
||||
@see JSON
|
||||
*/
|
||||
void writeToStream (OutputStream& output) const;
|
||||
|
||||
/** Reads back a stored binary representation of a value.
|
||||
The data in the stream must have been written using writeToStream(), or this
|
||||
will have unpredictable results.
|
||||
@see JSON
|
||||
*/
|
||||
static var readFromStream (InputStream& input);
|
||||
|
||||
//==============================================================================
|
||||
#if JUCE_ALLOW_STATIC_NULL_VARIABLES && ! defined (DOXYGEN)
|
||||
[[deprecated ("This was a static empty var object, but is now deprecated as it's too easy to accidentally "
|
||||
"use it indirectly during a static constructor leading to hard-to-find order-of-initialisation "
|
||||
"problems. Use var() or {} instead. For returning an empty var from a function by reference, "
|
||||
"use a function-local static var and return that.")]]
|
||||
static const var null;
|
||||
#endif
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
struct VariantType;
|
||||
struct Instance;
|
||||
|
||||
union ValueUnion
|
||||
{
|
||||
int intValue;
|
||||
int64 int64Value;
|
||||
bool boolValue;
|
||||
double doubleValue;
|
||||
char stringValue[sizeof (String)];
|
||||
ReferenceCountedObject* objectValue;
|
||||
MemoryBlock* binaryValue;
|
||||
NativeFunction* methodValue;
|
||||
};
|
||||
|
||||
friend bool canCompare (const var&, const var&);
|
||||
|
||||
const VariantType* type;
|
||||
ValueUnion value;
|
||||
|
||||
Array<var>* convertToArray();
|
||||
var (const VariantType&) noexcept;
|
||||
|
||||
// This is needed to prevent the wrong constructor/operator being called
|
||||
var (const ReferenceCountedObject*) = delete;
|
||||
var& operator= (const ReferenceCountedObject*) = delete;
|
||||
var (const void*) = delete;
|
||||
var& operator= (const void*) = delete;
|
||||
};
|
||||
|
||||
/** Compares the values of two var objects, using the var::equals() comparison. */
|
||||
JUCE_API bool operator== (const var&, const var&);
|
||||
/** Compares the values of two var objects, using the var::equals() comparison. */
|
||||
JUCE_API bool operator!= (const var&, const var&);
|
||||
/** Compares the values of two var objects, using the var::equals() comparison. */
|
||||
JUCE_API bool operator< (const var&, const var&);
|
||||
/** Compares the values of two var objects, using the var::equals() comparison. */
|
||||
JUCE_API bool operator<= (const var&, const var&);
|
||||
/** Compares the values of two var objects, using the var::equals() comparison. */
|
||||
JUCE_API bool operator> (const var&, const var&);
|
||||
/** Compares the values of two var objects, using the var::equals() comparison. */
|
||||
JUCE_API bool operator>= (const var&, const var&);
|
||||
|
||||
JUCE_API bool operator== (const var&, const String&);
|
||||
JUCE_API bool operator!= (const var&, const String&);
|
||||
JUCE_API bool operator== (const var&, const char*);
|
||||
JUCE_API bool operator!= (const var&, const char*);
|
||||
|
||||
//==============================================================================
|
||||
/** This template-overloaded class can be used to convert between var and custom types.
|
||||
|
||||
@tags{Core}
|
||||
*/
|
||||
template <typename Type>
|
||||
struct VariantConverter
|
||||
{
|
||||
static Type fromVar (const var& v) { return static_cast<Type> (v); }
|
||||
static var toVar (const Type& t) { return t; }
|
||||
};
|
||||
|
||||
#ifndef DOXYGEN
|
||||
template <>
|
||||
struct VariantConverter<String>
|
||||
{
|
||||
static String fromVar (const var& v) { return v.toString(); }
|
||||
static var toVar (const String& s) { return s; }
|
||||
};
|
||||
#endif
|
||||
|
||||
} // namespace juce
|
||||
|
Reference in New Issue
Block a user