upgrade to JUCE 5.4.3. Remove (probably) unused JUCE modules. Remove VST2 target (it's been end-of-life'd by Steinberg and by JUCE)
This commit is contained in:
@ -30,8 +30,8 @@ AbstractFifo::AbstractFifo (int capacity) noexcept : bufferSize (capacity)
|
||||
|
||||
AbstractFifo::~AbstractFifo() {}
|
||||
|
||||
int AbstractFifo::getTotalSize() const noexcept { return bufferSize; }
|
||||
int AbstractFifo::getFreeSpace() const noexcept { return bufferSize - getNumReady() - 1; }
|
||||
int AbstractFifo::getTotalSize() const noexcept { return bufferSize; }
|
||||
int AbstractFifo::getFreeSpace() const noexcept { return bufferSize - getNumReady() - 1; }
|
||||
|
||||
int AbstractFifo::getNumReady() const noexcept
|
||||
{
|
||||
@ -54,7 +54,8 @@ void AbstractFifo::setTotalSize (int newSize) noexcept
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void AbstractFifo::prepareToWrite (int numToWrite, int& startIndex1, int& blockSize1, int& startIndex2, int& blockSize2) const noexcept
|
||||
void AbstractFifo::prepareToWrite (int numToWrite, int& startIndex1, int& blockSize1,
|
||||
int& startIndex2, int& blockSize2) const noexcept
|
||||
{
|
||||
auto vs = validStart.get();
|
||||
auto ve = validEnd.get();
|
||||
@ -91,7 +92,8 @@ void AbstractFifo::finishedWrite (int numWritten) noexcept
|
||||
validEnd = newEnd;
|
||||
}
|
||||
|
||||
void AbstractFifo::prepareToRead (int numWanted, int& startIndex1, int& blockSize1, int& startIndex2, int& blockSize2) const noexcept
|
||||
void AbstractFifo::prepareToRead (int numWanted, int& startIndex1, int& blockSize1,
|
||||
int& startIndex2, int& blockSize2) const noexcept
|
||||
{
|
||||
auto vs = validStart.get();
|
||||
auto ve = validEnd.get();
|
||||
@ -129,13 +131,6 @@ void AbstractFifo::finishedRead (int numRead) noexcept
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
template <AbstractFifo::ReadOrWrite mode>
|
||||
AbstractFifo::ScopedReadWrite<mode>::ScopedReadWrite (AbstractFifo& f, int num) noexcept
|
||||
: fifo (&f)
|
||||
{
|
||||
prepare (*fifo, num);
|
||||
}
|
||||
|
||||
template <AbstractFifo::ReadOrWrite mode>
|
||||
AbstractFifo::ScopedReadWrite<mode>::ScopedReadWrite (ScopedReadWrite&& other) noexcept
|
||||
: startIndex1 (other.startIndex1),
|
||||
@ -154,13 +149,6 @@ AbstractFifo::ScopedReadWrite<mode>::operator= (ScopedReadWrite&& other) noexcep
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <AbstractFifo::ReadOrWrite mode>
|
||||
AbstractFifo::ScopedReadWrite<mode>::~ScopedReadWrite() noexcept
|
||||
{
|
||||
if (fifo != nullptr)
|
||||
finish (*fifo, blockSize1 + blockSize2);
|
||||
}
|
||||
|
||||
template <AbstractFifo::ReadOrWrite mode>
|
||||
void AbstractFifo::ScopedReadWrite<mode>::swap (ScopedReadWrite& other) noexcept
|
||||
{
|
||||
@ -171,33 +159,13 @@ void AbstractFifo::ScopedReadWrite<mode>::swap (ScopedReadWrite& other) noexcept
|
||||
std::swap (other.blockSize2, blockSize2);
|
||||
}
|
||||
|
||||
template<>
|
||||
void AbstractFifo::ScopedReadWrite<AbstractFifo::ReadOrWrite::read>::prepare (AbstractFifo& f, int num) noexcept
|
||||
{
|
||||
f.prepareToRead (num, startIndex1, blockSize1, startIndex2, blockSize2);
|
||||
}
|
||||
|
||||
template<>
|
||||
void AbstractFifo::ScopedReadWrite<AbstractFifo::ReadOrWrite::write>::prepare (AbstractFifo& f, int num) noexcept
|
||||
{
|
||||
f.prepareToWrite (num, startIndex1, blockSize1, startIndex2, blockSize2);
|
||||
}
|
||||
|
||||
template<>
|
||||
void AbstractFifo::ScopedReadWrite<AbstractFifo::ReadOrWrite::read>::finish (AbstractFifo& f, int num) noexcept
|
||||
{
|
||||
f.finishedRead (num);
|
||||
}
|
||||
|
||||
template<>
|
||||
void AbstractFifo::ScopedReadWrite<AbstractFifo::ReadOrWrite::write>::finish (AbstractFifo& f, int num) noexcept
|
||||
{
|
||||
f.finishedWrite (num);
|
||||
}
|
||||
|
||||
template class AbstractFifo::ScopedReadWrite<AbstractFifo::ReadOrWrite::read>;
|
||||
template class AbstractFifo::ScopedReadWrite<AbstractFifo::ReadOrWrite::write>;
|
||||
|
||||
AbstractFifo::ScopedRead AbstractFifo::read (int numToRead) noexcept { return { *this, numToRead }; }
|
||||
AbstractFifo::ScopedWrite AbstractFifo::write (int numToWrite) noexcept { return { *this, numToWrite }; }
|
||||
|
||||
|
||||
//==============================================================================
|
||||
#if JUCE_UNIT_TESTS
|
||||
|
||||
|
@ -38,13 +38,8 @@ namespace juce
|
||||
|
||||
e.g.
|
||||
@code
|
||||
class MyFifo
|
||||
struct MyFifo
|
||||
{
|
||||
public:
|
||||
MyFifo() : abstractFifo (1024)
|
||||
{
|
||||
}
|
||||
|
||||
void addToFifo (const int* someData, int numItems)
|
||||
{
|
||||
int start1, size1, start2, size2;
|
||||
@ -73,9 +68,8 @@ namespace juce
|
||||
abstractFifo.finishedRead (size1 + size2);
|
||||
}
|
||||
|
||||
private:
|
||||
AbstractFifo abstractFifo;
|
||||
int myBuffer [1024];
|
||||
AbstractFifo abstractFifo { 1024 };
|
||||
int myBuffer[1024];
|
||||
};
|
||||
@endcode
|
||||
|
||||
@ -225,7 +219,10 @@ public:
|
||||
This object will hold a pointer back to the fifo, so make sure that
|
||||
the fifo outlives this object.
|
||||
*/
|
||||
ScopedReadWrite (AbstractFifo&, int num) noexcept;
|
||||
ScopedReadWrite (AbstractFifo& f, int num) noexcept : fifo (&f)
|
||||
{
|
||||
prepare (*fifo, num);
|
||||
}
|
||||
|
||||
ScopedReadWrite (const ScopedReadWrite&) = delete;
|
||||
ScopedReadWrite (ScopedReadWrite&&) noexcept;
|
||||
@ -236,7 +233,11 @@ public:
|
||||
/** Calls finishedRead or finishedWrite if this is a non-null scoped
|
||||
reader/writer.
|
||||
*/
|
||||
~ScopedReadWrite() noexcept;
|
||||
~ScopedReadWrite() noexcept
|
||||
{
|
||||
if (fifo != nullptr)
|
||||
finish (*fifo, blockSize1 + blockSize2);
|
||||
}
|
||||
|
||||
/** Calls the passed function with each index that was deemed valid
|
||||
for the current read/write operation.
|
||||
@ -281,7 +282,7 @@ public:
|
||||
} // readHandle goes out of scope here, finishing the read operation
|
||||
@endcode
|
||||
*/
|
||||
ScopedRead read (int numToRead) noexcept { return { *this, numToRead }; }
|
||||
ScopedRead read (int numToRead) noexcept;
|
||||
|
||||
/** Replaces prepareToWrite/finishedWrite with a single function.
|
||||
This function returns an object which contains the start indices and
|
||||
@ -303,7 +304,7 @@ public:
|
||||
} // writeHandle goes out of scope here, finishing the write operation
|
||||
@endcode
|
||||
*/
|
||||
ScopedWrite write (int numToWrite) noexcept { return { *this, numToWrite }; }
|
||||
ScopedWrite write (int numToWrite) noexcept;
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
@ -313,4 +314,29 @@ private:
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AbstractFifo)
|
||||
};
|
||||
|
||||
template<>
|
||||
inline void AbstractFifo::ScopedReadWrite<AbstractFifo::ReadOrWrite::read>::finish (AbstractFifo& f, int num) noexcept
|
||||
{
|
||||
f.finishedRead (num);
|
||||
}
|
||||
|
||||
template<>
|
||||
inline void AbstractFifo::ScopedReadWrite<AbstractFifo::ReadOrWrite::write>::finish (AbstractFifo& f, int num) noexcept
|
||||
{
|
||||
f.finishedWrite (num);
|
||||
}
|
||||
|
||||
template<>
|
||||
inline void AbstractFifo::ScopedReadWrite<AbstractFifo::ReadOrWrite::read>::prepare (AbstractFifo& f, int num) noexcept
|
||||
{
|
||||
f.prepareToRead (num, startIndex1, blockSize1, startIndex2, blockSize2);
|
||||
}
|
||||
|
||||
template<>
|
||||
inline void AbstractFifo::ScopedReadWrite<AbstractFifo::ReadOrWrite::write>::prepare (AbstractFifo& f, int num) noexcept
|
||||
{
|
||||
f.prepareToWrite (num, startIndex1, blockSize1, startIndex2, blockSize2);
|
||||
}
|
||||
|
||||
|
||||
} // namespace juce
|
||||
|
@ -60,51 +60,40 @@ private:
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates an empty array. */
|
||||
Array() noexcept
|
||||
{
|
||||
}
|
||||
Array() = default;
|
||||
|
||||
/** Creates a copy of another array.
|
||||
@param other the array to copy
|
||||
*/
|
||||
Array (const Array<ElementType, TypeOfCriticalSectionToUse>& other)
|
||||
Array (const Array& other)
|
||||
{
|
||||
const ScopedLockType lock (other.getLock());
|
||||
numUsed = other.numUsed;
|
||||
data.setAllocatedSize (other.numUsed);
|
||||
|
||||
for (int i = 0; i < numUsed; ++i)
|
||||
new (data.elements + i) ElementType (other.data.elements[i]);
|
||||
values.addArray (other.values.begin(), other.values.size());
|
||||
}
|
||||
|
||||
Array (Array<ElementType, TypeOfCriticalSectionToUse>&& other) noexcept
|
||||
: data (static_cast<ArrayAllocationBase<ElementType, TypeOfCriticalSectionToUse>&&> (other.data)),
|
||||
numUsed (other.numUsed)
|
||||
Array (Array&& other) noexcept
|
||||
: values (std::move (other.values))
|
||||
{
|
||||
other.numUsed = 0;
|
||||
}
|
||||
|
||||
/** Initalises from a null-terminated raw array of values.
|
||||
@param values the array to copy from
|
||||
@param data the data to copy from
|
||||
*/
|
||||
template <typename TypeToCreateFrom>
|
||||
explicit Array (const TypeToCreateFrom* values)
|
||||
explicit Array (const TypeToCreateFrom* data)
|
||||
{
|
||||
while (*values != TypeToCreateFrom())
|
||||
add (*values++);
|
||||
add (*data++);
|
||||
}
|
||||
|
||||
/** Initalises from a raw array of values.
|
||||
@param values the array to copy from
|
||||
@param data the data to copy from
|
||||
@param numValues the number of values in the array
|
||||
*/
|
||||
template <typename TypeToCreateFrom>
|
||||
Array (const TypeToCreateFrom* values, int numValues) : numUsed (numValues)
|
||||
Array (const TypeToCreateFrom* data, int numValues)
|
||||
{
|
||||
data.setAllocatedSize (numValues);
|
||||
|
||||
for (int i = 0; i < numValues; ++i)
|
||||
new (data.elements + i) ElementType (values[i]);
|
||||
values.addArray (data, numValues);
|
||||
}
|
||||
|
||||
/** Initalises an Array of size 1 containing a single element. */
|
||||
@ -116,23 +105,21 @@ public:
|
||||
/** Initalises an Array of size 1 containing a single element. */
|
||||
Array (ElementType&& singleElementToAdd)
|
||||
{
|
||||
add (static_cast<ElementType&&> (singleElementToAdd));
|
||||
add (std::move (singleElementToAdd));
|
||||
}
|
||||
|
||||
/** Initalises an Array from a list of items. */
|
||||
template <typename... OtherElements>
|
||||
Array (const ElementType& firstNewElement, OtherElements... otherElements)
|
||||
{
|
||||
data.setAllocatedSize (1 + (int) sizeof... (otherElements));
|
||||
addAssumingCapacityIsReady (firstNewElement, otherElements...);
|
||||
values.add (firstNewElement, otherElements...);
|
||||
}
|
||||
|
||||
/** Initalises an Array from a list of items. */
|
||||
template <typename... OtherElements>
|
||||
Array (ElementType&& firstNewElement, OtherElements... otherElements)
|
||||
{
|
||||
data.setAllocatedSize (1 + (int) sizeof... (otherElements));
|
||||
addAssumingCapacityIsReady (static_cast<ElementType&&> (firstNewElement), otherElements...);
|
||||
values.add (std::move (firstNewElement), otherElements...);
|
||||
}
|
||||
|
||||
template <typename TypeToCreateFrom>
|
||||
@ -142,10 +129,7 @@ public:
|
||||
}
|
||||
|
||||
/** Destructor. */
|
||||
~Array()
|
||||
{
|
||||
deleteAllElements();
|
||||
}
|
||||
~Array() = default;
|
||||
|
||||
/** Copies another array.
|
||||
@param other the array to copy
|
||||
@ -164,10 +148,7 @@ public:
|
||||
Array& operator= (Array&& other) noexcept
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
deleteAllElements();
|
||||
data = static_cast<ArrayAllocationBase<ElementType, TypeOfCriticalSectionToUse>&&> (other.data);
|
||||
numUsed = other.numUsed;
|
||||
other.numUsed = 0;
|
||||
values = std::move (other.values);
|
||||
return *this;
|
||||
}
|
||||
|
||||
@ -182,15 +163,7 @@ public:
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
const typename OtherArrayType::ScopedLockType lock2 (other.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;
|
||||
return values == other;
|
||||
}
|
||||
|
||||
/** Compares this array to another one.
|
||||
@ -215,9 +188,8 @@ public:
|
||||
void clear()
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
deleteAllElements();
|
||||
data.setAllocatedSize (0);
|
||||
numUsed = 0;
|
||||
clearQuick();
|
||||
values.setAllocatedSize (0);
|
||||
}
|
||||
|
||||
/** Removes all elements from the array without freeing the array's allocated storage.
|
||||
@ -226,8 +198,7 @@ public:
|
||||
void clearQuick()
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
deleteAllElements();
|
||||
numUsed = 0;
|
||||
values.clear();
|
||||
}
|
||||
|
||||
/** Fills the Array with the provided value. */
|
||||
@ -243,13 +214,14 @@ public:
|
||||
/** Returns the current number of elements in the array. */
|
||||
inline int size() const noexcept
|
||||
{
|
||||
return numUsed;
|
||||
const ScopedLockType lock (getLock());
|
||||
return values.size();
|
||||
}
|
||||
|
||||
/** Returns true if the array is empty, false otherwise. */
|
||||
inline bool isEmpty() const noexcept
|
||||
{
|
||||
return numUsed == 0;
|
||||
return size() == 0;
|
||||
}
|
||||
|
||||
/** Returns one of the elements in the array.
|
||||
@ -262,17 +234,10 @@ public:
|
||||
@param index the index of the element being requested (0 is the first element in the array)
|
||||
@see getUnchecked, getFirst, getLast
|
||||
*/
|
||||
ElementType operator[] (const int index) const
|
||||
ElementType operator[] (int index) const
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
|
||||
if (isPositiveAndBelow (index, numUsed))
|
||||
{
|
||||
jassert (data.elements != nullptr);
|
||||
return data.elements[index];
|
||||
}
|
||||
|
||||
return ElementType();
|
||||
return values.getValueWithDefault (index);
|
||||
}
|
||||
|
||||
/** Returns one of the elements in the array, without checking the index passed in.
|
||||
@ -284,11 +249,10 @@ public:
|
||||
@param index the index of the element being requested (0 is the first element in the array)
|
||||
@see operator[], getFirst, getLast
|
||||
*/
|
||||
inline ElementType getUnchecked (const int index) const
|
||||
inline ElementType getUnchecked (int index) const
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
jassert (isPositiveAndBelow (index, numUsed) && data.elements != nullptr);
|
||||
return data.elements[index];
|
||||
return values[index];
|
||||
}
|
||||
|
||||
/** Returns a direct reference to one of the elements in the array, without checking the index passed in.
|
||||
@ -300,44 +264,29 @@ public:
|
||||
@param index the index of the element being requested (0 is the first element in the array)
|
||||
@see operator[], getFirst, getLast
|
||||
*/
|
||||
inline ElementType& getReference (const int index) const noexcept
|
||||
inline ElementType& getReference (int index) const noexcept
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
jassert (isPositiveAndBelow (index, numUsed) && data.elements != nullptr);
|
||||
return data.elements[index];
|
||||
return values[index];
|
||||
}
|
||||
|
||||
/** Returns the first element in the array, or a default value if the array is empty.
|
||||
@see operator[], getUnchecked, getLast
|
||||
*/
|
||||
inline ElementType getFirst() const
|
||||
inline ElementType getFirst() const noexcept
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
|
||||
if (numUsed > 0)
|
||||
{
|
||||
jassert (data.elements != nullptr);
|
||||
return data.elements[0];
|
||||
}
|
||||
|
||||
return ElementType();
|
||||
return values.getFirst();
|
||||
}
|
||||
|
||||
/** Returns the last element in the array, or a default value if the array is empty.
|
||||
|
||||
@see operator[], getUnchecked, getFirst
|
||||
*/
|
||||
inline ElementType getLast() const
|
||||
inline ElementType getLast() const noexcept
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
|
||||
if (numUsed > 0)
|
||||
{
|
||||
jassert (data.elements != nullptr);
|
||||
return data.elements[numUsed - 1];
|
||||
}
|
||||
|
||||
return ElementType();
|
||||
return values.getLast();
|
||||
}
|
||||
|
||||
/** Returns a pointer to the actual array data.
|
||||
@ -346,7 +295,7 @@ public:
|
||||
*/
|
||||
inline ElementType* getRawDataPointer() noexcept
|
||||
{
|
||||
return data.elements;
|
||||
return values.begin();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
@ -355,7 +304,7 @@ public:
|
||||
*/
|
||||
inline ElementType* begin() const noexcept
|
||||
{
|
||||
return data.elements;
|
||||
return values.begin();
|
||||
}
|
||||
|
||||
/** Returns a pointer to the element which follows the last element in the array.
|
||||
@ -363,12 +312,15 @@ public:
|
||||
*/
|
||||
inline ElementType* end() const noexcept
|
||||
{
|
||||
#if JUCE_DEBUG
|
||||
if (data.elements == nullptr || numUsed <= 0) // (to keep static analysers happy)
|
||||
return data.elements;
|
||||
#endif
|
||||
return values.end();
|
||||
}
|
||||
|
||||
return data.elements + numUsed;
|
||||
/** Returns a pointer to the first element in the array.
|
||||
This method is provided for compatibility with the standard C++ containers.
|
||||
*/
|
||||
inline ElementType* data() const noexcept
|
||||
{
|
||||
return begin();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
@ -383,12 +335,12 @@ public:
|
||||
int indexOf (ParameterType elementToLookFor) const
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
auto e = data.elements.get();
|
||||
auto endPtr = e + numUsed;
|
||||
auto e = values.begin();
|
||||
auto endPtr = values.end();
|
||||
|
||||
for (; e != endPtr; ++e)
|
||||
if (elementToLookFor == *e)
|
||||
return static_cast<int> (e - data.elements.get());
|
||||
return static_cast<int> (e - values.begin());
|
||||
|
||||
return -1;
|
||||
}
|
||||
@ -401,8 +353,8 @@ public:
|
||||
bool contains (ParameterType elementToLookFor) const
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
auto e = data.elements.get();
|
||||
auto endPtr = e + numUsed;
|
||||
auto e = values.begin();
|
||||
auto endPtr = values.end();
|
||||
|
||||
for (; e != endPtr; ++e)
|
||||
if (elementToLookFor == *e)
|
||||
@ -419,8 +371,7 @@ public:
|
||||
void add (const ElementType& newElement)
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
data.ensureAllocatedSize (numUsed + 1);
|
||||
new (data.elements + numUsed++) ElementType (newElement);
|
||||
values.add (newElement);
|
||||
}
|
||||
|
||||
/** Appends a new element at the end of the array.
|
||||
@ -430,8 +381,7 @@ public:
|
||||
void add (ElementType&& newElement)
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
data.ensureAllocatedSize (numUsed + 1);
|
||||
new (data.elements + numUsed++) ElementType (static_cast<ElementType&&> (newElement));
|
||||
values.add (std::move (newElement));
|
||||
}
|
||||
|
||||
/** Appends multiple new elements at the end of the array. */
|
||||
@ -439,8 +389,7 @@ public:
|
||||
void add (const ElementType& firstNewElement, OtherElements... otherElements)
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
data.ensureAllocatedSize (numUsed + 1 + (int) sizeof... (otherElements));
|
||||
addAssumingCapacityIsReady (firstNewElement, otherElements...);
|
||||
values.add (firstNewElement, otherElements...);
|
||||
}
|
||||
|
||||
/** Appends multiple new elements at the end of the array. */
|
||||
@ -448,8 +397,7 @@ public:
|
||||
void add (ElementType&& firstNewElement, OtherElements... otherElements)
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
data.ensureAllocatedSize (numUsed + 1 + (int) sizeof... (otherElements));
|
||||
addAssumingCapacityIsReady (static_cast<ElementType&&> (firstNewElement), otherElements...);
|
||||
values.add (std::move (firstNewElement), otherElements...);
|
||||
}
|
||||
|
||||
/** Inserts a new element into the array at a given position.
|
||||
@ -467,24 +415,7 @@ public:
|
||||
void insert (int indexToInsertAt, ParameterType newElement)
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
data.ensureAllocatedSize (numUsed + 1);
|
||||
jassert (data.elements != nullptr);
|
||||
|
||||
if (isPositiveAndBelow (indexToInsertAt, numUsed))
|
||||
{
|
||||
auto* insertPos = data.elements + indexToInsertAt;
|
||||
auto numberToMove = numUsed - indexToInsertAt;
|
||||
|
||||
if (numberToMove > 0)
|
||||
memmove (insertPos + 1, insertPos, ((size_t) numberToMove) * sizeof (ElementType));
|
||||
|
||||
new (insertPos) ElementType (newElement);
|
||||
++numUsed;
|
||||
}
|
||||
else
|
||||
{
|
||||
new (data.elements + numUsed++) ElementType (newElement);
|
||||
}
|
||||
values.insert (indexToInsertAt, newElement, 1);
|
||||
}
|
||||
|
||||
/** Inserts multiple copies of an element into the array at a given position.
|
||||
@ -505,28 +436,7 @@ public:
|
||||
if (numberOfTimesToInsertIt > 0)
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
data.ensureAllocatedSize (numUsed + numberOfTimesToInsertIt);
|
||||
ElementType* insertPos;
|
||||
|
||||
if (isPositiveAndBelow (indexToInsertAt, numUsed))
|
||||
{
|
||||
insertPos = data.elements + indexToInsertAt;
|
||||
auto numberToMove = numUsed - indexToInsertAt;
|
||||
memmove (insertPos + numberOfTimesToInsertIt, insertPos, ((size_t) numberToMove) * sizeof (ElementType));
|
||||
}
|
||||
else
|
||||
{
|
||||
insertPos = data.elements + numUsed;
|
||||
}
|
||||
|
||||
numUsed += numberOfTimesToInsertIt;
|
||||
|
||||
while (--numberOfTimesToInsertIt >= 0)
|
||||
{
|
||||
new (insertPos) ElementType (newElement);
|
||||
++insertPos; // NB: this increment is done separately from the
|
||||
// new statement to avoid a compiler bug in VS2014
|
||||
}
|
||||
values.insert (indexToInsertAt, newElement, numberOfTimesToInsertIt);
|
||||
}
|
||||
}
|
||||
|
||||
@ -549,24 +459,7 @@ public:
|
||||
if (numberOfElements > 0)
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
data.ensureAllocatedSize (numUsed + numberOfElements);
|
||||
ElementType* insertPos = data.elements;
|
||||
|
||||
if (isPositiveAndBelow (indexToInsertAt, numUsed))
|
||||
{
|
||||
insertPos += indexToInsertAt;
|
||||
auto numberToMove = numUsed - indexToInsertAt;
|
||||
memmove (insertPos + numberOfElements, insertPos, (size_t) numberToMove * sizeof (ElementType));
|
||||
}
|
||||
else
|
||||
{
|
||||
insertPos += numUsed;
|
||||
}
|
||||
|
||||
numUsed += numberOfElements;
|
||||
|
||||
while (--numberOfElements >= 0)
|
||||
new (insertPos++) ElementType (*newElements++);
|
||||
values.insertArray (indexToInsertAt, newElements, numberOfElements);
|
||||
}
|
||||
}
|
||||
|
||||
@ -599,20 +492,20 @@ public:
|
||||
@param newValue the new value to set for this index.
|
||||
@see add, insert
|
||||
*/
|
||||
void set (const int indexToChange, ParameterType newValue)
|
||||
void set (int indexToChange, ParameterType newValue)
|
||||
{
|
||||
jassert (indexToChange >= 0);
|
||||
const ScopedLockType lock (getLock());
|
||||
if (indexToChange >= 0)
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
|
||||
if (isPositiveAndBelow (indexToChange, numUsed))
|
||||
{
|
||||
jassert (data.elements != nullptr);
|
||||
data.elements[indexToChange] = newValue;
|
||||
if (indexToChange < values.size())
|
||||
values[indexToChange] = newValue;
|
||||
else
|
||||
values.add (newValue);
|
||||
}
|
||||
else if (indexToChange >= 0)
|
||||
else
|
||||
{
|
||||
data.ensureAllocatedSize (numUsed + 1);
|
||||
new (data.elements + numUsed++) ElementType (newValue);
|
||||
jassertfalse;
|
||||
}
|
||||
}
|
||||
|
||||
@ -625,11 +518,11 @@ public:
|
||||
@param newValue the new value to set for this index.
|
||||
@see set, getUnchecked
|
||||
*/
|
||||
void setUnchecked (const int indexToChange, ParameterType newValue)
|
||||
void setUnchecked (int indexToChange, ParameterType newValue)
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
jassert (isPositiveAndBelow (indexToChange, numUsed));
|
||||
data.elements[indexToChange] = newValue;
|
||||
jassert (isPositiveAndBelow (indexToChange, values.size()));
|
||||
values[indexToChange] = newValue;
|
||||
}
|
||||
|
||||
/** Adds elements from an array to the end of this array.
|
||||
@ -645,28 +538,14 @@ public:
|
||||
const ScopedLockType lock (getLock());
|
||||
|
||||
if (numElementsToAdd > 0)
|
||||
{
|
||||
data.ensureAllocatedSize (numUsed + numElementsToAdd);
|
||||
|
||||
while (--numElementsToAdd >= 0)
|
||||
{
|
||||
new (data.elements + numUsed) ElementType (*elementsToAdd++);
|
||||
++numUsed;
|
||||
}
|
||||
}
|
||||
values.addArray (elementsToAdd, numElementsToAdd);
|
||||
}
|
||||
|
||||
template <typename TypeToCreateFrom>
|
||||
void addArray (const std::initializer_list<TypeToCreateFrom>& items)
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
data.ensureAllocatedSize (numUsed + (int) items.size());
|
||||
|
||||
for (auto& item : items)
|
||||
{
|
||||
new (data.elements + numUsed) ElementType (item);
|
||||
++numUsed;
|
||||
}
|
||||
values.addArray (items);
|
||||
}
|
||||
|
||||
/** Adds elements from a null-terminated array of pointers to the end of this array.
|
||||
@ -696,8 +575,21 @@ public:
|
||||
{
|
||||
const ScopedLockType lock1 (getLock());
|
||||
const typename OtherArrayType::ScopedLockType lock2 (otherArray.getLock());
|
||||
data.swapWith (otherArray.data);
|
||||
std::swap (numUsed, otherArray.numUsed);
|
||||
values.swapWith (otherArray.values);
|
||||
}
|
||||
|
||||
/** Adds elements from another array to the end of this array.
|
||||
|
||||
@param arrayToAddFrom the array from which to copy the elements
|
||||
@see add
|
||||
*/
|
||||
template <class OtherArrayType>
|
||||
void addArray (const OtherArrayType& arrayToAddFrom)
|
||||
{
|
||||
const typename OtherArrayType::ScopedLockType lock1 (arrayToAddFrom.getLock());
|
||||
const ScopedLockType lock2 (getLock());
|
||||
|
||||
values.addArray (arrayToAddFrom);
|
||||
}
|
||||
|
||||
/** Adds elements from another array to the end of this array.
|
||||
@ -710,29 +602,15 @@ public:
|
||||
@see add
|
||||
*/
|
||||
template <class OtherArrayType>
|
||||
void addArray (const OtherArrayType& arrayToAddFrom,
|
||||
int startIndex = 0,
|
||||
int numElementsToAdd = -1)
|
||||
typename std::enable_if<! std::is_pointer<OtherArrayType>::value, void>::type
|
||||
addArray (const OtherArrayType& arrayToAddFrom,
|
||||
int startIndex,
|
||||
int numElementsToAdd = -1)
|
||||
{
|
||||
const typename OtherArrayType::ScopedLockType lock1 (arrayToAddFrom.getLock());
|
||||
const ScopedLockType lock2 (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);
|
||||
|
||||
while (--numElementsToAdd >= 0)
|
||||
addAssumingCapacityIsReady (arrayToAddFrom.getUnchecked (startIndex++));
|
||||
}
|
||||
values.addArray (arrayToAddFrom, startIndex, numElementsToAdd);
|
||||
}
|
||||
|
||||
/** This will enlarge or shrink the array to the given number of elements, by adding
|
||||
@ -742,13 +620,13 @@ public:
|
||||
until its size is as specified. If its size is larger than the target, items will be
|
||||
removed from its end to shorten it.
|
||||
*/
|
||||
void resize (const int targetNumItems)
|
||||
void resize (int targetNumItems)
|
||||
{
|
||||
jassert (targetNumItems >= 0);
|
||||
auto numToAdd = targetNumItems - numUsed;
|
||||
auto numToAdd = targetNumItems - values.size();
|
||||
|
||||
if (numToAdd > 0)
|
||||
insertMultiple (numUsed, ElementType(), numToAdd);
|
||||
insertMultiple (values.size(), ElementType(), numToAdd);
|
||||
else if (numToAdd < 0)
|
||||
removeRange (targetNumItems, -numToAdd);
|
||||
}
|
||||
@ -769,7 +647,7 @@ public:
|
||||
int addSorted (ElementComparator& comparator, ParameterType newElement)
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
auto index = findInsertIndexInSortedArray (comparator, data.elements.get(), newElement, 0, numUsed);
|
||||
auto index = findInsertIndexInSortedArray (comparator, values.begin(), newElement, 0, values.size());
|
||||
insert (index, newElement);
|
||||
return index;
|
||||
}
|
||||
@ -809,12 +687,12 @@ public:
|
||||
|
||||
const ScopedLockType lock (getLock());
|
||||
|
||||
for (int s = 0, e = numUsed;;)
|
||||
for (int s = 0, e = values.size();;)
|
||||
{
|
||||
if (s >= e)
|
||||
return -1;
|
||||
|
||||
if (comparator.compareElements (elementToLookFor, data.elements[s]) == 0)
|
||||
if (comparator.compareElements (elementToLookFor, values[s]) == 0)
|
||||
return s;
|
||||
|
||||
auto halfway = (s + e) / 2;
|
||||
@ -822,7 +700,7 @@ public:
|
||||
if (halfway == s)
|
||||
return -1;
|
||||
|
||||
if (comparator.compareElements (elementToLookFor, data.elements[halfway]) >= 0)
|
||||
if (comparator.compareElements (elementToLookFor, values[halfway]) >= 0)
|
||||
s = halfway;
|
||||
else
|
||||
e = halfway;
|
||||
@ -843,11 +721,8 @@ public:
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
|
||||
if (isPositiveAndBelow (indexToRemove, numUsed))
|
||||
{
|
||||
jassert (data.elements != nullptr);
|
||||
if (isPositiveAndBelow (indexToRemove, values.size()))
|
||||
removeInternal (indexToRemove);
|
||||
}
|
||||
}
|
||||
|
||||
/** Removes an element from the array.
|
||||
@ -864,15 +739,14 @@ public:
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
|
||||
if (isPositiveAndBelow (indexToRemove, numUsed))
|
||||
if (isPositiveAndBelow (indexToRemove, values.size()))
|
||||
{
|
||||
jassert (data.elements != nullptr);
|
||||
ElementType removed (data.elements[indexToRemove]);
|
||||
ElementType removed (values[indexToRemove]);
|
||||
removeInternal (indexToRemove);
|
||||
return removed;
|
||||
}
|
||||
|
||||
return {};
|
||||
return ElementType();
|
||||
}
|
||||
|
||||
/** Removes an element from the array.
|
||||
@ -890,10 +764,10 @@ public:
|
||||
jassert (elementToRemove != nullptr);
|
||||
const ScopedLockType lock (getLock());
|
||||
|
||||
jassert (data.elements != nullptr);
|
||||
auto indexToRemove = (int) (elementToRemove - data.elements);
|
||||
jassert (values.begin() != nullptr);
|
||||
auto indexToRemove = (int) (elementToRemove - values.begin());
|
||||
|
||||
if (! isPositiveAndBelow (indexToRemove, numUsed))
|
||||
if (! isPositiveAndBelow (indexToRemove, values.size()))
|
||||
{
|
||||
jassertfalse;
|
||||
return;
|
||||
@ -913,9 +787,9 @@ public:
|
||||
void removeFirstMatchingValue (ParameterType valueToRemove)
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
auto* e = data.elements.get();
|
||||
auto* e = values.begin();
|
||||
|
||||
for (int i = 0; i < numUsed; ++i)
|
||||
for (int i = 0; i < values.size(); ++i)
|
||||
{
|
||||
if (valueToRemove == e[i])
|
||||
{
|
||||
@ -939,9 +813,9 @@ public:
|
||||
int numRemoved = 0;
|
||||
const ScopedLockType lock (getLock());
|
||||
|
||||
for (int i = numUsed; --i >= 0;)
|
||||
for (int i = values.size(); --i >= 0;)
|
||||
{
|
||||
if (valueToRemove == data.elements[i])
|
||||
if (valueToRemove == values[i])
|
||||
{
|
||||
removeInternal (i);
|
||||
++numRemoved;
|
||||
@ -963,14 +837,14 @@ public:
|
||||
@see remove, removeRange, removeAllInstancesOf
|
||||
*/
|
||||
template <typename PredicateType>
|
||||
int removeIf (PredicateType predicate)
|
||||
int removeIf (PredicateType&& predicate)
|
||||
{
|
||||
int numRemoved = 0;
|
||||
const ScopedLockType lock (getLock());
|
||||
|
||||
for (int i = numUsed; --i >= 0;)
|
||||
for (int i = values.size(); --i >= 0;)
|
||||
{
|
||||
if (predicate (data.elements[i]))
|
||||
if (predicate (values[i]))
|
||||
{
|
||||
removeInternal (i);
|
||||
++numRemoved;
|
||||
@ -995,23 +869,14 @@ public:
|
||||
void removeRange (int startIndex, int numberToRemove)
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
auto endIndex = jlimit (0, numUsed, startIndex + numberToRemove);
|
||||
startIndex = jlimit (0, numUsed, startIndex);
|
||||
|
||||
if (endIndex > startIndex)
|
||||
auto endIndex = jlimit (0, values.size(), startIndex + numberToRemove);
|
||||
startIndex = jlimit (0, values.size(), startIndex);
|
||||
numberToRemove = endIndex - startIndex;
|
||||
|
||||
if (numberToRemove > 0)
|
||||
{
|
||||
auto* e = data.elements + startIndex;
|
||||
numberToRemove = endIndex - startIndex;
|
||||
|
||||
for (int i = 0; i < numberToRemove; ++i)
|
||||
e[i].~ElementType();
|
||||
|
||||
auto numToShift = numUsed - endIndex;
|
||||
|
||||
if (numToShift > 0)
|
||||
memmove (e, e + numberToRemove, ((size_t) numToShift) * sizeof (ElementType));
|
||||
|
||||
numUsed -= numberToRemove;
|
||||
values.removeElements (startIndex, numberToRemove);
|
||||
minimiseStorageAfterRemoval();
|
||||
}
|
||||
}
|
||||
@ -1023,16 +888,18 @@ public:
|
||||
*/
|
||||
void removeLast (int howManyToRemove = 1)
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
jassert (howManyToRemove >= 0);
|
||||
|
||||
if (howManyToRemove > numUsed)
|
||||
howManyToRemove = numUsed;
|
||||
if (howManyToRemove > 0)
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
|
||||
for (int i = 1; i <= howManyToRemove; ++i)
|
||||
data.elements[numUsed - i].~ElementType();
|
||||
if (howManyToRemove > values.size())
|
||||
howManyToRemove = values.size();
|
||||
|
||||
numUsed -= howManyToRemove;
|
||||
minimiseStorageAfterRemoval();
|
||||
values.removeElements (values.size() - howManyToRemove, howManyToRemove);
|
||||
minimiseStorageAfterRemoval();
|
||||
}
|
||||
}
|
||||
|
||||
/** Removes any elements which are also in another array.
|
||||
@ -1054,8 +921,8 @@ public:
|
||||
{
|
||||
if (otherArray.size() > 0)
|
||||
{
|
||||
for (int i = numUsed; --i >= 0;)
|
||||
if (otherArray.contains (data.elements[i]))
|
||||
for (int i = values.size(); --i >= 0;)
|
||||
if (otherArray.contains (values[i]))
|
||||
removeInternal (i);
|
||||
}
|
||||
}
|
||||
@ -1082,8 +949,8 @@ public:
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = numUsed; --i >= 0;)
|
||||
if (! otherArray.contains (data.elements[i]))
|
||||
for (int i = values.size(); --i >= 0;)
|
||||
if (! otherArray.contains (values[i]))
|
||||
removeInternal (i);
|
||||
}
|
||||
}
|
||||
@ -1100,13 +967,7 @@ public:
|
||||
void swap (int index1, int index2)
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
|
||||
if (isPositiveAndBelow (index1, numUsed)
|
||||
&& isPositiveAndBelow (index2, numUsed))
|
||||
{
|
||||
std::swap (data.elements[index1],
|
||||
data.elements[index2]);
|
||||
}
|
||||
values.swap (index1, index2);
|
||||
}
|
||||
|
||||
/** Moves one of the values to a different position.
|
||||
@ -1128,30 +989,7 @@ public:
|
||||
if (currentIndex != newIndex)
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
|
||||
if (isPositiveAndBelow (currentIndex, numUsed))
|
||||
{
|
||||
if (! isPositiveAndBelow (newIndex, numUsed))
|
||||
newIndex = numUsed - 1;
|
||||
|
||||
char tempCopy[sizeof (ElementType)];
|
||||
memcpy (tempCopy, data.elements + currentIndex, sizeof (ElementType));
|
||||
|
||||
if (newIndex > currentIndex)
|
||||
{
|
||||
memmove (data.elements + currentIndex,
|
||||
data.elements + currentIndex + 1,
|
||||
sizeof (ElementType) * (size_t) (newIndex - currentIndex));
|
||||
}
|
||||
else
|
||||
{
|
||||
memmove (data.elements + newIndex + 1,
|
||||
data.elements + newIndex,
|
||||
sizeof (ElementType) * (size_t) (currentIndex - newIndex));
|
||||
}
|
||||
|
||||
memcpy (data.elements + newIndex, tempCopy, sizeof (ElementType));
|
||||
}
|
||||
values.move (currentIndex, newIndex);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1165,7 +1003,7 @@ public:
|
||||
void minimiseStorageOverheads()
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
data.shrinkToNoMoreThan (numUsed);
|
||||
values.shrinkToNoMoreThan (values.size());
|
||||
}
|
||||
|
||||
/** Increases the array's internal storage to hold a minimum number of elements.
|
||||
@ -1174,10 +1012,10 @@ public:
|
||||
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)
|
||||
void ensureStorageAllocated (int minNumElements)
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
data.ensureAllocatedSize (minNumElements);
|
||||
values.ensureAllocatedSize (minNumElements);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
@ -1219,12 +1057,12 @@ public:
|
||||
*/
|
||||
template <class ElementComparator>
|
||||
void sort (ElementComparator& comparator,
|
||||
const bool retainOrderOfEquivalentItems = false)
|
||||
bool retainOrderOfEquivalentItems = false)
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
ignoreUnused (comparator); // if you pass in an object with a static compareElements() method, this
|
||||
// avoids getting warning messages about the parameter being unused
|
||||
sortArray (comparator, data.elements.get(), 0, size() - 1, retainOrderOfEquivalentItems);
|
||||
sortArray (comparator, values.begin(), 0, size() - 1, retainOrderOfEquivalentItems);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
@ -1232,7 +1070,7 @@ public:
|
||||
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; }
|
||||
inline const TypeOfCriticalSectionToUse& getLock() const noexcept { return values; }
|
||||
|
||||
/** Returns the type of scoped lock to use for locking this array */
|
||||
using ScopedLockType = typename TypeOfCriticalSectionToUse::ScopedLockType;
|
||||
@ -1247,49 +1085,18 @@ public:
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
ArrayAllocationBase <ElementType, TypeOfCriticalSectionToUse> data;
|
||||
int numUsed = 0;
|
||||
ArrayBase<ElementType, TypeOfCriticalSectionToUse> values;
|
||||
|
||||
void removeInternal (const int indexToRemove)
|
||||
void removeInternal (int indexToRemove)
|
||||
{
|
||||
--numUsed;
|
||||
auto* e = data.elements + indexToRemove;
|
||||
e->~ElementType();
|
||||
auto numberToShift = numUsed - indexToRemove;
|
||||
|
||||
if (numberToShift > 0)
|
||||
memmove (e, e + 1, ((size_t) numberToShift) * sizeof (ElementType));
|
||||
|
||||
values.removeElements (indexToRemove, 1);
|
||||
minimiseStorageAfterRemoval();
|
||||
}
|
||||
|
||||
inline void deleteAllElements() noexcept
|
||||
{
|
||||
for (int i = 0; i < numUsed; ++i)
|
||||
data.elements[i].~ElementType();
|
||||
}
|
||||
|
||||
void minimiseStorageAfterRemoval()
|
||||
{
|
||||
if (data.numAllocated > jmax (minimumAllocatedSize, numUsed * 2))
|
||||
data.shrinkToNoMoreThan (jmax (numUsed, jmax (minimumAllocatedSize, 64 / (int) sizeof (ElementType))));
|
||||
}
|
||||
|
||||
void addAssumingCapacityIsReady (const ElementType& e) { new (data.elements + numUsed++) ElementType (e); }
|
||||
void addAssumingCapacityIsReady (ElementType&& e) { new (data.elements + numUsed++) ElementType (static_cast<ElementType&&> (e)); }
|
||||
|
||||
template <typename... OtherElements>
|
||||
void addAssumingCapacityIsReady (const ElementType& firstNewElement, OtherElements... otherElements)
|
||||
{
|
||||
addAssumingCapacityIsReady (firstNewElement);
|
||||
addAssumingCapacityIsReady (otherElements...);
|
||||
}
|
||||
|
||||
template <typename... OtherElements>
|
||||
void addAssumingCapacityIsReady (ElementType&& firstNewElement, OtherElements... otherElements)
|
||||
{
|
||||
addAssumingCapacityIsReady (static_cast<ElementType&&> (firstNewElement));
|
||||
addAssumingCapacityIsReady (otherElements...);
|
||||
if (values.capacity() > jmax (minimumAllocatedSize, values.size() * 2))
|
||||
values.shrinkToNoMoreThan (jmax (values.size(), jmax (minimumAllocatedSize, 64 / (int) sizeof (ElementType))));
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -27,13 +27,9 @@ namespace juce
|
||||
/**
|
||||
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
|
||||
This class isn't really for public use - it used to be part of the
|
||||
container classes but has since been superseded by ArrayBase. Eventually
|
||||
it will be removed from the API.
|
||||
|
||||
@tags{Core}
|
||||
*/
|
||||
@ -43,24 +39,20 @@ class ArrayAllocationBase : public TypeOfCriticalSectionToUse
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates an empty array. */
|
||||
ArrayAllocationBase() noexcept
|
||||
{
|
||||
}
|
||||
ArrayAllocationBase() = default;
|
||||
|
||||
/** Destructor. */
|
||||
~ArrayAllocationBase() noexcept
|
||||
{
|
||||
}
|
||||
~ArrayAllocationBase() = default;
|
||||
|
||||
ArrayAllocationBase (ArrayAllocationBase&& other) noexcept
|
||||
: elements (static_cast<HeapBlock<ElementType>&&> (other.elements)),
|
||||
: elements (std::move (other.elements)),
|
||||
numAllocated (other.numAllocated)
|
||||
{
|
||||
}
|
||||
|
||||
ArrayAllocationBase& operator= (ArrayAllocationBase&& other) noexcept
|
||||
{
|
||||
elements = static_cast<HeapBlock<ElementType>&&> (other.elements);
|
||||
elements = std::move (other.elements);
|
||||
numAllocated = other.numAllocated;
|
||||
return *this;
|
||||
}
|
||||
|
600
modules/juce_core/containers/juce_ArrayBase.cpp
Normal file
600
modules/juce_core/containers/juce_ArrayBase.cpp
Normal file
@ -0,0 +1,600 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2018 - ROLI Ltd.
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
#if JUCE_UNIT_TESTS
|
||||
|
||||
namespace ArrayBaseTestsHelpers
|
||||
{
|
||||
class TriviallyCopyableType
|
||||
{
|
||||
public:
|
||||
TriviallyCopyableType() = default;
|
||||
|
||||
TriviallyCopyableType (int v)
|
||||
: value (v)
|
||||
{}
|
||||
|
||||
TriviallyCopyableType (float v)
|
||||
: value ((int) v)
|
||||
{}
|
||||
|
||||
bool operator== (const TriviallyCopyableType& other) const
|
||||
{
|
||||
return getValue() == other.getValue();
|
||||
}
|
||||
|
||||
int getValue() const { return value; }
|
||||
|
||||
private:
|
||||
int value { -1111 };
|
||||
};
|
||||
|
||||
class NonTriviallyCopyableType
|
||||
{
|
||||
public:
|
||||
NonTriviallyCopyableType() = default;
|
||||
|
||||
NonTriviallyCopyableType (int v)
|
||||
: value (v)
|
||||
{}
|
||||
|
||||
NonTriviallyCopyableType (float v)
|
||||
: value ((int) v)
|
||||
{}
|
||||
|
||||
NonTriviallyCopyableType (const NonTriviallyCopyableType& other)
|
||||
: value (other.value)
|
||||
{}
|
||||
|
||||
NonTriviallyCopyableType& operator= (const NonTriviallyCopyableType& other)
|
||||
{
|
||||
value = other.value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool operator== (const NonTriviallyCopyableType& other) const
|
||||
{
|
||||
return getValue() == other.getValue();
|
||||
}
|
||||
|
||||
int getValue() const { return *ptr; }
|
||||
|
||||
private:
|
||||
int value { -1111 };
|
||||
int* ptr = &value;
|
||||
};
|
||||
}
|
||||
|
||||
bool operator== (const ArrayBaseTestsHelpers::TriviallyCopyableType& tct,
|
||||
const ArrayBaseTestsHelpers::NonTriviallyCopyableType& ntct)
|
||||
{
|
||||
return tct.getValue() == ntct.getValue();
|
||||
}
|
||||
|
||||
bool operator== (const ArrayBaseTestsHelpers::NonTriviallyCopyableType& ntct,
|
||||
const ArrayBaseTestsHelpers::TriviallyCopyableType& tct)
|
||||
{
|
||||
return tct == ntct;
|
||||
}
|
||||
|
||||
class ArrayBaseTests : public UnitTest
|
||||
{
|
||||
using CopyableType = ArrayBaseTestsHelpers::TriviallyCopyableType;
|
||||
using NoncopyableType = ArrayBaseTestsHelpers::NonTriviallyCopyableType;
|
||||
|
||||
#if ! (defined(__GNUC__) && __GNUC__ < 5 && ! defined(__clang__))
|
||||
static_assert (std::is_trivially_copyable<CopyableType>::value,
|
||||
"Test TriviallyCopyableType is not trivially copyable");
|
||||
static_assert (! std::is_trivially_copyable<NoncopyableType>::value,
|
||||
"Test NonTriviallyCopyableType is trivially copyable");
|
||||
#endif
|
||||
|
||||
public:
|
||||
ArrayBaseTests()
|
||||
: UnitTest ("ArrayBase", "Containers")
|
||||
{}
|
||||
|
||||
void runTest() override
|
||||
{
|
||||
beginTest ("grow capacity");
|
||||
{
|
||||
std::vector<CopyableType> referenceContainer;
|
||||
ArrayBase<CopyableType, DummyCriticalSection> copyableContainer;
|
||||
ArrayBase<NoncopyableType, DummyCriticalSection> noncopyableContainer;
|
||||
|
||||
checkEqual (copyableContainer, noncopyableContainer, referenceContainer);
|
||||
|
||||
int originalCapacity = 4;
|
||||
referenceContainer.reserve ((size_t) originalCapacity);
|
||||
expectEquals ((int) referenceContainer.capacity(), originalCapacity);
|
||||
copyableContainer.setAllocatedSize (originalCapacity);
|
||||
expectEquals (copyableContainer.capacity(), originalCapacity);
|
||||
noncopyableContainer.setAllocatedSize (originalCapacity);
|
||||
expectEquals (noncopyableContainer.capacity(), originalCapacity);
|
||||
|
||||
checkEqual (copyableContainer, noncopyableContainer, referenceContainer);
|
||||
|
||||
addData (referenceContainer, copyableContainer, noncopyableContainer, 33);
|
||||
|
||||
checkEqual (copyableContainer, noncopyableContainer, referenceContainer);
|
||||
|
||||
expect ((int) referenceContainer.capacity() != originalCapacity);
|
||||
expect (copyableContainer.capacity() != originalCapacity);
|
||||
expect (noncopyableContainer.capacity() != originalCapacity);
|
||||
}
|
||||
|
||||
beginTest ("shrink capacity");
|
||||
{
|
||||
std::vector<CopyableType> referenceContainer;
|
||||
ArrayBase<CopyableType, DummyCriticalSection> copyableContainer;
|
||||
ArrayBase<NoncopyableType, DummyCriticalSection> noncopyableContainer;
|
||||
|
||||
int numElements = 45;
|
||||
addData (referenceContainer, copyableContainer, noncopyableContainer, numElements);
|
||||
|
||||
copyableContainer.shrinkToNoMoreThan (numElements);
|
||||
noncopyableContainer.setAllocatedSize (numElements + 1);
|
||||
|
||||
checkEqual (copyableContainer, noncopyableContainer, referenceContainer);
|
||||
|
||||
referenceContainer.clear();
|
||||
copyableContainer.removeElements (0, numElements);
|
||||
noncopyableContainer.removeElements (0, numElements);
|
||||
|
||||
checkEqual (copyableContainer, noncopyableContainer, referenceContainer);
|
||||
|
||||
copyableContainer.setAllocatedSize (0);
|
||||
noncopyableContainer.setAllocatedSize (0);
|
||||
|
||||
checkEqual (copyableContainer, noncopyableContainer, referenceContainer);
|
||||
|
||||
addData (referenceContainer, copyableContainer, noncopyableContainer, numElements);
|
||||
|
||||
checkEqual (copyableContainer, noncopyableContainer, referenceContainer);
|
||||
}
|
||||
|
||||
beginTest ("equality");
|
||||
{
|
||||
std::vector<int> referenceContainer = { 1, 2, 3 };
|
||||
ArrayBase<int, DummyCriticalSection> testContainer1, testContainer2;
|
||||
|
||||
for (auto i : referenceContainer)
|
||||
{
|
||||
testContainer1.add (i);
|
||||
testContainer2.add (i);
|
||||
}
|
||||
|
||||
expect (testContainer1 == referenceContainer);
|
||||
expect (testContainer2 == testContainer1);
|
||||
|
||||
testContainer1.ensureAllocatedSize (257);
|
||||
referenceContainer.shrink_to_fit();
|
||||
|
||||
expect (testContainer1 == referenceContainer);
|
||||
expect (testContainer2 == testContainer1);
|
||||
|
||||
testContainer1.removeElements (0, 1);
|
||||
|
||||
expect (testContainer1 != referenceContainer);
|
||||
expect (testContainer2 != testContainer1);
|
||||
}
|
||||
|
||||
beginTest ("accessors");
|
||||
{
|
||||
std::vector<CopyableType> referenceContainer;
|
||||
ArrayBase<CopyableType, DummyCriticalSection> copyableContainer;
|
||||
ArrayBase<NoncopyableType, DummyCriticalSection> noncopyableContainer;
|
||||
|
||||
addData (referenceContainer, copyableContainer, noncopyableContainer, 3);
|
||||
|
||||
int testValue = -123;
|
||||
referenceContainer[0] = testValue;
|
||||
copyableContainer[0] = testValue;
|
||||
noncopyableContainer[0] = testValue;
|
||||
|
||||
checkEqual (copyableContainer, noncopyableContainer, referenceContainer);
|
||||
|
||||
expect (copyableContainer .getFirst().getValue() == testValue);
|
||||
expect (noncopyableContainer.getFirst().getValue() == testValue);
|
||||
|
||||
auto last = referenceContainer.back().getValue();
|
||||
|
||||
expectEquals (copyableContainer .getLast().getValue(), last);
|
||||
expectEquals (noncopyableContainer.getLast().getValue(), last);
|
||||
|
||||
ArrayBase<CopyableType, DummyCriticalSection> copyableEmpty;
|
||||
ArrayBase<NoncopyableType, DummyCriticalSection> noncopyableEmpty;
|
||||
|
||||
auto defualtValue = CopyableType().getValue();
|
||||
expectEquals (defualtValue, NoncopyableType().getValue());
|
||||
|
||||
expectEquals (copyableEmpty .getFirst().getValue(), defualtValue);
|
||||
expectEquals (noncopyableEmpty.getFirst().getValue(), defualtValue);
|
||||
expectEquals (copyableEmpty .getLast() .getValue(), defualtValue);
|
||||
expectEquals (noncopyableEmpty.getLast() .getValue(), defualtValue);
|
||||
|
||||
ArrayBase<float*, DummyCriticalSection> floatPointers;
|
||||
expect (floatPointers.getValueWithDefault (-3) == nullptr);
|
||||
}
|
||||
|
||||
beginTest ("add moved");
|
||||
{
|
||||
std::vector<CopyableType> referenceContainer;
|
||||
ArrayBase<CopyableType, DummyCriticalSection> copyableContainer;
|
||||
ArrayBase<NoncopyableType, DummyCriticalSection> noncopyableContainer;
|
||||
|
||||
for (int i = 0; i < 5; ++i)
|
||||
{
|
||||
CopyableType ref (-i);
|
||||
CopyableType ct (-i);
|
||||
NoncopyableType nct (-i);
|
||||
referenceContainer.push_back (std::move (ref));
|
||||
copyableContainer.add (std::move (ct));
|
||||
noncopyableContainer.add (std::move (nct));
|
||||
}
|
||||
|
||||
checkEqual (copyableContainer, noncopyableContainer, referenceContainer);
|
||||
}
|
||||
|
||||
beginTest ("add multiple");
|
||||
{
|
||||
std::vector<CopyableType> referenceContainer;
|
||||
ArrayBase<CopyableType, DummyCriticalSection> copyableContainer;
|
||||
ArrayBase<NoncopyableType, DummyCriticalSection> noncopyableContainer;
|
||||
|
||||
for (int i = 4; i < 7; ++i)
|
||||
referenceContainer.push_back ({ -i });
|
||||
|
||||
copyableContainer.add (CopyableType (-4), CopyableType (-5), CopyableType (-6));
|
||||
noncopyableContainer.add (NoncopyableType (-4), NoncopyableType (-5), NoncopyableType (-6));
|
||||
|
||||
checkEqual (copyableContainer, noncopyableContainer, referenceContainer);
|
||||
}
|
||||
|
||||
beginTest ("add array from a pointer");
|
||||
{
|
||||
ArrayBase<CopyableType, DummyCriticalSection> copyableContainer;
|
||||
ArrayBase<NoncopyableType, DummyCriticalSection> noncopyableContainer;
|
||||
|
||||
std::vector<CopyableType> copyableData = { 3, 4, 5 };
|
||||
std::vector<NoncopyableType> noncopyableData = { 3, 4, 5 };
|
||||
|
||||
copyableContainer.addArray (copyableData.data(), (int) copyableData.size());
|
||||
noncopyableContainer.addArray (noncopyableData.data(), (int) noncopyableData.size());
|
||||
|
||||
checkEqual (copyableContainer, noncopyableContainer, copyableData);
|
||||
}
|
||||
|
||||
beginTest ("add array from a pointer of a different type");
|
||||
{
|
||||
std::vector<CopyableType> referenceContainer;
|
||||
ArrayBase<CopyableType, DummyCriticalSection> copyableContainer;
|
||||
ArrayBase<NoncopyableType, DummyCriticalSection> noncopyableContainer;
|
||||
|
||||
std::vector<float> floatData = { 1.4f, 2.5f, 3.6f };
|
||||
|
||||
for (auto f : floatData)
|
||||
referenceContainer.push_back ({ f });
|
||||
|
||||
copyableContainer.addArray (floatData.data(), (int) floatData.size());
|
||||
noncopyableContainer.addArray (floatData.data(), (int) floatData.size());
|
||||
|
||||
checkEqual (copyableContainer, noncopyableContainer, referenceContainer);
|
||||
}
|
||||
|
||||
beginTest ("add array from initilizer list");
|
||||
{
|
||||
std::vector<CopyableType> referenceContainer;
|
||||
ArrayBase<CopyableType, DummyCriticalSection> copyableContainer;
|
||||
ArrayBase<NoncopyableType, DummyCriticalSection> noncopyableContainer;
|
||||
|
||||
std::initializer_list<CopyableType> ilct { { 3 }, { 4 }, { 5 } };
|
||||
std::initializer_list<NoncopyableType> ilnct { { 3 }, { 4 }, { 5 } };
|
||||
|
||||
for (auto v : ilct)
|
||||
referenceContainer.push_back ({ v });
|
||||
|
||||
copyableContainer.addArray (ilct);
|
||||
noncopyableContainer.addArray (ilnct);
|
||||
|
||||
checkEqual (copyableContainer, noncopyableContainer, referenceContainer);
|
||||
}
|
||||
|
||||
beginTest ("add array from containers");
|
||||
{
|
||||
std::vector<CopyableType> referenceContainer;
|
||||
ArrayBase<CopyableType, DummyCriticalSection> copyableContainer;
|
||||
ArrayBase<NoncopyableType, DummyCriticalSection> noncopyableContainer;
|
||||
|
||||
addData (referenceContainer, copyableContainer, noncopyableContainer, 5);
|
||||
|
||||
std::vector<CopyableType> referenceContainerCopy (referenceContainer);
|
||||
std::vector<NoncopyableType> noncopyableReferenceContainerCopy;
|
||||
ArrayBase<CopyableType, DummyCriticalSection> copyableContainerCopy;
|
||||
ArrayBase<NoncopyableType, DummyCriticalSection> noncopyableContainerCopy;
|
||||
|
||||
for (auto& v : referenceContainerCopy)
|
||||
noncopyableReferenceContainerCopy.push_back ({ v.getValue() });
|
||||
|
||||
for (size_t i = 0; i < referenceContainerCopy.size(); ++i)
|
||||
{
|
||||
auto value = referenceContainerCopy[i].getValue();
|
||||
copyableContainerCopy.add ({ value });
|
||||
noncopyableContainerCopy.add ({ value });
|
||||
}
|
||||
|
||||
// From self-types
|
||||
copyableContainer.addArray (copyableContainerCopy);
|
||||
noncopyableContainer.addArray (noncopyableContainerCopy);
|
||||
|
||||
for (auto v : referenceContainerCopy)
|
||||
referenceContainer.push_back (v);
|
||||
|
||||
checkEqual (copyableContainer, noncopyableContainer, referenceContainer);
|
||||
|
||||
// From std containers
|
||||
copyableContainer.addArray (referenceContainerCopy);
|
||||
noncopyableContainer.addArray (noncopyableReferenceContainerCopy);
|
||||
|
||||
for (auto v : referenceContainerCopy)
|
||||
referenceContainer.push_back (v);
|
||||
|
||||
checkEqual (copyableContainer, noncopyableContainer, referenceContainer);
|
||||
|
||||
// From std containers with offset
|
||||
int offset = 5;
|
||||
copyableContainer.addArray (referenceContainerCopy, offset);
|
||||
noncopyableContainer.addArray (noncopyableReferenceContainerCopy, offset);
|
||||
|
||||
for (size_t i = 5; i < referenceContainerCopy.size(); ++i)
|
||||
referenceContainer.push_back (referenceContainerCopy[i]);
|
||||
|
||||
checkEqual (copyableContainer, noncopyableContainer, referenceContainer);
|
||||
}
|
||||
|
||||
beginTest ("insert");
|
||||
{
|
||||
std::vector<CopyableType> referenceContainer;
|
||||
ArrayBase<CopyableType, DummyCriticalSection> copyableContainer;
|
||||
ArrayBase<NoncopyableType, DummyCriticalSection> noncopyableContainer;
|
||||
|
||||
addData (referenceContainer, copyableContainer, noncopyableContainer, 8);
|
||||
|
||||
referenceContainer.insert (referenceContainer.begin(), -4);
|
||||
copyableContainer.insert (0, -4, 1);
|
||||
noncopyableContainer.insert (0, -4, 1);
|
||||
|
||||
checkEqual (copyableContainer, noncopyableContainer, referenceContainer);
|
||||
|
||||
for (int i = 0; i < 3; ++i)
|
||||
referenceContainer.insert (referenceContainer.begin() + 1, -3);
|
||||
|
||||
copyableContainer.insert (1, -3, 3);
|
||||
noncopyableContainer.insert (1, -3, 3);
|
||||
|
||||
checkEqual (copyableContainer, noncopyableContainer, referenceContainer);
|
||||
|
||||
for (int i = 0; i < 50; ++i)
|
||||
referenceContainer.insert (referenceContainer.end() - 1, -9);
|
||||
|
||||
copyableContainer.insert (copyableContainer.size() - 2, -9, 50);
|
||||
noncopyableContainer.insert (noncopyableContainer.size() - 2, -9, 50);
|
||||
}
|
||||
|
||||
beginTest ("insert array");
|
||||
{
|
||||
ArrayBase<CopyableType, DummyCriticalSection> copyableContainer;
|
||||
ArrayBase<NoncopyableType, DummyCriticalSection> noncopyableContainer;
|
||||
|
||||
std::vector<CopyableType> copyableData = { 3, 4, 5, 6, 7, 8 };
|
||||
std::vector<NoncopyableType> noncopyableData = { 3, 4, 5, 6, 7, 8 };
|
||||
|
||||
std::vector<CopyableType> referenceContainer { copyableData };
|
||||
|
||||
copyableContainer.insertArray (0, copyableData.data(), (int) copyableData.size());
|
||||
noncopyableContainer.insertArray (0, noncopyableData.data(), (int) noncopyableData.size());
|
||||
|
||||
checkEqual (copyableContainer, noncopyableContainer, referenceContainer);
|
||||
|
||||
int insertPos = copyableContainer.size() - 1;
|
||||
|
||||
for (auto it = copyableData.end(); it != copyableData.begin(); --it)
|
||||
referenceContainer.insert (referenceContainer.begin() + insertPos, CopyableType (*(it - 1)));
|
||||
|
||||
copyableContainer.insertArray (insertPos, copyableData.data(), (int) copyableData.size());
|
||||
noncopyableContainer.insertArray (insertPos, noncopyableData.data(), (int) noncopyableData.size());
|
||||
|
||||
checkEqual (copyableContainer, noncopyableContainer, referenceContainer);
|
||||
}
|
||||
|
||||
beginTest ("remove");
|
||||
{
|
||||
std::vector<CopyableType> referenceContainer;
|
||||
ArrayBase<CopyableType, DummyCriticalSection> copyableContainer;
|
||||
ArrayBase<NoncopyableType, DummyCriticalSection> noncopyableContainer;
|
||||
|
||||
addData (referenceContainer, copyableContainer, noncopyableContainer, 17);
|
||||
|
||||
for (int i = 0; i < 4; ++i)
|
||||
{
|
||||
referenceContainer.erase (referenceContainer.begin() + i);
|
||||
copyableContainer.removeElements (i, 1);
|
||||
noncopyableContainer.removeElements (i, 1);
|
||||
}
|
||||
|
||||
checkEqual (copyableContainer, noncopyableContainer, referenceContainer);
|
||||
|
||||
addData (referenceContainer, copyableContainer, noncopyableContainer, 17);
|
||||
int blockSize = 3;
|
||||
|
||||
for (int i = 0; i < 4; ++i)
|
||||
{
|
||||
for (int j = 0; j < blockSize; ++j)
|
||||
referenceContainer.erase (referenceContainer.begin() + i);
|
||||
|
||||
copyableContainer.removeElements (i, blockSize);
|
||||
noncopyableContainer.removeElements (i, blockSize);
|
||||
}
|
||||
|
||||
checkEqual (copyableContainer, noncopyableContainer, referenceContainer);
|
||||
|
||||
auto numToRemove = copyableContainer.size() - 2;
|
||||
|
||||
for (int i = 0; i < numToRemove; ++i)
|
||||
referenceContainer.erase (referenceContainer.begin() + 1);
|
||||
|
||||
copyableContainer.removeElements (1, numToRemove);
|
||||
noncopyableContainer.removeElements (1, numToRemove);
|
||||
|
||||
checkEqual (copyableContainer, noncopyableContainer, referenceContainer);
|
||||
}
|
||||
|
||||
beginTest ("move");
|
||||
{
|
||||
std::vector<CopyableType> referenceContainer;
|
||||
ArrayBase<CopyableType, DummyCriticalSection> copyableContainer;
|
||||
ArrayBase<NoncopyableType, DummyCriticalSection> noncopyableContainer;
|
||||
|
||||
addData (referenceContainer, copyableContainer, noncopyableContainer, 6);
|
||||
|
||||
std::vector<std::pair<int, int>> testValues;
|
||||
testValues.emplace_back (2, 4);
|
||||
testValues.emplace_back (0, 5);
|
||||
testValues.emplace_back (4, 1);
|
||||
testValues.emplace_back (5, 0);
|
||||
|
||||
for (auto p : testValues)
|
||||
{
|
||||
if (p.second > p.first)
|
||||
std::rotate (referenceContainer.begin() + p.first,
|
||||
referenceContainer.begin() + p.first + 1,
|
||||
referenceContainer.begin() + p.second + 1);
|
||||
else
|
||||
std::rotate (referenceContainer.begin() + p.second,
|
||||
referenceContainer.begin() + p.first,
|
||||
referenceContainer.begin() + p.first + 1);
|
||||
|
||||
copyableContainer.move (p.first, p.second);
|
||||
noncopyableContainer.move (p.first, p.second);
|
||||
|
||||
checkEqual (copyableContainer, noncopyableContainer, referenceContainer);
|
||||
}
|
||||
}
|
||||
|
||||
beginTest ("After converting move construction, ownership is transferred");
|
||||
{
|
||||
Derived obj;
|
||||
ArrayBase<Derived*, DummyCriticalSection> derived;
|
||||
derived.setAllocatedSize (5);
|
||||
derived.add (&obj);
|
||||
|
||||
ArrayBase<Base*, DummyCriticalSection> base { std::move (derived) };
|
||||
|
||||
expectEquals (base.capacity(), 5);
|
||||
expectEquals (base.size(), 1);
|
||||
expect (base.getFirst() == &obj);
|
||||
expectEquals (derived.capacity(), 0);
|
||||
expectEquals (derived.size(), 0);
|
||||
expect (derived.data() == nullptr);
|
||||
}
|
||||
|
||||
beginTest ("After converting move assignment, ownership is transferred");
|
||||
{
|
||||
Derived obj;
|
||||
ArrayBase<Derived*, DummyCriticalSection> derived;
|
||||
derived.setAllocatedSize (5);
|
||||
derived.add (&obj);
|
||||
ArrayBase<Base*, DummyCriticalSection> base;
|
||||
|
||||
base = std::move (derived);
|
||||
|
||||
expectEquals (base.capacity(), 5);
|
||||
expectEquals (base.size(), 1);
|
||||
expect (base.getFirst() == &obj);
|
||||
expectEquals (derived.capacity(), 0);
|
||||
expectEquals (derived.size(), 0);
|
||||
expect (derived.data() == nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
struct Base
|
||||
{
|
||||
virtual ~Base() = default;
|
||||
};
|
||||
|
||||
struct Derived : Base
|
||||
{
|
||||
};
|
||||
|
||||
static void addData (std::vector<CopyableType>& referenceContainer,
|
||||
ArrayBase<CopyableType, DummyCriticalSection>& copyableContainer,
|
||||
ArrayBase<NoncopyableType, DummyCriticalSection>& NoncopyableContainer,
|
||||
int numValues)
|
||||
{
|
||||
for (int i = 0; i < numValues; ++i)
|
||||
{
|
||||
referenceContainer.push_back ({ i });
|
||||
copyableContainer.add ({ i });
|
||||
NoncopyableContainer.add ({ i });
|
||||
}
|
||||
}
|
||||
|
||||
template<typename A, typename B>
|
||||
void checkEqual (const ArrayBase<A, DummyCriticalSection>& a,
|
||||
const ArrayBase<B, DummyCriticalSection>& b)
|
||||
{
|
||||
expectEquals ((int) a.size(), (int) b.size());
|
||||
|
||||
for (int i = 0; i < (int) a.size(); ++i)
|
||||
expect (a[i] == b[i]);
|
||||
}
|
||||
|
||||
template<typename A, typename B>
|
||||
void checkEqual (ArrayBase<A, DummyCriticalSection>& a,
|
||||
std::vector<B>& b)
|
||||
{
|
||||
expectEquals ((int) a.size(), (int) b.size());
|
||||
|
||||
for (int i = 0; i < (int) a.size(); ++i)
|
||||
expect (a[i] == b[(size_t) i]);
|
||||
}
|
||||
|
||||
template<typename A, typename B, typename C>
|
||||
void checkEqual (ArrayBase<A, DummyCriticalSection>& a,
|
||||
ArrayBase<B, DummyCriticalSection>& b,
|
||||
std::vector<C>& c)
|
||||
{
|
||||
checkEqual (a, b);
|
||||
checkEqual (a, c);
|
||||
checkEqual (b, c);
|
||||
}
|
||||
};
|
||||
|
||||
static ArrayBaseTests arrayBaseTests;
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace juce
|
596
modules/juce_core/containers/juce_ArrayBase.h
Normal file
596
modules/juce_core/containers/juce_ArrayBase.h
Normal file
@ -0,0 +1,596 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2017 - ROLI Ltd.
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
/**
|
||||
A basic object container.
|
||||
|
||||
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
|
||||
|
||||
@tags{Core}
|
||||
*/
|
||||
template <class ElementType, class TypeOfCriticalSectionToUse>
|
||||
class ArrayBase : public TypeOfCriticalSectionToUse
|
||||
{
|
||||
private:
|
||||
using ParameterType = typename TypeHelpers::ParameterType<ElementType>::type;
|
||||
|
||||
template <class OtherElementType, class OtherCriticalSection>
|
||||
using AllowConversion = typename std::enable_if<! std::is_same<std::tuple<ElementType, TypeOfCriticalSectionToUse>,
|
||||
std::tuple<OtherElementType, OtherCriticalSection>>::value>::type;
|
||||
|
||||
public:
|
||||
//==============================================================================
|
||||
ArrayBase() = default;
|
||||
|
||||
~ArrayBase()
|
||||
{
|
||||
clear();
|
||||
}
|
||||
|
||||
ArrayBase (ArrayBase&& other) noexcept
|
||||
: elements (std::move (other.elements)),
|
||||
numAllocated (other.numAllocated),
|
||||
numUsed (other.numUsed)
|
||||
{
|
||||
other.numAllocated = 0;
|
||||
other.numUsed = 0;
|
||||
}
|
||||
|
||||
ArrayBase& operator= (ArrayBase&& other) noexcept
|
||||
{
|
||||
if (this != &other)
|
||||
{
|
||||
auto tmp (std::move (other));
|
||||
swapWith (tmp);
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** Converting move constructor.
|
||||
Only enabled when the other array has a different type to this one.
|
||||
If you see a compile error here, it's probably because you're attempting a conversion that
|
||||
HeapBlock won't allow.
|
||||
*/
|
||||
template <class OtherElementType,
|
||||
class OtherCriticalSection,
|
||||
typename = AllowConversion<OtherElementType, OtherCriticalSection>>
|
||||
ArrayBase (ArrayBase<OtherElementType, OtherCriticalSection>&& other) noexcept
|
||||
: elements (std::move (other.elements)),
|
||||
numAllocated (other.numAllocated),
|
||||
numUsed (other.numUsed)
|
||||
{
|
||||
other.numAllocated = 0;
|
||||
other.numUsed = 0;
|
||||
}
|
||||
|
||||
/** Converting move assignment operator.
|
||||
Only enabled when the other array has a different type to this one.
|
||||
If you see a compile error here, it's probably because you're attempting a conversion that
|
||||
HeapBlock won't allow.
|
||||
*/
|
||||
template <class OtherElementType,
|
||||
class OtherCriticalSection,
|
||||
typename = AllowConversion<OtherElementType, OtherCriticalSection>>
|
||||
ArrayBase& operator= (ArrayBase<OtherElementType, OtherCriticalSection>&& other) noexcept
|
||||
{
|
||||
// No need to worry about assignment to *this, because 'other' must be of a different type.
|
||||
elements = std::move (other.elements);
|
||||
numAllocated = other.numAllocated;
|
||||
numUsed = other.numUsed;
|
||||
|
||||
other.numAllocated = 0;
|
||||
other.numUsed = 0;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
template <class OtherArrayType>
|
||||
bool operator== (const OtherArrayType& other) const noexcept
|
||||
{
|
||||
if (size() != (int) other.size())
|
||||
return false;
|
||||
|
||||
auto* e = begin();
|
||||
|
||||
for (auto& o : other)
|
||||
if (! (*e++ == o))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <class OtherArrayType>
|
||||
bool operator!= (const OtherArrayType& other) const noexcept
|
||||
{
|
||||
return ! operator== (other);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
inline ElementType& operator[] (const int index) const noexcept
|
||||
{
|
||||
jassert (elements != nullptr);
|
||||
jassert (isPositiveAndBelow (index, numUsed));
|
||||
return elements[index];
|
||||
}
|
||||
|
||||
inline ElementType getValueWithDefault (const int index) const noexcept
|
||||
{
|
||||
return isPositiveAndBelow (index, numUsed) ? elements[index] : ElementType();
|
||||
}
|
||||
|
||||
inline ElementType getFirst() const noexcept
|
||||
{
|
||||
return numUsed > 0 ? elements[0] : ElementType();
|
||||
}
|
||||
|
||||
inline ElementType getLast() const noexcept
|
||||
{
|
||||
return numUsed > 0 ? elements[numUsed - 1] : ElementType();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
inline ElementType* begin() const noexcept
|
||||
{
|
||||
return elements;
|
||||
}
|
||||
|
||||
inline ElementType* end() const noexcept
|
||||
{
|
||||
return elements + numUsed;
|
||||
}
|
||||
|
||||
inline ElementType* data() const noexcept
|
||||
{
|
||||
return elements;
|
||||
}
|
||||
|
||||
inline int size() const noexcept
|
||||
{
|
||||
return numUsed;
|
||||
}
|
||||
|
||||
inline int capacity() const noexcept
|
||||
{
|
||||
return numAllocated;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void setAllocatedSize (int numElements)
|
||||
{
|
||||
jassert (numElements >= numUsed);
|
||||
|
||||
if (numAllocated != numElements)
|
||||
{
|
||||
if (numElements > 0)
|
||||
setAllocatedSizeInternal (numElements);
|
||||
else
|
||||
elements.free();
|
||||
}
|
||||
|
||||
numAllocated = numElements;
|
||||
}
|
||||
|
||||
void ensureAllocatedSize (int minNumElements)
|
||||
{
|
||||
if (minNumElements > numAllocated)
|
||||
setAllocatedSize ((minNumElements + minNumElements / 2 + 8) & ~7);
|
||||
|
||||
jassert (numAllocated <= 0 || elements != nullptr);
|
||||
}
|
||||
|
||||
void shrinkToNoMoreThan (int maxNumElements)
|
||||
{
|
||||
if (maxNumElements < numAllocated)
|
||||
setAllocatedSize (maxNumElements);
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
for (int i = 0; i < numUsed; ++i)
|
||||
elements[i].~ElementType();
|
||||
|
||||
numUsed = 0;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void swapWith (ArrayBase& other) noexcept
|
||||
{
|
||||
elements.swapWith (other.elements);
|
||||
std::swap (numAllocated, other.numAllocated);
|
||||
std::swap (numUsed, other.numUsed);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void add (const ElementType& newElement)
|
||||
{
|
||||
checkSourceIsNotAMember (&newElement);
|
||||
ensureAllocatedSize (numUsed + 1);
|
||||
addAssumingCapacityIsReady (newElement);
|
||||
}
|
||||
|
||||
void add (ElementType&& newElement)
|
||||
{
|
||||
checkSourceIsNotAMember (&newElement);
|
||||
ensureAllocatedSize (numUsed + 1);
|
||||
addAssumingCapacityIsReady (std::move (newElement));
|
||||
}
|
||||
|
||||
template <typename... OtherElements>
|
||||
void add (const ElementType& firstNewElement, OtherElements... otherElements)
|
||||
{
|
||||
checkSourceIsNotAMember (&firstNewElement);
|
||||
ensureAllocatedSize (numUsed + 1 + (int) sizeof... (otherElements));
|
||||
addAssumingCapacityIsReady (firstNewElement, otherElements...);
|
||||
}
|
||||
|
||||
template <typename... OtherElements>
|
||||
void add (ElementType&& firstNewElement, OtherElements... otherElements)
|
||||
{
|
||||
checkSourceIsNotAMember (&firstNewElement);
|
||||
ensureAllocatedSize (numUsed + 1 + (int) sizeof... (otherElements));
|
||||
addAssumingCapacityIsReady (std::move (firstNewElement), otherElements...);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
template <typename Type>
|
||||
void addArray (const Type* elementsToAdd, int numElementsToAdd)
|
||||
{
|
||||
ensureAllocatedSize (numUsed + numElementsToAdd);
|
||||
addArrayInternal (elementsToAdd, numElementsToAdd);
|
||||
numUsed += numElementsToAdd;
|
||||
}
|
||||
|
||||
template <typename TypeToCreateFrom>
|
||||
void addArray (const std::initializer_list<TypeToCreateFrom>& items)
|
||||
{
|
||||
ensureAllocatedSize (numUsed + (int) items.size());
|
||||
|
||||
for (auto& item : items)
|
||||
new (elements + numUsed++) ElementType (item);
|
||||
}
|
||||
|
||||
template <class OtherArrayType>
|
||||
void addArray (const OtherArrayType& arrayToAddFrom)
|
||||
{
|
||||
jassert ((const void*) this != (const void*) &arrayToAddFrom); // can't add from our own elements!
|
||||
ensureAllocatedSize (numUsed + (int) arrayToAddFrom.size());
|
||||
|
||||
for (auto& e : arrayToAddFrom)
|
||||
addAssumingCapacityIsReady (e);
|
||||
}
|
||||
|
||||
template <class OtherArrayType>
|
||||
typename std::enable_if<! std::is_pointer<OtherArrayType>::value, int>::type
|
||||
addArray (const OtherArrayType& arrayToAddFrom,
|
||||
int startIndex, int numElementsToAdd = -1)
|
||||
{
|
||||
jassert ((const void*) this != (const void*) &arrayToAddFrom); // can't add from our own elements!
|
||||
|
||||
if (startIndex < 0)
|
||||
{
|
||||
jassertfalse;
|
||||
startIndex = 0;
|
||||
}
|
||||
|
||||
if (numElementsToAdd < 0 || startIndex + numElementsToAdd > (int) arrayToAddFrom.size())
|
||||
numElementsToAdd = (int) arrayToAddFrom.size() - startIndex;
|
||||
|
||||
addArray (arrayToAddFrom.data() + startIndex, numElementsToAdd);
|
||||
|
||||
return numElementsToAdd;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void insert (int indexToInsertAt, ParameterType newElement, int numberOfTimesToInsertIt)
|
||||
{
|
||||
checkSourceIsNotAMember (&newElement);
|
||||
auto* space = createInsertSpace (indexToInsertAt, numberOfTimesToInsertIt);
|
||||
|
||||
for (int i = 0; i < numberOfTimesToInsertIt; ++i)
|
||||
new (space++) ElementType (newElement);
|
||||
|
||||
numUsed += numberOfTimesToInsertIt;
|
||||
}
|
||||
|
||||
void insertArray (int indexToInsertAt, const ElementType* newElements, int numberOfElements)
|
||||
{
|
||||
auto* space = createInsertSpace (indexToInsertAt, numberOfElements);
|
||||
|
||||
for (int i = 0; i < numberOfElements; ++i)
|
||||
new (space++) ElementType (*(newElements++));
|
||||
|
||||
numUsed += numberOfElements;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void removeElements (int indexToRemoveAt, int numElementsToRemove)
|
||||
{
|
||||
jassert (indexToRemoveAt >= 0);
|
||||
jassert (numElementsToRemove >= 0);
|
||||
jassert (indexToRemoveAt + numElementsToRemove <= numUsed);
|
||||
|
||||
if (numElementsToRemove > 0)
|
||||
{
|
||||
removeElementsInternal (indexToRemoveAt, numElementsToRemove);
|
||||
numUsed -= numElementsToRemove;
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void swap (int index1, int index2)
|
||||
{
|
||||
if (isPositiveAndBelow (index1, numUsed)
|
||||
&& isPositiveAndBelow (index2, numUsed))
|
||||
{
|
||||
std::swap (elements[index1],
|
||||
elements[index2]);
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void move (int currentIndex, int newIndex) noexcept
|
||||
{
|
||||
if (isPositiveAndBelow (currentIndex, numUsed))
|
||||
{
|
||||
if (! isPositiveAndBelow (newIndex, numUsed))
|
||||
newIndex = numUsed - 1;
|
||||
|
||||
moveInternal (currentIndex, newIndex);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
template <typename T>
|
||||
#if defined(__GNUC__) && __GNUC__ < 5 && ! defined(__clang__)
|
||||
using IsTriviallyCopyable = std::is_scalar<T>;
|
||||
#else
|
||||
using IsTriviallyCopyable = std::is_trivially_copyable<T>;
|
||||
#endif
|
||||
|
||||
template <typename T>
|
||||
using TriviallyCopyableVoid = typename std::enable_if<IsTriviallyCopyable<T>::value, void>::type;
|
||||
|
||||
template <typename T>
|
||||
using NonTriviallyCopyableVoid = typename std::enable_if<! IsTriviallyCopyable<T>::value, void>::type;
|
||||
|
||||
//==============================================================================
|
||||
template <typename T = ElementType>
|
||||
TriviallyCopyableVoid<T> addArrayInternal (const ElementType* otherElements, int numElements)
|
||||
{
|
||||
memcpy (elements + numUsed, otherElements, (size_t) numElements * sizeof (ElementType));
|
||||
}
|
||||
|
||||
template <typename Type, typename T = ElementType>
|
||||
TriviallyCopyableVoid<T> addArrayInternal (const Type* otherElements, int numElements)
|
||||
{
|
||||
auto* start = elements + numUsed;
|
||||
|
||||
while (--numElements >= 0)
|
||||
new (start++) ElementType (*(otherElements++));
|
||||
}
|
||||
|
||||
template <typename Type, typename T = ElementType>
|
||||
NonTriviallyCopyableVoid<T> addArrayInternal (const Type* otherElements, int numElements)
|
||||
{
|
||||
auto* start = elements + numUsed;
|
||||
|
||||
while (--numElements >= 0)
|
||||
new (start++) ElementType (*(otherElements++));
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
template <typename T = ElementType>
|
||||
TriviallyCopyableVoid<T> setAllocatedSizeInternal (int numElements)
|
||||
{
|
||||
elements.realloc ((size_t) numElements);
|
||||
}
|
||||
|
||||
template <typename T = ElementType>
|
||||
NonTriviallyCopyableVoid<T> setAllocatedSizeInternal (int numElements)
|
||||
{
|
||||
HeapBlock<ElementType> newElements (numElements);
|
||||
|
||||
for (int i = 0; i < numUsed; ++i)
|
||||
{
|
||||
new (newElements + i) ElementType (std::move (elements[i]));
|
||||
elements[i].~ElementType();
|
||||
}
|
||||
|
||||
elements = std::move (newElements);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
ElementType* createInsertSpace (int indexToInsertAt, int numElements)
|
||||
{
|
||||
ensureAllocatedSize (numUsed + numElements);
|
||||
|
||||
if (! isPositiveAndBelow (indexToInsertAt, numUsed))
|
||||
return elements + numUsed;
|
||||
|
||||
createInsertSpaceInternal (indexToInsertAt, numElements);
|
||||
|
||||
return elements + indexToInsertAt;
|
||||
}
|
||||
|
||||
template <typename T = ElementType>
|
||||
TriviallyCopyableVoid<T> createInsertSpaceInternal (int indexToInsertAt, int numElements)
|
||||
{
|
||||
auto* start = elements + indexToInsertAt;
|
||||
auto numElementsToShift = numUsed - indexToInsertAt;
|
||||
memmove (start + numElements, start, (size_t) numElementsToShift * sizeof (ElementType));
|
||||
}
|
||||
|
||||
template <typename T = ElementType>
|
||||
NonTriviallyCopyableVoid<T> createInsertSpaceInternal (int indexToInsertAt, int numElements)
|
||||
{
|
||||
auto* end = elements + numUsed;
|
||||
auto* newEnd = end + numElements;
|
||||
auto numElementsToShift = numUsed - indexToInsertAt;
|
||||
|
||||
for (int i = 0; i < numElementsToShift; ++i)
|
||||
{
|
||||
new (--newEnd) ElementType (std::move (*(--end)));
|
||||
end->~ElementType();
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
template <typename T = ElementType>
|
||||
TriviallyCopyableVoid<T> removeElementsInternal (int indexToRemoveAt, int numElementsToRemove)
|
||||
{
|
||||
auto* start = elements + indexToRemoveAt;
|
||||
auto numElementsToShift = numUsed - (indexToRemoveAt + numElementsToRemove);
|
||||
memmove (start, start + numElementsToRemove, (size_t) numElementsToShift * sizeof (ElementType));
|
||||
}
|
||||
|
||||
template <typename T = ElementType>
|
||||
NonTriviallyCopyableVoid<T> removeElementsInternal (int indexToRemoveAt, int numElementsToRemove)
|
||||
{
|
||||
auto numElementsToShift = numUsed - (indexToRemoveAt + numElementsToRemove);
|
||||
auto* destination = elements + indexToRemoveAt;
|
||||
auto* source = destination + numElementsToRemove;
|
||||
|
||||
for (int i = 0; i < numElementsToShift; ++i)
|
||||
moveAssignElement (destination++, std::move (*(source++)));
|
||||
|
||||
for (int i = 0; i < numElementsToRemove; ++i)
|
||||
(destination++)->~ElementType();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
template <typename T = ElementType>
|
||||
TriviallyCopyableVoid<T> moveInternal (int currentIndex, int newIndex) noexcept
|
||||
{
|
||||
char tempCopy[sizeof (ElementType)];
|
||||
memcpy (tempCopy, elements + currentIndex, sizeof (ElementType));
|
||||
|
||||
if (newIndex > currentIndex)
|
||||
{
|
||||
memmove (elements + currentIndex,
|
||||
elements + currentIndex + 1,
|
||||
sizeof (ElementType) * (size_t) (newIndex - currentIndex));
|
||||
}
|
||||
else
|
||||
{
|
||||
memmove (elements + newIndex + 1,
|
||||
elements + newIndex,
|
||||
sizeof (ElementType) * (size_t) (currentIndex - newIndex));
|
||||
}
|
||||
|
||||
memcpy (elements + newIndex, tempCopy, sizeof (ElementType));
|
||||
}
|
||||
|
||||
template <typename T = ElementType>
|
||||
NonTriviallyCopyableVoid<T> moveInternal (int currentIndex, int newIndex) noexcept
|
||||
{
|
||||
auto* e = elements + currentIndex;
|
||||
ElementType tempCopy (std::move (*e));
|
||||
auto delta = newIndex - currentIndex;
|
||||
|
||||
if (delta > 0)
|
||||
{
|
||||
for (int i = 0; i < delta; ++i)
|
||||
{
|
||||
moveAssignElement (e, std::move (*(e + 1)));
|
||||
++e;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < -delta; ++i)
|
||||
{
|
||||
moveAssignElement (e, std::move (*(e - 1)));
|
||||
--e;
|
||||
}
|
||||
}
|
||||
|
||||
moveAssignElement (e, std::move (tempCopy));
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void addAssumingCapacityIsReady (const ElementType& element) { new (elements + numUsed++) ElementType (element); }
|
||||
void addAssumingCapacityIsReady (ElementType&& element) { new (elements + numUsed++) ElementType (std::move (element)); }
|
||||
|
||||
template <typename... OtherElements>
|
||||
void addAssumingCapacityIsReady (const ElementType& firstNewElement, OtherElements... otherElements)
|
||||
{
|
||||
addAssumingCapacityIsReady (firstNewElement);
|
||||
addAssumingCapacityIsReady (otherElements...);
|
||||
}
|
||||
|
||||
template <typename... OtherElements>
|
||||
void addAssumingCapacityIsReady (ElementType&& firstNewElement, OtherElements... otherElements)
|
||||
{
|
||||
addAssumingCapacityIsReady (std::move (firstNewElement));
|
||||
addAssumingCapacityIsReady (otherElements...);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
template <typename T = ElementType>
|
||||
typename std::enable_if<std::is_move_assignable<T>::value, void>::type
|
||||
moveAssignElement (ElementType* destination, ElementType&& source)
|
||||
{
|
||||
*destination = std::move (source);
|
||||
}
|
||||
|
||||
template <typename T = ElementType>
|
||||
typename std::enable_if<! std::is_move_assignable<T>::value, void>::type
|
||||
moveAssignElement (ElementType* destination, ElementType&& source)
|
||||
{
|
||||
destination->~ElementType();
|
||||
new (destination) ElementType (std::move (source));
|
||||
}
|
||||
|
||||
void checkSourceIsNotAMember (const ElementType* element)
|
||||
{
|
||||
// when you pass a reference to an existing element into a method like add() which
|
||||
// may need to reallocate the array to make more space, the incoming reference may
|
||||
// be deleted indirectly during the reallocation operation! To work around this,
|
||||
// make a local copy of the item you're trying to add (and maybe use std::move to
|
||||
// move it into the add() method to avoid any extra overhead)
|
||||
jassert (element < begin() || element >= end());
|
||||
ignoreUnused (element);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
HeapBlock<ElementType> elements;
|
||||
int numAllocated = 0, numUsed = 0;
|
||||
|
||||
template <class OtherElementType, class OtherCriticalSection>
|
||||
friend class ArrayBase;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE (ArrayBase)
|
||||
};
|
||||
|
||||
} // namespace juce
|
@ -42,7 +42,7 @@ public:
|
||||
//==============================================================================
|
||||
DynamicObject();
|
||||
DynamicObject (const DynamicObject&);
|
||||
~DynamicObject();
|
||||
~DynamicObject() override;
|
||||
|
||||
using Ptr = ReferenceCountedObjectPtr<DynamicObject>;
|
||||
|
||||
|
@ -120,7 +120,7 @@ public:
|
||||
*/
|
||||
explicit HashMap (int numberOfSlots = defaultHashTableSize,
|
||||
HashFunctionType hashFunction = HashFunctionType())
|
||||
: hashFunctionToUse (hashFunction), totalNumItems (0)
|
||||
: hashFunctionToUse (hashFunction)
|
||||
{
|
||||
hashSlots.insertMultiple (0, nullptr, numberOfSlots);
|
||||
}
|
||||
@ -354,7 +354,7 @@ public:
|
||||
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;
|
||||
using ScopedLockType = typename TypeOfCriticalSectionToUse::ScopedLockType;
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
@ -388,7 +388,7 @@ public:
|
||||
}
|
||||
@endcode
|
||||
|
||||
The order in which items are iterated bears no resemblence to the order in which
|
||||
The order in which items are iterated bears no resemblance 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
|
||||
@ -479,7 +479,7 @@ private:
|
||||
|
||||
HashFunctionType hashFunctionToUse;
|
||||
Array<HashEntry*> hashSlots;
|
||||
int totalNumItems;
|
||||
int totalNumItems = 0;
|
||||
TypeOfCriticalSectionToUse lock;
|
||||
|
||||
int generateHashFor (KeyTypeParameter key, int numSlots) const
|
||||
|
@ -70,10 +70,10 @@ class ListenerList
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates an empty list. */
|
||||
ListenerList() {}
|
||||
ListenerList() = default;
|
||||
|
||||
/** Destructor. */
|
||||
~ListenerList() {}
|
||||
~ListenerList() = default;
|
||||
|
||||
//==============================================================================
|
||||
/** Adds a listener to the list.
|
||||
@ -101,7 +101,7 @@ public:
|
||||
/** Returns the number of registered listeners. */
|
||||
int size() const noexcept { return listeners.size(); }
|
||||
|
||||
/** Returns true if any listeners are registered. */
|
||||
/** Returns true if no listeners are registered, false otherwise. */
|
||||
bool isEmpty() const noexcept { return listeners.isEmpty(); }
|
||||
|
||||
/** Clears the list. */
|
||||
@ -194,7 +194,7 @@ public:
|
||||
: list (listToIterate), index (listToIterate.size())
|
||||
{}
|
||||
|
||||
~Iterator() noexcept {}
|
||||
~Iterator() = default;
|
||||
|
||||
//==============================================================================
|
||||
bool next() noexcept
|
||||
|
@ -30,24 +30,24 @@ NamedValueSet::NamedValue::NamedValue (const Identifier& n, const var& v) : nam
|
||||
NamedValueSet::NamedValue::NamedValue (const NamedValue& other) : NamedValue (other.name, other.value) {}
|
||||
|
||||
NamedValueSet::NamedValue::NamedValue (NamedValue&& other) noexcept
|
||||
: NamedValue (static_cast<Identifier&&> (other.name),
|
||||
static_cast<var&&> (other.value))
|
||||
: NamedValue (std::move (other.name),
|
||||
std::move (other.value))
|
||||
{}
|
||||
|
||||
NamedValueSet::NamedValue::NamedValue (const Identifier& n, var&& v) noexcept
|
||||
: name (n), value (static_cast<var&&> (v))
|
||||
: name (n), value (std::move (v))
|
||||
{
|
||||
}
|
||||
|
||||
NamedValueSet::NamedValue::NamedValue (Identifier&& n, var&& v) noexcept
|
||||
: name (static_cast<Identifier&&> (n)),
|
||||
value (static_cast<var&&> (v))
|
||||
: name (std::move (n)),
|
||||
value (std::move (v))
|
||||
{}
|
||||
|
||||
NamedValueSet::NamedValue& NamedValueSet::NamedValue::operator= (NamedValue&& other) noexcept
|
||||
{
|
||||
name = static_cast<Identifier&&> (other.name);
|
||||
value = static_cast<var&&> (other.value);
|
||||
name = std::move (other.name);
|
||||
value = std::move (other.value);
|
||||
return *this;
|
||||
}
|
||||
|
||||
@ -59,7 +59,14 @@ NamedValueSet::NamedValueSet() noexcept {}
|
||||
NamedValueSet::~NamedValueSet() noexcept {}
|
||||
|
||||
NamedValueSet::NamedValueSet (const NamedValueSet& other) : values (other.values) {}
|
||||
NamedValueSet::NamedValueSet (NamedValueSet&& other) noexcept : values (static_cast<Array<NamedValue>&&> (other.values)) {}
|
||||
|
||||
NamedValueSet::NamedValueSet (NamedValueSet&& other) noexcept
|
||||
: values (std::move (other.values)) {}
|
||||
|
||||
NamedValueSet::NamedValueSet (std::initializer_list<NamedValue> list)
|
||||
: values (std::move (list))
|
||||
{
|
||||
}
|
||||
|
||||
NamedValueSet& NamedValueSet::operator= (const NamedValueSet& other)
|
||||
{
|
||||
@ -156,11 +163,11 @@ bool NamedValueSet::set (const Identifier& name, var&& newValue)
|
||||
if (v->equalsWithSameType (newValue))
|
||||
return false;
|
||||
|
||||
*v = static_cast<var&&> (newValue);
|
||||
*v = std::move (newValue);
|
||||
return true;
|
||||
}
|
||||
|
||||
values.add ({ name, static_cast<var&&> (newValue) });
|
||||
values.add ({ name, std::move (newValue) });
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -34,23 +34,6 @@ namespace juce
|
||||
class JUCE_API NamedValueSet
|
||||
{
|
||||
public:
|
||||
/** Creates an empty set. */
|
||||
NamedValueSet() noexcept;
|
||||
|
||||
NamedValueSet (const NamedValueSet&);
|
||||
NamedValueSet (NamedValueSet&&) noexcept;
|
||||
NamedValueSet& operator= (const NamedValueSet&);
|
||||
NamedValueSet& operator= (NamedValueSet&&) noexcept;
|
||||
|
||||
/** Destructor. */
|
||||
~NamedValueSet() noexcept;
|
||||
|
||||
/** Two NamedValueSets are considered equal if they contain all the same key/value
|
||||
pairs, regardless of the order.
|
||||
*/
|
||||
bool operator== (const NamedValueSet&) const noexcept;
|
||||
bool operator!= (const NamedValueSet&) const noexcept;
|
||||
|
||||
//==============================================================================
|
||||
/** Structure for a named var object */
|
||||
struct JUCE_API NamedValue
|
||||
@ -73,6 +56,27 @@ public:
|
||||
var value;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** Creates an empty set. */
|
||||
NamedValueSet() noexcept;
|
||||
|
||||
NamedValueSet (const NamedValueSet&);
|
||||
NamedValueSet (NamedValueSet&&) noexcept;
|
||||
NamedValueSet& operator= (const NamedValueSet&);
|
||||
NamedValueSet& operator= (NamedValueSet&&) noexcept;
|
||||
|
||||
/** Creates a NamedValueSet from a list of names and properties. */
|
||||
NamedValueSet (std::initializer_list<NamedValue>);
|
||||
|
||||
/** Destructor. */
|
||||
~NamedValueSet() noexcept;
|
||||
|
||||
/** Two NamedValueSets are considered equal if they contain all the same key/value
|
||||
pairs, regardless of the order.
|
||||
*/
|
||||
bool operator== (const NamedValueSet&) const noexcept;
|
||||
bool operator!= (const NamedValueSet&) const noexcept;
|
||||
|
||||
const NamedValueSet::NamedValue* begin() const noexcept { return values.begin(); }
|
||||
const NamedValueSet::NamedValue* end() const noexcept { return values.end(); }
|
||||
|
||||
@ -125,6 +129,8 @@ public:
|
||||
|
||||
Do not use this method unless you really need access to the internal var object
|
||||
for some reason - for normal reading and writing always prefer operator[]() and set().
|
||||
Also note that the pointer returned may become invalid as soon as any subsequent
|
||||
methods are called on the NamedValueSet.
|
||||
*/
|
||||
var* getVarPointer (const Identifier& name) const noexcept;
|
||||
|
||||
@ -135,6 +141,8 @@ public:
|
||||
|
||||
/** Returns the value of the item at a given index.
|
||||
The index must be between 0 and size() - 1, or this will return a nullptr
|
||||
Also note that the pointer returned may become invalid as soon as any subsequent
|
||||
methods are called on the NamedValueSet.
|
||||
*/
|
||||
var* getVarPointerAt (int index) const noexcept;
|
||||
|
||||
|
66
modules/juce_core/containers/juce_OwnedArray.cpp
Normal file
66
modules/juce_core/containers/juce_OwnedArray.cpp
Normal file
@ -0,0 +1,66 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2017 - ROLI Ltd.
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
#if JUCE_UNIT_TESTS
|
||||
|
||||
static struct OwnedArrayTest : public UnitTest
|
||||
{
|
||||
OwnedArrayTest() : UnitTest { "OwnedArray" } {}
|
||||
|
||||
struct Base
|
||||
{
|
||||
virtual ~Base() = default;
|
||||
};
|
||||
|
||||
struct Derived : Base
|
||||
{
|
||||
};
|
||||
|
||||
void runTest() override
|
||||
{
|
||||
beginTest ("After converting move construction, ownership is transferred");
|
||||
{
|
||||
OwnedArray<Derived> derived { new Derived{}, new Derived{}, new Derived{} };
|
||||
|
||||
OwnedArray<Base> base { std::move (derived) };
|
||||
|
||||
expectEquals (base.size(), 3);
|
||||
expectEquals (derived.size(), 0);
|
||||
}
|
||||
|
||||
beginTest ("After converting move assignment, ownership is transferred");
|
||||
{
|
||||
OwnedArray<Base> base;
|
||||
|
||||
base = OwnedArray<Derived> { new Derived{}, new Derived{}, new Derived{} };
|
||||
|
||||
expectEquals (base.size(), 3);
|
||||
}
|
||||
}
|
||||
} ownedArrayTest;
|
||||
|
||||
#endif
|
||||
|
||||
}
|
@ -52,9 +52,7 @@ class OwnedArray
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates an empty array. */
|
||||
OwnedArray() noexcept
|
||||
{
|
||||
}
|
||||
OwnedArray() = default;
|
||||
|
||||
/** Deletes the array and also deletes any objects inside it.
|
||||
|
||||
@ -68,10 +66,8 @@ public:
|
||||
|
||||
/** Move constructor. */
|
||||
OwnedArray (OwnedArray&& other) noexcept
|
||||
: data (static_cast<ArrayAllocationBase <ObjectClass*, TypeOfCriticalSectionToUse>&&> (other.data)),
|
||||
numUsed (other.numUsed)
|
||||
: values (std::move (other.values))
|
||||
{
|
||||
other.numUsed = 0;
|
||||
}
|
||||
|
||||
/** Creates an array from a list of objects. */
|
||||
@ -85,10 +81,24 @@ public:
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
deleteAllObjects();
|
||||
values = std::move (other.values);
|
||||
return *this;
|
||||
}
|
||||
|
||||
data = static_cast<ArrayAllocationBase <ObjectClass*, TypeOfCriticalSectionToUse>&&> (other.data);
|
||||
numUsed = other.numUsed;
|
||||
other.numUsed = 0;
|
||||
/** Converting move constructor. */
|
||||
template <class OtherObjectClass, class OtherCriticalSection>
|
||||
OwnedArray (OwnedArray<OtherObjectClass, OtherCriticalSection>&& other) noexcept
|
||||
: values (std::move (other.values))
|
||||
{
|
||||
}
|
||||
|
||||
/** Converting move assignment operator. */
|
||||
template <class OtherObjectClass, class OtherCriticalSection>
|
||||
OwnedArray& operator= (OwnedArray<OtherObjectClass, OtherCriticalSection>&& other) noexcept
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
deleteAllObjects();
|
||||
values = std::move (other.values);
|
||||
return *this;
|
||||
}
|
||||
|
||||
@ -97,12 +107,8 @@ public:
|
||||
void clear (bool deleteObjects = true)
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
|
||||
if (deleteObjects)
|
||||
deleteAllObjects();
|
||||
|
||||
data.setAllocatedSize (0);
|
||||
numUsed = 0;
|
||||
clearQuick (deleteObjects);
|
||||
values.setAllocatedSize (0);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
@ -113,8 +119,8 @@ public:
|
||||
|
||||
if (deleteObjects)
|
||||
deleteAllObjects();
|
||||
|
||||
numUsed = 0;
|
||||
else
|
||||
values.clear();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
@ -123,7 +129,7 @@ public:
|
||||
*/
|
||||
inline int size() const noexcept
|
||||
{
|
||||
return numUsed;
|
||||
return values.size();
|
||||
}
|
||||
|
||||
/** Returns true if the array is empty, false otherwise. */
|
||||
@ -143,13 +149,7 @@ public:
|
||||
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;
|
||||
return values.getValueWithDefault (index);
|
||||
}
|
||||
|
||||
/** Returns a pointer to the object at this index in the array, without checking whether the index is in-range.
|
||||
@ -160,8 +160,7 @@ public:
|
||||
inline ObjectClass* getUnchecked (const int index) const noexcept
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
jassert (isPositiveAndBelow (index, numUsed) && data.elements != nullptr);
|
||||
return data.elements[index];
|
||||
return values[index];
|
||||
}
|
||||
|
||||
/** Returns a pointer to the first object in the array.
|
||||
@ -172,14 +171,7 @@ public:
|
||||
inline ObjectClass* getFirst() const noexcept
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
|
||||
if (numUsed > 0)
|
||||
{
|
||||
jassert (data.elements != nullptr);
|
||||
return data.elements[0];
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
return values.getFirst();
|
||||
}
|
||||
|
||||
/** Returns a pointer to the last object in the array.
|
||||
@ -190,14 +182,7 @@ public:
|
||||
inline ObjectClass* getLast() const noexcept
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
|
||||
if (numUsed > 0)
|
||||
{
|
||||
jassert (data.elements != nullptr);
|
||||
return data.elements[numUsed - 1];
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
return values.getLast();
|
||||
}
|
||||
|
||||
/** Returns a pointer to the actual array data.
|
||||
@ -206,7 +191,7 @@ public:
|
||||
*/
|
||||
inline ObjectClass** getRawDataPointer() noexcept
|
||||
{
|
||||
return data.elements;
|
||||
return values.begin();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
@ -215,7 +200,7 @@ public:
|
||||
*/
|
||||
inline ObjectClass** begin() const noexcept
|
||||
{
|
||||
return data.elements;
|
||||
return values.begin();
|
||||
}
|
||||
|
||||
/** Returns a pointer to the element which follows the last element in the array.
|
||||
@ -223,12 +208,15 @@ public:
|
||||
*/
|
||||
inline ObjectClass** end() const noexcept
|
||||
{
|
||||
#if JUCE_DEBUG
|
||||
if (data.elements == nullptr || numUsed <= 0) // (to keep static analysers happy)
|
||||
return data.elements;
|
||||
#endif
|
||||
return values.end();
|
||||
}
|
||||
|
||||
return data.elements + numUsed;
|
||||
/** Returns a pointer to the first element in the array.
|
||||
This method is provided for compatibility with the standard C++ containers.
|
||||
*/
|
||||
inline ObjectClass** data() const noexcept
|
||||
{
|
||||
return begin();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
@ -240,12 +228,11 @@ public:
|
||||
int indexOf (const ObjectClass* objectToLookFor) const noexcept
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
auto** e = data.elements.get();
|
||||
auto** end_ = e + numUsed;
|
||||
auto** e = values.begin();
|
||||
|
||||
for (; e != end_; ++e)
|
||||
for (; e != values.end(); ++e)
|
||||
if (objectToLookFor == *e)
|
||||
return static_cast<int> (e - data.elements.get());
|
||||
return static_cast<int> (e - values.begin());
|
||||
|
||||
return -1;
|
||||
}
|
||||
@ -258,10 +245,9 @@ public:
|
||||
bool contains (const ObjectClass* objectToLookFor) const noexcept
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
auto** e = data.elements.get();
|
||||
auto** end_ = e + numUsed;
|
||||
auto** e = values.begin();
|
||||
|
||||
for (; e != end_; ++e)
|
||||
for (; e != values.end(); ++e)
|
||||
if (objectToLookFor == *e)
|
||||
return true;
|
||||
|
||||
@ -284,9 +270,7 @@ public:
|
||||
ObjectClass* add (ObjectClass* newObject) noexcept
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
data.ensureAllocatedSize (numUsed + 1);
|
||||
jassert (data.elements != nullptr);
|
||||
data.elements[numUsed++] = newObject;
|
||||
values.add (newObject);
|
||||
return newObject;
|
||||
}
|
||||
|
||||
@ -310,25 +294,8 @@ public:
|
||||
*/
|
||||
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);
|
||||
|
||||
auto** e = data.elements + indexToInsertAt;
|
||||
auto numToMove = numUsed - indexToInsertAt;
|
||||
|
||||
if (numToMove > 0)
|
||||
memmove (e + 1, e, sizeof (ObjectClass*) * (size_t) numToMove);
|
||||
|
||||
*e = newObject;
|
||||
++numUsed;
|
||||
values.insert (indexToInsertAt, newObject, 1);
|
||||
return newObject;
|
||||
}
|
||||
|
||||
@ -351,24 +318,7 @@ public:
|
||||
if (numberOfElements > 0)
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
data.ensureAllocatedSize (numUsed + numberOfElements);
|
||||
auto* insertPos = data.elements.get();
|
||||
|
||||
if (isPositiveAndBelow (indexToInsertAt, numUsed))
|
||||
{
|
||||
insertPos += indexToInsertAt;
|
||||
auto numberToMove = (size_t) (numUsed - indexToInsertAt);
|
||||
memmove (insertPos + numberOfElements, insertPos, numberToMove * sizeof (ObjectClass*));
|
||||
}
|
||||
else
|
||||
{
|
||||
insertPos += numUsed;
|
||||
}
|
||||
|
||||
numUsed += numberOfElements;
|
||||
|
||||
while (--numberOfElements >= 0)
|
||||
*insertPos++ = *newObjects++;
|
||||
values.insertArray (indexToInsertAt, newObjects, numberOfElements);
|
||||
}
|
||||
}
|
||||
|
||||
@ -413,22 +363,21 @@ public:
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
|
||||
if (indexToChange < numUsed)
|
||||
if (indexToChange < values.size())
|
||||
{
|
||||
if (deleteOldElement)
|
||||
{
|
||||
toDelete.reset (data.elements[indexToChange]);
|
||||
toDelete.reset (values[indexToChange]);
|
||||
|
||||
if (toDelete.get() == newObject)
|
||||
toDelete.release();
|
||||
}
|
||||
|
||||
data.elements[indexToChange] = newObject;
|
||||
values[indexToChange] = newObject;
|
||||
}
|
||||
else
|
||||
{
|
||||
data.ensureAllocatedSize (numUsed + 1);
|
||||
data.elements[numUsed++] = newObject;
|
||||
values.add (newObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -457,24 +406,7 @@ public:
|
||||
{
|
||||
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;
|
||||
}
|
||||
values.addArray (arrayToAddFrom, startIndex, numElementsToAdd);
|
||||
}
|
||||
|
||||
/** Adds elements from another array to the end of this array. */
|
||||
@ -482,13 +414,7 @@ public:
|
||||
void addArray (const std::initializer_list<OtherArrayType>& items)
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
data.ensureAllocatedSize (numUsed + (int) items.size());
|
||||
|
||||
for (auto* item : items)
|
||||
{
|
||||
data.elements[numUsed] = item;
|
||||
++numUsed;
|
||||
}
|
||||
values.addArray (items);
|
||||
}
|
||||
|
||||
/** Adds copies of the elements in another array to the end of this array.
|
||||
@ -522,11 +448,11 @@ public:
|
||||
if (numElementsToAdd < 0 || startIndex + numElementsToAdd > arrayToAddFrom.size())
|
||||
numElementsToAdd = arrayToAddFrom.size() - startIndex;
|
||||
|
||||
data.ensureAllocatedSize (numUsed + numElementsToAdd);
|
||||
jassert (numElementsToAdd <= 0 || data.elements != nullptr);
|
||||
jassert (numElementsToAdd >= 0);
|
||||
values.ensureAllocatedSize (values.size() + numElementsToAdd);
|
||||
|
||||
while (--numElementsToAdd >= 0)
|
||||
data.elements[numUsed++] = createCopyIfNotNull (arrayToAddFrom.getUnchecked (startIndex++));
|
||||
values.add (createCopyIfNotNull (arrayToAddFrom.getUnchecked (startIndex++)));
|
||||
}
|
||||
|
||||
/** Inserts a new object into the array assuming that the array is sorted.
|
||||
@ -544,10 +470,12 @@ public:
|
||||
template <class ElementComparator>
|
||||
int addSorted (ElementComparator& comparator, ObjectClass* const newObject) noexcept
|
||||
{
|
||||
ignoreUnused (comparator); // if you pass in an object with a static compareElements() method, this
|
||||
// avoids getting warning messages about the parameter being unused
|
||||
// If you pass in an object with a static compareElements() method, this
|
||||
// avoids getting warning messages about the parameter being unused
|
||||
ignoreUnused (comparator);
|
||||
|
||||
const ScopedLockType lock (getLock());
|
||||
const int index = findInsertIndexInSortedArray (comparator, data.elements.get(), newObject, 0, numUsed);
|
||||
const int index = findInsertIndexInSortedArray (comparator, values.begin(), newObject, 0, values.size());
|
||||
insert (index, newObject);
|
||||
return index;
|
||||
}
|
||||
@ -567,13 +495,16 @@ public:
|
||||
template <typename ElementComparator>
|
||||
int indexOfSorted (ElementComparator& comparator, const ObjectClass* const objectToLookFor) const noexcept
|
||||
{
|
||||
// If you pass in an object with a static compareElements() method, this
|
||||
// avoids getting warning messages about the parameter being unused
|
||||
ignoreUnused (comparator);
|
||||
|
||||
const ScopedLockType lock (getLock());
|
||||
int s = 0, e = numUsed;
|
||||
int s = 0, e = values.size();
|
||||
|
||||
while (s < e)
|
||||
{
|
||||
if (comparator.compareElements (objectToLookFor, data.elements[s]) == 0)
|
||||
if (comparator.compareElements (objectToLookFor, values[s]) == 0)
|
||||
return s;
|
||||
|
||||
auto halfway = (s + e) / 2;
|
||||
@ -581,7 +512,7 @@ public:
|
||||
if (halfway == s)
|
||||
break;
|
||||
|
||||
if (comparator.compareElements (objectToLookFor, data.elements[halfway]) >= 0)
|
||||
if (comparator.compareElements (objectToLookFor, values[halfway]) >= 0)
|
||||
s = halfway;
|
||||
else
|
||||
e = halfway;
|
||||
@ -608,22 +539,18 @@ public:
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
|
||||
if (isPositiveAndBelow (indexToRemove, numUsed))
|
||||
if (isPositiveAndBelow (indexToRemove, values.size()))
|
||||
{
|
||||
auto** e = data.elements + indexToRemove;
|
||||
auto** e = values.begin() + indexToRemove;
|
||||
|
||||
if (deleteObject)
|
||||
toDelete.reset (*e);
|
||||
|
||||
--numUsed;
|
||||
auto numToShift = numUsed - indexToRemove;
|
||||
|
||||
if (numToShift > 0)
|
||||
memmove (e, e + 1, sizeof (ObjectClass*) * (size_t) numToShift);
|
||||
values.removeElements (indexToRemove, 1);
|
||||
}
|
||||
}
|
||||
|
||||
if ((numUsed << 1) < data.numAllocated)
|
||||
if ((values.size() << 1) < values.capacity())
|
||||
minimiseStorageOverheads();
|
||||
}
|
||||
|
||||
@ -641,18 +568,13 @@ public:
|
||||
ObjectClass* removedItem = nullptr;
|
||||
const ScopedLockType lock (getLock());
|
||||
|
||||
if (isPositiveAndBelow (indexToRemove, numUsed))
|
||||
if (isPositiveAndBelow (indexToRemove, values.size()))
|
||||
{
|
||||
auto** e = data.elements + indexToRemove;
|
||||
removedItem = *e;
|
||||
removedItem = values[indexToRemove];
|
||||
|
||||
--numUsed;
|
||||
const int numToShift = numUsed - indexToRemove;
|
||||
values.removeElements (indexToRemove, 1);
|
||||
|
||||
if (numToShift > 0)
|
||||
memmove (e, e + 1, sizeof (ObjectClass*) * (size_t) numToShift);
|
||||
|
||||
if ((numUsed << 1) < data.numAllocated)
|
||||
if ((values.size() << 1) < values.capacity())
|
||||
minimiseStorageOverheads();
|
||||
}
|
||||
|
||||
@ -670,11 +592,10 @@ public:
|
||||
void removeObject (const ObjectClass* objectToRemove, bool deleteObject = true)
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
auto** e = data.elements.get();
|
||||
|
||||
for (int i = 0; i < numUsed; ++i)
|
||||
for (int i = 0; i < values.size(); ++i)
|
||||
{
|
||||
if (objectToRemove == e[i])
|
||||
if (objectToRemove == values[i])
|
||||
{
|
||||
remove (i, deleteObject);
|
||||
break;
|
||||
@ -698,32 +619,24 @@ public:
|
||||
void removeRange (int startIndex, int numberToRemove, bool deleteObjects = true)
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
auto endIndex = jlimit (0, numUsed, startIndex + numberToRemove);
|
||||
startIndex = jlimit (0, numUsed, startIndex);
|
||||
auto endIndex = jlimit (0, values.size(), startIndex + numberToRemove);
|
||||
startIndex = jlimit (0, values.size(), startIndex);
|
||||
numberToRemove = endIndex - startIndex;
|
||||
|
||||
if (endIndex > startIndex)
|
||||
if (numberToRemove > 0)
|
||||
{
|
||||
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)
|
||||
ContainerDeletePolicy<ObjectClass>::destroy (values[i]);
|
||||
values[i] = nullptr; // (in case one of the destructors accesses this array and hits a dangling pointer)
|
||||
}
|
||||
}
|
||||
|
||||
auto rangeSize = endIndex - startIndex;
|
||||
auto** e = data.elements + startIndex;
|
||||
auto numToShift = numUsed - endIndex;
|
||||
numUsed -= rangeSize;
|
||||
values.removeElements (startIndex, numberToRemove);
|
||||
|
||||
while (--numToShift >= 0)
|
||||
{
|
||||
*e = e[rangeSize];
|
||||
++e;
|
||||
}
|
||||
|
||||
if ((numUsed << 1) < data.numAllocated)
|
||||
if ((values.size() << 1) < values.capacity())
|
||||
minimiseStorageOverheads();
|
||||
}
|
||||
}
|
||||
@ -739,10 +652,10 @@ public:
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
|
||||
if (howManyToRemove >= numUsed)
|
||||
if (howManyToRemove >= values.size())
|
||||
clear (deleteObjects);
|
||||
else
|
||||
removeRange (numUsed - howManyToRemove, howManyToRemove, deleteObjects);
|
||||
removeRange (values.size() - howManyToRemove, howManyToRemove, deleteObjects);
|
||||
}
|
||||
|
||||
/** Swaps a pair of objects in the array.
|
||||
@ -750,17 +663,10 @@ public:
|
||||
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
|
||||
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]);
|
||||
}
|
||||
values.swap (index1, index2);
|
||||
}
|
||||
|
||||
/** Moves one of the objects to a different position.
|
||||
@ -781,29 +687,7 @@ public:
|
||||
if (currentIndex != newIndex)
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
|
||||
if (isPositiveAndBelow (currentIndex, numUsed))
|
||||
{
|
||||
if (! isPositiveAndBelow (newIndex, numUsed))
|
||||
newIndex = numUsed - 1;
|
||||
|
||||
auto* 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;
|
||||
}
|
||||
values.move (currentIndex, newIndex);
|
||||
}
|
||||
}
|
||||
|
||||
@ -817,8 +701,7 @@ public:
|
||||
{
|
||||
const ScopedLockType lock1 (getLock());
|
||||
const typename OtherArrayType::ScopedLockType lock2 (otherArray.getLock());
|
||||
data.swapWith (otherArray.data);
|
||||
std::swap (numUsed, otherArray.numUsed);
|
||||
values.swapWith (otherArray.values);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
@ -831,7 +714,7 @@ public:
|
||||
void minimiseStorageOverheads() noexcept
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
data.shrinkToNoMoreThan (numUsed);
|
||||
values.shrinkToNoMoreThan (values.size());
|
||||
}
|
||||
|
||||
/** Increases the array's internal storage to hold a minimum number of elements.
|
||||
@ -843,7 +726,7 @@ public:
|
||||
void ensureStorageAllocated (const int minNumElements) noexcept
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
data.ensureAllocatedSize (minNumElements);
|
||||
values.ensureAllocatedSize (minNumElements);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
@ -876,13 +759,14 @@ public:
|
||||
void sort (ElementComparator& comparator,
|
||||
bool retainOrderOfEquivalentItems = false) const noexcept
|
||||
{
|
||||
ignoreUnused (comparator); // if you pass in an object with a static compareElements() method, this
|
||||
// avoids getting warning messages about the parameter being unused
|
||||
// If you pass in an object with a static compareElements() method, this
|
||||
// avoids getting warning messages about the parameter being unused
|
||||
ignoreUnused (comparator);
|
||||
|
||||
const ScopedLockType lock (getLock());
|
||||
|
||||
if (size() > 1)
|
||||
sortArray (comparator, data.elements.get(), 0, size() - 1, retainOrderOfEquivalentItems);
|
||||
sortArray (comparator, values.begin(), 0, size() - 1, retainOrderOfEquivalentItems);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
@ -890,12 +774,11 @@ public:
|
||||
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; }
|
||||
inline const TypeOfCriticalSectionToUse& getLock() const noexcept { return values; }
|
||||
|
||||
/** Returns the type of scoped lock to use for locking this array */
|
||||
using ScopedLockType = typename TypeOfCriticalSectionToUse::ScopedLockType;
|
||||
|
||||
|
||||
//==============================================================================
|
||||
#ifndef DOXYGEN
|
||||
// Note that the swapWithArray method has been replaced by a more flexible templated version,
|
||||
@ -905,15 +788,19 @@ public:
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
ArrayAllocationBase <ObjectClass*, TypeOfCriticalSectionToUse> data;
|
||||
int numUsed = 0;
|
||||
ArrayBase <ObjectClass*, TypeOfCriticalSectionToUse> values;
|
||||
|
||||
void deleteAllObjects()
|
||||
{
|
||||
while (numUsed > 0)
|
||||
ContainerDeletePolicy<ObjectClass>::destroy (data.elements[--numUsed]);
|
||||
for (auto& e : values)
|
||||
ContainerDeletePolicy<ObjectClass>::destroy (e);
|
||||
|
||||
values.clear();
|
||||
}
|
||||
|
||||
template <class OtherObjectClass, class OtherCriticalSection>
|
||||
friend class OwnedArray;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OwnedArray)
|
||||
};
|
||||
|
||||
|
122
modules/juce_core/containers/juce_ReferenceCountedArray.cpp
Normal file
122
modules/juce_core/containers/juce_ReferenceCountedArray.cpp
Normal file
@ -0,0 +1,122 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2018 - ROLI Ltd.
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
#if JUCE_UNIT_TESTS
|
||||
|
||||
class ReferenceCountedArrayTests : public UnitTest
|
||||
{
|
||||
public:
|
||||
ReferenceCountedArrayTests() : UnitTest ("ReferenceCountedArray", "Containers") {}
|
||||
|
||||
//==============================================================================
|
||||
void runTest() override
|
||||
{
|
||||
beginTest ("Add derived objects");
|
||||
{
|
||||
ReferenceCountedArray<TestDerivedObj> derivedArray;
|
||||
derivedArray.add (static_cast<TestDerivedObj*> (new TestBaseObj()));
|
||||
expectEquals (derivedArray.size(), 1);
|
||||
expectEquals (derivedArray.getObjectPointer (0)->getReferenceCount(), 1);
|
||||
expectEquals (derivedArray[0]->getReferenceCount(), 2);
|
||||
|
||||
for (auto o : derivedArray)
|
||||
expectEquals (o->getReferenceCount(), 1);
|
||||
|
||||
ReferenceCountedArray<TestBaseObj> baseArray;
|
||||
baseArray.addArray (derivedArray);
|
||||
|
||||
for (auto o : baseArray)
|
||||
expectEquals (o->getReferenceCount(), 2);
|
||||
|
||||
derivedArray.clearQuick();
|
||||
baseArray.clearQuick();
|
||||
|
||||
auto* baseObject = new TestBaseObj();
|
||||
TestBaseObj::Ptr baseObjectPtr = baseObject;
|
||||
expectEquals (baseObject->getReferenceCount(), 1);
|
||||
|
||||
auto* derivedObject = new TestDerivedObj();
|
||||
TestDerivedObj::Ptr derivedObjectPtr = derivedObject;
|
||||
expectEquals (derivedObject->getReferenceCount(), 1);
|
||||
|
||||
baseArray.add (baseObject);
|
||||
baseArray.add (derivedObject);
|
||||
|
||||
for (auto o : baseArray)
|
||||
expectEquals (o->getReferenceCount(), 2);
|
||||
|
||||
expectEquals (baseObject->getReferenceCount(), 2);
|
||||
expectEquals (derivedObject->getReferenceCount(), 2);
|
||||
|
||||
derivedArray.add (derivedObject);
|
||||
|
||||
for (auto o : derivedArray)
|
||||
expectEquals (o->getReferenceCount(), 3);
|
||||
|
||||
derivedArray.clearQuick();
|
||||
baseArray.clearQuick();
|
||||
|
||||
expectEquals (baseObject->getReferenceCount(), 1);
|
||||
expectEquals (derivedObject->getReferenceCount(), 1);
|
||||
|
||||
baseArray.add (baseObjectPtr);
|
||||
|
||||
#if JUCE_STRICT_REFCOUNTEDPOINTER
|
||||
baseArray.add (derivedObjectPtr);
|
||||
#else
|
||||
baseArray.add (derivedObjectPtr.get());
|
||||
#endif
|
||||
|
||||
for (auto o : baseArray)
|
||||
expectEquals (o->getReferenceCount(), 2);
|
||||
|
||||
derivedArray.add (derivedObjectPtr);
|
||||
|
||||
for (auto o : derivedArray)
|
||||
expectEquals (o->getReferenceCount(), 3);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
struct TestBaseObj : public ReferenceCountedObject
|
||||
{
|
||||
using Ptr = ReferenceCountedObjectPtr<TestBaseObj>;
|
||||
|
||||
TestBaseObj() = default;
|
||||
};
|
||||
|
||||
struct TestDerivedObj : public TestBaseObj
|
||||
{
|
||||
using Ptr = ReferenceCountedObjectPtr<TestDerivedObj>;
|
||||
|
||||
TestDerivedObj() = default;
|
||||
};
|
||||
};
|
||||
|
||||
static ReferenceCountedArrayTests referenceCountedArrayTests;
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace juce
|
@ -56,17 +56,13 @@ public:
|
||||
/** Creates an empty array.
|
||||
@see ReferenceCountedObject, Array, OwnedArray
|
||||
*/
|
||||
ReferenceCountedArray() noexcept
|
||||
{
|
||||
}
|
||||
ReferenceCountedArray() = default;
|
||||
|
||||
/** Creates a copy of another array */
|
||||
ReferenceCountedArray (const ReferenceCountedArray& other) noexcept
|
||||
{
|
||||
const ScopedLockType lock (other.getLock());
|
||||
numUsed = other.numUsed;
|
||||
data.setAllocatedSize (numUsed);
|
||||
memcpy (data.elements, other.getRawDataPointer(), (size_t) numUsed * sizeof (ObjectClass*));
|
||||
values.addArray (other.begin(), other.size());
|
||||
|
||||
for (auto* o : *this)
|
||||
if (o != nullptr)
|
||||
@ -75,10 +71,8 @@ public:
|
||||
|
||||
/** Moves from another array */
|
||||
ReferenceCountedArray (ReferenceCountedArray&& other) noexcept
|
||||
: data (static_cast<ArrayAllocationBase<ObjectClass*, TypeOfCriticalSectionToUse>&&> (other.data)),
|
||||
numUsed (other.numUsed)
|
||||
: values (std::move (other.values))
|
||||
{
|
||||
other.numUsed = 0;
|
||||
}
|
||||
|
||||
/** Creates a copy of another array */
|
||||
@ -86,9 +80,7 @@ public:
|
||||
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(), (size_t) numUsed * sizeof (ObjectClass*));
|
||||
values.addArray (other.begin(), other.size());
|
||||
|
||||
for (auto* o : *this)
|
||||
if (o != nullptr)
|
||||
@ -121,9 +113,7 @@ public:
|
||||
ReferenceCountedArray& operator= (ReferenceCountedArray&& other) noexcept
|
||||
{
|
||||
releaseAllObjects();
|
||||
data = static_cast<ArrayAllocationBase<ObjectClass*, TypeOfCriticalSectionToUse>&&> (other.data);
|
||||
numUsed = other.numUsed;
|
||||
other.numUsed = 0;
|
||||
values = std::move (other.values);
|
||||
return *this;
|
||||
}
|
||||
|
||||
@ -137,13 +127,13 @@ public:
|
||||
|
||||
//==============================================================================
|
||||
/** Removes all objects from the array.
|
||||
Any objects in the array that whose reference counts drop to zero will be deleted.
|
||||
Any objects in the array whose reference counts drop to zero will be deleted.
|
||||
*/
|
||||
void clear()
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
releaseAllObjects();
|
||||
data.setAllocatedSize (0);
|
||||
clearQuick();
|
||||
values.setAllocatedSize (0);
|
||||
}
|
||||
|
||||
/** Removes all objects from the array without freeing the array's allocated storage.
|
||||
@ -159,13 +149,13 @@ public:
|
||||
/** Returns the current number of objects in the array. */
|
||||
inline int size() const noexcept
|
||||
{
|
||||
return numUsed;
|
||||
return values.size();
|
||||
}
|
||||
|
||||
/** Returns true if the array is empty, false otherwise. */
|
||||
inline bool isEmpty() const noexcept
|
||||
{
|
||||
return numUsed == 0;
|
||||
return size() == 0;
|
||||
}
|
||||
|
||||
/** Returns a pointer to the object at this index in the array.
|
||||
@ -178,7 +168,7 @@ public:
|
||||
*/
|
||||
inline ObjectClassPtr operator[] (int index) const noexcept
|
||||
{
|
||||
return getObjectPointer (index);
|
||||
return ObjectClassPtr (getObjectPointer (index));
|
||||
}
|
||||
|
||||
/** Returns a pointer to the object at this index in the array, without checking
|
||||
@ -189,7 +179,7 @@ public:
|
||||
*/
|
||||
inline ObjectClassPtr getUnchecked (int index) const noexcept
|
||||
{
|
||||
return getObjectPointerUnchecked (index);
|
||||
return ObjectClassPtr (getObjectPointerUnchecked (index));
|
||||
}
|
||||
|
||||
/** Returns a raw pointer to the object at this index in the array.
|
||||
@ -203,24 +193,16 @@ public:
|
||||
inline ObjectClass* getObjectPointer (int index) const noexcept
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
|
||||
if (isPositiveAndBelow (index, numUsed))
|
||||
{
|
||||
jassert (data.elements != nullptr);
|
||||
return data.elements[index];
|
||||
}
|
||||
|
||||
return {};
|
||||
return values.getValueWithDefault (index);
|
||||
}
|
||||
|
||||
/** 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
|
||||
inline ObjectClass* getObjectPointerUnchecked (int index) const noexcept
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
jassert (isPositiveAndBelow (index, numUsed) && data.elements != nullptr);
|
||||
return data.elements[index];
|
||||
return values[index];
|
||||
}
|
||||
|
||||
/** Returns a pointer to the first object in the array.
|
||||
@ -231,14 +213,7 @@ public:
|
||||
inline ObjectClassPtr getFirst() const noexcept
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
|
||||
if (numUsed > 0)
|
||||
{
|
||||
jassert (data.elements != nullptr);
|
||||
return data.elements[0];
|
||||
}
|
||||
|
||||
return {};
|
||||
return values.getFirst();
|
||||
}
|
||||
|
||||
/** Returns a pointer to the last object in the array.
|
||||
@ -249,14 +224,7 @@ public:
|
||||
inline ObjectClassPtr getLast() const noexcept
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
|
||||
if (numUsed > 0)
|
||||
{
|
||||
jassert (data.elements != nullptr);
|
||||
return data.elements[numUsed - 1];
|
||||
}
|
||||
|
||||
return {};
|
||||
return values.getLast();
|
||||
}
|
||||
|
||||
/** Returns a pointer to the actual array data.
|
||||
@ -265,7 +233,7 @@ public:
|
||||
*/
|
||||
inline ObjectClass** getRawDataPointer() const noexcept
|
||||
{
|
||||
return data.elements;
|
||||
return values.begin();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
@ -274,7 +242,7 @@ public:
|
||||
*/
|
||||
inline ObjectClass** begin() const noexcept
|
||||
{
|
||||
return data.elements;
|
||||
return values.begin();
|
||||
}
|
||||
|
||||
/** Returns a pointer to the element which follows the last element in the array.
|
||||
@ -282,7 +250,15 @@ public:
|
||||
*/
|
||||
inline ObjectClass** end() const noexcept
|
||||
{
|
||||
return data.elements + numUsed;
|
||||
return values.end();
|
||||
}
|
||||
|
||||
/** Returns a pointer to the first element in the array.
|
||||
This method is provided for compatibility with the standard C++ containers.
|
||||
*/
|
||||
inline ObjectClass** data() const noexcept
|
||||
{
|
||||
return begin();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
@ -294,13 +270,13 @@ public:
|
||||
int indexOf (const ObjectClass* objectToLookFor) const noexcept
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
auto** e = data.elements.get();
|
||||
auto** endPointer = e + numUsed;
|
||||
auto** e = values.begin();
|
||||
auto** endPointer = values.end();
|
||||
|
||||
while (e != endPointer)
|
||||
{
|
||||
if (objectToLookFor == *e)
|
||||
return static_cast<int> (e - data.elements.get());
|
||||
return static_cast<int> (e - values.begin());
|
||||
|
||||
++e;
|
||||
}
|
||||
@ -308,6 +284,13 @@ public:
|
||||
return -1;
|
||||
}
|
||||
|
||||
/** 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 ObjectClassPtr& objectToLookFor) const noexcept { return indexOf (objectToLookFor.get()); }
|
||||
|
||||
/** Returns true if the array contains a specified object.
|
||||
|
||||
@param objectToLookFor the object to look for
|
||||
@ -316,8 +299,8 @@ public:
|
||||
bool contains (const ObjectClass* objectToLookFor) const noexcept
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
auto** e = data.elements.get();
|
||||
auto** endPointer = e + numUsed;
|
||||
auto** e = values.begin();
|
||||
auto** endPointer = values.end();
|
||||
|
||||
while (e != endPointer)
|
||||
{
|
||||
@ -330,6 +313,13 @@ public:
|
||||
return false;
|
||||
}
|
||||
|
||||
/** 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 ObjectClassPtr& objectToLookFor) const noexcept { return contains (objectToLookFor.get()); }
|
||||
|
||||
/** Appends a new object to the end of the array.
|
||||
|
||||
This will increase the new object's reference count.
|
||||
@ -337,12 +327,42 @@ public:
|
||||
@param newObject the new object to add to the array
|
||||
@see set, insert, addIfNotAlreadyThere, addSorted, addArray
|
||||
*/
|
||||
ObjectClass* add (ObjectClass* newObject) noexcept
|
||||
ObjectClass* add (ObjectClass* newObject)
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
data.ensureAllocatedSize (numUsed + 1);
|
||||
jassert (data.elements != nullptr);
|
||||
data.elements[numUsed++] = newObject;
|
||||
values.add (newObject);
|
||||
|
||||
if (newObject != nullptr)
|
||||
newObject->incReferenceCount();
|
||||
|
||||
return newObject;
|
||||
}
|
||||
|
||||
/** 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 (const ObjectClassPtr& newObject) { return add (newObject.get()); }
|
||||
|
||||
/** 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* newObject)
|
||||
{
|
||||
values.insert (indexToInsertAt, newObject, 1);
|
||||
|
||||
if (newObject != nullptr)
|
||||
newObject->incReferenceCount();
|
||||
@ -363,32 +383,25 @@ public:
|
||||
@param newObject the new object to add to the array
|
||||
@see add, addSorted, addIfNotAlreadyThere, set
|
||||
*/
|
||||
ObjectClass* insert (int indexToInsertAt, ObjectClass* newObject) noexcept
|
||||
{
|
||||
if (indexToInsertAt < 0)
|
||||
return add (newObject);
|
||||
ObjectClass* insert (int indexToInsertAt, const ObjectClassPtr& newObject) { return insert (indexToInsertAt, newObject.get()); }
|
||||
|
||||
/** 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 true if the object has been added, false otherwise
|
||||
*/
|
||||
bool addIfNotAlreadyThere (ObjectClass* newObject)
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
|
||||
if (indexToInsertAt > numUsed)
|
||||
indexToInsertAt = numUsed;
|
||||
if (contains (newObject))
|
||||
return false;
|
||||
|
||||
data.ensureAllocatedSize (numUsed + 1);
|
||||
jassert (data.elements != nullptr);
|
||||
|
||||
auto** e = data.elements + indexToInsertAt;
|
||||
auto 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;
|
||||
add (newObject);
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Appends a new object at the end of the array as long as the array doesn't
|
||||
@ -399,16 +412,7 @@ public:
|
||||
@param newObject the new object to add to the array
|
||||
@returns true if the object has been added, false otherwise
|
||||
*/
|
||||
bool addIfNotAlreadyThere (ObjectClass* newObject) noexcept
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
|
||||
if (contains (newObject))
|
||||
return false;
|
||||
|
||||
add (newObject);
|
||||
return true;
|
||||
}
|
||||
bool addIfNotAlreadyThere (const ObjectClassPtr& newObject) { return addIfNotAlreadyThere (newObject.get()); }
|
||||
|
||||
/** Replaces an object in the array with a different one.
|
||||
|
||||
@ -431,16 +435,14 @@ public:
|
||||
if (newObject != nullptr)
|
||||
newObject->incReferenceCount();
|
||||
|
||||
if (indexToChange < numUsed)
|
||||
if (indexToChange < values.size())
|
||||
{
|
||||
releaseObject (data.elements[indexToChange]);
|
||||
data.elements[indexToChange] = newObject;
|
||||
releaseObject (values[indexToChange]);
|
||||
values[indexToChange] = newObject;
|
||||
}
|
||||
else
|
||||
{
|
||||
data.ensureAllocatedSize (numUsed + 1);
|
||||
jassert (data.elements != nullptr);
|
||||
data.elements[numUsed++] = newObject;
|
||||
values.add (newObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -463,22 +465,11 @@ public:
|
||||
{
|
||||
const ScopedLockType lock2 (getLock());
|
||||
|
||||
if (startIndex < 0)
|
||||
{
|
||||
jassertfalse;
|
||||
startIndex = 0;
|
||||
}
|
||||
auto numElementsAdded = values.addArray (arrayToAddFrom.values, startIndex, numElementsToAdd);
|
||||
auto** e = values.end();
|
||||
|
||||
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++));
|
||||
}
|
||||
for (int i = 0; i < numElementsAdded; ++i)
|
||||
(*(--e))->incReferenceCount();
|
||||
}
|
||||
}
|
||||
|
||||
@ -498,7 +489,7 @@ public:
|
||||
int addSorted (ElementComparator& comparator, ObjectClass* newObject) noexcept
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
auto index = findInsertIndexInSortedArray (comparator, data.elements.get(), newObject, 0, numUsed);
|
||||
auto index = findInsertIndexInSortedArray (comparator, values.begin(), newObject, 0, values.size());
|
||||
insert (index, newObject);
|
||||
return index;
|
||||
}
|
||||
@ -512,9 +503,9 @@ public:
|
||||
void addOrReplaceSorted (ElementComparator& comparator, ObjectClass* newObject) noexcept
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
auto index = findInsertIndexInSortedArray (comparator, data.elements.get(), newObject, 0, numUsed);
|
||||
auto index = findInsertIndexInSortedArray (comparator, values.begin(), newObject, 0, values.size());
|
||||
|
||||
if (index > 0 && comparator.compareElements (newObject, data.elements[index - 1]) == 0)
|
||||
if (index > 0 && comparator.compareElements (newObject, values[index - 1]) == 0)
|
||||
set (index - 1, newObject); // replace an existing object that matches
|
||||
else
|
||||
insert (index, newObject); // no match, so insert the new one
|
||||
@ -538,11 +529,11 @@ public:
|
||||
{
|
||||
ignoreUnused (comparator);
|
||||
const ScopedLockType lock (getLock());
|
||||
int s = 0, e = numUsed;
|
||||
int s = 0, e = values.size();
|
||||
|
||||
while (s < e)
|
||||
{
|
||||
if (comparator.compareElements (objectToLookFor, data.elements[s]) == 0)
|
||||
if (comparator.compareElements (objectToLookFor, values[s]) == 0)
|
||||
return s;
|
||||
|
||||
auto halfway = (s + e) / 2;
|
||||
@ -550,7 +541,7 @@ public:
|
||||
if (halfway == s)
|
||||
break;
|
||||
|
||||
if (comparator.compareElements (objectToLookFor, data.elements[halfway]) >= 0)
|
||||
if (comparator.compareElements (objectToLookFor, values[halfway]) >= 0)
|
||||
s = halfway;
|
||||
else
|
||||
e = halfway;
|
||||
@ -577,17 +568,13 @@ public:
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
|
||||
if (isPositiveAndBelow (indexToRemove, numUsed))
|
||||
if (isPositiveAndBelow (indexToRemove, values.size()))
|
||||
{
|
||||
auto** e = data.elements + indexToRemove;
|
||||
auto** e = values.begin() + indexToRemove;
|
||||
releaseObject (*e);
|
||||
--numUsed;
|
||||
auto numberToShift = numUsed - indexToRemove;
|
||||
values.removeElements (indexToRemove, 1);
|
||||
|
||||
if (numberToShift > 0)
|
||||
memmove (e, e + 1, sizeof (ObjectClass*) * (size_t) numberToShift);
|
||||
|
||||
if ((numUsed << 1) < data.numAllocated)
|
||||
if ((values.size() << 1) < values.capacity())
|
||||
minimiseStorageOverheads();
|
||||
}
|
||||
}
|
||||
@ -606,18 +593,14 @@ public:
|
||||
ObjectClassPtr removedItem;
|
||||
const ScopedLockType lock (getLock());
|
||||
|
||||
if (isPositiveAndBelow (indexToRemove, numUsed))
|
||||
if (isPositiveAndBelow (indexToRemove, values.size()))
|
||||
{
|
||||
auto** e = data.elements + indexToRemove;
|
||||
auto** e = values.begin() + indexToRemove;
|
||||
removedItem = *e;
|
||||
releaseObject (*e);
|
||||
--numUsed;
|
||||
auto numberToShift = numUsed - indexToRemove;
|
||||
values.removeElements (indexToRemove, 1);
|
||||
|
||||
if (numberToShift > 0)
|
||||
memmove (e, e + 1, sizeof (ObjectClass*) * (size_t) numberToShift);
|
||||
|
||||
if ((numUsed << 1) < data.numAllocated)
|
||||
if ((values.size() << 1) < values.capacity())
|
||||
minimiseStorageOverheads();
|
||||
}
|
||||
|
||||
@ -638,6 +621,16 @@ public:
|
||||
remove (indexOf (objectToRemove));
|
||||
}
|
||||
|
||||
/** 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 (const ObjectClassPtr& objectToRemove) { removeObject (objectToRemove.get()); }
|
||||
|
||||
/** Removes a range of objects from the array.
|
||||
|
||||
This will remove a set of objects, starting from the given index,
|
||||
@ -657,30 +650,21 @@ public:
|
||||
int numberToRemove)
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
startIndex = jlimit (0, values.size(), startIndex);
|
||||
auto endIndex = jlimit (0, values.size(), startIndex + numberToRemove);
|
||||
numberToRemove = endIndex - startIndex;
|
||||
|
||||
auto start = jlimit (0, numUsed, startIndex);
|
||||
auto endIndex = jlimit (0, numUsed, startIndex + numberToRemove);
|
||||
|
||||
if (endIndex > start)
|
||||
if (numberToRemove > 0)
|
||||
{
|
||||
for (int i = start; i < endIndex; ++i)
|
||||
for (int i = startIndex; i < endIndex; ++i)
|
||||
{
|
||||
releaseObject (data.elements[i]);
|
||||
data.elements[i] = nullptr; // (in case one of the destructors accesses this array and hits a dangling pointer)
|
||||
releaseObject (values[i]);
|
||||
values[i] = nullptr; // (in case one of the destructors accesses this array and hits a dangling pointer)
|
||||
}
|
||||
|
||||
auto rangeSize = endIndex - start;
|
||||
auto** e = data.elements + start;
|
||||
int i = numUsed - endIndex;
|
||||
numUsed -= rangeSize;
|
||||
values.removeElements (startIndex, numberToRemove);
|
||||
|
||||
while (--i >= 0)
|
||||
{
|
||||
*e = e[rangeSize];
|
||||
++e;
|
||||
}
|
||||
|
||||
if ((numUsed << 1) < data.numAllocated)
|
||||
if ((values.size() << 1) < values.capacity())
|
||||
minimiseStorageOverheads();
|
||||
}
|
||||
}
|
||||
@ -697,11 +681,11 @@ public:
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
|
||||
if (howManyToRemove > numUsed)
|
||||
howManyToRemove = numUsed;
|
||||
if (howManyToRemove > values.size())
|
||||
howManyToRemove = values.size();
|
||||
|
||||
while (--howManyToRemove >= 0)
|
||||
remove (numUsed - 1);
|
||||
remove (values.size() - 1);
|
||||
}
|
||||
|
||||
/** Swaps a pair of objects in the array.
|
||||
@ -713,11 +697,10 @@ public:
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
|
||||
if (isPositiveAndBelow (index1, numUsed)
|
||||
&& isPositiveAndBelow (index2, numUsed))
|
||||
if (isPositiveAndBelow (index1, values.size())
|
||||
&& isPositiveAndBelow (index2, values.size()))
|
||||
{
|
||||
std::swap (data.elements[index1],
|
||||
data.elements[index2]);
|
||||
std::swap (values[index1], values[index2]);
|
||||
}
|
||||
}
|
||||
|
||||
@ -739,29 +722,7 @@ public:
|
||||
if (currentIndex != newIndex)
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
|
||||
if (isPositiveAndBelow (currentIndex, numUsed))
|
||||
{
|
||||
if (! isPositiveAndBelow (newIndex, numUsed))
|
||||
newIndex = numUsed - 1;
|
||||
|
||||
auto* 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;
|
||||
}
|
||||
values.move (currentIndex, newIndex);
|
||||
}
|
||||
}
|
||||
|
||||
@ -776,8 +737,7 @@ public:
|
||||
{
|
||||
const ScopedLockType lock1 (getLock());
|
||||
const typename OtherArrayType::ScopedLockType lock2 (otherArray.getLock());
|
||||
data.swapWith (otherArray.data);
|
||||
std::swap (numUsed, otherArray.numUsed);
|
||||
values.swapWith (otherArray.values);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
@ -789,15 +749,7 @@ public:
|
||||
{
|
||||
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;
|
||||
return values == other.values;
|
||||
}
|
||||
|
||||
/** Compares this array to another one.
|
||||
@ -840,11 +792,12 @@ public:
|
||||
void sort (ElementComparator& comparator,
|
||||
bool retainOrderOfEquivalentItems = false) const noexcept
|
||||
{
|
||||
ignoreUnused (comparator); // if you pass in an object with a static compareElements() method, this
|
||||
// avoids getting warning messages about the parameter being unused
|
||||
// If you pass in an object with a static compareElements() method, this
|
||||
// avoids getting warning messages about the parameter being unused
|
||||
ignoreUnused (comparator);
|
||||
|
||||
const ScopedLockType lock (getLock());
|
||||
sortArray (comparator, data.elements.get(), 0, size() - 1, retainOrderOfEquivalentItems);
|
||||
sortArray (comparator, values.begin(), 0, values.size() - 1, retainOrderOfEquivalentItems);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
@ -857,7 +810,7 @@ public:
|
||||
void minimiseStorageOverheads() noexcept
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
data.shrinkToNoMoreThan (numUsed);
|
||||
values.shrinkToNoMoreThan (values.size());
|
||||
}
|
||||
|
||||
/** Increases the array's internal storage to hold a minimum number of elements.
|
||||
@ -869,7 +822,7 @@ public:
|
||||
void ensureStorageAllocated (const int minNumElements)
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
data.ensureAllocatedSize (minNumElements);
|
||||
values.ensureAllocatedSize (minNumElements);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
@ -877,12 +830,11 @@ public:
|
||||
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; }
|
||||
inline const TypeOfCriticalSectionToUse& getLock() const noexcept { return values; }
|
||||
|
||||
/** Returns the type of scoped lock to use for locking this array */
|
||||
using ScopedLockType = typename TypeOfCriticalSectionToUse::ScopedLockType;
|
||||
|
||||
|
||||
//==============================================================================
|
||||
#ifndef DOXYGEN
|
||||
// Note that the swapWithArray method has been replaced by a more flexible templated version,
|
||||
@ -892,15 +844,14 @@ public:
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
ArrayAllocationBase<ObjectClass*, TypeOfCriticalSectionToUse> data;
|
||||
int numUsed = 0;
|
||||
ArrayBase<ObjectClass*, TypeOfCriticalSectionToUse> values;
|
||||
|
||||
void releaseAllObjects()
|
||||
{
|
||||
while (numUsed > 0)
|
||||
releaseObject (data.elements[--numUsed]);
|
||||
for (auto& v : values)
|
||||
releaseObject (v);
|
||||
|
||||
jassert (numUsed == 0);
|
||||
values.clear();
|
||||
}
|
||||
|
||||
static void releaseObject (ObjectClass* o)
|
||||
|
@ -59,24 +59,24 @@ public:
|
||||
//==============================================================================
|
||||
/** Creates an empty set. */
|
||||
// VS2013 doesn't allow defaulted noexcept constructors.
|
||||
SortedSet() noexcept {}
|
||||
SortedSet() = default;
|
||||
|
||||
/** Creates a copy of another set. */
|
||||
SortedSet (const SortedSet&) = default;
|
||||
|
||||
/** Creates a copy of another set. */
|
||||
// VS2013 doesn't allow defaulted noexcept constructors.
|
||||
SortedSet (SortedSet&& other) noexcept : data (static_cast<decltype(data)&&> (other.data)) {}
|
||||
SortedSet (SortedSet&& other) noexcept : data (std::move (other.data)) {}
|
||||
|
||||
/** Makes a copy of another set. */
|
||||
SortedSet& operator= (const SortedSet&) = default;
|
||||
|
||||
/** Makes a copy of another set. */
|
||||
// VS2013 doesn't allow defaulted noexcept constructors.
|
||||
SortedSet& operator= (SortedSet&& other) noexcept { data = static_cast<decltype(data)&&> (other.data); return *this; }
|
||||
SortedSet& operator= (SortedSet&& other) noexcept { data = std::move (other.data); return *this; }
|
||||
|
||||
/** Destructor. */
|
||||
~SortedSet() noexcept {}
|
||||
~SortedSet() = default;
|
||||
|
||||
//==============================================================================
|
||||
/** Compares this set to another one.
|
||||
|
@ -41,13 +41,13 @@ class SparseSet
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
SparseSet() noexcept {}
|
||||
SparseSet() = default;
|
||||
|
||||
SparseSet (const SparseSet&) = default;
|
||||
SparseSet& operator= (const SparseSet&) = default;
|
||||
|
||||
SparseSet (SparseSet&& other) noexcept : ranges (static_cast<Array<Range<Type>>&&> (other.ranges)) {}
|
||||
SparseSet& operator= (SparseSet&& other) noexcept { ranges = static_cast<Array<Range<Type>>&&> (other.ranges); return *this; }
|
||||
SparseSet (SparseSet&& other) noexcept : ranges (std::move (other.ranges)) {}
|
||||
SparseSet& operator= (SparseSet&& other) noexcept { ranges = std::move (other.ranges); return *this; }
|
||||
|
||||
//==============================================================================
|
||||
/** Clears the set. */
|
||||
|
@ -53,17 +53,18 @@ public:
|
||||
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 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 bool isComparable() const noexcept { return false; }
|
||||
|
||||
virtual void cleanUp (ValueUnion&) const noexcept {}
|
||||
virtual void createCopy (ValueUnion& dest, const ValueUnion& source) const { dest = source; }
|
||||
@ -78,7 +79,8 @@ public:
|
||||
VariantType_Void() noexcept {}
|
||||
static const VariantType_Void instance;
|
||||
|
||||
bool isVoid() const noexcept override { return true; }
|
||||
bool isVoid() const noexcept override { return true; }
|
||||
bool isComparable() 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); }
|
||||
};
|
||||
@ -114,6 +116,7 @@ public:
|
||||
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 isComparable() const noexcept override { return true; }
|
||||
|
||||
bool equals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) const noexcept override
|
||||
{
|
||||
@ -144,6 +147,7 @@ public:
|
||||
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 isComparable() const noexcept override { return true; }
|
||||
|
||||
bool equals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) const noexcept override
|
||||
{
|
||||
@ -171,9 +175,10 @@ public:
|
||||
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, 20); }
|
||||
String toString (const ValueUnion& data) const override { return serialiseDouble (data.doubleValue); }
|
||||
bool toBool (const ValueUnion& data) const noexcept override { return data.doubleValue != 0.0; }
|
||||
bool isDouble() const noexcept override { return true; }
|
||||
bool isComparable() const noexcept override { return true; }
|
||||
|
||||
bool equals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) const noexcept override
|
||||
{
|
||||
@ -201,6 +206,7 @@ public:
|
||||
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 isComparable() const noexcept override { return true; }
|
||||
|
||||
bool equals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) const noexcept override
|
||||
{
|
||||
@ -232,6 +238,7 @@ public:
|
||||
bool toBool (const ValueUnion& data) const noexcept override { return getString (data)->getIntValue() != 0
|
||||
|| getString (data)->trim().equalsIgnoreCase ("true")
|
||||
|| getString (data)->trim().equalsIgnoreCase ("yes"); }
|
||||
bool isComparable() const noexcept override { return true; }
|
||||
|
||||
bool equals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) const noexcept override
|
||||
{
|
||||
@ -356,7 +363,7 @@ public:
|
||||
struct RefCountedArray : public ReferenceCountedObject
|
||||
{
|
||||
RefCountedArray (const Array<var>& a) : array (a) { incReferenceCount(); }
|
||||
RefCountedArray (Array<var>&& a) : array (static_cast<Array<var>&&> (a)) { incReferenceCount(); }
|
||||
RefCountedArray (Array<var>&& a) : array (std::move (a)) { incReferenceCount(); }
|
||||
Array<var> array;
|
||||
};
|
||||
};
|
||||
@ -536,24 +543,24 @@ var& var::operator= (var&& other) noexcept
|
||||
|
||||
var::var (String&& v) : type (&VariantType_String::instance)
|
||||
{
|
||||
new (value.stringValue) String (static_cast<String&&> (v));
|
||||
new (value.stringValue) String (std::move (v));
|
||||
}
|
||||
|
||||
var::var (MemoryBlock&& v) : type (&VariantType_Binary::instance)
|
||||
{
|
||||
value.binaryValue = new MemoryBlock (static_cast<MemoryBlock&&> (v));
|
||||
value.binaryValue = new MemoryBlock (std::move (v));
|
||||
}
|
||||
|
||||
var::var (Array<var>&& v) : type (&VariantType_Array::instance)
|
||||
{
|
||||
value.objectValue = new VariantType_Array::RefCountedArray (static_cast<Array<var>&&> (v));
|
||||
value.objectValue = new VariantType_Array::RefCountedArray (std::move (v));
|
||||
}
|
||||
|
||||
var& var::operator= (String&& v)
|
||||
{
|
||||
type->cleanUp (value);
|
||||
type = &VariantType_String::instance;
|
||||
new (value.stringValue) String (static_cast<String&&> (v));
|
||||
new (value.stringValue) String (std::move (v));
|
||||
return *this;
|
||||
}
|
||||
|
||||
@ -565,7 +572,7 @@ bool var::equals (const var& other) const noexcept
|
||||
|
||||
bool var::equalsWithSameType (const var& other) const noexcept
|
||||
{
|
||||
return type == other.type && equals (other);
|
||||
return hasSameTypeAs (other) && equals (other);
|
||||
}
|
||||
|
||||
bool var::hasSameTypeAs (const var& other) const noexcept
|
||||
@ -573,12 +580,31 @@ 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; }
|
||||
bool canCompare (const var& v1, const var& v2)
|
||||
{
|
||||
return v1.type->isComparable() && v2.type->isComparable();
|
||||
}
|
||||
|
||||
static int compare (const var& v1, const var& v2)
|
||||
{
|
||||
if (v1.isString() && v2.isString())
|
||||
return v1.toString().compare (v2.toString());
|
||||
|
||||
auto diff = static_cast<double> (v1) - static_cast<double> (v2);
|
||||
return diff == 0 ? 0 : (diff < 0 ? -1 : 1);
|
||||
}
|
||||
|
||||
bool operator== (const var& v1, const var& v2) { return v1.equals (v2); }
|
||||
bool operator!= (const var& v1, const var& v2) { return ! v1.equals (v2); }
|
||||
bool operator< (const var& v1, const var& v2) { return canCompare (v1, v2) && compare (v1, v2) < 0; }
|
||||
bool operator> (const var& v1, const var& v2) { return canCompare (v1, v2) && compare (v1, v2) > 0; }
|
||||
bool operator<= (const var& v1, const var& v2) { return canCompare (v1, v2) && compare (v1, v2) <= 0; }
|
||||
bool operator>= (const var& v1, const var& v2) { return canCompare (v1, v2) && compare (v1, v2) >= 0; }
|
||||
|
||||
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* v2) { return v1.toString() == v2; }
|
||||
bool operator!= (const var& v1, const char* v2) { return v1.toString() != v2; }
|
||||
|
||||
//==============================================================================
|
||||
var var::clone() const noexcept
|
||||
@ -666,7 +692,7 @@ var var::call (const Identifier& method, const var& arg1, const var& arg2, const
|
||||
//==============================================================================
|
||||
int var::size() const
|
||||
{
|
||||
if (auto* array = getArray())
|
||||
if (auto array = getArray())
|
||||
return array->size();
|
||||
|
||||
return 0;
|
||||
@ -674,7 +700,7 @@ int var::size() const
|
||||
|
||||
const var& var::operator[] (int arrayIndex) const
|
||||
{
|
||||
auto* array = getArray();
|
||||
auto array = getArray();
|
||||
|
||||
// When using this method, the var must actually be an array, and the index
|
||||
// must be in-range!
|
||||
@ -685,7 +711,7 @@ const var& var::operator[] (int arrayIndex) const
|
||||
|
||||
var& var::operator[] (int arrayIndex)
|
||||
{
|
||||
auto* array = getArray();
|
||||
auto array = getArray();
|
||||
|
||||
// When using this method, the var must actually be an array, and the index
|
||||
// must be in-range!
|
||||
@ -696,10 +722,11 @@ var& var::operator[] (int arrayIndex)
|
||||
|
||||
Array<var>* var::convertToArray()
|
||||
{
|
||||
if (auto* array = getArray())
|
||||
if (auto array = getArray())
|
||||
return array;
|
||||
|
||||
Array<var> tempVar;
|
||||
|
||||
if (! isVoid())
|
||||
tempVar.add (*this);
|
||||
|
||||
@ -714,7 +741,7 @@ void var::append (const var& n)
|
||||
|
||||
void var::remove (const int index)
|
||||
{
|
||||
if (auto* const array = getArray())
|
||||
if (auto array = getArray())
|
||||
array->remove (index);
|
||||
}
|
||||
|
||||
@ -730,7 +757,7 @@ void var::resize (const int numArrayElementsWanted)
|
||||
|
||||
int var::indexOf (const var& n) const
|
||||
{
|
||||
if (auto* const array = getArray())
|
||||
if (auto array = getArray())
|
||||
return array->indexOf (n);
|
||||
|
||||
return -1;
|
||||
@ -755,6 +782,7 @@ var var::readFromStream (InputStream& input)
|
||||
case varMarker_BoolTrue: return var (true);
|
||||
case varMarker_BoolFalse: return var (false);
|
||||
case varMarker_Double: return var (input.readDouble());
|
||||
|
||||
case varMarker_String:
|
||||
{
|
||||
MemoryOutputStream mo;
|
||||
|
@ -286,18 +286,18 @@ public:
|
||||
|
||||
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;
|
||||
class VariantType;
|
||||
class VariantType_Void;
|
||||
class VariantType_Undefined;
|
||||
class VariantType_Int;
|
||||
class VariantType_Int64;
|
||||
class VariantType_Double;
|
||||
class VariantType_Bool;
|
||||
class VariantType_String;
|
||||
class VariantType_Object;
|
||||
class VariantType_Array;
|
||||
class VariantType_Binary;
|
||||
class VariantType_Method;
|
||||
|
||||
union ValueUnion
|
||||
{
|
||||
@ -311,17 +311,32 @@ private:
|
||||
NativeFunction* methodValue;
|
||||
};
|
||||
|
||||
friend bool canCompare (const var&, const var&);
|
||||
|
||||
const VariantType* type;
|
||||
ValueUnion value;
|
||||
|
||||
Array<var>* convertToArray();
|
||||
var (const VariantType&) noexcept;
|
||||
|
||||
// This is needed to prevent the wrong constructor/operator being called
|
||||
var (const ReferenceCountedObject*) = delete;
|
||||
var& operator= (const ReferenceCountedObject*) = delete;
|
||||
};
|
||||
|
||||
/** Compares the values of two var objects, using the var::equals() comparison. */
|
||||
JUCE_API bool operator== (const var&, const var&) noexcept;
|
||||
JUCE_API bool operator== (const var&, const var&);
|
||||
/** Compares the values of two var objects, using the var::equals() comparison. */
|
||||
JUCE_API bool operator!= (const var&, const var&) noexcept;
|
||||
JUCE_API bool operator!= (const var&, const var&);
|
||||
/** Compares the values of two var objects, using the var::equals() comparison. */
|
||||
JUCE_API bool operator< (const var&, const var&);
|
||||
/** Compares the values of two var objects, using the var::equals() comparison. */
|
||||
JUCE_API bool operator<= (const var&, const var&);
|
||||
/** Compares the values of two var objects, using the var::equals() comparison. */
|
||||
JUCE_API bool operator> (const var&, const var&);
|
||||
/** Compares the values of two var objects, using the var::equals() comparison. */
|
||||
JUCE_API bool operator>= (const var&, const var&);
|
||||
|
||||
JUCE_API bool operator== (const var&, const String&);
|
||||
JUCE_API bool operator!= (const var&, const String&);
|
||||
JUCE_API bool operator== (const var&, const char*);
|
||||
|
Reference in New Issue
Block a user