git subrepo clone --branch=sono6good https://github.com/essej/JUCE.git deps/juce

subrepo:
  subdir:   "deps/juce"
  merged:   "b13f9084e"
upstream:
  origin:   "https://github.com/essej/JUCE.git"
  branch:   "sono6good"
  commit:   "b13f9084e"
git-subrepo:
  version:  "0.4.3"
  origin:   "https://github.com/ingydotnet/git-subrepo.git"
  commit:   "2f68596"
This commit is contained in:
essej
2022-04-18 17:51:22 -04:00
parent 63e175fee6
commit 25bd5d8adb
3210 changed files with 1045392 additions and 0 deletions

View File

@ -0,0 +1,271 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
AbstractFifo::AbstractFifo (int capacity) noexcept : bufferSize (capacity)
{
jassert (bufferSize > 0);
}
AbstractFifo::~AbstractFifo() {}
int AbstractFifo::getTotalSize() const noexcept { return bufferSize; }
int AbstractFifo::getFreeSpace() const noexcept { return bufferSize - getNumReady() - 1; }
int AbstractFifo::getNumReady() const noexcept
{
auto vs = validStart.get();
auto ve = validEnd.get();
return ve >= vs ? (ve - vs) : (bufferSize - (vs - ve));
}
void AbstractFifo::reset() noexcept
{
validEnd = 0;
validStart = 0;
}
void AbstractFifo::setTotalSize (int newSize) noexcept
{
jassert (newSize > 0);
reset();
bufferSize = newSize;
}
//==============================================================================
void AbstractFifo::prepareToWrite (int numToWrite, int& startIndex1, int& blockSize1,
int& startIndex2, int& blockSize2) const noexcept
{
auto vs = validStart.get();
auto ve = validEnd.get();
auto freeSpace = ve >= vs ? (bufferSize - (ve - vs)) : (vs - ve);
numToWrite = jmin (numToWrite, freeSpace - 1);
if (numToWrite <= 0)
{
startIndex1 = 0;
startIndex2 = 0;
blockSize1 = 0;
blockSize2 = 0;
}
else
{
startIndex1 = ve;
startIndex2 = 0;
blockSize1 = jmin (bufferSize - ve, numToWrite);
numToWrite -= blockSize1;
blockSize2 = numToWrite <= 0 ? 0 : jmin (numToWrite, vs);
}
}
void AbstractFifo::finishedWrite (int numWritten) noexcept
{
jassert (numWritten >= 0 && numWritten < bufferSize);
auto newEnd = validEnd.get() + numWritten;
if (newEnd >= bufferSize)
newEnd -= bufferSize;
validEnd = newEnd;
}
void AbstractFifo::prepareToRead (int numWanted, int& startIndex1, int& blockSize1,
int& startIndex2, int& blockSize2) const noexcept
{
auto vs = validStart.get();
auto ve = validEnd.get();
auto numReady = ve >= vs ? (ve - vs) : (bufferSize - (vs - ve));
numWanted = jmin (numWanted, numReady);
if (numWanted <= 0)
{
startIndex1 = 0;
startIndex2 = 0;
blockSize1 = 0;
blockSize2 = 0;
}
else
{
startIndex1 = vs;
startIndex2 = 0;
blockSize1 = jmin (bufferSize - vs, numWanted);
numWanted -= blockSize1;
blockSize2 = numWanted <= 0 ? 0 : jmin (numWanted, ve);
}
}
void AbstractFifo::finishedRead (int numRead) noexcept
{
jassert (numRead >= 0 && numRead <= bufferSize);
auto newStart = validStart.get() + numRead;
if (newStart >= bufferSize)
newStart -= bufferSize;
validStart = newStart;
}
//==============================================================================
template <AbstractFifo::ReadOrWrite mode>
AbstractFifo::ScopedReadWrite<mode>::ScopedReadWrite (ScopedReadWrite&& other) noexcept
: startIndex1 (other.startIndex1),
blockSize1 (other.blockSize1),
startIndex2 (other.startIndex2),
blockSize2 (other.blockSize2)
{
swap (other);
}
template <AbstractFifo::ReadOrWrite mode>
AbstractFifo::ScopedReadWrite<mode>&
AbstractFifo::ScopedReadWrite<mode>::operator= (ScopedReadWrite&& other) noexcept
{
swap (other);
return *this;
}
template <AbstractFifo::ReadOrWrite mode>
void AbstractFifo::ScopedReadWrite<mode>::swap (ScopedReadWrite& other) noexcept
{
std::swap (other.fifo, fifo);
std::swap (other.startIndex1, startIndex1);
std::swap (other.blockSize1, blockSize1);
std::swap (other.startIndex2, startIndex2);
std::swap (other.blockSize2, blockSize2);
}
template class AbstractFifo::ScopedReadWrite<AbstractFifo::ReadOrWrite::read>;
template class AbstractFifo::ScopedReadWrite<AbstractFifo::ReadOrWrite::write>;
AbstractFifo::ScopedRead AbstractFifo::read (int numToRead) noexcept { return { *this, numToRead }; }
AbstractFifo::ScopedWrite AbstractFifo::write (int numToWrite) noexcept { return { *this, numToWrite }; }
//==============================================================================
//==============================================================================
#if JUCE_UNIT_TESTS
class AbstractFifoTests : public UnitTest
{
public:
AbstractFifoTests()
: UnitTest ("Abstract Fifo", UnitTestCategories::containers)
{}
struct WriteThread : public Thread
{
WriteThread (AbstractFifo& f, int* b, Random rng)
: Thread ("fifo writer"), fifo (f), buffer (b), random (rng)
{
startThread();
}
~WriteThread()
{
stopThread (5000);
}
void run()
{
int n = 0;
while (! threadShouldExit())
{
int num = random.nextInt (2000) + 1;
auto writer = fifo.write (num);
jassert (writer.blockSize1 >= 0 && writer.blockSize2 >= 0);
jassert (writer.blockSize1 == 0
|| (writer.startIndex1 >= 0 && writer.startIndex1 < fifo.getTotalSize()));
jassert (writer.blockSize2 == 0
|| (writer.startIndex2 >= 0 && writer.startIndex2 < fifo.getTotalSize()));
writer.forEach ([this, &n] (int index) { this->buffer[index] = n++; });
}
}
AbstractFifo& fifo;
int* buffer;
Random random;
};
JUCE_BEGIN_IGNORE_WARNINGS_MSVC (6262)
void runTest() override
{
beginTest ("AbstractFifo");
int buffer[5000];
AbstractFifo fifo (numElementsInArray (buffer));
WriteThread writer (fifo, buffer, getRandom());
int n = 0;
Random r = getRandom();
r.combineSeed (12345);
for (int count = 100000; --count >= 0;)
{
int num = r.nextInt (6000) + 1;
auto reader = fifo.read (num);
if (! (reader.blockSize1 >= 0 && reader.blockSize2 >= 0)
&& (reader.blockSize1 == 0
|| (reader.startIndex1 >= 0 && reader.startIndex1 < fifo.getTotalSize()))
&& (reader.blockSize2 == 0
|| (reader.startIndex2 >= 0 && reader.startIndex2 < fifo.getTotalSize())))
{
expect (false, "prepareToRead returned -ve values");
break;
}
bool failed = false;
reader.forEach ([&failed, &buffer, &n] (int index)
{
failed = (buffer[index] != n++) || failed;
});
if (failed)
{
expect (false, "read values were incorrect");
break;
}
}
}
JUCE_END_IGNORE_WARNINGS_MSVC
};
static AbstractFifoTests fifoUnitTests;
#endif
} // namespace juce

View File

@ -0,0 +1,336 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
/**
Encapsulates the logic required to implement a lock-free FIFO.
This class handles the logic needed when building a single-reader, single-writer FIFO.
It doesn't actually hold any data itself, but your FIFO class can use one of these to manage
its position and status when reading or writing to it.
To use it, you can call prepareToWrite() to determine the position within your own buffer that
an incoming block of data should be stored, and prepareToRead() to find out when the next
outgoing block should be read from.
e.g.
@code
struct MyFifo
{
void addToFifo (const int* someData, int numItems)
{
const auto scope = abstractFifo.write (numItems);
if (scope.blockSize1 > 0)
copySomeData (myBuffer + scope.startIndex1, someData, scope.blockSize1);
if (scope.blockSize2 > 0)
copySomeData (myBuffer + scope.startIndex2, someData, scope.blockSize2);
}
void readFromFifo (int* someData, int numItems)
{
const auto scope = abstractFifo.read (numItems);
if (scope.blockSize1 > 0)
copySomeData (someData, myBuffer + scope.startIndex1, scope.blockSize1);
if (scope.blockSize2 > 0)
copySomeData (someData + scope.blockSize1, myBuffer + scope.startIndex2, scope.blockSize2);
}
AbstractFifo abstractFifo { 1024 };
int myBuffer[1024];
};
@endcode
@tags{Core}
*/
class JUCE_API AbstractFifo
{
public:
//==============================================================================
/** Creates a FIFO to manage a buffer with the specified capacity. */
AbstractFifo (int capacity) noexcept;
/** Destructor */
~AbstractFifo();
//==============================================================================
/** Returns the total size of the buffer being managed. */
int getTotalSize() const noexcept;
/** Returns the number of items that can currently be added to the buffer without it overflowing. */
int getFreeSpace() const noexcept;
/** Returns the number of items that can currently be read from the buffer. */
int getNumReady() const noexcept;
/** Clears the buffer positions, so that it appears empty. */
void reset() noexcept;
/** Changes the buffer's total size.
Note that this isn't thread-safe, so don't call it if there's any danger that it
might overlap with a call to any other method in this class!
*/
void setTotalSize (int newSize) noexcept;
//==============================================================================
/** Returns the location within the buffer at which an incoming block of data should be written.
Because the section of data that you want to add to the buffer may overlap the end
and wrap around to the start, two blocks within your buffer are returned, and you
should copy your data into the first one, with any remaining data spilling over into
the second.
If the number of items you ask for is too large to fit within the buffer's free space, then
blockSize1 + blockSize2 may add up to a lower value than numToWrite. If this happens, you
may decide to keep waiting and re-trying the method until there's enough space available.
After calling this method, if you choose to write your data into the blocks returned, you
must call finishedWrite() to tell the FIFO how much data you actually added.
e.g.
@code
void addToFifo (const int* someData, int numItems)
{
int start1, size1, start2, size2;
prepareToWrite (numItems, start1, size1, start2, size2);
if (size1 > 0)
copySomeData (myBuffer + start1, someData, size1);
if (size2 > 0)
copySomeData (myBuffer + start2, someData + size1, size2);
finishedWrite (size1 + size2);
}
@endcode
@param numToWrite indicates how many items you'd like to add to the buffer
@param startIndex1 on exit, this will contain the start index in your buffer at which your data should be written
@param blockSize1 on exit, this indicates how many items can be written to the block starting at startIndex1
@param startIndex2 on exit, this will contain the start index in your buffer at which any data that didn't fit into
the first block should be written
@param blockSize2 on exit, this indicates how many items can be written to the block starting at startIndex2
@see finishedWrite
*/
void prepareToWrite (int numToWrite, int& startIndex1, int& blockSize1, int& startIndex2, int& blockSize2) const noexcept;
/** Called after writing from the FIFO, to indicate that this many items have been added.
@see prepareToWrite
*/
void finishedWrite (int numWritten) noexcept;
/** Returns the location within the buffer from which the next block of data should be read.
Because the section of data that you want to read from the buffer may overlap the end
and wrap around to the start, two blocks within your buffer are returned, and you
should read from both of them.
If the number of items you ask for is greater than the amount of data available, then
blockSize1 + blockSize2 may add up to a lower value than numWanted. If this happens, you
may decide to keep waiting and re-trying the method until there's enough data available.
After calling this method, if you choose to read the data, you must call finishedRead() to
tell the FIFO how much data you have consumed.
e.g.
@code
void readFromFifo (int* someData, int numItems)
{
int start1, size1, start2, size2;
prepareToRead (numSamples, start1, size1, start2, size2);
if (size1 > 0)
copySomeData (someData, myBuffer + start1, size1);
if (size2 > 0)
copySomeData (someData + size1, myBuffer + start2, size2);
finishedRead (size1 + size2);
}
@endcode
@param numWanted indicates how many items you'd like to add to the buffer
@param startIndex1 on exit, this will contain the start index in your buffer at which your data should be written
@param blockSize1 on exit, this indicates how many items can be written to the block starting at startIndex1
@param startIndex2 on exit, this will contain the start index in your buffer at which any data that didn't fit into
the first block should be written
@param blockSize2 on exit, this indicates how many items can be written to the block starting at startIndex2
@see finishedRead
*/
void prepareToRead (int numWanted, int& startIndex1, int& blockSize1, int& startIndex2, int& blockSize2) const noexcept;
/** Called after reading from the FIFO, to indicate that this many items have now been consumed.
@see prepareToRead
*/
void finishedRead (int numRead) noexcept;
//==============================================================================
private:
enum class ReadOrWrite
{
read,
write
};
public:
/** Class for a scoped reader/writer */
template <ReadOrWrite mode>
class ScopedReadWrite final
{
public:
/** Construct an unassigned reader/writer. Doesn't do anything upon destruction. */
ScopedReadWrite() = default;
/** Construct a reader/writer and immediately call prepareRead/prepareWrite
on the abstractFifo which was passed in.
This object will hold a pointer back to the fifo, so make sure that
the fifo outlives this object.
*/
ScopedReadWrite (AbstractFifo& f, int num) noexcept : fifo (&f)
{
prepare (*fifo, num);
}
ScopedReadWrite (const ScopedReadWrite&) = delete;
ScopedReadWrite (ScopedReadWrite&&) noexcept;
ScopedReadWrite& operator= (const ScopedReadWrite&) = delete;
ScopedReadWrite& operator= (ScopedReadWrite&&) noexcept;
/** Calls finishedRead or finishedWrite if this is a non-null scoped
reader/writer.
*/
~ScopedReadWrite() noexcept
{
if (fifo != nullptr)
finish (*fifo, blockSize1 + blockSize2);
}
/** Calls the passed function with each index that was deemed valid
for the current read/write operation.
*/
template <typename FunctionToApply>
void forEach (FunctionToApply&& func) const
{
for (auto i = startIndex1, e = startIndex1 + blockSize1; i != e; ++i) func (i);
for (auto i = startIndex2, e = startIndex2 + blockSize2; i != e; ++i) func (i);
}
int startIndex1, blockSize1, startIndex2, blockSize2;
private:
void prepare (AbstractFifo&, int) noexcept;
static void finish (AbstractFifo&, int) noexcept;
void swap (ScopedReadWrite&) noexcept;
AbstractFifo* fifo = nullptr;
};
using ScopedRead = ScopedReadWrite<ReadOrWrite::read>;
using ScopedWrite = ScopedReadWrite<ReadOrWrite::write>;
/** Replaces prepareToRead/finishedRead with a single function.
This function returns an object which contains the start indices and
block sizes, and also automatically finishes the read operation when
it goes out of scope.
@code
{
auto readHandle = fifo.read (4);
for (auto i = 0; i != readHandle.blockSize1; ++i)
{
// read the item at index readHandle.startIndex1 + i
}
for (auto i = 0; i != readHandle.blockSize2; ++i)
{
// read the item at index readHandle.startIndex2 + i
}
} // readHandle goes out of scope here, finishing the read operation
@endcode
*/
ScopedRead read (int numToRead) noexcept;
/** Replaces prepareToWrite/finishedWrite with a single function.
This function returns an object which contains the start indices and
block sizes, and also automatically finishes the write operation when
it goes out of scope.
@code
{
auto writeHandle = fifo.write (5);
for (auto i = 0; i != writeHandle.blockSize1; ++i)
{
// write the item at index writeHandle.startIndex1 + i
}
for (auto i = 0; i != writeHandle.blockSize2; ++i)
{
// write the item at index writeHandle.startIndex2 + i
}
} // writeHandle goes out of scope here, finishing the write operation
@endcode
*/
ScopedWrite write (int numToWrite) noexcept;
private:
//==============================================================================
int bufferSize;
Atomic<int> validStart, validEnd;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AbstractFifo)
};
template <>
inline void AbstractFifo::ScopedReadWrite<AbstractFifo::ReadOrWrite::read>::finish (AbstractFifo& f, int num) noexcept
{
f.finishedRead (num);
}
template <>
inline void AbstractFifo::ScopedReadWrite<AbstractFifo::ReadOrWrite::write>::finish (AbstractFifo& f, int num) noexcept
{
f.finishedWrite (num);
}
template <>
inline void AbstractFifo::ScopedReadWrite<AbstractFifo::ReadOrWrite::read>::prepare (AbstractFifo& f, int num) noexcept
{
f.prepareToRead (num, startIndex1, blockSize1, startIndex2, blockSize2);
}
template <>
inline void AbstractFifo::ScopedReadWrite<AbstractFifo::ReadOrWrite::write>::prepare (AbstractFifo& f, int num) noexcept
{
f.prepareToWrite (num, startIndex1, blockSize1, startIndex2, blockSize2);
}
} // namespace juce

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,121 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
/**
Implements some basic array storage allocation functions.
This class isn't really for public use - it used to be part of the
container classes but has since been superseded by ArrayBase. Eventually
it will be removed from the API.
@tags{Core}
*/
template <class ElementType, class TypeOfCriticalSectionToUse>
class ArrayAllocationBase : public TypeOfCriticalSectionToUse
{
public:
//==============================================================================
/** Creates an empty array. */
ArrayAllocationBase() = default;
/** Destructor. */
~ArrayAllocationBase() = default;
ArrayAllocationBase (ArrayAllocationBase&& other) noexcept
: elements (std::move (other.elements)),
numAllocated (other.numAllocated)
{
}
ArrayAllocationBase& operator= (ArrayAllocationBase&& other) noexcept
{
elements = std::move (other.elements);
numAllocated = other.numAllocated;
return *this;
}
//==============================================================================
/** Changes the amount of storage allocated.
This will retain any data currently held in the array, and either add or
remove extra space at the end.
@param numElements the number of elements that are needed
*/
void setAllocatedSize (int numElements)
{
if (numAllocated != numElements)
{
if (numElements > 0)
elements.realloc ((size_t) numElements);
else
elements.free();
numAllocated = numElements;
}
}
/** Increases the amount of storage allocated if it is less than a given amount.
This will retain any data currently held in the array, but will add
extra space at the end to make sure there it's at least as big as the size
passed in. If it's already bigger, no action is taken.
@param minNumElements the minimum number of elements that are needed
*/
void ensureAllocatedSize (int minNumElements)
{
if (minNumElements > numAllocated)
setAllocatedSize ((minNumElements + minNumElements / 2 + 8) & ~7);
jassert (numAllocated <= 0 || elements != nullptr);
}
/** Minimises the amount of storage allocated so that it's no more than
the given number of elements.
*/
void shrinkToNoMoreThan (int maxNumElements)
{
if (maxNumElements < numAllocated)
setAllocatedSize (maxNumElements);
}
/** Swap the contents of two objects. */
void swapWith (ArrayAllocationBase& other) noexcept
{
elements.swapWith (other.elements);
std::swap (numAllocated, other.numAllocated);
}
//==============================================================================
HeapBlock<ElementType> elements;
int numAllocated = 0;
private:
JUCE_DECLARE_NON_COPYABLE (ArrayAllocationBase)
};
} // namespace juce

View File

@ -0,0 +1,600 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
#if JUCE_UNIT_TESTS
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;
};
}
static bool operator== (const ArrayBaseTestsHelpers::TriviallyCopyableType& tct,
const ArrayBaseTestsHelpers::NonTriviallyCopyableType& ntct)
{
return tct.getValue() == ntct.getValue();
}
static 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", UnitTestCategories::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 initializer_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

View File

@ -0,0 +1,607 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
/**
A 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) noexcept
{
jassert (elements != nullptr);
jassert (isPositiveAndBelow (index, numUsed));
return elements[index];
}
inline const 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() noexcept
{
return elements;
}
inline const ElementType* begin() const noexcept
{
return elements;
}
inline ElementType* end() noexcept
{
return elements + numUsed;
}
inline const ElementType* end() const noexcept
{
return elements + numUsed;
}
inline ElementType* data() noexcept
{
return elements;
}
inline const 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)
{
addImpl (newElement);
}
void add (ElementType&& newElement)
{
addImpl (std::move (newElement));
}
template <typename... OtherElements>
void add (const ElementType& firstNewElement, OtherElements&&... otherElements)
{
addImpl (firstNewElement, std::forward<OtherElements> (otherElements)...);
}
template <typename... OtherElements>
void add (ElementType&& firstNewElement, OtherElements&&... otherElements)
{
addImpl (std::move (firstNewElement), std::forward<OtherElements> (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)
{
if (numElements > 0)
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,
(size_t) (newIndex - currentIndex) * sizeof (ElementType));
}
else
{
memmove (elements + newIndex + 1,
elements + newIndex,
(size_t) (currentIndex - newIndex) * sizeof (ElementType));
}
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));
}
//==============================================================================
template <typename... Elements>
void addImpl (Elements&&... toAdd)
{
ignoreUnused (std::initializer_list<int> { (((void) checkSourceIsNotAMember (toAdd)), 0)... });
ensureAllocatedSize (numUsed + (int) sizeof... (toAdd));
addAssumingCapacityIsReady (std::forward<Elements> (toAdd)...);
}
template <typename... Elements>
void addAssumingCapacityIsReady (Elements&&... toAdd)
{
ignoreUnused (std::initializer_list<int> { ((void) (new (elements + numUsed++) ElementType (std::forward<Elements> (toAdd))), 0)... });
}
//==============================================================================
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)
jassertquiet (std::addressof (element) < begin() || end() <= std::addressof (element));
}
//==============================================================================
HeapBlock<ElementType> elements;
int numAllocated = 0, numUsed = 0;
template <class OtherElementType, class OtherCriticalSection>
friend class ArrayBase;
JUCE_DECLARE_NON_COPYABLE (ArrayBase)
};
} // namespace juce

View File

@ -0,0 +1,132 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
DynamicObject::DynamicObject()
{
}
DynamicObject::DynamicObject (const DynamicObject& other)
: ReferenceCountedObject(), properties (other.properties)
{
}
DynamicObject::~DynamicObject()
{
}
bool DynamicObject::hasProperty (const Identifier& propertyName) const
{
const var* const v = properties.getVarPointer (propertyName);
return v != nullptr && ! v->isMethod();
}
const var& DynamicObject::getProperty (const Identifier& propertyName) const
{
return properties [propertyName];
}
void DynamicObject::setProperty (const Identifier& propertyName, const var& newValue)
{
properties.set (propertyName, newValue);
}
void DynamicObject::removeProperty (const Identifier& propertyName)
{
properties.remove (propertyName);
}
bool DynamicObject::hasMethod (const Identifier& methodName) const
{
return getProperty (methodName).isMethod();
}
var DynamicObject::invokeMethod (Identifier method, const var::NativeFunctionArgs& args)
{
if (auto function = properties [method].getNativeFunction())
return function (args);
return {};
}
void DynamicObject::setMethod (Identifier name, var::NativeFunction function)
{
properties.set (name, var (function));
}
void DynamicObject::clear()
{
properties.clear();
}
void DynamicObject::cloneAllProperties()
{
for (int i = properties.size(); --i >= 0;)
if (auto* v = properties.getVarPointerAt (i))
*v = v->clone();
}
DynamicObject::Ptr DynamicObject::clone()
{
Ptr d (new DynamicObject (*this));
d->cloneAllProperties();
return d;
}
void DynamicObject::writeAsJSON (OutputStream& out, const int indentLevel, const bool allOnOneLine, int maximumDecimalPlaces)
{
out << '{';
if (! allOnOneLine)
out << newLine;
const int numValues = properties.size();
for (int i = 0; i < numValues; ++i)
{
if (! allOnOneLine)
JSONFormatter::writeSpaces (out, indentLevel + JSONFormatter::indentSize);
out << '"';
JSONFormatter::writeString (out, properties.getName (i));
out << "\": ";
JSONFormatter::write (out, properties.getValueAt (i), indentLevel + JSONFormatter::indentSize, allOnOneLine, maximumDecimalPlaces);
if (i < numValues - 1)
{
if (allOnOneLine)
out << ", ";
else
out << ',' << newLine;
}
else if (! allOnOneLine)
out << newLine;
}
if (! allOnOneLine)
JSONFormatter::writeSpaces (out, indentLevel);
out << '}';
}
} // namespace juce

View File

@ -0,0 +1,127 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
/**
Represents a dynamically implemented object.
This class is primarily intended for wrapping scripting language objects,
but could be used for other purposes.
An instance of a DynamicObject can be used to store named properties, and
by subclassing hasMethod() and invokeMethod(), you can give your object
methods.
@tags{Core}
*/
class JUCE_API DynamicObject : public ReferenceCountedObject
{
public:
//==============================================================================
DynamicObject();
DynamicObject (const DynamicObject&);
~DynamicObject() override;
using Ptr = ReferenceCountedObjectPtr<DynamicObject>;
//==============================================================================
/** Returns true if the object has a property with this name.
Note that if the property is actually a method, this will return false.
*/
virtual bool hasProperty (const Identifier& propertyName) const;
/** Returns a named property.
This returns var() if no such property exists.
*/
virtual const var& getProperty (const Identifier& propertyName) const;
/** Sets a named property. */
virtual void setProperty (const Identifier& propertyName, const var& newValue);
/** Removes a named property. */
virtual void removeProperty (const Identifier& propertyName);
//==============================================================================
/** Checks whether this object has the specified method.
The default implementation of this just checks whether there's a property
with this name that's actually a method, but this can be overridden for
building objects with dynamic invocation.
*/
virtual bool hasMethod (const Identifier& methodName) const;
/** Invokes a named method on this object.
The default implementation looks up the named property, and if it's a method
call, then it invokes it.
This method is virtual to allow more dynamic invocation to used for objects
where the methods may not already be set as properties.
*/
virtual var invokeMethod (Identifier methodName,
const var::NativeFunctionArgs& args);
/** Adds a method to the class.
This is basically the same as calling setProperty (methodName, (var::NativeFunction) myFunction), but
helps to avoid accidentally invoking the wrong type of var constructor. It also makes
the code easier to read.
*/
void setMethod (Identifier methodName, var::NativeFunction function);
//==============================================================================
/** Removes all properties and methods from the object. */
void clear();
/** Returns the NamedValueSet that holds the object's properties. */
NamedValueSet& getProperties() noexcept { return properties; }
/** Calls var::clone() on all the properties that this object contains. */
void cloneAllProperties();
//==============================================================================
/** Returns a clone of this object.
The default implementation of this method just returns a new DynamicObject
with a (deep) copy of all of its properties. Subclasses can override this to
implement their own custom copy routines.
*/
virtual Ptr clone();
//==============================================================================
/** Writes this object to a text stream in JSON format.
This method is used by JSON::toString and JSON::writeToStream, and you should
never need to call it directly, but it's virtual so that custom object types
can stringify themselves appropriately.
*/
virtual void writeAsJSON (OutputStream&, int indentLevel, bool allOnOneLine, int maximumDecimalPlaces);
private:
//==============================================================================
NamedValueSet properties;
JUCE_LEAK_DETECTOR (DynamicObject)
};
} // namespace juce

View File

@ -0,0 +1,197 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
#ifndef DOXYGEN
/** This is an internal helper class which converts a juce ElementComparator style
class (using a "compareElements" method) into a class that's compatible with
std::sort (i.e. using an operator() to compare the elements)
@tags{Core}
*/
template <typename ElementComparator>
struct SortFunctionConverter
{
SortFunctionConverter (ElementComparator& e) : comparator (e) {}
template <typename Type>
bool operator() (Type a, Type b) { return comparator.compareElements (a, b) < 0; }
private:
ElementComparator& comparator;
SortFunctionConverter& operator= (const SortFunctionConverter&) = delete;
};
#endif
//==============================================================================
/**
Sorts a range of elements in an array.
The comparator object that is passed-in must define a public method with the following
signature:
@code
int compareElements (ElementType first, ElementType second);
@endcode
..and this method must return:
- a value of < 0 if the first comes before the second
- a value of 0 if the two objects are equivalent
- a value of > 0 if the second comes before the first
To improve performance, the compareElements() method can be declared as static or const.
@param comparator an object which defines a compareElements() method
@param array the array to sort
@param firstElement the index of the first element of the range to be sorted
@param lastElement the index of the last element in the range that needs
sorting (this is inclusive)
@param retainOrderOfEquivalentItems if true, the order of items that the
comparator deems the same will be maintained - this will be
a slower algorithm than if they are allowed to be moved around.
@see sortArrayRetainingOrder
*/
template <class ElementType, class ElementComparator>
static void sortArray (ElementComparator& comparator,
ElementType* const array,
int firstElement,
int lastElement,
const bool retainOrderOfEquivalentItems)
{
jassert (firstElement >= 0);
if (lastElement > firstElement)
{
SortFunctionConverter<ElementComparator> converter (comparator);
if (retainOrderOfEquivalentItems)
std::stable_sort (array + firstElement, array + lastElement + 1, converter);
else
std::sort (array + firstElement, array + lastElement + 1, converter);
}
}
//==============================================================================
/**
Searches a sorted array of elements, looking for the index at which a specified value
should be inserted for it to be in the correct order.
The comparator object that is passed-in must define a public method with the following
signature:
@code
int compareElements (ElementType first, ElementType second);
@endcode
..and this method must return:
- a value of < 0 if the first comes before the second
- a value of 0 if the two objects are equivalent
- a value of > 0 if the second comes before the first
To improve performance, the compareElements() method can be declared as static or const.
@param comparator an object which defines a compareElements() method
@param array the array to search
@param newElement the value that is going to be inserted
@param firstElement the index of the first element to search
@param lastElement the index of the last element in the range (this is non-inclusive)
*/
template <class ElementType, class ElementComparator>
static int findInsertIndexInSortedArray (ElementComparator& comparator,
ElementType* const array,
const ElementType newElement,
int firstElement,
int lastElement)
{
jassert (firstElement <= lastElement);
ignoreUnused (comparator); // if you pass in an object with a static compareElements() method, this
// avoids getting warning messages about the parameter being unused
while (firstElement < lastElement)
{
if (comparator.compareElements (newElement, array [firstElement]) == 0)
{
++firstElement;
break;
}
else
{
const int halfway = (firstElement + lastElement) >> 1;
if (halfway == firstElement)
{
if (comparator.compareElements (newElement, array [halfway]) >= 0)
++firstElement;
break;
}
else if (comparator.compareElements (newElement, array [halfway]) >= 0)
{
firstElement = halfway;
}
else
{
lastElement = halfway;
}
}
}
return firstElement;
}
//==============================================================================
/**
A simple ElementComparator class that can be used to sort an array of
objects that support the '<' operator.
This will work for primitive types and objects that implement operator<().
Example: @code
Array<int> myArray;
DefaultElementComparator<int> sorter;
myArray.sort (sorter);
@endcode
@see ElementComparator
@tags{Core}
*/
template <class ElementType>
class DefaultElementComparator
{
private:
using ParameterType = typename TypeHelpers::ParameterType<ElementType>::type;
public:
static int compareElements (ParameterType first, ParameterType second)
{
return (first < second) ? -1 : ((second < first) ? 1 : 0);
}
};
} // namespace juce

View File

@ -0,0 +1,506 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
/**
A simple class to generate hash functions for some primitive types, intended for
use with the HashMap class.
@see HashMap
@tags{Core}
*/
struct DefaultHashFunctions
{
/** Generates a simple hash from an unsigned int. */
static int generateHash (uint32 key, int upperLimit) noexcept { return (int) (key % (uint32) upperLimit); }
/** Generates a simple hash from an integer. */
static int generateHash (int32 key, int upperLimit) noexcept { return generateHash ((uint32) key, upperLimit); }
/** Generates a simple hash from a uint64. */
static int generateHash (uint64 key, int upperLimit) noexcept { return (int) (key % (uint64) upperLimit); }
/** Generates a simple hash from an int64. */
static int generateHash (int64 key, int upperLimit) noexcept { return generateHash ((uint64) key, upperLimit); }
/** Generates a simple hash from a string. */
static int generateHash (const String& key, int upperLimit) noexcept { return generateHash ((uint32) key.hashCode(), upperLimit); }
/** Generates a simple hash from a variant. */
static int generateHash (const var& key, int upperLimit) noexcept { return generateHash (key.toString(), upperLimit); }
/** Generates a simple hash from a void ptr. */
static int generateHash (const void* key, int upperLimit) noexcept { return generateHash ((uint64) (pointer_sized_uint) key, upperLimit); }
/** Generates a simple hash from a UUID. */
static int generateHash (const Uuid& key, int upperLimit) noexcept { return generateHash (key.hash(), upperLimit); }
};
//==============================================================================
/**
Holds a set of mappings between some key/value pairs.
The types of the key and value objects are set as template parameters.
You can also specify a class to supply a hash function that converts a key value
into an hashed integer. This class must have the form:
@code
struct MyHashGenerator
{
int generateHash (MyKeyType key, int upperLimit) const
{
// The function must return a value 0 <= x < upperLimit
return someFunctionOfMyKeyType (key) % upperLimit;
}
};
@endcode
Like the Array class, the key and value types are expected to be copy-by-value
types, so if you define them to be pointer types, this class won't delete the
objects that they point to.
If you don't supply a class for the HashFunctionType template parameter, the
default one provides some simple mappings for strings and ints.
@code
HashMap<int, String> hash;
hash.set (1, "item1");
hash.set (2, "item2");
DBG (hash [1]); // prints "item1"
DBG (hash [2]); // prints "item2"
// This iterates the map, printing all of its key -> value pairs..
for (HashMap<int, String>::Iterator i (hash); i.next();)
DBG (i.getKey() << " -> " << i.getValue());
@endcode
@tparam HashFunctionType The class of hash function, which must be copy-constructible.
@see CriticalSection, DefaultHashFunctions, NamedValueSet, SortedSet
@tags{Core}
*/
template <typename KeyType,
typename ValueType,
class HashFunctionType = DefaultHashFunctions,
class TypeOfCriticalSectionToUse = DummyCriticalSection>
class HashMap
{
private:
using KeyTypeParameter = typename TypeHelpers::ParameterType<KeyType>::type;
using ValueTypeParameter = typename TypeHelpers::ParameterType<ValueType>::type;
public:
//==============================================================================
/** Creates an empty hash-map.
@param numberOfSlots Specifies the number of hash entries the map will use. This will be
the "upperLimit" parameter that is passed to your generateHash()
function. The number of hash slots will grow automatically if necessary,
or it can be remapped manually using remapTable().
@param hashFunction An instance of HashFunctionType, which will be copied and
stored to use with the HashMap. This parameter can be omitted
if HashFunctionType has a default constructor.
*/
explicit HashMap (int numberOfSlots = defaultHashTableSize,
HashFunctionType hashFunction = HashFunctionType())
: hashFunctionToUse (hashFunction)
{
hashSlots.insertMultiple (0, nullptr, numberOfSlots);
}
/** Destructor. */
~HashMap()
{
clear();
}
//==============================================================================
/** Removes all values from the map.
Note that this will clear the content, but won't affect the number of slots (see
remapTable and getNumSlots).
*/
void clear()
{
const ScopedLockType sl (getLock());
for (auto i = hashSlots.size(); --i >= 0;)
{
auto* h = hashSlots.getUnchecked(i);
while (h != nullptr)
{
const std::unique_ptr<HashEntry> deleter (h);
h = h->nextEntry;
}
hashSlots.set (i, nullptr);
}
totalNumItems = 0;
}
//==============================================================================
/** Returns the current number of items in the map. */
inline int size() const noexcept
{
return totalNumItems;
}
/** Returns the value corresponding to a given key.
If the map doesn't contain the key, a default instance of the value type is returned.
@param keyToLookFor the key of the item being requested
*/
inline ValueType operator[] (KeyTypeParameter keyToLookFor) const
{
const ScopedLockType sl (getLock());
if (auto* entry = getEntry (getSlot (keyToLookFor), keyToLookFor))
return entry->value;
return ValueType();
}
/** Returns a reference to the value corresponding to a given key.
If the map doesn't contain the key, a default instance of the value type is
added to the map and a reference to this is returned.
@param keyToLookFor the key of the item being requested
*/
inline ValueType& getReference (KeyTypeParameter keyToLookFor)
{
const ScopedLockType sl (getLock());
auto hashIndex = generateHashFor (keyToLookFor, getNumSlots());
auto* firstEntry = hashSlots.getUnchecked (hashIndex);
if (auto* entry = getEntry (firstEntry, keyToLookFor))
return entry->value;
auto* entry = new HashEntry (keyToLookFor, ValueType(), firstEntry);
hashSlots.set (hashIndex, entry);
++totalNumItems;
if (totalNumItems > (getNumSlots() * 3) / 2)
remapTable (getNumSlots() * 2);
return entry->value;
}
//==============================================================================
/** Returns true if the map contains an item with the specified key. */
bool contains (KeyTypeParameter keyToLookFor) const
{
const ScopedLockType sl (getLock());
return (getEntry (getSlot (keyToLookFor), keyToLookFor) != nullptr);
}
/** Returns true if the hash contains at least one occurrence of a given value. */
bool containsValue (ValueTypeParameter valueToLookFor) const
{
const ScopedLockType sl (getLock());
for (auto i = getNumSlots(); --i >= 0;)
for (auto* entry = hashSlots.getUnchecked(i); entry != nullptr; entry = entry->nextEntry)
if (entry->value == valueToLookFor)
return true;
return false;
}
//==============================================================================
/** Adds or replaces an element in the hash-map.
If there's already an item with the given key, this will replace its value. Otherwise, a new item
will be added to the map.
*/
void set (KeyTypeParameter newKey, ValueTypeParameter newValue) { getReference (newKey) = newValue; }
/** Removes an item with the given key. */
void remove (KeyTypeParameter keyToRemove)
{
const ScopedLockType sl (getLock());
auto hashIndex = generateHashFor (keyToRemove, getNumSlots());
auto* entry = hashSlots.getUnchecked (hashIndex);
HashEntry* previous = nullptr;
while (entry != nullptr)
{
if (entry->key == keyToRemove)
{
const std::unique_ptr<HashEntry> deleter (entry);
entry = entry->nextEntry;
if (previous != nullptr)
previous->nextEntry = entry;
else
hashSlots.set (hashIndex, entry);
--totalNumItems;
}
else
{
previous = entry;
entry = entry->nextEntry;
}
}
}
/** Removes all items with the given value. */
void removeValue (ValueTypeParameter valueToRemove)
{
const ScopedLockType sl (getLock());
for (auto i = getNumSlots(); --i >= 0;)
{
auto* entry = hashSlots.getUnchecked(i);
HashEntry* previous = nullptr;
while (entry != nullptr)
{
if (entry->value == valueToRemove)
{
const std::unique_ptr<HashEntry> deleter (entry);
entry = entry->nextEntry;
if (previous != nullptr)
previous->nextEntry = entry;
else
hashSlots.set (i, entry);
--totalNumItems;
}
else
{
previous = entry;
entry = entry->nextEntry;
}
}
}
}
/** Remaps the hash-map to use a different number of slots for its hash function.
Each slot corresponds to a single hash-code, and each one can contain multiple items.
@see getNumSlots()
*/
void remapTable (int newNumberOfSlots)
{
const ScopedLockType sl (getLock());
Array<HashEntry*> newSlots;
newSlots.insertMultiple (0, nullptr, newNumberOfSlots);
for (auto i = getNumSlots(); --i >= 0;)
{
HashEntry* nextEntry = nullptr;
for (auto* entry = hashSlots.getUnchecked(i); entry != nullptr; entry = nextEntry)
{
auto hashIndex = generateHashFor (entry->key, newNumberOfSlots);
nextEntry = entry->nextEntry;
entry->nextEntry = newSlots.getUnchecked (hashIndex);
newSlots.set (hashIndex, entry);
}
}
hashSlots.swapWith (newSlots);
}
/** Returns the number of slots which are available for hashing.
Each slot corresponds to a single hash-code, and each one can contain multiple items.
@see getNumSlots()
*/
inline int getNumSlots() const noexcept
{
return hashSlots.size();
}
//==============================================================================
/** Efficiently swaps the contents of two hash-maps. */
template <class OtherHashMapType>
void swapWith (OtherHashMapType& otherHashMap) noexcept
{
const ScopedLockType lock1 (getLock());
const typename OtherHashMapType::ScopedLockType lock2 (otherHashMap.getLock());
hashSlots.swapWith (otherHashMap.hashSlots);
std::swap (totalNumItems, otherHashMap.totalNumItems);
}
//==============================================================================
/** Returns the CriticalSection that locks this structure.
To lock, you can call getLock().enter() and getLock().exit(), or preferably use
an object of ScopedLockType as an RAII lock for it.
*/
inline const TypeOfCriticalSectionToUse& getLock() const noexcept { return lock; }
/** Returns the type of scoped lock to use for locking this array */
using ScopedLockType = typename TypeOfCriticalSectionToUse::ScopedLockType;
private:
//==============================================================================
class HashEntry
{
public:
HashEntry (KeyTypeParameter k, ValueTypeParameter val, HashEntry* const next)
: key (k), value (val), nextEntry (next)
{}
const KeyType key;
ValueType value;
HashEntry* nextEntry;
JUCE_DECLARE_NON_COPYABLE (HashEntry)
};
public:
//==============================================================================
/** Iterates over the items in a HashMap.
To use it, repeatedly call next() until it returns false, e.g.
@code
HashMap <String, String> myMap;
HashMap<String, String>::Iterator i (myMap);
while (i.next())
{
DBG (i.getKey() << " -> " << i.getValue());
}
@endcode
The order in which items are iterated bears no 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
iterators that were created beforehand will cease to be valid, and should not be used.
@see HashMap
*/
struct Iterator
{
Iterator (const HashMap& hashMapToIterate) noexcept
: hashMap (hashMapToIterate), entry (nullptr), index (0)
{}
Iterator (const Iterator& other) noexcept
: hashMap (other.hashMap), entry (other.entry), index (other.index)
{}
/** Moves to the next item, if one is available.
When this returns true, you can get the item's key and value using getKey() and
getValue(). If it returns false, the iteration has finished and you should stop.
*/
bool next() noexcept
{
if (entry != nullptr)
entry = entry->nextEntry;
while (entry == nullptr)
{
if (index >= hashMap.getNumSlots())
return false;
entry = hashMap.hashSlots.getUnchecked (index++);
}
return true;
}
/** Returns the current item's key.
This should only be called when a call to next() has just returned true.
*/
KeyType getKey() const
{
return entry != nullptr ? entry->key : KeyType();
}
/** Returns the current item's value.
This should only be called when a call to next() has just returned true.
*/
ValueType getValue() const
{
return entry != nullptr ? entry->value : ValueType();
}
/** Resets the iterator to its starting position. */
void reset() noexcept
{
entry = nullptr;
index = 0;
}
Iterator& operator++() noexcept { next(); return *this; }
ValueType operator*() const { return getValue(); }
bool operator!= (const Iterator& other) const noexcept { return entry != other.entry || index != other.index; }
void resetToEnd() noexcept { index = hashMap.getNumSlots(); }
private:
//==============================================================================
const HashMap& hashMap;
HashEntry* entry;
int index;
// using the copy constructor is ok, but you cannot assign iterators
Iterator& operator= (const Iterator&) = delete;
JUCE_LEAK_DETECTOR (Iterator)
};
/** Returns a start iterator for the values in this tree. */
Iterator begin() const noexcept { Iterator i (*this); i.next(); return i; }
/** Returns an end iterator for the values in this tree. */
Iterator end() const noexcept { Iterator i (*this); i.resetToEnd(); return i; }
private:
//==============================================================================
enum { defaultHashTableSize = 101 };
friend struct Iterator;
HashFunctionType hashFunctionToUse;
Array<HashEntry*> hashSlots;
int totalNumItems = 0;
TypeOfCriticalSectionToUse lock;
int generateHashFor (KeyTypeParameter key, int numSlots) const
{
const int hash = hashFunctionToUse.generateHash (key, numSlots);
jassert (isPositiveAndBelow (hash, numSlots)); // your hash function is generating out-of-range numbers!
return hash;
}
static HashEntry* getEntry (HashEntry* firstEntry, KeyType keyToLookFor) noexcept
{
for (auto* entry = firstEntry; entry != nullptr; entry = entry->nextEntry)
if (entry->key == keyToLookFor)
return entry;
return nullptr;
}
inline HashEntry* getSlot (KeyType key) const noexcept { return hashSlots.getUnchecked (generateHashFor (key, getNumSlots())); }
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (HashMap)
};
} // namespace juce

View File

@ -0,0 +1,278 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
struct HashMapTest : public UnitTest
{
HashMapTest()
: UnitTest ("HashMap", UnitTestCategories::containers)
{}
void runTest() override
{
doTest<AddElementsTest> ("AddElementsTest");
doTest<AccessTest> ("AccessTest");
doTest<RemoveTest> ("RemoveTest");
doTest<PersistantMemoryLocationOfValues> ("PersistantMemoryLocationOfValues");
}
//==============================================================================
struct AddElementsTest
{
template <typename KeyType>
static void run (UnitTest& u)
{
AssociativeMap<KeyType, int> groundTruth;
HashMap<KeyType, int> hashMap;
RandomKeys<KeyType> keyOracle (300, 3827829);
Random valueOracle (48735);
int totalValues = 0;
for (int i = 0; i < 10000; ++i)
{
auto key = keyOracle.next();
auto value = valueOracle.nextInt();
bool contains = (groundTruth.find (key) != nullptr);
u.expectEquals ((int) contains, (int) hashMap.contains (key));
groundTruth.add (key, value);
hashMap.set (key, value);
if (! contains) totalValues++;
u.expectEquals (hashMap.size(), totalValues);
}
}
};
struct AccessTest
{
template <typename KeyType>
static void run (UnitTest& u)
{
AssociativeMap<KeyType, int> groundTruth;
HashMap<KeyType, int> hashMap;
fillWithRandomValues (hashMap, groundTruth);
for (auto pair : groundTruth.pairs)
u.expectEquals (hashMap[pair.key], pair.value);
}
};
struct RemoveTest
{
template <typename KeyType>
static void run (UnitTest& u)
{
AssociativeMap<KeyType, int> groundTruth;
HashMap<KeyType, int> hashMap;
fillWithRandomValues (hashMap, groundTruth);
auto n = groundTruth.size();
Random r (3827387);
for (int i = 0; i < 100; ++i)
{
auto idx = r.nextInt (n-- - 1);
auto key = groundTruth.pairs.getReference (idx).key;
groundTruth.pairs.remove (idx);
hashMap.remove (key);
u.expect (! hashMap.contains (key));
for (auto pair : groundTruth.pairs)
u.expectEquals (hashMap[pair.key], pair.value);
}
}
};
// ensure that the addresses of object references don't change
struct PersistantMemoryLocationOfValues
{
struct AddressAndValue { int value; const int* valueAddress; };
template <typename KeyType>
static void run (UnitTest& u)
{
AssociativeMap<KeyType, AddressAndValue> groundTruth;
HashMap<KeyType, int> hashMap;
RandomKeys<KeyType> keyOracle (300, 3827829);
Random valueOracle (48735);
for (int i = 0; i < 1000; ++i)
{
auto key = keyOracle.next();
auto value = valueOracle.nextInt();
hashMap.set (key, value);
if (auto* existing = groundTruth.find (key))
{
// don't change the address: only the value
existing->value = value;
}
else
{
groundTruth.add (key, { value, &hashMap.getReference (key) });
}
for (auto pair : groundTruth.pairs)
{
const auto& hashMapValue = hashMap.getReference (pair.key);
u.expectEquals (hashMapValue, pair.value.value);
u.expect (&hashMapValue == pair.value.valueAddress);
}
}
auto n = groundTruth.size();
Random r (3827387);
for (int i = 0; i < 100; ++i)
{
auto idx = r.nextInt (n-- - 1);
auto key = groundTruth.pairs.getReference (idx).key;
groundTruth.pairs.remove (idx);
hashMap.remove (key);
for (auto pair : groundTruth.pairs)
{
const auto& hashMapValue = hashMap.getReference (pair.key);
u.expectEquals (hashMapValue, pair.value.value);
u.expect (&hashMapValue == pair.value.valueAddress);
}
}
}
};
//==============================================================================
template <class Test>
void doTest (const String& testName)
{
beginTest (testName);
Test::template run<int> (*this);
Test::template run<void*> (*this);
Test::template run<String> (*this);
}
//==============================================================================
template <typename KeyType, typename ValueType>
struct AssociativeMap
{
struct KeyValuePair { KeyType key; ValueType value; };
ValueType* find (KeyType key)
{
auto n = pairs.size();
for (int i = 0; i < n; ++i)
{
auto& pair = pairs.getReference (i);
if (pair.key == key)
return &pair.value;
}
return nullptr;
}
void add (KeyType key, ValueType value)
{
if (ValueType* v = find (key))
*v = value;
else
pairs.add ({key, value});
}
int size() const { return pairs.size(); }
Array<KeyValuePair> pairs;
};
template <typename KeyType, typename ValueType>
static void fillWithRandomValues (HashMap<KeyType, int>& hashMap, AssociativeMap<KeyType, ValueType>& groundTruth)
{
RandomKeys<KeyType> keyOracle (300, 3827829);
Random valueOracle (48735);
for (int i = 0; i < 10000; ++i)
{
auto key = keyOracle.next();
auto value = valueOracle.nextInt();
groundTruth.add (key, value);
hashMap.set (key, value);
}
}
//==============================================================================
template <typename KeyType>
class RandomKeys
{
public:
RandomKeys (int maxUniqueKeys, int seed) : r (seed)
{
for (int i = 0; i < maxUniqueKeys; ++i)
keys.add (generateRandomKey (r));
}
const KeyType& next()
{
int i = r.nextInt (keys.size() - 1);
return keys.getReference (i);
}
private:
static KeyType generateRandomKey (Random&);
Random r;
Array<KeyType> keys;
};
};
template <> int HashMapTest::RandomKeys<int> ::generateRandomKey (Random& rnd) { return rnd.nextInt(); }
template <> void* HashMapTest::RandomKeys<void*>::generateRandomKey (Random& rnd) { return reinterpret_cast<void*> (rnd.nextInt64()); }
template <> String HashMapTest::RandomKeys<String>::generateRandomKey (Random& rnd)
{
String str;
int len = rnd.nextInt (8)+1;
for (int i = 0; i < len; ++i)
str += static_cast<char> (rnd.nextInt (95) + 32);
return str;
}
static HashMapTest hashMapTest;
} // namespace juce

View File

@ -0,0 +1,370 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
/**
Helps to manipulate singly-linked lists of objects.
For objects that are designed to contain a pointer to the subsequent item in the
list, this class contains methods to deal with the list. To use it, the ObjectType
class that it points to must contain a LinkedListPointer called nextListItem, e.g.
@code
struct MyObject
{
int x, y, z;
// A linkable object must contain a member with this name and type, which must be
// accessible by the LinkedListPointer class. (This doesn't mean it has to be public -
// you could make your class a friend of a LinkedListPointer<MyObject> instead).
LinkedListPointer<MyObject> nextListItem;
};
LinkedListPointer<MyObject> myList;
myList.append (new MyObject());
myList.append (new MyObject());
int numItems = myList.size(); // returns 2
MyObject* lastInList = myList.getLast();
@endcode
@tags{Core}
*/
template <class ObjectType>
class LinkedListPointer
{
public:
//==============================================================================
/** Creates a null pointer to an empty list. */
LinkedListPointer() noexcept
: item (nullptr)
{
}
/** Creates a pointer to a list whose head is the item provided. */
explicit LinkedListPointer (ObjectType* const headItem) noexcept
: item (headItem)
{
}
/** Sets this pointer to point to a new list. */
LinkedListPointer& operator= (ObjectType* const newItem) noexcept
{
item = newItem;
return *this;
}
LinkedListPointer (LinkedListPointer&& other) noexcept
: item (other.item)
{
other.item = nullptr;
}
LinkedListPointer& operator= (LinkedListPointer&& other) noexcept
{
jassert (this != &other); // hopefully the compiler should make this situation impossible!
item = other.item;
other.item = nullptr;
return *this;
}
//==============================================================================
/** Returns the item which this pointer points to. */
inline operator ObjectType*() const noexcept
{
return item;
}
/** Returns the item which this pointer points to. */
inline ObjectType* get() const noexcept
{
return item;
}
/** Returns the last item in the list which this pointer points to.
This will iterate the list and return the last item found. Obviously the speed
of this operation will be proportional to the size of the list. If the list is
empty the return value will be this object.
If you're planning on appending a number of items to your list, it's much more
efficient to use the Appender class than to repeatedly call getLast() to find the end.
*/
LinkedListPointer& getLast() noexcept
{
auto* l = this;
while (l->item != nullptr)
l = &(l->item->nextListItem);
return *l;
}
/** Returns the number of items in the list.
Obviously with a simple linked list, getting the size involves iterating the list, so
this can be a lengthy operation - be careful when using this method in your code.
*/
int size() const noexcept
{
int total = 0;
for (auto* i = item; i != nullptr; i = i->nextListItem)
++total;
return total;
}
/** Returns the item at a given index in the list.
Since the only way to find an item is to iterate the list, this operation can obviously
be slow, depending on its size, so you should be careful when using this in algorithms.
*/
LinkedListPointer& operator[] (int index) noexcept
{
auto* l = this;
while (--index >= 0 && l->item != nullptr)
l = &(l->item->nextListItem);
return *l;
}
/** Returns the item at a given index in the list.
Since the only way to find an item is to iterate the list, this operation can obviously
be slow, depending on its size, so you should be careful when using this in algorithms.
*/
const LinkedListPointer& operator[] (int index) const noexcept
{
auto* l = this;
while (--index >= 0 && l->item != nullptr)
l = &(l->item->nextListItem);
return *l;
}
/** Returns true if the list contains the given item. */
bool contains (const ObjectType* const itemToLookFor) const noexcept
{
for (auto* i = item; i != nullptr; i = i->nextListItem)
if (itemToLookFor == i)
return true;
return false;
}
//==============================================================================
/** Inserts an item into the list, placing it before the item that this pointer
currently points to.
*/
void insertNext (ObjectType* const newItem)
{
JUCE_BEGIN_IGNORE_WARNINGS_MSVC (6011)
jassert (newItem != nullptr);
jassert (newItem->nextListItem == nullptr);
newItem->nextListItem = item;
item = newItem;
JUCE_END_IGNORE_WARNINGS_MSVC
}
/** Inserts an item at a numeric index in the list.
Obviously this will involve iterating the list to find the item at the given index,
so be careful about the impact this may have on execution time.
*/
void insertAtIndex (int index, ObjectType* newItem)
{
jassert (newItem != nullptr);
auto* l = this;
while (index != 0 && l->item != nullptr)
{
l = &(l->item->nextListItem);
--index;
}
l->insertNext (newItem);
}
/** Replaces the object that this pointer points to, appending the rest of the list to
the new object, and returning the old one.
*/
ObjectType* replaceNext (ObjectType* const newItem) noexcept
{
JUCE_BEGIN_IGNORE_WARNINGS_MSVC (6011 28182)
jassert (newItem != nullptr);
jassert (newItem->nextListItem == nullptr);
auto oldItem = item;
item = newItem;
item->nextListItem = oldItem->nextListItem.item;
oldItem->nextListItem.item = nullptr;
return oldItem;
JUCE_END_IGNORE_WARNINGS_MSVC
}
/** Adds an item to the end of the list.
This operation involves iterating the whole list, so can be slow - if you need to
append a number of items to your list, it's much more efficient to use the Appender
class than to repeatedly call append().
*/
void append (ObjectType* const newItem)
{
getLast().item = newItem;
}
/** Creates copies of all the items in another list and adds them to this one.
This will use the ObjectType's copy constructor to try to create copies of each
item in the other list, and appends them to this list.
*/
void addCopyOfList (const LinkedListPointer& other)
{
auto* insertPoint = this;
for (auto* i = other.item; i != nullptr; i = i->nextListItem)
{
insertPoint->insertNext (new ObjectType (*i));
insertPoint = &(insertPoint->item->nextListItem);
}
}
/** Removes the head item from the list.
This won't delete the object that is removed, but returns it, so the caller can
delete it if necessary.
*/
ObjectType* removeNext() noexcept
{
auto oldItem = item;
if (oldItem != nullptr)
{
item = oldItem->nextListItem;
oldItem->nextListItem.item = nullptr;
}
return oldItem;
}
/** Removes a specific item from the list.
Note that this will not delete the item, it simply unlinks it from the list.
*/
void remove (ObjectType* const itemToRemove)
{
if (auto* l = findPointerTo (itemToRemove))
l->removeNext();
}
/** Iterates the list, calling the delete operator on all of its elements and
leaving this pointer empty.
*/
void deleteAll()
{
while (item != nullptr)
{
auto oldItem = item;
item = oldItem->nextListItem;
delete oldItem;
}
}
/** Finds a pointer to a given item.
If the item is found in the list, this returns the pointer that points to it. If
the item isn't found, this returns null.
*/
LinkedListPointer* findPointerTo (ObjectType* const itemToLookFor) noexcept
{
auto* l = this;
while (l->item != nullptr)
{
if (l->item == itemToLookFor)
return l;
l = &(l->item->nextListItem);
}
return nullptr;
}
/** Copies the items in the list to an array.
The destArray must contain enough elements to hold the entire list - no checks are
made for this!
*/
void copyToArray (ObjectType** destArray) const noexcept
{
JUCE_BEGIN_IGNORE_WARNINGS_MSVC (6011)
jassert (destArray != nullptr);
for (auto* i = item; i != nullptr; i = i->nextListItem)
*destArray++ = i;
JUCE_END_IGNORE_WARNINGS_MSVC
}
/** Swaps this pointer with another one */
void swapWith (LinkedListPointer& other) noexcept
{
std::swap (item, other.item);
}
//==============================================================================
/**
Allows efficient repeated insertions into a list.
You can create an Appender object which points to the last element in your
list, and then repeatedly call Appender::append() to add items to the end
of the list in O(1) time.
*/
class Appender
{
public:
/** Creates an appender which will add items to the given list.
*/
Appender (LinkedListPointer& endOfListPointer) noexcept
: endOfList (&endOfListPointer)
{
// This can only be used to add to the end of a list.
jassert (endOfListPointer.item == nullptr);
}
/** Appends an item to the list. */
void append (ObjectType* const newItem) noexcept
{
*endOfList = newItem;
endOfList = &(newItem->nextListItem);
}
private:
LinkedListPointer* endOfList;
JUCE_DECLARE_NON_COPYABLE (Appender)
};
private:
//==============================================================================
ObjectType* item;
JUCE_DECLARE_NON_COPYABLE (LinkedListPointer)
};
} // namespace juce

View File

@ -0,0 +1,313 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
/**
Holds a set of objects and can invoke a member function callback on each object
in the set with a single call.
Use a ListenerList to manage a set of objects which need a callback, and you
can invoke a member function by simply calling call() or callChecked().
E.g.
@code
class MyListenerType
{
public:
void myCallbackMethod (int foo, bool bar);
};
ListenerList<MyListenerType> listeners;
listeners.add (someCallbackObjects...);
// This will invoke myCallbackMethod (1234, true) on each of the objects
// in the list...
listeners.call ([] (MyListenerType& l) { l.myCallbackMethod (1234, true); });
@endcode
If you add or remove listeners from the list during one of the callbacks - i.e. while
it's in the middle of iterating the listeners, then it's guaranteed that no listeners
will be mistakenly called after they've been removed, but it may mean that some of the
listeners could be called more than once, or not at all, depending on the list's order.
Sometimes, there's a chance that invoking one of the callbacks might result in the
list itself being deleted while it's still iterating - to survive this situation, you can
use callChecked() instead of call(), passing it a local object to act as a "BailOutChecker".
The BailOutChecker must implement a method of the form "bool shouldBailOut()", and
the list will check this after each callback to determine whether it should abort the
operation. For an example of a bail-out checker, see the Component::BailOutChecker class,
which can be used to check when a Component has been deleted. See also
ListenerList::DummyBailOutChecker, which is a dummy checker that always returns false.
@tags{Core}
*/
template <class ListenerClass,
class ArrayType = Array<ListenerClass*>>
class ListenerList
{
public:
//==============================================================================
/** Creates an empty list. */
ListenerList() = default;
/** Destructor. */
~ListenerList() = default;
//==============================================================================
/** Adds a listener to the list.
A listener can only be added once, so if the listener is already in the list,
this method has no effect.
@see remove
*/
void add (ListenerClass* listenerToAdd)
{
if (listenerToAdd != nullptr)
listeners.addIfNotAlreadyThere (listenerToAdd);
else
jassertfalse; // Listeners can't be null pointers!
}
/** Removes a listener from the list.
If the listener wasn't in the list, this has no effect.
*/
void remove (ListenerClass* listenerToRemove)
{
jassert (listenerToRemove != nullptr); // Listeners can't be null pointers!
listeners.removeFirstMatchingValue (listenerToRemove);
}
/** Returns the number of registered listeners. */
int size() const noexcept { return listeners.size(); }
/** Returns true if no listeners are registered, false otherwise. */
bool isEmpty() const noexcept { return listeners.isEmpty(); }
/** Clears the list. */
void clear() { listeners.clear(); }
/** Returns true if the specified listener has been added to the list. */
bool contains (ListenerClass* listener) const noexcept { return listeners.contains (listener); }
/** Returns the raw array of listeners. */
const ArrayType& getListeners() const noexcept { return listeners; }
//==============================================================================
/** Calls a member function on each listener in the list, with multiple parameters. */
template <typename Callback>
void call (Callback&& callback)
{
typename ArrayType::ScopedLockType lock (listeners.getLock());
for (Iterator<DummyBailOutChecker, ThisType> iter (*this); iter.next();)
callback (*iter.getListener());
}
/** Calls a member function with 1 parameter, on all but the specified listener in the list.
This can be useful if the caller is also a listener and needs to exclude itself.
*/
template <typename Callback>
void callExcluding (ListenerClass* listenerToExclude, Callback&& callback)
{
typename ArrayType::ScopedLockType lock (listeners.getLock());
for (Iterator<DummyBailOutChecker, ThisType> iter (*this); iter.next();)
{
auto* l = iter.getListener();
if (l != listenerToExclude)
callback (*l);
}
}
/** Calls a member function on each listener in the list, with 1 parameter and a bail-out-checker.
See the class description for info about writing a bail-out checker.
*/
template <typename Callback, typename BailOutCheckerType>
void callChecked (const BailOutCheckerType& bailOutChecker, Callback&& callback)
{
typename ArrayType::ScopedLockType lock (listeners.getLock());
for (Iterator<BailOutCheckerType, ThisType> iter (*this); iter.next (bailOutChecker);)
callback (*iter.getListener());
}
/** Calls a member function, with 1 parameter, on all but the specified listener in the list
with a bail-out-checker. This can be useful if the caller is also a listener and needs to
exclude itself. See the class description for info about writing a bail-out checker.
*/
template <typename Callback, typename BailOutCheckerType>
void callCheckedExcluding (ListenerClass* listenerToExclude,
const BailOutCheckerType& bailOutChecker,
Callback&& callback)
{
typename ArrayType::ScopedLockType lock (listeners.getLock());
for (Iterator<BailOutCheckerType, ThisType> iter (*this); iter.next (bailOutChecker);)
{
auto* l = iter.getListener();
if (l != listenerToExclude)
callback (*l);
}
}
//==============================================================================
/** A dummy bail-out checker that always returns false.
See the ListenerList notes for more info about bail-out checkers.
*/
struct DummyBailOutChecker
{
bool shouldBailOut() const noexcept { return false; }
};
using ThisType = ListenerList<ListenerClass, ArrayType>;
using ListenerType = ListenerClass;
//==============================================================================
/** Iterates the listeners in a ListenerList. */
template <class BailOutCheckerType, class ListType>
struct Iterator
{
Iterator (const ListType& listToIterate) noexcept
: list (listToIterate), index (listToIterate.size())
{}
~Iterator() = default;
//==============================================================================
bool next() noexcept
{
if (index <= 0)
return false;
auto listSize = list.size();
if (--index < listSize)
return true;
index = listSize - 1;
return index >= 0;
}
bool next (const BailOutCheckerType& bailOutChecker) noexcept
{
return (! bailOutChecker.shouldBailOut()) && next();
}
typename ListType::ListenerType* getListener() const noexcept
{
return list.getListeners().getUnchecked (index);
}
//==============================================================================
private:
const ListType& list;
int index;
JUCE_DECLARE_NON_COPYABLE (Iterator)
};
//==============================================================================
#ifndef DOXYGEN
// There are now lambda-based call functions that can be used to replace these old method-based versions.
// We'll eventually deprecate these old ones, so please begin moving your code to use lambdas!
void call (void (ListenerClass::*callbackFunction) ())
{
call ([=] (ListenerClass& l) { (l.*callbackFunction)(); });
}
void callExcluding (ListenerClass* listenerToExclude, void (ListenerClass::*callbackFunction) ())
{
callExcluding (listenerToExclude, [=] (ListenerClass& l) { (l.*callbackFunction)(); });
}
template <class BailOutCheckerType>
void callChecked (const BailOutCheckerType& bailOutChecker, void (ListenerClass::*callbackFunction) ())
{
callChecked (bailOutChecker, [=] (ListenerClass& l) { (l.*callbackFunction)(); });
}
template <class BailOutCheckerType>
void callCheckedExcluding (ListenerClass* listenerToExclude,
const BailOutCheckerType& bailOutChecker,
void (ListenerClass::*callbackFunction) ())
{
callCheckedExcluding (listenerToExclude, bailOutChecker, [=] (ListenerClass& l) { (l.*callbackFunction)(); });
}
template <typename... MethodArgs, typename... Args>
void call (void (ListenerClass::*callbackFunction) (MethodArgs...), Args&&... args)
{
typename ArrayType::ScopedLockType lock (listeners.getLock());
for (Iterator<DummyBailOutChecker, ThisType> iter (*this); iter.next();)
(iter.getListener()->*callbackFunction) (static_cast<typename TypeHelpers::ParameterType<Args>::type> (args)...);
}
template <typename... MethodArgs, typename... Args>
void callExcluding (ListenerClass* listenerToExclude,
void (ListenerClass::*callbackFunction) (MethodArgs...),
Args&&... args)
{
typename ArrayType::ScopedLockType lock (listeners.getLock());
for (Iterator<DummyBailOutChecker, ThisType> iter (*this); iter.next();)
if (iter.getListener() != listenerToExclude)
(iter.getListener()->*callbackFunction) (static_cast<typename TypeHelpers::ParameterType<Args>::type> (args)...);
}
template <typename BailOutCheckerType, typename... MethodArgs, typename... Args>
void callChecked (const BailOutCheckerType& bailOutChecker,
void (ListenerClass::*callbackFunction) (MethodArgs...),
Args&&... args)
{
typename ArrayType::ScopedLockType lock (listeners.getLock());
for (Iterator<BailOutCheckerType, ThisType> iter (*this); iter.next (bailOutChecker);)
(iter.getListener()->*callbackFunction) (static_cast<typename TypeHelpers::ParameterType<Args>::type> (args)...);
}
template <typename BailOutCheckerType, typename... MethodArgs, typename... Args>
void callCheckedExcluding (ListenerClass* listenerToExclude,
const BailOutCheckerType& bailOutChecker,
void (ListenerClass::*callbackFunction) (MethodArgs...),
Args&&... args)
{
typename ArrayType::ScopedLockType lock (listeners.getLock());
for (Iterator<BailOutCheckerType, ThisType> iter (*this); iter.next (bailOutChecker);)
if (iter.getListener() != listenerToExclude)
(iter.getListener()->*callbackFunction) (static_cast<typename TypeHelpers::ParameterType<Args>::type> (args)...);
}
#endif
private:
//==============================================================================
ArrayType listeners;
JUCE_DECLARE_NON_COPYABLE (ListenerList)
};
} // namespace juce

View File

@ -0,0 +1,306 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
NamedValueSet::NamedValue::NamedValue() noexcept {}
NamedValueSet::NamedValue::~NamedValue() noexcept {}
NamedValueSet::NamedValue::NamedValue (const Identifier& n, const var& v) : name (n), value (v) {}
NamedValueSet::NamedValue::NamedValue (const NamedValue& other) : NamedValue (other.name, other.value) {}
NamedValueSet::NamedValue::NamedValue (NamedValue&& other) noexcept
: NamedValue (std::move (other.name),
std::move (other.value))
{}
NamedValueSet::NamedValue::NamedValue (const Identifier& n, var&& v) noexcept
: name (n), value (std::move (v))
{
}
NamedValueSet::NamedValue::NamedValue (Identifier&& n, var&& v) noexcept
: name (std::move (n)),
value (std::move (v))
{}
NamedValueSet::NamedValue& NamedValueSet::NamedValue::operator= (NamedValue&& other) noexcept
{
name = std::move (other.name);
value = std::move (other.value);
return *this;
}
bool NamedValueSet::NamedValue::operator== (const NamedValue& other) const noexcept { return name == other.name && value == other.value; }
bool NamedValueSet::NamedValue::operator!= (const NamedValue& other) const noexcept { return ! operator== (other); }
//==============================================================================
NamedValueSet::NamedValueSet() noexcept {}
NamedValueSet::~NamedValueSet() noexcept {}
NamedValueSet::NamedValueSet (const NamedValueSet& other) : values (other.values) {}
NamedValueSet::NamedValueSet (NamedValueSet&& other) noexcept
: values (std::move (other.values)) {}
NamedValueSet::NamedValueSet (std::initializer_list<NamedValue> list)
: values (std::move (list))
{
}
NamedValueSet& NamedValueSet::operator= (const NamedValueSet& other)
{
clear();
values = other.values;
return *this;
}
NamedValueSet& NamedValueSet::operator= (NamedValueSet&& other) noexcept
{
other.values.swapWith (values);
return *this;
}
void NamedValueSet::clear()
{
values.clear();
}
bool NamedValueSet::operator== (const NamedValueSet& other) const noexcept
{
auto num = values.size();
if (num != other.values.size())
return false;
for (int i = 0; i < num; ++i)
{
// optimise for the case where the keys are in the same order
if (values.getReference(i).name == other.values.getReference(i).name)
{
if (values.getReference(i).value != other.values.getReference(i).value)
return false;
}
else
{
// if we encounter keys that are in a different order, search remaining items by brute force..
for (int j = i; j < num; ++j)
{
if (auto* otherVal = other.getVarPointer (values.getReference(j).name))
if (values.getReference(j).value == *otherVal)
continue;
return false;
}
return true;
}
}
return true;
}
bool NamedValueSet::operator!= (const NamedValueSet& other) const noexcept { return ! operator== (other); }
int NamedValueSet::size() const noexcept { return values.size(); }
bool NamedValueSet::isEmpty() const noexcept { return values.isEmpty(); }
static const var& getNullVarRef() noexcept
{
static var nullVar;
return nullVar;
}
const var& NamedValueSet::operator[] (const Identifier& name) const noexcept
{
if (auto* v = getVarPointer (name))
return *v;
return getNullVarRef();
}
var NamedValueSet::getWithDefault (const Identifier& name, const var& defaultReturnValue) const
{
if (auto* v = getVarPointer (name))
return *v;
return defaultReturnValue;
}
var* NamedValueSet::getVarPointer (const Identifier& name) noexcept
{
for (auto& i : values)
if (i.name == name)
return &(i.value);
return {};
}
const var* NamedValueSet::getVarPointer (const Identifier& name) const noexcept
{
for (auto& i : values)
if (i.name == name)
return &(i.value);
return {};
}
bool NamedValueSet::set (const Identifier& name, var&& newValue)
{
if (auto* v = getVarPointer (name))
{
if (v->equalsWithSameType (newValue))
return false;
*v = std::move (newValue);
return true;
}
values.add ({ name, std::move (newValue) });
return true;
}
bool NamedValueSet::set (const Identifier& name, const var& newValue)
{
if (auto* v = getVarPointer (name))
{
if (v->equalsWithSameType (newValue))
return false;
*v = newValue;
return true;
}
values.add ({ name, newValue });
return true;
}
bool NamedValueSet::contains (const Identifier& name) const noexcept
{
return getVarPointer (name) != nullptr;
}
int NamedValueSet::indexOf (const Identifier& name) const noexcept
{
auto numValues = values.size();
for (int i = 0; i < numValues; ++i)
if (values.getReference(i).name == name)
return i;
return -1;
}
bool NamedValueSet::remove (const Identifier& name)
{
auto numValues = values.size();
for (int i = 0; i < numValues; ++i)
{
if (values.getReference(i).name == name)
{
values.remove (i);
return true;
}
}
return false;
}
Identifier NamedValueSet::getName (const int index) const noexcept
{
if (isPositiveAndBelow (index, values.size()))
return values.getReference (index).name;
jassertfalse;
return {};
}
const var& NamedValueSet::getValueAt (const int index) const noexcept
{
if (isPositiveAndBelow (index, values.size()))
return values.getReference (index).value;
jassertfalse;
return getNullVarRef();
}
var* NamedValueSet::getVarPointerAt (int index) noexcept
{
if (isPositiveAndBelow (index, values.size()))
return &(values.getReference (index).value);
return {};
}
const var* NamedValueSet::getVarPointerAt (int index) const noexcept
{
if (isPositiveAndBelow (index, values.size()))
return &(values.getReference (index).value);
return {};
}
void NamedValueSet::setFromXmlAttributes (const XmlElement& xml)
{
values.clearQuick();
for (auto* att = xml.attributes.get(); att != nullptr; att = att->nextListItem)
{
if (att->name.toString().startsWith ("base64:"))
{
MemoryBlock mb;
if (mb.fromBase64Encoding (att->value))
{
values.add ({ att->name.toString().substring (7), var (mb) });
continue;
}
}
values.add ({ att->name, var (att->value) });
}
}
void NamedValueSet::copyToXmlAttributes (XmlElement& xml) const
{
for (auto& i : values)
{
if (auto* mb = i.value.getBinaryData())
{
xml.setAttribute ("base64:" + i.name.toString(), mb->toBase64Encoding());
}
else
{
// These types can't be stored as XML!
jassert (! i.value.isObject());
jassert (! i.value.isMethod());
jassert (! i.value.isArray());
xml.setAttribute (i.name.toString(),
i.value.toString());
}
}
}
} // namespace juce

View File

@ -0,0 +1,185 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
/** Holds a set of named var objects.
This can be used as a basic structure to hold a set of var object, which can
be retrieved by using their identifier.
@tags{Core}
*/
class JUCE_API NamedValueSet
{
public:
//==============================================================================
/** Structure for a named var object */
struct JUCE_API NamedValue
{
NamedValue() noexcept;
~NamedValue() noexcept;
NamedValue (const Identifier& name, const var& value);
NamedValue (const Identifier& name, var&& value) noexcept;
NamedValue (Identifier&& name, var&& value) noexcept;
NamedValue (const NamedValue&);
NamedValue (NamedValue&&) noexcept;
NamedValue& operator= (NamedValue&&) noexcept;
bool operator== (const NamedValue&) const noexcept;
bool operator!= (const NamedValue&) const noexcept;
Identifier name;
var value;
};
//==============================================================================
/** Creates an empty set. */
NamedValueSet() noexcept;
NamedValueSet (const NamedValueSet&);
NamedValueSet (NamedValueSet&&) noexcept;
NamedValueSet& operator= (const NamedValueSet&);
NamedValueSet& operator= (NamedValueSet&&) noexcept;
/** Creates a NamedValueSet from a list of names and properties. */
NamedValueSet (std::initializer_list<NamedValue>);
/** Destructor. */
~NamedValueSet() noexcept;
/** Two NamedValueSets are considered equal if they contain all the same key/value
pairs, regardless of the order.
*/
bool operator== (const NamedValueSet&) const noexcept;
bool operator!= (const NamedValueSet&) const noexcept;
const NamedValueSet::NamedValue* begin() const noexcept { return values.begin(); }
const NamedValueSet::NamedValue* end() const noexcept { return values.end(); }
//==============================================================================
/** Returns the total number of values that the set contains. */
int size() const noexcept;
/** Returns true if the set is empty. */
bool isEmpty() const noexcept;
/** Returns the value of a named item.
If the name isn't found, this will return a void variant.
*/
const var& operator[] (const Identifier& name) const noexcept;
/** Tries to return the named value, but if no such value is found, this will
instead return the supplied default value.
*/
var getWithDefault (const Identifier& name, const var& defaultReturnValue) const;
/** Changes or adds a named value.
@returns true if a value was changed or added; false if the
value was already set the value passed-in.
*/
bool set (const Identifier& name, const var& newValue);
/** Changes or adds a named value.
@returns true if a value was changed or added; false if the
value was already set the value passed-in.
*/
bool set (const Identifier& name, var&& newValue);
/** Returns true if the set contains an item with the specified name. */
bool contains (const Identifier& name) const noexcept;
/** Removes a value from the set.
@returns true if a value was removed; false if there was no value
with the name that was given.
*/
bool remove (const Identifier& name);
/** Returns the name of the value at a given index.
The index must be between 0 and size() - 1.
*/
Identifier getName (int index) const noexcept;
/** Returns a pointer to the var that holds a named value, or null if there is
no value with this name.
Do not use this method unless you really need access to the internal var object
for some reason - for normal reading and writing always prefer operator[]() and set().
Also note that the pointer returned may become invalid as soon as any subsequent
methods are called on the NamedValueSet.
*/
var* getVarPointer (const Identifier& name) noexcept;
/** Returns a pointer to the var that holds a named value, or null if there is
no value with this name.
Do not use this method unless you really need access to the internal var object
for some reason - for normal reading and writing always prefer operator[]() and set().
Also note that the pointer returned may become invalid as soon as any subsequent
methods are called on the NamedValueSet.
*/
const var* getVarPointer (const Identifier& name) const noexcept;
/** Returns the value of the item at a given index.
The index must be between 0 and size() - 1.
*/
const var& getValueAt (int index) const noexcept;
/** Returns the value of the item at a given index.
The index must be between 0 and size() - 1, or this will return a nullptr
Also note that the pointer returned may become invalid as soon as any subsequent
methods are called on the NamedValueSet.
*/
var* getVarPointerAt (int index) noexcept;
/** Returns the value of the item at a given index.
The index must be between 0 and size() - 1, or this will return a nullptr
Also note that the pointer returned may become invalid as soon as any subsequent
methods are called on the NamedValueSet.
*/
const var* getVarPointerAt (int index) const noexcept;
/** Returns the index of the given name, or -1 if it's not found. */
int indexOf (const Identifier& name) const noexcept;
/** Removes all values. */
void clear();
//==============================================================================
/** Sets properties to the values of all of an XML element's attributes. */
void setFromXmlAttributes (const XmlElement& xml);
/** Sets attributes in an XML element corresponding to each of this object's
properties.
*/
void copyToXmlAttributes (XmlElement& xml) const;
private:
//==============================================================================
Array<NamedValue> values;
};
} // namespace juce

View File

@ -0,0 +1,130 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
#if JUCE_UNIT_TESTS
static struct OwnedArrayTest : public UnitTest
{
struct Base
{
Base() = default;
virtual ~Base() = default;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Base)
};
struct Derived : Base
{
Derived() = default;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Derived)
};
struct DestructorObj
{
DestructorObj (OwnedArrayTest& p,
OwnedArray<DestructorObj>& arr)
: parent (p), objectArray (arr)
{}
~DestructorObj()
{
data = 0;
for (auto* o : objectArray)
{
parent.expect (o != nullptr);
parent.expect (o != this);
if (o != nullptr)
parent.expectEquals (o->data, 956);
}
}
OwnedArrayTest& parent;
OwnedArray<DestructorObj>& objectArray;
int data = 956;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DestructorObj)
};
OwnedArrayTest()
: UnitTest ("OwnedArray", UnitTestCategories::containers)
{}
void runTest() override
{
beginTest ("After converting move construction, ownership is transferred");
{
OwnedArray<Derived> derived { new Derived{}, new Derived{}, new Derived{} };
OwnedArray<Base> base { std::move (derived) };
expectEquals (base.size(), 3);
expectEquals (derived.size(), 0);
}
beginTest ("After converting move assignment, ownership is transferred");
{
OwnedArray<Base> base;
base = OwnedArray<Derived> { new Derived{}, new Derived{}, new Derived{} };
expectEquals (base.size(), 3);
}
beginTest ("Iterate in destructor");
{
{
OwnedArray<DestructorObj> arr;
for (int i = 0; i < 2; ++i)
arr.add (new DestructorObj (*this, arr));
}
OwnedArray<DestructorObj> arr;
for (int i = 0; i < 1025; ++i)
arr.add (new DestructorObj (*this, arr));
while (! arr.isEmpty())
arr.remove (0);
for (int i = 0; i < 1025; ++i)
arr.add (new DestructorObj (*this, arr));
arr.removeRange (1, arr.size() - 3);
for (int i = 0; i < 1025; ++i)
arr.add (new DestructorObj (*this, arr));
arr.set (500, new DestructorObj (*this, arr));
}
}
} ownedArrayTest;
#endif
}

View File

@ -0,0 +1,873 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
/** An array designed for holding objects.
This holds a list of pointers to objects, and will automatically
delete the objects when they are removed from the array, or when the
array is itself deleted.
Declare it in the form: OwnedArray<MyObjectClass>
..and then add new objects, e.g. myOwnedArray.add (new MyObjectClass());
After adding objects, they are 'owned' by the array and will be deleted when
removed or replaced.
To make all the array's methods thread-safe, pass in "CriticalSection" as the templated
TypeOfCriticalSectionToUse parameter, instead of the default DummyCriticalSection.
@see Array, ReferenceCountedArray, StringArray, CriticalSection
@tags{Core}
*/
template <class ObjectClass,
class TypeOfCriticalSectionToUse = DummyCriticalSection>
class OwnedArray
{
public:
//==============================================================================
/** Creates an empty array. */
OwnedArray() = default;
/** Deletes the array and also deletes any objects inside it.
To get rid of the array without deleting its objects, use its
clear (false) method before deleting it.
*/
~OwnedArray()
{
deleteAllObjects();
}
/** Move constructor. */
OwnedArray (OwnedArray&& other) noexcept
: values (std::move (other.values))
{
}
/** Creates an array from a list of objects. */
OwnedArray (const std::initializer_list<ObjectClass*>& items)
{
addArray (items);
}
/** Move assignment operator. */
OwnedArray& operator= (OwnedArray&& other) noexcept
{
const ScopedLockType lock (getLock());
deleteAllObjects();
values = std::move (other.values);
return *this;
}
/** 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;
}
//==============================================================================
/** Clears the array, optionally deleting the objects inside it first. */
void clear (bool deleteObjects = true)
{
const ScopedLockType lock (getLock());
clearQuick (deleteObjects);
values.setAllocatedSize (0);
}
//==============================================================================
/** Clears the array, optionally deleting the objects inside it first. */
void clearQuick (bool deleteObjects)
{
const ScopedLockType lock (getLock());
if (deleteObjects)
deleteAllObjects();
else
values.clear();
}
//==============================================================================
/** Returns the number of items currently in the array.
@see operator[]
*/
inline int size() const noexcept
{
return values.size();
}
/** Returns true if the array is empty, false otherwise. */
inline bool isEmpty() const noexcept
{
return size() == 0;
}
/** Returns a pointer to the object at this index in the array.
If the index is out-of-range, this will return a null pointer, (and
it could be null anyway, because it's ok for the array to hold null
pointers as well as objects).
@see getUnchecked
*/
inline ObjectClass* operator[] (int index) const noexcept
{
const ScopedLockType lock (getLock());
return values.getValueWithDefault (index);
}
/** Returns a pointer to the object at this index in the array, without checking whether the index is in-range.
This is a faster and less safe version of operator[] which doesn't check the index passed in, so
it can be used when you're sure the index is always going to be legal.
*/
inline ObjectClass* getUnchecked (int index) const noexcept
{
const ScopedLockType lock (getLock());
return values[index];
}
/** Returns a pointer to the first object in the array.
This will return a null pointer if the array's empty.
@see getLast
*/
inline ObjectClass* getFirst() const noexcept
{
const ScopedLockType lock (getLock());
return values.getFirst();
}
/** Returns a pointer to the last object in the array.
This will return a null pointer if the array's empty.
@see getFirst
*/
inline ObjectClass* getLast() const noexcept
{
const ScopedLockType lock (getLock());
return values.getLast();
}
/** Returns a pointer to the actual array data.
This pointer will only be valid until the next time a non-const method
is called on the array.
*/
inline ObjectClass** getRawDataPointer() noexcept
{
return values.begin();
}
//==============================================================================
/** Returns a pointer to the first element in the array.
This method is provided for compatibility with standard C++ iteration mechanisms.
*/
inline ObjectClass** begin() noexcept
{
return values.begin();
}
/** Returns a pointer to the first element in the array.
This method is provided for compatibility with standard C++ iteration mechanisms.
*/
inline ObjectClass* const* begin() const noexcept
{
return values.begin();
}
/** Returns a pointer to the element which follows the last element in the array.
This method is provided for compatibility with standard C++ iteration mechanisms.
*/
inline ObjectClass** end() noexcept
{
return values.end();
}
/** Returns a pointer to the element which follows the last element in the array.
This method is provided for compatibility with standard C++ iteration mechanisms.
*/
inline ObjectClass* const* end() const noexcept
{
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() noexcept
{
return begin();
}
/** Returns a pointer to the first element in the array.
This method is provided for compatibility with the standard C++ containers.
*/
inline ObjectClass* const* data() const noexcept
{
return begin();
}
//==============================================================================
/** Finds the index of an object which might be in the array.
@param objectToLookFor the object to look for
@returns the index at which the object was found, or -1 if it's not found
*/
int indexOf (const ObjectClass* objectToLookFor) const noexcept
{
const ScopedLockType lock (getLock());
auto* e = values.begin();
for (; e != values.end(); ++e)
if (objectToLookFor == *e)
return static_cast<int> (e - values.begin());
return -1;
}
/** Returns true if the array contains a specified object.
@param objectToLookFor the object to look for
@returns true if the object is in the array
*/
bool contains (const ObjectClass* objectToLookFor) const noexcept
{
const ScopedLockType lock (getLock());
auto* e = values.begin();
for (; e != values.end(); ++e)
if (objectToLookFor == *e)
return true;
return false;
}
//==============================================================================
/** Appends a new object to the end of the array.
Note that this object will be deleted by the OwnedArray when it is removed,
so be careful not to delete it somewhere else.
Also be careful not to add the same object to the array more than once,
as this will obviously cause deletion of dangling pointers.
@param newObject the new object to add to the array
@returns the new object that was added
@see set, insert, addSorted
*/
ObjectClass* add (ObjectClass* newObject)
{
const ScopedLockType lock (getLock());
values.add (newObject);
return newObject;
}
/** Appends a new object to the end of the array.
Note that this object will be deleted by the OwnedArray when it is removed,
so be careful not to delete it somewhere else.
Also be careful not to add the same object to the array more than once,
as this will obviously cause deletion of dangling pointers.
@param newObject the new object to add to the array
@returns the new object that was added
@see set, insert, addSorted
*/
ObjectClass* add (std::unique_ptr<ObjectClass> newObject)
{
return add (newObject.release());
}
/** Inserts a new object into the array at the given index.
Note that this object will be deleted by the OwnedArray when it is removed,
so be careful not to delete it somewhere else.
If the index is less than 0 or greater than the size of the array, the
element will be added to the end of the array.
Otherwise, it will be inserted into the array, moving all the later elements
along to make room.
Be careful not to add the same object to the array more than once,
as this will obviously cause deletion of dangling pointers.
@param indexToInsertAt the index at which the new element should be inserted
@param newObject the new object to add to the array
@returns the new object that was added
@see add, addSorted, set
*/
ObjectClass* insert (int indexToInsertAt, ObjectClass* newObject)
{
const ScopedLockType lock (getLock());
values.insert (indexToInsertAt, newObject, 1);
return newObject;
}
/** Inserts a new object into the array at the given index.
Note that this object will be deleted by the OwnedArray when it is removed,
so be careful not to delete it somewhere else.
If the index is less than 0 or greater than the size of the array, the
element will be added to the end of the array.
Otherwise, it will be inserted into the array, moving all the later elements
along to make room.
Be careful not to add the same object to the array more than once,
as this will obviously cause deletion of dangling pointers.
@param indexToInsertAt the index at which the new element should be inserted
@param newObject the new object to add to the array
@returns the new object that was added
@see add, addSorted, set
*/
ObjectClass* insert (int indexToInsertAt, std::unique_ptr<ObjectClass> newObject)
{
return insert (indexToInsertAt, newObject.release());
}
/** Inserts an array of values into this array at a given position.
If the index is less than 0 or greater than the size of the array, the
new elements will be added to the end of the array.
Otherwise, they will be inserted into the array, moving all the later elements
along to make room.
@param indexToInsertAt the index at which the first new element should be inserted
@param newObjects the new values to add to the array
@param numberOfElements how many items are in the array
@see insert, add, addSorted, set
*/
void insertArray (int indexToInsertAt,
ObjectClass* const* newObjects,
int numberOfElements)
{
if (numberOfElements > 0)
{
const ScopedLockType lock (getLock());
values.insertArray (indexToInsertAt, newObjects, numberOfElements);
}
}
/** Replaces an object in the array with a different one.
If the index is less than zero, this method does nothing.
If the index is beyond the end of the array, the new object is added to the end of the array.
Be careful not to add the same object to the array more than once,
as this will obviously cause deletion of dangling pointers.
@param indexToChange the index whose value you want to change
@param newObject the new value to set for this index.
@param deleteOldElement whether to delete the object that's being replaced with the new one
@see add, insert, remove
*/
ObjectClass* set (int indexToChange, ObjectClass* newObject, bool deleteOldElement = true)
{
if (indexToChange >= 0)
{
std::unique_ptr<ObjectClass> toDelete;
{
const ScopedLockType lock (getLock());
if (indexToChange < values.size())
{
if (deleteOldElement)
{
toDelete.reset (values[indexToChange]);
if (toDelete.get() == newObject)
toDelete.release();
}
values[indexToChange] = newObject;
}
else
{
values.add (newObject);
}
}
}
else
{
jassertfalse; // you're trying to set an object at a negative index, which doesn't have
// any effect - but since the object is not being added, it may be leaking..
}
return newObject;
}
/** Replaces an object in the array with a different one.
If the index is less than zero, this method does nothing.
If the index is beyond the end of the array, the new object is added to the end of the array.
Be careful not to add the same object to the array more than once,
as this will obviously cause deletion of dangling pointers.
@param indexToChange the index whose value you want to change
@param newObject the new value to set for this index.
@param deleteOldElement whether to delete the object that's being replaced with the new one
@see add, insert, remove
*/
ObjectClass* set (int indexToChange, std::unique_ptr<ObjectClass> newObject, bool deleteOldElement = true)
{
return set (indexToChange, newObject.release(), deleteOldElement);
}
/** Adds elements from another array to the end of this array.
@param arrayToAddFrom the array from which to copy the elements
@param startIndex the first element of the other array to start copying from
@param numElementsToAdd how many elements to add from the other array. If this
value is negative or greater than the number of available elements,
all available elements will be copied.
@see add
*/
template <class OtherArrayType>
void addArray (const OtherArrayType& arrayToAddFrom,
int startIndex = 0,
int numElementsToAdd = -1)
{
const typename OtherArrayType::ScopedLockType lock1 (arrayToAddFrom.getLock());
const ScopedLockType lock2 (getLock());
values.addArray (arrayToAddFrom, startIndex, numElementsToAdd);
}
/** Adds elements from another array to the end of this array. */
template <typename OtherArrayType>
void addArray (const std::initializer_list<OtherArrayType>& items)
{
const ScopedLockType lock (getLock());
values.addArray (items);
}
/** Adds copies of the elements in another array to the end of this array.
The other array must be either an OwnedArray of a compatible type of object, or an Array
containing pointers to the same kind of object. The objects involved must provide
a copy constructor, and this will be used to create new copies of each element, and
add them to this array.
@param arrayToAddFrom the array from which to copy the elements
@param startIndex the first element of the other array to start copying from
@param numElementsToAdd how many elements to add from the other array. If this
value is negative or greater than the number of available elements,
all available elements will be copied.
@see add
*/
template <class OtherArrayType>
void addCopiesOf (const OtherArrayType& arrayToAddFrom,
int startIndex = 0,
int numElementsToAdd = -1)
{
const typename OtherArrayType::ScopedLockType lock1 (arrayToAddFrom.getLock());
const ScopedLockType lock2 (getLock());
if (startIndex < 0)
{
jassertfalse;
startIndex = 0;
}
if (numElementsToAdd < 0 || startIndex + numElementsToAdd > arrayToAddFrom.size())
numElementsToAdd = arrayToAddFrom.size() - startIndex;
jassert (numElementsToAdd >= 0);
values.ensureAllocatedSize (values.size() + numElementsToAdd);
while (--numElementsToAdd >= 0)
values.add (createCopyIfNotNull (arrayToAddFrom.getUnchecked (startIndex++)));
}
/** Inserts a new object into the array assuming that the array is sorted.
This will use a comparator to find the position at which the new object
should go. If the array isn't sorted, the behaviour of this
method will be unpredictable.
@param comparator the comparator to use to compare the elements - see the sort method
for details about this object's structure
@param newObject the new object to insert to the array
@returns the index at which the new object was added
@see add, sort, indexOfSorted
*/
template <class ElementComparator>
int addSorted (ElementComparator& comparator, ObjectClass* newObject) 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());
auto index = findInsertIndexInSortedArray (comparator, values.begin(), newObject, 0, values.size());
insert (index, newObject);
return index;
}
/** Finds the index of an object in the array, assuming that the array is sorted.
This will use a comparator to do a binary-chop to find the index of the given
element, if it exists. If the array isn't sorted, the behaviour of this
method will be unpredictable.
@param comparator the comparator to use to compare the elements - see the sort()
method for details about the form this object should take
@param objectToLookFor the object to search for
@returns the index of the element, or -1 if it's not found
@see addSorted, sort
*/
template <typename ElementComparator>
int indexOfSorted (ElementComparator& comparator, const ObjectClass* 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 = values.size();
while (s < e)
{
if (comparator.compareElements (objectToLookFor, values[s]) == 0)
return s;
auto halfway = (s + e) / 2;
if (halfway == s)
break;
if (comparator.compareElements (objectToLookFor, values[halfway]) >= 0)
s = halfway;
else
e = halfway;
}
return -1;
}
//==============================================================================
/** Removes an object from the array.
This will remove the object at a given index (optionally also
deleting it) and move back all the subsequent objects to close the gap.
If the index passed in is out-of-range, nothing will happen.
@param indexToRemove the index of the element to remove
@param deleteObject whether to delete the object that is removed
@see removeObject, removeRange
*/
void remove (int indexToRemove, bool deleteObject = true)
{
std::unique_ptr<ObjectClass> toDelete;
{
const ScopedLockType lock (getLock());
if (isPositiveAndBelow (indexToRemove, values.size()))
{
auto** e = values.begin() + indexToRemove;
if (deleteObject)
toDelete.reset (*e);
values.removeElements (indexToRemove, 1);
}
}
if ((values.size() << 1) < values.capacity())
minimiseStorageOverheads();
}
/** Removes and returns an object from the array without deleting it.
This will remove the object at a given index and return it, moving back all
the subsequent objects to close the gap. If the index passed in is out-of-range,
nothing will happen.
@param indexToRemove the index of the element to remove
@see remove, removeObject, removeRange
*/
ObjectClass* removeAndReturn (int indexToRemove)
{
ObjectClass* removedItem = nullptr;
const ScopedLockType lock (getLock());
if (isPositiveAndBelow (indexToRemove, values.size()))
{
removedItem = values[indexToRemove];
values.removeElements (indexToRemove, 1);
if ((values.size() << 1) < values.capacity())
minimiseStorageOverheads();
}
return removedItem;
}
/** Removes a specified object from the array.
If the item isn't found, no action is taken.
@param objectToRemove the object to try to remove
@param deleteObject whether to delete the object (if it's found)
@see remove, removeRange
*/
void removeObject (const ObjectClass* objectToRemove, bool deleteObject = true)
{
const ScopedLockType lock (getLock());
for (int i = 0; i < values.size(); ++i)
{
if (objectToRemove == values[i])
{
remove (i, deleteObject);
break;
}
}
}
/** Removes a range of objects from the array.
This will remove a set of objects, starting from the given index,
and move any subsequent elements down to close the gap.
If the range extends beyond the bounds of the array, it will
be safely clipped to the size of the array.
@param startIndex the index of the first object to remove
@param numberToRemove how many objects should be removed
@param deleteObjects whether to delete the objects that get removed
@see remove, removeObject
*/
void removeRange (int startIndex, int numberToRemove, bool deleteObjects = true)
{
const ScopedLockType lock (getLock());
auto endIndex = jlimit (0, values.size(), startIndex + numberToRemove);
startIndex = jlimit (0, values.size(), startIndex);
numberToRemove = endIndex - startIndex;
if (numberToRemove > 0)
{
Array<ObjectClass*> objectsToDelete;
if (deleteObjects)
objectsToDelete.addArray (values.begin() + startIndex, numberToRemove);
values.removeElements (startIndex, numberToRemove);
for (auto& o : objectsToDelete)
ContainerDeletePolicy<ObjectClass>::destroy (o);
if ((values.size() << 1) < values.capacity())
minimiseStorageOverheads();
}
}
/** Removes the last n objects from the array.
@param howManyToRemove how many objects to remove from the end of the array
@param deleteObjects whether to also delete the objects that are removed
@see remove, removeObject, removeRange
*/
void removeLast (int howManyToRemove = 1,
bool deleteObjects = true)
{
const ScopedLockType lock (getLock());
if (howManyToRemove >= values.size())
clear (deleteObjects);
else
removeRange (values.size() - howManyToRemove, howManyToRemove, deleteObjects);
}
/** Swaps a pair of objects in the array.
If either of the indexes passed in is out-of-range, nothing will happen,
otherwise the two objects at these positions will be exchanged.
*/
void swap (int index1, int index2) noexcept
{
const ScopedLockType lock (getLock());
values.swap (index1, index2);
}
/** Moves one of the objects to a different position.
This will move the object to a specified index, shuffling along
any intervening elements as required.
So for example, if you have the array { 0, 1, 2, 3, 4, 5 } then calling
move (2, 4) would result in { 0, 1, 3, 4, 2, 5 }.
@param currentIndex the index of the object to be moved. If this isn't a
valid index, then nothing will be done
@param newIndex the index at which you'd like this object to end up. If this
is less than zero, it will be moved to the end of the array
*/
void move (int currentIndex, int newIndex) noexcept
{
if (currentIndex != newIndex)
{
const ScopedLockType lock (getLock());
values.move (currentIndex, newIndex);
}
}
/** This swaps the contents of this array with those of another array.
If you need to exchange two arrays, this is vastly quicker than using copy-by-value
because it just swaps their internal pointers.
*/
template <class OtherArrayType>
void swapWith (OtherArrayType& otherArray) noexcept
{
const ScopedLockType lock1 (getLock());
const typename OtherArrayType::ScopedLockType lock2 (otherArray.getLock());
values.swapWith (otherArray.values);
}
//==============================================================================
/** Reduces the amount of storage being used by the array.
Arrays typically allocate slightly more storage than they need, and after
removing elements, they may have quite a lot of unused space allocated.
This method will reduce the amount of allocated storage to a minimum.
*/
void minimiseStorageOverheads() noexcept
{
const ScopedLockType lock (getLock());
values.shrinkToNoMoreThan (values.size());
}
/** Increases the array's internal storage to hold a minimum number of elements.
Calling this before adding a large known number of elements means that
the array won't have to keep dynamically resizing itself as the elements
are added, and it'll therefore be more efficient.
*/
void ensureStorageAllocated (int minNumElements) noexcept
{
const ScopedLockType lock (getLock());
values.ensureAllocatedSize (minNumElements);
}
//==============================================================================
/** Sorts the elements in the array.
This will use a comparator object to sort the elements into order. The object
passed must have a method of the form:
@code
int compareElements (ElementType* first, ElementType* second);
@endcode
..and this method must return:
- a value of < 0 if the first comes before the second
- a value of 0 if the two objects are equivalent
- a value of > 0 if the second comes before the first
To improve performance, the compareElements() method can be declared as static or const.
@param comparator the comparator to use for comparing elements.
@param retainOrderOfEquivalentItems if this is true, then items
which the comparator says are equivalent will be
kept in the order in which they currently appear
in the array. This is slower to perform, but may
be important in some cases. If it's false, a faster
algorithm is used, but equivalent elements may be
rearranged.
@see sortArray, indexOfSorted
*/
template <class ElementComparator>
void sort (ElementComparator& comparator,
bool retainOrderOfEquivalentItems = false) 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());
if (size() > 1)
sortArray (comparator, values.begin(), 0, size() - 1, retainOrderOfEquivalentItems);
}
//==============================================================================
/** Returns the CriticalSection that locks this array.
To lock, you can call getLock().enter() and getLock().exit(), or preferably use
an object of ScopedLockType as an RAII lock for it.
*/
inline const TypeOfCriticalSectionToUse& getLock() const noexcept { return values; }
/** Returns the type of scoped lock to use for locking this array */
using ScopedLockType = typename TypeOfCriticalSectionToUse::ScopedLockType;
//==============================================================================
#ifndef DOXYGEN
[[deprecated ("This method has been replaced by a more flexible templated version and renamed "
"to swapWith to be more consistent with the names used in other classes.")]]
void swapWithArray (OwnedArray& other) noexcept { swapWith (other); }
#endif
private:
//==============================================================================
ArrayBase <ObjectClass*, TypeOfCriticalSectionToUse> values;
void deleteAllObjects()
{
auto i = values.size();
while (--i >= 0)
{
auto* e = values[i];
values.removeElements (i, 1);
ContainerDeletePolicy<ObjectClass>::destroy (e);
}
}
template <class OtherObjectClass, class OtherCriticalSection>
friend class OwnedArray;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OwnedArray)
};
} // namespace juce

View File

@ -0,0 +1,217 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
PropertySet::PropertySet (bool ignoreCaseOfKeyNames)
: properties (ignoreCaseOfKeyNames),
fallbackProperties (nullptr),
ignoreCaseOfKeys (ignoreCaseOfKeyNames)
{
}
PropertySet::PropertySet (const PropertySet& other)
: properties (other.properties),
fallbackProperties (other.fallbackProperties),
ignoreCaseOfKeys (other.ignoreCaseOfKeys)
{
}
PropertySet& PropertySet::operator= (const PropertySet& other)
{
properties = other.properties;
fallbackProperties = other.fallbackProperties;
ignoreCaseOfKeys = other.ignoreCaseOfKeys;
propertyChanged();
return *this;
}
PropertySet::~PropertySet()
{
}
void PropertySet::clear()
{
const ScopedLock sl (lock);
if (properties.size() > 0)
{
properties.clear();
propertyChanged();
}
}
String PropertySet::getValue (StringRef keyName, const String& defaultValue) const noexcept
{
const ScopedLock sl (lock);
auto index = properties.getAllKeys().indexOf (keyName, ignoreCaseOfKeys);
if (index >= 0)
return properties.getAllValues() [index];
return fallbackProperties != nullptr ? fallbackProperties->getValue (keyName, defaultValue)
: defaultValue;
}
int PropertySet::getIntValue (StringRef keyName, int defaultValue) const noexcept
{
const ScopedLock sl (lock);
auto index = properties.getAllKeys().indexOf (keyName, ignoreCaseOfKeys);
if (index >= 0)
return properties.getAllValues() [index].getIntValue();
return fallbackProperties != nullptr ? fallbackProperties->getIntValue (keyName, defaultValue)
: defaultValue;
}
double PropertySet::getDoubleValue (StringRef keyName, double defaultValue) const noexcept
{
const ScopedLock sl (lock);
auto index = properties.getAllKeys().indexOf (keyName, ignoreCaseOfKeys);
if (index >= 0)
return properties.getAllValues()[index].getDoubleValue();
return fallbackProperties != nullptr ? fallbackProperties->getDoubleValue (keyName, defaultValue)
: defaultValue;
}
bool PropertySet::getBoolValue (StringRef keyName, bool defaultValue) const noexcept
{
const ScopedLock sl (lock);
auto index = properties.getAllKeys().indexOf (keyName, ignoreCaseOfKeys);
if (index >= 0)
return properties.getAllValues() [index].getIntValue() != 0;
return fallbackProperties != nullptr ? fallbackProperties->getBoolValue (keyName, defaultValue)
: defaultValue;
}
std::unique_ptr<XmlElement> PropertySet::getXmlValue (StringRef keyName) const
{
return parseXML (getValue (keyName));
}
void PropertySet::setValue (StringRef keyName, const var& v)
{
jassert (keyName.isNotEmpty()); // shouldn't use an empty key name!
if (keyName.isNotEmpty())
{
auto value = v.toString();
const ScopedLock sl (lock);
auto index = properties.getAllKeys().indexOf (keyName, ignoreCaseOfKeys);
if (index < 0 || properties.getAllValues() [index] != value)
{
properties.set (keyName, value);
propertyChanged();
}
}
}
void PropertySet::removeValue (StringRef keyName)
{
if (keyName.isNotEmpty())
{
const ScopedLock sl (lock);
auto index = properties.getAllKeys().indexOf (keyName, ignoreCaseOfKeys);
if (index >= 0)
{
properties.remove (keyName);
propertyChanged();
}
}
}
void PropertySet::setValue (StringRef keyName, const XmlElement* xml)
{
setValue (keyName, xml == nullptr ? var()
: var (xml->toString (XmlElement::TextFormat().singleLine().withoutHeader())));
}
bool PropertySet::containsKey (StringRef keyName) const noexcept
{
const ScopedLock sl (lock);
return properties.getAllKeys().contains (keyName, ignoreCaseOfKeys);
}
void PropertySet::addAllPropertiesFrom (const PropertySet& source)
{
const ScopedLock sl (source.getLock());
for (int i = 0; i < source.properties.size(); ++i)
setValue (source.properties.getAllKeys() [i],
source.properties.getAllValues() [i]);
}
void PropertySet::setFallbackPropertySet (PropertySet* fallbackProperties_) noexcept
{
const ScopedLock sl (lock);
fallbackProperties = fallbackProperties_;
}
std::unique_ptr<XmlElement> PropertySet::createXml (const String& nodeName) const
{
auto xml = std::make_unique<XmlElement> (nodeName);
const ScopedLock sl (lock);
for (int i = 0; i < properties.getAllKeys().size(); ++i)
{
auto e = xml->createNewChildElement ("VALUE");
e->setAttribute ("name", properties.getAllKeys()[i]);
e->setAttribute ("val", properties.getAllValues()[i]);
}
return xml;
}
void PropertySet::restoreFromXml (const XmlElement& xml)
{
const ScopedLock sl (lock);
clear();
for (auto* e : xml.getChildWithTagNameIterator ("VALUE"))
{
if (e->hasAttribute ("name")
&& e->hasAttribute ("val"))
{
properties.set (e->getStringAttribute ("name"),
e->getStringAttribute ("val"));
}
}
if (properties.size() > 0)
propertyChanged();
}
void PropertySet::propertyChanged()
{
}
} // namespace juce

View File

@ -0,0 +1,205 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
/**
A set of named property values, which can be strings, integers, floating point, etc.
Effectively, this just wraps a StringPairArray in an interface that makes it easier
to load and save types other than strings.
See the PropertiesFile class for a subclass of this, which automatically broadcasts change
messages and saves/loads the list from a file.
@tags{Core}
*/
class JUCE_API PropertySet
{
public:
//==============================================================================
/** Creates an empty PropertySet.
@param ignoreCaseOfKeyNames if true, the names of properties are compared in a
case-insensitive way
*/
PropertySet (bool ignoreCaseOfKeyNames = false);
/** Creates a copy of another PropertySet. */
PropertySet (const PropertySet& other);
/** Copies another PropertySet over this one. */
PropertySet& operator= (const PropertySet& other);
/** Destructor. */
virtual ~PropertySet();
//==============================================================================
/** Returns one of the properties as a string.
If the value isn't found in this set, then this will look for it in a fallback
property set (if you've specified one with the setFallbackPropertySet() method),
and if it can't find one there, it'll return the default value passed-in.
@param keyName the name of the property to retrieve
@param defaultReturnValue a value to return if the named property doesn't actually exist
*/
String getValue (StringRef keyName, const String& defaultReturnValue = String()) const noexcept;
/** Returns one of the properties as an integer.
If the value isn't found in this set, then this will look for it in a fallback
property set (if you've specified one with the setFallbackPropertySet() method),
and if it can't find one there, it'll return the default value passed-in.
@param keyName the name of the property to retrieve
@param defaultReturnValue a value to return if the named property doesn't actually exist
*/
int getIntValue (StringRef keyName, int defaultReturnValue = 0) const noexcept;
/** Returns one of the properties as an double.
If the value isn't found in this set, then this will look for it in a fallback
property set (if you've specified one with the setFallbackPropertySet() method),
and if it can't find one there, it'll return the default value passed-in.
@param keyName the name of the property to retrieve
@param defaultReturnValue a value to return if the named property doesn't actually exist
*/
double getDoubleValue (StringRef keyName, double defaultReturnValue = 0.0) const noexcept;
/** Returns one of the properties as an boolean.
The result will be true if the string found for this key name can be parsed as a non-zero
integer.
If the value isn't found in this set, then this will look for it in a fallback
property set (if you've specified one with the setFallbackPropertySet() method),
and if it can't find one there, it'll return the default value passed-in.
@param keyName the name of the property to retrieve
@param defaultReturnValue a value to return if the named property doesn't actually exist
*/
bool getBoolValue (StringRef keyName, bool defaultReturnValue = false) const noexcept;
/** Returns one of the properties as an XML element.
The result will a new XMLElement object. It may return nullptr if the key isn't found,
or if the entry contains an string that isn't valid XML.
If the value isn't found in this set, then this will look for it in a fallback
property set (if you've specified one with the setFallbackPropertySet() method),
and if it can't find one there, it'll return the default value passed-in.
@param keyName the name of the property to retrieve
*/
std::unique_ptr<XmlElement> getXmlValue (StringRef keyName) const;
//==============================================================================
/** Sets a named property.
@param keyName the name of the property to set. (This mustn't be an empty string)
@param value the new value to set it to
*/
void setValue (StringRef keyName, const var& value);
/** Sets a named property to an XML element.
@param keyName the name of the property to set. (This mustn't be an empty string)
@param xml the new element to set it to. If this is a nullptr, the value will
be set to an empty string
@see getXmlValue
*/
void setValue (StringRef keyName, const XmlElement* xml);
/** This copies all the values from a source PropertySet to this one.
This won't remove any existing settings, it just adds any that it finds in the source set.
*/
void addAllPropertiesFrom (const PropertySet& source);
//==============================================================================
/** Deletes a property.
@param keyName the name of the property to delete. (This mustn't be an empty string)
*/
void removeValue (StringRef keyName);
/** Returns true if the properties include the given key. */
bool containsKey (StringRef keyName) const noexcept;
/** Removes all values. */
void clear();
//==============================================================================
/** Returns the keys/value pair array containing all the properties. */
StringPairArray& getAllProperties() noexcept { return properties; }
/** Returns the lock used when reading or writing to this set */
const CriticalSection& getLock() const noexcept { return lock; }
//==============================================================================
/** Returns an XML element which encapsulates all the items in this property set.
The string parameter is the tag name that should be used for the node.
@see restoreFromXml
*/
std::unique_ptr<XmlElement> createXml (const String& nodeName) const;
/** Reloads a set of properties that were previously stored as XML.
The node passed in must have been created by the createXml() method.
@see createXml
*/
void restoreFromXml (const XmlElement& xml);
//==============================================================================
/** Sets up a second PopertySet that will be used to look up any values that aren't
set in this one.
If you set this up to be a pointer to a second property set, then whenever one
of the getValue() methods fails to find an entry in this set, it will look up that
value in the fallback set, and if it finds it, it will return that.
Make sure that you don't delete the fallback set while it's still being used by
another set! To remove the fallback set, just call this method with a null pointer.
@see getFallbackPropertySet
*/
void setFallbackPropertySet (PropertySet* fallbackProperties) noexcept;
/** Returns the fallback property set.
@see setFallbackPropertySet
*/
PropertySet* getFallbackPropertySet() const noexcept { return fallbackProperties; }
protected:
/** Subclasses can override this to be told when one of the properties has been changed. */
virtual void propertyChanged();
private:
StringPairArray properties;
PropertySet* fallbackProperties;
CriticalSection lock;
bool ignoreCaseOfKeys;
JUCE_LEAK_DETECTOR (PropertySet)
};
} // namespace juce

View File

@ -0,0 +1,179 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
#if JUCE_UNIT_TESTS
class ReferenceCountedArrayTests : public UnitTest
{
public:
ReferenceCountedArrayTests()
: UnitTest ("ReferenceCountedArray", UnitTestCategories::containers)
{}
//==============================================================================
void runTest() override
{
beginTest ("Add derived objects");
{
ReferenceCountedArray<TestDerivedObj> derivedArray;
derivedArray.add (static_cast<TestDerivedObj*> (new TestBaseObj()));
expectEquals (derivedArray.size(), 1);
expectEquals (derivedArray.getObjectPointer (0)->getReferenceCount(), 1);
expectEquals (derivedArray[0]->getReferenceCount(), 2);
for (auto o : derivedArray)
expectEquals (o->getReferenceCount(), 1);
ReferenceCountedArray<TestBaseObj> baseArray;
baseArray.addArray (derivedArray);
for (auto o : baseArray)
expectEquals (o->getReferenceCount(), 2);
derivedArray.clearQuick();
baseArray.clearQuick();
auto* baseObject = new TestBaseObj();
TestBaseObj::Ptr baseObjectPtr = baseObject;
expectEquals (baseObject->getReferenceCount(), 1);
auto* derivedObject = new TestDerivedObj();
TestDerivedObj::Ptr derivedObjectPtr = derivedObject;
expectEquals (derivedObject->getReferenceCount(), 1);
baseArray.add (baseObject);
baseArray.add (derivedObject);
for (auto o : baseArray)
expectEquals (o->getReferenceCount(), 2);
expectEquals (baseObject->getReferenceCount(), 2);
expectEquals (derivedObject->getReferenceCount(), 2);
derivedArray.add (derivedObject);
for (auto o : derivedArray)
expectEquals (o->getReferenceCount(), 3);
derivedArray.clearQuick();
baseArray.clearQuick();
expectEquals (baseObject->getReferenceCount(), 1);
expectEquals (derivedObject->getReferenceCount(), 1);
baseArray.add (baseObjectPtr);
baseArray.add (derivedObjectPtr.get());
for (auto o : baseArray)
expectEquals (o->getReferenceCount(), 2);
derivedArray.add (derivedObjectPtr);
for (auto o : derivedArray)
expectEquals (o->getReferenceCount(), 3);
}
beginTest ("Iterate in destructor");
{
{
ReferenceCountedArray<DestructorObj> arr;
for (int i = 0; i < 2; ++i)
arr.add (new DestructorObj (*this, arr));
}
ReferenceCountedArray<DestructorObj> arr;
for (int i = 0; i < 1025; ++i)
arr.add (new DestructorObj (*this, arr));
while (! arr.isEmpty())
arr.remove (0);
for (int i = 0; i < 1025; ++i)
arr.add (new DestructorObj (*this, arr));
arr.removeRange (1, arr.size() - 3);
for (int i = 0; i < 1025; ++i)
arr.add (new DestructorObj (*this, arr));
arr.set (500, new DestructorObj (*this, arr));
}
}
private:
struct TestBaseObj : public ReferenceCountedObject
{
using Ptr = ReferenceCountedObjectPtr<TestBaseObj>;
TestBaseObj() = default;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TestBaseObj)
};
struct TestDerivedObj : public TestBaseObj
{
using Ptr = ReferenceCountedObjectPtr<TestDerivedObj>;
TestDerivedObj() = default;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TestDerivedObj)
};
struct DestructorObj : public ReferenceCountedObject
{
DestructorObj (ReferenceCountedArrayTests& p,
ReferenceCountedArray<DestructorObj>& arr)
: parent (p), objectArray (arr)
{}
~DestructorObj()
{
data = 0;
for (auto* o : objectArray)
{
parent.expect (o != nullptr);
parent.expect (o != this);
if (o != nullptr)
parent.expectEquals (o->data, 374);
}
}
ReferenceCountedArrayTests& parent;
ReferenceCountedArray<DestructorObj>& objectArray;
int data = 374;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DestructorObj)
};
};
static ReferenceCountedArrayTests referenceCountedArrayTests;
#endif
} // namespace juce

View File

@ -0,0 +1,907 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
/**
Holds a list of objects derived from ReferenceCountedObject, or which implement basic
reference-count handling methods.
The template parameter specifies the class of the object you want to point to - the easiest
way to make a class reference-countable is to simply make it inherit from ReferenceCountedObject
or SingleThreadedReferenceCountedObject, but if you need to, you can roll your own reference-countable
class by implementing a set of methods called incReferenceCount(), decReferenceCount(), and
decReferenceCountWithoutDeleting(). See ReferenceCountedObject for examples of how these methods
should behave.
A ReferenceCountedArray holds objects derived from ReferenceCountedObject,
and takes care of incrementing and decrementing their ref counts when they
are added and removed from the array.
To make all the array's methods thread-safe, pass in "CriticalSection" as the templated
TypeOfCriticalSectionToUse parameter, instead of the default DummyCriticalSection.
@see Array, OwnedArray, StringArray
@tags{Core}
*/
template <class ObjectClass, class TypeOfCriticalSectionToUse = DummyCriticalSection>
class ReferenceCountedArray
{
public:
using ObjectClassPtr = ReferenceCountedObjectPtr<ObjectClass>;
//==============================================================================
/** Creates an empty array.
@see ReferenceCountedObject, Array, OwnedArray
*/
ReferenceCountedArray() = default;
/** Creates a copy of another array */
ReferenceCountedArray (const ReferenceCountedArray& other) noexcept
{
const ScopedLockType lock (other.getLock());
values.addArray (other.begin(), other.size());
for (auto* o : *this)
if (o != nullptr)
o->incReferenceCount();
}
/** Moves from another array */
ReferenceCountedArray (ReferenceCountedArray&& other) noexcept
: values (std::move (other.values))
{
}
/** Creates a copy of another array */
template <class OtherObjectClass, class OtherCriticalSection>
ReferenceCountedArray (const ReferenceCountedArray<OtherObjectClass, OtherCriticalSection>& other) noexcept
{
const typename ReferenceCountedArray<OtherObjectClass, OtherCriticalSection>::ScopedLockType lock (other.getLock());
values.addArray (other.begin(), other.size());
for (auto* o : *this)
if (o != nullptr)
o->incReferenceCount();
}
/** Copies another array into this one.
Any existing objects in this array will first be released.
*/
ReferenceCountedArray& operator= (const ReferenceCountedArray& other) noexcept
{
releaseAllObjects();
auto otherCopy = other;
swapWith (otherCopy);
return *this;
}
/** Copies another array into this one.
Any existing objects in this array will first be released.
*/
template <class OtherObjectClass>
ReferenceCountedArray& operator= (const ReferenceCountedArray<OtherObjectClass, TypeOfCriticalSectionToUse>& other) noexcept
{
auto otherCopy = other;
swapWith (otherCopy);
return *this;
}
/** Moves from another array */
ReferenceCountedArray& operator= (ReferenceCountedArray&& other) noexcept
{
releaseAllObjects();
values = std::move (other.values);
return *this;
}
/** Destructor.
Any objects in the array will be released, and may be deleted if not referenced from elsewhere.
*/
~ReferenceCountedArray()
{
releaseAllObjects();
}
//==============================================================================
/** Removes all objects from the array.
Any objects in the array whose reference counts drop to zero will be deleted.
*/
void clear()
{
const ScopedLockType lock (getLock());
clearQuick();
values.setAllocatedSize (0);
}
/** Removes all objects from the array without freeing the array's allocated storage.
Any objects in the array that whose reference counts drop to zero will be deleted.
@see clear
*/
void clearQuick()
{
const ScopedLockType lock (getLock());
releaseAllObjects();
}
/** Returns the current number of objects in the array. */
inline int size() const noexcept
{
return values.size();
}
/** Returns true if the array is empty, false otherwise. */
inline bool isEmpty() const noexcept
{
return size() == 0;
}
/** Returns a pointer to the object at this index in the array.
If the index is out-of-range, this will return a null pointer, (and
it could be null anyway, because it's ok for the array to hold null
pointers as well as objects).
@see getUnchecked
*/
inline ObjectClassPtr operator[] (int index) const noexcept
{
return ObjectClassPtr (getObjectPointer (index));
}
/** Returns a pointer to the object at this index in the array, without checking
whether the index is in-range.
This is a faster and less safe version of operator[] which doesn't check the index passed in, so
it can be used when you're sure the index is always going to be legal.
*/
inline ObjectClassPtr getUnchecked (int index) const noexcept
{
return ObjectClassPtr (getObjectPointerUnchecked (index));
}
/** Returns a raw pointer to the object at this index in the array.
If the index is out-of-range, this will return a null pointer, (and
it could be null anyway, because it's ok for the array to hold null
pointers as well as objects).
@see getUnchecked
*/
inline ObjectClass* getObjectPointer (int index) const noexcept
{
const ScopedLockType lock (getLock());
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 (int index) const noexcept
{
const ScopedLockType lock (getLock());
return values[index];
}
/** Returns a pointer to the first object in the array.
This will return a null pointer if the array's empty.
@see getLast
*/
inline ObjectClassPtr getFirst() const noexcept
{
const ScopedLockType lock (getLock());
return values.getFirst();
}
/** Returns a pointer to the last object in the array.
This will return a null pointer if the array's empty.
@see getFirst
*/
inline ObjectClassPtr getLast() const noexcept
{
const ScopedLockType lock (getLock());
return values.getLast();
}
/** Returns a pointer to the actual array data.
This pointer will only be valid until the next time a non-const method
is called on the array.
*/
inline ObjectClass** getRawDataPointer() const noexcept
{
return values.begin();
}
//==============================================================================
/** Returns a pointer to the first element in the array.
This method is provided for compatibility with standard C++ iteration mechanisms.
*/
inline ObjectClass** begin() noexcept
{
return values.begin();
}
/** Returns a pointer to the first element in the array.
This method is provided for compatibility with standard C++ iteration mechanisms.
*/
inline ObjectClass* const* begin() const noexcept
{
return values.begin();
}
/** Returns a pointer to the element which follows the last element in the array.
This method is provided for compatibility with standard C++ iteration mechanisms.
*/
inline ObjectClass** end() noexcept
{
return values.end();
}
/** Returns a pointer to the element which follows the last element in the array.
This method is provided for compatibility with standard C++ iteration mechanisms.
*/
inline ObjectClass* const* end() const noexcept
{
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() noexcept
{
return begin();
}
/** Returns a pointer to the first element in the array.
This method is provided for compatibility with the standard C++ containers.
*/
inline ObjectClass* const* data() const noexcept
{
return begin();
}
//==============================================================================
/** Finds the index of the first occurrence of an object in the array.
@param objectToLookFor the object to look for
@returns the index at which the object was found, or -1 if it's not found
*/
int indexOf (const ObjectClass* objectToLookFor) const noexcept
{
const ScopedLockType lock (getLock());
auto* e = values.begin();
auto* endPointer = values.end();
while (e != endPointer)
{
if (objectToLookFor == *e)
return static_cast<int> (e - values.begin());
++e;
}
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
@returns true if the object is in the array
*/
bool contains (const ObjectClass* objectToLookFor) const noexcept
{
const ScopedLockType lock (getLock());
auto* e = values.begin();
auto* endPointer = values.end();
while (e != endPointer)
{
if (objectToLookFor == *e)
return true;
++e;
}
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.
@param newObject the new object to add to the array
@see set, insert, addIfNotAlreadyThere, addSorted, addArray
*/
ObjectClass* add (ObjectClass* newObject)
{
const ScopedLockType lock (getLock());
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();
return newObject;
}
/** Inserts a new object into the array at the given index.
If the index is less than 0 or greater than the size of the array, the
element will be added to the end of the array.
Otherwise, it will be inserted into the array, moving all the later elements
along to make room.
This will increase the new object's reference count.
@param indexToInsertAt the index at which the new element should be inserted
@param newObject the new object to add to the array
@see add, addSorted, addIfNotAlreadyThere, set
*/
ObjectClass* insert (int indexToInsertAt, 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 (contains (newObject))
return false;
add (newObject);
return true;
}
/** 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 (const ObjectClassPtr& newObject) { return addIfNotAlreadyThere (newObject.get()); }
/** Replaces an object in the array with a different one.
If the index is less than zero, this method does nothing.
If the index is beyond the end of the array, the new object is added to the end of the array.
The object being added has its reference count increased, and if it's replacing
another object, then that one has its reference count decreased, and may be deleted.
@param indexToChange the index whose value you want to change
@param newObject the new value to set for this index.
@see add, insert, remove
*/
void set (int indexToChange, ObjectClass* newObject)
{
if (indexToChange >= 0)
{
const ScopedLockType lock (getLock());
if (newObject != nullptr)
newObject->incReferenceCount();
if (indexToChange < values.size())
{
auto* e = values[indexToChange];
values[indexToChange] = newObject;
releaseObject (e);
}
else
{
values.add (newObject);
}
}
}
/** Replaces an object in the array with a different one.
If the index is less than zero, this method does nothing.
If the index is beyond the end of the array, the new object is added to the end of the array.
The object being added has its reference count increased, and if it's replacing
another object, then that one has its reference count decreased, and may be deleted.
@param indexToChange the index whose value you want to change
@param newObject the new value to set for this index.
@see add, insert, remove
*/
void set (int indexToChange, const ObjectClassPtr& newObject) { set (indexToChange, newObject.get()); }
/** Adds elements from another array to the end of this array.
@param arrayToAddFrom the array from which to copy the elements
@param startIndex the first element of the other array to start copying from
@param numElementsToAdd how many elements to add from the other array. If this
value is negative or greater than the number of available elements,
all available elements will be copied.
@see add
*/
void addArray (const ReferenceCountedArray& arrayToAddFrom,
int startIndex = 0,
int numElementsToAdd = -1) noexcept
{
const ScopedLockType lock1 (arrayToAddFrom.getLock());
{
const ScopedLockType lock2 (getLock());
auto numElementsAdded = values.addArray (arrayToAddFrom.values, startIndex, numElementsToAdd);
auto** e = values.end();
for (int i = 0; i < numElementsAdded; ++i)
(*(--e))->incReferenceCount();
}
}
/** Inserts a new object into the array assuming that the array is sorted.
This will use a comparator to find the position at which the new object
should go. If the array isn't sorted, the behaviour of this
method will be unpredictable.
@param comparator the comparator object to use to compare the elements - see the
sort() method for details about this object's form
@param newObject the new object to insert to the array
@returns the index at which the new object was added
@see add, sort
*/
template <class ElementComparator>
int addSorted (ElementComparator& comparator, ObjectClass* newObject) noexcept
{
const ScopedLockType lock (getLock());
auto index = findInsertIndexInSortedArray (comparator, values.begin(), newObject, 0, values.size());
insert (index, newObject);
return index;
}
/** Inserts or replaces an object in the array, assuming it is sorted.
This is similar to addSorted, but if a matching element already exists, then it will be
replaced by the new one, rather than the new one being added as well.
*/
template <class ElementComparator>
void addOrReplaceSorted (ElementComparator& comparator, ObjectClass* newObject) noexcept
{
const ScopedLockType lock (getLock());
auto index = findInsertIndexInSortedArray (comparator, values.begin(), newObject, 0, values.size());
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
}
/** Finds the index of an object in the array, assuming that the array is sorted.
This will use a comparator to do a binary-chop to find the index of the given
element, if it exists. If the array isn't sorted, the behaviour of this
method will be unpredictable.
@param comparator the comparator to use to compare the elements - see the sort()
method for details about the form this object should take
@param objectToLookFor the object to search for
@returns the index of the element, or -1 if it's not found
@see addSorted, sort
*/
template <class ElementComparator>
int indexOfSorted (ElementComparator& comparator,
const ObjectClass* objectToLookFor) const noexcept
{
ignoreUnused (comparator);
const ScopedLockType lock (getLock());
int s = 0, e = values.size();
while (s < e)
{
if (comparator.compareElements (objectToLookFor, values[s]) == 0)
return s;
auto halfway = (s + e) / 2;
if (halfway == s)
break;
if (comparator.compareElements (objectToLookFor, values[halfway]) >= 0)
s = halfway;
else
e = halfway;
}
return -1;
}
//==============================================================================
/** Removes an object from the array.
This will remove the object at a given index and move back all the
subsequent objects to close the gap.
If the index passed in is out-of-range, nothing will happen.
The object that is removed will have its reference count decreased,
and may be deleted if not referenced from elsewhere.
@param indexToRemove the index of the element to remove
@see removeObject, removeRange
*/
void remove (int indexToRemove)
{
const ScopedLockType lock (getLock());
if (isPositiveAndBelow (indexToRemove, values.size()))
{
auto* e = *(values.begin() + indexToRemove);
values.removeElements (indexToRemove, 1);
releaseObject (e);
if ((values.size() << 1) < values.capacity())
minimiseStorageOverheads();
}
}
/** Removes and returns an object from the array.
This will remove the object at a given index and return it, moving back all
the subsequent objects to close the gap. If the index passed in is out-of-range,
nothing will happen and a null pointer will be returned.
@param indexToRemove the index of the element to remove
@see remove, removeObject, removeRange
*/
ObjectClassPtr removeAndReturn (int indexToRemove)
{
ObjectClassPtr removedItem;
const ScopedLockType lock (getLock());
if (isPositiveAndBelow (indexToRemove, values.size()))
{
auto* e = *(values.begin() + indexToRemove);
removedItem = e;
values.removeElements (indexToRemove, 1);
releaseObject (e);
if ((values.size() << 1) < values.capacity())
minimiseStorageOverheads();
}
return removedItem;
}
/** Removes the first occurrence of a specified object from the array.
If the item isn't found, no action is taken. If it is found, it is
removed and has its reference count decreased.
@param objectToRemove the object to try to remove
@see remove, removeRange
*/
void removeObject (ObjectClass* objectToRemove)
{
const ScopedLockType lock (getLock());
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,
and move any subsequent elements down to close the gap.
If the range extends beyond the bounds of the array, it will
be safely clipped to the size of the array.
The objects that are removed will have their reference counts decreased,
and may be deleted if not referenced from elsewhere.
@param startIndex the index of the first object to remove
@param numberToRemove how many objects should be removed
@see remove, removeObject
*/
void removeRange (int startIndex,
int numberToRemove)
{
const ScopedLockType lock (getLock());
startIndex = jlimit (0, values.size(), startIndex);
auto endIndex = jlimit (0, values.size(), startIndex + numberToRemove);
numberToRemove = endIndex - startIndex;
if (numberToRemove > 0)
{
Array<ObjectClass*> objectsToRemove;
objectsToRemove.addArray (values.begin() + startIndex, numberToRemove);
values.removeElements (startIndex, numberToRemove);
for (auto& o : objectsToRemove)
releaseObject (o);
if ((values.size() << 1) < values.capacity())
minimiseStorageOverheads();
}
}
/** Removes the last n objects from the array.
The objects that are removed will have their reference counts decreased,
and may be deleted if not referenced from elsewhere.
@param howManyToRemove how many objects to remove from the end of the array
@see remove, removeObject, removeRange
*/
void removeLast (int howManyToRemove = 1)
{
const ScopedLockType lock (getLock());
if (howManyToRemove > values.size())
howManyToRemove = values.size();
while (--howManyToRemove >= 0)
remove (values.size() - 1);
}
/** Swaps a pair of objects in the array.
If either of the indexes passed in is out-of-range, nothing will happen,
otherwise the two objects at these positions will be exchanged.
*/
void swap (int index1, int index2) noexcept
{
const ScopedLockType lock (getLock());
if (isPositiveAndBelow (index1, values.size())
&& isPositiveAndBelow (index2, values.size()))
{
std::swap (values[index1], values[index2]);
}
}
/** Moves one of the objects to a different position.
This will move the object to a specified index, shuffling along
any intervening elements as required.
So for example, if you have the array { 0, 1, 2, 3, 4, 5 } then calling
move (2, 4) would result in { 0, 1, 3, 4, 2, 5 }.
@param currentIndex the index of the object to be moved. If this isn't a
valid index, then nothing will be done
@param newIndex the index at which you'd like this object to end up. If this
is less than zero, it will be moved to the end of the array
*/
void move (int currentIndex, int newIndex) noexcept
{
if (currentIndex != newIndex)
{
const ScopedLockType lock (getLock());
values.move (currentIndex, newIndex);
}
}
//==============================================================================
/** This swaps the contents of this array with those of another array.
If you need to exchange two arrays, this is vastly quicker than using copy-by-value
because it just swaps their internal pointers.
*/
template <class OtherArrayType>
void swapWith (OtherArrayType& otherArray) noexcept
{
const ScopedLockType lock1 (getLock());
const typename OtherArrayType::ScopedLockType lock2 (otherArray.getLock());
values.swapWith (otherArray.values);
}
//==============================================================================
/** Compares this array to another one.
@returns true only if the other array contains the same objects in the same order
*/
bool operator== (const ReferenceCountedArray& other) const noexcept
{
const ScopedLockType lock2 (other.getLock());
const ScopedLockType lock1 (getLock());
return values == other.values;
}
/** Compares this array to another one.
@see operator==
*/
bool operator!= (const ReferenceCountedArray<ObjectClass, TypeOfCriticalSectionToUse>& other) const noexcept
{
return ! operator== (other);
}
//==============================================================================
/** Sorts the elements in the array.
This will use a comparator object to sort the elements into order. The object
passed must have a method of the form:
@code
int compareElements (ElementType first, ElementType second);
@endcode
..and this method must return:
- a value of < 0 if the first comes before the second
- a value of 0 if the two objects are equivalent
- a value of > 0 if the second comes before the first
To improve performance, the compareElements() method can be declared as static or const.
@param comparator the comparator to use for comparing elements.
@param retainOrderOfEquivalentItems if this is true, then items
which the comparator says are equivalent will be
kept in the order in which they currently appear
in the array. This is slower to perform, but may
be important in some cases. If it's false, a faster
algorithm is used, but equivalent elements may be
rearranged.
@see sortArray
*/
template <class ElementComparator>
void sort (ElementComparator& comparator,
bool retainOrderOfEquivalentItems = false) 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());
sortArray (comparator, values.begin(), 0, values.size() - 1, retainOrderOfEquivalentItems);
}
//==============================================================================
/** Reduces the amount of storage being used by the array.
Arrays typically allocate slightly more storage than they need, and after
removing elements, they may have quite a lot of unused space allocated.
This method will reduce the amount of allocated storage to a minimum.
*/
void minimiseStorageOverheads() noexcept
{
const ScopedLockType lock (getLock());
values.shrinkToNoMoreThan (values.size());
}
/** Increases the array's internal storage to hold a minimum number of elements.
Calling this before adding a large known number of elements means that
the array won't have to keep dynamically resizing itself as the elements
are added, and it'll therefore be more efficient.
*/
void ensureStorageAllocated (const int minNumElements)
{
const ScopedLockType lock (getLock());
values.ensureAllocatedSize (minNumElements);
}
//==============================================================================
/** Returns the CriticalSection that locks this array.
To lock, you can call getLock().enter() and getLock().exit(), or preferably use
an object of ScopedLockType as an RAII lock for it.
*/
inline const TypeOfCriticalSectionToUse& getLock() const noexcept { return values; }
/** Returns the type of scoped lock to use for locking this array */
using ScopedLockType = typename TypeOfCriticalSectionToUse::ScopedLockType;
//==============================================================================
#ifndef DOXYGEN
[[deprecated ("This method has been replaced by a more flexible templated version and renamed "
"to swapWith to be more consistent with the names used in other classes.")]]
void swapWithArray (ReferenceCountedArray& other) noexcept { swapWith (other); }
#endif
private:
//==============================================================================
ArrayBase<ObjectClass*, TypeOfCriticalSectionToUse> values;
void releaseAllObjects()
{
auto i = values.size();
while (--i >= 0)
{
auto* e = values[i];
values.removeElements (i, 1);
releaseObject (e);
}
}
static void releaseObject (ObjectClass* o)
{
if (o != nullptr && o->decReferenceCountWithoutDeleting())
ContainerDeletePolicy<ObjectClass>::destroy (o);
}
};
} // namespace juce

View File

@ -0,0 +1,93 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
/**
Helper class providing an RAII-based mechanism for temporarily setting and
then re-setting a value.
E.g. @code
int x = 1;
{
ScopedValueSetter setter (x, 2);
// x is now 2
}
// x is now 1 again
{
ScopedValueSetter setter (x, 3, 4);
// x is now 3
}
// x is now 4
@endcode
@tags{Core}
*/
template <typename ValueType>
class ScopedValueSetter
{
public:
/** Creates a ScopedValueSetter that will immediately change the specified value to the
given new value, and will then reset it to its original value when this object is deleted.
*/
ScopedValueSetter (ValueType& valueToSet,
ValueType newValue)
: value (valueToSet),
originalValue (valueToSet)
{
valueToSet = newValue;
}
/** Creates a ScopedValueSetter that will immediately change the specified value to the
given new value, and will then reset it to be valueWhenDeleted when this object is deleted.
*/
ScopedValueSetter (ValueType& valueToSet,
ValueType newValue,
ValueType valueWhenDeleted)
: value (valueToSet),
originalValue (valueWhenDeleted)
{
valueToSet = newValue;
}
~ScopedValueSetter()
{
value = originalValue;
}
private:
//==============================================================================
ValueType& value;
const ValueType originalValue;
JUCE_DECLARE_NON_COPYABLE (ScopedValueSetter)
};
} // namespace juce

View File

@ -0,0 +1,126 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
/**
Encapsulates the logic for a single-threaded FIFO.
This might be useful for building buffers which can be written and read in
blocks of different sizes. For example, in an audio effect we might wish to
run some processing on fixed-size blocks of audio input, but the host may
provide input blocks of varying sizes. In this situation, we might want to
store the previous input in a buffer, and extract a fixed-size block
whenever there are enough samples available. The SingleThreadedAbstractFifo
implements logic suitable for this use-case.
This class is quite similar to AbstractFifo, in that it only keeps track of
the current read/write locations. The user is responsible for providing the
actual buffer that will be read/written.
The intended usage of this class is as follows:
- Create some backing storage in a vector, AudioBuffer etc.
- Construct a SingleThreadedAbstractFifo to manage the buffer, passing the
number of items in the buffer.
- Each time new input is ready, call write(), passing the number of items
you wish to write into the buffer. This function returns a pair of ranges
describing which indices in the backing storage should be written.
- Call getNumReadable() to find out how many items are ready to read from
the buffer.
- If there are enough items ready to read, call read(), passing the number
of items you require. This function returns a pair of ranges describing
which indices in the backing storage may be read.
Unlike AbstractFifo, the SingleThreadedAbstractFifo is intended for use
from a single thread. It is not safe to call any non-const member function
of SingleThreadedAbstractFifo concurrently with any other member function.
@see AbstractFifo
@tags{Core}
*/
class SingleThreadedAbstractFifo
{
public:
/** Creates a SingleThreadedAbstractFifo with no size. */
SingleThreadedAbstractFifo() = default;
/** Creates a SingleThreadedAbstractFifo that can manage a buffer of the specified size. */
explicit SingleThreadedAbstractFifo (int sizeIn)
: size (sizeIn)
{
// This class only works properly when the size is a power of two.
// Use nextPowerOfTwo() to find a good size, and ensure that your
// backing storage is the same size.
jassert (isPowerOfTwo (sizeIn));
}
/** Returns the number of unused elements present in the buffer. */
int getRemainingSpace() const { return size - numReadable; }
/** Returns the number of pending elements present in the buffer. */
int getNumReadable() const { return numReadable; }
/** Returns the size of the managed buffer. */
int getSize() const { return size; }
/** Returns two blocks in the buffer where new items may be written.
Note that if the buffer is running low on free space, the sum of the lengths of
the returned ranges may be less than num!
*/
std::array<Range<int>, 2> write (int num)
{
const auto startPos = (readPos + numReadable) & (size - 1);
const auto maxToWrite = jmin (getRemainingSpace(), num);
const auto firstBlockSize = jmin (maxToWrite, size - startPos);
numReadable += maxToWrite;
return { { { startPos, startPos + firstBlockSize }, { 0, maxToWrite - firstBlockSize } } };
}
/** Returns two blocks in the buffer from which new items may be read.
Note that if the buffer doesn't have the requested number of items available,
the sum of the lengths of the returned ranges may be less than num!
*/
std::array<Range<int>, 2> read (int num)
{
const auto startPos = readPos;
const auto maxToRead = jmin (numReadable, num);
const auto firstBlockSize = jmin (maxToRead, size - startPos);
readPos = (startPos + maxToRead) & (size - 1);
numReadable -= maxToRead;
return { { { startPos, startPos + firstBlockSize }, { 0, maxToRead - firstBlockSize } } };
}
private:
int size = 0, readPos = 0, numReadable = 0;
};
} // namespace juce

View File

@ -0,0 +1,489 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4512)
//==============================================================================
/**
Holds a set of unique primitive objects, such as ints or doubles.
A set can only hold one item with a given value, so if for example it's a
set of integers, attempting to add the same integer twice will do nothing
the second time.
Internally, the list of items is kept sorted (which means that whatever
kind of primitive type is used must support the ==, <, >, <= and >= operators
to determine the order), and searching the set for known values is very fast
because it uses a binary-chop method.
Note that if you're using a class or struct as the element type, it must be
capable of being copied or moved with a straightforward memcpy, rather than
needing construction and destruction code.
To make all the set's methods thread-safe, pass in "CriticalSection" as the templated
TypeOfCriticalSectionToUse parameter, instead of the default DummyCriticalSection.
@see Array, OwnedArray, ReferenceCountedArray, StringArray, CriticalSection
@tags{Core}
*/
template <class ElementType, class TypeOfCriticalSectionToUse = DummyCriticalSection>
class SortedSet
{
public:
//==============================================================================
/** Creates an empty set. */
SortedSet() = default;
/** Creates a copy of another set. */
SortedSet (const SortedSet&) = default;
/** Creates a copy of another set. */
SortedSet (SortedSet&&) noexcept = default;
/** Makes a copy of another set. */
SortedSet& operator= (const SortedSet&) = default;
/** Makes a copy of another set. */
SortedSet& operator= (SortedSet&&) noexcept = default;
/** Destructor. */
~SortedSet() = default;
//==============================================================================
/** Compares this set to another one.
Two sets are considered equal if they both contain the same set of elements.
@param other the other set to compare with
*/
bool operator== (const SortedSet<ElementType>& other) const noexcept
{
return data == other.data;
}
/** Compares this set to another one.
Two sets are considered equal if they both contain the same set of elements.
@param other the other set to compare with
*/
bool operator!= (const SortedSet<ElementType>& other) const noexcept
{
return ! operator== (other);
}
//==============================================================================
/** Removes all elements from the set.
This will remove all the elements, and free any storage that the set is
using. To clear it without freeing the storage, use the clearQuick()
method instead.
@see clearQuick
*/
void clear() noexcept
{
data.clear();
}
/** Removes all elements from the set without freeing the array's allocated storage.
@see clear
*/
void clearQuick() noexcept
{
data.clearQuick();
}
//==============================================================================
/** Returns the current number of elements in the set. */
inline int size() const noexcept
{
return data.size();
}
/** Returns true if the set is empty, false otherwise. */
inline bool isEmpty() const noexcept
{
return size() == 0;
}
/** Returns one of the elements in the set.
If the index passed in is beyond the range of valid elements, this
will return zero.
If you're certain that the index will always be a valid element, you
can call getUnchecked() instead, which is faster.
@param index the index of the element being requested (0 is the first element in the set)
@see getUnchecked, getFirst, getLast
*/
inline ElementType operator[] (const int index) const noexcept
{
return data [index];
}
/** Returns one of the elements in the set, without checking the index passed in.
Unlike the operator[] method, this will try to return an element without
checking that the index is within the bounds of the set, so should only
be used when you're confident that it will always be a valid index.
@param index the index of the element being requested (0 is the first element in the set)
@see operator[], getFirst, getLast
*/
inline ElementType getUnchecked (const int index) const noexcept
{
return data.getUnchecked (index);
}
/** Returns a direct reference to one of the elements in the set, without checking the index passed in.
This is like getUnchecked, but returns a direct reference to the element, so that
you can alter it directly. Obviously this can be dangerous, so only use it when
absolutely necessary.
@param index the index of the element being requested (0 is the first element in the array)
*/
inline ElementType& getReference (const int index) noexcept
{
return data.getReference (index);
}
/** Returns a direct reference to one of the elements in the set, without checking the index passed in.
@param index the index of the element being requested (0 is the first element in the array)
*/
inline const ElementType& getReference (const int index) const noexcept
{
return data.getReference (index);
}
/** Returns the first element in the set, or 0 if the set is empty.
@see operator[], getUnchecked, getLast
*/
inline ElementType getFirst() const noexcept
{
return data.getFirst();
}
/** Returns the last element in the set, or 0 if the set is empty.
@see operator[], getUnchecked, getFirst
*/
inline ElementType getLast() const noexcept
{
return data.getLast();
}
//==============================================================================
/** Returns a pointer to the first element in the set.
This method is provided for compatibility with standard C++ iteration mechanisms.
*/
inline const ElementType* begin() const noexcept
{
return data.begin();
}
/** Returns a pointer to the element which follows the last element in the set.
This method is provided for compatibility with standard C++ iteration mechanisms.
*/
inline const ElementType* end() const noexcept
{
return data.end();
}
//==============================================================================
/** Finds the index of the first element which matches the value passed in.
This will search the set for the given object, and return the index
of its first occurrence. If the object isn't found, the method will return -1.
@param elementToLookFor the value or object to look for
@returns the index of the object, or -1 if it's not found
*/
int indexOf (const ElementType& elementToLookFor) const noexcept
{
const ScopedLockType lock (data.getLock());
int s = 0;
int e = data.size();
for (;;)
{
if (s >= e)
return -1;
if (elementToLookFor == data.getReference (s))
return s;
auto halfway = (s + e) / 2;
if (halfway == s)
return -1;
if (elementToLookFor < data.getReference (halfway))
e = halfway;
else
s = halfway;
}
}
/** Returns true if the set contains at least one occurrence of an object.
@param elementToLookFor the value or object to look for
@returns true if the item is found
*/
bool contains (const ElementType& elementToLookFor) const noexcept
{
return indexOf (elementToLookFor) >= 0;
}
//==============================================================================
/** Adds a new element to the set, (as long as it's not already in there).
Note that if a matching element already exists, the new value will be assigned
to the existing one using operator=, so that if there are any differences between
the objects which were not recognised by the object's operator==, then the
set will always contain a copy of the most recently added one.
@param newElement the new object to add to the set
@returns true if the value was added, or false if it already existed
@see set, insert, addIfNotAlreadyThere, addSorted, addSet, addArray
*/
bool add (const ElementType& newElement) noexcept
{
const ScopedLockType lock (getLock());
int s = 0;
int e = data.size();
while (s < e)
{
auto& elem = data.getReference (s);
if (newElement == elem)
{
elem = newElement; // force an update in case operator== permits differences.
return false;
}
auto halfway = (s + e) / 2;
bool isBeforeHalfway = (newElement < data.getReference (halfway));
if (halfway == s)
{
if (! isBeforeHalfway)
++s;
break;
}
if (isBeforeHalfway)
e = halfway;
else
s = halfway;
}
data.insert (s, newElement);
return true;
}
/** Adds elements from an array to this set.
@param elementsToAdd the array of elements to add
@param numElementsToAdd how many elements are in this other array
@see add
*/
void addArray (const ElementType* elementsToAdd,
int numElementsToAdd) noexcept
{
const ScopedLockType lock (getLock());
while (--numElementsToAdd >= 0)
add (*elementsToAdd++);
}
/** Adds elements from another set to this one.
@param setToAddFrom the set from which to copy the elements
@param startIndex the first element of the other set to start copying from
@param numElementsToAdd how many elements to add from the other set. If this
value is negative or greater than the number of available elements,
all available elements will be copied.
@see add
*/
template <class OtherSetType>
void addSet (const OtherSetType& setToAddFrom,
int startIndex = 0,
int numElementsToAdd = -1) noexcept
{
const typename OtherSetType::ScopedLockType lock1 (setToAddFrom.getLock());
const ScopedLockType lock2 (getLock());
jassert (this != &setToAddFrom);
if (this != &setToAddFrom)
{
if (startIndex < 0)
{
jassertfalse;
startIndex = 0;
}
if (numElementsToAdd < 0 || startIndex + numElementsToAdd > setToAddFrom.size())
numElementsToAdd = setToAddFrom.size() - startIndex;
if (numElementsToAdd > 0)
addArray (&setToAddFrom.data.getReference (startIndex), numElementsToAdd);
}
}
//==============================================================================
/** Removes an element from the set.
This will remove the element at a given index.
If the index passed in is out-of-range, nothing will happen.
@param indexToRemove the index of the element to remove
@returns the element that has been removed
@see removeValue, removeRange
*/
ElementType remove (const int indexToRemove) noexcept
{
return data.removeAndReturn (indexToRemove);
}
/** Removes an item from the set.
This will remove the given element from the set, if it's there.
@param valueToRemove the object to try to remove
@see remove, removeRange
*/
void removeValue (const ElementType& valueToRemove) noexcept
{
const ScopedLockType lock (getLock());
data.remove (indexOf (valueToRemove));
}
/** Removes any elements which are also in another set.
@param otherSet the other set in which to look for elements to remove
@see removeValuesNotIn, remove, removeValue, removeRange
*/
template <class OtherSetType>
void removeValuesIn (const OtherSetType& otherSet) noexcept
{
const typename OtherSetType::ScopedLockType lock1 (otherSet.getLock());
const ScopedLockType lock2 (getLock());
if (this == &otherSet)
{
clear();
}
else if (! otherSet.isEmpty())
{
for (int i = data.size(); --i >= 0;)
if (otherSet.contains (data.getReference (i)))
remove (i);
}
}
/** Removes any elements which are not found in another set.
Only elements which occur in this other set will be retained.
@param otherSet the set in which to look for elements NOT to remove
@see removeValuesIn, remove, removeValue, removeRange
*/
template <class OtherSetType>
void removeValuesNotIn (const OtherSetType& otherSet) noexcept
{
const typename OtherSetType::ScopedLockType lock1 (otherSet.getLock());
const ScopedLockType lock2 (getLock());
if (this != &otherSet)
{
if (otherSet.isEmpty())
{
clear();
}
else
{
for (int i = data.size(); --i >= 0;)
if (! otherSet.contains (data.getReference (i)))
remove (i);
}
}
}
/** This swaps the contents of this array with those of another array.
If you need to exchange two arrays, this is vastly quicker than using copy-by-value
because it just swaps their internal pointers.
*/
template <class OtherSetType>
void swapWith (OtherSetType& otherSet) noexcept
{
data.swapWith (otherSet.data);
}
//==============================================================================
/** Reduces the amount of storage being used by the set.
Sets typically allocate slightly more storage than they need, and after
removing elements, they may have quite a lot of unused space allocated.
This method will reduce the amount of allocated storage to a minimum.
*/
void minimiseStorageOverheads() noexcept
{
data.minimiseStorageOverheads();
}
/** Increases the set's internal storage to hold a minimum number of elements.
Calling this before adding a large known number of elements means that
the set won't have to keep dynamically resizing itself as the elements
are added, and it'll therefore be more efficient.
*/
void ensureStorageAllocated (const int minNumElements)
{
data.ensureStorageAllocated (minNumElements);
}
//==============================================================================
/** Returns the CriticalSection that locks this array.
To lock, you can call getLock().enter() and getLock().exit(), or preferably use
an object of ScopedLockType as an RAII lock for it.
*/
inline const TypeOfCriticalSectionToUse& getLock() const noexcept { return data.getLock(); }
/** Returns the type of scoped lock to use for locking this array */
using ScopedLockType = typename TypeOfCriticalSectionToUse::ScopedLockType;
private:
//==============================================================================
Array<ElementType, TypeOfCriticalSectionToUse> data;
};
JUCE_END_IGNORE_WARNINGS_MSVC
} // namespace juce

View File

@ -0,0 +1,206 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
#if JUCE_UNIT_TESTS
class SparseSetTests : public UnitTest
{
public:
SparseSetTests()
: UnitTest ("SparseSet class", UnitTestCategories::containers)
{}
void runTest() override
{
beginTest ("basic operations");
{
SparseSet<int> set;
expect (set.isEmpty());
expectEquals (set.size(), 0);
expectEquals (set.getNumRanges(), 0);
expect (set.getTotalRange().isEmpty());
set.addRange ({0, 10});
expect (! set.isEmpty());
expectEquals (set.size(), 10);
expectEquals (set.getNumRanges(), 1);
expect (! set.getTotalRange().isEmpty());
expect (set.getRange (0) == Range<int> (0, 10));
expectEquals (set[0], 0);
expectEquals (set[5], 5);
expectEquals (set[9], 9);
// Index out of range yields a default value for a type
expectEquals (set[10], 0);
expect (set.contains (0));
expect (set.contains (9));
expect (! set.contains (10));
}
beginTest ("adding ranges");
{
SparseSet<int> set;
// Adding same range twice should yield just a single range
set.addRange ({0, 10});
set.addRange ({0, 10});
expectEquals (set.getNumRanges(), 1);
expect (set.getRange (0) == Range<int> (0, 10));
// Adding already included range does not increase num ranges
set.addRange ({0, 2});
expectEquals (set.getNumRanges(), 1);
set.addRange ({8, 10});
expectEquals (set.getNumRanges(), 1);
set.addRange ({2, 5});
expectEquals (set.getNumRanges(), 1);
// Adding non adjacent range includes total number of ranges
set.addRange ({-10, -5});
expectEquals (set.getNumRanges(), 2);
expect (set.getRange (0) == Range<int> (-10, -5));
expect (set.getRange (1) == Range<int> (0, 10));
expect (set.getTotalRange() == Range<int> (-10, 10));
set.addRange ({15, 20});
expectEquals (set.getNumRanges(), 3);
expect (set.getRange (0) == Range<int> (-10, -5));
expect (set.getRange (1) == Range<int> (0, 10));
expect (set.getRange (2) == Range<int> (15, 20));
expect (set.getTotalRange() == Range<int> (-10, 20));
// Adding adjacent ranges merges them.
set.addRange ({-5, -3});
expectEquals (set.getNumRanges(), 3);
expect (set.getRange (0) == Range<int> (-10, -3));
expect (set.getRange (1) == Range<int> (0, 10));
expect (set.getRange (2) == Range<int> (15, 20));
expect (set.getTotalRange() == Range<int> (-10, 20));
set.addRange ({20, 25});
expectEquals (set.getNumRanges(), 3);
expect (set.getRange (0) == Range<int> (-10, -3));
expect (set.getRange (1) == Range<int> (0, 10));
expect (set.getRange (2) == Range<int> (15, 25));
expect (set.getTotalRange() == Range<int> (-10, 25));
// Adding range containing other ranges merges them
set.addRange ({-50, 50});
expectEquals (set.getNumRanges(), 1);
expect (set.getRange (0) == Range<int> (-50, 50));
expect (set.getTotalRange() == Range<int> (-50, 50));
}
beginTest ("removing ranges");
{
SparseSet<int> set;
set.addRange ({-20, -10});
set.addRange ({0, 10});
set.addRange ({20, 30});
expectEquals (set.getNumRanges(), 3);
// Removing ranges not included in the set has no effect
set.removeRange ({-5, 5});
expectEquals (set.getNumRanges(), 3);
// Removing partially overlapping range
set.removeRange ({-15, 5});
expectEquals (set.getNumRanges(), 3);
expect (set.getRange (0) == Range<int> (-20, -15));
expect (set.getRange (1) == Range<int> (5, 10));
expect (set.getRange (2) == Range<int> (20, 30));
// Removing subrange of existing range
set.removeRange ({20, 22});
expectEquals (set.getNumRanges(), 3);
expect (set.getRange (2) == Range<int> (22, 30));
set.removeRange ({28, 30});
expectEquals (set.getNumRanges(), 3);
expect (set.getRange (2) == Range<int> (22, 28));
set.removeRange ({24, 26});
expectEquals (set.getNumRanges(), 4);
expect (set.getRange (0) == Range<int> (-20, -15));
expect (set.getRange (1) == Range<int> (5, 10));
expect (set.getRange (2) == Range<int> (22, 24));
expect (set.getRange (3) == Range<int> (26, 28));
}
beginTest ("XORing ranges");
{
SparseSet<int> set;
set.addRange ({0, 10});
set.invertRange ({0, 10});
expectEquals (set.getNumRanges(), 0);
set.invertRange ({0, 10});
expectEquals (set.getNumRanges(), 1);
set.invertRange ({4, 6});
expectEquals (set.getNumRanges(), 2);
expect (set.getRange (0) == Range<int> (0, 4));
expect (set.getRange (1) == Range<int> (6, 10));
set.invertRange ({-2, 2});
expectEquals (set.getNumRanges(), 3);
expect (set.getRange (0) == Range<int> (-2, 0));
expect (set.getRange (1) == Range<int> (2, 4));
expect (set.getRange (2) == Range<int> (6, 10));
}
beginTest ("range contains & overlaps checks");
{
SparseSet<int> set;
set.addRange ({0, 10});
expect (set.containsRange (Range<int> (0, 2)));
expect (set.containsRange (Range<int> (8, 10)));
expect (set.containsRange (Range<int> (0, 10)));
expect (! set.containsRange (Range<int> (-2, 0)));
expect (! set.containsRange (Range<int> (-2, 10)));
expect (! set.containsRange (Range<int> (10, 12)));
expect (! set.containsRange (Range<int> (0, 12)));
expect (set.overlapsRange (Range<int> (0, 2)));
expect (set.overlapsRange (Range<int> (8, 10)));
expect (set.overlapsRange (Range<int> (0, 10)));
expect (! set.overlapsRange (Range<int> (-2, 0)));
expect ( set.overlapsRange (Range<int> (-2, 10)));
expect (! set.overlapsRange (Range<int> (10, 12)));
expect ( set.overlapsRange (Range<int> (0, 12)));
}
}
};
static SparseSetTests sparseSetTests;
#endif
} // namespace juce

View File

@ -0,0 +1,268 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
/**
Holds a set of primitive values, storing them as a set of ranges.
This container acts like an array, but can efficiently hold large contiguous
ranges of values. It's quite a specialised class, mostly useful for things
like keeping the set of selected rows in a listbox.
The type used as a template parameter must be an integer type, such as int, short,
int64, etc.
@tags{Core}
*/
template <class Type>
class SparseSet
{
public:
//==============================================================================
SparseSet() = default;
SparseSet (const SparseSet&) = default;
SparseSet& operator= (const SparseSet&) = default;
SparseSet (SparseSet&& other) noexcept : ranges (std::move (other.ranges)) {}
SparseSet& operator= (SparseSet&& other) noexcept { ranges = std::move (other.ranges); return *this; }
//==============================================================================
/** Clears the set. */
void clear() { ranges.clear(); }
/** Checks whether the set is empty.
This is much quicker than using (size() == 0).
*/
bool isEmpty() const noexcept { return ranges.isEmpty(); }
/** Returns the number of values in the set.
Because of the way the data is stored, this method can take longer if there
are a lot of items in the set. Use isEmpty() for a quick test of whether there
are any items.
*/
Type size() const noexcept
{
Type total = {};
for (auto& r : ranges)
total += r.getLength();
return total;
}
/** Returns one of the values in the set.
@param index the index of the value to retrieve, in the range 0 to (size() - 1).
@returns the value at this index, or 0 if it's out-of-range
*/
Type operator[] (Type index) const noexcept
{
Type total = {};
for (auto& r : ranges)
{
auto end = total + r.getLength();
if (index < end)
return r.getStart() + (index - total);
total = end;
}
return {};
}
/** Checks whether a particular value is in the set. */
bool contains (Type valueToLookFor) const noexcept
{
for (auto& r : ranges)
{
if (r.getStart() > valueToLookFor)
break;
if (r.getEnd() > valueToLookFor)
return true;
}
return false;
}
//==============================================================================
/** Returns the number of contiguous blocks of values.
@see getRange
*/
int getNumRanges() const noexcept { return ranges.size(); }
/** Returns one of the contiguous ranges of values stored.
@param rangeIndex the index of the range to look up, between 0
and (getNumRanges() - 1)
@see getTotalRange
*/
Range<Type> getRange (int rangeIndex) const noexcept { return ranges[rangeIndex]; }
/** Returns the range between the lowest and highest values in the set.
@see getRange
*/
Range<Type> getTotalRange() const noexcept
{
if (ranges.isEmpty())
return {};
return { ranges.getFirst().getStart(),
ranges.getLast().getEnd() };
}
//==============================================================================
/** Adds a range of contiguous values to the set.
e.g. addRange (Range \<int\> (10, 14)) will add (10, 11, 12, 13) to the set.
*/
void addRange (Range<Type> range)
{
if (! range.isEmpty())
{
removeRange (range);
ranges.add (range);
std::sort (ranges.begin(), ranges.end(),
[] (Range<Type> a, Range<Type> b) { return a.getStart() < b.getStart(); });
simplify();
}
}
/** Removes a range of values from the set.
e.g. removeRange (Range\<int\> (10, 14)) will remove (10, 11, 12, 13) from the set.
*/
void removeRange (Range<Type> rangeToRemove)
{
if (getTotalRange().intersects (rangeToRemove) && ! rangeToRemove.isEmpty())
{
for (int i = ranges.size(); --i >= 0;)
{
auto& r = ranges.getReference(i);
if (r.getEnd() <= rangeToRemove.getStart())
break;
if (r.getStart() >= rangeToRemove.getEnd())
continue;
if (rangeToRemove.contains (r))
{
ranges.remove (i);
}
else if (r.contains (rangeToRemove))
{
auto r1 = r.withEnd (rangeToRemove.getStart());
auto r2 = r.withStart (rangeToRemove.getEnd());
// this should be covered in if (rangeToRemove.contains (r))
jassert (! r1.isEmpty() || ! r2.isEmpty());
r = r1;
if (r.isEmpty())
r = r2;
if (! r1.isEmpty() && ! r2.isEmpty())
ranges.insert (i + 1, r2);
}
else if (rangeToRemove.getEnd() > r.getEnd())
{
r.setEnd (rangeToRemove.getStart());
}
else
{
r.setStart (rangeToRemove.getEnd());
}
}
}
}
/** Does an XOR of the values in a given range. */
void invertRange (Range<Type> range)
{
SparseSet newItems;
newItems.addRange (range);
for (auto& r : ranges)
newItems.removeRange (r);
removeRange (range);
for (auto& r : newItems.ranges)
addRange (r);
}
/** Checks whether any part of a given range overlaps any part of this set. */
bool overlapsRange (Range<Type> range) const noexcept
{
if (! range.isEmpty())
for (auto& r : ranges)
if (r.intersects (range))
return true;
return false;
}
/** Checks whether the whole of a given range is contained within this one. */
bool containsRange (Range<Type> range) const noexcept
{
if (! range.isEmpty())
for (auto& r : ranges)
if (r.contains (range))
return true;
return false;
}
/** Returns the set as a list of ranges, which you may want to iterate over. */
const Array<Range<Type>>& getRanges() const noexcept { return ranges; }
//==============================================================================
bool operator== (const SparseSet& other) const noexcept { return ranges == other.ranges; }
bool operator!= (const SparseSet& other) const noexcept { return ranges != other.ranges; }
private:
//==============================================================================
Array<Range<Type>> ranges;
void simplify()
{
for (int i = ranges.size(); --i > 0;)
{
auto& r1 = ranges.getReference (i - 1);
auto& r2 = ranges.getReference (i);
if (r1.getEnd() == r2.getStart())
{
r1.setEnd (r2.getEnd());
ranges.remove (i);
}
}
}
};
} // namespace juce

View File

@ -0,0 +1,909 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
enum VariantStreamMarkers
{
varMarker_Int = 1,
varMarker_BoolTrue = 2,
varMarker_BoolFalse = 3,
varMarker_Double = 4,
varMarker_String = 5,
varMarker_Int64 = 6,
varMarker_Array = 7,
varMarker_Binary = 8,
varMarker_Undefined = 9
};
//==============================================================================
struct var::VariantType
{
struct VoidTag {};
struct UndefinedTag {};
struct IntTag {};
struct Int64Tag {};
struct DoubleTag {};
struct BoolTag {};
struct StringTag {};
struct ObjectTag {};
struct ArrayTag {};
struct BinaryTag {};
struct MethodTag {};
// members =====================================================================
bool isVoid = false;
bool isUndefined = false;
bool isInt = false;
bool isInt64 = false;
bool isBool = false;
bool isDouble = false;
bool isString = false;
bool isObject = false;
bool isArray = false;
bool isBinary = false;
bool isMethod = false;
bool isComparable = false;
int (*toInt) (const ValueUnion&) = defaultToInt;
int64 (*toInt64) (const ValueUnion&) = defaultToInt64;
double (*toDouble) (const ValueUnion&) = defaultToDouble;
String (*toString) (const ValueUnion&) = defaultToString;
bool (*toBool) (const ValueUnion&) = defaultToBool;
ReferenceCountedObject* (*toObject) (const ValueUnion&) = defaultToObject;
Array<var>* (*toArray) (const ValueUnion&) = defaultToArray;
MemoryBlock* (*toBinary) (const ValueUnion&) = defaultToBinary;
var (*clone) (const var&) = defaultClone;
void (*cleanUp) (ValueUnion&) = defaultCleanUp;
void (*createCopy) (ValueUnion&, const ValueUnion&) = defaultCreateCopy;
bool (*equals) (const ValueUnion&, const ValueUnion&, const VariantType&) = nullptr;
void (*writeToStream) (const ValueUnion&, OutputStream&) = nullptr;
// defaults ====================================================================
static int defaultToInt (const ValueUnion&) { return 0; }
static int64 defaultToInt64 (const ValueUnion&) { return 0; }
static double defaultToDouble (const ValueUnion&) { return 0; }
static String defaultToString (const ValueUnion&) { return {}; }
static bool defaultToBool (const ValueUnion&) { return false; }
static ReferenceCountedObject* defaultToObject (const ValueUnion&) { return nullptr; }
static Array<var>* defaultToArray (const ValueUnion&) { return nullptr; }
static MemoryBlock* defaultToBinary (const ValueUnion&) { return nullptr; }
static var defaultClone (const var& other) { return other; }
static void defaultCleanUp (ValueUnion&) {}
static void defaultCreateCopy (ValueUnion& dest, const ValueUnion& source) { dest = source; }
// void ========================================================================
static bool voidEquals (const ValueUnion&, const ValueUnion&, const VariantType& otherType) noexcept
{
return otherType.isVoid || otherType.isUndefined;
}
static void voidWriteToStream (const ValueUnion&, OutputStream& output)
{
output.writeCompressedInt (0);
}
constexpr explicit VariantType (VoidTag) noexcept
: isVoid (true),
isComparable (true),
equals (voidEquals),
writeToStream (voidWriteToStream) {}
// undefined ===================================================================
static String undefinedToString (const ValueUnion&) { return "undefined"; }
static bool undefinedEquals (const ValueUnion&, const ValueUnion&, const VariantType& otherType) noexcept
{
return otherType.isVoid || otherType.isUndefined;
}
static void undefinedWriteToStream (const ValueUnion&, OutputStream& output)
{
output.writeCompressedInt (1);
output.writeByte (varMarker_Undefined);
}
constexpr explicit VariantType (UndefinedTag) noexcept
: isUndefined (true),
toString (undefinedToString),
equals (undefinedEquals),
writeToStream (undefinedWriteToStream) {}
// int =========================================================================
static int intToInt (const ValueUnion& data) noexcept { return data.intValue; }
static int64 intToInt64 (const ValueUnion& data) noexcept { return (int64) data.intValue; }
static double intToDouble (const ValueUnion& data) noexcept { return (double) data.intValue; }
static String intToString (const ValueUnion& data) { return String (data.intValue); }
static bool intToBool (const ValueUnion& data) noexcept { return data.intValue != 0; }
static bool intEquals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) noexcept
{
if (otherType.isDouble || otherType.isInt64 || otherType.isString)
return otherType.equals (otherData, data, VariantType { IntTag{} });
return otherType.toInt (otherData) == data.intValue;
}
static void intWriteToStream (const ValueUnion& data, OutputStream& output)
{
output.writeCompressedInt (5);
output.writeByte (varMarker_Int);
output.writeInt (data.intValue);
}
constexpr explicit VariantType (IntTag) noexcept
: isInt (true),
isComparable (true),
toInt (intToInt),
toInt64 (intToInt64),
toDouble (intToDouble),
toString (intToString),
toBool (intToBool),
equals (intEquals),
writeToStream (intWriteToStream) {}
// int64 =======================================================================
static int int64ToInt (const ValueUnion& data) noexcept { return (int) data.int64Value; }
static int64 int64ToInt64 (const ValueUnion& data) noexcept { return data.int64Value; }
static double int64ToDouble (const ValueUnion& data) noexcept { return (double) data.int64Value; }
static String int64ToString (const ValueUnion& data) { return String (data.int64Value); }
static bool int64ToBool (const ValueUnion& data) noexcept { return data.int64Value != 0; }
static bool int64Equals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) noexcept
{
if (otherType.isDouble || otherType.isString)
return otherType.equals (otherData, data, VariantType { Int64Tag{} });
return otherType.toInt64 (otherData) == data.int64Value;
}
static void int64WriteToStream (const ValueUnion& data, OutputStream& output)
{
output.writeCompressedInt (9);
output.writeByte (varMarker_Int64);
output.writeInt64 (data.int64Value);
}
constexpr explicit VariantType (Int64Tag) noexcept
: isInt64 (true),
isComparable (true),
toInt (int64ToInt),
toInt64 (int64ToInt64),
toDouble (int64ToDouble),
toString (int64ToString),
toBool (int64ToBool),
equals (int64Equals),
writeToStream (int64WriteToStream) {}
// double ======================================================================
static int doubleToInt (const ValueUnion& data) noexcept { return (int) data.doubleValue; }
static int64 doubleToInt64 (const ValueUnion& data) noexcept { return (int64) data.doubleValue; }
static double doubleToDouble (const ValueUnion& data) noexcept { return data.doubleValue; }
static String doubleToString (const ValueUnion& data) { return serialiseDouble (data.doubleValue); }
static bool doubleToBool (const ValueUnion& data) noexcept { return data.doubleValue != 0.0; }
static bool doubleEquals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) noexcept
{
return std::abs (otherType.toDouble (otherData) - data.doubleValue) < std::numeric_limits<double>::epsilon();
}
static void doubleWriteToStream (const ValueUnion& data, OutputStream& output)
{
output.writeCompressedInt (9);
output.writeByte (varMarker_Double);
output.writeDouble (data.doubleValue);
}
constexpr explicit VariantType (DoubleTag) noexcept
: isDouble (true),
isComparable (true),
toInt (doubleToInt),
toInt64 (doubleToInt64),
toDouble (doubleToDouble),
toString (doubleToString),
toBool (doubleToBool),
equals (doubleEquals),
writeToStream (doubleWriteToStream) {}
// bool ========================================================================
static int boolToInt (const ValueUnion& data) noexcept { return data.boolValue ? 1 : 0; }
static int64 boolToInt64 (const ValueUnion& data) noexcept { return data.boolValue ? 1 : 0; }
static double boolToDouble (const ValueUnion& data) noexcept { return data.boolValue ? 1.0 : 0.0; }
static String boolToString (const ValueUnion& data) { return String::charToString (data.boolValue ? (juce_wchar) '1' : (juce_wchar) '0'); }
static bool boolToBool (const ValueUnion& data) noexcept { return data.boolValue; }
static bool boolEquals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) noexcept
{
return otherType.toBool (otherData) == data.boolValue;
}
static void boolWriteToStream (const ValueUnion& data, OutputStream& output)
{
output.writeCompressedInt (1);
output.writeByte (data.boolValue ? (char) varMarker_BoolTrue : (char) varMarker_BoolFalse);
}
constexpr explicit VariantType (BoolTag) noexcept
: isBool (true),
isComparable (true),
toInt (boolToInt),
toInt64 (boolToInt64),
toDouble (boolToDouble),
toString (boolToString),
toBool (boolToBool),
equals (boolEquals),
writeToStream (boolWriteToStream) {}
// string ======================================================================
static const String* getString (const ValueUnion& data) noexcept { return unalignedPointerCast<const String*> (data.stringValue); }
static String* getString ( ValueUnion& data) noexcept { return unalignedPointerCast<String*> (data.stringValue); }
static int stringToInt (const ValueUnion& data) noexcept { return getString (data)->getIntValue(); }
static int64 stringToInt64 (const ValueUnion& data) noexcept { return getString (data)->getLargeIntValue(); }
static double stringToDouble (const ValueUnion& data) noexcept { return getString (data)->getDoubleValue(); }
static String stringToString (const ValueUnion& data) { return *getString (data); }
static bool stringToBool (const ValueUnion& data) noexcept
{
return getString (data)->getIntValue() != 0
|| getString (data)->trim().equalsIgnoreCase ("true")
|| getString (data)->trim().equalsIgnoreCase ("yes");
}
static void stringCleanUp (ValueUnion& data) noexcept { getString (data)-> ~String(); }
static void stringCreateCopy (ValueUnion& dest, const ValueUnion& source) { new (dest.stringValue) String (*getString (source)); }
static bool stringEquals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) noexcept
{
return otherType.toString (otherData) == *getString (data);
}
static void stringWriteToStream (const ValueUnion& data, OutputStream& output)
{
auto* s = getString (data);
const size_t len = s->getNumBytesAsUTF8() + 1;
HeapBlock<char> temp (len);
s->copyToUTF8 (temp, len);
output.writeCompressedInt ((int) (len + 1));
output.writeByte (varMarker_String);
output.write (temp, len);
}
constexpr explicit VariantType (StringTag) noexcept
: isString (true),
isComparable (true),
toInt (stringToInt),
toInt64 (stringToInt64),
toDouble (stringToDouble),
toString (stringToString),
toBool (stringToBool),
cleanUp (stringCleanUp),
createCopy (stringCreateCopy),
equals (stringEquals),
writeToStream (stringWriteToStream) {}
// object ======================================================================
static String objectToString (const ValueUnion& data)
{
return "Object 0x" + String::toHexString ((int) (pointer_sized_int) data.objectValue);
}
static bool objectToBool (const ValueUnion& data) noexcept { return data.objectValue != nullptr; }
static ReferenceCountedObject* objectToObject (const ValueUnion& data) noexcept { return data.objectValue; }
static var objectClone (const var& original)
{
if (auto* d = original.getDynamicObject())
return d->clone().get();
jassertfalse; // can only clone DynamicObjects!
return {};
}
static void objectCleanUp (ValueUnion& data) noexcept { if (data.objectValue != nullptr) data.objectValue->decReferenceCount(); }
static void objectCreateCopy (ValueUnion& dest, const ValueUnion& source)
{
dest.objectValue = source.objectValue;
if (dest.objectValue != nullptr)
dest.objectValue->incReferenceCount();
}
static bool objectEquals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) noexcept
{
return otherType.toObject (otherData) == data.objectValue;
}
static void objectWriteToStream (const ValueUnion&, OutputStream& output)
{
jassertfalse; // Can't write an object to a stream!
output.writeCompressedInt (0);
}
constexpr explicit VariantType (ObjectTag) noexcept
: isObject (true),
toString (objectToString),
toBool (objectToBool),
toObject (objectToObject),
clone (objectClone),
cleanUp (objectCleanUp),
createCopy (objectCreateCopy),
equals (objectEquals),
writeToStream (objectWriteToStream) {}
// array =======================================================================
static String arrayToString (const ValueUnion&) { return "[Array]"; }
static ReferenceCountedObject* arrayToObject (const ValueUnion&) noexcept { return nullptr; }
static Array<var>* arrayToArray (const ValueUnion& data) noexcept
{
if (auto* a = dynamic_cast<RefCountedArray*> (data.objectValue))
return &(a->array);
return nullptr;
}
static bool arrayEquals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) noexcept
{
auto* thisArray = arrayToArray (data);
auto* otherArray = otherType.toArray (otherData);
return thisArray == otherArray || (thisArray != nullptr && otherArray != nullptr && *otherArray == *thisArray);
}
static var arrayClone (const var& original)
{
Array<var> arrayCopy;
if (auto* array = arrayToArray (original.value))
{
arrayCopy.ensureStorageAllocated (array->size());
for (auto& i : *array)
arrayCopy.add (i.clone());
}
return var (arrayCopy);
}
static void arrayWriteToStream (const ValueUnion& data, OutputStream& output)
{
if (auto* array = arrayToArray (data))
{
MemoryOutputStream buffer (512);
buffer.writeCompressedInt (array->size());
for (auto& i : *array)
i.writeToStream (buffer);
output.writeCompressedInt (1 + (int) buffer.getDataSize());
output.writeByte (varMarker_Array);
output << buffer;
}
}
struct RefCountedArray : public ReferenceCountedObject
{
RefCountedArray (const Array<var>& a) : array (a) { incReferenceCount(); }
RefCountedArray (Array<var>&& a) : array (std::move (a)) { incReferenceCount(); }
Array<var> array;
};
constexpr explicit VariantType (ArrayTag) noexcept
: isObject (true),
isArray (true),
toString (arrayToString),
toBool (objectToBool),
toObject (arrayToObject),
toArray (arrayToArray),
clone (arrayClone),
cleanUp (objectCleanUp),
createCopy (objectCreateCopy),
equals (arrayEquals),
writeToStream (arrayWriteToStream) {}
// binary ======================================================================
static void binaryCleanUp (ValueUnion& data) noexcept { delete data.binaryValue; }
static void binaryCreateCopy (ValueUnion& dest, const ValueUnion& source) { dest.binaryValue = new MemoryBlock (*source.binaryValue); }
static String binaryToString (const ValueUnion& data) { return data.binaryValue->toBase64Encoding(); }
static MemoryBlock* binaryToBinary (const ValueUnion& data) noexcept { return data.binaryValue; }
static bool binaryEquals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) noexcept
{
const MemoryBlock* const otherBlock = otherType.toBinary (otherData);
return otherBlock != nullptr && *otherBlock == *data.binaryValue;
}
static void binaryWriteToStream (const ValueUnion& data, OutputStream& output)
{
output.writeCompressedInt (1 + (int) data.binaryValue->getSize());
output.writeByte (varMarker_Binary);
output << *data.binaryValue;
}
constexpr explicit VariantType (BinaryTag) noexcept
: isBinary (true),
toString (binaryToString),
toBinary (binaryToBinary),
cleanUp (binaryCleanUp),
createCopy (binaryCreateCopy),
equals (binaryEquals),
writeToStream (binaryWriteToStream) {}
// method ======================================================================
static void methodCleanUp (ValueUnion& data) noexcept { if (data.methodValue != nullptr ) delete data.methodValue; }
static void methodCreateCopy (ValueUnion& dest, const ValueUnion& source) { dest.methodValue = new NativeFunction (*source.methodValue); }
static String methodToString (const ValueUnion&) { return "Method"; }
static bool methodToBool (const ValueUnion& data) noexcept { return data.methodValue != nullptr; }
static bool methodEquals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) noexcept
{
return otherType.isMethod && otherData.methodValue == data.methodValue;
}
static void methodWriteToStream (const ValueUnion&, OutputStream& output)
{
jassertfalse; // Can't write a method to a stream!
output.writeCompressedInt (0);
}
constexpr explicit VariantType (MethodTag) noexcept
: isMethod (true),
toString (methodToString),
toBool (methodToBool),
cleanUp (methodCleanUp),
createCopy (methodCreateCopy),
equals (methodEquals),
writeToStream (methodWriteToStream) {}
};
struct var::Instance
{
static constexpr VariantType attributesVoid { VariantType::VoidTag{} };
static constexpr VariantType attributesUndefined { VariantType::UndefinedTag{} };
static constexpr VariantType attributesInt { VariantType::IntTag{} };
static constexpr VariantType attributesInt64 { VariantType::Int64Tag{} };
static constexpr VariantType attributesBool { VariantType::BoolTag{} };
static constexpr VariantType attributesDouble { VariantType::DoubleTag{} };
static constexpr VariantType attributesMethod { VariantType::MethodTag{} };
static constexpr VariantType attributesArray { VariantType::ArrayTag{} };
static constexpr VariantType attributesString { VariantType::StringTag{} };
static constexpr VariantType attributesBinary { VariantType::BinaryTag{} };
static constexpr VariantType attributesObject { VariantType::ObjectTag{} };
};
constexpr var::VariantType var::Instance::attributesVoid;
constexpr var::VariantType var::Instance::attributesUndefined;
constexpr var::VariantType var::Instance::attributesInt;
constexpr var::VariantType var::Instance::attributesInt64;
constexpr var::VariantType var::Instance::attributesBool;
constexpr var::VariantType var::Instance::attributesDouble;
constexpr var::VariantType var::Instance::attributesMethod;
constexpr var::VariantType var::Instance::attributesArray;
constexpr var::VariantType var::Instance::attributesString;
constexpr var::VariantType var::Instance::attributesBinary;
constexpr var::VariantType var::Instance::attributesObject;
//==============================================================================
var::var() noexcept : type (&Instance::attributesVoid) {}
var::var (const VariantType& t) noexcept : type (&t) {}
var::~var() noexcept { type->cleanUp (value); }
//==============================================================================
var::var (const var& valueToCopy) : type (valueToCopy.type)
{
type->createCopy (value, valueToCopy.value);
}
var::var (const int v) noexcept : type (&Instance::attributesInt) { value.intValue = v; }
var::var (const int64 v) noexcept : type (&Instance::attributesInt64) { value.int64Value = v; }
var::var (const bool v) noexcept : type (&Instance::attributesBool) { value.boolValue = v; }
var::var (const double v) noexcept : type (&Instance::attributesDouble) { value.doubleValue = v; }
var::var (NativeFunction m) noexcept : type (&Instance::attributesMethod) { value.methodValue = new NativeFunction (m); }
var::var (const Array<var>& v) : type (&Instance::attributesArray) { value.objectValue = new VariantType::RefCountedArray (v); }
var::var (const String& v) : type (&Instance::attributesString) { new (value.stringValue) String (v); }
var::var (const char* const v) : type (&Instance::attributesString) { new (value.stringValue) String (v); }
var::var (const wchar_t* const v) : type (&Instance::attributesString) { new (value.stringValue) String (v); }
var::var (const void* v, size_t sz) : type (&Instance::attributesBinary) { value.binaryValue = new MemoryBlock (v, sz); }
var::var (const MemoryBlock& v) : type (&Instance::attributesBinary) { value.binaryValue = new MemoryBlock (v); }
var::var (const StringArray& v) : type (&Instance::attributesArray)
{
Array<var> strings;
strings.ensureStorageAllocated (v.size());
for (auto& i : v)
strings.add (var (i));
value.objectValue = new VariantType::RefCountedArray (strings);
}
var::var (ReferenceCountedObject* const object) : type (&Instance::attributesObject)
{
value.objectValue = object;
if (object != nullptr)
object->incReferenceCount();
}
var var::undefined() noexcept { return var (Instance::attributesUndefined); }
//==============================================================================
bool var::isVoid() const noexcept { return type->isVoid; }
bool var::isUndefined() const noexcept { return type->isUndefined; }
bool var::isInt() const noexcept { return type->isInt; }
bool var::isInt64() const noexcept { return type->isInt64; }
bool var::isBool() const noexcept { return type->isBool; }
bool var::isDouble() const noexcept { return type->isDouble; }
bool var::isString() const noexcept { return type->isString; }
bool var::isObject() const noexcept { return type->isObject; }
bool var::isArray() const noexcept { return type->isArray; }
bool var::isBinaryData() const noexcept { return type->isBinary; }
bool var::isMethod() const noexcept { return type->isMethod; }
var::operator int() const noexcept { return type->toInt (value); }
var::operator int64() const noexcept { return type->toInt64 (value); }
var::operator bool() const noexcept { return type->toBool (value); }
var::operator float() const noexcept { return (float) type->toDouble (value); }
var::operator double() const noexcept { return type->toDouble (value); }
String var::toString() const { return type->toString (value); }
var::operator String() const { return type->toString (value); }
ReferenceCountedObject* var::getObject() const noexcept { return type->toObject (value); }
Array<var>* var::getArray() const noexcept { return type->toArray (value); }
MemoryBlock* var::getBinaryData() const noexcept { return type->toBinary (value); }
DynamicObject* var::getDynamicObject() const noexcept { return dynamic_cast<DynamicObject*> (getObject()); }
//==============================================================================
void var::swapWith (var& other) noexcept
{
std::swap (type, other.type);
std::swap (value, other.value);
}
var& var::operator= (const var& v) { type->cleanUp (value); type = v.type; type->createCopy (value, v.value); return *this; }
var& var::operator= (const int v) { type->cleanUp (value); type = &Instance::attributesInt; value.intValue = v; return *this; }
var& var::operator= (const int64 v) { type->cleanUp (value); type = &Instance::attributesInt64; value.int64Value = v; return *this; }
var& var::operator= (const bool v) { type->cleanUp (value); type = &Instance::attributesBool; value.boolValue = v; return *this; }
var& var::operator= (const double v) { type->cleanUp (value); type = &Instance::attributesDouble; value.doubleValue = v; return *this; }
var& var::operator= (const char* const v) { type->cleanUp (value); type = &Instance::attributesString; new (value.stringValue) String (v); return *this; }
var& var::operator= (const wchar_t* const v) { type->cleanUp (value); type = &Instance::attributesString; new (value.stringValue) String (v); return *this; }
var& var::operator= (const String& v) { type->cleanUp (value); type = &Instance::attributesString; new (value.stringValue) String (v); return *this; }
var& var::operator= (const MemoryBlock& v) { type->cleanUp (value); type = &Instance::attributesBinary; value.binaryValue = new MemoryBlock (v); return *this; }
var& var::operator= (const Array<var>& v) { var v2 (v); swapWith (v2); return *this; }
var& var::operator= (ReferenceCountedObject* v) { var v2 (v); swapWith (v2); return *this; }
var& var::operator= (NativeFunction v) { var v2 (v); swapWith (v2); return *this; }
var::var (var&& other) noexcept
: type (other.type),
value (other.value)
{
other.type = &Instance::attributesVoid;
}
var& var::operator= (var&& other) noexcept
{
swapWith (other);
return *this;
}
var::var (String&& v) : type (&Instance::attributesString)
{
new (value.stringValue) String (std::move (v));
}
var::var (MemoryBlock&& v) : type (&Instance::attributesBinary)
{
value.binaryValue = new MemoryBlock (std::move (v));
}
var::var (Array<var>&& v) : type (&Instance::attributesArray)
{
value.objectValue = new VariantType::RefCountedArray (std::move (v));
}
var& var::operator= (String&& v)
{
type->cleanUp (value);
type = &Instance::attributesString;
new (value.stringValue) String (std::move (v));
return *this;
}
//==============================================================================
bool var::equals (const var& other) const noexcept
{
return type->equals (value, other.value, *other.type);
}
bool var::equalsWithSameType (const var& other) const noexcept
{
return hasSameTypeAs (other) && equals (other);
}
bool var::hasSameTypeAs (const var& other) const noexcept
{
return type == other.type;
}
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
{
return type->clone (*this);
}
//==============================================================================
const var& var::operator[] (const Identifier& propertyName) const
{
if (auto* o = getDynamicObject())
return o->getProperty (propertyName);
return getNullVarRef();
}
const var& var::operator[] (const char* const propertyName) const
{
return operator[] (Identifier (propertyName));
}
var var::getProperty (const Identifier& propertyName, const var& defaultReturnValue) const
{
if (auto* o = getDynamicObject())
return o->getProperties().getWithDefault (propertyName, defaultReturnValue);
return defaultReturnValue;
}
bool var::hasProperty (const Identifier& propertyName) const noexcept
{
if (auto* o = getDynamicObject())
return o->hasProperty (propertyName);
return false;
}
var::NativeFunction var::getNativeFunction() const
{
return isMethod() && (value.methodValue != nullptr) ? *value.methodValue : nullptr;
}
var var::invoke (const Identifier& method, const var* arguments, int numArguments) const
{
if (auto* o = getDynamicObject())
return o->invokeMethod (method, var::NativeFunctionArgs (*this, arguments, numArguments));
return {};
}
var var::call (const Identifier& method) const
{
return invoke (method, nullptr, 0);
}
var var::call (const Identifier& method, const var& arg1) const
{
return invoke (method, &arg1, 1);
}
var var::call (const Identifier& method, const var& arg1, const var& arg2) const
{
var args[] = { arg1, arg2 };
return invoke (method, args, 2);
}
var var::call (const Identifier& method, const var& arg1, const var& arg2, const var& arg3)
{
var args[] = { arg1, arg2, arg3 };
return invoke (method, args, 3);
}
var var::call (const Identifier& method, const var& arg1, const var& arg2, const var& arg3, const var& arg4) const
{
var args[] = { arg1, arg2, arg3, arg4 };
return invoke (method, args, 4);
}
var var::call (const Identifier& method, const var& arg1, const var& arg2, const var& arg3, const var& arg4, const var& arg5) const
{
var args[] = { arg1, arg2, arg3, arg4, arg5 };
return invoke (method, args, 5);
}
//==============================================================================
int var::size() const
{
if (auto array = getArray())
return array->size();
return 0;
}
const var& var::operator[] (int arrayIndex) const
{
auto array = getArray();
// When using this method, the var must actually be an array, and the index
// must be in-range!
jassert (array != nullptr && isPositiveAndBelow (arrayIndex, array->size()));
return array->getReference (arrayIndex);
}
var& var::operator[] (int arrayIndex)
{
auto array = getArray();
// When using this method, the var must actually be an array, and the index
// must be in-range!
jassert (array != nullptr && isPositiveAndBelow (arrayIndex, array->size()));
return array->getReference (arrayIndex);
}
Array<var>* var::convertToArray()
{
if (auto array = getArray())
return array;
Array<var> tempVar;
if (! isVoid())
tempVar.add (*this);
*this = tempVar;
return getArray();
}
void var::append (const var& n)
{
convertToArray()->add (n);
}
void var::remove (const int index)
{
if (auto array = getArray())
array->remove (index);
}
void var::insert (const int index, const var& n)
{
convertToArray()->insert (index, n);
}
void var::resize (const int numArrayElementsWanted)
{
convertToArray()->resize (numArrayElementsWanted);
}
int var::indexOf (const var& n) const
{
if (auto array = getArray())
return array->indexOf (n);
return -1;
}
//==============================================================================
void var::writeToStream (OutputStream& output) const
{
type->writeToStream (value, output);
}
var var::readFromStream (InputStream& input)
{
const int numBytes = input.readCompressedInt();
if (numBytes > 0)
{
switch (input.readByte())
{
case varMarker_Int: return var (input.readInt());
case varMarker_Int64: return var (input.readInt64());
case varMarker_BoolTrue: return var (true);
case varMarker_BoolFalse: return var (false);
case varMarker_Double: return var (input.readDouble());
case varMarker_String:
{
MemoryOutputStream mo;
mo.writeFromInputStream (input, numBytes - 1);
return var (mo.toUTF8());
}
case varMarker_Binary:
{
MemoryBlock mb ((size_t) numBytes - 1);
if (numBytes > 1)
{
const int numRead = input.read (mb.getData(), numBytes - 1);
mb.setSize ((size_t) numRead);
}
return var (mb);
}
case varMarker_Array:
{
var v;
auto* destArray = v.convertToArray();
for (int i = input.readCompressedInt(); --i >= 0;)
destArray->add (readFromStream (input));
return v;
}
default:
input.skipNextBytes (numBytes - 1); break;
}
}
return {};
}
var::NativeFunctionArgs::NativeFunctionArgs (const var& t, const var* args, int numArgs) noexcept
: thisObject (t), arguments (args), numArguments (numArgs)
{
}
//==============================================================================
#if JUCE_ALLOW_STATIC_NULL_VARIABLES
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations")
JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4996)
const var var::null;
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
JUCE_END_IGNORE_WARNINGS_MSVC
#endif
} // namespace juce

View File

@ -0,0 +1,354 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
/**
A variant class, that can be used to hold a range of primitive values.
A var object can hold a range of simple primitive values, strings, or
any kind of ReferenceCountedObject. The var class is intended to act like
the kind of values used in dynamic scripting languages.
You can save/load var objects either in a small, proprietary binary format
using writeToStream()/readFromStream(), or as JSON by using the JSON class.
@see JSON, DynamicObject
@tags{Core}
*/
class JUCE_API var
{
public:
//==============================================================================
/** This structure is passed to a NativeFunction callback, and contains invocation
details about the function's arguments and context.
*/
struct JUCE_API NativeFunctionArgs
{
NativeFunctionArgs (const var& thisObject, const var* args, int numArgs) noexcept;
const var& thisObject;
const var* arguments;
int numArguments;
};
using NativeFunction = std::function<var (const NativeFunctionArgs&)>;
//==============================================================================
/** Creates a void variant. */
var() noexcept;
/** Destructor. */
~var() noexcept;
var (const var& valueToCopy);
var (int value) noexcept;
var (int64 value) noexcept;
var (bool value) noexcept;
var (double value) noexcept;
var (const char* value);
var (const wchar_t* value);
var (const String& value);
var (const Array<var>& value);
var (const StringArray& value);
var (ReferenceCountedObject* object);
var (NativeFunction method) noexcept;
var (const void* binaryData, size_t dataSize);
var (const MemoryBlock& binaryData);
var& operator= (const var& valueToCopy);
var& operator= (int value);
var& operator= (int64 value);
var& operator= (bool value);
var& operator= (double value);
var& operator= (const char* value);
var& operator= (const wchar_t* value);
var& operator= (const String& value);
var& operator= (const MemoryBlock& value);
var& operator= (const Array<var>& value);
var& operator= (ReferenceCountedObject* object);
var& operator= (NativeFunction method);
var (var&&) noexcept;
var (String&&);
var (MemoryBlock&&);
var (Array<var>&&);
var& operator= (var&&) noexcept;
var& operator= (String&&);
void swapWith (var& other) noexcept;
/** Returns a var object that can be used where you need the javascript "undefined" value. */
static var undefined() noexcept;
//==============================================================================
operator int() const noexcept;
operator int64() const noexcept;
operator bool() const noexcept;
operator float() const noexcept;
operator double() const noexcept;
operator String() const;
String toString() const;
/** If this variant holds an array, this provides access to it.
NOTE: Beware when you use this - the array pointer is only valid for the lifetime
of the variant that returned it, so be very careful not to call this method on temporary
var objects that are the return-value of a function, and which may go out of scope before
you use the array!
*/
Array<var>* getArray() const noexcept;
/** If this variant holds a memory block, this provides access to it.
NOTE: Beware when you use this - the MemoryBlock pointer is only valid for the lifetime
of the variant that returned it, so be very careful not to call this method on temporary
var objects that are the return-value of a function, and which may go out of scope before
you use the MemoryBlock!
*/
MemoryBlock* getBinaryData() const noexcept;
ReferenceCountedObject* getObject() const noexcept;
DynamicObject* getDynamicObject() const noexcept;
//==============================================================================
bool isVoid() const noexcept;
bool isUndefined() const noexcept;
bool isInt() const noexcept;
bool isInt64() const noexcept;
bool isBool() const noexcept;
bool isDouble() const noexcept;
bool isString() const noexcept;
bool isObject() const noexcept;
bool isArray() const noexcept;
bool isBinaryData() const noexcept;
bool isMethod() const noexcept;
/** Returns true if this var has the same value as the one supplied.
Note that this ignores the type, so a string var "123" and an integer var with the
value 123 are considered to be equal.
@see equalsWithSameType
*/
bool equals (const var& other) const noexcept;
/** Returns true if this var has the same value and type as the one supplied.
This differs from equals() because e.g. "123" and 123 will be considered different.
@see equals
*/
bool equalsWithSameType (const var& other) const noexcept;
/** Returns true if this var has the same type as the one supplied. */
bool hasSameTypeAs (const var& other) const noexcept;
/** Returns a deep copy of this object.
For simple types this just returns a copy, but if the object contains any arrays
or DynamicObjects, they will be cloned (recursively).
*/
var clone() const noexcept;
//==============================================================================
/** If the var is an array, this returns the number of elements.
If the var isn't actually an array, this will return 0.
*/
int size() const;
/** If the var is an array, this can be used to return one of its elements.
To call this method, you must make sure that the var is actually an array, and
that the index is a valid number. If these conditions aren't met, behaviour is
undefined.
For more control over the array's contents, you can call getArray() and manipulate
it directly as an Array\<var\>.
*/
const var& operator[] (int arrayIndex) const;
/** If the var is an array, this can be used to return one of its elements.
To call this method, you must make sure that the var is actually an array, and
that the index is a valid number. If these conditions aren't met, behaviour is
undefined.
For more control over the array's contents, you can call getArray() and manipulate
it directly as an Array\<var\>.
*/
var& operator[] (int arrayIndex);
/** Appends an element to the var, converting it to an array if it isn't already one.
If the var isn't an array, it will be converted to one, and if its value was non-void,
this value will be kept as the first element of the new array. The parameter value
will then be appended to it.
For more control over the array's contents, you can call getArray() and manipulate
it directly as an Array\<var\>.
*/
void append (const var& valueToAppend);
/** Inserts an element to the var, converting it to an array if it isn't already one.
If the var isn't an array, it will be converted to one, and if its value was non-void,
this value will be kept as the first element of the new array. The parameter value
will then be inserted into it.
For more control over the array's contents, you can call getArray() and manipulate
it directly as an Array\<var\>.
*/
void insert (int index, const var& value);
/** If the var is an array, this removes one of its elements.
If the index is out-of-range or the var isn't an array, nothing will be done.
For more control over the array's contents, you can call getArray() and manipulate
it directly as an Array\<var\>.
*/
void remove (int index);
/** Treating the var as an array, this resizes it to contain the specified number of elements.
If the var isn't an array, it will be converted to one, and if its value was non-void,
this value will be kept as the first element of the new array before resizing.
For more control over the array's contents, you can call getArray() and manipulate
it directly as an Array\<var\>.
*/
void resize (int numArrayElementsWanted);
/** If the var is an array, this searches it for the first occurrence of the specified value,
and returns its index.
If the var isn't an array, or if the value isn't found, this returns -1.
*/
int indexOf (const var& value) const;
//==============================================================================
/** If this variant is an object, this returns one of its properties. */
const var& operator[] (const Identifier& propertyName) const;
/** If this variant is an object, this returns one of its properties. */
const var& operator[] (const char* propertyName) const;
/** If this variant is an object, this returns one of its properties, or a default
fallback value if the property is not set. */
var getProperty (const Identifier& propertyName, const var& defaultReturnValue) const;
/** Returns true if this variant is an object and if it has the given property. */
bool hasProperty (const Identifier& propertyName) const noexcept;
/** Invokes a named method call with no arguments. */
var call (const Identifier& method) const;
/** Invokes a named method call with one argument. */
var call (const Identifier& method, const var& arg1) const;
/** Invokes a named method call with 2 arguments. */
var call (const Identifier& method, const var& arg1, const var& arg2) const;
/** Invokes a named method call with 3 arguments. */
var call (const Identifier& method, const var& arg1, const var& arg2, const var& arg3);
/** Invokes a named method call with 4 arguments. */
var call (const Identifier& method, const var& arg1, const var& arg2, const var& arg3, const var& arg4) const;
/** Invokes a named method call with 5 arguments. */
var call (const Identifier& method, const var& arg1, const var& arg2, const var& arg3, const var& arg4, const var& arg5) const;
/** Invokes a named method call with a list of arguments. */
var invoke (const Identifier& method, const var* arguments, int numArguments) const;
/** If this object is a method, this returns the function pointer. */
NativeFunction getNativeFunction() const;
//==============================================================================
/** Writes a binary representation of this value to a stream.
The data can be read back later using readFromStream().
@see JSON
*/
void writeToStream (OutputStream& output) const;
/** Reads back a stored binary representation of a value.
The data in the stream must have been written using writeToStream(), or this
will have unpredictable results.
@see JSON
*/
static var readFromStream (InputStream& input);
//==============================================================================
#if JUCE_ALLOW_STATIC_NULL_VARIABLES && ! defined (DOXYGEN)
[[deprecated ("This was a static empty var object, but is now deprecated as it's too easy to accidentally "
"use it indirectly during a static constructor leading to hard-to-find order-of-initialisation "
"problems. Use var() or {} instead. For returning an empty var from a function by reference, "
"use a function-local static var and return that.")]]
static const var null;
#endif
private:
//==============================================================================
struct VariantType;
struct Instance;
union ValueUnion
{
int intValue;
int64 int64Value;
bool boolValue;
double doubleValue;
char stringValue[sizeof (String)];
ReferenceCountedObject* objectValue;
MemoryBlock* binaryValue;
NativeFunction* methodValue;
};
friend bool canCompare (const var&, const var&);
const VariantType* type;
ValueUnion value;
Array<var>* convertToArray();
var (const VariantType&) noexcept;
// This is needed to prevent the wrong constructor/operator being called
var (const ReferenceCountedObject*) = delete;
var& operator= (const ReferenceCountedObject*) = delete;
var (const void*) = delete;
var& operator= (const void*) = delete;
};
/** Compares the values of two var objects, using the var::equals() comparison. */
JUCE_API bool operator== (const var&, const var&);
/** Compares the values of two var objects, using the var::equals() comparison. */
JUCE_API bool operator!= (const var&, const var&);
/** Compares the values of two var objects, using the var::equals() comparison. */
JUCE_API bool operator< (const var&, const var&);
/** Compares the values of two var objects, using the var::equals() comparison. */
JUCE_API bool operator<= (const var&, const var&);
/** Compares the values of two var objects, using the var::equals() comparison. */
JUCE_API bool operator> (const var&, const var&);
/** Compares the values of two var objects, using the var::equals() comparison. */
JUCE_API bool operator>= (const var&, const var&);
JUCE_API bool operator== (const var&, const String&);
JUCE_API bool operator!= (const var&, const String&);
JUCE_API bool operator== (const var&, const char*);
JUCE_API bool operator!= (const var&, const char*);
//==============================================================================
/** This template-overloaded class can be used to convert between var and custom types.
@tags{Core}
*/
template <typename Type>
struct VariantConverter
{
static Type fromVar (const var& v) { return static_cast<Type> (v); }
static var toVar (const Type& t) { return t; }
};
#ifndef DOXYGEN
template <>
struct VariantConverter<String>
{
static String fromVar (const var& v) { return v.toString(); }
static var toVar (const String& s) { return s; }
};
#endif
} // namespace juce

View File

@ -0,0 +1,150 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
StringArray DirectoryIterator::parseWildcards (const String& pattern)
{
StringArray s;
s.addTokens (pattern, ";,", "\"'");
s.trim();
s.removeEmptyStrings();
return s;
}
bool DirectoryIterator::fileMatches (const StringArray& wildcards, const String& filename)
{
for (auto& w : wildcards)
if (filename.matchesWildcard (w, ! File::areFileNamesCaseSensitive()))
return true;
return false;
}
bool DirectoryIterator::next()
{
return next (nullptr, nullptr, nullptr, nullptr, nullptr, nullptr);
}
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations")
JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4996)
bool DirectoryIterator::next (bool* isDirResult, bool* isHiddenResult, int64* fileSize,
Time* modTime, Time* creationTime, bool* isReadOnly)
{
for (;;)
{
hasBeenAdvanced = true;
if (subIterator != nullptr)
{
if (subIterator->next (isDirResult, isHiddenResult, fileSize, modTime, creationTime, isReadOnly))
return true;
subIterator.reset();
}
String filename;
bool isDirectory, isHidden = false, shouldContinue = false;
while (fileFinder.next (filename, &isDirectory,
(isHiddenResult != nullptr || (whatToLookFor & File::ignoreHiddenFiles) != 0) ? &isHidden : nullptr,
fileSize, modTime, creationTime, isReadOnly))
{
++index;
if (! filename.containsOnly ("."))
{
bool matches = false;
if (isDirectory)
{
if (isRecursive && ((whatToLookFor & File::ignoreHiddenFiles) == 0 || ! isHidden))
subIterator.reset (new DirectoryIterator (File::createFileWithoutCheckingPath (path + filename),
true, wildCard, whatToLookFor));
matches = (whatToLookFor & File::findDirectories) != 0;
}
else
{
matches = (whatToLookFor & File::findFiles) != 0;
}
// if we're not relying on the OS iterator to do the wildcard match, do it now..
if (matches && (isRecursive || wildCards.size() > 1))
matches = fileMatches (wildCards, filename);
if (matches && (whatToLookFor & File::ignoreHiddenFiles) != 0)
matches = ! isHidden;
if (matches)
{
currentFile = File::createFileWithoutCheckingPath (path + filename);
if (isHiddenResult != nullptr) *isHiddenResult = isHidden;
if (isDirResult != nullptr) *isDirResult = isDirectory;
return true;
}
if (subIterator != nullptr)
{
shouldContinue = true;
break;
}
}
}
if (! shouldContinue)
return false;
}
}
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
JUCE_END_IGNORE_WARNINGS_MSVC
const File& DirectoryIterator::getFile() const
{
if (subIterator != nullptr && subIterator->hasBeenAdvanced)
return subIterator->getFile();
// You need to call DirectoryIterator::next() before asking it for the file that it found!
jassert (hasBeenAdvanced);
return currentFile;
}
float DirectoryIterator::getEstimatedProgress() const
{
if (totalNumFiles < 0)
totalNumFiles = File (path).getNumberOfChildFiles (File::findFilesAndDirectories);
if (totalNumFiles <= 0)
return 0.0f;
auto detailedIndex = (subIterator != nullptr) ? (float) index + subIterator->getEstimatedProgress()
: (float) index;
return jlimit (0.0f, 1.0f, detailedIndex / (float) totalNumFiles);
}
} // namespace juce

View File

@ -0,0 +1,164 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
#ifndef DOXYGEN
//==============================================================================
/**
This class is now deprecated in favour of RangedDirectoryIterator.
Searches through the files in a directory, returning each file that is found.
A DirectoryIterator will search through a directory and its subdirectories using
a wildcard filepattern match.
If you may be scanning a large number of files, it's usually smarter to use this
class than File::findChildFiles() because it allows you to stop at any time, rather
than having to wait for the entire scan to finish before getting the results.
Please note that the order in which files are returned is completely undefined!
They'll arrive in whatever order the underlying OS calls provide them, which will
depend on the filesystem and other factors. If you need a sorted list, you'll need
to manually sort them using your preferred comparator after collecting the list.
It also provides an estimate of its progress, using a (highly inaccurate!) algorithm.
@tags{Core}
@see RangedDirectoryIterator
*/
class JUCE_API DirectoryIterator final
{
public:
//==============================================================================
/** Creates a DirectoryIterator for a given directory.
After creating one of these, call its next() method to get the
first file - e.g. @code
DirectoryIterator iter (File ("/animals/mooses"), true, "*.moose");
while (iter.next())
{
File theFileItFound (iter.getFile());
... etc
}
@endcode
@see RangedDirectoryIterator
*/
[[deprecated ("This class is now deprecated in favour of RangedDirectoryIterator.")]]
DirectoryIterator (const File& directory,
bool recursive,
const String& pattern = "*",
int type = File::findFiles)
: wildCards (parseWildcards (pattern)),
fileFinder (directory, (recursive || wildCards.size() > 1) ? "*" : pattern),
wildCard (pattern),
path (File::addTrailingSeparator (directory.getFullPathName())),
whatToLookFor (type),
isRecursive (recursive)
{
// you have to specify the type of files you're looking for!
jassert ((whatToLookFor & (File::findFiles | File::findDirectories)) != 0);
jassert (whatToLookFor > 0 && whatToLookFor <= 7);
}
/** Moves the iterator along to the next file.
@returns true if a file was found (you can then use getFile() to see what it was) - or
false if there are no more matching files.
*/
bool next();
/** Moves the iterator along to the next file, and returns various properties of that file.
If you need to find out details about the file, it's more efficient to call this method than
to call the normal next() method and then find out the details afterwards.
All the parameters are optional, so pass null pointers for any items that you're not
interested in.
@returns true if a file was found (you can then use getFile() to see what it was) - or
false if there are no more matching files. If it returns false, then none of the
parameters will be filled-in.
*/
bool next (bool* isDirectory,
bool* isHidden,
int64* fileSize,
Time* modTime,
Time* creationTime,
bool* isReadOnly);
/** Returns the file that the iterator is currently pointing at.
The result of this call is only valid after a call to next() has returned true.
*/
const File& getFile() const;
/** Returns a guess of how far through the search the iterator has got.
@returns a value 0.0 to 1.0 to show the progress, although this won't be
very accurate.
*/
float getEstimatedProgress() const;
private:
//==============================================================================
struct NativeIterator
{
NativeIterator (const File& directory, const String& wildCard);
~NativeIterator();
bool next (String& filenameFound,
bool* isDirectory, bool* isHidden, int64* fileSize,
Time* modTime, Time* creationTime, bool* isReadOnly);
class Pimpl;
std::unique_ptr<Pimpl> pimpl;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (NativeIterator)
};
StringArray wildCards;
NativeIterator fileFinder;
String wildCard, path;
int index = -1;
mutable int totalNumFiles = -1;
const int whatToLookFor;
const bool isRecursive;
bool hasBeenAdvanced = false;
std::unique_ptr<DirectoryIterator> subIterator;
File currentFile;
static StringArray parseWildcards (const String& pattern);
static bool fileMatches (const StringArray& wildCards, const String& filename);
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DirectoryIterator)
};
#endif
} // namespace juce

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,40 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
FileFilter::FileFilter (const String& filterDescription)
: description (filterDescription)
{
}
FileFilter::~FileFilter()
{
}
const String& FileFilter::getDescription() const noexcept
{
return description;
}
} // namespace juce

View File

@ -0,0 +1,71 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
/**
Interface for deciding which files are suitable for something.
For example, this is used by DirectoryContentsList to select which files
go into the list.
@see WildcardFileFilter, DirectoryContentsList, FileListComponent, FileBrowserComponent
@tags{Core}
*/
class JUCE_API FileFilter
{
public:
//==============================================================================
/** Creates a filter with the given description.
The description can be returned later with the getDescription() method.
*/
FileFilter (const String& filterDescription);
/** Destructor. */
virtual ~FileFilter();
//==============================================================================
/** Returns the description that the filter was created with. */
const String& getDescription() const noexcept;
//==============================================================================
/** Should return true if this file is suitable for inclusion in whatever context
the object is being used.
*/
virtual bool isFileSuitable (const File& file) const = 0;
/** Should return true if this directory is suitable for inclusion in whatever context
the object is being used.
*/
virtual bool isDirectorySuitable (const File& file) const = 0;
protected:
//==============================================================================
String description;
};
} // namespace juce

View File

@ -0,0 +1,177 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
int64 juce_fileSetPosition (void* handle, int64 pos);
//==============================================================================
FileInputStream::FileInputStream (const File& f) : file (f)
{
openHandle();
}
int64 FileInputStream::getTotalLength()
{
// You should always check that a stream opened successfully before using it!
jassert (openedOk());
return file.getSize();
}
int FileInputStream::read (void* buffer, int bytesToRead)
{
// You should always check that a stream opened successfully before using it!
jassert (openedOk());
// The buffer should never be null, and a negative size is probably a
// sign that something is broken!
jassert (buffer != nullptr && bytesToRead >= 0);
auto num = readInternal (buffer, (size_t) bytesToRead);
currentPosition += (int64) num;
return (int) num;
}
bool FileInputStream::isExhausted()
{
return currentPosition >= getTotalLength();
}
int64 FileInputStream::getPosition()
{
return currentPosition;
}
bool FileInputStream::setPosition (int64 pos)
{
// You should always check that a stream opened successfully before using it!
jassert (openedOk());
if (pos != currentPosition)
currentPosition = juce_fileSetPosition (fileHandle, pos);
return currentPosition == pos;
}
//==============================================================================
//==============================================================================
#if JUCE_UNIT_TESTS
struct FileInputStreamTests : public UnitTest
{
FileInputStreamTests()
: UnitTest ("FileInputStream", UnitTestCategories::streams)
{}
void runTest() override
{
beginTest ("Open stream non-existent file");
{
auto tempFile = File::createTempFile (".txt");
expect (! tempFile.exists());
FileInputStream stream (tempFile);
expect (stream.failedToOpen());
}
beginTest ("Open stream existing file");
{
auto tempFile = File::createTempFile (".txt");
tempFile.create();
expect (tempFile.exists());
FileInputStream stream (tempFile);
expect (stream.openedOk());
}
const MemoryBlock data ("abcdefghijklmnopqrstuvwxyz", 26);
File f (File::createTempFile (".txt"));
f.appendData (data.getData(), data.getSize());
FileInputStream stream (f);
beginTest ("Read");
{
expectEquals (stream.getPosition(), (int64) 0);
expectEquals (stream.getTotalLength(), (int64) data.getSize());
expectEquals (stream.getNumBytesRemaining(), stream.getTotalLength());
expect (! stream.isExhausted());
size_t numBytesRead = 0;
MemoryBlock readBuffer (data.getSize());
while (numBytesRead < data.getSize())
{
numBytesRead += (size_t) stream.read (&readBuffer[numBytesRead], 3);
expectEquals (stream.getPosition(), (int64) numBytesRead);
expectEquals (stream.getNumBytesRemaining(), (int64) (data.getSize() - numBytesRead));
expect (stream.isExhausted() == (numBytesRead == data.getSize()));
}
expectEquals (stream.getPosition(), (int64) data.getSize());
expectEquals (stream.getNumBytesRemaining(), (int64) 0);
expect (stream.isExhausted());
expect (readBuffer == data);
}
beginTest ("Skip");
{
stream.setPosition (0);
expectEquals (stream.getPosition(), (int64) 0);
expectEquals (stream.getTotalLength(), (int64) data.getSize());
expectEquals (stream.getNumBytesRemaining(), stream.getTotalLength());
expect (! stream.isExhausted());
size_t numBytesRead = 0;
const int numBytesToSkip = 5;
while (numBytesRead < data.getSize())
{
stream.skipNextBytes (numBytesToSkip);
numBytesRead += numBytesToSkip;
numBytesRead = std::min (numBytesRead, data.getSize());
expectEquals (stream.getPosition(), (int64) numBytesRead);
expectEquals (stream.getNumBytesRemaining(), (int64) (data.getSize() - numBytesRead));
expect (stream.isExhausted() == (numBytesRead == data.getSize()));
}
expectEquals (stream.getPosition(), (int64) data.getSize());
expectEquals (stream.getNumBytesRemaining(), (int64) 0);
expect (stream.isExhausted());
f.deleteFile();
}
}
};
static FileInputStreamTests fileInputStreamTests;
#endif
} // namespace juce

View File

@ -0,0 +1,90 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
/**
An input stream that reads from a local file.
@see InputStream, FileOutputStream, File::createInputStream
@tags{Core}
*/
class JUCE_API FileInputStream : public InputStream
{
public:
//==============================================================================
/** Creates a FileInputStream to read from the given file.
After creating a FileInputStream, you should use openedOk() or failedToOpen()
to make sure that it's OK before trying to read from it! If it failed, you
can call getStatus() to get more error information.
*/
explicit FileInputStream (const File& fileToRead);
/** Destructor. */
~FileInputStream() override;
//==============================================================================
/** Returns the file that this stream is reading from. */
const File& getFile() const noexcept { return file; }
/** Returns the status of the file stream.
The result will be ok if the file opened successfully. If an error occurs while
opening or reading from the file, this will contain an error message.
*/
const Result& getStatus() const noexcept { return status; }
/** Returns true if the stream couldn't be opened for some reason.
@see getResult()
*/
bool failedToOpen() const noexcept { return status.failed(); }
/** Returns true if the stream opened without problems.
@see getResult()
*/
bool openedOk() const noexcept { return status.wasOk(); }
//==============================================================================
int64 getTotalLength() override;
int read (void*, int) override;
bool isExhausted() override;
int64 getPosition() override;
bool setPosition (int64) override;
private:
//==============================================================================
const File file;
void* fileHandle = nullptr;
int64 currentPosition = 0;
Result status { Result::ok() };
void openHandle();
size_t readInternal (void*, size_t);
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FileInputStream)
};
} // namespace juce

View File

@ -0,0 +1,130 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
FileOutputStream::FileOutputStream (const File& f, const size_t bufferSizeToUse)
: file (f),
bufferSize (bufferSizeToUse),
buffer (jmax (bufferSizeToUse, (size_t) 16))
{
openHandle();
}
FileOutputStream::~FileOutputStream()
{
flushBuffer();
closeHandle();
}
int64 FileOutputStream::getPosition()
{
return currentPosition;
}
bool FileOutputStream::setPosition (int64 newPosition)
{
if (newPosition != currentPosition)
{
flushBuffer();
currentPosition = juce_fileSetPosition (fileHandle, newPosition);
}
return newPosition == currentPosition;
}
bool FileOutputStream::flushBuffer()
{
bool ok = true;
if (bytesInBuffer > 0)
{
ok = (writeInternal (buffer, bytesInBuffer) == (ssize_t) bytesInBuffer);
bytesInBuffer = 0;
}
return ok;
}
void FileOutputStream::flush()
{
flushBuffer();
flushInternal();
}
bool FileOutputStream::write (const void* const src, const size_t numBytes)
{
jassert (src != nullptr && ((ssize_t) numBytes) >= 0);
if (! openedOk())
return false;
if (bytesInBuffer + numBytes < bufferSize)
{
memcpy (buffer + bytesInBuffer, src, numBytes);
bytesInBuffer += numBytes;
currentPosition += (int64) numBytes;
}
else
{
if (! flushBuffer())
return false;
if (numBytes < bufferSize)
{
memcpy (buffer + bytesInBuffer, src, numBytes);
bytesInBuffer += numBytes;
currentPosition += (int64) numBytes;
}
else
{
auto bytesWritten = writeInternal (src, numBytes);
if (bytesWritten < 0)
return false;
currentPosition += (int64) bytesWritten;
return bytesWritten == (ssize_t) numBytes;
}
}
return true;
}
bool FileOutputStream::writeRepeatedByte (uint8 byte, size_t numBytes)
{
jassert (((ssize_t) numBytes) >= 0);
if (bytesInBuffer + numBytes < bufferSize)
{
memset (buffer + bytesInBuffer, byte, numBytes);
bytesInBuffer += numBytes;
currentPosition += (int64) numBytes;
return true;
}
return OutputStream::writeRepeatedByte (byte, numBytes);
}
} // namespace juce

View File

@ -0,0 +1,126 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
/**
An output stream that writes into a local file.
@see OutputStream, FileInputStream, File::createOutputStream
@tags{Core}
*/
class JUCE_API FileOutputStream : public OutputStream
{
public:
//==============================================================================
/** Creates a FileOutputStream.
If the file doesn't exist, it will first be created. If the file can't be
created or opened (for example, because the parent directory of the file
does not exist), the failedToOpen() method will return true.
If the file already exists when opened, the stream's write-position will
be set to the end of the file. To overwrite an existing file, you can truncate
it like this:
@code
FileOutputStream stream (file);
if (stream.openedOk())
{
stream.setPosition (0);
stream.truncate();
...
}
@endcode
Destroying a FileOutputStream object does not force the operating system
to write the buffered data to disk immediately. If this is required you
should call flush() before triggering the destructor.
@see TemporaryFile
*/
FileOutputStream (const File& fileToWriteTo,
size_t bufferSizeToUse = 16384);
/** Destructor. */
~FileOutputStream() override;
//==============================================================================
/** Returns the file that this stream is writing to.
*/
const File& getFile() const { return file; }
/** Returns the status of the file stream.
The result will be ok if the file opened successfully. If an error occurs while
opening or writing to the file, this will contain an error message.
*/
const Result& getStatus() const noexcept { return status; }
/** Returns true if the stream couldn't be opened for some reason.
@see getResult()
*/
bool failedToOpen() const noexcept { return status.failed(); }
/** Returns true if the stream opened without problems.
@see getResult()
*/
bool openedOk() const noexcept { return status.wasOk(); }
/** Attempts to truncate the file to the current write position.
To truncate a file to a specific size, first use setPosition() to seek to the
appropriate location, and then call this method.
*/
Result truncate();
//==============================================================================
void flush() override;
int64 getPosition() override;
bool setPosition (int64) override;
bool write (const void*, size_t) override;
bool writeRepeatedByte (uint8 byte, size_t numTimesToRepeat) override;
private:
//==============================================================================
File file;
void* fileHandle = nullptr;
Result status { Result::ok() };
int64 currentPosition = 0;
size_t bufferSize, bytesInBuffer = 0;
HeapBlock<char> buffer;
void openHandle();
void closeHandle();
void flushInternal();
bool flushBuffer();
int64 setPositionInternal (int64);
ssize_t writeInternal (const void*, size_t);
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FileOutputStream)
};
} // namespace juce

View File

@ -0,0 +1,173 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
FileSearchPath::FileSearchPath() {}
FileSearchPath::~FileSearchPath() {}
FileSearchPath::FileSearchPath (const String& path)
{
init (path);
}
FileSearchPath::FileSearchPath (const FileSearchPath& other)
: directories (other.directories)
{
}
FileSearchPath& FileSearchPath::operator= (const FileSearchPath& other)
{
directories = other.directories;
return *this;
}
FileSearchPath& FileSearchPath::operator= (const String& path)
{
init (path);
return *this;
}
void FileSearchPath::init (const String& path)
{
directories.clear();
directories.addTokens (path, ";", "\"");
directories.trim();
directories.removeEmptyStrings();
for (auto& d : directories)
d = d.unquoted();
}
int FileSearchPath::getNumPaths() const
{
return directories.size();
}
File FileSearchPath::operator[] (int index) const
{
return File (directories[index]);
}
String FileSearchPath::toString() const
{
auto dirs = directories;
for (auto& d : dirs)
if (d.containsChar (';'))
d = d.quoted();
return dirs.joinIntoString (";");
}
void FileSearchPath::add (const File& dir, int insertIndex)
{
directories.insert (insertIndex, dir.getFullPathName());
}
bool FileSearchPath::addIfNotAlreadyThere (const File& dir)
{
for (auto& d : directories)
if (File (d) == dir)
return false;
add (dir);
return true;
}
void FileSearchPath::remove (int index)
{
directories.remove (index);
}
void FileSearchPath::addPath (const FileSearchPath& other)
{
for (int i = 0; i < other.getNumPaths(); ++i)
addIfNotAlreadyThere (other[i]);
}
void FileSearchPath::removeRedundantPaths()
{
for (int i = directories.size(); --i >= 0;)
{
const File d1 (directories[i]);
for (int j = directories.size(); --j >= 0;)
{
const File d2 (directories[j]);
if (i != j && (d1.isAChildOf (d2) || d1 == d2))
{
directories.remove (i);
break;
}
}
}
}
void FileSearchPath::removeNonExistentPaths()
{
for (int i = directories.size(); --i >= 0;)
if (! File (directories[i]).isDirectory())
directories.remove (i);
}
Array<File> FileSearchPath::findChildFiles (int whatToLookFor, bool recurse, const String& wildcard) const
{
Array<File> results;
findChildFiles (results, whatToLookFor, recurse, wildcard);
return results;
}
int FileSearchPath::findChildFiles (Array<File>& results, int whatToLookFor,
bool recurse, const String& wildcard) const
{
int total = 0;
for (auto& d : directories)
total += File (d).findChildFiles (results, whatToLookFor, recurse, wildcard);
return total;
}
bool FileSearchPath::isFileInPath (const File& fileToCheck,
const bool checkRecursively) const
{
for (auto& d : directories)
{
if (checkRecursively)
{
if (fileToCheck.isAChildOf (File (d)))
return true;
}
else
{
if (fileToCheck.getParentDirectory() == File (d))
return true;
}
}
return false;
}
} // namespace juce

View File

@ -0,0 +1,171 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
/**
Represents a set of folders that make up a search path.
@see File
@tags{Core}
*/
class JUCE_API FileSearchPath
{
public:
//==============================================================================
/** Creates an empty search path. */
FileSearchPath();
/** Creates a search path from a string of pathnames.
The path can be semicolon- or comma-separated, e.g.
"/foo/bar;/foo/moose;/fish/moose"
The separate folders are tokenised and added to the search path.
*/
FileSearchPath (const String& path);
/** Creates a copy of another search path. */
FileSearchPath (const FileSearchPath&);
/** Copies another search path. */
FileSearchPath& operator= (const FileSearchPath&);
/** Destructor. */
~FileSearchPath();
/** Uses a string containing a list of pathnames to re-initialise this list.
This search path is cleared and the semicolon- or comma-separated folders
in this string are added instead. e.g. "/foo/bar;/foo/moose;/fish/moose"
*/
FileSearchPath& operator= (const String& path);
//==============================================================================
/** Returns the number of folders in this search path.
@see operator[]
*/
int getNumPaths() const;
/** Returns one of the folders in this search path.
The file returned isn't guaranteed to actually be a valid directory.
@see getNumPaths
*/
File operator[] (int index) const;
/** Returns the search path as a semicolon-separated list of directories. */
String toString() const;
//==============================================================================
/** Adds a new directory to the search path.
The new directory is added to the end of the list if the insertIndex parameter is
less than zero, otherwise it is inserted at the given index.
*/
void add (const File& directoryToAdd,
int insertIndex = -1);
/** Adds a new directory to the search path if it's not already in there.
@return true if the directory has been added, false otherwise.
*/
bool addIfNotAlreadyThere (const File& directoryToAdd);
/** Removes a directory from the search path. */
void remove (int indexToRemove);
/** Merges another search path into this one.
This will remove any duplicate directories.
*/
void addPath (const FileSearchPath&);
/** Removes any directories that are actually subdirectories of one of the other directories in the search path.
If the search is intended to be recursive, there's no point having nested folders in the search
path, because they'll just get searched twice and you'll get duplicate results.
e.g. if the path is "c:\abc\de;c:\abc", this method will simplify it to "c:\abc"
*/
void removeRedundantPaths();
/** Removes any directories that don't actually exist. */
void removeNonExistentPaths();
//==============================================================================
/** Searches the path for a wildcard.
This will search all the directories in the search path in order and return
an array of the files that were found.
@param whatToLookFor a value from the File::TypesOfFileToFind enum, specifying whether to
return files, directories, or both.
@param searchRecursively whether to recursively search the subdirectories too
@param wildCardPattern a pattern to match against the filenames
@returns the number of files added to the array
@see File::findChildFiles
*/
Array<File> findChildFiles (int whatToLookFor,
bool searchRecursively,
const String& wildCardPattern = "*") const;
/** Searches the path for a wildcard.
Note that there's a newer, better version of this method which returns the results
array, and in almost all cases, you should use that one instead! This one is kept around
mainly for legacy code to use.
*/
int findChildFiles (Array<File>& results,
int whatToLookFor,
bool searchRecursively,
const String& wildCardPattern = "*") const;
//==============================================================================
/** Finds out whether a file is inside one of the path's directories.
This will return true if the specified file is a child of one of the
directories specified by this path. Note that this doesn't actually do any
searching or check that the files exist - it just looks at the pathnames
to work out whether the file would be inside a directory.
@param fileToCheck the file to look for
@param checkRecursively if true, then this will return true if the file is inside a
subfolder of one of the path's directories (at any depth). If false
it will only return true if the file is actually a direct child
of one of the directories.
@see File::isAChildOf
*/
bool isFileInPath (const File& fileToCheck,
bool checkRecursively) const;
private:
//==============================================================================
StringArray directories;
void init (const String&);
JUCE_LEAK_DETECTOR (FileSearchPath)
};
} // namespace juce

View File

@ -0,0 +1,115 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
/**
Maps a file into virtual memory for easy reading and/or writing.
@tags{Core}
*/
class JUCE_API MemoryMappedFile
{
public:
/** The read/write flags used when opening a memory mapped file. */
enum AccessMode
{
readOnly, /**< Indicates that the memory can only be read. */
readWrite /**< Indicates that the memory can be read and written to - changes that are
made will be flushed back to disk at the whim of the OS. */
};
/** Opens a file and maps it to an area of virtual memory.
The file should already exist, and should already be the size that you want to work with
when you call this. If the file is resized after being opened, the behaviour is undefined.
If the file exists and the operation succeeds, the getData() and getSize() methods will
return the location and size of the data that can be read or written. Note that the entire
file is not read into memory immediately - the OS simply creates a virtual mapping, which
will lazily pull the data into memory when blocks are accessed.
If the file can't be opened for some reason, the getData() method will return a null pointer.
If exclusive is false then other apps can also open the same memory mapped file and use this
mapping as an effective way of communicating. If exclusive is true then the mapped file will
be opened exclusively - preventing other apps to access the file which may improve the
performance of accessing the file.
*/
MemoryMappedFile (const File& file, AccessMode mode, bool exclusive = false);
/** Opens a section of a file and maps it to an area of virtual memory.
The file should already exist, and should already be the size that you want to work with
when you call this. If the file is resized after being opened, the behaviour is undefined.
If the file exists and the operation succeeds, the getData() and getSize() methods will
return the location and size of the data that can be read or written. Note that the entire
file is not read into memory immediately - the OS simply creates a virtual mapping, which
will lazily pull the data into memory when blocks are accessed.
If the file can't be opened for some reason, the getData() method will return a null pointer.
NOTE: The start of the actual range used may be rounded-down to a multiple of the OS's page-size,
so do not assume that the mapped memory will begin at exactly the position you requested - always
use getRange() to check the actual range that is being used.
*/
MemoryMappedFile (const File& file,
const Range<int64>& fileRange,
AccessMode mode,
bool exclusive = false);
/** Destructor. */
~MemoryMappedFile();
/** Returns the address at which this file has been mapped, or a null pointer if
the file couldn't be successfully mapped.
*/
void* getData() const noexcept { return address; }
/** Returns the number of bytes of data that are available for reading or writing.
This will normally be the size of the file.
*/
size_t getSize() const noexcept { return (size_t) range.getLength(); }
/** Returns the section of the file at which the mapped memory represents. */
Range<int64> getRange() const noexcept { return range; }
private:
//==============================================================================
void* address = nullptr;
Range<int64> range;
#if JUCE_WINDOWS
void* fileHandle = nullptr;
#else
int fileHandle = 0;
#endif
void openInternal (const File&, AccessMode, bool);
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MemoryMappedFile)
};
} // namespace juce

View File

@ -0,0 +1,77 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations")
JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4996)
float DirectoryEntry::getEstimatedProgress() const
{
if (auto it = iterator.lock())
return it->getEstimatedProgress();
return 0.0f;
}
// We implement this in terms of the deprecated DirectoryIterator,
// but the old DirectoryIterator might go away in the future!
RangedDirectoryIterator::RangedDirectoryIterator (const File& directory,
bool isRecursive,
const String& wildCard,
int whatToLookFor)
: iterator (new DirectoryIterator (directory,
isRecursive,
wildCard,
whatToLookFor))
{
entry.iterator = iterator;
increment();
}
bool RangedDirectoryIterator::next()
{
const auto result = iterator->next (&entry.directory,
&entry.hidden,
&entry.fileSize,
&entry.modTime,
&entry.creationTime,
&entry.readOnly);
if (result)
entry.file = iterator->getFile();
else
entry = {};
return result;
}
void RangedDirectoryIterator::increment()
{
if (iterator != nullptr && ! next())
iterator = nullptr;
}
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
JUCE_END_IGNORE_WARNINGS_MSVC
} // namespace juce

View File

@ -0,0 +1,188 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations")
JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4996)
/**
Describes the attributes of a file or folder.
@tags{Core}
*/
class DirectoryEntry final
{
public:
/** The path to a file or folder. */
File getFile() const { return file; }
/** The time at which the item was last modified. */
Time getModificationTime() const { return modTime; }
/** The time at which the item was created. */
Time getCreationTime() const { return creationTime; }
/** The size of the item. */
int64 getFileSize() const { return fileSize; }
/** True if the item is a directory, false otherwise. */
bool isDirectory() const { return directory; }
/** True if the item is hidden, false otherwise. */
bool isHidden() const { return hidden; }
/** True if the item is read-only, false otherwise. */
bool isReadOnly() const { return readOnly; }
/** The estimated proportion of the range that has been visited
by the iterator, from 0.0 to 1.0.
*/
float getEstimatedProgress() const;
private:
std::weak_ptr<DirectoryIterator> iterator;
File file;
Time modTime;
Time creationTime;
int64 fileSize = 0;
bool directory = false;
bool hidden = false;
bool readOnly = false;
friend class RangedDirectoryIterator;
};
/** A convenience operator so that the expression `*it++` works correctly when
`it` is an instance of RangedDirectoryIterator.
*/
inline const DirectoryEntry& operator* (const DirectoryEntry& e) noexcept { return e; }
//==============================================================================
/**
Allows iterating over files and folders using C++11 range-for syntax.
In the following example, we recursively find all hidden files in a
specific directory.
@code
std::vector<File> hiddenFiles;
for (DirectoryEntry entry : RangedDirectoryIterator (File ("/path/to/folder"), isRecursive))
if (entry.isHidden())
hiddenFiles.push_back (entry.getFile());
@endcode
@tags{Core}
*/
class RangedDirectoryIterator final
{
public:
using difference_type = std::ptrdiff_t;
using value_type = DirectoryEntry;
using reference = DirectoryEntry;
using pointer = void;
using iterator_category = std::input_iterator_tag;
/** The default-constructed iterator acts as the 'end' sentinel. */
RangedDirectoryIterator() = default;
/** Creates a RangedDirectoryIterator for a given directory.
The resulting iterator can be used directly in a 'range-for' expression.
@param directory the directory to search in
@param isRecursive whether all the subdirectories should also be searched
@param wildCard the file pattern to match. This may contain multiple patterns
separated by a semi-colon or comma, e.g. "*.jpg;*.png"
@param whatToLookFor a value from the File::TypesOfFileToFind enum, specifying
whether to look for files, directories, or both.
*/
RangedDirectoryIterator (const File& directory,
bool isRecursive,
const String& wildCard = "*",
int whatToLookFor = File::findFiles);
/** Returns true if both iterators are in their end/sentinel state,
otherwise returns false.
*/
bool operator== (const RangedDirectoryIterator& other) const noexcept
{
return iterator == nullptr && other.iterator == nullptr;
}
/** Returns the inverse of operator== */
bool operator!= (const RangedDirectoryIterator& other) const noexcept
{
return ! operator== (other);
}
/** Return an object containing metadata about the file or folder to
which the iterator is currently pointing.
*/
const DirectoryEntry& operator* () const noexcept { return entry; }
const DirectoryEntry* operator->() const noexcept { return &entry; }
/** Moves the iterator along to the next file. */
RangedDirectoryIterator& operator++()
{
increment();
return *this;
}
/** Moves the iterator along to the next file.
@returns an object containing metadata about the file or folder to
to which the iterator was previously pointing.
*/
DirectoryEntry operator++ (int)
{
auto result = *(*this);
++(*this);
return result;
}
private:
bool next();
void increment();
std::shared_ptr<DirectoryIterator> iterator;
DirectoryEntry entry;
};
/** Returns the iterator that was passed in.
Provided for range-for compatibility.
*/
inline RangedDirectoryIterator begin (const RangedDirectoryIterator& it) { return it; }
/** Returns a default-constructed sentinel value.
Provided for range-for compatibility.
*/
inline RangedDirectoryIterator end (const RangedDirectoryIterator&) { return {}; }
JUCE_END_IGNORE_WARNINGS_MSVC
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
} // namespace juce

View File

@ -0,0 +1,117 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
static File createTempFile (const File& parentDirectory, String name,
const String& suffix, int optionFlags)
{
if ((optionFlags & TemporaryFile::useHiddenFile) != 0)
name = "." + name;
return parentDirectory.getNonexistentChildFile (name, suffix, (optionFlags & TemporaryFile::putNumbersInBrackets) != 0);
}
TemporaryFile::TemporaryFile (const String& suffix, const int optionFlags)
: temporaryFile (createTempFile (File::getSpecialLocation (File::tempDirectory),
"temp_" + String::toHexString (Random::getSystemRandom().nextInt()),
suffix, optionFlags)),
targetFile()
{
}
TemporaryFile::TemporaryFile (const File& target, const int optionFlags)
: temporaryFile (createTempFile (target.getParentDirectory(),
target.getFileNameWithoutExtension()
+ "_temp" + String::toHexString (Random::getSystemRandom().nextInt()),
target.getFileExtension(), optionFlags)),
targetFile (target)
{
// If you use this constructor, you need to give it a valid target file!
jassert (targetFile != File());
}
TemporaryFile::TemporaryFile (const File& target, const File& temporary)
: temporaryFile (temporary), targetFile (target)
{
}
TemporaryFile::~TemporaryFile()
{
if (! deleteTemporaryFile())
{
/* Failed to delete our temporary file! The most likely reason for this would be
that you've not closed an output stream that was being used to write to file.
If you find that something beyond your control is changing permissions on
your temporary files and preventing them from being deleted, you may want to
call TemporaryFile::deleteTemporaryFile() to detect those error cases and
handle them appropriately.
*/
jassertfalse;
}
}
//==============================================================================
bool TemporaryFile::overwriteTargetFileWithTemporary() const
{
// This method only works if you created this object with the constructor
// that takes a target file!
jassert (targetFile != File());
if (temporaryFile.exists())
{
// Have a few attempts at overwriting the file before giving up..
for (int i = 5; --i >= 0;)
{
if (temporaryFile.replaceFileIn (targetFile))
return true;
Thread::sleep (100);
}
}
else
{
// There's no temporary file to use. If your write failed, you should
// probably check, and not bother calling this method.
jassertfalse;
}
return false;
}
bool TemporaryFile::deleteTemporaryFile() const
{
// Have a few attempts at deleting the file before giving up..
for (int i = 5; --i >= 0;)
{
if (temporaryFile.deleteFile())
return true;
Thread::sleep (50);
}
return false;
}
} // namespace juce

View File

@ -0,0 +1,162 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
/**
Manages a temporary file, which will be deleted when this object is deleted.
This object is intended to be used as a stack based object, using its scope
to make sure the temporary file isn't left lying around.
For example:
@code
{
File myTargetFile ("~/myfile.txt");
// this will choose a file called something like "~/myfile_temp239348.txt"
// which definitely doesn't exist at the time the constructor is called.
TemporaryFile temp (myTargetFile);
// create a stream to the temporary file, and write some data to it...
if (auto out = std::unique_ptr<FileOutputStream> (temp.getFile().createOutputStream()))
{
out->write ( ...etc )
out.reset(); // (deletes the stream)
// ..now we've finished writing, this will rename the temp file to
// make it replace the target file we specified above.
bool succeeded = temp.overwriteTargetFileWithTemporary();
}
// ..and even if something went wrong and our overwrite failed,
// as the TemporaryFile object goes out of scope here, it'll make sure
// that the temp file gets deleted.
}
@endcode
@see File, FileOutputStream
@tags{Core}
*/
class JUCE_API TemporaryFile
{
public:
//==============================================================================
enum OptionFlags
{
useHiddenFile = 1, /**< Indicates that the temporary file should be hidden -
i.e. its name should start with a dot. */
putNumbersInBrackets = 2 /**< Indicates that when numbers are appended to make sure
the file is unique, they should go in brackets rather
than just being appended (see File::getNonexistentSibling() )*/
};
//==============================================================================
/** Creates a randomly-named temporary file in the default temp directory.
@param suffix a file suffix to use for the file
@param optionFlags a combination of the values listed in the OptionFlags enum
The file will not be created until you write to it. And remember that when
this object is deleted, the file will also be deleted!
*/
TemporaryFile (const String& suffix = String(),
int optionFlags = 0);
/** Creates a temporary file in the same directory as a specified file.
This is useful if you have a file that you want to overwrite, but don't
want to harm the original file if the write operation fails. You can
use this to create a temporary file next to the target file, then
write to the temporary file, and finally use overwriteTargetFileWithTemporary()
to replace the target file with the one you've just written.
This class won't create any files until you actually write to them. And remember
that when this object is deleted, the temporary file will also be deleted!
@param targetFile the file that you intend to overwrite - the temporary
file will be created in the same directory as this
@param optionFlags a combination of the values listed in the OptionFlags enum
*/
TemporaryFile (const File& targetFile,
int optionFlags = 0);
/** Creates a temporary file using an explicit filename.
The other constructors are a better choice than this one, unless for some reason
you need to explicitly specify the temporary file you want to use.
@param targetFile the file that you intend to overwrite
@param temporaryFile the temporary file to be used
*/
TemporaryFile (const File& targetFile,
const File& temporaryFile);
/** Destructor.
When this object is deleted it will make sure that its temporary file is
also deleted! If the operation fails, it'll throw an assertion in debug
mode.
*/
~TemporaryFile();
//==============================================================================
/** Returns the temporary file. */
const File& getFile() const noexcept { return temporaryFile; }
/** Returns the target file that was specified in the constructor. */
const File& getTargetFile() const noexcept { return targetFile; }
/** Tries to move the temporary file to overwrite the target file that was
specified in the constructor.
If you used the constructor that specified a target file, this will attempt
to replace that file with the temporary one.
Before calling this, make sure:
- that you've actually written to the temporary file
- that you've closed any open streams that you were using to write to it
- and that you don't have any streams open to the target file, which would
prevent it being overwritten
If the file move succeeds, this returns true, and the temporary file will
have disappeared. If it fails, the temporary file will probably still exist,
but will be deleted when this object is destroyed.
*/
bool overwriteTargetFileWithTemporary() const;
/** Attempts to delete the temporary file, if it exists.
@returns true if the file is successfully deleted (or if it didn't exist).
*/
bool deleteTemporaryFile() const;
private:
//==============================================================================
const File temporaryFile, targetFile;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TemporaryFile)
};
} // namespace juce

View File

@ -0,0 +1,74 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
static void parseWildcard (const String& pattern, StringArray& result)
{
result.addTokens (pattern.toLowerCase(), ";,", "\"'");
result.trim();
result.removeEmptyStrings();
// special case for *.*, because people use it to mean "any file", but it
// would actually ignore files with no extension.
for (auto& r : result)
if (r == "*.*")
r = "*";
}
static bool matchWildcard (const File& file, const StringArray& wildcards)
{
auto filename = file.getFileName();
for (auto& w : wildcards)
if (filename.matchesWildcard (w, true))
return true;
return false;
}
WildcardFileFilter::WildcardFileFilter (const String& fileWildcardPatterns,
const String& directoryWildcardPatterns,
const String& desc)
: FileFilter (desc.isEmpty() ? fileWildcardPatterns
: (desc + " (" + fileWildcardPatterns + ")"))
{
parseWildcard (fileWildcardPatterns, fileWildcards);
parseWildcard (directoryWildcardPatterns, directoryWildcards);
}
WildcardFileFilter::~WildcardFileFilter()
{
}
bool WildcardFileFilter::isFileSuitable (const File& file) const
{
return matchWildcard (file, fileWildcards);
}
bool WildcardFileFilter::isDirectorySuitable (const File& file) const
{
return matchWildcard (file, directoryWildcards);
}
} // namespace juce

View File

@ -0,0 +1,76 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
/**
A type of FileFilter that works by wildcard pattern matching.
This filter only allows files that match one of the specified patterns, but
allows all directories through.
@see FileFilter, DirectoryContentsList, FileListComponent, FileBrowserComponent
@tags{Core}
*/
class JUCE_API WildcardFileFilter : public FileFilter
{
public:
//==============================================================================
/**
Creates a wildcard filter for one or more patterns.
The wildcardPatterns parameter is a comma or semicolon-delimited set of
patterns, e.g. "*.wav;*.aiff" would look for files ending in either .wav
or .aiff.
Passing an empty string as a pattern will fail to match anything, so by leaving
either the file or directory pattern parameter empty means you can control
whether files or directories are found.
The description is a name to show the user in a list of possible patterns, so
for the wav/aiff example, your description might be "audio files".
*/
WildcardFileFilter (const String& fileWildcardPatterns,
const String& directoryWildcardPatterns,
const String& filterDescription);
/** Destructor. */
~WildcardFileFilter() override;
//==============================================================================
/** Returns true if the filename matches one of the patterns specified. */
bool isFileSuitable (const File& file) const override;
/** This always returns true. */
bool isDirectorySuitable (const File& file) const override;
private:
//==============================================================================
StringArray fileWildcards, directoryWildcards;
JUCE_LEAK_DETECTOR (WildcardFileFilter)
};
} // namespace juce

View File

@ -0,0 +1,693 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
struct JSONParser
{
JSONParser (String::CharPointerType text) : startLocation (text), currentLocation (text) {}
String::CharPointerType startLocation, currentLocation;
struct ErrorException
{
String message;
int line = 1, column = 1;
String getDescription() const { return String (line) + ":" + String (column) + ": error: " + message; }
Result getResult() const { return Result::fail (getDescription()); }
};
[[noreturn]] void throwError (juce::String message, String::CharPointerType location)
{
ErrorException e;
e.message = std::move (message);
for (auto i = startLocation; i < location && ! i.isEmpty(); ++i)
{
++e.column;
if (*i == '\n') { e.column = 1; e.line++; }
}
throw e;
}
void skipWhitespace() { currentLocation = currentLocation.findEndOfWhitespace(); }
juce_wchar readChar() { return currentLocation.getAndAdvance(); }
juce_wchar peekChar() const { return *currentLocation; }
bool matchIf (char c) { if (peekChar() == (juce_wchar) c) { ++currentLocation; return true; } return false; }
bool isEOF() const { return peekChar() == 0; }
bool matchString (const char* t)
{
while (*t != 0)
if (! matchIf (*t++))
return false;
return true;
}
var parseObjectOrArray()
{
skipWhitespace();
if (matchIf ('{')) return parseObject();
if (matchIf ('[')) return parseArray();
if (! isEOF())
throwError ("Expected '{' or '['", currentLocation);
return {};
}
String parseString (const juce_wchar quoteChar)
{
MemoryOutputStream buffer (256);
for (;;)
{
auto c = readChar();
if (c == quoteChar)
break;
if (c == '\\')
{
auto errorLocation = currentLocation;
c = readChar();
switch (c)
{
case '"':
case '\'':
case '\\':
case '/': break;
case 'a': c = '\a'; break;
case 'b': c = '\b'; break;
case 'f': c = '\f'; break;
case 'n': c = '\n'; break;
case 'r': c = '\r'; break;
case 't': c = '\t'; break;
case 'u':
{
c = 0;
for (int i = 4; --i >= 0;)
{
auto digitValue = CharacterFunctions::getHexDigitValue (readChar());
if (digitValue < 0)
throwError ("Syntax error in unicode escape sequence", errorLocation);
c = (juce_wchar) ((c << 4) + static_cast<juce_wchar> (digitValue));
}
break;
}
default: break;
}
}
if (c == 0)
throwError ("Unexpected EOF in string constant", currentLocation);
buffer.appendUTF8Char (c);
}
return buffer.toUTF8();
}
var parseAny()
{
skipWhitespace();
auto originalLocation = currentLocation;
switch (readChar())
{
case '{': return parseObject();
case '[': return parseArray();
case '"': return parseString ('"');
case '\'': return parseString ('\'');
case '-':
skipWhitespace();
return parseNumber (true);
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
currentLocation = originalLocation;
return parseNumber (false);
case 't': // "true"
if (matchString ("rue"))
return var (true);
break;
case 'f': // "false"
if (matchString ("alse"))
return var (false);
break;
case 'n': // "null"
if (matchString ("ull"))
return {};
break;
default:
break;
}
throwError ("Syntax error", originalLocation);
}
var parseNumber (bool isNegative)
{
auto originalPos = currentLocation;
int64 intValue = readChar() - '0';
jassert (intValue >= 0 && intValue < 10);
for (;;)
{
auto lastPos = currentLocation;
auto c = readChar();
auto digit = ((int) c) - '0';
if (isPositiveAndBelow (digit, 10))
{
intValue = intValue * 10 + digit;
continue;
}
if (c == 'e' || c == 'E' || c == '.')
{
currentLocation = originalPos;
auto asDouble = CharacterFunctions::readDoubleValue (currentLocation);
return var (isNegative ? -asDouble : asDouble);
}
if (CharacterFunctions::isWhitespace (c)
|| c == ',' || c == '}' || c == ']' || c == 0)
{
currentLocation = lastPos;
break;
}
throwError ("Syntax error in number", lastPos);
}
auto correctedValue = isNegative ? -intValue : intValue;
return (intValue >> 31) != 0 ? var (correctedValue)
: var ((int) correctedValue);
}
var parseObject()
{
auto resultObject = new DynamicObject();
var result (resultObject);
auto& resultProperties = resultObject->getProperties();
auto startOfObjectDecl = currentLocation;
for (;;)
{
skipWhitespace();
auto errorLocation = currentLocation;
auto c = readChar();
if (c == '}')
break;
if (c == 0)
throwError ("Unexpected EOF in object declaration", startOfObjectDecl);
if (c != '"')
throwError ("Expected a property name in double-quotes", errorLocation);
errorLocation = currentLocation;
Identifier propertyName (parseString ('"'));
if (! propertyName.isValid())
throwError ("Invalid property name", errorLocation);
skipWhitespace();
errorLocation = currentLocation;
if (readChar() != ':')
throwError ("Expected ':'", errorLocation);
resultProperties.set (propertyName, parseAny());
skipWhitespace();
if (matchIf (',')) continue;
if (matchIf ('}')) break;
throwError ("Expected ',' or '}'", currentLocation);
}
return result;
}
var parseArray()
{
auto result = var (Array<var>());
auto destArray = result.getArray();
auto startOfArrayDecl = currentLocation;
for (;;)
{
skipWhitespace();
if (matchIf (']'))
break;
if (isEOF())
throwError ("Unexpected EOF in array declaration", startOfArrayDecl);
destArray->add (parseAny());
skipWhitespace();
if (matchIf (',')) continue;
if (matchIf (']')) break;
throwError ("Expected ',' or ']'", currentLocation);
}
return result;
}
};
//==============================================================================
struct JSONFormatter
{
static void write (OutputStream& out, const var& v,
int indentLevel, bool allOnOneLine, int maximumDecimalPlaces)
{
if (v.isString())
{
out << '"';
writeString (out, v.toString().getCharPointer());
out << '"';
}
else if (v.isVoid())
{
out << "null";
}
else if (v.isUndefined())
{
out << "undefined";
}
else if (v.isBool())
{
out << (static_cast<bool> (v) ? "true" : "false");
}
else if (v.isDouble())
{
auto d = static_cast<double> (v);
if (juce_isfinite (d))
{
out << serialiseDouble (d);
}
else
{
out << "null";
}
}
else if (v.isArray())
{
writeArray (out, *v.getArray(), indentLevel, allOnOneLine, maximumDecimalPlaces);
}
else if (v.isObject())
{
if (auto* object = v.getDynamicObject())
object->writeAsJSON (out, indentLevel, allOnOneLine, maximumDecimalPlaces);
else
jassertfalse; // Only DynamicObjects can be converted to JSON!
}
else
{
// Can't convert these other types of object to JSON!
jassert (! (v.isMethod() || v.isBinaryData()));
out << v.toString();
}
}
static void writeEscapedChar (OutputStream& out, const unsigned short value)
{
out << "\\u" << String::toHexString ((int) value).paddedLeft ('0', 4);
}
static void writeString (OutputStream& out, String::CharPointerType t)
{
for (;;)
{
auto c = t.getAndAdvance();
switch (c)
{
case 0: return;
case '\"': out << "\\\""; break;
case '\\': out << "\\\\"; break;
case '\a': out << "\\a"; break;
case '\b': out << "\\b"; break;
case '\f': out << "\\f"; break;
case '\t': out << "\\t"; break;
case '\r': out << "\\r"; break;
case '\n': out << "\\n"; break;
default:
if (c >= 32 && c < 127)
{
out << (char) c;
}
else
{
if (CharPointer_UTF16::getBytesRequiredFor (c) > 2)
{
CharPointer_UTF16::CharType chars[2];
CharPointer_UTF16 utf16 (chars);
utf16.write (c);
for (int i = 0; i < 2; ++i)
writeEscapedChar (out, (unsigned short) chars[i]);
}
else
{
writeEscapedChar (out, (unsigned short) c);
}
}
break;
}
}
}
static void writeSpaces (OutputStream& out, int numSpaces)
{
out.writeRepeatedByte (' ', (size_t) numSpaces);
}
static void writeArray (OutputStream& out, const Array<var>& array,
int indentLevel, bool allOnOneLine, int maximumDecimalPlaces)
{
out << '[';
if (! array.isEmpty())
{
if (! allOnOneLine)
out << newLine;
for (int i = 0; i < array.size(); ++i)
{
if (! allOnOneLine)
writeSpaces (out, indentLevel + indentSize);
write (out, array.getReference(i), indentLevel + indentSize, allOnOneLine, maximumDecimalPlaces);
if (i < array.size() - 1)
{
if (allOnOneLine)
out << ", ";
else
out << ',' << newLine;
}
else if (! allOnOneLine)
out << newLine;
}
if (! allOnOneLine)
writeSpaces (out, indentLevel);
}
out << ']';
}
enum { indentSize = 2 };
};
//==============================================================================
var JSON::parse (const String& text)
{
var result;
if (parse (text, result))
return result;
return {};
}
var JSON::fromString (StringRef text)
{
try
{
return JSONParser (text.text).parseAny();
}
catch (const JSONParser::ErrorException&) {}
return {};
}
var JSON::parse (InputStream& input)
{
return parse (input.readEntireStreamAsString());
}
var JSON::parse (const File& file)
{
return parse (file.loadFileAsString());
}
Result JSON::parse (const String& text, var& result)
{
try
{
result = JSONParser (text.getCharPointer()).parseObjectOrArray();
}
catch (const JSONParser::ErrorException& error)
{
return error.getResult();
}
return Result::ok();
}
String JSON::toString (const var& data, const bool allOnOneLine, int maximumDecimalPlaces)
{
MemoryOutputStream mo (1024);
JSONFormatter::write (mo, data, 0, allOnOneLine, maximumDecimalPlaces);
return mo.toUTF8();
}
void JSON::writeToStream (OutputStream& output, const var& data, const bool allOnOneLine, int maximumDecimalPlaces)
{
JSONFormatter::write (output, data, 0, allOnOneLine, maximumDecimalPlaces);
}
String JSON::escapeString (StringRef s)
{
MemoryOutputStream mo;
JSONFormatter::writeString (mo, s.text);
return mo.toString();
}
Result JSON::parseQuotedString (String::CharPointerType& t, var& result)
{
try
{
JSONParser parser (t);
auto quote = parser.readChar();
if (quote != '"' && quote != '\'')
return Result::fail ("Not a quoted string!");
result = parser.parseString (quote);
t = parser.currentLocation;
}
catch (const JSONParser::ErrorException& error)
{
return error.getResult();
}
return Result::ok();
}
//==============================================================================
//==============================================================================
#if JUCE_UNIT_TESTS
class JSONTests : public UnitTest
{
public:
JSONTests()
: UnitTest ("JSON", UnitTestCategories::json)
{}
static String createRandomWideCharString (Random& r)
{
juce_wchar buffer[40] = { 0 };
for (int i = 0; i < numElementsInArray (buffer) - 1; ++i)
{
if (r.nextBool())
{
do
{
buffer[i] = (juce_wchar) (1 + r.nextInt (0x10ffff - 1));
}
while (! CharPointer_UTF16::canRepresent (buffer[i]));
}
else
buffer[i] = (juce_wchar) (1 + r.nextInt (0xff));
}
return CharPointer_UTF32 (buffer);
}
static String createRandomIdentifier (Random& r)
{
char buffer[30] = { 0 };
for (int i = 0; i < numElementsInArray (buffer) - 1; ++i)
{
static const char chars[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-:";
buffer[i] = chars [r.nextInt (sizeof (chars) - 1)];
}
return CharPointer_ASCII (buffer);
}
// Creates a random double that can be easily stringified, to avoid
// false failures when decimal places are rounded or truncated slightly
static var createRandomDouble (Random& r)
{
return var ((r.nextDouble() * 1000.0) + 0.1);
}
static var createRandomVar (Random& r, int depth)
{
switch (r.nextInt (depth > 3 ? 6 : 8))
{
case 0: return {};
case 1: return r.nextInt();
case 2: return r.nextInt64();
case 3: return r.nextBool();
case 4: return createRandomDouble (r);
case 5: return createRandomWideCharString (r);
case 6:
{
var v (createRandomVar (r, depth + 1));
for (int i = 1 + r.nextInt (30); --i >= 0;)
v.append (createRandomVar (r, depth + 1));
return v;
}
case 7:
{
auto o = new DynamicObject();
for (int i = r.nextInt (30); --i >= 0;)
o->setProperty (createRandomIdentifier (r), createRandomVar (r, depth + 1));
return o;
}
default:
return {};
}
}
void runTest() override
{
{
beginTest ("JSON");
auto r = getRandom();
expect (JSON::parse (String()) == var());
expect (JSON::parse ("{}").isObject());
expect (JSON::parse ("[]").isArray());
expect (JSON::parse ("[ 1234 ]")[0].isInt());
expect (JSON::parse ("[ 12345678901234 ]")[0].isInt64());
expect (JSON::parse ("[ 1.123e3 ]")[0].isDouble());
expect (JSON::parse ("[ -1234]")[0].isInt());
expect (JSON::parse ("[-12345678901234]")[0].isInt64());
expect (JSON::parse ("[-1.123e3]")[0].isDouble());
for (int i = 100; --i >= 0;)
{
var v;
if (i > 0)
v = createRandomVar (r, 0);
const bool oneLine = r.nextBool();
String asString (JSON::toString (v, oneLine));
var parsed = JSON::parse ("[" + asString + "]")[0];
String parsedString (JSON::toString (parsed, oneLine));
expect (asString.isNotEmpty() && parsedString == asString);
}
}
{
beginTest ("Float formatting");
std::map<double, String> tests;
tests[1] = "1.0";
tests[1.1] = "1.1";
tests[1.01] = "1.01";
tests[0.76378] = "0.76378";
tests[-10] = "-10.0";
tests[10.01] = "10.01";
tests[0.0123] = "0.0123";
tests[-3.7e-27] = "-3.7e-27";
tests[1e+40] = "1.0e40";
tests[-12345678901234567.0] = "-1.234567890123457e16";
tests[192000] = "192000.0";
tests[1234567] = "1.234567e6";
tests[0.00006] = "0.00006";
tests[0.000006] = "6.0e-6";
for (auto& test : tests)
expectEquals (JSON::toString (test.first), test.second);
}
}
};
static JSONTests JSONUnitTests;
#endif
} // namespace juce

View File

@ -0,0 +1,136 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
/**
Contains static methods for converting JSON-formatted text to and from var objects.
The var class is structurally compatible with JSON-formatted data, so these
functions allow you to parse JSON into a var object, and to convert a var
object to JSON-formatted text.
@see var
@tags{Core}
*/
class JUCE_API JSON
{
public:
//==============================================================================
/** Parses a string of JSON-formatted text, and returns a result code containing
any parse errors.
This will return the parsed structure in the parsedResult parameter, and will
return a Result object to indicate whether parsing was successful, and if not,
it will contain an error message.
If you're not interested in the error message, you can use one of the other
shortcut parse methods, which simply return a var() if the parsing fails.
Note that this will only parse valid JSON, which means that the item given must
be either an object or an array definition. If you want to also be able to parse
any kind of primitive JSON object, use the fromString() method.
*/
static Result parse (const String& text, var& parsedResult);
/** Attempts to parse some JSON-formatted text, and returns the result as a var object.
If the parsing fails, this simply returns var() - if you need to find out more
detail about the parse error, use the alternative parse() method which returns a Result.
Note that this will only parse valid JSON, which means that the item given must
be either an object or an array definition. If you want to also be able to parse
any kind of primitive JSON object, use the fromString() method.
*/
static var parse (const String& text);
/** Attempts to parse some JSON-formatted text from a file, and returns the result
as a var object.
Note that this is just a short-cut for reading the entire file into a string and
parsing the result.
If the parsing fails, this simply returns var() - if you need to find out more
detail about the parse error, use the alternative parse() method which returns a Result.
*/
static var parse (const File& file);
/** Attempts to parse some JSON-formatted text from a stream, and returns the result
as a var object.
Note that this is just a short-cut for reading the entire stream into a string and
parsing the result.
If the parsing fails, this simply returns var() - if you need to find out more
detail about the parse error, use the alternative parse() method which returns a Result.
*/
static var parse (InputStream& input);
//==============================================================================
/** Returns a string which contains a JSON-formatted representation of the var object.
If allOnOneLine is true, the result will be compacted into a single line of text
with no carriage-returns. If false, it will be laid-out in a more human-readable format.
The maximumDecimalPlaces parameter determines the precision of floating point numbers
in scientific notation.
@see writeToStream
*/
static String toString (const var& objectToFormat,
bool allOnOneLine = false,
int maximumDecimalPlaces = 15);
/** Parses a string that was created with the toString() method.
This is slightly different to the parse() methods because they will reject primitive
values and only accept array or object definitions, whereas this method will handle
either.
*/
static var fromString (StringRef);
/** Writes a JSON-formatted representation of the var object to the given stream.
If allOnOneLine is true, the result will be compacted into a single line of text
with no carriage-returns. If false, it will be laid-out in a more human-readable format.
The maximumDecimalPlaces parameter determines the precision of floating point numbers
in scientific notation.
@see toString
*/
static void writeToStream (OutputStream& output,
const var& objectToFormat,
bool allOnOneLine = false,
int maximumDecimalPlaces = 15);
/** Returns a version of a string with any extended characters escaped. */
static String escapeString (StringRef);
/** Parses a quoted string-literal in JSON format, returning the un-escaped result in the
result parameter, and an error message in case the content was illegal.
This advances the text parameter, leaving it positioned after the closing quote.
*/
static Result parseQuotedString (String::CharPointerType& text, var& result);
private:
//==============================================================================
JSON() = delete; // This class can't be instantiated - just use its static methods.
};
} // namespace juce

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,122 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
/**
A simple javascript interpreter!
It's not fully standards-compliant, and won't be as fast as the fancy JIT-compiled
engines that you get in browsers, but this is an extremely compact, low-overhead javascript
interpreter, which is integrated with the juce var and DynamicObject classes. If you need
a few simple bits of scripting in your app, and want to be able to easily let the JS
work with native objects defined as DynamicObject subclasses, then this might do the job.
To use, simply create an instance of this class and call execute() to run your code.
Variables that the script sets can be retrieved with evaluate(), and if you need to provide
native objects for the script to use, you can add them with registerNativeObject().
One caveat: Because the values and objects that the engine works with are DynamicObject
and var objects, they use reference-counting rather than garbage-collection, so if your
script creates complex connections between objects, you run the risk of creating cyclic
dependencies and hence leaking.
@tags{Core}
*/
class JUCE_API JavascriptEngine final
{
public:
/** Creates an instance of the engine.
This creates a root namespace and defines some basic Object, String, Array
and Math library methods.
*/
JavascriptEngine();
/** Destructor. */
~JavascriptEngine();
/** Attempts to parse and run a block of javascript code.
If there's a parse or execution error, the error description is returned in
the result.
You can specify a maximum time for which the program is allowed to run, and
it'll return with an error message if this time is exceeded.
*/
Result execute (const String& javascriptCode);
/** Attempts to parse and run a javascript expression, and returns the result.
If there's a syntax error, or the expression can't be evaluated, the return value
will be var::undefined(). The errorMessage parameter gives you a way to find out
any parsing errors.
You can specify a maximum time for which the program is allowed to run, and
it'll return with an error message if this time is exceeded.
*/
var evaluate (const String& javascriptCode,
Result* errorMessage = nullptr);
/** Calls a function in the root namespace, and returns the result.
The function arguments are passed in the same format as used by native
methods in the var class.
*/
var callFunction (const Identifier& function,
const var::NativeFunctionArgs& args,
Result* errorMessage = nullptr);
/** Calls a function object in the namespace of a dynamic object, and returns the result.
The function arguments are passed in the same format as used by native
methods in the var class.
*/
var callFunctionObject (DynamicObject* objectScope,
const var& functionObject,
const var::NativeFunctionArgs& args,
Result* errorMessage = nullptr);
/** Adds a native object to the root namespace.
The object passed-in is reference-counted, and will be retained by the
engine until the engine is deleted. The name must be a simple JS identifier,
without any dots.
*/
void registerNativeObject (const Identifier& objectName, DynamicObject* object);
/** This value indicates how long a call to one of the evaluate methods is permitted
to run before timing-out and failing.
The default value is a number of seconds, but you can change this to whatever value
suits your application.
*/
RelativeTime maximumExecutionTime;
/** When called from another thread, causes the interpreter to time-out as soon as possible */
void stop() noexcept;
/** Provides access to the set of properties of the root namespace object. */
const NamedValueSet& getRootObjectProperties() const noexcept;
private:
JUCE_PUBLIC_IN_DLL_BUILD (struct RootObject)
const ReferenceCountedObjectPtr<RootObject> root;
void prepareTimeout() const noexcept;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (JavascriptEngine)
};
} // namespace juce

View File

@ -0,0 +1,284 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
#ifdef JUCE_CORE_H_INCLUDED
/* When you add this cpp file to your project, you mustn't include it in a file where you've
already included any other headers - just put it inside a file on its own, possibly with your config
flags preceding it, but don't include anything else. That also includes avoiding any automatic prefix
header files that the compiler may be using.
*/
#error "Incorrect use of JUCE cpp file"
#endif
#define JUCE_CORE_INCLUDE_OBJC_HELPERS 1
#define JUCE_CORE_INCLUDE_COM_SMART_PTR 1
#define JUCE_CORE_INCLUDE_NATIVE_HEADERS 1
#define JUCE_CORE_INCLUDE_JNI_HELPERS 1
#include "juce_core.h"
#include <cctype>
#include <cstdarg>
#include <locale>
#include <thread>
#if ! JUCE_ANDROID
#include <sys/timeb.h>
#include <cwctype>
#endif
#if JUCE_WINDOWS
#include <ctime>
#if JUCE_MINGW
#include <ws2spi.h>
#include <cstdio>
#include <locale.h>
#else
JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4091)
#include <Dbghelp.h>
JUCE_END_IGNORE_WARNINGS_MSVC
#if ! JUCE_DONT_AUTOLINK_TO_WIN32_LIBRARIES
#pragma comment (lib, "DbgHelp.lib")
#endif
#endif
#else
#if JUCE_LINUX || JUCE_BSD || JUCE_ANDROID
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/errno.h>
#include <unistd.h>
#include <netinet/in.h>
#endif
#if JUCE_WASM
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <errno.h>
#include <unistd.h>
#include <netinet/in.h>
#include <sys/stat.h>
#endif
#if JUCE_LINUX || JUCE_BSD
#include <stdio.h>
#include <langinfo.h>
#include <ifaddrs.h>
#include <sys/resource.h>
#if JUCE_USE_CURL
#include <curl/curl.h>
#endif
#endif
#include <pwd.h>
#include <fcntl.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <netinet/tcp.h>
#include <sys/time.h>
#include <net/if.h>
#include <sys/ioctl.h>
#if ! (JUCE_ANDROID || JUCE_WASM)
#include <execinfo.h>
#endif
#endif
#if JUCE_MAC || JUCE_IOS
#include <xlocale.h>
#include <mach/mach.h>
#endif
#if JUCE_ANDROID
#include <ifaddrs.h>
#include <android/log.h>
#endif
#undef check
//==============================================================================
#ifndef JUCE_STANDALONE_APPLICATION
JUCE_COMPILER_WARNING ("Please re-save your project with the latest Projucer version to avoid this warning")
#define JUCE_STANDALONE_APPLICATION 0
#endif
//==============================================================================
#include "containers/juce_AbstractFifo.cpp"
#include "containers/juce_ArrayBase.cpp"
#include "containers/juce_NamedValueSet.cpp"
#include "containers/juce_OwnedArray.cpp"
#include "containers/juce_PropertySet.cpp"
#include "containers/juce_ReferenceCountedArray.cpp"
#include "containers/juce_SparseSet.cpp"
#include "files/juce_DirectoryIterator.cpp"
#include "files/juce_RangedDirectoryIterator.cpp"
#include "files/juce_File.cpp"
#include "files/juce_FileInputStream.cpp"
#include "files/juce_FileOutputStream.cpp"
#include "files/juce_FileSearchPath.cpp"
#include "files/juce_TemporaryFile.cpp"
#include "logging/juce_FileLogger.cpp"
#include "logging/juce_Logger.cpp"
#include "maths/juce_BigInteger.cpp"
#include "maths/juce_Expression.cpp"
#include "maths/juce_Random.cpp"
#include "memory/juce_MemoryBlock.cpp"
#include "memory/juce_AllocationHooks.cpp"
#include "misc/juce_RuntimePermissions.cpp"
#include "misc/juce_Result.cpp"
#include "misc/juce_Uuid.cpp"
#include "misc/juce_ConsoleApplication.cpp"
#include "network/juce_MACAddress.cpp"
#include "network/juce_NamedPipe.cpp"
#include "network/juce_Socket.cpp"
#include "network/juce_IPAddress.cpp"
#include "streams/juce_BufferedInputStream.cpp"
#include "streams/juce_FileInputSource.cpp"
#include "streams/juce_InputStream.cpp"
#include "streams/juce_MemoryInputStream.cpp"
#include "streams/juce_MemoryOutputStream.cpp"
#include "streams/juce_SubregionStream.cpp"
#include "system/juce_SystemStats.cpp"
#include "text/juce_CharacterFunctions.cpp"
#include "text/juce_Identifier.cpp"
#include "text/juce_LocalisedStrings.cpp"
#include "text/juce_String.cpp"
#include "streams/juce_OutputStream.cpp"
#include "text/juce_StringArray.cpp"
#include "text/juce_StringPairArray.cpp"
#include "text/juce_StringPool.cpp"
#include "text/juce_TextDiff.cpp"
#include "text/juce_Base64.cpp"
#include "threads/juce_ReadWriteLock.cpp"
#include "threads/juce_Thread.cpp"
#include "threads/juce_ThreadPool.cpp"
#include "threads/juce_TimeSliceThread.cpp"
#include "time/juce_PerformanceCounter.cpp"
#include "time/juce_RelativeTime.cpp"
#include "time/juce_Time.cpp"
#include "unit_tests/juce_UnitTest.cpp"
#include "containers/juce_Variant.cpp"
#include "javascript/juce_JSON.cpp"
#include "javascript/juce_Javascript.cpp"
#include "containers/juce_DynamicObject.cpp"
#include "xml/juce_XmlDocument.cpp"
#include "xml/juce_XmlElement.cpp"
#include "zip/juce_GZIPDecompressorInputStream.cpp"
#include "zip/juce_GZIPCompressorOutputStream.cpp"
#include "zip/juce_ZipFile.cpp"
#include "files/juce_FileFilter.cpp"
#include "files/juce_WildcardFileFilter.cpp"
//==============================================================================
#if ! JUCE_WINDOWS
#include "native/juce_posix_SharedCode.h"
#include "native/juce_posix_NamedPipe.cpp"
#if ! JUCE_ANDROID || __ANDROID_API__ >= 24
#include "native/juce_posix_IPAddress.h"
#endif
#endif
//==============================================================================
#if JUCE_MAC || JUCE_IOS
#include "native/juce_mac_Files.mm"
#include "native/juce_mac_Network.mm"
#include "native/juce_mac_Strings.mm"
#include "native/juce_intel_SharedCode.h"
#include "native/juce_mac_SystemStats.mm"
#include "native/juce_mac_Threads.mm"
//==============================================================================
#elif JUCE_WINDOWS
#include "native/juce_win32_Files.cpp"
#include "native/juce_win32_Network.cpp"
#include "native/juce_win32_Registry.cpp"
#include "native/juce_win32_SystemStats.cpp"
#include "native/juce_win32_Threads.cpp"
//==============================================================================
#elif JUCE_LINUX || JUCE_BSD
#include "native/juce_linux_CommonFile.cpp"
#include "native/juce_linux_Files.cpp"
#include "native/juce_linux_Network.cpp"
#if JUCE_USE_CURL
#include "native/juce_curl_Network.cpp"
#endif
#if JUCE_BSD
#include "native/juce_intel_SharedCode.h"
#endif
#include "native/juce_linux_SystemStats.cpp"
#include "native/juce_linux_Threads.cpp"
//==============================================================================
#elif JUCE_ANDROID
#include "native/juce_linux_CommonFile.cpp"
#include "native/juce_android_JNIHelpers.cpp"
#include "native/juce_android_Files.cpp"
#include "native/juce_android_Misc.cpp"
#include "native/juce_android_Network.cpp"
#include "native/juce_android_SystemStats.cpp"
#include "native/juce_android_Threads.cpp"
#include "native/juce_android_RuntimePermissions.cpp"
#elif JUCE_WASM
#include "native/juce_wasm_SystemStats.cpp"
#endif
#include "threads/juce_HighResolutionTimer.cpp"
#include "threads/juce_WaitableEvent.cpp"
#include "network/juce_URL.cpp"
#if ! JUCE_WASM
#include "threads/juce_ChildProcess.cpp"
#include "network/juce_WebInputStream.cpp"
#include "streams/juce_URLInputSource.cpp"
#endif
//==============================================================================
#if JUCE_UNIT_TESTS
#include "containers/juce_HashMap_test.cpp"
#endif
//==============================================================================
namespace juce
{
/*
As the very long class names here try to explain, the purpose of this code is to cause
a linker error if not all of your compile units are consistent in the options that they
enable before including JUCE headers. The reason this is important is that if you have
two cpp files, and one includes the juce headers with debug enabled, and the other doesn't,
then each will be generating code with different memory layouts for the classes, and
you'll get subtle and hard-to-track-down memory corruption bugs!
*/
#if JUCE_DEBUG
this_will_fail_to_link_if_some_of_your_compile_units_are_built_in_debug_mode
::this_will_fail_to_link_if_some_of_your_compile_units_are_built_in_debug_mode() noexcept {}
#else
this_will_fail_to_link_if_some_of_your_compile_units_are_built_in_release_mode
::this_will_fail_to_link_if_some_of_your_compile_units_are_built_in_release_mode() noexcept {}
#endif
}

391
deps/juce/modules/juce_core/juce_core.h vendored Normal file
View File

@ -0,0 +1,391 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
/*******************************************************************************
The block below describes the properties of this module, and is read by
the Projucer to automatically generate project code that uses it.
For details about the syntax and how to create or use a module, see the
JUCE Module Format.md file.
BEGIN_JUCE_MODULE_DECLARATION
ID: juce_core
vendor: juce
version: 6.1.2
name: JUCE core classes
description: The essential set of basic JUCE classes, as required by all the other JUCE modules. Includes text, container, memory, threading and i/o functionality.
website: http://www.juce.com/juce
license: ISC
minimumCppStandard: 14
dependencies:
OSXFrameworks: Cocoa Foundation IOKit
iOSFrameworks: Foundation
linuxLibs: rt dl pthread
mingwLibs: uuid wsock32 wininet version ole32 ws2_32 oleaut32 imm32 comdlg32 shlwapi rpcrt4 winmm
END_JUCE_MODULE_DECLARATION
*******************************************************************************/
#pragma once
#define JUCE_CORE_H_INCLUDED
//==============================================================================
#ifdef _MSC_VER
#pragma warning (push)
// Disable warnings for long class names, padding, and undefined preprocessor definitions.
#pragma warning (disable: 4251 4786 4668 4820)
#ifdef __INTEL_COMPILER
#pragma warning (disable: 1125)
#endif
#endif
#include "system/juce_TargetPlatform.h"
//==============================================================================
/** Config: JUCE_FORCE_DEBUG
Normally, JUCE_DEBUG is set to 1 or 0 based on compiler and project settings,
but if you define this value, you can override this to force it to be true or false.
*/
#ifndef JUCE_FORCE_DEBUG
//#define JUCE_FORCE_DEBUG 0
#endif
//==============================================================================
/** Config: JUCE_LOG_ASSERTIONS
If this flag is enabled, the jassert and jassertfalse macros will always use Logger::writeToLog()
to write a message when an assertion happens.
Enabling it will also leave this turned on in release builds. When it's disabled,
however, the jassert and jassertfalse macros will not be compiled in a
release build.
@see jassert, jassertfalse, Logger
*/
#ifndef JUCE_LOG_ASSERTIONS
#if JUCE_ANDROID
#define JUCE_LOG_ASSERTIONS 1
#else
#define JUCE_LOG_ASSERTIONS 0
#endif
#endif
//==============================================================================
/** Config: JUCE_CHECK_MEMORY_LEAKS
Enables a memory-leak check for certain objects when the app terminates. See the LeakedObjectDetector
class and the JUCE_LEAK_DETECTOR macro for more details about enabling leak checking for specific classes.
*/
#if JUCE_DEBUG && ! defined (JUCE_CHECK_MEMORY_LEAKS)
#define JUCE_CHECK_MEMORY_LEAKS 1
#endif
//==============================================================================
/** Config: JUCE_DONT_AUTOLINK_TO_WIN32_LIBRARIES
In a Windows build, this can be used to stop the required system libs being
automatically added to the link stage.
*/
#ifndef JUCE_DONT_AUTOLINK_TO_WIN32_LIBRARIES
#define JUCE_DONT_AUTOLINK_TO_WIN32_LIBRARIES 0
#endif
/** Config: JUCE_INCLUDE_ZLIB_CODE
This can be used to disable Juce's embedded 3rd-party zlib code.
You might need to tweak this if you're linking to an external zlib library in your app,
but for normal apps, this option should be left alone.
If you disable this, you might also want to set a value for JUCE_ZLIB_INCLUDE_PATH, to
specify the path where your zlib headers live.
*/
#ifndef JUCE_INCLUDE_ZLIB_CODE
#define JUCE_INCLUDE_ZLIB_CODE 1
#endif
#ifndef JUCE_ZLIB_INCLUDE_PATH
#define JUCE_ZLIB_INCLUDE_PATH <zlib.h>
#endif
/** Config: JUCE_USE_CURL
Enables http/https support via libcurl (Linux only). Enabling this will add an additional
run-time dynamic dependency to libcurl.
If you disable this then https/ssl support will not be available on Linux.
*/
#ifndef JUCE_USE_CURL
#define JUCE_USE_CURL 1
#endif
/** Config: JUCE_LOAD_CURL_SYMBOLS_LAZILY
If enabled, JUCE will load libcurl lazily when required (for example, when WebInputStream
is used). Enabling this flag may also help with library dependency errors as linking
libcurl at compile-time may instruct the linker to hard depend on a specific version
of libcurl. It's also useful if you want to limit the amount of JUCE dependencies and
you are not using WebInputStream or the URL classes.
*/
#ifndef JUCE_LOAD_CURL_SYMBOLS_LAZILY
#define JUCE_LOAD_CURL_SYMBOLS_LAZILY 0
#endif
/** Config: JUCE_CATCH_UNHANDLED_EXCEPTIONS
If enabled, this will add some exception-catching code to forward unhandled exceptions
to your JUCEApplicationBase::unhandledException() callback.
*/
#ifndef JUCE_CATCH_UNHANDLED_EXCEPTIONS
#define JUCE_CATCH_UNHANDLED_EXCEPTIONS 0
#endif
/** Config: JUCE_ALLOW_STATIC_NULL_VARIABLES
If disabled, this will turn off dangerous static globals like String::empty, var::null, etc
which can cause nasty order-of-initialisation problems if they are referenced during static
constructor code.
*/
#ifndef JUCE_ALLOW_STATIC_NULL_VARIABLES
#define JUCE_ALLOW_STATIC_NULL_VARIABLES 0
#endif
/** Config: JUCE_STRICT_REFCOUNTEDPOINTER
If enabled, this will make the ReferenceCountedObjectPtr class stricter about allowing
itself to be cast directly to a raw pointer. By default this is disabled, for compatibility
with old code, but if possible, you should always enable it to improve code safety!
*/
#ifndef JUCE_STRICT_REFCOUNTEDPOINTER
#define JUCE_STRICT_REFCOUNTEDPOINTER 0
#endif
/** Config: JUCE_ENABLE_ALLOCATION_HOOKS
If enabled, this will add global allocation functions with built-in assertions, which may
help when debugging allocations in unit tests.
*/
#ifndef JUCE_ENABLE_ALLOCATION_HOOKS
#define JUCE_ENABLE_ALLOCATION_HOOKS 0
#endif
#ifndef JUCE_STRING_UTF_TYPE
#define JUCE_STRING_UTF_TYPE 8
#endif
//==============================================================================
//==============================================================================
#if JUCE_CORE_INCLUDE_NATIVE_HEADERS
#include "native/juce_BasicNativeHeaders.h"
#endif
#if JUCE_WINDOWS
#undef small
#endif
#include "system/juce_StandardHeader.h"
namespace juce
{
class StringRef;
class MemoryBlock;
class File;
class InputStream;
class OutputStream;
class DynamicObject;
class FileInputStream;
class FileOutputStream;
class XmlElement;
extern JUCE_API bool JUCE_CALLTYPE juce_isRunningUnderDebugger() noexcept;
extern JUCE_API void JUCE_CALLTYPE logAssertion (const char* file, int line) noexcept;
}
#include "memory/juce_Memory.h"
#include "maths/juce_MathsFunctions.h"
#include "memory/juce_ByteOrder.h"
#include "memory/juce_Atomic.h"
#include "text/juce_CharacterFunctions.h"
JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4514 4996)
#include "text/juce_CharPointer_UTF8.h"
#include "text/juce_CharPointer_UTF16.h"
#include "text/juce_CharPointer_UTF32.h"
#include "text/juce_CharPointer_ASCII.h"
JUCE_END_IGNORE_WARNINGS_MSVC
#include "text/juce_String.h"
#include "text/juce_StringRef.h"
#include "logging/juce_Logger.h"
#include "memory/juce_LeakedObjectDetector.h"
#include "memory/juce_ContainerDeletePolicy.h"
#include "memory/juce_HeapBlock.h"
#include "memory/juce_MemoryBlock.h"
#include "memory/juce_ReferenceCountedObject.h"
#include "memory/juce_ScopedPointer.h"
#include "memory/juce_OptionalScopedPointer.h"
#include "memory/juce_Singleton.h"
#include "memory/juce_WeakReference.h"
#include "threads/juce_ScopedLock.h"
#include "threads/juce_CriticalSection.h"
#include "maths/juce_Range.h"
#include "maths/juce_NormalisableRange.h"
#include "maths/juce_StatisticsAccumulator.h"
#include "containers/juce_ElementComparator.h"
#include "containers/juce_ArrayAllocationBase.h"
#include "containers/juce_ArrayBase.h"
#include "containers/juce_Array.h"
#include "containers/juce_LinkedListPointer.h"
#include "containers/juce_ListenerList.h"
#include "containers/juce_OwnedArray.h"
#include "containers/juce_ReferenceCountedArray.h"
#include "containers/juce_ScopedValueSetter.h"
#include "containers/juce_SortedSet.h"
#include "containers/juce_SparseSet.h"
#include "containers/juce_AbstractFifo.h"
#include "containers/juce_SingleThreadedAbstractFifo.h"
#include "text/juce_NewLine.h"
#include "text/juce_StringPool.h"
#include "text/juce_Identifier.h"
#include "text/juce_StringArray.h"
#include "system/juce_SystemStats.h"
#include "memory/juce_HeavyweightLeakedObjectDetector.h"
#include "text/juce_StringPairArray.h"
#include "text/juce_TextDiff.h"
#include "text/juce_LocalisedStrings.h"
#include "text/juce_Base64.h"
#include "misc/juce_Functional.h"
#include "misc/juce_Result.h"
#include "misc/juce_Uuid.h"
#include "misc/juce_ConsoleApplication.h"
#include "containers/juce_Variant.h"
#include "containers/juce_NamedValueSet.h"
#include "containers/juce_DynamicObject.h"
#include "containers/juce_HashMap.h"
#include "time/juce_RelativeTime.h"
#include "time/juce_Time.h"
#include "streams/juce_InputStream.h"
#include "streams/juce_OutputStream.h"
#include "streams/juce_BufferedInputStream.h"
#include "streams/juce_MemoryInputStream.h"
#include "streams/juce_MemoryOutputStream.h"
#include "streams/juce_SubregionStream.h"
#include "streams/juce_InputSource.h"
#include "files/juce_File.h"
#include "files/juce_DirectoryIterator.h"
#include "files/juce_RangedDirectoryIterator.h"
#include "files/juce_FileInputStream.h"
#include "files/juce_FileOutputStream.h"
#include "files/juce_FileSearchPath.h"
#include "files/juce_MemoryMappedFile.h"
#include "files/juce_TemporaryFile.h"
#include "files/juce_FileFilter.h"
#include "files/juce_WildcardFileFilter.h"
#include "streams/juce_FileInputSource.h"
#include "logging/juce_FileLogger.h"
#include "javascript/juce_JSON.h"
#include "javascript/juce_Javascript.h"
#include "maths/juce_BigInteger.h"
#include "maths/juce_Expression.h"
#include "maths/juce_Random.h"
#include "misc/juce_RuntimePermissions.h"
#include "misc/juce_WindowsRegistry.h"
#include "threads/juce_ChildProcess.h"
#include "threads/juce_DynamicLibrary.h"
#include "threads/juce_HighResolutionTimer.h"
#include "threads/juce_InterProcessLock.h"
#include "threads/juce_Process.h"
#include "threads/juce_SpinLock.h"
#include "threads/juce_WaitableEvent.h"
#include "threads/juce_Thread.h"
#include "threads/juce_ThreadLocalValue.h"
#include "threads/juce_ThreadPool.h"
#include "threads/juce_TimeSliceThread.h"
#include "threads/juce_ReadWriteLock.h"
#include "threads/juce_ScopedReadLock.h"
#include "threads/juce_ScopedWriteLock.h"
#include "network/juce_IPAddress.h"
#include "network/juce_MACAddress.h"
#include "network/juce_NamedPipe.h"
#include "network/juce_Socket.h"
#include "network/juce_URL.h"
#include "network/juce_WebInputStream.h"
#include "streams/juce_URLInputSource.h"
#include "time/juce_PerformanceCounter.h"
#include "unit_tests/juce_UnitTest.h"
#include "xml/juce_XmlDocument.h"
#include "xml/juce_XmlElement.h"
#include "zip/juce_GZIPCompressorOutputStream.h"
#include "zip/juce_GZIPDecompressorInputStream.h"
#include "zip/juce_ZipFile.h"
#include "containers/juce_PropertySet.h"
#include "memory/juce_SharedResourcePointer.h"
#include "memory/juce_AllocationHooks.h"
#include "memory/juce_Reservoir.h"
#if JUCE_CORE_INCLUDE_OBJC_HELPERS && (JUCE_MAC || JUCE_IOS)
#include "native/juce_mac_ObjCHelpers.h"
#endif
#if JUCE_CORE_INCLUDE_COM_SMART_PTR && JUCE_WINDOWS
#include "native/juce_win32_ComSmartPtr.h"
#endif
#if JUCE_CORE_INCLUDE_JNI_HELPERS && JUCE_ANDROID
#include <jni.h>
#include "native/juce_android_JNIHelpers.h"
#endif
#if JUCE_UNIT_TESTS
#include "unit_tests/juce_UnitTestCategories.h"
#endif
#ifndef DOXYGEN
namespace juce
{
/*
As the very long class names here try to explain, the purpose of this code is to cause
a linker error if not all of your compile units are consistent in the options that they
enable before including JUCE headers. The reason this is important is that if you have
two cpp files, and one includes the juce headers with debug enabled, and another does so
without that, then each will be generating code with different class layouts, and you'll
get subtle and hard-to-track-down memory corruption!
*/
#if JUCE_DEBUG
struct JUCE_API this_will_fail_to_link_if_some_of_your_compile_units_are_built_in_debug_mode
{ this_will_fail_to_link_if_some_of_your_compile_units_are_built_in_debug_mode() noexcept; };
static this_will_fail_to_link_if_some_of_your_compile_units_are_built_in_debug_mode compileUnitMismatchSentinel;
#else
struct JUCE_API this_will_fail_to_link_if_some_of_your_compile_units_are_built_in_release_mode
{ this_will_fail_to_link_if_some_of_your_compile_units_are_built_in_release_mode() noexcept; };
static this_will_fail_to_link_if_some_of_your_compile_units_are_built_in_release_mode compileUnitMismatchSentinel;
#endif
}
#endif
JUCE_END_IGNORE_WARNINGS_MSVC
// In DLL builds, need to disable this warnings for other modules
#if defined (JUCE_DLL_BUILD) || defined (JUCE_DLL)
JUCE_IGNORE_MSVC (4251)
#endif

View File

@ -0,0 +1,23 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
#include "juce_core.cpp"

View File

@ -0,0 +1,133 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
FileLogger::FileLogger (const File& file,
const String& welcomeMessage,
const int64 maxInitialFileSizeBytes)
: logFile (file)
{
if (maxInitialFileSizeBytes >= 0)
trimFileSize (logFile, maxInitialFileSizeBytes);
if (! file.exists())
file.create(); // (to create the parent directories)
String welcome;
welcome << newLine
<< "**********************************************************" << newLine
<< welcomeMessage << newLine
<< "Log started: " << Time::getCurrentTime().toString (true, true) << newLine;
FileLogger::logMessage (welcome);
}
FileLogger::~FileLogger() {}
//==============================================================================
void FileLogger::logMessage (const String& message)
{
const ScopedLock sl (logLock);
DBG (message);
FileOutputStream out (logFile, 256);
out << message << newLine;
}
void FileLogger::trimFileSize (const File& file, int64 maxFileSizeBytes)
{
if (maxFileSizeBytes <= 0)
{
file.deleteFile();
}
else
{
const int64 fileSize = file.getSize();
if (fileSize > maxFileSizeBytes)
{
TemporaryFile tempFile (file);
{
FileOutputStream out (tempFile.getFile());
FileInputStream in (file);
if (! (out.openedOk() && in.openedOk()))
return;
in.setPosition (fileSize - maxFileSizeBytes);
for (;;)
{
const char c = in.readByte();
if (c == 0)
return;
if (c == '\n' || c == '\r')
{
out << c;
break;
}
}
out.writeFromInputStream (in, -1);
}
tempFile.overwriteTargetFileWithTemporary();
}
}
}
//==============================================================================
File FileLogger::getSystemLogFileFolder()
{
#if JUCE_MAC
return File ("~/Library/Logs");
#else
return File::getSpecialLocation (File::userApplicationDataDirectory);
#endif
}
FileLogger* FileLogger::createDefaultAppLogger (const String& logFileSubDirectoryName,
const String& logFileName,
const String& welcomeMessage,
const int64 maxInitialFileSizeBytes)
{
return new FileLogger (getSystemLogFileFolder().getChildFile (logFileSubDirectoryName)
.getChildFile (logFileName),
welcomeMessage, maxInitialFileSizeBytes);
}
FileLogger* FileLogger::createDateStampedLogger (const String& logFileSubDirectoryName,
const String& logFileNameRoot,
const String& logFileNameSuffix,
const String& welcomeMessage)
{
return new FileLogger (getSystemLogFileFolder().getChildFile (logFileSubDirectoryName)
.getChildFile (logFileNameRoot + Time::getCurrentTime().formatted ("%Y-%m-%d_%H-%M-%S"))
.withFileExtension (logFileNameSuffix)
.getNonexistentSibling(),
welcomeMessage, 0);
}
} // namespace juce

View File

@ -0,0 +1,133 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
/**
A simple implementation of a Logger that writes to a file.
@see Logger
@tags{Core}
*/
class JUCE_API FileLogger : public Logger
{
public:
//==============================================================================
/** Creates a FileLogger for a given file.
@param fileToWriteTo the file that to use - new messages will be appended
to the file. If the file doesn't exist, it will be created,
along with any parent directories that are needed.
@param welcomeMessage when opened, the logger will write a header to the log, along
with the current date and time, and this welcome message
@param maxInitialFileSizeBytes if this is zero or greater, then if the file already exists
but is larger than this number of bytes, then the start of the
file will be truncated to keep the size down. This prevents a log
file getting ridiculously large over time. The file will be truncated
at a new-line boundary. If this value is less than zero, no size limit
will be imposed; if it's zero, the file will always be deleted. Note that
the size is only checked once when this object is created - any logging
that is done later will be appended without any checking
*/
FileLogger (const File& fileToWriteTo,
const String& welcomeMessage,
const int64 maxInitialFileSizeBytes = 128 * 1024);
/** Destructor. */
~FileLogger() override;
//==============================================================================
/** Returns the file that this logger is writing to. */
const File& getLogFile() const noexcept { return logFile; }
//==============================================================================
/** Helper function to create a log file in the correct place for this platform.
The method might return nullptr if the file can't be created for some reason.
@param logFileSubDirectoryName the name of the subdirectory to create inside the logs folder (as
returned by getSystemLogFileFolder). It's best to use something
like the name of your application here.
@param logFileName the name of the file to create, e.g. "MyAppLog.txt".
@param welcomeMessage a message that will be written to the log when it's opened.
@param maxInitialFileSizeBytes (see the FileLogger constructor for more info on this)
*/
static FileLogger* createDefaultAppLogger (const String& logFileSubDirectoryName,
const String& logFileName,
const String& welcomeMessage,
const int64 maxInitialFileSizeBytes = 128 * 1024);
/** Helper function to create a log file in the correct place for this platform.
The filename used is based on the root and suffix strings provided, along with a
time and date string, meaning that a new, empty log file will be always be created
rather than appending to an existing one.
The method might return nullptr if the file can't be created for some reason.
@param logFileSubDirectoryName the name of the subdirectory to create inside the logs folder (as
returned by getSystemLogFileFolder). It's best to use something
like the name of your application here.
@param logFileNameRoot the start of the filename to use, e.g. "MyAppLog_". This will have
a timestamp and the logFileNameSuffix appended to it
@param logFileNameSuffix the file suffix to use, e.g. ".txt"
@param welcomeMessage a message that will be written to the log when it's opened.
*/
static FileLogger* createDateStampedLogger (const String& logFileSubDirectoryName,
const String& logFileNameRoot,
const String& logFileNameSuffix,
const String& welcomeMessage);
//==============================================================================
/** Returns an OS-specific folder where log-files should be stored.
On Windows this will return a logger with a path such as:
c:\\Documents and Settings\\username\\Application Data\\[logFileSubDirectoryName]\\[logFileName]
On the Mac it'll create something like:
~/Library/Logs/[logFileSubDirectoryName]/[logFileName]
@see createDefaultAppLogger
*/
static File getSystemLogFileFolder();
// (implementation of the Logger virtual method)
void logMessage (const String&) override;
//==============================================================================
/** This is a utility function which removes lines from the start of a text
file to make sure that its total size is below the given size.
*/
static void trimFileSize (const File& file, int64 maxFileSize);
private:
//==============================================================================
File logFile;
CriticalSection logLock;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FileLogger)
};
} // namespace juce

View File

@ -0,0 +1,62 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
Logger::Logger() {}
Logger::~Logger()
{
// You're deleting this logger while it's still being used!
// Always call Logger::setCurrentLogger (nullptr) before deleting the active logger.
jassert (currentLogger != this);
}
Logger* Logger::currentLogger = nullptr;
void Logger::setCurrentLogger (Logger* const newLogger) noexcept { currentLogger = newLogger; }
Logger* Logger::getCurrentLogger() noexcept { return currentLogger; }
void Logger::writeToLog (const String& message)
{
if (currentLogger != nullptr)
currentLogger->logMessage (message);
else
outputDebugString (message);
}
#if JUCE_LOG_ASSERTIONS || JUCE_DEBUG
void JUCE_API JUCE_CALLTYPE logAssertion (const char* const filename, const int lineNum) noexcept
{
String m ("JUCE Assertion failure in ");
m << File::createFileWithoutCheckingPath (CharPointer_UTF8 (filename)).getFileName() << ':' << lineNum;
#if JUCE_LOG_ASSERTIONS
Logger::writeToLog (m);
#else
DBG (m);
#endif
}
#endif
} // namespace juce

View File

@ -0,0 +1,91 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
/**
Acts as an application-wide logging class.
A subclass of Logger can be created and passed into the Logger::setCurrentLogger
method and this will then be used by all calls to writeToLog.
The logger class also contains methods for writing messages to the debugger's
output stream.
@see FileLogger
@tags{Core}
*/
class JUCE_API Logger
{
public:
//==============================================================================
/** Destructor. */
virtual ~Logger();
//==============================================================================
/** Sets the current logging class to use.
Note that the object passed in will not be owned or deleted by the logger, so
the caller must make sure that it is not deleted while still being used.
A null pointer can be passed-in to reset the system to the default logger.
*/
static void JUCE_CALLTYPE setCurrentLogger (Logger* newLogger) noexcept;
/** Returns the current logger, or nullptr if no custom logger has been set. */
static Logger* JUCE_CALLTYPE getCurrentLogger() noexcept;
/** Writes a string to the current logger.
This will pass the string to the logger's logMessage() method if a logger
has been set.
@see logMessage
*/
static void JUCE_CALLTYPE writeToLog (const String& message);
//==============================================================================
/** Writes a message to the standard error stream.
This can be called directly, or by using the DBG() macro in
juce_PlatformDefs.h (which will avoid calling the method in non-debug builds).
*/
static void JUCE_CALLTYPE outputDebugString (const String& text);
protected:
//==============================================================================
Logger();
/** This is overloaded by subclasses to implement custom logging behaviour.
@see setCurrentLogger
*/
virtual void logMessage (const String& message) = 0;
private:
static Logger* currentLogger;
};
} // namespace juce

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,342 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
/**
An arbitrarily large integer class.
A BigInteger can be used in a similar way to a normal integer, but has no size
limit (except for memory and performance constraints).
Negative values are possible, but the value isn't stored as 2s-complement, so
be careful if you use negative values and look at the values of individual bits.
@tags{Core}
*/
class JUCE_API BigInteger
{
public:
//==============================================================================
/** Creates an empty BigInteger */
BigInteger();
/** Creates a BigInteger containing an integer value in its low bits.
The low 32 bits of the number are initialised with this value.
*/
BigInteger (uint32 value);
/** Creates a BigInteger containing an integer value in its low bits.
The low 32 bits of the number are initialised with the absolute value
passed in, and its sign is set to reflect the sign of the number.
*/
BigInteger (int32 value);
/** Creates a BigInteger containing an integer value in its low bits.
The low 64 bits of the number are initialised with the absolute value
passed in, and its sign is set to reflect the sign of the number.
*/
BigInteger (int64 value);
/** Creates a copy of another BigInteger. */
BigInteger (const BigInteger&);
/** Move constructor */
BigInteger (BigInteger&&) noexcept;
/** Move assignment operator */
BigInteger& operator= (BigInteger&&) noexcept;
/** Destructor. */
~BigInteger();
//==============================================================================
/** Copies another BigInteger onto this one. */
BigInteger& operator= (const BigInteger&);
/** Swaps the internal contents of this with another object. */
void swapWith (BigInteger&) noexcept;
//==============================================================================
/** Returns the value of a specified bit in the number.
If the index is out-of-range, the result will be false.
*/
bool operator[] (int bit) const noexcept;
/** Returns true if no bits are set. */
bool isZero() const noexcept;
/** Returns true if the value is 1. */
bool isOne() const noexcept;
/** Attempts to get the lowest 32 bits of the value as an integer.
If the value is bigger than the integer limits, this will return only the lower bits.
*/
int toInteger() const noexcept;
/** Attempts to get the lowest 64 bits of the value as an integer.
If the value is bigger than the integer limits, this will return only the lower bits.
*/
int64 toInt64() const noexcept;
//==============================================================================
/** Resets the value to 0. */
void clear() noexcept;
/** Clears a particular bit in the number. */
void clearBit (int bitNumber) noexcept;
/** Sets a specified bit to 1. */
void setBit (int bitNumber);
/** Sets or clears a specified bit. */
void setBit (int bitNumber, bool shouldBeSet);
/** Sets a range of bits to be either on or off.
@param startBit the first bit to change
@param numBits the number of bits to change
@param shouldBeSet whether to turn these bits on or off
*/
void setRange (int startBit, int numBits, bool shouldBeSet);
/** Inserts a bit an a given position, shifting up any bits above it. */
void insertBit (int bitNumber, bool shouldBeSet);
/** Returns a range of bits as a new BigInteger.
e.g. getBitRangeAsInt (0, 64) would return the lowest 64 bits.
@see getBitRangeAsInt
*/
BigInteger getBitRange (int startBit, int numBits) const;
/** Returns a range of bits as an integer value.
e.g. getBitRangeAsInt (0, 32) would return the lowest 32 bits.
Asking for more than 32 bits isn't allowed (obviously) - for that, use
getBitRange().
*/
uint32 getBitRangeAsInt (int startBit, int numBits) const noexcept;
/** Sets a range of bits to an integer value.
Copies the given integer onto a range of bits, starting at startBit,
and using up to numBits of the available bits.
*/
void setBitRangeAsInt (int startBit, int numBits, uint32 valueToSet);
/** Shifts a section of bits left or right.
@param howManyBitsLeft how far to move the bits (+ve numbers shift it left, -ve numbers shift it right).
@param startBit the first bit to affect - if this is > 0, only bits above that index will be affected.
*/
void shiftBits (int howManyBitsLeft, int startBit);
/** Returns the total number of set bits in the value. */
int countNumberOfSetBits() const noexcept;
/** Looks for the index of the next set bit after a given starting point.
This searches from startIndex (inclusive) upwards for the first set bit,
and returns its index. If no set bits are found, it returns -1.
*/
int findNextSetBit (int startIndex) const noexcept;
/** Looks for the index of the next clear bit after a given starting point.
This searches from startIndex (inclusive) upwards for the first clear bit,
and returns its index.
*/
int findNextClearBit (int startIndex) const noexcept;
/** Returns the index of the highest set bit in the number.
If the value is zero, this will return -1.
*/
int getHighestBit() const noexcept;
//==============================================================================
/** Returns true if the value is less than zero.
@see setNegative, negate
*/
bool isNegative() const noexcept;
/** Changes the sign of the number to be positive or negative.
@see isNegative, negate
*/
void setNegative (bool shouldBeNegative) noexcept;
/** Inverts the sign of the number.
@see isNegative, setNegative
*/
void negate() noexcept;
//==============================================================================
// All the standard arithmetic ops...
BigInteger& operator+= (const BigInteger&);
BigInteger& operator-= (const BigInteger&);
BigInteger& operator*= (const BigInteger&);
BigInteger& operator/= (const BigInteger&);
BigInteger& operator|= (const BigInteger&);
BigInteger& operator&= (const BigInteger&);
BigInteger& operator^= (const BigInteger&);
BigInteger& operator%= (const BigInteger&);
BigInteger& operator<<= (int numBitsToShift);
BigInteger& operator>>= (int numBitsToShift);
BigInteger& operator++();
BigInteger& operator--();
BigInteger operator++ (int);
BigInteger operator-- (int);
BigInteger operator-() const;
BigInteger operator+ (const BigInteger&) const;
BigInteger operator- (const BigInteger&) const;
BigInteger operator* (const BigInteger&) const;
BigInteger operator/ (const BigInteger&) const;
BigInteger operator| (const BigInteger&) const;
BigInteger operator& (const BigInteger&) const;
BigInteger operator^ (const BigInteger&) const;
BigInteger operator% (const BigInteger&) const;
BigInteger operator<< (int numBitsToShift) const;
BigInteger operator>> (int numBitsToShift) const;
bool operator== (const BigInteger&) const noexcept;
bool operator!= (const BigInteger&) const noexcept;
bool operator< (const BigInteger&) const noexcept;
bool operator<= (const BigInteger&) const noexcept;
bool operator> (const BigInteger&) const noexcept;
bool operator>= (const BigInteger&) const noexcept;
//==============================================================================
/** Does a signed comparison of two BigIntegers.
Return values are:
- 0 if the numbers are the same
- < 0 if this number is smaller than the other
- > 0 if this number is bigger than the other
*/
int compare (const BigInteger& other) const noexcept;
/** Compares the magnitudes of two BigIntegers, ignoring their signs.
Return values are:
- 0 if the numbers are the same
- < 0 if this number is smaller than the other
- > 0 if this number is bigger than the other
*/
int compareAbsolute (const BigInteger& other) const noexcept;
//==============================================================================
/** Divides this value by another one and returns the remainder.
This number is divided by other, leaving the quotient in this number,
with the remainder being copied to the other BigInteger passed in.
*/
void divideBy (const BigInteger& divisor, BigInteger& remainder);
/** Returns the largest value that will divide both this value and the argument. */
BigInteger findGreatestCommonDivisor (BigInteger other) const;
/** Performs a combined exponent and modulo operation.
This BigInteger's value becomes (this ^ exponent) % modulus.
*/
void exponentModulo (const BigInteger& exponent, const BigInteger& modulus);
/** Performs an inverse modulo on the value.
i.e. the result is (this ^ -1) mod (modulus).
*/
void inverseModulo (const BigInteger& modulus);
/** Performs the Montgomery Multiplication with modulo.
This object is left containing the result value: ((this * other) * R1) % modulus.
To get this result, we need modulus, modulusp and k such as R = 2^k, with
modulus * modulusp - R * R1 = GCD(modulus, R) = 1
*/
void montgomeryMultiplication (const BigInteger& other, const BigInteger& modulus,
const BigInteger& modulusp, int k);
/** Performs the Extended Euclidean algorithm.
This method will set the xOut and yOut arguments such that (a * xOut) - (b * yOut) = GCD (a, b).
On return, this object is left containing the value of the GCD.
*/
void extendedEuclidean (const BigInteger& a, const BigInteger& b,
BigInteger& xOut, BigInteger& yOut);
//==============================================================================
/** Converts the number to a string.
Specify a base such as 2 (binary), 8 (octal), 10 (decimal), 16 (hex).
If minimumNumCharacters is greater than 0, the returned string will be
padded with leading zeros to reach at least that length.
*/
String toString (int base, int minimumNumCharacters = 1) const;
/** Reads the numeric value from a string.
Specify a base such as 2 (binary), 8 (octal), 10 (decimal), 16 (hex).
Any invalid characters will be ignored.
*/
void parseString (StringRef text, int base);
//==============================================================================
/** Turns the number into a block of binary data.
The data is arranged as little-endian, so the first byte of data is the low 8 bits
of the number, and so on.
@see loadFromMemoryBlock
*/
MemoryBlock toMemoryBlock() const;
/** Converts a block of raw data into a number.
The data is arranged as little-endian, so the first byte of data is the low 8 bits
of the number, and so on.
@see toMemoryBlock
*/
void loadFromMemoryBlock (const MemoryBlock& data);
private:
//==============================================================================
enum { numPreallocatedInts = 4 };
HeapBlock<uint32> heapAllocation;
uint32 preallocated[numPreallocatedInts];
size_t allocatedSize;
int highestBit = -1;
bool negative = false;
uint32* getValues() const noexcept;
uint32* ensureSize (size_t);
void shiftLeft (int bits, int startBit);
void shiftRight (int bits, int startBit);
JUCE_LEAK_DETECTOR (BigInteger)
};
/** Writes a BigInteger to an OutputStream as a UTF8 decimal string. */
OutputStream& JUCE_CALLTYPE operator<< (OutputStream& stream, const BigInteger& value);
} // namespace juce

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,251 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
/**
A class for dynamically evaluating simple numeric expressions.
This class can parse a simple C-style string expression involving floating point
numbers, named symbols and functions. The basic arithmetic operations of +, -, *, /
are supported, as well as parentheses, and any alphanumeric identifiers are
assumed to be named symbols which will be resolved when the expression is
evaluated.
Expressions which use identifiers and functions require a subclass of
Expression::Scope to be supplied when evaluating them, and this object
is expected to be able to resolve the symbol names and perform the functions that
are used.
@tags{Core}
*/
class JUCE_API Expression
{
public:
//==============================================================================
/** Creates a simple expression with a value of 0. */
Expression();
/** Destructor. */
~Expression();
/** Creates a copy of an expression. */
Expression (const Expression&);
/** Copies another expression. */
Expression& operator= (const Expression&);
/** Move constructor */
Expression (Expression&&) noexcept;
/** Move assignment operator */
Expression& operator= (Expression&&) noexcept;
/** Creates a simple expression with a specified constant value. */
explicit Expression (double constant);
/** Attempts to create an expression by parsing a string.
Any errors are returned in the parseError argument provided.
*/
Expression (const String& stringToParse, String& parseError);
/** Returns a string version of the expression. */
String toString() const;
/** Returns an expression which is an addition operation of two existing expressions. */
Expression operator+ (const Expression&) const;
/** Returns an expression which is a subtraction operation of two existing expressions. */
Expression operator- (const Expression&) const;
/** Returns an expression which is a multiplication operation of two existing expressions. */
Expression operator* (const Expression&) const;
/** Returns an expression which is a division operation of two existing expressions. */
Expression operator/ (const Expression&) const;
/** Returns an expression which performs a negation operation on an existing expression. */
Expression operator-() const;
/** Returns an Expression which is an identifier reference. */
static Expression symbol (const String& symbol);
/** Returns an Expression which is a function call. */
static Expression function (const String& functionName, const Array<Expression>& parameters);
/** Returns an Expression which parses a string from a character pointer, and updates the pointer
to indicate where it finished.
The pointer is incremented so that on return, it indicates the character that follows
the end of the expression that was parsed.
If there's a syntax error in parsing, the parseError argument will be set
to a description of the problem.
*/
static Expression parse (String::CharPointerType& stringToParse, String& parseError);
//==============================================================================
/** When evaluating an Expression object, this class is used to resolve symbols and
perform functions that the expression uses.
*/
class JUCE_API Scope
{
public:
Scope();
virtual ~Scope();
/** Returns some kind of globally unique ID that identifies this scope. */
virtual String getScopeUID() const;
/** Returns the value of a symbol.
If the symbol is unknown, this can throw an Expression::EvaluationError exception.
The member value is set to the part of the symbol that followed the dot, if there is
one, e.g. for "foo.bar", symbol = "foo" and member = "bar".
@throws Expression::EvaluationError
*/
virtual Expression getSymbolValue (const String& symbol) const;
/** Executes a named function.
If the function name is unknown, this can throw an Expression::EvaluationError exception.
@throws Expression::EvaluationError
*/
virtual double evaluateFunction (const String& functionName,
const double* parameters, int numParameters) const;
/** Used as a callback by the Scope::visitRelativeScope() method.
You should never create an instance of this class yourself, it's used by the
expression evaluation code.
*/
class Visitor
{
public:
virtual ~Visitor() = default;
virtual void visit (const Scope&) = 0;
};
/** Creates a Scope object for a named scope, and then calls a visitor
to do some kind of processing with this new scope.
If the name is valid, this method must create a suitable (temporary) Scope
object to represent it, and must call the Visitor::visit() method with this
new scope.
*/
virtual void visitRelativeScope (const String& scopeName, Visitor& visitor) const;
};
/** Evaluates this expression, without using a Scope.
Without a Scope, no symbols can be used, and only basic functions such as sin, cos, tan,
min, max are available.
To find out about any errors during evaluation, use the other version of this method which
takes a String parameter.
*/
double evaluate() const;
/** Evaluates this expression, providing a scope that should be able to evaluate any symbols
or functions that it uses.
To find out about any errors during evaluation, use the other version of this method which
takes a String parameter.
*/
double evaluate (const Scope& scope) const;
/** Evaluates this expression, providing a scope that should be able to evaluate any symbols
or functions that it uses.
*/
double evaluate (const Scope& scope, String& evaluationError) const;
/** Attempts to return an expression which is a copy of this one, but with a constant adjusted
to make the expression resolve to a target value.
E.g. if the expression is "x + 10" and x is 5, then asking for a target value of 8 will return
the expression "x + 3". Obviously some expressions can't be reversed in this way, in which
case they might just be adjusted by adding a constant to the original expression.
@throws Expression::EvaluationError
*/
Expression adjustedToGiveNewResult (double targetValue, const Scope& scope) const;
/** Represents a symbol that is used in an Expression. */
struct Symbol
{
Symbol (const String& scopeUID, const String& symbolName);
bool operator== (const Symbol&) const noexcept;
bool operator!= (const Symbol&) const noexcept;
String scopeUID; /**< The unique ID of the Scope that contains this symbol. */
String symbolName; /**< The name of the symbol. */
};
/** Returns a copy of this expression in which all instances of a given symbol have been renamed. */
Expression withRenamedSymbol (const Symbol& oldSymbol, const String& newName, const Scope& scope) const;
/** Returns true if this expression makes use of the specified symbol.
If a suitable scope is supplied, the search will dereference and recursively check
all symbols, so that it can be determined whether this expression relies on the given
symbol at any level in its evaluation. If the scope parameter is null, this just checks
whether the expression contains any direct references to the symbol.
@throws Expression::EvaluationError
*/
bool referencesSymbol (const Symbol& symbol, const Scope& scope) const;
/** Returns true if this expression contains any symbols. */
bool usesAnySymbols() const;
/** Returns a list of all symbols that may be needed to resolve this expression in the given scope. */
void findReferencedSymbols (Array<Symbol>& results, const Scope& scope) const;
//==============================================================================
/** Expression type.
@see Expression::getType()
*/
enum Type
{
constantType,
functionType,
operatorType,
symbolType
};
/** Returns the type of this expression. */
Type getType() const noexcept;
/** If this expression is a symbol, function or operator, this returns its identifier. */
String getSymbolOrFunction() const;
/** Returns the number of inputs to this expression.
@see getInput
*/
int getNumInputs() const;
/** Retrieves one of the inputs to this expression.
@see getNumInputs
*/
Expression getInput (int index) const;
private:
//==============================================================================
class Term;
struct Helpers;
ReferenceCountedObjectPtr<Term> term;
explicit Expression (Term*);
};
} // namespace juce

View File

@ -0,0 +1,685 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
/*
This file sets up some handy mathematical typdefs and functions.
*/
//==============================================================================
// Definitions for the int8, int16, int32, int64 and pointer_sized_int types.
/** A platform-independent 8-bit signed integer type. */
using int8 = signed char;
/** A platform-independent 8-bit unsigned integer type. */
using uint8 = unsigned char;
/** A platform-independent 16-bit signed integer type. */
using int16 = signed short;
/** A platform-independent 16-bit unsigned integer type. */
using uint16 = unsigned short;
/** A platform-independent 32-bit signed integer type. */
using int32 = signed int;
/** A platform-independent 32-bit unsigned integer type. */
using uint32 = unsigned int;
#if JUCE_MSVC
/** A platform-independent 64-bit integer type. */
using int64 = __int64;
/** A platform-independent 64-bit unsigned integer type. */
using uint64 = unsigned __int64;
#else
/** A platform-independent 64-bit integer type. */
using int64 = long long;
/** A platform-independent 64-bit unsigned integer type. */
using uint64 = unsigned long long;
#endif
#ifndef DOXYGEN
/** A macro for creating 64-bit literals.
Historically, this was needed to support portability with MSVC6, and is kept here
so that old code will still compile, but nowadays every compiler will support the
LL and ULL suffixes, so you should use those in preference to this macro.
*/
#define literal64bit(longLiteral) (longLiteral##LL)
#endif
#if JUCE_64BIT
/** A signed integer type that's guaranteed to be large enough to hold a pointer without truncating it. */
using pointer_sized_int = int64;
/** An unsigned integer type that's guaranteed to be large enough to hold a pointer without truncating it. */
using pointer_sized_uint = uint64;
#elif JUCE_MSVC
/** A signed integer type that's guaranteed to be large enough to hold a pointer without truncating it. */
using pointer_sized_int = _W64 int;
/** An unsigned integer type that's guaranteed to be large enough to hold a pointer without truncating it. */
using pointer_sized_uint = _W64 unsigned int;
#else
/** A signed integer type that's guaranteed to be large enough to hold a pointer without truncating it. */
using pointer_sized_int = int;
/** An unsigned integer type that's guaranteed to be large enough to hold a pointer without truncating it. */
using pointer_sized_uint = unsigned int;
#endif
#if JUCE_WINDOWS && ! JUCE_MINGW
using ssize_t = pointer_sized_int;
#endif
//==============================================================================
// Some indispensable min/max functions
/** Returns the larger of two values. */
template <typename Type>
constexpr Type jmax (Type a, Type b) { return a < b ? b : a; }
/** Returns the larger of three values. */
template <typename Type>
constexpr Type jmax (Type a, Type b, Type c) { return a < b ? (b < c ? c : b) : (a < c ? c : a); }
/** Returns the larger of four values. */
template <typename Type>
constexpr Type jmax (Type a, Type b, Type c, Type d) { return jmax (a, jmax (b, c, d)); }
/** Returns the smaller of two values. */
template <typename Type>
constexpr Type jmin (Type a, Type b) { return b < a ? b : a; }
/** Returns the smaller of three values. */
template <typename Type>
constexpr Type jmin (Type a, Type b, Type c) { return b < a ? (c < b ? c : b) : (c < a ? c : a); }
/** Returns the smaller of four values. */
template <typename Type>
constexpr Type jmin (Type a, Type b, Type c, Type d) { return jmin (a, jmin (b, c, d)); }
/** Remaps a normalised value (between 0 and 1) to a target range.
This effectively returns (targetRangeMin + value0To1 * (targetRangeMax - targetRangeMin)).
*/
template <typename Type>
constexpr Type jmap (Type value0To1, Type targetRangeMin, Type targetRangeMax)
{
return targetRangeMin + value0To1 * (targetRangeMax - targetRangeMin);
}
/** Remaps a value from a source range to a target range. */
template <typename Type>
Type jmap (Type sourceValue, Type sourceRangeMin, Type sourceRangeMax, Type targetRangeMin, Type targetRangeMax)
{
jassert (sourceRangeMax != sourceRangeMin); // mapping from a range of zero will produce NaN!
return targetRangeMin + ((targetRangeMax - targetRangeMin) * (sourceValue - sourceRangeMin)) / (sourceRangeMax - sourceRangeMin);
}
/** Remaps a normalised value (between 0 and 1) to a logarithmic target range.
The entire target range must be greater than zero.
@see mapFromLog10
@code
mapToLog10 (0.5, 0.4, 40.0) == 4.0
@endcode
*/
template <typename Type>
Type mapToLog10 (Type value0To1, Type logRangeMin, Type logRangeMax)
{
jassert (logRangeMin > 0);
jassert (logRangeMax > 0);
auto logMin = std::log10 (logRangeMin);
auto logMax = std::log10 (logRangeMax);
return std::pow ((Type) 10.0, value0To1 * (logMax - logMin) + logMin);
}
/** Remaps a logarithmic value in a target range to a normalised value (between 0 and 1).
The entire target range must be greater than zero.
@see mapToLog10
@code
mapFromLog10 (4.0, 0.4, 40.0) == 0.5
@endcode
*/
template <typename Type>
Type mapFromLog10 (Type valueInLogRange, Type logRangeMin, Type logRangeMax)
{
jassert (logRangeMin > 0);
jassert (logRangeMax > 0);
auto logMin = std::log10 (logRangeMin);
auto logMax = std::log10 (logRangeMax);
return (std::log10 (valueInLogRange) - logMin) / (logMax - logMin);
}
/** Scans an array of values, returning the minimum value that it contains. */
template <typename Type>
Type findMinimum (const Type* data, int numValues)
{
if (numValues <= 0)
return Type (0);
auto result = *data++;
while (--numValues > 0) // (> 0 rather than >= 0 because we've already taken the first sample)
{
auto v = *data++;
if (v < result)
result = v;
}
return result;
}
/** Scans an array of values, returning the maximum value that it contains. */
template <typename Type>
Type findMaximum (const Type* values, int numValues)
{
if (numValues <= 0)
return Type (0);
auto result = *values++;
while (--numValues > 0) // (> 0 rather than >= 0 because we've already taken the first sample)
{
auto v = *values++;
if (result < v)
result = v;
}
return result;
}
/** Scans an array of values, returning the minimum and maximum values that it contains. */
template <typename Type>
void findMinAndMax (const Type* values, int numValues, Type& lowest, Type& highest)
{
if (numValues <= 0)
{
lowest = Type (0);
highest = Type (0);
}
else
{
auto mn = *values++;
auto mx = mn;
while (--numValues > 0) // (> 0 rather than >= 0 because we've already taken the first sample)
{
auto v = *values++;
if (mx < v) mx = v;
if (v < mn) mn = v;
}
lowest = mn;
highest = mx;
}
}
//==============================================================================
/** Constrains a value to keep it within a given range.
This will check that the specified value lies between the lower and upper bounds
specified, and if not, will return the nearest value that would be in-range. Effectively,
it's like calling jmax (lowerLimit, jmin (upperLimit, value)).
Note that it expects that lowerLimit <= upperLimit. If this isn't true,
the results will be unpredictable.
@param lowerLimit the minimum value to return
@param upperLimit the maximum value to return
@param valueToConstrain the value to try to return
@returns the closest value to valueToConstrain which lies between lowerLimit
and upperLimit (inclusive)
@see jmin, jmax, jmap
*/
template <typename Type>
Type jlimit (Type lowerLimit,
Type upperLimit,
Type valueToConstrain) noexcept
{
jassert (lowerLimit <= upperLimit); // if these are in the wrong order, results are unpredictable..
return valueToConstrain < lowerLimit ? lowerLimit
: (upperLimit < valueToConstrain ? upperLimit
: valueToConstrain);
}
/** Returns true if a value is at least zero, and also below a specified upper limit.
This is basically a quicker way to write:
@code valueToTest >= 0 && valueToTest < upperLimit
@endcode
*/
template <typename Type1, typename Type2>
bool isPositiveAndBelow (Type1 valueToTest, Type2 upperLimit) noexcept
{
jassert (Type1() <= static_cast<Type1> (upperLimit)); // makes no sense to call this if the upper limit is itself below zero..
return Type1() <= valueToTest && valueToTest < static_cast<Type1> (upperLimit);
}
template <typename Type>
bool isPositiveAndBelow (int valueToTest, Type upperLimit) noexcept
{
jassert (upperLimit >= 0); // makes no sense to call this if the upper limit is itself below zero..
return static_cast<unsigned int> (valueToTest) < static_cast<unsigned int> (upperLimit);
}
/** Returns true if a value is at least zero, and also less than or equal to a specified upper limit.
This is basically a quicker way to write:
@code valueToTest >= 0 && valueToTest <= upperLimit
@endcode
*/
template <typename Type1, typename Type2>
bool isPositiveAndNotGreaterThan (Type1 valueToTest, Type2 upperLimit) noexcept
{
jassert (Type1() <= static_cast<Type1> (upperLimit)); // makes no sense to call this if the upper limit is itself below zero..
return Type1() <= valueToTest && valueToTest <= static_cast<Type1> (upperLimit);
}
template <typename Type>
bool isPositiveAndNotGreaterThan (int valueToTest, Type upperLimit) noexcept
{
jassert (upperLimit >= 0); // makes no sense to call this if the upper limit is itself below zero..
return static_cast<unsigned int> (valueToTest) <= static_cast<unsigned int> (upperLimit);
}
/** Computes the absolute difference between two values and returns true if it is less than or equal
to a given tolerance, otherwise it returns false.
*/
template <typename Type>
bool isWithin (Type a, Type b, Type tolerance) noexcept
{
return std::abs (a - b) <= tolerance;
}
/** Returns true if the two numbers are approximately equal. This is useful for floating-point
and double comparisons.
*/
template <typename Type>
bool approximatelyEqual (Type a, Type b) noexcept
{
return std::abs (a - b) <= (std::numeric_limits<Type>::epsilon() * std::max (a, b))
|| std::abs (a - b) < std::numeric_limits<Type>::min();
}
//==============================================================================
/** Handy function for avoiding unused variables warning. */
template <typename... Types>
void ignoreUnused (Types&&...) noexcept {}
/** Handy function for getting the number of elements in a simple const C array.
E.g.
@code
static int myArray[] = { 1, 2, 3 };
int numElements = numElementsInArray (myArray) // returns 3
@endcode
*/
template <typename Type, size_t N>
constexpr int numElementsInArray (Type (&)[N]) noexcept { return N; }
//==============================================================================
// Some useful maths functions that aren't always present with all compilers and build settings.
/** Using juce_hypot is easier than dealing with the different types of hypot function
that are provided by the various platforms and compilers. */
template <typename Type>
Type juce_hypot (Type a, Type b) noexcept
{
#if JUCE_MSVC
return static_cast<Type> (_hypot (a, b));
#else
return static_cast<Type> (hypot (a, b));
#endif
}
#ifndef DOXYGEN
template <>
inline float juce_hypot (float a, float b) noexcept
{
#if JUCE_MSVC
return _hypotf (a, b);
#else
return hypotf (a, b);
#endif
}
#endif
//==============================================================================
/** Commonly used mathematical constants
@tags{Core}
*/
template <typename FloatType>
struct MathConstants
{
/** A predefined value for Pi */
static constexpr FloatType pi = static_cast<FloatType> (3.141592653589793238L);
/** A predefined value for 2 * Pi */
static constexpr FloatType twoPi = static_cast<FloatType> (2 * 3.141592653589793238L);
/** A predefined value for Pi / 2 */
static constexpr FloatType halfPi = static_cast<FloatType> (3.141592653589793238L / 2);
/** A predefined value for Euler's number */
static constexpr FloatType euler = static_cast<FloatType> (2.71828182845904523536L);
/** A predefined value for sqrt(2) */
static constexpr FloatType sqrt2 = static_cast<FloatType> (1.4142135623730950488L);
};
#ifndef DOXYGEN
/** A double-precision constant for pi. */
[[deprecated ("This is deprecated in favour of MathConstants<double>::pi.")]]
const constexpr double double_Pi = MathConstants<double>::pi;
/** A single-precision constant for pi. */
[[deprecated ("This is deprecated in favour of MathConstants<double>::pi.")]]
const constexpr float float_Pi = MathConstants<float>::pi;
#endif
/** Converts an angle in degrees to radians. */
template <typename FloatType>
constexpr FloatType degreesToRadians (FloatType degrees) noexcept { return degrees * (MathConstants<FloatType>::pi / FloatType (180)); }
/** Converts an angle in radians to degrees. */
template <typename FloatType>
constexpr FloatType radiansToDegrees (FloatType radians) noexcept { return radians * (FloatType (180) / MathConstants<FloatType>::pi); }
//==============================================================================
/** The isfinite() method seems to vary between platforms, so this is a
platform-independent function for it.
*/
template <typename NumericType>
bool juce_isfinite (NumericType) noexcept
{
return true; // Integer types are always finite
}
template <>
inline bool juce_isfinite (float value) noexcept
{
#if JUCE_WINDOWS && ! JUCE_MINGW
return _finite (value) != 0;
#else
return std::isfinite (value);
#endif
}
template <>
inline bool juce_isfinite (double value) noexcept
{
#if JUCE_WINDOWS && ! JUCE_MINGW
return _finite (value) != 0;
#else
return std::isfinite (value);
#endif
}
//==============================================================================
#if JUCE_MSVC
#pragma optimize ("t", off)
#ifndef __INTEL_COMPILER
#pragma float_control (precise, on, push)
#endif
#endif
/** Fast floating-point-to-integer conversion.
This is faster than using the normal c++ cast to convert a float to an int, and
it will round the value to the nearest integer, rather than rounding it down
like the normal cast does.
Note that this routine gets its speed at the expense of some accuracy, and when
rounding values whose floating point component is exactly 0.5, odd numbers and
even numbers will be rounded up or down differently.
*/
template <typename FloatType>
int roundToInt (const FloatType value) noexcept
{
#ifdef __INTEL_COMPILER
#pragma float_control (precise, on, push)
#endif
union { int asInt[2]; double asDouble; } n;
n.asDouble = ((double) value) + 6755399441055744.0;
#if JUCE_BIG_ENDIAN
return n.asInt [1];
#else
return n.asInt [0];
#endif
}
inline int roundToInt (int value) noexcept
{
return value;
}
#if JUCE_MSVC
#ifndef __INTEL_COMPILER
#pragma float_control (pop)
#endif
#pragma optimize ("", on) // resets optimisations to the project defaults
#endif
/** Fast floating-point-to-integer conversion.
This is a slightly slower and slightly more accurate version of roundToInt(). It works
fine for values above zero, but negative numbers are rounded the wrong way.
*/
inline int roundToIntAccurate (double value) noexcept
{
#ifdef __INTEL_COMPILER
#pragma float_control (pop)
#endif
return roundToInt (value + 1.5e-8);
}
//==============================================================================
/** Truncates a positive floating-point number to an unsigned int.
This is generally faster than static_cast<unsigned int> (std::floor (x))
but it only works for positive numbers small enough to be represented as an
unsigned int.
*/
template <typename FloatType>
unsigned int truncatePositiveToUnsignedInt (FloatType value) noexcept
{
jassert (value >= static_cast<FloatType> (0));
jassert (static_cast<FloatType> (value)
<= static_cast<FloatType> (std::numeric_limits<unsigned int>::max()));
return static_cast<unsigned int> (value);
}
//==============================================================================
/** Returns true if the specified integer is a power-of-two. */
template <typename IntegerType>
constexpr bool isPowerOfTwo (IntegerType value)
{
return (value & (value - 1)) == 0;
}
/** Returns the smallest power-of-two which is equal to or greater than the given integer. */
inline int nextPowerOfTwo (int n) noexcept
{
--n;
n |= (n >> 1);
n |= (n >> 2);
n |= (n >> 4);
n |= (n >> 8);
n |= (n >> 16);
return n + 1;
}
/** Returns the index of the highest set bit in a (non-zero) number.
So for n=3 this would return 1, for n=7 it returns 2, etc.
An input value of 0 is illegal!
*/
int findHighestSetBit (uint32 n) noexcept;
/** Returns the number of bits in a 32-bit integer. */
inline int countNumberOfBits (uint32 n) noexcept
{
n -= ((n >> 1) & 0x55555555);
n = (((n >> 2) & 0x33333333) + (n & 0x33333333));
n = (((n >> 4) + n) & 0x0f0f0f0f);
n += (n >> 8);
n += (n >> 16);
return (int) (n & 0x3f);
}
/** Returns the number of bits in a 64-bit integer. */
inline int countNumberOfBits (uint64 n) noexcept
{
return countNumberOfBits ((uint32) n) + countNumberOfBits ((uint32) (n >> 32));
}
/** Performs a modulo operation, but can cope with the dividend being negative.
The divisor must be greater than zero.
*/
template <typename IntegerType>
IntegerType negativeAwareModulo (IntegerType dividend, const IntegerType divisor) noexcept
{
jassert (divisor > 0);
dividend %= divisor;
return (dividend < 0) ? (dividend + divisor) : dividend;
}
/** Returns the square of its argument. */
template <typename NumericType>
inline constexpr NumericType square (NumericType n) noexcept
{
return n * n;
}
//==============================================================================
/** Writes a number of bits into a memory buffer at a given bit index.
The buffer is treated as a sequence of 8-bit bytes, and the value is encoded in little-endian order,
so for example if startBit = 10, and numBits = 11 then the lower 6 bits of the value would be written
into bits 2-8 of targetBuffer[1], and the upper 5 bits of value into bits 0-5 of targetBuffer[2].
@see readLittleEndianBitsInBuffer
*/
void writeLittleEndianBitsInBuffer (void* targetBuffer, uint32 startBit, uint32 numBits, uint32 value) noexcept;
/** Reads a number of bits from a buffer at a given bit index.
The buffer is treated as a sequence of 8-bit bytes, and the value is encoded in little-endian order,
so for example if startBit = 10, and numBits = 11 then the lower 6 bits of the result would be read
from bits 2-8 of sourceBuffer[1], and the upper 5 bits of the result from bits 0-5 of sourceBuffer[2].
@see writeLittleEndianBitsInBuffer
*/
uint32 readLittleEndianBitsInBuffer (const void* sourceBuffer, uint32 startBit, uint32 numBits) noexcept;
//==============================================================================
#if JUCE_INTEL || DOXYGEN
/** This macro can be applied to a float variable to check whether it contains a denormalised
value, and to normalise it if necessary.
On CPUs that aren't vulnerable to denormalisation problems, this will have no effect.
*/
#define JUCE_UNDENORMALISE(x) { (x) += 0.1f; (x) -= 0.1f; }
#else
#define JUCE_UNDENORMALISE(x)
#endif
//==============================================================================
/** This namespace contains a few template classes for helping work out class type variations.
*/
namespace TypeHelpers
{
/** The ParameterType struct is used to find the best type to use when passing some kind
of object as a parameter.
Of course, this is only likely to be useful in certain esoteric template situations.
E.g. "myFunction (typename TypeHelpers::ParameterType<int>::type, typename TypeHelpers::ParameterType<MyObject>::type)"
would evaluate to "myfunction (int, const MyObject&)", keeping any primitive types as
pass-by-value, but passing objects as a const reference, to avoid copying.
@tags{Core}
*/
template <typename Type> struct ParameterType { using type = const Type&; };
#ifndef DOXYGEN
template <typename Type> struct ParameterType <Type&> { using type = Type&; };
template <typename Type> struct ParameterType <Type*> { using type = Type*; };
template <> struct ParameterType <char> { using type = char; };
template <> struct ParameterType <unsigned char> { using type = unsigned char; };
template <> struct ParameterType <short> { using type = short; };
template <> struct ParameterType <unsigned short> { using type = unsigned short; };
template <> struct ParameterType <int> { using type = int; };
template <> struct ParameterType <unsigned int> { using type = unsigned int; };
template <> struct ParameterType <long> { using type = long; };
template <> struct ParameterType <unsigned long> { using type = unsigned long; };
template <> struct ParameterType <int64> { using type = int64; };
template <> struct ParameterType <uint64> { using type = uint64; };
template <> struct ParameterType <bool> { using type = bool; };
template <> struct ParameterType <float> { using type = float; };
template <> struct ParameterType <double> { using type = double; };
#endif
/** These templates are designed to take a type, and if it's a double, they return a double
type; for anything else, they return a float type.
@tags{Core}
*/
template <typename Type> struct SmallestFloatType { using type = float; };
#ifndef DOXYGEN
template <> struct SmallestFloatType <double> { using type = double; };
#endif
/** These templates are designed to take an integer type, and return an unsigned int
version with the same size.
@tags{Core}
*/
template <int bytes> struct UnsignedTypeWithSize {};
#ifndef DOXYGEN
template <> struct UnsignedTypeWithSize<1> { using type = uint8; };
template <> struct UnsignedTypeWithSize<2> { using type = uint16; };
template <> struct UnsignedTypeWithSize<4> { using type = uint32; };
template <> struct UnsignedTypeWithSize<8> { using type = uint64; };
#endif
}
//==============================================================================
#ifndef DOXYGEN
[[deprecated ("Use roundToInt instead.")]] inline int roundDoubleToInt (double value) noexcept { return roundToInt (value); }
[[deprecated ("Use roundToInt instead.")]] inline int roundFloatToInt (float value) noexcept { return roundToInt (value); }
[[deprecated ("Use std::abs() instead.")]] inline int64 abs64 (int64 n) noexcept { return std::abs (n); }
#endif
} // namespace juce

View File

@ -0,0 +1,261 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
/**
Represents a mapping between an arbitrary range of values and a
normalised 0->1 range.
The properties of the mapping also include an optional snapping interval
and skew-factor.
@see Range
@tags{Core}
*/
template <typename ValueType>
class NormalisableRange
{
public:
/** Creates a continuous range that performs a dummy mapping. */
NormalisableRange() = default;
NormalisableRange (const NormalisableRange&) = default;
NormalisableRange& operator= (const NormalisableRange&) = default;
NormalisableRange (NormalisableRange&&) = default;
NormalisableRange& operator= (NormalisableRange&&) = default;
/** Creates a NormalisableRange with a given range, interval and skew factor. */
NormalisableRange (ValueType rangeStart,
ValueType rangeEnd,
ValueType intervalValue,
ValueType skewFactor,
bool useSymmetricSkew = false) noexcept
: start (rangeStart), end (rangeEnd), interval (intervalValue),
skew (skewFactor), symmetricSkew (useSymmetricSkew)
{
checkInvariants();
}
/** Creates a NormalisableRange with a given range, continuous interval, but a dummy skew-factor. */
NormalisableRange (ValueType rangeStart,
ValueType rangeEnd) noexcept
: start (rangeStart), end (rangeEnd)
{
checkInvariants();
}
/** Creates a NormalisableRange with a given range and interval, but a dummy skew-factor. */
NormalisableRange (ValueType rangeStart,
ValueType rangeEnd,
ValueType intervalValue) noexcept
: start (rangeStart), end (rangeEnd), interval (intervalValue)
{
checkInvariants();
}
/** Creates a NormalisableRange with a given range, continuous interval, but a dummy skew-factor. */
NormalisableRange (Range<ValueType> range) noexcept
: NormalisableRange (range.getStart(), range.getEnd())
{
}
/** Creates a NormalisableRange with a given range and interval, but a dummy skew-factor. */
NormalisableRange (Range<ValueType> range, ValueType intervalValue) noexcept
: NormalisableRange (range.getStart(), range.getEnd(), intervalValue)
{
}
/** A function object which can remap a value in some way based on the start and end of a range. */
using ValueRemapFunction = std::function<ValueType(ValueType rangeStart,
ValueType rangeEnd,
ValueType valueToRemap)>;
/** Creates a NormalisableRange with a given range and an injective mapping function.
@param rangeStart The minimum value in the range.
@param rangeEnd The maximum value in the range.
@param convertFrom0To1Func A function which uses the current start and end of this NormalisableRange
and produces a mapped value from a normalised value.
@param convertTo0To1Func A function which uses the current start and end of this NormalisableRange
and produces a normalised value from a mapped value.
@param snapToLegalValueFunc A function which uses the current start and end of this NormalisableRange
to take a mapped value and snap it to the nearest legal value.
*/
NormalisableRange (ValueType rangeStart,
ValueType rangeEnd,
ValueRemapFunction convertFrom0To1Func,
ValueRemapFunction convertTo0To1Func,
ValueRemapFunction snapToLegalValueFunc = {}) noexcept
: start (rangeStart),
end (rangeEnd),
convertFrom0To1Function (std::move (convertFrom0To1Func)),
convertTo0To1Function (std::move (convertTo0To1Func)),
snapToLegalValueFunction (std::move (snapToLegalValueFunc))
{
checkInvariants();
}
/** Uses the properties of this mapping to convert a non-normalised value to
its 0->1 representation.
*/
ValueType convertTo0to1 (ValueType v) const noexcept
{
if (convertTo0To1Function != nullptr)
return clampTo0To1 (convertTo0To1Function (start, end, v));
auto proportion = clampTo0To1 ((v - start) / (end - start));
if (skew == static_cast<ValueType> (1))
return proportion;
if (! symmetricSkew)
return std::pow (proportion, skew);
auto distanceFromMiddle = static_cast<ValueType> (2) * proportion - static_cast<ValueType> (1);
return (static_cast<ValueType> (1) + std::pow (std::abs (distanceFromMiddle), skew)
* (distanceFromMiddle < ValueType() ? static_cast<ValueType> (-1)
: static_cast<ValueType> (1)))
/ static_cast<ValueType> (2);
}
/** Uses the properties of this mapping to convert a normalised 0->1 value to
its full-range representation.
*/
ValueType convertFrom0to1 (ValueType proportion) const noexcept
{
proportion = clampTo0To1 (proportion);
if (convertFrom0To1Function != nullptr)
return convertFrom0To1Function (start, end, proportion);
if (! symmetricSkew)
{
if (skew != static_cast<ValueType> (1) && proportion > ValueType())
proportion = std::exp (std::log (proportion) / skew);
return start + (end - start) * proportion;
}
auto distanceFromMiddle = static_cast<ValueType> (2) * proportion - static_cast<ValueType> (1);
if (skew != static_cast<ValueType> (1) && distanceFromMiddle != static_cast<ValueType> (0))
distanceFromMiddle = std::exp (std::log (std::abs (distanceFromMiddle)) / skew)
* (distanceFromMiddle < ValueType() ? static_cast<ValueType> (-1)
: static_cast<ValueType> (1));
return start + (end - start) / static_cast<ValueType> (2) * (static_cast<ValueType> (1) + distanceFromMiddle);
}
/** Takes a non-normalised value and snaps it based on either the interval property of
this NormalisableRange or the lambda function supplied to the constructor.
*/
ValueType snapToLegalValue (ValueType v) const noexcept
{
if (snapToLegalValueFunction != nullptr)
return snapToLegalValueFunction (start, end, v);
if (interval > ValueType())
v = start + interval * std::floor ((v - start) / interval + static_cast<ValueType> (0.5));
return (v <= start || end <= start) ? start : (v >= end ? end : v);
}
/** Returns the extent of the normalisable range. */
Range<ValueType> getRange() const noexcept { return { start, end }; }
/** Given a value which is between the start and end points, this sets the skew
such that convertFrom0to1 (0.5) will return this value.
If you have used lambda functions for convertFrom0to1Func and convertFrom0to1Func in the
constructor of this class then the skew value is ignored.
@param centrePointValue this must be greater than the start of the range and less than the end.
*/
void setSkewForCentre (ValueType centrePointValue) noexcept
{
jassert (centrePointValue > start);
jassert (centrePointValue < end);
symmetricSkew = false;
skew = std::log (static_cast<ValueType> (0.5)) / std::log ((centrePointValue - start) / (end - start));
checkInvariants();
}
/** The minimum value of the non-normalised range. */
ValueType start = 0;
/** The maximum value of the non-normalised range. */
ValueType end = 1;
/** The snapping interval that should be used (for a non-normalised value). Use 0 for a
continuous range.
If you have used a lambda function for snapToLegalValueFunction in the constructor of
this class then the interval is ignored.
*/
ValueType interval = 0;
/** An optional skew factor that alters the way values are distribute across the range.
The skew factor lets you skew the mapping logarithmically so that larger or smaller
values are given a larger proportion of the available space.
A factor of 1.0 has no skewing effect at all. If the factor is < 1.0, the lower end
of the range will fill more of the slider's length; if the factor is > 1.0, the upper
end of the range will be expanded.
If you have used lambda functions for convertFrom0to1Func and convertFrom0to1Func in the
constructor of this class then the skew value is ignored.
*/
ValueType skew = 1;
/** If true, the skew factor applies from the middle of the slider to each of its ends. */
bool symmetricSkew = false;
private:
void checkInvariants() const
{
jassert (end > start);
jassert (interval >= ValueType());
jassert (skew > ValueType());
}
static ValueType clampTo0To1 (ValueType value)
{
auto clampedValue = jlimit (static_cast<ValueType> (0), static_cast<ValueType> (1), value);
// If you hit this assertion then either your normalisation function is not working
// correctly or your input is out of the expected bounds.
jassert (clampedValue == value);
return clampedValue;
}
ValueRemapFunction convertFrom0To1Function, convertTo0To1Function, snapToLegalValueFunction;
};
} // namespace juce

View File

@ -0,0 +1,204 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
Random::Random (int64 seedValue) noexcept : seed (seedValue)
{
}
Random::Random() : seed (1)
{
setSeedRandomly();
}
Random::~Random() noexcept
{
}
void Random::setSeed (const int64 newSeed) noexcept
{
if (this == &getSystemRandom())
{
// Resetting the system Random risks messing up
// JUCE's internal state. If you need a predictable
// stream of random numbers you should use a local
// Random object.
jassertfalse;
return;
}
seed = newSeed;
}
void Random::combineSeed (const int64 seedValue) noexcept
{
seed ^= nextInt64() ^ seedValue;
}
void Random::setSeedRandomly()
{
static std::atomic<int64> globalSeed { 0 };
combineSeed (globalSeed ^ (int64) (pointer_sized_int) this);
combineSeed (Time::getMillisecondCounter());
combineSeed (Time::getHighResolutionTicks());
combineSeed (Time::getHighResolutionTicksPerSecond());
combineSeed (Time::currentTimeMillis());
globalSeed ^= seed;
}
Random& Random::getSystemRandom() noexcept
{
static Random sysRand;
return sysRand;
}
//==============================================================================
int Random::nextInt() noexcept
{
seed = (int64) (((((uint64) seed) * 0x5deece66dLL) + 11) & 0xffffffffffffLL);
return (int) (seed >> 16);
}
int Random::nextInt (const int maxValue) noexcept
{
jassert (maxValue > 0);
return (int) ((((unsigned int) nextInt()) * (uint64) maxValue) >> 32);
}
int Random::nextInt (Range<int> range) noexcept
{
return range.getStart() + nextInt (range.getLength());
}
int64 Random::nextInt64() noexcept
{
return (int64) ((((uint64) (unsigned int) nextInt()) << 32) | (uint64) (unsigned int) nextInt());
}
bool Random::nextBool() noexcept
{
return (nextInt() & 0x40000000) != 0;
}
float Random::nextFloat() noexcept
{
auto result = static_cast<float> (static_cast<uint32> (nextInt()))
/ (static_cast<float> (std::numeric_limits<uint32>::max()) + 1.0f);
return result == 1.0f ? 1.0f - std::numeric_limits<float>::epsilon() : result;
}
double Random::nextDouble() noexcept
{
return static_cast<uint32> (nextInt()) / (std::numeric_limits<uint32>::max() + 1.0);
}
BigInteger Random::nextLargeNumber (const BigInteger& maximumValue)
{
BigInteger n;
do
{
fillBitsRandomly (n, 0, maximumValue.getHighestBit() + 1);
}
while (n >= maximumValue);
return n;
}
void Random::fillBitsRandomly (void* const buffer, size_t bytes)
{
int* d = static_cast<int*> (buffer);
for (; bytes >= sizeof (int); bytes -= sizeof (int))
*d++ = nextInt();
if (bytes > 0)
{
const int lastBytes = nextInt();
memcpy (d, &lastBytes, bytes);
}
}
void Random::fillBitsRandomly (BigInteger& arrayToChange, int startBit, int numBits)
{
arrayToChange.setBit (startBit + numBits - 1, true); // to force the array to pre-allocate space
while ((startBit & 31) != 0 && numBits > 0)
{
arrayToChange.setBit (startBit++, nextBool());
--numBits;
}
while (numBits >= 32)
{
arrayToChange.setBitRangeAsInt (startBit, 32, (unsigned int) nextInt());
startBit += 32;
numBits -= 32;
}
while (--numBits >= 0)
arrayToChange.setBit (startBit + numBits, nextBool());
}
//==============================================================================
//==============================================================================
#if JUCE_UNIT_TESTS
class RandomTests : public UnitTest
{
public:
RandomTests()
: UnitTest ("Random", UnitTestCategories::maths)
{}
void runTest() override
{
beginTest ("Random");
Random r = getRandom();
for (int i = 2000; --i >= 0;)
{
expect (r.nextDouble() >= 0.0 && r.nextDouble() < 1.0);
expect (r.nextFloat() >= 0.0f && r.nextFloat() < 1.0f);
expect (r.nextInt (5) >= 0 && r.nextInt (5) < 5);
expect (r.nextInt (1) == 0);
int n = r.nextInt (50) + 1;
expect (r.nextInt (n) >= 0 && r.nextInt (n) < n);
n = r.nextInt (0x7ffffffe) + 1;
expect (r.nextInt (n) >= 0 && r.nextInt (n) < n);
}
}
};
static RandomTests randomTests;
#endif
} // namespace juce

View File

@ -0,0 +1,137 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
/**
A random number generator.
You can create a Random object and use it to generate a sequence of random numbers.
@tags{Core}
*/
class JUCE_API Random final
{
public:
//==============================================================================
/** Creates a Random object based on a seed value.
For a given seed value, the subsequent numbers generated by this object
will be predictable, so a good idea is to set this value based
on the time, e.g.
new Random (Time::currentTimeMillis())
*/
explicit Random (int64 seedValue) noexcept;
/** Creates a Random object using a random seed value.
Internally, this calls setSeedRandomly() to randomise the seed.
*/
Random();
/** Destructor. */
~Random() noexcept;
/** Returns the next random 32 bit integer.
@returns a random integer from the full range 0x80000000 to 0x7fffffff
*/
int nextInt() noexcept;
/** Returns the next random number, limited to a given range.
The maxValue parameter may not be negative, or zero.
@returns a random integer between 0 (inclusive) and maxValue (exclusive).
*/
int nextInt (int maxValue) noexcept;
/** Returns the next random number, limited to a given range.
@returns a random integer between the range start (inclusive) and its end (exclusive).
*/
int nextInt (Range<int> range) noexcept;
/** Returns the next 64-bit random number.
@returns a random integer from the full range 0x8000000000000000 to 0x7fffffffffffffff
*/
int64 nextInt64() noexcept;
/** Returns the next random floating-point number.
@returns a random value in the range 0 (inclusive) to 1.0 (exclusive)
*/
float nextFloat() noexcept;
/** Returns the next random floating-point number.
@returns a random value in the range 0 (inclusive) to 1.0 (exclusive)
*/
double nextDouble() noexcept;
/** Returns the next random boolean value. */
bool nextBool() noexcept;
/** Returns a BigInteger containing a random number.
@returns a random value in the range 0 to (maximumValue - 1).
*/
BigInteger nextLargeNumber (const BigInteger& maximumValue);
/** Fills a block of memory with random values. */
void fillBitsRandomly (void* bufferToFill, size_t sizeInBytes);
/** Sets a range of bits in a BigInteger to random values. */
void fillBitsRandomly (BigInteger& arrayToChange, int startBit, int numBits);
//==============================================================================
/** Resets this Random object to a given seed value. */
void setSeed (int64 newSeed) noexcept;
/** Returns the RNG's current seed. */
int64 getSeed() const noexcept { return seed; }
/** Merges this object's seed with another value.
This sets the seed to be a value created by combining the current seed and this
new value.
*/
void combineSeed (int64 seedValue) noexcept;
/** Reseeds this generator using a value generated from various semi-random system
properties like the current time, etc.
Because this function convolves the time with the last seed value, calling
it repeatedly will increase the randomness of the final result.
*/
void setSeedRandomly();
/** The overhead of creating a new Random object is fairly small, but if you want to avoid
it, you can call this method to get a global shared Random object.
It's not thread-safe though, so threads should use their own Random object, otherwise
you run the risk of your random numbers becoming.. erm.. randomly corrupted..
*/
static Random& getSystemRandom() noexcept;
private:
//==============================================================================
int64 seed;
JUCE_LEAK_DETECTOR (Random)
};
} // namespace juce

View File

@ -0,0 +1,297 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
/** A general-purpose range object, that simply represents any linear range with
a start and end point.
Note that when checking whether values fall within the range, the start value is
considered to be inclusive, and the end of the range exclusive.
The templated parameter is expected to be a primitive integer or floating point
type, though class types could also be used if they behave in a number-like way.
@tags{Core}
*/
template <typename ValueType>
class Range
{
public:
//==============================================================================
/** Constructs an empty range. */
constexpr Range() = default;
/** Constructs a range with given start and end values. */
constexpr Range (const ValueType startValue, const ValueType endValue) noexcept
: start (startValue), end (jmax (startValue, endValue))
{
}
/** Constructs a copy of another range. */
constexpr Range (const Range&) = default;
/** Copies another range object. */
Range& operator= (const Range&) = default;
/** Returns the range that lies between two positions (in either order). */
constexpr static Range between (const ValueType position1, const ValueType position2) noexcept
{
return position1 < position2 ? Range (position1, position2)
: Range (position2, position1);
}
/** Returns a range with a given start and length. */
static Range withStartAndLength (const ValueType startValue, const ValueType length) noexcept
{
jassert (length >= ValueType());
return Range (startValue, startValue + length);
}
/** Returns a range with the specified start position and a length of zero. */
constexpr static Range emptyRange (const ValueType start) noexcept
{
return Range (start, start);
}
//==============================================================================
/** Returns the start of the range. */
constexpr inline ValueType getStart() const noexcept { return start; }
/** Returns the length of the range. */
constexpr inline ValueType getLength() const noexcept { return end - start; }
/** Returns the end of the range. */
constexpr inline ValueType getEnd() const noexcept { return end; }
/** Returns true if the range has a length of zero. */
constexpr inline bool isEmpty() const noexcept { return start == end; }
//==============================================================================
/** Changes the start position of the range, leaving the end position unchanged.
If the new start position is higher than the current end of the range, the end point
will be pushed along to equal it, leaving an empty range at the new position.
*/
void setStart (const ValueType newStart) noexcept
{
start = newStart;
if (end < newStart)
end = newStart;
}
/** Returns a range with the same end as this one, but a different start.
If the new start position is higher than the current end of the range, the end point
will be pushed along to equal it, returning an empty range at the new position.
*/
constexpr Range withStart (const ValueType newStart) const noexcept
{
return Range (newStart, jmax (newStart, end));
}
/** Returns a range with the same length as this one, but moved to have the given start position. */
constexpr Range movedToStartAt (const ValueType newStart) const noexcept
{
return Range (newStart, end + (newStart - start));
}
/** Changes the end position of the range, leaving the start unchanged.
If the new end position is below the current start of the range, the start point
will be pushed back to equal the new end point.
*/
void setEnd (const ValueType newEnd) noexcept
{
end = newEnd;
if (newEnd < start)
start = newEnd;
}
/** Returns a range with the same start position as this one, but a different end.
If the new end position is below the current start of the range, the start point
will be pushed back to equal the new end point.
*/
constexpr Range withEnd (const ValueType newEnd) const noexcept
{
return Range (jmin (start, newEnd), newEnd);
}
/** Returns a range with the same length as this one, but moved to have the given end position. */
constexpr Range movedToEndAt (const ValueType newEnd) const noexcept
{
return Range (start + (newEnd - end), newEnd);
}
/** Changes the length of the range.
Lengths less than zero are treated as zero.
*/
void setLength (const ValueType newLength) noexcept
{
end = start + jmax (ValueType(), newLength);
}
/** Returns a range with the same start as this one, but a different length.
Lengths less than zero are treated as zero.
*/
constexpr Range withLength (const ValueType newLength) const noexcept
{
return Range (start, start + newLength);
}
/** Returns a range which has its start moved down and its end moved up by the
given amount.
@returns The returned range will be (start - amount, end + amount)
*/
constexpr Range expanded (ValueType amount) const noexcept
{
return Range (start - amount, end + amount);
}
//==============================================================================
/** Adds an amount to the start and end of the range. */
inline Range operator+= (const ValueType amountToAdd) noexcept
{
start += amountToAdd;
end += amountToAdd;
return *this;
}
/** Subtracts an amount from the start and end of the range. */
inline Range operator-= (const ValueType amountToSubtract) noexcept
{
start -= amountToSubtract;
end -= amountToSubtract;
return *this;
}
/** Returns a range that is equal to this one with an amount added to its
start and end.
*/
constexpr Range operator+ (const ValueType amountToAdd) const noexcept
{
return Range (start + amountToAdd, end + amountToAdd);
}
/** Returns a range that is equal to this one with the specified amount
subtracted from its start and end. */
constexpr Range operator- (const ValueType amountToSubtract) const noexcept
{
return Range (start - amountToSubtract, end - amountToSubtract);
}
constexpr bool operator== (Range other) const noexcept { return start == other.start && end == other.end; }
constexpr bool operator!= (Range other) const noexcept { return start != other.start || end != other.end; }
//==============================================================================
/** Returns true if the given position lies inside this range.
When making this comparison, the start value is considered to be inclusive,
and the end of the range exclusive.
*/
constexpr bool contains (const ValueType position) const noexcept
{
return start <= position && position < end;
}
/** Returns the nearest value to the one supplied, which lies within the range. */
ValueType clipValue (const ValueType value) const noexcept
{
return jlimit (start, end, value);
}
/** Returns true if the given range lies entirely inside this range. */
constexpr bool contains (Range other) const noexcept
{
return start <= other.start && end >= other.end;
}
/** Returns true if the given range intersects this one. */
constexpr bool intersects (Range other) const noexcept
{
return other.start < end && start < other.end;
}
/** Returns the range that is the intersection of the two ranges, or an empty range
with an undefined start position if they don't overlap. */
constexpr Range getIntersectionWith (Range other) const noexcept
{
return Range (jmax (start, other.start),
jmin (end, other.end));
}
/** Returns the smallest range that contains both this one and the other one. */
constexpr Range getUnionWith (Range other) const noexcept
{
return Range (jmin (start, other.start),
jmax (end, other.end));
}
/** Returns the smallest range that contains both this one and the given value. */
constexpr Range getUnionWith (const ValueType valueToInclude) const noexcept
{
return Range (jmin (valueToInclude, start),
jmax (valueToInclude, end));
}
/** Returns a given range, after moving it forwards or backwards to fit it
within this range.
If the supplied range has a greater length than this one, the return value
will be this range.
Otherwise, if the supplied range is smaller than this one, the return value
will be the new range, shifted forwards or backwards so that it doesn't extend
beyond this one, but keeping its original length.
*/
Range constrainRange (Range rangeToConstrain) const noexcept
{
const ValueType otherLen = rangeToConstrain.getLength();
return getLength() <= otherLen
? *this
: rangeToConstrain.movedToStartAt (jlimit (start, end - otherLen, rangeToConstrain.getStart()));
}
/** Scans an array of values for its min and max, and returns these as a Range. */
static Range findMinAndMax (const ValueType* values, int numValues) noexcept
{
if (numValues <= 0)
return Range();
const ValueType first (*values++);
Range r (first, first);
while (--numValues > 0) // (> 0 rather than >= 0 because we've already taken the first sample)
{
const ValueType v (*values++);
if (r.end < v) r.end = v;
if (v < r.start) r.start = v;
}
return r;
}
private:
//==============================================================================
ValueType start{}, end{};
};
} // namespace juce

View File

@ -0,0 +1,136 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
/**
A class that measures various statistics about a series of floating point
values that it is given.
@tags{Core}
*/
template <typename FloatType>
class StatisticsAccumulator
{
public:
//==============================================================================
/** Constructs a new StatisticsAccumulator. */
StatisticsAccumulator() = default;
//==============================================================================
/** Add a new value to the accumulator.
This will update all running statistics accordingly.
*/
void addValue (FloatType v) noexcept
{
jassert (juce_isfinite (v));
sum += v;
sumSquares += v * v;
++count;
if (v > maximum) maximum = v;
if (v < minimum) minimum = v;
}
/** Reset the accumulator.
This will reset all currently saved statistcs.
*/
void reset() noexcept { *this = StatisticsAccumulator<FloatType>(); }
//==============================================================================
/** Returns the average (arithmetic mean) of all previously added values.
If no values have been added yet, this will return zero.
*/
FloatType getAverage() const noexcept
{
return count > 0 ? sum / (FloatType) count
: FloatType();
}
/** Returns the variance of all previously added values.
If no values have been added yet, this will return zero.
*/
FloatType getVariance() const noexcept
{
return count > 0 ? (sumSquares - sum * sum / (FloatType) count) / (FloatType) count
: FloatType();
}
/** Returns the standard deviation of all previously added values.
If no values have been added yet, this will return zero.
*/
FloatType getStandardDeviation() const noexcept
{
return std::sqrt (getVariance());
}
/** Returns the smallest of all previously added values.
If no values have been added yet, this will return positive infinity.
*/
FloatType getMinValue() const noexcept
{
return minimum;
}
/** Returns the largest of all previously added values.
If no values have been added yet, this will return negative infinity.
*/
FloatType getMaxValue() const noexcept
{
return maximum;
}
/** Returns how many values have been added to this accumulator. */
size_t getCount() const noexcept
{
return count;
}
private:
//==============================================================================
struct KahanSum
{
KahanSum() = default;
operator FloatType() const noexcept { return sum; }
void JUCE_NO_ASSOCIATIVE_MATH_OPTIMISATIONS operator+= (FloatType value) noexcept
{
FloatType correctedValue = value - error;
FloatType newSum = sum + correctedValue;
error = (newSum - sum) - correctedValue;
sum = newSum;
}
FloatType sum{}, error{};
};
//==============================================================================
size_t count { 0 };
KahanSum sum, sumSquares;
FloatType minimum { std::numeric_limits<FloatType>::infinity() },
maximum { -std::numeric_limits<FloatType>::infinity() };
};
} // namespace juce

View File

@ -0,0 +1,100 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
#if JUCE_ENABLE_ALLOCATION_HOOKS
namespace juce
{
static AllocationHooks& getAllocationHooksForThread()
{
thread_local AllocationHooks hooks;
return hooks;
}
void notifyAllocationHooksForThread()
{
getAllocationHooksForThread().listenerList.call ([] (AllocationHooks::Listener& l)
{
l.newOrDeleteCalled();
});
}
}
void* operator new (size_t s)
{
juce::notifyAllocationHooksForThread();
return std::malloc (s);
}
void* operator new[] (size_t s)
{
juce::notifyAllocationHooksForThread();
return std::malloc (s);
}
void operator delete (void* p) noexcept
{
juce::notifyAllocationHooksForThread();
std::free (p);
}
void operator delete[] (void* p) noexcept
{
juce::notifyAllocationHooksForThread();
std::free (p);
}
void operator delete (void* p, size_t) noexcept
{
juce::notifyAllocationHooksForThread();
std::free (p);
}
void operator delete[] (void* p, size_t) noexcept
{
juce::notifyAllocationHooksForThread();
std::free (p);
}
namespace juce
{
//==============================================================================
UnitTestAllocationChecker::UnitTestAllocationChecker (UnitTest& test)
: unitTest (test)
{
getAllocationHooksForThread().addListener (this);
}
UnitTestAllocationChecker::~UnitTestAllocationChecker() noexcept
{
getAllocationHooksForThread().removeListener (this);
unitTest.expectEquals ((int) calls, 0, "new or delete was incorrectly called while allocation checker was active");
}
void UnitTestAllocationChecker::newOrDeleteCalled() noexcept { ++calls; }
}
#endif

View File

@ -0,0 +1,73 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
#if JUCE_ENABLE_ALLOCATION_HOOKS
namespace juce
{
class AllocationHooks
{
public:
struct Listener
{
virtual ~Listener() noexcept = default;
virtual void newOrDeleteCalled() noexcept = 0;
};
void addListener (Listener* l) { listenerList.add (l); }
void removeListener (Listener* l) noexcept { listenerList.remove (l); }
private:
friend void notifyAllocationHooksForThread();
ListenerList<Listener> listenerList;
};
//==============================================================================
/** Scoped checker which will cause a unit test failure if any new/delete calls
are made during the lifetime of the UnitTestAllocationChecker.
*/
class UnitTestAllocationChecker : private AllocationHooks::Listener
{
public:
/** Create a checker which will log a failure to the passed test if
any calls to new/delete are made.
Remember to call `UnitTest::beginTest` before constructing this checker!
*/
explicit UnitTestAllocationChecker (UnitTest& test);
/** Will add a failure to the test if the number of new/delete calls during
this object's lifetime was greater than zero.
*/
~UnitTestAllocationChecker() noexcept override;
private:
void newOrDeleteCalled() noexcept override;
UnitTest& unitTest;
size_t calls = 0;
};
}
#endif

View File

@ -0,0 +1,145 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
#ifndef DOXYGEN
namespace AtomicHelpers
{
template <typename T> struct DiffTypeHelper { using Type = T; };
template <typename T> struct DiffTypeHelper<T*> { using Type = std::ptrdiff_t; };
}
#endif
//==============================================================================
/**
A simple wrapper around std::atomic.
@tags{Core}
*/
template <typename Type>
struct Atomic final
{
using DiffType = typename AtomicHelpers::DiffTypeHelper<Type>::Type;
/** Creates a new value, initialised to zero. */
Atomic() noexcept : value (Type()) {}
/** Creates a new value, with a given initial value. */
Atomic (Type initialValue) noexcept : value (initialValue) {}
/** Copies another value (atomically). */
Atomic (const Atomic& other) noexcept : value (other.get()) {}
/** Destructor. */
~Atomic() noexcept
{
#if __cpp_lib_atomic_is_always_lock_free
static_assert (std::atomic<Type>::is_always_lock_free,
"This class can only be used for lock-free types");
#endif
}
/** Atomically reads and returns the current value. */
Type get() const noexcept { return value.load(); }
/** Atomically sets the current value. */
void set (Type newValue) noexcept { value = newValue; }
/** Atomically sets the current value, returning the value that was replaced. */
Type exchange (Type newValue) noexcept { return value.exchange (newValue); }
/** Atomically compares this value with a target value, and if it is equal, sets
this to be equal to a new value.
This operation is the atomic equivalent of doing this:
@code
bool compareAndSetBool (Type newValue, Type valueToCompare)
{
if (get() == valueToCompare)
{
set (newValue);
return true;
}
return false;
}
@endcode
Internally, this method calls std::atomic::compare_exchange_strong with
memory_order_seq_cst (the strictest std::memory_order).
@returns true if the comparison was true and the value was replaced; false if
the comparison failed and the value was left unchanged.
@see compareAndSetValue
*/
bool compareAndSetBool (Type newValue, Type valueToCompare) noexcept
{
return value.compare_exchange_strong (valueToCompare, newValue);
}
/** Copies another value into this one (atomically). */
Atomic<Type>& operator= (const Atomic& other) noexcept
{
value = other.value.load();
return *this;
}
/** Copies another value into this one (atomically). */
Atomic<Type>& operator= (Type newValue) noexcept
{
value = newValue;
return *this;
}
/** Atomically adds a number to this value, returning the new value. */
Type operator+= (DiffType amountToAdd) noexcept { return value += amountToAdd; }
/** Atomically subtracts a number from this value, returning the new value. */
Type operator-= (DiffType amountToSubtract) noexcept { return value -= amountToSubtract; }
/** Atomically increments this value, returning the new value. */
Type operator++() noexcept { return ++value; }
/** Atomically decrements this value, returning the new value. */
Type operator--() noexcept { return --value; }
/** Implements a memory read/write barrier.
Internally this calls std::atomic_thread_fence with
memory_order_seq_cst (the strictest std::memory_order).
*/
void memoryBarrier() noexcept { atomic_thread_fence (std::memory_order_seq_cst); }
/** The std::atomic object that this class operates on. */
std::atomic<Type> value;
//==============================================================================
#ifndef DOXYGEN
[[deprecated ("This method has been deprecated as there is no equivalent method in "
"std::atomic. Use compareAndSetBool instead.")]]
Type compareAndSetValue (Type, Type) noexcept;
#endif
};
} // namespace juce

View File

@ -0,0 +1,224 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
#if ! defined (DOXYGEN) && (JUCE_MAC || JUCE_IOS)
#include <libkern/OSByteOrder.h>
#endif
namespace juce
{
//==============================================================================
/** Contains static methods for converting the byte order between different
endiannesses.
@tags{Core}
*/
class JUCE_API ByteOrder
{
public:
//==============================================================================
/** Swaps the upper and lower bytes of a 16-bit integer. */
constexpr static uint16 swap (uint16 value) noexcept;
/** Swaps the upper and lower bytes of a 16-bit integer. */
constexpr static int16 swap (int16 value) noexcept;
/** Reverses the order of the 4 bytes in a 32-bit integer. */
static uint32 swap (uint32 value) noexcept;
/** Reverses the order of the 4 bytes in a 32-bit integer. */
static int32 swap (int32 value) noexcept;
/** Reverses the order of the 8 bytes in a 64-bit integer. */
static uint64 swap (uint64 value) noexcept;
/** Reverses the order of the 8 bytes in a 64-bit integer. */
static int64 swap (int64 value) noexcept;
/** Returns a garbled float which has the reverse byte-order of the original. */
static float swap (float value) noexcept;
/** Returns a garbled double which has the reverse byte-order of the original. */
static double swap (double value) noexcept;
//==============================================================================
/** Swaps the byte order of a signed or unsigned integer if the CPU is big-endian */
template <typename Type>
static Type swapIfBigEndian (Type value) noexcept
{
#if JUCE_LITTLE_ENDIAN
return value;
#else
return swap (value);
#endif
}
/** Swaps the byte order of a signed or unsigned integer if the CPU is little-endian */
template <typename Type>
static Type swapIfLittleEndian (Type value) noexcept
{
#if JUCE_LITTLE_ENDIAN
return swap (value);
#else
return value;
#endif
}
//==============================================================================
/** Turns 4 bytes into a little-endian integer. */
constexpr static uint32 littleEndianInt (const void* bytes) noexcept;
/** Turns 8 bytes into a little-endian integer. */
constexpr static uint64 littleEndianInt64 (const void* bytes) noexcept;
/** Turns 2 bytes into a little-endian integer. */
constexpr static uint16 littleEndianShort (const void* bytes) noexcept;
/** Converts 3 little-endian bytes into a signed 24-bit value (which is sign-extended to 32 bits). */
constexpr static int littleEndian24Bit (const void* bytes) noexcept;
/** Copies a 24-bit number to 3 little-endian bytes. */
static void littleEndian24BitToChars (int32 value, void* destBytes) noexcept;
//==============================================================================
/** Turns 4 bytes into a big-endian integer. */
constexpr static uint32 bigEndianInt (const void* bytes) noexcept;
/** Turns 8 bytes into a big-endian integer. */
constexpr static uint64 bigEndianInt64 (const void* bytes) noexcept;
/** Turns 2 bytes into a big-endian integer. */
constexpr static uint16 bigEndianShort (const void* bytes) noexcept;
/** Converts 3 big-endian bytes into a signed 24-bit value (which is sign-extended to 32 bits). */
constexpr static int bigEndian24Bit (const void* bytes) noexcept;
/** Copies a 24-bit number to 3 big-endian bytes. */
static void bigEndian24BitToChars (int32 value, void* destBytes) noexcept;
//==============================================================================
/** Constructs a 16-bit integer from its constituent bytes, in order of significance. */
constexpr static uint16 makeInt (uint8 leastSig, uint8 mostSig) noexcept;
/** Constructs a 32-bit integer from its constituent bytes, in order of significance. */
constexpr static uint32 makeInt (uint8 leastSig, uint8 byte1, uint8 byte2, uint8 mostSig) noexcept;
/** Constructs a 64-bit integer from its constituent bytes, in order of significance. */
constexpr static uint64 makeInt (uint8 leastSig, uint8 byte1, uint8 byte2, uint8 byte3,
uint8 byte4, uint8 byte5, uint8 byte6, uint8 mostSig) noexcept;
//==============================================================================
/** Returns true if the current CPU is big-endian. */
constexpr static bool isBigEndian() noexcept
{
#if JUCE_LITTLE_ENDIAN
return false;
#else
return true;
#endif
}
private:
ByteOrder() = delete;
};
//==============================================================================
constexpr inline uint16 ByteOrder::swap (uint16 v) noexcept { return static_cast<uint16> ((v << 8) | (v >> 8)); }
constexpr inline int16 ByteOrder::swap (int16 v) noexcept { return static_cast<int16> (swap (static_cast<uint16> (v))); }
inline int32 ByteOrder::swap (int32 v) noexcept { return static_cast<int32> (swap (static_cast<uint32> (v))); }
inline int64 ByteOrder::swap (int64 v) noexcept { return static_cast<int64> (swap (static_cast<uint64> (v))); }
inline float ByteOrder::swap (float v) noexcept { union { uint32 asUInt; float asFloat; } n; n.asFloat = v; n.asUInt = swap (n.asUInt); return n.asFloat; }
inline double ByteOrder::swap (double v) noexcept { union { uint64 asUInt; double asFloat; } n; n.asFloat = v; n.asUInt = swap (n.asUInt); return n.asFloat; }
#if JUCE_MSVC && ! defined (__INTEL_COMPILER)
#pragma intrinsic (_byteswap_ulong)
#endif
inline uint32 ByteOrder::swap (uint32 n) noexcept
{
#if JUCE_MAC || JUCE_IOS
return OSSwapInt32 (n);
#elif (JUCE_GCC || JUCE_CLANG) && JUCE_INTEL && ! JUCE_NO_INLINE_ASM
asm("bswap %%eax" : "=a"(n) : "a"(n));
return n;
#elif JUCE_MSVC
return _byteswap_ulong (n);
#elif JUCE_ANDROID
return bswap_32 (n);
#else
return (n << 24) | (n >> 24) | ((n & 0xff00) << 8) | ((n & 0xff0000) >> 8);
#endif
}
inline uint64 ByteOrder::swap (uint64 value) noexcept
{
#if JUCE_MAC || JUCE_IOS
return OSSwapInt64 (value);
#elif JUCE_MSVC
return _byteswap_uint64 (value);
#else
return (((uint64) swap ((uint32) value)) << 32) | swap ((uint32) (value >> 32));
#endif
}
constexpr inline uint16 ByteOrder::makeInt (uint8 b0, uint8 b1) noexcept
{
return static_cast<uint16> (static_cast<uint16> (b0) | (static_cast<uint16> (b1) << 8));
}
constexpr inline uint32 ByteOrder::makeInt (uint8 b0, uint8 b1, uint8 b2, uint8 b3) noexcept
{
return static_cast<uint32> (b0) | (static_cast<uint32> (b1) << 8)
| (static_cast<uint32> (b2) << 16) | (static_cast<uint32> (b3) << 24);
}
constexpr inline uint64 ByteOrder::makeInt (uint8 b0, uint8 b1, uint8 b2, uint8 b3, uint8 b4, uint8 b5, uint8 b6, uint8 b7) noexcept
{
return static_cast<uint64> (b0) | (static_cast<uint64> (b1) << 8) | (static_cast<uint64> (b2) << 16) | (static_cast<uint64> (b3) << 24)
| (static_cast<uint64> (b4) << 32) | (static_cast<uint64> (b5) << 40) | (static_cast<uint64> (b6) << 48) | (static_cast<uint64> (b7) << 56);
}
constexpr inline uint16 ByteOrder::littleEndianShort (const void* bytes) noexcept { return makeInt (static_cast<const uint8*> (bytes)[0], static_cast<const uint8*> (bytes)[1]); }
constexpr inline uint32 ByteOrder::littleEndianInt (const void* bytes) noexcept { return makeInt (static_cast<const uint8*> (bytes)[0], static_cast<const uint8*> (bytes)[1],
static_cast<const uint8*> (bytes)[2], static_cast<const uint8*> (bytes)[3]); }
constexpr inline uint64 ByteOrder::littleEndianInt64 (const void* bytes) noexcept { return makeInt (static_cast<const uint8*> (bytes)[0], static_cast<const uint8*> (bytes)[1],
static_cast<const uint8*> (bytes)[2], static_cast<const uint8*> (bytes)[3],
static_cast<const uint8*> (bytes)[4], static_cast<const uint8*> (bytes)[5],
static_cast<const uint8*> (bytes)[6], static_cast<const uint8*> (bytes)[7]); }
constexpr inline uint16 ByteOrder::bigEndianShort (const void* bytes) noexcept { return makeInt (static_cast<const uint8*> (bytes)[1], static_cast<const uint8*> (bytes)[0]); }
constexpr inline uint32 ByteOrder::bigEndianInt (const void* bytes) noexcept { return makeInt (static_cast<const uint8*> (bytes)[3], static_cast<const uint8*> (bytes)[2],
static_cast<const uint8*> (bytes)[1], static_cast<const uint8*> (bytes)[0]); }
constexpr inline uint64 ByteOrder::bigEndianInt64 (const void* bytes) noexcept { return makeInt (static_cast<const uint8*> (bytes)[7], static_cast<const uint8*> (bytes)[6],
static_cast<const uint8*> (bytes)[5], static_cast<const uint8*> (bytes)[4],
static_cast<const uint8*> (bytes)[3], static_cast<const uint8*> (bytes)[2],
static_cast<const uint8*> (bytes)[1], static_cast<const uint8*> (bytes)[0]); }
constexpr inline int32 ByteOrder::littleEndian24Bit (const void* bytes) noexcept { return (int32) ((((uint32) static_cast<const int8*> (bytes)[2]) << 16) | (((uint32) static_cast<const uint8*> (bytes)[1]) << 8) | ((uint32) static_cast<const uint8*> (bytes)[0])); }
constexpr inline int32 ByteOrder::bigEndian24Bit (const void* bytes) noexcept { return (int32) ((((uint32) static_cast<const int8*> (bytes)[0]) << 16) | (((uint32) static_cast<const uint8*> (bytes)[1]) << 8) | ((uint32) static_cast<const uint8*> (bytes)[2])); }
inline void ByteOrder::littleEndian24BitToChars (int32 value, void* destBytes) noexcept { static_cast<uint8*> (destBytes)[0] = (uint8) value; static_cast<uint8*> (destBytes)[1] = (uint8) (value >> 8); static_cast<uint8*> (destBytes)[2] = (uint8) (value >> 16); }
inline void ByteOrder::bigEndian24BitToChars (int32 value, void* destBytes) noexcept { static_cast<uint8*> (destBytes)[0] = (uint8) (value >> 16); static_cast<uint8*> (destBytes)[1] = (uint8) (value >> 8); static_cast<uint8*> (destBytes)[2] = (uint8) value; }
} // namespace juce

View File

@ -0,0 +1,58 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
/**
Used by container classes as an indirect way to delete an object of a
particular type.
The generic implementation of this class simply calls 'delete', but you can
create a specialised version of it for a particular class if you need to
delete that type of object in a more appropriate way.
@see OwnedArray
@tags{Core}
*/
template <typename ObjectType>
struct ContainerDeletePolicy
{
static void destroy (ObjectType* object)
{
// If the line below triggers a compiler error, it means that you are using
// an incomplete type for ObjectType (for example, a type that is declared
// but not defined). This is a problem because then the following delete is
// undefined behaviour. The purpose of the sizeof is to capture this situation.
// If this was caused by a OwnedArray of a forward-declared type, move the
// implementation of all methods trying to use the OwnedArray (e.g. the destructor
// of the class owning it) into cpp files where they can see to the definition
// of ObjectType. This should fix the error.
ignoreUnused (sizeof (ObjectType));
delete object;
}
};
} // namespace juce

View File

@ -0,0 +1,350 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
#if ! (DOXYGEN || JUCE_EXCEPTIONS_DISABLED)
namespace HeapBlockHelper
{
template <bool shouldThrow>
struct ThrowOnFail { static void checkPointer (void*) {} };
template <>
struct ThrowOnFail<true> { static void checkPointer (void* data) { if (data == nullptr) throw std::bad_alloc(); } };
}
#endif
//==============================================================================
/**
Very simple container class to hold a pointer to some data on the heap.
When you need to allocate some heap storage for something, always try to use
this class instead of allocating the memory directly using malloc/free.
A HeapBlock<char> object can be treated in pretty much exactly the same way
as an char*, but as long as you allocate it on the stack or as a class member,
it's almost impossible for it to leak memory.
It also makes your code much more concise and readable than doing the same thing
using direct allocations,
E.g. instead of this:
@code
int* temp = (int*) malloc (1024 * sizeof (int));
memcpy (temp, xyz, 1024 * sizeof (int));
free (temp);
temp = (int*) calloc (2048 * sizeof (int));
temp[0] = 1234;
memcpy (foobar, temp, 2048 * sizeof (int));
free (temp);
@endcode
..you could just write this:
@code
HeapBlock<int> temp (1024);
memcpy (temp, xyz, 1024 * sizeof (int));
temp.calloc (2048);
temp[0] = 1234;
memcpy (foobar, temp, 2048 * sizeof (int));
@endcode
The class is extremely lightweight, containing only a pointer to the
data, and exposes malloc/realloc/calloc/free methods that do the same jobs
as their less object-oriented counterparts. Despite adding safety, you probably
won't sacrifice any performance by using this in place of normal pointers.
The throwOnFailure template parameter can be set to true if you'd like the class
to throw a std::bad_alloc exception when an allocation fails. If this is false,
then a failed allocation will just leave the heapblock with a null pointer (assuming
that the system's malloc() function doesn't throw).
@see Array, OwnedArray, MemoryBlock
@tags{Core}
*/
template <class ElementType, bool throwOnFailure = false>
class HeapBlock
{
private:
template <class OtherElementType>
using AllowConversion = typename std::enable_if<std::is_base_of<typename std::remove_pointer<ElementType>::type,
typename std::remove_pointer<OtherElementType>::type>::value>::type;
public:
//==============================================================================
/** Creates a HeapBlock which is initially just a null pointer.
After creation, you can resize the array using the malloc(), calloc(),
or realloc() methods.
*/
HeapBlock() = default;
/** Creates a HeapBlock containing a number of elements.
The contents of the block are undefined, as it will have been created by a
malloc call.
If you want an array of zero values, you can use the calloc() method or the
other constructor that takes an InitialisationState parameter.
*/
template <typename SizeType, std::enable_if_t<std::is_integral<SizeType>::value, int> = 0>
explicit HeapBlock (SizeType numElements)
: data (static_cast<ElementType*> (std::malloc (static_cast<size_t> (numElements) * sizeof (ElementType))))
{
throwOnAllocationFailure();
}
/** Creates a HeapBlock containing a number of elements.
The initialiseToZero parameter determines whether the new memory should be cleared,
or left uninitialised.
*/
template <typename SizeType, std::enable_if_t<std::is_integral<SizeType>::value, int> = 0>
HeapBlock (SizeType numElements, bool initialiseToZero)
: data (static_cast<ElementType*> (initialiseToZero
? std::calloc (static_cast<size_t> (numElements), sizeof (ElementType))
: std::malloc (static_cast<size_t> (numElements) * sizeof (ElementType))))
{
throwOnAllocationFailure();
}
/** Destructor.
This will free the data, if any has been allocated.
*/
~HeapBlock()
{
std::free (data);
}
/** Move constructor */
HeapBlock (HeapBlock&& other) noexcept
: data (other.data)
{
other.data = nullptr;
}
/** Move assignment operator */
HeapBlock& operator= (HeapBlock&& other) noexcept
{
std::swap (data, other.data);
return *this;
}
/** Converting move constructor.
Only enabled if this is a HeapBlock<Base*> and the other object is a HeapBlock<Derived*>,
where std::is_base_of<Base, Derived>::value == true.
*/
template <class OtherElementType, bool otherThrowOnFailure, typename = AllowConversion<OtherElementType>>
HeapBlock (HeapBlock<OtherElementType, otherThrowOnFailure>&& other) noexcept
: data (reinterpret_cast<ElementType*> (other.data))
{
other.data = nullptr;
}
/** Converting move assignment operator.
Only enabled if this is a HeapBlock<Base*> and the other object is a HeapBlock<Derived*>,
where std::is_base_of<Base, Derived>::value == true.
*/
template <class OtherElementType, bool otherThrowOnFailure, typename = AllowConversion<OtherElementType>>
HeapBlock& operator= (HeapBlock<OtherElementType, otherThrowOnFailure>&& other) noexcept
{
free();
data = reinterpret_cast<ElementType*> (other.data);
other.data = nullptr;
return *this;
}
//==============================================================================
/** Returns a raw pointer to the allocated data.
This may be a null pointer if the data hasn't yet been allocated, or if it has been
freed by calling the free() method.
*/
inline operator ElementType*() const noexcept { return data; }
/** Returns a raw pointer to the allocated data.
This may be a null pointer if the data hasn't yet been allocated, or if it has been
freed by calling the free() method.
*/
inline ElementType* get() const noexcept { return data; }
/** Returns a raw pointer to the allocated data.
This may be a null pointer if the data hasn't yet been allocated, or if it has been
freed by calling the free() method.
*/
inline ElementType* getData() const noexcept { return data; }
/** Returns a void pointer to the allocated data.
This may be a null pointer if the data hasn't yet been allocated, or if it has been
freed by calling the free() method.
*/
inline operator void*() const noexcept { return static_cast<void*> (data); }
/** Returns a void pointer to the allocated data.
This may be a null pointer if the data hasn't yet been allocated, or if it has been
freed by calling the free() method.
*/
inline operator const void*() const noexcept { return static_cast<const void*> (data); }
/** Lets you use indirect calls to the first element in the array.
Obviously this will cause problems if the array hasn't been initialised, because it'll
be referencing a null pointer.
*/
inline ElementType* operator->() const noexcept { return data; }
/** Returns a reference to one of the data elements.
Obviously there's no bounds-checking here, as this object is just a dumb pointer and
has no idea of the size it currently has allocated.
*/
template <typename IndexType>
ElementType& operator[] (IndexType index) const noexcept { return data [index]; }
/** Returns a pointer to a data element at an offset from the start of the array.
This is the same as doing pointer arithmetic on the raw pointer itself.
*/
template <typename IndexType>
ElementType* operator+ (IndexType index) const noexcept { return data + index; }
//==============================================================================
/** Compares the pointer with another pointer.
This can be handy for checking whether this is a null pointer.
*/
inline bool operator== (const ElementType* otherPointer) const noexcept { return otherPointer == data; }
/** Compares the pointer with another pointer.
This can be handy for checking whether this is a null pointer.
*/
inline bool operator!= (const ElementType* otherPointer) const noexcept { return otherPointer != data; }
//==============================================================================
/** Allocates a specified amount of memory.
This uses the normal malloc to allocate an amount of memory for this object.
Any previously allocated memory will be freed by this method.
The number of bytes allocated will be (newNumElements * elementSize). Normally
you wouldn't need to specify the second parameter, but it can be handy if you need
to allocate a size in bytes rather than in terms of the number of elements.
The data that is allocated will be freed when this object is deleted, or when you
call free() or any of the allocation methods.
*/
template <typename SizeType>
void malloc (SizeType newNumElements, size_t elementSize = sizeof (ElementType))
{
std::free (data);
data = static_cast<ElementType*> (std::malloc (static_cast<size_t> (newNumElements) * elementSize));
throwOnAllocationFailure();
}
/** Allocates a specified amount of memory and clears it.
This does the same job as the malloc() method, but clears the memory that it allocates.
*/
template <typename SizeType>
void calloc (SizeType newNumElements, const size_t elementSize = sizeof (ElementType))
{
std::free (data);
data = static_cast<ElementType*> (std::calloc (static_cast<size_t> (newNumElements), elementSize));
throwOnAllocationFailure();
}
/** Allocates a specified amount of memory and optionally clears it.
This does the same job as either malloc() or calloc(), depending on the
initialiseToZero parameter.
*/
template <typename SizeType>
void allocate (SizeType newNumElements, bool initialiseToZero)
{
std::free (data);
data = static_cast<ElementType*> (initialiseToZero
? std::calloc (static_cast<size_t> (newNumElements), sizeof (ElementType))
: std::malloc (static_cast<size_t> (newNumElements) * sizeof (ElementType)));
throwOnAllocationFailure();
}
/** Re-allocates a specified amount of memory.
The semantics of this method are the same as malloc() and calloc(), but it
uses realloc() to keep as much of the existing data as possible.
*/
template <typename SizeType>
void realloc (SizeType newNumElements, size_t elementSize = sizeof (ElementType))
{
data = static_cast<ElementType*> (data == nullptr ? std::malloc (static_cast<size_t> (newNumElements) * elementSize)
: std::realloc (data, static_cast<size_t> (newNumElements) * elementSize));
throwOnAllocationFailure();
}
/** Frees any currently-allocated data.
This will free the data and reset this object to be a null pointer.
*/
void free() noexcept
{
std::free (data);
data = nullptr;
}
/** Swaps this object's data with the data of another HeapBlock.
The two objects simply exchange their data pointers.
*/
template <bool otherBlockThrows>
void swapWith (HeapBlock<ElementType, otherBlockThrows>& other) noexcept
{
std::swap (data, other.data);
}
/** This fills the block with zeros, up to the number of elements specified.
Since the block has no way of knowing its own size, you must make sure that the number of
elements you specify doesn't exceed the allocated size.
*/
template <typename SizeType>
void clear (SizeType numElements) noexcept
{
zeromem (data, sizeof (ElementType) * static_cast<size_t> (numElements));
}
/** This typedef can be used to get the type of the heapblock's elements. */
using Type = ElementType;
private:
//==============================================================================
ElementType* data = nullptr;
void throwOnAllocationFailure() const
{
#if JUCE_EXCEPTIONS_DISABLED
jassert (data != nullptr); // without exceptions, you'll need to find a better way to handle this failure case.
#else
HeapBlockHelper::ThrowOnFail<throwOnFailure>::checkPointer (data);
#endif
}
template <class OtherElementType, bool otherThrowOnFailure>
friend class HeapBlock;
#if ! (defined (JUCE_DLL) || defined (JUCE_DLL_BUILD))
JUCE_DECLARE_NON_COPYABLE (HeapBlock)
JUCE_PREVENT_HEAP_ALLOCATION // Creating a 'new HeapBlock' would be missing the point!
#endif
};
} // namespace juce

View File

@ -0,0 +1,144 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
/**
This class is a useful way of tracking down hard to find memory leaks when the
regular LeakedObjectDetector isn't enough.
As well as firing when any instances of the OwnerClass type are leaked, it will
print out a stack trace showing where the leaked object was created. This is obviously
quite a heavyweight task so, unlike the LeakedObjectDetector which should be always
be added to your classes, you should only use this object temporarily when you are
debugging and remove it when finished.
To use it, use the JUCE_HEAVYWEIGHT_LEAK_DETECTOR macro as a simple way to put
one in your class declaration.
@tags{Core}
*/
template <class OwnerClass>
class HeavyweightLeakedObjectDetector
{
public:
//==============================================================================
HeavyweightLeakedObjectDetector() noexcept { getBacktraceMap()[this] = SystemStats::getStackBacktrace(); }
HeavyweightLeakedObjectDetector (const HeavyweightLeakedObjectDetector&) noexcept { getBacktraceMap()[this] = SystemStats::getStackBacktrace(); }
~HeavyweightLeakedObjectDetector() { getBacktraceMap().erase (this); }
private:
//==============================================================================
typedef std::map<HeavyweightLeakedObjectDetector<OwnerClass>*, String> BacktraceMap;
//==============================================================================
struct BacktraceMapHolder
{
BacktraceMapHolder() = default;
~BacktraceMapHolder()
{
if (map.size() > 0)
{
DBG ("*** Leaked objects detected: " << map.size() << " instance(s) of class " << getLeakedObjectClassName());
DBG (getFormattedBacktracesString());
/** If you hit this, then you've leaked one or more objects of the type specified by
the 'OwnerClass' template parameter - the name and stack trace of its creation should
have been printed by the lines above.
If you're leaking, it's probably because you're using old-fashioned, non-RAII techniques for
your object management. Tut, tut. Always, always use std::unique_ptrs, OwnedArrays,
ReferenceCountedObjects, etc, and avoid the 'delete' operator at all costs!
*/
jassertfalse;
}
}
String getFormattedBacktracesString() const
{
String str;
int counter = 1;
for (auto& bt : map)
{
str << "\nBacktrace " << String (counter++) << "\n"
<< "-----------------------------------------------------------------" << "\n"
<< bt.second;
}
return str;
}
BacktraceMap map;
};
static BacktraceMap& getBacktraceMap()
{
static BacktraceMapHolder holder;
return holder.map;
}
static const char* getLeakedObjectClassName()
{
return OwnerClass::getLeakedObjectClassName();
}
};
//==============================================================================
#if DOXYGEN || ! defined (JUCE_HEAVYWEIGHT_LEAK_DETECTOR)
#if (DOXYGEN || JUCE_CHECK_MEMORY_LEAKS)
/** This macro lets you embed a heavyweight leak-detecting object inside a class.
To use it, simply declare a JUCE_HEAVYWEIGHT_LEAK_DETECTOR (YourClassName) inside a private section
of the class declaration. E.g.
@code
class MyClass
{
public:
MyClass();
void blahBlah();
private:
JUCE_HEAVYWEIGHT_LEAK_DETECTOR (MyClass)
};
@endcode
NB: you should only use this when you really need to track down a tricky memory leak, and
should never leave one of these inside a class!
@see HeavyweightLeakedObjectDetector, JUCE_LEAK_DETECTOR, LeakedObjectDetector
*/
#define JUCE_HEAVYWEIGHT_LEAK_DETECTOR(OwnerClass) \
friend class juce::HeavyweightLeakedObjectDetector<OwnerClass>; \
static const char* getLeakedObjectClassName() noexcept { return #OwnerClass; } \
juce::HeavyweightLeakedObjectDetector<OwnerClass> JUCE_JOIN_MACRO (leakDetector, __LINE__);
#else
#define JUCE_HEAVYWEIGHT_LEAK_DETECTOR(OwnerClass)
#endif
#endif
} // namespace juce

View File

@ -0,0 +1,142 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
/**
Embedding an instance of this class inside another class can be used as a low-overhead
way of detecting leaked instances.
This class keeps an internal static count of the number of instances that are
active, so that when the app is shutdown and the static destructors are called,
it can check whether there are any left-over instances that may have been leaked.
To use it, use the JUCE_LEAK_DETECTOR macro as a simple way to put one in your
class declaration. Have a look through the juce codebase for examples, it's used
in most of the classes.
@tags{Core}
*/
template <class OwnerClass>
class LeakedObjectDetector
{
public:
//==============================================================================
LeakedObjectDetector() noexcept { ++(getCounter().numObjects); }
LeakedObjectDetector (const LeakedObjectDetector&) noexcept { ++(getCounter().numObjects); }
LeakedObjectDetector& operator= (const LeakedObjectDetector&) noexcept = default;
~LeakedObjectDetector()
{
if (--(getCounter().numObjects) < 0)
{
DBG ("*** Dangling pointer deletion! Class: " << getLeakedObjectClassName());
/** If you hit this, then you've managed to delete more instances of this class than you've
created.. That indicates that you're deleting some dangling pointers.
Note that although this assertion will have been triggered during a destructor, it might
not be this particular deletion that's at fault - the incorrect one may have happened
at an earlier point in the program, and simply not been detected until now.
Most errors like this are caused by using old-fashioned, non-RAII techniques for
your object management. Tut, tut. Always, always use std::unique_ptrs, OwnedArrays,
ReferenceCountedObjects, etc, and avoid the 'delete' operator at all costs!
*/
jassertfalse;
}
}
private:
//==============================================================================
class LeakCounter
{
public:
LeakCounter() = default;
~LeakCounter()
{
if (numObjects.value > 0)
{
DBG ("*** Leaked objects detected: " << numObjects.value << " instance(s) of class " << getLeakedObjectClassName());
/** If you hit this, then you've leaked one or more objects of the type specified by
the 'OwnerClass' template parameter - the name should have been printed by the line above.
If you're leaking, it's probably because you're using old-fashioned, non-RAII techniques for
your object management. Tut, tut. Always, always use std::unique_ptrs, OwnedArrays,
ReferenceCountedObjects, etc, and avoid the 'delete' operator at all costs!
*/
jassertfalse;
}
}
Atomic<int> numObjects;
};
static const char* getLeakedObjectClassName()
{
return OwnerClass::getLeakedObjectClassName();
}
static LeakCounter& getCounter() noexcept
{
static LeakCounter counter;
return counter;
}
};
//==============================================================================
#if DOXYGEN || ! defined (JUCE_LEAK_DETECTOR)
#if (DOXYGEN || JUCE_CHECK_MEMORY_LEAKS)
/** This macro lets you embed a leak-detecting object inside a class.
To use it, simply declare a JUCE_LEAK_DETECTOR(YourClassName) inside a private section
of the class declaration. E.g.
@code
class MyClass
{
public:
MyClass();
void blahBlah();
private:
JUCE_LEAK_DETECTOR (MyClass)
};
@endcode
@see JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR, LeakedObjectDetector
*/
#define JUCE_LEAK_DETECTOR(OwnerClass) \
friend class juce::LeakedObjectDetector<OwnerClass>; \
static const char* getLeakedObjectClassName() noexcept { return #OwnerClass; } \
juce::LeakedObjectDetector<OwnerClass> JUCE_JOIN_MACRO (leakDetector, __LINE__);
#else
#define JUCE_LEAK_DETECTOR(OwnerClass)
#endif
#endif
} // namespace juce

View File

@ -0,0 +1,200 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
/** Fills a block of memory with zeros. */
inline void zeromem (void* memory, size_t numBytes) noexcept { memset (memory, 0, numBytes); }
/** Overwrites a structure or object with zeros. */
template <typename Type>
inline void zerostruct (Type& structure) noexcept { memset ((void*) &structure, 0, sizeof (structure)); }
/** Delete an object pointer, and sets the pointer to null.
Remember that it's not good c++ practice to use delete directly - always try to use a std::unique_ptr
or other automatic lifetime-management system rather than resorting to deleting raw pointers!
*/
template <typename Type>
inline void deleteAndZero (Type& pointer) { delete pointer; pointer = nullptr; }
/** A handy function to round up a pointer to the nearest multiple of a given number of bytes.
alignmentBytes must be a power of two. */
template <typename Type, typename IntegerType>
inline Type* snapPointerToAlignment (Type* basePointer, IntegerType alignmentBytes) noexcept
{
return (Type*) ((((size_t) basePointer) + (alignmentBytes - 1)) & ~(alignmentBytes - 1));
}
/** A handy function which returns the difference between any two pointers, in bytes.
The address of the second pointer is subtracted from the first, and the difference in bytes is returned.
*/
template <typename Type1, typename Type2>
inline int getAddressDifference (Type1* pointer1, Type2* pointer2) noexcept { return (int) (((const char*) pointer1) - (const char*) pointer2); }
/** If a pointer is non-null, this returns a new copy of the object that it points to, or safely returns
nullptr if the pointer is null.
*/
template <class Type>
inline Type* createCopyIfNotNull (const Type* objectToCopy) { return objectToCopy != nullptr ? new Type (*objectToCopy) : nullptr; }
//==============================================================================
/** A handy function to read un-aligned memory without a performance penalty or bus-error. */
template <typename Type>
inline Type readUnaligned (const void* srcPtr) noexcept
{
Type value;
memcpy (&value, srcPtr, sizeof (Type));
return value;
}
/** A handy function to write un-aligned memory without a performance penalty or bus-error. */
template <typename Type>
inline void writeUnaligned (void* dstPtr, Type value) noexcept
{
memcpy (dstPtr, &value, sizeof (Type));
}
//==============================================================================
/** Casts a pointer to another type via `void*`, which suppresses the cast-align
warning which sometimes arises when casting pointers to types with different
alignment.
You should only use this when you know for a fact that the input pointer points
to a region that has suitable alignment for `Type`, e.g. regions returned from
malloc/calloc that should be suitable for any non-over-aligned type.
*/
template <typename Type, typename std::enable_if<std::is_pointer<Type>::value, int>::type = 0>
inline Type unalignedPointerCast (void* ptr) noexcept
{
return reinterpret_cast<Type> (ptr);
}
/** Casts a pointer to another type via `void*`, which suppresses the cast-align
warning which sometimes arises when casting pointers to types with different
alignment.
You should only use this when you know for a fact that the input pointer points
to a region that has suitable alignment for `Type`, e.g. regions returned from
malloc/calloc that should be suitable for any non-over-aligned type.
*/
template <typename Type, typename std::enable_if<std::is_pointer<Type>::value, int>::type = 0>
inline Type unalignedPointerCast (const void* ptr) noexcept
{
return reinterpret_cast<Type> (ptr);
}
/** A handy function which adds a number of bytes to any type of pointer and returns the result.
This can be useful to avoid casting pointers to a char* and back when you want to move them by
a specific number of bytes,
*/
template <typename Type, typename IntegerType>
inline Type* addBytesToPointer (Type* basePointer, IntegerType bytes) noexcept
{
return unalignedPointerCast<Type*> (reinterpret_cast<char*> (basePointer) + bytes);
}
/** A handy function which adds a number of bytes to any type of pointer and returns the result.
This can be useful to avoid casting pointers to a char* and back when you want to move them by
a specific number of bytes,
*/
template <typename Type, typename IntegerType>
inline const Type* addBytesToPointer (const Type* basePointer, IntegerType bytes) noexcept
{
return unalignedPointerCast<const Type*> (reinterpret_cast<const char*> (basePointer) + bytes);
}
//==============================================================================
#if JUCE_MAC || JUCE_IOS || DOXYGEN
/** A handy C++ wrapper that creates and deletes an NSAutoreleasePool object using RAII.
You should use the JUCE_AUTORELEASEPOOL macro to create a local auto-release pool on the stack.
@tags{Core}
*/
class JUCE_API ScopedAutoReleasePool
{
public:
ScopedAutoReleasePool();
~ScopedAutoReleasePool();
private:
void* pool;
JUCE_DECLARE_NON_COPYABLE (ScopedAutoReleasePool)
};
/** A macro that can be used to easily declare a local ScopedAutoReleasePool
object for RAII-based obj-C autoreleasing.
Because this may use the \@autoreleasepool syntax, you must follow the macro with
a set of braces to mark the scope of the pool.
*/
#if (JUCE_COMPILER_SUPPORTS_ARC && defined (__OBJC__)) || DOXYGEN
#define JUCE_AUTORELEASEPOOL @autoreleasepool
#else
#define JUCE_AUTORELEASEPOOL const juce::ScopedAutoReleasePool JUCE_JOIN_MACRO (autoReleasePool_, __LINE__);
#endif
#else
#define JUCE_AUTORELEASEPOOL
#endif
//==============================================================================
/* In a Windows DLL build, we'll expose some malloc/free functions that live inside the DLL, and use these for
allocating all the objects - that way all juce objects in the DLL and in the host will live in the same heap,
avoiding problems when an object is created in one module and passed across to another where it is deleted.
By piggy-backing on the JUCE_LEAK_DETECTOR macro, these allocators can be injected into most juce classes.
*/
#if JUCE_MSVC && (defined (JUCE_DLL) || defined (JUCE_DLL_BUILD)) && ! (JUCE_DISABLE_DLL_ALLOCATORS || DOXYGEN)
extern JUCE_API void* juceDLL_malloc (size_t);
extern JUCE_API void juceDLL_free (void*);
#define JUCE_LEAK_DETECTOR(OwnerClass) public:\
static void* operator new (size_t sz) { return juce::juceDLL_malloc (sz); } \
static void* operator new (size_t, void* p) { return p; } \
static void operator delete (void* p) { juce::juceDLL_free (p); } \
static void operator delete (void*, void*) {}
#endif
//==============================================================================
/** (Deprecated) This was a Windows-specific way of checking for object leaks - now please
use the JUCE_LEAK_DETECTOR instead.
*/
#ifndef juce_UseDebuggingNewOperator
#define juce_UseDebuggingNewOperator
#endif
/** Converts an owning raw pointer into a unique_ptr, deriving the
type of the unique_ptr automatically.
This should only be used with pointers to single objects.
Do NOT pass a pointer to an array to this function, as the
destructor of the unique_ptr will incorrectly call `delete`
instead of `delete[]` on the pointer.
*/
template <typename T>
std::unique_ptr<T> rawToUniquePtr (T* ptr)
{
return std::unique_ptr<T> (ptr);
}
} // namespace juce

View File

@ -0,0 +1,413 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
MemoryBlock::MemoryBlock() noexcept {}
MemoryBlock::MemoryBlock (size_t initialSize, bool initialiseToZero)
{
if (initialSize > 0)
{
size = initialSize;
data.allocate (initialSize, initialiseToZero);
}
else
{
size = 0;
}
}
MemoryBlock::MemoryBlock (const MemoryBlock& other)
: size (other.size)
{
if (size > 0)
{
jassert (other.data != nullptr);
data.malloc (size);
memcpy (data, other.data, size);
}
}
MemoryBlock::MemoryBlock (const void* const dataToInitialiseFrom, const size_t sizeInBytes)
: size (sizeInBytes)
{
jassert (((ssize_t) sizeInBytes) >= 0);
if (size > 0)
{
jassert (dataToInitialiseFrom != nullptr); // non-zero size, but a zero pointer passed-in?
data.malloc (size);
if (dataToInitialiseFrom != nullptr)
memcpy (data, dataToInitialiseFrom, size);
}
}
MemoryBlock::~MemoryBlock() noexcept
{
}
MemoryBlock& MemoryBlock::operator= (const MemoryBlock& other)
{
if (this != &other)
{
setSize (other.size, false);
memcpy (data, other.data, size);
}
return *this;
}
MemoryBlock::MemoryBlock (MemoryBlock&& other) noexcept
: data (std::move (other.data)),
size (other.size)
{
}
MemoryBlock& MemoryBlock::operator= (MemoryBlock&& other) noexcept
{
data = std::move (other.data);
size = other.size;
return *this;
}
//==============================================================================
bool MemoryBlock::operator== (const MemoryBlock& other) const noexcept
{
return matches (other.data, other.size);
}
bool MemoryBlock::operator!= (const MemoryBlock& other) const noexcept
{
return ! operator== (other);
}
bool MemoryBlock::matches (const void* dataToCompare, size_t dataSize) const noexcept
{
return size == dataSize
&& memcmp (data, dataToCompare, size) == 0;
}
//==============================================================================
// this will resize the block to this size
void MemoryBlock::setSize (const size_t newSize, const bool initialiseToZero)
{
if (size != newSize)
{
if (newSize <= 0)
{
reset();
}
else
{
if (data != nullptr)
{
data.realloc (newSize);
if (initialiseToZero && (newSize > size))
zeromem (data + size, newSize - size);
}
else
{
data.allocate (newSize, initialiseToZero);
}
size = newSize;
}
}
}
void MemoryBlock::reset()
{
data.free();
size = 0;
}
void MemoryBlock::ensureSize (size_t minimumSize, bool initialiseToZero)
{
if (size < minimumSize)
setSize (minimumSize, initialiseToZero);
}
void MemoryBlock::swapWith (MemoryBlock& other) noexcept
{
std::swap (size, other.size);
data.swapWith (other.data);
}
//==============================================================================
void MemoryBlock::fillWith (uint8 value) noexcept
{
memset (data, (int) value, size);
}
void MemoryBlock::append (const void* srcData, size_t numBytes)
{
if (numBytes > 0)
{
jassert (srcData != nullptr); // this must not be null!
auto oldSize = size;
setSize (size + numBytes);
memcpy (data + oldSize, srcData, numBytes);
}
}
void MemoryBlock::replaceAll (const void* srcData, size_t numBytes)
{
if (numBytes <= 0)
{
reset();
return;
}
jassert (srcData != nullptr); // this must not be null!
setSize (numBytes);
memcpy (data, srcData, numBytes);
}
void MemoryBlock::insert (const void* srcData, size_t numBytes, size_t insertPosition)
{
if (numBytes > 0)
{
jassert (srcData != nullptr); // this must not be null!
insertPosition = jmin (size, insertPosition);
auto trailingDataSize = size - insertPosition;
setSize (size + numBytes, false);
if (trailingDataSize > 0)
memmove (data + insertPosition + numBytes,
data + insertPosition,
trailingDataSize);
memcpy (data + insertPosition, srcData, numBytes);
}
}
void MemoryBlock::removeSection (size_t startByte, size_t numBytesToRemove)
{
if (startByte + numBytesToRemove >= size)
{
setSize (startByte);
}
else if (numBytesToRemove > 0)
{
memmove (data + startByte,
data + startByte + numBytesToRemove,
size - (startByte + numBytesToRemove));
setSize (size - numBytesToRemove);
}
}
void MemoryBlock::copyFrom (const void* const src, int offset, size_t num) noexcept
{
auto* d = static_cast<const char*> (src);
if (offset < 0)
{
d -= offset;
num += (size_t) -offset;
offset = 0;
}
if ((size_t) offset + num > size)
num = size - (size_t) offset;
if (num > 0)
memcpy (data + offset, d, num);
}
void MemoryBlock::copyTo (void* const dst, int offset, size_t num) const noexcept
{
auto* d = static_cast<char*> (dst);
if (offset < 0)
{
zeromem (d, (size_t) -offset);
d -= offset;
num -= (size_t) -offset;
offset = 0;
}
if ((size_t) offset + num > size)
{
auto newNum = (size_t) size - (size_t) offset;
zeromem (d + newNum, num - newNum);
num = newNum;
}
if (num > 0)
memcpy (d, data + offset, num);
}
String MemoryBlock::toString() const
{
return String::fromUTF8 (data, (int) size);
}
//==============================================================================
int MemoryBlock::getBitRange (size_t bitRangeStart, size_t numBits) const noexcept
{
int res = 0;
auto byte = bitRangeStart >> 3;
auto offsetInByte = bitRangeStart & 7;
size_t bitsSoFar = 0;
while (numBits > 0 && (size_t) byte < size)
{
auto bitsThisTime = jmin (numBits, 8 - offsetInByte);
const int mask = (0xff >> (8 - bitsThisTime)) << offsetInByte;
res |= (((data[byte] & mask) >> offsetInByte) << bitsSoFar);
bitsSoFar += bitsThisTime;
numBits -= bitsThisTime;
++byte;
offsetInByte = 0;
}
return res;
}
void MemoryBlock::setBitRange (const size_t bitRangeStart, size_t numBits, int bitsToSet) noexcept
{
auto byte = bitRangeStart >> 3;
auto offsetInByte = bitRangeStart & 7;
uint32 mask = ~((((uint32) 0xffffffff) << (32 - numBits)) >> (32 - numBits));
while (numBits > 0 && (size_t) byte < size)
{
auto bitsThisTime = jmin (numBits, 8 - offsetInByte);
const uint32 tempMask = (mask << offsetInByte) | ~((((uint32) 0xffffffff) >> offsetInByte) << offsetInByte);
const uint32 tempBits = (uint32) bitsToSet << offsetInByte;
data[byte] = (char) (((uint32) data[byte] & tempMask) | tempBits);
++byte;
numBits -= bitsThisTime;
bitsToSet >>= bitsThisTime;
mask >>= bitsThisTime;
offsetInByte = 0;
}
}
//==============================================================================
void MemoryBlock::loadFromHexString (StringRef hex)
{
ensureSize ((size_t) hex.length() >> 1);
char* dest = data;
auto t = hex.text;
for (;;)
{
juce_wchar byte = 0;
for (int loop = 2; --loop >= 0;)
{
byte <<= 4;
for (;;)
{
auto c = t.getAndAdvance();
if (c >= '0' && c <= '9') { byte |= c - '0'; break; }
if (c >= 'a' && c <= 'z') { byte |= c - ('a' - 10); break; }
if (c >= 'A' && c <= 'Z') { byte |= c - ('A' - 10); break; }
if (c == 0)
{
setSize (static_cast<size_t> (dest - data));
return;
}
}
}
*dest++ = (char) byte;
}
}
//==============================================================================
static const char base64EncodingTable[] = ".ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+";
String MemoryBlock::toBase64Encoding() const
{
auto numChars = ((size << 3) + 5) / 6;
String destString ((unsigned int) size); // store the length, followed by a '.', and then the data.
auto initialLen = destString.length();
destString.preallocateBytes ((size_t) initialLen * sizeof (String::CharPointerType::CharType) + 2 + numChars);
auto d = destString.getCharPointer();
d += initialLen;
d.write ('.');
for (size_t i = 0; i < numChars; ++i)
d.write ((juce_wchar) (uint8) base64EncodingTable[getBitRange (i * 6, 6)]);
d.writeNull();
return destString;
}
static const char base64DecodingTable[] =
{
63, 0, 0, 0, 0, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 0, 0, 0, 0, 0, 0, 0,
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26,
0, 0, 0, 0, 0, 0, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52
};
bool MemoryBlock::fromBase64Encoding (StringRef s)
{
auto dot = CharacterFunctions::find (s.text, (juce_wchar) '.');
if (dot.isEmpty())
return false;
auto numBytesNeeded = String (s.text, dot).getIntValue();
setSize ((size_t) numBytesNeeded, true);
auto srcChars = dot + 1;
int pos = 0;
for (;;)
{
auto c = (int) srcChars.getAndAdvance();
if (c == 0)
return true;
c -= 43;
if (isPositiveAndBelow (c, numElementsInArray (base64DecodingTable)))
{
setBitRange ((size_t) pos, 6, base64DecodingTable[c]);
pos += 6;
}
}
}
} // namespace juce

View File

@ -0,0 +1,290 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
/**
A class to hold a resizable block of raw data.
@tags{Core}
*/
class JUCE_API MemoryBlock
{
public:
//==============================================================================
/** Create an uninitialised block with 0 size. */
MemoryBlock() noexcept;
/** Creates a memory block with a given initial size.
@param initialSize the size of block to create
@param initialiseToZero whether to clear the memory or just leave it uninitialised
*/
MemoryBlock (const size_t initialSize,
bool initialiseToZero = false);
/** Creates a copy of another memory block. */
MemoryBlock (const MemoryBlock&);
/** Creates a memory block using a copy of a block of data.
@param dataToInitialiseFrom some data to copy into this block
@param sizeInBytes how much space to use
*/
MemoryBlock (const void* dataToInitialiseFrom, size_t sizeInBytes);
/** Destructor. */
~MemoryBlock() noexcept;
/** Copies another memory block onto this one.
This block will be resized and copied to exactly match the other one.
*/
MemoryBlock& operator= (const MemoryBlock&);
/** Move constructor */
MemoryBlock (MemoryBlock&&) noexcept;
/** Move assignment operator */
MemoryBlock& operator= (MemoryBlock&&) noexcept;
//==============================================================================
/** Compares two memory blocks.
@returns true only if the two blocks are the same size and have identical contents.
*/
bool operator== (const MemoryBlock& other) const noexcept;
/** Compares two memory blocks.
@returns true if the two blocks are different sizes or have different contents.
*/
bool operator!= (const MemoryBlock& other) const noexcept;
/** Returns true if the data in this MemoryBlock matches the raw bytes passed-in. */
bool matches (const void* data, size_t dataSize) const noexcept;
//==============================================================================
/** Returns a void pointer to the data.
Note that the pointer returned will probably become invalid when the
block is resized.
*/
void* getData() noexcept { return data; }
/** Returns a void pointer to the data.
Note that the pointer returned will probably become invalid when the
block is resized.
*/
const void* getData() const noexcept { return data; }
/** Returns a byte from the memory block.
This returns a reference, so you can also use it to set a byte.
*/
template <typename Type>
char& operator[] (const Type offset) noexcept { return data [offset]; }
/** Returns a byte from the memory block. */
template <typename Type>
const char& operator[] (const Type offset) const noexcept { return data [offset]; }
/** Returns an iterator for the data. */
char* begin() noexcept { return data; }
/** Returns an iterator for the data. */
const char* begin() const noexcept { return data; }
/** Returns an end-iterator for the data. */
char* end() noexcept { return begin() + getSize(); }
/** Returns an end-iterator for the data. */
const char* end() const noexcept { return begin() + getSize(); }
//==============================================================================
/** Returns true if the memory block has zero size. */
bool isEmpty() const noexcept { return getSize() == 0; }
/** Returns the block's current allocated size, in bytes. */
size_t getSize() const noexcept { return size; }
/** Resizes the memory block.
Any data that is present in both the old and new sizes will be retained.
When enlarging the block, the new space that is allocated at the end can either be
cleared, or left uninitialised.
@param newSize the new desired size for the block
@param initialiseNewSpaceToZero if the block gets enlarged, this determines
whether to clear the new section or just leave it
uninitialised
@see ensureSize
*/
void setSize (const size_t newSize,
bool initialiseNewSpaceToZero = false);
/** Increases the block's size only if it's smaller than a given size.
@param minimumSize if the block is already bigger than this size, no action
will be taken; otherwise it will be increased to this size
@param initialiseNewSpaceToZero if the block gets enlarged, this determines
whether to clear the new section or just leave it
uninitialised
@see setSize
*/
void ensureSize (const size_t minimumSize,
bool initialiseNewSpaceToZero = false);
/** Frees all the blocks data, setting its size to 0. */
void reset();
//==============================================================================
/** Fills the entire memory block with a repeated byte value.
This is handy for clearing a block of memory to zero.
*/
void fillWith (uint8 valueToUse) noexcept;
/** Adds another block of data to the end of this one.
The data pointer must not be null. This block's size will be increased accordingly.
*/
void append (const void* data, size_t numBytes);
/** Resizes this block to the given size and fills its contents from the supplied buffer.
The data pointer must not be null.
*/
void replaceAll (const void* data, size_t numBytes);
/** Inserts some data into the block.
The dataToInsert pointer must not be null. This block's size will be increased accordingly.
If the insert position lies outside the valid range of the block, it will be clipped to
within the range before being used.
*/
void insert (const void* dataToInsert, size_t numBytesToInsert, size_t insertPosition);
/** Chops out a section of the block.
This will remove a section of the memory block and close the gap around it,
shifting any subsequent data downwards and reducing the size of the block.
If the range specified goes beyond the size of the block, it will be clipped.
*/
void removeSection (size_t startByte, size_t numBytesToRemove);
//==============================================================================
/** Copies data into this MemoryBlock from a memory address.
@param srcData the memory location of the data to copy into this block
@param destinationOffset the offset in this block at which the data being copied should begin
@param numBytes how much to copy in (if this goes beyond the size of the memory block,
it will be clipped so not to do anything nasty)
*/
void copyFrom (const void* srcData,
int destinationOffset,
size_t numBytes) noexcept;
/** Copies data from this MemoryBlock to a memory address.
@param destData the memory location to write to
@param sourceOffset the offset within this block from which the copied data will be read
@param numBytes how much to copy (if this extends beyond the limits of the memory block,
zeros will be used for that portion of the data)
*/
void copyTo (void* destData,
int sourceOffset,
size_t numBytes) const noexcept;
//==============================================================================
/** Exchanges the contents of this and another memory block.
No actual copying is required for this, so it's very fast.
*/
void swapWith (MemoryBlock& other) noexcept;
//==============================================================================
/** Attempts to parse the contents of the block as a zero-terminated UTF8 string. */
String toString() const;
//==============================================================================
/** Parses a string of hexadecimal numbers and writes this data into the memory block.
The block will be resized to the number of valid bytes read from the string.
Non-hex characters in the string will be ignored.
@see String::toHexString()
*/
void loadFromHexString (StringRef sourceHexString);
//==============================================================================
/** Sets a number of bits in the memory block, treating it as a long binary sequence. */
void setBitRange (size_t bitRangeStart,
size_t numBits,
int binaryNumberToApply) noexcept;
/** Reads a number of bits from the memory block, treating it as one long binary sequence */
int getBitRange (size_t bitRangeStart,
size_t numBitsToRead) const noexcept;
//==============================================================================
/** Returns a string of characters in a JUCE-specific text encoding that represents the
binary contents of this block.
This uses a JUCE-specific (i.e. not standard!) 64-bit encoding system to convert binary
data into a string of ASCII characters for purposes like storage in XML.
Note that this proprietary format is mainly kept here for backwards-compatibility, and
you may prefer to use the Base64::toBase64() method if you want to use the standard
base-64 encoding.
@see fromBase64Encoding, Base64::toBase64, Base64::convertToBase64
*/
String toBase64Encoding() const;
/** Takes a string created by MemoryBlock::toBase64Encoding() and extracts the original data.
The string passed in must have been created by to64BitEncoding(), and this
block will be resized to recreate the original data block.
Note that these methods use a JUCE-specific (i.e. not standard!) 64-bit encoding system.
You may prefer to use the Base64::convertFromBase64() method if you want to use the
standard base-64 encoding.
@see toBase64Encoding, Base64::convertFromBase64
*/
bool fromBase64Encoding (StringRef encodedString);
//==============================================================================
#ifndef DOXYGEN
[[deprecated ("Use the replaceAll method instead, which will also replace the data when numBytes == 0.")]]
void replaceWith (const void* srcData, size_t numBytes)
{
if (numBytes > 0)
replaceAll (srcData, numBytes);
}
#endif
private:
//==============================================================================
using HeapBlockType = HeapBlock<char, true>;
HeapBlockType data;
size_t size = 0;
JUCE_LEAK_DETECTOR (MemoryBlock)
};
} // namespace juce

View File

@ -0,0 +1,191 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
/**
Holds a pointer to an object which can optionally be deleted when this pointer
goes out of scope.
This acts in many ways like a std::unique_ptr, but allows you to specify whether or
not the object is deleted.
@tags{Core}
*/
template <class ObjectType>
class OptionalScopedPointer
{
public:
//==============================================================================
/** Creates an empty OptionalScopedPointer. */
OptionalScopedPointer() = default;
/** Creates an OptionalScopedPointer to point to a given object, and specifying whether
the OptionalScopedPointer will delete it.
If takeOwnership is true, then the OptionalScopedPointer will act like a std::unique_ptr,
deleting the object when it is itself deleted. If this parameter is false, then the
OptionalScopedPointer just holds a normal pointer to the object, and won't delete it.
*/
OptionalScopedPointer (ObjectType* objectToHold, bool takeOwnership)
: object (objectToHold),
shouldDelete (takeOwnership)
{
}
/** Takes ownership of the object that another OptionalScopedPointer holds.
Like a normal std::unique_ptr, the objectToTransferFrom object will become null,
as ownership of the managed object is transferred to this object.
The flag to indicate whether or not to delete the managed object is also
copied from the source object.
*/
OptionalScopedPointer (OptionalScopedPointer&& other) noexcept
: object (std::move (other.object)),
shouldDelete (std::move (other.shouldDelete))
{
}
/** Takes ownership of the object owned by `ptr`. */
explicit OptionalScopedPointer (std::unique_ptr<ObjectType>&& ptr) noexcept
: OptionalScopedPointer (ptr.release(), true)
{
}
/** Points to the same object as `ref`, but does not take ownership. */
explicit OptionalScopedPointer (ObjectType& ref) noexcept
: OptionalScopedPointer (std::addressof (ref), false)
{
}
/** Takes ownership of the object that another OptionalScopedPointer holds.
Like a normal std::unique_ptr, the objectToTransferFrom object will become null,
as ownership of the managed object is transferred to this object.
The ownership flag that says whether or not to delete the managed object is also
copied from the source object.
*/
OptionalScopedPointer& operator= (OptionalScopedPointer&& other) noexcept
{
swapWith (other);
other.reset();
return *this;
}
/** The destructor may or may not delete the object that is being held, depending on the
takeOwnership flag that was specified when the object was first passed into an
OptionalScopedPointer constructor.
*/
~OptionalScopedPointer() noexcept
{
reset();
}
//==============================================================================
/** Returns the object that this pointer is managing. */
operator ObjectType*() const noexcept { return object.get(); }
/** Returns the object that this pointer is managing. */
ObjectType* get() const noexcept { return object.get(); }
/** Returns the object that this pointer is managing. */
ObjectType& operator*() const noexcept { return *object; }
/** Lets you access methods and properties of the object that this pointer is holding. */
ObjectType* operator->() const noexcept { return object.get(); }
//==============================================================================
/** Removes the current object from this OptionalScopedPointer without deleting it.
This will return the current object, and set this OptionalScopedPointer to a null pointer.
*/
ObjectType* release() noexcept { return object.release(); }
/** Resets this pointer to null, possibly deleting the object that it holds, if it has
ownership of it.
*/
void reset() noexcept
{
if (! shouldDelete)
object.release();
else
object.reset();
}
/** Does the same thing as reset(). */
void clear() { reset(); }
/** Makes this OptionalScopedPointer point at a new object, specifying whether the
OptionalScopedPointer will take ownership of the object.
If takeOwnership is true, then the OptionalScopedPointer will act like a std::unique_ptr,
deleting the object when it is itself deleted. If this parameter is false, then the
OptionalScopedPointer just holds a normal pointer to the object, and won't delete it.
*/
void set (ObjectType* newObject, bool takeOwnership)
{
if (object.get() != newObject)
{
reset();
object.reset (newObject);
}
shouldDelete = takeOwnership;
}
/** Makes this OptionalScopedPointer point at a new object, and take ownership of that object. */
void setOwned (ObjectType* newObject)
{
set (newObject, true);
}
/** Makes this OptionalScopedPointer point at a new object, but will not take ownership of that object. */
void setNonOwned (ObjectType* newObject)
{
set (newObject, false);
}
/** Returns true if the target object will be deleted when this pointer
object is deleted.
*/
bool willDeleteObject() const noexcept { return shouldDelete; }
//==============================================================================
/** Swaps this object with another OptionalScopedPointer.
The two objects simply exchange their states.
*/
void swapWith (OptionalScopedPointer<ObjectType>& other) noexcept
{
std::swap (other.object, object);
std::swap (other.shouldDelete, shouldDelete);
}
private:
//==============================================================================
std::unique_ptr<ObjectType> object;
bool shouldDelete = false;
};
} // namespace juce

View File

@ -0,0 +1,469 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
/**
A base class which provides methods for reference-counting.
To add reference-counting to a class, derive it from this class, and
use the ReferenceCountedObjectPtr class to point to it.
e.g. @code
class MyClass : public ReferenceCountedObject
{
void foo();
// This is a neat way of declaring a typedef for a pointer class,
// rather than typing out the full templated name each time..
using Ptr = ReferenceCountedObjectPtr<MyClass>;
};
MyClass::Ptr p = new MyClass();
MyClass::Ptr p2 = p;
p = nullptr;
p2->foo();
@endcode
Once a new ReferenceCountedObject has been assigned to a pointer, be
careful not to delete the object manually.
This class uses an Atomic<int> value to hold the reference count, so
the reference count can be updated on multiple threads. Note that
whilst it's thread-safe to create and delete a ReferenceCountedObjectPtr
to a ReferenceCountedObject shared between threads, it's not thread-safe
to modify or swap the ReferenceCountedObject.
For a faster but non-thread-safe version, use SingleThreadedReferenceCountedObject
instead.
@see ReferenceCountedObjectPtr, ReferenceCountedArray, SingleThreadedReferenceCountedObject
@tags{Core}
*/
class JUCE_API ReferenceCountedObject
{
public:
//==============================================================================
/** Increments the object's reference count.
This is done automatically by the smart pointer, but is public just
in case it's needed for nefarious purposes.
*/
void incReferenceCount() noexcept
{
++refCount;
}
/** Decreases the object's reference count.
If the count gets to zero, the object will be deleted.
*/
void decReferenceCount() noexcept
{
jassert (getReferenceCount() > 0);
if (--refCount == 0)
delete this;
}
/** Decreases the object's reference count.
If the count gets to zero, the object will not be deleted, but this method
will return true, allowing the caller to take care of deletion.
*/
bool decReferenceCountWithoutDeleting() noexcept
{
jassert (getReferenceCount() > 0);
return --refCount == 0;
}
/** Returns the object's current reference count. */
int getReferenceCount() const noexcept { return refCount.get(); }
protected:
//==============================================================================
/** Creates the reference-counted object (with an initial ref count of zero). */
ReferenceCountedObject() = default;
/** Copying from another object does not affect this one's reference-count. */
ReferenceCountedObject (const ReferenceCountedObject&) noexcept {}
/** Copying from another object does not affect this one's reference-count. */
ReferenceCountedObject (ReferenceCountedObject&&) noexcept {}
/** Copying from another object does not affect this one's reference-count. */
ReferenceCountedObject& operator= (const ReferenceCountedObject&) noexcept { return *this; }
/** Copying from another object does not affect this one's reference-count. */
ReferenceCountedObject& operator= (ReferenceCountedObject&&) noexcept { return *this; }
/** Destructor. */
virtual ~ReferenceCountedObject()
{
// it's dangerous to delete an object that's still referenced by something else!
jassert (getReferenceCount() == 0);
}
/** Resets the reference count to zero without deleting the object.
You should probably never need to use this!
*/
void resetReferenceCount() noexcept
{
refCount = 0;
}
private:
//==============================================================================
Atomic<int> refCount { 0 };
friend struct ContainerDeletePolicy<ReferenceCountedObject>;
};
//==============================================================================
/**
Adds reference-counting to an object.
This is effectively a version of the ReferenceCountedObject class, but which
uses a non-atomic counter, and so is not thread-safe (but which will be more
efficient).
For more details on how to use it, see the ReferenceCountedObject class notes.
@see ReferenceCountedObject, ReferenceCountedObjectPtr, ReferenceCountedArray
@tags{Core}
*/
class JUCE_API SingleThreadedReferenceCountedObject
{
public:
//==============================================================================
/** Increments the object's reference count.
This is done automatically by the smart pointer, but is public just
in case it's needed for nefarious purposes.
*/
void incReferenceCount() noexcept
{
++refCount;
}
/** Decreases the object's reference count.
If the count gets to zero, the object will be deleted.
*/
void decReferenceCount() noexcept
{
jassert (getReferenceCount() > 0);
if (--refCount == 0)
delete this;
}
/** Decreases the object's reference count.
If the count gets to zero, the object will not be deleted, but this method
will return true, allowing the caller to take care of deletion.
*/
bool decReferenceCountWithoutDeleting() noexcept
{
jassert (getReferenceCount() > 0);
return --refCount == 0;
}
/** Returns the object's current reference count. */
int getReferenceCount() const noexcept { return refCount; }
protected:
//==============================================================================
/** Creates the reference-counted object (with an initial ref count of zero). */
SingleThreadedReferenceCountedObject() = default;
/** Copying from another object does not affect this one's reference-count. */
SingleThreadedReferenceCountedObject (const SingleThreadedReferenceCountedObject&) {}
/** Copying from another object does not affect this one's reference-count. */
SingleThreadedReferenceCountedObject (SingleThreadedReferenceCountedObject&&) {}
/** Copying from another object does not affect this one's reference-count. */
SingleThreadedReferenceCountedObject& operator= (const SingleThreadedReferenceCountedObject&) { return *this; }
/** Copying from another object does not affect this one's reference-count. */
SingleThreadedReferenceCountedObject& operator= (SingleThreadedReferenceCountedObject&&) { return *this; }
/** Destructor. */
virtual ~SingleThreadedReferenceCountedObject()
{
// it's dangerous to delete an object that's still referenced by something else!
jassert (getReferenceCount() == 0);
}
private:
//==============================================================================
int refCount = 0;
friend struct ContainerDeletePolicy<ReferenceCountedObject>;
};
//==============================================================================
/**
A smart-pointer class which points to a reference-counted object.
The template parameter specifies the class of the object you want to point to - the easiest
way to make a class reference-countable is to simply make it inherit from ReferenceCountedObject
or SingleThreadedReferenceCountedObject, but if you need to, you can roll your own reference-countable
class by implementing a set of methods called incReferenceCount(), decReferenceCount(), and
decReferenceCountWithoutDeleting(). See ReferenceCountedObject for examples of how these methods
should behave.
When using this class, you'll probably want to create a typedef to abbreviate the full
templated name - e.g.
@code
struct MyClass : public ReferenceCountedObject
{
using Ptr = ReferenceCountedObjectPtr<MyClass>;
...
}
@endcode
@see ReferenceCountedObject, ReferenceCountedObjectArray
@tags{Core}
*/
template <class ObjectType>
class ReferenceCountedObjectPtr
{
public:
/** The class being referenced by this pointer. */
using ReferencedType = ObjectType;
//==============================================================================
/** Creates a pointer to a null object. */
ReferenceCountedObjectPtr() = default;
/** Creates a pointer to a null object. */
ReferenceCountedObjectPtr (decltype (nullptr)) noexcept {}
/** Creates a pointer to an object.
This will increment the object's reference-count.
*/
ReferenceCountedObjectPtr (ReferencedType* refCountedObject) noexcept
: referencedObject (refCountedObject)
{
incIfNotNull (refCountedObject);
}
/** Creates a pointer to an object.
This will increment the object's reference-count.
*/
ReferenceCountedObjectPtr (ReferencedType& refCountedObject) noexcept
: referencedObject (&refCountedObject)
{
refCountedObject.incReferenceCount();
}
/** Copies another pointer.
This will increment the object's reference-count.
*/
ReferenceCountedObjectPtr (const ReferenceCountedObjectPtr& other) noexcept
: referencedObject (other.referencedObject)
{
incIfNotNull (referencedObject);
}
/** Takes-over the object from another pointer. */
ReferenceCountedObjectPtr (ReferenceCountedObjectPtr&& other) noexcept
: referencedObject (other.referencedObject)
{
other.referencedObject = nullptr;
}
/** Copies another pointer.
This will increment the object's reference-count (if it is non-null).
*/
template <typename Convertible>
ReferenceCountedObjectPtr (const ReferenceCountedObjectPtr<Convertible>& other) noexcept
: referencedObject (other.get())
{
incIfNotNull (referencedObject);
}
/** Changes this pointer to point at a different object.
The reference count of the old object is decremented, and it might be
deleted if it hits zero. The new object's count is incremented.
*/
ReferenceCountedObjectPtr& operator= (const ReferenceCountedObjectPtr& other)
{
return operator= (other.referencedObject);
}
/** Changes this pointer to point at a different object.
The reference count of the old object is decremented, and it might be
deleted if it hits zero. The new object's count is incremented.
*/
template <typename Convertible>
ReferenceCountedObjectPtr& operator= (const ReferenceCountedObjectPtr<Convertible>& other)
{
return operator= (other.get());
}
/** Changes this pointer to point at a different object.
The reference count of the old object is decremented, and it might be
deleted if it hits zero. The new object's count is incremented.
*/
ReferenceCountedObjectPtr& operator= (ReferencedType* newObject)
{
if (newObject != nullptr)
return operator= (*newObject);
reset();
return *this;
}
/** Changes this pointer to point at a different object.
The reference count of the old object is decremented, and it might be
deleted if it hits zero. The new object's count is incremented.
*/
ReferenceCountedObjectPtr& operator= (ReferencedType& newObject)
{
if (referencedObject != &newObject)
{
newObject.incReferenceCount();
auto* oldObject = referencedObject;
referencedObject = &newObject;
decIfNotNull (oldObject);
}
return *this;
}
/** Resets this pointer to a null pointer. */
ReferenceCountedObjectPtr& operator= (decltype (nullptr))
{
reset();
return *this;
}
/** Takes-over the object from another pointer. */
ReferenceCountedObjectPtr& operator= (ReferenceCountedObjectPtr&& other) noexcept
{
std::swap (referencedObject, other.referencedObject);
return *this;
}
/** Destructor.
This will decrement the object's reference-count, which will cause the
object to be deleted when the ref-count hits zero.
*/
~ReferenceCountedObjectPtr()
{
decIfNotNull (referencedObject);
}
//==============================================================================
/** Returns the object that this pointer references.
The pointer returned may be null, of course.
*/
ReferencedType* get() const noexcept { return referencedObject; }
/** Resets this object to a null pointer. */
void reset() noexcept
{
auto oldObject = referencedObject; // need to null the pointer before deleting the object
referencedObject = nullptr; // in case this ptr is itself deleted as a side-effect
decIfNotNull (oldObject); // of the destructor
}
// the -> operator is called on the referenced object
ReferencedType* operator->() const noexcept
{
jassert (referencedObject != nullptr); // null pointer method call!
return referencedObject;
}
/** Dereferences the object that this pointer references.
The pointer returned may be null, of course.
*/
ReferencedType& operator*() const noexcept { jassert (referencedObject != nullptr); return *referencedObject; }
/** Checks whether this pointer is null */
bool operator== (decltype (nullptr)) const noexcept { return referencedObject == nullptr; }
/** Checks whether this pointer is null */
bool operator!= (decltype (nullptr)) const noexcept { return referencedObject != nullptr; }
/** Compares two ReferenceCountedObjectPtrs. */
bool operator== (const ObjectType* other) const noexcept { return referencedObject == other; }
/** Compares two ReferenceCountedObjectPtrs. */
bool operator== (const ReferenceCountedObjectPtr& other) const noexcept { return referencedObject == other.get(); }
/** Compares two ReferenceCountedObjectPtrs. */
bool operator!= (const ObjectType* other) const noexcept { return referencedObject != other; }
/** Compares two ReferenceCountedObjectPtrs. */
bool operator!= (const ReferenceCountedObjectPtr& other) const noexcept { return referencedObject != other.get(); }
#if JUCE_STRICT_REFCOUNTEDPOINTER
/** Checks whether this pointer is null */
explicit operator bool() const noexcept { return referencedObject != nullptr; }
#else
/** Returns the object that this pointer references.
The pointer returned may be null, of course.
Note that this methods allows the compiler to be very lenient with what it allows you to do
with the pointer, it's safer to disable this by setting JUCE_STRICT_REFCOUNTEDPOINTER=1, which
increased type safety and can prevent some common slip-ups.
*/
operator ReferencedType*() const noexcept { return referencedObject; }
#endif
#ifndef DOXYGEN
[[deprecated ("Use the get method instead.")]]
ReferencedType* getObject() const { return get(); }
#endif
private:
//==============================================================================
ReferencedType* referencedObject = nullptr;
static void incIfNotNull (ReferencedType* o) noexcept
{
if (o != nullptr)
o->incReferenceCount();
}
static void decIfNotNull (ReferencedType* o) noexcept
{
if (o != nullptr && o->decReferenceCountWithoutDeleting())
ContainerDeletePolicy<ReferencedType>::destroy (o);
}
};
//==============================================================================
/** Compares two ReferenceCountedObjectPtrs. */
template <typename Type>
bool operator== (const Type* object1, const ReferenceCountedObjectPtr<Type>& object2) noexcept
{
return object1 == object2.get();
}
/** Compares two ReferenceCountedObjectPtrs. */
template <typename Type>
bool operator!= (const Type* object1, const ReferenceCountedObjectPtr<Type>& object2) noexcept
{
return object1 != object2.get();
}
} // namespace juce

View File

@ -0,0 +1,100 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
End User License Agreement: www.juce.com/juce-6-licence
Privacy Policy: www.juce.com/juce-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
/**
Helper functions for managing buffered readers.
*/
struct Reservoir
{
/** Attempts to read the requested range from some kind of input stream,
with intermediate buffering in a 'reservoir'.
While there are still samples in the requested range left to read, this
function will check whether the next part of the requested range is
already loaded into the reservoir. If the range is available, then
doBufferedRead will call readFromReservoir with the range that should
be copied to the output buffer. If the range is not available,
doBufferedRead will call fillReservoir to request that a new region is
loaded into the reservoir. It will repeat these steps until either the
entire requested region has been read, or the stream ends.
This will return the range that could not be read successfully, if any.
An empty range implies that the entire read was successful.
Note that all ranges, including those provided as arguments to the
callbacks, are relative to the original unbuffered input. That is, if
getBufferedRange returns the range [200, 300), then readFromReservoir
might be passed the range [250, 300) in order to copy the final 50
samples from the reservoir.
@param rangeToRead the absolute position of the range that should
be read
@param getBufferedRange a function void -> Range<Index> that returns
the region currently held in the reservoir
@param readFromReservoir a function Range<Index> -> void that can be
used to copy samples from the region in the
reservoir specified in the input range
@param fillReservoir a function Index -> void that is given a
requested read location, and that should
attempt to fill the reservoir starting at this
location. After this function,
getBufferedRange should return the new region
contained in the managed buffer
*/
template <typename Index, typename GetBufferedRange, typename ReadFromReservoir, typename FillReservoir>
static Range<Index> doBufferedRead (Range<Index> rangeToRead,
GetBufferedRange&& getBufferedRange,
ReadFromReservoir&& readFromReservoir,
FillReservoir&& fillReservoir)
{
while (! rangeToRead.isEmpty())
{
const auto rangeToReadInBuffer = rangeToRead.getIntersectionWith (getBufferedRange());
if (rangeToReadInBuffer.isEmpty())
{
fillReservoir (rangeToRead.getStart());
const auto newRange = getBufferedRange();
if (newRange.isEmpty() || ! newRange.contains (rangeToRead.getStart()))
break;
}
else
{
readFromReservoir (rangeToReadInBuffer);
rangeToRead.setStart (rangeToReadInBuffer.getEnd());
}
}
return rangeToRead;
}
};
} // namespace juce

View File

@ -0,0 +1,224 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
#ifndef DOXYGEN
namespace juce
{
//==============================================================================
/**
This class is deprecated. You should use std::unique_ptr instead.
*/
template <class ObjectType>
class [[deprecated]] ScopedPointer
{
public:
//==============================================================================
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations")
JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4996)
inline ScopedPointer() {}
inline ScopedPointer (decltype (nullptr)) noexcept {}
inline ScopedPointer (ObjectType* objectToTakePossessionOf) noexcept
: object (objectToTakePossessionOf)
{
}
ScopedPointer (ScopedPointer& objectToTransferFrom) noexcept
: object (objectToTransferFrom.release())
{
}
inline ~ScopedPointer() { reset(); }
ScopedPointer& operator= (ScopedPointer& objectToTransferFrom)
{
if (this != objectToTransferFrom.getAddress())
{
// Two ScopedPointers should never be able to refer to the same object - if
// this happens, you must have done something dodgy!
jassert (object == nullptr || object != objectToTransferFrom.object);
reset (objectToTransferFrom.release());
}
return *this;
}
ScopedPointer& operator= (ObjectType* newObjectToTakePossessionOf)
{
reset (newObjectToTakePossessionOf);
return *this;
}
ScopedPointer (ScopedPointer&& other) noexcept : object (other.object)
{
other.object = nullptr;
}
ScopedPointer& operator= (ScopedPointer&& other) noexcept
{
reset (other.release());
return *this;
}
//==============================================================================
inline operator ObjectType*() const noexcept { return object; }
inline ObjectType* get() const noexcept { return object; }
inline ObjectType& operator*() const noexcept { return *object; }
inline ObjectType* operator->() const noexcept { return object; }
void reset()
{
auto* oldObject = object;
object = {};
ContainerDeletePolicy<ObjectType>::destroy (oldObject);
}
void reset (ObjectType* newObject)
{
if (object != newObject)
{
auto* oldObject = object;
object = newObject;
ContainerDeletePolicy<ObjectType>::destroy (oldObject);
}
else
{
// You're trying to reset this ScopedPointer to itself! This will work here as ScopedPointer does an equality check
// but be aware that std::unique_ptr won't do this and you could end up with some nasty, subtle bugs!
jassert (newObject == nullptr);
}
}
void reset (ScopedPointer& newObject)
{
reset (newObject.release());
}
ObjectType* release() noexcept { auto* o = object; object = {}; return o; }
//==============================================================================
void swapWith (ScopedPointer<ObjectType>& other) noexcept
{
// Two ScopedPointers should never be able to refer to the same object - if
// this happens, you must have done something dodgy!
jassert (object != other.object || this == other.getAddress() || object == nullptr);
std::swap (object, other.object);
}
inline ObjectType* createCopy() const { return createCopyIfNotNull (object); }
private:
//==============================================================================
ObjectType* object = nullptr;
const ScopedPointer* getAddress() const noexcept { return this; } // Used internally to avoid the & operator
#if ! JUCE_MSVC // (MSVC can't deal with multiple copy constructors)
ScopedPointer (const ScopedPointer&) = delete;
ScopedPointer& operator= (const ScopedPointer&) = delete;
#endif
JUCE_END_IGNORE_WARNINGS_MSVC
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
};
//==============================================================================
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations")
JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4996)
template <typename ObjectType1, typename ObjectType2>
bool operator== (ObjectType1* pointer1, const ScopedPointer<ObjectType2>& pointer2) noexcept
{
return pointer1 == pointer2.get();
}
template <typename ObjectType1, typename ObjectType2>
bool operator!= (ObjectType1* pointer1, const ScopedPointer<ObjectType2>& pointer2) noexcept
{
return pointer1 != pointer2.get();
}
template <typename ObjectType1, typename ObjectType2>
bool operator== (const ScopedPointer<ObjectType1>& pointer1, ObjectType2* pointer2) noexcept
{
return pointer1.get() == pointer2;
}
template <typename ObjectType1, typename ObjectType2>
bool operator!= (const ScopedPointer<ObjectType1>& pointer1, ObjectType2* pointer2) noexcept
{
return pointer1.get() != pointer2;
}
template <typename ObjectType1, typename ObjectType2>
bool operator== (const ScopedPointer<ObjectType1>& pointer1, const ScopedPointer<ObjectType2>& pointer2) noexcept
{
return pointer1.get() == pointer2.get();
}
template <typename ObjectType1, typename ObjectType2>
bool operator!= (const ScopedPointer<ObjectType1>& pointer1, const ScopedPointer<ObjectType2>& pointer2) noexcept
{
return pointer1.get() != pointer2.get();
}
template <class ObjectType>
bool operator== (decltype (nullptr), const ScopedPointer<ObjectType>& pointer) noexcept
{
return pointer.get() == nullptr;
}
template <class ObjectType>
bool operator!= (decltype (nullptr), const ScopedPointer<ObjectType>& pointer) noexcept
{
return pointer.get() != nullptr;
}
template <class ObjectType>
bool operator== (const ScopedPointer<ObjectType>& pointer, decltype (nullptr)) noexcept
{
return pointer.get() == nullptr;
}
template <class ObjectType>
bool operator!= (const ScopedPointer<ObjectType>& pointer, decltype (nullptr)) noexcept
{
return pointer.get() != nullptr;
}
//==============================================================================
// NB: This is just here to prevent any silly attempts to call deleteAndZero() on a ScopedPointer.
template <typename Type>
void deleteAndZero (ScopedPointer<Type>&) { static_assert (sizeof (Type) == 12345,
"Attempt to call deleteAndZero() on a ScopedPointer"); }
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
JUCE_END_IGNORE_WARNINGS_MSVC
} // namespace juce
#endif

View File

@ -0,0 +1,162 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
/**
A smart-pointer that automatically creates and manages the lifetime of a
shared static instance of a class.
The SharedObjectType template type indicates the class to use for the shared
object - the only requirements on this class are that it must have a public
default constructor and destructor.
The SharedResourcePointer offers a pattern that differs from using a singleton or
static instance of an object, because it uses reference-counting to make sure that
the underlying shared object is automatically created/destroyed according to the
number of SharedResourcePointer objects that exist. When the last one is deleted,
the underlying object is also immediately destroyed. This allows you to use scoping
to manage the lifetime of a shared resource.
Note: The construction/deletion of the shared object must not involve any
code that makes recursive calls to a SharedResourcePointer, or you'll cause
a deadlock.
Example:
@code
// An example of a class that contains the shared data you want to use.
struct MySharedData
{
// There's no need to ever create an instance of this class directly yourself,
// but it does need a public constructor that does the initialisation.
MySharedData()
{
sharedStuff = generateHeavyweightStuff();
}
Array<SomeKindOfData> sharedStuff;
};
struct DataUserClass
{
DataUserClass()
{
// Multiple instances of the DataUserClass will all have the same
// shared common instance of MySharedData referenced by their sharedData
// member variables.
useSharedStuff (sharedData->sharedStuff);
}
// By keeping this pointer as a member variable, the shared resource
// is guaranteed to be available for as long as the DataUserClass object.
SharedResourcePointer<MySharedData> sharedData;
};
@endcode
@tags{Core}
*/
template <typename SharedObjectType>
class SharedResourcePointer
{
public:
/** Creates an instance of the shared object.
If other SharedResourcePointer objects for this type already exist, then
this one will simply point to the same shared object that they are already
using. Otherwise, if this is the first SharedResourcePointer to be created,
then a shared object will be created automatically.
*/
SharedResourcePointer()
{
initialise();
}
SharedResourcePointer (const SharedResourcePointer&)
{
initialise();
}
/** Destructor.
If no other SharedResourcePointer objects exist, this will also delete
the shared object to which it refers.
*/
~SharedResourcePointer()
{
auto& holder = getSharedObjectHolder();
const SpinLock::ScopedLockType sl (holder.lock);
if (--(holder.refCount) == 0)
holder.sharedInstance = nullptr;
}
/** Returns the shared object. */
operator SharedObjectType*() const noexcept { return sharedObject; }
/** Returns the shared object. */
SharedObjectType& get() const noexcept { return *sharedObject; }
/** Returns the object that this pointer references. */
SharedObjectType& getObject() const noexcept { return *sharedObject; }
/** Returns the shared object pointer. */
SharedObjectType* operator->() const noexcept { return sharedObject; }
/** Returns the number of SharedResourcePointers that are currently holding the shared object. */
int getReferenceCount() const noexcept { return getSharedObjectHolder().refCount; }
private:
struct SharedObjectHolder
{
SpinLock lock;
std::unique_ptr<SharedObjectType> sharedInstance;
int refCount;
};
static SharedObjectHolder& getSharedObjectHolder() noexcept
{
static void* holder [(sizeof (SharedObjectHolder) + sizeof(void*) - 1) / sizeof(void*)] = { nullptr };
return *reinterpret_cast<SharedObjectHolder*> (holder);
}
SharedObjectType* sharedObject;
void initialise()
{
auto& holder = getSharedObjectHolder();
const SpinLock::ScopedLockType sl (holder.lock);
if (++(holder.refCount) == 1)
holder.sharedInstance.reset (new SharedObjectType());
sharedObject = holder.sharedInstance.get();
}
// There's no need to assign to a SharedResourcePointer because every
// instance of the class is exactly the same!
SharedResourcePointer& operator= (const SharedResourcePointer&) = delete;
JUCE_LEAK_DETECTOR (SharedResourcePointer)
};
} // namespace juce

View File

@ -0,0 +1,280 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
/**
Used by the JUCE_DECLARE_SINGLETON macros to manage a static pointer
to a singleton instance.
You generally won't use this directly, but see the macros JUCE_DECLARE_SINGLETON,
JUCE_DECLARE_SINGLETON_SINGLETHREADED, JUCE_DECLARE_SINGLETON_SINGLETHREADED_MINIMAL,
and JUCE_IMPLEMENT_SINGLETON for how it is intended to be used.
@tags{Core}
*/
template <typename Type, typename MutexType, bool onlyCreateOncePerRun>
struct SingletonHolder : private MutexType // (inherited so we can use the empty-base-class optimisation)
{
SingletonHolder() = default;
~SingletonHolder()
{
/* The static singleton holder is being deleted before the object that it holds
has been deleted. This could mean that you've forgotten to call clearSingletonInstance()
in the class's destructor, or have failed to delete it before your app shuts down.
If you're having trouble cleaning up your singletons, perhaps consider using the
SharedResourcePointer class instead.
*/
jassert (instance == nullptr);
}
/** Returns the current instance, or creates a new instance if there isn't one. */
Type* get()
{
if (instance == nullptr)
{
typename MutexType::ScopedLockType sl (*this);
if (instance == nullptr)
{
auto once = onlyCreateOncePerRun; // (local copy avoids VS compiler warning about this being constant)
if (once)
{
static bool createdOnceAlready = false;
if (createdOnceAlready)
{
// This means that the doNotRecreateAfterDeletion flag was set
// and you tried to create the singleton more than once.
jassertfalse;
return nullptr;
}
createdOnceAlready = true;
}
static bool alreadyInside = false;
if (alreadyInside)
{
// This means that your object's constructor has done something which has
// ended up causing a recursive loop of singleton creation..
jassertfalse;
}
else
{
alreadyInside = true;
getWithoutChecking();
alreadyInside = false;
}
}
}
return instance;
}
/** Returns the current instance, or creates a new instance if there isn't one, but doesn't do
any locking, or checking for recursion or error conditions.
*/
Type* getWithoutChecking()
{
if (instance == nullptr)
{
auto newObject = new Type(); // (create into a local so that instance is still null during construction)
instance = newObject;
}
return instance;
}
/** Deletes and resets the current instance, if there is one. */
void deleteInstance()
{
typename MutexType::ScopedLockType sl (*this);
auto old = instance;
instance = nullptr;
delete old;
}
/** Called by the class's destructor to clear the pointer if it is currently set to the given object. */
void clear (Type* expectedObject) noexcept
{
if (instance == expectedObject)
instance = nullptr;
}
Type* instance = nullptr;
};
//==============================================================================
/**
Macro to generate the appropriate methods and boilerplate for a singleton class.
To use this, add the line JUCE_DECLARE_SINGLETON(MyClass, doNotRecreateAfterDeletion)
to the class's definition.
Then put a macro JUCE_IMPLEMENT_SINGLETON(MyClass) along with the class's
implementation code.
It's also a very good idea to also add the call clearSingletonInstance() in your class's
destructor, in case it is deleted by other means than deleteInstance()
Clients can then call the static method MyClass::getInstance() to get a pointer
to the singleton, or MyClass::getInstanceWithoutCreating() which will return nullptr if
no instance currently exists.
e.g. @code
struct MySingleton
{
MySingleton() {}
~MySingleton()
{
// this ensures that no dangling pointers are left when the
// singleton is deleted.
clearSingletonInstance();
}
JUCE_DECLARE_SINGLETON (MySingleton, false)
};
// ..and this goes in a suitable .cpp file:
JUCE_IMPLEMENT_SINGLETON (MySingleton)
// example of usage:
auto* m = MySingleton::getInstance(); // creates the singleton if there isn't already one.
...
MySingleton::deleteInstance(); // safely deletes the singleton (if it's been created).
@endcode
If doNotRecreateAfterDeletion = true, it won't allow the object to be created more
than once during the process's lifetime - i.e. after you've created and deleted the
object, getInstance() will refuse to create another one. This can be useful to stop
objects being accidentally re-created during your app's shutdown code.
If you know that your object will only be created and deleted by a single thread, you
can use the slightly more efficient JUCE_DECLARE_SINGLETON_SINGLETHREADED macro instead
of this one.
@see JUCE_IMPLEMENT_SINGLETON, JUCE_DECLARE_SINGLETON_SINGLETHREADED
*/
#define JUCE_DECLARE_SINGLETON(Classname, doNotRecreateAfterDeletion) \
\
static juce::SingletonHolder<Classname, juce::CriticalSection, doNotRecreateAfterDeletion> singletonHolder; \
friend decltype (singletonHolder); \
\
static Classname* JUCE_CALLTYPE getInstance() { return singletonHolder.get(); } \
static Classname* JUCE_CALLTYPE getInstanceWithoutCreating() noexcept { return singletonHolder.instance; } \
static void JUCE_CALLTYPE deleteInstance() noexcept { singletonHolder.deleteInstance(); } \
void clearSingletonInstance() noexcept { singletonHolder.clear (this); }
//==============================================================================
/** This is a counterpart to the JUCE_DECLARE_SINGLETON macros.
After adding the JUCE_DECLARE_SINGLETON to the class definition, this macro has
to be used in the cpp file.
*/
#define JUCE_IMPLEMENT_SINGLETON(Classname) \
\
decltype (Classname::singletonHolder) Classname::singletonHolder;
//==============================================================================
/**
Macro to declare member variables and methods for a singleton class.
This is exactly the same as JUCE_DECLARE_SINGLETON, but doesn't use a critical
section to make access to it thread-safe. If you know that your object will
only ever be created or deleted by a single thread, then this is a
more efficient version to use.
If doNotRecreateAfterDeletion = true, it won't allow the object to be created more
than once during the process's lifetime - i.e. after you've created and deleted the
object, getInstance() will refuse to create another one. This can be useful to stop
objects being accidentally re-created during your app's shutdown code.
See the documentation for JUCE_DECLARE_SINGLETON for more information about
how to use it. Just like JUCE_DECLARE_SINGLETON you need to also have a
corresponding JUCE_IMPLEMENT_SINGLETON statement somewhere in your code.
@see JUCE_IMPLEMENT_SINGLETON, JUCE_DECLARE_SINGLETON, JUCE_DECLARE_SINGLETON_SINGLETHREADED_MINIMAL
*/
#define JUCE_DECLARE_SINGLETON_SINGLETHREADED(Classname, doNotRecreateAfterDeletion) \
\
static juce::SingletonHolder<Classname, juce::DummyCriticalSection, doNotRecreateAfterDeletion> singletonHolder; \
friend decltype (singletonHolder); \
\
static Classname* JUCE_CALLTYPE getInstance() { return singletonHolder.get(); } \
static Classname* JUCE_CALLTYPE getInstanceWithoutCreating() noexcept { return singletonHolder.instance; } \
static void JUCE_CALLTYPE deleteInstance() noexcept { singletonHolder.deleteInstance(); } \
void clearSingletonInstance() noexcept { singletonHolder.clear (this); }
//==============================================================================
/**
Macro to declare member variables and methods for a singleton class.
This is like JUCE_DECLARE_SINGLETON_SINGLETHREADED, but doesn't do any checking
for recursion or repeated instantiation. It's intended for use as a lightweight
version of a singleton, where you're using it in very straightforward
circumstances and don't need the extra checking.
See the documentation for JUCE_DECLARE_SINGLETON for more information about
how to use it. Just like JUCE_DECLARE_SINGLETON you need to also have a
corresponding JUCE_IMPLEMENT_SINGLETON statement somewhere in your code.
@see JUCE_IMPLEMENT_SINGLETON, JUCE_DECLARE_SINGLETON
*/
#define JUCE_DECLARE_SINGLETON_SINGLETHREADED_MINIMAL(Classname) \
\
static juce::SingletonHolder<Classname, juce::DummyCriticalSection, false> singletonHolder; \
friend decltype (singletonHolder); \
\
static Classname* JUCE_CALLTYPE getInstance() { return singletonHolder.getWithoutChecking(); } \
static Classname* JUCE_CALLTYPE getInstanceWithoutCreating() noexcept { return singletonHolder.instance; } \
static void JUCE_CALLTYPE deleteInstance() noexcept { singletonHolder.deleteInstance(); } \
void clearSingletonInstance() noexcept { singletonHolder.clear (this); }
//==============================================================================
#ifndef DOXYGEN
// These are ancient macros, and have now been updated with new names to match the JUCE style guide,
// so please update your code to use the newer versions!
#define juce_DeclareSingleton(Classname, doNotRecreate) JUCE_DECLARE_SINGLETON(Classname, doNotRecreate)
#define juce_DeclareSingleton_SingleThreaded(Classname, doNotRecreate) JUCE_DECLARE_SINGLETON_SINGLETHREADED(Classname, doNotRecreate)
#define juce_DeclareSingleton_SingleThreaded_Minimal(Classname) JUCE_DECLARE_SINGLETON_SINGLETHREADED_MINIMAL(Classname)
#define juce_ImplementSingleton(Classname) JUCE_IMPLEMENT_SINGLETON(Classname)
#define juce_ImplementSingleton_SingleThreaded(Classname) JUCE_IMPLEMENT_SINGLETON(Classname)
#endif
} // namespace juce

View File

@ -0,0 +1,240 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
/**
This class acts as a pointer which will automatically become null if the object
to which it points is deleted.
To accomplish this, the source object needs to cooperate by performing a couple of simple tasks.
It must embed a WeakReference::Master object, which stores a shared pointer object, and must clear
this master pointer in its destructor.
Note that WeakReference is not designed to be thread-safe, so if you're accessing it from
different threads, you'll need to do your own locking around all uses of the pointer and
the object it refers to.
E.g.
@code
class MyObject
{
public:
MyObject() {}
~MyObject()
{
// This will zero all the references - you need to call this in your destructor.
masterReference.clear();
}
private:
// You need to embed a variable of this type, with the name "masterReference" inside your object. If the
// variable is not public, you should make your class a friend of WeakReference<MyObject> so that the
// WeakReference class can access it.
WeakReference<MyObject>::Master masterReference;
friend class WeakReference<MyObject>;
};
OR: just use the handy JUCE_DECLARE_WEAK_REFERENCEABLE macro to do all this for you.
// Here's an example of using a pointer..
auto* n = new MyObject();
WeakReference<MyObject> myObjectRef = n;
auto pointer1 = myObjectRef.get(); // returns a valid pointer to 'n'
delete n;
auto pointer2 = myObjectRef.get(); // now returns nullptr
@endcode
@see WeakReference::Master
@tags{Core}
*/
template <class ObjectType, class ReferenceCountingType = ReferenceCountedObject>
class WeakReference
{
public:
/** Creates a null WeakReference. */
inline WeakReference() = default;
/** Creates a WeakReference that points at the given object. */
WeakReference (ObjectType* object) : holder (getRef (object)) {}
/** Creates a copy of another WeakReference. */
WeakReference (const WeakReference& other) noexcept : holder (other.holder) {}
/** Move constructor */
WeakReference (WeakReference&& other) noexcept : holder (std::move (other.holder)) {}
/** Copies another pointer to this one. */
WeakReference& operator= (const WeakReference& other) { holder = other.holder; return *this; }
/** Copies another pointer to this one. */
WeakReference& operator= (ObjectType* newObject) { holder = getRef (newObject); return *this; }
/** Move assignment operator */
WeakReference& operator= (WeakReference&& other) noexcept { holder = std::move (other.holder); return *this; }
/** Returns the object that this pointer refers to, or null if the object no longer exists. */
ObjectType* get() const noexcept { return holder != nullptr ? holder->get() : nullptr; }
/** Returns the object that this pointer refers to, or null if the object no longer exists. */
operator ObjectType*() const noexcept { return get(); }
/** Returns the object that this pointer refers to, or null if the object no longer exists. */
ObjectType* operator->() const noexcept { return get(); }
/** This returns true if this reference has been pointing at an object, but that object has
since been deleted.
If this reference was only ever pointing at a null pointer, this will return false. Using
operator=() to make this refer to a different object will reset this flag to match the status
of the reference from which you're copying.
*/
bool wasObjectDeleted() const noexcept { return holder != nullptr && holder->get() == nullptr; }
bool operator== (ObjectType* object) const noexcept { return get() == object; }
bool operator!= (ObjectType* object) const noexcept { return get() != object; }
//==============================================================================
/** This class is used internally by the WeakReference class - don't use it directly
in your code!
@see WeakReference
*/
class SharedPointer : public ReferenceCountingType
{
public:
explicit SharedPointer (ObjectType* obj) noexcept : owner (obj) {}
inline ObjectType* get() const noexcept { return owner; }
void clearPointer() noexcept { owner = nullptr; }
private:
ObjectType* owner;
JUCE_DECLARE_NON_COPYABLE (SharedPointer)
};
using SharedRef = ReferenceCountedObjectPtr<SharedPointer>;
//==============================================================================
/**
This class is embedded inside an object to which you want to attach WeakReference pointers.
See the WeakReference class notes for an example of how to use this class.
@see WeakReference
*/
class Master
{
public:
Master() = default;
~Master() noexcept
{
// You must remember to call clear() in your source object's destructor! See the notes
// for the WeakReference class for an example of how to do this.
jassert (sharedPointer == nullptr || sharedPointer->get() == nullptr);
}
/** The first call to this method will create an internal object that is shared by all weak
references to the object.
*/
SharedRef getSharedPointer (ObjectType* object)
{
if (sharedPointer == nullptr)
{
sharedPointer = *new SharedPointer (object);
}
else
{
// You're trying to create a weak reference to an object that has already been deleted!!
jassert (sharedPointer->get() != nullptr);
}
return sharedPointer;
}
/** The object that owns this master pointer should call this before it gets destroyed,
to zero all the references to this object that may be out there. See the WeakReference
class notes for an example of how to do this.
*/
void clear() noexcept
{
if (sharedPointer != nullptr)
sharedPointer->clearPointer();
}
/** Returns the number of WeakReferences that are out there pointing to this object. */
int getNumActiveWeakReferences() const noexcept
{
return sharedPointer == nullptr ? 0 : (sharedPointer->getReferenceCount() - 1);
}
private:
SharedRef sharedPointer;
JUCE_DECLARE_NON_COPYABLE (Master)
};
private:
SharedRef holder;
static SharedRef getRef (ObjectType* o)
{
if (o != nullptr)
return o->masterReference.getSharedPointer (o);
return {};
}
};
//==============================================================================
/**
Macro to easily allow a class to be made weak-referenceable.
This can be inserted in a class definition to add the requisite weak-ref boilerplate to that class.
e.g.
@code
class MyObject
{
public:
MyObject();
~MyObject();
private:
JUCE_DECLARE_WEAK_REFERENCEABLE (MyObject)
};
@endcode
@see WeakReference, WeakReference::Master
*/
#define JUCE_DECLARE_WEAK_REFERENCEABLE(Class) \
struct WeakRefMaster : public juce::WeakReference<Class>::Master { ~WeakRefMaster() { this->clear(); } }; \
WeakRefMaster masterReference; \
friend class juce::WeakReference<Class>; \
} // namespace juce

View File

@ -0,0 +1,439 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
static File resolveFilename (const String& name)
{
return File::getCurrentWorkingDirectory().getChildFile (name.unquoted());
}
static File checkFileExists (const File& f)
{
if (! f.exists())
ConsoleApplication::fail ("Could not find file: " + f.getFullPathName());
return f;
}
static File checkFolderExists (const File& f)
{
if (! f.isDirectory())
ConsoleApplication::fail ("Could not find folder: " + f.getFullPathName());
return f;
}
static File resolveFilenameForOption (const ArgumentList& args, StringRef option, const String& filename)
{
if (filename.isEmpty())
{
args.failIfOptionIsMissing (option);
ConsoleApplication::fail ("Expected a filename after the " + option + " option");
}
return resolveFilename (filename);
}
File ArgumentList::Argument::resolveAsFile() const
{
return resolveFilename (text);
}
File ArgumentList::Argument::resolveAsExistingFile() const
{
return checkFileExists (resolveAsFile());
}
File ArgumentList::Argument::resolveAsExistingFolder() const
{
auto f = resolveAsFile();
if (! f.isDirectory())
ConsoleApplication::fail ("Could not find folder: " + f.getFullPathName());
return f;
}
static bool isShortOptionFormat (StringRef s) { return s[0] == '-' && s[1] != '-'; }
static bool isLongOptionFormat (StringRef s) { return s[0] == '-' && s[1] == '-' && s[2] != '-'; }
static bool isOptionFormat (StringRef s) { return s[0] == '-'; }
bool ArgumentList::Argument::isLongOption() const { return isLongOptionFormat (text); }
bool ArgumentList::Argument::isShortOption() const { return isShortOptionFormat (text); }
bool ArgumentList::Argument::isOption() const { return isOptionFormat (text); }
bool ArgumentList::Argument::isLongOption (const String& option) const
{
if (! isLongOptionFormat (option))
{
jassert (! isShortOptionFormat (option)); // this will always fail to match
return isLongOption ("--" + option);
}
return text.upToFirstOccurrenceOf ("=", false, false) == option;
}
String ArgumentList::Argument::getLongOptionValue() const
{
if (isLongOption())
{
auto equalsIndex = text.indexOfChar ('=');
if (equalsIndex > 0)
return text.substring (equalsIndex + 1);
}
return {};
}
bool ArgumentList::Argument::isShortOption (char option) const
{
jassert (option != '-'); // this is probably not what you intended to pass in
return isShortOption() && text.containsChar (String (option)[0]);
}
bool ArgumentList::Argument::operator== (StringRef wildcard) const
{
for (auto& o : StringArray::fromTokens (wildcard, "|", {}))
{
if (text == o)
return true;
if (isShortOptionFormat (o) && o.length() == 2 && isShortOption ((char) o[1]))
return true;
if (isLongOptionFormat (o) && isLongOption (o))
return true;
}
return false;
}
bool ArgumentList::Argument::operator!= (StringRef s) const { return ! operator== (s); }
//==============================================================================
ArgumentList::ArgumentList (String exeName, StringArray args)
: executableName (std::move (exeName))
{
args.trim();
args.removeEmptyStrings();
for (auto& a : args)
arguments.add ({ a.unquoted() });
}
ArgumentList::ArgumentList (int argc, char* argv[])
: ArgumentList (argv[0], StringArray (argv + 1, argc - 1))
{
}
ArgumentList::ArgumentList (const String& exeName, const String& args)
: ArgumentList (exeName, StringArray::fromTokens (args, true))
{
}
int ArgumentList::size() const { return arguments.size(); }
ArgumentList::Argument ArgumentList::operator[] (int index) const { return arguments[index]; }
void ArgumentList::checkMinNumArguments (int expectedMinNumberOfArgs) const
{
if (size() < expectedMinNumberOfArgs)
ConsoleApplication::fail ("Not enough arguments!");
}
int ArgumentList::indexOfOption (StringRef option) const
{
jassert (option == String (option).trim()); // passing non-trimmed strings will always fail to find a match!
for (int i = 0; i < arguments.size(); ++i)
if (arguments.getReference (i) == option)
return i;
return -1;
}
bool ArgumentList::containsOption (StringRef option) const
{
return indexOfOption (option) >= 0;
}
bool ArgumentList::removeOptionIfFound (StringRef option)
{
auto i = indexOfOption (option);
if (i >= 0)
arguments.remove (i);
return i >= 0;
}
void ArgumentList::failIfOptionIsMissing (StringRef option) const
{
if (indexOfOption (option) < 0)
ConsoleApplication::fail ("Expected the option " + option);
}
String ArgumentList::getValueForOption (StringRef option) const
{
jassert (isOptionFormat (option)); // the thing you're searching for must be an option
for (int i = 0; i < arguments.size(); ++i)
{
auto& arg = arguments.getReference(i);
if (arg == option)
{
if (arg.isShortOption())
{
if (i < arguments.size() - 1 && ! arguments.getReference (i + 1).isOption())
return arguments.getReference (i + 1).text;
return {};
}
if (arg.isLongOption())
return arg.getLongOptionValue();
}
}
return {};
}
String ArgumentList::removeValueForOption (StringRef option)
{
jassert (isOptionFormat (option)); // the thing you're searching for must be an option
for (int i = 0; i < arguments.size(); ++i)
{
auto& arg = arguments.getReference(i);
if (arg == option)
{
if (arg.isShortOption())
{
if (i < arguments.size() - 1 && ! arguments.getReference (i + 1).isOption())
{
auto result = arguments.getReference (i + 1).text;
arguments.removeRange (i, 2);
return result;
}
arguments.remove (i);
return {};
}
if (arg.isLongOption())
{
auto result = arg.getLongOptionValue();
arguments.remove (i);
return result;
}
}
}
return {};
}
File ArgumentList::getFileForOption (StringRef option) const
{
return resolveFilenameForOption (*this, option, getValueForOption (option));
}
File ArgumentList::getFileForOptionAndRemove (StringRef option)
{
return resolveFilenameForOption (*this, option, removeValueForOption (option));
}
File ArgumentList::getExistingFileForOption (StringRef option) const
{
return checkFileExists (getFileForOption (option));
}
File ArgumentList::getExistingFileForOptionAndRemove (StringRef option)
{
return checkFileExists (getFileForOptionAndRemove (option));
}
File ArgumentList::getExistingFolderForOption (StringRef option) const
{
return checkFolderExists (getFileForOption (option));
}
File ArgumentList::getExistingFolderForOptionAndRemove (StringRef option)
{
return checkFolderExists (getFileForOptionAndRemove (option));
}
//==============================================================================
struct ConsoleAppFailureCode
{
String errorMessage;
int returnCode;
};
void ConsoleApplication::fail (String errorMessage, int returnCode)
{
throw ConsoleAppFailureCode { std::move (errorMessage), returnCode };
}
int ConsoleApplication::invokeCatchingFailures (std::function<int()>&& f)
{
int returnCode = 0;
try
{
returnCode = f();
}
catch (const ConsoleAppFailureCode& error)
{
std::cerr << error.errorMessage << std::endl;
returnCode = error.returnCode;
}
return returnCode;
}
const ConsoleApplication::Command* ConsoleApplication::findCommand (const ArgumentList& args, bool optionMustBeFirstArg) const
{
for (auto& c : commands)
{
auto index = args.indexOfOption (c.commandOption);
if (optionMustBeFirstArg ? (index == 0) : (index >= 0))
return &c;
}
if (commandIfNoOthersRecognised >= 0)
return &commands[(size_t) commandIfNoOthersRecognised];
return {};
}
int ConsoleApplication::findAndRunCommand (const ArgumentList& args, bool optionMustBeFirstArg) const
{
return invokeCatchingFailures ([&args, optionMustBeFirstArg, this]
{
if (auto c = findCommand (args, optionMustBeFirstArg))
c->command (args);
else
fail ("Unrecognised arguments");
return 0;
});
}
int ConsoleApplication::findAndRunCommand (int argc, char* argv[]) const
{
return findAndRunCommand (ArgumentList (argc, argv));
}
void ConsoleApplication::addCommand (Command c)
{
commands.emplace_back (std::move (c));
}
void ConsoleApplication::addDefaultCommand (Command c)
{
commandIfNoOthersRecognised = (int) commands.size();
addCommand (std::move (c));
}
void ConsoleApplication::addHelpCommand (String arg, String helpMessage, bool makeDefaultCommand)
{
Command c { arg, arg, "Prints the list of commands", {},
[this, helpMessage] (const ArgumentList& args)
{
std::cout << helpMessage << std::endl;
printCommandList (args);
}};
if (makeDefaultCommand)
addDefaultCommand (std::move (c));
else
addCommand (std::move (c));
}
void ConsoleApplication::addVersionCommand (String arg, String versionText)
{
addCommand ({ arg, arg, "Prints the current version number", {},
[versionText] (const ArgumentList&)
{
std::cout << versionText << std::endl;
}});
}
const std::vector<ConsoleApplication::Command>& ConsoleApplication::getCommands() const
{
return commands;
}
static String getExeNameAndArgs (const ArgumentList& args, const ConsoleApplication::Command& command)
{
auto exeName = args.executableName.fromLastOccurrenceOf ("/", false, false)
.fromLastOccurrenceOf ("\\", false, false);
return " " + exeName + " " + command.argumentDescription;
}
static void printCommandDescription (const ArgumentList& args, const ConsoleApplication::Command& command,
int descriptionIndent)
{
auto nameAndArgs = getExeNameAndArgs (args, command);
if (nameAndArgs.length() > descriptionIndent)
std::cout << nameAndArgs << std::endl << String().paddedRight (' ', descriptionIndent);
else
std::cout << nameAndArgs.paddedRight (' ', descriptionIndent);
std::cout << command.shortDescription << std::endl;
}
void ConsoleApplication::printCommandList (const ArgumentList& args) const
{
int descriptionIndent = 0;
for (auto& c : commands)
descriptionIndent = std::max (descriptionIndent, getExeNameAndArgs (args, c).length());
descriptionIndent = std::min (descriptionIndent + 2, 40);
for (auto& c : commands)
printCommandDescription (args, c, descriptionIndent);
std::cout << std::endl;
}
void ConsoleApplication::printCommandDetails (const ArgumentList& args, const Command& command) const
{
auto len = getExeNameAndArgs (args, command).length();
printCommandDescription (args, command, std::min (len + 3, 40));
if (command.longDescription.isNotEmpty())
std::cout << std::endl << command.longDescription << std::endl;
}
} // namespace juce

View File

@ -0,0 +1,353 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
/**
Holds a list of command-line arguments, and provides useful methods for searching
and operating on them.
You can create an ArgumentList manually, or give it some argv/argc values from a
main() function to parse.
@see ConsoleApplication
@tags{Core}
*/
struct ArgumentList
{
/** Creates an argument list for a given executable. */
ArgumentList (String executable, StringArray arguments);
/** Parses a standard argv/argc pair to create an argument list. */
ArgumentList (int argc, char* argv[]);
/** Tokenises a string containing all the arguments to create an argument list. */
ArgumentList (const String& executable, const String& arguments);
ArgumentList (const ArgumentList&) = default;
ArgumentList& operator= (const ArgumentList&) = default;
//==============================================================================
/**
One of the arguments in an ArgumentList.
@tags{Core}
*/
struct Argument
{
/** The original text of this argument. */
String text;
/** Resolves this argument as an absolute File, using the current working
directory as a base for resolving relative paths, and stripping quotes, etc.
*/
File resolveAsFile() const;
/** Resolves this argument as an absolute File, using the current working
directory as a base for resolving relative paths, and also doing a check to
make sure the file exists.
If the file doesn't exist, this will call fail() with a suitable error.
@see resolveAsFile, resolveAsExistingFolder
*/
File resolveAsExistingFile() const;
/** Resolves a user-supplied folder name into an absolute File, using the current working
directory as a base for resolving relative paths, and also doing a check to make
sure the folder exists.
If the folder doesn't exist, this will call fail() with a suitable error.
@see resolveAsFile, resolveAsExistingFile
*/
File resolveAsExistingFolder() const;
/** Returns true if this argument starts with a double dash. */
bool isLongOption() const;
/** Returns true if this argument starts with a single dash. */
bool isShortOption() const;
/** Returns true if this argument starts with a double dash, followed by the given string. */
bool isLongOption (const String& optionRoot) const;
/** If this argument is a long option with a value, this returns the value.
e.g. for "--foo=bar", this would return 'bar'.
*/
String getLongOptionValue() const;
/** Returns true if this argument starts with a single dash and then contains the given character somewhere inside it. */
bool isShortOption (char shortOptionCharacter) const;
/** Returns true if this argument starts with one or more dashes. */
bool isOption() const;
/** Compares this argument against a string.
The string may be a pipe-separated list of options, e.g. "--help|-h"
*/
bool operator== (StringRef stringToCompare) const;
/** Compares this argument against a string.
The string may be a pipe-separated list of options, e.g. "--help|-h"
*/
bool operator!= (StringRef stringToCompare) const;
};
//==============================================================================
/** Returns the number of arguments in the list. */
int size() const;
/** Returns one of the arguments */
Argument operator[] (int index) const;
/** Throws an error unless there are at least the given number of arguments. */
void checkMinNumArguments (int expectedMinNumberOfArgs) const;
/** Returns true if the given string matches one of the arguments.
The option can also be a list of different versions separated by pipes, e.g. "--help|-h"
@see removeOptionIfFound
*/
bool containsOption (StringRef option) const;
/** Returns true if the given string matches one of the arguments, and also removes the
argument from the list if found.
The option can also be a list of different versions separated by pipes, e.g. "--help|-h"
@see containsOption
*/
bool removeOptionIfFound (StringRef option);
/** Returns the index of the given string if it matches one of the arguments, or -1 if it doesn't.
The option can also be a list of different versions separated by pipes, e.g. "--help|-h"
*/
int indexOfOption (StringRef option) const;
/** Throws an error unless the given option is found in the argument list. */
void failIfOptionIsMissing (StringRef option) const;
/** Looks for a given argument and returns either its assigned value (for long options) or the
string that follows it (for short options).
The option can also be a list of different versions separated by pipes, e.g. "--help|-h"
If it finds a long option, it will look for an assignment with a '=' sign, e.g. "--file=foo.txt",
and will return the string following the '='. If there's no '=', it will return an empty string.
If it finds a short option, it will attempt to return the argument that follows it, unless
it's another option.
If the argument isn't found, this returns an empty string.
*/
String getValueForOption (StringRef option) const;
/** Looks for a given argument and returns either its assigned value (for long options) or the
string that follows it (for short options).
This works like getValueForOption() but also removes the option argument (and any value arguments)
from the list if they are found.
*/
String removeValueForOption (StringRef option);
/** Looks for the value of argument using getValueForOption() and tries to parse that value as a file.
If the option isn't found, or if the value can't be parsed as a filename, it will throw an error.
*/
File getFileForOption (StringRef option) const;
/** Looks for the value of argument using getValueForOption() and tries to parse that value as a file.
This works like getFileForOption() but also removes the option argument (and any value arguments)
from the list if they are found.
*/
File getFileForOptionAndRemove (StringRef option);
/** Looks for a file argument using getFileForOption() and fails with a suitable error if
the file doesn't exist.
*/
File getExistingFileForOption (StringRef option) const;
/** Looks for a file argument using getFileForOption() and fails with a suitable error if
the file doesn't exist.
This works like getExistingFileForOption() but also removes the option argument (and any
value arguments) from the list if they are found.
*/
File getExistingFileForOptionAndRemove (StringRef option);
/** Looks for a filename argument using getFileForOption() and fails with a suitable error if
the file isn't a folder that exists.
*/
File getExistingFolderForOption (StringRef option) const;
/** Looks for a filename argument using getFileForOption() and fails with a suitable error if
the file isn't a folder that exists.
This works like getExistingFolderForOption() but also removes the option argument (and any
value arguments) from the list if they are found.
*/
File getExistingFolderForOptionAndRemove (StringRef option);
/** The name or path of the executable that was invoked, as it was specified on the command-line. */
String executableName;
/** The list of arguments (not including the name of the executable that was invoked). */
Array<Argument> arguments;
};
//==============================================================================
/**
Represents a the set of commands that a console app can perform, and provides
helper functions for performing them.
When using these helper classes to implement a console app, you probably want to
do something along these lines:
@code
int main (int argc, char* argv[])
{
ConsoleApplication app;
app.addHelpCommand ("--help|-h", "Usage:", true);
app.addVersionCommand ("--version|-v", "MyApp version 1.2.3");
app.addCommand ({ "--foo",
"--foo filename",
"Performs a foo operation on the given file",
[] (const auto& args) { doFoo (args); }});
return app.findAndRunCommand (argc, argv);
}
@endcode
@see ArgumentList
@tags{Core}
*/
struct ConsoleApplication
{
//==============================================================================
/**
Represents a command that can be executed if its command-line arguments are matched.
@see ConsoleApplication::addCommand(), ConsoleApplication::findAndRunCommand()
@tags{Core}
*/
struct Command
{
/** The option string that must appear in the argument list for this command to be invoked.
This can also be a list of different versions separated by pipes, e.g. "--help|-h"
*/
String commandOption;
/** A description of the command-line arguments needed for this command, which will be
printed as part of the help text.
*/
String argumentDescription;
/** A short (one line) description of this command, which can be printed by
ConsoleApplication::printCommandList().
*/
String shortDescription;
/** A longer description of this command, for use in extended help. */
String longDescription;
/** The actual command that should be invoked to perform this action. */
std::function<void (const ArgumentList&)> command;
};
//==============================================================================
/** Adds a command to the list. */
void addCommand (Command);
/** Adds a command to the list, and marks it as one which is invoked if no other
command matches.
*/
void addDefaultCommand (Command);
/** Adds a command that will print the given text in response to the "--version" option. */
void addVersionCommand (String versionArgument, String versionText);
/** Adds a help command to the list.
This command will print the user-supplied message that's passed in here as an
argument, followed by a list of all the registered commands.
*/
void addHelpCommand (String helpArgument, String helpMessage, bool makeDefaultCommand);
/** Prints out the list of commands and their short descriptions in a format that's
suitable for use as help.
*/
void printCommandList (const ArgumentList&) const;
/** Prints out a longer description of a particular command, based on its
longDescription member.
*/
void printCommandDetails (const ArgumentList&, const Command&) const;
//==============================================================================
/** Throws a failure exception to cause a command-line app to terminate.
This is intended to be called from code in a Command, so that the
exception will be automatically caught and turned into a printed error message
and a return code which will be returned from main().
@see ConsoleApplication::invokeCatchingFailures()
*/
static void fail (String errorMessage, int returnCode = 1);
/** Invokes a function, catching any fail() calls that it might trigger, and handling
them by printing their error message and returning their error code.
@see ConsoleApplication::fail()
*/
static int invokeCatchingFailures (std::function<int()>&& functionToCall);
//==============================================================================
/** Looks for the first command in the list which matches the given arguments, and
tries to invoke it.
If no command is found, and if there is no default command to run, it fails with
a suitable error message.
If the command calls the fail() function, this will throw an exception that gets
automatically caught and handled, and this method will return the error code that
was passed into the fail() call.
If optionMustBeFirstArg is true, then only the first argument will be looked at
when searching the available commands - this lets you do 'git' style commands where
the executable name is followed by a verb.
*/
int findAndRunCommand (const ArgumentList&,
bool optionMustBeFirstArg = false) const;
/** Creates an ArgumentList object from the argc and argv variablrs, and invokes
findAndRunCommand() using it.
*/
int findAndRunCommand (int argc, char* argv[]) const;
/** Looks for the first command in the list which matches the given arguments.
If none is found, this returns either the default command (if one is set)
or nullptr.
If optionMustBeFirstArg is true, then only the first argument will be looked at
when searching the available commands - this lets you do 'git' style commands where
the executable name is followed by a verb.
*/
const Command* findCommand (const ArgumentList&, bool optionMustBeFirstArg) const;
/** Gives read-only access to the list of registered commands. */
const std::vector<Command>& getCommands() const;
private:
//==============================================================================
std::vector<Command> commands;
int commandIfNoOthersRecognised = -1;
};
} // namespace juce

View File

@ -0,0 +1,53 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
/** Some helper methods for checking a callable object before invoking with
the specified arguments.
If the object is a std::function it will check for nullptr before
calling. For a callable object it will invoke the function call operator.
@tags{Core}
*/
struct NullCheckedInvocation
{
template <typename... Signature, typename... Args>
static void invoke (std::function<Signature...>&& fn, Args&&... args)
{
if (fn != nullptr)
fn (std::forward<Args> (args)...);
}
template <typename Callable, typename... Args>
static void invoke (Callable&& fn, Args&&... args)
{
fn (std::forward<Args> (args)...);
}
template <typename... Args>
static void invoke (std::nullptr_t, Args&&...) {}
};
} // namespace juce

View File

@ -0,0 +1,80 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
Result::Result() noexcept {}
Result::Result (const String& message) noexcept
: errorMessage (message)
{
}
Result::Result (const Result& other)
: errorMessage (other.errorMessage)
{
}
Result& Result::operator= (const Result& other)
{
errorMessage = other.errorMessage;
return *this;
}
Result::Result (Result&& other) noexcept
: errorMessage (std::move (other.errorMessage))
{
}
Result& Result::operator= (Result&& other) noexcept
{
errorMessage = std::move (other.errorMessage);
return *this;
}
bool Result::operator== (const Result& other) const noexcept
{
return errorMessage == other.errorMessage;
}
bool Result::operator!= (const Result& other) const noexcept
{
return errorMessage != other.errorMessage;
}
Result Result::fail (const String& errorMessage) noexcept
{
return Result (errorMessage.isEmpty() ? "Unknown Error" : errorMessage);
}
const String& Result::getErrorMessage() const noexcept
{
return errorMessage;
}
bool Result::wasOk() const noexcept { return errorMessage.isEmpty(); }
Result::operator bool() const noexcept { return errorMessage.isEmpty(); }
bool Result::failed() const noexcept { return errorMessage.isNotEmpty(); }
bool Result::operator!() const noexcept { return errorMessage.isNotEmpty(); }
} // namespace juce

View File

@ -0,0 +1,116 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
/**
Represents the 'success' or 'failure' of an operation, and holds an associated
error message to describe the error when there's a failure.
E.g.
@code
Result myOperation()
{
if (doSomeKindOfFoobar())
return Result::ok();
else
return Result::fail ("foobar didn't work!");
}
const Result result (myOperation());
if (result.wasOk())
{
...it's all good...
}
else
{
warnUserAboutFailure ("The foobar operation failed! Error message was: "
+ result.getErrorMessage());
}
@endcode
@tags{Core}
*/
class JUCE_API Result
{
public:
//==============================================================================
/** Creates and returns a 'successful' result. */
static Result ok() noexcept { return Result(); }
/** Creates a 'failure' result.
If you pass a blank error message in here, a default "Unknown Error" message
will be used instead.
*/
static Result fail (const String& errorMessage) noexcept;
//==============================================================================
/** Returns true if this result indicates a success. */
bool wasOk() const noexcept;
/** Returns true if this result indicates a failure.
You can use getErrorMessage() to retrieve the error message associated
with the failure.
*/
bool failed() const noexcept;
/** Returns true if this result indicates a success.
This is equivalent to calling wasOk().
*/
operator bool() const noexcept;
/** Returns true if this result indicates a failure.
This is equivalent to calling failed().
*/
bool operator!() const noexcept;
/** Returns the error message that was set when this result was created.
For a successful result, this will be an empty string;
*/
const String& getErrorMessage() const noexcept;
//==============================================================================
Result (const Result&);
Result& operator= (const Result&);
Result (Result&&) noexcept;
Result& operator= (Result&&) noexcept;
bool operator== (const Result& other) const noexcept;
bool operator!= (const Result& other) const noexcept;
private:
String errorMessage;
// The default constructor is not for public use!
// Instead, use Result::ok() or Result::fail()
Result() noexcept;
explicit Result (const String&) noexcept;
// These casts are private to prevent people trying to use the Result object in numeric contexts
operator int() const;
operator void*() const;
};
} // namespace juce

View File

@ -0,0 +1,36 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
#if ! JUCE_ANDROID // We currently don't request runtime permissions on any other platform
// than Android, so this file contains a dummy implementation for those.
// This may change in the future.
void RuntimePermissions::request (PermissionID, Callback callback) { callback (true); }
bool RuntimePermissions::isRequired (PermissionID) { return false; }
bool RuntimePermissions::isGranted (PermissionID) { return true; }
#endif
} // namespace juce

View File

@ -0,0 +1,131 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
/**
Class to handle app runtime permissions for certain functionality on some platforms.
The use of this class is currently only required if the app should run on
Android API level 23 and higher.
On lower API levels, the permissions are specified in the app manifest. On iOS,
runtime permission requests are handled automatically by the Apple APIs and not
manually in the app code. On Windows, OS X, and Linux, runtime permissions are not
used at all. In all these cases, request() will simply call through to the
callback with no overhead and pass true (making it safe to use on all platforms).
For example, to enable audio recording on Android in your cross-platform app,
you could modify your code as follows:
Old code:
audioDeviceManager.initialise (2, 2, nullptr, true, String(), nullptr);
New code:
RuntimePermissions::request (
RuntimePermissions::audioRecording,
[this] (bool wasGranted)
{
if (! wasGranted)
{
// e.g. display an error or initialise with 0 input channels
return;
}
audioDeviceManager.initialise (2, 2, nullptr, true, String(), nullptr);
}
);
@tags{Core}
*/
class JUCE_API RuntimePermissions
{
public:
//==============================================================================
enum PermissionID
{
/** Permission to access the microphone (required on Android).
You need to request this, for example, to initialise an AudioDeviceManager with
a non-zero number of input channels, and to open the default audio input device.
*/
recordAudio = 1,
/** Permission to scan for and pair to Bluetooth MIDI devices (required on Android).
You need to request this before calling BluetoothMidiDevicePairingDialogue::open(),
otherwise no devices will be found.
*/
bluetoothMidi = 2,
/** Permission to read from external storage such as SD cards */
readExternalStorage = 3,
/** Permission to write to external storage such as SD cards */
writeExternalStorage = 4,
/** Permission to use camera */
camera = 5
};
//==============================================================================
/** Function type of runtime permission request callbacks. */
using Callback = std::function<void (bool)>;
//==============================================================================
/** Call this method to request a runtime permission.
@param permission The PermissionID of the permission you want to request.
@param callback The callback to be called after the request has been granted
or denied; the argument passed will be true if the permission
has been granted and false otherwise.
If no runtime request is required or possible to obtain the permission, the
callback will be called immediately. The argument passed in will be true
if the permission is granted or no permission is required on this platform,
and false otherwise.
If a runtime request is required to obtain the permission, the callback
will be called asynchronously after the OS has granted or denied the requested
permission (typically by displaying a dialog box to the user and waiting until
the user has responded).
*/
static void request (PermissionID permission, Callback callback);
/** Returns whether a runtime request is required to obtain the permission
on the current platform.
*/
static bool isRequired (PermissionID permission);
/** Returns true if the app has been already granted this permission, either
via a previous runtime request or otherwise, or no permission is necessary.
Note that this can be false even if isRequired returns false. In this case,
the permission can not be obtained at all at runtime.
*/
static bool isGranted (PermissionID permission);
};
} // namespace juce

View File

@ -0,0 +1,147 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
Uuid::Uuid()
{
Random r;
for (size_t i = 0; i < sizeof (uuid); ++i)
uuid[i] = (uint8) (r.nextInt (256));
// To make it RFC 4122 compliant, need to force a few bits...
uuid[6] = (uuid[6] & 0x0f) | 0x40;
uuid[8] = (uuid[8] & 0x3f) | 0x80;
}
Uuid::~Uuid() noexcept {}
Uuid::Uuid (const Uuid& other) noexcept
{
memcpy (uuid, other.uuid, sizeof (uuid));
}
Uuid& Uuid::operator= (const Uuid& other) noexcept
{
memcpy (uuid, other.uuid, sizeof (uuid));
return *this;
}
bool Uuid::operator== (const Uuid& other) const noexcept { return memcmp (uuid, other.uuid, sizeof (uuid)) == 0; }
bool Uuid::operator!= (const Uuid& other) const noexcept { return ! operator== (other); }
bool Uuid::operator< (const Uuid& other) const noexcept { return compare (other) < 0; }
bool Uuid::operator> (const Uuid& other) const noexcept { return compare (other) > 0; }
bool Uuid::operator<= (const Uuid& other) const noexcept { return compare (other) <= 0; }
bool Uuid::operator>= (const Uuid& other) const noexcept { return compare (other) >= 0; }
int Uuid::compare (Uuid other) const noexcept
{
for (size_t i = 0; i < sizeof (uuid); ++i)
if (int diff = uuid[i] - (int) other.uuid[i])
return diff > 0 ? 1 : -1;
return 0;
}
Uuid Uuid::null() noexcept
{
return Uuid ((const uint8*) nullptr);
}
bool Uuid::isNull() const noexcept
{
for (auto i : uuid)
if (i != 0)
return false;
return true;
}
String Uuid::getHexRegion (int start, int length) const
{
return String::toHexString (uuid + start, length, 0);
}
String Uuid::toString() const
{
return getHexRegion (0, 16);
}
String Uuid::toDashedString() const
{
return getHexRegion (0, 4)
+ "-" + getHexRegion (4, 2)
+ "-" + getHexRegion (6, 2)
+ "-" + getHexRegion (8, 2)
+ "-" + getHexRegion (10, 6);
}
Uuid::Uuid (const String& uuidString)
{
operator= (uuidString);
}
Uuid& Uuid::operator= (const String& uuidString)
{
MemoryBlock mb;
mb.loadFromHexString (uuidString);
mb.ensureSize (sizeof (uuid), true);
mb.copyTo (uuid, 0, sizeof (uuid));
return *this;
}
Uuid::Uuid (const uint8* const rawData) noexcept
{
operator= (rawData);
}
Uuid& Uuid::operator= (const uint8* const rawData) noexcept
{
if (rawData != nullptr)
memcpy (uuid, rawData, sizeof (uuid));
else
zeromem (uuid, sizeof (uuid));
return *this;
}
uint32 Uuid::getTimeLow() const noexcept { return ByteOrder::bigEndianInt (uuid); }
uint16 Uuid::getTimeMid() const noexcept { return ByteOrder::bigEndianShort (uuid + 4); }
uint16 Uuid::getTimeHighAndVersion() const noexcept { return ByteOrder::bigEndianShort (uuid + 6); }
uint8 Uuid::getClockSeqAndReserved() const noexcept { return uuid[8]; }
uint8 Uuid::getClockSeqLow() const noexcept { return uuid[9]; }
uint64 Uuid::getNode() const noexcept { return (((uint64) ByteOrder::bigEndianShort (uuid + 10)) << 32) + ByteOrder::bigEndianInt (uuid + 12); }
uint64 Uuid::hash() const noexcept
{
uint64 result = 0;
for (auto n : uuid)
result = ((uint64) 101) * result + n;
return result;
}
} // namespace juce

View File

@ -0,0 +1,147 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
/**
A universally unique 128-bit identifier.
This class generates very random unique numbers. It's vanishingly unlikely
that two identical UUIDs would ever be created by chance. The values are
formatted to meet the RFC 4122 version 4 standard.
The class includes methods for saving the ID as a string or as raw binary data.
@tags{Core}
*/
class JUCE_API Uuid
{
public:
//==============================================================================
/** Creates a new unique ID, compliant with RFC 4122 version 4. */
Uuid();
/** Destructor. */
~Uuid() noexcept;
/** Creates a copy of another UUID. */
Uuid (const Uuid&) noexcept;
/** Copies another UUID. */
Uuid& operator= (const Uuid&) noexcept;
//==============================================================================
/** Returns true if the ID is zero. */
bool isNull() const noexcept;
/** Returns a null Uuid object. */
static Uuid null() noexcept;
bool operator== (const Uuid&) const noexcept;
bool operator!= (const Uuid&) const noexcept;
bool operator< (const Uuid&) const noexcept;
bool operator> (const Uuid&) const noexcept;
bool operator<= (const Uuid&) const noexcept;
bool operator>= (const Uuid&) const noexcept;
//==============================================================================
/** Returns a stringified version of this UUID.
A Uuid object can later be reconstructed from this string using operator= or
the constructor that takes a string parameter.
@returns a 32 character hex string.
*/
String toString() const;
/** Returns a stringified version of this UUID, separating it into sections with dashes.
@returns a string in the format: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
*/
String toDashedString() const;
/** Creates an ID from an encoded string version.
@see toString
*/
Uuid (const String& uuidString);
/** Copies from a stringified UUID.
The string passed in should be one that was created with the toString() method.
*/
Uuid& operator= (const String& uuidString);
//==============================================================================
/** Returns the time-low section of the UUID. */
uint32 getTimeLow() const noexcept;
/** Returns the time-mid section of the UUID. */
uint16 getTimeMid() const noexcept;
/** Returns the time-high-and-version section of the UUID. */
uint16 getTimeHighAndVersion() const noexcept;
/** Returns the clock-seq-and-reserved section of the UUID. */
uint8 getClockSeqAndReserved() const noexcept;
/** Returns the clock-seq-low section of the UUID. */
uint8 getClockSeqLow() const noexcept;
/** Returns the node section of the UUID. */
uint64 getNode() const noexcept;
/** Returns a hash of the UUID. */
uint64 hash() const noexcept;
//==============================================================================
/** Returns a pointer to the internal binary representation of the ID.
This is an array of 16 bytes. To reconstruct a Uuid from its data, use
the constructor or operator= method that takes an array of uint8s.
*/
const uint8* getRawData() const noexcept { return uuid; }
/** Creates a UUID from a 16-byte array.
@see getRawData
*/
Uuid (const uint8* rawData) noexcept;
/** Sets this UUID from 16-bytes of raw data. */
Uuid& operator= (const uint8* rawData) noexcept;
private:
//==============================================================================
uint8 uuid[16];
String getHexRegion (int, int) const;
int compare (Uuid) const noexcept;
JUCE_LEAK_DETECTOR (Uuid)
};
} // namespace juce
#ifndef DOXYGEN
namespace std
{
template <> struct hash<juce::Uuid>
{
size_t operator() (const juce::Uuid& u) const noexcept { return (size_t) u.hash(); }
};
}
#endif

View File

@ -0,0 +1,140 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
#if JUCE_WINDOWS || DOXYGEN
/**
Contains some static helper functions for manipulating the MS Windows registry
(Only available on Windows, of course!)
@tags{Core}
*/
class JUCE_API WindowsRegistry
{
public:
/** These values can be used to specify whether the 32- or 64-bit registry should be used.
When running on a 32-bit OS, there is no 64-bit registry, so the mode will be ignored.
*/
enum WoW64Mode
{
/** Default handling: 32-bit apps will use the 32-bit registry, and 64-bit apps
will use the 64-bit registry. */
WoW64_Default = 0,
/** Always use the 64-bit registry store. (KEY_WOW64_64KEY). */
WoW64_64bit = 0x100,
/** Always use the 32-bit registry store. (KEY_WOW64_32KEY). */
WoW64_32bit = 0x200
};
//==============================================================================
/** Returns a string from the registry.
The path is a string for the entire path of a value in the registry,
e.g. "HKEY_CURRENT_USER\Software\foo\bar"
*/
static String JUCE_CALLTYPE getValue (const String& regValuePath,
const String& defaultValue = String(),
WoW64Mode mode = WoW64_Default);
/** Reads a binary block from the registry.
The path is a string for the entire path of a value in the registry,
e.g. "HKEY_CURRENT_USER\Software\foo\bar"
@returns a DWORD indicating the type of the key.
*/
static uint32 JUCE_CALLTYPE getBinaryValue (const String& regValuePath, MemoryBlock& resultData, WoW64Mode mode = WoW64_Default);
/** Sets a registry value as a string.
This will take care of creating any groups needed to get to the given registry value.
*/
static bool JUCE_CALLTYPE setValue (const String& regValuePath, const String& value, WoW64Mode mode = WoW64_Default);
/** Sets a registry value as a DWORD.
This will take care of creating any groups needed to get to the given registry value.
*/
static bool JUCE_CALLTYPE setValue (const String& regValuePath, uint32 value, WoW64Mode mode = WoW64_Default);
/** Sets a registry value as a QWORD.
This will take care of creating any groups needed to get to the given registry value.
*/
static bool JUCE_CALLTYPE setValue (const String& regValuePath, uint64 value, WoW64Mode mode = WoW64_Default);
/** Sets a registry value as a binary block.
This will take care of creating any groups needed to get to the given registry value.
*/
static bool JUCE_CALLTYPE setValue (const String& regValuePath, const MemoryBlock& value, WoW64Mode mode = WoW64_Default);
/** Returns true if the given value exists in the registry. */
static bool JUCE_CALLTYPE valueExists (const String& regValuePath, WoW64Mode mode = WoW64_Default);
/** Returns true if the given key exists in the registry. */
static bool JUCE_CALLTYPE keyExists (const String& regKeyPath, WoW64Mode mode = WoW64_Default);
/** Deletes a registry value. */
static bool JUCE_CALLTYPE deleteValue (const String& regValuePath, WoW64Mode mode = WoW64_Default);
/** Deletes a registry key (which is registry-talk for 'folder'). */
static bool JUCE_CALLTYPE deleteKey (const String& regKeyPath, WoW64Mode mode = WoW64_Default);
/** Creates a file association in the registry.
This lets you set the executable that should be launched by a given file extension.
@param fileExtension the file extension to associate, including the
initial dot, e.g. ".txt"
@param symbolicDescription a space-free short token to identify the file type
@param fullDescription a human-readable description of the file type
@param targetExecutable the executable that should be launched
@param iconResourceNumber the icon that gets displayed for the file type will be
found by looking up this resource number in the
executable. Pass 0 here to not use an icon
@param registerForCurrentUserOnly if false, this will try to register the association
for all users (you might not have permission to do this
unless running in an installer). If true, it will register the
association in HKEY_CURRENT_USER.
@param mode the WoW64 mode to use for choosing the database
*/
static bool JUCE_CALLTYPE registerFileAssociation (const String& fileExtension,
const String& symbolicDescription,
const String& fullDescription,
const File& targetExecutable,
int iconResourceNumber,
bool registerForCurrentUserOnly,
WoW64Mode mode = WoW64_Default);
#ifndef DOXYGEN
// DEPRECATED: use the other methods with a WoW64Mode parameter of WoW64_64bit instead.
[[deprecated]] static String getValueWow64 (const String&, const String& defaultValue = String());
[[deprecated]] static bool valueExistsWow64 (const String&);
[[deprecated]] static bool keyExistsWow64 (const String&);
#endif
private:
WindowsRegistry() = delete;
JUCE_DECLARE_NON_COPYABLE (WindowsRegistry)
};
#endif
} // namespace juce

View File

@ -0,0 +1,43 @@
The Java code in the module's native/java subfolders have been used to generate
dex byte-code in various places in the JUCE framework. These are the steps
required to re-generate the dex byte-code from any Java source code inside the
native/java subfolders:
1. Create a new JUCE android project with the minimal sdk version which is
required for the Java source code you wish to compile.
2. If you are creating byte-code for new .java files, move the new files into
the native/javacore/app folder of the module, or create one if it doesn't
exist. Remember that .java files need to be in nested sub-folders which
resemble their package, i.e. a Java class com.rmsl.juce.HelloWorld.java should
be in the module's native/javacore/app/com/rmsl/juce folder. If you wish to
modify existing .java files in the JUCE modules then just rename native/java to
native/javacore.
3. Build your project with Android Studio and run. The app will now use the
source code in the folder created in step 2 so you can debug your Java code
this way.
4. Once everything is working rebuild your app in release mode.
5. Go to your app's Builds/Android folder. Inside there you will find
build/intermediates/javac/release_Release/compileRelease_ReleaseJavaWithJavac/classes.
Inside of that folder, you will find all your Java byte-code compiled classes.
Remove any classes that you are not interested in (typically you'll find
Java.class and JuceApp.class which you will probably want to remove).
6. Inside of
build/intermediates/javac/release_Release/compileRelease_ReleaseJavaWithJavac/classes
execute the following dx command:
<path-to-your-android-sdk>/build-tools/<latest-build-tool-version>/dx --dex --verbose --min-sdk-version=<your-min-sdk-of-your-classes> --output /tmp/JavaDexByteCode.dex .
(Replace <your-min-sdk-of-your-classes> with the minimal sdk version you used in step 1.)
7. gzip the output:
gzip /tmp/JavaDexByteCode.dex
8. The output /tmp/JavaDexByteCode.dex.gz is now the byte code that can be
included into JUCE. You can use the Projucer's BinaryData generator
functionality to get this into a convenient char array like form.

View File

@ -0,0 +1,80 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
package com.rmsl.juce;
import android.app.DialogFragment;
import android.content.Intent;
import android.os.Bundle;
public class FragmentOverlay extends DialogFragment
{
@Override
public void onCreate (Bundle state)
{
super.onCreate (state);
cppThis = getArguments ().getLong ("cppThis");
if (cppThis != 0)
onCreateNative (cppThis, state);
}
@Override
public void onStart ()
{
super.onStart ();
if (cppThis != 0)
onStartNative (cppThis);
}
public void onRequestPermissionsResult (int requestCode,
String[] permissions,
int[] grantResults)
{
if (cppThis != 0)
onRequestPermissionsResultNative (cppThis, requestCode,
permissions, grantResults);
}
@Override
public void onActivityResult (int requestCode, int resultCode, Intent data)
{
if (cppThis != 0)
onActivityResultNative (cppThis, requestCode, resultCode, data);
}
public void close ()
{
cppThis = 0;
dismiss ();
}
//==============================================================================
private long cppThis = 0;
private native void onActivityResultNative (long myself, int requestCode, int resultCode, Intent data);
private native void onCreateNative (long myself, Bundle state);
private native void onStartNative (long myself);
private native void onRequestPermissionsResultNative (long myself, int requestCode,
String[] permissions, int[] grantResults);
}

View File

@ -0,0 +1,429 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
package com.rmsl.juce;
import java.lang.Runnable;
import java.io.*;
import java.net.URL;
import java.net.HttpURLConnection;
import java.util.concurrent.CancellationException;
import java.util.concurrent.Future;
import java.util.concurrent.Executors;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Callable;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.atomic.*;
public class JuceHTTPStream
{
public JuceHTTPStream(String address, boolean isPostToUse, byte[] postDataToUse,
String headersToUse, int timeOutMsToUse,
int[] statusCodeToUse, StringBuffer responseHeadersToUse,
int numRedirectsToFollowToUse, String httpRequestCmdToUse) throws IOException
{
isPost = isPostToUse;
postData = postDataToUse;
headers = headersToUse;
timeOutMs = timeOutMsToUse;
statusCode = statusCodeToUse;
responseHeaders = responseHeadersToUse;
totalLength = -1;
numRedirectsToFollow = numRedirectsToFollowToUse;
httpRequestCmd = httpRequestCmdToUse;
connection = createConnection(address, isPost, postData, headers, timeOutMs, httpRequestCmd);
}
public static final JuceHTTPStream createHTTPStream(String address, boolean isPost, byte[] postData,
String headers, int timeOutMs, int[] statusCode,
StringBuffer responseHeaders, int numRedirectsToFollow,
String httpRequestCmd)
{
// timeout parameter of zero for HttpUrlConnection is a blocking connect (negative value for juce::URL)
if (timeOutMs < 0)
timeOutMs = 0;
else if (timeOutMs == 0)
timeOutMs = 30000;
for (; ; )
{
try
{
JuceHTTPStream httpStream = new JuceHTTPStream(address, isPost, postData, headers,
timeOutMs, statusCode, responseHeaders,
numRedirectsToFollow, httpRequestCmd);
return httpStream;
} catch (Throwable e)
{
}
return null;
}
}
private final HttpURLConnection createConnection(String address, boolean isPost, byte[] postData,
String headers, int timeOutMs, String httpRequestCmdToUse) throws IOException
{
HttpURLConnection newConnection = (HttpURLConnection) (new URL(address).openConnection());
try
{
newConnection.setInstanceFollowRedirects(false);
newConnection.setConnectTimeout(timeOutMs);
newConnection.setReadTimeout(timeOutMs);
// headers - if not empty, this string is appended onto the headers that are used for the request. It must therefore be a valid set of HTML header directives, separated by newlines.
// So convert headers string to an array, with an element for each line
String headerLines[] = headers.split("\\n");
// Set request headers
for (int i = 0; i < headerLines.length; ++i)
{
int pos = headerLines[i].indexOf(":");
if (pos > 0 && pos < headerLines[i].length())
{
String field = headerLines[i].substring(0, pos);
String value = headerLines[i].substring(pos + 1);
if (value.length() > 0)
newConnection.setRequestProperty(field, value);
}
}
newConnection.setRequestMethod(httpRequestCmd);
if (isPost)
{
newConnection.setDoOutput(true);
if (postData != null)
{
OutputStream out = newConnection.getOutputStream();
out.write(postData);
out.flush();
}
}
return newConnection;
} catch (Throwable e)
{
newConnection.disconnect();
throw new IOException("Connection error");
}
}
private final InputStream getCancellableStream(final boolean isInput) throws ExecutionException
{
synchronized (createFutureLock)
{
if (hasBeenCancelled.get())
return null;
streamFuture = executor.submit(new Callable<BufferedInputStream>()
{
@Override
public BufferedInputStream call() throws IOException
{
return new BufferedInputStream(isInput ? connection.getInputStream()
: connection.getErrorStream());
}
});
}
try
{
return streamFuture.get();
} catch (InterruptedException e)
{
return null;
} catch (CancellationException e)
{
return null;
}
}
public final boolean connect()
{
boolean result = false;
int numFollowedRedirects = 0;
while (true)
{
result = doConnect();
if (!result)
return false;
if (++numFollowedRedirects > numRedirectsToFollow)
break;
int status = statusCode[0];
if (status == 301 || status == 302 || status == 303 || status == 307)
{
// Assumes only one occurrence of "Location"
int pos1 = responseHeaders.indexOf("Location:") + 10;
int pos2 = responseHeaders.indexOf("\n", pos1);
if (pos2 > pos1)
{
String currentLocation = connection.getURL().toString();
String newLocation = responseHeaders.substring(pos1, pos2);
try
{
// Handle newLocation whether it's absolute or relative
URL baseUrl = new URL(currentLocation);
URL newUrl = new URL(baseUrl, newLocation);
String transformedNewLocation = newUrl.toString();
if (transformedNewLocation != currentLocation)
{
// Clear responseHeaders before next iteration
responseHeaders.delete(0, responseHeaders.length());
synchronized (createStreamLock)
{
if (hasBeenCancelled.get())
return false;
connection.disconnect();
try
{
connection = createConnection(transformedNewLocation, isPost,
postData, headers, timeOutMs,
httpRequestCmd);
} catch (Throwable e)
{
return false;
}
}
} else
{
break;
}
} catch (Throwable e)
{
return false;
}
} else
{
break;
}
} else
{
break;
}
}
return result;
}
private final boolean doConnect()
{
synchronized (createStreamLock)
{
if (hasBeenCancelled.get())
return false;
try
{
try
{
inputStream = getCancellableStream(true);
} catch (ExecutionException e)
{
if (connection.getResponseCode() < 400)
{
statusCode[0] = connection.getResponseCode();
connection.disconnect();
return false;
}
} finally
{
statusCode[0] = connection.getResponseCode();
}
try
{
if (statusCode[0] >= 400)
inputStream = getCancellableStream(false);
else
inputStream = getCancellableStream(true);
} catch (ExecutionException e)
{
}
for (java.util.Map.Entry<String, java.util.List<String>> entry : connection.getHeaderFields().entrySet())
{
if (entry.getKey() != null && entry.getValue() != null)
{
responseHeaders.append(entry.getKey() + ": "
+ android.text.TextUtils.join(",", entry.getValue()) + "\n");
if (entry.getKey().compareTo("Content-Length") == 0)
totalLength = Integer.decode(entry.getValue().get(0));
}
}
return true;
} catch (IOException e)
{
return false;
}
}
}
static class DisconnectionRunnable implements Runnable
{
public DisconnectionRunnable(HttpURLConnection theConnection,
InputStream theInputStream,
ReentrantLock theCreateStreamLock,
Object theCreateFutureLock,
Future<BufferedInputStream> theStreamFuture)
{
connectionToDisconnect = theConnection;
inputStream = theInputStream;
createStreamLock = theCreateStreamLock;
createFutureLock = theCreateFutureLock;
streamFuture = theStreamFuture;
}
public void run()
{
try
{
if (!createStreamLock.tryLock())
{
synchronized (createFutureLock)
{
if (streamFuture != null)
streamFuture.cancel(true);
}
createStreamLock.lock();
}
if (connectionToDisconnect != null)
connectionToDisconnect.disconnect();
if (inputStream != null)
inputStream.close();
} catch (IOException e)
{
} finally
{
createStreamLock.unlock();
}
}
private HttpURLConnection connectionToDisconnect;
private InputStream inputStream;
private ReentrantLock createStreamLock;
private Object createFutureLock;
Future<BufferedInputStream> streamFuture;
}
public final void release()
{
DisconnectionRunnable disconnectionRunnable = new DisconnectionRunnable(connection,
inputStream,
createStreamLock,
createFutureLock,
streamFuture);
synchronized (createStreamLock)
{
hasBeenCancelled.set(true);
connection = null;
}
Thread disconnectionThread = new Thread(disconnectionRunnable);
disconnectionThread.start();
}
public final int read(byte[] buffer, int numBytes)
{
int num = 0;
try
{
synchronized (createStreamLock)
{
if (inputStream != null)
num = inputStream.read(buffer, 0, numBytes);
}
} catch (IOException e)
{
}
if (num > 0)
position += num;
return num;
}
public final long getPosition()
{
return position;
}
public final long getTotalLength()
{
return totalLength;
}
public final boolean isExhausted()
{
return false;
}
public final boolean setPosition(long newPos)
{
return false;
}
private boolean isPost;
private byte[] postData;
private String headers;
private int timeOutMs;
String httpRequestCmd;
private HttpURLConnection connection;
private int[] statusCode;
private StringBuffer responseHeaders;
private int totalLength;
private int numRedirectsToFollow;
private InputStream inputStream;
private long position;
private final ReentrantLock createStreamLock = new ReentrantLock();
private final Object createFutureLock = new Object();
private AtomicBoolean hasBeenCancelled = new AtomicBoolean();
private final ExecutorService executor = Executors.newCachedThreadPool(Executors.defaultThreadFactory());
Future<BufferedInputStream> streamFuture;
}

View File

@ -0,0 +1,60 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
package com.rmsl.juce;
import java.lang.reflect.*;
public class JuceInvocationHandler implements InvocationHandler
{
public JuceInvocationHandler (long nativeContextRef)
{
nativeContext = nativeContextRef;
}
public void clear()
{
nativeContext = 0;
}
@Override
public void finalize()
{
if (nativeContext != 0)
dispatchFinalize (nativeContext);
}
@Override
public Object invoke (Object proxy, Method method, Object[] args) throws Throwable
{
if (nativeContext != 0)
return dispatchInvoke (nativeContext, proxy, method, args);
return null;
}
//==============================================================================
private long nativeContext = 0;
private native void dispatchFinalize (long nativeContextRef);
private native Object dispatchInvoke (long nativeContextRef, Object proxy, Method method, Object[] args);
}

Some files were not shown because too many files have changed in this diff Show More