upgrade to JUCE 5.4.3. Remove (probably) unused JUCE modules. Remove VST2 target (it's been end-of-life'd by Steinberg and by JUCE)

This commit is contained in:
Alex Birch
2019-06-22 20:41:38 +01:00
parent d22c2cd4fa
commit 9ee566b251
1140 changed files with 67534 additions and 105952 deletions

View File

@ -30,8 +30,8 @@ AbstractFifo::AbstractFifo (int capacity) noexcept : bufferSize (capacity)
AbstractFifo::~AbstractFifo() {}
int AbstractFifo::getTotalSize() const noexcept { return bufferSize; }
int AbstractFifo::getFreeSpace() const noexcept { return bufferSize - getNumReady() - 1; }
int AbstractFifo::getTotalSize() const noexcept { return bufferSize; }
int AbstractFifo::getFreeSpace() const noexcept { return bufferSize - getNumReady() - 1; }
int AbstractFifo::getNumReady() const noexcept
{
@ -54,7 +54,8 @@ void AbstractFifo::setTotalSize (int newSize) noexcept
}
//==============================================================================
void AbstractFifo::prepareToWrite (int numToWrite, int& startIndex1, int& blockSize1, int& startIndex2, int& blockSize2) const noexcept
void AbstractFifo::prepareToWrite (int numToWrite, int& startIndex1, int& blockSize1,
int& startIndex2, int& blockSize2) const noexcept
{
auto vs = validStart.get();
auto ve = validEnd.get();
@ -91,7 +92,8 @@ void AbstractFifo::finishedWrite (int numWritten) noexcept
validEnd = newEnd;
}
void AbstractFifo::prepareToRead (int numWanted, int& startIndex1, int& blockSize1, int& startIndex2, int& blockSize2) const noexcept
void AbstractFifo::prepareToRead (int numWanted, int& startIndex1, int& blockSize1,
int& startIndex2, int& blockSize2) const noexcept
{
auto vs = validStart.get();
auto ve = validEnd.get();
@ -129,13 +131,6 @@ void AbstractFifo::finishedRead (int numRead) noexcept
}
//==============================================================================
template <AbstractFifo::ReadOrWrite mode>
AbstractFifo::ScopedReadWrite<mode>::ScopedReadWrite (AbstractFifo& f, int num) noexcept
: fifo (&f)
{
prepare (*fifo, num);
}
template <AbstractFifo::ReadOrWrite mode>
AbstractFifo::ScopedReadWrite<mode>::ScopedReadWrite (ScopedReadWrite&& other) noexcept
: startIndex1 (other.startIndex1),
@ -154,13 +149,6 @@ AbstractFifo::ScopedReadWrite<mode>::operator= (ScopedReadWrite&& other) noexcep
return *this;
}
template <AbstractFifo::ReadOrWrite mode>
AbstractFifo::ScopedReadWrite<mode>::~ScopedReadWrite() noexcept
{
if (fifo != nullptr)
finish (*fifo, blockSize1 + blockSize2);
}
template <AbstractFifo::ReadOrWrite mode>
void AbstractFifo::ScopedReadWrite<mode>::swap (ScopedReadWrite& other) noexcept
{
@ -171,33 +159,13 @@ void AbstractFifo::ScopedReadWrite<mode>::swap (ScopedReadWrite& other) noexcept
std::swap (other.blockSize2, blockSize2);
}
template<>
void AbstractFifo::ScopedReadWrite<AbstractFifo::ReadOrWrite::read>::prepare (AbstractFifo& f, int num) noexcept
{
f.prepareToRead (num, startIndex1, blockSize1, startIndex2, blockSize2);
}
template<>
void AbstractFifo::ScopedReadWrite<AbstractFifo::ReadOrWrite::write>::prepare (AbstractFifo& f, int num) noexcept
{
f.prepareToWrite (num, startIndex1, blockSize1, startIndex2, blockSize2);
}
template<>
void AbstractFifo::ScopedReadWrite<AbstractFifo::ReadOrWrite::read>::finish (AbstractFifo& f, int num) noexcept
{
f.finishedRead (num);
}
template<>
void AbstractFifo::ScopedReadWrite<AbstractFifo::ReadOrWrite::write>::finish (AbstractFifo& f, int num) noexcept
{
f.finishedWrite (num);
}
template class AbstractFifo::ScopedReadWrite<AbstractFifo::ReadOrWrite::read>;
template class AbstractFifo::ScopedReadWrite<AbstractFifo::ReadOrWrite::write>;
AbstractFifo::ScopedRead AbstractFifo::read (int numToRead) noexcept { return { *this, numToRead }; }
AbstractFifo::ScopedWrite AbstractFifo::write (int numToWrite) noexcept { return { *this, numToWrite }; }
//==============================================================================
#if JUCE_UNIT_TESTS

View File

@ -38,13 +38,8 @@ namespace juce
e.g.
@code
class MyFifo
struct MyFifo
{
public:
MyFifo() : abstractFifo (1024)
{
}
void addToFifo (const int* someData, int numItems)
{
int start1, size1, start2, size2;
@ -73,9 +68,8 @@ namespace juce
abstractFifo.finishedRead (size1 + size2);
}
private:
AbstractFifo abstractFifo;
int myBuffer [1024];
AbstractFifo abstractFifo { 1024 };
int myBuffer[1024];
};
@endcode
@ -225,7 +219,10 @@ public:
This object will hold a pointer back to the fifo, so make sure that
the fifo outlives this object.
*/
ScopedReadWrite (AbstractFifo&, int num) noexcept;
ScopedReadWrite (AbstractFifo& f, int num) noexcept : fifo (&f)
{
prepare (*fifo, num);
}
ScopedReadWrite (const ScopedReadWrite&) = delete;
ScopedReadWrite (ScopedReadWrite&&) noexcept;
@ -236,7 +233,11 @@ public:
/** Calls finishedRead or finishedWrite if this is a non-null scoped
reader/writer.
*/
~ScopedReadWrite() noexcept;
~ScopedReadWrite() noexcept
{
if (fifo != nullptr)
finish (*fifo, blockSize1 + blockSize2);
}
/** Calls the passed function with each index that was deemed valid
for the current read/write operation.
@ -281,7 +282,7 @@ public:
} // readHandle goes out of scope here, finishing the read operation
@endcode
*/
ScopedRead read (int numToRead) noexcept { return { *this, numToRead }; }
ScopedRead read (int numToRead) noexcept;
/** Replaces prepareToWrite/finishedWrite with a single function.
This function returns an object which contains the start indices and
@ -303,7 +304,7 @@ public:
} // writeHandle goes out of scope here, finishing the write operation
@endcode
*/
ScopedWrite write (int numToWrite) noexcept { return { *this, numToWrite }; }
ScopedWrite write (int numToWrite) noexcept;
private:
//==============================================================================
@ -313,4 +314,29 @@ private:
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AbstractFifo)
};
template<>
inline void AbstractFifo::ScopedReadWrite<AbstractFifo::ReadOrWrite::read>::finish (AbstractFifo& f, int num) noexcept
{
f.finishedRead (num);
}
template<>
inline void AbstractFifo::ScopedReadWrite<AbstractFifo::ReadOrWrite::write>::finish (AbstractFifo& f, int num) noexcept
{
f.finishedWrite (num);
}
template<>
inline void AbstractFifo::ScopedReadWrite<AbstractFifo::ReadOrWrite::read>::prepare (AbstractFifo& f, int num) noexcept
{
f.prepareToRead (num, startIndex1, blockSize1, startIndex2, blockSize2);
}
template<>
inline void AbstractFifo::ScopedReadWrite<AbstractFifo::ReadOrWrite::write>::prepare (AbstractFifo& f, int num) noexcept
{
f.prepareToWrite (num, startIndex1, blockSize1, startIndex2, blockSize2);
}
} // namespace juce

View File

@ -60,51 +60,40 @@ private:
public:
//==============================================================================
/** Creates an empty array. */
Array() noexcept
{
}
Array() = default;
/** Creates a copy of another array.
@param other the array to copy
*/
Array (const Array<ElementType, TypeOfCriticalSectionToUse>& other)
Array (const Array& other)
{
const ScopedLockType lock (other.getLock());
numUsed = other.numUsed;
data.setAllocatedSize (other.numUsed);
for (int i = 0; i < numUsed; ++i)
new (data.elements + i) ElementType (other.data.elements[i]);
values.addArray (other.values.begin(), other.values.size());
}
Array (Array<ElementType, TypeOfCriticalSectionToUse>&& other) noexcept
: data (static_cast<ArrayAllocationBase<ElementType, TypeOfCriticalSectionToUse>&&> (other.data)),
numUsed (other.numUsed)
Array (Array&& other) noexcept
: values (std::move (other.values))
{
other.numUsed = 0;
}
/** Initalises from a null-terminated raw array of values.
@param values the array to copy from
@param data the data to copy from
*/
template <typename TypeToCreateFrom>
explicit Array (const TypeToCreateFrom* values)
explicit Array (const TypeToCreateFrom* data)
{
while (*values != TypeToCreateFrom())
add (*values++);
add (*data++);
}
/** Initalises from a raw array of values.
@param values the array to copy from
@param data the data to copy from
@param numValues the number of values in the array
*/
template <typename TypeToCreateFrom>
Array (const TypeToCreateFrom* values, int numValues) : numUsed (numValues)
Array (const TypeToCreateFrom* data, int numValues)
{
data.setAllocatedSize (numValues);
for (int i = 0; i < numValues; ++i)
new (data.elements + i) ElementType (values[i]);
values.addArray (data, numValues);
}
/** Initalises an Array of size 1 containing a single element. */
@ -116,23 +105,21 @@ public:
/** Initalises an Array of size 1 containing a single element. */
Array (ElementType&& singleElementToAdd)
{
add (static_cast<ElementType&&> (singleElementToAdd));
add (std::move (singleElementToAdd));
}
/** Initalises an Array from a list of items. */
template <typename... OtherElements>
Array (const ElementType& firstNewElement, OtherElements... otherElements)
{
data.setAllocatedSize (1 + (int) sizeof... (otherElements));
addAssumingCapacityIsReady (firstNewElement, otherElements...);
values.add (firstNewElement, otherElements...);
}
/** Initalises an Array from a list of items. */
template <typename... OtherElements>
Array (ElementType&& firstNewElement, OtherElements... otherElements)
{
data.setAllocatedSize (1 + (int) sizeof... (otherElements));
addAssumingCapacityIsReady (static_cast<ElementType&&> (firstNewElement), otherElements...);
values.add (std::move (firstNewElement), otherElements...);
}
template <typename TypeToCreateFrom>
@ -142,10 +129,7 @@ public:
}
/** Destructor. */
~Array()
{
deleteAllElements();
}
~Array() = default;
/** Copies another array.
@param other the array to copy
@ -164,10 +148,7 @@ public:
Array& operator= (Array&& other) noexcept
{
const ScopedLockType lock (getLock());
deleteAllElements();
data = static_cast<ArrayAllocationBase<ElementType, TypeOfCriticalSectionToUse>&&> (other.data);
numUsed = other.numUsed;
other.numUsed = 0;
values = std::move (other.values);
return *this;
}
@ -182,15 +163,7 @@ public:
{
const ScopedLockType lock (getLock());
const typename OtherArrayType::ScopedLockType lock2 (other.getLock());
if (numUsed != other.numUsed)
return false;
for (int i = numUsed; --i >= 0;)
if (! (data.elements[i] == other.data.elements[i]))
return false;
return true;
return values == other;
}
/** Compares this array to another one.
@ -215,9 +188,8 @@ public:
void clear()
{
const ScopedLockType lock (getLock());
deleteAllElements();
data.setAllocatedSize (0);
numUsed = 0;
clearQuick();
values.setAllocatedSize (0);
}
/** Removes all elements from the array without freeing the array's allocated storage.
@ -226,8 +198,7 @@ public:
void clearQuick()
{
const ScopedLockType lock (getLock());
deleteAllElements();
numUsed = 0;
values.clear();
}
/** Fills the Array with the provided value. */
@ -243,13 +214,14 @@ public:
/** Returns the current number of elements in the array. */
inline int size() const noexcept
{
return numUsed;
const ScopedLockType lock (getLock());
return values.size();
}
/** Returns true if the array is empty, false otherwise. */
inline bool isEmpty() const noexcept
{
return numUsed == 0;
return size() == 0;
}
/** Returns one of the elements in the array.
@ -262,17 +234,10 @@ public:
@param index the index of the element being requested (0 is the first element in the array)
@see getUnchecked, getFirst, getLast
*/
ElementType operator[] (const int index) const
ElementType operator[] (int index) const
{
const ScopedLockType lock (getLock());
if (isPositiveAndBelow (index, numUsed))
{
jassert (data.elements != nullptr);
return data.elements[index];
}
return ElementType();
return values.getValueWithDefault (index);
}
/** Returns one of the elements in the array, without checking the index passed in.
@ -284,11 +249,10 @@ public:
@param index the index of the element being requested (0 is the first element in the array)
@see operator[], getFirst, getLast
*/
inline ElementType getUnchecked (const int index) const
inline ElementType getUnchecked (int index) const
{
const ScopedLockType lock (getLock());
jassert (isPositiveAndBelow (index, numUsed) && data.elements != nullptr);
return data.elements[index];
return values[index];
}
/** Returns a direct reference to one of the elements in the array, without checking the index passed in.
@ -300,44 +264,29 @@ public:
@param index the index of the element being requested (0 is the first element in the array)
@see operator[], getFirst, getLast
*/
inline ElementType& getReference (const int index) const noexcept
inline ElementType& getReference (int index) const noexcept
{
const ScopedLockType lock (getLock());
jassert (isPositiveAndBelow (index, numUsed) && data.elements != nullptr);
return data.elements[index];
return values[index];
}
/** Returns the first element in the array, or a default value if the array is empty.
@see operator[], getUnchecked, getLast
*/
inline ElementType getFirst() const
inline ElementType getFirst() const noexcept
{
const ScopedLockType lock (getLock());
if (numUsed > 0)
{
jassert (data.elements != nullptr);
return data.elements[0];
}
return ElementType();
return values.getFirst();
}
/** Returns the last element in the array, or a default value if the array is empty.
@see operator[], getUnchecked, getFirst
*/
inline ElementType getLast() const
inline ElementType getLast() const noexcept
{
const ScopedLockType lock (getLock());
if (numUsed > 0)
{
jassert (data.elements != nullptr);
return data.elements[numUsed - 1];
}
return ElementType();
return values.getLast();
}
/** Returns a pointer to the actual array data.
@ -346,7 +295,7 @@ public:
*/
inline ElementType* getRawDataPointer() noexcept
{
return data.elements;
return values.begin();
}
//==============================================================================
@ -355,7 +304,7 @@ public:
*/
inline ElementType* begin() const noexcept
{
return data.elements;
return values.begin();
}
/** Returns a pointer to the element which follows the last element in the array.
@ -363,12 +312,15 @@ public:
*/
inline ElementType* end() const noexcept
{
#if JUCE_DEBUG
if (data.elements == nullptr || numUsed <= 0) // (to keep static analysers happy)
return data.elements;
#endif
return values.end();
}
return data.elements + numUsed;
/** Returns a pointer to the first element in the array.
This method is provided for compatibility with the standard C++ containers.
*/
inline ElementType* data() const noexcept
{
return begin();
}
//==============================================================================
@ -383,12 +335,12 @@ public:
int indexOf (ParameterType elementToLookFor) const
{
const ScopedLockType lock (getLock());
auto e = data.elements.get();
auto endPtr = e + numUsed;
auto e = values.begin();
auto endPtr = values.end();
for (; e != endPtr; ++e)
if (elementToLookFor == *e)
return static_cast<int> (e - data.elements.get());
return static_cast<int> (e - values.begin());
return -1;
}
@ -401,8 +353,8 @@ public:
bool contains (ParameterType elementToLookFor) const
{
const ScopedLockType lock (getLock());
auto e = data.elements.get();
auto endPtr = e + numUsed;
auto e = values.begin();
auto endPtr = values.end();
for (; e != endPtr; ++e)
if (elementToLookFor == *e)
@ -419,8 +371,7 @@ public:
void add (const ElementType& newElement)
{
const ScopedLockType lock (getLock());
data.ensureAllocatedSize (numUsed + 1);
new (data.elements + numUsed++) ElementType (newElement);
values.add (newElement);
}
/** Appends a new element at the end of the array.
@ -430,8 +381,7 @@ public:
void add (ElementType&& newElement)
{
const ScopedLockType lock (getLock());
data.ensureAllocatedSize (numUsed + 1);
new (data.elements + numUsed++) ElementType (static_cast<ElementType&&> (newElement));
values.add (std::move (newElement));
}
/** Appends multiple new elements at the end of the array. */
@ -439,8 +389,7 @@ public:
void add (const ElementType& firstNewElement, OtherElements... otherElements)
{
const ScopedLockType lock (getLock());
data.ensureAllocatedSize (numUsed + 1 + (int) sizeof... (otherElements));
addAssumingCapacityIsReady (firstNewElement, otherElements...);
values.add (firstNewElement, otherElements...);
}
/** Appends multiple new elements at the end of the array. */
@ -448,8 +397,7 @@ public:
void add (ElementType&& firstNewElement, OtherElements... otherElements)
{
const ScopedLockType lock (getLock());
data.ensureAllocatedSize (numUsed + 1 + (int) sizeof... (otherElements));
addAssumingCapacityIsReady (static_cast<ElementType&&> (firstNewElement), otherElements...);
values.add (std::move (firstNewElement), otherElements...);
}
/** Inserts a new element into the array at a given position.
@ -467,24 +415,7 @@ public:
void insert (int indexToInsertAt, ParameterType newElement)
{
const ScopedLockType lock (getLock());
data.ensureAllocatedSize (numUsed + 1);
jassert (data.elements != nullptr);
if (isPositiveAndBelow (indexToInsertAt, numUsed))
{
auto* insertPos = data.elements + indexToInsertAt;
auto numberToMove = numUsed - indexToInsertAt;
if (numberToMove > 0)
memmove (insertPos + 1, insertPos, ((size_t) numberToMove) * sizeof (ElementType));
new (insertPos) ElementType (newElement);
++numUsed;
}
else
{
new (data.elements + numUsed++) ElementType (newElement);
}
values.insert (indexToInsertAt, newElement, 1);
}
/** Inserts multiple copies of an element into the array at a given position.
@ -505,28 +436,7 @@ public:
if (numberOfTimesToInsertIt > 0)
{
const ScopedLockType lock (getLock());
data.ensureAllocatedSize (numUsed + numberOfTimesToInsertIt);
ElementType* insertPos;
if (isPositiveAndBelow (indexToInsertAt, numUsed))
{
insertPos = data.elements + indexToInsertAt;
auto numberToMove = numUsed - indexToInsertAt;
memmove (insertPos + numberOfTimesToInsertIt, insertPos, ((size_t) numberToMove) * sizeof (ElementType));
}
else
{
insertPos = data.elements + numUsed;
}
numUsed += numberOfTimesToInsertIt;
while (--numberOfTimesToInsertIt >= 0)
{
new (insertPos) ElementType (newElement);
++insertPos; // NB: this increment is done separately from the
// new statement to avoid a compiler bug in VS2014
}
values.insert (indexToInsertAt, newElement, numberOfTimesToInsertIt);
}
}
@ -549,24 +459,7 @@ public:
if (numberOfElements > 0)
{
const ScopedLockType lock (getLock());
data.ensureAllocatedSize (numUsed + numberOfElements);
ElementType* insertPos = data.elements;
if (isPositiveAndBelow (indexToInsertAt, numUsed))
{
insertPos += indexToInsertAt;
auto numberToMove = numUsed - indexToInsertAt;
memmove (insertPos + numberOfElements, insertPos, (size_t) numberToMove * sizeof (ElementType));
}
else
{
insertPos += numUsed;
}
numUsed += numberOfElements;
while (--numberOfElements >= 0)
new (insertPos++) ElementType (*newElements++);
values.insertArray (indexToInsertAt, newElements, numberOfElements);
}
}
@ -599,20 +492,20 @@ public:
@param newValue the new value to set for this index.
@see add, insert
*/
void set (const int indexToChange, ParameterType newValue)
void set (int indexToChange, ParameterType newValue)
{
jassert (indexToChange >= 0);
const ScopedLockType lock (getLock());
if (indexToChange >= 0)
{
const ScopedLockType lock (getLock());
if (isPositiveAndBelow (indexToChange, numUsed))
{
jassert (data.elements != nullptr);
data.elements[indexToChange] = newValue;
if (indexToChange < values.size())
values[indexToChange] = newValue;
else
values.add (newValue);
}
else if (indexToChange >= 0)
else
{
data.ensureAllocatedSize (numUsed + 1);
new (data.elements + numUsed++) ElementType (newValue);
jassertfalse;
}
}
@ -625,11 +518,11 @@ public:
@param newValue the new value to set for this index.
@see set, getUnchecked
*/
void setUnchecked (const int indexToChange, ParameterType newValue)
void setUnchecked (int indexToChange, ParameterType newValue)
{
const ScopedLockType lock (getLock());
jassert (isPositiveAndBelow (indexToChange, numUsed));
data.elements[indexToChange] = newValue;
jassert (isPositiveAndBelow (indexToChange, values.size()));
values[indexToChange] = newValue;
}
/** Adds elements from an array to the end of this array.
@ -645,28 +538,14 @@ public:
const ScopedLockType lock (getLock());
if (numElementsToAdd > 0)
{
data.ensureAllocatedSize (numUsed + numElementsToAdd);
while (--numElementsToAdd >= 0)
{
new (data.elements + numUsed) ElementType (*elementsToAdd++);
++numUsed;
}
}
values.addArray (elementsToAdd, numElementsToAdd);
}
template <typename TypeToCreateFrom>
void addArray (const std::initializer_list<TypeToCreateFrom>& items)
{
const ScopedLockType lock (getLock());
data.ensureAllocatedSize (numUsed + (int) items.size());
for (auto& item : items)
{
new (data.elements + numUsed) ElementType (item);
++numUsed;
}
values.addArray (items);
}
/** Adds elements from a null-terminated array of pointers to the end of this array.
@ -696,8 +575,21 @@ public:
{
const ScopedLockType lock1 (getLock());
const typename OtherArrayType::ScopedLockType lock2 (otherArray.getLock());
data.swapWith (otherArray.data);
std::swap (numUsed, otherArray.numUsed);
values.swapWith (otherArray.values);
}
/** Adds elements from another array to the end of this array.
@param arrayToAddFrom the array from which to copy the elements
@see add
*/
template <class OtherArrayType>
void addArray (const OtherArrayType& arrayToAddFrom)
{
const typename OtherArrayType::ScopedLockType lock1 (arrayToAddFrom.getLock());
const ScopedLockType lock2 (getLock());
values.addArray (arrayToAddFrom);
}
/** Adds elements from another array to the end of this array.
@ -710,29 +602,15 @@ public:
@see add
*/
template <class OtherArrayType>
void addArray (const OtherArrayType& arrayToAddFrom,
int startIndex = 0,
int numElementsToAdd = -1)
typename std::enable_if<! std::is_pointer<OtherArrayType>::value, void>::type
addArray (const OtherArrayType& arrayToAddFrom,
int startIndex,
int numElementsToAdd = -1)
{
const typename OtherArrayType::ScopedLockType lock1 (arrayToAddFrom.getLock());
const ScopedLockType lock2 (getLock());
{
const ScopedLockType lock2 (getLock());
if (startIndex < 0)
{
jassertfalse;
startIndex = 0;
}
if (numElementsToAdd < 0 || startIndex + numElementsToAdd > arrayToAddFrom.size())
numElementsToAdd = arrayToAddFrom.size() - startIndex;
data.ensureAllocatedSize (numUsed + numElementsToAdd);
while (--numElementsToAdd >= 0)
addAssumingCapacityIsReady (arrayToAddFrom.getUnchecked (startIndex++));
}
values.addArray (arrayToAddFrom, startIndex, numElementsToAdd);
}
/** This will enlarge or shrink the array to the given number of elements, by adding
@ -742,13 +620,13 @@ public:
until its size is as specified. If its size is larger than the target, items will be
removed from its end to shorten it.
*/
void resize (const int targetNumItems)
void resize (int targetNumItems)
{
jassert (targetNumItems >= 0);
auto numToAdd = targetNumItems - numUsed;
auto numToAdd = targetNumItems - values.size();
if (numToAdd > 0)
insertMultiple (numUsed, ElementType(), numToAdd);
insertMultiple (values.size(), ElementType(), numToAdd);
else if (numToAdd < 0)
removeRange (targetNumItems, -numToAdd);
}
@ -769,7 +647,7 @@ public:
int addSorted (ElementComparator& comparator, ParameterType newElement)
{
const ScopedLockType lock (getLock());
auto index = findInsertIndexInSortedArray (comparator, data.elements.get(), newElement, 0, numUsed);
auto index = findInsertIndexInSortedArray (comparator, values.begin(), newElement, 0, values.size());
insert (index, newElement);
return index;
}
@ -809,12 +687,12 @@ public:
const ScopedLockType lock (getLock());
for (int s = 0, e = numUsed;;)
for (int s = 0, e = values.size();;)
{
if (s >= e)
return -1;
if (comparator.compareElements (elementToLookFor, data.elements[s]) == 0)
if (comparator.compareElements (elementToLookFor, values[s]) == 0)
return s;
auto halfway = (s + e) / 2;
@ -822,7 +700,7 @@ public:
if (halfway == s)
return -1;
if (comparator.compareElements (elementToLookFor, data.elements[halfway]) >= 0)
if (comparator.compareElements (elementToLookFor, values[halfway]) >= 0)
s = halfway;
else
e = halfway;
@ -843,11 +721,8 @@ public:
{
const ScopedLockType lock (getLock());
if (isPositiveAndBelow (indexToRemove, numUsed))
{
jassert (data.elements != nullptr);
if (isPositiveAndBelow (indexToRemove, values.size()))
removeInternal (indexToRemove);
}
}
/** Removes an element from the array.
@ -864,15 +739,14 @@ public:
{
const ScopedLockType lock (getLock());
if (isPositiveAndBelow (indexToRemove, numUsed))
if (isPositiveAndBelow (indexToRemove, values.size()))
{
jassert (data.elements != nullptr);
ElementType removed (data.elements[indexToRemove]);
ElementType removed (values[indexToRemove]);
removeInternal (indexToRemove);
return removed;
}
return {};
return ElementType();
}
/** Removes an element from the array.
@ -890,10 +764,10 @@ public:
jassert (elementToRemove != nullptr);
const ScopedLockType lock (getLock());
jassert (data.elements != nullptr);
auto indexToRemove = (int) (elementToRemove - data.elements);
jassert (values.begin() != nullptr);
auto indexToRemove = (int) (elementToRemove - values.begin());
if (! isPositiveAndBelow (indexToRemove, numUsed))
if (! isPositiveAndBelow (indexToRemove, values.size()))
{
jassertfalse;
return;
@ -913,9 +787,9 @@ public:
void removeFirstMatchingValue (ParameterType valueToRemove)
{
const ScopedLockType lock (getLock());
auto* e = data.elements.get();
auto* e = values.begin();
for (int i = 0; i < numUsed; ++i)
for (int i = 0; i < values.size(); ++i)
{
if (valueToRemove == e[i])
{
@ -939,9 +813,9 @@ public:
int numRemoved = 0;
const ScopedLockType lock (getLock());
for (int i = numUsed; --i >= 0;)
for (int i = values.size(); --i >= 0;)
{
if (valueToRemove == data.elements[i])
if (valueToRemove == values[i])
{
removeInternal (i);
++numRemoved;
@ -963,14 +837,14 @@ public:
@see remove, removeRange, removeAllInstancesOf
*/
template <typename PredicateType>
int removeIf (PredicateType predicate)
int removeIf (PredicateType&& predicate)
{
int numRemoved = 0;
const ScopedLockType lock (getLock());
for (int i = numUsed; --i >= 0;)
for (int i = values.size(); --i >= 0;)
{
if (predicate (data.elements[i]))
if (predicate (values[i]))
{
removeInternal (i);
++numRemoved;
@ -995,23 +869,14 @@ public:
void removeRange (int startIndex, int numberToRemove)
{
const ScopedLockType lock (getLock());
auto endIndex = jlimit (0, numUsed, startIndex + numberToRemove);
startIndex = jlimit (0, numUsed, startIndex);
if (endIndex > startIndex)
auto endIndex = jlimit (0, values.size(), startIndex + numberToRemove);
startIndex = jlimit (0, values.size(), startIndex);
numberToRemove = endIndex - startIndex;
if (numberToRemove > 0)
{
auto* e = data.elements + startIndex;
numberToRemove = endIndex - startIndex;
for (int i = 0; i < numberToRemove; ++i)
e[i].~ElementType();
auto numToShift = numUsed - endIndex;
if (numToShift > 0)
memmove (e, e + numberToRemove, ((size_t) numToShift) * sizeof (ElementType));
numUsed -= numberToRemove;
values.removeElements (startIndex, numberToRemove);
minimiseStorageAfterRemoval();
}
}
@ -1023,16 +888,18 @@ public:
*/
void removeLast (int howManyToRemove = 1)
{
const ScopedLockType lock (getLock());
jassert (howManyToRemove >= 0);
if (howManyToRemove > numUsed)
howManyToRemove = numUsed;
if (howManyToRemove > 0)
{
const ScopedLockType lock (getLock());
for (int i = 1; i <= howManyToRemove; ++i)
data.elements[numUsed - i].~ElementType();
if (howManyToRemove > values.size())
howManyToRemove = values.size();
numUsed -= howManyToRemove;
minimiseStorageAfterRemoval();
values.removeElements (values.size() - howManyToRemove, howManyToRemove);
minimiseStorageAfterRemoval();
}
}
/** Removes any elements which are also in another array.
@ -1054,8 +921,8 @@ public:
{
if (otherArray.size() > 0)
{
for (int i = numUsed; --i >= 0;)
if (otherArray.contains (data.elements[i]))
for (int i = values.size(); --i >= 0;)
if (otherArray.contains (values[i]))
removeInternal (i);
}
}
@ -1082,8 +949,8 @@ public:
}
else
{
for (int i = numUsed; --i >= 0;)
if (! otherArray.contains (data.elements[i]))
for (int i = values.size(); --i >= 0;)
if (! otherArray.contains (values[i]))
removeInternal (i);
}
}
@ -1100,13 +967,7 @@ public:
void swap (int index1, int index2)
{
const ScopedLockType lock (getLock());
if (isPositiveAndBelow (index1, numUsed)
&& isPositiveAndBelow (index2, numUsed))
{
std::swap (data.elements[index1],
data.elements[index2]);
}
values.swap (index1, index2);
}
/** Moves one of the values to a different position.
@ -1128,30 +989,7 @@ public:
if (currentIndex != newIndex)
{
const ScopedLockType lock (getLock());
if (isPositiveAndBelow (currentIndex, numUsed))
{
if (! isPositiveAndBelow (newIndex, numUsed))
newIndex = numUsed - 1;
char tempCopy[sizeof (ElementType)];
memcpy (tempCopy, data.elements + currentIndex, sizeof (ElementType));
if (newIndex > currentIndex)
{
memmove (data.elements + currentIndex,
data.elements + currentIndex + 1,
sizeof (ElementType) * (size_t) (newIndex - currentIndex));
}
else
{
memmove (data.elements + newIndex + 1,
data.elements + newIndex,
sizeof (ElementType) * (size_t) (currentIndex - newIndex));
}
memcpy (data.elements + newIndex, tempCopy, sizeof (ElementType));
}
values.move (currentIndex, newIndex);
}
}
@ -1165,7 +1003,7 @@ public:
void minimiseStorageOverheads()
{
const ScopedLockType lock (getLock());
data.shrinkToNoMoreThan (numUsed);
values.shrinkToNoMoreThan (values.size());
}
/** Increases the array's internal storage to hold a minimum number of elements.
@ -1174,10 +1012,10 @@ public:
the array won't have to keep dynamically resizing itself as the elements
are added, and it'll therefore be more efficient.
*/
void ensureStorageAllocated (const int minNumElements)
void ensureStorageAllocated (int minNumElements)
{
const ScopedLockType lock (getLock());
data.ensureAllocatedSize (minNumElements);
values.ensureAllocatedSize (minNumElements);
}
//==============================================================================
@ -1219,12 +1057,12 @@ public:
*/
template <class ElementComparator>
void sort (ElementComparator& comparator,
const bool retainOrderOfEquivalentItems = false)
bool retainOrderOfEquivalentItems = false)
{
const ScopedLockType lock (getLock());
ignoreUnused (comparator); // if you pass in an object with a static compareElements() method, this
// avoids getting warning messages about the parameter being unused
sortArray (comparator, data.elements.get(), 0, size() - 1, retainOrderOfEquivalentItems);
sortArray (comparator, values.begin(), 0, size() - 1, retainOrderOfEquivalentItems);
}
//==============================================================================
@ -1232,7 +1070,7 @@ public:
To lock, you can call getLock().enter() and getLock().exit(), or preferably use
an object of ScopedLockType as an RAII lock for it.
*/
inline const TypeOfCriticalSectionToUse& getLock() const noexcept { return data; }
inline const TypeOfCriticalSectionToUse& getLock() const noexcept { return values; }
/** Returns the type of scoped lock to use for locking this array */
using ScopedLockType = typename TypeOfCriticalSectionToUse::ScopedLockType;
@ -1247,49 +1085,18 @@ public:
private:
//==============================================================================
ArrayAllocationBase <ElementType, TypeOfCriticalSectionToUse> data;
int numUsed = 0;
ArrayBase<ElementType, TypeOfCriticalSectionToUse> values;
void removeInternal (const int indexToRemove)
void removeInternal (int indexToRemove)
{
--numUsed;
auto* e = data.elements + indexToRemove;
e->~ElementType();
auto numberToShift = numUsed - indexToRemove;
if (numberToShift > 0)
memmove (e, e + 1, ((size_t) numberToShift) * sizeof (ElementType));
values.removeElements (indexToRemove, 1);
minimiseStorageAfterRemoval();
}
inline void deleteAllElements() noexcept
{
for (int i = 0; i < numUsed; ++i)
data.elements[i].~ElementType();
}
void minimiseStorageAfterRemoval()
{
if (data.numAllocated > jmax (minimumAllocatedSize, numUsed * 2))
data.shrinkToNoMoreThan (jmax (numUsed, jmax (minimumAllocatedSize, 64 / (int) sizeof (ElementType))));
}
void addAssumingCapacityIsReady (const ElementType& e) { new (data.elements + numUsed++) ElementType (e); }
void addAssumingCapacityIsReady (ElementType&& e) { new (data.elements + numUsed++) ElementType (static_cast<ElementType&&> (e)); }
template <typename... OtherElements>
void addAssumingCapacityIsReady (const ElementType& firstNewElement, OtherElements... otherElements)
{
addAssumingCapacityIsReady (firstNewElement);
addAssumingCapacityIsReady (otherElements...);
}
template <typename... OtherElements>
void addAssumingCapacityIsReady (ElementType&& firstNewElement, OtherElements... otherElements)
{
addAssumingCapacityIsReady (static_cast<ElementType&&> (firstNewElement));
addAssumingCapacityIsReady (otherElements...);
if (values.capacity() > jmax (minimumAllocatedSize, values.size() * 2))
values.shrinkToNoMoreThan (jmax (values.size(), jmax (minimumAllocatedSize, 64 / (int) sizeof (ElementType))));
}
};

View File

@ -27,13 +27,9 @@ namespace juce
/**
Implements some basic array storage allocation functions.
This class isn't really for public use - it's used by the other
array classes, but might come in handy for some purposes.
It inherits from a critical section class to allow the arrays to use
the "empty base class optimisation" pattern to reduce their footprint.
@see Array, OwnedArray, ReferenceCountedArray
This class isn't really for public use - it used to be part of the
container classes but has since been superseded by ArrayBase. Eventually
it will be removed from the API.
@tags{Core}
*/
@ -43,24 +39,20 @@ class ArrayAllocationBase : public TypeOfCriticalSectionToUse
public:
//==============================================================================
/** Creates an empty array. */
ArrayAllocationBase() noexcept
{
}
ArrayAllocationBase() = default;
/** Destructor. */
~ArrayAllocationBase() noexcept
{
}
~ArrayAllocationBase() = default;
ArrayAllocationBase (ArrayAllocationBase&& other) noexcept
: elements (static_cast<HeapBlock<ElementType>&&> (other.elements)),
: elements (std::move (other.elements)),
numAllocated (other.numAllocated)
{
}
ArrayAllocationBase& operator= (ArrayAllocationBase&& other) noexcept
{
elements = static_cast<HeapBlock<ElementType>&&> (other.elements);
elements = std::move (other.elements);
numAllocated = other.numAllocated;
return *this;
}

View File

@ -0,0 +1,600 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2018 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
#if JUCE_UNIT_TESTS
namespace ArrayBaseTestsHelpers
{
class TriviallyCopyableType
{
public:
TriviallyCopyableType() = default;
TriviallyCopyableType (int v)
: value (v)
{}
TriviallyCopyableType (float v)
: value ((int) v)
{}
bool operator== (const TriviallyCopyableType& other) const
{
return getValue() == other.getValue();
}
int getValue() const { return value; }
private:
int value { -1111 };
};
class NonTriviallyCopyableType
{
public:
NonTriviallyCopyableType() = default;
NonTriviallyCopyableType (int v)
: value (v)
{}
NonTriviallyCopyableType (float v)
: value ((int) v)
{}
NonTriviallyCopyableType (const NonTriviallyCopyableType& other)
: value (other.value)
{}
NonTriviallyCopyableType& operator= (const NonTriviallyCopyableType& other)
{
value = other.value;
return *this;
}
bool operator== (const NonTriviallyCopyableType& other) const
{
return getValue() == other.getValue();
}
int getValue() const { return *ptr; }
private:
int value { -1111 };
int* ptr = &value;
};
}
bool operator== (const ArrayBaseTestsHelpers::TriviallyCopyableType& tct,
const ArrayBaseTestsHelpers::NonTriviallyCopyableType& ntct)
{
return tct.getValue() == ntct.getValue();
}
bool operator== (const ArrayBaseTestsHelpers::NonTriviallyCopyableType& ntct,
const ArrayBaseTestsHelpers::TriviallyCopyableType& tct)
{
return tct == ntct;
}
class ArrayBaseTests : public UnitTest
{
using CopyableType = ArrayBaseTestsHelpers::TriviallyCopyableType;
using NoncopyableType = ArrayBaseTestsHelpers::NonTriviallyCopyableType;
#if ! (defined(__GNUC__) && __GNUC__ < 5 && ! defined(__clang__))
static_assert (std::is_trivially_copyable<CopyableType>::value,
"Test TriviallyCopyableType is not trivially copyable");
static_assert (! std::is_trivially_copyable<NoncopyableType>::value,
"Test NonTriviallyCopyableType is trivially copyable");
#endif
public:
ArrayBaseTests()
: UnitTest ("ArrayBase", "Containers")
{}
void runTest() override
{
beginTest ("grow capacity");
{
std::vector<CopyableType> referenceContainer;
ArrayBase<CopyableType, DummyCriticalSection> copyableContainer;
ArrayBase<NoncopyableType, DummyCriticalSection> noncopyableContainer;
checkEqual (copyableContainer, noncopyableContainer, referenceContainer);
int originalCapacity = 4;
referenceContainer.reserve ((size_t) originalCapacity);
expectEquals ((int) referenceContainer.capacity(), originalCapacity);
copyableContainer.setAllocatedSize (originalCapacity);
expectEquals (copyableContainer.capacity(), originalCapacity);
noncopyableContainer.setAllocatedSize (originalCapacity);
expectEquals (noncopyableContainer.capacity(), originalCapacity);
checkEqual (copyableContainer, noncopyableContainer, referenceContainer);
addData (referenceContainer, copyableContainer, noncopyableContainer, 33);
checkEqual (copyableContainer, noncopyableContainer, referenceContainer);
expect ((int) referenceContainer.capacity() != originalCapacity);
expect (copyableContainer.capacity() != originalCapacity);
expect (noncopyableContainer.capacity() != originalCapacity);
}
beginTest ("shrink capacity");
{
std::vector<CopyableType> referenceContainer;
ArrayBase<CopyableType, DummyCriticalSection> copyableContainer;
ArrayBase<NoncopyableType, DummyCriticalSection> noncopyableContainer;
int numElements = 45;
addData (referenceContainer, copyableContainer, noncopyableContainer, numElements);
copyableContainer.shrinkToNoMoreThan (numElements);
noncopyableContainer.setAllocatedSize (numElements + 1);
checkEqual (copyableContainer, noncopyableContainer, referenceContainer);
referenceContainer.clear();
copyableContainer.removeElements (0, numElements);
noncopyableContainer.removeElements (0, numElements);
checkEqual (copyableContainer, noncopyableContainer, referenceContainer);
copyableContainer.setAllocatedSize (0);
noncopyableContainer.setAllocatedSize (0);
checkEqual (copyableContainer, noncopyableContainer, referenceContainer);
addData (referenceContainer, copyableContainer, noncopyableContainer, numElements);
checkEqual (copyableContainer, noncopyableContainer, referenceContainer);
}
beginTest ("equality");
{
std::vector<int> referenceContainer = { 1, 2, 3 };
ArrayBase<int, DummyCriticalSection> testContainer1, testContainer2;
for (auto i : referenceContainer)
{
testContainer1.add (i);
testContainer2.add (i);
}
expect (testContainer1 == referenceContainer);
expect (testContainer2 == testContainer1);
testContainer1.ensureAllocatedSize (257);
referenceContainer.shrink_to_fit();
expect (testContainer1 == referenceContainer);
expect (testContainer2 == testContainer1);
testContainer1.removeElements (0, 1);
expect (testContainer1 != referenceContainer);
expect (testContainer2 != testContainer1);
}
beginTest ("accessors");
{
std::vector<CopyableType> referenceContainer;
ArrayBase<CopyableType, DummyCriticalSection> copyableContainer;
ArrayBase<NoncopyableType, DummyCriticalSection> noncopyableContainer;
addData (referenceContainer, copyableContainer, noncopyableContainer, 3);
int testValue = -123;
referenceContainer[0] = testValue;
copyableContainer[0] = testValue;
noncopyableContainer[0] = testValue;
checkEqual (copyableContainer, noncopyableContainer, referenceContainer);
expect (copyableContainer .getFirst().getValue() == testValue);
expect (noncopyableContainer.getFirst().getValue() == testValue);
auto last = referenceContainer.back().getValue();
expectEquals (copyableContainer .getLast().getValue(), last);
expectEquals (noncopyableContainer.getLast().getValue(), last);
ArrayBase<CopyableType, DummyCriticalSection> copyableEmpty;
ArrayBase<NoncopyableType, DummyCriticalSection> noncopyableEmpty;
auto defualtValue = CopyableType().getValue();
expectEquals (defualtValue, NoncopyableType().getValue());
expectEquals (copyableEmpty .getFirst().getValue(), defualtValue);
expectEquals (noncopyableEmpty.getFirst().getValue(), defualtValue);
expectEquals (copyableEmpty .getLast() .getValue(), defualtValue);
expectEquals (noncopyableEmpty.getLast() .getValue(), defualtValue);
ArrayBase<float*, DummyCriticalSection> floatPointers;
expect (floatPointers.getValueWithDefault (-3) == nullptr);
}
beginTest ("add moved");
{
std::vector<CopyableType> referenceContainer;
ArrayBase<CopyableType, DummyCriticalSection> copyableContainer;
ArrayBase<NoncopyableType, DummyCriticalSection> noncopyableContainer;
for (int i = 0; i < 5; ++i)
{
CopyableType ref (-i);
CopyableType ct (-i);
NoncopyableType nct (-i);
referenceContainer.push_back (std::move (ref));
copyableContainer.add (std::move (ct));
noncopyableContainer.add (std::move (nct));
}
checkEqual (copyableContainer, noncopyableContainer, referenceContainer);
}
beginTest ("add multiple");
{
std::vector<CopyableType> referenceContainer;
ArrayBase<CopyableType, DummyCriticalSection> copyableContainer;
ArrayBase<NoncopyableType, DummyCriticalSection> noncopyableContainer;
for (int i = 4; i < 7; ++i)
referenceContainer.push_back ({ -i });
copyableContainer.add (CopyableType (-4), CopyableType (-5), CopyableType (-6));
noncopyableContainer.add (NoncopyableType (-4), NoncopyableType (-5), NoncopyableType (-6));
checkEqual (copyableContainer, noncopyableContainer, referenceContainer);
}
beginTest ("add array from a pointer");
{
ArrayBase<CopyableType, DummyCriticalSection> copyableContainer;
ArrayBase<NoncopyableType, DummyCriticalSection> noncopyableContainer;
std::vector<CopyableType> copyableData = { 3, 4, 5 };
std::vector<NoncopyableType> noncopyableData = { 3, 4, 5 };
copyableContainer.addArray (copyableData.data(), (int) copyableData.size());
noncopyableContainer.addArray (noncopyableData.data(), (int) noncopyableData.size());
checkEqual (copyableContainer, noncopyableContainer, copyableData);
}
beginTest ("add array from a pointer of a different type");
{
std::vector<CopyableType> referenceContainer;
ArrayBase<CopyableType, DummyCriticalSection> copyableContainer;
ArrayBase<NoncopyableType, DummyCriticalSection> noncopyableContainer;
std::vector<float> floatData = { 1.4f, 2.5f, 3.6f };
for (auto f : floatData)
referenceContainer.push_back ({ f });
copyableContainer.addArray (floatData.data(), (int) floatData.size());
noncopyableContainer.addArray (floatData.data(), (int) floatData.size());
checkEqual (copyableContainer, noncopyableContainer, referenceContainer);
}
beginTest ("add array from initilizer list");
{
std::vector<CopyableType> referenceContainer;
ArrayBase<CopyableType, DummyCriticalSection> copyableContainer;
ArrayBase<NoncopyableType, DummyCriticalSection> noncopyableContainer;
std::initializer_list<CopyableType> ilct { { 3 }, { 4 }, { 5 } };
std::initializer_list<NoncopyableType> ilnct { { 3 }, { 4 }, { 5 } };
for (auto v : ilct)
referenceContainer.push_back ({ v });
copyableContainer.addArray (ilct);
noncopyableContainer.addArray (ilnct);
checkEqual (copyableContainer, noncopyableContainer, referenceContainer);
}
beginTest ("add array from containers");
{
std::vector<CopyableType> referenceContainer;
ArrayBase<CopyableType, DummyCriticalSection> copyableContainer;
ArrayBase<NoncopyableType, DummyCriticalSection> noncopyableContainer;
addData (referenceContainer, copyableContainer, noncopyableContainer, 5);
std::vector<CopyableType> referenceContainerCopy (referenceContainer);
std::vector<NoncopyableType> noncopyableReferenceContainerCopy;
ArrayBase<CopyableType, DummyCriticalSection> copyableContainerCopy;
ArrayBase<NoncopyableType, DummyCriticalSection> noncopyableContainerCopy;
for (auto& v : referenceContainerCopy)
noncopyableReferenceContainerCopy.push_back ({ v.getValue() });
for (size_t i = 0; i < referenceContainerCopy.size(); ++i)
{
auto value = referenceContainerCopy[i].getValue();
copyableContainerCopy.add ({ value });
noncopyableContainerCopy.add ({ value });
}
// From self-types
copyableContainer.addArray (copyableContainerCopy);
noncopyableContainer.addArray (noncopyableContainerCopy);
for (auto v : referenceContainerCopy)
referenceContainer.push_back (v);
checkEqual (copyableContainer, noncopyableContainer, referenceContainer);
// From std containers
copyableContainer.addArray (referenceContainerCopy);
noncopyableContainer.addArray (noncopyableReferenceContainerCopy);
for (auto v : referenceContainerCopy)
referenceContainer.push_back (v);
checkEqual (copyableContainer, noncopyableContainer, referenceContainer);
// From std containers with offset
int offset = 5;
copyableContainer.addArray (referenceContainerCopy, offset);
noncopyableContainer.addArray (noncopyableReferenceContainerCopy, offset);
for (size_t i = 5; i < referenceContainerCopy.size(); ++i)
referenceContainer.push_back (referenceContainerCopy[i]);
checkEqual (copyableContainer, noncopyableContainer, referenceContainer);
}
beginTest ("insert");
{
std::vector<CopyableType> referenceContainer;
ArrayBase<CopyableType, DummyCriticalSection> copyableContainer;
ArrayBase<NoncopyableType, DummyCriticalSection> noncopyableContainer;
addData (referenceContainer, copyableContainer, noncopyableContainer, 8);
referenceContainer.insert (referenceContainer.begin(), -4);
copyableContainer.insert (0, -4, 1);
noncopyableContainer.insert (0, -4, 1);
checkEqual (copyableContainer, noncopyableContainer, referenceContainer);
for (int i = 0; i < 3; ++i)
referenceContainer.insert (referenceContainer.begin() + 1, -3);
copyableContainer.insert (1, -3, 3);
noncopyableContainer.insert (1, -3, 3);
checkEqual (copyableContainer, noncopyableContainer, referenceContainer);
for (int i = 0; i < 50; ++i)
referenceContainer.insert (referenceContainer.end() - 1, -9);
copyableContainer.insert (copyableContainer.size() - 2, -9, 50);
noncopyableContainer.insert (noncopyableContainer.size() - 2, -9, 50);
}
beginTest ("insert array");
{
ArrayBase<CopyableType, DummyCriticalSection> copyableContainer;
ArrayBase<NoncopyableType, DummyCriticalSection> noncopyableContainer;
std::vector<CopyableType> copyableData = { 3, 4, 5, 6, 7, 8 };
std::vector<NoncopyableType> noncopyableData = { 3, 4, 5, 6, 7, 8 };
std::vector<CopyableType> referenceContainer { copyableData };
copyableContainer.insertArray (0, copyableData.data(), (int) copyableData.size());
noncopyableContainer.insertArray (0, noncopyableData.data(), (int) noncopyableData.size());
checkEqual (copyableContainer, noncopyableContainer, referenceContainer);
int insertPos = copyableContainer.size() - 1;
for (auto it = copyableData.end(); it != copyableData.begin(); --it)
referenceContainer.insert (referenceContainer.begin() + insertPos, CopyableType (*(it - 1)));
copyableContainer.insertArray (insertPos, copyableData.data(), (int) copyableData.size());
noncopyableContainer.insertArray (insertPos, noncopyableData.data(), (int) noncopyableData.size());
checkEqual (copyableContainer, noncopyableContainer, referenceContainer);
}
beginTest ("remove");
{
std::vector<CopyableType> referenceContainer;
ArrayBase<CopyableType, DummyCriticalSection> copyableContainer;
ArrayBase<NoncopyableType, DummyCriticalSection> noncopyableContainer;
addData (referenceContainer, copyableContainer, noncopyableContainer, 17);
for (int i = 0; i < 4; ++i)
{
referenceContainer.erase (referenceContainer.begin() + i);
copyableContainer.removeElements (i, 1);
noncopyableContainer.removeElements (i, 1);
}
checkEqual (copyableContainer, noncopyableContainer, referenceContainer);
addData (referenceContainer, copyableContainer, noncopyableContainer, 17);
int blockSize = 3;
for (int i = 0; i < 4; ++i)
{
for (int j = 0; j < blockSize; ++j)
referenceContainer.erase (referenceContainer.begin() + i);
copyableContainer.removeElements (i, blockSize);
noncopyableContainer.removeElements (i, blockSize);
}
checkEqual (copyableContainer, noncopyableContainer, referenceContainer);
auto numToRemove = copyableContainer.size() - 2;
for (int i = 0; i < numToRemove; ++i)
referenceContainer.erase (referenceContainer.begin() + 1);
copyableContainer.removeElements (1, numToRemove);
noncopyableContainer.removeElements (1, numToRemove);
checkEqual (copyableContainer, noncopyableContainer, referenceContainer);
}
beginTest ("move");
{
std::vector<CopyableType> referenceContainer;
ArrayBase<CopyableType, DummyCriticalSection> copyableContainer;
ArrayBase<NoncopyableType, DummyCriticalSection> noncopyableContainer;
addData (referenceContainer, copyableContainer, noncopyableContainer, 6);
std::vector<std::pair<int, int>> testValues;
testValues.emplace_back (2, 4);
testValues.emplace_back (0, 5);
testValues.emplace_back (4, 1);
testValues.emplace_back (5, 0);
for (auto p : testValues)
{
if (p.second > p.first)
std::rotate (referenceContainer.begin() + p.first,
referenceContainer.begin() + p.first + 1,
referenceContainer.begin() + p.second + 1);
else
std::rotate (referenceContainer.begin() + p.second,
referenceContainer.begin() + p.first,
referenceContainer.begin() + p.first + 1);
copyableContainer.move (p.first, p.second);
noncopyableContainer.move (p.first, p.second);
checkEqual (copyableContainer, noncopyableContainer, referenceContainer);
}
}
beginTest ("After converting move construction, ownership is transferred");
{
Derived obj;
ArrayBase<Derived*, DummyCriticalSection> derived;
derived.setAllocatedSize (5);
derived.add (&obj);
ArrayBase<Base*, DummyCriticalSection> base { std::move (derived) };
expectEquals (base.capacity(), 5);
expectEquals (base.size(), 1);
expect (base.getFirst() == &obj);
expectEquals (derived.capacity(), 0);
expectEquals (derived.size(), 0);
expect (derived.data() == nullptr);
}
beginTest ("After converting move assignment, ownership is transferred");
{
Derived obj;
ArrayBase<Derived*, DummyCriticalSection> derived;
derived.setAllocatedSize (5);
derived.add (&obj);
ArrayBase<Base*, DummyCriticalSection> base;
base = std::move (derived);
expectEquals (base.capacity(), 5);
expectEquals (base.size(), 1);
expect (base.getFirst() == &obj);
expectEquals (derived.capacity(), 0);
expectEquals (derived.size(), 0);
expect (derived.data() == nullptr);
}
}
private:
struct Base
{
virtual ~Base() = default;
};
struct Derived : Base
{
};
static void addData (std::vector<CopyableType>& referenceContainer,
ArrayBase<CopyableType, DummyCriticalSection>& copyableContainer,
ArrayBase<NoncopyableType, DummyCriticalSection>& NoncopyableContainer,
int numValues)
{
for (int i = 0; i < numValues; ++i)
{
referenceContainer.push_back ({ i });
copyableContainer.add ({ i });
NoncopyableContainer.add ({ i });
}
}
template<typename A, typename B>
void checkEqual (const ArrayBase<A, DummyCriticalSection>& a,
const ArrayBase<B, DummyCriticalSection>& b)
{
expectEquals ((int) a.size(), (int) b.size());
for (int i = 0; i < (int) a.size(); ++i)
expect (a[i] == b[i]);
}
template<typename A, typename B>
void checkEqual (ArrayBase<A, DummyCriticalSection>& a,
std::vector<B>& b)
{
expectEquals ((int) a.size(), (int) b.size());
for (int i = 0; i < (int) a.size(); ++i)
expect (a[i] == b[(size_t) i]);
}
template<typename A, typename B, typename C>
void checkEqual (ArrayBase<A, DummyCriticalSection>& a,
ArrayBase<B, DummyCriticalSection>& b,
std::vector<C>& c)
{
checkEqual (a, b);
checkEqual (a, c);
checkEqual (b, c);
}
};
static ArrayBaseTests arrayBaseTests;
#endif
} // namespace juce

View File

@ -0,0 +1,596 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
/**
A basic object container.
This class isn't really for public use - it's used by the other
array classes, but might come in handy for some purposes.
It inherits from a critical section class to allow the arrays to use
the "empty base class optimisation" pattern to reduce their footprint.
@see Array, OwnedArray, ReferenceCountedArray
@tags{Core}
*/
template <class ElementType, class TypeOfCriticalSectionToUse>
class ArrayBase : public TypeOfCriticalSectionToUse
{
private:
using ParameterType = typename TypeHelpers::ParameterType<ElementType>::type;
template <class OtherElementType, class OtherCriticalSection>
using AllowConversion = typename std::enable_if<! std::is_same<std::tuple<ElementType, TypeOfCriticalSectionToUse>,
std::tuple<OtherElementType, OtherCriticalSection>>::value>::type;
public:
//==============================================================================
ArrayBase() = default;
~ArrayBase()
{
clear();
}
ArrayBase (ArrayBase&& other) noexcept
: elements (std::move (other.elements)),
numAllocated (other.numAllocated),
numUsed (other.numUsed)
{
other.numAllocated = 0;
other.numUsed = 0;
}
ArrayBase& operator= (ArrayBase&& other) noexcept
{
if (this != &other)
{
auto tmp (std::move (other));
swapWith (tmp);
}
return *this;
}
/** Converting move constructor.
Only enabled when the other array has a different type to this one.
If you see a compile error here, it's probably because you're attempting a conversion that
HeapBlock won't allow.
*/
template <class OtherElementType,
class OtherCriticalSection,
typename = AllowConversion<OtherElementType, OtherCriticalSection>>
ArrayBase (ArrayBase<OtherElementType, OtherCriticalSection>&& other) noexcept
: elements (std::move (other.elements)),
numAllocated (other.numAllocated),
numUsed (other.numUsed)
{
other.numAllocated = 0;
other.numUsed = 0;
}
/** Converting move assignment operator.
Only enabled when the other array has a different type to this one.
If you see a compile error here, it's probably because you're attempting a conversion that
HeapBlock won't allow.
*/
template <class OtherElementType,
class OtherCriticalSection,
typename = AllowConversion<OtherElementType, OtherCriticalSection>>
ArrayBase& operator= (ArrayBase<OtherElementType, OtherCriticalSection>&& other) noexcept
{
// No need to worry about assignment to *this, because 'other' must be of a different type.
elements = std::move (other.elements);
numAllocated = other.numAllocated;
numUsed = other.numUsed;
other.numAllocated = 0;
other.numUsed = 0;
return *this;
}
//==============================================================================
template <class OtherArrayType>
bool operator== (const OtherArrayType& other) const noexcept
{
if (size() != (int) other.size())
return false;
auto* e = begin();
for (auto& o : other)
if (! (*e++ == o))
return false;
return true;
}
template <class OtherArrayType>
bool operator!= (const OtherArrayType& other) const noexcept
{
return ! operator== (other);
}
//==============================================================================
inline ElementType& operator[] (const int index) const noexcept
{
jassert (elements != nullptr);
jassert (isPositiveAndBelow (index, numUsed));
return elements[index];
}
inline ElementType getValueWithDefault (const int index) const noexcept
{
return isPositiveAndBelow (index, numUsed) ? elements[index] : ElementType();
}
inline ElementType getFirst() const noexcept
{
return numUsed > 0 ? elements[0] : ElementType();
}
inline ElementType getLast() const noexcept
{
return numUsed > 0 ? elements[numUsed - 1] : ElementType();
}
//==============================================================================
inline ElementType* begin() const noexcept
{
return elements;
}
inline ElementType* end() const noexcept
{
return elements + numUsed;
}
inline ElementType* data() const noexcept
{
return elements;
}
inline int size() const noexcept
{
return numUsed;
}
inline int capacity() const noexcept
{
return numAllocated;
}
//==============================================================================
void setAllocatedSize (int numElements)
{
jassert (numElements >= numUsed);
if (numAllocated != numElements)
{
if (numElements > 0)
setAllocatedSizeInternal (numElements);
else
elements.free();
}
numAllocated = numElements;
}
void ensureAllocatedSize (int minNumElements)
{
if (minNumElements > numAllocated)
setAllocatedSize ((minNumElements + minNumElements / 2 + 8) & ~7);
jassert (numAllocated <= 0 || elements != nullptr);
}
void shrinkToNoMoreThan (int maxNumElements)
{
if (maxNumElements < numAllocated)
setAllocatedSize (maxNumElements);
}
void clear()
{
for (int i = 0; i < numUsed; ++i)
elements[i].~ElementType();
numUsed = 0;
}
//==============================================================================
void swapWith (ArrayBase& other) noexcept
{
elements.swapWith (other.elements);
std::swap (numAllocated, other.numAllocated);
std::swap (numUsed, other.numUsed);
}
//==============================================================================
void add (const ElementType& newElement)
{
checkSourceIsNotAMember (&newElement);
ensureAllocatedSize (numUsed + 1);
addAssumingCapacityIsReady (newElement);
}
void add (ElementType&& newElement)
{
checkSourceIsNotAMember (&newElement);
ensureAllocatedSize (numUsed + 1);
addAssumingCapacityIsReady (std::move (newElement));
}
template <typename... OtherElements>
void add (const ElementType& firstNewElement, OtherElements... otherElements)
{
checkSourceIsNotAMember (&firstNewElement);
ensureAllocatedSize (numUsed + 1 + (int) sizeof... (otherElements));
addAssumingCapacityIsReady (firstNewElement, otherElements...);
}
template <typename... OtherElements>
void add (ElementType&& firstNewElement, OtherElements... otherElements)
{
checkSourceIsNotAMember (&firstNewElement);
ensureAllocatedSize (numUsed + 1 + (int) sizeof... (otherElements));
addAssumingCapacityIsReady (std::move (firstNewElement), otherElements...);
}
//==============================================================================
template <typename Type>
void addArray (const Type* elementsToAdd, int numElementsToAdd)
{
ensureAllocatedSize (numUsed + numElementsToAdd);
addArrayInternal (elementsToAdd, numElementsToAdd);
numUsed += numElementsToAdd;
}
template <typename TypeToCreateFrom>
void addArray (const std::initializer_list<TypeToCreateFrom>& items)
{
ensureAllocatedSize (numUsed + (int) items.size());
for (auto& item : items)
new (elements + numUsed++) ElementType (item);
}
template <class OtherArrayType>
void addArray (const OtherArrayType& arrayToAddFrom)
{
jassert ((const void*) this != (const void*) &arrayToAddFrom); // can't add from our own elements!
ensureAllocatedSize (numUsed + (int) arrayToAddFrom.size());
for (auto& e : arrayToAddFrom)
addAssumingCapacityIsReady (e);
}
template <class OtherArrayType>
typename std::enable_if<! std::is_pointer<OtherArrayType>::value, int>::type
addArray (const OtherArrayType& arrayToAddFrom,
int startIndex, int numElementsToAdd = -1)
{
jassert ((const void*) this != (const void*) &arrayToAddFrom); // can't add from our own elements!
if (startIndex < 0)
{
jassertfalse;
startIndex = 0;
}
if (numElementsToAdd < 0 || startIndex + numElementsToAdd > (int) arrayToAddFrom.size())
numElementsToAdd = (int) arrayToAddFrom.size() - startIndex;
addArray (arrayToAddFrom.data() + startIndex, numElementsToAdd);
return numElementsToAdd;
}
//==============================================================================
void insert (int indexToInsertAt, ParameterType newElement, int numberOfTimesToInsertIt)
{
checkSourceIsNotAMember (&newElement);
auto* space = createInsertSpace (indexToInsertAt, numberOfTimesToInsertIt);
for (int i = 0; i < numberOfTimesToInsertIt; ++i)
new (space++) ElementType (newElement);
numUsed += numberOfTimesToInsertIt;
}
void insertArray (int indexToInsertAt, const ElementType* newElements, int numberOfElements)
{
auto* space = createInsertSpace (indexToInsertAt, numberOfElements);
for (int i = 0; i < numberOfElements; ++i)
new (space++) ElementType (*(newElements++));
numUsed += numberOfElements;
}
//==============================================================================
void removeElements (int indexToRemoveAt, int numElementsToRemove)
{
jassert (indexToRemoveAt >= 0);
jassert (numElementsToRemove >= 0);
jassert (indexToRemoveAt + numElementsToRemove <= numUsed);
if (numElementsToRemove > 0)
{
removeElementsInternal (indexToRemoveAt, numElementsToRemove);
numUsed -= numElementsToRemove;
}
}
//==============================================================================
void swap (int index1, int index2)
{
if (isPositiveAndBelow (index1, numUsed)
&& isPositiveAndBelow (index2, numUsed))
{
std::swap (elements[index1],
elements[index2]);
}
}
//==============================================================================
void move (int currentIndex, int newIndex) noexcept
{
if (isPositiveAndBelow (currentIndex, numUsed))
{
if (! isPositiveAndBelow (newIndex, numUsed))
newIndex = numUsed - 1;
moveInternal (currentIndex, newIndex);
}
}
private:
//==============================================================================
template <typename T>
#if defined(__GNUC__) && __GNUC__ < 5 && ! defined(__clang__)
using IsTriviallyCopyable = std::is_scalar<T>;
#else
using IsTriviallyCopyable = std::is_trivially_copyable<T>;
#endif
template <typename T>
using TriviallyCopyableVoid = typename std::enable_if<IsTriviallyCopyable<T>::value, void>::type;
template <typename T>
using NonTriviallyCopyableVoid = typename std::enable_if<! IsTriviallyCopyable<T>::value, void>::type;
//==============================================================================
template <typename T = ElementType>
TriviallyCopyableVoid<T> addArrayInternal (const ElementType* otherElements, int numElements)
{
memcpy (elements + numUsed, otherElements, (size_t) numElements * sizeof (ElementType));
}
template <typename Type, typename T = ElementType>
TriviallyCopyableVoid<T> addArrayInternal (const Type* otherElements, int numElements)
{
auto* start = elements + numUsed;
while (--numElements >= 0)
new (start++) ElementType (*(otherElements++));
}
template <typename Type, typename T = ElementType>
NonTriviallyCopyableVoid<T> addArrayInternal (const Type* otherElements, int numElements)
{
auto* start = elements + numUsed;
while (--numElements >= 0)
new (start++) ElementType (*(otherElements++));
}
//==============================================================================
template <typename T = ElementType>
TriviallyCopyableVoid<T> setAllocatedSizeInternal (int numElements)
{
elements.realloc ((size_t) numElements);
}
template <typename T = ElementType>
NonTriviallyCopyableVoid<T> setAllocatedSizeInternal (int numElements)
{
HeapBlock<ElementType> newElements (numElements);
for (int i = 0; i < numUsed; ++i)
{
new (newElements + i) ElementType (std::move (elements[i]));
elements[i].~ElementType();
}
elements = std::move (newElements);
}
//==============================================================================
ElementType* createInsertSpace (int indexToInsertAt, int numElements)
{
ensureAllocatedSize (numUsed + numElements);
if (! isPositiveAndBelow (indexToInsertAt, numUsed))
return elements + numUsed;
createInsertSpaceInternal (indexToInsertAt, numElements);
return elements + indexToInsertAt;
}
template <typename T = ElementType>
TriviallyCopyableVoid<T> createInsertSpaceInternal (int indexToInsertAt, int numElements)
{
auto* start = elements + indexToInsertAt;
auto numElementsToShift = numUsed - indexToInsertAt;
memmove (start + numElements, start, (size_t) numElementsToShift * sizeof (ElementType));
}
template <typename T = ElementType>
NonTriviallyCopyableVoid<T> createInsertSpaceInternal (int indexToInsertAt, int numElements)
{
auto* end = elements + numUsed;
auto* newEnd = end + numElements;
auto numElementsToShift = numUsed - indexToInsertAt;
for (int i = 0; i < numElementsToShift; ++i)
{
new (--newEnd) ElementType (std::move (*(--end)));
end->~ElementType();
}
}
//==============================================================================
template <typename T = ElementType>
TriviallyCopyableVoid<T> removeElementsInternal (int indexToRemoveAt, int numElementsToRemove)
{
auto* start = elements + indexToRemoveAt;
auto numElementsToShift = numUsed - (indexToRemoveAt + numElementsToRemove);
memmove (start, start + numElementsToRemove, (size_t) numElementsToShift * sizeof (ElementType));
}
template <typename T = ElementType>
NonTriviallyCopyableVoid<T> removeElementsInternal (int indexToRemoveAt, int numElementsToRemove)
{
auto numElementsToShift = numUsed - (indexToRemoveAt + numElementsToRemove);
auto* destination = elements + indexToRemoveAt;
auto* source = destination + numElementsToRemove;
for (int i = 0; i < numElementsToShift; ++i)
moveAssignElement (destination++, std::move (*(source++)));
for (int i = 0; i < numElementsToRemove; ++i)
(destination++)->~ElementType();
}
//==============================================================================
template <typename T = ElementType>
TriviallyCopyableVoid<T> moveInternal (int currentIndex, int newIndex) noexcept
{
char tempCopy[sizeof (ElementType)];
memcpy (tempCopy, elements + currentIndex, sizeof (ElementType));
if (newIndex > currentIndex)
{
memmove (elements + currentIndex,
elements + currentIndex + 1,
sizeof (ElementType) * (size_t) (newIndex - currentIndex));
}
else
{
memmove (elements + newIndex + 1,
elements + newIndex,
sizeof (ElementType) * (size_t) (currentIndex - newIndex));
}
memcpy (elements + newIndex, tempCopy, sizeof (ElementType));
}
template <typename T = ElementType>
NonTriviallyCopyableVoid<T> moveInternal (int currentIndex, int newIndex) noexcept
{
auto* e = elements + currentIndex;
ElementType tempCopy (std::move (*e));
auto delta = newIndex - currentIndex;
if (delta > 0)
{
for (int i = 0; i < delta; ++i)
{
moveAssignElement (e, std::move (*(e + 1)));
++e;
}
}
else
{
for (int i = 0; i < -delta; ++i)
{
moveAssignElement (e, std::move (*(e - 1)));
--e;
}
}
moveAssignElement (e, std::move (tempCopy));
}
//==============================================================================
void addAssumingCapacityIsReady (const ElementType& element) { new (elements + numUsed++) ElementType (element); }
void addAssumingCapacityIsReady (ElementType&& element) { new (elements + numUsed++) ElementType (std::move (element)); }
template <typename... OtherElements>
void addAssumingCapacityIsReady (const ElementType& firstNewElement, OtherElements... otherElements)
{
addAssumingCapacityIsReady (firstNewElement);
addAssumingCapacityIsReady (otherElements...);
}
template <typename... OtherElements>
void addAssumingCapacityIsReady (ElementType&& firstNewElement, OtherElements... otherElements)
{
addAssumingCapacityIsReady (std::move (firstNewElement));
addAssumingCapacityIsReady (otherElements...);
}
//==============================================================================
template <typename T = ElementType>
typename std::enable_if<std::is_move_assignable<T>::value, void>::type
moveAssignElement (ElementType* destination, ElementType&& source)
{
*destination = std::move (source);
}
template <typename T = ElementType>
typename std::enable_if<! std::is_move_assignable<T>::value, void>::type
moveAssignElement (ElementType* destination, ElementType&& source)
{
destination->~ElementType();
new (destination) ElementType (std::move (source));
}
void checkSourceIsNotAMember (const ElementType* element)
{
// when you pass a reference to an existing element into a method like add() which
// may need to reallocate the array to make more space, the incoming reference may
// be deleted indirectly during the reallocation operation! To work around this,
// make a local copy of the item you're trying to add (and maybe use std::move to
// move it into the add() method to avoid any extra overhead)
jassert (element < begin() || element >= end());
ignoreUnused (element);
}
//==============================================================================
HeapBlock<ElementType> elements;
int numAllocated = 0, numUsed = 0;
template <class OtherElementType, class OtherCriticalSection>
friend class ArrayBase;
JUCE_DECLARE_NON_COPYABLE (ArrayBase)
};
} // namespace juce

View File

@ -42,7 +42,7 @@ public:
//==============================================================================
DynamicObject();
DynamicObject (const DynamicObject&);
~DynamicObject();
~DynamicObject() override;
using Ptr = ReferenceCountedObjectPtr<DynamicObject>;

View File

@ -120,7 +120,7 @@ public:
*/
explicit HashMap (int numberOfSlots = defaultHashTableSize,
HashFunctionType hashFunction = HashFunctionType())
: hashFunctionToUse (hashFunction), totalNumItems (0)
: hashFunctionToUse (hashFunction)
{
hashSlots.insertMultiple (0, nullptr, numberOfSlots);
}
@ -354,7 +354,7 @@ public:
inline const TypeOfCriticalSectionToUse& getLock() const noexcept { return lock; }
/** Returns the type of scoped lock to use for locking this array */
typedef typename TypeOfCriticalSectionToUse::ScopedLockType ScopedLockType;
using ScopedLockType = typename TypeOfCriticalSectionToUse::ScopedLockType;
private:
//==============================================================================
@ -388,7 +388,7 @@ public:
}
@endcode
The order in which items are iterated bears no resemblence to the order in which
The order in which items are iterated bears no resemblance to the order in which
they were originally added!
Obviously as soon as you call any non-const methods on the original hash-map, any
@ -479,7 +479,7 @@ private:
HashFunctionType hashFunctionToUse;
Array<HashEntry*> hashSlots;
int totalNumItems;
int totalNumItems = 0;
TypeOfCriticalSectionToUse lock;
int generateHashFor (KeyTypeParameter key, int numSlots) const

View File

@ -70,10 +70,10 @@ class ListenerList
public:
//==============================================================================
/** Creates an empty list. */
ListenerList() {}
ListenerList() = default;
/** Destructor. */
~ListenerList() {}
~ListenerList() = default;
//==============================================================================
/** Adds a listener to the list.
@ -101,7 +101,7 @@ public:
/** Returns the number of registered listeners. */
int size() const noexcept { return listeners.size(); }
/** Returns true if any listeners are registered. */
/** Returns true if no listeners are registered, false otherwise. */
bool isEmpty() const noexcept { return listeners.isEmpty(); }
/** Clears the list. */
@ -194,7 +194,7 @@ public:
: list (listToIterate), index (listToIterate.size())
{}
~Iterator() noexcept {}
~Iterator() = default;
//==============================================================================
bool next() noexcept

View File

@ -30,24 +30,24 @@ NamedValueSet::NamedValue::NamedValue (const Identifier& n, const var& v) : nam
NamedValueSet::NamedValue::NamedValue (const NamedValue& other) : NamedValue (other.name, other.value) {}
NamedValueSet::NamedValue::NamedValue (NamedValue&& other) noexcept
: NamedValue (static_cast<Identifier&&> (other.name),
static_cast<var&&> (other.value))
: NamedValue (std::move (other.name),
std::move (other.value))
{}
NamedValueSet::NamedValue::NamedValue (const Identifier& n, var&& v) noexcept
: name (n), value (static_cast<var&&> (v))
: name (n), value (std::move (v))
{
}
NamedValueSet::NamedValue::NamedValue (Identifier&& n, var&& v) noexcept
: name (static_cast<Identifier&&> (n)),
value (static_cast<var&&> (v))
: name (std::move (n)),
value (std::move (v))
{}
NamedValueSet::NamedValue& NamedValueSet::NamedValue::operator= (NamedValue&& other) noexcept
{
name = static_cast<Identifier&&> (other.name);
value = static_cast<var&&> (other.value);
name = std::move (other.name);
value = std::move (other.value);
return *this;
}
@ -59,7 +59,14 @@ NamedValueSet::NamedValueSet() noexcept {}
NamedValueSet::~NamedValueSet() noexcept {}
NamedValueSet::NamedValueSet (const NamedValueSet& other) : values (other.values) {}
NamedValueSet::NamedValueSet (NamedValueSet&& other) noexcept : values (static_cast<Array<NamedValue>&&> (other.values)) {}
NamedValueSet::NamedValueSet (NamedValueSet&& other) noexcept
: values (std::move (other.values)) {}
NamedValueSet::NamedValueSet (std::initializer_list<NamedValue> list)
: values (std::move (list))
{
}
NamedValueSet& NamedValueSet::operator= (const NamedValueSet& other)
{
@ -156,11 +163,11 @@ bool NamedValueSet::set (const Identifier& name, var&& newValue)
if (v->equalsWithSameType (newValue))
return false;
*v = static_cast<var&&> (newValue);
*v = std::move (newValue);
return true;
}
values.add ({ name, static_cast<var&&> (newValue) });
values.add ({ name, std::move (newValue) });
return true;
}

View File

@ -34,23 +34,6 @@ namespace juce
class JUCE_API NamedValueSet
{
public:
/** Creates an empty set. */
NamedValueSet() noexcept;
NamedValueSet (const NamedValueSet&);
NamedValueSet (NamedValueSet&&) noexcept;
NamedValueSet& operator= (const NamedValueSet&);
NamedValueSet& operator= (NamedValueSet&&) noexcept;
/** Destructor. */
~NamedValueSet() noexcept;
/** Two NamedValueSets are considered equal if they contain all the same key/value
pairs, regardless of the order.
*/
bool operator== (const NamedValueSet&) const noexcept;
bool operator!= (const NamedValueSet&) const noexcept;
//==============================================================================
/** Structure for a named var object */
struct JUCE_API NamedValue
@ -73,6 +56,27 @@ public:
var value;
};
//==============================================================================
/** Creates an empty set. */
NamedValueSet() noexcept;
NamedValueSet (const NamedValueSet&);
NamedValueSet (NamedValueSet&&) noexcept;
NamedValueSet& operator= (const NamedValueSet&);
NamedValueSet& operator= (NamedValueSet&&) noexcept;
/** Creates a NamedValueSet from a list of names and properties. */
NamedValueSet (std::initializer_list<NamedValue>);
/** Destructor. */
~NamedValueSet() noexcept;
/** Two NamedValueSets are considered equal if they contain all the same key/value
pairs, regardless of the order.
*/
bool operator== (const NamedValueSet&) const noexcept;
bool operator!= (const NamedValueSet&) const noexcept;
const NamedValueSet::NamedValue* begin() const noexcept { return values.begin(); }
const NamedValueSet::NamedValue* end() const noexcept { return values.end(); }
@ -125,6 +129,8 @@ public:
Do not use this method unless you really need access to the internal var object
for some reason - for normal reading and writing always prefer operator[]() and set().
Also note that the pointer returned may become invalid as soon as any subsequent
methods are called on the NamedValueSet.
*/
var* getVarPointer (const Identifier& name) const noexcept;
@ -135,6 +141,8 @@ public:
/** Returns the value of the item at a given index.
The index must be between 0 and size() - 1, or this will return a nullptr
Also note that the pointer returned may become invalid as soon as any subsequent
methods are called on the NamedValueSet.
*/
var* getVarPointerAt (int index) const noexcept;

View File

@ -0,0 +1,66 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
#if JUCE_UNIT_TESTS
static struct OwnedArrayTest : public UnitTest
{
OwnedArrayTest() : UnitTest { "OwnedArray" } {}
struct Base
{
virtual ~Base() = default;
};
struct Derived : Base
{
};
void runTest() override
{
beginTest ("After converting move construction, ownership is transferred");
{
OwnedArray<Derived> derived { new Derived{}, new Derived{}, new Derived{} };
OwnedArray<Base> base { std::move (derived) };
expectEquals (base.size(), 3);
expectEquals (derived.size(), 0);
}
beginTest ("After converting move assignment, ownership is transferred");
{
OwnedArray<Base> base;
base = OwnedArray<Derived> { new Derived{}, new Derived{}, new Derived{} };
expectEquals (base.size(), 3);
}
}
} ownedArrayTest;
#endif
}

View File

@ -52,9 +52,7 @@ class OwnedArray
public:
//==============================================================================
/** Creates an empty array. */
OwnedArray() noexcept
{
}
OwnedArray() = default;
/** Deletes the array and also deletes any objects inside it.
@ -68,10 +66,8 @@ public:
/** Move constructor. */
OwnedArray (OwnedArray&& other) noexcept
: data (static_cast<ArrayAllocationBase <ObjectClass*, TypeOfCriticalSectionToUse>&&> (other.data)),
numUsed (other.numUsed)
: values (std::move (other.values))
{
other.numUsed = 0;
}
/** Creates an array from a list of objects. */
@ -85,10 +81,24 @@ public:
{
const ScopedLockType lock (getLock());
deleteAllObjects();
values = std::move (other.values);
return *this;
}
data = static_cast<ArrayAllocationBase <ObjectClass*, TypeOfCriticalSectionToUse>&&> (other.data);
numUsed = other.numUsed;
other.numUsed = 0;
/** Converting move constructor. */
template <class OtherObjectClass, class OtherCriticalSection>
OwnedArray (OwnedArray<OtherObjectClass, OtherCriticalSection>&& other) noexcept
: values (std::move (other.values))
{
}
/** Converting move assignment operator. */
template <class OtherObjectClass, class OtherCriticalSection>
OwnedArray& operator= (OwnedArray<OtherObjectClass, OtherCriticalSection>&& other) noexcept
{
const ScopedLockType lock (getLock());
deleteAllObjects();
values = std::move (other.values);
return *this;
}
@ -97,12 +107,8 @@ public:
void clear (bool deleteObjects = true)
{
const ScopedLockType lock (getLock());
if (deleteObjects)
deleteAllObjects();
data.setAllocatedSize (0);
numUsed = 0;
clearQuick (deleteObjects);
values.setAllocatedSize (0);
}
//==============================================================================
@ -113,8 +119,8 @@ public:
if (deleteObjects)
deleteAllObjects();
numUsed = 0;
else
values.clear();
}
//==============================================================================
@ -123,7 +129,7 @@ public:
*/
inline int size() const noexcept
{
return numUsed;
return values.size();
}
/** Returns true if the array is empty, false otherwise. */
@ -143,13 +149,7 @@ public:
inline ObjectClass* operator[] (const int index) const noexcept
{
const ScopedLockType lock (getLock());
if (isPositiveAndBelow (index, numUsed))
{
jassert (data.elements != nullptr);
return data.elements[index];
}
return nullptr;
return values.getValueWithDefault (index);
}
/** Returns a pointer to the object at this index in the array, without checking whether the index is in-range.
@ -160,8 +160,7 @@ public:
inline ObjectClass* getUnchecked (const int index) const noexcept
{
const ScopedLockType lock (getLock());
jassert (isPositiveAndBelow (index, numUsed) && data.elements != nullptr);
return data.elements[index];
return values[index];
}
/** Returns a pointer to the first object in the array.
@ -172,14 +171,7 @@ public:
inline ObjectClass* getFirst() const noexcept
{
const ScopedLockType lock (getLock());
if (numUsed > 0)
{
jassert (data.elements != nullptr);
return data.elements[0];
}
return nullptr;
return values.getFirst();
}
/** Returns a pointer to the last object in the array.
@ -190,14 +182,7 @@ public:
inline ObjectClass* getLast() const noexcept
{
const ScopedLockType lock (getLock());
if (numUsed > 0)
{
jassert (data.elements != nullptr);
return data.elements[numUsed - 1];
}
return nullptr;
return values.getLast();
}
/** Returns a pointer to the actual array data.
@ -206,7 +191,7 @@ public:
*/
inline ObjectClass** getRawDataPointer() noexcept
{
return data.elements;
return values.begin();
}
//==============================================================================
@ -215,7 +200,7 @@ public:
*/
inline ObjectClass** begin() const noexcept
{
return data.elements;
return values.begin();
}
/** Returns a pointer to the element which follows the last element in the array.
@ -223,12 +208,15 @@ public:
*/
inline ObjectClass** end() const noexcept
{
#if JUCE_DEBUG
if (data.elements == nullptr || numUsed <= 0) // (to keep static analysers happy)
return data.elements;
#endif
return values.end();
}
return data.elements + numUsed;
/** Returns a pointer to the first element in the array.
This method is provided for compatibility with the standard C++ containers.
*/
inline ObjectClass** data() const noexcept
{
return begin();
}
//==============================================================================
@ -240,12 +228,11 @@ public:
int indexOf (const ObjectClass* objectToLookFor) const noexcept
{
const ScopedLockType lock (getLock());
auto** e = data.elements.get();
auto** end_ = e + numUsed;
auto** e = values.begin();
for (; e != end_; ++e)
for (; e != values.end(); ++e)
if (objectToLookFor == *e)
return static_cast<int> (e - data.elements.get());
return static_cast<int> (e - values.begin());
return -1;
}
@ -258,10 +245,9 @@ public:
bool contains (const ObjectClass* objectToLookFor) const noexcept
{
const ScopedLockType lock (getLock());
auto** e = data.elements.get();
auto** end_ = e + numUsed;
auto** e = values.begin();
for (; e != end_; ++e)
for (; e != values.end(); ++e)
if (objectToLookFor == *e)
return true;
@ -284,9 +270,7 @@ public:
ObjectClass* add (ObjectClass* newObject) noexcept
{
const ScopedLockType lock (getLock());
data.ensureAllocatedSize (numUsed + 1);
jassert (data.elements != nullptr);
data.elements[numUsed++] = newObject;
values.add (newObject);
return newObject;
}
@ -310,25 +294,8 @@ public:
*/
ObjectClass* insert (int indexToInsertAt, ObjectClass* newObject) noexcept
{
if (indexToInsertAt < 0)
return add (newObject);
const ScopedLockType lock (getLock());
if (indexToInsertAt > numUsed)
indexToInsertAt = numUsed;
data.ensureAllocatedSize (numUsed + 1);
jassert (data.elements != nullptr);
auto** e = data.elements + indexToInsertAt;
auto numToMove = numUsed - indexToInsertAt;
if (numToMove > 0)
memmove (e + 1, e, sizeof (ObjectClass*) * (size_t) numToMove);
*e = newObject;
++numUsed;
values.insert (indexToInsertAt, newObject, 1);
return newObject;
}
@ -351,24 +318,7 @@ public:
if (numberOfElements > 0)
{
const ScopedLockType lock (getLock());
data.ensureAllocatedSize (numUsed + numberOfElements);
auto* insertPos = data.elements.get();
if (isPositiveAndBelow (indexToInsertAt, numUsed))
{
insertPos += indexToInsertAt;
auto numberToMove = (size_t) (numUsed - indexToInsertAt);
memmove (insertPos + numberOfElements, insertPos, numberToMove * sizeof (ObjectClass*));
}
else
{
insertPos += numUsed;
}
numUsed += numberOfElements;
while (--numberOfElements >= 0)
*insertPos++ = *newObjects++;
values.insertArray (indexToInsertAt, newObjects, numberOfElements);
}
}
@ -413,22 +363,21 @@ public:
{
const ScopedLockType lock (getLock());
if (indexToChange < numUsed)
if (indexToChange < values.size())
{
if (deleteOldElement)
{
toDelete.reset (data.elements[indexToChange]);
toDelete.reset (values[indexToChange]);
if (toDelete.get() == newObject)
toDelete.release();
}
data.elements[indexToChange] = newObject;
values[indexToChange] = newObject;
}
else
{
data.ensureAllocatedSize (numUsed + 1);
data.elements[numUsed++] = newObject;
values.add (newObject);
}
}
}
@ -457,24 +406,7 @@ public:
{
const typename OtherArrayType::ScopedLockType lock1 (arrayToAddFrom.getLock());
const ScopedLockType lock2 (getLock());
if (startIndex < 0)
{
jassertfalse;
startIndex = 0;
}
if (numElementsToAdd < 0 || startIndex + numElementsToAdd > arrayToAddFrom.size())
numElementsToAdd = arrayToAddFrom.size() - startIndex;
data.ensureAllocatedSize (numUsed + numElementsToAdd);
jassert (numElementsToAdd <= 0 || data.elements != nullptr);
while (--numElementsToAdd >= 0)
{
data.elements[numUsed] = arrayToAddFrom.getUnchecked (startIndex++);
++numUsed;
}
values.addArray (arrayToAddFrom, startIndex, numElementsToAdd);
}
/** Adds elements from another array to the end of this array. */
@ -482,13 +414,7 @@ public:
void addArray (const std::initializer_list<OtherArrayType>& items)
{
const ScopedLockType lock (getLock());
data.ensureAllocatedSize (numUsed + (int) items.size());
for (auto* item : items)
{
data.elements[numUsed] = item;
++numUsed;
}
values.addArray (items);
}
/** Adds copies of the elements in another array to the end of this array.
@ -522,11 +448,11 @@ public:
if (numElementsToAdd < 0 || startIndex + numElementsToAdd > arrayToAddFrom.size())
numElementsToAdd = arrayToAddFrom.size() - startIndex;
data.ensureAllocatedSize (numUsed + numElementsToAdd);
jassert (numElementsToAdd <= 0 || data.elements != nullptr);
jassert (numElementsToAdd >= 0);
values.ensureAllocatedSize (values.size() + numElementsToAdd);
while (--numElementsToAdd >= 0)
data.elements[numUsed++] = createCopyIfNotNull (arrayToAddFrom.getUnchecked (startIndex++));
values.add (createCopyIfNotNull (arrayToAddFrom.getUnchecked (startIndex++)));
}
/** Inserts a new object into the array assuming that the array is sorted.
@ -544,10 +470,12 @@ public:
template <class ElementComparator>
int addSorted (ElementComparator& comparator, ObjectClass* const newObject) noexcept
{
ignoreUnused (comparator); // if you pass in an object with a static compareElements() method, this
// avoids getting warning messages about the parameter being unused
// If you pass in an object with a static compareElements() method, this
// avoids getting warning messages about the parameter being unused
ignoreUnused (comparator);
const ScopedLockType lock (getLock());
const int index = findInsertIndexInSortedArray (comparator, data.elements.get(), newObject, 0, numUsed);
const int index = findInsertIndexInSortedArray (comparator, values.begin(), newObject, 0, values.size());
insert (index, newObject);
return index;
}
@ -567,13 +495,16 @@ public:
template <typename ElementComparator>
int indexOfSorted (ElementComparator& comparator, const ObjectClass* const objectToLookFor) const noexcept
{
// If you pass in an object with a static compareElements() method, this
// avoids getting warning messages about the parameter being unused
ignoreUnused (comparator);
const ScopedLockType lock (getLock());
int s = 0, e = numUsed;
int s = 0, e = values.size();
while (s < e)
{
if (comparator.compareElements (objectToLookFor, data.elements[s]) == 0)
if (comparator.compareElements (objectToLookFor, values[s]) == 0)
return s;
auto halfway = (s + e) / 2;
@ -581,7 +512,7 @@ public:
if (halfway == s)
break;
if (comparator.compareElements (objectToLookFor, data.elements[halfway]) >= 0)
if (comparator.compareElements (objectToLookFor, values[halfway]) >= 0)
s = halfway;
else
e = halfway;
@ -608,22 +539,18 @@ public:
{
const ScopedLockType lock (getLock());
if (isPositiveAndBelow (indexToRemove, numUsed))
if (isPositiveAndBelow (indexToRemove, values.size()))
{
auto** e = data.elements + indexToRemove;
auto** e = values.begin() + indexToRemove;
if (deleteObject)
toDelete.reset (*e);
--numUsed;
auto numToShift = numUsed - indexToRemove;
if (numToShift > 0)
memmove (e, e + 1, sizeof (ObjectClass*) * (size_t) numToShift);
values.removeElements (indexToRemove, 1);
}
}
if ((numUsed << 1) < data.numAllocated)
if ((values.size() << 1) < values.capacity())
minimiseStorageOverheads();
}
@ -641,18 +568,13 @@ public:
ObjectClass* removedItem = nullptr;
const ScopedLockType lock (getLock());
if (isPositiveAndBelow (indexToRemove, numUsed))
if (isPositiveAndBelow (indexToRemove, values.size()))
{
auto** e = data.elements + indexToRemove;
removedItem = *e;
removedItem = values[indexToRemove];
--numUsed;
const int numToShift = numUsed - indexToRemove;
values.removeElements (indexToRemove, 1);
if (numToShift > 0)
memmove (e, e + 1, sizeof (ObjectClass*) * (size_t) numToShift);
if ((numUsed << 1) < data.numAllocated)
if ((values.size() << 1) < values.capacity())
minimiseStorageOverheads();
}
@ -670,11 +592,10 @@ public:
void removeObject (const ObjectClass* objectToRemove, bool deleteObject = true)
{
const ScopedLockType lock (getLock());
auto** e = data.elements.get();
for (int i = 0; i < numUsed; ++i)
for (int i = 0; i < values.size(); ++i)
{
if (objectToRemove == e[i])
if (objectToRemove == values[i])
{
remove (i, deleteObject);
break;
@ -698,32 +619,24 @@ public:
void removeRange (int startIndex, int numberToRemove, bool deleteObjects = true)
{
const ScopedLockType lock (getLock());
auto endIndex = jlimit (0, numUsed, startIndex + numberToRemove);
startIndex = jlimit (0, numUsed, startIndex);
auto endIndex = jlimit (0, values.size(), startIndex + numberToRemove);
startIndex = jlimit (0, values.size(), startIndex);
numberToRemove = endIndex - startIndex;
if (endIndex > startIndex)
if (numberToRemove > 0)
{
if (deleteObjects)
{
for (int i = startIndex; i < endIndex; ++i)
{
ContainerDeletePolicy<ObjectClass>::destroy (data.elements[i]);
data.elements[i] = nullptr; // (in case one of the destructors accesses this array and hits a dangling pointer)
ContainerDeletePolicy<ObjectClass>::destroy (values[i]);
values[i] = nullptr; // (in case one of the destructors accesses this array and hits a dangling pointer)
}
}
auto rangeSize = endIndex - startIndex;
auto** e = data.elements + startIndex;
auto numToShift = numUsed - endIndex;
numUsed -= rangeSize;
values.removeElements (startIndex, numberToRemove);
while (--numToShift >= 0)
{
*e = e[rangeSize];
++e;
}
if ((numUsed << 1) < data.numAllocated)
if ((values.size() << 1) < values.capacity())
minimiseStorageOverheads();
}
}
@ -739,10 +652,10 @@ public:
{
const ScopedLockType lock (getLock());
if (howManyToRemove >= numUsed)
if (howManyToRemove >= values.size())
clear (deleteObjects);
else
removeRange (numUsed - howManyToRemove, howManyToRemove, deleteObjects);
removeRange (values.size() - howManyToRemove, howManyToRemove, deleteObjects);
}
/** Swaps a pair of objects in the array.
@ -750,17 +663,10 @@ public:
If either of the indexes passed in is out-of-range, nothing will happen,
otherwise the two objects at these positions will be exchanged.
*/
void swap (int index1,
int index2) noexcept
void swap (int index1, int index2) noexcept
{
const ScopedLockType lock (getLock());
if (isPositiveAndBelow (index1, numUsed)
&& isPositiveAndBelow (index2, numUsed))
{
std::swap (data.elements[index1],
data.elements[index2]);
}
values.swap (index1, index2);
}
/** Moves one of the objects to a different position.
@ -781,29 +687,7 @@ public:
if (currentIndex != newIndex)
{
const ScopedLockType lock (getLock());
if (isPositiveAndBelow (currentIndex, numUsed))
{
if (! isPositiveAndBelow (newIndex, numUsed))
newIndex = numUsed - 1;
auto* value = data.elements[currentIndex];
if (newIndex > currentIndex)
{
memmove (data.elements + currentIndex,
data.elements + currentIndex + 1,
sizeof (ObjectClass*) * (size_t) (newIndex - currentIndex));
}
else
{
memmove (data.elements + newIndex + 1,
data.elements + newIndex,
sizeof (ObjectClass*) * (size_t) (currentIndex - newIndex));
}
data.elements[newIndex] = value;
}
values.move (currentIndex, newIndex);
}
}
@ -817,8 +701,7 @@ public:
{
const ScopedLockType lock1 (getLock());
const typename OtherArrayType::ScopedLockType lock2 (otherArray.getLock());
data.swapWith (otherArray.data);
std::swap (numUsed, otherArray.numUsed);
values.swapWith (otherArray.values);
}
//==============================================================================
@ -831,7 +714,7 @@ public:
void minimiseStorageOverheads() noexcept
{
const ScopedLockType lock (getLock());
data.shrinkToNoMoreThan (numUsed);
values.shrinkToNoMoreThan (values.size());
}
/** Increases the array's internal storage to hold a minimum number of elements.
@ -843,7 +726,7 @@ public:
void ensureStorageAllocated (const int minNumElements) noexcept
{
const ScopedLockType lock (getLock());
data.ensureAllocatedSize (minNumElements);
values.ensureAllocatedSize (minNumElements);
}
//==============================================================================
@ -876,13 +759,14 @@ public:
void sort (ElementComparator& comparator,
bool retainOrderOfEquivalentItems = false) const noexcept
{
ignoreUnused (comparator); // if you pass in an object with a static compareElements() method, this
// avoids getting warning messages about the parameter being unused
// If you pass in an object with a static compareElements() method, this
// avoids getting warning messages about the parameter being unused
ignoreUnused (comparator);
const ScopedLockType lock (getLock());
if (size() > 1)
sortArray (comparator, data.elements.get(), 0, size() - 1, retainOrderOfEquivalentItems);
sortArray (comparator, values.begin(), 0, size() - 1, retainOrderOfEquivalentItems);
}
//==============================================================================
@ -890,12 +774,11 @@ public:
To lock, you can call getLock().enter() and getLock().exit(), or preferably use
an object of ScopedLockType as an RAII lock for it.
*/
inline const TypeOfCriticalSectionToUse& getLock() const noexcept { return data; }
inline const TypeOfCriticalSectionToUse& getLock() const noexcept { return values; }
/** Returns the type of scoped lock to use for locking this array */
using ScopedLockType = typename TypeOfCriticalSectionToUse::ScopedLockType;
//==============================================================================
#ifndef DOXYGEN
// Note that the swapWithArray method has been replaced by a more flexible templated version,
@ -905,15 +788,19 @@ public:
private:
//==============================================================================
ArrayAllocationBase <ObjectClass*, TypeOfCriticalSectionToUse> data;
int numUsed = 0;
ArrayBase <ObjectClass*, TypeOfCriticalSectionToUse> values;
void deleteAllObjects()
{
while (numUsed > 0)
ContainerDeletePolicy<ObjectClass>::destroy (data.elements[--numUsed]);
for (auto& e : values)
ContainerDeletePolicy<ObjectClass>::destroy (e);
values.clear();
}
template <class OtherObjectClass, class OtherCriticalSection>
friend class OwnedArray;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OwnedArray)
};

View File

@ -0,0 +1,122 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2018 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
#if JUCE_UNIT_TESTS
class ReferenceCountedArrayTests : public UnitTest
{
public:
ReferenceCountedArrayTests() : UnitTest ("ReferenceCountedArray", "Containers") {}
//==============================================================================
void runTest() override
{
beginTest ("Add derived objects");
{
ReferenceCountedArray<TestDerivedObj> derivedArray;
derivedArray.add (static_cast<TestDerivedObj*> (new TestBaseObj()));
expectEquals (derivedArray.size(), 1);
expectEquals (derivedArray.getObjectPointer (0)->getReferenceCount(), 1);
expectEquals (derivedArray[0]->getReferenceCount(), 2);
for (auto o : derivedArray)
expectEquals (o->getReferenceCount(), 1);
ReferenceCountedArray<TestBaseObj> baseArray;
baseArray.addArray (derivedArray);
for (auto o : baseArray)
expectEquals (o->getReferenceCount(), 2);
derivedArray.clearQuick();
baseArray.clearQuick();
auto* baseObject = new TestBaseObj();
TestBaseObj::Ptr baseObjectPtr = baseObject;
expectEquals (baseObject->getReferenceCount(), 1);
auto* derivedObject = new TestDerivedObj();
TestDerivedObj::Ptr derivedObjectPtr = derivedObject;
expectEquals (derivedObject->getReferenceCount(), 1);
baseArray.add (baseObject);
baseArray.add (derivedObject);
for (auto o : baseArray)
expectEquals (o->getReferenceCount(), 2);
expectEquals (baseObject->getReferenceCount(), 2);
expectEquals (derivedObject->getReferenceCount(), 2);
derivedArray.add (derivedObject);
for (auto o : derivedArray)
expectEquals (o->getReferenceCount(), 3);
derivedArray.clearQuick();
baseArray.clearQuick();
expectEquals (baseObject->getReferenceCount(), 1);
expectEquals (derivedObject->getReferenceCount(), 1);
baseArray.add (baseObjectPtr);
#if JUCE_STRICT_REFCOUNTEDPOINTER
baseArray.add (derivedObjectPtr);
#else
baseArray.add (derivedObjectPtr.get());
#endif
for (auto o : baseArray)
expectEquals (o->getReferenceCount(), 2);
derivedArray.add (derivedObjectPtr);
for (auto o : derivedArray)
expectEquals (o->getReferenceCount(), 3);
}
}
private:
struct TestBaseObj : public ReferenceCountedObject
{
using Ptr = ReferenceCountedObjectPtr<TestBaseObj>;
TestBaseObj() = default;
};
struct TestDerivedObj : public TestBaseObj
{
using Ptr = ReferenceCountedObjectPtr<TestDerivedObj>;
TestDerivedObj() = default;
};
};
static ReferenceCountedArrayTests referenceCountedArrayTests;
#endif
} // namespace juce

View File

@ -56,17 +56,13 @@ public:
/** Creates an empty array.
@see ReferenceCountedObject, Array, OwnedArray
*/
ReferenceCountedArray() noexcept
{
}
ReferenceCountedArray() = default;
/** Creates a copy of another array */
ReferenceCountedArray (const ReferenceCountedArray& other) noexcept
{
const ScopedLockType lock (other.getLock());
numUsed = other.numUsed;
data.setAllocatedSize (numUsed);
memcpy (data.elements, other.getRawDataPointer(), (size_t) numUsed * sizeof (ObjectClass*));
values.addArray (other.begin(), other.size());
for (auto* o : *this)
if (o != nullptr)
@ -75,10 +71,8 @@ public:
/** Moves from another array */
ReferenceCountedArray (ReferenceCountedArray&& other) noexcept
: data (static_cast<ArrayAllocationBase<ObjectClass*, TypeOfCriticalSectionToUse>&&> (other.data)),
numUsed (other.numUsed)
: values (std::move (other.values))
{
other.numUsed = 0;
}
/** Creates a copy of another array */
@ -86,9 +80,7 @@ public:
ReferenceCountedArray (const ReferenceCountedArray<OtherObjectClass, OtherCriticalSection>& other) noexcept
{
const typename ReferenceCountedArray<OtherObjectClass, OtherCriticalSection>::ScopedLockType lock (other.getLock());
numUsed = other.size();
data.setAllocatedSize (numUsed);
memcpy (data.elements, other.getRawDataPointer(), (size_t) numUsed * sizeof (ObjectClass*));
values.addArray (other.begin(), other.size());
for (auto* o : *this)
if (o != nullptr)
@ -121,9 +113,7 @@ public:
ReferenceCountedArray& operator= (ReferenceCountedArray&& other) noexcept
{
releaseAllObjects();
data = static_cast<ArrayAllocationBase<ObjectClass*, TypeOfCriticalSectionToUse>&&> (other.data);
numUsed = other.numUsed;
other.numUsed = 0;
values = std::move (other.values);
return *this;
}
@ -137,13 +127,13 @@ public:
//==============================================================================
/** Removes all objects from the array.
Any objects in the array that whose reference counts drop to zero will be deleted.
Any objects in the array whose reference counts drop to zero will be deleted.
*/
void clear()
{
const ScopedLockType lock (getLock());
releaseAllObjects();
data.setAllocatedSize (0);
clearQuick();
values.setAllocatedSize (0);
}
/** Removes all objects from the array without freeing the array's allocated storage.
@ -159,13 +149,13 @@ public:
/** Returns the current number of objects in the array. */
inline int size() const noexcept
{
return numUsed;
return values.size();
}
/** Returns true if the array is empty, false otherwise. */
inline bool isEmpty() const noexcept
{
return numUsed == 0;
return size() == 0;
}
/** Returns a pointer to the object at this index in the array.
@ -178,7 +168,7 @@ public:
*/
inline ObjectClassPtr operator[] (int index) const noexcept
{
return getObjectPointer (index);
return ObjectClassPtr (getObjectPointer (index));
}
/** Returns a pointer to the object at this index in the array, without checking
@ -189,7 +179,7 @@ public:
*/
inline ObjectClassPtr getUnchecked (int index) const noexcept
{
return getObjectPointerUnchecked (index);
return ObjectClassPtr (getObjectPointerUnchecked (index));
}
/** Returns a raw pointer to the object at this index in the array.
@ -203,24 +193,16 @@ public:
inline ObjectClass* getObjectPointer (int index) const noexcept
{
const ScopedLockType lock (getLock());
if (isPositiveAndBelow (index, numUsed))
{
jassert (data.elements != nullptr);
return data.elements[index];
}
return {};
return values.getValueWithDefault (index);
}
/** Returns a raw pointer to the object at this index in the array, without checking
whether the index is in-range.
*/
inline ObjectClass* getObjectPointerUnchecked (const int index) const noexcept
inline ObjectClass* getObjectPointerUnchecked (int index) const noexcept
{
const ScopedLockType lock (getLock());
jassert (isPositiveAndBelow (index, numUsed) && data.elements != nullptr);
return data.elements[index];
return values[index];
}
/** Returns a pointer to the first object in the array.
@ -231,14 +213,7 @@ public:
inline ObjectClassPtr getFirst() const noexcept
{
const ScopedLockType lock (getLock());
if (numUsed > 0)
{
jassert (data.elements != nullptr);
return data.elements[0];
}
return {};
return values.getFirst();
}
/** Returns a pointer to the last object in the array.
@ -249,14 +224,7 @@ public:
inline ObjectClassPtr getLast() const noexcept
{
const ScopedLockType lock (getLock());
if (numUsed > 0)
{
jassert (data.elements != nullptr);
return data.elements[numUsed - 1];
}
return {};
return values.getLast();
}
/** Returns a pointer to the actual array data.
@ -265,7 +233,7 @@ public:
*/
inline ObjectClass** getRawDataPointer() const noexcept
{
return data.elements;
return values.begin();
}
//==============================================================================
@ -274,7 +242,7 @@ public:
*/
inline ObjectClass** begin() const noexcept
{
return data.elements;
return values.begin();
}
/** Returns a pointer to the element which follows the last element in the array.
@ -282,7 +250,15 @@ public:
*/
inline ObjectClass** end() const noexcept
{
return data.elements + numUsed;
return values.end();
}
/** Returns a pointer to the first element in the array.
This method is provided for compatibility with the standard C++ containers.
*/
inline ObjectClass** data() const noexcept
{
return begin();
}
//==============================================================================
@ -294,13 +270,13 @@ public:
int indexOf (const ObjectClass* objectToLookFor) const noexcept
{
const ScopedLockType lock (getLock());
auto** e = data.elements.get();
auto** endPointer = e + numUsed;
auto** e = values.begin();
auto** endPointer = values.end();
while (e != endPointer)
{
if (objectToLookFor == *e)
return static_cast<int> (e - data.elements.get());
return static_cast<int> (e - values.begin());
++e;
}
@ -308,6 +284,13 @@ public:
return -1;
}
/** Finds the index of the first occurrence of an object in the array.
@param objectToLookFor the object to look for
@returns the index at which the object was found, or -1 if it's not found
*/
int indexOf (const ObjectClassPtr& objectToLookFor) const noexcept { return indexOf (objectToLookFor.get()); }
/** Returns true if the array contains a specified object.
@param objectToLookFor the object to look for
@ -316,8 +299,8 @@ public:
bool contains (const ObjectClass* objectToLookFor) const noexcept
{
const ScopedLockType lock (getLock());
auto** e = data.elements.get();
auto** endPointer = e + numUsed;
auto** e = values.begin();
auto** endPointer = values.end();
while (e != endPointer)
{
@ -330,6 +313,13 @@ public:
return false;
}
/** Returns true if the array contains a specified object.
@param objectToLookFor the object to look for
@returns true if the object is in the array
*/
bool contains (const ObjectClassPtr& objectToLookFor) const noexcept { return contains (objectToLookFor.get()); }
/** Appends a new object to the end of the array.
This will increase the new object's reference count.
@ -337,12 +327,42 @@ public:
@param newObject the new object to add to the array
@see set, insert, addIfNotAlreadyThere, addSorted, addArray
*/
ObjectClass* add (ObjectClass* newObject) noexcept
ObjectClass* add (ObjectClass* newObject)
{
const ScopedLockType lock (getLock());
data.ensureAllocatedSize (numUsed + 1);
jassert (data.elements != nullptr);
data.elements[numUsed++] = newObject;
values.add (newObject);
if (newObject != nullptr)
newObject->incReferenceCount();
return newObject;
}
/** Appends a new object to the end of the array.
This will increase the new object's reference count.
@param newObject the new object to add to the array
@see set, insert, addIfNotAlreadyThere, addSorted, addArray
*/
ObjectClass* add (const ObjectClassPtr& newObject) { return add (newObject.get()); }
/** Inserts a new object into the array at the given index.
If the index is less than 0 or greater than the size of the array, the
element will be added to the end of the array.
Otherwise, it will be inserted into the array, moving all the later elements
along to make room.
This will increase the new object's reference count.
@param indexToInsertAt the index at which the new element should be inserted
@param newObject the new object to add to the array
@see add, addSorted, addIfNotAlreadyThere, set
*/
ObjectClass* insert (int indexToInsertAt, ObjectClass* newObject)
{
values.insert (indexToInsertAt, newObject, 1);
if (newObject != nullptr)
newObject->incReferenceCount();
@ -363,32 +383,25 @@ public:
@param newObject the new object to add to the array
@see add, addSorted, addIfNotAlreadyThere, set
*/
ObjectClass* insert (int indexToInsertAt, ObjectClass* newObject) noexcept
{
if (indexToInsertAt < 0)
return add (newObject);
ObjectClass* insert (int indexToInsertAt, const ObjectClassPtr& newObject) { return insert (indexToInsertAt, newObject.get()); }
/** Appends a new object at the end of the array as long as the array doesn't
already contain it.
If the array already contains a matching object, nothing will be done.
@param newObject the new object to add to the array
@returns true if the object has been added, false otherwise
*/
bool addIfNotAlreadyThere (ObjectClass* newObject)
{
const ScopedLockType lock (getLock());
if (indexToInsertAt > numUsed)
indexToInsertAt = numUsed;
if (contains (newObject))
return false;
data.ensureAllocatedSize (numUsed + 1);
jassert (data.elements != nullptr);
auto** e = data.elements + indexToInsertAt;
auto numToMove = numUsed - indexToInsertAt;
if (numToMove > 0)
memmove (e + 1, e, sizeof (ObjectClass*) * (size_t) numToMove);
*e = newObject;
if (newObject != nullptr)
newObject->incReferenceCount();
++numUsed;
return newObject;
add (newObject);
return true;
}
/** Appends a new object at the end of the array as long as the array doesn't
@ -399,16 +412,7 @@ public:
@param newObject the new object to add to the array
@returns true if the object has been added, false otherwise
*/
bool addIfNotAlreadyThere (ObjectClass* newObject) noexcept
{
const ScopedLockType lock (getLock());
if (contains (newObject))
return false;
add (newObject);
return true;
}
bool addIfNotAlreadyThere (const ObjectClassPtr& newObject) { return addIfNotAlreadyThere (newObject.get()); }
/** Replaces an object in the array with a different one.
@ -431,16 +435,14 @@ public:
if (newObject != nullptr)
newObject->incReferenceCount();
if (indexToChange < numUsed)
if (indexToChange < values.size())
{
releaseObject (data.elements[indexToChange]);
data.elements[indexToChange] = newObject;
releaseObject (values[indexToChange]);
values[indexToChange] = newObject;
}
else
{
data.ensureAllocatedSize (numUsed + 1);
jassert (data.elements != nullptr);
data.elements[numUsed++] = newObject;
values.add (newObject);
}
}
}
@ -463,22 +465,11 @@ public:
{
const ScopedLockType lock2 (getLock());
if (startIndex < 0)
{
jassertfalse;
startIndex = 0;
}
auto numElementsAdded = values.addArray (arrayToAddFrom.values, startIndex, numElementsToAdd);
auto** e = values.end();
if (numElementsToAdd < 0 || startIndex + numElementsToAdd > arrayToAddFrom.size())
numElementsToAdd = arrayToAddFrom.size() - startIndex;
if (numElementsToAdd > 0)
{
data.ensureAllocatedSize (numUsed + numElementsToAdd);
while (--numElementsToAdd >= 0)
add (arrayToAddFrom.getUnchecked (startIndex++));
}
for (int i = 0; i < numElementsAdded; ++i)
(*(--e))->incReferenceCount();
}
}
@ -498,7 +489,7 @@ public:
int addSorted (ElementComparator& comparator, ObjectClass* newObject) noexcept
{
const ScopedLockType lock (getLock());
auto index = findInsertIndexInSortedArray (comparator, data.elements.get(), newObject, 0, numUsed);
auto index = findInsertIndexInSortedArray (comparator, values.begin(), newObject, 0, values.size());
insert (index, newObject);
return index;
}
@ -512,9 +503,9 @@ public:
void addOrReplaceSorted (ElementComparator& comparator, ObjectClass* newObject) noexcept
{
const ScopedLockType lock (getLock());
auto index = findInsertIndexInSortedArray (comparator, data.elements.get(), newObject, 0, numUsed);
auto index = findInsertIndexInSortedArray (comparator, values.begin(), newObject, 0, values.size());
if (index > 0 && comparator.compareElements (newObject, data.elements[index - 1]) == 0)
if (index > 0 && comparator.compareElements (newObject, values[index - 1]) == 0)
set (index - 1, newObject); // replace an existing object that matches
else
insert (index, newObject); // no match, so insert the new one
@ -538,11 +529,11 @@ public:
{
ignoreUnused (comparator);
const ScopedLockType lock (getLock());
int s = 0, e = numUsed;
int s = 0, e = values.size();
while (s < e)
{
if (comparator.compareElements (objectToLookFor, data.elements[s]) == 0)
if (comparator.compareElements (objectToLookFor, values[s]) == 0)
return s;
auto halfway = (s + e) / 2;
@ -550,7 +541,7 @@ public:
if (halfway == s)
break;
if (comparator.compareElements (objectToLookFor, data.elements[halfway]) >= 0)
if (comparator.compareElements (objectToLookFor, values[halfway]) >= 0)
s = halfway;
else
e = halfway;
@ -577,17 +568,13 @@ public:
{
const ScopedLockType lock (getLock());
if (isPositiveAndBelow (indexToRemove, numUsed))
if (isPositiveAndBelow (indexToRemove, values.size()))
{
auto** e = data.elements + indexToRemove;
auto** e = values.begin() + indexToRemove;
releaseObject (*e);
--numUsed;
auto numberToShift = numUsed - indexToRemove;
values.removeElements (indexToRemove, 1);
if (numberToShift > 0)
memmove (e, e + 1, sizeof (ObjectClass*) * (size_t) numberToShift);
if ((numUsed << 1) < data.numAllocated)
if ((values.size() << 1) < values.capacity())
minimiseStorageOverheads();
}
}
@ -606,18 +593,14 @@ public:
ObjectClassPtr removedItem;
const ScopedLockType lock (getLock());
if (isPositiveAndBelow (indexToRemove, numUsed))
if (isPositiveAndBelow (indexToRemove, values.size()))
{
auto** e = data.elements + indexToRemove;
auto** e = values.begin() + indexToRemove;
removedItem = *e;
releaseObject (*e);
--numUsed;
auto numberToShift = numUsed - indexToRemove;
values.removeElements (indexToRemove, 1);
if (numberToShift > 0)
memmove (e, e + 1, sizeof (ObjectClass*) * (size_t) numberToShift);
if ((numUsed << 1) < data.numAllocated)
if ((values.size() << 1) < values.capacity())
minimiseStorageOverheads();
}
@ -638,6 +621,16 @@ public:
remove (indexOf (objectToRemove));
}
/** Removes the first occurrence of a specified object from the array.
If the item isn't found, no action is taken. If it is found, it is
removed and has its reference count decreased.
@param objectToRemove the object to try to remove
@see remove, removeRange
*/
void removeObject (const ObjectClassPtr& objectToRemove) { removeObject (objectToRemove.get()); }
/** Removes a range of objects from the array.
This will remove a set of objects, starting from the given index,
@ -657,30 +650,21 @@ public:
int numberToRemove)
{
const ScopedLockType lock (getLock());
startIndex = jlimit (0, values.size(), startIndex);
auto endIndex = jlimit (0, values.size(), startIndex + numberToRemove);
numberToRemove = endIndex - startIndex;
auto start = jlimit (0, numUsed, startIndex);
auto endIndex = jlimit (0, numUsed, startIndex + numberToRemove);
if (endIndex > start)
if (numberToRemove > 0)
{
for (int i = start; i < endIndex; ++i)
for (int i = startIndex; i < endIndex; ++i)
{
releaseObject (data.elements[i]);
data.elements[i] = nullptr; // (in case one of the destructors accesses this array and hits a dangling pointer)
releaseObject (values[i]);
values[i] = nullptr; // (in case one of the destructors accesses this array and hits a dangling pointer)
}
auto rangeSize = endIndex - start;
auto** e = data.elements + start;
int i = numUsed - endIndex;
numUsed -= rangeSize;
values.removeElements (startIndex, numberToRemove);
while (--i >= 0)
{
*e = e[rangeSize];
++e;
}
if ((numUsed << 1) < data.numAllocated)
if ((values.size() << 1) < values.capacity())
minimiseStorageOverheads();
}
}
@ -697,11 +681,11 @@ public:
{
const ScopedLockType lock (getLock());
if (howManyToRemove > numUsed)
howManyToRemove = numUsed;
if (howManyToRemove > values.size())
howManyToRemove = values.size();
while (--howManyToRemove >= 0)
remove (numUsed - 1);
remove (values.size() - 1);
}
/** Swaps a pair of objects in the array.
@ -713,11 +697,10 @@ public:
{
const ScopedLockType lock (getLock());
if (isPositiveAndBelow (index1, numUsed)
&& isPositiveAndBelow (index2, numUsed))
if (isPositiveAndBelow (index1, values.size())
&& isPositiveAndBelow (index2, values.size()))
{
std::swap (data.elements[index1],
data.elements[index2]);
std::swap (values[index1], values[index2]);
}
}
@ -739,29 +722,7 @@ public:
if (currentIndex != newIndex)
{
const ScopedLockType lock (getLock());
if (isPositiveAndBelow (currentIndex, numUsed))
{
if (! isPositiveAndBelow (newIndex, numUsed))
newIndex = numUsed - 1;
auto* value = data.elements[currentIndex];
if (newIndex > currentIndex)
{
memmove (data.elements + currentIndex,
data.elements + currentIndex + 1,
sizeof (ObjectClass*) * (size_t) (newIndex - currentIndex));
}
else
{
memmove (data.elements + newIndex + 1,
data.elements + newIndex,
sizeof (ObjectClass*) * (size_t) (currentIndex - newIndex));
}
data.elements[newIndex] = value;
}
values.move (currentIndex, newIndex);
}
}
@ -776,8 +737,7 @@ public:
{
const ScopedLockType lock1 (getLock());
const typename OtherArrayType::ScopedLockType lock2 (otherArray.getLock());
data.swapWith (otherArray.data);
std::swap (numUsed, otherArray.numUsed);
values.swapWith (otherArray.values);
}
//==============================================================================
@ -789,15 +749,7 @@ public:
{
const ScopedLockType lock2 (other.getLock());
const ScopedLockType lock1 (getLock());
if (numUsed != other.numUsed)
return false;
for (int i = numUsed; --i >= 0;)
if (data.elements[i] != other.data.elements[i])
return false;
return true;
return values == other.values;
}
/** Compares this array to another one.
@ -840,11 +792,12 @@ public:
void sort (ElementComparator& comparator,
bool retainOrderOfEquivalentItems = false) const noexcept
{
ignoreUnused (comparator); // if you pass in an object with a static compareElements() method, this
// avoids getting warning messages about the parameter being unused
// If you pass in an object with a static compareElements() method, this
// avoids getting warning messages about the parameter being unused
ignoreUnused (comparator);
const ScopedLockType lock (getLock());
sortArray (comparator, data.elements.get(), 0, size() - 1, retainOrderOfEquivalentItems);
sortArray (comparator, values.begin(), 0, values.size() - 1, retainOrderOfEquivalentItems);
}
//==============================================================================
@ -857,7 +810,7 @@ public:
void minimiseStorageOverheads() noexcept
{
const ScopedLockType lock (getLock());
data.shrinkToNoMoreThan (numUsed);
values.shrinkToNoMoreThan (values.size());
}
/** Increases the array's internal storage to hold a minimum number of elements.
@ -869,7 +822,7 @@ public:
void ensureStorageAllocated (const int minNumElements)
{
const ScopedLockType lock (getLock());
data.ensureAllocatedSize (minNumElements);
values.ensureAllocatedSize (minNumElements);
}
//==============================================================================
@ -877,12 +830,11 @@ public:
To lock, you can call getLock().enter() and getLock().exit(), or preferably use
an object of ScopedLockType as an RAII lock for it.
*/
inline const TypeOfCriticalSectionToUse& getLock() const noexcept { return data; }
inline const TypeOfCriticalSectionToUse& getLock() const noexcept { return values; }
/** Returns the type of scoped lock to use for locking this array */
using ScopedLockType = typename TypeOfCriticalSectionToUse::ScopedLockType;
//==============================================================================
#ifndef DOXYGEN
// Note that the swapWithArray method has been replaced by a more flexible templated version,
@ -892,15 +844,14 @@ public:
private:
//==============================================================================
ArrayAllocationBase<ObjectClass*, TypeOfCriticalSectionToUse> data;
int numUsed = 0;
ArrayBase<ObjectClass*, TypeOfCriticalSectionToUse> values;
void releaseAllObjects()
{
while (numUsed > 0)
releaseObject (data.elements[--numUsed]);
for (auto& v : values)
releaseObject (v);
jassert (numUsed == 0);
values.clear();
}
static void releaseObject (ObjectClass* o)

View File

@ -59,24 +59,24 @@ public:
//==============================================================================
/** Creates an empty set. */
// VS2013 doesn't allow defaulted noexcept constructors.
SortedSet() noexcept {}
SortedSet() = default;
/** Creates a copy of another set. */
SortedSet (const SortedSet&) = default;
/** Creates a copy of another set. */
// VS2013 doesn't allow defaulted noexcept constructors.
SortedSet (SortedSet&& other) noexcept : data (static_cast<decltype(data)&&> (other.data)) {}
SortedSet (SortedSet&& other) noexcept : data (std::move (other.data)) {}
/** Makes a copy of another set. */
SortedSet& operator= (const SortedSet&) = default;
/** Makes a copy of another set. */
// VS2013 doesn't allow defaulted noexcept constructors.
SortedSet& operator= (SortedSet&& other) noexcept { data = static_cast<decltype(data)&&> (other.data); return *this; }
SortedSet& operator= (SortedSet&& other) noexcept { data = std::move (other.data); return *this; }
/** Destructor. */
~SortedSet() noexcept {}
~SortedSet() = default;
//==============================================================================
/** Compares this set to another one.

View File

@ -41,13 +41,13 @@ class SparseSet
{
public:
//==============================================================================
SparseSet() noexcept {}
SparseSet() = default;
SparseSet (const SparseSet&) = default;
SparseSet& operator= (const SparseSet&) = default;
SparseSet (SparseSet&& other) noexcept : ranges (static_cast<Array<Range<Type>>&&> (other.ranges)) {}
SparseSet& operator= (SparseSet&& other) noexcept { ranges = static_cast<Array<Range<Type>>&&> (other.ranges); return *this; }
SparseSet (SparseSet&& other) noexcept : ranges (std::move (other.ranges)) {}
SparseSet& operator= (SparseSet&& other) noexcept { ranges = std::move (other.ranges); return *this; }
//==============================================================================
/** Clears the set. */

View File

@ -53,17 +53,18 @@ public:
virtual MemoryBlock* toBinary (const ValueUnion&) const noexcept { return nullptr; }
virtual var clone (const var& original) const { return original; }
virtual bool isVoid() const noexcept { return false; }
virtual bool isUndefined() const noexcept { return false; }
virtual bool isInt() const noexcept { return false; }
virtual bool isInt64() const noexcept { return false; }
virtual bool isBool() const noexcept { return false; }
virtual bool isDouble() const noexcept { return false; }
virtual bool isString() const noexcept { return false; }
virtual bool isObject() const noexcept { return false; }
virtual bool isArray() const noexcept { return false; }
virtual bool isBinary() const noexcept { return false; }
virtual bool isMethod() const noexcept { return false; }
virtual bool isVoid() const noexcept { return false; }
virtual bool isUndefined() const noexcept { return false; }
virtual bool isInt() const noexcept { return false; }
virtual bool isInt64() const noexcept { return false; }
virtual bool isBool() const noexcept { return false; }
virtual bool isDouble() const noexcept { return false; }
virtual bool isString() const noexcept { return false; }
virtual bool isObject() const noexcept { return false; }
virtual bool isArray() const noexcept { return false; }
virtual bool isBinary() const noexcept { return false; }
virtual bool isMethod() const noexcept { return false; }
virtual bool isComparable() const noexcept { return false; }
virtual void cleanUp (ValueUnion&) const noexcept {}
virtual void createCopy (ValueUnion& dest, const ValueUnion& source) const { dest = source; }
@ -78,7 +79,8 @@ public:
VariantType_Void() noexcept {}
static const VariantType_Void instance;
bool isVoid() const noexcept override { return true; }
bool isVoid() const noexcept override { return true; }
bool isComparable() const noexcept override { return true; }
bool equals (const ValueUnion&, const ValueUnion&, const VariantType& otherType) const noexcept override { return otherType.isVoid() || otherType.isUndefined(); }
void writeToStream (const ValueUnion&, OutputStream& output) const override { output.writeCompressedInt (0); }
};
@ -114,6 +116,7 @@ public:
String toString (const ValueUnion& data) const override { return String (data.intValue); }
bool toBool (const ValueUnion& data) const noexcept override { return data.intValue != 0; }
bool isInt() const noexcept override { return true; }
bool isComparable() const noexcept override { return true; }
bool equals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) const noexcept override
{
@ -144,6 +147,7 @@ public:
String toString (const ValueUnion& data) const override { return String (data.int64Value); }
bool toBool (const ValueUnion& data) const noexcept override { return data.int64Value != 0; }
bool isInt64() const noexcept override { return true; }
bool isComparable() const noexcept override { return true; }
bool equals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) const noexcept override
{
@ -171,9 +175,10 @@ public:
int toInt (const ValueUnion& data) const noexcept override { return (int) data.doubleValue; }
int64 toInt64 (const ValueUnion& data) const noexcept override { return (int64) data.doubleValue; }
double toDouble (const ValueUnion& data) const noexcept override { return data.doubleValue; }
String toString (const ValueUnion& data) const override { return String (data.doubleValue, 20); }
String toString (const ValueUnion& data) const override { return serialiseDouble (data.doubleValue); }
bool toBool (const ValueUnion& data) const noexcept override { return data.doubleValue != 0.0; }
bool isDouble() const noexcept override { return true; }
bool isComparable() const noexcept override { return true; }
bool equals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) const noexcept override
{
@ -201,6 +206,7 @@ public:
String toString (const ValueUnion& data) const override { return String::charToString (data.boolValue ? (juce_wchar) '1' : (juce_wchar) '0'); }
bool toBool (const ValueUnion& data) const noexcept override { return data.boolValue; }
bool isBool() const noexcept override { return true; }
bool isComparable() const noexcept override { return true; }
bool equals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) const noexcept override
{
@ -232,6 +238,7 @@ public:
bool toBool (const ValueUnion& data) const noexcept override { return getString (data)->getIntValue() != 0
|| getString (data)->trim().equalsIgnoreCase ("true")
|| getString (data)->trim().equalsIgnoreCase ("yes"); }
bool isComparable() const noexcept override { return true; }
bool equals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) const noexcept override
{
@ -356,7 +363,7 @@ public:
struct RefCountedArray : public ReferenceCountedObject
{
RefCountedArray (const Array<var>& a) : array (a) { incReferenceCount(); }
RefCountedArray (Array<var>&& a) : array (static_cast<Array<var>&&> (a)) { incReferenceCount(); }
RefCountedArray (Array<var>&& a) : array (std::move (a)) { incReferenceCount(); }
Array<var> array;
};
};
@ -536,24 +543,24 @@ var& var::operator= (var&& other) noexcept
var::var (String&& v) : type (&VariantType_String::instance)
{
new (value.stringValue) String (static_cast<String&&> (v));
new (value.stringValue) String (std::move (v));
}
var::var (MemoryBlock&& v) : type (&VariantType_Binary::instance)
{
value.binaryValue = new MemoryBlock (static_cast<MemoryBlock&&> (v));
value.binaryValue = new MemoryBlock (std::move (v));
}
var::var (Array<var>&& v) : type (&VariantType_Array::instance)
{
value.objectValue = new VariantType_Array::RefCountedArray (static_cast<Array<var>&&> (v));
value.objectValue = new VariantType_Array::RefCountedArray (std::move (v));
}
var& var::operator= (String&& v)
{
type->cleanUp (value);
type = &VariantType_String::instance;
new (value.stringValue) String (static_cast<String&&> (v));
new (value.stringValue) String (std::move (v));
return *this;
}
@ -565,7 +572,7 @@ bool var::equals (const var& other) const noexcept
bool var::equalsWithSameType (const var& other) const noexcept
{
return type == other.type && equals (other);
return hasSameTypeAs (other) && equals (other);
}
bool var::hasSameTypeAs (const var& other) const noexcept
@ -573,12 +580,31 @@ bool var::hasSameTypeAs (const var& other) const noexcept
return type == other.type;
}
bool operator== (const var& v1, const var& v2) noexcept { return v1.equals (v2); }
bool operator!= (const var& v1, const var& v2) noexcept { return ! v1.equals (v2); }
bool operator== (const var& v1, const String& v2) { return v1.toString() == v2; }
bool operator!= (const var& v1, const String& v2) { return v1.toString() != v2; }
bool operator== (const var& v1, const char* const v2) { return v1.toString() == v2; }
bool operator!= (const var& v1, const char* const v2) { return v1.toString() != v2; }
bool canCompare (const var& v1, const var& v2)
{
return v1.type->isComparable() && v2.type->isComparable();
}
static int compare (const var& v1, const var& v2)
{
if (v1.isString() && v2.isString())
return v1.toString().compare (v2.toString());
auto diff = static_cast<double> (v1) - static_cast<double> (v2);
return diff == 0 ? 0 : (diff < 0 ? -1 : 1);
}
bool operator== (const var& v1, const var& v2) { return v1.equals (v2); }
bool operator!= (const var& v1, const var& v2) { return ! v1.equals (v2); }
bool operator< (const var& v1, const var& v2) { return canCompare (v1, v2) && compare (v1, v2) < 0; }
bool operator> (const var& v1, const var& v2) { return canCompare (v1, v2) && compare (v1, v2) > 0; }
bool operator<= (const var& v1, const var& v2) { return canCompare (v1, v2) && compare (v1, v2) <= 0; }
bool operator>= (const var& v1, const var& v2) { return canCompare (v1, v2) && compare (v1, v2) >= 0; }
bool operator== (const var& v1, const String& v2) { return v1.toString() == v2; }
bool operator!= (const var& v1, const String& v2) { return v1.toString() != v2; }
bool operator== (const var& v1, const char* v2) { return v1.toString() == v2; }
bool operator!= (const var& v1, const char* v2) { return v1.toString() != v2; }
//==============================================================================
var var::clone() const noexcept
@ -666,7 +692,7 @@ var var::call (const Identifier& method, const var& arg1, const var& arg2, const
//==============================================================================
int var::size() const
{
if (auto* array = getArray())
if (auto array = getArray())
return array->size();
return 0;
@ -674,7 +700,7 @@ int var::size() const
const var& var::operator[] (int arrayIndex) const
{
auto* array = getArray();
auto array = getArray();
// When using this method, the var must actually be an array, and the index
// must be in-range!
@ -685,7 +711,7 @@ const var& var::operator[] (int arrayIndex) const
var& var::operator[] (int arrayIndex)
{
auto* array = getArray();
auto array = getArray();
// When using this method, the var must actually be an array, and the index
// must be in-range!
@ -696,10 +722,11 @@ var& var::operator[] (int arrayIndex)
Array<var>* var::convertToArray()
{
if (auto* array = getArray())
if (auto array = getArray())
return array;
Array<var> tempVar;
if (! isVoid())
tempVar.add (*this);
@ -714,7 +741,7 @@ void var::append (const var& n)
void var::remove (const int index)
{
if (auto* const array = getArray())
if (auto array = getArray())
array->remove (index);
}
@ -730,7 +757,7 @@ void var::resize (const int numArrayElementsWanted)
int var::indexOf (const var& n) const
{
if (auto* const array = getArray())
if (auto array = getArray())
return array->indexOf (n);
return -1;
@ -755,6 +782,7 @@ var var::readFromStream (InputStream& input)
case varMarker_BoolTrue: return var (true);
case varMarker_BoolFalse: return var (false);
case varMarker_Double: return var (input.readDouble());
case varMarker_String:
{
MemoryOutputStream mo;

View File

@ -286,18 +286,18 @@ public:
private:
//==============================================================================
class VariantType; friend class VariantType;
class VariantType_Void; friend class VariantType_Void;
class VariantType_Undefined; friend class VariantType_Undefined;
class VariantType_Int; friend class VariantType_Int;
class VariantType_Int64; friend class VariantType_Int64;
class VariantType_Double; friend class VariantType_Double;
class VariantType_Bool; friend class VariantType_Bool;
class VariantType_String; friend class VariantType_String;
class VariantType_Object; friend class VariantType_Object;
class VariantType_Array; friend class VariantType_Array;
class VariantType_Binary; friend class VariantType_Binary;
class VariantType_Method; friend class VariantType_Method;
class VariantType;
class VariantType_Void;
class VariantType_Undefined;
class VariantType_Int;
class VariantType_Int64;
class VariantType_Double;
class VariantType_Bool;
class VariantType_String;
class VariantType_Object;
class VariantType_Array;
class VariantType_Binary;
class VariantType_Method;
union ValueUnion
{
@ -311,17 +311,32 @@ private:
NativeFunction* methodValue;
};
friend bool canCompare (const var&, const var&);
const VariantType* type;
ValueUnion value;
Array<var>* convertToArray();
var (const VariantType&) noexcept;
// This is needed to prevent the wrong constructor/operator being called
var (const ReferenceCountedObject*) = delete;
var& operator= (const ReferenceCountedObject*) = delete;
};
/** Compares the values of two var objects, using the var::equals() comparison. */
JUCE_API bool operator== (const var&, const var&) noexcept;
JUCE_API bool operator== (const var&, const var&);
/** Compares the values of two var objects, using the var::equals() comparison. */
JUCE_API bool operator!= (const var&, const var&) noexcept;
JUCE_API bool operator!= (const var&, const var&);
/** Compares the values of two var objects, using the var::equals() comparison. */
JUCE_API bool operator< (const var&, const var&);
/** Compares the values of two var objects, using the var::equals() comparison. */
JUCE_API bool operator<= (const var&, const var&);
/** Compares the values of two var objects, using the var::equals() comparison. */
JUCE_API bool operator> (const var&, const var&);
/** Compares the values of two var objects, using the var::equals() comparison. */
JUCE_API bool operator>= (const var&, const var&);
JUCE_API bool operator== (const var&, const String&);
JUCE_API bool operator!= (const var&, const String&);
JUCE_API bool operator== (const var&, const char*);

View File

@ -118,9 +118,8 @@ public:
private:
//==============================================================================
class NativeIterator
struct NativeIterator
{
public:
NativeIterator (const File& directory, const String& wildCard);
~NativeIterator();
@ -129,16 +128,11 @@ private:
Time* modTime, Time* creationTime, bool* isReadOnly);
class Pimpl;
private:
friend class DirectoryIterator;
friend struct ContainerDeletePolicy<Pimpl>;
std::unique_ptr<Pimpl> pimpl;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (NativeIterator)
};
friend struct ContainerDeletePolicy<NativeIterator::Pimpl>;
StringArray wildCards;
NativeIterator fileFinder;
String wildCard, path;

View File

@ -53,17 +53,17 @@ File& File::operator= (const File& other)
}
File::File (File&& other) noexcept
: fullPath (static_cast<String&&> (other.fullPath))
: fullPath (std::move (other.fullPath))
{
}
File& File::operator= (File&& other) noexcept
{
fullPath = static_cast<String&&> (other.fullPath);
fullPath = std::move (other.fullPath);
return *this;
}
JUCE_DECLARE_DEPRECATED_STATIC (const File File::nonexistent;)
JUCE_DECLARE_DEPRECATED_STATIC (const File File::nonexistent{};)
//==============================================================================
static String removeEllipsis (const String& path)
@ -256,13 +256,13 @@ bool File::setExecutePermission (bool shouldBeExecutable) const
return setFileExecutableInternal (shouldBeExecutable);
}
bool File::deleteRecursively() const
bool File::deleteRecursively (bool followSymlinks) const
{
bool worked = true;
if (isDirectory())
if (isDirectory() && (followSymlinks || ! isSymbolicLink()))
for (auto& f : findChildFiles (File::findFilesAndDirectories, false))
worked = f.deleteRecursively() && worked;
worked = f.deleteRecursively (followSymlinks) && worked;
return deleteFile() && worked;
}

View File

@ -48,7 +48,7 @@ public:
You can use its operator= method to point it at a proper file.
*/
File() noexcept {}
File() = default;
/** Creates a file from an absolute path.
@ -66,7 +66,7 @@ public:
File (const File&);
/** Destructor. */
~File() noexcept {}
~File() = default;
/** Sets the file based on an absolute pathname.
@ -114,7 +114,7 @@ public:
bool isDirectory() const;
/** Checks whether the path of this file represents the root of a file system,
irrespective of its existance.
irrespective of its existence.
This will return true for "C:", "D:", etc on Windows and "/" on other
platforms.
@ -457,6 +457,9 @@ public:
If this file is actually a directory, it may not be deleted correctly if it
contains files. See deleteRecursively() as a better way of deleting directories.
If this file is a symlink, then the symlink will be deleted and not the target
of the symlink.
@returns true if the file has been successfully deleted (or if it didn't exist to
begin with).
@see deleteRecursively
@ -468,11 +471,14 @@ public:
If this file is a directory, this will try to delete it and all its subfolders. If
it's just a file, it will just try to delete the file.
@returns true if the file and all its subfolders have been successfully deleted
(or if it didn't exist to begin with).
@param followSymlinks If true, then any symlink pointing to a directory will also
recursively delete the contents of that directory
@returns true if the file and all its subfolders have been successfully
deleted (or if it didn't exist to begin with).
@see deleteFile
*/
bool deleteRecursively() const;
bool deleteRecursively (bool followSymlinks = false) const;
/** Moves this file or folder to the trash.
@ -601,6 +607,17 @@ public:
//==============================================================================
/** Creates a stream to read from this file.
Note that this is an old method, and actually it's usually best to avoid it and
instead use an RAII pattern with an FileInputStream directly, e.g.
@code
FileInputStream input (fileToOpen);
if (input.openedOk())
{
input.read (etc...
}
@endcode
@returns a stream that will read from this file (initially positioned at the
start of the file), or nullptr if the file can't be opened for some reason
@see createOutputStream, loadFileAsData
@ -609,9 +626,30 @@ public:
/** Creates a stream to write to this file.
Note that this is an old method, and actually it's usually best to avoid it and
instead use an RAII pattern with an FileOutputStream directly, e.g.
@code
FileOutputStream output (fileToOpen);
if (output.openedOk())
{
output.read etc...
}
@endcode
If the file exists, the stream that is returned will be positioned ready for
writing at the end of the file, so you might want to use deleteFile() first
to write to an empty file.
writing at the end of the file. If you want to write to the start of the file,
replacing the existing content, then you can do the following:
@code
FileOutputStream output (fileToOverwrite);
if (output.openedOk())
{
output.setPosition (0);
output.truncate();
...
}
@endcode
@returns a stream that will write to this file (initially positioned at the
end of the file), or nullptr if the file can't be opened for some reason

View File

@ -76,4 +76,79 @@ bool FileInputStream::setPosition (int64 pos)
return currentPosition == pos;
}
//==============================================================================
#if JUCE_UNIT_TESTS
struct FileInputStreamTests : public UnitTest
{
FileInputStreamTests()
: UnitTest ("FileInputStream", "Streams")
{}
void runTest() override
{
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());
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

@ -44,7 +44,7 @@ public:
explicit FileInputStream (const File& fileToRead);
/** Destructor. */
~FileInputStream();
~FileInputStream() override;
//==============================================================================
/** Returns the file that this stream is reading from. */

View File

@ -42,9 +42,20 @@ public:
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,
use File::deleteFile() before opening the stream, or use setPosition(0)
after it's opened (although this won't truncate the file).
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
@ -56,7 +67,7 @@ public:
size_t bufferSizeToUse = 16384);
/** Destructor. */
~FileOutputStream();
~FileOutputStream() override;
//==============================================================================
/** Returns the file that this stream is writing to.

View File

@ -71,7 +71,7 @@ public:
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,
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.
*/

View File

@ -35,7 +35,8 @@ static File createTempFile (const File& parentDirectory, String name,
TemporaryFile::TemporaryFile (const String& suffix, const int optionFlags)
: temporaryFile (createTempFile (File::getSpecialLocation (File::tempDirectory),
"temp_" + String::toHexString (Random::getSystemRandom().nextInt()),
suffix, optionFlags))
suffix, optionFlags)),
targetFile()
{
}

View File

@ -41,12 +41,10 @@ namespace juce
TemporaryFile temp (myTargetFile);
// create a stream to the temporary file, and write some data to it...
std::unique_ptr<FileOutputStream> out (temp.getFile().createOutputStream());
if (out != nullptr)
if (auto out = std::unique_ptr<FileOutputStream> (temp.getFile().createOutputStream()))
{
out->write ( ...etc )
out = nullptr; // (deletes the stream)
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.

View File

@ -57,7 +57,7 @@ public:
const String& description);
/** Destructor. */
~WildcardFileFilter();
~WildcardFileFilter() override;
//==============================================================================
/** Returns true if the filename matches one of the patterns specified. */

View File

@ -348,7 +348,16 @@ struct JSONFormatter
}
else if (v.isDouble())
{
out << String (static_cast<double> (v), maximumDecimalPlaces);
auto d = static_cast<double> (v);
if (juce_isfinite (d))
{
out << serialiseDouble (d);
}
else
{
out << "null";
}
}
else if (v.isArray())
{
@ -617,31 +626,57 @@ public:
void runTest() override
{
beginTest ("JSON");
Random 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;
beginTest ("JSON");
if (i > 0)
v = createRandomVar (r, 0);
Random r = getRandom();
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);
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);
}
}
};

View File

@ -92,12 +92,13 @@ public:
/** 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.
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 = 20);
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
@ -109,13 +110,14 @@ public:
/** 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.
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 = 20);
int maximumDecimalPlaces = 15);
/** Returns a version of a string with any extended characters escaped. */
static String escapeString (StringRef);

View File

@ -81,13 +81,13 @@ struct JavascriptEngine::RootObject : public DynamicObject
void execute (const String& code)
{
ExpressionTreeBuilder tb (code);
std::unique_ptr<BlockStatement> (tb.parseStatementList())->perform (Scope (nullptr, this, this), nullptr);
std::unique_ptr<BlockStatement> (tb.parseStatementList())->perform (Scope ({}, *this, *this), nullptr);
}
var evaluate (const String& code)
{
ExpressionTreeBuilder tb (code);
return ExpPtr (tb.parseExpression())->getResult (Scope (nullptr, this, this));
return ExpPtr (tb.parseExpression())->getResult (Scope ({}, *this, *this));
}
//==============================================================================
@ -103,7 +103,7 @@ struct JavascriptEngine::RootObject : public DynamicObject
static bool isNumericOrUndefined (const var& v) noexcept { return isNumeric (v) || v.isUndefined(); }
static int64 getOctalValue (const String& s) { BigInteger b; b.parseString (s.initialSectionContainingOnly ("01234567"), 8); return b.toInt64(); }
static Identifier getPrototypeIdentifier() { static const Identifier i ("prototype"); return i; }
static var* getPropertyPointer (DynamicObject* o, const Identifier& i) noexcept { return o->getProperties().getVarPointer (i); }
static var* getPropertyPointer (DynamicObject& o, const Identifier& i) noexcept { return o.getProperties().getVarPointer (i); }
//==============================================================================
struct CodeLocation
@ -131,9 +131,11 @@ struct JavascriptEngine::RootObject : public DynamicObject
//==============================================================================
struct Scope
{
Scope (const Scope* p, RootObject* r, DynamicObject* s) noexcept : parent (p), root (r), scope (s) {}
Scope (const Scope* p, ReferenceCountedObjectPtr<RootObject> rt, DynamicObject::Ptr scp) noexcept
: parent (p), root (std::move (rt)),
scope (std::move (scp)) {}
const Scope* parent;
const Scope* const parent;
ReferenceCountedObjectPtr<RootObject> root;
DynamicObject::Ptr scope;
@ -141,13 +143,13 @@ struct JavascriptEngine::RootObject : public DynamicObject
{
if (auto* o = targetObject.getDynamicObject())
{
if (auto* prop = getPropertyPointer (o, functionName))
if (auto* prop = getPropertyPointer (*o, functionName))
return *prop;
for (auto* p = o->getProperty (getPrototypeIdentifier()).getDynamicObject(); p != nullptr;
p = p->getProperty (getPrototypeIdentifier()).getDynamicObject())
{
if (auto* prop = getPropertyPointer (p, functionName))
if (auto* prop = getPropertyPointer (*p, functionName))
return *prop;
}
@ -174,14 +176,14 @@ struct JavascriptEngine::RootObject : public DynamicObject
var* findRootClassProperty (const Identifier& className, const Identifier& propName) const
{
if (auto* cls = root->getProperty (className).getDynamicObject())
return getPropertyPointer (cls, propName);
return getPropertyPointer (*cls, propName);
return nullptr;
}
var findSymbolInParentScopes (const Identifier& name) const
{
if (auto* v = getPropertyPointer (scope, name))
if (auto v = getPropertyPointer (*scope, name))
return *v;
return parent != nullptr ? parent->findSymbolInParentScopes (name)
@ -192,9 +194,9 @@ struct JavascriptEngine::RootObject : public DynamicObject
{
auto* target = args.thisObject.getDynamicObject();
if (target == nullptr || target == scope)
if (target == nullptr || target == scope.get())
{
if (auto* m = getPropertyPointer (scope, function))
if (auto* m = getPropertyPointer (*scope, function))
{
if (auto fo = dynamic_cast<FunctionObject*> (m->getObject()))
{
@ -208,7 +210,7 @@ struct JavascriptEngine::RootObject : public DynamicObject
for (int i = 0; i < props.size(); ++i)
if (auto* o = props.getValueAt (i).getDynamicObject())
if (Scope (this, root, o).findAndInvokeMethod (function, args, result))
if (Scope (this, *root, *o).findAndInvokeMethod (function, args, result))
return true;
return false;
@ -220,7 +222,7 @@ struct JavascriptEngine::RootObject : public DynamicObject
{
auto* target = args.thisObject.getDynamicObject();
if (target == nullptr || target == scope)
if (target == nullptr || target == scope.get())
{
if (auto fo = dynamic_cast<FunctionObject*> (m.getObject()))
{
@ -238,6 +240,8 @@ struct JavascriptEngine::RootObject : public DynamicObject
if (Time::getCurrentTime() > root->timeout)
location.throwError (root->timeout == Time() ? "Interrupted" : "Execution timed-out");
}
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Scope)
};
//==============================================================================
@ -378,7 +382,7 @@ struct JavascriptEngine::RootObject : public DynamicObject
void assign (const Scope& s, const var& newValue) const override
{
if (auto* v = getPropertyPointer (s.scope, name))
if (auto* v = getPropertyPointer (*s.scope, name))
*v = newValue;
else
s.root->setProperty (name, newValue);
@ -403,7 +407,7 @@ struct JavascriptEngine::RootObject : public DynamicObject
}
if (auto* o = p.getDynamicObject())
if (auto* v = getPropertyPointer (o, child))
if (auto* v = getPropertyPointer (*o, child))
return *v;
return var::undefined();
@ -436,7 +440,7 @@ struct JavascriptEngine::RootObject : public DynamicObject
if (auto* o = arrayVar.getDynamicObject())
if (key.isString())
if (auto* v = getPropertyPointer (o, Identifier (key)))
if (auto* v = getPropertyPointer (*o, Identifier (key)))
return *v;
return var::undefined();
@ -829,7 +833,7 @@ struct JavascriptEngine::RootObject : public DynamicObject
tb.parseFunctionParamsAndBody (*this);
}
DynamicObject::Ptr clone() override { return new FunctionObject (*this); }
DynamicObject::Ptr clone() override { return *new FunctionObject (*this); }
void writeAsJSON (OutputStream& out, int /*indentLevel*/, bool /*allOnOneLine*/, int /*maximumDecimalPlaces*/) override
{
@ -1092,6 +1096,9 @@ struct JavascriptEngine::RootObject : public DynamicObject
if (matchIf (TokenTypes::assign)) { ExpPtr rhs (parseExpression()); return new Assignment (location, lhs, rhs); }
if (matchIf (TokenTypes::plusEquals)) return parseInPlaceOpExpression<AdditionOp> (lhs);
if (matchIf (TokenTypes::minusEquals)) return parseInPlaceOpExpression<SubtractionOp> (lhs);
if (matchIf (TokenTypes::timesEquals)) return parseInPlaceOpExpression<MultiplyOp> (lhs);
if (matchIf (TokenTypes::divideEquals)) return parseInPlaceOpExpression<DivideOp> (lhs);
if (matchIf (TokenTypes::moduloEquals)) return parseInPlaceOpExpression<ModuloOp> (lhs);
if (matchIf (TokenTypes::leftShiftEquals)) return parseInPlaceOpExpression<LeftShiftOp> (lhs);
if (matchIf (TokenTypes::rightShiftEquals)) return parseInPlaceOpExpression<RightShiftOp> (lhs);
@ -1105,7 +1112,7 @@ struct JavascriptEngine::RootObject : public DynamicObject
Expression* parseInPlaceOpExpression (ExpPtr& lhs)
{
ExpPtr rhs (parseExpression());
Expression* bareLHS = lhs.get(); // careful - bare pointer is deliberately alised
Expression* bareLHS = lhs.get(); // careful - bare pointer is deliberately aliased
return new SelfAssignment (location, bareLHS, new OpType (location, lhs, rhs));
}
@ -1862,13 +1869,13 @@ var JavascriptEngine::evaluate (const String& code, Result* result)
var JavascriptEngine::callFunction (const Identifier& function, const var::NativeFunctionArgs& args, Result* result)
{
var returnVal (var::undefined());
auto returnVal = var::undefined();
try
{
prepareTimeout();
if (result != nullptr) *result = Result::ok();
RootObject::Scope (nullptr, root, root).findAndInvokeMethod (function, args, returnVal);
RootObject::Scope ({}, *root, *root).findAndInvokeMethod (function, args, returnVal);
}
catch (String& error)
{
@ -1881,14 +1888,15 @@ var JavascriptEngine::callFunction (const Identifier& function, const var::Nativ
var JavascriptEngine::callFunctionObject (DynamicObject* objectScope, const var& functionObject,
const var::NativeFunctionArgs& args, Result* result)
{
var returnVal (var::undefined());
auto returnVal = var::undefined();
try
{
prepareTimeout();
if (result != nullptr) *result = Result::ok();
RootObject::Scope rootScope (nullptr, root, root);
RootObject::Scope (&rootScope, root, objectScope).invokeMethod (functionObject, args, returnVal);
RootObject::Scope rootScope ({}, *root, *root);
RootObject::Scope (&rootScope, *root, DynamicObject::Ptr (objectScope))
.invokeMethod (functionObject, args, returnVal);
}
catch (String& error)
{

View File

@ -105,6 +105,7 @@
#endif
#if JUCE_ANDROID
#include <ifaddrs.h>
#include <android/log.h>
#endif
@ -118,19 +119,18 @@
//==============================================================================
#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 "containers/juce_Variant.cpp"
#include "files/juce_DirectoryIterator.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 "javascript/juce_JSON.cpp"
#include "javascript/juce_Javascript.cpp"
#include "containers/juce_DynamicObject.cpp"
#include "logging/juce_FileLogger.cpp"
#include "logging/juce_Logger.cpp"
#include "maths/juce_BigInteger.cpp"
@ -141,6 +141,7 @@
#include "misc/juce_Result.cpp"
#include "misc/juce_Uuid.cpp"
#include "misc/juce_StdFunctionCompat.cpp"
#include "misc/juce_ConsoleApplication.cpp"
#include "network/juce_MACAddress.cpp"
#include "network/juce_NamedPipe.cpp"
#include "network/juce_Socket.cpp"
@ -170,6 +171,10 @@
#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"
@ -182,6 +187,9 @@
#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
//==============================================================================
@ -213,7 +221,9 @@
//==============================================================================
#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"

View File

@ -32,7 +32,7 @@
ID: juce_core
vendor: juce
version: 5.3.2
version: 5.4.3
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
@ -42,7 +42,6 @@
OSXFrameworks: Cocoa IOKit
iOSFrameworks: Foundation
linuxLibs: rt dl pthread
linuxPackages: libcurl
mingwLibs: uuid wsock32 wininet version ole32 ws2_32 oleaut32 imm32 comdlg32 shlwapi rpcrt4 winmm
END_JUCE_MODULE_DECLARATION
@ -138,7 +137,22 @@
If you disable this then https/ssl support will not be available on linux.
*/
#ifndef JUCE_USE_CURL
#define JUCE_USE_CURL 0
#if JUCE_LINUX
#define JUCE_USE_CURL 1
#else
#define JUCE_USE_CURL 0
#endif
#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 erros 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
@ -159,6 +173,15 @@
#define JUCE_ALLOW_STATIC_NULL_VARIABLES 1
#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
#ifndef JUCE_STRING_UTF_TYPE
#define JUCE_STRING_UTF_TYPE 8
@ -232,6 +255,7 @@ namespace juce
#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"
@ -251,10 +275,13 @@ namespace juce
#include "text/juce_Base64.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 "system/juce_SystemStats.h"
#include "memory/juce_HeavyweightLeakedObjectDetector.h"
#include "time/juce_RelativeTime.h"
#include "time/juce_Time.h"
#include "streams/juce_InputStream.h"
@ -303,7 +330,6 @@ namespace juce
#include "network/juce_URL.h"
#include "network/juce_WebInputStream.h"
#include "streams/juce_URLInputSource.h"
#include "system/juce_SystemStats.h"
#include "time/juce_PerformanceCounter.h"
#include "unit_tests/juce_UnitTest.h"
#include "xml/juce_XmlDocument.h"

View File

@ -56,7 +56,7 @@ public:
const int64 maxInitialFileSizeBytes = 128 * 1024);
/** Destructor. */
~FileLogger();
~FileLogger() override;
//==============================================================================
/** Returns the file that this logger is writing to. */
@ -114,7 +114,7 @@ public:
static File getSystemLogFileFolder();
// (implementation of the Logger virtual method)
void logMessage (const String&);
void logMessage (const String&) override;
//==============================================================================
/** This is a utility function which removes lines from the start of a text

View File

@ -112,7 +112,7 @@ BigInteger::BigInteger (const BigInteger& other)
}
BigInteger::BigInteger (BigInteger&& other) noexcept
: heapAllocation (static_cast<HeapBlock<uint32>&&> (other.heapAllocation)),
: heapAllocation (std::move (other.heapAllocation)),
allocatedSize (other.allocatedSize),
highestBit (other.highestBit),
negative (other.negative)
@ -122,7 +122,7 @@ BigInteger::BigInteger (BigInteger&& other) noexcept
BigInteger& BigInteger::operator= (BigInteger&& other) noexcept
{
heapAllocation = static_cast<HeapBlock<uint32>&&> (other.heapAllocation);
heapAllocation = std::move (other.heapAllocation);
memcpy (preallocated, other.preallocated, sizeof (preallocated));
allocatedSize = other.allocatedSize;
highestBit = other.highestBit;
@ -171,7 +171,7 @@ uint32* BigInteger::getValues() const noexcept
jassert (heapAllocation != nullptr || allocatedSize <= numPreallocatedInts);
return heapAllocation != nullptr ? heapAllocation
: (uint32*) preallocated;
: const_cast<uint32*> (preallocated);
}
uint32* BigInteger::ensureSize (const size_t numVals)

View File

@ -107,14 +107,14 @@ struct Expression::Helpers
class Constant : public Term
{
public:
Constant (const double val, const bool resolutionTarget)
Constant (double val, bool resolutionTarget)
: value (val), isResolutionTarget (resolutionTarget) {}
Type getType() const noexcept { return constantType; }
Term* clone() const { return new Constant (value, isResolutionTarget); }
TermPtr resolve (const Scope&, int) { return this; }
TermPtr resolve (const Scope&, int) { return *this; }
double toDouble() const { return value; }
TermPtr negated() { return new Constant (-value, isResolutionTarget); }
TermPtr negated() { return *new Constant (-value, isResolutionTarget); }
String toString() const
{
@ -133,9 +133,9 @@ struct Expression::Helpers
class BinaryTerm : public Term
{
public:
BinaryTerm (Term* const l, Term* const r) : left (l), right (r)
BinaryTerm (TermPtr l, TermPtr r) : left (std::move (l)), right (std::move (r))
{
jassert (l != nullptr && r != nullptr);
jassert (left != nullptr && right != nullptr);
}
int getInputIndexFor (const Term* possibleInput) const
@ -145,22 +145,22 @@ struct Expression::Helpers
Type getType() const noexcept { return operatorType; }
int getNumInputs() const { return 2; }
Term* getInput (int index) const { return index == 0 ? left.get() : (index == 1 ? right.get() : 0); }
Term* getInput (int index) const { return index == 0 ? left.get() : (index == 1 ? right.get() : nullptr); }
virtual double performFunction (double left, double right) const = 0;
virtual void writeOperator (String& dest) const = 0;
TermPtr resolve (const Scope& scope, int recursionDepth)
{
return new Constant (performFunction (left ->resolve (scope, recursionDepth)->toDouble(),
right->resolve (scope, recursionDepth)->toDouble()), false);
return *new Constant (performFunction (left ->resolve (scope, recursionDepth)->toDouble(),
right->resolve (scope, recursionDepth)->toDouble()), false);
}
String toString() const
{
String s;
auto ourPrecendence = getOperatorPrecedence();
const int ourPrecendence = getOperatorPrecedence();
if (left->getOperatorPrecedence() > ourPrecendence)
s << '(' << left->toString() << ')';
else
@ -183,12 +183,12 @@ struct Expression::Helpers
{
jassert (input == left || input == right);
if (input != left && input != right)
return TermPtr();
return {};
if (const Term* const dest = findDestinationFor (topLevelTerm, this))
if (auto dest = findDestinationFor (topLevelTerm, this))
return dest->createTermToEvaluateInput (scope, this, overallTarget, topLevelTerm);
return new Constant (overallTarget, false);
return *new Constant (overallTarget, false);
}
};
@ -238,7 +238,7 @@ struct Expression::Helpers
Type getType() const noexcept { return functionType; }
Term* clone() const { return new Function (functionName, parameters); }
int getNumInputs() const { return parameters.size(); }
Term* getInput (int i) const { return parameters.getReference(i).term; }
Term* getInput (int i) const { return parameters.getReference(i).term.get(); }
String getName() const { return functionName; }
TermPtr resolve (const Scope& scope, int recursionDepth)
@ -261,7 +261,7 @@ struct Expression::Helpers
result = scope.evaluateFunction (functionName, nullptr, 0);
}
return new Constant (result, false);
return *new Constant (result, false);
}
int getInputIndexFor (const Term* possibleInput) const
@ -300,7 +300,7 @@ struct Expression::Helpers
class DotOperator : public BinaryTerm
{
public:
DotOperator (SymbolTerm* const l, Term* const r) : BinaryTerm (l, r) {}
DotOperator (SymbolTerm* l, TermPtr r) : BinaryTerm (TermPtr (l), r) {}
TermPtr resolve (const Scope& scope, int recursionDepth)
{
@ -311,7 +311,7 @@ struct Expression::Helpers
return visitor.output;
}
Term* clone() const { return new DotOperator (getSymbol(), right); }
Term* clone() const { return new DotOperator (getSymbol(), *right); }
String getName() const { return "."; }
int getOperatorPrecedence() const { return 1; }
void writeOperator (String& dest) const { dest << '.'; }
@ -414,11 +414,11 @@ struct Expression::Helpers
int getInputIndexFor (const Term* possibleInput) const { return possibleInput == input ? 0 : -1; }
int getNumInputs() const { return 1; }
Term* getInput (int index) const { return index == 0 ? input.get() : nullptr; }
Term* clone() const { return new Negate (input->clone()); }
Term* clone() const { return new Negate (*input->clone()); }
TermPtr resolve (const Scope& scope, int recursionDepth)
{
return new Constant (-input->resolve (scope, recursionDepth)->toDouble(), false);
return *new Constant (-input->resolve (scope, recursionDepth)->toDouble(), false);
}
String getName() const { return "-"; }
@ -431,8 +431,8 @@ struct Expression::Helpers
const Term* const dest = findDestinationFor (topLevelTerm, this);
return new Negate (dest == nullptr ? new Constant (overallTarget, false)
: dest->createTermToEvaluateInput (scope, this, overallTarget, topLevelTerm));
return *new Negate (dest == nullptr ? TermPtr (*new Constant (overallTarget, false))
: dest->createTermToEvaluateInput (scope, this, overallTarget, topLevelTerm));
}
String toString() const
@ -451,9 +451,9 @@ struct Expression::Helpers
class Add : public BinaryTerm
{
public:
Add (Term* const l, Term* const r) : BinaryTerm (l, r) {}
Add (TermPtr l, TermPtr r) : BinaryTerm (l, r) {}
Term* clone() const { return new Add (left->clone(), right->clone()); }
Term* clone() const { return new Add (*left->clone(), *right->clone()); }
double performFunction (double lhs, double rhs) const { return lhs + rhs; }
int getOperatorPrecedence() const { return 3; }
String getName() const { return "+"; }
@ -461,11 +461,10 @@ struct Expression::Helpers
TermPtr createTermToEvaluateInput (const Scope& scope, const Term* input, double overallTarget, Term* topLevelTerm) const
{
const TermPtr newDest (createDestinationTerm (scope, input, overallTarget, topLevelTerm));
if (newDest == nullptr)
return TermPtr();
if (auto newDest = createDestinationTerm (scope, input, overallTarget, topLevelTerm))
return *new Subtract (newDest, *(input == left ? right : left)->clone());
return new Subtract (newDest, (input == left ? right : left)->clone());
return {};
}
private:
@ -476,9 +475,9 @@ struct Expression::Helpers
class Subtract : public BinaryTerm
{
public:
Subtract (Term* const l, Term* const r) : BinaryTerm (l, r) {}
Subtract (TermPtr l, TermPtr r) : BinaryTerm (l, r) {}
Term* clone() const { return new Subtract (left->clone(), right->clone()); }
Term* clone() const { return new Subtract (*left->clone(), *right->clone()); }
double performFunction (double lhs, double rhs) const { return lhs - rhs; }
int getOperatorPrecedence() const { return 3; }
String getName() const { return "-"; }
@ -486,14 +485,15 @@ struct Expression::Helpers
TermPtr createTermToEvaluateInput (const Scope& scope, const Term* input, double overallTarget, Term* topLevelTerm) const
{
const TermPtr newDest (createDestinationTerm (scope, input, overallTarget, topLevelTerm));
if (newDest == nullptr)
return TermPtr();
if (auto newDest = createDestinationTerm (scope, input, overallTarget, topLevelTerm))
{
if (input == left)
return *new Add (*newDest, *right->clone());
if (input == left)
return new Add (newDest, right->clone());
return *new Subtract (*left->clone(), *newDest);
}
return new Subtract (left->clone(), newDest);
return {};
}
private:
@ -504,9 +504,9 @@ struct Expression::Helpers
class Multiply : public BinaryTerm
{
public:
Multiply (Term* const l, Term* const r) : BinaryTerm (l, r) {}
Multiply (TermPtr l, TermPtr r) : BinaryTerm (l, r) {}
Term* clone() const { return new Multiply (left->clone(), right->clone()); }
Term* clone() const { return new Multiply (*left->clone(), *right->clone()); }
double performFunction (double lhs, double rhs) const { return lhs * rhs; }
String getName() const { return "*"; }
void writeOperator (String& dest) const { dest << " * "; }
@ -514,14 +514,12 @@ struct Expression::Helpers
TermPtr createTermToEvaluateInput (const Scope& scope, const Term* input, double overallTarget, Term* topLevelTerm) const
{
const TermPtr newDest (createDestinationTerm (scope, input, overallTarget, topLevelTerm));
if (newDest == nullptr)
return TermPtr();
if (auto newDest = createDestinationTerm (scope, input, overallTarget, topLevelTerm))
return *new Divide (newDest, *(input == left ? right : left)->clone());
return new Divide (newDest, (input == left ? right : left)->clone());
return {};
}
private:
JUCE_DECLARE_NON_COPYABLE (Multiply)
};
@ -529,9 +527,9 @@ struct Expression::Helpers
class Divide : public BinaryTerm
{
public:
Divide (Term* const l, Term* const r) : BinaryTerm (l, r) {}
Divide (TermPtr l, TermPtr r) : BinaryTerm (l, r) {}
Term* clone() const { return new Divide (left->clone(), right->clone()); }
Term* clone() const { return new Divide (*left->clone(), *right->clone()); }
double performFunction (double lhs, double rhs) const { return lhs / rhs; }
String getName() const { return "/"; }
void writeOperator (String& dest) const { dest << " / "; }
@ -539,17 +537,17 @@ struct Expression::Helpers
TermPtr createTermToEvaluateInput (const Scope& scope, const Term* input, double overallTarget, Term* topLevelTerm) const
{
const TermPtr newDest (createDestinationTerm (scope, input, overallTarget, topLevelTerm));
auto newDest = createDestinationTerm (scope, input, overallTarget, topLevelTerm);
if (newDest == nullptr)
return TermPtr();
return {};
if (input == left)
return new Multiply (newDest, right->clone());
return *new Multiply (*newDest, *right->clone());
return new Divide (left->clone(), newDest);
return *new Divide (*left->clone(), *newDest);
}
private:
JUCE_DECLARE_NON_COPYABLE (Divide)
};
@ -601,22 +599,19 @@ struct Expression::Helpers
}
for (int i = 0; i < numIns; ++i)
{
Constant* const c = findTermToAdjust (term->getInput (i), mustBeFlagged);
if (c != nullptr)
if (auto c = findTermToAdjust (term->getInput (i), mustBeFlagged))
return c;
}
return nullptr;
}
static bool containsAnySymbols (const Term* const t)
static bool containsAnySymbols (const Term& t)
{
if (t->getType() == Expression::symbolType)
if (t.getType() == Expression::symbolType)
return true;
for (int i = t->getNumInputs(); --i >= 0;)
if (containsAnySymbols (t->getInput (i)))
for (int i = t.getNumInputs(); --i >= 0;)
if (containsAnySymbols (*t.getInput (i)))
return true;
return false;
@ -662,9 +657,9 @@ struct Expression::Helpers
TermPtr readUpToComma()
{
if (text.isEmpty())
return new Constant (0.0, false);
return *new Constant (0.0, false);
const TermPtr e (readExpression());
auto e = readExpression();
if (e == nullptr || ((! readOperator (",")) && ! text.isEmpty()))
return parseError ("Syntax error: \"" + String (text) + "\"");
@ -677,12 +672,12 @@ struct Expression::Helpers
private:
String::CharPointerType& text;
Term* parseError (const String& message)
TermPtr parseError (const String& message)
{
if (error.isEmpty())
error = message;
return nullptr;
return {};
}
//==============================================================================
@ -777,20 +772,20 @@ struct Expression::Helpers
TermPtr readExpression()
{
TermPtr lhs (readMultiplyOrDivideExpression());
auto lhs = readMultiplyOrDivideExpression();
char opType;
while (lhs != nullptr && readOperator ("+-", &opType))
{
TermPtr rhs (readMultiplyOrDivideExpression());
auto rhs = readMultiplyOrDivideExpression();
if (rhs == nullptr)
return parseError ("Expected expression after \"" + String::charToString ((juce_wchar) (uint8) opType) + "\"");
if (opType == '+')
lhs = new Add (lhs, rhs);
lhs = *new Add (lhs, rhs);
else
lhs = new Subtract (lhs, rhs);
lhs = *new Subtract (lhs, rhs);
}
return lhs;
@ -798,9 +793,9 @@ struct Expression::Helpers
TermPtr readMultiplyOrDivideExpression()
{
TermPtr lhs (readUnaryExpression());
auto lhs = readUnaryExpression();
char opType;
while (lhs != nullptr && readOperator ("*/", &opType))
{
TermPtr rhs (readUnaryExpression());
@ -809,9 +804,9 @@ struct Expression::Helpers
return parseError ("Expected expression after \"" + String::charToString ((juce_wchar) (uint8) opType) + "\"");
if (opType == '*')
lhs = new Multiply (lhs, rhs);
lhs = *new Multiply (lhs, rhs);
else
lhs = new Divide (lhs, rhs);
lhs = *new Divide (lhs, rhs);
}
return lhs;
@ -838,12 +833,10 @@ struct Expression::Helpers
TermPtr readPrimaryExpression()
{
TermPtr e (readParenthesisedExpression());
if (e != nullptr)
if (auto e = readParenthesisedExpression())
return e;
e = readNumber();
if (e != nullptr)
if (auto e = readNumber())
return e;
return readSymbolOrFunction();
@ -852,24 +845,25 @@ struct Expression::Helpers
TermPtr readSymbolOrFunction()
{
String identifier;
if (readIdentifier (identifier))
{
if (readOperator ("(")) // method call...
{
Function* const f = new Function (identifier);
auto f = new Function (identifier);
std::unique_ptr<Term> func (f); // (can't use std::unique_ptr<Function> in MSVC)
TermPtr param (readExpression());
auto param = readExpression();
if (param == nullptr)
{
if (readOperator (")"))
return func.release();
return TermPtr (func.release());
return parseError ("Expected parameters after \"" + identifier + " (\"");
}
f->parameters.add (Expression (param));
f->parameters.add (Expression (param.get()));
while (readOperator (","))
{
@ -878,11 +872,11 @@ struct Expression::Helpers
if (param == nullptr)
return parseError ("Expected expression after \",\"");
f->parameters.add (Expression (param));
f->parameters.add (Expression (param.get()));
}
if (readOperator (")"))
return func.release();
return TermPtr (func.release());
return parseError ("Expected \")\"");
}
@ -897,25 +891,26 @@ struct Expression::Helpers
if (identifier == "this")
return rhs;
return new DotOperator (new SymbolTerm (identifier), rhs);
return *new DotOperator (new SymbolTerm (identifier), rhs);
}
// just a symbol..
jassert (identifier.trim() == identifier);
return new SymbolTerm (identifier);
return *new SymbolTerm (identifier);
}
return TermPtr();
return {};
}
TermPtr readParenthesisedExpression()
{
if (! readOperator ("("))
return TermPtr();
return {};
auto e = readExpression();
const TermPtr e (readExpression());
if (e == nullptr || ! readOperator (")"))
return TermPtr();
return {};
return e;
}
@ -956,13 +951,13 @@ Expression& Expression::operator= (const Expression& other)
}
Expression::Expression (Expression&& other) noexcept
: term (static_cast<ReferenceCountedObjectPtr<Term>&&> (other.term))
: term (std::move (other.term))
{
}
Expression& Expression::operator= (Expression&& other) noexcept
{
term = static_cast<ReferenceCountedObjectPtr<Term>&&> (other.term);
term = std::move (other.term);
return *this;
}
@ -977,7 +972,7 @@ Expression::Expression (const String& stringToParse, String& parseError)
Expression Expression::parse (String::CharPointerType& stringToParse, String& parseError)
{
Helpers::Parser parser (stringToParse);
Expression e (parser.readUpToComma());
Expression e (parser.readUpToComma().get());
parseError = parser.error;
return e;
}
@ -1011,7 +1006,7 @@ Expression Expression::operator+ (const Expression& other) const { return Expre
Expression Expression::operator- (const Expression& other) const { return Expression (new Helpers::Subtract (term, other.term)); }
Expression Expression::operator* (const Expression& other) const { return Expression (new Helpers::Multiply (term, other.term)); }
Expression Expression::operator/ (const Expression& other) const { return Expression (new Helpers::Divide (term, other.term)); }
Expression Expression::operator-() const { return Expression (term->negated()); }
Expression Expression::operator-() const { return Expression (term->negated().get()); }
Expression Expression::symbol (const String& symbol) { return Expression (new Helpers::SymbolTerm (symbol)); }
Expression Expression::function (const String& functionName, const Array<Expression>& parameters)
@ -1023,14 +1018,14 @@ Expression Expression::adjustedToGiveNewResult (const double targetValue, const
{
std::unique_ptr<Term> newTerm (term->clone());
Helpers::Constant* termToAdjust = Helpers::findTermToAdjust (newTerm.get(), true);
auto termToAdjust = Helpers::findTermToAdjust (newTerm.get(), true);
if (termToAdjust == nullptr)
termToAdjust = Helpers::findTermToAdjust (newTerm.get(), false);
if (termToAdjust == nullptr)
{
newTerm.reset (new Helpers::Add (newTerm.release(), new Helpers::Constant (0, false)));
newTerm.reset (new Helpers::Add (*newTerm.release(), *new Helpers::Constant (0, false)));
termToAdjust = Helpers::findTermToAdjust (newTerm.get(), false);
}
@ -1039,7 +1034,7 @@ Expression Expression::adjustedToGiveNewResult (const double targetValue, const
if (const Term* parent = Helpers::findDestinationFor (newTerm.get(), termToAdjust))
{
if (Helpers::TermPtr reverseTerm = parent->createTermToEvaluateInput (scope, termToAdjust, targetValue, newTerm.get()))
termToAdjust->value = Expression (reverseTerm).evaluate (scope);
termToAdjust->value = Expression (reverseTerm.get()).evaluate (scope);
else
return Expression (targetValue);
}
@ -1089,7 +1084,7 @@ void Expression::findReferencedSymbols (Array<Symbol>& results, const Scope& sco
}
String Expression::toString() const { return term->toString(); }
bool Expression::usesAnySymbols() const { return Helpers::containsAnySymbols (term); }
bool Expression::usesAnySymbols() const { return Helpers::containsAnySymbols (*term); }
Expression::Type Expression::getType() const noexcept { return term->getType(); }
String Expression::getSymbolOrFunction() const { return term->getName(); }
int Expression::getNumInputs() const { return term->getNumInputs(); }
@ -1098,7 +1093,7 @@ Expression Expression::getInput (int index) const { return Expression (ter
//==============================================================================
ReferenceCountedObjectPtr<Expression::Term> Expression::Term::negated()
{
return new Helpers::Negate (this);
return *new Helpers::Negate (*this);
}
//==============================================================================

View File

@ -136,7 +136,7 @@ public:
class Visitor
{
public:
virtual ~Visitor() {}
virtual ~Visitor() = default;
virtual void visit (const Scope&) = 0;
};
@ -243,10 +243,6 @@ private:
//==============================================================================
class Term;
struct Helpers;
friend class Term;
friend struct Helpers;
friend struct ContainerDeletePolicy<Term>;
friend class ReferenceCountedObjectPtr<Term>;
ReferenceCountedObjectPtr<Term> term;
explicit Expression (Term*);

View File

@ -42,7 +42,7 @@ using uint16 = unsigned short;
/** A platform-independent 32-bit signed integer type. */
using int32 = signed int;
/** A platform-independent 32-bit unsigned integer type. */
typedef unsigned int uint32;
using uint32 = unsigned int;
#if JUCE_MSVC
/** A platform-independent 64-bit integer type. */
@ -197,7 +197,6 @@ void findMinAndMax (const Type* values, int numValues, Type& lowest, Type& highe
}
}
//==============================================================================
/** Constrains a value to keep it within a given range.
@ -265,6 +264,25 @@ bool isPositiveAndNotGreaterThan (int valueToTest, Type upperLimit) noexcept
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>

View File

@ -40,7 +40,7 @@ class NormalisableRange
{
public:
/** Creates a continuous range that performs a dummy mapping. */
NormalisableRange() noexcept {}
NormalisableRange() = default;
NormalisableRange (const NormalisableRange&) = default;
NormalisableRange& operator= (const NormalisableRange&) = default;
@ -50,9 +50,9 @@ public:
: start (other.start), end (other.end),
interval (other.interval), skew (other.skew),
symmetricSkew (other.symmetricSkew),
convertFrom0To1Function (static_cast<ConverstionFunction&&> (other.convertFrom0To1Function)),
convertTo0To1Function (static_cast<ConverstionFunction&&> (other.convertTo0To1Function)),
snapToLegalValueFunction (static_cast<ConverstionFunction&&> (other.snapToLegalValueFunction))
convertFrom0To1Function (std::move (other.convertFrom0To1Function)),
convertTo0To1Function (std::move (other.convertTo0To1Function)),
snapToLegalValueFunction (std::move (other.snapToLegalValueFunction))
{
}
@ -64,9 +64,9 @@ public:
interval = other.interval;
skew = other.skew;
symmetricSkew = other.symmetricSkew;
convertFrom0To1Function = static_cast<ConverstionFunction&&> (other.convertFrom0To1Function);
convertTo0To1Function = static_cast<ConverstionFunction&&> (other.convertTo0To1Function);
snapToLegalValueFunction = static_cast<ConverstionFunction&&> (other.snapToLegalValueFunction);
convertFrom0To1Function = std::move (other.convertFrom0To1Function);
convertTo0To1Function = std::move (other.convertTo0To1Function);
snapToLegalValueFunction = std::move (other.snapToLegalValueFunction);
return *this;
}
@ -274,11 +274,11 @@ private:
return clampedValue;
}
using ConverstionFunction = std::function<ValueType(ValueType, ValueType, ValueType)>;
using ConversionFunction = std::function<ValueType(ValueType, ValueType, ValueType)>;
ConverstionFunction convertFrom0To1Function = {},
convertTo0To1Function = {},
snapToLegalValueFunction = {};
ConversionFunction convertFrom0To1Function = {},
convertTo0To1Function = {},
snapToLegalValueFunction = {};
};
} // namespace juce

View File

@ -38,6 +38,16 @@ 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;
}

View File

@ -41,32 +41,22 @@ class Range
public:
//==============================================================================
/** Constructs an empty range. */
Range() noexcept : start(), end()
{
}
JUCE_CONSTEXPR Range() = default;
/** Constructs a range with given start and end values. */
Range (const ValueType startValue, const ValueType endValue) noexcept
JUCE_CONSTEXPR Range (const ValueType startValue, const ValueType endValue) noexcept
: start (startValue), end (jmax (startValue, endValue))
{
}
/** Constructs a copy of another range. */
Range (const Range& other) noexcept
: start (other.start), end (other.end)
{
}
JUCE_CONSTEXPR Range (const Range&) = default;
/** Copies another range object. */
Range& operator= (Range other) noexcept
{
start = other.start;
end = other.end;
return *this;
}
Range& operator= (const Range&) = default;
/** Returns the range that lies between two positions (in either order). */
static Range between (const ValueType position1, const ValueType position2) noexcept
JUCE_CONSTEXPR static Range between (const ValueType position1, const ValueType position2) noexcept
{
return position1 < position2 ? Range (position1, position2)
: Range (position2, position1);
@ -80,23 +70,23 @@ public:
}
/** Returns a range with the specified start position and a length of zero. */
static Range emptyRange (const ValueType start) noexcept
JUCE_CONSTEXPR static Range emptyRange (const ValueType start) noexcept
{
return Range (start, start);
}
//==============================================================================
/** Returns the start of the range. */
inline ValueType getStart() const noexcept { return start; }
JUCE_CONSTEXPR inline ValueType getStart() const noexcept { return start; }
/** Returns the length of the range. */
inline ValueType getLength() const noexcept { return end - start; }
JUCE_CONSTEXPR inline ValueType getLength() const noexcept { return end - start; }
/** Returns the end of the range. */
inline ValueType getEnd() const noexcept { return end; }
JUCE_CONSTEXPR inline ValueType getEnd() const noexcept { return end; }
/** Returns true if the range has a length of zero. */
inline bool isEmpty() const noexcept { return start == end; }
JUCE_CONSTEXPR inline bool isEmpty() const noexcept { return start == end; }
//==============================================================================
/** Changes the start position of the range, leaving the end position unchanged.
@ -114,13 +104,13 @@ public:
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.
*/
Range withStart (const ValueType newStart) const noexcept
JUCE_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. */
Range movedToStartAt (const ValueType newStart) const noexcept
JUCE_CONSTEXPR Range movedToStartAt (const ValueType newStart) const noexcept
{
return Range (newStart, end + (newStart - start));
}
@ -140,13 +130,13 @@ public:
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.
*/
Range withEnd (const ValueType newEnd) const noexcept
JUCE_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. */
Range movedToEndAt (const ValueType newEnd) const noexcept
JUCE_CONSTEXPR Range movedToEndAt (const ValueType newEnd) const noexcept
{
return Range (start + (newEnd - end), newEnd);
}
@ -162,7 +152,7 @@ public:
/** Returns a range with the same start as this one, but a different length.
Lengths less than zero are treated as zero.
*/
Range withLength (const ValueType newLength) const noexcept
JUCE_CONSTEXPR Range withLength (const ValueType newLength) const noexcept
{
return Range (start, start + newLength);
}
@ -171,7 +161,7 @@ public:
given amount.
@returns The returned range will be (start - amount, end + amount)
*/
Range expanded (ValueType amount) const noexcept
JUCE_CONSTEXPR Range expanded (ValueType amount) const noexcept
{
return Range (start - amount, end + amount);
}
@ -196,27 +186,27 @@ public:
/** Returns a range that is equal to this one with an amount added to its
start and end.
*/
Range operator+ (const ValueType amountToAdd) const noexcept
JUCE_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. */
Range operator- (const ValueType amountToSubtract) const noexcept
JUCE_CONSTEXPR Range operator- (const ValueType amountToSubtract) const noexcept
{
return Range (start - amountToSubtract, end - amountToSubtract);
}
bool operator== (Range other) const noexcept { return start == other.start && end == other.end; }
bool operator!= (Range other) const noexcept { return start != other.start || end != other.end; }
JUCE_CONSTEXPR bool operator== (Range other) const noexcept { return start == other.start && end == other.end; }
JUCE_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.
*/
bool contains (const ValueType position) const noexcept
JUCE_CONSTEXPR bool contains (const ValueType position) const noexcept
{
return start <= position && position < end;
}
@ -228,34 +218,34 @@ public:
}
/** Returns true if the given range lies entirely inside this range. */
bool contains (Range other) const noexcept
JUCE_CONSTEXPR bool contains (Range other) const noexcept
{
return start <= other.start && end >= other.end;
}
/** Returns true if the given range intersects this one. */
bool intersects (Range other) const noexcept
JUCE_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. */
Range getIntersectionWith (Range other) const noexcept
JUCE_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. */
Range getUnionWith (Range other) const noexcept
JUCE_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. */
Range getUnionWith (const ValueType valueToInclude) const noexcept
JUCE_CONSTEXPR Range getUnionWith (const ValueType valueToInclude) const noexcept
{
return Range (jmin (valueToInclude, start),
jmax (valueToInclude, end));
@ -301,7 +291,7 @@ public:
private:
//==============================================================================
ValueType start, end;
ValueType start{}, end{};
};
} // namespace juce

View File

@ -36,11 +36,7 @@ class StatisticsAccumulator
public:
//==============================================================================
/** Constructs a new StatisticsAccumulator. */
StatisticsAccumulator() noexcept
: count (0),
minimum ( std::numeric_limits<FloatType>::infinity()),
maximum (-std::numeric_limits<FloatType>::infinity())
{}
StatisticsAccumulator() = default;
//==============================================================================
/** Add a new value to the accumulator.
@ -116,7 +112,7 @@ private:
//==============================================================================
struct KahanSum
{
KahanSum() noexcept : sum(), error() {}
KahanSum() = default;
operator FloatType() const noexcept { return sum; }
void JUCE_NO_ASSOCIATIVE_MATH_OPTIMISATIONS operator+= (FloatType value) noexcept
@ -127,13 +123,14 @@ private:
sum = newSum;
}
FloatType sum, error;
FloatType sum{}, error{};
};
//==============================================================================
size_t count;
size_t count { 0 };
KahanSum sum, sumSquares;
FloatType minimum, maximum;
FloatType minimum { std::numeric_limits<FloatType>::infinity() },
maximum { -std::numeric_limits<FloatType>::infinity() };
};
} // namespace juce

View File

@ -40,10 +40,10 @@ namespace juce
template <typename Type>
struct Atomic final
{
typedef typename AtomicHelpers::DiffTypeHelper<Type>::Type DiffType;
using DiffType = typename AtomicHelpers::DiffTypeHelper<Type>::Type;
/** Creates a new value, initialised to zero. */
Atomic() noexcept : value (0) {}
Atomic() noexcept : value (Type()) {}
/** Creates a new value, with a given initial value. */
Atomic (Type initialValue) noexcept : value (initialValue) {}

View File

@ -85,6 +85,11 @@ namespace HeapBlockHelper
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.
@ -92,9 +97,7 @@ public:
After creation, you can resize the array using the malloc(), calloc(),
or realloc() methods.
*/
HeapBlock() noexcept
{
}
HeapBlock() = default;
/** Creates a HeapBlock containing a number of elements.
@ -147,6 +150,30 @@ public:
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
@ -296,7 +323,7 @@ public:
}
/** This typedef can be used to get the type of the heapblock's elements. */
typedef ElementType Type;
using Type = ElementType;
private:
//==============================================================================
@ -311,6 +338,9 @@ private:
#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!

View File

@ -0,0 +1,144 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
/**
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().set (this, SystemStats::getStackBacktrace()); }
HeavyweightLeakedObjectDetector (const HeavyweightLeakedObjectDetector&) noexcept { getBacktraceMap().set (this, SystemStats::getStackBacktrace()); }
~HeavyweightLeakedObjectDetector() { getBacktraceMap().remove (this); }
private:
//==============================================================================
typedef HashMap<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 (typename BacktraceMap::Iterator i (map); i.next();)
{
str << "\nBacktrace " << String (counter++) << "\n"
<< "-----------------------------------------------------------------" << "\n"
<< i.getValue();
}
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

@ -72,7 +72,7 @@ private:
class LeakCounter
{
public:
LeakCounter() noexcept {}
LeakCounter() = default;
~LeakCounter()
{

View File

@ -29,7 +29,7 @@ inline void zeromem (void* memory, size_t numBytes) noexcept { memset (me
/** Overwrites a structure or object with zeros. */
template <typename Type>
inline void zerostruct (Type& structure) noexcept { memset (&structure, 0, sizeof (structure)); }
inline void zerostruct (Type& structure) noexcept { memset ((void*) &structure, 0, sizeof (structure)); }
/** Delete an object pointer, and sets the pointer to null.
@ -44,7 +44,7 @@ inline void deleteAndZero (Type& pointer) { delete poi
a specific number of bytes,
*/
template <typename Type, typename IntegerType>
inline Type* addBytesToPointer (Type* basePointer, IntegerType bytes) noexcept { return (Type*) (((char*) basePointer) + bytes); }
inline Type* addBytesToPointer (Type* basePointer, IntegerType bytes) noexcept { return (Type*) (const_cast<char*> (reinterpret_cast<const char*> (basePointer)) + bytes); }
/** 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. */

View File

@ -81,14 +81,14 @@ MemoryBlock& MemoryBlock::operator= (const MemoryBlock& other)
}
MemoryBlock::MemoryBlock (MemoryBlock&& other) noexcept
: data (static_cast<HeapBlockType&&> (other.data)),
: data (std::move (other.data)),
size (other.size)
{
}
MemoryBlock& MemoryBlock::operator= (MemoryBlock&& other) noexcept
{
data = static_cast<HeapBlockType&&> (other.data);
data = std::move (other.data);
size = other.size;
return *this;
}

View File

@ -27,7 +27,6 @@ namespace juce
/**
A class to hold a resizable block of raw data.
@tags{Core}
*/
class JUCE_API MemoryBlock
@ -252,7 +251,7 @@ public:
private:
//==============================================================================
typedef HeapBlock<char, true> HeapBlockType;
using HeapBlockType = HeapBlock<char, true>;
HeapBlockType data;
size_t size = 0;

View File

@ -41,7 +41,7 @@ class OptionalScopedPointer
public:
//==============================================================================
/** Creates an empty OptionalScopedPointer. */
OptionalScopedPointer() : shouldDelete (false) {}
OptionalScopedPointer() = default;
/** Creates an OptionalScopedPointer to point to a given object, and specifying whether
the OptionalScopedPointer will delete it.
@ -177,7 +177,7 @@ public:
private:
//==============================================================================
ScopedPointer<ObjectType> object;
bool shouldDelete;
bool shouldDelete = false;
// This is here to avoid people accidentally taking a second owned copy of
// a scoped pointer, which is almost certainly not what you intended to do!

View File

@ -49,7 +49,7 @@ namespace juce
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 that it
This class uses an Atomic<int> value to hold the reference count, so that
the pointers can be passed between threads safely. For a faster but non-thread-safe
version, use SingleThreadedReferenceCountedObject instead.
@ -99,7 +99,7 @@ public:
protected:
//==============================================================================
/** Creates the reference-counted object (with an initial ref count of zero). */
ReferenceCountedObject() {}
ReferenceCountedObject() = default;
/** Copying from another object does not affect this one's reference-count. */
ReferenceCountedObject (const ReferenceCountedObject&) noexcept {}
@ -187,7 +187,7 @@ public:
protected:
//==============================================================================
/** Creates the reference-counted object (with an initial ref count of zero). */
SingleThreadedReferenceCountedObject() {}
SingleThreadedReferenceCountedObject() = default;
/** Copying from another object does not affect this one's reference-count. */
SingleThreadedReferenceCountedObject (const SingleThreadedReferenceCountedObject&) {}
@ -246,7 +246,7 @@ public:
//==============================================================================
/** Creates a pointer to a null object. */
ReferenceCountedObjectPtr() noexcept {}
ReferenceCountedObjectPtr() = default;
/** Creates a pointer to a null object. */
ReferenceCountedObjectPtr (decltype (nullptr)) noexcept {}
@ -260,6 +260,15 @@ public:
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.
*/
@ -269,6 +278,13 @@ public:
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).
*/
@ -305,26 +321,40 @@ public:
*/
ReferenceCountedObjectPtr& operator= (ReferencedType* newObject)
{
if (referencedObject != 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)
{
incIfNotNull (newObject);
newObject.incReferenceCount();
auto* oldObject = referencedObject;
referencedObject = newObject;
referencedObject = &newObject;
decIfNotNull (oldObject);
}
return *this;
}
/** Takes-over the object from another pointer. */
ReferenceCountedObjectPtr (ReferenceCountedObjectPtr&& other) noexcept
: referencedObject (other.referencedObject)
/** Resets this pointer to a null pointer. */
ReferenceCountedObjectPtr& operator= (decltype (nullptr))
{
other.referencedObject = nullptr;
reset();
return *this;
}
/** Takes-over the object from another pointer. */
ReferenceCountedObjectPtr& operator= (ReferenceCountedObjectPtr&& other)
ReferenceCountedObjectPtr& operator= (ReferenceCountedObjectPtr&& other) noexcept
{
std::swap (referencedObject, other.referencedObject);
return *this;
@ -343,17 +373,15 @@ public:
/** Returns the object that this pointer references.
The pointer returned may be null, of course.
*/
operator ReferencedType*() const noexcept { return referencedObject; }
ReferencedType* get() const noexcept { return referencedObject; }
/** Returns the object that this pointer references.
The pointer returned may be null, of course.
*/
ReferencedType* get() const noexcept { return referencedObject; }
/** Returns the object that this pointer references.
The pointer returned may be null, of course.
*/
ReferencedType* getObject() 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
@ -362,6 +390,43 @@ public:
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
// This old method is deprecated in favour of the shorter and more standard get() method.
JUCE_DEPRECATED_WITH_BODY (ReferencedType* getObject() const, { return get(); })
private:
//==============================================================================
ReferencedType* referencedObject = nullptr;
@ -382,43 +447,15 @@ private:
//==============================================================================
/** Compares two ReferenceCountedObjectPtrs. */
template <typename ObjectType>
bool operator== (const ReferenceCountedObjectPtr<ObjectType>& object1, ObjectType* const object2) noexcept
{
return object1.get() == object2;
}
/** Compares two ReferenceCountedObjectPtrs. */
template <typename ObjectType>
bool operator== (const ReferenceCountedObjectPtr<ObjectType>& object1, const ReferenceCountedObjectPtr<ObjectType>& object2) noexcept
{
return object1.get() == object2.get();
}
/** Compares two ReferenceCountedObjectPtrs. */
template <typename ObjectType>
bool operator== (ObjectType* object1, const ReferenceCountedObjectPtr<ObjectType>& object2) noexcept
template <typename Type>
bool operator== (const Type* object1, const ReferenceCountedObjectPtr<Type>& object2) noexcept
{
return object1 == object2.get();
}
/** Compares two ReferenceCountedObjectPtrs. */
template <typename ObjectType>
bool operator!= (const ReferenceCountedObjectPtr<ObjectType>& object1, const ObjectType* object2) noexcept
{
return object1.get() != object2;
}
/** Compares two ReferenceCountedObjectPtrs. */
template <typename ObjectType>
bool operator!= (const ReferenceCountedObjectPtr<ObjectType>& object1, const ReferenceCountedObjectPtr<ObjectType>& object2) noexcept
{
return object1.get() != object2.get();
}
/** Compares two ReferenceCountedObjectPtrs. */
template <typename ObjectType>
bool operator!= (ObjectType* object1, const ReferenceCountedObjectPtr<ObjectType>& object2) noexcept
template <typename Type>
bool operator!= (const Type* object1, const ReferenceCountedObjectPtr<Type>& object2) noexcept
{
return object1 != object2.get();
}

View File

@ -60,7 +60,7 @@ class ScopedPointer
public:
//==============================================================================
/** Creates a ScopedPointer containing a null pointer. */
inline ScopedPointer() noexcept {}
inline ScopedPointer() = default;
/** Creates a ScopedPointer containing a null pointer. */
inline ScopedPointer (decltype (nullptr)) noexcept {}
@ -149,8 +149,9 @@ public:
/** Clears this pointer, deleting the object it points to if there is one. */
void reset()
{
ContainerDeletePolicy<ObjectType>::destroy (object);
auto* oldObject = object;
object = {};
ContainerDeletePolicy<ObjectType>::destroy (oldObject);
}
/** Sets this pointer to a new object, deleting the old object that it was previously pointing to if there was one. */
@ -162,6 +163,12 @@ public:
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);
}
}
/** Sets this pointer to a new object, deleting the old object that it was previously pointing to if there was one. */

View File

@ -39,7 +39,7 @@ namespace juce
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
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.
@ -137,7 +137,7 @@ private:
static SharedObjectHolder& getSharedObjectHolder() noexcept
{
static void* holder [(sizeof (SharedObjectHolder) + sizeof(void*) - 1) / sizeof(void*)] = { 0 };
static void* holder [(sizeof (SharedObjectHolder) + sizeof(void*) - 1) / sizeof(void*)] = { nullptr };
return *reinterpret_cast<SharedObjectHolder*> (holder);
}

View File

@ -37,7 +37,7 @@ namespace juce
template <typename Type, typename MutexType, bool onlyCreateOncePerRun>
struct SingletonHolder : private MutexType // (inherited so we can use the empty-base-class optimisation)
{
SingletonHolder() noexcept {}
SingletonHolder() = default;
~SingletonHolder()
{

View File

@ -32,18 +32,16 @@ namespace juce
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()
{
// If you're planning on using your WeakReferences in a multi-threaded situation, you may choose
// to create a WeakReference to the object here in the constructor, which will pre-initialise the
// embedded object, avoiding an (extremely unlikely) race condition that could occur if multiple
// threads overlap while creating the first WeakReference to it.
}
MyObject() {}
~MyObject()
{
@ -63,12 +61,12 @@ namespace juce
// Here's an example of using a pointer..
MyObject* n = new MyObject();
auto* n = new MyObject();
WeakReference<MyObject> myObjectRef = n;
MyObject* pointer1 = myObjectRef; // returns a valid pointer to 'n'
auto pointer1 = myObjectRef.get(); // returns a valid pointer to 'n'
delete n;
MyObject* pointer2 = myObjectRef; // returns a null pointer
auto pointer2 = myObjectRef.get(); // now returns nullptr
@endcode
@see WeakReference::Master
@ -80,7 +78,7 @@ class WeakReference
{
public:
/** Creates a null WeakReference. */
inline WeakReference() noexcept {}
inline WeakReference() = default;
/** Creates a WeakReference that points at the given object. */
WeakReference (ObjectType* object) : holder (getRef (object)) {}
@ -89,7 +87,7 @@ public:
WeakReference (const WeakReference& other) noexcept : holder (other.holder) {}
/** Move constructor */
WeakReference (WeakReference&& other) noexcept : holder (static_cast<SharedRef&&> (other.holder)) {}
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; }
@ -98,7 +96,7 @@ public:
WeakReference& operator= (ObjectType* newObject) { holder = getRef (newObject); return *this; }
/** Move assignment operator */
WeakReference& operator= (WeakReference&& other) noexcept { holder = static_cast<SharedRef&&> (other.holder); return *this; }
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; }
@ -143,7 +141,7 @@ public:
JUCE_DECLARE_NON_COPYABLE (SharedPointer)
};
typedef ReferenceCountedObjectPtr<SharedPointer> SharedRef;
using SharedRef = ReferenceCountedObjectPtr<SharedPointer>;
//==============================================================================
/**
@ -154,7 +152,7 @@ public:
class Master
{
public:
Master() noexcept {}
Master() = default;
~Master() noexcept
{
@ -166,11 +164,11 @@ public:
/** The first call to this method will create an internal object that is shared by all weak
references to the object.
*/
SharedPointer* getSharedPointer (ObjectType* object)
SharedRef getSharedPointer (ObjectType* object)
{
if (sharedPointer == nullptr)
{
sharedPointer = new SharedPointer (object);
sharedPointer = *new SharedPointer (object);
}
else
{
@ -206,9 +204,12 @@ public:
private:
SharedRef holder;
static inline SharedPointer* getRef (ObjectType* o)
static inline SharedRef getRef (ObjectType* o)
{
return (o != nullptr) ? o->masterReference.getSharedPointer (o) : nullptr;
if (o != nullptr)
return o->masterReference.getSharedPointer (o);
return {};
}
};
@ -234,9 +235,9 @@ private:
@see WeakReference, WeakReference::Master
*/
#define JUCE_DECLARE_WEAK_REFERENCEABLE(Class) \
struct WeakRefMaster : public WeakReference<Class>::Master { ~WeakRefMaster() { this->clear(); } }; \
struct WeakRefMaster : public juce::WeakReference<Class>::Master { ~WeakRefMaster() { this->clear(); } }; \
WeakRefMaster masterReference; \
friend class WeakReference<Class>; \
friend class juce::WeakReference<Class>; \
} // namespace juce

View File

@ -0,0 +1,356 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
static inline File resolveFilename (const String& name)
{
return File::getCurrentWorkingDirectory().getChildFile (name.unquoted());
}
static inline void checkFileExists (const File& f)
{
if (! f.exists())
ConsoleApplication::fail ("Could not find file: " + f.getFullPathName());
}
static inline void checkFolderExists (const File& f)
{
if (! f.isDirectory())
ConsoleApplication::fail ("Could not find folder: " + f.getFullPathName());
}
File ArgumentList::Argument::resolveAsFile() const
{
return resolveFilename (text);
}
File ArgumentList::Argument::resolveAsExistingFile() const
{
auto f = resolveAsFile();
checkFileExists (f);
return f;
}
File ArgumentList::Argument::resolveAsExistingFolder() const
{
auto f = resolveAsFile();
if (! f.isDirectory())
ConsoleApplication::fail ("Could not find folder: " + f.getFullPathName());
return f;
}
static inline bool isShortOptionFormat (StringRef s) { return s[0] == '-' && s[1] != '-'; }
static inline bool isLongOptionFormat (StringRef s) { return s[0] == '-' && s[1] == '-' && s[2] != '-'; }
static inline 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())
if (auto equalsIndex = text.indexOfChar ('='))
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 (option);
}
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 });
}
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;
}
void ArgumentList::failIfOptionIsMissing (StringRef option) const
{
if (! containsOption (option))
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 {};
}
File ArgumentList::getFileForOption (StringRef option) const
{
auto text = getValueForOption (option);
if (text.isEmpty())
{
failIfOptionIsMissing (option);
ConsoleApplication::fail ("Expected a filename after the " + option + " option");
}
return resolveFilename (text);
}
File ArgumentList::getExistingFileForOption (StringRef option) const
{
auto file = getFileForOption (option);
checkFileExists (file);
return file;
}
File ArgumentList::getExistingFolderForOption (StringRef option) const
{
auto file = getFileForOption (option);
checkFolderExists (file);
return file;
}
//==============================================================================
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::cout << 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
{
if (auto c = findCommand (args, optionMustBeFirstArg))
return invokeCatchingFailures ([=] { c->command (args); return 0; });
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;
}
void ConsoleApplication::printCommandList (const ArgumentList& args) const
{
auto exeName = args.executableName.fromLastOccurrenceOf ("/", false, false)
.fromLastOccurrenceOf ("\\", false, false);
StringArray namesAndArgs;
int descriptionIndent = 0;
for (auto& c : commands)
{
auto nameAndArgs = exeName + " " + c.argumentDescription;
namesAndArgs.add (nameAndArgs);
descriptionIndent = std::max (descriptionIndent, nameAndArgs.length());
}
descriptionIndent = std::min (descriptionIndent + 1, 40);
for (size_t i = 0; i < commands.size(); ++i)
{
auto nameAndArgs = namesAndArgs[(int) i];
std::cout << ' ';
if (nameAndArgs.length() > descriptionIndent)
std::cout << nameAndArgs << std::endl << String::repeatedString (" ", descriptionIndent + 1);
else
std::cout << nameAndArgs.paddedRight (' ', descriptionIndent);
std::cout << commands[i].shortDescription << std::endl;
}
std::cout << std::endl;
}
} // namespace juce

View File

@ -0,0 +1,306 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
/**
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
*/
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.
*/
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"
*/
bool containsOption (StringRef option) const;
/** 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 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 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 filename argument using getFileForOption() and fails with a suitable error if
the file isn't a folder that exists.
*/
File getExistingFolderForOption (StringRef option) const;
/** 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
*/
struct ConsoleApplication
{
//==============================================================================
/**
Represents a command that can be executed if its command-line arguments are matched.
@see ConsoleApplication::addCommand(), ConsoleApplication::findAndRunCommand()
*/
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;
//==============================================================================
/** 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

@ -42,13 +42,13 @@ Result& Result::operator= (const Result& other)
}
Result::Result (Result&& other) noexcept
: errorMessage (static_cast<String&&> (other.errorMessage))
: errorMessage (std::move (other.errorMessage))
{
}
Result& Result::operator= (Result&& other) noexcept
{
errorMessage = static_cast<String&&> (other.errorMessage);
errorMessage = std::move (other.errorMessage);
return *this;
}

View File

@ -91,7 +91,7 @@ public:
//==============================================================================
/** Function type of runtime permission request callbacks. */
typedef std::function<void (bool)> Callback;
using Callback = std::function<void (bool)>;
//==============================================================================
/** Call this method to request a runtime permission.

View File

@ -62,7 +62,7 @@ namespace FunctionTestsHelpers
struct FunctionObject
{
FunctionObject() {}
FunctionObject() = default;
FunctionObject (const FunctionObject& other)
{
@ -72,6 +72,26 @@ namespace FunctionTestsHelpers
int operator()(int i) const { return bigData->sum() + i; }
std::unique_ptr<BigData> bigData { new BigData() };
JUCE_LEAK_DETECTOR (FunctionObject)
};
struct BigFunctionObject
{
BigFunctionObject() = default;
BigFunctionObject (const BigFunctionObject& other)
{
bigData.reset (new BigData (*other.bigData));
}
int operator()(int i) const { return bigData->sum() + i; }
std::unique_ptr<BigData> bigData { new BigData() };
int stackUsage[32];
JUCE_LEAK_DETECTOR (BigFunctionObject)
};
}
@ -95,13 +115,16 @@ public:
std::function<double(double, double)> f2 (FunctionTestsHelpers::multiply);
expectEquals (6.0, f2 (2.0, 3.0));
}
{
beginTest ("Function objects");
std::function<int(int)> f1 = FunctionTestsHelpers::FunctionObject();
expectEquals (f1 (5), FunctionTestsHelpers::BigData::bigDataSum + 5);
std::function<int(int)> f2 { FunctionTestsHelpers::BigFunctionObject() };
expectEquals (f2 (5), FunctionTestsHelpers::BigData::bigDataSum + 5);
}
{
@ -174,13 +197,13 @@ public:
beginTest ("move constructor");
std::unique_ptr<std::function<int()>> fStackTmp (new std::function<int()> (fStack));
std::function<int()> f1 (static_cast<std::function<int()>&&> (*fStackTmp));
std::function<int()> f1 (std::move (*fStackTmp));
fStackTmp.reset();
expectEquals (f1(), 3);
std::unique_ptr<std::function<int()>> fHeapTmp (new std::function<int()> (fHeap));
std::function<int()> f2 (static_cast<std::function<int()>&&> (*fHeapTmp));
std::function<int()> f2 (std::move (*fHeapTmp));
if (*fHeapTmp)
expect (false);
@ -188,7 +211,7 @@ public:
expectEquals (f2(), FunctionTestsHelpers::BigData::bigDataSum);
std::unique_ptr<std::function<int()>> fEmptyTmp (new std::function<int()>());
std::function<int()> f3 (static_cast<std::function<int()>&&> (*fEmptyTmp));
std::function<int()> f3 (std::move (*fEmptyTmp));
fEmptyTmp.reset();
if (f3)
expect (false);
@ -199,14 +222,14 @@ public:
std::function<int()> f1 (fHeap);
std::unique_ptr<std::function<int()>> fStackTmp (new std::function<int()> (fStack));
f1 = static_cast<std::function<int()>&&> (*fStackTmp);
f1 = std::move (*fStackTmp);
fStackTmp.reset();
expectEquals (f1(), 3);
std::function<int()> f2 (fStack);
std::unique_ptr<std::function<int()>> fHeapTmp (new std::function<int()> (fHeap));
f2 = static_cast<std::function<int()>&&> (*fHeapTmp);
f2 = std::move (*fHeapTmp);
if (*fHeapTmp)
expect (false);
@ -215,7 +238,7 @@ public:
std::function<int()> f3 (fHeap);
std::unique_ptr<std::function<int()>> fEmptyTmp (new std::function<int()>());
f3 = static_cast<std::function<int()>&&> (*fEmptyTmp);
f3 = std::move (*fEmptyTmp);
fEmptyTmp.reset();
if (f3)
expect (false);

View File

@ -51,7 +51,7 @@ namespace std
/** Creates an empty function. */
function (decltype (nullptr)) noexcept {}
/** Creates a function targetting the provided Functor. */
/** Creates a function targeting the provided Functor. */
template <typename Functor>
function (Functor f)
{
@ -192,11 +192,7 @@ namespace std
{
if (functorHolderHelper != nullptr)
{
if (functorHolderHelper->getSize() > functorHolderStackSize)
delete[] reinterpret_cast<char*> (functorHolderHelper);
else
functorHolderHelper->~FunctorHolderBase<Result, Arguments...>();
functorHolderHelper->~FunctorHolderBase<Result, Arguments...>();
functorHolderHelper = nullptr;
}
}

View File

@ -145,13 +145,3 @@ uint64 Uuid::hash() const noexcept
}
} // namespace juce
#if ! 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

@ -135,3 +135,13 @@ private:
};
} // namespace juce
#if ! 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

@ -1,169 +0,0 @@
$$CameraApi21
//==============================================================================
public class CameraDeviceStateCallback extends CameraDevice.StateCallback
{
private native void cameraDeviceStateClosed (long host, CameraDevice camera);
private native void cameraDeviceStateDisconnected (long host, CameraDevice camera);
private native void cameraDeviceStateError (long host, CameraDevice camera, int error);
private native void cameraDeviceStateOpened (long host, CameraDevice camera);
CameraDeviceStateCallback (long hostToUse)
{
host = hostToUse;
}
@Override
public void onClosed (CameraDevice camera)
{
cameraDeviceStateClosed (host, camera);
}
@Override
public void onDisconnected (CameraDevice camera)
{
cameraDeviceStateDisconnected (host, camera);
}
@Override
public void onError (CameraDevice camera, int error)
{
cameraDeviceStateError (host, camera, error);
}
@Override
public void onOpened (CameraDevice camera)
{
cameraDeviceStateOpened (host, camera);
}
private long host;
}
//==============================================================================
public class CameraCaptureSessionStateCallback extends CameraCaptureSession.StateCallback
{
private native void cameraCaptureSessionActive (long host, CameraCaptureSession session);
private native void cameraCaptureSessionClosed (long host, CameraCaptureSession session);
private native void cameraCaptureSessionConfigureFailed (long host, CameraCaptureSession session);
private native void cameraCaptureSessionConfigured (long host, CameraCaptureSession session);
private native void cameraCaptureSessionReady (long host, CameraCaptureSession session);
CameraCaptureSessionStateCallback (long hostToUse)
{
host = hostToUse;
}
@Override
public void onActive (CameraCaptureSession session)
{
cameraCaptureSessionActive (host, session);
}
@Override
public void onClosed (CameraCaptureSession session)
{
cameraCaptureSessionClosed (host, session);
}
@Override
public void onConfigureFailed (CameraCaptureSession session)
{
cameraCaptureSessionConfigureFailed (host, session);
}
@Override
public void onConfigured (CameraCaptureSession session)
{
cameraCaptureSessionConfigured (host, session);
}
@Override
public void onReady (CameraCaptureSession session)
{
cameraCaptureSessionReady (host, session);
}
private long host;
}
//==============================================================================
public class CameraCaptureSessionCaptureCallback extends CameraCaptureSession.CaptureCallback
{
private native void cameraCaptureSessionCaptureCompleted (long host, boolean isPreview, CameraCaptureSession session, CaptureRequest request, TotalCaptureResult result);
private native void cameraCaptureSessionCaptureFailed (long host, boolean isPreview, CameraCaptureSession session, CaptureRequest request, CaptureFailure failure);
private native void cameraCaptureSessionCaptureProgressed (long host, boolean isPreview, CameraCaptureSession session, CaptureRequest request, CaptureResult partialResult);
private native void cameraCaptureSessionCaptureStarted (long host, boolean isPreview, CameraCaptureSession session, CaptureRequest request, long timestamp, long frameNumber);
private native void cameraCaptureSessionCaptureSequenceAborted (long host, boolean isPreview, CameraCaptureSession session, int sequenceId);
private native void cameraCaptureSessionCaptureSequenceCompleted (long host, boolean isPreview, CameraCaptureSession session, int sequenceId, long frameNumber);
CameraCaptureSessionCaptureCallback (long hostToUse, boolean shouldBePreview)
{
host = hostToUse;
preview = shouldBePreview;
}
@Override
public void onCaptureCompleted (CameraCaptureSession session, CaptureRequest request,
TotalCaptureResult result)
{
cameraCaptureSessionCaptureCompleted (host, preview, session, request, result);
}
@Override
public void onCaptureFailed (CameraCaptureSession session, CaptureRequest request, CaptureFailure failure)
{
cameraCaptureSessionCaptureFailed (host, preview, session, request, failure);
}
@Override
public void onCaptureProgressed (CameraCaptureSession session, CaptureRequest request,
CaptureResult partialResult)
{
cameraCaptureSessionCaptureProgressed (host, preview, session, request, partialResult);
}
@Override
public void onCaptureSequenceAborted (CameraCaptureSession session, int sequenceId)
{
cameraCaptureSessionCaptureSequenceAborted (host, preview, session, sequenceId);
}
@Override
public void onCaptureSequenceCompleted (CameraCaptureSession session, int sequenceId, long frameNumber)
{
cameraCaptureSessionCaptureSequenceCompleted (host, preview, session, sequenceId, frameNumber);
}
@Override
public void onCaptureStarted (CameraCaptureSession session, CaptureRequest request, long timestamp,
long frameNumber)
{
cameraCaptureSessionCaptureStarted (host, preview, session, request, timestamp, frameNumber);
}
private long host;
private boolean preview;
}
//==============================================================================
public class JuceOrientationEventListener extends OrientationEventListener
{
private native void deviceOrientationChanged (long host, int orientation);
public JuceOrientationEventListener (long hostToUse, Context context, int rate)
{
super (context, rate);
host = hostToUse;
}
@Override
public void onOrientationChanged (int orientation)
{
deviceOrientationChanged (host, orientation);
}
private long host;
}
CameraApi21$$

View File

@ -1,999 +0,0 @@
//==============================================================================
public class BluetoothManager extends ScanCallback
{
BluetoothManager()
{
}
public String[] getMidiBluetoothAddresses()
{
return bluetoothMidiDevices.toArray (new String[bluetoothMidiDevices.size()]);
}
public String getHumanReadableStringForBluetoothAddress (String address)
{
BluetoothDevice btDevice = BluetoothAdapter.getDefaultAdapter().getRemoteDevice (address);
return btDevice.getName();
}
public int getBluetoothDeviceStatus (String address)
{
return getAndroidMidiDeviceManager().getBluetoothDeviceStatus (address);
}
public void startStopScan (boolean shouldStart)
{
BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if (bluetoothAdapter == null)
{
Log.d ("JUCE", "BluetoothManager error: could not get default Bluetooth adapter");
return;
}
BluetoothLeScanner bluetoothLeScanner = bluetoothAdapter.getBluetoothLeScanner();
if (bluetoothLeScanner == null)
{
Log.d ("JUCE", "BluetoothManager error: could not get Bluetooth LE scanner");
return;
}
if (shouldStart)
{
ScanFilter.Builder scanFilterBuilder = new ScanFilter.Builder();
scanFilterBuilder.setServiceUuid (ParcelUuid.fromString (bluetoothLEMidiServiceUUID));
ScanSettings.Builder scanSettingsBuilder = new ScanSettings.Builder();
scanSettingsBuilder.setCallbackType (ScanSettings.CALLBACK_TYPE_ALL_MATCHES)
.setScanMode (ScanSettings.SCAN_MODE_LOW_POWER)
.setScanMode (ScanSettings.MATCH_MODE_STICKY);
bluetoothLeScanner.startScan (Arrays.asList (scanFilterBuilder.build()),
scanSettingsBuilder.build(),
this);
}
else
{
bluetoothLeScanner.stopScan (this);
}
}
public boolean pairBluetoothMidiDevice(String address)
{
BluetoothDevice btDevice = BluetoothAdapter.getDefaultAdapter().getRemoteDevice (address);
if (btDevice == null)
{
Log.d ("JUCE", "failed to create buletooth device from address");
return false;
}
return getAndroidMidiDeviceManager().pairBluetoothDevice (btDevice);
}
public void unpairBluetoothMidiDevice (String address)
{
getAndroidMidiDeviceManager().unpairBluetoothDevice (address);
}
public void onScanFailed (int errorCode)
{
}
public void onScanResult (int callbackType, ScanResult result)
{
if (callbackType == ScanSettings.CALLBACK_TYPE_ALL_MATCHES
|| callbackType == ScanSettings.CALLBACK_TYPE_FIRST_MATCH)
{
BluetoothDevice device = result.getDevice();
if (device != null)
bluetoothMidiDevices.add (device.getAddress());
}
if (callbackType == ScanSettings.CALLBACK_TYPE_MATCH_LOST)
{
Log.d ("JUCE", "ScanSettings.CALLBACK_TYPE_MATCH_LOST");
BluetoothDevice device = result.getDevice();
if (device != null)
{
bluetoothMidiDevices.remove (device.getAddress());
unpairBluetoothMidiDevice (device.getAddress());
}
}
}
public void onBatchScanResults (List<ScanResult> results)
{
for (ScanResult result : results)
onScanResult (ScanSettings.CALLBACK_TYPE_ALL_MATCHES, result);
}
private BluetoothLeScanner scanner;
private static final String bluetoothLEMidiServiceUUID = "03B80E5A-EDE8-4B33-A751-6CE34EC4C700";
private HashSet<String> bluetoothMidiDevices = new HashSet<String>();
}
public static class JuceMidiInputPort extends MidiReceiver implements JuceMidiPort
{
private native void handleReceive (long host, byte[] msg, int offset, int count, long timestamp);
public JuceMidiInputPort (MidiDeviceManager mm, MidiOutputPort actualPort, MidiPortPath portPathToUse, long hostToUse)
{
owner = mm;
androidPort = actualPort;
portPath = portPathToUse;
juceHost = hostToUse;
isConnected = false;
}
@Override
protected void finalize() throws Throwable
{
close();
super.finalize();
}
@Override
public boolean isInputPort()
{
return true;
}
@Override
public void start()
{
if (owner != null && androidPort != null && ! isConnected) {
androidPort.connect(this);
isConnected = true;
}
}
@Override
public void stop()
{
if (owner != null && androidPort != null && isConnected) {
androidPort.disconnect(this);
isConnected = false;
}
}
@Override
public void close()
{
if (androidPort != null) {
try {
androidPort.close();
} catch (IOException exception) {
Log.d("JUCE", "IO Exception while closing port");
}
}
if (owner != null)
owner.removePort (portPath);
owner = null;
androidPort = null;
}
@Override
public void onSend (byte[] msg, int offset, int count, long timestamp)
{
if (count > 0)
handleReceive (juceHost, msg, offset, count, timestamp);
}
@Override
public void onFlush()
{}
@Override
public void sendMidi (byte[] msg, int offset, int count)
{
}
MidiDeviceManager owner;
MidiOutputPort androidPort;
MidiPortPath portPath;
long juceHost;
boolean isConnected;
}
public static class JuceMidiOutputPort implements JuceMidiPort
{
public JuceMidiOutputPort (MidiDeviceManager mm, MidiInputPort actualPort, MidiPortPath portPathToUse)
{
owner = mm;
androidPort = actualPort;
portPath = portPathToUse;
}
@Override
protected void finalize() throws Throwable
{
close();
super.finalize();
}
@Override
public boolean isInputPort()
{
return false;
}
@Override
public void start()
{
}
@Override
public void stop()
{
}
@Override
public void sendMidi (byte[] msg, int offset, int count)
{
if (androidPort != null)
{
try {
androidPort.send(msg, offset, count);
} catch (IOException exception)
{
Log.d ("JUCE", "send midi had IO exception");
}
}
}
@Override
public void close()
{
if (androidPort != null) {
try {
androidPort.close();
} catch (IOException exception) {
Log.d("JUCE", "IO Exception while closing port");
}
}
if (owner != null)
owner.removePort (portPath);
owner = null;
androidPort = null;
}
MidiDeviceManager owner;
MidiInputPort androidPort;
MidiPortPath portPath;
}
private static class MidiPortPath extends Object
{
public MidiPortPath (int deviceIdToUse, boolean direction, int androidIndex)
{
deviceId = deviceIdToUse;
isInput = direction;
portIndex = androidIndex;
}
public int deviceId;
public int portIndex;
public boolean isInput;
@Override
public int hashCode()
{
Integer i = new Integer ((deviceId * 128) + (portIndex < 128 ? portIndex : 127));
return i.hashCode() * (isInput ? -1 : 1);
}
@Override
public boolean equals (Object obj)
{
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
MidiPortPath other = (MidiPortPath) obj;
return (portIndex == other.portIndex && isInput == other.isInput && deviceId == other.deviceId);
}
}
//==============================================================================
public class MidiDeviceManager extends MidiManager.DeviceCallback implements MidiManager.OnDeviceOpenedListener
{
//==============================================================================
private class DummyBluetoothGattCallback extends BluetoothGattCallback
{
public DummyBluetoothGattCallback (MidiDeviceManager mm)
{
super();
owner = mm;
}
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState)
{
if (newState == BluetoothProfile.STATE_CONNECTED)
{
gatt.requestConnectionPriority(BluetoothGatt.CONNECTION_PRIORITY_HIGH);
owner.pairBluetoothDeviceStepTwo (gatt.getDevice());
}
}
public void onServicesDiscovered(BluetoothGatt gatt, int status) {}
public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {}
public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {}
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {}
public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {}
public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {}
public void onReliableWriteCompleted(BluetoothGatt gatt, int status) {}
public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {}
public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) {}
private MidiDeviceManager owner;
}
//==============================================================================
private class MidiDeviceOpenTask extends java.util.TimerTask
{
public MidiDeviceOpenTask (MidiDeviceManager deviceManager, MidiDevice device, BluetoothGatt gattToUse)
{
owner = deviceManager;
midiDevice = device;
btGatt = gattToUse;
}
@Override
public boolean cancel()
{
synchronized (MidiDeviceOpenTask.class)
{
owner = null;
boolean retval = super.cancel();
if (btGatt != null)
{
btGatt.disconnect();
btGatt.close();
btGatt = null;
}
if (midiDevice != null)
{
try
{
midiDevice.close();
}
catch (IOException e)
{}
midiDevice = null;
}
return retval;
}
}
public String getBluetoothAddress()
{
synchronized (MidiDeviceOpenTask.class)
{
if (midiDevice != null)
{
MidiDeviceInfo info = midiDevice.getInfo();
if (info.getType() == MidiDeviceInfo.TYPE_BLUETOOTH)
{
BluetoothDevice btDevice = (BluetoothDevice) info.getProperties().get (info.PROPERTY_BLUETOOTH_DEVICE);
if (btDevice != null)
return btDevice.getAddress();
}
}
}
return "";
}
public BluetoothGatt getGatt() { return btGatt; }
public int getID()
{
return midiDevice.getInfo().getId();
}
@Override
public void run()
{
synchronized (MidiDeviceOpenTask.class)
{
if (owner != null && midiDevice != null)
owner.onDeviceOpenedDelayed (midiDevice);
}
}
private MidiDeviceManager owner;
private MidiDevice midiDevice;
private BluetoothGatt btGatt;
}
//==============================================================================
public MidiDeviceManager()
{
manager = (MidiManager) getSystemService (MIDI_SERVICE);
if (manager == null)
{
Log.d ("JUCE", "MidiDeviceManager error: could not get MidiManager system service");
return;
}
openPorts = new HashMap<MidiPortPath, WeakReference<JuceMidiPort>> ();
midiDevices = new ArrayList<Pair<MidiDevice,BluetoothGatt>>();
openTasks = new HashMap<Integer, MidiDeviceOpenTask>();
btDevicesPairing = new HashMap<String, BluetoothGatt>();
MidiDeviceInfo[] foundDevices = manager.getDevices();
for (MidiDeviceInfo info : foundDevices)
onDeviceAdded (info);
manager.registerDeviceCallback (this, null);
}
protected void finalize() throws Throwable
{
manager.unregisterDeviceCallback (this);
synchronized (MidiDeviceManager.class)
{
btDevicesPairing.clear();
for (Integer deviceID : openTasks.keySet())
openTasks.get (deviceID).cancel();
openTasks = null;
}
for (MidiPortPath key : openPorts.keySet())
openPorts.get (key).get().close();
openPorts = null;
for (Pair<MidiDevice, BluetoothGatt> device : midiDevices)
{
if (device.second != null)
{
device.second.disconnect();
device.second.close();
}
device.first.close();
}
midiDevices.clear();
super.finalize();
}
public String[] getJuceAndroidMidiInputDevices()
{
return getJuceAndroidMidiDevices (MidiDeviceInfo.PortInfo.TYPE_OUTPUT);
}
public String[] getJuceAndroidMidiOutputDevices()
{
return getJuceAndroidMidiDevices (MidiDeviceInfo.PortInfo.TYPE_INPUT);
}
private String[] getJuceAndroidMidiDevices (int portType)
{
// only update the list when JUCE asks for a new list
synchronized (MidiDeviceManager.class)
{
deviceInfos = getDeviceInfos();
}
ArrayList<String> portNames = new ArrayList<String>();
int index = 0;
for (MidiPortPath portInfo = getPortPathForJuceIndex (portType, index); portInfo != null; portInfo = getPortPathForJuceIndex (portType, ++index))
portNames.add (getPortName (portInfo));
String[] names = new String[portNames.size()];
return portNames.toArray (names);
}
private JuceMidiPort openMidiPortWithJuceIndex (int index, long host, boolean isInput)
{
synchronized (MidiDeviceManager.class)
{
int portTypeToFind = (isInput ? MidiDeviceInfo.PortInfo.TYPE_OUTPUT : MidiDeviceInfo.PortInfo.TYPE_INPUT);
MidiPortPath portInfo = getPortPathForJuceIndex (portTypeToFind, index);
if (portInfo != null)
{
// ports must be opened exclusively!
if (openPorts.containsKey (portInfo))
return null;
Pair<MidiDevice,BluetoothGatt> devicePair = getMidiDevicePairForId (portInfo.deviceId);
if (devicePair != null)
{
MidiDevice device = devicePair.first;
if (device != null)
{
JuceMidiPort juceMidiPort = null;
if (isInput)
{
MidiOutputPort outputPort = device.openOutputPort(portInfo.portIndex);
if (outputPort != null)
juceMidiPort = new JuceMidiInputPort(this, outputPort, portInfo, host);
}
else
{
MidiInputPort inputPort = device.openInputPort(portInfo.portIndex);
if (inputPort != null)
juceMidiPort = new JuceMidiOutputPort(this, inputPort, portInfo);
}
if (juceMidiPort != null)
{
openPorts.put(portInfo, new WeakReference<JuceMidiPort>(juceMidiPort));
return juceMidiPort;
}
}
}
}
}
return null;
}
public JuceMidiPort openMidiInputPortWithJuceIndex (int index, long host)
{
return openMidiPortWithJuceIndex (index, host, true);
}
public JuceMidiPort openMidiOutputPortWithJuceIndex (int index)
{
return openMidiPortWithJuceIndex (index, 0, false);
}
/* 0: unpaired, 1: paired, 2: pairing */
public int getBluetoothDeviceStatus (String address)
{
synchronized (MidiDeviceManager.class)
{
if (! address.isEmpty())
{
if (findMidiDeviceForBluetoothAddress (address) != null)
return 1;
if (btDevicesPairing.containsKey (address))
return 2;
if (findOpenTaskForBluetoothAddress (address) != null)
return 2;
}
}
return 0;
}
public boolean pairBluetoothDevice (BluetoothDevice btDevice)
{
String btAddress = btDevice.getAddress();
if (btAddress.isEmpty())
return false;
synchronized (MidiDeviceManager.class)
{
if (getBluetoothDeviceStatus (btAddress) != 0)
return false;
btDevicesPairing.put (btDevice.getAddress(), null);
BluetoothGatt gatt = btDevice.connectGatt (getApplicationContext(), true, new DummyBluetoothGattCallback (this));
if (gatt != null)
{
btDevicesPairing.put (btDevice.getAddress(), gatt);
}
else
{
pairBluetoothDeviceStepTwo (btDevice);
}
}
return true;
}
public void pairBluetoothDeviceStepTwo (BluetoothDevice btDevice)
{
manager.openBluetoothDevice(btDevice, this, null);
}
public void unpairBluetoothDevice (String address)
{
if (address.isEmpty())
return;
synchronized (MidiDeviceManager.class)
{
if (btDevicesPairing.containsKey (address))
{
BluetoothGatt gatt = btDevicesPairing.get (address);
if (gatt != null)
{
gatt.disconnect();
gatt.close();
}
btDevicesPairing.remove (address);
}
MidiDeviceOpenTask openTask = findOpenTaskForBluetoothAddress (address);
if (openTask != null)
{
int deviceID = openTask.getID();
openTask.cancel();
openTasks.remove (deviceID);
}
Pair<MidiDevice, BluetoothGatt> midiDevicePair = findMidiDeviceForBluetoothAddress (address);
if (midiDevicePair != null)
{
MidiDevice midiDevice = midiDevicePair.first;
onDeviceRemoved (midiDevice.getInfo());
try {
midiDevice.close();
}
catch (IOException exception)
{
Log.d ("JUCE", "IOException while closing midi device");
}
}
}
}
private Pair<MidiDevice, BluetoothGatt> findMidiDeviceForBluetoothAddress (String address)
{
for (Pair<MidiDevice,BluetoothGatt> midiDevice : midiDevices)
{
MidiDeviceInfo info = midiDevice.first.getInfo();
if (info.getType() == MidiDeviceInfo.TYPE_BLUETOOTH)
{
BluetoothDevice btDevice = (BluetoothDevice) info.getProperties().get (info.PROPERTY_BLUETOOTH_DEVICE);
if (btDevice != null && btDevice.getAddress().equals (address))
return midiDevice;
}
}
return null;
}
private MidiDeviceOpenTask findOpenTaskForBluetoothAddress (String address)
{
for (Integer deviceID : openTasks.keySet())
{
MidiDeviceOpenTask openTask = openTasks.get (deviceID);
if (openTask.getBluetoothAddress().equals (address))
return openTask;
}
return null;
}
public void removePort (MidiPortPath path)
{
openPorts.remove (path);
}
public String getInputPortNameForJuceIndex (int index)
{
MidiPortPath portInfo = getPortPathForJuceIndex (MidiDeviceInfo.PortInfo.TYPE_OUTPUT, index);
if (portInfo != null)
return getPortName (portInfo);
return "";
}
public String getOutputPortNameForJuceIndex (int index)
{
MidiPortPath portInfo = getPortPathForJuceIndex (MidiDeviceInfo.PortInfo.TYPE_INPUT, index);
if (portInfo != null)
return getPortName (portInfo);
return "";
}
public void onDeviceAdded (MidiDeviceInfo info)
{
// only add standard midi devices
if (info.getType() == info.TYPE_BLUETOOTH)
return;
manager.openDevice (info, this, null);
}
public void onDeviceRemoved (MidiDeviceInfo info)
{
synchronized (MidiDeviceManager.class)
{
Pair<MidiDevice, BluetoothGatt> devicePair = getMidiDevicePairForId (info.getId());
if (devicePair != null)
{
MidiDevice midiDevice = devicePair.first;
BluetoothGatt gatt = devicePair.second;
// close all ports that use this device
boolean removedPort = true;
while (removedPort == true)
{
removedPort = false;
for (MidiPortPath key : openPorts.keySet())
{
if (key.deviceId == info.getId())
{
openPorts.get(key).get().close();
removedPort = true;
break;
}
}
}
if (gatt != null)
{
gatt.disconnect();
gatt.close();
}
midiDevices.remove (devicePair);
}
}
}
public void onDeviceStatusChanged (MidiDeviceStatus status)
{
}
@Override
public void onDeviceOpened (MidiDevice theDevice)
{
synchronized (MidiDeviceManager.class)
{
MidiDeviceInfo info = theDevice.getInfo();
int deviceID = info.getId();
BluetoothGatt gatt = null;
boolean isBluetooth = false;
if (! openTasks.containsKey (deviceID))
{
if (info.getType() == MidiDeviceInfo.TYPE_BLUETOOTH)
{
isBluetooth = true;
BluetoothDevice btDevice = (BluetoothDevice) info.getProperties().get (info.PROPERTY_BLUETOOTH_DEVICE);
if (btDevice != null)
{
String btAddress = btDevice.getAddress();
if (btDevicesPairing.containsKey (btAddress))
{
gatt = btDevicesPairing.get (btAddress);
btDevicesPairing.remove (btAddress);
}
else
{
// unpair was called in the mean time
try
{
Pair<MidiDevice, BluetoothGatt> midiDevicePair = findMidiDeviceForBluetoothAddress (btDevice.getAddress());
if (midiDevicePair != null)
{
gatt = midiDevicePair.second;
if (gatt != null)
{
gatt.disconnect();
gatt.close();
}
}
theDevice.close();
}
catch (IOException e)
{}
return;
}
}
}
MidiDeviceOpenTask openTask = new MidiDeviceOpenTask (this, theDevice, gatt);
openTasks.put (deviceID, openTask);
new java.util.Timer().schedule (openTask, (isBluetooth ? 2000 : 100));
}
}
}
public void onDeviceOpenedDelayed (MidiDevice theDevice)
{
synchronized (MidiDeviceManager.class)
{
int deviceID = theDevice.getInfo().getId();
if (openTasks.containsKey (deviceID))
{
if (! midiDevices.contains(theDevice))
{
BluetoothGatt gatt = openTasks.get (deviceID).getGatt();
openTasks.remove (deviceID);
midiDevices.add (new Pair<MidiDevice,BluetoothGatt> (theDevice, gatt));
}
}
else
{
// unpair was called in the mean time
MidiDeviceInfo info = theDevice.getInfo();
BluetoothDevice btDevice = (BluetoothDevice) info.getProperties().get (info.PROPERTY_BLUETOOTH_DEVICE);
if (btDevice != null)
{
String btAddress = btDevice.getAddress();
Pair<MidiDevice, BluetoothGatt> midiDevicePair = findMidiDeviceForBluetoothAddress (btDevice.getAddress());
if (midiDevicePair != null)
{
BluetoothGatt gatt = midiDevicePair.second;
if (gatt != null)
{
gatt.disconnect();
gatt.close();
}
}
}
try
{
theDevice.close();
}
catch (IOException e)
{}
}
}
}
public String getPortName(MidiPortPath path)
{
int portTypeToFind = (path.isInput ? MidiDeviceInfo.PortInfo.TYPE_INPUT : MidiDeviceInfo.PortInfo.TYPE_OUTPUT);
synchronized (MidiDeviceManager.class)
{
for (MidiDeviceInfo info : deviceInfos)
{
int localIndex = 0;
if (info.getId() == path.deviceId)
{
for (MidiDeviceInfo.PortInfo portInfo : info.getPorts())
{
int portType = portInfo.getType();
if (portType == portTypeToFind)
{
int portIndex = portInfo.getPortNumber();
if (portIndex == path.portIndex)
{
String portName = portInfo.getName();
if (portName.isEmpty())
portName = (String) info.getProperties().get(info.PROPERTY_NAME);
return portName;
}
}
}
}
}
}
return "";
}
public MidiPortPath getPortPathForJuceIndex (int portType, int juceIndex)
{
int portIdx = 0;
for (MidiDeviceInfo info : deviceInfos)
{
for (MidiDeviceInfo.PortInfo portInfo : info.getPorts())
{
if (portInfo.getType() == portType)
{
if (portIdx == juceIndex)
return new MidiPortPath (info.getId(),
(portType == MidiDeviceInfo.PortInfo.TYPE_INPUT),
portInfo.getPortNumber());
portIdx++;
}
}
}
return null;
}
private MidiDeviceInfo[] getDeviceInfos()
{
synchronized (MidiDeviceManager.class)
{
MidiDeviceInfo[] infos = new MidiDeviceInfo[midiDevices.size()];
int idx = 0;
for (Pair<MidiDevice,BluetoothGatt> midiDevice : midiDevices)
infos[idx++] = midiDevice.first.getInfo();
return infos;
}
}
private Pair<MidiDevice, BluetoothGatt> getMidiDevicePairForId (int deviceId)
{
synchronized (MidiDeviceManager.class)
{
for (Pair<MidiDevice,BluetoothGatt> midiDevice : midiDevices)
if (midiDevice.first.getInfo().getId() == deviceId)
return midiDevice;
}
return null;
}
private MidiManager manager;
private HashMap<String, BluetoothGatt> btDevicesPairing;
private HashMap<Integer, MidiDeviceOpenTask> openTasks;
private ArrayList<Pair<MidiDevice, BluetoothGatt>> midiDevices;
private MidiDeviceInfo[] deviceInfos;
private HashMap<MidiPortPath, WeakReference<JuceMidiPort>> openPorts;
}
public MidiDeviceManager getAndroidMidiDeviceManager()
{
if (getSystemService (MIDI_SERVICE) == null)
return null;
synchronized (JuceAppActivity.class)
{
if (midiDeviceManager == null)
midiDeviceManager = new MidiDeviceManager();
}
return midiDeviceManager;
}
public BluetoothManager getAndroidBluetoothManager()
{
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
if (adapter == null)
return null;
if (adapter.getBluetoothLeScanner() == null)
return null;
synchronized (JuceAppActivity.class)
{
if (bluetoothManager == null)
bluetoothManager = new BluetoothManager();
}
return bluetoothManager;
}

View File

@ -1,85 +0,0 @@
//==============================================================================
public class BluetoothManager
{
BluetoothManager()
{
}
public String[] getMidiBluetoothAddresses()
{
String[] bluetoothAddresses = new String[0];
return bluetoothAddresses;
}
public String getHumanReadableStringForBluetoothAddress (String address)
{
return address;
}
public int getBluetoothDeviceStatus (String address)
{
return 0;
}
public void startStopScan (boolean shouldStart)
{
}
public boolean pairBluetoothMidiDevice(String address)
{
return false;
}
public void unpairBluetoothMidiDevice (String address)
{
}
}
//==============================================================================
public class MidiDeviceManager
{
public MidiDeviceManager()
{
}
public String[] getJuceAndroidMidiInputDevices()
{
return new String[0];
}
public String[] getJuceAndroidMidiOutputDevices()
{
return new String[0];
}
public JuceMidiPort openMidiInputPortWithJuceIndex (int index, long host)
{
return null;
}
public JuceMidiPort openMidiOutputPortWithJuceIndex (int index)
{
return null;
}
public String getInputPortNameForJuceIndex (int index)
{
return "";
}
public String getOutputPortNameForJuceIndex (int index)
{
return "";
}
}
public MidiDeviceManager getAndroidMidiDeviceManager()
{
return null;
}
public BluetoothManager getAndroidBluetoothManager()
{
return null;
}

View File

@ -1,12 +0,0 @@
@Override
public void onRequestPermissionsResult (int permissionID, String permissions[], int[] grantResults)
{
boolean permissionsGranted = (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED);
if (! permissionsGranted)
Log.d ("JUCE", "onRequestPermissionsResult: runtime permission was DENIED: " + getAndroidPermissionName (permissionID));
Long ptrToCallback = permissionCallbackPtrMap.get (permissionID);
permissionCallbackPtrMap.remove (permissionID);
androidRuntimePermissionsCallback (permissionsGranted, ptrToCallback);
}

View File

@ -1,138 +0,0 @@
package com.juce;
import android.content.ContentProvider;
import android.content.ContentValues;
import android.content.res.AssetFileDescriptor;
import android.content.res.Resources;
import android.database.Cursor;
import android.database.MatrixCursor;
import android.net.Uri;
import android.os.FileObserver;
import android.os.ParcelFileDescriptor;
import java.lang.String;
public final class SharingContentProvider extends ContentProvider
{
private Object lock = new Object();
private native void contentSharerFileObserverEvent (long host, int event, String path);
private native Cursor contentSharerQuery (Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder);
private native void contentSharerCursorClosed (long host);
private native AssetFileDescriptor contentSharerOpenFile (Uri uri, String mode);
private native String[] contentSharerGetStreamTypes (Uri uri, String mimeTypeFilter);
public final class ProviderFileObserver extends FileObserver
{
public ProviderFileObserver (long hostToUse, String path, int mask)
{
super (path, mask);
host = hostToUse;
}
public void onEvent (int event, String path)
{
contentSharerFileObserverEvent (host, event, path);
}
private long host;
}
public final class ProviderCursor extends MatrixCursor
{
ProviderCursor (long hostToUse, String[] columnNames)
{
super (columnNames);
host = hostToUse;
}
@Override
public void close()
{
super.close();
contentSharerCursorClosed (host);
}
private long host;
}
@Override
public boolean onCreate()
{
return true;
}
@Override
public Cursor query (Uri url, String[] projection, String selection,
String[] selectionArgs, String sortOrder)
{
synchronized (lock)
{
return contentSharerQuery (url, projection, selection, selectionArgs, sortOrder);
}
}
@Override
public Uri insert (Uri uri, ContentValues values)
{
return null;
}
@Override
public int update (Uri uri, ContentValues values, String selection,
String[] selectionArgs)
{
return 0;
}
@Override
public int delete (Uri uri, String selection, String[] selectionArgs)
{
return 0;
}
@Override
public String getType (Uri uri)
{
return null;
}
@Override
public AssetFileDescriptor openAssetFile (Uri uri, String mode)
{
synchronized (lock)
{
return contentSharerOpenFile (uri, mode);
}
}
@Override
public ParcelFileDescriptor openFile (Uri uri, String mode)
{
synchronized (lock)
{
AssetFileDescriptor result = contentSharerOpenFile (uri, mode);
if (result != null)
return result.getParcelFileDescriptor();
return null;
}
}
$$ContentProviderApi11
@Override
public String[] getStreamTypes (Uri uri, String mimeTypeFilter)
{
synchronized (lock)
{
return contentSharerGetStreamTypes (uri, mimeTypeFilter);
}
}
ContentProviderApi11$$
}

View File

@ -1,69 +0,0 @@
$$WebViewNativeApi23 private native void webViewReceivedError (long host, WebView view, WebResourceRequest request, WebResourceError error);WebViewNativeApi23$$
$$WebViewNativeApi21 private native void webViewReceivedHttpError (long host, WebView view, WebResourceRequest request, WebResourceResponse errorResponse);WebViewNativeApi21$$
$$WebViewApi1_10
@Override
public void onPageStarted (WebView view, String url, Bitmap favicon)
{
if (host != 0)
webViewPageLoadStarted (host, view, url);
}
WebViewApi1_10$$
$$WebViewApi11_20
@Override
public WebResourceResponse shouldInterceptRequest (WebView view, String url)
{
synchronized (hostLock)
{
if (host != 0)
{
boolean shouldLoad = webViewPageLoadStarted (host, view, url);
if (shouldLoad)
return null;
}
}
return new WebResourceResponse ("text/html", null, null);
}
WebViewApi11_20$$
$$WebViewApi21
@Override
public WebResourceResponse shouldInterceptRequest (WebView view, WebResourceRequest request)
{
synchronized (hostLock)
{
if (host != 0)
{
boolean shouldLoad = webViewPageLoadStarted (host, view, request.getUrl().toString());
if (shouldLoad)
return null;
}
}
return new WebResourceResponse ("text/html", null, null);
}
WebViewApi21$$
$$WebViewApi23
@Override
public void onReceivedError (WebView view, WebResourceRequest request, WebResourceError error)
{
if (host == 0)
return;
webViewReceivedError (host, view, request, error);
}
@Override
public void onReceivedHttpError (WebView view, WebResourceRequest request, WebResourceResponse errorResponse)
{
if (host == 0)
return;
webViewReceivedHttpError (host, view, request, errorResponse);
}
WebViewApi23$$

View File

@ -1,971 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
package com.android.vending.billing;
/**
* InAppBillingService is the service that provides in-app billing version 3 and beyond.
* This service provides the following features:
* 1. Provides a new API to get details of in-app items published for the app including
* price, type, title and description.
* 2. The purchase flow is synchronous and purchase information is available immediately
* after it completes.
* 3. Purchase information of in-app purchases is maintained within the Google Play system
* till the purchase is consumed.
* 4. An API to consume a purchase of an inapp item. All purchases of one-time
* in-app items are consumable and thereafter can be purchased again.
* 5. An API to get current purchases of the user immediately. This will not contain any
* consumed purchases.
*
* All calls will give a response code with the following possible values
* RESULT_OK = 0 - success
* RESULT_USER_CANCELED = 1 - User pressed back or canceled a dialog
* RESULT_SERVICE_UNAVAILABLE = 2 - The network connection is down
* RESULT_BILLING_UNAVAILABLE = 3 - This billing API version is not supported for the type requested
* RESULT_ITEM_UNAVAILABLE = 4 - Requested SKU is not available for purchase
* RESULT_DEVELOPER_ERROR = 5 - Invalid arguments provided to the API
* RESULT_ERROR = 6 - Fatal error during the API action
* RESULT_ITEM_ALREADY_OWNED = 7 - Failure to purchase since item is already owned
* RESULT_ITEM_NOT_OWNED = 8 - Failure to consume since item is not owned
*/
public interface IInAppBillingService extends android.os.IInterface
{
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements com.android.vending.billing.IInAppBillingService
{
private static final java.lang.String DESCRIPTOR = "com.android.vending.billing.IInAppBillingService";
/** Construct the stub at attach it to the interface. */
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.android.vending.billing.IInAppBillingService interface,
* generating a proxy if needed.
*/
public static com.android.vending.billing.IInAppBillingService asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.android.vending.billing.IInAppBillingService))) {
return ((com.android.vending.billing.IInAppBillingService)iin);
}
return new com.android.vending.billing.IInAppBillingService.Stub.Proxy(obj);
}
@Override public android.os.IBinder asBinder()
{
return this;
}
@Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException
{
switch (code)
{
case INTERFACE_TRANSACTION:
{
reply.writeString(DESCRIPTOR);
return true;
}
case TRANSACTION_isBillingSupported:
{
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
java.lang.String _arg1;
_arg1 = data.readString();
java.lang.String _arg2;
_arg2 = data.readString();
int _result = this.isBillingSupported(_arg0, _arg1, _arg2);
reply.writeNoException();
reply.writeInt(_result);
return true;
}
case TRANSACTION_getSkuDetails:
{
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
java.lang.String _arg1;
_arg1 = data.readString();
java.lang.String _arg2;
_arg2 = data.readString();
android.os.Bundle _arg3;
if ((0!=data.readInt())) {
_arg3 = android.os.Bundle.CREATOR.createFromParcel(data);
}
else {
_arg3 = null;
}
android.os.Bundle _result = this.getSkuDetails(_arg0, _arg1, _arg2, _arg3);
reply.writeNoException();
if ((_result!=null)) {
reply.writeInt(1);
_result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
}
else {
reply.writeInt(0);
}
return true;
}
case TRANSACTION_getBuyIntent:
{
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
java.lang.String _arg1;
_arg1 = data.readString();
java.lang.String _arg2;
_arg2 = data.readString();
java.lang.String _arg3;
_arg3 = data.readString();
java.lang.String _arg4;
_arg4 = data.readString();
android.os.Bundle _result = this.getBuyIntent(_arg0, _arg1, _arg2, _arg3, _arg4);
reply.writeNoException();
if ((_result!=null)) {
reply.writeInt(1);
_result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
}
else {
reply.writeInt(0);
}
return true;
}
case TRANSACTION_getPurchases:
{
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
java.lang.String _arg1;
_arg1 = data.readString();
java.lang.String _arg2;
_arg2 = data.readString();
java.lang.String _arg3;
_arg3 = data.readString();
android.os.Bundle _result = this.getPurchases(_arg0, _arg1, _arg2, _arg3);
reply.writeNoException();
if ((_result!=null)) {
reply.writeInt(1);
_result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
}
else {
reply.writeInt(0);
}
return true;
}
case TRANSACTION_consumePurchase:
{
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
java.lang.String _arg1;
_arg1 = data.readString();
java.lang.String _arg2;
_arg2 = data.readString();
int _result = this.consumePurchase(_arg0, _arg1, _arg2);
reply.writeNoException();
reply.writeInt(_result);
return true;
}
case TRANSACTION_stub:
{
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
java.lang.String _arg1;
_arg1 = data.readString();
java.lang.String _arg2;
_arg2 = data.readString();
int _result = this.stub(_arg0, _arg1, _arg2);
reply.writeNoException();
reply.writeInt(_result);
return true;
}
case TRANSACTION_getBuyIntentToReplaceSkus:
{
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
java.lang.String _arg1;
_arg1 = data.readString();
java.util.List<java.lang.String> _arg2;
_arg2 = data.createStringArrayList();
java.lang.String _arg3;
_arg3 = data.readString();
java.lang.String _arg4;
_arg4 = data.readString();
java.lang.String _arg5;
_arg5 = data.readString();
android.os.Bundle _result = this.getBuyIntentToReplaceSkus(_arg0, _arg1, _arg2, _arg3, _arg4, _arg5);
reply.writeNoException();
if ((_result!=null)) {
reply.writeInt(1);
_result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
}
else {
reply.writeInt(0);
}
return true;
}
case TRANSACTION_getBuyIntentExtraParams:
{
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
java.lang.String _arg1;
_arg1 = data.readString();
java.lang.String _arg2;
_arg2 = data.readString();
java.lang.String _arg3;
_arg3 = data.readString();
java.lang.String _arg4;
_arg4 = data.readString();
android.os.Bundle _arg5;
if ((0!=data.readInt())) {
_arg5 = android.os.Bundle.CREATOR.createFromParcel(data);
}
else {
_arg5 = null;
}
android.os.Bundle _result = this.getBuyIntentExtraParams(_arg0, _arg1, _arg2, _arg3, _arg4, _arg5);
reply.writeNoException();
if ((_result!=null)) {
reply.writeInt(1);
_result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
}
else {
reply.writeInt(0);
}
return true;
}
case TRANSACTION_getPurchaseHistory:
{
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
java.lang.String _arg1;
_arg1 = data.readString();
java.lang.String _arg2;
_arg2 = data.readString();
java.lang.String _arg3;
_arg3 = data.readString();
android.os.Bundle _arg4;
if ((0!=data.readInt())) {
_arg4 = android.os.Bundle.CREATOR.createFromParcel(data);
}
else {
_arg4 = null;
}
android.os.Bundle _result = this.getPurchaseHistory(_arg0, _arg1, _arg2, _arg3, _arg4);
reply.writeNoException();
if ((_result!=null)) {
reply.writeInt(1);
_result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
}
else {
reply.writeInt(0);
}
return true;
}
case TRANSACTION_isBillingSupportedExtraParams:
{
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
java.lang.String _arg1;
_arg1 = data.readString();
java.lang.String _arg2;
_arg2 = data.readString();
android.os.Bundle _arg3;
if ((0!=data.readInt())) {
_arg3 = android.os.Bundle.CREATOR.createFromParcel(data);
}
else {
_arg3 = null;
}
int _result = this.isBillingSupportedExtraParams(_arg0, _arg1, _arg2, _arg3);
reply.writeNoException();
reply.writeInt(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements com.android.vending.billing.IInAppBillingService
{
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote)
{
mRemote = remote;
}
@Override public android.os.IBinder asBinder()
{
return mRemote;
}
public java.lang.String getInterfaceDescriptor()
{
return DESCRIPTOR;
}
@Override public int isBillingSupported(int apiVersion, java.lang.String packageName, java.lang.String type) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
int _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(apiVersion);
_data.writeString(packageName);
_data.writeString(type);
mRemote.transact(Stub.TRANSACTION_isBillingSupported, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
/**
* Provides details of a list of SKUs
* Given a list of SKUs of a valid type in the skusBundle, this returns a bundle
* with a list JSON strings containing the productId, price, title and description.
* This API can be called with a maximum of 20 SKUs.
* @param apiVersion billing API version that the app is using
* @param packageName the package name of the calling app
* @param type of the in-app items ("inapp" for one-time purchases
* and "subs" for subscriptions)
* @param skusBundle bundle containing a StringArrayList of SKUs with key "ITEM_ID_LIST"
* @return Bundle containing the following key-value pairs
* "RESPONSE_CODE" with int value, RESULT_OK(0) if success, appropriate response codes
* on failures.
* "DETAILS_LIST" with a StringArrayList containing purchase information
* in JSON format similar to:
* '{ "productId" : "exampleSku",
* "type" : "inapp",
* "price" : "$5.00",
* "price_currency": "USD",
* "price_amount_micros": 5000000,
* "title : "Example Title",
* "description" : "This is an example description" }'
*/
@Override public android.os.Bundle getSkuDetails(int apiVersion, java.lang.String packageName, java.lang.String type, android.os.Bundle skusBundle) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
android.os.Bundle _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(apiVersion);
_data.writeString(packageName);
_data.writeString(type);
if ((skusBundle!=null)) {
_data.writeInt(1);
skusBundle.writeToParcel(_data, 0);
}
else {
_data.writeInt(0);
}
mRemote.transact(Stub.TRANSACTION_getSkuDetails, _data, _reply, 0);
_reply.readException();
if ((0!=_reply.readInt())) {
_result = android.os.Bundle.CREATOR.createFromParcel(_reply);
}
else {
_result = null;
}
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
/**
* Returns a pending intent to launch the purchase flow for an in-app item by providing a SKU,
* the type, a unique purchase token and an optional developer payload.
* @param apiVersion billing API version that the app is using
* @param packageName package name of the calling app
* @param sku the SKU of the in-app item as published in the developer console
* @param type of the in-app item being purchased ("inapp" for one-time purchases
* and "subs" for subscriptions)
* @param developerPayload optional argument to be sent back with the purchase information
* @return Bundle containing the following key-value pairs
* "RESPONSE_CODE" with int value, RESULT_OK(0) if success, appropriate response codes
* on failures.
* "BUY_INTENT" - PendingIntent to start the purchase flow
*
* The Pending intent should be launched with startIntentSenderForResult. When purchase flow
* has completed, the onActivityResult() will give a resultCode of OK or CANCELED.
* If the purchase is successful, the result data will contain the following key-value pairs
* "RESPONSE_CODE" with int value, RESULT_OK(0) if success, appropriate response
* codes on failures.
* "INAPP_PURCHASE_DATA" - String in JSON format similar to
* '{"orderId":"12999763169054705758.1371079406387615",
* "packageName":"com.example.app",
* "productId":"exampleSku",
* "purchaseTime":1345678900000,
* "purchaseToken" : "122333444455555",
* "developerPayload":"example developer payload" }'
* "INAPP_DATA_SIGNATURE" - String containing the signature of the purchase data that
* was signed with the private key of the developer
*/
@Override public android.os.Bundle getBuyIntent(int apiVersion, java.lang.String packageName, java.lang.String sku, java.lang.String type, java.lang.String developerPayload) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
android.os.Bundle _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(apiVersion);
_data.writeString(packageName);
_data.writeString(sku);
_data.writeString(type);
_data.writeString(developerPayload);
mRemote.transact(Stub.TRANSACTION_getBuyIntent, _data, _reply, 0);
_reply.readException();
if ((0!=_reply.readInt())) {
_result = android.os.Bundle.CREATOR.createFromParcel(_reply);
}
else {
_result = null;
}
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
/**
* Returns the current SKUs owned by the user of the type and package name specified along with
* purchase information and a signature of the data to be validated.
* This will return all SKUs that have been purchased in V3 and managed items purchased using
* V1 and V2 that have not been consumed.
* @param apiVersion billing API version that the app is using
* @param packageName package name of the calling app
* @param type of the in-app items being requested ("inapp" for one-time purchases
* and "subs" for subscriptions)
* @param continuationToken to be set as null for the first call, if the number of owned
* skus are too many, a continuationToken is returned in the response bundle.
* This method can be called again with the continuation token to get the next set of
* owned skus.
* @return Bundle containing the following key-value pairs
* "RESPONSE_CODE" with int value, RESULT_OK(0) if success, appropriate response codes
on failures.
* "INAPP_PURCHASE_ITEM_LIST" - StringArrayList containing the list of SKUs
* "INAPP_PURCHASE_DATA_LIST" - StringArrayList containing the purchase information
* "INAPP_DATA_SIGNATURE_LIST"- StringArrayList containing the signatures
* of the purchase information
* "INAPP_CONTINUATION_TOKEN" - String containing a continuation token for the
* next set of in-app purchases. Only set if the
* user has more owned skus than the current list.
*/
@Override public android.os.Bundle getPurchases(int apiVersion, java.lang.String packageName, java.lang.String type, java.lang.String continuationToken) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
android.os.Bundle _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(apiVersion);
_data.writeString(packageName);
_data.writeString(type);
_data.writeString(continuationToken);
mRemote.transact(Stub.TRANSACTION_getPurchases, _data, _reply, 0);
_reply.readException();
if ((0!=_reply.readInt())) {
_result = android.os.Bundle.CREATOR.createFromParcel(_reply);
}
else {
_result = null;
}
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override public int consumePurchase(int apiVersion, java.lang.String packageName, java.lang.String purchaseToken) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
int _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(apiVersion);
_data.writeString(packageName);
_data.writeString(purchaseToken);
mRemote.transact(Stub.TRANSACTION_consumePurchase, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override public int stub(int apiVersion, java.lang.String packageName, java.lang.String type) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
int _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(apiVersion);
_data.writeString(packageName);
_data.writeString(type);
mRemote.transact(Stub.TRANSACTION_stub, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
/**
* Returns a pending intent to launch the purchase flow for upgrading or downgrading a
* subscription. The existing owned SKU(s) should be provided along with the new SKU that
* the user is upgrading or downgrading to.
* @param apiVersion billing API version that the app is using, must be 5 or later
* @param packageName package name of the calling app
* @param oldSkus the SKU(s) that the user is upgrading or downgrading from,
* if null or empty this method will behave like {@link #getBuyIntent}
* @param newSku the SKU that the user is upgrading or downgrading to
* @param type of the item being purchased, currently must be "subs"
* @param developerPayload optional argument to be sent back with the purchase information
* @return Bundle containing the following key-value pairs
* "RESPONSE_CODE" with int value, RESULT_OK(0) if success, appropriate response codes
* on failures.
* "BUY_INTENT" - PendingIntent to start the purchase flow
*
* The Pending intent should be launched with startIntentSenderForResult. When purchase flow
* has completed, the onActivityResult() will give a resultCode of OK or CANCELED.
* If the purchase is successful, the result data will contain the following key-value pairs
* "RESPONSE_CODE" with int value, RESULT_OK(0) if success, appropriate response
* codes on failures.
* "INAPP_PURCHASE_DATA" - String in JSON format similar to
* '{"orderId":"12999763169054705758.1371079406387615",
* "packageName":"com.example.app",
* "productId":"exampleSku",
* "purchaseTime":1345678900000,
* "purchaseToken" : "122333444455555",
* "developerPayload":"example developer payload" }'
* "INAPP_DATA_SIGNATURE" - String containing the signature of the purchase data that
* was signed with the private key of the developer
*/
@Override public android.os.Bundle getBuyIntentToReplaceSkus(int apiVersion, java.lang.String packageName, java.util.List<java.lang.String> oldSkus, java.lang.String newSku, java.lang.String type, java.lang.String developerPayload) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
android.os.Bundle _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(apiVersion);
_data.writeString(packageName);
_data.writeStringList(oldSkus);
_data.writeString(newSku);
_data.writeString(type);
_data.writeString(developerPayload);
mRemote.transact(Stub.TRANSACTION_getBuyIntentToReplaceSkus, _data, _reply, 0);
_reply.readException();
if ((0!=_reply.readInt())) {
_result = android.os.Bundle.CREATOR.createFromParcel(_reply);
}
else {
_result = null;
}
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
/**
* Returns a pending intent to launch the purchase flow for an in-app item. This method is
* a variant of the {@link #getBuyIntent} method and takes an additional {@code extraParams}
* parameter. This parameter is a Bundle of optional keys and values that affect the
* operation of the method.
* @param apiVersion billing API version that the app is using, must be 6 or later
* @param packageName package name of the calling app
* @param sku the SKU of the in-app item as published in the developer console
* @param type of the in-app item being purchased ("inapp" for one-time purchases
* and "subs" for subscriptions)
* @param developerPayload optional argument to be sent back with the purchase information
* @extraParams a Bundle with the following optional keys:
* "skusToReplace" - List<String> - an optional list of SKUs that the user is
* upgrading or downgrading from.
* Pass this field if the purchase is upgrading or downgrading
* existing subscriptions.
* The specified SKUs are replaced with the SKUs that the user is
* purchasing. Google Play replaces the specified SKUs at the start of
* the next billing cycle.
* "replaceSkusProration" - Boolean - whether the user should be credited for any unused
* subscription time on the SKUs they are upgrading or downgrading.
* If you set this field to true, Google Play swaps out the old SKUs
* and credits the user with the unused value of their subscription
* time on a pro-rated basis.
* Google Play applies this credit to the new subscription, and does
* not begin billing the user for the new subscription until after
* the credit is used up.
* If you set this field to false, the user does not receive credit for
* any unused subscription time and the recurrence date does not
* change.
* Default value is true. Ignored if you do not pass skusToReplace.
* "accountId" - String - an optional obfuscated string that is uniquely
* associated with the user's account in your app.
* If you pass this value, Google Play can use it to detect irregular
* activity, such as many devices making purchases on the same
* account in a short period of time.
* Do not use the developer ID or the user's Google ID for this field.
* In addition, this field should not contain the user's ID in
* cleartext.
* We recommend that you use a one-way hash to generate a string from
* the user's ID, and store the hashed string in this field.
* "vr" - Boolean - an optional flag indicating whether the returned intent
* should start a VR purchase flow. The apiVersion must also be 7 or
* later to use this flag.
*/
@Override public android.os.Bundle getBuyIntentExtraParams(int apiVersion, java.lang.String packageName, java.lang.String sku, java.lang.String type, java.lang.String developerPayload, android.os.Bundle extraParams) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
android.os.Bundle _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(apiVersion);
_data.writeString(packageName);
_data.writeString(sku);
_data.writeString(type);
_data.writeString(developerPayload);
if ((extraParams!=null)) {
_data.writeInt(1);
extraParams.writeToParcel(_data, 0);
}
else {
_data.writeInt(0);
}
mRemote.transact(Stub.TRANSACTION_getBuyIntentExtraParams, _data, _reply, 0);
_reply.readException();
if ((0!=_reply.readInt())) {
_result = android.os.Bundle.CREATOR.createFromParcel(_reply);
}
else {
_result = null;
}
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
/**
* Returns the most recent purchase made by the user for each SKU, even if that purchase is
* expired, canceled, or consumed.
* @param apiVersion billing API version that the app is using, must be 6 or later
* @param packageName package name of the calling app
* @param type of the in-app items being requested ("inapp" for one-time purchases
* and "subs" for subscriptions)
* @param continuationToken to be set as null for the first call, if the number of owned
* skus is too large, a continuationToken is returned in the response bundle.
* This method can be called again with the continuation token to get the next set of
* owned skus.
* @param extraParams a Bundle with extra params that would be appended into http request
* query string. Not used at this moment. Reserved for future functionality.
* @return Bundle containing the following key-value pairs
* "RESPONSE_CODE" with int value: RESULT_OK(0) if success,
* {@link IabHelper#BILLING_RESPONSE_RESULT_*} response codes on failures.
*
* "INAPP_PURCHASE_ITEM_LIST" - ArrayList<String> containing the list of SKUs
* "INAPP_PURCHASE_DATA_LIST" - ArrayList<String> containing the purchase information
* "INAPP_DATA_SIGNATURE_LIST"- ArrayList<String> containing the signatures
* of the purchase information
* "INAPP_CONTINUATION_TOKEN" - String containing a continuation token for the
* next set of in-app purchases. Only set if the
* user has more owned skus than the current list.
*/
@Override public android.os.Bundle getPurchaseHistory(int apiVersion, java.lang.String packageName, java.lang.String type, java.lang.String continuationToken, android.os.Bundle extraParams) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
android.os.Bundle _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(apiVersion);
_data.writeString(packageName);
_data.writeString(type);
_data.writeString(continuationToken);
if ((extraParams!=null)) {
_data.writeInt(1);
extraParams.writeToParcel(_data, 0);
}
else {
_data.writeInt(0);
}
mRemote.transact(Stub.TRANSACTION_getPurchaseHistory, _data, _reply, 0);
_reply.readException();
if ((0!=_reply.readInt())) {
_result = android.os.Bundle.CREATOR.createFromParcel(_reply);
}
else {
_result = null;
}
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override public int isBillingSupportedExtraParams(int apiVersion, java.lang.String packageName, java.lang.String type, android.os.Bundle extraParams) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
int _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(apiVersion);
_data.writeString(packageName);
_data.writeString(type);
if ((extraParams!=null)) {
_data.writeInt(1);
extraParams.writeToParcel(_data, 0);
}
else {
_data.writeInt(0);
}
mRemote.transact(Stub.TRANSACTION_isBillingSupportedExtraParams, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
static final int TRANSACTION_isBillingSupported = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_getSkuDetails = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
static final int TRANSACTION_getBuyIntent = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);
static final int TRANSACTION_getPurchases = (android.os.IBinder.FIRST_CALL_TRANSACTION + 3);
static final int TRANSACTION_consumePurchase = (android.os.IBinder.FIRST_CALL_TRANSACTION + 4);
static final int TRANSACTION_stub = (android.os.IBinder.FIRST_CALL_TRANSACTION + 5);
static final int TRANSACTION_getBuyIntentToReplaceSkus = (android.os.IBinder.FIRST_CALL_TRANSACTION + 6);
static final int TRANSACTION_getBuyIntentExtraParams = (android.os.IBinder.FIRST_CALL_TRANSACTION + 7);
static final int TRANSACTION_getPurchaseHistory = (android.os.IBinder.FIRST_CALL_TRANSACTION + 8);
static final int TRANSACTION_isBillingSupportedExtraParams = (android.os.IBinder.FIRST_CALL_TRANSACTION + 9);
}
public int isBillingSupported(int apiVersion, java.lang.String packageName, java.lang.String type) throws android.os.RemoteException;
/**
* Provides details of a list of SKUs
* Given a list of SKUs of a valid type in the skusBundle, this returns a bundle
* with a list JSON strings containing the productId, price, title and description.
* This API can be called with a maximum of 20 SKUs.
* @param apiVersion billing API version that the app is using
* @param packageName the package name of the calling app
* @param type of the in-app items ("inapp" for one-time purchases
* and "subs" for subscriptions)
* @param skusBundle bundle containing a StringArrayList of SKUs with key "ITEM_ID_LIST"
* @return Bundle containing the following key-value pairs
* "RESPONSE_CODE" with int value, RESULT_OK(0) if success, appropriate response codes
* on failures.
* "DETAILS_LIST" with a StringArrayList containing purchase information
* in JSON format similar to:
* '{ "productId" : "exampleSku",
* "type" : "inapp",
* "price" : "$5.00",
* "price_currency": "USD",
* "price_amount_micros": 5000000,
* "title : "Example Title",
* "description" : "This is an example description" }'
*/
public android.os.Bundle getSkuDetails(int apiVersion, java.lang.String packageName, java.lang.String type, android.os.Bundle skusBundle) throws android.os.RemoteException;
/**
* Returns a pending intent to launch the purchase flow for an in-app item by providing a SKU,
* the type, a unique purchase token and an optional developer payload.
* @param apiVersion billing API version that the app is using
* @param packageName package name of the calling app
* @param sku the SKU of the in-app item as published in the developer console
* @param type of the in-app item being purchased ("inapp" for one-time purchases
* and "subs" for subscriptions)
* @param developerPayload optional argument to be sent back with the purchase information
* @return Bundle containing the following key-value pairs
* "RESPONSE_CODE" with int value, RESULT_OK(0) if success, appropriate response codes
* on failures.
* "BUY_INTENT" - PendingIntent to start the purchase flow
*
* The Pending intent should be launched with startIntentSenderForResult. When purchase flow
* has completed, the onActivityResult() will give a resultCode of OK or CANCELED.
* If the purchase is successful, the result data will contain the following key-value pairs
* "RESPONSE_CODE" with int value, RESULT_OK(0) if success, appropriate response
* codes on failures.
* "INAPP_PURCHASE_DATA" - String in JSON format similar to
* '{"orderId":"12999763169054705758.1371079406387615",
* "packageName":"com.example.app",
* "productId":"exampleSku",
* "purchaseTime":1345678900000,
* "purchaseToken" : "122333444455555",
* "developerPayload":"example developer payload" }'
* "INAPP_DATA_SIGNATURE" - String containing the signature of the purchase data that
* was signed with the private key of the developer
*/
public android.os.Bundle getBuyIntent(int apiVersion, java.lang.String packageName, java.lang.String sku, java.lang.String type, java.lang.String developerPayload) throws android.os.RemoteException;
/**
* Returns the current SKUs owned by the user of the type and package name specified along with
* purchase information and a signature of the data to be validated.
* This will return all SKUs that have been purchased in V3 and managed items purchased using
* V1 and V2 that have not been consumed.
* @param apiVersion billing API version that the app is using
* @param packageName package name of the calling app
* @param type of the in-app items being requested ("inapp" for one-time purchases
* and "subs" for subscriptions)
* @param continuationToken to be set as null for the first call, if the number of owned
* skus are too many, a continuationToken is returned in the response bundle.
* This method can be called again with the continuation token to get the next set of
* owned skus.
* @return Bundle containing the following key-value pairs
* "RESPONSE_CODE" with int value, RESULT_OK(0) if success, appropriate response codes
on failures.
* "INAPP_PURCHASE_ITEM_LIST" - StringArrayList containing the list of SKUs
* "INAPP_PURCHASE_DATA_LIST" - StringArrayList containing the purchase information
* "INAPP_DATA_SIGNATURE_LIST"- StringArrayList containing the signatures
* of the purchase information
* "INAPP_CONTINUATION_TOKEN" - String containing a continuation token for the
* next set of in-app purchases. Only set if the
* user has more owned skus than the current list.
*/
public android.os.Bundle getPurchases(int apiVersion, java.lang.String packageName, java.lang.String type, java.lang.String continuationToken) throws android.os.RemoteException;
public int consumePurchase(int apiVersion, java.lang.String packageName, java.lang.String purchaseToken) throws android.os.RemoteException;
public int stub(int apiVersion, java.lang.String packageName, java.lang.String type) throws android.os.RemoteException;
/**
* Returns a pending intent to launch the purchase flow for upgrading or downgrading a
* subscription. The existing owned SKU(s) should be provided along with the new SKU that
* the user is upgrading or downgrading to.
* @param apiVersion billing API version that the app is using, must be 5 or later
* @param packageName package name of the calling app
* @param oldSkus the SKU(s) that the user is upgrading or downgrading from,
* if null or empty this method will behave like {@link #getBuyIntent}
* @param newSku the SKU that the user is upgrading or downgrading to
* @param type of the item being purchased, currently must be "subs"
* @param developerPayload optional argument to be sent back with the purchase information
* @return Bundle containing the following key-value pairs
* "RESPONSE_CODE" with int value, RESULT_OK(0) if success, appropriate response codes
* on failures.
* "BUY_INTENT" - PendingIntent to start the purchase flow
*
* The Pending intent should be launched with startIntentSenderForResult. When purchase flow
* has completed, the onActivityResult() will give a resultCode of OK or CANCELED.
* If the purchase is successful, the result data will contain the following key-value pairs
* "RESPONSE_CODE" with int value, RESULT_OK(0) if success, appropriate response
* codes on failures.
* "INAPP_PURCHASE_DATA" - String in JSON format similar to
* '{"orderId":"12999763169054705758.1371079406387615",
* "packageName":"com.example.app",
* "productId":"exampleSku",
* "purchaseTime":1345678900000,
* "purchaseToken" : "122333444455555",
* "developerPayload":"example developer payload" }'
* "INAPP_DATA_SIGNATURE" - String containing the signature of the purchase data that
* was signed with the private key of the developer
*/
public android.os.Bundle getBuyIntentToReplaceSkus(int apiVersion, java.lang.String packageName, java.util.List<java.lang.String> oldSkus, java.lang.String newSku, java.lang.String type, java.lang.String developerPayload) throws android.os.RemoteException;
/**
* Returns a pending intent to launch the purchase flow for an in-app item. This method is
* a variant of the {@link #getBuyIntent} method and takes an additional {@code extraParams}
* parameter. This parameter is a Bundle of optional keys and values that affect the
* operation of the method.
* @param apiVersion billing API version that the app is using, must be 6 or later
* @param packageName package name of the calling app
* @param sku the SKU of the in-app item as published in the developer console
* @param type of the in-app item being purchased ("inapp" for one-time purchases
* and "subs" for subscriptions)
* @param developerPayload optional argument to be sent back with the purchase information
* @extraParams a Bundle with the following optional keys:
* "skusToReplace" - List<String> - an optional list of SKUs that the user is
* upgrading or downgrading from.
* Pass this field if the purchase is upgrading or downgrading
* existing subscriptions.
* The specified SKUs are replaced with the SKUs that the user is
* purchasing. Google Play replaces the specified SKUs at the start of
* the next billing cycle.
* "replaceSkusProration" - Boolean - whether the user should be credited for any unused
* subscription time on the SKUs they are upgrading or downgrading.
* If you set this field to true, Google Play swaps out the old SKUs
* and credits the user with the unused value of their subscription
* time on a pro-rated basis.
* Google Play applies this credit to the new subscription, and does
* not begin billing the user for the new subscription until after
* the credit is used up.
* If you set this field to false, the user does not receive credit for
* any unused subscription time and the recurrence date does not
* change.
* Default value is true. Ignored if you do not pass skusToReplace.
* "accountId" - String - an optional obfuscated string that is uniquely
* associated with the user's account in your app.
* If you pass this value, Google Play can use it to detect irregular
* activity, such as many devices making purchases on the same
* account in a short period of time.
* Do not use the developer ID or the user's Google ID for this field.
* In addition, this field should not contain the user's ID in
* cleartext.
* We recommend that you use a one-way hash to generate a string from
* the user's ID, and store the hashed string in this field.
* "vr" - Boolean - an optional flag indicating whether the returned intent
* should start a VR purchase flow. The apiVersion must also be 7 or
* later to use this flag.
*/
public android.os.Bundle getBuyIntentExtraParams(int apiVersion, java.lang.String packageName, java.lang.String sku, java.lang.String type, java.lang.String developerPayload, android.os.Bundle extraParams) throws android.os.RemoteException;
/**
* Returns the most recent purchase made by the user for each SKU, even if that purchase is
* expired, canceled, or consumed.
* @param apiVersion billing API version that the app is using, must be 6 or later
* @param packageName package name of the calling app
* @param type of the in-app items being requested ("inapp" for one-time purchases
* and "subs" for subscriptions)
* @param continuationToken to be set as null for the first call, if the number of owned
* skus is too large, a continuationToken is returned in the response bundle.
* This method can be called again with the continuation token to get the next set of
* owned skus.
* @param extraParams a Bundle with extra params that would be appended into http request
* query string. Not used at this moment. Reserved for future functionality.
* @return Bundle containing the following key-value pairs
* "RESPONSE_CODE" with int value: RESULT_OK(0) if success,
* {@link IabHelper#BILLING_RESPONSE_RESULT_*} response codes on failures.
*
* "INAPP_PURCHASE_ITEM_LIST" - ArrayList<String> containing the list of SKUs
* "INAPP_PURCHASE_DATA_LIST" - ArrayList<String> containing the purchase information
* "INAPP_DATA_SIGNATURE_LIST"- ArrayList<String> containing the signatures
* of the purchase information
* "INAPP_CONTINUATION_TOKEN" - String containing a continuation token for the
* next set of in-app purchases. Only set if the
* user has more owned skus than the current list.
*/
public android.os.Bundle getPurchaseHistory(int apiVersion, java.lang.String packageName, java.lang.String type, java.lang.String continuationToken, android.os.Bundle extraParams) throws android.os.RemoteException;
public int isBillingSupportedExtraParams(int apiVersion, java.lang.String packageName, java.lang.String type, android.os.Bundle extraParams) throws android.os.RemoteException;
}

File diff suppressed because it is too large Load Diff

View File

@ -1,16 +0,0 @@
package com.juce;
import com.google.firebase.iid.*;
public final class JuceFirebaseInstanceIdService extends FirebaseInstanceIdService
{
private native void firebaseInstanceIdTokenRefreshed (String token);
@Override
public void onTokenRefresh()
{
String token = FirebaseInstanceId.getInstance().getToken();
firebaseInstanceIdTokenRefreshed (token);
}
}

View File

@ -1,35 +0,0 @@
package com.juce;
import com.google.firebase.messaging.*;
public final class JuceFirebaseMessagingService extends FirebaseMessagingService
{
private native void firebaseRemoteMessageReceived (RemoteMessage message);
private native void firebaseRemoteMessagesDeleted();
private native void firebaseRemoteMessageSent (String messageId);
private native void firebaseRemoteMessageSendError (String messageId, String error);
@Override
public void onMessageReceived (RemoteMessage message)
{
firebaseRemoteMessageReceived (message);
}
@Override
public void onDeletedMessages()
{
firebaseRemoteMessagesDeleted();
}
@Override
public void onMessageSent (String messageId)
{
firebaseRemoteMessageSent (messageId);
}
@Override
public void onSendError (String messageId, Exception e)
{
firebaseRemoteMessageSendError (messageId, e.toString());
}
}

View File

@ -0,0 +1,42 @@
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.roli.juce.HelloWorld.java
should be in the module's native/javacore/app/com/roli/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 AS 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, JuceApp.class and JuceSharingContentProvider.class which you will
probably want to remove).
6. Inside of app/build/intermediates/classes/release_/release 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,58 @@
package com.roli.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,407 @@
package com.roli.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,15 @@
package com.roli.juce;
import com.roli.juce.Java;
import android.app.Application;
public class JuceApp extends Application
{
@Override
public void onCreate ()
{
super.onCreate ();
Java.initialiseJUCE (this);
}
}

View File

@ -0,0 +1,13 @@
package com.roli.juce;
import android.content.Context;
public class Java
{
static
{
System.loadLibrary ("juce_jni");
}
public native static void initialiseJUCE (Context appContext);
}

View File

@ -23,47 +23,97 @@
namespace juce
{
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
METHOD (constructor, "<init>", "(Landroid/content/Context;Landroid/media/MediaScannerConnection$MediaScannerConnectionClient;)V") \
METHOD (connect, "connect", "()V") \
METHOD (disconnect, "disconnect", "()V") \
METHOD (scanFile, "scanFile", "(Ljava/lang/String;Ljava/lang/String;)V") \
DECLARE_JNI_CLASS (MediaScannerConnection, "android/media/MediaScannerConnection");
DECLARE_JNI_CLASS (MediaScannerConnection, "android/media/MediaScannerConnection")
#undef JNI_CLASS_MEMBERS
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
METHOD (query, "query", "(Landroid/net/Uri;[Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;)Landroid/database/Cursor;") \
METHOD (openInputStream, "openInputStream", "(Landroid/net/Uri;)Ljava/io/InputStream;") \
METHOD (openOutputStream, "openOutputStream", "(Landroid/net/Uri;)Ljava/io/OutputStream;")
DECLARE_JNI_CLASS (ContentResolver, "android/content/ContentResolver");
DECLARE_JNI_CLASS (ContentResolver, "android/content/ContentResolver")
#undef JNI_CLASS_MEMBERS
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
METHOD (moveToFirst, "moveToFirst", "()Z") \
METHOD (getColumnIndex, "getColumnIndex", "(Ljava/lang/String;)I") \
METHOD (getString, "getString", "(I)Ljava/lang/String;") \
METHOD (close, "close", "()V") \
DECLARE_JNI_CLASS (AndroidCursor, "android/database/Cursor");
DECLARE_JNI_CLASS (AndroidCursor, "android/database/Cursor")
#undef JNI_CLASS_MEMBERS
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
STATICMETHOD (getExternalStorageDirectory, "getExternalStorageDirectory", "()Ljava/io/File;") \
STATICMETHOD (getExternalStoragePublicDirectory, "getExternalStoragePublicDirectory", "(Ljava/lang/String;)Ljava/io/File;") \
STATICMETHOD (getDataDirectory, "getDataDirectory", "()Ljava/io/File;")
DECLARE_JNI_CLASS (AndroidEnvironment, "android/os/Environment");
DECLARE_JNI_CLASS (AndroidEnvironment, "android/os/Environment")
#undef JNI_CLASS_MEMBERS
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
METHOD (close, "close", "()V") \
METHOD (flush, "flush", "()V") \
METHOD (write, "write", "([BII)V")
DECLARE_JNI_CLASS (AndroidOutputStream, "java/io/OutputStream");
DECLARE_JNI_CLASS (AndroidOutputStream, "java/io/OutputStream")
#undef JNI_CLASS_MEMBERS
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
FIELD (publicSourceDir, "publicSourceDir", "Ljava/lang/String;") \
FIELD (dataDir, "dataDir", "Ljava/lang/String;")
DECLARE_JNI_CLASS (AndroidApplicationInfo, "android/content/pm/ApplicationInfo")
#undef JNI_CLASS_MEMBERS
//==============================================================================
static File juceFile (LocalRef<jobject> obj)
{
auto* env = getEnv();
if (env->IsInstanceOf (obj.get(), JavaFile) != 0)
return File (juceString (LocalRef<jstring> ((jstring) env->CallObjectMethod (obj.get(),
JavaFile.getAbsolutePath))));
return {};
}
static File getWellKnownFolder (const char* folderId)
{
auto* env = getEnv();
auto fieldId = env->GetStaticFieldID (AndroidEnvironment, folderId, "Ljava/lang/String;");
if (fieldId == 0)
{
// unknown field in environment
jassertfalse;
return {};
}
LocalRef<jobject> fieldValue (env->GetStaticObjectField (AndroidEnvironment, fieldId));
if (fieldValue == nullptr)
return {};
LocalRef<jobject> downloadFolder (env->CallStaticObjectMethod (AndroidEnvironment,
AndroidEnvironment.getExternalStoragePublicDirectory,
fieldValue.get()));
return (downloadFolder ? juceFile (downloadFolder) : File());
}
static LocalRef<jobject> urlToUri (const URL& url)
{
return LocalRef<jobject> (getEnv()->CallStaticObjectMethod (AndroidUri, AndroidUri.parse,
javaString (url.toString (true)).get()));
}
//==============================================================================
struct AndroidContentUriResolver
{
@ -74,7 +124,7 @@ public:
jassert (url.getScheme() == "content");
auto* env = getEnv();
LocalRef<jobject> contentResolver (android.activity.callObjectMethod (JuceAppActivity.getContentResolver));
LocalRef<jobject> contentResolver (env->CallObjectMethod (getAppContext().get(), AndroidContext.getContentResolver));
if (contentResolver)
return LocalRef<jobject> ((env->CallObjectMethod (contentResolver.get(),
@ -116,7 +166,7 @@ public:
else if (type.equalsIgnoreCase ("downloads"))
{
auto subDownloadPath = url.getSubPath().fromFirstOccurrenceOf ("tree/downloads", false, false);
return File (getWellKnownFolder ("Download").getFullPathName() + "/" + subDownloadPath);
return File (getWellKnownFolder ("DIRECTORY_DOWNLOADS").getFullPathName() + "/" + subDownloadPath);
}
else
{
@ -142,7 +192,7 @@ public:
{
auto uri = urlToUri (url);
auto* env = getEnv();
LocalRef<jobject> contentResolver (android.activity.callObjectMethod (JuceAppActivity.getContentResolver));
LocalRef<jobject> contentResolver (env->CallObjectMethod (getAppContext().get(), AndroidContext.getContentResolver));
if (contentResolver == 0)
return {};
@ -166,7 +216,7 @@ private:
{
auto uri = urlToUri (url);
auto* env = getEnv();
LocalRef<jobject> contentResolver (android.activity.callObjectMethod (JuceAppActivity.getContentResolver));
LocalRef<jobject> contentResolver (env->CallObjectMethod (getAppContext().get(), AndroidContext.getContentResolver));
if (contentResolver)
{
@ -188,6 +238,13 @@ private:
uri.get(), projection.get(), jSelection.get(),
args.get(), nullptr));
if (jniCheckHasExceptionOccurredAndClear())
{
// An exception has occurred, have you acquired RuntimePermission::readExternalStorage permission?
jassertfalse;
return {};
}
if (cursor)
{
if (env->CallBooleanMethod (cursor.get(), AndroidCursor.moveToFirst) != 0)
@ -210,17 +267,6 @@ private:
return {};
}
//==============================================================================
static File getWellKnownFolder (const String& folderId)
{
auto* env = getEnv();
LocalRef<jobject> downloadFolder (env->CallStaticObjectMethod (AndroidEnvironment,
AndroidEnvironment.getExternalStoragePublicDirectory,
javaString (folderId).get()));
return (downloadFolder ? juceFile (downloadFolder) : File());
}
//==============================================================================
static File getStorageDevicePath (const String& storageId)
{
@ -247,15 +293,15 @@ private:
{
Array<File> results;
if (getSDKVersion() >= 19)
if (getAndroidSDKVersion() >= 19)
{
auto* env = getEnv();
static jmethodID m = (env->GetMethodID (JuceAppActivity, "getExternalFilesDirs",
static jmethodID m = (env->GetMethodID (AndroidContext, "getExternalFilesDirs",
"(Ljava/lang/String;)[Ljava/io/File;"));
if (m == 0)
return {};
auto paths = convertFileArray (LocalRef<jobject> (android.activity.callObjectMethod (m, nullptr)));
auto paths = convertFileArray (LocalRef<jobject> (env->CallObjectMethod (getAppContext().get(), m, nullptr)));
for (auto path : paths)
results.add (getMountPointForFile (path));
@ -341,33 +387,6 @@ private:
return files;
}
static File juceFile (LocalRef<jobject> obj)
{
auto* env = getEnv();
if (env->IsInstanceOf (obj.get(), JavaFile) != 0)
return File (juceString (LocalRef<jstring> ((jstring) env->CallObjectMethod (obj.get(),
JavaFile.getAbsolutePath))));
return {};
}
//==============================================================================
static int getSDKVersion()
{
static int sdkVersion
= getEnv()->CallStaticIntMethod (JuceAppActivity,
JuceAppActivity.getAndroidSDKVersion);
return sdkVersion;
}
static LocalRef<jobject> urlToUri (const URL& url)
{
return LocalRef<jobject> (getEnv()->CallStaticObjectMethod (AndroidUri, AndroidUri.parse,
javaString (url.toString (true)).get()));
}
//==============================================================================
static String getStringUsingDataColumn (const String& columnNameToUse, JNIEnv* env,
const LocalRef<jobject>& uri,
@ -380,6 +399,13 @@ private:
uri.get(), projection.get(), nullptr,
nullptr, nullptr));
if (jniCheckHasExceptionOccurredAndClear())
{
// An exception has occurred, have you acquired RuntimePermission::readExternalStorage permission?
jassertfalse;
return {};
}
if (cursor == 0)
return {};
@ -458,7 +484,7 @@ OutputStream* juce_CreateContentURIOutputStream (const URL& url)
{
auto stream = AndroidContentUriResolver::getStreamForContentUri (url, false);
return (stream.get() != 0 ? new AndroidContentUriOutputStream (static_cast<LocalRef<jobject>&&> (stream)) : nullptr);
return (stream.get() != 0 ? new AndroidContentUriOutputStream (std::move (stream)) : nullptr);
}
//==============================================================================
@ -511,9 +537,24 @@ String File::getVersion() const
return {};
}
static File getSpecialFile (jmethodID type)
static File getDocumentsDirectory()
{
return File (juceString (LocalRef<jstring> ((jstring) getEnv()->CallStaticObjectMethod (JuceAppActivity, type))));
auto* env = getEnv();
if (getAndroidSDKVersion() >= 19)
return getWellKnownFolder ("DIRECTORY_DOCUMENTS");
return juceFile (LocalRef<jobject> (env->CallStaticObjectMethod (AndroidEnvironment, AndroidEnvironment.getDataDirectory)));
}
static File getAppDataDir (bool dataDir)
{
auto* env = getEnv();
LocalRef<jobject> applicationInfo (env->CallObjectMethod (getAppContext().get(), AndroidContext.getApplicationInfo));
LocalRef<jobject> jString (env->GetObjectField (applicationInfo.get(), dataDir ? AndroidApplicationInfo.dataDir : AndroidApplicationInfo.publicSourceDir));
return {juceString ((jstring) jString.get())};
}
File File::getSpecialLocation (const SpecialLocationType type)
@ -524,20 +565,41 @@ File File::getSpecialLocation (const SpecialLocationType type)
case userApplicationDataDirectory:
case userDesktopDirectory:
case commonApplicationDataDirectory:
return File (android.appDataDir);
{
static File appDataDir = getAppDataDir (true);
return appDataDir;
}
case userDocumentsDirectory:
case commonDocumentsDirectory: return getSpecialFile (JuceAppActivity.getDocumentsFolder);
case userPicturesDirectory: return getSpecialFile (JuceAppActivity.getPicturesFolder);
case userMusicDirectory: return getSpecialFile (JuceAppActivity.getMusicFolder);
case userMoviesDirectory: return getSpecialFile (JuceAppActivity.getMoviesFolder);
case commonDocumentsDirectory:
{
static auto docsDir = getDocumentsDirectory();
return docsDir;
}
case userPicturesDirectory:
{
static auto picturesDir = getWellKnownFolder ("DIRECTORY_PICTURES");
return picturesDir;
}
case userMusicDirectory:
{
static auto musicDir = getWellKnownFolder ("DIRECTORY_MUSIC");
return musicDir;
}
case userMoviesDirectory:
{
static auto moviesDir = getWellKnownFolder ("DIRECTORY_MOVIES");
return moviesDir;
}
case globalApplicationsDirectory:
return File ("/system/app");
case tempDirectory:
{
File tmp = File (android.appDataDir).getChildFile (".temp");
File tmp = getSpecialLocation (commonApplicationDataDirectory).getChildFile (".temp");
tmp.createDirectory();
return File (tmp.getFullPathName());
}
@ -546,7 +608,7 @@ File File::getSpecialLocation (const SpecialLocationType type)
case currentExecutableFile:
case currentApplicationFile:
case hostApplicationPath:
return juce_getExecutableFile();
return getAppDataDir (false);
default:
jassertfalse; // unknown type?
@ -567,8 +629,13 @@ bool File::moveToTrash() const
JUCE_API bool JUCE_CALLTYPE Process::openDocument (const String& fileName, const String&)
{
const LocalRef<jstring> t (javaString (fileName));
android.activity.callVoidMethod (JuceAppActivity.launchURL, t.get());
URL targetURL (fileName);
auto* env = getEnv();
const LocalRef<jstring> action (javaString ("android.intent.action.VIEW"));
LocalRef<jobject> intent (env->NewObject (AndroidIntent, AndroidIntent.constructWithUri, action.get(), urlToUri (targetURL).get()));
env->CallVoidMethod (getCurrentActivity(), AndroidContext.startActivity, intent.get());
return true;
}
@ -581,10 +648,10 @@ class SingleMediaScanner : public MediaScannerConnectionClient
{
public:
SingleMediaScanner (const String& filename)
: msc (getEnv()->NewObject (MediaScannerConnection,
MediaScannerConnection.constructor,
android.activity.get(),
CreateJavaInterface (this, "android/media/MediaScannerConnection$MediaScannerConnectionClient").get())),
: msc (LocalRef<jobject> (getEnv()->NewObject (MediaScannerConnection,
MediaScannerConnection.constructor,
getAppContext().get(),
CreateJavaInterface (this, "android/media/MediaScannerConnection$MediaScannerConnectionClient").get()))),
file (filename)
{
getEnv()->CallVoidMethod (msc.get(), MediaScannerConnection.connect);

View File

@ -0,0 +1,687 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
static const uint8 invocationHandleByteCode[] = {
31,139,8,8,170,94,229,91,0,3,105,110,118,111,99,97,116,105,111,110,95,104,97,110,100,108,101,114,46,100,101,120,0,109,
148,75,107,19,81,20,199,207,189,119,38,169,181,109,98,218,138,72,23,81,20,68,193,169,218,130,144,42,130,162,53,140,15,
104,200,66,187,232,52,185,181,147,78,103,98,58,141,33,184,168,197,47,224,66,168,187,186,241,83,168,184,16,247,162,31,193,
69,151,174,220,40,234,255,62,154,4,237,192,239,62,206,57,115,30,115,239,153,186,236,12,79,95,154,165,159,187,91,95,227,
217,197,247,87,42,31,231,191,156,157,58,241,253,199,220,201,79,75,175,118,58,14,81,147,136,58,213,153,2,217,231,131,32,
154,32,35,63,164,246,0,102,244,13,48,48,129,33,139,121,138,153,125,5,131,131,119,82,204,203,156,168,1,214,65,10,186,224,
53,120,7,62,131,61,144,131,237,57,112,30,92,4,183,192,93,176,4,154,160,11,182,193,11,97,252,171,216,46,200,144,137,59,
100,243,26,6,35,0,46,9,166,116,131,155,89,113,159,27,125,214,214,116,216,174,23,185,241,89,208,181,8,173,99,240,48,106,
247,99,182,198,156,149,231,245,204,232,136,246,203,173,189,65,189,61,7,209,31,60,167,48,239,26,119,90,183,35,84,190,66,
175,201,230,218,204,43,201,81,172,30,76,131,25,66,180,190,133,169,81,107,139,164,243,112,123,25,154,253,194,53,232,17,
231,2,74,190,140,106,212,190,57,205,201,97,99,168,207,209,223,71,61,147,202,182,57,104,59,74,11,45,212,255,56,251,60,251,
50,251,166,157,81,81,71,80,83,129,206,252,238,133,239,101,162,242,72,237,217,186,246,251,171,106,51,248,242,162,183,218,
183,207,204,133,113,152,94,37,86,38,215,47,251,190,79,142,175,198,211,126,45,89,247,90,73,20,122,141,205,154,244,202,24,
110,199,237,164,22,164,97,18,207,7,113,61,146,173,18,29,247,235,65,212,14,215,188,32,142,147,84,235,188,202,106,43,121,
178,81,162,130,223,8,218,129,23,5,241,35,239,222,114,67,214,210,18,77,14,200,180,93,176,28,201,18,162,245,197,45,185,18,
193,214,59,48,218,255,102,119,100,186,154,212,75,196,170,196,171,101,26,127,120,64,84,183,22,201,160,69,249,122,184,209,
12,210,218,234,205,48,14,162,176,43,105,108,95,162,130,173,73,26,90,217,215,100,66,35,25,141,145,66,91,94,79,226,84,118,
82,114,219,65,180,41,137,115,54,62,197,142,57,196,132,186,86,207,182,156,95,156,111,115,98,10,182,43,4,123,43,24,219,19,
185,127,206,70,247,159,237,77,62,208,159,98,160,71,157,129,62,117,169,223,171,25,234,247,171,200,155,181,62,231,162,121,
231,169,178,41,26,185,186,207,44,111,228,234,142,243,162,137,171,250,219,41,246,239,56,217,181,190,251,214,167,250,127,
252,5,76,239,47,69,120,4,0,0};
//==============================================================================
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
STATICMETHOD (newProxyInstance, "newProxyInstance", "(Ljava/lang/ClassLoader;[Ljava/lang/Class;Ljava/lang/reflect/InvocationHandler;)Ljava/lang/Object;") \
DECLARE_JNI_CLASS (JavaProxy, "java/lang/reflect/Proxy")
#undef JNI_CLASS_MEMBERS
//==============================================================================
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
METHOD (constructor, "<init>", "(J)V") \
METHOD (clear, "clear", "()V") \
CALLBACK (juce_invokeImplementer, "dispatchInvoke", "(JLjava/lang/Object;Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;") \
CALLBACK (juce_dispatchDelete, "dispatchFinalize", "(J)V")
DECLARE_JNI_CLASS_WITH_BYTECODE (JuceInvocationHandler, "com/roli/juce/JuceInvocationHandler", 10, invocationHandleByteCode, sizeof (invocationHandleByteCode))
#undef JNI_CLASS_MEMBERS
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
METHOD (findClass, "findClass", "(Ljava/lang/String;)Ljava/lang/Class;") \
STATICMETHOD (getSystemClassLoader, "getSystemClassLoader", "()Ljava/lang/ClassLoader;")
DECLARE_JNI_CLASS (JavaClassLoader, "java/lang/ClassLoader")
#undef JNI_CLASS_MEMBERS
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
METHOD (constructor, "<init>", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/ClassLoader;)V")
DECLARE_JNI_CLASS (AndroidDexClassLoader, "dalvik/system/DexClassLoader")
#undef JNI_CLASS_MEMBERS
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
METHOD (constructor, "<init>", "(Ljava/nio/ByteBuffer;Ljava/lang/ClassLoader;)V")
DECLARE_JNI_CLASS_WITH_MIN_SDK (AndroidInMemoryDexClassLoader, "dalvik/system/InMemoryDexClassLoader", 26)
#undef JNI_CLASS_MEMBERS
//==============================================================================
struct SystemJavaClassComparator
{
static int compareElements (JNIClassBase* first, JNIClassBase* second)
{
auto isSysClassA = isSystemClass (first);
auto isSysClassB = isSystemClass (second);
if ((! isSysClassA) && (! isSysClassB))
{
return DefaultElementComparator<bool>::compareElements (first != nullptr ? first->byteCode != nullptr : false,
second != nullptr ? second->byteCode != nullptr : false);
}
return DefaultElementComparator<bool>::compareElements (isSystemClass (first),
isSystemClass (second));
}
static bool isSystemClass (JNIClassBase* cls)
{
if (cls == nullptr)
return false;
String path (cls->getClassPath());
return path.startsWith ("java/")
|| path.startsWith ("android/")
|| path.startsWith ("dalvik/");
}
};
//==============================================================================
JNIClassBase::JNIClassBase (const char* cp, int classMinSDK, const void* bc, size_t n)
: classPath (cp), byteCode (bc), byteCodeSize (n), minSDK (classMinSDK), classRef (0)
{
SystemJavaClassComparator comparator;
getClasses().addSorted (comparator, this);
}
JNIClassBase::~JNIClassBase()
{
getClasses().removeFirstMatchingValue (this);
}
Array<JNIClassBase*>& JNIClassBase::getClasses()
{
static Array<JNIClassBase*> classes;
return classes;
}
// Get code cache directory without yet having a context object
static File getCodeCacheDirectory()
{
int pid = getpid();
File cmdline("/proc/" + String(pid) + "/cmdline");
auto bundleId = cmdline.loadFileAsString().trimStart().trimEnd();
if (bundleId.isEmpty())
return {};
return File("/data/data/" + bundleId + "/code_cache");
}
void JNIClassBase::initialise (JNIEnv* env)
{
auto sdkVersion = getAndroidSDKVersion();
if (sdkVersion >= minSDK)
{
LocalRef<jstring> classNameAndPackage (javaString (String (classPath).replaceCharacter (L'/', L'.')));
static Array<GlobalRef> byteCodeLoaders;
if (! SystemJavaClassComparator::isSystemClass(this))
{
LocalRef<jobject> defaultClassLoader (env->CallStaticObjectMethod (JavaClassLoader, JavaClassLoader.getSystemClassLoader));
tryLoadingClassWithClassLoader (env, defaultClassLoader.get());
if (classRef == 0)
{
for (auto& byteCodeLoader : byteCodeLoaders)
{
tryLoadingClassWithClassLoader (env, byteCodeLoader.get());
if (classRef != 0)
break;
}
// fallback by trying to load the class from bytecode
if (byteCode != nullptr)
{
LocalRef<jobject> byteCodeClassLoader;
MemoryOutputStream uncompressedByteCode;
{
MemoryInputStream rawGZipData (byteCode, byteCodeSize, false);
GZIPDecompressorInputStream gzipStream (&rawGZipData, false, GZIPDecompressorInputStream::gzipFormat);
uncompressedByteCode.writeFromInputStream (gzipStream, -1);
}
if (sdkVersion >= 26)
{
LocalRef<jbyteArray> byteArray (env->NewByteArray ((jsize) uncompressedByteCode.getDataSize()));
jboolean isCopy;
auto* dst = env->GetByteArrayElements (byteArray.get(), &isCopy);
memcpy (dst, uncompressedByteCode.getData(), uncompressedByteCode.getDataSize());
env->ReleaseByteArrayElements (byteArray.get(), dst, 0);
LocalRef<jobject> byteBuffer (env->CallStaticObjectMethod (JavaByteBuffer, JavaByteBuffer.wrap, byteArray.get()));
byteCodeClassLoader = LocalRef<jobject> (env->NewObject (AndroidInMemoryDexClassLoader,
AndroidInMemoryDexClassLoader.constructor,
byteBuffer.get(), defaultClassLoader.get()));
}
else if (uncompressedByteCode.getDataSize() >= 32)
{
auto codeCacheDir = getCodeCacheDirectory();
// The dex file has an embedded 20-byte long SHA-1 signature at offset 12
auto fileName = String::toHexString ((char*)uncompressedByteCode.getData() + 12, 20, 0) + ".dex";
auto dexFile = codeCacheDir.getChildFile (fileName);
auto optimizedDirectory = codeCacheDir.getChildFile ("optimized_cache");
optimizedDirectory.createDirectory();
if (dexFile.replaceWithData (uncompressedByteCode.getData(), uncompressedByteCode.getDataSize()))
{
byteCodeClassLoader = LocalRef<jobject> (env->NewObject (AndroidDexClassLoader,
AndroidDexClassLoader.constructor,
javaString (dexFile.getFullPathName()).get(),
javaString (optimizedDirectory.getFullPathName()).get(),
nullptr,
defaultClassLoader.get()));
}
else
{
// can't write to cache folder
jassertfalse;
}
}
if (byteCodeClassLoader != nullptr)
{
tryLoadingClassWithClassLoader (env, byteCodeClassLoader.get());
byteCodeLoaders.add (GlobalRef(byteCodeClassLoader));
}
}
}
}
if (classRef == 0)
classRef = (jclass) env->NewGlobalRef (LocalRef<jobject> (env->FindClass (classPath)));
jassert (classRef != 0);
initialiseFields (env);
}
}
void JNIClassBase::tryLoadingClassWithClassLoader (JNIEnv* env, jobject classLoader)
{
LocalRef<jstring> classNameAndPackage (javaString (String (classPath).replaceCharacter (L'/', L'.')));
// Android SDK <= 19 has a bug where the class loader might throw an exception but still return
// a non-nullptr. So don't assign the result of this call to a jobject just yet...
auto classObj = env->CallObjectMethod (classLoader, JavaClassLoader.findClass, classNameAndPackage.get());
if (jthrowable exception = env->ExceptionOccurred ())
{
env->ExceptionClear();
classObj = 0;
}
// later versions of Android don't throw at all, so re-check the object
if (classObj != nullptr)
classRef = (jclass) env->NewGlobalRef (LocalRef<jobject> (classObj));
}
void JNIClassBase::release (JNIEnv* env)
{
if (classRef != 0)
env->DeleteGlobalRef (classRef);
}
void JNIClassBase::initialiseAllClasses (JNIEnv* env)
{
const Array<JNIClassBase*>& classes = getClasses();
for (int i = classes.size(); --i >= 0;)
classes.getUnchecked(i)->initialise (env);
}
void JNIClassBase::releaseAllClasses (JNIEnv* env)
{
const Array<JNIClassBase*>& classes = getClasses();
for (int i = classes.size(); --i >= 0;)
classes.getUnchecked(i)->release (env);
}
jmethodID JNIClassBase::resolveMethod (JNIEnv* env, const char* methodName, const char* params)
{
jmethodID m = env->GetMethodID (classRef, methodName, params);
jassert (m != 0);
return m;
}
jmethodID JNIClassBase::resolveStaticMethod (JNIEnv* env, const char* methodName, const char* params)
{
jmethodID m = env->GetStaticMethodID (classRef, methodName, params);
jassert (m != 0);
return m;
}
jfieldID JNIClassBase::resolveField (JNIEnv* env, const char* fieldName, const char* signature)
{
jfieldID f = env->GetFieldID (classRef, fieldName, signature);
jassert (f != 0);
return f;
}
jfieldID JNIClassBase::resolveStaticField (JNIEnv* env, const char* fieldName, const char* signature)
{
jfieldID f = env->GetStaticFieldID (classRef, fieldName, signature);
jassert (f != 0);
return f;
}
void JNIClassBase::resolveCallbacks (JNIEnv* env, const Array<JNINativeMethod>& nativeCallbacks)
{
if (nativeCallbacks.size() > 0)
env->RegisterNatives (classRef, nativeCallbacks.begin(), (jint) nativeCallbacks.size());
}
//==============================================================================
LocalRef<jobject> CreateJavaInterface (AndroidInterfaceImplementer* implementer,
const StringArray& interfaceNames,
LocalRef<jobject> subclass)
{
auto* env = getEnv();
implementer->javaSubClass = GlobalRef (subclass);
// you need to override at least one interface
jassert (interfaceNames.size() > 0);
auto classArray = LocalRef<jobject> (env->NewObjectArray (interfaceNames.size(), JavaClass, nullptr));
LocalRef<jobject> classLoader;
for (auto i = 0; i < interfaceNames.size(); ++i)
{
auto aClass = LocalRef<jobject> (env->FindClass (interfaceNames[i].toRawUTF8()));
if (aClass != nullptr)
{
if (i == 0)
classLoader = LocalRef<jobject> (env->CallObjectMethod (aClass, JavaClass.getClassLoader));
env->SetObjectArrayElement ((jobjectArray) classArray.get(), i, aClass);
}
else
{
// interface class not found
jassertfalse;
}
}
auto invocationHandler = LocalRef<jobject> (env->NewObject (JuceInvocationHandler, JuceInvocationHandler.constructor,
reinterpret_cast<jlong> (implementer)));
// CreateJavaInterface() is expected to be called just once for a given implementer
jassert (implementer->invocationHandler == nullptr);
implementer->invocationHandler = GlobalRef (invocationHandler);
return LocalRef<jobject> (env->CallStaticObjectMethod (JavaProxy, JavaProxy.newProxyInstance,
classLoader.get(), classArray.get(),
invocationHandler.get()));
}
LocalRef<jobject> CreateJavaInterface (AndroidInterfaceImplementer* implementer,
const StringArray& interfaceNames)
{
return CreateJavaInterface (implementer, interfaceNames,
LocalRef<jobject> (getEnv()->NewObject (JavaObject,
JavaObject.constructor)));
}
LocalRef<jobject> CreateJavaInterface (AndroidInterfaceImplementer* implementer,
const String& interfaceName)
{
return CreateJavaInterface (implementer, StringArray (interfaceName));
}
AndroidInterfaceImplementer::~AndroidInterfaceImplementer()
{
clear();
}
void AndroidInterfaceImplementer::clear()
{
if (invocationHandler != nullptr)
getEnv()->CallVoidMethod (invocationHandler,
JuceInvocationHandler.clear);
}
jobject AndroidInterfaceImplementer::invoke (jobject /*proxy*/, jobject method, jobjectArray args)
{
auto* env = getEnv();
return env->CallObjectMethod (method, JavaMethod.invoke, javaSubClass.get(), args);
}
jobject juce_invokeImplementer (JNIEnv*, jobject /*object*/, jlong host, jobject proxy,
jobject method, jobjectArray args)
{
if (auto* myself = reinterpret_cast<AndroidInterfaceImplementer*> (host))
return myself->invoke (proxy, method, args);
return nullptr;
}
void juce_dispatchDelete (JNIEnv*, jobject /*object*/, jlong host)
{
if (auto* myself = reinterpret_cast<AndroidInterfaceImplementer*> (host))
delete myself;
}
//==============================================================================
jobject ActivityLifecycleCallbacks::invoke (jobject proxy, jobject method, jobjectArray args)
{
auto* env = getEnv();
auto methodName = juceString ((jstring) env->CallObjectMethod (method, JavaMethod.getName));
auto activity = env->GetArrayLength (args) > 0 ? env->GetObjectArrayElement (args, 0) : (jobject) nullptr;
auto bundle = env->GetArrayLength (args) > 1 ? env->GetObjectArrayElement (args, 1) : (jobject) nullptr;
if (methodName == "onActivityCreated") { onActivityCreated (activity, bundle); return nullptr; }
else if (methodName == "onActivityDestroyed") { onActivityDestroyed (activity); return nullptr; }
else if (methodName == "onActivityPaused") { onActivityPaused (activity); return nullptr; }
else if (methodName == "onActivityResumed") { onActivityResumed (activity); return nullptr; }
else if (methodName == "onActivitySaveInstanceState") { onActivitySaveInstanceState (activity, bundle); return nullptr; }
else if (methodName == "onActivityStarted") { onActivityStarted (activity); return nullptr; }
else if (methodName == "onActivityStopped") { onActivityStopped (activity); return nullptr; }
return AndroidInterfaceImplementer::invoke (proxy, method, args);
}
//==============================================================================
int getAndroidSDKVersion()
{
// this is used so often that we need to cache this
static int sdkVersion = []
{
// don't use any jni helpers as they might not have been initialised yet
// when this method is used
auto* env = getEnv();
auto buildVersion = env->FindClass ("android/os/Build$VERSION");
jassert (buildVersion != 0);
auto sdkVersionField = env->GetStaticFieldID (buildVersion, "SDK_INT", "I");
jassert (sdkVersionField != 0);
return env->GetStaticIntField (buildVersion, sdkVersionField);
}();
return sdkVersion;
}
bool isPermissionDeclaredInManifest (const String& requestedPermission)
{
auto* env = getEnv();
LocalRef<jobject> pkgManager (env->CallObjectMethod (getAppContext().get(), AndroidContext.getPackageManager));
LocalRef<jobject> pkgName (env->CallObjectMethod (getAppContext().get(), AndroidContext.getPackageName));
LocalRef<jobject> pkgInfo (env->CallObjectMethod (pkgManager.get(), AndroidPackageManager.getPackageInfo,
pkgName.get(), 0x00001000 /* PERMISSIONS */));
LocalRef<jobjectArray> permissions ((jobjectArray) env->GetObjectField (pkgInfo.get(), AndroidPackageInfo.requestedPermissions));
int n = env->GetArrayLength (permissions);
for (int i = 0; i < n; ++i)
{
LocalRef<jstring> jstr ((jstring) env->GetObjectArrayElement (permissions, i));
String permissionId (juceString (jstr));
if (permissionId == requestedPermission)
return true;
}
return false;
}
//==============================================================================
// This byte-code is generated from native/java/com/roli/juce/FragmentOverlay.java with min sdk version 16
// See juce_core/native/java/README.txt on how to generate this byte-code.
static const uint8 javaFragmentOverlay[] =
{31,139,8,8,197,105,229,91,0,3,70,114,97,103,109,101,110,116,79,118,101,114,108,97,121,46,100,101,120,0,133,149,93,136,
28,69,16,199,171,231,243,62,39,235,93,60,206,243,244,214,136,73,4,201,156,104,32,186,107,184,36,34,236,58,248,145,11,251,
112,241,101,216,29,215,137,123,51,155,153,217,35,1,65,61,2,201,131,8,10,126,97,2,17,84,16,204,91,30,228,240,73,130,95,40,
104,124,9,137,47,9,248,230,23,8,65,52,130,255,234,238,201,133,35,226,178,191,169,234,234,238,170,234,154,158,238,78,116,
100,100,254,129,157,116,170,242,197,47,171,219,126,253,250,218,79,107,167,30,234,189,251,229,123,127,239,158,219,187,86,
124,127,193,33,234,19,209,145,214,131,19,164,127,187,96,187,151,148,125,4,108,22,74,214,33,241,167,179,120,84,32,63,213,
237,186,65,244,130,69,244,12,228,121,147,232,34,248,29,252,1,174,130,191,192,63,224,118,140,217,9,154,224,121,240,34,88,
5,39,192,171,224,117,240,14,56,13,62,0,31,129,51,224,51,240,21,56,15,46,128,203,224,55,240,39,112,108,162,105,48,15,30,6,
77,240,44,56,1,94,3,167,193,25,176,6,62,7,223,2,164,73,72,135,176,76,114,193,16,24,214,107,29,5,147,188,102,0,247,114,
125,199,48,216,214,109,210,99,92,173,143,105,253,21,140,25,215,250,219,208,61,173,191,15,125,147,214,63,54,85,221,88,255,
4,250,45,90,63,7,125,66,235,223,200,88,130,166,136,243,52,100,12,3,217,221,169,219,91,116,30,51,196,227,84,63,203,91,181,
156,38,53,255,54,41,77,154,149,210,161,59,164,84,126,108,172,120,78,74,139,170,82,186,116,151,158,191,69,74,155,238,38,
181,102,65,164,163,40,157,127,67,142,146,38,44,108,251,193,86,53,236,87,120,44,103,190,84,229,10,148,253,151,116,127,217,
147,84,28,140,243,80,71,75,190,131,43,182,90,255,34,54,220,20,130,221,15,55,187,208,187,152,161,38,135,197,49,241,134,
251,225,138,51,12,95,30,241,76,94,255,207,152,195,107,74,171,130,14,192,163,11,235,24,205,136,41,74,170,38,170,60,74,75,
11,240,184,112,163,71,87,182,251,11,136,251,180,39,223,163,138,127,245,127,226,187,50,254,184,140,207,181,229,189,195,19,
249,253,165,21,206,231,166,113,230,55,145,37,60,93,55,71,239,57,210,82,233,195,178,46,66,83,238,55,238,45,117,30,97,72,
221,210,99,156,122,156,196,197,110,218,252,88,22,118,151,163,164,120,114,37,202,122,225,209,29,135,194,149,144,68,131,68,
147,140,102,64,34,160,217,32,76,58,89,26,119,252,176,223,247,31,141,195,94,218,45,103,213,104,250,122,111,59,77,10,152,
252,134,20,53,154,188,222,147,230,254,222,65,210,233,69,53,154,11,218,233,178,159,165,189,216,63,52,104,71,254,134,240,
53,154,8,56,3,191,23,38,93,127,177,200,226,164,91,35,209,34,171,213,104,4,252,12,2,50,90,77,178,91,77,54,176,128,197,108,
53,217,12,14,54,104,242,224,77,92,216,237,94,154,71,228,182,251,253,3,207,197,57,89,157,176,8,201,237,196,249,114,156,
231,52,214,141,138,61,89,119,192,169,228,228,162,21,164,73,23,230,44,76,138,253,81,62,232,193,92,73,147,61,237,34,94,137,
139,163,202,68,83,27,45,79,132,104,69,52,148,38,251,178,40,44,34,242,74,77,247,204,164,201,254,232,240,32,202,139,167,
162,140,67,199,105,146,107,111,213,255,238,211,179,221,52,89,44,194,172,160,113,173,104,251,104,127,125,2,141,102,202,
201,190,180,19,209,72,38,231,75,221,206,11,78,201,42,184,0,46,185,158,113,95,141,118,64,62,94,167,237,230,214,109,211,
174,119,252,77,26,19,219,93,175,126,238,248,18,85,205,173,247,204,194,246,22,190,57,215,123,4,22,18,54,62,111,235,229,
151,172,31,45,123,21,39,201,13,216,226,154,101,138,147,182,33,190,3,39,29,72,103,124,195,55,207,178,188,19,120,63,150,
247,130,73,235,119,67,185,103,249,126,224,179,163,188,35,28,90,191,39,68,85,181,249,174,16,21,117,46,240,249,106,84,149,
127,190,63,76,61,134,207,21,62,160,68,121,230,84,148,206,247,211,191,48,134,254,198,216,6,0,0};
//==============================================================================
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
METHOD (construct, "<init>", "()V") \
METHOD (close, "close", "()V") \
CALLBACK (FragmentOverlay::onActivityResultNative, "onActivityResultNative", "(JIILandroid/content/Intent;)V") \
CALLBACK (FragmentOverlay::onCreateNative, "onCreateNative", "(JLandroid/os/Bundle;)V") \
CALLBACK (FragmentOverlay::onStartNative, "onStartNative", "(J)V") \
CALLBACK (FragmentOverlay::onRequestPermissionsResultNative, "onRequestPermissionsResultNative", "(JI[Ljava/lang/String;[I)V")
DECLARE_JNI_CLASS_WITH_BYTECODE (JuceFragmentOverlay, "com/roli/juce/FragmentOverlay", 16, javaFragmentOverlay, sizeof(javaFragmentOverlay))
#undef JNI_CLASS_MEMBERS
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
METHOD (show, "show", "(Landroid/app/FragmentManager;Ljava/lang/String;)V")
DECLARE_JNI_CLASS (AndroidDialogFragment, "android/app/DialogFragment")
#undef JNI_CLASS_MEMBERS
//==============================================================================
FragmentOverlay::FragmentOverlay()
: native (LocalRef<jobject> (getEnv()->NewObject (JuceFragmentOverlay, JuceFragmentOverlay.construct)))
{}
FragmentOverlay::~FragmentOverlay()
{
auto* env = getEnv();
env->CallVoidMethod (native.get(), JuceFragmentOverlay.close);
}
void FragmentOverlay::open()
{
auto* env = getEnv();
LocalRef<jobject> bundle (env->NewObject (AndroidBundle, AndroidBundle.constructor));
env->CallVoidMethod (bundle.get(), AndroidBundle.putLong, javaString ("cppThis").get(), (jlong) this);
env->CallVoidMethod (native.get(), AndroidFragment.setArguments, bundle.get());
LocalRef<jobject> fm (env->CallObjectMethod (getCurrentActivity().get(), AndroidActivity.getFragmentManager));
env->CallVoidMethod (native.get(), AndroidDialogFragment.show, fm.get(), javaString ("FragmentOverlay").get());
}
void FragmentOverlay::onActivityResultNative (JNIEnv* env, jobject, jlong host,
jint requestCode, jint resultCode, jobject data)
{
if (auto* myself = reinterpret_cast<FragmentOverlay*> (host))
myself->onActivityResult (requestCode, resultCode, LocalRef<jobject> (env->NewLocalRef (data)));
}
void FragmentOverlay::onCreateNative (JNIEnv* env, jobject, jlong host, jobject bundle)
{
if (auto* myself = reinterpret_cast<FragmentOverlay*> (host))
myself->onCreated (LocalRef<jobject> (env->NewLocalRef (bundle)));
}
void FragmentOverlay::onStartNative (JNIEnv*, jobject, jlong host)
{
if (auto* myself = reinterpret_cast<FragmentOverlay*> (host))
myself->onStart();
}
void FragmentOverlay::onRequestPermissionsResultNative (JNIEnv* env, jobject, jlong host, jint requestCode,
jobjectArray jPermissions, jintArray jGrantResults)
{
if (auto* myself = reinterpret_cast<FragmentOverlay*> (host))
{
Array<int> grantResults;
int n = (jGrantResults != nullptr ? env->GetArrayLength (jGrantResults) : 0);
if (n > 0)
{
auto* data = env->GetIntArrayElements (jGrantResults, 0);
for (int i = 0; i < n; ++i)
grantResults.add (data[i]);
env->ReleaseIntArrayElements (jGrantResults, data, 0);
}
myself->onRequestPermissionsResult (requestCode,
javaStringArrayToJuce (LocalRef<jobjectArray> (jPermissions)),
grantResults);
}
}
jobject FragmentOverlay::getNativeHandle()
{
return native.get();
}
//==============================================================================
class ActivityLauncher : public FragmentOverlay
{
public:
ActivityLauncher (const LocalRef<jobject>& intentToUse,
int requestCodeToUse,
std::function<void (int, int, LocalRef<jobject>)> && callbackToUse)
: intent (intentToUse), requestCode (requestCodeToUse), callback (std::move (callbackToUse))
{}
void onStart() override
{
getEnv()->CallVoidMethod (getNativeHandle(), AndroidFragment.startActivityForResult,
intent.get(), requestCode);
}
void onActivityResult (int activityRequestCode, int resultCode, LocalRef<jobject> data) override
{
if (callback)
callback (activityRequestCode, resultCode, std::move (data));
getEnv()->CallVoidMethod (getNativeHandle(), JuceFragmentOverlay.close);
delete this;
}
private:
GlobalRef intent;
int requestCode;
std::function<void (int, int, LocalRef<jobject>)> callback;
};
void startAndroidActivityForResult (const LocalRef<jobject>& intent, int requestCode,
std::function<void (int, int, LocalRef<jobject>)> && callback)
{
auto* activityLauncher = new ActivityLauncher (intent, requestCode, std::move (callback));
activityLauncher->open();
}
//==============================================================================
bool androidHasSystemFeature (const String& property)
{
LocalRef<jobject> appContext (getAppContext());
if (appContext != nullptr)
{
auto* env = getEnv();
LocalRef<jobject> packageManager (env->CallObjectMethod (appContext.get(), AndroidContext.getPackageManager));
if (packageManager != nullptr)
return env->CallBooleanMethod (packageManager.get(),
AndroidPackageManager.hasSystemFeature,
javaString (property).get()) != 0;
}
// unable to get app's context
jassertfalse;
return false;
}
String audioManagerGetProperty (const String& property)
{
if (getAndroidSDKVersion() >= 17)
{
auto* env = getEnv();
LocalRef<jobject> audioManager (env->CallObjectMethod (getAppContext().get(), AndroidContext.getSystemService,
javaString ("audio").get()));
if (audioManager != nullptr)
{
LocalRef<jstring> jProperty (javaString (property));
auto methodID = env->GetMethodID (AndroidAudioManager, "getProperty", "(Ljava/lang/String;)Ljava/lang/String;");
if (methodID != nullptr)
return juceString (LocalRef<jstring> ((jstring) env->CallObjectMethod (audioManager.get(),
methodID,
javaString (property).get())));
}
}
return {};
}
}

File diff suppressed because it is too large Load Diff

View File

@ -23,15 +23,177 @@
namespace juce
{
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
//==============================================================================
// This byte-code is generated from native/java/com/roli/juce/JuceHTTPStream.java with min sdk version 16
// See juce_core/native/java/README.txt on how to generate this byte-code.
static const uint8 javaJuceHttpStream[] =
{31,139,8,8,96,105,229,91,0,3,74,117,99,101,72,84,84,80,83,116,114,101,97,109,46,100,101,120,0,125,154,11,124,84,213,157,
199,255,231,222,185,119,30,153,153,220,76,30,147,132,60,38,33,132,4,18,38,32,40,146,128,200,75,32,65,17,6,20,146,181,29,
50,23,50,48,220,9,51,119,32,88,84,64,171,212,218,74,173,86,250,82,139,88,105,87,91,171,237,214,86,187,109,197,118,219,
218,90,109,245,179,174,187,125,91,119,237,167,187,186,218,118,221,246,227,102,127,231,49,153,137,77,33,124,239,255,127,
254,231,127,206,61,143,255,121,204,36,41,123,34,208,119,193,18,58,123,73,224,169,145,163,171,22,29,152,124,110,71,248,
214,145,193,207,157,62,245,139,197,215,175,242,156,233,36,26,39,162,137,237,139,35,164,254,253,114,14,209,11,36,237,139,
192,183,116,162,56,228,9,15,81,61,228,147,38,209,149,144,71,188,68,200,34,79,128,104,101,19,81,10,242,205,90,162,63,130,
183,193,95,192,255,1,173,142,200,0,33,80,5,162,160,27,92,12,54,131,173,224,42,48,2,118,1,27,92,11,174,3,199,192,205,224,
86,112,59,56,5,206,128,179,224,41,240,10,104,140,18,45,3,215,128,27,193,253,224,187,224,183,128,161,193,49,112,17,184,28,
236,6,71,193,221,224,33,240,44,120,5,188,13,194,13,68,237,96,0,92,5,246,129,227,224,62,240,8,248,1,248,37,120,19,248,27,
137,174,6,105,112,45,248,4,120,2,188,6,106,102,161,14,112,13,56,2,62,9,254,5,76,130,30,140,211,213,96,20,236,6,123,129,3,
242,224,125,224,40,184,13,156,4,31,5,119,131,79,130,123,193,253,224,113,240,36,248,30,120,17,252,28,188,10,94,7,255,3,
222,1,70,51,81,16,68,65,12,244,128,53,96,59,24,3,215,130,219,192,167,193,3,224,9,112,14,252,24,188,10,254,2,204,22,244,
17,88,160,17,116,128,249,96,17,184,8,108,2,127,7,28,112,61,184,25,220,13,206,128,175,128,239,128,31,129,127,6,175,129,55,
193,36,240,181,18,213,129,78,208,15,46,3,27,193,86,240,94,176,23,28,2,71,193,9,112,23,184,23,60,0,30,6,143,129,175,129,
103,193,207,192,239,192,155,224,207,128,98,232,59,168,4,115,64,63,216,14,118,131,125,96,28,28,4,71,192,205,224,36,184,23,
60,0,190,0,190,9,94,4,63,3,191,6,255,14,254,12,188,109,68,179,65,31,88,9,182,128,221,96,63,112,193,17,112,2,124,28,60,8,
190,8,190,14,190,15,126,14,222,2,122,59,226,2,52,130,118,208,11,150,131,203,64,2,100,64,1,92,7,110,5,31,1,167,64,16,221,
178,0,194,138,16,62,132,233,37,76,15,97,40,73,117,153,80,61,193,149,102,131,14,128,229,75,88,214,52,23,116,129,110,48,15,
204,7,61,160,23,44,32,185,166,251,192,66,181,206,47,0,139,193,18,112,33,184,8,44,5,23,131,126,48,0,86,128,75,192,74,112,
41,88,5,214,128,117,224,50,176,30,108,4,91,192,54,176,29,92,69,178,31,197,127,33,37,151,98,111,8,43,125,101,153,190,30,
122,165,210,55,215,202,254,51,149,230,251,143,1,70,96,143,148,213,203,245,98,249,6,165,187,202,167,88,87,173,242,91,170,
236,181,202,94,173,244,35,202,30,45,179,71,149,189,70,233,55,65,175,83,250,109,202,94,175,236,181,74,95,170,244,134,50,
157,207,219,157,181,178,28,215,63,165,222,213,92,214,254,150,50,189,181,76,111,47,211,103,151,245,133,207,239,67,170,126,
62,199,93,170,206,121,202,135,207,67,175,210,7,149,206,251,114,133,210,191,12,125,72,233,79,150,233,125,101,58,111,255,
38,165,63,13,125,179,210,249,248,95,174,244,31,150,249,252,91,173,60,27,122,213,248,23,235,121,165,86,198,196,2,213,158,
173,74,255,61,236,9,165,187,170,47,11,213,123,117,204,244,247,136,203,5,244,28,164,7,35,103,11,89,75,123,132,236,161,180,
144,97,250,176,144,221,244,77,226,241,209,68,41,33,165,159,161,252,12,140,88,65,200,249,116,147,144,81,186,69,201,15,8,
41,235,49,240,190,59,72,198,217,105,33,25,125,78,200,56,125,94,200,122,250,130,144,125,244,13,33,139,239,197,154,87,242,
135,64,163,42,186,75,180,191,141,76,33,189,244,30,33,131,66,122,208,30,83,200,86,250,146,40,215,41,210,188,221,59,84,59,
71,133,172,163,221,66,154,52,166,236,7,132,244,211,97,33,13,186,65,201,99,42,255,164,144,58,61,36,100,43,61,172,198,225,
31,136,175,153,118,241,158,16,118,11,46,195,200,79,10,233,17,254,149,72,31,18,114,30,253,68,196,93,5,237,83,241,119,187,
136,189,86,81,46,138,113,217,165,228,135,72,198,246,199,132,92,64,95,23,178,146,190,45,164,37,100,61,86,212,136,144,17,
218,47,164,44,87,143,145,146,82,150,175,87,254,13,234,61,13,88,101,35,66,134,233,25,226,123,225,108,97,111,68,254,19,196,
215,83,43,57,66,250,232,90,33,27,233,125,42,125,68,200,0,93,79,114,221,29,21,50,74,199,133,140,209,99,66,118,211,151,149,
252,138,178,127,85,200,14,122,92,200,57,244,53,226,107,85,142,87,51,118,85,41,235,233,140,144,178,93,173,24,247,247,11,
25,164,123,136,239,205,33,154,32,190,63,7,233,58,37,111,36,190,158,103,145,75,124,45,55,208,157,196,215,113,51,125,139,
248,94,221,66,25,33,155,232,227,196,215,116,47,189,87,200,160,168,103,190,26,143,249,248,145,233,110,122,148,248,158,46,
237,92,222,45,228,124,122,86,165,127,76,242,142,70,36,215,22,223,35,170,32,31,199,198,117,229,28,105,247,148,229,247,169,
252,151,144,191,87,229,243,120,102,84,218,47,121,254,219,200,63,174,242,121,253,63,197,193,243,50,248,77,135,244,253,131,
146,239,116,240,117,128,251,4,124,67,115,164,45,170,100,167,146,151,40,185,126,14,175,75,23,250,167,176,233,249,32,135,
145,24,209,24,141,91,179,68,132,242,92,94,223,153,118,185,71,182,179,32,109,139,49,156,83,22,37,98,68,7,44,175,136,112,
199,234,19,114,60,86,141,18,85,108,122,222,2,33,187,222,228,109,99,226,125,15,183,203,126,58,22,183,4,69,31,77,252,240,
188,71,219,229,121,34,219,48,204,52,26,214,12,26,214,61,52,236,209,105,216,48,197,202,225,237,50,232,71,237,242,60,76,
244,121,80,23,223,105,3,56,63,107,41,177,80,167,102,150,232,51,209,82,31,113,25,137,228,98,235,17,35,45,76,250,94,46,74,
105,200,247,10,233,88,189,194,98,40,139,1,75,237,84,205,155,197,104,132,80,122,46,158,37,91,215,159,144,90,40,83,76,156,
16,132,213,171,3,46,153,58,187,116,113,206,243,158,47,17,82,71,219,47,184,33,116,225,114,172,204,0,241,81,122,165,93,238,
221,188,207,62,140,114,15,180,77,240,28,62,230,71,170,86,216,121,138,231,205,71,106,163,72,85,96,95,226,55,129,32,59,96,
93,198,199,145,13,31,11,208,200,173,149,52,252,193,8,13,223,22,164,29,31,170,163,225,15,215,208,240,237,213,20,249,239,
29,199,162,120,179,69,59,142,90,120,75,21,13,31,13,209,182,27,43,41,113,83,132,18,239,15,210,22,92,253,19,183,132,200,
123,204,123,199,65,175,95,213,232,69,207,53,117,35,168,154,45,227,47,129,185,173,18,177,233,21,49,82,15,123,158,199,135,
175,25,49,176,12,235,216,177,6,240,142,160,175,213,215,68,222,155,90,61,77,20,241,57,177,11,113,18,56,177,197,244,7,60,
47,194,179,209,119,53,180,86,156,3,65,45,162,183,117,45,185,49,78,235,124,154,222,232,215,97,111,166,83,20,48,151,155,
237,194,230,224,114,236,163,128,111,201,205,141,34,29,241,59,184,34,158,50,131,140,167,94,242,155,204,137,197,80,34,232,
229,158,94,120,46,247,97,22,251,46,166,26,239,75,186,206,186,158,79,156,13,161,214,165,104,195,210,96,132,34,245,78,108,
9,215,67,60,70,23,34,166,130,134,131,15,47,47,34,21,69,108,85,121,66,34,42,60,232,85,136,26,253,126,244,172,6,245,207,
245,201,59,206,85,124,70,217,172,61,213,56,23,2,208,15,205,150,247,139,254,112,45,69,194,237,168,193,251,25,246,168,247,
156,247,5,246,91,239,159,124,94,139,52,95,21,249,252,17,58,24,48,197,216,93,28,254,175,201,234,112,95,161,235,119,33,22,
161,174,183,229,216,86,201,122,27,43,68,140,135,233,19,179,249,12,227,126,228,59,201,34,53,17,61,241,128,159,154,141,196,
233,10,180,110,16,30,1,109,41,214,71,139,193,22,85,106,145,200,120,44,136,115,36,168,13,159,14,83,226,116,13,69,204,196,
131,94,120,46,226,35,226,29,244,106,166,180,174,209,52,115,241,241,159,82,68,155,94,130,251,198,81,107,80,203,89,171,149,
92,195,165,153,179,86,96,133,242,183,61,77,57,235,18,232,65,214,202,218,160,95,202,35,5,246,55,38,115,214,42,165,255,110,
50,241,96,53,70,174,1,209,218,133,121,43,122,181,106,81,204,65,55,118,221,160,214,232,243,8,253,56,226,164,209,175,81,
177,116,171,86,67,7,98,98,39,214,74,190,84,244,213,28,107,158,104,153,19,107,199,154,43,175,185,209,27,128,87,19,229,208,
206,139,181,179,147,165,26,99,20,241,230,98,43,145,35,61,15,88,13,178,14,171,81,140,225,142,211,81,234,186,61,36,198,177,
155,70,38,67,106,44,22,137,220,37,199,219,228,104,122,18,159,149,86,140,132,57,104,106,30,233,213,11,175,153,243,101,125,
139,39,103,158,139,110,90,54,25,210,90,140,185,90,72,75,156,249,107,15,143,57,247,93,115,218,77,113,148,232,166,158,73,
212,188,170,155,188,147,124,255,240,99,87,170,22,123,168,87,220,131,121,236,116,136,61,171,90,124,214,184,1,233,199,196,
222,19,18,103,44,63,243,159,83,126,255,170,236,191,80,233,255,84,233,55,196,174,205,232,29,81,111,53,249,152,180,27,55,
132,30,101,191,103,55,116,126,149,189,206,232,247,140,94,103,215,119,190,95,19,70,47,198,129,159,41,175,206,150,159,119,
34,90,98,171,143,154,245,196,149,50,90,25,5,216,82,134,104,213,189,109,56,37,174,12,80,59,246,244,241,62,70,9,51,23,91,
139,207,56,216,113,174,172,69,126,2,207,156,181,65,236,65,173,44,76,93,175,133,88,139,62,151,97,119,106,235,250,141,120,
254,82,158,165,13,240,225,235,206,135,247,206,85,251,172,70,157,90,117,247,156,158,26,232,94,62,23,24,12,126,103,168,198,
135,155,72,232,32,171,64,127,2,214,197,150,33,44,149,214,75,161,16,243,193,182,133,89,116,97,243,255,78,114,61,193,48,31,
250,154,42,166,87,179,94,198,111,151,139,172,26,170,102,11,148,142,177,101,113,165,195,159,93,32,244,11,172,151,85,233,
106,106,212,55,32,26,59,112,195,14,176,151,194,44,80,178,35,150,251,230,208,194,112,160,242,162,215,159,82,254,124,222,
47,16,253,117,172,229,92,250,139,254,78,223,92,250,85,101,176,162,184,187,158,162,246,32,180,190,126,186,173,130,251,6,
17,27,139,238,184,103,234,189,17,93,104,30,148,180,58,49,70,1,143,211,55,155,22,122,132,21,177,226,195,213,178,57,34,189,
203,231,165,82,140,70,75,164,155,38,38,49,218,17,140,118,128,91,186,105,188,172,141,189,240,230,169,109,122,37,201,247,
68,164,52,130,66,110,49,235,100,218,27,66,204,84,83,241,36,225,182,97,212,192,235,95,57,41,107,230,250,170,73,18,247,243,
185,104,233,205,106,255,187,91,204,163,135,62,13,233,133,198,239,125,124,247,127,74,165,117,186,11,91,228,41,118,195,172,
239,176,187,24,169,91,12,209,137,14,185,135,110,141,85,137,207,177,69,251,135,59,100,172,108,137,69,113,103,183,166,206,
179,59,213,125,41,130,29,221,15,111,15,126,62,209,33,63,27,226,76,220,133,157,86,75,36,195,180,148,249,137,203,136,238,
172,172,163,3,55,6,168,69,91,142,246,108,221,85,69,199,60,79,172,218,9,89,73,24,51,13,99,198,186,222,98,36,239,86,30,181,
134,26,85,92,206,154,58,255,27,88,168,137,199,44,67,116,106,244,88,135,252,238,160,29,37,18,7,112,255,200,97,47,206,227,
157,46,110,47,133,90,58,104,242,154,18,57,209,158,3,21,20,169,118,98,67,136,171,8,27,134,119,139,214,110,54,34,50,230,
163,46,199,234,33,126,67,146,109,137,136,27,17,19,223,79,48,241,51,7,111,144,55,212,103,202,250,206,191,75,228,255,52,42,
73,55,32,199,242,8,228,77,1,121,103,44,126,47,192,207,175,147,1,57,166,167,32,239,83,249,229,119,95,158,111,170,122,252,
74,242,239,15,206,42,223,14,85,95,157,146,81,245,222,162,140,171,250,226,170,78,63,201,207,58,113,225,209,39,62,191,68,
85,217,5,101,109,43,239,131,69,53,194,110,168,54,151,202,71,133,125,158,242,227,159,65,25,201,123,167,108,67,84,148,105,
130,165,7,189,233,86,246,102,85,46,78,197,241,96,24,222,46,194,133,141,45,35,109,89,140,216,0,153,3,105,39,237,174,32,
109,69,63,121,86,244,119,111,39,107,117,214,113,236,81,55,157,117,98,118,46,151,205,81,24,22,215,118,220,222,33,219,217,
227,142,81,237,154,116,126,116,202,105,75,193,113,146,187,50,54,177,13,164,109,24,34,125,195,208,6,242,224,129,253,112,
35,85,111,44,140,218,235,19,137,205,91,221,156,157,220,191,96,111,242,96,146,216,16,105,112,210,185,143,54,132,34,67,120,
120,135,134,118,14,13,161,130,128,82,184,174,13,237,164,250,161,164,147,202,101,211,169,184,107,79,184,241,4,30,219,220,
116,38,223,79,177,161,209,236,254,120,46,155,73,199,247,226,53,241,233,239,234,88,216,79,139,207,239,49,99,71,250,169,
229,188,165,250,169,125,40,149,204,28,76,239,139,39,29,39,235,38,121,225,248,90,103,52,147,205,167,157,61,171,51,201,60,
218,54,251,124,62,155,108,119,44,155,226,47,250,107,167,13,104,79,78,85,210,54,67,254,38,123,255,46,229,96,195,165,121,6,
151,173,233,61,78,210,45,228,208,149,198,25,178,19,99,185,236,33,81,148,207,70,60,157,141,175,42,236,222,109,231,236,212,
6,103,188,224,22,123,89,59,149,189,225,138,181,19,163,246,56,47,60,205,92,238,93,55,101,190,162,224,150,217,235,165,61,
147,116,246,196,87,143,37,115,91,237,3,5,219,25,181,167,42,18,57,101,245,87,151,153,55,32,238,246,216,57,62,211,211,141,
185,92,97,220,181,83,101,197,106,202,61,224,32,103,50,82,102,189,98,215,94,76,244,116,207,210,156,151,123,162,237,152,
164,233,109,151,54,57,80,253,212,48,67,78,58,147,226,89,229,21,97,164,237,100,106,122,87,197,232,203,119,54,73,179,99,
187,241,245,174,59,190,109,203,80,105,237,245,83,184,148,139,156,169,214,168,116,185,167,234,81,1,171,66,246,221,205,162,
33,86,153,117,40,157,119,167,154,33,44,155,146,227,29,107,29,55,119,184,159,54,205,100,30,248,235,241,120,87,125,51,120,
172,192,15,85,78,175,110,186,97,171,237,242,176,47,25,176,254,70,11,185,28,182,151,248,234,100,38,35,118,146,214,243,231,
247,83,207,223,114,64,96,193,135,143,74,89,104,116,207,236,189,118,194,30,45,188,203,181,243,124,174,89,4,111,238,96,154,
199,110,236,252,126,249,169,185,125,183,199,186,2,95,151,197,165,55,115,110,63,13,156,47,123,224,188,203,22,19,208,49,
115,105,25,140,235,146,163,104,32,102,125,254,204,94,136,157,253,233,209,248,165,66,172,202,102,51,118,18,227,50,111,102,
231,76,118,116,95,62,190,197,134,158,75,58,238,16,146,253,228,135,16,83,176,140,216,118,210,182,99,143,223,142,61,126,59,
246,120,19,15,190,215,35,177,147,2,219,203,246,249,237,59,137,237,36,109,231,70,0,57,188,10,108,160,234,225,25,86,165,54,
226,80,32,57,58,106,231,243,29,125,125,125,84,33,245,117,153,228,158,60,121,147,169,84,14,41,50,147,227,227,182,147,34,
239,174,100,222,222,150,203,144,185,75,140,22,121,70,17,70,100,142,138,88,33,131,239,200,54,249,177,225,143,39,115,118,
34,75,94,117,34,80,160,116,52,80,93,73,79,100,75,167,6,89,163,24,83,215,46,45,198,162,69,78,21,31,144,162,165,116,128,20,
45,50,37,124,42,213,136,22,135,142,204,148,61,154,77,217,84,147,178,119,39,11,25,119,218,228,241,220,140,237,218,20,72,
149,154,82,155,154,241,84,174,158,102,150,213,144,63,149,85,77,38,102,147,193,167,239,48,249,132,192,2,133,166,226,152,
140,221,105,59,147,130,200,20,242,99,164,239,65,102,13,30,197,133,134,23,168,46,133,97,93,203,111,11,42,93,137,244,122,
188,202,206,173,227,53,228,133,67,89,156,146,137,244,160,125,88,56,150,159,24,84,1,195,102,156,146,98,24,120,238,22,59,
63,158,117,242,24,100,140,7,175,38,129,131,44,163,238,34,188,26,236,134,228,131,220,158,204,20,108,178,198,146,249,85,
136,72,213,70,27,33,0,203,229,184,53,80,197,152,104,209,80,218,177,17,41,50,145,167,160,82,18,217,109,136,132,240,24,118,
226,45,252,144,202,187,171,247,167,168,122,122,90,58,5,184,81,181,151,165,201,155,118,82,246,196,21,187,169,34,93,214,67,
95,218,81,77,170,72,231,215,78,140,37,11,121,151,183,38,157,23,227,64,102,58,143,126,186,60,151,75,89,179,47,173,246,111,
242,236,205,166,17,7,25,217,79,15,95,106,228,113,146,251,49,165,142,125,104,117,114,116,204,78,201,233,220,140,85,74,33,
110,44,133,97,5,146,165,96,66,2,175,16,146,47,5,143,195,71,67,119,10,104,36,30,171,14,187,24,143,26,104,235,178,153,76,
246,144,157,218,98,167,210,57,212,36,173,83,169,68,86,58,80,195,76,86,53,126,89,44,187,178,134,232,89,116,85,31,199,219,
61,120,44,20,207,69,228,27,47,206,48,215,220,53,73,55,73,161,162,38,43,242,136,72,245,230,16,233,88,193,84,153,83,97,176,
94,77,91,205,187,12,178,148,9,43,150,11,233,185,2,222,157,71,188,86,225,161,218,147,72,239,183,121,115,42,96,90,147,149,
81,71,141,121,30,152,121,151,135,139,236,72,169,247,220,113,42,22,195,121,30,139,201,84,177,22,75,164,69,96,200,251,28,
69,74,150,205,57,140,67,206,61,76,70,126,60,147,118,33,220,100,14,115,14,225,22,242,20,144,82,132,116,101,73,151,125,8,
230,69,0,169,163,194,204,23,118,237,71,13,126,200,188,216,2,41,228,142,149,111,57,213,60,249,238,93,167,100,44,219,102,
194,48,150,47,194,74,164,183,150,191,204,227,142,165,17,39,252,217,209,71,126,23,93,197,48,109,194,242,157,82,85,156,186,
89,185,31,83,133,91,182,28,235,248,57,144,223,157,205,237,183,83,151,151,69,160,23,59,139,104,129,89,112,68,36,87,28,76,
102,58,138,43,193,56,40,150,137,113,40,135,232,39,109,162,143,142,104,223,103,228,13,211,81,254,156,92,54,64,71,120,234,
30,205,56,199,190,203,190,195,158,198,231,109,227,159,216,188,117,222,240,64,47,255,71,159,215,224,112,98,249,96,186,117,
100,112,120,240,154,97,109,188,121,109,219,57,170,243,143,220,201,30,98,31,96,207,176,111,178,71,216,25,246,65,230,13,
107,127,209,250,39,38,14,107,199,175,59,35,10,247,14,12,14,177,72,37,89,236,5,228,210,106,115,228,163,236,239,217,9,246,
3,225,253,138,238,249,36,107,30,28,184,100,159,174,221,194,22,49,166,235,31,98,108,249,9,221,124,144,177,207,78,232,236,
80,253,9,221,251,19,86,159,214,14,246,27,204,240,106,225,126,195,28,94,48,180,96,80,55,62,203,172,129,75,12,163,197,208,
12,189,85,167,124,211,0,205,243,79,85,255,143,236,139,236,126,118,43,94,210,59,135,85,69,209,230,94,173,162,191,141,126,
141,94,111,92,126,139,214,219,163,237,104,214,42,14,61,62,113,61,107,180,60,204,109,239,127,154,53,84,178,250,208,138,95,
177,250,106,131,241,42,195,56,52,13,226,218,220,19,95,190,230,71,125,90,203,243,6,105,187,250,207,117,124,76,179,250,233,
32,187,131,121,155,81,89,109,143,182,191,249,229,182,143,172,68,15,40,95,211,218,98,144,208,58,91,233,78,62,202,189,186,
245,37,22,111,211,67,167,25,27,234,213,190,77,61,134,165,93,218,108,90,31,56,172,87,125,131,49,86,95,117,78,15,159,69,
159,245,202,207,49,182,224,156,238,223,87,255,65,189,226,83,172,126,167,238,187,166,101,167,30,188,135,181,12,233,218,
179,172,62,206,44,235,240,3,90,104,135,97,173,68,215,125,70,176,85,15,228,155,52,103,163,17,48,53,211,103,6,77,235,125,
172,165,246,162,86,51,64,111,241,169,163,183,197,243,29,241,252,185,166,189,231,51,24,149,54,157,238,195,80,15,78,180,
247,238,213,38,154,7,232,25,158,253,71,157,198,141,229,123,7,90,119,234,230,129,89,3,13,134,73,199,117,246,105,62,117,
154,151,61,207,26,131,154,79,219,233,161,83,172,69,11,32,221,100,68,123,163,203,163,70,180,35,234,213,42,184,1,3,88,84,
170,52,19,74,189,33,138,248,78,177,104,133,44,227,137,174,86,37,68,210,31,101,209,197,209,206,232,122,84,37,51,124,154,
95,214,224,41,86,213,73,26,211,232,100,140,225,255,209,163,158,71,106,53,246,92,45,251,118,236,190,58,50,24,211,144,41,
126,200,115,236,168,231,201,58,131,253,71,29,85,153,94,175,198,34,226,71,228,49,109,218,143,72,155,240,127,164,158,157,
136,189,212,160,69,222,104,96,13,127,152,197,34,63,108,98,145,163,237,190,170,147,179,141,170,183,59,89,213,201,185,172,
234,44,120,18,60,210,197,170,206,116,139,239,148,168,236,187,6,46,139,127,187,197,191,31,40,254,253,86,241,123,11,254,55,
92,252,59,146,226,223,113,241,239,20,138,127,203,101,82,233,239,185,116,75,254,254,141,127,15,195,98,242,119,191,143,240,
239,79,98,210,135,255,254,144,89,165,223,41,106,49,249,94,254,247,95,186,242,231,191,195,243,196,72,252,238,137,255,126,
144,84,89,241,123,71,75,182,149,255,173,217,255,3,11,139,181,139,164,38,0,0};
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
METHOD (constructor, "<init>", "()V") \
METHOD (toString, "toString", "()Ljava/lang/String;") \
DECLARE_JNI_CLASS (StringBuffer, "java/lang/StringBuffer");
DECLARE_JNI_CLASS (StringBuffer, "java/lang/StringBuffer")
#undef JNI_CLASS_MEMBERS
//==============================================================================
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
STATICMETHOD (createHTTPStream, "createHTTPStream", "(Ljava/lang/String;Z[BLjava/lang/String;I[ILjava/lang/StringBuffer;ILjava/lang/String;)Lcom/roli/juce/JuceHTTPStream;") \
METHOD (connect, "connect", "()Z") \
METHOD (release, "release", "()V") \
METHOD (read, "read", "([BI)I") \
@ -40,15 +202,15 @@ DECLARE_JNI_CLASS (StringBuffer, "java/lang/StringBuffer");
METHOD (isExhausted, "isExhausted", "()Z") \
METHOD (setPosition, "setPosition", "(J)Z") \
DECLARE_JNI_CLASS (HTTPStream, JUCE_ANDROID_ACTIVITY_CLASSPATH "$HTTPStream");
DECLARE_JNI_CLASS_WITH_BYTECODE (HTTPStream, "com/roli/juce/JuceHTTPStream", 16, javaJuceHttpStream, sizeof(javaJuceHttpStream))
#undef JNI_CLASS_MEMBERS
//==============================================================================
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
METHOD (close, "close", "()V") \
METHOD (read, "read", "([BII)I") \
DECLARE_JNI_CLASS (AndroidInputStream, "java/io/InputStream");
DECLARE_JNI_CLASS (AndroidInputStream, "java/io/InputStream")
#undef JNI_CLASS_MEMBERS
//==============================================================================
@ -192,8 +354,8 @@ public:
const ScopedLock lock (createStreamLock);
if (! hasBeenCancelled)
stream = GlobalRef (LocalRef<jobject> (env->CallStaticObjectMethod (JuceAppActivity,
JuceAppActivity.createHTTPStream,
stream = GlobalRef (LocalRef<jobject> (env->CallStaticObjectMethod (HTTPStream,
HTTPStream.createHTTPStream,
javaString (address).get(),
(jboolean) isPost,
postDataArray,
@ -342,15 +504,22 @@ URL::DownloadTask* URL::downloadToFile (const File& targetLocation, String extra
}
//==============================================================================
static void addAddress (const sockaddr_in* addr_in, Array<IPAddress>& result)
{
in_addr_t addr = addr_in->sin_addr.s_addr;
#if __ANDROID_API__ < 24 // Android support for getifadds was added in Android 7.0 (API 24) so the posix implementation does not apply
if (addr != INADDR_NONE)
result.addIfNotAlreadyThere (IPAddress (ntohl (addr)));
static IPAddress makeAddress (const sockaddr_in *addr_in)
{
if (addr_in->sin_addr.s_addr == INADDR_NONE)
return {};
return IPAddress (ntohl (addr_in->sin_addr.s_addr));
}
static void findIPAddresses (int sock, Array<IPAddress>& result)
struct InterfaceInfo
{
IPAddress interfaceAddress, broadcastAddress;
};
static Array<InterfaceInfo> findIPAddresses (int dummySocket)
{
ifconf cfg;
HeapBlock<char> buffer;
@ -364,40 +533,66 @@ static void findIPAddresses (int sock, Array<IPAddress>& result)
cfg.ifc_len = bufferSize;
cfg.ifc_buf = buffer;
if (ioctl (sock, SIOCGIFCONF, &cfg) < 0 && errno != EINVAL)
return;
if (ioctl (dummySocket, SIOCGIFCONF, &cfg) < 0 && errno != EINVAL)
return {};
} while (bufferSize < cfg.ifc_len + 2 * (int) (IFNAMSIZ + sizeof (struct sockaddr_in6)));
#if JUCE_MAC || JUCE_IOS
while (cfg.ifc_len >= (int) (IFNAMSIZ + sizeof (struct sockaddr_in)))
{
if (cfg.ifc_req->ifr_addr.sa_family == AF_INET) // Skip non-internet addresses
addAddress ((const sockaddr_in*) &cfg.ifc_req->ifr_addr, result);
Array<InterfaceInfo> result;
cfg.ifc_len -= IFNAMSIZ + cfg.ifc_req->ifr_addr.sa_len;
cfg.ifc_buf += IFNAMSIZ + cfg.ifc_req->ifr_addr.sa_len;
}
#else
for (size_t i = 0; i < (size_t) cfg.ifc_len / (size_t) sizeof (struct ifreq); ++i)
{
const ifreq& item = cfg.ifc_req[i];
auto& item = cfg.ifc_req[i];
if (item.ifr_addr.sa_family == AF_INET)
addAddress ((const sockaddr_in*) &item.ifr_addr, result);
{
InterfaceInfo info;
info.interfaceAddress = makeAddress ((const sockaddr_in*) &item.ifr_addr);
if (! info.interfaceAddress.isNull())
{
if (ioctl (dummySocket, SIOCGIFBRDADDR, &item) == 0)
info.broadcastAddress = makeAddress ((const sockaddr_in*) &item.ifr_broadaddr);
result.add (info);
}
}
else if (item.ifr_addr.sa_family == AF_INET6)
{
// TODO: IPv6
}
}
#endif
return result;
}
static Array<InterfaceInfo> findIPAddresses()
{
auto dummySocket = socket (AF_INET, SOCK_DGRAM, 0); // a dummy socket to execute the IO control
if (dummySocket < 0)
return {};
auto result = findIPAddresses (dummySocket);
::close (dummySocket);
return result;
}
void IPAddress::findAllAddresses (Array<IPAddress>& result, bool /*includeIPv6*/)
{
const int sock = socket (AF_INET, SOCK_DGRAM, 0); // a dummy socket to execute the IO control
if (sock >= 0)
{
findIPAddresses (sock, result);
::close (sock);
}
for (auto& a : findIPAddresses())
result.add (a.interfaceAddress);
}
IPAddress IPAddress::getInterfaceBroadcastAddress (const IPAddress& address)
{
for (auto& a : findIPAddresses())
if (a.interfaceAddress == address)
return a.broadcastAddress;
return {};
}
#endif
} // namespace juce

View File

@ -23,33 +23,185 @@
namespace juce
{
static void handleAndroidCallback (bool permissionWasGranted, RuntimePermissions::Callback* callbackPtr)
//==============================================================================
static String jucePermissionToAndroidPermission (RuntimePermissions::PermissionID permission)
{
if (callbackPtr == nullptr)
switch (permission)
{
// got a nullptr passed in from java! this should never happen...
jassertfalse;
return;
case RuntimePermissions::recordAudio: return "android.permission.RECORD_AUDIO";
case RuntimePermissions::bluetoothMidi: return "android.permission.ACCESS_COARSE_LOCATION";
case RuntimePermissions::readExternalStorage: return "android.permission.READ_EXTERNAL_STORAGE";
case RuntimePermissions::writeExternalStorage: return "android.permission.WRITE_EXTERNAL_STORAGE";
case RuntimePermissions::camera: return "android.permission.CAMERA";
}
std::unique_ptr<RuntimePermissions::Callback> uptr (callbackPtr);
if (RuntimePermissions::Callback callbackObj = *uptr)
callbackObj (permissionWasGranted);
// invalid permission
jassertfalse;
return {};
}
JUCE_JNI_CALLBACK (JUCE_ANDROID_ACTIVITY_CLASSNAME,
androidRuntimePermissionsCallback,
void, (JNIEnv* env, jobject, jboolean permissionsGranted, jlong callbackPtr))
static RuntimePermissions::PermissionID androidPermissionToJucePermission (const String& permission)
{
setEnv (env);
handleAndroidCallback (permissionsGranted != 0,
reinterpret_cast<RuntimePermissions::Callback*> (callbackPtr));
if (permission == "android.permission.RECORD_AUDIO") return RuntimePermissions::recordAudio;
else if (permission == "android.permission.ACCESS_COARSE_LOCATION") return RuntimePermissions::bluetoothMidi;
else if (permission == "android.permission.READ_EXTERNAL_STORAGE") return RuntimePermissions::readExternalStorage;
else if (permission == "android.permission.WRITE_EXTERNAL_STORAGE") return RuntimePermissions::writeExternalStorage;
else if (permission == "android.permission.CAMERA") return RuntimePermissions::camera;
return static_cast<RuntimePermissions::PermissionID> (-1);
}
//==============================================================================
struct PermissionsRequest
{
PermissionsRequest() {}
// using "= default" on the following method triggers an internal compiler error
// in Android NDK 17
PermissionsRequest (const PermissionsRequest& o)
: callback (o.callback), permission (o.permission)
{}
PermissionsRequest (PermissionsRequest&& o)
: callback (std::move (o.callback)), permission (o.permission)
{
o.permission = static_cast<RuntimePermissions::PermissionID> (-1);
}
PermissionsRequest (RuntimePermissions::Callback && callbackToUse,
RuntimePermissions::PermissionID permissionToRequest)
: callback (std::move (callbackToUse)), permission (permissionToRequest)
{}
PermissionsRequest& operator= (const PermissionsRequest & o)
{
callback = o.callback;
permission = o.permission;
return *this;
}
PermissionsRequest& operator= (PermissionsRequest && o)
{
callback = std::move (o.callback);
permission = o.permission;
return *this;
}
RuntimePermissions::Callback callback;
RuntimePermissions::PermissionID permission;
};
//==============================================================================
struct PermissionsOverlay : FragmentOverlay
{
PermissionsOverlay (CriticalSection& cs) : overlayGuard (cs) {}
~PermissionsOverlay() {}
struct PermissionResult
{
PermissionsRequest request;
bool granted;
};
void onStart() override { onRequestPermissionsResult (0, {}, {}); }
void onRequestPermissionsResult (int /*requestCode*/,
const StringArray& permissions,
const Array<int>& grantResults) override
{
std::vector<PermissionResult> results;
{
ScopedLock lock (overlayGuard);
for (auto it = requests.begin(); it != requests.end();)
{
auto& request = *it;
if (RuntimePermissions::isGranted (request.permission))
{
results.push_back ({std::move (request), true});
it = requests.erase (it);
}
else
{
++it;
}
}
auto n = permissions.size();
for (int i = 0; i < n; ++i)
{
auto permission = androidPermissionToJucePermission (permissions[i]);
auto granted = (grantResults.getReference (i) == 0);
for (auto it = requests.begin(); it != requests.end();)
{
auto& request = *it;
if (request.permission == permission)
{
results.push_back ({std::move (request), granted});
it = requests.erase (it);
}
else
{
++it;
}
}
}
}
for (const auto& result : results)
if (result.request.callback)
result.request.callback (result.granted);
{
auto* env = getEnv();
ScopedLock lock (overlayGuard);
if (requests.size() > 0)
{
auto &request = requests.front();
StringArray permissionsArray{
jucePermissionToAndroidPermission (request.permission)};
auto jPermissionsArray = juceStringArrayToJava (permissionsArray);
auto requestPermissionsMethodID
= env->GetMethodID(AndroidFragment, "requestPermissions", "([Ljava/lang/String;I)V");
// this code should only be reached for SDKs >= 23, so this method should be
// be available
jassert(requestPermissionsMethodID != 0);
env->CallVoidMethod (getNativeHandle(), requestPermissionsMethodID, jPermissionsArray.get (), 0);
}
else
{
getSingleton() = nullptr;
}
}
}
static std::unique_ptr<PermissionsOverlay>& getSingleton()
{
static std::unique_ptr<PermissionsOverlay> instance;
return instance;
}
CriticalSection& overlayGuard;
std::vector<PermissionsRequest> requests;
};
//==============================================================================
void RuntimePermissions::request (PermissionID permission, Callback callback)
{
if (! android.activity.callBooleanMethod (JuceAppActivity.isPermissionDeclaredInManifest, (jint) permission))
auto requestedPermission = jucePermissionToAndroidPermission (permission);
if (! isPermissionDeclaredInManifest (requestedPermission))
{
// Error! If you want to be able to request this runtime permission, you
// also need to declare it in your app's manifest. You can do so via
@ -60,29 +212,50 @@ void RuntimePermissions::request (PermissionID permission, Callback callback)
return;
}
if (JUCE_ANDROID_API_VERSION < 23)
auto alreadyGranted = isGranted (permission);
if (alreadyGranted || getAndroidSDKVersion() < 23)
{
// There is no runtime permission system on API level below 23. As long as the
// permission is in the manifest (seems to be the case), we can simply ask Android
// if the app has the permission, and then directly call through to the callback.
callback (isGranted (permission));
callback (alreadyGranted);
return;
}
// we need to move the callback object to the heap so Java can keep track of the pointer
// and asynchronously pass it back to us (to be called and then deleted)
Callback* callbackPtr = new Callback (std::move (callback));
android.activity.callVoidMethod (JuceAppActivity.requestRuntimePermission, permission, (jlong) callbackPtr);
PermissionsRequest request (std::move (callback), permission);
static CriticalSection overlayGuard;
ScopedLock lock (overlayGuard);
std::unique_ptr<PermissionsOverlay>& overlay = PermissionsOverlay::getSingleton();
bool alreadyOpen = true;
if (overlay == nullptr)
{
overlay.reset (new PermissionsOverlay (overlayGuard));
alreadyOpen = false;
}
overlay->requests.push_back (std::move (request));
if (! alreadyOpen)
overlay->open();
}
bool RuntimePermissions::isRequired (PermissionID /*permission*/)
{
return JUCE_ANDROID_API_VERSION >= 23;
return getAndroidSDKVersion() >= 23;
}
bool RuntimePermissions::isGranted (PermissionID permission)
{
return android.activity.callBooleanMethod (JuceAppActivity.isPermissionGranted, permission);
auto* env = getEnv();
auto requestedPermission = jucePermissionToAndroidPermission (permission);
int result = env->CallIntMethod (getAppContext().get(), AndroidContext.checkCallingOrSelfPermission,
javaString (requestedPermission).get());
return result == 0 /* PERMISSION_GRANTED */;
}
} // namespace juce

View File

@ -23,319 +23,21 @@
namespace juce
{
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
STATICMETHOD (newProxyInstance, "newProxyInstance", "(Ljava/lang/ClassLoader;[Ljava/lang/Class;Ljava/lang/reflect/InvocationHandler;)Ljava/lang/Object;") \
DECLARE_JNI_CLASS (JavaProxy, "java/lang/reflect/Proxy");
#undef JNI_CLASS_MEMBERS
JNIClassBase::JNIClassBase (const char* cp) : classPath (cp), classRef (0)
{
getClasses().add (this);
}
JNIClassBase::~JNIClassBase()
{
getClasses().removeFirstMatchingValue (this);
}
Array<JNIClassBase*>& JNIClassBase::getClasses()
{
static Array<JNIClassBase*> classes;
return classes;
}
void JNIClassBase::initialise (JNIEnv* env)
{
classRef = (jclass) env->NewGlobalRef (LocalRef<jobject> (env->FindClass (classPath)));
jassert (classRef != 0);
initialiseFields (env);
}
void JNIClassBase::release (JNIEnv* env)
{
env->DeleteGlobalRef (classRef);
}
void JNIClassBase::initialiseAllClasses (JNIEnv* env)
{
const Array<JNIClassBase*>& classes = getClasses();
for (int i = classes.size(); --i >= 0;)
classes.getUnchecked(i)->initialise (env);
}
void JNIClassBase::releaseAllClasses (JNIEnv* env)
{
const Array<JNIClassBase*>& classes = getClasses();
for (int i = classes.size(); --i >= 0;)
classes.getUnchecked(i)->release (env);
}
jmethodID JNIClassBase::resolveMethod (JNIEnv* env, const char* methodName, const char* params)
{
jmethodID m = env->GetMethodID (classRef, methodName, params);
jassert (m != 0);
return m;
}
jmethodID JNIClassBase::resolveStaticMethod (JNIEnv* env, const char* methodName, const char* params)
{
jmethodID m = env->GetStaticMethodID (classRef, methodName, params);
jassert (m != 0);
return m;
}
jfieldID JNIClassBase::resolveField (JNIEnv* env, const char* fieldName, const char* signature)
{
jfieldID f = env->GetFieldID (classRef, fieldName, signature);
jassert (f != 0);
return f;
}
jfieldID JNIClassBase::resolveStaticField (JNIEnv* env, const char* fieldName, const char* signature)
{
jfieldID f = env->GetStaticFieldID (classRef, fieldName, signature);
jassert (f != 0);
return f;
}
//==============================================================================
LocalRef<jobject> CreateJavaInterface (AndroidInterfaceImplementer* implementer,
const StringArray& interfaceNames,
LocalRef<jobject> subclass)
{
auto* env = getEnv();
implementer->javaSubClass = GlobalRef (subclass);
// you need to override at least one interface
jassert (interfaceNames.size() > 0);
auto classArray = LocalRef<jobject> (env->NewObjectArray (interfaceNames.size(), JavaClass, nullptr));
LocalRef<jobject> classLoader;
for (auto i = 0; i < interfaceNames.size(); ++i)
{
auto aClass = LocalRef<jobject> (env->FindClass (interfaceNames[i].toRawUTF8()));
if (aClass != nullptr)
{
if (i == 0)
classLoader = LocalRef<jobject> (env->CallObjectMethod (aClass, JavaClass.getClassLoader));
env->SetObjectArrayElement ((jobjectArray) classArray.get(), i, aClass);
}
else
{
// interface class not found
jassertfalse;
}
}
auto invocationHandler = LocalRef<jobject> (env->CallObjectMethod (android.activity,
JuceAppActivity.createInvocationHandler,
reinterpret_cast<jlong> (implementer)));
// CreateJavaInterface() is expected to be called just once for a given implementer
jassert (implementer->invocationHandler == nullptr);
implementer->invocationHandler = GlobalRef (invocationHandler);
return LocalRef<jobject> (env->CallStaticObjectMethod (JavaProxy, JavaProxy.newProxyInstance,
classLoader.get(), classArray.get(),
invocationHandler.get()));
}
LocalRef<jobject> CreateJavaInterface (AndroidInterfaceImplementer* implementer,
const StringArray& interfaceNames)
{
return CreateJavaInterface (implementer, interfaceNames,
LocalRef<jobject> (getEnv()->NewObject (JavaObject,
JavaObject.constructor)));
}
LocalRef<jobject> CreateJavaInterface (AndroidInterfaceImplementer* implementer,
const String& interfaceName)
{
return CreateJavaInterface (implementer, StringArray (interfaceName));
}
AndroidInterfaceImplementer::~AndroidInterfaceImplementer()
{
if (invocationHandler != nullptr)
getEnv()->CallVoidMethod (android.activity,
JuceAppActivity.invocationHandlerContextDeleted,
invocationHandler.get());
}
jobject AndroidInterfaceImplementer::invoke (jobject /*proxy*/, jobject method, jobjectArray args)
{
auto* env = getEnv();
return env->CallObjectMethod (method, JavaMethod.invoke, javaSubClass.get(), args);
}
jobject juce_invokeImplementer (JNIEnv* env, jlong thisPtr, jobject proxy, jobject method, jobjectArray args)
{
setEnv (env);
return reinterpret_cast<AndroidInterfaceImplementer*> (thisPtr)->invoke (proxy, method, args);
}
void juce_dispatchDelete (JNIEnv* env, jlong thisPtr)
{
setEnv (env);
delete reinterpret_cast<AndroidInterfaceImplementer*> (thisPtr);
}
JUCE_JNI_CALLBACK (JUCE_JOIN_MACRO (JUCE_ANDROID_ACTIVITY_CLASSNAME, _00024NativeInvocationHandler), dispatchInvoke,
jobject, (JNIEnv* env, jobject /*object*/, jlong thisPtr, jobject proxy, jobject method, jobjectArray args))
{
return juce_invokeImplementer (env, thisPtr, proxy, method, args);
}
JUCE_JNI_CALLBACK (JUCE_JOIN_MACRO (JUCE_ANDROID_ACTIVITY_CLASSNAME, _00024NativeInvocationHandler), dispatchFinalize,
void, (JNIEnv* env, jobject /*object*/, jlong thisPtr))
{
juce_dispatchDelete (env, thisPtr);
}
//==============================================================================
JavaVM* androidJNIJavaVM = nullptr;
class JniEnvThreadHolder
{
public:
static JniEnvThreadHolder& getInstance() noexcept
{
// You cann only use JNI functions AFTER JNI_OnLoad was called
jassert (androidJNIJavaVM != nullptr);
try
{
if (instance == nullptr)
instance = new JniEnvThreadHolder;
}
catch (...)
{
jassertfalse;
std::terminate();
}
return *instance;
}
static JNIEnv* getEnv() { return reinterpret_cast<JNIEnv*> (pthread_getspecific (getInstance().threadKey)); }
static void setEnv (JNIEnv* env)
{
// env must not be a nullptr
jassert (env != nullptr);
#if JUCE_DEBUG
JNIEnv* oldenv = reinterpret_cast<JNIEnv*> (pthread_getspecific (getInstance().threadKey));
// This thread is already attached to the JavaVM and you trying to attach
// it to a different instance of the VM.
jassert (oldenv == nullptr || oldenv == env);
#endif
pthread_setspecific (getInstance().threadKey, env);
}
private:
pthread_key_t threadKey;
static void threadDetach (void* p)
{
if (JNIEnv* env = reinterpret_cast<JNIEnv*> (p))
{
ignoreUnused (env);
androidJNIJavaVM->DetachCurrentThread();
}
}
JniEnvThreadHolder()
{
pthread_key_create (&threadKey, threadDetach);
}
static JniEnvThreadHolder* instance;
};
JniEnvThreadHolder* JniEnvThreadHolder::instance = nullptr;
//==============================================================================
JNIEnv* attachAndroidJNI() noexcept
{
auto* env = JniEnvThreadHolder::getEnv();
if (env == nullptr)
{
androidJNIJavaVM->AttachCurrentThread (&env, nullptr);
setEnv (env);
}
return env;
}
JNIEnv* getEnv() noexcept
{
auto* env = JniEnvThreadHolder::getEnv();
// You are trying to use a JUCE function on a thread that was not created by JUCE.
// You need to first call setEnv on this thread before using JUCE
jassert (env != nullptr);
return env;
}
void setEnv (JNIEnv* env) noexcept { JniEnvThreadHolder::setEnv (env); }
extern "C" jint JNI_OnLoad (JavaVM* vm, void*)
{
// Huh? JNI_OnLoad was called two times!
jassert (androidJNIJavaVM == nullptr);
androidJNIJavaVM = vm;
return JNI_VERSION_1_2;
}
//==============================================================================
AndroidSystem::AndroidSystem() : screenWidth (0), screenHeight (0), dpi (160)
{
}
void AndroidSystem::initialise (JNIEnv* env, jobject act, jstring file, jstring dataDir)
{
setEnv (env);
screenWidth = screenHeight = 0;
dpi = 160;
JNIClassBase::initialiseAllClasses (env);
activity = GlobalRef (act);
appFile = juceString (env, file);
appDataDir = juceString (env, dataDir);
}
void AndroidSystem::shutdown (JNIEnv* env)
{
activity.clear();
JNIClassBase::releaseAllClasses (env);
}
AndroidSystem android;
//==============================================================================
namespace AndroidStatsHelpers
{
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
STATICMETHOD (getProperty, "getProperty", "(Ljava/lang/String;)Ljava/lang/String;")
DECLARE_JNI_CLASS (SystemClass, "java/lang/System");
DECLARE_JNI_CLASS (SystemClass, "java/lang/System")
#undef JNI_CLASS_MEMBERS
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
STATICMETHOD (getDefault, "getDefault", "()Ljava/util/Locale;") \
METHOD (getCountry, "getCountry", "()Ljava/lang/String;") \
METHOD (getLanguage, "getLanguage", "()Ljava/lang/String;")
DECLARE_JNI_CLASS (JavaLocale, "java/util/Locale")
#undef JNI_CLASS_MEMBERS
static inline String getSystemProperty (const String& name)
@ -347,19 +49,19 @@ namespace AndroidStatsHelpers
static inline String getLocaleValue (bool isRegion)
{
return juceString (LocalRef<jstring> ((jstring) getEnv()->CallStaticObjectMethod (JuceAppActivity,
JuceAppActivity.getLocaleValue,
isRegion)));
}
auto* env = getEnv();
LocalRef<jobject> locale (env->CallStaticObjectMethod (JavaLocale, JavaLocale.getDefault));
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD)
DECLARE_JNI_CLASS (BuildClass, "android/os/Build");
#undef JNI_CLASS_MEMBERS
auto stringResult = isRegion ? env->CallObjectMethod (locale.get(), JavaLocale.getCountry)
: env->CallObjectMethod (locale.get(), JavaLocale.getLanguage);
return juceString (LocalRef<jstring> ((jstring) stringResult));
}
static inline String getAndroidOsBuildValue (const char* fieldName)
{
return juceString (LocalRef<jstring> ((jstring) getEnv()->GetStaticObjectField (
BuildClass, getEnv()->GetStaticFieldID (BuildClass, fieldName, "Ljava/lang/String;"))));
AndroidBuild, getEnv()->GetStaticFieldID (AndroidBuild, fieldName, "Ljava/lang/String;"))));
}
}
@ -404,7 +106,7 @@ String SystemStats::getCpuModel()
return readPosixConfigFileValue ("/proc/cpuinfo", "Hardware");
}
int SystemStats::getCpuSpeedInMegaherz()
int SystemStats::getCpuSpeedInMegahertz()
{
int maxFreqKHz = 0;

View File

@ -28,6 +28,322 @@ namespace juce
live in juce_posix_SharedCode.h!
*/
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
FIELD (activityInfo, "activityInfo", "Landroid/content/pm/ActivityInfo;")
DECLARE_JNI_CLASS (AndroidResolveInfo, "android/content/pm/ResolveInfo")
#undef JNI_CLASS_MEMBERS
//==============================================================================
JavaVM* androidJNIJavaVM = nullptr;
jobject androidApkContext = nullptr;
//==============================================================================
JNIEnv* getEnv() noexcept
{
if (androidJNIJavaVM != nullptr)
{
JNIEnv* env;
androidJNIJavaVM->AttachCurrentThread (&env, nullptr);
return env;
}
// You did not call Thread::initialiseJUCE which must be called at least once in your apk
// before using any JUCE APIs. The Projucer will automatically generate java code
// which will invoke Thread::initialiseJUCE for you.
jassertfalse;
return nullptr;
}
void JNICALL juce_JavainitialiseJUCE (JNIEnv* env, jobject /*jclass*/, jobject context)
{
Thread::initialiseJUCE (env, context);
}
extern "C" jint JNIEXPORT JNI_OnLoad (JavaVM* vm, void*)
{
// Huh? JNI_OnLoad was called two times!
jassert (androidJNIJavaVM == nullptr);
androidJNIJavaVM = vm;
auto* env = getEnv();
// register the initialisation function
auto juceJavaClass = env->FindClass("com/roli/juce/Java");
if (juceJavaClass != nullptr)
{
JNINativeMethod method {"initialiseJUCE", "(Landroid/content/Context;)V",
reinterpret_cast<void*> (juce_JavainitialiseJUCE)};
auto status = env->RegisterNatives (juceJavaClass, &method, 1);
jassert (status == 0);
}
else
{
// com.roli.juce.Java class not found. Apparently this project is a library
// or was not generated by the Projucer. That's ok, the user will have to
// call Thread::initialiseJUCE manually
env->ExceptionClear();
}
JNIClassBase::initialiseAllClasses (env);
return JNI_VERSION_1_2;
}
//==============================================================================
class JuceActivityWatcher : public ActivityLifecycleCallbacks
{
public:
JuceActivityWatcher()
{
LocalRef<jobject> appContext (getAppContext());
if (appContext != nullptr)
{
auto* env = getEnv();
myself = GlobalRef (CreateJavaInterface (this, "android/app/Application$ActivityLifecycleCallbacks"));
env->CallVoidMethod (appContext.get(), AndroidApplication.registerActivityLifecycleCallbacks, myself.get());
}
checkActivityIsMain (androidApkContext);
}
~JuceActivityWatcher()
{
LocalRef<jobject> appContext (getAppContext());
if (appContext != nullptr && myself != nullptr)
{
auto* env = getEnv();
env->CallVoidMethod (appContext.get(), AndroidApplication.unregisterActivityLifecycleCallbacks, myself.get());
clear();
myself.clear();
}
}
void onActivityStarted (jobject activity) override
{
auto* env = getEnv();
checkActivityIsMain (activity);
ScopedLock lock (currentActivityLock);
if (currentActivity != nullptr)
{
// see Clarification June 2001 in JNI reference for why this is
// necessary
LocalRef<jobject> localStorage (env->NewLocalRef (currentActivity));
if (env->IsSameObject (localStorage.get(), activity) != 0)
return;
env->DeleteWeakGlobalRef (currentActivity);
currentActivity = nullptr;
}
if (activity != nullptr)
currentActivity = env->NewWeakGlobalRef (activity);
}
void onActivityStopped (jobject activity) override
{
auto* env = getEnv();
ScopedLock lock (currentActivityLock);
if (currentActivity != nullptr)
{
// important that the comparison happens in this order
// to avoid race condition where the weak reference becomes null
// just after the first check
if (env->IsSameObject (currentActivity, activity) != 0
|| env->IsSameObject (currentActivity, nullptr) != 0)
{
env->DeleteWeakGlobalRef (currentActivity);
currentActivity = nullptr;
}
}
}
LocalRef<jobject> getCurrent()
{
ScopedLock lock (currentActivityLock);
return LocalRef<jobject> (getEnv()->NewLocalRef (currentActivity));
}
LocalRef<jobject> getMain()
{
ScopedLock lock (currentActivityLock);
return LocalRef<jobject> (getEnv()->NewLocalRef (mainActivity));
}
static JuceActivityWatcher& getInstance()
{
static JuceActivityWatcher activityWatcher;
return activityWatcher;
}
private:
void checkActivityIsMain (jobject context)
{
auto* env = getEnv();
ScopedLock lock (currentActivityLock);
if (mainActivity != nullptr)
{
if (env->IsSameObject (mainActivity, nullptr) != 0)
{
env->DeleteWeakGlobalRef (mainActivity);
mainActivity = nullptr;
}
}
if (mainActivity == nullptr)
{
LocalRef<jobject> appContext (getAppContext());
auto mainActivityPath = getMainActivityClassPath();
if (mainActivityPath.isNotEmpty())
{
auto clasz = env->GetObjectClass (context);
auto activityPath = juceString (LocalRef<jstring> ((jstring) env->CallObjectMethod (clasz, JavaClass.getName)));
// This may be problematic for apps which use several activities with the same type. We just
// assume that the very first activity of this type is the main one
if (activityPath == mainActivityPath)
mainActivity = env->NewWeakGlobalRef (context);
}
}
}
static String getMainActivityClassPath()
{
static String mainActivityClassPath;
if (mainActivityClassPath.isEmpty())
{
LocalRef<jobject> appContext (getAppContext());
if (appContext != nullptr)
{
auto* env = getEnv();
LocalRef<jobject> pkgManager (env->CallObjectMethod (appContext.get(), AndroidContext.getPackageManager));
LocalRef<jstring> pkgName ((jstring) env->CallObjectMethod (appContext.get(), AndroidContext.getPackageName));
LocalRef<jobject> intent (env->NewObject (AndroidIntent, AndroidIntent.constructWithString,
javaString ("android.intent.action.MAIN").get()));
intent = LocalRef<jobject> (env->CallObjectMethod (intent.get(),
AndroidIntent.setPackage,
pkgName.get()));
LocalRef<jobject> resolveInfo (env->CallObjectMethod (pkgManager.get(), AndroidPackageManager.resolveActivity, intent.get(), 0));
if (resolveInfo != nullptr)
{
LocalRef<jobject> activityInfo (env->GetObjectField (resolveInfo.get(), AndroidResolveInfo.activityInfo));
LocalRef<jstring> jName ((jstring) env->GetObjectField (activityInfo.get(), AndroidPackageItemInfo.name));
LocalRef<jstring> jPackage ((jstring) env->GetObjectField (activityInfo.get(), AndroidPackageItemInfo.packageName));
mainActivityClassPath = juceString (jName);
}
}
}
return mainActivityClassPath;
}
GlobalRef myself;
CriticalSection currentActivityLock;
jweak currentActivity = nullptr;
jweak mainActivity = nullptr;
};
//==============================================================================
#if JUCE_MODULE_AVAILABLE_juce_events && JUCE_ANDROID
void juce_juceEventsAndroidStartApp();
#endif
void Thread::initialiseJUCE (void* jniEnv, void* context)
{
static CriticalSection cs;
ScopedLock lock (cs);
// jniEnv and context should not be null!
jassert (jniEnv != nullptr && context != nullptr);
auto* env = static_cast<JNIEnv*> (jniEnv);
if (androidJNIJavaVM == nullptr)
{
JavaVM* javaVM = nullptr;
auto status = env->GetJavaVM (&javaVM);
jassert (status == 0 && javaVM != nullptr);
androidJNIJavaVM = javaVM;
}
static bool firstCall = true;
if (firstCall)
{
firstCall = false;
// if we ever support unloading then this should probably be a weak reference
androidApkContext = env->NewGlobalRef (static_cast<jobject> (context));
JuceActivityWatcher::getInstance();
#if JUCE_MODULE_AVAILABLE_juce_events && JUCE_ANDROID
juce_juceEventsAndroidStartApp();
#endif
}
}
//==============================================================================
LocalRef<jobject> getAppContext() noexcept
{
auto* env = getEnv();
auto context = androidApkContext;
// You did not call Thread::initialiseJUCE which must be called at least once in your apk
// before using any JUCE APIs. The Projucer will automatically generate java code
// which will invoke Thread::initialiseJUCE for you.
jassert (env != nullptr && context != nullptr);
if (context == nullptr)
return LocalRef<jobject>();
if (env->IsInstanceOf (context, AndroidApplication) != 0)
return LocalRef<jobject> (env->NewLocalRef (context));
LocalRef<jobject> applicationContext (env->CallObjectMethod (context, AndroidContext.getApplicationContext));
if (applicationContext == nullptr)
return LocalRef<jobject> (env->NewLocalRef (context));
return applicationContext;
}
LocalRef<jobject> getCurrentActivity() noexcept
{
return JuceActivityWatcher::getInstance().getCurrent();
}
LocalRef<jobject> getMainActivity() noexcept
{
return JuceActivityWatcher::getInstance().getMain();
}
//==============================================================================
// sets the process to 0=low priority, 1=normal, 2=high, 3=realtime
JUCE_API void JUCE_CALLTYPE Process::setPriority (ProcessPriority prior)
@ -74,4 +390,6 @@ JUCE_API bool JUCE_CALLTYPE juce_isRunningUnderDebugger() noexcept
JUCE_API void JUCE_CALLTYPE Process::raisePrivilege() {}
JUCE_API void JUCE_CALLTYPE Process::lowerPrivilege() {}
} // namespace juce

View File

@ -23,6 +23,90 @@
namespace juce
{
struct CURLSymbols
{
CURL* (*curl_easy_init) (void);
CURLcode (*curl_easy_setopt) (CURL *curl, CURLoption option, ...);
void (*curl_easy_cleanup) (CURL *curl);
CURLcode (*curl_easy_getinfo) (CURL *curl, CURLINFO info, ...);
CURLMcode (*curl_multi_add_handle) (CURLM *multi_handle, CURL *curl_handle);
CURLMcode (*curl_multi_cleanup) (CURLM *multi_handle);
CURLMcode (*curl_multi_fdset) (CURLM *multi_handle, fd_set *read_fd_set, fd_set *write_fd_set, fd_set *exc_fd_set, int *max_fd);
CURLMsg* (*curl_multi_info_read) (CURLM *multi_handle, int *msgs_in_queue);
CURLM* (*curl_multi_init) (void);
CURLMcode (*curl_multi_perform) (CURLM *multi_handle, int *running_handles);
CURLMcode (*curl_multi_remove_handle) (CURLM *multi_handle, CURL *curl_handle);
CURLMcode (*curl_multi_timeout) (CURLM *multi_handle, long *milliseconds);
struct curl_slist* (*curl_slist_append) (struct curl_slist *, const char *);
void (*curl_slist_free_all) (struct curl_slist *);
curl_version_info_data* (*curl_version_info) (CURLversion);
static std::unique_ptr<CURLSymbols> create()
{
std::unique_ptr<CURLSymbols> symbols (new CURLSymbols);
#if JUCE_LOAD_CURL_SYMBOLS_LAZILY
const ScopedLock sl (getLibcurlLock());
#define JUCE_INIT_CURL_SYMBOL(name) if (! symbols->loadSymbol (symbols->name, #name)) return nullptr;
#else
#define JUCE_INIT_CURL_SYMBOL(name) symbols->name = ::name;
#endif
JUCE_INIT_CURL_SYMBOL (curl_easy_init)
JUCE_INIT_CURL_SYMBOL (curl_easy_setopt)
JUCE_INIT_CURL_SYMBOL (curl_easy_cleanup)
JUCE_INIT_CURL_SYMBOL (curl_easy_getinfo)
JUCE_INIT_CURL_SYMBOL (curl_multi_add_handle)
JUCE_INIT_CURL_SYMBOL (curl_multi_cleanup)
JUCE_INIT_CURL_SYMBOL (curl_multi_fdset)
JUCE_INIT_CURL_SYMBOL (curl_multi_info_read)
JUCE_INIT_CURL_SYMBOL (curl_multi_init)
JUCE_INIT_CURL_SYMBOL (curl_multi_perform)
JUCE_INIT_CURL_SYMBOL (curl_multi_remove_handle)
JUCE_INIT_CURL_SYMBOL (curl_multi_timeout)
JUCE_INIT_CURL_SYMBOL (curl_slist_append)
JUCE_INIT_CURL_SYMBOL (curl_slist_free_all)
JUCE_INIT_CURL_SYMBOL (curl_version_info)
return symbols;
}
// liburl's curl_multi_init calls curl_global_init which is not thread safe
// so we need to get a lock during calls to curl_multi_init and curl_multi_cleanup
static CriticalSection& getLibcurlLock() noexcept
{
static CriticalSection cs;
return cs;
}
private:
CURLSymbols() = default;
#if JUCE_LOAD_CURL_SYMBOLS_LAZILY
static DynamicLibrary& getLibcurl()
{
const ScopedLock sl (getLibcurlLock());
static DynamicLibrary libcurl;
if (libcurl.getNativeHandle() == nullptr)
for (auto libName : { "libcurl.so", "libcurl.so.4", "libcurl.so.3" })
if (libcurl.open (libName))
break;
return libcurl;
}
template <typename FuncPtr>
bool loadSymbol (FuncPtr& dst, const char* name)
{
dst = reinterpret_cast<FuncPtr> (getLibcurl().getFunction (name));
return (dst != nullptr);
}
#endif
};
//==============================================================================
class WebInputStream::Pimpl
{
public:
@ -30,14 +114,19 @@ public:
: owner (ownerStream), url (urlToCopy), isPost (shouldUsePost),
httpRequest (isPost ? "POST" : "GET")
{
multi = curl_multi_init();
jassert (symbols); // Unable to load libcurl!
{
const ScopedLock sl (CURLSymbols::getLibcurlLock());
multi = symbols->curl_multi_init();
}
if (multi != nullptr)
{
curl = curl_easy_init();
curl = symbols->curl_easy_init();
if (curl != nullptr)
if (curl_multi_add_handle (multi, curl) == CURLM_OK)
if (symbols->curl_multi_add_handle (multi, curl) == CURLM_OK)
return;
}
@ -100,24 +189,25 @@ public:
void cleanup()
{
const ScopedLock lock (cleanupLock);
const ScopedLock sl (CURLSymbols::getLibcurlLock());
if (curl != nullptr)
{
curl_multi_remove_handle (multi, curl);
symbols->curl_multi_remove_handle (multi, curl);
if (headerList != nullptr)
{
curl_slist_free_all (headerList);
symbols->curl_slist_free_all (headerList);
headerList = nullptr;
}
curl_easy_cleanup (curl);
symbols->curl_easy_cleanup (curl);
curl = nullptr;
}
if (multi != nullptr)
{
curl_multi_cleanup (multi);
symbols->curl_multi_cleanup (multi);
multi = nullptr;
}
}
@ -132,7 +222,7 @@ public:
{
auto address = url.toString (! isPost);
curl_version_info_data* data = curl_version_info (CURLVERSION_NOW);
curl_version_info_data* data = symbols->curl_version_info (CURLVERSION_NOW);
jassert (data != nullptr);
if (! requestHeaders.endsWithChar ('\n'))
@ -146,22 +236,22 @@ public:
auto userAgent = String ("curl/") + data->version;
if (curl_easy_setopt (curl, CURLOPT_URL, address.toRawUTF8()) == CURLE_OK
&& curl_easy_setopt (curl, CURLOPT_WRITEDATA, this) == CURLE_OK
&& curl_easy_setopt (curl, CURLOPT_WRITEFUNCTION, StaticCurlWrite) == CURLE_OK
&& curl_easy_setopt (curl, CURLOPT_NOSIGNAL, 1) == CURLE_OK
&& curl_easy_setopt (curl, CURLOPT_MAXREDIRS, static_cast<long> (maxRedirects)) == CURLE_OK
&& curl_easy_setopt (curl, CURLOPT_USERAGENT, userAgent.toRawUTF8()) == CURLE_OK
&& curl_easy_setopt (curl, CURLOPT_FOLLOWLOCATION, (maxRedirects > 0 ? 1 : 0)) == CURLE_OK)
if (symbols->curl_easy_setopt (curl, CURLOPT_URL, address.toRawUTF8()) == CURLE_OK
&& symbols->curl_easy_setopt (curl, CURLOPT_WRITEDATA, this) == CURLE_OK
&& symbols->curl_easy_setopt (curl, CURLOPT_WRITEFUNCTION, StaticCurlWrite) == CURLE_OK
&& symbols->curl_easy_setopt (curl, CURLOPT_NOSIGNAL, 1) == CURLE_OK
&& symbols->curl_easy_setopt (curl, CURLOPT_MAXREDIRS, static_cast<long> (maxRedirects)) == CURLE_OK
&& symbols->curl_easy_setopt (curl, CURLOPT_USERAGENT, userAgent.toRawUTF8()) == CURLE_OK
&& symbols->curl_easy_setopt (curl, CURLOPT_FOLLOWLOCATION, (maxRedirects > 0 ? 1 : 0)) == CURLE_OK)
{
if (isPost)
{
if (curl_easy_setopt (curl, CURLOPT_READDATA, this) != CURLE_OK
|| curl_easy_setopt (curl, CURLOPT_READFUNCTION, StaticCurlRead) != CURLE_OK)
if (symbols->curl_easy_setopt (curl, CURLOPT_READDATA, this) != CURLE_OK
|| symbols->curl_easy_setopt (curl, CURLOPT_READFUNCTION, StaticCurlRead) != CURLE_OK)
return false;
if (curl_easy_setopt (curl, CURLOPT_POST, 1) != CURLE_OK
|| curl_easy_setopt (curl, CURLOPT_POSTFIELDSIZE_LARGE, static_cast<curl_off_t> (headersAndPostData.getSize())) != CURLE_OK)
if (symbols->curl_easy_setopt (curl, CURLOPT_POST, 1) != CURLE_OK
|| symbols->curl_easy_setopt (curl, CURLOPT_POSTFIELDSIZE_LARGE, static_cast<curl_off_t> (headersAndPostData.getSize())) != CURLE_OK)
return false;
}
@ -169,20 +259,20 @@ public:
bool hasSpecialRequestCmd = isPost ? (httpRequest != "POST") : (httpRequest != "GET");
if (hasSpecialRequestCmd)
if (curl_easy_setopt (curl, CURLOPT_CUSTOMREQUEST, httpRequest.toRawUTF8()) != CURLE_OK)
if (symbols->curl_easy_setopt (curl, CURLOPT_CUSTOMREQUEST, httpRequest.toRawUTF8()) != CURLE_OK)
return false;
if (curl_easy_setopt (curl, CURLOPT_HEADERDATA, this) != CURLE_OK
|| curl_easy_setopt (curl, CURLOPT_HEADERFUNCTION, StaticCurlHeader) != CURLE_OK)
if (symbols->curl_easy_setopt (curl, CURLOPT_HEADERDATA, this) != CURLE_OK
|| symbols->curl_easy_setopt (curl, CURLOPT_HEADERFUNCTION, StaticCurlHeader) != CURLE_OK)
return false;
if (timeOutMs > 0)
{
auto timeOutSecs = ((long) timeOutMs + 999) / 1000;
if (curl_easy_setopt (curl, CURLOPT_CONNECTTIMEOUT, timeOutSecs) != CURLE_OK
|| curl_easy_setopt (curl, CURLOPT_LOW_SPEED_LIMIT, 100) != CURLE_OK
|| curl_easy_setopt (curl, CURLOPT_LOW_SPEED_TIME, timeOutSecs) != CURLE_OK)
if (symbols->curl_easy_setopt (curl, CURLOPT_CONNECTTIMEOUT, timeOutSecs) != CURLE_OK
|| symbols->curl_easy_setopt (curl, CURLOPT_LOW_SPEED_LIMIT, 100) != CURLE_OK
|| symbols->curl_easy_setopt (curl, CURLOPT_LOW_SPEED_TIME, timeOutSecs) != CURLE_OK)
return false;
}
@ -212,10 +302,10 @@ public:
// fromLines will always return at least one line if the string is not empty
jassert (headerLines.size() > 0);
headerList = curl_slist_append (headerList, headerLines [0].toRawUTF8());
headerList = symbols->curl_slist_append (headerList, headerLines [0].toRawUTF8());
for (int i = 1; (i < headerLines.size() && headerList != nullptr); ++i)
headerList = curl_slist_append (headerList, headerLines [i].toRawUTF8());
headerList = symbols->curl_slist_append (headerList, headerLines [i].toRawUTF8());
if (headerList == nullptr)
{
@ -223,7 +313,7 @@ public:
return false;
}
if (curl_easy_setopt (curl, CURLOPT_HTTPHEADER, headerList) != CURLE_OK)
if (symbols->curl_easy_setopt (curl, CURLOPT_HTTPHEADER, headerList) != CURLE_OK)
{
cleanup();
return false;
@ -272,12 +362,12 @@ public:
return false;
long responseCode;
if (curl_easy_getinfo (curl, CURLINFO_RESPONSE_CODE, &responseCode) == CURLE_OK)
if (symbols->curl_easy_getinfo (curl, CURLINFO_RESPONSE_CODE, &responseCode) == CURLE_OK)
statusCode = static_cast<int> (responseCode);
// get content length size
double curlLength;
if (curl_easy_getinfo (curl, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &curlLength) == CURLE_OK)
if (symbols->curl_easy_getinfo (curl, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &curlLength) == CURLE_OK)
contentLength = static_cast<int64> (curlLength);
}
@ -295,7 +385,7 @@ public:
{
int cnt = 0;
if (CURLMsg* msg = curl_multi_info_read (multi, &cnt))
if (CURLMsg* msg = symbols->curl_multi_info_read (multi, &cnt))
{
if (msg->msg == CURLMSG_DONE && msg->easy_handle == curl)
{
@ -328,7 +418,7 @@ public:
if (multi == nullptr)
return;
if ((lastError = (int) curl_multi_timeout (multi, &curl_timeo)) != CURLM_OK)
if ((lastError = (int) symbols->curl_multi_timeout (multi, &curl_timeo)) != CURLM_OK)
return;
}
@ -350,7 +440,7 @@ public:
if (multi == nullptr)
return;
if ((lastError = (int) curl_multi_fdset (multi, &fdread, &fdwrite, &fdexcep, &maxfd)) != CURLM_OK)
if ((lastError = (int) symbols->curl_multi_fdset (multi, &fdread, &fdwrite, &fdexcep, &maxfd)) != CURLM_OK)
return;
}
@ -374,7 +464,7 @@ public:
{
const ScopedLock lock (cleanupLock);
while ((curlRet = (int) curl_multi_perform (multi, &still_running)) == CURLM_CALL_MULTI_PERFORM)
while ((curlRet = (int) symbols->curl_multi_perform (multi, &still_running)) == CURLM_CALL_MULTI_PERFORM)
{}
}
@ -510,6 +600,7 @@ public:
//==============================================================================
WebInputStream& owner;
const URL url;
std::unique_ptr<CURLSymbols> symbols { CURLSymbols::create() };
//==============================================================================
// curl stuff

View File

@ -189,29 +189,27 @@ static bool isFileExecutable (const String& filename)
bool Process::openDocument (const String& fileName, const String& parameters)
{
String cmdString (fileName.replace (" ", "\\ ",false));
auto cmdString = fileName.replace (" ", "\\ ", false);
cmdString << " " << parameters;
if (/*URL::isProbablyAWebsiteURL (fileName)
||*/ cmdString.startsWithIgnoreCase ("file:")
/*|| URL::isProbablyAnEmailAddress (fileName)*/
if (cmdString.startsWithIgnoreCase ("file:")
|| File::createFileWithoutCheckingPath (fileName).isDirectory()
|| ! isFileExecutable (fileName))
{
// create a command that tries to launch a bunch of likely browsers
static const char* const browserNames[] = { "xdg-open", "/etc/alternatives/x-www-browser", "firefox", "mozilla",
"google-chrome", "chromium-browser", "opera", "konqueror" };
StringArray cmdLines;
for (int i = 0; i < numElementsInArray (browserNames); ++i)
cmdLines.add (String (browserNames[i]) + " " + cmdString.trim().quoted());
for (auto browserName : { "xdg-open", "/etc/alternatives/x-www-browser", "firefox", "mozilla",
"google-chrome", "chromium-browser", "opera", "konqueror" })
{
cmdLines.add (String (browserName) + " " + cmdString.trim());
}
cmdString = cmdLines.joinIntoString (" || ");
}
const char* const argv[4] = { "/bin/sh", "-c", cmdString.toUTF8(), 0 };
const int cpid = fork();
auto cpid = fork();
if (cpid == 0)
{

View File

@ -80,7 +80,7 @@ String SystemStats::getCpuModel()
return getCpuInfo ("model name");
}
int SystemStats::getCpuSpeedInMegaherz()
int SystemStats::getCpuSpeedInMegahertz()
{
return roundToInt (getCpuInfo ("cpu MHz").getFloatValue());
}
@ -142,16 +142,26 @@ String SystemStats::getDisplayLanguage() { return getUserLanguage() + "-" + getU
void CPUInformation::initialise() noexcept
{
auto flags = getCpuInfo ("flags");
hasMMX = flags.contains ("mmx");
hasSSE = flags.contains ("sse");
hasSSE2 = flags.contains ("sse2");
hasSSE3 = flags.contains ("sse3");
has3DNow = flags.contains ("3dnow");
hasSSSE3 = flags.contains ("ssse3");
hasSSE41 = flags.contains ("sse4_1");
hasSSE42 = flags.contains ("sse4_2");
hasAVX = flags.contains ("avx");
hasAVX2 = flags.contains ("avx2");
hasMMX = flags.contains ("mmx");
hasSSE = flags.contains ("sse");
hasSSE2 = flags.contains ("sse2");
hasSSE3 = flags.contains ("sse3");
has3DNow = flags.contains ("3dnow");
hasSSSE3 = flags.contains ("ssse3");
hasSSE41 = flags.contains ("sse4_1");
hasSSE42 = flags.contains ("sse4_2");
hasAVX = flags.contains ("avx");
hasAVX2 = flags.contains ("avx2");
hasAVX512F = flags.contains ("avx512f");
hasAVX512BW = flags.contains ("avx512bw");
hasAVX512CD = flags.contains ("avx512cd");
hasAVX512DQ = flags.contains ("avx512dq");
hasAVX512ER = flags.contains ("avx512er");
hasAVX512IFMA = flags.contains ("avx512ifma");
hasAVX512PF = flags.contains ("avx512pf");
hasAVX512VBMI = flags.contains ("avx512vbmi");
hasAVX512VL = flags.contains ("avx512vl");
hasAVX512VPOPCNTDQ = flags.contains ("avx512_vpopcntdq");
numLogicalCPUs = getCpuInfo ("processor").getIntValue() + 1;

View File

@ -65,7 +65,7 @@ namespace MacFileHelpers
{
const String type (buf.f_fstypename);
while (*types != 0)
while (*types != nullptr)
if (type.equalsIgnoreCase (*types++))
return true;
}
@ -105,14 +105,14 @@ namespace MacFileHelpers
#else
static bool launchExecutable (const String& pathAndArguments)
{
const char* const argv[4] = { "/bin/sh", "-c", pathAndArguments.toUTF8(), 0 };
const int cpid = fork();
auto cpid = fork();
if (cpid == 0)
{
const char* const argv[4] = { "/bin/sh", "-c", pathAndArguments.toUTF8(), nullptr };
// Child process
if (execve (argv[0], (char**) argv, 0) < 0)
if (execve (argv[0], (char**) argv, nullptr) < 0)
exit (0);
}
else
@ -412,10 +412,8 @@ bool JUCE_CALLTYPE Process::openDocument (const String& fileName, const String&
JUCE_AUTORELEASEPOOL
{
NSString* fileNameAsNS (juceStringToNS (fileName));
NSURL* filenameAsURL ([NSURL URLWithString: fileNameAsNS]);
if (filenameAsURL == nil)
filenameAsURL = [NSURL fileURLWithPath: fileNameAsNS];
NSURL* filenameAsURL = File::createFileWithoutCheckingPath (fileName).exists() ? [NSURL fileURLWithPath: fileNameAsNS]
: [NSURL URLWithString: fileNameAsNS];
#if JUCE_IOS
ignoreUnused (parameters);
@ -442,11 +440,13 @@ bool JUCE_CALLTYPE Process::openDocument (const String& fileName, const String&
StringArray params;
params.addTokens (parameters, true);
NSMutableArray* paramArray = [[[NSMutableArray alloc] init] autorelease];
NSMutableDictionary* dict = [[NSMutableDictionary new] autorelease];
NSMutableArray* paramArray = [[NSMutableArray new] autorelease];
for (int i = 0; i < params.size(); ++i)
[paramArray addObject: juceStringToNS (params[i])];
NSMutableDictionary* dict = [[[NSMutableDictionary alloc] init] autorelease];
[dict setObject: paramArray
forKey: nsStringLiteral ("NSWorkspaceLaunchConfigurationArguments")];

View File

@ -32,12 +32,12 @@ void MACAddress::findAllAddresses (Array<MACAddress>& result)
for (const ifaddrs* cursor = addrs; cursor != nullptr; cursor = cursor->ifa_next)
{
// Required to avoid misaligned pointer access
sockaddr_storage sto;
std::memcpy (&sto, cursor->ifa_addr, sizeof (sockaddr_storage));
sockaddr sto;
std::memcpy (&sto, cursor->ifa_addr, sizeof (sockaddr));
if (sto.ss_family == AF_LINK)
if (sto.sa_family == AF_LINK)
{
auto sadd = (const sockaddr_dl*) cursor->ifa_addr;
auto sadd = reinterpret_cast<const sockaddr_dl*> (cursor->ifa_addr);
#ifndef IFT_ETHER
enum { IFT_ETHER = 6 };
@ -127,7 +127,7 @@ public:
DelegateClass::setState (delegate, this);
}
~URLConnectionState()
~URLConnectionState() override
{
signalThreadShouldExit();
@ -331,7 +331,8 @@ public:
NSMutableData* data = nil;
NSDictionary* headers = nil;
int statusCode = 0;
bool initialised = false, hasFailed = false, hasFinished = false, isBeingDeleted = false;
std::atomic<bool> initialised { false };
bool hasFailed = false, hasFinished = false, isBeingDeleted = false;
const int numRedirectsToFollow;
int numRedirects = 0;
int64 latestTotalBytes = 0;
@ -413,9 +414,10 @@ struct BackgroundDownloadTask : public URL::DownloadTask
String extraHeadersToUse,
URL::DownloadTask::Listener* listenerToUse,
bool shouldUsePostRequest)
: targetLocation (targetLocationToUse), listener (listenerToUse),
: listener (listenerToUse),
uniqueIdentifier (String (urlToUse.toString (true).hashCode64()) + String (Random().nextInt64()))
{
targetLocation = targetLocationToUse;
downloaded = -1;
static DelegateClass cls;
@ -488,7 +490,6 @@ struct BackgroundDownloadTask : public URL::DownloadTask
}
//==============================================================================
File targetLocation;
URL::DownloadTask::Listener* listener;
NSObject<NSURLSessionDelegate>* delegate = nil;
NSURLSession* session = nil;
@ -691,7 +692,7 @@ public:
DelegateClass::setState (delegate, this);
}
~URLConnectionState()
~URLConnectionState() override
{
stop();
@ -808,7 +809,8 @@ public:
{
DBG (nsStringToJuce ([error description])); ignoreUnused (error);
nsUrlErrorCode = [error code];
hasFailed = initialised = true;
hasFailed = true;
initialised = true;
signalThreadShouldExit();
}
@ -826,7 +828,8 @@ public:
void finishedLoading()
{
hasFinished = initialised = true;
hasFinished = true;
initialised = true;
signalThreadShouldExit();
}
@ -860,7 +863,7 @@ public:
NSDictionary* headers = nil;
NSInteger nsUrlErrorCode = 0;
int statusCode = 0;
bool initialised = false, hasFailed = false, hasFinished = false;
std::atomic<bool> initialised { false }, hasFailed { false }, hasFinished { false };
const int numRedirectsToFollow;
int numRedirects = 0;
int latestTotalBytes = 0;
@ -962,6 +965,9 @@ public:
createConnection();
}
if (connection == nullptr)
return false;
if (! connection->start (owner, webInputListener))
{
// Workaround for deployment targets below 10.10 where HTTPS POST requests with keep-alive fail with the NSURLErrorNetworkConnectionLost error code.
@ -977,7 +983,7 @@ public:
return false;
}
if (connection != nullptr && connection->headers != nil)
if (connection->headers != nil)
{
statusCode = connection->statusCode;
@ -1093,38 +1099,44 @@ private:
{
jassert (connection == nullptr);
if (NSMutableURLRequest* req = [NSMutableURLRequest requestWithURL: [NSURL URLWithString: juceStringToNS (url.toString (! isPost))]
cachePolicy: NSURLRequestReloadIgnoringLocalCacheData
timeoutInterval: timeOutMs <= 0 ? 60.0 : (timeOutMs / 1000.0)])
if (NSURL* nsURL = [NSURL URLWithString: juceStringToNS (url.toString (! isPost))])
{
[req setHTTPMethod: [NSString stringWithUTF8String: httpRequestCmd.toRawUTF8()]];
if (isPost)
if (NSMutableURLRequest* req = [NSMutableURLRequest requestWithURL: nsURL
cachePolicy: NSURLRequestReloadIgnoringLocalCacheData
timeoutInterval: timeOutMs <= 0 ? 60.0 : (timeOutMs / 1000.0)])
{
WebInputStream::createHeadersAndPostData (url, headers, postData);
if (NSString* httpMethod = [NSString stringWithUTF8String: httpRequestCmd.toRawUTF8()])
{
[req setHTTPMethod: httpMethod];
if (postData.getSize() > 0)
[req setHTTPBody: [NSData dataWithBytes: postData.getData()
length: postData.getSize()]];
if (isPost)
{
WebInputStream::createHeadersAndPostData (url, headers, postData);
if (postData.getSize() > 0)
[req setHTTPBody: [NSData dataWithBytes: postData.getData()
length: postData.getSize()]];
}
StringArray headerLines;
headerLines.addLines (headers);
headerLines.removeEmptyStrings (true);
for (int i = 0; i < headerLines.size(); ++i)
{
auto key = headerLines[i].upToFirstOccurrenceOf (":", false, false).trim();
auto value = headerLines[i].fromFirstOccurrenceOf (":", false, false).trim();
if (key.isNotEmpty() && value.isNotEmpty())
[req addValue: juceStringToNS (value) forHTTPHeaderField: juceStringToNS (key)];
}
// Workaround for an Apple bug. See https://github.com/AFNetworking/AFNetworking/issues/2334
[req HTTPBody];
connection.reset (new URLConnectionState (req, numRedirectsToFollow));
}
}
StringArray headerLines;
headerLines.addLines (headers);
headerLines.removeEmptyStrings (true);
for (int i = 0; i < headerLines.size(); ++i)
{
String key = headerLines[i].upToFirstOccurrenceOf (":", false, false).trim();
String value = headerLines[i].fromFirstOccurrenceOf (":", false, false).trim();
if (key.isNotEmpty() && value.isNotEmpty())
[req addValue: juceStringToNS (value) forHTTPHeaderField: juceStringToNS (key)];
}
// Workaround for an Apple bug. See https://github.com/AFNetworking/AFNetworking/issues/2334
[req HTTPBody];
connection.reset (new URLConnectionState (req, numRedirectsToFollow));
}
}

View File

@ -25,7 +25,7 @@ namespace juce
String String::fromCFString (CFStringRef cfString)
{
if (cfString == 0)
if (cfString == nullptr)
return {};
CFRange range = { 0, CFStringGetLength (cfString) };
@ -72,7 +72,7 @@ String String::convertToPrecomposedUnicode() const
map.mappingVersion = kUnicodeUseLatestMapping;
UnicodeToTextInfo conversionInfo = 0;
UnicodeToTextInfo conversionInfo = {};
String result;
if (CreateUnicodeToTextInfo (&map, &conversionInfo) == noErr)
@ -88,11 +88,11 @@ String String::convertToPrecomposedUnicode() const
if (ConvertFromUnicodeToText (conversionInfo,
bytesNeeded, (ConstUniCharArrayPtr) toUTF16().getAddress(),
kUnicodeDefaultDirectionMask,
0, 0, 0, 0,
0, {}, {}, {},
bytesNeeded, &bytesRead,
&outputBufferSize, tempOut) == noErr)
{
result = String (CharPointer_UTF16 ((CharPointer_UTF16::CharType*) tempOut.get()));
result = String (CharPointer_UTF16 (reinterpret_cast<CharPointer_UTF16::CharType*> (tempOut.get())));
}
DisposeUnicodeToTextInfo (&conversionInfo);

View File

@ -51,14 +51,17 @@ namespace SystemStatsHelpers
{
uint32 la = a, lb = b, lc = c, ld = d;
asm ("mov %%ebx, %%esi \n\t"
"cpuid \n\t"
"xchg %%esi, %%ebx"
: "=a" (la), "=S" (lb), "=c" (lc), "=d" (ld) : "a" (type)
#if JUCE_64BIT
, "b" (lb), "c" (lc), "d" (ld)
#endif
);
#if JUCE_32BIT && defined (__pic__)
asm ("mov %%ebx, %%edi\n"
"cpuid\n"
"xchg %%edi, %%ebx\n"
: "=a" (la), "=D" (lb), "=c" (lc), "=d" (ld)
: "a" (type), "c" (0));
#else
asm ("cpuid\n"
: "=a" (la), "=b" (lb), "=c" (lc), "=d" (ld)
: "a" (type), "c" (0));
#endif
a = la; b = lb; c = lc; d = ld;
}
@ -83,7 +86,17 @@ void CPUInformation::initialise() noexcept
hasAVX = (c & (1u << 28)) != 0;
SystemStatsHelpers::doCPUID (a, b, c, d, 7);
hasAVX2 = (b & (1u << 5)) != 0;
hasAVX2 = (b & (1u << 5)) != 0;
hasAVX512F = (b & (1u << 16)) != 0;
hasAVX512DQ = (b & (1u << 17)) != 0;
hasAVX512IFMA = (b & (1u << 21)) != 0;
hasAVX512PF = (b & (1u << 26)) != 0;
hasAVX512ER = (b & (1u << 27)) != 0;
hasAVX512CD = (b & (1u << 28)) != 0;
hasAVX512BW = (b & (1u << 30)) != 0;
hasAVX512VL = (b & (1u << 31)) != 0;
hasAVX512VBMI = (c & (1u << 1)) != 0;
hasAVX512VPOPCNTDQ = (c & (1u << 14)) != 0;
#endif
numLogicalCPUs = (int) [[NSProcessInfo processInfo] activeProcessorCount];
@ -140,17 +153,20 @@ String SystemStats::getOperatingSystemName()
String SystemStats::getDeviceDescription()
{
#if JUCE_IOS
return nsStringToJuce ([[UIDevice currentDevice] model]);
const char* name = "hw.machine";
#else
const char* name = "hw.model";
#endif
size_t size;
if (sysctlbyname ("hw.model", nullptr, &size, nullptr, 0) >= 0)
if (sysctlbyname (name, nullptr, &size, nullptr, 0) >= 0)
{
HeapBlock<char> model (size);
if (sysctlbyname ("hw.model", model, &size, nullptr, 0) >= 0)
if (sysctlbyname (name, model, &size, nullptr, 0) >= 0)
return model.get();
}
return {};
#endif
}
String SystemStats::getDeviceManufacturer()
@ -174,7 +190,7 @@ int SystemStats::getMemorySizeInMegabytes()
uint64 mem = 0;
size_t memSize = sizeof (mem);
int mib[] = { CTL_HW, HW_MEMSIZE };
sysctl (mib, 2, &mem, &memSize, 0, 0);
sysctl (mib, 2, &mem, &memSize, nullptr, 0);
return (int) (mem / (1024 * 1024));
}
@ -203,12 +219,12 @@ String SystemStats::getCpuModel()
return {};
}
int SystemStats::getCpuSpeedInMegaherz()
int SystemStats::getCpuSpeedInMegahertz()
{
uint64 speedHz = 0;
size_t speedSize = sizeof (speedHz);
int mib[] = { CTL_HW, HW_CPU_FREQ };
sysctl (mib, 2, &speedHz, &speedSize, 0, 0);
sysctl (mib, 2, &speedHz, &speedSize, nullptr, 0);
#if JUCE_BIG_ENDIAN
if (speedSize == 4)

View File

@ -86,7 +86,7 @@ JUCE_API bool JUCE_CALLTYPE juce_isRunningUnderDebugger() noexcept
struct kinfo_proc info;
int m[] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, getpid() };
size_t sz = sizeof (info);
sysctl (m, 4, &info, &sz, 0, 0);
sysctl (m, 4, &info, &sz, nullptr, 0);
return (info.kp_proc.p_flag & P_TRACED) != 0;
}

View File

@ -59,7 +59,7 @@ static inline NSURL* createNSURLFromFile (const File& f)
static inline NSArray* createNSArrayFromStringArray (const StringArray& strings)
{
auto* array = [[NSMutableArray alloc] init];
auto array = [[NSMutableArray alloc] init];
for (auto string: strings)
[array addObject:juceStringToNS (string)];
@ -71,7 +71,7 @@ static NSArray* varArrayToNSArray (const var& varToParse);
static NSDictionary* varObjectToNSDictionary (const var& varToParse)
{
auto* dictionary = [NSMutableDictionary dictionary];
auto dictionary = [NSMutableDictionary dictionary];
if (varToParse.isObject())
{
@ -118,7 +118,7 @@ static NSArray* varArrayToNSArray (const var& varToParse)
const auto* varArray = varToParse.getArray();
auto* array = [NSMutableArray arrayWithCapacity: (NSUInteger) varArray->size()];
auto array = [NSMutableArray arrayWithCapacity: (NSUInteger) varArray->size()];
for (const auto& aVar : *varArray)
{
@ -145,29 +145,14 @@ static NSArray* varArrayToNSArray (const var& varToParse)
return array;
}
static var nsArrayToVar (NSArray* array);
static var nsObjectToVar (NSObject* array);
static var nsDictionaryToVar (NSDictionary* dictionary)
{
DynamicObject::Ptr dynamicObject = new DynamicObject();
DynamicObject::Ptr dynamicObject (new DynamicObject());
for (NSString* key in dictionary)
{
const auto keyString = nsStringToJuce (key);
id value = dictionary[key];
if ([value isKindOfClass: [NSString class]])
dynamicObject->setProperty (keyString, nsStringToJuce ((NSString*) value));
else if ([value isKindOfClass: [NSNumber class]])
dynamicObject->setProperty (keyString, nsStringToJuce ([(NSNumber*) value stringValue]));
else if ([value isKindOfClass: [NSDictionary class]])
dynamicObject->setProperty (keyString, nsDictionaryToVar ((NSDictionary*) value));
else if ([value isKindOfClass: [NSArray class]])
dynamicObject->setProperty (keyString, nsArrayToVar ((NSArray*) value));
else
jassertfalse; // Unsupported yet, add here!
}
dynamicObject->setProperty (nsStringToJuce (key), nsObjectToVar (dictionary[key]));
return var (dynamicObject.get());
}
@ -177,22 +162,26 @@ static var nsArrayToVar (NSArray* array)
Array<var> resultArray;
for (id value in array)
{
if ([value isKindOfClass: [NSString class]])
resultArray.add (var (nsStringToJuce ((NSString*) value)));
else if ([value isKindOfClass: [NSNumber class]])
resultArray.add (var (nsStringToJuce ([(NSNumber*) value stringValue])));
else if ([value isKindOfClass: [NSDictionary class]])
resultArray.add (nsDictionaryToVar ((NSDictionary*) value));
else if ([value isKindOfClass: [NSArray class]])
resultArray.add (nsArrayToVar ((NSArray*) value));
else
jassertfalse; // Unsupported yet, add here!
}
resultArray.add (nsObjectToVar (value));
return var (resultArray);
}
static var nsObjectToVar (NSObject* obj)
{
if ([obj isKindOfClass: [NSString class]]) return nsStringToJuce ((NSString*) obj);
else if ([obj isKindOfClass: [NSNumber class]]) return nsStringToJuce ([(NSNumber*) obj stringValue]);
else if ([obj isKindOfClass: [NSDictionary class]]) return nsDictionaryToVar ((NSDictionary*) obj);
else if ([obj isKindOfClass: [NSArray class]]) return nsArrayToVar ((NSArray*) obj);
else
{
// Unsupported yet, add here!
jassertfalse;
}
return {};
}
#if JUCE_MAC
template <typename RectangleType>
static NSRect makeNSRect (const RectangleType& r) noexcept
@ -211,7 +200,7 @@ static NSRect makeNSRect (const RectangleType& r) noexcept
template <typename ReturnValue, typename... Params>
static inline ReturnValue ObjCMsgSendSuper (struct objc_super* s, SEL sel, Params... params)
{
typedef ReturnValue (*SuperFn)(struct objc_super*, SEL, Params...);
using SuperFn = ReturnValue (*)(struct objc_super*, SEL, Params...);
SuperFn fn = reinterpret_cast<SuperFn> (objc_msgSendSuper);
return fn (s, sel, params...);
}
@ -226,16 +215,6 @@ static inline MsgSendFPRetFn getMsgSendFPRetFn() noexcept { return (MsgSendFPR
#endif
#endif
//==============================================================================
template <typename ObjectType>
struct NSObjectRetainer
{
inline NSObjectRetainer (ObjectType* o) : object (o) { [object retain]; }
inline ~NSObjectRetainer() { [object release]; }
ObjectType* object;
};
//==============================================================================
struct NSObjectDeleter
{

View File

@ -0,0 +1,133 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
namespace
{
static IPAddress makeAddress (const sockaddr_in6* addr_in)
{
if (addr_in == nullptr)
return {};
auto addr = addr_in->sin6_addr;
IPAddressByteUnion temp;
uint16 arr[8];
for (int i = 0; i < 8; ++i) // Swap bytes from network to host order
{
temp.split[0] = addr.s6_addr[i * 2 + 1];
temp.split[1] = addr.s6_addr[i * 2];
arr[i] = temp.combined;
}
return IPAddress (arr);
}
static IPAddress makeAddress (const sockaddr_in* addr_in)
{
if (addr_in->sin_addr.s_addr == INADDR_NONE)
return {};
return IPAddress (ntohl (addr_in->sin_addr.s_addr));
}
struct InterfaceInfo
{
IPAddress interfaceAddress, broadcastAddress;
};
bool operator== (const InterfaceInfo& lhs, const InterfaceInfo& rhs)
{
return lhs.interfaceAddress == rhs.interfaceAddress
&& lhs.broadcastAddress == rhs.broadcastAddress;
}
bool populateInterfaceInfo (struct ifaddrs* ifa, InterfaceInfo& interfaceInfo)
{
if (ifa->ifa_addr != nullptr)
{
if (ifa->ifa_addr->sa_family == AF_INET)
{
auto interfaceAddressInfo = reinterpret_cast<sockaddr_in*> (ifa->ifa_addr);
auto broadcastAddressInfo = reinterpret_cast<sockaddr_in*> (ifa->ifa_dstaddr);
if (interfaceAddressInfo->sin_addr.s_addr != INADDR_NONE)
{
interfaceInfo.interfaceAddress = makeAddress (interfaceAddressInfo);
interfaceInfo.broadcastAddress = makeAddress (broadcastAddressInfo);
return true;
}
}
else if (ifa->ifa_addr->sa_family == AF_INET6)
{
interfaceInfo.interfaceAddress = makeAddress (reinterpret_cast<sockaddr_in6*> (ifa->ifa_addr));
interfaceInfo.broadcastAddress = makeAddress (reinterpret_cast<sockaddr_in6*> (ifa->ifa_dstaddr));
return true;
}
}
return false;
}
Array<InterfaceInfo> getAllInterfaceInfo()
{
Array<InterfaceInfo> interfaces;
struct ifaddrs* ifaddr = nullptr;
if (getifaddrs (&ifaddr) != -1)
{
for (auto* ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next)
{
InterfaceInfo i;
if (populateInterfaceInfo (ifa, i))
interfaces.addIfNotAlreadyThere (i);
}
freeifaddrs (ifaddr);
}
return interfaces;
}
}
void IPAddress::findAllAddresses (Array<IPAddress>& result, bool includeIPv6)
{
for (auto& i : getAllInterfaceInfo())
if (includeIPv6 || ! i.interfaceAddress.isIPv6)
result.addIfNotAlreadyThere (i.interfaceAddress);
}
IPAddress IPAddress::getInterfaceBroadcastAddress (const IPAddress& interfaceAddress)
{
for (auto& i : getAllInterfaceInfo())
if (i.interfaceAddress == interfaceAddress)
return i.broadcastAddress;
return {};
}
} // namespace juce

View File

@ -47,18 +47,14 @@ public:
}
}
bool connect (int timeOutMilliseconds)
{
return openPipe (true, getTimeoutEnd (timeOutMilliseconds));
}
int read (char* destBuffer, int maxBytesToRead, int timeOutMilliseconds)
{
auto timeoutEnd = getTimeoutEnd (timeOutMilliseconds);
if (pipeIn == -1)
{
pipeIn = openPipe (createdPipe ? pipeInName : pipeOutName, O_RDWR | O_NONBLOCK, timeoutEnd);
if (pipeIn == -1)
return -1;
}
int bytesRead = 0;
while (bytesRead < maxBytesToRead)
@ -89,13 +85,8 @@ public:
{
auto timeoutEnd = getTimeoutEnd (timeOutMilliseconds);
if (pipeOut == -1)
{
pipeOut = openPipe (createdPipe ? pipeOutName : pipeInName, O_WRONLY, timeoutEnd);
if (pipeOut == -1)
return -1;
}
if (! openPipe (false, timeoutEnd))
return -1;
int bytesWritten = 0;
@ -160,6 +151,25 @@ private:
}
}
bool openPipe (bool isInput, uint32 timeoutEnd)
{
auto& pipe = isInput ? pipeIn : pipeOut;
int flags = isInput ? O_RDWR | O_NONBLOCK : O_WRONLY;
const String& pipeName = isInput ? (createdPipe ? pipeInName : pipeOutName)
: (createdPipe ? pipeOutName : pipeInName);
if (pipe == -1)
{
pipe = openPipe (pipeName, flags, timeoutEnd);
if (pipe == -1)
return false;
}
return true;
}
static void waitForInput (int handle, int timeoutMsecs) noexcept
{
struct timeval timeout;
@ -170,7 +180,7 @@ private:
FD_ZERO (&rset);
FD_SET (handle, &rset);
select (handle + 1, &rset, nullptr, 0, &timeout);
select (handle + 1, &rset, nullptr, nullptr, &timeout);
}
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Pimpl)
@ -211,6 +221,12 @@ bool NamedPipe::openInternal (const String& pipeName, bool createPipe, bool must
return false;
}
if (! pimpl->connect (200))
{
pimpl.reset();
return false;
}
return true;
}

View File

@ -44,7 +44,7 @@ void CriticalSection::exit() const noexcept { pthread_mutex_unlock (&loc
WaitableEvent::WaitableEvent (bool useManualReset) noexcept
: triggered (false), manualReset (useManualReset)
{
pthread_cond_init (&condition, 0);
pthread_cond_init (&condition, {});
pthread_mutexattr_t atts;
pthread_mutexattr_init (&atts);
@ -78,7 +78,7 @@ bool WaitableEvent::wait (int timeOutMillisecs) const noexcept
else
{
struct timeval now;
gettimeofday (&now, 0);
gettimeofday (&now, nullptr);
struct timespec time;
time.tv_sec = now.tv_sec + (timeOutMillisecs / 1000);
@ -428,11 +428,14 @@ bool File::setFileTimesInternal (int64 modificationTime, int64 accessTime, int64
bool File::deleteFile() const
{
if (! exists() && ! isSymbolicLink())
return true;
if (! isSymbolicLink())
{
if (! exists())
return true;
if (isDirectory())
return rmdir (fullPath.toUTF8()) == 0;
if (isDirectory())
return rmdir (fullPath.toUTF8()) == 0;
}
return remove (fullPath.toUTF8()) == 0;
}
@ -466,7 +469,7 @@ Result File::createDirectoryInternal (const String& fileName) const
//==============================================================================
int64 juce_fileSetPosition (void* handle, int64 pos)
{
if (handle != 0 && lseek (getFD (handle), (off_t) pos, SEEK_SET) == pos)
if (handle != nullptr && lseek (getFD (handle), (off_t) pos, SEEK_SET) == pos)
return pos;
return -1;
@ -484,7 +487,7 @@ void FileInputStream::openHandle()
FileInputStream::~FileInputStream()
{
if (fileHandle != 0)
if (fileHandle != nullptr)
close (getFD (fileHandle));
}
@ -492,7 +495,7 @@ size_t FileInputStream::readInternal (void* buffer, size_t numBytes)
{
ssize_t result = 0;
if (fileHandle != 0)
if (fileHandle != nullptr)
{
result = ::read (getFD (fileHandle), buffer, numBytes);
@ -545,16 +548,16 @@ void FileOutputStream::openHandle()
void FileOutputStream::closeHandle()
{
if (fileHandle != 0)
if (fileHandle != nullptr)
{
close (getFD (fileHandle));
fileHandle = 0;
fileHandle = nullptr;
}
}
ssize_t FileOutputStream::writeInternal (const void* data, size_t numBytes)
{
if (fileHandle == 0)
if (fileHandle == nullptr)
return 0;
auto result = ::write (getFD (fileHandle), data, numBytes);
@ -568,14 +571,14 @@ ssize_t FileOutputStream::writeInternal (const void* data, size_t numBytes)
#ifndef JUCE_ANDROID
void FileOutputStream::flushInternal()
{
if (fileHandle != 0 && fsync (getFD (fileHandle)) == -1)
if (fileHandle != nullptr && fsync (getFD (fileHandle)) == -1)
status = getResultForErrno();
}
#endif
Result FileOutputStream::truncate()
{
if (fileHandle == 0)
if (fileHandle == nullptr)
return status;
flush();
@ -607,10 +610,10 @@ void MemoryMappedFile::openInternal (const File& file, AccessMode mode, bool exc
if (fileHandle != -1)
{
void* m = mmap (0, (size_t) range.getLength(),
mode == readWrite ? (PROT_READ | PROT_WRITE) : PROT_READ,
exclusive ? MAP_PRIVATE : MAP_SHARED, fileHandle,
(off_t) range.getStart());
auto m = mmap (nullptr, (size_t) range.getLength(),
mode == readWrite ? (PROT_READ | PROT_WRITE) : PROT_READ,
exclusive ? MAP_PRIVATE : MAP_SHARED, fileHandle,
(off_t) range.getStart());
if (m != MAP_FAILED)
{
@ -637,9 +640,6 @@ MemoryMappedFile::~MemoryMappedFile()
File juce_getExecutableFile();
File juce_getExecutableFile()
{
#if JUCE_ANDROID
return File (android.appFile);
#else
struct DLAddrReader
{
static String getFilename()
@ -654,7 +654,6 @@ File juce_getExecutableFile()
static String filename = DLAddrReader::getFilename();
return File::getCurrentWorkingDirectory().getChildFile (filename);
#endif
}
//==============================================================================
@ -715,23 +714,7 @@ String File::getVolumeLabel() const
int File::getVolumeSerialNumber() const
{
int result = 0;
/* int fd = open (getFullPathName().toUTF8(), O_RDONLY | O_NONBLOCK);
char info[512];
#ifndef HDIO_GET_IDENTITY
#define HDIO_GET_IDENTITY 0x030d
#endif
if (ioctl (fd, HDIO_GET_IDENTITY, info) == 0)
{
DBG (String (info + 20, 20));
result = String (info + 20, 20).trim().getIntValue();
}
close (fd);*/
return result;
return 0;
}
//==============================================================================
@ -907,21 +890,25 @@ extern JavaVM* androidJNIJavaVM;
extern "C" void* threadEntryProc (void*);
extern "C" void* threadEntryProc (void* userData)
{
#if JUCE_ANDROID
// JNI_OnLoad was not called - make sure you load the JUCE shared library
// using System.load inside of Java
jassert (androidJNIJavaVM != nullptr);
JNIEnv* env;
androidJNIJavaVM->AttachCurrentThread (&env, nullptr);
setEnv (env);
#endif
auto* myself = static_cast<Thread*> (userData);
JUCE_AUTORELEASEPOOL
{
juce_threadEntryPoint (userData);
juce_threadEntryPoint (myself);
}
#if JUCE_ANDROID
if (androidJNIJavaVM != nullptr)
{
void* env = nullptr;
androidJNIJavaVM->GetEnv(&env, JNI_VERSION_1_2);
// only detach if we have actually been attached
if (env != nullptr)
androidJNIJavaVM->DetachCurrentThread();
}
#endif
return nullptr;
}
@ -952,8 +939,8 @@ void Thread::launchThread()
}
#endif
threadHandle = 0;
pthread_t handle = 0;
threadHandle = {};
pthread_t handle = {};
pthread_attr_t attr;
pthread_attr_t* attrPtr = nullptr;
@ -963,6 +950,7 @@ void Thread::launchThread()
pthread_attr_setstacksize (attrPtr, threadStackSize);
}
if (pthread_create (&handle, attrPtr, threadEntryProc, this) == 0)
{
pthread_detach (handle);
@ -976,13 +964,13 @@ void Thread::launchThread()
void Thread::closeThreadHandle()
{
threadId = 0;
threadHandle = 0;
threadId = {};
threadHandle = {};
}
void Thread::killThread()
{
if (threadHandle.get() != 0)
if (threadHandle.get() != nullptr)
{
#if JUCE_ANDROID
jassertfalse; // pthread_cancel not available!
@ -1312,7 +1300,7 @@ struct HighResolutionTimer::Pimpl
{
isRunning = false;
if (thread == 0)
if (thread == pthread_t())
return;
if (thread == pthread_self())
@ -1329,7 +1317,7 @@ struct HighResolutionTimer::Pimpl
pthread_mutex_unlock (&timerMutex);
pthread_join (thread, nullptr);
thread = 0;
thread = {};
}
HighResolutionTimer& owner;
@ -1343,15 +1331,7 @@ private:
static void* timerThread (void* param)
{
#if JUCE_ANDROID
// JNI_OnLoad was not called - make sure you load the JUCE shared library
// using System.load inside of Java
jassert (androidJNIJavaVM != nullptr);
JNIEnv* env;
androidJNIJavaVM->AttachCurrentThread (&env, nullptr);
setEnv (env);
#else
#if ! JUCE_ANDROID
int dummy;
pthread_setcancelstate (PTHREAD_CANCEL_ENABLE, &dummy);
#endif

View File

@ -129,7 +129,7 @@ namespace WindowsFileHelpers
File getSpecialFolderPath (int type)
{
WCHAR path [MAX_PATH + 256];
WCHAR path[MAX_PATH + 256];
if (SHGetSpecialFolderPath (0, path, type, FALSE))
return File (String (path));
@ -139,7 +139,7 @@ namespace WindowsFileHelpers
File getModuleFileName (HINSTANCE moduleHandle)
{
WCHAR dest [MAX_PATH + 256];
WCHAR dest[MAX_PATH + 256];
dest[0] = 0;
GetModuleFileName (moduleHandle, dest, (DWORD) numElementsInArray (dest));
return File (String (dest));
@ -147,7 +147,7 @@ namespace WindowsFileHelpers
Result getResultForLastError()
{
TCHAR messageBuffer [256] = { 0 };
TCHAR messageBuffer[256] = { 0 };
FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
nullptr, GetLastError(), MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
@ -181,7 +181,7 @@ bool File::existsAsFile() const
bool File::isDirectory() const
{
const DWORD attr = WindowsFileHelpers::getAtts (fullPath);
auto attr = WindowsFileHelpers::getAtts (fullPath);
return (attr & FILE_ATTRIBUTE_DIRECTORY) != 0 && attr != INVALID_FILE_ATTRIBUTES;
}
@ -250,22 +250,22 @@ bool File::moveToTrash() const
bool File::copyInternal (const File& dest) const
{
return CopyFile (fullPath.toWideCharPointer(), dest.getFullPathName().toWideCharPointer(), false) != 0;
return CopyFile (fullPath.toWideCharPointer(),
dest.getFullPathName().toWideCharPointer(), false) != 0;
}
bool File::moveInternal (const File& dest) const
{
return MoveFile (fullPath.toWideCharPointer(), dest.getFullPathName().toWideCharPointer()) != 0;
return MoveFile (fullPath.toWideCharPointer(),
dest.getFullPathName().toWideCharPointer()) != 0;
}
bool File::replaceInternal (const File& dest) const
{
void* lpExclude = 0;
void* lpReserved = 0;
return ReplaceFile (dest.getFullPathName().toWideCharPointer(), fullPath.toWideCharPointer(),
0, REPLACEFILE_IGNORE_MERGE_ERRORS | REPLACEFILE_IGNORE_ACL_ERRORS,
lpExclude, lpReserved) != 0;
return ReplaceFile (dest.getFullPathName().toWideCharPointer(),
fullPath.toWideCharPointer(),
0, REPLACEFILE_IGNORE_MERGE_ERRORS | 4 /*REPLACEFILE_IGNORE_ACL_ERRORS*/,
nullptr, nullptr) != 0;
}
Result File::createDirectoryInternal (const String& fileName) const
@ -279,14 +279,16 @@ int64 juce_fileSetPosition (void* handle, int64 pos)
{
LARGE_INTEGER li;
li.QuadPart = pos;
li.LowPart = SetFilePointer ((HANDLE) handle, (LONG) li.LowPart, &li.HighPart, FILE_BEGIN); // (returns -1 if it fails)
li.LowPart = SetFilePointer ((HANDLE) handle, (LONG) li.LowPart,
&li.HighPart, FILE_BEGIN); // (returns -1 if it fails)
return li.QuadPart;
}
void FileInputStream::openHandle()
{
HANDLE h = CreateFile (file.getFullPathName().toWideCharPointer(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, 0,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, 0);
auto h = CreateFile (file.getFullPathName().toWideCharPointer(),
GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, 0);
if (h != INVALID_HANDLE_VALUE)
fileHandle = (void*) h;
@ -304,6 +306,7 @@ size_t FileInputStream::readInternal (void* buffer, size_t numBytes)
if (fileHandle != 0)
{
DWORD actualNum = 0;
if (! ReadFile ((HANDLE) fileHandle, buffer, (DWORD) numBytes, &actualNum, 0))
status = WindowsFileHelpers::getResultForLastError();
@ -316,8 +319,9 @@ size_t FileInputStream::readInternal (void* buffer, size_t numBytes)
//==============================================================================
void FileOutputStream::openHandle()
{
HANDLE h = CreateFile (file.getFullPathName().toWideCharPointer(), GENERIC_WRITE, FILE_SHARE_READ, 0,
OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
auto h = CreateFile (file.getFullPathName().toWideCharPointer(),
GENERIC_WRITE, FILE_SHARE_READ, 0,
OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
if (h != INVALID_HANDLE_VALUE)
{
@ -343,16 +347,13 @@ void FileOutputStream::closeHandle()
ssize_t FileOutputStream::writeInternal (const void* bufferToWrite, size_t numBytes)
{
DWORD actualNum = 0;
if (fileHandle != nullptr)
{
DWORD actualNum = 0;
if (! WriteFile ((HANDLE) fileHandle, bufferToWrite, (DWORD) numBytes, &actualNum, 0))
status = WindowsFileHelpers::getResultForLastError();
return (ssize_t) actualNum;
}
return 0;
return (ssize_t) actualNum;
}
void FileOutputStream::flushInternal()
@ -396,15 +397,17 @@ void MemoryMappedFile::openInternal (const File& file, AccessMode mode, bool exc
access = FILE_MAP_ALL_ACCESS;
}
HANDLE h = CreateFile (file.getFullPathName().toWideCharPointer(), accessMode,
exclusive ? 0 : (FILE_SHARE_READ | (mode == readWrite ? FILE_SHARE_WRITE : 0)), 0,
createType, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, 0);
auto h = CreateFile (file.getFullPathName().toWideCharPointer(), accessMode,
exclusive ? 0 : (FILE_SHARE_READ | FILE_SHARE_DELETE | (mode == readWrite ? FILE_SHARE_WRITE : 0)), 0,
createType, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, 0);
if (h != INVALID_HANDLE_VALUE)
{
fileHandle = (void*) h;
HANDLE mappingHandle = CreateFileMapping (h, 0, protect, (DWORD) (range.getEnd() >> 32), (DWORD) range.getEnd(), 0);
auto mappingHandle = CreateFileMapping (h, 0, protect,
(DWORD) (range.getEnd() >> 32),
(DWORD) range.getEnd(), 0);
if (mappingHandle != 0)
{
@ -461,8 +464,9 @@ bool File::setFileTimesInternal (int64 modificationTime, int64 accessTime, int64
using namespace WindowsFileHelpers;
bool ok = false;
HANDLE h = CreateFile (fullPath.toWideCharPointer(), GENERIC_WRITE, FILE_SHARE_READ, 0,
OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
auto h = CreateFile (fullPath.toWideCharPointer(),
GENERIC_WRITE, FILE_SHARE_READ, 0,
OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
if (h != INVALID_HANDLE_VALUE)
{
@ -482,7 +486,7 @@ bool File::setFileTimesInternal (int64 modificationTime, int64 accessTime, int64
//==============================================================================
void File::findFileSystemRoots (Array<File>& destArray)
{
TCHAR buffer [2048] = { 0 };
TCHAR buffer[2048] = { 0 };
GetLogicalDriveStrings (2048, buffer);
const TCHAR* n = buffer;
@ -499,13 +503,14 @@ void File::findFileSystemRoots (Array<File>& destArray)
roots.sort (true);
for (int i = 0; i < roots.size(); ++i)
destArray.add (roots [i]);
destArray.add (roots[i]);
}
//==============================================================================
String File::getVolumeLabel() const
{
TCHAR dest[64];
if (! GetVolumeInformation (WindowsFileHelpers::getDriveFromPath (getFullPathName()).toWideCharPointer(), dest,
(DWORD) numElementsInArray (dest), 0, 0, 0, 0, 0))
dest[0] = 0;
@ -539,9 +544,9 @@ uint64 File::getFileIdentifier() const
{
uint64 result = 0;
HANDLE h = CreateFile (getFullPathName().toWideCharPointer(),
GENERIC_READ, FILE_SHARE_READ, nullptr,
OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0);
auto h = CreateFile (getFullPathName().toWideCharPointer(),
GENERIC_READ, FILE_SHARE_READ, nullptr,
OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0);
if (h != INVALID_HANDLE_VALUE)
{
@ -568,7 +573,7 @@ bool File::isOnHardDisk() const
if (fullPath.isEmpty())
return false;
const unsigned int n = WindowsFileHelpers::getWindowsDriveType (getFullPathName());
auto n = WindowsFileHelpers::getWindowsDriveType (getFullPathName());
if (fullPath.toLowerCase()[0] <= 'b' && fullPath[1] == ':')
return n != DRIVE_REMOVABLE;
@ -583,7 +588,7 @@ bool File::isOnRemovableDrive() const
if (fullPath.isEmpty())
return false;
const unsigned int n = WindowsFileHelpers::getWindowsDriveType (getFullPathName());
auto n = WindowsFileHelpers::getWindowsDriveType (getFullPathName());
return n == DRIVE_CDROM
|| n == DRIVE_REMOTE
@ -612,7 +617,7 @@ File JUCE_CALLTYPE File::getSpecialLocation (const SpecialLocationType type)
case tempDirectory:
{
WCHAR dest [2048];
WCHAR dest[2048];
dest[0] = 0;
GetTempPath ((DWORD) numElementsInArray (dest), dest);
return File (String (dest));
@ -620,7 +625,7 @@ File JUCE_CALLTYPE File::getSpecialLocation (const SpecialLocationType type)
case windowsSystemDirectory:
{
WCHAR dest [2048];
WCHAR dest[2048];
dest[0] = 0;
GetSystemDirectoryW (dest, (UINT) numElementsInArray (dest));
return File (String (dest));
@ -645,7 +650,7 @@ File JUCE_CALLTYPE File::getSpecialLocation (const SpecialLocationType type)
//==============================================================================
File File::getCurrentWorkingDirectory()
{
WCHAR dest [MAX_PATH + 256];
WCHAR dest[MAX_PATH + 256];
dest[0] = 0;
GetCurrentDirectory ((DWORD) numElementsInArray (dest), dest);
return File (String (dest));
@ -710,7 +715,7 @@ static String readWindowsLnkFile (File lnkFile, bool wantsAbsolutePath)
&& (! wantsAbsolutePath || SUCCEEDED (shellLink->Resolve (0, SLR_ANY_MATCH | SLR_NO_UI))))
{
WIN32_FIND_DATA winFindData;
WCHAR resolvedPath [MAX_PATH];
WCHAR resolvedPath[MAX_PATH];
DWORD flags = SLGP_UNCPRIORITY;
@ -1175,7 +1180,15 @@ bool NamedPipe::openInternal (const String& pipeName, const bool createPipe, boo
{
pimpl.reset (new Pimpl (pipeName, createPipe, mustNotExist));
if (createPipe && pimpl->pipeH == INVALID_HANDLE_VALUE)
if (createPipe)
{
if (pimpl->pipeH == INVALID_HANDLE_VALUE)
{
pimpl.reset();
return false;
}
}
else if (! pimpl->connect (200))
{
pimpl.reset();
return false;

View File

@ -343,6 +343,35 @@ private:
InternetSetOption (sessionHandle, option, &timeOutMs, sizeof (timeOutMs));
}
void sendHTTPRequest (INTERNET_BUFFERS& buffers, WebInputStream::Listener* listener)
{
if (! HttpSendRequestEx (request, &buffers, 0, HSR_INITIATE, 0))
return;
int totalBytesSent = 0;
while (totalBytesSent < (int) postData.getSize())
{
auto bytesToSend = jmin (1024, (int) postData.getSize() - totalBytesSent);
DWORD bytesSent = 0;
if (bytesToSend == 0
|| ! InternetWriteFile (request, static_cast<const char*> (postData.getData()) + totalBytesSent,
(DWORD) bytesToSend, &bytesSent))
{
return;
}
totalBytesSent += bytesSent;
if (listener != nullptr
&& ! listener->postDataSendProgress (owner, totalBytesSent, (int) postData.getSize()))
{
return;
}
}
}
void openHTTPConnection (URL_COMPONENTS& uc, const String& address, WebInputStream::Listener* listener)
{
const TCHAR* mimeTypes[] = { _T("*/*"), nullptr };
@ -365,43 +394,29 @@ private:
if (request != 0)
{
INTERNET_BUFFERS buffers = { 0 };
buffers.dwStructSize = sizeof (INTERNET_BUFFERS);
buffers.lpcszHeader = headers.toWideCharPointer();
buffers.dwStructSize = sizeof (INTERNET_BUFFERS);
buffers.lpcszHeader = headers.toWideCharPointer();
buffers.dwHeadersLength = (DWORD) headers.length();
buffers.dwBufferTotal = (DWORD) postData.getSize();
buffers.dwBufferTotal = (DWORD) postData.getSize();
if (HttpSendRequestEx (request, &buffers, 0, HSR_INITIATE, 0))
auto sendRequestAndTryEnd = [this, &buffers, &listener]() -> bool
{
int bytesSent = 0;
sendHTTPRequest (buffers, listener);
for (;;)
{
const int bytesToDo = jmin (1024, (int) postData.getSize() - bytesSent);
DWORD bytesDone = 0;
if (HttpEndRequest (request, 0, 0, 0))
return true;
if (bytesToDo > 0
&& ! InternetWriteFile (request,
static_cast<const char*> (postData.getData()) + bytesSent,
(DWORD) bytesToDo, &bytesDone))
{
break;
}
return false;
};
if (bytesToDo == 0 || (int) bytesDone < bytesToDo)
{
if (HttpEndRequest (request, 0, 0, 0))
return;
auto closed = sendRequestAndTryEnd();
break;
}
// N.B. this is needed for some authenticated HTTP connections
if (! closed && GetLastError() == ERROR_INTERNET_FORCE_RETRY)
closed = sendRequestAndTryEnd();
bytesSent += bytesDone;
if (listener != nullptr
&& ! listener->postDataSendProgress (owner, bytesSent, (int) postData.getSize()))
break;
}
}
if (closed)
return;
}
closeConnection();
@ -514,6 +529,37 @@ namespace MACAddressHelpers
split[1] = sa_in6->sin6_addr.u.Byte[off];
#endif
}
static IPAddress createAddress (const sockaddr_in6* sa_in6)
{
IPAddressByteUnion temp;
uint16 arr[8];
for (int i = 0; i < 8; ++i)
{
split (sa_in6, i * 2, temp.split);
arr[i] = temp.combined;
}
return IPAddress (arr);
}
static IPAddress createAddress (const sockaddr_in* sa_in)
{
return IPAddress ((uint8*) &sa_in->sin_addr.s_addr, false);
}
template <typename Type>
static void findAddresses (Array<IPAddress>& result, bool includeIPv6, Type start)
{
for (auto addr = start; addr != nullptr; addr = addr->Next)
{
if (addr->Address.lpSockaddr->sa_family == AF_INET)
result.addIfNotAlreadyThere (createAddress ((sockaddr_in*) addr->Address.lpSockaddr));
else if (addr->Address.lpSockaddr->sa_family == AF_INET6 && includeIPv6)
result.addIfNotAlreadyThere (createAddress ((sockaddr_in6*) addr->Address.lpSockaddr));
}
}
}
void MACAddress::findAllAddresses (Array<MACAddress>& result)
@ -530,94 +576,25 @@ void IPAddress::findAllAddresses (Array<IPAddress>& result, bool includeIPv6)
result.addIfNotAlreadyThere (IPAddress::local (true));
GetAdaptersAddressesHelper addressesHelper;
if (addressesHelper.callGetAdaptersAddresses())
{
for (PIP_ADAPTER_ADDRESSES adapter = addressesHelper.adaptersAddresses; adapter != nullptr; adapter = adapter->Next)
{
PIP_ADAPTER_UNICAST_ADDRESS pUnicast = nullptr;
for (pUnicast = adapter->FirstUnicastAddress; pUnicast != nullptr; pUnicast = pUnicast->Next)
{
if (pUnicast->Address.lpSockaddr->sa_family == AF_INET)
{
const sockaddr_in* sa_in = (sockaddr_in*)pUnicast->Address.lpSockaddr;
IPAddress ip ((uint8*)&sa_in->sin_addr.s_addr, false);
result.addIfNotAlreadyThere (ip);
}
else if (pUnicast->Address.lpSockaddr->sa_family == AF_INET6 && includeIPv6)
{
const sockaddr_in6* sa_in6 = (sockaddr_in6*)pUnicast->Address.lpSockaddr;
ByteUnion temp;
uint16 arr[8];
for (int i = 0; i < 8; ++i)
{
MACAddressHelpers::split (sa_in6, i * 2, temp.split);
arr[i] = temp.combined;
}
IPAddress ip (arr);
result.addIfNotAlreadyThere (ip);
}
}
PIP_ADAPTER_ANYCAST_ADDRESS pAnycast = nullptr;
for (pAnycast = adapter->FirstAnycastAddress; pAnycast != nullptr; pAnycast = pAnycast->Next)
{
if (pAnycast->Address.lpSockaddr->sa_family == AF_INET)
{
const sockaddr_in* sa_in = (sockaddr_in*)pAnycast->Address.lpSockaddr;
IPAddress ip ((uint8*)&sa_in->sin_addr.s_addr, false);
result.addIfNotAlreadyThere (ip);
}
else if (pAnycast->Address.lpSockaddr->sa_family == AF_INET6 && includeIPv6)
{
const sockaddr_in6* sa_in6 = (sockaddr_in6*)pAnycast->Address.lpSockaddr;
ByteUnion temp;
uint16 arr[8];
for (int i = 0; i < 8; ++i)
{
MACAddressHelpers::split (sa_in6, i * 2, temp.split);
arr[i] = temp.combined;
}
IPAddress ip (arr);
result.addIfNotAlreadyThere (ip);
}
}
PIP_ADAPTER_MULTICAST_ADDRESS pMulticast = nullptr;
for (pMulticast = adapter->FirstMulticastAddress; pMulticast != nullptr; pMulticast = pMulticast->Next)
{
if (pMulticast->Address.lpSockaddr->sa_family == AF_INET)
{
const sockaddr_in* sa_in = (sockaddr_in*)pMulticast->Address.lpSockaddr;
IPAddress ip ((uint8*)&sa_in->sin_addr.s_addr, false);
result.addIfNotAlreadyThere (ip);
}
else if (pMulticast->Address.lpSockaddr->sa_family == AF_INET6 && includeIPv6)
{
const sockaddr_in6* sa_in6 = (sockaddr_in6*)pMulticast->Address.lpSockaddr;
ByteUnion temp;
uint16 arr[8];
for (int i = 0; i < 8; ++i)
{
MACAddressHelpers::split (sa_in6, i * 2, temp.split);
arr[i] = temp.combined;
}
IPAddress ip (arr);
result.addIfNotAlreadyThere (ip);
}
}
MACAddressHelpers::findAddresses (result, includeIPv6, adapter->FirstUnicastAddress);
MACAddressHelpers::findAddresses (result, includeIPv6, adapter->FirstAnycastAddress);
MACAddressHelpers::findAddresses (result, includeIPv6, adapter->FirstMulticastAddress);
}
}
}
IPAddress IPAddress::getInterfaceBroadcastAddress (const IPAddress&)
{
// TODO
return {};
}
//==============================================================================
bool JUCE_CALLTYPE Process::openEmailWithAttachments (const String& targetEmailAddress,
const String& emailSubject,

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