diff --git a/.gitignore b/.gitignore
index 86b5dea1..b7d07bdf 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,25 @@
+._*
+*.mode1v3
+*.pbxuser
+*.perspectivev3
+*.user
+*.ncb
+*.suo
+*.obj
+*.ilk
+*.pch
+*.pdb
+*.dep
+*.idb
+*.manifest
+*.manifest.res
+*.o
+*.d
+*.sdf
+xcuserdata
+contents.xcworkspacedata
+.DS_Store
+.svn
+profile
Builds/VisualStudio2012/Debug
Builds/VisualStudio2012/Release
-Builds/VisualStudio2012/*.user
diff --git a/Builds/VisualStudio2012/BeastConfig.h b/Builds/VisualStudio2012/BeastConfig.h
new file mode 100644
index 00000000..29fd8364
--- /dev/null
+++ b/Builds/VisualStudio2012/BeastConfig.h
@@ -0,0 +1,23 @@
+
+#ifndef BEAST_BEASTCONFIG_HEADER
+#define BEAST_BEASTCONFIG_HEADER
+
+// beast_core flags:
+
+#ifndef BEAST_FORCE_DEBUG
+ //#define BEAST_FORCE_DEBUG
+#endif
+
+#ifndef BEAST_LOG_ASSERTIONS
+ //#define BEAST_LOG_ASSERTIONS 1
+#endif
+
+#ifndef BEAST_CHECK_MEMORY_LEAKS
+ //#define BEAST_CHECK_MEMORY_LEAKS
+#endif
+
+#ifndef BEAST_DONT_AUTOLINK_TO_WIN32_LIBRARIES
+ //#define BEAST_DONT_AUTOLINK_TO_WIN32_LIBRARIES
+#endif
+
+#endif
diff --git a/Builds/VisualStudio2012/beast.vcxproj b/Builds/VisualStudio2012/beast.vcxproj
index 9fe1e1d3..5ecfa007 100644
--- a/Builds/VisualStudio2012/beast.vcxproj
+++ b/Builds/VisualStudio2012/beast.vcxproj
@@ -13,14 +13,482 @@
+
+ true
+ true
+
+
+ true
+ true
+
+
+ true
+ true
+
+
+ true
+ true
+
+
+ true
+ true
+
+
+ true
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ true
+ true
+
+
+ true
+ true
+
+
+ true
+ true
+
+
+ true
+ true
+
+
+ true
+ true
+
+
+ true
+ true
+
+
+ true
+ true
+
+
+ true
+ true
+
+
+ true
+ true
+
+
+ true
+ true
+
+
+ true
+ true
+
+
+ true
+ true
+
+
+ true
+ true
+
+
+ true
+ true
+
+
+ true
+ true
+
+
+ true
+ true
+
+
+ true
+ true
+
+
+ true
+ true
+
+
+ true
+ true
+
+
+ true
+ true
+
+
+ true
+ true
+
+
+ true
+ true
+
+
+ true
+ true
+
+
+ true
+ true
+
+
+ true
+ true
+
+
+ true
+ true
+
+
+ true
+ true
+
+
+ true
+ true
+
+
+ true
+ true
+
+
+ true
+ true
+
+
+ true
+ true
+
+
+ true
+ true
+
+
+ true
+ true
+
+
+ true
+ true
+
+
+ true
+ true
+
+
+ true
+ true
+
+
+ true
+ true
+
+
+ true
+ true
+
+
+ true
+ true
+
+
+ true
+ true
+
+
+ true
+ true
+
+
+ true
+ true
+
+
+ true
+ true
+
+
+ true
+ true
+
+
+ true
+ true
+
+
+ true
+ true
+
+
+ true
+ true
+
+
+ true
+ true
+
+
+ true
+ true
+
+
+ true
+ true
+
+
+ true
+ true
+
+
+ true
+ true
+
+
+ true
+ true
+
+
+ true
+ true
+
+
+ true
+ true
+
+
+ true
+ true
+
+
+ true
+ true
+
+
+ true
+ true
+
+
+ true
+ true
+
+
+ true
+ true
+
+
+ true
+ true
+
+
+ true
+ true
+
+
+ true
+ true
+
+
+ true
+ true
+
+
+ true
+ true
+
+
+ true
+ true
+
+
+ true
+ true
+
+
+ true
+ true
+
+
+ true
+ true
+
+
+ true
+ true
+
+
+ true
+ true
+
+
+ true
+ true
+
+
+ true
+ true
+
+
+ true
+ true
+
+
+ true
+ true
+
+
+ true
+ true
+
+
+ true
+ true
+
+
+ true
+ true
+
+
+ true
+ true
+
+
+ true
+ true
+
+
+ true
+ true
+
+
+ true
+ true
+ {73C5A0F0-7629-4DE7-9194-BE7AC6C19535}
@@ -59,6 +527,7 @@
Level3DisabledWIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)
+ $(ProjectDir)Windows
@@ -74,6 +543,7 @@
truetrueWIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)
+ $(ProjectDir)Windows
diff --git a/Builds/VisualStudio2012/beast.vcxproj.filters b/Builds/VisualStudio2012/beast.vcxproj.filters
index d690caaf..1867cf79 100644
--- a/Builds/VisualStudio2012/beast.vcxproj.filters
+++ b/Builds/VisualStudio2012/beast.vcxproj.filters
@@ -6,26 +6,690 @@
beast_core
+
+ beast_core\native
+
+
+ beast_core\native
+
+
+ beast_core\native
+
+
+ beast_core\native
+
+
+ beast_core\native
+
+
+ beast_core\zip\zlib
+
+
+ beast_core\native\java
+ {6dafd8d5-2901-4b41-85b7-52f6e86baacc}
+
+ {7cc041c8-fdf4-4e98-a56a-df516c2a9aa2}
+
+
+ {3b7d8d7e-eabc-423c-a631-2ff37bf9b9eb}
+
+
+ {abe24d69-c1d1-49e2-98a1-1e7bf760b97a}
+
+
+ {0fa6b76b-305f-473c-9b94-c1028a3af3fc}
+
+
+ {e2339099-bb8e-4437-ae8b-d4f64ef1e3f3}
+
- {883f776d-bec2-4e7a-b4e8-726ed3039071}
+ {a89aa17e-4e01-4c2d-ba7e-196e9e0b67bb}
+
+
+ {64a314e1-0361-428a-a294-0615d8140f58}
+
+
+ {1d018a70-71b3-4d70-9280-02efd4f348de}
+
+
+ {ad675f57-9303-4712-9ff2-c7f59f959e7b}
+
+
+ {016d839c-4860-4bc3-8f6c-e965f50c2bfa}
+
+
+ {7fb77af6-ce16-4f53-823e-906963b42985}
+
+
+ {4603580c-5668-4e70-bed8-77f00f03f0ff}
+
+
+ {3409dc7a-92b5-43aa-b678-fde9f82ced55}
+
+
+ {d3ea4af5-ae48-4144-a2ef-a212342d72ee}
+
+
+ {0a8a6870-87a4-4f19-bbd3-b277f94bb83e}
+
+
+ {a1a38659-7779-41d6-8a3c-068433c4deaf}
+
+
+ {df5f5a69-5919-4a24-bbce-b3f87e4903cf}
+
+
+ {b0d206d9-c002-4be1-b503-4ad16aca838a}
+
+
+ {31038502-9139-4c19-bd67-8f90f08a70ca}
-
- beast_core\memory
- beast_core
+
+ beast_core\containers
+
+
+ beast_core\containers
+
+
+ beast_core\containers
+
+
+ beast_core\containers
+
+
+ beast_core\containers
+
+
+ beast_core\containers
+
+
+ beast_core\containers
+
+
+ beast_core\containers
+
+
+ beast_core\containers
+
+
+ beast_core\containers
+
+
+ beast_core\containers
+
+
+ beast_core\containers
+
+
+ beast_core\containers
+
+
+ beast_core\containers
+
+
+ beast_core\containers
+
+
+ beast_core\files
+
+
+ beast_core\files
+
+
+ beast_core\files
+
+
+ beast_core\files
+
+
+ beast_core\files
+
+
+ beast_core\files
+
+
+ beast_core\files
+
+
+ beast_core\json
+
+
+ beast_core\logging
+
+
+ beast_core\logging
+
+
+ beast_core\maths
+
+
+ beast_core\maths
+
+
+ beast_core\maths
+
+
+ beast_core\maths
+
+
+ beast_core\maths
+
+
+ beast_core\memory
+
+
+ beast_core\memory
+
+
+ beast_core\memory
+
+
+ beast_core\memory
+
+
+ beast_core\memory
+
+
+ beast_core\memory
+
+
+ beast_core\memory
+
+
+ beast_core\memory
+
+
+ beast_core\memory
+
+
+ beast_core\memory
+
+
+ beast_core\memory
+
+
+ beast_core\misc
+
+
+ beast_core\misc
+
+
+ beast_core\misc
+
+
+ beast_core\native
+
+
+ beast_core\native
+
+
+ beast_core\native
+
+
+ beast_core\native
+
+
+ beast_core\native
+
+
+ beast_core\network
+
+
+ beast_core\network
+
+
+ beast_core\network
+
+
+ beast_core\network
+
+
+ beast_core\network
+
+
+ beast_core\streams
+
+
+ beast_core\streams
+
+
+ beast_core\streams
+
+
+ beast_core\streams
+
+
+ beast_core\streams
+
+
+ beast_core\streams
+
+
+ beast_core\streams
+
+
+ beast_core\streams
+
+
+ beast_core\system
+
+
+ beast_core\system
+
+
+ beast_core\system
+
+
+ beast_core\system
+
+
+ beast_core\text
+
+
+ beast_core\text
+
+
+ beast_core\text
+
+
+ beast_core\text
+
+
+ beast_core\text
+
+
+ beast_core\text
+
+
+ beast_core\text
+
+
+ beast_core\text
+
+
+ beast_core\text
+
+
+ beast_core\text
+
+
+ beast_core\text
+
+
+ beast_core\text
+
+
+ beast_core\text
+
+
+ beast_core\threads
+
+
+ beast_core\threads
+
+
+ beast_core\threads
+
+
+ beast_core\threads
+
+
+ beast_core\threads
+
+
+ beast_core\threads
+
+
+ beast_core\threads
+
+
+ beast_core\threads
+
+
+ beast_core\threads
+
+
+ beast_core\threads
+
+
+ beast_core\threads
+
+
+ beast_core\threads
+
+
+ beast_core\threads
+
+
+ beast_core\threads
+
+
+ beast_core\threads
+
+
+ beast_core\threads
+
+
+ beast_core\time
+
+
+ beast_core\time
+
+
+ beast_core\time
+
+
+ beast_core\unit_tests
+
+
+ beast_core\xml
+
+
+ beast_core\xml
+
+
+ beast_core\zip
+
+
+ beast_core\zip
+
+
+ beast_core\zip
+
+
+ beast_core\zip\zlib
+
+
+ beast_core\zip\zlib
+
+
+ beast_core\zip\zlib
+
+
+ beast_core\zip\zlib
+
+
+ beast_core\zip\zlib
+
+
+ beast_core\zip\zlib
+
+
+ beast_core\zip\zlib
+
+
+ beast_core\zip\zlib
+
+
+ beast_core\zip\zlib
+
+
+ beast_core\zip\zlib
+
+
+ beast_core\zip\zlib
+
+ beast_core
+
+ beast_core\containers
+
+
+ beast_core\containers
+
+
+ beast_core\containers
+
+
+ beast_core\containers
+
+
+ beast_core\containers
+
+
+ beast_core\files
+
+
+ beast_core\files
+
+
+ beast_core\files
+
+
+ beast_core\files
+
+
+ beast_core\files
+
+
+ beast_core\files
+
+
+ beast_core\json
+
+
+ beast_core\logging
+
+
+ beast_core\logging
+
+
+ beast_core\maths
+
+
+ beast_core\maths
+
+
+ beast_core\maths
+
+
+ beast_core\memory
+
+
+ beast_core\misc
+
+
+ beast_core\misc
+
+
+ beast_core\native
+
+
+ beast_core\native
+
+
+ beast_core\native
+
+
+ beast_core\native
+
+
+ beast_core\native
+
+
+ beast_core\native
+
+
+ beast_core\native
+
+
+ beast_core\native
+
+
+ beast_core\native
+
+
+ beast_core\native
+
+
+ beast_core\native
+
+
+ beast_core\native
+
+
+ beast_core\native
+
+
+ beast_core\native
+
+
+ beast_core\native
+
+
+ beast_core\network
+
+
+ beast_core\network
+
+
+ beast_core\network
+
+
+ beast_core\network
+
+
+ beast_core\network
+
+
+ beast_core\streams
+
+
+ beast_core\streams
+
+
+ beast_core\streams
+
+
+ beast_core\streams
+
+
+ beast_core\streams
+
+
+ beast_core\streams
+
+
+ beast_core\streams
+
+
+ beast_core\system
+
+
+ beast_core\text
+
+
+ beast_core\text
+
+
+ beast_core\text
+
+
+ beast_core\text
+
+
+ beast_core\text
+
+
+ beast_core\text
+
+
+ beast_core\text
+
+
+ beast_core\text
+
+
+ beast_core\threads
+
+
+ beast_core\threads
+
+
+ beast_core\threads
+
+
+ beast_core\threads
+
+
+ beast_core\threads
+
+
+ beast_core\threads
+
+
+ beast_core\time
+
+
+ beast_core\time
+
+
+ beast_core\time
+
+
+ beast_core\unit_tests
+
+
+ beast_core\xml
+
+
+ beast_core\xml
+
+
+ beast_core\zip
+
+
+ beast_core\zip
+
+
+ beast_core\zip
+
+
+ beast_core\zip\zlib
+
+
+ beast_core\zip\zlib
+
+
+ beast_core\zip\zlib
+
+
+ beast_core\zip\zlib
+
+
+ beast_core\zip\zlib
+
+
+ beast_core\zip\zlib
+
+
+ beast_core\zip\zlib
+
+
+ beast_core\zip\zlib
+
+
+ beast_core\zip\zlib
+
+
+ beast_core\zip\zlib
+
+
+ beast_core\zip\zlib
+
\ No newline at end of file
diff --git a/ReadMe.md b/ReadMe.md
index 9c8689bd..5acd2f38 100644
--- a/ReadMe.md
+++ b/ReadMe.md
@@ -6,3 +6,9 @@ No external dependencies, no complicated build steps.
Things people need for building peer to peer, concurrent, cryptographic systems.
The hope is that this will replace the use of boost and other cumbersome jalopies.
+
+## JUCE
+
+Beast is based on the juce_core module which is provided under the ISC
+license. More information about JUCE is available at
+http://www.juce.com
diff --git a/modules/beast_core/beast_core.cpp b/modules/beast_core/beast_core.cpp
index 939a512e..d6eca853 100644
--- a/modules/beast_core/beast_core.cpp
+++ b/modules/beast_core/beast_core.cpp
@@ -1,2 +1,213 @@
+//------------------------------------------------------------------------------
+/*
+ This file is part of Beast: https://github.com/vinniefalco/Beast
+ Copyright 2013, Vinnie Falco
+ Portions of this file are from JUCE.
+ Copyright (c) 2013 - Raw Material Software Ltd.
+ Please visit http://www.juce.com
+
+ Permission to use, copy, modify, and/or distribute this software 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.
+*/
+//==============================================================================
+
+#if defined (BEAST_CORE_BEASTHEADER) && ! BEAST_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 BEAST cpp file"
+#endif
+
+// Your project must contain a BeastConfig.h file with your project-specific settings in it,
+// and your header search path must make it accessible to the module's files.
+#include "BeastConfig.h"
+
+//==============================================================================
+#include "native/beast_BasicNativeHeaders.h"
#include "beast_core.h"
+
+#include
+#include
+#include
+
+#if ! BEAST_ANDROID
+ #include
+#endif
+
+#if BEAST_WINDOWS
+ #include
+ #include
+ #include
+
+ #if ! BEAST_MINGW
+ #include
+
+ #if ! BEAST_DONT_AUTOLINK_TO_WIN32_LIBRARIES
+ #pragma comment (lib, "DbgHelp.lib")
+ #endif
+ #endif
+
+ #if BEAST_MINGW
+ #include
+ #endif
+
+#else
+ #if BEAST_LINUX || BEAST_ANDROID
+ #include
+ #include
+ #include
+ #include
+ #include
+ #endif
+
+ #if BEAST_LINUX
+ #include
+ #endif
+
+ #include
+ #include
+ #include
+ #include
+ #include
+ #include
+ #include
+ #include
+
+ #if ! BEAST_ANDROID
+ #include
+ #endif
+#endif
+
+#if BEAST_MAC || BEAST_IOS
+ #include
+ #include
+#endif
+
+#if BEAST_ANDROID
+ #include
+#endif
+
+
+//==============================================================================
+namespace beast
+{
+
+#include "containers/beast_AbstractFifo.cpp"
+#include "containers/beast_DynamicObject.cpp"
+#include "containers/beast_NamedValueSet.cpp"
+#include "containers/beast_PropertySet.cpp"
+#include "containers/beast_Variant.cpp"
+#include "files/beast_DirectoryIterator.cpp"
+#include "files/beast_File.cpp"
+#include "files/beast_FileInputStream.cpp"
+#include "files/beast_FileOutputStream.cpp"
+#include "files/beast_FileSearchPath.cpp"
+#include "files/beast_TemporaryFile.cpp"
+#include "json/beast_JSON.cpp"
+#include "logging/beast_FileLogger.cpp"
+#include "logging/beast_Logger.cpp"
+#include "maths/beast_BigInteger.cpp"
+#include "maths/beast_Expression.cpp"
+#include "maths/beast_Random.cpp"
+#include "memory/beast_MemoryBlock.cpp"
+#include "misc/beast_Result.cpp"
+#include "misc/beast_Uuid.cpp"
+#include "network/beast_MACAddress.cpp"
+#include "network/beast_NamedPipe.cpp"
+#include "network/beast_Socket.cpp"
+#include "network/beast_URL.cpp"
+#include "network/beast_IPAddress.cpp"
+#include "streams/beast_BufferedInputStream.cpp"
+#include "streams/beast_FileInputSource.cpp"
+#include "streams/beast_InputStream.cpp"
+#include "streams/beast_MemoryInputStream.cpp"
+#include "streams/beast_MemoryOutputStream.cpp"
+#include "streams/beast_OutputStream.cpp"
+#include "streams/beast_SubregionStream.cpp"
+#include "system/beast_SystemStats.cpp"
+#include "text/beast_CharacterFunctions.cpp"
+#include "text/beast_Identifier.cpp"
+#include "text/beast_LocalisedStrings.cpp"
+#include "text/beast_String.cpp"
+#include "text/beast_StringArray.cpp"
+#include "text/beast_StringPairArray.cpp"
+#include "text/beast_StringPool.cpp"
+#include "text/beast_TextDiff.cpp"
+#include "threads/beast_ChildProcess.cpp"
+#include "threads/beast_ReadWriteLock.cpp"
+#include "threads/beast_Thread.cpp"
+#include "threads/beast_ThreadPool.cpp"
+#include "threads/beast_TimeSliceThread.cpp"
+#include "time/beast_PerformanceCounter.cpp"
+#include "time/beast_RelativeTime.cpp"
+#include "time/beast_Time.cpp"
+#include "unit_tests/beast_UnitTest.cpp"
+#include "xml/beast_XmlDocument.cpp"
+#include "xml/beast_XmlElement.cpp"
+#include "zip/beast_GZIPDecompressorInputStream.cpp"
+#include "zip/beast_GZIPCompressorOutputStream.cpp"
+#include "zip/beast_ZipFile.cpp"
+
+//==============================================================================
+#if BEAST_MAC || BEAST_IOS
+#include "native/beast_osx_ObjCHelpers.h"
+#endif
+
+#if BEAST_ANDROID
+#include "native/beast_android_JNIHelpers.h"
+#endif
+
+#if ! BEAST_WINDOWS
+#include "native/beast_posix_SharedCode.h"
+#include "native/beast_posix_NamedPipe.cpp"
+#endif
+
+//==============================================================================
+#if BEAST_MAC || BEAST_IOS
+#include "native/beast_mac_Files.mm"
+#include "native/beast_mac_Network.mm"
+#include "native/beast_mac_Strings.mm"
+#include "native/beast_mac_SystemStats.mm"
+#include "native/beast_mac_Threads.mm"
+
+//==============================================================================
+#elif BEAST_WINDOWS
+#include "native/beast_win32_ComSmartPtr.h"
+#include "native/beast_win32_Files.cpp"
+#include "native/beast_win32_Network.cpp"
+#include "native/beast_win32_Registry.cpp"
+#include "native/beast_win32_SystemStats.cpp"
+#include "native/beast_win32_Threads.cpp"
+
+//==============================================================================
+#elif BEAST_LINUX
+#include "native/beast_linux_Files.cpp"
+#include "native/beast_linux_Network.cpp"
+#include "native/beast_linux_SystemStats.cpp"
+#include "native/beast_linux_Threads.cpp"
+
+//==============================================================================
+#elif BEAST_ANDROID
+#include "native/beast_android_Files.cpp"
+#include "native/beast_android_Misc.cpp"
+#include "native/beast_android_Network.cpp"
+#include "native/beast_android_SystemStats.cpp"
+#include "native/beast_android_Threads.cpp"
+
+#endif
+
+#include "threads/beast_HighResolutionTimer.cpp"
+
+}
diff --git a/modules/beast_core/beast_core.h b/modules/beast_core/beast_core.h
index 65284cd1..8c829e95 100644
--- a/modules/beast_core/beast_core.h
+++ b/modules/beast_core/beast_core.h
@@ -1,10 +1,440 @@
+//------------------------------------------------------------------------------
+/*
+ This file is part of Beast: https://github.com/vinniefalco/Beast
+ Copyright 2013, Vinnie Falco
-#include
-#include
+ Portions of this file are from JUCE.
+ Copyright (c) 2013 - Raw Material Software Ltd.
+ Please visit http://www.juce.com
+
+ Permission to use, copy, modify, and/or distribute this software 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.
+*/
+//==============================================================================
+
+#ifndef BEAST_CORE_BEASTHEADER
+#define BEAST_CORE_BEASTHEADER
+
+#ifndef BEAST_BEASTCONFIG_HEADER
+ /* If you fail to make sure that all your compile units are building Beast 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 BeastConfig.h file before any beast headers.
+ */
+ #ifdef _MSC_VER
+#pragma message ("Have you included your BeastConfig.h file before including the Beast headers?")
+ #else
+ #warning "Have you included your BeastConfig.h file before including the Beast headers?"
+ #endif
+#endif
+
+//==============================================================================
+#include "system/beast_TargetPlatform.h"
+
+//=============================================================================
+/** Config: BEAST_FORCE_DEBUG
+
+ Normally, BEAST_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 BEAST_FORCE_DEBUG
+ //#define BEAST_FORCE_DEBUG 0
+#endif
+
+//=============================================================================
+/** Config: BEAST_LOG_ASSERTIONS
+
+ If this flag is enabled, the the bassert 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 bassert and jassertfalse macros will not be compiled in a
+ release build.
+
+ @see bassert, jassertfalse, Logger
+*/
+#ifndef BEAST_LOG_ASSERTIONS
+ #if BEAST_ANDROID
+ #define BEAST_LOG_ASSERTIONS 1
+ #else
+ #define BEAST_LOG_ASSERTIONS 0
+ #endif
+#endif
+
+//=============================================================================
+/** Config: BEAST_CHECK_MEMORY_LEAKS
+
+ Enables a memory-leak check for certain objects when the app terminates. See the LeakedObjectDetector
+ class and the BEAST_LEAK_DETECTOR macro for more details about enabling leak checking for specific classes.
+*/
+#if BEAST_DEBUG && ! defined (BEAST_CHECK_MEMORY_LEAKS)
+ #define BEAST_CHECK_MEMORY_LEAKS 1
+#endif
+
+//=============================================================================
+/** Config: BEAST_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 BEAST_DONT_AUTOLINK_TO_WIN32_LIBRARIES
+ #define BEAST_DONT_AUTOLINK_TO_WIN32_LIBRARIES 0
+#endif
+
+/* Config: BEAST_INCLUDE_ZLIB_CODE
+ This can be used to disable Beast'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 BEAST_ZLIB_INCLUDE_PATH, to
+ specify the path where your zlib headers live.
+*/
+#ifndef BEAST_INCLUDE_ZLIB_CODE
+ #define BEAST_INCLUDE_ZLIB_CODE 1
+#endif
+
+#ifndef BEAST_ZLIB_INCLUDE_PATH
+ #define BEAST_ZLIB_INCLUDE_PATH
+#endif
+
+/* Config: BEAST_CATCH_UNHANDLED_EXCEPTIONS
+ If enabled, this will add some exception-catching code to forward unhandled exceptions
+ to your BEASTApplication::unhandledException() callback.
+*/
+#ifndef BEAST_CATCH_UNHANDLED_EXCEPTIONS
+ //#define BEAST_CATCH_UNHANDLED_EXCEPTIONS 1
+#endif
+
+//=============================================================================
+//=============================================================================
+#if BEAST_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/beast_StandardHeader.h"
namespace beast
{
-#include "memory/beast_ScopedPointer.h"
+// START_AUTOINCLUDE containers, files, json, logging, maths, memory, misc, network,
+// streams, system, text, threads, time, unit_tests, xml, zip
+#ifndef BEAST_ABSTRACTFIFO_BEASTHEADER
+ #include "containers/beast_AbstractFifo.h"
+#endif
+#ifndef BEAST_ARRAY_BEASTHEADER
+ #include "containers/beast_Array.h"
+#endif
+#ifndef BEAST_ARRAYALLOCATIONBASE_BEASTHEADER
+ #include "containers/beast_ArrayAllocationBase.h"
+#endif
+#ifndef BEAST_DYNAMICOBJECT_BEASTHEADER
+ #include "containers/beast_DynamicObject.h"
+#endif
+#ifndef BEAST_ELEMENTCOMPARATOR_BEASTHEADER
+ #include "containers/beast_ElementComparator.h"
+#endif
+#ifndef BEAST_HASHMAP_BEASTHEADER
+ #include "containers/beast_HashMap.h"
+#endif
+#ifndef BEAST_LINKEDLISTPOINTER_BEASTHEADER
+ #include "containers/beast_LinkedListPointer.h"
+#endif
+#ifndef BEAST_NAMEDVALUESET_BEASTHEADER
+ #include "containers/beast_NamedValueSet.h"
+#endif
+#ifndef BEAST_OWNEDARRAY_BEASTHEADER
+ #include "containers/beast_OwnedArray.h"
+#endif
+#ifndef BEAST_PROPERTYSET_BEASTHEADER
+ #include "containers/beast_PropertySet.h"
+#endif
+#ifndef BEAST_REFERENCECOUNTEDARRAY_BEASTHEADER
+ #include "containers/beast_ReferenceCountedArray.h"
+#endif
+#ifndef BEAST_SCOPEDVALUESETTER_BEASTHEADER
+ #include "containers/beast_ScopedValueSetter.h"
+#endif
+#ifndef BEAST_SORTEDSET_BEASTHEADER
+ #include "containers/beast_SortedSet.h"
+#endif
+#ifndef BEAST_SPARSESET_BEASTHEADER
+ #include "containers/beast_SparseSet.h"
+#endif
+#ifndef BEAST_VARIANT_BEASTHEADER
+ #include "containers/beast_Variant.h"
+#endif
+#ifndef BEAST_DIRECTORYITERATOR_BEASTHEADER
+ #include "files/beast_DirectoryIterator.h"
+#endif
+#ifndef BEAST_FILE_BEASTHEADER
+ #include "files/beast_File.h"
+#endif
+#ifndef BEAST_FILEINPUTSTREAM_BEASTHEADER
+ #include "files/beast_FileInputStream.h"
+#endif
+#ifndef BEAST_FILEOUTPUTSTREAM_BEASTHEADER
+ #include "files/beast_FileOutputStream.h"
+#endif
+#ifndef BEAST_FILESEARCHPATH_BEASTHEADER
+ #include "files/beast_FileSearchPath.h"
+#endif
+#ifndef BEAST_MEMORYMAPPEDFILE_BEASTHEADER
+ #include "files/beast_MemoryMappedFile.h"
+#endif
+#ifndef BEAST_TEMPORARYFILE_BEASTHEADER
+ #include "files/beast_TemporaryFile.h"
+#endif
+#ifndef BEAST_JSON_BEASTHEADER
+ #include "json/beast_JSON.h"
+#endif
+#ifndef BEAST_FILELOGGER_BEASTHEADER
+ #include "logging/beast_FileLogger.h"
+#endif
+#ifndef BEAST_LOGGER_BEASTHEADER
+ #include "logging/beast_Logger.h"
+#endif
+#ifndef BEAST_BIGINTEGER_BEASTHEADER
+ #include "maths/beast_BigInteger.h"
+#endif
+#ifndef BEAST_EXPRESSION_BEASTHEADER
+ #include "maths/beast_Expression.h"
+#endif
+#ifndef BEAST_MATHSFUNCTIONS_BEASTHEADER
+ #include "maths/beast_MathsFunctions.h"
+#endif
+#ifndef BEAST_RANDOM_BEASTHEADER
+ #include "maths/beast_Random.h"
+#endif
+#ifndef BEAST_RANGE_BEASTHEADER
+ #include "maths/beast_Range.h"
+#endif
+#ifndef BEAST_ATOMIC_BEASTHEADER
+ #include "memory/beast_Atomic.h"
+#endif
+#ifndef BEAST_BYTEORDER_BEASTHEADER
+ #include "memory/beast_ByteOrder.h"
+#endif
+#ifndef BEAST_HEAPBLOCK_BEASTHEADER
+ #include "memory/beast_HeapBlock.h"
+#endif
+#ifndef BEAST_LEAKEDOBJECTDETECTOR_BEASTHEADER
+ #include "memory/beast_LeakedObjectDetector.h"
+#endif
+#ifndef BEAST_MEMORY_BEASTHEADER
+ #include "memory/beast_Memory.h"
+#endif
+#ifndef BEAST_MEMORYBLOCK_BEASTHEADER
+ #include "memory/beast_MemoryBlock.h"
+#endif
+#ifndef BEAST_OPTIONALSCOPEDPOINTER_BEASTHEADER
+ #include "memory/beast_OptionalScopedPointer.h"
+#endif
+#ifndef BEAST_REFERENCECOUNTEDOBJECT_BEASTHEADER
+ #include "memory/beast_ReferenceCountedObject.h"
+#endif
+#ifndef BEAST_SCOPEDPOINTER_BEASTHEADER
+ #include "memory/beast_ScopedPointer.h"
+#endif
+#ifndef BEAST_SINGLETON_BEASTHEADER
+ #include "memory/beast_Singleton.h"
+#endif
+#ifndef BEAST_WEAKREFERENCE_BEASTHEADER
+ #include "memory/beast_WeakReference.h"
+#endif
+#ifndef BEAST_RESULT_BEASTHEADER
+ #include "misc/beast_Result.h"
+#endif
+#ifndef BEAST_UUID_BEASTHEADER
+ #include "misc/beast_Uuid.h"
+#endif
+#ifndef BEAST_WINDOWSREGISTRY_BEASTHEADER
+ #include "misc/beast_WindowsRegistry.h"
+#endif
+#ifndef BEAST_IPADDRESS_BEASTHEADER
+ #include "network/beast_IPAddress.h"
+#endif
+#ifndef BEAST_MACADDRESS_BEASTHEADER
+ #include "network/beast_MACAddress.h"
+#endif
+#ifndef BEAST_NAMEDPIPE_BEASTHEADER
+ #include "network/beast_NamedPipe.h"
+#endif
+#ifndef BEAST_SOCKET_BEASTHEADER
+ #include "network/beast_Socket.h"
+#endif
+#ifndef BEAST_URL_BEASTHEADER
+ #include "network/beast_URL.h"
+#endif
+#ifndef BEAST_BUFFEREDINPUTSTREAM_BEASTHEADER
+ #include "streams/beast_BufferedInputStream.h"
+#endif
+#ifndef BEAST_FILEINPUTSOURCE_BEASTHEADER
+ #include "streams/beast_FileInputSource.h"
+#endif
+#ifndef BEAST_INPUTSOURCE_BEASTHEADER
+ #include "streams/beast_InputSource.h"
+#endif
+#ifndef BEAST_INPUTSTREAM_BEASTHEADER
+ #include "streams/beast_InputStream.h"
+#endif
+#ifndef BEAST_MEMORYINPUTSTREAM_BEASTHEADER
+ #include "streams/beast_MemoryInputStream.h"
+#endif
+#ifndef BEAST_MEMORYOUTPUTSTREAM_BEASTHEADER
+ #include "streams/beast_MemoryOutputStream.h"
+#endif
+#ifndef BEAST_OUTPUTSTREAM_BEASTHEADER
+ #include "streams/beast_OutputStream.h"
+#endif
+#ifndef BEAST_SUBREGIONSTREAM_BEASTHEADER
+ #include "streams/beast_SubregionStream.h"
+#endif
+#ifndef BEAST_PLATFORMDEFS_BEASTHEADER
+ #include "system/beast_PlatformDefs.h"
+#endif
+#ifndef BEAST_STANDARDHEADER_BEASTHEADER
+ #include "system/beast_StandardHeader.h"
+#endif
+#ifndef BEAST_SYSTEMSTATS_BEASTHEADER
+ #include "system/beast_SystemStats.h"
+#endif
+#ifndef BEAST_TARGETPLATFORM_BEASTHEADER
+ #include "system/beast_TargetPlatform.h"
+#endif
+#ifndef BEAST_CHARACTERFUNCTIONS_BEASTHEADER
+ #include "text/beast_CharacterFunctions.h"
+#endif
+#ifndef BEAST_CHARPOINTER_ASCII_BEASTHEADER
+ #include "text/beast_CharPointer_ASCII.h"
+#endif
+#ifndef BEAST_CHARPOINTER_UTF16_BEASTHEADER
+ #include "text/beast_CharPointer_UTF16.h"
+#endif
+#ifndef BEAST_CHARPOINTER_UTF32_BEASTHEADER
+ #include "text/beast_CharPointer_UTF32.h"
+#endif
+#ifndef BEAST_CHARPOINTER_UTF8_BEASTHEADER
+ #include "text/beast_CharPointer_UTF8.h"
+#endif
+#ifndef BEAST_IDENTIFIER_BEASTHEADER
+ #include "text/beast_Identifier.h"
+#endif
+#ifndef BEAST_LOCALISEDSTRINGS_BEASTHEADER
+ #include "text/beast_LocalisedStrings.h"
+#endif
+#ifndef BEAST_NEWLINE_BEASTHEADER
+ #include "text/beast_NewLine.h"
+#endif
+#ifndef BEAST_STRING_BEASTHEADER
+ #include "text/beast_String.h"
+#endif
+#ifndef BEAST_STRINGARRAY_BEASTHEADER
+ #include "text/beast_StringArray.h"
+#endif
+#ifndef BEAST_STRINGPAIRARRAY_BEASTHEADER
+ #include "text/beast_StringPairArray.h"
+#endif
+#ifndef BEAST_STRINGPOOL_BEASTHEADER
+ #include "text/beast_StringPool.h"
+#endif
+#ifndef BEAST_TEXTDIFF_BEASTHEADER
+ #include "text/beast_TextDiff.h"
+#endif
+#ifndef BEAST_CHILDPROCESS_BEASTHEADER
+ #include "threads/beast_ChildProcess.h"
+#endif
+#ifndef BEAST_CRITICALSECTION_BEASTHEADER
+ #include "threads/beast_CriticalSection.h"
+#endif
+#ifndef BEAST_DYNAMICLIBRARY_BEASTHEADER
+ #include "threads/beast_DynamicLibrary.h"
+#endif
+#ifndef BEAST_HIGHRESOLUTIONTIMER_BEASTHEADER
+ #include "threads/beast_HighResolutionTimer.h"
+#endif
+#ifndef BEAST_INTERPROCESSLOCK_BEASTHEADER
+ #include "threads/beast_InterProcessLock.h"
+#endif
+#ifndef BEAST_PROCESS_BEASTHEADER
+ #include "threads/beast_Process.h"
+#endif
+#ifndef BEAST_READWRITELOCK_BEASTHEADER
+ #include "threads/beast_ReadWriteLock.h"
+#endif
+#ifndef BEAST_SCOPEDLOCK_BEASTHEADER
+ #include "threads/beast_ScopedLock.h"
+#endif
+#ifndef BEAST_SCOPEDREADLOCK_BEASTHEADER
+ #include "threads/beast_ScopedReadLock.h"
+#endif
+#ifndef BEAST_SCOPEDWRITELOCK_BEASTHEADER
+ #include "threads/beast_ScopedWriteLock.h"
+#endif
+#ifndef BEAST_SPINLOCK_BEASTHEADER
+ #include "threads/beast_SpinLock.h"
+#endif
+#ifndef BEAST_THREAD_BEASTHEADER
+ #include "threads/beast_Thread.h"
+#endif
+#ifndef BEAST_THREADLOCALVALUE_BEASTHEADER
+ #include "threads/beast_ThreadLocalValue.h"
+#endif
+#ifndef BEAST_THREADPOOL_BEASTHEADER
+ #include "threads/beast_ThreadPool.h"
+#endif
+#ifndef BEAST_TIMESLICETHREAD_BEASTHEADER
+ #include "threads/beast_TimeSliceThread.h"
+#endif
+#ifndef BEAST_WAITABLEEVENT_BEASTHEADER
+ #include "threads/beast_WaitableEvent.h"
+#endif
+#ifndef BEAST_PERFORMANCECOUNTER_BEASTHEADER
+ #include "time/beast_PerformanceCounter.h"
+#endif
+#ifndef BEAST_RELATIVETIME_BEASTHEADER
+ #include "time/beast_RelativeTime.h"
+#endif
+#ifndef BEAST_TIME_BEASTHEADER
+ #include "time/beast_Time.h"
+#endif
+#ifndef BEAST_UNITTEST_BEASTHEADER
+ #include "unit_tests/beast_UnitTest.h"
+#endif
+#ifndef BEAST_XMLDOCUMENT_BEASTHEADER
+ #include "xml/beast_XmlDocument.h"
+#endif
+#ifndef BEAST_XMLELEMENT_BEASTHEADER
+ #include "xml/beast_XmlElement.h"
+#endif
+#ifndef BEAST_GZIPCOMPRESSOROUTPUTSTREAM_BEASTHEADER
+ #include "zip/beast_GZIPCompressorOutputStream.h"
+#endif
+#ifndef BEAST_GZIPDECOMPRESSORINPUTSTREAM_BEASTHEADER
+ #include "zip/beast_GZIPDecompressorInputStream.h"
+#endif
+#ifndef BEAST_ZIPFILE_BEASTHEADER
+ #include "zip/beast_ZipFile.h"
+#endif
+// END_AUTOINCLUDE
}
+
+#if BEAST_MSVC
+ #pragma warning (pop)
+#endif
+
+#endif // BEAST_CORE_BEASTHEADER
diff --git a/modules/beast_core/beast_core.mm b/modules/beast_core/beast_core.mm
index 96fbd4d0..2b9492c6 100644
--- a/modules/beast_core/beast_core.mm
+++ b/modules/beast_core/beast_core.mm
@@ -1 +1,24 @@
+//------------------------------------------------------------------------------
+/*
+ This file is part of Beast: https://github.com/vinniefalco/Beast
+ Copyright 2013, Vinnie Falco
+
+ Portions of this file are from JUCE.
+ Copyright (c) 2013 - Raw Material Software Ltd.
+ Please visit http://www.juce.com
+
+ Permission to use, copy, modify, and/or distribute this software 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.
+*/
+//==============================================================================
+
#include "beast_core.cpp"
diff --git a/modules/beast_core/containers/beast_AbstractFifo.cpp b/modules/beast_core/containers/beast_AbstractFifo.cpp
new file mode 100644
index 00000000..9663e79f
--- /dev/null
+++ b/modules/beast_core/containers/beast_AbstractFifo.cpp
@@ -0,0 +1,229 @@
+//------------------------------------------------------------------------------
+/*
+ This file is part of Beast: https://github.com/vinniefalco/Beast
+ Copyright 2013, Vinnie Falco
+
+ Portions of this file are from JUCE.
+ Copyright (c) 2013 - Raw Material Software Ltd.
+ Please visit http://www.juce.com
+
+ Permission to use, copy, modify, and/or distribute this software 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.
+*/
+//==============================================================================
+
+AbstractFifo::AbstractFifo (const int capacity) noexcept
+ : bufferSize (capacity)
+{
+ bassert (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
+{
+ bassert (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 = bmin (numToWrite, freeSpace - 1);
+
+ if (numToWrite <= 0)
+ {
+ startIndex1 = 0;
+ startIndex2 = 0;
+ blockSize1 = 0;
+ blockSize2 = 0;
+ }
+ else
+ {
+ startIndex1 = ve;
+ startIndex2 = 0;
+ blockSize1 = bmin (bufferSize - ve, numToWrite);
+ numToWrite -= blockSize1;
+ blockSize2 = numToWrite <= 0 ? 0 : bmin (numToWrite, vs);
+ }
+}
+
+void AbstractFifo::finishedWrite (int numWritten) noexcept
+{
+ bassert (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 = bmin (numWanted, numReady);
+
+ if (numWanted <= 0)
+ {
+ startIndex1 = 0;
+ startIndex2 = 0;
+ blockSize1 = 0;
+ blockSize2 = 0;
+ }
+ else
+ {
+ startIndex1 = vs;
+ startIndex2 = 0;
+ blockSize1 = bmin (bufferSize - vs, numWanted);
+ numWanted -= blockSize1;
+ blockSize2 = numWanted <= 0 ? 0 : bmin (numWanted, ve);
+ }
+}
+
+void AbstractFifo::finishedRead (int numRead) noexcept
+{
+ bassert (numRead >= 0 && numRead <= bufferSize);
+
+ int newStart = validStart.value + numRead;
+ if (newStart >= bufferSize)
+ newStart -= bufferSize;
+
+ validStart = newStart;
+}
+
+//==============================================================================
+//==============================================================================
+#if BEAST_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);
+
+ bassert (size1 >= 0 && size2 >= 0);
+ bassert (size1 == 0 || (start1 >= 0 && start1 < fifo.getTotalSize()));
+ bassert (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
diff --git a/modules/beast_core/containers/beast_AbstractFifo.h b/modules/beast_core/containers/beast_AbstractFifo.h
new file mode 100644
index 00000000..4c5e6856
--- /dev/null
+++ b/modules/beast_core/containers/beast_AbstractFifo.h
@@ -0,0 +1,217 @@
+//------------------------------------------------------------------------------
+/*
+ This file is part of Beast: https://github.com/vinniefalco/Beast
+ Copyright 2013, Vinnie Falco
+
+ Portions of this file are from JUCE.
+ Copyright (c) 2013 - Raw Material Software Ltd.
+ Please visit http://www.juce.com
+
+ Permission to use, copy, modify, and/or distribute this software 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.
+*/
+//==============================================================================
+
+#ifndef BEAST_ABSTRACTFIFO_BEASTHEADER
+#define BEAST_ABSTRACTFIFO_BEASTHEADER
+
+#include "../memory/beast_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 BEAST_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 validStart, validEnd;
+
+ BEAST_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AbstractFifo)
+};
+
+
+#endif // BEAST_ABSTRACTFIFO_BEASTHEADER
diff --git a/modules/beast_core/containers/beast_Array.h b/modules/beast_core/containers/beast_Array.h
new file mode 100644
index 00000000..35c6ac42
--- /dev/null
+++ b/modules/beast_core/containers/beast_Array.h
@@ -0,0 +1,1047 @@
+//------------------------------------------------------------------------------
+/*
+ This file is part of Beast: https://github.com/vinniefalco/Beast
+ Copyright 2013, Vinnie Falco
+
+ Portions of this file are from JUCE.
+ Copyright (c) 2013 - Raw Material Software Ltd.
+ Please visit http://www.juce.com
+
+ Permission to use, copy, modify, and/or distribute this software 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.
+*/
+//==============================================================================
+
+#ifndef BEAST_ARRAY_BEASTHEADER
+#define BEAST_ARRAY_BEASTHEADER
+
+#include "beast_ArrayAllocationBase.h"
+#include "beast_ElementComparator.h"
+#include "../threads/beast_CriticalSection.h"
+
+
+//==============================================================================
+/**
+ Holds a resizable array of primitive or copy-by-value objects.
+
+ Examples of arrays are: Array, Array or Array
+
+ The Array class can be used to hold simple, non-polymorphic objects as well as primitive types - to
+ do so, the class must fulfil these requirements:
+ - it must have a copy constructor and assignment operator
+ - it must be able to be relocated in memory by a memcpy without this causing any problems - so
+ objects whose functionality relies on external pointers or references to themselves can be used.
+
+ You can of course have an array of pointers to any kind of object, e.g. Array , but if
+ you do this, the array doesn't take any ownership of the objects - see the OwnedArray class or the
+ ReferenceCountedArray class for more powerful ways of holding lists of objects.
+
+ For holding lists of strings, you can use Array\, but it's usually better to use the
+ specialised class StringArray, which provides more useful functions.
+
+ To make all the array's methods thread-safe, pass in "CriticalSection" as the templated
+ TypeOfCriticalSectionToUse parameter, instead of the default DummyCriticalSection.
+
+ @see OwnedArray, ReferenceCountedArray, StringArray, CriticalSection
+*/
+template
+class Array
+{
+private:
+ typedef PARAMETER_TYPE (ElementType) ParameterType;
+
+public:
+ //==============================================================================
+ /** Creates an empty array. */
+ Array() noexcept
+ : numUsed (0)
+ {
+ }
+
+ /** Creates a copy of another array.
+ @param other the array to copy
+ */
+ Array (const Array& other)
+ {
+ const ScopedLockType lock (other.getLock());
+ numUsed = other.numUsed;
+ data.setAllocatedSize (other.numUsed);
+
+ for (int i = 0; i < numUsed; ++i)
+ new (data.elements + i) ElementType (other.data.elements[i]);
+ }
+
+ #if BEAST_COMPILER_SUPPORTS_MOVE_SEMANTICS
+ Array (Array&& other) noexcept
+ : data (static_cast &&> (other.data)),
+ numUsed (other.numUsed)
+ {
+ other.numUsed = 0;
+ }
+ #endif
+
+ /** Initalises from a null-terminated C array of values.
+
+ @param values the array to copy from
+ */
+ template
+ explicit Array (const TypeToCreateFrom* values)
+ : numUsed (0)
+ {
+ while (*values != TypeToCreateFrom())
+ add (*values++);
+ }
+
+ /** Initalises from a C array of values.
+
+ @param values the array to copy from
+ @param numValues the number of values in the array
+ */
+ template
+ Array (const TypeToCreateFrom* values, int numValues)
+ : numUsed (numValues)
+ {
+ data.setAllocatedSize (numValues);
+
+ for (int i = 0; i < numValues; ++i)
+ new (data.elements + i) ElementType (values[i]);
+ }
+
+ /** Destructor. */
+ ~Array()
+ {
+ deleteAllElements();
+ }
+
+ /** Copies another array.
+ @param other the array to copy
+ */
+ Array& operator= (const Array& other)
+ {
+ if (this != &other)
+ {
+ Array otherCopy (other);
+ swapWithArray (otherCopy);
+ }
+
+ return *this;
+ }
+
+ #if BEAST_COMPILER_SUPPORTS_MOVE_SEMANTICS
+ Array& operator= (Array&& other) noexcept
+ {
+ const ScopedLockType lock (getLock());
+ data = static_cast &&> (other.data);
+ numUsed = other.numUsed;
+ other.numUsed = 0;
+ return *this;
+ }
+ #endif
+
+ //==============================================================================
+ /** Compares this array to another one.
+ Two arrays are considered equal if they both contain the same set of
+ elements, in the same order.
+ @param other the other array to compare with
+ */
+ template
+ bool operator== (const OtherArrayType& other) const
+ {
+ const ScopedLockType lock (getLock());
+ const typename OtherArrayType::ScopedLockType lock2 (other.getLock());
+
+ if (numUsed != other.numUsed)
+ return false;
+
+ for (int i = numUsed; --i >= 0;)
+ if (! (data.elements [i] == other.data.elements [i]))
+ return false;
+
+ return true;
+ }
+
+ /** Compares this array to another one.
+ Two arrays are considered equal if they both contain the same set of
+ elements, in the same order.
+ @param other the other array to compare with
+ */
+ template
+ bool operator!= (const OtherArrayType& other) const
+ {
+ return ! operator== (other);
+ }
+
+ //==============================================================================
+ /** Removes all elements from the array.
+ This will remove all the elements, and free any storage that the array is
+ using. To clear the array without freeing the storage, use the clearQuick()
+ method instead.
+
+ @see clearQuick
+ */
+ void clear()
+ {
+ const ScopedLockType lock (getLock());
+ deleteAllElements();
+ data.setAllocatedSize (0);
+ numUsed = 0;
+ }
+
+ /** Removes all elements from the array without freeing the array's allocated storage.
+
+ @see clear
+ */
+ void clearQuick()
+ {
+ const ScopedLockType lock (getLock());
+ deleteAllElements();
+ numUsed = 0;
+ }
+
+ //==============================================================================
+ /** Returns the current number of elements in the array.
+ */
+ inline int size() const noexcept
+ {
+ return numUsed;
+ }
+
+ /** Returns one of the elements in the array.
+ If the index passed in is beyond the range of valid elements, this
+ will return a default value.
+
+ 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 array)
+ @see getUnchecked, getFirst, getLast
+ */
+ ElementType operator[] (const int index) const
+ {
+ const ScopedLockType lock (getLock());
+ return isPositiveAndBelow (index, numUsed) ? data.elements [index]
+ : ElementType();
+ }
+
+ /** Returns one of the elements in the array, 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 array, 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 array)
+ @see operator[], getFirst, getLast
+ */
+ inline ElementType getUnchecked (const int index) const
+ {
+ const ScopedLockType lock (getLock());
+ bassert (isPositiveAndBelow (index, numUsed));
+ return data.elements [index];
+ }
+
+ /** Returns a direct reference to one of the elements in the array, 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)
+ @see operator[], getFirst, getLast
+ */
+ inline ElementType& getReference (const int index) const noexcept
+ {
+ const ScopedLockType lock (getLock());
+ bassert (isPositiveAndBelow (index, numUsed));
+ return data.elements [index];
+ }
+
+ /** Returns the first element in the array, or a default value if the array is empty.
+
+ @see operator[], getUnchecked, getLast
+ */
+ inline ElementType getFirst() const
+ {
+ const ScopedLockType lock (getLock());
+ return (numUsed > 0) ? data.elements [0]
+ : ElementType();
+ }
+
+ /** Returns the last element in the array, or a default value if the array is empty.
+
+ @see operator[], getUnchecked, getFirst
+ */
+ inline ElementType getLast() const
+ {
+ const ScopedLockType lock (getLock());
+ return (numUsed > 0) ? data.elements [numUsed - 1]
+ : ElementType();
+ }
+
+ /** 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 ElementType* 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 ElementType* 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 ElementType* end() const noexcept
+ {
+ return data.elements + numUsed;
+ }
+
+ //==============================================================================
+ /** Finds the index of the first element which matches the value passed in.
+
+ This will search the array 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 (ParameterType elementToLookFor) const
+ {
+ const ScopedLockType lock (getLock());
+ const ElementType* e = data.elements.getData();
+ const ElementType* const end_ = e + numUsed;
+
+ for (; e != end_; ++e)
+ if (elementToLookFor == *e)
+ return static_cast (e - data.elements.getData());
+
+ return -1;
+ }
+
+ /** Returns true if the array 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 (ParameterType elementToLookFor) const
+ {
+ const ScopedLockType lock (getLock());
+ const ElementType* e = data.elements.getData();
+ const ElementType* const end_ = e + numUsed;
+
+ for (; e != end_; ++e)
+ if (elementToLookFor == *e)
+ return true;
+
+ return false;
+ }
+
+ //==============================================================================
+ /** Appends a new element at the end of the array.
+
+ @param newElement the new object to add to the array
+ @see set, insert, addIfNotAlreadyThere, addSorted, addUsingDefaultSort, addArray
+ */
+ void add (ParameterType newElement)
+ {
+ const ScopedLockType lock (getLock());
+ data.ensureAllocatedSize (numUsed + 1);
+ new (data.elements + numUsed++) ElementType (newElement);
+ }
+
+ /** Inserts a new element into the array at a given position.
+
+ 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.
+
+ @param indexToInsertAt the index at which the new element should be
+ inserted (pass in -1 to add it to the end)
+ @param newElement the new object to add to the array
+ @see add, addSorted, addUsingDefaultSort, set
+ */
+ void insert (int indexToInsertAt, ParameterType newElement)
+ {
+ const ScopedLockType lock (getLock());
+ data.ensureAllocatedSize (numUsed + 1);
+
+ if (isPositiveAndBelow (indexToInsertAt, numUsed))
+ {
+ ElementType* const insertPos = data.elements + indexToInsertAt;
+ const int numberToMove = numUsed - indexToInsertAt;
+
+ if (numberToMove > 0)
+ memmove (insertPos + 1, insertPos, ((size_t) numberToMove) * sizeof (ElementType));
+
+ new (insertPos) ElementType (newElement);
+ ++numUsed;
+ }
+ else
+ {
+ new (data.elements + numUsed++) ElementType (newElement);
+ }
+ }
+
+ /** Inserts multiple copies of an element into the array at a given position.
+
+ 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.
+
+ @param indexToInsertAt the index at which the new element should be inserted
+ @param newElement the new object to add to the array
+ @param numberOfTimesToInsertIt how many copies of the value to insert
+ @see insert, add, addSorted, set
+ */
+ void insertMultiple (int indexToInsertAt, ParameterType newElement,
+ int numberOfTimesToInsertIt)
+ {
+ if (numberOfTimesToInsertIt > 0)
+ {
+ const ScopedLockType lock (getLock());
+ data.ensureAllocatedSize (numUsed + numberOfTimesToInsertIt);
+ ElementType* insertPos;
+
+ if (isPositiveAndBelow (indexToInsertAt, numUsed))
+ {
+ insertPos = data.elements + indexToInsertAt;
+ const int numberToMove = numUsed - indexToInsertAt;
+ memmove (insertPos + numberOfTimesToInsertIt, insertPos, ((size_t) numberToMove) * sizeof (ElementType));
+ }
+ else
+ {
+ insertPos = data.elements + numUsed;
+ }
+
+ numUsed += numberOfTimesToInsertIt;
+
+ while (--numberOfTimesToInsertIt >= 0)
+ new (insertPos++) ElementType (newElement);
+ }
+ }
+
+ /** 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 newElements 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,
+ const ElementType* newElements,
+ int numberOfElements)
+ {
+ if (numberOfElements > 0)
+ {
+ const ScopedLockType lock (getLock());
+ data.ensureAllocatedSize (numUsed + numberOfElements);
+ ElementType* insertPos = data.elements;
+
+ if (isPositiveAndBelow (indexToInsertAt, numUsed))
+ {
+ insertPos += indexToInsertAt;
+ const int numberToMove = numUsed - indexToInsertAt;
+ memmove (insertPos + numberOfElements, insertPos, numberToMove * sizeof (ElementType));
+ }
+ else
+ {
+ insertPos += numUsed;
+ }
+
+ numUsed += numberOfElements;
+
+ while (--numberOfElements >= 0)
+ new (insertPos++) ElementType (*newElements++);
+ }
+ }
+
+ /** Appends a new element at the end of the array as long as the array doesn't
+ already contain it.
+
+ If the array already contains an element that matches the one passed in, nothing
+ will be done.
+
+ @param newElement the new object to add to the array
+ */
+ void addIfNotAlreadyThere (ParameterType newElement)
+ {
+ const ScopedLockType lock (getLock());
+
+ if (! contains (newElement))
+ add (newElement);
+ }
+
+ /** Replaces an element with a new value.
+
+ If the index is less than zero, this method does nothing.
+ If the index is beyond the end of the array, the item is added to the end of the array.
+
+ @param indexToChange the index whose value you want to change
+ @param newValue the new value to set for this index.
+ @see add, insert
+ */
+ void set (const int indexToChange, ParameterType newValue)
+ {
+ bassert (indexToChange >= 0);
+ const ScopedLockType lock (getLock());
+
+ if (isPositiveAndBelow (indexToChange, numUsed))
+ {
+ data.elements [indexToChange] = newValue;
+ }
+ else if (indexToChange >= 0)
+ {
+ data.ensureAllocatedSize (numUsed + 1);
+ new (data.elements + numUsed++) ElementType (newValue);
+ }
+ }
+
+ /** Replaces an element with a new value without doing any bounds-checking.
+
+ This just sets a value directly in the array's internal storage, so you'd
+ better make sure it's in range!
+
+ @param indexToChange the index whose value you want to change
+ @param newValue the new value to set for this index.
+ @see set, getUnchecked
+ */
+ void setUnchecked (const int indexToChange, ParameterType newValue)
+ {
+ const ScopedLockType lock (getLock());
+ bassert (isPositiveAndBelow (indexToChange, numUsed));
+ data.elements [indexToChange] = newValue;
+ }
+
+ /** Adds elements from an array to the end of this array.
+
+ @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)
+ {
+ const ScopedLockType lock (getLock());
+
+ if (numElementsToAdd > 0)
+ {
+ data.ensureAllocatedSize (numUsed + numElementsToAdd);
+
+ while (--numElementsToAdd >= 0)
+ {
+ new (data.elements + numUsed) ElementType (*elementsToAdd++);
+ ++numUsed;
+ }
+ }
+ }
+
+ /** 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.
+ */
+ void swapWithArray (Array& otherArray) noexcept
+ {
+ const ScopedLockType lock1 (getLock());
+ const ScopedLockType lock2 (otherArray.getLock());
+
+ data.swapWith (otherArray.data);
+ std::swap (numUsed, otherArray.numUsed);
+ }
+
+ /** 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
+ 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;
+
+ while (--numElementsToAdd >= 0)
+ add (arrayToAddFrom.getUnchecked (startIndex++));
+ }
+ }
+
+ /** This will enlarge or shrink the array to the given number of elements, by adding
+ or removing items from its end.
+
+ If the array is smaller than the given target size, empty elements will be appended
+ until its size is as specified. If its size is larger than the target, items will be
+ removed from its end to shorten it.
+ */
+ void resize (const int targetNumItems)
+ {
+ bassert (targetNumItems >= 0);
+
+ const int numToAdd = targetNumItems - numUsed;
+ if (numToAdd > 0)
+ insertMultiple (numUsed, ElementType(), numToAdd);
+ else if (numToAdd < 0)
+ removeRange (targetNumItems, -numToAdd);
+ }
+
+ /** Inserts a new element into the array, assuming that the array is sorted.
+
+ This will use a comparator to find the position at which the new element
+ 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 the form this object should take
+ @param newElement the new element to insert to the array
+ @returns the index at which the new item was added
+ @see addUsingDefaultSort, add, sort
+ */
+ template
+ int addSorted (ElementComparator& comparator, ParameterType newElement)
+ {
+ const ScopedLockType lock (getLock());
+ const int index = findInsertIndexInSortedArray (comparator, data.elements.getData(), newElement, 0, numUsed);
+ insert (index, newElement);
+ return index;
+ }
+
+ /** Inserts a new element into the array, assuming that the array is sorted.
+
+ This will use the DefaultElementComparator class for sorting, so your ElementType
+ must be suitable for use with that class. If the array isn't sorted, the behaviour of this
+ method will be unpredictable.
+
+ @param newElement the new element to insert to the array
+ @see addSorted, sort
+ */
+ void addUsingDefaultSort (ParameterType newElement)
+ {
+ DefaultElementComparator comparator;
+ addSorted (comparator, newElement);
+ }
+
+ /** Finds the index of an element 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 elementToLookFor the element to search for
+ @returns the index of the element, or -1 if it's not found
+ @see addSorted, sort
+ */
+ template
+ int indexOfSorted (ElementComparator& comparator, TargetValueType elementToLookFor) const
+ {
+ (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());
+
+ for (int s = 0, e = numUsed;;)
+ {
+ if (s >= e)
+ return -1;
+
+ if (comparator.compareElements (elementToLookFor, data.elements [s]) == 0)
+ return s;
+
+ const int halfway = (s + e) / 2;
+ if (halfway == s)
+ return -1;
+
+ if (comparator.compareElements (elementToLookFor, data.elements [halfway]) >= 0)
+ s = halfway;
+ else
+ e = halfway;
+ }
+ }
+
+ //==============================================================================
+ /** Removes an element from the array.
+
+ This will remove the element at a given index, and move back
+ all the subsequent elements 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
+ @returns the element that has been removed
+ @see removeValue, removeRange
+ */
+ ElementType remove (const int indexToRemove)
+ {
+ const ScopedLockType lock (getLock());
+
+ if (isPositiveAndBelow (indexToRemove, numUsed))
+ {
+ ElementType removed (data.elements[indexToRemove]);
+ removeInternal (indexToRemove);
+ return removed;
+ }
+
+ return ElementType();
+ }
+
+ /** Removes an item from the array.
+
+ This will remove the first occurrence of the given element from the array.
+ If the item isn't found, no action is taken.
+
+ @param valueToRemove the object to try to remove
+ @see remove, removeRange
+ */
+ void removeFirstMatchingValue (ParameterType valueToRemove)
+ {
+ const ScopedLockType lock (getLock());
+ ElementType* const e = data.elements;
+
+ for (int i = 0; i < numUsed; ++i)
+ {
+ if (valueToRemove == e[i])
+ {
+ removeInternal (i);
+ break;
+ }
+ }
+ }
+
+ /** Removes an item from the array.
+
+ This will remove the first occurrence of the given element from the array.
+ If the item isn't found, no action is taken.
+
+ @param valueToRemove the object to try to remove
+ @see remove, removeRange
+ */
+ void removeAllInstancesOf (ParameterType valueToRemove)
+ {
+ const ScopedLockType lock (getLock());
+
+ for (int i = numUsed; --i >= 0;)
+ if (valueToRemove == data.elements[i])
+ removeInternal (i);
+ }
+
+ /** Removes a range of elements from the array.
+
+ This will remove a set of elements, starting from the given index,
+ and move 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 element to remove
+ @param numberToRemove how many elements should be removed
+ @see remove, removeValue
+ */
+ void removeRange (int startIndex, int numberToRemove)
+ {
+ const ScopedLockType lock (getLock());
+ const int endIndex = blimit (0, numUsed, startIndex + numberToRemove);
+ startIndex = blimit (0, numUsed, startIndex);
+
+ if (endIndex > startIndex)
+ {
+ ElementType* const e = data.elements + startIndex;
+
+ numberToRemove = endIndex - startIndex;
+ for (int i = 0; i < numberToRemove; ++i)
+ e[i].~ElementType();
+
+ const int numToShift = numUsed - endIndex;
+ if (numToShift > 0)
+ memmove (e, e + numberToRemove, ((size_t) numToShift) * sizeof (ElementType));
+
+ numUsed -= numberToRemove;
+ minimiseStorageAfterRemoval();
+ }
+ }
+
+ /** Removes the last n elements from the array.
+
+ @param howManyToRemove how many elements to remove from the end of the array
+ @see remove, removeValue, removeRange
+ */
+ void removeLast (int howManyToRemove = 1)
+ {
+ const ScopedLockType lock (getLock());
+
+ if (howManyToRemove > numUsed)
+ howManyToRemove = numUsed;
+
+ for (int i = 1; i <= howManyToRemove; ++i)
+ data.elements [numUsed - i].~ElementType();
+
+ numUsed -= howManyToRemove;
+ minimiseStorageAfterRemoval();
+ }
+
+ /** Removes any elements which are also in another array.
+
+ @param otherArray the other array in which to look for elements to remove
+ @see removeValuesNotIn, remove, removeValue, removeRange
+ */
+ template
+ void removeValuesIn (const OtherArrayType& otherArray)
+ {
+ const typename OtherArrayType::ScopedLockType lock1 (otherArray.getLock());
+ const ScopedLockType lock2 (getLock());
+
+ if (this == &otherArray)
+ {
+ clear();
+ }
+ else
+ {
+ if (otherArray.size() > 0)
+ {
+ for (int i = numUsed; --i >= 0;)
+ if (otherArray.contains (data.elements [i]))
+ removeInternal (i);
+ }
+ }
+ }
+
+ /** Removes any elements which are not found in another array.
+
+ Only elements which occur in this other array will be retained.
+
+ @param otherArray the array in which to look for elements NOT to remove
+ @see removeValuesIn, remove, removeValue, removeRange
+ */
+ template
+ void removeValuesNotIn (const OtherArrayType& otherArray)
+ {
+ const typename OtherArrayType::ScopedLockType lock1 (otherArray.getLock());
+ const ScopedLockType lock2 (getLock());
+
+ if (this != &otherArray)
+ {
+ if (otherArray.size() <= 0)
+ {
+ clear();
+ }
+ else
+ {
+ for (int i = numUsed; --i >= 0;)
+ if (! otherArray.contains (data.elements [i]))
+ removeInternal (i);
+ }
+ }
+ }
+
+ /** Swaps over two elements in the array.
+
+ This swaps over the elements found at the two indexes passed in.
+ If either index is out-of-range, this method will do nothing.
+
+ @param index1 index of one of the elements to swap
+ @param index2 index of the other element to swap
+ */
+ void swap (const int index1,
+ const int index2)
+ {
+ const ScopedLockType lock (getLock());
+
+ if (isPositiveAndBelow (index1, numUsed)
+ && isPositiveAndBelow (index2, numUsed))
+ {
+ std::swap (data.elements [index1],
+ data.elements [index2]);
+ }
+ }
+
+ /** Moves one of the values to a different position.
+
+ This will move the value 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 value 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 value to end up. If this
+ is less than zero, the value 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;
+
+ char tempCopy [sizeof (ElementType)];
+ memcpy (tempCopy, data.elements + currentIndex, sizeof (ElementType));
+
+ if (newIndex > currentIndex)
+ {
+ memmove (data.elements + currentIndex,
+ data.elements + currentIndex + 1,
+ sizeof (ElementType) * (size_t) (newIndex - currentIndex));
+ }
+ else
+ {
+ memmove (data.elements + newIndex + 1,
+ data.elements + newIndex,
+ sizeof (ElementType) * (size_t) (currentIndex - newIndex));
+ }
+
+ memcpy (data.elements + newIndex, tempCopy, sizeof (ElementType));
+ }
+ }
+ }
+
+ //==============================================================================
+ /** 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()
+ {
+ 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);
+ }
+
+ //==============================================================================
+ /** 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 addSorted, indexOfSorted, sortArray
+ */
+ template
+ void sort (ElementComparator& comparator,
+ const bool retainOrderOfEquivalentItems = false) const
+ {
+ const ScopedLockType lock (getLock());
+ (void) comparator; // if you pass in an object with a static compareElements() method, this
+ // avoids getting warning messages about the parameter being unused
+ sortArray (comparator, data.elements.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;
+
+
+private:
+ //==============================================================================
+ ArrayAllocationBase data;
+ int numUsed;
+
+ void removeInternal (const int indexToRemove)
+ {
+ --numUsed;
+ ElementType* const e = data.elements + indexToRemove;
+ e->~ElementType();
+ const int numberToShift = numUsed - indexToRemove;
+
+ if (numberToShift > 0)
+ memmove (e, e + 1, ((size_t) numberToShift) * sizeof (ElementType));
+
+ minimiseStorageAfterRemoval();
+ }
+
+ inline void deleteAllElements() noexcept
+ {
+ for (int i = 0; i < numUsed; ++i)
+ data.elements[i].~ElementType();
+ }
+
+ void minimiseStorageAfterRemoval()
+ {
+ if (data.numAllocated > bmax (minimumAllocatedSize, numUsed * 2))
+ data.shrinkToNoMoreThan (bmax (numUsed, bmax (minimumAllocatedSize, 64 / (int) sizeof (ElementType))));
+ }
+};
+
+
+#endif // BEAST_ARRAY_BEASTHEADER
diff --git a/modules/beast_core/containers/beast_ArrayAllocationBase.h b/modules/beast_core/containers/beast_ArrayAllocationBase.h
new file mode 100644
index 00000000..479fb909
--- /dev/null
+++ b/modules/beast_core/containers/beast_ArrayAllocationBase.h
@@ -0,0 +1,133 @@
+//------------------------------------------------------------------------------
+/*
+ This file is part of Beast: https://github.com/vinniefalco/Beast
+ Copyright 2013, Vinnie Falco
+
+ Portions of this file are from JUCE.
+ Copyright (c) 2013 - Raw Material Software Ltd.
+ Please visit http://www.juce.com
+
+ Permission to use, copy, modify, and/or distribute this software 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.
+*/
+//==============================================================================
+
+#ifndef BEAST_ARRAYALLOCATIONBASE_BEASTHEADER
+#define BEAST_ARRAYALLOCATIONBASE_BEASTHEADER
+
+#include "../memory/beast_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 ArrayAllocationBase : public TypeOfCriticalSectionToUse
+{
+public:
+ //==============================================================================
+ /** Creates an empty array. */
+ ArrayAllocationBase() noexcept
+ : numAllocated (0)
+ {
+ }
+
+ /** Destructor. */
+ ~ArrayAllocationBase() noexcept
+ {
+ }
+
+ #if BEAST_COMPILER_SUPPORTS_MOVE_SEMANTICS
+ ArrayAllocationBase (ArrayAllocationBase&& other) noexcept
+ : elements (static_cast &&> (other.elements)),
+ numAllocated (other.numAllocated)
+ {
+ }
+
+ ArrayAllocationBase& operator= (ArrayAllocationBase&& other) noexcept
+ {
+ elements = static_cast &&> (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);
+ }
+
+ /** 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 & other) noexcept
+ {
+ elements.swapWith (other.elements);
+ std::swap (numAllocated, other.numAllocated);
+ }
+
+ //==============================================================================
+ HeapBlock elements;
+ int numAllocated;
+
+private:
+ BEAST_DECLARE_NON_COPYABLE (ArrayAllocationBase)
+};
+
+
+#endif // BEAST_ARRAYALLOCATIONBASE_BEASTHEADER
diff --git a/modules/beast_core/containers/beast_DynamicObject.cpp b/modules/beast_core/containers/beast_DynamicObject.cpp
new file mode 100644
index 00000000..0a5e1546
--- /dev/null
+++ b/modules/beast_core/containers/beast_DynamicObject.cpp
@@ -0,0 +1,74 @@
+//------------------------------------------------------------------------------
+/*
+ This file is part of Beast: https://github.com/vinniefalco/Beast
+ Copyright 2013, Vinnie Falco
+
+ Portions of this file are from JUCE.
+ Copyright (c) 2013 - Raw Material Software Ltd.
+ Please visit http://www.juce.com
+
+ Permission to use, copy, modify, and/or distribute this software 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.
+*/
+//==============================================================================
+
+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();
+}
diff --git a/modules/beast_core/containers/beast_DynamicObject.h b/modules/beast_core/containers/beast_DynamicObject.h
new file mode 100644
index 00000000..08ce6d21
--- /dev/null
+++ b/modules/beast_core/containers/beast_DynamicObject.h
@@ -0,0 +1,121 @@
+//------------------------------------------------------------------------------
+/*
+ This file is part of Beast: https://github.com/vinniefalco/Beast
+ Copyright 2013, Vinnie Falco
+
+ Portions of this file are from JUCE.
+ Copyright (c) 2013 - Raw Material Software Ltd.
+ Please visit http://www.juce.com
+
+ Permission to use, copy, modify, and/or distribute this software 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.
+*/
+//==============================================================================
+
+#ifndef BEAST_DYNAMICOBJECT_BEASTHEADER
+#define BEAST_DYNAMICOBJECT_BEASTHEADER
+
+#include "beast_NamedValueSet.h"
+#include "../memory/beast_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 BEAST_API DynamicObject : public ReferenceCountedObject
+{
+public:
+ //==============================================================================
+ DynamicObject();
+
+ /** Destructor. */
+ virtual ~DynamicObject();
+
+ typedef ReferenceCountedObjectPtr 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;
+
+ BEAST_LEAK_DETECTOR (DynamicObject)
+};
+
+
+
+#endif // BEAST_DYNAMICOBJECT_BEASTHEADER
diff --git a/modules/beast_core/containers/beast_ElementComparator.h b/modules/beast_core/containers/beast_ElementComparator.h
new file mode 100644
index 00000000..7ad71b3e
--- /dev/null
+++ b/modules/beast_core/containers/beast_ElementComparator.h
@@ -0,0 +1,274 @@
+//------------------------------------------------------------------------------
+/*
+ This file is part of Beast: https://github.com/vinniefalco/Beast
+ Copyright 2013, Vinnie Falco
+
+ Portions of this file are from JUCE.
+ Copyright (c) 2013 - Raw Material Software Ltd.
+ Please visit http://www.juce.com
+
+ Permission to use, copy, modify, and/or distribute this software 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.
+*/
+//==============================================================================
+
+#ifndef BEAST_ELEMENTCOMPARATOR_BEASTHEADER
+#define BEAST_ELEMENTCOMPARATOR_BEASTHEADER
+
+
+//==============================================================================
+/**
+ 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
+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;
+
+ bassert (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
+static int findInsertIndexInSortedArray (ElementComparator& comparator,
+ ElementType* const array,
+ const ElementType newElement,
+ int firstElement,
+ int lastElement)
+{
+ bassert (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 myArray;
+ DefaultElementComparator sorter;
+ myArray.sort (sorter);
+ @endcode
+
+ @see ElementComparator
+*/
+template
+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 // BEAST_ELEMENTCOMPARATOR_BEASTHEADER
diff --git a/modules/beast_core/containers/beast_HashMap.h b/modules/beast_core/containers/beast_HashMap.h
new file mode 100644
index 00000000..d488020b
--- /dev/null
+++ b/modules/beast_core/containers/beast_HashMap.h
@@ -0,0 +1,447 @@
+//------------------------------------------------------------------------------
+/*
+ This file is part of Beast: https://github.com/vinniefalco/Beast
+ Copyright 2013, Vinnie Falco
+
+ Portions of this file are from JUCE.
+ Copyright (c) 2013 - Raw Material Software Ltd.
+ Please visit http://www.juce.com
+
+ Permission to use, copy, modify, and/or distribute this software 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.
+*/
+//==============================================================================
+
+#ifndef BEAST_HASHMAP_BEASTHEADER
+#define BEAST_HASHMAP_BEASTHEADER
+
+#include "beast_OwnedArray.h"
+#include "beast_LinkedListPointer.h"
+#include "../memory/beast_ScopedPointer.h"
+
+
+//==============================================================================
+/**
+ A simple class to generate hash functions for some primitive types, intended for
+ use with the HashMap class.
+ @see HashMap
+*/
+class DefaultHashFunctions
+{
+public:
+ /** Generates a simple hash from an integer. */
+ static int generateHash (const int key, const int upperLimit) noexcept { return std::abs (key) % upperLimit; }
+ /** Generates a simple hash from an int64. */
+ static int generateHash (const int64 key, const int upperLimit) noexcept { return std::abs ((int) key) % upperLimit; }
+ /** Generates a simple hash from a string. */
+ static int generateHash (const String& key, const int upperLimit) noexcept { return (int) (((uint32) key.hashCode()) % (uint32) upperLimit); }
+ /** Generates a simple hash from a variant. */
+ static int generateHash (const var& key, const int upperLimit) 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
+ {
+ static 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 HashFunctionToUse template parameter, the
+ default one provides some simple mappings for strings and ints.
+
+ @code
+ HashMap 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::Iterator i (hash); i.next();)
+ DBG (i.getKey() << " -> " << i.getValue());
+ @endcode
+
+ @see CriticalSection, DefaultHashFunctions, NamedValueSet, SortedSet
+*/
+template
+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().
+ */
+ explicit HashMap (const int numberOfSlots = defaultHashTableSize)
+ : 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 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 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 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. */
+ void swapWith (HashMap& otherHashMap) noexcept
+ {
+ const ScopedLockType lock1 (getLock());
+ const ScopedLockType lock2 (otherHashMap.getLock());
+
+ slots.swapWithArray (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;
+
+ BEAST_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 myMap;
+
+ HashMap::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;
+
+ BEAST_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Iterator)
+ };
+
+private:
+ //==============================================================================
+ enum { defaultHashTableSize = 101 };
+ friend class Iterator;
+
+ Array slots;
+ int totalNumItems;
+ TypeOfCriticalSectionToUse lock;
+
+ int generateHashFor (KeyTypeParameter key) const
+ {
+ const int hash = HashFunctionToUse::generateHash (key, getNumSlots());
+ bassert (isPositiveAndBelow (hash, getNumSlots())); // your hash function is generating out-of-range numbers!
+ return hash;
+ }
+
+ BEAST_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (HashMap)
+};
+
+
+#endif // BEAST_HASHMAP_BEASTHEADER
diff --git a/modules/beast_core/containers/beast_LinkedListPointer.h b/modules/beast_core/containers/beast_LinkedListPointer.h
new file mode 100644
index 00000000..4ac97c58
--- /dev/null
+++ b/modules/beast_core/containers/beast_LinkedListPointer.h
@@ -0,0 +1,366 @@
+//------------------------------------------------------------------------------
+/*
+ This file is part of Beast: https://github.com/vinniefalco/Beast
+ Copyright 2013, Vinnie Falco
+
+ Portions of this file are from JUCE.
+ Copyright (c) 2013 - Raw Material Software Ltd.
+ Please visit http://www.juce.com
+
+ Permission to use, copy, modify, and/or distribute this software 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.
+*/
+//==============================================================================
+
+#ifndef BEAST_LINKEDLISTPOINTER_BEASTHEADER
+#define BEAST_LINKEDLISTPOINTER_BEASTHEADER
+
+
+//==============================================================================
+/**
+ 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 instead).
+ LinkedListPointer nextListItem;
+ };
+
+ LinkedListPointer myList;
+ myList.append (new MyObject());
+ myList.append (new MyObject());
+
+ int numItems = myList.size(); // returns 2
+ MyObject* lastInList = myList.getLast();
+ @endcode
+*/
+template
+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 BEAST_COMPILER_SUPPORTS_MOVE_SEMANTICS
+ LinkedListPointer (LinkedListPointer&& other) noexcept
+ : item (other.item)
+ {
+ other.item = nullptr;
+ }
+
+ LinkedListPointer& operator= (LinkedListPointer&& other) noexcept
+ {
+ bassert (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)
+ {
+ bassert (newItem != nullptr);
+ bassert (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)
+ {
+ bassert (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
+ {
+ bassert (newItem != nullptr);
+ bassert (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
+ {
+ bassert (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.
+ bassert (endOfListPointer.item == nullptr);
+ }
+
+ /** Appends an item to the list. */
+ void append (ObjectType* const newItem) noexcept
+ {
+ *endOfList = newItem;
+ endOfList = &(newItem->nextListItem);
+ }
+
+ private:
+ LinkedListPointer* endOfList;
+
+ BEAST_DECLARE_NON_COPYABLE (Appender)
+ };
+
+private:
+ //==============================================================================
+ ObjectType* item;
+
+ BEAST_DECLARE_NON_COPYABLE (LinkedListPointer)
+};
+
+
+#endif // BEAST_LINKEDLISTPOINTER_BEASTHEADER
diff --git a/modules/beast_core/containers/beast_NamedValueSet.cpp b/modules/beast_core/containers/beast_NamedValueSet.cpp
new file mode 100644
index 00000000..e092865e
--- /dev/null
+++ b/modules/beast_core/containers/beast_NamedValueSet.cpp
@@ -0,0 +1,304 @@
+//------------------------------------------------------------------------------
+/*
+ This file is part of Beast: https://github.com/vinniefalco/Beast
+ Copyright 2013, Vinnie Falco
+
+ Portions of this file are from JUCE.
+ Copyright (c) 2013 - Raw Material Software Ltd.
+ Please visit http://www.juce.com
+
+ Permission to use, copy, modify, and/or distribute this software 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.
+*/
+//==============================================================================
+
+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 BEAST_COMPILER_SUPPORTS_MOVE_SEMANTICS
+NamedValueSet::NamedValue::NamedValue (NamedValue&& other) noexcept
+ : nextListItem (static_cast &&> (other.nextListItem)),
+ name (static_cast (other.name)),
+ value (static_cast (other.value))
+{
+}
+
+inline NamedValueSet::NamedValue::NamedValue (const Identifier n, var&& v)
+ : name (n), value (static_cast (v))
+{
+}
+
+NamedValueSet::NamedValue& NamedValueSet::NamedValue::operator= (NamedValue&& other) noexcept
+{
+ nextListItem = static_cast &&> (other.nextListItem);
+ name = static_cast (other.name);
+ value = static_cast (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 BEAST_COMPILER_SUPPORTS_MOVE_SEMANTICS
+NamedValueSet::NamedValueSet (NamedValueSet&& other) noexcept
+ : values (static_cast &&> (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 BEAST_COMPILER_SUPPORTS_MOVE_SEMANTICS
+bool NamedValueSet::set (const Identifier name, var&& newValue)
+{
+ LinkedListPointer* 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 (newValue);
+ return true;
+ }
+
+ i = &(v->nextListItem);
+ }
+
+ i->insertNext (new NamedValue (name, static_cast (newValue)));
+ return true;
+}
+#endif
+
+bool NamedValueSet::set (const Identifier name, const var& newValue)
+{
+ LinkedListPointer* 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* 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];
+ bassert (v != nullptr);
+ return v->name;
+}
+
+const var& NamedValueSet::getValueAt (const int index) const
+{
+ const NamedValue* const v = values[index];
+ bassert (v != nullptr);
+ return v->value;
+}
+
+void NamedValueSet::setFromXmlAttributes (const XmlElement& xml)
+{
+ clear();
+ LinkedListPointer::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!
+ bassert (! i->value.isObject());
+ bassert (! i->value.isMethod());
+ bassert (! i->value.isArray());
+
+ xml.setAttribute (i->name.toString(),
+ i->value.toString());
+ }
+ }
+}
diff --git a/modules/beast_core/containers/beast_NamedValueSet.h b/modules/beast_core/containers/beast_NamedValueSet.h
new file mode 100644
index 00000000..1c2d08d9
--- /dev/null
+++ b/modules/beast_core/containers/beast_NamedValueSet.h
@@ -0,0 +1,164 @@
+//------------------------------------------------------------------------------
+/*
+ This file is part of Beast: https://github.com/vinniefalco/Beast
+ Copyright 2013, Vinnie Falco
+
+ Portions of this file are from JUCE.
+ Copyright (c) 2013 - Raw Material Software Ltd.
+ Please visit http://www.juce.com
+
+ Permission to use, copy, modify, and/or distribute this software 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.
+*/
+//==============================================================================
+
+#ifndef BEAST_NAMEDVALUESET_BEASTHEADER
+#define BEAST_NAMEDVALUESET_BEASTHEADER
+
+#include "beast_Variant.h"
+#include "../containers/beast_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 BEAST_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 BEAST_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 BEAST_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 BEAST_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 nextListItem;
+ Identifier name;
+ var value;
+
+ private:
+ BEAST_LEAK_DETECTOR (NamedValue)
+ };
+
+ friend class LinkedListPointer;
+ LinkedListPointer values;
+
+ friend class JSONFormatter;
+};
+
+
+#endif // BEAST_NAMEDVALUESET_BEASTHEADER
diff --git a/modules/beast_core/containers/beast_OwnedArray.h b/modules/beast_core/containers/beast_OwnedArray.h
new file mode 100644
index 00000000..4740f8ca
--- /dev/null
+++ b/modules/beast_core/containers/beast_OwnedArray.h
@@ -0,0 +1,865 @@
+//------------------------------------------------------------------------------
+/*
+ This file is part of Beast: https://github.com/vinniefalco/Beast
+ Copyright 2013, Vinnie Falco
+
+ Portions of this file are from JUCE.
+ Copyright (c) 2013 - Raw Material Software Ltd.
+ Please visit http://www.juce.com
+
+ Permission to use, copy, modify, and/or distribute this software 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.
+*/
+//==============================================================================
+
+#ifndef BEAST_OWNEDARRAY_BEASTHEADER
+#define BEAST_OWNEDARRAY_BEASTHEADER
+
+#include "beast_ArrayAllocationBase.h"
+#include "beast_ElementComparator.h"
+#include "../threads/beast_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
+
+ ..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 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 BEAST_COMPILER_SUPPORTS_MOVE_SEMANTICS
+ OwnedArray (OwnedArray&& other) noexcept
+ : data (static_cast &&> (other.data)),
+ numUsed (other.numUsed)
+ {
+ other.numUsed = 0;
+ }
+
+ OwnedArray& operator= (OwnedArray&& other) noexcept
+ {
+ const ScopedLockType lock (getLock());
+ deleteAllObjects();
+
+ data = static_cast &&> (other.data);
+ numUsed = other.numUsed;
+ other.numUsed = 0;
+ return *this;
+ }
+ #endif
+
+ //==============================================================================
+ /** Clears the array, optionally deleting the objects inside it first. */
+ void clear (const bool deleteObjects = true)
+ {
+ const ScopedLockType lock (getLock());
+
+ if (deleteObjects)
+ deleteAllObjects();
+
+ data.setAllocatedSize (0);
+ 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());
+ return isPositiveAndBelow (index, numUsed) ? data.elements [index]
+ : static_cast (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());
+ bassert (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 ObjectClass* getFirst() const noexcept
+ {
+ const ScopedLockType lock (getLock());
+ return numUsed > 0 ? data.elements [0]
+ : static_cast (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 (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 (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
+ */
+ void add (const ObjectClass* const newObject) noexcept
+ {
+ const ScopedLockType lock (getLock());
+ data.ensureAllocatedSize (numUsed + 1);
+ data.elements [numUsed++] = const_cast (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,
+ const ObjectClass* const newObject) noexcept
+ {
+ if (indexToInsertAt >= 0)
+ {
+ const ScopedLockType lock (getLock());
+
+ if (indexToInsertAt > numUsed)
+ indexToInsertAt = numUsed;
+
+ data.ensureAllocatedSize (numUsed + 1);
+
+ 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 (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 (const 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 (newObject);
+ }
+ else
+ {
+ data.ensureAllocatedSize (numUsed + 1);
+ data.elements [numUsed++] = const_cast (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
+ 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);
+
+ 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
+ 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);
+
+ 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
+ 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
+ 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 = blimit (0, numUsed, startIndex + numberToRemove);
+ startIndex = blimit (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.
+ */
+ void swapWithArray (OwnedArray& otherArray) noexcept
+ {
+ const ScopedLockType lock1 (getLock());
+ const 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
+ 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;
+
+
+private:
+ //==============================================================================
+ ArrayAllocationBase data;
+ int numUsed;
+
+ void deleteAllObjects()
+ {
+ while (numUsed > 0)
+ delete data.elements [--numUsed];
+ }
+
+ BEAST_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OwnedArray)
+};
+
+
+#endif // BEAST_OWNEDARRAY_BEASTHEADER
diff --git a/modules/beast_core/containers/beast_PropertySet.cpp b/modules/beast_core/containers/beast_PropertySet.cpp
new file mode 100644
index 00000000..cf2ff79d
--- /dev/null
+++ b/modules/beast_core/containers/beast_PropertySet.cpp
@@ -0,0 +1,218 @@
+//------------------------------------------------------------------------------
+/*
+ This file is part of Beast: https://github.com/vinniefalco/Beast
+ Copyright 2013, Vinnie Falco
+
+ Portions of this file are from JUCE.
+ Copyright (c) 2013 - Raw Material Software Ltd.
+ Please visit http://www.juce.com
+
+ Permission to use, copy, modify, and/or distribute this software 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.
+*/
+//==============================================================================
+
+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)
+{
+ bassert (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()
+{
+}
diff --git a/modules/beast_core/containers/beast_PropertySet.h b/modules/beast_core/containers/beast_PropertySet.h
new file mode 100644
index 00000000..f6513acc
--- /dev/null
+++ b/modules/beast_core/containers/beast_PropertySet.h
@@ -0,0 +1,214 @@
+//------------------------------------------------------------------------------
+/*
+ This file is part of Beast: https://github.com/vinniefalco/Beast
+ Copyright 2013, Vinnie Falco
+
+ Portions of this file are from JUCE.
+ Copyright (c) 2013 - Raw Material Software Ltd.
+ Please visit http://www.juce.com
+
+ Permission to use, copy, modify, and/or distribute this software 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.
+*/
+//==============================================================================
+
+#ifndef BEAST_PROPERTYSET_BEASTHEADER
+#define BEAST_PROPERTYSET_BEASTHEADER
+
+#include "../text/beast_StringPairArray.h"
+#include "../xml/beast_XmlElement.h"
+#include "../containers/beast_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 BEAST_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;
+
+ BEAST_LEAK_DETECTOR (PropertySet)
+};
+
+
+#endif // BEAST_PROPERTYSET_BEASTHEADER
diff --git a/modules/beast_core/containers/beast_ReferenceCountedArray.h b/modules/beast_core/containers/beast_ReferenceCountedArray.h
new file mode 100644
index 00000000..dd4add79
--- /dev/null
+++ b/modules/beast_core/containers/beast_ReferenceCountedArray.h
@@ -0,0 +1,855 @@
+//------------------------------------------------------------------------------
+/*
+ This file is part of Beast: https://github.com/vinniefalco/Beast
+ Copyright 2013, Vinnie Falco
+
+ Portions of this file are from JUCE.
+ Copyright (c) 2013 - Raw Material Software Ltd.
+ Please visit http://www.juce.com
+
+ Permission to use, copy, modify, and/or distribute this software 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.
+*/
+//==============================================================================
+
+#ifndef BEAST_REFERENCECOUNTEDARRAY_BEASTHEADER
+#define BEAST_REFERENCECOUNTEDARRAY_BEASTHEADER
+
+#include "../memory/beast_ReferenceCountedObject.h"
+#include "beast_ArrayAllocationBase.h"
+#include "beast_ElementComparator.h"
+#include "../threads/beast_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 ReferenceCountedArray
+{
+public:
+ typedef ReferenceCountedObjectPtr 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
+ ReferenceCountedArray (const ReferenceCountedArray& other) noexcept
+ {
+ const typename ReferenceCountedArray::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);
+ swapWithArray (otherCopy);
+ return *this;
+ }
+
+ /** Copies another array into this one.
+ Any existing objects in this array will first be released.
+ */
+ template
+ ReferenceCountedArray& operator= (const ReferenceCountedArray& other) noexcept
+ {
+ ReferenceCountedArray otherCopy (other);
+ swapWithArray (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();
+
+ bassert (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());
+ bassert (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 (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 (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 (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
+ */
+ void add (ObjectClass* const newObject) noexcept
+ {
+ const ScopedLockType lock (getLock());
+ data.ensureAllocatedSize (numUsed + 1);
+ data.elements [numUsed++] = newObject;
+
+ if (newObject != nullptr)
+ newObject->incReferenceCount();
+ }
+
+ /** 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
+ */
+ void insert (int indexToInsertAt,
+ ObjectClass* const newObject) noexcept
+ {
+ if (indexToInsertAt >= 0)
+ {
+ const ScopedLockType lock (getLock());
+
+ if (indexToInsertAt > numUsed)
+ indexToInsertAt = numUsed;
+
+ data.ensureAllocatedSize (numUsed + 1);
+
+ 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;
+ }
+ else
+ {
+ 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);
+ 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& 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
+ 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
+ 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
+ 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 = blimit (0, numUsed, startIndex);
+ const int endIndex = blimit (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.
+ */
+ void swapWithArray (ReferenceCountedArray& otherArray) noexcept
+ {
+ const ScopedLockType lock1 (getLock());
+ const 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& 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
+ 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;
+
+
+private:
+ //==============================================================================
+ ArrayAllocationBase data;
+ int numUsed;
+};
+
+
+#endif // BEAST_REFERENCECOUNTEDARRAY_BEASTHEADER
diff --git a/modules/beast_core/containers/beast_ScopedValueSetter.h b/modules/beast_core/containers/beast_ScopedValueSetter.h
new file mode 100644
index 00000000..945ee13a
--- /dev/null
+++ b/modules/beast_core/containers/beast_ScopedValueSetter.h
@@ -0,0 +1,95 @@
+//------------------------------------------------------------------------------
+/*
+ This file is part of Beast: https://github.com/vinniefalco/Beast
+ Copyright 2013, Vinnie Falco
+
+ Portions of this file are from JUCE.
+ Copyright (c) 2013 - Raw Material Software Ltd.
+ Please visit http://www.juce.com
+
+ Permission to use, copy, modify, and/or distribute this software 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.
+*/
+//==============================================================================
+
+#ifndef BEAST_SCOPEDVALUESETTER_BEASTHEADER
+#define BEAST_SCOPEDVALUESETTER_BEASTHEADER
+
+
+//==============================================================================
+/**
+ 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
+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;
+
+ BEAST_DECLARE_NON_COPYABLE (ScopedValueSetter)
+};
+
+
+#endif // BEAST_SCOPEDVALUESETTER_BEASTHEADER
diff --git a/modules/beast_core/containers/beast_SortedSet.h b/modules/beast_core/containers/beast_SortedSet.h
new file mode 100644
index 00000000..ee6e106f
--- /dev/null
+++ b/modules/beast_core/containers/beast_SortedSet.h
@@ -0,0 +1,494 @@
+//------------------------------------------------------------------------------
+/*
+ This file is part of Beast: https://github.com/vinniefalco/Beast
+ Copyright 2013, Vinnie Falco
+
+ Portions of this file are from JUCE.
+ Copyright (c) 2013 - Raw Material Software Ltd.
+ Please visit http://www.juce.com
+
+ Permission to use, copy, modify, and/or distribute this software 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.
+*/
+//==============================================================================
+
+#ifndef BEAST_SORTEDSET_BEASTHEADER
+#define BEAST_SORTEDSET_BEASTHEADER
+
+#include "beast_ArrayAllocationBase.h"
+#include "../threads/beast_CriticalSection.h"
+
+#if BEAST_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 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& 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& 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
+ void addSet (const OtherSetType& setToAddFrom,
+ int startIndex = 0,
+ int numElementsToAdd = -1) noexcept
+ {
+ const typename OtherSetType::ScopedLockType lock1 (setToAddFrom.getLock());
+
+ {
+ const ScopedLockType lock2 (getLock());
+ bassert (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
+ 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
+ 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.
+ */
+ void swapWith (SortedSet& otherSet) noexcept
+ {
+ data.swapWithArray (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 data;
+};
+
+#if BEAST_MSVC
+ #pragma warning (pop)
+#endif
+
+#endif // BEAST_SORTEDSET_BEASTHEADER
diff --git a/modules/beast_core/containers/beast_SparseSet.h b/modules/beast_core/containers/beast_SparseSet.h
new file mode 100644
index 00000000..129c26d6
--- /dev/null
+++ b/modules/beast_core/containers/beast_SparseSet.h
@@ -0,0 +1,296 @@
+//------------------------------------------------------------------------------
+/*
+ This file is part of Beast: https://github.com/vinniefalco/Beast
+ Copyright 2013, Vinnie Falco
+
+ Portions of this file are from JUCE.
+ Copyright (c) 2013 - Raw Material Software Ltd.
+ Please visit http://www.juce.com
+
+ Permission to use, copy, modify, and/or distribute this software 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.
+*/
+//==============================================================================
+
+#ifndef BEAST_SPARSESET_BEASTHEADER
+#define BEAST_SPARSESET_BEASTHEADER
+
+#include "../maths/beast_Range.h"
+#include "../threads/beast_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 SparseSet
+{
+public:
+ //==============================================================================
+ /** Creates a new empty set. */
+ SparseSet()
+ {
+ }
+
+ /** Creates a copy of another SparseSet. */
+ SparseSet (const SparseSet& 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 getRange (const int rangeIndex) const
+ {
+ if (isPositiveAndBelow (rangeIndex, getNumRanges()))
+ return Range (values.getUnchecked (rangeIndex << 1),
+ values.getUnchecked ((rangeIndex << 1) + 1));
+
+ return Range();
+ }
+
+ /** Returns the range between the lowest and highest values in the set.
+ @see getRange
+ */
+ Range getTotalRange() const
+ {
+ if (values.size() > 0)
+ {
+ bassert ((values.size() & 1) == 0);
+ return Range (values.getUnchecked (0),
+ values.getUnchecked (values.size() - 1));
+ }
+
+ return Range();
+ }
+
+ //==============================================================================
+ /** Adds a range of contiguous values to the set.
+ e.g. addRange (Range \ (10, 14)) will add (10, 11, 12, 13) to the set.
+ */
+ void addRange (const Range range)
+ {
+ bassert (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\ (10, 14)) will remove (10, 11, 12, 13) from the set.
+ */
+ void removeRange (const Range rangeToRemove)
+ {
+ bassert (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 (bmin (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 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 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 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& other) noexcept
+ {
+ return values == other.values;
+ }
+
+ bool operator!= (const SparseSet& other) noexcept
+ {
+ return values != other.values;
+ }
+
+private:
+ //==============================================================================
+ // alternating start/end values of ranges of values that are present.
+ Array values;
+
+ void simplify()
+ {
+ bassert ((values.size() & 1) == 0);
+
+ for (int i = values.size(); --i > 0;)
+ if (values.getUnchecked(i) == values.getUnchecked (i - 1))
+ values.removeRange (--i, 2);
+ }
+};
+
+
+
+#endif // BEAST_SPARSESET_BEASTHEADER
diff --git a/modules/beast_core/containers/beast_Variant.cpp b/modules/beast_core/containers/beast_Variant.cpp
new file mode 100644
index 00000000..8dd827be
--- /dev/null
+++ b/modules/beast_core/containers/beast_Variant.cpp
@@ -0,0 +1,704 @@
+//------------------------------------------------------------------------------
+/*
+ This file is part of Beast: https://github.com/vinniefalco/Beast
+ Copyright 2013, Vinnie Falco
+
+ Portions of this file are from JUCE.
+ Copyright (c) 2013 - Raw Material Software Ltd.
+ Please visit http://www.juce.com
+
+ Permission to use, copy, modify, and/or distribute this software 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.
+*/
+//==============================================================================
+
+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* 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::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 ? (beast_wchar) '1' : (beast_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 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 (data.stringValue); }
+ static inline String* getString (ValueUnion& data) noexcept { return reinterpret_cast (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 (*(source.arrayValue)); }
+
+ String toString (const ValueUnion&) const { return "[Array]"; }
+ bool isArray() const noexcept { return true; }
+ Array* toArray (const ValueUnion& data) const noexcept { return data.arrayValue; }
+
+ bool equals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) const noexcept
+ {
+ const Array* 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& v) : type (&VariantType_Array::instance) { value.arrayValue = new Array (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::getArray() const noexcept { return type->toArray (value); }
+MemoryBlock* var::getBinaryData() const noexcept { return type->toBinary (value); }
+DynamicObject* var::getDynamicObject() const noexcept { return dynamic_cast (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& 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 BEAST_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 (v));
+}
+
+var::var (MemoryBlock&& v) : type (&VariantType_Binary::instance)
+{
+ value.binaryValue = new MemoryBlock (static_cast (v));
+}
+
+var& var::operator= (String&& v)
+{
+ type->cleanUp (value);
+ type = &VariantType_String::instance;
+ new (value.stringValue) String (static_cast (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
+{
+ bassert (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* const array = getArray())
+ return array->size();
+
+ return 0;
+}
+
+const var& var::operator[] (int arrayIndex) const
+{
+ const Array* const array = getArray();
+
+ // When using this method, the var must actually be an array, and the index
+ // must be in-range!
+ bassert (array != nullptr && isPositiveAndBelow (arrayIndex, array->size()));
+
+ return array->getReference (arrayIndex);
+}
+
+var& var::operator[] (int arrayIndex)
+{
+ const Array* const array = getArray();
+
+ // When using this method, the var must actually be an array, and the index
+ // must be in-range!
+ bassert (array != nullptr && isPositiveAndBelow (arrayIndex, array->size()));
+
+ return array->getReference (arrayIndex);
+}
+
+Array* var::convertToArray()
+{
+ Array* array = getArray();
+
+ if (array == nullptr)
+ {
+ const Array 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* 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* 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* 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;
+}
diff --git a/modules/beast_core/containers/beast_Variant.h b/modules/beast_core/containers/beast_Variant.h
new file mode 100644
index 00000000..460d526d
--- /dev/null
+++ b/modules/beast_core/containers/beast_Variant.h
@@ -0,0 +1,302 @@
+//------------------------------------------------------------------------------
+/*
+ This file is part of Beast: https://github.com/vinniefalco/Beast
+ Copyright 2013, Vinnie Falco
+
+ Portions of this file are from JUCE.
+ Copyright (c) 2013 - Raw Material Software Ltd.
+ Please visit http://www.juce.com
+
+ Permission to use, copy, modify, and/or distribute this software 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.
+*/
+//==============================================================================
+
+#ifndef BEAST_VARIANT_BEASTHEADER
+#define BEAST_VARIANT_BEASTHEADER
+
+#include "../text/beast_Identifier.h"
+#include "../streams/beast_OutputStream.h"
+#include "../streams/beast_InputStream.h"
+#include "../containers/beast_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 BEAST_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& 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& value);
+ var& operator= (ReferenceCountedObject* object);
+ var& operator= (MethodFunction method);
+
+ #if BEAST_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* 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\.
+ */
+ 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& 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\.
+ */
+ 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\.
+ */
+ 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\.
+ */
+ 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\.
+ */
+ 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* arrayValue;
+ MemoryBlock* binaryValue;
+ MethodFunction methodValue;
+ };
+
+ const VariantType* type;
+ ValueUnion value;
+
+ Array* 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 // BEAST_VARIANT_BEASTHEADER
diff --git a/modules/beast_core/files/beast_DirectoryIterator.cpp b/modules/beast_core/files/beast_DirectoryIterator.cpp
new file mode 100644
index 00000000..7d182941
--- /dev/null
+++ b/modules/beast_core/files/beast_DirectoryIterator.cpp
@@ -0,0 +1,154 @@
+//------------------------------------------------------------------------------
+/*
+ This file is part of Beast: https://github.com/vinniefalco/Beast
+ Copyright 2013, Vinnie Falco
+
+ Portions of this file are from JUCE.
+ Copyright (c) 2013 - Raw Material Software Ltd.
+ Please visit http://www.juce.com
+
+ Permission to use, copy, modify, and/or distribute this software 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.
+*/
+//==============================================================================
+
+static StringArray parseWildcards (const String& pattern)
+{
+ StringArray s;
+ s.addTokens (pattern, ";,", "\"'");
+ s.trim();
+ s.removeEmptyStrings();
+ return s;
+}
+
+static bool 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;
+}
+
+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!
+ bassert ((type & (File::findFiles | File::findDirectories)) != 0);
+ bassert (type > 0 && type <= 7);
+}
+
+DirectoryIterator::~DirectoryIterator()
+{
+}
+
+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!
+ bassert (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;
+}
diff --git a/modules/beast_core/files/beast_DirectoryIterator.h b/modules/beast_core/files/beast_DirectoryIterator.h
new file mode 100644
index 00000000..3ba068a8
--- /dev/null
+++ b/modules/beast_core/files/beast_DirectoryIterator.h
@@ -0,0 +1,154 @@
+//------------------------------------------------------------------------------
+/*
+ This file is part of Beast: https://github.com/vinniefalco/Beast
+ Copyright 2013, Vinnie Falco
+
+ Portions of this file are from JUCE.
+ Copyright (c) 2013 - Raw Material Software Ltd.
+ Please visit http://www.juce.com
+
+ Permission to use, copy, modify, and/or distribute this software 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.
+*/
+//==============================================================================
+
+#ifndef BEAST_DIRECTORYITERATOR_BEASTHEADER
+#define BEAST_DIRECTORYITERATOR_BEASTHEADER
+
+#include "beast_File.h"
+#include "../memory/beast_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 BEAST_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;
+ ScopedPointer pimpl;
+
+ BEAST_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (NativeIterator)
+ };
+
+ friend class ScopedPointer;
+ StringArray wildCards;
+ NativeIterator fileFinder;
+ String wildCard, path;
+ int index;
+ mutable int totalNumFiles;
+ const int whatToLookFor;
+ const bool isRecursive;
+ bool hasBeenAdvanced;
+ ScopedPointer subIterator;
+ File currentFile;
+
+ BEAST_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DirectoryIterator)
+};
+
+#endif // BEAST_DIRECTORYITERATOR_BEASTHEADER
diff --git a/modules/beast_core/files/beast_File.cpp b/modules/beast_core/files/beast_File.cpp
new file mode 100644
index 00000000..a4235f43
--- /dev/null
+++ b/modules/beast_core/files/beast_File.cpp
@@ -0,0 +1,1086 @@
+//------------------------------------------------------------------------------
+/*
+ This file is part of Beast: https://github.com/vinniefalco/Beast
+ Copyright 2013, Vinnie Falco
+
+ Portions of this file are from JUCE.
+ Copyright (c) 2013 - Raw Material Software Ltd.
+ Please visit http://www.juce.com
+
+ Permission to use, copy, modify, and/or distribute this software 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.
+*/
+//==============================================================================
+
+File::File (const String& fullPathName)
+ : fullPath (parseAbsolutePath (fullPathName))
+{
+}
+
+File File::createFileWithoutCheckingPath (const String& path) noexcept
+{
+ File f;
+ f.fullPath = path;
+ return f;
+}
+
+File::File (const File& other)
+ : fullPath (other.fullPath)
+{
+}
+
+File& File::operator= (const String& newPath)
+{
+ fullPath = parseAbsolutePath (newPath);
+ return *this;
+}
+
+File& File::operator= (const File& other)
+{
+ fullPath = other.fullPath;
+ return *this;
+}
+
+#if BEAST_COMPILER_SUPPORTS_MOVE_SEMANTICS
+File::File (File&& other) noexcept
+ : fullPath (static_cast (other.fullPath))
+{
+}
+
+File& File::operator= (File&& other) noexcept
+{
+ fullPath = static_cast (other.fullPath);
+ return *this;
+}
+#endif
+
+const File File::nonexistent;
+
+
+//==============================================================================
+String File::parseAbsolutePath (const String& p)
+{
+ if (p.isEmpty())
+ return String::empty;
+
+#if BEAST_WINDOWS
+ // Windows..
+ String path (p.replaceCharacter ('/', '\\'));
+
+ if (path.startsWithChar (separator))
+ {
+ if (path[1] != separator)
+ {
+ /* When you supply a raw string to the File object constructor, it must be an absolute path.
+ If you're trying to parse a string that may be either a relative path or an absolute path,
+ you MUST provide a context against which the partial path can be evaluated - you can do
+ this by simply using File::getChildFile() instead of the File constructor. E.g. saying
+ "File::getCurrentWorkingDirectory().getChildFile (myUnknownPath)" would return an absolute
+ path if that's what was supplied, or would evaluate a partial path relative to the CWD.
+ */
+ jassertfalse;
+
+ path = File::getCurrentWorkingDirectory().getFullPathName().substring (0, 2) + path;
+ }
+ }
+ else if (! path.containsChar (':'))
+ {
+ /* When you supply a raw string to the File object constructor, it must be an absolute path.
+ If you're trying to parse a string that may be either a relative path or an absolute path,
+ you MUST provide a context against which the partial path can be evaluated - you can do
+ this by simply using File::getChildFile() instead of the File constructor. E.g. saying
+ "File::getCurrentWorkingDirectory().getChildFile (myUnknownPath)" would return an absolute
+ path if that's what was supplied, or would evaluate a partial path relative to the CWD.
+ */
+ jassertfalse;
+
+ return File::getCurrentWorkingDirectory().getChildFile (path).getFullPathName();
+ }
+#else
+ // Mac or Linux..
+
+ // Yes, I know it's legal for a unix pathname to contain a backslash, but this assertion is here
+ // to catch anyone who's trying to run code that was written on Windows with hard-coded path names.
+ // If that's why you've ended up here, use File::getChildFile() to build your paths instead.
+ bassert ((! p.containsChar ('\\')) || (p.indexOfChar ('/') >= 0 && p.indexOfChar ('/') < p.indexOfChar ('\\')));
+
+ String path (p);
+
+ if (path.startsWithChar ('~'))
+ {
+ if (path[1] == separator || path[1] == 0)
+ {
+ // expand a name of the form "~/abc"
+ path = File::getSpecialLocation (File::userHomeDirectory).getFullPathName()
+ + path.substring (1);
+ }
+ else
+ {
+ // expand a name of type "~dave/abc"
+ const String userName (path.substring (1).upToFirstOccurrenceOf ("/", false, false));
+
+ if (struct passwd* const pw = getpwnam (userName.toUTF8()))
+ path = addTrailingSeparator (pw->pw_dir) + path.fromFirstOccurrenceOf ("/", false, false);
+ }
+ }
+ else if (! path.startsWithChar (separator))
+ {
+ #if BEAST_DEBUG || BEAST_LOG_ASSERTIONS
+ if (! (path.startsWith ("./") || path.startsWith ("../")))
+ {
+ /* When you supply a raw string to the File object constructor, it must be an absolute path.
+ If you're trying to parse a string that may be either a relative path or an absolute path,
+ you MUST provide a context against which the partial path can be evaluated - you can do
+ this by simply using File::getChildFile() instead of the File constructor. E.g. saying
+ "File::getCurrentWorkingDirectory().getChildFile (myUnknownPath)" would return an absolute
+ path if that's what was supplied, or would evaluate a partial path relative to the CWD.
+ */
+ jassertfalse;
+
+ #if BEAST_LOG_ASSERTIONS
+ Logger::writeToLog ("Illegal absolute path: " + path);
+ #endif
+ }
+ #endif
+
+ return File::getCurrentWorkingDirectory().getChildFile (path).getFullPathName();
+ }
+#endif
+
+ while (path.endsWithChar (separator) && path != separatorString) // careful not to turn a single "/" into an empty string.
+ path = path.dropLastCharacters (1);
+
+ return path;
+}
+
+String File::addTrailingSeparator (const String& path)
+{
+ return path.endsWithChar (separator) ? path
+ : path + separator;
+}
+
+//==============================================================================
+#if BEAST_LINUX
+ #define NAMES_ARE_CASE_SENSITIVE 1
+#endif
+
+bool File::areFileNamesCaseSensitive()
+{
+ #if NAMES_ARE_CASE_SENSITIVE
+ return true;
+ #else
+ return false;
+ #endif
+}
+
+static int compareFilenames (const String& name1, const String& name2) noexcept
+{
+ #if NAMES_ARE_CASE_SENSITIVE
+ return name1.compare (name2);
+ #else
+ return name1.compareIgnoreCase (name2);
+ #endif
+}
+
+bool File::operator== (const File& other) const { return compareFilenames (fullPath, other.fullPath) == 0; }
+bool File::operator!= (const File& other) const { return compareFilenames (fullPath, other.fullPath) != 0; }
+bool File::operator< (const File& other) const { return compareFilenames (fullPath, other.fullPath) < 0; }
+bool File::operator> (const File& other) const { return compareFilenames (fullPath, other.fullPath) > 0; }
+
+//==============================================================================
+bool File::setReadOnly (const bool shouldBeReadOnly,
+ const bool applyRecursively) const
+{
+ bool worked = true;
+
+ if (applyRecursively && isDirectory())
+ {
+ Array subFiles;
+ findChildFiles (subFiles, File::findFilesAndDirectories, false);
+
+ for (int i = subFiles.size(); --i >= 0;)
+ worked = subFiles.getReference(i).setReadOnly (shouldBeReadOnly, true) && worked;
+ }
+
+ return setFileReadOnlyInternal (shouldBeReadOnly) && worked;
+}
+
+bool File::deleteRecursively() const
+{
+ bool worked = true;
+
+ if (isDirectory())
+ {
+ Array subFiles;
+ findChildFiles (subFiles, File::findFilesAndDirectories, false);
+
+ for (int i = subFiles.size(); --i >= 0;)
+ worked = subFiles.getReference(i).deleteRecursively() && worked;
+ }
+
+ return deleteFile() && worked;
+}
+
+bool File::moveFileTo (const File& newFile) const
+{
+ if (newFile.fullPath == fullPath)
+ return true;
+
+ if (! exists())
+ return false;
+
+ #if ! NAMES_ARE_CASE_SENSITIVE
+ if (*this != newFile)
+ #endif
+ if (! newFile.deleteFile())
+ return false;
+
+ return moveInternal (newFile);
+}
+
+bool File::copyFileTo (const File& newFile) const
+{
+ return (*this == newFile)
+ || (exists() && newFile.deleteFile() && copyInternal (newFile));
+}
+
+bool File::copyDirectoryTo (const File& newDirectory) const
+{
+ if (isDirectory() && newDirectory.createDirectory())
+ {
+ Array subFiles;
+ findChildFiles (subFiles, File::findFiles, false);
+
+ for (int i = 0; i < subFiles.size(); ++i)
+ if (! subFiles.getReference(i).copyFileTo (newDirectory.getChildFile (subFiles.getReference(i).getFileName())))
+ return false;
+
+ subFiles.clear();
+ findChildFiles (subFiles, File::findDirectories, false);
+
+ for (int i = 0; i < subFiles.size(); ++i)
+ if (! subFiles.getReference(i).copyDirectoryTo (newDirectory.getChildFile (subFiles.getReference(i).getFileName())))
+ return false;
+
+ return true;
+ }
+
+ return false;
+}
+
+//==============================================================================
+String File::getPathUpToLastSlash() const
+{
+ const int lastSlash = fullPath.lastIndexOfChar (separator);
+
+ if (lastSlash > 0)
+ return fullPath.substring (0, lastSlash);
+
+ if (lastSlash == 0)
+ return separatorString;
+
+ return fullPath;
+}
+
+File File::getParentDirectory() const
+{
+ File f;
+ f.fullPath = getPathUpToLastSlash();
+ return f;
+}
+
+//==============================================================================
+String File::getFileName() const
+{
+ return fullPath.substring (fullPath.lastIndexOfChar (separator) + 1);
+}
+
+String File::getFileNameWithoutExtension() const
+{
+ const int lastSlash = fullPath.lastIndexOfChar (separator) + 1;
+ const int lastDot = fullPath.lastIndexOfChar ('.');
+
+ if (lastDot > lastSlash)
+ return fullPath.substring (lastSlash, lastDot);
+
+ return fullPath.substring (lastSlash);
+}
+
+bool File::isAChildOf (const File& potentialParent) const
+{
+ if (potentialParent == File::nonexistent)
+ return false;
+
+ const String ourPath (getPathUpToLastSlash());
+
+ if (compareFilenames (potentialParent.fullPath, ourPath) == 0)
+ return true;
+
+ if (potentialParent.fullPath.length() >= ourPath.length())
+ return false;
+
+ return getParentDirectory().isAChildOf (potentialParent);
+}
+
+int File::hashCode() const { return fullPath.hashCode(); }
+int64 File::hashCode64() const { return fullPath.hashCode64(); }
+
+//==============================================================================
+bool File::isAbsolutePath (const String& path)
+{
+ return path.startsWithChar (separator)
+ #if BEAST_WINDOWS
+ || (path.isNotEmpty() && path[1] == ':');
+ #else
+ || path.startsWithChar ('~');
+ #endif
+}
+
+File File::getChildFile (String relativePath) const
+{
+ if (isAbsolutePath (relativePath))
+ return File (relativePath);
+
+ String path (fullPath);
+
+ // It's relative, so remove any ../ or ./ bits at the start..
+ if (relativePath[0] == '.')
+ {
+ #if BEAST_WINDOWS
+ relativePath = relativePath.replaceCharacter ('/', '\\');
+ #endif
+
+ while (relativePath[0] == '.')
+ {
+ const beast_wchar secondChar = relativePath[1];
+
+ if (secondChar == '.')
+ {
+ const beast_wchar thirdChar = relativePath[2];
+
+ if (thirdChar == 0 || thirdChar == separator)
+ {
+ const int lastSlash = path.lastIndexOfChar (separator);
+ if (lastSlash >= 0)
+ path = path.substring (0, lastSlash);
+
+ relativePath = relativePath.substring (3);
+ }
+ else
+ {
+ break;
+ }
+ }
+ else if (secondChar == separator)
+ {
+ relativePath = relativePath.substring (2);
+ }
+ else
+ {
+ break;
+ }
+ }
+ }
+
+ return File (addTrailingSeparator (path) + relativePath);
+}
+
+File File::getSiblingFile (const String& fileName) const
+{
+ return getParentDirectory().getChildFile (fileName);
+}
+
+//==============================================================================
+String File::descriptionOfSizeInBytes (const int64 bytes)
+{
+ const char* suffix;
+ double divisor = 0;
+
+ if (bytes == 1) { suffix = " byte"; }
+ else if (bytes < 1024) { suffix = " bytes"; }
+ else if (bytes < 1024 * 1024) { suffix = " KB"; divisor = 1024.0; }
+ else if (bytes < 1024 * 1024 * 1024) { suffix = " MB"; divisor = 1024.0 * 1024.0; }
+ else { suffix = " GB"; divisor = 1024.0 * 1024.0 * 1024.0; }
+
+ return (divisor > 0 ? String (bytes / divisor, 1) : String (bytes)) + suffix;
+}
+
+//==============================================================================
+Result File::create() const
+{
+ if (exists())
+ return Result::ok();
+
+ const File parentDir (getParentDirectory());
+
+ if (parentDir == *this)
+ return Result::fail ("Cannot create parent directory");
+
+ Result r (parentDir.createDirectory());
+
+ if (r.wasOk())
+ {
+ FileOutputStream fo (*this, 8);
+ r = fo.getStatus();
+ }
+
+ return r;
+}
+
+Result File::createDirectory() const
+{
+ if (isDirectory())
+ return Result::ok();
+
+ const File parentDir (getParentDirectory());
+
+ if (parentDir == *this)
+ return Result::fail ("Cannot create parent directory");
+
+ Result r (parentDir.createDirectory());
+
+ if (r.wasOk())
+ r = createDirectoryInternal (fullPath.trimCharactersAtEnd (separatorString));
+
+ return r;
+}
+
+//==============================================================================
+Time File::getLastModificationTime() const { int64 m, a, c; getFileTimesInternal (m, a, c); return Time (m); }
+Time File::getLastAccessTime() const { int64 m, a, c; getFileTimesInternal (m, a, c); return Time (a); }
+Time File::getCreationTime() const { int64 m, a, c; getFileTimesInternal (m, a, c); return Time (c); }
+
+bool File::setLastModificationTime (Time t) const { return setFileTimesInternal (t.toMilliseconds(), 0, 0); }
+bool File::setLastAccessTime (Time t) const { return setFileTimesInternal (0, t.toMilliseconds(), 0); }
+bool File::setCreationTime (Time t) const { return setFileTimesInternal (0, 0, t.toMilliseconds()); }
+
+//==============================================================================
+bool File::loadFileAsData (MemoryBlock& destBlock) const
+{
+ if (! existsAsFile())
+ return false;
+
+ FileInputStream in (*this);
+ return in.openedOk() && getSize() == in.readIntoMemoryBlock (destBlock);
+}
+
+String File::loadFileAsString() const
+{
+ if (! existsAsFile())
+ return String::empty;
+
+ FileInputStream in (*this);
+ return in.openedOk() ? in.readEntireStreamAsString()
+ : String::empty;
+}
+
+void File::readLines (StringArray& destLines) const
+{
+ destLines.addLines (loadFileAsString());
+}
+
+//==============================================================================
+int File::findChildFiles (Array& results,
+ const int whatToLookFor,
+ const bool searchRecursively,
+ const String& wildCardPattern) const
+{
+ DirectoryIterator di (*this, searchRecursively, wildCardPattern, whatToLookFor);
+
+ int total = 0;
+ while (di.next())
+ {
+ results.add (di.getFile());
+ ++total;
+ }
+
+ return total;
+}
+
+int File::getNumberOfChildFiles (const int whatToLookFor, const String& wildCardPattern) const
+{
+ DirectoryIterator di (*this, false, wildCardPattern, whatToLookFor);
+
+ int total = 0;
+ while (di.next())
+ ++total;
+
+ return total;
+}
+
+bool File::containsSubDirectories() const
+{
+ if (! isDirectory())
+ return false;
+
+ DirectoryIterator di (*this, false, "*", findDirectories);
+ return di.next();
+}
+
+//==============================================================================
+File File::getNonexistentChildFile (const String& suggestedPrefix,
+ const String& suffix,
+ bool putNumbersInBrackets) const
+{
+ File f (getChildFile (suggestedPrefix + suffix));
+
+ if (f.exists())
+ {
+ int number = 1;
+ String prefix (suggestedPrefix);
+
+ // remove any bracketed numbers that may already be on the end..
+ if (prefix.trim().endsWithChar (')'))
+ {
+ putNumbersInBrackets = true;
+
+ const int openBracks = prefix.lastIndexOfChar ('(');
+ const int closeBracks = prefix.lastIndexOfChar (')');
+
+ if (openBracks > 0
+ && closeBracks > openBracks
+ && prefix.substring (openBracks + 1, closeBracks).containsOnly ("0123456789"))
+ {
+ number = prefix.substring (openBracks + 1, closeBracks).getIntValue();
+ prefix = prefix.substring (0, openBracks);
+ }
+ }
+
+ // also use brackets if it ends in a digit.
+ putNumbersInBrackets = putNumbersInBrackets
+ || CharacterFunctions::isDigit (prefix.getLastCharacter());
+
+ do
+ {
+ String newName (prefix);
+
+ if (putNumbersInBrackets)
+ newName << '(' << ++number << ')';
+ else
+ newName << ++number;
+
+ f = getChildFile (newName + suffix);
+
+ } while (f.exists());
+ }
+
+ return f;
+}
+
+File File::getNonexistentSibling (const bool putNumbersInBrackets) const
+{
+ if (! exists())
+ return *this;
+
+ return getParentDirectory().getNonexistentChildFile (getFileNameWithoutExtension(),
+ getFileExtension(),
+ putNumbersInBrackets);
+}
+
+//==============================================================================
+String File::getFileExtension() const
+{
+ const int indexOfDot = fullPath.lastIndexOfChar ('.');
+
+ if (indexOfDot > fullPath.lastIndexOfChar (separator))
+ return fullPath.substring (indexOfDot);
+
+ return String::empty;
+}
+
+bool File::hasFileExtension (const String& possibleSuffix) const
+{
+ if (possibleSuffix.isEmpty())
+ return fullPath.lastIndexOfChar ('.') <= fullPath.lastIndexOfChar (separator);
+
+ const int semicolon = possibleSuffix.indexOfChar (0, ';');
+
+ if (semicolon >= 0)
+ {
+ return hasFileExtension (possibleSuffix.substring (0, semicolon).trimEnd())
+ || hasFileExtension (possibleSuffix.substring (semicolon + 1).trimStart());
+ }
+ else
+ {
+ if (fullPath.endsWithIgnoreCase (possibleSuffix))
+ {
+ if (possibleSuffix.startsWithChar ('.'))
+ return true;
+
+ const int dotPos = fullPath.length() - possibleSuffix.length() - 1;
+
+ if (dotPos >= 0)
+ return fullPath [dotPos] == '.';
+ }
+ }
+
+ return false;
+}
+
+File File::withFileExtension (const String& newExtension) const
+{
+ if (fullPath.isEmpty())
+ return File::nonexistent;
+
+ String filePart (getFileName());
+
+ const int i = filePart.lastIndexOfChar ('.');
+ if (i >= 0)
+ filePart = filePart.substring (0, i);
+
+ if (newExtension.isNotEmpty() && ! newExtension.startsWithChar ('.'))
+ filePart << '.';
+
+ return getSiblingFile (filePart + newExtension);
+}
+
+//==============================================================================
+bool File::startAsProcess (const String& parameters) const
+{
+ return exists() && Process::openDocument (fullPath, parameters);
+}
+
+//==============================================================================
+FileInputStream* File::createInputStream() const
+{
+ ScopedPointer fin (new FileInputStream (*this));
+
+ if (fin->openedOk())
+ return fin.release();
+
+ return nullptr;
+}
+
+FileOutputStream* File::createOutputStream (const int bufferSize) const
+{
+ ScopedPointer out (new FileOutputStream (*this, bufferSize));
+
+ return out->failedToOpen() ? nullptr
+ : out.release();
+}
+
+//==============================================================================
+bool File::appendData (const void* const dataToAppend,
+ const size_t numberOfBytes) const
+{
+ bassert (((ssize_t) numberOfBytes) >= 0);
+
+ if (numberOfBytes == 0)
+ return true;
+
+ FileOutputStream out (*this, 8192);
+ return out.openedOk() && out.write (dataToAppend, numberOfBytes);
+}
+
+bool File::replaceWithData (const void* const dataToWrite,
+ const size_t numberOfBytes) const
+{
+ if (numberOfBytes == 0)
+ return deleteFile();
+
+ TemporaryFile tempFile (*this, TemporaryFile::useHiddenFile);
+ tempFile.getFile().appendData (dataToWrite, numberOfBytes);
+ return tempFile.overwriteTargetFileWithTemporary();
+}
+
+bool File::appendText (const String& text,
+ const bool asUnicode,
+ const bool writeUnicodeHeaderBytes) const
+{
+ FileOutputStream out (*this);
+
+ if (out.failedToOpen())
+ return false;
+
+ out.writeText (text, asUnicode, writeUnicodeHeaderBytes);
+ return true;
+}
+
+bool File::replaceWithText (const String& textToWrite,
+ const bool asUnicode,
+ const bool writeUnicodeHeaderBytes) const
+{
+ TemporaryFile tempFile (*this, TemporaryFile::useHiddenFile);
+ tempFile.getFile().appendText (textToWrite, asUnicode, writeUnicodeHeaderBytes);
+ return tempFile.overwriteTargetFileWithTemporary();
+}
+
+bool File::hasIdenticalContentTo (const File& other) const
+{
+ if (other == *this)
+ return true;
+
+ if (getSize() == other.getSize() && existsAsFile() && other.existsAsFile())
+ {
+ FileInputStream in1 (*this), in2 (other);
+
+ if (in1.openedOk() && in2.openedOk())
+ {
+ const int bufferSize = 4096;
+ HeapBlock buffer1 (bufferSize), buffer2 (bufferSize);
+
+ for (;;)
+ {
+ const int num1 = in1.read (buffer1, bufferSize);
+ const int num2 = in2.read (buffer2, bufferSize);
+
+ if (num1 != num2)
+ break;
+
+ if (num1 <= 0)
+ return true;
+
+ if (memcmp (buffer1, buffer2, (size_t) num1) != 0)
+ break;
+ }
+ }
+ }
+
+ return false;
+}
+
+//==============================================================================
+String File::createLegalPathName (const String& original)
+{
+ String s (original);
+ String start;
+
+ if (s[1] == ':')
+ {
+ start = s.substring (0, 2);
+ s = s.substring (2);
+ }
+
+ return start + s.removeCharacters ("\"#@,;:<>*^|?")
+ .substring (0, 1024);
+}
+
+String File::createLegalFileName (const String& original)
+{
+ String s (original.removeCharacters ("\"#@,;:<>*^|?\\/"));
+
+ const int maxLength = 128; // only the length of the filename, not the whole path
+ const int len = s.length();
+
+ if (len > maxLength)
+ {
+ const int lastDot = s.lastIndexOfChar ('.');
+
+ if (lastDot > bmax (0, len - 12))
+ {
+ s = s.substring (0, maxLength - (len - lastDot))
+ + s.substring (lastDot);
+ }
+ else
+ {
+ s = s.substring (0, maxLength);
+ }
+ }
+
+ return s;
+}
+
+//==============================================================================
+static int countNumberOfSeparators (String::CharPointerType s)
+{
+ int num = 0;
+
+ for (;;)
+ {
+ const beast_wchar c = s.getAndAdvance();
+
+ if (c == 0)
+ break;
+
+ if (c == File::separator)
+ ++num;
+ }
+
+ return num;
+}
+
+String File::getRelativePathFrom (const File& dir) const
+{
+ String thisPath (fullPath);
+
+ while (thisPath.endsWithChar (separator))
+ thisPath = thisPath.dropLastCharacters (1);
+
+ String dirPath (addTrailingSeparator (dir.existsAsFile() ? dir.getParentDirectory().getFullPathName()
+ : dir.fullPath));
+
+ int commonBitLength = 0;
+ String::CharPointerType thisPathAfterCommon (thisPath.getCharPointer());
+ String::CharPointerType dirPathAfterCommon (dirPath.getCharPointer());
+
+ {
+ String::CharPointerType thisPathIter (thisPath.getCharPointer());
+ String::CharPointerType dirPathIter (dirPath.getCharPointer());
+
+ for (int i = 0;;)
+ {
+ const beast_wchar c1 = thisPathIter.getAndAdvance();
+ const beast_wchar c2 = dirPathIter.getAndAdvance();
+
+ #if NAMES_ARE_CASE_SENSITIVE
+ if (c1 != c2
+ #else
+ if ((c1 != c2 && CharacterFunctions::toLowerCase (c1) != CharacterFunctions::toLowerCase (c2))
+ #endif
+ || c1 == 0)
+ break;
+
+ ++i;
+
+ if (c1 == separator)
+ {
+ thisPathAfterCommon = thisPathIter;
+ dirPathAfterCommon = dirPathIter;
+ commonBitLength = i;
+ }
+ }
+ }
+
+ // if the only common bit is the root, then just return the full path..
+ if (commonBitLength == 0 || (commonBitLength == 1 && thisPath[1] == separator))
+ return fullPath;
+
+ const int numUpDirectoriesNeeded = countNumberOfSeparators (dirPathAfterCommon);
+
+ if (numUpDirectoriesNeeded == 0)
+ return thisPathAfterCommon;
+
+ #if BEAST_WINDOWS
+ String s (String::repeatedString ("..\\", numUpDirectoriesNeeded));
+ #else
+ String s (String::repeatedString ("../", numUpDirectoriesNeeded));
+ #endif
+ s.appendCharPointer (thisPathAfterCommon);
+ return s;
+}
+
+//==============================================================================
+File File::createTempFile (const String& fileNameEnding)
+{
+ const File tempFile (getSpecialLocation (tempDirectory)
+ .getChildFile ("temp_" + String::toHexString (Random::getSystemRandom().nextInt()))
+ .withFileExtension (fileNameEnding));
+
+ if (tempFile.exists())
+ return createTempFile (fileNameEnding);
+
+ return tempFile;
+}
+
+//==============================================================================
+MemoryMappedFile::MemoryMappedFile (const File& file, MemoryMappedFile::AccessMode mode)
+ : address (nullptr), range (0, file.getSize()), fileHandle (0)
+{
+ openInternal (file, mode);
+}
+
+MemoryMappedFile::MemoryMappedFile (const File& file, const Range& fileRange, AccessMode mode)
+ : address (nullptr), range (fileRange.getIntersectionWith (Range (0, file.getSize()))), fileHandle (0)
+{
+ openInternal (file, mode);
+}
+
+
+//==============================================================================
+#if BEAST_UNIT_TESTS
+
+class FileTests : public UnitTest
+{
+public:
+ FileTests() : UnitTest ("Files") {}
+
+ void runTest()
+ {
+ beginTest ("Reading");
+
+ const File home (File::getSpecialLocation (File::userHomeDirectory));
+ const File temp (File::getSpecialLocation (File::tempDirectory));
+
+ expect (! File::nonexistent.exists());
+ expect (home.isDirectory());
+ expect (home.exists());
+ expect (! home.existsAsFile());
+ expect (File::getSpecialLocation (File::userDocumentsDirectory).isDirectory());
+ expect (File::getSpecialLocation (File::userApplicationDataDirectory).isDirectory());
+ expect (File::getSpecialLocation (File::currentExecutableFile).exists());
+ expect (File::getSpecialLocation (File::currentApplicationFile).exists());
+ expect (File::getSpecialLocation (File::invokedExecutableFile).exists());
+ expect (home.getVolumeTotalSize() > 1024 * 1024);
+ expect (home.getBytesFreeOnVolume() > 0);
+ expect (! home.isHidden());
+ expect (home.isOnHardDisk());
+ expect (! home.isOnCDRomDrive());
+ expect (File::getCurrentWorkingDirectory().exists());
+ expect (home.setAsCurrentWorkingDirectory());
+ expect (File::getCurrentWorkingDirectory() == home);
+
+ {
+ Array roots;
+ File::findFileSystemRoots (roots);
+ expect (roots.size() > 0);
+
+ int numRootsExisting = 0;
+ for (int i = 0; i < roots.size(); ++i)
+ if (roots[i].exists())
+ ++numRootsExisting;
+
+ // (on windows, some of the drives may not contain media, so as long as at least one is ok..)
+ expect (numRootsExisting > 0);
+ }
+
+ beginTest ("Writing");
+
+ File demoFolder (temp.getChildFile ("Beast UnitTests Temp Folder.folder"));
+ expect (demoFolder.deleteRecursively());
+ expect (demoFolder.createDirectory());
+ expect (demoFolder.isDirectory());
+ expect (demoFolder.getParentDirectory() == temp);
+ expect (temp.isDirectory());
+
+ {
+ Array files;
+ temp.findChildFiles (files, File::findFilesAndDirectories, false, "*");
+ expect (files.contains (demoFolder));
+ }
+
+ {
+ Array files;
+ temp.findChildFiles (files, File::findDirectories, true, "*.folder");
+ expect (files.contains (demoFolder));
+ }
+
+ File tempFile (demoFolder.getNonexistentChildFile ("test", ".txt", false));
+
+ expect (tempFile.getFileExtension() == ".txt");
+ expect (tempFile.hasFileExtension (".txt"));
+ expect (tempFile.hasFileExtension ("txt"));
+ expect (tempFile.withFileExtension ("xyz").hasFileExtension (".xyz"));
+ expect (tempFile.withFileExtension ("xyz").hasFileExtension ("abc;xyz;foo"));
+ expect (tempFile.withFileExtension ("xyz").hasFileExtension ("xyz;foo"));
+ expect (! tempFile.withFileExtension ("h").hasFileExtension ("bar;foo;xx"));
+ expect (tempFile.getSiblingFile ("foo").isAChildOf (temp));
+ expect (tempFile.hasWriteAccess());
+
+ {
+ FileOutputStream fo (tempFile);
+ fo.write ("0123456789", 10);
+ }
+
+ expect (tempFile.exists());
+ expect (tempFile.getSize() == 10);
+ expect (std::abs ((int) (tempFile.getLastModificationTime().toMilliseconds() - Time::getCurrentTime().toMilliseconds())) < 3000);
+ expectEquals (tempFile.loadFileAsString(), String ("0123456789"));
+ expect (! demoFolder.containsSubDirectories());
+
+ expectEquals (tempFile.getRelativePathFrom (demoFolder.getParentDirectory()), demoFolder.getFileName() + File::separatorString + tempFile.getFileName());
+ expectEquals (demoFolder.getParentDirectory().getRelativePathFrom (tempFile), ".." + File::separatorString + ".." + File::separatorString + demoFolder.getParentDirectory().getFileName());
+
+ expect (demoFolder.getNumberOfChildFiles (File::findFiles) == 1);
+ expect (demoFolder.getNumberOfChildFiles (File::findFilesAndDirectories) == 1);
+ expect (demoFolder.getNumberOfChildFiles (File::findDirectories) == 0);
+ demoFolder.getNonexistentChildFile ("tempFolder", "", false).createDirectory();
+ expect (demoFolder.getNumberOfChildFiles (File::findDirectories) == 1);
+ expect (demoFolder.getNumberOfChildFiles (File::findFilesAndDirectories) == 2);
+ expect (demoFolder.containsSubDirectories());
+
+ expect (tempFile.hasWriteAccess());
+ tempFile.setReadOnly (true);
+ expect (! tempFile.hasWriteAccess());
+ tempFile.setReadOnly (false);
+ expect (tempFile.hasWriteAccess());
+
+ Time t (Time::getCurrentTime());
+ tempFile.setLastModificationTime (t);
+ Time t2 = tempFile.getLastModificationTime();
+ expect (std::abs ((int) (t2.toMilliseconds() - t.toMilliseconds())) <= 1000);
+
+ {
+ MemoryBlock mb;
+ tempFile.loadFileAsData (mb);
+ expect (mb.getSize() == 10);
+ expect (mb[0] == '0');
+ }
+
+ {
+ expect (tempFile.getSize() == 10);
+ FileOutputStream fo (tempFile);
+ expect (fo.openedOk());
+
+ expect (fo.setPosition (7));
+ expect (fo.truncate().wasOk());
+ expect (tempFile.getSize() == 7);
+ fo.write ("789", 3);
+ fo.flush();
+ expect (tempFile.getSize() == 10);
+ }
+
+ beginTest ("Memory-mapped files");
+
+ {
+ MemoryMappedFile mmf (tempFile, MemoryMappedFile::readOnly);
+ expect (mmf.getSize() == 10);
+ expect (mmf.getData() != nullptr);
+ expect (memcmp (mmf.getData(), "0123456789", 10) == 0);
+ }
+
+ {
+ const File tempFile2 (tempFile.getNonexistentSibling (false));
+ expect (tempFile2.create());
+ expect (tempFile2.appendData ("xxxxxxxxxx", 10));
+
+ {
+ MemoryMappedFile mmf (tempFile2, MemoryMappedFile::readWrite);
+ expect (mmf.getSize() == 10);
+ expect (mmf.getData() != nullptr);
+ memcpy (mmf.getData(), "abcdefghij", 10);
+ }
+
+ {
+ MemoryMappedFile mmf (tempFile2, MemoryMappedFile::readWrite);
+ expect (mmf.getSize() == 10);
+ expect (mmf.getData() != nullptr);
+ expect (memcmp (mmf.getData(), "abcdefghij", 10) == 0);
+ }
+
+ expect (tempFile2.deleteFile());
+ }
+
+ beginTest ("More writing");
+
+ expect (tempFile.appendData ("abcdefghij", 10));
+ expect (tempFile.getSize() == 20);
+ expect (tempFile.replaceWithData ("abcdefghij", 10));
+ expect (tempFile.getSize() == 10);
+
+ File tempFile2 (tempFile.getNonexistentSibling (false));
+ expect (tempFile.copyFileTo (tempFile2));
+ expect (tempFile2.exists());
+ expect (tempFile2.hasIdenticalContentTo (tempFile));
+ expect (tempFile.deleteFile());
+ expect (! tempFile.exists());
+ expect (tempFile2.moveFileTo (tempFile));
+ expect (tempFile.exists());
+ expect (! tempFile2.exists());
+
+ expect (demoFolder.deleteRecursively());
+ expect (! demoFolder.exists());
+ }
+};
+
+static FileTests fileUnitTests;
+
+#endif
diff --git a/modules/beast_core/files/beast_File.h b/modules/beast_core/files/beast_File.h
new file mode 100644
index 00000000..462aeb58
--- /dev/null
+++ b/modules/beast_core/files/beast_File.h
@@ -0,0 +1,955 @@
+//------------------------------------------------------------------------------
+/*
+ This file is part of Beast: https://github.com/vinniefalco/Beast
+ Copyright 2013, Vinnie Falco
+
+ Portions of this file are from JUCE.
+ Copyright (c) 2013 - Raw Material Software Ltd.
+ Please visit http://www.juce.com
+
+ Permission to use, copy, modify, and/or distribute this software 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.
+*/
+//==============================================================================
+
+#ifndef BEAST_FILE_BEASTHEADER
+#define BEAST_FILE_BEASTHEADER
+
+#include "../containers/beast_Array.h"
+#include "../time/beast_Time.h"
+#include "../text/beast_StringArray.h"
+#include "../memory/beast_MemoryBlock.h"
+#include "../memory/beast_ScopedPointer.h"
+#include "../misc/beast_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 BEAST_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& path);
+
+ /** Creates a copy of another file object. */
+ File (const File& other);
+
+ /** 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& newFilePath);
+
+ /** Copies from another file object. */
+ File& operator= (const File& otherFile);
+
+ #if BEAST_COMPILER_SUPPORTS_MOVE_SEMANTICS
+ File (File&& otherFile) noexcept;
+ File& operator= (File&& otherFile) 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. The comparison used is
+ case-insensitve. 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 based on a relative path.
+
+ 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 ("../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 relativePath) 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& otherFile) const;
+ /** Compares the pathnames for two files. */
+ bool operator!= (const File& otherFile) const;
+ /** Compares the pathnames for two files. */
+ bool operator< (const File& otherFile) const;
+ /** Compares the pathnames for two files. */
+ bool operator> (const File& otherFile) 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& 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& 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 BEAST_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 beast_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;
+
+ //==============================================================================
+ /** Removes illegal characters from a filename.
+
+ 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.
+
+ @see createLegalPathName
+ */
+ static String createLegalFileName (const String& fileNameToFix);
+
+ /** Removes illegal characters from a pathname.
+
+ 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& path) 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 BEAST_MAC || BEAST_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 BEAST_MAC || DOXYGEN
+ /** OSX ONLY - Adds this file to the OSX dock */
+ void addToDock() const;
+ #endif
+
+ #if BEAST_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;
+
+ BEAST_LEAK_DETECTOR (File)
+};
+
+#endif // BEAST_FILE_BEASTHEADER
diff --git a/modules/beast_core/files/beast_FileInputStream.cpp b/modules/beast_core/files/beast_FileInputStream.cpp
new file mode 100644
index 00000000..bceed5ba
--- /dev/null
+++ b/modules/beast_core/files/beast_FileInputStream.cpp
@@ -0,0 +1,90 @@
+//------------------------------------------------------------------------------
+/*
+ This file is part of Beast: https://github.com/vinniefalco/Beast
+ Copyright 2013, Vinnie Falco
+
+ Portions of this file are from JUCE.
+ Copyright (c) 2013 - Raw Material Software Ltd.
+ Please visit http://www.juce.com
+
+ Permission to use, copy, modify, and/or distribute this software 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.
+*/
+//==============================================================================
+
+int64 beast_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)
+{
+ bassert (openedOk());
+ bassert (buffer != nullptr && bytesToRead >= 0);
+
+ if (needToSeek)
+ {
+ if (beast_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)
+{
+ bassert (openedOk());
+
+ if (pos != currentPosition)
+ {
+ pos = blimit ((int64) 0, getTotalLength(), pos);
+
+ needToSeek |= (currentPosition != pos);
+ currentPosition = pos;
+ }
+
+ return true;
+}
diff --git a/modules/beast_core/files/beast_FileInputStream.h b/modules/beast_core/files/beast_FileInputStream.h
new file mode 100644
index 00000000..72ee64f1
--- /dev/null
+++ b/modules/beast_core/files/beast_FileInputStream.h
@@ -0,0 +1,94 @@
+//------------------------------------------------------------------------------
+/*
+ This file is part of Beast: https://github.com/vinniefalco/Beast
+ Copyright 2013, Vinnie Falco
+
+ Portions of this file are from JUCE.
+ Copyright (c) 2013 - Raw Material Software Ltd.
+ Please visit http://www.juce.com
+
+ Permission to use, copy, modify, and/or distribute this software 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.
+*/
+//==============================================================================
+
+#ifndef BEAST_FILEINPUTSTREAM_BEASTHEADER
+#define BEAST_FILEINPUTSTREAM_BEASTHEADER
+
+#include "beast_File.h"
+#include "../streams/beast_InputStream.h"
+
+
+//==============================================================================
+/**
+ An input stream that reads from a local file.
+
+ @see InputStream, FileOutputStream, File::createInputStream
+*/
+class BEAST_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();
+ int read (void* destBuffer, int maxBytesToRead);
+ bool isExhausted();
+ int64 getPosition();
+ bool setPosition (int64 pos);
+
+private:
+ //==============================================================================
+ File file;
+ void* fileHandle;
+ int64 currentPosition;
+ Result status;
+ bool needToSeek;
+
+ void openHandle();
+ void closeHandle();
+ size_t readInternal (void* buffer, size_t numBytes);
+
+ BEAST_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FileInputStream)
+};
+
+#endif // BEAST_FILEINPUTSTREAM_BEASTHEADER
diff --git a/modules/beast_core/files/beast_FileOutputStream.cpp b/modules/beast_core/files/beast_FileOutputStream.cpp
new file mode 100644
index 00000000..368eb9c4
--- /dev/null
+++ b/modules/beast_core/files/beast_FileOutputStream.cpp
@@ -0,0 +1,131 @@
+//------------------------------------------------------------------------------
+/*
+ This file is part of Beast: https://github.com/vinniefalco/Beast
+ Copyright 2013, Vinnie Falco
+
+ Portions of this file are from JUCE.
+ Copyright (c) 2013 - Raw Material Software Ltd.
+ Please visit http://www.juce.com
+
+ Permission to use, copy, modify, and/or distribute this software 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.
+*/
+//==============================================================================
+
+int64 beast_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) bmax (bufferSize_, 16))
+{
+ openHandle();
+}
+
+FileOutputStream::~FileOutputStream()
+{
+ flushBuffer();
+ flushInternal();
+ closeHandle();
+}
+
+int64 FileOutputStream::getPosition()
+{
+ return currentPosition;
+}
+
+bool FileOutputStream::setPosition (int64 newPosition)
+{
+ if (newPosition != currentPosition)
+ {
+ flushBuffer();
+ currentPosition = beast_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)
+{
+ bassert (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;
+}
+
+void FileOutputStream::writeRepeatedByte (uint8 byte, size_t numBytes)
+{
+ bassert (((ssize_t) numBytes) >= 0);
+
+ if (bytesInBuffer + numBytes < bufferSize)
+ {
+ memset (buffer + bytesInBuffer, byte, numBytes);
+ bytesInBuffer += numBytes;
+ currentPosition += numBytes;
+ }
+ else
+ {
+ OutputStream::writeRepeatedByte (byte, numBytes);
+ }
+}
diff --git a/modules/beast_core/files/beast_FileOutputStream.h b/modules/beast_core/files/beast_FileOutputStream.h
new file mode 100644
index 00000000..a51d806c
--- /dev/null
+++ b/modules/beast_core/files/beast_FileOutputStream.h
@@ -0,0 +1,114 @@
+//------------------------------------------------------------------------------
+/*
+ This file is part of Beast: https://github.com/vinniefalco/Beast
+ Copyright 2013, Vinnie Falco
+
+ Portions of this file are from JUCE.
+ Copyright (c) 2013 - Raw Material Software Ltd.
+ Please visit http://www.juce.com
+
+ Permission to use, copy, modify, and/or distribute this software 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.
+*/
+//==============================================================================
+
+#ifndef BEAST_FILEOUTPUTSTREAM_BEASTHEADER
+#define BEAST_FILEOUTPUTSTREAM_BEASTHEADER
+
+#include "beast_File.h"
+#include "../streams/beast_OutputStream.h"
+
+
+//==============================================================================
+/**
+ An output stream that writes into a local file.
+
+ @see OutputStream, FileInputStream, File::createOutputStream
+*/
+class BEAST_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();
+ int64 getPosition();
+ bool setPosition (int64 pos);
+ bool write (const void* data, size_t numBytes);
+ void writeRepeatedByte (uint8 byte, size_t numTimesToRepeat);
+
+
+private:
+ //==============================================================================
+ File file;
+ void* fileHandle;
+ Result status;
+ int64 currentPosition;
+ size_t bufferSize, bytesInBuffer;
+ HeapBlock buffer;
+
+ void openHandle();
+ void closeHandle();
+ void flushInternal();
+ bool flushBuffer();
+ int64 setPositionInternal (int64);
+ ssize_t writeInternal (const void*, size_t);
+
+ BEAST_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FileOutputStream)
+};
+
+#endif // BEAST_FILEOUTPUTSTREAM_BEASTHEADER
diff --git a/modules/beast_core/files/beast_FileSearchPath.cpp b/modules/beast_core/files/beast_FileSearchPath.cpp
new file mode 100644
index 00000000..bfef91dd
--- /dev/null
+++ b/modules/beast_core/files/beast_FileSearchPath.cpp
@@ -0,0 +1,166 @@
+//------------------------------------------------------------------------------
+/*
+ This file is part of Beast: https://github.com/vinniefalco/Beast
+ Copyright 2013, Vinnie Falco
+
+ Portions of this file are from JUCE.
+ Copyright (c) 2013 - Raw Material Software Ltd.
+ Please visit http://www.juce.com
+
+ Permission to use, copy, modify, and/or distribute this software 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.
+*/
+//==============================================================================
+
+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& 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;
+}
diff --git a/modules/beast_core/files/beast_FileSearchPath.h b/modules/beast_core/files/beast_FileSearchPath.h
new file mode 100644
index 00000000..334b0a94
--- /dev/null
+++ b/modules/beast_core/files/beast_FileSearchPath.h
@@ -0,0 +1,164 @@
+//------------------------------------------------------------------------------
+/*
+ This file is part of Beast: https://github.com/vinniefalco/Beast
+ Copyright 2013, Vinnie Falco
+
+ Portions of this file are from JUCE.
+ Copyright (c) 2013 - Raw Material Software Ltd.
+ Please visit http://www.juce.com
+
+ Permission to use, copy, modify, and/or distribute this software 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.
+*/
+//==============================================================================
+
+#ifndef BEAST_FILESEARCHPATH_BEASTHEADER
+#define BEAST_FILESEARCHPATH_BEASTHEADER
+
+#include "beast_File.h"
+#include "../text/beast_StringArray.h"
+
+
+//==============================================================================
+/**
+ Encapsulates a set of folders that make up a search path.
+
+ @see File
+*/
+class BEAST_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& 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);
+
+ BEAST_LEAK_DETECTOR (FileSearchPath)
+};
+
+#endif // BEAST_FILESEARCHPATH_BEASTHEADER
diff --git a/modules/beast_core/files/beast_MemoryMappedFile.h b/modules/beast_core/files/beast_MemoryMappedFile.h
new file mode 100644
index 00000000..0c20619b
--- /dev/null
+++ b/modules/beast_core/files/beast_MemoryMappedFile.h
@@ -0,0 +1,111 @@
+//------------------------------------------------------------------------------
+/*
+ This file is part of Beast: https://github.com/vinniefalco/Beast
+ Copyright 2013, Vinnie Falco