migrating to the latest JUCE version

This commit is contained in:
2022-11-04 23:11:33 +01:00
committed by Nikolai Rodionov
parent 4257a0f8ba
commit faf8f18333
2796 changed files with 888518 additions and 784244 deletions

View File

@ -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

View File

@ -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

File diff suppressed because it is too large Load Diff

View File

@ -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

File diff suppressed because it is too large Load Diff

View File

@ -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

View File

@ -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

View File

@ -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

File diff suppressed because it is too large Load Diff

View File

@ -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

View File

@ -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

View 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

View File

@ -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

View File

@ -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

View File

@ -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

View 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

View 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

View File

@ -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
}

File diff suppressed because it is too large Load Diff

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

File diff suppressed because it is too large Load Diff

View File

@ -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