| @@ -0,0 +1,35 @@ | |||
| /* | |||
| * Carla Juce setup | |||
| * Copyright (C) 2013 Filipe Coelho <falktx@falktx.com> | |||
| * | |||
| * This program is free software; you can redistribute it and/or | |||
| * modify it under the terms of the GNU General Public License as | |||
| * published by the Free Software Foundation; either version 2 of | |||
| * the License, or any later version. | |||
| * | |||
| * This program is distributed in the hope that it will be useful, | |||
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
| * GNU General Public License for more details. | |||
| * | |||
| * For a full copy of the GNU General Public License see the doc/GPL.txt file. | |||
| */ | |||
| #ifndef CARLA_JUCE_HEADER_H_INCLUDED | |||
| #define CARLA_JUCE_HEADER_H_INCLUDED | |||
| #include "juce_core/AppConfig.h" | |||
| #include "juce_core/juce_core.h" | |||
| //#include "modules/juce_audio_basics/juce_audio_basics.h" | |||
| //#include "modules/juce_audio_devices/juce_audio_devices.h" | |||
| //#include "modules/juce_audio_formats/juce_audio_formats.h" | |||
| //#include "modules/juce_audio_processors/juce_audio_processors.h" | |||
| //#include "modules/juce_audio_utils/juce_audio_utils.h" | |||
| //#include "modules/juce_data_structures/juce_data_structures.h" | |||
| //#include "modules/juce_events/juce_events.h" | |||
| //#include "modules/juce_graphics/juce_graphics.h" | |||
| //#include "modules/juce_gui_basics/juce_gui_basics.h" | |||
| //#include "modules/juce_gui_extra/juce_gui_extra.h" | |||
| #endif // CARLA_JUCE_HEADER_H_INCLUDED | |||
| @@ -0,0 +1,98 @@ | |||
| /* | |||
| ============================================================================== | |||
| Build options for juce_core static library | |||
| ============================================================================== | |||
| */ | |||
| #ifndef CARLA_JUCE_CORE_APPCONFIG_H_INCLUDED | |||
| #define CARLA_JUCE_CORE_APPCONFIG_H_INCLUDED | |||
| //============================================================================= | |||
| /** 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. | |||
| */ | |||
| #if DEBUG | |||
| #define JUCE_FORCE_DEBUG 1 | |||
| #else | |||
| #define JUCE_FORCE_DEBUG 0 | |||
| #endif | |||
| //============================================================================= | |||
| /** 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 | |||
| */ | |||
| #if DEBUG | |||
| #define JUCE_LOG_ASSERTIONS 1 | |||
| #else | |||
| #define JUCE_LOG_ASSERTIONS 0 | |||
| #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 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 | |||
| //#define JUCE_ZLIB_INCLUDE_PATH <zlib.h> | |||
| /* Config: JUCE_CATCH_UNHANDLED_EXCEPTIONS | |||
| If enabled, this will add some exception-catching code to forward unhandled exceptions | |||
| to your JUCEApplication::unhandledException() callback. | |||
| */ | |||
| #if DEBUG | |||
| #define JUCE_CATCH_UNHANDLED_EXCEPTIONS 1 | |||
| #else | |||
| #define JUCE_CATCH_UNHANDLED_EXCEPTIONS 0 | |||
| #endif | |||
| #define JUCE_MODULE_AVAILABLE_juce_audio_basics 1 | |||
| #define JUCE_MODULE_AVAILABLE_juce_audio_devices 1 | |||
| #define JUCE_MODULE_AVAILABLE_juce_audio_formats 1 | |||
| #define JUCE_MODULE_AVAILABLE_juce_audio_processors 0 | |||
| #define JUCE_MODULE_AVAILABLE_juce_audio_utils 0 | |||
| #define JUCE_MODULE_AVAILABLE_juce_core 1 | |||
| #define JUCE_MODULE_AVAILABLE_juce_cryptography 0 | |||
| #define JUCE_MODULE_AVAILABLE_juce_data_structures 0 | |||
| #define JUCE_MODULE_AVAILABLE_juce_events 1 | |||
| #define JUCE_MODULE_AVAILABLE_juce_graphics 0 | |||
| #define JUCE_MODULE_AVAILABLE_juce_gui_basics 0 | |||
| #define JUCE_MODULE_AVAILABLE_juce_gui_extra 0 | |||
| #define JUCE_MODULE_AVAILABLE_juce_opengl 0 | |||
| #define JUCE_MODULE_AVAILABLE_juce_video 0 | |||
| #endif // CARLA_JUCE_CORE_APPCONFIG_H_INCLUDED | |||
| @@ -0,0 +1,88 @@ | |||
| #!/usr/bin/make -f | |||
| # Makefile for juce_core # | |||
| # ---------------------- # | |||
| # Created by falkTX | |||
| # | |||
| include ../../Makefile.mk | |||
| # -------------------------------------------------------------- | |||
| BUILD_CXX_FLAGS += -I. | |||
| ifeq ($(MACOS),true) | |||
| BUILD_CXX_FLAGS += -objc++ | |||
| OBJS = juce_core.mm.o | |||
| OBJS_posix32 = juce_core.mm.posix32.o | |||
| OBJS_posix64 = juce_core.mm.posix64.o | |||
| else | |||
| OBJS = juce_core.cpp.o | |||
| OBJS_posix32 = juce_core.cpp.posix32.o | |||
| OBJS_posix64 = juce_core.cpp.posix64.o | |||
| endif | |||
| OBJS_win32 = juce_core.cpp.win32.o | |||
| OBJS_win64 = juce_core.cpp.win64.o | |||
| # -------------------------------------------------------------- | |||
| all: ../juce_core.a | |||
| posix32: ../juce_core.posix32.a | |||
| posix64: ../juce_core.posix64.a | |||
| win32: ../juce_core.win32.a | |||
| win64: ../juce_core.win64.a | |||
| # -------------------------------------------------------------- | |||
| ../juce_core.a: $(OBJS) | |||
| $(AR) rs $@ $^ | |||
| ../juce_core.posix32.a: $(OBJS_posix32) | |||
| $(AR) rs $@ $^ | |||
| ../juce_core.posix64.a: $(OBJS_posix64) | |||
| $(AR) rs $@ $^ | |||
| ../juce_core.win32.a: $(OBJS_win32) | |||
| $(AR) rs $@ $^ | |||
| ../juce_core.win64.a: $(OBJS_win64) | |||
| $(AR) rs $@ $^ | |||
| ../juce_core.dll: $(OBJS) | |||
| $(CXX) $^ -shared $(LINK_FLAGS) -o $@ $(CMD_STRIP) $@ | |||
| ../juce_core.dylib: $(OBJS) | |||
| $(CXX) $^ -dynamiclib $(LINK_FLAGS) -o $@ $(CMD_STRIP) $@ | |||
| ../juce_core.so: $(OBJS) | |||
| $(CXX) $^ -shared $(LINK_FLAGS) -o $@ $(CMD_STRIP) $@ | |||
| # -------------------------------------------------------------- | |||
| %.cpp.o: %.cpp | |||
| $(CXX) $< $(BUILD_CXX_FLAGS) -c -o $@ | |||
| %.mm.o: %.mm | |||
| $(CXX) $< $(BUILD_CXX_FLAGS) -c -o $@ | |||
| %.posix32.o: % | |||
| $(CXX) $< $(BUILD_CXX_FLAGS) $(32BIT_FLAGS) -c -o $@ | |||
| %.posix64.o: % | |||
| $(CXX) $< $(BUILD_CXX_FLAGS) $(64BIT_FLAGS) -c -o $@ | |||
| %.win32.o: % | |||
| $(CXX) $< $(BUILD_CXX_FLAGS) $(32BIT_FLAGS) -c -o $@ | |||
| %.win64.o: % | |||
| $(CXX) $< $(BUILD_CXX_FLAGS) $(64BIT_FLAGS) -c -o $@ | |||
| # -------------------------------------------------------------- | |||
| clean: | |||
| rm -f *.o ../juce_core.* | |||
| debug: | |||
| $(MAKE) DEBUG=true | |||
| @@ -0,0 +1,234 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the juce_core module of the JUCE library. | |||
| Copyright (c) 2013 - Raw Material Software Ltd. | |||
| 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 THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||
| TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||
| NO EVENT SHALL THE AUTHOR 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. | |||
| ------------------------------------------------------------------------------ | |||
| NOTE! This permissive ISC license applies ONLY to files within the juce_core module! | |||
| All other JUCE modules are covered by a dual GPL/commercial license, so if you are | |||
| using any other modules, be sure to check that you also comply with their license. | |||
| For more details, visit www.juce.com | |||
| ============================================================================== | |||
| */ | |||
| 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(); } | |||
| 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") {} | |||
| class WriteThread : public Thread | |||
| { | |||
| public: | |||
| WriteThread (AbstractFifo& fifo_, int* buffer_) | |||
| : Thread ("fifo writer"), fifo (fifo_), buffer (buffer_) | |||
| { | |||
| startThread(); | |||
| } | |||
| ~WriteThread() | |||
| { | |||
| stopThread (5000); | |||
| } | |||
| void run() | |||
| { | |||
| int n = 0; | |||
| Random r; | |||
| while (! threadShouldExit()) | |||
| { | |||
| int num = r.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; | |||
| }; | |||
| void runTest() | |||
| { | |||
| beginTest ("AbstractFifo"); | |||
| int buffer [5000]; | |||
| AbstractFifo fifo (numElementsInArray (buffer)); | |||
| WriteThread writer (fifo, buffer); | |||
| int n = 0; | |||
| Random r; | |||
| 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 | |||
| @@ -0,0 +1,222 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the juce_core module of the JUCE library. | |||
| Copyright (c) 2013 - Raw Material Software Ltd. | |||
| 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 THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||
| TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||
| NO EVENT SHALL THE AUTHOR 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. | |||
| ------------------------------------------------------------------------------ | |||
| NOTE! This permissive ISC license applies ONLY to files within the juce_core module! | |||
| All other JUCE modules are covered by a dual GPL/commercial license, so if you are | |||
| using any other modules, be sure to check that you also comply with their license. | |||
| For more details, visit www.juce.com | |||
| ============================================================================== | |||
| */ | |||
| #ifndef JUCE_ABSTRACTFIFO_H_INCLUDED | |||
| #define JUCE_ABSTRACTFIFO_H_INCLUDED | |||
| #include "../memory/juce_Atomic.h" | |||
| //============================================================================== | |||
| /** | |||
| 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 (numSamples, 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) | |||
| }; | |||
| #endif // JUCE_ABSTRACTFIFO_H_INCLUDED | |||
| @@ -0,0 +1,140 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the juce_core module of the JUCE library. | |||
| Copyright (c) 2013 - Raw Material Software Ltd. | |||
| 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 THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||
| TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||
| NO EVENT SHALL THE AUTHOR 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. | |||
| ------------------------------------------------------------------------------ | |||
| NOTE! This permissive ISC license applies ONLY to files within the juce_core module! | |||
| All other JUCE modules are covered by a dual GPL/commercial license, so if you are | |||
| using any other modules, be sure to check that you also comply with their license. | |||
| For more details, visit www.juce.com | |||
| ============================================================================== | |||
| */ | |||
| #ifndef JUCE_ARRAYALLOCATIONBASE_H_INCLUDED | |||
| #define JUCE_ARRAYALLOCATIONBASE_H_INCLUDED | |||
| #include "../memory/juce_HeapBlock.h" | |||
| //============================================================================== | |||
| /** | |||
| 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 | |||
| { | |||
| } | |||
| #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS | |||
| 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; | |||
| } | |||
| #endif | |||
| //============================================================================== | |||
| /** 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) | |||
| }; | |||
| #endif // JUCE_ARRAYALLOCATIONBASE_H_INCLUDED | |||
| @@ -0,0 +1,79 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the juce_core module of the JUCE library. | |||
| Copyright (c) 2013 - Raw Material Software Ltd. | |||
| 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 THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||
| TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||
| NO EVENT SHALL THE AUTHOR 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. | |||
| ------------------------------------------------------------------------------ | |||
| NOTE! This permissive ISC license applies ONLY to files within the juce_core module! | |||
| All other JUCE modules are covered by a dual GPL/commercial license, so if you are | |||
| using any other modules, be sure to check that you also comply with their license. | |||
| For more details, visit www.juce.com | |||
| ============================================================================== | |||
| */ | |||
| DynamicObject::DynamicObject() | |||
| { | |||
| } | |||
| DynamicObject::~DynamicObject() | |||
| { | |||
| } | |||
| bool DynamicObject::hasProperty (const Identifier& propertyName) const | |||
| { | |||
| const var* const v = properties.getVarPointer (propertyName); | |||
| return v != nullptr && ! v->isMethod(); | |||
| } | |||
| 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 (const Identifier& methodName, | |||
| const var* parameters, | |||
| int numParameters) | |||
| { | |||
| return properties [methodName].invokeMethod (this, parameters, numParameters); | |||
| } | |||
| void DynamicObject::setMethod (const Identifier& name, | |||
| var::MethodFunction methodFunction) | |||
| { | |||
| properties.set (name, var (methodFunction)); | |||
| } | |||
| void DynamicObject::clear() | |||
| { | |||
| properties.clear(); | |||
| } | |||
| @@ -0,0 +1,126 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the juce_core module of the JUCE library. | |||
| Copyright (c) 2013 - Raw Material Software Ltd. | |||
| 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 THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||
| TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||
| NO EVENT SHALL THE AUTHOR 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. | |||
| ------------------------------------------------------------------------------ | |||
| NOTE! This permissive ISC license applies ONLY to files within the juce_core module! | |||
| All other JUCE modules are covered by a dual GPL/commercial license, so if you are | |||
| using any other modules, be sure to check that you also comply with their license. | |||
| For more details, visit www.juce.com | |||
| ============================================================================== | |||
| */ | |||
| #ifndef JUCE_DYNAMICOBJECT_H_INCLUDED | |||
| #define JUCE_DYNAMICOBJECT_H_INCLUDED | |||
| #include "juce_NamedValueSet.h" | |||
| #include "../memory/juce_ReferenceCountedObject.h" | |||
| //============================================================================== | |||
| /** | |||
| 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(); | |||
| /** Destructor. */ | |||
| virtual ~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 a void if no such property exists. | |||
| */ | |||
| virtual 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 (const Identifier& methodName, | |||
| const var* parameters, | |||
| int numParameters); | |||
| /** Sets up a method. | |||
| This is basically the same as calling setProperty (methodName, (var::MethodFunction) myFunction), but | |||
| helps to avoid accidentally invoking the wrong type of var constructor. It also makes | |||
| the code easier to read, | |||
| The compiler will probably force you to use an explicit cast your method to a (var::MethodFunction), e.g. | |||
| @code | |||
| setMethod ("doSomething", (var::MethodFunction) &MyClass::doSomething); | |||
| @endcode | |||
| */ | |||
| void setMethod (const Identifier& methodName, | |||
| var::MethodFunction methodFunction); | |||
| //============================================================================== | |||
| /** Removes all properties and methods from the object. */ | |||
| void clear(); | |||
| /** Returns the NamedValueSet that holds the object's properties. */ | |||
| NamedValueSet& getProperties() noexcept { return properties; } | |||
| private: | |||
| //============================================================================== | |||
| NamedValueSet properties; | |||
| JUCE_LEAK_DETECTOR (DynamicObject) | |||
| }; | |||
| #endif // JUCE_DYNAMICOBJECT_H_INCLUDED | |||
| @@ -0,0 +1,279 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the juce_core module of the JUCE library. | |||
| Copyright (c) 2013 - Raw Material Software Ltd. | |||
| 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 THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||
| TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||
| NO EVENT SHALL THE AUTHOR 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. | |||
| ------------------------------------------------------------------------------ | |||
| NOTE! This permissive ISC license applies ONLY to files within the juce_core module! | |||
| All other JUCE modules are covered by a dual GPL/commercial license, so if you are | |||
| using any other modules, be sure to check that you also comply with their license. | |||
| For more details, visit www.juce.com | |||
| ============================================================================== | |||
| */ | |||
| #ifndef JUCE_ELEMENTCOMPARATOR_H_INCLUDED | |||
| #define JUCE_ELEMENTCOMPARATOR_H_INCLUDED | |||
| //============================================================================== | |||
| /** | |||
| 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) | |||
| { | |||
| (void) comparator; // if you pass in an object with a static compareElements() method, this | |||
| // avoids getting warning messages about the parameter being unused | |||
| if (lastElement > firstElement) | |||
| { | |||
| if (retainOrderOfEquivalentItems) | |||
| { | |||
| for (int i = firstElement; i < lastElement; ++i) | |||
| { | |||
| if (comparator.compareElements (array[i], array [i + 1]) > 0) | |||
| { | |||
| std::swap (array[i], array[i + 1]); | |||
| if (i > firstElement) | |||
| i -= 2; | |||
| } | |||
| } | |||
| } | |||
| else | |||
| { | |||
| int fromStack[30], toStack[30]; | |||
| int stackIndex = 0; | |||
| for (;;) | |||
| { | |||
| const int size = (lastElement - firstElement) + 1; | |||
| if (size <= 8) | |||
| { | |||
| int j = lastElement; | |||
| int maxIndex; | |||
| while (j > firstElement) | |||
| { | |||
| maxIndex = firstElement; | |||
| for (int k = firstElement + 1; k <= j; ++k) | |||
| if (comparator.compareElements (array[k], array [maxIndex]) > 0) | |||
| maxIndex = k; | |||
| std::swap (array[j], array[maxIndex]); | |||
| --j; | |||
| } | |||
| } | |||
| else | |||
| { | |||
| const int mid = firstElement + (size >> 1); | |||
| std::swap (array[mid], array[firstElement]); | |||
| int i = firstElement; | |||
| int j = lastElement + 1; | |||
| for (;;) | |||
| { | |||
| while (++i <= lastElement | |||
| && comparator.compareElements (array[i], array [firstElement]) <= 0) | |||
| {} | |||
| while (--j > firstElement | |||
| && comparator.compareElements (array[j], array [firstElement]) >= 0) | |||
| {} | |||
| if (j < i) | |||
| break; | |||
| std::swap (array[i], array[j]); | |||
| } | |||
| std::swap (array[j], array[firstElement]); | |||
| if (j - 1 - firstElement >= lastElement - i) | |||
| { | |||
| if (firstElement + 1 < j) | |||
| { | |||
| fromStack [stackIndex] = firstElement; | |||
| toStack [stackIndex] = j - 1; | |||
| ++stackIndex; | |||
| } | |||
| if (i < lastElement) | |||
| { | |||
| firstElement = i; | |||
| continue; | |||
| } | |||
| } | |||
| else | |||
| { | |||
| if (i < lastElement) | |||
| { | |||
| fromStack [stackIndex] = i; | |||
| toStack [stackIndex] = lastElement; | |||
| ++stackIndex; | |||
| } | |||
| if (firstElement + 1 < j) | |||
| { | |||
| lastElement = j - 1; | |||
| continue; | |||
| } | |||
| } | |||
| } | |||
| if (--stackIndex < 0) | |||
| break; | |||
| jassert (stackIndex < numElementsInArray (fromStack)); | |||
| firstElement = fromStack [stackIndex]; | |||
| lastElement = toStack [stackIndex]; | |||
| } | |||
| } | |||
| } | |||
| } | |||
| //============================================================================== | |||
| /** | |||
| 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); | |||
| (void) 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 PARAMETER_TYPE (ElementType) ParameterType; | |||
| public: | |||
| static int compareElements (ParameterType first, ParameterType second) | |||
| { | |||
| return (first < second) ? -1 : ((second < first) ? 1 : 0); | |||
| } | |||
| }; | |||
| #endif // JUCE_ELEMENTCOMPARATOR_H_INCLUDED | |||
| @@ -0,0 +1,460 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the juce_core module of the JUCE library. | |||
| Copyright (c) 2013 - Raw Material Software Ltd. | |||
| 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 THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||
| TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||
| NO EVENT SHALL THE AUTHOR 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. | |||
| ------------------------------------------------------------------------------ | |||
| NOTE! This permissive ISC license applies ONLY to files within the juce_core module! | |||
| All other JUCE modules are covered by a dual GPL/commercial license, so if you are | |||
| using any other modules, be sure to check that you also comply with their license. | |||
| For more details, visit www.juce.com | |||
| ============================================================================== | |||
| */ | |||
| #ifndef JUCE_HASHMAP_H_INCLUDED | |||
| #define JUCE_HASHMAP_H_INCLUDED | |||
| #include "juce_OwnedArray.h" | |||
| #include "juce_LinkedListPointer.h" | |||
| #include "../memory/juce_ScopedPointer.h" | |||
| //============================================================================== | |||
| /** | |||
| 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 integer. */ | |||
| int generateHash (const int key, const int upperLimit) const noexcept { return std::abs (key) % upperLimit; } | |||
| /** Generates a simple hash from an int64. */ | |||
| int generateHash (const int64 key, const int upperLimit) const noexcept { return std::abs ((int) key) % upperLimit; } | |||
| /** Generates a simple hash from a string. */ | |||
| int generateHash (const String& key, const int upperLimit) const noexcept { return (int) (((uint32) key.hashCode()) % (uint32) upperLimit); } | |||
| /** Generates a simple hash from a variant. */ | |||
| int generateHash (const var& key, const int upperLimit) const noexcept { return generateHash (key.toString(), 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) | |||
| { | |||
| // 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 PARAMETER_TYPE (KeyType) KeyTypeParameter; | |||
| typedef PARAMETER_TYPE (ValueType) ValueTypeParameter; | |||
| public: | |||
| //============================================================================== | |||
| /** Creates an empty hash-map. | |||
| The numberOfSlots parameter 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) | |||
| { | |||
| slots.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 (int i = slots.size(); --i >= 0;) | |||
| { | |||
| HashEntry* h = slots.getUnchecked(i); | |||
| while (h != nullptr) | |||
| { | |||
| const ScopedPointer<HashEntry> deleter (h); | |||
| h = h->nextEntry; | |||
| } | |||
| slots.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()); | |||
| for (const HashEntry* entry = slots.getUnchecked (generateHashFor (keyToLookFor)); entry != nullptr; entry = entry->nextEntry) | |||
| if (entry->key == keyToLookFor) | |||
| return entry->value; | |||
| return ValueType(); | |||
| } | |||
| //============================================================================== | |||
| /** Returns true if the map contains an item with the specied key. */ | |||
| bool contains (KeyTypeParameter keyToLookFor) const | |||
| { | |||
| const ScopedLockType sl (getLock()); | |||
| for (const HashEntry* entry = slots.getUnchecked (generateHashFor (keyToLookFor)); entry != nullptr; entry = entry->nextEntry) | |||
| if (entry->key == keyToLookFor) | |||
| return true; | |||
| return false; | |||
| } | |||
| /** Returns true if the hash contains at least one occurrence of a given value. */ | |||
| bool containsValue (ValueTypeParameter valueToLookFor) const | |||
| { | |||
| const ScopedLockType sl (getLock()); | |||
| for (int i = getNumSlots(); --i >= 0;) | |||
| for (const HashEntry* entry = slots.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) | |||
| { | |||
| const ScopedLockType sl (getLock()); | |||
| const int hashIndex = generateHashFor (newKey); | |||
| HashEntry* const firstEntry = slots.getUnchecked (hashIndex); | |||
| for (HashEntry* entry = firstEntry; entry != nullptr; entry = entry->nextEntry) | |||
| { | |||
| if (entry->key == newKey) | |||
| { | |||
| entry->value = newValue; | |||
| return; | |||
| } | |||
| } | |||
| slots.set (hashIndex, new HashEntry (newKey, newValue, firstEntry)); | |||
| ++totalNumItems; | |||
| if (totalNumItems > (getNumSlots() * 3) / 2) | |||
| remapTable (getNumSlots() * 2); | |||
| } | |||
| /** Removes an item with the given key. */ | |||
| void remove (KeyTypeParameter keyToRemove) | |||
| { | |||
| const ScopedLockType sl (getLock()); | |||
| const int hashIndex = generateHashFor (keyToRemove); | |||
| HashEntry* entry = slots.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 | |||
| slots.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 (int i = getNumSlots(); --i >= 0;) | |||
| { | |||
| HashEntry* entry = slots.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 | |||
| slots.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) | |||
| { | |||
| HashMap newTable (newNumberOfSlots); | |||
| for (int i = getNumSlots(); --i >= 0;) | |||
| for (const HashEntry* entry = slots.getUnchecked(i); entry != nullptr; entry = entry->nextEntry) | |||
| newTable.set (entry->key, entry->value); | |||
| swapWith (newTable); | |||
| } | |||
| /** 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 slots.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()); | |||
| slots.swapWith (otherHashMap.slots); | |||
| 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 | |||
| */ | |||
| class Iterator | |||
| { | |||
| public: | |||
| //============================================================================== | |||
| Iterator (const HashMap& hashMapToIterate) | |||
| : hashMap (hashMapToIterate), entry (nullptr), index (0) | |||
| {} | |||
| /** 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() | |||
| { | |||
| if (entry != nullptr) | |||
| entry = entry->nextEntry; | |||
| while (entry == nullptr) | |||
| { | |||
| if (index >= hashMap.getNumSlots()) | |||
| return false; | |||
| entry = hashMap.slots.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(); | |||
| } | |||
| private: | |||
| //============================================================================== | |||
| const HashMap& hashMap; | |||
| HashEntry* entry; | |||
| int index; | |||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Iterator) | |||
| }; | |||
| private: | |||
| //============================================================================== | |||
| enum { defaultHashTableSize = 101 }; | |||
| friend class Iterator; | |||
| HashFunctionType hashFunctionToUse; | |||
| Array <HashEntry*> slots; | |||
| int totalNumItems; | |||
| TypeOfCriticalSectionToUse lock; | |||
| int generateHashFor (KeyTypeParameter key) const | |||
| { | |||
| const int hash = hashFunctionToUse.generateHash (key, getNumSlots()); | |||
| jassert (isPositiveAndBelow (hash, getNumSlots())); // your hash function is generating out-of-range numbers! | |||
| return hash; | |||
| } | |||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (HashMap) | |||
| }; | |||
| #endif // JUCE_HASHMAP_H_INCLUDED | |||
| @@ -0,0 +1,371 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the juce_core module of the JUCE library. | |||
| Copyright (c) 2013 - Raw Material Software Ltd. | |||
| 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 THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||
| TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||
| NO EVENT SHALL THE AUTHOR 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. | |||
| ------------------------------------------------------------------------------ | |||
| NOTE! This permissive ISC license applies ONLY to files within the juce_core module! | |||
| All other JUCE modules are covered by a dual GPL/commercial license, so if you are | |||
| using any other modules, be sure to check that you also comply with their license. | |||
| For more details, visit www.juce.com | |||
| ============================================================================== | |||
| */ | |||
| #ifndef JUCE_LINKEDLISTPOINTER_H_INCLUDED | |||
| #define JUCE_LINKEDLISTPOINTER_H_INCLUDED | |||
| //============================================================================== | |||
| /** | |||
| 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; | |||
| } | |||
| #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS | |||
| 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; | |||
| } | |||
| #endif | |||
| //============================================================================== | |||
| /** 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) | |||
| }; | |||
| #endif // JUCE_LINKEDLISTPOINTER_H_INCLUDED | |||
| @@ -0,0 +1,309 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the juce_core module of the JUCE library. | |||
| Copyright (c) 2013 - Raw Material Software Ltd. | |||
| 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 THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||
| TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||
| NO EVENT SHALL THE AUTHOR 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. | |||
| ------------------------------------------------------------------------------ | |||
| NOTE! This permissive ISC license applies ONLY to files within the juce_core module! | |||
| All other JUCE modules are covered by a dual GPL/commercial license, so if you are | |||
| using any other modules, be sure to check that you also comply with their license. | |||
| For more details, visit www.juce.com | |||
| ============================================================================== | |||
| */ | |||
| NamedValueSet::NamedValue::NamedValue() noexcept | |||
| { | |||
| } | |||
| inline NamedValueSet::NamedValue::NamedValue (const Identifier n, const var& v) | |||
| : name (n), value (v) | |||
| { | |||
| } | |||
| NamedValueSet::NamedValue::NamedValue (const NamedValue& other) | |||
| : name (other.name), value (other.value) | |||
| { | |||
| } | |||
| NamedValueSet::NamedValue& NamedValueSet::NamedValue::operator= (const NamedValueSet::NamedValue& other) | |||
| { | |||
| name = other.name; | |||
| value = other.value; | |||
| return *this; | |||
| } | |||
| #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS | |||
| NamedValueSet::NamedValue::NamedValue (NamedValue&& other) noexcept | |||
| : nextListItem (static_cast <LinkedListPointer<NamedValue>&&> (other.nextListItem)), | |||
| name (static_cast <Identifier&&> (other.name)), | |||
| value (static_cast <var&&> (other.value)) | |||
| { | |||
| } | |||
| inline NamedValueSet::NamedValue::NamedValue (const Identifier n, var&& v) | |||
| : name (n), value (static_cast <var&&> (v)) | |||
| { | |||
| } | |||
| NamedValueSet::NamedValue& NamedValueSet::NamedValue::operator= (NamedValue&& other) noexcept | |||
| { | |||
| nextListItem = static_cast <LinkedListPointer<NamedValue>&&> (other.nextListItem); | |||
| name = static_cast <Identifier&&> (other.name); | |||
| value = static_cast <var&&> (other.value); | |||
| return *this; | |||
| } | |||
| #endif | |||
| bool NamedValueSet::NamedValue::operator== (const NamedValueSet::NamedValue& other) const noexcept | |||
| { | |||
| return name == other.name && value == other.value; | |||
| } | |||
| //============================================================================== | |||
| NamedValueSet::NamedValueSet() noexcept | |||
| { | |||
| } | |||
| NamedValueSet::NamedValueSet (const NamedValueSet& other) | |||
| { | |||
| values.addCopyOfList (other.values); | |||
| } | |||
| NamedValueSet& NamedValueSet::operator= (const NamedValueSet& other) | |||
| { | |||
| clear(); | |||
| values.addCopyOfList (other.values); | |||
| return *this; | |||
| } | |||
| #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS | |||
| NamedValueSet::NamedValueSet (NamedValueSet&& other) noexcept | |||
| : values (static_cast <LinkedListPointer<NamedValue>&&> (other.values)) | |||
| { | |||
| } | |||
| NamedValueSet& NamedValueSet::operator= (NamedValueSet&& other) noexcept | |||
| { | |||
| other.values.swapWith (values); | |||
| return *this; | |||
| } | |||
| #endif | |||
| NamedValueSet::~NamedValueSet() | |||
| { | |||
| clear(); | |||
| } | |||
| void NamedValueSet::clear() | |||
| { | |||
| values.deleteAll(); | |||
| } | |||
| bool NamedValueSet::operator== (const NamedValueSet& other) const | |||
| { | |||
| const NamedValue* i1 = values; | |||
| const NamedValue* i2 = other.values; | |||
| while (i1 != nullptr && i2 != nullptr) | |||
| { | |||
| if (! (*i1 == *i2)) | |||
| return false; | |||
| i1 = i1->nextListItem; | |||
| i2 = i2->nextListItem; | |||
| } | |||
| return true; | |||
| } | |||
| bool NamedValueSet::operator!= (const NamedValueSet& other) const | |||
| { | |||
| return ! operator== (other); | |||
| } | |||
| int NamedValueSet::size() const noexcept | |||
| { | |||
| return values.size(); | |||
| } | |||
| const var& NamedValueSet::operator[] (const Identifier name) const | |||
| { | |||
| for (NamedValue* i = values; i != nullptr; i = i->nextListItem) | |||
| if (i->name == name) | |||
| return i->value; | |||
| return var::null; | |||
| } | |||
| 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* i = values; i != nullptr; i = i->nextListItem) | |||
| if (i->name == name) | |||
| return &(i->value); | |||
| return nullptr; | |||
| } | |||
| #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS | |||
| bool NamedValueSet::set (const Identifier name, var&& newValue) | |||
| { | |||
| LinkedListPointer<NamedValue>* i = &values; | |||
| while (i->get() != nullptr) | |||
| { | |||
| NamedValue* const v = i->get(); | |||
| if (v->name == name) | |||
| { | |||
| if (v->value.equalsWithSameType (newValue)) | |||
| return false; | |||
| v->value = static_cast <var&&> (newValue); | |||
| return true; | |||
| } | |||
| i = &(v->nextListItem); | |||
| } | |||
| i->insertNext (new NamedValue (name, static_cast <var&&> (newValue))); | |||
| return true; | |||
| } | |||
| #endif | |||
| bool NamedValueSet::set (const Identifier name, const var& newValue) | |||
| { | |||
| LinkedListPointer<NamedValue>* i = &values; | |||
| while (i->get() != nullptr) | |||
| { | |||
| NamedValue* const v = i->get(); | |||
| if (v->name == name) | |||
| { | |||
| if (v->value.equalsWithSameType (newValue)) | |||
| return false; | |||
| v->value = newValue; | |||
| return true; | |||
| } | |||
| i = &(v->nextListItem); | |||
| } | |||
| i->insertNext (new NamedValue (name, newValue)); | |||
| return true; | |||
| } | |||
| bool NamedValueSet::contains (const Identifier name) const | |||
| { | |||
| return getVarPointer (name) != nullptr; | |||
| } | |||
| bool NamedValueSet::remove (const Identifier name) | |||
| { | |||
| LinkedListPointer<NamedValue>* i = &values; | |||
| for (;;) | |||
| { | |||
| NamedValue* const v = i->get(); | |||
| if (v == nullptr) | |||
| break; | |||
| if (v->name == name) | |||
| { | |||
| delete i->removeNext(); | |||
| return true; | |||
| } | |||
| i = &(v->nextListItem); | |||
| } | |||
| return false; | |||
| } | |||
| const Identifier NamedValueSet::getName (const int index) const | |||
| { | |||
| const NamedValue* const v = values[index]; | |||
| jassert (v != nullptr); | |||
| return v->name; | |||
| } | |||
| const var& NamedValueSet::getValueAt (const int index) const | |||
| { | |||
| const NamedValue* const v = values[index]; | |||
| jassert (v != nullptr); | |||
| return v->value; | |||
| } | |||
| void NamedValueSet::setFromXmlAttributes (const XmlElement& xml) | |||
| { | |||
| clear(); | |||
| LinkedListPointer<NamedValue>::Appender appender (values); | |||
| const int numAtts = xml.getNumAttributes(); // xxx inefficient - should write an att iterator.. | |||
| for (int i = 0; i < numAtts; ++i) | |||
| { | |||
| const String& name = xml.getAttributeName (i); | |||
| const String& value = xml.getAttributeValue (i); | |||
| if (name.startsWith ("base64:")) | |||
| { | |||
| MemoryBlock mb; | |||
| if (mb.fromBase64Encoding (value)) | |||
| { | |||
| appender.append (new NamedValue (name.substring (7), var (mb))); | |||
| continue; | |||
| } | |||
| } | |||
| appender.append (new NamedValue (name, var (value))); | |||
| } | |||
| } | |||
| void NamedValueSet::copyToXmlAttributes (XmlElement& xml) const | |||
| { | |||
| for (NamedValue* i = values; i != nullptr; i = i->nextListItem) | |||
| { | |||
| 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()); | |||
| } | |||
| } | |||
| } | |||
| @@ -0,0 +1,169 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the juce_core module of the JUCE library. | |||
| Copyright (c) 2013 - Raw Material Software Ltd. | |||
| 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 THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||
| TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||
| NO EVENT SHALL THE AUTHOR 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. | |||
| ------------------------------------------------------------------------------ | |||
| NOTE! This permissive ISC license applies ONLY to files within the juce_core module! | |||
| All other JUCE modules are covered by a dual GPL/commercial license, so if you are | |||
| using any other modules, be sure to check that you also comply with their license. | |||
| For more details, visit www.juce.com | |||
| ============================================================================== | |||
| */ | |||
| #ifndef JUCE_NAMEDVALUESET_H_INCLUDED | |||
| #define JUCE_NAMEDVALUESET_H_INCLUDED | |||
| #include "juce_Variant.h" | |||
| #include "../containers/juce_LinkedListPointer.h" | |||
| class XmlElement; | |||
| #ifndef DOXYGEN | |||
| class JSONFormatter; | |||
| #endif | |||
| //============================================================================== | |||
| /** 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& other); | |||
| /** Replaces this set with a copy of another set. */ | |||
| NamedValueSet& operator= (const NamedValueSet& other); | |||
| #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS | |||
| NamedValueSet (NamedValueSet&& other) noexcept; | |||
| NamedValueSet& operator= (NamedValueSet&& other) noexcept; | |||
| #endif | |||
| /** Destructor. */ | |||
| ~NamedValueSet(); | |||
| bool operator== (const NamedValueSet& other) const; | |||
| bool operator!= (const NamedValueSet& other) const; | |||
| //============================================================================== | |||
| /** Returns the total number of values that the set contains. */ | |||
| int size() 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; | |||
| /** 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 the value passed-in. | |||
| */ | |||
| bool set (const Identifier name, const var& newValue); | |||
| #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS | |||
| /** Changes or adds a named value. | |||
| @returns true if a value was changed or added; false if the | |||
| value was already set the the value passed-in. | |||
| */ | |||
| bool set (const Identifier name, var&& newValue); | |||
| #endif | |||
| /** Returns true if the set contains an item with the specified name. */ | |||
| bool contains (const Identifier name) const; | |||
| /** 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. | |||
| */ | |||
| const Identifier getName (int index) const; | |||
| /** 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; | |||
| /** Removes all values. */ | |||
| void clear(); | |||
| //============================================================================== | |||
| /** 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; | |||
| //============================================================================== | |||
| /** 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: | |||
| //============================================================================== | |||
| class NamedValue | |||
| { | |||
| public: | |||
| NamedValue() noexcept; | |||
| NamedValue (const NamedValue&); | |||
| NamedValue (const Identifier name, const var& value); | |||
| NamedValue& operator= (const NamedValue&); | |||
| #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS | |||
| NamedValue (NamedValue&&) noexcept; | |||
| NamedValue (const Identifier name, var&& value); | |||
| NamedValue& operator= (NamedValue&&) noexcept; | |||
| #endif | |||
| bool operator== (const NamedValue& other) const noexcept; | |||
| LinkedListPointer<NamedValue> nextListItem; | |||
| Identifier name; | |||
| var value; | |||
| private: | |||
| JUCE_LEAK_DETECTOR (NamedValue) | |||
| }; | |||
| friend class LinkedListPointer<NamedValue>; | |||
| LinkedListPointer<NamedValue> values; | |||
| friend class JSONFormatter; | |||
| }; | |||
| #endif // JUCE_NAMEDVALUESET_H_INCLUDED | |||
| @@ -0,0 +1,897 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the juce_core module of the JUCE library. | |||
| Copyright (c) 2013 - Raw Material Software Ltd. | |||
| 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 THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||
| TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||
| NO EVENT SHALL THE AUTHOR 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. | |||
| ------------------------------------------------------------------------------ | |||
| NOTE! This permissive ISC license applies ONLY to files within the juce_core module! | |||
| All other JUCE modules are covered by a dual GPL/commercial license, so if you are | |||
| using any other modules, be sure to check that you also comply with their license. | |||
| For more details, visit www.juce.com | |||
| ============================================================================== | |||
| */ | |||
| #ifndef JUCE_OWNEDARRAY_H_INCLUDED | |||
| #define JUCE_OWNEDARRAY_H_INCLUDED | |||
| #include "juce_ArrayAllocationBase.h" | |||
| #include "juce_ElementComparator.h" | |||
| #include "../threads/juce_CriticalSection.h" | |||
| //============================================================================== | |||
| /** 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(); | |||
| } | |||
| #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS | |||
| OwnedArray (OwnedArray&& other) noexcept | |||
| : data (static_cast <ArrayAllocationBase <ObjectClass*, TypeOfCriticalSectionToUse>&&> (other.data)), | |||
| numUsed (other.numUsed) | |||
| { | |||
| other.numUsed = 0; | |||
| } | |||
| 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; | |||
| } | |||
| #endif | |||
| //============================================================================== | |||
| /** 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 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()); | |||
| return numUsed > 0 ? data.elements [0] | |||
| : static_cast <ObjectClass*> (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()); | |||
| return numUsed > 0 ? data.elements [numUsed - 1] | |||
| : static_cast <ObjectClass*> (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 | |||
| { | |||
| 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* const objectToLookFor) const noexcept | |||
| { | |||
| const ScopedLockType lock (getLock()); | |||
| ObjectClass* const* e = data.elements.getData(); | |||
| ObjectClass* const* const end_ = e + numUsed; | |||
| for (; e != end_; ++e) | |||
| if (objectToLookFor == *e) | |||
| return static_cast <int> (e - data.elements.getData()); | |||
| 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* const* e = data.elements.getData(); | |||
| 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 | |||
| @see set, insert, addIfNotAlreadyThere, addSorted | |||
| */ | |||
| ObjectClass* add (ObjectClass* const newObject) noexcept | |||
| { | |||
| const ScopedLockType lock (getLock()); | |||
| data.ensureAllocatedSize (numUsed + 1); | |||
| jassert (data.elements != nullptr); | |||
| data.elements [numUsed++] = const_cast <ObjectClass*> (newObject); | |||
| return const_cast <ObjectClass*> (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 | |||
| @see add, addSorted, addIfNotAlreadyThere, set | |||
| */ | |||
| void insert (int indexToInsertAt, | |||
| ObjectClass* const newObject) noexcept | |||
| { | |||
| if (indexToInsertAt >= 0) | |||
| { | |||
| 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 = const_cast <ObjectClass*> (newObject); | |||
| ++numUsed; | |||
| } | |||
| else | |||
| { | |||
| add (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 | |||
| */ | |||
| void addIfNotAlreadyThere (ObjectClass* const newObject) noexcept | |||
| { | |||
| const ScopedLockType lock (getLock()); | |||
| if (! contains (newObject)) | |||
| add (newObject); | |||
| } | |||
| /** Replaces an object in the array with a different one. | |||
| If the index is less than zero, this method does nothing. | |||
| If the index is beyond the end of the array, the new object is added to the end of the array. | |||
| 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 | |||
| */ | |||
| void set (const int indexToChange, | |||
| const ObjectClass* const newObject, | |||
| const bool deleteOldElement = true) | |||
| { | |||
| if (indexToChange >= 0) | |||
| { | |||
| ObjectClass* toDelete = nullptr; | |||
| { | |||
| const ScopedLockType lock (getLock()); | |||
| if (indexToChange < numUsed) | |||
| { | |||
| if (deleteOldElement) | |||
| { | |||
| toDelete = data.elements [indexToChange]; | |||
| if (toDelete == newObject) | |||
| toDelete = nullptr; | |||
| } | |||
| data.elements [indexToChange] = const_cast <ObjectClass*> (newObject); | |||
| } | |||
| else | |||
| { | |||
| data.ensureAllocatedSize (numUsed + 1); | |||
| data.elements [numUsed++] = const_cast <ObjectClass*> (newObject); | |||
| } | |||
| } | |||
| delete toDelete; // don't want to use a ScopedPointer here because if the | |||
| // object has a private destructor, both OwnedArray and | |||
| // ScopedPointer would need to be friend classes.. | |||
| } | |||
| 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.. | |||
| } | |||
| } | |||
| /** 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] = new ObjectClass (*arrayToAddFrom.getUnchecked (startIndex++)); | |||
| ++numUsed; | |||
| } | |||
| } | |||
| /** 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 | |||
| { | |||
| (void) 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.getData(), 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 | |||
| { | |||
| (void) 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 (const int indexToRemove, | |||
| const bool deleteObject = true) | |||
| { | |||
| ObjectClass* toDelete = nullptr; | |||
| { | |||
| 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); | |||
| } | |||
| } | |||
| delete toDelete; // don't want to use a ScopedPointer here because if the | |||
| // object has a private destructor, both OwnedArray and | |||
| // ScopedPointer would need to be friend classes.. | |||
| 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 (const 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* const objectToRemove, | |||
| const bool deleteObject = true) | |||
| { | |||
| const ScopedLockType lock (getLock()); | |||
| ObjectClass** const e = data.elements.getData(); | |||
| 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, | |||
| const int numberToRemove, | |||
| const 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) | |||
| { | |||
| delete 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, | |||
| const 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 (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); | |||
| } | |||
| //============================================================================== | |||
| /** 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, | |||
| const bool retainOrderOfEquivalentItems = false) const noexcept | |||
| { | |||
| (void) 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.getData(), 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; | |||
| //============================================================================== | |||
| // 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); }) | |||
| private: | |||
| //============================================================================== | |||
| ArrayAllocationBase <ObjectClass*, TypeOfCriticalSectionToUse> data; | |||
| int numUsed; | |||
| void deleteAllObjects() | |||
| { | |||
| while (numUsed > 0) | |||
| delete data.elements [--numUsed]; | |||
| } | |||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OwnedArray) | |||
| }; | |||
| #endif // JUCE_OWNEDARRAY_H_INCLUDED | |||
| @@ -0,0 +1,223 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the juce_core module of the JUCE library. | |||
| Copyright (c) 2013 - Raw Material Software Ltd. | |||
| 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 THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||
| TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||
| NO EVENT SHALL THE AUTHOR 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. | |||
| ------------------------------------------------------------------------------ | |||
| NOTE! This permissive ISC license applies ONLY to files within the juce_core module! | |||
| All other JUCE modules are covered by a dual GPL/commercial license, so if you are | |||
| using any other modules, be sure to check that you also comply with their license. | |||
| For more details, visit www.juce.com | |||
| ============================================================================== | |||
| */ | |||
| 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 (const String& 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 (const String& 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 (const String& 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 (const String& 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 (const String& 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 (const String& 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::null | |||
| : var (xml->createDocument (String::empty, true))); | |||
| } | |||
| bool PropertySet::containsKey (const String& 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() | |||
| { | |||
| } | |||
| @@ -0,0 +1,219 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the juce_core module of the JUCE library. | |||
| Copyright (c) 2013 - Raw Material Software Ltd. | |||
| 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 THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||
| TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||
| NO EVENT SHALL THE AUTHOR 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. | |||
| ------------------------------------------------------------------------------ | |||
| NOTE! This permissive ISC license applies ONLY to files within the juce_core module! | |||
| All other JUCE modules are covered by a dual GPL/commercial license, so if you are | |||
| using any other modules, be sure to check that you also comply with their license. | |||
| For more details, visit www.juce.com | |||
| ============================================================================== | |||
| */ | |||
| #ifndef JUCE_PROPERTYSET_H_INCLUDED | |||
| #define JUCE_PROPERTYSET_H_INCLUDED | |||
| #include "../text/juce_StringPairArray.h" | |||
| #include "../xml/juce_XmlElement.h" | |||
| #include "../containers/juce_Variant.h" | |||
| //============================================================================== | |||
| /** | |||
| 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 (const String& keyName, | |||
| const String& defaultReturnValue = String::empty) 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 (const String& keyName, | |||
| const 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 (const String& keyName, | |||
| const 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 (const String& keyName, | |||
| const 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 0 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 (const String& 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 zero, 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 (const String& keyName); | |||
| /** Returns true if the properies include the given key. */ | |||
| bool containsKey (const String& 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) | |||
| }; | |||
| #endif // JUCE_PROPERTYSET_H_INCLUDED | |||
| @@ -0,0 +1,872 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the juce_core module of the JUCE library. | |||
| Copyright (c) 2013 - Raw Material Software Ltd. | |||
| 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 THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||
| TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||
| NO EVENT SHALL THE AUTHOR 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. | |||
| ------------------------------------------------------------------------------ | |||
| NOTE! This permissive ISC license applies ONLY to files within the juce_core module! | |||
| All other JUCE modules are covered by a dual GPL/commercial license, so if you are | |||
| using any other modules, be sure to check that you also comply with their license. | |||
| For more details, visit www.juce.com | |||
| ============================================================================== | |||
| */ | |||
| #ifndef JUCE_REFERENCECOUNTEDARRAY_H_INCLUDED | |||
| #define JUCE_REFERENCECOUNTEDARRAY_H_INCLUDED | |||
| #include "../memory/juce_ReferenceCountedObject.h" | |||
| #include "juce_ArrayAllocationBase.h" | |||
| #include "juce_ElementComparator.h" | |||
| #include "../threads/juce_CriticalSection.h" | |||
| //============================================================================== | |||
| /** | |||
| Holds a list of objects derived from ReferenceCountedObject. | |||
| 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(), 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() | |||
| { | |||
| clear(); | |||
| } | |||
| //============================================================================== | |||
| /** Removes all objects from the array. | |||
| Any objects in the array that are not referenced from elsewhere will be deleted. | |||
| */ | |||
| void clear() | |||
| { | |||
| const ScopedLockType lock (getLock()); | |||
| while (numUsed > 0) | |||
| if (ObjectClass* o = data.elements [--numUsed]) | |||
| o->decReferenceCount(); | |||
| jassert (numUsed == 0); | |||
| data.setAllocatedSize (0); | |||
| } | |||
| /** Returns the current number of objects in the array. */ | |||
| inline int size() const noexcept | |||
| { | |||
| return numUsed; | |||
| } | |||
| /** 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()); | |||
| return isPositiveAndBelow (index, numUsed) ? data.elements [index] | |||
| : nullptr; | |||
| } | |||
| /** 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)); | |||
| 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()); | |||
| return numUsed > 0 ? data.elements [0] | |||
| : static_cast <ObjectClass*> (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 ObjectClassPtr getLast() const noexcept | |||
| { | |||
| const ScopedLockType lock (getLock()); | |||
| return numUsed > 0 ? data.elements [numUsed - 1] | |||
| : static_cast <ObjectClass*> (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() 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.getData(); | |||
| ObjectClass** const endPointer = e + numUsed; | |||
| while (e != endPointer) | |||
| { | |||
| if (objectToLookFor == *e) | |||
| return static_cast <int> (e - data.elements.getData()); | |||
| ++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.getData(); | |||
| 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) | |||
| { | |||
| 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; | |||
| } | |||
| else | |||
| { | |||
| return add (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 | |||
| */ | |||
| void addIfNotAlreadyThere (ObjectClass* const newObject) noexcept | |||
| { | |||
| const ScopedLockType lock (getLock()); | |||
| if (! contains (newObject)) | |||
| add (newObject); | |||
| } | |||
| /** Replaces an object in the array with a different one. | |||
| If the index is less than zero, this method does nothing. | |||
| If the index is beyond the end of the array, the new object is added to the end of the array. | |||
| The object being added has its reference count increased, and if it's replacing | |||
| another object, then that one has its reference count decreased, and may be deleted. | |||
| @param indexToChange the index whose value you want to change | |||
| @param newObject the new value to set for this index. | |||
| @see add, insert, remove | |||
| */ | |||
| void set (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]) | |||
| o->decReferenceCount(); | |||
| 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.getData(), 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.getData(), 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 | |||
| { | |||
| (void) 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) | |||
| o->decReferenceCount(); | |||
| --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; | |||
| o->decReferenceCount(); | |||
| } | |||
| --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]) | |||
| { | |||
| o->decReferenceCount(); | |||
| 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 | |||
| { | |||
| (void) 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.getData(), 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; | |||
| //============================================================================== | |||
| // 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); }) | |||
| private: | |||
| //============================================================================== | |||
| ArrayAllocationBase <ObjectClass*, TypeOfCriticalSectionToUse> data; | |||
| int numUsed; | |||
| }; | |||
| #endif // JUCE_REFERENCECOUNTEDARRAY_H_INCLUDED | |||
| @@ -0,0 +1,100 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the juce_core module of the JUCE library. | |||
| Copyright (c) 2013 - Raw Material Software Ltd. | |||
| 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 THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||
| TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||
| NO EVENT SHALL THE AUTHOR 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. | |||
| ------------------------------------------------------------------------------ | |||
| NOTE! This permissive ISC license applies ONLY to files within the juce_core module! | |||
| All other JUCE modules are covered by a dual GPL/commercial license, so if you are | |||
| using any other modules, be sure to check that you also comply with their license. | |||
| For more details, visit www.juce.com | |||
| ============================================================================== | |||
| */ | |||
| #ifndef JUCE_SCOPEDVALUESETTER_H_INCLUDED | |||
| #define JUCE_SCOPEDVALUESETTER_H_INCLUDED | |||
| //============================================================================== | |||
| /** | |||
| 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) | |||
| }; | |||
| #endif // JUCE_SCOPEDVALUESETTER_H_INCLUDED | |||
| @@ -0,0 +1,497 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the juce_core module of the JUCE library. | |||
| Copyright (c) 2013 - Raw Material Software Ltd. | |||
| 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 THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||
| TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||
| NO EVENT SHALL THE AUTHOR 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. | |||
| ------------------------------------------------------------------------------ | |||
| NOTE! This permissive ISC license applies ONLY to files within the juce_core module! | |||
| All other JUCE modules are covered by a dual GPL/commercial license, so if you are | |||
| using any other modules, be sure to check that you also comply with their license. | |||
| For more details, visit www.juce.com | |||
| ============================================================================== | |||
| */ | |||
| #ifndef JUCE_SORTEDSET_H_INCLUDED | |||
| #define JUCE_SORTEDSET_H_INCLUDED | |||
| #include "juce_ArrayAllocationBase.h" | |||
| #include "../threads/juce_CriticalSection.h" | |||
| #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. */ | |||
| SortedSet() noexcept | |||
| { | |||
| } | |||
| /** Creates a copy of another set. | |||
| @param other the set to copy | |||
| */ | |||
| SortedSet (const SortedSet& other) | |||
| : data (other.data) | |||
| { | |||
| } | |||
| /** Destructor. */ | |||
| ~SortedSet() noexcept | |||
| { | |||
| } | |||
| /** Copies another set over this one. | |||
| @param other the set to copy | |||
| */ | |||
| SortedSet& operator= (const SortedSet& other) noexcept | |||
| { | |||
| data = other.data; | |||
| return *this; | |||
| } | |||
| //============================================================================== | |||
| /** 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 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; | |||
| const int halfway = (s + e) / 2; | |||
| if (halfway == s) | |||
| return -1; | |||
| else 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) | |||
| { | |||
| ElementType& elem = data.getReference (s); | |||
| if (newElement == elem) | |||
| { | |||
| elem = newElement; // force an update in case operator== permits differences. | |||
| return false; | |||
| } | |||
| const int halfway = (s + e) / 2; | |||
| const bool isBeforeHalfway = (newElement < data.getReference (halfway)); | |||
| if (halfway == s) | |||
| { | |||
| if (! isBeforeHalfway) | |||
| ++s; | |||
| break; | |||
| } | |||
| else 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.remove (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.size() > 0) | |||
| { | |||
| 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.size() <= 0) | |||
| { | |||
| 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 | |||
| #endif // JUCE_SORTEDSET_H_INCLUDED | |||
| @@ -0,0 +1,301 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the juce_core module of the JUCE library. | |||
| Copyright (c) 2013 - Raw Material Software Ltd. | |||
| 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 THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||
| TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||
| NO EVENT SHALL THE AUTHOR 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. | |||
| ------------------------------------------------------------------------------ | |||
| NOTE! This permissive ISC license applies ONLY to files within the juce_core module! | |||
| All other JUCE modules are covered by a dual GPL/commercial license, so if you are | |||
| using any other modules, be sure to check that you also comply with their license. | |||
| For more details, visit www.juce.com | |||
| ============================================================================== | |||
| */ | |||
| #ifndef JUCE_SPARSESET_H_INCLUDED | |||
| #define JUCE_SPARSESET_H_INCLUDED | |||
| #include "../maths/juce_Range.h" | |||
| #include "../threads/juce_CriticalSection.h" | |||
| //============================================================================== | |||
| /** | |||
| 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 paramter 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); | |||
| } | |||
| }; | |||
| #endif // JUCE_SPARSESET_H_INCLUDED | |||
| @@ -0,0 +1,709 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the juce_core module of the JUCE library. | |||
| Copyright (c) 2013 - Raw Material Software Ltd. | |||
| 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 THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||
| TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||
| NO EVENT SHALL THE AUTHOR 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. | |||
| ------------------------------------------------------------------------------ | |||
| NOTE! This permissive ISC license applies ONLY to files within the juce_core module! | |||
| All other JUCE modules are covered by a dual GPL/commercial license, so if you are | |||
| using any other modules, be sure to check that you also comply with their license. | |||
| For more details, visit www.juce.com | |||
| ============================================================================== | |||
| */ | |||
| 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 | |||
| }; | |||
| //============================================================================== | |||
| 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 String::empty; } | |||
| 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 bool isVoid() 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 { return true; } | |||
| bool equals (const ValueUnion&, const ValueUnion&, const VariantType& otherType) const noexcept { return otherType.isVoid(); } | |||
| void writeToStream (const ValueUnion&, OutputStream& output) const { output.writeCompressedInt (0); } | |||
| }; | |||
| //============================================================================== | |||
| class var::VariantType_Int : public var::VariantType | |||
| { | |||
| public: | |||
| VariantType_Int() noexcept {} | |||
| static const VariantType_Int instance; | |||
| int toInt (const ValueUnion& data) const noexcept { return data.intValue; }; | |||
| int64 toInt64 (const ValueUnion& data) const noexcept { return (int64) data.intValue; }; | |||
| double toDouble (const ValueUnion& data) const noexcept { return (double) data.intValue; } | |||
| String toString (const ValueUnion& data) const { return String (data.intValue); } | |||
| bool toBool (const ValueUnion& data) const noexcept { return data.intValue != 0; } | |||
| bool isInt() const noexcept { return true; } | |||
| bool equals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) const noexcept | |||
| { | |||
| return otherType.toInt (otherData) == data.intValue; | |||
| } | |||
| void writeToStream (const ValueUnion& data, OutputStream& output) const | |||
| { | |||
| 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 { return (int) data.int64Value; }; | |||
| int64 toInt64 (const ValueUnion& data) const noexcept { return data.int64Value; }; | |||
| double toDouble (const ValueUnion& data) const noexcept { return (double) data.int64Value; } | |||
| String toString (const ValueUnion& data) const { return String (data.int64Value); } | |||
| bool toBool (const ValueUnion& data) const noexcept { return data.int64Value != 0; } | |||
| bool isInt64() const noexcept { return true; } | |||
| bool equals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) const noexcept | |||
| { | |||
| return otherType.toInt64 (otherData) == data.int64Value; | |||
| } | |||
| void writeToStream (const ValueUnion& data, OutputStream& output) const | |||
| { | |||
| 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 { return (int) data.doubleValue; }; | |||
| int64 toInt64 (const ValueUnion& data) const noexcept { return (int64) data.doubleValue; }; | |||
| double toDouble (const ValueUnion& data) const noexcept { return data.doubleValue; } | |||
| String toString (const ValueUnion& data) const { return String (data.doubleValue); } | |||
| bool toBool (const ValueUnion& data) const noexcept { return data.doubleValue != 0; } | |||
| bool isDouble() const noexcept { return true; } | |||
| bool equals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) const noexcept | |||
| { | |||
| return std::abs (otherType.toDouble (otherData) - data.doubleValue) < std::numeric_limits<double>::epsilon(); | |||
| } | |||
| void writeToStream (const ValueUnion& data, OutputStream& output) const | |||
| { | |||
| 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 { return data.boolValue ? 1 : 0; }; | |||
| int64 toInt64 (const ValueUnion& data) const noexcept { return data.boolValue ? 1 : 0; }; | |||
| double toDouble (const ValueUnion& data) const noexcept { return data.boolValue ? 1.0 : 0.0; } | |||
| String toString (const ValueUnion& data) const { return String::charToString (data.boolValue ? (juce_wchar) '1' : (juce_wchar) '0'); } | |||
| bool toBool (const ValueUnion& data) const noexcept { return data.boolValue; } | |||
| bool isBool() const noexcept { return true; } | |||
| bool equals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) const noexcept | |||
| { | |||
| return otherType.toBool (otherData) == data.boolValue; | |||
| } | |||
| void writeToStream (const ValueUnion& data, OutputStream& output) const | |||
| { | |||
| 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 { getString (data)-> ~String(); } | |||
| void createCopy (ValueUnion& dest, const ValueUnion& source) const { new (dest.stringValue) String (*getString (source)); } | |||
| bool isString() const noexcept { return true; } | |||
| int toInt (const ValueUnion& data) const noexcept { return getString (data)->getIntValue(); }; | |||
| int64 toInt64 (const ValueUnion& data) const noexcept { return getString (data)->getLargeIntValue(); }; | |||
| double toDouble (const ValueUnion& data) const noexcept { return getString (data)->getDoubleValue(); } | |||
| String toString (const ValueUnion& data) const { return *getString (data); } | |||
| bool toBool (const ValueUnion& data) const noexcept { 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 | |||
| { | |||
| return otherType.toString (otherData) == *getString (data); | |||
| } | |||
| void writeToStream (const ValueUnion& data, OutputStream& output) const | |||
| { | |||
| const String* const 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 { if (data.objectValue != nullptr) data.objectValue->decReferenceCount(); } | |||
| void createCopy (ValueUnion& dest, const ValueUnion& source) const | |||
| { | |||
| dest.objectValue = source.objectValue; | |||
| if (dest.objectValue != nullptr) | |||
| dest.objectValue->incReferenceCount(); | |||
| } | |||
| String toString (const ValueUnion& data) const { return "Object 0x" + String::toHexString ((int) (pointer_sized_int) data.objectValue); } | |||
| bool toBool (const ValueUnion& data) const noexcept { return data.objectValue != 0; } | |||
| ReferenceCountedObject* toObject (const ValueUnion& data) const noexcept { return data.objectValue; } | |||
| bool isObject() const noexcept { return true; } | |||
| bool equals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) const noexcept | |||
| { | |||
| return otherType.toObject (otherData) == data.objectValue; | |||
| } | |||
| void writeToStream (const ValueUnion&, OutputStream& output) const | |||
| { | |||
| jassertfalse; // Can't write an object to a stream! | |||
| output.writeCompressedInt (0); | |||
| } | |||
| }; | |||
| //============================================================================== | |||
| class var::VariantType_Array : public var::VariantType | |||
| { | |||
| public: | |||
| VariantType_Array() noexcept {} | |||
| static const VariantType_Array instance; | |||
| void cleanUp (ValueUnion& data) const noexcept { delete data.arrayValue; } | |||
| void createCopy (ValueUnion& dest, const ValueUnion& source) const { dest.arrayValue = new Array<var> (*(source.arrayValue)); } | |||
| String toString (const ValueUnion&) const { return "[Array]"; } | |||
| bool isArray() const noexcept { return true; } | |||
| Array<var>* toArray (const ValueUnion& data) const noexcept { return data.arrayValue; } | |||
| bool equals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) const noexcept | |||
| { | |||
| const Array<var>* const otherArray = otherType.toArray (otherData); | |||
| return otherArray != nullptr && *otherArray == *(data.arrayValue); | |||
| } | |||
| void writeToStream (const ValueUnion& data, OutputStream& output) const | |||
| { | |||
| MemoryOutputStream buffer (512); | |||
| const int numItems = data.arrayValue->size(); | |||
| buffer.writeCompressedInt (numItems); | |||
| for (int i = 0; i < numItems; ++i) | |||
| data.arrayValue->getReference(i).writeToStream (buffer); | |||
| output.writeCompressedInt (1 + (int) buffer.getDataSize()); | |||
| output.writeByte (varMarker_Array); | |||
| output << buffer; | |||
| } | |||
| }; | |||
| //============================================================================== | |||
| class var::VariantType_Binary : public var::VariantType | |||
| { | |||
| public: | |||
| VariantType_Binary() noexcept {} | |||
| static const VariantType_Binary instance; | |||
| void cleanUp (ValueUnion& data) const noexcept { delete data.binaryValue; } | |||
| void createCopy (ValueUnion& dest, const ValueUnion& source) const { dest.binaryValue = new MemoryBlock (*source.binaryValue); } | |||
| String toString (const ValueUnion& data) const { return data.binaryValue->toBase64Encoding(); } | |||
| bool isBinary() const noexcept { return true; } | |||
| MemoryBlock* toBinary (const ValueUnion& data) const noexcept { return data.binaryValue; } | |||
| bool equals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) const noexcept | |||
| { | |||
| const MemoryBlock* const otherBlock = otherType.toBinary (otherData); | |||
| return otherBlock != nullptr && *otherBlock == *data.binaryValue; | |||
| } | |||
| void writeToStream (const ValueUnion& data, OutputStream& output) const | |||
| { | |||
| 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; | |||
| String toString (const ValueUnion&) const { return "Method"; } | |||
| bool toBool (const ValueUnion& data) const noexcept { return data.methodValue != nullptr; } | |||
| bool isMethod() const noexcept { return true; } | |||
| bool equals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) const noexcept | |||
| { | |||
| return otherType.isMethod() && otherData.methodValue == data.methodValue; | |||
| } | |||
| void writeToStream (const ValueUnion&, OutputStream& output) const | |||
| { | |||
| jassertfalse; // Can't write a method to a stream! | |||
| output.writeCompressedInt (0); | |||
| } | |||
| }; | |||
| //============================================================================== | |||
| const var::VariantType_Void var::VariantType_Void::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() noexcept | |||
| { | |||
| type->cleanUp (value); | |||
| } | |||
| const var var::null; | |||
| //============================================================================== | |||
| 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 (MethodFunction m) noexcept : type (&VariantType_Method::instance) { value.methodValue = m; } | |||
| var::var (const Array<var>& v) : type (&VariantType_Array::instance) { value.arrayValue = new Array<var> (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 (ReferenceCountedObject* const object) : type (&VariantType_Object::instance) | |||
| { | |||
| value.objectValue = object; | |||
| if (object != nullptr) | |||
| object->incReferenceCount(); | |||
| } | |||
| //============================================================================== | |||
| bool var::isVoid() const noexcept { return type->isVoid(); } | |||
| 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 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= (MethodFunction v) { var v2 (v); swapWith (v2); return *this; } | |||
| #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS | |||
| 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::operator= (String&& v) | |||
| { | |||
| type->cleanUp (value); | |||
| type = &VariantType_String::instance; | |||
| new (value.stringValue) String (static_cast<String&&> (v)); | |||
| return *this; | |||
| } | |||
| #endif | |||
| //============================================================================== | |||
| 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 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::operator[] (const Identifier propertyName) const | |||
| { | |||
| if (DynamicObject* const o = getDynamicObject()) | |||
| return o->getProperty (propertyName); | |||
| return var::null; | |||
| } | |||
| var var::operator[] (const char* const propertyName) const | |||
| { | |||
| return operator[] (Identifier (propertyName)); | |||
| } | |||
| var var::getProperty (const Identifier propertyName, const var& defaultReturnValue) const | |||
| { | |||
| if (DynamicObject* const o = getDynamicObject()) | |||
| return o->getProperties().getWithDefault (propertyName, defaultReturnValue); | |||
| return defaultReturnValue; | |||
| } | |||
| var var::invoke (const Identifier method, const var* arguments, int numArguments) const | |||
| { | |||
| if (DynamicObject* const o = getDynamicObject()) | |||
| return o->invokeMethod (method, arguments, numArguments); | |||
| return var::null; | |||
| } | |||
| var var::invokeMethod (DynamicObject* const target, const var* const arguments, const int numArguments) const | |||
| { | |||
| jassert (target != nullptr); | |||
| if (isMethod()) | |||
| return (target->*(value.methodValue)) (arguments, numArguments); | |||
| return var::null; | |||
| } | |||
| 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 (const Array<var>* const array = getArray()) | |||
| return array->size(); | |||
| return 0; | |||
| } | |||
| const var& var::operator[] (int arrayIndex) const | |||
| { | |||
| const Array<var>* const 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) | |||
| { | |||
| const Array<var>* const 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() | |||
| { | |||
| Array<var>* array = getArray(); | |||
| if (array == nullptr) | |||
| { | |||
| const Array<var> tempVar; | |||
| var v (tempVar); | |||
| array = v.value.arrayValue; | |||
| if (! isVoid()) | |||
| array->add (*this); | |||
| swapWith (v); | |||
| } | |||
| return array; | |||
| } | |||
| void var::append (const var& n) | |||
| { | |||
| convertToArray()->add (n); | |||
| } | |||
| void var::remove (const int index) | |||
| { | |||
| if (Array<var>* 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 (const Array<var>* 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 (numBytes - 1); | |||
| if (numBytes > 1) | |||
| { | |||
| const int numRead = input.read (mb.getData(), numBytes - 1); | |||
| mb.setSize (numRead); | |||
| } | |||
| return var (mb); | |||
| } | |||
| case varMarker_Array: | |||
| { | |||
| var v; | |||
| Array<var>* const destArray = v.convertToArray(); | |||
| for (int i = input.readCompressedInt(); --i >= 0;) | |||
| destArray->add (readFromStream (input)); | |||
| return v; | |||
| } | |||
| default: | |||
| input.skipNextBytes (numBytes - 1); break; | |||
| } | |||
| } | |||
| return var::null; | |||
| } | |||
| @@ -0,0 +1,307 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the juce_core module of the JUCE library. | |||
| Copyright (c) 2013 - Raw Material Software Ltd. | |||
| 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 THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||
| TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||
| NO EVENT SHALL THE AUTHOR 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. | |||
| ------------------------------------------------------------------------------ | |||
| NOTE! This permissive ISC license applies ONLY to files within the juce_core module! | |||
| All other JUCE modules are covered by a dual GPL/commercial license, so if you are | |||
| using any other modules, be sure to check that you also comply with their license. | |||
| For more details, visit www.juce.com | |||
| ============================================================================== | |||
| */ | |||
| #ifndef JUCE_VARIANT_H_INCLUDED | |||
| #define JUCE_VARIANT_H_INCLUDED | |||
| #include "../text/juce_Identifier.h" | |||
| #include "../streams/juce_OutputStream.h" | |||
| #include "../streams/juce_InputStream.h" | |||
| #include "../containers/juce_Array.h" | |||
| #ifndef DOXYGEN | |||
| class ReferenceCountedObject; | |||
| class DynamicObject; | |||
| #endif | |||
| //============================================================================== | |||
| /** | |||
| 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: | |||
| //============================================================================== | |||
| typedef const var (DynamicObject::*MethodFunction) (const var* arguments, int numArguments); | |||
| typedef Identifier identifier; | |||
| //============================================================================== | |||
| /** Creates a void variant. */ | |||
| var() noexcept; | |||
| /** Destructor. */ | |||
| ~var() noexcept; | |||
| /** A static var object that can be used where you need an empty variant object. */ | |||
| static const var null; | |||
| 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 (ReferenceCountedObject* object); | |||
| var (MethodFunction 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 Array<var>& value); | |||
| var& operator= (ReferenceCountedObject* object); | |||
| var& operator= (MethodFunction method); | |||
| #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS | |||
| var (var&& other) noexcept; | |||
| var (String&& value); | |||
| var (MemoryBlock&& binaryData); | |||
| var& operator= (var&& other) noexcept; | |||
| var& operator= (String&& value); | |||
| #endif | |||
| void swapWith (var& other) 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 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; | |||
| //============================================================================== | |||
| /** 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. */ | |||
| var operator[] (const Identifier propertyName) const; | |||
| /** If this variant is an object, this returns one of its properties. */ | |||
| 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; | |||
| /** If this variant is an object, this invokes one of its methods with no arguments. */ | |||
| var call (const Identifier method) const; | |||
| /** If this variant is an object, this invokes one of its methods with one argument. */ | |||
| var call (const Identifier method, const var& arg1) const; | |||
| /** If this variant is an object, this invokes one of its methods with 2 arguments. */ | |||
| var call (const Identifier method, const var& arg1, const var& arg2) const; | |||
| /** If this variant is an object, this invokes one of its methods with 3 arguments. */ | |||
| var call (const Identifier method, const var& arg1, const var& arg2, const var& arg3); | |||
| /** If this variant is an object, this invokes one of its methods with 4 arguments. */ | |||
| var call (const Identifier method, const var& arg1, const var& arg2, const var& arg3, const var& arg4) const; | |||
| /** If this variant is an object, this invokes one of its methods with 5 arguments. */ | |||
| var call (const Identifier method, const var& arg1, const var& arg2, const var& arg3, const var& arg4, const var& arg5) const; | |||
| /** If this variant is an object, this invokes one of its methods with a list of arguments. */ | |||
| var invoke (const Identifier method, const var* arguments, int numArguments) 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_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; | |||
| Array<var>* arrayValue; | |||
| MemoryBlock* binaryValue; | |||
| MethodFunction methodValue; | |||
| }; | |||
| const VariantType* type; | |||
| ValueUnion value; | |||
| Array<var>* convertToArray(); | |||
| friend class DynamicObject; | |||
| var invokeMethod (DynamicObject*, const var*, int) const; | |||
| }; | |||
| /** Compares the values of two var objects, using the var::equals() comparison. */ | |||
| bool operator== (const var& v1, const var& v2) noexcept; | |||
| /** Compares the values of two var objects, using the var::equals() comparison. */ | |||
| bool operator!= (const var& v1, const var& v2) noexcept; | |||
| bool operator== (const var& v1, const String& v2); | |||
| bool operator!= (const var& v1, const String& v2); | |||
| bool operator== (const var& v1, const char* v2); | |||
| bool operator!= (const var& v1, const char* v2); | |||
| #endif // JUCE_VARIANT_H_INCLUDED | |||
| @@ -0,0 +1,159 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the juce_core module of the JUCE library. | |||
| Copyright (c) 2013 - Raw Material Software Ltd. | |||
| 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 THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||
| TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||
| NO EVENT SHALL THE AUTHOR 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. | |||
| ------------------------------------------------------------------------------ | |||
| NOTE! This permissive ISC license applies ONLY to files within the juce_core module! | |||
| All other JUCE modules are covered by a dual GPL/commercial license, so if you are | |||
| using any other modules, be sure to check that you also comply with their license. | |||
| For more details, visit www.juce.com | |||
| ============================================================================== | |||
| */ | |||
| 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) | |||
| { | |||
| hasBeenAdvanced = true; | |||
| if (subIterator != nullptr) | |||
| { | |||
| if (subIterator->next (isDirResult, isHiddenResult, fileSize, modTime, creationTime, isReadOnly)) | |||
| return true; | |||
| subIterator = nullptr; | |||
| } | |||
| String filename; | |||
| bool isDirectory, isHidden = 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 recursive, we're not relying on the OS iterator to do the wildcard match, so do it now.. | |||
| if (matches && isRecursive) | |||
| 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) | |||
| return next (isDirResult, isHiddenResult, fileSize, modTime, creationTime, isReadOnly); | |||
| } | |||
| } | |||
| 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 detailedIndex / totalNumFiles; | |||
| } | |||
| @@ -0,0 +1,162 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the juce_core module of the JUCE library. | |||
| Copyright (c) 2013 - Raw Material Software Ltd. | |||
| 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 THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||
| TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||
| NO EVENT SHALL THE AUTHOR 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. | |||
| ------------------------------------------------------------------------------ | |||
| NOTE! This permissive ISC license applies ONLY to files within the juce_core module! | |||
| All other JUCE modules are covered by a dual GPL/commercial license, so if you are | |||
| using any other modules, be sure to check that you also comply with their license. | |||
| For more details, visit www.juce.com | |||
| ============================================================================== | |||
| */ | |||
| #ifndef JUCE_DIRECTORYITERATOR_H_INCLUDED | |||
| #define JUCE_DIRECTORYITERATOR_H_INCLUDED | |||
| #include "juce_File.h" | |||
| #include "../memory/juce_ScopedPointer.h" | |||
| //============================================================================== | |||
| /** | |||
| Searches through a 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 finding a large number of files, this is better than | |||
| using File::findChildFiles() because it doesn't block while it finds them | |||
| all, and this is more memory-efficient. | |||
| It can also guess how far it's got using a wildly 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 class ScopedPointer<Pimpl>; | |||
| ScopedPointer<Pimpl> pimpl; | |||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (NativeIterator) | |||
| }; | |||
| friend class ScopedPointer<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) | |||
| }; | |||
| #endif // JUCE_DIRECTORYITERATOR_H_INCLUDED | |||
| @@ -0,0 +1,959 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the juce_core module of the JUCE library. | |||
| Copyright (c) 2013 - Raw Material Software Ltd. | |||
| 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 THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||
| TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||
| NO EVENT SHALL THE AUTHOR 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. | |||
| ------------------------------------------------------------------------------ | |||
| NOTE! This permissive ISC license applies ONLY to files within the juce_core module! | |||
| All other JUCE modules are covered by a dual GPL/commercial license, so if you are | |||
| using any other modules, be sure to check that you also comply with their license. | |||
| For more details, visit www.juce.com | |||
| ============================================================================== | |||
| */ | |||
| #ifndef JUCE_FILE_H_INCLUDED | |||
| #define JUCE_FILE_H_INCLUDED | |||
| #include "../containers/juce_Array.h" | |||
| #include "../time/juce_Time.h" | |||
| #include "../text/juce_StringArray.h" | |||
| #include "../memory/juce_MemoryBlock.h" | |||
| #include "../memory/juce_ScopedPointer.h" | |||
| #include "../misc/juce_Result.h" | |||
| class FileInputStream; | |||
| class FileOutputStream; | |||
| //============================================================================== | |||
| /** | |||
| Represents a local file or directory. | |||
| This class encapsulates the absolute pathname of a file or directory, and | |||
| has methods for finding out about the file and changing its properties. | |||
| To read or write to the file, there are methods for returning an input or | |||
| output stream. | |||
| @see FileInputStream, FileOutputStream | |||
| */ | |||
| class JUCE_API File | |||
| { | |||
| public: | |||
| //============================================================================== | |||
| /** Creates an (invalid) file object. | |||
| The file is initially set to an empty path, so getFullPath() will return | |||
| an empty string, and comparing the file to File::nonexistent will return | |||
| true. | |||
| You can use its operator= method to point it at a proper file. | |||
| */ | |||
| File() noexcept {} | |||
| /** Creates a file from an absolute path. | |||
| If the path supplied is a relative path, it is taken to be relative | |||
| to the current working directory (see File::getCurrentWorkingDirectory()), | |||
| but this isn't a recommended way of creating a file, because you | |||
| never know what the CWD is going to be. | |||
| On the Mac/Linux, the path can include "~" notation for referring to | |||
| user home directories. | |||
| */ | |||
| File (const String& absolutePath); | |||
| /** Creates a copy of another file object. */ | |||
| File (const File&); | |||
| /** Destructor. */ | |||
| ~File() noexcept {} | |||
| /** Sets the file based on an absolute pathname. | |||
| If the path supplied is a relative path, it is taken to be relative | |||
| to the current working directory (see File::getCurrentWorkingDirectory()), | |||
| but this isn't a recommended way of creating a file, because you | |||
| never know what the CWD is going to be. | |||
| On the Mac/Linux, the path can include "~" notation for referring to | |||
| user home directories. | |||
| */ | |||
| File& operator= (const String& newAbsolutePath); | |||
| /** Copies from another file object. */ | |||
| File& operator= (const File& otherFile); | |||
| #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS | |||
| File (File&&) noexcept; | |||
| File& operator= (File&&) noexcept; | |||
| #endif | |||
| //============================================================================== | |||
| /** This static constant is used for referring to an 'invalid' file. */ | |||
| static const File nonexistent; | |||
| //============================================================================== | |||
| /** Checks whether the file actually exists. | |||
| @returns true if the file exists, either as a file or a directory. | |||
| @see existsAsFile, isDirectory | |||
| */ | |||
| bool exists() const; | |||
| /** Checks whether the file exists and is a file rather than a directory. | |||
| @returns true only if this is a real file, false if it's a directory | |||
| or doesn't exist | |||
| @see exists, isDirectory | |||
| */ | |||
| bool existsAsFile() const; | |||
| /** Checks whether the file is a directory that exists. | |||
| @returns true only if the file is a directory which actually exists, so | |||
| false if it's a file or doesn't exist at all | |||
| @see exists, existsAsFile | |||
| */ | |||
| bool isDirectory() const; | |||
| /** Returns the size of the file in bytes. | |||
| @returns the number of bytes in the file, or 0 if it doesn't exist. | |||
| */ | |||
| int64 getSize() const; | |||
| /** Utility function to convert a file size in bytes to a neat string description. | |||
| So for example 100 would return "100 bytes", 2000 would return "2 KB", | |||
| 2000000 would produce "2 MB", etc. | |||
| */ | |||
| static String descriptionOfSizeInBytes (int64 bytes); | |||
| //============================================================================== | |||
| /** Returns the complete, absolute path of this file. | |||
| This includes the filename and all its parent folders. On Windows it'll | |||
| also include the drive letter prefix; on Mac or Linux it'll be a complete | |||
| path starting from the root folder. | |||
| If you just want the file's name, you should use getFileName() or | |||
| getFileNameWithoutExtension(). | |||
| @see getFileName, getRelativePathFrom | |||
| */ | |||
| const String& getFullPathName() const noexcept { return fullPath; } | |||
| /** Returns the last section of the pathname. | |||
| Returns just the final part of the path - e.g. if the whole path | |||
| is "/moose/fish/foo.txt" this will return "foo.txt". | |||
| For a directory, it returns the final part of the path - e.g. for the | |||
| directory "/moose/fish" it'll return "fish". | |||
| If the filename begins with a dot, it'll return the whole filename, e.g. for | |||
| "/moose/.fish", it'll return ".fish" | |||
| @see getFullPathName, getFileNameWithoutExtension | |||
| */ | |||
| String getFileName() const; | |||
| /** Creates a relative path that refers to a file relatively to a given directory. | |||
| e.g. File ("/moose/foo.txt").getRelativePathFrom (File ("/moose/fish/haddock")) | |||
| would return "../../foo.txt". | |||
| If it's not possible to navigate from one file to the other, an absolute | |||
| path is returned. If the paths are invalid, an empty string may also be | |||
| returned. | |||
| @param directoryToBeRelativeTo the directory which the resultant string will | |||
| be relative to. If this is actually a file rather than | |||
| a directory, its parent directory will be used instead. | |||
| If it doesn't exist, it's assumed to be a directory. | |||
| @see getChildFile, isAbsolutePath | |||
| */ | |||
| String getRelativePathFrom (const File& directoryToBeRelativeTo) const; | |||
| //============================================================================== | |||
| /** Returns the file's extension. | |||
| Returns the file extension of this file, also including the dot. | |||
| e.g. "/moose/fish/foo.txt" would return ".txt" | |||
| @see hasFileExtension, withFileExtension, getFileNameWithoutExtension | |||
| */ | |||
| String getFileExtension() const; | |||
| /** Checks whether the file has a given extension. | |||
| @param extensionToTest the extension to look for - it doesn't matter whether or | |||
| not this string has a dot at the start, so ".wav" and "wav" | |||
| will have the same effect. To compare with multiple extensions, this | |||
| parameter can contain multiple strings, separated by semi-colons - | |||
| so, for example: hasFileExtension (".jpeg;png;gif") would return | |||
| true if the file has any of those three extensions. | |||
| @see getFileExtension, withFileExtension, getFileNameWithoutExtension | |||
| */ | |||
| bool hasFileExtension (const String& extensionToTest) const; | |||
| /** Returns a version of this file with a different file extension. | |||
| e.g. File ("/moose/fish/foo.txt").withFileExtension ("html") returns "/moose/fish/foo.html" | |||
| @param newExtension the new extension, either with or without a dot at the start (this | |||
| doesn't make any difference). To get remove a file's extension altogether, | |||
| pass an empty string into this function. | |||
| @see getFileName, getFileExtension, hasFileExtension, getFileNameWithoutExtension | |||
| */ | |||
| File withFileExtension (const String& newExtension) const; | |||
| /** Returns the last part of the filename, without its file extension. | |||
| e.g. for "/moose/fish/foo.txt" this will return "foo". | |||
| @see getFileName, getFileExtension, hasFileExtension, withFileExtension | |||
| */ | |||
| String getFileNameWithoutExtension() const; | |||
| //============================================================================== | |||
| /** Returns a 32-bit hash-code that identifies this file. | |||
| This is based on the filename. Obviously it's possible, although unlikely, that | |||
| two files will have the same hash-code. | |||
| */ | |||
| int hashCode() const; | |||
| /** Returns a 64-bit hash-code that identifies this file. | |||
| This is based on the filename. Obviously it's possible, although unlikely, that | |||
| two files will have the same hash-code. | |||
| */ | |||
| int64 hashCode64() const; | |||
| //============================================================================== | |||
| /** Returns a file that represents a relative (or absolute) sub-path of the current one. | |||
| This will find a child file or directory of the current object. | |||
| e.g. | |||
| File ("/moose/fish").getChildFile ("foo.txt") will produce "/moose/fish/foo.txt". | |||
| File ("/moose/fish").getChildFile ("haddock/foo.txt") will produce "/moose/fish/haddock/foo.txt". | |||
| File ("/moose/fish").getChildFile ("../foo.txt") will produce "/moose/foo.txt". | |||
| If the string is actually an absolute path, it will be treated as such, e.g. | |||
| File ("/moose/fish").getChildFile ("/foo.txt") will produce "/foo.txt" | |||
| @see getSiblingFile, getParentDirectory, getRelativePathFrom, isAChildOf | |||
| */ | |||
| File getChildFile (String relativeOrAbsolutePath) const; | |||
| /** Returns a file which is in the same directory as this one. | |||
| This is equivalent to getParentDirectory().getChildFile (name). | |||
| @see getChildFile, getParentDirectory | |||
| */ | |||
| File getSiblingFile (const String& siblingFileName) const; | |||
| //============================================================================== | |||
| /** Returns the directory that contains this file or directory. | |||
| e.g. for "/moose/fish/foo.txt" this will return "/moose/fish". | |||
| */ | |||
| File getParentDirectory() const; | |||
| /** Checks whether a file is somewhere inside a directory. | |||
| Returns true if this file is somewhere inside a subdirectory of the directory | |||
| that is passed in. Neither file actually has to exist, because the function | |||
| just checks the paths for similarities. | |||
| e.g. File ("/moose/fish/foo.txt").isAChildOf ("/moose") is true. | |||
| File ("/moose/fish/foo.txt").isAChildOf ("/moose/fish") is also true. | |||
| */ | |||
| bool isAChildOf (const File& potentialParentDirectory) const; | |||
| //============================================================================== | |||
| /** Chooses a filename relative to this one that doesn't already exist. | |||
| If this file is a directory, this will return a child file of this | |||
| directory that doesn't exist, by adding numbers to a prefix and suffix until | |||
| it finds one that isn't already there. | |||
| If the prefix + the suffix doesn't exist, it won't bother adding a number. | |||
| e.g. File ("/moose/fish").getNonexistentChildFile ("foo", ".txt", true) might | |||
| return "/moose/fish/foo(2).txt" if there's already a file called "foo.txt". | |||
| @param prefix the string to use for the filename before the number | |||
| @param suffix the string to add to the filename after the number | |||
| @param putNumbersInBrackets if true, this will create filenames in the | |||
| format "prefix(number)suffix", if false, it will leave the | |||
| brackets out. | |||
| */ | |||
| File getNonexistentChildFile (const String& prefix, | |||
| const String& suffix, | |||
| bool putNumbersInBrackets = true) const; | |||
| /** Chooses a filename for a sibling file to this one that doesn't already exist. | |||
| If this file doesn't exist, this will just return itself, otherwise it | |||
| will return an appropriate sibling that doesn't exist, e.g. if a file | |||
| "/moose/fish/foo.txt" exists, this might return "/moose/fish/foo(2).txt". | |||
| @param putNumbersInBrackets whether to add brackets around the numbers that | |||
| get appended to the new filename. | |||
| */ | |||
| File getNonexistentSibling (bool putNumbersInBrackets = true) const; | |||
| //============================================================================== | |||
| /** Compares the pathnames for two files. */ | |||
| bool operator== (const File&) const; | |||
| /** Compares the pathnames for two files. */ | |||
| bool operator!= (const File&) const; | |||
| /** Compares the pathnames for two files. */ | |||
| bool operator< (const File&) const; | |||
| /** Compares the pathnames for two files. */ | |||
| bool operator> (const File&) const; | |||
| //============================================================================== | |||
| /** Checks whether a file can be created or written to. | |||
| @returns true if it's possible to create and write to this file. If the file | |||
| doesn't already exist, this will check its parent directory to | |||
| see if writing is allowed. | |||
| @see setReadOnly | |||
| */ | |||
| bool hasWriteAccess() const; | |||
| /** Changes the write-permission of a file or directory. | |||
| @param shouldBeReadOnly whether to add or remove write-permission | |||
| @param applyRecursively if the file is a directory and this is true, it will | |||
| recurse through all the subfolders changing the permissions | |||
| of all files | |||
| @returns true if it manages to change the file's permissions. | |||
| @see hasWriteAccess | |||
| */ | |||
| bool setReadOnly (bool shouldBeReadOnly, | |||
| bool applyRecursively = false) const; | |||
| /** Returns true if this file is a hidden or system file. | |||
| The criteria for deciding whether a file is hidden are platform-dependent. | |||
| */ | |||
| bool isHidden() const; | |||
| /** If this file is a link, this returns the file that it points to. | |||
| If this file isn't actually link, it'll just return itself. | |||
| */ | |||
| File getLinkedTarget() const; | |||
| //============================================================================== | |||
| /** Returns the last modification time of this file. | |||
| @returns the time, or an invalid time if the file doesn't exist. | |||
| @see setLastModificationTime, getLastAccessTime, getCreationTime | |||
| */ | |||
| Time getLastModificationTime() const; | |||
| /** Returns the last time this file was accessed. | |||
| @returns the time, or an invalid time if the file doesn't exist. | |||
| @see setLastAccessTime, getLastModificationTime, getCreationTime | |||
| */ | |||
| Time getLastAccessTime() const; | |||
| /** Returns the time that this file was created. | |||
| @returns the time, or an invalid time if the file doesn't exist. | |||
| @see getLastModificationTime, getLastAccessTime | |||
| */ | |||
| Time getCreationTime() const; | |||
| /** Changes the modification time for this file. | |||
| @param newTime the time to apply to the file | |||
| @returns true if it manages to change the file's time. | |||
| @see getLastModificationTime, setLastAccessTime, setCreationTime | |||
| */ | |||
| bool setLastModificationTime (Time newTime) const; | |||
| /** Changes the last-access time for this file. | |||
| @param newTime the time to apply to the file | |||
| @returns true if it manages to change the file's time. | |||
| @see getLastAccessTime, setLastModificationTime, setCreationTime | |||
| */ | |||
| bool setLastAccessTime (Time newTime) const; | |||
| /** Changes the creation date for this file. | |||
| @param newTime the time to apply to the file | |||
| @returns true if it manages to change the file's time. | |||
| @see getCreationTime, setLastModificationTime, setLastAccessTime | |||
| */ | |||
| bool setCreationTime (Time newTime) const; | |||
| /** If possible, this will try to create a version string for the given file. | |||
| The OS may be able to look at the file and give a version for it - e.g. with | |||
| executables, bundles, dlls, etc. If no version is available, this will | |||
| return an empty string. | |||
| */ | |||
| String getVersion() const; | |||
| //============================================================================== | |||
| /** Creates an empty file if it doesn't already exist. | |||
| If the file that this object refers to doesn't exist, this will create a file | |||
| of zero size. | |||
| If it already exists or is a directory, this method will do nothing. | |||
| @returns true if the file has been created (or if it already existed). | |||
| @see createDirectory | |||
| */ | |||
| Result create() const; | |||
| /** Creates a new directory for this filename. | |||
| This will try to create the file as a directory, and fill also create | |||
| any parent directories it needs in order to complete the operation. | |||
| @returns a result to indicate whether the directory was created successfully, or | |||
| an error message if it failed. | |||
| @see create | |||
| */ | |||
| Result createDirectory() const; | |||
| /** Deletes a file. | |||
| If this file is actually a directory, it may not be deleted correctly if it | |||
| contains files. See deleteRecursively() as a better way of deleting directories. | |||
| @returns true if the file has been successfully deleted (or if it didn't exist to | |||
| begin with). | |||
| @see deleteRecursively | |||
| */ | |||
| bool deleteFile() const; | |||
| /** Deletes a file or directory and all its subdirectories. | |||
| If this file is a directory, this will try to delete it and all its subfolders. If | |||
| it's just a file, it will just try to delete the file. | |||
| @returns true if the file and all its subfolders have been successfully deleted | |||
| (or if it didn't exist to begin with). | |||
| @see deleteFile | |||
| */ | |||
| bool deleteRecursively() const; | |||
| /** Moves this file or folder to the trash. | |||
| @returns true if the operation succeeded. It could fail if the trash is full, or | |||
| if the file is write-protected, so you should check the return value | |||
| and act appropriately. | |||
| */ | |||
| bool moveToTrash() const; | |||
| /** Moves or renames a file. | |||
| Tries to move a file to a different location. | |||
| If the target file already exists, this will attempt to delete it first, and | |||
| will fail if this can't be done. | |||
| Note that the destination file isn't the directory to put it in, it's the actual | |||
| filename that you want the new file to have. | |||
| @returns true if the operation succeeds | |||
| */ | |||
| bool moveFileTo (const File& targetLocation) const; | |||
| /** Copies a file. | |||
| Tries to copy a file to a different location. | |||
| If the target file already exists, this will attempt to delete it first, and | |||
| will fail if this can't be done. | |||
| @returns true if the operation succeeds | |||
| */ | |||
| bool copyFileTo (const File& targetLocation) const; | |||
| /** Copies a directory. | |||
| Tries to copy an entire directory, recursively. | |||
| If this file isn't a directory or if any target files can't be created, this | |||
| will return false. | |||
| @param newDirectory the directory that this one should be copied to. Note that this | |||
| is the name of the actual directory to create, not the directory | |||
| into which the new one should be placed, so there must be enough | |||
| write privileges to create it if it doesn't exist. Any files inside | |||
| it will be overwritten by similarly named ones that are copied. | |||
| */ | |||
| bool copyDirectoryTo (const File& newDirectory) const; | |||
| //============================================================================== | |||
| /** Used in file searching, to specify whether to return files, directories, or both. | |||
| */ | |||
| enum TypesOfFileToFind | |||
| { | |||
| findDirectories = 1, /**< Use this flag to indicate that you want to find directories. */ | |||
| findFiles = 2, /**< Use this flag to indicate that you want to find files. */ | |||
| findFilesAndDirectories = 3, /**< Use this flag to indicate that you want to find both files and directories. */ | |||
| ignoreHiddenFiles = 4 /**< Add this flag to avoid returning any hidden files in the results. */ | |||
| }; | |||
| /** Searches inside a directory for files matching a wildcard pattern. | |||
| Assuming that this file is a directory, this method will search it | |||
| for either files or subdirectories whose names match a filename pattern. | |||
| @param results an array to which File objects will be added for the | |||
| files that the search comes up with | |||
| @param whatToLookFor a value from the TypesOfFileToFind enum, specifying whether to | |||
| return files, directories, or both. If the ignoreHiddenFiles flag | |||
| is also added to this value, hidden files won't be returned | |||
| @param searchRecursively if true, all subdirectories will be recursed into to do | |||
| an exhaustive search | |||
| @param wildCardPattern the filename pattern to search for, e.g. "*.txt" | |||
| @returns the number of results that have been found | |||
| @see getNumberOfChildFiles, DirectoryIterator | |||
| */ | |||
| int findChildFiles (Array<File>& results, | |||
| int whatToLookFor, | |||
| bool searchRecursively, | |||
| const String& wildCardPattern = "*") const; | |||
| /** Searches inside a directory and counts how many files match a wildcard pattern. | |||
| Assuming that this file is a directory, this method will search it | |||
| for either files or subdirectories whose names match a filename pattern, | |||
| and will return the number of matches found. | |||
| This isn't a recursive call, and will only search this directory, not | |||
| its children. | |||
| @param whatToLookFor a value from the TypesOfFileToFind enum, specifying whether to | |||
| count files, directories, or both. If the ignoreHiddenFiles flag | |||
| is also added to this value, hidden files won't be counted | |||
| @param wildCardPattern the filename pattern to search for, e.g. "*.txt" | |||
| @returns the number of matches found | |||
| @see findChildFiles, DirectoryIterator | |||
| */ | |||
| int getNumberOfChildFiles (int whatToLookFor, | |||
| const String& wildCardPattern = "*") const; | |||
| /** Returns true if this file is a directory that contains one or more subdirectories. | |||
| @see isDirectory, findChildFiles | |||
| */ | |||
| bool containsSubDirectories() const; | |||
| //============================================================================== | |||
| /** Creates a stream to read from this file. | |||
| @returns a stream that will read from this file (initially positioned at the | |||
| start of the file), or nullptr if the file can't be opened for some reason | |||
| @see createOutputStream, loadFileAsData | |||
| */ | |||
| FileInputStream* createInputStream() const; | |||
| /** Creates a stream to write to this file. | |||
| If the file exists, the stream that is returned will be positioned ready for | |||
| writing at the end of the file, so you might want to use deleteFile() first | |||
| to write to an empty file. | |||
| @returns a stream that will write to this file (initially positioned at the | |||
| end of the file), or nullptr if the file can't be opened for some reason | |||
| @see createInputStream, appendData, appendText | |||
| */ | |||
| FileOutputStream* createOutputStream (int bufferSize = 0x8000) const; | |||
| //============================================================================== | |||
| /** Loads a file's contents into memory as a block of binary data. | |||
| Of course, trying to load a very large file into memory will blow up, so | |||
| it's better to check first. | |||
| @param result the data block to which the file's contents should be appended - note | |||
| that if the memory block might already contain some data, you | |||
| might want to clear it first | |||
| @returns true if the file could all be read into memory | |||
| */ | |||
| bool loadFileAsData (MemoryBlock& result) const; | |||
| /** Reads a file into memory as a string. | |||
| Attempts to load the entire file as a zero-terminated string. | |||
| This makes use of InputStream::readEntireStreamAsString, which can | |||
| read either UTF-16 or UTF-8 file formats. | |||
| */ | |||
| String loadFileAsString() const; | |||
| /** Reads the contents of this file as text and splits it into lines, which are | |||
| appended to the given StringArray. | |||
| */ | |||
| void readLines (StringArray& destLines) const; | |||
| //============================================================================== | |||
| /** Appends a block of binary data to the end of the file. | |||
| This will try to write the given buffer to the end of the file. | |||
| @returns false if it can't write to the file for some reason | |||
| */ | |||
| bool appendData (const void* dataToAppend, | |||
| size_t numberOfBytes) const; | |||
| /** Replaces this file's contents with a given block of data. | |||
| This will delete the file and replace it with the given data. | |||
| A nice feature of this method is that it's safe - instead of deleting | |||
| the file first and then re-writing it, it creates a new temporary file, | |||
| writes the data to that, and then moves the new file to replace the existing | |||
| file. This means that if the power gets pulled out or something crashes, | |||
| you're a lot less likely to end up with a corrupted or unfinished file.. | |||
| Returns true if the operation succeeds, or false if it fails. | |||
| @see appendText | |||
| */ | |||
| bool replaceWithData (const void* dataToWrite, | |||
| size_t numberOfBytes) const; | |||
| /** Appends a string to the end of the file. | |||
| This will try to append a text string to the file, as either 16-bit unicode | |||
| or 8-bit characters in the default system encoding. | |||
| It can also write the 'ff fe' unicode header bytes before the text to indicate | |||
| the endianness of the file. | |||
| Any single \\n characters in the string are replaced with \\r\\n before it is written. | |||
| @see replaceWithText | |||
| */ | |||
| bool appendText (const String& textToAppend, | |||
| bool asUnicode = false, | |||
| bool writeUnicodeHeaderBytes = false) const; | |||
| /** Replaces this file's contents with a given text string. | |||
| This will delete the file and replace it with the given text. | |||
| A nice feature of this method is that it's safe - instead of deleting | |||
| the file first and then re-writing it, it creates a new temporary file, | |||
| writes the text to that, and then moves the new file to replace the existing | |||
| file. This means that if the power gets pulled out or something crashes, | |||
| you're a lot less likely to end up with an empty file.. | |||
| For an explanation of the parameters here, see the appendText() method. | |||
| Returns true if the operation succeeds, or false if it fails. | |||
| @see appendText | |||
| */ | |||
| bool replaceWithText (const String& textToWrite, | |||
| bool asUnicode = false, | |||
| bool writeUnicodeHeaderBytes = false) const; | |||
| /** Attempts to scan the contents of this file and compare it to another file, returning | |||
| true if this is possible and they match byte-for-byte. | |||
| */ | |||
| bool hasIdenticalContentTo (const File& other) const; | |||
| //============================================================================== | |||
| /** Creates a set of files to represent each file root. | |||
| e.g. on Windows this will create files for "c:\", "d:\" etc according | |||
| to which ones are available. On the Mac/Linux, this will probably | |||
| just add a single entry for "/". | |||
| */ | |||
| static void findFileSystemRoots (Array<File>& results); | |||
| /** Finds the name of the drive on which this file lives. | |||
| @returns the volume label of the drive, or an empty string if this isn't possible | |||
| */ | |||
| String getVolumeLabel() const; | |||
| /** Returns the serial number of the volume on which this file lives. | |||
| @returns the serial number, or zero if there's a problem doing this | |||
| */ | |||
| int getVolumeSerialNumber() const; | |||
| /** Returns the number of bytes free on the drive that this file lives on. | |||
| @returns the number of bytes free, or 0 if there's a problem finding this out | |||
| @see getVolumeTotalSize | |||
| */ | |||
| int64 getBytesFreeOnVolume() const; | |||
| /** Returns the total size of the drive that contains this file. | |||
| @returns the total number of bytes that the volume can hold | |||
| @see getBytesFreeOnVolume | |||
| */ | |||
| int64 getVolumeTotalSize() const; | |||
| /** Returns true if this file is on a CD or DVD drive. */ | |||
| bool isOnCDRomDrive() const; | |||
| /** Returns true if this file is on a hard disk. | |||
| This will fail if it's a network drive, but will still be true for | |||
| removable hard-disks. | |||
| */ | |||
| bool isOnHardDisk() const; | |||
| /** Returns true if this file is on a removable disk drive. | |||
| This might be a usb-drive, a CD-rom, or maybe a network drive. | |||
| */ | |||
| bool isOnRemovableDrive() const; | |||
| //============================================================================== | |||
| /** Launches the file as a process. | |||
| - if the file is executable, this will run it. | |||
| - if it's a document of some kind, it will launch the document with its | |||
| default viewer application. | |||
| - if it's a folder, it will be opened in Explorer, Finder, or equivalent. | |||
| @see revealToUser | |||
| */ | |||
| bool startAsProcess (const String& parameters = String::empty) const; | |||
| /** Opens Finder, Explorer, or whatever the OS uses, to show the user this file's location. | |||
| @see startAsProcess | |||
| */ | |||
| void revealToUser() const; | |||
| //============================================================================== | |||
| /** A set of types of location that can be passed to the getSpecialLocation() method. | |||
| */ | |||
| enum SpecialLocationType | |||
| { | |||
| /** The user's home folder. This is the same as using File ("~"). */ | |||
| userHomeDirectory, | |||
| /** The user's default documents folder. On Windows, this might be the user's | |||
| "My Documents" folder. On the Mac it'll be their "Documents" folder. Linux | |||
| doesn't tend to have one of these, so it might just return their home folder. | |||
| */ | |||
| userDocumentsDirectory, | |||
| /** The folder that contains the user's desktop objects. */ | |||
| userDesktopDirectory, | |||
| /** The folder in which applications store their persistent user-specific settings. | |||
| On Windows, this might be "\Documents and Settings\username\Application Data". | |||
| On the Mac, it might be "~/Library". If you're going to store your settings in here, | |||
| always create your own sub-folder to put them in, to avoid making a mess. | |||
| */ | |||
| userApplicationDataDirectory, | |||
| /** An equivalent of the userApplicationDataDirectory folder that is shared by all users | |||
| of the computer, rather than just the current user. | |||
| On the Mac it'll be "/Library", on Windows, it could be something like | |||
| "\Documents and Settings\All Users\Application Data". | |||
| Depending on the setup, this folder may be read-only. | |||
| */ | |||
| commonApplicationDataDirectory, | |||
| /** The folder that should be used for temporary files. | |||
| Always delete them when you're finished, to keep the user's computer tidy! | |||
| */ | |||
| tempDirectory, | |||
| /** Returns this application's executable file. | |||
| If running as a plug-in or DLL, this will (where possible) be the DLL rather than the | |||
| host app. | |||
| On the mac this will return the unix binary, not the package folder - see | |||
| currentApplicationFile for that. | |||
| See also invokedExecutableFile, which is similar, but if the exe was launched from a | |||
| file link, invokedExecutableFile will return the name of the link. | |||
| */ | |||
| currentExecutableFile, | |||
| /** Returns this application's location. | |||
| If running as a plug-in or DLL, this will (where possible) be the DLL rather than the | |||
| host app. | |||
| On the mac this will return the package folder (if it's in one), not the unix binary | |||
| that's inside it - compare with currentExecutableFile. | |||
| */ | |||
| currentApplicationFile, | |||
| /** Returns the file that was invoked to launch this executable. | |||
| This may differ from currentExecutableFile if the app was started from e.g. a link - this | |||
| will return the name of the link that was used, whereas currentExecutableFile will return | |||
| the actual location of the target executable. | |||
| */ | |||
| invokedExecutableFile, | |||
| /** In a plugin, this will return the path of the host executable. */ | |||
| hostApplicationPath, | |||
| /** The directory in which applications normally get installed. | |||
| So on windows, this would be something like "c:\program files", on the | |||
| Mac "/Applications", or "/usr" on linux. | |||
| */ | |||
| globalApplicationsDirectory, | |||
| /** The most likely place where a user might store their music files. */ | |||
| userMusicDirectory, | |||
| /** The most likely place where a user might store their movie files. */ | |||
| userMoviesDirectory, | |||
| /** The most likely place where a user might store their picture files. */ | |||
| userPicturesDirectory | |||
| }; | |||
| /** Finds the location of a special type of file or directory, such as a home folder or | |||
| documents folder. | |||
| @see SpecialLocationType | |||
| */ | |||
| static File JUCE_CALLTYPE getSpecialLocation (const SpecialLocationType type); | |||
| //============================================================================== | |||
| /** Returns a temporary file in the system's temp directory. | |||
| This will try to return the name of a non-existent temp file. | |||
| To get the temp folder, you can use getSpecialLocation (File::tempDirectory). | |||
| */ | |||
| static File createTempFile (const String& fileNameEnding); | |||
| //============================================================================== | |||
| /** Returns the current working directory. | |||
| @see setAsCurrentWorkingDirectory | |||
| */ | |||
| static File getCurrentWorkingDirectory(); | |||
| /** Sets the current working directory to be this file. | |||
| For this to work the file must point to a valid directory. | |||
| @returns true if the current directory has been changed. | |||
| @see getCurrentWorkingDirectory | |||
| */ | |||
| bool setAsCurrentWorkingDirectory() const; | |||
| //============================================================================== | |||
| /** The system-specific file separator character. | |||
| On Windows, this will be '\', on Mac/Linux, it'll be '/' | |||
| */ | |||
| static const juce_wchar separator; | |||
| /** The system-specific file separator character, as a string. | |||
| On Windows, this will be '\', on Mac/Linux, it'll be '/' | |||
| */ | |||
| static const String separatorString; | |||
| //============================================================================== | |||
| /** Returns a version of a filename with any illegal characters removed. | |||
| This will return a copy of the given string after removing characters | |||
| that are not allowed in a legal filename, and possibly shortening the | |||
| string if it's too long. | |||
| Because this will remove slashes, don't use it on an absolute pathname - use | |||
| createLegalPathName() for that. | |||
| @see createLegalPathName | |||
| */ | |||
| static String createLegalFileName (const String& fileNameToFix); | |||
| /** Returns a version of a path with any illegal characters removed. | |||
| Similar to createLegalFileName(), but this won't remove slashes, so can | |||
| be used on a complete pathname. | |||
| @see createLegalFileName | |||
| */ | |||
| static String createLegalPathName (const String& pathNameToFix); | |||
| /** Indicates whether filenames are case-sensitive on the current operating system. */ | |||
| static bool areFileNamesCaseSensitive(); | |||
| /** Returns true if the string seems to be a fully-specified absolute path. */ | |||
| static bool isAbsolutePath (const String& path); | |||
| /** Creates a file that simply contains this string, without doing the sanity-checking | |||
| that the normal constructors do. | |||
| Best to avoid this unless you really know what you're doing. | |||
| */ | |||
| static File createFileWithoutCheckingPath (const String& absolutePath) noexcept; | |||
| /** Adds a separator character to the end of a path if it doesn't already have one. */ | |||
| static String addTrailingSeparator (const String& path); | |||
| #if JUCE_MAC || JUCE_IOS || DOXYGEN | |||
| //============================================================================== | |||
| /** OSX ONLY - Finds the OSType of a file from the its resources. */ | |||
| OSType getMacOSType() const; | |||
| /** OSX ONLY - Returns true if this file is actually a bundle. */ | |||
| bool isBundle() const; | |||
| #endif | |||
| #if JUCE_MAC || DOXYGEN | |||
| /** OSX ONLY - Adds this file to the OSX dock */ | |||
| void addToDock() const; | |||
| #endif | |||
| #if JUCE_WINDOWS | |||
| /** Windows ONLY - Creates a win32 .LNK shortcut file that links to this file. */ | |||
| bool createLink (const String& description, const File& linkFileToCreate) const; | |||
| #endif | |||
| private: | |||
| //============================================================================== | |||
| String fullPath; | |||
| static String parseAbsolutePath (const String&); | |||
| String getPathUpToLastSlash() const; | |||
| Result createDirectoryInternal (const String&) const; | |||
| bool copyInternal (const File&) const; | |||
| bool moveInternal (const File&) const; | |||
| bool setFileTimesInternal (int64 m, int64 a, int64 c) const; | |||
| void getFileTimesInternal (int64& m, int64& a, int64& c) const; | |||
| bool setFileReadOnlyInternal (bool) const; | |||
| }; | |||
| #endif // JUCE_FILE_H_INCLUDED | |||
| @@ -0,0 +1,95 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the juce_core module of the JUCE library. | |||
| Copyright (c) 2013 - Raw Material Software Ltd. | |||
| 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 THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||
| TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||
| NO EVENT SHALL THE AUTHOR 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. | |||
| ------------------------------------------------------------------------------ | |||
| NOTE! This permissive ISC license applies ONLY to files within the juce_core module! | |||
| All other JUCE modules are covered by a dual GPL/commercial license, so if you are | |||
| using any other modules, be sure to check that you also comply with their license. | |||
| For more details, visit www.juce.com | |||
| ============================================================================== | |||
| */ | |||
| int64 juce_fileSetPosition (void* handle, int64 pos); | |||
| //============================================================================== | |||
| FileInputStream::FileInputStream (const File& f) | |||
| : file (f), | |||
| fileHandle (nullptr), | |||
| currentPosition (0), | |||
| status (Result::ok()), | |||
| needToSeek (true) | |||
| { | |||
| openHandle(); | |||
| } | |||
| FileInputStream::~FileInputStream() | |||
| { | |||
| closeHandle(); | |||
| } | |||
| //============================================================================== | |||
| int64 FileInputStream::getTotalLength() | |||
| { | |||
| return file.getSize(); | |||
| } | |||
| int FileInputStream::read (void* buffer, int bytesToRead) | |||
| { | |||
| jassert (openedOk()); | |||
| jassert (buffer != nullptr && bytesToRead >= 0); | |||
| if (needToSeek) | |||
| { | |||
| if (juce_fileSetPosition (fileHandle, currentPosition) < 0) | |||
| return 0; | |||
| needToSeek = false; | |||
| } | |||
| const size_t num = readInternal (buffer, (size_t) bytesToRead); | |||
| currentPosition += num; | |||
| return (int) num; | |||
| } | |||
| bool FileInputStream::isExhausted() | |||
| { | |||
| return currentPosition >= getTotalLength(); | |||
| } | |||
| int64 FileInputStream::getPosition() | |||
| { | |||
| return currentPosition; | |||
| } | |||
| bool FileInputStream::setPosition (int64 pos) | |||
| { | |||
| jassert (openedOk()); | |||
| if (pos != currentPosition) | |||
| { | |||
| pos = jlimit ((int64) 0, getTotalLength(), pos); | |||
| needToSeek |= (currentPosition != pos); | |||
| currentPosition = pos; | |||
| } | |||
| return true; | |||
| } | |||
| @@ -0,0 +1,99 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the juce_core module of the JUCE library. | |||
| Copyright (c) 2013 - Raw Material Software Ltd. | |||
| 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 THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||
| TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||
| NO EVENT SHALL THE AUTHOR 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. | |||
| ------------------------------------------------------------------------------ | |||
| NOTE! This permissive ISC license applies ONLY to files within the juce_core module! | |||
| All other JUCE modules are covered by a dual GPL/commercial license, so if you are | |||
| using any other modules, be sure to check that you also comply with their license. | |||
| For more details, visit www.juce.com | |||
| ============================================================================== | |||
| */ | |||
| #ifndef JUCE_FILEINPUTSTREAM_H_INCLUDED | |||
| #define JUCE_FILEINPUTSTREAM_H_INCLUDED | |||
| #include "juce_File.h" | |||
| #include "../streams/juce_InputStream.h" | |||
| //============================================================================== | |||
| /** | |||
| An input stream that reads from a local file. | |||
| @see InputStream, FileOutputStream, File::createInputStream | |||
| */ | |||
| class JUCE_API FileInputStream : public InputStream | |||
| { | |||
| public: | |||
| //============================================================================== | |||
| /** Creates a FileInputStream. | |||
| @param fileToRead the file to read from - if the file can't be accessed for some | |||
| reason, then the stream will just contain no data | |||
| */ | |||
| 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* destBuffer, int maxBytesToRead) override; | |||
| bool isExhausted() override; | |||
| int64 getPosition() override; | |||
| bool setPosition (int64 pos) override; | |||
| private: | |||
| //============================================================================== | |||
| File file; | |||
| void* fileHandle; | |||
| int64 currentPosition; | |||
| Result status; | |||
| bool needToSeek; | |||
| void openHandle(); | |||
| void closeHandle(); | |||
| size_t readInternal (void* buffer, size_t numBytes); | |||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FileInputStream) | |||
| }; | |||
| #endif // JUCE_FILEINPUTSTREAM_H_INCLUDED | |||
| @@ -0,0 +1,135 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the juce_core module of the JUCE library. | |||
| Copyright (c) 2013 - Raw Material Software Ltd. | |||
| 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 THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||
| TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||
| NO EVENT SHALL THE AUTHOR 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. | |||
| ------------------------------------------------------------------------------ | |||
| NOTE! This permissive ISC license applies ONLY to files within the juce_core module! | |||
| All other JUCE modules are covered by a dual GPL/commercial license, so if you are | |||
| using any other modules, be sure to check that you also comply with their license. | |||
| For more details, visit www.juce.com | |||
| ============================================================================== | |||
| */ | |||
| int64 juce_fileSetPosition (void* handle, int64 pos); | |||
| //============================================================================== | |||
| FileOutputStream::FileOutputStream (const File& f, const int bufferSize_) | |||
| : file (f), | |||
| fileHandle (nullptr), | |||
| status (Result::ok()), | |||
| currentPosition (0), | |||
| bufferSize (bufferSize_), | |||
| bytesInBuffer (0), | |||
| buffer ((size_t) jmax (bufferSize_, 16)) | |||
| { | |||
| openHandle(); | |||
| } | |||
| FileOutputStream::~FileOutputStream() | |||
| { | |||
| flushBuffer(); | |||
| flushInternal(); | |||
| 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 += numBytes; | |||
| } | |||
| else | |||
| { | |||
| if (! flushBuffer()) | |||
| return false; | |||
| if (numBytes < bufferSize) | |||
| { | |||
| memcpy (buffer + bytesInBuffer, src, numBytes); | |||
| bytesInBuffer += numBytes; | |||
| currentPosition += numBytes; | |||
| } | |||
| else | |||
| { | |||
| const ssize_t bytesWritten = writeInternal (src, numBytes); | |||
| if (bytesWritten < 0) | |||
| return false; | |||
| currentPosition += 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 += numBytes; | |||
| return true; | |||
| } | |||
| return OutputStream::writeRepeatedByte (byte, numBytes); | |||
| } | |||
| @@ -0,0 +1,119 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the juce_core module of the JUCE library. | |||
| Copyright (c) 2013 - Raw Material Software Ltd. | |||
| 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 THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||
| TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||
| NO EVENT SHALL THE AUTHOR 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. | |||
| ------------------------------------------------------------------------------ | |||
| NOTE! This permissive ISC license applies ONLY to files within the juce_core module! | |||
| All other JUCE modules are covered by a dual GPL/commercial license, so if you are | |||
| using any other modules, be sure to check that you also comply with their license. | |||
| For more details, visit www.juce.com | |||
| ============================================================================== | |||
| */ | |||
| #ifndef JUCE_FILEOUTPUTSTREAM_H_INCLUDED | |||
| #define JUCE_FILEOUTPUTSTREAM_H_INCLUDED | |||
| #include "juce_File.h" | |||
| #include "../streams/juce_OutputStream.h" | |||
| //============================================================================== | |||
| /** | |||
| 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, the failedToOpen() method will return | |||
| true. | |||
| If the file already exists when opened, the stream's write-postion 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). | |||
| @see TemporaryFile | |||
| */ | |||
| FileOutputStream (const File& fileToWriteTo, | |||
| int 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) | |||
| }; | |||
| #endif // JUCE_FILEOUTPUTSTREAM_H_INCLUDED | |||
| @@ -0,0 +1,171 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the juce_core module of the JUCE library. | |||
| Copyright (c) 2013 - Raw Material Software Ltd. | |||
| 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 THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||
| TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||
| NO EVENT SHALL THE AUTHOR 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. | |||
| ------------------------------------------------------------------------------ | |||
| NOTE! This permissive ISC license applies ONLY to files within the juce_core module! | |||
| All other JUCE modules are covered by a dual GPL/commercial license, so if you are | |||
| using any other modules, be sure to check that you also comply with their license. | |||
| For more details, visit www.juce.com | |||
| ============================================================================== | |||
| */ | |||
| FileSearchPath::FileSearchPath() | |||
| { | |||
| } | |||
| FileSearchPath::FileSearchPath (const String& path) | |||
| { | |||
| init (path); | |||
| } | |||
| FileSearchPath::FileSearchPath (const FileSearchPath& other) | |||
| : directories (other.directories) | |||
| { | |||
| } | |||
| FileSearchPath::~FileSearchPath() | |||
| { | |||
| } | |||
| 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()); | |||
| } | |||
| void FileSearchPath::addIfNotAlreadyThere (const File& dir) | |||
| { | |||
| for (int i = 0; i < directories.size(); ++i) | |||
| if (File (directories[i]) == dir) | |||
| return; | |||
| add (dir); | |||
| } | |||
| 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; | |||
| } | |||
| @@ -0,0 +1,169 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the juce_core module of the JUCE library. | |||
| Copyright (c) 2013 - Raw Material Software Ltd. | |||
| 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 THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||
| TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||
| NO EVENT SHALL THE AUTHOR 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. | |||
| ------------------------------------------------------------------------------ | |||
| NOTE! This permissive ISC license applies ONLY to files within the juce_core module! | |||
| All other JUCE modules are covered by a dual GPL/commercial license, so if you are | |||
| using any other modules, be sure to check that you also comply with their license. | |||
| For more details, visit www.juce.com | |||
| ============================================================================== | |||
| */ | |||
| #ifndef JUCE_FILESEARCHPATH_H_INCLUDED | |||
| #define JUCE_FILESEARCHPATH_H_INCLUDED | |||
| #include "juce_File.h" | |||
| #include "../text/juce_StringArray.h" | |||
| //============================================================================== | |||
| /** | |||
| Encapsulates 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& other); | |||
| /** 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. */ | |||
| void 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& other); | |||
| /** 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& path); | |||
| JUCE_LEAK_DETECTOR (FileSearchPath) | |||
| }; | |||
| #endif // JUCE_FILESEARCHPATH_H_INCLUDED | |||
| @@ -0,0 +1,116 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the juce_core module of the JUCE library. | |||
| Copyright (c) 2013 - Raw Material Software Ltd. | |||
| 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 THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||
| TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||
| NO EVENT SHALL THE AUTHOR 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. | |||
| ------------------------------------------------------------------------------ | |||
| NOTE! This permissive ISC license applies ONLY to files within the juce_core module! | |||
| All other JUCE modules are covered by a dual GPL/commercial license, so if you are | |||
| using any other modules, be sure to check that you also comply with their license. | |||
| For more details, visit www.juce.com | |||
| ============================================================================== | |||
| */ | |||
| #ifndef JUCE_MEMORYMAPPEDFILE_H_INCLUDED | |||
| #define JUCE_MEMORYMAPPEDFILE_H_INCLUDED | |||
| #include "juce_File.h" | |||
| //============================================================================== | |||
| /** | |||
| 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. | |||
| */ | |||
| MemoryMappedFile (const File& file, AccessMode mode); | |||
| /** 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); | |||
| /** 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); | |||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MemoryMappedFile) | |||
| }; | |||
| #endif // JUCE_MEMORYMAPPEDFILE_H_INCLUDED | |||
| @@ -0,0 +1,117 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the juce_core module of the JUCE library. | |||
| Copyright (c) 2013 - Raw Material Software Ltd. | |||
| 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 THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||
| TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||
| NO EVENT SHALL THE AUTHOR 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. | |||
| ------------------------------------------------------------------------------ | |||
| NOTE! This permissive ISC license applies ONLY to files within the juce_core module! | |||
| All other JUCE modules are covered by a dual GPL/commercial license, so if you are | |||
| using any other modules, be sure to check that you also comply with their license. | |||
| For more details, visit www.juce.com | |||
| ============================================================================== | |||
| */ | |||
| 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::nonexistent); | |||
| } | |||
| 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::nonexistent); | |||
| if (temporaryFile.exists()) | |||
| { | |||
| // Have a few attempts at overwriting the file before giving up.. | |||
| for (int i = 5; --i >= 0;) | |||
| { | |||
| if (temporaryFile.moveFileTo (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; | |||
| } | |||
| @@ -0,0 +1,171 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the juce_core module of the JUCE library. | |||
| Copyright (c) 2013 - Raw Material Software Ltd. | |||
| 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 THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||
| TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||
| NO EVENT SHALL THE AUTHOR 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. | |||
| ------------------------------------------------------------------------------ | |||
| NOTE! This permissive ISC license applies ONLY to files within the juce_core module! | |||
| All other JUCE modules are covered by a dual GPL/commercial license, so if you are | |||
| using any other modules, be sure to check that you also comply with their license. | |||
| For more details, visit www.juce.com | |||
| ============================================================================== | |||
| */ | |||
| #ifndef JUCE_TEMPORARYFILE_H_INCLUDED | |||
| #define JUCE_TEMPORARYFILE_H_INCLUDED | |||
| #include "juce_File.h" | |||
| //============================================================================== | |||
| /** | |||
| 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::empty, | |||
| 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) | |||
| }; | |||
| #endif // JUCE_TEMPORARYFILE_H_INCLUDED | |||
| @@ -0,0 +1,650 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the juce_core module of the JUCE library. | |||
| Copyright (c) 2013 - Raw Material Software Ltd. | |||
| 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 THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||
| TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||
| NO EVENT SHALL THE AUTHOR 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. | |||
| ------------------------------------------------------------------------------ | |||
| NOTE! This permissive ISC license applies ONLY to files within the juce_core module! | |||
| All other JUCE modules are covered by a dual GPL/commercial license, so if you are | |||
| using any other modules, be sure to check that you also comply with their license. | |||
| For more details, visit www.juce.com | |||
| ============================================================================== | |||
| */ | |||
| class JSONParser | |||
| { | |||
| public: | |||
| static Result parseObjectOrArray (String::CharPointerType t, var& result) | |||
| { | |||
| t = t.findEndOfWhitespace(); | |||
| switch (t.getAndAdvance()) | |||
| { | |||
| case 0: result = var::null; return Result::ok(); | |||
| case '{': return parseObject (t, result); | |||
| case '[': return parseArray (t, result); | |||
| } | |||
| return createFail ("Expected '{' or '['", &t); | |||
| } | |||
| private: | |||
| 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 '-': | |||
| 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::null; | |||
| return Result::ok(); | |||
| } | |||
| break; | |||
| default: | |||
| break; | |||
| } | |||
| return createFail ("Syntax error", &t); | |||
| } | |||
| 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 String propertyName (propertyNameVar.toString()); | |||
| if (propertyName.isNotEmpty()) | |||
| { | |||
| t = t.findEndOfWhitespace(); | |||
| oldT = t; | |||
| const juce_wchar c2 = t.getAndAdvance(); | |||
| if (c2 != ':') | |||
| return createFail ("Expected ':', but found", &oldT); | |||
| resultProperties.set (propertyName, var::null); | |||
| 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::null); | |||
| 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(); | |||
| } | |||
| static Result parseString (String::CharPointerType& t, var& result) | |||
| { | |||
| MemoryOutputStream buffer (256); | |||
| for (;;) | |||
| { | |||
| juce_wchar c = t.getAndAdvance(); | |||
| if (c == '"') | |||
| break; | |||
| if (c == '\\') | |||
| { | |||
| c = t.getAndAdvance(); | |||
| switch (c) | |||
| { | |||
| case '"': | |||
| case '\\': | |||
| case '/': 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) + digitValue); | |||
| } | |||
| break; | |||
| } | |||
| } | |||
| } | |||
| if (c == 0) | |||
| return createFail ("Unexpected end-of-input in string constant"); | |||
| buffer.appendUTF8Char (c); | |||
| } | |||
| result = buffer.toString(); | |||
| return Result::ok(); | |||
| } | |||
| }; | |||
| //============================================================================== | |||
| class JSONFormatter | |||
| { | |||
| public: | |||
| static void write (OutputStream& out, const var& v, | |||
| const int indentLevel, const bool allOnOneLine) | |||
| { | |||
| if (v.isString()) | |||
| { | |||
| writeString (out, v.toString().getCharPointer()); | |||
| } | |||
| else if (v.isVoid()) | |||
| { | |||
| out << "null"; | |||
| } | |||
| else if (v.isBool()) | |||
| { | |||
| out << (static_cast<bool> (v) ? "true" : "false"); | |||
| } | |||
| else if (v.isArray()) | |||
| { | |||
| writeArray (out, *v.getArray(), indentLevel, allOnOneLine); | |||
| } | |||
| else if (v.isObject()) | |||
| { | |||
| if (DynamicObject* const object = v.getDynamicObject()) | |||
| writeObject (out, *object, indentLevel, allOnOneLine); | |||
| 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(); | |||
| } | |||
| } | |||
| private: | |||
| enum { indentSize = 2 }; | |||
| 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) | |||
| { | |||
| out << '"'; | |||
| for (;;) | |||
| { | |||
| const juce_wchar c (t.getAndAdvance()); | |||
| switch (c) | |||
| { | |||
| case 0: out << '"'; return; | |||
| case '\"': out << "\\\""; break; | |||
| case '\\': out << "\\\\"; 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) | |||
| { | |||
| out << '['; | |||
| 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); | |||
| if (i < array.size() - 1) | |||
| { | |||
| if (allOnOneLine) | |||
| out << ", "; | |||
| else | |||
| out << ',' << newLine; | |||
| } | |||
| else if (! allOnOneLine) | |||
| out << newLine; | |||
| } | |||
| if (! allOnOneLine) | |||
| writeSpaces (out, indentLevel); | |||
| out << ']'; | |||
| } | |||
| static void writeObject (OutputStream& out, DynamicObject& object, | |||
| const int indentLevel, const bool allOnOneLine) | |||
| { | |||
| NamedValueSet& props = object.getProperties(); | |||
| out << '{'; | |||
| if (! allOnOneLine) | |||
| out << newLine; | |||
| LinkedListPointer<NamedValueSet::NamedValue>* i = &(props.values); | |||
| for (;;) | |||
| { | |||
| NamedValueSet::NamedValue* const v = i->get(); | |||
| if (v == nullptr) | |||
| break; | |||
| if (! allOnOneLine) | |||
| writeSpaces (out, indentLevel + indentSize); | |||
| writeString (out, v->name); | |||
| out << ": "; | |||
| write (out, v->value, indentLevel + indentSize, allOnOneLine); | |||
| if (v->nextListItem.get() != nullptr) | |||
| { | |||
| if (allOnOneLine) | |||
| out << ", "; | |||
| else | |||
| out << ',' << newLine; | |||
| } | |||
| else if (! allOnOneLine) | |||
| out << newLine; | |||
| i = &(v->nextListItem); | |||
| } | |||
| if (! allOnOneLine) | |||
| writeSpaces (out, indentLevel); | |||
| out << '}'; | |||
| } | |||
| }; | |||
| //============================================================================== | |||
| var JSON::parse (const String& text) | |||
| { | |||
| var result; | |||
| if (! JSONParser::parseObjectOrArray (text.getCharPointer(), result)) | |||
| result = var::null; | |||
| 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) | |||
| { | |||
| MemoryOutputStream mo (1024); | |||
| JSONFormatter::write (mo, data, 0, allOnOneLine); | |||
| return mo.toString(); | |||
| } | |||
| void JSON::writeToStream (OutputStream& output, const var& data, const bool allOnOneLine) | |||
| { | |||
| JSONFormatter::write (output, data, 0, allOnOneLine); | |||
| } | |||
| //============================================================================== | |||
| //============================================================================== | |||
| #if JUCE_UNIT_TESTS | |||
| class JSONTests : public UnitTest | |||
| { | |||
| public: | |||
| JSONTests() : UnitTest ("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); | |||
| } | |||
| static var createRandomVar (Random& r, int depth) | |||
| { | |||
| switch (r.nextInt (depth > 3 ? 6 : 8)) | |||
| { | |||
| case 0: return var::null; | |||
| case 1: return r.nextInt(); | |||
| case 2: return r.nextInt64(); | |||
| case 3: return r.nextBool(); | |||
| case 4: return r.nextDouble(); | |||
| 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 var::null; | |||
| } | |||
| } | |||
| void runTest() | |||
| { | |||
| beginTest ("JSON"); | |||
| Random r; | |||
| r.setSeedRandomly(); | |||
| expect (JSON::parse (String::empty) == var::null); | |||
| 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 | |||
| @@ -0,0 +1,118 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the juce_core module of the JUCE library. | |||
| Copyright (c) 2013 - Raw Material Software Ltd. | |||
| 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 THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||
| TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||
| NO EVENT SHALL THE AUTHOR 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. | |||
| ------------------------------------------------------------------------------ | |||
| NOTE! This permissive ISC license applies ONLY to files within the juce_core module! | |||
| All other JUCE modules are covered by a dual GPL/commercial license, so if you are | |||
| using any other modules, be sure to check that you also comply with their license. | |||
| For more details, visit www.juce.com | |||
| ============================================================================== | |||
| */ | |||
| #ifndef JUCE_JSON_H_INCLUDED | |||
| #define JUCE_JSON_H_INCLUDED | |||
| #include "../misc/juce_Result.h" | |||
| #include "../containers/juce_Variant.h" | |||
| class InputStream; | |||
| class OutputStream; | |||
| class File; | |||
| //============================================================================== | |||
| /** | |||
| 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::null if the parsing fails. | |||
| */ | |||
| 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::null - 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 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::null - 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::null - 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. | |||
| @see writeToStream | |||
| */ | |||
| static String toString (const var& objectToFormat, | |||
| bool allOnOneLine = false); | |||
| /** 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. | |||
| @see toString | |||
| */ | |||
| static void writeToStream (OutputStream& output, | |||
| const var& objectToFormat, | |||
| bool allOnOneLine = false); | |||
| private: | |||
| //============================================================================== | |||
| JSON(); // This class can't be instantiated - just use its static methods. | |||
| }; | |||
| #endif // JUCE_JSON_H_INCLUDED | |||
| @@ -0,0 +1,218 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the juce_core module of the JUCE library. | |||
| Copyright (c) 2013 - Raw Material Software Ltd. | |||
| 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 THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||
| TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||
| NO EVENT SHALL THE AUTHOR 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. | |||
| ------------------------------------------------------------------------------ | |||
| NOTE! This permissive ISC license applies ONLY to files within the juce_core module! | |||
| All other JUCE modules are covered by a dual GPL/commercial license, so if you are | |||
| using any other modules, be sure to check that you also comply with their license. | |||
| For more details, visit www.juce.com | |||
| ============================================================================== | |||
| */ | |||
| #if defined (JUCE_CORE_H_INCLUDED) && ! JUCE_AMALGAMATED_INCLUDE | |||
| /* 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 | |||
| // Your project must contain an AppConfig.h file with your project-specific settings in it, | |||
| // and your header search path must make it accessible to the module's files. | |||
| #include "AppConfig.h" | |||
| //============================================================================== | |||
| #include "native/juce_BasicNativeHeaders.h" | |||
| #include "juce_core.h" | |||
| #include <locale> | |||
| #include <cctype> | |||
| #include <sys/timeb.h> | |||
| #if ! JUCE_ANDROID | |||
| #include <cwctype> | |||
| #endif | |||
| #if JUCE_WINDOWS | |||
| #include <ctime> | |||
| #include <winsock2.h> | |||
| #include <ws2tcpip.h> | |||
| #if ! JUCE_MINGW | |||
| #include <Dbghelp.h> | |||
| #if ! JUCE_DONT_AUTOLINK_TO_WIN32_LIBRARIES | |||
| #pragma comment (lib, "DbgHelp.lib") | |||
| #endif | |||
| #endif | |||
| #if JUCE_MINGW | |||
| #include <ws2spi.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 <langinfo.h> | |||
| #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 | |||
| //============================================================================== | |||
| namespace juce | |||
| { | |||
| #include "containers/juce_AbstractFifo.cpp" | |||
| #include "containers/juce_DynamicObject.cpp" | |||
| #include "containers/juce_NamedValueSet.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 "json/juce_JSON.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_Result.cpp" | |||
| #include "misc/juce_Uuid.cpp" | |||
| #include "network/juce_MACAddress.cpp" | |||
| #include "network/juce_NamedPipe.cpp" | |||
| #include "network/juce_Socket.cpp" | |||
| #include "network/juce_URL.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_OutputStream.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 "text/juce_StringArray.cpp" | |||
| #include "text/juce_StringPairArray.cpp" | |||
| #include "text/juce_StringPool.cpp" | |||
| #include "text/juce_TextDiff.cpp" | |||
| #include "threads/juce_ChildProcess.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" | |||
| //============================================================================== | |||
| #if JUCE_MAC || JUCE_IOS | |||
| #include "native/juce_osx_ObjCHelpers.h" | |||
| #endif | |||
| #if JUCE_ANDROID | |||
| #include "native/juce_android_JNIHelpers.h" | |||
| #endif | |||
| #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_ComSmartPtr.h" | |||
| #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_Files.cpp" | |||
| #include "native/juce_linux_Network.cpp" | |||
| #include "native/juce_linux_SystemStats.cpp" | |||
| #include "native/juce_linux_Threads.cpp" | |||
| //============================================================================== | |||
| #elif JUCE_ANDROID | |||
| #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" | |||
| #endif | |||
| #include "threads/juce_HighResolutionTimer.cpp" | |||
| } | |||
| @@ -0,0 +1,253 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the juce_core module of the JUCE library. | |||
| Copyright (c) 2013 - Raw Material Software Ltd. | |||
| 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 THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||
| TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||
| NO EVENT SHALL THE AUTHOR 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. | |||
| ------------------------------------------------------------------------------ | |||
| NOTE! This permissive ISC license applies ONLY to files within the juce_core module! | |||
| All other JUCE modules are covered by a dual GPL/commercial license, so if you are | |||
| using any other modules, be sure to check that you also comply with their license. | |||
| For more details, visit www.juce.com | |||
| ============================================================================== | |||
| */ | |||
| #ifndef JUCE_CORE_H_INCLUDED | |||
| #define JUCE_CORE_H_INCLUDED | |||
| #ifndef JUCE_MODULE_AVAILABLE_juce_core | |||
| /* If you fail to make sure that all your compile units are building JUCE with the same set of | |||
| option flags, then there's a risk that different compile units will treat the classes as having | |||
| different memory layouts, leading to very nasty memory corruption errors when they all get | |||
| linked together. That's why it's best to always include the Introjucer-generated AppConfig.h | |||
| file before any juce headers. | |||
| Note that if you do have an AppConfig.h file and hit this warning, it means that it doesn't | |||
| contain the JUCE_MODULE_AVAILABLE_xxx flags, which are necessary for some inter-module | |||
| functionality to work correctly. In that case, you should either rebuild your AppConfig.h with | |||
| the latest introjucer, or fix it manually to contain these flags. | |||
| */ | |||
| #ifdef _MSC_VER | |||
| #pragma message ("Have you included your AppConfig.h file before including the JUCE headers?") | |||
| #else | |||
| #warning "Have you included your AppConfig.h file before including the JUCE headers?" | |||
| #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 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_CATCH_UNHANDLED_EXCEPTIONS | |||
| If enabled, this will add some exception-catching code to forward unhandled exceptions | |||
| to your JUCEApplication::unhandledException() callback. | |||
| */ | |||
| #ifndef JUCE_CATCH_UNHANDLED_EXCEPTIONS | |||
| //#define JUCE_CATCH_UNHANDLED_EXCEPTIONS 1 | |||
| #endif | |||
| //============================================================================= | |||
| //============================================================================= | |||
| #if JUCE_MSVC | |||
| #pragma warning (disable: 4251) // (DLL build warning, must be disabled before pushing the warning state) | |||
| #pragma warning (push) | |||
| #pragma warning (disable: 4786) // (long class name warning) | |||
| #ifdef __INTEL_COMPILER | |||
| #pragma warning (disable: 1125) | |||
| #endif | |||
| #endif | |||
| #include "system/juce_StandardHeader.h" | |||
| namespace juce | |||
| { | |||
| // START_AUTOINCLUDE containers, files, json, logging, maths, memory, misc, network, | |||
| // streams, system, text, threads, time, unit_tests, xml, zip | |||
| #include "containers/juce_AbstractFifo.h" | |||
| #include "containers/juce_Array.h" | |||
| #include "containers/juce_ArrayAllocationBase.h" | |||
| #include "containers/juce_DynamicObject.h" | |||
| #include "containers/juce_ElementComparator.h" | |||
| #include "containers/juce_HashMap.h" | |||
| #include "containers/juce_LinkedListPointer.h" | |||
| #include "containers/juce_NamedValueSet.h" | |||
| #include "containers/juce_OwnedArray.h" | |||
| #include "containers/juce_PropertySet.h" | |||
| #include "containers/juce_ReferenceCountedArray.h" | |||
| #include "containers/juce_ScopedValueSetter.h" | |||
| #include "containers/juce_SortedSet.h" | |||
| #include "containers/juce_SparseSet.h" | |||
| #include "containers/juce_Variant.h" | |||
| #include "files/juce_DirectoryIterator.h" | |||
| #include "files/juce_File.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 "json/juce_JSON.h" | |||
| #include "logging/juce_FileLogger.h" | |||
| #include "logging/juce_Logger.h" | |||
| #include "maths/juce_BigInteger.h" | |||
| #include "maths/juce_Expression.h" | |||
| #include "maths/juce_MathsFunctions.h" | |||
| #include "maths/juce_Random.h" | |||
| #include "maths/juce_Range.h" | |||
| #include "memory/juce_Atomic.h" | |||
| #include "memory/juce_ByteOrder.h" | |||
| #include "memory/juce_HeapBlock.h" | |||
| #include "memory/juce_LeakedObjectDetector.h" | |||
| #include "memory/juce_Memory.h" | |||
| #include "memory/juce_MemoryBlock.h" | |||
| #include "memory/juce_OptionalScopedPointer.h" | |||
| #include "memory/juce_ReferenceCountedObject.h" | |||
| #include "memory/juce_ScopedPointer.h" | |||
| #include "memory/juce_Singleton.h" | |||
| #include "memory/juce_WeakReference.h" | |||
| #include "misc/juce_Result.h" | |||
| #include "misc/juce_Uuid.h" | |||
| #include "misc/juce_WindowsRegistry.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 "streams/juce_BufferedInputStream.h" | |||
| #include "streams/juce_FileInputSource.h" | |||
| #include "streams/juce_InputSource.h" | |||
| #include "streams/juce_InputStream.h" | |||
| #include "streams/juce_MemoryInputStream.h" | |||
| #include "streams/juce_MemoryOutputStream.h" | |||
| #include "streams/juce_OutputStream.h" | |||
| #include "streams/juce_SubregionStream.h" | |||
| #include "system/juce_PlatformDefs.h" | |||
| #include "system/juce_StandardHeader.h" | |||
| #include "system/juce_SystemStats.h" | |||
| #include "system/juce_TargetPlatform.h" | |||
| #include "text/juce_CharacterFunctions.h" | |||
| #include "text/juce_CharPointer_ASCII.h" | |||
| #include "text/juce_CharPointer_UTF16.h" | |||
| #include "text/juce_CharPointer_UTF32.h" | |||
| #include "text/juce_CharPointer_UTF8.h" | |||
| #include "text/juce_Identifier.h" | |||
| #include "text/juce_LocalisedStrings.h" | |||
| #include "text/juce_NewLine.h" | |||
| #include "text/juce_String.h" | |||
| #include "text/juce_StringArray.h" | |||
| #include "text/juce_StringPairArray.h" | |||
| #include "text/juce_StringPool.h" | |||
| #include "text/juce_TextDiff.h" | |||
| #include "threads/juce_ChildProcess.h" | |||
| #include "threads/juce_CriticalSection.h" | |||
| #include "threads/juce_DynamicLibrary.h" | |||
| #include "threads/juce_HighResolutionTimer.h" | |||
| #include "threads/juce_InterProcessLock.h" | |||
| #include "threads/juce_Process.h" | |||
| #include "threads/juce_ReadWriteLock.h" | |||
| #include "threads/juce_ScopedLock.h" | |||
| #include "threads/juce_ScopedReadLock.h" | |||
| #include "threads/juce_ScopedWriteLock.h" | |||
| #include "threads/juce_SpinLock.h" | |||
| #include "threads/juce_Thread.h" | |||
| #include "threads/juce_ThreadLocalValue.h" | |||
| #include "threads/juce_ThreadPool.h" | |||
| #include "threads/juce_TimeSliceThread.h" | |||
| #include "threads/juce_WaitableEvent.h" | |||
| #include "time/juce_PerformanceCounter.h" | |||
| #include "time/juce_RelativeTime.h" | |||
| #include "time/juce_Time.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" | |||
| // END_AUTOINCLUDE | |||
| } | |||
| #if JUCE_MSVC | |||
| #pragma warning (pop) | |||
| #endif | |||
| #endif // JUCE_CORE_H_INCLUDED | |||
| @@ -0,0 +1,29 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the juce_core module of the JUCE library. | |||
| Copyright (c) 2013 - Raw Material Software Ltd. | |||
| 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 THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||
| TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||
| NO EVENT SHALL THE AUTHOR 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. | |||
| ------------------------------------------------------------------------------ | |||
| NOTE! This permissive ISC license applies ONLY to files within the juce_core module! | |||
| All other JUCE modules are covered by a dual GPL/commercial license, so if you are | |||
| using any other modules, be sure to check that you also comply with their license. | |||
| For more details, visit www.juce.com | |||
| ============================================================================== | |||
| */ | |||
| #include "juce_core.cpp" | |||
| @@ -0,0 +1,38 @@ | |||
| { | |||
| "id": "juce_core", | |||
| "name": "JUCE core classes", | |||
| "version": "2.1.2", | |||
| "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 Permissive", | |||
| "dependencies": [], | |||
| "include": "juce_core.h", | |||
| "compile": [ { "file": "juce_core.cpp", "target": "! xcode" }, | |||
| { "file": "juce_core.mm", "target": "xcode" } ], | |||
| "browse": [ "text/*", | |||
| "maths/*", | |||
| "memory/*", | |||
| "containers/*", | |||
| "threads/*", | |||
| "time/*", | |||
| "files/*", | |||
| "network/*", | |||
| "streams/*", | |||
| "logging/*", | |||
| "system/*", | |||
| "xml/*", | |||
| "json/*", | |||
| "zip/*", | |||
| "unit_tests/*", | |||
| "misc/*", | |||
| "native/*" ], | |||
| "OSXFrameworks": "Cocoa IOKit", | |||
| "iOSFrameworks": "Foundation", | |||
| "LinuxLibs": "rt dl pthread", | |||
| "mingwLibs": "uuid wsock32 wininet version ole32 ws2_32 oleaut32 imm32 comdlg32 shlwapi rpcrt4 winmm" | |||
| } | |||
| @@ -0,0 +1,134 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the juce_core module of the JUCE library. | |||
| Copyright (c) 2013 - Raw Material Software Ltd. | |||
| 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 THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||
| TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||
| NO EVENT SHALL THE AUTHOR 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. | |||
| ------------------------------------------------------------------------------ | |||
| NOTE! This permissive ISC license applies ONLY to files within the juce_core module! | |||
| All other JUCE modules are covered by a dual GPL/commercial license, so if you are | |||
| using any other modules, be sure to check that you also comply with their license. | |||
| For more details, visit www.juce.com | |||
| ============================================================================== | |||
| */ | |||
| FileLogger::FileLogger (const File& file, | |||
| const String& welcomeMessage, | |||
| const int64 maxInitialFileSizeBytes) | |||
| : logFile (file) | |||
| { | |||
| if (maxInitialFileSizeBytes >= 0) | |||
| trimFileSize (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 (int64 maxFileSizeBytes) const | |||
| { | |||
| if (maxFileSizeBytes <= 0) | |||
| { | |||
| logFile.deleteFile(); | |||
| } | |||
| else | |||
| { | |||
| const int64 fileSize = logFile.getSize(); | |||
| if (fileSize > maxFileSizeBytes) | |||
| { | |||
| TemporaryFile tempFile (logFile); | |||
| { | |||
| FileOutputStream out (tempFile.getFile()); | |||
| FileInputStream in (logFile); | |||
| 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); | |||
| } | |||
| @@ -0,0 +1,139 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the juce_core module of the JUCE library. | |||
| Copyright (c) 2013 - Raw Material Software Ltd. | |||
| 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 THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||
| TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||
| NO EVENT SHALL THE AUTHOR 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. | |||
| ------------------------------------------------------------------------------ | |||
| NOTE! This permissive ISC license applies ONLY to files within the juce_core module! | |||
| All other JUCE modules are covered by a dual GPL/commercial license, so if you are | |||
| using any other modules, be sure to check that you also comply with their license. | |||
| For more details, visit www.juce.com | |||
| ============================================================================== | |||
| */ | |||
| #ifndef JUCE_FILELOGGER_H_INCLUDED | |||
| #define JUCE_FILELOGGER_H_INCLUDED | |||
| #include "juce_Logger.h" | |||
| #include "../files/juce_File.h" | |||
| #include "../memory/juce_ScopedPointer.h" | |||
| //============================================================================== | |||
| /** | |||
| 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&); | |||
| private: | |||
| //============================================================================== | |||
| File logFile; | |||
| CriticalSection logLock; | |||
| void trimFileSize (int64 maxFileSizeBytes) const; | |||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FileLogger) | |||
| }; | |||
| #endif // JUCE_FILELOGGER_H_INCLUDED | |||
| @@ -0,0 +1,63 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the juce_core module of the JUCE library. | |||
| Copyright (c) 2013 - Raw Material Software Ltd. | |||
| 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 THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||
| TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||
| NO EVENT SHALL THE AUTHOR 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. | |||
| ------------------------------------------------------------------------------ | |||
| NOTE! This permissive ISC license applies ONLY to files within the juce_core module! | |||
| All other JUCE modules are covered by a dual GPL/commercial license, so if you are | |||
| using any other modules, be sure to check that you also comply with their license. | |||
| For more details, visit www.juce.com | |||
| ============================================================================== | |||
| */ | |||
| 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 | |||
| @@ -0,0 +1,99 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the juce_core module of the JUCE library. | |||
| Copyright (c) 2013 - Raw Material Software Ltd. | |||
| 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 THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||
| TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||
| NO EVENT SHALL THE AUTHOR 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. | |||
| ------------------------------------------------------------------------------ | |||
| NOTE! This permissive ISC license applies ONLY to files within the juce_core module! | |||
| All other JUCE modules are covered by a dual GPL/commercial license, so if you are | |||
| using any other modules, be sure to check that you also comply with their license. | |||
| For more details, visit www.juce.com | |||
| ============================================================================== | |||
| */ | |||
| #ifndef JUCE_LOGGER_H_INCLUDED | |||
| #define JUCE_LOGGER_H_INCLUDED | |||
| #include "../text/juce_String.h" | |||
| //============================================================================== | |||
| /** | |||
| 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 disable any logging. | |||
| */ | |||
| static void JUCE_CALLTYPE setCurrentLogger (Logger* newLogger) noexcept; | |||
| /** Returns the current logger, or nullptr if none has been set. */ | |||
| static Logger* 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; | |||
| }; | |||
| #endif // JUCE_LOGGER_H_INCLUDED | |||
| @@ -0,0 +1,334 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the juce_core module of the JUCE library. | |||
| Copyright (c) 2013 - Raw Material Software Ltd. | |||
| 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 THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||
| TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||
| NO EVENT SHALL THE AUTHOR 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. | |||
| ------------------------------------------------------------------------------ | |||
| NOTE! This permissive ISC license applies ONLY to files within the juce_core module! | |||
| All other JUCE modules are covered by a dual GPL/commercial license, so if you are | |||
| using any other modules, be sure to check that you also comply with their license. | |||
| For more details, visit www.juce.com | |||
| ============================================================================== | |||
| */ | |||
| #ifndef JUCE_BIGINTEGER_H_INCLUDED | |||
| #define JUCE_BIGINTEGER_H_INCLUDED | |||
| #include "../text/juce_String.h" | |||
| #include "../memory/juce_HeapBlock.h" | |||
| class MemoryBlock; | |||
| //============================================================================== | |||
| /** | |||
| 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& other); | |||
| #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS | |||
| BigInteger (BigInteger&& other) noexcept; | |||
| BigInteger& operator= (BigInteger&& other) noexcept; | |||
| #endif | |||
| /** Destructor. */ | |||
| ~BigInteger(); | |||
| //============================================================================== | |||
| /** Copies another BigInteger onto this one. */ | |||
| BigInteger& operator= (const BigInteger& other); | |||
| /** Swaps the internal contents of this with another object. */ | |||
| void swapWith (BigInteger& other) 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 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; | |||
| //============================================================================== | |||
| /** Resets the value to 0. */ | |||
| void clear(); | |||
| /** 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; | |||
| //============================================================================== | |||
| // All the standard arithmetic ops... | |||
| BigInteger& operator+= (const BigInteger& other); | |||
| BigInteger& operator-= (const BigInteger& other); | |||
| BigInteger& operator*= (const BigInteger& other); | |||
| BigInteger& operator/= (const BigInteger& other); | |||
| BigInteger& operator|= (const BigInteger& other); | |||
| BigInteger& operator&= (const BigInteger& other); | |||
| BigInteger& operator^= (const BigInteger& other); | |||
| BigInteger& operator%= (const BigInteger& other); | |||
| 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& other) const; | |||
| BigInteger operator- (const BigInteger& other) const; | |||
| BigInteger operator* (const BigInteger& other) const; | |||
| BigInteger operator/ (const BigInteger& other) const; | |||
| BigInteger operator| (const BigInteger& other) const; | |||
| BigInteger operator& (const BigInteger& other) const; | |||
| BigInteger operator^ (const BigInteger& other) const; | |||
| BigInteger operator% (const BigInteger& other) const; | |||
| BigInteger operator<< (int numBitsToShift) const; | |||
| BigInteger operator>> (int numBitsToShift) const; | |||
| bool operator== (const BigInteger& other) const noexcept; | |||
| bool operator!= (const BigInteger& other) const noexcept; | |||
| bool operator< (const BigInteger& other) const noexcept; | |||
| bool operator<= (const BigInteger& other) const noexcept; | |||
| bool operator> (const BigInteger& other) const noexcept; | |||
| bool operator>= (const BigInteger& other) 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 one passed-in. | |||
| */ | |||
| 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); | |||
| //============================================================================== | |||
| /** 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; | |||
| //============================================================================== | |||
| /** 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 (const String& 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: | |||
| //============================================================================== | |||
| HeapBlock <uint32> values; | |||
| size_t numValues; | |||
| int highestBit; | |||
| bool negative; | |||
| void ensureSize (size_t numVals); | |||
| 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); | |||
| //============================================================================== | |||
| #ifndef DOXYGEN | |||
| // For backwards compatibility, BitArray is defined as an alias for BigInteger. | |||
| typedef BigInteger BitArray; | |||
| #endif | |||
| #endif // JUCE_BIGINTEGER_H_INCLUDED | |||
| @@ -0,0 +1,274 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the juce_core module of the JUCE library. | |||
| Copyright (c) 2013 - Raw Material Software Ltd. | |||
| 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 THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||
| TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||
| NO EVENT SHALL THE AUTHOR 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. | |||
| ------------------------------------------------------------------------------ | |||
| NOTE! This permissive ISC license applies ONLY to files within the juce_core module! | |||
| All other JUCE modules are covered by a dual GPL/commercial license, so if you are | |||
| using any other modules, be sure to check that you also comply with their license. | |||
| For more details, visit www.juce.com | |||
| ============================================================================== | |||
| */ | |||
| #ifndef JUCE_EXPRESSION_H_INCLUDED | |||
| #define JUCE_EXPRESSION_H_INCLUDED | |||
| #include "../memory/juce_ReferenceCountedObject.h" | |||
| #include "../containers/juce_Array.h" | |||
| #include "../memory/juce_ScopedPointer.h" | |||
| //============================================================================== | |||
| /** | |||
| 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 simple expression with a specified constant value. */ | |||
| explicit Expression (double constant); | |||
| /** Creates a copy of an expression. */ | |||
| Expression (const Expression& other); | |||
| /** Copies another expression. */ | |||
| Expression& operator= (const Expression& other); | |||
| #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS | |||
| Expression (Expression&& other) noexcept; | |||
| Expression& operator= (Expression&& other) noexcept; | |||
| #endif | |||
| /** Creates an expression by parsing a string. | |||
| If there's a syntax error in the string, this will throw a ParseError exception. | |||
| @throws ParseError | |||
| */ | |||
| explicit Expression (const String& stringToParse); | |||
| /** Returns a string version of the expression. */ | |||
| String toString() const; | |||
| /** Returns an expression which is an addtion operation of two existing expressions. */ | |||
| Expression operator+ (const Expression& other) const; | |||
| /** Returns an expression which is a subtraction operation of two existing expressions. */ | |||
| Expression operator- (const Expression& other) const; | |||
| /** Returns an expression which is a multiplication operation of two existing expressions. */ | |||
| Expression operator* (const Expression& other) const; | |||
| /** Returns an expression which is a division operation of two existing expressions. */ | |||
| Expression operator/ (const Expression& other) 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 the string, this will throw a ParseError exception. | |||
| @throws ParseError | |||
| */ | |||
| static Expression parse (String::CharPointerType& stringToParse); | |||
| //============================================================================== | |||
| /** 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; | |||
| //============================================================================== | |||
| /** An exception that can be thrown by Expression::parse(). */ | |||
| class ParseError : public std::exception | |||
| { | |||
| public: | |||
| ParseError (const String& message); | |||
| String description; | |||
| }; | |||
| //============================================================================== | |||
| /** 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 class ScopedPointer<Term>; | |||
| friend class ReferenceCountedObjectPtr<Term>; | |||
| ReferenceCountedObjectPtr<Term> term; | |||
| explicit Expression (Term*); | |||
| }; | |||
| #endif // JUCE_EXPRESSION_H_INCLUDED | |||
| @@ -0,0 +1,514 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the juce_core module of the JUCE library. | |||
| Copyright (c) 2013 - Raw Material Software Ltd. | |||
| 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 THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||
| TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||
| NO EVENT SHALL THE AUTHOR 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. | |||
| ------------------------------------------------------------------------------ | |||
| NOTE! This permissive ISC license applies ONLY to files within the juce_core module! | |||
| All other JUCE modules are covered by a dual GPL/commercial license, so if you are | |||
| using any other modules, be sure to check that you also comply with their license. | |||
| For more details, visit www.juce.com | |||
| ============================================================================== | |||
| */ | |||
| #ifndef JUCE_MATHSFUNCTIONS_H_INCLUDED | |||
| #define JUCE_MATHSFUNCTIONS_H_INCLUDED | |||
| //============================================================================== | |||
| /* | |||
| 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_MSVC | |||
| typedef pointer_sized_int ssize_t; | |||
| #endif | |||
| //============================================================================== | |||
| // Some indispensible min/max functions | |||
| /** Returns the larger of two values. */ | |||
| template <typename Type> | |||
| inline Type jmax (const Type a, const Type b) { return (a < b) ? b : a; } | |||
| /** Returns the larger of three values. */ | |||
| template <typename Type> | |||
| inline 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> | |||
| inline 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> | |||
| inline Type jmin (const Type a, const Type b) { return (b < a) ? b : a; } | |||
| /** Returns the smaller of three values. */ | |||
| template <typename Type> | |||
| inline 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> | |||
| inline Type jmin (const Type a, const Type b, const Type c, const Type d) { return jmin (a, jmin (b, c, d)); } | |||
| /** Scans an array of values, returning the minimum value that it contains. */ | |||
| template <typename Type> | |||
| const 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> | |||
| const 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 jlimit0To, jmin, jmax | |||
| */ | |||
| template <typename Type> | |||
| inline Type jlimit (const Type lowerLimit, | |||
| const Type upperLimit, | |||
| const 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 Type> | |||
| inline bool isPositiveAndBelow (Type valueToTest, Type upperLimit) noexcept | |||
| { | |||
| jassert (Type() <= upperLimit); // makes no sense to call this if the upper limit is itself below zero.. | |||
| return Type() <= valueToTest && valueToTest < upperLimit; | |||
| } | |||
| template <> | |||
| inline bool isPositiveAndBelow (const int valueToTest, const int 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 Type> | |||
| inline bool isPositiveAndNotGreaterThan (Type valueToTest, Type upperLimit) noexcept | |||
| { | |||
| jassert (Type() <= upperLimit); // makes no sense to call this if the upper limit is itself below zero.. | |||
| return Type() <= valueToTest && valueToTest <= upperLimit; | |||
| } | |||
| template <> | |||
| inline bool isPositiveAndNotGreaterThan (const int valueToTest, const int 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 to swap two values. */ | |||
| template <typename Type> | |||
| inline void swapVariables (Type& variable1, Type& variable2) | |||
| { | |||
| std::swap (variable1, variable2); | |||
| } | |||
| /** 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> | |||
| inline int numElementsInArray (Type (&array)[N]) | |||
| { | |||
| (void) array; // (required to avoid a spurious warning in MS compilers) | |||
| (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> | |||
| inline 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 | |||
| } | |||
| /** 64-bit abs function. */ | |||
| inline int64 abs64 (const int64 n) noexcept | |||
| { | |||
| return (n >= 0) ? n : -n; | |||
| } | |||
| //============================================================================== | |||
| /** A predefined value for Pi, at double-precision. | |||
| @see float_Pi | |||
| */ | |||
| const double double_Pi = 3.1415926535897932384626433832795; | |||
| /** A predefined value for Pi, at single-precision. | |||
| @see double_Pi | |||
| */ | |||
| const float float_Pi = 3.14159265358979323846f; | |||
| //============================================================================== | |||
| /** The isfinite() method seems to vary between platforms, so this is a | |||
| platform-independent function for it. | |||
| */ | |||
| template <typename FloatingPointType> | |||
| inline bool juce_isfinite (FloatingPointType value) | |||
| { | |||
| #if JUCE_WINDOWS | |||
| return _finite (value); | |||
| #elif JUCE_ANDROID | |||
| return isfinite (value); | |||
| #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> | |||
| inline 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 | |||
| } | |||
| #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 (const 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 (const 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 (const float value) noexcept | |||
| { | |||
| return roundToInt (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; | |||
| } | |||
| /** 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; | |||
| } | |||
| //============================================================================== | |||
| #if (JUCE_INTEL && JUCE_32BIT) || 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 += 1.0f; x -= 1.0f; | |||
| #else | |||
| #define JUCE_UNDENORMALISE(x) | |||
| #endif | |||
| //============================================================================== | |||
| /** This namespace contains a few template classes for helping work out class type variations. | |||
| */ | |||
| namespace TypeHelpers | |||
| { | |||
| #if JUCE_VC8_OR_EARLIER | |||
| #define PARAMETER_TYPE(type) const type& | |||
| #else | |||
| /** 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. | |||
| Because "typename TypeHelpers::ParameterType<SomeClass>::type" is a bit of a mouthful, there's | |||
| a PARAMETER_TYPE(SomeClass) macro that you can use to get the same effect. | |||
| E.g. "myFunction (PARAMETER_TYPE (int), PARAMETER_TYPE (MyObject))" | |||
| 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 | |||
| /** A helpful macro to simplify the use of the ParameterType template. | |||
| @see ParameterType | |||
| */ | |||
| #define PARAMETER_TYPE(a) typename TypeHelpers::ParameterType<a>::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; }; | |||
| } | |||
| //============================================================================== | |||
| #endif // JUCE_MATHSFUNCTIONS_H_INCLUDED | |||
| @@ -0,0 +1,190 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the juce_core module of the JUCE library. | |||
| Copyright (c) 2013 - Raw Material Software Ltd. | |||
| 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 THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||
| TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||
| NO EVENT SHALL THE AUTHOR 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. | |||
| ------------------------------------------------------------------------------ | |||
| NOTE! This permissive ISC license applies ONLY to files within the juce_core module! | |||
| All other JUCE modules are covered by a dual GPL/commercial license, so if you are | |||
| using any other modules, be sure to check that you also comply with their license. | |||
| For more details, visit www.juce.com | |||
| ============================================================================== | |||
| */ | |||
| Random::Random (const 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 = (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); | |||
| } | |||
| int64 Random::nextInt64() noexcept | |||
| { | |||
| return (((int64) nextInt()) << 32) | (int64) (uint64) (uint32) nextInt(); | |||
| } | |||
| bool Random::nextBool() noexcept | |||
| { | |||
| return (nextInt() & 0x40000000) != 0; | |||
| } | |||
| float Random::nextFloat() noexcept | |||
| { | |||
| return static_cast <uint32> (nextInt()) / (float) 0xffffffff; | |||
| } | |||
| double Random::nextDouble() noexcept | |||
| { | |||
| return static_cast <uint32> (nextInt()) / (double) 0xffffffff; | |||
| } | |||
| 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") {} | |||
| void runTest() | |||
| { | |||
| beginTest ("Random"); | |||
| for (int j = 10; --j >= 0;) | |||
| { | |||
| Random r; | |||
| r.setSeedRandomly(); | |||
| for (int i = 20; --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 | |||
| @@ -0,0 +1,143 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the juce_core module of the JUCE library. | |||
| Copyright (c) 2013 - Raw Material Software Ltd. | |||
| 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 THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||
| TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||
| NO EVENT SHALL THE AUTHOR 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. | |||
| ------------------------------------------------------------------------------ | |||
| NOTE! This permissive ISC license applies ONLY to files within the juce_core module! | |||
| All other JUCE modules are covered by a dual GPL/commercial license, so if you are | |||
| using any other modules, be sure to check that you also comply with their license. | |||
| For more details, visit www.juce.com | |||
| ============================================================================== | |||
| */ | |||
| #ifndef JUCE_RANDOM_H_INCLUDED | |||
| #define JUCE_RANDOM_H_INCLUDED | |||
| #include "juce_BigInteger.h" | |||
| //============================================================================== | |||
| /** | |||
| 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 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 to 1.0 | |||
| */ | |||
| float nextFloat() noexcept; | |||
| /** Returns the next random floating-point number. | |||
| @returns a random value in the range 0 to 1.0 | |||
| */ | |||
| 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; | |||
| /** 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) | |||
| }; | |||
| #endif // JUCE_RANDOM_H_INCLUDED | |||
| @@ -0,0 +1,264 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the juce_core module of the JUCE library. | |||
| Copyright (c) 2013 - Raw Material Software Ltd. | |||
| 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 THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||
| TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||
| NO EVENT SHALL THE AUTHOR 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. | |||
| ------------------------------------------------------------------------------ | |||
| NOTE! This permissive ISC license applies ONLY to files within the juce_core module! | |||
| All other JUCE modules are covered by a dual GPL/commercial license, so if you are | |||
| using any other modules, be sure to check that you also comply with their license. | |||
| For more details, visit www.juce.com | |||
| ============================================================================== | |||
| */ | |||
| #ifndef JUCE_RANGE_H_INCLUDED | |||
| #define JUCE_RANGE_H_INCLUDED | |||
| //============================================================================== | |||
| /** A general-purpose range object, that simply represents any linear range with | |||
| a start and end point. | |||
| 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 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); | |||
| } | |||
| //============================================================================== | |||
| /** 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. */ | |||
| 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 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())); | |||
| } | |||
| private: | |||
| //============================================================================== | |||
| ValueType start, end; | |||
| }; | |||
| #endif // JUCE_RANGE_H_INCLUDED | |||
| @@ -0,0 +1,396 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the juce_core module of the JUCE library. | |||
| Copyright (c) 2013 - Raw Material Software Ltd. | |||
| 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 THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||
| TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||
| NO EVENT SHALL THE AUTHOR 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. | |||
| ------------------------------------------------------------------------------ | |||
| NOTE! This permissive ISC license applies ONLY to files within the juce_core module! | |||
| All other JUCE modules are covered by a dual GPL/commercial license, so if you are | |||
| using any other modules, be sure to check that you also comply with their license. | |||
| For more details, visit www.juce.com | |||
| ============================================================================== | |||
| */ | |||
| #ifndef JUCE_ATOMIC_H_INCLUDED | |||
| #define JUCE_ATOMIC_H_INCLUDED | |||
| //============================================================================== | |||
| /** | |||
| 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: | |||
| /** Creates a new value, initialised to zero. */ | |||
| inline Atomic() noexcept | |||
| : value (0) | |||
| { | |||
| } | |||
| /** Creates a new value, with a given initial value. */ | |||
| inline explicit Atomic (const Type initialValue) noexcept | |||
| : value (initialValue) | |||
| { | |||
| } | |||
| /** Copies another value (atomically). */ | |||
| inline Atomic (const Atomic& other) noexcept | |||
| : value (other.get()) | |||
| { | |||
| } | |||
| /** Destructor. */ | |||
| inline ~Atomic() noexcept | |||
| { | |||
| // This class can only be used for types which are 32 or 64 bits in size. | |||
| static_jassert (sizeof (Type) == 4 || sizeof (Type) == 8); | |||
| } | |||
| /** Atomically reads and returns the current value. */ | |||
| Type get() const noexcept; | |||
| /** Copies another value onto this one (atomically). */ | |||
| inline Atomic& operator= (const Atomic& other) noexcept { exchange (other.get()); return *this; } | |||
| /** Copies another value onto this one (atomically). */ | |||
| inline Atomic& operator= (const Type newValue) noexcept { exchange (newValue); return *this; } | |||
| /** Atomically sets the current value. */ | |||
| void set (Type newValue) noexcept { exchange (newValue); } | |||
| /** Atomically sets the current value, returning the value that was replaced. */ | |||
| Type exchange (Type value) noexcept; | |||
| /** Atomically adds a number to this value, returning the new value. */ | |||
| Type operator+= (Type amountToAdd) noexcept; | |||
| /** Atomically subtracts a number from this value, returning the new value. */ | |||
| Type operator-= (Type 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 | |||
| */ | |||
| bool compareAndSetBool (Type newValue, Type valueToCompare) 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 | |||
| 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 | |||
| */ | |||
| Type compareAndSetValue (Type newValue, Type valueToCompare) noexcept; | |||
| /** Implements a memory read/write barrier. */ | |||
| 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 publically in case you need to manipulate it directly | |||
| for performance reasons. | |||
| */ | |||
| volatile Type value; | |||
| private: | |||
| 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)); | |||
| } | |||
| }; | |||
| //============================================================================== | |||
| /* | |||
| The following code is in the header so that the atomics can be inlined where possible... | |||
| */ | |||
| #if JUCE_IOS || (JUCE_MAC && (JUCE_PPC || JUCE_CLANG || __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 2))) | |||
| #define JUCE_ATOMICS_MAC 1 // Older OSX builds using gcc4.1 or earlier | |||
| #if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5 | |||
| #define JUCE_MAC_ATOMICS_VOLATILE | |||
| #else | |||
| #define JUCE_MAC_ATOMICS_VOLATILE volatile | |||
| #endif | |||
| #if JUCE_PPC || JUCE_IOS | |||
| // None of these atomics are available for PPC or for iOS 3.1 or earlier!! | |||
| template <typename Type> static Type OSAtomicAdd64Barrier (Type b, JUCE_MAC_ATOMICS_VOLATILE Type* a) noexcept { jassertfalse; return *a += b; } | |||
| template <typename Type> static Type OSAtomicIncrement64Barrier (JUCE_MAC_ATOMICS_VOLATILE Type* a) noexcept { jassertfalse; return ++*a; } | |||
| template <typename Type> static Type OSAtomicDecrement64Barrier (JUCE_MAC_ATOMICS_VOLATILE Type* a) noexcept { jassertfalse; return --*a; } | |||
| template <typename Type> static bool OSAtomicCompareAndSwap64Barrier (Type old, Type newValue, JUCE_MAC_ATOMICS_VOLATILE Type* value) noexcept | |||
| { jassertfalse; if (old == *value) { *value = newValue; return true; } return false; } | |||
| #define JUCE_64BIT_ATOMICS_UNAVAILABLE 1 | |||
| #endif | |||
| //============================================================================== | |||
| #elif JUCE_GCC | |||
| #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 | |||
| //============================================================================== | |||
| #else | |||
| #define JUCE_ATOMICS_WINDOWS 1 // Windows with intrinsics | |||
| #if JUCE_USE_INTRINSICS | |||
| #ifndef __INTEL_COMPILER | |||
| #pragma intrinsic (_InterlockedExchange, _InterlockedIncrement, _InterlockedDecrement, _InterlockedCompareExchange, \ | |||
| _InterlockedCompareExchange64, _InterlockedExchangeAdd, _ReadWriteBarrier) | |||
| #endif | |||
| #define juce_InterlockedExchange(a, b) _InterlockedExchange(a, b) | |||
| #define juce_InterlockedIncrement(a) _InterlockedIncrement(a) | |||
| #define juce_InterlockedDecrement(a) _InterlockedDecrement(a) | |||
| #define juce_InterlockedExchangeAdd(a, b) _InterlockedExchangeAdd(a, b) | |||
| #define juce_InterlockedCompareExchange(a, b, c) _InterlockedCompareExchange(a, b, c) | |||
| #define juce_InterlockedCompareExchange64(a, b, c) _InterlockedCompareExchange64(a, b, c) | |||
| #define juce_MemoryBarrier _ReadWriteBarrier | |||
| #else | |||
| long juce_InterlockedExchange (volatile long* a, long b) noexcept; | |||
| long juce_InterlockedIncrement (volatile long* a) noexcept; | |||
| long juce_InterlockedDecrement (volatile long* a) noexcept; | |||
| long juce_InterlockedExchangeAdd (volatile long* a, long b) noexcept; | |||
| long juce_InterlockedCompareExchange (volatile long* a, long b, long c) noexcept; | |||
| __int64 juce_InterlockedCompareExchange64 (volatile __int64* a, __int64 b, __int64 c) noexcept; | |||
| inline void juce_MemoryBarrier() noexcept { long x = 0; juce_InterlockedIncrement (&x); } | |||
| #endif | |||
| #if JUCE_64BIT | |||
| #ifndef __INTEL_COMPILER | |||
| #pragma intrinsic (_InterlockedExchangeAdd64, _InterlockedExchange64, _InterlockedIncrement64, _InterlockedDecrement64) | |||
| #endif | |||
| #define juce_InterlockedExchangeAdd64(a, b) _InterlockedExchangeAdd64(a, b) | |||
| #define juce_InterlockedExchange64(a, b) _InterlockedExchange64(a, b) | |||
| #define juce_InterlockedIncrement64(a) _InterlockedIncrement64(a) | |||
| #define juce_InterlockedDecrement64(a) _InterlockedDecrement64(a) | |||
| #else | |||
| // None of these atomics are available in a 32-bit Windows build!! | |||
| template <typename Type> static Type juce_InterlockedExchangeAdd64 (volatile Type* a, Type b) noexcept { jassertfalse; Type old = *a; *a += b; return old; } | |||
| template <typename Type> static Type juce_InterlockedExchange64 (volatile Type* a, Type b) noexcept { jassertfalse; Type old = *a; *a = b; return old; } | |||
| template <typename Type> static Type juce_InterlockedIncrement64 (volatile Type* a) noexcept { jassertfalse; return ++*a; } | |||
| template <typename Type> static Type juce_InterlockedDecrement64 (volatile Type* a) noexcept { jassertfalse; return --*a; } | |||
| #define JUCE_64BIT_ATOMICS_UNAVAILABLE 1 | |||
| #endif | |||
| #endif | |||
| #if JUCE_MSVC | |||
| #pragma warning (push) | |||
| #pragma warning (disable: 4311) // (truncation warning) | |||
| #endif | |||
| //============================================================================== | |||
| template <typename Type> | |||
| inline Type Atomic<Type>::get() const noexcept | |||
| { | |||
| #if JUCE_ATOMICS_MAC | |||
| return sizeof (Type) == 4 ? castFrom32Bit ((int32) OSAtomicAdd32Barrier ((int32_t) 0, (JUCE_MAC_ATOMICS_VOLATILE int32_t*) &value)) | |||
| : castFrom64Bit ((int64) OSAtomicAdd64Barrier ((int64_t) 0, (JUCE_MAC_ATOMICS_VOLATILE int64_t*) &value)); | |||
| #elif JUCE_ATOMICS_WINDOWS | |||
| return sizeof (Type) == 4 ? castFrom32Bit ((int32) juce_InterlockedExchangeAdd ((volatile long*) &value, (long) 0)) | |||
| : castFrom64Bit ((int64) juce_InterlockedExchangeAdd64 ((volatile __int64*) &value, (__int64) 0)); | |||
| #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 Atomic<Type>::exchange (const Type newValue) noexcept | |||
| { | |||
| #if JUCE_ATOMICS_MAC || JUCE_ATOMICS_GCC | |||
| Type currentVal = value; | |||
| while (! compareAndSetBool (newValue, currentVal)) { currentVal = value; } | |||
| return currentVal; | |||
| #elif JUCE_ATOMICS_WINDOWS | |||
| return sizeof (Type) == 4 ? castFrom32Bit ((int32) juce_InterlockedExchange ((volatile long*) &value, (long) castTo32Bit (newValue))) | |||
| : castFrom64Bit ((int64) juce_InterlockedExchange64 ((volatile __int64*) &value, (__int64) castTo64Bit (newValue))); | |||
| #endif | |||
| } | |||
| template <typename Type> | |||
| inline Type Atomic<Type>::operator+= (const Type amountToAdd) noexcept | |||
| { | |||
| #if JUCE_ATOMICS_MAC | |||
| return sizeof (Type) == 4 ? (Type) OSAtomicAdd32Barrier ((int32_t) castTo32Bit (amountToAdd), (JUCE_MAC_ATOMICS_VOLATILE int32_t*) &value) | |||
| : (Type) OSAtomicAdd64Barrier ((int64_t) amountToAdd, (JUCE_MAC_ATOMICS_VOLATILE int64_t*) &value); | |||
| #elif JUCE_ATOMICS_WINDOWS | |||
| return sizeof (Type) == 4 ? (Type) (juce_InterlockedExchangeAdd ((volatile long*) &value, (long) amountToAdd) + (long) amountToAdd) | |||
| : (Type) (juce_InterlockedExchangeAdd64 ((volatile __int64*) &value, (__int64) amountToAdd) + (__int64) amountToAdd); | |||
| #elif JUCE_ATOMICS_GCC | |||
| return (Type) __sync_add_and_fetch (&value, amountToAdd); | |||
| #endif | |||
| } | |||
| template <typename Type> | |||
| inline Type Atomic<Type>::operator-= (const Type amountToSubtract) noexcept | |||
| { | |||
| return operator+= (negateValue (amountToSubtract)); | |||
| } | |||
| template <typename Type> | |||
| inline Type Atomic<Type>::operator++() noexcept | |||
| { | |||
| #if JUCE_ATOMICS_MAC | |||
| return sizeof (Type) == 4 ? (Type) OSAtomicIncrement32Barrier ((JUCE_MAC_ATOMICS_VOLATILE int32_t*) &value) | |||
| : (Type) OSAtomicIncrement64Barrier ((JUCE_MAC_ATOMICS_VOLATILE int64_t*) &value); | |||
| #elif JUCE_ATOMICS_WINDOWS | |||
| return sizeof (Type) == 4 ? (Type) juce_InterlockedIncrement ((volatile long*) &value) | |||
| : (Type) juce_InterlockedIncrement64 ((volatile __int64*) &value); | |||
| #elif JUCE_ATOMICS_GCC | |||
| return (Type) __sync_add_and_fetch (&value, 1); | |||
| #endif | |||
| } | |||
| template <typename Type> | |||
| inline Type Atomic<Type>::operator--() noexcept | |||
| { | |||
| #if JUCE_ATOMICS_MAC | |||
| return sizeof (Type) == 4 ? (Type) OSAtomicDecrement32Barrier ((JUCE_MAC_ATOMICS_VOLATILE int32_t*) &value) | |||
| : (Type) OSAtomicDecrement64Barrier ((JUCE_MAC_ATOMICS_VOLATILE int64_t*) &value); | |||
| #elif JUCE_ATOMICS_WINDOWS | |||
| return sizeof (Type) == 4 ? (Type) juce_InterlockedDecrement ((volatile long*) &value) | |||
| : (Type) juce_InterlockedDecrement64 ((volatile __int64*) &value); | |||
| #elif JUCE_ATOMICS_GCC | |||
| return (Type) __sync_add_and_fetch (&value, -1); | |||
| #endif | |||
| } | |||
| template <typename Type> | |||
| inline bool Atomic<Type>::compareAndSetBool (const Type newValue, const Type valueToCompare) noexcept | |||
| { | |||
| #if JUCE_ATOMICS_MAC | |||
| return sizeof (Type) == 4 ? OSAtomicCompareAndSwap32Barrier ((int32_t) castTo32Bit (valueToCompare), (int32_t) castTo32Bit (newValue), (JUCE_MAC_ATOMICS_VOLATILE int32_t*) &value) | |||
| : OSAtomicCompareAndSwap64Barrier ((int64_t) castTo64Bit (valueToCompare), (int64_t) castTo64Bit (newValue), (JUCE_MAC_ATOMICS_VOLATILE int64_t*) &value); | |||
| #elif JUCE_ATOMICS_WINDOWS | |||
| return compareAndSetValue (newValue, valueToCompare) == valueToCompare; | |||
| #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 Atomic<Type>::compareAndSetValue (const Type newValue, const Type valueToCompare) noexcept | |||
| { | |||
| #if JUCE_ATOMICS_MAC | |||
| 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_WINDOWS | |||
| return sizeof (Type) == 4 ? castFrom32Bit ((int32) juce_InterlockedCompareExchange ((volatile long*) &value, (long) castTo32Bit (newValue), (long) castTo32Bit (valueToCompare))) | |||
| : castFrom64Bit ((int64) juce_InterlockedCompareExchange64 ((volatile __int64*) &value, (__int64) castTo64Bit (newValue), (__int64) castTo64Bit (valueToCompare))); | |||
| #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 Atomic<Type>::memoryBarrier() noexcept | |||
| { | |||
| #if JUCE_ATOMICS_MAC | |||
| OSMemoryBarrier(); | |||
| #elif JUCE_ATOMICS_GCC | |||
| __sync_synchronize(); | |||
| #elif JUCE_ATOMICS_WINDOWS | |||
| juce_MemoryBarrier(); | |||
| #endif | |||
| } | |||
| #if JUCE_MSVC | |||
| #pragma warning (pop) | |||
| #endif | |||
| #endif // JUCE_ATOMIC_H_INCLUDED | |||
| @@ -0,0 +1,186 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the juce_core module of the JUCE library. | |||
| Copyright (c) 2013 - Raw Material Software Ltd. | |||
| 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 THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||
| TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||
| NO EVENT SHALL THE AUTHOR 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. | |||
| ------------------------------------------------------------------------------ | |||
| NOTE! This permissive ISC license applies ONLY to files within the juce_core module! | |||
| All other JUCE modules are covered by a dual GPL/commercial license, so if you are | |||
| using any other modules, be sure to check that you also comply with their license. | |||
| For more details, visit www.juce.com | |||
| ============================================================================== | |||
| */ | |||
| #ifndef JUCE_BYTEORDER_H_INCLUDED | |||
| #define JUCE_BYTEORDER_H_INCLUDED | |||
| //============================================================================== | |||
| /** 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. */ | |||
| static uint16 swap (uint16 value); | |||
| /** Reverses the order of the 4 bytes in a 32-bit integer. */ | |||
| static uint32 swap (uint32 value); | |||
| /** Reverses the order of the 8 bytes in a 64-bit integer. */ | |||
| static uint64 swap (uint64 value); | |||
| //============================================================================== | |||
| /** Swaps the byte order of a 16-bit int if the CPU is big-endian */ | |||
| static uint16 swapIfBigEndian (uint16 value); | |||
| /** Swaps the byte order of a 32-bit int if the CPU is big-endian */ | |||
| static uint32 swapIfBigEndian (uint32 value); | |||
| /** Swaps the byte order of a 64-bit int if the CPU is big-endian */ | |||
| static uint64 swapIfBigEndian (uint64 value); | |||
| /** Swaps the byte order of a 16-bit int if the CPU is little-endian */ | |||
| static uint16 swapIfLittleEndian (uint16 value); | |||
| /** Swaps the byte order of a 32-bit int if the CPU is little-endian */ | |||
| static uint32 swapIfLittleEndian (uint32 value); | |||
| /** Swaps the byte order of a 64-bit int if the CPU is little-endian */ | |||
| static uint64 swapIfLittleEndian (uint64 value); | |||
| //============================================================================== | |||
| /** Turns 4 bytes into a little-endian integer. */ | |||
| static uint32 littleEndianInt (const void* bytes); | |||
| /** Turns 2 bytes into a little-endian integer. */ | |||
| static uint16 littleEndianShort (const void* bytes); | |||
| /** Turns 4 bytes into a big-endian integer. */ | |||
| static uint32 bigEndianInt (const void* bytes); | |||
| /** Turns 2 bytes into a big-endian integer. */ | |||
| static uint16 bigEndianShort (const void* bytes); | |||
| //============================================================================== | |||
| /** Converts 3 little-endian bytes into a signed 24-bit value (which is sign-extended to 32 bits). */ | |||
| static int littleEndian24Bit (const char* bytes); | |||
| /** Converts 3 big-endian bytes into a signed 24-bit value (which is sign-extended to 32 bits). */ | |||
| static int bigEndian24Bit (const char* bytes); | |||
| /** Copies a 24-bit number to 3 little-endian bytes. */ | |||
| static void littleEndian24BitToChars (int value, char* destBytes); | |||
| /** Copies a 24-bit number to 3 big-endian bytes. */ | |||
| static void bigEndian24BitToChars (int value, char* destBytes); | |||
| //============================================================================== | |||
| /** Returns true if the current CPU is big-endian. */ | |||
| static bool isBigEndian(); | |||
| private: | |||
| ByteOrder(); | |||
| JUCE_DECLARE_NON_COPYABLE (ByteOrder) | |||
| }; | |||
| //============================================================================== | |||
| #if JUCE_USE_INTRINSICS && ! defined (__INTEL_COMPILER) | |||
| #pragma intrinsic (_byteswap_ulong) | |||
| #endif | |||
| inline uint16 ByteOrder::swap (uint16 n) | |||
| { | |||
| #if JUCE_USE_INTRINSICSxxx // agh - the MS compiler has an internal error when you try to use this intrinsic! | |||
| return static_cast <uint16> (_byteswap_ushort (n)); | |||
| #else | |||
| return static_cast <uint16> ((n << 8) | (n >> 8)); | |||
| #endif | |||
| } | |||
| inline uint32 ByteOrder::swap (uint32 n) | |||
| { | |||
| #if JUCE_MAC || JUCE_IOS | |||
| return OSSwapInt32 (n); | |||
| #elif JUCE_GCC && JUCE_INTEL && ! JUCE_NO_INLINE_ASM | |||
| asm("bswap %%eax" : "=a"(n) : "a"(n)); | |||
| return n; | |||
| #elif JUCE_USE_INTRINSICS | |||
| return _byteswap_ulong (n); | |||
| #elif JUCE_MSVC && ! JUCE_NO_INLINE_ASM | |||
| __asm { | |||
| mov eax, n | |||
| bswap eax | |||
| mov n, eax | |||
| } | |||
| return 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) | |||
| { | |||
| #if JUCE_MAC || JUCE_IOS | |||
| return OSSwapInt64 (value); | |||
| #elif JUCE_USE_INTRINSICS | |||
| return _byteswap_uint64 (value); | |||
| #else | |||
| return (((int64) swap ((uint32) value)) << 32) | swap ((uint32) (value >> 32)); | |||
| #endif | |||
| } | |||
| #if JUCE_LITTLE_ENDIAN | |||
| inline uint16 ByteOrder::swapIfBigEndian (const uint16 v) { return v; } | |||
| inline uint32 ByteOrder::swapIfBigEndian (const uint32 v) { return v; } | |||
| inline uint64 ByteOrder::swapIfBigEndian (const uint64 v) { return v; } | |||
| inline uint16 ByteOrder::swapIfLittleEndian (const uint16 v) { return swap (v); } | |||
| inline uint32 ByteOrder::swapIfLittleEndian (const uint32 v) { return swap (v); } | |||
| inline uint64 ByteOrder::swapIfLittleEndian (const uint64 v) { return swap (v); } | |||
| inline uint32 ByteOrder::littleEndianInt (const void* const bytes) { return *static_cast <const uint32*> (bytes); } | |||
| inline uint16 ByteOrder::littleEndianShort (const void* const bytes) { return *static_cast <const uint16*> (bytes); } | |||
| inline uint32 ByteOrder::bigEndianInt (const void* const bytes) { return swap (*static_cast <const uint32*> (bytes)); } | |||
| inline uint16 ByteOrder::bigEndianShort (const void* const bytes) { return swap (*static_cast <const uint16*> (bytes)); } | |||
| inline bool ByteOrder::isBigEndian() { return false; } | |||
| #else | |||
| inline uint16 ByteOrder::swapIfBigEndian (const uint16 v) { return swap (v); } | |||
| inline uint32 ByteOrder::swapIfBigEndian (const uint32 v) { return swap (v); } | |||
| inline uint64 ByteOrder::swapIfBigEndian (const uint64 v) { return swap (v); } | |||
| inline uint16 ByteOrder::swapIfLittleEndian (const uint16 v) { return v; } | |||
| inline uint32 ByteOrder::swapIfLittleEndian (const uint32 v) { return v; } | |||
| inline uint64 ByteOrder::swapIfLittleEndian (const uint64 v) { return v; } | |||
| inline uint32 ByteOrder::littleEndianInt (const void* const bytes) { return swap (*static_cast <const uint32*> (bytes)); } | |||
| inline uint16 ByteOrder::littleEndianShort (const void* const bytes) { return swap (*static_cast <const uint16*> (bytes)); } | |||
| inline uint32 ByteOrder::bigEndianInt (const void* const bytes) { return *static_cast <const uint32*> (bytes); } | |||
| inline uint16 ByteOrder::bigEndianShort (const void* const bytes) { return *static_cast <const uint16*> (bytes); } | |||
| inline bool ByteOrder::isBigEndian() { return true; } | |||
| #endif | |||
| inline int ByteOrder::littleEndian24Bit (const char* const bytes) { return (((int) bytes[2]) << 16) | (((int) (uint8) bytes[1]) << 8) | ((int) (uint8) bytes[0]); } | |||
| inline int ByteOrder::bigEndian24Bit (const char* const bytes) { return (((int) bytes[0]) << 16) | (((int) (uint8) bytes[1]) << 8) | ((int) (uint8) bytes[2]); } | |||
| inline void ByteOrder::littleEndian24BitToChars (const int value, char* const destBytes) { destBytes[0] = (char)(value & 0xff); destBytes[1] = (char)((value >> 8) & 0xff); destBytes[2] = (char)((value >> 16) & 0xff); } | |||
| inline void ByteOrder::bigEndian24BitToChars (const int value, char* const destBytes) { destBytes[0] = (char)((value >> 16) & 0xff); destBytes[1] = (char)((value >> 8) & 0xff); destBytes[2] = (char)(value & 0xff); } | |||
| #endif // JUCE_BYTEORDER_H_INCLUDED | |||
| @@ -0,0 +1,308 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the juce_core module of the JUCE library. | |||
| Copyright (c) 2013 - Raw Material Software Ltd. | |||
| 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 THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||
| TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||
| NO EVENT SHALL THE AUTHOR 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. | |||
| ------------------------------------------------------------------------------ | |||
| NOTE! This permissive ISC license applies ONLY to files within the juce_core module! | |||
| All other JUCE modules are covered by a dual GPL/commercial license, so if you are | |||
| using any other modules, be sure to check that you also comply with their license. | |||
| For more details, visit www.juce.com | |||
| ============================================================================== | |||
| */ | |||
| #ifndef JUCE_HEAPBLOCK_H_INCLUDED | |||
| #define JUCE_HEAPBLOCK_H_INCLUDED | |||
| #ifndef DOXYGEN | |||
| namespace HeapBlockHelper | |||
| { | |||
| template <bool shouldThrow> | |||
| struct ThrowOnFail { static void check (void*) {} }; | |||
| template<> | |||
| struct ThrowOnFail <true> { static void check (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); | |||
| } | |||
| #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS | |||
| HeapBlock (HeapBlock&& other) noexcept | |||
| : data (other.data) | |||
| { | |||
| other.data = nullptr; | |||
| } | |||
| HeapBlock& operator= (HeapBlock&& other) noexcept | |||
| { | |||
| std::swap (data, other.data); | |||
| return *this; | |||
| } | |||
| #endif | |||
| //============================================================================== | |||
| /** 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* 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() | |||
| { | |||
| 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 | |||
| { | |||
| HeapBlockHelper::ThrowOnFail<throwOnFailure>::check (data); | |||
| } | |||
| #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 | |||
| }; | |||
| #endif // JUCE_HEAPBLOCK_H_INCLUDED | |||
| @@ -0,0 +1,149 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the juce_core module of the JUCE library. | |||
| Copyright (c) 2013 - Raw Material Software Ltd. | |||
| 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 THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||
| TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||
| NO EVENT SHALL THE AUTHOR 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. | |||
| ------------------------------------------------------------------------------ | |||
| NOTE! This permissive ISC license applies ONLY to files within the juce_core module! | |||
| All other JUCE modules are covered by a dual GPL/commercial license, so if you are | |||
| using any other modules, be sure to check that you also comply with their license. | |||
| For more details, visit www.juce.com | |||
| ============================================================================== | |||
| */ | |||
| #ifndef JUCE_LEAKEDOBJECTDETECTOR_H_INCLUDED | |||
| #define JUCE_LEAKEDOBJECTDETECTOR_H_INCLUDED | |||
| #include "../text/juce_String.h" | |||
| #include "juce_Atomic.h" | |||
| //============================================================================== | |||
| /** | |||
| 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 | |||
| #endif // JUCE_LEAKEDOBJECTDETECTOR_H_INCLUDED | |||
| @@ -0,0 +1,126 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the juce_core module of the JUCE library. | |||
| Copyright (c) 2013 - Raw Material Software Ltd. | |||
| 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 THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||
| TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||
| NO EVENT SHALL THE AUTHOR 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. | |||
| ------------------------------------------------------------------------------ | |||
| NOTE! This permissive ISC license applies ONLY to files within the juce_core module! | |||
| All other JUCE modules are covered by a dual GPL/commercial license, so if you are | |||
| using any other modules, be sure to check that you also comply with their license. | |||
| For more details, visit www.juce.com | |||
| ============================================================================== | |||
| */ | |||
| #ifndef JUCE_MEMORY_H_INCLUDED | |||
| #define JUCE_MEMORY_H_INCLUDED | |||
| //============================================================================== | |||
| /** 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* pointer, IntegerType bytes) noexcept { return (Type*) (((char*) pointer) + bytes); } | |||
| /** 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* pointer) { return pointer != nullptr ? new Type (*pointer) : nullptr; } | |||
| //============================================================================== | |||
| #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 | |||
| #endif // JUCE_MEMORY_H_INCLUDED | |||
| @@ -0,0 +1,421 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the juce_core module of the JUCE library. | |||
| Copyright (c) 2013 - Raw Material Software Ltd. | |||
| 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 THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||
| TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||
| NO EVENT SHALL THE AUTHOR 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. | |||
| ------------------------------------------------------------------------------ | |||
| NOTE! This permissive ISC license applies ONLY to files within the juce_core module! | |||
| All other JUCE modules are covered by a dual GPL/commercial license, so if you are | |||
| using any other modules, be sure to check that you also comply with their license. | |||
| For more details, visit www.juce.com | |||
| ============================================================================== | |||
| */ | |||
| 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; | |||
| } | |||
| #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS | |||
| MemoryBlock::MemoryBlock (MemoryBlock&& other) noexcept | |||
| : data (static_cast <HeapBlock <char>&&> (other.data)), | |||
| size (other.size) | |||
| { | |||
| } | |||
| MemoryBlock& MemoryBlock::operator= (MemoryBlock&& other) noexcept | |||
| { | |||
| data = static_cast <HeapBlock <char>&&> (other.data); | |||
| size = other.size; | |||
| return *this; | |||
| } | |||
| #endif | |||
| //============================================================================== | |||
| 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) | |||
| { | |||
| data.free(); | |||
| size = 0; | |||
| } | |||
| 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::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 -= offset; | |||
| offset = 0; | |||
| } | |||
| if (offset + num > size) | |||
| num = size - 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 += offset; | |||
| offset = 0; | |||
| } | |||
| if (offset + num > size) | |||
| { | |||
| const size_t newNum = size - offset; | |||
| zeromem (d + newNum, num - newNum); | |||
| num = newNum; | |||
| } | |||
| if (num > 0) | |||
| memcpy (d, data + offset, num); | |||
| } | |||
| String MemoryBlock::toString() const | |||
| { | |||
| return String (CharPointer_UTF8 (data), size); | |||
| } | |||
| //============================================================================== | |||
| int MemoryBlock::getBitRange (const size_t bitRangeStart, size_t numBits) const noexcept | |||
| { | |||
| int res = 0; | |||
| size_t byte = bitRangeStart >> 3; | |||
| int offsetInByte = (int) bitRangeStart & 7; | |||
| size_t bitsSoFar = 0; | |||
| while (numBits > 0 && (size_t) byte < size) | |||
| { | |||
| const int bitsThisTime = jmin ((int) 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; | |||
| int offsetInByte = (int) bitRangeStart & 7; | |||
| unsigned int mask = ~((((unsigned int) 0xffffffff) << (32 - numBits)) >> (32 - numBits)); | |||
| while (numBits > 0 && (size_t) byte < size) | |||
| { | |||
| const int bitsThisTime = jmin ((int) numBits, 8 - offsetInByte); | |||
| const unsigned int tempMask = (mask << offsetInByte) | ~((((unsigned int) 0xffffffff) >> offsetInByte) << offsetInByte); | |||
| const unsigned int tempBits = (unsigned int) bitsToSet << offsetInByte; | |||
| data[byte] = (char) ((data[byte] & tempMask) | tempBits); | |||
| ++byte; | |||
| numBits -= bitsThisTime; | |||
| bitsToSet >>= bitsThisTime; | |||
| mask >>= bitsThisTime; | |||
| offsetInByte = 0; | |||
| } | |||
| } | |||
| //============================================================================== | |||
| void MemoryBlock::loadFromHexString (const String& hex) | |||
| { | |||
| ensureSize ((size_t) hex.length() >> 1); | |||
| char* dest = data; | |||
| String::CharPointerType t (hex.getCharPointer()); | |||
| 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; | |||
| } | |||
| else if (c >= 'a' && c <= 'z') | |||
| { | |||
| byte |= c - ('a' - 10); | |||
| break; | |||
| } | |||
| else if (c >= 'A' && c <= 'Z') | |||
| { | |||
| byte |= c - ('A' - 10); | |||
| break; | |||
| } | |||
| else if (c == 0) | |||
| { | |||
| setSize (static_cast <size_t> (dest - data)); | |||
| return; | |||
| } | |||
| } | |||
| } | |||
| *dest++ = (char) byte; | |||
| } | |||
| } | |||
| //============================================================================== | |||
| const char* const MemoryBlock::encodingTable = ".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) encodingTable [getBitRange (i * 6, 6)]); | |||
| d.writeNull(); | |||
| return destString; | |||
| } | |||
| bool MemoryBlock::fromBase64Encoding (const String& s) | |||
| { | |||
| const int startPos = s.indexOfChar ('.') + 1; | |||
| if (startPos <= 0) | |||
| return false; | |||
| const int numBytesNeeded = s.substring (0, startPos - 1).getIntValue(); | |||
| setSize ((size_t) numBytesNeeded, true); | |||
| const int numChars = s.length() - startPos; | |||
| String::CharPointerType srcChars (s.getCharPointer()); | |||
| srcChars += startPos; | |||
| int pos = 0; | |||
| for (int i = 0; i < numChars; ++i) | |||
| { | |||
| const char c = (char) srcChars.getAndAdvance(); | |||
| for (int j = 0; j < 64; ++j) | |||
| { | |||
| if (encodingTable[j] == c) | |||
| { | |||
| setBitRange ((size_t) pos, 6, j); | |||
| pos += 6; | |||
| break; | |||
| } | |||
| } | |||
| } | |||
| return true; | |||
| } | |||
| @@ -0,0 +1,260 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the juce_core module of the JUCE library. | |||
| Copyright (c) 2013 - Raw Material Software Ltd. | |||
| 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 THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||
| TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||
| NO EVENT SHALL THE AUTHOR 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. | |||
| ------------------------------------------------------------------------------ | |||
| NOTE! This permissive ISC license applies ONLY to files within the juce_core module! | |||
| All other JUCE modules are covered by a dual GPL/commercial license, so if you are | |||
| using any other modules, be sure to check that you also comply with their license. | |||
| For more details, visit www.juce.com | |||
| ============================================================================== | |||
| */ | |||
| #ifndef JUCE_MEMORYBLOCK_H_INCLUDED | |||
| #define JUCE_MEMORYBLOCK_H_INCLUDED | |||
| #include "../text/juce_String.h" | |||
| #include "../memory/juce_HeapBlock.h" | |||
| //============================================================================== | |||
| /** | |||
| 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& other); | |||
| /** 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& other); | |||
| #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS | |||
| MemoryBlock (MemoryBlock&& other) noexcept; | |||
| MemoryBlock& operator= (MemoryBlock&& other) noexcept; | |||
| #endif | |||
| //============================================================================== | |||
| /** 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 the block's current allocated size, in bytes. */ | |||
| size_t getSize() const noexcept { return size; } | |||
| /** Resizes the memory block. | |||
| This will try to keep as much of the block's current content as it can, | |||
| and can optionally be made to clear any new space that gets allocated at | |||
| the end of the block. | |||
| @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); | |||
| //============================================================================== | |||
| /** 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 (const String& 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 that represent the binary contents of this block. | |||
| Uses a 64-bit encoding system to allow binary data to be turned into a string | |||
| of simple non-extended characters, e.g. for storage in XML. | |||
| @see fromBase64Encoding | |||
| */ | |||
| String toBase64Encoding() const; | |||
| /** Takes a string of encoded characters and turns it into binary data. | |||
| The string passed in must have been created by to64BitEncoding(), and this | |||
| block will be resized to recreate the original data block. | |||
| @see toBase64Encoding | |||
| */ | |||
| bool fromBase64Encoding (const String& encodedString); | |||
| private: | |||
| //============================================================================== | |||
| HeapBlock <char> data; | |||
| size_t size; | |||
| static const char* const encodingTable; | |||
| JUCE_LEAK_DETECTOR (MemoryBlock) | |||
| }; | |||
| #endif // JUCE_MEMORYBLOCK_H_INCLUDED | |||
| @@ -0,0 +1,188 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the juce_core module of the JUCE library. | |||
| Copyright (c) 2013 - Raw Material Software Ltd. | |||
| 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 THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||
| TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||
| NO EVENT SHALL THE AUTHOR 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. | |||
| ------------------------------------------------------------------------------ | |||
| NOTE! This permissive ISC license applies ONLY to files within the juce_core module! | |||
| All other JUCE modules are covered by a dual GPL/commercial license, so if you are | |||
| using any other modules, be sure to check that you also comply with their license. | |||
| For more details, visit www.juce.com | |||
| ============================================================================== | |||
| */ | |||
| #ifndef JUCE_OPTIONALSCOPEDPOINTER_H_INCLUDED | |||
| #define JUCE_OPTIONALSCOPEDPOINTER_H_INCLUDED | |||
| #include "juce_ScopedPointer.h" | |||
| //============================================================================== | |||
| /** | |||
| 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; | |||
| }; | |||
| #endif // JUCE_OPTIONALSCOPEDPOINTER_H_INCLUDED | |||
| @@ -0,0 +1,400 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the juce_core module of the JUCE library. | |||
| Copyright (c) 2013 - Raw Material Software Ltd. | |||
| 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 THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||
| TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||
| NO EVENT SHALL THE AUTHOR 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. | |||
| ------------------------------------------------------------------------------ | |||
| NOTE! This permissive ISC license applies ONLY to files within the juce_core module! | |||
| All other JUCE modules are covered by a dual GPL/commercial license, so if you are | |||
| using any other modules, be sure to check that you also comply with their license. | |||
| For more details, visit www.juce.com | |||
| ============================================================================== | |||
| */ | |||
| #ifndef JUCE_REFERENCECOUNTEDOBJECT_H_INCLUDED | |||
| #define JUCE_REFERENCECOUNTEDOBJECT_H_INCLUDED | |||
| #include "juce_Atomic.h" | |||
| //============================================================================== | |||
| /** | |||
| Adds reference-counting to an object. | |||
| 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. | |||
| */ | |||
| inline void incReferenceCount() noexcept | |||
| { | |||
| ++refCount; | |||
| } | |||
| /** Decreases the object's reference count. | |||
| If the count gets to zero, the object will be deleted. | |||
| */ | |||
| inline void decReferenceCount() noexcept | |||
| { | |||
| jassert (getReferenceCount() > 0); | |||
| if (--refCount == 0) | |||
| delete this; | |||
| } | |||
| /** Returns the object's current reference count. */ | |||
| inline 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; | |||
| 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. | |||
| */ | |||
| inline void incReferenceCount() noexcept | |||
| { | |||
| ++refCount; | |||
| } | |||
| /** Decreases the object's reference count. | |||
| If the count gets to zero, the object will be deleted. | |||
| */ | |||
| inline void decReferenceCount() noexcept | |||
| { | |||
| jassert (getReferenceCount() > 0); | |||
| if (--refCount == 0) | |||
| delete this; | |||
| } | |||
| /** Returns the object's current reference count. */ | |||
| inline 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; | |||
| 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, | |||
| but if you need to, you could roll your own reference-countable class by implementing a pair of | |||
| mathods called incReferenceCount() and decReferenceCount(). | |||
| When using this class, you'll probably want to create a typedef to abbreviate the full | |||
| templated name - e.g. | |||
| @code typedef ReferenceCountedObjectPtr<MyClass> MyClassPtr;@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. */ | |||
| inline ReferenceCountedObjectPtr() noexcept | |||
| : referencedObject (nullptr) | |||
| { | |||
| } | |||
| /** Creates a pointer to an object. | |||
| This will increment the object's reference-count if it is non-null. | |||
| */ | |||
| inline ReferenceCountedObjectPtr (ReferenceCountedObjectClass* const refCountedObject) noexcept | |||
| : referencedObject (refCountedObject) | |||
| { | |||
| if (refCountedObject != nullptr) | |||
| refCountedObject->incReferenceCount(); | |||
| } | |||
| /** Copies another pointer. | |||
| This will increment the object's reference-count (if it is non-null). | |||
| */ | |||
| inline ReferenceCountedObjectPtr (const ReferenceCountedObjectPtr& other) noexcept | |||
| : referencedObject (other.referencedObject) | |||
| { | |||
| if (referencedObject != nullptr) | |||
| referencedObject->incReferenceCount(); | |||
| } | |||
| #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS | |||
| /** Takes-over the object from another pointer. */ | |||
| inline ReferenceCountedObjectPtr (ReferenceCountedObjectPtr&& other) noexcept | |||
| : referencedObject (other.referencedObject) | |||
| { | |||
| other.referencedObject = nullptr; | |||
| } | |||
| #endif | |||
| /** Copies another pointer. | |||
| This will increment the object's reference-count (if it is non-null). | |||
| */ | |||
| template <class DerivedClass> | |||
| inline ReferenceCountedObjectPtr (const ReferenceCountedObjectPtr<DerivedClass>& other) noexcept | |||
| : referencedObject (static_cast <ReferenceCountedObjectClass*> (other.get())) | |||
| { | |||
| if (referencedObject != nullptr) | |||
| referencedObject->incReferenceCount(); | |||
| } | |||
| /** 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 <class DerivedClass> | |||
| ReferenceCountedObjectPtr& operator= (const ReferenceCountedObjectPtr<DerivedClass>& other) | |||
| { | |||
| return operator= (static_cast <ReferenceCountedObjectClass*> (other.get())); | |||
| } | |||
| #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS | |||
| /** Takes-over the object from another pointer. */ | |||
| ReferenceCountedObjectPtr& operator= (ReferenceCountedObjectPtr&& other) | |||
| { | |||
| std::swap (referencedObject, other.referencedObject); | |||
| return *this; | |||
| } | |||
| #endif | |||
| /** 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= (ReferenceCountedObjectClass* const newObject) | |||
| { | |||
| if (referencedObject != newObject) | |||
| { | |||
| if (newObject != nullptr) | |||
| newObject->incReferenceCount(); | |||
| ReferenceCountedObjectClass* const oldObject = referencedObject; | |||
| referencedObject = newObject; | |||
| if (oldObject != nullptr) | |||
| oldObject->decReferenceCount(); | |||
| } | |||
| return *this; | |||
| } | |||
| /** Destructor. | |||
| This will decrement the object's reference-count, and may delete it if it | |||
| gets to zero. | |||
| */ | |||
| inline ~ReferenceCountedObjectPtr() | |||
| { | |||
| if (referencedObject != nullptr) | |||
| referencedObject->decReferenceCount(); | |||
| } | |||
| /** Returns the object that this pointer references. | |||
| The pointer returned may be zero, of course. | |||
| */ | |||
| inline operator ReferenceCountedObjectClass*() const noexcept | |||
| { | |||
| return referencedObject; | |||
| } | |||
| // the -> operator is called on the referenced object | |||
| inline ReferenceCountedObjectClass* operator->() const noexcept | |||
| { | |||
| return referencedObject; | |||
| } | |||
| /** Returns the object that this pointer references. | |||
| The pointer returned may be zero, of course. | |||
| */ | |||
| inline ReferenceCountedObjectClass* get() const noexcept | |||
| { | |||
| return referencedObject; | |||
| } | |||
| /** Returns the object that this pointer references. | |||
| The pointer returned may be zero, of course. | |||
| */ | |||
| inline ReferenceCountedObjectClass* getObject() const noexcept | |||
| { | |||
| return referencedObject; | |||
| } | |||
| private: | |||
| //============================================================================== | |||
| ReferenceCountedObjectClass* referencedObject; | |||
| }; | |||
| /** Compares two ReferenceCountedObjectPointers. */ | |||
| template <class ReferenceCountedObjectClass> | |||
| bool operator== (const ReferenceCountedObjectPtr<ReferenceCountedObjectClass>& object1, ReferenceCountedObjectClass* const object2) noexcept | |||
| { | |||
| return object1.get() == object2; | |||
| } | |||
| /** Compares two ReferenceCountedObjectPointers. */ | |||
| template <class ReferenceCountedObjectClass> | |||
| bool operator== (const ReferenceCountedObjectPtr<ReferenceCountedObjectClass>& object1, const ReferenceCountedObjectPtr<ReferenceCountedObjectClass>& object2) noexcept | |||
| { | |||
| return object1.get() == object2.get(); | |||
| } | |||
| /** Compares two ReferenceCountedObjectPointers. */ | |||
| template <class ReferenceCountedObjectClass> | |||
| bool operator== (ReferenceCountedObjectClass* object1, ReferenceCountedObjectPtr<ReferenceCountedObjectClass>& object2) noexcept | |||
| { | |||
| return object1 == object2.get(); | |||
| } | |||
| /** Compares two ReferenceCountedObjectPointers. */ | |||
| template <class ReferenceCountedObjectClass> | |||
| bool operator!= (const ReferenceCountedObjectPtr<ReferenceCountedObjectClass>& object1, const ReferenceCountedObjectClass* object2) noexcept | |||
| { | |||
| return object1.get() != object2; | |||
| } | |||
| /** Compares two ReferenceCountedObjectPointers. */ | |||
| template <class ReferenceCountedObjectClass> | |||
| bool operator!= (const ReferenceCountedObjectPtr<ReferenceCountedObjectClass>& object1, ReferenceCountedObjectPtr<ReferenceCountedObjectClass>& object2) noexcept | |||
| { | |||
| return object1.get() != object2.get(); | |||
| } | |||
| /** Compares two ReferenceCountedObjectPointers. */ | |||
| template <class ReferenceCountedObjectClass> | |||
| bool operator!= (ReferenceCountedObjectClass* object1, ReferenceCountedObjectPtr<ReferenceCountedObjectClass>& object2) noexcept | |||
| { | |||
| return object1 != object2.get(); | |||
| } | |||
| #endif // JUCE_REFERENCECOUNTEDOBJECT_H_INCLUDED | |||
| @@ -0,0 +1,253 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the juce_core module of the JUCE library. | |||
| Copyright (c) 2013 - Raw Material Software Ltd. | |||
| 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 THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||
| TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||
| NO EVENT SHALL THE AUTHOR 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. | |||
| ------------------------------------------------------------------------------ | |||
| NOTE! This permissive ISC license applies ONLY to files within the juce_core module! | |||
| All other JUCE modules are covered by a dual GPL/commercial license, so if you are | |||
| using any other modules, be sure to check that you also comply with their license. | |||
| For more details, visit www.juce.com | |||
| ============================================================================== | |||
| */ | |||
| #ifndef JUCE_SCOPEDPOINTER_H_INCLUDED | |||
| #define JUCE_SCOPEDPOINTER_H_INCLUDED | |||
| //============================================================================== | |||
| /** | |||
| 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, wheras 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 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() { delete 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; | |||
| delete 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; | |||
| delete oldObject; | |||
| } | |||
| return *this; | |||
| } | |||
| #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS | |||
| ScopedPointer (ScopedPointer&& other) noexcept | |||
| : object (other.object) | |||
| { | |||
| other.object = nullptr; | |||
| } | |||
| ScopedPointer& operator= (ScopedPointer&& other) noexcept | |||
| { | |||
| object = other.object; | |||
| other.object = nullptr; | |||
| return *this; | |||
| } | |||
| #endif | |||
| //============================================================================== | |||
| /** 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()); | |||
| 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_jassert (sizeof (Type) == 12345); } | |||
| #endif | |||
| #endif // JUCE_SCOPEDPOINTER_H_INCLUDED | |||
| @@ -0,0 +1,292 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the juce_core module of the JUCE library. | |||
| Copyright (c) 2013 - Raw Material Software Ltd. | |||
| 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 THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||
| TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||
| NO EVENT SHALL THE AUTHOR 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. | |||
| ------------------------------------------------------------------------------ | |||
| NOTE! This permissive ISC license applies ONLY to files within the juce_core module! | |||
| All other JUCE modules are covered by a dual GPL/commercial license, so if you are | |||
| using any other modules, be sure to check that you also comply with their license. | |||
| For more details, visit www.juce.com | |||
| ============================================================================== | |||
| */ | |||
| #ifndef JUCE_SINGLETON_H_INCLUDED | |||
| #define JUCE_SINGLETON_H_INCLUDED | |||
| //============================================================================== | |||
| /** | |||
| 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 0 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; | |||
| #endif // JUCE_SINGLETON_H_INCLUDED | |||
| @@ -0,0 +1,214 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the juce_core module of the JUCE library. | |||
| Copyright (c) 2013 - Raw Material Software Ltd. | |||
| 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 THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||
| TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||
| NO EVENT SHALL THE AUTHOR 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. | |||
| ------------------------------------------------------------------------------ | |||
| NOTE! This permissive ISC license applies ONLY to files within the juce_core module! | |||
| All other JUCE modules are covered by a dual GPL/commercial license, so if you are | |||
| using any other modules, be sure to check that you also comply with their license. | |||
| For more details, visit www.juce.com | |||
| ============================================================================== | |||
| */ | |||
| #ifndef JUCE_WEAKREFERENCE_H_INCLUDED | |||
| #define JUCE_WEAKREFERENCE_H_INCLUDED | |||
| #include "juce_ReferenceCountedObject.h" | |||
| //============================================================================== | |||
| /** | |||
| 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>; | |||
| }; | |||
| // 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) {} | |||
| /** 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; } | |||
| #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS | |||
| WeakReference (WeakReference&& other) noexcept : holder (static_cast <SharedRef&&> (other.holder)) {} | |||
| WeakReference& operator= (WeakReference&& other) noexcept { holder = static_cast <SharedRef&&> (other.holder); return *this; } | |||
| #endif | |||
| /** 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() | |||
| { | |||
| // 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() | |||
| { | |||
| 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; | |||
| } | |||
| }; | |||
| #endif // JUCE_WEAKREFERENCE_H_INCLUDED | |||
| @@ -0,0 +1,83 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the juce_core module of the JUCE library. | |||
| Copyright (c) 2013 - Raw Material Software Ltd. | |||
| 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 THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||
| TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||
| NO EVENT SHALL THE AUTHOR 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. | |||
| ------------------------------------------------------------------------------ | |||
| NOTE! This permissive ISC license applies ONLY to files within the juce_core module! | |||
| All other JUCE modules are covered by a dual GPL/commercial license, so if you are | |||
| using any other modules, be sure to check that you also comply with their license. | |||
| For more details, visit www.juce.com | |||
| ============================================================================== | |||
| */ | |||
| 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; | |||
| } | |||
| #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS | |||
| 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; | |||
| } | |||
| #endif | |||
| 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(); } | |||
| @@ -0,0 +1,127 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the juce_core module of the JUCE library. | |||
| Copyright (c) 2013 - Raw Material Software Ltd. | |||
| 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 THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||
| TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||
| NO EVENT SHALL THE AUTHOR 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. | |||
| ------------------------------------------------------------------------------ | |||
| NOTE! This permissive ISC license applies ONLY to files within the juce_core module! | |||
| All other JUCE modules are covered by a dual GPL/commercial license, so if you are | |||
| using any other modules, be sure to check that you also comply with their license. | |||
| For more details, visit www.juce.com | |||
| ============================================================================== | |||
| */ | |||
| #ifndef JUCE_RESULT_H_INCLUDED | |||
| #define JUCE_RESULT_H_INCLUDED | |||
| #include "../text/juce_String.h" | |||
| //============================================================================== | |||
| /** | |||
| 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&); | |||
| #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS | |||
| Result (Result&&) noexcept; | |||
| Result& operator= (Result&&) noexcept; | |||
| #endif | |||
| 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; | |||
| }; | |||
| #endif // JUCE_RESULT_H_INCLUDED | |||
| @@ -0,0 +1,114 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the juce_core module of the JUCE library. | |||
| Copyright (c) 2013 - Raw Material Software Ltd. | |||
| 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 THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||
| TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||
| NO EVENT SHALL THE AUTHOR 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. | |||
| ------------------------------------------------------------------------------ | |||
| NOTE! This permissive ISC license applies ONLY to files within the juce_core module! | |||
| All other JUCE modules are covered by a dual GPL/commercial license, so if you are | |||
| using any other modules, be sure to check that you also comply with their license. | |||
| For more details, visit www.juce.com | |||
| ============================================================================== | |||
| */ | |||
| namespace | |||
| { | |||
| int64 getRandomSeedFromMACAddresses() | |||
| { | |||
| Array<MACAddress> result; | |||
| MACAddress::findAllAddresses (result); | |||
| Random r; | |||
| for (int i = 0; i < result.size(); ++i) | |||
| r.combineSeed (result[i].toInt64()); | |||
| return r.nextInt64(); | |||
| } | |||
| } | |||
| //============================================================================== | |||
| Uuid::Uuid() | |||
| { | |||
| // The normal random seeding is pretty good, but we'll throw some MAC addresses | |||
| // into the mix too, to make it very very unlikely that two UUIDs will ever be the same.. | |||
| static Random r1 (getRandomSeedFromMACAddresses()); | |||
| Random r2; | |||
| for (size_t i = 0; i < sizeof (uuid); ++i) | |||
| uuid[i] = (uint8) (r1.nextInt() ^ r2.nextInt()); | |||
| } | |||
| 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::isNull() const noexcept | |||
| { | |||
| for (size_t i = 0; i < sizeof (uuid); ++i) | |||
| if (uuid[i] != 0) | |||
| return false; | |||
| return true; | |||
| } | |||
| String Uuid::toString() const | |||
| { | |||
| return String::toHexString (uuid, sizeof (uuid), 0); | |||
| } | |||
| 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) | |||
| { | |||
| 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; | |||
| } | |||
| @@ -0,0 +1,114 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the juce_core module of the JUCE library. | |||
| Copyright (c) 2013 - Raw Material Software Ltd. | |||
| 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 THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||
| TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||
| NO EVENT SHALL THE AUTHOR 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. | |||
| ------------------------------------------------------------------------------ | |||
| NOTE! This permissive ISC license applies ONLY to files within the juce_core module! | |||
| All other JUCE modules are covered by a dual GPL/commercial license, so if you are | |||
| using any other modules, be sure to check that you also comply with their license. | |||
| For more details, visit www.juce.com | |||
| ============================================================================== | |||
| */ | |||
| #ifndef JUCE_UUID_H_INCLUDED | |||
| #define JUCE_UUID_H_INCLUDED | |||
| #include "../text/juce_String.h" | |||
| //============================================================================== | |||
| /** | |||
| A universally unique 128-bit identifier. | |||
| This class generates very random unique numbers based on the system time | |||
| and MAC addresses if any are available. It's extremely unlikely that two identical | |||
| UUIDs would ever be created by chance. | |||
| 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. */ | |||
| Uuid(); | |||
| /** Destructor. */ | |||
| ~Uuid() noexcept; | |||
| /** Creates a copy of another UUID. */ | |||
| Uuid (const Uuid& other) noexcept; | |||
| /** Copies another UUID. */ | |||
| Uuid& operator= (const Uuid& other) noexcept; | |||
| //============================================================================== | |||
| /** Returns true if the ID is zero. */ | |||
| bool isNull() const noexcept; | |||
| bool operator== (const Uuid& other) const noexcept; | |||
| bool operator!= (const Uuid& other) 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; | |||
| /** 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 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); | |||
| /** Sets this UUID from 16-bytes of raw data. */ | |||
| Uuid& operator= (const uint8* rawData) noexcept; | |||
| private: | |||
| //============================================================================== | |||
| uint8 uuid[16]; | |||
| JUCE_LEAK_DETECTOR (Uuid) | |||
| }; | |||
| #endif // JUCE_UUID_H_INCLUDED | |||
| @@ -0,0 +1,130 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the juce_core module of the JUCE library. | |||
| Copyright (c) 2013 - Raw Material Software Ltd. | |||
| 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 THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||
| TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||
| NO EVENT SHALL THE AUTHOR 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. | |||
| ------------------------------------------------------------------------------ | |||
| NOTE! This permissive ISC license applies ONLY to files within the juce_core module! | |||
| All other JUCE modules are covered by a dual GPL/commercial license, so if you are | |||
| using any other modules, be sure to check that you also comply with their license. | |||
| For more details, visit www.juce.com | |||
| ============================================================================== | |||
| */ | |||
| #ifndef JUCE_WINDOWSREGISTRY_H_INCLUDED | |||
| #define JUCE_WINDOWSREGISTRY_H_INCLUDED | |||
| #if JUCE_WINDOWS || DOXYGEN | |||
| /** | |||
| Contains some static helper functions for manipulating the MS Windows registry | |||
| (Only available on Windows, of course!) | |||
| */ | |||
| class WindowsRegistry | |||
| { | |||
| public: | |||
| //============================================================================== | |||
| /** 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 getValue (const String& regValuePath, | |||
| const String& defaultValue = String::empty); | |||
| /** Returns a string from the WOW64 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 getValueWow64 (const String& regValuePath, | |||
| const String& defaultValue = String::empty); | |||
| /** 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 getBinaryValue (const String& regValuePath, MemoryBlock& resultData); | |||
| /** 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 setValue (const String& regValuePath, const String& value); | |||
| /** 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 setValue (const String& regValuePath, uint32 value); | |||
| /** 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 setValue (const String& regValuePath, uint64 value); | |||
| /** 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 setValue (const String& regValuePath, const MemoryBlock& value); | |||
| /** Returns true if the given value exists in the registry. */ | |||
| static bool valueExists (const String& regValuePath); | |||
| /** Returns true if the given value exists in the registry. */ | |||
| static bool valueExistsWow64 (const String& regValuePath); | |||
| /** Returns true if the given key exists in the registry. */ | |||
| static bool keyExists (const String& regValuePath); | |||
| /** Returns true if the given key exists in the registry. */ | |||
| static bool keyExistsWow64 (const String& regValuePath); | |||
| /** Deletes a registry value. */ | |||
| static void deleteValue (const String& regValuePath); | |||
| /** Deletes a registry key (which is registry-talk for 'folder'). */ | |||
| static void deleteKey (const String& regKeyPath); | |||
| /** 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. | |||
| */ | |||
| static bool registerFileAssociation (const String& fileExtension, | |||
| const String& symbolicDescription, | |||
| const String& fullDescription, | |||
| const File& targetExecutable, | |||
| int iconResourceNumber, | |||
| bool registerForCurrentUserOnly); | |||
| private: | |||
| WindowsRegistry(); | |||
| JUCE_DECLARE_NON_COPYABLE (WindowsRegistry) | |||
| }; | |||
| #endif | |||
| #endif // JUCE_WINDOWSREGISTRY_H_INCLUDED | |||
| @@ -0,0 +1,707 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the JUCE library. | |||
| Copyright (c) 2013 - Raw Material Software Ltd. | |||
| Permission is granted to use this software under the terms of either: | |||
| a) the GPL v2 (or any later version) | |||
| b) the Affero GPL v3 | |||
| Details of these licenses can be found at: www.gnu.org/licenses | |||
| JUCE is distributed in the hope that it will be useful, but WITHOUT ANY | |||
| WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR | |||
| A PARTICULAR PURPOSE. See the GNU General Public License for more details. | |||
| ------------------------------------------------------------------------------ | |||
| To release a closed-source product which uses JUCE, commercial licenses are | |||
| available: visit www.juce.com for more information. | |||
| ============================================================================== | |||
| */ | |||
| package com.juce; | |||
| import android.app.Activity; | |||
| import android.app.AlertDialog; | |||
| import android.content.DialogInterface; | |||
| import android.content.Context; | |||
| import android.content.Intent; | |||
| import android.content.res.Configuration; | |||
| import android.net.Uri; | |||
| import android.os.Bundle; | |||
| import android.view.*; | |||
| import android.view.inputmethod.BaseInputConnection; | |||
| import android.view.inputmethod.EditorInfo; | |||
| import android.view.inputmethod.InputConnection; | |||
| import android.view.inputmethod.InputMethodManager; | |||
| import android.graphics.*; | |||
| import android.opengl.*; | |||
| import android.text.ClipboardManager; | |||
| import android.text.InputType; | |||
| import android.util.DisplayMetrics; | |||
| import java.io.BufferedInputStream; | |||
| import java.io.IOException; | |||
| import java.io.InputStream; | |||
| import java.io.OutputStream; | |||
| import java.net.URL; | |||
| import java.net.HttpURLConnection; | |||
| import javax.microedition.khronos.egl.EGLConfig; | |||
| import javax.microedition.khronos.opengles.GL10; | |||
| import android.media.AudioManager; | |||
| import android.media.MediaScannerConnection; | |||
| import android.media.MediaScannerConnection.MediaScannerConnectionClient; | |||
| //============================================================================== | |||
| public final class JuceAppActivity extends Activity | |||
| { | |||
| //============================================================================== | |||
| static | |||
| { | |||
| System.loadLibrary ("juce_jni"); | |||
| } | |||
| @Override | |||
| public final void onCreate (Bundle savedInstanceState) | |||
| { | |||
| super.onCreate (savedInstanceState); | |||
| viewHolder = new ViewHolder (this); | |||
| setContentView (viewHolder); | |||
| setVolumeControlStream (AudioManager.STREAM_MUSIC); | |||
| } | |||
| @Override | |||
| protected final void onDestroy() | |||
| { | |||
| quitApp(); | |||
| super.onDestroy(); | |||
| } | |||
| @Override | |||
| protected final void onPause() | |||
| { | |||
| if (viewHolder != null) | |||
| viewHolder.onPause(); | |||
| suspendApp(); | |||
| super.onPause(); | |||
| } | |||
| @Override | |||
| protected final void onResume() | |||
| { | |||
| super.onResume(); | |||
| if (viewHolder != null) | |||
| viewHolder.onResume(); | |||
| resumeApp(); | |||
| } | |||
| @Override | |||
| public void onConfigurationChanged (Configuration cfg) | |||
| { | |||
| super.onConfigurationChanged (cfg); | |||
| setContentView (viewHolder); | |||
| } | |||
| private void callAppLauncher() | |||
| { | |||
| launchApp (getApplicationInfo().publicSourceDir, | |||
| getApplicationInfo().dataDir); | |||
| } | |||
| //============================================================================== | |||
| private native void launchApp (String appFile, String appDataDir); | |||
| private native void quitApp(); | |||
| private native void suspendApp(); | |||
| private native void resumeApp(); | |||
| private native void setScreenSize (int screenWidth, int screenHeight, int dpi); | |||
| //============================================================================== | |||
| public native void deliverMessage (long value); | |||
| private android.os.Handler messageHandler = new android.os.Handler(); | |||
| public final void postMessage (long value) | |||
| { | |||
| messageHandler.post (new MessageCallback (value)); | |||
| } | |||
| private final class MessageCallback implements Runnable | |||
| { | |||
| public MessageCallback (long value_) { value = value_; } | |||
| public final void run() { deliverMessage (value); } | |||
| private long value; | |||
| } | |||
| //============================================================================== | |||
| private ViewHolder viewHolder; | |||
| public final ComponentPeerView createNewView (boolean opaque) | |||
| { | |||
| ComponentPeerView v = new ComponentPeerView (this, opaque); | |||
| viewHolder.addView (v); | |||
| return v; | |||
| } | |||
| public final void deleteView (ComponentPeerView view) | |||
| { | |||
| ViewGroup group = (ViewGroup) (view.getParent()); | |||
| if (group != null) | |||
| group.removeView (view); | |||
| } | |||
| final class ViewHolder extends ViewGroup | |||
| { | |||
| public ViewHolder (Context context) | |||
| { | |||
| super (context); | |||
| setDescendantFocusability (ViewGroup.FOCUS_AFTER_DESCENDANTS); | |||
| setFocusable (false); | |||
| } | |||
| protected final void onLayout (boolean changed, int left, int top, int right, int bottom) | |||
| { | |||
| setScreenSize (getWidth(), getHeight(), getDPI()); | |||
| if (isFirstResize) | |||
| { | |||
| isFirstResize = false; | |||
| callAppLauncher(); | |||
| } | |||
| } | |||
| public final void onPause() | |||
| { | |||
| for (int i = getChildCount(); --i >= 0;) | |||
| { | |||
| View v = getChildAt (i); | |||
| if (v instanceof ComponentPeerView) | |||
| ((ComponentPeerView) v).onPause(); | |||
| } | |||
| } | |||
| public final void onResume() | |||
| { | |||
| for (int i = getChildCount(); --i >= 0;) | |||
| { | |||
| View v = getChildAt (i); | |||
| if (v instanceof ComponentPeerView) | |||
| ((ComponentPeerView) v).onResume(); | |||
| } | |||
| } | |||
| private final int getDPI() | |||
| { | |||
| DisplayMetrics metrics = new DisplayMetrics(); | |||
| getWindowManager().getDefaultDisplay().getMetrics (metrics); | |||
| return metrics.densityDpi; | |||
| } | |||
| private boolean isFirstResize = true; | |||
| } | |||
| public final void excludeClipRegion (android.graphics.Canvas canvas, float left, float top, float right, float bottom) | |||
| { | |||
| canvas.clipRect (left, top, right, bottom, android.graphics.Region.Op.DIFFERENCE); | |||
| } | |||
| //============================================================================== | |||
| public final String getClipboardContent() | |||
| { | |||
| ClipboardManager clipboard = (ClipboardManager) getSystemService (CLIPBOARD_SERVICE); | |||
| return clipboard.getText().toString(); | |||
| } | |||
| public final void setClipboardContent (String newText) | |||
| { | |||
| ClipboardManager clipboard = (ClipboardManager) getSystemService (CLIPBOARD_SERVICE); | |||
| clipboard.setText (newText); | |||
| } | |||
| //============================================================================== | |||
| public final void showMessageBox (String title, String message, final long callback) | |||
| { | |||
| AlertDialog.Builder builder = new AlertDialog.Builder (this); | |||
| builder.setTitle (title) | |||
| .setMessage (message) | |||
| .setCancelable (true) | |||
| .setPositiveButton ("OK", new DialogInterface.OnClickListener() | |||
| { | |||
| public void onClick (DialogInterface dialog, int id) | |||
| { | |||
| dialog.cancel(); | |||
| JuceAppActivity.this.alertDismissed (callback, 0); | |||
| } | |||
| }); | |||
| builder.create().show(); | |||
| } | |||
| public final void showOkCancelBox (String title, String message, final long callback) | |||
| { | |||
| AlertDialog.Builder builder = new AlertDialog.Builder (this); | |||
| builder.setTitle (title) | |||
| .setMessage (message) | |||
| .setCancelable (true) | |||
| .setPositiveButton ("OK", new DialogInterface.OnClickListener() | |||
| { | |||
| public void onClick (DialogInterface dialog, int id) | |||
| { | |||
| dialog.cancel(); | |||
| JuceAppActivity.this.alertDismissed (callback, 1); | |||
| } | |||
| }) | |||
| .setNegativeButton ("Cancel", new DialogInterface.OnClickListener() | |||
| { | |||
| public void onClick (DialogInterface dialog, int id) | |||
| { | |||
| dialog.cancel(); | |||
| JuceAppActivity.this.alertDismissed (callback, 0); | |||
| } | |||
| }); | |||
| builder.create().show(); | |||
| } | |||
| public final void showYesNoCancelBox (String title, String message, final long callback) | |||
| { | |||
| AlertDialog.Builder builder = new AlertDialog.Builder (this); | |||
| builder.setTitle (title) | |||
| .setMessage (message) | |||
| .setCancelable (true) | |||
| .setPositiveButton ("Yes", new DialogInterface.OnClickListener() | |||
| { | |||
| public void onClick (DialogInterface dialog, int id) | |||
| { | |||
| dialog.cancel(); | |||
| JuceAppActivity.this.alertDismissed (callback, 1); | |||
| } | |||
| }) | |||
| .setNegativeButton ("No", new DialogInterface.OnClickListener() | |||
| { | |||
| public void onClick (DialogInterface dialog, int id) | |||
| { | |||
| dialog.cancel(); | |||
| JuceAppActivity.this.alertDismissed (callback, 2); | |||
| } | |||
| }) | |||
| .setNeutralButton ("Cancel", new DialogInterface.OnClickListener() | |||
| { | |||
| public void onClick (DialogInterface dialog, int id) | |||
| { | |||
| dialog.cancel(); | |||
| JuceAppActivity.this.alertDismissed (callback, 0); | |||
| } | |||
| }); | |||
| builder.create().show(); | |||
| } | |||
| public native void alertDismissed (long callback, int id); | |||
| //============================================================================== | |||
| public final class ComponentPeerView extends ViewGroup | |||
| implements View.OnFocusChangeListener | |||
| { | |||
| public ComponentPeerView (Context context, boolean opaque_) | |||
| { | |||
| super (context); | |||
| setWillNotDraw (false); | |||
| opaque = opaque_; | |||
| setFocusable (true); | |||
| setFocusableInTouchMode (true); | |||
| setOnFocusChangeListener (this); | |||
| requestFocus(); | |||
| } | |||
| //============================================================================== | |||
| private native void handlePaint (Canvas canvas); | |||
| @Override | |||
| public void draw (Canvas canvas) | |||
| { | |||
| super.draw (canvas); | |||
| handlePaint (canvas); | |||
| } | |||
| @Override | |||
| public boolean isOpaque() | |||
| { | |||
| return opaque; | |||
| } | |||
| private boolean opaque; | |||
| //============================================================================== | |||
| private native void handleMouseDown (int index, float x, float y, long time); | |||
| private native void handleMouseDrag (int index, float x, float y, long time); | |||
| private native void handleMouseUp (int index, float x, float y, long time); | |||
| @Override | |||
| public boolean onTouchEvent (MotionEvent event) | |||
| { | |||
| int action = event.getAction(); | |||
| long time = event.getEventTime(); | |||
| switch (action & MotionEvent.ACTION_MASK) | |||
| { | |||
| case MotionEvent.ACTION_DOWN: | |||
| handleMouseDown (event.getPointerId(0), event.getX(), event.getY(), time); | |||
| return true; | |||
| case MotionEvent.ACTION_CANCEL: | |||
| case MotionEvent.ACTION_UP: | |||
| handleMouseUp (event.getPointerId(0), event.getX(), event.getY(), time); | |||
| return true; | |||
| case MotionEvent.ACTION_MOVE: | |||
| { | |||
| int n = event.getPointerCount(); | |||
| for (int i = 0; i < n; ++i) | |||
| handleMouseDrag (event.getPointerId(i), event.getX(i), event.getY(i), time); | |||
| return true; | |||
| } | |||
| case MotionEvent.ACTION_POINTER_UP: | |||
| { | |||
| int i = (action & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT; | |||
| handleMouseUp (event.getPointerId(i), event.getX(i), event.getY(i), time); | |||
| return true; | |||
| } | |||
| case MotionEvent.ACTION_POINTER_DOWN: | |||
| { | |||
| int i = (action & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT; | |||
| handleMouseDown (event.getPointerId(i), event.getX(i), event.getY(i), time); | |||
| return true; | |||
| } | |||
| default: | |||
| break; | |||
| } | |||
| return false; | |||
| } | |||
| //============================================================================== | |||
| private native void handleKeyDown (int keycode, int textchar); | |||
| private native void handleKeyUp (int keycode, int textchar); | |||
| public void showKeyboard (boolean shouldShow) | |||
| { | |||
| InputMethodManager imm = (InputMethodManager) getSystemService (Context.INPUT_METHOD_SERVICE); | |||
| if (imm != null) | |||
| { | |||
| if (shouldShow) | |||
| imm.showSoftInput (this, InputMethodManager.SHOW_FORCED); | |||
| else | |||
| imm.hideSoftInputFromWindow (getWindowToken(), 0); | |||
| } | |||
| } | |||
| @Override | |||
| public boolean onKeyDown (int keyCode, KeyEvent event) | |||
| { | |||
| handleKeyDown (keyCode, event.getUnicodeChar()); | |||
| return true; | |||
| } | |||
| @Override | |||
| public boolean onKeyUp (int keyCode, KeyEvent event) | |||
| { | |||
| handleKeyUp (keyCode, event.getUnicodeChar()); | |||
| return true; | |||
| } | |||
| // this is here to make keyboard entry work on a Galaxy Tab2 10.1 | |||
| @Override | |||
| public InputConnection onCreateInputConnection (EditorInfo outAttrs) | |||
| { | |||
| outAttrs.actionLabel = ""; | |||
| outAttrs.hintText = ""; | |||
| outAttrs.initialCapsMode = 0; | |||
| outAttrs.initialSelEnd = outAttrs.initialSelStart = -1; | |||
| outAttrs.label = ""; | |||
| outAttrs.imeOptions = EditorInfo.IME_ACTION_DONE | EditorInfo.IME_FLAG_NO_EXTRACT_UI; | |||
| outAttrs.inputType = InputType.TYPE_NULL; | |||
| return new BaseInputConnection (this, false); | |||
| } | |||
| //============================================================================== | |||
| @Override | |||
| protected void onSizeChanged (int w, int h, int oldw, int oldh) | |||
| { | |||
| super.onSizeChanged (w, h, oldw, oldh); | |||
| viewSizeChanged(); | |||
| } | |||
| @Override | |||
| protected void onLayout (boolean changed, int left, int top, int right, int bottom) | |||
| { | |||
| for (int i = getChildCount(); --i >= 0;) | |||
| requestTransparentRegion (getChildAt (i)); | |||
| } | |||
| private native void viewSizeChanged(); | |||
| @Override | |||
| public void onFocusChange (View v, boolean hasFocus) | |||
| { | |||
| if (v == this) | |||
| focusChanged (hasFocus); | |||
| } | |||
| private native void focusChanged (boolean hasFocus); | |||
| public void setViewName (String newName) {} | |||
| public boolean isVisible() { return getVisibility() == VISIBLE; } | |||
| public void setVisible (boolean b) { setVisibility (b ? VISIBLE : INVISIBLE); } | |||
| public boolean containsPoint (int x, int y) | |||
| { | |||
| return true; //xxx needs to check overlapping views | |||
| } | |||
| public final void onPause() | |||
| { | |||
| for (int i = getChildCount(); --i >= 0;) | |||
| { | |||
| View v = getChildAt (i); | |||
| if (v instanceof OpenGLView) | |||
| ((OpenGLView) v).onPause(); | |||
| } | |||
| } | |||
| public final void onResume() | |||
| { | |||
| for (int i = getChildCount(); --i >= 0;) | |||
| { | |||
| View v = getChildAt (i); | |||
| if (v instanceof OpenGLView) | |||
| ((OpenGLView) v).onResume(); | |||
| } | |||
| } | |||
| public OpenGLView createGLView() | |||
| { | |||
| OpenGLView glView = new OpenGLView (getContext()); | |||
| addView (glView); | |||
| return glView; | |||
| } | |||
| } | |||
| //============================================================================== | |||
| public final class OpenGLView extends GLSurfaceView | |||
| implements GLSurfaceView.Renderer | |||
| { | |||
| OpenGLView (Context context) | |||
| { | |||
| super (context); | |||
| setEGLContextClientVersion (2); | |||
| setRenderer (this); | |||
| setRenderMode (RENDERMODE_WHEN_DIRTY); | |||
| } | |||
| @Override | |||
| public void onSurfaceCreated (GL10 unused, EGLConfig config) | |||
| { | |||
| contextCreated(); | |||
| } | |||
| @Override | |||
| public void onSurfaceChanged (GL10 unused, int width, int height) | |||
| { | |||
| contextChangedSize(); | |||
| } | |||
| @Override | |||
| public void onDrawFrame (GL10 unused) | |||
| { | |||
| render(); | |||
| } | |||
| private native void contextCreated(); | |||
| private native void contextChangedSize(); | |||
| private native void render(); | |||
| } | |||
| //============================================================================== | |||
| public final int[] renderGlyph (char glyph, Paint paint, android.graphics.Matrix matrix, Rect bounds) | |||
| { | |||
| Path p = new Path(); | |||
| paint.getTextPath (String.valueOf (glyph), 0, 1, 0.0f, 0.0f, p); | |||
| RectF boundsF = new RectF(); | |||
| p.computeBounds (boundsF, true); | |||
| matrix.mapRect (boundsF); | |||
| boundsF.roundOut (bounds); | |||
| bounds.left--; | |||
| bounds.right++; | |||
| final int w = bounds.width(); | |||
| final int h = Math.max (1, bounds.height()); | |||
| Bitmap bm = Bitmap.createBitmap (w, h, Bitmap.Config.ARGB_8888); | |||
| Canvas c = new Canvas (bm); | |||
| matrix.postTranslate (-bounds.left, -bounds.top); | |||
| c.setMatrix (matrix); | |||
| c.drawPath (p, paint); | |||
| final int sizeNeeded = w * h; | |||
| if (cachedRenderArray.length < sizeNeeded) | |||
| cachedRenderArray = new int [sizeNeeded]; | |||
| bm.getPixels (cachedRenderArray, 0, w, 0, 0, w, h); | |||
| bm.recycle(); | |||
| return cachedRenderArray; | |||
| } | |||
| private int[] cachedRenderArray = new int [256]; | |||
| //============================================================================== | |||
| public static class HTTPStream | |||
| { | |||
| public HTTPStream (HttpURLConnection connection_) throws IOException | |||
| { | |||
| connection = connection_; | |||
| inputStream = new BufferedInputStream (connection.getInputStream()); | |||
| } | |||
| public final void release() | |||
| { | |||
| try | |||
| { | |||
| inputStream.close(); | |||
| } | |||
| catch (IOException e) | |||
| {} | |||
| connection.disconnect(); | |||
| } | |||
| public final int read (byte[] buffer, int numBytes) | |||
| { | |||
| int num = 0; | |||
| try | |||
| { | |||
| num = inputStream.read (buffer, 0, numBytes); | |||
| } | |||
| catch (IOException e) | |||
| {} | |||
| if (num > 0) | |||
| position += num; | |||
| return num; | |||
| } | |||
| public final long getPosition() { return position; } | |||
| public final long getTotalLength() { return -1; } | |||
| public final boolean isExhausted() { return false; } | |||
| public final boolean setPosition (long newPos) { return false; } | |||
| private HttpURLConnection connection; | |||
| private InputStream inputStream; | |||
| private long position; | |||
| } | |||
| public static final HTTPStream createHTTPStream (String address, boolean isPost, byte[] postData, | |||
| String headers, int timeOutMs, | |||
| java.lang.StringBuffer responseHeaders) | |||
| { | |||
| try | |||
| { | |||
| HttpURLConnection connection = (HttpURLConnection) (new URL (address).openConnection()); | |||
| if (connection != null) | |||
| { | |||
| try | |||
| { | |||
| if (isPost) | |||
| { | |||
| connection.setConnectTimeout (timeOutMs); | |||
| connection.setDoOutput (true); | |||
| connection.setChunkedStreamingMode (0); | |||
| OutputStream out = connection.getOutputStream(); | |||
| out.write (postData); | |||
| out.flush(); | |||
| } | |||
| return new HTTPStream (connection); | |||
| } | |||
| catch (Throwable e) | |||
| { | |||
| connection.disconnect(); | |||
| } | |||
| } | |||
| } | |||
| catch (Throwable e) | |||
| {} | |||
| return null; | |||
| } | |||
| public final void launchURL (String url) | |||
| { | |||
| startActivity (new Intent (Intent.ACTION_VIEW, Uri.parse (url))); | |||
| } | |||
| public static final String getLocaleValue (boolean isRegion) | |||
| { | |||
| java.util.Locale locale = java.util.Locale.getDefault(); | |||
| return isRegion ? locale.getDisplayCountry (java.util.Locale.US) | |||
| : locale.getDisplayLanguage (java.util.Locale.US); | |||
| } | |||
| //============================================================================== | |||
| private final class SingleMediaScanner implements MediaScannerConnectionClient | |||
| { | |||
| public SingleMediaScanner (Context context, String filename) | |||
| { | |||
| file = filename; | |||
| msc = new MediaScannerConnection (context, this); | |||
| msc.connect(); | |||
| } | |||
| @Override | |||
| public void onMediaScannerConnected() | |||
| { | |||
| msc.scanFile (file, null); | |||
| } | |||
| @Override | |||
| public void onScanCompleted (String path, Uri uri) | |||
| { | |||
| msc.disconnect(); | |||
| } | |||
| private MediaScannerConnection msc; | |||
| private String file; | |||
| } | |||
| public final void scanFile (String filename) | |||
| { | |||
| new SingleMediaScanner (this, filename); | |||
| } | |||
| } | |||
| @@ -0,0 +1,223 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the juce_core module of the JUCE library. | |||
| Copyright (c) 2013 - Raw Material Software Ltd. | |||
| 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 THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||
| TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||
| NO EVENT SHALL THE AUTHOR 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. | |||
| ------------------------------------------------------------------------------ | |||
| NOTE! This permissive ISC license applies ONLY to files within the juce_core module! | |||
| All other JUCE modules are covered by a dual GPL/commercial license, so if you are | |||
| using any other modules, be sure to check that you also comply with their license. | |||
| For more details, visit www.juce.com | |||
| ============================================================================== | |||
| */ | |||
| #ifndef JUCE_BASICNATIVEHEADERS_H_INCLUDED | |||
| #define JUCE_BASICNATIVEHEADERS_H_INCLUDED | |||
| #include "../system/juce_TargetPlatform.h" | |||
| #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 | |||
| #define Point CarbonDummyPointName | |||
| #define Component CarbonDummyCompName | |||
| #import <Cocoa/Cocoa.h> | |||
| #import <CoreAudio/HostTime.h> | |||
| #undef Point | |||
| #undef Component | |||
| #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) | |||
| #pragma warning (disable : 4100 4201 4514 4312 4995) | |||
| #endif | |||
| #define STRICT 1 | |||
| #define WIN32_LEAN_AND_MEAN 1 | |||
| #if JUCE_MINGW | |||
| #define _WIN32_WINNT 0x0501 | |||
| #else | |||
| #define _WIN32_WINNT 0x0600 | |||
| #endif | |||
| #define _UNICODE 1 | |||
| #define UNICODE 1 | |||
| #ifndef _WIN32_IE | |||
| #define _WIN32_IE 0x0400 | |||
| #endif | |||
| #include <windows.h> | |||
| #include <shellapi.h> | |||
| #include <tchar.h> | |||
| #include <stddef.h> | |||
| #include <ctime> | |||
| #include <wininet.h> | |||
| #include <nb30.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> | |||
| #else | |||
| #include <crtdbg.h> | |||
| #include <comutil.h> | |||
| #endif | |||
| #undef PACKED | |||
| #if JUCE_MSVC | |||
| #pragma warning (pop) | |||
| #pragma warning (4: 4511 4512 4100 /*4365*/) // (enable some warnings that are turned off in VC8) | |||
| #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> | |||
| #endif | |||
| // Need to clear various moronic redefinitions made by system headers.. | |||
| #undef max | |||
| #undef min | |||
| #undef direct | |||
| #undef check | |||
| #endif // JUCE_BASICNATIVEHEADERS_H_INCLUDED | |||
| @@ -0,0 +1,241 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the juce_core module of the JUCE library. | |||
| Copyright (c) 2013 - Raw Material Software Ltd. | |||
| 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 THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||
| TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||
| NO EVENT SHALL THE AUTHOR 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. | |||
| ------------------------------------------------------------------------------ | |||
| NOTE! This permissive ISC license applies ONLY to files within the juce_core module! | |||
| All other JUCE modules are covered by a dual GPL/commercial license, so if you are | |||
| using any other modules, be sure to check that you also comply with their license. | |||
| For more details, visit www.juce.com | |||
| ============================================================================== | |||
| */ | |||
| 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::isOnCDRomDrive() const | |||
| { | |||
| return false; | |||
| } | |||
| bool File::isOnHardDisk() const | |||
| { | |||
| return true; | |||
| } | |||
| bool File::isOnRemovableDrive() const | |||
| { | |||
| return false; | |||
| } | |||
| bool File::isHidden() const | |||
| { | |||
| return getFileName().startsWithChar ('.'); | |||
| } | |||
| //============================================================================== | |||
| namespace | |||
| { | |||
| File juce_readlink (const String& file, const File& defaultFile) | |||
| { | |||
| const int size = 8192; | |||
| HeapBlock<char> buffer; | |||
| buffer.malloc (size + 4); | |||
| const size_t numBytes = readlink (file.toUTF8(), buffer, size); | |||
| if (numBytes > 0 && numBytes <= size) | |||
| return File (file).getSiblingFile (String::fromUTF8 (buffer, (int) numBytes)); | |||
| return defaultFile; | |||
| } | |||
| } | |||
| File File::getLinkedTarget() const | |||
| { | |||
| return juce_readlink (getFullPathName().toUTF8(), *this); | |||
| } | |||
| //============================================================================== | |||
| File File::getSpecialLocation (const SpecialLocationType type) | |||
| { | |||
| switch (type) | |||
| { | |||
| case userHomeDirectory: | |||
| case userDocumentsDirectory: | |||
| case userMusicDirectory: | |||
| case userMoviesDirectory: | |||
| case userPicturesDirectory: | |||
| case userApplicationDataDirectory: | |||
| case userDesktopDirectory: | |||
| return File (android.appDataDir); | |||
| case commonApplicationDataDirectory: | |||
| return File (android.appDataDir); | |||
| case globalApplicationsDirectory: | |||
| return File ("/system/app"); | |||
| case tempDirectory: | |||
| //return File (AndroidStatsHelpers::getSystemProperty ("java.io.tmpdir")); | |||
| return File (android.appDataDir).getChildFile (".temp"); | |||
| case invokedExecutableFile: | |||
| case currentExecutableFile: | |||
| case currentApplicationFile: | |||
| case hostApplicationPath: | |||
| return juce_getExecutableFile(); | |||
| default: | |||
| jassertfalse; // unknown type? | |||
| break; | |||
| } | |||
| return File::nonexistent; | |||
| } | |||
| //============================================================================== | |||
| String File::getVersion() const | |||
| { | |||
| return String::empty; | |||
| } | |||
| //============================================================================== | |||
| bool File::moveToTrash() const | |||
| { | |||
| if (! exists()) | |||
| return true; | |||
| // TODO | |||
| return false; | |||
| } | |||
| //============================================================================== | |||
| class DirectoryIterator::NativeIterator::Pimpl | |||
| { | |||
| public: | |||
| Pimpl (const File& directory, const String& wildCard_) | |||
| : parentDir (File::addTrailingSeparator (directory.getFullPathName())), | |||
| wildCard (wildCard_), | |||
| dir (opendir (directory.getFullPathName().toUTF8())) | |||
| { | |||
| } | |||
| ~Pimpl() | |||
| { | |||
| if (dir != 0) | |||
| 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 != 0) | |||
| { | |||
| 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 != 0) | |||
| *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& wildCard) | |||
| : pimpl (new DirectoryIterator::NativeIterator::Pimpl (directory, wildCard)) | |||
| { | |||
| } | |||
| DirectoryIterator::NativeIterator::~NativeIterator() | |||
| { | |||
| } | |||
| bool DirectoryIterator::NativeIterator::next (String& filenameFound, | |||
| bool* const isDir, bool* const isHidden, int64* const fileSize, | |||
| Time* const modTime, Time* const creationTime, bool* const isReadOnly) | |||
| { | |||
| return pimpl->next (filenameFound, isDir, isHidden, fileSize, modTime, creationTime, isReadOnly); | |||
| } | |||
| //============================================================================== | |||
| bool Process::openDocument (const String& fileName, const String& parameters) | |||
| { | |||
| const LocalRef<jstring> t (javaString (fileName)); | |||
| android.activity.callVoidMethod (JuceAppActivity.launchURL, t.get()); | |||
| } | |||
| void File::revealToUser() const | |||
| { | |||
| } | |||
| @@ -0,0 +1,407 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the juce_core module of the JUCE library. | |||
| Copyright (c) 2013 - Raw Material Software Ltd. | |||
| 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 THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||
| TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||
| NO EVENT SHALL THE AUTHOR 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. | |||
| ------------------------------------------------------------------------------ | |||
| NOTE! This permissive ISC license applies ONLY to files within the juce_core module! | |||
| All other JUCE modules are covered by a dual GPL/commercial license, so if you are | |||
| using any other modules, be sure to check that you also comply with their license. | |||
| For more details, visit www.juce.com | |||
| ============================================================================== | |||
| */ | |||
| #ifndef JUCE_ANDROID_JNIHELPERS_H_INCLUDED | |||
| #define JUCE_ANDROID_JNIHELPERS_H_INCLUDED | |||
| #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; | |||
| //============================================================================== | |||
| 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)) {} | |||
| ~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 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; | |||
| static inline jobject retain (jobject obj) | |||
| { | |||
| return obj == 0 ? 0 : getEnv()->NewGlobalRef (obj); | |||
| } | |||
| }; | |||
| //============================================================================== | |||
| template <typename JavaType> | |||
| class LocalRef | |||
| { | |||
| public: | |||
| explicit inline LocalRef (JavaType o) noexcept : obj (o) {} | |||
| inline LocalRef (const LocalRef& other) noexcept : obj (retain (other.obj)) {} | |||
| ~LocalRef() { clear(); } | |||
| void clear() | |||
| { | |||
| if (obj != 0) | |||
| getEnv()->DeleteLocalRef (obj); | |||
| } | |||
| LocalRef& operator= (const LocalRef& other) | |||
| { | |||
| jobject newObj = retain (other.obj); | |||
| clear(); | |||
| obj = newObj; | |||
| 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 | |||
| { | |||
| 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; | |||
| } | |||
| String juceString (jstring s) | |||
| { | |||
| return juceString (getEnv(), s); | |||
| } | |||
| LocalRef<jstring> javaString (const String& s) | |||
| { | |||
| return LocalRef<jstring> (getEnv()->NewStringUTF (s.toUTF8())); | |||
| } | |||
| 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) \ | |||
| { \ | |||
| 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; | |||
| //============================================================================== | |||
| #define JUCE_JNI_CALLBACK(className, methodName, returnType, params) \ | |||
| extern "C" __attribute__ ((visibility("default"))) 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; | |||
| //============================================================================== | |||
| class ThreadLocalJNIEnvHolder | |||
| { | |||
| public: | |||
| ThreadLocalJNIEnvHolder() | |||
| : jvm (nullptr) | |||
| { | |||
| zeromem (threads, sizeof (threads)); | |||
| zeromem (envs, sizeof (envs)); | |||
| } | |||
| void initialise (JNIEnv* env) | |||
| { | |||
| // NB: the DLL can be left loaded by the JVM, so the same static | |||
| // objects can end up being reused by subsequent runs of the app | |||
| zeromem (threads, sizeof (threads)); | |||
| zeromem (envs, sizeof (envs)); | |||
| env->GetJavaVM (&jvm); | |||
| addEnv (env); | |||
| } | |||
| JNIEnv* attach() | |||
| { | |||
| JNIEnv* env = nullptr; | |||
| jvm->AttachCurrentThread (&env, nullptr); | |||
| if (env != nullptr) | |||
| addEnv (env); | |||
| return env; | |||
| } | |||
| void detach() | |||
| { | |||
| jvm->DetachCurrentThread(); | |||
| const pthread_t thisThread = pthread_self(); | |||
| SpinLock::ScopedLockType sl (addRemoveLock); | |||
| for (int i = 0; i < maxThreads; ++i) | |||
| if (threads[i] == thisThread) | |||
| threads[i] = 0; | |||
| } | |||
| JNIEnv* getOrAttach() noexcept | |||
| { | |||
| JNIEnv* env = get(); | |||
| if (env == nullptr) | |||
| env = attach(); | |||
| jassert (env != nullptr); | |||
| return env; | |||
| } | |||
| JNIEnv* get() const noexcept | |||
| { | |||
| const pthread_t thisThread = pthread_self(); | |||
| for (int i = 0; i < maxThreads; ++i) | |||
| if (threads[i] == thisThread) | |||
| return envs[i]; | |||
| return nullptr; | |||
| } | |||
| enum { maxThreads = 32 }; | |||
| private: | |||
| JavaVM* jvm; | |||
| pthread_t threads [maxThreads]; | |||
| JNIEnv* envs [maxThreads]; | |||
| SpinLock addRemoveLock; | |||
| void addEnv (JNIEnv* env) | |||
| { | |||
| SpinLock::ScopedLockType sl (addRemoveLock); | |||
| if (get() == nullptr) | |||
| { | |||
| const pthread_t thisThread = pthread_self(); | |||
| for (int i = 0; i < maxThreads; ++i) | |||
| { | |||
| if (threads[i] == 0) | |||
| { | |||
| envs[i] = env; | |||
| threads[i] = thisThread; | |||
| return; | |||
| } | |||
| } | |||
| } | |||
| jassertfalse; // too many threads! | |||
| } | |||
| }; | |||
| extern ThreadLocalJNIEnvHolder threadLocalJNIEnvHolder; | |||
| //============================================================================== | |||
| #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \ | |||
| METHOD (createNewView, "createNewView", "(Z)L" JUCE_ANDROID_ACTIVITY_CLASSPATH "$ComponentPeerView;") \ | |||
| METHOD (deleteView, "deleteView", "(L" JUCE_ANDROID_ACTIVITY_CLASSPATH "$ComponentPeerView;)V") \ | |||
| METHOD (postMessage, "postMessage", "(J)V") \ | |||
| METHOD (finish, "finish", "()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", "(CLandroid/graphics/Paint;Landroid/graphics/Matrix;Landroid/graphics/Rect;)[I") \ | |||
| STATICMETHOD (createHTTPStream, "createHTTPStream", "(Ljava/lang/String;Z[BLjava/lang/String;ILjava/lang/StringBuffer;)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;J)V") \ | |||
| METHOD (showYesNoCancelBox, "showYesNoCancelBox", "(Ljava/lang/String;Ljava/lang/String;J)V") \ | |||
| STATICMETHOD (getLocaleValue, "getLocaleValue", "(Z)Ljava/lang/String;") \ | |||
| METHOD (scanFile, "scanFile", "(Ljava/lang/String;)V") | |||
| 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 | |||
| #endif // JUCE_ANDROID_JNIHELPERS_H_INCLUDED | |||
| @@ -0,0 +1,32 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the juce_core module of the JUCE library. | |||
| Copyright (c) 2013 - Raw Material Software Ltd. | |||
| 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 THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||
| TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||
| NO EVENT SHALL THE AUTHOR 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. | |||
| ------------------------------------------------------------------------------ | |||
| NOTE! This permissive ISC license applies ONLY to files within the juce_core module! | |||
| All other JUCE modules are covered by a dual GPL/commercial license, so if you are | |||
| using any other modules, be sure to check that you also comply with their license. | |||
| For more details, visit www.juce.com | |||
| ============================================================================== | |||
| */ | |||
| void Logger::outputDebugString (const String& text) | |||
| { | |||
| __android_log_print (ANDROID_LOG_INFO, "JUCE", text.toUTF8()); | |||
| } | |||
| @@ -0,0 +1,175 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the juce_core module of the JUCE library. | |||
| Copyright (c) 2013 - Raw Material Software Ltd. | |||
| 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 THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||
| TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||
| NO EVENT SHALL THE AUTHOR 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. | |||
| ------------------------------------------------------------------------------ | |||
| NOTE! This permissive ISC license applies ONLY to files within the juce_core module! | |||
| All other JUCE modules are covered by a dual GPL/commercial license, so if you are | |||
| using any other modules, be sure to check that you also comply with their license. | |||
| For more details, visit www.juce.com | |||
| ============================================================================== | |||
| */ | |||
| //============================================================================== | |||
| #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 (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 | |||
| } | |||
| bool Process::openEmailWithAttachments (const String& targetEmailAddress, | |||
| const String& emailSubject, | |||
| const String& bodyText, | |||
| const StringArray& filesToAttach) | |||
| { | |||
| // TODO | |||
| return false; | |||
| } | |||
| //============================================================================== | |||
| class WebInputStream : public InputStream | |||
| { | |||
| public: | |||
| WebInputStream (String address, bool isPost, const MemoryBlock& postData, | |||
| URL::OpenStreamProgressCallback* progressCallback, void* progressCallbackContext, | |||
| const String& headers, int timeOutMs, StringPairArray* responseHeaders) | |||
| { | |||
| if (! address.contains ("://")) | |||
| address = "http://" + address; | |||
| JNIEnv* env = getEnv(); | |||
| jbyteArray postDataArray = 0; | |||
| if (postData.getSize() > 0) | |||
| { | |||
| postDataArray = env->NewByteArray (postData.getSize()); | |||
| env->SetByteArrayRegion (postDataArray, 0, postData.getSize(), (const jbyte*) postData.getData()); | |||
| } | |||
| LocalRef<jobject> responseHeaderBuffer (env->NewObject (StringBuffer, StringBuffer.constructor)); | |||
| stream = GlobalRef (env->CallStaticObjectMethod (JuceAppActivity, | |||
| JuceAppActivity.createHTTPStream, | |||
| javaString (address).get(), | |||
| (jboolean) isPost, | |||
| postDataArray, | |||
| javaString (headers).get(), | |||
| (jint) timeOutMs, | |||
| responseHeaderBuffer.get())); | |||
| 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)); | |||
| } | |||
| if (responseHeaders != 0) | |||
| { | |||
| 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)); | |||
| } | |||
| } | |||
| } | |||
| } | |||
| ~WebInputStream() | |||
| { | |||
| if (stream != 0) | |||
| stream.callVoidMethod (HTTPStream.release); | |||
| } | |||
| //============================================================================== | |||
| bool isExhausted() override { return stream != nullptr && stream.callBooleanMethod (HTTPStream.isExhausted); } | |||
| int64 getTotalLength() override { return stream != nullptr ? stream.callLongMethod (HTTPStream.getTotalLength) : 0; } | |||
| int64 getPosition() override { return stream != nullptr ? stream.callLongMethod (HTTPStream.getPosition) : 0; } | |||
| bool setPosition (int64 wantedPos) override { return stream != nullptr && stream.callBooleanMethod (HTTPStream.setPosition, (jlong) wantedPos); } | |||
| int read (void* buffer, int bytesToRead) override | |||
| { | |||
| jassert (buffer != nullptr && bytesToRead >= 0); | |||
| 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; | |||
| } | |||
| //============================================================================== | |||
| GlobalRef stream; | |||
| private: | |||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WebInputStream) | |||
| }; | |||
| InputStream* URL::createNativeStream (const String& address, bool isPost, const MemoryBlock& postData, | |||
| OpenStreamProgressCallback* progressCallback, void* progressCallbackContext, | |||
| const String& headers, const int timeOutMs, StringPairArray* responseHeaders) | |||
| { | |||
| ScopedPointer <WebInputStream> wi (new WebInputStream (address, isPost, postData, | |||
| progressCallback, progressCallbackContext, | |||
| headers, timeOutMs, responseHeaders)); | |||
| return wi->stream != 0 ? wi.release() : nullptr; | |||
| } | |||
| @@ -0,0 +1,307 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the juce_core module of the JUCE library. | |||
| Copyright (c) 2013 - Raw Material Software Ltd. | |||
| 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 THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||
| TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||
| NO EVENT SHALL THE AUTHOR 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. | |||
| ------------------------------------------------------------------------------ | |||
| NOTE! This permissive ISC license applies ONLY to files within the juce_core module! | |||
| All other JUCE modules are covered by a dual GPL/commercial license, so if you are | |||
| using any other modules, be sure to check that you also comply with their license. | |||
| For more details, visit www.juce.com | |||
| ============================================================================== | |||
| */ | |||
| JNIClassBase::JNIClassBase (const char* classPath_) | |||
| : classPath (classPath_), 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; | |||
| } | |||
| //============================================================================== | |||
| ThreadLocalJNIEnvHolder threadLocalJNIEnvHolder; | |||
| #if JUCE_DEBUG | |||
| static bool systemInitialised = false; | |||
| #endif | |||
| JNIEnv* getEnv() noexcept | |||
| { | |||
| #if JUCE_DEBUG | |||
| if (! systemInitialised) | |||
| { | |||
| DBG ("*** Call to getEnv() when system not initialised"); | |||
| jassertfalse; | |||
| std::exit (EXIT_FAILURE); | |||
| } | |||
| #endif | |||
| return threadLocalJNIEnvHolder.getOrAttach(); | |||
| } | |||
| extern "C" jint JNI_OnLoad (JavaVM*, void*) | |||
| { | |||
| return JNI_VERSION_1_2; | |||
| } | |||
| //============================================================================== | |||
| AndroidSystem::AndroidSystem() : screenWidth (0), screenHeight (0), dpi (160) | |||
| { | |||
| } | |||
| void AndroidSystem::initialise (JNIEnv* env, jobject activity_, | |||
| jstring appFile_, jstring appDataDir_) | |||
| { | |||
| screenWidth = screenHeight = 0; | |||
| dpi = 160; | |||
| JNIClassBase::initialiseAllClasses (env); | |||
| threadLocalJNIEnvHolder.initialise (env); | |||
| #if JUCE_DEBUG | |||
| systemInitialised = true; | |||
| #endif | |||
| activity = GlobalRef (activity_); | |||
| appFile = juceString (env, appFile_); | |||
| appDataDir = juceString (env, appDataDir_); | |||
| } | |||
| void AndroidSystem::shutdown (JNIEnv* env) | |||
| { | |||
| activity.clear(); | |||
| #if JUCE_DEBUG | |||
| systemInitialised = false; | |||
| #endif | |||
| 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 | |||
| //============================================================================== | |||
| String getSystemProperty (const String& name) | |||
| { | |||
| return juceString (LocalRef<jstring> ((jstring) getEnv()->CallStaticObjectMethod (SystemClass, | |||
| SystemClass.getProperty, | |||
| javaString (name).get()))); | |||
| } | |||
| //============================================================================== | |||
| String getLocaleValue (bool isRegion) | |||
| { | |||
| return juceString (LocalRef<jstring> ((jstring) getEnv()->CallStaticObjectMethod (JuceAppActivity, | |||
| JuceAppActivity.getLocaleValue, | |||
| isRegion))); | |||
| } | |||
| } | |||
| //============================================================================== | |||
| SystemStats::OperatingSystemType SystemStats::getOperatingSystemType() | |||
| { | |||
| return Android; | |||
| } | |||
| String SystemStats::getOperatingSystemName() | |||
| { | |||
| return "Android " + AndroidStatsHelpers::getSystemProperty ("os.version"); | |||
| } | |||
| bool SystemStats::isOperatingSystem64Bit() | |||
| { | |||
| #if JUCE_64BIT | |||
| return true; | |||
| #else | |||
| return false; | |||
| #endif | |||
| } | |||
| String SystemStats::getCpuVendor() | |||
| { | |||
| return AndroidStatsHelpers::getSystemProperty ("os.arch"); | |||
| } | |||
| int SystemStats::getCpuSpeedInMegaherz() | |||
| { | |||
| return 0; // TODO | |||
| } | |||
| int SystemStats::getMemorySizeInMegabytes() | |||
| { | |||
| #if __ANDROID_API__ >= 9 | |||
| struct sysinfo sysi; | |||
| if (sysinfo (&sysi) == 0) | |||
| return (sysi.totalram * sysi.mem_unit / (1024 * 1024)); | |||
| #endif | |||
| return 0; | |||
| } | |||
| int SystemStats::getPageSize() | |||
| { | |||
| return sysconf (_SC_PAGESIZE); | |||
| } | |||
| //============================================================================== | |||
| String SystemStats::getLogonName() | |||
| { | |||
| const char* user = getenv ("USER"); | |||
| if (user == 0) | |||
| { | |||
| struct passwd* const pw = getpwuid (getuid()); | |||
| if (pw != 0) | |||
| user = pw->pw_name; | |||
| } | |||
| return CharPointer_UTF8 (user); | |||
| } | |||
| String SystemStats::getFullUserName() | |||
| { | |||
| return getLogonName(); | |||
| } | |||
| String SystemStats::getComputerName() | |||
| { | |||
| char name [256] = { 0 }; | |||
| if (gethostname (name, sizeof (name) - 1) == 0) | |||
| return name; | |||
| return String::empty; | |||
| } | |||
| String SystemStats::getUserLanguage() { return AndroidStatsHelpers::getLocaleValue (false); } | |||
| String SystemStats::getUserRegion() { return AndroidStatsHelpers::getLocaleValue (true); } | |||
| String SystemStats::getDisplayLanguage() { return getUserLanguage(); } | |||
| //============================================================================== | |||
| void CPUInformation::initialise() noexcept | |||
| { | |||
| numCpus = jmax (1, sysconf (_SC_NPROCESSORS_ONLN)); | |||
| } | |||
| //============================================================================== | |||
| uint32 juce_millisecondsSinceStartup() noexcept | |||
| { | |||
| timespec t; | |||
| clock_gettime (CLOCK_MONOTONIC, &t); | |||
| return t.tv_sec * 1000 + t.tv_nsec / 1000000; | |||
| } | |||
| 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; | |||
| } | |||
| @@ -0,0 +1,81 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the juce_core module of the JUCE library. | |||
| Copyright (c) 2013 - Raw Material Software Ltd. | |||
| 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 THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||
| TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||
| NO EVENT SHALL THE AUTHOR 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. | |||
| ------------------------------------------------------------------------------ | |||
| NOTE! This permissive ISC license applies ONLY to files within the juce_core module! | |||
| All other JUCE modules are covered by a dual GPL/commercial license, so if you are | |||
| using any other modules, be sure to check that you also comply with their license. | |||
| For more details, visit www.juce.com | |||
| ============================================================================== | |||
| */ | |||
| /* | |||
| 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 | |||
| void 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); | |||
| } | |||
| void Process::terminate() | |||
| { | |||
| std::exit (EXIT_FAILURE); | |||
| } | |||
| JUCE_API bool JUCE_CALLTYPE juce_isRunningUnderDebugger() | |||
| { | |||
| return false; | |||
| } | |||
| JUCE_API bool JUCE_CALLTYPE Process::isRunningUnderDebugger() | |||
| { | |||
| return juce_isRunningUnderDebugger(); | |||
| } | |||
| void Process::raisePrivilege() {} | |||
| void Process::lowerPrivilege() {} | |||
| @@ -0,0 +1,378 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the juce_core module of the JUCE library. | |||
| Copyright (c) 2013 - Raw Material Software Ltd. | |||
| 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 THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||
| TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||
| NO EVENT SHALL THE AUTHOR 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. | |||
| ------------------------------------------------------------------------------ | |||
| NOTE! This permissive ISC license applies ONLY to files within the juce_core module! | |||
| All other JUCE modules are covered by a dual GPL/commercial license, so if you are | |||
| using any other modules, be sure to check that you also comply with their license. | |||
| For more details, visit www.juce.com | |||
| ============================================================================== | |||
| */ | |||
| enum | |||
| { | |||
| U_ISOFS_SUPER_MAGIC = 0x9660, // linux/iso_fs.h | |||
| U_MSDOS_SUPER_MAGIC = 0x4d44, // linux/msdos_fs.h | |||
| U_NFS_SUPER_MAGIC = 0x6969, // linux/nfs_fs.h | |||
| U_SMB_SUPER_MAGIC = 0x517B // linux/smb_fs.h | |||
| }; | |||
| //============================================================================== | |||
| 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::isOnCDRomDrive() const | |||
| { | |||
| struct statfs buf; | |||
| return statfs (getFullPathName().toUTF8(), &buf) == 0 | |||
| && buf.f_type == (short) U_ISOFS_SUPER_MAGIC; | |||
| } | |||
| bool File::isOnHardDisk() const | |||
| { | |||
| struct statfs buf; | |||
| if (statfs (getFullPathName().toUTF8(), &buf) == 0) | |||
| { | |||
| switch (buf.f_type) | |||
| { | |||
| case U_ISOFS_SUPER_MAGIC: // CD-ROM | |||
| case U_MSDOS_SUPER_MAGIC: // Probably floppy (but could be mounted FAT filesystem) | |||
| case U_NFS_SUPER_MAGIC: // Network NFS | |||
| case U_SMB_SUPER_MAGIC: // Network Samba | |||
| return false; | |||
| default: | |||
| // Assume anything else is a hard-disk (but note it could | |||
| // be a RAM disk. There isn't a good way of determining | |||
| // this for sure) | |||
| return true; | |||
| } | |||
| } | |||
| // Assume so if this fails for some reason | |||
| return true; | |||
| } | |||
| bool File::isOnRemovableDrive() const | |||
| { | |||
| jassertfalse; // xxx not implemented for linux! | |||
| return false; | |||
| } | |||
| bool File::isHidden() const | |||
| { | |||
| return getFileName().startsWithChar ('.'); | |||
| } | |||
| //============================================================================== | |||
| namespace | |||
| { | |||
| File juce_readlink (const String& file, const File& defaultFile) | |||
| { | |||
| const size_t size = 8192; | |||
| HeapBlock<char> buffer; | |||
| buffer.malloc (size + 4); | |||
| const size_t numBytes = readlink (file.toUTF8(), buffer, size); | |||
| if (numBytes > 0 && numBytes <= size) | |||
| return File (file).getSiblingFile (String::fromUTF8 (buffer, (int) numBytes)); | |||
| return defaultFile; | |||
| } | |||
| } | |||
| File File::getLinkedTarget() const | |||
| { | |||
| return juce_readlink (getFullPathName().toUTF8(), *this); | |||
| } | |||
| //============================================================================== | |||
| static File resolveXDGFolder (const char* const type, const char* const fallbackFolder) | |||
| { | |||
| StringArray confLines; | |||
| File ("~/.config/user-dirs.dirs").readLines (confLines); | |||
| for (int i = 0; i < confLines.size(); ++i) | |||
| { | |||
| const String line (confLines[i].trimStart()); | |||
| if (line.startsWith (type)) | |||
| { | |||
| // eg. resolve XDG_MUSIC_DIR="$HOME/Music" to /home/user/Music | |||
| const File f (line.replace ("$HOME", File ("~").getFullPathName()) | |||
| .fromFirstOccurrenceOf ("=", false, false) | |||
| .trim().unquoted()); | |||
| if (f.isDirectory()) | |||
| return f; | |||
| } | |||
| } | |||
| return File (fallbackFolder); | |||
| } | |||
| const char* const* juce_argv = nullptr; | |||
| int juce_argc = 0; | |||
| File File::getSpecialLocation (const SpecialLocationType type) | |||
| { | |||
| switch (type) | |||
| { | |||
| case userHomeDirectory: | |||
| { | |||
| const char* homeDir = getenv ("HOME"); | |||
| if (homeDir == nullptr) | |||
| { | |||
| struct passwd* const pw = getpwuid (getuid()); | |||
| if (pw != nullptr) | |||
| homeDir = pw->pw_dir; | |||
| } | |||
| return File (CharPointer_UTF8 (homeDir)); | |||
| } | |||
| case userDocumentsDirectory: return resolveXDGFolder ("XDG_DOCUMENTS_DIR", "~"); | |||
| case userMusicDirectory: return resolveXDGFolder ("XDG_MUSIC_DIR", "~"); | |||
| case userMoviesDirectory: return resolveXDGFolder ("XDG_VIDEOS_DIR", "~"); | |||
| case userPicturesDirectory: return resolveXDGFolder ("XDG_PICTURES_DIR", "~"); | |||
| case userDesktopDirectory: return resolveXDGFolder ("XDG_DESKTOP_DIR", "~/Desktop"); | |||
| case userApplicationDataDirectory: return File ("~"); | |||
| case commonApplicationDataDirectory: return File ("/var"); | |||
| case globalApplicationsDirectory: return File ("/usr"); | |||
| case tempDirectory: | |||
| { | |||
| File tmp ("/var/tmp"); | |||
| if (! tmp.isDirectory()) | |||
| { | |||
| tmp = "/tmp"; | |||
| if (! tmp.isDirectory()) | |||
| tmp = File::getCurrentWorkingDirectory(); | |||
| } | |||
| return tmp; | |||
| } | |||
| case invokedExecutableFile: | |||
| if (juce_argv != nullptr && juce_argc > 0) | |||
| return File (CharPointer_UTF8 (juce_argv[0])); | |||
| // deliberate fall-through... | |||
| case currentExecutableFile: | |||
| case currentApplicationFile: | |||
| return juce_getExecutableFile(); | |||
| case hostApplicationPath: | |||
| return juce_readlink ("/proc/self/exe", juce_getExecutableFile()); | |||
| default: | |||
| jassertfalse; // unknown type? | |||
| break; | |||
| } | |||
| return File::nonexistent; | |||
| } | |||
| //============================================================================== | |||
| String File::getVersion() const | |||
| { | |||
| return String::empty; // xxx not yet implemented | |||
| } | |||
| //============================================================================== | |||
| bool File::moveToTrash() const | |||
| { | |||
| if (! exists()) | |||
| return true; | |||
| File trashCan ("~/.Trash"); | |||
| if (! trashCan.isDirectory()) | |||
| trashCan = "~/.local/share/Trash/files"; | |||
| if (! trashCan.isDirectory()) | |||
| return false; | |||
| return moveFileTo (trashCan.getNonexistentChildFile (getFileNameWithoutExtension(), | |||
| getFileExtension())); | |||
| } | |||
| //============================================================================== | |||
| class DirectoryIterator::NativeIterator::Pimpl | |||
| { | |||
| public: | |||
| Pimpl (const File& directory, const String& wildCard_) | |||
| : parentDir (File::addTrailingSeparator (directory.getFullPathName())), | |||
| wildCard (wildCard_), | |||
| 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& wildCard) | |||
| : pimpl (new DirectoryIterator::NativeIterator::Pimpl (directory, wildCard)) | |||
| { | |||
| } | |||
| DirectoryIterator::NativeIterator::~NativeIterator() | |||
| { | |||
| } | |||
| bool DirectoryIterator::NativeIterator::next (String& filenameFound, | |||
| bool* const isDir, bool* const isHidden, int64* const fileSize, | |||
| Time* const modTime, Time* const creationTime, bool* const isReadOnly) | |||
| { | |||
| return pimpl->next (filenameFound, isDir, isHidden, fileSize, modTime, creationTime, isReadOnly); | |||
| } | |||
| //============================================================================== | |||
| static bool isFileExecutable (const String& filename) | |||
| { | |||
| juce_statStruct info; | |||
| return juce_stat (filename, info) | |||
| && S_ISREG (info.st_mode) | |||
| && access (filename.toUTF8(), X_OK) == 0; | |||
| } | |||
| bool Process::openDocument (const String& fileName, const String& parameters) | |||
| { | |||
| String cmdString (fileName.replace (" ", "\\ ",false)); | |||
| cmdString << " " << parameters; | |||
| if (URL::isProbablyAWebsiteURL (fileName) | |||
| || cmdString.startsWithIgnoreCase ("file:") | |||
| || URL::isProbablyAnEmailAddress (fileName) | |||
| || File::createFileWithoutCheckingPath (fileName).isDirectory() | |||
| || ! isFileExecutable (fileName)) | |||
| { | |||
| // create a command that tries to launch a bunch of likely browsers | |||
| const char* const browserNames[] = { "xdg-open", "/etc/alternatives/x-www-browser", "firefox", "mozilla", | |||
| "google-chrome", "chromium-browser", "opera", "konqueror" }; | |||
| StringArray cmdLines; | |||
| for (int i = 0; i < numElementsInArray (browserNames); ++i) | |||
| cmdLines.add (String (browserNames[i]) + " " + cmdString.trim().quoted()); | |||
| cmdString = cmdLines.joinIntoString (" || "); | |||
| } | |||
| const char* const argv[4] = { "/bin/sh", "-c", cmdString.toUTF8(), 0 }; | |||
| const int cpid = fork(); | |||
| if (cpid == 0) | |||
| { | |||
| setsid(); | |||
| // Child process | |||
| execve (argv[0], (char**) argv, environ); | |||
| exit (0); | |||
| } | |||
| return cpid >= 0; | |||
| } | |||
| void File::revealToUser() const | |||
| { | |||
| if (isDirectory()) | |||
| startAsProcess(); | |||
| else if (getParentDirectory().exists()) | |||
| getParentDirectory().startAsProcess(); | |||
| } | |||
| @@ -0,0 +1,462 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the juce_core module of the JUCE library. | |||
| Copyright (c) 2013 - Raw Material Software Ltd. | |||
| 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 THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||
| TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||
| NO EVENT SHALL THE AUTHOR 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. | |||
| ------------------------------------------------------------------------------ | |||
| NOTE! This permissive ISC license applies ONLY to files within the juce_core module! | |||
| All other JUCE modules are covered by a dual GPL/commercial license, so if you are | |||
| using any other modules, be sure to check that you also comply with their license. | |||
| For more details, visit www.juce.com | |||
| ============================================================================== | |||
| */ | |||
| void MACAddress::findAllAddresses (Array<MACAddress>& result) | |||
| { | |||
| const int s = socket (AF_INET, SOCK_DGRAM, 0); | |||
| if (s != -1) | |||
| { | |||
| char buf [1024]; | |||
| struct ifconf ifc; | |||
| ifc.ifc_len = sizeof (buf); | |||
| ifc.ifc_buf = buf; | |||
| ioctl (s, SIOCGIFCONF, &ifc); | |||
| for (unsigned int i = 0; i < ifc.ifc_len / sizeof (struct ifreq); ++i) | |||
| { | |||
| struct ifreq ifr; | |||
| strcpy (ifr.ifr_name, ifc.ifc_req[i].ifr_name); | |||
| if (ioctl (s, SIOCGIFFLAGS, &ifr) == 0 | |||
| && (ifr.ifr_flags & IFF_LOOPBACK) == 0 | |||
| && ioctl (s, SIOCGIFHWADDR, &ifr) == 0) | |||
| { | |||
| result.addIfNotAlreadyThere (MACAddress ((const uint8*) ifr.ifr_hwaddr.sa_data)); | |||
| } | |||
| } | |||
| close (s); | |||
| } | |||
| } | |||
| bool Process::openEmailWithAttachments (const String& /* targetEmailAddress */, | |||
| const String& /* emailSubject */, | |||
| const String& /* bodyText */, | |||
| const StringArray& /* filesToAttach */) | |||
| { | |||
| jassertfalse; // xxx todo | |||
| return false; | |||
| } | |||
| //============================================================================== | |||
| class WebInputStream : public InputStream | |||
| { | |||
| public: | |||
| WebInputStream (const String& address_, bool isPost_, const MemoryBlock& postData_, | |||
| URL::OpenStreamProgressCallback* progressCallback, void* progressCallbackContext, | |||
| const String& headers_, int timeOutMs_, StringPairArray* responseHeaders) | |||
| : socketHandle (-1), levelsOfRedirection (0), | |||
| address (address_), headers (headers_), postData (postData_), position (0), | |||
| finished (false), isPost (isPost_), timeOutMs (timeOutMs_) | |||
| { | |||
| createConnection (progressCallback, progressCallbackContext); | |||
| if (responseHeaders != nullptr && ! isError()) | |||
| { | |||
| for (int i = 0; i < headerLines.size(); ++i) | |||
| { | |||
| const String& headersEntry = headerLines[i]; | |||
| const String key (headersEntry.upToFirstOccurrenceOf (": ", false, false)); | |||
| const String value (headersEntry.fromFirstOccurrenceOf (": ", false, false)); | |||
| const String previousValue ((*responseHeaders) [key]); | |||
| responseHeaders->set (key, previousValue.isEmpty() ? value : (previousValue + "," + value)); | |||
| } | |||
| } | |||
| } | |||
| ~WebInputStream() | |||
| { | |||
| closeSocket(); | |||
| } | |||
| //============================================================================== | |||
| bool isError() const { return socketHandle < 0; } | |||
| bool isExhausted() override { return finished; } | |||
| int64 getPosition() override { return position; } | |||
| int64 getTotalLength() override | |||
| { | |||
| //xxx to do | |||
| return -1; | |||
| } | |||
| int read (void* buffer, int bytesToRead) override | |||
| { | |||
| if (finished || isError()) | |||
| return 0; | |||
| fd_set readbits; | |||
| FD_ZERO (&readbits); | |||
| FD_SET (socketHandle, &readbits); | |||
| struct timeval tv; | |||
| tv.tv_sec = jmax (1, timeOutMs / 1000); | |||
| tv.tv_usec = 0; | |||
| if (select (socketHandle + 1, &readbits, 0, 0, &tv) <= 0) | |||
| return 0; // (timeout) | |||
| const int bytesRead = jmax (0, (int) recv (socketHandle, buffer, bytesToRead, MSG_WAITALL)); | |||
| if (bytesRead == 0) | |||
| finished = true; | |||
| position += bytesRead; | |||
| return bytesRead; | |||
| } | |||
| bool setPosition (int64 wantedPos) override | |||
| { | |||
| if (isError()) | |||
| return false; | |||
| if (wantedPos != position) | |||
| { | |||
| finished = false; | |||
| if (wantedPos < position) | |||
| { | |||
| closeSocket(); | |||
| position = 0; | |||
| createConnection (0, 0); | |||
| } | |||
| skipNextBytes (wantedPos - position); | |||
| } | |||
| return true; | |||
| } | |||
| //============================================================================== | |||
| private: | |||
| int socketHandle, levelsOfRedirection; | |||
| StringArray headerLines; | |||
| String address, headers; | |||
| MemoryBlock postData; | |||
| int64 position; | |||
| bool finished; | |||
| const bool isPost; | |||
| const int timeOutMs; | |||
| void closeSocket() | |||
| { | |||
| if (socketHandle >= 0) | |||
| close (socketHandle); | |||
| socketHandle = -1; | |||
| levelsOfRedirection = 0; | |||
| } | |||
| void createConnection (URL::OpenStreamProgressCallback* progressCallback, void* progressCallbackContext) | |||
| { | |||
| closeSocket(); | |||
| uint32 timeOutTime = Time::getMillisecondCounter(); | |||
| if (timeOutMs == 0) | |||
| timeOutTime += 60000; | |||
| else if (timeOutMs < 0) | |||
| timeOutTime = 0xffffffff; | |||
| else | |||
| timeOutTime += timeOutMs; | |||
| String hostName, hostPath; | |||
| int hostPort; | |||
| if (! decomposeURL (address, hostName, hostPath, hostPort)) | |||
| return; | |||
| String serverName, proxyName, proxyPath; | |||
| int proxyPort = 0; | |||
| int port = 0; | |||
| const String proxyURL (getenv ("http_proxy")); | |||
| if (proxyURL.startsWithIgnoreCase ("http://")) | |||
| { | |||
| if (! decomposeURL (proxyURL, proxyName, proxyPath, proxyPort)) | |||
| return; | |||
| serverName = proxyName; | |||
| port = proxyPort; | |||
| } | |||
| else | |||
| { | |||
| serverName = hostName; | |||
| port = hostPort; | |||
| } | |||
| struct addrinfo hints; | |||
| zerostruct (hints); | |||
| hints.ai_family = AF_UNSPEC; | |||
| hints.ai_socktype = SOCK_STREAM; | |||
| hints.ai_flags = AI_NUMERICSERV; | |||
| struct addrinfo* result = nullptr; | |||
| if (getaddrinfo (serverName.toUTF8(), String (port).toUTF8(), &hints, &result) != 0 || result == 0) | |||
| return; | |||
| socketHandle = socket (result->ai_family, result->ai_socktype, 0); | |||
| if (socketHandle == -1) | |||
| { | |||
| freeaddrinfo (result); | |||
| return; | |||
| } | |||
| int receiveBufferSize = 16384; | |||
| setsockopt (socketHandle, SOL_SOCKET, SO_RCVBUF, (char*) &receiveBufferSize, sizeof (receiveBufferSize)); | |||
| setsockopt (socketHandle, SOL_SOCKET, SO_KEEPALIVE, 0, 0); | |||
| #if JUCE_MAC | |||
| setsockopt (socketHandle, SOL_SOCKET, SO_NOSIGPIPE, 0, 0); | |||
| #endif | |||
| if (connect (socketHandle, result->ai_addr, result->ai_addrlen) == -1) | |||
| { | |||
| closeSocket(); | |||
| freeaddrinfo (result); | |||
| return; | |||
| } | |||
| freeaddrinfo (result); | |||
| { | |||
| const MemoryBlock requestHeader (createRequestHeader (hostName, hostPort, proxyName, proxyPort, | |||
| hostPath, address, headers, postData, isPost)); | |||
| if (! sendHeader (socketHandle, requestHeader, timeOutTime, progressCallback, progressCallbackContext)) | |||
| { | |||
| closeSocket(); | |||
| return; | |||
| } | |||
| } | |||
| String responseHeader (readResponse (socketHandle, timeOutTime)); | |||
| if (responseHeader.isNotEmpty()) | |||
| { | |||
| headerLines = StringArray::fromLines (responseHeader); | |||
| const int statusCode = responseHeader.fromFirstOccurrenceOf (" ", false, false) | |||
| .substring (0, 3).getIntValue(); | |||
| //int contentLength = findHeaderItem (lines, "Content-Length:").getIntValue(); | |||
| //bool isChunked = findHeaderItem (lines, "Transfer-Encoding:").equalsIgnoreCase ("chunked"); | |||
| String location (findHeaderItem (headerLines, "Location:")); | |||
| if (statusCode >= 300 && statusCode < 400 && location.isNotEmpty()) | |||
| { | |||
| if (! location.startsWithIgnoreCase ("http://")) | |||
| location = "http://" + location; | |||
| if (++levelsOfRedirection <= 3) | |||
| { | |||
| address = location; | |||
| createConnection (progressCallback, progressCallbackContext); | |||
| return; | |||
| } | |||
| } | |||
| else | |||
| { | |||
| levelsOfRedirection = 0; | |||
| return; | |||
| } | |||
| } | |||
| closeSocket(); | |||
| } | |||
| //============================================================================== | |||
| static String readResponse (const int socketHandle, const uint32 timeOutTime) | |||
| { | |||
| int bytesRead = 0, numConsecutiveLFs = 0; | |||
| MemoryBlock buffer (1024, true); | |||
| while (numConsecutiveLFs < 2 && bytesRead < 32768 | |||
| && Time::getMillisecondCounter() <= timeOutTime) | |||
| { | |||
| fd_set readbits; | |||
| FD_ZERO (&readbits); | |||
| FD_SET (socketHandle, &readbits); | |||
| struct timeval tv; | |||
| tv.tv_sec = jmax (1, (int) (timeOutTime - Time::getMillisecondCounter()) / 1000); | |||
| tv.tv_usec = 0; | |||
| if (select (socketHandle + 1, &readbits, 0, 0, &tv) <= 0) | |||
| return String::empty; // (timeout) | |||
| buffer.ensureSize (bytesRead + 8, true); | |||
| char* const dest = (char*) buffer.getData() + bytesRead; | |||
| if (recv (socketHandle, dest, 1, 0) == -1) | |||
| return String::empty; | |||
| const char lastByte = *dest; | |||
| ++bytesRead; | |||
| if (lastByte == '\n') | |||
| ++numConsecutiveLFs; | |||
| else if (lastByte != '\r') | |||
| numConsecutiveLFs = 0; | |||
| } | |||
| const String header (CharPointer_UTF8 ((const char*) buffer.getData())); | |||
| if (header.startsWithIgnoreCase ("HTTP/")) | |||
| return header.trimEnd(); | |||
| return String::empty; | |||
| } | |||
| static void writeValueIfNotPresent (MemoryOutputStream& dest, const String& headers, const String& key, const String& value) | |||
| { | |||
| if (! headers.containsIgnoreCase (key)) | |||
| dest << "\r\n" << key << ' ' << value; | |||
| } | |||
| static void writeHost (MemoryOutputStream& dest, const bool isPost, const String& path, const String& host, const int port) | |||
| { | |||
| dest << (isPost ? "POST " : "GET ") << path << " HTTP/1.0\r\nHost: " << host; | |||
| if (port > 0) | |||
| dest << ':' << port; | |||
| } | |||
| static MemoryBlock createRequestHeader (const String& hostName, const int hostPort, | |||
| const String& proxyName, const int proxyPort, | |||
| const String& hostPath, const String& originalURL, | |||
| const String& userHeaders, const MemoryBlock& postData, | |||
| const bool isPost) | |||
| { | |||
| MemoryOutputStream header; | |||
| if (proxyName.isEmpty()) | |||
| writeHost (header, isPost, hostPath, hostName, hostPort); | |||
| else | |||
| writeHost (header, isPost, originalURL, proxyName, proxyPort); | |||
| writeValueIfNotPresent (header, userHeaders, "User-Agent:", "JUCE/" JUCE_STRINGIFY(JUCE_MAJOR_VERSION) | |||
| "." JUCE_STRINGIFY(JUCE_MINOR_VERSION) | |||
| "." JUCE_STRINGIFY(JUCE_BUILDNUMBER)); | |||
| writeValueIfNotPresent (header, userHeaders, "Connection:", "Close"); | |||
| if (isPost) | |||
| writeValueIfNotPresent (header, userHeaders, "Content-Length:", String ((int) postData.getSize())); | |||
| header << "\r\n" << userHeaders | |||
| << "\r\n" << postData; | |||
| return header.getMemoryBlock(); | |||
| } | |||
| static bool sendHeader (int socketHandle, const MemoryBlock& requestHeader, const uint32 timeOutTime, | |||
| URL::OpenStreamProgressCallback* progressCallback, void* progressCallbackContext) | |||
| { | |||
| size_t totalHeaderSent = 0; | |||
| while (totalHeaderSent < requestHeader.getSize()) | |||
| { | |||
| if (Time::getMillisecondCounter() > timeOutTime) | |||
| return false; | |||
| const int numToSend = jmin (1024, (int) (requestHeader.getSize() - totalHeaderSent)); | |||
| if (send (socketHandle, static_cast <const char*> (requestHeader.getData()) + totalHeaderSent, numToSend, 0) != numToSend) | |||
| return false; | |||
| totalHeaderSent += numToSend; | |||
| if (progressCallback != nullptr && ! progressCallback (progressCallbackContext, totalHeaderSent, requestHeader.getSize())) | |||
| return false; | |||
| } | |||
| return true; | |||
| } | |||
| static bool decomposeURL (const String& url, String& host, String& path, int& port) | |||
| { | |||
| if (! url.startsWithIgnoreCase ("http://")) | |||
| return false; | |||
| const int nextSlash = url.indexOfChar (7, '/'); | |||
| int nextColon = url.indexOfChar (7, ':'); | |||
| if (nextColon > nextSlash && nextSlash > 0) | |||
| nextColon = -1; | |||
| if (nextColon >= 0) | |||
| { | |||
| host = url.substring (7, nextColon); | |||
| if (nextSlash >= 0) | |||
| port = url.substring (nextColon + 1, nextSlash).getIntValue(); | |||
| else | |||
| port = url.substring (nextColon + 1).getIntValue(); | |||
| } | |||
| else | |||
| { | |||
| port = 80; | |||
| if (nextSlash >= 0) | |||
| host = url.substring (7, nextSlash); | |||
| else | |||
| host = url.substring (7); | |||
| } | |||
| if (nextSlash >= 0) | |||
| path = url.substring (nextSlash); | |||
| else | |||
| path = "/"; | |||
| return true; | |||
| } | |||
| static String findHeaderItem (const StringArray& lines, const String& itemName) | |||
| { | |||
| for (int i = 0; i < lines.size(); ++i) | |||
| if (lines[i].startsWithIgnoreCase (itemName)) | |||
| return lines[i].substring (itemName.length()).trim(); | |||
| return String::empty; | |||
| } | |||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WebInputStream) | |||
| }; | |||
| InputStream* URL::createNativeStream (const String& address, bool isPost, const MemoryBlock& postData, | |||
| OpenStreamProgressCallback* progressCallback, void* progressCallbackContext, | |||
| const String& headers, const int timeOutMs, StringPairArray* responseHeaders) | |||
| { | |||
| ScopedPointer <WebInputStream> wi (new WebInputStream (address, isPost, postData, | |||
| progressCallback, progressCallbackContext, | |||
| headers, timeOutMs, responseHeaders)); | |||
| return wi->isError() ? nullptr : wi.release(); | |||
| } | |||
| @@ -0,0 +1,181 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the juce_core module of the JUCE library. | |||
| Copyright (c) 2013 - Raw Material Software Ltd. | |||
| 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 THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||
| TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||
| NO EVENT SHALL THE AUTHOR 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. | |||
| ------------------------------------------------------------------------------ | |||
| NOTE! This permissive ISC license applies ONLY to files within the juce_core module! | |||
| All other JUCE modules are covered by a dual GPL/commercial license, so if you are | |||
| using any other modules, be sure to check that you also comply with their license. | |||
| For more details, visit www.juce.com | |||
| ============================================================================== | |||
| */ | |||
| void Logger::outputDebugString (const String& text) | |||
| { | |||
| std::cerr << text << std::endl; | |||
| } | |||
| //============================================================================== | |||
| SystemStats::OperatingSystemType SystemStats::getOperatingSystemType() | |||
| { | |||
| return Linux; | |||
| } | |||
| String SystemStats::getOperatingSystemName() | |||
| { | |||
| return "Linux"; | |||
| } | |||
| bool SystemStats::isOperatingSystem64Bit() | |||
| { | |||
| #if JUCE_64BIT | |||
| return true; | |||
| #else | |||
| //xxx not sure how to find this out?.. | |||
| return false; | |||
| #endif | |||
| } | |||
| //============================================================================== | |||
| namespace LinuxStatsHelpers | |||
| { | |||
| String getCpuInfo (const char* const key) | |||
| { | |||
| StringArray lines; | |||
| File ("/proc/cpuinfo").readLines (lines); | |||
| for (int i = lines.size(); --i >= 0;) // (NB - it's important that this runs in reverse order) | |||
| if (lines[i].startsWithIgnoreCase (key)) | |||
| return lines[i].fromFirstOccurrenceOf (":", false, false).trim(); | |||
| return String::empty; | |||
| } | |||
| } | |||
| String SystemStats::getCpuVendor() | |||
| { | |||
| return LinuxStatsHelpers::getCpuInfo ("vendor_id"); | |||
| } | |||
| int SystemStats::getCpuSpeedInMegaherz() | |||
| { | |||
| return roundToInt (LinuxStatsHelpers::getCpuInfo ("cpu MHz").getFloatValue()); | |||
| } | |||
| int SystemStats::getMemorySizeInMegabytes() | |||
| { | |||
| struct sysinfo sysi; | |||
| if (sysinfo (&sysi) == 0) | |||
| return sysi.totalram * sysi.mem_unit / (1024 * 1024); | |||
| return 0; | |||
| } | |||
| int SystemStats::getPageSize() | |||
| { | |||
| return sysconf (_SC_PAGESIZE); | |||
| } | |||
| //============================================================================== | |||
| String SystemStats::getLogonName() | |||
| { | |||
| const char* user = getenv ("USER"); | |||
| if (user == nullptr) | |||
| if (passwd* const pw = getpwuid (getuid())) | |||
| user = pw->pw_name; | |||
| return CharPointer_UTF8 (user); | |||
| } | |||
| String SystemStats::getFullUserName() | |||
| { | |||
| return getLogonName(); | |||
| } | |||
| String SystemStats::getComputerName() | |||
| { | |||
| char name [256] = { 0 }; | |||
| if (gethostname (name, sizeof (name) - 1) == 0) | |||
| return name; | |||
| return String::empty; | |||
| } | |||
| static String getLocaleValue (nl_item key) | |||
| { | |||
| const char* oldLocale = ::setlocale (LC_ALL, ""); | |||
| String result (String::fromUTF8 (nl_langinfo (key))); | |||
| ::setlocale (LC_ALL, oldLocale); | |||
| return result; | |||
| } | |||
| String SystemStats::getUserLanguage() { return getLocaleValue (_NL_IDENTIFICATION_LANGUAGE); } | |||
| String SystemStats::getUserRegion() { return getLocaleValue (_NL_IDENTIFICATION_TERRITORY); } | |||
| String SystemStats::getDisplayLanguage() { return getUserLanguage(); } | |||
| //============================================================================== | |||
| void CPUInformation::initialise() noexcept | |||
| { | |||
| const String flags (LinuxStatsHelpers::getCpuInfo ("flags")); | |||
| hasMMX = flags.contains ("mmx"); | |||
| hasSSE = flags.contains ("sse"); | |||
| hasSSE2 = flags.contains ("sse2"); | |||
| hasSSE3 = flags.contains ("sse3"); | |||
| has3DNow = flags.contains ("3dnow"); | |||
| numCpus = LinuxStatsHelpers::getCpuInfo ("processor").getIntValue() + 1; | |||
| } | |||
| //============================================================================== | |||
| uint32 juce_millisecondsSinceStartup() noexcept | |||
| { | |||
| timespec t; | |||
| clock_gettime (CLOCK_MONOTONIC, &t); | |||
| return t.tv_sec * 1000 + t.tv_nsec / 1000000; | |||
| } | |||
| 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 | |||
| { | |||
| timeval t; | |||
| t.tv_sec = millisSinceEpoch / 1000; | |||
| t.tv_usec = (millisSinceEpoch - t.tv_sec * 1000) * 1000; | |||
| return settimeofday (&t, 0) == 0; | |||
| } | |||
| @@ -0,0 +1,94 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the juce_core module of the JUCE library. | |||
| Copyright (c) 2013 - Raw Material Software Ltd. | |||
| 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 THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||
| TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||
| NO EVENT SHALL THE AUTHOR 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. | |||
| ------------------------------------------------------------------------------ | |||
| NOTE! This permissive ISC license applies ONLY to files within the juce_core module! | |||
| All other JUCE modules are covered by a dual GPL/commercial license, so if you are | |||
| using any other modules, be sure to check that you also comply with their license. | |||
| For more details, visit www.juce.com | |||
| ============================================================================== | |||
| */ | |||
| /* | |||
| Note that a lot of methods that you'd expect to find in this file actually | |||
| live in juce_posix_SharedCode.h! | |||
| */ | |||
| //============================================================================== | |||
| void Process::setPriority (const ProcessPriority prior) | |||
| { | |||
| const int policy = (prior <= NormalPriority) ? SCHED_OTHER : SCHED_RR; | |||
| const int minp = sched_get_priority_min (policy); | |||
| const int maxp = sched_get_priority_max (policy); | |||
| struct sched_param param; | |||
| switch (prior) | |||
| { | |||
| case LowPriority: | |||
| case NormalPriority: param.sched_priority = 0; break; | |||
| case HighPriority: param.sched_priority = minp + (maxp - minp) / 4; break; | |||
| case RealtimePriority: param.sched_priority = minp + (3 * (maxp - minp) / 4); break; | |||
| default: jassertfalse; break; | |||
| } | |||
| pthread_setschedparam (pthread_self(), policy, ¶m); | |||
| } | |||
| void Process::terminate() | |||
| { | |||
| std::exit (EXIT_FAILURE); | |||
| } | |||
| JUCE_API bool JUCE_CALLTYPE juce_isRunningUnderDebugger() | |||
| { | |||
| #if JUCE_BSD | |||
| return false; | |||
| #else | |||
| static char testResult = 0; | |||
| if (testResult == 0) | |||
| { | |||
| testResult = (char) ptrace (PT_TRACE_ME, 0, 0, 0); | |||
| if (testResult >= 0) | |||
| { | |||
| ptrace (PT_DETACH, 0, (caddr_t) 1, 0); | |||
| testResult = 1; | |||
| } | |||
| } | |||
| return testResult < 0; | |||
| #endif | |||
| } | |||
| JUCE_API bool JUCE_CALLTYPE Process::isRunningUnderDebugger() | |||
| { | |||
| return juce_isRunningUnderDebugger(); | |||
| } | |||
| static void swapUserAndEffectiveUser() | |||
| { | |||
| (void) setreuid (geteuid(), getuid()); | |||
| (void) setregid (getegid(), getgid()); | |||
| } | |||
| void Process::raisePrivilege() { if (geteuid() != 0 && getuid() == 0) swapUserAndEffectiveUser(); } | |||
| void Process::lowerPrivilege() { if (geteuid() == 0 && getuid() != 0) swapUserAndEffectiveUser(); } | |||
| @@ -0,0 +1,485 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the juce_core module of the JUCE library. | |||
| Copyright (c) 2013 - Raw Material Software Ltd. | |||
| 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 THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||
| TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||
| NO EVENT SHALL THE AUTHOR 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. | |||
| ------------------------------------------------------------------------------ | |||
| NOTE! This permissive ISC license applies ONLY to files within the juce_core module! | |||
| All other JUCE modules are covered by a dual GPL/commercial license, so if you are | |||
| using any other modules, be sure to check that you also comply with their license. | |||
| For more details, visit www.juce.com | |||
| ============================================================================== | |||
| */ | |||
| /* | |||
| Note that a lot of methods that you'd expect to find in this file actually | |||
| live in juce_posix_SharedCode.h! | |||
| */ | |||
| //============================================================================== | |||
| bool File::copyInternal (const File& dest) const | |||
| { | |||
| JUCE_AUTORELEASEPOOL | |||
| { | |||
| NSFileManager* fm = [NSFileManager defaultManager]; | |||
| return [fm fileExistsAtPath: juceStringToNS (fullPath)] | |||
| #if defined (MAC_OS_X_VERSION_10_6) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6 | |||
| && [fm copyItemAtPath: juceStringToNS (fullPath) | |||
| toPath: juceStringToNS (dest.getFullPathName()) | |||
| error: nil]; | |||
| #else | |||
| && [fm copyPath: juceStringToNS (fullPath) | |||
| toPath: juceStringToNS (dest.getFullPathName()) | |||
| handler: nil]; | |||
| #endif | |||
| } | |||
| } | |||
| void File::findFileSystemRoots (Array<File>& destArray) | |||
| { | |||
| destArray.add (File ("/")); | |||
| } | |||
| //============================================================================== | |||
| namespace FileHelpers | |||
| { | |||
| static bool isFileOnDriveType (const File& f, const char* const* types) | |||
| { | |||
| struct statfs buf; | |||
| if (juce_doStatFS (f, buf)) | |||
| { | |||
| const String type (buf.f_fstypename); | |||
| while (*types != 0) | |||
| if (type.equalsIgnoreCase (*types++)) | |||
| return true; | |||
| } | |||
| return false; | |||
| } | |||
| static bool isHiddenFile (const String& path) | |||
| { | |||
| #if defined (MAC_OS_X_VERSION_10_6) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_6 | |||
| JUCE_AUTORELEASEPOOL | |||
| { | |||
| NSNumber* hidden = nil; | |||
| NSError* err = nil; | |||
| return [[NSURL fileURLWithPath: juceStringToNS (path)] | |||
| getResourceValue: &hidden forKey: NSURLIsHiddenKey error: &err] | |||
| && [hidden boolValue]; | |||
| } | |||
| #elif JUCE_IOS | |||
| return File (path).getFileName().startsWithChar ('.'); | |||
| #else | |||
| FSRef ref; | |||
| LSItemInfoRecord info; | |||
| return FSPathMakeRefWithOptions ((const UInt8*) path.toRawUTF8(), kFSPathMakeRefDoNotFollowLeafSymlink, &ref, 0) == noErr | |||
| && LSCopyItemInfoForRef (&ref, kLSRequestBasicFlagsOnly, &info) == noErr | |||
| && (info.flags & kLSItemInfoIsInvisible) != 0; | |||
| #endif | |||
| } | |||
| #if JUCE_IOS | |||
| String getIOSSystemLocation (NSSearchPathDirectory type) | |||
| { | |||
| return nsStringToJuce ([NSSearchPathForDirectoriesInDomains (type, NSUserDomainMask, YES) | |||
| objectAtIndex: 0]); | |||
| } | |||
| #endif | |||
| static bool launchExecutable (const String& pathAndArguments) | |||
| { | |||
| const char* const argv[4] = { "/bin/sh", "-c", pathAndArguments.toUTF8(), 0 }; | |||
| const int cpid = fork(); | |||
| if (cpid == 0) | |||
| { | |||
| // Child process | |||
| if (execve (argv[0], (char**) argv, 0) < 0) | |||
| exit (0); | |||
| } | |||
| else | |||
| { | |||
| if (cpid < 0) | |||
| return false; | |||
| } | |||
| return true; | |||
| } | |||
| } | |||
| bool File::isOnCDRomDrive() const | |||
| { | |||
| const char* const cdTypes[] = { "cd9660", "cdfs", "cddafs", "udf", 0 }; | |||
| return FileHelpers::isFileOnDriveType (*this, cdTypes); | |||
| } | |||
| bool File::isOnHardDisk() const | |||
| { | |||
| const char* const nonHDTypes[] = { "nfs", "smbfs", "ramfs", 0 }; | |||
| return ! (isOnCDRomDrive() || FileHelpers::isFileOnDriveType (*this, nonHDTypes)); | |||
| } | |||
| bool File::isOnRemovableDrive() const | |||
| { | |||
| #if JUCE_IOS | |||
| return false; // xxx is this possible? | |||
| #else | |||
| JUCE_AUTORELEASEPOOL | |||
| { | |||
| BOOL removable = false; | |||
| [[NSWorkspace sharedWorkspace] | |||
| getFileSystemInfoForPath: juceStringToNS (getFullPathName()) | |||
| isRemovable: &removable | |||
| isWritable: nil | |||
| isUnmountable: nil | |||
| description: nil | |||
| type: nil]; | |||
| return removable; | |||
| } | |||
| #endif | |||
| } | |||
| bool File::isHidden() const | |||
| { | |||
| return FileHelpers::isHiddenFile (getFullPathName()); | |||
| } | |||
| //============================================================================== | |||
| const char* const* juce_argv = nullptr; | |||
| int juce_argc = 0; | |||
| File File::getSpecialLocation (const SpecialLocationType type) | |||
| { | |||
| JUCE_AUTORELEASEPOOL | |||
| { | |||
| String resultPath; | |||
| switch (type) | |||
| { | |||
| case userHomeDirectory: resultPath = nsStringToJuce (NSHomeDirectory()); break; | |||
| #if JUCE_IOS | |||
| case userDocumentsDirectory: resultPath = FileHelpers::getIOSSystemLocation (NSDocumentDirectory); break; | |||
| case userDesktopDirectory: resultPath = FileHelpers::getIOSSystemLocation (NSDesktopDirectory); break; | |||
| case tempDirectory: | |||
| { | |||
| File tmp (FileHelpers::getIOSSystemLocation (NSCachesDirectory)); | |||
| tmp = tmp.getChildFile (juce_getExecutableFile().getFileNameWithoutExtension()); | |||
| tmp.createDirectory(); | |||
| return tmp.getFullPathName(); | |||
| } | |||
| #else | |||
| case userDocumentsDirectory: resultPath = "~/Documents"; break; | |||
| case userDesktopDirectory: resultPath = "~/Desktop"; break; | |||
| case tempDirectory: | |||
| { | |||
| File tmp ("~/Library/Caches/" + juce_getExecutableFile().getFileNameWithoutExtension()); | |||
| tmp.createDirectory(); | |||
| return tmp.getFullPathName(); | |||
| } | |||
| #endif | |||
| case userMusicDirectory: resultPath = "~/Music"; break; | |||
| case userMoviesDirectory: resultPath = "~/Movies"; break; | |||
| case userPicturesDirectory: resultPath = "~/Pictures"; break; | |||
| case userApplicationDataDirectory: resultPath = "~/Library"; break; | |||
| case commonApplicationDataDirectory: resultPath = "/Library"; break; | |||
| case globalApplicationsDirectory: resultPath = "/Applications"; break; | |||
| case invokedExecutableFile: | |||
| if (juce_argv != nullptr && juce_argc > 0) | |||
| return File (CharPointer_UTF8 (juce_argv[0])); | |||
| // deliberate fall-through... | |||
| case currentExecutableFile: | |||
| return juce_getExecutableFile(); | |||
| case currentApplicationFile: | |||
| { | |||
| const File exe (juce_getExecutableFile()); | |||
| const File parent (exe.getParentDirectory()); | |||
| #if JUCE_IOS | |||
| return parent; | |||
| #else | |||
| return parent.getFullPathName().endsWithIgnoreCase ("Contents/MacOS") | |||
| ? parent.getParentDirectory().getParentDirectory() | |||
| : exe; | |||
| #endif | |||
| } | |||
| case hostApplicationPath: | |||
| { | |||
| unsigned int size = 8192; | |||
| HeapBlock<char> buffer; | |||
| buffer.calloc (size + 8); | |||
| _NSGetExecutablePath (buffer.getData(), &size); | |||
| return String::fromUTF8 (buffer, (int) size); | |||
| } | |||
| default: | |||
| jassertfalse; // unknown type? | |||
| break; | |||
| } | |||
| if (resultPath.isNotEmpty()) | |||
| return File (resultPath.convertToPrecomposedUnicode()); | |||
| } | |||
| return File::nonexistent; | |||
| } | |||
| //============================================================================== | |||
| String File::getVersion() const | |||
| { | |||
| JUCE_AUTORELEASEPOOL | |||
| { | |||
| if (NSBundle* bundle = [NSBundle bundleWithPath: juceStringToNS (getFullPathName())]) | |||
| if (NSDictionary* info = [bundle infoDictionary]) | |||
| if (NSString* name = [info valueForKey: nsStringLiteral ("CFBundleShortVersionString")]) | |||
| return nsStringToJuce (name); | |||
| } | |||
| return String::empty; | |||
| } | |||
| //============================================================================== | |||
| File File::getLinkedTarget() const | |||
| { | |||
| #if JUCE_IOS || (defined (MAC_OS_X_VERSION_10_5) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5) | |||
| NSString* dest = [[NSFileManager defaultManager] destinationOfSymbolicLinkAtPath: juceStringToNS (getFullPathName()) error: nil]; | |||
| #else | |||
| // (the cast here avoids a deprecation warning) | |||
| NSString* dest = [((id) [NSFileManager defaultManager]) pathContentOfSymbolicLinkAtPath: juceStringToNS (getFullPathName())]; | |||
| #endif | |||
| if (dest != nil) | |||
| return File (nsStringToJuce (dest)); | |||
| return *this; | |||
| } | |||
| //============================================================================== | |||
| bool File::moveToTrash() const | |||
| { | |||
| if (! exists()) | |||
| return true; | |||
| #if JUCE_IOS | |||
| return deleteFile(); //xxx is there a trashcan on the iOS? | |||
| #else | |||
| JUCE_AUTORELEASEPOOL | |||
| { | |||
| NSString* p = juceStringToNS (getFullPathName()); | |||
| return [[NSWorkspace sharedWorkspace] | |||
| performFileOperation: NSWorkspaceRecycleOperation | |||
| source: [p stringByDeletingLastPathComponent] | |||
| destination: nsEmptyString() | |||
| files: [NSArray arrayWithObject: [p lastPathComponent]] | |||
| tag: nil ]; | |||
| } | |||
| #endif | |||
| } | |||
| //============================================================================== | |||
| class DirectoryIterator::NativeIterator::Pimpl | |||
| { | |||
| public: | |||
| Pimpl (const File& directory, const String& wildCard_) | |||
| : parentDir (File::addTrailingSeparator (directory.getFullPathName())), | |||
| wildCard (wildCard_), | |||
| enumerator (nil) | |||
| { | |||
| JUCE_AUTORELEASEPOOL | |||
| { | |||
| enumerator = [[[NSFileManager defaultManager] enumeratorAtPath: juceStringToNS (directory.getFullPathName())] retain]; | |||
| } | |||
| } | |||
| ~Pimpl() | |||
| { | |||
| [enumerator release]; | |||
| } | |||
| bool next (String& filenameFound, | |||
| bool* const isDir, bool* const isHidden, int64* const fileSize, | |||
| Time* const modTime, Time* const creationTime, bool* const isReadOnly) | |||
| { | |||
| JUCE_AUTORELEASEPOOL | |||
| { | |||
| const char* wildcardUTF8 = nullptr; | |||
| for (;;) | |||
| { | |||
| NSString* file; | |||
| if (enumerator == nil || (file = [enumerator nextObject]) == nil) | |||
| return false; | |||
| [enumerator skipDescendents]; | |||
| filenameFound = nsStringToJuce (file); | |||
| if (wildcardUTF8 == nullptr) | |||
| wildcardUTF8 = wildCard.toUTF8(); | |||
| if (fnmatch (wildcardUTF8, filenameFound.toUTF8(), FNM_CASEFOLD) != 0) | |||
| continue; | |||
| const String fullPath (parentDir + filenameFound); | |||
| updateStatInfoForFile (fullPath, isDir, fileSize, modTime, creationTime, isReadOnly); | |||
| if (isHidden != nullptr) | |||
| *isHidden = FileHelpers::isHiddenFile (fullPath); | |||
| return true; | |||
| } | |||
| } | |||
| } | |||
| private: | |||
| String parentDir, wildCard; | |||
| NSDirectoryEnumerator* enumerator; | |||
| JUCE_DECLARE_NON_COPYABLE (Pimpl) | |||
| }; | |||
| DirectoryIterator::NativeIterator::NativeIterator (const File& directory, const String& wildcard) | |||
| : pimpl (new DirectoryIterator::NativeIterator::Pimpl (directory, wildcard)) | |||
| { | |||
| } | |||
| DirectoryIterator::NativeIterator::~NativeIterator() | |||
| { | |||
| } | |||
| bool DirectoryIterator::NativeIterator::next (String& filenameFound, | |||
| bool* const isDir, bool* const isHidden, int64* const fileSize, | |||
| Time* const modTime, Time* const creationTime, bool* const isReadOnly) | |||
| { | |||
| return pimpl->next (filenameFound, isDir, isHidden, fileSize, modTime, creationTime, isReadOnly); | |||
| } | |||
| //============================================================================== | |||
| bool Process::openDocument (const String& fileName, const String& parameters) | |||
| { | |||
| #if JUCE_IOS | |||
| return [[UIApplication sharedApplication] openURL: [NSURL URLWithString: juceStringToNS (fileName)]]; | |||
| #else | |||
| JUCE_AUTORELEASEPOOL | |||
| { | |||
| if (parameters.isEmpty()) | |||
| { | |||
| return [[NSWorkspace sharedWorkspace] openFile: juceStringToNS (fileName)] | |||
| || [[NSWorkspace sharedWorkspace] openURL: [NSURL URLWithString: juceStringToNS (fileName)]]; | |||
| } | |||
| bool ok = false; | |||
| const File file (fileName); | |||
| if (file.isBundle()) | |||
| { | |||
| NSMutableArray* urls = [NSMutableArray array]; | |||
| StringArray docs; | |||
| docs.addTokens (parameters, true); | |||
| for (int i = 0; i < docs.size(); ++i) | |||
| [urls addObject: juceStringToNS (docs[i])]; | |||
| ok = [[NSWorkspace sharedWorkspace] openURLs: urls | |||
| withAppBundleIdentifier: [[NSBundle bundleWithPath: juceStringToNS (fileName)] bundleIdentifier] | |||
| options: 0 | |||
| additionalEventParamDescriptor: nil | |||
| launchIdentifiers: nil]; | |||
| } | |||
| else if (file.exists()) | |||
| { | |||
| ok = FileHelpers::launchExecutable ("\"" + fileName + "\" " + parameters); | |||
| } | |||
| return ok; | |||
| } | |||
| #endif | |||
| } | |||
| void File::revealToUser() const | |||
| { | |||
| #if ! JUCE_IOS | |||
| if (exists()) | |||
| [[NSWorkspace sharedWorkspace] selectFile: juceStringToNS (getFullPathName()) inFileViewerRootedAtPath: nsEmptyString()]; | |||
| else if (getParentDirectory().exists()) | |||
| getParentDirectory().revealToUser(); | |||
| #endif | |||
| } | |||
| //============================================================================== | |||
| OSType File::getMacOSType() const | |||
| { | |||
| JUCE_AUTORELEASEPOOL | |||
| { | |||
| #if JUCE_IOS || (defined (MAC_OS_X_VERSION_10_5) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5) | |||
| NSDictionary* fileDict = [[NSFileManager defaultManager] attributesOfItemAtPath: juceStringToNS (getFullPathName()) error: nil]; | |||
| #else | |||
| // (the cast here avoids a deprecation warning) | |||
| NSDictionary* fileDict = [((id) [NSFileManager defaultManager]) fileAttributesAtPath: juceStringToNS (getFullPathName()) traverseLink: NO]; | |||
| #endif | |||
| return [fileDict fileHFSTypeCode]; | |||
| } | |||
| } | |||
| bool File::isBundle() const | |||
| { | |||
| #if JUCE_IOS | |||
| return false; // xxx can't find a sensible way to do this without trying to open the bundle.. | |||
| #else | |||
| JUCE_AUTORELEASEPOOL | |||
| { | |||
| return [[NSWorkspace sharedWorkspace] isFilePackageAtPath: juceStringToNS (getFullPathName())]; | |||
| } | |||
| #endif | |||
| } | |||
| #if JUCE_MAC | |||
| void File::addToDock() const | |||
| { | |||
| // check that it's not already there... | |||
| if (! juce_getOutputFromCommand ("defaults read com.apple.dock persistent-apps").containsIgnoreCase (getFullPathName())) | |||
| { | |||
| juce_runSystemCommand ("defaults write com.apple.dock persistent-apps -array-add \"<dict><key>tile-data</key><dict><key>file-data</key><dict><key>_CFURLString</key><string>" | |||
| + getFullPathName() + "</string><key>_CFURLStringType</key><integer>0</integer></dict></dict></dict>\""); | |||
| juce_runSystemCommand ("osascript -e \"tell application \\\"Dock\\\" to quit\""); | |||
| } | |||
| } | |||
| #endif | |||
| @@ -0,0 +1,432 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the juce_core module of the JUCE library. | |||
| Copyright (c) 2013 - Raw Material Software Ltd. | |||
| 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 THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||
| TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||
| NO EVENT SHALL THE AUTHOR 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. | |||
| ------------------------------------------------------------------------------ | |||
| NOTE! This permissive ISC license applies ONLY to files within the juce_core module! | |||
| All other JUCE modules are covered by a dual GPL/commercial license, so if you are | |||
| using any other modules, be sure to check that you also comply with their license. | |||
| For more details, visit www.juce.com | |||
| ============================================================================== | |||
| */ | |||
| void MACAddress::findAllAddresses (Array<MACAddress>& result) | |||
| { | |||
| ifaddrs* addrs = nullptr; | |||
| if (getifaddrs (&addrs) == 0) | |||
| { | |||
| for (const ifaddrs* cursor = addrs; cursor != nullptr; cursor = cursor->ifa_next) | |||
| { | |||
| sockaddr_storage* sto = (sockaddr_storage*) cursor->ifa_addr; | |||
| if (sto->ss_family == AF_LINK) | |||
| { | |||
| const sockaddr_dl* const sadd = (const sockaddr_dl*) cursor->ifa_addr; | |||
| #ifndef IFT_ETHER | |||
| #define IFT_ETHER 6 | |||
| #endif | |||
| if (sadd->sdl_type == IFT_ETHER) | |||
| result.addIfNotAlreadyThere (MACAddress (((const uint8*) sadd->sdl_data) + sadd->sdl_nlen)); | |||
| } | |||
| } | |||
| freeifaddrs (addrs); | |||
| } | |||
| } | |||
| //============================================================================== | |||
| bool Process::openEmailWithAttachments (const String& targetEmailAddress, | |||
| const String& emailSubject, | |||
| const String& bodyText, | |||
| const StringArray& filesToAttach) | |||
| { | |||
| #if JUCE_IOS | |||
| //xxx probably need to use MFMailComposeViewController | |||
| jassertfalse; | |||
| return false; | |||
| #else | |||
| JUCE_AUTORELEASEPOOL | |||
| { | |||
| String script; | |||
| script << "tell application \"Mail\"\r\n" | |||
| "set newMessage to make new outgoing message with properties {subject:\"" | |||
| << emailSubject.replace ("\"", "\\\"") | |||
| << "\", content:\"" | |||
| << bodyText.replace ("\"", "\\\"") | |||
| << "\" & return & return}\r\n" | |||
| "tell newMessage\r\n" | |||
| "set visible to true\r\n" | |||
| "set sender to \"sdfsdfsdfewf\"\r\n" | |||
| "make new to recipient at end of to recipients with properties {address:\"" | |||
| << targetEmailAddress | |||
| << "\"}\r\n"; | |||
| for (int i = 0; i < filesToAttach.size(); ++i) | |||
| { | |||
| script << "tell content\r\n" | |||
| "make new attachment with properties {file name:\"" | |||
| << filesToAttach[i].replace ("\"", "\\\"") | |||
| << "\"} at after the last paragraph\r\n" | |||
| "end tell\r\n"; | |||
| } | |||
| script << "end tell\r\n" | |||
| "end tell\r\n"; | |||
| NSAppleScript* s = [[NSAppleScript alloc] initWithSource: juceStringToNS (script)]; | |||
| NSDictionary* error = nil; | |||
| const bool ok = [s executeAndReturnError: &error] != nil; | |||
| [s release]; | |||
| return ok; | |||
| } | |||
| #endif | |||
| } | |||
| //============================================================================== | |||
| class URLConnectionState : public Thread | |||
| { | |||
| public: | |||
| URLConnectionState (NSURLRequest* req) | |||
| : Thread ("http connection"), | |||
| contentLength (-1), | |||
| delegate (nil), | |||
| request ([req retain]), | |||
| connection (nil), | |||
| data ([[NSMutableData data] retain]), | |||
| headers (nil), | |||
| initialised (false), | |||
| hasFailed (false), | |||
| hasFinished (false) | |||
| { | |||
| static DelegateClass cls; | |||
| delegate = [cls.createInstance() init]; | |||
| DelegateClass::setState (delegate, this); | |||
| } | |||
| ~URLConnectionState() | |||
| { | |||
| stop(); | |||
| [connection release]; | |||
| [data release]; | |||
| [request release]; | |||
| [headers release]; | |||
| [delegate release]; | |||
| } | |||
| bool start (URL::OpenStreamProgressCallback* callback, void* context) | |||
| { | |||
| startThread(); | |||
| while (isThreadRunning() && ! initialised) | |||
| { | |||
| if (callback != nullptr) | |||
| callback (context, -1, (int) [[request HTTPBody] length]); | |||
| Thread::sleep (1); | |||
| } | |||
| return connection != nil && ! hasFailed; | |||
| } | |||
| void stop() | |||
| { | |||
| [connection cancel]; | |||
| stopThread (10000); | |||
| } | |||
| int read (char* dest, int numBytes) | |||
| { | |||
| int numDone = 0; | |||
| while (numBytes > 0) | |||
| { | |||
| const int available = jmin (numBytes, (int) [data length]); | |||
| if (available > 0) | |||
| { | |||
| const ScopedLock sl (dataLock); | |||
| [data getBytes: dest length: (NSUInteger) available]; | |||
| [data replaceBytesInRange: NSMakeRange (0, (NSUInteger) available) withBytes: nil length: 0]; | |||
| numDone += available; | |||
| numBytes -= available; | |||
| dest += available; | |||
| } | |||
| else | |||
| { | |||
| if (hasFailed || hasFinished) | |||
| break; | |||
| Thread::sleep (1); | |||
| } | |||
| } | |||
| return numDone; | |||
| } | |||
| void didReceiveResponse (NSURLResponse* response) | |||
| { | |||
| { | |||
| const ScopedLock sl (dataLock); | |||
| [data setLength: 0]; | |||
| } | |||
| initialised = true; | |||
| contentLength = [response expectedContentLength]; | |||
| [headers release]; | |||
| headers = nil; | |||
| if ([response isKindOfClass: [NSHTTPURLResponse class]]) | |||
| headers = [[((NSHTTPURLResponse*) response) allHeaderFields] retain]; | |||
| } | |||
| void didFailWithError (NSError* error) | |||
| { | |||
| DBG (nsStringToJuce ([error description])); (void) error; | |||
| hasFailed = true; | |||
| initialised = true; | |||
| signalThreadShouldExit(); | |||
| } | |||
| void didReceiveData (NSData* newData) | |||
| { | |||
| const ScopedLock sl (dataLock); | |||
| [data appendData: newData]; | |||
| initialised = true; | |||
| } | |||
| void didSendBodyData (int /*totalBytesWritten*/, int /*totalBytesExpected*/) | |||
| { | |||
| } | |||
| void finishedLoading() | |||
| { | |||
| hasFinished = true; | |||
| initialised = true; | |||
| signalThreadShouldExit(); | |||
| } | |||
| void run() override | |||
| { | |||
| connection = [[NSURLConnection alloc] initWithRequest: request | |||
| delegate: delegate]; | |||
| while (! threadShouldExit()) | |||
| { | |||
| JUCE_AUTORELEASEPOOL | |||
| { | |||
| [[NSRunLoop currentRunLoop] runUntilDate: [NSDate dateWithTimeIntervalSinceNow: 0.01]]; | |||
| } | |||
| } | |||
| } | |||
| int64 contentLength; | |||
| CriticalSection dataLock; | |||
| NSObject* delegate; | |||
| NSURLRequest* request; | |||
| NSURLConnection* connection; | |||
| NSMutableData* data; | |||
| NSDictionary* headers; | |||
| bool initialised, hasFailed, hasFinished; | |||
| private: | |||
| //============================================================================== | |||
| struct DelegateClass : public ObjCClass <NSObject> | |||
| { | |||
| DelegateClass() : ObjCClass <NSObject> ("JUCEAppDelegate_") | |||
| { | |||
| addIvar <URLConnectionState*> ("state"); | |||
| addMethod (@selector (connection:didReceiveResponse:), didReceiveResponse, "v@:@@"); | |||
| addMethod (@selector (connection:didFailWithError:), didFailWithError, "v@:@@"); | |||
| addMethod (@selector (connection:didReceiveData:), didReceiveData, "v@:@@"); | |||
| addMethod (@selector (connection:didSendBodyData:totalBytesWritten:totalBytesExpectedToWrite:totalBytesExpectedToWrite:), | |||
| connectionDidSendBodyData, "v@:@iii"); | |||
| addMethod (@selector (connectionDidFinishLoading:), connectionDidFinishLoading, "v@:@"); | |||
| registerClass(); | |||
| } | |||
| static void setState (id self, URLConnectionState* state) { object_setInstanceVariable (self, "state", state); } | |||
| static URLConnectionState* getState (id self) { return getIvar<URLConnectionState*> (self, "state"); } | |||
| private: | |||
| static void didReceiveResponse (id self, SEL, NSURLConnection*, NSURLResponse* response) | |||
| { | |||
| getState (self)->didReceiveResponse (response); | |||
| } | |||
| static void didFailWithError (id self, SEL, NSURLConnection*, NSError* error) | |||
| { | |||
| getState (self)->didFailWithError (error); | |||
| } | |||
| static void didReceiveData (id self, SEL, NSURLConnection*, NSData* newData) | |||
| { | |||
| getState (self)->didReceiveData (newData); | |||
| } | |||
| static void connectionDidSendBodyData (id self, SEL, NSURLConnection*, NSInteger, NSInteger totalBytesWritten, NSInteger totalBytesExpected) | |||
| { | |||
| getState (self)->didSendBodyData (totalBytesWritten, totalBytesExpected); | |||
| } | |||
| static void connectionDidFinishLoading (id self, SEL, NSURLConnection*) | |||
| { | |||
| getState (self)->finishedLoading(); | |||
| } | |||
| }; | |||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (URLConnectionState) | |||
| }; | |||
| //============================================================================== | |||
| class WebInputStream : public InputStream | |||
| { | |||
| public: | |||
| WebInputStream (const String& address_, bool isPost_, const MemoryBlock& postData_, | |||
| URL::OpenStreamProgressCallback* progressCallback, void* progressCallbackContext, | |||
| const String& headers_, int timeOutMs_, StringPairArray* responseHeaders) | |||
| : address (address_), headers (headers_), postData (postData_), position (0), | |||
| finished (false), isPost (isPost_), timeOutMs (timeOutMs_) | |||
| { | |||
| JUCE_AUTORELEASEPOOL | |||
| { | |||
| createConnection (progressCallback, progressCallbackContext); | |||
| if (responseHeaders != nullptr && connection != nullptr && connection->headers != nil) | |||
| { | |||
| NSEnumerator* enumerator = [connection->headers keyEnumerator]; | |||
| NSString* key; | |||
| while ((key = [enumerator nextObject]) != nil) | |||
| responseHeaders->set (nsStringToJuce (key), | |||
| nsStringToJuce ((NSString*) [connection->headers objectForKey: key])); | |||
| } | |||
| } | |||
| } | |||
| //============================================================================== | |||
| bool isError() const { return connection == nullptr; } | |||
| int64 getTotalLength() override { return connection == nullptr ? -1 : connection->contentLength; } | |||
| bool isExhausted() override { return finished; } | |||
| int64 getPosition() override { return position; } | |||
| int read (void* buffer, int bytesToRead) override | |||
| { | |||
| jassert (buffer != nullptr && bytesToRead >= 0); | |||
| if (finished || isError()) | |||
| return 0; | |||
| JUCE_AUTORELEASEPOOL | |||
| { | |||
| const int bytesRead = connection->read (static_cast <char*> (buffer), bytesToRead); | |||
| position += bytesRead; | |||
| if (bytesRead == 0) | |||
| finished = true; | |||
| return bytesRead; | |||
| } | |||
| } | |||
| bool setPosition (int64 wantedPos) override | |||
| { | |||
| if (wantedPos != position) | |||
| { | |||
| finished = false; | |||
| if (wantedPos < position) | |||
| { | |||
| connection = nullptr; | |||
| position = 0; | |||
| createConnection (0, 0); | |||
| } | |||
| skipNextBytes (wantedPos - position); | |||
| } | |||
| return true; | |||
| } | |||
| private: | |||
| ScopedPointer<URLConnectionState> connection; | |||
| String address, headers; | |||
| MemoryBlock postData; | |||
| int64 position; | |||
| bool finished; | |||
| const bool isPost; | |||
| const int timeOutMs; | |||
| void createConnection (URL::OpenStreamProgressCallback* progressCallback, | |||
| void* progressCallbackContext) | |||
| { | |||
| jassert (connection == nullptr); | |||
| NSMutableURLRequest* req = [NSMutableURLRequest requestWithURL: [NSURL URLWithString: juceStringToNS (address)] | |||
| cachePolicy: NSURLRequestReloadIgnoringLocalCacheData | |||
| timeoutInterval: timeOutMs <= 0 ? 60.0 : (timeOutMs / 1000.0)]; | |||
| if (req != nil) | |||
| { | |||
| [req setHTTPMethod: nsStringLiteral (isPost ? "POST" : "GET")]; | |||
| //[req setCachePolicy: NSURLRequestReloadIgnoringLocalAndRemoteCacheData]; | |||
| StringArray headerLines; | |||
| headerLines.addLines (headers); | |||
| headerLines.removeEmptyStrings (true); | |||
| for (int i = 0; i < headerLines.size(); ++i) | |||
| { | |||
| const String key (headerLines[i].upToFirstOccurrenceOf (":", false, false).trim()); | |||
| const String value (headerLines[i].fromFirstOccurrenceOf (":", false, false).trim()); | |||
| if (key.isNotEmpty() && value.isNotEmpty()) | |||
| [req addValue: juceStringToNS (value) forHTTPHeaderField: juceStringToNS (key)]; | |||
| } | |||
| if (isPost && postData.getSize() > 0) | |||
| [req setHTTPBody: [NSData dataWithBytes: postData.getData() | |||
| length: postData.getSize()]]; | |||
| connection = new URLConnectionState (req); | |||
| if (! connection->start (progressCallback, progressCallbackContext)) | |||
| connection = nullptr; | |||
| } | |||
| } | |||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WebInputStream) | |||
| }; | |||
| InputStream* URL::createNativeStream (const String& address, bool isPost, const MemoryBlock& postData, | |||
| OpenStreamProgressCallback* progressCallback, void* progressCallbackContext, | |||
| const String& headers, const int timeOutMs, StringPairArray* responseHeaders) | |||
| { | |||
| ScopedPointer <WebInputStream> wi (new WebInputStream (address, isPost, postData, | |||
| progressCallback, progressCallbackContext, | |||
| headers, timeOutMs, responseHeaders)); | |||
| return wi->isError() ? nullptr : wi.release(); | |||
| } | |||
| @@ -0,0 +1,96 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the juce_core module of the JUCE library. | |||
| Copyright (c) 2013 - Raw Material Software Ltd. | |||
| 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 THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||
| TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||
| NO EVENT SHALL THE AUTHOR 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. | |||
| ------------------------------------------------------------------------------ | |||
| NOTE! This permissive ISC license applies ONLY to files within the juce_core module! | |||
| All other JUCE modules are covered by a dual GPL/commercial license, so if you are | |||
| using any other modules, be sure to check that you also comply with their license. | |||
| For more details, visit www.juce.com | |||
| ============================================================================== | |||
| */ | |||
| String String::fromCFString (CFStringRef cfString) | |||
| { | |||
| if (cfString == 0) | |||
| return String::empty; | |||
| CFRange range = { 0, CFStringGetLength (cfString) }; | |||
| HeapBlock <UniChar> u ((size_t) range.length + 1); | |||
| CFStringGetCharacters (cfString, range, u); | |||
| u[range.length] = 0; | |||
| return String (CharPointer_UTF16 ((const CharPointer_UTF16::CharType*) u.getData())); | |||
| } | |||
| CFStringRef String::toCFString() const | |||
| { | |||
| CharPointer_UTF16 utf16 (toUTF16()); | |||
| return CFStringCreateWithCharacters (kCFAllocatorDefault, (const UniChar*) utf16.getAddress(), (CFIndex) utf16.length()); | |||
| } | |||
| String String::convertToPrecomposedUnicode() const | |||
| { | |||
| #if JUCE_IOS | |||
| JUCE_AUTORELEASEPOOL | |||
| { | |||
| return nsStringToJuce ([juceStringToNS (*this) precomposedStringWithCanonicalMapping]); | |||
| } | |||
| #else | |||
| UnicodeMapping map; | |||
| map.unicodeEncoding = CreateTextEncoding (kTextEncodingUnicodeDefault, | |||
| kUnicodeNoSubset, | |||
| kTextEncodingDefaultFormat); | |||
| map.otherEncoding = CreateTextEncoding (kTextEncodingUnicodeDefault, | |||
| kUnicodeCanonicalCompVariant, | |||
| kTextEncodingDefaultFormat); | |||
| map.mappingVersion = kUnicodeUseLatestMapping; | |||
| UnicodeToTextInfo conversionInfo = 0; | |||
| String result; | |||
| if (CreateUnicodeToTextInfo (&map, &conversionInfo) == noErr) | |||
| { | |||
| const size_t bytesNeeded = CharPointer_UTF16::getBytesRequiredFor (getCharPointer()); | |||
| HeapBlock <char> tempOut; | |||
| tempOut.calloc (bytesNeeded + 4); | |||
| ByteCount bytesRead = 0; | |||
| ByteCount outputBufferSize = 0; | |||
| if (ConvertFromUnicodeToText (conversionInfo, | |||
| bytesNeeded, (ConstUniCharArrayPtr) toUTF16().getAddress(), | |||
| kUnicodeDefaultDirectionMask, | |||
| 0, 0, 0, 0, | |||
| bytesNeeded, &bytesRead, | |||
| &outputBufferSize, tempOut) == noErr) | |||
| { | |||
| result = String (CharPointer_UTF16 ((CharPointer_UTF16::CharType*) tempOut.getData())); | |||
| } | |||
| DisposeUnicodeToTextInfo (&conversionInfo); | |||
| } | |||
| return result; | |||
| #endif | |||
| } | |||
| @@ -0,0 +1,292 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the juce_core module of the JUCE library. | |||
| Copyright (c) 2013 - Raw Material Software Ltd. | |||
| 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 THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||
| TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||
| NO EVENT SHALL THE AUTHOR 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. | |||
| ------------------------------------------------------------------------------ | |||
| NOTE! This permissive ISC license applies ONLY to files within the juce_core module! | |||
| All other JUCE modules are covered by a dual GPL/commercial license, so if you are | |||
| using any other modules, be sure to check that you also comply with their license. | |||
| For more details, visit www.juce.com | |||
| ============================================================================== | |||
| */ | |||
| ScopedAutoReleasePool::ScopedAutoReleasePool() | |||
| { | |||
| pool = [[NSAutoreleasePool alloc] init]; | |||
| } | |||
| ScopedAutoReleasePool::~ScopedAutoReleasePool() | |||
| { | |||
| [((NSAutoreleasePool*) pool) release]; | |||
| } | |||
| //============================================================================== | |||
| void Logger::outputDebugString (const String& text) | |||
| { | |||
| // Would prefer to use std::cerr here, but avoiding it for | |||
| // the moment, due to clang JIT linkage problems. | |||
| fputs (text.toRawUTF8(), stderr); | |||
| fputs ("\n", stderr); | |||
| fflush (stderr); | |||
| } | |||
| //============================================================================== | |||
| namespace SystemStatsHelpers | |||
| { | |||
| #if JUCE_INTEL && ! JUCE_NO_INLINE_ASM | |||
| static void doCPUID (uint32& a, uint32& b, uint32& c, uint32& d, uint32 type) | |||
| { | |||
| uint32 la = a, lb = b, lc = c, ld = d; | |||
| asm ("mov %%ebx, %%esi \n\t" | |||
| "cpuid \n\t" | |||
| "xchg %%esi, %%ebx" | |||
| : "=a" (la), "=S" (lb), "=c" (lc), "=d" (ld) : "a" (type) | |||
| #if JUCE_64BIT | |||
| , "b" (lb), "c" (lc), "d" (ld) | |||
| #endif | |||
| ); | |||
| a = la; b = lb; c = lc; d = ld; | |||
| } | |||
| #endif | |||
| } | |||
| //============================================================================== | |||
| void CPUInformation::initialise() noexcept | |||
| { | |||
| #if JUCE_INTEL && ! JUCE_NO_INLINE_ASM | |||
| uint32 a = 0, b = 0, d = 0, c = 0; | |||
| SystemStatsHelpers::doCPUID (a, b, c, d, 1); | |||
| hasMMX = (d & (1u << 23)) != 0; | |||
| hasSSE = (d & (1u << 25)) != 0; | |||
| hasSSE2 = (d & (1u << 26)) != 0; | |||
| has3DNow = (b & (1u << 31)) != 0; | |||
| hasSSE3 = (c & (1u << 0)) != 0; | |||
| #endif | |||
| #if JUCE_IOS || (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5) | |||
| numCpus = (int) [[NSProcessInfo processInfo] activeProcessorCount]; | |||
| #else | |||
| numCpus = (int) MPProcessors(); | |||
| #endif | |||
| } | |||
| #if JUCE_MAC | |||
| struct RLimitInitialiser | |||
| { | |||
| RLimitInitialiser() | |||
| { | |||
| rlimit lim; | |||
| getrlimit (RLIMIT_NOFILE, &lim); | |||
| lim.rlim_cur = lim.rlim_max = RLIM_INFINITY; | |||
| setrlimit (RLIMIT_NOFILE, &lim); | |||
| } | |||
| }; | |||
| static RLimitInitialiser rLimitInitialiser; | |||
| #endif | |||
| //============================================================================== | |||
| #if ! JUCE_IOS | |||
| static String getOSXVersion() | |||
| { | |||
| JUCE_AUTORELEASEPOOL | |||
| { | |||
| NSDictionary* dict = [NSDictionary dictionaryWithContentsOfFile: | |||
| nsStringLiteral ("/System/Library/CoreServices/SystemVersion.plist")]; | |||
| return nsStringToJuce ([dict objectForKey: nsStringLiteral ("ProductVersion")]); | |||
| } | |||
| } | |||
| #endif | |||
| SystemStats::OperatingSystemType SystemStats::getOperatingSystemType() | |||
| { | |||
| #if JUCE_IOS | |||
| return iOS; | |||
| #else | |||
| StringArray parts; | |||
| parts.addTokens (getOSXVersion(), ".", String::empty); | |||
| jassert (parts[0].getIntValue() == 10); | |||
| const int major = parts[1].getIntValue(); | |||
| jassert (major > 2); | |||
| return (OperatingSystemType) (major + MacOSX_10_4 - 4); | |||
| #endif | |||
| } | |||
| String SystemStats::getOperatingSystemName() | |||
| { | |||
| #if JUCE_IOS | |||
| return "iOS " + nsStringToJuce ([[UIDevice currentDevice] systemVersion]); | |||
| #else | |||
| return "Mac OSX " + getOSXVersion(); | |||
| #endif | |||
| } | |||
| bool SystemStats::isOperatingSystem64Bit() | |||
| { | |||
| #if JUCE_IOS | |||
| return false; | |||
| #elif JUCE_64BIT | |||
| return true; | |||
| #else | |||
| return getOperatingSystemType() >= MacOSX_10_6; | |||
| #endif | |||
| } | |||
| int SystemStats::getMemorySizeInMegabytes() | |||
| { | |||
| uint64 mem = 0; | |||
| size_t memSize = sizeof (mem); | |||
| int mib[] = { CTL_HW, HW_MEMSIZE }; | |||
| sysctl (mib, 2, &mem, &memSize, 0, 0); | |||
| return (int) (mem / (1024 * 1024)); | |||
| } | |||
| String SystemStats::getCpuVendor() | |||
| { | |||
| #if JUCE_INTEL && ! JUCE_NO_INLINE_ASM | |||
| uint32 dummy = 0; | |||
| uint32 vendor[4] = { 0 }; | |||
| SystemStatsHelpers::doCPUID (dummy, vendor[0], vendor[2], vendor[1], 0); | |||
| return String (reinterpret_cast <const char*> (vendor), 12); | |||
| #else | |||
| return String::empty; | |||
| #endif | |||
| } | |||
| int SystemStats::getCpuSpeedInMegaherz() | |||
| { | |||
| uint64 speedHz = 0; | |||
| size_t speedSize = sizeof (speedHz); | |||
| int mib[] = { CTL_HW, HW_CPU_FREQ }; | |||
| sysctl (mib, 2, &speedHz, &speedSize, 0, 0); | |||
| #if JUCE_BIG_ENDIAN | |||
| if (speedSize == 4) | |||
| speedHz >>= 32; | |||
| #endif | |||
| return (int) (speedHz / 1000000); | |||
| } | |||
| //============================================================================== | |||
| String SystemStats::getLogonName() | |||
| { | |||
| return nsStringToJuce (NSUserName()); | |||
| } | |||
| String SystemStats::getFullUserName() | |||
| { | |||
| return nsStringToJuce (NSFullUserName()); | |||
| } | |||
| String SystemStats::getComputerName() | |||
| { | |||
| char name [256] = { 0 }; | |||
| if (gethostname (name, sizeof (name) - 1) == 0) | |||
| return String (name).upToLastOccurrenceOf (".local", false, true); | |||
| return String::empty; | |||
| } | |||
| static String getLocaleValue (CFStringRef key) | |||
| { | |||
| CFLocaleRef cfLocale = CFLocaleCopyCurrent(); | |||
| const String result (String::fromCFString ((CFStringRef) CFLocaleGetValue (cfLocale, key))); | |||
| CFRelease (cfLocale); | |||
| return result; | |||
| } | |||
| String SystemStats::getUserLanguage() { return getLocaleValue (kCFLocaleLanguageCode); } | |||
| String SystemStats::getUserRegion() { return getLocaleValue (kCFLocaleCountryCode); } | |||
| String SystemStats::getDisplayLanguage() | |||
| { | |||
| CFArrayRef cfPrefLangs = CFLocaleCopyPreferredLanguages(); | |||
| const String result (String::fromCFString ((CFStringRef) CFArrayGetValueAtIndex (cfPrefLangs, 0))); | |||
| CFRelease (cfPrefLangs); | |||
| return result; | |||
| } | |||
| //============================================================================== | |||
| class HiResCounterHandler | |||
| { | |||
| public: | |||
| HiResCounterHandler() | |||
| { | |||
| mach_timebase_info_data_t timebase; | |||
| (void) mach_timebase_info (&timebase); | |||
| if (timebase.numer % 1000000 == 0) | |||
| { | |||
| numerator = timebase.numer / 1000000; | |||
| denominator = timebase.denom; | |||
| } | |||
| else | |||
| { | |||
| numerator = timebase.numer; | |||
| denominator = timebase.denom * (uint64) 1000000; | |||
| } | |||
| highResTimerFrequency = (timebase.denom * (uint64) 1000000000) / timebase.numer; | |||
| highResTimerToMillisecRatio = numerator / (double) denominator; | |||
| } | |||
| inline uint32 millisecondsSinceStartup() const noexcept | |||
| { | |||
| return (uint32) ((mach_absolute_time() * numerator) / denominator); | |||
| } | |||
| inline double getMillisecondCounterHiRes() const noexcept | |||
| { | |||
| return mach_absolute_time() * highResTimerToMillisecRatio; | |||
| } | |||
| int64 highResTimerFrequency; | |||
| private: | |||
| uint64 numerator, denominator; | |||
| double highResTimerToMillisecRatio; | |||
| }; | |||
| static HiResCounterHandler hiResCounterHandler; | |||
| uint32 juce_millisecondsSinceStartup() noexcept { return hiResCounterHandler.millisecondsSinceStartup(); } | |||
| double Time::getMillisecondCounterHiRes() noexcept { return hiResCounterHandler.getMillisecondCounterHiRes(); } | |||
| int64 Time::getHighResolutionTicksPerSecond() noexcept { return hiResCounterHandler.highResTimerFrequency; } | |||
| int64 Time::getHighResolutionTicks() noexcept { return (int64) mach_absolute_time(); } | |||
| bool Time::setSystemTimeToThisTime() const | |||
| { | |||
| jassertfalse; | |||
| return false; | |||
| } | |||
| //============================================================================== | |||
| int SystemStats::getPageSize() | |||
| { | |||
| return (int) NSPageSize(); | |||
| } | |||
| @@ -0,0 +1,98 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the juce_core module of the JUCE library. | |||
| Copyright (c) 2013 - Raw Material Software Ltd. | |||
| 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 THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||
| TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||
| NO EVENT SHALL THE AUTHOR 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. | |||
| ------------------------------------------------------------------------------ | |||
| NOTE! This permissive ISC license applies ONLY to files within the juce_core module! | |||
| All other JUCE modules are covered by a dual GPL/commercial license, so if you are | |||
| using any other modules, be sure to check that you also comply with their license. | |||
| For more details, visit www.juce.com | |||
| ============================================================================== | |||
| */ | |||
| /* | |||
| Note that a lot of methods that you'd expect to find in this file actually | |||
| live in juce_posix_SharedCode.h! | |||
| */ | |||
| //============================================================================== | |||
| bool Process::isForegroundProcess() | |||
| { | |||
| #if JUCE_MAC | |||
| return [NSApp isActive]; | |||
| #else | |||
| return true; // xxx change this if more than one app is ever possible on iOS! | |||
| #endif | |||
| } | |||
| void Process::makeForegroundProcess() | |||
| { | |||
| #if JUCE_MAC | |||
| [NSApp activateIgnoringOtherApps: YES]; | |||
| #endif | |||
| } | |||
| void Process::hide() | |||
| { | |||
| #if JUCE_MAC | |||
| [NSApp hide: nil]; | |||
| #endif | |||
| } | |||
| void Process::raisePrivilege() | |||
| { | |||
| jassertfalse; | |||
| } | |||
| void Process::lowerPrivilege() | |||
| { | |||
| jassertfalse; | |||
| } | |||
| void Process::terminate() | |||
| { | |||
| std::exit (EXIT_FAILURE); | |||
| } | |||
| void Process::setPriority (ProcessPriority) | |||
| { | |||
| // xxx | |||
| } | |||
| //============================================================================== | |||
| JUCE_API bool JUCE_CALLTYPE juce_isRunningUnderDebugger() | |||
| { | |||
| static char testResult = 0; | |||
| if (testResult == 0) | |||
| { | |||
| struct kinfo_proc info; | |||
| int m[] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, getpid() }; | |||
| size_t sz = sizeof (info); | |||
| sysctl (m, 4, &info, &sz, 0, 0); | |||
| testResult = ((info.kp_proc.p_flag & P_TRACED) != 0) ? 1 : -1; | |||
| } | |||
| return testResult > 0; | |||
| } | |||
| JUCE_API bool JUCE_CALLTYPE Process::isRunningUnderDebugger() | |||
| { | |||
| return juce_isRunningUnderDebugger(); | |||
| } | |||
| @@ -0,0 +1,167 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the juce_core module of the JUCE library. | |||
| Copyright (c) 2013 - Raw Material Software Ltd. | |||
| 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 THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||
| TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||
| NO EVENT SHALL THE AUTHOR 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. | |||
| ------------------------------------------------------------------------------ | |||
| NOTE! This permissive ISC license applies ONLY to files within the juce_core module! | |||
| All other JUCE modules are covered by a dual GPL/commercial license, so if you are | |||
| using any other modules, be sure to check that you also comply with their license. | |||
| For more details, visit www.juce.com | |||
| ============================================================================== | |||
| */ | |||
| #ifndef JUCE_OSX_OBJCHELPERS_H_INCLUDED | |||
| #define JUCE_OSX_OBJCHELPERS_H_INCLUDED | |||
| /* This file contains a few helper functions that are used internally but which | |||
| need to be kept away from the public headers because they use obj-C symbols. | |||
| */ | |||
| namespace | |||
| { | |||
| //============================================================================== | |||
| static inline String nsStringToJuce (NSString* s) | |||
| { | |||
| return CharPointer_UTF8 ([s UTF8String]); | |||
| } | |||
| static inline NSString* juceStringToNS (const String& s) | |||
| { | |||
| return [NSString stringWithUTF8String: s.toUTF8()]; | |||
| } | |||
| static inline NSString* nsStringLiteral (const char* const s) noexcept | |||
| { | |||
| return [NSString stringWithUTF8String: s]; | |||
| } | |||
| static inline NSString* nsEmptyString() noexcept | |||
| { | |||
| return [NSString string]; | |||
| } | |||
| template <typename RectangleType> | |||
| static NSRect makeNSRect (const RectangleType& r) noexcept | |||
| { | |||
| return NSMakeRect (static_cast <CGFloat> (r.getX()), | |||
| static_cast <CGFloat> (r.getY()), | |||
| static_cast <CGFloat> (r.getWidth()), | |||
| static_cast <CGFloat> (r.getHeight())); | |||
| } | |||
| } | |||
| //============================================================================== | |||
| template <typename ObjectType> | |||
| struct NSObjectRetainer | |||
| { | |||
| inline NSObjectRetainer (ObjectType* o) : object (o) { [object retain]; } | |||
| inline ~NSObjectRetainer() { [object release]; } | |||
| ObjectType* object; | |||
| }; | |||
| //============================================================================== | |||
| template <typename SuperclassType> | |||
| struct ObjCClass | |||
| { | |||
| ObjCClass (const char* nameRoot) | |||
| : cls (objc_allocateClassPair ([SuperclassType class], getRandomisedName (nameRoot).toUTF8(), 0)) | |||
| { | |||
| } | |||
| ~ObjCClass() | |||
| { | |||
| objc_disposeClassPair (cls); | |||
| } | |||
| void registerClass() | |||
| { | |||
| objc_registerClassPair (cls); | |||
| } | |||
| SuperclassType* createInstance() const | |||
| { | |||
| return class_createInstance (cls, 0); | |||
| } | |||
| template <typename Type> | |||
| void addIvar (const char* name) | |||
| { | |||
| BOOL b = class_addIvar (cls, name, sizeof (Type), (uint8_t) rint (log2 (sizeof (Type))), @encode (Type)); | |||
| jassert (b); (void) b; | |||
| } | |||
| template <typename FunctionType> | |||
| void addMethod (SEL selector, FunctionType callbackFn, const char* signature) | |||
| { | |||
| BOOL b = class_addMethod (cls, selector, (IMP) callbackFn, signature); | |||
| jassert (b); (void) b; | |||
| } | |||
| template <typename FunctionType> | |||
| void addMethod (SEL selector, FunctionType callbackFn, const char* sig1, const char* sig2) | |||
| { | |||
| addMethod (selector, callbackFn, (String (sig1) + sig2).toUTF8()); | |||
| } | |||
| template <typename FunctionType> | |||
| void addMethod (SEL selector, FunctionType callbackFn, const char* sig1, const char* sig2, const char* sig3) | |||
| { | |||
| addMethod (selector, callbackFn, (String (sig1) + sig2 + sig3).toUTF8()); | |||
| } | |||
| template <typename FunctionType> | |||
| void addMethod (SEL selector, FunctionType callbackFn, const char* sig1, const char* sig2, const char* sig3, const char* sig4) | |||
| { | |||
| addMethod (selector, callbackFn, (String (sig1) + sig2 + sig3 + sig4).toUTF8()); | |||
| } | |||
| void addProtocol (Protocol* protocol) | |||
| { | |||
| BOOL b = class_addProtocol (cls, protocol); | |||
| jassert (b); (void) b; | |||
| } | |||
| static id sendSuperclassMessage (id self, SEL selector) | |||
| { | |||
| objc_super s = { self, [SuperclassType class] }; | |||
| return objc_msgSendSuper (&s, selector); | |||
| } | |||
| template <typename Type> | |||
| static Type getIvar (id self, const char* name) | |||
| { | |||
| void* v = nullptr; | |||
| object_getInstanceVariable (self, name, &v); | |||
| return static_cast <Type> (v); | |||
| } | |||
| Class cls; | |||
| private: | |||
| static String getRandomisedName (const char* root) | |||
| { | |||
| return root + String::toHexString (juce::Random::getSystemRandom().nextInt64()); | |||
| } | |||
| JUCE_DECLARE_NON_COPYABLE (ObjCClass) | |||
| }; | |||
| #endif // JUCE_OSX_OBJCHELPERS_H_INCLUDED | |||
| @@ -0,0 +1,219 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the juce_core module of the JUCE library. | |||
| Copyright (c) 2013 - Raw Material Software Ltd. | |||
| 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 THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||
| TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||
| NO EVENT SHALL THE AUTHOR 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. | |||
| ------------------------------------------------------------------------------ | |||
| NOTE! This permissive ISC license applies ONLY to files within the juce_core module! | |||
| All other JUCE modules are covered by a dual GPL/commercial license, so if you are | |||
| using any other modules, be sure to check that you also comply with their license. | |||
| For more details, visit www.juce.com | |||
| ============================================================================== | |||
| */ | |||
| class NamedPipe::Pimpl | |||
| { | |||
| public: | |||
| Pimpl (const String& pipePath, bool createPipe) | |||
| : pipeInName (pipePath + "_in"), | |||
| pipeOutName (pipePath + "_out"), | |||
| pipeIn (-1), pipeOut (-1), | |||
| createdPipe (createPipe), | |||
| stopReadOperation (false) | |||
| { | |||
| signal (SIGPIPE, signalHandler); | |||
| juce_siginterrupt (SIGPIPE, 1); | |||
| } | |||
| ~Pimpl() | |||
| { | |||
| if (pipeIn != -1) ::close (pipeIn); | |||
| if (pipeOut != -1) ::close (pipeOut); | |||
| if (createdPipe) | |||
| { | |||
| unlink (pipeInName.toUTF8()); | |||
| unlink (pipeOutName.toUTF8()); | |||
| } | |||
| } | |||
| int read (char* destBuffer, int maxBytesToRead, int timeOutMilliseconds) | |||
| { | |||
| const uint32 timeoutEnd = getTimeoutEnd (timeOutMilliseconds); | |||
| if (pipeIn == -1) | |||
| { | |||
| pipeIn = openPipe (createdPipe ? pipeInName : pipeOutName, O_RDWR | O_NONBLOCK, timeoutEnd); | |||
| if (pipeIn == -1) | |||
| return -1; | |||
| } | |||
| int bytesRead = 0; | |||
| while (bytesRead < maxBytesToRead) | |||
| { | |||
| const int bytesThisTime = maxBytesToRead - bytesRead; | |||
| const int numRead = (int) ::read (pipeIn, destBuffer, (size_t) bytesThisTime); | |||
| if (numRead <= 0) | |||
| { | |||
| if (errno != EWOULDBLOCK || stopReadOperation || hasExpired (timeoutEnd)) | |||
| return -1; | |||
| const int maxWaitingTime = 30; | |||
| waitForInput (pipeIn, timeoutEnd == 0 ? maxWaitingTime | |||
| : jmin (maxWaitingTime, | |||
| (int) (timeoutEnd - Time::getMillisecondCounter()))); | |||
| continue; | |||
| } | |||
| bytesRead += numRead; | |||
| destBuffer += numRead; | |||
| } | |||
| return bytesRead; | |||
| } | |||
| int write (const char* sourceBuffer, int numBytesToWrite, int timeOutMilliseconds) | |||
| { | |||
| const uint32 timeoutEnd = getTimeoutEnd (timeOutMilliseconds); | |||
| if (pipeOut == -1) | |||
| { | |||
| pipeOut = openPipe (createdPipe ? pipeOutName : pipeInName, O_WRONLY, timeoutEnd); | |||
| if (pipeOut == -1) | |||
| return -1; | |||
| } | |||
| int bytesWritten = 0; | |||
| while (bytesWritten < numBytesToWrite && ! hasExpired (timeoutEnd)) | |||
| { | |||
| const int bytesThisTime = numBytesToWrite - bytesWritten; | |||
| const int numWritten = (int) ::write (pipeOut, sourceBuffer, (size_t) bytesThisTime); | |||
| if (numWritten <= 0) | |||
| return -1; | |||
| bytesWritten += numWritten; | |||
| sourceBuffer += numWritten; | |||
| } | |||
| return bytesWritten; | |||
| } | |||
| bool createFifos() const | |||
| { | |||
| return (mkfifo (pipeInName .toUTF8(), 0666) == 0 || errno == EEXIST) | |||
| && (mkfifo (pipeOutName.toUTF8(), 0666) == 0 || errno == EEXIST); | |||
| } | |||
| const String pipeInName, pipeOutName; | |||
| int pipeIn, pipeOut; | |||
| const bool createdPipe; | |||
| bool stopReadOperation; | |||
| private: | |||
| static void signalHandler (int) {} | |||
| static uint32 getTimeoutEnd (const int timeOutMilliseconds) | |||
| { | |||
| return timeOutMilliseconds >= 0 ? Time::getMillisecondCounter() + (uint32) timeOutMilliseconds : 0; | |||
| } | |||
| static bool hasExpired (const uint32 timeoutEnd) | |||
| { | |||
| return timeoutEnd != 0 && Time::getMillisecondCounter() >= timeoutEnd; | |||
| } | |||
| int openPipe (const String& name, int flags, const uint32 timeoutEnd) | |||
| { | |||
| for (;;) | |||
| { | |||
| const int p = ::open (name.toUTF8(), flags); | |||
| if (p != -1 || hasExpired (timeoutEnd) || stopReadOperation) | |||
| return p; | |||
| Thread::sleep (2); | |||
| } | |||
| } | |||
| static void waitForInput (const int handle, const int timeoutMsecs) noexcept | |||
| { | |||
| struct timeval timeout; | |||
| timeout.tv_sec = timeoutMsecs / 1000; | |||
| timeout.tv_usec = (timeoutMsecs % 1000) * 1000; | |||
| fd_set rset; | |||
| FD_ZERO (&rset); | |||
| FD_SET (handle, &rset); | |||
| select (handle + 1, &rset, nullptr, 0, &timeout); | |||
| } | |||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Pimpl) | |||
| }; | |||
| void NamedPipe::close() | |||
| { | |||
| if (pimpl != nullptr) | |||
| { | |||
| pimpl->stopReadOperation = true; | |||
| char buffer[1] = { 0 }; | |||
| ssize_t done = ::write (pimpl->pipeIn, buffer, 1); | |||
| (void) done; | |||
| ScopedWriteLock sl (lock); | |||
| pimpl = nullptr; | |||
| } | |||
| } | |||
| bool NamedPipe::openInternal (const String& pipeName, const bool createPipe) | |||
| { | |||
| #if JUCE_IOS | |||
| pimpl = new Pimpl (File::getSpecialLocation (File::tempDirectory) | |||
| .getChildFile (File::createLegalFileName (pipeName)).getFullPathName(), createPipe); | |||
| #else | |||
| pimpl = new Pimpl ("/tmp/" + File::createLegalFileName (pipeName), createPipe); | |||
| #endif | |||
| if (createPipe && ! pimpl->createFifos()) | |||
| { | |||
| pimpl = nullptr; | |||
| return false; | |||
| } | |||
| return true; | |||
| } | |||
| int NamedPipe::read (void* destBuffer, int maxBytesToRead, int timeOutMilliseconds) | |||
| { | |||
| ScopedReadLock sl (lock); | |||
| return pimpl != nullptr ? pimpl->read (static_cast <char*> (destBuffer), maxBytesToRead, timeOutMilliseconds) : -1; | |||
| } | |||
| int NamedPipe::write (const void* sourceBuffer, int numBytesToWrite, int timeOutMilliseconds) | |||
| { | |||
| ScopedReadLock sl (lock); | |||
| return pimpl != nullptr ? pimpl->write (static_cast <const char*> (sourceBuffer), numBytesToWrite, timeOutMilliseconds) : -1; | |||
| } | |||
| @@ -0,0 +1,170 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the juce_core module of the JUCE library. | |||
| Copyright (c) 2013 - Raw Material Software Ltd. | |||
| 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 THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||
| TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||
| NO EVENT SHALL THE AUTHOR 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. | |||
| ------------------------------------------------------------------------------ | |||
| NOTE! This permissive ISC license applies ONLY to files within the juce_core module! | |||
| All other JUCE modules are covered by a dual GPL/commercial license, so if you are | |||
| using any other modules, be sure to check that you also comply with their license. | |||
| For more details, visit www.juce.com | |||
| ============================================================================== | |||
| */ | |||
| #ifndef JUCE_WIN32_COMSMARTPTR_H_INCLUDED | |||
| #define JUCE_WIN32_COMSMARTPTR_H_INCLUDED | |||
| #ifndef _MSC_VER | |||
| template<typename Type> struct UUIDGetter { static CLSID get() { jassertfalse; return CLSID(); } }; | |||
| #define __uuidof(x) UUIDGetter<x>::get() | |||
| #endif | |||
| inline GUID uuidFromString (const char* const s) noexcept | |||
| { | |||
| unsigned long p0; | |||
| unsigned int p1, p2, p3, p4, p5, p6, p7, p8, p9, p10; | |||
| #ifndef _MSC_VER | |||
| sscanf | |||
| #else | |||
| sscanf_s | |||
| #endif | |||
| (s, "%08lX-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X", | |||
| &p0, &p1, &p2, &p3, &p4, &p5, &p6, &p7, &p8, &p9, &p10); | |||
| GUID g = { p0, (uint16) p1, (uint16) p2, { (uint8) p3, (uint8) p4, (uint8) p5, (uint8) p6, | |||
| (uint8) p7, (uint8) p8, (uint8) p9, (uint8) p10 }}; | |||
| return g; | |||
| } | |||
| //============================================================================== | |||
| /** A simple COM smart pointer. | |||
| */ | |||
| template <class ComClass> | |||
| class ComSmartPtr | |||
| { | |||
| public: | |||
| ComSmartPtr() throw() : p (0) {} | |||
| ComSmartPtr (ComClass* const obj) : p (obj) { if (p) p->AddRef(); } | |||
| ComSmartPtr (const ComSmartPtr<ComClass>& other) : p (other.p) { if (p) p->AddRef(); } | |||
| ~ComSmartPtr() { release(); } | |||
| operator ComClass*() const throw() { return p; } | |||
| ComClass& operator*() const throw() { return *p; } | |||
| ComClass* operator->() const throw() { return p; } | |||
| ComSmartPtr& operator= (ComClass* const newP) | |||
| { | |||
| if (newP != 0) newP->AddRef(); | |||
| release(); | |||
| p = newP; | |||
| return *this; | |||
| } | |||
| ComSmartPtr& operator= (const ComSmartPtr<ComClass>& newP) { return operator= (newP.p); } | |||
| // Releases and nullifies this pointer and returns its address | |||
| ComClass** resetAndGetPointerAddress() | |||
| { | |||
| release(); | |||
| p = 0; | |||
| return &p; | |||
| } | |||
| HRESULT CoCreateInstance (REFCLSID classUUID, DWORD dwClsContext = CLSCTX_INPROC_SERVER) | |||
| { | |||
| HRESULT hr = ::CoCreateInstance (classUUID, 0, dwClsContext, __uuidof (ComClass), (void**) resetAndGetPointerAddress()); | |||
| jassert (hr != CO_E_NOTINITIALIZED); // You haven't called CoInitialize for the current thread! | |||
| return hr; | |||
| } | |||
| template <class OtherComClass> | |||
| HRESULT QueryInterface (REFCLSID classUUID, ComSmartPtr<OtherComClass>& destObject) const | |||
| { | |||
| if (p == 0) | |||
| return E_POINTER; | |||
| return p->QueryInterface (classUUID, (void**) destObject.resetAndGetPointerAddress()); | |||
| } | |||
| template <class OtherComClass> | |||
| HRESULT QueryInterface (ComSmartPtr<OtherComClass>& destObject) const | |||
| { | |||
| return this->QueryInterface (__uuidof (OtherComClass), destObject); | |||
| } | |||
| private: | |||
| ComClass* p; | |||
| void release() { if (p != 0) p->Release(); } | |||
| ComClass** operator&() throw(); // private to avoid it being used accidentally | |||
| }; | |||
| //============================================================================== | |||
| #define JUCE_COMRESULT HRESULT __stdcall | |||
| //============================================================================== | |||
| template <class ComClass> | |||
| class ComBaseClassHelperBase : public ComClass | |||
| { | |||
| public: | |||
| ComBaseClassHelperBase (unsigned int initialRefCount) : refCount (initialRefCount) {} | |||
| virtual ~ComBaseClassHelperBase() {} | |||
| ULONG __stdcall AddRef() { return ++refCount; } | |||
| ULONG __stdcall Release() { const ULONG r = --refCount; if (r == 0) delete this; return r; } | |||
| protected: | |||
| ULONG refCount; | |||
| JUCE_COMRESULT QueryInterface (REFIID refId, void** result) | |||
| { | |||
| if (refId == IID_IUnknown) | |||
| return castToType <IUnknown> (result); | |||
| *result = 0; | |||
| return E_NOINTERFACE; | |||
| } | |||
| template <class Type> | |||
| JUCE_COMRESULT castToType (void** result) | |||
| { | |||
| this->AddRef(); *result = dynamic_cast <Type*> (this); return S_OK; | |||
| } | |||
| }; | |||
| /** Handy base class for writing COM objects, providing ref-counting and a basic QueryInterface method. | |||
| */ | |||
| template <class ComClass> | |||
| class ComBaseClassHelper : public ComBaseClassHelperBase <ComClass> | |||
| { | |||
| public: | |||
| ComBaseClassHelper (unsigned int initialRefCount = 1) : ComBaseClassHelperBase <ComClass> (initialRefCount) {} | |||
| ~ComBaseClassHelper() {} | |||
| JUCE_COMRESULT QueryInterface (REFIID refId, void** result) | |||
| { | |||
| if (refId == __uuidof (ComClass)) | |||
| return this->template castToType <ComClass> (result); | |||
| return ComBaseClassHelperBase <ComClass>::QueryInterface (refId, result); | |||
| } | |||
| }; | |||
| #endif // JUCE_WIN32_COMSMARTPTR_H_INCLUDED | |||
| @@ -0,0 +1,961 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the juce_core module of the JUCE library. | |||
| Copyright (c) 2013 - Raw Material Software Ltd. | |||
| 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 THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||
| TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||
| NO EVENT SHALL THE AUTHOR 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. | |||
| ------------------------------------------------------------------------------ | |||
| NOTE! This permissive ISC license applies ONLY to files within the juce_core module! | |||
| All other JUCE modules are covered by a dual GPL/commercial license, so if you are | |||
| using any other modules, be sure to check that you also comply with their license. | |||
| For more details, visit www.juce.com | |||
| ============================================================================== | |||
| */ | |||
| #ifndef INVALID_FILE_ATTRIBUTES | |||
| #define INVALID_FILE_ATTRIBUTES ((DWORD) -1) | |||
| #endif | |||
| //============================================================================== | |||
| namespace WindowsFileHelpers | |||
| { | |||
| DWORD getAtts (const String& path) | |||
| { | |||
| return GetFileAttributes (path.toWideCharPointer()); | |||
| } | |||
| int64 fileTimeToTime (const FILETIME* const ft) | |||
| { | |||
| static_jassert (sizeof (ULARGE_INTEGER) == sizeof (FILETIME)); // tell me if this fails! | |||
| return (int64) ((reinterpret_cast<const ULARGE_INTEGER*> (ft)->QuadPart - 116444736000000000LL) / 10000); | |||
| } | |||
| FILETIME* timeToFileTime (const int64 time, FILETIME* const ft) noexcept | |||
| { | |||
| if (time <= 0) | |||
| return nullptr; | |||
| reinterpret_cast<ULARGE_INTEGER*> (ft)->QuadPart = (ULONGLONG) (time * 10000 + 116444736000000000LL); | |||
| return ft; | |||
| } | |||
| String getDriveFromPath (String path) | |||
| { | |||
| if (path.isNotEmpty() && path[1] == ':' && path[2] == 0) | |||
| path << '\\'; | |||
| const size_t numBytes = CharPointer_UTF16::getBytesRequiredFor (path.getCharPointer()) + 4; | |||
| HeapBlock<WCHAR> pathCopy; | |||
| pathCopy.calloc (numBytes, 1); | |||
| path.copyToUTF16 (pathCopy, numBytes); | |||
| if (PathStripToRoot (pathCopy)) | |||
| path = static_cast <const WCHAR*> (pathCopy); | |||
| return path; | |||
| } | |||
| int64 getDiskSpaceInfo (const String& path, const bool total) | |||
| { | |||
| ULARGE_INTEGER spc, tot, totFree; | |||
| if (GetDiskFreeSpaceEx (getDriveFromPath (path).toWideCharPointer(), &spc, &tot, &totFree)) | |||
| return total ? (int64) tot.QuadPart | |||
| : (int64) spc.QuadPart; | |||
| return 0; | |||
| } | |||
| unsigned int getWindowsDriveType (const String& path) | |||
| { | |||
| return GetDriveType (getDriveFromPath (path).toWideCharPointer()); | |||
| } | |||
| File getSpecialFolderPath (int type) | |||
| { | |||
| WCHAR path [MAX_PATH + 256]; | |||
| if (SHGetSpecialFolderPath (0, path, type, FALSE)) | |||
| return File (String (path)); | |||
| return File::nonexistent; | |||
| } | |||
| File getModuleFileName (HINSTANCE moduleHandle) | |||
| { | |||
| WCHAR dest [MAX_PATH + 256]; | |||
| dest[0] = 0; | |||
| GetModuleFileName (moduleHandle, dest, (DWORD) numElementsInArray (dest)); | |||
| return File (String (dest)); | |||
| } | |||
| Result getResultForLastError() | |||
| { | |||
| TCHAR messageBuffer [256] = { 0 }; | |||
| FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, | |||
| nullptr, GetLastError(), MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), | |||
| messageBuffer, (DWORD) numElementsInArray (messageBuffer) - 1, nullptr); | |||
| return Result::fail (String (messageBuffer)); | |||
| } | |||
| } | |||
| //============================================================================== | |||
| const juce_wchar File::separator = '\\'; | |||
| const String File::separatorString ("\\"); | |||
| //============================================================================== | |||
| bool File::exists() const | |||
| { | |||
| return fullPath.isNotEmpty() | |||
| && WindowsFileHelpers::getAtts (fullPath) != INVALID_FILE_ATTRIBUTES; | |||
| } | |||
| bool File::existsAsFile() const | |||
| { | |||
| return fullPath.isNotEmpty() | |||
| && (WindowsFileHelpers::getAtts (fullPath) & FILE_ATTRIBUTE_DIRECTORY) == 0; | |||
| } | |||
| bool File::isDirectory() const | |||
| { | |||
| const DWORD attr = WindowsFileHelpers::getAtts (fullPath); | |||
| return ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) && (attr != INVALID_FILE_ATTRIBUTES); | |||
| } | |||
| bool File::hasWriteAccess() const | |||
| { | |||
| if (exists()) | |||
| return (WindowsFileHelpers::getAtts (fullPath) & FILE_ATTRIBUTE_READONLY) == 0; | |||
| // on windows, it seems that even read-only directories can still be written into, | |||
| // so checking the parent directory's permissions would return the wrong result.. | |||
| return true; | |||
| } | |||
| bool File::setFileReadOnlyInternal (const bool shouldBeReadOnly) const | |||
| { | |||
| const DWORD oldAtts = WindowsFileHelpers::getAtts (fullPath); | |||
| if (oldAtts == INVALID_FILE_ATTRIBUTES) | |||
| return false; | |||
| const DWORD newAtts = shouldBeReadOnly ? (oldAtts | FILE_ATTRIBUTE_READONLY) | |||
| : (oldAtts & ~FILE_ATTRIBUTE_READONLY); | |||
| return newAtts == oldAtts | |||
| || SetFileAttributes (fullPath.toWideCharPointer(), newAtts) != FALSE; | |||
| } | |||
| bool File::isHidden() const | |||
| { | |||
| return (WindowsFileHelpers::getAtts (fullPath) & FILE_ATTRIBUTE_HIDDEN) != 0; | |||
| } | |||
| //============================================================================== | |||
| bool File::deleteFile() const | |||
| { | |||
| if (! exists()) | |||
| return true; | |||
| return isDirectory() ? RemoveDirectory (fullPath.toWideCharPointer()) != 0 | |||
| : DeleteFile (fullPath.toWideCharPointer()) != 0; | |||
| } | |||
| bool File::moveToTrash() const | |||
| { | |||
| if (! exists()) | |||
| return true; | |||
| // The string we pass in must be double null terminated.. | |||
| const size_t numBytes = CharPointer_UTF16::getBytesRequiredFor (fullPath.getCharPointer()) + 8; | |||
| HeapBlock<WCHAR> doubleNullTermPath; | |||
| doubleNullTermPath.calloc (numBytes, 1); | |||
| fullPath.copyToUTF16 (doubleNullTermPath, numBytes); | |||
| SHFILEOPSTRUCT fos = { 0 }; | |||
| fos.wFunc = FO_DELETE; | |||
| fos.pFrom = doubleNullTermPath; | |||
| fos.fFlags = FOF_ALLOWUNDO | FOF_NOERRORUI | FOF_SILENT | FOF_NOCONFIRMATION | |||
| | FOF_NOCONFIRMMKDIR | FOF_RENAMEONCOLLISION; | |||
| return SHFileOperation (&fos) == 0; | |||
| } | |||
| bool File::copyInternal (const File& dest) const | |||
| { | |||
| return CopyFile (fullPath.toWideCharPointer(), dest.getFullPathName().toWideCharPointer(), false) != 0; | |||
| } | |||
| bool File::moveInternal (const File& dest) const | |||
| { | |||
| return MoveFile (fullPath.toWideCharPointer(), dest.getFullPathName().toWideCharPointer()) != 0; | |||
| } | |||
| Result File::createDirectoryInternal (const String& fileName) const | |||
| { | |||
| return CreateDirectory (fileName.toWideCharPointer(), 0) ? Result::ok() | |||
| : WindowsFileHelpers::getResultForLastError(); | |||
| } | |||
| //============================================================================== | |||
| int64 juce_fileSetPosition (void* handle, int64 pos) | |||
| { | |||
| LARGE_INTEGER li; | |||
| li.QuadPart = pos; | |||
| li.LowPart = SetFilePointer ((HANDLE) handle, (LONG) li.LowPart, &li.HighPart, FILE_BEGIN); // (returns -1 if it fails) | |||
| return li.QuadPart; | |||
| } | |||
| void FileInputStream::openHandle() | |||
| { | |||
| HANDLE h = CreateFile (file.getFullPathName().toWideCharPointer(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, | |||
| OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, 0); | |||
| if (h != INVALID_HANDLE_VALUE) | |||
| fileHandle = (void*) h; | |||
| else | |||
| status = WindowsFileHelpers::getResultForLastError(); | |||
| } | |||
| void FileInputStream::closeHandle() | |||
| { | |||
| CloseHandle ((HANDLE) fileHandle); | |||
| } | |||
| size_t FileInputStream::readInternal (void* buffer, size_t numBytes) | |||
| { | |||
| if (fileHandle != 0) | |||
| { | |||
| DWORD actualNum = 0; | |||
| if (! ReadFile ((HANDLE) fileHandle, buffer, (DWORD) numBytes, &actualNum, 0)) | |||
| status = WindowsFileHelpers::getResultForLastError(); | |||
| return (size_t) actualNum; | |||
| } | |||
| return 0; | |||
| } | |||
| //============================================================================== | |||
| void FileOutputStream::openHandle() | |||
| { | |||
| HANDLE h = CreateFile (file.getFullPathName().toWideCharPointer(), GENERIC_WRITE, FILE_SHARE_READ, 0, | |||
| OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0); | |||
| if (h != INVALID_HANDLE_VALUE) | |||
| { | |||
| LARGE_INTEGER li; | |||
| li.QuadPart = 0; | |||
| li.LowPart = SetFilePointer (h, 0, &li.HighPart, FILE_END); | |||
| if (li.LowPart != INVALID_SET_FILE_POINTER) | |||
| { | |||
| fileHandle = (void*) h; | |||
| currentPosition = li.QuadPart; | |||
| return; | |||
| } | |||
| } | |||
| status = WindowsFileHelpers::getResultForLastError(); | |||
| } | |||
| void FileOutputStream::closeHandle() | |||
| { | |||
| CloseHandle ((HANDLE) fileHandle); | |||
| } | |||
| ssize_t FileOutputStream::writeInternal (const void* buffer, size_t numBytes) | |||
| { | |||
| if (fileHandle != nullptr) | |||
| { | |||
| DWORD actualNum = 0; | |||
| if (! WriteFile ((HANDLE) fileHandle, buffer, (DWORD) numBytes, &actualNum, 0)) | |||
| status = WindowsFileHelpers::getResultForLastError(); | |||
| return (ssize_t) actualNum; | |||
| } | |||
| return 0; | |||
| } | |||
| void FileOutputStream::flushInternal() | |||
| { | |||
| if (fileHandle != nullptr) | |||
| if (! FlushFileBuffers ((HANDLE) fileHandle)) | |||
| status = WindowsFileHelpers::getResultForLastError(); | |||
| } | |||
| Result FileOutputStream::truncate() | |||
| { | |||
| if (fileHandle == nullptr) | |||
| return status; | |||
| flush(); | |||
| return SetEndOfFile ((HANDLE) fileHandle) ? Result::ok() | |||
| : WindowsFileHelpers::getResultForLastError(); | |||
| } | |||
| //============================================================================== | |||
| void MemoryMappedFile::openInternal (const File& file, AccessMode mode) | |||
| { | |||
| jassert (mode == readOnly || mode == readWrite); | |||
| if (range.getStart() > 0) | |||
| { | |||
| SYSTEM_INFO systemInfo; | |||
| GetNativeSystemInfo (&systemInfo); | |||
| range.setStart (range.getStart() - (range.getStart() % systemInfo.dwAllocationGranularity)); | |||
| } | |||
| DWORD accessMode = GENERIC_READ, createType = OPEN_EXISTING; | |||
| DWORD protect = PAGE_READONLY, access = FILE_MAP_READ; | |||
| if (mode == readWrite) | |||
| { | |||
| accessMode = GENERIC_READ | GENERIC_WRITE; | |||
| createType = OPEN_ALWAYS; | |||
| protect = PAGE_READWRITE; | |||
| access = FILE_MAP_ALL_ACCESS; | |||
| } | |||
| HANDLE h = CreateFile (file.getFullPathName().toWideCharPointer(), accessMode, FILE_SHARE_READ, 0, | |||
| createType, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, 0); | |||
| if (h != INVALID_HANDLE_VALUE) | |||
| { | |||
| fileHandle = (void*) h; | |||
| HANDLE mappingHandle = CreateFileMapping (h, 0, protect, (DWORD) (range.getEnd() >> 32), (DWORD) range.getEnd(), 0); | |||
| if (mappingHandle != 0) | |||
| { | |||
| address = MapViewOfFile (mappingHandle, access, (DWORD) (range.getStart() >> 32), | |||
| (DWORD) range.getStart(), (SIZE_T) range.getLength()); | |||
| if (address == nullptr) | |||
| range = Range<int64>(); | |||
| CloseHandle (mappingHandle); | |||
| } | |||
| } | |||
| } | |||
| MemoryMappedFile::~MemoryMappedFile() | |||
| { | |||
| if (address != nullptr) | |||
| UnmapViewOfFile (address); | |||
| if (fileHandle != nullptr) | |||
| CloseHandle ((HANDLE) fileHandle); | |||
| } | |||
| //============================================================================== | |||
| int64 File::getSize() const | |||
| { | |||
| WIN32_FILE_ATTRIBUTE_DATA attributes; | |||
| if (GetFileAttributesEx (fullPath.toWideCharPointer(), GetFileExInfoStandard, &attributes)) | |||
| return (((int64) attributes.nFileSizeHigh) << 32) | attributes.nFileSizeLow; | |||
| return 0; | |||
| } | |||
| void File::getFileTimesInternal (int64& modificationTime, int64& accessTime, int64& creationTime) const | |||
| { | |||
| using namespace WindowsFileHelpers; | |||
| WIN32_FILE_ATTRIBUTE_DATA attributes; | |||
| if (GetFileAttributesEx (fullPath.toWideCharPointer(), GetFileExInfoStandard, &attributes)) | |||
| { | |||
| modificationTime = fileTimeToTime (&attributes.ftLastWriteTime); | |||
| creationTime = fileTimeToTime (&attributes.ftCreationTime); | |||
| accessTime = fileTimeToTime (&attributes.ftLastAccessTime); | |||
| } | |||
| else | |||
| { | |||
| creationTime = accessTime = modificationTime = 0; | |||
| } | |||
| } | |||
| bool File::setFileTimesInternal (int64 modificationTime, int64 accessTime, int64 creationTime) const | |||
| { | |||
| using namespace WindowsFileHelpers; | |||
| bool ok = false; | |||
| HANDLE h = CreateFile (fullPath.toWideCharPointer(), GENERIC_WRITE, FILE_SHARE_READ, 0, | |||
| OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0); | |||
| if (h != INVALID_HANDLE_VALUE) | |||
| { | |||
| FILETIME m, a, c; | |||
| ok = SetFileTime (h, | |||
| timeToFileTime (creationTime, &c), | |||
| timeToFileTime (accessTime, &a), | |||
| timeToFileTime (modificationTime, &m)) != 0; | |||
| CloseHandle (h); | |||
| } | |||
| return ok; | |||
| } | |||
| //============================================================================== | |||
| void File::findFileSystemRoots (Array<File>& destArray) | |||
| { | |||
| TCHAR buffer [2048] = { 0 }; | |||
| GetLogicalDriveStrings (2048, buffer); | |||
| const TCHAR* n = buffer; | |||
| StringArray roots; | |||
| while (*n != 0) | |||
| { | |||
| roots.add (String (n)); | |||
| while (*n++ != 0) | |||
| {} | |||
| } | |||
| roots.sort (true); | |||
| for (int i = 0; i < roots.size(); ++i) | |||
| destArray.add (roots [i]); | |||
| } | |||
| //============================================================================== | |||
| String File::getVolumeLabel() const | |||
| { | |||
| TCHAR dest[64]; | |||
| if (! GetVolumeInformation (WindowsFileHelpers::getDriveFromPath (getFullPathName()).toWideCharPointer(), dest, | |||
| (DWORD) numElementsInArray (dest), 0, 0, 0, 0, 0)) | |||
| dest[0] = 0; | |||
| return dest; | |||
| } | |||
| int File::getVolumeSerialNumber() const | |||
| { | |||
| TCHAR dest[64]; | |||
| DWORD serialNum; | |||
| if (! GetVolumeInformation (WindowsFileHelpers::getDriveFromPath (getFullPathName()).toWideCharPointer(), dest, | |||
| (DWORD) numElementsInArray (dest), &serialNum, 0, 0, 0, 0)) | |||
| return 0; | |||
| return (int) serialNum; | |||
| } | |||
| int64 File::getBytesFreeOnVolume() const | |||
| { | |||
| return WindowsFileHelpers::getDiskSpaceInfo (getFullPathName(), false); | |||
| } | |||
| int64 File::getVolumeTotalSize() const | |||
| { | |||
| return WindowsFileHelpers::getDiskSpaceInfo (getFullPathName(), true); | |||
| } | |||
| //============================================================================== | |||
| bool File::isOnCDRomDrive() const | |||
| { | |||
| return WindowsFileHelpers::getWindowsDriveType (getFullPathName()) == DRIVE_CDROM; | |||
| } | |||
| bool File::isOnHardDisk() const | |||
| { | |||
| if (fullPath.isEmpty()) | |||
| return false; | |||
| const unsigned int n = WindowsFileHelpers::getWindowsDriveType (getFullPathName()); | |||
| if (fullPath.toLowerCase()[0] <= 'b' && fullPath[1] == ':') | |||
| return n != DRIVE_REMOVABLE; | |||
| return n != DRIVE_CDROM && n != DRIVE_REMOTE; | |||
| } | |||
| bool File::isOnRemovableDrive() const | |||
| { | |||
| if (fullPath.isEmpty()) | |||
| return false; | |||
| const unsigned int n = WindowsFileHelpers::getWindowsDriveType (getFullPathName()); | |||
| return n == DRIVE_CDROM | |||
| || n == DRIVE_REMOTE | |||
| || n == DRIVE_REMOVABLE | |||
| || n == DRIVE_RAMDISK; | |||
| } | |||
| //============================================================================== | |||
| File JUCE_CALLTYPE File::getSpecialLocation (const SpecialLocationType type) | |||
| { | |||
| int csidlType = 0; | |||
| switch (type) | |||
| { | |||
| case userHomeDirectory: csidlType = CSIDL_PROFILE; break; | |||
| case userDocumentsDirectory: csidlType = CSIDL_PERSONAL; break; | |||
| case userDesktopDirectory: csidlType = CSIDL_DESKTOP; break; | |||
| case userApplicationDataDirectory: csidlType = CSIDL_APPDATA; break; | |||
| case commonApplicationDataDirectory: csidlType = CSIDL_COMMON_APPDATA; break; | |||
| case globalApplicationsDirectory: csidlType = CSIDL_PROGRAM_FILES; break; | |||
| case userMusicDirectory: csidlType = 0x0d; /*CSIDL_MYMUSIC*/ break; | |||
| case userMoviesDirectory: csidlType = 0x0e; /*CSIDL_MYVIDEO*/ break; | |||
| case userPicturesDirectory: csidlType = 0x27; /*CSIDL_MYPICTURES*/ break; | |||
| case tempDirectory: | |||
| { | |||
| WCHAR dest [2048]; | |||
| dest[0] = 0; | |||
| GetTempPath ((DWORD) numElementsInArray (dest), dest); | |||
| return File (String (dest)); | |||
| } | |||
| case invokedExecutableFile: | |||
| case currentExecutableFile: | |||
| case currentApplicationFile: | |||
| return WindowsFileHelpers::getModuleFileName ((HINSTANCE) Process::getCurrentModuleInstanceHandle()); | |||
| case hostApplicationPath: | |||
| return WindowsFileHelpers::getModuleFileName (0); | |||
| default: | |||
| jassertfalse; // unknown type? | |||
| return File::nonexistent; | |||
| } | |||
| return WindowsFileHelpers::getSpecialFolderPath (csidlType); | |||
| } | |||
| //============================================================================== | |||
| File File::getCurrentWorkingDirectory() | |||
| { | |||
| WCHAR dest [MAX_PATH + 256]; | |||
| dest[0] = 0; | |||
| GetCurrentDirectory ((DWORD) numElementsInArray (dest), dest); | |||
| return File (String (dest)); | |||
| } | |||
| bool File::setAsCurrentWorkingDirectory() const | |||
| { | |||
| return SetCurrentDirectory (getFullPathName().toWideCharPointer()) != FALSE; | |||
| } | |||
| //============================================================================== | |||
| String File::getVersion() const | |||
| { | |||
| String result; | |||
| DWORD handle = 0; | |||
| DWORD bufferSize = GetFileVersionInfoSize (getFullPathName().toWideCharPointer(), &handle); | |||
| HeapBlock<char> buffer; | |||
| buffer.calloc (bufferSize); | |||
| if (GetFileVersionInfo (getFullPathName().toWideCharPointer(), 0, bufferSize, buffer)) | |||
| { | |||
| VS_FIXEDFILEINFO* vffi; | |||
| UINT len = 0; | |||
| if (VerQueryValue (buffer, (LPTSTR) _T("\\"), (LPVOID*) &vffi, &len)) | |||
| { | |||
| result << (int) HIWORD (vffi->dwFileVersionMS) << '.' | |||
| << (int) LOWORD (vffi->dwFileVersionMS) << '.' | |||
| << (int) HIWORD (vffi->dwFileVersionLS) << '.' | |||
| << (int) LOWORD (vffi->dwFileVersionLS); | |||
| } | |||
| } | |||
| return result; | |||
| } | |||
| //============================================================================== | |||
| File File::getLinkedTarget() const | |||
| { | |||
| File result (*this); | |||
| String p (getFullPathName()); | |||
| if (! exists()) | |||
| p += ".lnk"; | |||
| else if (! hasFileExtension (".lnk")) | |||
| return result; | |||
| ComSmartPtr <IShellLink> shellLink; | |||
| ComSmartPtr <IPersistFile> persistFile; | |||
| if (SUCCEEDED (shellLink.CoCreateInstance (CLSID_ShellLink)) | |||
| && SUCCEEDED (shellLink.QueryInterface (persistFile)) | |||
| && SUCCEEDED (persistFile->Load (p.toWideCharPointer(), STGM_READ)) | |||
| && SUCCEEDED (shellLink->Resolve (0, SLR_ANY_MATCH | SLR_NO_UI))) | |||
| { | |||
| WIN32_FIND_DATA winFindData; | |||
| WCHAR resolvedPath [MAX_PATH]; | |||
| if (SUCCEEDED (shellLink->GetPath (resolvedPath, MAX_PATH, &winFindData, SLGP_UNCPRIORITY))) | |||
| result = File (resolvedPath); | |||
| } | |||
| return result; | |||
| } | |||
| bool File::createLink (const String& description, const File& linkFileToCreate) const | |||
| { | |||
| linkFileToCreate.deleteFile(); | |||
| ComSmartPtr <IShellLink> shellLink; | |||
| ComSmartPtr <IPersistFile> persistFile; | |||
| return SUCCEEDED (shellLink.CoCreateInstance (CLSID_ShellLink)) | |||
| && SUCCEEDED (shellLink->SetPath (getFullPathName().toWideCharPointer())) | |||
| && SUCCEEDED (shellLink->SetDescription (description.toWideCharPointer())) | |||
| && SUCCEEDED (shellLink.QueryInterface (persistFile)) | |||
| && SUCCEEDED (persistFile->Save (linkFileToCreate.getFullPathName().toWideCharPointer(), TRUE)); | |||
| } | |||
| //============================================================================== | |||
| class DirectoryIterator::NativeIterator::Pimpl | |||
| { | |||
| public: | |||
| Pimpl (const File& directory, const String& wildCard) | |||
| : directoryWithWildCard (File::addTrailingSeparator (directory.getFullPathName()) + wildCard), | |||
| handle (INVALID_HANDLE_VALUE) | |||
| { | |||
| } | |||
| ~Pimpl() | |||
| { | |||
| if (handle != INVALID_HANDLE_VALUE) | |||
| FindClose (handle); | |||
| } | |||
| bool next (String& filenameFound, | |||
| bool* const isDir, bool* const isHidden, int64* const fileSize, | |||
| Time* const modTime, Time* const creationTime, bool* const isReadOnly) | |||
| { | |||
| using namespace WindowsFileHelpers; | |||
| WIN32_FIND_DATA findData; | |||
| if (handle == INVALID_HANDLE_VALUE) | |||
| { | |||
| handle = FindFirstFile (directoryWithWildCard.toWideCharPointer(), &findData); | |||
| if (handle == INVALID_HANDLE_VALUE) | |||
| return false; | |||
| } | |||
| else | |||
| { | |||
| if (FindNextFile (handle, &findData) == 0) | |||
| return false; | |||
| } | |||
| filenameFound = findData.cFileName; | |||
| if (isDir != nullptr) *isDir = ((findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0); | |||
| if (isHidden != nullptr) *isHidden = ((findData.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) != 0); | |||
| if (isReadOnly != nullptr) *isReadOnly = ((findData.dwFileAttributes & FILE_ATTRIBUTE_READONLY) != 0); | |||
| if (fileSize != nullptr) *fileSize = findData.nFileSizeLow + (((int64) findData.nFileSizeHigh) << 32); | |||
| if (modTime != nullptr) *modTime = Time (fileTimeToTime (&findData.ftLastWriteTime)); | |||
| if (creationTime != nullptr) *creationTime = Time (fileTimeToTime (&findData.ftCreationTime)); | |||
| return true; | |||
| } | |||
| private: | |||
| const String directoryWithWildCard; | |||
| HANDLE handle; | |||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Pimpl) | |||
| }; | |||
| DirectoryIterator::NativeIterator::NativeIterator (const File& directory, const String& wildCard) | |||
| : pimpl (new DirectoryIterator::NativeIterator::Pimpl (directory, wildCard)) | |||
| { | |||
| } | |||
| DirectoryIterator::NativeIterator::~NativeIterator() | |||
| { | |||
| } | |||
| bool DirectoryIterator::NativeIterator::next (String& filenameFound, | |||
| bool* const isDir, bool* const isHidden, int64* const fileSize, | |||
| Time* const modTime, Time* const creationTime, bool* const isReadOnly) | |||
| { | |||
| return pimpl->next (filenameFound, isDir, isHidden, fileSize, modTime, creationTime, isReadOnly); | |||
| } | |||
| //============================================================================== | |||
| bool Process::openDocument (const String& fileName, const String& parameters) | |||
| { | |||
| HINSTANCE hInstance = 0; | |||
| JUCE_TRY | |||
| { | |||
| hInstance = ShellExecute (0, 0, fileName.toWideCharPointer(), | |||
| parameters.toWideCharPointer(), 0, SW_SHOWDEFAULT); | |||
| } | |||
| JUCE_CATCH_ALL | |||
| return hInstance > (HINSTANCE) 32; | |||
| } | |||
| void File::revealToUser() const | |||
| { | |||
| DynamicLibrary dll ("Shell32.dll"); | |||
| JUCE_LOAD_WINAPI_FUNCTION (dll, ILCreateFromPathW, ilCreateFromPathW, ITEMIDLIST*, (LPCWSTR)) | |||
| JUCE_LOAD_WINAPI_FUNCTION (dll, ILFree, ilFree, void, (ITEMIDLIST*)) | |||
| JUCE_LOAD_WINAPI_FUNCTION (dll, SHOpenFolderAndSelectItems, shOpenFolderAndSelectItems, HRESULT, (ITEMIDLIST*, UINT, void*, DWORD)) | |||
| if (ilCreateFromPathW != nullptr && shOpenFolderAndSelectItems != nullptr && ilFree != nullptr) | |||
| { | |||
| if (ITEMIDLIST* const itemIDList = ilCreateFromPathW (fullPath.toWideCharPointer())) | |||
| { | |||
| shOpenFolderAndSelectItems (itemIDList, 0, nullptr, 0); | |||
| ilFree (itemIDList); | |||
| } | |||
| } | |||
| } | |||
| //============================================================================== | |||
| class NamedPipe::Pimpl | |||
| { | |||
| public: | |||
| Pimpl (const String& pipeName, const bool createPipe) | |||
| : filename ("\\\\.\\pipe\\" + File::createLegalFileName (pipeName)), | |||
| pipeH (INVALID_HANDLE_VALUE), | |||
| cancelEvent (CreateEvent (0, FALSE, FALSE, 0)), | |||
| connected (false), ownsPipe (createPipe), shouldStop (false) | |||
| { | |||
| if (createPipe) | |||
| pipeH = CreateNamedPipe (filename.toWideCharPointer(), | |||
| PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, 0, | |||
| PIPE_UNLIMITED_INSTANCES, 4096, 4096, 0, 0); | |||
| } | |||
| ~Pimpl() | |||
| { | |||
| disconnectPipe(); | |||
| if (pipeH != INVALID_HANDLE_VALUE) | |||
| CloseHandle (pipeH); | |||
| CloseHandle (cancelEvent); | |||
| } | |||
| bool connect (const int timeOutMs) | |||
| { | |||
| if (! ownsPipe) | |||
| { | |||
| if (pipeH != INVALID_HANDLE_VALUE) | |||
| return true; | |||
| const Time timeOutEnd (Time::getCurrentTime() + RelativeTime::milliseconds (timeOutMs)); | |||
| for (;;) | |||
| { | |||
| { | |||
| const ScopedLock sl (createFileLock); | |||
| if (pipeH == INVALID_HANDLE_VALUE) | |||
| pipeH = CreateFile (filename.toWideCharPointer(), | |||
| GENERIC_READ | GENERIC_WRITE, 0, 0, | |||
| OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0); | |||
| } | |||
| if (pipeH != INVALID_HANDLE_VALUE) | |||
| return true; | |||
| if (shouldStop || (timeOutMs >= 0 && Time::getCurrentTime() > timeOutEnd)) | |||
| return false; | |||
| Thread::sleep (1); | |||
| } | |||
| } | |||
| if (! connected) | |||
| { | |||
| OverlappedEvent over; | |||
| if (ConnectNamedPipe (pipeH, &over.over) == 0) | |||
| { | |||
| switch (GetLastError()) | |||
| { | |||
| case ERROR_PIPE_CONNECTED: connected = true; break; | |||
| case ERROR_IO_PENDING: | |||
| case ERROR_PIPE_LISTENING: connected = waitForIO (over, timeOutMs); break; | |||
| default: break; | |||
| } | |||
| } | |||
| } | |||
| return connected; | |||
| } | |||
| void disconnectPipe() | |||
| { | |||
| if (ownsPipe && connected) | |||
| { | |||
| DisconnectNamedPipe (pipeH); | |||
| connected = false; | |||
| } | |||
| } | |||
| int read (void* destBuffer, const int maxBytesToRead, const int timeOutMilliseconds) | |||
| { | |||
| while (connect (timeOutMilliseconds)) | |||
| { | |||
| if (maxBytesToRead <= 0) | |||
| return 0; | |||
| OverlappedEvent over; | |||
| unsigned long numRead; | |||
| if (ReadFile (pipeH, destBuffer, (DWORD) maxBytesToRead, &numRead, &over.over)) | |||
| return (int) numRead; | |||
| const DWORD lastError = GetLastError(); | |||
| if (lastError == ERROR_IO_PENDING) | |||
| { | |||
| if (! waitForIO (over, timeOutMilliseconds)) | |||
| return -1; | |||
| if (GetOverlappedResult (pipeH, &over.over, &numRead, FALSE)) | |||
| return (int) numRead; | |||
| } | |||
| if (ownsPipe && (GetLastError() == ERROR_BROKEN_PIPE || GetLastError() == ERROR_PIPE_NOT_CONNECTED)) | |||
| disconnectPipe(); | |||
| else | |||
| break; | |||
| } | |||
| return -1; | |||
| } | |||
| int write (const void* sourceBuffer, int numBytesToWrite, int timeOutMilliseconds) | |||
| { | |||
| if (connect (timeOutMilliseconds)) | |||
| { | |||
| if (numBytesToWrite <= 0) | |||
| return 0; | |||
| OverlappedEvent over; | |||
| unsigned long numWritten; | |||
| if (WriteFile (pipeH, sourceBuffer, (DWORD) numBytesToWrite, &numWritten, &over.over)) | |||
| return (int) numWritten; | |||
| if (GetLastError() == ERROR_IO_PENDING) | |||
| { | |||
| if (! waitForIO (over, timeOutMilliseconds)) | |||
| return -1; | |||
| if (GetOverlappedResult (pipeH, &over.over, &numWritten, FALSE)) | |||
| return (int) numWritten; | |||
| if (GetLastError() == ERROR_BROKEN_PIPE && ownsPipe) | |||
| disconnectPipe(); | |||
| } | |||
| } | |||
| return -1; | |||
| } | |||
| const String filename; | |||
| HANDLE pipeH, cancelEvent; | |||
| bool connected, ownsPipe, shouldStop; | |||
| CriticalSection createFileLock; | |||
| private: | |||
| struct OverlappedEvent | |||
| { | |||
| OverlappedEvent() | |||
| { | |||
| zerostruct (over); | |||
| over.hEvent = CreateEvent (0, TRUE, FALSE, 0); | |||
| } | |||
| ~OverlappedEvent() | |||
| { | |||
| CloseHandle (over.hEvent); | |||
| } | |||
| OVERLAPPED over; | |||
| }; | |||
| bool waitForIO (OverlappedEvent& over, int timeOutMilliseconds) | |||
| { | |||
| if (shouldStop) | |||
| return false; | |||
| HANDLE handles[] = { over.over.hEvent, cancelEvent }; | |||
| DWORD waitResult = WaitForMultipleObjects (2, handles, FALSE, | |||
| timeOutMilliseconds >= 0 ? timeOutMilliseconds | |||
| : INFINITE); | |||
| if (waitResult == WAIT_OBJECT_0) | |||
| return true; | |||
| CancelIo (pipeH); | |||
| return false; | |||
| } | |||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Pimpl) | |||
| }; | |||
| void NamedPipe::close() | |||
| { | |||
| if (pimpl != nullptr) | |||
| { | |||
| pimpl->shouldStop = true; | |||
| SetEvent (pimpl->cancelEvent); | |||
| ScopedWriteLock sl (lock); | |||
| pimpl = nullptr; | |||
| } | |||
| } | |||
| bool NamedPipe::openInternal (const String& pipeName, const bool createPipe) | |||
| { | |||
| pimpl = new Pimpl (pipeName, createPipe); | |||
| if (createPipe && pimpl->pipeH == INVALID_HANDLE_VALUE) | |||
| { | |||
| pimpl = nullptr; | |||
| return false; | |||
| } | |||
| return true; | |||
| } | |||
| int NamedPipe::read (void* destBuffer, int maxBytesToRead, int timeOutMilliseconds) | |||
| { | |||
| ScopedReadLock sl (lock); | |||
| return pimpl != nullptr ? pimpl->read (destBuffer, maxBytesToRead, timeOutMilliseconds) : -1; | |||
| } | |||
| int NamedPipe::write (const void* sourceBuffer, int numBytesToWrite, int timeOutMilliseconds) | |||
| { | |||
| ScopedReadLock sl (lock); | |||
| return pimpl != nullptr ? pimpl->write (sourceBuffer, numBytesToWrite, timeOutMilliseconds) : -1; | |||
| } | |||
| @@ -0,0 +1,469 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the juce_core module of the JUCE library. | |||
| Copyright (c) 2013 - Raw Material Software Ltd. | |||
| 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 THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||
| TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||
| NO EVENT SHALL THE AUTHOR 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. | |||
| ------------------------------------------------------------------------------ | |||
| NOTE! This permissive ISC license applies ONLY to files within the juce_core module! | |||
| All other JUCE modules are covered by a dual GPL/commercial license, so if you are | |||
| using any other modules, be sure to check that you also comply with their license. | |||
| For more details, visit www.juce.com | |||
| ============================================================================== | |||
| */ | |||
| #ifndef INTERNET_FLAG_NEED_FILE | |||
| #define INTERNET_FLAG_NEED_FILE 0x00000010 | |||
| #endif | |||
| #ifndef INTERNET_OPTION_DISABLE_AUTODIAL | |||
| #define INTERNET_OPTION_DISABLE_AUTODIAL 70 | |||
| #endif | |||
| //============================================================================== | |||
| class WebInputStream : public InputStream | |||
| { | |||
| public: | |||
| WebInputStream (const String& address_, bool isPost_, const MemoryBlock& postData_, | |||
| URL::OpenStreamProgressCallback* progressCallback, void* progressCallbackContext, | |||
| const String& headers_, int timeOutMs_, StringPairArray* responseHeaders) | |||
| : connection (0), request (0), | |||
| address (address_), headers (headers_), postData (postData_), position (0), | |||
| finished (false), isPost (isPost_), timeOutMs (timeOutMs_) | |||
| { | |||
| createConnection (progressCallback, progressCallbackContext); | |||
| if (responseHeaders != nullptr && ! isError()) | |||
| { | |||
| DWORD bufferSizeBytes = 4096; | |||
| for (;;) | |||
| { | |||
| HeapBlock<char> buffer ((size_t) bufferSizeBytes); | |||
| if (HttpQueryInfo (request, HTTP_QUERY_RAW_HEADERS_CRLF, buffer.getData(), &bufferSizeBytes, 0)) | |||
| { | |||
| StringArray headersArray; | |||
| headersArray.addLines (reinterpret_cast <const WCHAR*> (buffer.getData())); | |||
| for (int i = 0; i < headersArray.size(); ++i) | |||
| { | |||
| const String& header = headersArray[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)); | |||
| } | |||
| break; | |||
| } | |||
| if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) | |||
| break; | |||
| } | |||
| } | |||
| } | |||
| ~WebInputStream() | |||
| { | |||
| close(); | |||
| } | |||
| //============================================================================== | |||
| bool isError() const { return request == 0; } | |||
| bool isExhausted() { return finished; } | |||
| int64 getPosition() { return position; } | |||
| int64 getTotalLength() | |||
| { | |||
| if (! isError()) | |||
| { | |||
| DWORD index = 0, result = 0, size = sizeof (result); | |||
| if (HttpQueryInfo (request, HTTP_QUERY_CONTENT_LENGTH | HTTP_QUERY_FLAG_NUMBER, &result, &size, &index)) | |||
| return (int64) result; | |||
| } | |||
| return -1; | |||
| } | |||
| int read (void* buffer, int bytesToRead) | |||
| { | |||
| jassert (buffer != nullptr && bytesToRead >= 0); | |||
| DWORD bytesRead = 0; | |||
| if (! (finished || isError())) | |||
| { | |||
| InternetReadFile (request, buffer, (DWORD) bytesToRead, &bytesRead); | |||
| position += bytesRead; | |||
| if (bytesRead == 0) | |||
| finished = true; | |||
| } | |||
| return (int) bytesRead; | |||
| } | |||
| bool setPosition (int64 wantedPos) | |||
| { | |||
| if (isError()) | |||
| return false; | |||
| if (wantedPos != position) | |||
| { | |||
| finished = false; | |||
| position = (int64) InternetSetFilePointer (request, (LONG) wantedPos, 0, FILE_BEGIN, 0); | |||
| if (position == wantedPos) | |||
| return true; | |||
| if (wantedPos < position) | |||
| { | |||
| close(); | |||
| position = 0; | |||
| createConnection (0, 0); | |||
| } | |||
| skipNextBytes (wantedPos - position); | |||
| } | |||
| return true; | |||
| } | |||
| private: | |||
| //============================================================================== | |||
| HINTERNET connection, request; | |||
| String address, headers; | |||
| MemoryBlock postData; | |||
| int64 position; | |||
| bool finished; | |||
| const bool isPost; | |||
| int timeOutMs; | |||
| void close() | |||
| { | |||
| if (request != 0) | |||
| { | |||
| InternetCloseHandle (request); | |||
| request = 0; | |||
| } | |||
| if (connection != 0) | |||
| { | |||
| InternetCloseHandle (connection); | |||
| connection = 0; | |||
| } | |||
| } | |||
| void createConnection (URL::OpenStreamProgressCallback* progressCallback, | |||
| void* progressCallbackContext) | |||
| { | |||
| static HINTERNET sessionHandle = InternetOpen (_T("juce"), INTERNET_OPEN_TYPE_PRECONFIG, 0, 0, 0); | |||
| close(); | |||
| if (sessionHandle != 0) | |||
| { | |||
| // break up the url.. | |||
| const int fileNumChars = 65536; | |||
| const int serverNumChars = 2048; | |||
| const int usernameNumChars = 1024; | |||
| const int passwordNumChars = 1024; | |||
| HeapBlock<TCHAR> file (fileNumChars), server (serverNumChars), | |||
| username (usernameNumChars), password (passwordNumChars); | |||
| URL_COMPONENTS uc = { 0 }; | |||
| uc.dwStructSize = sizeof (uc); | |||
| uc.lpszUrlPath = file; | |||
| uc.dwUrlPathLength = fileNumChars; | |||
| uc.lpszHostName = server; | |||
| uc.dwHostNameLength = serverNumChars; | |||
| uc.lpszUserName = username; | |||
| uc.dwUserNameLength = usernameNumChars; | |||
| uc.lpszPassword = password; | |||
| uc.dwPasswordLength = passwordNumChars; | |||
| if (InternetCrackUrl (address.toWideCharPointer(), 0, 0, &uc)) | |||
| openConnection (uc, sessionHandle, progressCallback, progressCallbackContext); | |||
| } | |||
| } | |||
| void openConnection (URL_COMPONENTS& uc, HINTERNET sessionHandle, | |||
| URL::OpenStreamProgressCallback* progressCallback, | |||
| void* progressCallbackContext) | |||
| { | |||
| int disable = 1; | |||
| InternetSetOption (sessionHandle, INTERNET_OPTION_DISABLE_AUTODIAL, &disable, sizeof (disable)); | |||
| if (timeOutMs == 0) | |||
| timeOutMs = 30000; | |||
| else if (timeOutMs < 0) | |||
| timeOutMs = -1; | |||
| applyTimeout (sessionHandle, INTERNET_OPTION_CONNECT_TIMEOUT); | |||
| applyTimeout (sessionHandle, INTERNET_OPTION_RECEIVE_TIMEOUT); | |||
| applyTimeout (sessionHandle, INTERNET_OPTION_SEND_TIMEOUT); | |||
| applyTimeout (sessionHandle, INTERNET_OPTION_DATA_RECEIVE_TIMEOUT); | |||
| applyTimeout (sessionHandle, INTERNET_OPTION_DATA_SEND_TIMEOUT); | |||
| const bool isFtp = address.startsWithIgnoreCase ("ftp:"); | |||
| connection = InternetConnect (sessionHandle, uc.lpszHostName, uc.nPort, | |||
| uc.lpszUserName, uc.lpszPassword, | |||
| isFtp ? (DWORD) INTERNET_SERVICE_FTP | |||
| : (DWORD) INTERNET_SERVICE_HTTP, | |||
| 0, 0); | |||
| if (connection != 0) | |||
| { | |||
| if (isFtp) | |||
| request = FtpOpenFile (connection, uc.lpszUrlPath, GENERIC_READ, | |||
| FTP_TRANSFER_TYPE_BINARY | INTERNET_FLAG_NEED_FILE, 0); | |||
| else | |||
| openHTTPConnection (uc, progressCallback, progressCallbackContext); | |||
| } | |||
| } | |||
| void applyTimeout (HINTERNET sessionHandle, const DWORD option) | |||
| { | |||
| InternetSetOption (sessionHandle, option, &timeOutMs, sizeof (timeOutMs)); | |||
| } | |||
| void openHTTPConnection (URL_COMPONENTS& uc, URL::OpenStreamProgressCallback* progressCallback, | |||
| void* progressCallbackContext) | |||
| { | |||
| const TCHAR* mimeTypes[] = { _T("*/*"), nullptr }; | |||
| DWORD flags = INTERNET_FLAG_RELOAD | INTERNET_FLAG_NO_CACHE_WRITE | INTERNET_FLAG_NO_COOKIES; | |||
| if (address.startsWithIgnoreCase ("https:")) | |||
| flags |= INTERNET_FLAG_SECURE; // (this flag only seems necessary if the OS is running IE6 - | |||
| // IE7 seems to automatically work out when it's https) | |||
| request = HttpOpenRequest (connection, isPost ? _T("POST") : _T("GET"), | |||
| uc.lpszUrlPath, 0, 0, mimeTypes, flags, 0); | |||
| if (request != 0) | |||
| { | |||
| INTERNET_BUFFERS buffers = { 0 }; | |||
| buffers.dwStructSize = sizeof (INTERNET_BUFFERS); | |||
| buffers.lpcszHeader = headers.toWideCharPointer(); | |||
| buffers.dwHeadersLength = (DWORD) headers.length(); | |||
| buffers.dwBufferTotal = (DWORD) postData.getSize(); | |||
| if (HttpSendRequestEx (request, &buffers, 0, HSR_INITIATE, 0)) | |||
| { | |||
| int bytesSent = 0; | |||
| for (;;) | |||
| { | |||
| const int bytesToDo = jmin (1024, (int) postData.getSize() - bytesSent); | |||
| DWORD bytesDone = 0; | |||
| if (bytesToDo > 0 | |||
| && ! InternetWriteFile (request, | |||
| static_cast <const char*> (postData.getData()) + bytesSent, | |||
| (DWORD) bytesToDo, &bytesDone)) | |||
| { | |||
| break; | |||
| } | |||
| if (bytesToDo == 0 || (int) bytesDone < bytesToDo) | |||
| { | |||
| if (HttpEndRequest (request, 0, 0, 0)) | |||
| return; | |||
| break; | |||
| } | |||
| bytesSent += bytesDone; | |||
| if (progressCallback != nullptr | |||
| && ! progressCallback (progressCallbackContext, bytesSent, (int) postData.getSize())) | |||
| break; | |||
| } | |||
| } | |||
| } | |||
| close(); | |||
| } | |||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WebInputStream) | |||
| }; | |||
| InputStream* URL::createNativeStream (const String& address, bool isPost, const MemoryBlock& postData, | |||
| OpenStreamProgressCallback* progressCallback, void* progressCallbackContext, | |||
| const String& headers, const int timeOutMs, StringPairArray* responseHeaders) | |||
| { | |||
| ScopedPointer <WebInputStream> wi (new WebInputStream (address, isPost, postData, | |||
| progressCallback, progressCallbackContext, | |||
| headers, timeOutMs, responseHeaders)); | |||
| return wi->isError() ? nullptr : wi.release(); | |||
| } | |||
| //============================================================================== | |||
| struct GetAdaptersInfoHelper | |||
| { | |||
| bool callGetAdaptersInfo() | |||
| { | |||
| DynamicLibrary dll ("iphlpapi.dll"); | |||
| JUCE_LOAD_WINAPI_FUNCTION (dll, GetAdaptersInfo, getAdaptersInfo, DWORD, (PIP_ADAPTER_INFO, PULONG)) | |||
| if (getAdaptersInfo == nullptr) | |||
| return false; | |||
| adapterInfo.malloc (1); | |||
| ULONG len = sizeof (IP_ADAPTER_INFO); | |||
| if (getAdaptersInfo (adapterInfo, &len) == ERROR_BUFFER_OVERFLOW) | |||
| adapterInfo.malloc (len, 1); | |||
| return getAdaptersInfo (adapterInfo, &len) == NO_ERROR; | |||
| } | |||
| HeapBlock<IP_ADAPTER_INFO> adapterInfo; | |||
| }; | |||
| namespace MACAddressHelpers | |||
| { | |||
| void getViaGetAdaptersInfo (Array<MACAddress>& result) | |||
| { | |||
| GetAdaptersInfoHelper gah; | |||
| if (gah.callGetAdaptersInfo()) | |||
| { | |||
| for (PIP_ADAPTER_INFO adapter = gah.adapterInfo; adapter != nullptr; adapter = adapter->Next) | |||
| if (adapter->AddressLength >= 6) | |||
| result.addIfNotAlreadyThere (MACAddress (adapter->Address)); | |||
| } | |||
| } | |||
| void getViaNetBios (Array<MACAddress>& result) | |||
| { | |||
| DynamicLibrary dll ("netapi32.dll"); | |||
| JUCE_LOAD_WINAPI_FUNCTION (dll, Netbios, NetbiosCall, UCHAR, (PNCB)) | |||
| if (NetbiosCall != 0) | |||
| { | |||
| LANA_ENUM enums = { 0 }; | |||
| { | |||
| NCB ncb = { 0 }; | |||
| ncb.ncb_command = NCBENUM; | |||
| ncb.ncb_buffer = (unsigned char*) &enums; | |||
| ncb.ncb_length = sizeof (LANA_ENUM); | |||
| NetbiosCall (&ncb); | |||
| } | |||
| for (int i = 0; i < enums.length; ++i) | |||
| { | |||
| NCB ncb2 = { 0 }; | |||
| ncb2.ncb_command = NCBRESET; | |||
| ncb2.ncb_lana_num = enums.lana[i]; | |||
| if (NetbiosCall (&ncb2) == 0) | |||
| { | |||
| NCB ncb = { 0 }; | |||
| memcpy (ncb.ncb_callname, "* ", NCBNAMSZ); | |||
| ncb.ncb_command = NCBASTAT; | |||
| ncb.ncb_lana_num = enums.lana[i]; | |||
| struct ASTAT | |||
| { | |||
| ADAPTER_STATUS adapt; | |||
| NAME_BUFFER NameBuff [30]; | |||
| }; | |||
| ASTAT astat; | |||
| zerostruct (astat); | |||
| ncb.ncb_buffer = (unsigned char*) &astat; | |||
| ncb.ncb_length = sizeof (ASTAT); | |||
| if (NetbiosCall (&ncb) == 0 && astat.adapt.adapter_type == 0xfe) | |||
| result.addIfNotAlreadyThere (MACAddress (astat.adapt.adapter_address)); | |||
| } | |||
| } | |||
| } | |||
| } | |||
| } | |||
| void MACAddress::findAllAddresses (Array<MACAddress>& result) | |||
| { | |||
| MACAddressHelpers::getViaGetAdaptersInfo (result); | |||
| MACAddressHelpers::getViaNetBios (result); | |||
| } | |||
| void IPAddress::findAllAddresses (Array<IPAddress>& result) | |||
| { | |||
| result.addIfNotAlreadyThere (IPAddress::local()); | |||
| GetAdaptersInfoHelper gah; | |||
| if (gah.callGetAdaptersInfo()) | |||
| { | |||
| for (PIP_ADAPTER_INFO adapter = gah.adapterInfo; adapter != nullptr; adapter = adapter->Next) | |||
| { | |||
| IPAddress ip (adapter->IpAddressList.IpAddress.String); | |||
| if (ip != IPAddress::any()) | |||
| result.addIfNotAlreadyThere (ip); | |||
| } | |||
| } | |||
| } | |||
| //============================================================================== | |||
| bool Process::openEmailWithAttachments (const String& targetEmailAddress, | |||
| const String& emailSubject, | |||
| const String& bodyText, | |||
| const StringArray& filesToAttach) | |||
| { | |||
| DynamicLibrary dll ("MAPI32.dll"); | |||
| JUCE_LOAD_WINAPI_FUNCTION (dll, MAPISendMail, mapiSendMail, | |||
| ULONG, (LHANDLE, ULONG, lpMapiMessage, ::FLAGS, ULONG)) | |||
| if (mapiSendMail == nullptr) | |||
| return false; | |||
| MapiMessage message = { 0 }; | |||
| message.lpszSubject = (LPSTR) emailSubject.toRawUTF8(); | |||
| message.lpszNoteText = (LPSTR) bodyText.toRawUTF8(); | |||
| MapiRecipDesc recip = { 0 }; | |||
| recip.ulRecipClass = MAPI_TO; | |||
| String targetEmailAddress_ (targetEmailAddress); | |||
| if (targetEmailAddress_.isEmpty()) | |||
| targetEmailAddress_ = " "; // (Windows Mail can't deal with a blank address) | |||
| recip.lpszName = (LPSTR) targetEmailAddress_.toRawUTF8(); | |||
| message.nRecipCount = 1; | |||
| message.lpRecips = &recip; | |||
| HeapBlock <MapiFileDesc> files; | |||
| files.calloc ((size_t) filesToAttach.size()); | |||
| message.nFileCount = (ULONG) filesToAttach.size(); | |||
| message.lpFiles = files; | |||
| for (int i = 0; i < filesToAttach.size(); ++i) | |||
| { | |||
| files[i].nPosition = (ULONG) -1; | |||
| files[i].lpszPathName = (LPSTR) filesToAttach[i].toRawUTF8(); | |||
| } | |||
| return mapiSendMail (0, 0, &message, MAPI_DIALOG | MAPI_LOGON_UI, 0) == SUCCESS_SUCCESS; | |||
| } | |||
| @@ -0,0 +1,236 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the juce_core module of the JUCE library. | |||
| Copyright (c) 2013 - Raw Material Software Ltd. | |||
| 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 THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||
| TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||
| NO EVENT SHALL THE AUTHOR 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. | |||
| ------------------------------------------------------------------------------ | |||
| NOTE! This permissive ISC license applies ONLY to files within the juce_core module! | |||
| All other JUCE modules are covered by a dual GPL/commercial license, so if you are | |||
| using any other modules, be sure to check that you also comply with their license. | |||
| For more details, visit www.juce.com | |||
| ============================================================================== | |||
| */ | |||
| struct RegistryKeyWrapper | |||
| { | |||
| RegistryKeyWrapper (String name, const bool createForWriting, const DWORD wow64Flags) | |||
| : key (0), wideCharValueName (nullptr) | |||
| { | |||
| HKEY rootKey = 0; | |||
| if (name.startsWithIgnoreCase ("HKEY_CURRENT_USER\\")) rootKey = HKEY_CURRENT_USER; | |||
| else if (name.startsWithIgnoreCase ("HKEY_LOCAL_MACHINE\\")) rootKey = HKEY_LOCAL_MACHINE; | |||
| else if (name.startsWithIgnoreCase ("HKEY_CLASSES_ROOT\\")) rootKey = HKEY_CLASSES_ROOT; | |||
| if (rootKey != 0) | |||
| { | |||
| name = name.substring (name.indexOfChar ('\\') + 1); | |||
| const int lastSlash = name.lastIndexOfChar ('\\'); | |||
| valueName = name.substring (lastSlash + 1); | |||
| wideCharValueName = valueName.toWideCharPointer(); | |||
| name = name.substring (0, lastSlash); | |||
| const wchar_t* const wideCharName = name.toWideCharPointer(); | |||
| DWORD result; | |||
| if (createForWriting) | |||
| RegCreateKeyEx (rootKey, wideCharName, 0, 0, REG_OPTION_NON_VOLATILE, | |||
| KEY_WRITE | KEY_QUERY_VALUE | wow64Flags, 0, &key, &result); | |||
| else | |||
| RegOpenKeyEx (rootKey, wideCharName, 0, KEY_READ | wow64Flags, &key); | |||
| } | |||
| } | |||
| ~RegistryKeyWrapper() | |||
| { | |||
| if (key != 0) | |||
| RegCloseKey (key); | |||
| } | |||
| static bool setValue (const String& regValuePath, const DWORD type, | |||
| const void* data, size_t dataSize) | |||
| { | |||
| const RegistryKeyWrapper key (regValuePath, true, 0); | |||
| return key.key != 0 | |||
| && RegSetValueEx (key.key, key.wideCharValueName, 0, type, | |||
| reinterpret_cast <const BYTE*> (data), | |||
| (DWORD) dataSize) == ERROR_SUCCESS; | |||
| } | |||
| static uint32 getBinaryValue (const String& regValuePath, MemoryBlock& result, DWORD wow64Flags) | |||
| { | |||
| const RegistryKeyWrapper key (regValuePath, false, wow64Flags); | |||
| if (key.key != 0) | |||
| { | |||
| for (unsigned long bufferSize = 1024; ; bufferSize *= 2) | |||
| { | |||
| result.setSize (bufferSize, false); | |||
| DWORD type = REG_NONE; | |||
| const LONG err = RegQueryValueEx (key.key, key.wideCharValueName, 0, &type, | |||
| (LPBYTE) result.getData(), &bufferSize); | |||
| if (err == ERROR_SUCCESS) | |||
| { | |||
| result.setSize (bufferSize, false); | |||
| return type; | |||
| } | |||
| if (err != ERROR_MORE_DATA) | |||
| break; | |||
| } | |||
| } | |||
| return REG_NONE; | |||
| } | |||
| static String getValue (const String& regValuePath, const String& defaultValue, DWORD wow64Flags) | |||
| { | |||
| MemoryBlock buffer; | |||
| switch (getBinaryValue (regValuePath, buffer, wow64Flags)) | |||
| { | |||
| case REG_SZ: return static_cast <const WCHAR*> (buffer.getData()); | |||
| case REG_DWORD: return String ((int) *reinterpret_cast<const DWORD*> (buffer.getData())); | |||
| default: break; | |||
| } | |||
| return defaultValue; | |||
| } | |||
| static bool keyExists (const String& regValuePath, const DWORD wow64Flags) | |||
| { | |||
| return RegistryKeyWrapper (regValuePath, false, wow64Flags).key != 0; | |||
| } | |||
| static bool valueExists (const String& regValuePath, const DWORD wow64Flags) | |||
| { | |||
| const RegistryKeyWrapper key (regValuePath, false, wow64Flags); | |||
| if (key.key == 0) | |||
| return false; | |||
| unsigned char buffer [512]; | |||
| unsigned long bufferSize = sizeof (buffer); | |||
| DWORD type = 0; | |||
| const LONG result = RegQueryValueEx (key.key, key.wideCharValueName, | |||
| 0, &type, buffer, &bufferSize); | |||
| return result == ERROR_SUCCESS || result == ERROR_MORE_DATA; | |||
| } | |||
| HKEY key; | |||
| const wchar_t* wideCharValueName; | |||
| String valueName; | |||
| JUCE_DECLARE_NON_COPYABLE (RegistryKeyWrapper) | |||
| }; | |||
| uint32 WindowsRegistry::getBinaryValue (const String& regValuePath, MemoryBlock& result) | |||
| { | |||
| return RegistryKeyWrapper::getBinaryValue (regValuePath, result, 0); | |||
| } | |||
| String WindowsRegistry::getValue (const String& regValuePath, const String& defaultValue) | |||
| { | |||
| return RegistryKeyWrapper::getValue (regValuePath, defaultValue, 0); | |||
| } | |||
| String WindowsRegistry::getValueWow64 (const String& regValuePath, const String& defaultValue) | |||
| { | |||
| return RegistryKeyWrapper::getValue (regValuePath, defaultValue, 0x100 /*KEY_WOW64_64KEY*/); | |||
| } | |||
| bool WindowsRegistry::valueExistsWow64 (const String& regValuePath) | |||
| { | |||
| return RegistryKeyWrapper::valueExists (regValuePath, 0x100 /*KEY_WOW64_64KEY*/); | |||
| } | |||
| bool WindowsRegistry::keyExistsWow64 (const String& regValuePath) | |||
| { | |||
| return RegistryKeyWrapper::keyExists (regValuePath, 0x100 /*KEY_WOW64_64KEY*/); | |||
| } | |||
| bool WindowsRegistry::setValue (const String& regValuePath, const String& value) | |||
| { | |||
| return RegistryKeyWrapper::setValue (regValuePath, REG_SZ, value.toWideCharPointer(), | |||
| CharPointer_UTF16::getBytesRequiredFor (value.getCharPointer())); | |||
| } | |||
| bool WindowsRegistry::setValue (const String& regValuePath, const uint32 value) | |||
| { | |||
| return RegistryKeyWrapper::setValue (regValuePath, REG_DWORD, &value, sizeof (value)); | |||
| } | |||
| bool WindowsRegistry::setValue (const String& regValuePath, const uint64 value) | |||
| { | |||
| return RegistryKeyWrapper::setValue (regValuePath, REG_QWORD, &value, sizeof (value)); | |||
| } | |||
| bool WindowsRegistry::setValue (const String& regValuePath, const MemoryBlock& value) | |||
| { | |||
| return RegistryKeyWrapper::setValue (regValuePath, REG_BINARY, value.getData(), value.getSize()); | |||
| } | |||
| bool WindowsRegistry::valueExists (const String& regValuePath) | |||
| { | |||
| return RegistryKeyWrapper::valueExists (regValuePath, 0); | |||
| } | |||
| bool WindowsRegistry::keyExists (const String& regValuePath) | |||
| { | |||
| return RegistryKeyWrapper::keyExists (regValuePath, 0); | |||
| } | |||
| void WindowsRegistry::deleteValue (const String& regValuePath) | |||
| { | |||
| const RegistryKeyWrapper key (regValuePath, true, 0); | |||
| if (key.key != 0) | |||
| RegDeleteValue (key.key, key.wideCharValueName); | |||
| } | |||
| void WindowsRegistry::deleteKey (const String& regKeyPath) | |||
| { | |||
| const RegistryKeyWrapper key (regKeyPath, true, 0); | |||
| if (key.key != 0) | |||
| RegDeleteKey (key.key, key.wideCharValueName); | |||
| } | |||
| bool WindowsRegistry::registerFileAssociation (const String& fileExtension, | |||
| const String& symbolicDescription, | |||
| const String& fullDescription, | |||
| const File& targetExecutable, | |||
| const int iconResourceNumber, | |||
| const bool registerForCurrentUserOnly) | |||
| { | |||
| const char* const root = registerForCurrentUserOnly ? "HKEY_CURRENT_USER\\Software\\Classes\\" | |||
| : "HKEY_CLASSES_ROOT\\"; | |||
| const String key (root + symbolicDescription); | |||
| return setValue (root + fileExtension + "\\", symbolicDescription) | |||
| && setValue (key + "\\", fullDescription) | |||
| && setValue (key + "\\shell\\open\\command\\", targetExecutable.getFullPathName() + " \"%1\"") | |||
| && (iconResourceNumber == 0 | |||
| || setValue (key + "\\DefaultIcon\\", | |||
| targetExecutable.getFullPathName() + "," + String (-iconResourceNumber))); | |||
| } | |||
| @@ -0,0 +1,435 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the juce_core module of the JUCE library. | |||
| Copyright (c) 2013 - Raw Material Software Ltd. | |||
| 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 THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||
| TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||
| NO EVENT SHALL THE AUTHOR 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. | |||
| ------------------------------------------------------------------------------ | |||
| NOTE! This permissive ISC license applies ONLY to files within the juce_core module! | |||
| All other JUCE modules are covered by a dual GPL/commercial license, so if you are | |||
| using any other modules, be sure to check that you also comply with their license. | |||
| For more details, visit www.juce.com | |||
| ============================================================================== | |||
| */ | |||
| void Logger::outputDebugString (const String& text) | |||
| { | |||
| OutputDebugString ((text + "\n").toWideCharPointer()); | |||
| } | |||
| //============================================================================== | |||
| #ifdef JUCE_DLL_BUILD | |||
| JUCE_API void* juceDLL_malloc (size_t sz) { return std::malloc (sz); } | |||
| JUCE_API void juceDLL_free (void* block) { std::free (block); } | |||
| #endif | |||
| //============================================================================== | |||
| #if JUCE_USE_INTRINSICS | |||
| // CPU info functions using intrinsics... | |||
| #pragma intrinsic (__cpuid) | |||
| #pragma intrinsic (__rdtsc) | |||
| String SystemStats::getCpuVendor() | |||
| { | |||
| int info [4]; | |||
| __cpuid (info, 0); | |||
| char v [12]; | |||
| memcpy (v, info + 1, 4); | |||
| memcpy (v + 4, info + 3, 4); | |||
| memcpy (v + 8, info + 2, 4); | |||
| return String (v, 12); | |||
| } | |||
| #else | |||
| //============================================================================== | |||
| // CPU info functions using old fashioned inline asm... | |||
| static void juce_getCpuVendor (char* const v) | |||
| { | |||
| int vendor[4] = { 0 }; | |||
| #if ! JUCE_MINGW | |||
| __try | |||
| #endif | |||
| { | |||
| #if JUCE_GCC | |||
| unsigned int dummy = 0; | |||
| __asm__ ("cpuid" : "=a" (dummy), "=b" (vendor[0]), "=c" (vendor[2]),"=d" (vendor[1]) : "a" (0)); | |||
| #else | |||
| __asm | |||
| { | |||
| mov eax, 0 | |||
| cpuid | |||
| mov [vendor], ebx | |||
| mov [vendor + 4], edx | |||
| mov [vendor + 8], ecx | |||
| } | |||
| #endif | |||
| } | |||
| #if ! JUCE_MINGW | |||
| __except (EXCEPTION_EXECUTE_HANDLER) | |||
| { | |||
| } | |||
| #endif | |||
| memcpy (v, vendor, 16); | |||
| } | |||
| String SystemStats::getCpuVendor() | |||
| { | |||
| char v [16]; | |||
| juce_getCpuVendor (v); | |||
| return String (v, 16); | |||
| } | |||
| #endif | |||
| //============================================================================== | |||
| void CPUInformation::initialise() noexcept | |||
| { | |||
| hasMMX = IsProcessorFeaturePresent (PF_MMX_INSTRUCTIONS_AVAILABLE) != 0; | |||
| hasSSE = IsProcessorFeaturePresent (PF_XMMI_INSTRUCTIONS_AVAILABLE) != 0; | |||
| hasSSE2 = IsProcessorFeaturePresent (PF_XMMI64_INSTRUCTIONS_AVAILABLE) != 0; | |||
| hasSSE3 = IsProcessorFeaturePresent (13 /*PF_SSE3_INSTRUCTIONS_AVAILABLE*/) != 0; | |||
| has3DNow = IsProcessorFeaturePresent (7 /*PF_AMD3D_INSTRUCTIONS_AVAILABLE*/) != 0; | |||
| SYSTEM_INFO systemInfo; | |||
| GetNativeSystemInfo (&systemInfo); | |||
| numCpus = (int) systemInfo.dwNumberOfProcessors; | |||
| } | |||
| #if JUCE_MSVC && JUCE_CHECK_MEMORY_LEAKS | |||
| struct DebugFlagsInitialiser | |||
| { | |||
| DebugFlagsInitialiser() | |||
| { | |||
| _CrtSetDbgFlag (_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF); | |||
| } | |||
| }; | |||
| static DebugFlagsInitialiser debugFlagsInitialiser; | |||
| #endif | |||
| //============================================================================== | |||
| static bool isWindowsVersionOrLater (SystemStats::OperatingSystemType target) | |||
| { | |||
| OSVERSIONINFOEX info; | |||
| zerostruct (info); | |||
| info.dwOSVersionInfoSize = sizeof (OSVERSIONINFOEX); | |||
| if (target >= SystemStats::WinVista) | |||
| { | |||
| info.dwMajorVersion = 6; | |||
| switch (target) | |||
| { | |||
| case SystemStats::WinVista: info.dwMinorVersion = 0; break; | |||
| case SystemStats::Windows7: info.dwMinorVersion = 1; break; | |||
| case SystemStats::Windows8: info.dwMinorVersion = 2; break; | |||
| default: jassertfalse; break; | |||
| } | |||
| } | |||
| else | |||
| { | |||
| info.dwMajorVersion = 5; | |||
| info.dwMinorVersion = target >= SystemStats::WinXP ? 1 : 0; | |||
| } | |||
| DWORDLONG mask = 0; | |||
| VER_SET_CONDITION (mask, VER_MAJORVERSION, VER_GREATER_EQUAL); | |||
| VER_SET_CONDITION (mask, VER_MINORVERSION, VER_GREATER_EQUAL); | |||
| VER_SET_CONDITION (mask, VER_SERVICEPACKMAJOR, VER_GREATER_EQUAL); | |||
| VER_SET_CONDITION (mask, VER_SERVICEPACKMINOR, VER_GREATER_EQUAL); | |||
| return VerifyVersionInfo (&info, | |||
| VER_MAJORVERSION | VER_MINORVERSION | |||
| | VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR, | |||
| mask) != FALSE; | |||
| } | |||
| SystemStats::OperatingSystemType SystemStats::getOperatingSystemType() | |||
| { | |||
| const SystemStats::OperatingSystemType types[] | |||
| = { Windows8, Windows7, WinVista, WinXP, Win2000 }; | |||
| for (int i = 0; i < numElementsInArray (types); ++i) | |||
| if (isWindowsVersionOrLater (types[i])) | |||
| return types[i]; | |||
| jassertfalse; // need to support whatever new version is running! | |||
| return UnknownOS; | |||
| } | |||
| String SystemStats::getOperatingSystemName() | |||
| { | |||
| const char* name = "Unknown OS"; | |||
| switch (getOperatingSystemType()) | |||
| { | |||
| case Windows7: name = "Windows 7"; break; | |||
| case Windows8: name = "Windows 8"; break; | |||
| case WinVista: name = "Windows Vista"; break; | |||
| case WinXP: name = "Windows XP"; break; | |||
| case Win2000: name = "Windows 2000"; break; | |||
| default: jassertfalse; break; // !! new type of OS? | |||
| } | |||
| return name; | |||
| } | |||
| bool SystemStats::isOperatingSystem64Bit() | |||
| { | |||
| #if JUCE_64BIT | |||
| return true; | |||
| #else | |||
| typedef BOOL (WINAPI* LPFN_ISWOW64PROCESS) (HANDLE, PBOOL); | |||
| LPFN_ISWOW64PROCESS fnIsWow64Process | |||
| = (LPFN_ISWOW64PROCESS) GetProcAddress (GetModuleHandleA ("kernel32"), "IsWow64Process"); | |||
| BOOL isWow64 = FALSE; | |||
| return fnIsWow64Process != nullptr | |||
| && fnIsWow64Process (GetCurrentProcess(), &isWow64) | |||
| && isWow64 != FALSE; | |||
| #endif | |||
| } | |||
| //============================================================================== | |||
| int SystemStats::getMemorySizeInMegabytes() | |||
| { | |||
| MEMORYSTATUSEX mem; | |||
| mem.dwLength = sizeof (mem); | |||
| GlobalMemoryStatusEx (&mem); | |||
| return (int) (mem.ullTotalPhys / (1024 * 1024)) + 1; | |||
| } | |||
| //============================================================================== | |||
| String SystemStats::getEnvironmentVariable (const String& name, const String& defaultValue) | |||
| { | |||
| DWORD len = GetEnvironmentVariableW (name.toWideCharPointer(), nullptr, 0); | |||
| if (GetLastError() == ERROR_ENVVAR_NOT_FOUND) | |||
| return String (defaultValue); | |||
| HeapBlock<WCHAR> buffer (len); | |||
| len = GetEnvironmentVariableW (name.toWideCharPointer(), buffer, len); | |||
| return String (CharPointer_wchar_t (buffer), | |||
| CharPointer_wchar_t (buffer + len)); | |||
| } | |||
| //============================================================================== | |||
| uint32 juce_millisecondsSinceStartup() noexcept | |||
| { | |||
| return (uint32) timeGetTime(); | |||
| } | |||
| //============================================================================== | |||
| class HiResCounterHandler | |||
| { | |||
| public: | |||
| HiResCounterHandler() | |||
| : hiResTicksOffset (0) | |||
| { | |||
| const MMRESULT res = timeBeginPeriod (1); | |||
| (void) res; | |||
| jassert (res == TIMERR_NOERROR); | |||
| LARGE_INTEGER f; | |||
| QueryPerformanceFrequency (&f); | |||
| hiResTicksPerSecond = f.QuadPart; | |||
| hiResTicksScaleFactor = 1000.0 / hiResTicksPerSecond; | |||
| } | |||
| inline int64 getHighResolutionTicks() noexcept | |||
| { | |||
| LARGE_INTEGER ticks; | |||
| QueryPerformanceCounter (&ticks); | |||
| const int64 mainCounterAsHiResTicks = (juce_millisecondsSinceStartup() * hiResTicksPerSecond) / 1000; | |||
| const int64 newOffset = mainCounterAsHiResTicks - ticks.QuadPart; | |||
| // fix for a very obscure PCI hardware bug that can make the counter | |||
| // sometimes jump forwards by a few seconds.. | |||
| const int64 offsetDrift = abs64 (newOffset - hiResTicksOffset); | |||
| if (offsetDrift > (hiResTicksPerSecond >> 1)) | |||
| hiResTicksOffset = newOffset; | |||
| return ticks.QuadPart + hiResTicksOffset; | |||
| } | |||
| inline double getMillisecondCounterHiRes() noexcept | |||
| { | |||
| return getHighResolutionTicks() * hiResTicksScaleFactor; | |||
| } | |||
| int64 hiResTicksPerSecond, hiResTicksOffset; | |||
| double hiResTicksScaleFactor; | |||
| }; | |||
| static HiResCounterHandler hiResCounterHandler; | |||
| int64 Time::getHighResolutionTicksPerSecond() noexcept { return hiResCounterHandler.hiResTicksPerSecond; } | |||
| int64 Time::getHighResolutionTicks() noexcept { return hiResCounterHandler.getHighResolutionTicks(); } | |||
| double Time::getMillisecondCounterHiRes() noexcept { return hiResCounterHandler.getMillisecondCounterHiRes(); } | |||
| //============================================================================== | |||
| static int64 juce_getClockCycleCounter() noexcept | |||
| { | |||
| #if JUCE_USE_INTRINSICS | |||
| // MS intrinsics version... | |||
| return (int64) __rdtsc(); | |||
| #elif JUCE_GCC | |||
| // GNU inline asm version... | |||
| unsigned int hi = 0, lo = 0; | |||
| __asm__ __volatile__ ( | |||
| "xor %%eax, %%eax \n\ | |||
| xor %%edx, %%edx \n\ | |||
| rdtsc \n\ | |||
| movl %%eax, %[lo] \n\ | |||
| movl %%edx, %[hi]" | |||
| : | |||
| : [hi] "m" (hi), | |||
| [lo] "m" (lo) | |||
| : "cc", "eax", "ebx", "ecx", "edx", "memory"); | |||
| return (int64) ((((uint64) hi) << 32) | lo); | |||
| #else | |||
| // MSVC inline asm version... | |||
| unsigned int hi = 0, lo = 0; | |||
| __asm | |||
| { | |||
| xor eax, eax | |||
| xor edx, edx | |||
| rdtsc | |||
| mov lo, eax | |||
| mov hi, edx | |||
| } | |||
| return (int64) ((((uint64) hi) << 32) | lo); | |||
| #endif | |||
| } | |||
| int SystemStats::getCpuSpeedInMegaherz() | |||
| { | |||
| const int64 cycles = juce_getClockCycleCounter(); | |||
| const uint32 millis = Time::getMillisecondCounter(); | |||
| int lastResult = 0; | |||
| for (;;) | |||
| { | |||
| int n = 1000000; | |||
| while (--n > 0) {} | |||
| const uint32 millisElapsed = Time::getMillisecondCounter() - millis; | |||
| const int64 cyclesNow = juce_getClockCycleCounter(); | |||
| if (millisElapsed > 80) | |||
| { | |||
| const int newResult = (int) (((cyclesNow - cycles) / millisElapsed) / 1000); | |||
| if (millisElapsed > 500 || (lastResult == newResult && newResult > 100)) | |||
| return newResult; | |||
| lastResult = newResult; | |||
| } | |||
| } | |||
| } | |||
| //============================================================================== | |||
| bool Time::setSystemTimeToThisTime() const | |||
| { | |||
| SYSTEMTIME st; | |||
| st.wDayOfWeek = 0; | |||
| st.wYear = (WORD) getYear(); | |||
| st.wMonth = (WORD) (getMonth() + 1); | |||
| st.wDay = (WORD) getDayOfMonth(); | |||
| st.wHour = (WORD) getHours(); | |||
| st.wMinute = (WORD) getMinutes(); | |||
| st.wSecond = (WORD) getSeconds(); | |||
| st.wMilliseconds = (WORD) (millisSinceEpoch % 1000); | |||
| // do this twice because of daylight saving conversion problems - the | |||
| // first one sets it up, the second one kicks it in. | |||
| return SetLocalTime (&st) != 0 | |||
| && SetLocalTime (&st) != 0; | |||
| } | |||
| int SystemStats::getPageSize() | |||
| { | |||
| SYSTEM_INFO systemInfo; | |||
| GetNativeSystemInfo (&systemInfo); | |||
| return (int) systemInfo.dwPageSize; | |||
| } | |||
| //============================================================================== | |||
| String SystemStats::getLogonName() | |||
| { | |||
| TCHAR text [256] = { 0 }; | |||
| DWORD len = (DWORD) numElementsInArray (text) - 1; | |||
| GetUserName (text, &len); | |||
| return String (text, len); | |||
| } | |||
| String SystemStats::getFullUserName() | |||
| { | |||
| return getLogonName(); | |||
| } | |||
| String SystemStats::getComputerName() | |||
| { | |||
| TCHAR text [MAX_COMPUTERNAME_LENGTH + 1] = { 0 }; | |||
| DWORD len = (DWORD) numElementsInArray (text) - 1; | |||
| GetComputerName (text, &len); | |||
| return String (text, len); | |||
| } | |||
| static String getLocaleValue (LCID locale, LCTYPE key, const char* defaultValue) | |||
| { | |||
| TCHAR buffer [256] = { 0 }; | |||
| if (GetLocaleInfo (locale, key, buffer, 255) > 0) | |||
| return buffer; | |||
| return defaultValue; | |||
| } | |||
| String SystemStats::getUserLanguage() { return getLocaleValue (LOCALE_USER_DEFAULT, LOCALE_SISO639LANGNAME, "en"); } | |||
| String SystemStats::getUserRegion() { return getLocaleValue (LOCALE_USER_DEFAULT, LOCALE_SISO3166CTRYNAME, "US"); } | |||
| String SystemStats::getDisplayLanguage() | |||
| { | |||
| DynamicLibrary dll ("kernel32.dll"); | |||
| JUCE_LOAD_WINAPI_FUNCTION (dll, GetUserDefaultUILanguage, getUserDefaultUILanguage, LANGID, (void)) | |||
| if (getUserDefaultUILanguage != nullptr) | |||
| return getLocaleValue (MAKELCID (getUserDefaultUILanguage(), SORT_DEFAULT), LOCALE_SISO639LANGNAME, "en"); | |||
| return "en"; | |||
| } | |||
| @@ -0,0 +1,639 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the juce_core module of the JUCE library. | |||
| Copyright (c) 2013 - Raw Material Software Ltd. | |||
| 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 THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||
| TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||
| NO EVENT SHALL THE AUTHOR 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. | |||
| ------------------------------------------------------------------------------ | |||
| NOTE! This permissive ISC license applies ONLY to files within the juce_core module! | |||
| All other JUCE modules are covered by a dual GPL/commercial license, so if you are | |||
| using any other modules, be sure to check that you also comply with their license. | |||
| For more details, visit www.juce.com | |||
| ============================================================================== | |||
| */ | |||
| HWND juce_messageWindowHandle = 0; // (this is used by other parts of the codebase) | |||
| //============================================================================== | |||
| #if ! JUCE_USE_INTRINSICS | |||
| // In newer compilers, the inline versions of these are used (in juce_Atomic.h), but in | |||
| // older ones we have to actually call the ops as win32 functions.. | |||
| long juce_InterlockedExchange (volatile long* a, long b) noexcept { return InterlockedExchange (a, b); } | |||
| long juce_InterlockedIncrement (volatile long* a) noexcept { return InterlockedIncrement (a); } | |||
| long juce_InterlockedDecrement (volatile long* a) noexcept { return InterlockedDecrement (a); } | |||
| long juce_InterlockedExchangeAdd (volatile long* a, long b) noexcept { return InterlockedExchangeAdd (a, b); } | |||
| long juce_InterlockedCompareExchange (volatile long* a, long b, long c) noexcept { return InterlockedCompareExchange (a, b, c); } | |||
| __int64 juce_InterlockedCompareExchange64 (volatile __int64* value, __int64 newValue, __int64 valueToCompare) noexcept | |||
| { | |||
| jassertfalse; // This operation isn't available in old MS compiler versions! | |||
| __int64 oldValue = *value; | |||
| if (oldValue == valueToCompare) | |||
| *value = newValue; | |||
| return oldValue; | |||
| } | |||
| #endif | |||
| //============================================================================== | |||
| CriticalSection::CriticalSection() noexcept | |||
| { | |||
| // (just to check the MS haven't changed this structure and broken things...) | |||
| #if JUCE_VC7_OR_EARLIER | |||
| static_jassert (sizeof (CRITICAL_SECTION) <= 24); | |||
| #else | |||
| static_jassert (sizeof (CRITICAL_SECTION) <= sizeof (internal)); | |||
| #endif | |||
| InitializeCriticalSection ((CRITICAL_SECTION*) internal); | |||
| } | |||
| CriticalSection::~CriticalSection() noexcept | |||
| { | |||
| DeleteCriticalSection ((CRITICAL_SECTION*) internal); | |||
| } | |||
| void CriticalSection::enter() const noexcept | |||
| { | |||
| EnterCriticalSection ((CRITICAL_SECTION*) internal); | |||
| } | |||
| bool CriticalSection::tryEnter() const noexcept | |||
| { | |||
| return TryEnterCriticalSection ((CRITICAL_SECTION*) internal) != FALSE; | |||
| } | |||
| void CriticalSection::exit() const noexcept | |||
| { | |||
| LeaveCriticalSection ((CRITICAL_SECTION*) internal); | |||
| } | |||
| //============================================================================== | |||
| WaitableEvent::WaitableEvent (const bool manualReset) noexcept | |||
| : internal (CreateEvent (0, manualReset ? TRUE : FALSE, FALSE, 0)) | |||
| { | |||
| } | |||
| WaitableEvent::~WaitableEvent() noexcept | |||
| { | |||
| CloseHandle (internal); | |||
| } | |||
| bool WaitableEvent::wait (const int timeOutMillisecs) const noexcept | |||
| { | |||
| return WaitForSingleObject (internal, (DWORD) timeOutMillisecs) == WAIT_OBJECT_0; | |||
| } | |||
| void WaitableEvent::signal() const noexcept | |||
| { | |||
| SetEvent (internal); | |||
| } | |||
| void WaitableEvent::reset() const noexcept | |||
| { | |||
| ResetEvent (internal); | |||
| } | |||
| //============================================================================== | |||
| void JUCE_API juce_threadEntryPoint (void*); | |||
| static unsigned int __stdcall threadEntryProc (void* userData) | |||
| { | |||
| if (juce_messageWindowHandle != 0) | |||
| AttachThreadInput (GetWindowThreadProcessId (juce_messageWindowHandle, 0), | |||
| GetCurrentThreadId(), TRUE); | |||
| juce_threadEntryPoint (userData); | |||
| _endthreadex (0); | |||
| return 0; | |||
| } | |||
| void Thread::launchThread() | |||
| { | |||
| unsigned int newThreadId; | |||
| threadHandle = (void*) _beginthreadex (0, 0, &threadEntryProc, this, 0, &newThreadId); | |||
| threadId = (ThreadID) newThreadId; | |||
| } | |||
| void Thread::closeThreadHandle() | |||
| { | |||
| CloseHandle ((HANDLE) threadHandle); | |||
| threadId = 0; | |||
| threadHandle = 0; | |||
| } | |||
| void Thread::killThread() | |||
| { | |||
| if (threadHandle != 0) | |||
| { | |||
| #if JUCE_DEBUG | |||
| OutputDebugStringA ("** Warning - Forced thread termination **\n"); | |||
| #endif | |||
| TerminateThread (threadHandle, 0); | |||
| } | |||
| } | |||
| void Thread::setCurrentThreadName (const String& name) | |||
| { | |||
| #if JUCE_DEBUG && JUCE_MSVC | |||
| struct | |||
| { | |||
| DWORD dwType; | |||
| LPCSTR szName; | |||
| DWORD dwThreadID; | |||
| DWORD dwFlags; | |||
| } info; | |||
| info.dwType = 0x1000; | |||
| info.szName = name.toUTF8(); | |||
| info.dwThreadID = GetCurrentThreadId(); | |||
| info.dwFlags = 0; | |||
| __try | |||
| { | |||
| RaiseException (0x406d1388 /*MS_VC_EXCEPTION*/, 0, sizeof (info) / sizeof (ULONG_PTR), (ULONG_PTR*) &info); | |||
| } | |||
| __except (EXCEPTION_CONTINUE_EXECUTION) | |||
| {} | |||
| #else | |||
| (void) name; | |||
| #endif | |||
| } | |||
| Thread::ThreadID Thread::getCurrentThreadId() | |||
| { | |||
| return (ThreadID) (pointer_sized_int) GetCurrentThreadId(); | |||
| } | |||
| bool Thread::setThreadPriority (void* handle, int priority) | |||
| { | |||
| int pri = THREAD_PRIORITY_TIME_CRITICAL; | |||
| if (priority < 1) pri = THREAD_PRIORITY_IDLE; | |||
| else if (priority < 2) pri = THREAD_PRIORITY_LOWEST; | |||
| else if (priority < 5) pri = THREAD_PRIORITY_BELOW_NORMAL; | |||
| else if (priority < 7) pri = THREAD_PRIORITY_NORMAL; | |||
| else if (priority < 9) pri = THREAD_PRIORITY_ABOVE_NORMAL; | |||
| else if (priority < 10) pri = THREAD_PRIORITY_HIGHEST; | |||
| if (handle == 0) | |||
| handle = GetCurrentThread(); | |||
| return SetThreadPriority (handle, pri) != FALSE; | |||
| } | |||
| void Thread::setCurrentThreadAffinityMask (const uint32 affinityMask) | |||
| { | |||
| SetThreadAffinityMask (GetCurrentThread(), affinityMask); | |||
| } | |||
| //============================================================================== | |||
| struct SleepEvent | |||
| { | |||
| SleepEvent() noexcept | |||
| : handle (CreateEvent (nullptr, FALSE, FALSE, | |||
| #if JUCE_DEBUG | |||
| _T("JUCE Sleep Event"))) | |||
| #else | |||
| nullptr)) | |||
| #endif | |||
| {} | |||
| ~SleepEvent() noexcept | |||
| { | |||
| CloseHandle (handle); | |||
| handle = 0; | |||
| } | |||
| HANDLE handle; | |||
| }; | |||
| static SleepEvent sleepEvent; | |||
| void JUCE_CALLTYPE Thread::sleep (const int millisecs) | |||
| { | |||
| if (millisecs >= 10 || sleepEvent.handle == 0) | |||
| { | |||
| Sleep ((DWORD) millisecs); | |||
| } | |||
| else | |||
| { | |||
| // unlike Sleep() this is guaranteed to return to the current thread after | |||
| // the time expires, so we'll use this for short waits, which are more likely | |||
| // to need to be accurate | |||
| WaitForSingleObject (sleepEvent.handle, (DWORD) millisecs); | |||
| } | |||
| } | |||
| void Thread::yield() | |||
| { | |||
| Sleep (0); | |||
| } | |||
| //============================================================================== | |||
| static int lastProcessPriority = -1; | |||
| // called by WindowDriver because Windows does weird things to process priority | |||
| // when you swap apps, and this forces an update when the app is brought to the front. | |||
| void juce_repeatLastProcessPriority() | |||
| { | |||
| if (lastProcessPriority >= 0) // (avoid changing this if it's not been explicitly set by the app..) | |||
| { | |||
| DWORD p; | |||
| switch (lastProcessPriority) | |||
| { | |||
| case Process::LowPriority: p = IDLE_PRIORITY_CLASS; break; | |||
| case Process::NormalPriority: p = NORMAL_PRIORITY_CLASS; break; | |||
| case Process::HighPriority: p = HIGH_PRIORITY_CLASS; break; | |||
| case Process::RealtimePriority: p = REALTIME_PRIORITY_CLASS; break; | |||
| default: jassertfalse; return; // bad priority value | |||
| } | |||
| SetPriorityClass (GetCurrentProcess(), p); | |||
| } | |||
| } | |||
| void Process::setPriority (ProcessPriority prior) | |||
| { | |||
| if (lastProcessPriority != (int) prior) | |||
| { | |||
| lastProcessPriority = (int) prior; | |||
| juce_repeatLastProcessPriority(); | |||
| } | |||
| } | |||
| JUCE_API bool JUCE_CALLTYPE juce_isRunningUnderDebugger() | |||
| { | |||
| return IsDebuggerPresent() != FALSE; | |||
| } | |||
| bool JUCE_CALLTYPE Process::isRunningUnderDebugger() | |||
| { | |||
| return juce_isRunningUnderDebugger(); | |||
| } | |||
| static void* currentModuleHandle = nullptr; | |||
| void* Process::getCurrentModuleInstanceHandle() noexcept | |||
| { | |||
| if (currentModuleHandle == nullptr) | |||
| currentModuleHandle = GetModuleHandleA (nullptr); | |||
| return currentModuleHandle; | |||
| } | |||
| void Process::setCurrentModuleInstanceHandle (void* const newHandle) noexcept | |||
| { | |||
| currentModuleHandle = newHandle; | |||
| } | |||
| void Process::raisePrivilege() | |||
| { | |||
| jassertfalse; // xxx not implemented | |||
| } | |||
| void Process::lowerPrivilege() | |||
| { | |||
| jassertfalse; // xxx not implemented | |||
| } | |||
| void Process::terminate() | |||
| { | |||
| #if JUCE_MSVC && JUCE_CHECK_MEMORY_LEAKS | |||
| _CrtDumpMemoryLeaks(); | |||
| #endif | |||
| // bullet in the head in case there's a problem shutting down.. | |||
| ExitProcess (1); | |||
| } | |||
| bool juce_isRunningInWine() | |||
| { | |||
| HMODULE ntdll = GetModuleHandleA ("ntdll"); | |||
| return ntdll != 0 && GetProcAddress (ntdll, "wine_get_version") != nullptr; | |||
| } | |||
| //============================================================================== | |||
| bool DynamicLibrary::open (const String& name) | |||
| { | |||
| close(); | |||
| JUCE_TRY | |||
| { | |||
| handle = LoadLibrary (name.toWideCharPointer()); | |||
| } | |||
| JUCE_CATCH_ALL | |||
| return handle != nullptr; | |||
| } | |||
| void DynamicLibrary::close() | |||
| { | |||
| JUCE_TRY | |||
| { | |||
| if (handle != nullptr) | |||
| { | |||
| FreeLibrary ((HMODULE) handle); | |||
| handle = nullptr; | |||
| } | |||
| } | |||
| JUCE_CATCH_ALL | |||
| } | |||
| void* DynamicLibrary::getFunction (const String& functionName) noexcept | |||
| { | |||
| return handle != nullptr ? (void*) GetProcAddress ((HMODULE) handle, functionName.toUTF8()) // (void* cast is required for mingw) | |||
| : nullptr; | |||
| } | |||
| //============================================================================== | |||
| class InterProcessLock::Pimpl | |||
| { | |||
| public: | |||
| Pimpl (String name, const int timeOutMillisecs) | |||
| : handle (0), refCount (1) | |||
| { | |||
| name = name.replaceCharacter ('\\', '/'); | |||
| handle = CreateMutexW (0, TRUE, ("Global\\" + name).toWideCharPointer()); | |||
| // Not 100% sure why a global mutex sometimes can't be allocated, but if it fails, fall back to | |||
| // a local one. (A local one also sometimes fails on other machines so neither type appears to be | |||
| // universally reliable) | |||
| if (handle == 0) | |||
| handle = CreateMutexW (0, TRUE, ("Local\\" + name).toWideCharPointer()); | |||
| if (handle != 0 && GetLastError() == ERROR_ALREADY_EXISTS) | |||
| { | |||
| if (timeOutMillisecs == 0) | |||
| { | |||
| close(); | |||
| return; | |||
| } | |||
| switch (WaitForSingleObject (handle, timeOutMillisecs < 0 ? INFINITE : timeOutMillisecs)) | |||
| { | |||
| case WAIT_OBJECT_0: | |||
| case WAIT_ABANDONED: | |||
| break; | |||
| case WAIT_TIMEOUT: | |||
| default: | |||
| close(); | |||
| break; | |||
| } | |||
| } | |||
| } | |||
| ~Pimpl() | |||
| { | |||
| close(); | |||
| } | |||
| void close() | |||
| { | |||
| if (handle != 0) | |||
| { | |||
| ReleaseMutex (handle); | |||
| CloseHandle (handle); | |||
| handle = 0; | |||
| } | |||
| } | |||
| HANDLE handle; | |||
| int refCount; | |||
| }; | |||
| InterProcessLock::InterProcessLock (const String& name_) | |||
| : name (name_) | |||
| { | |||
| } | |||
| InterProcessLock::~InterProcessLock() | |||
| { | |||
| } | |||
| bool InterProcessLock::enter (const int timeOutMillisecs) | |||
| { | |||
| const ScopedLock sl (lock); | |||
| if (pimpl == nullptr) | |||
| { | |||
| pimpl = new Pimpl (name, timeOutMillisecs); | |||
| if (pimpl->handle == 0) | |||
| pimpl = nullptr; | |||
| } | |||
| else | |||
| { | |||
| pimpl->refCount++; | |||
| } | |||
| return pimpl != nullptr; | |||
| } | |||
| void InterProcessLock::exit() | |||
| { | |||
| const ScopedLock sl (lock); | |||
| // Trying to release the lock too many times! | |||
| jassert (pimpl != nullptr); | |||
| if (pimpl != nullptr && --(pimpl->refCount) == 0) | |||
| pimpl = nullptr; | |||
| } | |||
| //============================================================================== | |||
| class ChildProcess::ActiveProcess | |||
| { | |||
| public: | |||
| ActiveProcess (const String& command) | |||
| : ok (false), readPipe (0), writePipe (0) | |||
| { | |||
| SECURITY_ATTRIBUTES securityAtts = { 0 }; | |||
| securityAtts.nLength = sizeof (securityAtts); | |||
| securityAtts.bInheritHandle = TRUE; | |||
| if (CreatePipe (&readPipe, &writePipe, &securityAtts, 0) | |||
| && SetHandleInformation (readPipe, HANDLE_FLAG_INHERIT, 0)) | |||
| { | |||
| STARTUPINFOW startupInfo = { 0 }; | |||
| startupInfo.cb = sizeof (startupInfo); | |||
| startupInfo.hStdError = writePipe; | |||
| startupInfo.hStdOutput = writePipe; | |||
| startupInfo.dwFlags = STARTF_USESTDHANDLES; | |||
| ok = CreateProcess (nullptr, const_cast <LPWSTR> (command.toWideCharPointer()), | |||
| nullptr, nullptr, TRUE, CREATE_NO_WINDOW | CREATE_UNICODE_ENVIRONMENT, | |||
| nullptr, nullptr, &startupInfo, &processInfo) != FALSE; | |||
| } | |||
| } | |||
| ~ActiveProcess() | |||
| { | |||
| if (ok) | |||
| { | |||
| CloseHandle (processInfo.hThread); | |||
| CloseHandle (processInfo.hProcess); | |||
| } | |||
| if (readPipe != 0) | |||
| CloseHandle (readPipe); | |||
| if (writePipe != 0) | |||
| CloseHandle (writePipe); | |||
| } | |||
| bool isRunning() const | |||
| { | |||
| return WaitForSingleObject (processInfo.hProcess, 0) != WAIT_OBJECT_0; | |||
| } | |||
| int read (void* dest, int numNeeded) const | |||
| { | |||
| int total = 0; | |||
| while (ok && numNeeded > 0) | |||
| { | |||
| DWORD available = 0; | |||
| if (! PeekNamedPipe ((HANDLE) readPipe, nullptr, 0, nullptr, &available, nullptr)) | |||
| break; | |||
| const int numToDo = jmin ((int) available, numNeeded); | |||
| if (available == 0) | |||
| { | |||
| if (! isRunning()) | |||
| break; | |||
| Thread::yield(); | |||
| } | |||
| else | |||
| { | |||
| DWORD numRead = 0; | |||
| if (! ReadFile ((HANDLE) readPipe, dest, numToDo, &numRead, nullptr)) | |||
| break; | |||
| total += numRead; | |||
| dest = addBytesToPointer (dest, numRead); | |||
| numNeeded -= numRead; | |||
| } | |||
| } | |||
| return total; | |||
| } | |||
| bool killProcess() const | |||
| { | |||
| return TerminateProcess (processInfo.hProcess, 0) != FALSE; | |||
| } | |||
| bool ok; | |||
| private: | |||
| HANDLE readPipe, writePipe; | |||
| PROCESS_INFORMATION processInfo; | |||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ActiveProcess) | |||
| }; | |||
| bool ChildProcess::start (const String& command) | |||
| { | |||
| activeProcess = new ActiveProcess (command); | |||
| if (! activeProcess->ok) | |||
| activeProcess = nullptr; | |||
| return activeProcess != nullptr; | |||
| } | |||
| bool ChildProcess::start (const StringArray& args) | |||
| { | |||
| return start (args.joinIntoString (" ")); | |||
| } | |||
| bool ChildProcess::isRunning() const | |||
| { | |||
| return activeProcess != nullptr && activeProcess->isRunning(); | |||
| } | |||
| int ChildProcess::readProcessOutput (void* dest, int numBytes) | |||
| { | |||
| return activeProcess != nullptr ? activeProcess->read (dest, numBytes) : 0; | |||
| } | |||
| bool ChildProcess::kill() | |||
| { | |||
| return activeProcess == nullptr || activeProcess->killProcess(); | |||
| } | |||
| //============================================================================== | |||
| struct HighResolutionTimer::Pimpl | |||
| { | |||
| Pimpl (HighResolutionTimer& t) noexcept : owner (t), periodMs (0) | |||
| { | |||
| } | |||
| ~Pimpl() | |||
| { | |||
| jassert (periodMs == 0); | |||
| } | |||
| void start (int newPeriod) | |||
| { | |||
| if (newPeriod != periodMs) | |||
| { | |||
| stop(); | |||
| periodMs = newPeriod; | |||
| TIMECAPS tc; | |||
| if (timeGetDevCaps (&tc, sizeof (tc)) == TIMERR_NOERROR) | |||
| { | |||
| const int actualPeriod = jlimit ((int) tc.wPeriodMin, (int) tc.wPeriodMax, newPeriod); | |||
| timerID = timeSetEvent (actualPeriod, tc.wPeriodMin, callbackFunction, (DWORD_PTR) this, | |||
| TIME_PERIODIC | TIME_CALLBACK_FUNCTION | 0x100 /*TIME_KILL_SYNCHRONOUS*/); | |||
| } | |||
| } | |||
| } | |||
| void stop() | |||
| { | |||
| periodMs = 0; | |||
| timeKillEvent (timerID); | |||
| } | |||
| HighResolutionTimer& owner; | |||
| int periodMs; | |||
| private: | |||
| unsigned int timerID; | |||
| static void __stdcall callbackFunction (UINT, UINT, DWORD_PTR userInfo, DWORD_PTR, DWORD_PTR) | |||
| { | |||
| if (Pimpl* const timer = reinterpret_cast<Pimpl*> (userInfo)) | |||
| if (timer->periodMs != 0) | |||
| timer->owner.hiResTimerCallback(); | |||
| } | |||
| JUCE_DECLARE_NON_COPYABLE (Pimpl) | |||
| }; | |||
| @@ -0,0 +1,149 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the juce_core module of the JUCE library. | |||
| Copyright (c) 2013 - Raw Material Software Ltd. | |||
| 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 THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||
| TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||
| NO EVENT SHALL THE AUTHOR 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. | |||
| ------------------------------------------------------------------------------ | |||
| NOTE! This permissive ISC license applies ONLY to files within the juce_core module! | |||
| All other JUCE modules are covered by a dual GPL/commercial license, so if you are | |||
| using any other modules, be sure to check that you also comply with their license. | |||
| For more details, visit www.juce.com | |||
| ============================================================================== | |||
| */ | |||
| IPAddress::IPAddress() noexcept | |||
| { | |||
| address[0] = 0; address[1] = 0; | |||
| address[2] = 0; address[3] = 0; | |||
| } | |||
| IPAddress::IPAddress (const uint8 bytes[4]) noexcept | |||
| { | |||
| address[0] = bytes[0]; address[1] = bytes[1]; | |||
| address[2] = bytes[2]; address[3] = bytes[3]; | |||
| } | |||
| IPAddress::IPAddress (uint8 a0, uint8 a1, uint8 a2, uint8 a3) noexcept | |||
| { | |||
| address[0] = a0; address[1] = a1; | |||
| address[2] = a2; address[3] = a3; | |||
| } | |||
| IPAddress::IPAddress (uint32 n) noexcept | |||
| { | |||
| address[0] = (n >> 24); | |||
| address[1] = (n >> 16) & 255; | |||
| address[2] = (n >> 8) & 255; | |||
| address[3] = (n & 255); | |||
| } | |||
| IPAddress::IPAddress (const String& adr) | |||
| { | |||
| StringArray tokens; | |||
| tokens.addTokens (adr, ".", String::empty); | |||
| for (int i = 0; i < 4; ++i) | |||
| address[i] = (uint8) tokens[i].getIntValue(); | |||
| } | |||
| String IPAddress::toString() const | |||
| { | |||
| String s ((int) address[0]); | |||
| for (int i = 1; i < 4; ++i) | |||
| s << '.' << (int) address[i]; | |||
| return s; | |||
| } | |||
| IPAddress IPAddress::any() noexcept { return IPAddress(); } | |||
| IPAddress IPAddress::broadcast() noexcept { return IPAddress (255, 255, 255, 255); } | |||
| IPAddress IPAddress::local() noexcept { return IPAddress (127, 0, 0, 1); } | |||
| bool IPAddress::operator== (const IPAddress& other) const noexcept | |||
| { | |||
| return address[0] == other.address[0] | |||
| && address[1] == other.address[1] | |||
| && address[2] == other.address[2] | |||
| && address[3] == other.address[3]; | |||
| } | |||
| bool IPAddress::operator!= (const IPAddress& other) const noexcept | |||
| { | |||
| return ! operator== (other); | |||
| } | |||
| #if ! JUCE_WINDOWS | |||
| 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; | |||
| size_t bufferSize = 1024; | |||
| do | |||
| { | |||
| bufferSize *= 2; | |||
| buffer.calloc (bufferSize); | |||
| cfg.ifc_len = bufferSize; | |||
| cfg.ifc_buf = buffer; | |||
| if (ioctl (sock, SIOCGIFCONF, &cfg) < 0 && errno != EINVAL) | |||
| return; | |||
| } while (bufferSize < cfg.ifc_len + 2 * (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 (int i = 0; i < cfg.ifc_len / 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) | |||
| { | |||
| 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); | |||
| } | |||
| } | |||
| #endif | |||
| @@ -0,0 +1,82 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the juce_core module of the JUCE library. | |||
| Copyright (c) 2013 - Raw Material Software Ltd. | |||
| 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 THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||
| TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||
| NO EVENT SHALL THE AUTHOR 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. | |||
| ------------------------------------------------------------------------------ | |||
| NOTE! This permissive ISC license applies ONLY to files within the juce_core module! | |||
| All other JUCE modules are covered by a dual GPL/commercial license, so if you are | |||
| using any other modules, be sure to check that you also comply with their license. | |||
| For more details, visit www.juce.com | |||
| ============================================================================== | |||
| */ | |||
| #ifndef JUCE_IPADDRESS_H_INCLUDED | |||
| #define JUCE_IPADDRESS_H_INCLUDED | |||
| //============================================================================== | |||
| /** | |||
| An IPV4 address. | |||
| */ | |||
| class JUCE_API IPAddress | |||
| { | |||
| public: | |||
| //============================================================================== | |||
| /** Populates a list of all the IP addresses that this machine is using. */ | |||
| static void findAllAddresses (Array<IPAddress>& results); | |||
| //============================================================================== | |||
| /** Creates a null address (0.0.0.0). */ | |||
| IPAddress() noexcept; | |||
| /** Creates an address from 4 bytes. */ | |||
| explicit IPAddress (const uint8 bytes[4]) noexcept; | |||
| /** Creates an address from 4 bytes. */ | |||
| IPAddress (uint8 address1, uint8 address2, uint8 address3, uint8 address4) noexcept; | |||
| /** Creates an address from a packed 32-bit integer, where the MSB is | |||
| the first number in the address, and the LSB is the last. | |||
| */ | |||
| explicit IPAddress (uint32 asNativeEndian32Bit) noexcept; | |||
| /** Parses a string IP address of the form "a.b.c.d". */ | |||
| explicit IPAddress (const String& address); | |||
| /** Returns a dot-separated string in the form "1.2.3.4" */ | |||
| String toString() const; | |||
| /** Returns an address meaning "any" (0.0.0.0) */ | |||
| static IPAddress any() noexcept; | |||
| /** Returns an address meaning "broadcast" (255.255.255.255) */ | |||
| static IPAddress broadcast() noexcept; | |||
| /** Returns an address meaning "localhost" (127.0.0.1) */ | |||
| static IPAddress local() noexcept; | |||
| bool operator== (const IPAddress& other) const noexcept; | |||
| bool operator!= (const IPAddress& other) const noexcept; | |||
| /** The elements of the IP address. */ | |||
| uint8 address[4]; | |||
| }; | |||
| #endif // JUCE_IPADDRESS_H_INCLUDED | |||
| @@ -0,0 +1,78 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the juce_core module of the JUCE library. | |||
| Copyright (c) 2013 - Raw Material Software Ltd. | |||
| 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 THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||
| TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||
| NO EVENT SHALL THE AUTHOR 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. | |||
| ------------------------------------------------------------------------------ | |||
| NOTE! This permissive ISC license applies ONLY to files within the juce_core module! | |||
| All other JUCE modules are covered by a dual GPL/commercial license, so if you are | |||
| using any other modules, be sure to check that you also comply with their license. | |||
| For more details, visit www.juce.com | |||
| ============================================================================== | |||
| */ | |||
| MACAddress::MACAddress() | |||
| { | |||
| zeromem (address, sizeof (address)); | |||
| } | |||
| MACAddress::MACAddress (const MACAddress& other) | |||
| { | |||
| memcpy (address, other.address, sizeof (address)); | |||
| } | |||
| MACAddress& MACAddress::operator= (const MACAddress& other) | |||
| { | |||
| memcpy (address, other.address, sizeof (address)); | |||
| return *this; | |||
| } | |||
| MACAddress::MACAddress (const uint8 bytes[6]) | |||
| { | |||
| memcpy (address, bytes, sizeof (address)); | |||
| } | |||
| String MACAddress::toString() const | |||
| { | |||
| String s; | |||
| for (size_t i = 0; i < sizeof (address); ++i) | |||
| { | |||
| s << String::toHexString ((int) address[i]).paddedLeft ('0', 2); | |||
| if (i < sizeof (address) - 1) | |||
| s << '-'; | |||
| } | |||
| return s; | |||
| } | |||
| int64 MACAddress::toInt64() const noexcept | |||
| { | |||
| int64 n = 0; | |||
| for (int i = (int) sizeof (address); --i >= 0;) | |||
| n = (n << 8) | address[i]; | |||
| return n; | |||
| } | |||
| bool MACAddress::isNull() const noexcept { return toInt64() == 0; } | |||
| bool MACAddress::operator== (const MACAddress& other) const noexcept { return memcmp (address, other.address, sizeof (address)) == 0; } | |||
| bool MACAddress::operator!= (const MACAddress& other) const noexcept { return ! operator== (other); } | |||