- clean working copy for leaving SVN
This commit is contained in:
@@ -0,0 +1,235 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the juce_core module of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
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.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
|
||||
TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
|
||||
NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
|
||||
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
|
||||
IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
|
||||
CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
NOTE! This permissive ISC license applies ONLY to files within the juce_core module!
|
||||
All other JUCE modules are covered by a dual GPL/commercial license, so if you are
|
||||
using any other modules, be sure to check that you also comply with their license.
|
||||
|
||||
For more details, visit www.juce.com
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
AbstractFifo::AbstractFifo (const 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
|
||||
{
|
||||
const int vs = validStart.get();
|
||||
const int 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
|
||||
{
|
||||
const int vs = validStart.get();
|
||||
const int ve = validEnd.value;
|
||||
|
||||
const int 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);
|
||||
int newEnd = validEnd.value + numWritten;
|
||||
if (newEnd >= bufferSize)
|
||||
newEnd -= bufferSize;
|
||||
|
||||
validEnd = newEnd;
|
||||
}
|
||||
|
||||
void AbstractFifo::prepareToRead (int numWanted, int& startIndex1, int& blockSize1, int& startIndex2, int& blockSize2) const noexcept
|
||||
{
|
||||
const int vs = validStart.value;
|
||||
const int ve = validEnd.get();
|
||||
|
||||
const int 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);
|
||||
|
||||
int newStart = validStart.value + numRead;
|
||||
if (newStart >= bufferSize)
|
||||
newStart -= bufferSize;
|
||||
|
||||
validStart = newStart;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
//==============================================================================
|
||||
#if JUCE_UNIT_TESTS
|
||||
|
||||
class AbstractFifoTests : public UnitTest
|
||||
{
|
||||
public:
|
||||
AbstractFifoTests() : UnitTest ("Abstract Fifo") {}
|
||||
|
||||
class WriteThread : public Thread
|
||||
{
|
||||
public:
|
||||
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;
|
||||
|
||||
int start1, size1, start2, size2;
|
||||
fifo.prepareToWrite (num, start1, size1, start2, size2);
|
||||
|
||||
jassert (size1 >= 0 && size2 >= 0);
|
||||
jassert (size1 == 0 || (start1 >= 0 && start1 < fifo.getTotalSize()));
|
||||
jassert (size2 == 0 || (start2 >= 0 && start2 < fifo.getTotalSize()));
|
||||
|
||||
for (int i = 0; i < size1; ++i)
|
||||
buffer [start1 + i] = n++;
|
||||
|
||||
for (int i = 0; i < size2; ++i)
|
||||
buffer [start2 + i] = n++;
|
||||
|
||||
fifo.finishedWrite (size1 + size2);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
AbstractFifo& fifo;
|
||||
int* buffer;
|
||||
Random random;
|
||||
};
|
||||
|
||||
void runTest()
|
||||
{
|
||||
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;
|
||||
|
||||
int start1, size1, start2, size2;
|
||||
fifo.prepareToRead (num, start1, size1, start2, size2);
|
||||
|
||||
if (! (size1 >= 0 && size2 >= 0)
|
||||
&& (size1 == 0 || (start1 >= 0 && start1 < fifo.getTotalSize()))
|
||||
&& (size2 == 0 || (start2 >= 0 && start2 < fifo.getTotalSize())))
|
||||
{
|
||||
expect (false, "prepareToRead returned -ve values");
|
||||
break;
|
||||
}
|
||||
|
||||
bool failed = false;
|
||||
|
||||
for (int i = 0; i < size1; ++i)
|
||||
failed = (buffer [start1 + i] != n++) || failed;
|
||||
|
||||
for (int i = 0; i < size2; ++i)
|
||||
failed = (buffer [start2 + i] != n++) || failed;
|
||||
|
||||
if (failed)
|
||||
{
|
||||
expect (false, "read values were incorrect");
|
||||
break;
|
||||
}
|
||||
|
||||
fifo.finishedRead (size1 + size2);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static AbstractFifoTests fifoUnitTests;
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,220 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the juce_core module of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
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.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
|
||||
TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
|
||||
NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
|
||||
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
|
||||
IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
|
||||
CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
NOTE! This permissive ISC license applies ONLY to files within the juce_core module!
|
||||
All other JUCE modules are covered by a dual GPL/commercial license, so if you are
|
||||
using any other modules, be sure to check that you also comply with their license.
|
||||
|
||||
For more details, visit www.juce.com
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef JUCE_ABSTRACTFIFO_H_INCLUDED
|
||||
#define JUCE_ABSTRACTFIFO_H_INCLUDED
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
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
|
||||
class MyFifo
|
||||
{
|
||||
public:
|
||||
MyFifo() : abstractFifo (1024)
|
||||
{
|
||||
}
|
||||
|
||||
void addToFifo (const int* someData, int numItems)
|
||||
{
|
||||
int start1, size1, start2, size2;
|
||||
abstractFifo.prepareToWrite (numItems, start1, size1, start2, size2);
|
||||
|
||||
if (size1 > 0)
|
||||
copySomeData (myBuffer + start1, someData, size1);
|
||||
|
||||
if (size2 > 0)
|
||||
copySomeData (myBuffer + start2, someData + size1, size2);
|
||||
|
||||
abstractFifo.finishedWrite (size1 + size2);
|
||||
}
|
||||
|
||||
void readFromFifo (int* someData, int numItems)
|
||||
{
|
||||
int start1, size1, start2, size2;
|
||||
abstractFifo.prepareToRead (numSamples, start1, size1, start2, size2);
|
||||
|
||||
if (size1 > 0)
|
||||
copySomeData (someData, myBuffer + start1, size1);
|
||||
|
||||
if (size2 > 0)
|
||||
copySomeData (someData + size1, myBuffer + start2, size2);
|
||||
|
||||
abstractFifo.finishedRead (size1 + size2);
|
||||
}
|
||||
|
||||
private:
|
||||
AbstractFifo abstractFifo;
|
||||
int myBuffer [1024];
|
||||
};
|
||||
@endcode
|
||||
*/
|
||||
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:
|
||||
//==============================================================================
|
||||
int bufferSize;
|
||||
Atomic <int> validStart, validEnd;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AbstractFifo)
|
||||
};
|
||||
|
||||
|
||||
#endif // JUCE_ABSTRACTFIFO_H_INCLUDED
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,138 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the juce_core module of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
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.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
|
||||
TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
|
||||
NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
|
||||
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
|
||||
IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
|
||||
CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
NOTE! This permissive ISC license applies ONLY to files within the juce_core module!
|
||||
All other JUCE modules are covered by a dual GPL/commercial license, so if you are
|
||||
using any other modules, be sure to check that you also comply with their license.
|
||||
|
||||
For more details, visit www.juce.com
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef JUCE_ARRAYALLOCATIONBASE_H_INCLUDED
|
||||
#define JUCE_ARRAYALLOCATIONBASE_H_INCLUDED
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Implements some basic array storage allocation functions.
|
||||
|
||||
This class isn't really for public use - it's used by the other
|
||||
array classes, but might come in handy for some purposes.
|
||||
|
||||
It inherits from a critical section class to allow the arrays to use
|
||||
the "empty base class optimisation" pattern to reduce their footprint.
|
||||
|
||||
@see Array, OwnedArray, ReferenceCountedArray
|
||||
*/
|
||||
template <class ElementType, class TypeOfCriticalSectionToUse>
|
||||
class ArrayAllocationBase : public TypeOfCriticalSectionToUse
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates an empty array. */
|
||||
ArrayAllocationBase() noexcept
|
||||
: numAllocated (0)
|
||||
{
|
||||
}
|
||||
|
||||
/** Destructor. */
|
||||
~ArrayAllocationBase() noexcept
|
||||
{
|
||||
}
|
||||
|
||||
#if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS
|
||||
ArrayAllocationBase (ArrayAllocationBase<ElementType, TypeOfCriticalSectionToUse>&& other) noexcept
|
||||
: elements (static_cast <HeapBlock <ElementType>&&> (other.elements)),
|
||||
numAllocated (other.numAllocated)
|
||||
{
|
||||
}
|
||||
|
||||
ArrayAllocationBase& operator= (ArrayAllocationBase<ElementType, TypeOfCriticalSectionToUse>&& other) noexcept
|
||||
{
|
||||
elements = static_cast <HeapBlock <ElementType>&&> (other.elements);
|
||||
numAllocated = other.numAllocated;
|
||||
return *this;
|
||||
}
|
||||
#endif
|
||||
|
||||
//==============================================================================
|
||||
/** 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 (const 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 (const 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 (const int maxNumElements)
|
||||
{
|
||||
if (maxNumElements < numAllocated)
|
||||
setAllocatedSize (maxNumElements);
|
||||
}
|
||||
|
||||
/** Swap the contents of two objects. */
|
||||
void swapWith (ArrayAllocationBase <ElementType, TypeOfCriticalSectionToUse>& other) noexcept
|
||||
{
|
||||
elements.swapWith (other.elements);
|
||||
std::swap (numAllocated, other.numAllocated);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
HeapBlock <ElementType> elements;
|
||||
int numAllocated;
|
||||
|
||||
private:
|
||||
JUCE_DECLARE_NON_COPYABLE (ArrayAllocationBase)
|
||||
};
|
||||
|
||||
|
||||
#endif // JUCE_ARRAYALLOCATIONBASE_H_INCLUDED
|
||||
@@ -0,0 +1,133 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the juce_core module of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
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.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
|
||||
TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
|
||||
NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
|
||||
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
|
||||
IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
|
||||
CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
NOTE! This permissive ISC license applies ONLY to files within the juce_core module!
|
||||
All other JUCE modules are covered by a dual GPL/commercial license, so if you are
|
||||
using any other modules, be sure to check that you also comply with their license.
|
||||
|
||||
For more details, visit www.juce.com
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
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 (var::NativeFunction function = properties [method].getNativeFunction())
|
||||
return function (args);
|
||||
|
||||
return var();
|
||||
}
|
||||
|
||||
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 (var* 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)
|
||||
{
|
||||
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);
|
||||
|
||||
if (i < numValues - 1)
|
||||
{
|
||||
if (allOnOneLine)
|
||||
out << ", ";
|
||||
else
|
||||
out << ',' << newLine;
|
||||
}
|
||||
else if (! allOnOneLine)
|
||||
out << newLine;
|
||||
}
|
||||
|
||||
if (! allOnOneLine)
|
||||
JSONFormatter::writeSpaces (out, indentLevel);
|
||||
|
||||
out << '}';
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the juce_core module of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
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.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
|
||||
TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
|
||||
NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
|
||||
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
|
||||
IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
|
||||
CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
NOTE! This permissive ISC license applies ONLY to files within the juce_core module!
|
||||
All other JUCE modules are covered by a dual GPL/commercial license, so if you are
|
||||
using any other modules, be sure to check that you also comply with their license.
|
||||
|
||||
For more details, visit www.juce.com
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef JUCE_DYNAMICOBJECT_H_INCLUDED
|
||||
#define JUCE_DYNAMICOBJECT_H_INCLUDED
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
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.
|
||||
*/
|
||||
class JUCE_API DynamicObject : public ReferenceCountedObject
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
DynamicObject();
|
||||
DynamicObject (const DynamicObject&);
|
||||
~DynamicObject();
|
||||
|
||||
typedef ReferenceCountedObjectPtr<DynamicObject> Ptr;
|
||||
|
||||
//==============================================================================
|
||||
/** 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::null if no such property exists.
|
||||
*/
|
||||
virtual 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 properies.
|
||||
*/
|
||||
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);
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
NamedValueSet properties;
|
||||
|
||||
#if JUCE_CATCH_DEPRECATED_CODE_MISUSE
|
||||
// These methods have been deprecated - use var::invoke instead
|
||||
virtual void invokeMethod (const Identifier&, const var*, int) {}
|
||||
#endif
|
||||
|
||||
JUCE_LEAK_DETECTOR (DynamicObject)
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif // JUCE_DYNAMICOBJECT_H_INCLUDED
|
||||
@@ -0,0 +1,195 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the juce_core module of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
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.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
|
||||
TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
|
||||
NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
|
||||
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
|
||||
IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
|
||||
CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
NOTE! This permissive ISC license applies ONLY to files within the juce_core module!
|
||||
All other JUCE modules are covered by a dual GPL/commercial license, so if you are
|
||||
using any other modules, be sure to check that you also comply with their license.
|
||||
|
||||
For more details, visit www.juce.com
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef JUCE_ELEMENTCOMPARATOR_H_INCLUDED
|
||||
#define JUCE_ELEMENTCOMPARATOR_H_INCLUDED
|
||||
|
||||
#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)
|
||||
*/
|
||||
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&) JUCE_DELETED_FUNCTION;
|
||||
};
|
||||
|
||||
#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)
|
||||
{
|
||||
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);
|
||||
|
||||
(void) 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
|
||||
*/
|
||||
template <class ElementType>
|
||||
class DefaultElementComparator
|
||||
{
|
||||
private:
|
||||
typedef PARAMETER_TYPE (ElementType) ParameterType;
|
||||
|
||||
public:
|
||||
static int compareElements (ParameterType first, ParameterType second)
|
||||
{
|
||||
return (first < second) ? -1 : ((second < first) ? 1 : 0);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
#endif // JUCE_ELEMENTCOMPARATOR_H_INCLUDED
|
||||
@@ -0,0 +1,455 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the juce_core module of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
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.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
|
||||
TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
|
||||
NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
|
||||
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
|
||||
IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
|
||||
CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
NOTE! This permissive ISC license applies ONLY to files within the juce_core module!
|
||||
All other JUCE modules are covered by a dual GPL/commercial license, so if you are
|
||||
using any other modules, be sure to check that you also comply with their license.
|
||||
|
||||
For more details, visit www.juce.com
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef JUCE_HASHMAP_H_INCLUDED
|
||||
#define JUCE_HASHMAP_H_INCLUDED
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A simple class to generate hash functions for some primitive types, intended for
|
||||
use with the HashMap class.
|
||||
@see HashMap
|
||||
*/
|
||||
struct DefaultHashFunctions
|
||||
{
|
||||
/** Generates a simple hash from an integer. */
|
||||
int generateHash (const int key, const int upperLimit) const noexcept { return std::abs (key) % upperLimit; }
|
||||
/** Generates a simple hash from an int64. */
|
||||
int generateHash (const int64 key, const int upperLimit) const noexcept { return std::abs ((int) key) % upperLimit; }
|
||||
/** Generates a simple hash from a string. */
|
||||
int generateHash (const String& key, const int upperLimit) const noexcept { return (int) (((uint32) key.hashCode()) % (uint32) upperLimit); }
|
||||
/** Generates a simple hash from a variant. */
|
||||
int generateHash (const var& key, const int upperLimit) const noexcept { return generateHash (key.toString(), upperLimit); }
|
||||
};
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Holds a set of mappings between some key/value pairs.
|
||||
|
||||
The types of the key and value objects are set as template parameters.
|
||||
You can also specify a class to supply a hash function that converts a key value
|
||||
into an hashed integer. This class must have the form:
|
||||
|
||||
@code
|
||||
struct MyHashGenerator
|
||||
{
|
||||
int generateHash (MyKeyType key, int upperLimit) const
|
||||
{
|
||||
// The function must return a value 0 <= x < upperLimit
|
||||
return someFunctionOfMyKeyType (key) % upperLimit;
|
||||
}
|
||||
};
|
||||
@endcode
|
||||
|
||||
Like the Array class, the key and value types are expected to be copy-by-value
|
||||
types, so if you define them to be pointer types, this class won't delete the
|
||||
objects that they point to.
|
||||
|
||||
If you don't supply a class for the HashFunctionType template parameter, the
|
||||
default one provides some simple mappings for strings and ints.
|
||||
|
||||
@code
|
||||
HashMap<int, String> hash;
|
||||
hash.set (1, "item1");
|
||||
hash.set (2, "item2");
|
||||
|
||||
DBG (hash [1]); // prints "item1"
|
||||
DBG (hash [2]); // prints "item2"
|
||||
|
||||
// This iterates the map, printing all of its key -> value pairs..
|
||||
for (HashMap<int, String>::Iterator i (hash); i.next();)
|
||||
DBG (i.getKey() << " -> " << i.getValue());
|
||||
@endcode
|
||||
|
||||
@tparam HashFunctionType The class of hash function, which must be copy-constructible.
|
||||
@see CriticalSection, DefaultHashFunctions, NamedValueSet, SortedSet
|
||||
*/
|
||||
template <typename KeyType,
|
||||
typename ValueType,
|
||||
class HashFunctionType = DefaultHashFunctions,
|
||||
class TypeOfCriticalSectionToUse = DummyCriticalSection>
|
||||
class HashMap
|
||||
{
|
||||
private:
|
||||
typedef PARAMETER_TYPE (KeyType) KeyTypeParameter;
|
||||
typedef PARAMETER_TYPE (ValueType) ValueTypeParameter;
|
||||
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates an empty hash-map.
|
||||
|
||||
@param numberOfSlots Specifies the number of hash entries the map will use. This will be
|
||||
the "upperLimit" parameter that is passed to your generateHash()
|
||||
function. The number of hash slots will grow automatically if necessary,
|
||||
or it can be remapped manually using remapTable().
|
||||
@param hashFunction An instance of HashFunctionType, which will be copied and
|
||||
stored to use with the HashMap. This parameter can be omitted
|
||||
if HashFunctionType has a default constructor.
|
||||
*/
|
||||
explicit HashMap (int numberOfSlots = defaultHashTableSize,
|
||||
HashFunctionType hashFunction = HashFunctionType())
|
||||
: hashFunctionToUse (hashFunction), totalNumItems (0)
|
||||
{
|
||||
hashSlots.insertMultiple (0, nullptr, numberOfSlots);
|
||||
}
|
||||
|
||||
/** Destructor. */
|
||||
~HashMap()
|
||||
{
|
||||
clear();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Removes all values from the map.
|
||||
Note that this will clear the content, but won't affect the number of slots (see
|
||||
remapTable and getNumSlots).
|
||||
*/
|
||||
void clear()
|
||||
{
|
||||
const ScopedLockType sl (getLock());
|
||||
|
||||
for (int i = hashSlots.size(); --i >= 0;)
|
||||
{
|
||||
HashEntry* h = hashSlots.getUnchecked(i);
|
||||
|
||||
while (h != nullptr)
|
||||
{
|
||||
const ScopedPointer<HashEntry> deleter (h);
|
||||
h = h->nextEntry;
|
||||
}
|
||||
|
||||
hashSlots.set (i, nullptr);
|
||||
}
|
||||
|
||||
totalNumItems = 0;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the current number of items in the map. */
|
||||
inline int size() const noexcept
|
||||
{
|
||||
return totalNumItems;
|
||||
}
|
||||
|
||||
/** Returns the value corresponding to a given key.
|
||||
If the map doesn't contain the key, a default instance of the value type is returned.
|
||||
@param keyToLookFor the key of the item being requested
|
||||
*/
|
||||
inline ValueType operator[] (KeyTypeParameter keyToLookFor) const
|
||||
{
|
||||
const ScopedLockType sl (getLock());
|
||||
|
||||
for (const HashEntry* entry = hashSlots.getUnchecked (generateHashFor (keyToLookFor)); entry != nullptr; entry = entry->nextEntry)
|
||||
if (entry->key == keyToLookFor)
|
||||
return entry->value;
|
||||
|
||||
return ValueType();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Returns true if the map contains an item with the specied key. */
|
||||
bool contains (KeyTypeParameter keyToLookFor) const
|
||||
{
|
||||
const ScopedLockType sl (getLock());
|
||||
|
||||
for (const HashEntry* entry = hashSlots.getUnchecked (generateHashFor (keyToLookFor)); entry != nullptr; entry = entry->nextEntry)
|
||||
if (entry->key == keyToLookFor)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Returns true if the hash contains at least one occurrence of a given value. */
|
||||
bool containsValue (ValueTypeParameter valueToLookFor) const
|
||||
{
|
||||
const ScopedLockType sl (getLock());
|
||||
|
||||
for (int i = getNumSlots(); --i >= 0;)
|
||||
for (const HashEntry* entry = hashSlots.getUnchecked(i); entry != nullptr; entry = entry->nextEntry)
|
||||
if (entry->value == valueToLookFor)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Adds or replaces an element in the hash-map.
|
||||
If there's already an item with the given key, this will replace its value. Otherwise, a new item
|
||||
will be added to the map.
|
||||
*/
|
||||
void set (KeyTypeParameter newKey, ValueTypeParameter newValue)
|
||||
{
|
||||
const ScopedLockType sl (getLock());
|
||||
const int hashIndex = generateHashFor (newKey);
|
||||
|
||||
HashEntry* const firstEntry = hashSlots.getUnchecked (hashIndex);
|
||||
|
||||
for (HashEntry* entry = firstEntry; entry != nullptr; entry = entry->nextEntry)
|
||||
{
|
||||
if (entry->key == newKey)
|
||||
{
|
||||
entry->value = newValue;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
hashSlots.set (hashIndex, new HashEntry (newKey, newValue, firstEntry));
|
||||
++totalNumItems;
|
||||
|
||||
if (totalNumItems > (getNumSlots() * 3) / 2)
|
||||
remapTable (getNumSlots() * 2);
|
||||
}
|
||||
|
||||
/** Removes an item with the given key. */
|
||||
void remove (KeyTypeParameter keyToRemove)
|
||||
{
|
||||
const ScopedLockType sl (getLock());
|
||||
const int hashIndex = generateHashFor (keyToRemove);
|
||||
HashEntry* entry = hashSlots.getUnchecked (hashIndex);
|
||||
HashEntry* previous = nullptr;
|
||||
|
||||
while (entry != nullptr)
|
||||
{
|
||||
if (entry->key == keyToRemove)
|
||||
{
|
||||
const ScopedPointer<HashEntry> deleter (entry);
|
||||
|
||||
entry = entry->nextEntry;
|
||||
|
||||
if (previous != nullptr)
|
||||
previous->nextEntry = entry;
|
||||
else
|
||||
hashSlots.set (hashIndex, entry);
|
||||
|
||||
--totalNumItems;
|
||||
}
|
||||
else
|
||||
{
|
||||
previous = entry;
|
||||
entry = entry->nextEntry;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Removes all items with the given value. */
|
||||
void removeValue (ValueTypeParameter valueToRemove)
|
||||
{
|
||||
const ScopedLockType sl (getLock());
|
||||
|
||||
for (int i = getNumSlots(); --i >= 0;)
|
||||
{
|
||||
HashEntry* entry = hashSlots.getUnchecked(i);
|
||||
HashEntry* previous = nullptr;
|
||||
|
||||
while (entry != nullptr)
|
||||
{
|
||||
if (entry->value == valueToRemove)
|
||||
{
|
||||
const ScopedPointer<HashEntry> deleter (entry);
|
||||
|
||||
entry = entry->nextEntry;
|
||||
|
||||
if (previous != nullptr)
|
||||
previous->nextEntry = entry;
|
||||
else
|
||||
hashSlots.set (i, entry);
|
||||
|
||||
--totalNumItems;
|
||||
}
|
||||
else
|
||||
{
|
||||
previous = entry;
|
||||
entry = entry->nextEntry;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Remaps the hash-map to use a different number of slots for its hash function.
|
||||
Each slot corresponds to a single hash-code, and each one can contain multiple items.
|
||||
@see getNumSlots()
|
||||
*/
|
||||
void remapTable (int newNumberOfSlots)
|
||||
{
|
||||
HashMap newTable (newNumberOfSlots);
|
||||
|
||||
for (int i = getNumSlots(); --i >= 0;)
|
||||
for (const HashEntry* entry = hashSlots.getUnchecked(i); entry != nullptr; entry = entry->nextEntry)
|
||||
newTable.set (entry->key, entry->value);
|
||||
|
||||
swapWith (newTable);
|
||||
}
|
||||
|
||||
/** Returns the number of slots which are available for hashing.
|
||||
Each slot corresponds to a single hash-code, and each one can contain multiple items.
|
||||
@see getNumSlots()
|
||||
*/
|
||||
inline int getNumSlots() const noexcept
|
||||
{
|
||||
return hashSlots.size();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Efficiently swaps the contents of two hash-maps. */
|
||||
template <class OtherHashMapType>
|
||||
void swapWith (OtherHashMapType& otherHashMap) noexcept
|
||||
{
|
||||
const ScopedLockType lock1 (getLock());
|
||||
const typename OtherHashMapType::ScopedLockType lock2 (otherHashMap.getLock());
|
||||
|
||||
hashSlots.swapWith (otherHashMap.hashSlots);
|
||||
std::swap (totalNumItems, otherHashMap.totalNumItems);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the CriticalSection that locks this structure.
|
||||
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 lock; }
|
||||
|
||||
/** Returns the type of scoped lock to use for locking this array */
|
||||
typedef typename TypeOfCriticalSectionToUse::ScopedLockType ScopedLockType;
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
class HashEntry
|
||||
{
|
||||
public:
|
||||
HashEntry (KeyTypeParameter k, ValueTypeParameter val, HashEntry* const next)
|
||||
: key (k), value (val), nextEntry (next)
|
||||
{}
|
||||
|
||||
const KeyType key;
|
||||
ValueType value;
|
||||
HashEntry* nextEntry;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE (HashEntry)
|
||||
};
|
||||
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Iterates over the items in a HashMap.
|
||||
|
||||
To use it, repeatedly call next() until it returns false, e.g.
|
||||
@code
|
||||
HashMap <String, String> myMap;
|
||||
|
||||
HashMap<String, String>::Iterator i (myMap);
|
||||
|
||||
while (i.next())
|
||||
{
|
||||
DBG (i.getKey() << " -> " << i.getValue());
|
||||
}
|
||||
@endcode
|
||||
|
||||
The order in which items are iterated bears no resemblence to the order in which
|
||||
they were originally added!
|
||||
|
||||
Obviously as soon as you call any non-const methods on the original hash-map, any
|
||||
iterators that were created beforehand will cease to be valid, and should not be used.
|
||||
|
||||
@see HashMap
|
||||
*/
|
||||
class Iterator
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
Iterator (const HashMap& hashMapToIterate)
|
||||
: hashMap (hashMapToIterate), entry (nullptr), index (0)
|
||||
{}
|
||||
|
||||
/** Moves to the next item, if one is available.
|
||||
When this returns true, you can get the item's key and value using getKey() and
|
||||
getValue(). If it returns false, the iteration has finished and you should stop.
|
||||
*/
|
||||
bool next()
|
||||
{
|
||||
if (entry != nullptr)
|
||||
entry = entry->nextEntry;
|
||||
|
||||
while (entry == nullptr)
|
||||
{
|
||||
if (index >= hashMap.getNumSlots())
|
||||
return false;
|
||||
|
||||
entry = hashMap.hashSlots.getUnchecked (index++);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Returns the current item's key.
|
||||
This should only be called when a call to next() has just returned true.
|
||||
*/
|
||||
KeyType getKey() const
|
||||
{
|
||||
return entry != nullptr ? entry->key : KeyType();
|
||||
}
|
||||
|
||||
/** Returns the current item's value.
|
||||
This should only be called when a call to next() has just returned true.
|
||||
*/
|
||||
ValueType getValue() const
|
||||
{
|
||||
return entry != nullptr ? entry->value : ValueType();
|
||||
}
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
const HashMap& hashMap;
|
||||
HashEntry* entry;
|
||||
int index;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Iterator)
|
||||
};
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
enum { defaultHashTableSize = 101 };
|
||||
friend class Iterator;
|
||||
|
||||
HashFunctionType hashFunctionToUse;
|
||||
Array<HashEntry*> hashSlots;
|
||||
int totalNumItems;
|
||||
TypeOfCriticalSectionToUse lock;
|
||||
|
||||
int generateHashFor (KeyTypeParameter key) const
|
||||
{
|
||||
const int hash = hashFunctionToUse.generateHash (key, getNumSlots());
|
||||
jassert (isPositiveAndBelow (hash, getNumSlots())); // your hash function is generating out-of-range numbers!
|
||||
return hash;
|
||||
}
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (HashMap)
|
||||
};
|
||||
|
||||
|
||||
#endif // JUCE_HASHMAP_H_INCLUDED
|
||||
@@ -0,0 +1,371 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the juce_core module of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
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.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
|
||||
TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
|
||||
NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
|
||||
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
|
||||
IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
|
||||
CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
NOTE! This permissive ISC license applies ONLY to files within the juce_core module!
|
||||
All other JUCE modules are covered by a dual GPL/commercial license, so if you are
|
||||
using any other modules, be sure to check that you also comply with their license.
|
||||
|
||||
For more details, visit www.juce.com
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef JUCE_LINKEDLISTPOINTER_H_INCLUDED
|
||||
#define JUCE_LINKEDLISTPOINTER_H_INCLUDED
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
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
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
#if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS
|
||||
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;
|
||||
}
|
||||
#endif
|
||||
|
||||
//==============================================================================
|
||||
/** 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
|
||||
{
|
||||
LinkedListPointer* 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 (ObjectType* 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
|
||||
{
|
||||
LinkedListPointer* 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
|
||||
{
|
||||
const LinkedListPointer* 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 (ObjectType* 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)
|
||||
{
|
||||
jassert (newItem != nullptr);
|
||||
jassert (newItem->nextListItem == nullptr);
|
||||
newItem->nextListItem = item;
|
||||
item = newItem;
|
||||
}
|
||||
|
||||
/** 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);
|
||||
LinkedListPointer* 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
|
||||
{
|
||||
jassert (newItem != nullptr);
|
||||
jassert (newItem->nextListItem == nullptr);
|
||||
|
||||
ObjectType* const oldItem = item;
|
||||
item = newItem;
|
||||
item->nextListItem = oldItem->nextListItem.item;
|
||||
oldItem->nextListItem.item = nullptr;
|
||||
return oldItem;
|
||||
}
|
||||
|
||||
/** 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)
|
||||
{
|
||||
LinkedListPointer* insertPoint = this;
|
||||
|
||||
for (ObjectType* 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
|
||||
{
|
||||
ObjectType* const 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 (LinkedListPointer* const 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)
|
||||
{
|
||||
ObjectType* const 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
|
||||
{
|
||||
LinkedListPointer* 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
|
||||
{
|
||||
jassert (destArray != nullptr);
|
||||
|
||||
for (ObjectType* i = item; i != nullptr; i = i->nextListItem)
|
||||
*destArray++ = i;
|
||||
}
|
||||
|
||||
/** 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)
|
||||
};
|
||||
|
||||
|
||||
#endif // JUCE_LINKEDLISTPOINTER_H_INCLUDED
|
||||
@@ -0,0 +1,270 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the juce_core module of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
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.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
|
||||
TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
|
||||
NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
|
||||
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
|
||||
IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
|
||||
CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
NOTE! This permissive ISC license applies ONLY to files within the juce_core module!
|
||||
All other JUCE modules are covered by a dual GPL/commercial license, so if you are
|
||||
using any other modules, be sure to check that you also comply with their license.
|
||||
|
||||
For more details, visit www.juce.com
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
struct NamedValueSet::NamedValue
|
||||
{
|
||||
NamedValue() noexcept {}
|
||||
NamedValue (Identifier n, const var& v) : name (n), value (v) {}
|
||||
NamedValue (const NamedValue& other) : name (other.name), value (other.value) {}
|
||||
|
||||
#if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS
|
||||
NamedValue (NamedValue&& other) noexcept
|
||||
: name (static_cast<Identifier&&> (other.name)),
|
||||
value (static_cast<var&&> (other.value))
|
||||
{
|
||||
}
|
||||
|
||||
NamedValue (Identifier n, var&& v) : name (n), value (static_cast<var&&> (v))
|
||||
{
|
||||
}
|
||||
|
||||
NamedValue& operator= (NamedValue&& other) noexcept
|
||||
{
|
||||
name = static_cast<Identifier&&> (other.name);
|
||||
value = static_cast<var&&> (other.value);
|
||||
return *this;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool operator== (const NamedValue& other) const noexcept { return name == other.name && value == other.value; }
|
||||
bool operator!= (const NamedValue& other) const noexcept { return ! operator== (other); }
|
||||
|
||||
Identifier name;
|
||||
var value;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
NamedValueSet::NamedValueSet() noexcept
|
||||
{
|
||||
}
|
||||
|
||||
NamedValueSet::NamedValueSet (const NamedValueSet& other)
|
||||
: values (other.values)
|
||||
{
|
||||
}
|
||||
|
||||
NamedValueSet& NamedValueSet::operator= (const NamedValueSet& other)
|
||||
{
|
||||
clear();
|
||||
values = other.values;
|
||||
return *this;
|
||||
}
|
||||
|
||||
#if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS
|
||||
NamedValueSet::NamedValueSet (NamedValueSet&& other) noexcept
|
||||
: values (static_cast <Array<NamedValue>&&> (other.values))
|
||||
{
|
||||
}
|
||||
|
||||
NamedValueSet& NamedValueSet::operator= (NamedValueSet&& other) noexcept
|
||||
{
|
||||
other.values.swapWith (values);
|
||||
return *this;
|
||||
}
|
||||
#endif
|
||||
|
||||
NamedValueSet::~NamedValueSet() noexcept
|
||||
{
|
||||
}
|
||||
|
||||
void NamedValueSet::clear()
|
||||
{
|
||||
values.clear();
|
||||
}
|
||||
|
||||
bool NamedValueSet::operator== (const NamedValueSet& other) const
|
||||
{
|
||||
return values == other.values;
|
||||
}
|
||||
|
||||
bool NamedValueSet::operator!= (const NamedValueSet& other) const
|
||||
{
|
||||
return ! operator== (other);
|
||||
}
|
||||
|
||||
int NamedValueSet::size() const noexcept
|
||||
{
|
||||
return values.size();
|
||||
}
|
||||
|
||||
const var& NamedValueSet::operator[] (const Identifier& name) const noexcept
|
||||
{
|
||||
if (const var* v = getVarPointer (name))
|
||||
return *v;
|
||||
|
||||
return var::null;
|
||||
}
|
||||
|
||||
var NamedValueSet::getWithDefault (const Identifier& name, const var& defaultReturnValue) const
|
||||
{
|
||||
if (const var* const v = getVarPointer (name))
|
||||
return *v;
|
||||
|
||||
return defaultReturnValue;
|
||||
}
|
||||
|
||||
var* NamedValueSet::getVarPointer (const Identifier& name) const noexcept
|
||||
{
|
||||
for (NamedValue* e = values.end(), *i = values.begin(); i != e; ++i)
|
||||
if (i->name == name)
|
||||
return &(i->value);
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
#if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS
|
||||
bool NamedValueSet::set (Identifier name, var&& newValue)
|
||||
{
|
||||
if (var* const v = getVarPointer (name))
|
||||
{
|
||||
if (v->equalsWithSameType (newValue))
|
||||
return false;
|
||||
|
||||
*v = static_cast<var&&> (newValue);
|
||||
return true;
|
||||
}
|
||||
|
||||
values.add (NamedValue (name, static_cast<var&&> (newValue)));
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool NamedValueSet::set (Identifier name, const var& newValue)
|
||||
{
|
||||
if (var* const v = getVarPointer (name))
|
||||
{
|
||||
if (v->equalsWithSameType (newValue))
|
||||
return false;
|
||||
|
||||
*v = newValue;
|
||||
return true;
|
||||
}
|
||||
|
||||
values.add (NamedValue (name, newValue));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NamedValueSet::contains (const Identifier& name) const noexcept
|
||||
{
|
||||
return getVarPointer (name) != nullptr;
|
||||
}
|
||||
|
||||
int NamedValueSet::indexOf (const Identifier& name) const noexcept
|
||||
{
|
||||
const int 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)
|
||||
{
|
||||
const int 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 Identifier();
|
||||
}
|
||||
|
||||
const var& NamedValueSet::getValueAt (const int index) const noexcept
|
||||
{
|
||||
if (isPositiveAndBelow (index, values.size()))
|
||||
return values.getReference (index).value;
|
||||
|
||||
jassertfalse;
|
||||
return var::null;
|
||||
}
|
||||
|
||||
var* NamedValueSet::getVarPointerAt (int index) const noexcept
|
||||
{
|
||||
if (isPositiveAndBelow (index, values.size()))
|
||||
return &(values.getReference (index).value);
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void NamedValueSet::setFromXmlAttributes (const XmlElement& xml)
|
||||
{
|
||||
values.clearQuick();
|
||||
|
||||
for (const XmlElement::XmlAttributeNode* att = xml.attributes; att != nullptr; att = att->nextListItem)
|
||||
{
|
||||
if (att->name.toString().startsWith ("base64:"))
|
||||
{
|
||||
MemoryBlock mb;
|
||||
|
||||
if (mb.fromBase64Encoding (att->value))
|
||||
{
|
||||
values.add (NamedValue (att->name.toString().substring (7), var (mb)));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
values.add (NamedValue (att->name, var (att->value)));
|
||||
}
|
||||
}
|
||||
|
||||
void NamedValueSet::copyToXmlAttributes (XmlElement& xml) const
|
||||
{
|
||||
for (NamedValue* e = values.end(), *i = values.begin(); i != e; ++i)
|
||||
{
|
||||
if (const MemoryBlock* 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());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the juce_core module of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
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.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
|
||||
TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
|
||||
NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
|
||||
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
|
||||
IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
|
||||
CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
NOTE! This permissive ISC license applies ONLY to files within the juce_core module!
|
||||
All other JUCE modules are covered by a dual GPL/commercial license, so if you are
|
||||
using any other modules, be sure to check that you also comply with their license.
|
||||
|
||||
For more details, visit www.juce.com
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef JUCE_NAMEDVALUESET_H_INCLUDED
|
||||
#define JUCE_NAMEDVALUESET_H_INCLUDED
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/** 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.
|
||||
*/
|
||||
class JUCE_API NamedValueSet
|
||||
{
|
||||
public:
|
||||
/** Creates an empty set. */
|
||||
NamedValueSet() noexcept;
|
||||
|
||||
/** Creates a copy of another set. */
|
||||
NamedValueSet (const NamedValueSet&);
|
||||
|
||||
/** Replaces this set with a copy of another set. */
|
||||
NamedValueSet& operator= (const NamedValueSet&);
|
||||
|
||||
#if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS
|
||||
NamedValueSet (NamedValueSet&&) noexcept;
|
||||
NamedValueSet& operator= (NamedValueSet&&) noexcept;
|
||||
#endif
|
||||
|
||||
/** Destructor. */
|
||||
~NamedValueSet() noexcept;
|
||||
|
||||
bool operator== (const NamedValueSet&) const;
|
||||
bool operator!= (const NamedValueSet&) const;
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the total number of values that the set contains. */
|
||||
int size() const noexcept;
|
||||
|
||||
/** Returns the value of a named item.
|
||||
If the name isn't found, this will return a void variant.
|
||||
@see getProperty
|
||||
*/
|
||||
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 (Identifier name, const var& newValue);
|
||||
|
||||
#if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS
|
||||
/** 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 (Identifier name, var&& newValue);
|
||||
#endif
|
||||
|
||||
/** 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().
|
||||
*/
|
||||
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
|
||||
*/
|
||||
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:
|
||||
//==============================================================================
|
||||
struct NamedValue;
|
||||
Array<NamedValue> values;
|
||||
};
|
||||
|
||||
|
||||
#endif // JUCE_NAMEDVALUESET_H_INCLUDED
|
||||
@@ -0,0 +1,897 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the juce_core module of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
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.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
|
||||
TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
|
||||
NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
|
||||
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
|
||||
IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
|
||||
CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
NOTE! This permissive ISC license applies ONLY to files within the juce_core module!
|
||||
All other JUCE modules are covered by a dual GPL/commercial license, so if you are
|
||||
using any other modules, be sure to check that you also comply with their license.
|
||||
|
||||
For more details, visit www.juce.com
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef JUCE_OWNEDARRAY_H_INCLUDED
|
||||
#define JUCE_OWNEDARRAY_H_INCLUDED
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/** An array designed for holding objects.
|
||||
|
||||
This holds a list of pointers to objects, and will automatically
|
||||
delete the objects when they are removed from the array, or when the
|
||||
array is itself deleted.
|
||||
|
||||
Declare it in the form: OwnedArray<MyObjectClass>
|
||||
|
||||
..and then add new objects, e.g. myOwnedArray.add (new MyObjectClass());
|
||||
|
||||
After adding objects, they are 'owned' by the array and will be deleted when
|
||||
removed or replaced.
|
||||
|
||||
To make all the array's methods thread-safe, pass in "CriticalSection" as the templated
|
||||
TypeOfCriticalSectionToUse parameter, instead of the default DummyCriticalSection.
|
||||
|
||||
@see Array, ReferenceCountedArray, StringArray, CriticalSection
|
||||
*/
|
||||
template <class ObjectClass,
|
||||
class TypeOfCriticalSectionToUse = DummyCriticalSection>
|
||||
|
||||
class OwnedArray
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates an empty array. */
|
||||
OwnedArray() noexcept
|
||||
: numUsed (0)
|
||||
{
|
||||
}
|
||||
|
||||
/** Deletes the array and also deletes any objects inside it.
|
||||
|
||||
To get rid of the array without deleting its objects, use its
|
||||
clear (false) method before deleting it.
|
||||
*/
|
||||
~OwnedArray()
|
||||
{
|
||||
deleteAllObjects();
|
||||
}
|
||||
|
||||
#if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS
|
||||
OwnedArray (OwnedArray&& other) noexcept
|
||||
: data (static_cast <ArrayAllocationBase <ObjectClass*, TypeOfCriticalSectionToUse>&&> (other.data)),
|
||||
numUsed (other.numUsed)
|
||||
{
|
||||
other.numUsed = 0;
|
||||
}
|
||||
|
||||
OwnedArray& operator= (OwnedArray&& other) noexcept
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
deleteAllObjects();
|
||||
|
||||
data = static_cast <ArrayAllocationBase <ObjectClass*, TypeOfCriticalSectionToUse>&&> (other.data);
|
||||
numUsed = other.numUsed;
|
||||
other.numUsed = 0;
|
||||
return *this;
|
||||
}
|
||||
#endif
|
||||
|
||||
//==============================================================================
|
||||
/** Clears the array, optionally deleting the objects inside it first. */
|
||||
void clear (bool deleteObjects = true)
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
|
||||
if (deleteObjects)
|
||||
deleteAllObjects();
|
||||
|
||||
data.setAllocatedSize (0);
|
||||
numUsed = 0;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Clears the array, optionally deleting the objects inside it first. */
|
||||
void clearQuick (bool deleteObjects)
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
|
||||
if (deleteObjects)
|
||||
deleteAllObjects();
|
||||
|
||||
numUsed = 0;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the number of items currently in the array.
|
||||
@see operator[]
|
||||
*/
|
||||
inline int size() const noexcept
|
||||
{
|
||||
return numUsed;
|
||||
}
|
||||
|
||||
/** Returns a pointer to the object at this index in the array.
|
||||
|
||||
If the index is out-of-range, this will return a null pointer, (and
|
||||
it could be null anyway, because it's ok for the array to hold null
|
||||
pointers as well as objects).
|
||||
|
||||
@see getUnchecked
|
||||
*/
|
||||
inline ObjectClass* operator[] (const int index) const noexcept
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
if (isPositiveAndBelow (index, numUsed))
|
||||
{
|
||||
jassert (data.elements != nullptr);
|
||||
return data.elements [index];
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/** Returns a pointer to the object at this index in the array, without checking whether the index is in-range.
|
||||
|
||||
This is a faster and less safe version of operator[] which doesn't check the index passed in, so
|
||||
it can be used when you're sure the index is always going to be legal.
|
||||
*/
|
||||
inline ObjectClass* getUnchecked (const int index) const noexcept
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
jassert (isPositiveAndBelow (index, numUsed) && data.elements != nullptr);
|
||||
return data.elements [index];
|
||||
}
|
||||
|
||||
/** Returns a pointer to the first object in the array.
|
||||
|
||||
This will return a null pointer if the array's empty.
|
||||
@see getLast
|
||||
*/
|
||||
inline ObjectClass* getFirst() const noexcept
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
|
||||
if (numUsed > 0)
|
||||
{
|
||||
jassert (data.elements != nullptr);
|
||||
return data.elements [0];
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/** Returns a pointer to the last object in the array.
|
||||
|
||||
This will return a null pointer if the array's empty.
|
||||
@see getFirst
|
||||
*/
|
||||
inline ObjectClass* getLast() const noexcept
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
|
||||
if (numUsed > 0)
|
||||
{
|
||||
jassert (data.elements != nullptr);
|
||||
return data.elements [numUsed - 1];
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/** Returns a pointer to the actual array data.
|
||||
This pointer will only be valid until the next time a non-const method
|
||||
is called on the array.
|
||||
*/
|
||||
inline ObjectClass** getRawDataPointer() noexcept
|
||||
{
|
||||
return data.elements;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Returns a pointer to the first element in the array.
|
||||
This method is provided for compatibility with standard C++ iteration mechanisms.
|
||||
*/
|
||||
inline ObjectClass** begin() const noexcept
|
||||
{
|
||||
return data.elements;
|
||||
}
|
||||
|
||||
/** Returns a pointer to the element which follows the last element in the array.
|
||||
This method is provided for compatibility with standard C++ iteration mechanisms.
|
||||
*/
|
||||
inline ObjectClass** end() const noexcept
|
||||
{
|
||||
#if JUCE_DEBUG
|
||||
if (data.elements == nullptr || numUsed <= 0) // (to keep static analysers happy)
|
||||
return data.elements;
|
||||
#endif
|
||||
|
||||
return data.elements + numUsed;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Finds the index of an object which might be in the array.
|
||||
|
||||
@param objectToLookFor the object to look for
|
||||
@returns the index at which the object was found, or -1 if it's not found
|
||||
*/
|
||||
int indexOf (const ObjectClass* objectToLookFor) const noexcept
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
ObjectClass* const* e = data.elements.getData();
|
||||
ObjectClass* const* const end_ = e + numUsed;
|
||||
|
||||
for (; e != end_; ++e)
|
||||
if (objectToLookFor == *e)
|
||||
return static_cast <int> (e - data.elements.getData());
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/** Returns true if the array contains a specified object.
|
||||
|
||||
@param objectToLookFor the object to look for
|
||||
@returns true if the object is in the array
|
||||
*/
|
||||
bool contains (const ObjectClass* objectToLookFor) const noexcept
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
ObjectClass* const* e = data.elements.getData();
|
||||
ObjectClass* const* const end_ = e + numUsed;
|
||||
|
||||
for (; e != end_; ++e)
|
||||
if (objectToLookFor == *e)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Appends a new object to the end of the array.
|
||||
|
||||
Note that the this object will be deleted by the OwnedArray when it
|
||||
is removed, so be careful not to delete it somewhere else.
|
||||
|
||||
Also be careful not to add the same object to the array more than once,
|
||||
as this will obviously cause deletion of dangling pointers.
|
||||
|
||||
@param newObject the new object to add to the array
|
||||
@returns the new object that was added
|
||||
@see set, insert, addIfNotAlreadyThere, addSorted
|
||||
*/
|
||||
ObjectClass* add (ObjectClass* newObject) noexcept
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
data.ensureAllocatedSize (numUsed + 1);
|
||||
jassert (data.elements != nullptr);
|
||||
data.elements [numUsed++] = newObject;
|
||||
return newObject;
|
||||
}
|
||||
|
||||
/** Inserts a new object into the array at the given index.
|
||||
|
||||
Note that the this object will be deleted by the OwnedArray when it
|
||||
is removed, so be careful not to delete it somewhere else.
|
||||
|
||||
If the index is less than 0 or greater than the size of the array, the
|
||||
element will be added to the end of the array.
|
||||
Otherwise, it will be inserted into the array, moving all the later elements
|
||||
along to make room.
|
||||
|
||||
Be careful not to add the same object to the array more than once,
|
||||
as this will obviously cause deletion of dangling pointers.
|
||||
|
||||
@param indexToInsertAt the index at which the new element should be inserted
|
||||
@param newObject the new object to add to the array
|
||||
@returns the new object that was added
|
||||
@see add, addSorted, addIfNotAlreadyThere, set
|
||||
*/
|
||||
ObjectClass* insert (int indexToInsertAt, ObjectClass* newObject) noexcept
|
||||
{
|
||||
if (indexToInsertAt < 0)
|
||||
return add (newObject);
|
||||
|
||||
const ScopedLockType lock (getLock());
|
||||
|
||||
if (indexToInsertAt > numUsed)
|
||||
indexToInsertAt = numUsed;
|
||||
|
||||
data.ensureAllocatedSize (numUsed + 1);
|
||||
jassert (data.elements != nullptr);
|
||||
|
||||
ObjectClass** const e = data.elements + indexToInsertAt;
|
||||
const int numToMove = numUsed - indexToInsertAt;
|
||||
|
||||
if (numToMove > 0)
|
||||
memmove (e + 1, e, sizeof (ObjectClass*) * (size_t) numToMove);
|
||||
|
||||
*e = newObject;
|
||||
++numUsed;
|
||||
return newObject;
|
||||
}
|
||||
|
||||
/** Inserts an array of values into this array at a given position.
|
||||
|
||||
If the index is less than 0 or greater than the size of the array, the
|
||||
new elements will be added to the end of the array.
|
||||
Otherwise, they will be inserted into the array, moving all the later elements
|
||||
along to make room.
|
||||
|
||||
@param indexToInsertAt the index at which the first new element should be inserted
|
||||
@param newObjects the new values to add to the array
|
||||
@param numberOfElements how many items are in the array
|
||||
@see insert, add, addSorted, set
|
||||
*/
|
||||
void insertArray (int indexToInsertAt,
|
||||
ObjectClass* const* newObjects,
|
||||
int numberOfElements)
|
||||
{
|
||||
if (numberOfElements > 0)
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
data.ensureAllocatedSize (numUsed + numberOfElements);
|
||||
ObjectClass** insertPos = data.elements;
|
||||
|
||||
if (isPositiveAndBelow (indexToInsertAt, numUsed))
|
||||
{
|
||||
insertPos += indexToInsertAt;
|
||||
const size_t numberToMove = (size_t) (numUsed - indexToInsertAt);
|
||||
memmove (insertPos + numberOfElements, insertPos, numberToMove * sizeof (ObjectClass*));
|
||||
}
|
||||
else
|
||||
{
|
||||
insertPos += numUsed;
|
||||
}
|
||||
|
||||
numUsed += numberOfElements;
|
||||
|
||||
while (--numberOfElements >= 0)
|
||||
*insertPos++ = *newObjects++;
|
||||
}
|
||||
}
|
||||
|
||||
/** Appends a new object at the end of the array as long as the array doesn't
|
||||
already contain it.
|
||||
|
||||
If the array already contains a matching object, nothing will be done.
|
||||
|
||||
@param newObject the new object to add to the array
|
||||
@returns the new object that was added
|
||||
*/
|
||||
ObjectClass* addIfNotAlreadyThere (ObjectClass* newObject) noexcept
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
|
||||
if (! contains (newObject))
|
||||
add (newObject);
|
||||
|
||||
return newObject;
|
||||
}
|
||||
|
||||
/** Replaces an object in the array with a different one.
|
||||
|
||||
If the index is less than zero, this method does nothing.
|
||||
If the index is beyond the end of the array, the new object is added to the end of the array.
|
||||
|
||||
Be careful not to add the same object to the array more than once,
|
||||
as this will obviously cause deletion of dangling pointers.
|
||||
|
||||
@param indexToChange the index whose value you want to change
|
||||
@param newObject the new value to set for this index.
|
||||
@param deleteOldElement whether to delete the object that's being replaced with the new one
|
||||
@see add, insert, remove
|
||||
*/
|
||||
ObjectClass* set (int indexToChange, ObjectClass* newObject, bool deleteOldElement = true)
|
||||
{
|
||||
if (indexToChange >= 0)
|
||||
{
|
||||
ScopedPointer<ObjectClass> toDelete;
|
||||
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
|
||||
if (indexToChange < numUsed)
|
||||
{
|
||||
if (deleteOldElement)
|
||||
{
|
||||
toDelete = data.elements [indexToChange];
|
||||
|
||||
if (toDelete == newObject)
|
||||
toDelete.release();
|
||||
}
|
||||
|
||||
data.elements [indexToChange] = newObject;
|
||||
}
|
||||
else
|
||||
{
|
||||
data.ensureAllocatedSize (numUsed + 1);
|
||||
data.elements [numUsed++] = newObject;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
jassertfalse; // you're trying to set an object at a negative index, which doesn't have
|
||||
// any effect - but since the object is not being added, it may be leaking..
|
||||
}
|
||||
|
||||
return newObject;
|
||||
}
|
||||
|
||||
/** Adds elements from another array to the end of this array.
|
||||
|
||||
@param arrayToAddFrom the array from which to copy the elements
|
||||
@param startIndex the first element of the other array to start copying from
|
||||
@param numElementsToAdd how many elements to add from the other array. If this
|
||||
value is negative or greater than the number of available elements,
|
||||
all available elements will be copied.
|
||||
@see add
|
||||
*/
|
||||
template <class OtherArrayType>
|
||||
void addArray (const OtherArrayType& arrayToAddFrom,
|
||||
int startIndex = 0,
|
||||
int numElementsToAdd = -1)
|
||||
{
|
||||
const typename OtherArrayType::ScopedLockType lock1 (arrayToAddFrom.getLock());
|
||||
const ScopedLockType lock2 (getLock());
|
||||
|
||||
if (startIndex < 0)
|
||||
{
|
||||
jassertfalse;
|
||||
startIndex = 0;
|
||||
}
|
||||
|
||||
if (numElementsToAdd < 0 || startIndex + numElementsToAdd > arrayToAddFrom.size())
|
||||
numElementsToAdd = arrayToAddFrom.size() - startIndex;
|
||||
|
||||
data.ensureAllocatedSize (numUsed + numElementsToAdd);
|
||||
jassert (numElementsToAdd <= 0 || data.elements != nullptr);
|
||||
|
||||
while (--numElementsToAdd >= 0)
|
||||
{
|
||||
data.elements [numUsed] = arrayToAddFrom.getUnchecked (startIndex++);
|
||||
++numUsed;
|
||||
}
|
||||
}
|
||||
|
||||
/** Adds copies of the elements in another array to the end of this array.
|
||||
|
||||
The other array must be either an OwnedArray of a compatible type of object, or an Array
|
||||
containing pointers to the same kind of object. The objects involved must provide
|
||||
a copy constructor, and this will be used to create new copies of each element, and
|
||||
add them to this array.
|
||||
|
||||
@param arrayToAddFrom the array from which to copy the elements
|
||||
@param startIndex the first element of the other array to start copying from
|
||||
@param numElementsToAdd how many elements to add from the other array. If this
|
||||
value is negative or greater than the number of available elements,
|
||||
all available elements will be copied.
|
||||
@see add
|
||||
*/
|
||||
template <class OtherArrayType>
|
||||
void addCopiesOf (const OtherArrayType& arrayToAddFrom,
|
||||
int startIndex = 0,
|
||||
int numElementsToAdd = -1)
|
||||
{
|
||||
const typename OtherArrayType::ScopedLockType lock1 (arrayToAddFrom.getLock());
|
||||
const ScopedLockType lock2 (getLock());
|
||||
|
||||
if (startIndex < 0)
|
||||
{
|
||||
jassertfalse;
|
||||
startIndex = 0;
|
||||
}
|
||||
|
||||
if (numElementsToAdd < 0 || startIndex + numElementsToAdd > arrayToAddFrom.size())
|
||||
numElementsToAdd = arrayToAddFrom.size() - startIndex;
|
||||
|
||||
data.ensureAllocatedSize (numUsed + numElementsToAdd);
|
||||
jassert (numElementsToAdd <= 0 || data.elements != nullptr);
|
||||
|
||||
while (--numElementsToAdd >= 0)
|
||||
data.elements [numUsed++] = createCopyIfNotNull (arrayToAddFrom.getUnchecked (startIndex++));
|
||||
}
|
||||
|
||||
/** Inserts a new object into the array assuming that the array is sorted.
|
||||
|
||||
This will use a comparator to find the position at which the new object
|
||||
should go. If the array isn't sorted, the behaviour of this
|
||||
method will be unpredictable.
|
||||
|
||||
@param comparator the comparator to use to compare the elements - see the sort method
|
||||
for details about this object's structure
|
||||
@param newObject the new object to insert to the array
|
||||
@returns the index at which the new object was added
|
||||
@see add, sort, indexOfSorted
|
||||
*/
|
||||
template <class ElementComparator>
|
||||
int addSorted (ElementComparator& comparator, ObjectClass* const newObject) noexcept
|
||||
{
|
||||
(void) comparator; // if you pass in an object with a static compareElements() method, this
|
||||
// avoids getting warning messages about the parameter being unused
|
||||
const ScopedLockType lock (getLock());
|
||||
const int index = findInsertIndexInSortedArray (comparator, data.elements.getData(), newObject, 0, numUsed);
|
||||
insert (index, newObject);
|
||||
return index;
|
||||
}
|
||||
|
||||
/** Finds the index of an object in the array, assuming that the array is sorted.
|
||||
|
||||
This will use a comparator to do a binary-chop to find the index of the given
|
||||
element, if it exists. If the array isn't sorted, the behaviour of this
|
||||
method will be unpredictable.
|
||||
|
||||
@param comparator the comparator to use to compare the elements - see the sort()
|
||||
method for details about the form this object should take
|
||||
@param objectToLookFor the object to search for
|
||||
@returns the index of the element, or -1 if it's not found
|
||||
@see addSorted, sort
|
||||
*/
|
||||
template <typename ElementComparator>
|
||||
int indexOfSorted (ElementComparator& comparator, const ObjectClass* const objectToLookFor) const noexcept
|
||||
{
|
||||
(void) comparator;
|
||||
const ScopedLockType lock (getLock());
|
||||
int s = 0, e = numUsed;
|
||||
|
||||
while (s < e)
|
||||
{
|
||||
if (comparator.compareElements (objectToLookFor, data.elements [s]) == 0)
|
||||
return s;
|
||||
|
||||
const int halfway = (s + e) / 2;
|
||||
if (halfway == s)
|
||||
break;
|
||||
|
||||
if (comparator.compareElements (objectToLookFor, data.elements [halfway]) >= 0)
|
||||
s = halfway;
|
||||
else
|
||||
e = halfway;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Removes an object from the array.
|
||||
|
||||
This will remove the object at a given index (optionally also
|
||||
deleting it) and move back all the subsequent objects to close the gap.
|
||||
If the index passed in is out-of-range, nothing will happen.
|
||||
|
||||
@param indexToRemove the index of the element to remove
|
||||
@param deleteObject whether to delete the object that is removed
|
||||
@see removeObject, removeRange
|
||||
*/
|
||||
void remove (int indexToRemove, bool deleteObject = true)
|
||||
{
|
||||
ScopedPointer<ObjectClass> toDelete;
|
||||
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
|
||||
if (isPositiveAndBelow (indexToRemove, numUsed))
|
||||
{
|
||||
ObjectClass** const e = data.elements + indexToRemove;
|
||||
|
||||
if (deleteObject)
|
||||
toDelete = *e;
|
||||
|
||||
--numUsed;
|
||||
const int numToShift = numUsed - indexToRemove;
|
||||
|
||||
if (numToShift > 0)
|
||||
memmove (e, e + 1, sizeof (ObjectClass*) * (size_t) numToShift);
|
||||
}
|
||||
}
|
||||
|
||||
if ((numUsed << 1) < data.numAllocated)
|
||||
minimiseStorageOverheads();
|
||||
}
|
||||
|
||||
/** Removes and returns an object from the array without deleting it.
|
||||
|
||||
This will remove the object at a given index and return it, moving back all
|
||||
the subsequent objects to close the gap. If the index passed in is out-of-range,
|
||||
nothing will happen.
|
||||
|
||||
@param indexToRemove the index of the element to remove
|
||||
@see remove, removeObject, removeRange
|
||||
*/
|
||||
ObjectClass* removeAndReturn (int indexToRemove)
|
||||
{
|
||||
ObjectClass* removedItem = nullptr;
|
||||
const ScopedLockType lock (getLock());
|
||||
|
||||
if (isPositiveAndBelow (indexToRemove, numUsed))
|
||||
{
|
||||
ObjectClass** const e = data.elements + indexToRemove;
|
||||
removedItem = *e;
|
||||
|
||||
--numUsed;
|
||||
const int numToShift = numUsed - indexToRemove;
|
||||
|
||||
if (numToShift > 0)
|
||||
memmove (e, e + 1, sizeof (ObjectClass*) * (size_t) numToShift);
|
||||
|
||||
if ((numUsed << 1) < data.numAllocated)
|
||||
minimiseStorageOverheads();
|
||||
}
|
||||
|
||||
return removedItem;
|
||||
}
|
||||
|
||||
/** Removes a specified object from the array.
|
||||
|
||||
If the item isn't found, no action is taken.
|
||||
|
||||
@param objectToRemove the object to try to remove
|
||||
@param deleteObject whether to delete the object (if it's found)
|
||||
@see remove, removeRange
|
||||
*/
|
||||
void removeObject (const ObjectClass* objectToRemove, bool deleteObject = true)
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
ObjectClass** const e = data.elements.getData();
|
||||
|
||||
for (int i = 0; i < numUsed; ++i)
|
||||
{
|
||||
if (objectToRemove == e[i])
|
||||
{
|
||||
remove (i, deleteObject);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Removes a range of objects from the array.
|
||||
|
||||
This will remove a set of objects, starting from the given index,
|
||||
and move any subsequent elements down to close the gap.
|
||||
|
||||
If the range extends beyond the bounds of the array, it will
|
||||
be safely clipped to the size of the array.
|
||||
|
||||
@param startIndex the index of the first object to remove
|
||||
@param numberToRemove how many objects should be removed
|
||||
@param deleteObjects whether to delete the objects that get removed
|
||||
@see remove, removeObject
|
||||
*/
|
||||
void removeRange (int startIndex, int numberToRemove, bool deleteObjects = true)
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
const int endIndex = jlimit (0, numUsed, startIndex + numberToRemove);
|
||||
startIndex = jlimit (0, numUsed, startIndex);
|
||||
|
||||
if (endIndex > startIndex)
|
||||
{
|
||||
if (deleteObjects)
|
||||
{
|
||||
for (int i = startIndex; i < endIndex; ++i)
|
||||
{
|
||||
ContainerDeletePolicy<ObjectClass>::destroy (data.elements [i]);
|
||||
data.elements [i] = nullptr; // (in case one of the destructors accesses this array and hits a dangling pointer)
|
||||
}
|
||||
}
|
||||
|
||||
const int rangeSize = endIndex - startIndex;
|
||||
ObjectClass** e = data.elements + startIndex;
|
||||
int numToShift = numUsed - endIndex;
|
||||
numUsed -= rangeSize;
|
||||
|
||||
while (--numToShift >= 0)
|
||||
{
|
||||
*e = e [rangeSize];
|
||||
++e;
|
||||
}
|
||||
|
||||
if ((numUsed << 1) < data.numAllocated)
|
||||
minimiseStorageOverheads();
|
||||
}
|
||||
}
|
||||
|
||||
/** Removes the last n objects from the array.
|
||||
|
||||
@param howManyToRemove how many objects to remove from the end of the array
|
||||
@param deleteObjects whether to also delete the objects that are removed
|
||||
@see remove, removeObject, removeRange
|
||||
*/
|
||||
void removeLast (int howManyToRemove = 1,
|
||||
bool deleteObjects = true)
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
|
||||
if (howManyToRemove >= numUsed)
|
||||
clear (deleteObjects);
|
||||
else
|
||||
removeRange (numUsed - howManyToRemove, howManyToRemove, deleteObjects);
|
||||
}
|
||||
|
||||
/** Swaps a pair of objects in the array.
|
||||
|
||||
If either of the indexes passed in is out-of-range, nothing will happen,
|
||||
otherwise the two objects at these positions will be exchanged.
|
||||
*/
|
||||
void swap (int index1,
|
||||
int index2) noexcept
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
|
||||
if (isPositiveAndBelow (index1, numUsed)
|
||||
&& isPositiveAndBelow (index2, numUsed))
|
||||
{
|
||||
std::swap (data.elements [index1],
|
||||
data.elements [index2]);
|
||||
}
|
||||
}
|
||||
|
||||
/** Moves one of the objects to a different position.
|
||||
|
||||
This will move the object to a specified index, shuffling along
|
||||
any intervening elements as required.
|
||||
|
||||
So for example, if you have the array { 0, 1, 2, 3, 4, 5 } then calling
|
||||
move (2, 4) would result in { 0, 1, 3, 4, 2, 5 }.
|
||||
|
||||
@param currentIndex the index of the object to be moved. If this isn't a
|
||||
valid index, then nothing will be done
|
||||
@param newIndex the index at which you'd like this object to end up. If this
|
||||
is less than zero, it will be moved to the end of the array
|
||||
*/
|
||||
void move (int currentIndex, int newIndex) noexcept
|
||||
{
|
||||
if (currentIndex != newIndex)
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
|
||||
if (isPositiveAndBelow (currentIndex, numUsed))
|
||||
{
|
||||
if (! isPositiveAndBelow (newIndex, numUsed))
|
||||
newIndex = numUsed - 1;
|
||||
|
||||
ObjectClass* const value = data.elements [currentIndex];
|
||||
|
||||
if (newIndex > currentIndex)
|
||||
{
|
||||
memmove (data.elements + currentIndex,
|
||||
data.elements + currentIndex + 1,
|
||||
sizeof (ObjectClass*) * (size_t) (newIndex - currentIndex));
|
||||
}
|
||||
else
|
||||
{
|
||||
memmove (data.elements + newIndex + 1,
|
||||
data.elements + newIndex,
|
||||
sizeof (ObjectClass*) * (size_t) (currentIndex - newIndex));
|
||||
}
|
||||
|
||||
data.elements [newIndex] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** 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 OtherArrayType>
|
||||
void swapWith (OtherArrayType& otherArray) noexcept
|
||||
{
|
||||
const ScopedLockType lock1 (getLock());
|
||||
const typename OtherArrayType::ScopedLockType lock2 (otherArray.getLock());
|
||||
data.swapWith (otherArray.data);
|
||||
std::swap (numUsed, otherArray.numUsed);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Reduces the amount of storage being used by the array.
|
||||
|
||||
Arrays 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
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
data.shrinkToNoMoreThan (numUsed);
|
||||
}
|
||||
|
||||
/** Increases the array's internal storage to hold a minimum number of elements.
|
||||
|
||||
Calling this before adding a large known number of elements means that
|
||||
the array 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) noexcept
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
data.ensureAllocatedSize (minNumElements);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Sorts the elements in the array.
|
||||
|
||||
This will use a comparator object to sort the elements into order. The object
|
||||
passed must have a method of the form:
|
||||
@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 the comparator to use for comparing elements.
|
||||
@param retainOrderOfEquivalentItems if this is true, then items
|
||||
which the comparator says are equivalent will be
|
||||
kept in the order in which they currently appear
|
||||
in the array. This is slower to perform, but may
|
||||
be important in some cases. If it's false, a faster
|
||||
algorithm is used, but equivalent elements may be
|
||||
rearranged.
|
||||
@see sortArray, indexOfSorted
|
||||
*/
|
||||
template <class ElementComparator>
|
||||
void sort (ElementComparator& comparator,
|
||||
bool retainOrderOfEquivalentItems = false) const noexcept
|
||||
{
|
||||
(void) comparator; // if you pass in an object with a static compareElements() method, this
|
||||
// avoids getting warning messages about the parameter being unused
|
||||
|
||||
const ScopedLockType lock (getLock());
|
||||
sortArray (comparator, data.elements.getData(), 0, size() - 1, retainOrderOfEquivalentItems);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** 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; }
|
||||
|
||||
/** Returns the type of scoped lock to use for locking this array */
|
||||
typedef typename TypeOfCriticalSectionToUse::ScopedLockType ScopedLockType;
|
||||
|
||||
|
||||
//==============================================================================
|
||||
#ifndef DOXYGEN
|
||||
// Note that the swapWithArray method has been replaced by a more flexible templated version,
|
||||
// and renamed "swapWith" to be more consistent with the names used in other classes.
|
||||
JUCE_DEPRECATED_WITH_BODY (void swapWithArray (OwnedArray& other) noexcept, { swapWith (other); })
|
||||
#endif
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
ArrayAllocationBase <ObjectClass*, TypeOfCriticalSectionToUse> data;
|
||||
int numUsed;
|
||||
|
||||
void deleteAllObjects()
|
||||
{
|
||||
while (numUsed > 0)
|
||||
ContainerDeletePolicy<ObjectClass>::destroy (data.elements [--numUsed]);
|
||||
}
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OwnedArray)
|
||||
};
|
||||
|
||||
|
||||
#endif // JUCE_OWNEDARRAY_H_INCLUDED
|
||||
@@ -0,0 +1,219 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the juce_core module of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
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.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
|
||||
TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
|
||||
NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
|
||||
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
|
||||
IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
|
||||
CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
NOTE! This permissive ISC license applies ONLY to files within the juce_core module!
|
||||
All other JUCE modules are covered by a dual GPL/commercial license, so if you are
|
||||
using any other modules, be sure to check that you also comply with their license.
|
||||
|
||||
For more details, visit www.juce.com
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
PropertySet::PropertySet (const 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);
|
||||
|
||||
const int 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, const int defaultValue) const noexcept
|
||||
{
|
||||
const ScopedLock sl (lock);
|
||||
const int 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, const double defaultValue) const noexcept
|
||||
{
|
||||
const ScopedLock sl (lock);
|
||||
const int 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, const bool defaultValue) const noexcept
|
||||
{
|
||||
const ScopedLock sl (lock);
|
||||
const int index = properties.getAllKeys().indexOf (keyName, ignoreCaseOfKeys);
|
||||
|
||||
if (index >= 0)
|
||||
return properties.getAllValues() [index].getIntValue() != 0;
|
||||
|
||||
return fallbackProperties != nullptr ? fallbackProperties->getBoolValue (keyName, defaultValue)
|
||||
: defaultValue;
|
||||
}
|
||||
|
||||
XmlElement* PropertySet::getXmlValue (StringRef keyName) const
|
||||
{
|
||||
return XmlDocument::parse (getValue (keyName));
|
||||
}
|
||||
|
||||
void PropertySet::setValue (const String& keyName, const var& v)
|
||||
{
|
||||
jassert (keyName.isNotEmpty()); // shouldn't use an empty key name!
|
||||
|
||||
if (keyName.isNotEmpty())
|
||||
{
|
||||
const String value (v.toString());
|
||||
const ScopedLock sl (lock);
|
||||
|
||||
const int 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);
|
||||
const int index = properties.getAllKeys().indexOf (keyName, ignoreCaseOfKeys);
|
||||
|
||||
if (index >= 0)
|
||||
{
|
||||
properties.remove (keyName);
|
||||
propertyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PropertySet::setValue (const String& keyName, const XmlElement* const xml)
|
||||
{
|
||||
setValue (keyName, xml == nullptr ? var()
|
||||
: var (xml->createDocument ("", true)));
|
||||
}
|
||||
|
||||
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_;
|
||||
}
|
||||
|
||||
XmlElement* PropertySet::createXml (const String& nodeName) const
|
||||
{
|
||||
const ScopedLock sl (lock);
|
||||
XmlElement* const xml = new XmlElement (nodeName);
|
||||
|
||||
for (int i = 0; i < properties.getAllKeys().size(); ++i)
|
||||
{
|
||||
XmlElement* const 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();
|
||||
|
||||
forEachXmlChildElementWithTagName (xml, e, "VALUE")
|
||||
{
|
||||
if (e->hasAttribute ("name")
|
||||
&& e->hasAttribute ("val"))
|
||||
{
|
||||
properties.set (e->getStringAttribute ("name"),
|
||||
e->getStringAttribute ("val"));
|
||||
}
|
||||
}
|
||||
|
||||
if (properties.size() > 0)
|
||||
propertyChanged();
|
||||
}
|
||||
|
||||
void PropertySet::propertyChanged()
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,211 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the juce_core module of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
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.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
|
||||
TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
|
||||
NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
|
||||
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
|
||||
IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
|
||||
CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
NOTE! This permissive ISC license applies ONLY to files within the juce_core module!
|
||||
All other JUCE modules are covered by a dual GPL/commercial license, so if you are
|
||||
using any other modules, be sure to check that you also comply with their license.
|
||||
|
||||
For more details, visit www.juce.com
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef JUCE_PROPERTYSET_H_INCLUDED
|
||||
#define JUCE_PROPERTYSET_H_INCLUDED
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
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.
|
||||
*/
|
||||
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 that the caller must delete. If 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
|
||||
*/
|
||||
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 (const String& 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 zero, the value will be set to
|
||||
an empty string
|
||||
@see getXmlValue
|
||||
*/
|
||||
void setValue (const String& 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 properies 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
|
||||
*/
|
||||
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 properies has been changed. */
|
||||
virtual void propertyChanged();
|
||||
|
||||
private:
|
||||
StringPairArray properties;
|
||||
PropertySet* fallbackProperties;
|
||||
CriticalSection lock;
|
||||
bool ignoreCaseOfKeys;
|
||||
|
||||
JUCE_LEAK_DETECTOR (PropertySet)
|
||||
};
|
||||
|
||||
|
||||
#endif // JUCE_PROPERTYSET_H_INCLUDED
|
||||
@@ -0,0 +1,897 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the juce_core module of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
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.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
|
||||
TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
|
||||
NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
|
||||
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
|
||||
IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
|
||||
CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
NOTE! This permissive ISC license applies ONLY to files within the juce_core module!
|
||||
All other JUCE modules are covered by a dual GPL/commercial license, so if you are
|
||||
using any other modules, be sure to check that you also comply with their license.
|
||||
|
||||
For more details, visit www.juce.com
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef JUCE_REFERENCECOUNTEDARRAY_H_INCLUDED
|
||||
#define JUCE_REFERENCECOUNTEDARRAY_H_INCLUDED
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Holds a list of objects derived from ReferenceCountedObject, or which implement basic
|
||||
reference-count handling methods.
|
||||
|
||||
The template parameter specifies the class of the object you want to point to - the easiest
|
||||
way to make a class reference-countable is to simply make it inherit from ReferenceCountedObject
|
||||
or SingleThreadedReferenceCountedObject, but if you need to, you can roll your own reference-countable
|
||||
class by implementing a set of methods called incReferenceCount(), decReferenceCount(), and
|
||||
decReferenceCountWithoutDeleting(). See ReferenceCountedObject for examples of how these methods
|
||||
should behave.
|
||||
|
||||
A ReferenceCountedArray holds objects derived from ReferenceCountedObject,
|
||||
and takes care of incrementing and decrementing their ref counts when they
|
||||
are added and removed from the array.
|
||||
|
||||
To make all the array's methods thread-safe, pass in "CriticalSection" as the templated
|
||||
TypeOfCriticalSectionToUse parameter, instead of the default DummyCriticalSection.
|
||||
|
||||
@see Array, OwnedArray, StringArray
|
||||
*/
|
||||
template <class ObjectClass, class TypeOfCriticalSectionToUse = DummyCriticalSection>
|
||||
class ReferenceCountedArray
|
||||
{
|
||||
public:
|
||||
typedef ReferenceCountedObjectPtr<ObjectClass> ObjectClassPtr;
|
||||
|
||||
//==============================================================================
|
||||
/** Creates an empty array.
|
||||
@see ReferenceCountedObject, Array, OwnedArray
|
||||
*/
|
||||
ReferenceCountedArray() noexcept
|
||||
: numUsed (0)
|
||||
{
|
||||
}
|
||||
|
||||
/** Creates a copy of another array */
|
||||
ReferenceCountedArray (const ReferenceCountedArray& other) noexcept
|
||||
{
|
||||
const ScopedLockType lock (other.getLock());
|
||||
numUsed = other.size();
|
||||
data.setAllocatedSize (numUsed);
|
||||
memcpy (data.elements, other.getRawDataPointer(), (size_t) numUsed * sizeof (ObjectClass*));
|
||||
|
||||
for (int i = numUsed; --i >= 0;)
|
||||
if (ObjectClass* o = data.elements[i])
|
||||
o->incReferenceCount();
|
||||
}
|
||||
|
||||
/** Creates a copy of another array */
|
||||
template <class OtherObjectClass, class OtherCriticalSection>
|
||||
ReferenceCountedArray (const ReferenceCountedArray<OtherObjectClass, OtherCriticalSection>& other) noexcept
|
||||
{
|
||||
const typename ReferenceCountedArray<OtherObjectClass, OtherCriticalSection>::ScopedLockType lock (other.getLock());
|
||||
numUsed = other.size();
|
||||
data.setAllocatedSize (numUsed);
|
||||
memcpy (data.elements, other.getRawDataPointer(), numUsed * sizeof (ObjectClass*));
|
||||
|
||||
for (int i = numUsed; --i >= 0;)
|
||||
if (ObjectClass* o = data.elements[i])
|
||||
o->incReferenceCount();
|
||||
}
|
||||
|
||||
/** Copies another array into this one.
|
||||
Any existing objects in this array will first be released.
|
||||
*/
|
||||
ReferenceCountedArray& operator= (const ReferenceCountedArray& other) noexcept
|
||||
{
|
||||
ReferenceCountedArray otherCopy (other);
|
||||
swapWith (otherCopy);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** Copies another array into this one.
|
||||
Any existing objects in this array will first be released.
|
||||
*/
|
||||
template <class OtherObjectClass>
|
||||
ReferenceCountedArray<ObjectClass, TypeOfCriticalSectionToUse>& operator= (const ReferenceCountedArray<OtherObjectClass, TypeOfCriticalSectionToUse>& other) noexcept
|
||||
{
|
||||
ReferenceCountedArray<ObjectClass, TypeOfCriticalSectionToUse> otherCopy (other);
|
||||
swapWith (otherCopy);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** Destructor.
|
||||
Any objects in the array will be released, and may be deleted if not referenced from elsewhere.
|
||||
*/
|
||||
~ReferenceCountedArray()
|
||||
{
|
||||
clear();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Removes all objects from the array.
|
||||
|
||||
Any objects in the array that are not referenced from elsewhere will be deleted.
|
||||
*/
|
||||
void clear()
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
|
||||
while (numUsed > 0)
|
||||
if (ObjectClass* o = data.elements [--numUsed])
|
||||
releaseObject (o);
|
||||
|
||||
jassert (numUsed == 0);
|
||||
data.setAllocatedSize (0);
|
||||
}
|
||||
|
||||
/** Returns the current number of objects in the array. */
|
||||
inline int size() const noexcept
|
||||
{
|
||||
return numUsed;
|
||||
}
|
||||
|
||||
/** Returns a pointer to the object at this index in the array.
|
||||
|
||||
If the index is out-of-range, this will return a null pointer, (and
|
||||
it could be null anyway, because it's ok for the array to hold null
|
||||
pointers as well as objects).
|
||||
|
||||
@see getUnchecked
|
||||
*/
|
||||
inline ObjectClassPtr operator[] (const int index) const noexcept
|
||||
{
|
||||
return getObjectPointer (index);
|
||||
}
|
||||
|
||||
/** Returns a pointer to the object at this index in the array, without checking
|
||||
whether the index is in-range.
|
||||
|
||||
This is a faster and less safe version of operator[] which doesn't check the index passed in, so
|
||||
it can be used when you're sure the index is always going to be legal.
|
||||
*/
|
||||
inline ObjectClassPtr getUnchecked (const int index) const noexcept
|
||||
{
|
||||
return getObjectPointerUnchecked (index);
|
||||
}
|
||||
|
||||
/** Returns a raw pointer to the object at this index in the array.
|
||||
|
||||
If the index is out-of-range, this will return a null pointer, (and
|
||||
it could be null anyway, because it's ok for the array to hold null
|
||||
pointers as well as objects).
|
||||
|
||||
@see getUnchecked
|
||||
*/
|
||||
inline ObjectClass* getObjectPointer (const int index) const noexcept
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
|
||||
if (isPositiveAndBelow (index, numUsed))
|
||||
{
|
||||
jassert (data.elements != nullptr);
|
||||
return data.elements [index];
|
||||
}
|
||||
|
||||
return ObjectClassPtr();
|
||||
}
|
||||
|
||||
/** Returns a raw pointer to the object at this index in the array, without checking
|
||||
whether the index is in-range.
|
||||
*/
|
||||
inline ObjectClass* getObjectPointerUnchecked (const int index) const noexcept
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
jassert (isPositiveAndBelow (index, numUsed) && data.elements != nullptr);
|
||||
return data.elements [index];
|
||||
}
|
||||
|
||||
/** Returns a pointer to the first object in the array.
|
||||
|
||||
This will return a null pointer if the array's empty.
|
||||
@see getLast
|
||||
*/
|
||||
inline ObjectClassPtr getFirst() const noexcept
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
|
||||
if (numUsed > 0)
|
||||
{
|
||||
jassert (data.elements != nullptr);
|
||||
return data.elements [0];
|
||||
}
|
||||
|
||||
return ObjectClassPtr();
|
||||
}
|
||||
|
||||
/** Returns a pointer to the last object in the array.
|
||||
|
||||
This will return a null pointer if the array's empty.
|
||||
@see getFirst
|
||||
*/
|
||||
inline ObjectClassPtr getLast() const noexcept
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
|
||||
if (numUsed > 0)
|
||||
{
|
||||
jassert (data.elements != nullptr);
|
||||
return data.elements [numUsed - 1];
|
||||
}
|
||||
|
||||
return ObjectClassPtr();
|
||||
}
|
||||
|
||||
/** Returns a pointer to the actual array data.
|
||||
This pointer will only be valid until the next time a non-const method
|
||||
is called on the array.
|
||||
*/
|
||||
inline ObjectClass** getRawDataPointer() const noexcept
|
||||
{
|
||||
return data.elements;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Returns a pointer to the first element in the array.
|
||||
This method is provided for compatibility with standard C++ iteration mechanisms.
|
||||
*/
|
||||
inline ObjectClass** begin() const noexcept
|
||||
{
|
||||
return data.elements;
|
||||
}
|
||||
|
||||
/** Returns a pointer to the element which follows the last element in the array.
|
||||
This method is provided for compatibility with standard C++ iteration mechanisms.
|
||||
*/
|
||||
inline ObjectClass** end() const noexcept
|
||||
{
|
||||
return data.elements + numUsed;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Finds the index of the first occurrence of an object in the array.
|
||||
|
||||
@param objectToLookFor the object to look for
|
||||
@returns the index at which the object was found, or -1 if it's not found
|
||||
*/
|
||||
int indexOf (const ObjectClass* const objectToLookFor) const noexcept
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
ObjectClass** e = data.elements.getData();
|
||||
ObjectClass** const endPointer = e + numUsed;
|
||||
|
||||
while (e != endPointer)
|
||||
{
|
||||
if (objectToLookFor == *e)
|
||||
return static_cast <int> (e - data.elements.getData());
|
||||
|
||||
++e;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/** Returns true if the array contains a specified object.
|
||||
|
||||
@param objectToLookFor the object to look for
|
||||
@returns true if the object is in the array
|
||||
*/
|
||||
bool contains (const ObjectClass* const objectToLookFor) const noexcept
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
ObjectClass** e = data.elements.getData();
|
||||
ObjectClass** const endPointer = e + numUsed;
|
||||
|
||||
while (e != endPointer)
|
||||
{
|
||||
if (objectToLookFor == *e)
|
||||
return true;
|
||||
|
||||
++e;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Appends a new object to the end of the array.
|
||||
|
||||
This will increase the new object's reference count.
|
||||
|
||||
@param newObject the new object to add to the array
|
||||
@see set, insert, addIfNotAlreadyThere, addSorted, addArray
|
||||
*/
|
||||
ObjectClass* add (ObjectClass* const newObject) noexcept
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
data.ensureAllocatedSize (numUsed + 1);
|
||||
jassert (data.elements != nullptr);
|
||||
data.elements [numUsed++] = newObject;
|
||||
|
||||
if (newObject != nullptr)
|
||||
newObject->incReferenceCount();
|
||||
|
||||
return newObject;
|
||||
}
|
||||
|
||||
/** Inserts a new object into the array at the given index.
|
||||
|
||||
If the index is less than 0 or greater than the size of the array, the
|
||||
element will be added to the end of the array.
|
||||
Otherwise, it will be inserted into the array, moving all the later elements
|
||||
along to make room.
|
||||
|
||||
This will increase the new object's reference count.
|
||||
|
||||
@param indexToInsertAt the index at which the new element should be inserted
|
||||
@param newObject the new object to add to the array
|
||||
@see add, addSorted, addIfNotAlreadyThere, set
|
||||
*/
|
||||
ObjectClass* insert (int indexToInsertAt,
|
||||
ObjectClass* const newObject) noexcept
|
||||
{
|
||||
if (indexToInsertAt < 0)
|
||||
return add (newObject);
|
||||
|
||||
const ScopedLockType lock (getLock());
|
||||
|
||||
if (indexToInsertAt > numUsed)
|
||||
indexToInsertAt = numUsed;
|
||||
|
||||
data.ensureAllocatedSize (numUsed + 1);
|
||||
jassert (data.elements != nullptr);
|
||||
|
||||
ObjectClass** const e = data.elements + indexToInsertAt;
|
||||
const int numToMove = numUsed - indexToInsertAt;
|
||||
|
||||
if (numToMove > 0)
|
||||
memmove (e + 1, e, sizeof (ObjectClass*) * (size_t) numToMove);
|
||||
|
||||
*e = newObject;
|
||||
|
||||
if (newObject != nullptr)
|
||||
newObject->incReferenceCount();
|
||||
|
||||
++numUsed;
|
||||
|
||||
return newObject;
|
||||
}
|
||||
|
||||
/** Appends a new object at the end of the array as long as the array doesn't
|
||||
already contain it.
|
||||
|
||||
If the array already contains a matching object, nothing will be done.
|
||||
|
||||
@param newObject the new object to add to the array
|
||||
*/
|
||||
void addIfNotAlreadyThere (ObjectClass* const newObject) noexcept
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
if (! contains (newObject))
|
||||
add (newObject);
|
||||
}
|
||||
|
||||
/** Replaces an object in the array with a different one.
|
||||
|
||||
If the index is less than zero, this method does nothing.
|
||||
If the index is beyond the end of the array, the new object is added to the end of the array.
|
||||
|
||||
The object being added has its reference count increased, and if it's replacing
|
||||
another object, then that one has its reference count decreased, and may be deleted.
|
||||
|
||||
@param indexToChange the index whose value you want to change
|
||||
@param newObject the new value to set for this index.
|
||||
@see add, insert, remove
|
||||
*/
|
||||
void set (const int indexToChange,
|
||||
ObjectClass* const newObject)
|
||||
{
|
||||
if (indexToChange >= 0)
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
|
||||
if (newObject != nullptr)
|
||||
newObject->incReferenceCount();
|
||||
|
||||
if (indexToChange < numUsed)
|
||||
{
|
||||
if (ObjectClass* o = data.elements [indexToChange])
|
||||
releaseObject (o);
|
||||
|
||||
data.elements [indexToChange] = newObject;
|
||||
}
|
||||
else
|
||||
{
|
||||
data.ensureAllocatedSize (numUsed + 1);
|
||||
jassert (data.elements != nullptr);
|
||||
data.elements [numUsed++] = newObject;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Adds elements from another array to the end of this array.
|
||||
|
||||
@param arrayToAddFrom the array from which to copy the elements
|
||||
@param startIndex the first element of the other array to start copying from
|
||||
@param numElementsToAdd how many elements to add from the other array. If this
|
||||
value is negative or greater than the number of available elements,
|
||||
all available elements will be copied.
|
||||
@see add
|
||||
*/
|
||||
void addArray (const ReferenceCountedArray<ObjectClass, TypeOfCriticalSectionToUse>& arrayToAddFrom,
|
||||
int startIndex = 0,
|
||||
int numElementsToAdd = -1) noexcept
|
||||
{
|
||||
const ScopedLockType lock1 (arrayToAddFrom.getLock());
|
||||
|
||||
{
|
||||
const ScopedLockType lock2 (getLock());
|
||||
|
||||
if (startIndex < 0)
|
||||
{
|
||||
jassertfalse;
|
||||
startIndex = 0;
|
||||
}
|
||||
|
||||
if (numElementsToAdd < 0 || startIndex + numElementsToAdd > arrayToAddFrom.size())
|
||||
numElementsToAdd = arrayToAddFrom.size() - startIndex;
|
||||
|
||||
if (numElementsToAdd > 0)
|
||||
{
|
||||
data.ensureAllocatedSize (numUsed + numElementsToAdd);
|
||||
|
||||
while (--numElementsToAdd >= 0)
|
||||
add (arrayToAddFrom.getUnchecked (startIndex++));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Inserts a new object into the array assuming that the array is sorted.
|
||||
|
||||
This will use a comparator to find the position at which the new object
|
||||
should go. If the array isn't sorted, the behaviour of this
|
||||
method will be unpredictable.
|
||||
|
||||
@param comparator the comparator object to use to compare the elements - see the
|
||||
sort() method for details about this object's form
|
||||
@param newObject the new object to insert to the array
|
||||
@returns the index at which the new object was added
|
||||
@see add, sort
|
||||
*/
|
||||
template <class ElementComparator>
|
||||
int addSorted (ElementComparator& comparator, ObjectClass* newObject) noexcept
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
const int index = findInsertIndexInSortedArray (comparator, data.elements.getData(), newObject, 0, numUsed);
|
||||
insert (index, newObject);
|
||||
return index;
|
||||
}
|
||||
|
||||
/** Inserts or replaces an object in the array, assuming it is sorted.
|
||||
|
||||
This is similar to addSorted, but if a matching element already exists, then it will be
|
||||
replaced by the new one, rather than the new one being added as well.
|
||||
*/
|
||||
template <class ElementComparator>
|
||||
void addOrReplaceSorted (ElementComparator& comparator,
|
||||
ObjectClass* newObject) noexcept
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
const int index = findInsertIndexInSortedArray (comparator, data.elements.getData(), newObject, 0, numUsed);
|
||||
|
||||
if (index > 0 && comparator.compareElements (newObject, data.elements [index - 1]) == 0)
|
||||
set (index - 1, newObject); // replace an existing object that matches
|
||||
else
|
||||
insert (index, newObject); // no match, so insert the new one
|
||||
}
|
||||
|
||||
/** Finds the index of an object in the array, assuming that the array is sorted.
|
||||
|
||||
This will use a comparator to do a binary-chop to find the index of the given
|
||||
element, if it exists. If the array isn't sorted, the behaviour of this
|
||||
method will be unpredictable.
|
||||
|
||||
@param comparator the comparator to use to compare the elements - see the sort()
|
||||
method for details about the form this object should take
|
||||
@param objectToLookFor the object to search for
|
||||
@returns the index of the element, or -1 if it's not found
|
||||
@see addSorted, sort
|
||||
*/
|
||||
template <class ElementComparator>
|
||||
int indexOfSorted (ElementComparator& comparator,
|
||||
const ObjectClass* const objectToLookFor) const noexcept
|
||||
{
|
||||
(void) comparator;
|
||||
const ScopedLockType lock (getLock());
|
||||
int s = 0, e = numUsed;
|
||||
|
||||
while (s < e)
|
||||
{
|
||||
if (comparator.compareElements (objectToLookFor, data.elements [s]) == 0)
|
||||
return s;
|
||||
|
||||
const int halfway = (s + e) / 2;
|
||||
if (halfway == s)
|
||||
break;
|
||||
|
||||
if (comparator.compareElements (objectToLookFor, data.elements [halfway]) >= 0)
|
||||
s = halfway;
|
||||
else
|
||||
e = halfway;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Removes an object from the array.
|
||||
|
||||
This will remove the object at a given index and move back all the
|
||||
subsequent objects to close the gap.
|
||||
|
||||
If the index passed in is out-of-range, nothing will happen.
|
||||
|
||||
The object that is removed will have its reference count decreased,
|
||||
and may be deleted if not referenced from elsewhere.
|
||||
|
||||
@param indexToRemove the index of the element to remove
|
||||
@see removeObject, removeRange
|
||||
*/
|
||||
void remove (const int indexToRemove)
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
|
||||
if (isPositiveAndBelow (indexToRemove, numUsed))
|
||||
{
|
||||
ObjectClass** const e = data.elements + indexToRemove;
|
||||
|
||||
if (ObjectClass* o = *e)
|
||||
releaseObject (o);
|
||||
|
||||
--numUsed;
|
||||
const int numberToShift = numUsed - indexToRemove;
|
||||
|
||||
if (numberToShift > 0)
|
||||
memmove (e, e + 1, sizeof (ObjectClass*) * (size_t) numberToShift);
|
||||
|
||||
if ((numUsed << 1) < data.numAllocated)
|
||||
minimiseStorageOverheads();
|
||||
}
|
||||
}
|
||||
|
||||
/** Removes and returns an object from the array.
|
||||
|
||||
This will remove the object at a given index and return it, moving back all
|
||||
the subsequent objects to close the gap. If the index passed in is out-of-range,
|
||||
nothing will happen and a null pointer will be returned.
|
||||
|
||||
@param indexToRemove the index of the element to remove
|
||||
@see remove, removeObject, removeRange
|
||||
*/
|
||||
ObjectClassPtr removeAndReturn (const int indexToRemove)
|
||||
{
|
||||
ObjectClassPtr removedItem;
|
||||
const ScopedLockType lock (getLock());
|
||||
|
||||
if (isPositiveAndBelow (indexToRemove, numUsed))
|
||||
{
|
||||
ObjectClass** const e = data.elements + indexToRemove;
|
||||
|
||||
if (ObjectClass* o = *e)
|
||||
{
|
||||
removedItem = o;
|
||||
releaseObject (o);
|
||||
}
|
||||
|
||||
--numUsed;
|
||||
const int numberToShift = numUsed - indexToRemove;
|
||||
|
||||
if (numberToShift > 0)
|
||||
memmove (e, e + 1, sizeof (ObjectClass*) * (size_t) numberToShift);
|
||||
|
||||
if ((numUsed << 1) < data.numAllocated)
|
||||
minimiseStorageOverheads();
|
||||
}
|
||||
|
||||
return removedItem;
|
||||
}
|
||||
|
||||
/** Removes the first occurrence of a specified object from the array.
|
||||
|
||||
If the item isn't found, no action is taken. If it is found, it is
|
||||
removed and has its reference count decreased.
|
||||
|
||||
@param objectToRemove the object to try to remove
|
||||
@see remove, removeRange
|
||||
*/
|
||||
void removeObject (ObjectClass* const objectToRemove)
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
remove (indexOf (objectToRemove));
|
||||
}
|
||||
|
||||
/** Removes a range of objects from the array.
|
||||
|
||||
This will remove a set of objects, starting from the given index,
|
||||
and move any subsequent elements down to close the gap.
|
||||
|
||||
If the range extends beyond the bounds of the array, it will
|
||||
be safely clipped to the size of the array.
|
||||
|
||||
The objects that are removed will have their reference counts decreased,
|
||||
and may be deleted if not referenced from elsewhere.
|
||||
|
||||
@param startIndex the index of the first object to remove
|
||||
@param numberToRemove how many objects should be removed
|
||||
@see remove, removeObject
|
||||
*/
|
||||
void removeRange (const int startIndex,
|
||||
const int numberToRemove)
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
|
||||
const int start = jlimit (0, numUsed, startIndex);
|
||||
const int endIndex = jlimit (0, numUsed, startIndex + numberToRemove);
|
||||
|
||||
if (endIndex > start)
|
||||
{
|
||||
int i;
|
||||
for (i = start; i < endIndex; ++i)
|
||||
{
|
||||
if (ObjectClass* o = data.elements[i])
|
||||
{
|
||||
releaseObject (o);
|
||||
data.elements[i] = nullptr; // (in case one of the destructors accesses this array and hits a dangling pointer)
|
||||
}
|
||||
}
|
||||
|
||||
const int rangeSize = endIndex - start;
|
||||
ObjectClass** e = data.elements + start;
|
||||
i = numUsed - endIndex;
|
||||
numUsed -= rangeSize;
|
||||
|
||||
while (--i >= 0)
|
||||
{
|
||||
*e = e [rangeSize];
|
||||
++e;
|
||||
}
|
||||
|
||||
if ((numUsed << 1) < data.numAllocated)
|
||||
minimiseStorageOverheads();
|
||||
}
|
||||
}
|
||||
|
||||
/** Removes the last n objects from the array.
|
||||
|
||||
The objects that are removed will have their reference counts decreased,
|
||||
and may be deleted if not referenced from elsewhere.
|
||||
|
||||
@param howManyToRemove how many objects to remove from the end of the array
|
||||
@see remove, removeObject, removeRange
|
||||
*/
|
||||
void removeLast (int howManyToRemove = 1)
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
|
||||
if (howManyToRemove > numUsed)
|
||||
howManyToRemove = numUsed;
|
||||
|
||||
while (--howManyToRemove >= 0)
|
||||
remove (numUsed - 1);
|
||||
}
|
||||
|
||||
/** Swaps a pair of objects in the array.
|
||||
|
||||
If either of the indexes passed in is out-of-range, nothing will happen,
|
||||
otherwise the two objects at these positions will be exchanged.
|
||||
*/
|
||||
void swap (const int index1,
|
||||
const int index2) noexcept
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
|
||||
if (isPositiveAndBelow (index1, numUsed)
|
||||
&& isPositiveAndBelow (index2, numUsed))
|
||||
{
|
||||
std::swap (data.elements [index1],
|
||||
data.elements [index2]);
|
||||
}
|
||||
}
|
||||
|
||||
/** Moves one of the objects to a different position.
|
||||
|
||||
This will move the object to a specified index, shuffling along
|
||||
any intervening elements as required.
|
||||
|
||||
So for example, if you have the array { 0, 1, 2, 3, 4, 5 } then calling
|
||||
move (2, 4) would result in { 0, 1, 3, 4, 2, 5 }.
|
||||
|
||||
@param currentIndex the index of the object to be moved. If this isn't a
|
||||
valid index, then nothing will be done
|
||||
@param newIndex the index at which you'd like this object to end up. If this
|
||||
is less than zero, it will be moved to the end of the array
|
||||
*/
|
||||
void move (const int currentIndex,
|
||||
int newIndex) noexcept
|
||||
{
|
||||
if (currentIndex != newIndex)
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
|
||||
if (isPositiveAndBelow (currentIndex, numUsed))
|
||||
{
|
||||
if (! isPositiveAndBelow (newIndex, numUsed))
|
||||
newIndex = numUsed - 1;
|
||||
|
||||
ObjectClass* const value = data.elements [currentIndex];
|
||||
|
||||
if (newIndex > currentIndex)
|
||||
{
|
||||
memmove (data.elements + currentIndex,
|
||||
data.elements + currentIndex + 1,
|
||||
sizeof (ObjectClass*) * (size_t) (newIndex - currentIndex));
|
||||
}
|
||||
else
|
||||
{
|
||||
memmove (data.elements + newIndex + 1,
|
||||
data.elements + newIndex,
|
||||
sizeof (ObjectClass*) * (size_t) (currentIndex - newIndex));
|
||||
}
|
||||
|
||||
data.elements [newIndex] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** 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 OtherArrayType>
|
||||
void swapWith (OtherArrayType& otherArray) noexcept
|
||||
{
|
||||
const ScopedLockType lock1 (getLock());
|
||||
const typename OtherArrayType::ScopedLockType lock2 (otherArray.getLock());
|
||||
data.swapWith (otherArray.data);
|
||||
std::swap (numUsed, otherArray.numUsed);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Compares this array to another one.
|
||||
|
||||
@returns true only if the other array contains the same objects in the same order
|
||||
*/
|
||||
bool operator== (const ReferenceCountedArray& other) const noexcept
|
||||
{
|
||||
const ScopedLockType lock2 (other.getLock());
|
||||
const ScopedLockType lock1 (getLock());
|
||||
|
||||
if (numUsed != other.numUsed)
|
||||
return false;
|
||||
|
||||
for (int i = numUsed; --i >= 0;)
|
||||
if (data.elements [i] != other.data.elements [i])
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Compares this array to another one.
|
||||
|
||||
@see operator==
|
||||
*/
|
||||
bool operator!= (const ReferenceCountedArray<ObjectClass, TypeOfCriticalSectionToUse>& other) const noexcept
|
||||
{
|
||||
return ! operator== (other);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Sorts the elements in the array.
|
||||
|
||||
This will use a comparator object to sort the elements into order. The object
|
||||
passed must have a method of the form:
|
||||
@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 the comparator to use for comparing elements.
|
||||
@param retainOrderOfEquivalentItems if this is true, then items
|
||||
which the comparator says are equivalent will be
|
||||
kept in the order in which they currently appear
|
||||
in the array. This is slower to perform, but may
|
||||
be important in some cases. If it's false, a faster
|
||||
algorithm is used, but equivalent elements may be
|
||||
rearranged.
|
||||
|
||||
@see sortArray
|
||||
*/
|
||||
template <class ElementComparator>
|
||||
void sort (ElementComparator& comparator,
|
||||
const bool retainOrderOfEquivalentItems = false) const noexcept
|
||||
{
|
||||
(void) comparator; // if you pass in an object with a static compareElements() method, this
|
||||
// avoids getting warning messages about the parameter being unused
|
||||
|
||||
const ScopedLockType lock (getLock());
|
||||
sortArray (comparator, data.elements.getData(), 0, size() - 1, retainOrderOfEquivalentItems);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Reduces the amount of storage being used by the array.
|
||||
|
||||
Arrays 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
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
data.shrinkToNoMoreThan (numUsed);
|
||||
}
|
||||
|
||||
/** Increases the array's internal storage to hold a minimum number of elements.
|
||||
|
||||
Calling this before adding a large known number of elements means that
|
||||
the array 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)
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
data.ensureAllocatedSize (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; }
|
||||
|
||||
/** Returns the type of scoped lock to use for locking this array */
|
||||
typedef typename TypeOfCriticalSectionToUse::ScopedLockType ScopedLockType;
|
||||
|
||||
|
||||
//==============================================================================
|
||||
#ifndef DOXYGEN
|
||||
// Note that the swapWithArray method has been replaced by a more flexible templated version,
|
||||
// and renamed "swapWith" to be more consistent with the names used in other classes.
|
||||
JUCE_DEPRECATED_WITH_BODY (void swapWithArray (ReferenceCountedArray& other) noexcept, { swapWith (other); })
|
||||
#endif
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
ArrayAllocationBase <ObjectClass*, TypeOfCriticalSectionToUse> data;
|
||||
int numUsed;
|
||||
|
||||
static void releaseObject (ObjectClass* o)
|
||||
{
|
||||
if (o->decReferenceCountWithoutDeleting())
|
||||
ContainerDeletePolicy<ObjectClass>::destroy (o);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
#endif // JUCE_REFERENCECOUNTEDARRAY_H_INCLUDED
|
||||
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the juce_core module of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
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.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
|
||||
TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
|
||||
NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
|
||||
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
|
||||
IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
|
||||
CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
NOTE! This permissive ISC license applies ONLY to files within the juce_core module!
|
||||
All other JUCE modules are covered by a dual GPL/commercial license, so if you are
|
||||
using any other modules, be sure to check that you also comply with their license.
|
||||
|
||||
For more details, visit www.juce.com
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef JUCE_SCOPEDVALUESETTER_H_INCLUDED
|
||||
#define JUCE_SCOPEDVALUESETTER_H_INCLUDED
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
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
|
||||
|
||||
*/
|
||||
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)
|
||||
};
|
||||
|
||||
|
||||
#endif // JUCE_SCOPEDVALUESETTER_H_INCLUDED
|
||||
@@ -0,0 +1,494 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the juce_core module of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
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.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
|
||||
TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
|
||||
NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
|
||||
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
|
||||
IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
|
||||
CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
NOTE! This permissive ISC license applies ONLY to files within the juce_core module!
|
||||
All other JUCE modules are covered by a dual GPL/commercial license, so if you are
|
||||
using any other modules, be sure to check that you also comply with their license.
|
||||
|
||||
For more details, visit www.juce.com
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef JUCE_SORTEDSET_H_INCLUDED
|
||||
#define JUCE_SORTEDSET_H_INCLUDED
|
||||
|
||||
#if JUCE_MSVC
|
||||
#pragma warning (push)
|
||||
#pragma warning (disable: 4512)
|
||||
#endif
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
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
|
||||
*/
|
||||
template <class ElementType, class TypeOfCriticalSectionToUse = DummyCriticalSection>
|
||||
class SortedSet
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates an empty set. */
|
||||
SortedSet() noexcept
|
||||
{
|
||||
}
|
||||
|
||||
/** Creates a copy of another set.
|
||||
@param other the set to copy
|
||||
*/
|
||||
SortedSet (const SortedSet& other)
|
||||
: data (other.data)
|
||||
{
|
||||
}
|
||||
|
||||
/** Destructor. */
|
||||
~SortedSet() noexcept
|
||||
{
|
||||
}
|
||||
|
||||
/** Copies another set over this one.
|
||||
@param other the set to copy
|
||||
*/
|
||||
SortedSet& operator= (const SortedSet& other) noexcept
|
||||
{
|
||||
data = other.data;
|
||||
return *this;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** 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 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) 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 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 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;
|
||||
|
||||
const int 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)
|
||||
{
|
||||
ElementType& elem = data.getReference (s);
|
||||
if (newElement == elem)
|
||||
{
|
||||
elem = newElement; // force an update in case operator== permits differences.
|
||||
return false;
|
||||
}
|
||||
|
||||
const int halfway = (s + e) / 2;
|
||||
const 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.remove (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.size() > 0)
|
||||
{
|
||||
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.size() <= 0)
|
||||
{
|
||||
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 */
|
||||
typedef typename TypeOfCriticalSectionToUse::ScopedLockType ScopedLockType;
|
||||
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
Array<ElementType, TypeOfCriticalSectionToUse> data;
|
||||
};
|
||||
|
||||
#if JUCE_MSVC
|
||||
#pragma warning (pop)
|
||||
#endif
|
||||
|
||||
#endif // JUCE_SORTEDSET_H_INCLUDED
|
||||
@@ -0,0 +1,298 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the juce_core module of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
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.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
|
||||
TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
|
||||
NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
|
||||
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
|
||||
IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
|
||||
CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
NOTE! This permissive ISC license applies ONLY to files within the juce_core module!
|
||||
All other JUCE modules are covered by a dual GPL/commercial license, so if you are
|
||||
using any other modules, be sure to check that you also comply with their license.
|
||||
|
||||
For more details, visit www.juce.com
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef JUCE_SPARSESET_H_INCLUDED
|
||||
#define JUCE_SPARSESET_H_INCLUDED
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
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.
|
||||
*/
|
||||
template <class Type>
|
||||
class SparseSet
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates a new empty set. */
|
||||
SparseSet()
|
||||
{
|
||||
}
|
||||
|
||||
/** Creates a copy of another SparseSet. */
|
||||
SparseSet (const SparseSet<Type>& other)
|
||||
: values (other.values)
|
||||
{
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Clears the set. */
|
||||
void clear()
|
||||
{
|
||||
values.clear();
|
||||
}
|
||||
|
||||
/** Checks whether the set is empty.
|
||||
|
||||
This is much quicker than using (size() == 0).
|
||||
*/
|
||||
bool isEmpty() const noexcept
|
||||
{
|
||||
return values.size() == 0;
|
||||
}
|
||||
|
||||
/** 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
|
||||
{
|
||||
Type total (0);
|
||||
|
||||
for (int i = 0; i < values.size(); i += 2)
|
||||
total += values.getUnchecked (i + 1) - values.getUnchecked (i);
|
||||
|
||||
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
|
||||
{
|
||||
for (int i = 0; i < values.size(); i += 2)
|
||||
{
|
||||
const Type start (values.getUnchecked (i));
|
||||
const Type len (values.getUnchecked (i + 1) - start);
|
||||
|
||||
if (index < len)
|
||||
return start + index;
|
||||
|
||||
index -= len;
|
||||
}
|
||||
|
||||
return Type();
|
||||
}
|
||||
|
||||
/** Checks whether a particular value is in the set. */
|
||||
bool contains (const Type valueToLookFor) const
|
||||
{
|
||||
for (int i = 0; i < values.size(); ++i)
|
||||
if (valueToLookFor < values.getUnchecked(i))
|
||||
return (i & 1) != 0;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the number of contiguous blocks of values.
|
||||
@see getRange
|
||||
*/
|
||||
int getNumRanges() const noexcept
|
||||
{
|
||||
return values.size() >> 1;
|
||||
}
|
||||
|
||||
/** 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
|
||||
*/
|
||||
const Range<Type> getRange (const int rangeIndex) const
|
||||
{
|
||||
if (isPositiveAndBelow (rangeIndex, getNumRanges()))
|
||||
return Range<Type> (values.getUnchecked (rangeIndex << 1),
|
||||
values.getUnchecked ((rangeIndex << 1) + 1));
|
||||
|
||||
return Range<Type>();
|
||||
}
|
||||
|
||||
/** Returns the range between the lowest and highest values in the set.
|
||||
@see getRange
|
||||
*/
|
||||
Range<Type> getTotalRange() const
|
||||
{
|
||||
if (values.size() > 0)
|
||||
{
|
||||
jassert ((values.size() & 1) == 0);
|
||||
return Range<Type> (values.getUnchecked (0),
|
||||
values.getUnchecked (values.size() - 1));
|
||||
}
|
||||
|
||||
return Range<Type>();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** 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 (const Range<Type> range)
|
||||
{
|
||||
jassert (range.getLength() >= 0);
|
||||
if (range.getLength() > 0)
|
||||
{
|
||||
removeRange (range);
|
||||
|
||||
values.addUsingDefaultSort (range.getStart());
|
||||
values.addUsingDefaultSort (range.getEnd());
|
||||
|
||||
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 (const Range<Type> rangeToRemove)
|
||||
{
|
||||
jassert (rangeToRemove.getLength() >= 0);
|
||||
|
||||
if (rangeToRemove.getLength() > 0
|
||||
&& values.size() > 0
|
||||
&& rangeToRemove.getStart() < values.getUnchecked (values.size() - 1)
|
||||
&& values.getUnchecked(0) < rangeToRemove.getEnd())
|
||||
{
|
||||
const bool onAtStart = contains (rangeToRemove.getStart() - 1);
|
||||
const Type lastValue (jmin (rangeToRemove.getEnd(), values.getLast()));
|
||||
const bool onAtEnd = contains (lastValue);
|
||||
|
||||
for (int i = values.size(); --i >= 0;)
|
||||
{
|
||||
if (values.getUnchecked(i) <= lastValue)
|
||||
{
|
||||
while (values.getUnchecked(i) >= rangeToRemove.getStart())
|
||||
{
|
||||
values.remove (i);
|
||||
|
||||
if (--i < 0)
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (onAtStart) values.addUsingDefaultSort (rangeToRemove.getStart());
|
||||
if (onAtEnd) values.addUsingDefaultSort (lastValue);
|
||||
|
||||
simplify();
|
||||
}
|
||||
}
|
||||
|
||||
/** Does an XOR of the values in a given range. */
|
||||
void invertRange (const Range<Type> range)
|
||||
{
|
||||
SparseSet newItems;
|
||||
newItems.addRange (range);
|
||||
|
||||
for (int i = getNumRanges(); --i >= 0;)
|
||||
newItems.removeRange (getRange (i));
|
||||
|
||||
removeRange (range);
|
||||
|
||||
for (int i = newItems.getNumRanges(); --i >= 0;)
|
||||
addRange (newItems.getRange(i));
|
||||
}
|
||||
|
||||
/** Checks whether any part of a given range overlaps any part of this set. */
|
||||
bool overlapsRange (const Range<Type> range)
|
||||
{
|
||||
if (range.getLength() > 0)
|
||||
{
|
||||
for (int i = getNumRanges(); --i >= 0;)
|
||||
{
|
||||
if (values.getUnchecked ((i << 1) + 1) <= range.getStart())
|
||||
return false;
|
||||
|
||||
if (values.getUnchecked (i << 1) < range.getEnd())
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Checks whether the whole of a given range is contained within this one. */
|
||||
bool containsRange (const Range<Type> range)
|
||||
{
|
||||
if (range.getLength() > 0)
|
||||
{
|
||||
for (int i = getNumRanges(); --i >= 0;)
|
||||
{
|
||||
if (values.getUnchecked ((i << 1) + 1) <= range.getStart())
|
||||
return false;
|
||||
|
||||
if (values.getUnchecked (i << 1) <= range.getStart()
|
||||
&& range.getEnd() <= values.getUnchecked ((i << 1) + 1))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
bool operator== (const SparseSet<Type>& other) noexcept
|
||||
{
|
||||
return values == other.values;
|
||||
}
|
||||
|
||||
bool operator!= (const SparseSet<Type>& other) noexcept
|
||||
{
|
||||
return values != other.values;
|
||||
}
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
// alternating start/end values of ranges of values that are present.
|
||||
Array<Type, DummyCriticalSection> values;
|
||||
|
||||
void simplify()
|
||||
{
|
||||
jassert ((values.size() & 1) == 0);
|
||||
|
||||
for (int i = values.size(); --i > 0;)
|
||||
if (values.getUnchecked(i) == values.getUnchecked (i - 1))
|
||||
values.removeRange (--i, 2);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif // JUCE_SPARSESET_H_INCLUDED
|
||||
@@ -0,0 +1,780 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the juce_core module of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
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.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
|
||||
TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
|
||||
NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
|
||||
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
|
||||
IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
|
||||
CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
NOTE! This permissive ISC license applies ONLY to files within the juce_core module!
|
||||
All other JUCE modules are covered by a dual GPL/commercial license, so if you are
|
||||
using any other modules, be sure to check that you also comply with their license.
|
||||
|
||||
For more details, visit www.juce.com
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
enum VariantStreamMarkers
|
||||
{
|
||||
varMarker_Int = 1,
|
||||
varMarker_BoolTrue = 2,
|
||||
varMarker_BoolFalse = 3,
|
||||
varMarker_Double = 4,
|
||||
varMarker_String = 5,
|
||||
varMarker_Int64 = 6,
|
||||
varMarker_Array = 7,
|
||||
varMarker_Binary = 8,
|
||||
varMarker_Undefined = 9
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class var::VariantType
|
||||
{
|
||||
public:
|
||||
VariantType() noexcept {}
|
||||
virtual ~VariantType() noexcept {}
|
||||
|
||||
virtual int toInt (const ValueUnion&) const noexcept { return 0; }
|
||||
virtual int64 toInt64 (const ValueUnion&) const noexcept { return 0; }
|
||||
virtual double toDouble (const ValueUnion&) const noexcept { return 0; }
|
||||
virtual String toString (const ValueUnion&) const { return String::empty; }
|
||||
virtual bool toBool (const ValueUnion&) const noexcept { return false; }
|
||||
virtual ReferenceCountedObject* toObject (const ValueUnion&) const noexcept { return nullptr; }
|
||||
virtual Array<var>* toArray (const ValueUnion&) const noexcept { return nullptr; }
|
||||
virtual MemoryBlock* toBinary (const ValueUnion&) const noexcept { return nullptr; }
|
||||
virtual var clone (const var& original) const { return original; }
|
||||
|
||||
virtual bool isVoid() const noexcept { return false; }
|
||||
virtual bool isUndefined() const noexcept { return false; }
|
||||
virtual bool isInt() const noexcept { return false; }
|
||||
virtual bool isInt64() const noexcept { return false; }
|
||||
virtual bool isBool() const noexcept { return false; }
|
||||
virtual bool isDouble() const noexcept { return false; }
|
||||
virtual bool isString() const noexcept { return false; }
|
||||
virtual bool isObject() const noexcept { return false; }
|
||||
virtual bool isArray() const noexcept { return false; }
|
||||
virtual bool isBinary() const noexcept { return false; }
|
||||
virtual bool isMethod() const noexcept { return false; }
|
||||
|
||||
virtual void cleanUp (ValueUnion&) const noexcept {}
|
||||
virtual void createCopy (ValueUnion& dest, const ValueUnion& source) const { dest = source; }
|
||||
virtual bool equals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) const noexcept = 0;
|
||||
virtual void writeToStream (const ValueUnion& data, OutputStream& output) const = 0;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class var::VariantType_Void : public var::VariantType
|
||||
{
|
||||
public:
|
||||
VariantType_Void() noexcept {}
|
||||
static const VariantType_Void instance;
|
||||
|
||||
bool isVoid() const noexcept override { return true; }
|
||||
bool equals (const ValueUnion&, const ValueUnion&, const VariantType& otherType) const noexcept override { return otherType.isVoid() || otherType.isUndefined(); }
|
||||
void writeToStream (const ValueUnion&, OutputStream& output) const override { output.writeCompressedInt (0); }
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class var::VariantType_Undefined : public var::VariantType
|
||||
{
|
||||
public:
|
||||
VariantType_Undefined() noexcept {}
|
||||
static const VariantType_Undefined instance;
|
||||
|
||||
bool isUndefined() const noexcept override { return true; }
|
||||
String toString (const ValueUnion&) const override { return "undefined"; }
|
||||
bool equals (const ValueUnion&, const ValueUnion&, const VariantType& otherType) const noexcept override { return otherType.isVoid() || otherType.isUndefined(); }
|
||||
|
||||
void writeToStream (const ValueUnion&, OutputStream& output) const override
|
||||
{
|
||||
output.writeCompressedInt (1);
|
||||
output.writeByte (varMarker_Undefined);
|
||||
}
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class var::VariantType_Int : public var::VariantType
|
||||
{
|
||||
public:
|
||||
VariantType_Int() noexcept {}
|
||||
static const VariantType_Int instance;
|
||||
|
||||
int toInt (const ValueUnion& data) const noexcept override { return data.intValue; };
|
||||
int64 toInt64 (const ValueUnion& data) const noexcept override { return (int64) data.intValue; };
|
||||
double toDouble (const ValueUnion& data) const noexcept override { return (double) data.intValue; }
|
||||
String toString (const ValueUnion& data) const override { return String (data.intValue); }
|
||||
bool toBool (const ValueUnion& data) const noexcept override { return data.intValue != 0; }
|
||||
bool isInt() const noexcept override { return true; }
|
||||
|
||||
bool equals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) const noexcept override
|
||||
{
|
||||
if (otherType.isDouble() || otherType.isInt64() || otherType.isString())
|
||||
return otherType.equals (otherData, data, *this);
|
||||
|
||||
return otherType.toInt (otherData) == data.intValue;
|
||||
}
|
||||
|
||||
void writeToStream (const ValueUnion& data, OutputStream& output) const override
|
||||
{
|
||||
output.writeCompressedInt (5);
|
||||
output.writeByte (varMarker_Int);
|
||||
output.writeInt (data.intValue);
|
||||
}
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class var::VariantType_Int64 : public var::VariantType
|
||||
{
|
||||
public:
|
||||
VariantType_Int64() noexcept {}
|
||||
static const VariantType_Int64 instance;
|
||||
|
||||
int toInt (const ValueUnion& data) const noexcept override { return (int) data.int64Value; };
|
||||
int64 toInt64 (const ValueUnion& data) const noexcept override { return data.int64Value; };
|
||||
double toDouble (const ValueUnion& data) const noexcept override { return (double) data.int64Value; }
|
||||
String toString (const ValueUnion& data) const override { return String (data.int64Value); }
|
||||
bool toBool (const ValueUnion& data) const noexcept override { return data.int64Value != 0; }
|
||||
bool isInt64() const noexcept override { return true; }
|
||||
|
||||
bool equals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) const noexcept override
|
||||
{
|
||||
if (otherType.isDouble() || otherType.isString())
|
||||
return otherType.equals (otherData, data, *this);
|
||||
|
||||
return otherType.toInt64 (otherData) == data.int64Value;
|
||||
}
|
||||
|
||||
void writeToStream (const ValueUnion& data, OutputStream& output) const override
|
||||
{
|
||||
output.writeCompressedInt (9);
|
||||
output.writeByte (varMarker_Int64);
|
||||
output.writeInt64 (data.int64Value);
|
||||
}
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class var::VariantType_Double : public var::VariantType
|
||||
{
|
||||
public:
|
||||
VariantType_Double() noexcept {}
|
||||
static const VariantType_Double instance;
|
||||
|
||||
int toInt (const ValueUnion& data) const noexcept override { return (int) data.doubleValue; };
|
||||
int64 toInt64 (const ValueUnion& data) const noexcept override { return (int64) data.doubleValue; };
|
||||
double toDouble (const ValueUnion& data) const noexcept override { return data.doubleValue; }
|
||||
String toString (const ValueUnion& data) const override { return String (data.doubleValue); }
|
||||
bool toBool (const ValueUnion& data) const noexcept override { return data.doubleValue != 0; }
|
||||
bool isDouble() const noexcept override { return true; }
|
||||
|
||||
bool equals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) const noexcept override
|
||||
{
|
||||
return std::abs (otherType.toDouble (otherData) - data.doubleValue) < std::numeric_limits<double>::epsilon();
|
||||
}
|
||||
|
||||
void writeToStream (const ValueUnion& data, OutputStream& output) const override
|
||||
{
|
||||
output.writeCompressedInt (9);
|
||||
output.writeByte (varMarker_Double);
|
||||
output.writeDouble (data.doubleValue);
|
||||
}
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class var::VariantType_Bool : public var::VariantType
|
||||
{
|
||||
public:
|
||||
VariantType_Bool() noexcept {}
|
||||
static const VariantType_Bool instance;
|
||||
|
||||
int toInt (const ValueUnion& data) const noexcept override { return data.boolValue ? 1 : 0; };
|
||||
int64 toInt64 (const ValueUnion& data) const noexcept override { return data.boolValue ? 1 : 0; };
|
||||
double toDouble (const ValueUnion& data) const noexcept override { return data.boolValue ? 1.0 : 0.0; }
|
||||
String toString (const ValueUnion& data) const override { return String::charToString (data.boolValue ? (juce_wchar) '1' : (juce_wchar) '0'); }
|
||||
bool toBool (const ValueUnion& data) const noexcept override { return data.boolValue; }
|
||||
bool isBool() const noexcept override { return true; }
|
||||
|
||||
bool equals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) const noexcept override
|
||||
{
|
||||
return otherType.toBool (otherData) == data.boolValue;
|
||||
}
|
||||
|
||||
void writeToStream (const ValueUnion& data, OutputStream& output) const override
|
||||
{
|
||||
output.writeCompressedInt (1);
|
||||
output.writeByte (data.boolValue ? (char) varMarker_BoolTrue : (char) varMarker_BoolFalse);
|
||||
}
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class var::VariantType_String : public var::VariantType
|
||||
{
|
||||
public:
|
||||
VariantType_String() noexcept {}
|
||||
static const VariantType_String instance;
|
||||
|
||||
void cleanUp (ValueUnion& data) const noexcept override { getString (data)-> ~String(); }
|
||||
void createCopy (ValueUnion& dest, const ValueUnion& source) const override { new (dest.stringValue) String (*getString (source)); }
|
||||
|
||||
bool isString() const noexcept override { return true; }
|
||||
int toInt (const ValueUnion& data) const noexcept override { return getString (data)->getIntValue(); };
|
||||
int64 toInt64 (const ValueUnion& data) const noexcept override { return getString (data)->getLargeIntValue(); };
|
||||
double toDouble (const ValueUnion& data) const noexcept override { return getString (data)->getDoubleValue(); }
|
||||
String toString (const ValueUnion& data) const override { return *getString (data); }
|
||||
bool toBool (const ValueUnion& data) const noexcept override { return getString (data)->getIntValue() != 0
|
||||
|| getString (data)->trim().equalsIgnoreCase ("true")
|
||||
|| getString (data)->trim().equalsIgnoreCase ("yes"); }
|
||||
|
||||
bool equals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) const noexcept override
|
||||
{
|
||||
return otherType.toString (otherData) == *getString (data);
|
||||
}
|
||||
|
||||
void writeToStream (const ValueUnion& data, OutputStream& output) const override
|
||||
{
|
||||
const String* const s = getString (data);
|
||||
const size_t len = s->getNumBytesAsUTF8() + 1;
|
||||
HeapBlock<char> temp (len);
|
||||
s->copyToUTF8 (temp, len);
|
||||
output.writeCompressedInt ((int) (len + 1));
|
||||
output.writeByte (varMarker_String);
|
||||
output.write (temp, len);
|
||||
}
|
||||
|
||||
private:
|
||||
static inline const String* getString (const ValueUnion& data) noexcept { return reinterpret_cast <const String*> (data.stringValue); }
|
||||
static inline String* getString (ValueUnion& data) noexcept { return reinterpret_cast <String*> (data.stringValue); }
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class var::VariantType_Object : public var::VariantType
|
||||
{
|
||||
public:
|
||||
VariantType_Object() noexcept {}
|
||||
static const VariantType_Object instance;
|
||||
|
||||
void cleanUp (ValueUnion& data) const noexcept override { if (data.objectValue != nullptr) data.objectValue->decReferenceCount(); }
|
||||
|
||||
void createCopy (ValueUnion& dest, const ValueUnion& source) const override
|
||||
{
|
||||
dest.objectValue = source.objectValue;
|
||||
if (dest.objectValue != nullptr)
|
||||
dest.objectValue->incReferenceCount();
|
||||
}
|
||||
|
||||
String toString (const ValueUnion& data) const override { return "Object 0x" + String::toHexString ((int) (pointer_sized_int) data.objectValue); }
|
||||
bool toBool (const ValueUnion& data) const noexcept override { return data.objectValue != 0; }
|
||||
ReferenceCountedObject* toObject (const ValueUnion& data) const noexcept override { return data.objectValue; }
|
||||
bool isObject() const noexcept override { return true; }
|
||||
|
||||
bool equals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) const noexcept override
|
||||
{
|
||||
return otherType.toObject (otherData) == data.objectValue;
|
||||
}
|
||||
|
||||
var clone (const var& original) const override
|
||||
{
|
||||
if (DynamicObject* d = original.getDynamicObject())
|
||||
return d->clone().get();
|
||||
|
||||
jassertfalse; // can only clone DynamicObjects!
|
||||
return var();
|
||||
}
|
||||
|
||||
void writeToStream (const ValueUnion&, OutputStream& output) const override
|
||||
{
|
||||
jassertfalse; // Can't write an object to a stream!
|
||||
output.writeCompressedInt (0);
|
||||
}
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class var::VariantType_Array : public var::VariantType_Object
|
||||
{
|
||||
public:
|
||||
VariantType_Array() noexcept {}
|
||||
static const VariantType_Array instance;
|
||||
|
||||
String toString (const ValueUnion&) const override { return "[Array]"; }
|
||||
ReferenceCountedObject* toObject (const ValueUnion&) const noexcept override { return nullptr; }
|
||||
bool isArray() const noexcept override { return true; }
|
||||
|
||||
Array<var>* toArray (const ValueUnion& data) const noexcept override
|
||||
{
|
||||
if (RefCountedArray* a = dynamic_cast<RefCountedArray*> (data.objectValue))
|
||||
return &(a->array);
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool equals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) const noexcept override
|
||||
{
|
||||
const Array<var>* const thisArray = toArray (data);
|
||||
const Array<var>* const otherArray = otherType.toArray (otherData);
|
||||
return thisArray == otherArray || (thisArray != nullptr && otherArray != nullptr && *otherArray == *thisArray);
|
||||
}
|
||||
|
||||
var clone (const var& original) const override
|
||||
{
|
||||
Array<var> arrayCopy;
|
||||
|
||||
if (const Array<var>* array = toArray (original.value))
|
||||
for (int i = 0; i < array->size(); ++i)
|
||||
arrayCopy.add (array->getReference(i).clone());
|
||||
|
||||
return var (arrayCopy);
|
||||
}
|
||||
|
||||
void writeToStream (const ValueUnion& data, OutputStream& output) const override
|
||||
{
|
||||
if (const Array<var>* array = toArray (data))
|
||||
{
|
||||
MemoryOutputStream buffer (512);
|
||||
const int numItems = array->size();
|
||||
buffer.writeCompressedInt (numItems);
|
||||
|
||||
for (int i = 0; i < numItems; ++i)
|
||||
array->getReference(i).writeToStream (buffer);
|
||||
|
||||
output.writeCompressedInt (1 + (int) buffer.getDataSize());
|
||||
output.writeByte (varMarker_Array);
|
||||
output << buffer;
|
||||
}
|
||||
}
|
||||
|
||||
struct RefCountedArray : public ReferenceCountedObject
|
||||
{
|
||||
RefCountedArray (const Array<var>& a) : array (a) { incReferenceCount(); }
|
||||
#if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS
|
||||
RefCountedArray (Array<var>&& a) : array (static_cast<Array<var>&&> (a)) { incReferenceCount(); }
|
||||
#endif
|
||||
Array<var> array;
|
||||
};
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class var::VariantType_Binary : public var::VariantType
|
||||
{
|
||||
public:
|
||||
VariantType_Binary() noexcept {}
|
||||
|
||||
static const VariantType_Binary instance;
|
||||
|
||||
void cleanUp (ValueUnion& data) const noexcept override { delete data.binaryValue; }
|
||||
void createCopy (ValueUnion& dest, const ValueUnion& source) const override { dest.binaryValue = new MemoryBlock (*source.binaryValue); }
|
||||
|
||||
String toString (const ValueUnion& data) const override { return data.binaryValue->toBase64Encoding(); }
|
||||
bool isBinary() const noexcept override { return true; }
|
||||
MemoryBlock* toBinary (const ValueUnion& data) const noexcept override { return data.binaryValue; }
|
||||
|
||||
bool equals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) const noexcept override
|
||||
{
|
||||
const MemoryBlock* const otherBlock = otherType.toBinary (otherData);
|
||||
return otherBlock != nullptr && *otherBlock == *data.binaryValue;
|
||||
}
|
||||
|
||||
void writeToStream (const ValueUnion& data, OutputStream& output) const override
|
||||
{
|
||||
output.writeCompressedInt (1 + (int) data.binaryValue->getSize());
|
||||
output.writeByte (varMarker_Binary);
|
||||
output << *data.binaryValue;
|
||||
}
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class var::VariantType_Method : public var::VariantType
|
||||
{
|
||||
public:
|
||||
VariantType_Method() noexcept {}
|
||||
static const VariantType_Method instance;
|
||||
|
||||
String toString (const ValueUnion&) const override { return "Method"; }
|
||||
bool toBool (const ValueUnion& data) const noexcept override { return data.methodValue != nullptr; }
|
||||
bool isMethod() const noexcept override { return true; }
|
||||
|
||||
bool equals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) const noexcept override
|
||||
{
|
||||
return otherType.isMethod() && otherData.methodValue == data.methodValue;
|
||||
}
|
||||
|
||||
void writeToStream (const ValueUnion&, OutputStream& output) const override
|
||||
{
|
||||
jassertfalse; // Can't write a method to a stream!
|
||||
output.writeCompressedInt (0);
|
||||
}
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
const var::VariantType_Void var::VariantType_Void::instance;
|
||||
const var::VariantType_Undefined var::VariantType_Undefined::instance;
|
||||
const var::VariantType_Int var::VariantType_Int::instance;
|
||||
const var::VariantType_Int64 var::VariantType_Int64::instance;
|
||||
const var::VariantType_Bool var::VariantType_Bool::instance;
|
||||
const var::VariantType_Double var::VariantType_Double::instance;
|
||||
const var::VariantType_String var::VariantType_String::instance;
|
||||
const var::VariantType_Object var::VariantType_Object::instance;
|
||||
const var::VariantType_Array var::VariantType_Array::instance;
|
||||
const var::VariantType_Binary var::VariantType_Binary::instance;
|
||||
const var::VariantType_Method var::VariantType_Method::instance;
|
||||
|
||||
|
||||
//==============================================================================
|
||||
var::var() noexcept : type (&VariantType_Void::instance) {}
|
||||
var::var (const VariantType& t) noexcept : type (&t) {}
|
||||
var::~var() noexcept { type->cleanUp (value); }
|
||||
|
||||
const var var::null;
|
||||
|
||||
//==============================================================================
|
||||
var::var (const var& valueToCopy) : type (valueToCopy.type)
|
||||
{
|
||||
type->createCopy (value, valueToCopy.value);
|
||||
}
|
||||
|
||||
var::var (const int v) noexcept : type (&VariantType_Int::instance) { value.intValue = v; }
|
||||
var::var (const int64 v) noexcept : type (&VariantType_Int64::instance) { value.int64Value = v; }
|
||||
var::var (const bool v) noexcept : type (&VariantType_Bool::instance) { value.boolValue = v; }
|
||||
var::var (const double v) noexcept : type (&VariantType_Double::instance) { value.doubleValue = v; }
|
||||
var::var (NativeFunction m) noexcept : type (&VariantType_Method::instance) { value.methodValue = m; }
|
||||
var::var (const Array<var>& v) : type (&VariantType_Array::instance) { value.objectValue = new VariantType_Array::RefCountedArray(v); }
|
||||
var::var (const String& v) : type (&VariantType_String::instance) { new (value.stringValue) String (v); }
|
||||
var::var (const char* const v) : type (&VariantType_String::instance) { new (value.stringValue) String (v); }
|
||||
var::var (const wchar_t* const v) : type (&VariantType_String::instance) { new (value.stringValue) String (v); }
|
||||
var::var (const void* v, size_t sz) : type (&VariantType_Binary::instance) { value.binaryValue = new MemoryBlock (v, sz); }
|
||||
var::var (const MemoryBlock& v) : type (&VariantType_Binary::instance) { value.binaryValue = new MemoryBlock (v); }
|
||||
|
||||
var::var (ReferenceCountedObject* const object) : type (&VariantType_Object::instance)
|
||||
{
|
||||
value.objectValue = object;
|
||||
|
||||
if (object != nullptr)
|
||||
object->incReferenceCount();
|
||||
}
|
||||
|
||||
var var::undefined() noexcept { return var (VariantType_Undefined::instance); }
|
||||
|
||||
//==============================================================================
|
||||
bool var::isVoid() const noexcept { return type->isVoid(); }
|
||||
bool var::isUndefined() const noexcept { return type->isUndefined(); }
|
||||
bool var::isInt() const noexcept { return type->isInt(); }
|
||||
bool var::isInt64() const noexcept { return type->isInt64(); }
|
||||
bool var::isBool() const noexcept { return type->isBool(); }
|
||||
bool var::isDouble() const noexcept { return type->isDouble(); }
|
||||
bool var::isString() const noexcept { return type->isString(); }
|
||||
bool var::isObject() const noexcept { return type->isObject(); }
|
||||
bool var::isArray() const noexcept { return type->isArray(); }
|
||||
bool var::isBinaryData() const noexcept { return type->isBinary(); }
|
||||
bool var::isMethod() const noexcept { return type->isMethod(); }
|
||||
|
||||
var::operator int() const noexcept { return type->toInt (value); }
|
||||
var::operator int64() const noexcept { return type->toInt64 (value); }
|
||||
var::operator bool() const noexcept { return type->toBool (value); }
|
||||
var::operator float() const noexcept { return (float) type->toDouble (value); }
|
||||
var::operator double() const noexcept { return type->toDouble (value); }
|
||||
String var::toString() const { return type->toString (value); }
|
||||
var::operator String() const { return type->toString (value); }
|
||||
ReferenceCountedObject* var::getObject() const noexcept { return type->toObject (value); }
|
||||
Array<var>* var::getArray() const noexcept { return type->toArray (value); }
|
||||
MemoryBlock* var::getBinaryData() const noexcept { return type->toBinary (value); }
|
||||
DynamicObject* var::getDynamicObject() const noexcept { return dynamic_cast <DynamicObject*> (getObject()); }
|
||||
|
||||
//==============================================================================
|
||||
void var::swapWith (var& other) noexcept
|
||||
{
|
||||
std::swap (type, other.type);
|
||||
std::swap (value, other.value);
|
||||
}
|
||||
|
||||
var& var::operator= (const var& v) { type->cleanUp (value); type = v.type; type->createCopy (value, v.value); return *this; }
|
||||
var& var::operator= (const int v) { type->cleanUp (value); type = &VariantType_Int::instance; value.intValue = v; return *this; }
|
||||
var& var::operator= (const int64 v) { type->cleanUp (value); type = &VariantType_Int64::instance; value.int64Value = v; return *this; }
|
||||
var& var::operator= (const bool v) { type->cleanUp (value); type = &VariantType_Bool::instance; value.boolValue = v; return *this; }
|
||||
var& var::operator= (const double v) { type->cleanUp (value); type = &VariantType_Double::instance; value.doubleValue = v; return *this; }
|
||||
var& var::operator= (const char* const v) { type->cleanUp (value); type = &VariantType_String::instance; new (value.stringValue) String (v); return *this; }
|
||||
var& var::operator= (const wchar_t* const v) { type->cleanUp (value); type = &VariantType_String::instance; new (value.stringValue) String (v); return *this; }
|
||||
var& var::operator= (const String& v) { type->cleanUp (value); type = &VariantType_String::instance; new (value.stringValue) String (v); return *this; }
|
||||
var& var::operator= (const Array<var>& v) { var v2 (v); swapWith (v2); return *this; }
|
||||
var& var::operator= (ReferenceCountedObject* v) { var v2 (v); swapWith (v2); return *this; }
|
||||
var& var::operator= (NativeFunction v) { var v2 (v); swapWith (v2); return *this; }
|
||||
|
||||
#if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS
|
||||
var::var (var&& other) noexcept
|
||||
: type (other.type),
|
||||
value (other.value)
|
||||
{
|
||||
other.type = &VariantType_Void::instance;
|
||||
}
|
||||
|
||||
var& var::operator= (var&& other) noexcept
|
||||
{
|
||||
swapWith (other);
|
||||
return *this;
|
||||
}
|
||||
|
||||
var::var (String&& v) : type (&VariantType_String::instance)
|
||||
{
|
||||
new (value.stringValue) String (static_cast<String&&> (v));
|
||||
}
|
||||
|
||||
var::var (MemoryBlock&& v) : type (&VariantType_Binary::instance)
|
||||
{
|
||||
value.binaryValue = new MemoryBlock (static_cast<MemoryBlock&&> (v));
|
||||
}
|
||||
|
||||
var::var (Array<var>&& v) : type (&VariantType_Array::instance)
|
||||
{
|
||||
value.objectValue = new VariantType_Array::RefCountedArray (static_cast<Array<var>&&> (v));
|
||||
}
|
||||
|
||||
var& var::operator= (String&& v)
|
||||
{
|
||||
type->cleanUp (value);
|
||||
type = &VariantType_String::instance;
|
||||
new (value.stringValue) String (static_cast<String&&> (v));
|
||||
return *this;
|
||||
}
|
||||
#endif
|
||||
|
||||
//==============================================================================
|
||||
bool var::equals (const var& other) const noexcept
|
||||
{
|
||||
return type->equals (value, other.value, *other.type);
|
||||
}
|
||||
|
||||
bool var::equalsWithSameType (const var& other) const noexcept
|
||||
{
|
||||
return type == other.type && equals (other);
|
||||
}
|
||||
|
||||
bool var::hasSameTypeAs (const var& other) const noexcept
|
||||
{
|
||||
return type == other.type;
|
||||
}
|
||||
|
||||
bool operator== (const var& v1, const var& v2) noexcept { return v1.equals (v2); }
|
||||
bool operator!= (const var& v1, const var& v2) noexcept { return ! v1.equals (v2); }
|
||||
bool operator== (const var& v1, const String& v2) { return v1.toString() == v2; }
|
||||
bool operator!= (const var& v1, const String& v2) { return v1.toString() != v2; }
|
||||
bool operator== (const var& v1, const char* const v2) { return v1.toString() == v2; }
|
||||
bool operator!= (const var& v1, const char* const v2) { return v1.toString() != v2; }
|
||||
|
||||
//==============================================================================
|
||||
var var::clone() const noexcept
|
||||
{
|
||||
return type->clone (*this);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
var var::operator[] (const Identifier propertyName) const
|
||||
{
|
||||
if (DynamicObject* const o = getDynamicObject())
|
||||
return o->getProperty (propertyName);
|
||||
|
||||
return var();
|
||||
}
|
||||
|
||||
var var::operator[] (const char* const propertyName) const
|
||||
{
|
||||
return operator[] (Identifier (propertyName));
|
||||
}
|
||||
|
||||
var var::getProperty (const Identifier propertyName, const var& defaultReturnValue) const
|
||||
{
|
||||
if (DynamicObject* const o = getDynamicObject())
|
||||
return o->getProperties().getWithDefault (propertyName, defaultReturnValue);
|
||||
|
||||
return defaultReturnValue;
|
||||
}
|
||||
|
||||
var::NativeFunction var::getNativeFunction() const
|
||||
{
|
||||
return isMethod() ? value.methodValue : nullptr;
|
||||
}
|
||||
|
||||
var var::invoke (Identifier method, const var* arguments, int numArguments) const
|
||||
{
|
||||
if (DynamicObject* const o = getDynamicObject())
|
||||
return o->invokeMethod (method, var::NativeFunctionArgs (*this, arguments, numArguments));
|
||||
|
||||
return var();
|
||||
}
|
||||
|
||||
var var::call (const Identifier method) const
|
||||
{
|
||||
return invoke (method, nullptr, 0);
|
||||
}
|
||||
|
||||
var var::call (const Identifier method, const var& arg1) const
|
||||
{
|
||||
return invoke (method, &arg1, 1);
|
||||
}
|
||||
|
||||
var var::call (const Identifier method, const var& arg1, const var& arg2) const
|
||||
{
|
||||
var args[] = { arg1, arg2 };
|
||||
return invoke (method, args, 2);
|
||||
}
|
||||
|
||||
var var::call (const Identifier method, const var& arg1, const var& arg2, const var& arg3)
|
||||
{
|
||||
var args[] = { arg1, arg2, arg3 };
|
||||
return invoke (method, args, 3);
|
||||
}
|
||||
|
||||
var var::call (const Identifier method, const var& arg1, const var& arg2, const var& arg3, const var& arg4) const
|
||||
{
|
||||
var args[] = { arg1, arg2, arg3, arg4 };
|
||||
return invoke (method, args, 4);
|
||||
}
|
||||
|
||||
var var::call (const Identifier method, const var& arg1, const var& arg2, const var& arg3, const var& arg4, const var& arg5) const
|
||||
{
|
||||
var args[] = { arg1, arg2, arg3, arg4, arg5 };
|
||||
return invoke (method, args, 5);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
int var::size() const
|
||||
{
|
||||
if (const Array<var>* const array = getArray())
|
||||
return array->size();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
const var& var::operator[] (int arrayIndex) const
|
||||
{
|
||||
const Array<var>* const array = getArray();
|
||||
|
||||
// When using this method, the var must actually be an array, and the index
|
||||
// must be in-range!
|
||||
jassert (array != nullptr && isPositiveAndBelow (arrayIndex, array->size()));
|
||||
|
||||
return array->getReference (arrayIndex);
|
||||
}
|
||||
|
||||
var& var::operator[] (int arrayIndex)
|
||||
{
|
||||
const Array<var>* const array = getArray();
|
||||
|
||||
// When using this method, the var must actually be an array, and the index
|
||||
// must be in-range!
|
||||
jassert (array != nullptr && isPositiveAndBelow (arrayIndex, array->size()));
|
||||
|
||||
return array->getReference (arrayIndex);
|
||||
}
|
||||
|
||||
Array<var>* var::convertToArray()
|
||||
{
|
||||
if (Array<var>* array = getArray())
|
||||
return array;
|
||||
|
||||
Array<var> tempVar;
|
||||
if (! isVoid())
|
||||
tempVar.add (*this);
|
||||
|
||||
*this = tempVar;
|
||||
return getArray();
|
||||
}
|
||||
|
||||
void var::append (const var& n)
|
||||
{
|
||||
convertToArray()->add (n);
|
||||
}
|
||||
|
||||
void var::remove (const int index)
|
||||
{
|
||||
if (Array<var>* const array = getArray())
|
||||
array->remove (index);
|
||||
}
|
||||
|
||||
void var::insert (const int index, const var& n)
|
||||
{
|
||||
convertToArray()->insert (index, n);
|
||||
}
|
||||
|
||||
void var::resize (const int numArrayElementsWanted)
|
||||
{
|
||||
convertToArray()->resize (numArrayElementsWanted);
|
||||
}
|
||||
|
||||
int var::indexOf (const var& n) const
|
||||
{
|
||||
if (const Array<var>* const array = getArray())
|
||||
return array->indexOf (n);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void var::writeToStream (OutputStream& output) const
|
||||
{
|
||||
type->writeToStream (value, output);
|
||||
}
|
||||
|
||||
var var::readFromStream (InputStream& input)
|
||||
{
|
||||
const int numBytes = input.readCompressedInt();
|
||||
|
||||
if (numBytes > 0)
|
||||
{
|
||||
switch (input.readByte())
|
||||
{
|
||||
case varMarker_Int: return var (input.readInt());
|
||||
case varMarker_Int64: return var (input.readInt64());
|
||||
case varMarker_BoolTrue: return var (true);
|
||||
case varMarker_BoolFalse: return var (false);
|
||||
case varMarker_Double: return var (input.readDouble());
|
||||
case varMarker_String:
|
||||
{
|
||||
MemoryOutputStream mo;
|
||||
mo.writeFromInputStream (input, numBytes - 1);
|
||||
return var (mo.toUTF8());
|
||||
}
|
||||
|
||||
case varMarker_Binary:
|
||||
{
|
||||
MemoryBlock mb ((size_t) numBytes - 1);
|
||||
|
||||
if (numBytes > 1)
|
||||
{
|
||||
const int numRead = input.read (mb.getData(), numBytes - 1);
|
||||
mb.setSize ((size_t) numRead);
|
||||
}
|
||||
|
||||
return var (mb);
|
||||
}
|
||||
|
||||
case varMarker_Array:
|
||||
{
|
||||
var v;
|
||||
Array<var>* const destArray = v.convertToArray();
|
||||
|
||||
for (int i = input.readCompressedInt(); --i >= 0;)
|
||||
destArray->add (readFromStream (input));
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
default:
|
||||
input.skipNextBytes (numBytes - 1); break;
|
||||
}
|
||||
}
|
||||
|
||||
return var();
|
||||
}
|
||||
|
||||
var::NativeFunctionArgs::NativeFunctionArgs (const var& t, const var* args, int numArgs) noexcept
|
||||
: thisObject (t), arguments (args), numArguments (numArgs)
|
||||
{}
|
||||
@@ -0,0 +1,327 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the juce_core module of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
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.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
|
||||
TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
|
||||
NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
|
||||
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
|
||||
IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
|
||||
CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
NOTE! This permissive ISC license applies ONLY to files within the juce_core module!
|
||||
All other JUCE modules are covered by a dual GPL/commercial license, so if you are
|
||||
using any other modules, be sure to check that you also comply with their license.
|
||||
|
||||
For more details, visit www.juce.com
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef JUCE_VARIANT_H_INCLUDED
|
||||
#define JUCE_VARIANT_H_INCLUDED
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
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
|
||||
*/
|
||||
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 NativeFunctionArgs
|
||||
{
|
||||
NativeFunctionArgs (const var& thisObject, const var* args, int numArgs) noexcept;
|
||||
|
||||
const var& thisObject;
|
||||
const var* arguments;
|
||||
int numArguments;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE (NativeFunctionArgs)
|
||||
};
|
||||
|
||||
typedef var (*NativeFunction) (const NativeFunctionArgs&);
|
||||
typedef Identifier identifier;
|
||||
|
||||
//==============================================================================
|
||||
/** Creates a void variant. */
|
||||
var() noexcept;
|
||||
|
||||
/** Destructor. */
|
||||
~var() noexcept;
|
||||
|
||||
/** A static var object that can be used where you need an empty variant object. */
|
||||
static const var null;
|
||||
|
||||
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 (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 Array<var>& value);
|
||||
var& operator= (ReferenceCountedObject* object);
|
||||
var& operator= (NativeFunction method);
|
||||
|
||||
#if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS
|
||||
var (var&& other) noexcept;
|
||||
var (String&& value);
|
||||
var (MemoryBlock&& binaryData);
|
||||
var (Array<var>&& value);
|
||||
var& operator= (var&& other) noexcept;
|
||||
var& operator= (String&& value);
|
||||
#endif
|
||||
|
||||
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. */
|
||||
var operator[] (Identifier propertyName) const;
|
||||
/** If this variant is an object, this returns one of its properties. */
|
||||
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 (Identifier propertyName, const var& defaultReturnValue) const;
|
||||
|
||||
/** Invokes a named method call with no arguments. */
|
||||
var call (Identifier method) const;
|
||||
/** Invokes a named method call with one argument. */
|
||||
var call (Identifier method, const var& arg1) const;
|
||||
/** Invokes a named method call with 2 arguments. */
|
||||
var call (Identifier method, const var& arg1, const var& arg2) const;
|
||||
/** Invokes a named method call with 3 arguments. */
|
||||
var call (Identifier method, const var& arg1, const var& arg2, const var& arg3);
|
||||
/** Invokes a named method call with 4 arguments. */
|
||||
var call (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 (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 (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);
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
class VariantType; friend class VariantType;
|
||||
class VariantType_Void; friend class VariantType_Void;
|
||||
class VariantType_Undefined; friend class VariantType_Undefined;
|
||||
class VariantType_Int; friend class VariantType_Int;
|
||||
class VariantType_Int64; friend class VariantType_Int64;
|
||||
class VariantType_Double; friend class VariantType_Double;
|
||||
class VariantType_Bool; friend class VariantType_Bool;
|
||||
class VariantType_String; friend class VariantType_String;
|
||||
class VariantType_Object; friend class VariantType_Object;
|
||||
class VariantType_Array; friend class VariantType_Array;
|
||||
class VariantType_Binary; friend class VariantType_Binary;
|
||||
class VariantType_Method; friend class VariantType_Method;
|
||||
|
||||
union ValueUnion
|
||||
{
|
||||
int intValue;
|
||||
int64 int64Value;
|
||||
bool boolValue;
|
||||
double doubleValue;
|
||||
char stringValue [sizeof (String)];
|
||||
ReferenceCountedObject* objectValue;
|
||||
MemoryBlock* binaryValue;
|
||||
NativeFunction methodValue;
|
||||
};
|
||||
|
||||
const VariantType* type;
|
||||
ValueUnion value;
|
||||
|
||||
Array<var>* convertToArray();
|
||||
var (const VariantType&) noexcept;
|
||||
};
|
||||
|
||||
/** Compares the values of two var objects, using the var::equals() comparison. */
|
||||
bool operator== (const var& v1, const var& v2) noexcept;
|
||||
/** Compares the values of two var objects, using the var::equals() comparison. */
|
||||
bool operator!= (const var& v1, const var& v2) noexcept;
|
||||
bool operator== (const var& v1, const String& v2);
|
||||
bool operator!= (const var& v1, const String& v2);
|
||||
bool operator== (const var& v1, const char* v2);
|
||||
bool operator!= (const var& v1, const char* v2);
|
||||
|
||||
|
||||
#endif // JUCE_VARIANT_H_INCLUDED
|
||||
Reference in New Issue
Block a user