@@ -1,144 +0,0 @@ | |||
#ifndef CARLA_JUCE_APPCONFIG_H_INCLUDED | |||
#define CARLA_JUCE_APPCONFIG_H_INCLUDED | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
// Check OS | |||
#if defined(WIN64) || defined(_WIN64) || defined(__WIN64__) | |||
# define APPCONFIG_OS_WIN64 | |||
#elif defined(WIN32) || defined(_WIN32) || defined(__WIN32__) | |||
# define APPCONFIG_OS_WIN32 | |||
#elif defined(__APPLE__) | |||
# define APPCONFIG_OS_MAC | |||
#elif defined(__HAIKU__) | |||
# define APPCONFIG_OS_HAIKU | |||
#elif defined(__linux__) || defined(__linux) | |||
# define APPCONFIG_OS_LINUX | |||
#else | |||
# warning Unsupported platform! | |||
#endif | |||
#if defined(APPCONFIG_OS_WIN32) || defined(APPCONFIG_OS_WIN64) | |||
# define APPCONFIG_OS_WIN | |||
#elif defined(APPCONFIG_OS_LINUX) || defined(APPCONFIG_OS_MAC) | |||
# define APPCONFIG_OS_UNIX | |||
#endif | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
// always enabled | |||
#define JUCE_MODULE_AVAILABLE_juce_core 1 | |||
// always disabled | |||
#define JUCE_MODULE_AVAILABLE_juce_audio_plugin_client 0 | |||
#define JUCE_MODULE_AVAILABLE_juce_audio_utils 0 | |||
#define JUCE_MODULE_AVAILABLE_juce_cryptography 0 | |||
#define JUCE_MODULE_AVAILABLE_juce_opengl 0 | |||
#define JUCE_MODULE_AVAILABLE_juce_video 0 | |||
// also disabled | |||
#define JUCE_MODULE_AVAILABLE_juce_audio_basics 0 | |||
#define JUCE_MODULE_AVAILABLE_juce_audio_devices 0 | |||
#define JUCE_MODULE_AVAILABLE_juce_audio_formats 0 | |||
#define JUCE_MODULE_AVAILABLE_juce_audio_processors 0 | |||
#define JUCE_MODULE_AVAILABLE_juce_data_structures 0 | |||
#define JUCE_MODULE_AVAILABLE_juce_events 0 | |||
#define JUCE_MODULE_AVAILABLE_juce_graphics 0 | |||
#define JUCE_MODULE_AVAILABLE_juce_gui_basics 0 | |||
#define JUCE_MODULE_AVAILABLE_juce_gui_extra 0 | |||
// misc | |||
#define JUCE_DISABLE_JUCE_VERSION_PRINTING 1 | |||
#define JUCE_GLOBAL_MODULE_SETTINGS_INCLUDED 1 | |||
#define JUCE_STANDALONE_APPLICATION 0 | |||
#define JUCE_REPORT_APP_USAGE 0 | |||
#define JUCE_DISPLAY_SPLASH_SCREEN 0 | |||
#define JUCE_USE_DARK_SPLASH_SCREEN 0 | |||
#define JUCE_STRING_UTF_TYPE 8 | |||
#define JUCE_USE_VFORK 1 | |||
#if ! (defined(APPCONFIG_OS_MAC) || defined(APPCONFIG_OS_WIN)) | |||
# define JUCE_MODAL_LOOPS_PERMITTED 0 | |||
# define JUCE_AUDIOPROCESSOR_NO_GUI 1 | |||
#endif | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
// juce_core | |||
//============================================================================= | |||
/** Config: JUCE_FORCE_DEBUG | |||
Normally, JUCE_DEBUG is set to 1 or 0 based on compiler and project settings, | |||
but if you define this value, you can override this to force it to be true or false. | |||
*/ | |||
#define JUCE_FORCE_DEBUG 0 | |||
//============================================================================= | |||
/** Config: JUCE_LOG_ASSERTIONS | |||
If this flag is enabled, the the jassert and jassertfalse macros will always use Logger::writeToLog() | |||
to write a message when an assertion happens. | |||
Enabling it will also leave this turned on in release builds. When it's disabled, | |||
however, the jassert and jassertfalse macros will not be compiled in a | |||
release build. | |||
@see jassert, jassertfalse, Logger | |||
*/ | |||
#define JUCE_LOG_ASSERTIONS 1 | |||
//============================================================================= | |||
/** Config: JUCE_CHECK_MEMORY_LEAKS | |||
Enables a memory-leak check for certain objects when the app terminates. See the LeakedObjectDetector | |||
class and the JUCE_LEAK_DETECTOR macro for more details about enabling leak checking for specific classes. | |||
*/ | |||
#ifdef DEBUG | |||
#define JUCE_CHECK_MEMORY_LEAKS 1 | |||
#else | |||
#define JUCE_CHECK_MEMORY_LEAKS 0 | |||
#endif | |||
//============================================================================= | |||
/** Config: JUCE_DONT_AUTOLINK_TO_WIN32_LIBRARIES | |||
In a Visual C++ build, this can be used to stop the required system libs being | |||
automatically added to the link stage. | |||
*/ | |||
#define JUCE_DONT_AUTOLINK_TO_WIN32_LIBRARIES 0 | |||
/** Config: JUCE_INCLUDE_ZLIB_CODE | |||
This can be used to disable Juce's embedded 3rd-party zlib code. | |||
You might need to tweak this if you're linking to an external zlib library in your app, | |||
but for normal apps, this option should be left alone. | |||
If you disable this, you might also want to set a value for JUCE_ZLIB_INCLUDE_PATH, to | |||
specify the path where your zlib headers live. | |||
*/ | |||
#define JUCE_INCLUDE_ZLIB_CODE 1 | |||
/** Config: JUCE_USE_CURL | |||
Enables http/https support via libcurl (Linux only). Enabling this will add an additional | |||
run-time dynmic dependency to libcurl. | |||
If you disable this then https/ssl support will not be available on linux. | |||
*/ | |||
#define JUCE_USE_CURL 0 | |||
/* Config: JUCE_CATCH_UNHANDLED_EXCEPTIONS | |||
If enabled, this will add some exception-catching code to forward unhandled exceptions | |||
to your JUCEApplicationBase::unhandledException() callback. | |||
*/ | |||
#define JUCE_CATCH_UNHANDLED_EXCEPTIONS 0 | |||
/** Config: JUCE_ALLOW_STATIC_NULL_VARIABLES | |||
If disabled, this will turn off dangerous static globals like String::empty, var::null, etc | |||
which can cause nasty order-of-initialisation problems if they are referenced during static | |||
constructor code. | |||
*/ | |||
#define JUCE_ALLOW_STATIC_NULL_VARIABLES 0 | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
#endif // CARLA_JUCE_APPCONFIG_H_INCLUDED |
@@ -1,123 +0,0 @@ | |||
#!/usr/bin/make -f | |||
# Makefile for juce_core # | |||
# ---------------------- # | |||
# Created by falkTX | |||
# | |||
CWD=../.. | |||
MODULENAME=juce_core | |||
include ../Makefile.mk | |||
# ---------------------------------------------------------------------------------------------------------------------------- | |||
BUILD_CXX_FLAGS += $(JUCE_CORE_FLAGS) -I.. | |||
ifeq ($(WIN32),true) | |||
BUILD_CXX_FLAGS += -w | |||
endif | |||
# ---------------------------------------------------------------------------------------------------------------------------- | |||
ifeq ($(MACOS),true) | |||
OBJS = $(OBJDIR)/$(MODULENAME).mm.o | |||
OBJS_posix32 = $(OBJDIR)/$(MODULENAME).mm.posix32.o | |||
OBJS_posix64 = $(OBJDIR)/$(MODULENAME).mm.posix64.o | |||
else | |||
OBJS = $(OBJDIR)/$(MODULENAME).cpp.o | |||
OBJS_posix32 = $(OBJDIR)/$(MODULENAME).cpp.posix32.o | |||
OBJS_posix64 = $(OBJDIR)/$(MODULENAME).cpp.posix64.o | |||
endif | |||
OBJS_win32 = $(OBJDIR)/$(MODULENAME).cpp.win32.o | |||
OBJS_win64 = $(OBJDIR)/$(MODULENAME).cpp.win64.o | |||
# ---------------------------------------------------------------------------------------------------------------------------- | |||
all: $(MODULEDIR)/$(MODULENAME).a | |||
posix32: $(MODULEDIR)/$(MODULENAME).posix32.a | |||
posix64: $(MODULEDIR)/$(MODULENAME).posix64.a | |||
win32: $(MODULEDIR)/$(MODULENAME).win32.a | |||
win64: $(MODULEDIR)/$(MODULENAME).win64.a | |||
# ---------------------------------------------------------------------------------------------------------------------------- | |||
clean: | |||
rm -f $(OBJDIR)/*.o $(MODULEDIR)/$(MODULENAME)*.a | |||
debug: | |||
$(MAKE) DEBUG=true | |||
# ---------------------------------------------------------------------------------------------------------------------------- | |||
$(MODULEDIR)/$(MODULENAME).a: $(OBJS) | |||
-@mkdir -p $(MODULEDIR) | |||
@echo "Creating $(MODULENAME).a" | |||
@rm -f $@ | |||
@$(AR) crs $@ $^ | |||
$(MODULEDIR)/$(MODULENAME).posix32.a: $(OBJS_posix32) | |||
-@mkdir -p $(MODULEDIR) | |||
@echo "Creating $(MODULENAME).posix32.a" | |||
@rm -f $@ | |||
@$(AR) crs $@ $^ | |||
$(MODULEDIR)/$(MODULENAME).posix64.a: $(OBJS_posix64) | |||
-@mkdir -p $(MODULEDIR) | |||
@echo "Creating $(MODULENAME).posix64.a" | |||
@rm -f $@ | |||
@$(AR) crs $@ $^ | |||
$(MODULEDIR)/$(MODULENAME).win32.a: $(OBJS_win32) | |||
-@mkdir -p $(MODULEDIR) | |||
@echo "Creating $(MODULENAME).win32.a" | |||
@rm -f $@ | |||
@$(AR) crs $@ $^ | |||
$(MODULEDIR)/$(MODULENAME).win64.a: $(OBJS_win64) | |||
-@mkdir -p $(MODULEDIR) | |||
@echo "Creating $(MODULENAME).win64.a" | |||
@rm -f $@ | |||
@$(AR) crs $@ $^ | |||
# ---------------------------------------------------------------------------------------------------------------------------- | |||
$(OBJDIR)/$(MODULENAME).cpp.o: $(MODULENAME).cpp | |||
-@mkdir -p $(OBJDIR) | |||
@echo "Compiling $<" | |||
@$(CXX) $< $(BUILD_CXX_FLAGS) -c -o $@ | |||
$(OBJDIR)/$(MODULENAME).cpp.%32.o: $(MODULENAME).cpp | |||
-@mkdir -p $(OBJDIR) | |||
@echo "Compiling $< (32bit)" | |||
@$(CXX) $< $(BUILD_CXX_FLAGS) $(32BIT_FLAGS) -c -o $@ | |||
$(OBJDIR)/$(MODULENAME).cpp.%64.o: $(MODULENAME).cpp | |||
-@mkdir -p $(OBJDIR) | |||
@echo "Compiling $< (64bit)" | |||
@$(CXX) $< $(BUILD_CXX_FLAGS) $(64BIT_FLAGS) -c -o $@ | |||
# ---------------------------------------------------------------------------------------------------------------------------- | |||
$(OBJDIR)/$(MODULENAME).mm.o: $(MODULENAME).cpp | |||
-@mkdir -p $(OBJDIR) | |||
@echo "Compiling $<" | |||
@$(CXX) $< $(BUILD_CXX_FLAGS) -ObjC++ -c -o $@ | |||
$(OBJDIR)/$(MODULENAME).mm.%32.o: $(MODULENAME).cpp | |||
-@mkdir -p $(OBJDIR) | |||
@echo "Compiling $< (32bit)" | |||
@$(CXX) $< $(BUILD_CXX_FLAGS) $(32BIT_FLAGS) -ObjC++ -c -o $@ | |||
$(OBJDIR)/$(MODULENAME).mm.%64.o: $(MODULENAME).cpp | |||
-@mkdir -p $(OBJDIR) | |||
@echo "Compiling $< (64bit)" | |||
@$(CXX) $< $(BUILD_CXX_FLAGS) $(64BIT_FLAGS) -ObjC++ -c -o $@ | |||
# ---------------------------------------------------------------------------------------------------------------------------- | |||
-include $(OBJS:%.o=%.d) | |||
-include $(OBJS_posix32:%.o=%.d) | |||
-include $(OBJS_posix64:%.o=%.d) | |||
-include $(OBJS_win32:%.o=%.d) | |||
-include $(OBJS_win64:%.o=%.d) | |||
# ---------------------------------------------------------------------------------------------------------------------------- |
@@ -1,234 +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. | |||
============================================================================== | |||
*/ | |||
namespace juce | |||
{ | |||
AbstractFifo::AbstractFifo (const int capacity) noexcept | |||
: bufferSize (capacity) | |||
{ | |||
jassert (bufferSize > 0); | |||
} | |||
AbstractFifo::~AbstractFifo() {} | |||
int AbstractFifo::getTotalSize() const noexcept { return bufferSize; } | |||
int AbstractFifo::getFreeSpace() const noexcept { return bufferSize - getNumReady() - 1; } | |||
int AbstractFifo::getNumReady() const noexcept | |||
{ | |||
const int vs = validStart.get(); | |||
const int ve = validEnd.get(); | |||
return ve >= vs ? (ve - vs) : (bufferSize - (vs - ve)); | |||
} | |||
void AbstractFifo::reset() noexcept | |||
{ | |||
validEnd = 0; | |||
validStart = 0; | |||
} | |||
void AbstractFifo::setTotalSize (int newSize) noexcept | |||
{ | |||
jassert (newSize > 0); | |||
reset(); | |||
bufferSize = newSize; | |||
} | |||
//============================================================================== | |||
void AbstractFifo::prepareToWrite (int numToWrite, int& startIndex1, int& blockSize1, int& startIndex2, int& blockSize2) const noexcept | |||
{ | |||
const int vs = validStart.get(); | |||
const int ve = validEnd.value; | |||
const int freeSpace = ve >= vs ? (bufferSize - (ve - vs)) : (vs - ve); | |||
numToWrite = jmin (numToWrite, freeSpace - 1); | |||
if (numToWrite <= 0) | |||
{ | |||
startIndex1 = 0; | |||
startIndex2 = 0; | |||
blockSize1 = 0; | |||
blockSize2 = 0; | |||
} | |||
else | |||
{ | |||
startIndex1 = ve; | |||
startIndex2 = 0; | |||
blockSize1 = jmin (bufferSize - ve, numToWrite); | |||
numToWrite -= blockSize1; | |||
blockSize2 = numToWrite <= 0 ? 0 : jmin (numToWrite, vs); | |||
} | |||
} | |||
void AbstractFifo::finishedWrite (int numWritten) noexcept | |||
{ | |||
jassert (numWritten >= 0 && numWritten < bufferSize); | |||
int newEnd = validEnd.value + numWritten; | |||
if (newEnd >= bufferSize) | |||
newEnd -= bufferSize; | |||
validEnd = newEnd; | |||
} | |||
void AbstractFifo::prepareToRead (int numWanted, int& startIndex1, int& blockSize1, int& startIndex2, int& blockSize2) const noexcept | |||
{ | |||
const int vs = validStart.value; | |||
const int ve = validEnd.get(); | |||
const int numReady = ve >= vs ? (ve - vs) : (bufferSize - (vs - ve)); | |||
numWanted = jmin (numWanted, numReady); | |||
if (numWanted <= 0) | |||
{ | |||
startIndex1 = 0; | |||
startIndex2 = 0; | |||
blockSize1 = 0; | |||
blockSize2 = 0; | |||
} | |||
else | |||
{ | |||
startIndex1 = vs; | |||
startIndex2 = 0; | |||
blockSize1 = jmin (bufferSize - vs, numWanted); | |||
numWanted -= blockSize1; | |||
blockSize2 = numWanted <= 0 ? 0 : jmin (numWanted, ve); | |||
} | |||
} | |||
void AbstractFifo::finishedRead (int numRead) noexcept | |||
{ | |||
jassert (numRead >= 0 && numRead <= bufferSize); | |||
int newStart = validStart.value + numRead; | |||
if (newStart >= bufferSize) | |||
newStart -= bufferSize; | |||
validStart = newStart; | |||
} | |||
//============================================================================== | |||
//============================================================================== | |||
#if JUCE_UNIT_TESTS | |||
class AbstractFifoTests : public UnitTest | |||
{ | |||
public: | |||
AbstractFifoTests() : UnitTest ("Abstract Fifo", "Containers") {} | |||
class WriteThread : public Thread | |||
{ | |||
public: | |||
WriteThread (AbstractFifo& f, int* b, Random rng) | |||
: Thread ("fifo writer"), fifo (f), buffer (b), random (rng) | |||
{ | |||
startThread(); | |||
} | |||
~WriteThread() | |||
{ | |||
stopThread (5000); | |||
} | |||
void run() | |||
{ | |||
int n = 0; | |||
while (! threadShouldExit()) | |||
{ | |||
int num = random.nextInt (2000) + 1; | |||
int start1, size1, start2, size2; | |||
fifo.prepareToWrite (num, start1, size1, start2, size2); | |||
jassert (size1 >= 0 && size2 >= 0); | |||
jassert (size1 == 0 || (start1 >= 0 && start1 < fifo.getTotalSize())); | |||
jassert (size2 == 0 || (start2 >= 0 && start2 < fifo.getTotalSize())); | |||
for (int i = 0; i < size1; ++i) | |||
buffer [start1 + i] = n++; | |||
for (int i = 0; i < size2; ++i) | |||
buffer [start2 + i] = n++; | |||
fifo.finishedWrite (size1 + size2); | |||
} | |||
} | |||
private: | |||
AbstractFifo& fifo; | |||
int* buffer; | |||
Random random; | |||
}; | |||
void runTest() override | |||
{ | |||
beginTest ("AbstractFifo"); | |||
int buffer [5000]; | |||
AbstractFifo fifo (numElementsInArray (buffer)); | |||
WriteThread writer (fifo, buffer, getRandom()); | |||
int n = 0; | |||
Random r = getRandom(); | |||
r.combineSeed (12345); | |||
for (int count = 100000; --count >= 0;) | |||
{ | |||
int num = r.nextInt (6000) + 1; | |||
int start1, size1, start2, size2; | |||
fifo.prepareToRead (num, start1, size1, start2, size2); | |||
if (! (size1 >= 0 && size2 >= 0) | |||
&& (size1 == 0 || (start1 >= 0 && start1 < fifo.getTotalSize())) | |||
&& (size2 == 0 || (start2 >= 0 && start2 < fifo.getTotalSize()))) | |||
{ | |||
expect (false, "prepareToRead returned -ve values"); | |||
break; | |||
} | |||
bool failed = false; | |||
for (int i = 0; i < size1; ++i) | |||
failed = (buffer [start1 + i] != n++) || failed; | |||
for (int i = 0; i < size2; ++i) | |||
failed = (buffer [start2 + i] != n++) || failed; | |||
if (failed) | |||
{ | |||
expect (false, "read values were incorrect"); | |||
break; | |||
} | |||
fifo.finishedRead (size1 + size2); | |||
} | |||
} | |||
}; | |||
static AbstractFifoTests fifoUnitTests; | |||
#endif | |||
} // namespace juce |
@@ -1,212 +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. | |||
============================================================================== | |||
*/ | |||
namespace juce | |||
{ | |||
//============================================================================== | |||
/** | |||
Encapsulates the logic required to implement a lock-free FIFO. | |||
This class handles the logic needed when building a single-reader, single-writer FIFO. | |||
It doesn't actually hold any data itself, but your FIFO class can use one of these to manage | |||
its position and status when reading or writing to it. | |||
To use it, you can call prepareToWrite() to determine the position within your own buffer that | |||
an incoming block of data should be stored, and prepareToRead() to find out when the next | |||
outgoing block should be read from. | |||
e.g. | |||
@code | |||
class MyFifo | |||
{ | |||
public: | |||
MyFifo() : abstractFifo (1024) | |||
{ | |||
} | |||
void addToFifo (const int* someData, int numItems) | |||
{ | |||
int start1, size1, start2, size2; | |||
abstractFifo.prepareToWrite (numItems, start1, size1, start2, size2); | |||
if (size1 > 0) | |||
copySomeData (myBuffer + start1, someData, size1); | |||
if (size2 > 0) | |||
copySomeData (myBuffer + start2, someData + size1, size2); | |||
abstractFifo.finishedWrite (size1 + size2); | |||
} | |||
void readFromFifo (int* someData, int numItems) | |||
{ | |||
int start1, size1, start2, size2; | |||
abstractFifo.prepareToRead (numItems, start1, size1, start2, size2); | |||
if (size1 > 0) | |||
copySomeData (someData, myBuffer + start1, size1); | |||
if (size2 > 0) | |||
copySomeData (someData + size1, myBuffer + start2, size2); | |||
abstractFifo.finishedRead (size1 + size2); | |||
} | |||
private: | |||
AbstractFifo abstractFifo; | |||
int myBuffer [1024]; | |||
}; | |||
@endcode | |||
*/ | |||
class JUCE_API AbstractFifo | |||
{ | |||
public: | |||
//============================================================================== | |||
/** Creates a FIFO to manage a buffer with the specified capacity. */ | |||
AbstractFifo (int capacity) noexcept; | |||
/** Destructor */ | |||
~AbstractFifo(); | |||
//============================================================================== | |||
/** Returns the total size of the buffer being managed. */ | |||
int getTotalSize() const noexcept; | |||
/** Returns the number of items that can currently be added to the buffer without it overflowing. */ | |||
int getFreeSpace() const noexcept; | |||
/** Returns the number of items that can currently be read from the buffer. */ | |||
int getNumReady() const noexcept; | |||
/** Clears the buffer positions, so that it appears empty. */ | |||
void reset() noexcept; | |||
/** Changes the buffer's total size. | |||
Note that this isn't thread-safe, so don't call it if there's any danger that it | |||
might overlap with a call to any other method in this class! | |||
*/ | |||
void setTotalSize (int newSize) noexcept; | |||
//============================================================================== | |||
/** Returns the location within the buffer at which an incoming block of data should be written. | |||
Because the section of data that you want to add to the buffer may overlap the end | |||
and wrap around to the start, two blocks within your buffer are returned, and you | |||
should copy your data into the first one, with any remaining data spilling over into | |||
the second. | |||
If the number of items you ask for is too large to fit within the buffer's free space, then | |||
blockSize1 + blockSize2 may add up to a lower value than numToWrite. If this happens, you | |||
may decide to keep waiting and re-trying the method until there's enough space available. | |||
After calling this method, if you choose to write your data into the blocks returned, you | |||
must call finishedWrite() to tell the FIFO how much data you actually added. | |||
e.g. | |||
@code | |||
void addToFifo (const int* someData, int numItems) | |||
{ | |||
int start1, size1, start2, size2; | |||
prepareToWrite (numItems, start1, size1, start2, size2); | |||
if (size1 > 0) | |||
copySomeData (myBuffer + start1, someData, size1); | |||
if (size2 > 0) | |||
copySomeData (myBuffer + start2, someData + size1, size2); | |||
finishedWrite (size1 + size2); | |||
} | |||
@endcode | |||
@param numToWrite indicates how many items you'd like to add to the buffer | |||
@param startIndex1 on exit, this will contain the start index in your buffer at which your data should be written | |||
@param blockSize1 on exit, this indicates how many items can be written to the block starting at startIndex1 | |||
@param startIndex2 on exit, this will contain the start index in your buffer at which any data that didn't fit into | |||
the first block should be written | |||
@param blockSize2 on exit, this indicates how many items can be written to the block starting at startIndex2 | |||
@see finishedWrite | |||
*/ | |||
void prepareToWrite (int numToWrite, int& startIndex1, int& blockSize1, int& startIndex2, int& blockSize2) const noexcept; | |||
/** Called after writing from the FIFO, to indicate that this many items have been added. | |||
@see prepareToWrite | |||
*/ | |||
void finishedWrite (int numWritten) noexcept; | |||
/** Returns the location within the buffer from which the next block of data should be read. | |||
Because the section of data that you want to read from the buffer may overlap the end | |||
and wrap around to the start, two blocks within your buffer are returned, and you | |||
should read from both of them. | |||
If the number of items you ask for is greater than the amount of data available, then | |||
blockSize1 + blockSize2 may add up to a lower value than numWanted. If this happens, you | |||
may decide to keep waiting and re-trying the method until there's enough data available. | |||
After calling this method, if you choose to read the data, you must call finishedRead() to | |||
tell the FIFO how much data you have consumed. | |||
e.g. | |||
@code | |||
void readFromFifo (int* someData, int numItems) | |||
{ | |||
int start1, size1, start2, size2; | |||
prepareToRead (numSamples, start1, size1, start2, size2); | |||
if (size1 > 0) | |||
copySomeData (someData, myBuffer + start1, size1); | |||
if (size2 > 0) | |||
copySomeData (someData + size1, myBuffer + start2, size2); | |||
finishedRead (size1 + size2); | |||
} | |||
@endcode | |||
@param numWanted indicates how many items you'd like to add to the buffer | |||
@param startIndex1 on exit, this will contain the start index in your buffer at which your data should be written | |||
@param blockSize1 on exit, this indicates how many items can be written to the block starting at startIndex1 | |||
@param startIndex2 on exit, this will contain the start index in your buffer at which any data that didn't fit into | |||
the first block should be written | |||
@param blockSize2 on exit, this indicates how many items can be written to the block starting at startIndex2 | |||
@see finishedRead | |||
*/ | |||
void prepareToRead (int numWanted, int& startIndex1, int& blockSize1, int& startIndex2, int& blockSize2) const noexcept; | |||
/** Called after reading from the FIFO, to indicate that this many items have now been consumed. | |||
@see prepareToRead | |||
*/ | |||
void finishedRead (int numRead) noexcept; | |||
private: | |||
//============================================================================== | |||
int bufferSize; | |||
Atomic <int> validStart, validEnd; | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AbstractFifo) | |||
}; | |||
} // namespace juce |
@@ -1,128 +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. | |||
============================================================================== | |||
*/ | |||
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 | |||
*/ | |||
template <class ElementType, class TypeOfCriticalSectionToUse> | |||
class ArrayAllocationBase : public TypeOfCriticalSectionToUse | |||
{ | |||
public: | |||
//============================================================================== | |||
/** Creates an empty array. */ | |||
ArrayAllocationBase() noexcept | |||
: numAllocated (0) | |||
{ | |||
} | |||
/** Destructor. */ | |||
~ArrayAllocationBase() noexcept | |||
{ | |||
} | |||
ArrayAllocationBase (ArrayAllocationBase<ElementType, TypeOfCriticalSectionToUse>&& other) noexcept | |||
: elements (static_cast<HeapBlock<ElementType>&&> (other.elements)), | |||
numAllocated (other.numAllocated) | |||
{ | |||
} | |||
ArrayAllocationBase& operator= (ArrayAllocationBase<ElementType, TypeOfCriticalSectionToUse>&& other) noexcept | |||
{ | |||
elements = static_cast<HeapBlock<ElementType>&&> (other.elements); | |||
numAllocated = other.numAllocated; | |||
return *this; | |||
} | |||
//============================================================================== | |||
/** Changes the amount of storage allocated. | |||
This will retain any data currently held in the array, and either add or | |||
remove extra space at the end. | |||
@param numElements the number of elements that are needed | |||
*/ | |||
void setAllocatedSize (const int numElements) | |||
{ | |||
if (numAllocated != numElements) | |||
{ | |||
if (numElements > 0) | |||
elements.realloc ((size_t) numElements); | |||
else | |||
elements.free(); | |||
numAllocated = numElements; | |||
} | |||
} | |||
/** Increases the amount of storage allocated if it is less than a given amount. | |||
This will retain any data currently held in the array, but will add | |||
extra space at the end to make sure there it's at least as big as the size | |||
passed in. If it's already bigger, no action is taken. | |||
@param minNumElements the minimum number of elements that are needed | |||
*/ | |||
void ensureAllocatedSize (const int minNumElements) | |||
{ | |||
if (minNumElements > numAllocated) | |||
setAllocatedSize ((minNumElements + minNumElements / 2 + 8) & ~7); | |||
jassert (numAllocated <= 0 || elements != nullptr); | |||
} | |||
/** Minimises the amount of storage allocated so that it's no more than | |||
the given number of elements. | |||
*/ | |||
void shrinkToNoMoreThan (const int maxNumElements) | |||
{ | |||
if (maxNumElements < numAllocated) | |||
setAllocatedSize (maxNumElements); | |||
} | |||
/** Swap the contents of two objects. */ | |||
void swapWith (ArrayAllocationBase <ElementType, TypeOfCriticalSectionToUse>& other) noexcept | |||
{ | |||
elements.swapWith (other.elements); | |||
std::swap (numAllocated, other.numAllocated); | |||
} | |||
//============================================================================== | |||
HeapBlock<ElementType> elements; | |||
int numAllocated; | |||
private: | |||
JUCE_DECLARE_NON_COPYABLE (ArrayAllocationBase) | |||
}; | |||
} // namespace juce |
@@ -1,132 +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. | |||
============================================================================== | |||
*/ | |||
namespace juce | |||
{ | |||
DynamicObject::DynamicObject() | |||
{ | |||
} | |||
DynamicObject::DynamicObject (const DynamicObject& other) | |||
: ReferenceCountedObject(), properties (other.properties) | |||
{ | |||
} | |||
DynamicObject::~DynamicObject() | |||
{ | |||
} | |||
bool DynamicObject::hasProperty (const Identifier& propertyName) const | |||
{ | |||
const var* const v = properties.getVarPointer (propertyName); | |||
return v != nullptr && ! v->isMethod(); | |||
} | |||
const var& DynamicObject::getProperty (const Identifier& propertyName) const | |||
{ | |||
return properties [propertyName]; | |||
} | |||
void DynamicObject::setProperty (const Identifier& propertyName, const var& newValue) | |||
{ | |||
properties.set (propertyName, newValue); | |||
} | |||
void DynamicObject::removeProperty (const Identifier& propertyName) | |||
{ | |||
properties.remove (propertyName); | |||
} | |||
bool DynamicObject::hasMethod (const Identifier& methodName) const | |||
{ | |||
return getProperty (methodName).isMethod(); | |||
} | |||
var DynamicObject::invokeMethod (Identifier method, const var::NativeFunctionArgs& args) | |||
{ | |||
if (auto function = properties [method].getNativeFunction()) | |||
return function (args); | |||
return {}; | |||
} | |||
void DynamicObject::setMethod (Identifier name, var::NativeFunction function) | |||
{ | |||
properties.set (name, var (function)); | |||
} | |||
void DynamicObject::clear() | |||
{ | |||
properties.clear(); | |||
} | |||
void DynamicObject::cloneAllProperties() | |||
{ | |||
for (int i = properties.size(); --i >= 0;) | |||
if (auto* v = properties.getVarPointerAt (i)) | |||
*v = v->clone(); | |||
} | |||
DynamicObject::Ptr DynamicObject::clone() | |||
{ | |||
Ptr d (new DynamicObject (*this)); | |||
d->cloneAllProperties(); | |||
return d; | |||
} | |||
void DynamicObject::writeAsJSON (OutputStream& out, const int indentLevel, const bool allOnOneLine, int maximumDecimalPlaces) | |||
{ | |||
out << '{'; | |||
if (! allOnOneLine) | |||
out << newLine; | |||
const int numValues = properties.size(); | |||
for (int i = 0; i < numValues; ++i) | |||
{ | |||
if (! allOnOneLine) | |||
JSONFormatter::writeSpaces (out, indentLevel + JSONFormatter::indentSize); | |||
out << '"'; | |||
JSONFormatter::writeString (out, properties.getName (i)); | |||
out << "\": "; | |||
JSONFormatter::write (out, properties.getValueAt (i), indentLevel + JSONFormatter::indentSize, allOnOneLine, maximumDecimalPlaces); | |||
if (i < numValues - 1) | |||
{ | |||
if (allOnOneLine) | |||
out << ", "; | |||
else | |||
out << ',' << newLine; | |||
} | |||
else if (! allOnOneLine) | |||
out << newLine; | |||
} | |||
if (! allOnOneLine) | |||
JSONFormatter::writeSpaces (out, indentLevel); | |||
out << '}'; | |||
} | |||
} // namespace juce |
@@ -1,130 +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. | |||
============================================================================== | |||
*/ | |||
namespace juce | |||
{ | |||
//============================================================================== | |||
/** | |||
Represents a dynamically implemented object. | |||
This class is primarily intended for wrapping scripting language objects, | |||
but could be used for other purposes. | |||
An instance of a DynamicObject can be used to store named properties, and | |||
by subclassing hasMethod() and invokeMethod(), you can give your object | |||
methods. | |||
*/ | |||
class JUCE_API DynamicObject : public ReferenceCountedObject | |||
{ | |||
public: | |||
//============================================================================== | |||
DynamicObject(); | |||
DynamicObject (const DynamicObject&); | |||
~DynamicObject(); | |||
typedef ReferenceCountedObjectPtr<DynamicObject> Ptr; | |||
//============================================================================== | |||
/** Returns true if the object has a property with this name. | |||
Note that if the property is actually a method, this will return false. | |||
*/ | |||
virtual bool hasProperty (const Identifier& propertyName) const; | |||
/** Returns a named property. | |||
This returns var() if no such property exists. | |||
*/ | |||
virtual const var& getProperty (const Identifier& propertyName) const; | |||
/** Sets a named property. */ | |||
virtual void setProperty (const Identifier& propertyName, const var& newValue); | |||
/** Removes a named property. */ | |||
virtual void removeProperty (const Identifier& propertyName); | |||
//============================================================================== | |||
/** Checks whether this object has the specified method. | |||
The default implementation of this just checks whether there's a property | |||
with this name that's actually a method, but this can be overridden for | |||
building objects with dynamic invocation. | |||
*/ | |||
virtual bool hasMethod (const Identifier& methodName) const; | |||
/** Invokes a named method on this object. | |||
The default implementation looks up the named property, and if it's a method | |||
call, then it invokes it. | |||
This method is virtual to allow more dynamic invocation to used for objects | |||
where the methods may not already be set as properies. | |||
*/ | |||
virtual var invokeMethod (Identifier methodName, | |||
const var::NativeFunctionArgs& args); | |||
/** Adds a method to the class. | |||
This is basically the same as calling setProperty (methodName, (var::NativeFunction) myFunction), but | |||
helps to avoid accidentally invoking the wrong type of var constructor. It also makes | |||
the code easier to read, | |||
*/ | |||
void setMethod (Identifier methodName, var::NativeFunction function); | |||
//============================================================================== | |||
/** Removes all properties and methods from the object. */ | |||
void clear(); | |||
/** Returns the NamedValueSet that holds the object's properties. */ | |||
NamedValueSet& getProperties() noexcept { return properties; } | |||
/** Calls var::clone() on all the properties that this object contains. */ | |||
void cloneAllProperties(); | |||
//============================================================================== | |||
/** Returns a clone of this object. | |||
The default implementation of this method just returns a new DynamicObject | |||
with a (deep) copy of all of its properties. Subclasses can override this to | |||
implement their own custom copy routines. | |||
*/ | |||
virtual Ptr clone(); | |||
//============================================================================== | |||
/** Writes this object to a text stream in JSON format. | |||
This method is used by JSON::toString and JSON::writeToStream, and you should | |||
never need to call it directly, but it's virtual so that custom object types | |||
can stringify themselves appropriately. | |||
*/ | |||
virtual void writeAsJSON (OutputStream&, int indentLevel, bool allOnOneLine, int maximumDecimalPlaces); | |||
private: | |||
//============================================================================== | |||
NamedValueSet properties; | |||
#if JUCE_CATCH_DEPRECATED_CODE_MISUSE | |||
// This method has been deprecated - use var::invoke instead | |||
virtual void invokeMethod (const Identifier&, const var*, int) {} | |||
#endif | |||
JUCE_LEAK_DETECTOR (DynamicObject) | |||
}; | |||
} // namespace juce |
@@ -1,188 +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. | |||
============================================================================== | |||
*/ | |||
namespace juce | |||
{ | |||
#ifndef DOXYGEN | |||
/** This is an internal helper class which converts a juce ElementComparator style | |||
class (using a "compareElements" method) into a class that's compatible with | |||
std::sort (i.e. using an operator() to compare the elements) | |||
*/ | |||
template <typename ElementComparator> | |||
struct SortFunctionConverter | |||
{ | |||
SortFunctionConverter (ElementComparator& e) : comparator (e) {} | |||
template <typename Type> | |||
bool operator() (Type a, Type b) { return comparator.compareElements (a, b) < 0; } | |||
private: | |||
ElementComparator& comparator; | |||
SortFunctionConverter& operator= (const SortFunctionConverter&) JUCE_DELETED_FUNCTION; | |||
}; | |||
#endif | |||
//============================================================================== | |||
/** | |||
Sorts a range of elements in an array. | |||
The comparator object that is passed-in must define a public method with the following | |||
signature: | |||
@code | |||
int compareElements (ElementType first, ElementType second); | |||
@endcode | |||
..and this method must return: | |||
- a value of < 0 if the first comes before the second | |||
- a value of 0 if the two objects are equivalent | |||
- a value of > 0 if the second comes before the first | |||
To improve performance, the compareElements() method can be declared as static or const. | |||
@param comparator an object which defines a compareElements() method | |||
@param array the array to sort | |||
@param firstElement the index of the first element of the range to be sorted | |||
@param lastElement the index of the last element in the range that needs | |||
sorting (this is inclusive) | |||
@param retainOrderOfEquivalentItems if true, the order of items that the | |||
comparator deems the same will be maintained - this will be | |||
a slower algorithm than if they are allowed to be moved around. | |||
@see sortArrayRetainingOrder | |||
*/ | |||
template <class ElementType, class ElementComparator> | |||
static void sortArray (ElementComparator& comparator, | |||
ElementType* const array, | |||
int firstElement, | |||
int lastElement, | |||
const bool retainOrderOfEquivalentItems) | |||
{ | |||
SortFunctionConverter<ElementComparator> converter (comparator); | |||
if (retainOrderOfEquivalentItems) | |||
std::stable_sort (array + firstElement, array + lastElement + 1, converter); | |||
else | |||
std::sort (array + firstElement, array + lastElement + 1, converter); | |||
} | |||
//============================================================================== | |||
/** | |||
Searches a sorted array of elements, looking for the index at which a specified value | |||
should be inserted for it to be in the correct order. | |||
The comparator object that is passed-in must define a public method with the following | |||
signature: | |||
@code | |||
int compareElements (ElementType first, ElementType second); | |||
@endcode | |||
..and this method must return: | |||
- a value of < 0 if the first comes before the second | |||
- a value of 0 if the two objects are equivalent | |||
- a value of > 0 if the second comes before the first | |||
To improve performance, the compareElements() method can be declared as static or const. | |||
@param comparator an object which defines a compareElements() method | |||
@param array the array to search | |||
@param newElement the value that is going to be inserted | |||
@param firstElement the index of the first element to search | |||
@param lastElement the index of the last element in the range (this is non-inclusive) | |||
*/ | |||
template <class ElementType, class ElementComparator> | |||
static int findInsertIndexInSortedArray (ElementComparator& comparator, | |||
ElementType* const array, | |||
const ElementType newElement, | |||
int firstElement, | |||
int lastElement) | |||
{ | |||
jassert (firstElement <= lastElement); | |||
ignoreUnused (comparator); // if you pass in an object with a static compareElements() method, this | |||
// avoids getting warning messages about the parameter being unused | |||
while (firstElement < lastElement) | |||
{ | |||
if (comparator.compareElements (newElement, array [firstElement]) == 0) | |||
{ | |||
++firstElement; | |||
break; | |||
} | |||
else | |||
{ | |||
const int halfway = (firstElement + lastElement) >> 1; | |||
if (halfway == firstElement) | |||
{ | |||
if (comparator.compareElements (newElement, array [halfway]) >= 0) | |||
++firstElement; | |||
break; | |||
} | |||
else if (comparator.compareElements (newElement, array [halfway]) >= 0) | |||
{ | |||
firstElement = halfway; | |||
} | |||
else | |||
{ | |||
lastElement = halfway; | |||
} | |||
} | |||
} | |||
return firstElement; | |||
} | |||
//============================================================================== | |||
/** | |||
A simple ElementComparator class that can be used to sort an array of | |||
objects that support the '<' operator. | |||
This will work for primitive types and objects that implement operator<(). | |||
Example: @code | |||
Array <int> myArray; | |||
DefaultElementComparator<int> sorter; | |||
myArray.sort (sorter); | |||
@endcode | |||
@see ElementComparator | |||
*/ | |||
template <class ElementType> | |||
class DefaultElementComparator | |||
{ | |||
private: | |||
typedef typename TypeHelpers::ParameterType<ElementType>::type ParameterType; | |||
public: | |||
static int compareElements (ParameterType first, ParameterType second) | |||
{ | |||
return (first < second) ? -1 : ((second < first) ? 1 : 0); | |||
} | |||
}; | |||
} // namespace juce |
@@ -1,500 +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. | |||
============================================================================== | |||
*/ | |||
namespace juce | |||
{ | |||
//============================================================================== | |||
/** | |||
A simple class to generate hash functions for some primitive types, intended for | |||
use with the HashMap class. | |||
@see HashMap | |||
*/ | |||
struct DefaultHashFunctions | |||
{ | |||
/** Generates a simple hash from an unsigned int. */ | |||
static int generateHash (uint32 key, int upperLimit) noexcept { return (int) (key % (uint32) upperLimit); } | |||
/** Generates a simple hash from an integer. */ | |||
static int generateHash (int32 key, int upperLimit) noexcept { return generateHash ((uint32) key, upperLimit); } | |||
/** Generates a simple hash from a uint64. */ | |||
static int generateHash (uint64 key, int upperLimit) noexcept { return (int) (key % (uint64) upperLimit); } | |||
/** Generates a simple hash from an int64. */ | |||
static int generateHash (int64 key, int upperLimit) noexcept { return generateHash ((uint64) key, upperLimit); } | |||
/** Generates a simple hash from a string. */ | |||
static int generateHash (const String& key, int upperLimit) noexcept { return generateHash ((uint32) key.hashCode(), upperLimit); } | |||
/** Generates a simple hash from a variant. */ | |||
static int generateHash (const var& key, int upperLimit) noexcept { return generateHash (key.toString(), upperLimit); } | |||
/** Generates a simple hash from a void ptr. */ | |||
static int generateHash (const void* key, int upperLimit) noexcept { return generateHash ((pointer_sized_uint) key, upperLimit); } | |||
}; | |||
//============================================================================== | |||
/** | |||
Holds a set of mappings between some key/value pairs. | |||
The types of the key and value objects are set as template parameters. | |||
You can also specify a class to supply a hash function that converts a key value | |||
into an hashed integer. This class must have the form: | |||
@code | |||
struct MyHashGenerator | |||
{ | |||
int generateHash (MyKeyType key, int upperLimit) const | |||
{ | |||
// The function must return a value 0 <= x < upperLimit | |||
return someFunctionOfMyKeyType (key) % upperLimit; | |||
} | |||
}; | |||
@endcode | |||
Like the Array class, the key and value types are expected to be copy-by-value | |||
types, so if you define them to be pointer types, this class won't delete the | |||
objects that they point to. | |||
If you don't supply a class for the HashFunctionType template parameter, the | |||
default one provides some simple mappings for strings and ints. | |||
@code | |||
HashMap<int, String> hash; | |||
hash.set (1, "item1"); | |||
hash.set (2, "item2"); | |||
DBG (hash [1]); // prints "item1" | |||
DBG (hash [2]); // prints "item2" | |||
// This iterates the map, printing all of its key -> value pairs.. | |||
for (HashMap<int, String>::Iterator i (hash); i.next();) | |||
DBG (i.getKey() << " -> " << i.getValue()); | |||
@endcode | |||
@tparam HashFunctionType The class of hash function, which must be copy-constructible. | |||
@see CriticalSection, DefaultHashFunctions, NamedValueSet, SortedSet | |||
*/ | |||
template <typename KeyType, | |||
typename ValueType, | |||
class HashFunctionType = DefaultHashFunctions, | |||
class TypeOfCriticalSectionToUse = DummyCriticalSection> | |||
class HashMap | |||
{ | |||
private: | |||
typedef typename TypeHelpers::ParameterType<KeyType>::type KeyTypeParameter; | |||
typedef typename TypeHelpers::ParameterType<ValueType>::type ValueTypeParameter; | |||
public: | |||
//============================================================================== | |||
/** Creates an empty hash-map. | |||
@param numberOfSlots Specifies the number of hash entries the map will use. This will be | |||
the "upperLimit" parameter that is passed to your generateHash() | |||
function. The number of hash slots will grow automatically if necessary, | |||
or it can be remapped manually using remapTable(). | |||
@param hashFunction An instance of HashFunctionType, which will be copied and | |||
stored to use with the HashMap. This parameter can be omitted | |||
if HashFunctionType has a default constructor. | |||
*/ | |||
explicit HashMap (int numberOfSlots = defaultHashTableSize, | |||
HashFunctionType hashFunction = HashFunctionType()) | |||
: hashFunctionToUse (hashFunction), totalNumItems (0) | |||
{ | |||
hashSlots.insertMultiple (0, nullptr, numberOfSlots); | |||
} | |||
/** Destructor. */ | |||
~HashMap() | |||
{ | |||
clear(); | |||
} | |||
//============================================================================== | |||
/** Removes all values from the map. | |||
Note that this will clear the content, but won't affect the number of slots (see | |||
remapTable and getNumSlots). | |||
*/ | |||
void clear() | |||
{ | |||
const ScopedLockType sl (getLock()); | |||
for (auto i = hashSlots.size(); --i >= 0;) | |||
{ | |||
auto* h = hashSlots.getUnchecked(i); | |||
while (h != nullptr) | |||
{ | |||
const ScopedPointer<HashEntry> deleter (h); | |||
h = h->nextEntry; | |||
} | |||
hashSlots.set (i, nullptr); | |||
} | |||
totalNumItems = 0; | |||
} | |||
//============================================================================== | |||
/** Returns the current number of items in the map. */ | |||
inline int size() const noexcept | |||
{ | |||
return totalNumItems; | |||
} | |||
/** Returns the value corresponding to a given key. | |||
If the map doesn't contain the key, a default instance of the value type is returned. | |||
@param keyToLookFor the key of the item being requested | |||
*/ | |||
inline ValueType operator[] (KeyTypeParameter keyToLookFor) const | |||
{ | |||
const ScopedLockType sl (getLock()); | |||
if (auto* entry = getEntry (getSlot (keyToLookFor), keyToLookFor)) | |||
return entry->value; | |||
return ValueType(); | |||
} | |||
/** Returns a reference to the value corresponding to a given key. | |||
If the map doesn't contain the key, a default instance of the value type is | |||
added to the map and a reference to this is returned. | |||
@param keyToLookFor the key of the item being requested | |||
*/ | |||
inline ValueType& getReference (KeyTypeParameter keyToLookFor) | |||
{ | |||
const ScopedLockType sl (getLock()); | |||
auto hashIndex = generateHashFor (keyToLookFor, getNumSlots()); | |||
auto* firstEntry = hashSlots.getUnchecked (hashIndex); | |||
if (auto* entry = getEntry (firstEntry, keyToLookFor)) | |||
return entry->value; | |||
auto* entry = new HashEntry (keyToLookFor, ValueType(), firstEntry); | |||
hashSlots.set (hashIndex, entry); | |||
++totalNumItems; | |||
if (totalNumItems > (getNumSlots() * 3) / 2) | |||
remapTable (getNumSlots() * 2); | |||
return entry->value; | |||
} | |||
//============================================================================== | |||
/** Returns true if the map contains an item with the specied key. */ | |||
bool contains (KeyTypeParameter keyToLookFor) const | |||
{ | |||
const ScopedLockType sl (getLock()); | |||
return (getEntry (getSlot (keyToLookFor), keyToLookFor) != nullptr); | |||
} | |||
/** Returns true if the hash contains at least one occurrence of a given value. */ | |||
bool containsValue (ValueTypeParameter valueToLookFor) const | |||
{ | |||
const ScopedLockType sl (getLock()); | |||
for (auto i = getNumSlots(); --i >= 0;) | |||
for (auto* entry = hashSlots.getUnchecked(i); entry != nullptr; entry = entry->nextEntry) | |||
if (entry->value == valueToLookFor) | |||
return true; | |||
return false; | |||
} | |||
//============================================================================== | |||
/** Adds or replaces an element in the hash-map. | |||
If there's already an item with the given key, this will replace its value. Otherwise, a new item | |||
will be added to the map. | |||
*/ | |||
void set (KeyTypeParameter newKey, ValueTypeParameter newValue) { getReference (newKey) = newValue; } | |||
/** Removes an item with the given key. */ | |||
void remove (KeyTypeParameter keyToRemove) | |||
{ | |||
const ScopedLockType sl (getLock()); | |||
auto hashIndex = generateHashFor (keyToRemove, getNumSlots()); | |||
auto* entry = hashSlots.getUnchecked (hashIndex); | |||
HashEntry* previous = nullptr; | |||
while (entry != nullptr) | |||
{ | |||
if (entry->key == keyToRemove) | |||
{ | |||
const ScopedPointer<HashEntry> deleter (entry); | |||
entry = entry->nextEntry; | |||
if (previous != nullptr) | |||
previous->nextEntry = entry; | |||
else | |||
hashSlots.set (hashIndex, entry); | |||
--totalNumItems; | |||
} | |||
else | |||
{ | |||
previous = entry; | |||
entry = entry->nextEntry; | |||
} | |||
} | |||
} | |||
/** Removes all items with the given value. */ | |||
void removeValue (ValueTypeParameter valueToRemove) | |||
{ | |||
const ScopedLockType sl (getLock()); | |||
for (auto i = getNumSlots(); --i >= 0;) | |||
{ | |||
auto* entry = hashSlots.getUnchecked(i); | |||
HashEntry* previous = nullptr; | |||
while (entry != nullptr) | |||
{ | |||
if (entry->value == valueToRemove) | |||
{ | |||
const ScopedPointer<HashEntry> deleter (entry); | |||
entry = entry->nextEntry; | |||
if (previous != nullptr) | |||
previous->nextEntry = entry; | |||
else | |||
hashSlots.set (i, entry); | |||
--totalNumItems; | |||
} | |||
else | |||
{ | |||
previous = entry; | |||
entry = entry->nextEntry; | |||
} | |||
} | |||
} | |||
} | |||
/** Remaps the hash-map to use a different number of slots for its hash function. | |||
Each slot corresponds to a single hash-code, and each one can contain multiple items. | |||
@see getNumSlots() | |||
*/ | |||
void remapTable (int newNumberOfSlots) | |||
{ | |||
const ScopedLockType sl (getLock()); | |||
Array<HashEntry*> newSlots; | |||
newSlots.insertMultiple (0, nullptr, newNumberOfSlots); | |||
for (auto i = getNumSlots(); --i >= 0;) | |||
{ | |||
HashEntry* nextEntry = nullptr; | |||
for (auto* entry = hashSlots.getUnchecked(i); entry != nullptr; entry = nextEntry) | |||
{ | |||
auto hashIndex = generateHashFor (entry->key, newNumberOfSlots); | |||
nextEntry = entry->nextEntry; | |||
entry->nextEntry = newSlots.getUnchecked (hashIndex); | |||
newSlots.set (hashIndex, entry); | |||
} | |||
} | |||
hashSlots.swapWith (newSlots); | |||
} | |||
/** Returns the number of slots which are available for hashing. | |||
Each slot corresponds to a single hash-code, and each one can contain multiple items. | |||
@see getNumSlots() | |||
*/ | |||
inline int getNumSlots() const noexcept | |||
{ | |||
return hashSlots.size(); | |||
} | |||
//============================================================================== | |||
/** Efficiently swaps the contents of two hash-maps. */ | |||
template <class OtherHashMapType> | |||
void swapWith (OtherHashMapType& otherHashMap) noexcept | |||
{ | |||
const ScopedLockType lock1 (getLock()); | |||
const typename OtherHashMapType::ScopedLockType lock2 (otherHashMap.getLock()); | |||
hashSlots.swapWith (otherHashMap.hashSlots); | |||
std::swap (totalNumItems, otherHashMap.totalNumItems); | |||
} | |||
//============================================================================== | |||
/** Returns the CriticalSection that locks this structure. | |||
To lock, you can call getLock().enter() and getLock().exit(), or preferably use | |||
an object of ScopedLockType as an RAII lock for it. | |||
*/ | |||
inline const TypeOfCriticalSectionToUse& getLock() const noexcept { return lock; } | |||
/** Returns the type of scoped lock to use for locking this array */ | |||
typedef typename TypeOfCriticalSectionToUse::ScopedLockType ScopedLockType; | |||
private: | |||
//============================================================================== | |||
class HashEntry | |||
{ | |||
public: | |||
HashEntry (KeyTypeParameter k, ValueTypeParameter val, HashEntry* const next) | |||
: key (k), value (val), nextEntry (next) | |||
{} | |||
const KeyType key; | |||
ValueType value; | |||
HashEntry* nextEntry; | |||
JUCE_DECLARE_NON_COPYABLE (HashEntry) | |||
}; | |||
public: | |||
//============================================================================== | |||
/** Iterates over the items in a HashMap. | |||
To use it, repeatedly call next() until it returns false, e.g. | |||
@code | |||
HashMap <String, String> myMap; | |||
HashMap<String, String>::Iterator i (myMap); | |||
while (i.next()) | |||
{ | |||
DBG (i.getKey() << " -> " << i.getValue()); | |||
} | |||
@endcode | |||
The order in which items are iterated bears no resemblence to the order in which | |||
they were originally added! | |||
Obviously as soon as you call any non-const methods on the original hash-map, any | |||
iterators that were created beforehand will cease to be valid, and should not be used. | |||
@see HashMap | |||
*/ | |||
struct Iterator | |||
{ | |||
Iterator (const HashMap& hashMapToIterate) noexcept | |||
: hashMap (hashMapToIterate), entry (nullptr), index (0) | |||
{} | |||
Iterator (const Iterator& other) noexcept | |||
: hashMap (other.hashMap), entry (other.entry), index (other.index) | |||
{} | |||
/** Moves to the next item, if one is available. | |||
When this returns true, you can get the item's key and value using getKey() and | |||
getValue(). If it returns false, the iteration has finished and you should stop. | |||
*/ | |||
bool next() noexcept | |||
{ | |||
if (entry != nullptr) | |||
entry = entry->nextEntry; | |||
while (entry == nullptr) | |||
{ | |||
if (index >= hashMap.getNumSlots()) | |||
return false; | |||
entry = hashMap.hashSlots.getUnchecked (index++); | |||
} | |||
return true; | |||
} | |||
/** Returns the current item's key. | |||
This should only be called when a call to next() has just returned true. | |||
*/ | |||
KeyType getKey() const | |||
{ | |||
return entry != nullptr ? entry->key : KeyType(); | |||
} | |||
/** Returns the current item's value. | |||
This should only be called when a call to next() has just returned true. | |||
*/ | |||
ValueType getValue() const | |||
{ | |||
return entry != nullptr ? entry->value : ValueType(); | |||
} | |||
/** Resets the iterator to its starting position. */ | |||
void reset() noexcept | |||
{ | |||
entry = nullptr; | |||
index = 0; | |||
} | |||
Iterator& operator++() noexcept { next(); return *this; } | |||
ValueType operator*() const { return getValue(); } | |||
bool operator!= (const Iterator& other) const noexcept { return entry != other.entry || index != other.index; } | |||
void resetToEnd() noexcept { index = hashMap.getNumSlots(); } | |||
private: | |||
//============================================================================== | |||
const HashMap& hashMap; | |||
HashEntry* entry; | |||
int index; | |||
// using the copy constructor is ok, but you cannot assign iterators | |||
Iterator& operator= (const Iterator&) JUCE_DELETED_FUNCTION; | |||
JUCE_LEAK_DETECTOR (Iterator) | |||
}; | |||
/** Returns a start iterator for the values in this tree. */ | |||
Iterator begin() const noexcept { Iterator i (*this); i.next(); return i; } | |||
/** Returns an end iterator for the values in this tree. */ | |||
Iterator end() const noexcept { Iterator i (*this); i.resetToEnd(); return i; } | |||
private: | |||
//============================================================================== | |||
enum { defaultHashTableSize = 101 }; | |||
friend struct Iterator; | |||
HashFunctionType hashFunctionToUse; | |||
Array<HashEntry*> hashSlots; | |||
int totalNumItems; | |||
TypeOfCriticalSectionToUse lock; | |||
int generateHashFor (KeyTypeParameter key, int numSlots) const | |||
{ | |||
const int hash = hashFunctionToUse.generateHash (key, numSlots); | |||
jassert (isPositiveAndBelow (hash, numSlots)); // your hash function is generating out-of-range numbers! | |||
return hash; | |||
} | |||
static inline HashEntry* getEntry (HashEntry* firstEntry, KeyType keyToLookFor) noexcept | |||
{ | |||
for (auto* entry = firstEntry; entry != nullptr; entry = entry->nextEntry) | |||
if (entry->key == keyToLookFor) | |||
return entry; | |||
return nullptr; | |||
} | |||
inline HashEntry* getSlot (KeyType key) const noexcept { return hashSlots.getUnchecked (generateHashFor (key, getNumSlots())); } | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (HashMap) | |||
}; | |||
} // namespace juce |
@@ -1,276 +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. | |||
============================================================================== | |||
*/ | |||
namespace juce | |||
{ | |||
struct HashMapTest : public UnitTest | |||
{ | |||
HashMapTest() : UnitTest ("HashMap", "Containers") {} | |||
void runTest() override | |||
{ | |||
doTest<AddElementsTest> ("AddElementsTest"); | |||
doTest<AccessTest> ("AccessTest"); | |||
doTest<RemoveTest> ("RemoveTest"); | |||
doTest<PersistantMemoryLocationOfValues> ("PersistantMemoryLocationOfValues"); | |||
} | |||
//============================================================================== | |||
struct AddElementsTest | |||
{ | |||
template <typename KeyType> | |||
static void run (UnitTest& u) | |||
{ | |||
AssociativeMap<KeyType, int> groundTruth; | |||
HashMap<KeyType, int> hashMap; | |||
RandomKeys<KeyType> keyOracle (300, 3827829); | |||
Random valueOracle (48735); | |||
int totalValues = 0; | |||
for (int i = 0; i < 10000; ++i) | |||
{ | |||
auto key = keyOracle.next(); | |||
auto value = valueOracle.nextInt(); | |||
bool contains = (groundTruth.find (key) != nullptr); | |||
u.expectEquals ((int) contains, (int) hashMap.contains (key)); | |||
groundTruth.add (key, value); | |||
hashMap.set (key, value); | |||
if (! contains) totalValues++; | |||
u.expectEquals (hashMap.size(), totalValues); | |||
} | |||
} | |||
}; | |||
struct AccessTest | |||
{ | |||
template <typename KeyType> | |||
static void run (UnitTest& u) | |||
{ | |||
AssociativeMap<KeyType, int> groundTruth; | |||
HashMap<KeyType, int> hashMap; | |||
fillWithRandomValues (hashMap, groundTruth); | |||
for (auto pair : groundTruth.pairs) | |||
u.expectEquals (hashMap[pair.key], pair.value); | |||
} | |||
}; | |||
struct RemoveTest | |||
{ | |||
template <typename KeyType> | |||
static void run (UnitTest& u) | |||
{ | |||
AssociativeMap<KeyType, int> groundTruth; | |||
HashMap<KeyType, int> hashMap; | |||
fillWithRandomValues (hashMap, groundTruth); | |||
auto n = groundTruth.size(); | |||
Random r (3827387); | |||
for (int i = 0; i < 100; ++i) | |||
{ | |||
auto idx = r.nextInt (n-- - 1); | |||
auto key = groundTruth.pairs.getReference (idx).key; | |||
groundTruth.pairs.remove (idx); | |||
hashMap.remove (key); | |||
u.expect (! hashMap.contains (key)); | |||
for (auto pair : groundTruth.pairs) | |||
u.expectEquals (hashMap[pair.key], pair.value); | |||
} | |||
} | |||
}; | |||
// ensure that the addresses of object references don't change | |||
struct PersistantMemoryLocationOfValues | |||
{ | |||
struct AddressAndValue { int value; const int* valueAddress; }; | |||
template <typename KeyType> | |||
static void run (UnitTest& u) | |||
{ | |||
AssociativeMap<KeyType, AddressAndValue> groundTruth; | |||
HashMap<KeyType, int> hashMap; | |||
RandomKeys<KeyType> keyOracle (300, 3827829); | |||
Random valueOracle (48735); | |||
for (int i = 0; i < 1000; ++i) | |||
{ | |||
auto key = keyOracle.next(); | |||
auto value = valueOracle.nextInt(); | |||
hashMap.set (key, value); | |||
if (auto* existing = groundTruth.find (key)) | |||
{ | |||
// don't change the address: only the value | |||
existing->value = value; | |||
} | |||
else | |||
{ | |||
groundTruth.add (key, { value, &hashMap.getReference (key) }); | |||
} | |||
for (auto pair : groundTruth.pairs) | |||
{ | |||
const auto& hashMapValue = hashMap.getReference (pair.key); | |||
u.expectEquals (hashMapValue, pair.value.value); | |||
u.expect (&hashMapValue == pair.value.valueAddress); | |||
} | |||
} | |||
auto n = groundTruth.size(); | |||
Random r (3827387); | |||
for (int i = 0; i < 100; ++i) | |||
{ | |||
auto idx = r.nextInt (n-- - 1); | |||
auto key = groundTruth.pairs.getReference (idx).key; | |||
groundTruth.pairs.remove (idx); | |||
hashMap.remove (key); | |||
for (auto pair : groundTruth.pairs) | |||
{ | |||
const auto& hashMapValue = hashMap.getReference (pair.key); | |||
u.expectEquals (hashMapValue, pair.value.value); | |||
u.expect (&hashMapValue == pair.value.valueAddress); | |||
} | |||
} | |||
} | |||
}; | |||
//============================================================================== | |||
template <class Test> | |||
void doTest (const String& testName) | |||
{ | |||
beginTest (testName); | |||
Test::template run<int> (*this); | |||
Test::template run<void*> (*this); | |||
Test::template run<String> (*this); | |||
} | |||
//============================================================================== | |||
template <typename KeyType, typename ValueType> | |||
struct AssociativeMap | |||
{ | |||
struct KeyValuePair { KeyType key; ValueType value; }; | |||
ValueType* find (KeyType key) | |||
{ | |||
auto n = pairs.size(); | |||
for (int i = 0; i < n; ++i) | |||
{ | |||
auto& pair = pairs.getReference (i); | |||
if (pair.key == key) | |||
return &pair.value; | |||
} | |||
return nullptr; | |||
} | |||
void add (KeyType key, ValueType value) | |||
{ | |||
if (ValueType* v = find (key)) | |||
*v = value; | |||
else | |||
pairs.add ({key, value}); | |||
} | |||
int size() const { return pairs.size(); } | |||
Array<KeyValuePair> pairs; | |||
}; | |||
template <typename KeyType, typename ValueType> | |||
static void fillWithRandomValues (HashMap<KeyType, int>& hashMap, AssociativeMap<KeyType, ValueType>& groundTruth) | |||
{ | |||
RandomKeys<KeyType> keyOracle (300, 3827829); | |||
Random valueOracle (48735); | |||
for (int i = 0; i < 10000; ++i) | |||
{ | |||
auto key = keyOracle.next(); | |||
auto value = valueOracle.nextInt(); | |||
groundTruth.add (key, value); | |||
hashMap.set (key, value); | |||
} | |||
} | |||
//============================================================================== | |||
template <typename KeyType> | |||
class RandomKeys | |||
{ | |||
public: | |||
RandomKeys (int maxUniqueKeys, int seed) : r (seed) | |||
{ | |||
for (int i = 0; i < maxUniqueKeys; ++i) | |||
keys.add (generateRandomKey (r)); | |||
} | |||
const KeyType& next() | |||
{ | |||
int i = r.nextInt (keys.size() - 1); | |||
return keys.getReference (i); | |||
} | |||
private: | |||
static KeyType generateRandomKey (Random&); | |||
Random r; | |||
Array<KeyType> keys; | |||
}; | |||
}; | |||
template <> int HashMapTest::RandomKeys<int> ::generateRandomKey (Random& rnd) { return rnd.nextInt(); } | |||
template <> void* HashMapTest::RandomKeys<void*>::generateRandomKey (Random& rnd) { return reinterpret_cast<void*> (rnd.nextInt64()); } | |||
template <> String HashMapTest::RandomKeys<String>::generateRandomKey (Random& rnd) | |||
{ | |||
String str; | |||
int len = rnd.nextInt (8)+1; | |||
for (int i = 0; i < len; ++i) | |||
str += static_cast<char> (rnd.nextInt (95) + 32); | |||
return str; | |||
} | |||
static HashMapTest hashMapTest; | |||
} // namespace juce |
@@ -1,361 +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. | |||
============================================================================== | |||
*/ | |||
namespace juce | |||
{ | |||
//============================================================================== | |||
/** | |||
Helps to manipulate singly-linked lists of objects. | |||
For objects that are designed to contain a pointer to the subsequent item in the | |||
list, this class contains methods to deal with the list. To use it, the ObjectType | |||
class that it points to must contain a LinkedListPointer called nextListItem, e.g. | |||
@code | |||
struct MyObject | |||
{ | |||
int x, y, z; | |||
// A linkable object must contain a member with this name and type, which must be | |||
// accessible by the LinkedListPointer class. (This doesn't mean it has to be public - | |||
// you could make your class a friend of a LinkedListPointer<MyObject> instead). | |||
LinkedListPointer<MyObject> nextListItem; | |||
}; | |||
LinkedListPointer<MyObject> myList; | |||
myList.append (new MyObject()); | |||
myList.append (new MyObject()); | |||
int numItems = myList.size(); // returns 2 | |||
MyObject* lastInList = myList.getLast(); | |||
@endcode | |||
*/ | |||
template <class ObjectType> | |||
class LinkedListPointer | |||
{ | |||
public: | |||
//============================================================================== | |||
/** Creates a null pointer to an empty list. */ | |||
LinkedListPointer() noexcept | |||
: item (nullptr) | |||
{ | |||
} | |||
/** Creates a pointer to a list whose head is the item provided. */ | |||
explicit LinkedListPointer (ObjectType* const headItem) noexcept | |||
: item (headItem) | |||
{ | |||
} | |||
/** Sets this pointer to point to a new list. */ | |||
LinkedListPointer& operator= (ObjectType* const newItem) noexcept | |||
{ | |||
item = newItem; | |||
return *this; | |||
} | |||
LinkedListPointer (LinkedListPointer&& other) noexcept | |||
: item (other.item) | |||
{ | |||
other.item = nullptr; | |||
} | |||
LinkedListPointer& operator= (LinkedListPointer&& other) noexcept | |||
{ | |||
jassert (this != &other); // hopefully the compiler should make this situation impossible! | |||
item = other.item; | |||
other.item = nullptr; | |||
return *this; | |||
} | |||
//============================================================================== | |||
/** Returns the item which this pointer points to. */ | |||
inline operator ObjectType*() const noexcept | |||
{ | |||
return item; | |||
} | |||
/** Returns the item which this pointer points to. */ | |||
inline ObjectType* get() const noexcept | |||
{ | |||
return item; | |||
} | |||
/** Returns the last item in the list which this pointer points to. | |||
This will iterate the list and return the last item found. Obviously the speed | |||
of this operation will be proportional to the size of the list. If the list is | |||
empty the return value will be this object. | |||
If you're planning on appending a number of items to your list, it's much more | |||
efficient to use the Appender class than to repeatedly call getLast() to find the end. | |||
*/ | |||
LinkedListPointer& getLast() noexcept | |||
{ | |||
LinkedListPointer* l = this; | |||
while (l->item != nullptr) | |||
l = &(l->item->nextListItem); | |||
return *l; | |||
} | |||
/** Returns the number of items in the list. | |||
Obviously with a simple linked list, getting the size involves iterating the list, so | |||
this can be a lengthy operation - be careful when using this method in your code. | |||
*/ | |||
int size() const noexcept | |||
{ | |||
int total = 0; | |||
for (ObjectType* i = item; i != nullptr; i = i->nextListItem) | |||
++total; | |||
return total; | |||
} | |||
/** Returns the item at a given index in the list. | |||
Since the only way to find an item is to iterate the list, this operation can obviously | |||
be slow, depending on its size, so you should be careful when using this in algorithms. | |||
*/ | |||
LinkedListPointer& operator[] (int index) noexcept | |||
{ | |||
LinkedListPointer* l = this; | |||
while (--index >= 0 && l->item != nullptr) | |||
l = &(l->item->nextListItem); | |||
return *l; | |||
} | |||
/** Returns the item at a given index in the list. | |||
Since the only way to find an item is to iterate the list, this operation can obviously | |||
be slow, depending on its size, so you should be careful when using this in algorithms. | |||
*/ | |||
const LinkedListPointer& operator[] (int index) const noexcept | |||
{ | |||
const LinkedListPointer* l = this; | |||
while (--index >= 0 && l->item != nullptr) | |||
l = &(l->item->nextListItem); | |||
return *l; | |||
} | |||
/** Returns true if the list contains the given item. */ | |||
bool contains (const ObjectType* const itemToLookFor) const noexcept | |||
{ | |||
for (ObjectType* i = item; i != nullptr; i = i->nextListItem) | |||
if (itemToLookFor == i) | |||
return true; | |||
return false; | |||
} | |||
//============================================================================== | |||
/** Inserts an item into the list, placing it before the item that this pointer | |||
currently points to. | |||
*/ | |||
void insertNext (ObjectType* const newItem) | |||
{ | |||
jassert (newItem != nullptr); | |||
jassert (newItem->nextListItem == nullptr); | |||
newItem->nextListItem = item; | |||
item = newItem; | |||
} | |||
/** Inserts an item at a numeric index in the list. | |||
Obviously this will involve iterating the list to find the item at the given index, | |||
so be careful about the impact this may have on execution time. | |||
*/ | |||
void insertAtIndex (int index, ObjectType* newItem) | |||
{ | |||
jassert (newItem != nullptr); | |||
LinkedListPointer* l = this; | |||
while (index != 0 && l->item != nullptr) | |||
{ | |||
l = &(l->item->nextListItem); | |||
--index; | |||
} | |||
l->insertNext (newItem); | |||
} | |||
/** Replaces the object that this pointer points to, appending the rest of the list to | |||
the new object, and returning the old one. | |||
*/ | |||
ObjectType* replaceNext (ObjectType* const newItem) noexcept | |||
{ | |||
jassert (newItem != nullptr); | |||
jassert (newItem->nextListItem == nullptr); | |||
ObjectType* const oldItem = item; | |||
item = newItem; | |||
item->nextListItem = oldItem->nextListItem.item; | |||
oldItem->nextListItem.item = nullptr; | |||
return oldItem; | |||
} | |||
/** Adds an item to the end of the list. | |||
This operation involves iterating the whole list, so can be slow - if you need to | |||
append a number of items to your list, it's much more efficient to use the Appender | |||
class than to repeatedly call append(). | |||
*/ | |||
void append (ObjectType* const newItem) | |||
{ | |||
getLast().item = newItem; | |||
} | |||
/** Creates copies of all the items in another list and adds them to this one. | |||
This will use the ObjectType's copy constructor to try to create copies of each | |||
item in the other list, and appends them to this list. | |||
*/ | |||
void addCopyOfList (const LinkedListPointer& other) | |||
{ | |||
LinkedListPointer* insertPoint = this; | |||
for (ObjectType* i = other.item; i != nullptr; i = i->nextListItem) | |||
{ | |||
insertPoint->insertNext (new ObjectType (*i)); | |||
insertPoint = &(insertPoint->item->nextListItem); | |||
} | |||
} | |||
/** Removes the head item from the list. | |||
This won't delete the object that is removed, but returns it, so the caller can | |||
delete it if necessary. | |||
*/ | |||
ObjectType* removeNext() noexcept | |||
{ | |||
ObjectType* const oldItem = item; | |||
if (oldItem != nullptr) | |||
{ | |||
item = oldItem->nextListItem; | |||
oldItem->nextListItem.item = nullptr; | |||
} | |||
return oldItem; | |||
} | |||
/** Removes a specific item from the list. | |||
Note that this will not delete the item, it simply unlinks it from the list. | |||
*/ | |||
void remove (ObjectType* const itemToRemove) | |||
{ | |||
if (LinkedListPointer* const l = findPointerTo (itemToRemove)) | |||
l->removeNext(); | |||
} | |||
/** Iterates the list, calling the delete operator on all of its elements and | |||
leaving this pointer empty. | |||
*/ | |||
void deleteAll() | |||
{ | |||
while (item != nullptr) | |||
{ | |||
ObjectType* const oldItem = item; | |||
item = oldItem->nextListItem; | |||
delete oldItem; | |||
} | |||
} | |||
/** Finds a pointer to a given item. | |||
If the item is found in the list, this returns the pointer that points to it. If | |||
the item isn't found, this returns null. | |||
*/ | |||
LinkedListPointer* findPointerTo (ObjectType* const itemToLookFor) noexcept | |||
{ | |||
LinkedListPointer* l = this; | |||
while (l->item != nullptr) | |||
{ | |||
if (l->item == itemToLookFor) | |||
return l; | |||
l = &(l->item->nextListItem); | |||
} | |||
return nullptr; | |||
} | |||
/** Copies the items in the list to an array. | |||
The destArray must contain enough elements to hold the entire list - no checks are | |||
made for this! | |||
*/ | |||
void copyToArray (ObjectType** destArray) const noexcept | |||
{ | |||
jassert (destArray != nullptr); | |||
for (ObjectType* i = item; i != nullptr; i = i->nextListItem) | |||
*destArray++ = i; | |||
} | |||
/** Swaps this pointer with another one */ | |||
void swapWith (LinkedListPointer& other) noexcept | |||
{ | |||
std::swap (item, other.item); | |||
} | |||
//============================================================================== | |||
/** | |||
Allows efficient repeated insertions into a list. | |||
You can create an Appender object which points to the last element in your | |||
list, and then repeatedly call Appender::append() to add items to the end | |||
of the list in O(1) time. | |||
*/ | |||
class Appender | |||
{ | |||
public: | |||
/** Creates an appender which will add items to the given list. | |||
*/ | |||
Appender (LinkedListPointer& endOfListPointer) noexcept | |||
: endOfList (&endOfListPointer) | |||
{ | |||
// This can only be used to add to the end of a list. | |||
jassert (endOfListPointer.item == nullptr); | |||
} | |||
/** Appends an item to the list. */ | |||
void append (ObjectType* const newItem) noexcept | |||
{ | |||
*endOfList = newItem; | |||
endOfList = &(newItem->nextListItem); | |||
} | |||
private: | |||
LinkedListPointer* endOfList; | |||
JUCE_DECLARE_NON_COPYABLE (Appender) | |||
}; | |||
private: | |||
//============================================================================== | |||
ObjectType* item; | |||
JUCE_DECLARE_NON_COPYABLE (LinkedListPointer) | |||
}; | |||
} // namespace juce |
@@ -1,189 +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. | |||
============================================================================== | |||
*/ | |||
namespace juce | |||
{ | |||
#if JUCE_UNIT_TESTS | |||
struct ListenerBase | |||
{ | |||
ListenerBase (int& counter) : c (counter) {} | |||
virtual ~ListenerBase() {} | |||
// Required to supress VS2013 compiler warnings | |||
ListenerBase& operator= (const ListenerBase&) = delete; | |||
virtual void f () = 0; | |||
virtual void f (void*) = 0; | |||
virtual void f (void*, void*) = 0; | |||
virtual void f (void*, void*, void*) = 0; | |||
virtual void f (void*, void*, void*, void*) = 0; | |||
virtual void f (void*, void*, void*, void*, void*) = 0; | |||
virtual void f (void*, void*, void*, void*, void*, void*) = 0; | |||
int& c; | |||
}; | |||
struct Listener1 : public ListenerBase | |||
{ | |||
Listener1 (int& counter) : ListenerBase (counter) {} | |||
// Required to supress VS2013 compiler warnings | |||
Listener1& operator= (const Listener1&) = delete; | |||
void f () override { c += 1; } | |||
void f (void*) override { c += 2; } | |||
void f (void*, void*) override { c += 3; } | |||
void f (void*, void*, void*) override { c += 4; } | |||
void f (void*, void*, void*, void*) override { c += 5; } | |||
void f (void*, void*, void*, void*, void*) override { c += 6; } | |||
void f (void*, void*, void*, void*, void*, void*) override { c += 7; } | |||
}; | |||
struct Listener2 : public ListenerBase | |||
{ | |||
Listener2 (int& counter) : ListenerBase (counter) {} | |||
// Required to supress VS2013 compiler warnings | |||
Listener1& operator= (const Listener1&) = delete; | |||
void f () override { c -= 2; } | |||
void f (void*) override { c -= 4; } | |||
void f (void*, void*) override { c -= 6; } | |||
void f (void*, void*, void*) override { c -= 8; } | |||
void f (void*, void*, void*, void*) override { c -= 10; } | |||
void f (void*, void*, void*, void*, void*) override { c -= 12; } | |||
void f (void*, void*, void*, void*, void*, void*) override { c -= 14; } | |||
}; | |||
class ListenerListTests : public UnitTest | |||
{ | |||
public: | |||
ListenerListTests() : UnitTest ("ListenerList", "Containers") {} | |||
template <typename... Args> | |||
void callHelper (std::vector<int>& expectedCounterValues) | |||
{ | |||
counter = 0; | |||
listeners.call (&ListenerBase::f); | |||
expect (counter == expectedCounterValues[0]); | |||
ListenerList<ListenerBase>::DummyBailOutChecker boc; | |||
counter = 0; | |||
listeners.callChecked (boc, &ListenerBase::f); | |||
expect (counter == expectedCounterValues[0]); | |||
} | |||
template<typename T, typename... Args> | |||
void callHelper (std::vector<int>& expectedCounterValues, T first, Args... args) | |||
{ | |||
const int expected = expectedCounterValues[sizeof... (args) + 1]; | |||
counter = 0; | |||
listeners.call (&ListenerBase::f, first, args...); | |||
expect (counter == expected); | |||
ListenerList<ListenerBase>::DummyBailOutChecker boc; | |||
counter = 0; | |||
listeners.callChecked (boc, &ListenerBase::f, first, args...); | |||
expect (counter == expected); | |||
callHelper (expectedCounterValues, args...); | |||
} | |||
template <typename... Args> | |||
void callExcludingHelper (ListenerBase* listenerToExclude, | |||
std::vector<int>& expectedCounterValues) | |||
{ | |||
counter = 0; | |||
listeners.callExcluding (listenerToExclude, &ListenerBase::f); | |||
expect (counter == expectedCounterValues[0]); | |||
ListenerList<ListenerBase>::DummyBailOutChecker boc; | |||
counter = 0; | |||
listeners.callCheckedExcluding (listenerToExclude, boc, &ListenerBase::f); | |||
expect (counter == expectedCounterValues[0]); | |||
} | |||
template<typename T, typename... Args> | |||
void callExcludingHelper (ListenerBase* listenerToExclude, | |||
std::vector<int>& expectedCounterValues, T first, Args... args) | |||
{ | |||
const int expected = expectedCounterValues[sizeof... (args) + 1]; | |||
counter = 0; | |||
listeners.callExcluding (listenerToExclude, &ListenerBase::f, first, args...); | |||
expect (counter == expected); | |||
ListenerList<ListenerBase>::DummyBailOutChecker boc; | |||
counter = 0; | |||
listeners.callCheckedExcluding (listenerToExclude, boc, &ListenerBase::f, first, args...); | |||
expect (counter == expected); | |||
callExcludingHelper (listenerToExclude, expectedCounterValues, args...); | |||
} | |||
void runTest() override | |||
{ | |||
counter = 0; | |||
beginTest ("Call single listener"); | |||
listeners.add (&listener1); | |||
std::vector<int> expectedCounterValues; | |||
for (int i = 1; i < 8; ++i) | |||
expectedCounterValues.push_back (i); | |||
callHelper (expectedCounterValues, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr); | |||
beginTest ("Call multiple listeners"); | |||
listeners.add (&listener2); | |||
expectedCounterValues.clear(); | |||
for (int i = 1; i < 8; ++i) | |||
expectedCounterValues.push_back (-i); | |||
callHelper (expectedCounterValues, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr); | |||
beginTest ("Call listeners excluding"); | |||
expectedCounterValues.clear(); | |||
for (int i = 1; i < 8; ++i) | |||
expectedCounterValues.push_back (i); | |||
callExcludingHelper (&listener2, expectedCounterValues, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr); | |||
listeners.remove (&listener1); | |||
listeners.remove (&listener2); | |||
} | |||
int counter = 0; | |||
ListenerList<ListenerBase> listeners; | |||
Listener1 listener1 {counter}; | |||
Listener2 listener2 {counter}; | |||
}; | |||
static ListenerListTests listenerListTests; | |||
#endif | |||
} // namespace juce |
@@ -1,544 +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. | |||
============================================================================== | |||
*/ | |||
namespace juce | |||
{ | |||
//============================================================================== | |||
/** | |||
Holds a set of objects and can invoke a member function callback on each object | |||
in the set with a single call. | |||
Use a ListenerList to manage a set of objects which need a callback, and you | |||
can invoke a member function by simply calling call() or callChecked(). | |||
E.g. | |||
@code | |||
class MyListenerType | |||
{ | |||
public: | |||
void myCallbackMethod (int foo, bool bar); | |||
}; | |||
ListenerList <MyListenerType> listeners; | |||
listeners.add (someCallbackObjects...); | |||
// This will invoke myCallbackMethod (1234, true) on each of the objects | |||
// in the list... | |||
listeners.call (&MyListenerType::myCallbackMethod, 1234, true); | |||
@endcode | |||
If you add or remove listeners from the list during one of the callbacks - i.e. while | |||
it's in the middle of iterating the listeners, then it's guaranteed that no listeners | |||
will be mistakenly called after they've been removed, but it may mean that some of the | |||
listeners could be called more than once, or not at all, depending on the list's order. | |||
Sometimes, there's a chance that invoking one of the callbacks might result in the | |||
list itself being deleted while it's still iterating - to survive this situation, you can | |||
use callChecked() instead of call(), passing it a local object to act as a "BailOutChecker". | |||
The BailOutChecker must implement a method of the form "bool shouldBailOut()", and | |||
the list will check this after each callback to determine whether it should abort the | |||
operation. For an example of a bail-out checker, see the Component::BailOutChecker class, | |||
which can be used to check when a Component has been deleted. See also | |||
ListenerList::DummyBailOutChecker, which is a dummy checker that always returns false. | |||
*/ | |||
template <class ListenerClass, | |||
class ArrayType = Array<ListenerClass*> > | |||
class ListenerList | |||
{ | |||
#ifndef DOXYGEN | |||
#define LL_TEMPLATE(a) typename P##a | |||
#define LL_PARAM(a) typename TypeHelpers::ParameterType<P##a>::type param##a | |||
#endif | |||
public: | |||
//============================================================================== | |||
/** Creates an empty list. */ | |||
ListenerList() | |||
{ | |||
} | |||
/** Destructor. */ | |||
~ListenerList() | |||
{ | |||
} | |||
//============================================================================== | |||
/** Adds a listener to the list. | |||
A listener can only be added once, so if the listener is already in the list, | |||
this method has no effect. | |||
@see remove | |||
*/ | |||
void add (ListenerClass* const listenerToAdd) | |||
{ | |||
// Listeners can't be null pointers! | |||
jassert (listenerToAdd != nullptr); | |||
if (listenerToAdd != nullptr) | |||
listeners.addIfNotAlreadyThere (listenerToAdd); | |||
} | |||
/** Removes a listener from the list. | |||
If the listener wasn't in the list, this has no effect. | |||
*/ | |||
void remove (ListenerClass* const listenerToRemove) | |||
{ | |||
// Listeners can't be null pointers! | |||
jassert (listenerToRemove != nullptr); | |||
listeners.removeFirstMatchingValue (listenerToRemove); | |||
} | |||
/** Returns the number of registered listeners. */ | |||
int size() const noexcept | |||
{ | |||
return listeners.size(); | |||
} | |||
/** Returns true if any listeners are registered. */ | |||
bool isEmpty() const noexcept | |||
{ | |||
return listeners.size() == 0; | |||
} | |||
/** Clears the list. */ | |||
void clear() | |||
{ | |||
listeners.clear(); | |||
} | |||
/** Returns true if the specified listener has been added to the list. */ | |||
bool contains (ListenerClass* const listener) const noexcept | |||
{ | |||
return listeners.contains (listener); | |||
} | |||
//============================================================================== | |||
/** Calls a member function on each listener in the list, with no parameters. */ | |||
void call (void (ListenerClass::*callbackFunction) ()) | |||
{ | |||
callChecked (static_cast<const DummyBailOutChecker&> (DummyBailOutChecker()), callbackFunction); | |||
} | |||
/** Calls a member function, with no parameters, on all but the specified listener in the list. | |||
This can be useful if the caller is also a listener and needs to exclude itself. | |||
*/ | |||
void callExcluding (ListenerClass* listenerToExclude, void (ListenerClass::*callbackFunction) ()) | |||
{ | |||
callCheckedExcluding (listenerToExclude, | |||
static_cast<const DummyBailOutChecker&> (DummyBailOutChecker()), callbackFunction); | |||
} | |||
/** Calls a member function on each listener in the list, with no parameters and a bail-out-checker. | |||
See the class description for info about writing a bail-out checker. | |||
*/ | |||
template <class BailOutCheckerType> | |||
void callChecked (const BailOutCheckerType& bailOutChecker, | |||
void (ListenerClass::*callbackFunction) ()) | |||
{ | |||
for (Iterator<BailOutCheckerType, ThisType> iter (*this); iter.next (bailOutChecker);) | |||
(iter.getListener()->*callbackFunction) (); | |||
} | |||
/** Calls a member function on all but the specified listener in the list with a bail-out-checker. | |||
This can be useful if the caller is also a listener and needs to exclude itself. See the class | |||
description for info about writing a bail-out checker. | |||
*/ | |||
template <class BailOutCheckerType> | |||
void callCheckedExcluding (ListenerClass* listenerToExclude, | |||
const BailOutCheckerType& bailOutChecker, | |||
void (ListenerClass::*callbackFunction) ()) | |||
{ | |||
for (Iterator<BailOutCheckerType, ThisType> iter (*this); iter.next (bailOutChecker);) | |||
if (iter.getListener() != listenerToExclude) | |||
(iter.getListener()->*callbackFunction) (); | |||
} | |||
//============================================================================== | |||
/** Calls a member function on each listener in the list, with 1 parameter. */ | |||
template <LL_TEMPLATE(1)> | |||
void call (void (ListenerClass::*callbackFunction) (P1), LL_PARAM(1)) | |||
{ | |||
for (Iterator<DummyBailOutChecker, ThisType> iter (*this); iter.next();) | |||
(iter.getListener()->*callbackFunction) (param1); | |||
} | |||
/** Calls a member function, with 1 parameter, on all but the specified listener in the list. | |||
This can be useful if the caller is also a listener and needs to exclude itself. | |||
*/ | |||
template <LL_TEMPLATE(1)> | |||
void callExcluding (ListenerClass* listenerToExclude, | |||
void (ListenerClass::*callbackFunction) (P1), LL_PARAM(1)) | |||
{ | |||
for (Iterator<DummyBailOutChecker, ThisType> iter (*this); iter.next();) | |||
if (iter.getListener() != listenerToExclude) | |||
(iter.getListener()->*callbackFunction) (param1); | |||
} | |||
/** Calls a member function on each listener in the list, with 1 parameter and a bail-out-checker. | |||
See the class description for info about writing a bail-out checker. | |||
*/ | |||
template <class BailOutCheckerType, LL_TEMPLATE(1)> | |||
void callChecked (const BailOutCheckerType& bailOutChecker, | |||
void (ListenerClass::*callbackFunction) (P1), | |||
LL_PARAM(1)) | |||
{ | |||
for (Iterator<BailOutCheckerType, ThisType> iter (*this); iter.next (bailOutChecker);) | |||
(iter.getListener()->*callbackFunction) (param1); | |||
} | |||
/** Calls a member function, with 1 parameter, on all but the specified listener in the list | |||
with a bail-out-checker. This can be useful if the caller is also a listener and needs to | |||
exclude itself. See the class description for info about writing a bail-out checker. | |||
*/ | |||
template <class BailOutCheckerType, LL_TEMPLATE(1)> | |||
void callCheckedExcluding (ListenerClass* listenerToExclude, | |||
const BailOutCheckerType& bailOutChecker, | |||
void (ListenerClass::*callbackFunction) (P1), | |||
LL_PARAM(1)) | |||
{ | |||
for (Iterator<BailOutCheckerType, ThisType> iter (*this); iter.next (bailOutChecker);) | |||
if (iter.getListener() != listenerToExclude) | |||
(iter.getListener()->*callbackFunction) (param1); | |||
} | |||
//============================================================================== | |||
/** Calls a member function on each listener in the list, with 2 parameters. */ | |||
template <LL_TEMPLATE(1), LL_TEMPLATE(2)> | |||
void call (void (ListenerClass::*callbackFunction) (P1, P2), | |||
LL_PARAM(1), LL_PARAM(2)) | |||
{ | |||
for (Iterator<DummyBailOutChecker, ThisType> iter (*this); iter.next();) | |||
(iter.getListener()->*callbackFunction) (param1, param2); | |||
} | |||
/** Calls a member function, with 2 parameters, on all but the specified listener in the list. | |||
This can be useful if the caller is also a listener and needs to exclude itself. | |||
*/ | |||
template <LL_TEMPLATE(1), LL_TEMPLATE(2)> | |||
void callExcluding (ListenerClass* listenerToExclude, | |||
void (ListenerClass::*callbackFunction) (P1, P2), | |||
LL_PARAM(1), LL_PARAM(2)) | |||
{ | |||
for (Iterator<DummyBailOutChecker, ThisType> iter (*this); iter.next();) | |||
if (iter.getListener() != listenerToExclude) | |||
(iter.getListener()->*callbackFunction) (param1, param2); | |||
} | |||
/** Calls a member function on each listener in the list, with 2 parameters and a bail-out-checker. | |||
See the class description for info about writing a bail-out checker. | |||
*/ | |||
template <class BailOutCheckerType, LL_TEMPLATE(1), LL_TEMPLATE(2)> | |||
void callChecked (const BailOutCheckerType& bailOutChecker, | |||
void (ListenerClass::*callbackFunction) (P1, P2), | |||
LL_PARAM(1), LL_PARAM(2)) | |||
{ | |||
for (Iterator<BailOutCheckerType, ThisType> iter (*this); iter.next (bailOutChecker);) | |||
(iter.getListener()->*callbackFunction) (param1, param2); | |||
} | |||
/** Calls a member function, with 2 parameters, on all but the specified listener in the list | |||
with a bail-out-checker. This can be useful if the caller is also a listener and needs to | |||
exclude itself. See the class description for info about writing a bail-out checker. | |||
*/ | |||
template <class BailOutCheckerType, LL_TEMPLATE(1), LL_TEMPLATE(2)> | |||
void callCheckedExcluding (ListenerClass* listenerToExclude, | |||
const BailOutCheckerType& bailOutChecker, | |||
void (ListenerClass::*callbackFunction) (P1, P2), | |||
LL_PARAM(1), LL_PARAM(2)) | |||
{ | |||
for (Iterator<BailOutCheckerType, ThisType> iter (*this); iter.next (bailOutChecker);) | |||
if (iter.getListener() != listenerToExclude) | |||
(iter.getListener()->*callbackFunction) (param1, param2); | |||
} | |||
//============================================================================== | |||
/** Calls a member function on each listener in the list, with 3 parameters. */ | |||
template <LL_TEMPLATE(1), LL_TEMPLATE(2), LL_TEMPLATE(3)> | |||
void call (void (ListenerClass::*callbackFunction) (P1, P2, P3), | |||
LL_PARAM(1), LL_PARAM(2), LL_PARAM(3)) | |||
{ | |||
for (Iterator<DummyBailOutChecker, ThisType> iter (*this); iter.next();) | |||
(iter.getListener()->*callbackFunction) (param1, param2, param3); | |||
} | |||
/** Calls a member function, with 3 parameters, on all but the specified listener in the list. | |||
This can be useful if the caller is also a listener and needs to exclude itself. | |||
*/ | |||
template <LL_TEMPLATE(1), LL_TEMPLATE(2), LL_TEMPLATE(3)> | |||
void callExcluding (ListenerClass* listenerToExclude, | |||
void (ListenerClass::*callbackFunction) (P1, P2, P3), | |||
LL_PARAM(1), LL_PARAM(2), LL_PARAM(3)) | |||
{ | |||
for (Iterator<DummyBailOutChecker, ThisType> iter (*this); iter.next();) | |||
if (iter.getListener() != listenerToExclude) | |||
(iter.getListener()->*callbackFunction) (param1, param2, param3); | |||
} | |||
/** Calls a member function on each listener in the list, with 3 parameters and a bail-out-checker. | |||
See the class description for info about writing a bail-out checker. | |||
*/ | |||
template <class BailOutCheckerType, LL_TEMPLATE(1), LL_TEMPLATE(2), LL_TEMPLATE(3)> | |||
void callChecked (const BailOutCheckerType& bailOutChecker, | |||
void (ListenerClass::*callbackFunction) (P1, P2, P3), | |||
LL_PARAM(1), LL_PARAM(2), LL_PARAM(3)) | |||
{ | |||
for (Iterator<BailOutCheckerType, ThisType> iter (*this); iter.next (bailOutChecker);) | |||
(iter.getListener()->*callbackFunction) (param1, param2, param3); | |||
} | |||
/** Calls a member function, with 3 parameters, on all but the specified listener in the list | |||
with a bail-out-checker. This can be useful if the caller is also a listener and needs to | |||
exclude itself. See the class description for info about writing a bail-out checker. | |||
*/ | |||
template <class BailOutCheckerType, LL_TEMPLATE(1), LL_TEMPLATE(2), LL_TEMPLATE(3)> | |||
void callCheckedExcluding (ListenerClass* listenerToExclude, | |||
const BailOutCheckerType& bailOutChecker, | |||
void (ListenerClass::*callbackFunction) (P1, P2, P3), | |||
LL_PARAM(1), LL_PARAM(2), LL_PARAM(3)) | |||
{ | |||
for (Iterator<BailOutCheckerType, ThisType> iter (*this); iter.next (bailOutChecker);) | |||
if (iter.getListener() != listenerToExclude) | |||
(iter.getListener()->*callbackFunction) (param1, param2, param3); | |||
} | |||
//============================================================================== | |||
/** Calls a member function on each listener in the list, with 4 parameters. */ | |||
template <LL_TEMPLATE(1), LL_TEMPLATE(2), LL_TEMPLATE(3), LL_TEMPLATE(4)> | |||
void call (void (ListenerClass::*callbackFunction) (P1, P2, P3, P4), | |||
LL_PARAM(1), LL_PARAM(2), LL_PARAM(3), LL_PARAM(4)) | |||
{ | |||
for (Iterator<DummyBailOutChecker, ThisType> iter (*this); iter.next();) | |||
(iter.getListener()->*callbackFunction) (param1, param2, param3, param4); | |||
} | |||
/** Calls a member function, with 4 parameters, on all but the specified listener in the list. | |||
This can be useful if the caller is also a listener and needs to exclude itself. | |||
*/ | |||
template <LL_TEMPLATE(1), LL_TEMPLATE(2), LL_TEMPLATE(3), LL_TEMPLATE(4)> | |||
void callExcluding (ListenerClass* listenerToExclude, | |||
void (ListenerClass::*callbackFunction) (P1, P2, P3, P4), | |||
LL_PARAM(1), LL_PARAM(2), LL_PARAM(3), LL_PARAM(4)) | |||
{ | |||
for (Iterator<DummyBailOutChecker, ThisType> iter (*this); iter.next();) | |||
if (iter.getListener() != listenerToExclude) | |||
(iter.getListener()->*callbackFunction) (param1, param2, param3, param4); | |||
} | |||
/** Calls a member function on each listener in the list, with 4 parameters and a bail-out-checker. | |||
See the class description for info about writing a bail-out checker. | |||
*/ | |||
template <class BailOutCheckerType, LL_TEMPLATE(1), LL_TEMPLATE(2), LL_TEMPLATE(3), LL_TEMPLATE(4)> | |||
void callChecked (const BailOutCheckerType& bailOutChecker, | |||
void (ListenerClass::*callbackFunction) (P1, P2, P3, P4), | |||
LL_PARAM(1), LL_PARAM(2), LL_PARAM(3), LL_PARAM(4)) | |||
{ | |||
for (Iterator<BailOutCheckerType, ThisType> iter (*this); iter.next (bailOutChecker);) | |||
(iter.getListener()->*callbackFunction) (param1, param2, param3, param4); | |||
} | |||
/** Calls a member function, with 4 parameters, on all but the specified listener in the list | |||
with a bail-out-checker. This can be useful if the caller is also a listener and needs to | |||
exclude itself. See the class description for info about writing a bail-out checker. | |||
*/ | |||
template <class BailOutCheckerType, LL_TEMPLATE(1), LL_TEMPLATE(2), LL_TEMPLATE(3), LL_TEMPLATE(4)> | |||
void callCheckedExcluding (ListenerClass* listenerToExclude, | |||
const BailOutCheckerType& bailOutChecker, | |||
void (ListenerClass::*callbackFunction) (P1, P2, P3, P4), | |||
LL_PARAM(1), LL_PARAM(2), LL_PARAM(3), LL_PARAM(4)) | |||
{ | |||
for (Iterator<BailOutCheckerType, ThisType> iter (*this); iter.next (bailOutChecker);) | |||
if (iter.getListener() != listenerToExclude) | |||
(iter.getListener()->*callbackFunction) (param1, param2, param3, param4); | |||
} | |||
//============================================================================== | |||
/** Calls a member function on each listener in the list, with 5 parameters. */ | |||
template <LL_TEMPLATE(1), LL_TEMPLATE(2), LL_TEMPLATE(3), LL_TEMPLATE(4), LL_TEMPLATE(5)> | |||
void call (void (ListenerClass::*callbackFunction) (P1, P2, P3, P4, P5), | |||
LL_PARAM(1), LL_PARAM(2), LL_PARAM(3), LL_PARAM(4), LL_PARAM(5)) | |||
{ | |||
for (Iterator<DummyBailOutChecker, ThisType> iter (*this); iter.next();) | |||
(iter.getListener()->*callbackFunction) (param1, param2, param3, param4, param5); | |||
} | |||
/** Calls a member function, with 5 parameters, on all but the specified listener in the list. | |||
This can be useful if the caller is also a listener and needs to exclude itself. | |||
*/ | |||
template <LL_TEMPLATE(1), LL_TEMPLATE(2), LL_TEMPLATE(3), LL_TEMPLATE(4), LL_TEMPLATE(5)> | |||
void callExcluding (ListenerClass* listenerToExclude, | |||
void (ListenerClass::*callbackFunction) (P1, P2, P3, P4, P5), | |||
LL_PARAM(1), LL_PARAM(2), LL_PARAM(3), LL_PARAM(4), LL_PARAM(5)) | |||
{ | |||
for (Iterator<DummyBailOutChecker, ThisType> iter (*this); iter.next();) | |||
if (iter.getListener() != listenerToExclude) | |||
(iter.getListener()->*callbackFunction) (param1, param2, param3, param4, param5); | |||
} | |||
/** Calls a member function on each listener in the list, with 5 parameters and a bail-out-checker. | |||
See the class description for info about writing a bail-out checker. | |||
*/ | |||
template <class BailOutCheckerType, LL_TEMPLATE(1), LL_TEMPLATE(2), LL_TEMPLATE(3), LL_TEMPLATE(4), LL_TEMPLATE(5)> | |||
void callChecked (const BailOutCheckerType& bailOutChecker, | |||
void (ListenerClass::*callbackFunction) (P1, P2, P3, P4, P5), | |||
LL_PARAM(1), LL_PARAM(2), LL_PARAM(3), LL_PARAM(4), LL_PARAM(5)) | |||
{ | |||
for (Iterator<BailOutCheckerType, ThisType> iter (*this); iter.next (bailOutChecker);) | |||
(iter.getListener()->*callbackFunction) (param1, param2, param3, param4, param5); | |||
} | |||
/** Calls a member function, with 5 parameters, on all but the specified listener in the list | |||
with a bail-out-checker. This can be useful if the caller is also a listener and needs to | |||
exclude itself. See the class description for info about writing a bail-out checker. | |||
*/ | |||
template <class BailOutCheckerType, LL_TEMPLATE(1), LL_TEMPLATE(2), LL_TEMPLATE(3), LL_TEMPLATE(4), LL_TEMPLATE(5)> | |||
void callCheckedExcluding (ListenerClass* listenerToExclude, | |||
const BailOutCheckerType& bailOutChecker, | |||
void (ListenerClass::*callbackFunction) (P1, P2, P3, P4, P5), | |||
LL_PARAM(1), LL_PARAM(2), LL_PARAM(3), LL_PARAM(4), LL_PARAM(5)) | |||
{ | |||
for (Iterator<BailOutCheckerType, ThisType> iter (*this); iter.next (bailOutChecker);) | |||
if (iter.getListener() != listenerToExclude) | |||
(iter.getListener()->*callbackFunction) (param1, param2, param3, param4, param5); | |||
} | |||
//============================================================================== | |||
/** Calls a member function on each listener in the list, with 6 parameters. */ | |||
template <LL_TEMPLATE(1), LL_TEMPLATE(2), LL_TEMPLATE(3), LL_TEMPLATE(4), LL_TEMPLATE(5), LL_TEMPLATE(6)> | |||
void call (void (ListenerClass::*callbackFunction) (P1, P2, P3, P4, P5, P6), | |||
LL_PARAM(1), LL_PARAM(2), LL_PARAM(3), LL_PARAM(4), LL_PARAM(5), LL_PARAM(6)) | |||
{ | |||
for (Iterator<DummyBailOutChecker, ThisType> iter (*this); iter.next();) | |||
(iter.getListener()->*callbackFunction) (param1, param2, param3, param4, param5, param6); | |||
} | |||
/** Calls a member function, with 6 parameters, on all but the specified listener in the list. | |||
This can be useful if the caller is also a listener and needs to exclude itself. | |||
*/ | |||
template <LL_TEMPLATE(1), LL_TEMPLATE(2), LL_TEMPLATE(3), LL_TEMPLATE(4), LL_TEMPLATE(5), LL_TEMPLATE(6)> | |||
void callExcluding (ListenerClass* listenerToExclude, | |||
void (ListenerClass::*callbackFunction) (P1, P2, P3, P4, P5, P6), | |||
LL_PARAM(1), LL_PARAM(2), LL_PARAM(3), LL_PARAM(4), LL_PARAM(5), LL_PARAM(6)) | |||
{ | |||
for (Iterator<DummyBailOutChecker, ThisType> iter (*this); iter.next();) | |||
if (iter.getListener() != listenerToExclude) | |||
(iter.getListener()->*callbackFunction) (param1, param2, param3, param4, param5, param6); | |||
} | |||
/** Calls a member function on each listener in the list, with 6 parameters and a bail-out-checker. | |||
See the class description for info about writing a bail-out checker. | |||
*/ | |||
template <class BailOutCheckerType, LL_TEMPLATE(1), LL_TEMPLATE(2), LL_TEMPLATE(3), LL_TEMPLATE(4), LL_TEMPLATE(5), LL_TEMPLATE(6)> | |||
void callChecked (const BailOutCheckerType& bailOutChecker, | |||
void (ListenerClass::*callbackFunction) (P1, P2, P3, P4, P5, P6), | |||
LL_PARAM(1), LL_PARAM(2), LL_PARAM(3), LL_PARAM(4), LL_PARAM(5), LL_PARAM(6)) | |||
{ | |||
for (Iterator<BailOutCheckerType, ThisType> iter (*this); iter.next (bailOutChecker);) | |||
(iter.getListener()->*callbackFunction) (param1, param2, param3, param4, param5, param6); | |||
} | |||
/** Calls a member function, with 5 parameters, on all but the specified listener in the list | |||
with a bail-out-checker. This can be useful if the caller is also a listener and needs to | |||
exclude itself. See the class description for info about writing a bail-out checker. | |||
*/ | |||
template <class BailOutCheckerType, LL_TEMPLATE(1), LL_TEMPLATE(2), LL_TEMPLATE(3), LL_TEMPLATE(4), LL_TEMPLATE(5), LL_TEMPLATE(6)> | |||
void callCheckedExcluding (ListenerClass* listenerToExclude, | |||
const BailOutCheckerType& bailOutChecker, | |||
void (ListenerClass::*callbackFunction) (P1, P2, P3, P4, P5, P6), | |||
LL_PARAM(1), LL_PARAM(2), LL_PARAM(3), LL_PARAM(4), LL_PARAM(5), LL_PARAM(6)) | |||
{ | |||
for (Iterator<BailOutCheckerType, ThisType> iter (*this); iter.next (bailOutChecker);) | |||
if (iter.getListener() != listenerToExclude) | |||
(iter.getListener()->*callbackFunction) (param1, param2, param3, param4, param5, param6); | |||
} | |||
//============================================================================== | |||
/** A dummy bail-out checker that always returns false. | |||
See the ListenerList notes for more info about bail-out checkers. | |||
*/ | |||
struct DummyBailOutChecker | |||
{ | |||
bool shouldBailOut() const noexcept { return false; } | |||
}; | |||
//============================================================================== | |||
/** Iterates the listeners in a ListenerList. */ | |||
template <class BailOutCheckerType, class ListType> | |||
class Iterator | |||
{ | |||
public: | |||
//============================================================================== | |||
Iterator (const ListType& listToIterate) noexcept | |||
: list (listToIterate), index (listToIterate.size()) | |||
{} | |||
~Iterator() noexcept {} | |||
//============================================================================== | |||
bool next() noexcept | |||
{ | |||
if (index <= 0) | |||
return false; | |||
const int listSize = list.size(); | |||
if (--index < listSize) | |||
return true; | |||
index = listSize - 1; | |||
return index >= 0; | |||
} | |||
bool next (const BailOutCheckerType& bailOutChecker) noexcept | |||
{ | |||
return (! bailOutChecker.shouldBailOut()) && next(); | |||
} | |||
typename ListType::ListenerType* getListener() const noexcept | |||
{ | |||
return list.getListeners().getUnchecked (index); | |||
} | |||
//============================================================================== | |||
private: | |||
const ListType& list; | |||
int index; | |||
JUCE_DECLARE_NON_COPYABLE (Iterator) | |||
}; | |||
typedef ListenerList<ListenerClass, ArrayType> ThisType; | |||
typedef ListenerClass ListenerType; | |||
const ArrayType& getListeners() const noexcept { return listeners; } | |||
private: | |||
//============================================================================== | |||
ArrayType listeners; | |||
JUCE_DECLARE_NON_COPYABLE (ListenerList) | |||
#undef LL_TEMPLATE | |||
#undef LL_PARAM | |||
}; | |||
} // namespace juce |
@@ -1,247 +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. | |||
============================================================================== | |||
*/ | |||
namespace juce | |||
{ | |||
NamedValueSet::NamedValueSet() noexcept | |||
{ | |||
} | |||
NamedValueSet::NamedValueSet (const NamedValueSet& other) | |||
: values (other.values) | |||
{ | |||
} | |||
NamedValueSet& NamedValueSet::operator= (const NamedValueSet& other) | |||
{ | |||
clear(); | |||
values = other.values; | |||
return *this; | |||
} | |||
NamedValueSet::NamedValueSet (NamedValueSet&& other) noexcept | |||
: values (static_cast<Array<NamedValue>&&> (other.values)) | |||
{ | |||
} | |||
NamedValueSet& NamedValueSet::operator= (NamedValueSet&& other) noexcept | |||
{ | |||
other.values.swapWith (values); | |||
return *this; | |||
} | |||
NamedValueSet::~NamedValueSet() noexcept | |||
{ | |||
} | |||
void NamedValueSet::clear() | |||
{ | |||
values.clear(); | |||
} | |||
bool NamedValueSet::operator== (const NamedValueSet& other) const | |||
{ | |||
return values == other.values; | |||
} | |||
bool NamedValueSet::operator!= (const NamedValueSet& other) const | |||
{ | |||
return ! operator== (other); | |||
} | |||
int NamedValueSet::size() const noexcept | |||
{ | |||
return values.size(); | |||
} | |||
bool NamedValueSet::isEmpty() const noexcept | |||
{ | |||
return values.isEmpty(); | |||
} | |||
static const var& getNullVarRef() noexcept | |||
{ | |||
#if JUCE_ALLOW_STATIC_NULL_VARIABLES | |||
return var::null; | |||
#else | |||
static var nullVar; | |||
return nullVar; | |||
#endif | |||
} | |||
const var& NamedValueSet::operator[] (const Identifier& name) const noexcept | |||
{ | |||
if (const var* v = getVarPointer (name)) | |||
return *v; | |||
return getNullVarRef(); | |||
} | |||
var NamedValueSet::getWithDefault (const Identifier& name, const var& defaultReturnValue) const | |||
{ | |||
if (const var* const v = getVarPointer (name)) | |||
return *v; | |||
return defaultReturnValue; | |||
} | |||
var* NamedValueSet::getVarPointer (const Identifier& name) const noexcept | |||
{ | |||
for (NamedValue* e = values.end(), *i = values.begin(); i != e; ++i) | |||
if (i->name == name) | |||
return &(i->value); | |||
return nullptr; | |||
} | |||
bool NamedValueSet::set (const Identifier& name, var&& newValue) | |||
{ | |||
if (var* const v = getVarPointer (name)) | |||
{ | |||
if (v->equalsWithSameType (newValue)) | |||
return false; | |||
*v = static_cast<var&&> (newValue); | |||
return true; | |||
} | |||
values.add (NamedValue (name, static_cast<var&&> (newValue))); | |||
return true; | |||
} | |||
bool NamedValueSet::set (const Identifier& name, const var& newValue) | |||
{ | |||
if (var* const v = getVarPointer (name)) | |||
{ | |||
if (v->equalsWithSameType (newValue)) | |||
return false; | |||
*v = newValue; | |||
return true; | |||
} | |||
values.add (NamedValue (name, newValue)); | |||
return true; | |||
} | |||
bool NamedValueSet::contains (const Identifier& name) const noexcept | |||
{ | |||
return getVarPointer (name) != nullptr; | |||
} | |||
int NamedValueSet::indexOf (const Identifier& name) const noexcept | |||
{ | |||
const int numValues = values.size(); | |||
for (int i = 0; i < numValues; ++i) | |||
if (values.getReference(i).name == name) | |||
return i; | |||
return -1; | |||
} | |||
bool NamedValueSet::remove (const Identifier& name) | |||
{ | |||
const int numValues = values.size(); | |||
for (int i = 0; i < numValues; ++i) | |||
{ | |||
if (values.getReference(i).name == name) | |||
{ | |||
values.remove (i); | |||
return true; | |||
} | |||
} | |||
return false; | |||
} | |||
Identifier NamedValueSet::getName (const int index) const noexcept | |||
{ | |||
if (isPositiveAndBelow (index, values.size())) | |||
return values.getReference (index).name; | |||
jassertfalse; | |||
return Identifier(); | |||
} | |||
const var& NamedValueSet::getValueAt (const int index) const noexcept | |||
{ | |||
if (isPositiveAndBelow (index, values.size())) | |||
return values.getReference (index).value; | |||
jassertfalse; | |||
return getNullVarRef(); | |||
} | |||
var* NamedValueSet::getVarPointerAt (int index) const noexcept | |||
{ | |||
if (isPositiveAndBelow (index, values.size())) | |||
return &(values.getReference (index).value); | |||
return nullptr; | |||
} | |||
void NamedValueSet::setFromXmlAttributes (const XmlElement& xml) | |||
{ | |||
values.clearQuick(); | |||
for (const XmlElement::XmlAttributeNode* att = xml.attributes; att != nullptr; att = att->nextListItem) | |||
{ | |||
if (att->name.toString().startsWith ("base64:")) | |||
{ | |||
MemoryBlock mb; | |||
if (mb.fromBase64Encoding (att->value)) | |||
{ | |||
values.add (NamedValue (att->name.toString().substring (7), var (mb))); | |||
continue; | |||
} | |||
} | |||
values.add (NamedValue (att->name, var (att->value))); | |||
} | |||
} | |||
void NamedValueSet::copyToXmlAttributes (XmlElement& xml) const | |||
{ | |||
for (NamedValue* e = values.end(), *i = values.begin(); i != e; ++i) | |||
{ | |||
if (const MemoryBlock* mb = i->value.getBinaryData()) | |||
{ | |||
xml.setAttribute ("base64:" + i->name.toString(), mb->toBase64Encoding()); | |||
} | |||
else | |||
{ | |||
// These types can't be stored as XML! | |||
jassert (! i->value.isObject()); | |||
jassert (! i->value.isMethod()); | |||
jassert (! i->value.isArray()); | |||
xml.setAttribute (i->name.toString(), | |||
i->value.toString()); | |||
} | |||
} | |||
} | |||
} // namespace juce |
@@ -1,175 +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. | |||
============================================================================== | |||
*/ | |||
namespace juce | |||
{ | |||
//============================================================================== | |||
/** Holds a set of named var objects. | |||
This can be used as a basic structure to hold a set of var object, which can | |||
be retrieved by using their identifier. | |||
*/ | |||
class JUCE_API NamedValueSet | |||
{ | |||
public: | |||
/** Creates an empty set. */ | |||
NamedValueSet() noexcept; | |||
/** Creates a copy of another set. */ | |||
NamedValueSet (const NamedValueSet&); | |||
/** Replaces this set with a copy of another set. */ | |||
NamedValueSet& operator= (const NamedValueSet&); | |||
/** Move constructor */ | |||
NamedValueSet (NamedValueSet&&) noexcept; | |||
/** Move assignment operator */ | |||
NamedValueSet& operator= (NamedValueSet&&) noexcept; | |||
/** Destructor. */ | |||
~NamedValueSet() noexcept; | |||
bool operator== (const NamedValueSet&) const; | |||
bool operator!= (const NamedValueSet&) const; | |||
//============================================================================== | |||
struct NamedValue | |||
{ | |||
NamedValue() noexcept {} | |||
NamedValue (const Identifier& n, const var& v) : name (n), value (v) {} | |||
NamedValue (const NamedValue& other) : name (other.name), value (other.value) {} | |||
NamedValue (NamedValue&& other) noexcept | |||
: name (static_cast<Identifier&&> (other.name)), | |||
value (static_cast<var&&> (other.value)) | |||
{ | |||
} | |||
NamedValue (Identifier&& n, var&& v) noexcept | |||
: name (static_cast<Identifier&&> (n)), | |||
value (static_cast<var&&> (v)) | |||
{ | |||
} | |||
NamedValue& operator= (NamedValue&& other) noexcept | |||
{ | |||
name = static_cast<Identifier&&> (other.name); | |||
value = static_cast<var&&> (other.value); | |||
return *this; | |||
} | |||
bool operator== (const NamedValue& other) const noexcept { return name == other.name && value == other.value; } | |||
bool operator!= (const NamedValue& other) const noexcept { return ! operator== (other); } | |||
Identifier name; | |||
var value; | |||
}; | |||
NamedValueSet::NamedValue* begin() { return values.begin(); } | |||
NamedValueSet::NamedValue* end() { return values.end(); } | |||
//============================================================================== | |||
/** Returns the total number of values that the set contains. */ | |||
int size() const noexcept; | |||
/** Returns true if the set is empty. */ | |||
bool isEmpty() const noexcept; | |||
/** Returns the value of a named item. | |||
If the name isn't found, this will return a void variant. | |||
@see getProperty | |||
*/ | |||
const var& operator[] (const Identifier& name) const noexcept; | |||
/** Tries to return the named value, but if no such value is found, this will | |||
instead return the supplied default value. | |||
*/ | |||
var getWithDefault (const Identifier& name, const var& defaultReturnValue) const; | |||
/** Changes or adds a named value. | |||
@returns true if a value was changed or added; false if the | |||
value was already set the value passed-in. | |||
*/ | |||
bool set (const Identifier& name, const var& newValue); | |||
/** Changes or adds a named value. | |||
@returns true if a value was changed or added; false if the | |||
value was already set the value passed-in. | |||
*/ | |||
bool set (const Identifier& name, var&& newValue); | |||
/** Returns true if the set contains an item with the specified name. */ | |||
bool contains (const Identifier& name) const noexcept; | |||
/** Removes a value from the set. | |||
@returns true if a value was removed; false if there was no value | |||
with the name that was given. | |||
*/ | |||
bool remove (const Identifier& name); | |||
/** Returns the name of the value at a given index. | |||
The index must be between 0 and size() - 1. | |||
*/ | |||
Identifier getName (int index) const noexcept; | |||
/** Returns a pointer to the var that holds a named value, or null if there is | |||
no value with this name. | |||
Do not use this method unless you really need access to the internal var object | |||
for some reason - for normal reading and writing always prefer operator[]() and set(). | |||
*/ | |||
var* getVarPointer (const Identifier& name) const noexcept; | |||
/** Returns the value of the item at a given index. | |||
The index must be between 0 and size() - 1. | |||
*/ | |||
const var& getValueAt (int index) const noexcept; | |||
/** Returns the value of the item at a given index. | |||
The index must be between 0 and size() - 1, or this will return a nullptr | |||
*/ | |||
var* getVarPointerAt (int index) const noexcept; | |||
/** Returns the index of the given name, or -1 if it's not found. */ | |||
int indexOf (const Identifier& name) const noexcept; | |||
/** Removes all values. */ | |||
void clear(); | |||
//============================================================================== | |||
/** Sets properties to the values of all of an XML element's attributes. */ | |||
void setFromXmlAttributes (const XmlElement& xml); | |||
/** Sets attributes in an XML element corresponding to each of this object's | |||
properties. | |||
*/ | |||
void copyToXmlAttributes (XmlElement& xml) const; | |||
private: | |||
//============================================================================== | |||
Array<NamedValue> values; | |||
}; | |||
} // namespace juce |
@@ -1,896 +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. | |||
============================================================================== | |||
*/ | |||
namespace juce | |||
{ | |||
//============================================================================== | |||
/** An array designed for holding objects. | |||
This holds a list of pointers to objects, and will automatically | |||
delete the objects when they are removed from the array, or when the | |||
array is itself deleted. | |||
Declare it in the form: OwnedArray<MyObjectClass> | |||
..and then add new objects, e.g. myOwnedArray.add (new MyObjectClass()); | |||
After adding objects, they are 'owned' by the array and will be deleted when | |||
removed or replaced. | |||
To make all the array's methods thread-safe, pass in "CriticalSection" as the templated | |||
TypeOfCriticalSectionToUse parameter, instead of the default DummyCriticalSection. | |||
@see Array, ReferenceCountedArray, StringArray, CriticalSection | |||
*/ | |||
template <class ObjectClass, | |||
class TypeOfCriticalSectionToUse = DummyCriticalSection> | |||
class OwnedArray | |||
{ | |||
public: | |||
//============================================================================== | |||
/** Creates an empty array. */ | |||
OwnedArray() noexcept | |||
: numUsed (0) | |||
{ | |||
} | |||
/** Deletes the array and also deletes any objects inside it. | |||
To get rid of the array without deleting its objects, use its | |||
clear (false) method before deleting it. | |||
*/ | |||
~OwnedArray() | |||
{ | |||
deleteAllObjects(); | |||
} | |||
/** Move constructor */ | |||
OwnedArray (OwnedArray&& other) noexcept | |||
: data (static_cast<ArrayAllocationBase <ObjectClass*, TypeOfCriticalSectionToUse>&&> (other.data)), | |||
numUsed (other.numUsed) | |||
{ | |||
other.numUsed = 0; | |||
} | |||
/** Move assignment operator */ | |||
OwnedArray& operator= (OwnedArray&& other) noexcept | |||
{ | |||
const ScopedLockType lock (getLock()); | |||
deleteAllObjects(); | |||
data = static_cast<ArrayAllocationBase <ObjectClass*, TypeOfCriticalSectionToUse>&&> (other.data); | |||
numUsed = other.numUsed; | |||
other.numUsed = 0; | |||
return *this; | |||
} | |||
//============================================================================== | |||
/** Clears the array, optionally deleting the objects inside it first. */ | |||
void clear (bool deleteObjects = true) | |||
{ | |||
const ScopedLockType lock (getLock()); | |||
if (deleteObjects) | |||
deleteAllObjects(); | |||
data.setAllocatedSize (0); | |||
numUsed = 0; | |||
} | |||
//============================================================================== | |||
/** Clears the array, optionally deleting the objects inside it first. */ | |||
void clearQuick (bool deleteObjects) | |||
{ | |||
const ScopedLockType lock (getLock()); | |||
if (deleteObjects) | |||
deleteAllObjects(); | |||
numUsed = 0; | |||
} | |||
//============================================================================== | |||
/** Returns the number of items currently in the array. | |||
@see operator[] | |||
*/ | |||
inline int size() const noexcept | |||
{ | |||
return numUsed; | |||
} | |||
/** Returns true if the array is empty, false otherwise. */ | |||
inline bool isEmpty() const noexcept | |||
{ | |||
return size() == 0; | |||
} | |||
/** Returns a pointer to the object at this index in the array. | |||
If the index is out-of-range, this will return a null pointer, (and | |||
it could be null anyway, because it's ok for the array to hold null | |||
pointers as well as objects). | |||
@see getUnchecked | |||
*/ | |||
inline ObjectClass* operator[] (const int index) const noexcept | |||
{ | |||
const ScopedLockType lock (getLock()); | |||
if (isPositiveAndBelow (index, numUsed)) | |||
{ | |||
jassert (data.elements != nullptr); | |||
return data.elements [index]; | |||
} | |||
return nullptr; | |||
} | |||
/** Returns a pointer to the object at this index in the array, without checking whether the index is in-range. | |||
This is a faster and less safe version of operator[] which doesn't check the index passed in, so | |||
it can be used when you're sure the index is always going to be legal. | |||
*/ | |||
inline ObjectClass* getUnchecked (const int index) const noexcept | |||
{ | |||
const ScopedLockType lock (getLock()); | |||
jassert (isPositiveAndBelow (index, numUsed) && data.elements != nullptr); | |||
return data.elements [index]; | |||
} | |||
/** Returns a pointer to the first object in the array. | |||
This will return a null pointer if the array's empty. | |||
@see getLast | |||
*/ | |||
inline ObjectClass* getFirst() const noexcept | |||
{ | |||
const ScopedLockType lock (getLock()); | |||
if (numUsed > 0) | |||
{ | |||
jassert (data.elements != nullptr); | |||
return data.elements [0]; | |||
} | |||
return nullptr; | |||
} | |||
/** Returns a pointer to the last object in the array. | |||
This will return a null pointer if the array's empty. | |||
@see getFirst | |||
*/ | |||
inline ObjectClass* getLast() const noexcept | |||
{ | |||
const ScopedLockType lock (getLock()); | |||
if (numUsed > 0) | |||
{ | |||
jassert (data.elements != nullptr); | |||
return data.elements [numUsed - 1]; | |||
} | |||
return nullptr; | |||
} | |||
/** Returns a pointer to the actual array data. | |||
This pointer will only be valid until the next time a non-const method | |||
is called on the array. | |||
*/ | |||
inline ObjectClass** getRawDataPointer() noexcept | |||
{ | |||
return data.elements; | |||
} | |||
//============================================================================== | |||
/** Returns a pointer to the first element in the array. | |||
This method is provided for compatibility with standard C++ iteration mechanisms. | |||
*/ | |||
inline ObjectClass** begin() const noexcept | |||
{ | |||
return data.elements; | |||
} | |||
/** Returns a pointer to the element which follows the last element in the array. | |||
This method is provided for compatibility with standard C++ iteration mechanisms. | |||
*/ | |||
inline ObjectClass** end() const noexcept | |||
{ | |||
#if JUCE_DEBUG | |||
if (data.elements == nullptr || numUsed <= 0) // (to keep static analysers happy) | |||
return data.elements; | |||
#endif | |||
return data.elements + numUsed; | |||
} | |||
//============================================================================== | |||
/** Finds the index of an object which might be in the array. | |||
@param objectToLookFor the object to look for | |||
@returns the index at which the object was found, or -1 if it's not found | |||
*/ | |||
int indexOf (const ObjectClass* objectToLookFor) const noexcept | |||
{ | |||
const ScopedLockType lock (getLock()); | |||
ObjectClass* const* e = data.elements.get(); | |||
ObjectClass* const* const end_ = e + numUsed; | |||
for (; e != end_; ++e) | |||
if (objectToLookFor == *e) | |||
return static_cast<int> (e - data.elements.get()); | |||
return -1; | |||
} | |||
/** Returns true if the array contains a specified object. | |||
@param objectToLookFor the object to look for | |||
@returns true if the object is in the array | |||
*/ | |||
bool contains (const ObjectClass* objectToLookFor) const noexcept | |||
{ | |||
const ScopedLockType lock (getLock()); | |||
ObjectClass* const* e = data.elements.get(); | |||
ObjectClass* const* const end_ = e + numUsed; | |||
for (; e != end_; ++e) | |||
if (objectToLookFor == *e) | |||
return true; | |||
return false; | |||
} | |||
//============================================================================== | |||
/** Appends a new object to the end of the array. | |||
Note that the this object will be deleted by the OwnedArray when it | |||
is removed, so be careful not to delete it somewhere else. | |||
Also be careful not to add the same object to the array more than once, | |||
as this will obviously cause deletion of dangling pointers. | |||
@param newObject the new object to add to the array | |||
@returns the new object that was added | |||
@see set, insert, addIfNotAlreadyThere, addSorted | |||
*/ | |||
ObjectClass* add (ObjectClass* newObject) noexcept | |||
{ | |||
const ScopedLockType lock (getLock()); | |||
data.ensureAllocatedSize (numUsed + 1); | |||
jassert (data.elements != nullptr); | |||
data.elements [numUsed++] = newObject; | |||
return newObject; | |||
} | |||
/** Inserts a new object into the array at the given index. | |||
Note that the this object will be deleted by the OwnedArray when it | |||
is removed, so be careful not to delete it somewhere else. | |||
If the index is less than 0 or greater than the size of the array, the | |||
element will be added to the end of the array. | |||
Otherwise, it will be inserted into the array, moving all the later elements | |||
along to make room. | |||
Be careful not to add the same object to the array more than once, | |||
as this will obviously cause deletion of dangling pointers. | |||
@param indexToInsertAt the index at which the new element should be inserted | |||
@param newObject the new object to add to the array | |||
@returns the new object that was added | |||
@see add, addSorted, addIfNotAlreadyThere, set | |||
*/ | |||
ObjectClass* insert (int indexToInsertAt, ObjectClass* newObject) noexcept | |||
{ | |||
if (indexToInsertAt < 0) | |||
return add (newObject); | |||
const ScopedLockType lock (getLock()); | |||
if (indexToInsertAt > numUsed) | |||
indexToInsertAt = numUsed; | |||
data.ensureAllocatedSize (numUsed + 1); | |||
jassert (data.elements != nullptr); | |||
ObjectClass** const e = data.elements + indexToInsertAt; | |||
const int numToMove = numUsed - indexToInsertAt; | |||
if (numToMove > 0) | |||
memmove (e + 1, e, sizeof (ObjectClass*) * (size_t) numToMove); | |||
*e = newObject; | |||
++numUsed; | |||
return newObject; | |||
} | |||
/** Inserts an array of values into this array at a given position. | |||
If the index is less than 0 or greater than the size of the array, the | |||
new elements will be added to the end of the array. | |||
Otherwise, they will be inserted into the array, moving all the later elements | |||
along to make room. | |||
@param indexToInsertAt the index at which the first new element should be inserted | |||
@param newObjects the new values to add to the array | |||
@param numberOfElements how many items are in the array | |||
@see insert, add, addSorted, set | |||
*/ | |||
void insertArray (int indexToInsertAt, | |||
ObjectClass* const* newObjects, | |||
int numberOfElements) | |||
{ | |||
if (numberOfElements > 0) | |||
{ | |||
const ScopedLockType lock (getLock()); | |||
data.ensureAllocatedSize (numUsed + numberOfElements); | |||
ObjectClass** insertPos = data.elements; | |||
if (isPositiveAndBelow (indexToInsertAt, numUsed)) | |||
{ | |||
insertPos += indexToInsertAt; | |||
const size_t numberToMove = (size_t) (numUsed - indexToInsertAt); | |||
memmove (insertPos + numberOfElements, insertPos, numberToMove * sizeof (ObjectClass*)); | |||
} | |||
else | |||
{ | |||
insertPos += numUsed; | |||
} | |||
numUsed += numberOfElements; | |||
while (--numberOfElements >= 0) | |||
*insertPos++ = *newObjects++; | |||
} | |||
} | |||
/** Appends a new object at the end of the array as long as the array doesn't | |||
already contain it. | |||
If the array already contains a matching object, nothing will be done. | |||
@param newObject the new object to add to the array | |||
@returns true if the new object was added, false otherwise | |||
*/ | |||
bool addIfNotAlreadyThere (ObjectClass* newObject) noexcept | |||
{ | |||
const ScopedLockType lock (getLock()); | |||
if (contains (newObject)) | |||
return false; | |||
add (newObject); | |||
return true; | |||
} | |||
/** Replaces an object in the array with a different one. | |||
If the index is less than zero, this method does nothing. | |||
If the index is beyond the end of the array, the new object is added to the end of the array. | |||
Be careful not to add the same object to the array more than once, | |||
as this will obviously cause deletion of dangling pointers. | |||
@param indexToChange the index whose value you want to change | |||
@param newObject the new value to set for this index. | |||
@param deleteOldElement whether to delete the object that's being replaced with the new one | |||
@see add, insert, remove | |||
*/ | |||
ObjectClass* set (int indexToChange, ObjectClass* newObject, bool deleteOldElement = true) | |||
{ | |||
if (indexToChange >= 0) | |||
{ | |||
ScopedPointer<ObjectClass> toDelete; | |||
{ | |||
const ScopedLockType lock (getLock()); | |||
if (indexToChange < numUsed) | |||
{ | |||
if (deleteOldElement) | |||
{ | |||
toDelete = data.elements [indexToChange]; | |||
if (toDelete == newObject) | |||
toDelete.release(); | |||
} | |||
data.elements [indexToChange] = newObject; | |||
} | |||
else | |||
{ | |||
data.ensureAllocatedSize (numUsed + 1); | |||
data.elements [numUsed++] = newObject; | |||
} | |||
} | |||
} | |||
else | |||
{ | |||
jassertfalse; // you're trying to set an object at a negative index, which doesn't have | |||
// any effect - but since the object is not being added, it may be leaking.. | |||
} | |||
return newObject; | |||
} | |||
/** Adds elements from another array to the end of this array. | |||
@param arrayToAddFrom the array from which to copy the elements | |||
@param startIndex the first element of the other array to start copying from | |||
@param numElementsToAdd how many elements to add from the other array. If this | |||
value is negative or greater than the number of available elements, | |||
all available elements will be copied. | |||
@see add | |||
*/ | |||
template <class OtherArrayType> | |||
void addArray (const OtherArrayType& arrayToAddFrom, | |||
int startIndex = 0, | |||
int numElementsToAdd = -1) | |||
{ | |||
const typename OtherArrayType::ScopedLockType lock1 (arrayToAddFrom.getLock()); | |||
const ScopedLockType lock2 (getLock()); | |||
if (startIndex < 0) | |||
{ | |||
jassertfalse; | |||
startIndex = 0; | |||
} | |||
if (numElementsToAdd < 0 || startIndex + numElementsToAdd > arrayToAddFrom.size()) | |||
numElementsToAdd = arrayToAddFrom.size() - startIndex; | |||
data.ensureAllocatedSize (numUsed + numElementsToAdd); | |||
jassert (numElementsToAdd <= 0 || data.elements != nullptr); | |||
while (--numElementsToAdd >= 0) | |||
{ | |||
data.elements [numUsed] = arrayToAddFrom.getUnchecked (startIndex++); | |||
++numUsed; | |||
} | |||
} | |||
/** Adds copies of the elements in another array to the end of this array. | |||
The other array must be either an OwnedArray of a compatible type of object, or an Array | |||
containing pointers to the same kind of object. The objects involved must provide | |||
a copy constructor, and this will be used to create new copies of each element, and | |||
add them to this array. | |||
@param arrayToAddFrom the array from which to copy the elements | |||
@param startIndex the first element of the other array to start copying from | |||
@param numElementsToAdd how many elements to add from the other array. If this | |||
value is negative or greater than the number of available elements, | |||
all available elements will be copied. | |||
@see add | |||
*/ | |||
template <class OtherArrayType> | |||
void addCopiesOf (const OtherArrayType& arrayToAddFrom, | |||
int startIndex = 0, | |||
int numElementsToAdd = -1) | |||
{ | |||
const typename OtherArrayType::ScopedLockType lock1 (arrayToAddFrom.getLock()); | |||
const ScopedLockType lock2 (getLock()); | |||
if (startIndex < 0) | |||
{ | |||
jassertfalse; | |||
startIndex = 0; | |||
} | |||
if (numElementsToAdd < 0 || startIndex + numElementsToAdd > arrayToAddFrom.size()) | |||
numElementsToAdd = arrayToAddFrom.size() - startIndex; | |||
data.ensureAllocatedSize (numUsed + numElementsToAdd); | |||
jassert (numElementsToAdd <= 0 || data.elements != nullptr); | |||
while (--numElementsToAdd >= 0) | |||
data.elements [numUsed++] = createCopyIfNotNull (arrayToAddFrom.getUnchecked (startIndex++)); | |||
} | |||
/** Inserts a new object into the array assuming that the array is sorted. | |||
This will use a comparator to find the position at which the new object | |||
should go. If the array isn't sorted, the behaviour of this | |||
method will be unpredictable. | |||
@param comparator the comparator to use to compare the elements - see the sort method | |||
for details about this object's structure | |||
@param newObject the new object to insert to the array | |||
@returns the index at which the new object was added | |||
@see add, sort, indexOfSorted | |||
*/ | |||
template <class ElementComparator> | |||
int addSorted (ElementComparator& comparator, ObjectClass* const newObject) noexcept | |||
{ | |||
ignoreUnused (comparator); // if you pass in an object with a static compareElements() method, this | |||
// avoids getting warning messages about the parameter being unused | |||
const ScopedLockType lock (getLock()); | |||
const int index = findInsertIndexInSortedArray (comparator, data.elements.get(), newObject, 0, numUsed); | |||
insert (index, newObject); | |||
return index; | |||
} | |||
/** Finds the index of an object in the array, assuming that the array is sorted. | |||
This will use a comparator to do a binary-chop to find the index of the given | |||
element, if it exists. If the array isn't sorted, the behaviour of this | |||
method will be unpredictable. | |||
@param comparator the comparator to use to compare the elements - see the sort() | |||
method for details about the form this object should take | |||
@param objectToLookFor the object to search for | |||
@returns the index of the element, or -1 if it's not found | |||
@see addSorted, sort | |||
*/ | |||
template <typename ElementComparator> | |||
int indexOfSorted (ElementComparator& comparator, const ObjectClass* const objectToLookFor) const noexcept | |||
{ | |||
ignoreUnused (comparator); | |||
const ScopedLockType lock (getLock()); | |||
int s = 0, e = numUsed; | |||
while (s < e) | |||
{ | |||
if (comparator.compareElements (objectToLookFor, data.elements [s]) == 0) | |||
return s; | |||
const int halfway = (s + e) / 2; | |||
if (halfway == s) | |||
break; | |||
if (comparator.compareElements (objectToLookFor, data.elements [halfway]) >= 0) | |||
s = halfway; | |||
else | |||
e = halfway; | |||
} | |||
return -1; | |||
} | |||
//============================================================================== | |||
/** Removes an object from the array. | |||
This will remove the object at a given index (optionally also | |||
deleting it) and move back all the subsequent objects to close the gap. | |||
If the index passed in is out-of-range, nothing will happen. | |||
@param indexToRemove the index of the element to remove | |||
@param deleteObject whether to delete the object that is removed | |||
@see removeObject, removeRange | |||
*/ | |||
void remove (int indexToRemove, bool deleteObject = true) | |||
{ | |||
ScopedPointer<ObjectClass> toDelete; | |||
{ | |||
const ScopedLockType lock (getLock()); | |||
if (isPositiveAndBelow (indexToRemove, numUsed)) | |||
{ | |||
ObjectClass** const e = data.elements + indexToRemove; | |||
if (deleteObject) | |||
toDelete = *e; | |||
--numUsed; | |||
const int numToShift = numUsed - indexToRemove; | |||
if (numToShift > 0) | |||
memmove (e, e + 1, sizeof (ObjectClass*) * (size_t) numToShift); | |||
} | |||
} | |||
if ((numUsed << 1) < data.numAllocated) | |||
minimiseStorageOverheads(); | |||
} | |||
/** Removes and returns an object from the array without deleting it. | |||
This will remove the object at a given index and return it, moving back all | |||
the subsequent objects to close the gap. If the index passed in is out-of-range, | |||
nothing will happen. | |||
@param indexToRemove the index of the element to remove | |||
@see remove, removeObject, removeRange | |||
*/ | |||
ObjectClass* removeAndReturn (int indexToRemove) | |||
{ | |||
ObjectClass* removedItem = nullptr; | |||
const ScopedLockType lock (getLock()); | |||
if (isPositiveAndBelow (indexToRemove, numUsed)) | |||
{ | |||
ObjectClass** const e = data.elements + indexToRemove; | |||
removedItem = *e; | |||
--numUsed; | |||
const int numToShift = numUsed - indexToRemove; | |||
if (numToShift > 0) | |||
memmove (e, e + 1, sizeof (ObjectClass*) * (size_t) numToShift); | |||
if ((numUsed << 1) < data.numAllocated) | |||
minimiseStorageOverheads(); | |||
} | |||
return removedItem; | |||
} | |||
/** Removes a specified object from the array. | |||
If the item isn't found, no action is taken. | |||
@param objectToRemove the object to try to remove | |||
@param deleteObject whether to delete the object (if it's found) | |||
@see remove, removeRange | |||
*/ | |||
void removeObject (const ObjectClass* objectToRemove, bool deleteObject = true) | |||
{ | |||
const ScopedLockType lock (getLock()); | |||
ObjectClass** const e = data.elements.get(); | |||
for (int i = 0; i < numUsed; ++i) | |||
{ | |||
if (objectToRemove == e[i]) | |||
{ | |||
remove (i, deleteObject); | |||
break; | |||
} | |||
} | |||
} | |||
/** Removes a range of objects from the array. | |||
This will remove a set of objects, starting from the given index, | |||
and move any subsequent elements down to close the gap. | |||
If the range extends beyond the bounds of the array, it will | |||
be safely clipped to the size of the array. | |||
@param startIndex the index of the first object to remove | |||
@param numberToRemove how many objects should be removed | |||
@param deleteObjects whether to delete the objects that get removed | |||
@see remove, removeObject | |||
*/ | |||
void removeRange (int startIndex, int numberToRemove, bool deleteObjects = true) | |||
{ | |||
const ScopedLockType lock (getLock()); | |||
const int endIndex = jlimit (0, numUsed, startIndex + numberToRemove); | |||
startIndex = jlimit (0, numUsed, startIndex); | |||
if (endIndex > startIndex) | |||
{ | |||
if (deleteObjects) | |||
{ | |||
for (int i = startIndex; i < endIndex; ++i) | |||
{ | |||
ContainerDeletePolicy<ObjectClass>::destroy (data.elements [i]); | |||
data.elements [i] = nullptr; // (in case one of the destructors accesses this array and hits a dangling pointer) | |||
} | |||
} | |||
const int rangeSize = endIndex - startIndex; | |||
ObjectClass** e = data.elements + startIndex; | |||
int numToShift = numUsed - endIndex; | |||
numUsed -= rangeSize; | |||
while (--numToShift >= 0) | |||
{ | |||
*e = e [rangeSize]; | |||
++e; | |||
} | |||
if ((numUsed << 1) < data.numAllocated) | |||
minimiseStorageOverheads(); | |||
} | |||
} | |||
/** Removes the last n objects from the array. | |||
@param howManyToRemove how many objects to remove from the end of the array | |||
@param deleteObjects whether to also delete the objects that are removed | |||
@see remove, removeObject, removeRange | |||
*/ | |||
void removeLast (int howManyToRemove = 1, | |||
bool deleteObjects = true) | |||
{ | |||
const ScopedLockType lock (getLock()); | |||
if (howManyToRemove >= numUsed) | |||
clear (deleteObjects); | |||
else | |||
removeRange (numUsed - howManyToRemove, howManyToRemove, deleteObjects); | |||
} | |||
/** Swaps a pair of objects in the array. | |||
If either of the indexes passed in is out-of-range, nothing will happen, | |||
otherwise the two objects at these positions will be exchanged. | |||
*/ | |||
void swap (int index1, | |||
int index2) noexcept | |||
{ | |||
const ScopedLockType lock (getLock()); | |||
if (isPositiveAndBelow (index1, numUsed) | |||
&& isPositiveAndBelow (index2, numUsed)) | |||
{ | |||
std::swap (data.elements [index1], | |||
data.elements [index2]); | |||
} | |||
} | |||
/** Moves one of the objects to a different position. | |||
This will move the object to a specified index, shuffling along | |||
any intervening elements as required. | |||
So for example, if you have the array { 0, 1, 2, 3, 4, 5 } then calling | |||
move (2, 4) would result in { 0, 1, 3, 4, 2, 5 }. | |||
@param currentIndex the index of the object to be moved. If this isn't a | |||
valid index, then nothing will be done | |||
@param newIndex the index at which you'd like this object to end up. If this | |||
is less than zero, it will be moved to the end of the array | |||
*/ | |||
void move (int currentIndex, int newIndex) noexcept | |||
{ | |||
if (currentIndex != newIndex) | |||
{ | |||
const ScopedLockType lock (getLock()); | |||
if (isPositiveAndBelow (currentIndex, numUsed)) | |||
{ | |||
if (! isPositiveAndBelow (newIndex, numUsed)) | |||
newIndex = numUsed - 1; | |||
ObjectClass* const value = data.elements [currentIndex]; | |||
if (newIndex > currentIndex) | |||
{ | |||
memmove (data.elements + currentIndex, | |||
data.elements + currentIndex + 1, | |||
sizeof (ObjectClass*) * (size_t) (newIndex - currentIndex)); | |||
} | |||
else | |||
{ | |||
memmove (data.elements + newIndex + 1, | |||
data.elements + newIndex, | |||
sizeof (ObjectClass*) * (size_t) (currentIndex - newIndex)); | |||
} | |||
data.elements [newIndex] = value; | |||
} | |||
} | |||
} | |||
/** This swaps the contents of this array with those of another array. | |||
If you need to exchange two arrays, this is vastly quicker than using copy-by-value | |||
because it just swaps their internal pointers. | |||
*/ | |||
template <class OtherArrayType> | |||
void swapWith (OtherArrayType& otherArray) noexcept | |||
{ | |||
const ScopedLockType lock1 (getLock()); | |||
const typename OtherArrayType::ScopedLockType lock2 (otherArray.getLock()); | |||
data.swapWith (otherArray.data); | |||
std::swap (numUsed, otherArray.numUsed); | |||
} | |||
//============================================================================== | |||
/** Reduces the amount of storage being used by the array. | |||
Arrays typically allocate slightly more storage than they need, and after | |||
removing elements, they may have quite a lot of unused space allocated. | |||
This method will reduce the amount of allocated storage to a minimum. | |||
*/ | |||
void minimiseStorageOverheads() noexcept | |||
{ | |||
const ScopedLockType lock (getLock()); | |||
data.shrinkToNoMoreThan (numUsed); | |||
} | |||
/** Increases the array's internal storage to hold a minimum number of elements. | |||
Calling this before adding a large known number of elements means that | |||
the array won't have to keep dynamically resizing itself as the elements | |||
are added, and it'll therefore be more efficient. | |||
*/ | |||
void ensureStorageAllocated (const int minNumElements) noexcept | |||
{ | |||
const ScopedLockType lock (getLock()); | |||
data.ensureAllocatedSize (minNumElements); | |||
} | |||
//============================================================================== | |||
/** Sorts the elements in the array. | |||
This will use a comparator object to sort the elements into order. The object | |||
passed must have a method of the form: | |||
@code | |||
int compareElements (ElementType* first, ElementType* second); | |||
@endcode | |||
..and this method must return: | |||
- a value of < 0 if the first comes before the second | |||
- a value of 0 if the two objects are equivalent | |||
- a value of > 0 if the second comes before the first | |||
To improve performance, the compareElements() method can be declared as static or const. | |||
@param comparator the comparator to use for comparing elements. | |||
@param retainOrderOfEquivalentItems if this is true, then items | |||
which the comparator says are equivalent will be | |||
kept in the order in which they currently appear | |||
in the array. This is slower to perform, but may | |||
be important in some cases. If it's false, a faster | |||
algorithm is used, but equivalent elements may be | |||
rearranged. | |||
@see sortArray, indexOfSorted | |||
*/ | |||
template <class ElementComparator> | |||
void sort (ElementComparator& comparator, | |||
bool retainOrderOfEquivalentItems = false) const noexcept | |||
{ | |||
ignoreUnused (comparator); // if you pass in an object with a static compareElements() method, this | |||
// avoids getting warning messages about the parameter being unused | |||
const ScopedLockType lock (getLock()); | |||
sortArray (comparator, data.elements.get(), 0, size() - 1, retainOrderOfEquivalentItems); | |||
} | |||
//============================================================================== | |||
/** Returns the CriticalSection that locks this array. | |||
To lock, you can call getLock().enter() and getLock().exit(), or preferably use | |||
an object of ScopedLockType as an RAII lock for it. | |||
*/ | |||
inline const TypeOfCriticalSectionToUse& getLock() const noexcept { return data; } | |||
/** Returns the type of scoped lock to use for locking this array */ | |||
typedef typename TypeOfCriticalSectionToUse::ScopedLockType ScopedLockType; | |||
//============================================================================== | |||
#ifndef DOXYGEN | |||
// Note that the swapWithArray method has been replaced by a more flexible templated version, | |||
// and renamed "swapWith" to be more consistent with the names used in other classes. | |||
JUCE_DEPRECATED_WITH_BODY (void swapWithArray (OwnedArray& other) noexcept, { swapWith (other); }) | |||
#endif | |||
private: | |||
//============================================================================== | |||
ArrayAllocationBase <ObjectClass*, TypeOfCriticalSectionToUse> data; | |||
int numUsed; | |||
void deleteAllObjects() | |||
{ | |||
while (numUsed > 0) | |||
ContainerDeletePolicy<ObjectClass>::destroy (data.elements [--numUsed]); | |||
} | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OwnedArray) | |||
}; | |||
} // namespace juce |
@@ -1,218 +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. | |||
============================================================================== | |||
*/ | |||
namespace juce | |||
{ | |||
PropertySet::PropertySet (const bool ignoreCaseOfKeyNames) | |||
: properties (ignoreCaseOfKeyNames), | |||
fallbackProperties (nullptr), | |||
ignoreCaseOfKeys (ignoreCaseOfKeyNames) | |||
{ | |||
} | |||
PropertySet::PropertySet (const PropertySet& other) | |||
: properties (other.properties), | |||
fallbackProperties (other.fallbackProperties), | |||
ignoreCaseOfKeys (other.ignoreCaseOfKeys) | |||
{ | |||
} | |||
PropertySet& PropertySet::operator= (const PropertySet& other) | |||
{ | |||
properties = other.properties; | |||
fallbackProperties = other.fallbackProperties; | |||
ignoreCaseOfKeys = other.ignoreCaseOfKeys; | |||
propertyChanged(); | |||
return *this; | |||
} | |||
PropertySet::~PropertySet() | |||
{ | |||
} | |||
void PropertySet::clear() | |||
{ | |||
const ScopedLock sl (lock); | |||
if (properties.size() > 0) | |||
{ | |||
properties.clear(); | |||
propertyChanged(); | |||
} | |||
} | |||
String PropertySet::getValue (StringRef keyName, const String& defaultValue) const noexcept | |||
{ | |||
const ScopedLock sl (lock); | |||
const int index = properties.getAllKeys().indexOf (keyName, ignoreCaseOfKeys); | |||
if (index >= 0) | |||
return properties.getAllValues() [index]; | |||
return fallbackProperties != nullptr ? fallbackProperties->getValue (keyName, defaultValue) | |||
: defaultValue; | |||
} | |||
int PropertySet::getIntValue (StringRef keyName, const int defaultValue) const noexcept | |||
{ | |||
const ScopedLock sl (lock); | |||
const int index = properties.getAllKeys().indexOf (keyName, ignoreCaseOfKeys); | |||
if (index >= 0) | |||
return properties.getAllValues() [index].getIntValue(); | |||
return fallbackProperties != nullptr ? fallbackProperties->getIntValue (keyName, defaultValue) | |||
: defaultValue; | |||
} | |||
double PropertySet::getDoubleValue (StringRef keyName, const double defaultValue) const noexcept | |||
{ | |||
const ScopedLock sl (lock); | |||
const int index = properties.getAllKeys().indexOf (keyName, ignoreCaseOfKeys); | |||
if (index >= 0) | |||
return properties.getAllValues()[index].getDoubleValue(); | |||
return fallbackProperties != nullptr ? fallbackProperties->getDoubleValue (keyName, defaultValue) | |||
: defaultValue; | |||
} | |||
bool PropertySet::getBoolValue (StringRef keyName, const bool defaultValue) const noexcept | |||
{ | |||
const ScopedLock sl (lock); | |||
const int index = properties.getAllKeys().indexOf (keyName, ignoreCaseOfKeys); | |||
if (index >= 0) | |||
return properties.getAllValues() [index].getIntValue() != 0; | |||
return fallbackProperties != nullptr ? fallbackProperties->getBoolValue (keyName, defaultValue) | |||
: defaultValue; | |||
} | |||
XmlElement* PropertySet::getXmlValue (StringRef keyName) const | |||
{ | |||
return XmlDocument::parse (getValue (keyName)); | |||
} | |||
void PropertySet::setValue (const String& keyName, const var& v) | |||
{ | |||
jassert (keyName.isNotEmpty()); // shouldn't use an empty key name! | |||
if (keyName.isNotEmpty()) | |||
{ | |||
const String value (v.toString()); | |||
const ScopedLock sl (lock); | |||
const int index = properties.getAllKeys().indexOf (keyName, ignoreCaseOfKeys); | |||
if (index < 0 || properties.getAllValues() [index] != value) | |||
{ | |||
properties.set (keyName, value); | |||
propertyChanged(); | |||
} | |||
} | |||
} | |||
void PropertySet::removeValue (StringRef keyName) | |||
{ | |||
if (keyName.isNotEmpty()) | |||
{ | |||
const ScopedLock sl (lock); | |||
const int index = properties.getAllKeys().indexOf (keyName, ignoreCaseOfKeys); | |||
if (index >= 0) | |||
{ | |||
properties.remove (keyName); | |||
propertyChanged(); | |||
} | |||
} | |||
} | |||
void PropertySet::setValue (const String& keyName, const XmlElement* const xml) | |||
{ | |||
setValue (keyName, xml == nullptr ? var() | |||
: var (xml->createDocument ("", true))); | |||
} | |||
bool PropertySet::containsKey (StringRef keyName) const noexcept | |||
{ | |||
const ScopedLock sl (lock); | |||
return properties.getAllKeys().contains (keyName, ignoreCaseOfKeys); | |||
} | |||
void PropertySet::addAllPropertiesFrom (const PropertySet& source) | |||
{ | |||
const ScopedLock sl (source.getLock()); | |||
for (int i = 0; i < source.properties.size(); ++i) | |||
setValue (source.properties.getAllKeys() [i], | |||
source.properties.getAllValues() [i]); | |||
} | |||
void PropertySet::setFallbackPropertySet (PropertySet* fallbackProperties_) noexcept | |||
{ | |||
const ScopedLock sl (lock); | |||
fallbackProperties = fallbackProperties_; | |||
} | |||
XmlElement* PropertySet::createXml (const String& nodeName) const | |||
{ | |||
const ScopedLock sl (lock); | |||
XmlElement* const xml = new XmlElement (nodeName); | |||
for (int i = 0; i < properties.getAllKeys().size(); ++i) | |||
{ | |||
XmlElement* const e = xml->createNewChildElement ("VALUE"); | |||
e->setAttribute ("name", properties.getAllKeys()[i]); | |||
e->setAttribute ("val", properties.getAllValues()[i]); | |||
} | |||
return xml; | |||
} | |||
void PropertySet::restoreFromXml (const XmlElement& xml) | |||
{ | |||
const ScopedLock sl (lock); | |||
clear(); | |||
forEachXmlChildElementWithTagName (xml, e, "VALUE") | |||
{ | |||
if (e->hasAttribute ("name") | |||
&& e->hasAttribute ("val")) | |||
{ | |||
properties.set (e->getStringAttribute ("name"), | |||
e->getStringAttribute ("val")); | |||
} | |||
} | |||
if (properties.size() > 0) | |||
propertyChanged(); | |||
} | |||
void PropertySet::propertyChanged() | |||
{ | |||
} | |||
} // namespace juce |
@@ -1,203 +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. | |||
============================================================================== | |||
*/ | |||
namespace juce | |||
{ | |||
//============================================================================== | |||
/** | |||
A set of named property values, which can be strings, integers, floating point, etc. | |||
Effectively, this just wraps a StringPairArray in an interface that makes it easier | |||
to load and save types other than strings. | |||
See the PropertiesFile class for a subclass of this, which automatically broadcasts change | |||
messages and saves/loads the list from a file. | |||
*/ | |||
class JUCE_API PropertySet | |||
{ | |||
public: | |||
//============================================================================== | |||
/** Creates an empty PropertySet. | |||
@param ignoreCaseOfKeyNames if true, the names of properties are compared in a | |||
case-insensitive way | |||
*/ | |||
PropertySet (bool ignoreCaseOfKeyNames = false); | |||
/** Creates a copy of another PropertySet. */ | |||
PropertySet (const PropertySet& other); | |||
/** Copies another PropertySet over this one. */ | |||
PropertySet& operator= (const PropertySet& other); | |||
/** Destructor. */ | |||
virtual ~PropertySet(); | |||
//============================================================================== | |||
/** Returns one of the properties as a string. | |||
If the value isn't found in this set, then this will look for it in a fallback | |||
property set (if you've specified one with the setFallbackPropertySet() method), | |||
and if it can't find one there, it'll return the default value passed-in. | |||
@param keyName the name of the property to retrieve | |||
@param defaultReturnValue a value to return if the named property doesn't actually exist | |||
*/ | |||
String getValue (StringRef keyName, const String& defaultReturnValue = String()) const noexcept; | |||
/** Returns one of the properties as an integer. | |||
If the value isn't found in this set, then this will look for it in a fallback | |||
property set (if you've specified one with the setFallbackPropertySet() method), | |||
and if it can't find one there, it'll return the default value passed-in. | |||
@param keyName the name of the property to retrieve | |||
@param defaultReturnValue a value to return if the named property doesn't actually exist | |||
*/ | |||
int getIntValue (StringRef keyName, int defaultReturnValue = 0) const noexcept; | |||
/** Returns one of the properties as an double. | |||
If the value isn't found in this set, then this will look for it in a fallback | |||
property set (if you've specified one with the setFallbackPropertySet() method), | |||
and if it can't find one there, it'll return the default value passed-in. | |||
@param keyName the name of the property to retrieve | |||
@param defaultReturnValue a value to return if the named property doesn't actually exist | |||
*/ | |||
double getDoubleValue (StringRef keyName, double defaultReturnValue = 0.0) const noexcept; | |||
/** Returns one of the properties as an boolean. | |||
The result will be true if the string found for this key name can be parsed as a non-zero | |||
integer. | |||
If the value isn't found in this set, then this will look for it in a fallback | |||
property set (if you've specified one with the setFallbackPropertySet() method), | |||
and if it can't find one there, it'll return the default value passed-in. | |||
@param keyName the name of the property to retrieve | |||
@param defaultReturnValue a value to return if the named property doesn't actually exist | |||
*/ | |||
bool getBoolValue (StringRef keyName, bool defaultReturnValue = false) const noexcept; | |||
/** Returns one of the properties as an XML element. | |||
The result will a new XMLElement object that the caller must delete. If may return nullptr | |||
if the key isn't found, or if the entry contains an string that isn't valid XML. | |||
If the value isn't found in this set, then this will look for it in a fallback | |||
property set (if you've specified one with the setFallbackPropertySet() method), | |||
and if it can't find one there, it'll return the default value passed-in. | |||
@param keyName the name of the property to retrieve | |||
*/ | |||
XmlElement* getXmlValue (StringRef keyName) const; | |||
//============================================================================== | |||
/** Sets a named property. | |||
@param keyName the name of the property to set. (This mustn't be an empty string) | |||
@param value the new value to set it to | |||
*/ | |||
void setValue (const String& keyName, const var& value); | |||
/** Sets a named property to an XML element. | |||
@param keyName the name of the property to set. (This mustn't be an empty string) | |||
@param xml the new element to set it to. If this is a nullptr, the value will | |||
be set to an empty string | |||
@see getXmlValue | |||
*/ | |||
void setValue (const String& keyName, const XmlElement* xml); | |||
/** This copies all the values from a source PropertySet to this one. | |||
This won't remove any existing settings, it just adds any that it finds in the source set. | |||
*/ | |||
void addAllPropertiesFrom (const PropertySet& source); | |||
//============================================================================== | |||
/** Deletes a property. | |||
@param keyName the name of the property to delete. (This mustn't be an empty string) | |||
*/ | |||
void removeValue (StringRef keyName); | |||
/** Returns true if the properies include the given key. */ | |||
bool containsKey (StringRef keyName) const noexcept; | |||
/** Removes all values. */ | |||
void clear(); | |||
//============================================================================== | |||
/** Returns the keys/value pair array containing all the properties. */ | |||
StringPairArray& getAllProperties() noexcept { return properties; } | |||
/** Returns the lock used when reading or writing to this set */ | |||
const CriticalSection& getLock() const noexcept { return lock; } | |||
//============================================================================== | |||
/** Returns an XML element which encapsulates all the items in this property set. | |||
The string parameter is the tag name that should be used for the node. | |||
@see restoreFromXml | |||
*/ | |||
XmlElement* createXml (const String& nodeName) const; | |||
/** Reloads a set of properties that were previously stored as XML. | |||
The node passed in must have been created by the createXml() method. | |||
@see createXml | |||
*/ | |||
void restoreFromXml (const XmlElement& xml); | |||
//============================================================================== | |||
/** Sets up a second PopertySet that will be used to look up any values that aren't | |||
set in this one. | |||
If you set this up to be a pointer to a second property set, then whenever one | |||
of the getValue() methods fails to find an entry in this set, it will look up that | |||
value in the fallback set, and if it finds it, it will return that. | |||
Make sure that you don't delete the fallback set while it's still being used by | |||
another set! To remove the fallback set, just call this method with a null pointer. | |||
@see getFallbackPropertySet | |||
*/ | |||
void setFallbackPropertySet (PropertySet* fallbackProperties) noexcept; | |||
/** Returns the fallback property set. | |||
@see setFallbackPropertySet | |||
*/ | |||
PropertySet* getFallbackPropertySet() const noexcept { return fallbackProperties; } | |||
protected: | |||
/** Subclasses can override this to be told when one of the properies has been changed. */ | |||
virtual void propertyChanged(); | |||
private: | |||
StringPairArray properties; | |||
PropertySet* fallbackProperties; | |||
CriticalSection lock; | |||
bool ignoreCaseOfKeys; | |||
JUCE_LEAK_DETECTOR (PropertySet) | |||
}; | |||
} // namespace juce |
@@ -1,913 +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. | |||
============================================================================== | |||
*/ | |||
namespace juce | |||
{ | |||
//============================================================================== | |||
/** | |||
Holds a list of objects derived from ReferenceCountedObject, or which implement basic | |||
reference-count handling methods. | |||
The template parameter specifies the class of the object you want to point to - the easiest | |||
way to make a class reference-countable is to simply make it inherit from ReferenceCountedObject | |||
or SingleThreadedReferenceCountedObject, but if you need to, you can roll your own reference-countable | |||
class by implementing a set of methods called incReferenceCount(), decReferenceCount(), and | |||
decReferenceCountWithoutDeleting(). See ReferenceCountedObject for examples of how these methods | |||
should behave. | |||
A ReferenceCountedArray holds objects derived from ReferenceCountedObject, | |||
and takes care of incrementing and decrementing their ref counts when they | |||
are added and removed from the array. | |||
To make all the array's methods thread-safe, pass in "CriticalSection" as the templated | |||
TypeOfCriticalSectionToUse parameter, instead of the default DummyCriticalSection. | |||
@see Array, OwnedArray, StringArray | |||
*/ | |||
template <class ObjectClass, class TypeOfCriticalSectionToUse = DummyCriticalSection> | |||
class ReferenceCountedArray | |||
{ | |||
public: | |||
typedef ReferenceCountedObjectPtr<ObjectClass> ObjectClassPtr; | |||
//============================================================================== | |||
/** Creates an empty array. | |||
@see ReferenceCountedObject, Array, OwnedArray | |||
*/ | |||
ReferenceCountedArray() noexcept | |||
: numUsed (0) | |||
{ | |||
} | |||
/** Creates a copy of another array */ | |||
ReferenceCountedArray (const ReferenceCountedArray& other) noexcept | |||
{ | |||
const ScopedLockType lock (other.getLock()); | |||
numUsed = other.size(); | |||
data.setAllocatedSize (numUsed); | |||
memcpy (data.elements, other.getRawDataPointer(), (size_t) numUsed * sizeof (ObjectClass*)); | |||
for (int i = numUsed; --i >= 0;) | |||
if (ObjectClass* o = data.elements[i]) | |||
o->incReferenceCount(); | |||
} | |||
/** Creates a copy of another array */ | |||
template <class OtherObjectClass, class OtherCriticalSection> | |||
ReferenceCountedArray (const ReferenceCountedArray<OtherObjectClass, OtherCriticalSection>& other) noexcept | |||
{ | |||
const typename ReferenceCountedArray<OtherObjectClass, OtherCriticalSection>::ScopedLockType lock (other.getLock()); | |||
numUsed = other.size(); | |||
data.setAllocatedSize (numUsed); | |||
memcpy (data.elements, other.getRawDataPointer(), numUsed * sizeof (ObjectClass*)); | |||
for (int i = numUsed; --i >= 0;) | |||
if (ObjectClass* o = data.elements[i]) | |||
o->incReferenceCount(); | |||
} | |||
/** Copies another array into this one. | |||
Any existing objects in this array will first be released. | |||
*/ | |||
ReferenceCountedArray& operator= (const ReferenceCountedArray& other) noexcept | |||
{ | |||
ReferenceCountedArray otherCopy (other); | |||
swapWith (otherCopy); | |||
return *this; | |||
} | |||
/** Copies another array into this one. | |||
Any existing objects in this array will first be released. | |||
*/ | |||
template <class OtherObjectClass> | |||
ReferenceCountedArray<ObjectClass, TypeOfCriticalSectionToUse>& operator= (const ReferenceCountedArray<OtherObjectClass, TypeOfCriticalSectionToUse>& other) noexcept | |||
{ | |||
ReferenceCountedArray<ObjectClass, TypeOfCriticalSectionToUse> otherCopy (other); | |||
swapWith (otherCopy); | |||
return *this; | |||
} | |||
/** Destructor. | |||
Any objects in the array will be released, and may be deleted if not referenced from elsewhere. | |||
*/ | |||
~ReferenceCountedArray() | |||
{ | |||
releaseAllObjects(); | |||
} | |||
//============================================================================== | |||
/** Removes all objects from the array. | |||
Any objects in the array that whose reference counts drop to zero will be deleted. | |||
*/ | |||
void clear() | |||
{ | |||
const ScopedLockType lock (getLock()); | |||
releaseAllObjects(); | |||
data.setAllocatedSize (0); | |||
} | |||
/** Removes all objects from the array without freeing the array's allocated storage. | |||
Any objects in the array that whose reference counts drop to zero will be deleted. | |||
@see clear | |||
*/ | |||
void clearQuick() | |||
{ | |||
const ScopedLockType lock (getLock()); | |||
releaseAllObjects(); | |||
} | |||
/** Returns the current number of objects in the array. */ | |||
inline int size() const noexcept | |||
{ | |||
return numUsed; | |||
} | |||
/** Returns true if the array is empty, false otherwise. */ | |||
inline bool isEmpty() const noexcept | |||
{ | |||
return size() == 0; | |||
} | |||
/** Returns a pointer to the object at this index in the array. | |||
If the index is out-of-range, this will return a null pointer, (and | |||
it could be null anyway, because it's ok for the array to hold null | |||
pointers as well as objects). | |||
@see getUnchecked | |||
*/ | |||
inline ObjectClassPtr operator[] (const int index) const noexcept | |||
{ | |||
return getObjectPointer (index); | |||
} | |||
/** Returns a pointer to the object at this index in the array, without checking | |||
whether the index is in-range. | |||
This is a faster and less safe version of operator[] which doesn't check the index passed in, so | |||
it can be used when you're sure the index is always going to be legal. | |||
*/ | |||
inline ObjectClassPtr getUnchecked (const int index) const noexcept | |||
{ | |||
return getObjectPointerUnchecked (index); | |||
} | |||
/** Returns a raw pointer to the object at this index in the array. | |||
If the index is out-of-range, this will return a null pointer, (and | |||
it could be null anyway, because it's ok for the array to hold null | |||
pointers as well as objects). | |||
@see getUnchecked | |||
*/ | |||
inline ObjectClass* getObjectPointer (const int index) const noexcept | |||
{ | |||
const ScopedLockType lock (getLock()); | |||
if (isPositiveAndBelow (index, numUsed)) | |||
{ | |||
jassert (data.elements != nullptr); | |||
return data.elements [index]; | |||
} | |||
return ObjectClassPtr(); | |||
} | |||
/** Returns a raw pointer to the object at this index in the array, without checking | |||
whether the index is in-range. | |||
*/ | |||
inline ObjectClass* getObjectPointerUnchecked (const int index) const noexcept | |||
{ | |||
const ScopedLockType lock (getLock()); | |||
jassert (isPositiveAndBelow (index, numUsed) && data.elements != nullptr); | |||
return data.elements [index]; | |||
} | |||
/** Returns a pointer to the first object in the array. | |||
This will return a null pointer if the array's empty. | |||
@see getLast | |||
*/ | |||
inline ObjectClassPtr getFirst() const noexcept | |||
{ | |||
const ScopedLockType lock (getLock()); | |||
if (numUsed > 0) | |||
{ | |||
jassert (data.elements != nullptr); | |||
return data.elements [0]; | |||
} | |||
return ObjectClassPtr(); | |||
} | |||
/** Returns a pointer to the last object in the array. | |||
This will return a null pointer if the array's empty. | |||
@see getFirst | |||
*/ | |||
inline ObjectClassPtr getLast() const noexcept | |||
{ | |||
const ScopedLockType lock (getLock()); | |||
if (numUsed > 0) | |||
{ | |||
jassert (data.elements != nullptr); | |||
return data.elements [numUsed - 1]; | |||
} | |||
return ObjectClassPtr(); | |||
} | |||
/** Returns a pointer to the actual array data. | |||
This pointer will only be valid until the next time a non-const method | |||
is called on the array. | |||
*/ | |||
inline ObjectClass** getRawDataPointer() const noexcept | |||
{ | |||
return data.elements; | |||
} | |||
//============================================================================== | |||
/** Returns a pointer to the first element in the array. | |||
This method is provided for compatibility with standard C++ iteration mechanisms. | |||
*/ | |||
inline ObjectClass** begin() const noexcept | |||
{ | |||
return data.elements; | |||
} | |||
/** Returns a pointer to the element which follows the last element in the array. | |||
This method is provided for compatibility with standard C++ iteration mechanisms. | |||
*/ | |||
inline ObjectClass** end() const noexcept | |||
{ | |||
return data.elements + numUsed; | |||
} | |||
//============================================================================== | |||
/** Finds the index of the first occurrence of an object in the array. | |||
@param objectToLookFor the object to look for | |||
@returns the index at which the object was found, or -1 if it's not found | |||
*/ | |||
int indexOf (const ObjectClass* const objectToLookFor) const noexcept | |||
{ | |||
const ScopedLockType lock (getLock()); | |||
ObjectClass** e = data.elements.get(); | |||
ObjectClass** const endPointer = e + numUsed; | |||
while (e != endPointer) | |||
{ | |||
if (objectToLookFor == *e) | |||
return static_cast<int> (e - data.elements.get()); | |||
++e; | |||
} | |||
return -1; | |||
} | |||
/** Returns true if the array contains a specified object. | |||
@param objectToLookFor the object to look for | |||
@returns true if the object is in the array | |||
*/ | |||
bool contains (const ObjectClass* const objectToLookFor) const noexcept | |||
{ | |||
const ScopedLockType lock (getLock()); | |||
ObjectClass** e = data.elements.get(); | |||
ObjectClass** const endPointer = e + numUsed; | |||
while (e != endPointer) | |||
{ | |||
if (objectToLookFor == *e) | |||
return true; | |||
++e; | |||
} | |||
return false; | |||
} | |||
/** Appends a new object to the end of the array. | |||
This will increase the new object's reference count. | |||
@param newObject the new object to add to the array | |||
@see set, insert, addIfNotAlreadyThere, addSorted, addArray | |||
*/ | |||
ObjectClass* add (ObjectClass* const newObject) noexcept | |||
{ | |||
const ScopedLockType lock (getLock()); | |||
data.ensureAllocatedSize (numUsed + 1); | |||
jassert (data.elements != nullptr); | |||
data.elements [numUsed++] = newObject; | |||
if (newObject != nullptr) | |||
newObject->incReferenceCount(); | |||
return newObject; | |||
} | |||
/** Inserts a new object into the array at the given index. | |||
If the index is less than 0 or greater than the size of the array, the | |||
element will be added to the end of the array. | |||
Otherwise, it will be inserted into the array, moving all the later elements | |||
along to make room. | |||
This will increase the new object's reference count. | |||
@param indexToInsertAt the index at which the new element should be inserted | |||
@param newObject the new object to add to the array | |||
@see add, addSorted, addIfNotAlreadyThere, set | |||
*/ | |||
ObjectClass* insert (int indexToInsertAt, | |||
ObjectClass* const newObject) noexcept | |||
{ | |||
if (indexToInsertAt < 0) | |||
return add (newObject); | |||
const ScopedLockType lock (getLock()); | |||
if (indexToInsertAt > numUsed) | |||
indexToInsertAt = numUsed; | |||
data.ensureAllocatedSize (numUsed + 1); | |||
jassert (data.elements != nullptr); | |||
ObjectClass** const e = data.elements + indexToInsertAt; | |||
const int numToMove = numUsed - indexToInsertAt; | |||
if (numToMove > 0) | |||
memmove (e + 1, e, sizeof (ObjectClass*) * (size_t) numToMove); | |||
*e = newObject; | |||
if (newObject != nullptr) | |||
newObject->incReferenceCount(); | |||
++numUsed; | |||
return newObject; | |||
} | |||
/** Appends a new object at the end of the array as long as the array doesn't | |||
already contain it. | |||
If the array already contains a matching object, nothing will be done. | |||
@param newObject the new object to add to the array | |||
@returns true if the object has been added, false otherwise | |||
*/ | |||
bool addIfNotAlreadyThere (ObjectClass* const newObject) noexcept | |||
{ | |||
const ScopedLockType lock (getLock()); | |||
if (contains (newObject)) | |||
return false; | |||
add (newObject); | |||
return true; | |||
} | |||
/** Replaces an object in the array with a different one. | |||
If the index is less than zero, this method does nothing. | |||
If the index is beyond the end of the array, the new object is added to the end of the array. | |||
The object being added has its reference count increased, and if it's replacing | |||
another object, then that one has its reference count decreased, and may be deleted. | |||
@param indexToChange the index whose value you want to change | |||
@param newObject the new value to set for this index. | |||
@see add, insert, remove | |||
*/ | |||
void set (const int indexToChange, | |||
ObjectClass* const newObject) | |||
{ | |||
if (indexToChange >= 0) | |||
{ | |||
const ScopedLockType lock (getLock()); | |||
if (newObject != nullptr) | |||
newObject->incReferenceCount(); | |||
if (indexToChange < numUsed) | |||
{ | |||
if (ObjectClass* o = data.elements [indexToChange]) | |||
releaseObject (o); | |||
data.elements [indexToChange] = newObject; | |||
} | |||
else | |||
{ | |||
data.ensureAllocatedSize (numUsed + 1); | |||
jassert (data.elements != nullptr); | |||
data.elements [numUsed++] = newObject; | |||
} | |||
} | |||
} | |||
/** Adds elements from another array to the end of this array. | |||
@param arrayToAddFrom the array from which to copy the elements | |||
@param startIndex the first element of the other array to start copying from | |||
@param numElementsToAdd how many elements to add from the other array. If this | |||
value is negative or greater than the number of available elements, | |||
all available elements will be copied. | |||
@see add | |||
*/ | |||
void addArray (const ReferenceCountedArray<ObjectClass, TypeOfCriticalSectionToUse>& arrayToAddFrom, | |||
int startIndex = 0, | |||
int numElementsToAdd = -1) noexcept | |||
{ | |||
const ScopedLockType lock1 (arrayToAddFrom.getLock()); | |||
{ | |||
const ScopedLockType lock2 (getLock()); | |||
if (startIndex < 0) | |||
{ | |||
jassertfalse; | |||
startIndex = 0; | |||
} | |||
if (numElementsToAdd < 0 || startIndex + numElementsToAdd > arrayToAddFrom.size()) | |||
numElementsToAdd = arrayToAddFrom.size() - startIndex; | |||
if (numElementsToAdd > 0) | |||
{ | |||
data.ensureAllocatedSize (numUsed + numElementsToAdd); | |||
while (--numElementsToAdd >= 0) | |||
add (arrayToAddFrom.getUnchecked (startIndex++)); | |||
} | |||
} | |||
} | |||
/** Inserts a new object into the array assuming that the array is sorted. | |||
This will use a comparator to find the position at which the new object | |||
should go. If the array isn't sorted, the behaviour of this | |||
method will be unpredictable. | |||
@param comparator the comparator object to use to compare the elements - see the | |||
sort() method for details about this object's form | |||
@param newObject the new object to insert to the array | |||
@returns the index at which the new object was added | |||
@see add, sort | |||
*/ | |||
template <class ElementComparator> | |||
int addSorted (ElementComparator& comparator, ObjectClass* newObject) noexcept | |||
{ | |||
const ScopedLockType lock (getLock()); | |||
const int index = findInsertIndexInSortedArray (comparator, data.elements.get(), newObject, 0, numUsed); | |||
insert (index, newObject); | |||
return index; | |||
} | |||
/** Inserts or replaces an object in the array, assuming it is sorted. | |||
This is similar to addSorted, but if a matching element already exists, then it will be | |||
replaced by the new one, rather than the new one being added as well. | |||
*/ | |||
template <class ElementComparator> | |||
void addOrReplaceSorted (ElementComparator& comparator, | |||
ObjectClass* newObject) noexcept | |||
{ | |||
const ScopedLockType lock (getLock()); | |||
const int index = findInsertIndexInSortedArray (comparator, data.elements.get(), newObject, 0, numUsed); | |||
if (index > 0 && comparator.compareElements (newObject, data.elements [index - 1]) == 0) | |||
set (index - 1, newObject); // replace an existing object that matches | |||
else | |||
insert (index, newObject); // no match, so insert the new one | |||
} | |||
/** Finds the index of an object in the array, assuming that the array is sorted. | |||
This will use a comparator to do a binary-chop to find the index of the given | |||
element, if it exists. If the array isn't sorted, the behaviour of this | |||
method will be unpredictable. | |||
@param comparator the comparator to use to compare the elements - see the sort() | |||
method for details about the form this object should take | |||
@param objectToLookFor the object to search for | |||
@returns the index of the element, or -1 if it's not found | |||
@see addSorted, sort | |||
*/ | |||
template <class ElementComparator> | |||
int indexOfSorted (ElementComparator& comparator, | |||
const ObjectClass* const objectToLookFor) const noexcept | |||
{ | |||
ignoreUnused (comparator); | |||
const ScopedLockType lock (getLock()); | |||
int s = 0, e = numUsed; | |||
while (s < e) | |||
{ | |||
if (comparator.compareElements (objectToLookFor, data.elements [s]) == 0) | |||
return s; | |||
const int halfway = (s + e) / 2; | |||
if (halfway == s) | |||
break; | |||
if (comparator.compareElements (objectToLookFor, data.elements [halfway]) >= 0) | |||
s = halfway; | |||
else | |||
e = halfway; | |||
} | |||
return -1; | |||
} | |||
//============================================================================== | |||
/** Removes an object from the array. | |||
This will remove the object at a given index and move back all the | |||
subsequent objects to close the gap. | |||
If the index passed in is out-of-range, nothing will happen. | |||
The object that is removed will have its reference count decreased, | |||
and may be deleted if not referenced from elsewhere. | |||
@param indexToRemove the index of the element to remove | |||
@see removeObject, removeRange | |||
*/ | |||
void remove (const int indexToRemove) | |||
{ | |||
const ScopedLockType lock (getLock()); | |||
if (isPositiveAndBelow (indexToRemove, numUsed)) | |||
{ | |||
ObjectClass** const e = data.elements + indexToRemove; | |||
if (ObjectClass* o = *e) | |||
releaseObject (o); | |||
--numUsed; | |||
const int numberToShift = numUsed - indexToRemove; | |||
if (numberToShift > 0) | |||
memmove (e, e + 1, sizeof (ObjectClass*) * (size_t) numberToShift); | |||
if ((numUsed << 1) < data.numAllocated) | |||
minimiseStorageOverheads(); | |||
} | |||
} | |||
/** Removes and returns an object from the array. | |||
This will remove the object at a given index and return it, moving back all | |||
the subsequent objects to close the gap. If the index passed in is out-of-range, | |||
nothing will happen and a null pointer will be returned. | |||
@param indexToRemove the index of the element to remove | |||
@see remove, removeObject, removeRange | |||
*/ | |||
ObjectClassPtr removeAndReturn (const int indexToRemove) | |||
{ | |||
ObjectClassPtr removedItem; | |||
const ScopedLockType lock (getLock()); | |||
if (isPositiveAndBelow (indexToRemove, numUsed)) | |||
{ | |||
ObjectClass** const e = data.elements + indexToRemove; | |||
if (ObjectClass* o = *e) | |||
{ | |||
removedItem = o; | |||
releaseObject (o); | |||
} | |||
--numUsed; | |||
const int numberToShift = numUsed - indexToRemove; | |||
if (numberToShift > 0) | |||
memmove (e, e + 1, sizeof (ObjectClass*) * (size_t) numberToShift); | |||
if ((numUsed << 1) < data.numAllocated) | |||
minimiseStorageOverheads(); | |||
} | |||
return removedItem; | |||
} | |||
/** Removes the first occurrence of a specified object from the array. | |||
If the item isn't found, no action is taken. If it is found, it is | |||
removed and has its reference count decreased. | |||
@param objectToRemove the object to try to remove | |||
@see remove, removeRange | |||
*/ | |||
void removeObject (ObjectClass* const objectToRemove) | |||
{ | |||
const ScopedLockType lock (getLock()); | |||
remove (indexOf (objectToRemove)); | |||
} | |||
/** Removes a range of objects from the array. | |||
This will remove a set of objects, starting from the given index, | |||
and move any subsequent elements down to close the gap. | |||
If the range extends beyond the bounds of the array, it will | |||
be safely clipped to the size of the array. | |||
The objects that are removed will have their reference counts decreased, | |||
and may be deleted if not referenced from elsewhere. | |||
@param startIndex the index of the first object to remove | |||
@param numberToRemove how many objects should be removed | |||
@see remove, removeObject | |||
*/ | |||
void removeRange (const int startIndex, | |||
const int numberToRemove) | |||
{ | |||
const ScopedLockType lock (getLock()); | |||
const int start = jlimit (0, numUsed, startIndex); | |||
const int endIndex = jlimit (0, numUsed, startIndex + numberToRemove); | |||
if (endIndex > start) | |||
{ | |||
int i; | |||
for (i = start; i < endIndex; ++i) | |||
{ | |||
if (ObjectClass* o = data.elements[i]) | |||
{ | |||
releaseObject (o); | |||
data.elements[i] = nullptr; // (in case one of the destructors accesses this array and hits a dangling pointer) | |||
} | |||
} | |||
const int rangeSize = endIndex - start; | |||
ObjectClass** e = data.elements + start; | |||
i = numUsed - endIndex; | |||
numUsed -= rangeSize; | |||
while (--i >= 0) | |||
{ | |||
*e = e [rangeSize]; | |||
++e; | |||
} | |||
if ((numUsed << 1) < data.numAllocated) | |||
minimiseStorageOverheads(); | |||
} | |||
} | |||
/** Removes the last n objects from the array. | |||
The objects that are removed will have their reference counts decreased, | |||
and may be deleted if not referenced from elsewhere. | |||
@param howManyToRemove how many objects to remove from the end of the array | |||
@see remove, removeObject, removeRange | |||
*/ | |||
void removeLast (int howManyToRemove = 1) | |||
{ | |||
const ScopedLockType lock (getLock()); | |||
if (howManyToRemove > numUsed) | |||
howManyToRemove = numUsed; | |||
while (--howManyToRemove >= 0) | |||
remove (numUsed - 1); | |||
} | |||
/** Swaps a pair of objects in the array. | |||
If either of the indexes passed in is out-of-range, nothing will happen, | |||
otherwise the two objects at these positions will be exchanged. | |||
*/ | |||
void swap (const int index1, | |||
const int index2) noexcept | |||
{ | |||
const ScopedLockType lock (getLock()); | |||
if (isPositiveAndBelow (index1, numUsed) | |||
&& isPositiveAndBelow (index2, numUsed)) | |||
{ | |||
std::swap (data.elements [index1], | |||
data.elements [index2]); | |||
} | |||
} | |||
/** Moves one of the objects to a different position. | |||
This will move the object to a specified index, shuffling along | |||
any intervening elements as required. | |||
So for example, if you have the array { 0, 1, 2, 3, 4, 5 } then calling | |||
move (2, 4) would result in { 0, 1, 3, 4, 2, 5 }. | |||
@param currentIndex the index of the object to be moved. If this isn't a | |||
valid index, then nothing will be done | |||
@param newIndex the index at which you'd like this object to end up. If this | |||
is less than zero, it will be moved to the end of the array | |||
*/ | |||
void move (const int currentIndex, | |||
int newIndex) noexcept | |||
{ | |||
if (currentIndex != newIndex) | |||
{ | |||
const ScopedLockType lock (getLock()); | |||
if (isPositiveAndBelow (currentIndex, numUsed)) | |||
{ | |||
if (! isPositiveAndBelow (newIndex, numUsed)) | |||
newIndex = numUsed - 1; | |||
ObjectClass* const value = data.elements [currentIndex]; | |||
if (newIndex > currentIndex) | |||
{ | |||
memmove (data.elements + currentIndex, | |||
data.elements + currentIndex + 1, | |||
sizeof (ObjectClass*) * (size_t) (newIndex - currentIndex)); | |||
} | |||
else | |||
{ | |||
memmove (data.elements + newIndex + 1, | |||
data.elements + newIndex, | |||
sizeof (ObjectClass*) * (size_t) (currentIndex - newIndex)); | |||
} | |||
data.elements [newIndex] = value; | |||
} | |||
} | |||
} | |||
//============================================================================== | |||
/** This swaps the contents of this array with those of another array. | |||
If you need to exchange two arrays, this is vastly quicker than using copy-by-value | |||
because it just swaps their internal pointers. | |||
*/ | |||
template <class OtherArrayType> | |||
void swapWith (OtherArrayType& otherArray) noexcept | |||
{ | |||
const ScopedLockType lock1 (getLock()); | |||
const typename OtherArrayType::ScopedLockType lock2 (otherArray.getLock()); | |||
data.swapWith (otherArray.data); | |||
std::swap (numUsed, otherArray.numUsed); | |||
} | |||
//============================================================================== | |||
/** Compares this array to another one. | |||
@returns true only if the other array contains the same objects in the same order | |||
*/ | |||
bool operator== (const ReferenceCountedArray& other) const noexcept | |||
{ | |||
const ScopedLockType lock2 (other.getLock()); | |||
const ScopedLockType lock1 (getLock()); | |||
if (numUsed != other.numUsed) | |||
return false; | |||
for (int i = numUsed; --i >= 0;) | |||
if (data.elements [i] != other.data.elements [i]) | |||
return false; | |||
return true; | |||
} | |||
/** Compares this array to another one. | |||
@see operator== | |||
*/ | |||
bool operator!= (const ReferenceCountedArray<ObjectClass, TypeOfCriticalSectionToUse>& other) const noexcept | |||
{ | |||
return ! operator== (other); | |||
} | |||
//============================================================================== | |||
/** Sorts the elements in the array. | |||
This will use a comparator object to sort the elements into order. The object | |||
passed must have a method of the form: | |||
@code | |||
int compareElements (ElementType first, ElementType second); | |||
@endcode | |||
..and this method must return: | |||
- a value of < 0 if the first comes before the second | |||
- a value of 0 if the two objects are equivalent | |||
- a value of > 0 if the second comes before the first | |||
To improve performance, the compareElements() method can be declared as static or const. | |||
@param comparator the comparator to use for comparing elements. | |||
@param retainOrderOfEquivalentItems if this is true, then items | |||
which the comparator says are equivalent will be | |||
kept in the order in which they currently appear | |||
in the array. This is slower to perform, but may | |||
be important in some cases. If it's false, a faster | |||
algorithm is used, but equivalent elements may be | |||
rearranged. | |||
@see sortArray | |||
*/ | |||
template <class ElementComparator> | |||
void sort (ElementComparator& comparator, | |||
const bool retainOrderOfEquivalentItems = false) const noexcept | |||
{ | |||
ignoreUnused (comparator); // if you pass in an object with a static compareElements() method, this | |||
// avoids getting warning messages about the parameter being unused | |||
const ScopedLockType lock (getLock()); | |||
sortArray (comparator, data.elements.get(), 0, size() - 1, retainOrderOfEquivalentItems); | |||
} | |||
//============================================================================== | |||
/** Reduces the amount of storage being used by the array. | |||
Arrays typically allocate slightly more storage than they need, and after | |||
removing elements, they may have quite a lot of unused space allocated. | |||
This method will reduce the amount of allocated storage to a minimum. | |||
*/ | |||
void minimiseStorageOverheads() noexcept | |||
{ | |||
const ScopedLockType lock (getLock()); | |||
data.shrinkToNoMoreThan (numUsed); | |||
} | |||
/** Increases the array's internal storage to hold a minimum number of elements. | |||
Calling this before adding a large known number of elements means that | |||
the array won't have to keep dynamically resizing itself as the elements | |||
are added, and it'll therefore be more efficient. | |||
*/ | |||
void ensureStorageAllocated (const int minNumElements) | |||
{ | |||
const ScopedLockType lock (getLock()); | |||
data.ensureAllocatedSize (minNumElements); | |||
} | |||
//============================================================================== | |||
/** Returns the CriticalSection that locks this array. | |||
To lock, you can call getLock().enter() and getLock().exit(), or preferably use | |||
an object of ScopedLockType as an RAII lock for it. | |||
*/ | |||
inline const TypeOfCriticalSectionToUse& getLock() const noexcept { return data; } | |||
/** Returns the type of scoped lock to use for locking this array */ | |||
typedef typename TypeOfCriticalSectionToUse::ScopedLockType ScopedLockType; | |||
//============================================================================== | |||
#ifndef DOXYGEN | |||
// Note that the swapWithArray method has been replaced by a more flexible templated version, | |||
// and renamed "swapWith" to be more consistent with the names used in other classes. | |||
JUCE_DEPRECATED_WITH_BODY (void swapWithArray (ReferenceCountedArray& other) noexcept, { swapWith (other); }) | |||
#endif | |||
private: | |||
//============================================================================== | |||
ArrayAllocationBase <ObjectClass*, TypeOfCriticalSectionToUse> data; | |||
int numUsed; | |||
void releaseAllObjects() | |||
{ | |||
while (numUsed > 0) | |||
if (ObjectClass* o = data.elements [--numUsed]) | |||
releaseObject (o); | |||
jassert (numUsed == 0); | |||
} | |||
static void releaseObject (ObjectClass* o) | |||
{ | |||
if (o->decReferenceCountWithoutDeleting()) | |||
ContainerDeletePolicy<ObjectClass>::destroy (o); | |||
} | |||
}; | |||
} // namespace juce |
@@ -1,92 +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. | |||
============================================================================== | |||
*/ | |||
namespace juce | |||
{ | |||
//============================================================================== | |||
/** | |||
Helper class providing an RAII-based mechanism for temporarily setting and | |||
then re-setting a value. | |||
E.g. @code | |||
int x = 1; | |||
{ | |||
ScopedValueSetter setter (x, 2); | |||
// x is now 2 | |||
} | |||
// x is now 1 again | |||
{ | |||
ScopedValueSetter setter (x, 3, 4); | |||
// x is now 3 | |||
} | |||
// x is now 4 | |||
@endcode | |||
*/ | |||
template <typename ValueType> | |||
class ScopedValueSetter | |||
{ | |||
public: | |||
/** Creates a ScopedValueSetter that will immediately change the specified value to the | |||
given new value, and will then reset it to its original value when this object is deleted. | |||
*/ | |||
ScopedValueSetter (ValueType& valueToSet, | |||
ValueType newValue) | |||
: value (valueToSet), | |||
originalValue (valueToSet) | |||
{ | |||
valueToSet = newValue; | |||
} | |||
/** Creates a ScopedValueSetter that will immediately change the specified value to the | |||
given new value, and will then reset it to be valueWhenDeleted when this object is deleted. | |||
*/ | |||
ScopedValueSetter (ValueType& valueToSet, | |||
ValueType newValue, | |||
ValueType valueWhenDeleted) | |||
: value (valueToSet), | |||
originalValue (valueWhenDeleted) | |||
{ | |||
valueToSet = newValue; | |||
} | |||
~ScopedValueSetter() | |||
{ | |||
value = originalValue; | |||
} | |||
private: | |||
//============================================================================== | |||
ValueType& value; | |||
const ValueType originalValue; | |||
JUCE_DECLARE_NON_COPYABLE (ScopedValueSetter) | |||
}; | |||
} // namespace juce |
@@ -1,486 +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. | |||
============================================================================== | |||
*/ | |||
namespace juce | |||
{ | |||
#if JUCE_MSVC | |||
#pragma warning (push) | |||
#pragma warning (disable: 4512) | |||
#endif | |||
//============================================================================== | |||
/** | |||
Holds a set of unique primitive objects, such as ints or doubles. | |||
A set can only hold one item with a given value, so if for example it's a | |||
set of integers, attempting to add the same integer twice will do nothing | |||
the second time. | |||
Internally, the list of items is kept sorted (which means that whatever | |||
kind of primitive type is used must support the ==, <, >, <= and >= operators | |||
to determine the order), and searching the set for known values is very fast | |||
because it uses a binary-chop method. | |||
Note that if you're using a class or struct as the element type, it must be | |||
capable of being copied or moved with a straightforward memcpy, rather than | |||
needing construction and destruction code. | |||
To make all the set's methods thread-safe, pass in "CriticalSection" as the templated | |||
TypeOfCriticalSectionToUse parameter, instead of the default DummyCriticalSection. | |||
@see Array, OwnedArray, ReferenceCountedArray, StringArray, CriticalSection | |||
*/ | |||
template <class ElementType, class TypeOfCriticalSectionToUse = DummyCriticalSection> | |||
class SortedSet | |||
{ | |||
public: | |||
//============================================================================== | |||
/** Creates an empty set. */ | |||
// VS2013 doesn't allow defaulted noexcept constructors. | |||
SortedSet() noexcept {} | |||
/** 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)) {} | |||
/** 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; } | |||
/** Destructor. */ | |||
~SortedSet() noexcept {} | |||
//============================================================================== | |||
/** Compares this set to another one. | |||
Two sets are considered equal if they both contain the same set of elements. | |||
@param other the other set to compare with | |||
*/ | |||
bool operator== (const SortedSet<ElementType>& other) const noexcept | |||
{ | |||
return data == other.data; | |||
} | |||
/** Compares this set to another one. | |||
Two sets are considered equal if they both contain the same set of elements. | |||
@param other the other set to compare with | |||
*/ | |||
bool operator!= (const SortedSet<ElementType>& other) const noexcept | |||
{ | |||
return ! operator== (other); | |||
} | |||
//============================================================================== | |||
/** Removes all elements from the set. | |||
This will remove all the elements, and free any storage that the set is | |||
using. To clear it without freeing the storage, use the clearQuick() | |||
method instead. | |||
@see clearQuick | |||
*/ | |||
void clear() noexcept | |||
{ | |||
data.clear(); | |||
} | |||
/** Removes all elements from the set without freeing the array's allocated storage. | |||
@see clear | |||
*/ | |||
void clearQuick() noexcept | |||
{ | |||
data.clearQuick(); | |||
} | |||
//============================================================================== | |||
/** Returns the current number of elements in the set. */ | |||
inline int size() const noexcept | |||
{ | |||
return data.size(); | |||
} | |||
/** Returns true if the set is empty, false otherwise. */ | |||
inline bool isEmpty() const noexcept | |||
{ | |||
return size() == 0; | |||
} | |||
/** Returns one of the elements in the set. | |||
If the index passed in is beyond the range of valid elements, this | |||
will return zero. | |||
If you're certain that the index will always be a valid element, you | |||
can call getUnchecked() instead, which is faster. | |||
@param index the index of the element being requested (0 is the first element in the set) | |||
@see getUnchecked, getFirst, getLast | |||
*/ | |||
inline ElementType operator[] (const int index) const noexcept | |||
{ | |||
return data [index]; | |||
} | |||
/** Returns one of the elements in the set, without checking the index passed in. | |||
Unlike the operator[] method, this will try to return an element without | |||
checking that the index is within the bounds of the set, so should only | |||
be used when you're confident that it will always be a valid index. | |||
@param index the index of the element being requested (0 is the first element in the set) | |||
@see operator[], getFirst, getLast | |||
*/ | |||
inline ElementType getUnchecked (const int index) const noexcept | |||
{ | |||
return data.getUnchecked (index); | |||
} | |||
/** Returns a direct reference to one of the elements in the set, without checking the index passed in. | |||
This is like getUnchecked, but returns a direct reference to the element, so that | |||
you can alter it directly. Obviously this can be dangerous, so only use it when | |||
absolutely necessary. | |||
@param index the index of the element being requested (0 is the first element in the array) | |||
*/ | |||
inline ElementType& getReference (const int index) const noexcept | |||
{ | |||
return data.getReference (index); | |||
} | |||
/** Returns the first element in the set, or 0 if the set is empty. | |||
@see operator[], getUnchecked, getLast | |||
*/ | |||
inline ElementType getFirst() const noexcept | |||
{ | |||
return data.getFirst(); | |||
} | |||
/** Returns the last element in the set, or 0 if the set is empty. | |||
@see operator[], getUnchecked, getFirst | |||
*/ | |||
inline ElementType getLast() const noexcept | |||
{ | |||
return data.getLast(); | |||
} | |||
//============================================================================== | |||
/** Returns a pointer to the first element in the set. | |||
This method is provided for compatibility with standard C++ iteration mechanisms. | |||
*/ | |||
inline ElementType* begin() const noexcept | |||
{ | |||
return data.begin(); | |||
} | |||
/** Returns a pointer to the element which follows the last element in the set. | |||
This method is provided for compatibility with standard C++ iteration mechanisms. | |||
*/ | |||
inline ElementType* end() const noexcept | |||
{ | |||
return data.end(); | |||
} | |||
//============================================================================== | |||
/** Finds the index of the first element which matches the value passed in. | |||
This will search the set for the given object, and return the index | |||
of its first occurrence. If the object isn't found, the method will return -1. | |||
@param elementToLookFor the value or object to look for | |||
@returns the index of the object, or -1 if it's not found | |||
*/ | |||
int indexOf (const ElementType& elementToLookFor) const noexcept | |||
{ | |||
const ScopedLockType lock (data.getLock()); | |||
int s = 0; | |||
int e = data.size(); | |||
for (;;) | |||
{ | |||
if (s >= e) | |||
return -1; | |||
if (elementToLookFor == data.getReference (s)) | |||
return s; | |||
auto halfway = (s + e) / 2; | |||
if (halfway == s) | |||
return -1; | |||
if (elementToLookFor < data.getReference (halfway)) | |||
e = halfway; | |||
else | |||
s = halfway; | |||
} | |||
} | |||
/** Returns true if the set contains at least one occurrence of an object. | |||
@param elementToLookFor the value or object to look for | |||
@returns true if the item is found | |||
*/ | |||
bool contains (const ElementType& elementToLookFor) const noexcept | |||
{ | |||
return indexOf (elementToLookFor) >= 0; | |||
} | |||
//============================================================================== | |||
/** Adds a new element to the set, (as long as it's not already in there). | |||
Note that if a matching element already exists, the new value will be assigned | |||
to the existing one using operator=, so that if there are any differences between | |||
the objects which were not recognised by the object's operator==, then the | |||
set will always contain a copy of the most recently added one. | |||
@param newElement the new object to add to the set | |||
@returns true if the value was added, or false if it already existed | |||
@see set, insert, addIfNotAlreadyThere, addSorted, addSet, addArray | |||
*/ | |||
bool add (const ElementType& newElement) noexcept | |||
{ | |||
const ScopedLockType lock (getLock()); | |||
int s = 0; | |||
int e = data.size(); | |||
while (s < e) | |||
{ | |||
auto& elem = data.getReference (s); | |||
if (newElement == elem) | |||
{ | |||
elem = newElement; // force an update in case operator== permits differences. | |||
return false; | |||
} | |||
auto halfway = (s + e) / 2; | |||
bool isBeforeHalfway = (newElement < data.getReference (halfway)); | |||
if (halfway == s) | |||
{ | |||
if (! isBeforeHalfway) | |||
++s; | |||
break; | |||
} | |||
if (isBeforeHalfway) | |||
e = halfway; | |||
else | |||
s = halfway; | |||
} | |||
data.insert (s, newElement); | |||
return true; | |||
} | |||
/** Adds elements from an array to this set. | |||
@param elementsToAdd the array of elements to add | |||
@param numElementsToAdd how many elements are in this other array | |||
@see add | |||
*/ | |||
void addArray (const ElementType* elementsToAdd, | |||
int numElementsToAdd) noexcept | |||
{ | |||
const ScopedLockType lock (getLock()); | |||
while (--numElementsToAdd >= 0) | |||
add (*elementsToAdd++); | |||
} | |||
/** Adds elements from another set to this one. | |||
@param setToAddFrom the set from which to copy the elements | |||
@param startIndex the first element of the other set to start copying from | |||
@param numElementsToAdd how many elements to add from the other set. If this | |||
value is negative or greater than the number of available elements, | |||
all available elements will be copied. | |||
@see add | |||
*/ | |||
template <class OtherSetType> | |||
void addSet (const OtherSetType& setToAddFrom, | |||
int startIndex = 0, | |||
int numElementsToAdd = -1) noexcept | |||
{ | |||
const typename OtherSetType::ScopedLockType lock1 (setToAddFrom.getLock()); | |||
const ScopedLockType lock2 (getLock()); | |||
jassert (this != &setToAddFrom); | |||
if (this != &setToAddFrom) | |||
{ | |||
if (startIndex < 0) | |||
{ | |||
jassertfalse; | |||
startIndex = 0; | |||
} | |||
if (numElementsToAdd < 0 || startIndex + numElementsToAdd > setToAddFrom.size()) | |||
numElementsToAdd = setToAddFrom.size() - startIndex; | |||
if (numElementsToAdd > 0) | |||
addArray (&setToAddFrom.data.getReference (startIndex), numElementsToAdd); | |||
} | |||
} | |||
//============================================================================== | |||
/** Removes an element from the set. | |||
This will remove the element at a given index. | |||
If the index passed in is out-of-range, nothing will happen. | |||
@param indexToRemove the index of the element to remove | |||
@returns the element that has been removed | |||
@see removeValue, removeRange | |||
*/ | |||
ElementType remove (const int indexToRemove) noexcept | |||
{ | |||
return data.removeAndReturn (indexToRemove); | |||
} | |||
/** Removes an item from the set. | |||
This will remove the given element from the set, if it's there. | |||
@param valueToRemove the object to try to remove | |||
@see remove, removeRange | |||
*/ | |||
void removeValue (const ElementType valueToRemove) noexcept | |||
{ | |||
const ScopedLockType lock (getLock()); | |||
data.remove (indexOf (valueToRemove)); | |||
} | |||
/** Removes any elements which are also in another set. | |||
@param otherSet the other set in which to look for elements to remove | |||
@see removeValuesNotIn, remove, removeValue, removeRange | |||
*/ | |||
template <class OtherSetType> | |||
void removeValuesIn (const OtherSetType& otherSet) noexcept | |||
{ | |||
const typename OtherSetType::ScopedLockType lock1 (otherSet.getLock()); | |||
const ScopedLockType lock2 (getLock()); | |||
if (this == &otherSet) | |||
{ | |||
clear(); | |||
} | |||
else if (! otherSet.isEmpty()) | |||
{ | |||
for (int i = data.size(); --i >= 0;) | |||
if (otherSet.contains (data.getReference (i))) | |||
remove (i); | |||
} | |||
} | |||
/** Removes any elements which are not found in another set. | |||
Only elements which occur in this other set will be retained. | |||
@param otherSet the set in which to look for elements NOT to remove | |||
@see removeValuesIn, remove, removeValue, removeRange | |||
*/ | |||
template <class OtherSetType> | |||
void removeValuesNotIn (const OtherSetType& otherSet) noexcept | |||
{ | |||
const typename OtherSetType::ScopedLockType lock1 (otherSet.getLock()); | |||
const ScopedLockType lock2 (getLock()); | |||
if (this != &otherSet) | |||
{ | |||
if (otherSet.isEmpty()) | |||
{ | |||
clear(); | |||
} | |||
else | |||
{ | |||
for (int i = data.size(); --i >= 0;) | |||
if (! otherSet.contains (data.getReference (i))) | |||
remove (i); | |||
} | |||
} | |||
} | |||
/** This swaps the contents of this array with those of another array. | |||
If you need to exchange two arrays, this is vastly quicker than using copy-by-value | |||
because it just swaps their internal pointers. | |||
*/ | |||
template <class OtherSetType> | |||
void swapWith (OtherSetType& otherSet) noexcept | |||
{ | |||
data.swapWith (otherSet.data); | |||
} | |||
//============================================================================== | |||
/** Reduces the amount of storage being used by the set. | |||
Sets typically allocate slightly more storage than they need, and after | |||
removing elements, they may have quite a lot of unused space allocated. | |||
This method will reduce the amount of allocated storage to a minimum. | |||
*/ | |||
void minimiseStorageOverheads() noexcept | |||
{ | |||
data.minimiseStorageOverheads(); | |||
} | |||
/** Increases the set's internal storage to hold a minimum number of elements. | |||
Calling this before adding a large known number of elements means that | |||
the set won't have to keep dynamically resizing itself as the elements | |||
are added, and it'll therefore be more efficient. | |||
*/ | |||
void ensureStorageAllocated (const int minNumElements) | |||
{ | |||
data.ensureStorageAllocated (minNumElements); | |||
} | |||
//============================================================================== | |||
/** Returns the CriticalSection that locks this array. | |||
To lock, you can call getLock().enter() and getLock().exit(), or preferably use | |||
an object of ScopedLockType as an RAII lock for it. | |||
*/ | |||
inline const TypeOfCriticalSectionToUse& getLock() const noexcept { return data.getLock(); } | |||
/** Returns the type of scoped lock to use for locking this array */ | |||
typedef typename TypeOfCriticalSectionToUse::ScopedLockType ScopedLockType; | |||
private: | |||
//============================================================================== | |||
Array<ElementType, TypeOfCriticalSectionToUse> data; | |||
}; | |||
#if JUCE_MSVC | |||
#pragma warning (pop) | |||
#endif | |||
} // namespace juce |
@@ -1,289 +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. | |||
============================================================================== | |||
*/ | |||
namespace juce | |||
{ | |||
//============================================================================== | |||
/** | |||
Holds a set of primitive values, storing them as a set of ranges. | |||
This container acts like an array, but can efficiently hold large contiguous | |||
ranges of values. It's quite a specialised class, mostly useful for things | |||
like keeping the set of selected rows in a listbox. | |||
The type used as a template parameter must be an integer type, such as int, short, | |||
int64, etc. | |||
*/ | |||
template <class Type> | |||
class SparseSet | |||
{ | |||
public: | |||
//============================================================================== | |||
/** Creates a new empty set. */ | |||
SparseSet() | |||
{ | |||
} | |||
/** Creates a copy of another SparseSet. */ | |||
SparseSet (const SparseSet<Type>& other) | |||
: values (other.values) | |||
{ | |||
} | |||
//============================================================================== | |||
/** Clears the set. */ | |||
void clear() | |||
{ | |||
values.clear(); | |||
} | |||
/** Checks whether the set is empty. | |||
This is much quicker than using (size() == 0). | |||
*/ | |||
bool isEmpty() const noexcept | |||
{ | |||
return values.size() == 0; | |||
} | |||
/** Returns the number of values in the set. | |||
Because of the way the data is stored, this method can take longer if there | |||
are a lot of items in the set. Use isEmpty() for a quick test of whether there | |||
are any items. | |||
*/ | |||
Type size() const | |||
{ | |||
Type total (0); | |||
for (int i = 0; i < values.size(); i += 2) | |||
total += values.getUnchecked (i + 1) - values.getUnchecked (i); | |||
return total; | |||
} | |||
/** Returns one of the values in the set. | |||
@param index the index of the value to retrieve, in the range 0 to (size() - 1). | |||
@returns the value at this index, or 0 if it's out-of-range | |||
*/ | |||
Type operator[] (Type index) const | |||
{ | |||
for (int i = 0; i < values.size(); i += 2) | |||
{ | |||
const Type start (values.getUnchecked (i)); | |||
const Type len (values.getUnchecked (i + 1) - start); | |||
if (index < len) | |||
return start + index; | |||
index -= len; | |||
} | |||
return Type(); | |||
} | |||
/** Checks whether a particular value is in the set. */ | |||
bool contains (const Type valueToLookFor) const | |||
{ | |||
for (int i = 0; i < values.size(); ++i) | |||
if (valueToLookFor < values.getUnchecked(i)) | |||
return (i & 1) != 0; | |||
return false; | |||
} | |||
//============================================================================== | |||
/** Returns the number of contiguous blocks of values. | |||
@see getRange | |||
*/ | |||
int getNumRanges() const noexcept | |||
{ | |||
return values.size() >> 1; | |||
} | |||
/** Returns one of the contiguous ranges of values stored. | |||
@param rangeIndex the index of the range to look up, between 0 | |||
and (getNumRanges() - 1) | |||
@see getTotalRange | |||
*/ | |||
const Range<Type> getRange (const int rangeIndex) const | |||
{ | |||
if (isPositiveAndBelow (rangeIndex, getNumRanges())) | |||
return Range<Type> (values.getUnchecked (rangeIndex << 1), | |||
values.getUnchecked ((rangeIndex << 1) + 1)); | |||
return Range<Type>(); | |||
} | |||
/** Returns the range between the lowest and highest values in the set. | |||
@see getRange | |||
*/ | |||
Range<Type> getTotalRange() const | |||
{ | |||
if (values.size() > 0) | |||
{ | |||
jassert ((values.size() & 1) == 0); | |||
return Range<Type> (values.getUnchecked (0), | |||
values.getUnchecked (values.size() - 1)); | |||
} | |||
return Range<Type>(); | |||
} | |||
//============================================================================== | |||
/** Adds a range of contiguous values to the set. | |||
e.g. addRange (Range \<int\> (10, 14)) will add (10, 11, 12, 13) to the set. | |||
*/ | |||
void addRange (const Range<Type> range) | |||
{ | |||
jassert (range.getLength() >= 0); | |||
if (range.getLength() > 0) | |||
{ | |||
removeRange (range); | |||
values.addUsingDefaultSort (range.getStart()); | |||
values.addUsingDefaultSort (range.getEnd()); | |||
simplify(); | |||
} | |||
} | |||
/** Removes a range of values from the set. | |||
e.g. removeRange (Range\<int\> (10, 14)) will remove (10, 11, 12, 13) from the set. | |||
*/ | |||
void removeRange (const Range<Type> rangeToRemove) | |||
{ | |||
jassert (rangeToRemove.getLength() >= 0); | |||
if (rangeToRemove.getLength() > 0 | |||
&& values.size() > 0 | |||
&& rangeToRemove.getStart() < values.getUnchecked (values.size() - 1) | |||
&& values.getUnchecked(0) < rangeToRemove.getEnd()) | |||
{ | |||
const bool onAtStart = contains (rangeToRemove.getStart() - 1); | |||
const Type lastValue (jmin (rangeToRemove.getEnd(), values.getLast())); | |||
const bool onAtEnd = contains (lastValue); | |||
for (int i = values.size(); --i >= 0;) | |||
{ | |||
if (values.getUnchecked(i) <= lastValue) | |||
{ | |||
while (values.getUnchecked(i) >= rangeToRemove.getStart()) | |||
{ | |||
values.remove (i); | |||
if (--i < 0) | |||
break; | |||
} | |||
break; | |||
} | |||
} | |||
if (onAtStart) values.addUsingDefaultSort (rangeToRemove.getStart()); | |||
if (onAtEnd) values.addUsingDefaultSort (lastValue); | |||
simplify(); | |||
} | |||
} | |||
/** Does an XOR of the values in a given range. */ | |||
void invertRange (const Range<Type> range) | |||
{ | |||
SparseSet newItems; | |||
newItems.addRange (range); | |||
for (int i = getNumRanges(); --i >= 0;) | |||
newItems.removeRange (getRange (i)); | |||
removeRange (range); | |||
for (int i = newItems.getNumRanges(); --i >= 0;) | |||
addRange (newItems.getRange(i)); | |||
} | |||
/** Checks whether any part of a given range overlaps any part of this set. */ | |||
bool overlapsRange (const Range<Type> range) | |||
{ | |||
if (range.getLength() > 0) | |||
{ | |||
for (int i = getNumRanges(); --i >= 0;) | |||
{ | |||
if (values.getUnchecked ((i << 1) + 1) <= range.getStart()) | |||
return false; | |||
if (values.getUnchecked (i << 1) < range.getEnd()) | |||
return true; | |||
} | |||
} | |||
return false; | |||
} | |||
/** Checks whether the whole of a given range is contained within this one. */ | |||
bool containsRange (const Range<Type> range) | |||
{ | |||
if (range.getLength() > 0) | |||
{ | |||
for (int i = getNumRanges(); --i >= 0;) | |||
{ | |||
if (values.getUnchecked ((i << 1) + 1) <= range.getStart()) | |||
return false; | |||
if (values.getUnchecked (i << 1) <= range.getStart() | |||
&& range.getEnd() <= values.getUnchecked ((i << 1) + 1)) | |||
return true; | |||
} | |||
} | |||
return false; | |||
} | |||
//============================================================================== | |||
bool operator== (const SparseSet<Type>& other) noexcept | |||
{ | |||
return values == other.values; | |||
} | |||
bool operator!= (const SparseSet<Type>& other) noexcept | |||
{ | |||
return values != other.values; | |||
} | |||
private: | |||
//============================================================================== | |||
// alternating start/end values of ranges of values that are present. | |||
Array<Type, DummyCriticalSection> values; | |||
void simplify() | |||
{ | |||
jassert ((values.size() & 1) == 0); | |||
for (int i = values.size(); --i > 0;) | |||
if (values.getUnchecked(i) == values.getUnchecked (i - 1)) | |||
values.removeRange (--i, 2); | |||
} | |||
}; | |||
} // namespace juce |
@@ -1,803 +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. | |||
============================================================================== | |||
*/ | |||
namespace juce | |||
{ | |||
enum VariantStreamMarkers | |||
{ | |||
varMarker_Int = 1, | |||
varMarker_BoolTrue = 2, | |||
varMarker_BoolFalse = 3, | |||
varMarker_Double = 4, | |||
varMarker_String = 5, | |||
varMarker_Int64 = 6, | |||
varMarker_Array = 7, | |||
varMarker_Binary = 8, | |||
varMarker_Undefined = 9 | |||
}; | |||
//============================================================================== | |||
class var::VariantType | |||
{ | |||
public: | |||
VariantType() noexcept {} | |||
virtual ~VariantType() noexcept {} | |||
virtual int toInt (const ValueUnion&) const noexcept { return 0; } | |||
virtual int64 toInt64 (const ValueUnion&) const noexcept { return 0; } | |||
virtual double toDouble (const ValueUnion&) const noexcept { return 0; } | |||
virtual String toString (const ValueUnion&) const { return {}; } | |||
virtual bool toBool (const ValueUnion&) const noexcept { return false; } | |||
virtual ReferenceCountedObject* toObject (const ValueUnion&) const noexcept { return nullptr; } | |||
virtual Array<var>* toArray (const ValueUnion&) const noexcept { return nullptr; } | |||
virtual MemoryBlock* toBinary (const ValueUnion&) const noexcept { return nullptr; } | |||
virtual var clone (const var& original) const { return original; } | |||
virtual bool isVoid() const noexcept { return false; } | |||
virtual bool isUndefined() const noexcept { return false; } | |||
virtual bool isInt() const noexcept { return false; } | |||
virtual bool isInt64() const noexcept { return false; } | |||
virtual bool isBool() const noexcept { return false; } | |||
virtual bool isDouble() const noexcept { return false; } | |||
virtual bool isString() const noexcept { return false; } | |||
virtual bool isObject() const noexcept { return false; } | |||
virtual bool isArray() const noexcept { return false; } | |||
virtual bool isBinary() const noexcept { return false; } | |||
virtual bool isMethod() const noexcept { return false; } | |||
virtual void cleanUp (ValueUnion&) const noexcept {} | |||
virtual void createCopy (ValueUnion& dest, const ValueUnion& source) const { dest = source; } | |||
virtual bool equals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) const noexcept = 0; | |||
virtual void writeToStream (const ValueUnion& data, OutputStream& output) const = 0; | |||
}; | |||
//============================================================================== | |||
class var::VariantType_Void : public var::VariantType | |||
{ | |||
public: | |||
VariantType_Void() noexcept {} | |||
static const VariantType_Void instance; | |||
bool isVoid() const noexcept override { return true; } | |||
bool equals (const ValueUnion&, const ValueUnion&, const VariantType& otherType) const noexcept override { return otherType.isVoid() || otherType.isUndefined(); } | |||
void writeToStream (const ValueUnion&, OutputStream& output) const override { output.writeCompressedInt (0); } | |||
}; | |||
//============================================================================== | |||
class var::VariantType_Undefined : public var::VariantType | |||
{ | |||
public: | |||
VariantType_Undefined() noexcept {} | |||
static const VariantType_Undefined instance; | |||
bool isUndefined() const noexcept override { return true; } | |||
String toString (const ValueUnion&) const override { return "undefined"; } | |||
bool equals (const ValueUnion&, const ValueUnion&, const VariantType& otherType) const noexcept override { return otherType.isVoid() || otherType.isUndefined(); } | |||
void writeToStream (const ValueUnion&, OutputStream& output) const override | |||
{ | |||
output.writeCompressedInt (1); | |||
output.writeByte (varMarker_Undefined); | |||
} | |||
}; | |||
//============================================================================== | |||
class var::VariantType_Int : public var::VariantType | |||
{ | |||
public: | |||
VariantType_Int() noexcept {} | |||
static const VariantType_Int instance; | |||
int toInt (const ValueUnion& data) const noexcept override { return data.intValue; } | |||
int64 toInt64 (const ValueUnion& data) const noexcept override { return (int64) data.intValue; } | |||
double toDouble (const ValueUnion& data) const noexcept override { return (double) data.intValue; } | |||
String toString (const ValueUnion& data) const override { return String (data.intValue); } | |||
bool toBool (const ValueUnion& data) const noexcept override { return data.intValue != 0; } | |||
bool isInt() const noexcept override { return true; } | |||
bool equals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) const noexcept override | |||
{ | |||
if (otherType.isDouble() || otherType.isInt64() || otherType.isString()) | |||
return otherType.equals (otherData, data, *this); | |||
return otherType.toInt (otherData) == data.intValue; | |||
} | |||
void writeToStream (const ValueUnion& data, OutputStream& output) const override | |||
{ | |||
output.writeCompressedInt (5); | |||
output.writeByte (varMarker_Int); | |||
output.writeInt (data.intValue); | |||
} | |||
}; | |||
//============================================================================== | |||
class var::VariantType_Int64 : public var::VariantType | |||
{ | |||
public: | |||
VariantType_Int64() noexcept {} | |||
static const VariantType_Int64 instance; | |||
int toInt (const ValueUnion& data) const noexcept override { return (int) data.int64Value; } | |||
int64 toInt64 (const ValueUnion& data) const noexcept override { return data.int64Value; } | |||
double toDouble (const ValueUnion& data) const noexcept override { return (double) data.int64Value; } | |||
String toString (const ValueUnion& data) const override { return String (data.int64Value); } | |||
bool toBool (const ValueUnion& data) const noexcept override { return data.int64Value != 0; } | |||
bool isInt64() const noexcept override { return true; } | |||
bool equals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) const noexcept override | |||
{ | |||
if (otherType.isDouble() || otherType.isString()) | |||
return otherType.equals (otherData, data, *this); | |||
return otherType.toInt64 (otherData) == data.int64Value; | |||
} | |||
void writeToStream (const ValueUnion& data, OutputStream& output) const override | |||
{ | |||
output.writeCompressedInt (9); | |||
output.writeByte (varMarker_Int64); | |||
output.writeInt64 (data.int64Value); | |||
} | |||
}; | |||
//============================================================================== | |||
class var::VariantType_Double : public var::VariantType | |||
{ | |||
public: | |||
VariantType_Double() noexcept {} | |||
static const VariantType_Double instance; | |||
int toInt (const ValueUnion& data) const noexcept override { return (int) data.doubleValue; } | |||
int64 toInt64 (const ValueUnion& data) const noexcept override { return (int64) data.doubleValue; } | |||
double toDouble (const ValueUnion& data) const noexcept override { return data.doubleValue; } | |||
String toString (const ValueUnion& data) const override { return String (data.doubleValue, 20); } | |||
bool toBool (const ValueUnion& data) const noexcept override { return data.doubleValue != 0.0; } | |||
bool isDouble() const noexcept override { return true; } | |||
bool equals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) const noexcept override | |||
{ | |||
return std::abs (otherType.toDouble (otherData) - data.doubleValue) < std::numeric_limits<double>::epsilon(); | |||
} | |||
void writeToStream (const ValueUnion& data, OutputStream& output) const override | |||
{ | |||
output.writeCompressedInt (9); | |||
output.writeByte (varMarker_Double); | |||
output.writeDouble (data.doubleValue); | |||
} | |||
}; | |||
//============================================================================== | |||
class var::VariantType_Bool : public var::VariantType | |||
{ | |||
public: | |||
VariantType_Bool() noexcept {} | |||
static const VariantType_Bool instance; | |||
int toInt (const ValueUnion& data) const noexcept override { return data.boolValue ? 1 : 0; } | |||
int64 toInt64 (const ValueUnion& data) const noexcept override { return data.boolValue ? 1 : 0; } | |||
double toDouble (const ValueUnion& data) const noexcept override { return data.boolValue ? 1.0 : 0.0; } | |||
String toString (const ValueUnion& data) const override { return String::charToString (data.boolValue ? (juce_wchar) '1' : (juce_wchar) '0'); } | |||
bool toBool (const ValueUnion& data) const noexcept override { return data.boolValue; } | |||
bool isBool() const noexcept override { return true; } | |||
bool equals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) const noexcept override | |||
{ | |||
return otherType.toBool (otherData) == data.boolValue; | |||
} | |||
void writeToStream (const ValueUnion& data, OutputStream& output) const override | |||
{ | |||
output.writeCompressedInt (1); | |||
output.writeByte (data.boolValue ? (char) varMarker_BoolTrue : (char) varMarker_BoolFalse); | |||
} | |||
}; | |||
//============================================================================== | |||
class var::VariantType_String : public var::VariantType | |||
{ | |||
public: | |||
VariantType_String() noexcept {} | |||
static const VariantType_String instance; | |||
void cleanUp (ValueUnion& data) const noexcept override { getString (data)-> ~String(); } | |||
void createCopy (ValueUnion& dest, const ValueUnion& source) const override { new (dest.stringValue) String (*getString (source)); } | |||
bool isString() const noexcept override { return true; } | |||
int toInt (const ValueUnion& data) const noexcept override { return getString (data)->getIntValue(); } | |||
int64 toInt64 (const ValueUnion& data) const noexcept override { return getString (data)->getLargeIntValue(); } | |||
double toDouble (const ValueUnion& data) const noexcept override { return getString (data)->getDoubleValue(); } | |||
String toString (const ValueUnion& data) const override { return *getString (data); } | |||
bool toBool (const ValueUnion& data) const noexcept override { return getString (data)->getIntValue() != 0 | |||
|| getString (data)->trim().equalsIgnoreCase ("true") | |||
|| getString (data)->trim().equalsIgnoreCase ("yes"); } | |||
bool equals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) const noexcept override | |||
{ | |||
return otherType.toString (otherData) == *getString (data); | |||
} | |||
void writeToStream (const ValueUnion& data, OutputStream& output) const override | |||
{ | |||
auto* s = getString (data); | |||
const size_t len = s->getNumBytesAsUTF8() + 1; | |||
HeapBlock<char> temp (len); | |||
s->copyToUTF8 (temp, len); | |||
output.writeCompressedInt ((int) (len + 1)); | |||
output.writeByte (varMarker_String); | |||
output.write (temp, len); | |||
} | |||
private: | |||
static inline const String* getString (const ValueUnion& data) noexcept { return reinterpret_cast<const String*> (data.stringValue); } | |||
static inline String* getString (ValueUnion& data) noexcept { return reinterpret_cast<String*> (data.stringValue); } | |||
}; | |||
//============================================================================== | |||
class var::VariantType_Object : public var::VariantType | |||
{ | |||
public: | |||
VariantType_Object() noexcept {} | |||
static const VariantType_Object instance; | |||
void cleanUp (ValueUnion& data) const noexcept override { if (data.objectValue != nullptr) data.objectValue->decReferenceCount(); } | |||
void createCopy (ValueUnion& dest, const ValueUnion& source) const override | |||
{ | |||
dest.objectValue = source.objectValue; | |||
if (dest.objectValue != nullptr) | |||
dest.objectValue->incReferenceCount(); | |||
} | |||
String toString (const ValueUnion& data) const override { return "Object 0x" + String::toHexString ((int) (pointer_sized_int) data.objectValue); } | |||
bool toBool (const ValueUnion& data) const noexcept override { return data.objectValue != nullptr; } | |||
ReferenceCountedObject* toObject (const ValueUnion& data) const noexcept override { return data.objectValue; } | |||
bool isObject() const noexcept override { return true; } | |||
bool equals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) const noexcept override | |||
{ | |||
return otherType.toObject (otherData) == data.objectValue; | |||
} | |||
var clone (const var& original) const override | |||
{ | |||
if (auto* d = original.getDynamicObject()) | |||
return d->clone().get(); | |||
jassertfalse; // can only clone DynamicObjects! | |||
return {}; | |||
} | |||
void writeToStream (const ValueUnion&, OutputStream& output) const override | |||
{ | |||
jassertfalse; // Can't write an object to a stream! | |||
output.writeCompressedInt (0); | |||
} | |||
}; | |||
//============================================================================== | |||
class var::VariantType_Array : public var::VariantType_Object | |||
{ | |||
public: | |||
VariantType_Array() noexcept {} | |||
static const VariantType_Array instance; | |||
String toString (const ValueUnion&) const override { return "[Array]"; } | |||
ReferenceCountedObject* toObject (const ValueUnion&) const noexcept override { return nullptr; } | |||
bool isArray() const noexcept override { return true; } | |||
Array<var>* toArray (const ValueUnion& data) const noexcept override | |||
{ | |||
if (auto* a = dynamic_cast<RefCountedArray*> (data.objectValue)) | |||
return &(a->array); | |||
return nullptr; | |||
} | |||
bool equals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) const noexcept override | |||
{ | |||
auto* thisArray = toArray (data); | |||
auto* otherArray = otherType.toArray (otherData); | |||
return thisArray == otherArray || (thisArray != nullptr && otherArray != nullptr && *otherArray == *thisArray); | |||
} | |||
var clone (const var& original) const override | |||
{ | |||
Array<var> arrayCopy; | |||
if (auto* array = toArray (original.value)) | |||
{ | |||
arrayCopy.ensureStorageAllocated (array->size()); | |||
for (auto& i : *array) | |||
arrayCopy.add (i.clone()); | |||
} | |||
return var (arrayCopy); | |||
} | |||
void writeToStream (const ValueUnion& data, OutputStream& output) const override | |||
{ | |||
if (auto* array = toArray (data)) | |||
{ | |||
MemoryOutputStream buffer (512); | |||
buffer.writeCompressedInt (array->size()); | |||
for (auto& i : *array) | |||
i.writeToStream (buffer); | |||
output.writeCompressedInt (1 + (int) buffer.getDataSize()); | |||
output.writeByte (varMarker_Array); | |||
output << buffer; | |||
} | |||
} | |||
struct RefCountedArray : public ReferenceCountedObject | |||
{ | |||
RefCountedArray (const Array<var>& a) : array (a) { incReferenceCount(); } | |||
RefCountedArray (Array<var>&& a) : array (static_cast<Array<var>&&> (a)) { incReferenceCount(); } | |||
Array<var> array; | |||
}; | |||
}; | |||
//============================================================================== | |||
class var::VariantType_Binary : public var::VariantType | |||
{ | |||
public: | |||
VariantType_Binary() noexcept {} | |||
static const VariantType_Binary instance; | |||
void cleanUp (ValueUnion& data) const noexcept override { delete data.binaryValue; } | |||
void createCopy (ValueUnion& dest, const ValueUnion& source) const override { dest.binaryValue = new MemoryBlock (*source.binaryValue); } | |||
String toString (const ValueUnion& data) const override { return data.binaryValue->toBase64Encoding(); } | |||
bool isBinary() const noexcept override { return true; } | |||
MemoryBlock* toBinary (const ValueUnion& data) const noexcept override { return data.binaryValue; } | |||
bool equals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) const noexcept override | |||
{ | |||
const MemoryBlock* const otherBlock = otherType.toBinary (otherData); | |||
return otherBlock != nullptr && *otherBlock == *data.binaryValue; | |||
} | |||
void writeToStream (const ValueUnion& data, OutputStream& output) const override | |||
{ | |||
output.writeCompressedInt (1 + (int) data.binaryValue->getSize()); | |||
output.writeByte (varMarker_Binary); | |||
output << *data.binaryValue; | |||
} | |||
}; | |||
//============================================================================== | |||
class var::VariantType_Method : public var::VariantType | |||
{ | |||
public: | |||
VariantType_Method() noexcept {} | |||
static const VariantType_Method instance; | |||
void cleanUp (ValueUnion& data) const noexcept override { if (data.methodValue != nullptr ) delete data.methodValue; } | |||
void createCopy (ValueUnion& dest, const ValueUnion& source) const override { dest.methodValue = new NativeFunction (*source.methodValue); } | |||
String toString (const ValueUnion&) const override { return "Method"; } | |||
bool toBool (const ValueUnion& data) const noexcept override { return data.methodValue != nullptr; } | |||
bool isMethod() const noexcept override { return true; } | |||
bool equals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) const noexcept override | |||
{ | |||
return otherType.isMethod() && otherData.methodValue == data.methodValue; | |||
} | |||
void writeToStream (const ValueUnion&, OutputStream& output) const override | |||
{ | |||
jassertfalse; // Can't write a method to a stream! | |||
output.writeCompressedInt (0); | |||
} | |||
}; | |||
//============================================================================== | |||
const var::VariantType_Void var::VariantType_Void::instance; | |||
const var::VariantType_Undefined var::VariantType_Undefined::instance; | |||
const var::VariantType_Int var::VariantType_Int::instance; | |||
const var::VariantType_Int64 var::VariantType_Int64::instance; | |||
const var::VariantType_Bool var::VariantType_Bool::instance; | |||
const var::VariantType_Double var::VariantType_Double::instance; | |||
const var::VariantType_String var::VariantType_String::instance; | |||
const var::VariantType_Object var::VariantType_Object::instance; | |||
const var::VariantType_Array var::VariantType_Array::instance; | |||
const var::VariantType_Binary var::VariantType_Binary::instance; | |||
const var::VariantType_Method var::VariantType_Method::instance; | |||
//============================================================================== | |||
var::var() noexcept : type (&VariantType_Void::instance) {} | |||
var::var (const VariantType& t) noexcept : type (&t) {} | |||
var::~var() noexcept { type->cleanUp (value); } | |||
#if JUCE_ALLOW_STATIC_NULL_VARIABLES | |||
const var var::null; | |||
#endif | |||
//============================================================================== | |||
var::var (const var& valueToCopy) : type (valueToCopy.type) | |||
{ | |||
type->createCopy (value, valueToCopy.value); | |||
} | |||
var::var (const int v) noexcept : type (&VariantType_Int::instance) { value.intValue = v; } | |||
var::var (const int64 v) noexcept : type (&VariantType_Int64::instance) { value.int64Value = v; } | |||
var::var (const bool v) noexcept : type (&VariantType_Bool::instance) { value.boolValue = v; } | |||
var::var (const double v) noexcept : type (&VariantType_Double::instance) { value.doubleValue = v; } | |||
var::var (NativeFunction m) noexcept : type (&VariantType_Method::instance) { value.methodValue = new NativeFunction (m); } | |||
var::var (const Array<var>& v) : type (&VariantType_Array::instance) { value.objectValue = new VariantType_Array::RefCountedArray(v); } | |||
var::var (const String& v) : type (&VariantType_String::instance) { new (value.stringValue) String (v); } | |||
var::var (const char* const v) : type (&VariantType_String::instance) { new (value.stringValue) String (v); } | |||
var::var (const wchar_t* const v) : type (&VariantType_String::instance) { new (value.stringValue) String (v); } | |||
var::var (const void* v, size_t sz) : type (&VariantType_Binary::instance) { value.binaryValue = new MemoryBlock (v, sz); } | |||
var::var (const MemoryBlock& v) : type (&VariantType_Binary::instance) { value.binaryValue = new MemoryBlock (v); } | |||
var::var (const StringArray& v) : type (&VariantType_Array::instance) | |||
{ | |||
Array<var> strings; | |||
strings.ensureStorageAllocated (v.size()); | |||
for (auto& i : v) | |||
strings.add (var (i)); | |||
value.objectValue = new VariantType_Array::RefCountedArray (strings); | |||
} | |||
var::var (ReferenceCountedObject* const object) : type (&VariantType_Object::instance) | |||
{ | |||
value.objectValue = object; | |||
if (object != nullptr) | |||
object->incReferenceCount(); | |||
} | |||
var var::undefined() noexcept { return var (VariantType_Undefined::instance); } | |||
//============================================================================== | |||
bool var::isVoid() const noexcept { return type->isVoid(); } | |||
bool var::isUndefined() const noexcept { return type->isUndefined(); } | |||
bool var::isInt() const noexcept { return type->isInt(); } | |||
bool var::isInt64() const noexcept { return type->isInt64(); } | |||
bool var::isBool() const noexcept { return type->isBool(); } | |||
bool var::isDouble() const noexcept { return type->isDouble(); } | |||
bool var::isString() const noexcept { return type->isString(); } | |||
bool var::isObject() const noexcept { return type->isObject(); } | |||
bool var::isArray() const noexcept { return type->isArray(); } | |||
bool var::isBinaryData() const noexcept { return type->isBinary(); } | |||
bool var::isMethod() const noexcept { return type->isMethod(); } | |||
var::operator int() const noexcept { return type->toInt (value); } | |||
var::operator int64() const noexcept { return type->toInt64 (value); } | |||
var::operator bool() const noexcept { return type->toBool (value); } | |||
var::operator float() const noexcept { return (float) type->toDouble (value); } | |||
var::operator double() const noexcept { return type->toDouble (value); } | |||
String var::toString() const { return type->toString (value); } | |||
var::operator String() const { return type->toString (value); } | |||
ReferenceCountedObject* var::getObject() const noexcept { return type->toObject (value); } | |||
Array<var>* var::getArray() const noexcept { return type->toArray (value); } | |||
MemoryBlock* var::getBinaryData() const noexcept { return type->toBinary (value); } | |||
DynamicObject* var::getDynamicObject() const noexcept { return dynamic_cast<DynamicObject*> (getObject()); } | |||
//============================================================================== | |||
void var::swapWith (var& other) noexcept | |||
{ | |||
std::swap (type, other.type); | |||
std::swap (value, other.value); | |||
} | |||
var& var::operator= (const var& v) { type->cleanUp (value); type = v.type; type->createCopy (value, v.value); return *this; } | |||
var& var::operator= (const int v) { type->cleanUp (value); type = &VariantType_Int::instance; value.intValue = v; return *this; } | |||
var& var::operator= (const int64 v) { type->cleanUp (value); type = &VariantType_Int64::instance; value.int64Value = v; return *this; } | |||
var& var::operator= (const bool v) { type->cleanUp (value); type = &VariantType_Bool::instance; value.boolValue = v; return *this; } | |||
var& var::operator= (const double v) { type->cleanUp (value); type = &VariantType_Double::instance; value.doubleValue = v; return *this; } | |||
var& var::operator= (const char* const v) { type->cleanUp (value); type = &VariantType_String::instance; new (value.stringValue) String (v); return *this; } | |||
var& var::operator= (const wchar_t* const v) { type->cleanUp (value); type = &VariantType_String::instance; new (value.stringValue) String (v); return *this; } | |||
var& var::operator= (const String& v) { type->cleanUp (value); type = &VariantType_String::instance; new (value.stringValue) String (v); return *this; } | |||
var& var::operator= (const MemoryBlock& v) { type->cleanUp (value); type = &VariantType_Binary::instance; value.binaryValue = new MemoryBlock (v); return *this; } | |||
var& var::operator= (const Array<var>& v) { var v2 (v); swapWith (v2); return *this; } | |||
var& var::operator= (ReferenceCountedObject* v) { var v2 (v); swapWith (v2); return *this; } | |||
var& var::operator= (NativeFunction v) { var v2 (v); swapWith (v2); return *this; } | |||
var::var (var&& other) noexcept | |||
: type (other.type), | |||
value (other.value) | |||
{ | |||
other.type = &VariantType_Void::instance; | |||
} | |||
var& var::operator= (var&& other) noexcept | |||
{ | |||
swapWith (other); | |||
return *this; | |||
} | |||
var::var (String&& v) : type (&VariantType_String::instance) | |||
{ | |||
new (value.stringValue) String (static_cast<String&&> (v)); | |||
} | |||
var::var (MemoryBlock&& v) : type (&VariantType_Binary::instance) | |||
{ | |||
value.binaryValue = new MemoryBlock (static_cast<MemoryBlock&&> (v)); | |||
} | |||
var::var (Array<var>&& v) : type (&VariantType_Array::instance) | |||
{ | |||
value.objectValue = new VariantType_Array::RefCountedArray (static_cast<Array<var>&&> (v)); | |||
} | |||
var& var::operator= (String&& v) | |||
{ | |||
type->cleanUp (value); | |||
type = &VariantType_String::instance; | |||
new (value.stringValue) String (static_cast<String&&> (v)); | |||
return *this; | |||
} | |||
//============================================================================== | |||
bool var::equals (const var& other) const noexcept | |||
{ | |||
return type->equals (value, other.value, *other.type); | |||
} | |||
bool var::equalsWithSameType (const var& other) const noexcept | |||
{ | |||
return type == other.type && equals (other); | |||
} | |||
bool var::hasSameTypeAs (const var& other) const noexcept | |||
{ | |||
return type == other.type; | |||
} | |||
bool operator== (const var& v1, const var& v2) noexcept { return v1.equals (v2); } | |||
bool operator!= (const var& v1, const var& v2) noexcept { return ! v1.equals (v2); } | |||
bool operator== (const var& v1, const String& v2) { return v1.toString() == v2; } | |||
bool operator!= (const var& v1, const String& v2) { return v1.toString() != v2; } | |||
bool operator== (const var& v1, const char* const v2) { return v1.toString() == v2; } | |||
bool operator!= (const var& v1, const char* const v2) { return v1.toString() != v2; } | |||
//============================================================================== | |||
var var::clone() const noexcept | |||
{ | |||
return type->clone (*this); | |||
} | |||
//============================================================================== | |||
const var& var::operator[] (const Identifier& propertyName) const | |||
{ | |||
if (auto* o = getDynamicObject()) | |||
return o->getProperty (propertyName); | |||
return getNullVarRef(); | |||
} | |||
const var& var::operator[] (const char* const propertyName) const | |||
{ | |||
return operator[] (Identifier (propertyName)); | |||
} | |||
var var::getProperty (const Identifier& propertyName, const var& defaultReturnValue) const | |||
{ | |||
if (auto* o = getDynamicObject()) | |||
return o->getProperties().getWithDefault (propertyName, defaultReturnValue); | |||
return defaultReturnValue; | |||
} | |||
bool var::hasProperty (const Identifier& propertyName) const noexcept | |||
{ | |||
if (auto* o = getDynamicObject()) | |||
return o->hasProperty (propertyName); | |||
return false; | |||
} | |||
var::NativeFunction var::getNativeFunction() const | |||
{ | |||
return isMethod() && (value.methodValue != nullptr) ? *value.methodValue : nullptr; | |||
} | |||
var var::invoke (const Identifier& method, const var* arguments, int numArguments) const | |||
{ | |||
if (auto* o = getDynamicObject()) | |||
return o->invokeMethod (method, var::NativeFunctionArgs (*this, arguments, numArguments)); | |||
return {}; | |||
} | |||
var var::call (const Identifier& method) const | |||
{ | |||
return invoke (method, nullptr, 0); | |||
} | |||
var var::call (const Identifier& method, const var& arg1) const | |||
{ | |||
return invoke (method, &arg1, 1); | |||
} | |||
var var::call (const Identifier& method, const var& arg1, const var& arg2) const | |||
{ | |||
var args[] = { arg1, arg2 }; | |||
return invoke (method, args, 2); | |||
} | |||
var var::call (const Identifier& method, const var& arg1, const var& arg2, const var& arg3) | |||
{ | |||
var args[] = { arg1, arg2, arg3 }; | |||
return invoke (method, args, 3); | |||
} | |||
var var::call (const Identifier& method, const var& arg1, const var& arg2, const var& arg3, const var& arg4) const | |||
{ | |||
var args[] = { arg1, arg2, arg3, arg4 }; | |||
return invoke (method, args, 4); | |||
} | |||
var var::call (const Identifier& method, const var& arg1, const var& arg2, const var& arg3, const var& arg4, const var& arg5) const | |||
{ | |||
var args[] = { arg1, arg2, arg3, arg4, arg5 }; | |||
return invoke (method, args, 5); | |||
} | |||
//============================================================================== | |||
int var::size() const | |||
{ | |||
if (auto* array = getArray()) | |||
return array->size(); | |||
return 0; | |||
} | |||
const var& var::operator[] (int arrayIndex) const | |||
{ | |||
auto* array = getArray(); | |||
// When using this method, the var must actually be an array, and the index | |||
// must be in-range! | |||
jassert (array != nullptr && isPositiveAndBelow (arrayIndex, array->size())); | |||
return array->getReference (arrayIndex); | |||
} | |||
var& var::operator[] (int arrayIndex) | |||
{ | |||
auto* array = getArray(); | |||
// When using this method, the var must actually be an array, and the index | |||
// must be in-range! | |||
jassert (array != nullptr && isPositiveAndBelow (arrayIndex, array->size())); | |||
return array->getReference (arrayIndex); | |||
} | |||
Array<var>* var::convertToArray() | |||
{ | |||
if (auto* array = getArray()) | |||
return array; | |||
Array<var> tempVar; | |||
if (! isVoid()) | |||
tempVar.add (*this); | |||
*this = tempVar; | |||
return getArray(); | |||
} | |||
void var::append (const var& n) | |||
{ | |||
convertToArray()->add (n); | |||
} | |||
void var::remove (const int index) | |||
{ | |||
if (auto* const array = getArray()) | |||
array->remove (index); | |||
} | |||
void var::insert (const int index, const var& n) | |||
{ | |||
convertToArray()->insert (index, n); | |||
} | |||
void var::resize (const int numArrayElementsWanted) | |||
{ | |||
convertToArray()->resize (numArrayElementsWanted); | |||
} | |||
int var::indexOf (const var& n) const | |||
{ | |||
if (auto* const array = getArray()) | |||
return array->indexOf (n); | |||
return -1; | |||
} | |||
//============================================================================== | |||
void var::writeToStream (OutputStream& output) const | |||
{ | |||
type->writeToStream (value, output); | |||
} | |||
var var::readFromStream (InputStream& input) | |||
{ | |||
const int numBytes = input.readCompressedInt(); | |||
if (numBytes > 0) | |||
{ | |||
switch (input.readByte()) | |||
{ | |||
case varMarker_Int: return var (input.readInt()); | |||
case varMarker_Int64: return var (input.readInt64()); | |||
case varMarker_BoolTrue: return var (true); | |||
case varMarker_BoolFalse: return var (false); | |||
case varMarker_Double: return var (input.readDouble()); | |||
case varMarker_String: | |||
{ | |||
MemoryOutputStream mo; | |||
mo.writeFromInputStream (input, numBytes - 1); | |||
return var (mo.toUTF8()); | |||
} | |||
case varMarker_Binary: | |||
{ | |||
MemoryBlock mb ((size_t) numBytes - 1); | |||
if (numBytes > 1) | |||
{ | |||
const int numRead = input.read (mb.getData(), numBytes - 1); | |||
mb.setSize ((size_t) numRead); | |||
} | |||
return var (mb); | |||
} | |||
case varMarker_Array: | |||
{ | |||
var v; | |||
auto* destArray = v.convertToArray(); | |||
for (int i = input.readCompressedInt(); --i >= 0;) | |||
destArray->add (readFromStream (input)); | |||
return v; | |||
} | |||
default: | |||
input.skipNextBytes (numBytes - 1); break; | |||
} | |||
} | |||
return {}; | |||
} | |||
var::NativeFunctionArgs::NativeFunctionArgs (const var& t, const var* args, int numArgs) noexcept | |||
: thisObject (t), arguments (args), numArguments (numArgs) | |||
{} | |||
} // namespace juce |
@@ -1,340 +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. | |||
============================================================================== | |||
*/ | |||
namespace juce | |||
{ | |||
//============================================================================== | |||
/** | |||
A variant class, that can be used to hold a range of primitive values. | |||
A var object can hold a range of simple primitive values, strings, or | |||
any kind of ReferenceCountedObject. The var class is intended to act like | |||
the kind of values used in dynamic scripting languages. | |||
You can save/load var objects either in a small, proprietary binary format | |||
using writeToStream()/readFromStream(), or as JSON by using the JSON class. | |||
@see JSON, DynamicObject | |||
*/ | |||
class JUCE_API var | |||
{ | |||
public: | |||
//============================================================================== | |||
/** This structure is passed to a NativeFunction callback, and contains invocation | |||
details about the function's arguments and context. | |||
*/ | |||
struct JUCE_API NativeFunctionArgs | |||
{ | |||
NativeFunctionArgs (const var& thisObject, const var* args, int numArgs) noexcept; | |||
// Suppress a VS2013 compiler warning | |||
NativeFunctionArgs& operator= (const NativeFunctionArgs&) = delete; | |||
const var& thisObject; | |||
const var* arguments; | |||
int numArguments; | |||
}; | |||
using NativeFunction = std::function<var (const NativeFunctionArgs&)>; | |||
//============================================================================== | |||
/** Creates a void variant. */ | |||
var() noexcept; | |||
/** Destructor. */ | |||
~var() noexcept; | |||
#if JUCE_ALLOW_STATIC_NULL_VARIABLES | |||
/** A static var object that can be used where you need an empty variant object. */ | |||
static const var null; | |||
#endif | |||
var (const var& valueToCopy); | |||
var (int value) noexcept; | |||
var (int64 value) noexcept; | |||
var (bool value) noexcept; | |||
var (double value) noexcept; | |||
var (const char* value); | |||
var (const wchar_t* value); | |||
var (const String& value); | |||
var (const Array<var>& value); | |||
var (const StringArray& value); | |||
var (ReferenceCountedObject* object); | |||
var (NativeFunction method) noexcept; | |||
var (const void* binaryData, size_t dataSize); | |||
var (const MemoryBlock& binaryData); | |||
var& operator= (const var& valueToCopy); | |||
var& operator= (int value); | |||
var& operator= (int64 value); | |||
var& operator= (bool value); | |||
var& operator= (double value); | |||
var& operator= (const char* value); | |||
var& operator= (const wchar_t* value); | |||
var& operator= (const String& value); | |||
var& operator= (const MemoryBlock& value); | |||
var& operator= (const Array<var>& value); | |||
var& operator= (ReferenceCountedObject* object); | |||
var& operator= (NativeFunction method); | |||
var (var&&) noexcept; | |||
var (String&&); | |||
var (MemoryBlock&&); | |||
var (Array<var>&&); | |||
var& operator= (var&&) noexcept; | |||
var& operator= (String&&); | |||
void swapWith (var& other) noexcept; | |||
/** Returns a var object that can be used where you need the javascript "undefined" value. */ | |||
static var undefined() noexcept; | |||
//============================================================================== | |||
operator int() const noexcept; | |||
operator int64() const noexcept; | |||
operator bool() const noexcept; | |||
operator float() const noexcept; | |||
operator double() const noexcept; | |||
operator String() const; | |||
String toString() const; | |||
/** If this variant holds an array, this provides access to it. | |||
NOTE: Beware when you use this - the array pointer is only valid for the lifetime | |||
of the variant that returned it, so be very careful not to call this method on temporary | |||
var objects that are the return-value of a function, and which may go out of scope before | |||
you use the array! | |||
*/ | |||
Array<var>* getArray() const noexcept; | |||
/** If this variant holds a memory block, this provides access to it. | |||
NOTE: Beware when you use this - the MemoryBlock pointer is only valid for the lifetime | |||
of the variant that returned it, so be very careful not to call this method on temporary | |||
var objects that are the return-value of a function, and which may go out of scope before | |||
you use the MemoryBlock! | |||
*/ | |||
MemoryBlock* getBinaryData() const noexcept; | |||
ReferenceCountedObject* getObject() const noexcept; | |||
DynamicObject* getDynamicObject() const noexcept; | |||
//============================================================================== | |||
bool isVoid() const noexcept; | |||
bool isUndefined() const noexcept; | |||
bool isInt() const noexcept; | |||
bool isInt64() const noexcept; | |||
bool isBool() const noexcept; | |||
bool isDouble() const noexcept; | |||
bool isString() const noexcept; | |||
bool isObject() const noexcept; | |||
bool isArray() const noexcept; | |||
bool isBinaryData() const noexcept; | |||
bool isMethod() const noexcept; | |||
/** Returns true if this var has the same value as the one supplied. | |||
Note that this ignores the type, so a string var "123" and an integer var with the | |||
value 123 are considered to be equal. | |||
@see equalsWithSameType | |||
*/ | |||
bool equals (const var& other) const noexcept; | |||
/** Returns true if this var has the same value and type as the one supplied. | |||
This differs from equals() because e.g. "123" and 123 will be considered different. | |||
@see equals | |||
*/ | |||
bool equalsWithSameType (const var& other) const noexcept; | |||
/** Returns true if this var has the same type as the one supplied. */ | |||
bool hasSameTypeAs (const var& other) const noexcept; | |||
/** Returns a deep copy of this object. | |||
For simple types this just returns a copy, but if the object contains any arrays | |||
or DynamicObjects, they will be cloned (recursively). | |||
*/ | |||
var clone() const noexcept; | |||
//============================================================================== | |||
/** If the var is an array, this returns the number of elements. | |||
If the var isn't actually an array, this will return 0. | |||
*/ | |||
int size() const; | |||
/** If the var is an array, this can be used to return one of its elements. | |||
To call this method, you must make sure that the var is actually an array, and | |||
that the index is a valid number. If these conditions aren't met, behaviour is | |||
undefined. | |||
For more control over the array's contents, you can call getArray() and manipulate | |||
it directly as an Array\<var\>. | |||
*/ | |||
const var& operator[] (int arrayIndex) const; | |||
/** If the var is an array, this can be used to return one of its elements. | |||
To call this method, you must make sure that the var is actually an array, and | |||
that the index is a valid number. If these conditions aren't met, behaviour is | |||
undefined. | |||
For more control over the array's contents, you can call getArray() and manipulate | |||
it directly as an Array\<var\>. | |||
*/ | |||
var& operator[] (int arrayIndex); | |||
/** Appends an element to the var, converting it to an array if it isn't already one. | |||
If the var isn't an array, it will be converted to one, and if its value was non-void, | |||
this value will be kept as the first element of the new array. The parameter value | |||
will then be appended to it. | |||
For more control over the array's contents, you can call getArray() and manipulate | |||
it directly as an Array\<var\>. | |||
*/ | |||
void append (const var& valueToAppend); | |||
/** Inserts an element to the var, converting it to an array if it isn't already one. | |||
If the var isn't an array, it will be converted to one, and if its value was non-void, | |||
this value will be kept as the first element of the new array. The parameter value | |||
will then be inserted into it. | |||
For more control over the array's contents, you can call getArray() and manipulate | |||
it directly as an Array\<var\>. | |||
*/ | |||
void insert (int index, const var& value); | |||
/** If the var is an array, this removes one of its elements. | |||
If the index is out-of-range or the var isn't an array, nothing will be done. | |||
For more control over the array's contents, you can call getArray() and manipulate | |||
it directly as an Array\<var\>. | |||
*/ | |||
void remove (int index); | |||
/** Treating the var as an array, this resizes it to contain the specified number of elements. | |||
If the var isn't an array, it will be converted to one, and if its value was non-void, | |||
this value will be kept as the first element of the new array before resizing. | |||
For more control over the array's contents, you can call getArray() and manipulate | |||
it directly as an Array\<var\>. | |||
*/ | |||
void resize (int numArrayElementsWanted); | |||
/** If the var is an array, this searches it for the first occurrence of the specified value, | |||
and returns its index. | |||
If the var isn't an array, or if the value isn't found, this returns -1. | |||
*/ | |||
int indexOf (const var& value) const; | |||
//============================================================================== | |||
/** If this variant is an object, this returns one of its properties. */ | |||
const var& operator[] (const Identifier& propertyName) const; | |||
/** If this variant is an object, this returns one of its properties. */ | |||
const var& operator[] (const char* propertyName) const; | |||
/** If this variant is an object, this returns one of its properties, or a default | |||
fallback value if the property is not set. */ | |||
var getProperty (const Identifier& propertyName, const var& defaultReturnValue) const; | |||
/** Returns true if this variant is an object and if it has the given property. */ | |||
bool hasProperty (const Identifier& propertyName) const noexcept; | |||
/** Invokes a named method call with no arguments. */ | |||
var call (const Identifier& method) const; | |||
/** Invokes a named method call with one argument. */ | |||
var call (const Identifier& method, const var& arg1) const; | |||
/** Invokes a named method call with 2 arguments. */ | |||
var call (const Identifier& method, const var& arg1, const var& arg2) const; | |||
/** Invokes a named method call with 3 arguments. */ | |||
var call (const Identifier& method, const var& arg1, const var& arg2, const var& arg3); | |||
/** Invokes a named method call with 4 arguments. */ | |||
var call (const Identifier& method, const var& arg1, const var& arg2, const var& arg3, const var& arg4) const; | |||
/** Invokes a named method call with 5 arguments. */ | |||
var call (const Identifier& method, const var& arg1, const var& arg2, const var& arg3, const var& arg4, const var& arg5) const; | |||
/** Invokes a named method call with a list of arguments. */ | |||
var invoke (const Identifier& method, const var* arguments, int numArguments) const; | |||
/** If this object is a method, this returns the function pointer. */ | |||
NativeFunction getNativeFunction() const; | |||
//============================================================================== | |||
/** Writes a binary representation of this value to a stream. | |||
The data can be read back later using readFromStream(). | |||
@see JSON | |||
*/ | |||
void writeToStream (OutputStream& output) const; | |||
/** Reads back a stored binary representation of a value. | |||
The data in the stream must have been written using writeToStream(), or this | |||
will have unpredictable results. | |||
@see JSON | |||
*/ | |||
static var readFromStream (InputStream& input); | |||
private: | |||
//============================================================================== | |||
class VariantType; friend class VariantType; | |||
class VariantType_Void; friend class VariantType_Void; | |||
class VariantType_Undefined; friend class VariantType_Undefined; | |||
class VariantType_Int; friend class VariantType_Int; | |||
class VariantType_Int64; friend class VariantType_Int64; | |||
class VariantType_Double; friend class VariantType_Double; | |||
class VariantType_Bool; friend class VariantType_Bool; | |||
class VariantType_String; friend class VariantType_String; | |||
class VariantType_Object; friend class VariantType_Object; | |||
class VariantType_Array; friend class VariantType_Array; | |||
class VariantType_Binary; friend class VariantType_Binary; | |||
class VariantType_Method; friend class VariantType_Method; | |||
union ValueUnion | |||
{ | |||
int intValue; | |||
int64 int64Value; | |||
bool boolValue; | |||
double doubleValue; | |||
char stringValue [sizeof (String)]; | |||
ReferenceCountedObject* objectValue; | |||
MemoryBlock* binaryValue; | |||
NativeFunction* methodValue; | |||
}; | |||
const VariantType* type; | |||
ValueUnion value; | |||
Array<var>* convertToArray(); | |||
var (const VariantType&) noexcept; | |||
}; | |||
/** Compares the values of two var objects, using the var::equals() comparison. */ | |||
JUCE_API bool operator== (const var&, const var&) noexcept; | |||
/** 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 String&); | |||
JUCE_API bool operator!= (const var&, const String&); | |||
JUCE_API bool operator== (const var&, const char*); | |||
JUCE_API bool operator!= (const var&, const char*); | |||
//============================================================================== | |||
/** This template-overloaded class can be used to convert between var and custom types. */ | |||
template <typename Type> | |||
struct VariantConverter | |||
{ | |||
static Type fromVar (const var& v) { return static_cast<Type> (v); } | |||
static var toVar (const Type& t) { return t; } | |||
}; | |||
/** This template-overloaded class can be used to convert between var and custom types. */ | |||
template <> | |||
struct VariantConverter<String> | |||
{ | |||
static String fromVar (const var& v) { return v.toString(); } | |||
static var toVar (const String& s) { return s; } | |||
}; | |||
} // namespace juce |
@@ -1,165 +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. | |||
============================================================================== | |||
*/ | |||
namespace juce | |||
{ | |||
DirectoryIterator::DirectoryIterator (const File& directory, bool recursive, | |||
const String& pattern, const int type) | |||
: wildCards (parseWildcards (pattern)), | |||
fileFinder (directory, (recursive || wildCards.size() > 1) ? "*" : pattern), | |||
wildCard (pattern), | |||
path (File::addTrailingSeparator (directory.getFullPathName())), | |||
index (-1), | |||
totalNumFiles (-1), | |||
whatToLookFor (type), | |||
isRecursive (recursive), | |||
hasBeenAdvanced (false) | |||
{ | |||
// you have to specify the type of files you're looking for! | |||
jassert ((type & (File::findFiles | File::findDirectories)) != 0); | |||
jassert (type > 0 && type <= 7); | |||
} | |||
DirectoryIterator::~DirectoryIterator() | |||
{ | |||
} | |||
StringArray DirectoryIterator::parseWildcards (const String& pattern) | |||
{ | |||
StringArray s; | |||
s.addTokens (pattern, ";,", "\"'"); | |||
s.trim(); | |||
s.removeEmptyStrings(); | |||
return s; | |||
} | |||
bool DirectoryIterator::fileMatches (const StringArray& wildCards, const String& filename) | |||
{ | |||
for (int i = 0; i < wildCards.size(); ++i) | |||
if (filename.matchesWildcard (wildCards[i], ! File::areFileNamesCaseSensitive())) | |||
return true; | |||
return false; | |||
} | |||
bool DirectoryIterator::next() | |||
{ | |||
return next (nullptr, nullptr, nullptr, nullptr, nullptr, nullptr); | |||
} | |||
bool DirectoryIterator::next (bool* const isDirResult, bool* const isHiddenResult, int64* const fileSize, | |||
Time* const modTime, Time* const creationTime, bool* const isReadOnly) | |||
{ | |||
for (;;) | |||
{ | |||
hasBeenAdvanced = true; | |||
if (subIterator != nullptr) | |||
{ | |||
if (subIterator->next (isDirResult, isHiddenResult, fileSize, modTime, creationTime, isReadOnly)) | |||
return true; | |||
subIterator = nullptr; | |||
} | |||
String filename; | |||
bool isDirectory, isHidden = false, shouldContinue = false; | |||
while (fileFinder.next (filename, &isDirectory, | |||
(isHiddenResult != nullptr || (whatToLookFor & File::ignoreHiddenFiles) != 0) ? &isHidden : nullptr, | |||
fileSize, modTime, creationTime, isReadOnly)) | |||
{ | |||
++index; | |||
if (! filename.containsOnly (".")) | |||
{ | |||
bool matches = false; | |||
if (isDirectory) | |||
{ | |||
if (isRecursive && ((whatToLookFor & File::ignoreHiddenFiles) == 0 || ! isHidden)) | |||
subIterator = new DirectoryIterator (File::createFileWithoutCheckingPath (path + filename), | |||
true, wildCard, whatToLookFor); | |||
matches = (whatToLookFor & File::findDirectories) != 0; | |||
} | |||
else | |||
{ | |||
matches = (whatToLookFor & File::findFiles) != 0; | |||
} | |||
// if we're not relying on the OS iterator to do the wildcard match, do it now.. | |||
if (matches && (isRecursive || wildCards.size() > 1)) | |||
matches = fileMatches (wildCards, filename); | |||
if (matches && (whatToLookFor & File::ignoreHiddenFiles) != 0) | |||
matches = ! isHidden; | |||
if (matches) | |||
{ | |||
currentFile = File::createFileWithoutCheckingPath (path + filename); | |||
if (isHiddenResult != nullptr) *isHiddenResult = isHidden; | |||
if (isDirResult != nullptr) *isDirResult = isDirectory; | |||
return true; | |||
} | |||
if (subIterator != nullptr) | |||
{ | |||
shouldContinue = true; | |||
break; | |||
} | |||
} | |||
} | |||
if (! shouldContinue) | |||
return false; | |||
} | |||
} | |||
const File& DirectoryIterator::getFile() const | |||
{ | |||
if (subIterator != nullptr && subIterator->hasBeenAdvanced) | |||
return subIterator->getFile(); | |||
// You need to call DirectoryIterator::next() before asking it for the file that it found! | |||
jassert (hasBeenAdvanced); | |||
return currentFile; | |||
} | |||
float DirectoryIterator::getEstimatedProgress() const | |||
{ | |||
if (totalNumFiles < 0) | |||
totalNumFiles = File (path).getNumberOfChildFiles (File::findFilesAndDirectories); | |||
if (totalNumFiles <= 0) | |||
return 0.0f; | |||
const float detailedIndex = (subIterator != nullptr) ? index + subIterator->getEstimatedProgress() | |||
: (float) index; | |||
return jlimit (0.0f, 1.0f, detailedIndex / totalNumFiles); | |||
} | |||
} // namespace juce |
@@ -1,152 +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. | |||
============================================================================== | |||
*/ | |||
namespace juce | |||
{ | |||
//============================================================================== | |||
/** | |||
Searches through the files in a directory, returning each file that is found. | |||
A DirectoryIterator will search through a directory and its subdirectories using | |||
a wildcard filepattern match. | |||
If you may be scanning a large number of files, it's usually smarter to use this | |||
class than File::findChildFiles() because it allows you to stop at any time, rather | |||
than having to wait for the entire scan to finish before getting the results. | |||
It also provides an estimate of its progress, using a (highly inaccurate!) algorithm. | |||
*/ | |||
class JUCE_API DirectoryIterator | |||
{ | |||
public: | |||
//============================================================================== | |||
/** Creates a DirectoryIterator for a given directory. | |||
After creating one of these, call its next() method to get the | |||
first file - e.g. @code | |||
DirectoryIterator iter (File ("/animals/mooses"), true, "*.moose"); | |||
while (iter.next()) | |||
{ | |||
File theFileItFound (iter.getFile()); | |||
... etc | |||
} | |||
@endcode | |||
@param directory the directory to search in | |||
@param isRecursive whether all the subdirectories should also be searched | |||
@param wildCard the file pattern to match. This may contain multiple patterns | |||
separated by a semi-colon or comma, e.g. "*.jpg;*.png" | |||
@param whatToLookFor a value from the File::TypesOfFileToFind enum, specifying | |||
whether to look for files, directories, or both. | |||
*/ | |||
DirectoryIterator (const File& directory, | |||
bool isRecursive, | |||
const String& wildCard = "*", | |||
int whatToLookFor = File::findFiles); | |||
/** Destructor. */ | |||
~DirectoryIterator(); | |||
/** Moves the iterator along to the next file. | |||
@returns true if a file was found (you can then use getFile() to see what it was) - or | |||
false if there are no more matching files. | |||
*/ | |||
bool next(); | |||
/** Moves the iterator along to the next file, and returns various properties of that file. | |||
If you need to find out details about the file, it's more efficient to call this method than | |||
to call the normal next() method and then find out the details afterwards. | |||
All the parameters are optional, so pass null pointers for any items that you're not | |||
interested in. | |||
@returns true if a file was found (you can then use getFile() to see what it was) - or | |||
false if there are no more matching files. If it returns false, then none of the | |||
parameters will be filled-in. | |||
*/ | |||
bool next (bool* isDirectory, | |||
bool* isHidden, | |||
int64* fileSize, | |||
Time* modTime, | |||
Time* creationTime, | |||
bool* isReadOnly); | |||
/** Returns the file that the iterator is currently pointing at. | |||
The result of this call is only valid after a call to next() has returned true. | |||
*/ | |||
const File& getFile() const; | |||
/** Returns a guess of how far through the search the iterator has got. | |||
@returns a value 0.0 to 1.0 to show the progress, although this won't be | |||
very accurate. | |||
*/ | |||
float getEstimatedProgress() const; | |||
private: | |||
//============================================================================== | |||
class NativeIterator | |||
{ | |||
public: | |||
NativeIterator (const File& directory, const String& wildCard); | |||
~NativeIterator(); | |||
bool next (String& filenameFound, | |||
bool* isDirectory, bool* isHidden, int64* fileSize, | |||
Time* modTime, Time* creationTime, bool* isReadOnly); | |||
class Pimpl; | |||
private: | |||
friend class DirectoryIterator; | |||
friend struct ContainerDeletePolicy<Pimpl>; | |||
ScopedPointer<Pimpl> pimpl; | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (NativeIterator) | |||
}; | |||
friend struct ContainerDeletePolicy<NativeIterator::Pimpl>; | |||
StringArray wildCards; | |||
NativeIterator fileFinder; | |||
String wildCard, path; | |||
int index; | |||
mutable int totalNumFiles; | |||
const int whatToLookFor; | |||
const bool isRecursive; | |||
bool hasBeenAdvanced; | |||
ScopedPointer<DirectoryIterator> subIterator; | |||
File currentFile; | |||
static StringArray parseWildcards (const String& pattern); | |||
static bool fileMatches (const StringArray& wildCards, const String& filename); | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DirectoryIterator) | |||
}; | |||
} // namespace juce |
@@ -1,40 +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. | |||
============================================================================== | |||
*/ | |||
namespace juce | |||
{ | |||
FileFilter::FileFilter (const String& filterDescription) | |||
: description (filterDescription) | |||
{ | |||
} | |||
FileFilter::~FileFilter() | |||
{ | |||
} | |||
const String& FileFilter::getDescription() const noexcept | |||
{ | |||
return description; | |||
} | |||
} // namespace juce |
@@ -1,69 +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. | |||
============================================================================== | |||
*/ | |||
namespace juce | |||
{ | |||
//============================================================================== | |||
/** | |||
Interface for deciding which files are suitable for something. | |||
For example, this is used by DirectoryContentsList to select which files | |||
go into the list. | |||
@see WildcardFileFilter, DirectoryContentsList, FileListComponent, FileBrowserComponent | |||
*/ | |||
class JUCE_API FileFilter | |||
{ | |||
public: | |||
//============================================================================== | |||
/** Creates a filter with the given description. | |||
The description can be returned later with the getDescription() method. | |||
*/ | |||
FileFilter (const String& filterDescription); | |||
/** Destructor. */ | |||
virtual ~FileFilter(); | |||
//============================================================================== | |||
/** Returns the description that the filter was created with. */ | |||
const String& getDescription() const noexcept; | |||
//============================================================================== | |||
/** Should return true if this file is suitable for inclusion in whatever context | |||
the object is being used. | |||
*/ | |||
virtual bool isFileSuitable (const File& file) const = 0; | |||
/** Should return true if this directory is suitable for inclusion in whatever context | |||
the object is being used. | |||
*/ | |||
virtual bool isDirectorySuitable (const File& file) const = 0; | |||
protected: | |||
//============================================================================== | |||
String description; | |||
}; | |||
} // namespace juce |
@@ -1,83 +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. | |||
============================================================================== | |||
*/ | |||
namespace juce | |||
{ | |||
int64 juce_fileSetPosition (void* handle, int64 pos); | |||
//============================================================================== | |||
FileInputStream::FileInputStream (const File& f) | |||
: file (f), | |||
fileHandle (nullptr), | |||
currentPosition (0), | |||
status (Result::ok()) | |||
{ | |||
openHandle(); | |||
} | |||
int64 FileInputStream::getTotalLength() | |||
{ | |||
// You should always check that a stream opened successfully before using it! | |||
jassert (openedOk()); | |||
return file.getSize(); | |||
} | |||
int FileInputStream::read (void* buffer, int bytesToRead) | |||
{ | |||
// You should always check that a stream opened successfully before using it! | |||
jassert (openedOk()); | |||
// The buffer should never be null, and a negative size is probably a | |||
// sign that something is broken! | |||
jassert (buffer != nullptr && bytesToRead >= 0); | |||
const size_t num = readInternal (buffer, (size_t) bytesToRead); | |||
currentPosition += (int64) num; | |||
return (int) num; | |||
} | |||
bool FileInputStream::isExhausted() | |||
{ | |||
return currentPosition >= getTotalLength(); | |||
} | |||
int64 FileInputStream::getPosition() | |||
{ | |||
return currentPosition; | |||
} | |||
bool FileInputStream::setPosition (int64 pos) | |||
{ | |||
// You should always check that a stream opened successfully before using it! | |||
jassert (openedOk()); | |||
if (pos != currentPosition) | |||
currentPosition = juce_fileSetPosition (fileHandle, pos); | |||
return currentPosition == pos; | |||
} | |||
} // namespace juce |
@@ -1,88 +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. | |||
============================================================================== | |||
*/ | |||
namespace juce | |||
{ | |||
//============================================================================== | |||
/** | |||
An input stream that reads from a local file. | |||
@see InputStream, FileOutputStream, File::createInputStream | |||
*/ | |||
class JUCE_API FileInputStream : public InputStream | |||
{ | |||
public: | |||
//============================================================================== | |||
/** Creates a FileInputStream to read from the given file. | |||
After creating a FileInputStream, you should use openedOk() or failedToOpen() | |||
to make sure that it's OK before trying to read from it! If it failed, you | |||
can call getStatus() to get more error information. | |||
*/ | |||
explicit FileInputStream (const File& fileToRead); | |||
/** Destructor. */ | |||
~FileInputStream(); | |||
//============================================================================== | |||
/** Returns the file that this stream is reading from. */ | |||
const File& getFile() const noexcept { return file; } | |||
/** Returns the status of the file stream. | |||
The result will be ok if the file opened successfully. If an error occurs while | |||
opening or reading from the file, this will contain an error message. | |||
*/ | |||
const Result& getStatus() const noexcept { return status; } | |||
/** Returns true if the stream couldn't be opened for some reason. | |||
@see getResult() | |||
*/ | |||
bool failedToOpen() const noexcept { return status.failed(); } | |||
/** Returns true if the stream opened without problems. | |||
@see getResult() | |||
*/ | |||
bool openedOk() const noexcept { return status.wasOk(); } | |||
//============================================================================== | |||
int64 getTotalLength() override; | |||
int read (void*, int) override; | |||
bool isExhausted() override; | |||
int64 getPosition() override; | |||
bool setPosition (int64) override; | |||
private: | |||
//============================================================================== | |||
const File file; | |||
void* fileHandle; | |||
int64 currentPosition; | |||
Result status; | |||
void openHandle(); | |||
size_t readInternal (void*, size_t); | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FileInputStream) | |||
}; | |||
} // namespace juce |
@@ -1,133 +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. | |||
============================================================================== | |||
*/ | |||
namespace juce | |||
{ | |||
int64 juce_fileSetPosition (void* handle, int64 pos); | |||
//============================================================================== | |||
FileOutputStream::FileOutputStream (const File& f, const size_t bufferSizeToUse) | |||
: file (f), | |||
fileHandle (nullptr), | |||
status (Result::ok()), | |||
currentPosition (0), | |||
bufferSize (bufferSizeToUse), | |||
bytesInBuffer (0), | |||
buffer (jmax (bufferSizeToUse, (size_t) 16)) | |||
{ | |||
openHandle(); | |||
} | |||
FileOutputStream::~FileOutputStream() | |||
{ | |||
flushBuffer(); | |||
closeHandle(); | |||
} | |||
int64 FileOutputStream::getPosition() | |||
{ | |||
return currentPosition; | |||
} | |||
bool FileOutputStream::setPosition (int64 newPosition) | |||
{ | |||
if (newPosition != currentPosition) | |||
{ | |||
flushBuffer(); | |||
currentPosition = juce_fileSetPosition (fileHandle, newPosition); | |||
} | |||
return newPosition == currentPosition; | |||
} | |||
bool FileOutputStream::flushBuffer() | |||
{ | |||
bool ok = true; | |||
if (bytesInBuffer > 0) | |||
{ | |||
ok = (writeInternal (buffer, bytesInBuffer) == (ssize_t) bytesInBuffer); | |||
bytesInBuffer = 0; | |||
} | |||
return ok; | |||
} | |||
void FileOutputStream::flush() | |||
{ | |||
flushBuffer(); | |||
flushInternal(); | |||
} | |||
bool FileOutputStream::write (const void* const src, const size_t numBytes) | |||
{ | |||
jassert (src != nullptr && ((ssize_t) numBytes) >= 0); | |||
if (bytesInBuffer + numBytes < bufferSize) | |||
{ | |||
memcpy (buffer + bytesInBuffer, src, numBytes); | |||
bytesInBuffer += numBytes; | |||
currentPosition += (int64) numBytes; | |||
} | |||
else | |||
{ | |||
if (! flushBuffer()) | |||
return false; | |||
if (numBytes < bufferSize) | |||
{ | |||
memcpy (buffer + bytesInBuffer, src, numBytes); | |||
bytesInBuffer += numBytes; | |||
currentPosition += (int64) numBytes; | |||
} | |||
else | |||
{ | |||
const ssize_t bytesWritten = writeInternal (src, numBytes); | |||
if (bytesWritten < 0) | |||
return false; | |||
currentPosition += (int64) bytesWritten; | |||
return bytesWritten == (ssize_t) numBytes; | |||
} | |||
} | |||
return true; | |||
} | |||
bool FileOutputStream::writeRepeatedByte (uint8 byte, size_t numBytes) | |||
{ | |||
jassert (((ssize_t) numBytes) >= 0); | |||
if (bytesInBuffer + numBytes < bufferSize) | |||
{ | |||
memset (buffer + bytesInBuffer, byte, numBytes); | |||
bytesInBuffer += numBytes; | |||
currentPosition += (int64) numBytes; | |||
return true; | |||
} | |||
return OutputStream::writeRepeatedByte (byte, numBytes); | |||
} | |||
} // namespace juce |
@@ -1,113 +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. | |||
============================================================================== | |||
*/ | |||
namespace juce | |||
{ | |||
//============================================================================== | |||
/** | |||
An output stream that writes into a local file. | |||
@see OutputStream, FileInputStream, File::createOutputStream | |||
*/ | |||
class JUCE_API FileOutputStream : public OutputStream | |||
{ | |||
public: | |||
//============================================================================== | |||
/** Creates a FileOutputStream. | |||
If the file doesn't exist, it will first be created. If the file can't be | |||
created or opened (for example, because the parent directory of the file | |||
does not exist), the failedToOpen() method will return true. | |||
If the file already exists when opened, the stream's write-position will | |||
be set to the end of the file. To overwrite an existing file, | |||
use File::deleteFile() before opening the stream, or use setPosition(0) | |||
after it's opened (although this won't truncate the file). | |||
Destroying a FileOutputStream object does not force the operating system | |||
to write the buffered data to disk immediately. If this is required you | |||
should call flush() before triggering the destructor. | |||
@see TemporaryFile | |||
*/ | |||
FileOutputStream (const File& fileToWriteTo, | |||
size_t bufferSizeToUse = 16384); | |||
/** Destructor. */ | |||
~FileOutputStream(); | |||
//============================================================================== | |||
/** Returns the file that this stream is writing to. | |||
*/ | |||
const File& getFile() const { return file; } | |||
/** Returns the status of the file stream. | |||
The result will be ok if the file opened successfully. If an error occurs while | |||
opening or writing to the file, this will contain an error message. | |||
*/ | |||
const Result& getStatus() const noexcept { return status; } | |||
/** Returns true if the stream couldn't be opened for some reason. | |||
@see getResult() | |||
*/ | |||
bool failedToOpen() const noexcept { return status.failed(); } | |||
/** Returns true if the stream opened without problems. | |||
@see getResult() | |||
*/ | |||
bool openedOk() const noexcept { return status.wasOk(); } | |||
/** Attempts to truncate the file to the current write position. | |||
To truncate a file to a specific size, first use setPosition() to seek to the | |||
appropriate location, and then call this method. | |||
*/ | |||
Result truncate(); | |||
//============================================================================== | |||
void flush() override; | |||
int64 getPosition() override; | |||
bool setPosition (int64) override; | |||
bool write (const void*, size_t) override; | |||
bool writeRepeatedByte (uint8 byte, size_t numTimesToRepeat) override; | |||
private: | |||
//============================================================================== | |||
File file; | |||
void* fileHandle; | |||
Result status; | |||
int64 currentPosition; | |||
size_t bufferSize, bytesInBuffer; | |||
HeapBlock<char> buffer; | |||
void openHandle(); | |||
void closeHandle(); | |||
void flushInternal(); | |||
bool flushBuffer(); | |||
int64 setPositionInternal (int64); | |||
ssize_t writeInternal (const void*, size_t); | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FileOutputStream) | |||
}; | |||
} // namespace juce |
@@ -1,172 +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. | |||
============================================================================== | |||
*/ | |||
namespace juce | |||
{ | |||
FileSearchPath::FileSearchPath() {} | |||
FileSearchPath::~FileSearchPath() {} | |||
FileSearchPath::FileSearchPath (const String& path) | |||
{ | |||
init (path); | |||
} | |||
FileSearchPath::FileSearchPath (const FileSearchPath& other) | |||
: directories (other.directories) | |||
{ | |||
} | |||
FileSearchPath& FileSearchPath::operator= (const FileSearchPath& other) | |||
{ | |||
directories = other.directories; | |||
return *this; | |||
} | |||
FileSearchPath& FileSearchPath::operator= (const String& path) | |||
{ | |||
init (path); | |||
return *this; | |||
} | |||
void FileSearchPath::init (const String& path) | |||
{ | |||
directories.clear(); | |||
directories.addTokens (path, ";", "\""); | |||
directories.trim(); | |||
directories.removeEmptyStrings(); | |||
for (int i = directories.size(); --i >= 0;) | |||
directories.set (i, directories[i].unquoted()); | |||
} | |||
int FileSearchPath::getNumPaths() const | |||
{ | |||
return directories.size(); | |||
} | |||
File FileSearchPath::operator[] (const int index) const | |||
{ | |||
return File (directories [index]); | |||
} | |||
String FileSearchPath::toString() const | |||
{ | |||
StringArray directories2 (directories); | |||
for (int i = directories2.size(); --i >= 0;) | |||
if (directories2[i].containsChar (';')) | |||
directories2.set (i, directories2[i].quoted()); | |||
return directories2.joinIntoString (";"); | |||
} | |||
void FileSearchPath::add (const File& dir, const int insertIndex) | |||
{ | |||
directories.insert (insertIndex, dir.getFullPathName()); | |||
} | |||
bool FileSearchPath::addIfNotAlreadyThere (const File& dir) | |||
{ | |||
for (int i = 0; i < directories.size(); ++i) | |||
if (File (directories[i]) == dir) | |||
return false; | |||
add (dir); | |||
return true; | |||
} | |||
void FileSearchPath::remove (const int index) | |||
{ | |||
directories.remove (index); | |||
} | |||
void FileSearchPath::addPath (const FileSearchPath& other) | |||
{ | |||
for (int i = 0; i < other.getNumPaths(); ++i) | |||
addIfNotAlreadyThere (other[i]); | |||
} | |||
void FileSearchPath::removeRedundantPaths() | |||
{ | |||
for (int i = directories.size(); --i >= 0;) | |||
{ | |||
const File d1 (directories[i]); | |||
for (int j = directories.size(); --j >= 0;) | |||
{ | |||
const File d2 (directories[j]); | |||
if ((i != j) && (d1.isAChildOf (d2) || d1 == d2)) | |||
{ | |||
directories.remove (i); | |||
break; | |||
} | |||
} | |||
} | |||
} | |||
void FileSearchPath::removeNonExistentPaths() | |||
{ | |||
for (int i = directories.size(); --i >= 0;) | |||
if (! File (directories[i]).isDirectory()) | |||
directories.remove (i); | |||
} | |||
int FileSearchPath::findChildFiles (Array<File>& results, | |||
const int whatToLookFor, | |||
const bool searchRecursively, | |||
const String& wildCardPattern) const | |||
{ | |||
int total = 0; | |||
for (int i = 0; i < directories.size(); ++i) | |||
total += operator[] (i).findChildFiles (results, | |||
whatToLookFor, | |||
searchRecursively, | |||
wildCardPattern); | |||
return total; | |||
} | |||
bool FileSearchPath::isFileInPath (const File& fileToCheck, | |||
const bool checkRecursively) const | |||
{ | |||
for (int i = directories.size(); --i >= 0;) | |||
{ | |||
const File d (directories[i]); | |||
if (checkRecursively) | |||
{ | |||
if (fileToCheck.isAChildOf (d)) | |||
return true; | |||
} | |||
else | |||
{ | |||
if (fileToCheck.getParentDirectory() == d) | |||
return true; | |||
} | |||
} | |||
return false; | |||
} | |||
} // namespace juce |
@@ -1,161 +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. | |||
============================================================================== | |||
*/ | |||
namespace juce | |||
{ | |||
//============================================================================== | |||
/** | |||
Represents a set of folders that make up a search path. | |||
@see File | |||
*/ | |||
class JUCE_API FileSearchPath | |||
{ | |||
public: | |||
//============================================================================== | |||
/** Creates an empty search path. */ | |||
FileSearchPath(); | |||
/** Creates a search path from a string of pathnames. | |||
The path can be semicolon- or comma-separated, e.g. | |||
"/foo/bar;/foo/moose;/fish/moose" | |||
The separate folders are tokenised and added to the search path. | |||
*/ | |||
FileSearchPath (const String& path); | |||
/** Creates a copy of another search path. */ | |||
FileSearchPath (const FileSearchPath&); | |||
/** Copies another search path. */ | |||
FileSearchPath& operator= (const FileSearchPath&); | |||
/** Destructor. */ | |||
~FileSearchPath(); | |||
/** Uses a string containing a list of pathnames to re-initialise this list. | |||
This search path is cleared and the semicolon- or comma-separated folders | |||
in this string are added instead. e.g. "/foo/bar;/foo/moose;/fish/moose" | |||
*/ | |||
FileSearchPath& operator= (const String& path); | |||
//============================================================================== | |||
/** Returns the number of folders in this search path. | |||
@see operator[] | |||
*/ | |||
int getNumPaths() const; | |||
/** Returns one of the folders in this search path. | |||
The file returned isn't guaranteed to actually be a valid directory. | |||
@see getNumPaths | |||
*/ | |||
File operator[] (int index) const; | |||
/** Returns the search path as a semicolon-separated list of directories. */ | |||
String toString() const; | |||
//============================================================================== | |||
/** Adds a new directory to the search path. | |||
The new directory is added to the end of the list if the insertIndex parameter is | |||
less than zero, otherwise it is inserted at the given index. | |||
*/ | |||
void add (const File& directoryToAdd, | |||
int insertIndex = -1); | |||
/** Adds a new directory to the search path if it's not already in there. | |||
@return true if the directory has been added, false otherwise. | |||
*/ | |||
bool addIfNotAlreadyThere (const File& directoryToAdd); | |||
/** Removes a directory from the search path. */ | |||
void remove (int indexToRemove); | |||
/** Merges another search path into this one. | |||
This will remove any duplicate directories. | |||
*/ | |||
void addPath (const FileSearchPath&); | |||
/** Removes any directories that are actually subdirectories of one of the other directories in the search path. | |||
If the search is intended to be recursive, there's no point having nested folders in the search | |||
path, because they'll just get searched twice and you'll get duplicate results. | |||
e.g. if the path is "c:\abc\de;c:\abc", this method will simplify it to "c:\abc" | |||
*/ | |||
void removeRedundantPaths(); | |||
/** Removes any directories that don't actually exist. */ | |||
void removeNonExistentPaths(); | |||
//============================================================================== | |||
/** Searches the path for a wildcard. | |||
This will search all the directories in the search path in order, adding any | |||
matching files to the results array. | |||
@param results an array to append the results to | |||
@param whatToLookFor a value from the File::TypesOfFileToFind enum, specifying whether to | |||
return files, directories, or both. | |||
@param searchRecursively whether to recursively search the subdirectories too | |||
@param wildCardPattern a pattern to match against the filenames | |||
@returns the number of files added to the array | |||
@see File::findChildFiles | |||
*/ | |||
int findChildFiles (Array<File>& results, | |||
int whatToLookFor, | |||
bool searchRecursively, | |||
const String& wildCardPattern = "*") const; | |||
//============================================================================== | |||
/** Finds out whether a file is inside one of the path's directories. | |||
This will return true if the specified file is a child of one of the | |||
directories specified by this path. Note that this doesn't actually do any | |||
searching or check that the files exist - it just looks at the pathnames | |||
to work out whether the file would be inside a directory. | |||
@param fileToCheck the file to look for | |||
@param checkRecursively if true, then this will return true if the file is inside a | |||
subfolder of one of the path's directories (at any depth). If false | |||
it will only return true if the file is actually a direct child | |||
of one of the directories. | |||
@see File::isAChildOf | |||
*/ | |||
bool isFileInPath (const File& fileToCheck, | |||
bool checkRecursively) const; | |||
private: | |||
//============================================================================== | |||
StringArray directories; | |||
void init (const String&); | |||
JUCE_LEAK_DETECTOR (FileSearchPath) | |||
}; | |||
} // namespace juce |
@@ -1,113 +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. | |||
============================================================================== | |||
*/ | |||
namespace juce | |||
{ | |||
//============================================================================== | |||
/** | |||
Maps a file into virtual memory for easy reading and/or writing. | |||
*/ | |||
class JUCE_API MemoryMappedFile | |||
{ | |||
public: | |||
/** The read/write flags used when opening a memory mapped file. */ | |||
enum AccessMode | |||
{ | |||
readOnly, /**< Indicates that the memory can only be read. */ | |||
readWrite /**< Indicates that the memory can be read and written to - changes that are | |||
made will be flushed back to disk at the whim of the OS. */ | |||
}; | |||
/** Opens a file and maps it to an area of virtual memory. | |||
The file should already exist, and should already be the size that you want to work with | |||
when you call this. If the file is resized after being opened, the behaviour is undefined. | |||
If the file exists and the operation succeeds, the getData() and getSize() methods will | |||
return the location and size of the data that can be read or written. Note that the entire | |||
file is not read into memory immediately - the OS simply creates a virtual mapping, which | |||
will lazily pull the data into memory when blocks are accessed. | |||
If the file can't be opened for some reason, the getData() method will return a null pointer. | |||
If exclusive is false then other apps can also open the same memory mapped file and use this | |||
mapping as an effective way of communicating. If exclusive is true then the mapped file will | |||
be opened exclusively - preventing other apps to access the file which may improve the | |||
performance of accessing the file. | |||
*/ | |||
MemoryMappedFile (const File& file, AccessMode mode, bool exclusive = false); | |||
/** Opens a section of a file and maps it to an area of virtual memory. | |||
The file should already exist, and should already be the size that you want to work with | |||
when you call this. If the file is resized after being opened, the behaviour is undefined. | |||
If the file exists and the operation succeeds, the getData() and getSize() methods will | |||
return the location and size of the data that can be read or written. Note that the entire | |||
file is not read into memory immediately - the OS simply creates a virtual mapping, which | |||
will lazily pull the data into memory when blocks are accessed. | |||
If the file can't be opened for some reason, the getData() method will return a null pointer. | |||
NOTE: the start of the actual range used may be rounded-down to a multiple of the OS's page-size, | |||
so do not assume that the mapped memory will begin at exactly the position you requested - always | |||
use getRange() to check the actual range that is being used. | |||
*/ | |||
MemoryMappedFile (const File& file, | |||
const Range<int64>& fileRange, | |||
AccessMode mode, | |||
bool exclusive = false); | |||
/** Destructor. */ | |||
~MemoryMappedFile(); | |||
/** Returns the address at which this file has been mapped, or a null pointer if | |||
the file couldn't be successfully mapped. | |||
*/ | |||
void* getData() const noexcept { return address; } | |||
/** Returns the number of bytes of data that are available for reading or writing. | |||
This will normally be the size of the file. | |||
*/ | |||
size_t getSize() const noexcept { return (size_t) range.getLength(); } | |||
/** Returns the section of the file at which the mapped memory represents. */ | |||
Range<int64> getRange() const noexcept { return range; } | |||
private: | |||
//============================================================================== | |||
void* address; | |||
Range<int64> range; | |||
#if JUCE_WINDOWS | |||
void* fileHandle; | |||
#else | |||
int fileHandle; | |||
#endif | |||
void openInternal (const File&, AccessMode, bool); | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MemoryMappedFile) | |||
}; | |||
} // namespace juce |
@@ -1,116 +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. | |||
============================================================================== | |||
*/ | |||
namespace juce | |||
{ | |||
static File createTempFile (const File& parentDirectory, String name, | |||
const String& suffix, const int optionFlags) | |||
{ | |||
if ((optionFlags & TemporaryFile::useHiddenFile) != 0) | |||
name = "." + name; | |||
return parentDirectory.getNonexistentChildFile (name, suffix, (optionFlags & TemporaryFile::putNumbersInBrackets) != 0); | |||
} | |||
TemporaryFile::TemporaryFile (const String& suffix, const int optionFlags) | |||
: temporaryFile (createTempFile (File::getSpecialLocation (File::tempDirectory), | |||
"temp_" + String::toHexString (Random::getSystemRandom().nextInt()), | |||
suffix, optionFlags)) | |||
{ | |||
} | |||
TemporaryFile::TemporaryFile (const File& target, const int optionFlags) | |||
: temporaryFile (createTempFile (target.getParentDirectory(), | |||
target.getFileNameWithoutExtension() | |||
+ "_temp" + String::toHexString (Random::getSystemRandom().nextInt()), | |||
target.getFileExtension(), optionFlags)), | |||
targetFile (target) | |||
{ | |||
// If you use this constructor, you need to give it a valid target file! | |||
jassert (targetFile != File()); | |||
} | |||
TemporaryFile::TemporaryFile (const File& target, const File& temporary) | |||
: temporaryFile (temporary), targetFile (target) | |||
{ | |||
} | |||
TemporaryFile::~TemporaryFile() | |||
{ | |||
if (! deleteTemporaryFile()) | |||
{ | |||
/* Failed to delete our temporary file! The most likely reason for this would be | |||
that you've not closed an output stream that was being used to write to file. | |||
If you find that something beyond your control is changing permissions on | |||
your temporary files and preventing them from being deleted, you may want to | |||
call TemporaryFile::deleteTemporaryFile() to detect those error cases and | |||
handle them appropriately. | |||
*/ | |||
jassertfalse; | |||
} | |||
} | |||
//============================================================================== | |||
bool TemporaryFile::overwriteTargetFileWithTemporary() const | |||
{ | |||
// This method only works if you created this object with the constructor | |||
// that takes a target file! | |||
jassert (targetFile != File()); | |||
if (temporaryFile.exists()) | |||
{ | |||
// Have a few attempts at overwriting the file before giving up.. | |||
for (int i = 5; --i >= 0;) | |||
{ | |||
if (temporaryFile.replaceFileIn (targetFile)) | |||
return true; | |||
Thread::sleep (100); | |||
} | |||
} | |||
else | |||
{ | |||
// There's no temporary file to use. If your write failed, you should | |||
// probably check, and not bother calling this method. | |||
jassertfalse; | |||
} | |||
return false; | |||
} | |||
bool TemporaryFile::deleteTemporaryFile() const | |||
{ | |||
// Have a few attempts at deleting the file before giving up.. | |||
for (int i = 5; --i >= 0;) | |||
{ | |||
if (temporaryFile.deleteFile()) | |||
return true; | |||
Thread::sleep (50); | |||
} | |||
return false; | |||
} | |||
} // namespace juce |
@@ -1,162 +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. | |||
============================================================================== | |||
*/ | |||
namespace juce | |||
{ | |||
//============================================================================== | |||
/** | |||
Manages a temporary file, which will be deleted when this object is deleted. | |||
This object is intended to be used as a stack based object, using its scope | |||
to make sure the temporary file isn't left lying around. | |||
For example: | |||
@code | |||
{ | |||
File myTargetFile ("~/myfile.txt"); | |||
// this will choose a file called something like "~/myfile_temp239348.txt" | |||
// which definitely doesn't exist at the time the constructor is called. | |||
TemporaryFile temp (myTargetFile); | |||
// create a stream to the temporary file, and write some data to it... | |||
ScopedPointer<FileOutputStream> out (temp.getFile().createOutputStream()); | |||
if (out != nullptr) | |||
{ | |||
out->write ( ...etc ) | |||
out = nullptr; // (deletes the stream) | |||
// ..now we've finished writing, this will rename the temp file to | |||
// make it replace the target file we specified above. | |||
bool succeeded = temp.overwriteTargetFileWithTemporary(); | |||
} | |||
// ..and even if something went wrong and our overwrite failed, | |||
// as the TemporaryFile object goes out of scope here, it'll make sure | |||
// that the temp file gets deleted. | |||
} | |||
@endcode | |||
@see File, FileOutputStream | |||
*/ | |||
class JUCE_API TemporaryFile | |||
{ | |||
public: | |||
//============================================================================== | |||
enum OptionFlags | |||
{ | |||
useHiddenFile = 1, /**< Indicates that the temporary file should be hidden - | |||
i.e. its name should start with a dot. */ | |||
putNumbersInBrackets = 2 /**< Indicates that when numbers are appended to make sure | |||
the file is unique, they should go in brackets rather | |||
than just being appended (see File::getNonexistentSibling() )*/ | |||
}; | |||
//============================================================================== | |||
/** Creates a randomly-named temporary file in the default temp directory. | |||
@param suffix a file suffix to use for the file | |||
@param optionFlags a combination of the values listed in the OptionFlags enum | |||
The file will not be created until you write to it. And remember that when | |||
this object is deleted, the file will also be deleted! | |||
*/ | |||
TemporaryFile (const String& suffix = String(), | |||
int optionFlags = 0); | |||
/** Creates a temporary file in the same directory as a specified file. | |||
This is useful if you have a file that you want to overwrite, but don't | |||
want to harm the original file if the write operation fails. You can | |||
use this to create a temporary file next to the target file, then | |||
write to the temporary file, and finally use overwriteTargetFileWithTemporary() | |||
to replace the target file with the one you've just written. | |||
This class won't create any files until you actually write to them. And remember | |||
that when this object is deleted, the temporary file will also be deleted! | |||
@param targetFile the file that you intend to overwrite - the temporary | |||
file will be created in the same directory as this | |||
@param optionFlags a combination of the values listed in the OptionFlags enum | |||
*/ | |||
TemporaryFile (const File& targetFile, | |||
int optionFlags = 0); | |||
/** Creates a temporary file using an explicit filename. | |||
The other constructors are a better choice than this one, unless for some reason | |||
you need to explicitly specify the temporary file you want to use. | |||
@param targetFile the file that you intend to overwrite | |||
@param temporaryFile the temporary file to be used | |||
*/ | |||
TemporaryFile (const File& targetFile, | |||
const File& temporaryFile); | |||
/** Destructor. | |||
When this object is deleted it will make sure that its temporary file is | |||
also deleted! If the operation fails, it'll throw an assertion in debug | |||
mode. | |||
*/ | |||
~TemporaryFile(); | |||
//============================================================================== | |||
/** Returns the temporary file. */ | |||
const File& getFile() const noexcept { return temporaryFile; } | |||
/** Returns the target file that was specified in the constructor. */ | |||
const File& getTargetFile() const noexcept { return targetFile; } | |||
/** Tries to move the temporary file to overwrite the target file that was | |||
specified in the constructor. | |||
If you used the constructor that specified a target file, this will attempt | |||
to replace that file with the temporary one. | |||
Before calling this, make sure: | |||
- that you've actually written to the temporary file | |||
- that you've closed any open streams that you were using to write to it | |||
- and that you don't have any streams open to the target file, which would | |||
prevent it being overwritten | |||
If the file move succeeds, this returns false, and the temporary file will | |||
have disappeared. If it fails, the temporary file will probably still exist, | |||
but will be deleted when this object is destroyed. | |||
*/ | |||
bool overwriteTargetFileWithTemporary() const; | |||
/** Attempts to delete the temporary file, if it exists. | |||
@returns true if the file is successfully deleted (or if it didn't exist). | |||
*/ | |||
bool deleteTemporaryFile() const; | |||
private: | |||
//============================================================================== | |||
const File temporaryFile, targetFile; | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TemporaryFile) | |||
}; | |||
} // namespace juce |
@@ -1,75 +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. | |||
============================================================================== | |||
*/ | |||
namespace juce | |||
{ | |||
static void parseWildcard (const String& pattern, StringArray& result) | |||
{ | |||
result.addTokens (pattern.toLowerCase(), ";,", "\"'"); | |||
result.trim(); | |||
result.removeEmptyStrings(); | |||
// special case for *.*, because people use it to mean "any file", but it | |||
// would actually ignore files with no extension. | |||
for (int i = result.size(); --i >= 0;) | |||
if (result[i] == "*.*") | |||
result.set (i, "*"); | |||
} | |||
static bool matchWildcard (const File& file, const StringArray& wildcards) | |||
{ | |||
const String filename (file.getFileName()); | |||
for (int i = wildcards.size(); --i >= 0;) | |||
if (filename.matchesWildcard (wildcards[i], true)) | |||
return true; | |||
return false; | |||
} | |||
WildcardFileFilter::WildcardFileFilter (const String& fileWildcardPatterns, | |||
const String& directoryWildcardPatterns, | |||
const String& desc) | |||
: FileFilter (desc.isEmpty() ? fileWildcardPatterns | |||
: (desc + " (" + fileWildcardPatterns + ")")) | |||
{ | |||
parseWildcard (fileWildcardPatterns, fileWildcards); | |||
parseWildcard (directoryWildcardPatterns, directoryWildcards); | |||
} | |||
WildcardFileFilter::~WildcardFileFilter() | |||
{ | |||
} | |||
bool WildcardFileFilter::isFileSuitable (const File& file) const | |||
{ | |||
return matchWildcard (file, fileWildcards); | |||
} | |||
bool WildcardFileFilter::isDirectorySuitable (const File& file) const | |||
{ | |||
return matchWildcard (file, directoryWildcards); | |||
} | |||
} // namespace juce |
@@ -1,74 +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. | |||
============================================================================== | |||
*/ | |||
namespace juce | |||
{ | |||
//============================================================================== | |||
/** | |||
A type of FileFilter that works by wildcard pattern matching. | |||
This filter only allows files that match one of the specified patterns, but | |||
allows all directories through. | |||
@see FileFilter, DirectoryContentsList, FileListComponent, FileBrowserComponent | |||
*/ | |||
class JUCE_API WildcardFileFilter : public FileFilter | |||
{ | |||
public: | |||
//============================================================================== | |||
/** | |||
Creates a wildcard filter for one or more patterns. | |||
The wildcardPatterns parameter is a comma or semicolon-delimited set of | |||
patterns, e.g. "*.wav;*.aiff" would look for files ending in either .wav | |||
or .aiff. | |||
Passing an empty string as a pattern will fail to match anything, so by leaving | |||
either the file or directory pattern parameter empty means you can control | |||
whether files or directories are found. | |||
The description is a name to show the user in a list of possible patterns, so | |||
for the wav/aiff example, your description might be "audio files". | |||
*/ | |||
WildcardFileFilter (const String& fileWildcardPatterns, | |||
const String& directoryWildcardPatterns, | |||
const String& description); | |||
/** Destructor. */ | |||
~WildcardFileFilter(); | |||
//============================================================================== | |||
/** Returns true if the filename matches one of the patterns specified. */ | |||
bool isFileSuitable (const File& file) const override; | |||
/** This always returns true. */ | |||
bool isDirectorySuitable (const File& file) const override; | |||
private: | |||
//============================================================================== | |||
StringArray fileWildcards, directoryWildcards; | |||
JUCE_LEAK_DETECTOR (WildcardFileFilter) | |||
}; | |||
} // namespace juce |
@@ -1,653 +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. | |||
============================================================================== | |||
*/ | |||
namespace juce | |||
{ | |||
struct JSONParser | |||
{ | |||
static Result parseObjectOrArray (String::CharPointerType t, var& result) | |||
{ | |||
t = t.findEndOfWhitespace(); | |||
switch (t.getAndAdvance()) | |||
{ | |||
case 0: result = var(); return Result::ok(); | |||
case '{': return parseObject (t, result); | |||
case '[': return parseArray (t, result); | |||
} | |||
return createFail ("Expected '{' or '['", &t); | |||
} | |||
static Result parseString (const juce_wchar quoteChar, String::CharPointerType& t, var& result) | |||
{ | |||
MemoryOutputStream buffer (256); | |||
for (;;) | |||
{ | |||
juce_wchar c = t.getAndAdvance(); | |||
if (c == quoteChar) | |||
break; | |||
if (c == '\\') | |||
{ | |||
c = t.getAndAdvance(); | |||
switch (c) | |||
{ | |||
case '"': | |||
case '\'': | |||
case '\\': | |||
case '/': break; | |||
case 'a': c = '\a'; break; | |||
case 'b': c = '\b'; break; | |||
case 'f': c = '\f'; break; | |||
case 'n': c = '\n'; break; | |||
case 'r': c = '\r'; break; | |||
case 't': c = '\t'; break; | |||
case 'u': | |||
{ | |||
c = 0; | |||
for (int i = 4; --i >= 0;) | |||
{ | |||
const int digitValue = CharacterFunctions::getHexDigitValue (t.getAndAdvance()); | |||
if (digitValue < 0) | |||
return createFail ("Syntax error in unicode escape sequence"); | |||
c = (juce_wchar) ((c << 4) + static_cast<juce_wchar> (digitValue)); | |||
} | |||
break; | |||
} | |||
} | |||
} | |||
if (c == 0) | |||
return createFail ("Unexpected end-of-input in string constant"); | |||
buffer.appendUTF8Char (c); | |||
} | |||
result = buffer.toUTF8(); | |||
return Result::ok(); | |||
} | |||
static Result parseAny (String::CharPointerType& t, var& result) | |||
{ | |||
t = t.findEndOfWhitespace(); | |||
String::CharPointerType t2 (t); | |||
switch (t2.getAndAdvance()) | |||
{ | |||
case '{': t = t2; return parseObject (t, result); | |||
case '[': t = t2; return parseArray (t, result); | |||
case '"': t = t2; return parseString ('"', t, result); | |||
case '\'': t = t2; return parseString ('\'', t, result); | |||
case '-': | |||
t2 = t2.findEndOfWhitespace(); | |||
if (! CharacterFunctions::isDigit (*t2)) | |||
break; | |||
t = t2; | |||
return parseNumber (t, result, true); | |||
case '0': case '1': case '2': case '3': case '4': | |||
case '5': case '6': case '7': case '8': case '9': | |||
return parseNumber (t, result, false); | |||
case 't': // "true" | |||
if (t2.getAndAdvance() == 'r' && t2.getAndAdvance() == 'u' && t2.getAndAdvance() == 'e') | |||
{ | |||
t = t2; | |||
result = var (true); | |||
return Result::ok(); | |||
} | |||
break; | |||
case 'f': // "false" | |||
if (t2.getAndAdvance() == 'a' && t2.getAndAdvance() == 'l' | |||
&& t2.getAndAdvance() == 's' && t2.getAndAdvance() == 'e') | |||
{ | |||
t = t2; | |||
result = var (false); | |||
return Result::ok(); | |||
} | |||
break; | |||
case 'n': // "null" | |||
if (t2.getAndAdvance() == 'u' && t2.getAndAdvance() == 'l' && t2.getAndAdvance() == 'l') | |||
{ | |||
t = t2; | |||
result = var(); | |||
return Result::ok(); | |||
} | |||
break; | |||
default: | |||
break; | |||
} | |||
return createFail ("Syntax error", &t); | |||
} | |||
private: | |||
static Result createFail (const char* const message, const String::CharPointerType* location = nullptr) | |||
{ | |||
String m (message); | |||
if (location != nullptr) | |||
m << ": \"" << String (*location, 20) << '"'; | |||
return Result::fail (m); | |||
} | |||
static Result parseNumber (String::CharPointerType& t, var& result, const bool isNegative) | |||
{ | |||
String::CharPointerType oldT (t); | |||
int64 intValue = t.getAndAdvance() - '0'; | |||
jassert (intValue >= 0 && intValue < 10); | |||
for (;;) | |||
{ | |||
String::CharPointerType previousChar (t); | |||
const juce_wchar c = t.getAndAdvance(); | |||
const int digit = ((int) c) - '0'; | |||
if (isPositiveAndBelow (digit, 10)) | |||
{ | |||
intValue = intValue * 10 + digit; | |||
continue; | |||
} | |||
if (c == 'e' || c == 'E' || c == '.') | |||
{ | |||
t = oldT; | |||
const double asDouble = CharacterFunctions::readDoubleValue (t); | |||
result = isNegative ? -asDouble : asDouble; | |||
return Result::ok(); | |||
} | |||
if (CharacterFunctions::isWhitespace (c) | |||
|| c == ',' || c == '}' || c == ']' || c == 0) | |||
{ | |||
t = previousChar; | |||
break; | |||
} | |||
return createFail ("Syntax error in number", &oldT); | |||
} | |||
const int64 correctedValue = isNegative ? -intValue : intValue; | |||
if ((intValue >> 31) != 0) | |||
result = correctedValue; | |||
else | |||
result = (int) correctedValue; | |||
return Result::ok(); | |||
} | |||
static Result parseObject (String::CharPointerType& t, var& result) | |||
{ | |||
DynamicObject* const resultObject = new DynamicObject(); | |||
result = resultObject; | |||
NamedValueSet& resultProperties = resultObject->getProperties(); | |||
for (;;) | |||
{ | |||
t = t.findEndOfWhitespace(); | |||
String::CharPointerType oldT (t); | |||
const juce_wchar c = t.getAndAdvance(); | |||
if (c == '}') | |||
break; | |||
if (c == 0) | |||
return createFail ("Unexpected end-of-input in object declaration"); | |||
if (c == '"') | |||
{ | |||
var propertyNameVar; | |||
Result r (parseString ('"', t, propertyNameVar)); | |||
if (r.failed()) | |||
return r; | |||
const Identifier propertyName (propertyNameVar.toString()); | |||
if (propertyName.isValid()) | |||
{ | |||
t = t.findEndOfWhitespace(); | |||
oldT = t; | |||
const juce_wchar c2 = t.getAndAdvance(); | |||
if (c2 != ':') | |||
return createFail ("Expected ':', but found", &oldT); | |||
resultProperties.set (propertyName, var()); | |||
var* propertyValue = resultProperties.getVarPointer (propertyName); | |||
Result r2 (parseAny (t, *propertyValue)); | |||
if (r2.failed()) | |||
return r2; | |||
t = t.findEndOfWhitespace(); | |||
oldT = t; | |||
const juce_wchar nextChar = t.getAndAdvance(); | |||
if (nextChar == ',') | |||
continue; | |||
if (nextChar == '}') | |||
break; | |||
} | |||
} | |||
return createFail ("Expected object member declaration, but found", &oldT); | |||
} | |||
return Result::ok(); | |||
} | |||
static Result parseArray (String::CharPointerType& t, var& result) | |||
{ | |||
result = var (Array<var>()); | |||
Array<var>* const destArray = result.getArray(); | |||
for (;;) | |||
{ | |||
t = t.findEndOfWhitespace(); | |||
String::CharPointerType oldT (t); | |||
const juce_wchar c = t.getAndAdvance(); | |||
if (c == ']') | |||
break; | |||
if (c == 0) | |||
return createFail ("Unexpected end-of-input in array declaration"); | |||
t = oldT; | |||
destArray->add (var()); | |||
Result r (parseAny (t, destArray->getReference (destArray->size() - 1))); | |||
if (r.failed()) | |||
return r; | |||
t = t.findEndOfWhitespace(); | |||
oldT = t; | |||
const juce_wchar nextChar = t.getAndAdvance(); | |||
if (nextChar == ',') | |||
continue; | |||
if (nextChar == ']') | |||
break; | |||
return createFail ("Expected object array item, but found", &oldT); | |||
} | |||
return Result::ok(); | |||
} | |||
}; | |||
//============================================================================== | |||
struct JSONFormatter | |||
{ | |||
static void write (OutputStream& out, const var& v, | |||
const int indentLevel, const bool allOnOneLine, | |||
int maximumDecimalPlaces) | |||
{ | |||
if (v.isString()) | |||
{ | |||
out << '"'; | |||
writeString (out, v.toString().getCharPointer()); | |||
out << '"'; | |||
} | |||
else if (v.isVoid()) | |||
{ | |||
out << "null"; | |||
} | |||
else if (v.isUndefined()) | |||
{ | |||
out << "undefined"; | |||
} | |||
else if (v.isBool()) | |||
{ | |||
out << (static_cast<bool> (v) ? "true" : "false"); | |||
} | |||
else if (v.isDouble()) | |||
{ | |||
out << String (static_cast<double> (v), maximumDecimalPlaces); | |||
} | |||
else if (v.isArray()) | |||
{ | |||
writeArray (out, *v.getArray(), indentLevel, allOnOneLine, maximumDecimalPlaces); | |||
} | |||
else if (v.isObject()) | |||
{ | |||
if (DynamicObject* object = v.getDynamicObject()) | |||
object->writeAsJSON (out, indentLevel, allOnOneLine, maximumDecimalPlaces); | |||
else | |||
jassertfalse; // Only DynamicObjects can be converted to JSON! | |||
} | |||
else | |||
{ | |||
// Can't convert these other types of object to JSON! | |||
jassert (! (v.isMethod() || v.isBinaryData())); | |||
out << v.toString(); | |||
} | |||
} | |||
static void writeEscapedChar (OutputStream& out, const unsigned short value) | |||
{ | |||
out << "\\u" << String::toHexString ((int) value).paddedLeft ('0', 4); | |||
} | |||
static void writeString (OutputStream& out, String::CharPointerType t) | |||
{ | |||
for (;;) | |||
{ | |||
const juce_wchar c (t.getAndAdvance()); | |||
switch (c) | |||
{ | |||
case 0: return; | |||
case '\"': out << "\\\""; break; | |||
case '\\': out << "\\\\"; break; | |||
case '\a': out << "\\a"; break; | |||
case '\b': out << "\\b"; break; | |||
case '\f': out << "\\f"; break; | |||
case '\t': out << "\\t"; break; | |||
case '\r': out << "\\r"; break; | |||
case '\n': out << "\\n"; break; | |||
default: | |||
if (c >= 32 && c < 127) | |||
{ | |||
out << (char) c; | |||
} | |||
else | |||
{ | |||
if (CharPointer_UTF16::getBytesRequiredFor (c) > 2) | |||
{ | |||
CharPointer_UTF16::CharType chars[2]; | |||
CharPointer_UTF16 utf16 (chars); | |||
utf16.write (c); | |||
for (int i = 0; i < 2; ++i) | |||
writeEscapedChar (out, (unsigned short) chars[i]); | |||
} | |||
else | |||
{ | |||
writeEscapedChar (out, (unsigned short) c); | |||
} | |||
} | |||
break; | |||
} | |||
} | |||
} | |||
static void writeSpaces (OutputStream& out, int numSpaces) | |||
{ | |||
out.writeRepeatedByte (' ', (size_t) numSpaces); | |||
} | |||
static void writeArray (OutputStream& out, const Array<var>& array, | |||
const int indentLevel, const bool allOnOneLine, | |||
int maximumDecimalPlaces) | |||
{ | |||
out << '['; | |||
if (array.size() > 0) | |||
{ | |||
if (! allOnOneLine) | |||
out << newLine; | |||
for (int i = 0; i < array.size(); ++i) | |||
{ | |||
if (! allOnOneLine) | |||
writeSpaces (out, indentLevel + indentSize); | |||
write (out, array.getReference(i), indentLevel + indentSize, allOnOneLine, maximumDecimalPlaces); | |||
if (i < array.size() - 1) | |||
{ | |||
if (allOnOneLine) | |||
out << ", "; | |||
else | |||
out << ',' << newLine; | |||
} | |||
else if (! allOnOneLine) | |||
out << newLine; | |||
} | |||
if (! allOnOneLine) | |||
writeSpaces (out, indentLevel); | |||
} | |||
out << ']'; | |||
} | |||
enum { indentSize = 2 }; | |||
}; | |||
//============================================================================== | |||
var JSON::parse (const String& text) | |||
{ | |||
var result; | |||
if (! parse (text, result)) | |||
result = var(); | |||
return result; | |||
} | |||
var JSON::fromString (StringRef text) | |||
{ | |||
var result; | |||
if (! JSONParser::parseAny (text.text, result)) | |||
result = var(); | |||
return result; | |||
} | |||
var JSON::parse (InputStream& input) | |||
{ | |||
return parse (input.readEntireStreamAsString()); | |||
} | |||
var JSON::parse (const File& file) | |||
{ | |||
return parse (file.loadFileAsString()); | |||
} | |||
Result JSON::parse (const String& text, var& result) | |||
{ | |||
return JSONParser::parseObjectOrArray (text.getCharPointer(), result); | |||
} | |||
String JSON::toString (const var& data, const bool allOnOneLine, int maximumDecimalPlaces) | |||
{ | |||
MemoryOutputStream mo (1024); | |||
JSONFormatter::write (mo, data, 0, allOnOneLine, maximumDecimalPlaces); | |||
return mo.toUTF8(); | |||
} | |||
void JSON::writeToStream (OutputStream& output, const var& data, const bool allOnOneLine, int maximumDecimalPlaces) | |||
{ | |||
JSONFormatter::write (output, data, 0, allOnOneLine, maximumDecimalPlaces); | |||
} | |||
String JSON::escapeString (StringRef s) | |||
{ | |||
MemoryOutputStream mo; | |||
JSONFormatter::writeString (mo, s.text); | |||
return mo.toString(); | |||
} | |||
Result JSON::parseQuotedString (String::CharPointerType& t, var& result) | |||
{ | |||
const juce_wchar quote = t.getAndAdvance(); | |||
if (quote == '"' || quote == '\'') | |||
return JSONParser::parseString (quote, t, result); | |||
return Result::fail ("Not a quoted string!"); | |||
} | |||
//============================================================================== | |||
//============================================================================== | |||
#if JUCE_UNIT_TESTS | |||
class JSONTests : public UnitTest | |||
{ | |||
public: | |||
JSONTests() : UnitTest ("JSON", "JSON") {} | |||
static String createRandomWideCharString (Random& r) | |||
{ | |||
juce_wchar buffer[40] = { 0 }; | |||
for (int i = 0; i < numElementsInArray (buffer) - 1; ++i) | |||
{ | |||
if (r.nextBool()) | |||
{ | |||
do | |||
{ | |||
buffer[i] = (juce_wchar) (1 + r.nextInt (0x10ffff - 1)); | |||
} | |||
while (! CharPointer_UTF16::canRepresent (buffer[i])); | |||
} | |||
else | |||
buffer[i] = (juce_wchar) (1 + r.nextInt (0xff)); | |||
} | |||
return CharPointer_UTF32 (buffer); | |||
} | |||
static String createRandomIdentifier (Random& r) | |||
{ | |||
char buffer[30] = { 0 }; | |||
for (int i = 0; i < numElementsInArray (buffer) - 1; ++i) | |||
{ | |||
static const char chars[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-:"; | |||
buffer[i] = chars [r.nextInt (sizeof (chars) - 1)]; | |||
} | |||
return CharPointer_ASCII (buffer); | |||
} | |||
// Creates a random double that can be easily stringified, to avoid | |||
// false failures when decimal places are rounded or truncated slightly | |||
static var createRandomDouble (Random& r) | |||
{ | |||
return var ((r.nextDouble() * 1000.0) + 0.1); | |||
} | |||
static var createRandomVar (Random& r, int depth) | |||
{ | |||
switch (r.nextInt (depth > 3 ? 6 : 8)) | |||
{ | |||
case 0: return {}; | |||
case 1: return r.nextInt(); | |||
case 2: return r.nextInt64(); | |||
case 3: return r.nextBool(); | |||
case 4: return createRandomDouble (r); | |||
case 5: return createRandomWideCharString (r); | |||
case 6: | |||
{ | |||
var v (createRandomVar (r, depth + 1)); | |||
for (int i = 1 + r.nextInt (30); --i >= 0;) | |||
v.append (createRandomVar (r, depth + 1)); | |||
return v; | |||
} | |||
case 7: | |||
{ | |||
DynamicObject* o = new DynamicObject(); | |||
for (int i = r.nextInt (30); --i >= 0;) | |||
o->setProperty (createRandomIdentifier (r), createRandomVar (r, depth + 1)); | |||
return o; | |||
} | |||
default: | |||
return {}; | |||
} | |||
} | |||
void runTest() override | |||
{ | |||
beginTest ("JSON"); | |||
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; | |||
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); | |||
} | |||
} | |||
}; | |||
static JSONTests JSONUnitTests; | |||
#endif | |||
} // namespace juce |
@@ -1,132 +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. | |||
============================================================================== | |||
*/ | |||
namespace juce | |||
{ | |||
//============================================================================== | |||
/** | |||
Contains static methods for converting JSON-formatted text to and from var objects. | |||
The var class is structurally compatible with JSON-formatted data, so these | |||
functions allow you to parse JSON into a var object, and to convert a var | |||
object to JSON-formatted text. | |||
@see var | |||
*/ | |||
class JUCE_API JSON | |||
{ | |||
public: | |||
//============================================================================== | |||
/** Parses a string of JSON-formatted text, and returns a result code containing | |||
any parse errors. | |||
This will return the parsed structure in the parsedResult parameter, and will | |||
return a Result object to indicate whether parsing was successful, and if not, | |||
it will contain an error message. | |||
If you're not interested in the error message, you can use one of the other | |||
shortcut parse methods, which simply return a var() if the parsing fails. | |||
Note that this will only parse valid JSON, which means that the item given must | |||
be either an object or an array definition. If you want to also be able to parse | |||
any kind of primitive JSON object, use the fromString() method. | |||
*/ | |||
static Result parse (const String& text, var& parsedResult); | |||
/** Attempts to parse some JSON-formatted text, and returns the result as a var object. | |||
If the parsing fails, this simply returns var() - if you need to find out more | |||
detail about the parse error, use the alternative parse() method which returns a Result. | |||
Note that this will only parse valid JSON, which means that the item given must | |||
be either an object or an array definition. If you want to also be able to parse | |||
any kind of primitive JSON object, use the fromString() method. | |||
*/ | |||
static var parse (const String& text); | |||
/** Attempts to parse some JSON-formatted text from a file, and returns the result | |||
as a var object. | |||
Note that this is just a short-cut for reading the entire file into a string and | |||
parsing the result. | |||
If the parsing fails, this simply returns var() - if you need to find out more | |||
detail about the parse error, use the alternative parse() method which returns a Result. | |||
*/ | |||
static var parse (const File& file); | |||
/** Attempts to parse some JSON-formatted text from a stream, and returns the result | |||
as a var object. | |||
Note that this is just a short-cut for reading the entire stream into a string and | |||
parsing the result. | |||
If the parsing fails, this simply returns var() - if you need to find out more | |||
detail about the parse error, use the alternative parse() method which returns a Result. | |||
*/ | |||
static var parse (InputStream& input); | |||
//============================================================================== | |||
/** Returns a string which contains a JSON-formatted representation of the var object. | |||
If allOnOneLine is true, the result will be compacted into a single line of text | |||
with no carriage-returns. If false, it will be laid-out in a more human-readable format. | |||
The maximumDecimalPlaces parameter determines the precision of floating point numbers. | |||
@see writeToStream | |||
*/ | |||
static String toString (const var& objectToFormat, | |||
bool allOnOneLine = false, | |||
int maximumDecimalPlaces = 20); | |||
/** Parses a string that was created with the toString() method. | |||
This is slightly different to the parse() methods because they will reject primitive | |||
values and only accept array or object definitions, whereas this method will handle | |||
either. | |||
*/ | |||
static var fromString (StringRef); | |||
/** Writes a JSON-formatted representation of the var object to the given stream. | |||
If allOnOneLine is true, the result will be compacted into a single line of text | |||
with no carriage-returns. If false, it will be laid-out in a more human-readable format. | |||
The maximumDecimalPlaces parameter determines the precision of floating point numbers. | |||
@see toString | |||
*/ | |||
static void writeToStream (OutputStream& output, | |||
const var& objectToFormat, | |||
bool allOnOneLine = false, | |||
int maximumDecimalPlaces = 20); | |||
/** Returns a version of a string with any extended characters escaped. */ | |||
static String escapeString (StringRef); | |||
/** Parses a quoted string-literal in JSON format, returning the un-escaped result in the | |||
result parameter, and an error message in case the content was illegal. | |||
This advances the text parameter, leaving it positioned after the closing quote. | |||
*/ | |||
static Result parseQuotedString (String::CharPointerType& text, var& result); | |||
private: | |||
//============================================================================== | |||
JSON() JUCE_DELETED_FUNCTION; // This class can't be instantiated - just use its static methods. | |||
}; | |||
} // namespace juce |
@@ -1,120 +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. | |||
============================================================================== | |||
*/ | |||
namespace juce | |||
{ | |||
//============================================================================== | |||
/** | |||
A simple javascript interpreter! | |||
It's not fully standards-compliant, and won't be as fast as the fancy JIT-compiled | |||
engines that you get in browsers, but this is an extremely compact, low-overhead javascript | |||
interpreter, which is integrated with the juce var and DynamicObject classes. If you need | |||
a few simple bits of scripting in your app, and want to be able to easily let the JS | |||
work with native objects defined as DynamicObject subclasses, then this might do the job. | |||
To use, simply create an instance of this class and call execute() to run your code. | |||
Variables that the script sets can be retrieved with evaluate(), and if you need to provide | |||
native objects for the script to use, you can add them with registerNativeObject(). | |||
One caveat: Because the values and objects that the engine works with are DynamicObject | |||
and var objects, they use reference-counting rather than garbage-collection, so if your | |||
script creates complex connections between objects, you run the risk of creating cyclic | |||
dependencies and hence leaking. | |||
*/ | |||
class JUCE_API JavascriptEngine | |||
{ | |||
public: | |||
/** Creates an instance of the engine. | |||
This creates a root namespace and defines some basic Object, String, Array | |||
and Math library methods. | |||
*/ | |||
JavascriptEngine(); | |||
/** Destructor. */ | |||
~JavascriptEngine(); | |||
/** Attempts to parse and run a block of javascript code. | |||
If there's a parse or execution error, the error description is returned in | |||
the result. | |||
You can specify a maximum time for which the program is allowed to run, and | |||
it'll return with an error message if this time is exceeded. | |||
*/ | |||
Result execute (const String& javascriptCode); | |||
/** Attempts to parse and run a javascript expression, and returns the result. | |||
If there's a syntax error, or the expression can't be evaluated, the return value | |||
will be var::undefined(). The errorMessage parameter gives you a way to find out | |||
any parsing errors. | |||
You can specify a maximum time for which the program is allowed to run, and | |||
it'll return with an error message if this time is exceeded. | |||
*/ | |||
var evaluate (const String& javascriptCode, | |||
Result* errorMessage = nullptr); | |||
/** Calls a function in the root namespace, and returns the result. | |||
The function arguments are passed in the same format as used by native | |||
methods in the var class. | |||
*/ | |||
var callFunction (const Identifier& function, | |||
const var::NativeFunctionArgs& args, | |||
Result* errorMessage = nullptr); | |||
/** Calls a function object in the namespace of a dynamic object, and returns the result. | |||
The function arguments are passed in the same format as used by native | |||
methods in the var class. | |||
*/ | |||
var callFunctionObject (DynamicObject* objectScope, | |||
const var& functionObject, | |||
const var::NativeFunctionArgs& args, | |||
Result* errorMessage = nullptr); | |||
/** Adds a native object to the root namespace. | |||
The object passed-in is reference-counted, and will be retained by the | |||
engine until the engine is deleted. The name must be a simple JS identifier, | |||
without any dots. | |||
*/ | |||
void registerNativeObject (const Identifier& objectName, DynamicObject* object); | |||
/** This value indicates how long a call to one of the evaluate methods is permitted | |||
to run before timing-out and failing. | |||
The default value is a number of seconds, but you can change this to whatever value | |||
suits your application. | |||
*/ | |||
RelativeTime maximumExecutionTime; | |||
/** When called from another thread, causes the interpreter to time-out as soon as possible */ | |||
void stop() noexcept; | |||
/** Provides access to the set of properties of the root namespace object. */ | |||
const NamedValueSet& getRootObjectProperties() const noexcept; | |||
private: | |||
JUCE_PUBLIC_IN_DLL_BUILD (struct RootObject) | |||
const ReferenceCountedObjectPtr<RootObject> root; | |||
void prepareTimeout() const noexcept; | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (JavascriptEngine) | |||
}; | |||
} // namespace juce |
@@ -1,254 +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. | |||
============================================================================== | |||
*/ | |||
#ifdef JUCE_CORE_H_INCLUDED | |||
/* When you add this cpp file to your project, you mustn't include it in a file where you've | |||
already included any other headers - just put it inside a file on its own, possibly with your config | |||
flags preceding it, but don't include anything else. That also includes avoiding any automatic prefix | |||
header files that the compiler may be using. | |||
*/ | |||
#error "Incorrect use of JUCE cpp file" | |||
#endif | |||
#define JUCE_CORE_INCLUDE_OBJC_HELPERS 1 | |||
#define JUCE_CORE_INCLUDE_COM_SMART_PTR 1 | |||
#define JUCE_CORE_INCLUDE_NATIVE_HEADERS 1 | |||
#define JUCE_CORE_INCLUDE_JNI_HELPERS 1 | |||
#include "juce_core.h" | |||
#include <locale> | |||
#include <cctype> | |||
#include <cstdarg> | |||
#if ! JUCE_ANDROID | |||
#include <sys/timeb.h> | |||
#include <cwctype> | |||
#endif | |||
#if JUCE_WINDOWS | |||
#include <ctime> | |||
#if ! JUCE_MINGW | |||
#pragma warning (push) | |||
#pragma warning (disable: 4091) | |||
#include <Dbghelp.h> | |||
#pragma warning (pop) | |||
#if ! JUCE_DONT_AUTOLINK_TO_WIN32_LIBRARIES | |||
#pragma comment (lib, "DbgHelp.lib") | |||
#endif | |||
#endif | |||
#if JUCE_MINGW | |||
#include <ws2spi.h> | |||
#include <cstdio> | |||
#include <locale.h> | |||
#endif | |||
#else | |||
#if JUCE_LINUX || JUCE_ANDROID | |||
#include <sys/types.h> | |||
#include <sys/socket.h> | |||
#include <sys/errno.h> | |||
#include <unistd.h> | |||
#include <netinet/in.h> | |||
#endif | |||
#if JUCE_LINUX | |||
#include <stdio.h> | |||
#include <langinfo.h> | |||
#include <ifaddrs.h> | |||
#include <sys/resource.h> | |||
#if JUCE_USE_CURL | |||
#include <curl/curl.h> | |||
#endif | |||
#endif | |||
#include <pwd.h> | |||
#include <fcntl.h> | |||
#include <netdb.h> | |||
#include <arpa/inet.h> | |||
#include <netinet/tcp.h> | |||
#include <sys/time.h> | |||
#include <net/if.h> | |||
#include <sys/ioctl.h> | |||
#if ! JUCE_ANDROID | |||
#include <execinfo.h> | |||
#endif | |||
#endif | |||
#if JUCE_MAC || JUCE_IOS | |||
#include <xlocale.h> | |||
#include <mach/mach.h> | |||
#endif | |||
#if JUCE_ANDROID | |||
#include <android/log.h> | |||
#endif | |||
#undef check | |||
//============================================================================== | |||
#ifndef JUCE_STANDALONE_APPLICATION | |||
JUCE_COMPILER_WARNING ("Please re-save your project with the latest Projucer version to avoid this warning") | |||
#define JUCE_STANDALONE_APPLICATION 0 | |||
#endif | |||
//============================================================================== | |||
#include "containers/juce_AbstractFifo.cpp" | |||
#include "containers/juce_NamedValueSet.cpp" | |||
#include "containers/juce_ListenerList.cpp" | |||
#include "containers/juce_PropertySet.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" | |||
#include "maths/juce_Expression.cpp" | |||
#include "maths/juce_Random.cpp" | |||
#include "memory/juce_MemoryBlock.cpp" | |||
#include "misc/juce_RuntimePermissions.cpp" | |||
#include "misc/juce_Result.cpp" | |||
#include "misc/juce_Uuid.cpp" | |||
#include "misc/juce_StdFunctionCompat.cpp" | |||
#include "network/juce_MACAddress.cpp" | |||
#include "network/juce_NamedPipe.cpp" | |||
#include "network/juce_Socket.cpp" | |||
#include "network/juce_IPAddress.cpp" | |||
#include "streams/juce_BufferedInputStream.cpp" | |||
#include "streams/juce_FileInputSource.cpp" | |||
#include "streams/juce_InputStream.cpp" | |||
#include "streams/juce_MemoryInputStream.cpp" | |||
#include "streams/juce_MemoryOutputStream.cpp" | |||
#include "streams/juce_SubregionStream.cpp" | |||
#include "system/juce_SystemStats.cpp" | |||
#include "text/juce_CharacterFunctions.cpp" | |||
#include "text/juce_Identifier.cpp" | |||
#include "text/juce_LocalisedStrings.cpp" | |||
#include "text/juce_String.cpp" | |||
#include "streams/juce_OutputStream.cpp" | |||
#include "text/juce_StringArray.cpp" | |||
#include "text/juce_StringPairArray.cpp" | |||
#include "text/juce_StringPool.cpp" | |||
#include "text/juce_TextDiff.cpp" | |||
#include "text/juce_Base64.cpp" | |||
#include "threads/juce_ReadWriteLock.cpp" | |||
#include "threads/juce_Thread.cpp" | |||
#include "threads/juce_ThreadPool.cpp" | |||
#include "threads/juce_TimeSliceThread.cpp" | |||
#include "time/juce_PerformanceCounter.cpp" | |||
#include "time/juce_RelativeTime.cpp" | |||
#include "time/juce_Time.cpp" | |||
#include "unit_tests/juce_UnitTest.cpp" | |||
#include "xml/juce_XmlDocument.cpp" | |||
#include "xml/juce_XmlElement.cpp" | |||
#include "zip/juce_GZIPDecompressorInputStream.cpp" | |||
#include "zip/juce_GZIPCompressorOutputStream.cpp" | |||
#include "zip/juce_ZipFile.cpp" | |||
#include "files/juce_FileFilter.cpp" | |||
#include "files/juce_WildcardFileFilter.cpp" | |||
//============================================================================== | |||
#if ! JUCE_WINDOWS | |||
#include "native/juce_posix_SharedCode.h" | |||
#include "native/juce_posix_NamedPipe.cpp" | |||
#endif | |||
//============================================================================== | |||
#if JUCE_MAC || JUCE_IOS | |||
#include "native/juce_mac_Files.mm" | |||
#include "native/juce_mac_Network.mm" | |||
#include "native/juce_mac_Strings.mm" | |||
#include "native/juce_mac_SystemStats.mm" | |||
#include "native/juce_mac_Threads.mm" | |||
//============================================================================== | |||
#elif JUCE_WINDOWS | |||
#include "native/juce_win32_Files.cpp" | |||
#include "native/juce_win32_Network.cpp" | |||
#include "native/juce_win32_Registry.cpp" | |||
#include "native/juce_win32_SystemStats.cpp" | |||
#include "native/juce_win32_Threads.cpp" | |||
//============================================================================== | |||
#elif JUCE_LINUX | |||
#include "native/juce_linux_CommonFile.cpp" | |||
#include "native/juce_linux_Files.cpp" | |||
#include "native/juce_linux_Network.cpp" | |||
#if JUCE_USE_CURL | |||
#include "native/juce_curl_Network.cpp" | |||
#endif | |||
#include "native/juce_linux_SystemStats.cpp" | |||
#include "native/juce_linux_Threads.cpp" | |||
//============================================================================== | |||
#elif JUCE_ANDROID | |||
#include "native/juce_linux_CommonFile.cpp" | |||
#include "native/juce_android_Files.cpp" | |||
#include "native/juce_android_Misc.cpp" | |||
#include "native/juce_android_Network.cpp" | |||
#include "native/juce_android_SystemStats.cpp" | |||
#include "native/juce_android_Threads.cpp" | |||
#include "native/juce_android_RuntimePermissions.cpp" | |||
#endif | |||
#include "threads/juce_ChildProcess.cpp" | |||
#include "threads/juce_HighResolutionTimer.cpp" | |||
#include "network/juce_URL.cpp" | |||
#include "network/juce_WebInputStream.cpp" | |||
//============================================================================== | |||
#if JUCE_UNIT_TESTS | |||
#include "containers/juce_HashMap_test.cpp" | |||
#endif | |||
//============================================================================== | |||
namespace juce | |||
{ | |||
/* | |||
As the very long class names here try to explain, the purpose of this code is to cause | |||
a linker error if not all of your compile units are consistent in the options that they | |||
enable before including JUCE headers. The reason this is important is that if you have | |||
two cpp files, and one includes the juce headers with debug enabled, and the other doesn't, | |||
then each will be generating code with different memory layouts for the classes, and | |||
you'll get subtle and hard-to-track-down memory corruption bugs! | |||
*/ | |||
#if JUCE_DEBUG | |||
this_will_fail_to_link_if_some_of_your_compile_units_are_built_in_debug_mode | |||
::this_will_fail_to_link_if_some_of_your_compile_units_are_built_in_debug_mode() noexcept {} | |||
#else | |||
this_will_fail_to_link_if_some_of_your_compile_units_are_built_in_release_mode | |||
::this_will_fail_to_link_if_some_of_your_compile_units_are_built_in_release_mode() noexcept {} | |||
#endif | |||
} |
@@ -1,355 +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. | |||
============================================================================== | |||
*/ | |||
/******************************************************************************* | |||
The block below describes the properties of this module, and is read by | |||
the Projucer to automatically generate project code that uses it. | |||
For details about the syntax and how to create or use a module, see the | |||
JUCE Module Format.txt file. | |||
BEGIN_JUCE_MODULE_DECLARATION | |||
ID: juce_core | |||
vendor: juce | |||
version: 5.1.2 | |||
name: JUCE core classes | |||
description: The essential set of basic JUCE classes, as required by all the other JUCE modules. Includes text, container, memory, threading and i/o functionality. | |||
website: http://www.juce.com/juce | |||
license: ISC | |||
dependencies: | |||
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 | |||
*******************************************************************************/ | |||
#pragma once | |||
#define JUCE_CORE_H_INCLUDED | |||
//============================================================================== | |||
#ifdef _MSC_VER | |||
#pragma warning (push) | |||
// Disable warnings for long class names, padding, and undefined preprocessor definitions. | |||
#pragma warning (disable: 4251 4786 4668 4820) | |||
#ifdef __INTEL_COMPILER | |||
#pragma warning (disable: 1125) | |||
#endif | |||
#endif | |||
#include "system/juce_TargetPlatform.h" | |||
//============================================================================== | |||
/** Config: JUCE_FORCE_DEBUG | |||
Normally, JUCE_DEBUG is set to 1 or 0 based on compiler and project settings, | |||
but if you define this value, you can override this to force it to be true or false. | |||
*/ | |||
#ifndef JUCE_FORCE_DEBUG | |||
//#define JUCE_FORCE_DEBUG 0 | |||
#endif | |||
//============================================================================== | |||
/** Config: JUCE_LOG_ASSERTIONS | |||
If this flag is enabled, the jassert and jassertfalse macros will always use Logger::writeToLog() | |||
to write a message when an assertion happens. | |||
Enabling it will also leave this turned on in release builds. When it's disabled, | |||
however, the jassert and jassertfalse macros will not be compiled in a | |||
release build. | |||
@see jassert, jassertfalse, Logger | |||
*/ | |||
#ifndef JUCE_LOG_ASSERTIONS | |||
#if JUCE_ANDROID | |||
#define JUCE_LOG_ASSERTIONS 1 | |||
#else | |||
#define JUCE_LOG_ASSERTIONS 0 | |||
#endif | |||
#endif | |||
//============================================================================== | |||
/** Config: JUCE_CHECK_MEMORY_LEAKS | |||
Enables a memory-leak check for certain objects when the app terminates. See the LeakedObjectDetector | |||
class and the JUCE_LEAK_DETECTOR macro for more details about enabling leak checking for specific classes. | |||
*/ | |||
#if JUCE_DEBUG && ! defined (JUCE_CHECK_MEMORY_LEAKS) | |||
#define JUCE_CHECK_MEMORY_LEAKS 1 | |||
#endif | |||
//============================================================================== | |||
/** Config: JUCE_DONT_AUTOLINK_TO_WIN32_LIBRARIES | |||
In a Visual C++ build, this can be used to stop the required system libs being | |||
automatically added to the link stage. | |||
*/ | |||
#ifndef JUCE_DONT_AUTOLINK_TO_WIN32_LIBRARIES | |||
#define JUCE_DONT_AUTOLINK_TO_WIN32_LIBRARIES 0 | |||
#endif | |||
/** Config: JUCE_INCLUDE_ZLIB_CODE | |||
This can be used to disable Juce's embedded 3rd-party zlib code. | |||
You might need to tweak this if you're linking to an external zlib library in your app, | |||
but for normal apps, this option should be left alone. | |||
If you disable this, you might also want to set a value for JUCE_ZLIB_INCLUDE_PATH, to | |||
specify the path where your zlib headers live. | |||
*/ | |||
#ifndef JUCE_INCLUDE_ZLIB_CODE | |||
#define JUCE_INCLUDE_ZLIB_CODE 1 | |||
#endif | |||
#ifndef JUCE_ZLIB_INCLUDE_PATH | |||
#define JUCE_ZLIB_INCLUDE_PATH <zlib.h> | |||
#endif | |||
/** Config: JUCE_USE_CURL | |||
Enables http/https support via libcurl (Linux only). Enabling this will add an additional | |||
run-time dynamic dependency to libcurl. | |||
If you disable this then https/ssl support will not be available on linux. | |||
*/ | |||
#ifndef JUCE_USE_CURL | |||
#define JUCE_USE_CURL 0 | |||
#endif | |||
/** Config: JUCE_CATCH_UNHANDLED_EXCEPTIONS | |||
If enabled, this will add some exception-catching code to forward unhandled exceptions | |||
to your JUCEApplicationBase::unhandledException() callback. | |||
*/ | |||
#ifndef JUCE_CATCH_UNHANDLED_EXCEPTIONS | |||
//#define JUCE_CATCH_UNHANDLED_EXCEPTIONS 1 | |||
#endif | |||
/** Config: JUCE_ALLOW_STATIC_NULL_VARIABLES | |||
If disabled, this will turn off dangerous static globals like String::empty, var::null, etc | |||
which can cause nasty order-of-initialisation problems if they are referenced during static | |||
constructor code. | |||
*/ | |||
#ifndef JUCE_ALLOW_STATIC_NULL_VARIABLES | |||
#define JUCE_ALLOW_STATIC_NULL_VARIABLES 1 | |||
#endif | |||
#ifndef JUCE_STRING_UTF_TYPE | |||
#define JUCE_STRING_UTF_TYPE 8 | |||
#endif | |||
//============================================================================== | |||
//============================================================================== | |||
#if JUCE_CORE_INCLUDE_NATIVE_HEADERS | |||
#include "native/juce_BasicNativeHeaders.h" | |||
#endif | |||
#include "system/juce_StandardHeader.h" | |||
namespace juce | |||
{ | |||
class StringRef; | |||
class MemoryBlock; | |||
class File; | |||
class InputStream; | |||
class OutputStream; | |||
class DynamicObject; | |||
class FileInputStream; | |||
class FileOutputStream; | |||
class XmlElement; | |||
extern JUCE_API bool JUCE_CALLTYPE juce_isRunningUnderDebugger() noexcept; | |||
extern JUCE_API void JUCE_CALLTYPE logAssertion (const char* file, int line) noexcept; | |||
} | |||
#include "memory/juce_Memory.h" | |||
#include "maths/juce_MathsFunctions.h" | |||
#include "memory/juce_ByteOrder.h" | |||
#include "memory/juce_Atomic.h" | |||
#include "text/juce_CharacterFunctions.h" | |||
#if JUCE_MSVC | |||
#pragma warning (push) | |||
#pragma warning (disable: 4514 4996) | |||
#endif | |||
#include "text/juce_CharPointer_UTF8.h" | |||
#include "text/juce_CharPointer_UTF16.h" | |||
#include "text/juce_CharPointer_UTF32.h" | |||
#include "text/juce_CharPointer_ASCII.h" | |||
#if JUCE_MSVC | |||
#pragma warning (pop) | |||
#endif | |||
#include "text/juce_String.h" | |||
#include "text/juce_StringRef.h" | |||
#include "logging/juce_Logger.h" | |||
#include "memory/juce_LeakedObjectDetector.h" | |||
#include "memory/juce_ContainerDeletePolicy.h" | |||
#include "memory/juce_HeapBlock.h" | |||
#include "memory/juce_MemoryBlock.h" | |||
#include "memory/juce_ReferenceCountedObject.h" | |||
#include "memory/juce_ScopedPointer.h" | |||
#include "memory/juce_OptionalScopedPointer.h" | |||
#include "memory/juce_Singleton.h" | |||
#include "memory/juce_WeakReference.h" | |||
#include "threads/juce_ScopedLock.h" | |||
#include "threads/juce_CriticalSection.h" | |||
#include "maths/juce_Range.h" | |||
#include "maths/juce_NormalisableRange.h" | |||
#include "maths/juce_StatisticsAccumulator.h" | |||
#include "containers/juce_ElementComparator.h" | |||
#include "containers/juce_ArrayAllocationBase.h" | |||
#include "containers/juce_Array.h" | |||
#include "containers/juce_LinkedListPointer.h" | |||
#include "containers/juce_ListenerList.h" | |||
#include "containers/juce_OwnedArray.h" | |||
#include "containers/juce_ReferenceCountedArray.h" | |||
#include "containers/juce_ScopedValueSetter.h" | |||
#include "containers/juce_SortedSet.h" | |||
#include "containers/juce_SparseSet.h" | |||
#include "containers/juce_AbstractFifo.h" | |||
#include "text/juce_NewLine.h" | |||
#include "text/juce_StringPool.h" | |||
#include "text/juce_Identifier.h" | |||
#include "text/juce_StringArray.h" | |||
#include "text/juce_StringPairArray.h" | |||
#include "text/juce_TextDiff.h" | |||
#include "text/juce_LocalisedStrings.h" | |||
#include "text/juce_Base64.h" | |||
#include "misc/juce_Result.h" | |||
#include "containers/juce_Variant.h" | |||
#include "containers/juce_NamedValueSet.h" | |||
#include "containers/juce_DynamicObject.h" | |||
#include "containers/juce_HashMap.h" | |||
#include "time/juce_RelativeTime.h" | |||
#include "time/juce_Time.h" | |||
#include "streams/juce_InputStream.h" | |||
#include "streams/juce_OutputStream.h" | |||
#include "streams/juce_BufferedInputStream.h" | |||
#include "streams/juce_MemoryInputStream.h" | |||
#include "streams/juce_MemoryOutputStream.h" | |||
#include "streams/juce_SubregionStream.h" | |||
#include "streams/juce_InputSource.h" | |||
#include "files/juce_File.h" | |||
#include "files/juce_DirectoryIterator.h" | |||
#include "files/juce_FileInputStream.h" | |||
#include "files/juce_FileOutputStream.h" | |||
#include "files/juce_FileSearchPath.h" | |||
#include "files/juce_MemoryMappedFile.h" | |||
#include "files/juce_TemporaryFile.h" | |||
#include "files/juce_FileFilter.h" | |||
#include "files/juce_WildcardFileFilter.h" | |||
#include "streams/juce_FileInputSource.h" | |||
#include "logging/juce_FileLogger.h" | |||
#include "javascript/juce_JSON.h" | |||
#include "javascript/juce_Javascript.h" | |||
#include "maths/juce_BigInteger.h" | |||
#include "maths/juce_Expression.h" | |||
#include "maths/juce_Random.h" | |||
#include "misc/juce_RuntimePermissions.h" | |||
#include "misc/juce_Uuid.h" | |||
#include "misc/juce_WindowsRegistry.h" | |||
#include "threads/juce_ChildProcess.h" | |||
#include "threads/juce_DynamicLibrary.h" | |||
#include "threads/juce_HighResolutionTimer.h" | |||
#include "threads/juce_InterProcessLock.h" | |||
#include "threads/juce_Process.h" | |||
#include "threads/juce_SpinLock.h" | |||
#include "threads/juce_WaitableEvent.h" | |||
#include "threads/juce_Thread.h" | |||
#include "threads/juce_ThreadLocalValue.h" | |||
#include "threads/juce_ThreadPool.h" | |||
#include "threads/juce_TimeSliceThread.h" | |||
#include "threads/juce_ReadWriteLock.h" | |||
#include "threads/juce_ScopedReadLock.h" | |||
#include "threads/juce_ScopedWriteLock.h" | |||
#include "network/juce_IPAddress.h" | |||
#include "network/juce_MACAddress.h" | |||
#include "network/juce_NamedPipe.h" | |||
#include "network/juce_Socket.h" | |||
#include "network/juce_URL.h" | |||
#include "network/juce_WebInputStream.h" | |||
#include "system/juce_SystemStats.h" | |||
#include "time/juce_PerformanceCounter.h" | |||
#include "unit_tests/juce_UnitTest.h" | |||
#include "xml/juce_XmlDocument.h" | |||
#include "xml/juce_XmlElement.h" | |||
#include "zip/juce_GZIPCompressorOutputStream.h" | |||
#include "zip/juce_GZIPDecompressorInputStream.h" | |||
#include "zip/juce_ZipFile.h" | |||
#include "containers/juce_PropertySet.h" | |||
#include "memory/juce_SharedResourcePointer.h" | |||
#if JUCE_CORE_INCLUDE_OBJC_HELPERS && (JUCE_MAC || JUCE_IOS) | |||
#include "native/juce_osx_ObjCHelpers.h" | |||
#endif | |||
#if JUCE_CORE_INCLUDE_COM_SMART_PTR && JUCE_WINDOWS | |||
#include "native/juce_win32_ComSmartPtr.h" | |||
#endif | |||
#if JUCE_CORE_INCLUDE_JNI_HELPERS && JUCE_ANDROID | |||
#include <jni.h> | |||
#include "native/juce_android_JNIHelpers.h" | |||
#endif | |||
#ifndef DOXYGEN | |||
namespace juce | |||
{ | |||
/* | |||
As the very long class names here try to explain, the purpose of this code is to cause | |||
a linker error if not all of your compile units are consistent in the options that they | |||
enable before including JUCE headers. The reason this is important is that if you have | |||
two cpp files, and one includes the juce headers with debug enabled, and another does so | |||
without that, then each will be generating code with different class layouts, and you'll | |||
get subtle and hard-to-track-down memory corruption! | |||
*/ | |||
#if JUCE_DEBUG | |||
struct JUCE_API this_will_fail_to_link_if_some_of_your_compile_units_are_built_in_debug_mode | |||
{ this_will_fail_to_link_if_some_of_your_compile_units_are_built_in_debug_mode() noexcept; }; | |||
static this_will_fail_to_link_if_some_of_your_compile_units_are_built_in_debug_mode compileUnitMismatchSentinel; | |||
#else | |||
struct JUCE_API this_will_fail_to_link_if_some_of_your_compile_units_are_built_in_release_mode | |||
{ this_will_fail_to_link_if_some_of_your_compile_units_are_built_in_release_mode() noexcept; }; | |||
static this_will_fail_to_link_if_some_of_your_compile_units_are_built_in_release_mode compileUnitMismatchSentinel; | |||
#endif | |||
} | |||
#endif | |||
#if JUCE_MSVC | |||
#pragma warning (pop) | |||
// In DLL builds, need to disable this warnings for other modules | |||
#if defined (JUCE_DLL_BUILD) || defined (JUCE_DLL) | |||
#pragma warning (disable: 4251) | |||
#endif | |||
#endif |
@@ -1,133 +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. | |||
============================================================================== | |||
*/ | |||
namespace juce | |||
{ | |||
FileLogger::FileLogger (const File& file, | |||
const String& welcomeMessage, | |||
const int64 maxInitialFileSizeBytes) | |||
: logFile (file) | |||
{ | |||
if (maxInitialFileSizeBytes >= 0) | |||
trimFileSize (logFile, maxInitialFileSizeBytes); | |||
if (! file.exists()) | |||
file.create(); // (to create the parent directories) | |||
String welcome; | |||
welcome << newLine | |||
<< "**********************************************************" << newLine | |||
<< welcomeMessage << newLine | |||
<< "Log started: " << Time::getCurrentTime().toString (true, true) << newLine; | |||
FileLogger::logMessage (welcome); | |||
} | |||
FileLogger::~FileLogger() {} | |||
//============================================================================== | |||
void FileLogger::logMessage (const String& message) | |||
{ | |||
const ScopedLock sl (logLock); | |||
DBG (message); | |||
FileOutputStream out (logFile, 256); | |||
out << message << newLine; | |||
} | |||
void FileLogger::trimFileSize (const File& file, int64 maxFileSizeBytes) | |||
{ | |||
if (maxFileSizeBytes <= 0) | |||
{ | |||
file.deleteFile(); | |||
} | |||
else | |||
{ | |||
const int64 fileSize = file.getSize(); | |||
if (fileSize > maxFileSizeBytes) | |||
{ | |||
TemporaryFile tempFile (file); | |||
{ | |||
FileOutputStream out (tempFile.getFile()); | |||
FileInputStream in (file); | |||
if (! (out.openedOk() && in.openedOk())) | |||
return; | |||
in.setPosition (fileSize - maxFileSizeBytes); | |||
for (;;) | |||
{ | |||
const char c = in.readByte(); | |||
if (c == 0) | |||
return; | |||
if (c == '\n' || c == '\r') | |||
{ | |||
out << c; | |||
break; | |||
} | |||
} | |||
out.writeFromInputStream (in, -1); | |||
} | |||
tempFile.overwriteTargetFileWithTemporary(); | |||
} | |||
} | |||
} | |||
//============================================================================== | |||
File FileLogger::getSystemLogFileFolder() | |||
{ | |||
#if JUCE_MAC | |||
return File ("~/Library/Logs"); | |||
#else | |||
return File::getSpecialLocation (File::userApplicationDataDirectory); | |||
#endif | |||
} | |||
FileLogger* FileLogger::createDefaultAppLogger (const String& logFileSubDirectoryName, | |||
const String& logFileName, | |||
const String& welcomeMessage, | |||
const int64 maxInitialFileSizeBytes) | |||
{ | |||
return new FileLogger (getSystemLogFileFolder().getChildFile (logFileSubDirectoryName) | |||
.getChildFile (logFileName), | |||
welcomeMessage, maxInitialFileSizeBytes); | |||
} | |||
FileLogger* FileLogger::createDateStampedLogger (const String& logFileSubDirectoryName, | |||
const String& logFileNameRoot, | |||
const String& logFileNameSuffix, | |||
const String& welcomeMessage) | |||
{ | |||
return new FileLogger (getSystemLogFileFolder().getChildFile (logFileSubDirectoryName) | |||
.getChildFile (logFileNameRoot + Time::getCurrentTime().formatted ("%Y-%m-%d_%H-%M-%S")) | |||
.withFileExtension (logFileNameSuffix) | |||
.getNonexistentSibling(), | |||
welcomeMessage, 0); | |||
} | |||
} // namespace juce |
@@ -1,131 +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. | |||
============================================================================== | |||
*/ | |||
namespace juce | |||
{ | |||
//============================================================================== | |||
/** | |||
A simple implementation of a Logger that writes to a file. | |||
@see Logger | |||
*/ | |||
class JUCE_API FileLogger : public Logger | |||
{ | |||
public: | |||
//============================================================================== | |||
/** Creates a FileLogger for a given file. | |||
@param fileToWriteTo the file that to use - new messages will be appended | |||
to the file. If the file doesn't exist, it will be created, | |||
along with any parent directories that are needed. | |||
@param welcomeMessage when opened, the logger will write a header to the log, along | |||
with the current date and time, and this welcome message | |||
@param maxInitialFileSizeBytes if this is zero or greater, then if the file already exists | |||
but is larger than this number of bytes, then the start of the | |||
file will be truncated to keep the size down. This prevents a log | |||
file getting ridiculously large over time. The file will be truncated | |||
at a new-line boundary. If this value is less than zero, no size limit | |||
will be imposed; if it's zero, the file will always be deleted. Note that | |||
the size is only checked once when this object is created - any logging | |||
that is done later will be appended without any checking | |||
*/ | |||
FileLogger (const File& fileToWriteTo, | |||
const String& welcomeMessage, | |||
const int64 maxInitialFileSizeBytes = 128 * 1024); | |||
/** Destructor. */ | |||
~FileLogger(); | |||
//============================================================================== | |||
/** Returns the file that this logger is writing to. */ | |||
const File& getLogFile() const noexcept { return logFile; } | |||
//============================================================================== | |||
/** Helper function to create a log file in the correct place for this platform. | |||
The method might return nullptr if the file can't be created for some reason. | |||
@param logFileSubDirectoryName the name of the subdirectory to create inside the logs folder (as | |||
returned by getSystemLogFileFolder). It's best to use something | |||
like the name of your application here. | |||
@param logFileName the name of the file to create, e.g. "MyAppLog.txt". | |||
@param welcomeMessage a message that will be written to the log when it's opened. | |||
@param maxInitialFileSizeBytes (see the FileLogger constructor for more info on this) | |||
*/ | |||
static FileLogger* createDefaultAppLogger (const String& logFileSubDirectoryName, | |||
const String& logFileName, | |||
const String& welcomeMessage, | |||
const int64 maxInitialFileSizeBytes = 128 * 1024); | |||
/** Helper function to create a log file in the correct place for this platform. | |||
The filename used is based on the root and suffix strings provided, along with a | |||
time and date string, meaning that a new, empty log file will be always be created | |||
rather than appending to an exising one. | |||
The method might return nullptr if the file can't be created for some reason. | |||
@param logFileSubDirectoryName the name of the subdirectory to create inside the logs folder (as | |||
returned by getSystemLogFileFolder). It's best to use something | |||
like the name of your application here. | |||
@param logFileNameRoot the start of the filename to use, e.g. "MyAppLog_". This will have | |||
a timestamp and the logFileNameSuffix appended to it | |||
@param logFileNameSuffix the file suffix to use, e.g. ".txt" | |||
@param welcomeMessage a message that will be written to the log when it's opened. | |||
*/ | |||
static FileLogger* createDateStampedLogger (const String& logFileSubDirectoryName, | |||
const String& logFileNameRoot, | |||
const String& logFileNameSuffix, | |||
const String& welcomeMessage); | |||
//============================================================================== | |||
/** Returns an OS-specific folder where log-files should be stored. | |||
On Windows this will return a logger with a path such as: | |||
c:\\Documents and Settings\\username\\Application Data\\[logFileSubDirectoryName]\\[logFileName] | |||
On the Mac it'll create something like: | |||
~/Library/Logs/[logFileSubDirectoryName]/[logFileName] | |||
@see createDefaultAppLogger | |||
*/ | |||
static File getSystemLogFileFolder(); | |||
// (implementation of the Logger virtual method) | |||
void logMessage (const String&); | |||
//============================================================================== | |||
/** This is a utility function which removes lines from the start of a text | |||
file to make sure that its total size is below the given size. | |||
*/ | |||
static void trimFileSize (const File& file, int64 maxFileSize); | |||
private: | |||
//============================================================================== | |||
File logFile; | |||
CriticalSection logLock; | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FileLogger) | |||
}; | |||
} // namespace juce |
@@ -1,62 +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. | |||
============================================================================== | |||
*/ | |||
namespace juce | |||
{ | |||
Logger::Logger() {} | |||
Logger::~Logger() | |||
{ | |||
// You're deleting this logger while it's still being used! | |||
// Always call Logger::setCurrentLogger (nullptr) before deleting the active logger. | |||
jassert (currentLogger != this); | |||
} | |||
Logger* Logger::currentLogger = nullptr; | |||
void Logger::setCurrentLogger (Logger* const newLogger) noexcept { currentLogger = newLogger; } | |||
Logger* Logger::getCurrentLogger() noexcept { return currentLogger; } | |||
void Logger::writeToLog (const String& message) | |||
{ | |||
if (currentLogger != nullptr) | |||
currentLogger->logMessage (message); | |||
else | |||
outputDebugString (message); | |||
} | |||
#if JUCE_LOG_ASSERTIONS || JUCE_DEBUG | |||
void JUCE_API JUCE_CALLTYPE logAssertion (const char* const filename, const int lineNum) noexcept | |||
{ | |||
String m ("JUCE Assertion failure in "); | |||
m << File::createFileWithoutCheckingPath (filename).getFileName() << ':' << lineNum; | |||
#if JUCE_LOG_ASSERTIONS | |||
Logger::writeToLog (m); | |||
#else | |||
DBG (m); | |||
#endif | |||
} | |||
#endif | |||
} // namespace juce |
@@ -1,89 +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. | |||
============================================================================== | |||
*/ | |||
namespace juce | |||
{ | |||
//============================================================================== | |||
/** | |||
Acts as an application-wide logging class. | |||
A subclass of Logger can be created and passed into the Logger::setCurrentLogger | |||
method and this will then be used by all calls to writeToLog. | |||
The logger class also contains methods for writing messages to the debugger's | |||
output stream. | |||
@see FileLogger | |||
*/ | |||
class JUCE_API Logger | |||
{ | |||
public: | |||
//============================================================================== | |||
/** Destructor. */ | |||
virtual ~Logger(); | |||
//============================================================================== | |||
/** Sets the current logging class to use. | |||
Note that the object passed in will not be owned or deleted by the logger, so | |||
the caller must make sure that it is not deleted while still being used. | |||
A null pointer can be passed-in to reset the system to the default logger. | |||
*/ | |||
static void JUCE_CALLTYPE setCurrentLogger (Logger* newLogger) noexcept; | |||
/** Returns the current logger, or nullptr if no custom logger has been set. */ | |||
static Logger* JUCE_CALLTYPE getCurrentLogger() noexcept; | |||
/** Writes a string to the current logger. | |||
This will pass the string to the logger's logMessage() method if a logger | |||
has been set. | |||
@see logMessage | |||
*/ | |||
static void JUCE_CALLTYPE writeToLog (const String& message); | |||
//============================================================================== | |||
/** Writes a message to the standard error stream. | |||
This can be called directly, or by using the DBG() macro in | |||
juce_PlatformDefs.h (which will avoid calling the method in non-debug builds). | |||
*/ | |||
static void JUCE_CALLTYPE outputDebugString (const String& text); | |||
protected: | |||
//============================================================================== | |||
Logger(); | |||
/** This is overloaded by subclasses to implement custom logging behaviour. | |||
@see setCurrentLogger | |||
*/ | |||
virtual void logMessage (const String& message) = 0; | |||
private: | |||
static Logger* currentLogger; | |||
}; | |||
} // namespace juce |
@@ -1,340 +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. | |||
============================================================================== | |||
*/ | |||
namespace juce | |||
{ | |||
//============================================================================== | |||
/** | |||
An arbitrarily large integer class. | |||
A BigInteger can be used in a similar way to a normal integer, but has no size | |||
limit (except for memory and performance constraints). | |||
Negative values are possible, but the value isn't stored as 2s-complement, so | |||
be careful if you use negative values and look at the values of individual bits. | |||
*/ | |||
class JUCE_API BigInteger | |||
{ | |||
public: | |||
//============================================================================== | |||
/** Creates an empty BigInteger */ | |||
BigInteger(); | |||
/** Creates a BigInteger containing an integer value in its low bits. | |||
The low 32 bits of the number are initialised with this value. | |||
*/ | |||
BigInteger (uint32 value); | |||
/** Creates a BigInteger containing an integer value in its low bits. | |||
The low 32 bits of the number are initialised with the absolute value | |||
passed in, and its sign is set to reflect the sign of the number. | |||
*/ | |||
BigInteger (int32 value); | |||
/** Creates a BigInteger containing an integer value in its low bits. | |||
The low 64 bits of the number are initialised with the absolute value | |||
passed in, and its sign is set to reflect the sign of the number. | |||
*/ | |||
BigInteger (int64 value); | |||
/** Creates a copy of another BigInteger. */ | |||
BigInteger (const BigInteger&); | |||
/** Move constructor */ | |||
BigInteger (BigInteger&&) noexcept; | |||
/** Move assignment operator */ | |||
BigInteger& operator= (BigInteger&&) noexcept; | |||
/** Destructor. */ | |||
~BigInteger(); | |||
//============================================================================== | |||
/** Copies another BigInteger onto this one. */ | |||
BigInteger& operator= (const BigInteger&); | |||
/** Swaps the internal contents of this with another object. */ | |||
void swapWith (BigInteger&) noexcept; | |||
//============================================================================== | |||
/** Returns the value of a specified bit in the number. | |||
If the index is out-of-range, the result will be false. | |||
*/ | |||
bool operator[] (int bit) const noexcept; | |||
/** Returns true if no bits are set. */ | |||
bool isZero() const noexcept; | |||
/** Returns true if the value is 1. */ | |||
bool isOne() const noexcept; | |||
/** Attempts to get the lowest 32 bits of the value as an integer. | |||
If the value is bigger than the integer limits, this will return only the lower bits. | |||
*/ | |||
int toInteger() const noexcept; | |||
/** Attempts to get the lowest 64 bits of the value as an integer. | |||
If the value is bigger than the integer limits, this will return only the lower bits. | |||
*/ | |||
int64 toInt64() const noexcept; | |||
//============================================================================== | |||
/** Resets the value to 0. */ | |||
void clear() noexcept; | |||
/** Clears a particular bit in the number. */ | |||
void clearBit (int bitNumber) noexcept; | |||
/** Sets a specified bit to 1. */ | |||
void setBit (int bitNumber); | |||
/** Sets or clears a specified bit. */ | |||
void setBit (int bitNumber, bool shouldBeSet); | |||
/** Sets a range of bits to be either on or off. | |||
@param startBit the first bit to change | |||
@param numBits the number of bits to change | |||
@param shouldBeSet whether to turn these bits on or off | |||
*/ | |||
void setRange (int startBit, int numBits, bool shouldBeSet); | |||
/** Inserts a bit an a given position, shifting up any bits above it. */ | |||
void insertBit (int bitNumber, bool shouldBeSet); | |||
/** Returns a range of bits as a new BigInteger. | |||
e.g. getBitRangeAsInt (0, 64) would return the lowest 64 bits. | |||
@see getBitRangeAsInt | |||
*/ | |||
BigInteger getBitRange (int startBit, int numBits) const; | |||
/** Returns a range of bits as an integer value. | |||
e.g. getBitRangeAsInt (0, 32) would return the lowest 32 bits. | |||
Asking for more than 32 bits isn't allowed (obviously) - for that, use | |||
getBitRange(). | |||
*/ | |||
uint32 getBitRangeAsInt (int startBit, int numBits) const noexcept; | |||
/** Sets a range of bits to an integer value. | |||
Copies the given integer onto a range of bits, starting at startBit, | |||
and using up to numBits of the available bits. | |||
*/ | |||
void setBitRangeAsInt (int startBit, int numBits, uint32 valueToSet); | |||
/** Shifts a section of bits left or right. | |||
@param howManyBitsLeft how far to move the bits (+ve numbers shift it left, -ve numbers shift it right). | |||
@param startBit the first bit to affect - if this is > 0, only bits above that index will be affected. | |||
*/ | |||
void shiftBits (int howManyBitsLeft, int startBit); | |||
/** Returns the total number of set bits in the value. */ | |||
int countNumberOfSetBits() const noexcept; | |||
/** Looks for the index of the next set bit after a given starting point. | |||
This searches from startIndex (inclusive) upwards for the first set bit, | |||
and returns its index. If no set bits are found, it returns -1. | |||
*/ | |||
int findNextSetBit (int startIndex) const noexcept; | |||
/** Looks for the index of the next clear bit after a given starting point. | |||
This searches from startIndex (inclusive) upwards for the first clear bit, | |||
and returns its index. | |||
*/ | |||
int findNextClearBit (int startIndex) const noexcept; | |||
/** Returns the index of the highest set bit in the number. | |||
If the value is zero, this will return -1. | |||
*/ | |||
int getHighestBit() const noexcept; | |||
//============================================================================== | |||
/** Returns true if the value is less than zero. | |||
@see setNegative, negate | |||
*/ | |||
bool isNegative() const noexcept; | |||
/** Changes the sign of the number to be positive or negative. | |||
@see isNegative, negate | |||
*/ | |||
void setNegative (bool shouldBeNegative) noexcept; | |||
/** Inverts the sign of the number. | |||
@see isNegative, setNegative | |||
*/ | |||
void negate() noexcept; | |||
//============================================================================== | |||
// All the standard arithmetic ops... | |||
BigInteger& operator+= (const BigInteger&); | |||
BigInteger& operator-= (const BigInteger&); | |||
BigInteger& operator*= (const BigInteger&); | |||
BigInteger& operator/= (const BigInteger&); | |||
BigInteger& operator|= (const BigInteger&); | |||
BigInteger& operator&= (const BigInteger&); | |||
BigInteger& operator^= (const BigInteger&); | |||
BigInteger& operator%= (const BigInteger&); | |||
BigInteger& operator<<= (int numBitsToShift); | |||
BigInteger& operator>>= (int numBitsToShift); | |||
BigInteger& operator++(); | |||
BigInteger& operator--(); | |||
BigInteger operator++ (int); | |||
BigInteger operator-- (int); | |||
BigInteger operator-() const; | |||
BigInteger operator+ (const BigInteger&) const; | |||
BigInteger operator- (const BigInteger&) const; | |||
BigInteger operator* (const BigInteger&) const; | |||
BigInteger operator/ (const BigInteger&) const; | |||
BigInteger operator| (const BigInteger&) const; | |||
BigInteger operator& (const BigInteger&) const; | |||
BigInteger operator^ (const BigInteger&) const; | |||
BigInteger operator% (const BigInteger&) const; | |||
BigInteger operator<< (int numBitsToShift) const; | |||
BigInteger operator>> (int numBitsToShift) const; | |||
bool operator== (const BigInteger&) const noexcept; | |||
bool operator!= (const BigInteger&) const noexcept; | |||
bool operator< (const BigInteger&) const noexcept; | |||
bool operator<= (const BigInteger&) const noexcept; | |||
bool operator> (const BigInteger&) const noexcept; | |||
bool operator>= (const BigInteger&) const noexcept; | |||
//============================================================================== | |||
/** Does a signed comparison of two BigIntegers. | |||
Return values are: | |||
- 0 if the numbers are the same | |||
- < 0 if this number is smaller than the other | |||
- > 0 if this number is bigger than the other | |||
*/ | |||
int compare (const BigInteger& other) const noexcept; | |||
/** Compares the magnitudes of two BigIntegers, ignoring their signs. | |||
Return values are: | |||
- 0 if the numbers are the same | |||
- < 0 if this number is smaller than the other | |||
- > 0 if this number is bigger than the other | |||
*/ | |||
int compareAbsolute (const BigInteger& other) const noexcept; | |||
//============================================================================== | |||
/** Divides this value by another one and returns the remainder. | |||
This number is divided by other, leaving the quotient in this number, | |||
with the remainder being copied to the other BigInteger passed in. | |||
*/ | |||
void divideBy (const BigInteger& divisor, BigInteger& remainder); | |||
/** Returns the largest value that will divide both this value and the argument. */ | |||
BigInteger findGreatestCommonDivisor (BigInteger other) const; | |||
/** Performs a combined exponent and modulo operation. | |||
This BigInteger's value becomes (this ^ exponent) % modulus. | |||
*/ | |||
void exponentModulo (const BigInteger& exponent, const BigInteger& modulus); | |||
/** Performs an inverse modulo on the value. | |||
i.e. the result is (this ^ -1) mod (modulus). | |||
*/ | |||
void inverseModulo (const BigInteger& modulus); | |||
/** Performs the Montgomery Multiplication with modulo. | |||
This object is left containing the result value: ((this * other) * R1) % modulus. | |||
To get this result, we need modulus, modulusp and k such as R = 2^k, with | |||
modulus * modulusp - R * R1 = GCD(modulus, R) = 1 | |||
*/ | |||
void montgomeryMultiplication (const BigInteger& other, const BigInteger& modulus, | |||
const BigInteger& modulusp, int k); | |||
/** Performs the Extended Euclidean algorithm. | |||
This method will set the xOut and yOut arguments such that (a * xOut) - (b * yOut) = GCD (a, b). | |||
On return, this object is left containing the value of the GCD. | |||
*/ | |||
void extendedEuclidean (const BigInteger& a, const BigInteger& b, | |||
BigInteger& xOut, BigInteger& yOut); | |||
//============================================================================== | |||
/** Converts the number to a string. | |||
Specify a base such as 2 (binary), 8 (octal), 10 (decimal), 16 (hex). | |||
If minimumNumCharacters is greater than 0, the returned string will be | |||
padded with leading zeros to reach at least that length. | |||
*/ | |||
String toString (int base, int minimumNumCharacters = 1) const; | |||
/** Reads the numeric value from a string. | |||
Specify a base such as 2 (binary), 8 (octal), 10 (decimal), 16 (hex). | |||
Any invalid characters will be ignored. | |||
*/ | |||
void parseString (StringRef text, int base); | |||
//============================================================================== | |||
/** Turns the number into a block of binary data. | |||
The data is arranged as little-endian, so the first byte of data is the low 8 bits | |||
of the number, and so on. | |||
@see loadFromMemoryBlock | |||
*/ | |||
MemoryBlock toMemoryBlock() const; | |||
/** Converts a block of raw data into a number. | |||
The data is arranged as little-endian, so the first byte of data is the low 8 bits | |||
of the number, and so on. | |||
@see toMemoryBlock | |||
*/ | |||
void loadFromMemoryBlock (const MemoryBlock& data); | |||
private: | |||
//============================================================================== | |||
enum { numPreallocatedInts = 4 }; | |||
HeapBlock<uint32> heapAllocation; | |||
uint32 preallocated[numPreallocatedInts]; | |||
size_t allocatedSize; | |||
int highestBit; | |||
bool negative; | |||
uint32* getValues() const noexcept; | |||
uint32* ensureSize (size_t); | |||
void shiftLeft (int bits, int startBit); | |||
void shiftRight (int bits, int startBit); | |||
JUCE_LEAK_DETECTOR (BigInteger) | |||
}; | |||
/** Writes a BigInteger to an OutputStream as a UTF8 decimal string. */ | |||
OutputStream& JUCE_CALLTYPE operator<< (OutputStream& stream, const BigInteger& value); | |||
} // namespace juce |
@@ -1,253 +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. | |||
============================================================================== | |||
*/ | |||
namespace juce | |||
{ | |||
//============================================================================== | |||
/** | |||
A class for dynamically evaluating simple numeric expressions. | |||
This class can parse a simple C-style string expression involving floating point | |||
numbers, named symbols and functions. The basic arithmetic operations of +, -, *, / | |||
are supported, as well as parentheses, and any alphanumeric identifiers are | |||
assumed to be named symbols which will be resolved when the expression is | |||
evaluated. | |||
Expressions which use identifiers and functions require a subclass of | |||
Expression::Scope to be supplied when evaluating them, and this object | |||
is expected to be able to resolve the symbol names and perform the functions that | |||
are used. | |||
*/ | |||
class JUCE_API Expression | |||
{ | |||
public: | |||
//============================================================================== | |||
/** Creates a simple expression with a value of 0. */ | |||
Expression(); | |||
/** Destructor. */ | |||
~Expression(); | |||
/** Creates a copy of an expression. */ | |||
Expression (const Expression&); | |||
/** Copies another expression. */ | |||
Expression& operator= (const Expression&); | |||
/** Move constructor */ | |||
Expression (Expression&&) noexcept; | |||
/** Move assignment operator */ | |||
Expression& operator= (Expression&&) noexcept; | |||
/** Creates a simple expression with a specified constant value. */ | |||
explicit Expression (double constant); | |||
/** Attempts to create an expression by parsing a string. | |||
Any errors are returned in the parseError argument provided. | |||
*/ | |||
Expression (const String& stringToParse, String& parseError); | |||
/** Returns a string version of the expression. */ | |||
String toString() const; | |||
/** Returns an expression which is an addition operation of two existing expressions. */ | |||
Expression operator+ (const Expression&) const; | |||
/** Returns an expression which is a subtraction operation of two existing expressions. */ | |||
Expression operator- (const Expression&) const; | |||
/** Returns an expression which is a multiplication operation of two existing expressions. */ | |||
Expression operator* (const Expression&) const; | |||
/** Returns an expression which is a division operation of two existing expressions. */ | |||
Expression operator/ (const Expression&) const; | |||
/** Returns an expression which performs a negation operation on an existing expression. */ | |||
Expression operator-() const; | |||
/** Returns an Expression which is an identifier reference. */ | |||
static Expression symbol (const String& symbol); | |||
/** Returns an Expression which is a function call. */ | |||
static Expression function (const String& functionName, const Array<Expression>& parameters); | |||
/** Returns an Expression which parses a string from a character pointer, and updates the pointer | |||
to indicate where it finished. | |||
The pointer is incremented so that on return, it indicates the character that follows | |||
the end of the expression that was parsed. | |||
If there's a syntax error in parsing, the parseError argument will be set | |||
to a description of the problem. | |||
*/ | |||
static Expression parse (String::CharPointerType& stringToParse, String& parseError); | |||
//============================================================================== | |||
/** When evaluating an Expression object, this class is used to resolve symbols and | |||
perform functions that the expression uses. | |||
*/ | |||
class JUCE_API Scope | |||
{ | |||
public: | |||
Scope(); | |||
virtual ~Scope(); | |||
/** Returns some kind of globally unique ID that identifies this scope. */ | |||
virtual String getScopeUID() const; | |||
/** Returns the value of a symbol. | |||
If the symbol is unknown, this can throw an Expression::EvaluationError exception. | |||
The member value is set to the part of the symbol that followed the dot, if there is | |||
one, e.g. for "foo.bar", symbol = "foo" and member = "bar". | |||
@throws Expression::EvaluationError | |||
*/ | |||
virtual Expression getSymbolValue (const String& symbol) const; | |||
/** Executes a named function. | |||
If the function name is unknown, this can throw an Expression::EvaluationError exception. | |||
@throws Expression::EvaluationError | |||
*/ | |||
virtual double evaluateFunction (const String& functionName, | |||
const double* parameters, int numParameters) const; | |||
/** Used as a callback by the Scope::visitRelativeScope() method. | |||
You should never create an instance of this class yourself, it's used by the | |||
expression evaluation code. | |||
*/ | |||
class Visitor | |||
{ | |||
public: | |||
virtual ~Visitor() {} | |||
virtual void visit (const Scope&) = 0; | |||
}; | |||
/** Creates a Scope object for a named scope, and then calls a visitor | |||
to do some kind of processing with this new scope. | |||
If the name is valid, this method must create a suitable (temporary) Scope | |||
object to represent it, and must call the Visitor::visit() method with this | |||
new scope. | |||
*/ | |||
virtual void visitRelativeScope (const String& scopeName, Visitor& visitor) const; | |||
}; | |||
/** Evaluates this expression, without using a Scope. | |||
Without a Scope, no symbols can be used, and only basic functions such as sin, cos, tan, | |||
min, max are available. | |||
To find out about any errors during evaluation, use the other version of this method which | |||
takes a String parameter. | |||
*/ | |||
double evaluate() const; | |||
/** Evaluates this expression, providing a scope that should be able to evaluate any symbols | |||
or functions that it uses. | |||
To find out about any errors during evaluation, use the other version of this method which | |||
takes a String parameter. | |||
*/ | |||
double evaluate (const Scope& scope) const; | |||
/** Evaluates this expression, providing a scope that should be able to evaluate any symbols | |||
or functions that it uses. | |||
*/ | |||
double evaluate (const Scope& scope, String& evaluationError) const; | |||
/** Attempts to return an expression which is a copy of this one, but with a constant adjusted | |||
to make the expression resolve to a target value. | |||
E.g. if the expression is "x + 10" and x is 5, then asking for a target value of 8 will return | |||
the expression "x + 3". Obviously some expressions can't be reversed in this way, in which | |||
case they might just be adjusted by adding a constant to the original expression. | |||
@throws Expression::EvaluationError | |||
*/ | |||
Expression adjustedToGiveNewResult (double targetValue, const Scope& scope) const; | |||
/** Represents a symbol that is used in an Expression. */ | |||
struct Symbol | |||
{ | |||
Symbol (const String& scopeUID, const String& symbolName); | |||
bool operator== (const Symbol&) const noexcept; | |||
bool operator!= (const Symbol&) const noexcept; | |||
String scopeUID; /**< The unique ID of the Scope that contains this symbol. */ | |||
String symbolName; /**< The name of the symbol. */ | |||
}; | |||
/** Returns a copy of this expression in which all instances of a given symbol have been renamed. */ | |||
Expression withRenamedSymbol (const Symbol& oldSymbol, const String& newName, const Scope& scope) const; | |||
/** Returns true if this expression makes use of the specified symbol. | |||
If a suitable scope is supplied, the search will dereference and recursively check | |||
all symbols, so that it can be determined whether this expression relies on the given | |||
symbol at any level in its evaluation. If the scope parameter is null, this just checks | |||
whether the expression contains any direct references to the symbol. | |||
@throws Expression::EvaluationError | |||
*/ | |||
bool referencesSymbol (const Symbol& symbol, const Scope& scope) const; | |||
/** Returns true if this expression contains any symbols. */ | |||
bool usesAnySymbols() const; | |||
/** Returns a list of all symbols that may be needed to resolve this expression in the given scope. */ | |||
void findReferencedSymbols (Array<Symbol>& results, const Scope& scope) const; | |||
//============================================================================== | |||
/** Expression type. | |||
@see Expression::getType() | |||
*/ | |||
enum Type | |||
{ | |||
constantType, | |||
functionType, | |||
operatorType, | |||
symbolType | |||
}; | |||
/** Returns the type of this expression. */ | |||
Type getType() const noexcept; | |||
/** If this expression is a symbol, function or operator, this returns its identifier. */ | |||
String getSymbolOrFunction() const; | |||
/** Returns the number of inputs to this expression. | |||
@see getInput | |||
*/ | |||
int getNumInputs() const; | |||
/** Retrieves one of the inputs to this expression. | |||
@see getNumInputs | |||
*/ | |||
Expression getInput (int index) const; | |||
private: | |||
//============================================================================== | |||
class Term; | |||
struct Helpers; | |||
friend class Term; | |||
friend struct Helpers; | |||
friend struct ContainerDeletePolicy<Term>; | |||
friend class ReferenceCountedObjectPtr<Term>; | |||
ReferenceCountedObjectPtr<Term> term; | |||
explicit Expression (Term*); | |||
}; | |||
} // namespace juce |
@@ -1,658 +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. | |||
============================================================================== | |||
*/ | |||
namespace juce | |||
{ | |||
//============================================================================== | |||
/* | |||
This file sets up some handy mathematical typdefs and functions. | |||
*/ | |||
//============================================================================== | |||
// Definitions for the int8, int16, int32, int64 and pointer_sized_int types. | |||
/** A platform-independent 8-bit signed integer type. */ | |||
typedef signed char int8; | |||
/** A platform-independent 8-bit unsigned integer type. */ | |||
typedef unsigned char uint8; | |||
/** A platform-independent 16-bit signed integer type. */ | |||
typedef signed short int16; | |||
/** A platform-independent 16-bit unsigned integer type. */ | |||
typedef unsigned short uint16; | |||
/** A platform-independent 32-bit signed integer type. */ | |||
typedef signed int int32; | |||
/** A platform-independent 32-bit unsigned integer type. */ | |||
typedef unsigned int uint32; | |||
#if JUCE_MSVC | |||
/** A platform-independent 64-bit integer type. */ | |||
typedef __int64 int64; | |||
/** A platform-independent 64-bit unsigned integer type. */ | |||
typedef unsigned __int64 uint64; | |||
#else | |||
/** A platform-independent 64-bit integer type. */ | |||
typedef long long int64; | |||
/** A platform-independent 64-bit unsigned integer type. */ | |||
typedef unsigned long long uint64; | |||
#endif | |||
#ifndef DOXYGEN | |||
/** A macro for creating 64-bit literals. | |||
Historically, this was needed to support portability with MSVC6, and is kept here | |||
so that old code will still compile, but nowadays every compiler will support the | |||
LL and ULL suffixes, so you should use those in preference to this macro. | |||
*/ | |||
#define literal64bit(longLiteral) (longLiteral##LL) | |||
#endif | |||
#if JUCE_64BIT | |||
/** A signed integer type that's guaranteed to be large enough to hold a pointer without truncating it. */ | |||
typedef int64 pointer_sized_int; | |||
/** An unsigned integer type that's guaranteed to be large enough to hold a pointer without truncating it. */ | |||
typedef uint64 pointer_sized_uint; | |||
#elif JUCE_MSVC | |||
/** A signed integer type that's guaranteed to be large enough to hold a pointer without truncating it. */ | |||
typedef _W64 int pointer_sized_int; | |||
/** An unsigned integer type that's guaranteed to be large enough to hold a pointer without truncating it. */ | |||
typedef _W64 unsigned int pointer_sized_uint; | |||
#else | |||
/** A signed integer type that's guaranteed to be large enough to hold a pointer without truncating it. */ | |||
typedef int pointer_sized_int; | |||
/** An unsigned integer type that's guaranteed to be large enough to hold a pointer without truncating it. */ | |||
typedef unsigned int pointer_sized_uint; | |||
#endif | |||
#if JUCE_WINDOWS && ! JUCE_MINGW | |||
typedef pointer_sized_int ssize_t; | |||
#endif | |||
//============================================================================== | |||
// Some indispensable min/max functions | |||
/** Returns the larger of two values. */ | |||
template <typename Type> | |||
Type jmax (const Type a, const Type b) { return (a < b) ? b : a; } | |||
/** Returns the larger of three values. */ | |||
template <typename Type> | |||
Type jmax (const Type a, const Type b, const Type c) { return (a < b) ? ((b < c) ? c : b) : ((a < c) ? c : a); } | |||
/** Returns the larger of four values. */ | |||
template <typename Type> | |||
Type jmax (const Type a, const Type b, const Type c, const Type d) { return jmax (a, jmax (b, c, d)); } | |||
/** Returns the smaller of two values. */ | |||
template <typename Type> | |||
Type jmin (const Type a, const Type b) { return (b < a) ? b : a; } | |||
/** Returns the smaller of three values. */ | |||
template <typename Type> | |||
Type jmin (const Type a, const Type b, const Type c) { return (b < a) ? ((c < b) ? c : b) : ((c < a) ? c : a); } | |||
/** Returns the smaller of four values. */ | |||
template <typename Type> | |||
Type jmin (const Type a, const Type b, const Type c, const Type d) { return jmin (a, jmin (b, c, d)); } | |||
/** Remaps a normalised value (between 0 and 1) to a target range. | |||
This effectively returns (targetRangeMin + value0To1 * (targetRangeMax - targetRangeMin)). | |||
*/ | |||
template <typename Type> | |||
Type jmap (Type value0To1, Type targetRangeMin, Type targetRangeMax) | |||
{ | |||
return targetRangeMin + value0To1 * (targetRangeMax - targetRangeMin); | |||
} | |||
/** Remaps a value from a source range to a target range. */ | |||
template <typename Type> | |||
Type jmap (Type sourceValue, Type sourceRangeMin, Type sourceRangeMax, Type targetRangeMin, Type targetRangeMax) | |||
{ | |||
jassert (sourceRangeMax != sourceRangeMin); // mapping from a range of zero will produce NaN! | |||
return targetRangeMin + ((targetRangeMax - targetRangeMin) * (sourceValue - sourceRangeMin)) / (sourceRangeMax - sourceRangeMin); | |||
} | |||
/** Scans an array of values, returning the minimum value that it contains. */ | |||
template <typename Type> | |||
Type findMinimum (const Type* data, int numValues) | |||
{ | |||
if (numValues <= 0) | |||
return Type(); | |||
Type result (*data++); | |||
while (--numValues > 0) // (> 0 rather than >= 0 because we've already taken the first sample) | |||
{ | |||
const Type& v = *data++; | |||
if (v < result) result = v; | |||
} | |||
return result; | |||
} | |||
/** Scans an array of values, returning the maximum value that it contains. */ | |||
template <typename Type> | |||
Type findMaximum (const Type* values, int numValues) | |||
{ | |||
if (numValues <= 0) | |||
return Type(); | |||
Type result (*values++); | |||
while (--numValues > 0) // (> 0 rather than >= 0 because we've already taken the first sample) | |||
{ | |||
const Type& v = *values++; | |||
if (result < v) result = v; | |||
} | |||
return result; | |||
} | |||
/** Scans an array of values, returning the minimum and maximum values that it contains. */ | |||
template <typename Type> | |||
void findMinAndMax (const Type* values, int numValues, Type& lowest, Type& highest) | |||
{ | |||
if (numValues <= 0) | |||
{ | |||
lowest = Type(); | |||
highest = Type(); | |||
} | |||
else | |||
{ | |||
Type mn (*values++); | |||
Type mx (mn); | |||
while (--numValues > 0) // (> 0 rather than >= 0 because we've already taken the first sample) | |||
{ | |||
const Type& v = *values++; | |||
if (mx < v) mx = v; | |||
if (v < mn) mn = v; | |||
} | |||
lowest = mn; | |||
highest = mx; | |||
} | |||
} | |||
//============================================================================== | |||
/** Constrains a value to keep it within a given range. | |||
This will check that the specified value lies between the lower and upper bounds | |||
specified, and if not, will return the nearest value that would be in-range. Effectively, | |||
it's like calling jmax (lowerLimit, jmin (upperLimit, value)). | |||
Note that it expects that lowerLimit <= upperLimit. If this isn't true, | |||
the results will be unpredictable. | |||
@param lowerLimit the minimum value to return | |||
@param upperLimit the maximum value to return | |||
@param valueToConstrain the value to try to return | |||
@returns the closest value to valueToConstrain which lies between lowerLimit | |||
and upperLimit (inclusive) | |||
@see jmin, jmax, jmap | |||
*/ | |||
template <typename Type> | |||
Type jlimit (Type lowerLimit, | |||
Type upperLimit, | |||
Type valueToConstrain) noexcept | |||
{ | |||
jassert (lowerLimit <= upperLimit); // if these are in the wrong order, results are unpredictable.. | |||
return (valueToConstrain < lowerLimit) ? lowerLimit | |||
: ((upperLimit < valueToConstrain) ? upperLimit | |||
: valueToConstrain); | |||
} | |||
/** Returns true if a value is at least zero, and also below a specified upper limit. | |||
This is basically a quicker way to write: | |||
@code valueToTest >= 0 && valueToTest < upperLimit | |||
@endcode | |||
*/ | |||
template <typename Type1, typename Type2> | |||
bool isPositiveAndBelow (Type1 valueToTest, Type2 upperLimit) noexcept | |||
{ | |||
jassert (Type1() <= static_cast<Type1> (upperLimit)); // makes no sense to call this if the upper limit is itself below zero.. | |||
return Type1() <= valueToTest && valueToTest < static_cast<Type1> (upperLimit); | |||
} | |||
template <typename Type> | |||
bool isPositiveAndBelow (int valueToTest, Type upperLimit) noexcept | |||
{ | |||
jassert (upperLimit >= 0); // makes no sense to call this if the upper limit is itself below zero.. | |||
return static_cast<unsigned int> (valueToTest) < static_cast<unsigned int> (upperLimit); | |||
} | |||
/** Returns true if a value is at least zero, and also less than or equal to a specified upper limit. | |||
This is basically a quicker way to write: | |||
@code valueToTest >= 0 && valueToTest <= upperLimit | |||
@endcode | |||
*/ | |||
template <typename Type1, typename Type2> | |||
bool isPositiveAndNotGreaterThan (Type1 valueToTest, Type2 upperLimit) noexcept | |||
{ | |||
jassert (Type1() <= static_cast<Type1> (upperLimit)); // makes no sense to call this if the upper limit is itself below zero.. | |||
return Type1() <= valueToTest && valueToTest <= static_cast<Type1> (upperLimit); | |||
} | |||
template <typename Type> | |||
bool isPositiveAndNotGreaterThan (int valueToTest, Type upperLimit) noexcept | |||
{ | |||
jassert (upperLimit >= 0); // makes no sense to call this if the upper limit is itself below zero.. | |||
return static_cast<unsigned int> (valueToTest) <= static_cast<unsigned int> (upperLimit); | |||
} | |||
//============================================================================== | |||
/** Handy function for avoiding unused variables warning. */ | |||
template <typename Type1> | |||
void ignoreUnused (const Type1&) noexcept {} | |||
template <typename Type1, typename Type2> | |||
void ignoreUnused (const Type1&, const Type2&) noexcept {} | |||
template <typename Type1, typename Type2, typename Type3> | |||
void ignoreUnused (const Type1&, const Type2&, const Type3&) noexcept {} | |||
template <typename Type1, typename Type2, typename Type3, typename Type4> | |||
void ignoreUnused (const Type1&, const Type2&, const Type3&, const Type4&) noexcept {} | |||
/** Handy function for getting the number of elements in a simple const C array. | |||
E.g. | |||
@code | |||
static int myArray[] = { 1, 2, 3 }; | |||
int numElements = numElementsInArray (myArray) // returns 3 | |||
@endcode | |||
*/ | |||
template <typename Type, int N> | |||
int numElementsInArray (Type (&array)[N]) | |||
{ | |||
ignoreUnused (array); | |||
(void) sizeof (0[array]); // This line should cause an error if you pass an object with a user-defined subscript operator | |||
return N; | |||
} | |||
//============================================================================== | |||
// Some useful maths functions that aren't always present with all compilers and build settings. | |||
/** Using juce_hypot is easier than dealing with the different types of hypot function | |||
that are provided by the various platforms and compilers. */ | |||
template <typename Type> | |||
Type juce_hypot (Type a, Type b) noexcept | |||
{ | |||
#if JUCE_MSVC | |||
return static_cast<Type> (_hypot (a, b)); | |||
#else | |||
return static_cast<Type> (hypot (a, b)); | |||
#endif | |||
} | |||
#ifndef DOXYGEN | |||
template <> | |||
inline float juce_hypot (float a, float b) noexcept | |||
{ | |||
#if JUCE_MSVC | |||
return _hypotf (a, b); | |||
#else | |||
return hypotf (a, b); | |||
#endif | |||
} | |||
#endif | |||
/** 64-bit abs function. */ | |||
inline int64 abs64 (const int64 n) noexcept | |||
{ | |||
return (n >= 0) ? n : -n; | |||
} | |||
#if JUCE_MSVC && ! defined (DOXYGEN) // The MSVC libraries omit these functions for some reason... | |||
template<typename Type> Type asinh (Type x) { return std::log (x + std::sqrt (x * x + (Type) 1)); } | |||
template<typename Type> Type acosh (Type x) { return std::log (x + std::sqrt (x * x - (Type) 1)); } | |||
template<typename Type> Type atanh (Type x) { return (std::log (x + (Type) 1) - std::log (((Type) 1) - x)) / (Type) 2; } | |||
#endif | |||
//============================================================================== | |||
/** Commonly used mathematical constants */ | |||
template <typename FloatType> | |||
struct MathConstants | |||
{ | |||
/** A predefined value for Pi */ | |||
static const FloatType pi; | |||
/** A predfined value for Euler's number */ | |||
static const FloatType euler; | |||
}; | |||
template <typename FloatType> | |||
const FloatType MathConstants<FloatType>::pi = static_cast<FloatType> (3.141592653589793238L); | |||
template <typename FloatType> | |||
const FloatType MathConstants<FloatType>::euler = static_cast<FloatType> (2.71828182845904523536L); | |||
/** A predefined value for Pi, at double-precision. | |||
@see float_Pi | |||
*/ | |||
const double double_Pi = MathConstants<double>::pi; | |||
/** A predefined value for Pi, at single-precision. | |||
@see double_Pi | |||
*/ | |||
const float float_Pi = MathConstants<float>::pi; | |||
/** Converts an angle in degrees to radians. */ | |||
inline float degreesToRadians (float degrees) noexcept { return degrees * (float_Pi / 180.0f); } | |||
/** Converts an angle in degrees to radians. */ | |||
inline double degreesToRadians (double degrees) noexcept { return degrees * (double_Pi / 180.0); } | |||
/** Converts an angle in radians to degrees. */ | |||
inline float radiansToDegrees (float radians) noexcept { return radians * (180.0f / float_Pi); } | |||
/** Converts an angle in radians to degrees. */ | |||
inline double radiansToDegrees (double radians) noexcept { return radians * (180.0 / double_Pi); } | |||
//============================================================================== | |||
/** The isfinite() method seems to vary between platforms, so this is a | |||
platform-independent function for it. | |||
*/ | |||
template <typename NumericType> | |||
bool juce_isfinite (NumericType) noexcept | |||
{ | |||
return true; // Integer types are always finite | |||
} | |||
template <> | |||
inline bool juce_isfinite (float value) noexcept | |||
{ | |||
#if JUCE_WINDOWS && ! JUCE_MINGW | |||
return _finite (value) != 0; | |||
#else | |||
return std::isfinite (value); | |||
#endif | |||
} | |||
template <> | |||
inline bool juce_isfinite (double value) noexcept | |||
{ | |||
#if JUCE_WINDOWS && ! JUCE_MINGW | |||
return _finite (value) != 0; | |||
#else | |||
return std::isfinite (value); | |||
#endif | |||
} | |||
//============================================================================== | |||
#if JUCE_MSVC | |||
#pragma optimize ("t", off) | |||
#ifndef __INTEL_COMPILER | |||
#pragma float_control (precise, on, push) | |||
#endif | |||
#endif | |||
/** Fast floating-point-to-integer conversion. | |||
This is faster than using the normal c++ cast to convert a float to an int, and | |||
it will round the value to the nearest integer, rather than rounding it down | |||
like the normal cast does. | |||
Note that this routine gets its speed at the expense of some accuracy, and when | |||
rounding values whose floating point component is exactly 0.5, odd numbers and | |||
even numbers will be rounded up or down differently. | |||
*/ | |||
template <typename FloatType> | |||
int roundToInt (const FloatType value) noexcept | |||
{ | |||
#ifdef __INTEL_COMPILER | |||
#pragma float_control (precise, on, push) | |||
#endif | |||
union { int asInt[2]; double asDouble; } n; | |||
n.asDouble = ((double) value) + 6755399441055744.0; | |||
#if JUCE_BIG_ENDIAN | |||
return n.asInt [1]; | |||
#else | |||
return n.asInt [0]; | |||
#endif | |||
} | |||
inline int roundToInt (int value) noexcept | |||
{ | |||
return value; | |||
} | |||
#if JUCE_MSVC | |||
#ifndef __INTEL_COMPILER | |||
#pragma float_control (pop) | |||
#endif | |||
#pragma optimize ("", on) // resets optimisations to the project defaults | |||
#endif | |||
/** Fast floating-point-to-integer conversion. | |||
This is a slightly slower and slightly more accurate version of roundDoubleToInt(). It works | |||
fine for values above zero, but negative numbers are rounded the wrong way. | |||
*/ | |||
inline int roundToIntAccurate (double value) noexcept | |||
{ | |||
#ifdef __INTEL_COMPILER | |||
#pragma float_control (pop) | |||
#endif | |||
return roundToInt (value + 1.5e-8); | |||
} | |||
/** Fast floating-point-to-integer conversion. | |||
This is faster than using the normal c++ cast to convert a double to an int, and | |||
it will round the value to the nearest integer, rather than rounding it down | |||
like the normal cast does. | |||
Note that this routine gets its speed at the expense of some accuracy, and when | |||
rounding values whose floating point component is exactly 0.5, odd numbers and | |||
even numbers will be rounded up or down differently. For a more accurate conversion, | |||
see roundDoubleToIntAccurate(). | |||
*/ | |||
inline int roundDoubleToInt (double value) noexcept | |||
{ | |||
return roundToInt (value); | |||
} | |||
/** Fast floating-point-to-integer conversion. | |||
This is faster than using the normal c++ cast to convert a float to an int, and | |||
it will round the value to the nearest integer, rather than rounding it down | |||
like the normal cast does. | |||
Note that this routine gets its speed at the expense of some accuracy, and when | |||
rounding values whose floating point component is exactly 0.5, odd numbers and | |||
even numbers will be rounded up or down differently. | |||
*/ | |||
inline int roundFloatToInt (float value) noexcept | |||
{ | |||
return roundToInt (value); | |||
} | |||
//============================================================================== | |||
/** Truncates a positive floating-point number to an unsigned int. | |||
This is generally faster than static_cast<unsigned int> (std::floor (x)) | |||
but it only works for positive numbers small enough to be represented as an | |||
unsigned int. | |||
*/ | |||
template <typename FloatType> | |||
unsigned int truncatePositiveToUnsignedInt (FloatType value) noexcept | |||
{ | |||
jassert (value >= static_cast<FloatType> (0)); | |||
jassert (static_cast<FloatType> (value) <= std::numeric_limits<unsigned int>::max()); | |||
return static_cast<unsigned int> (value); | |||
} | |||
//============================================================================== | |||
/** Returns true if the specified integer is a power-of-two. */ | |||
template <typename IntegerType> | |||
bool isPowerOfTwo (IntegerType value) | |||
{ | |||
return (value & (value - 1)) == 0; | |||
} | |||
/** Returns the smallest power-of-two which is equal to or greater than the given integer. */ | |||
inline int nextPowerOfTwo (int n) noexcept | |||
{ | |||
--n; | |||
n |= (n >> 1); | |||
n |= (n >> 2); | |||
n |= (n >> 4); | |||
n |= (n >> 8); | |||
n |= (n >> 16); | |||
return n + 1; | |||
} | |||
/** Returns the index of the highest set bit in a (non-zero) number. | |||
So for n=3 this would return 1, for n=7 it returns 2, etc. | |||
An input value of 0 is illegal! | |||
*/ | |||
int findHighestSetBit (uint32 n) noexcept; | |||
/** Returns the number of bits in a 32-bit integer. */ | |||
inline int countNumberOfBits (uint32 n) noexcept | |||
{ | |||
n -= ((n >> 1) & 0x55555555); | |||
n = (((n >> 2) & 0x33333333) + (n & 0x33333333)); | |||
n = (((n >> 4) + n) & 0x0f0f0f0f); | |||
n += (n >> 8); | |||
n += (n >> 16); | |||
return (int) (n & 0x3f); | |||
} | |||
/** Returns the number of bits in a 64-bit integer. */ | |||
inline int countNumberOfBits (uint64 n) noexcept | |||
{ | |||
return countNumberOfBits ((uint32) n) + countNumberOfBits ((uint32) (n >> 32)); | |||
} | |||
/** Performs a modulo operation, but can cope with the dividend being negative. | |||
The divisor must be greater than zero. | |||
*/ | |||
template <typename IntegerType> | |||
IntegerType negativeAwareModulo (IntegerType dividend, const IntegerType divisor) noexcept | |||
{ | |||
jassert (divisor > 0); | |||
dividend %= divisor; | |||
return (dividend < 0) ? (dividend + divisor) : dividend; | |||
} | |||
/** Returns the square of its argument. */ | |||
template <typename NumericType> | |||
NumericType square (NumericType n) noexcept | |||
{ | |||
return n * n; | |||
} | |||
//============================================================================== | |||
/** Writes a number of bits into a memory buffer at a given bit index. | |||
The buffer is treated as a sequence of 8-bit bytes, and the value is encoded in little-endian order, | |||
so for example if startBit = 10, and numBits = 11 then the lower 6 bits of the value would be written | |||
into bits 2-8 of targetBuffer[1], and the upper 5 bits of value into bits 0-5 of targetBuffer[2]. | |||
@see readLittleEndianBitsInBuffer | |||
*/ | |||
void writeLittleEndianBitsInBuffer (void* targetBuffer, uint32 startBit, uint32 numBits, uint32 value) noexcept; | |||
/** Reads a number of bits from a buffer at a given bit index. | |||
The buffer is treated as a sequence of 8-bit bytes, and the value is encoded in little-endian order, | |||
so for example if startBit = 10, and numBits = 11 then the lower 6 bits of the result would be read | |||
from bits 2-8 of sourceBuffer[1], and the upper 5 bits of the result from bits 0-5 of sourceBuffer[2]. | |||
@see writeLittleEndianBitsInBuffer | |||
*/ | |||
uint32 readLittleEndianBitsInBuffer (const void* sourceBuffer, uint32 startBit, uint32 numBits) noexcept; | |||
//============================================================================== | |||
#if JUCE_INTEL || defined (DOXYGEN) | |||
/** This macro can be applied to a float variable to check whether it contains a denormalised | |||
value, and to normalise it if necessary. | |||
On CPUs that aren't vulnerable to denormalisation problems, this will have no effect. | |||
*/ | |||
#define JUCE_UNDENORMALISE(x) { (x) += 0.1f; (x) -= 0.1f; } | |||
#else | |||
#define JUCE_UNDENORMALISE(x) | |||
#endif | |||
//============================================================================== | |||
/** This namespace contains a few template classes for helping work out class type variations. | |||
*/ | |||
namespace TypeHelpers | |||
{ | |||
/** The ParameterType struct is used to find the best type to use when passing some kind | |||
of object as a parameter. | |||
Of course, this is only likely to be useful in certain esoteric template situations. | |||
E.g. "myFunction (typename TypeHelpers::ParameterType<int>::type, typename TypeHelpers::ParameterType<MyObject>::type)" | |||
would evaluate to "myfunction (int, const MyObject&)", keeping any primitive types as | |||
pass-by-value, but passing objects as a const reference, to avoid copying. | |||
*/ | |||
template <typename Type> struct ParameterType { typedef const Type& type; }; | |||
#if ! DOXYGEN | |||
template <typename Type> struct ParameterType <Type&> { typedef Type& type; }; | |||
template <typename Type> struct ParameterType <Type*> { typedef Type* type; }; | |||
template <> struct ParameterType <char> { typedef char type; }; | |||
template <> struct ParameterType <unsigned char> { typedef unsigned char type; }; | |||
template <> struct ParameterType <short> { typedef short type; }; | |||
template <> struct ParameterType <unsigned short> { typedef unsigned short type; }; | |||
template <> struct ParameterType <int> { typedef int type; }; | |||
template <> struct ParameterType <unsigned int> { typedef unsigned int type; }; | |||
template <> struct ParameterType <long> { typedef long type; }; | |||
template <> struct ParameterType <unsigned long> { typedef unsigned long type; }; | |||
template <> struct ParameterType <int64> { typedef int64 type; }; | |||
template <> struct ParameterType <uint64> { typedef uint64 type; }; | |||
template <> struct ParameterType <bool> { typedef bool type; }; | |||
template <> struct ParameterType <float> { typedef float type; }; | |||
template <> struct ParameterType <double> { typedef double type; }; | |||
#endif | |||
/** These templates are designed to take a type, and if it's a double, they return a double | |||
type; for anything else, they return a float type. | |||
*/ | |||
template <typename Type> struct SmallestFloatType { typedef float type; }; | |||
template <> struct SmallestFloatType <double> { typedef double type; }; | |||
/** These templates are designed to take an integer type, and return an unsigned int | |||
version with the same size. | |||
*/ | |||
template <int bytes> struct UnsignedTypeWithSize {}; | |||
template <> struct UnsignedTypeWithSize<1> { typedef uint8 type; }; | |||
template <> struct UnsignedTypeWithSize<2> { typedef uint16 type; }; | |||
template <> struct UnsignedTypeWithSize<4> { typedef uint32 type; }; | |||
template <> struct UnsignedTypeWithSize<8> { typedef uint64 type; }; | |||
} | |||
} // namespace juce |
@@ -1,270 +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. | |||
============================================================================== | |||
*/ | |||
namespace juce | |||
{ | |||
//============================================================================== | |||
/** | |||
Represents a mapping between an arbitrary range of values and a | |||
normalised 0->1 range. | |||
The properties of the mapping also include an optional snapping interval | |||
and skew-factor. | |||
@see Range | |||
*/ | |||
template <typename ValueType> | |||
class NormalisableRange | |||
{ | |||
public: | |||
/** Creates a continuous range that performs a dummy mapping. */ | |||
NormalisableRange() noexcept | |||
: start(), end (1), interval(), | |||
skew (static_cast<ValueType> (1)), symmetricSkew (false) | |||
{} | |||
/** Creates a copy of another range. */ | |||
NormalisableRange (const NormalisableRange& other) noexcept | |||
: start (other.start), end (other.end), | |||
interval (other.interval), skew (other.skew), | |||
symmetricSkew (other.symmetricSkew), | |||
convertFrom0To1Function (other.convertFrom0To1Function), | |||
convertTo0To1Function (other.convertTo0To1Function), | |||
snapToLegalValueFunction (other.snapToLegalValueFunction) | |||
{ | |||
checkInvariants(); | |||
} | |||
/** Creates a copy of another range. */ | |||
NormalisableRange& operator= (const NormalisableRange& other) noexcept | |||
{ | |||
start = other.start; | |||
end = other.end; | |||
interval = other.interval; | |||
skew = other.skew; | |||
symmetricSkew = other.symmetricSkew; | |||
convertFrom0To1Function = other.convertFrom0To1Function; | |||
convertTo0To1Function = other.convertTo0To1Function; | |||
snapToLegalValueFunction = other.snapToLegalValueFunction; | |||
checkInvariants(); | |||
return *this; | |||
} | |||
/** Creates a NormalisableRange with a given range, interval and skew factor. */ | |||
NormalisableRange (ValueType rangeStart, | |||
ValueType rangeEnd, | |||
ValueType intervalValue, | |||
ValueType skewFactor, | |||
bool useSymmetricSkew = false) noexcept | |||
: start (rangeStart), end (rangeEnd), interval (intervalValue), | |||
skew (skewFactor), symmetricSkew (useSymmetricSkew) | |||
{ | |||
checkInvariants(); | |||
} | |||
/** Creates a NormalisableRange with a given range and interval, but a dummy skew-factor. */ | |||
NormalisableRange (ValueType rangeStart, | |||
ValueType rangeEnd, | |||
ValueType intervalValue) noexcept | |||
: start (rangeStart), end (rangeEnd), interval (intervalValue), | |||
skew (static_cast<ValueType> (1)), symmetricSkew (false) | |||
{ | |||
checkInvariants(); | |||
} | |||
/** Creates a NormalisableRange with a given range, continuous interval, but a dummy skew-factor. */ | |||
NormalisableRange (ValueType rangeStart, | |||
ValueType rangeEnd) noexcept | |||
: start (rangeStart), end (rangeEnd), interval(), | |||
skew (static_cast<ValueType> (1)), symmetricSkew (false) | |||
{ | |||
checkInvariants(); | |||
} | |||
/** Creates a NormalisableRange with a given range and an injective mapping function. | |||
@param rangeStart The minimum value in the range. | |||
@param rangeEnd The maximum value in the range. | |||
@param convertFrom0To1Func A function which uses the current start and end of this NormalisableRange | |||
and produces a mapped value from a normalised value. | |||
@param convertTo0To1Func A function which uses the current start and end of this NormalisableRange | |||
and produces a normalised value from a mapped value. | |||
@param snapToLegalValueFunc A function which uses the current start and end of this NormalisableRange | |||
to take a mapped value and snap it to the nearest legal value. | |||
*/ | |||
NormalisableRange (ValueType rangeStart, | |||
ValueType rangeEnd, | |||
std::function<ValueType (ValueType currentRangeStart, ValueType currentRangeEnd, ValueType normalisedValue)> convertFrom0To1Func, | |||
std::function<ValueType (ValueType currentRangeStart, ValueType currentRangeEnd, ValueType mappedValue)> convertTo0To1Func, | |||
std::function<ValueType (ValueType currentRangeStart, ValueType currentRangeEnd, ValueType valueToSnap)> snapToLegalValueFunc = nullptr) noexcept | |||
: start (rangeStart), | |||
end (rangeEnd), | |||
interval(), | |||
skew (static_cast<ValueType> (1)), | |||
symmetricSkew (false), | |||
convertFrom0To1Function (convertFrom0To1Func), | |||
convertTo0To1Function (convertTo0To1Func), | |||
snapToLegalValueFunction (snapToLegalValueFunc) | |||
{ | |||
checkInvariants(); | |||
} | |||
/** Uses the properties of this mapping to convert a non-normalised value to | |||
its 0->1 representation. | |||
*/ | |||
ValueType convertTo0to1 (ValueType v) const noexcept | |||
{ | |||
if (convertTo0To1Function != nullptr) | |||
return convertTo0To1Function (start, end, v); | |||
ValueType proportion = (v - start) / (end - start); | |||
if (skew == static_cast<ValueType> (1)) | |||
return proportion; | |||
if (! symmetricSkew) | |||
return std::pow (proportion, skew); | |||
ValueType distanceFromMiddle = static_cast<ValueType> (2) * proportion - static_cast<ValueType> (1); | |||
return (static_cast<ValueType> (1) + std::pow (std::abs (distanceFromMiddle), skew) | |||
* (distanceFromMiddle < static_cast<ValueType> (0) ? static_cast<ValueType> (-1) | |||
: static_cast<ValueType> (1))) | |||
/ static_cast<ValueType> (2); | |||
} | |||
/** Uses the properties of this mapping to convert a normalised 0->1 value to | |||
its full-range representation. | |||
*/ | |||
ValueType convertFrom0to1 (ValueType proportion) const noexcept | |||
{ | |||
if (convertFrom0To1Function != nullptr) | |||
return convertFrom0To1Function (start, end, proportion); | |||
if (! symmetricSkew) | |||
{ | |||
if (skew != static_cast<ValueType> (1) && proportion > ValueType()) | |||
proportion = std::exp (std::log (proportion) / skew); | |||
return start + (end - start) * proportion; | |||
} | |||
ValueType distanceFromMiddle = static_cast<ValueType> (2) * proportion - static_cast<ValueType> (1); | |||
if (skew != static_cast<ValueType> (1) && distanceFromMiddle != static_cast<ValueType> (0)) | |||
distanceFromMiddle = std::exp (std::log (std::abs (distanceFromMiddle)) / skew) | |||
* (distanceFromMiddle < static_cast<ValueType> (0) ? static_cast<ValueType> (-1) | |||
: static_cast<ValueType> (1)); | |||
return start + (end - start) / static_cast<ValueType> (2) * (static_cast<ValueType> (1) + distanceFromMiddle); | |||
} | |||
/** Takes a non-normalised value and snaps it based on either the interval property of | |||
this NormalisedRange or the lambda function supplied to the constructor. | |||
*/ | |||
ValueType snapToLegalValue (ValueType v) const noexcept | |||
{ | |||
if (snapToLegalValueFunction != nullptr) | |||
return snapToLegalValueFunction (start, end, v); | |||
if (interval > ValueType()) | |||
v = start + interval * std::floor ((v - start) / interval + static_cast<ValueType> (0.5)); | |||
if (v <= start || end <= start) | |||
return start; | |||
if (v >= end) | |||
return end; | |||
return v; | |||
} | |||
/** Returns the extent of the normalisable range. */ | |||
Range<ValueType> getRange() const noexcept { return Range<ValueType> (start, end); } | |||
/** Given a value which is between the start and end points, this sets the skew | |||
such that convertFrom0to1 (0.5) will return this value. | |||
If you have used lambda functions for convertFrom0to1Func and convertFrom0to1Func in the | |||
constructor of this class then the skew value is ignored. | |||
@param centrePointValue this must be greater than the start of the range and less than the end. | |||
*/ | |||
void setSkewForCentre (ValueType centrePointValue) noexcept | |||
{ | |||
jassert (centrePointValue > start); | |||
jassert (centrePointValue < end); | |||
symmetricSkew = false; | |||
skew = std::log (static_cast<ValueType> (0.5)) | |||
/ std::log ((centrePointValue - start) / (end - start)); | |||
checkInvariants(); | |||
} | |||
/** The minimum value of the non-normalised range. */ | |||
ValueType start; | |||
/** The maximum value of the non-normalised range. */ | |||
ValueType end; | |||
/** The snapping interval that should be used (for a non-normalised value). Use 0 for a | |||
continuous range. | |||
If you have used a lambda function for snapToLegalValueFunction in the constructor of | |||
this class then the interval is ignored. | |||
*/ | |||
ValueType interval; | |||
/** An optional skew factor that alters the way values are distribute across the range. | |||
The skew factor lets you skew the mapping logarithmically so that larger or smaller | |||
values are given a larger proportion of the available space. | |||
A factor of 1.0 has no skewing effect at all. If the factor is < 1.0, the lower end | |||
of the range will fill more of the slider's length; if the factor is > 1.0, the upper | |||
end of the range will be expanded. | |||
If you have used lambda functions for convertFrom0to1Func and convertFrom0to1Func in the | |||
constructor of this class then the skew value is ignored. | |||
*/ | |||
ValueType skew; | |||
/** If true, the skew factor applies from the middle of the slider to each of its ends. */ | |||
bool symmetricSkew; | |||
private: | |||
void checkInvariants() const | |||
{ | |||
jassert (end > start); | |||
jassert (interval >= ValueType()); | |||
jassert (skew > ValueType()); | |||
} | |||
std::function<ValueType (ValueType, ValueType, ValueType)> convertFrom0To1Function = nullptr, | |||
convertTo0To1Function = nullptr, | |||
snapToLegalValueFunction = nullptr; | |||
}; | |||
} // namespace juce |
@@ -1,188 +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. | |||
============================================================================== | |||
*/ | |||
namespace juce | |||
{ | |||
Random::Random (int64 seedValue) noexcept : seed (seedValue) | |||
{ | |||
} | |||
Random::Random() : seed (1) | |||
{ | |||
setSeedRandomly(); | |||
} | |||
Random::~Random() noexcept | |||
{ | |||
} | |||
void Random::setSeed (const int64 newSeed) noexcept | |||
{ | |||
seed = newSeed; | |||
} | |||
void Random::combineSeed (const int64 seedValue) noexcept | |||
{ | |||
seed ^= nextInt64() ^ seedValue; | |||
} | |||
void Random::setSeedRandomly() | |||
{ | |||
static int64 globalSeed = 0; | |||
combineSeed (globalSeed ^ (int64) (pointer_sized_int) this); | |||
combineSeed (Time::getMillisecondCounter()); | |||
combineSeed (Time::getHighResolutionTicks()); | |||
combineSeed (Time::getHighResolutionTicksPerSecond()); | |||
combineSeed (Time::currentTimeMillis()); | |||
globalSeed ^= seed; | |||
} | |||
Random& Random::getSystemRandom() noexcept | |||
{ | |||
static Random sysRand; | |||
return sysRand; | |||
} | |||
//============================================================================== | |||
int Random::nextInt() noexcept | |||
{ | |||
seed = (int64) (((((uint64) seed) * 0x5deece66dLL) + 11) & 0xffffffffffffLL); | |||
return (int) (seed >> 16); | |||
} | |||
int Random::nextInt (const int maxValue) noexcept | |||
{ | |||
jassert (maxValue > 0); | |||
return (int) ((((unsigned int) nextInt()) * (uint64) maxValue) >> 32); | |||
} | |||
int Random::nextInt (Range<int> range) noexcept | |||
{ | |||
return range.getStart() + nextInt (range.getLength()); | |||
} | |||
int64 Random::nextInt64() noexcept | |||
{ | |||
return (int64) ((((uint64) (unsigned int) nextInt()) << 32) | (uint64) (unsigned int) nextInt()); | |||
} | |||
bool Random::nextBool() noexcept | |||
{ | |||
return (nextInt() & 0x40000000) != 0; | |||
} | |||
float Random::nextFloat() noexcept | |||
{ | |||
return static_cast<uint32> (nextInt()) / (std::numeric_limits<uint32>::max() + 1.0f); | |||
} | |||
double Random::nextDouble() noexcept | |||
{ | |||
return static_cast<uint32> (nextInt()) / (std::numeric_limits<uint32>::max() + 1.0); | |||
} | |||
BigInteger Random::nextLargeNumber (const BigInteger& maximumValue) | |||
{ | |||
BigInteger n; | |||
do | |||
{ | |||
fillBitsRandomly (n, 0, maximumValue.getHighestBit() + 1); | |||
} | |||
while (n >= maximumValue); | |||
return n; | |||
} | |||
void Random::fillBitsRandomly (void* const buffer, size_t bytes) | |||
{ | |||
int* d = static_cast<int*> (buffer); | |||
for (; bytes >= sizeof (int); bytes -= sizeof (int)) | |||
*d++ = nextInt(); | |||
if (bytes > 0) | |||
{ | |||
const int lastBytes = nextInt(); | |||
memcpy (d, &lastBytes, bytes); | |||
} | |||
} | |||
void Random::fillBitsRandomly (BigInteger& arrayToChange, int startBit, int numBits) | |||
{ | |||
arrayToChange.setBit (startBit + numBits - 1, true); // to force the array to pre-allocate space | |||
while ((startBit & 31) != 0 && numBits > 0) | |||
{ | |||
arrayToChange.setBit (startBit++, nextBool()); | |||
--numBits; | |||
} | |||
while (numBits >= 32) | |||
{ | |||
arrayToChange.setBitRangeAsInt (startBit, 32, (unsigned int) nextInt()); | |||
startBit += 32; | |||
numBits -= 32; | |||
} | |||
while (--numBits >= 0) | |||
arrayToChange.setBit (startBit + numBits, nextBool()); | |||
} | |||
//============================================================================== | |||
#if JUCE_UNIT_TESTS | |||
class RandomTests : public UnitTest | |||
{ | |||
public: | |||
RandomTests() : UnitTest ("Random", "Maths") {} | |||
void runTest() override | |||
{ | |||
beginTest ("Random"); | |||
Random r = getRandom(); | |||
for (int i = 2000; --i >= 0;) | |||
{ | |||
expect (r.nextDouble() >= 0.0 && r.nextDouble() < 1.0); | |||
expect (r.nextFloat() >= 0.0f && r.nextFloat() < 1.0f); | |||
expect (r.nextInt (5) >= 0 && r.nextInt (5) < 5); | |||
expect (r.nextInt (1) == 0); | |||
int n = r.nextInt (50) + 1; | |||
expect (r.nextInt (n) >= 0 && r.nextInt (n) < n); | |||
n = r.nextInt (0x7ffffffe) + 1; | |||
expect (r.nextInt (n) >= 0 && r.nextInt (n) < n); | |||
} | |||
} | |||
}; | |||
static RandomTests randomTests; | |||
#endif | |||
} // namespace juce |
@@ -1,135 +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. | |||
============================================================================== | |||
*/ | |||
namespace juce | |||
{ | |||
//============================================================================== | |||
/** | |||
A random number generator. | |||
You can create a Random object and use it to generate a sequence of random numbers. | |||
*/ | |||
class JUCE_API Random | |||
{ | |||
public: | |||
//============================================================================== | |||
/** Creates a Random object based on a seed value. | |||
For a given seed value, the subsequent numbers generated by this object | |||
will be predictable, so a good idea is to set this value based | |||
on the time, e.g. | |||
new Random (Time::currentTimeMillis()) | |||
*/ | |||
explicit Random (int64 seedValue) noexcept; | |||
/** Creates a Random object using a random seed value. | |||
Internally, this calls setSeedRandomly() to randomise the seed. | |||
*/ | |||
Random(); | |||
/** Destructor. */ | |||
~Random() noexcept; | |||
/** Returns the next random 32 bit integer. | |||
@returns a random integer from the full range 0x80000000 to 0x7fffffff | |||
*/ | |||
int nextInt() noexcept; | |||
/** Returns the next random number, limited to a given range. | |||
The maxValue parameter may not be negative, or zero. | |||
@returns a random integer between 0 (inclusive) and maxValue (exclusive). | |||
*/ | |||
int nextInt (int maxValue) noexcept; | |||
/** Returns the next random number, limited to a given range. | |||
@returns a random integer between the range start (inclusive) and its end (exclusive). | |||
*/ | |||
int nextInt (Range<int> range) noexcept; | |||
/** Returns the next 64-bit random number. | |||
@returns a random integer from the full range 0x8000000000000000 to 0x7fffffffffffffff | |||
*/ | |||
int64 nextInt64() noexcept; | |||
/** Returns the next random floating-point number. | |||
@returns a random value in the range 0 (inclusive) to 1.0 (exclusive) | |||
*/ | |||
float nextFloat() noexcept; | |||
/** Returns the next random floating-point number. | |||
@returns a random value in the range 0 (inclusive) to 1.0 (exclusive) | |||
*/ | |||
double nextDouble() noexcept; | |||
/** Returns the next random boolean value. */ | |||
bool nextBool() noexcept; | |||
/** Returns a BigInteger containing a random number. | |||
@returns a random value in the range 0 to (maximumValue - 1). | |||
*/ | |||
BigInteger nextLargeNumber (const BigInteger& maximumValue); | |||
/** Fills a block of memory with random values. */ | |||
void fillBitsRandomly (void* bufferToFill, size_t sizeInBytes); | |||
/** Sets a range of bits in a BigInteger to random values. */ | |||
void fillBitsRandomly (BigInteger& arrayToChange, int startBit, int numBits); | |||
//============================================================================== | |||
/** Resets this Random object to a given seed value. */ | |||
void setSeed (int64 newSeed) noexcept; | |||
/** Returns the RNG's current seed. */ | |||
int64 getSeed() const noexcept { return seed; } | |||
/** Merges this object's seed with another value. | |||
This sets the seed to be a value created by combining the current seed and this | |||
new value. | |||
*/ | |||
void combineSeed (int64 seedValue) noexcept; | |||
/** Reseeds this generator using a value generated from various semi-random system | |||
properties like the current time, etc. | |||
Because this function convolves the time with the last seed value, calling | |||
it repeatedly will increase the randomness of the final result. | |||
*/ | |||
void setSeedRandomly(); | |||
/** The overhead of creating a new Random object is fairly small, but if you want to avoid | |||
it, you can call this method to get a global shared Random object. | |||
It's not thread-safe though, so threads should use their own Random object, otherwise | |||
you run the risk of your random numbers becoming.. erm.. randomly corrupted.. | |||
*/ | |||
static Random& getSystemRandom() noexcept; | |||
private: | |||
//============================================================================== | |||
int64 seed; | |||
JUCE_LEAK_DETECTOR (Random) | |||
}; | |||
} // namespace juce |
@@ -1,305 +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. | |||
============================================================================== | |||
*/ | |||
namespace juce | |||
{ | |||
//============================================================================== | |||
/** A general-purpose range object, that simply represents any linear range with | |||
a start and end point. | |||
Note that when checking whether values fall within the range, the start value is | |||
considered to be inclusive, and the end of the range exclusive. | |||
The templated parameter is expected to be a primitive integer or floating point | |||
type, though class types could also be used if they behave in a number-like way. | |||
*/ | |||
template <typename ValueType> | |||
class Range | |||
{ | |||
public: | |||
//============================================================================== | |||
/** Constructs an empty range. */ | |||
Range() noexcept : start(), end() | |||
{ | |||
} | |||
/** Constructs a range with given start and end values. */ | |||
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) | |||
{ | |||
} | |||
/** Copies another range object. */ | |||
Range& operator= (Range other) noexcept | |||
{ | |||
start = other.start; | |||
end = other.end; | |||
return *this; | |||
} | |||
/** Returns the range that lies between two positions (in either order). */ | |||
static Range between (const ValueType position1, const ValueType position2) noexcept | |||
{ | |||
return position1 < position2 ? Range (position1, position2) | |||
: Range (position2, position1); | |||
} | |||
/** Returns a range with a given start and length. */ | |||
static Range withStartAndLength (const ValueType startValue, const ValueType length) noexcept | |||
{ | |||
jassert (length >= ValueType()); | |||
return Range (startValue, startValue + length); | |||
} | |||
/** Returns a range with the specified start position and a length of zero. */ | |||
static Range emptyRange (const ValueType start) noexcept | |||
{ | |||
return Range (start, start); | |||
} | |||
//============================================================================== | |||
/** Returns the start of the range. */ | |||
inline ValueType getStart() const noexcept { return start; } | |||
/** Returns the length of the range. */ | |||
inline ValueType getLength() const noexcept { return end - start; } | |||
/** Returns the end of the range. */ | |||
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; } | |||
//============================================================================== | |||
/** Changes the start position of the range, leaving the end position unchanged. | |||
If the new start position is higher than the current end of the range, the end point | |||
will be pushed along to equal it, leaving an empty range at the new position. | |||
*/ | |||
void setStart (const ValueType newStart) noexcept | |||
{ | |||
start = newStart; | |||
if (end < newStart) | |||
end = newStart; | |||
} | |||
/** Returns a range with the same end as this one, but a different start. | |||
If the new start position is higher than the current end of the range, the end point | |||
will be pushed along to equal it, returning an empty range at the new position. | |||
*/ | |||
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 | |||
{ | |||
return Range (newStart, end + (newStart - start)); | |||
} | |||
/** Changes the end position of the range, leaving the start unchanged. | |||
If the new end position is below the current start of the range, the start point | |||
will be pushed back to equal the new end point. | |||
*/ | |||
void setEnd (const ValueType newEnd) noexcept | |||
{ | |||
end = newEnd; | |||
if (newEnd < start) | |||
start = newEnd; | |||
} | |||
/** Returns a range with the same start position as this one, but a different end. | |||
If the new end position is below the current start of the range, the start point | |||
will be pushed back to equal the new end point. | |||
*/ | |||
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 | |||
{ | |||
return Range (start + (newEnd - end), newEnd); | |||
} | |||
/** Changes the length of the range. | |||
Lengths less than zero are treated as zero. | |||
*/ | |||
void setLength (const ValueType newLength) noexcept | |||
{ | |||
end = start + jmax (ValueType(), newLength); | |||
} | |||
/** Returns a range with the same start as this one, but a different length. | |||
Lengths less than zero are treated as zero. | |||
*/ | |||
Range withLength (const ValueType newLength) const noexcept | |||
{ | |||
return Range (start, start + newLength); | |||
} | |||
/** Returns a range which has its start moved down and its end moved up by the | |||
given amount. | |||
@returns The returned range will be (start - amount, end + amount) | |||
*/ | |||
Range expanded (ValueType amount) const noexcept | |||
{ | |||
return Range (start - amount, end + amount); | |||
} | |||
//============================================================================== | |||
/** Adds an amount to the start and end of the range. */ | |||
inline Range operator+= (const ValueType amountToAdd) noexcept | |||
{ | |||
start += amountToAdd; | |||
end += amountToAdd; | |||
return *this; | |||
} | |||
/** Subtracts an amount from the start and end of the range. */ | |||
inline Range operator-= (const ValueType amountToSubtract) noexcept | |||
{ | |||
start -= amountToSubtract; | |||
end -= amountToSubtract; | |||
return *this; | |||
} | |||
/** Returns a range that is equal to this one with an amount added to its | |||
start and end. | |||
*/ | |||
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 | |||
{ | |||
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; } | |||
//============================================================================== | |||
/** 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 | |||
{ | |||
return start <= position && position < end; | |||
} | |||
/** Returns the nearest value to the one supplied, which lies within the range. */ | |||
ValueType clipValue (const ValueType value) const noexcept | |||
{ | |||
return jlimit (start, end, value); | |||
} | |||
/** Returns true if the given range lies entirely inside this range. */ | |||
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 | |||
{ | |||
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 | |||
{ | |||
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 | |||
{ | |||
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 | |||
{ | |||
return Range (jmin (valueToInclude, start), | |||
jmax (valueToInclude, end)); | |||
} | |||
/** Returns a given range, after moving it forwards or backwards to fit it | |||
within this range. | |||
If the supplied range has a greater length than this one, the return value | |||
will be this range. | |||
Otherwise, if the supplied range is smaller than this one, the return value | |||
will be the new range, shifted forwards or backwards so that it doesn't extend | |||
beyond this one, but keeping its original length. | |||
*/ | |||
Range constrainRange (Range rangeToConstrain) const noexcept | |||
{ | |||
const ValueType otherLen = rangeToConstrain.getLength(); | |||
return getLength() <= otherLen | |||
? *this | |||
: rangeToConstrain.movedToStartAt (jlimit (start, end - otherLen, rangeToConstrain.getStart())); | |||
} | |||
/** Scans an array of values for its min and max, and returns these as a Range. */ | |||
static Range findMinAndMax (const ValueType* values, int numValues) noexcept | |||
{ | |||
if (numValues <= 0) | |||
return Range(); | |||
const ValueType first (*values++); | |||
Range r (first, first); | |||
while (--numValues > 0) // (> 0 rather than >= 0 because we've already taken the first sample) | |||
{ | |||
const ValueType v (*values++); | |||
if (r.end < v) r.end = v; | |||
if (v < r.start) r.start = v; | |||
} | |||
return r; | |||
} | |||
private: | |||
//============================================================================== | |||
ValueType start, end; | |||
}; | |||
} // namespace juce |
@@ -1,137 +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. | |||
============================================================================== | |||
*/ | |||
namespace juce | |||
{ | |||
//============================================================================== | |||
/** | |||
A class that measures various statistics about a series of floating point | |||
values that it is given. | |||
*/ | |||
template <typename FloatType> | |||
class StatisticsAccumulator | |||
{ | |||
public: | |||
//============================================================================== | |||
/** Constructs a new StatisticsAccumulator. */ | |||
StatisticsAccumulator() noexcept | |||
: count (0), | |||
minimum ( std::numeric_limits<FloatType>::infinity()), | |||
maximum (-std::numeric_limits<FloatType>::infinity()) | |||
{} | |||
//============================================================================== | |||
/** Add a new value to the accumulator. | |||
This will update all running statistics accordingly. | |||
*/ | |||
void addValue (FloatType v) noexcept | |||
{ | |||
jassert (juce_isfinite (v)); | |||
sum += v; | |||
sumSquares += v * v; | |||
++count; | |||
if (v > maximum) maximum = v; | |||
if (v < minimum) minimum = v; | |||
} | |||
/** Reset the accumulator. | |||
This will reset all currently saved statistcs. | |||
*/ | |||
void reset() noexcept { *this = StatisticsAccumulator<FloatType>(); } | |||
//============================================================================== | |||
/** Returns the average (arithmetic mean) of all previously added values. | |||
If no values have been added yet, this will return zero. | |||
*/ | |||
FloatType getAverage() const noexcept | |||
{ | |||
return count > 0 ? sum / (FloatType) count | |||
: FloatType(); | |||
} | |||
/** Returns the variance of all previously added values. | |||
If no values have been added yet, this will return zero. | |||
*/ | |||
FloatType getVariance() const noexcept | |||
{ | |||
return count > 0 ? (sumSquares - sum * sum / (FloatType) count) / (FloatType) count | |||
: FloatType(); | |||
} | |||
/** Returns the standard deviation of all previously added values. | |||
If no values have been added yet, this will return zero. | |||
*/ | |||
FloatType getStandardDeviation() const noexcept | |||
{ | |||
return std::sqrt (getVariance()); | |||
} | |||
/** Returns the smallest of all previously added values. | |||
If no values have been added yet, this will return positive infinity. | |||
*/ | |||
FloatType getMinValue() const noexcept | |||
{ | |||
return minimum; | |||
} | |||
/** Returns the largest of all previously added values. | |||
If no values have been added yet, this will return negative infinity. | |||
*/ | |||
FloatType getMaxValue() const noexcept | |||
{ | |||
return maximum; | |||
} | |||
/** Returns how many values have been added to this accumulator. */ | |||
size_t getCount() const noexcept | |||
{ | |||
return count; | |||
} | |||
private: | |||
//============================================================================== | |||
struct KahanSum | |||
{ | |||
KahanSum() noexcept : sum(), error() {} | |||
operator FloatType() const noexcept { return sum; } | |||
void JUCE_NO_ASSOCIATIVE_MATH_OPTIMISATIONS operator+= (FloatType value) noexcept | |||
{ | |||
FloatType correctedValue = value - error; | |||
FloatType newSum = sum + correctedValue; | |||
error = (newSum - sum) - correctedValue; | |||
sum = newSum; | |||
} | |||
FloatType sum, error; | |||
}; | |||
//============================================================================== | |||
size_t count; | |||
KahanSum sum, sumSquares; | |||
FloatType minimum, maximum; | |||
}; | |||
} // namespace juce |
@@ -1,477 +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. | |||
============================================================================== | |||
*/ | |||
namespace juce | |||
{ | |||
#ifndef DOXYGEN | |||
namespace AtomicHelpers | |||
{ | |||
template <typename T> struct DiffTypeHelper { typedef T Type; }; | |||
template <typename T> struct DiffTypeHelper<T*> { typedef std::ptrdiff_t Type; }; | |||
} | |||
#endif | |||
#if JUCE_ATOMIC_AVAILABLE | |||
//============================================================================== | |||
/** | |||
A simple wrapper around std::atomic. | |||
*/ | |||
template <typename Type> | |||
struct Atomic | |||
{ | |||
typedef typename AtomicHelpers::DiffTypeHelper<Type>::Type DiffType; | |||
/** Creates a new value, initialised to zero. */ | |||
Atomic() noexcept : value (0) {} | |||
/** Creates a new value, with a given initial value. */ | |||
Atomic (Type initialValue) noexcept : value (initialValue) {} | |||
/** Copies another value (atomically). */ | |||
Atomic (const Atomic& other) noexcept : value (other.get()) {} | |||
/** Destructor. */ | |||
~Atomic() noexcept | |||
{ | |||
#if __cpp_lib_atomic_is_always_lock_free | |||
static_assert (std::atomic<Type>::is_always_lock_free, | |||
"This class can only be used for lock-free types"); | |||
#endif | |||
} | |||
/** Atomically reads and returns the current value. */ | |||
Type get() const noexcept { return value.load(); } | |||
/** Atomically sets the current value. */ | |||
void set (Type newValue) noexcept { value = newValue; } | |||
/** Atomically sets the current value, returning the value that was replaced. */ | |||
Type exchange (Type newValue) noexcept { return value.exchange (newValue); } | |||
/** Atomically compares this value with a target value, and if it is equal, sets | |||
this to be equal to a new value. | |||
This operation is the atomic equivalent of doing this: | |||
@code | |||
bool compareAndSetBool (Type newValue, Type valueToCompare) | |||
{ | |||
if (get() == valueToCompare) | |||
{ | |||
set (newValue); | |||
return true; | |||
} | |||
return false; | |||
} | |||
@endcode | |||
Internally, this method calls std::atomic::compare_exchange_strong with | |||
memory_order_seq_cst (the strictest std::memory_order). | |||
@returns true if the comparison was true and the value was replaced; false if | |||
the comparison failed and the value was left unchanged. | |||
@see compareAndSetValue | |||
*/ | |||
bool compareAndSetBool (Type newValue, Type valueToCompare) noexcept | |||
{ | |||
return value.compare_exchange_strong (valueToCompare, newValue); | |||
} | |||
/** Copies another value into this one (atomically). */ | |||
Atomic<Type>& operator= (const Atomic& other) noexcept | |||
{ | |||
value = other.value.load(); | |||
return *this; | |||
} | |||
/** Copies another value into this one (atomically). */ | |||
Atomic<Type>& operator= (Type newValue) noexcept | |||
{ | |||
value = newValue; | |||
return *this; | |||
} | |||
/** Atomically adds a number to this value, returning the new value. */ | |||
Type operator+= (DiffType amountToAdd) noexcept { return value += amountToAdd; } | |||
/** Atomically subtracts a number from this value, returning the new value. */ | |||
Type operator-= (DiffType amountToSubtract) noexcept { return value -= amountToSubtract; } | |||
/** Atomically increments this value, returning the new value. */ | |||
Type operator++() noexcept { return ++value; } | |||
/** Atomically decrements this value, returning the new value. */ | |||
Type operator--() noexcept { return --value; } | |||
/** Implements a memory read/write barrier. | |||
Internally this calls std::atomic_thread_fence with | |||
memory_order_seq_cst (the strictest std::memory_order). | |||
*/ | |||
void memoryBarrier() noexcept { atomic_thread_fence (std::memory_order_seq_cst); } | |||
/** The std::atomic object that this class operates on. */ | |||
std::atomic<Type> value; | |||
//============================================================================== | |||
#ifndef DOXYGEN | |||
// This method has been deprecated as there is no equivalent method in std::atomic. | |||
JUCE_DEPRECATED (Type compareAndSetValue (Type, Type) noexcept); | |||
#endif | |||
}; | |||
#else | |||
#ifndef DOXYGEN | |||
template <typename Type> class AtomicBase; | |||
#endif | |||
//============================================================================== | |||
/** | |||
Simple class to hold a primitive value and perform atomic operations on it. | |||
The type used must be a 32 or 64 bit primitive, like an int, pointer, etc. | |||
There are methods to perform most of the basic atomic operations. | |||
*/ | |||
template <typename Type> | |||
class Atomic : public AtomicBase<Type> | |||
{ | |||
public: | |||
/** Resulting type when subtracting the underlying Type. */ | |||
typedef typename AtomicBase<Type>::DiffType DiffType; | |||
/** Creates a new value, initialised to zero. */ | |||
inline Atomic() noexcept {} | |||
/** Creates a new value, with a given initial value. */ | |||
inline explicit Atomic (const Type initialValue) noexcept : AtomicBase<Type> (initialValue) {} | |||
/** Copies another value (atomically). */ | |||
inline Atomic (const Atomic& other) noexcept : AtomicBase<Type> (other) {} | |||
/** Destructor. */ | |||
inline ~Atomic() noexcept | |||
{ | |||
static_assert (sizeof (Type) == 4 || sizeof (Type) == 8, | |||
"Atomic can only be used for types which are 32 or 64 bits in size"); | |||
} | |||
/** Atomically reads and returns the current value. */ | |||
inline Type get() const noexcept { return AtomicBase<Type>::get(); } | |||
/** Copies another value into this one (atomically). */ | |||
inline Atomic& operator= (const Atomic& other) noexcept { AtomicBase<Type>::operator= (other); return *this; } | |||
/** Copies another value into this one (atomically). */ | |||
inline Atomic& operator= (const Type newValue) noexcept { AtomicBase<Type>::operator= (newValue); return *this; } | |||
/** Atomically sets the current value. */ | |||
inline void set (Type newValue) noexcept { exchange (newValue); } | |||
/** Atomically sets the current value, returning the value that was replaced. */ | |||
inline Type exchange (Type v) noexcept { return AtomicBase<Type>::exchange (v); } | |||
/** Atomically adds a number to this value, returning the new value. */ | |||
Type operator+= (DiffType amountToAdd) noexcept; | |||
/** Atomically subtracts a number from this value, returning the new value. */ | |||
Type operator-= (DiffType amountToSubtract) noexcept; | |||
/** Atomically increments this value, returning the new value. */ | |||
Type operator++() noexcept; | |||
/** Atomically decrements this value, returning the new value. */ | |||
Type operator--() noexcept; | |||
/** Atomically compares this value with a target value, and if it is equal, sets | |||
this to be equal to a new value. | |||
This operation is the atomic equivalent of doing this: | |||
@code | |||
bool compareAndSetBool (Type newValue, Type valueToCompare) | |||
{ | |||
if (get() == valueToCompare) | |||
{ | |||
set (newValue); | |||
return true; | |||
} | |||
return false; | |||
} | |||
@endcode | |||
@returns true if the comparison was true and the value was replaced; false if | |||
the comparison failed and the value was left unchanged. | |||
@see compareAndSetValue | |||
*/ | |||
inline bool compareAndSetBool (Type newValue, Type valueToCompare) noexcept { return AtomicBase<Type>::compareAndSetBool (newValue, valueToCompare); } | |||
/** Atomically compares this value with a target value, and if it is equal, sets | |||
this to be equal to a new value. | |||
This operation is the atomic equivalent of doing this: | |||
@code | |||
Type compareAndSetValue (Type newValue, Type valueToCompare) | |||
{ | |||
Type oldValue = get(); | |||
if (oldValue == valueToCompare) | |||
set (newValue); | |||
return oldValue; | |||
} | |||
@endcode | |||
@returns the old value before it was changed. | |||
@see compareAndSetBool | |||
*/ | |||
inline Type compareAndSetValue (Type newValue, Type valueToCompare) noexcept { return AtomicBase<Type>::compareAndSetValue (newValue, valueToCompare); } | |||
/** Implements a memory read/write barrier. */ | |||
static inline void memoryBarrier() noexcept { AtomicBase<Type>::memoryBarrier (); } | |||
}; | |||
#ifndef DOXYGEN | |||
//============================================================================== | |||
// Internal implementation follows | |||
//============================================================================== | |||
template <typename Type> | |||
class AtomicBase | |||
{ | |||
public: | |||
typedef typename AtomicHelpers::DiffTypeHelper<Type>::Type DiffType; | |||
inline AtomicBase() noexcept : value (0) {} | |||
inline explicit AtomicBase (const Type v) noexcept : value (v) {} | |||
inline AtomicBase (const AtomicBase& other) noexcept : value (other.get()) {} | |||
Type get() const noexcept; | |||
inline AtomicBase& operator= (const AtomicBase<Type>& other) noexcept { exchange (other.get()); return *this; } | |||
inline AtomicBase& operator= (const Type newValue) noexcept { exchange (newValue); return *this; } | |||
void set (Type newValue) noexcept { exchange (newValue); } | |||
Type exchange (Type) noexcept; | |||
bool compareAndSetBool (Type, Type) noexcept; | |||
Type compareAndSetValue (Type, Type) noexcept; | |||
static void memoryBarrier() noexcept; | |||
//============================================================================== | |||
#if JUCE_64BIT | |||
JUCE_ALIGN (8) | |||
#else | |||
JUCE_ALIGN (4) | |||
#endif | |||
/** The raw value that this class operates on. | |||
This is exposed publicly in case you need to manipulate it directly | |||
for performance reasons. | |||
*/ | |||
volatile Type value; | |||
protected: | |||
template <typename Dest, typename Source> | |||
static inline Dest castTo (Source value) noexcept { union { Dest d; Source s; } u; u.s = value; return u.d; } | |||
static inline Type castFrom32Bit (int32 value) noexcept { return castTo <Type, int32> (value); } | |||
static inline Type castFrom64Bit (int64 value) noexcept { return castTo <Type, int64> (value); } | |||
static inline int32 castTo32Bit (Type value) noexcept { return castTo <int32, Type> (value); } | |||
static inline int64 castTo64Bit (Type value) noexcept { return castTo <int64, Type> (value); } | |||
Type operator++ (int); // better to just use pre-increment with atomics.. | |||
Type operator-- (int); | |||
/** This templated negate function will negate pointers as well as integers */ | |||
template <typename ValueType> | |||
inline ValueType negateValue (ValueType n) noexcept | |||
{ | |||
return sizeof (ValueType) == 1 ? (ValueType) -(signed char) n | |||
: (sizeof (ValueType) == 2 ? (ValueType) -(short) n | |||
: (sizeof (ValueType) == 4 ? (ValueType) -(int) n | |||
: ((ValueType) -(int64) n))); | |||
} | |||
/** This templated negate function will negate pointers as well as integers */ | |||
template <typename PointerType> | |||
inline PointerType* negateValue (PointerType* n) noexcept | |||
{ | |||
return reinterpret_cast<PointerType*> (-reinterpret_cast<pointer_sized_int> (n)); | |||
} | |||
}; | |||
//============================================================================== | |||
// Specialisation for void* which does not include the pointer arithmetic | |||
template <> | |||
class Atomic<void*> : public AtomicBase<void*> | |||
{ | |||
public: | |||
inline Atomic() noexcept {} | |||
inline explicit Atomic (void* const initialValue) noexcept : AtomicBase<void*> (initialValue) {} | |||
inline Atomic (const Atomic<void*>& other) noexcept : AtomicBase<void*> (other) {} | |||
inline void* get() const noexcept { return AtomicBase<void*>::get(); } | |||
inline Atomic& operator= (const Atomic& other) noexcept { AtomicBase<void*>::operator= (other); return *this; } | |||
inline Atomic& operator= (void* const newValue) noexcept { AtomicBase<void*>::operator= (newValue); return *this; } | |||
inline void set (void* newValue) noexcept { exchange (newValue); } | |||
inline void* exchange (void* v) noexcept { return AtomicBase<void*>::exchange (v); } | |||
inline bool compareAndSetBool (void* newValue, void* valueToCompare) noexcept { return AtomicBase<void*>::compareAndSetBool (newValue, valueToCompare); } | |||
inline void* compareAndSetValue (void* newValue, void* valueToCompare) noexcept { return AtomicBase<void*>::compareAndSetValue (newValue, valueToCompare); } | |||
static inline void memoryBarrier() noexcept { AtomicBase<void*>::memoryBarrier(); } | |||
}; | |||
//============================================================================== | |||
/* | |||
The following code is in the header so that the atomics can be inlined where possible... | |||
*/ | |||
#if JUCE_MAC && (__GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 2)) | |||
#define JUCE_ATOMICS_MAC_LEGACY 1 // Older OSX builds using gcc4.1 or earlier | |||
#elif JUCE_GCC || JUCE_CLANG | |||
#define JUCE_ATOMICS_GCC 1 // GCC with intrinsics | |||
#if JUCE_IOS || JUCE_ANDROID // (64-bit ops will compile but not link on these mobile OSes) | |||
#define JUCE_64BIT_ATOMICS_UNAVAILABLE 1 | |||
#endif | |||
#endif | |||
template <typename Type> | |||
struct AtomicIncrementDecrement | |||
{ | |||
static inline Type inc (AtomicBase<Type>& a) noexcept | |||
{ | |||
#if JUCE_ATOMICS_MAC_LEGACY | |||
return sizeof (Type) == 4 ? (Type) OSAtomicIncrement32Barrier ((volatile int32_t*) &a.value) | |||
: (Type) OSAtomicIncrement64Barrier ((volatile int64_t*) &a.value); | |||
#elif JUCE_ATOMICS_GCC | |||
return sizeof (Type) == 4 ? (Type) __sync_add_and_fetch (& (a.value), (Type) 1) | |||
: (Type) __sync_add_and_fetch ((int64_t*) & (a.value), 1); | |||
#endif | |||
} | |||
static inline Type dec (AtomicBase<Type>& a) noexcept | |||
{ | |||
#if JUCE_ATOMICS_MAC_LEGACY | |||
return sizeof (Type) == 4 ? (Type) OSAtomicDecrement32Barrier ((volatile int32_t*) &a.value) | |||
: (Type) OSAtomicDecrement64Barrier ((volatile int64_t*) &a.value); | |||
#elif JUCE_ATOMICS_GCC | |||
return sizeof (Type) == 4 ? (Type) __sync_add_and_fetch (& (a.value), (Type) -1) | |||
: (Type) __sync_add_and_fetch ((int64_t*) & (a.value), -1); | |||
#endif | |||
} | |||
}; | |||
template <typename Type> | |||
struct AtomicIncrementDecrement<Type*> | |||
{ | |||
static inline Type* inc (Atomic<Type*>& a) noexcept { return a.operator+= (1); } | |||
static inline Type* dec (Atomic<Type*>& a) noexcept { return a.operator-= (1); } | |||
}; | |||
//============================================================================== | |||
template <typename Type> | |||
inline Type AtomicBase<Type>::get() const noexcept | |||
{ | |||
#if JUCE_ATOMICS_MAC_LEGACY | |||
return sizeof (Type) == 4 ? castFrom32Bit ((int32) OSAtomicAdd32Barrier ((int32_t) 0, (volatile int32_t*) &value)) | |||
: castFrom64Bit ((int64) OSAtomicAdd64Barrier ((int64_t) 0, (volatile int64_t*) &value)); | |||
#elif JUCE_ATOMICS_GCC | |||
return sizeof (Type) == 4 ? castFrom32Bit ((int32) __sync_add_and_fetch ((volatile int32*) &value, 0)) | |||
: castFrom64Bit ((int64) __sync_add_and_fetch ((volatile int64*) &value, 0)); | |||
#endif | |||
} | |||
template <typename Type> | |||
inline Type AtomicBase<Type>::exchange (const Type newValue) noexcept | |||
{ | |||
#if JUCE_ATOMICS_MAC_LEGACY || JUCE_ATOMICS_GCC | |||
Type currentVal = value; | |||
while (! compareAndSetBool (newValue, currentVal)) { currentVal = value; } | |||
return currentVal; | |||
#endif | |||
} | |||
template <typename Type> | |||
inline Type Atomic<Type>::operator+= (const DiffType amountToAdd) noexcept | |||
{ | |||
Type amount = (Type() + amountToAdd); | |||
#if JUCE_ATOMICS_MAC_LEGACY | |||
return sizeof (Type) == 4 ? (Type) OSAtomicAdd32Barrier ((int32_t) castTo32Bit (amount), (volatile int32_t*) &AtomicBase<Type>::value) | |||
: (Type) OSAtomicAdd64Barrier ((int64_t) amount, (volatile int64_t*) &AtomicBase<Type>::value); | |||
#elif JUCE_ATOMICS_GCC | |||
return (Type) __sync_add_and_fetch (& (AtomicBase<Type>::value), amount); | |||
#endif | |||
} | |||
template <typename Type> | |||
inline Type Atomic<Type>::operator-= (const DiffType amountToSubtract) noexcept | |||
{ | |||
return operator+= (AtomicBase<Type>::negateValue (amountToSubtract)); | |||
} | |||
template <typename Type> | |||
inline Type Atomic<Type>::operator++() noexcept { return AtomicIncrementDecrement<Type>::inc (*this); } | |||
template <typename Type> | |||
inline Type Atomic<Type>::operator--() noexcept { return AtomicIncrementDecrement<Type>::dec (*this); } | |||
template <typename Type> | |||
inline bool AtomicBase<Type>::compareAndSetBool (const Type newValue, const Type valueToCompare) noexcept | |||
{ | |||
#if JUCE_ATOMICS_MAC_LEGACY | |||
return sizeof (Type) == 4 ? OSAtomicCompareAndSwap32Barrier ((int32_t) castTo32Bit (valueToCompare), (int32_t) castTo32Bit (newValue), (volatile int32_t*) &value) | |||
: OSAtomicCompareAndSwap64Barrier ((int64_t) castTo64Bit (valueToCompare), (int64_t) castTo64Bit (newValue), (volatile int64_t*) &value); | |||
#elif JUCE_ATOMICS_GCC | |||
return sizeof (Type) == 4 ? __sync_bool_compare_and_swap ((volatile int32*) &value, castTo32Bit (valueToCompare), castTo32Bit (newValue)) | |||
: __sync_bool_compare_and_swap ((volatile int64*) &value, castTo64Bit (valueToCompare), castTo64Bit (newValue)); | |||
#endif | |||
} | |||
template <typename Type> | |||
inline Type AtomicBase<Type>::compareAndSetValue (const Type newValue, const Type valueToCompare) noexcept | |||
{ | |||
#if JUCE_ATOMICS_MAC_LEGACY | |||
for (;;) // Annoying workaround for only having a bool CAS operation.. | |||
{ | |||
if (compareAndSetBool (newValue, valueToCompare)) | |||
return valueToCompare; | |||
const Type result = value; | |||
if (result != valueToCompare) | |||
return result; | |||
} | |||
#elif JUCE_ATOMICS_GCC | |||
return sizeof (Type) == 4 ? castFrom32Bit ((int32) __sync_val_compare_and_swap ((volatile int32*) &value, castTo32Bit (valueToCompare), castTo32Bit (newValue))) | |||
: castFrom64Bit ((int64) __sync_val_compare_and_swap ((volatile int64*) &value, castTo64Bit (valueToCompare), castTo64Bit (newValue))); | |||
#endif | |||
} | |||
template <typename Type> | |||
inline void AtomicBase<Type>::memoryBarrier() noexcept | |||
{ | |||
#if JUCE_ATOMICS_MAC_LEGACY | |||
OSMemoryBarrier(); | |||
#elif JUCE_ATOMICS_GCC | |||
__sync_synchronize(); | |||
#endif | |||
} | |||
#endif | |||
#endif | |||
} // namespace juce |
@@ -1,237 +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. | |||
============================================================================== | |||
*/ | |||
namespace juce | |||
{ | |||
//============================================================================== | |||
/** Contains static methods for converting the byte order between different | |||
endiannesses. | |||
*/ | |||
class JUCE_API ByteOrder | |||
{ | |||
public: | |||
//============================================================================== | |||
/** Swaps the upper and lower bytes of a 16-bit integer. */ | |||
JUCE_CONSTEXPR static uint16 swap (uint16 value) noexcept; | |||
/** Reverses the order of the 4 bytes in a 32-bit integer. */ | |||
static uint32 swap (uint32 value) noexcept; | |||
/** Reverses the order of the 8 bytes in a 64-bit integer. */ | |||
static uint64 swap (uint64 value) noexcept; | |||
//============================================================================== | |||
/** Swaps the byte order of a 16-bit unsigned int if the CPU is big-endian */ | |||
JUCE_CONSTEXPR static uint16 swapIfBigEndian (uint16 value) noexcept; | |||
/** Swaps the byte order of a 32-bit unsigned int if the CPU is big-endian */ | |||
static uint32 swapIfBigEndian (uint32 value) noexcept; | |||
/** Swaps the byte order of a 64-bit unsigned int if the CPU is big-endian */ | |||
static uint64 swapIfBigEndian (uint64 value) noexcept; | |||
/** Swaps the byte order of a 16-bit signed int if the CPU is big-endian */ | |||
JUCE_CONSTEXPR static int16 swapIfBigEndian (int16 value) noexcept; | |||
/** Swaps the byte order of a 32-bit signed int if the CPU is big-endian */ | |||
static int32 swapIfBigEndian (int32 value) noexcept; | |||
/** Swaps the byte order of a 64-bit signed int if the CPU is big-endian */ | |||
static int64 swapIfBigEndian (int64 value) noexcept; | |||
/** Swaps the byte order of a 32-bit float if the CPU is big-endian */ | |||
static float swapIfBigEndian (float value) noexcept; | |||
/** Swaps the byte order of a 64-bit float if the CPU is big-endian */ | |||
static double swapIfBigEndian (double value) noexcept; | |||
/** Swaps the byte order of a 16-bit unsigned int if the CPU is little-endian */ | |||
JUCE_CONSTEXPR static uint16 swapIfLittleEndian (uint16 value) noexcept; | |||
/** Swaps the byte order of a 32-bit unsigned int if the CPU is little-endian */ | |||
static uint32 swapIfLittleEndian (uint32 value) noexcept; | |||
/** Swaps the byte order of a 64-bit unsigned int if the CPU is little-endian */ | |||
static uint64 swapIfLittleEndian (uint64 value) noexcept; | |||
/** Swaps the byte order of a 16-bit signed int if the CPU is little-endian */ | |||
JUCE_CONSTEXPR static int16 swapIfLittleEndian (int16 value) noexcept; | |||
/** Swaps the byte order of a 32-bit signed int if the CPU is little-endian */ | |||
static int32 swapIfLittleEndian (int32 value) noexcept; | |||
/** Swaps the byte order of a 64-bit signed int if the CPU is little-endian */ | |||
static int64 swapIfLittleEndian (int64 value) noexcept; | |||
/** Swaps the byte order of a 32-bit float if the CPU is little-endian */ | |||
static float swapIfLittleEndian (float value) noexcept; | |||
/** Swaps the byte order of a 64-bit float if the CPU is little-endian */ | |||
static double swapIfLittleEndian (double value) noexcept; | |||
//============================================================================== | |||
/** Turns 4 bytes into a little-endian integer. */ | |||
static uint32 littleEndianInt (const void* bytes) noexcept; | |||
/** Turns 4 characters into a little-endian integer. */ | |||
JUCE_CONSTEXPR static uint32 littleEndianInt (char c1, char c2, char c3, char c4) noexcept; | |||
/** Turns 8 bytes into a little-endian integer. */ | |||
static uint64 littleEndianInt64 (const void* bytes) noexcept; | |||
/** Turns 2 bytes into a little-endian integer. */ | |||
static uint16 littleEndianShort (const void* bytes) noexcept; | |||
/** Turns 4 bytes into a big-endian integer. */ | |||
static uint32 bigEndianInt (const void* bytes) noexcept; | |||
/** Turns 8 bytes into a big-endian integer. */ | |||
static uint64 bigEndianInt64 (const void* bytes) noexcept; | |||
/** Turns 2 bytes into a big-endian integer. */ | |||
static uint16 bigEndianShort (const void* bytes) noexcept; | |||
//============================================================================== | |||
/** Converts 3 little-endian bytes into a signed 24-bit value (which is sign-extended to 32 bits). */ | |||
static int littleEndian24Bit (const void* bytes) noexcept; | |||
/** Converts 3 big-endian bytes into a signed 24-bit value (which is sign-extended to 32 bits). */ | |||
static int bigEndian24Bit (const void* bytes) noexcept; | |||
/** Copies a 24-bit number to 3 little-endian bytes. */ | |||
static void littleEndian24BitToChars (int value, void* destBytes) noexcept; | |||
/** Copies a 24-bit number to 3 big-endian bytes. */ | |||
static void bigEndian24BitToChars (int value, void* destBytes) noexcept; | |||
//============================================================================== | |||
/** Returns true if the current CPU is big-endian. */ | |||
JUCE_CONSTEXPR static bool isBigEndian() noexcept; | |||
private: | |||
ByteOrder() JUCE_DELETED_FUNCTION; | |||
JUCE_DECLARE_NON_COPYABLE (ByteOrder) | |||
}; | |||
//============================================================================== | |||
#if JUCE_MSVC && ! defined (__INTEL_COMPILER) | |||
#pragma intrinsic (_byteswap_ulong) | |||
#endif | |||
JUCE_CONSTEXPR inline uint16 ByteOrder::swap (uint16 n) noexcept | |||
{ | |||
return static_cast<uint16> ((n << 8) | (n >> 8)); | |||
} | |||
inline uint32 ByteOrder::swap (uint32 n) noexcept | |||
{ | |||
#if JUCE_MAC || JUCE_IOS | |||
return OSSwapInt32 (n); | |||
#elif (JUCE_GCC || JUCE_CLANG) && JUCE_INTEL && ! JUCE_NO_INLINE_ASM | |||
asm("bswap %%eax" : "=a"(n) : "a"(n)); | |||
return n; | |||
#elif JUCE_MSVC | |||
return _byteswap_ulong (n); | |||
#elif JUCE_ANDROID | |||
return bswap_32 (n); | |||
#else | |||
return (n << 24) | (n >> 24) | ((n & 0xff00) << 8) | ((n & 0xff0000) >> 8); | |||
#endif | |||
} | |||
inline uint64 ByteOrder::swap (uint64 value) noexcept | |||
{ | |||
#if JUCE_MAC || JUCE_IOS | |||
return OSSwapInt64 (value); | |||
#elif JUCE_MSVC | |||
return _byteswap_uint64 (value); | |||
#else | |||
return (((uint64) swap ((uint32) value)) << 32) | swap ((uint32) (value >> 32)); | |||
#endif | |||
} | |||
#if JUCE_LITTLE_ENDIAN | |||
JUCE_CONSTEXPR inline uint16 ByteOrder::swapIfBigEndian (const uint16 v) noexcept { return v; } | |||
inline uint32 ByteOrder::swapIfBigEndian (const uint32 v) noexcept { return v; } | |||
inline uint64 ByteOrder::swapIfBigEndian (const uint64 v) noexcept { return v; } | |||
JUCE_CONSTEXPR inline int16 ByteOrder::swapIfBigEndian (const int16 v) noexcept { return v; } | |||
inline int32 ByteOrder::swapIfBigEndian (const int32 v) noexcept { return v; } | |||
inline int64 ByteOrder::swapIfBigEndian (const int64 v) noexcept { return v; } | |||
inline float ByteOrder::swapIfBigEndian (const float v) noexcept { return v; } | |||
inline double ByteOrder::swapIfBigEndian (const double v) noexcept { return v; } | |||
JUCE_CONSTEXPR inline uint16 ByteOrder::swapIfLittleEndian (const uint16 v) noexcept { return swap (v); } | |||
inline uint32 ByteOrder::swapIfLittleEndian (const uint32 v) noexcept { return swap (v); } | |||
inline uint64 ByteOrder::swapIfLittleEndian (const uint64 v) noexcept { return swap (v); } | |||
JUCE_CONSTEXPR inline int16 ByteOrder::swapIfLittleEndian (const int16 v) noexcept { return static_cast<int16> (swap (static_cast<uint16> (v))); } | |||
inline int32 ByteOrder::swapIfLittleEndian (const int32 v) noexcept { return static_cast<int32> (swap (static_cast<uint32> (v))); } | |||
inline int64 ByteOrder::swapIfLittleEndian (const int64 v) noexcept { return static_cast<int64> (swap (static_cast<uint64> (v))); } | |||
inline float ByteOrder::swapIfLittleEndian (const float v) noexcept { union { uint32 asUInt; float asFloat; } n; n.asFloat = v; n.asUInt = ByteOrder::swap (n.asUInt); return n.asFloat; } | |||
inline double ByteOrder::swapIfLittleEndian (const double v) noexcept { union { uint64 asUInt; double asFloat; } n; n.asFloat = v; n.asUInt = ByteOrder::swap (n.asUInt); return n.asFloat; } | |||
inline uint32 ByteOrder::littleEndianInt (const void* const bytes) noexcept { return *static_cast<const uint32*> (bytes); } | |||
JUCE_CONSTEXPR inline uint32 ByteOrder::littleEndianInt (char c1, char c2, char c3, char c4) noexcept { return (((uint32) c4) << 24) + (((uint32) c3) << 16) + (((uint32) c2) << 8) + (uint32) c1; } | |||
inline uint64 ByteOrder::littleEndianInt64 (const void* const bytes) noexcept { return *static_cast<const uint64*> (bytes); } | |||
inline uint16 ByteOrder::littleEndianShort (const void* const bytes) noexcept { return *static_cast<const uint16*> (bytes); } | |||
inline uint32 ByteOrder::bigEndianInt (const void* const bytes) noexcept { return swap (*static_cast<const uint32*> (bytes)); } | |||
inline uint64 ByteOrder::bigEndianInt64 (const void* const bytes) noexcept { return swap (*static_cast<const uint64*> (bytes)); } | |||
inline uint16 ByteOrder::bigEndianShort (const void* const bytes) noexcept { return swap (*static_cast<const uint16*> (bytes)); } | |||
JUCE_CONSTEXPR inline bool ByteOrder::isBigEndian() noexcept { return false; } | |||
#else | |||
JUCE_CONSTEXPR inline uint16 ByteOrder::swapIfBigEndian (const uint16 v) noexcept { return swap (v); } | |||
inline uint32 ByteOrder::swapIfBigEndian (const uint32 v) noexcept { return swap (v); } | |||
inline uint64 ByteOrder::swapIfBigEndian (const uint64 v) noexcept { return swap (v); } | |||
JUCE_CONSTEXPR inline int16 ByteOrder::swapIfBigEndian (const int16 v) noexcept { return static_cast<int16> (swap (static_cast<uint16> (v))); } | |||
inline int32 ByteOrder::swapIfBigEndian (const int32 v) noexcept { return static_cast<int16> (swap (static_cast<uint16> (v))); } | |||
inline int64 ByteOrder::swapIfBigEndian (const int64 v) noexcept { return static_cast<int16> (swap (static_cast<uint16> (v))); } | |||
inline float ByteOrder::swapIfBigEndian (const float v) noexcept { union { uint32 asUInt; float asFloat; } n; n.asFloat = v; n.asUInt = ByteOrder::swap (n.asUInt); return n.asFloat; } | |||
inline double ByteOrder::swapIfBigEndian (const double v) noexcept { union { uint64 asUInt; double asFloat; } n; n.asFloat = v; n.asUInt = ByteOrder::swap (n.asUInt); return n.asFloat; } | |||
JUCE_CONSTEXPR inline uint16 ByteOrder::swapIfLittleEndian (const uint16 v) noexcept { return v; } | |||
inline uint32 ByteOrder::swapIfLittleEndian (const uint32 v) noexcept { return v; } | |||
inline uint64 ByteOrder::swapIfLittleEndian (const uint64 v) noexcept { return v; } | |||
JUCE_CONSTEXPR inline int16 ByteOrder::swapIfLittleEndian (const int16 v) noexcept { return v; } | |||
inline int32 ByteOrder::swapIfLittleEndian (const int32 v) noexcept { return v; } | |||
inline int64 ByteOrder::swapIfLittleEndian (const int64 v) noexcept { return v; } | |||
inline float ByteOrder::swapIfLittleEndian (const float v) noexcept { return v; } | |||
inline double ByteOrder::swapIfLittleEndian (const double v) noexcept { return v; } | |||
inline uint32 ByteOrder::littleEndianInt (const void* const bytes) noexcept { return swap (*static_cast<const uint32*> (bytes)); } | |||
JUCE_CONSTEXPR inline uint32 ByteOrder::littleEndianInt (char c1, char c2, char c3, char c4) noexcept { return (((uint32) c1) << 24) + (((uint32) c2) << 16) + (((uint32) c3) << 8) + (uint32) c4; } | |||
inline uint64 ByteOrder::littleEndianInt64 (const void* const bytes) noexcept { return swap (*static_cast<const uint64*> (bytes)); } | |||
inline uint16 ByteOrder::littleEndianShort (const void* const bytes) noexcept { return swap (*static_cast<const uint16*> (bytes)); } | |||
inline uint32 ByteOrder::bigEndianInt (const void* const bytes) noexcept { return *static_cast<const uint32*> (bytes); } | |||
inline uint64 ByteOrder::bigEndianInt64 (const void* const bytes) noexcept { return *static_cast<const uint64*> (bytes); } | |||
inline uint16 ByteOrder::bigEndianShort (const void* const bytes) noexcept { return *static_cast<const uint16*> (bytes); } | |||
JUCE_CONSTEXPR inline bool ByteOrder::isBigEndian() noexcept { return true; } | |||
#endif | |||
inline int ByteOrder::littleEndian24Bit (const void* const bytes) noexcept { return (int) ((((unsigned int) static_cast<const int8*> (bytes)[2]) << 16) | (((unsigned int) static_cast<const uint8*> (bytes)[1]) << 8) | ((unsigned int) static_cast<const uint8*> (bytes)[0])); } | |||
inline int ByteOrder::bigEndian24Bit (const void* const bytes) noexcept { return (int) ((((unsigned int) static_cast<const int8*> (bytes)[0]) << 16) | (((unsigned int) static_cast<const uint8*> (bytes)[1]) << 8) | ((unsigned int) static_cast<const uint8*> (bytes)[2])); } | |||
inline void ByteOrder::littleEndian24BitToChars (const int value, void* const destBytes) noexcept { static_cast<uint8*> (destBytes)[0] = (uint8) value; static_cast<uint8*> (destBytes)[1] = (uint8) (value >> 8); static_cast<uint8*> (destBytes)[2] = (uint8) (value >> 16); } | |||
inline void ByteOrder::bigEndian24BitToChars (const int value, void* const destBytes) noexcept { static_cast<uint8*> (destBytes)[0] = (uint8) (value >> 16); static_cast<uint8*> (destBytes)[1] = (uint8) (value >> 8); static_cast<uint8*> (destBytes)[2] = (uint8) value; } | |||
} // namespace juce |
@@ -1,56 +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. | |||
============================================================================== | |||
*/ | |||
namespace juce | |||
{ | |||
//============================================================================== | |||
/** | |||
Used by container classes as an indirect way to delete an object of a | |||
particular type. | |||
The generic implementation of this class simply calls 'delete', but you can | |||
create a specialised version of it for a particular class if you need to | |||
delete that type of object in a more appropriate way. | |||
@see ScopedPointer, OwnedArray | |||
*/ | |||
template <typename ObjectType> | |||
struct ContainerDeletePolicy | |||
{ | |||
static void destroy (ObjectType* object) | |||
{ | |||
// If the line below triggers a compiler error, it means that you are using | |||
// an incomplete type for ObjectType (for example, a type that is declared | |||
// but not defined). This is a problem because then the following delete is | |||
// undefined behaviour. The purpose of the sizeof is to capture this situation. | |||
// If this was caused by a ScopedPointer to a forward-declared type, move the | |||
// implementation of all methods trying to use the ScopedPointer (e.g. the destructor | |||
// of the class owning it) into cpp files where they can see to the definition | |||
// of ObjectType. This should fix the error. | |||
ignoreUnused (sizeof (ObjectType)); | |||
delete object; | |||
} | |||
}; | |||
} // namespace juce |
@@ -1,312 +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. | |||
============================================================================== | |||
*/ | |||
namespace juce | |||
{ | |||
#if ! (defined (DOXYGEN) || JUCE_EXCEPTIONS_DISABLED) | |||
namespace HeapBlockHelper | |||
{ | |||
template <bool shouldThrow> | |||
struct ThrowOnFail { static void checkPointer (void*) {} }; | |||
template<> | |||
struct ThrowOnFail<true> { static void checkPointer (void* data) { if (data == nullptr) throw std::bad_alloc(); } }; | |||
} | |||
#endif | |||
//============================================================================== | |||
/** | |||
Very simple container class to hold a pointer to some data on the heap. | |||
When you need to allocate some heap storage for something, always try to use | |||
this class instead of allocating the memory directly using malloc/free. | |||
A HeapBlock<char> object can be treated in pretty much exactly the same way | |||
as an char*, but as long as you allocate it on the stack or as a class member, | |||
it's almost impossible for it to leak memory. | |||
It also makes your code much more concise and readable than doing the same thing | |||
using direct allocations, | |||
E.g. instead of this: | |||
@code | |||
int* temp = (int*) malloc (1024 * sizeof (int)); | |||
memcpy (temp, xyz, 1024 * sizeof (int)); | |||
free (temp); | |||
temp = (int*) calloc (2048 * sizeof (int)); | |||
temp[0] = 1234; | |||
memcpy (foobar, temp, 2048 * sizeof (int)); | |||
free (temp); | |||
@endcode | |||
..you could just write this: | |||
@code | |||
HeapBlock<int> temp (1024); | |||
memcpy (temp, xyz, 1024 * sizeof (int)); | |||
temp.calloc (2048); | |||
temp[0] = 1234; | |||
memcpy (foobar, temp, 2048 * sizeof (int)); | |||
@endcode | |||
The class is extremely lightweight, containing only a pointer to the | |||
data, and exposes malloc/realloc/calloc/free methods that do the same jobs | |||
as their less object-oriented counterparts. Despite adding safety, you probably | |||
won't sacrifice any performance by using this in place of normal pointers. | |||
The throwOnFailure template parameter can be set to true if you'd like the class | |||
to throw a std::bad_alloc exception when an allocation fails. If this is false, | |||
then a failed allocation will just leave the heapblock with a null pointer (assuming | |||
that the system's malloc() function doesn't throw). | |||
@see Array, OwnedArray, MemoryBlock | |||
*/ | |||
template <class ElementType, bool throwOnFailure = false> | |||
class HeapBlock | |||
{ | |||
public: | |||
//============================================================================== | |||
/** Creates a HeapBlock which is initially just a null pointer. | |||
After creation, you can resize the array using the malloc(), calloc(), | |||
or realloc() methods. | |||
*/ | |||
HeapBlock() noexcept : data (nullptr) | |||
{ | |||
} | |||
/** Creates a HeapBlock containing a number of elements. | |||
The contents of the block are undefined, as it will have been created by a | |||
malloc call. | |||
If you want an array of zero values, you can use the calloc() method or the | |||
other constructor that takes an InitialisationState parameter. | |||
*/ | |||
explicit HeapBlock (const size_t numElements) | |||
: data (static_cast<ElementType*> (std::malloc (numElements * sizeof (ElementType)))) | |||
{ | |||
throwOnAllocationFailure(); | |||
} | |||
/** Creates a HeapBlock containing a number of elements. | |||
The initialiseToZero parameter determines whether the new memory should be cleared, | |||
or left uninitialised. | |||
*/ | |||
HeapBlock (const size_t numElements, const bool initialiseToZero) | |||
: data (static_cast<ElementType*> (initialiseToZero | |||
? std::calloc (numElements, sizeof (ElementType)) | |||
: std::malloc (numElements * sizeof (ElementType)))) | |||
{ | |||
throwOnAllocationFailure(); | |||
} | |||
/** Destructor. | |||
This will free the data, if any has been allocated. | |||
*/ | |||
~HeapBlock() | |||
{ | |||
std::free (data); | |||
} | |||
/** Move constructor */ | |||
HeapBlock (HeapBlock&& other) noexcept | |||
: data (other.data) | |||
{ | |||
other.data = nullptr; | |||
} | |||
/** Move assignment operator */ | |||
HeapBlock& operator= (HeapBlock&& other) noexcept | |||
{ | |||
std::swap (data, other.data); | |||
return *this; | |||
} | |||
//============================================================================== | |||
/** Returns a raw pointer to the allocated data. | |||
This may be a null pointer if the data hasn't yet been allocated, or if it has been | |||
freed by calling the free() method. | |||
*/ | |||
inline operator ElementType*() const noexcept { return data; } | |||
/** Returns a raw pointer to the allocated data. | |||
This may be a null pointer if the data hasn't yet been allocated, or if it has been | |||
freed by calling the free() method. | |||
*/ | |||
inline ElementType* get() const noexcept { return data; } | |||
/** Returns a raw pointer to the allocated data. | |||
This may be a null pointer if the data hasn't yet been allocated, or if it has been | |||
freed by calling the free() method. | |||
*/ | |||
inline ElementType* getData() const noexcept { return data; } | |||
/** Returns a void pointer to the allocated data. | |||
This may be a null pointer if the data hasn't yet been allocated, or if it has been | |||
freed by calling the free() method. | |||
*/ | |||
inline operator void*() const noexcept { return static_cast<void*> (data); } | |||
/** Returns a void pointer to the allocated data. | |||
This may be a null pointer if the data hasn't yet been allocated, or if it has been | |||
freed by calling the free() method. | |||
*/ | |||
inline operator const void*() const noexcept { return static_cast<const void*> (data); } | |||
/** Lets you use indirect calls to the first element in the array. | |||
Obviously this will cause problems if the array hasn't been initialised, because it'll | |||
be referencing a null pointer. | |||
*/ | |||
inline ElementType* operator->() const noexcept { return data; } | |||
/** Returns a reference to one of the data elements. | |||
Obviously there's no bounds-checking here, as this object is just a dumb pointer and | |||
has no idea of the size it currently has allocated. | |||
*/ | |||
template <typename IndexType> | |||
inline ElementType& operator[] (IndexType index) const noexcept { return data [index]; } | |||
/** Returns a pointer to a data element at an offset from the start of the array. | |||
This is the same as doing pointer arithmetic on the raw pointer itself. | |||
*/ | |||
template <typename IndexType> | |||
inline ElementType* operator+ (IndexType index) const noexcept { return data + index; } | |||
//============================================================================== | |||
/** Compares the pointer with another pointer. | |||
This can be handy for checking whether this is a null pointer. | |||
*/ | |||
inline bool operator== (const ElementType* const otherPointer) const noexcept { return otherPointer == data; } | |||
/** Compares the pointer with another pointer. | |||
This can be handy for checking whether this is a null pointer. | |||
*/ | |||
inline bool operator!= (const ElementType* const otherPointer) const noexcept { return otherPointer != data; } | |||
//============================================================================== | |||
/** Allocates a specified amount of memory. | |||
This uses the normal malloc to allocate an amount of memory for this object. | |||
Any previously allocated memory will be freed by this method. | |||
The number of bytes allocated will be (newNumElements * elementSize). Normally | |||
you wouldn't need to specify the second parameter, but it can be handy if you need | |||
to allocate a size in bytes rather than in terms of the number of elements. | |||
The data that is allocated will be freed when this object is deleted, or when you | |||
call free() or any of the allocation methods. | |||
*/ | |||
void malloc (const size_t newNumElements, const size_t elementSize = sizeof (ElementType)) | |||
{ | |||
std::free (data); | |||
data = static_cast<ElementType*> (std::malloc (newNumElements * elementSize)); | |||
throwOnAllocationFailure(); | |||
} | |||
/** Allocates a specified amount of memory and clears it. | |||
This does the same job as the malloc() method, but clears the memory that it allocates. | |||
*/ | |||
void calloc (const size_t newNumElements, const size_t elementSize = sizeof (ElementType)) | |||
{ | |||
std::free (data); | |||
data = static_cast<ElementType*> (std::calloc (newNumElements, elementSize)); | |||
throwOnAllocationFailure(); | |||
} | |||
/** Allocates a specified amount of memory and optionally clears it. | |||
This does the same job as either malloc() or calloc(), depending on the | |||
initialiseToZero parameter. | |||
*/ | |||
void allocate (const size_t newNumElements, bool initialiseToZero) | |||
{ | |||
std::free (data); | |||
data = static_cast<ElementType*> (initialiseToZero | |||
? std::calloc (newNumElements, sizeof (ElementType)) | |||
: std::malloc (newNumElements * sizeof (ElementType))); | |||
throwOnAllocationFailure(); | |||
} | |||
/** Re-allocates a specified amount of memory. | |||
The semantics of this method are the same as malloc() and calloc(), but it | |||
uses realloc() to keep as much of the existing data as possible. | |||
*/ | |||
void realloc (const size_t newNumElements, const size_t elementSize = sizeof (ElementType)) | |||
{ | |||
data = static_cast<ElementType*> (data == nullptr ? std::malloc (newNumElements * elementSize) | |||
: std::realloc (data, newNumElements * elementSize)); | |||
throwOnAllocationFailure(); | |||
} | |||
/** Frees any currently-allocated data. | |||
This will free the data and reset this object to be a null pointer. | |||
*/ | |||
void free() noexcept | |||
{ | |||
std::free (data); | |||
data = nullptr; | |||
} | |||
/** Swaps this object's data with the data of another HeapBlock. | |||
The two objects simply exchange their data pointers. | |||
*/ | |||
template <bool otherBlockThrows> | |||
void swapWith (HeapBlock<ElementType, otherBlockThrows>& other) noexcept | |||
{ | |||
std::swap (data, other.data); | |||
} | |||
/** This fills the block with zeros, up to the number of elements specified. | |||
Since the block has no way of knowing its own size, you must make sure that the number of | |||
elements you specify doesn't exceed the allocated size. | |||
*/ | |||
void clear (size_t numElements) noexcept | |||
{ | |||
zeromem (data, sizeof (ElementType) * numElements); | |||
} | |||
/** This typedef can be used to get the type of the heapblock's elements. */ | |||
typedef ElementType Type; | |||
private: | |||
//============================================================================== | |||
ElementType* data; | |||
void throwOnAllocationFailure() const | |||
{ | |||
#if JUCE_EXCEPTIONS_DISABLED | |||
jassert (data != nullptr); // without exceptions, you'll need to find a better way to handle this failure case. | |||
#else | |||
HeapBlockHelper::ThrowOnFail<throwOnFailure>::checkPointer (data); | |||
#endif | |||
} | |||
#if ! (defined (JUCE_DLL) || defined (JUCE_DLL_BUILD)) | |||
JUCE_DECLARE_NON_COPYABLE (HeapBlock) | |||
JUCE_PREVENT_HEAP_ALLOCATION // Creating a 'new HeapBlock' would be missing the point! | |||
#endif | |||
}; | |||
} // namespace juce |
@@ -1,138 +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. | |||
============================================================================== | |||
*/ | |||
namespace juce | |||
{ | |||
//============================================================================== | |||
/** | |||
Embedding an instance of this class inside another class can be used as a low-overhead | |||
way of detecting leaked instances. | |||
This class keeps an internal static count of the number of instances that are | |||
active, so that when the app is shutdown and the static destructors are called, | |||
it can check whether there are any left-over instances that may have been leaked. | |||
To use it, use the JUCE_LEAK_DETECTOR macro as a simple way to put one in your | |||
class declaration. Have a look through the juce codebase for examples, it's used | |||
in most of the classes. | |||
*/ | |||
template <class OwnerClass> | |||
class LeakedObjectDetector | |||
{ | |||
public: | |||
//============================================================================== | |||
LeakedObjectDetector() noexcept { ++(getCounter().numObjects); } | |||
LeakedObjectDetector (const LeakedObjectDetector&) noexcept { ++(getCounter().numObjects); } | |||
~LeakedObjectDetector() | |||
{ | |||
if (--(getCounter().numObjects) < 0) | |||
{ | |||
DBG ("*** Dangling pointer deletion! Class: " << getLeakedObjectClassName()); | |||
/** If you hit this, then you've managed to delete more instances of this class than you've | |||
created.. That indicates that you're deleting some dangling pointers. | |||
Note that although this assertion will have been triggered during a destructor, it might | |||
not be this particular deletion that's at fault - the incorrect one may have happened | |||
at an earlier point in the program, and simply not been detected until now. | |||
Most errors like this are caused by using old-fashioned, non-RAII techniques for | |||
your object management. Tut, tut. Always, always use ScopedPointers, OwnedArrays, | |||
ReferenceCountedObjects, etc, and avoid the 'delete' operator at all costs! | |||
*/ | |||
jassertfalse; | |||
} | |||
} | |||
private: | |||
//============================================================================== | |||
class LeakCounter | |||
{ | |||
public: | |||
LeakCounter() noexcept {} | |||
~LeakCounter() | |||
{ | |||
if (numObjects.value > 0) | |||
{ | |||
DBG ("*** Leaked objects detected: " << numObjects.value << " instance(s) of class " << getLeakedObjectClassName()); | |||
/** If you hit this, then you've leaked one or more objects of the type specified by | |||
the 'OwnerClass' template parameter - the name should have been printed by the line above. | |||
If you're leaking, it's probably because you're using old-fashioned, non-RAII techniques for | |||
your object management. Tut, tut. Always, always use ScopedPointers, OwnedArrays, | |||
ReferenceCountedObjects, etc, and avoid the 'delete' operator at all costs! | |||
*/ | |||
jassertfalse; | |||
} | |||
} | |||
Atomic<int> numObjects; | |||
}; | |||
static const char* getLeakedObjectClassName() | |||
{ | |||
return OwnerClass::getLeakedObjectClassName(); | |||
} | |||
static LeakCounter& getCounter() noexcept | |||
{ | |||
static LeakCounter counter; | |||
return counter; | |||
} | |||
}; | |||
//============================================================================== | |||
#if DOXYGEN || ! defined (JUCE_LEAK_DETECTOR) | |||
#if (DOXYGEN || JUCE_CHECK_MEMORY_LEAKS) | |||
/** This macro lets you embed a leak-detecting object inside a class. | |||
To use it, simply declare a JUCE_LEAK_DETECTOR(YourClassName) inside a private section | |||
of the class declaration. E.g. | |||
@code | |||
class MyClass | |||
{ | |||
public: | |||
MyClass(); | |||
void blahBlah(); | |||
private: | |||
JUCE_LEAK_DETECTOR (MyClass) | |||
}; | |||
@endcode | |||
@see JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR, LeakedObjectDetector | |||
*/ | |||
#define JUCE_LEAK_DETECTOR(OwnerClass) \ | |||
friend class juce::LeakedObjectDetector<OwnerClass>; \ | |||
static const char* getLeakedObjectClassName() noexcept { return #OwnerClass; } \ | |||
juce::LeakedObjectDetector<OwnerClass> JUCE_JOIN_MACRO (leakDetector, __LINE__); | |||
#else | |||
#define JUCE_LEAK_DETECTOR(OwnerClass) | |||
#endif | |||
#endif | |||
} // namespace juce |
@@ -1,144 +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. | |||
============================================================================== | |||
*/ | |||
namespace juce | |||
{ | |||
//============================================================================== | |||
/** Fills a block of memory with zeros. */ | |||
inline void zeromem (void* memory, size_t numBytes) noexcept { memset (memory, 0, numBytes); } | |||
/** Overwrites a structure or object with zeros. */ | |||
template <typename Type> | |||
inline void zerostruct (Type& structure) noexcept { memset (&structure, 0, sizeof (structure)); } | |||
/** Delete an object pointer, and sets the pointer to null. | |||
Remember that it's not good c++ practice to use delete directly - always try to use a ScopedPointer | |||
or other automatic lifetime-management system rather than resorting to deleting raw pointers! | |||
*/ | |||
template <typename Type> | |||
inline void deleteAndZero (Type& pointer) { delete pointer; pointer = nullptr; } | |||
/** A handy function which adds a number of bytes to any type of pointer and returns the result. | |||
This can be useful to avoid casting pointers to a char* and back when you want to move them by | |||
a specific number of bytes, | |||
*/ | |||
template <typename Type, typename IntegerType> | |||
inline Type* addBytesToPointer (Type* basePointer, IntegerType bytes) noexcept { return (Type*) (((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. */ | |||
template <typename Type, typename IntegerType> | |||
inline Type* snapPointerToAlignment (Type* basePointer, IntegerType alignmentBytes) noexcept | |||
{ | |||
return (Type*) ((((size_t) basePointer) + (alignmentBytes - 1)) & ~(alignmentBytes - 1)); | |||
} | |||
/** A handy function which returns the difference between any two pointers, in bytes. | |||
The address of the second pointer is subtracted from the first, and the difference in bytes is returned. | |||
*/ | |||
template <typename Type1, typename Type2> | |||
inline int getAddressDifference (Type1* pointer1, Type2* pointer2) noexcept { return (int) (((const char*) pointer1) - (const char*) pointer2); } | |||
/** If a pointer is non-null, this returns a new copy of the object that it points to, or safely returns | |||
nullptr if the pointer is null. | |||
*/ | |||
template <class Type> | |||
inline Type* createCopyIfNotNull (const Type* objectToCopy) { return objectToCopy != nullptr ? new Type (*objectToCopy) : nullptr; } | |||
//============================================================================== | |||
/** A handy function to read un-aligned memory without a performance penalty or bus-error. */ | |||
template <typename Type> | |||
inline Type readUnaligned (const void* srcPtr) noexcept | |||
{ | |||
Type value; | |||
memcpy (&value, srcPtr, sizeof (Type)); | |||
return value; | |||
} | |||
/** A handy function to write un-aligned memory without a performance penalty or bus-error. */ | |||
template <typename Type> | |||
inline void writeUnaligned (void* dstPtr, Type value) noexcept | |||
{ | |||
memcpy (dstPtr, &value, sizeof (Type)); | |||
} | |||
//============================================================================== | |||
#if JUCE_MAC || JUCE_IOS || DOXYGEN | |||
/** A handy C++ wrapper that creates and deletes an NSAutoreleasePool object using RAII. | |||
You should use the JUCE_AUTORELEASEPOOL macro to create a local auto-release pool on the stack. | |||
*/ | |||
class JUCE_API ScopedAutoReleasePool | |||
{ | |||
public: | |||
ScopedAutoReleasePool(); | |||
~ScopedAutoReleasePool(); | |||
private: | |||
void* pool; | |||
JUCE_DECLARE_NON_COPYABLE (ScopedAutoReleasePool) | |||
}; | |||
/** A macro that can be used to easily declare a local ScopedAutoReleasePool | |||
object for RAII-based obj-C autoreleasing. | |||
Because this may use the \@autoreleasepool syntax, you must follow the macro with | |||
a set of braces to mark the scope of the pool. | |||
*/ | |||
#if (JUCE_COMPILER_SUPPORTS_ARC && defined (__OBJC__)) || DOXYGEN | |||
#define JUCE_AUTORELEASEPOOL @autoreleasepool | |||
#else | |||
#define JUCE_AUTORELEASEPOOL const juce::ScopedAutoReleasePool JUCE_JOIN_MACRO (autoReleasePool_, __LINE__); | |||
#endif | |||
#else | |||
#define JUCE_AUTORELEASEPOOL | |||
#endif | |||
//============================================================================== | |||
/* In a Windows DLL build, we'll expose some malloc/free functions that live inside the DLL, and use these for | |||
allocating all the objects - that way all juce objects in the DLL and in the host will live in the same heap, | |||
avoiding problems when an object is created in one module and passed across to another where it is deleted. | |||
By piggy-backing on the JUCE_LEAK_DETECTOR macro, these allocators can be injected into most juce classes. | |||
*/ | |||
#if JUCE_MSVC && (defined (JUCE_DLL) || defined (JUCE_DLL_BUILD)) && ! (JUCE_DISABLE_DLL_ALLOCATORS || DOXYGEN) | |||
extern JUCE_API void* juceDLL_malloc (size_t); | |||
extern JUCE_API void juceDLL_free (void*); | |||
#define JUCE_LEAK_DETECTOR(OwnerClass) public:\ | |||
static void* operator new (size_t sz) { return juce::juceDLL_malloc (sz); } \ | |||
static void* operator new (size_t, void* p) { return p; } \ | |||
static void operator delete (void* p) { juce::juceDLL_free (p); } \ | |||
static void operator delete (void*, void*) {} | |||
#endif | |||
//============================================================================== | |||
/** (Deprecated) This was a Windows-specific way of checking for object leaks - now please | |||
use the JUCE_LEAK_DETECTOR instead. | |||
*/ | |||
#ifndef juce_UseDebuggingNewOperator | |||
#define juce_UseDebuggingNewOperator | |||
#endif | |||
} // namespace juce |
@@ -1,411 +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. | |||
============================================================================== | |||
*/ | |||
namespace juce | |||
{ | |||
MemoryBlock::MemoryBlock() noexcept : size (0) | |||
{ | |||
} | |||
MemoryBlock::MemoryBlock (const size_t initialSize, const bool initialiseToZero) | |||
{ | |||
if (initialSize > 0) | |||
{ | |||
size = initialSize; | |||
data.allocate (initialSize, initialiseToZero); | |||
} | |||
else | |||
{ | |||
size = 0; | |||
} | |||
} | |||
MemoryBlock::MemoryBlock (const MemoryBlock& other) | |||
: size (other.size) | |||
{ | |||
if (size > 0) | |||
{ | |||
jassert (other.data != nullptr); | |||
data.malloc (size); | |||
memcpy (data, other.data, size); | |||
} | |||
} | |||
MemoryBlock::MemoryBlock (const void* const dataToInitialiseFrom, const size_t sizeInBytes) | |||
: size (sizeInBytes) | |||
{ | |||
jassert (((ssize_t) sizeInBytes) >= 0); | |||
if (size > 0) | |||
{ | |||
jassert (dataToInitialiseFrom != nullptr); // non-zero size, but a zero pointer passed-in? | |||
data.malloc (size); | |||
if (dataToInitialiseFrom != nullptr) | |||
memcpy (data, dataToInitialiseFrom, size); | |||
} | |||
} | |||
MemoryBlock::~MemoryBlock() noexcept | |||
{ | |||
} | |||
MemoryBlock& MemoryBlock::operator= (const MemoryBlock& other) | |||
{ | |||
if (this != &other) | |||
{ | |||
setSize (other.size, false); | |||
memcpy (data, other.data, size); | |||
} | |||
return *this; | |||
} | |||
MemoryBlock::MemoryBlock (MemoryBlock&& other) noexcept | |||
: data (static_cast<HeapBlockType&&> (other.data)), | |||
size (other.size) | |||
{ | |||
} | |||
MemoryBlock& MemoryBlock::operator= (MemoryBlock&& other) noexcept | |||
{ | |||
data = static_cast<HeapBlockType&&> (other.data); | |||
size = other.size; | |||
return *this; | |||
} | |||
//============================================================================== | |||
bool MemoryBlock::operator== (const MemoryBlock& other) const noexcept | |||
{ | |||
return matches (other.data, other.size); | |||
} | |||
bool MemoryBlock::operator!= (const MemoryBlock& other) const noexcept | |||
{ | |||
return ! operator== (other); | |||
} | |||
bool MemoryBlock::matches (const void* dataToCompare, size_t dataSize) const noexcept | |||
{ | |||
return size == dataSize | |||
&& memcmp (data, dataToCompare, size) == 0; | |||
} | |||
//============================================================================== | |||
// this will resize the block to this size | |||
void MemoryBlock::setSize (const size_t newSize, const bool initialiseToZero) | |||
{ | |||
if (size != newSize) | |||
{ | |||
if (newSize <= 0) | |||
{ | |||
reset(); | |||
} | |||
else | |||
{ | |||
if (data != nullptr) | |||
{ | |||
data.realloc (newSize); | |||
if (initialiseToZero && (newSize > size)) | |||
zeromem (data + size, newSize - size); | |||
} | |||
else | |||
{ | |||
data.allocate (newSize, initialiseToZero); | |||
} | |||
size = newSize; | |||
} | |||
} | |||
} | |||
void MemoryBlock::reset() | |||
{ | |||
data.free(); | |||
size = 0; | |||
} | |||
void MemoryBlock::ensureSize (const size_t minimumSize, const bool initialiseToZero) | |||
{ | |||
if (size < minimumSize) | |||
setSize (minimumSize, initialiseToZero); | |||
} | |||
void MemoryBlock::swapWith (MemoryBlock& other) noexcept | |||
{ | |||
std::swap (size, other.size); | |||
data.swapWith (other.data); | |||
} | |||
//============================================================================== | |||
void MemoryBlock::fillWith (const uint8 value) noexcept | |||
{ | |||
memset (data, (int) value, size); | |||
} | |||
void MemoryBlock::append (const void* const srcData, const size_t numBytes) | |||
{ | |||
if (numBytes > 0) | |||
{ | |||
jassert (srcData != nullptr); // this must not be null! | |||
const size_t oldSize = size; | |||
setSize (size + numBytes); | |||
memcpy (data + oldSize, srcData, numBytes); | |||
} | |||
} | |||
void MemoryBlock::replaceWith (const void* const srcData, const size_t numBytes) | |||
{ | |||
if (numBytes > 0) | |||
{ | |||
jassert (srcData != nullptr); // this must not be null! | |||
setSize (numBytes); | |||
memcpy (data, srcData, numBytes); | |||
} | |||
} | |||
void MemoryBlock::insert (const void* const srcData, const size_t numBytes, size_t insertPosition) | |||
{ | |||
if (numBytes > 0) | |||
{ | |||
jassert (srcData != nullptr); // this must not be null! | |||
insertPosition = jmin (size, insertPosition); | |||
const size_t trailingDataSize = size - insertPosition; | |||
setSize (size + numBytes, false); | |||
if (trailingDataSize > 0) | |||
memmove (data + insertPosition + numBytes, | |||
data + insertPosition, | |||
trailingDataSize); | |||
memcpy (data + insertPosition, srcData, numBytes); | |||
} | |||
} | |||
void MemoryBlock::removeSection (const size_t startByte, const size_t numBytesToRemove) | |||
{ | |||
if (startByte + numBytesToRemove >= size) | |||
{ | |||
setSize (startByte); | |||
} | |||
else if (numBytesToRemove > 0) | |||
{ | |||
memmove (data + startByte, | |||
data + startByte + numBytesToRemove, | |||
size - (startByte + numBytesToRemove)); | |||
setSize (size - numBytesToRemove); | |||
} | |||
} | |||
void MemoryBlock::copyFrom (const void* const src, int offset, size_t num) noexcept | |||
{ | |||
const char* d = static_cast<const char*> (src); | |||
if (offset < 0) | |||
{ | |||
d -= offset; | |||
num += (size_t) -offset; | |||
offset = 0; | |||
} | |||
if ((size_t) offset + num > size) | |||
num = size - (size_t) offset; | |||
if (num > 0) | |||
memcpy (data + offset, d, num); | |||
} | |||
void MemoryBlock::copyTo (void* const dst, int offset, size_t num) const noexcept | |||
{ | |||
char* d = static_cast<char*> (dst); | |||
if (offset < 0) | |||
{ | |||
zeromem (d, (size_t) -offset); | |||
d -= offset; | |||
num -= (size_t) -offset; | |||
offset = 0; | |||
} | |||
if ((size_t) offset + num > size) | |||
{ | |||
const size_t newNum = (size_t) size - (size_t) offset; | |||
zeromem (d + newNum, num - newNum); | |||
num = newNum; | |||
} | |||
if (num > 0) | |||
memcpy (d, data + offset, num); | |||
} | |||
String MemoryBlock::toString() const | |||
{ | |||
return String::fromUTF8 (data, (int) size); | |||
} | |||
//============================================================================== | |||
int MemoryBlock::getBitRange (const size_t bitRangeStart, size_t numBits) const noexcept | |||
{ | |||
int res = 0; | |||
size_t byte = bitRangeStart >> 3; | |||
size_t offsetInByte = bitRangeStart & 7; | |||
size_t bitsSoFar = 0; | |||
while (numBits > 0 && (size_t) byte < size) | |||
{ | |||
const size_t bitsThisTime = jmin (numBits, 8 - offsetInByte); | |||
const int mask = (0xff >> (8 - bitsThisTime)) << offsetInByte; | |||
res |= (((data[byte] & mask) >> offsetInByte) << bitsSoFar); | |||
bitsSoFar += bitsThisTime; | |||
numBits -= bitsThisTime; | |||
++byte; | |||
offsetInByte = 0; | |||
} | |||
return res; | |||
} | |||
void MemoryBlock::setBitRange (const size_t bitRangeStart, size_t numBits, int bitsToSet) noexcept | |||
{ | |||
size_t byte = bitRangeStart >> 3; | |||
size_t offsetInByte = bitRangeStart & 7; | |||
uint32 mask = ~((((uint32) 0xffffffff) << (32 - numBits)) >> (32 - numBits)); | |||
while (numBits > 0 && (size_t) byte < size) | |||
{ | |||
const size_t bitsThisTime = jmin (numBits, 8 - offsetInByte); | |||
const uint32 tempMask = (mask << offsetInByte) | ~((((uint32) 0xffffffff) >> offsetInByte) << offsetInByte); | |||
const uint32 tempBits = (uint32) bitsToSet << offsetInByte; | |||
data[byte] = (char) (((uint32) data[byte] & tempMask) | tempBits); | |||
++byte; | |||
numBits -= bitsThisTime; | |||
bitsToSet >>= bitsThisTime; | |||
mask >>= bitsThisTime; | |||
offsetInByte = 0; | |||
} | |||
} | |||
//============================================================================== | |||
void MemoryBlock::loadFromHexString (StringRef hex) | |||
{ | |||
ensureSize ((size_t) hex.length() >> 1); | |||
char* dest = data; | |||
String::CharPointerType t (hex.text); | |||
for (;;) | |||
{ | |||
int byte = 0; | |||
for (int loop = 2; --loop >= 0;) | |||
{ | |||
byte <<= 4; | |||
for (;;) | |||
{ | |||
const juce_wchar c = t.getAndAdvance(); | |||
if (c >= '0' && c <= '9') { byte |= c - '0'; break; } | |||
if (c >= 'a' && c <= 'z') { byte |= c - ('a' - 10); break; } | |||
if (c >= 'A' && c <= 'Z') { byte |= c - ('A' - 10); break; } | |||
if (c == 0) | |||
{ | |||
setSize (static_cast<size_t> (dest - data)); | |||
return; | |||
} | |||
} | |||
} | |||
*dest++ = (char) byte; | |||
} | |||
} | |||
//============================================================================== | |||
static const char base64EncodingTable[] = ".ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+"; | |||
String MemoryBlock::toBase64Encoding() const | |||
{ | |||
const size_t numChars = ((size << 3) + 5) / 6; | |||
String destString ((unsigned int) size); // store the length, followed by a '.', and then the data. | |||
const int initialLen = destString.length(); | |||
destString.preallocateBytes (sizeof (String::CharPointerType::CharType) * (size_t) initialLen + 2 + numChars); | |||
String::CharPointerType d (destString.getCharPointer()); | |||
d += initialLen; | |||
d.write ('.'); | |||
for (size_t i = 0; i < numChars; ++i) | |||
d.write ((juce_wchar) (uint8) base64EncodingTable [getBitRange (i * 6, 6)]); | |||
d.writeNull(); | |||
return destString; | |||
} | |||
static const char base64DecodingTable[] = | |||
{ | |||
63, 0, 0, 0, 0, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 0, 0, 0, 0, 0, 0, 0, | |||
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, | |||
0, 0, 0, 0, 0, 0, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52 | |||
}; | |||
bool MemoryBlock::fromBase64Encoding (StringRef s) | |||
{ | |||
String::CharPointerType dot (CharacterFunctions::find (s.text, (juce_wchar) '.')); | |||
if (dot.isEmpty()) | |||
return false; | |||
const int numBytesNeeded = String (s.text, dot).getIntValue(); | |||
setSize ((size_t) numBytesNeeded, true); | |||
String::CharPointerType srcChars (dot + 1); | |||
int pos = 0; | |||
for (;;) | |||
{ | |||
int c = (int) srcChars.getAndAdvance(); | |||
if (c == 0) | |||
return true; | |||
c -= 43; | |||
if (isPositiveAndBelow (c, numElementsInArray (base64DecodingTable))) | |||
{ | |||
setBitRange ((size_t) pos, 6, base64DecodingTable [c]); | |||
pos += 6; | |||
} | |||
} | |||
} | |||
} // namespace juce |
@@ -1,260 +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. | |||
============================================================================== | |||
*/ | |||
namespace juce | |||
{ | |||
//============================================================================== | |||
/** | |||
A class to hold a resizable block of raw data. | |||
*/ | |||
class JUCE_API MemoryBlock | |||
{ | |||
public: | |||
//============================================================================== | |||
/** Create an uninitialised block with 0 size. */ | |||
MemoryBlock() noexcept; | |||
/** Creates a memory block with a given initial size. | |||
@param initialSize the size of block to create | |||
@param initialiseToZero whether to clear the memory or just leave it uninitialised | |||
*/ | |||
MemoryBlock (const size_t initialSize, | |||
bool initialiseToZero = false); | |||
/** Creates a copy of another memory block. */ | |||
MemoryBlock (const MemoryBlock&); | |||
/** Creates a memory block using a copy of a block of data. | |||
@param dataToInitialiseFrom some data to copy into this block | |||
@param sizeInBytes how much space to use | |||
*/ | |||
MemoryBlock (const void* dataToInitialiseFrom, size_t sizeInBytes); | |||
/** Destructor. */ | |||
~MemoryBlock() noexcept; | |||
/** Copies another memory block onto this one. | |||
This block will be resized and copied to exactly match the other one. | |||
*/ | |||
MemoryBlock& operator= (const MemoryBlock&); | |||
/** Move constructor */ | |||
MemoryBlock (MemoryBlock&&) noexcept; | |||
/** Move assignment operator */ | |||
MemoryBlock& operator= (MemoryBlock&&) noexcept; | |||
//============================================================================== | |||
/** Compares two memory blocks. | |||
@returns true only if the two blocks are the same size and have identical contents. | |||
*/ | |||
bool operator== (const MemoryBlock& other) const noexcept; | |||
/** Compares two memory blocks. | |||
@returns true if the two blocks are different sizes or have different contents. | |||
*/ | |||
bool operator!= (const MemoryBlock& other) const noexcept; | |||
/** Returns true if the data in this MemoryBlock matches the raw bytes passed-in. */ | |||
bool matches (const void* data, size_t dataSize) const noexcept; | |||
//============================================================================== | |||
/** Returns a void pointer to the data. | |||
Note that the pointer returned will probably become invalid when the | |||
block is resized. | |||
*/ | |||
void* getData() const noexcept { return data; } | |||
/** Returns a byte from the memory block. | |||
This returns a reference, so you can also use it to set a byte. | |||
*/ | |||
template <typename Type> | |||
char& operator[] (const Type offset) const noexcept { return data [offset]; } | |||
/** Returns an iterator for the data. */ | |||
char* begin() const noexcept { return data; } | |||
/** Returns an end-iterator for the data. */ | |||
char* end() const noexcept { return begin() + getSize(); } | |||
//============================================================================== | |||
/** Returns the block's current allocated size, in bytes. */ | |||
size_t getSize() const noexcept { return size; } | |||
/** Resizes the memory block. | |||
Any data that is present in both the old and new sizes will be retained. | |||
When enlarging the block, the new space that is allocated at the end can either be | |||
cleared, or left uninitialised. | |||
@param newSize the new desired size for the block | |||
@param initialiseNewSpaceToZero if the block gets enlarged, this determines | |||
whether to clear the new section or just leave it | |||
uninitialised | |||
@see ensureSize | |||
*/ | |||
void setSize (const size_t newSize, | |||
bool initialiseNewSpaceToZero = false); | |||
/** Increases the block's size only if it's smaller than a given size. | |||
@param minimumSize if the block is already bigger than this size, no action | |||
will be taken; otherwise it will be increased to this size | |||
@param initialiseNewSpaceToZero if the block gets enlarged, this determines | |||
whether to clear the new section or just leave it | |||
uninitialised | |||
@see setSize | |||
*/ | |||
void ensureSize (const size_t minimumSize, | |||
bool initialiseNewSpaceToZero = false); | |||
/** Frees all the blocks data, setting its size to 0. */ | |||
void reset(); | |||
//============================================================================== | |||
/** Fills the entire memory block with a repeated byte value. | |||
This is handy for clearing a block of memory to zero. | |||
*/ | |||
void fillWith (uint8 valueToUse) noexcept; | |||
/** Adds another block of data to the end of this one. | |||
The data pointer must not be null. This block's size will be increased accordingly. | |||
*/ | |||
void append (const void* data, size_t numBytes); | |||
/** Resizes this block to the given size and fills its contents from the supplied buffer. | |||
The data pointer must not be null. | |||
*/ | |||
void replaceWith (const void* data, size_t numBytes); | |||
/** Inserts some data into the block. | |||
The dataToInsert pointer must not be null. This block's size will be increased accordingly. | |||
If the insert position lies outside the valid range of the block, it will be clipped to | |||
within the range before being used. | |||
*/ | |||
void insert (const void* dataToInsert, size_t numBytesToInsert, size_t insertPosition); | |||
/** Chops out a section of the block. | |||
This will remove a section of the memory block and close the gap around it, | |||
shifting any subsequent data downwards and reducing the size of the block. | |||
If the range specified goes beyond the size of the block, it will be clipped. | |||
*/ | |||
void removeSection (size_t startByte, size_t numBytesToRemove); | |||
//============================================================================== | |||
/** Copies data into this MemoryBlock from a memory address. | |||
@param srcData the memory location of the data to copy into this block | |||
@param destinationOffset the offset in this block at which the data being copied should begin | |||
@param numBytes how much to copy in (if this goes beyond the size of the memory block, | |||
it will be clipped so not to do anything nasty) | |||
*/ | |||
void copyFrom (const void* srcData, | |||
int destinationOffset, | |||
size_t numBytes) noexcept; | |||
/** Copies data from this MemoryBlock to a memory address. | |||
@param destData the memory location to write to | |||
@param sourceOffset the offset within this block from which the copied data will be read | |||
@param numBytes how much to copy (if this extends beyond the limits of the memory block, | |||
zeros will be used for that portion of the data) | |||
*/ | |||
void copyTo (void* destData, | |||
int sourceOffset, | |||
size_t numBytes) const noexcept; | |||
//============================================================================== | |||
/** Exchanges the contents of this and another memory block. | |||
No actual copying is required for this, so it's very fast. | |||
*/ | |||
void swapWith (MemoryBlock& other) noexcept; | |||
//============================================================================== | |||
/** Attempts to parse the contents of the block as a zero-terminated UTF8 string. */ | |||
String toString() const; | |||
//============================================================================== | |||
/** Parses a string of hexadecimal numbers and writes this data into the memory block. | |||
The block will be resized to the number of valid bytes read from the string. | |||
Non-hex characters in the string will be ignored. | |||
@see String::toHexString() | |||
*/ | |||
void loadFromHexString (StringRef sourceHexString); | |||
//============================================================================== | |||
/** Sets a number of bits in the memory block, treating it as a long binary sequence. */ | |||
void setBitRange (size_t bitRangeStart, | |||
size_t numBits, | |||
int binaryNumberToApply) noexcept; | |||
/** Reads a number of bits from the memory block, treating it as one long binary sequence */ | |||
int getBitRange (size_t bitRangeStart, | |||
size_t numBitsToRead) const noexcept; | |||
//============================================================================== | |||
/** Returns a string of characters in a JUCE-specific text encoding that represents the | |||
binary contents of this block. | |||
This uses a JUCE-specific (i.e. not standard!) 64-bit encoding system to convert binary | |||
data into a string of ASCII characters for purposes like storage in XML. | |||
Note that this proprietary format is mainly kept here for backwards-compatibility, and | |||
you may prefer to use the Base64::toBase64() method if you want to use the standard | |||
base-64 encoding. | |||
@see fromBase64Encoding, Base64::toBase64, Base64::convertToBase64 | |||
*/ | |||
String toBase64Encoding() const; | |||
/** Takes a string created by MemoryBlock::toBase64Encoding() and extracts the original data. | |||
The string passed in must have been created by to64BitEncoding(), and this | |||
block will be resized to recreate the original data block. | |||
Note that these methods use a JUCE-specific (i.e. not standard!) 64-bit encoding system. | |||
You may prefer to use the Base64::convertFromBase64() method if you want to use the | |||
standard base-64 encoding. | |||
@see toBase64Encoding, Base64::convertFromBase64 | |||
*/ | |||
bool fromBase64Encoding (StringRef encodedString); | |||
private: | |||
//============================================================================== | |||
typedef HeapBlock<char, true> HeapBlockType; | |||
HeapBlockType data; | |||
size_t size; | |||
JUCE_LEAK_DETECTOR (MemoryBlock) | |||
}; | |||
} // namespace juce |
@@ -1,184 +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. | |||
============================================================================== | |||
*/ | |||
namespace juce | |||
{ | |||
//============================================================================== | |||
/** | |||
Holds a pointer to an object which can optionally be deleted when this pointer | |||
goes out of scope. | |||
This acts in many ways like a ScopedPointer, but allows you to specify whether or | |||
not the object is deleted. | |||
@see ScopedPointer | |||
*/ | |||
template <class ObjectType> | |||
class OptionalScopedPointer | |||
{ | |||
public: | |||
//============================================================================== | |||
/** Creates an empty OptionalScopedPointer. */ | |||
OptionalScopedPointer() : shouldDelete (false) {} | |||
/** Creates an OptionalScopedPointer to point to a given object, and specifying whether | |||
the OptionalScopedPointer will delete it. | |||
If takeOwnership is true, then the OptionalScopedPointer will act like a ScopedPointer, | |||
deleting the object when it is itself deleted. If this parameter is false, then the | |||
OptionalScopedPointer just holds a normal pointer to the object, and won't delete it. | |||
*/ | |||
OptionalScopedPointer (ObjectType* objectToHold, bool takeOwnership) | |||
: object (objectToHold), shouldDelete (takeOwnership) | |||
{ | |||
} | |||
/** Takes ownership of the object that another OptionalScopedPointer holds. | |||
Like a normal ScopedPointer, the objectToTransferFrom object will become null, | |||
as ownership of the managed object is transferred to this object. | |||
The flag to indicate whether or not to delete the managed object is also | |||
copied from the source object. | |||
*/ | |||
OptionalScopedPointer (OptionalScopedPointer& objectToTransferFrom) | |||
: object (objectToTransferFrom.release()), | |||
shouldDelete (objectToTransferFrom.shouldDelete) | |||
{ | |||
} | |||
/** Takes ownership of the object that another OptionalScopedPointer holds. | |||
Like a normal ScopedPointer, the objectToTransferFrom object will become null, | |||
as ownership of the managed object is transferred to this object. | |||
The ownership flag that says whether or not to delete the managed object is also | |||
copied from the source object. | |||
*/ | |||
OptionalScopedPointer& operator= (OptionalScopedPointer& objectToTransferFrom) | |||
{ | |||
if (object != objectToTransferFrom.object) | |||
{ | |||
clear(); | |||
object = objectToTransferFrom.object; | |||
} | |||
shouldDelete = objectToTransferFrom.shouldDelete; | |||
return *this; | |||
} | |||
/** The destructor may or may not delete the object that is being held, depending on the | |||
takeOwnership flag that was specified when the object was first passed into an | |||
OptionalScopedPointer constructor. | |||
*/ | |||
~OptionalScopedPointer() | |||
{ | |||
clear(); | |||
} | |||
//============================================================================== | |||
/** Returns the object that this pointer is managing. */ | |||
inline operator ObjectType*() const noexcept { return object; } | |||
/** Returns the object that this pointer is managing. */ | |||
inline ObjectType* get() const noexcept { return object; } | |||
/** Returns the object that this pointer is managing. */ | |||
inline ObjectType& operator*() const noexcept { return *object; } | |||
/** Lets you access methods and properties of the object that this pointer is holding. */ | |||
inline ObjectType* operator->() const noexcept { return object; } | |||
//============================================================================== | |||
/** Removes the current object from this OptionalScopedPointer without deleting it. | |||
This will return the current object, and set this OptionalScopedPointer to a null pointer. | |||
*/ | |||
ObjectType* release() noexcept { return object.release(); } | |||
/** Resets this pointer to null, possibly deleting the object that it holds, if it has | |||
ownership of it. | |||
*/ | |||
void clear() | |||
{ | |||
if (! shouldDelete) | |||
object.release(); | |||
} | |||
/** Makes this OptionalScopedPointer point at a new object, specifying whether the | |||
OptionalScopedPointer will take ownership of the object. | |||
If takeOwnership is true, then the OptionalScopedPointer will act like a ScopedPointer, | |||
deleting the object when it is itself deleted. If this parameter is false, then the | |||
OptionalScopedPointer just holds a normal pointer to the object, and won't delete it. | |||
*/ | |||
void set (ObjectType* newObject, bool takeOwnership) | |||
{ | |||
if (object != newObject) | |||
{ | |||
clear(); | |||
object = newObject; | |||
} | |||
shouldDelete = takeOwnership; | |||
} | |||
/** Makes this OptionalScopedPointer point at a new object, and take ownership of that object. */ | |||
void setOwned (ObjectType* newObject) | |||
{ | |||
set (newObject, true); | |||
} | |||
/** Makes this OptionalScopedPointer point at a new object, but will not take ownership of that object. */ | |||
void setNonOwned (ObjectType* newObject) | |||
{ | |||
set (newObject, false); | |||
} | |||
/** Returns true if the target object will be deleted when this pointer | |||
object is deleted. | |||
*/ | |||
bool willDeleteObject() const noexcept { return shouldDelete; } | |||
//============================================================================== | |||
/** Swaps this object with another OptionalScopedPointer. | |||
The two objects simply exchange their states. | |||
*/ | |||
void swapWith (OptionalScopedPointer<ObjectType>& other) noexcept | |||
{ | |||
object.swapWith (other.object); | |||
std::swap (shouldDelete, other.shouldDelete); | |||
} | |||
private: | |||
//============================================================================== | |||
ScopedPointer<ObjectType> object; | |||
bool shouldDelete; | |||
// 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! | |||
// If you hit a problem with this, you probably meant to say | |||
// myPointer.setOwned (myScopedPointer.release()) | |||
void setOwned (const ScopedPointer<ObjectType>&) JUCE_DELETED_FUNCTION; | |||
}; | |||
} // namespace juce |
@@ -1,411 +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. | |||
============================================================================== | |||
*/ | |||
namespace juce | |||
{ | |||
//============================================================================== | |||
/** | |||
A base class which provides methods for reference-counting. | |||
To add reference-counting to a class, derive it from this class, and | |||
use the ReferenceCountedObjectPtr class to point to it. | |||
e.g. @code | |||
class MyClass : public ReferenceCountedObject | |||
{ | |||
void foo(); | |||
// This is a neat way of declaring a typedef for a pointer class, | |||
// rather than typing out the full templated name each time.. | |||
typedef ReferenceCountedObjectPtr<MyClass> Ptr; | |||
}; | |||
MyClass::Ptr p = new MyClass(); | |||
MyClass::Ptr p2 = p; | |||
p = nullptr; | |||
p2->foo(); | |||
@endcode | |||
Once a new ReferenceCountedObject has been assigned to a pointer, be | |||
careful not to delete the object manually. | |||
This class uses an Atomic<int> value to hold the reference count, so that it | |||
the pointers can be passed between threads safely. For a faster but non-thread-safe | |||
version, use SingleThreadedReferenceCountedObject instead. | |||
@see ReferenceCountedObjectPtr, ReferenceCountedArray, SingleThreadedReferenceCountedObject | |||
*/ | |||
class JUCE_API ReferenceCountedObject | |||
{ | |||
public: | |||
//============================================================================== | |||
/** Increments the object's reference count. | |||
This is done automatically by the smart pointer, but is public just | |||
in case it's needed for nefarious purposes. | |||
*/ | |||
void incReferenceCount() noexcept | |||
{ | |||
++refCount; | |||
} | |||
/** Decreases the object's reference count. | |||
If the count gets to zero, the object will be deleted. | |||
*/ | |||
void decReferenceCount() noexcept | |||
{ | |||
jassert (getReferenceCount() > 0); | |||
if (--refCount == 0) | |||
delete this; | |||
} | |||
/** Decreases the object's reference count. | |||
If the count gets to zero, the object will not be deleted, but this method | |||
will return true, allowing the caller to take care of deletion. | |||
*/ | |||
bool decReferenceCountWithoutDeleting() noexcept | |||
{ | |||
jassert (getReferenceCount() > 0); | |||
return --refCount == 0; | |||
} | |||
/** Returns the object's current reference count. */ | |||
int getReferenceCount() const noexcept { return refCount.get(); } | |||
protected: | |||
//============================================================================== | |||
/** Creates the reference-counted object (with an initial ref count of zero). */ | |||
ReferenceCountedObject() {} | |||
/** Destructor. */ | |||
virtual ~ReferenceCountedObject() | |||
{ | |||
// it's dangerous to delete an object that's still referenced by something else! | |||
jassert (getReferenceCount() == 0); | |||
} | |||
/** Resets the reference count to zero without deleting the object. | |||
You should probably never need to use this! | |||
*/ | |||
void resetReferenceCount() noexcept | |||
{ | |||
refCount = 0; | |||
} | |||
private: | |||
//============================================================================== | |||
Atomic <int> refCount; | |||
friend struct ContainerDeletePolicy<ReferenceCountedObject>; | |||
JUCE_DECLARE_NON_COPYABLE (ReferenceCountedObject) | |||
}; | |||
//============================================================================== | |||
/** | |||
Adds reference-counting to an object. | |||
This is effectively a version of the ReferenceCountedObject class, but which | |||
uses a non-atomic counter, and so is not thread-safe (but which will be more | |||
efficient). | |||
For more details on how to use it, see the ReferenceCountedObject class notes. | |||
@see ReferenceCountedObject, ReferenceCountedObjectPtr, ReferenceCountedArray | |||
*/ | |||
class JUCE_API SingleThreadedReferenceCountedObject | |||
{ | |||
public: | |||
//============================================================================== | |||
/** Increments the object's reference count. | |||
This is done automatically by the smart pointer, but is public just | |||
in case it's needed for nefarious purposes. | |||
*/ | |||
void incReferenceCount() noexcept | |||
{ | |||
++refCount; | |||
} | |||
/** Decreases the object's reference count. | |||
If the count gets to zero, the object will be deleted. | |||
*/ | |||
void decReferenceCount() noexcept | |||
{ | |||
jassert (getReferenceCount() > 0); | |||
if (--refCount == 0) | |||
delete this; | |||
} | |||
/** Decreases the object's reference count. | |||
If the count gets to zero, the object will not be deleted, but this method | |||
will return true, allowing the caller to take care of deletion. | |||
*/ | |||
bool decReferenceCountWithoutDeleting() noexcept | |||
{ | |||
jassert (getReferenceCount() > 0); | |||
return --refCount == 0; | |||
} | |||
/** Returns the object's current reference count. */ | |||
int getReferenceCount() const noexcept { return refCount; } | |||
protected: | |||
//============================================================================== | |||
/** Creates the reference-counted object (with an initial ref count of zero). */ | |||
SingleThreadedReferenceCountedObject() : refCount (0) {} | |||
/** Destructor. */ | |||
virtual ~SingleThreadedReferenceCountedObject() | |||
{ | |||
// it's dangerous to delete an object that's still referenced by something else! | |||
jassert (getReferenceCount() == 0); | |||
} | |||
private: | |||
//============================================================================== | |||
int refCount; | |||
friend struct ContainerDeletePolicy<ReferenceCountedObject>; | |||
JUCE_DECLARE_NON_COPYABLE (SingleThreadedReferenceCountedObject) | |||
}; | |||
//============================================================================== | |||
/** | |||
A smart-pointer class which points to a reference-counted object. | |||
The template parameter specifies the class of the object you want to point to - the easiest | |||
way to make a class reference-countable is to simply make it inherit from ReferenceCountedObject | |||
or SingleThreadedReferenceCountedObject, but if you need to, you can roll your own reference-countable | |||
class by implementing a set of methods called incReferenceCount(), decReferenceCount(), and | |||
decReferenceCountWithoutDeleting(). See ReferenceCountedObject for examples of how these methods | |||
should behave. | |||
When using this class, you'll probably want to create a typedef to abbreviate the full | |||
templated name - e.g. | |||
@code | |||
struct MyClass : public ReferenceCountedObject | |||
{ | |||
typedef ReferenceCountedObjectPtr<MyClass> Ptr; | |||
... | |||
@endcode | |||
@see ReferenceCountedObject, ReferenceCountedObjectArray | |||
*/ | |||
template <class ReferenceCountedObjectClass> | |||
class ReferenceCountedObjectPtr | |||
{ | |||
public: | |||
/** The class being referenced by this pointer. */ | |||
typedef ReferenceCountedObjectClass ReferencedType; | |||
//============================================================================== | |||
/** Creates a pointer to a null object. */ | |||
ReferenceCountedObjectPtr() noexcept | |||
: referencedObject (nullptr) | |||
{ | |||
} | |||
/** Creates a pointer to an object. | |||
This will increment the object's reference-count. | |||
*/ | |||
ReferenceCountedObjectPtr (ReferencedType* refCountedObject) noexcept | |||
: referencedObject (refCountedObject) | |||
{ | |||
incIfNotNull (refCountedObject); | |||
} | |||
/** Creates a pointer to a null object. */ | |||
ReferenceCountedObjectPtr (decltype (nullptr)) noexcept | |||
: referencedObject (nullptr) | |||
{ | |||
} | |||
/** Copies another pointer. | |||
This will increment the object's reference-count. | |||
*/ | |||
ReferenceCountedObjectPtr (const ReferenceCountedObjectPtr& other) noexcept | |||
: referencedObject (other.referencedObject) | |||
{ | |||
incIfNotNull (referencedObject); | |||
} | |||
/** Copies another pointer. | |||
This will increment the object's reference-count (if it is non-null). | |||
*/ | |||
template <typename Convertible> | |||
ReferenceCountedObjectPtr (const ReferenceCountedObjectPtr<Convertible>& other) noexcept | |||
: referencedObject (static_cast<ReferencedType*> (other.get())) | |||
{ | |||
incIfNotNull (referencedObject); | |||
} | |||
/** Changes this pointer to point at a different object. | |||
The reference count of the old object is decremented, and it might be | |||
deleted if it hits zero. The new object's count is incremented. | |||
*/ | |||
ReferenceCountedObjectPtr& operator= (const ReferenceCountedObjectPtr& other) | |||
{ | |||
return operator= (other.referencedObject); | |||
} | |||
/** Changes this pointer to point at a different object. | |||
The reference count of the old object is decremented, and it might be | |||
deleted if it hits zero. The new object's count is incremented. | |||
*/ | |||
template <typename Convertible> | |||
ReferenceCountedObjectPtr& operator= (const ReferenceCountedObjectPtr<Convertible>& other) | |||
{ | |||
return operator= (static_cast<ReferencedType*> (other.get())); | |||
} | |||
/** Changes this pointer to point at a different object. | |||
The reference count of the old object is decremented, and it might be | |||
deleted if it hits zero. The new object's count is incremented. | |||
*/ | |||
ReferenceCountedObjectPtr& operator= (ReferencedType* const newObject) | |||
{ | |||
if (referencedObject != newObject) | |||
{ | |||
incIfNotNull (newObject); | |||
ReferencedType* const oldObject = referencedObject; | |||
referencedObject = newObject; | |||
decIfNotNull (oldObject); | |||
} | |||
return *this; | |||
} | |||
/** Takes-over the object from another pointer. */ | |||
ReferenceCountedObjectPtr (ReferenceCountedObjectPtr&& other) noexcept | |||
: referencedObject (other.referencedObject) | |||
{ | |||
other.referencedObject = nullptr; | |||
} | |||
/** Takes-over the object from another pointer. */ | |||
ReferenceCountedObjectPtr& operator= (ReferenceCountedObjectPtr&& other) | |||
{ | |||
std::swap (referencedObject, other.referencedObject); | |||
return *this; | |||
} | |||
/** Destructor. | |||
This will decrement the object's reference-count, which will cause the | |||
object to be deleted when the ref-count hits zero. | |||
*/ | |||
~ReferenceCountedObjectPtr() | |||
{ | |||
decIfNotNull (referencedObject); | |||
} | |||
//============================================================================== | |||
/** Returns the object that this pointer references. | |||
The pointer returned may be null, of course. | |||
*/ | |||
operator ReferencedType*() 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; } | |||
// the -> operator is called on the referenced object | |||
ReferencedType* operator->() const noexcept | |||
{ | |||
jassert (referencedObject != nullptr); // null pointer method call! | |||
return referencedObject; | |||
} | |||
private: | |||
//============================================================================== | |||
ReferencedType* referencedObject; | |||
static void incIfNotNull (ReferencedType* o) noexcept | |||
{ | |||
if (o != nullptr) | |||
o->incReferenceCount(); | |||
} | |||
static void decIfNotNull (ReferencedType* o) noexcept | |||
{ | |||
if (o != nullptr && o->decReferenceCountWithoutDeleting()) | |||
ContainerDeletePolicy<ReferencedType>::destroy (o); | |||
} | |||
}; | |||
//============================================================================== | |||
/** Compares two ReferenceCountedObjectPtrs. */ | |||
template <typename ReferenceCountedObjectClass> | |||
bool operator== (const ReferenceCountedObjectPtr<ReferenceCountedObjectClass>& object1, ReferenceCountedObjectClass* const object2) noexcept | |||
{ | |||
return object1.get() == object2; | |||
} | |||
/** Compares two ReferenceCountedObjectPtrs. */ | |||
template <typename ReferenceCountedObjectClass> | |||
bool operator== (const ReferenceCountedObjectPtr<ReferenceCountedObjectClass>& object1, const ReferenceCountedObjectPtr<ReferenceCountedObjectClass>& object2) noexcept | |||
{ | |||
return object1.get() == object2.get(); | |||
} | |||
/** Compares two ReferenceCountedObjectPtrs. */ | |||
template <typename ReferenceCountedObjectClass> | |||
bool operator== (ReferenceCountedObjectClass* object1, const ReferenceCountedObjectPtr<ReferenceCountedObjectClass>& object2) noexcept | |||
{ | |||
return object1 == object2.get(); | |||
} | |||
/** Compares two ReferenceCountedObjectPtrs. */ | |||
template <typename ReferenceCountedObjectClass> | |||
bool operator!= (const ReferenceCountedObjectPtr<ReferenceCountedObjectClass>& object1, const ReferenceCountedObjectClass* object2) noexcept | |||
{ | |||
return object1.get() != object2; | |||
} | |||
/** Compares two ReferenceCountedObjectPtrs. */ | |||
template <typename ReferenceCountedObjectClass> | |||
bool operator!= (const ReferenceCountedObjectPtr<ReferenceCountedObjectClass>& object1, const ReferenceCountedObjectPtr<ReferenceCountedObjectClass>& object2) noexcept | |||
{ | |||
return object1.get() != object2.get(); | |||
} | |||
/** Compares two ReferenceCountedObjectPtrs. */ | |||
template <typename ReferenceCountedObjectClass> | |||
bool operator!= (ReferenceCountedObjectClass* object1, const ReferenceCountedObjectPtr<ReferenceCountedObjectClass>& object2) noexcept | |||
{ | |||
return object1 != object2.get(); | |||
} | |||
} // namespace juce |
@@ -1,254 +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. | |||
============================================================================== | |||
*/ | |||
namespace juce | |||
{ | |||
//============================================================================== | |||
/** | |||
This class holds a pointer which is automatically deleted when this object goes | |||
out of scope. | |||
Once a pointer has been passed to a ScopedPointer, it will make sure that the pointer | |||
gets deleted when the ScopedPointer is deleted. Using the ScopedPointer on the stack or | |||
as member variables is a good way to use RAII to avoid accidentally leaking dynamically | |||
created objects. | |||
A ScopedPointer can be used in pretty much the same way that you'd use a normal pointer | |||
to an object. If you use the assignment operator to assign a different object to a | |||
ScopedPointer, the old one will be automatically deleted. | |||
Important note: The class is designed to hold a pointer to an object, NOT to an array! | |||
It calls delete on its payload, not delete[], so do not give it an array to hold! For | |||
that kind of purpose, you should be using HeapBlock or Array instead. | |||
A const ScopedPointer is guaranteed not to lose ownership of its object or change the | |||
object to which it points during its lifetime. This means that making a copy of a const | |||
ScopedPointer is impossible, as that would involve the new copy taking ownership from the | |||
old one. | |||
If you need to get a pointer out of a ScopedPointer without it being deleted, you | |||
can use the release() method. | |||
Something to note is the main difference between this class and the std::auto_ptr class, | |||
which is that ScopedPointer provides a cast-to-object operator, whereas std::auto_ptr | |||
requires that you always call get() to retrieve the pointer. The advantages of providing | |||
the cast is that you don't need to call get(), so can use the ScopedPointer in pretty much | |||
exactly the same way as a raw pointer. The disadvantage is that the compiler is free to | |||
use the cast in unexpected and sometimes dangerous ways - in particular, it becomes difficult | |||
to return a ScopedPointer as the result of a function. To avoid this causing errors, | |||
ScopedPointer contains an overloaded constructor that should cause a syntax error in these | |||
circumstances, but it does mean that instead of returning a ScopedPointer from a function, | |||
you'd need to return a raw pointer (or use a std::auto_ptr instead). | |||
*/ | |||
template <class ObjectType> | |||
class ScopedPointer | |||
{ | |||
public: | |||
//============================================================================== | |||
/** Creates a ScopedPointer containing a null pointer. */ | |||
inline ScopedPointer() noexcept : object (nullptr) | |||
{ | |||
} | |||
/** Creates a ScopedPointer containing a null pointer. */ | |||
inline ScopedPointer (decltype (nullptr)) noexcept : object (nullptr) | |||
{ | |||
} | |||
/** Creates a ScopedPointer that owns the specified object. */ | |||
inline ScopedPointer (ObjectType* const objectToTakePossessionOf) noexcept | |||
: object (objectToTakePossessionOf) | |||
{ | |||
} | |||
/** Creates a ScopedPointer that takes its pointer from another ScopedPointer. | |||
Because a pointer can only belong to one ScopedPointer, this transfers | |||
the pointer from the other object to this one, and the other object is reset to | |||
be a null pointer. | |||
*/ | |||
ScopedPointer (ScopedPointer& objectToTransferFrom) noexcept | |||
: object (objectToTransferFrom.object) | |||
{ | |||
objectToTransferFrom.object = nullptr; | |||
} | |||
/** Destructor. | |||
This will delete the object that this ScopedPointer currently refers to. | |||
*/ | |||
inline ~ScopedPointer() { ContainerDeletePolicy<ObjectType>::destroy (object); } | |||
/** Changes this ScopedPointer to point to a new object. | |||
Because a pointer can only belong to one ScopedPointer, this transfers | |||
the pointer from the other object to this one, and the other object is reset to | |||
be a null pointer. | |||
If this ScopedPointer already points to an object, that object | |||
will first be deleted. | |||
*/ | |||
ScopedPointer& operator= (ScopedPointer& objectToTransferFrom) | |||
{ | |||
if (this != objectToTransferFrom.getAddress()) | |||
{ | |||
// Two ScopedPointers should never be able to refer to the same object - if | |||
// this happens, you must have done something dodgy! | |||
jassert (object == nullptr || object != objectToTransferFrom.object); | |||
ObjectType* const oldObject = object; | |||
object = objectToTransferFrom.object; | |||
objectToTransferFrom.object = nullptr; | |||
ContainerDeletePolicy<ObjectType>::destroy (oldObject); | |||
} | |||
return *this; | |||
} | |||
/** Changes this ScopedPointer to point to a new object. | |||
If this ScopedPointer already points to an object, that object | |||
will first be deleted. | |||
The pointer that you pass in may be a nullptr. | |||
*/ | |||
ScopedPointer& operator= (ObjectType* const newObjectToTakePossessionOf) | |||
{ | |||
if (object != newObjectToTakePossessionOf) | |||
{ | |||
ObjectType* const oldObject = object; | |||
object = newObjectToTakePossessionOf; | |||
ContainerDeletePolicy<ObjectType>::destroy (oldObject); | |||
} | |||
return *this; | |||
} | |||
/** Take ownership of another ScopedPointer */ | |||
ScopedPointer (ScopedPointer&& other) noexcept | |||
: object (other.object) | |||
{ | |||
other.object = nullptr; | |||
} | |||
/** Take ownership of another ScopedPointer */ | |||
ScopedPointer& operator= (ScopedPointer&& other) noexcept | |||
{ | |||
ContainerDeletePolicy<ObjectType>::destroy (object); | |||
object = other.object; | |||
other.object = nullptr; | |||
return *this; | |||
} | |||
//============================================================================== | |||
/** Returns the object that this ScopedPointer refers to. */ | |||
inline operator ObjectType*() const noexcept { return object; } | |||
/** Returns the object that this ScopedPointer refers to. */ | |||
inline ObjectType* get() const noexcept { return object; } | |||
/** Returns the object that this ScopedPointer refers to. */ | |||
inline ObjectType& operator*() const noexcept { return *object; } | |||
/** Lets you access methods and properties of the object that this ScopedPointer refers to. */ | |||
inline ObjectType* operator->() const noexcept { return object; } | |||
//============================================================================== | |||
/** Removes the current object from this ScopedPointer without deleting it. | |||
This will return the current object, and set the ScopedPointer to a null pointer. | |||
*/ | |||
ObjectType* release() noexcept { ObjectType* const o = object; object = nullptr; return o; } | |||
//============================================================================== | |||
/** Swaps this object with that of another ScopedPointer. | |||
The two objects simply exchange their pointers. | |||
*/ | |||
void swapWith (ScopedPointer<ObjectType>& other) noexcept | |||
{ | |||
// Two ScopedPointers should never be able to refer to the same object - if | |||
// this happens, you must have done something dodgy! | |||
jassert (object != other.object || this == other.getAddress() || object == nullptr); | |||
std::swap (object, other.object); | |||
} | |||
/** If the pointer is non-null, this will attempt to return a new copy of the object that is pointed to. | |||
If the pointer is null, this will safely return a nullptr. | |||
*/ | |||
inline ObjectType* createCopy() const { return createCopyIfNotNull (object); } | |||
private: | |||
//============================================================================== | |||
ObjectType* object; | |||
// (Required as an alternative to the overloaded & operator). | |||
const ScopedPointer* getAddress() const noexcept { return this; } | |||
#if ! JUCE_MSVC // (MSVC can't deal with multiple copy constructors) | |||
/* The copy constructors are private to stop people accidentally copying a const ScopedPointer | |||
(the compiler would let you do so by implicitly casting the source to its raw object pointer). | |||
A side effect of this is that in a compiler that doesn't support C++11, you may hit an | |||
error when you write something like this: | |||
ScopedPointer<MyClass> m = new MyClass(); // Compile error: copy constructor is private. | |||
Even though the compiler would normally ignore the assignment here, it can't do so when the | |||
copy constructor is private. It's very easy to fix though - just write it like this: | |||
ScopedPointer<MyClass> m (new MyClass()); // Compiles OK | |||
It's probably best to use the latter form when writing your object declarations anyway, as | |||
this is a better representation of the code that you actually want the compiler to produce. | |||
*/ | |||
JUCE_DECLARE_NON_COPYABLE (ScopedPointer) | |||
#endif | |||
}; | |||
//============================================================================== | |||
/** Compares a ScopedPointer with another pointer. | |||
This can be handy for checking whether this is a null pointer. | |||
*/ | |||
template <class ObjectType> | |||
bool operator== (const ScopedPointer<ObjectType>& pointer1, ObjectType* const pointer2) noexcept | |||
{ | |||
return static_cast<ObjectType*> (pointer1) == pointer2; | |||
} | |||
/** Compares a ScopedPointer with another pointer. | |||
This can be handy for checking whether this is a null pointer. | |||
*/ | |||
template <class ObjectType> | |||
bool operator!= (const ScopedPointer<ObjectType>& pointer1, ObjectType* const pointer2) noexcept | |||
{ | |||
return static_cast<ObjectType*> (pointer1) != pointer2; | |||
} | |||
//============================================================================== | |||
#ifndef DOXYGEN | |||
// NB: This is just here to prevent any silly attempts to call deleteAndZero() on a ScopedPointer. | |||
template <typename Type> | |||
void deleteAndZero (ScopedPointer<Type>&) { static_assert (sizeof (Type) == 12345, | |||
"Attempt to call deleteAndZero() on a ScopedPointer"); } | |||
#endif | |||
} // namespace juce |
@@ -1,162 +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. | |||
============================================================================== | |||
*/ | |||
namespace juce | |||
{ | |||
//============================================================================== | |||
/** | |||
A smart-pointer that automatically creates and manages the lifetime of a | |||
shared static instance of a class. | |||
The SharedObjectType template type indicates the class to use for the shared | |||
object - the only requirements on this class are that it must have a public | |||
default constructor and destructor. | |||
The SharedResourcePointer offers a pattern that differs from using a singleton or | |||
static instance of an object, because it uses reference-counting to make sure that | |||
the underlying shared object is automatically created/destroyed according to the | |||
number of SharedResourcePointer objects that exist. When the last one is deleted, | |||
the underlying object is also immediately destroyed. This allows you to use scoping | |||
to manage the lifetime of a shared resource. | |||
Note: the construction/deletion of the shared object must not involve any | |||
code that makes recursive calls to a SharedResourcePointer, or you'll cause | |||
a deadlock. | |||
Example: | |||
@code | |||
// An example of a class that contains the shared data you want to use. | |||
struct MySharedData | |||
{ | |||
// There's no need to ever create an instance of this class directly yourself, | |||
// but it does need a public constructor that does the initialisation. | |||
MySharedData() | |||
{ | |||
sharedStuff = generateHeavyweightStuff(); | |||
} | |||
Array<SomeKindOfData> sharedStuff; | |||
}; | |||
struct DataUserClass | |||
{ | |||
DataUserClass() | |||
{ | |||
// Multiple instances of the DataUserClass will all have the same | |||
// shared common instance of MySharedData referenced by their sharedData | |||
// member variables. | |||
useSharedStuff (sharedData->sharedStuff); | |||
} | |||
// By keeping this pointer as a member variable, the shared resource | |||
// is guaranteed to be available for as long as the DataUserClass object. | |||
SharedResourcePointer<MySharedData> sharedData; | |||
}; | |||
@endcode | |||
*/ | |||
template <typename SharedObjectType> | |||
class SharedResourcePointer | |||
{ | |||
public: | |||
/** Creates an instance of the shared object. | |||
If other SharedResourcePointer objects for this type already exist, then | |||
this one will simply point to the same shared object that they are already | |||
using. Otherwise, if this is the first SharedResourcePointer to be created, | |||
then a shared object will be created automatically. | |||
*/ | |||
SharedResourcePointer() | |||
{ | |||
initialise(); | |||
} | |||
SharedResourcePointer (const SharedResourcePointer&) | |||
{ | |||
initialise(); | |||
} | |||
/** Destructor. | |||
If no other SharedResourcePointer objects exist, this will also delete | |||
the shared object to which it refers. | |||
*/ | |||
~SharedResourcePointer() | |||
{ | |||
auto& holder = getSharedObjectHolder(); | |||
const SpinLock::ScopedLockType sl (holder.lock); | |||
if (--(holder.refCount) == 0) | |||
holder.sharedInstance = nullptr; | |||
} | |||
/** Returns the shared object. */ | |||
operator SharedObjectType*() const noexcept { return sharedObject; } | |||
/** Returns the shared object. */ | |||
SharedObjectType& get() const noexcept { return *sharedObject; } | |||
/** Returns the object that this pointer references. | |||
The pointer returned may be a nullptr, of course. | |||
*/ | |||
SharedObjectType& getObject() const noexcept { return *sharedObject; } | |||
/** Returns the shared object. */ | |||
SharedObjectType* operator->() const noexcept { return sharedObject; } | |||
/** Returns the number of SharedResourcePointers that are currently holding the shared object. */ | |||
int getReferenceCount() const noexcept { return getSharedObjectHolder().refCount; } | |||
private: | |||
struct SharedObjectHolder | |||
{ | |||
SpinLock lock; | |||
ScopedPointer<SharedObjectType> sharedInstance; | |||
int refCount; | |||
}; | |||
static SharedObjectHolder& getSharedObjectHolder() noexcept | |||
{ | |||
static void* holder [(sizeof (SharedObjectHolder) + sizeof(void*) - 1) / sizeof(void*)] = { 0 }; | |||
return *reinterpret_cast<SharedObjectHolder*> (holder); | |||
} | |||
SharedObjectType* sharedObject; | |||
void initialise() | |||
{ | |||
auto& holder = getSharedObjectHolder(); | |||
const SpinLock::ScopedLockType sl (holder.lock); | |||
if (++(holder.refCount) == 1) | |||
holder.sharedInstance = new SharedObjectType(); | |||
sharedObject = holder.sharedInstance; | |||
} | |||
// There's no need to assign to a SharedResourcePointer because every | |||
// instance of the class is exactly the same! | |||
SharedResourcePointer& operator= (const SharedResourcePointer&) = delete; | |||
JUCE_LEAK_DETECTOR (SharedResourcePointer) | |||
}; | |||
} // namespace juce |
@@ -1,283 +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. | |||
============================================================================== | |||
*/ | |||
namespace juce | |||
{ | |||
//============================================================================== | |||
/** | |||
Macro to declare member variables and methods for a singleton class. | |||
To use this, add the line juce_DeclareSingleton (MyClass, doNotRecreateAfterDeletion) | |||
to the class's definition. | |||
Then put a macro juce_ImplementSingleton (MyClass) along with the class's | |||
implementation code. | |||
It's also a very good idea to also add the call clearSingletonInstance() in your class's | |||
destructor, in case it is deleted by other means than deleteInstance() | |||
Clients can then call the static method MyClass::getInstance() to get a pointer | |||
to the singleton, or MyClass::getInstanceWithoutCreating() which will return nullptr if | |||
no instance currently exists. | |||
e.g. @code | |||
class MySingleton | |||
{ | |||
public: | |||
MySingleton() | |||
{ | |||
} | |||
~MySingleton() | |||
{ | |||
// this ensures that no dangling pointers are left when the | |||
// singleton is deleted. | |||
clearSingletonInstance(); | |||
} | |||
juce_DeclareSingleton (MySingleton, false) | |||
}; | |||
juce_ImplementSingleton (MySingleton) | |||
// example of usage: | |||
MySingleton* m = MySingleton::getInstance(); // creates the singleton if there isn't already one. | |||
... | |||
MySingleton::deleteInstance(); // safely deletes the singleton (if it's been created). | |||
@endcode | |||
If doNotRecreateAfterDeletion = true, it won't allow the object to be created more | |||
than once during the process's lifetime - i.e. after you've created and deleted the | |||
object, getInstance() will refuse to create another one. This can be useful to stop | |||
objects being accidentally re-created during your app's shutdown code. | |||
If you know that your object will only be created and deleted by a single thread, you | |||
can use the slightly more efficient juce_DeclareSingleton_SingleThreaded() macro instead | |||
of this one. | |||
@see juce_ImplementSingleton, juce_DeclareSingleton_SingleThreaded | |||
*/ | |||
#define juce_DeclareSingleton(classname, doNotRecreateAfterDeletion) \ | |||
\ | |||
static classname* _singletonInstance; \ | |||
static juce::CriticalSection _singletonLock; \ | |||
\ | |||
static classname* JUCE_CALLTYPE getInstance() \ | |||
{ \ | |||
if (_singletonInstance == nullptr) \ | |||
{\ | |||
const juce::ScopedLock sl (_singletonLock); \ | |||
\ | |||
if (_singletonInstance == nullptr) \ | |||
{ \ | |||
static bool alreadyInside = false; \ | |||
static bool createdOnceAlready = false; \ | |||
\ | |||
const bool problem = alreadyInside || ((doNotRecreateAfterDeletion) && createdOnceAlready); \ | |||
jassert (! problem); \ | |||
if (! problem) \ | |||
{ \ | |||
createdOnceAlready = true; \ | |||
alreadyInside = true; \ | |||
classname* newObject = new classname(); /* (use a stack variable to avoid setting the newObject value before the class has finished its constructor) */ \ | |||
alreadyInside = false; \ | |||
\ | |||
_singletonInstance = newObject; \ | |||
} \ | |||
} \ | |||
} \ | |||
\ | |||
return _singletonInstance; \ | |||
} \ | |||
\ | |||
static inline classname* JUCE_CALLTYPE getInstanceWithoutCreating() noexcept\ | |||
{ \ | |||
return _singletonInstance; \ | |||
} \ | |||
\ | |||
static void JUCE_CALLTYPE deleteInstance() \ | |||
{ \ | |||
const juce::ScopedLock sl (_singletonLock); \ | |||
if (_singletonInstance != nullptr) \ | |||
{ \ | |||
classname* const old = _singletonInstance; \ | |||
_singletonInstance = nullptr; \ | |||
delete old; \ | |||
} \ | |||
} \ | |||
\ | |||
void clearSingletonInstance() noexcept\ | |||
{ \ | |||
if (_singletonInstance == this) \ | |||
_singletonInstance = nullptr; \ | |||
} | |||
//============================================================================== | |||
/** This is a counterpart to the juce_DeclareSingleton macro. | |||
After adding the juce_DeclareSingleton to the class definition, this macro has | |||
to be used in the cpp file. | |||
*/ | |||
#define juce_ImplementSingleton(classname) \ | |||
\ | |||
classname* classname::_singletonInstance = nullptr; \ | |||
juce::CriticalSection classname::_singletonLock; | |||
//============================================================================== | |||
/** | |||
Macro to declare member variables and methods for a singleton class. | |||
This is exactly the same as juce_DeclareSingleton, but doesn't use a critical | |||
section to make access to it thread-safe. If you know that your object will | |||
only ever be created or deleted by a single thread, then this is a | |||
more efficient version to use. | |||
If doNotRecreateAfterDeletion = true, it won't allow the object to be created more | |||
than once during the process's lifetime - i.e. after you've created and deleted the | |||
object, getInstance() will refuse to create another one. This can be useful to stop | |||
objects being accidentally re-created during your app's shutdown code. | |||
See the documentation for juce_DeclareSingleton for more information about | |||
how to use it, the only difference being that you have to use | |||
juce_ImplementSingleton_SingleThreaded instead of juce_ImplementSingleton. | |||
@see juce_ImplementSingleton_SingleThreaded, juce_DeclareSingleton, juce_DeclareSingleton_SingleThreaded_Minimal | |||
*/ | |||
#define juce_DeclareSingleton_SingleThreaded(classname, doNotRecreateAfterDeletion) \ | |||
\ | |||
static classname* _singletonInstance; \ | |||
\ | |||
static classname* getInstance() \ | |||
{ \ | |||
if (_singletonInstance == nullptr) \ | |||
{ \ | |||
static bool alreadyInside = false; \ | |||
static bool createdOnceAlready = false; \ | |||
\ | |||
const bool problem = alreadyInside || ((doNotRecreateAfterDeletion) && createdOnceAlready); \ | |||
jassert (! problem); \ | |||
if (! problem) \ | |||
{ \ | |||
createdOnceAlready = true; \ | |||
alreadyInside = true; \ | |||
classname* newObject = new classname(); /* (use a stack variable to avoid setting the newObject value before the class has finished its constructor) */ \ | |||
alreadyInside = false; \ | |||
\ | |||
_singletonInstance = newObject; \ | |||
} \ | |||
} \ | |||
\ | |||
return _singletonInstance; \ | |||
} \ | |||
\ | |||
static inline classname* getInstanceWithoutCreating() noexcept\ | |||
{ \ | |||
return _singletonInstance; \ | |||
} \ | |||
\ | |||
static void deleteInstance() \ | |||
{ \ | |||
if (_singletonInstance != nullptr) \ | |||
{ \ | |||
classname* const old = _singletonInstance; \ | |||
_singletonInstance = nullptr; \ | |||
delete old; \ | |||
} \ | |||
} \ | |||
\ | |||
void clearSingletonInstance() noexcept\ | |||
{ \ | |||
if (_singletonInstance == this) \ | |||
_singletonInstance = nullptr; \ | |||
} | |||
//============================================================================== | |||
/** | |||
Macro to declare member variables and methods for a singleton class. | |||
This is like juce_DeclareSingleton_SingleThreaded, but doesn't do any checking | |||
for recursion or repeated instantiation. It's intended for use as a lightweight | |||
version of a singleton, where you're using it in very straightforward | |||
circumstances and don't need the extra checking. | |||
Juce use the normal juce_ImplementSingleton_SingleThreaded as the counterpart | |||
to this declaration, as you would with juce_DeclareSingleton_SingleThreaded. | |||
See the documentation for juce_DeclareSingleton for more information about | |||
how to use it, the only difference being that you have to use | |||
juce_ImplementSingleton_SingleThreaded instead of juce_ImplementSingleton. | |||
@see juce_ImplementSingleton_SingleThreaded, juce_DeclareSingleton | |||
*/ | |||
#define juce_DeclareSingleton_SingleThreaded_Minimal(classname) \ | |||
\ | |||
static classname* _singletonInstance; \ | |||
\ | |||
static classname* getInstance() \ | |||
{ \ | |||
if (_singletonInstance == nullptr) \ | |||
_singletonInstance = new classname(); \ | |||
\ | |||
return _singletonInstance; \ | |||
} \ | |||
\ | |||
static inline classname* getInstanceWithoutCreating() noexcept\ | |||
{ \ | |||
return _singletonInstance; \ | |||
} \ | |||
\ | |||
static void deleteInstance() \ | |||
{ \ | |||
if (_singletonInstance != nullptr) \ | |||
{ \ | |||
classname* const old = _singletonInstance; \ | |||
_singletonInstance = nullptr; \ | |||
delete old; \ | |||
} \ | |||
} \ | |||
\ | |||
void clearSingletonInstance() noexcept\ | |||
{ \ | |||
if (_singletonInstance == this) \ | |||
_singletonInstance = nullptr; \ | |||
} | |||
//============================================================================== | |||
/** This is a counterpart to the juce_DeclareSingleton_SingleThreaded macro. | |||
After adding juce_DeclareSingleton_SingleThreaded or juce_DeclareSingleton_SingleThreaded_Minimal | |||
to the class definition, this macro has to be used somewhere in the cpp file. | |||
*/ | |||
#define juce_ImplementSingleton_SingleThreaded(classname) \ | |||
\ | |||
classname* classname::_singletonInstance = nullptr; | |||
} // namespace juce |
@@ -1,234 +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. | |||
============================================================================== | |||
*/ | |||
namespace juce | |||
{ | |||
//============================================================================== | |||
/** | |||
This class acts as a pointer which will automatically become null if the object | |||
to which it points is deleted. | |||
To accomplish this, the source object needs to cooperate by performing a couple of simple tasks. | |||
It must embed a WeakReference::Master object, which stores a shared pointer object, and must clear | |||
this master pointer in its destructor. | |||
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() | |||
{ | |||
// This will zero all the references - you need to call this in your destructor. | |||
masterReference.clear(); | |||
} | |||
private: | |||
// You need to embed a variable of this type, with the name "masterReference" inside your object. If the | |||
// variable is not public, you should make your class a friend of WeakReference<MyObject> so that the | |||
// WeakReference class can access it. | |||
WeakReference<MyObject>::Master masterReference; | |||
friend class WeakReference<MyObject>; | |||
}; | |||
OR: just use the handy JUCE_DECLARE_WEAK_REFERENCEABLE macro to do all this for you. | |||
// Here's an example of using a pointer.. | |||
MyObject* n = new MyObject(); | |||
WeakReference<MyObject> myObjectRef = n; | |||
MyObject* pointer1 = myObjectRef; // returns a valid pointer to 'n' | |||
delete n; | |||
MyObject* pointer2 = myObjectRef; // returns a null pointer | |||
@endcode | |||
@see WeakReference::Master | |||
*/ | |||
template <class ObjectType, class ReferenceCountingType = ReferenceCountedObject> | |||
class WeakReference | |||
{ | |||
public: | |||
/** Creates a null SafePointer. */ | |||
inline WeakReference() noexcept {} | |||
/** Creates a WeakReference that points at the given object. */ | |||
WeakReference (ObjectType* const object) : holder (getRef (object)) {} | |||
/** Creates a copy of another WeakReference. */ | |||
WeakReference (const WeakReference& other) noexcept : holder (other.holder) {} | |||
/** Move constructor */ | |||
WeakReference (WeakReference&& other) noexcept : holder (static_cast<SharedRef&&> (other.holder)) {} | |||
/** Copies another pointer to this one. */ | |||
WeakReference& operator= (const WeakReference& other) { holder = other.holder; return *this; } | |||
/** Copies another pointer to this one. */ | |||
WeakReference& operator= (ObjectType* const newObject) { holder = getRef (newObject); return *this; } | |||
/** Move assignment operator */ | |||
WeakReference& operator= (WeakReference&& other) noexcept { holder = static_cast<SharedRef&&> (other.holder); return *this; } | |||
/** Returns the object that this pointer refers to, or null if the object no longer exists. */ | |||
ObjectType* get() const noexcept { return holder != nullptr ? holder->get() : nullptr; } | |||
/** Returns the object that this pointer refers to, or null if the object no longer exists. */ | |||
operator ObjectType*() const noexcept { return get(); } | |||
/** Returns the object that this pointer refers to, or null if the object no longer exists. */ | |||
ObjectType* operator->() noexcept { return get(); } | |||
/** Returns the object that this pointer refers to, or null if the object no longer exists. */ | |||
const ObjectType* operator->() const noexcept { return get(); } | |||
/** This returns true if this reference has been pointing at an object, but that object has | |||
since been deleted. | |||
If this reference was only ever pointing at a null pointer, this will return false. Using | |||
operator=() to make this refer to a different object will reset this flag to match the status | |||
of the reference from which you're copying. | |||
*/ | |||
bool wasObjectDeleted() const noexcept { return holder != nullptr && holder->get() == nullptr; } | |||
bool operator== (ObjectType* const object) const noexcept { return get() == object; } | |||
bool operator!= (ObjectType* const object) const noexcept { return get() != object; } | |||
//============================================================================== | |||
/** This class is used internally by the WeakReference class - don't use it directly | |||
in your code! | |||
@see WeakReference | |||
*/ | |||
class SharedPointer : public ReferenceCountingType | |||
{ | |||
public: | |||
explicit SharedPointer (ObjectType* const obj) noexcept : owner (obj) {} | |||
inline ObjectType* get() const noexcept { return owner; } | |||
void clearPointer() noexcept { owner = nullptr; } | |||
private: | |||
ObjectType* volatile owner; | |||
JUCE_DECLARE_NON_COPYABLE (SharedPointer) | |||
}; | |||
typedef ReferenceCountedObjectPtr<SharedPointer> SharedRef; | |||
//============================================================================== | |||
/** | |||
This class is embedded inside an object to which you want to attach WeakReference pointers. | |||
See the WeakReference class notes for an example of how to use this class. | |||
@see WeakReference | |||
*/ | |||
class Master | |||
{ | |||
public: | |||
Master() noexcept {} | |||
~Master() noexcept | |||
{ | |||
// You must remember to call clear() in your source object's destructor! See the notes | |||
// for the WeakReference class for an example of how to do this. | |||
jassert (sharedPointer == nullptr || sharedPointer->get() == nullptr); | |||
} | |||
/** The first call to this method will create an internal object that is shared by all weak | |||
references to the object. | |||
*/ | |||
SharedPointer* getSharedPointer (ObjectType* const object) | |||
{ | |||
if (sharedPointer == nullptr) | |||
{ | |||
sharedPointer = new SharedPointer (object); | |||
} | |||
else | |||
{ | |||
// You're trying to create a weak reference to an object that has already been deleted!! | |||
jassert (sharedPointer->get() != nullptr); | |||
} | |||
return sharedPointer; | |||
} | |||
/** The object that owns this master pointer should call this before it gets destroyed, | |||
to zero all the references to this object that may be out there. See the WeakReference | |||
class notes for an example of how to do this. | |||
*/ | |||
void clear() noexcept | |||
{ | |||
if (sharedPointer != nullptr) | |||
sharedPointer->clearPointer(); | |||
} | |||
private: | |||
SharedRef sharedPointer; | |||
JUCE_DECLARE_NON_COPYABLE (Master) | |||
}; | |||
private: | |||
SharedRef holder; | |||
static inline SharedPointer* getRef (ObjectType* const o) | |||
{ | |||
return (o != nullptr) ? o->masterReference.getSharedPointer (o) : nullptr; | |||
} | |||
}; | |||
//============================================================================== | |||
/** | |||
Macro to easily allow a class to be made weak-referenceable. | |||
This can be inserted in a class definition to add the requisite weak-ref boilerplate to that class. | |||
e.g. | |||
@code | |||
class MyObject | |||
{ | |||
public: | |||
MyObject(); | |||
~MyObject(); | |||
private: | |||
JUCE_DECLARE_WEAK_REFERENCEABLE (MyObject) | |||
}; | |||
@endcode | |||
@see WeakReference, WeakReference::Master | |||
*/ | |||
#define JUCE_DECLARE_WEAK_REFERENCEABLE(Class) \ | |||
struct WeakRefMaster : public WeakReference<Class>::Master { ~WeakRefMaster() { this->clear(); } }; \ | |||
WeakRefMaster masterReference; \ | |||
friend class WeakReference<Class>; \ | |||
} // namespace juce |
@@ -1,80 +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. | |||
============================================================================== | |||
*/ | |||
namespace juce | |||
{ | |||
Result::Result() noexcept {} | |||
Result::Result (const String& message) noexcept | |||
: errorMessage (message) | |||
{ | |||
} | |||
Result::Result (const Result& other) | |||
: errorMessage (other.errorMessage) | |||
{ | |||
} | |||
Result& Result::operator= (const Result& other) | |||
{ | |||
errorMessage = other.errorMessage; | |||
return *this; | |||
} | |||
Result::Result (Result&& other) noexcept | |||
: errorMessage (static_cast<String&&> (other.errorMessage)) | |||
{ | |||
} | |||
Result& Result::operator= (Result&& other) noexcept | |||
{ | |||
errorMessage = static_cast<String&&> (other.errorMessage); | |||
return *this; | |||
} | |||
bool Result::operator== (const Result& other) const noexcept | |||
{ | |||
return errorMessage == other.errorMessage; | |||
} | |||
bool Result::operator!= (const Result& other) const noexcept | |||
{ | |||
return errorMessage != other.errorMessage; | |||
} | |||
Result Result::fail (const String& errorMessage) noexcept | |||
{ | |||
return Result (errorMessage.isEmpty() ? "Unknown Error" : errorMessage); | |||
} | |||
const String& Result::getErrorMessage() const noexcept | |||
{ | |||
return errorMessage; | |||
} | |||
bool Result::wasOk() const noexcept { return errorMessage.isEmpty(); } | |||
Result::operator bool() const noexcept { return errorMessage.isEmpty(); } | |||
bool Result::failed() const noexcept { return errorMessage.isNotEmpty(); } | |||
bool Result::operator!() const noexcept { return errorMessage.isNotEmpty(); } | |||
} // namespace juce |
@@ -1,114 +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. | |||
============================================================================== | |||
*/ | |||
namespace juce | |||
{ | |||
//============================================================================== | |||
/** | |||
Represents the 'success' or 'failure' of an operation, and holds an associated | |||
error message to describe the error when there's a failure. | |||
E.g. | |||
@code | |||
Result myOperation() | |||
{ | |||
if (doSomeKindOfFoobar()) | |||
return Result::ok(); | |||
else | |||
return Result::fail ("foobar didn't work!"); | |||
} | |||
const Result result (myOperation()); | |||
if (result.wasOk()) | |||
{ | |||
...it's all good... | |||
} | |||
else | |||
{ | |||
warnUserAboutFailure ("The foobar operation failed! Error message was: " | |||
+ result.getErrorMessage()); | |||
} | |||
@endcode | |||
*/ | |||
class JUCE_API Result | |||
{ | |||
public: | |||
//============================================================================== | |||
/** Creates and returns a 'successful' result. */ | |||
static Result ok() noexcept { return Result(); } | |||
/** Creates a 'failure' result. | |||
If you pass a blank error message in here, a default "Unknown Error" message | |||
will be used instead. | |||
*/ | |||
static Result fail (const String& errorMessage) noexcept; | |||
//============================================================================== | |||
/** Returns true if this result indicates a success. */ | |||
bool wasOk() const noexcept; | |||
/** Returns true if this result indicates a failure. | |||
You can use getErrorMessage() to retrieve the error message associated | |||
with the failure. | |||
*/ | |||
bool failed() const noexcept; | |||
/** Returns true if this result indicates a success. | |||
This is equivalent to calling wasOk(). | |||
*/ | |||
operator bool() const noexcept; | |||
/** Returns true if this result indicates a failure. | |||
This is equivalent to calling failed(). | |||
*/ | |||
bool operator!() const noexcept; | |||
/** Returns the error message that was set when this result was created. | |||
For a successful result, this will be an empty string; | |||
*/ | |||
const String& getErrorMessage() const noexcept; | |||
//============================================================================== | |||
Result (const Result&); | |||
Result& operator= (const Result&); | |||
Result (Result&&) noexcept; | |||
Result& operator= (Result&&) noexcept; | |||
bool operator== (const Result& other) const noexcept; | |||
bool operator!= (const Result& other) const noexcept; | |||
private: | |||
String errorMessage; | |||
// The default constructor is not for public use! | |||
// Instead, use Result::ok() or Result::fail() | |||
Result() noexcept; | |||
explicit Result (const String&) noexcept; | |||
// These casts are private to prevent people trying to use the Result object in numeric contexts | |||
operator int() const; | |||
operator void*() const; | |||
}; | |||
} // namespace juce |
@@ -1,36 +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. | |||
============================================================================== | |||
*/ | |||
namespace juce | |||
{ | |||
#if ! JUCE_ANDROID // We currently don't request runtime permissions on any other platform | |||
// than Android, so this file contains a dummy implementation for those. | |||
// This may change in the future. | |||
void RuntimePermissions::request (PermissionID, Callback callback) { callback (true); } | |||
bool RuntimePermissions::isRequired (PermissionID) { return false; } | |||
bool RuntimePermissions::isGranted (PermissionID) { return true; } | |||
#endif | |||
} // namespace juce |
@@ -1,126 +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. | |||
============================================================================== | |||
*/ | |||
namespace juce | |||
{ | |||
//============================================================================== | |||
/** | |||
Class to handle app runtime permissions for certain functionality on some platforms. | |||
The use of this class is currently only required if the app should run on | |||
Android API level 23 and higher. | |||
On lower API levels, the permissions are specified in the app manifest. On iOS, | |||
runtime permission requests are handled automatically by the Apple APIs and not | |||
manually in the app code. On Windows, OS X, and Linux, runtime permissions are not | |||
used at all. In all these cases, request() will simply call through to the | |||
callback with no overhead and pass true (making it safe to use on all platforms). | |||
For example, to enable audio recording on Android in your cross-platform app, | |||
you could modify your code as follows: | |||
Old code: | |||
audioDeviceManager.initialise (2, 2, nullptr, true, String(), nullptr); | |||
New code: | |||
RuntimePermissions::request ( | |||
RuntimePermissions::audioRecording, | |||
[this] (bool wasGranted) | |||
{ | |||
if (! wasGranted) | |||
{ | |||
// e.g. display an error or initialise with 0 input channels | |||
return; | |||
} | |||
audioDeviceManager.initialise (2, 2, nullptr, true, String(), nullptr); | |||
} | |||
); | |||
*/ | |||
class JUCE_API RuntimePermissions | |||
{ | |||
public: | |||
//============================================================================== | |||
enum PermissionID | |||
{ | |||
/** Permission to access the microphone (required on Android). | |||
You need to request this, for example, to initialise an AudioDeviceManager with | |||
a non-zero number of input channels, and to open the default audio input device. | |||
*/ | |||
recordAudio = 1, | |||
/** Permission to scan for and pair to Bluetooth MIDI devices (required on Android). | |||
You need to request this before calling BluetoothMidiDevicePairingDialogue::open(), | |||
otherwise no devices will be found. | |||
*/ | |||
bluetoothMidi = 2, | |||
/** Permission to read from external storage such as SD cards */ | |||
readExternalStorage = 3, | |||
/** Permission to write to external storage such as SD cards */ | |||
writeExternalStorage = 4 | |||
}; | |||
//============================================================================== | |||
/** Function type of runtime permission request callbacks. */ | |||
typedef std::function<void (bool)> Callback; | |||
//============================================================================== | |||
/** Call this method to request a runtime permission. | |||
@param permission The PermissionID of the permission you want to request. | |||
@param callback The callback to be called after the request has been granted | |||
or denied; the argument passed will be true if the permission | |||
has been granted and false otherwise. | |||
If no runtime request is required or possible to obtain the permission, the | |||
callback will be called immediately. The argument passed in will be true | |||
if the permission is granted or no permission is required on this platform, | |||
and false otherwise. | |||
If a runtime request is required to obtain the permission, the callback | |||
will be called asynchronously after the OS has granted or denied the requested | |||
permission (typically by displaying a dialog box to the user and waiting until | |||
the user has responded). | |||
*/ | |||
static void request (PermissionID permission, Callback callback); | |||
/** Returns whether a runtime request is required to obtain the permission | |||
on the current platform. | |||
*/ | |||
static bool isRequired (PermissionID permission); | |||
/** Returns true if the app has been already granted this permission, either | |||
via a previous runtime request or otherwise, or no permission is necessary. | |||
Note that this can be false even if isRequired returns false. In this case, | |||
the permission can not be obtained at all at runtime. | |||
*/ | |||
static bool isGranted (PermissionID permission); | |||
}; | |||
} // namespace juce |
@@ -1,259 +0,0 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2017 - ROLI Ltd. | |||
Permission is granted to use this software 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. | |||
THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH REGARD | |||
TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND | |||
FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, | |||
OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF | |||
USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER | |||
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE | |||
OF THIS SOFTWARE. | |||
----------------------------------------------------------------------------- | |||
To release a closed-source product which uses other parts of JUCE not | |||
licensed under the ISC terms, commercial licenses are available: visit | |||
www.juce.com for more information. | |||
============================================================================== | |||
*/ | |||
namespace juce | |||
{ | |||
#if JUCE_UNIT_TESTS | |||
namespace FunctionTestsHelpers | |||
{ | |||
void incrementArgument (int& x) { x++; } | |||
double multiply (double x, double a) noexcept { return a * x; } | |||
struct BigData | |||
{ | |||
BigData() | |||
{ | |||
for (auto i = 0; i < bigDataSize; ++i) | |||
content[i] = i + 1; | |||
} | |||
int sum() const | |||
{ | |||
int result = 0; | |||
for (auto i = 0; i < bigDataSize; ++i) | |||
result += content[i]; | |||
return result; | |||
} | |||
static const int bigDataSize = 32, | |||
bigDataSum = bigDataSize * (bigDataSize + 1) / 2; | |||
int content[bigDataSize]; | |||
}; | |||
struct FunctionObject | |||
{ | |||
FunctionObject() {} | |||
FunctionObject (const FunctionObject& other) | |||
{ | |||
bigData = new BigData (*other.bigData); | |||
} | |||
int operator()(int i) const { return bigData->sum() + i; } | |||
ScopedPointer<BigData> bigData { new BigData() }; | |||
}; | |||
} | |||
class FunctionTests : public UnitTest | |||
{ | |||
public: | |||
FunctionTests() : UnitTest ("Function", "Function") {} | |||
void runTest() override | |||
{ | |||
FunctionTestsHelpers::BigData bigData; | |||
{ | |||
beginTest ("Functions"); | |||
std::function<void(int&)> f1 (FunctionTestsHelpers::incrementArgument); | |||
auto x = 0; | |||
f1 (x); | |||
expectEquals (x, 1); | |||
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); | |||
} | |||
{ | |||
beginTest ("Lambdas"); | |||
std::function<int()> fStack ([]() { return 3; }); | |||
expectEquals (fStack(), 3); | |||
std::function<int()> fHeap ([=]() { return bigData.sum(); }); | |||
expectEquals (fHeap(), FunctionTestsHelpers::BigData::bigDataSum); | |||
} | |||
{ | |||
beginTest ("Boolean"); | |||
std::function<void(int&)> f1; | |||
if (f1) | |||
expect (false); | |||
std::function<int()> f2 ([]() { return 3; }); | |||
if (! f2) | |||
expect (false); | |||
} | |||
std::function<int()> fEmpty; | |||
std::function<int()> fStack ([]() { return 3; }); | |||
std::function<int()> fHeap ([=]() { return bigData.sum(); }); | |||
{ | |||
beginTest ("copy constructor"); | |||
std::function<int()> f1 (fStack); | |||
expectEquals (f1(), 3); | |||
std::function<int()> f2 (fHeap); | |||
expectEquals (f2(), FunctionTestsHelpers::BigData::bigDataSum); | |||
std::function<int()> f3 (fEmpty); | |||
if (f3) | |||
expect (false); | |||
} | |||
{ | |||
beginTest ("assignment"); | |||
std::function<int()> f1; | |||
f1 = fStack; | |||
expectEquals (f1(), 3); | |||
std::function<int()> f2; | |||
f2 = fHeap; | |||
expectEquals (f2(), FunctionTestsHelpers::BigData::bigDataSum); | |||
f1 = fHeap; | |||
expectEquals (f1(), FunctionTestsHelpers::BigData::bigDataSum); | |||
f2 = fStack; | |||
expectEquals (f2(), 3); | |||
f1 = fEmpty; | |||
if (f1) | |||
expect (false); | |||
} | |||
{ | |||
beginTest ("move constructor"); | |||
ScopedPointer<std::function<int()>> fStackTmp (new std::function<int()> (fStack)); | |||
std::function<int()> f1 (static_cast<std::function<int()>&&> (*fStackTmp)); | |||
fStackTmp = nullptr; | |||
expectEquals (f1(), 3); | |||
ScopedPointer<std::function<int()>> fHeapTmp (new std::function<int()> (fHeap)); | |||
std::function<int()> f2 (static_cast<std::function<int()>&&> (*fHeapTmp)); | |||
if (*fHeapTmp) | |||
expect (false); | |||
fHeapTmp = nullptr; | |||
expectEquals (f2(), FunctionTestsHelpers::BigData::bigDataSum); | |||
ScopedPointer<std::function<int()>> fEmptyTmp (new std::function<int()>()); | |||
std::function<int()> f3 (static_cast<std::function<int()>&&> (*fEmptyTmp)); | |||
fEmptyTmp = nullptr; | |||
if (f3) | |||
expect (false); | |||
} | |||
{ | |||
beginTest ("move assignment"); | |||
std::function<int()> f1 (fHeap); | |||
ScopedPointer<std::function<int()>> fStackTmp (new std::function<int()> (fStack)); | |||
f1 = static_cast<std::function<int()>&&> (*fStackTmp); | |||
fStackTmp = nullptr; | |||
expectEquals (f1(), 3); | |||
std::function<int()> f2 (fStack); | |||
ScopedPointer<std::function<int()>> fHeapTmp (new std::function<int()> (fHeap)); | |||
f2 = static_cast<std::function<int()>&&> (*fHeapTmp); | |||
if (*fHeapTmp) | |||
expect (false); | |||
fHeapTmp = nullptr; | |||
expectEquals (f2(), FunctionTestsHelpers::BigData::bigDataSum); | |||
std::function<int()> f3 (fHeap); | |||
ScopedPointer<std::function<int()>> fEmptyTmp (new std::function<int()>()); | |||
f3 = static_cast<std::function<int()>&&> (*fEmptyTmp); | |||
fEmptyTmp = nullptr; | |||
if (f3) | |||
expect (false); | |||
} | |||
{ | |||
beginTest ("nullptr"); | |||
std::function<int()> f1 (nullptr); | |||
if (f1) | |||
expect (false); | |||
std::function<int()> f2 ([]() { return 11; }); | |||
f2 = nullptr; | |||
if (f2) | |||
expect (false); | |||
} | |||
{ | |||
beginTest ("Swap"); | |||
std::function<int()> f1; | |||
std::function<int()> f2 (fStack); | |||
f2.swap (f1); | |||
expectEquals (f1(), 3); | |||
if (f2) | |||
expect (false); | |||
std::function<int()> f3 (fHeap); | |||
f3.swap (f1); | |||
expectEquals (f3(), 3); | |||
expectEquals (f1(), FunctionTestsHelpers::BigData::bigDataSum); | |||
} | |||
} | |||
}; | |||
static FunctionTests functionTests; | |||
#endif | |||
} // namespace juce |
@@ -1,207 +0,0 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2017 - ROLI Ltd. | |||
Permission is granted to use this software 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. | |||
THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH REGARD | |||
TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND | |||
FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, | |||
OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF | |||
USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER | |||
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE | |||
OF THIS SOFTWARE. | |||
----------------------------------------------------------------------------- | |||
To release a closed-source product which uses other parts of JUCE not | |||
licensed under the ISC terms, commercial licenses are available: visit | |||
www.juce.com for more information. | |||
============================================================================== | |||
*/ | |||
namespace std | |||
{ | |||
/** | |||
This class provides an alternative to std::function that is compatible | |||
with OS X 10.6 and earlier. This will only be used in OS X versions 10.6 | |||
and earlier and the Projucer live build. | |||
*/ | |||
template <typename> | |||
class function; | |||
template <typename Result, typename... Arguments> | |||
class function<Result (Arguments...)> | |||
{ | |||
public: | |||
/** Creates an empty function. */ | |||
function() noexcept {} | |||
/** Creates an empty function. */ | |||
function (decltype (nullptr)) noexcept {} | |||
/** Creates a function targetting the provided Functor. */ | |||
template <typename Functor> | |||
function (Functor f) | |||
{ | |||
functorHolderHelper = getFunctorStorage (sizeof (FunctorHolder<Functor, Result, Arguments...>)); | |||
new (functorHolderHelper) FunctorHolder<Functor, Result, Arguments...> (f); | |||
} | |||
/** Copy constructor. */ | |||
function (function const& other) | |||
{ | |||
copy (other); | |||
} | |||
/** Move constructor */ | |||
function (function&& other) | |||
{ | |||
move (other); | |||
} | |||
/** Destructor. */ | |||
~function() | |||
{ | |||
release(); | |||
} | |||
/** Replaces the contents of this function with the contents of another. */ | |||
function& operator= (function const& other) | |||
{ | |||
release(); | |||
copy (other); | |||
return *this; | |||
} | |||
/** Moves the contents of another function into this one. */ | |||
function& operator= (function&& other) | |||
{ | |||
release(); | |||
move (other); | |||
return *this; | |||
} | |||
/** Allows conditional expressions to test if this function is empty. */ | |||
explicit operator bool() const noexcept | |||
{ | |||
return functorHolderHelper != nullptr; | |||
} | |||
/** Swaps the contents of this function with another. After this operation the | |||
two functions will be pointing at each other's targets. */ | |||
void swap (function& other) | |||
{ | |||
function<Result (Arguments...)> tmp (*this); | |||
*this = other; | |||
other = tmp; | |||
} | |||
/** Invokes the target of this function. */ | |||
Result operator() (Arguments... args) const | |||
{ | |||
return (*functorHolderHelper) (args...); | |||
} | |||
bool operator== (decltype (nullptr)) const noexcept { return (functorHolderHelper == nullptr); } | |||
bool operator!= (decltype (nullptr)) const noexcept { return (functorHolderHelper != nullptr); } | |||
private: | |||
//============================================================================== | |||
template <typename ReturnType, typename... Args> | |||
struct FunctorHolderBase | |||
{ | |||
virtual ~FunctorHolderBase() {} | |||
virtual int getSize() const noexcept = 0; | |||
virtual void copy (void*) const = 0; | |||
virtual ReturnType operator()(Args...) = 0; | |||
}; | |||
template <typename Functor, typename ReturnType, typename... Args> | |||
struct FunctorHolder : FunctorHolderBase<Result, Arguments...> | |||
{ | |||
FunctorHolder (Functor func) : f (func) {} | |||
int getSize() const noexcept override final | |||
{ | |||
return sizeof (*this); | |||
} | |||
void copy (void* destination) const override final | |||
{ | |||
new (destination) FunctorHolder (f); | |||
} | |||
ReturnType operator()(Args... args) override final | |||
{ | |||
return f (args...); | |||
} | |||
Functor f; | |||
}; | |||
FunctorHolderBase<Result, Arguments...>* getFunctorStorage (int size) | |||
{ | |||
return reinterpret_cast<FunctorHolderBase<Result, Arguments...>*> | |||
(size > functorHolderStackSize ? new char [size] | |||
: &(stackFunctorStorage[0])); | |||
} | |||
void copy (function const& other) | |||
{ | |||
if (other.functorHolderHelper != nullptr) | |||
{ | |||
functorHolderHelper = getFunctorStorage (other.functorHolderHelper->getSize()); | |||
other.functorHolderHelper->copy (functorHolderHelper); | |||
} | |||
} | |||
void move (function& other) | |||
{ | |||
if (other.functorHolderHelper != nullptr) | |||
{ | |||
if (other.functorHolderHelper->getSize() > functorHolderStackSize) | |||
{ | |||
functorHolderHelper = other.functorHolderHelper; | |||
} | |||
else | |||
{ | |||
std::copy (other.stackFunctorStorage, other.stackFunctorStorage + functorHolderStackSize, | |||
stackFunctorStorage); | |||
functorHolderHelper = reinterpret_cast<FunctorHolderBase<Result, Arguments...>*> (&(stackFunctorStorage[0])); | |||
} | |||
other.functorHolderHelper = nullptr; | |||
} | |||
} | |||
void release() | |||
{ | |||
if (functorHolderHelper != nullptr) | |||
{ | |||
if (functorHolderHelper->getSize() > functorHolderStackSize) | |||
delete[] reinterpret_cast<char*> (functorHolderHelper); | |||
else | |||
functorHolderHelper->~FunctorHolderBase<Result, Arguments...>(); | |||
functorHolderHelper = nullptr; | |||
} | |||
} | |||
static const int functorHolderStackSize = 24; | |||
char stackFunctorStorage[functorHolderStackSize]; | |||
FunctorHolderBase<Result, Arguments...>* functorHolderHelper = nullptr; | |||
}; | |||
} |
@@ -1,137 +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. | |||
============================================================================== | |||
*/ | |||
namespace juce | |||
{ | |||
Uuid::Uuid() | |||
{ | |||
Random r; | |||
for (size_t i = 0; i < sizeof (uuid); ++i) | |||
uuid[i] = (uint8) (r.nextInt (256)); | |||
// To make it RFC 4122 compliant, need to force a few bits... | |||
uuid[6] = (uuid[6] & 0x0f) | 0x40; | |||
uuid[8] = (uuid[8] & 0x3f) | 0x80; | |||
} | |||
Uuid::~Uuid() noexcept {} | |||
Uuid::Uuid (const Uuid& other) noexcept | |||
{ | |||
memcpy (uuid, other.uuid, sizeof (uuid)); | |||
} | |||
Uuid& Uuid::operator= (const Uuid& other) noexcept | |||
{ | |||
memcpy (uuid, other.uuid, sizeof (uuid)); | |||
return *this; | |||
} | |||
bool Uuid::operator== (const Uuid& other) const noexcept { return memcmp (uuid, other.uuid, sizeof (uuid)) == 0; } | |||
bool Uuid::operator!= (const Uuid& other) const noexcept { return ! operator== (other); } | |||
bool Uuid::operator< (const Uuid& other) const noexcept { return compare (other) < 0; } | |||
bool Uuid::operator> (const Uuid& other) const noexcept { return compare (other) > 0; } | |||
bool Uuid::operator<= (const Uuid& other) const noexcept { return compare (other) <= 0; } | |||
bool Uuid::operator>= (const Uuid& other) const noexcept { return compare (other) >= 0; } | |||
int Uuid::compare (Uuid other) const noexcept | |||
{ | |||
for (size_t i = 0; i < sizeof (uuid); ++i) | |||
if (int diff = uuid[i] - (int) other.uuid[i]) | |||
return diff > 0 ? 1 : -1; | |||
return 0; | |||
} | |||
Uuid Uuid::null() noexcept | |||
{ | |||
return Uuid ((const uint8*) nullptr); | |||
} | |||
bool Uuid::isNull() const noexcept | |||
{ | |||
for (auto i : uuid) | |||
if (i != 0) | |||
return false; | |||
return true; | |||
} | |||
String Uuid::getHexRegion (int start, int length) const | |||
{ | |||
return String::toHexString (uuid + start, length, 0); | |||
} | |||
String Uuid::toString() const | |||
{ | |||
return getHexRegion (0, 16); | |||
} | |||
String Uuid::toDashedString() const | |||
{ | |||
return getHexRegion (0, 4) | |||
+ "-" + getHexRegion (4, 2) | |||
+ "-" + getHexRegion (6, 2) | |||
+ "-" + getHexRegion (8, 2) | |||
+ "-" + getHexRegion (10, 6); | |||
} | |||
Uuid::Uuid (const String& uuidString) | |||
{ | |||
operator= (uuidString); | |||
} | |||
Uuid& Uuid::operator= (const String& uuidString) | |||
{ | |||
MemoryBlock mb; | |||
mb.loadFromHexString (uuidString); | |||
mb.ensureSize (sizeof (uuid), true); | |||
mb.copyTo (uuid, 0, sizeof (uuid)); | |||
return *this; | |||
} | |||
Uuid::Uuid (const uint8* const rawData) noexcept | |||
{ | |||
operator= (rawData); | |||
} | |||
Uuid& Uuid::operator= (const uint8* const rawData) noexcept | |||
{ | |||
if (rawData != nullptr) | |||
memcpy (uuid, rawData, sizeof (uuid)); | |||
else | |||
zeromem (uuid, sizeof (uuid)); | |||
return *this; | |||
} | |||
uint32 Uuid::getTimeLow() const noexcept { return ByteOrder::bigEndianInt (uuid); } | |||
uint16 Uuid::getTimeMid() const noexcept { return ByteOrder::bigEndianShort (uuid + 4); } | |||
uint16 Uuid::getTimeHighAndVersion() const noexcept { return ByteOrder::bigEndianShort (uuid + 6); } | |||
uint8 Uuid::getClockSeqAndReserved() const noexcept { return uuid[8]; } | |||
uint8 Uuid::getClockSeqLow() const noexcept { return uuid[9]; } | |||
uint64 Uuid::getNode() const noexcept { return (((uint64) ByteOrder::bigEndianShort (uuid + 10)) << 32) + ByteOrder::bigEndianInt (uuid + 12); } | |||
} // namespace juce |
@@ -1,132 +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. | |||
============================================================================== | |||
*/ | |||
namespace juce | |||
{ | |||
//============================================================================== | |||
/** | |||
A universally unique 128-bit identifier. | |||
This class generates very random unique numbers. It's vanishingly unlikely | |||
that two identical UUIDs would ever be created by chance. The values are | |||
formatted to meet the RFC 4122 version 4 standard. | |||
The class includes methods for saving the ID as a string or as raw binary data. | |||
*/ | |||
class JUCE_API Uuid | |||
{ | |||
public: | |||
//============================================================================== | |||
/** Creates a new unique ID, compliant with RFC 4122 version 4. */ | |||
Uuid(); | |||
/** Destructor. */ | |||
~Uuid() noexcept; | |||
/** Creates a copy of another UUID. */ | |||
Uuid (const Uuid&) noexcept; | |||
/** Copies another UUID. */ | |||
Uuid& operator= (const Uuid&) noexcept; | |||
//============================================================================== | |||
/** Returns true if the ID is zero. */ | |||
bool isNull() const noexcept; | |||
/** Returns a null Uuid object. */ | |||
static Uuid null() noexcept; | |||
bool operator== (const Uuid&) const noexcept; | |||
bool operator!= (const Uuid&) const noexcept; | |||
bool operator< (const Uuid&) const noexcept; | |||
bool operator> (const Uuid&) const noexcept; | |||
bool operator<= (const Uuid&) const noexcept; | |||
bool operator>= (const Uuid&) const noexcept; | |||
//============================================================================== | |||
/** Returns a stringified version of this UUID. | |||
A Uuid object can later be reconstructed from this string using operator= or | |||
the constructor that takes a string parameter. | |||
@returns a 32 character hex string. | |||
*/ | |||
String toString() const; | |||
/** Returns a stringified version of this UUID, separating it into sections with dashes. | |||
@returns a string in the format: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx | |||
*/ | |||
String toDashedString() const; | |||
/** Creates an ID from an encoded string version. | |||
@see toString | |||
*/ | |||
Uuid (const String& uuidString); | |||
/** Copies from a stringified UUID. | |||
The string passed in should be one that was created with the toString() method. | |||
*/ | |||
Uuid& operator= (const String& uuidString); | |||
//============================================================================== | |||
/** Returns the time-low section of the UUID. */ | |||
uint32 getTimeLow() const noexcept; | |||
/** Returns the time-mid section of the UUID. */ | |||
uint16 getTimeMid() const noexcept; | |||
/** Returns the time-high-and-version section of the UUID. */ | |||
uint16 getTimeHighAndVersion() const noexcept; | |||
/** Returns the clock-seq-and-reserved section of the UUID. */ | |||
uint8 getClockSeqAndReserved() const noexcept; | |||
/** Returns the clock-seq-low section of the UUID. */ | |||
uint8 getClockSeqLow() const noexcept; | |||
/** Returns the node section of the UUID. */ | |||
uint64 getNode() const noexcept; | |||
//============================================================================== | |||
/** Returns a pointer to the internal binary representation of the ID. | |||
This is an array of 16 bytes. To reconstruct a Uuid from its data, use | |||
the constructor or operator= method that takes an array of uint8s. | |||
*/ | |||
const uint8* getRawData() const noexcept { return uuid; } | |||
/** Creates a UUID from a 16-byte array. | |||
@see getRawData | |||
*/ | |||
Uuid (const uint8* rawData) noexcept; | |||
/** Sets this UUID from 16-bytes of raw data. */ | |||
Uuid& operator= (const uint8* rawData) noexcept; | |||
private: | |||
//============================================================================== | |||
uint8 uuid[16]; | |||
String getHexRegion (int, int) const; | |||
int compare (Uuid) const noexcept; | |||
JUCE_LEAK_DETECTOR (Uuid) | |||
}; | |||
} // namespace juce |
@@ -1,136 +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. | |||
============================================================================== | |||
*/ | |||
namespace juce | |||
{ | |||
#if JUCE_WINDOWS || DOXYGEN | |||
/** | |||
Contains some static helper functions for manipulating the MS Windows registry | |||
(Only available on Windows, of course!) | |||
*/ | |||
class JUCE_API WindowsRegistry | |||
{ | |||
public: | |||
/** These values can be used to specify whether the 32- or 64-bit registry should be used. | |||
When running on a 32-bit OS, there is no 64-bit registry, so the mode will be ignored. | |||
*/ | |||
enum WoW64Mode | |||
{ | |||
/** Default handling: 32-bit apps will use the 32-bit registry, and 64-bit apps | |||
will use the 64-bit registry. */ | |||
WoW64_Default = 0, | |||
/** Always use the 64-bit registry store. (KEY_WOW64_64KEY). */ | |||
WoW64_64bit = 0x100, | |||
/** Always use the 32-bit registry store. (KEY_WOW64_32KEY). */ | |||
WoW64_32bit = 0x200 | |||
}; | |||
//============================================================================== | |||
/** Returns a string from the registry. | |||
The path is a string for the entire path of a value in the registry, | |||
e.g. "HKEY_CURRENT_USER\Software\foo\bar" | |||
*/ | |||
static String JUCE_CALLTYPE getValue (const String& regValuePath, | |||
const String& defaultValue = String(), | |||
WoW64Mode mode = WoW64_Default); | |||
/** Reads a binary block from the registry. | |||
The path is a string for the entire path of a value in the registry, | |||
e.g. "HKEY_CURRENT_USER\Software\foo\bar" | |||
@returns a DWORD indicating the type of the key. | |||
*/ | |||
static uint32 JUCE_CALLTYPE getBinaryValue (const String& regValuePath, MemoryBlock& resultData, WoW64Mode mode = WoW64_Default); | |||
/** Sets a registry value as a string. | |||
This will take care of creating any groups needed to get to the given registry value. | |||
*/ | |||
static bool JUCE_CALLTYPE setValue (const String& regValuePath, const String& value, WoW64Mode mode = WoW64_Default); | |||
/** Sets a registry value as a DWORD. | |||
This will take care of creating any groups needed to get to the given registry value. | |||
*/ | |||
static bool JUCE_CALLTYPE setValue (const String& regValuePath, uint32 value, WoW64Mode mode = WoW64_Default); | |||
/** Sets a registry value as a QWORD. | |||
This will take care of creating any groups needed to get to the given registry value. | |||
*/ | |||
static bool JUCE_CALLTYPE setValue (const String& regValuePath, uint64 value, WoW64Mode mode = WoW64_Default); | |||
/** Sets a registry value as a binary block. | |||
This will take care of creating any groups needed to get to the given registry value. | |||
*/ | |||
static bool JUCE_CALLTYPE setValue (const String& regValuePath, const MemoryBlock& value, WoW64Mode mode = WoW64_Default); | |||
/** Returns true if the given value exists in the registry. */ | |||
static bool JUCE_CALLTYPE valueExists (const String& regValuePath, WoW64Mode mode = WoW64_Default); | |||
/** Returns true if the given key exists in the registry. */ | |||
static bool JUCE_CALLTYPE keyExists (const String& regValuePath, WoW64Mode mode = WoW64_Default); | |||
/** Deletes a registry value. */ | |||
static bool JUCE_CALLTYPE deleteValue (const String& regValuePath, WoW64Mode mode = WoW64_Default); | |||
/** Deletes a registry key (which is registry-talk for 'folder'). */ | |||
static bool JUCE_CALLTYPE deleteKey (const String& regKeyPath, WoW64Mode mode = WoW64_Default); | |||
/** Creates a file association in the registry. | |||
This lets you set the executable that should be launched by a given file extension. | |||
@param fileExtension the file extension to associate, including the | |||
initial dot, e.g. ".txt" | |||
@param symbolicDescription a space-free short token to identify the file type | |||
@param fullDescription a human-readable description of the file type | |||
@param targetExecutable the executable that should be launched | |||
@param iconResourceNumber the icon that gets displayed for the file type will be | |||
found by looking up this resource number in the | |||
executable. Pass 0 here to not use an icon | |||
@param registerForCurrentUserOnly if false, this will try to register the association | |||
for all users (you might not have permission to do this | |||
unless running in an installer). If true, it will register the | |||
association in HKEY_CURRENT_USER. | |||
@param mode the WoW64 mode to use for choosing the database | |||
*/ | |||
static bool JUCE_CALLTYPE registerFileAssociation (const String& fileExtension, | |||
const String& symbolicDescription, | |||
const String& fullDescription, | |||
const File& targetExecutable, | |||
int iconResourceNumber, | |||
bool registerForCurrentUserOnly, | |||
WoW64Mode mode = WoW64_Default); | |||
// DEPRECATED: use the other methods with a WoW64Mode parameter of WoW64_64bit instead. | |||
JUCE_DEPRECATED (static String getValueWow64 (const String&, const String& defaultValue = String())); | |||
JUCE_DEPRECATED (static bool valueExistsWow64 (const String&)); | |||
JUCE_DEPRECATED (static bool keyExistsWow64 (const String&)); | |||
private: | |||
WindowsRegistry() JUCE_DELETED_FUNCTION; | |||
JUCE_DECLARE_NON_COPYABLE (WindowsRegistry) | |||
}; | |||
#endif | |||
} // namespace juce |
@@ -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,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; | |||
} |
@@ -1,266 +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. | |||
============================================================================== | |||
*/ | |||
#pragma once | |||
#undef T | |||
//============================================================================== | |||
#if JUCE_MAC || JUCE_IOS | |||
#if JUCE_IOS | |||
#import <Foundation/Foundation.h> | |||
#import <UIKit/UIKit.h> | |||
#import <CoreData/CoreData.h> | |||
#import <MobileCoreServices/MobileCoreServices.h> | |||
#include <sys/fcntl.h> | |||
#else | |||
#import <Cocoa/Cocoa.h> | |||
#if (! defined MAC_OS_X_VERSION_10_12) || MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_12 | |||
#define NSEventModifierFlagCommand NSCommandKeyMask | |||
#define NSEventModifierFlagControl NSControlKeyMask | |||
#define NSEventModifierFlagHelp NSHelpKeyMask | |||
#define NSEventModifierFlagNumericPad NSNumericPadKeyMask | |||
#define NSEventModifierFlagOption NSAlternateKeyMask | |||
#define NSEventModifierFlagShift NSShiftKeyMask | |||
#define NSCompositingOperationSourceOver NSCompositeSourceOver | |||
#define NSEventMaskApplicationDefined NSApplicationDefinedMask | |||
#define NSEventTypeApplicationDefined NSApplicationDefined | |||
#define NSEventTypeCursorUpdate NSCursorUpdate | |||
#define NSEventTypeMouseMoved NSMouseMoved | |||
#define NSEventTypeLeftMouseDown NSLeftMouseDown | |||
#define NSEventTypeRightMouseDown NSRightMouseDown | |||
#define NSEventTypeOtherMouseDown NSOtherMouseDown | |||
#define NSEventTypeLeftMouseUp NSLeftMouseUp | |||
#define NSEventTypeRightMouseUp NSRightMouseUp | |||
#define NSEventTypeOtherMouseUp NSOtherMouseUp | |||
#define NSEventTypeLeftMouseDragged NSLeftMouseDragged | |||
#define NSEventTypeRightMouseDragged NSRightMouseDragged | |||
#define NSEventTypeOtherMouseDragged NSOtherMouseDragged | |||
#define NSEventTypeScrollWheel NSScrollWheel | |||
#define NSEventTypeKeyDown NSKeyDown | |||
#define NSEventTypeKeyUp NSKeyUp | |||
#define NSEventTypeFlagsChanged NSFlagsChanged | |||
#define NSEventMaskAny NSAnyEventMask | |||
#define NSWindowStyleMaskBorderless NSBorderlessWindowMask | |||
#define NSWindowStyleMaskClosable NSClosableWindowMask | |||
#define NSWindowStyleMaskFullScreen NSFullScreenWindowMask | |||
#define NSWindowStyleMaskMiniaturizable NSMiniaturizableWindowMask | |||
#define NSWindowStyleMaskResizable NSResizableWindowMask | |||
#define NSWindowStyleMaskTitled NSTitledWindowMask | |||
#define NSAlertStyleCritical NSCriticalAlertStyle | |||
#define NSControlSizeRegular NSRegularControlSize | |||
#define NSEventTypeMouseEntered NSMouseEntered | |||
#define NSEventTypeMouseExited NSMouseExited | |||
#define NSAlertStyleInformational NSInformationalAlertStyle | |||
#define NSEventTypeTabletPoint NSTabletPoint | |||
#define NSEventTypeTabletProximity NSTabletProximity | |||
#endif | |||
#import <CoreAudio/HostTime.h> | |||
#include <sys/dir.h> | |||
#endif | |||
#include <sys/socket.h> | |||
#include <sys/sysctl.h> | |||
#include <sys/stat.h> | |||
#include <sys/param.h> | |||
#include <sys/mount.h> | |||
#include <sys/utsname.h> | |||
#include <sys/mman.h> | |||
#include <fnmatch.h> | |||
#include <utime.h> | |||
#include <dlfcn.h> | |||
#include <ifaddrs.h> | |||
#include <net/if_dl.h> | |||
#include <mach/mach_time.h> | |||
#include <mach-o/dyld.h> | |||
#include <objc/runtime.h> | |||
#include <objc/objc.h> | |||
#include <objc/message.h> | |||
//============================================================================== | |||
#elif JUCE_WINDOWS | |||
#if JUCE_MSVC | |||
#ifndef _CPPRTTI | |||
#error "You're compiling without RTTI enabled! This is needed for a lot of JUCE classes, please update your compiler settings!" | |||
#endif | |||
#ifndef _CPPUNWIND | |||
#error "You're compiling without exceptions enabled! This is needed for a lot of JUCE classes, please update your compiler settings!" | |||
#endif | |||
#pragma warning (push, 0) // disable all warnings whilst including system headers | |||
#endif | |||
#define NOMINMAX | |||
#define _WINSOCK_DEPRECATED_NO_WARNINGS 1 | |||
#define STRICT 1 | |||
#define WIN32_LEAN_AND_MEAN 1 | |||
#if JUCE_MINGW | |||
#define _WIN32_WINNT 0x0502 | |||
#else | |||
#define _WIN32_WINNT 0x0602 | |||
#endif | |||
#define _UNICODE 1 | |||
#define UNICODE 1 | |||
#ifndef _WIN32_IE | |||
#define _WIN32_IE 0x0500 | |||
#endif | |||
#include <windows.h> | |||
#include <shellapi.h> | |||
#include <tchar.h> | |||
#include <stddef.h> | |||
#include <ctime> | |||
#include <wininet.h> | |||
#include <nb30.h> | |||
#include <winsock2.h> | |||
#include <ws2tcpip.h> | |||
#include <iphlpapi.h> | |||
#include <mapi.h> | |||
#include <float.h> | |||
#include <process.h> | |||
#include <shlobj.h> | |||
#include <shlwapi.h> | |||
#include <mmsystem.h> | |||
#if JUCE_MINGW | |||
#include <basetyps.h> | |||
#include <sys/time.h> | |||
#ifndef alloca | |||
#define alloca __builtin_alloca | |||
#endif | |||
#else | |||
#include <crtdbg.h> | |||
#include <comutil.h> | |||
#endif | |||
#ifndef S_FALSE | |||
#define S_FALSE (1) // (apparently some obscure win32 dev environments don't define this) | |||
#endif | |||
#undef PACKED | |||
#if JUCE_MSVC | |||
#pragma warning (pop) | |||
#pragma warning (4: 4511 4512 4100) | |||
#endif | |||
#if JUCE_MSVC && ! JUCE_DONT_AUTOLINK_TO_WIN32_LIBRARIES | |||
#pragma comment (lib, "kernel32.lib") | |||
#pragma comment (lib, "user32.lib") | |||
#pragma comment (lib, "wininet.lib") | |||
#pragma comment (lib, "advapi32.lib") | |||
#pragma comment (lib, "ws2_32.lib") | |||
#pragma comment (lib, "version.lib") | |||
#pragma comment (lib, "shlwapi.lib") | |||
#pragma comment (lib, "winmm.lib") | |||
#ifdef _NATIVE_WCHAR_T_DEFINED | |||
#ifdef _DEBUG | |||
#pragma comment (lib, "comsuppwd.lib") | |||
#else | |||
#pragma comment (lib, "comsuppw.lib") | |||
#endif | |||
#else | |||
#ifdef _DEBUG | |||
#pragma comment (lib, "comsuppd.lib") | |||
#else | |||
#pragma comment (lib, "comsupp.lib") | |||
#endif | |||
#endif | |||
#endif | |||
/* Used with DynamicLibrary to simplify importing functions from a win32 DLL. | |||
dll: the DynamicLibrary object | |||
functionName: function to import | |||
localFunctionName: name you want to use to actually call it (must be different) | |||
returnType: the return type | |||
params: list of params (bracketed) | |||
*/ | |||
#define JUCE_LOAD_WINAPI_FUNCTION(dll, functionName, localFunctionName, returnType, params) \ | |||
typedef returnType (WINAPI *type##localFunctionName) params; \ | |||
type##localFunctionName localFunctionName = (type##localFunctionName) dll.getFunction (#functionName); | |||
//============================================================================== | |||
#elif JUCE_LINUX | |||
#include <sched.h> | |||
#include <pthread.h> | |||
#include <sys/time.h> | |||
#include <errno.h> | |||
#include <sys/stat.h> | |||
#include <sys/dir.h> | |||
#include <sys/ptrace.h> | |||
#include <sys/vfs.h> | |||
#include <sys/wait.h> | |||
#include <sys/mman.h> | |||
#include <fnmatch.h> | |||
#include <utime.h> | |||
#include <pwd.h> | |||
#include <fcntl.h> | |||
#include <dlfcn.h> | |||
#include <netdb.h> | |||
#include <arpa/inet.h> | |||
#include <netinet/in.h> | |||
#include <sys/types.h> | |||
#include <sys/ioctl.h> | |||
#include <sys/socket.h> | |||
#include <net/if.h> | |||
#include <sys/sysinfo.h> | |||
#include <sys/file.h> | |||
#include <sys/prctl.h> | |||
#include <signal.h> | |||
#include <stddef.h> | |||
//============================================================================== | |||
#elif JUCE_ANDROID | |||
#include <jni.h> | |||
#include <pthread.h> | |||
#include <sched.h> | |||
#include <sys/time.h> | |||
#include <utime.h> | |||
#include <errno.h> | |||
#include <fcntl.h> | |||
#include <dlfcn.h> | |||
#include <sys/stat.h> | |||
#include <sys/statfs.h> | |||
#include <sys/ptrace.h> | |||
#include <sys/sysinfo.h> | |||
#include <sys/mman.h> | |||
#include <pwd.h> | |||
#include <dirent.h> | |||
#include <fnmatch.h> | |||
#include <sys/wait.h> | |||
#include <android/api-level.h> | |||
// If you are getting include errors here, then you to re-build the Projucer | |||
// and re-save your .jucer file. | |||
#include <cpu-features.h> | |||
#endif | |||
// Need to clear various moronic redefinitions made by system headers.. | |||
#undef max | |||
#undef min | |||
#undef direct | |||
#undef check |
@@ -1,196 +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. | |||
============================================================================== | |||
*/ | |||
namespace juce | |||
{ | |||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \ | |||
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"); | |||
#undef JNI_CLASS_MEMBERS | |||
//============================================================================== | |||
class MediaScannerConnectionClient : public AndroidInterfaceImplementer | |||
{ | |||
public: | |||
virtual void onMediaScannerConnected() = 0; | |||
virtual void onScanCompleted() = 0; | |||
private: | |||
jobject invoke (jobject proxy, jobject method, jobjectArray args) override | |||
{ | |||
auto* env = getEnv(); | |||
auto methodName = juceString ((jstring) env->CallObjectMethod (method, Method.getName)); | |||
if (methodName == "onMediaScannerConnected") | |||
{ | |||
onMediaScannerConnected(); | |||
return nullptr; | |||
} | |||
else if (methodName == "onScanCompleted") | |||
{ | |||
onScanCompleted(); | |||
return nullptr; | |||
} | |||
return AndroidInterfaceImplementer::invoke (proxy, method, args); | |||
} | |||
}; | |||
//============================================================================== | |||
bool File::isOnCDRomDrive() const | |||
{ | |||
return false; | |||
} | |||
bool File::isOnHardDisk() const | |||
{ | |||
return true; | |||
} | |||
bool File::isOnRemovableDrive() const | |||
{ | |||
return false; | |||
} | |||
String File::getVersion() const | |||
{ | |||
return {}; | |||
} | |||
static File getSpecialFile (jmethodID type) | |||
{ | |||
return File (juceString (LocalRef<jstring> ((jstring) getEnv()->CallStaticObjectMethod (JuceAppActivity, type)))); | |||
} | |||
File File::getSpecialLocation (const SpecialLocationType type) | |||
{ | |||
switch (type) | |||
{ | |||
case userHomeDirectory: | |||
case userApplicationDataDirectory: | |||
case userDesktopDirectory: | |||
case commonApplicationDataDirectory: | |||
return File (android.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 globalApplicationsDirectory: | |||
return File ("/system/app"); | |||
case tempDirectory: | |||
{ | |||
File tmp = File (android.appDataDir).getChildFile (".temp"); | |||
tmp.createDirectory(); | |||
return File (tmp.getFullPathName()); | |||
} | |||
case invokedExecutableFile: | |||
case currentExecutableFile: | |||
case currentApplicationFile: | |||
case hostApplicationPath: | |||
return juce_getExecutableFile(); | |||
default: | |||
jassertfalse; // unknown type? | |||
break; | |||
} | |||
return {}; | |||
} | |||
bool File::moveToTrash() const | |||
{ | |||
if (! exists()) | |||
return true; | |||
// TODO | |||
return false; | |||
} | |||
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()); | |||
return true; | |||
} | |||
void File::revealToUser() const | |||
{ | |||
} | |||
//============================================================================== | |||
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())), | |||
file (filename) | |||
{ | |||
getEnv()->CallVoidMethod (msc.get(), MediaScannerConnection.connect); | |||
} | |||
void onMediaScannerConnected() override | |||
{ | |||
auto* env = getEnv(); | |||
env->CallVoidMethod (msc.get(), MediaScannerConnection.scanFile, javaString (file).get(), 0); | |||
} | |||
void onScanCompleted() override | |||
{ | |||
getEnv()->CallVoidMethod (msc.get(), MediaScannerConnection.disconnect); | |||
} | |||
private: | |||
GlobalRef msc; | |||
String file; | |||
}; | |||
void FileOutputStream::flushInternal() | |||
{ | |||
if (fileHandle != 0) | |||
{ | |||
if (fsync (getFD (fileHandle)) == -1) | |||
status = getResultForErrno(); | |||
// This stuff tells the OS to asynchronously update the metadata | |||
// that the OS has cached aboud the file - this metadata is used | |||
// when the device is acting as a USB drive, and unless it's explicitly | |||
// refreshed, it'll get out of step with the real file. | |||
new SingleMediaScanner (file.getFullPathName()); | |||
} | |||
} | |||
} // namespace juce |
@@ -1,434 +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. | |||
============================================================================== | |||
*/ | |||
namespace juce | |||
{ | |||
#if ! (defined (JUCE_ANDROID_ACTIVITY_CLASSNAME) && defined (JUCE_ANDROID_ACTIVITY_CLASSPATH)) | |||
#error "The JUCE_ANDROID_ACTIVITY_CLASSNAME and JUCE_ANDROID_ACTIVITY_CLASSPATH macros must be set!" | |||
#endif | |||
//============================================================================== | |||
extern JNIEnv* getEnv() noexcept; | |||
// You should rarely need to use this function. Only if you expect callbacks | |||
// on a java thread which you did not create yourself. | |||
extern void setEnv (JNIEnv* env) noexcept; | |||
/* @internal */ | |||
extern JNIEnv* attachAndroidJNI() noexcept; | |||
//============================================================================== | |||
class GlobalRef | |||
{ | |||
public: | |||
inline GlobalRef() noexcept : obj (0) {} | |||
inline explicit GlobalRef (jobject o) : obj (retain (o)) {} | |||
inline GlobalRef (const GlobalRef& other) : obj (retain (other.obj)) {} | |||
inline GlobalRef (GlobalRef && other) noexcept : obj (0) { std::swap (other.obj, obj); } | |||
~GlobalRef() { clear(); } | |||
inline void clear() | |||
{ | |||
if (obj != 0) | |||
{ | |||
getEnv()->DeleteGlobalRef (obj); | |||
obj = 0; | |||
} | |||
} | |||
inline GlobalRef& operator= (const GlobalRef& other) | |||
{ | |||
jobject newObj = retain (other.obj); | |||
clear(); | |||
obj = newObj; | |||
return *this; | |||
} | |||
inline GlobalRef& operator= (GlobalRef&& other) | |||
{ | |||
clear(); | |||
std::swap (obj, other.obj); | |||
return *this; | |||
} | |||
//============================================================================== | |||
inline operator jobject() const noexcept { return obj; } | |||
inline jobject get() const noexcept { return obj; } | |||
//============================================================================== | |||
#define DECLARE_CALL_TYPE_METHOD(returnType, typeName) \ | |||
returnType call##typeName##Method (jmethodID methodID, ... ) const \ | |||
{ \ | |||
va_list args; \ | |||
va_start (args, methodID); \ | |||
returnType result = getEnv()->Call##typeName##MethodV (obj, methodID, args); \ | |||
va_end (args); \ | |||
return result; \ | |||
} | |||
DECLARE_CALL_TYPE_METHOD (jobject, Object) | |||
DECLARE_CALL_TYPE_METHOD (jboolean, Boolean) | |||
DECLARE_CALL_TYPE_METHOD (jbyte, Byte) | |||
DECLARE_CALL_TYPE_METHOD (jchar, Char) | |||
DECLARE_CALL_TYPE_METHOD (jshort, Short) | |||
DECLARE_CALL_TYPE_METHOD (jint, Int) | |||
DECLARE_CALL_TYPE_METHOD (jlong, Long) | |||
DECLARE_CALL_TYPE_METHOD (jfloat, Float) | |||
DECLARE_CALL_TYPE_METHOD (jdouble, Double) | |||
#undef DECLARE_CALL_TYPE_METHOD | |||
void callVoidMethod (jmethodID methodID, ... ) const | |||
{ | |||
va_list args; | |||
va_start (args, methodID); | |||
getEnv()->CallVoidMethodV (obj, methodID, args); | |||
va_end (args); | |||
} | |||
private: | |||
//============================================================================== | |||
jobject obj = 0; | |||
static inline jobject retain (jobject obj) | |||
{ | |||
return obj == 0 ? 0 : getEnv()->NewGlobalRef (obj); | |||
} | |||
}; | |||
//============================================================================== | |||
template <typename JavaType> | |||
class LocalRef | |||
{ | |||
public: | |||
explicit inline LocalRef () noexcept : obj (0) {} | |||
explicit inline LocalRef (JavaType o) noexcept : obj (o) {} | |||
inline LocalRef (const LocalRef& other) noexcept : obj (retain (other.obj)) {} | |||
inline LocalRef (LocalRef&& other) noexcept : obj (0) { std::swap (obj, other.obj); } | |||
~LocalRef() { clear(); } | |||
void clear() | |||
{ | |||
if (obj != 0) | |||
{ | |||
getEnv()->DeleteLocalRef (obj); | |||
obj = 0; | |||
} | |||
} | |||
LocalRef& operator= (const LocalRef& other) | |||
{ | |||
jobject newObj = retain (other.obj); | |||
clear(); | |||
obj = newObj; | |||
return *this; | |||
} | |||
LocalRef& operator= (LocalRef&& other) | |||
{ | |||
clear(); | |||
std::swap (other.obj, obj); | |||
return *this; | |||
} | |||
inline operator JavaType() const noexcept { return obj; } | |||
inline JavaType get() const noexcept { return obj; } | |||
private: | |||
JavaType obj; | |||
static JavaType retain (JavaType obj) | |||
{ | |||
return obj == 0 ? 0 : (JavaType) getEnv()->NewLocalRef (obj); | |||
} | |||
}; | |||
//============================================================================== | |||
namespace | |||
{ | |||
inline String juceString (JNIEnv* env, jstring s) | |||
{ | |||
const char* const utf8 = env->GetStringUTFChars (s, nullptr); | |||
CharPointer_UTF8 utf8CP (utf8); | |||
const String result (utf8CP); | |||
env->ReleaseStringUTFChars (s, utf8); | |||
return result; | |||
} | |||
inline String juceString (jstring s) | |||
{ | |||
return juceString (getEnv(), s); | |||
} | |||
inline LocalRef<jstring> javaString (const String& s) | |||
{ | |||
return LocalRef<jstring> (getEnv()->NewStringUTF (s.toUTF8())); | |||
} | |||
inline LocalRef<jstring> javaStringFromChar (const juce_wchar c) | |||
{ | |||
char utf8[8] = { 0 }; | |||
CharPointer_UTF8 (utf8).write (c); | |||
return LocalRef<jstring> (getEnv()->NewStringUTF (utf8)); | |||
} | |||
} | |||
//============================================================================== | |||
class JNIClassBase | |||
{ | |||
public: | |||
explicit JNIClassBase (const char* classPath); | |||
virtual ~JNIClassBase(); | |||
inline operator jclass() const noexcept { return classRef; } | |||
static void initialiseAllClasses (JNIEnv*); | |||
static void releaseAllClasses (JNIEnv*); | |||
protected: | |||
virtual void initialiseFields (JNIEnv*) = 0; | |||
jmethodID resolveMethod (JNIEnv*, const char* methodName, const char* params); | |||
jmethodID resolveStaticMethod (JNIEnv*, const char* methodName, const char* params); | |||
jfieldID resolveField (JNIEnv*, const char* fieldName, const char* signature); | |||
jfieldID resolveStaticField (JNIEnv*, const char* fieldName, const char* signature); | |||
private: | |||
const char* const classPath; | |||
jclass classRef; | |||
static Array<JNIClassBase*>& getClasses(); | |||
void initialise (JNIEnv*); | |||
void release (JNIEnv*); | |||
JUCE_DECLARE_NON_COPYABLE (JNIClassBase) | |||
}; | |||
//============================================================================== | |||
#define CREATE_JNI_METHOD(methodID, stringName, params) methodID = resolveMethod (env, stringName, params); | |||
#define CREATE_JNI_STATICMETHOD(methodID, stringName, params) methodID = resolveStaticMethod (env, stringName, params); | |||
#define CREATE_JNI_FIELD(fieldID, stringName, signature) fieldID = resolveField (env, stringName, signature); | |||
#define CREATE_JNI_STATICFIELD(fieldID, stringName, signature) fieldID = resolveStaticField (env, stringName, signature); | |||
#define DECLARE_JNI_METHOD(methodID, stringName, params) jmethodID methodID; | |||
#define DECLARE_JNI_FIELD(fieldID, stringName, signature) jfieldID fieldID; | |||
#define DECLARE_JNI_CLASS(CppClassName, javaPath) \ | |||
class CppClassName ## _Class : public JNIClassBase \ | |||
{ \ | |||
public: \ | |||
CppClassName ## _Class() : JNIClassBase (javaPath) {} \ | |||
\ | |||
void initialiseFields (JNIEnv* env) \ | |||
{ \ | |||
ignoreUnused (env); \ | |||
JNI_CLASS_MEMBERS (CREATE_JNI_METHOD, CREATE_JNI_STATICMETHOD, CREATE_JNI_FIELD, CREATE_JNI_STATICFIELD); \ | |||
} \ | |||
\ | |||
JNI_CLASS_MEMBERS (DECLARE_JNI_METHOD, DECLARE_JNI_METHOD, DECLARE_JNI_FIELD, DECLARE_JNI_FIELD) \ | |||
}; \ | |||
static CppClassName ## _Class CppClassName; | |||
//============================================================================== | |||
#if defined (__arm__) | |||
#define JUCE_ARM_SOFT_FLOAT_ABI __attribute__ ((pcs("aapcs"))) | |||
#else | |||
#define JUCE_ARM_SOFT_FLOAT_ABI | |||
#endif | |||
#define JUCE_JNI_CALLBACK(className, methodName, returnType, params) \ | |||
extern "C" __attribute__ ((visibility("default"))) JUCE_ARM_SOFT_FLOAT_ABI returnType JUCE_JOIN_MACRO (JUCE_JOIN_MACRO (Java_, className), _ ## methodName) params | |||
//============================================================================== | |||
class AndroidSystem | |||
{ | |||
public: | |||
AndroidSystem(); | |||
void initialise (JNIEnv*, jobject activity, jstring appFile, jstring appDataDir); | |||
void shutdown (JNIEnv*); | |||
//============================================================================== | |||
GlobalRef activity; | |||
String appFile, appDataDir; | |||
int screenWidth, screenHeight, dpi; | |||
}; | |||
extern AndroidSystem android; | |||
//============================================================================== | |||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \ | |||
METHOD (createNewView, "createNewView", "(ZJ)L" JUCE_ANDROID_ACTIVITY_CLASSPATH "$ComponentPeerView;") \ | |||
METHOD (deleteView, "deleteView", "(L" JUCE_ANDROID_ACTIVITY_CLASSPATH "$ComponentPeerView;)V") \ | |||
METHOD (createNativeSurfaceView, "createNativeSurfaceView", "(J)L" JUCE_ANDROID_ACTIVITY_CLASSPATH "$NativeSurfaceView;") \ | |||
METHOD (finish, "finish", "()V") \ | |||
METHOD (setRequestedOrientation,"setRequestedOrientation", "(I)V") \ | |||
METHOD (getClipboardContent, "getClipboardContent", "()Ljava/lang/String;") \ | |||
METHOD (setClipboardContent, "setClipboardContent", "(Ljava/lang/String;)V") \ | |||
METHOD (excludeClipRegion, "excludeClipRegion", "(Landroid/graphics/Canvas;FFFF)V") \ | |||
METHOD (renderGlyph, "renderGlyph", "(CCLandroid/graphics/Paint;Landroid/graphics/Matrix;Landroid/graphics/Rect;)[I") \ | |||
STATICMETHOD (createHTTPStream, "createHTTPStream", "(Ljava/lang/String;Z[BLjava/lang/String;I[ILjava/lang/StringBuffer;ILjava/lang/String;)L" JUCE_ANDROID_ACTIVITY_CLASSPATH "$HTTPStream;") \ | |||
METHOD (launchURL, "launchURL", "(Ljava/lang/String;)V") \ | |||
METHOD (showMessageBox, "showMessageBox", "(Ljava/lang/String;Ljava/lang/String;J)V") \ | |||
METHOD (showOkCancelBox, "showOkCancelBox", "(Ljava/lang/String;Ljava/lang/String;JLjava/lang/String;Ljava/lang/String;)V") \ | |||
METHOD (showYesNoCancelBox, "showYesNoCancelBox", "(Ljava/lang/String;Ljava/lang/String;J)V") \ | |||
STATICMETHOD (getLocaleValue, "getLocaleValue", "(Z)Ljava/lang/String;") \ | |||
STATICMETHOD (getDocumentsFolder, "getDocumentsFolder", "()Ljava/lang/String;") \ | |||
STATICMETHOD (getPicturesFolder, "getPicturesFolder", "()Ljava/lang/String;") \ | |||
STATICMETHOD (getMusicFolder, "getMusicFolder", "()Ljava/lang/String;") \ | |||
STATICMETHOD (getDownloadsFolder, "getDownloadsFolder", "()Ljava/lang/String;") \ | |||
STATICMETHOD (getMoviesFolder, "getMoviesFolder", "()Ljava/lang/String;") \ | |||
METHOD (getTypeFaceFromAsset, "getTypeFaceFromAsset", "(Ljava/lang/String;)Landroid/graphics/Typeface;") \ | |||
METHOD (getTypeFaceFromByteArray,"getTypeFaceFromByteArray","([B)Landroid/graphics/Typeface;") \ | |||
METHOD (setScreenSaver, "setScreenSaver", "(Z)V") \ | |||
METHOD (getScreenSaver, "getScreenSaver", "()Z") \ | |||
METHOD (getAndroidMidiDeviceManager, "getAndroidMidiDeviceManager", "()L" JUCE_ANDROID_ACTIVITY_CLASSPATH "$MidiDeviceManager;") \ | |||
METHOD (getAndroidBluetoothManager, "getAndroidBluetoothManager", "()L" JUCE_ANDROID_ACTIVITY_CLASSPATH "$BluetoothManager;") \ | |||
STATICMETHOD (getAndroidSDKVersion, "getAndroidSDKVersion", "()I") \ | |||
METHOD (audioManagerGetProperty, "audioManagerGetProperty", "(Ljava/lang/String;)Ljava/lang/String;") \ | |||
METHOD (hasSystemFeature, "hasSystemFeature", "(Ljava/lang/String;)Z" ) \ | |||
METHOD (requestRuntimePermission, "requestRuntimePermission", "(IJ)V" ) \ | |||
METHOD (isPermissionGranted, "isPermissionGranted", "(I)Z" ) \ | |||
METHOD (isPermissionDeclaredInManifest, "isPermissionDeclaredInManifest", "(I)Z" ) \ | |||
METHOD (getSystemService, "getSystemService", "(Ljava/lang/String;)Ljava/lang/Object;") \ | |||
STATICMETHOD (createInvocationHandler, "createInvocationHandler", "(J)Ljava/lang/reflect/InvocationHandler;") \ | |||
METHOD (bindService, "bindService", "(Landroid/content/Intent;Landroid/content/ServiceConnection;I)Z") \ | |||
METHOD (unbindService, "unbindService", "(Landroid/content/ServiceConnection;)V") \ | |||
METHOD (startIntentSenderForResult, "startIntentSenderForResult", "(Landroid/content/IntentSender;ILandroid/content/Intent;III)V") \ | |||
METHOD (getPackageName, "getPackageName", "()Ljava/lang/String;") \ | |||
DECLARE_JNI_CLASS (JuceAppActivity, JUCE_ANDROID_ACTIVITY_CLASSPATH); | |||
#undef JNI_CLASS_MEMBERS | |||
//============================================================================== | |||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \ | |||
METHOD (constructor, "<init>", "(I)V") \ | |||
METHOD (setColor, "setColor", "(I)V") \ | |||
METHOD (setAlpha, "setAlpha", "(I)V") \ | |||
METHOD (setTypeface, "setTypeface", "(Landroid/graphics/Typeface;)Landroid/graphics/Typeface;") \ | |||
METHOD (ascent, "ascent", "()F") \ | |||
METHOD (descent, "descent", "()F") \ | |||
METHOD (setTextSize, "setTextSize", "(F)V") \ | |||
METHOD (getTextWidths, "getTextWidths", "(Ljava/lang/String;[F)I") \ | |||
METHOD (setTextScaleX, "setTextScaleX", "(F)V") \ | |||
METHOD (getTextPath, "getTextPath", "(Ljava/lang/String;IIFFLandroid/graphics/Path;)V") \ | |||
METHOD (setShader, "setShader", "(Landroid/graphics/Shader;)Landroid/graphics/Shader;") \ | |||
DECLARE_JNI_CLASS (Paint, "android/graphics/Paint"); | |||
#undef JNI_CLASS_MEMBERS | |||
//============================================================================== | |||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \ | |||
METHOD (constructor, "<init>", "()V") \ | |||
METHOD (setValues, "setValues", "([F)V") \ | |||
DECLARE_JNI_CLASS (Matrix, "android/graphics/Matrix"); | |||
#undef JNI_CLASS_MEMBERS | |||
//============================================================================== | |||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \ | |||
METHOD (constructor, "<init>", "(IIII)V") \ | |||
FIELD (left, "left", "I") \ | |||
FIELD (right, "right", "I") \ | |||
FIELD (top, "top", "I") \ | |||
FIELD (bottom, "bottom", "I") \ | |||
DECLARE_JNI_CLASS (RectClass, "android/graphics/Rect"); | |||
#undef JNI_CLASS_MEMBERS | |||
//============================================================================== | |||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \ | |||
METHOD (getName, "getName", "()Ljava/lang/String;") \ | |||
METHOD (getModifiers, "getModifiers", "()I") \ | |||
METHOD (getParameterTypes, "getParameterTypes", "()[Ljava/lang/Class;") \ | |||
METHOD (getReturnType, "getReturnType", "()Ljava/lang/Class;") \ | |||
METHOD (invoke, "invoke", "(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;") \ | |||
METHOD (hashCode, "hashCode", "()I") \ | |||
METHOD (equals, "equals", "(Ljava/lang/Object;)Z") \ | |||
DECLARE_JNI_CLASS (Method, "java/lang/reflect/Method"); | |||
#undef JNI_CLASS_MEMBERS | |||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \ | |||
METHOD (getName, "getName", "()Ljava/lang/String;") \ | |||
METHOD (getModifiers, "getModifiers", "()I") \ | |||
METHOD (isAnnotation, "isAnnotation", "()Z") \ | |||
METHOD (isAnonymousClass, "isAnonymousClass", "()Z") \ | |||
METHOD (isArray, "isArray", "()Z") \ | |||
METHOD (isEnum, "isEnum", "()Z") \ | |||
METHOD (isInterface, "isInterface", "()Z") \ | |||
METHOD (isLocalClass, "isLocalClass", "()Z") \ | |||
METHOD (isMemberClass, "isMemberClass", "()Z") \ | |||
METHOD (isPrimitive, "isPrimitive", "()Z") \ | |||
METHOD (isSynthetic, "isSynthetic", "()Z") \ | |||
METHOD (getComponentType, "getComponentType", "()Ljava/lang/Class;") \ | |||
METHOD (getSuperclass, "getSuperclass", "()Ljava/lang/Class;") \ | |||
METHOD (getClassLoader, "getClassLoader", "()Ljava/lang/ClassLoader;") \ | |||
DECLARE_JNI_CLASS (JavaClass, "java/lang/Class"); | |||
#undef JNI_CLASS_MEMBERS | |||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \ | |||
METHOD (constructor, "<init>", "()V") \ | |||
DECLARE_JNI_CLASS (JavaObject, "java/lang/Object"); | |||
#undef JNI_CLASS_MEMBERS | |||
//============================================================================== | |||
class AndroidInterfaceImplementer; | |||
// This function takes ownership of the implementer. When the returned GlobalRef | |||
// goes out of scope (and no other Java routine has a reference on the return-value) | |||
// then the implementer will be deleted as well. | |||
LocalRef<jobject> CreateJavaInterface (AndroidInterfaceImplementer* implementer, | |||
const StringArray& interfaceNames, | |||
LocalRef<jobject> subclass); | |||
//============================================================================== | |||
jobject juce_invokeImplementer (JNIEnv*, jlong, jobject, jobject, jobjectArray); | |||
void juce_dispatchDelete (JNIEnv*, jlong); | |||
//============================================================================== | |||
class AndroidInterfaceImplementer | |||
{ | |||
protected: | |||
virtual ~AndroidInterfaceImplementer() {} | |||
virtual jobject invoke (jobject proxy, jobject method, jobjectArray args); | |||
//============================================================================== | |||
friend LocalRef<jobject> CreateJavaInterface (AndroidInterfaceImplementer*, const StringArray&, LocalRef<jobject>); | |||
friend jobject juce_invokeImplementer (JNIEnv*, jlong, jobject, jobject, jobjectArray); | |||
friend void juce_dispatchDelete (JNIEnv*, jlong); | |||
private: | |||
GlobalRef javaSubClass; | |||
}; | |||
LocalRef<jobject> CreateJavaInterface (AndroidInterfaceImplementer* implementer, | |||
const StringArray& interfaceNames); | |||
LocalRef<jobject> CreateJavaInterface (AndroidInterfaceImplementer* implementer, | |||
const String& interfaceName); | |||
} // namespace juce |
@@ -1,31 +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. | |||
============================================================================== | |||
*/ | |||
namespace juce | |||
{ | |||
void Logger::outputDebugString (const String& text) | |||
{ | |||
__android_log_print (ANDROID_LOG_INFO, "JUCE", "%s", text.toUTF8().getAddress()); | |||
} | |||
} // namespace juce |
@@ -1,304 +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. | |||
============================================================================== | |||
*/ | |||
namespace juce | |||
{ | |||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \ | |||
METHOD (constructor, "<init>", "()V") \ | |||
METHOD (toString, "toString", "()Ljava/lang/String;") \ | |||
DECLARE_JNI_CLASS (StringBuffer, "java/lang/StringBuffer"); | |||
#undef JNI_CLASS_MEMBERS | |||
//============================================================================== | |||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \ | |||
METHOD (connect, "connect", "()Z") \ | |||
METHOD (release, "release", "()V") \ | |||
METHOD (read, "read", "([BI)I") \ | |||
METHOD (getPosition, "getPosition", "()J") \ | |||
METHOD (getTotalLength, "getTotalLength", "()J") \ | |||
METHOD (isExhausted, "isExhausted", "()Z") \ | |||
METHOD (setPosition, "setPosition", "(J)Z") \ | |||
DECLARE_JNI_CLASS (HTTPStream, JUCE_ANDROID_ACTIVITY_CLASSPATH "$HTTPStream"); | |||
#undef JNI_CLASS_MEMBERS | |||
//============================================================================== | |||
void MACAddress::findAllAddresses (Array<MACAddress>& /*result*/) | |||
{ | |||
// TODO | |||
} | |||
JUCE_API bool JUCE_CALLTYPE Process::openEmailWithAttachments (const String& /*targetEmailAddress*/, | |||
const String& /*emailSubject*/, | |||
const String& /*bodyText*/, | |||
const StringArray& /*filesToAttach*/) | |||
{ | |||
// TODO | |||
return false; | |||
} | |||
//============================================================================== | |||
class WebInputStream::Pimpl | |||
{ | |||
public: | |||
Pimpl (WebInputStream&, const URL& urlToCopy, bool shouldBePost) | |||
: url (urlToCopy), isPost (shouldBePost), | |||
httpRequest (isPost ? "POST" : "GET") | |||
{} | |||
~Pimpl() | |||
{ | |||
cancel(); | |||
} | |||
void cancel() | |||
{ | |||
const ScopedLock lock (createStreamLock); | |||
if (stream != 0) | |||
{ | |||
stream.callVoidMethod (HTTPStream.release); | |||
stream.clear(); | |||
} | |||
hasBeenCancelled = true; | |||
} | |||
bool connect (WebInputStream::Listener* /*listener*/) | |||
{ | |||
String address = url.toString (! isPost); | |||
if (! address.contains ("://")) | |||
address = "http://" + address; | |||
MemoryBlock postData; | |||
if (isPost) | |||
WebInputStream::createHeadersAndPostData (url, headers, postData); | |||
JNIEnv* env = getEnv(); | |||
jbyteArray postDataArray = 0; | |||
if (postData.getSize() > 0) | |||
{ | |||
postDataArray = env->NewByteArray (static_cast<jsize> (postData.getSize())); | |||
env->SetByteArrayRegion (postDataArray, 0, static_cast<jsize> (postData.getSize()), (const jbyte*) postData.getData()); | |||
} | |||
LocalRef<jobject> responseHeaderBuffer (env->NewObject (StringBuffer, StringBuffer.constructor)); | |||
// Annoyingly, the android HTTP functions will choke on this call if you try to do it on the message | |||
// thread. You'll need to move your networking code to a background thread to keep it happy.. | |||
jassert (Thread::getCurrentThread() != nullptr); | |||
jintArray statusCodeArray = env->NewIntArray (1); | |||
jassert (statusCodeArray != 0); | |||
{ | |||
const ScopedLock lock (createStreamLock); | |||
if (! hasBeenCancelled) | |||
stream = GlobalRef (env->CallStaticObjectMethod (JuceAppActivity, | |||
JuceAppActivity.createHTTPStream, | |||
javaString (address).get(), | |||
(jboolean) isPost, | |||
postDataArray, | |||
javaString (headers).get(), | |||
(jint) timeOutMs, | |||
statusCodeArray, | |||
responseHeaderBuffer.get(), | |||
(jint) numRedirectsToFollow, | |||
javaString (httpRequest).get())); | |||
} | |||
if (stream != 0 && ! stream.callBooleanMethod (HTTPStream.connect)) | |||
stream.clear(); | |||
jint* const statusCodeElements = env->GetIntArrayElements (statusCodeArray, 0); | |||
statusCode = statusCodeElements[0]; | |||
env->ReleaseIntArrayElements (statusCodeArray, statusCodeElements, 0); | |||
env->DeleteLocalRef (statusCodeArray); | |||
if (postDataArray != 0) | |||
env->DeleteLocalRef (postDataArray); | |||
if (stream != 0) | |||
{ | |||
StringArray headerLines; | |||
{ | |||
LocalRef<jstring> headersString ((jstring) env->CallObjectMethod (responseHeaderBuffer.get(), | |||
StringBuffer.toString)); | |||
headerLines.addLines (juceString (env, headersString)); | |||
} | |||
for (int i = 0; i < headerLines.size(); ++i) | |||
{ | |||
const String& header = headerLines[i]; | |||
const String key (header.upToFirstOccurrenceOf (": ", false, false)); | |||
const String value (header.fromFirstOccurrenceOf (": ", false, false)); | |||
const String previousValue (responseHeaders[key]); | |||
responseHeaders.set (key, previousValue.isEmpty() ? value : (previousValue + "," + value)); | |||
} | |||
return true; | |||
} | |||
return false; | |||
} | |||
//============================================================================== | |||
// WebInputStream methods | |||
void withExtraHeaders (const String& extraHeaders) | |||
{ | |||
if (! headers.endsWithChar ('\n') && headers.isNotEmpty()) | |||
headers << "\r\n"; | |||
headers << extraHeaders; | |||
if (! headers.endsWithChar ('\n') && headers.isNotEmpty()) | |||
headers << "\r\n"; | |||
} | |||
void withCustomRequestCommand (const String& customRequestCommand) { httpRequest = customRequestCommand; } | |||
void withConnectionTimeout (int timeoutInMs) { timeOutMs = timeoutInMs; } | |||
void withNumRedirectsToFollow (int maxRedirectsToFollow) { numRedirectsToFollow = maxRedirectsToFollow; } | |||
StringPairArray getRequestHeaders() const { return WebInputStream::parseHttpHeaders (headers); } | |||
StringPairArray getResponseHeaders() const { return responseHeaders; } | |||
int getStatusCode() const { return statusCode; } | |||
//============================================================================== | |||
bool isError() const { return stream == nullptr; } | |||
bool isExhausted() { return stream != nullptr && stream.callBooleanMethod (HTTPStream.isExhausted); } | |||
int64 getTotalLength() { return stream != nullptr ? stream.callLongMethod (HTTPStream.getTotalLength) : 0; } | |||
int64 getPosition() { return stream != nullptr ? stream.callLongMethod (HTTPStream.getPosition) : 0; } | |||
bool setPosition (int64 wantedPos) { return stream != nullptr && stream.callBooleanMethod (HTTPStream.setPosition, (jlong) wantedPos); } | |||
int read (void* buffer, int bytesToRead) | |||
{ | |||
jassert (buffer != nullptr && bytesToRead >= 0); | |||
const ScopedLock lock (createStreamLock); | |||
if (stream == nullptr) | |||
return 0; | |||
JNIEnv* env = getEnv(); | |||
jbyteArray javaArray = env->NewByteArray (bytesToRead); | |||
int numBytes = stream.callIntMethod (HTTPStream.read, javaArray, (jint) bytesToRead); | |||
if (numBytes > 0) | |||
env->GetByteArrayRegion (javaArray, 0, numBytes, static_cast<jbyte*> (buffer)); | |||
env->DeleteLocalRef (javaArray); | |||
return numBytes; | |||
} | |||
//============================================================================== | |||
int statusCode = 0; | |||
private: | |||
const URL url; | |||
bool isPost; | |||
int numRedirectsToFollow = 5, timeOutMs = 0; | |||
String httpRequest, headers; | |||
StringPairArray responseHeaders; | |||
CriticalSection createStreamLock; | |||
bool hasBeenCancelled = false; | |||
GlobalRef stream; | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Pimpl) | |||
}; | |||
URL::DownloadTask* URL::downloadToFile (const File& targetLocation, String extraHeaders, DownloadTask::Listener* listener, bool shouldUsePost) | |||
{ | |||
return URL::DownloadTask::createFallbackDownloader (*this, targetLocation, extraHeaders, listener, shouldUsePost); | |||
} | |||
//============================================================================== | |||
static void addAddress (const sockaddr_in* addr_in, Array<IPAddress>& result) | |||
{ | |||
in_addr_t addr = addr_in->sin_addr.s_addr; | |||
if (addr != INADDR_NONE) | |||
result.addIfNotAlreadyThere (IPAddress (ntohl (addr))); | |||
} | |||
static void findIPAddresses (int sock, Array<IPAddress>& result) | |||
{ | |||
ifconf cfg; | |||
HeapBlock<char> buffer; | |||
int bufferSize = 1024; | |||
do | |||
{ | |||
bufferSize *= 2; | |||
buffer.calloc ((size_t) bufferSize); | |||
cfg.ifc_len = bufferSize; | |||
cfg.ifc_buf = buffer; | |||
if (ioctl (sock, 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); | |||
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]; | |||
if (item.ifr_addr.sa_family == AF_INET) | |||
addAddress ((const sockaddr_in*) &item.ifr_addr, result); | |||
} | |||
#endif | |||
} | |||
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); | |||
} | |||
} | |||
} // namespace juce |
@@ -1,88 +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. | |||
============================================================================== | |||
*/ | |||
namespace juce | |||
{ | |||
static void handleAndroidCallback (bool permissionWasGranted, RuntimePermissions::Callback* callbackPtr) | |||
{ | |||
if (callbackPtr == nullptr) | |||
{ | |||
// got a nullptr passed in from java! this should never happen... | |||
jassertfalse; | |||
return; | |||
} | |||
std::unique_ptr<RuntimePermissions::Callback> uptr (callbackPtr); | |||
if (RuntimePermissions::Callback callbackObj = *uptr) | |||
callbackObj (permissionWasGranted); | |||
} | |||
JUCE_JNI_CALLBACK (JUCE_ANDROID_ACTIVITY_CLASSNAME, | |||
androidRuntimePermissionsCallback, | |||
void, (JNIEnv* env, jobject, jboolean permissionsGranted, jlong callbackPtr)) | |||
{ | |||
setEnv (env); | |||
handleAndroidCallback (permissionsGranted != 0, | |||
reinterpret_cast<RuntimePermissions::Callback*> (callbackPtr)); | |||
} | |||
void RuntimePermissions::request (PermissionID permission, Callback callback) | |||
{ | |||
if (! android.activity.callBooleanMethod (JuceAppActivity.isPermissionDeclaredInManifest, (jint) permission)) | |||
{ | |||
// 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 | |||
// the Projucer. Otherwise this can't work. | |||
jassertfalse; | |||
callback (false); | |||
return; | |||
} | |||
if (JUCE_ANDROID_API_VERSION < 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)); | |||
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); | |||
} | |||
bool RuntimePermissions::isRequired (PermissionID /*permission*/) | |||
{ | |||
return JUCE_ANDROID_API_VERSION >= 23; | |||
} | |||
bool RuntimePermissions::isGranted (PermissionID permission) | |||
{ | |||
return android.activity.callBooleanMethod (JuceAppActivity.isPermissionGranted, permission); | |||
} | |||
} // namespace juce |
@@ -1,520 +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. | |||
============================================================================== | |||
*/ | |||
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 (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->CallStaticObjectMethod (JuceAppActivity, | |||
JuceAppActivity.createInvocationHandler, | |||
reinterpret_cast<jlong> (implementer))); | |||
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)); | |||
} | |||
jobject AndroidInterfaceImplementer::invoke (jobject /*proxy*/, jobject method, jobjectArray args) | |||
{ | |||
auto* env = getEnv(); | |||
return env->CallObjectMethod (method, Method.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) \ | |||
STATICMETHOD (getProperty, "getProperty", "(Ljava/lang/String;)Ljava/lang/String;") | |||
DECLARE_JNI_CLASS (SystemClass, "java/lang/System"); | |||
#undef JNI_CLASS_MEMBERS | |||
static inline String getSystemProperty (const String& name) | |||
{ | |||
return juceString (LocalRef<jstring> ((jstring) getEnv()->CallStaticObjectMethod (SystemClass, | |||
SystemClass.getProperty, | |||
javaString (name).get()))); | |||
} | |||
static inline String getLocaleValue (bool isRegion) | |||
{ | |||
return juceString (LocalRef<jstring> ((jstring) getEnv()->CallStaticObjectMethod (JuceAppActivity, | |||
JuceAppActivity.getLocaleValue, | |||
isRegion))); | |||
} | |||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) | |||
DECLARE_JNI_CLASS (BuildClass, "android/os/Build"); | |||
#undef JNI_CLASS_MEMBERS | |||
static inline String getAndroidOsBuildValue (const char* fieldName) | |||
{ | |||
return juceString (LocalRef<jstring> ((jstring) getEnv()->GetStaticObjectField ( | |||
BuildClass, getEnv()->GetStaticFieldID (BuildClass, fieldName, "Ljava/lang/String;")))); | |||
} | |||
} | |||
//============================================================================== | |||
SystemStats::OperatingSystemType SystemStats::getOperatingSystemType() | |||
{ | |||
return Android; | |||
} | |||
String SystemStats::getOperatingSystemName() | |||
{ | |||
return "Android " + AndroidStatsHelpers::getSystemProperty ("os.version"); | |||
} | |||
String SystemStats::getDeviceDescription() | |||
{ | |||
return AndroidStatsHelpers::getAndroidOsBuildValue ("MODEL") | |||
+ "-" + AndroidStatsHelpers::getAndroidOsBuildValue ("SERIAL"); | |||
} | |||
bool SystemStats::isOperatingSystem64Bit() | |||
{ | |||
#if JUCE_64BIT | |||
return true; | |||
#else | |||
return false; | |||
#endif | |||
} | |||
String SystemStats::getCpuVendor() | |||
{ | |||
return AndroidStatsHelpers::getSystemProperty ("os.arch"); | |||
} | |||
String SystemStats::getCpuModel() | |||
{ | |||
return readPosixConfigFileValue ("/proc/cpuinfo", "Hardware"); | |||
} | |||
int SystemStats::getCpuSpeedInMegaherz() | |||
{ | |||
int maxFreqKHz = 0; | |||
for (int i = 0; i < getNumCpus(); ++i) | |||
{ | |||
int freqKHz = File ("/sys/devices/system/cpu/cpu" + String(i) + "/cpufreq/cpuinfo_max_freq") | |||
.loadFileAsString() | |||
.getIntValue(); | |||
maxFreqKHz = jmax (freqKHz, maxFreqKHz); | |||
} | |||
return maxFreqKHz / 1000; | |||
} | |||
int SystemStats::getMemorySizeInMegabytes() | |||
{ | |||
#if __ANDROID_API__ >= 9 | |||
struct sysinfo sysi; | |||
if (sysinfo (&sysi) == 0) | |||
return static_cast<int> ((sysi.totalram * sysi.mem_unit) / (1024 * 1024)); | |||
#endif | |||
return 0; | |||
} | |||
int SystemStats::getPageSize() | |||
{ | |||
return static_cast<int> (sysconf (_SC_PAGESIZE)); | |||
} | |||
//============================================================================== | |||
String SystemStats::getLogonName() | |||
{ | |||
if (const char* user = getenv ("USER")) | |||
return CharPointer_UTF8 (user); | |||
if (struct passwd* const pw = getpwuid (getuid())) | |||
return CharPointer_UTF8 (pw->pw_name); | |||
return {}; | |||
} | |||
String SystemStats::getFullUserName() | |||
{ | |||
return getLogonName(); | |||
} | |||
String SystemStats::getComputerName() | |||
{ | |||
char name [256] = { 0 }; | |||
if (gethostname (name, sizeof (name) - 1) == 0) | |||
return name; | |||
return {}; | |||
} | |||
String SystemStats::getUserLanguage() { return AndroidStatsHelpers::getLocaleValue (false); } | |||
String SystemStats::getUserRegion() { return AndroidStatsHelpers::getLocaleValue (true); } | |||
String SystemStats::getDisplayLanguage() { return getUserLanguage() + "-" + getUserRegion(); } | |||
//============================================================================== | |||
void CPUInformation::initialise() noexcept | |||
{ | |||
numPhysicalCPUs = numLogicalCPUs = jmax ((int) 1, (int) android_getCpuCount()); | |||
auto cpuFamily = android_getCpuFamily(); | |||
auto cpuFeatures = android_getCpuFeatures(); | |||
if (cpuFamily == ANDROID_CPU_FAMILY_X86 || cpuFamily == ANDROID_CPU_FAMILY_X86_64) | |||
{ | |||
hasMMX = hasSSE = hasSSE2 = (cpuFamily == ANDROID_CPU_FAMILY_X86_64); | |||
hasSSSE3 = ((cpuFeatures & ANDROID_CPU_X86_FEATURE_SSSE3) != 0); | |||
hasSSE41 = ((cpuFeatures & ANDROID_CPU_X86_FEATURE_SSE4_1) != 0); | |||
hasSSE42 = ((cpuFeatures & ANDROID_CPU_X86_FEATURE_SSE4_2) != 0); | |||
hasAVX = ((cpuFeatures & ANDROID_CPU_X86_FEATURE_AVX) != 0); | |||
hasAVX2 = ((cpuFeatures & ANDROID_CPU_X86_FEATURE_AVX2) != 0); | |||
// Google does not distinguish between MMX, SSE, SSE2, SSE3 and SSSE3. So | |||
// I assume (and quick Google searches seem to confirm this) that there are | |||
// only devices out there that either support all of this or none of this. | |||
if (hasSSSE3) | |||
hasMMX = hasSSE = hasSSE2 = hasSSE3 = true; | |||
} | |||
else if (cpuFamily == ANDROID_CPU_FAMILY_ARM) | |||
{ | |||
hasNeon = ((cpuFeatures & ANDROID_CPU_ARM_FEATURE_NEON) != 0); | |||
} | |||
else if (cpuFamily == ANDROID_CPU_FAMILY_ARM64) | |||
{ | |||
// all arm 64-bit cpus have neon | |||
hasNeon = true; | |||
} | |||
} | |||
//============================================================================== | |||
uint32 juce_millisecondsSinceStartup() noexcept | |||
{ | |||
timespec t; | |||
clock_gettime (CLOCK_MONOTONIC, &t); | |||
return static_cast<uint32> (t.tv_sec) * 1000U + static_cast<uint32> (t.tv_nsec) / 1000000U; | |||
} | |||
int64 Time::getHighResolutionTicks() noexcept | |||
{ | |||
timespec t; | |||
clock_gettime (CLOCK_MONOTONIC, &t); | |||
return (t.tv_sec * (int64) 1000000) + (t.tv_nsec / 1000); | |||
} | |||
int64 Time::getHighResolutionTicksPerSecond() noexcept | |||
{ | |||
return 1000000; // (microseconds) | |||
} | |||
double Time::getMillisecondCounterHiRes() noexcept | |||
{ | |||
return getHighResolutionTicks() * 0.001; | |||
} | |||
bool Time::setSystemTimeToThisTime() const | |||
{ | |||
jassertfalse; | |||
return false; | |||
} | |||
} // namespace juce |
@@ -1,77 +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. | |||
============================================================================== | |||
*/ | |||
namespace juce | |||
{ | |||
/* | |||
Note that a lot of methods that you'd expect to find in this file actually | |||
live in juce_posix_SharedCode.h! | |||
*/ | |||
//============================================================================== | |||
// sets the process to 0=low priority, 1=normal, 2=high, 3=realtime | |||
JUCE_API void JUCE_CALLTYPE Process::setPriority (ProcessPriority prior) | |||
{ | |||
// TODO | |||
struct sched_param param; | |||
int policy, maxp, minp; | |||
const int p = (int) prior; | |||
if (p <= 1) | |||
policy = SCHED_OTHER; | |||
else | |||
policy = SCHED_RR; | |||
minp = sched_get_priority_min (policy); | |||
maxp = sched_get_priority_max (policy); | |||
if (p < 2) | |||
param.sched_priority = 0; | |||
else if (p == 2 ) | |||
// Set to middle of lower realtime priority range | |||
param.sched_priority = minp + (maxp - minp) / 4; | |||
else | |||
// Set to middle of higher realtime priority range | |||
param.sched_priority = minp + (3 * (maxp - minp) / 4); | |||
pthread_setschedparam (pthread_self(), policy, ¶m); | |||
} | |||
JUCE_API bool JUCE_CALLTYPE juce_isRunningUnderDebugger() noexcept | |||
{ | |||
StringArray lines; | |||
File ("/proc/self/status").readLines (lines); | |||
for (int i = lines.size(); --i >= 0;) // (NB - it's important that this runs in reverse order) | |||
if (lines[i].upToFirstOccurrenceOf (":", false, false).trim().equalsIgnoreCase ("TracerPid")) | |||
return (lines[i].fromFirstOccurrenceOf (":", false, false).trim().getIntValue() > 0); | |||
return false; | |||
} | |||
JUCE_API void JUCE_CALLTYPE Process::raisePrivilege() {} | |||
JUCE_API void JUCE_CALLTYPE Process::lowerPrivilege() {} | |||
} // namespace juce |
@@ -1,559 +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. | |||
============================================================================== | |||
*/ | |||
namespace juce | |||
{ | |||
class WebInputStream::Pimpl | |||
{ | |||
public: | |||
Pimpl (WebInputStream& ownerStream, const URL& urlToCopy, bool shouldUsePost) | |||
: owner (ownerStream), url (urlToCopy), isPost (shouldUsePost), | |||
httpRequest (isPost ? "POST" : "GET") | |||
{ | |||
multi = curl_multi_init(); | |||
if (multi != nullptr) | |||
{ | |||
curl = curl_easy_init(); | |||
if (curl != nullptr) | |||
if (curl_multi_add_handle (multi, curl) == CURLM_OK) | |||
return; | |||
} | |||
cleanup(); | |||
} | |||
~Pimpl() | |||
{ | |||
cleanup(); | |||
} | |||
//============================================================================== | |||
// Input Stream overrides | |||
bool isError() const { return curl == nullptr || lastError != CURLE_OK; } | |||
bool isExhausted() { return (isError() || finished) && curlBuffer.getSize() == 0; } | |||
int64 getPosition() { return streamPos; } | |||
int64 getTotalLength() { return contentLength; } | |||
int read (void* buffer, int bytesToRead) | |||
{ | |||
return readOrSkip (buffer, bytesToRead, false); | |||
} | |||
bool setPosition (int64 wantedPos) | |||
{ | |||
const int amountToSkip = static_cast<int> (wantedPos - getPosition()); | |||
if (amountToSkip < 0) | |||
return false; | |||
if (amountToSkip == 0) | |||
return true; | |||
const int actuallySkipped = readOrSkip (nullptr, amountToSkip, true); | |||
return actuallySkipped == amountToSkip; | |||
} | |||
//============================================================================== | |||
// WebInputStream methods | |||
void withExtraHeaders (const String& extraHeaders) | |||
{ | |||
if (! requestHeaders.endsWithChar ('\n') && requestHeaders.isNotEmpty()) | |||
requestHeaders << "\r\n"; | |||
requestHeaders << extraHeaders; | |||
if (! requestHeaders.endsWithChar ('\n') && requestHeaders.isNotEmpty()) | |||
requestHeaders << "\r\n"; | |||
} | |||
void withCustomRequestCommand (const String& customRequestCommand) { httpRequest = customRequestCommand; } | |||
void withConnectionTimeout (int timeoutInMs) { timeOutMs = timeoutInMs; } | |||
void withNumRedirectsToFollow (int maxRedirectsToFollow) { maxRedirects = maxRedirectsToFollow; } | |||
StringPairArray getRequestHeaders() const { return WebInputStream::parseHttpHeaders (requestHeaders); } | |||
StringPairArray getResponseHeaders() const { return WebInputStream::parseHttpHeaders (responseHeaders); } | |||
int getStatusCode() const { return statusCode; } | |||
//============================================================================== | |||
void cleanup() | |||
{ | |||
const ScopedLock lock (cleanupLock); | |||
if (curl != nullptr) | |||
{ | |||
curl_multi_remove_handle (multi, curl); | |||
if (headerList != nullptr) | |||
{ | |||
curl_slist_free_all (headerList); | |||
headerList = nullptr; | |||
} | |||
curl_easy_cleanup (curl); | |||
curl = nullptr; | |||
} | |||
if (multi != nullptr) | |||
{ | |||
curl_multi_cleanup (multi); | |||
multi = nullptr; | |||
} | |||
} | |||
void cancel() | |||
{ | |||
cleanup(); | |||
} | |||
//============================================================================== | |||
bool setOptions() | |||
{ | |||
const String address = url.toString (! isPost); | |||
curl_version_info_data* data = curl_version_info (CURLVERSION_NOW); | |||
jassert (data != nullptr); | |||
if (! requestHeaders.endsWithChar ('\n')) | |||
requestHeaders << "\r\n"; | |||
if (isPost) | |||
WebInputStream::createHeadersAndPostData (url, requestHeaders, headersAndPostData); | |||
if (! requestHeaders.endsWithChar ('\n')) | |||
requestHeaders << "\r\n"; | |||
String 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_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 (isPost) | |||
{ | |||
if (curl_easy_setopt (curl, CURLOPT_READDATA, this) != CURLE_OK | |||
|| 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) | |||
return false; | |||
} | |||
// handle special http request commands | |||
bool hasSpecialRequestCmd = isPost ? (httpRequest != "POST") : (httpRequest != "GET"); | |||
if (hasSpecialRequestCmd) | |||
{ | |||
if (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) | |||
return false; | |||
if (timeOutMs > 0) | |||
{ | |||
long 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) | |||
return false; | |||
} | |||
return true; | |||
} | |||
return false; | |||
} | |||
bool connect (WebInputStream::Listener* webInputListener) | |||
{ | |||
{ | |||
const ScopedLock lock (cleanupLock); | |||
if (curl == nullptr) | |||
return false; | |||
if (! setOptions()) | |||
{ | |||
cleanup(); | |||
return false; | |||
} | |||
if (requestHeaders.isNotEmpty()) | |||
{ | |||
const StringArray headerLines = StringArray::fromLines (requestHeaders); | |||
// 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()); | |||
for (int i = 1; (i < headerLines.size() && headerList != nullptr); ++i) | |||
headerList = curl_slist_append (headerList, headerLines [i].toRawUTF8()); | |||
if (headerList == nullptr) | |||
{ | |||
cleanup(); | |||
return false; | |||
} | |||
if (curl_easy_setopt (curl, CURLOPT_HTTPHEADER, headerList) != CURLE_OK) | |||
{ | |||
cleanup(); | |||
return false; | |||
} | |||
} | |||
} | |||
listener = webInputListener; | |||
if (isPost) | |||
postBuffer = &headersAndPostData; | |||
size_t lastPos = static_cast<size_t> (-1); | |||
// step until either: 1) there is an error 2) the transaction is complete | |||
// or 3) data is in the in buffer | |||
while ((! finished) && curlBuffer.getSize() == 0) | |||
{ | |||
{ | |||
const ScopedLock lock (cleanupLock); | |||
if (curl == nullptr) | |||
return false; | |||
} | |||
singleStep(); | |||
// call callbacks if this is a post request | |||
if (isPost && listener != nullptr && lastPos != postPosition) | |||
{ | |||
lastPos = postPosition; | |||
if (! listener->postDataSendProgress (owner, static_cast<int> (lastPos), static_cast<int> (headersAndPostData.getSize()))) | |||
{ | |||
// user has decided to abort the transaction | |||
cleanup(); | |||
return false; | |||
} | |||
} | |||
} | |||
{ | |||
const ScopedLock lock (cleanupLock); | |||
if (curl == nullptr) | |||
return false; | |||
long responseCode; | |||
if (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) | |||
contentLength = static_cast<int64> (curlLength); | |||
} | |||
return true; | |||
} | |||
void finish() | |||
{ | |||
const ScopedLock lock (cleanupLock); | |||
if (curl == nullptr) | |||
return; | |||
for (;;) | |||
{ | |||
int cnt = 0; | |||
if (CURLMsg* msg = curl_multi_info_read (multi, &cnt)) | |||
{ | |||
if (msg->msg == CURLMSG_DONE && msg->easy_handle == curl) | |||
{ | |||
lastError = msg->data.result; // this is the error that stopped our process from continuing | |||
break; | |||
} | |||
} | |||
else | |||
{ | |||
break; | |||
} | |||
} | |||
finished = true; | |||
} | |||
//============================================================================== | |||
void singleStep() | |||
{ | |||
if (lastError != CURLE_OK) | |||
return; | |||
fd_set fdread, fdwrite, fdexcep; | |||
int maxfd = -1; | |||
long curl_timeo; | |||
{ | |||
const ScopedLock lock (cleanupLock); | |||
if (multi == nullptr) | |||
return; | |||
if ((lastError = (int) curl_multi_timeout (multi, &curl_timeo)) != CURLM_OK) | |||
return; | |||
} | |||
// why 980? see http://curl.haxx.se/libcurl/c/curl_multi_timeout.html | |||
if (curl_timeo < 0) | |||
curl_timeo = 980; | |||
struct timeval tv; | |||
tv.tv_sec = curl_timeo / 1000; | |||
tv.tv_usec = (curl_timeo % 1000) * 1000; | |||
FD_ZERO (&fdread); | |||
FD_ZERO (&fdwrite); | |||
FD_ZERO (&fdexcep); | |||
{ | |||
const ScopedLock lock (cleanupLock); | |||
if (multi == nullptr) | |||
return; | |||
if ((lastError = (int) curl_multi_fdset (multi, &fdread, &fdwrite, &fdexcep, &maxfd)) != CURLM_OK) | |||
return; | |||
} | |||
if (maxfd != -1) | |||
{ | |||
if (select (maxfd + 1, &fdread, &fdwrite, &fdexcep, &tv) < 0) | |||
{ | |||
lastError = -1; | |||
return; | |||
} | |||
} | |||
else | |||
{ | |||
// if curl does not return any sockets for to wait on, then the doc says to wait 100 ms | |||
Thread::sleep (100); | |||
} | |||
int still_running = 0; | |||
int curlRet; | |||
{ | |||
const ScopedLock lock (cleanupLock); | |||
while ((curlRet = (int) curl_multi_perform (multi, &still_running)) == CURLM_CALL_MULTI_PERFORM) | |||
{} | |||
} | |||
if ((lastError = curlRet) != CURLM_OK) | |||
return; | |||
if (still_running <= 0) | |||
finish(); | |||
} | |||
int readOrSkip (void* buffer, int bytesToRead, bool skip) | |||
{ | |||
if (bytesToRead <= 0) | |||
return 0; | |||
size_t pos = 0; | |||
size_t len = static_cast<size_t> (bytesToRead); | |||
while (len > 0) | |||
{ | |||
size_t bufferBytes = curlBuffer.getSize(); | |||
bool removeSection = true; | |||
if (bufferBytes == 0) | |||
{ | |||
// do not call curl again if we are finished | |||
{ | |||
const ScopedLock lock (cleanupLock); | |||
if (finished || curl == nullptr) | |||
return static_cast<int> (pos); | |||
} | |||
skipBytes = skip ? len : 0; | |||
singleStep(); | |||
// update the amount that was read/skipped from curl | |||
bufferBytes = skip ? len - skipBytes : curlBuffer.getSize(); | |||
removeSection = ! skip; | |||
} | |||
// can we copy data from the internal buffer? | |||
if (bufferBytes > 0) | |||
{ | |||
size_t max = jmin (len, bufferBytes); | |||
if (! skip) | |||
memcpy (addBytesToPointer (buffer, pos), curlBuffer.getData(), max); | |||
pos += max; | |||
streamPos += static_cast<int64> (max); | |||
len -= max; | |||
if (removeSection) | |||
curlBuffer.removeSection (0, max); | |||
} | |||
} | |||
return static_cast<int> (pos); | |||
} | |||
//============================================================================== | |||
// CURL callbacks | |||
size_t curlWriteCallback (char* ptr, size_t size, size_t nmemb) | |||
{ | |||
if (curl == nullptr || lastError != CURLE_OK) | |||
return 0; | |||
const size_t len = size * nmemb; | |||
// skip bytes if necessary | |||
size_t max = jmin (skipBytes, len); | |||
skipBytes -= max; | |||
if (len > max) | |||
curlBuffer.append (ptr + max, len - max); | |||
return len; | |||
} | |||
size_t curlReadCallback (char* ptr, size_t size, size_t nmemb) | |||
{ | |||
if (curl == nullptr || postBuffer == nullptr || lastError != CURLE_OK) | |||
return 0; | |||
const size_t len = size * nmemb; | |||
size_t max = jmin (postBuffer->getSize() - postPosition, len); | |||
memcpy (ptr, (char*)postBuffer->getData() + postPosition, max); | |||
postPosition += max; | |||
return max; | |||
} | |||
size_t curlHeaderCallback (char* ptr, size_t size, size_t nmemb) | |||
{ | |||
if (curl == nullptr || lastError != CURLE_OK) | |||
return 0; | |||
size_t len = size * nmemb; | |||
String header (ptr, len); | |||
if (! header.contains (":") && header.startsWithIgnoreCase ("HTTP/")) | |||
responseHeaders.clear(); | |||
else | |||
responseHeaders += header; | |||
return len; | |||
} | |||
//============================================================================== | |||
// Static method wrappers | |||
static size_t StaticCurlWrite (char* ptr, size_t size, size_t nmemb, void* userdata) | |||
{ | |||
WebInputStream::Pimpl* wi = reinterpret_cast<WebInputStream::Pimpl*> (userdata); | |||
return wi->curlWriteCallback (ptr, size, nmemb); | |||
} | |||
static size_t StaticCurlRead (char* ptr, size_t size, size_t nmemb, void* userdata) | |||
{ | |||
WebInputStream::Pimpl* wi = reinterpret_cast<WebInputStream::Pimpl*> (userdata); | |||
return wi->curlReadCallback (ptr, size, nmemb); | |||
} | |||
static size_t StaticCurlHeader (char* ptr, size_t size, size_t nmemb, void* userdata) | |||
{ | |||
WebInputStream::Pimpl* wi = reinterpret_cast<WebInputStream::Pimpl*> (userdata); | |||
return wi->curlHeaderCallback (ptr, size, nmemb); | |||
} | |||
//============================================================================== | |||
WebInputStream& owner; | |||
const URL url; | |||
//============================================================================== | |||
// curl stuff | |||
CURLM* multi = nullptr; | |||
CURL* curl = nullptr; | |||
struct curl_slist* headerList = nullptr; | |||
int lastError = CURLE_OK; | |||
//============================================================================== | |||
// Options | |||
int timeOutMs = 0; | |||
int maxRedirects = 5; | |||
const bool isPost; | |||
String httpRequest; | |||
//============================================================================== | |||
// internal buffers and buffer positions | |||
int64 contentLength = -1, streamPos = 0; | |||
MemoryBlock curlBuffer; | |||
MemoryBlock headersAndPostData; | |||
String responseHeaders, requestHeaders; | |||
int statusCode = -1; | |||
//============================================================================== | |||
bool finished = false; | |||
size_t skipBytes = 0; | |||
//============================================================================== | |||
// Http POST variables | |||
const MemoryBlock* postBuffer = nullptr; | |||
size_t postPosition = 0; | |||
//============================================================================== | |||
WebInputStream::Listener* listener = nullptr; | |||
//============================================================================== | |||
CriticalSection cleanupLock; | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Pimpl) | |||
}; | |||
URL::DownloadTask* URL::downloadToFile (const File& targetLocation, String extraHeaders, DownloadTask::Listener* listener, bool shouldUsePost) | |||
{ | |||
return URL::DownloadTask::createFallbackDownloader (*this, targetLocation, extraHeaders, listener, shouldUsePost); | |||
} | |||
} // namespace juce |
@@ -1,152 +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. | |||
============================================================================== | |||
*/ | |||
namespace juce | |||
{ | |||
bool File::copyInternal (const File& dest) const | |||
{ | |||
FileInputStream in (*this); | |||
if (dest.deleteFile()) | |||
{ | |||
{ | |||
FileOutputStream out (dest); | |||
if (out.failedToOpen()) | |||
return false; | |||
if (out.writeFromInputStream (in, -1) == getSize()) | |||
return true; | |||
} | |||
dest.deleteFile(); | |||
} | |||
return false; | |||
} | |||
void File::findFileSystemRoots (Array<File>& destArray) | |||
{ | |||
destArray.add (File ("/")); | |||
} | |||
bool File::isHidden() const | |||
{ | |||
return getFileName().startsWithChar ('.'); | |||
} | |||
static String getLinkedFile (const String& file) | |||
{ | |||
HeapBlock<char> buffer (8194); | |||
const int numBytes = (int) readlink (file.toRawUTF8(), buffer, 8192); | |||
return String::fromUTF8 (buffer, jmax (0, numBytes)); | |||
} | |||
bool File::isSymbolicLink() const | |||
{ | |||
return getLinkedFile (getFullPathName()).isNotEmpty(); | |||
} | |||
File File::getLinkedTarget() const | |||
{ | |||
String f (getLinkedFile (getFullPathName())); | |||
if (f.isNotEmpty()) | |||
return getSiblingFile (f); | |||
return *this; | |||
} | |||
//============================================================================== | |||
class DirectoryIterator::NativeIterator::Pimpl | |||
{ | |||
public: | |||
Pimpl (const File& directory, const String& wc) | |||
: parentDir (File::addTrailingSeparator (directory.getFullPathName())), | |||
wildCard (wc), dir (opendir (directory.getFullPathName().toUTF8())) | |||
{ | |||
} | |||
~Pimpl() | |||
{ | |||
if (dir != nullptr) | |||
closedir (dir); | |||
} | |||
bool next (String& filenameFound, | |||
bool* const isDir, bool* const isHidden, int64* const fileSize, | |||
Time* const modTime, Time* const creationTime, bool* const isReadOnly) | |||
{ | |||
if (dir != nullptr) | |||
{ | |||
const char* wildcardUTF8 = nullptr; | |||
for (;;) | |||
{ | |||
struct dirent* const de = readdir (dir); | |||
if (de == nullptr) | |||
break; | |||
if (wildcardUTF8 == nullptr) | |||
wildcardUTF8 = wildCard.toUTF8(); | |||
if (fnmatch (wildcardUTF8, de->d_name, FNM_CASEFOLD) == 0) | |||
{ | |||
filenameFound = CharPointer_UTF8 (de->d_name); | |||
updateStatInfoForFile (parentDir + filenameFound, isDir, fileSize, modTime, creationTime, isReadOnly); | |||
if (isHidden != nullptr) | |||
*isHidden = filenameFound.startsWithChar ('.'); | |||
return true; | |||
} | |||
} | |||
} | |||
return false; | |||
} | |||
private: | |||
String parentDir, wildCard; | |||
DIR* dir; | |||
JUCE_DECLARE_NON_COPYABLE (Pimpl) | |||
}; | |||
DirectoryIterator::NativeIterator::NativeIterator (const File& directory, const String& wildCardStr) | |||
: pimpl (new DirectoryIterator::NativeIterator::Pimpl (directory, wildCardStr)) | |||
{ | |||
} | |||
DirectoryIterator::NativeIterator::~NativeIterator() {} | |||
bool DirectoryIterator::NativeIterator::next (String& filenameFound, | |||
bool* isDir, bool* isHidden, int64* fileSize, | |||
Time* modTime, Time* creationTime, bool* isReadOnly) | |||
{ | |||
return pimpl->next (filenameFound, isDir, isHidden, fileSize, modTime, creationTime, isReadOnly); | |||
} | |||
} // namespace juce |