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