summaryrefslogtreecommitdiff
path: root/Runtime/Utilities
diff options
context:
space:
mode:
Diffstat (limited to 'Runtime/Utilities')
-rw-r--r--Runtime/Utilities/Annotations.h52
-rw-r--r--Runtime/Utilities/Argv.cpp129
-rw-r--r--Runtime/Utilities/Argv.h30
-rw-r--r--Runtime/Utilities/ArrayUtility.h9
-rw-r--r--Runtime/Utilities/BitSetSerialization.h78
-rw-r--r--Runtime/Utilities/BitUtility.h189
-rw-r--r--Runtime/Utilities/CStringHash.h52
-rw-r--r--Runtime/Utilities/CopyPaste.h10
-rw-r--r--Runtime/Utilities/DateTime.cpp45
-rw-r--r--Runtime/Utilities/DateTime.h30
-rw-r--r--Runtime/Utilities/EditorPrefsTests.cpp160
-rw-r--r--Runtime/Utilities/EndianHelper.h148
-rw-r--r--Runtime/Utilities/EnumFlags.h13
-rw-r--r--Runtime/Utilities/ErrorExit.cpp165
-rw-r--r--Runtime/Utilities/ErrorExit.h77
-rw-r--r--Runtime/Utilities/File.cpp831
-rw-r--r--Runtime/Utilities/File.h194
-rw-r--r--Runtime/Utilities/FileStripped.h10
-rw-r--r--Runtime/Utilities/FileUtilities.cpp27
-rw-r--r--Runtime/Utilities/FileUtilities.h113
-rw-r--r--Runtime/Utilities/GLSLUtilities.cpp835
-rw-r--r--Runtime/Utilities/GLSLUtilities.h30
-rw-r--r--Runtime/Utilities/GUID.cpp241
-rw-r--r--Runtime/Utilities/GUID.h73
-rw-r--r--Runtime/Utilities/GlobalPreferences.cpp151
-rw-r--r--Runtime/Utilities/GlobalPreferences.h15
-rw-r--r--Runtime/Utilities/Hash128.cpp33
-rw-r--r--Runtime/Utilities/Hash128.h49
-rw-r--r--Runtime/Utilities/HashFunctions.h87
-rw-r--r--Runtime/Utilities/InitializeAndCleanup.cpp51
-rw-r--r--Runtime/Utilities/InitializeAndCleanup.h12
-rw-r--r--Runtime/Utilities/LODUtility.cpp86
-rw-r--r--Runtime/Utilities/LODUtility.h7
-rw-r--r--Runtime/Utilities/LinkedList.h385
-rw-r--r--Runtime/Utilities/LogAssert.cpp1294
-rw-r--r--Runtime/Utilities/LogAssert.h340
-rw-r--r--Runtime/Utilities/LogUtility.cpp4
-rw-r--r--Runtime/Utilities/LogUtility.h28
-rw-r--r--Runtime/Utilities/MemoryPool.cpp210
-rw-r--r--Runtime/Utilities/MemoryPool.h280
-rw-r--r--Runtime/Utilities/MemoryUtilities.cpp15
-rw-r--r--Runtime/Utilities/MemoryUtilities.h3
-rw-r--r--Runtime/Utilities/NonCopyable.h18
-rw-r--r--Runtime/Utilities/OptimizationUtility.h20
-rw-r--r--Runtime/Utilities/PathNameUtility.cpp601
-rw-r--r--Runtime/Utilities/PathNameUtility.h153
-rw-r--r--Runtime/Utilities/PlayerPrefs.h65
-rw-r--r--Runtime/Utilities/Prefetch.h52
-rw-r--r--Runtime/Utilities/RecursionLimit.h54
-rw-r--r--Runtime/Utilities/ReportHardware.cpp391
-rw-r--r--Runtime/Utilities/ReportHardware.h19
-rw-r--r--Runtime/Utilities/SpatialHash.cpp90
-rw-r--r--Runtime/Utilities/SpatialHash.h50
-rw-r--r--Runtime/Utilities/Stacktrace.cpp246
-rw-r--r--Runtime/Utilities/Stacktrace.h12
-rw-r--r--Runtime/Utilities/StaticAssert.h55
-rw-r--r--Runtime/Utilities/StrideIterator.h89
-rw-r--r--Runtime/Utilities/TypeUtilities.h20
-rw-r--r--Runtime/Utilities/URLUtility.h8
-rw-r--r--Runtime/Utilities/UniqueIDGenerator.cpp34
-rw-r--r--Runtime/Utilities/UniqueIDGenerator.h18
-rw-r--r--Runtime/Utilities/UnityString.h10
-rw-r--r--Runtime/Utilities/UserAuthorizationManager.cpp118
-rw-r--r--Runtime/Utilities/UserAuthorizationManager.h47
-rw-r--r--Runtime/Utilities/Utility.h214
-rw-r--r--Runtime/Utilities/UtilityTests.cpp629
-rw-r--r--Runtime/Utilities/VFPUtility.h43
-rw-r--r--Runtime/Utilities/ValidateArgs.h81
-rw-r--r--Runtime/Utilities/WavFileUtility.cpp54
-rw-r--r--Runtime/Utilities/WavFileUtility.h6
-rw-r--r--Runtime/Utilities/Word.cpp659
-rw-r--r--Runtime/Utilities/Word.h227
-rw-r--r--Runtime/Utilities/WordTests.cpp151
-rw-r--r--Runtime/Utilities/algorithm_utility.h115
-rw-r--r--Runtime/Utilities/delayed_set.h45
-rw-r--r--Runtime/Utilities/dense_hash_map.h243
-rw-r--r--Runtime/Utilities/densehashtable.h899
-rw-r--r--Runtime/Utilities/dynamic_array.h340
-rw-r--r--Runtime/Utilities/dynamic_array_tests.cpp254
-rw-r--r--Runtime/Utilities/dynamic_bitset.h1144
-rw-r--r--Runtime/Utilities/dynamic_block_vector.h135
-rw-r--r--Runtime/Utilities/fixed_bitset.h53
-rw-r--r--Runtime/Utilities/sorted_vector.h358
-rw-r--r--Runtime/Utilities/triple.h46
-rw-r--r--Runtime/Utilities/type_traits.h250
-rw-r--r--Runtime/Utilities/vector_map.h268
-rw-r--r--Runtime/Utilities/vector_set.h229
-rw-r--r--Runtime/Utilities/vector_utility.h95
88 files changed, 15299 insertions, 0 deletions
diff --git a/Runtime/Utilities/Annotations.h b/Runtime/Utilities/Annotations.h
new file mode 100644
index 0000000..39f7e5a
--- /dev/null
+++ b/Runtime/Utilities/Annotations.h
@@ -0,0 +1,52 @@
+#ifndef ANNOTATIONS_H
+#define ANNOTATIONS_H
+
+// Annotations for various stuff.
+// At the moment only understood by Visual Studio, and expand to nothing elsewhere.
+
+#if UNITY_WIN
+#pragma warning(disable:6255) // _alloca
+#pragma warning(disable:6211) // leaking due to exception
+#define INPUT_NOTNULL _In_
+#define INPUT_OPTIONAL _In_opt_
+#define OUTPUT_NOTNULL _Out_
+#define OUTPUT_OPTIONAL _Out_opt_
+#define INOUT_NOTNULL _Inout_
+#define INOUT_OPTIONAL _Inout_opt_
+#define RETVAL_NOTNULL _Ret_
+#define RETVAL_OPTIONAL _Ret_opt_
+#define DOES_NOT_RETURN __declspec(noreturn)
+#define ANALYSIS_ASSUME(x) { __analysis_assume(x); }
+#define TAKES_PRINTF_ARGS(n,m)
+
+#elif defined(__GNUC__)
+
+#define INPUT_NOTNULL
+#define INPUT_OPTIONAL
+#define OUTPUT_NOTNULL
+#define OUTPUT_OPTIONAL
+#define INOUT_NOTNULL
+#define INOUT_OPTIONAL
+#define RETVAL_NOTNULL
+#define RETVAL_OPTIONAL
+#define DOES_NOT_RETURN __attribute__((noreturn))
+#define ANALYSIS_ASSUME(x)
+#define TAKES_PRINTF_ARGS(m,n) __attribute__((format(printf,m,n)))
+
+#else
+
+#define INPUT_NOTNULL
+#define INPUT_OPTIONAL
+#define OUTPUT_NOTNULL
+#define OUTPUT_OPTIONAL
+#define INOUT_NOTNULL
+#define INOUT_OPTIONAL
+#define RETVAL_NOTNULL
+#define RETVAL_OPTIONAL
+#define DOES_NOT_RETURN
+#define ANALYSIS_ASSUME(x)
+#define TAKES_PRINTF_ARGS(n,m)
+
+#endif
+
+#endif
diff --git a/Runtime/Utilities/Argv.cpp b/Runtime/Utilities/Argv.cpp
new file mode 100644
index 0000000..b05da23
--- /dev/null
+++ b/Runtime/Utilities/Argv.cpp
@@ -0,0 +1,129 @@
+#include "UnityPrefix.h"
+#include "Argv.h"
+#if UNITY_WIN
+#include "PlatformDependent/Win/WinUtils.h"
+#endif
+
+using namespace std;
+
+static int argc;
+static const char** argv;
+static vector<string> relaunchArguments;
+
+struct KnownArguments
+{
+ bool isBatchmode;
+ bool isAutomated;
+};
+
+static KnownArguments knownArgs;
+
+void SetupArgv (int a, const char** b)
+{
+ argc = a;
+ argv = b;
+ knownArgs.isBatchmode = HasARGV ("batchmode");
+ knownArgs.isAutomated = HasARGV ("automated");
+}
+
+bool HasARGV (const string& name)
+{
+ for (int i=0;i<argc;i++)
+ {
+ if (StrICmp (argv[i], "-" + name) == 0)
+ return true;
+ }
+ return false;
+}
+
+bool IsBatchmode ()
+{
+ return knownArgs.isBatchmode;
+}
+
+bool IsHumanControllingUs ()
+{
+ return !(knownArgs.isBatchmode || knownArgs.isAutomated);
+}
+
+void SetIsBatchmode (bool value)
+{
+ knownArgs.isBatchmode = true;
+}
+
+void PrintARGV ()
+{
+ for (int i=0;i<argc;i++)
+ {
+ printf_console ("%s\n", argv[i]);
+ }
+}
+
+vector<string> GetValuesForARGV (const string& name)
+{
+ vector<string> values;
+ values.reserve (argc);
+
+ bool found = false;
+ for (int i=0;i<argc;i++)
+ {
+ if (found)
+ {
+ if (argv[i][0] == '-')
+ return values;
+ else
+ values.push_back (argv[i]);
+ }
+ else if (StrICmp (argv[i], "-" + name) == 0)
+ found = true;
+ }
+
+ return values;
+}
+
+string GetFirstValueForARGV (const string& name)
+{
+ vector<string> values = GetValuesForARGV (name);
+ if (values.empty ())
+ return string ();
+ else
+ return values[0];
+}
+
+vector<string> GetAllArguments()
+{
+ vector<string> values;
+ values.reserve (argc);
+ for (int i=1;i<argc;i++)
+ values.push_back (argv[i]);
+ return values;
+}
+
+void SetRelaunchApplicationArguments (const vector<string>& args)
+{
+ relaunchArguments = args;
+}
+
+vector<string> GetRelaunchApplicationArguments ()
+{
+ return relaunchArguments;
+}
+
+void CheckBatchModeErrorString (const string& error)
+{
+ if (error.empty ())
+ return;
+
+ ErrorString(error);
+
+ if (!IsBatchmode())
+ return;
+
+#if UNITY_WIN && UNITY_EDITOR
+ winutils::RedirectStdoutToConsole();
+#elif UNITY_OSX
+ ResetStdout();
+#endif
+ printf_console ("\nAborting batchmode due to failure:\n%s\n\n", error.c_str());
+ exit(1);
+}
diff --git a/Runtime/Utilities/Argv.h b/Runtime/Utilities/Argv.h
new file mode 100644
index 0000000..835b4bc
--- /dev/null
+++ b/Runtime/Utilities/Argv.h
@@ -0,0 +1,30 @@
+#ifndef ARGV_H
+#define ARGV_H
+
+void SetupArgv (int argc, const char** argv);
+
+const char **GetArgv();
+int GetArgc();
+
+/// Returns true if the commandline contains a -name.
+bool HasARGV (const std::string& name);
+
+bool IsBatchmode ();
+bool IsHumanControllingUs ();
+
+void SetIsBatchmode (bool value);
+
+/// Returns a list of values for the argument
+std::vector<std::string> GetValuesForARGV (const std::string& name);
+
+std::vector<std::string> GetAllArguments();
+std::string GetFirstValueForARGV (const std::string& name);
+
+void SetRelaunchApplicationArguments (const std::vector<std::string>& args);
+std::vector<std::string> GetRelaunchApplicationArguments ();
+
+void PrintARGV ();
+
+void CheckBatchModeErrorString (const std::string& error);
+
+#endif
diff --git a/Runtime/Utilities/ArrayUtility.h b/Runtime/Utilities/ArrayUtility.h
new file mode 100644
index 0000000..c618296
--- /dev/null
+++ b/Runtime/Utilities/ArrayUtility.h
@@ -0,0 +1,9 @@
+#ifndef ARRAY_UTILITY_H
+#define ARRAY_UTILITY_H
+
+
+// Element count of a static array
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
+
+
+#endif
diff --git a/Runtime/Utilities/BitSetSerialization.h b/Runtime/Utilities/BitSetSerialization.h
new file mode 100644
index 0000000..1ac2eac
--- /dev/null
+++ b/Runtime/Utilities/BitSetSerialization.h
@@ -0,0 +1,78 @@
+#include "dynamic_bitset.h"
+#include "Runtime/Serialize/TypeTree.h"
+#include "Runtime/Serialize/SwapEndianArray.h"
+
+template<>
+class SerializeTraits<dynamic_bitset>: public SerializeTraitsBase<dynamic_bitset>
+{
+ public:
+
+ typedef dynamic_bitset value_type;
+
+ inline static const char* GetTypeString (value_type*) { return "bitset"; }
+ inline static bool IsAnimationChannel () { return false; }
+ inline static bool MightContainPPtr () { return false; }
+ inline static bool AllowTransferOptimization () { return false; }
+
+ template<class TransferFunction> inline
+ static void Transfer (value_type& data, TransferFunction& transfer)
+ {
+ SInt32 bitCount = data.size ();
+ transfer.Transfer (bitCount, "bitCount");
+
+ unsigned byteSize = data.num_blocks () * sizeof (value_type::block_type);
+ transfer.TransferTypeless (&byteSize, "bitblocks");
+ AssertIf (sizeof (value_type::block_type) != 4);
+ AssertIf (byteSize % 4 != 0);
+
+ if (transfer.IsReading ())
+ {
+ data.resize (bitCount);
+ transfer.TransferTypelessData (byteSize, data.m_bits);
+ if (transfer.ConvertEndianess ())
+ SwapEndianArray (data.m_bits, sizeof (value_type::block_type), byteSize / 4);
+ data.m_zero_unused_bits ();
+ }
+ else if (transfer.IsWriting ())
+ {
+ value_type::block_type* writeData = data.m_bits;
+ if (transfer.ConvertEndianess ())
+ {
+ writeData = (value_type::block_type*)UNITY_MALLOC (kMemTempAlloc, byteSize);
+ memcpy (writeData, data.m_bits, byteSize);
+ SwapEndianArray (writeData, sizeof (value_type::block_type), byteSize / 4);
+ }
+
+ AssertIf (data.num_blocks () != byteSize / 4);
+ transfer.TransferTypelessData (byteSize, writeData);
+
+ if (transfer.ConvertEndianess ())
+ UNITY_FREE (kMemTempAlloc, writeData);
+ }
+ else
+ transfer.TransferTypelessData (byteSize, NULL);
+ }
+ // Deque<bool> converter
+ template<class TransferFunction>
+ static bool Convert (value_type& data, TransferFunction& transfer)
+ {
+ const TypeTree& oldTypeTree = transfer.GetActiveOldTypeTree ();
+ const std::string& oldType = transfer.GetActiveOldTypeTree ().m_Type;
+ if ((oldType == "vector" || oldType == "deque") && GetElementTypeFromContainer (oldTypeTree).m_Type == "bool")
+ {
+ std::deque<bool> dequeBool;
+ transfer.TransferSTLStyleArray (dequeBool);
+ data.resize (dequeBool.size ());
+
+ std::deque<bool>::iterator d = dequeBool.begin ();
+ for (int i=0;i<data.size ();i++)
+ {
+ data[i] = *d;
+ d++;
+ }
+ return true;
+ }
+ else
+ return false;
+ }
+};
diff --git a/Runtime/Utilities/BitUtility.h b/Runtime/Utilities/BitUtility.h
new file mode 100644
index 0000000..774d6e0
--- /dev/null
+++ b/Runtime/Utilities/BitUtility.h
@@ -0,0 +1,189 @@
+#ifndef BITUTILITY_H
+#define BITUTILITY_H
+
+#include <limits.h>
+
+// index of the most significant bit in the mask
+
+const char gHighestBitLut[] = {-1,0,1,1,2,2,2,2,3,3,3,3,3,3,3,3};
+
+inline int HighestBit(UInt32 mask)
+{
+#ifdef SN_TARGET_PS3
+ return 31 - __cntlzw(mask);
+#else
+ int base = 0;
+
+ if ( mask & 0xffff0000 )
+ {
+ base = 16;
+ mask >>= 16;
+ }
+ if ( mask & 0x0000ff00 )
+ {
+ base += 8;
+ mask >>= 8;
+ }
+ if ( mask & 0x000000f0 )
+ {
+ base += 4;
+ mask >>= 4;
+ }
+
+ return base + gHighestBitLut[ mask ];
+#endif
+}
+
+
+// index of the least significant bit in the mask
+
+const char gLowestBitLut[] = {-1,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0};
+inline int LowestBit(UInt32 mask)
+{
+ AssertIf (mask == 0);
+
+ int base = 0;
+
+ if ( !(mask & 0xffff) )
+ {
+ base = 16;
+ mask >>= 16;
+ }
+ if ( !(mask & 0x00ff) )
+ {
+ base += 8;
+ mask >>= 8;
+ }
+ if ( !(mask & 0x000f) )
+ {
+ base += 4;
+ mask >>= 4;
+ }
+
+ return base + gLowestBitLut[ mask & 15 ];
+}
+
+// can be optimized later
+inline int AnyBitFromMask (UInt32 mask)
+{
+ return HighestBit (mask);
+}
+
+// index of the first consecutiveBitCount enabled bits
+// -1 if not available
+inline int LowestBitConsecutive (UInt32 bitMask, int consecutiveBitCount)
+{
+ UInt32 tempBitMask = bitMask;
+ int i;
+ for (i=1;i<consecutiveBitCount;i++)
+ tempBitMask &= bitMask >> i;
+
+ if (!tempBitMask)
+ return -1;
+ else
+ return LowestBit (tempBitMask);
+}
+/*int LowestBitConsecutive ( u_int value, u_int consecutiveBitCount )
+{
+ u_int mask = (1 << consecutiveBitCount) - 1;
+ u_int notValue = value ^ 0xffffffff;
+ u_int workingMask = mask;
+ u_int prevMask = 0;
+ int match = notValue & workingMask;
+ u_int shift = 1;
+ while ( (match != 0) && (prevMask < workingMask) )
+ {
+ shift = 2*u_int(match & -match);
+ prevMask = workingMask;
+ workingMask = mask * shift;
+ match = notValue & workingMask;
+ }
+ if ( prevMask < workingMask )
+ {
+ return LowestBit( shift );
+ }
+ else
+ {
+ return -1;
+ }
+}*/
+
+// number of set bits in the 32 bit mask
+inline int BitsInMask (UInt32 v)
+{
+ // From http://www-graphics.stanford.edu/~seander/bithacks.html#CountBitsSetParallel
+ // This variant about 30% faster on 360 than what was here before.
+ v = v - ((v >> 1) & 0x55555555);
+ v = (v & 0x33333333) + ((v >> 2) & 0x33333333);
+ return ((v + (v >> 4) & 0xF0F0F0F) * 0x1010101) >> 24;
+}
+
+// number of set bits in the 64 bit mask
+inline int BitsInMask64 (UInt64 v)
+{
+ // From http://www-graphics.stanford.edu/~seander/bithacks.html#CountBitsSetParallel
+ v = v - ((v >> 1) & (UInt64)~(UInt64)0/3);
+ v = (v & (UInt64)~(UInt64)0/15*3) + ((v >> 2) & (UInt64)~(UInt64)0/15*3);
+ v = (v + (v >> 4)) & (UInt64)~(UInt64)0/255*15;
+ return (UInt64)(v * ((UInt64)~(UInt64)0/255)) >> (sizeof(UInt64) - 1) * CHAR_BIT;
+}
+
+// reverse bit order
+inline void ReverseBits(UInt32& mask)
+{
+ mask = ((mask >> 1) & 0x55555555) | ((mask << 1) & 0xaaaaaaaa);
+ mask = ((mask >> 2) & 0x33333333) | ((mask << 2) & 0xcccccccc);
+ mask = ((mask >> 4) & 0x0f0f0f0f) | ((mask << 4) & 0xf0f0f0f0);
+ mask = ((mask >> 8) & 0x00ff00ff) | ((mask << 8) & 0xff00ff00);
+ mask = ((mask >> 16) & 0x0000ffff) | ((mask << 16) & 0xffff0000) ;
+}
+
+// is value a power-of-two
+inline bool IsPowerOfTwo(UInt32 mask)
+{
+ return (mask & (mask-1)) == 0;
+}
+
+// return the next power-of-two of a 32bit number
+inline UInt32 NextPowerOfTwo(UInt32 v)
+{
+ v -= 1;
+ v |= v >> 16;
+ v |= v >> 8;
+ v |= v >> 4;
+ v |= v >> 2;
+ v |= v >> 1;
+ return v + 1;
+}
+
+// return the closest power-of-two of a 32bit number
+inline UInt32 ClosestPowerOfTwo(UInt32 v)
+{
+ UInt32 nextPower = NextPowerOfTwo (v);
+ UInt32 prevPower = nextPower >> 1;
+ if (v - prevPower < nextPower - v)
+ return prevPower;
+ else
+ return nextPower;
+}
+
+inline UInt32 ToggleBit (UInt32 bitfield, int index)
+{
+ AssertIf (index < 0 || index >= 32);
+ return bitfield ^ (1 << index);
+}
+
+// Template argument must be a power of 2
+template<int n>
+struct StaticLog2
+{
+ static const int value = StaticLog2<n/2>::value + 1;
+};
+
+template<>
+struct StaticLog2<1>
+{
+ static const int value = 0;
+};
+
+#endif
diff --git a/Runtime/Utilities/CStringHash.h b/Runtime/Utilities/CStringHash.h
new file mode 100644
index 0000000..a4b58ef
--- /dev/null
+++ b/Runtime/Utilities/CStringHash.h
@@ -0,0 +1,52 @@
+#ifndef CSTRINGHASH_H
+#define CSTRINGHASH_H
+#include <functional>
+
+struct hash_cstring : std::unary_function<const char*, std::size_t>
+{
+ unsigned operator ()(const char* key) const
+ {
+ unsigned h = 0;
+ const unsigned sr = 8 * sizeof (unsigned) - 8;
+ const unsigned mask = 0xF << (sr + 4);
+ while (*key != '\0')
+ {
+ h = (h << 4) + *key;
+ std::size_t g = h & mask;
+ h ^= g | (g >> sr);
+ key++;
+ }
+ return h;
+ }
+};
+
+struct equal_cstring : std::binary_function<char*, char*, std::size_t>
+{
+ bool operator () (char* lhs, char* rhs) const
+ {
+ while (*lhs != '\0')
+ {
+ if (*lhs != *rhs)
+ return false;
+ lhs++; rhs++;
+ }
+ return *lhs == *rhs;
+ }
+};
+
+struct smaller_cstring : std::binary_function<const char*, const char*, std::size_t>
+{
+ bool operator () (const char* lhs, const char* rhs) const { return strcmp (lhs, rhs) < 0; }
+};
+
+struct compare_cstring : public std::binary_function<const char*, const char*, bool>
+{
+ bool operator ()(const char* lhs, const char* rhs) const { return strcmp (lhs, rhs) < 0; }
+};
+
+struct compare_string_insensitive : public std::binary_function<const std::string, const std::string, bool>
+{
+ bool operator ()(const std::string& lhs, const std::string& rhs) const { return StrICmp (lhs, rhs) < 0; }
+};
+
+#endif
diff --git a/Runtime/Utilities/CopyPaste.h b/Runtime/Utilities/CopyPaste.h
new file mode 100644
index 0000000..8c9e299
--- /dev/null
+++ b/Runtime/Utilities/CopyPaste.h
@@ -0,0 +1,10 @@
+#ifndef COPYPASTE_H
+#define COPYPASTE_H
+
+/// Get the system-wide copy buffer for pasting into a textfield
+std::string GetCopyBuffer ();
+
+/// Set the system-wide copybuffer for pasting from a textfield
+void SetCopyBuffer (const std::string &utf8string);
+
+#endif
diff --git a/Runtime/Utilities/DateTime.cpp b/Runtime/Utilities/DateTime.cpp
new file mode 100644
index 0000000..b95e06c
--- /dev/null
+++ b/Runtime/Utilities/DateTime.cpp
@@ -0,0 +1,45 @@
+#include "UnityPrefix.h"
+#include "DateTime.h"
+#include "Runtime/Serialize/SwapEndianBytes.h"
+
+
+DateTime::DateTime ()
+{
+ highSeconds = 0;
+ lowSeconds = 0;
+ fraction = 0;
+}
+
+bool operator < (const DateTime& d0, const DateTime& d1)
+{
+ if (d0.highSeconds < d1.highSeconds)
+ return true;
+ else if (d0.highSeconds > d1.highSeconds)
+ return false;
+
+ if (d0.lowSeconds < d1.lowSeconds)
+ return true;
+ else if (d0.lowSeconds > d1.lowSeconds)
+ return false;
+
+ return d0.fraction < d1.fraction;
+}
+
+bool operator == (const DateTime& d0, const DateTime& d1)
+{
+ if (d0.highSeconds != d1.highSeconds)
+ return false;
+
+ if (d0.lowSeconds != d1.lowSeconds)
+ return false;
+
+ return d0.fraction == d1.fraction;
+}
+
+
+void ByteSwapDateTime (DateTime& dateTime)
+{
+ SwapEndianBytes(dateTime.highSeconds);
+ SwapEndianBytes(dateTime.fraction);
+ SwapEndianBytes(dateTime.lowSeconds);
+}
diff --git a/Runtime/Utilities/DateTime.h b/Runtime/Utilities/DateTime.h
new file mode 100644
index 0000000..b747628
--- /dev/null
+++ b/Runtime/Utilities/DateTime.h
@@ -0,0 +1,30 @@
+#pragma once
+
+#include "Runtime/Serialize/SerializeUtility.h"
+
+struct DateTime
+{
+ // We are wasting memory and serialization
+ // in assetdatabase is wrong because of the alignment!
+ // We do this change in 1.5 because it will force a rebuild of all assets
+ UInt16 highSeconds;
+ UInt16 fraction;
+ UInt32 lowSeconds;
+ DECLARE_SERIALIZE_OPTIMIZE_TRANSFER (DateTime)
+
+ DateTime ();
+
+ friend bool operator < (const DateTime& d0, const DateTime& d1);
+ friend bool operator == (const DateTime& d0, const DateTime& d1);
+ friend bool operator != (const DateTime& d0, const DateTime& d1) { return !(d0 == d1); }
+};
+
+template<class TransferFunction>
+void DateTime::Transfer (TransferFunction& transfer)
+{
+ TRANSFER (highSeconds);
+ TRANSFER (fraction);
+ TRANSFER (lowSeconds);
+}
+
+void ByteSwapDateTime (DateTime& dateTime);
diff --git a/Runtime/Utilities/EditorPrefsTests.cpp b/Runtime/Utilities/EditorPrefsTests.cpp
new file mode 100644
index 0000000..3d066f0
--- /dev/null
+++ b/Runtime/Utilities/EditorPrefsTests.cpp
@@ -0,0 +1,160 @@
+#include "UnityPrefix.h"
+#include "Runtime/Utilities/PlayerPrefs.h"
+
+#if UNITY_EDITOR && ENABLE_UNIT_TESTS
+
+#include "External/UnitTest++/src/UnitTest++.h"
+
+#if UNITY_OSX
+ // Defined in EditorPrefsTests.mm
+ void InitNSAutoreleasePool();
+ void ReleaseNSAutoreleasePool();
+
+ #define INIT_TEST InitNSAutoreleasePool()
+ #define RELEASE_TEST ReleaseNSAutoreleasePool()
+#else
+ #define INIT_TEST
+ #define RELEASE_TEST
+#endif
+
+// We test PlayerPrefs in Integration tests
+// EditorPrefs are not exposed on C# side, so we need to tests them in C++
+
+SUITE (EditorPrefsTests)
+{
+ TEST(TestEditorPrefs)
+ {
+ INIT_TEST;
+
+ const std::string
+ kBoolKeyName = "someBool",
+ kIntKeyName = "someInt",
+ kFloatKeyName = "someFloat",
+ kStringKeyName = "someString",
+ kNonExistingKeyName = "someName";
+ const std::string
+ kBoolKeyNameCaps = "someBOOL",
+ kIntKeyNameCaps = "someINT",
+ kFloatKeyNameCaps = "someFLOAT",
+ kStringKeyNameCaps = "someSTRING";
+ const bool kStoredBool = true, kStoredBoolCaps = false, kDefaultBool = false;
+ const int kStoredInt = 3, kStoredIntCaps = 4, kDefaultInt = 7;
+ const float kStoredFloat = 5.7f, kStoredFloatCaps = 6.23f, kDefaultFloat = 7.17f;
+ const std::string kStoredString = "This is the string", kStoredStringCaps = "CAPs string", kDefaultString = "Did not find meh";
+
+ // Making sure there are no permanently stored keys
+ EditorPrefs::DeleteKey(kBoolKeyName);
+ EditorPrefs::DeleteKey(kIntKeyName);
+ EditorPrefs::DeleteKey(kFloatKeyName);
+ EditorPrefs::DeleteKey(kStringKeyName);
+
+ EditorPrefs::DeleteKey(kBoolKeyNameCaps);
+ EditorPrefs::DeleteKey(kIntKeyNameCaps);
+ EditorPrefs::DeleteKey(kFloatKeyNameCaps);
+ EditorPrefs::DeleteKey(kStringKeyNameCaps);
+
+ CHECK(EditorPrefs::SetBool(kBoolKeyName, kStoredBool));
+ CHECK(EditorPrefs::SetInt(kIntKeyName, kStoredInt));
+ CHECK(EditorPrefs::SetFloat(kFloatKeyName, kStoredFloat));
+ CHECK(EditorPrefs::SetString(kStringKeyName, kStoredString));
+
+ CHECK_EQUAL(EditorPrefs::GetBool(kBoolKeyName, kDefaultBool), kStoredBool);
+ CHECK_EQUAL(EditorPrefs::GetInt(kBoolKeyName, kDefaultInt), 1); // SetBool fallsback to SetInt, so this call suceeds getting value of kBoolKeyName
+ CHECK_EQUAL(EditorPrefs::GetFloat(kBoolKeyName, kDefaultFloat), kDefaultFloat);
+ CHECK_EQUAL(EditorPrefs::GetString(kBoolKeyName, kDefaultString), kDefaultString);
+
+ CHECK_EQUAL(EditorPrefs::GetBool(kIntKeyName, kDefaultBool), true); // GetBool fallsback to GetInt, so this call succeeds getting value of kIntKeyName
+ CHECK_EQUAL(EditorPrefs::GetInt(kIntKeyName, kDefaultInt), kStoredInt);
+ CHECK_EQUAL(EditorPrefs::GetFloat(kIntKeyName, kDefaultFloat), kDefaultFloat);
+ CHECK_EQUAL(EditorPrefs::GetString (kIntKeyName, kDefaultString), kDefaultString);
+
+ CHECK_EQUAL(EditorPrefs::GetBool(kFloatKeyName, kDefaultBool), kDefaultBool);
+ CHECK_EQUAL(EditorPrefs::GetInt (kFloatKeyName, kDefaultInt), kDefaultInt);
+ CHECK_EQUAL(EditorPrefs::GetFloat (kFloatKeyName, kDefaultFloat), kStoredFloat);
+ CHECK_EQUAL(EditorPrefs::GetString (kFloatKeyName, kDefaultString), kDefaultString);
+
+ CHECK_EQUAL(EditorPrefs::GetBool(kStringKeyName, kDefaultBool), kDefaultBool);
+ CHECK_EQUAL(EditorPrefs::GetInt (kStringKeyName, kDefaultInt), kDefaultInt);
+ CHECK_EQUAL(EditorPrefs::GetFloat (kStringKeyName, kDefaultFloat), kDefaultFloat);
+ CHECK_EQUAL(EditorPrefs::GetString (kStringKeyName, kDefaultString), kStoredString);
+
+ CHECK_EQUAL(EditorPrefs::GetBool(kNonExistingKeyName, kDefaultBool), kDefaultBool);
+ CHECK_EQUAL(EditorPrefs::GetInt (kNonExistingKeyName, kDefaultInt), kDefaultInt);
+ CHECK_EQUAL(EditorPrefs::GetFloat (kNonExistingKeyName, kDefaultFloat), kDefaultFloat);
+ CHECK_EQUAL(EditorPrefs::GetString (kNonExistingKeyName, kDefaultString), kDefaultString);
+
+ CHECK(EditorPrefs::HasKey(kBoolKeyName));
+ CHECK(EditorPrefs::HasKey(kIntKeyName));
+ CHECK(EditorPrefs::HasKey(kFloatKeyName));
+ CHECK(EditorPrefs::HasKey(kStringKeyName));
+ CHECK(!EditorPrefs::HasKey(kNonExistingKeyName));
+
+ // Case sensitivity tests
+ CHECK_EQUAL(EditorPrefs::GetBool(kBoolKeyNameCaps, kDefaultBool), kDefaultBool);
+ CHECK_EQUAL(EditorPrefs::GetInt(kIntKeyNameCaps, kDefaultInt), kDefaultInt);
+ CHECK_EQUAL(EditorPrefs::GetFloat(kFloatKeyNameCaps, kDefaultFloat), kDefaultFloat);
+ CHECK_EQUAL(EditorPrefs::GetString(kStringKeyNameCaps, kDefaultString), kDefaultString);
+
+ CHECK(!EditorPrefs::HasKey(kBoolKeyNameCaps));
+ CHECK(!EditorPrefs::HasKey(kIntKeyNameCaps));
+ CHECK(!EditorPrefs::HasKey(kFloatKeyNameCaps));
+ CHECK(!EditorPrefs::HasKey(kStringKeyNameCaps));
+
+ CHECK(EditorPrefs::SetBool(kBoolKeyNameCaps, kStoredBoolCaps));
+ CHECK(EditorPrefs::SetInt(kIntKeyNameCaps, kStoredIntCaps));
+ CHECK(EditorPrefs::SetFloat(kFloatKeyNameCaps, kStoredFloatCaps));
+ CHECK(EditorPrefs::SetString(kStringKeyNameCaps, kStoredStringCaps));
+
+ CHECK_EQUAL(EditorPrefs::GetBool(kBoolKeyName, kDefaultBool), kStoredBool);
+ CHECK_EQUAL(EditorPrefs::GetInt(kIntKeyName, kDefaultInt), kStoredInt);
+ CHECK_EQUAL(EditorPrefs::GetFloat(kFloatKeyName, kDefaultFloat), kStoredFloat);
+ CHECK_EQUAL(EditorPrefs::GetString(kStringKeyName, kDefaultString), kStoredString);
+
+ CHECK_EQUAL(EditorPrefs::GetBool(kBoolKeyNameCaps, kDefaultBool), kStoredBoolCaps);
+ CHECK_EQUAL(EditorPrefs::GetInt(kIntKeyNameCaps, kDefaultInt), kStoredIntCaps);
+ CHECK_EQUAL(EditorPrefs::GetFloat(kFloatKeyNameCaps, kDefaultFloat), kStoredFloatCaps);
+ CHECK_EQUAL(EditorPrefs::GetString(kStringKeyNameCaps, kDefaultString), kStoredStringCaps);
+
+ RELEASE_TEST;
+ }
+
+
+ // Set value using one type, then set with another type; Check values for both types
+ #define TEST_OVERRIDE(TypeName1, TypeName2, Expected1, Expected2) \
+ EditorPrefs::DeleteKey(kKeyName); \
+ CHECK(EditorPrefs::Set##TypeName1(kKeyName, kStored##TypeName1)); \
+ CHECK(EditorPrefs::Set##TypeName2(kKeyName, kStored##TypeName2)); \
+ CHECK_EQUAL(EditorPrefs::Get##TypeName1(kKeyName, kDefault##TypeName1), Expected1); \
+ CHECK_EQUAL(EditorPrefs::Get##TypeName2(kKeyName, kDefault##TypeName2), Expected2); \
+
+ TEST(EditorPrefsOverriding)
+ {
+ INIT_TEST;
+
+ const std::string kKeyName = "MyKey";
+ const bool kStoredBool = true, kDefaultBool = false;
+ const int kStoredInt = 3, kDefaultInt = 7;
+ const float kStoredFloat = 5.7f, kDefaultFloat = 7.17f;
+ const std::string kStoredString = "This is the string", kDefaultString = "Did not find meh";
+
+ TEST_OVERRIDE(Bool, Int, true, kStoredInt); // ints and bool are stored the same, so value is shared
+ TEST_OVERRIDE(Bool, Float, kDefaultBool, kStoredFloat);
+ TEST_OVERRIDE(Bool, String, kDefaultBool, kStoredString);
+
+ TEST_OVERRIDE(Int, Bool, 1, true); // ints and bool are stored the same, so value is shared
+ TEST_OVERRIDE(Int, Float, kDefaultInt, kStoredFloat);
+ TEST_OVERRIDE(Int, String, kDefaultInt, kStoredString);
+
+ TEST_OVERRIDE(Float, Bool, kDefaultFloat, kStoredBool);
+ TEST_OVERRIDE(Float, Int, kDefaultFloat, kStoredInt);
+ TEST_OVERRIDE(Float, String, kDefaultFloat, kStoredString);
+
+ TEST_OVERRIDE(String, Bool, kDefaultString, kStoredBool);
+ TEST_OVERRIDE(String, Int, kDefaultString, kStoredInt);
+ TEST_OVERRIDE(String, Float, kDefaultString, kStoredFloat);
+
+ RELEASE_TEST;
+ }
+}
+
+#endif
diff --git a/Runtime/Utilities/EndianHelper.h b/Runtime/Utilities/EndianHelper.h
new file mode 100644
index 0000000..42df0ac
--- /dev/null
+++ b/Runtime/Utilities/EndianHelper.h
@@ -0,0 +1,148 @@
+#ifndef UNITY_ENDIAN_HELPER_H_
+#define UNITY_ENDIAN_HELPER_H_
+
+
+enum EndianMode {
+ kEndianDirect = 0,
+ kEndianConvert = 1,
+#if UNITY_BIG_ENDIAN
+ kHostEndian = kEndianDirect,
+ kTargetEndian = kEndianDirect,
+#else
+ kHostEndian = kEndianConvert,
+ kTargetEndian = kEndianConvert,
+#endif
+};
+
+ template<EndianMode Endian>
+ struct EndianHelper;
+
+ template<>
+ struct EndianHelper<kEndianDirect>
+ {
+ static UInt16 Load( UInt16 const* p ) {
+ return *p;
+ }
+
+ static UInt32 Load( UInt32 const* p ) {
+ return *p;
+ }
+
+ template<int Offset>
+ static void Store( UInt16* p, UInt16 arg ) {
+ *reinterpret_cast<UInt16*>( ((intptr_t)p + Offset) ) = arg;
+ }
+
+ template<int Offset>
+ static void Store( UInt32* p, UInt32 arg ) {
+ *reinterpret_cast<UInt32*>( ((intptr_t)p + Offset) ) = arg;
+ }
+
+ };
+
+ template<>
+ struct EndianHelper<kEndianConvert>
+ {
+ static UInt16 Load( register UInt16 const* p ) {
+//#if defined(__native_client__) || (defined(__GNUC__) && !defined(__APPLE__))
+ return ((*p&0xff00U)>>8) | ((*p&0x00ffU)<<8);
+//#elif defined(__ppc__)
+// register UInt16 temp;
+// __asm {
+// lhbrx temp, 0, p
+// }
+// return temp;
+//#elif defined(__i386) || defined(_M_IX86)
+// register UInt16 temp = *p;
+// __asm {
+// mov ax, temp
+// xchg ah, al
+// mov temp, ax
+// }
+// return temp;
+//#else
+//#error "Unsupported architecture"
+//#endif
+ }
+
+ static UInt32 Load( register UInt32 const* p ) {
+//#if defined(__native_client__) || (defined(__GNUC__) && !defined(__APPLE__))
+ return
+ ((*p&0xff000000U)>>24) |
+ ((*p&0x00ff0000U)>>8) |
+ ((*p&0x0000ff00U)<<8) |
+ ((*p&0x000000ffU)<<24)
+ ;
+//#elif defined(__ppc__)
+// register UInt32 temp;
+// __asm {
+// lwbrx r4, 0, p
+// }
+// return temp;
+//#elif defined(__i386) || defined(_M_IX86)
+// register UInt32 temp = *p;
+// __asm {
+// mov eax, temp
+// bswap eax
+// mov temp, eax
+// }
+// return temp;
+//#else
+//#error "Unsupported architecture"
+//#endif
+ }
+
+ // Offset is in bytes
+ template<int Offset>
+ static void Store( register UInt16* p, register UInt16 arg ) {
+//#if defined(__native_client__) || (defined(__GNUC__) && !defined(__APPLE__))
+ UInt16 temp = ((arg&0xff00U)>>8) | ((arg&0x00ffU)<<8);
+ *(UInt16*)((UInt8*)p + Offset) = temp;
+//#elif defined(__ppc__)
+// register short int ofs = Offset;
+// __asm {
+// sthbrx arg, ofs, p
+// }
+//#elif defined(__i386) || defined(_M_IX86)
+// register void* pp = (void*)((uintptr_t)p + Offset);
+// __asm {
+// mov ax, arg
+// xchg ah, al
+// mov ecx, dword ptr [pp]
+// mov word ptr [ecx], ax
+// }
+//#else
+//#error "Unsupported architecture"
+//#endif
+ }
+
+ template<int Offset>
+ static void Store( register UInt32* p, register UInt32 arg ) {
+//#if defined(__native_client__) || (defined(__GNUC__) && !defined(__APPLE__))
+ UInt32 temp =
+ ((arg&0xff000000U)>>24) |
+ ((arg&0x00ff0000U)>>8) |
+ ((arg&0x0000ff00U)<<8) |
+ ((arg&0x000000ffU)<<24)
+ ;
+ *(UInt32*)((UInt8*)p + Offset) = temp;
+//#elif defined(__ppc__)
+// register short int ofs = Offset;
+// __asm {
+// stwbrx arg, ofs, p
+// }
+//#elif defined(__i386) || defined(_M_IX86)
+// register void* pp = (void*)((uintptr_t)p + Offset);
+// __asm {
+// mov eax, arg
+// bswap eax
+// mov ecx, dword ptr [pp]
+// mov word ptr [ecx], eax
+// }
+//#else
+//#error "Unsupported architecture"
+//#endif
+ }
+ };
+
+#endif // UNITY_ENDIAN_HELPER_H_
diff --git a/Runtime/Utilities/EnumFlags.h b/Runtime/Utilities/EnumFlags.h
new file mode 100644
index 0000000..61d323b
--- /dev/null
+++ b/Runtime/Utilities/EnumFlags.h
@@ -0,0 +1,13 @@
+#ifndef ENUM_FLAGS_H
+#define ENUM_FLAGS_H
+
+#define ENUM_FLAGS(T) \
+inline T operator |(const T s, const T e) { return (T)((unsigned)s | e); } \
+inline T &operator |=(T &s, const T e) { return s = s | e; } \
+inline T operator &(const T s, const T e) { return (T)((unsigned)s & e); } \
+inline T &operator &=(T &s, const T e) { return s = s & e; } \
+inline T operator ^(const T s, const T e) { return (T)((unsigned)s ^ e); } \
+inline T &operator ^=(T &s, const T e) { return s = s ^ e; } \
+inline T operator ~(const T s) { return (T)~(unsigned)s; }
+
+#endif
diff --git a/Runtime/Utilities/ErrorExit.cpp b/Runtime/Utilities/ErrorExit.cpp
new file mode 100644
index 0000000..414aa06
--- /dev/null
+++ b/Runtime/Utilities/ErrorExit.cpp
@@ -0,0 +1,165 @@
+#include "UnityPrefix.h"
+#include "ErrorExit.h"
+#include "Runtime/Misc/ReproductionLog.h"
+#if SUPPORT_ERROR_EXIT
+#include "Runtime/Threads/ThreadSpecificValue.h"
+#include "Stacktrace.h"
+
+#if UNITY_WIN
+
+#include "Configuration/UnityConfigureOther.h"
+#include "Runtime/Mono/MonoIncludes.h"
+
+#include "PlatformDependent/Win/WinUtils.h"
+
+#if !UNITY_WIN_ENABLE_CRASHHANDLER
+#error "UNITY_WIN_ENABLE_CRASHHANDLER must be defined"
+#endif
+
+#include "../../Tools/BugReporterWin/lib/CrashHandler.h"
+extern CrashHandler *gUnityCrashHandler;
+
+#endif
+
+ExitErrorCode gExitErrorCode = kErrorNone;
+bool gExitErrorDidCleanup = false;
+#if !UNITY_WIN
+static UNITY_TLS_VALUE(jmp_buf*) gJumpBuffer;
+#endif
+
+ExitErrorCode GetExitErrorCode()
+{
+ return gExitErrorCode;
+}
+
+#if UNITY_PEPPER
+#define STACKTRACE() ""
+#else
+#define STACKTRACE() GetStacktrace().c_str()
+#endif
+
+void ExitWithErrorCode(ExitErrorCode error)
+{
+ printf_console("Exit with error code: %d\n", error);
+ printf_console("\n========== OUTPUTING STACK TRACE ==================\n\n");
+ printf_console("%s\n", STACKTRACE());
+ printf_console("\n========== END OF STACKTRACE ===========\n\n");
+
+ #if SUPPORT_REPRODUCE_LOG
+ if (RunningReproduction())
+ {
+ ErrorString("ExitWithErrorCode invoked, exiting application because we are in reproduction mode.");
+ ReproductionExitPlayer(1);
+ }
+ #endif
+
+ #if !UNITY_RELEASE
+ printf_console("\n*** DEBUGMODE -> ExitWithError code calling abort for debugging ease *** ");
+ abort();
+ #endif
+
+ gExitErrorCode = error;
+
+ #if !UNITY_WIN
+
+ jmp_buf *buf = gJumpBuffer;
+
+ if (buf)
+ {
+ printf_console("Got error %d. Bailing out.\n", error);
+ longjmp (*buf, 1);
+ }
+ else
+ {
+ printf_console("Got error %d. Cannot exit at this point.\n", error);
+ }
+
+ #endif
+}
+
+#if !UNITY_WIN
+
+void InsertThreadExitPoint(jmp_buf *buf)
+{
+ gJumpBuffer = buf;
+}
+
+AutoRemoveEntryPoint::AutoRemoveEntryPoint (jmp_buf *buf)
+{
+ if (gJumpBuffer)
+ m_ShouldRemove = false;
+ else
+ {
+ m_ShouldRemove = true;
+ gJumpBuffer = buf;
+ }
+}
+
+AutoRemoveEntryPoint::~AutoRemoveEntryPoint ()
+{
+ if (m_ShouldRemove)
+ gJumpBuffer = NULL;
+}
+
+#endif
+
+const char* GetExitErrorString(ExitErrorCode err)
+{
+ switch(err)
+ {
+ case kErrorSecurityBreach:
+ return "The content was stopped because it\nattempted an insecure operation.";
+ case kErrorFatalException:
+ return "The content was stopped because a fatal\ncontent error has been detected.";
+ case kErrorNoSSE2Architecture:
+ return "Unity Web Player requires an SSE2 capable CPU.";
+ case kErrorIncompatibleRuntimeVersion:
+ return "The Unity Web Player content which you are trying to load was built with an older version of the Unity Editor, and is incompatible with this runtime.";
+ case kErrorUnsupportedGPU:
+ return "Unity Web Player requires DX9 level graphics card.\nMake sure you have graphics card drivers installed.";
+ default:
+ return NULL;
+ }
+}
+
+#if UNITY_WIN
+
+extern LPTOP_LEVEL_EXCEPTION_FILTER exceptionFilter;
+int HandleSignal( EXCEPTION_POINTERS* ep );
+
+DWORD OnExcept(DWORD code, LPEXCEPTION_POINTERS exception)
+{
+ __try
+ {
+ DWORD result;
+ if( NULL != exceptionFilter )
+ {
+ #if WEBPLUG
+ winutils::ProcessInternalCrash(exception, false);
+ #endif
+
+ result = exceptionFilter(exception);
+ }
+ else
+ {
+ result = mono_unity_seh_handler(exception);
+ }
+
+ if (EXCEPTION_CONTINUE_EXECUTION != result)
+ {
+ gExitErrorCode = kErrorFatalException;
+ result = EXCEPTION_EXECUTE_HANDLER;
+ }
+
+ return result;
+ }
+ __except (EXCEPTION_EXECUTE_HANDLER)
+ {
+ gExitErrorCode = kErrorFatalException;
+ return EXCEPTION_EXECUTE_HANDLER;
+ }
+}
+
+#endif
+#endif
+
diff --git a/Runtime/Utilities/ErrorExit.h b/Runtime/Utilities/ErrorExit.h
new file mode 100644
index 0000000..b531ace
--- /dev/null
+++ b/Runtime/Utilities/ErrorExit.h
@@ -0,0 +1,77 @@
+#ifndef ERROREXIT_H
+#define ERROREXIT_H
+
+#include "Configuration/UnityConfigure.h"
+
+#if SUPPORT_ERROR_EXIT
+
+enum ExitErrorCode
+{
+ kErrorNone = 0,
+ kErrorSecurityBreach = 1,
+ kErrorFatalException = 2,
+ kErrorNoSSE2Architecture = 3,
+ kErrorIncompatibleRuntimeVersion = 4,
+ kErrorUnsupportedGPU = 5,
+};
+
+extern ExitErrorCode gExitErrorCode;
+
+#if !UNITY_WIN
+#include <setjmp.h>
+void InsertThreadExitPoint(jmp_buf *buf);
+class AutoRemoveEntryPoint {
+ bool m_ShouldRemove;
+public:
+ AutoRemoveEntryPoint (jmp_buf *buf);
+ ~AutoRemoveEntryPoint ();
+};
+#endif
+void ExitWithErrorCode(ExitErrorCode error);
+ExitErrorCode GetExitErrorCode();
+const char *GetExitErrorString(ExitErrorCode err);
+
+#if UNITY_WIN
+
+DWORD OnExcept(DWORD code, LPEXCEPTION_POINTERS exception);
+
+#define UNITY_ENTRY_POINT(x) if(GetExitErrorCode()) return x; __try {
+#define UNITY_ENTRY_POINT_NO_RETURN_VALUE() if(GetExitErrorCode()) return; __try {
+#define UNITY_ENTRY_POINT_SKIP() if(!GetExitErrorCode()) { __try {
+#define UNITY_EXIT_POINT(x) } __except (OnExcept(GetExceptionCode(), GetExceptionInformation())) { return x; }
+#define UNITY_EXIT_POINT_NO_RETURN_VALUE() } __except (OnExcept(GetExceptionCode(), GetExceptionInformation())) { return; }
+#define UNITY_EXIT_POINT_SKIP() } __except (OnExcept(GetExceptionCode(), GetExceptionInformation())) { } }
+
+#define ERROR_EXIT_THREAD_ENTRY() \
+ __try \
+ {
+#define ERROR_EXIT_THREAD_EXIT() \
+ } \
+ __except (OnExcept(GetExceptionCode(), \
+ GetExceptionInformation())) \
+ { \
+ /* do nothing */ \
+ }
+
+#else
+#define UNITY_ENTRY_POINT(x) if(GetExitErrorCode()) return x; jmp_buf buf; if (setjmp (buf)) return x; AutoRemoveEntryPoint entry(&buf);
+#define UNITY_ENTRY_POINT_NO_RETURN_VALUE() if(GetExitErrorCode()) return; jmp_buf buf; if (setjmp (buf)) return; AutoRemoveEntryPoint entry(&buf);
+#define UNITY_EXIT_POINT(x)
+#define UNITY_EXIT_POINT_NO_RETURN_VALUE()
+
+#define ERROR_EXIT_THREAD_ENTRY() \
+ jmp_buf buf; \
+ if (!setjmp (buf)) \
+ { \
+ InsertThreadExitPoint (&buf);
+#define ERROR_EXIT_THREAD_EXIT() \
+ }
+
+#endif
+
+#else
+#define ExitWithErrorCode(x)
+#define GetExitErrorCode() kErrorNone
+#endif
+
+#endif
diff --git a/Runtime/Utilities/File.cpp b/Runtime/Utilities/File.cpp
new file mode 100644
index 0000000..f93e2df
--- /dev/null
+++ b/Runtime/Utilities/File.cpp
@@ -0,0 +1,831 @@
+#include "UnityPrefix.h"
+#include "File.h"
+#include "PathNameUtility.h"
+#include "PlatformDependent/Win/unistd.h"
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#if UNITY_EDITOR
+#include "FileUtilities.h"
+#include "Editor/Platform/Interface/EditorUtility.h"
+#endif
+#include "Runtime/Scripting/ScriptingUtility.h"
+#if UNITY_OSX || UNITY_IPHONE || UNITY_LINUX || UNITY_FLASH || UNITY_WEBGL || UNITY_TIZEN
+#include <sys/types.h>
+#include <sys/stat.h>
+#include "dirent.h"
+#endif
+#if UNITY_BB10
+#include <unistd.h>
+#include <dirent.h>
+#endif
+#include "Runtime/Threads/Thread.h"
+
+// This is File implementation for OSX and possibly others. Windows implementation is in PlatformDependent
+#if UNITY_WIN
+#error "Windows implementation of File is not here!"
+#endif
+
+using namespace std;
+
+#if UNITY_EDITOR
+string GetFormattedFileError (int error, const std::string& operation);
+#endif
+
+static string gCurrentDirectory;
+
+static FILE* OpenFileWithPath( const string& path, File::Permission permission )
+{
+#if SUPPORT_DIRECT_FILE_ACCESS
+ const char* fileMode = NULL;
+ switch (permission) {
+ case File::kReadPermission:
+ fileMode="rb";
+ break;
+ case File::kWritePermission:
+ fileMode="wb";
+ break;
+ case File::kReadWritePermission:
+ fileMode="r+b";
+ break;
+ case File::kAppendPermission:
+ fileMode="ab";
+ break;
+ }
+
+ return fopen( PathToAbsolutePath(path).c_str(), fileMode );
+
+#else //SUPPORT_DIRECT_FILE_ACCESS
+
+ ErrorString("No file access allowed!");
+ return NULL;
+#endif
+}
+
+bool IsAbsoluteFilePath( const std::string& path )
+{
+ if( path.empty() )
+ return false;
+
+ if( path[0] == kPlatformPathNameSeparator )
+ return true; // paths starting with separator are absolute
+
+ return false;
+}
+
+
+string PathToAbsolutePath (const string& path)
+{
+ if( IsAbsoluteFilePath(path) )
+ return path;
+ else
+ return AppendPathName (File::GetCurrentDirectory (), path);
+}
+
+bool ReadFromFile (const string& pathName, void *data, int fileStart, int byteLength)
+{
+ FILE* file = OpenFileWithPath( pathName, File::kReadPermission );
+ if (file == NULL)
+ return false;
+
+ fseek(file, 0, SEEK_END);
+
+ int length = ftell(file);
+ fseek(file, 0, SEEK_SET);
+ if (length < byteLength)
+ {
+ fclose(file);
+ return false;
+ }
+
+ int readLength = fread(data, 1, byteLength, file);
+
+ fclose(file);
+
+ if (readLength != byteLength)
+ return false;
+
+ return true;
+}
+
+
+#if !UNITY_FLASH //flash implementation in AS3Utility.cpp
+bool ReadStringFromFile (InputString* outData, const string& pathName)
+{
+ FILE* file = OpenFileWithPath( pathName, File::kReadPermission );
+ if (file == NULL)
+ return false;
+
+ fseek(file, 0, SEEK_END);
+
+ int length = ftell(file);
+ fseek(file, 0, SEEK_SET);
+ if (length < 0)
+ {
+ fclose( file );
+ return false;
+ }
+
+ outData->resize(length);
+ int readLength = fread(&*outData->begin(), 1, length, file);
+
+ fclose(file);
+
+ if (readLength != length)
+ {
+ outData->clear();
+ return false;
+ }
+
+ return true;
+}
+#endif
+
+
+bool WriteBytesToFile (const void *data, int byteLength, const string& pathName)
+{
+ File file;
+ if (!file.Open(pathName, File::kWritePermission))
+ return false;
+
+ bool success = file.Write(data, byteLength);
+
+ file.Close();
+
+ return success;
+}
+
+
+int GetFileLength (const string& pathName)
+{
+ #if UNITY_OSX || UNITY_BB10
+ struct stat statbuffer;
+ if( stat(pathName.c_str(), &statbuffer) != 0 )
+ return 0; /// return -1 for error???
+ Assert (S_ISREG(statbuffer.st_mode));
+ return statbuffer.st_size;
+ #elif UNITY_FLASH
+ return Ext_FileContainer_GetFileLength(pathName.c_str());
+ #else
+ FILE* file = OpenFileWithPath( pathName, File::kReadPermission );
+ if (file == NULL)
+ return 0; /// return -1???
+
+ fseek(file, 0, SEEK_END);
+ int length = ftell(file);
+ fclose(file);
+
+ return length;
+ #endif
+}
+
+File::File () { m_File = NULL;m_Position = 0; }
+
+File::~File () { AssertIf(m_File != NULL); }
+
+#if UNITY_EDITOR
+string GetFormattedFileError (int error, const std::string& operation)
+{
+ const char* opstr = operation.c_str();
+ switch (error)
+ {
+ case ENAMETOOLONG:
+ return Format("%s failed because file name is too long", opstr);
+ case ENOTDIR:
+ return Format("%s failed: from directory to non-directory", opstr);
+ case EISDIR:
+ return Format("%s failed: from non-directory to directory", opstr);
+ case EXDEV:
+ return Format("%s failed: to and from are on different file systems", opstr);
+ case EIO:
+ return Format("%s failed: I/O error updating directory", opstr);
+ case EROFS:
+ return Format("%s failed: read only file system", opstr);
+ case EFAULT:
+ return Format("%s failed: segmentation fault", opstr);
+ case EINVAL:
+ return Format("%s failed: from is a parent of to, or rename of . or ..", opstr);
+ case ENOTEMPTY:
+ return Format("%s failed: to is a directory and not empty", opstr);
+ case EPERM:
+ return Format("%s failed because the operation was not permitted", opstr);
+ case ENOENT:
+ return Format("%s failed because the file or directory does not exist", opstr);
+ case ENOMEM:
+ return Format("%s failed because there was not enough memory available", opstr);
+ case EACCES:
+ return Format("%s failed because permission for the file was denied", opstr);
+ case EEXIST:
+ return Format("%s failed because the file already exists", opstr);
+ case ENOSPC:
+ return Format("%s failed because there is no disk space left. Please free some disk space and continue.", opstr);
+ #if UNITY_OSX
+ case EAUTH:
+ return Format("%s failed because of an authentication failure", opstr);
+ case ENEEDAUTH:
+ return Format("%s failed because you need an authenticator", opstr);
+ case ELOOP:
+ return Format("%s failed: too many symbolic links", opstr);
+ case EDQUOT:
+ return Format("%s failed: quota limit reached", opstr);
+ #endif
+ default:
+ return Format("%s failed with error: %s", opstr, strerror(error));
+ }
+}
+
+#endif
+static bool HandleFileError (const char* title, const string& operation, FILE* file)
+{
+ #if UNITY_EDITOR
+ int err = ferror(file);
+ clearerr(file);
+
+ int result = DisplayDialogComplex (title, GetFormattedFileError(err, operation), "Try Again", "Cancel", "Force Quit");
+ if (result == 1)
+ return false;
+ else if (result == 2)
+ exit(1);
+ else
+ return true;
+ #else
+ return false;
+ #endif
+}
+
+bool File::Open (const std::string& path, File::Permission perm, AutoBehavior behavior)
+{
+ Close();
+ m_Path = path;
+
+ int retryCount = 5;
+
+ while (true)
+ {
+ m_File = OpenFileWithPath( path, perm );
+ m_Position = 0;
+ if (m_File != NULL)
+ {
+ if (perm == kAppendPermission)
+ m_Position = ftell(m_File);
+ return true;
+ }
+ else
+ {
+#if SUPPORT_THREADS
+ if ( (behavior & kRetryOnOpenFail) && (--retryCount > 0))
+ {
+ Thread::Sleep(0.2);
+ continue;
+ }
+#endif
+
+ if ( behavior & kSilentReturnOnOpenFail )
+ return false;
+
+ #if UNITY_EDITOR
+ int result = DisplayDialogComplex ("Opening file failed", GetFormattedFileError(errno, "Opening file "+path), "Try Again", "Cancel", "Force Quit");
+ if (result == 1)
+ return false;
+ else if (result == 2)
+ exit(1);
+ #else
+ ErrorString("Failed to open file at path: " + path);
+ return false;
+ #endif
+ }
+ }
+ return false;
+}
+
+bool File::Close ()
+{
+ if (m_File != NULL)
+ {
+ if (fclose(m_File) != 0)
+ {
+ #if UNITY_EDITOR
+ ErrorString(GetFormattedFileError(errno, "Closing file " + m_Path));
+ #elif UNITY_FLASH
+ //no problemo
+ #else
+ ErrorString("Closing file " + m_Path);
+ #endif
+ }
+ m_File = NULL;
+ }
+
+ m_Path.clear();
+ return true;
+}
+
+int File::Read (void* buffer, int size)
+{
+ if (m_File)
+ {
+ int s = fread(buffer, 1, size, m_File);
+
+ if (s == size || ferror(m_File) == 0)
+ {
+ m_Position += s;
+ return s;
+ }
+ else
+ {
+ //m_Position = -1;
+ if (!HandleFileError("Reading file failed", "Reading from file " + m_Path, m_File))
+ return false;
+
+ // We don't know how far the file was read.
+ // So we just play safe and go through the API that seeks from a specific offset
+ int oldPos = m_Position;
+ m_Position = -1;
+ return Read(oldPos, buffer, size);
+ }
+ }
+ else
+ {
+ ErrorString("Reading failed because the file was not opened");
+ return 0;
+ }
+}
+
+int File::Read (int position, void* buffer, int size)
+{
+ if (m_File)
+ {
+ while (true)
+ {
+ // Seek if necessary
+ if (position != m_Position)
+ {
+ if (fseek(m_File, position, SEEK_SET) != -1)
+ m_Position = position;
+ else
+ {
+ m_Position = -1;
+ if (!HandleFileError("Reading file failed", "Seeking in file " + m_Path, m_File ))
+ return 0;
+
+ continue;
+ }
+ }
+
+ int s = fread(buffer, 1, size, m_File);
+ if (s == size || ferror(m_File) == 0)
+ {
+ m_Position += s;
+ return s;
+ }
+ else
+ {
+ m_Position = -1;
+ if (!HandleFileError("Reading file failed", "Reading from file " + m_Path, m_File ))
+ return 0;
+ }
+ }
+ }
+ else
+ {
+ ErrorString("Reading failed because the file was not opened");
+ return 0;
+ }
+ return 0;
+}
+
+
+
+bool File::Write (const void* buffer, int size)
+{
+ if (m_File)
+ {
+ int s = fwrite(buffer, 1, size, m_File);
+ if (s == size)
+ {
+ m_Position += s;
+ return true;
+ }
+ else
+ {
+ if (!HandleFileError("Writing file failed", "Writing to file " + m_Path, m_File))
+ return false;
+
+ // We don't know how far the file was read.
+ // So we just play safe and go through the API that seeks from a specific offset
+ int oldPos = m_Position;
+ m_Position = -1;
+ return Write(oldPos, buffer, size);
+ }
+ }
+ else
+ {
+ ErrorString("Writing failed because the file was not opened");
+ return false;
+ }
+}
+
+bool File::Write (int position, const void* buffer, int size)
+{
+ if (m_File)
+ {
+ while (true)
+ {
+ // Seek if necessary
+ if (position != m_Position)
+ {
+ if (fseek(m_File, position, SEEK_SET) != -1)
+ m_Position = position;
+ else
+ {
+ m_Position = -1;
+ if (!HandleFileError("Writing file failed", "Seeking in file "+m_Path, m_File))
+ return false;
+
+ continue;
+ }
+ }
+
+ int s = fwrite(buffer, 1, size, m_File);
+ if (s == size)
+ {
+ m_Position += s;
+ return true;
+ }
+ else
+ {
+ m_Position = -1;
+ if (!HandleFileError("Writing file failed", "Writing to file "+m_Path, m_File))
+ return false;
+ }
+ }
+ }
+ else
+ {
+ ErrorString("Writing failed because the file was not opened");
+ }
+ return false;
+}
+
+bool File::SetFileLength (int size)
+{
+ return ::SetFileLength(m_Path, size);
+}
+
+int File::GetFileLength()
+{
+ return ::GetFileLength(m_Path);
+}
+
+void File::SetCurrentDirectory (const string& path)
+{
+ gCurrentDirectory = path;
+}
+
+const string& File::GetCurrentDirectory ()
+{
+ return gCurrentDirectory;
+}
+
+void File::CleanupClass()
+{
+ gCurrentDirectory = string();
+}
+
+bool SetFileLength (const std::string& path, int size)
+{
+ #if UNITY_PEPPER || UNITY_FLASH || UNITY_WEBGL
+ ErrorString("SetFileLength not supported on this platform!");
+ return false;
+ #else
+ while (true)
+ {
+ int error = truncate(path.c_str(), size);
+ if (error == 0)
+ return true;
+
+ #if UNITY_EDITOR
+ int result = DisplayDialogComplex ("Writing file error", GetFormattedFileError(errno, "Resizing file " + path), "Try Again", "Cancel", "Quit");
+ if (result == 1)
+ return false;
+ else if (result == 2)
+ exit(1);
+ #else
+ return false;
+ #endif
+ }
+ #endif
+
+ return true;
+}
+
+static bool IsFileCreatedAtAbsolutePath (const string & path)
+{
+ #if UNITY_OSX || UNITY_IPHONE || UNITY_LINUX || UNITY_FLASH || UNITY_WEBGL || UNITY_BB10 || UNITY_TIZEN
+
+ struct stat status;
+
+ if (stat(path.c_str(), &status) != 0)
+ return false;
+
+#if TARGET_IPHONE_SIMULATOR
+ // some bad hack for simulator, if we can stat it then file exists...
+ return true;
+#endif
+
+ return S_ISREG (status.st_mode);
+
+ #elif !SUPPORT_DIRECT_FILE_ACCESS
+
+ ErrorString("No file access allowed!");
+ return false;
+
+ #else
+
+ #error "Unsupported platform!"
+
+ #endif
+}
+
+#if !UNITY_FLASH //Flash implementation in AS3Utility.cpp
+bool IsFileCreated (const string& path)
+{
+ return IsFileCreatedAtAbsolutePath (PathToAbsolutePath (path));
+}
+#endif
+
+
+#if UNITY_OSX || UNITY_IPHONE || UNITY_PEPPER || UNITY_LINUX || UNITY_BB10 || UNITY_TIZEN
+bool IsFileOrDirectoryInUse ( const string& path )
+{
+#if UNITY_LINUX || UNITY_BB10 || UNITY_TIZEN
+ return false;
+#elif !UNITY_PEPPER
+ bool isUsed = false;
+
+ if ( IsDirectoryCreated( path ) )
+ {
+ set<string> paths;
+ if ( GetFolderContentsAtPath( path, paths ) )
+ {
+ for (set<string>::iterator i = paths.begin(); i != paths.end(); i++)
+ {
+ if ( IsFileOrDirectoryInUse( *i ) )
+ return true;
+ }
+ }
+ }
+ else if ( IsFileCreated( path ) )
+ {
+ File openTest;
+ if ( openTest.Open( path, File::kReadPermission ) )
+ {
+ if ( !openTest.Lock( File::kExclusive, false ) )
+ isUsed = true;
+ openTest.Lock( File::kNone, false );
+ openTest.Close();
+ }
+ else
+ isUsed = true;
+ }
+
+ return isUsed;
+#else
+ ErrorString("No file access allowed!");
+ return false;
+#endif
+}
+#endif
+
+static bool IsDirectoryCreatedAtAbsolutePath (const string & path)
+{
+ #if UNITY_OSX || UNITY_LINUX || UNITY_IPHONE || UNITY_BB10 || UNITY_WEBGL || UNITY_TIZEN
+ struct stat status;
+ if (stat(path.c_str(), &status) != 0)
+ return false;
+ return S_ISDIR(status.st_mode);
+
+ #elif UNITY_FLASH
+ #warning "IsDirectoryCreatedAtAbsolutePath() NOT implemented for UNITY_FLASH"
+ return false;
+
+ #elif !SUPPORT_DIRECT_FILE_ACCESS
+
+ ErrorString("No file access allowed!");
+ return false;
+ #else
+ #error Unknown platform
+ #endif
+}
+
+bool IsDirectoryCreated (const string& path)
+{
+ return IsDirectoryCreatedAtAbsolutePath (PathToAbsolutePath (path));
+}
+
+bool CreateDirectory (const string& pathName)
+{
+#if SUPPORT_DIRECT_FILE_ACCESS && !UNITY_FLASH
+ if (IsFileCreated (pathName))
+ return false;
+ else if (IsDirectoryCreated (pathName))
+ return true;
+
+ string absolutePath = PathToAbsolutePath (pathName);
+
+#if UNITY_EDITOR
+ std::string fileNamePart = GetLastPathNameComponent (absolutePath);
+ if (CheckValidFileNameDetail (fileNamePart) == kFileNameInvalid)
+ {
+ ErrorStringMsg ("%s is not a valid directory name. Please make sure there are no unallowed characters in the name.", fileNamePart.c_str ());
+ return false;
+ }
+#endif
+
+ int res = mkdir(absolutePath.c_str(), 0755);
+ return res == 0;
+#else
+ ErrorString("No file access allowed!");
+ return false;
+#endif
+}
+
+bool DeleteFile (const string& path)
+{
+#if !UNITY_PEPPER && !UNITY_FLASH && !UNITY_WEBGL
+ string absolutePath = PathToAbsolutePath (path);
+ if (IsFileCreated (absolutePath))
+ {
+ return unlink(absolutePath.c_str()) == 0;
+ }
+ else
+ {
+ return false;
+ }
+#else
+ ErrorString("No file access allowed!");
+ return false;
+#endif
+}
+
+int DeleteFilesAndDirsRecursive(const string& path)
+{
+#if SUPPORT_DIRECT_FILE_ACCESS && !UNITY_FLASH && !UNITY_WEBGL
+ int res;
+
+ struct stat status;
+ res = lstat(path.c_str(), &status);
+ if (res != 0)
+ return res;
+
+ if (S_ISDIR(status.st_mode) && !S_ISLNK(status.st_mode))
+ {
+ DIR *dirp = opendir (path.c_str());
+ if (dirp == NULL)
+ return -1;
+
+ struct dirent *dp;
+ while ( (dp = readdir(dirp)) )
+ {
+ if (strcmp(dp->d_name, ".") != 0 && strcmp(dp->d_name, "..") != 0)
+ {
+ string name = dp->d_name;
+ res = DeleteFilesAndDirsRecursive(AppendPathName (path, name));
+ if (res != 0)
+ {
+ closedir(dirp);
+ return res;
+ }
+ }
+ }
+ closedir(dirp);
+
+ res = rmdir(path.c_str());
+ }
+ else
+ {
+ res = unlink(path.c_str());
+ }
+
+ return res;
+#else
+ ErrorString("No file access allowed!");
+ return false;
+#endif
+}
+
+bool DeleteFileOrDirectory (const string& path)
+{
+ int res = DeleteFilesAndDirsRecursive(PathToAbsolutePath(path));
+ return res == 0;
+}
+
+bool ShouldIgnoreFile (const char* name, size_t length)
+{
+ AssertIf(!name);
+ if (length < 1)
+ return true;
+
+ // ignore hidden files
+ if (name[0] == '.')
+ return true;
+ // ignore CVS files
+ if (StrICmp(name, "cvs") == 0)
+ return true;
+
+ // ignore temporary and backup files
+ if ((name[length-1] == '~') || (length >= 4 && StrICmp(name + length - 4, ".tmp" ) == 0))
+ return true;
+
+ return false;
+}
+
+bool GetFolderContentsAtPath (const string& pathName, set<string>& paths)
+{
+#if SUPPORT_DIRECT_FILE_ACCESS && !UNITY_FLASH
+ string absolutePath = PathToAbsolutePath(pathName);
+ DIR *dirp;
+ struct dirent *dp;
+
+ if ((dirp = opendir(absolutePath.c_str())) == NULL)
+ return false;
+
+ while ( (dp = readdir(dirp)) )
+ {
+ if (strcmp(dp->d_name, ".") != 0 && strcmp(dp->d_name, "..") != 0)
+ {
+ if (!ShouldIgnoreFile (dp->d_name, strlen (dp->d_name)))
+ {
+ string name = dp->d_name;
+ paths.insert (AppendPathName (pathName, name));
+ }
+ }
+ }
+ closedir(dirp);
+ return true;
+#else
+ ErrorString("No file access allowed!");
+ return false;
+#endif
+}
+
+#if UNITY_EDITOR
+
+bool MoveReplaceFile (const string& fromPath, const string& toPath)
+{
+ if( IsDirectoryCreated(fromPath) )
+ {
+ ErrorString( Format("Path %s is a directory", fromPath.c_str()) );
+ return false;
+ }
+ while (true)
+ {
+ int error = rename( PathToAbsolutePath(fromPath).c_str(), PathToAbsolutePath(toPath).c_str() );
+ if( error == 0 )
+ return true;
+
+ int errorCode = errno;
+ if( errorCode == EXDEV )
+ {
+ DeleteFileOrDirectory(toPath);
+ if (CopyFileOrDirectory(fromPath, toPath))
+ {
+ if (DeleteFileOrDirectory(fromPath))
+ return true;
+ }
+ }
+
+ #if UNITY_EDITOR
+ int result = DisplayDialogComplex ("Moving file failed", GetFormattedFileError(errno, "Moving "+fromPath+" to "+toPath), "Try Again", "Cancel", "Force Quit");
+ if (result == 1)
+ return false;
+ else if (result == 2)
+ exit(1);
+ #else
+ return false;
+ #endif
+ }
+ return false;
+}
+
+#endif
+
+#if UNITY_OSX || UNITY_IPHONE
+bool File::Lock(File::LockMode mode, bool block)
+{
+ int fd = fileno (m_File);
+ return flock (fd, mode + (block?0:LOCK_NB) ) == 0;
+}
+
+#include <sys/resource.h>
+//Local testing and various internet sources suggest that this is the absolute maximim of open files OSX can hande.
+#define kMaxOpenFile 10240
+
+void SetupFileLimits ()
+{
+ struct rlimit limit;
+ limit.rlim_cur = kMaxOpenFile;
+ limit.rlim_max = kMaxOpenFile;
+ if (setrlimit(RLIMIT_NOFILE, &limit) != 0)
+ printf_console("setrlimit() failed with errno=%d\n", errno);
+}
+#endif
diff --git a/Runtime/Utilities/File.h b/Runtime/Utilities/File.h
new file mode 100644
index 0000000..b033278
--- /dev/null
+++ b/Runtime/Utilities/File.h
@@ -0,0 +1,194 @@
+#pragma once
+
+#include "Runtime/Misc/Allocator.h"
+#if UNITY_OSX || UNITY_IPHONE || UNITY_ANDROID || UNITY_LINUX || UNITY_BB10 || UNITY_TIZEN
+#include "fcntl.h"
+#endif
+#include "Runtime/Utilities/NonCopyable.h"
+#include "Runtime/Utilities/PathNameUtility.h"
+#include <set>
+#include "Runtime/Utilities/EnumFlags.h"
+
+#if UNITY_WII
+#include <rvlaux/file-io.h>
+#endif
+
+#if UNITY_WINRT
+// Enable only one
+#define ENABLE_CRT_IO 0
+#define ENABLE_WIN_IO 1
+#endif
+
+class File : public NonCopyable
+{
+ int m_Position;
+ std::string m_Path;
+#if UNITY_WII
+ bool m_Open;
+ WiiFileInfo m_File;
+#elif UNITY_WINRT
+ //Microsoft::WRL::ComPtr<IInspectable> m_Stream;
+# if ENABLE_CRT_IO
+ FILE* m_File;
+# elif ENABLE_WIN_IO
+ HANDLE m_FileHandle;
+# else
+ std::vector<unsigned char> m_Data;
+# endif
+#elif UNITY_WIN || UNITY_XENON
+ HANDLE m_FileHandle;
+#elif UNITY_PS3
+ int m_FileDescriptor;
+#else
+ FILE* m_File;
+#endif
+
+public:
+
+ File ();
+ ~File ();
+
+ enum Permission { kReadPermission = 0, kWritePermission = 1, kReadWritePermission = 2, kAppendPermission = 3 };
+ enum AutoBehavior { kNormalBehavior = 0, kSilentReturnOnOpenFail = 1<<0, kRetryOnOpenFail = 1<<1};
+
+ bool Open (const std::string& path, Permission perm, AutoBehavior behavior = kNormalBehavior);
+ bool Close ();
+
+ int Read (void* buffer, int size);
+ int Read (int position, void* buffer, int size);
+
+ bool Write (const void* buffer, int size);
+ bool Write (int pos, const void* buffer, int size);
+ bool SetFileLength (int size);
+ int GetFileLength ();
+ int GetPosition () const { return m_Position; }
+
+ static void SetCurrentDirectory (const std::string& path);
+ static const std::string& GetCurrentDirectory ();
+ static void CleanupClass();
+
+#if UNITY_OSX || UNITY_IPHONE || UNITY_ANDROID || UNITY_LINUX || UNITY_BB10 || UNITY_TIZEN
+ enum LockMode {
+ kNone = LOCK_UN,
+ kShared = LOCK_SH,
+ kExclusive = LOCK_EX
+ };
+ bool Lock(File::LockMode mode, bool block);
+#endif
+};
+
+ENUM_FLAGS(File::AutoBehavior);
+
+/// Returns the file length of the file at pathName. If the file doesn't exist returns -1.
+int GetFileLength (const std::string& pathName);
+size_t GetFileSize (FILE* file);
+
+
+std::string PathToAbsolutePath (const std::string& path);
+// Path is absolute in platform dependent way!
+bool IsAbsoluteFilePath( const std::string& path );
+
+/// Reads the contents of the file at pathName into data.
+/// byteStart is the first byte read in the file, byteLength is the number of bytes that should be read
+bool ReadFromFile (const std::string& pathName, void *data, int fileStart, int byteLength);
+
+bool WriteBytesToFile (const void *data, int byteLength, const std::string& pathName);
+
+#if UNITY_WII
+typedef std::basic_string<char, std::char_traits<char>, STL_ALLOCATOR_ALIGNED(kMemIOTemp, char, 32)> InputString;
+#else
+typedef TEMP_STRING InputString;
+#endif
+bool ReadStringFromFile (InputString* outData, const std::string& pathName);
+
+bool IsFileCreated (const std::string& path);
+bool IsFileOrDirectoryInUse( const std::string& path );
+bool CreateDirectory (const std::string& pathName);
+
+/// Returns a set of files and folders that are children of the folder at pathname.
+/// Ignores invisible files
+bool GetFolderContentsAtPath (const std::string& pathName, std::set<std::string>& paths);
+
+/// Deletes a single file. Returns true if the file could be deleted.
+/// Returns false if the file couldnt be deleted or the file was a folder.
+bool DeleteFile (const std::string& file);
+bool DeleteFileOrDirectory (const std::string& path);
+
+bool SetFileLength (const std::string& path, int size);
+#if UNITY_WIN
+bool SetFileLength (const HANDLE hFile, const std::string& path, int size);
+bool GetFolderContentsAtPathImpl( const std::wstring& pathNameWide, std::set<std::string>& paths, bool deep );
+#endif
+bool IsDirectoryCreated (const std::string& path);
+
+#if UNITY_EDITOR
+bool MoveReplaceFile (const std::string& fromPath, const std::string& toPath);
+#if UNITY_OSX || UNITY_LINUX
+std::string GetFormattedFileError (int error, const std::string& operation);
+#endif
+#endif
+
+#if !UNITY_EXTERNAL_TOOL
+inline bool CreateDirectoryRecursive (const std::string& pathName)
+{
+ if (IsDirectoryCreated(DeleteLastPathNameComponent(pathName)))
+ {
+ if (IsDirectoryCreated (pathName))
+ return true;
+ else if (IsFileCreated (pathName))
+ return false;
+ else
+ return CreateDirectory (pathName);
+ }
+
+ std::string::size_type pos = pathName.rfind ("/");
+ if (pos == std::string::npos)
+ return false;
+
+ std::string superPath;
+ superPath.assign (pathName.begin (), pathName.begin () + pos);
+
+ if (!CreateDirectoryRecursive (superPath))
+ return false;
+
+ return CreateDirectoryRecursive (pathName);
+}
+#endif
+
+// On Vista, its search indexer can wreak havoc on files that are written then renamed. Full story here:
+// http://stackoverflow.com/questions/153257/random-movefileex-failures-on-vista-access-denied-looks-like-caused-by-search-i
+// but in short, whenever a file is temporary or should be excluded from search indexing, we have to set those flags.
+enum FileFlags {
+ kFileFlagTemporary = (1<<0), // File is meant to be temporary.
+ kFileFlagDontIndex = (1<<1), // Exclude file from search indexing (Spotlight, Vista Search, ...)
+ kFileFlagHidden = (1<<2), // Set file as hidden file.
+
+ kAllFileFlags = kFileFlagTemporary | kFileFlagDontIndex
+};
+
+#if UNITY_WIN
+bool SetFileFlags (const std::string& path, UInt32 attributeMask, UInt32 attributeValue);
+
+bool isHiddenFile (const std::string& path);
+
+#elif UNITY_EDITOR
+bool SetFileFlags (const std::string& path, UInt32 attributeMask, UInt32 attributeValue);
+
+bool isHiddenFile (const std::string& path);
+
+#else
+inline bool SetFileFlags (const std::string& path, UInt32 attributeMask, UInt32 attributeValue)
+{
+ return true;
+}
+
+inline bool isHiddenFile (const std::string& path)
+{
+ return false;
+}
+#endif
+
+#if UNITY_OSX
+void SetupFileLimits ();
+#endif
+
diff --git a/Runtime/Utilities/FileStripped.h b/Runtime/Utilities/FileStripped.h
new file mode 100644
index 0000000..c6590c6
--- /dev/null
+++ b/Runtime/Utilities/FileStripped.h
@@ -0,0 +1,10 @@
+
+#ifndef __FILE_STRIPPED__
+
+#if ENABLE_PROFILER || DEBUGMODE
+#define __FILE_STRIPPED__ __FILE__
+#else
+#define __FILE_STRIPPED__ ""
+#endif
+
+#endif \ No newline at end of file
diff --git a/Runtime/Utilities/FileUtilities.cpp b/Runtime/Utilities/FileUtilities.cpp
new file mode 100644
index 0000000..6ae5cb0
--- /dev/null
+++ b/Runtime/Utilities/FileUtilities.cpp
@@ -0,0 +1,27 @@
+#include "UnityPrefix.h"
+#include "Runtime/Utilities/FileUtilities.h"
+#include "Runtime/Utilities/GUID.h"
+
+
+using namespace std;
+
+
+#if UNITY_HAVE_GUID_INIT
+
+string GetUniqueTempPath (const std::string& basePath)
+{
+ while (true)
+ {
+ UnityGUID guid; guid.Init();
+ string tmpFilePath = basePath + GUIDToString(guid);
+ if (!IsFileCreated(tmpFilePath))
+ return tmpFilePath;
+ }
+}
+
+string GetUniqueTempPathInProject ()
+{
+ return GetUniqueTempPath ("Temp/UnityTempFile-");
+}
+
+#endif
diff --git a/Runtime/Utilities/FileUtilities.h b/Runtime/Utilities/FileUtilities.h
new file mode 100644
index 0000000..c9589c2
--- /dev/null
+++ b/Runtime/Utilities/FileUtilities.h
@@ -0,0 +1,113 @@
+#ifndef FILEUTILITIES_H
+#define FILEUTILITIES_H
+
+#include <list>
+#include <string>
+#include <set>
+#include "PathNameUtility.h"
+#include "DateTime.h"
+#include "File.h"
+
+using std::string;
+
+#if UNITY_EDITOR
+
+/// Returns the real pathname of path
+/// (eg. if pathname is all lowercase, the returned string will match the actual pathname)
+/// If there is no file at path returns path.
+std::string GetActualPathSlow (const std::string & input);
+std::string GetActualAbsolutePathSlow (const std::string & path);
+
+// Copies a file or folder. Resolves all symlinks in from before copying.
+bool CopyFileOrDirectory (const string& from, const string& to);
+bool MoveToTrash (const string& path);
+bool CopyReplaceFile (string from, string to);
+bool MoveFileOrDirectory (const string& fromPath, const string& toPath);
+
+string GetProjectRelativePath (const string& path);
+
+bool CreateDirectory (const string& pathName);
+
+/// Returns a set of files and folders that are deep children of the folder at pathname.
+/// Ignores invisible files
+bool GetDeepFolderContentsAtPath (const string& pathName, std::set<string>& paths);
+
+/// Returns the content modification date for the file at path or zero timestamp if file couldn't be found
+DateTime GetContentModificationDate (const string& path);
+
+/// Sets the content modification date (write time) for the file at path
+bool SetContentModificationDateToCurrentTime (const string& path);
+
+bool CreateDirectorySafe (const string& pathName);
+
+std::string GenerateUniquePath (std::string inPath);
+std::string GenerateUniquePathSafe (const std::string& pathName);
+std::string GetUniqueTempPathInProject ();
+std::string GetUniqueTempPath (const std::string& basePath);
+
+bool IsPathCreated (const std::string& path);
+
+#if UNITY_OSX || UNITY_LINUX
+inline void UnixTimeToUnityTime (time_t time, DateTime &dateTime )
+{
+ // Seconds from 1904 to 2001: 3061152000 (kCFAbsoluteTimeIntervalSince1904)
+ // Seconds from 1970 to 2001: 978307200 (kCFAbsoluteTimeIntervalSince1970)
+ // Thus seconds from 1904 to 1970: 2082844800
+ UInt64 seconds = time - 2082844800;
+ dateTime.fraction = 0;
+ dateTime.lowSeconds = seconds & 0xFFFFFFFF;
+ dateTime.highSeconds = seconds >> 32;
+}
+#endif
+
+#if UNITY_OSX
+bool PathToFSSpec (const std::string& path, FSSpec* spec);
+#endif
+
+#if UNITY_WIN
+void FileTimeToUnityTime( const FILETIME& ft, DateTime& dateTime );
+bool CopyFileOrDirectoryCheckPermissions (const string& from, const string& to, bool &accessdenied);
+#endif
+
+enum AtomicWriteMode {
+ kProjectTempFolder = 0,
+ #if UNITY_OSX
+ kSystemTempFolder = 1,
+ #endif
+ kNotAtomic = 2,
+};
+bool WriteStringToFile (const string& inData, const string& pathName, AtomicWriteMode mode, UInt32 fileFlags);
+
+bool IsSymlink (const std::string& pathName);
+
+// Resolve symlinks & canonicalize the path
+std::string ResolveSymlinks (const std::string& pathName);
+
+std::string GetUserAppDataFolder ();
+std::string GetUserAppCacheFolder ();
+
+#endif // UNITY_EDITOR
+
+string GetApplicationPath ();
+string GetApplicationContentsPath ();
+string GetApplicationFolder ();
+string GetApplicationLocalFolder();
+string GetApplicationManagedPath();
+string GetApplicationTemporaryFolder();
+
+#if UNITY_STANDALONE || UNITY_EDITOR
+string GetApplicationNativeLibsPath();
+#endif
+
+bool CreateFile (const string& path, int creator = '?', int fileType = '?');
+
+#if UNITY_LINUX || UNITY_BB10 || UNITY_TIZEN
+void SetApplicationPath( std::string path );
+bool IsSymlink(const std::string &pathName);
+std::string ResolveSymlink(const std::string& pathName);
+#if !UNITY_BB10 && !UNITY_TIZEN
+std::string GetUserConfigFolder ();
+#endif
+#endif
+
+#endif
diff --git a/Runtime/Utilities/GLSLUtilities.cpp b/Runtime/Utilities/GLSLUtilities.cpp
new file mode 100644
index 0000000..2152ef3
--- /dev/null
+++ b/Runtime/Utilities/GLSLUtilities.cpp
@@ -0,0 +1,835 @@
+#include "UnityPrefix.h"
+#include "GLSLUtilities.h"
+#include "External/shaderlab/Library/ShaderLabErrors.h"
+
+#if UNITY_ANDROID || UNITY_IPHONE
+ #include "Runtime/Shaders/GraphicsCaps.h"
+#endif
+
+using namespace std;
+
+// --------------------------------------------------------------------------
+
+
+void OutputGLSLShaderError(const char* log, GLSLErrorType errorType, ShaderErrors& outErrors)
+{
+
+ const char* strErrorIds[kErrorCount] =
+ {
+ "GLSL Error in Vertex Shader: ",
+ "GLSL Error in Fragment Shader: ",
+ "GLSL Error while linking: "
+ };
+
+ string errorMessage = string(strErrorIds[errorType]) + log;
+
+ int errorLine = 0;
+ switch(errorType)
+ {
+ case kErrorCompileVertexShader:
+ case kErrorCompileFragShader:
+ {
+ size_t b = errorMessage.find('(');
+ size_t e = errorMessage.find(')');
+ if(b != string::npos && e != string::npos && e>b)
+ {
+ string errorLineText = errorMessage.substr(b + 1, e - b - 1);
+ errorLine = StringToInt(errorLineText);
+ }
+
+ outErrors.AddShaderError (errorMessage, errorLine, false);
+ }
+ break;
+ case kErrorLinkProgram:
+ errorMessage += log;
+ outErrors.AddShaderError (errorMessage, errorLine, false);
+ break;
+ default:
+ break;
+ }
+}
+
+
+// --------------------------------------------------------------------------
+// GLSL patching for fog
+
+#define DEBUG_FOG_PATCHING 0
+
+static inline bool IsNewline( char c ) { return c == '\n' || c == '\r'; }
+
+static size_t FindStartOfBlock (const std::string& src, const std::string& token)
+{
+ size_t pos = src.find (token);
+ if (pos == std::string::npos)
+ return pos;
+ const size_t n = src.size();
+ while (pos < n && !IsNewline(src[pos])) ++pos; // skip until newline
+ while (pos < n && IsNewline(src[pos])) ++pos; // skip newlines
+ if (pos >= n)
+ return std::string::npos;
+ return pos;
+}
+
+static bool SkipUntilStatementEnd (const std::string& src, size_t& pos)
+{
+ const size_t n = src.size();
+ while (pos < n && src[pos] != ';') ++pos; if (pos < n) ++pos;
+ while (pos < n && IsNewline(src[pos])) ++pos; // skip following newlines
+ return pos < n;
+}
+
+static void SkipUntilStatementBegin (const std::string& src, size_t& pos)
+{
+ const size_t n = src.size();
+ while (pos > 0 && src[pos] != ';' && src[pos] != '{') --pos; if (pos < n) ++pos;
+ while (pos < n && IsSpace(src[pos])) ++pos; // skip following whitespace
+}
+
+static size_t SkipGLSLDirectives (const std::string& src, size_t startPos = 0)
+{
+ size_t pos = startPos;
+ const size_t n = src.size();
+ while (pos < n)
+ {
+ // skip whitespace
+ while (pos < n && IsSpace(src[pos])) ++pos;
+ // have a '#'?
+ if (pos >= n || src[pos] != '#')
+ break;
+ // skip until end of line
+ while (pos < n && !IsNewline(src[pos])) ++pos;
+ }
+ return pos;
+}
+
+
+static int ParseGLSLVersion (const std::string& src, size_t startPos = 0)
+{
+ size_t pos = startPos;
+ const size_t n = src.size();
+ // skip whitespace
+ while (pos < n && IsSpace(src[pos])) ++pos;
+ // have a '#'?
+ if (pos >= n || src[pos] != '#')
+ return 0;
+ ++pos;
+ // skip whitespace
+ while (pos < n && IsSpace(src[pos])) ++pos;
+ // have a "version"?
+ if (0 != strncmp(src.c_str() + pos, "version", 7))
+ return 0;
+ pos += 7;
+ // skip whitespace
+ while (pos < n && IsSpace(src[pos])) ++pos;
+ // parse a number
+ int v = atoi(src.c_str() + pos);
+ return v;
+}
+
+static const char* GetGLSLFogData (int version, bool vertex)
+{
+ if (version >= 150)
+ {
+ if (vertex)
+ return "uniform vec4 _unity_FogColor; uniform vec4 _unity_FogParams;\nout float _unity_FogVar;\n";
+ else
+ return "uniform vec4 _unity_FogColor; uniform vec4 _unity_FogParams;\nin float _unity_FogVar;\n";
+ }
+ return "uniform vec4 _unity_FogColor; uniform vec4 _unity_FogParams;\nvarying float _unity_FogVar;\n";
+}
+
+static bool InsertFogVertexCode (std::string& src)
+{
+ size_t vertexStart = FindStartOfBlock (src, "#ifdef VERTEX");
+ if (vertexStart == std::string::npos)
+ return false;
+ const int version = ParseGLSLVersion (src, vertexStart);
+ vertexStart = SkipGLSLDirectives (src, vertexStart);
+
+ const char* fogData = GetGLSLFogData(version, true);
+ src.insert (vertexStart, fogData);
+
+ size_t posWriteStart = src.find ("gl_Position", vertexStart);
+ if (posWriteStart == std::string::npos)
+ return false;
+
+ size_t pos = posWriteStart;
+ if (!SkipUntilStatementEnd (src, pos))
+ return false;
+
+ // insert fog code at pos!
+ src.insert (pos, "_unity_FogVar = gl_Position.z;\n");
+
+ return true;
+}
+
+static bool InsertFogFragmentCode (std::string& src, FogMode fog)
+{
+ size_t fragmentStart = FindStartOfBlock (src, "#ifdef FRAGMENT");
+ if (fragmentStart == std::string::npos)
+ return false;
+ const int version = ParseGLSLVersion (src, fragmentStart);
+ fragmentStart = SkipGLSLDirectives (src, fragmentStart);
+
+ const char* fogData = GetGLSLFogData(version, false);
+ src.insert (fragmentStart, fogData);
+
+ bool writesToData = true; // writes to gl_FragData vs. gl_FragColor
+ size_t colorWriteStart = src.find ("gl_FragData[0]", fragmentStart);
+ if (colorWriteStart == std::string::npos)
+ {
+ colorWriteStart = src.find ("gl_FragColor", fragmentStart);
+ if (colorWriteStart == std::string::npos)
+ return false;
+ writesToData = false;
+ }
+
+ size_t pos = colorWriteStart;
+ if (!SkipUntilStatementEnd (src, pos))
+ return false;
+
+ // insert fog code at pos!
+ std::string color = writesToData ? "gl_FragData[0]" : "gl_FragColor";
+ if (fog == kFogExp2)
+ {
+ // fog = exp(-(density*z)^2)
+ src.insert (pos,
+ " float _patchFog = _unity_FogParams.x * _unity_FogVar;\n"
+ " _patchFog = _patchFog * _patchFog;\n"
+ " _patchFog = clamp (exp2(-_patchFog), 0.0, 1.0);\n"
+ " " + color + ".rgb = mix (_unity_FogColor.rgb, " + color + ".rgb, _patchFog);\n");
+ }
+ else if (fog == kFogExp)
+ {
+ // fog = exp(-density*z)
+ src.insert (pos,
+ " float _patchFog = _unity_FogParams.y * _unity_FogVar;\n"
+ " _patchFog = clamp (exp2(-_patchFog), 0.0, 1.0);\n"
+ " " + color + ".rgb = mix (_unity_FogColor.rgb, " + color + ".rgb, _patchFog);\n");
+ }
+ else if (fog == kFogLinear)
+ {
+ // fog = (end-z)/(end-start)
+ src.insert (pos,
+ " float _patchFog = clamp (_unity_FogParams.z * _unity_FogVar + _unity_FogParams.w, 0.0, 1.0);\n"
+ " " + color + ".rgb = mix (_unity_FogColor.rgb, " + color + ".rgb, _patchFog);\n");
+ }
+
+ return true;
+}
+
+bool PatchShaderFogGLSL (std::string& src, FogMode fog)
+{
+ #if DEBUG_FOG_PATCHING
+ printf_console ("GLSL fog patching: original shader:\n%s\n", src.c_str());
+ #endif
+
+ if (!InsertFogVertexCode (src))
+ return false;
+ if (!InsertFogFragmentCode (src, fog))
+ return false;
+
+ #if DEBUG_FOG_PATCHING
+ printf_console ("GLSL fog patching: after patching, fog mode %d:\n%s\n", fog, src.c_str());
+ #endif
+ return true;
+}
+
+
+
+static bool InsertFogVertexCodeGLES (std::string& src, FogMode fog, bool canUseOptimizedCode)
+{
+ size_t posWriteStart = src.find ("gl_Position");
+ if (posWriteStart == std::string::npos)
+ return false;
+
+ size_t pos = posWriteStart;
+ if (!SkipUntilStatementEnd (src, pos))
+ return false;
+
+ // TODO: remove duplication between optimized/normal fog code
+
+ // insert fog code at pos!
+ if (fog == kFogExp2)
+ {
+ // fog = exp(-(density*z)^2)
+ if(canUseOptimizedCode)
+ {
+ src.insert (pos,
+ " float _patchFog = _unity_FogParams.x * gl_Position.z;\n"
+ " _patchFog = _patchFog * _patchFog;\n"
+ " _unity_FogVar = vec4(clamp (exp2(-_patchFog), 0.0, 1.0)); _unity_FogVar.a = 1.0;\n"
+ " _unity_FogColorPreMul = _unity_FogColor * (vec4(1.0)-_unity_FogVar);\n"
+ );
+ }
+ else
+ {
+ src.insert (pos,
+ " float _patchFog = _unity_FogParams.x * gl_Position.z;\n"
+ " _patchFog = _patchFog * _patchFog;\n"
+ " _unity_FogVar = clamp (exp2(-_patchFog), 0.0, 1.0);\n"
+ );
+ }
+ }
+ else if (fog == kFogExp)
+ {
+ // fog = exp(-density*z)
+ if(canUseOptimizedCode)
+ {
+ src.insert (pos,
+ " float _patchFog = _unity_FogParams.y * gl_Position.z;\n"
+ " _unity_FogVar = vec4(clamp (exp2(-_patchFog), 0.0, 1.0)); _unity_FogVar.a = 1.0;\n"
+ " _unity_FogColorPreMul = _unity_FogColor * (vec4(1.0)-_unity_FogVar);\n"
+ );
+ }
+ else
+ {
+ src.insert (pos,
+ " float _patchFog = _unity_FogParams.y * gl_Position.z;\n"
+ " _unity_FogVar = clamp (exp2(-_patchFog), 0.0, 1.0);\n"
+ );
+ }
+ }
+ else if (fog == kFogLinear)
+ {
+ // fog = (end-z)/(end-start)
+ if(canUseOptimizedCode)
+ {
+ src.insert (pos,
+ " _unity_FogVar = vec4(clamp (_unity_FogParams.z * gl_Position.z + _unity_FogParams.w, 0.0, 1.0)); _unity_FogVar.a = 1.0;\n"
+ " _unity_FogColorPreMul = _unity_FogColor * (vec4(1.0)-_unity_FogVar);\n"
+ );
+ }
+ else
+ {
+ src.insert (pos,
+ " _unity_FogVar = clamp (_unity_FogParams.z * gl_Position.z + _unity_FogParams.w, 0.0, 1.0);\n"
+ );
+ }
+ }
+
+ return true;
+}
+
+static bool InsertFogFragmentCodeGLES (std::string& src, bool canUseOptimizedCode)
+{
+ bool writesToData = true; // writes to gl_FragData vs. gl_FragColor
+ size_t colorWriteStart = src.find ("gl_FragData[0]");
+ if (colorWriteStart == std::string::npos)
+ {
+ colorWriteStart = src.find ("gl_FragColor");
+ if (colorWriteStart == std::string::npos)
+ return false;
+ writesToData = false;
+ }
+
+ // iPad2 crashes with some shaders using fog (not all of them though), case 399638.
+ // Seems to avoid the crash if we don't read gl_FragColor after writing it. So instead,
+ // introduce a new local variable, replace any previous uses with it, do fog patching
+ // on it, and write out to final destination in the end.
+
+ // replace color with new variable name
+ const std::string color = writesToData ? "gl_FragData[0]" : "gl_FragColor";
+ replace_string (src, color, "_patchFragColor");
+
+ // find color output statement start & end
+ size_t posBegin, posEnd;
+ posBegin = posEnd = colorWriteStart;
+ if (!SkipUntilStatementEnd (src, posEnd))
+ return false;
+ SkipUntilStatementBegin (src, posBegin);
+
+ // insert new output variable declaration
+ src.insert (posBegin, "lowp vec4 _patchFragColor;\n");
+ posEnd += strlen("lowp vec4 _patchFragColor;\n");
+
+ // insert fog code
+ if(canUseOptimizedCode)
+ src.insert (posEnd, " _patchFragColor = _patchFragColor * _unity_FogVar + _unity_FogColorPreMul; " + color + " = _patchFragColor;\n");
+ else
+ src.insert (posEnd, " _patchFragColor.rgb = mix (_unity_FogColor.rgb, _patchFragColor.rgb, _unity_FogVar); " + color + " = _patchFragColor;\n");
+
+ return true;
+}
+
+bool PatchShaderFogGLES (std::string& srcVS, std::string& srcPS, FogMode fog, bool useOptimizedFogCode)
+{
+ #if DEBUG_FOG_PATCHING
+ printf_console ("GLES fog patching: original vertex shader:\n%s\n...and pixel shader:\n%s\n", srcVS.c_str(), srcPS.c_str());
+ #endif
+
+ const int versionVS = ParseGLSLVersion (srcVS);
+ const int versionPS = ParseGLSLVersion (srcPS);
+ const size_t startVS = SkipGLSLDirectives(srcVS);
+ const size_t startPS = SkipGLSLDirectives(srcPS);
+ const char* fogDataVS = NULL;
+ const char* fogDataPS = NULL;
+ if (useOptimizedFogCode)
+ {
+ if (versionVS >= 150)
+ {
+ fogDataVS = "uniform highp vec4 _unity_FogParams;\nuniform lowp vec4 _unity_FogColor;\nout lowp vec4 _unity_FogColorPreMul;\nout lowp vec4 _unity_FogVar;\n";
+ fogDataPS = "in lowp vec4 _unity_FogColorPreMul;\nin lowp vec4 _unity_FogVar;\n";
+ }
+ else
+ {
+ fogDataVS = "uniform highp vec4 _unity_FogParams;\nuniform lowp vec4 _unity_FogColor;\nvarying lowp vec4 _unity_FogColorPreMul;\nvarying lowp vec4 _unity_FogVar;\n";
+ fogDataPS = "varying lowp vec4 _unity_FogColorPreMul;\nvarying lowp vec4 _unity_FogVar;\n";
+ }
+ }
+ else
+ {
+ if (versionPS >= 150)
+ {
+ fogDataVS = "uniform highp vec4 _unity_FogParams;\nout lowp float _unity_FogVar;\n";
+ fogDataPS = "uniform lowp vec4 _unity_FogColor;\nin lowp float _unity_FogVar;\n";
+ }
+ else
+ {
+ fogDataVS = "uniform highp vec4 _unity_FogParams;\nvarying lowp float _unity_FogVar;\n";
+ fogDataPS = "uniform lowp vec4 _unity_FogColor;\nvarying lowp float _unity_FogVar;\n";
+ }
+ }
+ srcVS.insert (startVS, fogDataVS);
+ srcPS.insert (startPS, fogDataPS);
+
+
+ if (!InsertFogVertexCodeGLES (srcVS, fog, useOptimizedFogCode))
+ return false;
+ if (!InsertFogFragmentCodeGLES (srcPS, useOptimizedFogCode))
+ return false;
+
+ #if DEBUG_FOG_PATCHING
+ printf_console ("GLES fog patching: after patching, fog mode %d:\n%s\n...and pixel shader:\n%s\n", fog, srcVS.c_str(), srcPS.c_str());
+ #endif
+ return true;
+}
+
+static unsigned CalculateVaryingCount(const std::string& src)
+{
+ unsigned varCount = 0;
+
+ size_t varUsage = src.find("varying ");
+ while(varUsage != std::string::npos)
+ {
+ ++varCount;
+ varUsage = src.find("varying ", varUsage+7);
+ }
+
+ return varCount;
+}
+
+bool CanUseOptimizedFogCodeGLES(const std::string& srcVS)
+{
+#if UNITY_ANDROID || UNITY_IPHONE
+ // we add 2 varyings in optimized path
+ // in normal path we add one, so would be good to check it too, but we dont take "packing" into account
+ // so we just act conservatively and apply optimization only when we are sure it works
+ return (CalculateVaryingCount(srcVS) + 2 <= gGraphicsCaps.gles20.maxVaryings);
+#else
+ (void)srcVS;
+ return true;
+#endif
+}
+
+bool IsShaderParameterArray(const char* name, unsigned nameLen, int arraySize, bool* isZeroElem)
+{
+ bool zeroElem = nameLen > 3 && strcmp (name+nameLen-3, "[0]") == 0;
+ if(isZeroElem)
+ *isZeroElem = zeroElem;
+
+ return arraySize > 1 || zeroElem;
+}
+
+
+// --------------------------------------------------------------------------
+
+#if ENABLE_UNIT_TESTS
+
+#include "External/UnitTest++/src/UnitTest++.h"
+
+SUITE (GlslFogPatchingTests)
+{
+ struct GlslPatchTestFixture
+ {
+ std::string vs, fs, s;
+ std::string expvs, expfs, exps;
+ bool DoPatching(FogMode mode, bool checkOptimizedGLESCode)
+ {
+ s = "#ifdef VERTEX\n" + vs + "\n#endif\n#ifdef FRAGMENT\n" + fs + "\n#endif\n";
+ bool ok = true;
+ ok &= PatchShaderFogGLSL (s, mode);
+ ok &= PatchShaderFogGLES (vs, fs, mode, checkOptimizedGLESCode);
+ return ok;
+ }
+ };
+
+ TEST(SkipGLSLDirectivesWorks)
+ {
+ const char* s;
+ CHECK_EQUAL(0, SkipGLSLDirectives("void")); // no directives
+ CHECK_EQUAL(2, SkipGLSLDirectives(" void")); // skipped space
+ CHECK_EQUAL(2, SkipGLSLDirectives("\n\nvoid")); // skipped newlines
+ CHECK_EQUAL(12, SkipGLSLDirectives("#pragma foo\n")); // skipped whole line
+ CHECK_EQUAL(14, SkipGLSLDirectives(" #pragma foo\n")); // skipped whole line
+ CHECK_EQUAL(15, SkipGLSLDirectives("#pragma foo\r\n\r\n")); // skipped whole line, including various newlines
+ CHECK_EQUAL(24, SkipGLSLDirectives("#pragma foo\n#pragma foo\nvoid")); // skipped two lines
+ }
+
+ TEST(ParseGLSLVersionWorks)
+ {
+ CHECK_EQUAL(0, ParseGLSLVersion(""));
+ CHECK_EQUAL(0, ParseGLSLVersion("void"));
+ CHECK_EQUAL(0, ParseGLSLVersion(" # pragma"));
+ CHECK_EQUAL(0, ParseGLSLVersion("#version"));
+
+ CHECK_EQUAL(120, ParseGLSLVersion("#version 120"));
+ CHECK_EQUAL(200, ParseGLSLVersion(" # version 200"));
+ }
+
+ TEST_FIXTURE(GlslPatchTestFixture,PatchSkipsVersionAndExtensionDirectives)
+ {
+ vs =
+ "#version 120\n"
+ "#extension bar : enable\n"
+ "void main() {\n"
+ " gl_Position = vec4(1,2,3,4);\n"
+ "}";
+ fs =
+ "#extension bar : enable\n"
+ "void main() {\n"
+ " gl_FragColor = vec4(1,2,3,4);\n"
+ "}";
+ CHECK(DoPatching(kFogExp2,true));
+ expvs =
+ "#version 120\n"
+ "#extension bar : enable\n"
+ "uniform highp vec4 _unity_FogParams;\n";
+ expfs =
+ "#extension bar : enable\n"
+ "varying lowp vec4 _unity_FogColorPreMul;\n";
+ exps =
+ "#ifdef VERTEX\n"
+ "#version 120\n"
+ "#extension bar : enable\n"
+ "uniform vec4 _unity_FogColor; uniform vec4 _unity_FogParams;\n"
+ "varying float _unity_FogVar;\n"
+ "void main() {\n"
+ " gl_Position = vec4(1,2,3,4);\n"
+ "_unity_FogVar = gl_Position.z;\n"
+ "}\n"
+ "#endif\n"
+ "#ifdef FRAGMENT\n"
+ "#extension bar : enable\n"
+ "uniform vec4 _unity_FogColor; uniform vec4 _unity_FogParams;\n"
+ "varying float _unity_FogVar;\n"
+ "void main() {\n"
+ " gl_FragColor = vec4(1,2,3,4);\n"
+ " float _patchFog = _unity_FogParams.x * _unity_FogVar;\n"
+ " _patchFog = _patchFog * _patchFog;\n"
+ " _patchFog = clamp (exp2(-_patchFog), 0.0, 1.0);\n"
+ " gl_FragColor.rgb = mix (_unity_FogColor.rgb, gl_FragColor.rgb, _patchFog);\n"
+ "}\n"
+ "#endif\n";
+ CHECK (BeginsWith(vs, expvs));
+ CHECK (BeginsWith(fs, expfs));
+ CHECK_EQUAL (exps, s);
+ }
+
+ TEST_FIXTURE(GlslPatchTestFixture,PatchTakesVersionIntoAccount)
+ {
+ vs =
+ "#version 300 es\n"
+ "void main() {\n"
+ " gl_Position = vec4(1,2,3,4);\n"
+ "}";
+ fs =
+ " #version 420\n"
+ "void main() {\n"
+ " gl_FragColor = vec4(1,2,3,4);\n"
+ "}";
+ CHECK(DoPatching(kFogExp2,true));
+ expvs =
+ "#version 300 es\n"
+ "uniform highp vec4 _unity_FogParams;\n"
+ "uniform lowp vec4 _unity_FogColor;\n"
+ "out lowp vec4 _unity_FogColorPreMul;\n"
+ "out lowp vec4 _unity_FogVar;\n";
+ expfs =
+ " #version 420\n"
+ "in lowp vec4 _unity_FogColorPreMul;\n"
+ "in lowp vec4 _unity_FogVar;\n";
+ exps =
+ "#ifdef VERTEX\n"
+ "#version 300 es\n"
+ "uniform vec4 _unity_FogColor; uniform vec4 _unity_FogParams;\n"
+ "out float _unity_FogVar;\n"
+ "void main() {\n"
+ " gl_Position = vec4(1,2,3,4);\n"
+ "_unity_FogVar = gl_Position.z;\n"
+ "}\n"
+ "#endif\n"
+ "#ifdef FRAGMENT\n"
+ " #version 420\n"
+ "uniform vec4 _unity_FogColor; uniform vec4 _unity_FogParams;\n"
+ "in float _unity_FogVar;\n"
+ "void main() {\n"
+ " gl_FragColor = vec4(1,2,3,4);\n"
+ " float _patchFog = _unity_FogParams.x * _unity_FogVar;\n"
+ " _patchFog = _patchFog * _patchFog;\n"
+ " _patchFog = clamp (exp2(-_patchFog), 0.0, 1.0);\n"
+ " gl_FragColor.rgb = mix (_unity_FogColor.rgb, gl_FragColor.rgb, _patchFog);\n"
+ "}\n"
+ "#endif\n";
+ CHECK (BeginsWith(vs, expvs));
+ CHECK (BeginsWith(fs, expfs));
+ CHECK_EQUAL (exps, s);
+ }
+
+ TEST_FIXTURE(GlslPatchTestFixture,PatchExp2WriteToColorOptimized)
+ {
+ vs =
+ "void main() {\n"
+ " gl_Position = vec4(1,2,3,4);\n"
+ "}";
+ fs =
+ "void main() {\n"
+ " gl_FragColor = vec4(1,2,3,4);\n"
+ "}";
+ CHECK(DoPatching(kFogExp2,true));
+ expvs =
+ "uniform highp vec4 _unity_FogParams;\n"
+ "uniform lowp vec4 _unity_FogColor;\n"
+ "varying lowp vec4 _unity_FogColorPreMul;\n"
+ "varying lowp vec4 _unity_FogVar;\n"
+ "void main() {\n"
+ " gl_Position = vec4(1,2,3,4);\n"
+ " float _patchFog = _unity_FogParams.x * gl_Position.z;\n"
+ " _patchFog = _patchFog * _patchFog;\n"
+ " _unity_FogVar = vec4(clamp (exp2(-_patchFog), 0.0, 1.0)); _unity_FogVar.a = 1.0;\n"
+ " _unity_FogColorPreMul = _unity_FogColor * (vec4(1.0)-_unity_FogVar);\n"
+ "}";
+ expfs =
+ "varying lowp vec4 _unity_FogColorPreMul;\n"
+ "varying lowp vec4 _unity_FogVar;\n"
+ "void main() {\n"
+ " lowp vec4 _patchFragColor;\n"
+ "_patchFragColor = vec4(1,2,3,4);\n"
+ " _patchFragColor = _patchFragColor * _unity_FogVar + _unity_FogColorPreMul; gl_FragColor = _patchFragColor;\n"
+ "}";
+ exps =
+ "#ifdef VERTEX\n"
+ "uniform vec4 _unity_FogColor; uniform vec4 _unity_FogParams;\n"
+ "varying float _unity_FogVar;\n"
+ "void main() {\n"
+ " gl_Position = vec4(1,2,3,4);\n"
+ "_unity_FogVar = gl_Position.z;\n"
+ "}\n"
+ "#endif\n"
+ "#ifdef FRAGMENT\n"
+ "uniform vec4 _unity_FogColor; uniform vec4 _unity_FogParams;\n"
+ "varying float _unity_FogVar;\n"
+ "void main() {\n"
+ " gl_FragColor = vec4(1,2,3,4);\n"
+ " float _patchFog = _unity_FogParams.x * _unity_FogVar;\n"
+ " _patchFog = _patchFog * _patchFog;\n"
+ " _patchFog = clamp (exp2(-_patchFog), 0.0, 1.0);\n"
+ " gl_FragColor.rgb = mix (_unity_FogColor.rgb, gl_FragColor.rgb, _patchFog);\n"
+ "}\n"
+ "#endif\n";
+ CHECK_EQUAL (expvs, vs);
+ CHECK_EQUAL (expfs, fs);
+ CHECK_EQUAL (exps, s);
+ }
+
+ TEST_FIXTURE(GlslPatchTestFixture,PatchExp2WriteToColor)
+ {
+ vs =
+ "void main() {\n"
+ " gl_Position = vec4(1,2,3,4);\n"
+ "}";
+ fs =
+ "void main() {\n"
+ " gl_FragColor = vec4(1,2,3,4);\n"
+ "}";
+ CHECK(DoPatching(kFogExp2,false));
+ expvs =
+ "uniform highp vec4 _unity_FogParams;\n"
+ "varying lowp float _unity_FogVar;\n"
+ "void main() {\n"
+ " gl_Position = vec4(1,2,3,4);\n"
+ " float _patchFog = _unity_FogParams.x * gl_Position.z;\n"
+ " _patchFog = _patchFog * _patchFog;\n"
+ " _unity_FogVar = clamp (exp2(-_patchFog), 0.0, 1.0);\n"
+ "}";
+ expfs =
+ "uniform lowp vec4 _unity_FogColor;\n"
+ "varying lowp float _unity_FogVar;\n"
+ "void main() {\n"
+ " lowp vec4 _patchFragColor;\n"
+ "_patchFragColor = vec4(1,2,3,4);\n"
+ " _patchFragColor.rgb = mix (_unity_FogColor.rgb, _patchFragColor.rgb, _unity_FogVar); gl_FragColor = _patchFragColor;\n"
+ "}";
+ exps =
+ "#ifdef VERTEX\n"
+ "uniform vec4 _unity_FogColor; uniform vec4 _unity_FogParams;\n"
+ "varying float _unity_FogVar;\n"
+ "void main() {\n"
+ " gl_Position = vec4(1,2,3,4);\n"
+ "_unity_FogVar = gl_Position.z;\n"
+ "}\n"
+ "#endif\n"
+ "#ifdef FRAGMENT\n"
+ "uniform vec4 _unity_FogColor; uniform vec4 _unity_FogParams;\n"
+ "varying float _unity_FogVar;\n"
+ "void main() {\n"
+ " gl_FragColor = vec4(1,2,3,4);\n"
+ " float _patchFog = _unity_FogParams.x * _unity_FogVar;\n"
+ " _patchFog = _patchFog * _patchFog;\n"
+ " _patchFog = clamp (exp2(-_patchFog), 0.0, 1.0);\n"
+ " gl_FragColor.rgb = mix (_unity_FogColor.rgb, gl_FragColor.rgb, _patchFog);\n"
+ "}\n"
+ "#endif\n";
+ CHECK_EQUAL (expvs, vs);
+ CHECK_EQUAL (expfs, fs);
+ CHECK_EQUAL (exps, s);
+ }
+
+ TEST_FIXTURE(GlslPatchTestFixture,PatchExpWriteToDataWithStuffAroundOptimized)
+ {
+ vs =
+ "varying float foo;\n"
+ "void main() {\n"
+ " gl_Position = vec4(1,2,3,4);\n"
+ " foo = 1.0;\n"
+ "}";
+ fs =
+ "varying float foo;\n"
+ "void main() {\n"
+ " foo = 2.0;\n"
+ " gl_FragData[0] = vec4(1,2,3,4);\n"
+ "}";
+ CHECK(DoPatching(kFogExp,true));
+ expvs =
+ "uniform highp vec4 _unity_FogParams;\n"
+ "uniform lowp vec4 _unity_FogColor;\n"
+ "varying lowp vec4 _unity_FogColorPreMul;\n"
+ "varying lowp vec4 _unity_FogVar;\n"
+ "varying float foo;\n"
+ "void main() {\n"
+ " gl_Position = vec4(1,2,3,4);\n"
+ " float _patchFog = _unity_FogParams.y * gl_Position.z;\n"
+ " _unity_FogVar = vec4(clamp (exp2(-_patchFog), 0.0, 1.0)); _unity_FogVar.a = 1.0;\n"
+ " _unity_FogColorPreMul = _unity_FogColor * (vec4(1.0)-_unity_FogVar);\n"
+ " foo = 1.0;\n"
+ "}";
+ expfs =
+ "varying lowp vec4 _unity_FogColorPreMul;\n"
+ "varying lowp vec4 _unity_FogVar;\n"
+ "varying float foo;\n"
+ "void main() {\n"
+ " foo = 2.0;\n"
+ " lowp vec4 _patchFragColor;\n"
+ "_patchFragColor = vec4(1,2,3,4);\n"
+ " _patchFragColor = _patchFragColor * _unity_FogVar + _unity_FogColorPreMul; gl_FragData[0] = _patchFragColor;\n"
+ "}";
+ exps =
+ "#ifdef VERTEX\n"
+ "uniform vec4 _unity_FogColor; uniform vec4 _unity_FogParams;\n"
+ "varying float _unity_FogVar;\n"
+ "varying float foo;\n"
+ "void main() {\n"
+ " gl_Position = vec4(1,2,3,4);\n"
+ "_unity_FogVar = gl_Position.z;\n"
+ " foo = 1.0;\n"
+ "}\n"
+ "#endif\n"
+ "#ifdef FRAGMENT\n"
+ "uniform vec4 _unity_FogColor; uniform vec4 _unity_FogParams;\n"
+ "varying float _unity_FogVar;\n"
+ "varying float foo;\n"
+ "void main() {\n"
+ " foo = 2.0;\n"
+ " gl_FragData[0] = vec4(1,2,3,4);\n"
+ " float _patchFog = _unity_FogParams.y * _unity_FogVar;\n"
+ " _patchFog = clamp (exp2(-_patchFog), 0.0, 1.0);\n"
+ " gl_FragData[0].rgb = mix (_unity_FogColor.rgb, gl_FragData[0].rgb, _patchFog);\n"
+ "}\n"
+ "#endif\n";
+ CHECK_EQUAL (expvs, vs);
+ CHECK_EQUAL (expfs, fs);
+ CHECK_EQUAL (exps, s);
+ }
+
+ TEST_FIXTURE(GlslPatchTestFixture,PatchExpWriteToDataWithStuffAround)
+ {
+ vs =
+ "varying float foo;\n"
+ "void main() {\n"
+ " gl_Position = vec4(1,2,3,4);\n"
+ " foo = 1.0;\n"
+ "}";
+ fs =
+ "varying float foo;\n"
+ "void main() {\n"
+ " foo = 2.0;\n"
+ " gl_FragData[0] = vec4(1,2,3,4);\n"
+ "}";
+ CHECK(DoPatching(kFogExp,false));
+ expvs =
+ "uniform highp vec4 _unity_FogParams;\n"
+ "varying lowp float _unity_FogVar;\n"
+ "varying float foo;\n"
+ "void main() {\n"
+ " gl_Position = vec4(1,2,3,4);\n"
+ " float _patchFog = _unity_FogParams.y * gl_Position.z;\n"
+ " _unity_FogVar = clamp (exp2(-_patchFog), 0.0, 1.0);\n"
+ " foo = 1.0;\n"
+ "}";
+ expfs =
+ "uniform lowp vec4 _unity_FogColor;\n"
+ "varying lowp float _unity_FogVar;\n"
+ "varying float foo;\n"
+ "void main() {\n"
+ " foo = 2.0;\n"
+ " lowp vec4 _patchFragColor;\n"
+ "_patchFragColor = vec4(1,2,3,4);\n"
+ " _patchFragColor.rgb = mix (_unity_FogColor.rgb, _patchFragColor.rgb, _unity_FogVar); gl_FragData[0] = _patchFragColor;\n"
+ "}";
+ exps =
+ "#ifdef VERTEX\n"
+ "uniform vec4 _unity_FogColor; uniform vec4 _unity_FogParams;\n"
+ "varying float _unity_FogVar;\n"
+ "varying float foo;\n"
+ "void main() {\n"
+ " gl_Position = vec4(1,2,3,4);\n"
+ "_unity_FogVar = gl_Position.z;\n"
+ " foo = 1.0;\n"
+ "}\n"
+ "#endif\n"
+ "#ifdef FRAGMENT\n"
+ "uniform vec4 _unity_FogColor; uniform vec4 _unity_FogParams;\n"
+ "varying float _unity_FogVar;\n"
+ "varying float foo;\n"
+ "void main() {\n"
+ " foo = 2.0;\n"
+ " gl_FragData[0] = vec4(1,2,3,4);\n"
+ " float _patchFog = _unity_FogParams.y * _unity_FogVar;\n"
+ " _patchFog = clamp (exp2(-_patchFog), 0.0, 1.0);\n"
+ " gl_FragData[0].rgb = mix (_unity_FogColor.rgb, gl_FragData[0].rgb, _patchFog);\n"
+ "}\n"
+ "#endif\n";
+ CHECK_EQUAL (expvs, vs);
+ CHECK_EQUAL (expfs, fs);
+ CHECK_EQUAL (exps, s);
+ }
+
+ TEST(CalculateVaryingCount)
+ {
+ // TODO: should we handle packing? for now we dont need it, but point for the future
+ std::string test = "varying float foo; varying float foo2;";
+ CHECK_EQUAL(CalculateVaryingCount(test), 2);
+ }
+} // SUITE
+
+#endif // ENABLE_UNIT_TESTS
diff --git a/Runtime/Utilities/GLSLUtilities.h b/Runtime/Utilities/GLSLUtilities.h
new file mode 100644
index 0000000..147339d
--- /dev/null
+++ b/Runtime/Utilities/GLSLUtilities.h
@@ -0,0 +1,30 @@
+#ifndef GLSLUTILITIES_H
+#define GLSLUTILITIES_H
+
+#include "Runtime/GfxDevice/GfxDeviceTypes.h"
+
+class ShaderErrors;
+
+enum GLSLErrorType
+{
+ kErrorCompileVertexShader,
+ kErrorCompileFragShader,
+ kErrorLinkProgram,
+ kErrorCount
+};
+void OutputGLSLShaderError (const char* log, GLSLErrorType errorType, ShaderErrors& outErrors);
+
+bool CanUseOptimizedFogCodeGLES(const std::string& srcVS);
+
+bool PatchShaderFogGLSL (std::string& src, FogMode fog);
+bool PatchShaderFogGLES (std::string& srcVS, std::string& srcPS, FogMode fog, bool useOptimizedFogCode);
+
+// TODO: might be better to share whole parm filling procedure, but good enough for now
+// TODO: as for now all gles impls we know returns first elem name
+// in case of using array[0] only - arraySize would be 1
+// in case of using array[1] only - arraySize would be 2 and uniform name would be array[0]
+// but this behaviour might change in future
+bool IsShaderParameterArray(const char* name, unsigned nameLen, int arraySize, bool* isZeroElem=0);
+
+
+#endif
diff --git a/Runtime/Utilities/GUID.cpp b/Runtime/Utilities/GUID.cpp
new file mode 100644
index 0000000..9fe4f7f
--- /dev/null
+++ b/Runtime/Utilities/GUID.cpp
@@ -0,0 +1,241 @@
+#include "UnityPrefix.h"
+#include "GUID.h"
+#include "LogAssert.h"
+#include "StaticAssert.h"
+#include <string>
+
+#if UNITY_IPHONE
+#include <CoreFoundation/CFUUID.h>
+#endif
+
+#if UNITY_WIN
+#include <ObjBase.h>
+#endif
+
+#if UNITY_XENON
+#include <comdecl.h>
+#include <time.h>
+#endif
+
+using namespace std;
+
+#if UNITY_ANDROID
+#include <fcntl.h>
+#include <unistd.h>
+#elif UNITY_LINUX || UNITY_BB10 || UNITY_TIZEN
+#include <fcntl.h>
+#include <sys/time.h>
+#endif
+
+#if UNITY_PS3
+#include <sys/random_number.h>
+#endif
+
+#if UNITY_HAVE_GUID_INIT
+void UnityGUID::Init ()
+{
+#if UNITY_OSX || UNITY_IPHONE
+ CompileTimeAssert (sizeof (CFUUIDBytes) == 16, "GIUD shoud be 16 bytes");
+#elif UNITY_WIN || UNITY_XENON
+ CompileTimeAssert (sizeof (GUID) == 16, "GIUD shoud be 16 bytes");
+#endif
+
+
+#if UNITY_OSX || UNITY_IPHONE
+
+ CFUUIDRef myUUID;
+ myUUID = CFUUIDCreate (kCFAllocatorDefault);
+ CFUUIDBytes bytes = CFUUIDGetUUIDBytes (myUUID);
+ memcpy (data, &bytes, sizeof (CFUUIDBytes));
+ CFRelease (myUUID);
+
+#elif UNITY_ANDROID || UNITY_LINUX || UNITY_BB10 || UNITY_TIZEN
+
+ #pragma message("Android GUID/UUID creation NOT tested!")
+ {
+ unsigned char bytes[16];
+
+ struct timeval tv ;
+ gettimeofday(&tv, 0) ;
+ int fd = open("/dev/urandom", O_RDONLY);
+ if (fd == -1)
+ fd = open("/dev/random", O_RDONLY | O_NONBLOCK) ;
+
+ srand((getpid() << 16) ^ getuid() ^ tv.tv_sec ^ tv.tv_usec) ;
+
+ // Crank the random number generator a few times
+ gettimeofday(&tv, 0) ;
+ for (int i = (tv.tv_sec ^ tv.tv_usec) & 0x1F; i > 0; i--) rand() ;
+
+ int n = 16 ;
+ int lose_counter = 0 ;
+ unsigned char *cp = (unsigned char *) bytes ;
+ while (n > 0 && fd != -1)
+ {
+ int i = read(fd, cp, n) ;
+ if (i <= 0)
+ {
+ if (lose_counter++ > 16) break ;
+ continue ;
+ }
+ n -= i ;
+ cp += i ;
+ lose_counter = 0 ;
+ }
+
+ if (fd != -1)
+ close(fd);
+
+ // We do this all the time, but this is the only source of
+ // randomness if /dev/random/urandom is out to lunch.
+ for (unsigned int i = 0; i<16; ++i)
+ bytes[i] ^= (rand() >> 7) & 0xFF ;
+
+ // clock_seq is bytes #8 and #9
+ uint16_t clock_seq = (uint16_t(bytes[8]) << 8) + bytes[9] ;
+ clock_seq = (clock_seq & 0x3FFF) | 0x8000 ;
+ bytes[8] = clock_seq >> 8 ;
+ bytes[9] = clock_seq & 0xFF ;
+
+ // time_hi_and_version is bytes #6 and #7
+ uint16_t time_hi_and_version = (uint16_t(bytes[6]) << 8) + bytes[7] ;
+ time_hi_and_version = (time_hi_and_version & 0x0FFF) | 0x4000 ;
+ bytes[6] = time_hi_and_version >> 8 ;
+ bytes[7] = time_hi_and_version & 0xFF ;
+
+ memcpy (data, &bytes, sizeof (data));
+ }
+#elif UNITY_WIN
+
+ GUID guid;
+ ::CoCreateGuid( &guid );
+ memcpy( data, &guid, sizeof(guid) );
+
+#elif UNITY_PS3
+
+ sys_get_random_number( &data, sizeof(UnityGUID) );
+
+#elif UNITY_XENON
+
+ // RFC 4122, section 4.4 (Algorithms for Creating a UUID from Truly Random or Pseudo-Random Numbers)
+ // http://www.ietf.org/rfc/rfc4122.txt
+ GUID guid;
+ unsigned char* bytes = (unsigned char*)&guid;
+
+ srand( time(NULL) );
+ for (unsigned int i = 0; i<16; ++i)
+ bytes[i] ^= (rand() >> 7) & 0xFF;
+
+ // V4
+ guid.Data3 &= 0x0fff;
+ guid.Data3 |= (4 << 12);
+ guid.Data4[0] &= 0x3f;
+ guid.Data4[0] |= 0x80;
+
+ memcpy( data, &guid, sizeof(guid) );
+
+#else
+
+ ErrorString("GUID::Init() not implemented.");
+
+#endif
+}
+#endif
+
+bool CompareGUIDStringLess (const UnityGUID& lhsGUID, const UnityGUID& rhsGUID)
+{
+ char lhs[32];
+ char rhs[32];
+ GUIDToString(lhsGUID, lhs);
+ GUIDToString(rhsGUID, rhs);
+
+ for (int i=0;i<32;i++)
+ {
+ if (lhs[i] != rhs[i])
+ return lhs[i] < rhs[i];
+ }
+
+ return false;
+}
+
+const char kHexToLiteral[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
+
+string GUIDToString(const UnityGUID& guid)
+{
+ char name[kGUIDStringLength+1];
+ GUIDToString (guid, name);
+ name[kGUIDStringLength] = '\0';
+ return name;
+}
+
+void GUIDToString(const UnityGUID& guid, char* name)
+{
+ for (int i=0;i<4;i++)
+ {
+ for (int j=8;j--;)
+ {
+ UInt32 cur = guid.data[i];
+ cur >>= (j * 4);
+ cur &= 0xF;
+ name[i * 8 + j] = kHexToLiteral[cur];
+ }
+ }
+}
+
+UnityGUID StringToGUID (const std::string& guidString)
+{
+ return StringToGUID(guidString.c_str(), guidString.size());
+}
+
+UnityGUID StringToGUID (const char* guidString, size_t length)
+{
+ if (length != kGUIDStringLength)
+ return UnityGUID ();
+
+ // Initialize hex char to int table
+ static char s_LiteralToHex[255];
+ static bool s_IsInitialized = false;
+ if (!s_IsInitialized)
+ {
+ for (int i=0;i<255;i++)
+ s_LiteralToHex[i] = -1;
+ s_LiteralToHex['0'] = 0;
+ s_LiteralToHex['1'] = 1;
+ s_LiteralToHex['2'] = 2;
+ s_LiteralToHex['3'] = 3;
+ s_LiteralToHex['4'] = 4;
+ s_LiteralToHex['5'] = 5;
+ s_LiteralToHex['6'] = 6;
+ s_LiteralToHex['7'] = 7;
+ s_LiteralToHex['8'] = 8;
+ s_LiteralToHex['9'] = 9;
+ s_LiteralToHex['a'] = 10;
+ s_LiteralToHex['b'] = 11;
+ s_LiteralToHex['c'] = 12;
+ s_LiteralToHex['d'] = 13;
+ s_LiteralToHex['e'] = 14;
+ s_LiteralToHex['f'] = 15;
+ s_IsInitialized = true;
+ }
+
+ // Convert every hex char into an int [0...16]
+ int hex[kGUIDStringLength];
+ for (int i=0;i<kGUIDStringLength;i++)
+ hex[i] = s_LiteralToHex[guidString[i]];
+
+ UnityGUID guid;
+ for (int i=0;i<4;i++)
+ {
+ UInt32 cur = 0;
+ for (int j=8;j--;)
+ {
+ int curHex = hex[i * 8 + j];
+ if (curHex == -1)
+ return UnityGUID ();
+
+ cur |= (curHex << (j * 4));
+ }
+ guid.data[i] = cur;
+ }
+ return guid;
+}
diff --git a/Runtime/Utilities/GUID.h b/Runtime/Utilities/GUID.h
new file mode 100644
index 0000000..e310c1f
--- /dev/null
+++ b/Runtime/Utilities/GUID.h
@@ -0,0 +1,73 @@
+#ifndef UNITY_GUID_H
+#define UNITY_GUID_H
+
+#include "Runtime/Serialize/SerializeUtility.h"
+#include <string>
+
+enum { kGUIDStringLength = 32 };
+
+#define UNITY_HAVE_GUID_INIT (UNITY_OSX || UNITY_WIN || UNITY_IPHONE || UNITY_ANDROID || UNITY_LINUX || UNITY_PEPPER || UNITY_PS3 || UNITY_XENON || UNITY_BB10 || UNITY_TIZEN)
+
+// To setup the unique identifier use Init ().
+// You can compare it against other unique identifiers
+// It is guaranteed to be unique over space and time
+//
+// Called UnityGUID because Visual Studio really does not like structs named GUID!
+struct UnityGUID
+{
+ UInt32 data[4];
+
+ // Used to be called GUID, so for serialization it has the old name
+ DECLARE_SERIALIZE_OPTIMIZE_TRANSFER (GUID)
+
+ UnityGUID (UInt32 a, UInt32 b, UInt32 c, UInt32 d) { data[0] = a; data[1] = b; data[2] = c; data[3] = d; }
+ UnityGUID () { data[0] = 0; data[1] = 0; data[2] = 0; data[3] = 0; }
+
+ bool operator == (const UnityGUID& rhs) const {
+ return data[0] == rhs.data[0] && data[1] == rhs.data[1] && data[2] == rhs.data[2] && data[3] == rhs.data[3];
+ }
+ bool operator != (const UnityGUID& rhs) const { return !(*this == rhs); }
+
+ bool operator < (const UnityGUID& rhs) const { return CompareGUID (*this, rhs) == -1; }
+ friend int CompareGUID (const UnityGUID& lhs, const UnityGUID& rhs);
+
+ // Use this instead of guid != UnityGUID() -- Will often result in self-documented code
+ bool IsValid() const { return data[0] != 0 || data[1] != 0 || data[2] != 0 || data[3] != 0; }
+
+#if UNITY_HAVE_GUID_INIT
+ void Init ();
+#endif
+};
+
+std::string GUIDToString(const UnityGUID& guid);
+void GUIDToString(const UnityGUID& guid, char* string);
+
+UnityGUID StringToGUID (const std::string& guidString);
+UnityGUID StringToGUID (const char* guidString, size_t stringLength);
+
+inline int CompareGUID (const UnityGUID& lhs, const UnityGUID& rhs)
+{
+ for (int i=0;i<4;i++)
+ {
+ if (lhs.data[i] < rhs.data[i])
+ return -1;
+ if (lhs.data[i] > rhs.data[i])
+ return 1;
+ }
+ return 0;
+}
+
+bool CompareGUIDStringLess (const UnityGUID& lhs, const UnityGUID& rhs);
+
+template<class TransferFunction>
+void UnityGUID::Transfer (TransferFunction& transfer)
+{
+ TRANSFER (data[0]);
+ TRANSFER (data[1]);
+ TRANSFER (data[2]);
+ TRANSFER (data[3]);
+}
+
+extern const char kHexToLiteral[16];
+
+#endif
diff --git a/Runtime/Utilities/GlobalPreferences.cpp b/Runtime/Utilities/GlobalPreferences.cpp
new file mode 100644
index 0000000..fb741b0
--- /dev/null
+++ b/Runtime/Utilities/GlobalPreferences.cpp
@@ -0,0 +1,151 @@
+#include "UnityPrefix.h"
+
+#include "Runtime/Utilities/PlayerPrefs.h"
+
+#if UNITY_PLUGIN
+#if UNITY_OSX
+#include "OSXWebPluginUtility.h"
+#endif // #if UNITY_OSX
+#include "PlatformDependent/CommonWebPlugin/WebPluginUtility.h"
+#endif // #if UNITY_PLUGIN
+
+#include "GlobalPreferences.h"
+
+
+#if UNITY_WIN
+#include "PlatformDependent/Win/Registry.h"
+#if UNITY_EDITOR
+const char* kRegistryPath = "Software\\Unity\\UnityEditor";
+#elif UNITY_STANDALONE
+const char* kRegistryPath = "Software\\Unity\\UnityStandalone";
+#else
+const char* kRegistryPath = "Software\\Unity\\WebPlayer";
+#endif
+#endif // #if UNITY_WIN
+
+#if UNITY_OSX
+#if UNITY_EDITOR
+const char* kPrefsAppID = "com.unity3d.UnityEditor";
+#elif UNITY_STANDALONE
+const char* kPrefsAppID = "com.unity3d.UnityStandalone";
+#else
+const char* kPrefsAppID = "com.unity3d.UnityWebPlayer";
+#endif
+#endif // #if UNITY_OSX
+
+#if UNITY_LINUX
+#include "PlatformDependent/Linux/XmlOptions.h"
+#include "Runtime/Utilities/FileUtilities.h"
+#include "Runtime/Utilities/PathNameUtility.h"
+
+const char* kPrefsFileName = "global.prefs";
+#endif // #if UNITY_LINUX
+
+
+std::string GetGlobalPreference(const char *key)
+{
+ std::string result;
+
+ #if UNITY_WIN && !UNITY_WINRT
+ return registry::getString( kRegistryPath, key, "" );
+
+ #elif UNITY_OSX
+ CFStringRef cfPrefsAppID = CFStringCreateWithCString (NULL, kPrefsAppID, kCFStringEncodingASCII);
+ CFStringRef cfKey = CFStringCreateWithCString (NULL, key, kCFStringEncodingASCII);
+ CFStringRef val = (CFStringRef)CFPreferencesCopyAppValue (cfKey, cfPrefsAppID);
+ if (val)
+ {
+ result = CFStringToString(val);
+ CFRelease (val);
+ }
+ CFRelease (cfPrefsAppID);
+ CFRelease (cfKey);
+
+ #elif UNITY_LINUX
+ std::string path = AppendPathName (GetUserConfigFolder (), kPrefsFileName);
+ XmlOptions options;
+ if (options.Load (path))
+ {
+ result = options.GetString (key, "");
+ }
+
+ #elif GAMERELEASE
+ return PlayerPrefs::GetString (key);
+
+ #else
+ // TODO
+ #endif
+ return result;
+}
+
+bool GetGlobalBoolPreference(const char *key, bool defaultValue)
+{
+ std::string const pref = GetGlobalPreference (key);
+ if (pref == "yes")
+ return true;
+ if (pref == "no")
+ return false;
+ return defaultValue;
+}
+
+void SetGlobalPreference(const char *key, std::string value)
+{
+ #if UNITY_WIN && !UNITY_WINRT
+ registry::setString( kRegistryPath, key, value.c_str() );
+
+ #elif UNITY_OSX
+ CFStringRef cfPrefsAppID = CFStringCreateWithCString (NULL, kPrefsAppID, kCFStringEncodingASCII);
+ CFStringRef cfKey = CFStringCreateWithCString (NULL, key, kCFStringEncodingASCII);
+ CFStringRef cfValue = CFStringCreateWithCString (NULL, value.c_str(), kCFStringEncodingASCII);
+
+ CFPreferencesSetAppValue( cfKey, cfValue, cfPrefsAppID );
+ CFPreferencesAppSynchronize( cfPrefsAppID );
+
+ #elif UNITY_LINUX
+ std::string path = AppendPathName (GetUserConfigFolder (), kPrefsFileName);
+ XmlOptions options;
+ options.Load (path);
+ options.SetString (key, value);
+ options.Save (path);
+
+ #elif GAMERELEASE
+ PlayerPrefs::SetString (key, value);
+
+ #else
+ // TODO
+ #endif
+}
+
+void SetGlobalBoolPreference(const char *key, bool value)
+{
+ SetGlobalPreference (key, value?"yes":"no");
+}
+
+
+#if WEBPLUG && !UNITY_PLUGIN
+#include "Runtime/Misc/PlayerSettings.h"
+
+std::string GetStrippedPlayerDomain ()
+{
+ std::string currentDomain = GetPlayerSettings().absoluteURL;
+ if (currentDomain.find("http://") == 0 || currentDomain.find("https://") == 0)
+ {
+ //remove http://
+ if (currentDomain.find("http://") == 0)
+ currentDomain.erase(0, 7);
+ else if (currentDomain.find("https://") == 0)
+ currentDomain.erase(0, 8);
+
+ //remove path
+ std::string::size_type pos = currentDomain.find("/", 0);
+ if (pos != std::string::npos)
+ currentDomain.erase(currentDomain.begin() + pos, currentDomain.end());
+
+ //remove port if present
+ pos = currentDomain.find(":", 0);
+ if (pos != std::string::npos)
+ currentDomain.erase(currentDomain.begin() + pos, currentDomain.end());
+ }
+ return currentDomain;
+}
+#endif // #if WEBPLUG && !UNITY_PLUGIN
diff --git a/Runtime/Utilities/GlobalPreferences.h b/Runtime/Utilities/GlobalPreferences.h
new file mode 100644
index 0000000..b0ca58f
--- /dev/null
+++ b/Runtime/Utilities/GlobalPreferences.h
@@ -0,0 +1,15 @@
+#ifndef GLOBALPREFERENCES_H
+#define GLOBALPREFERENCES_H
+
+#if WEBPLUG
+std::string GetStrippedPlayerDomain ();
+#endif
+
+// Used for hardware stats and webplayer settings.
+std::string GetGlobalPreference(const char *key);
+bool GetGlobalBoolPreference(const char *key, bool defaultValue);
+
+void SetGlobalPreference(const char *key, std::string value);
+void SetGlobalBoolPreference(const char *key, bool value);
+
+#endif \ No newline at end of file
diff --git a/Runtime/Utilities/Hash128.cpp b/Runtime/Utilities/Hash128.cpp
new file mode 100644
index 0000000..8e8cb68
--- /dev/null
+++ b/Runtime/Utilities/Hash128.cpp
@@ -0,0 +1,33 @@
+#include "UnityPrefix.h"
+#include "Hash128.h"
+
+std::string Hash128ToString(const Hash128& digest)
+{
+ std::string name;
+ name.resize (32);
+ for(int i=0; i < 16; i++)
+ sprintf(&name[i*2],"%02hhx",digest.hashData.bytes[i]);
+ return name;
+}
+
+static inline int HexToNumber(char c)
+{
+ if( c >= '0' && c <= '9')
+ return c-'0';
+ if( c >= 'a' && c <= 'f')
+ return c-'a'+10;
+ if( c >= 'A' && c <= 'F')
+ return c-'A'+10;
+ return 0;
+}
+
+
+Hash128 StringToHash128(const std::string& name)
+{
+ Hash128 digest;
+ int end = name.size() > 32 ? 16 : name.size()/2;
+ for( int i = 0; i < end; ++i ) {
+ digest.hashData.bytes[i] = HexToNumber(name[i*2]) * 16 + HexToNumber(name[i*2+1]);
+ }
+ return digest;
+} \ No newline at end of file
diff --git a/Runtime/Utilities/Hash128.h b/Runtime/Utilities/Hash128.h
new file mode 100644
index 0000000..ec659fe
--- /dev/null
+++ b/Runtime/Utilities/Hash128.h
@@ -0,0 +1,49 @@
+#pragma once
+
+#include "Runtime/Serialize/SerializeUtility.h"
+
+// Holds a 128 bit hash value
+struct Hash128
+{
+ union
+ {
+ unsigned char bytes[16];
+ UInt64 u64[2];
+ UInt32 u32[4];
+ } hashData ;
+
+ friend inline bool operator == (const Hash128& lhs, const Hash128& rhs) { return lhs.hashData.u64[0] == rhs.hashData.u64[0] && lhs.hashData.u64[1] == rhs.hashData.u64[1]; }
+ friend inline bool operator != (const Hash128& lhs, const Hash128& rhs) { return lhs.hashData.u64[0] != rhs.hashData.u64[0] || lhs.hashData.u64[1] != rhs.hashData.u64[1]; }
+
+ Hash128 () { hashData.u64[0] = 0; hashData.u64[1] = 0; }
+
+ DECLARE_SERIALIZE_NO_PPTR (Hash128)
+};
+
+// String conversion
+std::string Hash128ToString(const Hash128& digest);
+Hash128 StringToHash128(const std::string& name);
+
+// Serialization
+template<class T>
+void Hash128::Transfer (T& transfer)
+{
+ UInt8* bytes = hashData.bytes;
+
+ TRANSFER(bytes[0]);
+ TRANSFER(bytes[1]);
+ TRANSFER(bytes[2]);
+ TRANSFER(bytes[3]);
+ TRANSFER(bytes[4]);
+ TRANSFER(bytes[5]);
+ TRANSFER(bytes[6]);
+ TRANSFER(bytes[7]);
+ TRANSFER(bytes[8]);
+ TRANSFER(bytes[9]);
+ TRANSFER(bytes[10]);
+ TRANSFER(bytes[11]);
+ TRANSFER(bytes[12]);
+ TRANSFER(bytes[13]);
+ TRANSFER(bytes[14]);
+ TRANSFER(bytes[15]);
+} \ No newline at end of file
diff --git a/Runtime/Utilities/HashFunctions.h b/Runtime/Utilities/HashFunctions.h
new file mode 100644
index 0000000..0f1f465
--- /dev/null
+++ b/Runtime/Utilities/HashFunctions.h
@@ -0,0 +1,87 @@
+#ifndef __HASH_FUNCTIONS_H
+#define __HASH_FUNCTIONS_H
+
+#include <string>
+#include "Runtime/Utilities/PathNameUtility.h"
+#include "Runtime/Utilities/dense_hash_map.h"
+#include "Runtime/Utilities/Word.h"
+#include "Runtime/Allocator/MemoryMacros.h"
+
+struct InstanceIDHashFunctor
+{
+ inline size_t operator()(SInt32 x) const {
+
+ UInt32 a = static_cast<UInt32> (x);
+ a = (a+0x7ed55d16) + (a<<12);
+ a = (a^0xc761c23c) ^ (a>>19);
+ a = (a+0x165667b1) + (a<<5);
+ a = (a+0xd3a2646c) ^ (a<<9);
+ a = (a+0xfd7046c5) + (a<<3);
+ a = (a^0xb55a4f09) ^ (a>>16);
+
+ return a;
+ }
+};
+
+typedef pair<const SInt32, Object*> InstanceIdToObjectPair;
+struct InstanceIdToObjectPtrHashMap :
+ dense_hash_map<SInt32, Object*, InstanceIDHashFunctor, std::equal_to<SInt32>, STL_ALLOCATOR( kMemSTL, InstanceIdToObjectPair ) >
+{
+ typedef dense_hash_map<SInt32, Object*, InstanceIDHashFunctor, std::equal_to<SInt32>, STL_ALLOCATOR( kMemSTL, InstanceIdToObjectPair )> base_t;
+ InstanceIdToObjectPtrHashMap (int n)
+ : base_t (n)
+ {
+ set_empty_key (-1);
+ set_deleted_key (-2);
+ }
+};
+
+// http://www.cse.yorku.ca/~oz/hash.html
+// Other hashes here: http://burtleburtle.net/bob/
+struct djb2_hash
+{
+ inline size_t operator()(const std::string& str) const
+ {
+ unsigned long hash = 5381;
+ char c;
+ const char* s = str.c_str ();
+
+ while ((c = *s++))
+ hash = ((hash << 5) + hash) ^ c;
+
+ return hash;
+ }
+};
+
+#if UNITY_EDITOR
+struct djb2_hash_lowercase
+{
+ inline size_t operator() (const std::string& str) const
+ {
+ unsigned long hash = 5381;
+ char c;
+#if UNITY_OSX
+ const char* s = NULL;
+ bool ascii = !RequiresNormalization (str);
+ if (ascii)
+ s = ToLower (str).c_str();
+ else
+ s = ToLower (NormalizeUnicode (str, true)).c_str();
+#else
+ std::string help = ToLower(str);
+ const char* s = help.c_str();
+#endif
+
+ while ((c = *s++))
+ hash = ((hash << 5) + hash) ^ c;
+
+ return hash;
+ }
+};
+#endif
+
+
+bool ComputeMD5Hash( const UInt8* data, size_t dataSize, UInt8 outHash[16] );
+bool ComputeSHA1Hash( const UInt8* data, size_t dataSize, UInt8 outHash[20] );
+
+#endif
diff --git a/Runtime/Utilities/InitializeAndCleanup.cpp b/Runtime/Utilities/InitializeAndCleanup.cpp
new file mode 100644
index 0000000..77280f5
--- /dev/null
+++ b/Runtime/Utilities/InitializeAndCleanup.cpp
@@ -0,0 +1,51 @@
+#include "UnityPrefix.h"
+#include "InitializeAndCleanup.h"
+
+enum { kMaxCount = 40 };
+
+struct OrderedCallback
+{
+ int order;
+ RegisterRuntimeInitializeAndCleanup::CallbackFunction* init;
+ RegisterRuntimeInitializeAndCleanup::CallbackFunction* cleanup;
+};
+
+static int gNumRegisteredCallbacks = 0;
+static OrderedCallback gCallbacks[kMaxCount];
+
+
+bool operator < (const OrderedCallback& lhs, const OrderedCallback& rhs)
+{
+ return lhs.order < rhs.order;
+}
+
+RegisterRuntimeInitializeAndCleanup::RegisterRuntimeInitializeAndCleanup(CallbackFunction* Initialize, CallbackFunction* Cleanup, int order)
+{
+ gCallbacks[gNumRegisteredCallbacks].init = Initialize;
+ gCallbacks[gNumRegisteredCallbacks].cleanup = Cleanup;
+ gCallbacks[gNumRegisteredCallbacks].order = order;
+
+ gNumRegisteredCallbacks++;
+ Assert(gNumRegisteredCallbacks <= kMaxCount);
+}
+
+void RegisterRuntimeInitializeAndCleanup::ExecuteInitializations()
+{
+ std::sort (gCallbacks, gCallbacks + gNumRegisteredCallbacks);
+
+ for (int i = 0; i < gNumRegisteredCallbacks; i++)
+ {
+ if (gCallbacks[i].init)
+ gCallbacks[i].init ();
+ }
+}
+
+void RegisterRuntimeInitializeAndCleanup::ExecuteCleanup()
+{
+ for (int i = gNumRegisteredCallbacks-1; i >=0 ; i--)
+ {
+ if (gCallbacks[i].cleanup)
+ gCallbacks[i].cleanup ();
+ }
+}
+
diff --git a/Runtime/Utilities/InitializeAndCleanup.h b/Runtime/Utilities/InitializeAndCleanup.h
new file mode 100644
index 0000000..b079f47
--- /dev/null
+++ b/Runtime/Utilities/InitializeAndCleanup.h
@@ -0,0 +1,12 @@
+#pragma once
+
+class RegisterRuntimeInitializeAndCleanup
+{
+public:
+ typedef void CallbackFunction ();
+ RegisterRuntimeInitializeAndCleanup(CallbackFunction* Initialize, CallbackFunction* Cleanup, int order = 0);
+
+ static void ExecuteInitializations();
+ static void ExecuteCleanup();
+};
+
diff --git a/Runtime/Utilities/LODUtility.cpp b/Runtime/Utilities/LODUtility.cpp
new file mode 100644
index 0000000..d2268fb
--- /dev/null
+++ b/Runtime/Utilities/LODUtility.cpp
@@ -0,0 +1,86 @@
+#include "UnityPrefix.h"
+
+#include "LODUtility.h"
+#include "Runtime/Filters/Renderer.h"
+#include "Runtime/Math/Vector3.h"
+#include "Runtime/Camera/LODGroup.h"
+#include "Runtime/Filters/AABBUtility.h"
+#include "Runtime/Camera/LODGroupManager.h"
+
+void CalculateLODGroupBoundingBox ( LODGroup& group )
+{
+ Matrix4x4f worldToLocal = group.GetComponent(Transform).GetWorldToLocalMatrix();
+
+ MinMaxAABB minmax;
+ minmax.Init();
+ for (int i=0;i<group.GetLODCount();i++)
+ {
+ for (int r=0;r<group.GetLOD(i).renderers.size();r++)
+ {
+ Renderer* renderer = group.GetLOD(i).renderers[r].renderer;
+ if (renderer && renderer->GetGameObjectPtr())
+ {
+ AABB localBounds;
+ if (CalculateLocalAABB (renderer->GetGameObject(), &localBounds))
+ {
+ Matrix4x4f relativeTransform;
+ Matrix4x4f rendererLocalToWorld = renderer->GetTransform().GetLocalToWorldMatrix();
+
+ MultiplyMatrices4x4(&worldToLocal, &rendererLocalToWorld, &relativeTransform);
+
+ AABB lodGroupRelativeBoundds;
+ TransformAABBSlow (localBounds, relativeTransform, lodGroupRelativeBoundds);
+
+ minmax.Encapsulate(lodGroupRelativeBoundds);
+ }
+ }
+ }
+ }
+
+ float size;
+ if (minmax.IsValid())
+ {
+ group.SetLocalReferencePoint (minmax.GetCenter());
+ Vector3f extent = minmax.GetExtent() * 2.0F;
+ size = std::max(std::max(extent.x, extent.y), extent.z);
+ }
+ else
+ {
+ group.SetLocalReferencePoint (Vector3f (0, 0, 0));
+ size = 1;
+ }
+
+ float scale = group.GetWorldSpaceScale();
+ if (scale > 0.0001F)
+ size /= scale;
+
+ group.SetSize (size);
+}
+
+void ForceLODLevel (const LODGroup& group, int index)
+{
+ int LODCount = group.GetLODCount();
+ if (index >= LODCount)
+ if (index >= LODCount)
+ {
+ WarningString("SetLODs: Attempting to force a LOD outside the number available LODs");
+ return;
+ }
+
+ // mask of 0 = no force
+ // now create a mask for the rest
+ UInt32 lodMask = 0;
+ if (index >= 0)
+ lodMask = 1 << index;
+
+ LODGroupManager& m = GetLODGroupManager();
+ int lodGroupIndex = group.GetLODGroup();
+ if (lodGroupIndex < 0)
+ {
+ WarningString("SetLODs: Attempting to force a LOD outside the number available LODs");
+ return;
+ }
+
+ m.SetForceLODMask (lodGroupIndex, lodMask);
+}
+
diff --git a/Runtime/Utilities/LODUtility.h b/Runtime/Utilities/LODUtility.h
new file mode 100644
index 0000000..58ff47d
--- /dev/null
+++ b/Runtime/Utilities/LODUtility.h
@@ -0,0 +1,7 @@
+#pragma once
+
+class LODGroup;
+
+void CalculateLODGroupBoundingBox (LODGroup& group);
+
+void ForceLODLevel (const LODGroup& group, int index);
diff --git a/Runtime/Utilities/LinkedList.h b/Runtime/Utilities/LinkedList.h
new file mode 100644
index 0000000..f2d7d3f
--- /dev/null
+++ b/Runtime/Utilities/LinkedList.h
@@ -0,0 +1,385 @@
+#ifndef LINKED_LIST_H
+#define LINKED_LIST_H
+
+#if !UNITY_RELEASE
+ #define LINKED_LIST_ASSERT(x) Assert(x)
+#else
+ #define LINKED_LIST_ASSERT(x)
+#endif
+
+class ListElement
+{
+public:
+ inline ListElement();
+ inline ~ListElement() { RemoveFromList(); }
+
+ inline bool IsInList() const;
+ inline bool RemoveFromList();
+ inline void InsertInList(ListElement* pos);
+
+ // Check against List::end(), not NULL
+ ListElement* GetPrev() const { return m_Prev; }
+ ListElement* GetNext() const { return m_Next; }
+
+private:
+ // Non copyable
+ ListElement(const ListElement&);
+ ListElement& operator=(const ListElement&);
+
+ ListElement* m_Prev;
+ ListElement* m_Next;
+
+ template <class T> friend class List;
+ inline void ValidateLinks() const;
+
+#if !UNITY_RELEASE
+ // Iterator debugging only
+ template <class T> friend class ListIterator;
+ template <class T> friend class ListConstIterator;
+ void SetList(void* l) { m_List = l; }
+ void* m_List;
+#else
+ void SetList(void*) {}
+#endif
+};
+
+template <class T>
+class ListNode : public ListElement
+{
+public:
+ ListNode(T* data = NULL) : m_Data(data) {}
+ T& operator*() const { return *m_Data; }
+ T* operator->() const { return m_Data; }
+ T* GetData() const { return m_Data; }
+ void SetData(T* data) { m_Data = data; }
+
+ // We know the type of prev and next element
+ ListNode* GetPrev() const { return static_cast<ListNode*>(ListElement::GetPrev()); }
+ ListNode* GetNext() const { return static_cast<ListNode*>(ListElement::GetNext()); }
+
+private:
+ T* m_Data;
+};
+
+template <class T>
+class ListIterator
+{
+public:
+ ListIterator(T* node = NULL) : m_Node(node) {}
+
+ // Pre- and post-increment operator
+ ListIterator& operator++() { m_Node = m_Node->GetNext(); return *this; }
+ ListIterator operator++(int) { ListIterator ret(*this); ++(*this); return ret; }
+
+ // Pre- and post-decrement operator
+ ListIterator& operator--() { m_Node = m_Node->GetPrev(); return *this; }
+ ListIterator operator--(int) { ListIterator ret(*this); --(*this); return ret; }
+
+ T& operator*() const { return static_cast<T&>(*m_Node); }
+ T* operator->() const { return static_cast<T*>(m_Node); }
+
+ friend bool operator !=(const ListIterator& x, const ListIterator& y) { return x.m_Node != y.m_Node; }
+ friend bool operator ==(const ListIterator& x, const ListIterator& y) { return x.m_Node == y.m_Node; }
+
+private:
+ template <class S> friend class List;
+ ListIterator(ListElement* node) : m_Node(node) {}
+ ListElement* m_Node;
+};
+
+
+template <class T>
+class ListConstIterator
+{
+public:
+ ListConstIterator(const T* node = NULL) : m_Node(node) {}
+
+ // Pre- and post-increment operator
+ ListConstIterator& operator++() { m_Node = m_Node->GetNext(); return *this; }
+ ListConstIterator operator++(int) { ListConstIterator ret(*this); ++(*this); return ret; }
+
+ // Pre- and post-decrement operator
+ ListConstIterator& operator--() { m_Node = m_Node->GetPrev(); return *this; }
+ ListConstIterator operator--(int) { ListConstIterator ret(*this); --(*this); return ret; }
+
+ const T& operator*() const { return static_cast<const T&>(*m_Node); }
+ const T* operator->() const { return static_cast<const T*>(m_Node); }
+
+ friend bool operator !=(const ListConstIterator& x, const ListConstIterator& y) { return x.m_Node != y.m_Node; }
+ friend bool operator ==(const ListConstIterator& x, const ListConstIterator& y) { return x.m_Node == y.m_Node; }
+
+private:
+ template <class S> friend class List;
+ ListConstIterator(const ListElement* node) : m_Node(node) {}
+ const ListElement* m_Node;
+};
+
+template <class T>
+class List
+{
+public:
+ typedef ListConstIterator<T> const_iterator;
+ typedef ListIterator<T> iterator;
+ typedef T value_type;
+
+ inline List();
+ inline ~List();
+
+ void push_back(T& node) { node.InsertInList(&m_Root); }
+ void push_front(T& node) { node.InsertInList(m_Root.m_Next); }
+ void insert(iterator pos, T& node) { node.InsertInList(&(*pos)); }
+ void erase(iterator pos) { pos->RemoveFromList(); }
+
+ void pop_back() { if (m_Root.m_Prev != &m_Root) m_Root.m_Prev->RemoveFromList(); }
+ void pop_front() { if (m_Root.m_Next != &m_Root) m_Root.m_Next->RemoveFromList(); }
+
+ iterator begin() { return iterator(m_Root.m_Next); }
+ iterator end() { return iterator(&m_Root); }
+
+ const_iterator begin() const { return const_iterator(m_Root.m_Next); }
+ const_iterator end() const { return const_iterator(&m_Root); }
+
+ T& front() { LINKED_LIST_ASSERT(!empty()); return static_cast<T&>(*m_Root.m_Next); }
+ T& back() { LINKED_LIST_ASSERT(!empty()); return static_cast<T&>(*m_Root.m_Prev); }
+
+ const T& front() const { LINKED_LIST_ASSERT(!empty()); return static_cast<const T&>(*m_Root.m_Next); }
+ const T& back() const { LINKED_LIST_ASSERT(!empty()); return static_cast<const T&>(*m_Root.m_Prev); }
+
+ bool empty() const { return begin() == end(); }
+
+ size_t size_slow() const;
+ inline void clear();
+ inline void swap(List& other);
+
+ // Insert list into list (removes elements from source)
+ inline void insert(iterator pos, List& src);
+ inline void append(List& src);
+
+private:
+ ListElement m_Root;
+};
+
+
+template <class T>
+List<T>::List()
+{
+ m_Root.m_Prev = &m_Root;
+ m_Root.m_Next = &m_Root;
+ m_Root.SetList(this);
+}
+
+template <class T>
+List<T>::~List()
+{
+ clear();
+}
+
+template <class T>
+size_t List<T>::size_slow () const
+{
+ size_t size = 0;
+ ListElement* node = m_Root.m_Next;
+ while (node != &m_Root)
+ {
+ node = node->m_Next;
+ size++;
+ }
+ return size;
+}
+
+template <class T>
+void List<T>::clear()
+{
+ ListElement* node = m_Root.m_Next;
+ while (node != &m_Root)
+ {
+ ListElement* next = node->m_Next;
+ node->m_Prev = NULL;
+ node->m_Next = NULL;
+ node->SetList(NULL);
+ node = next;
+ }
+ m_Root.m_Next = &m_Root;
+ m_Root.m_Prev = &m_Root;
+}
+
+template <class T>
+void List<T>::swap(List<T>& other)
+{
+ LINKED_LIST_ASSERT(this != &other);
+
+ std::swap(other.m_Root.m_Prev, m_Root.m_Prev);
+ std::swap(other.m_Root.m_Next, m_Root.m_Next);
+
+ if (other.m_Root.m_Prev == &m_Root)
+ other.m_Root.m_Prev = &other.m_Root;
+ if (m_Root.m_Prev == &other.m_Root)
+ m_Root.m_Prev = &m_Root;
+ if (other.m_Root.m_Next == &m_Root)
+ other.m_Root.m_Next = &other.m_Root;
+ if (m_Root.m_Next == &other.m_Root)
+ m_Root.m_Next = &m_Root;
+
+ other.m_Root.m_Prev->m_Next = &other.m_Root;
+ other.m_Root.m_Next->m_Prev = &other.m_Root;
+
+ m_Root.m_Prev->m_Next = &m_Root;
+ m_Root.m_Next->m_Prev = &m_Root;
+
+#if !UNITY_RELEASE
+ iterator my_it, my_end = end();
+ for (my_it = begin(); my_it != my_end; ++my_it)
+ my_it->m_List = this;
+ iterator other_it, other_end = other.end();
+ for (other_it = other.begin(); other_it != other_end; ++other_it)
+ other_it->m_List = &other;
+#endif
+}
+
+template <class T>
+void List<T>::insert(iterator pos, List<T>& src)
+{
+ LINKED_LIST_ASSERT(this != &src);
+ if (src.empty())
+ return;
+
+#if !UNITY_RELEASE
+ iterator src_it, src_end = src.end();
+ for (src_it = src.begin(); src_it != src_end; ++src_it)
+ src_it->m_List = this;
+#endif
+ // Insert source before pos
+ ListElement* a = pos.m_Node->m_Prev;
+ ListElement* b = pos.m_Node;
+ a->m_Next = src.m_Root.m_Next;
+ b->m_Prev = src.m_Root.m_Prev;
+ a->m_Next->m_Prev = a;
+ b->m_Prev->m_Next = b;
+ // Clear source list
+ src.m_Root.m_Next = &src.m_Root;
+ src.m_Root.m_Prev = &src.m_Root;
+}
+
+template <class T>
+void List<T>::append(List& src)
+{
+ insert(end(), src);
+}
+
+ListElement::ListElement()
+{
+ m_Prev = NULL;
+ m_Next = NULL;
+ SetList(NULL);
+}
+
+bool ListElement::IsInList() const
+{
+ return m_Prev != NULL;
+}
+
+bool ListElement::RemoveFromList()
+{
+ if (!IsInList())
+ return false;
+
+#if !UNITY_RELEASE
+ ValidateLinks();
+#endif
+ m_Prev->m_Next = m_Next;
+ m_Next->m_Prev = m_Prev;
+ m_Prev = NULL;
+ m_Next = NULL;
+ return true;
+}
+
+void ListElement::InsertInList(ListElement* pos)
+{
+ if (this == pos)
+ return;
+
+ if (IsInList())
+ RemoveFromList();
+
+#if !UNITY_RELEASE
+ m_List = pos->m_List;
+ pos->m_Prev->ValidateLinks();
+ pos->ValidateLinks();
+#endif
+ m_Prev = pos->m_Prev;
+ m_Next = pos;
+ m_Prev->m_Next = this;
+ m_Next->m_Prev = this;
+#if !UNITY_RELEASE
+ ValidateLinks();
+#endif
+ return;
+}
+
+void ListElement::ValidateLinks() const
+{
+#if !UNITY_RELEASE
+ LINKED_LIST_ASSERT(m_Prev != NULL && m_Next != NULL);
+ LINKED_LIST_ASSERT(m_Prev->m_Next == this && m_Next->m_Prev == this);
+ LINKED_LIST_ASSERT(m_Prev->m_List == m_List && m_Next->m_List == m_List);
+#endif
+}
+
+/// Allows for iterating a linked list, even if you add / remove any node during traversal.
+template<class T>
+class SafeIterator
+{
+public:
+ SafeIterator(T& list)
+ : m_SourceList(list)
+ {
+ m_CurrentNode = NULL;
+ m_ExecuteList.swap(m_SourceList);
+ }
+
+ ~SafeIterator()
+ {
+ // Call Complete if you abort the iteration!
+ LINKED_LIST_ASSERT(m_ExecuteList.empty());
+ }
+
+ // You must call complete if you are in some way aborting list iteration.
+ // If you dont call Complete, the source list will lose nodes that have not yet been iterated permanently.
+ //
+ /// SafeIterator<Behaviour*> i(myList);
+ /// i =0;
+ /// while(i.GetNext() && ++i != 3)
+ /// (**i).Update();
+ /// i.Complete();
+ void Complete()
+ {
+ m_SourceList.append(m_ExecuteList);
+ }
+
+ typename T::value_type* Next()
+ {
+ if(!m_ExecuteList.empty())
+ {
+ typename T::iterator it = m_ExecuteList.begin();
+ m_CurrentNode = &*it;
+ m_ExecuteList.erase(it);
+ m_SourceList.push_back(*m_CurrentNode);
+ }
+ else
+ {
+ m_CurrentNode = NULL;
+ }
+ return m_CurrentNode;
+ }
+
+ typename T::value_type& operator *() const { return *m_CurrentNode; }
+ typename T::value_type* operator ->() const { return m_CurrentNode; }
+
+private:
+ T m_ExecuteList;
+ T& m_SourceList;
+ typename T::value_type* m_CurrentNode;
+};
+
+
+#endif
diff --git a/Runtime/Utilities/LogAssert.cpp b/Runtime/Utilities/LogAssert.cpp
new file mode 100644
index 0000000..6a863b3
--- /dev/null
+++ b/Runtime/Utilities/LogAssert.cpp
@@ -0,0 +1,1294 @@
+#include "UnityPrefix.h"
+#include "LogAssert.h"
+#include <stdarg.h>
+#include "PathNameUtility.h"
+#include <sys/stat.h>
+#include <list>
+#include "Configuration/UnityConfigure.h"
+#include "Runtime/Threads/AtomicOps.h"
+#if !UNITY_EXTERNAL_TOOL && !UNITY_PLUGIN
+#include "Runtime/Threads/Thread.h"
+#include "Runtime/Threads/Mutex.h"
+#include "Runtime/Threads/ThreadSpecificValue.h"
+#include "Runtime/Misc/PlayerSettings.h"
+#endif
+#include "Runtime/Profiler/Profiler.h"
+
+#if UNITY_OSX || UNITY_IPHONE || UNITY_LINUX || UNITY_TIZEN
+#include <syslog.h>
+#include <sys/fcntl.h>
+#include <unistd.h>
+#include "Runtime/Utilities/File.h"
+#endif
+#if UNITY_NACL
+#include "PlatformDependent/PepperPlugin/UnityInstance.h"
+#endif
+
+#if UNITY_BB10
+#include <fcntl.h>
+#include <stdio.h>
+#include <unistd.h>
+#endif
+#if UNITY_WIN
+#if !UNITY_WP8
+#include <ShlObj.h>
+#endif
+#include "PlatformDependent/Win/PathUnicodeConversion.h"
+#include "FileUtilities.h"
+#include "PlatformDependent/Win/WinUtils.h"
+#include "io.h"
+#endif
+
+#if UNITY_TIZEN
+#include <osp/FBaseLog.h>
+#endif
+#if UNITY_EDITOR
+#include "File.h"
+#include "Editor/Src/Utility/Analytics.h"
+#endif
+
+#if WEBPLUG
+#include "ErrorExit.h"
+#endif
+
+#include "Runtime/Scripting/ScriptingUtility.h"
+
+#if UNITY_WII
+#include "Platformdependent/wii/WiiDbgUtils.h"
+#endif
+#if UNITY_ANDROID
+#include <android/log.h>
+
+int CsToAndroid[LogType_NumLevels] =
+{
+ ANDROID_LOG_ERROR, // LogType_Error = 0,
+ ANDROID_LOG_ERROR, // LogType_Assert = 1,
+ ANDROID_LOG_WARN, // LogType_Warning = 2,
+ ANDROID_LOG_INFO, // LogType_Log = 3,
+ ANDROID_LOG_ERROR, // LogType_Exception = 4,
+ ANDROID_LOG_DEBUG // LogType_Debug = 5,
+};
+#endif
+
+using namespace std;
+
+#if UNITY_WP8
+ #include "PlatformDependent\MetroPlayer\MetroUtils.h"
+#endif
+
+#if WEBPLUG && !UNITY_WIN
+#define CAP_LOG_OUTPUT_SIZE 1
+#else
+#define CAP_LOG_OUTPUT_SIZE 0
+#endif
+
+#if UNITY_WIN && DEBUGMODE && !UNITY_WP8
+ extern "C" WINBASEAPI BOOL WINAPI IsDebuggerPresent( VOID );
+ #define LOG_TO_WINDOWS_DEBUGGER \
+ char buf[4096]; buf[4095]=0; \
+ vsnprintf( buf, sizeof(buf)-1, log, list ); \
+ OutputDebugStringA( buf )
+#elif UNITY_WP8 && UNITY_DEVELOPER_BUILD
+ #define LOG_TO_WINDOWS_DEBUGGER \
+ char buf[4096]; buf[4095]=0; \
+ vsnprintf (buf, sizeof(buf) - 1, log, list); \
+ auto str = ConvertUtf8ToString (buf); \
+ s_WinRTBridge->WP8Utility->OutputLogMessage (str)
+#else
+ #define LOG_TO_WINDOWS_DEBUGGER
+#endif
+
+#if UNITY_FLASH
+extern "C" void Ext_Flash_LogCallstack();
+#endif
+
+static LogEntryHandler gCurrentLogEntryHandler = NULL;
+static std::list<LogEntryHandler> *gCleanLogEntryHandlers = NULL;
+
+void ReleaseLogHandlers()
+{
+ if (gCleanLogEntryHandlers != NULL)
+ {
+ delete gCleanLogEntryHandlers;
+ gCleanLogEntryHandlers = NULL;
+ }
+}
+
+void SetLogEntryHandler(LogEntryHandler newHandler)
+{
+ gCurrentLogEntryHandler = newHandler;
+}
+
+void AddCleanLogEntryHandler(LogEntryHandler newHandler)
+{
+ if (gCleanLogEntryHandlers == NULL)
+ gCleanLogEntryHandlers = new std::list<LogEntryHandler>();
+
+ gCleanLogEntryHandlers->push_back(newHandler);
+}
+
+FILE* gConsoleFile = NULL;
+FILE* gReproductionLogFile = NULL;
+
+bool DefaultCleanLogHandlerv (LogType logType, const char* log, va_list list);
+
+void InitializeCleanedLogFile (FILE* file)
+{
+ Assert(gReproductionLogFile == NULL);
+ Assert(gCleanLogEntryHandlers == NULL || count(gCleanLogEntryHandlers->begin(), gCleanLogEntryHandlers->end(), &DefaultCleanLogHandlerv) == 0);
+
+ gReproductionLogFile = file;
+
+ AddCleanLogEntryHandler(&DefaultCleanLogHandlerv);
+}
+
+static StaticString gConsolePath;
+#if CAP_LOG_OUTPUT_SIZE
+static int gConsoleSizeCheck = 0;
+#endif
+#if !UNITY_EXTERNAL_TOOL
+static UNITY_TLS_VALUE(int) gRecursionLock;
+#endif
+
+enum { kMaxLogSize = 2000000 };
+
+
+
+#if UNITY_OSX || UNITY_IPHONE || UNITY_LINUX || UNITY_BB10 || UNITY_TIZEN
+fpos_t gStdOutPosition;
+int gStdoutFd;
+void ResetStdout()
+{
+ fflush(stdout);
+ dup2(gStdoutFd, fileno(stdout));
+ close(gStdoutFd);
+ clearerr(stdout);
+ fsetpos(stdout, &gStdOutPosition);
+}
+#endif
+
+#if UNITY_XENON || UNITY_PS3
+void LogOutputToSpecificFile (const char* path)
+{
+ if (path == NULL)
+ return;
+
+ gConsolePath = path;
+ gConsoleFile = fopen(path, "w");
+ if(gConsoleFile)
+ fclose(gConsoleFile);
+
+}
+#elif !UNITY_PS3 && !UNITY_ANDROID && !UNITY_PEPPER && !UNITY_FLASH
+
+void LogOutputToSpecificFile (const char* path)
+{
+#if UNITY_OSX || UNITY_IPHONE || UNITY_LINUX || UNITY_BB10 || UNITY_TIZEN
+
+ // Save the stdout position for later
+ fgetpos(stdout, &gStdOutPosition);
+ gStdoutFd = dup(fileno(stdout));
+
+ if (path == NULL || strlen(path) == 0)
+ {
+ gConsoleFile = stdout;
+ }
+ else
+ {
+ gConsoleFile = fopen(path, "w");
+ int fd = open(path, O_WRONLY, 0);
+ dup2(fd, 1);
+ dup2(fd, 2);
+ close(fd);
+ }
+
+#elif UNITY_WII
+
+ // TODO: see if logging to the host machine (windows) works
+ if (path == NULL)
+ return;
+
+ gConsolePath = path;
+ gConsoleFile = fopen(path, "w");
+
+#else
+
+ // On windows just default to editor.log instead
+ if (path == NULL)
+ return;
+
+ gConsolePath = path;
+ gConsoleFile = fopen(path, "w");
+#endif
+}
+#endif
+
+#if SUPPORT_ENVIRONMENT_VARIABLES
+std::string GetStringFromEnv(const char *envName)
+{
+ const char* env = getenv( envName );
+ std::string result;
+ if( env != NULL && env[0] != 0 )
+ result.append(env);
+ return result;
+}
+std::string GetCustomLogFile()
+{
+ return GetStringFromEnv("UNITY_LOG_FILE");
+}
+std::string GetCleanedLogFile()
+{
+ return GetStringFromEnv("UNITY_CLEANED_LOG_FILE");
+}
+#endif
+
+#if UNITY_WIN
+
+string SetLogFilePath(string const& path)
+{
+ Assert(gConsolePath.empty());
+
+ gConsolePath = path;
+
+ #if !UNITY_EDITOR && !UNITY_EXTERNAL_TOOL && !UNITY_WINRT
+
+ if (!gConsolePath.empty())
+ {
+ string const customLogFile = GetCustomLogFile();
+
+ if (!customLogFile.empty())
+ {
+ gConsolePath = customLogFile.c_str();
+ }
+ }
+
+ #endif
+
+ return gConsolePath.c_str();
+}
+
+namespace
+{
+ int gStdOutFd = -1;
+ int gStdErrFd = -1;
+ FILE* gStdOutFile = NULL;
+ FILE* gStdErrFile = NULL;
+
+ void CloseConsoleWin()
+ {
+ gConsoleFile = NULL;
+
+ if (NULL != gStdOutFile)
+ {
+ int const result = fclose(gStdOutFile);
+ //Assert(0 == result);
+
+ gStdOutFile = NULL;
+ }
+
+ if (NULL != gStdErrFile)
+ {
+ int const result = fclose(gStdErrFile);
+ //Assert(0 == result);
+
+ gStdErrFile = NULL;
+ }
+
+ if (-1 != gStdOutFd)
+ {
+ int const result = _dup2(gStdOutFd, 1);
+ //Assert(0 == result);
+
+ gStdOutFd = -1;
+ }
+
+ if (-1 != gStdErrFd)
+ {
+ int const result = _dup2(gStdErrFd, 2);
+ //Assert(0 == result);
+
+ gStdErrFd = -1;
+ }
+ gConsolePath.clear();
+ }
+
+ void OpenConsoleWin()
+ {
+ // don't assert in this function because it might cause stack overflow
+ std::wstring widePath;
+
+ //Assert(NULL == gConsoleFile);
+
+ // check for no log file
+
+ if (gConsolePath.empty())
+ {
+ gConsoleFile = stdout;
+ return;
+ }
+
+ // duplicate stdout and stderr file descriptors so they can be restored later
+
+ gStdOutFd = _dup(1);
+ //Assert(-1 != gStdOutFd);
+
+ if (-1 == gStdOutFd)
+ {
+ goto error;
+ }
+
+ gStdErrFd = _dup(2);
+ //Assert(-1 != gStdErrFd);
+
+ if (-1 == gStdErrFd)
+ {
+ goto error;
+ }
+
+ // reassign stdout and stderr file pointers
+
+ ConvertUnityPathName(gConsolePath, widePath);
+
+ gStdOutFile = _wfreopen(widePath.c_str(), L"a", stdout);
+ //Assert(NULL != gStdOutFile);
+
+ if (NULL == gStdOutFile)
+ {
+ goto error;
+ }
+
+ gStdErrFile = _wfreopen(widePath.c_str(), L"a", stderr);
+ //Assert(NULL != gStdErrFile);
+
+ if (NULL == gStdErrFile)
+ {
+ goto error;
+ }
+
+ // redirect stderr to stdout
+
+ int const error = _dup2(1, 2);
+ //Assert(0 == error);
+
+ if (0 != error)
+ {
+ goto error;
+ }
+
+ // disable stdout and stderr buffering
+
+ setbuf(stdout, NULL);
+ setbuf(stderr, NULL);
+
+ // done
+
+ gConsoleFile = stdout;
+ return;
+
+ // failed
+
+ error:
+
+ CloseConsoleWin();
+
+ gConsoleFile = stdout;
+ return;
+ }
+}
+
+#endif
+
+#if (UNITY_OSX || UNITY_LINUX) && !UNITY_EXTERNAL_TOOL
+// Get the logfile path, relative to the user's home directory, using a system-specific subpath
+static std::string GetHomedirLogfile (const std::string &relativePath)
+{
+ string folder = getenv ("HOME");
+ if (folder.empty ())
+ return "";
+
+ // Create log file and parent folders
+ folder = AppendPathName( folder, relativePath);
+ CreateDirectoryRecursive (folder);
+
+ #if UNITY_EDITOR
+ std::string result = AppendPathName( folder, "Editor.log" );
+ // move any existing log file into Editor-prev.log
+ if( IsFileCreated(result) )
+ MoveReplaceFile( result, AppendPathName(folder,"Editor-prev.log" ) );
+ #else
+ std::string result = GetCustomLogFile();
+ if (result.empty())
+ result = AppendPathName( folder, "Player.log" );
+ #endif
+
+ return result;
+}
+
+// Open the console path and redirect stdout/stderr if appropriate
+static void OpenConsoleFile ()
+{
+ #if UNITY_EDITOR
+ gConsoleFile = fopen(gConsolePath.c_str(), "w");
+ #else
+ gConsoleFile = fopen(gConsolePath.c_str(), "w+");
+ #endif
+
+ #if !WEBPLUG
+ // Save the stdout position for later
+ fgetpos(stdout, &gStdOutPosition);
+ gStdoutFd = dup(fileno(stdout));
+
+ if (gConsoleFile)
+ {
+ int fd = fileno (gConsoleFile);
+
+ if (dup2 (fd, fileno (stdout)) < 0)
+ fprintf (stderr, "Failed to redirect stdout to the console file %s.\n", gConsolePath.c_str ());
+ if (dup2 (fd, fileno (stderr)) < 0)
+ fprintf (stderr, "Failed to redirect stderr to the console file %s.\n", gConsolePath.c_str ());
+ }
+ #endif
+}
+#endif
+
+FILE* OpenConsole ()
+{
+ if (gConsoleFile != NULL)
+ return gConsoleFile;
+
+ #define PLATFORM_ALWAYS_USES_STDOUT_FOR_LOG (UNITY_XENON || UNITY_IPHONE || UNITY_ANDROID || UNITY_PEPPER || UNITY_FLASH || UNITY_WII || UNITY_PS3 || UNITY_EXTERNAL_TOOL || UNITY_WEBGL || UNITY_BB10 || UNITY_TIZEN || ENABLE_GFXDEVICE_REMOTE_PROCESS_WORKER )
+
+ #if GAMERELEASE && !UNITY_EXTERNAL_TOOL && !WEBPLUG && !UNITY_PLUGIN && !PLATFORM_ALWAYS_USES_STDOUT_FOR_LOG
+ if (GetPlayerSettingsPtr() == NULL)
+ return stdout;
+
+ if (!GetPlayerSettings().GetUsePlayerLog())
+ {
+ gConsoleFile = stdout;
+ return gConsoleFile;
+ }
+ #endif
+
+ #if PLATFORM_ALWAYS_USES_STDOUT_FOR_LOG
+
+ #if UNITY_NACL
+ // in nacl, we can't write to a custom log location.
+ // so if we need a clean log, use stdout for that, and use 0 for the
+ // normal, non-clean log.
+ if (GetUnityInstance().GetCleanLog())
+ return 0;
+ #endif
+
+ gConsoleFile = stdout;
+
+ #elif UNITY_OSX
+ gConsolePath = GetHomedirLogfile ("Library/Logs/Unity");
+ if (!gConsolePath.empty())
+ OpenConsoleFile ();
+
+ #elif UNITY_LINUX
+ gConsolePath = GetHomedirLogfile (".config/unity3d");
+ if (!gConsolePath.empty())
+ OpenConsoleFile ();
+
+ #elif UNITY_WIN
+
+ OpenConsoleWin();
+
+ #elif UNITY_PS3
+ gConsoleFile = stdout;
+ // When running from a read only file system, stdout is a valid file pointer, but it crashes later
+ // if trying to write into it. So we check if valid _fileno exists for it.
+ if( gConsoleFile && fileno(gConsoleFile) < 0 )
+ gConsoleFile = NULL;
+
+ #else
+
+ #error "Unknown platform"
+
+ #endif
+
+ return gConsoleFile;
+}
+
+#if UNITY_WIN
+void CloseConsoleFile()
+{
+ CloseConsoleWin();
+}
+#endif
+
+#if UNITY_EDITOR
+string GetEditorConsoleLogPath ()
+{
+ #if UNITY_OSX
+
+ string home = getenv ("HOME");
+ return AppendPathName (home, "Library/Logs/Unity/Editor.log");
+
+ #elif UNITY_WIN
+
+ return gConsolePath.c_str();
+
+ #elif UNITY_LINUX
+
+ string home = getenv ("HOME");
+ return AppendPathName (home, ".config/Unity/Editor/Editor.log");
+
+ #else
+ #error "Unknown platform"
+ #endif
+}
+
+string GetMonoDevelopLogPath ()
+{
+ #if UNITY_OSX
+
+ string result = getenv ("HOME");
+ return AppendPathName( result, "Library/Logs/MonoDevelop/MonoDevelop.log");
+
+ #elif UNITY_WIN
+
+ wchar_t widePath[MAX_PATH];
+ if( SUCCEEDED(SHGetFolderPathW( NULL, CSIDL_APPDATA, NULL, SHGFP_TYPE_CURRENT, widePath )) )
+ {
+ std::string folder;
+ ConvertWindowsPathName( widePath, folder );
+ folder = AppendPathName( folder, "MonoDevelop-Unity" );
+ return AppendPathName( folder, "log.txt" );
+ }
+
+ #elif UNITY_LINUX
+
+ string result = getenv ("HOME");
+ return AppendPathName( result, ".config/MonoDevelop-Unity/log.txt");
+
+ #else
+ #error "Unknown platform"
+ #endif
+
+ return "";
+}
+
+#if UNITY_OSX
+string GetPlayerConsoleLogPath ()
+{
+ string home = getenv ("HOME");
+ return AppendPathName (home, "Library/Logs/Unity/Player.log");
+}
+#endif
+
+#if UNITY_LINUX
+string GetPlayerConsoleLogPath ()
+{
+ string home = getenv ("HOME");
+ return AppendPathName (home, ".config/unity3d/Editor/Player.log");
+}
+#endif
+#endif
+
+string GetConsoleLogPath ()
+{
+ return gConsolePath.c_str();
+}
+
+#if (UNITY_XENON || UNITY_PS3) && !MASTER_BUILD
+static Mutex s_mutex;
+#endif
+
+void printf_consolev (LogType logType, const char* log, va_list alist)
+{
+ va_list list;
+ va_copy (list, alist);
+
+ if (gCurrentLogEntryHandler && !gCurrentLogEntryHandler (logType, log, list))
+ return;
+
+#if UNITY_FLASH
+ char buffer[1024 * 10];
+ vsnprintf (buffer, 1024 * 10, log, list);
+ Ext_Trace(buffer);
+
+ va_end (list);
+ return;
+#endif
+
+#if UNITY_ANDROID
+ if (gReproductionLogFile == NULL) // gReproductionLogFile / 'cleanedLogFile' should remove all other logging
+ {
+ if (ENABLE_PROFILER /* == development player */ || logType < LogType_Debug)
+ __android_log_vprint(CsToAndroid[logType], "Unity", log, list);
+ }
+
+ va_end (list);
+ return;
+#endif
+
+#if UNITY_TIZEN
+ if (gReproductionLogFile == NULL)
+ {
+ static char buffer[1024 * 10];
+ memset(buffer, 0, 1024*10);
+ vsnprintf (buffer, 1024 * 10, log, list);
+ AppLogTagInternal("Unity", "", 0, buffer);
+ }
+#endif
+
+#if UNITY_XENON
+#if !MASTER_BUILD
+ Mutex::AutoLock lock(s_mutex);
+
+ char buffer[1024 * 8] = { 0 };
+ vsnprintf(buffer, 1024 * 8, log, list);
+
+ gConsoleFile = fopen(gConsolePath.c_str(), "a");
+ if(gConsoleFile)
+ {
+ fprintf(gConsoleFile, buffer);
+ fflush(gConsoleFile);
+ fclose(gConsoleFile);
+ }
+
+ OutputDebugString(buffer);
+
+#endif
+ va_end (list);
+ return;
+#endif
+
+#if UNITY_PS3
+#if !MASTER_BUILD
+ Mutex::AutoLock lock(s_mutex);
+
+ gConsoleFile = fopen(gConsolePath.c_str(), "a");
+ if(gConsoleFile)
+ {
+ vfprintf (gConsoleFile, log, list);
+ fflush( gConsoleFile );
+ fclose( gConsoleFile );
+ vfprintf (stdout, log, list);
+ }
+ else
+ {
+ vfprintf(stdout, log, list);
+ fflush(stdout);
+ }
+
+#endif
+ va_end (list);
+ return;
+#endif
+
+#if UNITY_WII
+ #if !MASTER_BUILD
+ vfprintf (stdout, log, list);
+ fflush (stdout);
+ va_end (list);
+ #endif
+ return;
+#endif
+
+ if (gConsoleFile == NULL)
+ {
+ if (OpenConsole() == NULL) {
+ va_end (list);
+ return;
+ }
+ }
+
+ if (gConsoleFile)
+ {
+ vfprintf (gConsoleFile, log, list);
+ fflush( gConsoleFile );
+
+ // Clamp the size of the file to 100kb with a rolling buffer.
+ // copy last 50kb to beginning of file.
+ #if CAP_LOG_OUTPUT_SIZE
+ gConsoleSizeCheck++;
+ if (gConsoleSizeCheck > 20)
+ {
+ gConsoleSizeCheck = 0;
+ struct stat statbuffer;
+ if( ::stat(gConsolePath.c_str(), &statbuffer) == 0 && statbuffer.st_size > kMaxLogSize)
+ {
+ FILE* file = fopen(gConsolePath.c_str(), "r");
+ if (file)
+ {
+ fseek(file, statbuffer.st_size - kMaxLogSize / 2, SEEK_SET);
+ UInt8* buffer = new UInt8[kMaxLogSize / 2];
+ if (fread(buffer, 1, kMaxLogSize / 2, file) == kMaxLogSize / 2)
+ {
+ fclose(file);
+ fclose(gConsoleFile);
+ gConsoleFile = fopen(gConsolePath.c_str(), "w");
+ fwrite(buffer, 1, kMaxLogSize / 2, gConsoleFile);
+ }
+ else
+ {
+ fclose(file);
+ }
+ delete[] buffer;
+ }
+ }
+ }
+ #endif
+ }
+ else
+ {
+#ifndef DISABLE_TTY
+ vfprintf (stdout, log, list);
+ fflush( stdout );
+#endif
+ }
+
+ LOG_TO_WINDOWS_DEBUGGER;
+ va_end (list);
+}
+
+extern "C" void printf_console_log(const char* log, va_list list)
+{
+ printf_consolev(LogType_Log, log, list);
+}
+
+extern "C" void printf_console (const char* log, ...)
+{
+ va_list vl;
+ va_start(vl, log);
+ printf_consolev(LogType_Debug, log, vl);
+ va_end(vl);
+}
+
+static void InternalLogConsole (const char* log, ...)
+{
+ va_list vl;
+ va_start(vl, log);
+ printf_consolev(LogType_Log, log, vl);
+ va_end(vl);
+}
+
+static void InternalWarningConsole (const char* log, ...)
+{
+ va_list vl;
+ va_start(vl, log);
+ printf_consolev(LogType_Warning, log, vl);
+ va_end(vl);
+}
+
+static void InternalAssertConsole (const char* log, ...)
+{
+ va_list vl;
+ va_start(vl, log);
+ printf_consolev(LogType_Assert, log, vl);
+ va_end(vl);
+}
+
+static void InternalErrorConsole (const char* log, ...)
+{
+ va_list vl;
+ va_start(vl, log);
+ printf_consolev(LogType_Error, log, vl);
+ va_end(vl);
+}
+
+static void InternalIgnoreConsole (const char* log, ...)
+{
+}
+
+static LogToConsoleImpl* gLogToConsoleFunc = NULL;
+static LogCallback* gLogCallbackFunc = NULL;
+static bool gLogCallbackFuncThreadSafe = false;
+static PreprocessCondition* gPreprocessor = NULL;
+static RemoveLogFunction* gRemoveLog = NULL;
+static RemoveLogFunction* gShowLogWithMode = NULL;
+extern "C" void __msl_assertion_failed(char const *condition, char const *filename, char const *funcname, int lineno);
+
+/*
+extern "C"
+{
+void __eprintf(const char* log, ...)
+{
+printf_console (log, va_list(&log + 1));
+}
+
+void malloc_printf (const char* log, ...)
+{
+ printf_console (log, va_list(&log + 1));
+}
+}
+*/
+void RegisterLogToConsole (LogToConsoleImpl* func)
+{
+ gLogToConsoleFunc = func;
+}
+
+void RegisterLogCallback (LogCallback* callback, bool threadsafe)
+{
+ gLogCallbackFunc = callback;
+ gLogCallbackFuncThreadSafe = threadsafe;
+}
+
+void RegisterLogPreprocessor (PreprocessCondition* func)
+{
+ gPreprocessor = func;
+}
+
+void RegisterRemoveImportErrorFromConsole (RemoveLogFunction* func)
+{
+ gRemoveLog = func;
+}
+
+void RegisterShowErrorWithMode (RemoveLogFunction* func)
+{
+ gShowLogWithMode = func;
+}
+
+
+extern "C" void __msl_assertion_failed(char const *condition, char const *filename, char const *funcname, int lineno)
+{
+ DebugStringToFile (condition, 0, filename, lineno, kAssert, 0);
+}
+
+inline bool ContainsNewLine (const char* c)
+{
+ while (*c != '\0')
+ {
+ if (*c == '\n')
+ return true;
+ c++;
+ }
+ return false;
+}
+
+
+static void CompilerErrorAnalytics (int mode, const char *condition)
+{
+#if UNITY_EDITOR
+ if ( mode & kScriptCompileError )
+ {
+ std::string message = condition;
+
+ // The compiler error message is formatted like this:
+ // filename: error/warning number: description
+ int n1 = message.find(": ");
+ if ( n1 != string::npos )
+ {
+ int n2 = message.find(": ", n1+2);
+ if ( n2 != string::npos )
+ {
+ string error = message.substr(n1+2, n2-n1-2);
+ string description = message.substr(n2+2);
+ AnalyticsTrackEvent("Compiler", error, description, 1);
+ return;
+ }
+ }
+ AnalyticsTrackEvent("Compiler", "Unknown", message, 1);
+ }
+#endif
+}
+
+bool DefaultCleanLogHandlerv (LogType logType, const char* log, va_list alist)
+{
+ va_list list;
+ va_copy (list, alist);
+
+#if UNITY_ANDROID // On Android we don't use a separate clean log-file, but instead we clean up the actual logcat
+#define LOG_PRINTF(x, ...) __android_log_vprint(ANDROID_LOG_INFO, "Unity", __VA_ARGS__)
+#define LOG_FLUSH(x)
+#else
+#define LOG_PRINTF vfprintf
+#define LOG_FLUSH fflush
+#endif
+
+LOG_PRINTF (gReproductionLogFile, log, list);
+LOG_FLUSH( gReproductionLogFile );
+
+#undef LOG_PRINTF
+#undef LOG_FLUSH
+
+ va_end (list);
+
+ return true;
+}
+
+void CleanLogHandler(LogType logType, const char* log, ...)
+{
+ if (gCleanLogEntryHandlers != NULL)
+ {
+ for (std::list<LogEntryHandler>::iterator it = gCleanLogEntryHandlers->begin();
+ it != gCleanLogEntryHandlers->end();
+ it++)
+ {
+ va_list vl;
+ va_start(vl, log);
+ (**it)(logType, log, vl);
+ }
+ }
+}
+
+
+typedef void PrintConsole (const char* log, ...);
+
+// convert the log mode to the cs LogType enum
+// LogType.Error = 0, LogType.Assert = 1, LogType.Warning = 2, LogType.Log = 3, LogType.Exception = 4
+inline LogType LogModeToLogType(int mode)
+{
+ LogType logType;
+ if ( mode & (kScriptingException) ) logType = LogType_Exception;
+ else if ( mode & (kError | kFatal | kScriptingError | kScriptCompileError | kStickyError | kAssetImportError | kGraphCompileError) ) logType = LogType_Error;
+ else if ( mode & kAssert ) logType = LogType_Assert;
+ else if ( mode & (kScriptingWarning | kScriptCompileWarning | kAssetImportWarning) ) logType = LogType_Warning;
+ else logType = LogType_Log;
+
+ return logType;
+}
+
+void DebugStringToFilePostprocessedStacktrace (const char* condition, const char* strippedStacktrace, const char* stacktrace, int errorNum, const char* file, int line, int mode, int objectInstanceID, int identifier)
+{
+ LogType logType = LogModeToLogType(mode);
+
+ #if !UNITY_EXTERNAL_TOOL
+ int depth = gRecursionLock;
+ if (depth == 1)
+ return;
+ gRecursionLock = 1;
+
+ if ( gLogCallbackFunc)
+ {
+#if SUPPORT_THREADS
+ if (gLogCallbackFuncThreadSafe || Thread::CurrentThreadIsMainThread())
+#endif
+ gLogCallbackFunc (condition, strippedStacktrace, (int)logType);
+ }
+
+ #endif
+#if UNITY_WII
+ if (mode & kFatal)
+ {
+ wii::DbgFatalError ("%s\n", condition);
+ }
+ else if (mode & (kAssert | kError))
+ {
+ wii::DbgOutput ("%s\n", condition);
+ }
+#elif UNITY_XENON && MASTER_BUILD
+ return;
+#endif
+ CompilerErrorAnalytics (mode, condition);
+
+ string conditionAndStacktrace = condition;
+ if ( stacktrace )
+ {
+ conditionAndStacktrace += "\n";
+ conditionAndStacktrace += stacktrace;
+ }
+
+ string conditionAndStrippedStacktrace = condition;
+ if ( stacktrace )
+ {
+ conditionAndStrippedStacktrace += "\n";
+ conditionAndStrippedStacktrace += strippedStacktrace;
+ }
+
+ if (errorNum)
+ CleanLogHandler (logType, "%s (Error: %d)\n\n", condition, errorNum);
+ else
+ CleanLogHandler (logType, "%s\n\n", condition);
+
+
+ PrintConsole* printConsole = InternalIgnoreConsole;
+ // Logs
+ if (mode & (kLog | kScriptingLog))
+ printConsole = InternalLogConsole;
+ else if (mode & (kScriptingWarning | kAssetImportWarning))
+ printConsole = InternalWarningConsole;
+ else if (mode & kAssert)
+ printConsole = InternalAssertConsole;
+ // Real errors ---- YOU WANT TO BREAKPOINT THIS LINE!
+ else
+ printConsole = InternalErrorConsole;
+
+
+ if (errorNum)
+ {
+ if (ContainsNewLine (conditionAndStacktrace.c_str()))
+ printConsole ("%s \n(Error: %li Filename: %s Line: %li)\n\n", conditionAndStacktrace.c_str(), errorNum, file, line);
+ else
+ printConsole ("%s (Error: %li Filename: %s Line: %li)\n", conditionAndStacktrace.c_str(), errorNum, file, line);
+ }
+ else
+ {
+ if (ContainsNewLine (conditionAndStacktrace.c_str()))
+ printConsole ("%s \n(Filename: %s Line: %li)\n\n", conditionAndStacktrace.c_str(), file, line);
+ else
+ printConsole ("%s (Filename: %s Line: %li)\n", conditionAndStacktrace.c_str(), file, line);
+ }
+
+ if (gLogToConsoleFunc)
+ gLogToConsoleFunc (conditionAndStrippedStacktrace, errorNum, file, line, mode, objectInstanceID, identifier);
+
+ #if WEBPLUG
+ #if DEBUGMODE && 0
+ if (mode & kAssert)
+ {
+ DebugBreak();
+ }
+ #endif
+ if (mode & kFatal)
+ {
+ ExitWithErrorCode(kErrorFatalException);
+ }
+ #elif UNITY_WINRT
+ if (mode & kAssert)
+ {
+ // Used to set a breakpoint
+ int s = 5;
+ }
+ if (mode & kFatal)
+ __debugbreak();
+ #endif
+
+ #if !UNITY_EXTERNAL_TOOL
+ gRecursionLock = 0;
+ #endif
+}
+
+PROFILER_INFORMATION (gProfilerLogString, "LogStringToConsole", kProfilerOther);
+
+void DebugStringToFile (const char* condition, int errorNum, const char* file, int line, int mode, int objectInstanceID, int identifier)
+{
+ PROFILER_AUTO(gProfilerLogString, NULL);
+ SET_ALLOC_OWNER(NULL);
+#if UNITY_ANDROID && i386
+ printf_console("%s: %d at %s:%d (%d, %d, %d)\n", condition, errorNum, file, line, mode, objectInstanceID, identifier);
+#endif
+
+#if UNITY_FLASH || UNITY_WEBGL
+ printf_console(condition);
+ if (!(mode&(kScriptingWarning | kScriptingLog ))){
+#if UNITY_FLASH
+ Ext_Flash_LogCallstack();//Let's only switch this on for internal debugging, it sucks having to go through callstacks because of a log.
+#elif UNITY_WEBGL
+ // __asm __volatile__("console.log(new Error().stack)");
+#endif
+ }else{
+ return;
+ }
+#endif
+
+ string stackTrace;
+ string strippedStackTrace;
+ string preprocessedFile;
+ if (gPreprocessor)
+ {
+ preprocessedFile = file;
+ string conditionStr = condition;
+
+ gPreprocessor (conditionStr, strippedStackTrace, stackTrace, errorNum, preprocessedFile, &line, mode, objectInstanceID);
+
+ file = preprocessedFile.c_str ();
+ }
+
+ DebugStringToFilePostprocessedStacktrace (condition, strippedStackTrace.c_str(), stackTrace.c_str(), errorNum, file, line, mode, objectInstanceID, identifier);
+
+}
+
+void RemoveErrorWithIdentifierFromConsole (int identifier)
+{
+ if (gRemoveLog)
+ gRemoveLog (identifier);
+}
+
+void ShowErrorWithMode (int identifier)
+{
+ if (gShowLogWithMode)
+ gShowLogWithMode (identifier);
+}
+
+
+void DebugTextLineByLine(const char* text, int maxLineLen)
+{
+ if(maxLineLen == -1)
+ maxLineLen = 1023;
+
+ // on android we can output maximum 1023 chars (1024 including \0)
+#if UNITY_ANDROID
+ if(maxLineLen > 1023)
+ maxLineLen = 1023;
+#endif
+
+ #define SKIP_CRLF(ptr) \
+ do{ \
+ while( (*ptr == '\r' || *ptr == '\n') && *ptr != 0 ) \
+ ++ptr; \
+ } while(0)
+
+ #define SKIP_UNTIL_CRLF(ptr) \
+ do{ \
+ while( *ptr != '\r' && *ptr != '\n' && *ptr != 0 ) \
+ ++ptr; \
+ } while(0)
+
+
+ const char* lineStart = text;
+ SKIP_CRLF(lineStart);
+
+ std::string out;
+ while(*lineStart != 0)
+ {
+ const char* lineEnd = lineStart;
+ SKIP_UNTIL_CRLF(lineEnd);
+
+ if(lineEnd - lineStart > maxLineLen)
+ lineEnd = lineStart + maxLineLen;
+
+ bool needSkipCRLF = *lineEnd == '\r' || *lineEnd == '\n';
+
+ out.assign(lineStart, lineEnd-lineStart);
+ #if UNITY_ANDROID
+ __android_log_print( ANDROID_LOG_DEBUG, "Unity", "%s", out.c_str());
+ #else
+ printf_console("%s\n", out.c_str());
+ #endif
+
+ lineStart = lineEnd;
+ if(needSkipCRLF)
+ SKIP_CRLF(lineStart);
+ }
+
+ #undef SKIP_UNTIL_CRLF
+ #undef SKIP_CRLF
+}
+
+
+#if UNITY_IPHONE || UNITY_ANDROID
+
+#if UNITY_IPHONE
+#include <execinfo.h>
+#elif UNITY_ANDROID
+#include "PlatformDependent/AndroidPlayer/utils/backtrace_impl.h"
+#endif
+
+void DumpCallstackConsole( const char* prefix, const char* file, int line )
+{
+ const size_t kMaxDepth = 100;
+
+ size_t stackDepth;
+ void* stackAddr[kMaxDepth];
+ char** stackSymbol;
+
+ stackDepth = backtrace(stackAddr, kMaxDepth);
+ stackSymbol = backtrace_symbols(stackAddr, stackDepth);
+
+ printf_console("%s%s:%d\n", prefix, file, line);
+
+ // TODO: demangle? use unwind to get more info?
+ // start from 1 to bypass self
+ for( unsigned stackI = 1 ; stackI < stackDepth ; ++stackI )
+ {
+ #if UNITY_IPHONE
+ printf_console(" #%02d %s\n", stackI-1, stackSymbol[stackI]);
+ #elif UNITY_ANDROID // just for now
+ __android_log_print( ANDROID_LOG_DEBUG, "DEBUG", " #%02d %s\n", stackI-1, stackSymbol[stackI]);
+ #endif
+ }
+
+ ::free(stackSymbol);
+}
+
+#else
+
+void DumpCallstackConsole( const char* /*prefix*/, const char* /*file*/, int /*line*/ )
+{
+}
+
+#endif
+
+#if UNITY_OSX || UNITY_IPHONE
+#include <sys/sysctl.h>
+// taken from apple technical note QA1361
+bool EXPORT_COREMODULE IsDebuggerPresent()
+{
+ static bool debuggerPresent = false;
+ static bool inited = false;
+
+ if(!inited)
+ {
+ kinfo_proc info;
+ ::memset(&info, 0x00, sizeof(info));
+
+ int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, ::getpid()};
+
+ size_t size = sizeof(info);
+ sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, NULL, 0);
+
+ debuggerPresent = (info.kp_proc.p_flag & P_TRACED) != 0;
+ inited = true;
+ }
+
+ return debuggerPresent;
+}
+#endif
+
+#if UNITY_LINUX || UNITY_TIZEN
+#include <sys/ptrace.h>
+#include <sys/prctl.h>
+#include <sys/wait.h>
+#include <errno.h>
+
+bool IsDebuggerPresent ()
+{
+ int status = 0,
+ pid = -1,
+ result = 0;
+
+#ifdef PR_SET_PTRACER
+// Guard ancient versions (&*^$%@*&#^%$ build agents)
+ // Enable tracing by self and children
+ if (0 != prctl (PR_SET_PTRACER, getpid (), 0, 0, 0))
+ {
+ ErrorString (Format ("Unable to enable tracing: %s", strerror (errno)));
+ return false;
+ }
+#endif
+
+ pid = fork ();
+ if (0 > pid)
+ {
+ ErrorString ("Error creating child process");
+ return false;
+ }
+
+ if (0 == pid)
+ {
+ // Child
+ int parent = getppid();
+
+ // Attempt to attach to parent
+ if (ptrace (PTRACE_ATTACH, parent, NULL, NULL) == 0)
+ {
+ // Debugger is not attached; continue parent once it stops
+ waitpid (parent, NULL, WUNTRACED | WCONTINUED);
+ ptrace (PTRACE_DETACH, getppid (), NULL, NULL);
+ result = 0;
+ }
+ else
+ {
+ // Debugger is already tracing parent
+ result = 1;
+ }
+ exit(result);
+ }
+
+ // Parent
+ waitpid (pid, &status, 0);
+ result = WEXITSTATUS (status);
+#ifdef PR_SET_PTRACER
+ // Clear tracing
+ prctl (PR_SET_PTRACER, 0, 0, 0, 0);
+#endif
+
+ return (0 != result);
+}
+#endif
+
+
diff --git a/Runtime/Utilities/LogAssert.h b/Runtime/Utilities/LogAssert.h
new file mode 100644
index 0000000..f3cb304
--- /dev/null
+++ b/Runtime/Utilities/LogAssert.h
@@ -0,0 +1,340 @@
+#ifndef LOGASSERT_H
+#define LOGASSERT_H
+
+class Object;
+#include <string>
+#include <set>
+#include <stdarg.h>
+#include <stdio.h>
+#include "Annotations.h"
+#include "Runtime/Utilities/FileStripped.h"
+#if UNITY_EXTERNAL_TOOL
+#define EXPORT_COREMODULE
+#else
+#include "Runtime/Modules/ExportModules.h"
+#endif
+
+#if UNITY_LINUX || UNITY_BB10 || UNITY_TIZEN
+ #include <signal.h>
+#endif
+
+#if UNITY_OSX || UNITY_IPHONE || UNITY_LINUX
+ extern bool IsDebuggerPresent();
+#endif
+
+enum
+{
+ kError = 1 << 0,
+ kAssert = 1 << 1,
+ kLog = 1 << 2,
+ kFatal = 1 << 4,
+ kAssetImportError = 1 << 6,
+ kAssetImportWarning = 1 << 7,
+ kScriptingError = 1 << 8,
+ kScriptingWarning = 1 << 9,
+ kScriptingLog = 1 << 10,
+ kScriptCompileError = 1 << 11,
+ kScriptCompileWarning = 1 << 12,
+ kStickyError = 1 << 13,
+ kMayIgnoreLineNumber = 1 << 14,
+ kReportBug = 1 << 15,
+ kDisplayPreviousErrorInStatusBar = 1 << 16,
+ kScriptingException = 1 << 17,
+ kDontExtractStacktrace = 1 << 18,
+ kGraphCompileError = 1 << 20,
+};
+
+/// The type of the log message in the delegate registered with Application.RegisterLogCallback.
+///
+enum LogType
+{
+ /// LogType used for Errors.
+ LogType_Error = 0,
+ /// LogType used for Asserts. (These indicate an error inside Unity itself.)
+ LogType_Assert = 1,
+ /// LogType used for Warnings.
+ LogType_Warning = 2,
+ /// LogType used for regular log messages.
+ LogType_Log = 3,
+ /// LogType used for Exceptions.
+ LogType_Exception = 4,
+ /// LogType used for Debug.
+ LogType_Debug = 5,
+ ///
+ LogType_NumLevels
+};
+
+inline const char* LogTypeToString (LogType type)
+{
+ switch (type)
+ {
+ case LogType_Assert: return "Assert";
+ case LogType_Debug: return "Debug";
+ case LogType_Exception: return "Exception";
+ case LogType_Error: return "Error";
+ case LogType_Log: return "Log";
+ case LogType_Warning: return "Warning";
+ default: return "";
+ }
+}
+
+typedef void LogToConsoleImpl (const std::string& condition, int errorNum, const char* file, int line, int type, int targetObjectInstanceID, int identifier);
+typedef void LogCallback (const std::string& condition, const std::string &stackTrace, int type);
+typedef void PreprocessCondition (const std::string& condition, std::string &strippedStacktrace, std::string &stackTrace, int errorNum, std::string& file, int* line, int type, int targetInstanceID);
+typedef void RemoveLogFunction (int identifier);
+
+/// Callback function that is invoked before a log message is written to the log output. Return true to
+/// continue logging the message as normal or false to mark the log message as handled and prevent normal
+/// processing of it.
+typedef bool (*LogEntryHandler) (LogType logType, const char* log, va_list list);
+
+void RegisterLogToConsole (LogToConsoleImpl* func);
+void RegisterLogCallback (LogCallback* callback, bool threadSafe);
+void RegisterLogPreprocessor (PreprocessCondition* func);
+void RegisterRemoveImportErrorFromConsole (RemoveLogFunction* func);
+void RegisterShowErrorWithMode (RemoveLogFunction* func);
+
+void DebugStringToFilePostprocessedStacktrace (const char* condition, const char* strippedStacktrace, const char* stacktrace, int errorNum, const char* file, int line, int mode, int targetInstanceID = 0, int identifier = 0);
+void EXPORT_COREMODULE DebugStringToFile (const char* condition, int errorNum, const char* file, int line, int mode, int targetInstanceID = 0, int identifier = 0);
+template<typename alloc>
+void EXPORT_COREMODULE DebugStringToFile (const std::basic_string<char, std::char_traits<char>, alloc>& condition, int errorNum, const char* file, int line, int mode, const int objectInstanceID = 0, int identifier = 0)
+{
+ DebugStringToFile (condition.c_str (), errorNum, file, line, mode, objectInstanceID, identifier);
+}
+
+void SetLogEntryHandler(LogEntryHandler newHandler);
+void AddCleanLogEntryHandler(LogEntryHandler newHandler);
+void ReleaseLogHandlers();
+
+void DumpCallstackConsole( const char* prefix, const char* file, int line );
+#define DUMP_CALLSTACK(message) DumpCallstackConsole(message, __FILE_STRIPPED__, __LINE__)
+
+void DebugTextLineByLine(const char* text, int maxLineLen=-1);
+
+#define ErrorIf(x) do{ if (x) DebugStringToFile (#x, 0, __FILE_STRIPPED__, __LINE__, kError); ANALYSIS_ASSUME(!(x)); }while(0)
+#define ErrorAndReturnValueIf(x, y) do{ if (x) { DebugStringToFile (#x, 0, __FILE_STRIPPED__, __LINE__, kError); return y; ANALYSIS_ASSUME(!(x)); } else { ANALYSIS_ASSUME(!(x)); } }while(0)
+#define ErrorAndReturnIf(x) ErrorAndReturnValueIf( x, /**/ )
+
+#define ErrorString(x) do{ DebugStringToFile (x, 0, __FILE_STRIPPED__, __LINE__, kError); }while(0)
+#define ErrorStringWithoutStacktrace(x) do{ DebugStringToFile (x, 0, __FILE_STRIPPED__, __LINE__, kDontExtractStacktrace | kError); }while(0)
+#define ErrorStringMsg(...) do{ DebugStringToFile (Format(__VA_ARGS__), 0, __FILE_STRIPPED__, __LINE__, kError); }while(0)
+#define WarningString(x) do{ DebugStringToFile (x, 0, __FILE_STRIPPED__, __LINE__, kScriptingWarning); }while(0)
+#define WarningStringWithoutStacktrace(x) do{ DebugStringToFile (x, 0, __FILE_STRIPPED__, __LINE__, kDontExtractStacktrace | kScriptingWarning); }while(0)
+#define WarningStringMsg(...) do{ DebugStringToFile (Format(__VA_ARGS__), 0, __FILE_STRIPPED__, __LINE__, kScriptingWarning); }while(0)
+
+
+#define ErrorOSErr(x) do{ int XERRORRESULT = x; if (XERRORRESULT) DebugStringToFile (#x, XERRORRESULT, __FILE_STRIPPED__, __LINE__, kError); }while(0)
+
+/// These errors pass an Object* as the place in which object the error occurred
+#define ErrorIfObject(x,o) do{ if (x) DebugStringToFile (#x, 0, __FILE_STRIPPED__, __LINE__, kError, (o) ? (o)->GetInstanceID() : 0); }while(0)
+#define ErrorStringObject(x,o) do{ DebugStringToFile (x, 0, __FILE_STRIPPED__, __LINE__, kError, (o) ? (o)->GetInstanceID() : 0); }while(0)
+#define WarningStringObject(x,o) do{ DebugStringToFile (x, 0, __FILE_STRIPPED__, __LINE__, kScriptingWarning, (o) ? (o)->GetInstanceID() : 0); }while(0)
+
+#define LogStringObject(x,o) do{ DebugStringToFile (x, 0, __FILE_STRIPPED__, __LINE__, kLog, (o) ? (o)->GetInstanceID() : 0); }while(0)
+#define LogString(x) do{ DebugStringToFile (x, 0, __FILE_STRIPPED__, __LINE__, kLog); }while(0)
+#define LogStringMsg(...) do{ DebugStringToFile (Format(__VA_ARGS__), 0, __FILE_STRIPPED__, __LINE__, kLog); }while(0)
+
+#define FatalErrorOSErr(x) do{ int XERRORRESULT = x; if (XERRORRESULT) DebugStringToFile (#x, XERRORRESULT, __FILE_STRIPPED__, __LINE__, kError | kFatal | kReportBug); }while(0)
+#define FatalErrorString(x) do{ DebugStringToFile (x, 0, __FILE_STRIPPED__, __LINE__, kError | kFatal | kReportBug); }while(0)
+#define FatalErrorIf(x) do{ if (x) DebugStringToFile (#x, 0, __FILE_STRIPPED__, __LINE__, kError | kFatal | kReportBug); }while(0)
+#define FatalErrorStringDontReport(x) do{ DebugStringToFile (x, 0, __FILE_STRIPPED__, __LINE__, kError | kFatal); }while(0)
+#define FatalErrorMsg(...) do{ DebugStringToFile (Format(__VA_ARGS__), 0, __FILE_STRIPPED__, __LINE__, kError | kFatal); }while(0)
+
+
+#define ErrorFiniteParameter(x) if (!IsFinite(x)) { ErrorString("Invalid parameter because it was infinity or nan."); return; }
+#define ErrorFiniteParameterReturnFalse(x) if (!IsFinite(x)) { ErrorString("Invalid parameter because it was infinity or nan."); return false; }
+
+
+/***** MAKE SURE THAT AssertIf's only compare code to compare, ****/
+/***** Which can safely be not called in non-debug mode ****/
+
+
+// TODO: should have ASSERT_ENABLED define, but difficult to add now
+
+#if defined(__ppc__)
+ #define DEBUG_BREAK __asm__("li r0, 20\nsc\nnop\nli r0, 37\nli r4, 2\nsc\nnop\n" : : : "memory","r0","r3","r4" )
+#elif UNITY_OSX
+ #define DEBUG_BREAK if(IsDebuggerPresent()) __asm { int 3 }
+#elif UNITY_WIN
+ #define DEBUG_BREAK if (IsDebuggerPresent()) __debugbreak()
+#elif UNITY_XENON || UNITY_METRO
+ #define DEBUG_BREAK __debugbreak()
+#elif UNITY_PS3
+ #define DEBUG_BREAK __asm__ volatile ("tw 31,1,1 ")
+#elif (UNITY_IPHONE && !TARGET_IPHONE_SIMULATOR)
+ #define DEBUG_BREAK __asm__ __volatile__ ( "bkpt #0\n\t bx lr\n\t" : : : )
+#elif UNITY_ANDROID
+ #define DEBUG_BREAK __builtin_trap()
+#elif UNITY_LINUX
+ #define DEBUG_BREAK if (IsDebuggerPresent ()) raise(SIGTRAP)
+#else
+ #define DEBUG_BREAK
+#endif
+
+#ifndef ASSERT_SHOULD_BREAK
+// On Metro breaking without debugger attached stops the program... So don't do it !
+#define ASSERT_SHOULD_BREAK DEBUGMODE && !UNITY_RELEASE && !UNITY_METRO
+#endif
+
+#if ASSERT_SHOULD_BREAK
+ #define ASSERT_BREAK DEBUG_BREAK
+#else
+ #define ASSERT_BREAK
+#endif
+
+
+#if DEBUGMODE
+
+ #define AssertIf(x) \
+ do { \
+ if(x) \
+ { \
+ DebugStringToFile (#x, 0, __FILE_STRIPPED__, __LINE__, kAssert); \
+ ASSERT_BREAK; \
+ ANALYSIS_ASSUME(!(x)); \
+ } \
+ } while(0)
+
+ #define AssertIfObject(x,o) \
+ do { \
+ if(x) \
+ { \
+ DebugStringToFile (#x, 0, __FILE_STRIPPED__, __LINE__, kAssert, (o)?(o)->GetInstanceID():0); \
+ ASSERT_BREAK; \
+ ANALYSIS_ASSUME(!(x)); \
+ } \
+ } while(0)
+
+
+ #define Assert(x) \
+ do { \
+ if(!(x)) \
+ { \
+ DebugStringToFile (#x, 0, __FILE_STRIPPED__, __LINE__, kAssert); \
+ ASSERT_BREAK; \
+ ANALYSIS_ASSUME(x); \
+ } \
+ } while(0)
+
+ #define AssertMsg(x,...) \
+ do { \
+ if(!(x)) \
+ { \
+ DebugStringToFile (Format(__VA_ARGS__), 0, __FILE_STRIPPED__, __LINE__, kAssert); \
+ ASSERT_BREAK; \
+ ANALYSIS_ASSUME(x); \
+ } \
+ } while(0)
+
+ #define AssertMsgObject(x,o,...) \
+ do { \
+ if(!(x)) \
+ { \
+ DebugStringToFile (Format(__VA_ARGS__), 0, __FILE_STRIPPED__, __LINE__, kAssert, (o) ? (o)->GetInstanceID() : 0); \
+ ASSERT_BREAK; \
+ ANALYSIS_ASSUME(x); \
+ } \
+ } while(0)
+
+
+#else
+
+ #define AssertIf(x) do { (void)sizeof(x); } while(0)
+ #define AssertIfObject(x,o) do { (void)sizeof(x); (void)sizeof(o); } while(0)
+ #define Assert(x) do { (void)sizeof(x); } while(0)
+ #define AssertMsg(x,...) do { (void)sizeof(x); } while(0)
+ #define AssertMsgObject(x,o,...) do { (void)sizeof(x); } while(0)
+
+#endif
+
+
+#if DEBUGMODE
+
+ #define AssertString(x) { DebugStringToFile (x, 0, __FILE_STRIPPED__, __LINE__, kAssert); }
+ #define AssertFiniteParameter(x) if (!IsFinite(x)) { AssertString("Invalid parameter because it was infinity or nan."); }
+
+ /// These errors pass an Object* as the place in which object the error occurred
+ #define AssertStringObject(x,o) { DebugStringToFile (x, 0, __FILE_STRIPPED__, __LINE__, kAssert, (o) ? (o)->GetInstanceID() : 0); }
+ #define ScriptWarning(x,o) { DebugStringToFile (x, 0, __FILE_STRIPPED__, __LINE__, kScriptingWarning); }
+#else
+
+ #define AssertString(x) { }
+ #define AssertFiniteParameter(x) {}
+
+ #define AssertStringObject(x,o) { }
+ #define ScriptWarning(x,o) { }
+#endif
+
+
+#if UNITY_RELEASE
+ #define DebugAssertIf(x) do { (void)sizeof(x); } while(0)
+ #define DebugAssert(x) do { (void)sizeof(x); } while(0)
+ #define DebugAssertMsg(x, ...) { }
+ #define AssertBreak(x) Assert(x)
+#else
+ #define DebugAssertIf(x) AssertIf(x)
+ #define DebugAssert(x) Assert(x)
+ #define DebugAssertMsg(x, ...) AssertMsg(x, __VA_ARGS__)
+
+ #define AssertBreak(x) \
+ do { \
+ if(!(x)) \
+ { \
+ DEBUG_BREAK; \
+ Assert(x); \
+ } \
+ } while(0)
+ #endif
+
+
+#ifdef __cplusplus
+extern "C"
+ {
+#endif
+EXPORT_COREMODULE TAKES_PRINTF_ARGS(1,2) void printf_console (const char* string, ...);
+#ifdef __cplusplus
+ }
+#endif
+void printf_consolev (LogType logType, const char* log, va_list list);
+
+#if MASTER_BUILD
+#define UNITY_TRACE(...)
+#define UNITY_TRACEIF(condition, ...)
+#else
+#define UNITY_TRACE(...) printf_console(__VA_ARGS__)
+#define UNITY_TRACEIF(condition, ...) if (condition) printf_console(__VA_ARGS__)
+#endif
+
+/// When logging an error it can be passed an identifier. The identifier can be used to remove errors from the console later on.
+/// Eg. when reimporting an asset all errors generated for that asset should be removed before importing!
+void RemoveErrorWithIdentifierFromConsole (int identifier);
+void ShowErrorWithMode (int mode);
+
+#if UNITY_OSX
+void ResetStdout();
+#endif
+
+// Route input to a custom outputfile out instead of Player.log
+void LogOutputToSpecificFile (const char* path);
+void InitializeCleanedLogFile (FILE* file);
+std::string GetCleanedLogFile();
+std::string GetConsoleLogPath();
+
+#if UNITY_EDITOR
+std::string GetEditorConsoleLogPath ();
+std::string GetPlayerConsoleLogPath ();
+std::string GetMonoDevelopLogPath ();
+#endif
+
+#if UNITY_WIN
+FILE* OpenConsole ();
+std::string SetLogFilePath(std::string const& path);
+#endif
+
+#if defined(_MSC_VER)
+ #define PRINTF_SIZET_FORMAT "Iu"
+#else
+ #define PRINTF_SIZET_FORMAT "zu"
+#endif
+
+#endif
diff --git a/Runtime/Utilities/LogUtility.cpp b/Runtime/Utilities/LogUtility.cpp
new file mode 100644
index 0000000..7b16094
--- /dev/null
+++ b/Runtime/Utilities/LogUtility.cpp
@@ -0,0 +1,4 @@
+#include "UnityPrefix.h"
+#include "LogUtility.h"
+
+int NestedLogOutput::s_LogDepth = 0;
diff --git a/Runtime/Utilities/LogUtility.h b/Runtime/Utilities/LogUtility.h
new file mode 100644
index 0000000..164ea3f
--- /dev/null
+++ b/Runtime/Utilities/LogUtility.h
@@ -0,0 +1,28 @@
+#pragma once
+
+#include "LogAssert.h"
+
+// Nested log with automatic indentation. NESTED_LOG(name,fmt,...) prints to the console
+// and indents further log calls. Log indentation is decreased again when current scope ends.
+
+class NestedLogOutput
+{
+public:
+ NestedLogOutput(const char* name, const std::string& msg)
+ {
+ printf_console("%s: %*c%s\n", name, s_LogDepth, ' ', msg.c_str());
+ s_LogDepth += 4;
+ }
+ ~NestedLogOutput()
+ {
+ s_LogDepth -= 4;
+ }
+private:
+ static int s_LogDepth;
+};
+
+#if !UNITY_RELEASE
+#define NESTED_LOG(name,...) NestedLogOutput _nested_log_##__LINE__ (name,Format(__VA_ARGS__))
+#else
+#define NESTED_LOG(name,...)
+#endif
diff --git a/Runtime/Utilities/MemoryPool.cpp b/Runtime/Utilities/MemoryPool.cpp
new file mode 100644
index 0000000..f11d88a
--- /dev/null
+++ b/Runtime/Utilities/MemoryPool.cpp
@@ -0,0 +1,210 @@
+#include "UnityPrefix.h"
+#include "MemoryPool.h"
+#include "Runtime/Utilities/Word.h"
+#include "Runtime/Profiler/MemoryProfiler.h"
+#include "Runtime/Utilities/InitializeAndCleanup.h"
+
+static int kMinBlockSize = sizeof(void*);
+UNITY_VECTOR(kMemPoolAlloc,MemoryPool*)* MemoryPool::s_MemoryPools = NULL;
+void MemoryPool::StaticInitialize()
+{
+ s_MemoryPools = UNITY_NEW(UNITY_VECTOR(kMemPoolAlloc,MemoryPool*),kMemPoolAlloc);
+}
+
+void MemoryPool::StaticDestroy()
+{
+ for(size_t i = 0; i < s_MemoryPools->size(); i++)
+ UNITY_DELETE((*s_MemoryPools)[i],kMemPoolAlloc);
+ UNITY_DELETE(s_MemoryPools,kMemPoolAlloc);
+}
+
+static RegisterRuntimeInitializeAndCleanup s_MemoryPoolCallbacks(MemoryPool::StaticInitialize, MemoryPool::StaticDestroy);
+
+void MemoryPool::RegisterStaticMemoryPool(MemoryPool* pool)
+{
+ s_MemoryPools->push_back(pool);
+}
+
+MemoryPool::MemoryPool( bool threadCheck, const char* name, int blockSize, int hintSize, MemLabelId label )
+: m_AllocLabel(MemLabelId(label.label, GET_CURRENT_ALLOC_ROOT_HEADER()))
+, m_Bubbles(MemLabelId(label.label, GET_CURRENT_ALLOC_ROOT_HEADER()))
+#if DEBUGMODE
+, m_PeakAllocCount(0)
+, m_Name(name)
+#endif
+{
+ #if ENABLE_THREAD_CHECK_IN_ALLOCS
+ m_ThreadCheck = threadCheck;
+ #endif
+
+ if (blockSize < kMinBlockSize)
+ blockSize = kMinBlockSize;
+ m_BlockSize = blockSize;
+
+
+ m_BubbleSize = hintSize;
+ m_BlocksPerBubble = (m_BubbleSize - sizeof(Bubble) + 1) / blockSize;
+
+ int usedBubbleSize = sizeof(Bubble) + m_BlocksPerBubble * blockSize - 1;
+ Assert(usedBubbleSize <= m_BubbleSize);
+ Assert(m_BubbleSize - usedBubbleSize <= m_BlockSize);
+
+ Assert (m_BlocksPerBubble >= 128);
+ Assert(hintSize % 4096 == 0);
+
+ m_AllocateMemoryAutomatically = true;
+
+ Reset();
+}
+
+MemoryPool::~MemoryPool()
+{
+ #if !UNITY_EDITOR && DEBUGMODE
+ if (m_AllocCount > 0)
+ ErrorStringMsg( "Memory pool has %d unallocated objects: %s", m_AllocCount, m_Name ); // some stuff not deallocated?
+ #endif
+ DeallocateAll();
+}
+
+void MemoryPool::Reset()
+{
+ #if DEBUGMODE
+ m_AllocCount = 0;
+ #endif
+ m_HeadOfFreeList = NULL;
+}
+
+void MemoryPool::DeallocateAll()
+{
+ Bubbles::iterator it, itEnd = m_Bubbles.end();
+ for( it = m_Bubbles.begin(); it != itEnd; ++it )
+ UNITY_FREE( m_AllocLabel, *it );
+ m_Bubbles.clear();
+ Reset();
+}
+
+void MemoryPool::PreallocateMemory (int size)
+{
+ bool temp = m_AllocateMemoryAutomatically;
+ m_AllocateMemoryAutomatically = true;
+ for (int i=0;i <= size / (m_BlocksPerBubble * m_BlockSize);i++)
+ {
+ AllocNewBubble();
+ }
+ m_AllocateMemoryAutomatically = temp;
+}
+
+void MemoryPool::AllocNewBubble( )
+{
+ if (!m_AllocateMemoryAutomatically)
+ return;
+
+ AssertIf (m_BlocksPerBubble == 1); // can't have 1 element per bubble
+
+ Bubble *bubble = (Bubble*)UNITY_MALLOC( m_AllocLabel, m_BubbleSize );
+ AssertIf( !bubble );
+
+ // put to bubble list
+ m_Bubbles.push_back( bubble );
+
+ // setup the free list inside a bubble
+ void* oldHeadOfFreeList = m_HeadOfFreeList;
+ m_HeadOfFreeList = bubble->data;
+ AssertIf( !m_HeadOfFreeList );
+
+ void **newBubble = (void**)m_HeadOfFreeList;
+ for( int j = 0; j < m_BlocksPerBubble-1; ++j )
+ {
+ newBubble[0] = (char*)newBubble + m_BlockSize;
+ newBubble = (void**)newBubble[0];
+ }
+
+ newBubble[0] = oldHeadOfFreeList; // continue with existing free list (or terminate with NULL if no free elements)
+
+ // still failure, error out
+ if( !m_HeadOfFreeList )
+ {
+ ErrorString( "out of memory!" );
+ }
+}
+
+void* MemoryPool::Allocate()
+{
+ return Allocate( m_BlockSize );
+}
+
+void *MemoryPool::Allocate( size_t amount )
+{
+#if ENABLE_THREAD_CHECK_IN_ALLOCS
+ ErrorAndReturnValueIf(m_ThreadCheck && Thread::mainThreadId && !Thread::CurrentThreadIsMainThread(), NULL);
+#endif
+
+
+ void *returnBlock;
+
+ if( amount > (unsigned int)m_BlockSize ) {
+ ErrorString( Format("requested larger amount than block size! requested: %d, blocksize: %d", (unsigned)amount, (unsigned)m_BlockSize ));
+ return NULL;
+ }
+
+ if( !m_HeadOfFreeList ) {
+ // allocate new bubble
+ AllocNewBubble();
+
+ // Can't allocate
+ if( m_HeadOfFreeList == NULL )
+ return NULL;
+ }
+
+ #if DEBUGMODE
+ ++m_AllocCount;
+ if( m_AllocCount > m_PeakAllocCount )
+ m_PeakAllocCount = m_AllocCount;
+ #endif
+
+ returnBlock = m_HeadOfFreeList;
+
+ // move the pointer to the next block
+ m_HeadOfFreeList = *((void**)m_HeadOfFreeList);
+
+ return returnBlock;
+}
+
+void MemoryPool::Deallocate( void *mem_Block )
+{
+#if ENABLE_THREAD_CHECK_IN_ALLOCS
+ ErrorAndReturnIf(m_ThreadCheck && Thread::mainThreadId && !Thread::CurrentThreadIsMainThread());
+#endif
+
+ if( !mem_Block ) // ignore NULL deletes
+ return;
+
+ #if DEBUGMODE
+ // check to see if the memory is from the allocated range
+ bool ok = false;
+ size_t n = m_Bubbles.size();
+ for( size_t i = 0; i < n; ++i ) {
+ Bubble* p = m_Bubbles[i];
+ if( (char*)mem_Block >= p->data && (char*)mem_Block < (p->data + m_BlockSize * m_BlocksPerBubble) ) {
+ ok = true;
+ break;
+ }
+ }
+ AssertIf( !ok );
+ #endif
+
+ #if DEBUGMODE
+ // invalidate the memory
+ memset( mem_Block, 0xDD, m_BlockSize );
+ AssertIf(m_AllocCount == 0);
+ #endif
+
+ #if DEBUGMODE
+ --m_AllocCount;
+ #endif
+
+ // make the block point to the first free item in the list
+ *((void**)mem_Block) = m_HeadOfFreeList;
+ // the list head is now the Deallocated block
+ m_HeadOfFreeList = mem_Block;
+}
diff --git a/Runtime/Utilities/MemoryPool.h b/Runtime/Utilities/MemoryPool.h
new file mode 100644
index 0000000..b7ef488
--- /dev/null
+++ b/Runtime/Utilities/MemoryPool.h
@@ -0,0 +1,280 @@
+#ifndef MEMORY_POOL_H_
+#define MEMORY_POOL_H_
+
+#include "Runtime/Allocator/MemoryMacros.h"
+#include "Runtime/Utilities/dynamic_array.h"
+#include "Runtime/Utilities/LogAssert.h"
+#include "Runtime/Modules/ExportModules.h"
+
+#if ENABLE_THREAD_CHECK_IN_ALLOCS
+#include "Runtime/Threads/Thread.h"
+#endif
+
+
+// --------------------------------------------------------------------------
+// A free-list based fixed size allocator.
+//
+// To override new/delete per class use DECLARE_POOLED_ALLOC and DEFINE_POOLED_ALLOC.
+//
+// memory_pool<T> is an STL allocator that only supports allocating a single element
+// at a time. So it can be used with list, map, set but not with vector or string.
+//
+// Allocator creates "bubbles" of objects, each containin a free-list inside. When a bubble
+// is full, it allocates a new one. Empty bubbles are NOT destroyed.
+
+
+// --------------------------------------------------------------------------
+
+class EXPORT_COREMODULE MemoryPool {
+public:
+ MemoryPool( bool threadCheck, const char* name, int blockSize, int allocatedSizeHint, MemLabelId label = kMemPoolAlloc );
+ ~MemoryPool();
+
+ /// Allocate single block
+ void* Allocate();
+ /// Allocate less than single block
+ void* Allocate( size_t amount );
+ /// Deallocate
+ void Deallocate( void *ptr );
+ /// Deallocate everything
+ void DeallocateAll();
+
+
+ #if !DEPLOY_OPTIMIZED
+ size_t GetBubbleCount() const { return m_Bubbles.size(); }
+ int GetAllocCount() const { return m_AllocCount; }
+ #endif
+
+ int GetAllocatedBytes() { return m_Bubbles.size () * m_BlocksPerBubble * m_BlockSize; }
+
+ #if DEBUGMODE
+ int GetAllocatedObjectsCount() { return m_AllocCount; }
+ #endif
+
+ void PreallocateMemory(int size);
+ void SetAllocateMemoryAutomatically (bool allocateMemoryAuto) { m_AllocateMemoryAutomatically = allocateMemoryAuto; }
+
+ static void StaticInitialize();
+ static void StaticDestroy();
+ static void RegisterStaticMemoryPool(MemoryPool* pool);
+
+private:
+ void AllocNewBubble();
+
+ struct Bubble
+ {
+ char data[1]; // actually byteCount
+ };
+ typedef dynamic_array<Bubble*> Bubbles;
+
+ void Reset();
+private:
+ int m_BlockSize;
+ int m_BubbleSize;
+ int m_BlocksPerBubble;
+
+ Bubbles m_Bubbles;
+
+ void* m_HeadOfFreeList;
+ bool m_AllocateMemoryAutomatically;
+
+ MemLabelId m_AllocLabel;
+
+ int m_AllocCount; // number of blocks currently allocated
+ #if DEBUGMODE
+ int m_PeakAllocCount; // stats
+ const char* m_Name; // for debugging
+ #endif
+
+ #if ENABLE_THREAD_CHECK_IN_ALLOCS
+ bool m_ThreadCheck;
+ #endif
+
+ static UNITY_VECTOR(kMemPoolAlloc,MemoryPool*)* s_MemoryPools;
+};
+
+
+// --------------------------------------------------------------------------
+// Macros for class fixed-size pooled allocations:
+// DECLARE_POOLED_ALLOC in the .h file, in a private section of a class,
+// DEFINE_POOLED_ALLOC in the .cpp file
+
+#define ENABLE_MEMORY_POOL 1
+
+#if ENABLE_MEMORY_POOL
+
+#define STATIC_INITIALIZE_POOL( _clazz ) _clazz::s_PoolAllocator = UNITY_NEW(MemoryPool, kMemPoolAlloc)(true, #_clazz, sizeof(_clazz), _clazz::s_PoolSize)
+#define STATIC_DESTROY_POOL( _clazz ) UNITY_DELETE(_clazz::s_PoolAllocator, kMemPoolAlloc)
+
+#define DECLARE_POOLED_ALLOC( _clazz ) \
+public: \
+ inline void* operator new( size_t size ) { return s_PoolAllocator->Allocate(size); } \
+ inline void operator delete( void* p ) { s_PoolAllocator->Deallocate(p); } \
+ static MemoryPool *s_PoolAllocator; \
+ static int s_PoolSize; \
+private:
+
+#define DEFINE_POOLED_ALLOC( _clazz, _bubbleSize ) \
+ MemoryPool* _clazz::s_PoolAllocator = NULL; \
+ int _clazz::s_PoolSize = _bubbleSize;
+
+#else
+
+#define STATIC_INITIALIZE_POOL( _clazz )
+#define STATIC_DESTROY_POOL( _clazz )
+#define DECLARE_POOLED_ALLOC( _clazz )
+#define DEFINE_POOLED_ALLOC( _clazz, _bubbleSize )
+
+#endif
+
+
+// --------------------------------------------------------------------------
+
+template<int SIZE>
+struct memory_pool_impl
+{
+ struct AutoPoolWrapper
+ {
+ AutoPoolWrapper( int size)
+ {
+ SET_ALLOC_OWNER(NULL);
+ pool = UNITY_NEW(MemoryPool( true, "mempoolalloc", size, 32 * 1024, kMemPoolAlloc ), kMemPoolAlloc);
+ MemoryPool::RegisterStaticMemoryPool(pool);
+ }
+ ~AutoPoolWrapper()
+ {
+ }
+ MemoryPool* pool;
+ };
+ static MemoryPool& get_pool () {
+ static AutoPoolWrapper pool( SIZE );
+ return *(pool.pool);
+ }
+};
+
+/*
+
+ THIS IS NOT THREAD SAFE. sharing of pools is by size, thus pools might randomly be shared from different threads.
+
+*/
+
+
+
+template<typename T>
+class memory_pool
+{
+public:
+ typedef size_t size_type;
+ typedef std::ptrdiff_t difference_type;
+ typedef T* pointer;
+ typedef const T* const_pointer;
+ typedef T& reference;
+ typedef const T& const_reference;
+ typedef T value_type;
+
+ template <class U> struct rebind { typedef memory_pool<U> other; };
+
+ memory_pool() { }
+ memory_pool( const memory_pool<T>& ) { }
+ template<class B> memory_pool(const memory_pool<B>&) { } // construct from a related allocator
+ template<class B> memory_pool<T>& operator=(const memory_pool<B>&) { return *this; } // assign from a related allocator
+
+ ~memory_pool() throw() { }
+
+ pointer address(reference x) const { return &x; }
+ const_pointer address(const_reference x) const { return &x; }
+ size_type max_size() const throw() {return size_t(-1) / sizeof(value_type);}
+ void construct(pointer p, const T& val) { ::new((void*)p) T(val); }
+ void destroy(pointer p) { p->~T(); }
+
+ pointer allocate(size_type n, std::allocator<void>::const_pointer /*hint*/ = 0)
+ {
+ if(n==1)
+ return reinterpret_cast<pointer>( memory_pool_impl<sizeof(T)>::get_pool ().Allocate(n * sizeof(T)) );
+ else
+ return reinterpret_cast<pointer>(UNITY_MALLOC(kMemPoolAlloc,n*sizeof(T)));
+ }
+
+ void deallocate(pointer p, size_type n)
+ {
+ if(n==1)
+ return memory_pool_impl<sizeof(T)>::get_pool ().Deallocate( p );
+ else
+ return UNITY_FREE(kMemPoolAlloc,p);
+ }
+};
+
+template<typename T>
+class memory_pool_explicit
+{
+public:
+ typedef size_t size_type;
+ typedef std::ptrdiff_t difference_type;
+ typedef T* pointer;
+ typedef const T* const_pointer;
+ typedef T& reference;
+ typedef const T& const_reference;
+ typedef T value_type;
+ template <class U> struct rebind { typedef memory_pool_explicit<U> other; };
+
+ MemoryPool* m_Pool;
+
+ memory_pool_explicit(MemoryPool& pool) { m_Pool = &pool; }
+ memory_pool_explicit() { m_Pool = NULL; }
+ memory_pool_explicit( const memory_pool_explicit<T>& b) { m_Pool = b.m_Pool; }
+ template<class B> memory_pool_explicit(const memory_pool_explicit<B>& b) { m_Pool = b.m_Pool; } // construct from a related allocator
+ template<class B> memory_pool_explicit<T>& operator=(const memory_pool_explicit<B>& b) { m_Pool = b.m_Pool; return *this; } // assign from a related allocator
+
+ ~memory_pool_explicit() throw() { }
+
+ pointer address(reference x) const { return &x; }
+ const_pointer address(const_reference x) const { return &x; }
+ size_type max_size() const throw() {return size_t(-1) / sizeof(value_type);}
+ void construct(pointer p, const T& val) { ::new((void*)p) T(val); }
+ void destroy(pointer p) { p->~T(); }
+
+ pointer allocate(size_type n, std::allocator<void>::const_pointer /*hint*/ = 0)
+ {
+ DebugAssertIf(n != 1);
+ AssertIf(m_Pool == NULL);
+ return reinterpret_cast<pointer>( m_Pool->Allocate(n * sizeof(T)) );
+ }
+
+ void deallocate(pointer p, size_type n)
+ {
+ DebugAssertIf(n != 1);
+ AssertIf(m_Pool == NULL);
+ m_Pool->Deallocate( p );
+ }
+};
+
+template<typename A, typename B>
+inline bool operator==( const memory_pool<A>&, const memory_pool<B>& )
+{
+ // test for allocator equality (always true)
+ return true;
+}
+
+template<typename A, typename B>
+inline bool operator!=( const memory_pool<A>&, const memory_pool<B>& )
+{
+ // test for allocator inequality (always false)
+ return false;
+}
+
+
+template<typename A, typename B>
+inline bool operator==( const memory_pool_explicit<A>& lhs, const memory_pool_explicit<B>& rhs)
+{
+ // test for allocator equality (always true)
+ return lhs.m_Pool == rhs.m_Pool;
+}
+
+template<typename A, typename B>
+inline bool operator!=( const memory_pool_explicit<A>& lhs, const memory_pool_explicit<B>& rhs)
+{
+ // test for allocator inequality (always false)
+ return lhs.m_Pool != rhs.m_Pool;
+}
+
+#endif
diff --git a/Runtime/Utilities/MemoryUtilities.cpp b/Runtime/Utilities/MemoryUtilities.cpp
new file mode 100644
index 0000000..b97ffad
--- /dev/null
+++ b/Runtime/Utilities/MemoryUtilities.cpp
@@ -0,0 +1,15 @@
+#include "UnityPrefix.h"
+#include "MemoryUtilities.h"
+
+void memset32 (void *dst, UInt32 value, UInt64 bytecount)
+{
+ UInt32 i;
+ for( i = 0; i < (bytecount & (~3)); i+=4 )
+ {
+ *((UInt32*)((char*)dst + i)) = value;
+ }
+ for( ; i < bytecount; i++ )
+ {
+ ((char*)dst)[i] = ((char*)&value)[i&4];
+ }
+}
diff --git a/Runtime/Utilities/MemoryUtilities.h b/Runtime/Utilities/MemoryUtilities.h
new file mode 100644
index 0000000..78c097e
--- /dev/null
+++ b/Runtime/Utilities/MemoryUtilities.h
@@ -0,0 +1,3 @@
+#pragma once
+
+void memset32(void *dst, UInt32 value, UInt64 bytecount);
diff --git a/Runtime/Utilities/NonCopyable.h b/Runtime/Utilities/NonCopyable.h
new file mode 100644
index 0000000..e23357f
--- /dev/null
+++ b/Runtime/Utilities/NonCopyable.h
@@ -0,0 +1,18 @@
+#ifndef NON_COPYABLE_H
+#define NON_COPYABLE_H
+
+#ifndef EXPORT_COREMODULE
+#define EXPORT_COREMODULE
+#endif
+
+class EXPORT_COREMODULE NonCopyable
+{
+public:
+ NonCopyable() {}
+
+private:
+ NonCopyable(const NonCopyable&);
+ NonCopyable& operator=(const NonCopyable&);
+};
+
+#endif
diff --git a/Runtime/Utilities/OptimizationUtility.h b/Runtime/Utilities/OptimizationUtility.h
new file mode 100644
index 0000000..5b69177
--- /dev/null
+++ b/Runtime/Utilities/OptimizationUtility.h
@@ -0,0 +1,20 @@
+#ifndef OPTIMIZATION_UTILITY_H
+#define OPTIMIZATION_UTILITY_H 1
+
+
+// ALIGN_LOOP_OPTIMIZATION should be placed in heavy inner loops!
+// for (int i=0;i<10000;i++)
+// ALIGN_LOOP_OPTIMIZATION
+// ...
+
+// On the Wii global function alignment is 4 for debug builds to save exe size
+// as there are a lot of non-inlined functions.
+// In that case having align 16 generates warnings, so we blank the define.
+#if defined(__MWERKS__) && UNITY_RELEASE && !defined(_DEBUG)
+#define ALIGN_LOOP_OPTIMIZATION asm {align 16}
+#else
+///@TODO: optimize this for gcc too
+#define ALIGN_LOOP_OPTIMIZATION
+#endif
+
+#endif
diff --git a/Runtime/Utilities/PathNameUtility.cpp b/Runtime/Utilities/PathNameUtility.cpp
new file mode 100644
index 0000000..d168c61
--- /dev/null
+++ b/Runtime/Utilities/PathNameUtility.cpp
@@ -0,0 +1,601 @@
+#include "UnityPrefix.h"
+#include "PathNameUtility.h"
+#include "Word.h"
+
+#include <algorithm>
+
+using namespace std;
+
+static void AppendPathNameImpl( const string& pathName, const string& append, char separator, std::string& res )
+{
+ res.reserve (pathName.size () + append.size () + 1);
+ if (!pathName.empty () && !append.empty ())
+ {
+ if (pathName[pathName.size () - 1] == separator)
+ {
+ if (append[0] == separator)
+ {
+ res += pathName;
+ res.append (append.begin () + 1, append.end ());
+ return;
+ }
+ else
+ {
+ res += pathName;
+ res += append;
+ return;
+ }
+ }
+ else
+ {
+ if (append[0] == separator)
+ {
+ res += pathName;
+ res += append;
+ return;
+ }
+ else
+ {
+ res += pathName;
+ res += separator;
+ res += append;
+ return;
+ }
+ }
+ }
+ else if (pathName.empty ())
+ res = append;
+ else
+ res = pathName;
+}
+
+string AppendPathName (const string& pathName, const string& append)
+{
+ string res;
+ AppendPathNameImpl( pathName, append, kPathNameSeparator, res );
+ return res;
+}
+
+string PlatformAppendPathName (const string& pathName, const string& append)
+{
+ string res;
+ AppendPathNameImpl( pathName, append, kPlatformPathNameSeparator, res );
+ return res;
+}
+
+
+string AppendPathNameExtension (const string& pathName, const string& extension)
+{
+ if (extension.empty ())
+ return pathName;
+
+ string newPathName;
+ newPathName.reserve (pathName.size () + 1 + extension.size ());
+ newPathName.append (pathName);
+ newPathName.append (".");
+ newPathName.append (extension);
+ return newPathName;
+}
+
+string GetPathNameExtension (const string& pathName)
+{
+ return GetPathNameExtension (pathName.c_str(), pathName.size());
+}
+
+const char* GetPathNameExtension (const char* path, size_t length)
+{
+ for (size_t i=0;i<length;i++)
+ {
+ if (path[length - i - 1] == kPathNameSeparator)
+ return "";
+ if (path[length - i - 1] == '.')
+ return path + length - i;
+ }
+ return "";
+}
+
+string DeletePathNameExtension (const string& pathName)
+{
+ string::size_type slash = pathName.rfind (kPathNameSeparator);
+ string::size_type dot = pathName.rfind ('.');
+
+ if (dot != string::npos)
+ {
+ if (slash == string::npos || dot > slash)
+ return string (&pathName[0], dot);
+ }
+ return pathName;
+}
+
+vector<string> FindSeparatedPathComponents (char const* constPathName, size_t size, char separator)
+{
+ char const* current = constPathName, *end = constPathName + size;
+ vector<string> components;
+
+ while (current != end)
+ {
+ char const* pos = std::find (current, end, separator);
+
+ if (pos != current)
+ components.push_back (std::string (current, pos));
+
+ if (pos == end)
+ break;
+
+ current = pos + 1;
+ }
+
+ return components;
+}
+
+string DeleteLastPathNameComponent (const string& pathName)
+{
+ string::size_type p = pathName.rfind (kPathNameSeparator);
+ if (p == string::npos)
+ return string ();
+ else
+ return string (&pathName[0], p);
+}
+
+string PlatformDeleteLastPathNameComponent (const string& pathName)
+{
+ string::size_type p = pathName.rfind (kPlatformPathNameSeparator);
+ if (p == string::npos)
+ return string ();
+ else
+ return string (&pathName[0], p);
+}
+
+string GetLastPathNameComponent (const string& pathName)
+{
+ return GetLastPathNameComponent(pathName.c_str(), pathName.size());
+}
+
+const char* GetLastPathNameComponent (const char* path, size_t length)
+{
+ for (size_t i=0;i<length;i++)
+ {
+ if (path[length - i - 1] == kPathNameSeparator)
+ return path + length - i;
+ }
+ return path;
+}
+
+string PlatformGetLastPathNameComponent(const string& pathName)
+{
+ string::size_type p = pathName.rfind (kPlatformPathNameSeparator);
+ if (p == string::npos)
+ return pathName;
+ else
+ return string (&pathName[1 + p], pathName.size () - 1 - p);
+}
+
+string DeleteFirstPathNameComponent (const string& pathName)
+{
+ string::size_type p = pathName.find (kPathNameSeparator);
+ if (p == string::npos)
+ return string ();
+ else
+ return string (&pathName[1 + p], pathName.size () - 1 - p);
+}
+
+string StandardizePathName (const string& pathName)
+{
+ if (pathName.empty ())
+ return pathName;
+
+ // Remove initial / if not a // in the beginning
+ if (pathName[0] == kPathNameSeparator && pathName.size () > 1 && pathName[1] != kPathNameSeparator)
+ return string (&pathName[1], pathName.size () - 1);
+ else
+ return pathName;
+}
+
+bool IsPathNameVisible (const string& path)
+{
+ if (path.empty())
+ return false;
+
+ string::size_type p = 0;
+ while (p < path.size ())
+ {
+ if (path[p] == '.')
+ return false;
+ p = path.find (kPathNameSeparator, p);
+ if (p == string::npos)
+ return true;
+ p++;
+ }
+ return true;
+}
+
+
+void ConvertSeparatorsToUnity( char* pathName )
+{
+ while( *pathName != '\0' ) {
+ if( *pathName == '\\' )
+ *pathName = kPathNameSeparator;
+ ++pathName;
+ }
+}
+
+void ConvertSeparatorsToPlatform( char* pathName )
+{
+ if (kPathNameSeparator != kPlatformPathNameSeparator) {
+ while( *pathName != '\0' ) {
+ if( *pathName == kPathNameSeparator )
+ *pathName = kPlatformPathNameSeparator;
+ ++pathName;
+ }
+ }
+}
+
+void ConvertSeparatorsToPlatform( std::string& pathName )
+{
+ if (kPathNameSeparator != kPlatformPathNameSeparator) {
+ std::string::iterator it = pathName.begin(), itEnd = pathName.end();
+ while( it != itEnd ) {
+ if( *it == kPathNameSeparator )
+ *it = kPlatformPathNameSeparator;
+ ++it;
+ }
+ }
+}
+
+#if UNITY_EDITOR
+const char* invalidFilenameChars = "/?<>\\:*|\"";
+
+const char* GetInvalidFilenameChars ()
+{
+ return invalidFilenameChars;
+}
+
+bool IsValidFileNameSymbol (const char c)
+{
+ return std::string(invalidFilenameChars).find(c) == string::npos;
+}
+
+bool CheckValidFileName (const std::string& name)
+{
+ return CheckValidFileNameDetail(name) == kFileNameValid;
+}
+
+
+std::string MakeFileNameValid (const std::string& fileName)
+{
+ string output = fileName;
+
+ // Disallow space at the beginning
+ while (!output.empty() && output[0] == ' ')
+ output.erase(0, 1);
+
+ if (output.empty())
+ return output;
+
+ // Disallow space at the end
+ if (output[output.size()-1] == ' ' || output[output.size()-1] == '.')
+ output[output.size()-1] = '_';
+
+ for (int i=0;i<output.size();i++)
+ {
+ if (!IsValidFileNameSymbol(output[i]))
+ output[i] = '_';
+ }
+
+
+ if (CheckValidFileName(output))
+ return output;
+ else
+ return string();
+}
+
+
+FileNameValid CheckValidFileNameDetail (const std::string& name)
+{
+ // none of these can be part of file/folder name
+ if( name.find_first_of("/\\") != string::npos )
+ return kFileNameInvalid;
+
+ // anything up to first dot can't be: com1..com9, lpt1..lpt9, con, nul, prn
+ size_t dotIndex = name.find('.');
+ if( dotIndex == 4 )
+ {
+ if( name[0]=='c' && name[1]=='o' && name[2]=='m' && name[3]>='0' && name[3]<='9' )
+ return kFileNameInvalid;
+ if( name[0]=='l' && name[1]=='p' && name[2]=='t' && name[3]>='0' && name[3]<='9' )
+ return kFileNameInvalid;
+ }
+ if( dotIndex == 3 )
+ {
+ if( name[0]=='c' && name[1]=='o' && name[2]=='n' )
+ return kFileNameInvalid;
+ if( name[0]=='n' && name[1]=='u' && name[2]=='l' )
+ return kFileNameInvalid;
+ if( name[0]=='p' && name[1]=='r' && name[2]=='n' )
+ return kFileNameInvalid;
+ }
+
+ // . on OS X means invisible file
+ if( dotIndex == 0 )
+ return kFileNameInvalid;
+
+ // file/folder name can't end with a dot or a space
+ if( !name.empty() )
+ {
+ // http://support.microsoft.com/kb/115827
+ char lastChar = name[name.size()-1];
+ if( lastChar == ' ' || lastChar == '.' )
+ return kFileNameInvalid;
+
+ // File names starting with a space will not get preserved correctly when zipping
+ // on windows. So don't allow that.
+ if (name[0] == ' ')
+ return kFileNameNotRecommended;
+ }
+
+ if( name.find_first_of(invalidFilenameChars) != string::npos )
+ return kFileNameNotRecommended;
+
+ return kFileNameValid;
+}
+
+std::string TrimSlash(const std::string& path)
+{
+ if (!path.empty())
+ {
+ const char lastChar = path[path.size() - 1];
+ if (lastChar == '/' || lastChar == '\\')
+ return path.substr(0, path.size() - 1);
+ }
+
+ return path;
+}
+
+string NormalizeUnicode (const string& utf8, bool decompose)
+{
+#if UNITY_OSX
+ // OS X stores Unicode file names in decomposed form. Ie † (U-Umlaut), becomes represented as two characters
+ // -U and ¬ (Unicode U+0308), instead of a single † (Unicode U+00DC). For proper display and consistency of
+ // names, they need to be converted into precomposed form.
+ CFStringRef cfLowercased = StringToCFString(utf8);
+ if (!cfLowercased)
+ ErrorStringMsg("Failed to convert string '%s' to CFString", utf8.c_str());
+ CFMutableStringRef cf = CFStringCreateMutableCopy(kCFAllocatorDefault, 0, cfLowercased);
+ CFStringNormalize(cf, decompose?kCFStringNormalizationFormD:kCFStringNormalizationFormC);
+ string result = CFStringToString(cf);
+ if (cf != NULL)
+ CFRelease(cf);
+ if (cfLowercased != NULL)
+ CFRelease(cfLowercased);
+ return result;
+#else
+ return utf8;
+#endif
+}
+
+// Serialization does weird shit without case mangling - even if filesystem paths shouldn't
+// be mangled on Linux, we apparently need them mangled in some serialization stuff anyway...
+string& GetGoodPathNameForGUID (const string& pathname)
+{
+ static string lowercased;
+
+ lowercased = GetGoodPathName(pathname);
+
+#if UNITY_LINUX
+ ToLowerInplace(lowercased);
+#endif
+
+ return lowercased;
+}
+
+string& GetGoodPathName (const string& pathname)
+{
+ static string lowercased;
+ if (lowercased.capacity () < 4096)
+ lowercased.reserve (4096);
+
+ if (!pathname.empty () && pathname[0] == kPathNameSeparator)
+ {
+ lowercased.clear();
+ return lowercased;
+ }
+
+ lowercased = pathname;
+
+#if UNITY_WIN
+ ConvertSeparatorsToUnity( lowercased );
+#endif
+
+#if UNITY_OSX
+ if (RequiresNormalization(lowercased))
+ lowercased = NormalizeUnicode (lowercased, false);
+#endif
+
+#if !UNITY_LINUX
+ ToLowerInplace(lowercased);
+#endif
+
+ return lowercased;
+}
+
+#endif
+
+
+#if !UNITY_PLUGIN
+bool StartsWithPath (const std::string &s, const std::string& beginsWith)
+{
+ size_t beginsWithSize = beginsWith.size ();
+ if (s.size () < beginsWithSize)
+ return false;
+
+ for (size_t i=0;i<beginsWithSize;i++)
+ {
+ if (ToLower(s[i]) != ToLower(beginsWith[i]))
+ return false;
+ }
+
+ // Exact match or empty beginsWith
+ if (s.size () == beginsWithSize || beginsWith.empty())
+ return true;
+ // Next unmatched character or last matched character is
+ // path separator.
+ else if (s[beginsWithSize] == kPathNameSeparator ||
+ beginsWith[beginsWithSize - 1] == kPathNameSeparator)
+ return true;
+ else
+ return false;
+}
+
+// Converts a hex digit to the actual value. Invalid hex characters are treated as '0'
+static inline unsigned char Unhex(char c) {
+ if( c >= '0' && c <= '9')
+ return c-'0';
+ if( c >= 'a' && c <= 'f')
+ return c-'a'+10;
+ if( c >= 'A' && c <= 'F')
+ return c-'A'+10;
+ return 0;
+}
+
+// Takes a string containing path characters and URL escapes and returns a string that can be used as a file name
+string EncodePath(const string& s)
+{
+ string result;
+ size_t i=0;
+ while( i < s.size() )
+ {
+ size_t j=s.find_first_not_of("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 ", i);
+ if (j == string::npos )
+ {
+ result += ToLower(s.substr(i));
+ break;
+ }
+
+ if( j>i ) result += ToLower(s.substr(i,j-i));
+
+ if ( s[j] == '%' && j+2 < s.size() )
+ {
+ // decode %xx URL escapes
+ unsigned char decoded = ToLower((char)(Unhex(s[j+1]) << 4 | Unhex(s[j+2])));
+ if( (decoded >= 'a' && decoded <= 'z') || (decoded >= '0' && decoded <= '9') || decoded == ' ' )
+ result += decoded;
+ else if (decoded == '/')
+ result += '-';
+ else
+ result += Format("_%02x",(int)decoded);
+ i=j+3;
+
+ }
+ else if (s[j] == '?')
+ {
+ // Don't include URL parameters
+ break;
+ }
+ else if (s[j] == '/')
+ {
+ // Convert slashes to dashes
+ while ( j+1 < s.size() && s[j+1] == '/' ) j++; // Collapse multiple /es into one
+ result += '-';
+ i=j+1;
+ }
+ else
+ {
+ // Encode anything "dangerous" as _XX hex encoding
+ result += Format("_%02x",(int)s[j]);
+ i=j+1;
+ }
+ }
+
+ return result;
+}
+
+string DecodePath(const string& s)
+{
+ string result;
+ size_t i=0;
+ while( i < s.size() )
+ {
+ size_t j=s.find("_", i);
+ if (j == string::npos )
+ {
+ result += s.substr(i);
+ break;
+ }
+
+ if( j>i ) result += s.substr (i,j-i);
+
+ string escape = s.substr (j, 3);
+ unsigned int ch = ' ';
+ sscanf(escape.c_str(), "_%x", &ch);
+
+ result += (unsigned char)ch;
+ i = j+3;
+ }
+
+ return result;
+}
+
+#endif
+
+
+
+// --------------------------------------------------------------------------
+
+#if ENABLE_UNIT_TESTS
+
+#include "External/UnitTest++/src/UnitTest++.h"
+
+SUITE (PathNameUtilityTests)
+{
+ #if UNITY_EDITOR
+ TEST(RequiresNormalization)
+ {
+ CHECK(!RequiresNormalization(""));
+ CHECK(!RequiresNormalization("!aA09~|"));
+ CHECK(RequiresNormalization("\n"));
+ CHECK(RequiresNormalization("\t"));
+ CHECK(RequiresNormalization("\x80"));
+ CHECK(RequiresNormalization("\xCC"));
+ }
+
+ TEST(CheckValidFileName)
+ {
+ CHECK_EQUAL(kFileNameNotRecommended, CheckValidFileNameDetail(" temp"));
+ CHECK_EQUAL(kFileNameValid, CheckValidFileNameDetail("temp"));
+ CHECK_EQUAL(kFileNameInvalid, CheckValidFileNameDetail("temp."));
+ CHECK_EQUAL(kFileNameValid, CheckValidFileNameDetail("tet.png.bytes"));
+ CHECK_EQUAL(kFileNameInvalid, CheckValidFileNameDetail("test/"));
+ CHECK_EQUAL(kFileNameInvalid, CheckValidFileNameDetail("test\\"));
+ CHECK_EQUAL(kFileNameNotRecommended, CheckValidFileNameDetail("te:st"));
+
+ CHECK_EQUAL(kFileNameValid, CheckValidFileNameDetail("test...test.png"));
+ CHECK_EQUAL(kFileNameValid, CheckValidFileNameDetail("test..test.png"));
+ CHECK_EQUAL(kFileNameInvalid, CheckValidFileNameDetail(".."));
+ CHECK_EQUAL(kFileNameInvalid, CheckValidFileNameDetail("..."));
+
+ CHECK_EQUAL(kFileNameInvalid, CheckValidFileNameDetail(GetLastPathNameComponent("Assets/\\bad.tmp")));
+
+ CHECK_EQUAL("test", MakeFileNameValid("test"));
+ CHECK_EQUAL("", MakeFileNameValid(" "));
+ CHECK_EQUAL("", MakeFileNameValid(" "));
+ CHECK_EQUAL("", MakeFileNameValid("..test"));
+ CHECK_EQUAL("test", MakeFileNameValid(" test"));
+ CHECK_EQUAL("test_", MakeFileNameValid(" test "));
+ CHECK_EQUAL("test_", MakeFileNameValid("test."));
+ CHECK_EQUAL("____", MakeFileNameValid(" /// "));
+ }
+ #endif
+
+ TEST(GetPathNameExtension)
+ {
+ CHECK(strcmp(GetPathNameExtension(".dll").c_str(), "dll") == 0);
+ CHECK(strcmp(GetPathNameExtension(".dll/boing").c_str(), "") == 0);
+ CHECK(strcmp(GetPathNameExtension("hello/.dlL").c_str(), "dlL") == 0);
+ CHECK(strcmp(GetPathNameExtension("hello/blah.grr.dll").c_str(), "dll") == 0);
+ CHECK(strcmp(GetPathNameExtension("hello/boing.dll").c_str(), "dll") == 0);
+ }
+
+
+} // SUITE
+
+#endif // ENABLE_UNIT_TESTS
diff --git a/Runtime/Utilities/PathNameUtility.h b/Runtime/Utilities/PathNameUtility.h
new file mode 100644
index 0000000..0ab1c48
--- /dev/null
+++ b/Runtime/Utilities/PathNameUtility.h
@@ -0,0 +1,153 @@
+#ifndef PATHNAMEUTILITY_H
+#define PATHNAMEUTILITY_H
+
+#include <string>
+#include <vector>
+#include <functional>
+
+
+// Path names in Unity always use forward slash as a separator; and convert to other one (if needed)
+// only in low-level file functions.
+const char kPathNameSeparator = '/';
+
+// If absolutely required, this defines a platform specific path separator.
+#if UNITY_WIN || UNITY_XENON
+const char kPlatformPathNameSeparator = '\\';
+#elif UNITY_OSX || UNITY_WII || UNITY_PS3 || UNITY_IPHONE || UNITY_ANDROID || UNITY_PEPPER || UNITY_LINUX || UNITY_FLASH || UNITY_WEBGL || UNITY_BB10 || UNITY_TIZEN
+const char kPlatformPathNameSeparator = '/';
+#else
+#error "Unknown platform"
+#endif
+
+
+
+std::string AppendPathName (const std::string& pathName, const std::string& append);
+std::string AppendPathNameExtension (const std::string& pathName, const std::string& extension);
+
+std::string GetPathNameExtension (const std::string& pathName);
+const char* GetPathNameExtension (const char* path, size_t cachedStringLength);
+std::string GetLastPathNameComponent (const std::string& pathName);
+const char* GetLastPathNameComponent (const char* path, size_t length);
+
+
+// Returns true if path is a child folder of beginsWith or the path itself. Case insensitive.
+bool StartsWithPath (const std::string &path, const std::string& beginsWith);
+
+std::vector<std::string> FindSeparatedPathComponents (char const* pathName, size_t size, char separator);
+inline std::vector<std::string> FindSeparatedPathComponents (std::string const& pathName, char separator) {
+ return FindSeparatedPathComponents (pathName.c_str (), pathName.size (), separator);
+}
+
+std::string DeletePathNameExtension (const std::string& pathName);
+std::string StandardizePathName (const std::string& pathName);
+
+std::string DeleteLastPathNameComponent (const std::string& pathName);
+std::string DeleteFirstPathNameComponent (const std::string& pathName);
+
+inline std::string GetFileNameWithoutExtension(const std::string& pathName) { return DeletePathNameExtension(GetLastPathNameComponent(pathName)); }
+
+bool IsPathNameVisible (const std::string& path);
+
+
+void ConvertSeparatorsToUnity( char* pathName );
+template<typename alloc>
+void ConvertSeparatorsToUnity( std::basic_string<char, std::char_traits<char>, alloc>& pathName )
+{
+ typename std::basic_string<char, std::char_traits<char>, alloc>::iterator it = pathName.begin(), itEnd = pathName.end();
+ while( it != itEnd ) {
+ if( *it == '\\' )
+ *it = kPathNameSeparator;
+ ++it;
+ }
+}
+void ConvertSeparatorsToPlatform( char* pathName );
+void ConvertSeparatorsToPlatform( std::string& pathName );
+
+int StrICmp (const char* a, const char* b);
+
+// These functions operate on platform dependent path separators
+
+std::string PlatformAppendPathName (const std::string& pathName, const std::string& append);
+std::string PlatformGetLastPathNameComponent (const std::string& pathName);
+std::string PlatformDeleteLastPathNameComponent (const std::string& pathName);
+
+std::string EncodePath(const std::string& s);
+std::string DecodePath(const std::string& s);
+
+#if UNITY_EDITOR
+bool IsValidFileNameSymbol (const char c);
+
+bool CheckValidFileName (const std::string& name);
+
+const char* GetInvalidFilenameChars ();
+
+enum FileNameValid { kFileNameValid = 0, kFileNameInvalid = 1, kFileNameNotRecommended = 2 };
+FileNameValid CheckValidFileNameDetail (const std::string& name);
+
+std::string MakeFileNameValid (const std::string& fileName);
+
+// Removes last / or \ if it exists in path
+std::string TrimSlash(const std::string& path);
+
+std::string& GetGoodPathNameForGUID (const std::string& pathname);
+std::string& GetGoodPathName (const std::string& pathname);
+
+std::string NormalizeUnicode (const std::string& utf8, bool decompose);
+
+inline bool RequiresNormalization (const char* utf8)
+{
+ const unsigned char* c = (const unsigned char*)utf8;
+ while (*c)
+ {
+ if (*c < 32 || *c > 127)
+ return true;
+
+ c++;
+ }
+
+ return false;
+}
+
+inline bool RequiresNormalization (const std::string& utf8)
+{
+ return RequiresNormalization (utf8.c_str());
+}
+
+// Use this functor as a comparison function when using paths as keys in std::map.
+struct PathCompareFunctor : std::binary_function<std::string, std::string, bool>
+{
+ bool operator() ( const std::string& path_a, const std::string& path_b ) const
+ {
+ #if UNITY_OSX
+ bool ascii = !RequiresNormalization (path_a) && !RequiresNormalization (path_b);
+ if (ascii)
+ return StrICmp(path_a.c_str(), path_b.c_str()) < 0;
+ else
+ return StrICmp(NormalizeUnicode(path_a, true).c_str(), NormalizeUnicode(path_b, true).c_str()) < 0;
+ #else
+ return StrICmp(path_a.c_str(), path_b.c_str()) < 0;
+ #endif
+ }
+};
+
+struct PathEqualityFunctor : std::binary_function<std::string, std::string, bool>
+{
+ bool operator() ( const std::string& path_a, const std::string& path_b ) const
+ {
+#if UNITY_OSX
+ bool ascii = !RequiresNormalization (path_a) && !RequiresNormalization (path_b);
+ if (ascii)
+ return StrICmp(path_a.c_str(), path_b.c_str()) == 0;
+ else
+ return StrICmp(NormalizeUnicode(path_a, true).c_str(), NormalizeUnicode(path_b, true).c_str()) == 0;
+#else
+ return StrICmp(path_a.c_str(), path_b.c_str()) == 0;
+#endif
+ }
+};
+
+
+#endif
+
+#endif
+
diff --git a/Runtime/Utilities/PlayerPrefs.h b/Runtime/Utilities/PlayerPrefs.h
new file mode 100644
index 0000000..fbb693c
--- /dev/null
+++ b/Runtime/Utilities/PlayerPrefs.h
@@ -0,0 +1,65 @@
+#ifndef PLAYERPREFS_H
+#define PLAYERPREFS_H
+
+#include <string>
+#include <vector>
+
+#if UNITY_EDITOR
+class EditorPrefs {
+public:
+
+ static void UseCleanTestPrefs ();
+ static bool SetInt (const std::string& name, int value);
+ static bool SetBool (const std::string& name, bool value);
+ static bool SetString (const std::string& name, const std::string& value);
+ static bool SetFloat (const std::string& name, float value);
+
+ static int GetInt (const std::string& name, int def = 0);
+ static bool GetBool (const std::string& name, bool def = false);
+ static std::string GetString (const std::string& name, const std::string& def = std::string ());
+ static float GetFloat (const std::string& name, float def = 0.0F);
+
+ static bool HasKey (const std::string& name);
+ static void DeleteKey (const std::string& name);
+ static void DeleteAll ();
+
+ static void Sync ();
+};
+#endif
+
+class PlayerPrefs {
+public:
+ typedef std::vector<UInt8> RawData;
+public:
+ static bool SetInt (const std::string& name, int value);
+ static bool SetString (const std::string& name, const std::string& value);
+ static bool SetFloat (const std::string& name, float value);
+
+ static int GetInt (const std::string& name, int def = 0);
+ static std::string GetString (const std::string& name, const std::string& def = std::string ());
+ static float GetFloat (const std::string& name, float def = 0.0F);
+
+ static bool HasKey (const std::string& name);
+ static void DeleteKey (const std::string& name);
+ static void DeleteAll ();
+
+ #if UNITY_WII_API && (UNITY_WIN || UNITY_WII)
+ static bool GetRawData(RawData& rawData);
+ static bool SetRawData(const RawData& rawData);
+ #endif
+
+ static void Sync ();
+ #if UNITY_METRO
+ static void Init();
+ #elif WEBPLUG
+ static void Init (const std::string& playerURL);
+ #endif
+ #if UNITY_LINUX
+ static std::string GetPath ();
+ #endif
+
+ static void StaticInitialize();
+ static void StaticDestroy();
+};
+
+#endif
diff --git a/Runtime/Utilities/Prefetch.h b/Runtime/Utilities/Prefetch.h
new file mode 100644
index 0000000..d18922f
--- /dev/null
+++ b/Runtime/Utilities/Prefetch.h
@@ -0,0 +1,52 @@
+#ifndef PREFETCH_H
+#define PREFETCH_H
+
+#if UNITY_PS3
+#include <ppu_intrinsics.h>
+#endif
+
+// This assembly will not work for Jungle. TODO: use arm version check here
+#if defined(__arm__) && !UNITY_LINUX && !UNITY_WINRT
+
+inline void Prefetch(const void* p)
+{
+ unsigned char* pCurr = (unsigned char*)p;
+ asm volatile(
+ "pld [%0] \n\t"
+ : "=r" (pCurr)
+ : "0" (pCurr)
+ : "r0");
+}
+
+inline void Prefetch(const void* p, size_t size)
+{
+ unsigned char* pCurr = (unsigned char*)p;
+ unsigned char* pEnd = pCurr + size;
+
+ while (pCurr < pEnd)
+ {
+ asm volatile(
+ "pld [%0] \n\t"
+ : "=r" (pCurr)
+ : "0" (pCurr)
+ : "r0");
+ pCurr += 32;
+ }
+}
+
+#elif defined(_XBOX)
+__forceinline void Prefetch(const void* p, size_t size = 32)
+{
+ __dcbt(0, p);
+}
+#elif UNITY_PS3
+inline void Prefetch(const void* p, size_t size = 32)
+{
+ __dcbt(p);
+}
+#else
+//@TODO: gcc __builtin_prefetch(p); profile & enable
+inline void Prefetch(const void* /*p*/, size_t /*size*/ = 32) { }
+#endif
+
+#endif
diff --git a/Runtime/Utilities/RecursionLimit.h b/Runtime/Utilities/RecursionLimit.h
new file mode 100644
index 0000000..8425335
--- /dev/null
+++ b/Runtime/Utilities/RecursionLimit.h
@@ -0,0 +1,54 @@
+#ifndef RECURSIONLIMIT_H
+#define RECURSIONLIMIT_H
+
+class RecursionLimiter {
+public:
+ RecursionLimiter (unsigned int *count)
+ {
+ m_Count = count;
+ (*m_Count)++;
+ }
+
+ ~RecursionLimiter ()
+ {
+ (*m_Count)--;
+ }
+private:
+ unsigned int *m_Count;
+};
+
+#define LIMIT_RECURSION(x, retval) static unsigned int reclimit = 0; RecursionLimiter limiter(&reclimit); if (reclimit > x) return retval;
+
+
+class ReentrancyChecker
+{
+public:
+ ReentrancyChecker( bool* variable )
+ : m_Variable(variable)
+ {
+ if( *m_Variable == false )
+ {
+ *m_Variable = true;
+ m_OK = true;
+ }
+ else
+ {
+ m_OK = false;
+ }
+ }
+ ~ReentrancyChecker()
+ {
+ if( m_OK )
+ {
+ *m_Variable = false;
+ }
+ }
+ bool IsOK() const { return m_OK; }
+
+private:
+ bool* m_Variable;
+ bool m_OK;
+};
+
+
+#endif
diff --git a/Runtime/Utilities/ReportHardware.cpp b/Runtime/Utilities/ReportHardware.cpp
new file mode 100644
index 0000000..868287f
--- /dev/null
+++ b/Runtime/Utilities/ReportHardware.cpp
@@ -0,0 +1,391 @@
+#include "UnityPrefix.h"
+#include "ReportHardware.h"
+#include "Runtime/Misc/SystemInfo.h"
+#include "Runtime/Shaders/GraphicsCaps.h"
+#include "HashFunctions.h"
+#include "Configuration/UnityConfigureVersion.h"
+#include "Configuration/UnityConfigureOther.h"
+#include "Configuration/UnityConfigure.h"
+#include "Runtime/Graphics/ScreenManager.h"
+#include "Runtime/Export/WWW.h"
+#include "Runtime/Misc/BuildSettings.h"
+#include "Runtime/Misc/CPUInfo.h"
+#include "Runtime/Misc/PlayerSettings.h"
+#include "Runtime/Utilities/GlobalPreferences.h"
+#include "Runtime/Utilities/Argv.h"
+#include "Runtime/Utilities/Word.h"
+
+#if UNITY_LINUX
+#include "PlatformDependent/Linux/X11Quarantine.h"
+#endif
+
+#if SUPPORT_REPRODUCE_LOG
+#include "Runtime/Misc/ReproductionLog.h"
+#endif
+
+#if UNITY_ANDROID
+#include "PlatformDependent/AndroidPlayer/AndroidSystemInfo.h"
+#include "PlatformDependent/AndroidPlayer/EntryPoint.h"
+#endif
+
+#if UNITY_IPHONE
+ extern "C" const char* UnityAdvertisingIdentifier();
+ extern "C" bool UnityAdvertisingTrackingEnabled();
+#endif
+
+#if UNITY_METRO
+#include "PlatformDependent/MetroPlayer/AppCallbacks.h"
+#include "PlatformDependent/MetroPlayer/MetroCapabilities.h"
+#endif
+
+#if UNITY_OSX
+static int numberForKey( CFDictionaryRef desc, CFStringRef key )
+{
+ CFNumberRef value;
+ int num = 0;
+ if ( (value = (CFNumberRef)CFDictionaryGetValue(desc, key)) == NULL )
+ return 0;
+ CFNumberGetValue(value, kCFNumberIntType, &num);
+ return num;
+}
+#endif
+
+#if UNITY_EDITOR
+const char* kHardwareReportDoneKey = "Editor StatsDone";
+#elif UNITY_STANDALONE
+const char* kHardwareReportDoneKey = "StandaloneStatsDone";
+#else
+const char* kHardwareReportDoneKey = "StatsDone";
+#endif
+
+// URL to report hardware info to
+const char* kHardwareReportURL = "http://stats.unity3d.com/HWStats.cgi";
+
+
+static bool CheckHardwareReportNeeded()
+{
+ #if !UNITY_ENABLE_HWSTATS_REPORT
+ return false;
+ #endif
+
+ #if SUPPORT_REPRODUCE_LOG
+ if (RunningReproduction())
+ return false;
+ #endif
+
+ #if !WEBPLUG
+ if (!IsHumanControllingUs())
+ return false;
+ #endif
+
+ #if UNITY_ANDROID
+ // development player == no stats
+ if (UNITY_DEVELOPER_BUILD)
+ return false;
+
+ if (!GetPlayerSettings().GetEnableHWStatistics())
+ return false;
+
+ // Only report once per OS version
+ std::string osName = systeminfo::GetOperatingSystem();
+ UInt8 md5Value[16];
+ ComputeMD5Hash( reinterpret_cast<const UInt8*>( osName.c_str() ), osName.size(), md5Value );
+ std::string md5String = BytesToHexString( md5Value, 16 );
+
+ if (GetGlobalPreference(UNITY_VERSION_DIGITS) == md5String)
+ return false;
+
+ SetGlobalPreference(UNITY_VERSION_DIGITS, md5String);
+
+ return true;
+ #endif // #if UNITY_ANDROID
+
+
+ #if UNITY_METRO
+ if (metro::Capabilities::IsSupported(metro::Capabilities::kInternetClient, "", false) == false &&
+ metro::Capabilities::IsSupported(metro::Capabilities::kInternetClientServer, "", false) == false)
+ return false;
+ #endif // #if UNITY_METRO
+
+ #if UNITY_IPHONE || UNITY_BB10 || UNITY_TIZEN
+ if (!GetPlayerSettings().GetEnableHWStatistics())
+ return false;
+ #endif
+
+ if ( GetGlobalBoolPreference (kHardwareReportDoneKey, false) )
+ return false;
+ SetGlobalBoolPreference (kHardwareReportDoneKey, true);
+
+ return true;
+}
+
+
+static std::string EscapeFieldString( const std::string& s )
+{
+ const char* kHexToLiteral = "0123456789abcdef";
+ const char *kForbidden = "@&;:<>=?\"'/\\!#%+";
+
+ std::string result;
+ result.reserve( s.size() );
+ for( size_t i = 0; i < s.size(); ++i ) {
+ unsigned char c = s[i];
+ if( c == 32 )
+ result += '+';
+ else if( c < 32 || c > 126 || strchr(kForbidden, c) != NULL )
+ {
+ result += '%';
+ result += kHexToLiteral[c >> 4];
+ result += kHexToLiteral[c & 0xF];
+ }
+ else
+ result += c;
+ }
+ return result;
+}
+
+
+void HardwareInfoReporter::Shutdown()
+{
+ #if ENABLE_WWW
+ if( m_InfoPost != NULL )
+ {
+ m_InfoPost->Release();
+ m_InfoPost = NULL;
+ }
+ #endif
+}
+
+static void StripGfxDriverVersion(std::string& fullName, const std::string& version)
+{
+ size_t pos = fullName.find(version);
+
+ if (pos != std::string::npos && pos > 1)
+ fullName.erase(pos-1, version.size()+1);
+}
+
+void HardwareInfoReporter::ReportHardwareInfo()
+{
+#if ENABLE_WWW
+ AssertIf( m_InfoPost != NULL );
+
+ // If hardware info already reported, do nothing
+ if( !CheckHardwareReportNeeded() )
+ return;
+
+
+ // Get all the information to post
+ std::string infoUUID; // Used to be sent by 2.0.2, not anymore with 2.1. Keeping to match the hash value. Now used by Android and iPhone only.
+ std::string deviceModel; // Used by Android and iPhone only
+ // Report runtime version used
+ std::string infoOS = systeminfo::GetOperatingSystem();
+ std::string infoCPU = systeminfo::GetProcessorType();
+ std::string infoGfxName = gGraphicsCaps.rendererString;
+ std::string infoGfxVendor = gGraphicsCaps.vendorString;
+ std::string infoGfxVersion = gGraphicsCaps.fixedVersionString;
+ std::string infoGfxDriver = gGraphicsCaps.driverLibraryString;
+#if UNITY_ANDROID || UNITY_IPHONE || UNITY_BB10 || UNITY_TIZEN
+ std::string appId = GetPlayerSettings().GetiPhoneBundleIdentifier();
+#else
+ std::string appId = Format("%s.%s",GetPlayerSettings().GetCompanyName().c_str(), GetPlayerSettings().GetProductName().c_str());
+#endif
+ int infoCpuCount = systeminfo::GetProcessorCount();
+ int infoRAM = systeminfo::GetPhysicalMemoryMB();
+ int infoVRAM = gGraphicsCaps.videoMemoryMB;
+
+ std::string unityVersion = UNITY_VERSION; // Report runtime version used
+ std::string buildVersion = "0.0.0"; // Unknown build version used
+
+ // Get desktop display mode dimensions. Do this directly because
+ // we initiate the report before screen manager is even set up
+ // (so that in case something horrible happens while initializing
+ // the game, we'd still get a chance to get the report).
+ #if UNITY_WIN && !UNITY_WINRT
+ DEVMODE displayMode;
+ memset( &displayMode, 0, sizeof(displayMode) );
+ displayMode.dmSize = sizeof(DEVMODE);
+ EnumDisplaySettings( NULL, ENUM_CURRENT_SETTINGS, &displayMode );
+ std::string infoScreenRes = Format("%i x %i", displayMode.dmPelsWidth, displayMode.dmPelsHeight);
+
+ #elif UNITY_OSX
+ CFDictionaryRef currentMode = CGDisplayCurrentMode(kCGDirectMainDisplay);
+ int desktopWidth = numberForKey(currentMode, kCGDisplayWidth);
+ int desktopHeight = numberForKey(currentMode, kCGDisplayHeight);
+ std::string infoScreenRes = Format("%i x %i", desktopWidth, desktopHeight);
+
+ #elif UNITY_LINUX
+ unsigned width = 0;
+ unsigned height = 0;
+ #if SUPPORT_X11
+ NativeDisplayPtr display = OpenDisplay(NULL);
+ NativeWindow rootWindow = GetRootWindow(display);
+ GetWindowSize(display, rootWindow, &width, &height);
+ CloseDisplay(display);
+ #endif
+ std::string infoScreenRes = Format("%u x %u", width, height);
+ #elif UNITY_ANDROID
+ // Create a less obscure OS string
+ infoOS = Format ("%s API-%i", systeminfo::GetDeviceSystemName(), android::systeminfo::ApiLevel());
+ // Strip 'driverLibraryString' from 'fixedVersionString', if duplicated
+ StripGfxDriverVersion(infoGfxVersion, infoGfxDriver);
+ // Get the raw display size (physical size of the display)
+ int width, height;
+ android::systeminfo::DisplaySize(&width, &height);
+ std::string infoScreenRes = Format("%u x %u", width, height);
+ // Use ANDROID ID/IMEI/WiFi MAC as UUID
+ infoUUID = android::systeminfo::UniqueIdentifier();
+ // This matches the first part of ro.build.fingerprint
+ deviceModel = Format ("%s/%s/%s", android::systeminfo::Manufacturer(), android::systeminfo::Model(), android::systeminfo::Device());
+ #elif UNITY_IPHONE
+ std::string infoScreenRes = Format("%u x %u", GetScreenManager().GetWidth(), GetScreenManager().GetHeight());
+ infoUUID = systeminfo::GetDeviceUniqueIdentifier(); // it is hash of WiFi MAC address
+ deviceModel = systeminfo::GetDeviceModel();
+ StripGfxDriverVersion(infoGfxVersion, infoGfxDriver);
+ #elif UNITY_BLACKBERRY
+ std::string infoScreenRes = Format("%u x %u", GetScreenManager().GetWidth(), GetScreenManager().GetHeight());
+ // This will be EMPTY for Blackberry if permission is set false
+ infoUUID = systeminfo::GetDeviceUniqueIdentifier(); // Unique ID from OS / it is hash of WiFi MAC address
+ deviceModel = Format("%s/%s", "BlackBerry", systeminfo::GetDeviceModel());
+ StripGfxDriverVersion(infoGfxVersion, infoGfxDriver);
+ #elif UNITY_METRO
+ std::string infoScreenRes = Format("%u x %u", GetScreenManager().GetWidth(), GetScreenManager().GetHeight());
+ // It seems if we call GetDeviceUniqueIdentifier, this somehow distrubts the data flow, and server doesn't accept it
+ infoUUID = systeminfo::GetDeviceUniqueIdentifier(); // it is hash of WiFi MAC address
+ {
+ // Make infoUUID different one from the Editor and encode it again
+ infoUUID+="Metro";
+ UInt8 md5Value[16];
+ ComputeMD5Hash( reinterpret_cast<const UInt8*>(infoUUID.c_str() ), infoUUID.size(), md5Value);
+ infoUUID = BytesToHexString(md5Value, 16);
+ }
+ deviceModel = "n/a";
+ #elif UNITY_WP8
+ auto& screenManager = GetScreenManager ();
+ std::string infoScreenRes;
+ if (screenManager.GetScreenOrientation () == ScreenOrientation::kLandscapeLeft || screenManager.GetScreenOrientation () == ScreenOrientation::kLandscapeRight)
+ {
+ infoScreenRes = Format ("%u x %u", screenManager.GetHeight (), screenManager.GetWidth ());
+ }
+ else
+ {
+ infoScreenRes = Format ("%u x %u", screenManager.GetWidth (), screenManager.GetHeight ());
+ }
+ infoUUID = systeminfo::GetDeviceUniqueIdentifier ();
+ deviceModel = systeminfo::GetDeviceName ();
+ #elif UNITY_TIZEN
+ std::string infoScreenRes = Format("%u x %u", GetScreenManager().GetWidth(), GetScreenManager().GetHeight());
+ infoUUID = systeminfo::GetDeviceUniqueIdentifier();
+ deviceModel = Format("%s/%s", "Tizen", systeminfo::GetDeviceModel());
+ StripGfxDriverVersion(infoGfxVersion, infoGfxDriver);
+ #else
+ #error "Unsupported platform"
+ #endif
+
+ // Various flags, bits:
+ // 0..3: license type
+ // 4..5: metro presentation types
+ // 6..17: CPU capabilities, reserved up to 23
+ // 24..25: GPU capabilities
+ int flags = 0;
+ BuildSettings* buildSettings = &GetBuildSettings();
+ if (buildSettings)
+ {
+ // bits 0..2: 1=Indie, 2=Pro
+ flags |= buildSettings->hasPROVersion ? 2 : 1;
+ // bit 3: 0=Licensed, 1=Trial
+ if (!buildSettings->hasPublishingRights)
+ flags |= 8;
+
+ // Report build version used
+ buildVersion = buildSettings->GetVersion();
+ }
+
+#if UNITY_METRO
+ // bits 4..5: 1=XAML app, 2=D3D app
+ flags |= UnityPlayer::AppCallbacks::Instance->GetAppType() == UnityPlayer::AppCallbacks::kAppTypeD3DXAML ? (1 << 4) : (1 << 5);
+#endif
+
+ // CPU capabilities flags, bits 6..17
+ flags |= (1<<6); // CPU caps bits are present (to determine between earlier versions that did not send these flags at all)
+ if (CPUInfo::HasSSE2Support())
+ flags |= (1<<7);
+ if (CPUInfo::HasSSE41Support())
+ flags |= (1<<8);
+ if (CPUInfo::HasSSE42Support())
+ flags |= (1<<9);
+ if (CPUInfo::HasAVXSupport())
+ flags |= (1<<10);
+ if (CPUInfo::HasNEONSupport())
+ flags |= (1<<11);
+ if (CPUInfo::HasSSE3Support())
+ flags |= (1<<12);
+ if (CPUInfo::HasSupplementalSSE3Support())
+ flags |= (1<<13);
+ if (CPUInfo::HasAVX2Support())
+ flags |= (1<<14);
+ if (CPUInfo::HasAVX512Support())
+ flags |= (1<<15);
+ if (CPUInfo::HasFP16CSupport())
+ flags |= (1<<16);
+ if (CPUInfo::HasFMASupport())
+ flags |= (1<<17);
+
+ // GPU capabilities flags, bits 24..25
+ if (gGraphicsCaps.hasNativeDepthTexture)
+ flags |= (1<<24);
+ if (gGraphicsCaps.hasNativeShadowMap)
+ flags |= (1<<25);
+
+
+ // Send a hash some fields plus a salt as a cheap way to prevent "spam" on hardware
+ // report script.
+ std::string stringToHash;
+ stringToHash = std::string("KonfiguracijosReportoDruska-") + infoUUID + infoOS + infoCPU + infoGfxName + infoGfxVendor + infoGfxVersion;
+
+ UInt8 md5Value[16];
+ ComputeMD5Hash( reinterpret_cast<const UInt8*>( stringToHash.c_str() ), stringToHash.size(), md5Value );
+ std::string md5String = BytesToHexString( md5Value, 16 );
+
+ // Construct POST request data
+ std::string postData;
+ postData += "os=" + EscapeFieldString(infoOS);
+ postData += "&cpu=" + EscapeFieldString(infoCPU);
+ postData += "&gfxname=" + EscapeFieldString(infoGfxName);
+ postData += "&gfxvendor=" + EscapeFieldString(infoGfxVendor);
+ postData += "&gfxversion=" + EscapeFieldString(infoGfxVersion);
+ postData += "&gfxdriver=" + EscapeFieldString(infoGfxDriver);
+ postData += "&cpucount=" + IntToString(infoCpuCount);
+ postData += "&ram=" + IntToString(infoRAM);
+ postData += "&vram=" + IntToString(infoVRAM);
+ postData += "&screen=" + EscapeFieldString(infoScreenRes);
+ postData += "&platform=" + IntToString(systeminfo::GetRuntimePlatform());
+ postData += "&flags=" + IntToString(flags);
+ postData += "&hash=" + md5String;
+ postData += "&appId=" + EscapeFieldString(appId);
+#if UNITY_ANDROID || UNITY_IPHONE || UNITY_BB10 || UNITY_WINRT || UNITY_TIZEN
+ postData += "&uuid=" + infoUUID;
+ postData += "&model=" + deviceModel;
+#endif
+#if UNITY_IPHONE
+ if (const char *iOSAdvertisingIdentifier = UnityAdvertisingIdentifier())
+ postData += "&adid=" + std::string(iOSAdvertisingIdentifier);
+ postData += "&adtrack=" + std::string(UnityAdvertisingTrackingEnabled()?"1":"0");
+#endif
+ postData += "&unity=" + unityVersion;
+ postData += "&build=" + buildVersion;
+
+ // Initiate POST request
+ std::map<std::string,std::string> headers;
+ headers.insert( std::make_pair("Content-Type", "application/x-www-form-urlencoded") );
+
+ m_InfoPost = WWW::Create( kHardwareReportURL, postData.c_str(), postData.size(), headers, false );
+
+ /*
+ // Server accepts only 5% percent of data, so if you want to debug, use the loop below, to ensure that data reaches server
+ for (int i = 0; i < 100; i++)
+ {
+ m_InfoPost = WWW::Create( kHardwareReportURL, postData.c_str(), postData.size(), headers, false );
+ Thread::Sleep(0.1f);
+ }
+ */
+#endif // ENABLE_WWW
+}
diff --git a/Runtime/Utilities/ReportHardware.h b/Runtime/Utilities/ReportHardware.h
new file mode 100644
index 0000000..2943869
--- /dev/null
+++ b/Runtime/Utilities/ReportHardware.h
@@ -0,0 +1,19 @@
+#ifndef __REPORT_HARDWARE_H
+#define __REPORT_HARDWARE_H
+
+class WWW;
+
+
+class HardwareInfoReporter {
+public:
+ HardwareInfoReporter() : m_InfoPost(NULL) { };
+
+ void ReportHardwareInfo();
+ void Shutdown();
+
+private:
+ WWW* m_InfoPost;
+};
+
+
+#endif
diff --git a/Runtime/Utilities/SpatialHash.cpp b/Runtime/Utilities/SpatialHash.cpp
new file mode 100644
index 0000000..4865bd4
--- /dev/null
+++ b/Runtime/Utilities/SpatialHash.cpp
@@ -0,0 +1,90 @@
+#include "UnityPrefix.h"
+#include "SpatialHash.h"
+#include "External/MurmurHash/MurmurHash2.h"
+#include "Runtime/Geometry/AABB.h"
+#include <float.h>
+
+
+// quantisation of direction
+inline unsigned QuantiseDirection(const Vector3f& dir)
+{
+ // quantise direction
+ unsigned id;
+ if (fabsf(dir.x) >= fabsf(dir.y) && fabsf(dir.x) >= fabsf(dir.z))
+ id = dir.x > 0 ? 0 : 1;
+ else if (fabsf(dir.y) >= fabsf(dir.z))
+ id = dir.y > 0 ? 2 : 3;
+ else
+ id = dir.z > 0 ? 4 : 5;
+ return id;
+}
+
+// quantisation of position
+inline void QuantisePosition(const Vector3f& pos, float voxelSize, int* xyz)
+{
+ // quantise position
+ const float cell_side = voxelSize;
+ const float cell_height = voxelSize * kVoxelHeightMultiplier;
+ xyz[0] = (int)std::floor(pos.x / cell_side);
+ xyz[1] = (int)std::floor(pos.y / cell_height);
+ xyz[2] = (int)std::floor(pos.z / cell_side);
+}
+
+inline AABB QuantisedValueToAABB(int* quantisedValue, float voxelSize)
+{
+ const float cell_side = voxelSize;
+ const float cell_height = voxelSize * kVoxelHeightMultiplier;
+ const Vector3f pos(quantisedValue[0]*cell_side,quantisedValue[1]*cell_height,quantisedValue[2]*cell_side);
+ const Vector3f halfDir( cell_side*.5f, cell_height*.5f, cell_side*.5f );
+ return AABB(pos + halfDir, Abs(halfDir));
+}
+
+// compute hash index for directions cache
+inline UInt64 ComputeHash(const Vector3f& pos, const Vector3f& dir, float voxelSize )
+{
+ int qp[3];
+ QuantisePosition(pos, voxelSize, qp);
+ UInt64 key = MurmurHash64A( (void*)qp, sizeof(int)*3, 0xdeadbeef186726feULL );
+ // quantise direction
+ unsigned id = QuantiseDirection(dir);
+ key = key ^ id;
+ return key;
+}
+
+/////////////////////////////////////////////////////
+// Collision plane cache members
+PlaneColliderCache_dense_hashmap::PlaneColliderCache_dense_hashmap()
+{
+ m_PlaneHashMap.set_empty_key (ComputeHash(Vector3f(FLT_MAX,FLT_MAX,FLT_MAX),Vector3f(FLT_MAX,FLT_MAX,FLT_MAX),1.f));
+ m_PlaneHashMap.set_deleted_key (ComputeHash(Vector3f(FLT_MIN,FLT_MIN,FLT_MIN),Vector3f(FLT_MIN,FLT_MIN,FLT_MIN),1.f));
+}
+
+bool PlaneColliderCache_dense_hashmap::Replace (const Vector3f& pos, const Vector3f& dir, const Plane& plane, int colliderInstanceID, int rigidBodyOrColliderInstanceID, float voxelSize)
+{
+ UInt64 hash = ComputeHash (pos, dir, voxelSize);
+ int sz = Size();
+ m_PlaneHashMap.insert (std::make_pair (hash, PlaneData (plane, colliderInstanceID, rigidBodyOrColliderInstanceID)));
+ if ( sz >= Size() )
+ {
+ PlaneHashMap::iterator it = m_PlaneHashMap.find (hash);
+ if (it == m_PlaneHashMap.end ())
+ return false;
+ it->second.m_Plane = plane;
+ it->second.m_ColliderInstanceID = colliderInstanceID;
+ it->second.m_RigidBodyOrColliderInstanceID = rigidBodyOrColliderInstanceID;
+ }
+ return true;
+}
+
+bool PlaneColliderCache_dense_hashmap::Find (const Vector3f& pos, const Vector3f& dir, Plane& plane, int& colliderInstanceID, int& rigidBodyOrColliderInstanceID, float voxelSize) const
+{
+ UInt64 hash = ComputeHash (pos, dir, voxelSize);
+ PlaneHashMap::const_iterator it = m_PlaneHashMap.find (hash);
+ if (it == m_PlaneHashMap.end ())
+ return false;
+ plane = it->second.m_Plane;
+ colliderInstanceID = it->second.m_ColliderInstanceID;
+ rigidBodyOrColliderInstanceID = it->second.m_RigidBodyOrColliderInstanceID;
+ return true;
+}
+
diff --git a/Runtime/Utilities/SpatialHash.h b/Runtime/Utilities/SpatialHash.h
new file mode 100644
index 0000000..74dd01e
--- /dev/null
+++ b/Runtime/Utilities/SpatialHash.h
@@ -0,0 +1,50 @@
+#pragma once
+
+#include "Runtime/Geometry/Plane.h"
+#include "Runtime/Utilities/dynamic_array.h"
+#include "Runtime/Utilities/dense_hash_map.h"
+#include <algorithm>
+
+static const float kVoxelHeightMultiplier = 4.f;
+
+struct PlaneData
+{
+ PlaneData () {}
+ PlaneData (const Plane& plane, int colliderInstanceID, int rigidBodyOrColliderInstanceID)
+ : m_Plane (plane)
+ , m_ColliderInstanceID (colliderInstanceID)
+ , m_RigidBodyOrColliderInstanceID (rigidBodyOrColliderInstanceID)
+ {}
+ Plane m_Plane;
+ int m_ColliderInstanceID;
+ int m_RigidBodyOrColliderInstanceID;
+};
+
+// Collision plane cache using the google dense hashmap
+class PlaneColliderCache_dense_hashmap
+{
+public:
+ PlaneColliderCache_dense_hashmap ();
+ ~PlaneColliderCache_dense_hashmap () {Clear();}
+ int Size () const { return m_PlaneHashMap.size(); };
+ bool Find (const Vector3f& pos, const Vector3f& dir, Plane& plane, int& colliderInstanceID, int& rigidBodyOrColliderInstanceID, float voxelSize) const;
+ bool Replace (const Vector3f& pos, const Vector3f& dir, const Plane& plane, int colliderInstanceID, int rigidBodyOrColliderInstanceID, float voxelSize);
+ void Clear ()
+ {
+ m_PlaneHashMap.clear ();
+ }
+private:
+ // Hash map for cached planes
+ struct UInt64HashFunctor
+ {
+ inline size_t operator ()(UInt64 x) const
+ {
+ return int(x >> 32);
+ }
+ };
+ typedef std::pair<const UInt64, PlaneData> KeyToPlanePair;
+ typedef dense_hash_map<UInt64, PlaneData, UInt64HashFunctor, std::equal_to<UInt64>, STL_ALLOCATOR (kMemSTL, KeyToPlanePair) > PlaneHashMap;
+ PlaneHashMap m_PlaneHashMap;
+};
+
+typedef PlaneColliderCache_dense_hashmap PlaneColliderCache;
diff --git a/Runtime/Utilities/Stacktrace.cpp b/Runtime/Utilities/Stacktrace.cpp
new file mode 100644
index 0000000..e87b53a
--- /dev/null
+++ b/Runtime/Utilities/Stacktrace.cpp
@@ -0,0 +1,246 @@
+#include "UnityPrefix.h"
+
+#define HAS_POSIX_BACKTRACE UNITY_OSX || UNITY_ANDROID
+#if HAS_POSIX_BACKTRACE && UNITY_OSX
+ #define POSIX_BACKTRACE_DECL extern "C"
+#else
+ #define POSIX_BACKTRACE_DECL
+#endif
+
+#if UNITY_WIN
+#include <winnt.h>
+#include "PlatformDependent/Win/StackWalker.h"
+typedef USHORT (WINAPI *CaptureStackBackTraceType)(__in ULONG, __in ULONG, __out PVOID*, __out_opt PULONG);
+CaptureStackBackTraceType backtrace = NULL;
+// Then use 'backtrace' as if it were CaptureStackBackTrace
+
+class MyStackWalker: public StackWalker
+{
+protected:
+ virtual void OnSymInit(LPCSTR szSearchPath, DWORD symOptions, LPCSTR szUserName) {}
+ virtual void OnLoadModule(LPCSTR img, LPCSTR mod, DWORD64 baseAddr, DWORD size, DWORD result, LPCSTR symType, LPCSTR pdbName, ULONGLONG fileVersion) {}
+ virtual void OnCallstackEntry(CallstackEntryType eType, CallstackEntry &entry)
+ {
+ if(*entry.undFullName != '\0')
+ OnOutput(entry.undFullName);
+ else if(*entry.undName != '\0')
+ OnOutput(entry.undName);
+ else if(*entry.name != '\0')
+ OnOutput(entry.name);
+ OnOutput("\n");
+ }
+ virtual void OnDbgHelpErr(LPCSTR szFuncName, DWORD gle, DWORD64 addr) {}
+};
+
+class StringStackWalker: public MyStackWalker
+{
+public:
+ StringStackWalker(): result(NULL) { }
+
+ void SetOutputString(std::string &_result) {result = &_result;}
+protected:
+ std::string *result;
+
+ virtual void OnOutput(LPCSTR szTest)
+ {
+ if(result)
+ *result += szTest;
+ }
+};
+
+class BufferStackWalker: public MyStackWalker
+{
+public:
+ BufferStackWalker(): maxSize(0) { }
+
+ void SetOutputBuffer(char *_buffer, int _maxSize)
+ {
+ buffer = _buffer;
+ maxSize = _maxSize;
+ }
+protected:
+ char *buffer;
+ int maxSize;
+
+ virtual void OnOutput(LPCSTR szTest)
+ {
+ while(maxSize > 1 && *szTest != '\0')
+ {
+ *(buffer++) = *(szTest++);
+ maxSize--;
+ }
+ *buffer = '\0';
+ }
+};
+#elif HAS_POSIX_BACKTRACE
+POSIX_BACKTRACE_DECL int backtrace(void** buffer, int size);
+POSIX_BACKTRACE_DECL char** backtrace_symbols(void* const* buffer, int size);
+#include "Runtime/Mono/MonoIncludes.h"
+ #if UNITY_ANDROID
+ #include <corkscrew/backtrace.h>
+ #include "Runtime/Mono/MonoIncludes.h"
+ #endif
+#endif
+
+#if UNITY_WIN
+StringStackWalker* g_StringStackWalker = NULL;
+BufferStackWalker* g_BufferStackWalker = NULL;
+#endif
+
+void InitializeStackWalker(){
+#if UNITY_WIN
+ if (g_BufferStackWalker == NULL){
+ g_BufferStackWalker = new BufferStackWalker();
+ }
+ if (g_StringStackWalker == NULL){
+ g_StringStackWalker = new StringStackWalker();
+ }
+#endif
+}
+
+std::string GetStacktrace(bool includeMonoStackFrames)
+{
+ std::string trace;
+#if UNITY_WIN
+ InitializeStackWalker();
+ g_StringStackWalker->SetOutputString(trace);
+ if (includeMonoStackFrames)
+ g_StringStackWalker->ShowCallstackSimple();
+ else
+ g_StringStackWalker->ShowCallstack();
+#elif HAS_POSIX_BACKTRACE
+ void* backtraceFrames[128];
+ int frameCount = backtrace(&backtraceFrames[0], 128);
+ char** frameStrings = backtrace_symbols(&backtraceFrames[0], frameCount);
+
+ if(frameStrings != NULL)
+ {
+ for(int x = 0; x < frameCount; x++)
+ {
+ if(frameStrings[x] != NULL)
+ {
+ std::string str = frameStrings[x];
+ if(str.find("???") != std::string::npos)
+ {
+ // If the symbol could not be resolved, try if mono knows about the address.
+ unsigned int addr = 0;
+ if (sscanf(str.substr(str.find("0x")).c_str(), "0x%x", &addr))
+ {
+ if(addr == -1){
+ break;
+ }
+ const char *mono_frame = mono_pmip((void*)addr);
+ // If mono knows the address, replace the stack trace line with
+ // the information from mono.
+ if (mono_frame)
+ str = str.substr(0,4) + "[Mono JITed code] " + mono_frame;
+ }
+ }
+ trace = trace + str + '\n';
+ }
+ }
+ free(frameStrings);
+ }
+#else
+ trace = "Stacktrace is not supported on this platform.";
+#endif
+ return trace;
+}
+
+UInt32 GetStacktrace(void** trace, int maxFrames, int startframe)
+{
+#if UNITY_WIN
+ if(!backtrace)
+ backtrace = (CaptureStackBackTraceType)(GetProcAddress(LoadLibrary("kernel32.dll"), "RtlCaptureStackBackTrace"));
+ DWORD dwHashCode = 0;
+ int count = backtrace(startframe, maxFrames-1, trace, &dwHashCode);
+ trace[count] = 0;
+ return dwHashCode;
+// InitializeStackWalker();
+// g_BufferStackWalker->GetCurrentCallstack(trace,maxFrames);
+#elif HAS_POSIX_BACKTRACE
+ int framecount = backtrace(trace, maxFrames);
+ trace[framecount] = 0;
+ int index = startframe;
+ UInt32 hash = 0;
+ while(trace[index])
+ {
+ trace[index-startframe] = trace[index];
+ hash ^= (UInt32)(trace[index]) ^ (hash << 7) ^ (hash >> 21);
+ index++;
+ }
+ trace[index-startframe] = 0;
+ return hash;
+#endif
+ return 0;
+}
+
+void GetReadableStackTrace(char* output, int bufsize, void** input, int frameCount)
+{
+#if UNITY_WIN
+ InitializeStackWalker();
+ g_BufferStackWalker->GetStringFromStacktrace(output, bufsize, input);
+#elif HAS_POSIX_BACKTRACE
+ char **frameStrings = backtrace_symbols(input, frameCount);
+ std::string trace;
+ if(frameStrings != NULL)
+ {
+ for(int x = 0; x < frameCount; x++)
+ {
+ if(frameStrings[x] != NULL)
+ {
+ #if UNITY_ANDROID
+ char line[256] = {0};
+ snprintf(line, 255, " #%02d %s\n", x, frameStrings[x]);
+ trace = trace + line;
+ #else
+ trace = trace + frameStrings[x] + '\n';
+ #endif
+ }
+ }
+ free(frameStrings);
+ }
+ strcpy(output,trace.c_str());
+#endif
+
+}
+
+void GetStacktrace(char *trace, int maxSize, int maxFrames)
+{
+#if UNITY_WIN
+ InitializeStackWalker();
+ g_BufferStackWalker->SetOutputBuffer(trace, maxSize);
+ g_BufferStackWalker->ShowCallstack(
+ GetCurrentThread(),
+ NULL,
+ NULL,
+ NULL,
+ maxFrames);
+#elif HAS_POSIX_BACKTRACE
+ void *backtraceFrames[128];
+ int frameCount = backtrace(&backtraceFrames[0], 128);
+ char **frameStrings = backtrace_symbols(&backtraceFrames[0], frameCount);
+ frameCount = std::min(frameCount, maxFrames);
+ if(frameStrings != NULL)
+ {
+ for(int x = 0; x < frameCount; x++)
+ {
+ if(frameStrings[x] != NULL)
+ {
+ #if UNITY_ANDROID
+ int numChars = snprintf(trace, maxSize, " #%02d %s\n", x, frameStrings[x]);
+ #else
+ int numChars = snprintf(trace, maxSize, "%s\n", frameStrings[x]);
+ #endif
+ trace += numChars;
+ maxSize -= numChars;
+ }
+ if(maxSize <= 0)
+ break;
+ }
+ free(frameStrings);
+ }
+#else
+ strncpy(trace, "Stacktrace is not supported on this platform.", maxSize);
+#endif
+}
diff --git a/Runtime/Utilities/Stacktrace.h b/Runtime/Utilities/Stacktrace.h
new file mode 100644
index 0000000..7344e91
--- /dev/null
+++ b/Runtime/Utilities/Stacktrace.h
@@ -0,0 +1,12 @@
+#ifndef STACKTRACE_H
+#define STACKTRACE_H
+
+void InitializeStackWalker();
+
+std::string GetStacktrace(bool includeMonoStackFrames = false);
+
+//can't use string version when debugging new/delete allocations, as string class may allocate memory itself!
+void GetStacktrace(char *trace, int maxSize, int maxFrames = INT_MAX);
+UInt32 GetStacktrace(void** trace, int maxFrames, int startframe);
+void GetReadableStackTrace(char* output, int bufsize, void** input, int count);
+#endif
diff --git a/Runtime/Utilities/StaticAssert.h b/Runtime/Utilities/StaticAssert.h
new file mode 100644
index 0000000..874b526
--- /dev/null
+++ b/Runtime/Utilities/StaticAssert.h
@@ -0,0 +1,55 @@
+#pragma once
+#include "Configuration/UnityConfigure.h"
+#include "Runtime/Utilities/TypeUtilities.h"
+
+#ifndef SUPPORT_CPP0X_STATIC_ASSERT
+ #if (defined _MSC_VER) && (_MSC_VER >= 1600)
+ #define SUPPORT_CPP0X_STATIC_ASSERT 1
+ #elif (defined __has_extension)
+ #define SUPPORT_CPP0X_STATIC_ASSERT __has_extension(cxx_static_assert)
+ #else
+ #define SUPPORT_CPP0X_STATIC_ASSERT 0
+ #endif
+#endif
+
+#if SUPPORT_CPP0X_STATIC_ASSERT
+ #define CompileTimeAssert(expression, message) static_assert(expression, message)
+#else
+ template<bool> struct CompileTimeAssertImpl;
+ template<> struct CompileTimeAssertImpl<true> {};
+
+ #define CT_ASSERT_HACK_JOIN(X,Y) CT_ASSERT_HACK_DO_JOIN(X,Y)
+ #define CT_ASSERT_HACK_DO_JOIN(X,Y) CT_ASSERT_HACK_DO_JOIN2(X,Y)
+ #define CT_ASSERT_HACK_DO_JOIN2(X,Y) X##Y
+
+ #define CompileTimeAssert(expression, message) \
+ enum{ CT_ASSERT_HACK_JOIN(ct_assert_, __LINE__) = sizeof(CompileTimeAssertImpl<(expression)>) }
+#endif
+
+
+#ifndef SUPPORT_CPP0X_DECLTYPE
+ #if (defined _MSC_VER) && (_MSC_VER >= 1600)
+ #define SUPPORT_CPP0X_DECLTYPE 1
+ #elif (defined __has_extension)
+ #define SUPPORT_CPP0X_DECLTYPE __has_extension(cxx_decltype)
+ #else
+ #define SUPPORT_CPP0X_DECLTYPE 0
+ #endif
+#endif
+
+
+#if SUPPORT_CPP0X_STATIC_ASSERT && SUPPORT_CPP0X_DECLTYPE
+ /// Usage Example:
+ /// int a;
+ /// double b;
+ /// AssertVariableType(a, int); // OK
+ /// AssertVariableType(b, int); // Error during compile time
+ /// AssertIfVariableType(a, int); // Error during compile time
+ /// AssertIfVariableType(b, int); // OK
+
+ #define AssertVariableType(Variable, Type) CompileTimeAssert(IsSameType<Type,decltype(Variable)>::result, #Variable" is not of type "#Type)
+ #define AssertIfVariableType(Variable, Type) CompileTimeAssert(!IsSameType<Type,decltype(Variable)>::result, #Variable" is of type "#Type)
+#else
+ #define AssertVariableType(Variable, Type) do { (void)sizeof(Variable); } while(0)
+ #define AssertIfVariableType(Variable, Type) do { (void)sizeof(Variable); } while(0)
+#endif
diff --git a/Runtime/Utilities/StrideIterator.h b/Runtime/Utilities/StrideIterator.h
new file mode 100644
index 0000000..c36877e
--- /dev/null
+++ b/Runtime/Utilities/StrideIterator.h
@@ -0,0 +1,89 @@
+#ifndef STRIDE_ITERATOR_H_
+#define STRIDE_ITERATOR_H_
+
+#include <iterator>
+
+template<class T>
+class StrideIterator : public std::iterator<std::random_access_iterator_tag, T>
+{
+public:
+ //@TODO: Get rid of all stl usage and remove this crap.
+ typedef std::iterator<std::random_access_iterator_tag, T> base_iterator;
+ typedef typename base_iterator::value_type value_type;
+ typedef typename base_iterator::difference_type difference_type;
+ typedef typename base_iterator::pointer pointer;
+ typedef typename base_iterator::reference reference;
+ typedef typename base_iterator::iterator_category iterator_category;
+
+ StrideIterator () : m_Pointer(NULL), m_Stride(1) {}
+ StrideIterator (void* p, int stride) : m_Pointer (reinterpret_cast<UInt8*> (p)), m_Stride (stride) {}
+ StrideIterator (StrideIterator const& arg) : m_Pointer(arg.m_Pointer), m_Stride(arg.m_Stride) {}
+
+ void operator = (StrideIterator const& arg) { m_Pointer = arg.m_Pointer; m_Stride = arg.m_Stride; }
+
+ bool operator == (StrideIterator const& arg) const { return m_Pointer == arg.m_Pointer; }
+ bool operator != (StrideIterator const& arg) const { return m_Pointer != arg.m_Pointer; }
+
+ bool operator < (StrideIterator const& arg) const { return m_Pointer < arg.m_Pointer; }
+
+ void operator++() { m_Pointer += m_Stride; }
+ void operator++(int) { m_Pointer += m_Stride; }
+
+ StrideIterator operator + (difference_type n) const { return StrideIterator (m_Pointer + m_Stride * n, m_Stride); }
+ void operator += (difference_type n) { m_Pointer += m_Stride * n; }
+
+ difference_type operator-(StrideIterator const& it) const
+ {
+ Assert (m_Stride == it.m_Stride && "Iterators stride must be equal");
+ Assert (m_Stride != 0 && "Stide must not be zero");
+ return ((uintptr_t)m_Pointer - (uintptr_t)it.m_Pointer) / m_Stride;
+ }
+
+ T& operator[](size_t index) { return *reinterpret_cast<T*> (m_Pointer + m_Stride * index); }
+ const T& operator[](size_t index) const { return *reinterpret_cast<const T*> (m_Pointer + m_Stride * index); }
+
+ T& operator*() { return *reinterpret_cast<T*> (m_Pointer); }
+ const T& operator*() const { return *reinterpret_cast<const T*> (m_Pointer); }
+
+ T* operator->() { return reinterpret_cast<T*> (m_Pointer); }
+ const T* operator->() const { return reinterpret_cast<const T*> (m_Pointer); }
+
+ // Iterator is NULL if not valid
+ bool IsNull () const { return m_Pointer == 0; }
+ void* GetPointer() const { return m_Pointer; }
+ int GetStride() const { return m_Stride; }
+
+private:
+ UInt8* m_Pointer;
+ int m_Stride;
+};
+
+template<class T>
+void strided_copy (const T* src, const T* srcEnd, StrideIterator<T> dst)
+{
+ for (; src != srcEnd ; src++, ++dst)
+ *dst = *src;
+}
+
+template<class T>
+void strided_copy (StrideIterator<T> src, StrideIterator<T> srcEnd, StrideIterator<T> dst)
+{
+ for (; src != srcEnd ; ++src, ++dst)
+ *dst = *src;
+}
+
+template<class T>
+void strided_copy (StrideIterator<T> src, StrideIterator<T> srcEnd, T* dst)
+{
+ for (; src != srcEnd ; ++src, ++dst)
+ *dst = *src;
+}
+
+template<class T, class T2>
+void strided_copy_convert (const T* src, const T* srcEnd, StrideIterator<T2> dst)
+{
+ for (; src != srcEnd ; ++src, ++dst)
+ *dst = *src;
+}
+
+#endif // STRIDE_ITERATOR_H_
diff --git a/Runtime/Utilities/TypeUtilities.h b/Runtime/Utilities/TypeUtilities.h
new file mode 100644
index 0000000..3455701
--- /dev/null
+++ b/Runtime/Utilities/TypeUtilities.h
@@ -0,0 +1,20 @@
+// Utility functions and types for working with static types.
+#pragma once
+
+template<typename T1, typename T2>
+struct IsSameType
+{
+ static const bool result = false;
+};
+
+template<typename T>
+struct IsSameType<T, T>
+{
+ static const bool result = true;
+};
+
+template<typename Expected, typename Actual>
+inline bool IsOfType (Actual& value)
+{
+ return IsSameType<Actual, Expected>::result;
+}
diff --git a/Runtime/Utilities/URLUtility.h b/Runtime/Utilities/URLUtility.h
new file mode 100644
index 0000000..39cbadd
--- /dev/null
+++ b/Runtime/Utilities/URLUtility.h
@@ -0,0 +1,8 @@
+#ifndef URLUTILITY_H
+#define URLUTILITY_H
+
+#include <string>
+
+void OpenURL (const std::string& url);
+
+#endif
diff --git a/Runtime/Utilities/UniqueIDGenerator.cpp b/Runtime/Utilities/UniqueIDGenerator.cpp
new file mode 100644
index 0000000..5649997
--- /dev/null
+++ b/Runtime/Utilities/UniqueIDGenerator.cpp
@@ -0,0 +1,34 @@
+#include "UnityPrefix.h"
+#include "UniqueIDGenerator.h"
+
+UniqueIDGenerator::UniqueIDGenerator()
+{
+ m_IDs.clear();
+ m_IDs.push_back(2); // Generated ID sequence should not contain 0
+ m_free = 1;
+}
+
+unsigned int UniqueIDGenerator::AllocateID ()
+{
+ DebugAssert( m_free <= m_IDs.size() );
+
+ if (m_free == m_IDs.size())
+ {
+ m_IDs.push_back(m_free+1);
+ }
+
+ unsigned int result = m_free;
+ m_free = m_IDs[m_free];
+
+ DebugAssert(result != 0);
+
+ return result;
+}
+
+void UniqueIDGenerator::RemoveID (unsigned int _ID)
+{
+ DebugAssert( (_ID > 0) && (_ID < m_IDs.size()) );
+
+ m_IDs[_ID] = m_free;
+ m_free = _ID;
+}
diff --git a/Runtime/Utilities/UniqueIDGenerator.h b/Runtime/Utilities/UniqueIDGenerator.h
new file mode 100644
index 0000000..b5f7386
--- /dev/null
+++ b/Runtime/Utilities/UniqueIDGenerator.h
@@ -0,0 +1,18 @@
+#pragma once
+
+#include "UnityPrefix.h"
+#include "Runtime/Utilities/dynamic_array.h"
+
+
+// Class to generate small unique IDs with allocate and remove functions.
+class UniqueIDGenerator
+{
+public:
+ UniqueIDGenerator();
+ unsigned int AllocateID();
+ void RemoveID( unsigned int _ID);
+
+private:
+ dynamic_array<unsigned int> m_IDs;
+ unsigned int m_free;
+};
diff --git a/Runtime/Utilities/UnityString.h b/Runtime/Utilities/UnityString.h
new file mode 100644
index 0000000..574f42d
--- /dev/null
+++ b/Runtime/Utilities/UnityString.h
@@ -0,0 +1,10 @@
+#ifndef UNITY_STRING_H
+#define UNITY_STRING_H
+
+#if !UNITY_EXTERNAL_TOOL
+#if UNITY_WIN
+#include "Runtime/Allocator/MemoryMacros.h"
+#endif
+#endif
+
+#endif
diff --git a/Runtime/Utilities/UserAuthorizationManager.cpp b/Runtime/Utilities/UserAuthorizationManager.cpp
new file mode 100644
index 0000000..5513cb6
--- /dev/null
+++ b/Runtime/Utilities/UserAuthorizationManager.cpp
@@ -0,0 +1,118 @@
+#include "UnityPrefix.h"
+#include "UserAuthorizationManager.h"
+#include "Runtime/Misc/GameObjectUtility.h"
+
+#if WEBPLUG
+#include "Runtime/Misc/PlayerSettings.h"
+#include "Runtime/Utilities/GlobalPreferences.h"
+#include "PlatformDependent/CommonWebPlugin/Verification.h"
+#endif
+
+#if UNITY_EDITOR
+#include "Editor/Src/EditorUserBuildSettings.h"
+#include "Editor/Src/BuildPipeline/BuildTargetPlatformSpecific.h"
+#endif
+
+UserAuthorizationManager gUserAuthorizationManager;
+
+UserAuthorizationManager& GetUserAuthorizationManager()
+{
+ return gUserAuthorizationManager;
+}
+
+UserAuthorizationManager::UserAuthorizationManager()
+{
+ m_AuthorizationOperation = NULL;
+ Reset ();
+}
+
+#if WEBPLUG && !UNITY_PEPPER
+std::string GetUserAuthorizationModeKey ()
+{
+ static const char *kUserAuthorizationModeKey = "UserAuthorizationMode";
+ std::string domain = GetStrippedPlayerDomain ();
+ std::string hash = GenerateHash ((unsigned char*)&domain[0], domain.size());
+ return kUserAuthorizationModeKey + hash;
+}
+#endif
+
+void UserAuthorizationManager::Reset()
+{
+#if WEBPLUG && !UNITY_PEPPER
+ m_AuthorizationMode = kNone;
+ if (GetPlayerSettingsPtr())
+ {
+ std::string auth = GetGlobalPreference(GetUserAuthorizationModeKey().c_str());
+ sscanf(auth.c_str(), "%x", &m_AuthorizationMode);
+ }
+#else
+#if UNITY_EDITOR
+ if (GetEditorUserBuildSettingsPtr() != NULL && GetBuildTargetGroup(GetEditorUserBuildSettings().GetActiveBuildTarget()) == kPlatformWebPlayer)
+ m_AuthorizationMode = kNone;
+ else
+#endif
+ // Allow Webcam and Microphone on all others targets than webplayer.
+ m_AuthorizationMode = kWebCam | kMicrophone;
+#endif
+ if (m_AuthorizationOperation)
+ {
+ m_AuthorizationOperation->Release();
+ m_AuthorizationOperation = NULL;
+ }
+ m_AuthorizationRequest = kNone;
+}
+
+AsyncOperation *UserAuthorizationManager::RequestUserAuthorization(int mode)
+{
+ if (m_AuthorizationOperation != NULL)
+ {
+ ErrorString ("A RequestUserAuthorization is already pending.");
+ }
+ else if (!HasUserAuthorization (mode))
+ {
+ m_AuthorizationRequest = mode;
+ m_AuthorizationOperation = new UserAuthorizationManagerOperation ();
+ m_AuthorizationOperation->Retain();
+ return m_AuthorizationOperation;
+ }
+
+ return new UserAuthorizationManagerErrorOperation ();
+}
+
+void UserAuthorizationManager::ReplyToUserAuthorizationRequest(bool reply, bool remember)
+{
+ if (reply)
+ {
+ m_AuthorizationMode |= m_AuthorizationRequest;
+#if WEBPLUG && !UNITY_PEPPER
+ if (remember)
+ SetGlobalPreference(GetUserAuthorizationModeKey().c_str(), Format("%x", m_AuthorizationMode).c_str());
+#endif
+ }
+ m_AuthorizationRequest = kNone;
+ if (m_AuthorizationOperation)
+ {
+ m_AuthorizationOperation->InvokeCoroutine();
+ m_AuthorizationOperation->Release();
+ m_AuthorizationOperation = NULL;
+ }
+}
+
+MonoBehaviour *UserAuthorizationManager::GetAuthorizationDialog ()
+{
+ if (m_AuthorizationRequest != kNone)
+ {
+ if (!m_AuthorizationDialog.IsValid())
+ {
+ m_AuthorizationDialog = &CreateGameObject ("", "Transform", "UserAuthorizationDialog", NULL);
+ m_AuthorizationDialog->SetHideFlags (Object::kHideInHierarchy);
+ }
+ return m_AuthorizationDialog->QueryComponent (MonoBehaviour);
+ }
+ else
+ {
+ if (m_AuthorizationDialog.IsValid ())
+ DestroyObjectHighLevel (m_AuthorizationDialog, true);
+ return NULL;
+ }
+}
diff --git a/Runtime/Utilities/UserAuthorizationManager.h b/Runtime/Utilities/UserAuthorizationManager.h
new file mode 100644
index 0000000..386a22c
--- /dev/null
+++ b/Runtime/Utilities/UserAuthorizationManager.h
@@ -0,0 +1,47 @@
+#ifndef USERAUTHORIZATIONMANAGER_H
+#define USERAUTHORIZATIONMANAGER_H
+
+#include "Runtime/Mono/MonoBehaviour.h"
+#include "Runtime/Misc/AsyncOperation.h"
+
+class UserAuthorizationManager;
+
+UserAuthorizationManager &GetUserAuthorizationManager();
+
+class UserAuthorizationManager {
+public:
+ UserAuthorizationManager ();
+
+ enum Mode {
+ kNone = 0,
+ kWebCam = 1 << 0,
+ kMicrophone = 1 << 1
+ };
+
+ void Reset ();
+ AsyncOperation *RequestUserAuthorization (int mode);
+ void ReplyToUserAuthorizationRequest (bool reply, bool remember = false);
+ bool HasUserAuthorization (int mode) const { return (m_AuthorizationMode & mode) == mode; }
+
+ int GetAuthorizationRequest() const { return m_AuthorizationRequest; }
+
+ MonoBehaviour *GetAuthorizationDialog ();
+private:
+ class UserAuthorizationManagerOperation : public AsyncOperation
+ {
+ virtual float GetProgress () { return 0.0f; }
+ virtual bool IsDone () { return GetUserAuthorizationManager().m_AuthorizationRequest == 0; }
+ };
+
+ class UserAuthorizationManagerErrorOperation : public AsyncOperation
+ {
+ virtual float GetProgress () { return 0.0f; }
+ virtual bool IsDone () { return true; }
+ };
+
+ int m_AuthorizationMode;
+ int m_AuthorizationRequest;
+ UserAuthorizationManagerOperation *m_AuthorizationOperation;
+ PPtr<GameObject> m_AuthorizationDialog;
+};
+#endif \ No newline at end of file
diff --git a/Runtime/Utilities/Utility.h b/Runtime/Utilities/Utility.h
new file mode 100644
index 0000000..b24fcba
--- /dev/null
+++ b/Runtime/Utilities/Utility.h
@@ -0,0 +1,214 @@
+#ifndef UTILITY_H
+#define UTILITY_H
+
+#include "Runtime/Allocator/MemoryMacros.h"
+
+#define UNUSED(x) (void)sizeof(x)
+
+#define SAFE_RELEASE(obj) if (obj) { (obj)->Release(); (obj) = NULL; } else {}
+#define SAFE_RELEASE_LABEL(obj,label) if (obj) { (obj)->Release(label); (obj) = NULL; } else {}
+
+template <class T0, class T1>
+inline void CopyData (T0 *dst, const T1 *src, long int inHowmany)
+{
+ for (long int i=0;i<inHowmany;i++)
+ {
+ dst[i] = src[i];
+ }
+}
+
+template <class DataType>
+inline void CopyOverlappingData (DataType *dst, const DataType *src, long int inHowmany)
+{
+ if (dst < src)
+ {
+ for (long int i=0;i<inHowmany;i++)
+ {
+ dst[i] = src[i];
+ }
+ }
+ else if (dst > src)
+ {
+ for (long int i=inHowmany-1;i>=0;i--)
+ {
+ dst[i] = src[i];
+ }
+ }
+}
+
+template <class DataType>
+inline void CopyRange (DataType *dst, const DataType *src, long int inStart, long int inHowmany)
+{
+ for (long int i=inStart;i<inHowmany+inStart;i++)
+ {
+ dst[i] = src[i];
+ }
+}
+
+template <class DataType>
+inline void CopyData (DataType *dst, const DataType src, long int inHowmany)
+{
+ for (long int i=0;i<inHowmany;i++)
+ {
+ dst[i] = src;
+ }
+}
+
+template <class DataType>
+inline void CopyDataReverse (DataType *dst, const DataType *src, long int inHowmany)
+{
+ for (long int i=0;i<inHowmany;i++)
+ {
+ dst[i] = src[inHowmany-1-i];
+ }
+}
+
+template <class DataType>
+inline bool CompareArrays (const DataType *lhs, const DataType *rhs, long int arraySize)
+{
+ for (long int i=0; i<arraySize; i++)
+ {
+ if (lhs[i] != rhs[i])
+ return false;
+ }
+ return true;
+}
+
+template <class DataType>
+inline bool CompareMemory (const DataType& lhs, const DataType& rhs)
+{
+#ifdef ALIGN_OF
+ // We check at compile time if it's safe to cast data to int*
+ if (ALIGN_OF(DataType) >= ALIGN_OF(int) && (sizeof(DataType) % sizeof(int))==0)
+ {
+ return CompareArrays((const int*)&lhs, (const int*)&rhs, sizeof(DataType) / sizeof(int));
+ }
+#endif
+ return CompareArrays((const char*)&lhs, (const char*)&rhs, sizeof(DataType));
+}
+
+template <class T>
+class UNITY_AutoDelete
+{
+public:
+ UNITY_AutoDelete() : m_val(T()) { }
+ ~UNITY_AutoDelete() { if(m_val) UNITY_DELETE ( m_val, m_label ); }
+
+ void Assign(T val, MemLabelId label) { m_val = val; m_label = label; return *this; }
+ bool operator!() { return !m_val; }
+
+ /* Releases the internal pointer without deleting */
+ T releasePtr() { T tmp = m_val; m_val = T(); return tmp; }
+private:
+ UNITY_AutoDelete &operator=(T val);
+ UNITY_AutoDelete(const UNITY_AutoDelete<T>& other); // disabled
+ void operator=(const UNITY_AutoDelete<T>& other); // disabled
+ T m_val;
+ MemLabelId m_label;
+};
+
+class AutoFree
+{
+public:
+ AutoFree() : m_val(NULL), m_label(kMemDefault) { }
+ ~AutoFree() { if(m_val) UNITY_FREE ( m_label, m_val ); }
+
+ bool operator!() { return !m_val; }
+ void Assign(void* val, MemLabelId label) { m_label = label; m_val = val; }
+
+ /* Releases the internal pointer without deleting */
+ void* releasePtr() { void* tmp = m_val; m_val = NULL; return tmp; }
+private:
+ AutoFree &operator=(void* val); // disabled
+ AutoFree(const AutoFree& other); // disabled
+ void operator=(const AutoFree& other); // disabled
+ void* m_val;
+ MemLabelId m_label;
+};
+
+template <class T>
+inline T clamp (const T&t, const T& t0, const T& t1)
+{
+ if (t < t0)
+ return t0;
+ else if (t > t1)
+ return t1;
+ else
+ return t;
+}
+
+template <>
+inline float clamp (const float&t, const float& t0, const float& t1)
+{
+#if UNITY_XENON || UNITY_PS3
+ return FloatMin(FloatMax(t, t0), t1);
+#else
+ if (t < t0)
+ return t0;
+ else if (t > t1)
+ return t1;
+ else
+ return t;
+#endif
+}
+
+template <class T>
+inline T clamp01 (const T& t)
+{
+ if (t < 0)
+ return 0;
+ else if (t > 1)
+ return 1;
+ else
+ return t;
+}
+
+template <>
+inline float clamp01<float> (const float& t)
+{
+#if UNITY_XENON || UNITY_PS3
+ return FloatMin(FloatMax(t, 0.0f), 1.0f);
+#else
+ if (t < 0.0F)
+ return 0.0F;
+ else if (t > 1.0F)
+ return 1.0F;
+ else
+ return t;
+#endif
+}
+
+// Asserts if from is NULL or can't be cast to type To
+template<class To, class From> inline
+To assert_cast (From from)
+{
+ AssertIf (dynamic_cast<To> (from) == NULL);
+ return static_cast<To> (from);
+}
+
+inline float SmoothStep (float from, float to, float t)
+{
+ t = clamp01(t);
+ t = -2.0F * t*t*t + 3.0F * t*t;
+ return to * t + from * (1.0f - t);
+}
+// Rounds value down.
+// Note: base must be power of two value.
+inline UInt32 RoundDown (UInt32 value, SInt32 base)
+{
+ return value & (-base);
+}
+// Rounds value up.
+// Note: base must be power of two value.
+inline UInt32 RoundUp (UInt32 value, SInt32 base)
+{
+ return (value + base - 1) & (-base);
+}
+
+template<class T>
+inline T* Stride (T* p, size_t offset)
+{
+ return reinterpret_cast<T*>((char*)p + offset);
+}
+
+#endif // include-guard
diff --git a/Runtime/Utilities/UtilityTests.cpp b/Runtime/Utilities/UtilityTests.cpp
new file mode 100644
index 0000000..daef426
--- /dev/null
+++ b/Runtime/Utilities/UtilityTests.cpp
@@ -0,0 +1,629 @@
+#include "UnityPrefix.h"
+#include "Configuration/UnityConfigure.h"
+
+#if ENABLE_UNIT_TESTS
+
+#include "External/UnitTest++/src/UnitTest++.h"
+
+#include "Runtime/Utilities/LinkedList.h"
+#include "Runtime/Utilities/MemoryPool.h"
+#include "Runtime/Utilities/File.h"
+#include <string.h>
+#include "Runtime/Utilities/dynamic_bitset.h"
+#include "Runtime/Utilities/dynamic_array.h"
+#include "Runtime/Utilities/vector_set.h"
+#include "Runtime/Utilities/ArrayUtility.h"
+#include "Runtime/Allocator/FixedSizeAllocator.h"
+
+
+#if !GAMERELEASE
+#include "Runtime/Utilities/PathNameUtility.h"
+#include "Runtime/Utilities/FileUtilities.h"
+#include "Runtime/Utilities/GUID.h"
+UnityGUID StringToGUID (const std::string& pathName);
+static const char* kTempFolder = "Temp";
+#endif
+
+
+SUITE (UtilityTests)
+{
+
+
+TEST (TestMemoryPool)
+{
+ const int kBubble = 256;
+ const int kBubbleSize = 64;
+
+ // memory pool with 128 elements per bubble
+ MemoryPool pool (true, "TestPool", kBubbleSize, kBubble * kBubbleSize);
+
+ std::vector<void*> ptrs;
+
+ // allocate 128, should be one bubble and 128 objects
+ for (int i = 0; i < kBubble; ++i)
+ {
+ ptrs.push_back (pool.Allocate ());
+ }
+ CHECK_EQUAL (kBubble, pool.GetAllocCount ());
+ CHECK_EQUAL (1, pool.GetBubbleCount ());
+
+ // Allocate 1 more, should be 2 bubbles and kBubble+1 objects
+ ptrs.push_back (pool.Allocate ());
+ CHECK_EQUAL (kBubble + 1, pool.GetAllocCount ());
+ CHECK_EQUAL (2, pool.GetBubbleCount ());
+
+ // deallocate first bubble
+ void* last = ptrs[kBubble];
+ for (int i = 0; i < kBubble; ++i)
+ {
+ pool.Deallocate (ptrs[i]);
+ }
+ ptrs.clear ();
+ ptrs.push_back (last);
+ CHECK_EQUAL (1, pool.GetAllocCount());
+ CHECK_EQUAL (2, pool.GetBubbleCount());
+
+ // now we have two bubbles: 1st totally free, 2nd one with 1 allocation
+ // allocate 255 more objects. Should all fit into existing bubbles!
+ for (int i = 0; i < kBubble * 2 - 1; ++i)
+ {
+ ptrs.push_back (pool.Allocate ());
+ }
+ CHECK_EQUAL (kBubble * 2, pool.GetAllocCount ());
+ CHECK_EQUAL (2, pool.GetBubbleCount ());
+
+ // Allocate one more. Should cause additional bubble
+ ptrs.push_back (pool.Allocate ());
+ CHECK_EQUAL (kBubble * 2 + 1, pool.GetAllocCount ());
+ CHECK_EQUAL (3, pool.GetBubbleCount ());
+
+ // Deallocate all.
+ for (int i = 0; i < ptrs.size (); ++i)
+ {
+ pool.Deallocate (ptrs[i]);
+ }
+ ptrs.clear ();
+ CHECK_EQUAL (0, pool.GetAllocCount ());
+ CHECK_EQUAL (3, pool.GetBubbleCount ());
+
+ // now we have three totally free bubbles
+ // allocate 3*128 objects. Should fit into existing bubbles!
+ for (int i = 0; i < kBubble * 3; ++i)
+ {
+ ptrs.push_back (pool.Allocate ());
+ }
+ CHECK_EQUAL (kBubble * 3, pool.GetAllocCount ());
+ CHECK_EQUAL (3, pool.GetBubbleCount ());
+
+ // deallocate all from last bubble
+ for (int i = kBubble * 2; i < kBubble * 3; ++i)
+ {
+ pool.Deallocate (ptrs[i]);
+ }
+ ptrs.resize (kBubble * 2);
+ CHECK_EQUAL (kBubble * 2, pool.GetAllocCount ());
+ CHECK_EQUAL (3, pool.GetBubbleCount ());
+
+ // allocate one more
+ ptrs.push_back (pool.Allocate ());
+ CHECK_EQUAL (kBubble * 2 + 1, pool.GetAllocCount ());
+ CHECK_EQUAL (3, pool.GetBubbleCount ());
+
+ pool.DeallocateAll ();
+ ptrs.clear ();
+ CHECK_EQUAL (0, pool.GetAllocCount ());
+ CHECK_EQUAL (0, pool.GetBubbleCount ());
+
+ // Test pre-allocation: preallocate 1.5 worth of bubbles
+ pool.PreallocateMemory (kBubble * kBubbleSize * 3/2);
+ // should result in 2 bubbles
+ CHECK_EQUAL (2, pool.GetBubbleCount ());
+ for (int i = 0; i < kBubble * 2; ++i)
+ {
+ ptrs.push_back (pool.Allocate ());
+ }
+ CHECK_EQUAL (kBubble * 2, pool.GetAllocCount ());
+ CHECK_EQUAL (2, pool.GetBubbleCount ());
+
+ // Allocate one more, should create additional bubble
+ ptrs.push_back (pool.Allocate ());
+ CHECK_EQUAL (kBubble * 2 + 1, pool.GetAllocCount ());
+ CHECK_EQUAL (3, pool.GetBubbleCount ());
+ pool.DeallocateAll ();
+}
+
+
+#if !GAMERELEASE
+TEST (TestPathExist)
+{
+ using namespace std;
+
+ CreateDirectory (kTempFolder);
+
+ string filePath = AppendPathName (kTempFolder, "TestIsFileCreated");
+ DeleteFileOrDirectory (filePath);
+
+ CHECK (!IsPathCreated (filePath));
+ CHECK (!IsDirectoryCreated (filePath));
+ CHECK (!IsFileCreated (filePath));
+
+ CreateFile (filePath);
+
+ CHECK (IsPathCreated (kTempFolder));
+ CHECK (IsDirectoryCreated (kTempFolder));
+ CHECK (!IsFileCreated (kTempFolder));
+
+ CHECK (IsPathCreated (filePath));
+ CHECK (!IsDirectoryCreated (filePath));
+ CHECK (IsFileCreated (filePath));
+}
+#endif
+
+
+TEST (DynamicArray)
+{
+ // no allocation for empty array
+ dynamic_array<int> array;
+ CHECK_EQUAL (0, array.capacity ());
+
+ // push_back allocates
+ int j = 1;
+ array.push_back (j);
+ CHECK_EQUAL (1, array.size ());
+ CHECK (array.capacity () > 0);
+
+ // push_back(void)
+ int& i = array.push_back ();
+ i = 666;
+ CHECK_EQUAL(666, array.back ());
+
+ // clear frees memory?
+ array.clear ();
+ CHECK_EQUAL (0, array.size ());
+ CHECK_EQUAL (0, array.capacity ());
+
+ // 3 item list
+ j = 6;
+ array.push_back (j);
+ j = 7;
+ array.push_back (j);
+ j = 8;
+ array.push_back (j);
+
+ CHECK_EQUAL (3, array.size ());
+
+ // swapping
+ dynamic_array<int> ().swap (array);
+ CHECK_EQUAL (0, array.capacity ());
+ CHECK_EQUAL (0, array.size ());
+
+ // reserve
+ array.reserve (1024);
+ CHECK_EQUAL (1024, array.capacity ());
+ CHECK_EQUAL (0, array.size ());
+
+ // copy assignment
+ dynamic_array<int> array1;
+ j = 888;
+ array1.push_back (j);
+
+ array = array1;
+
+ CHECK_EQUAL (1, array.size ());
+ CHECK_EQUAL (888, array.back ());
+ CHECK_EQUAL (1, array1.size ());
+ CHECK_EQUAL (888, array1.back ());
+}
+
+ struct Stuff
+ {
+ int value;
+ int identifier;
+
+ bool operator < (const Stuff& rhs) const
+ {
+ return value < rhs.value;
+ }
+
+ Stuff (int a, int b) { value = a; identifier = b; }
+ };
+
+
+TEST (Test_vector_set_assign_clear_duplicates)
+{
+ // Make sure that duplicates are removed,
+ // but also that only the first same instance is maintained, the following ones are killed
+ Stuff input[] = { Stuff (10, 0), Stuff (11, 1), Stuff (3, 2), Stuff (3, 3), Stuff (4, 4), Stuff (10, 5) };
+ Stuff output[] = { Stuff (3, 2), Stuff (4, 4), Stuff (10, 0), Stuff (11, 1) };
+
+ vector_set<Stuff> test_set;
+ test_set.assign_clear_duplicates(input, input + ARRAY_SIZE(input));
+
+ CHECK_EQUAL(test_set.size(), ARRAY_SIZE(output));
+ for (int i=0;i<ARRAY_SIZE(output);i++)
+ {
+ CHECK_EQUAL(output[i].value, test_set[i].value);
+ CHECK_EQUAL(output[i].identifier, test_set[i].identifier);
+ }
+}
+
+class TestNode : public ListElement
+{
+};
+
+TEST (TestList)
+{
+ typedef List<TestNode> ListType;
+
+ struct
+ {
+ void operator () (ListType& list, TestNode* nodes[], int count)
+ {
+ CHECK_EQUAL (count, list.size_slow ());
+ int c = 0;
+ for (ListType::iterator i = list.begin (); i != list.end (); ++i)
+ {
+ CHECK (nodes[c] == &*i);
+ ++c;
+ }
+
+ CHECK_EQUAL (count, c);
+ }
+ } CheckNodes;
+
+ ListType list, emptyList, emptyList2;
+
+ CHECK_EQUAL (0, emptyList.size_slow ());
+ emptyList.clear ();
+ CHECK_EQUAL (0, emptyList.size_slow ());
+
+ TestNode* nodes[] =
+ {
+ new TestNode (),
+ new TestNode (),
+ new TestNode (),
+ new TestNode (),
+ new TestNode (),
+ new TestNode ()
+ };
+
+ emptyList.swap (emptyList2);
+ CHECK_EQUAL (0, emptyList.size_slow ());
+ CHECK_EQUAL (0, emptyList2.size_slow ());
+
+ // insertion and pushback
+ list.push_back (*nodes[1]);
+ list.insert (nodes[1], *nodes[0]);
+ list.push_back (*nodes[2]);
+ list.push_back (*nodes[3]);
+ list.push_back (*nodes[5]);
+ list.insert (nodes[5], *nodes[4]);
+ CheckNodes (list, nodes, 6);
+ // insert before self
+ list.insert (list.begin(), *list.begin ());
+ CheckNodes (list, nodes, 6);
+
+ list.append (emptyList);
+ CHECK_EQUAL (0, emptyList.size_slow ());
+ CheckNodes (list, nodes, 6);
+
+ // append remove into something empty
+ emptyList.append (list);
+ CHECK_EQUAL (0, list.size_slow ());
+ CheckNodes (emptyList, nodes, 6);
+
+ // invert operation by doing a swap
+ emptyList.swap (list);
+ CHECK_EQUAL (0, emptyList.size_slow ());
+ CheckNodes (list, nodes, 6);
+
+ // Create another list to test copying
+ TestNode* nodes2[] =
+ {
+ new TestNode (),
+ new TestNode (),
+ new TestNode ()
+ };
+ ListType list2;
+ list2.push_back (*nodes2[1]);
+ list2.push_front (*nodes2[0]);
+ list2.push_back (*nodes2[2]);
+ CheckNodes (list2, nodes2, 3);
+
+ // swap back and forth
+ list2.swap (list);
+ CheckNodes (list, nodes2, 3);
+ CheckNodes (list2, nodes, 6);
+ list.swap (list2);
+ CheckNodes (list, nodes, 6);
+ CheckNodes (list2, nodes2, 3);
+
+ list.append (list2); // insert before self
+ int c = 0;
+ for (ListType::iterator i = list.begin (); i != list.end (); ++i)
+ {
+ if (c >= 6)
+ CHECK (nodes2[c - 6] == &*i);
+ else
+ CHECK (nodes[c] == &*i);
+ ++c;
+ }
+ CHECK_EQUAL (9, list.size_slow ());
+ CHECK_EQUAL (0, list2.size_slow ());
+ CHECK_EQUAL (9, c);
+
+ emptyList.append (emptyList2);
+ CHECK_EQUAL (0, emptyList2.size_slow ());
+ CHECK_EQUAL (0, emptyList.size_slow ());
+}
+
+TEST (DynamicBitSet)
+{
+ dynamic_bitset set;
+ UInt32 block = 1 << 0 | 1 << 3 | 1 << 5;
+ set.resize (6);
+ from_block_range (&block, &block + 1, set);
+
+ CHECK_EQUAL (true, set.test (0));
+ CHECK_EQUAL (false, set.test (1));
+ CHECK_EQUAL (false, set.test (2));
+ CHECK_EQUAL (true, set.test (3));
+ CHECK_EQUAL (false, set.test (4));
+ CHECK_EQUAL (true, set.test (5));
+
+ to_block_range (set, &block);
+ bool res;
+
+ res = block & (1 << 0);
+ CHECK_EQUAL (true, res);
+ res = block & (1 << 1);
+ CHECK_EQUAL (false, res);
+ res = block & (1 << 2);
+ CHECK_EQUAL (false, res);
+ res = block & (1 << 3);
+ CHECK_EQUAL (true, res);
+ res = block & (1 << 4);
+ CHECK_EQUAL (false, res);
+ res = block & (1 << 5);
+ CHECK_EQUAL (true, res);
+}
+
+
+TEST (DynamicArrayMisc)
+{
+ // no allocation for empty array
+ dynamic_array<int> array;
+ CHECK_EQUAL (0, array.capacity ());
+ CHECK (array.owns_data ());
+ CHECK (array.begin () == array.end ());
+ CHECK (array.empty ());
+
+ // push_back allocates
+ int j = 1;
+ array.push_back (j);
+ CHECK_EQUAL (1, array.size ());
+ CHECK (array.capacity () > 0);
+
+ // push_back(void)
+ int& i = array.push_back ();
+ i = 666;
+ CHECK_EQUAL(666, array.back ());
+
+ // clear frees memory?
+ array.clear ();
+ CHECK_EQUAL (0, array.size ());
+ CHECK_EQUAL (0, array.capacity ());
+
+ // 3 item list
+ array.push_back (6);
+ array.push_back (7);
+ array.push_back (8);
+
+ CHECK_EQUAL (3, array.size ());
+
+ // swapping
+ dynamic_array<int> ().swap (array);
+ CHECK_EQUAL (0, array.capacity ());
+ CHECK_EQUAL (0, array.size ());
+
+ // reserve
+ array.reserve (1024);
+ CHECK_EQUAL (1024, array.capacity ());
+ CHECK_EQUAL (0, array.size ());
+
+ // copy assignment
+ dynamic_array<int> array1;
+ j = 888;
+ array1.push_back (j);
+
+ array = array1;
+
+ CHECK_EQUAL (1, array.size ());
+ CHECK_EQUAL (888, array.back ());
+ CHECK_EQUAL (1, array1.size ());
+ CHECK_EQUAL (888, array1.back ());
+}
+
+
+TEST (DynamicArrayErase)
+{
+ dynamic_array<int> vs;
+ vs.resize_uninitialized(5);
+
+ vs[0] = 0;
+ vs[1] = 1;
+ vs[2] = 2;
+ vs[3] = 3;
+ vs[4] = 4;
+
+ vs.erase(vs.begin() + 1, vs.begin() + 4);
+ CHECK_EQUAL (2, vs.size());
+ CHECK_EQUAL (5, vs.capacity());
+ CHECK_EQUAL (0, vs[0]);
+ CHECK_EQUAL (4, vs[1]);
+}
+
+static void VerifyConsecutiveIntArray (dynamic_array<int>& vs, int size, int capacity)
+{
+ CHECK_EQUAL (capacity, vs.capacity());
+ CHECK_EQUAL (size, vs.size());
+ for (int i=0;i<vs.size();i++)
+ CHECK_EQUAL (i, vs[i]);
+}
+
+TEST (DynamicArrayInsertOnEmpty)
+{
+ dynamic_array<int> vs;
+ int vals[] = { 0, 1 };
+
+ vs.insert(vs.begin(), vals, vals + ARRAY_SIZE(vals));
+
+ VerifyConsecutiveIntArray(vs, 2, 2);
+}
+
+
+TEST (DynamicArrayInsert)
+{
+ dynamic_array<int> vs;
+ vs.resize_uninitialized(5);
+
+ vs[0] = 0;
+ vs[1] = 1;
+ vs[2] = 4;
+ vs[3] = 5;
+ vs[4] = 6;
+
+ int vals[] = { 2, 3 };
+
+ // inser two values
+ vs.insert(vs.begin() + 2, vals, vals + ARRAY_SIZE(vals));
+ VerifyConsecutiveIntArray(vs, 7, 7);
+
+ // empty insert
+ vs.insert(vs.begin() + 2, vals, vals);
+
+ VerifyConsecutiveIntArray(vs, 7, 7);
+}
+
+TEST (DynamicArrayResize)
+{
+ dynamic_array<int> vs;
+ vs.resize_initialized(3, 2);
+ CHECK_EQUAL (3, vs.capacity());
+ CHECK_EQUAL (3, vs.size());
+ CHECK_EQUAL (2, vs[0]);
+ CHECK_EQUAL (2, vs[1]);
+ CHECK_EQUAL (2, vs[2]);
+
+ vs.resize_initialized(6, 3);
+ CHECK_EQUAL (6, vs.capacity());
+ CHECK_EQUAL (6, vs.size());
+ CHECK_EQUAL (2, vs[0]);
+ CHECK_EQUAL (2, vs[1]);
+ CHECK_EQUAL (2, vs[2]);
+ CHECK_EQUAL (3, vs[3]);
+ CHECK_EQUAL (3, vs[4]);
+ CHECK_EQUAL (3, vs[5]);
+
+ vs.resize_initialized(5, 3);
+ CHECK_EQUAL (6, vs.capacity());
+ CHECK_EQUAL (5, vs.size());
+ CHECK_EQUAL (2, vs[0]);
+ CHECK_EQUAL (2, vs[1]);
+ CHECK_EQUAL (2, vs[2]);
+ CHECK_EQUAL (3, vs[3]);
+ CHECK_EQUAL (3, vs[4]);
+
+ vs.resize_initialized(2, 3);
+ CHECK_EQUAL (6, vs.capacity());
+ CHECK_EQUAL (2, vs.size());
+ CHECK_EQUAL (2, vs[0]);
+ CHECK_EQUAL (2, vs[1]);
+}
+
+
+TEST(FixedSizeAllocator)
+{
+ typedef FixedSizeAllocator<sizeof(int)> TestAllocator;
+
+ TestAllocator testalloc = TestAllocator(MemLabelId(kMemDefaultId, NULL));
+
+ CHECK(testalloc.capacity() == 0);
+ CHECK(testalloc.total_free() == 0);
+ CHECK(testalloc.total_allocated() == 0);
+
+ int* ptr1 = (int*)testalloc.alloc();
+ *ptr1 = 1;
+
+ CHECK(testalloc.capacity() == 255*sizeof(int));
+ CHECK(testalloc.total_free() == 254*sizeof(int));
+ CHECK(testalloc.total_allocated() == sizeof(int));
+
+ int* ptr2 = (int*)testalloc.alloc();
+ *ptr2 = 2;
+
+ CHECK(testalloc.capacity() == 255*sizeof(int));
+ CHECK(testalloc.total_free() == 253*sizeof(int));
+ CHECK(testalloc.total_allocated() == 2*sizeof(int));
+ CHECK(*ptr1==1);
+ CHECK(ptr1 + 1 == ptr2);
+
+ testalloc.reset();
+
+ CHECK(testalloc.capacity() == 255*sizeof(int));
+ CHECK(testalloc.total_free() == 255*sizeof(int));
+ CHECK(testalloc.total_allocated() == 0);
+
+ testalloc.free_memory();
+
+ CHECK(testalloc.capacity() == 0);
+ CHECK(testalloc.total_free() == 0);
+ CHECK(testalloc.total_allocated() == 0);
+}
+
+
+
+TEST(StringFormatTest)
+{
+ CHECK_EQUAL ("Hello world it works", Format ("Hello %s it %s", "world", "works"));
+}
+
+
+#if !GAMERELEASE
+TEST(UnityGUIDTest)
+{
+ UnityGUID identifier[5];
+ identifier[0].Init ();
+ identifier[1].Init ();
+ identifier[2].Init ();
+ identifier[3].Init ();
+ identifier[4].Init ();
+
+ CHECK (identifier[0] != identifier[1]);
+ CHECK (identifier[1] != identifier[2]);
+ CHECK (identifier[2] != identifier[3]);
+ CHECK (identifier[3] != identifier[4]);
+ identifier[0] = identifier[1];
+ CHECK (identifier[0] == identifier[1]);
+}
+#endif
+
+
+TEST (TestUtility)
+{
+ using namespace std;
+
+ // Make sure that our stl implementation has consistent clear behaviour.
+ // If it doesn't we should probably stop using clear.
+ //
+ // If this test fails, it means that std::vector clear deallocates memory.
+ // Some optimized code this to not be the case!
+
+ vector<int> test;
+ test.resize (10);
+ test.clear ();
+ CHECK (test.capacity () != 0);
+}
+
+}
+#endif
diff --git a/Runtime/Utilities/VFPUtility.h b/Runtime/Utilities/VFPUtility.h
new file mode 100644
index 0000000..e0176b9
--- /dev/null
+++ b/Runtime/Utilities/VFPUtility.h
@@ -0,0 +1,43 @@
+#ifndef VFPTILITY_H
+#define VFPTILITY_H
+
+#if UNITY_SUPPORTS_VFP
+
+#define FMULS3(s0,s1,s2, s4,s5,s6, s8,s9,s10) \
+ fmuls s##s0, s##s4, s##s8 ; \
+ fmuls s##s1, s##s5, s##s9 ; \
+ fmuls s##s2, s##s6, s##s10
+
+#define FMACS3(s0,s1,s2, s4,s5,s6, s8,s9,s10) \
+ fmacs s##s0, s##s4, s##s8 ; \
+ fmacs s##s1, s##s5, s##s9 ; \
+ fmacs s##s2, s##s6, s##s10
+
+#define FCPYS3(s0,s1,s2, s4,s5,s6) \
+ fcpys s##s0, s##s4 ; \
+ fcpys s##s1, s##s5 ; \
+ fcpys s##s2, s##s6 ; \
+
+
+
+#define FMULS4(s0,s1,s2,s3, s4,s5,s6,s7, s8,s9,s10,s11) \
+ fmuls s##s0, s##s4, s##s8 ; \
+ fmuls s##s1, s##s5, s##s9 ; \
+ fmuls s##s2, s##s6, s##s10 ;\
+ fmuls s##s3, s##s7, s##s11
+
+#define FMACS4(s0,s1,s2,s3, s4,s5,s6,s7, s8,s9,s10,s11) \
+ fmacs s##s0, s##s4, s##s8 ; \
+ fmacs s##s1, s##s5, s##s9 ; \
+ fmacs s##s2, s##s6, s##s10 ;\
+ fmacs s##s3, s##s7, s##s11
+
+#define FCPYS4(s0,s1,s2,s3, s4,s5,s6,s7) \
+ fcpys s##s0, s##s4 ; \
+ fcpys s##s1, s##s5 ; \
+ fcpys s##s2, s##s6 ; \
+ fcpys s##s3, s##s7 ; \
+
+#endif
+
+#endif
diff --git a/Runtime/Utilities/ValidateArgs.h b/Runtime/Utilities/ValidateArgs.h
new file mode 100644
index 0000000..7f50c66
--- /dev/null
+++ b/Runtime/Utilities/ValidateArgs.h
@@ -0,0 +1,81 @@
+#ifndef VALIDATEARGS_H
+#define VALIDATEARGS_H
+
+#if DEBUGMODE
+
+#define ABORT_INVALID_FLOAT(value,varname,classname) \
+if (!IsFinite (value)) \
+{ \
+ ErrorStringObject (Format ("%s.%s assign attempt for '%s' is not valid. Input %s is { %s }.", #classname, #varname, GetName (), #varname, FloatToString (value).c_str ()), this); \
+ return; \
+}
+
+#define ABORT_INVALID_ARG_FLOAT(value,varname,methodname,classname) \
+if (!IsFinite (value)) \
+{ \
+ ErrorStringObject (Format ("%s.%s(%s) assign attempt for '%s' is not valid. Input %s is { %s }.", #classname, #methodname, #varname, GetName (), #varname, FloatToString (value).c_str ()), this); \
+ return; \
+}
+
+#ifdef VECTOR2_H
+#define ABORT_INVALID_VECTOR2(value,varname,classname) \
+if (!IsFinite (value)) \
+{ \
+ ErrorStringObject (Format ("%s.%s assign attempt for '%s' is not valid. Input %s is { %s, %s }.", #classname, #varname, GetName (), #varname, FloatToString (value.x).c_str (), FloatToString (value.y).c_str ()), this); \
+ return; \
+}
+
+#define ABORT_INVALID_ARG_VECTOR2(value,varname,methodname,classname) \
+if (!IsFinite (value)) \
+{ \
+ ErrorStringObject (Format ("%s.%s(%s) assign attempt for '%s' is not valid. Input %s is { %s, %s }.", #classname, #methodname, #varname, GetName (), #varname, FloatToString (value.x).c_str (), FloatToString (value.y).c_str ()), this); \
+ return; \
+}
+#endif
+
+#ifdef VECTOR3_H
+#define ABORT_INVALID_VECTOR3(value,varname,classname) \
+if (!IsFinite (value)) \
+{ \
+ ErrorStringObject (Format ("%s.%s assign attempt for '%s' is not valid. Input %s is { %s, %s, %s }.", #classname, #varname, GetName (), #varname, FloatToString (value.x).c_str (), FloatToString (value.y).c_str (), FloatToString (value.z).c_str ()), this); \
+ return; \
+}
+
+#define ABORT_INVALID_ARG_VECTOR3(value,varname,methodname,classname) \
+ if (!IsFinite (value)) \
+{ \
+ ErrorStringObject (Format ("%s.%s(%s) assign attempt for '%s' is not valid. Input %s is { %s, %s, %s }.", #classname, #methodname, #varname, GetName (), #varname, FloatToString (value.x).c_str (), FloatToString (value.y).c_str (), FloatToString (value.z).c_str ()), this); \
+ return; \
+}
+#endif
+
+#ifdef QUATERNION_H
+#define ABORT_INVALID_QUATERNION(value,varname,classname) \
+if (!IsFinite(value)) \
+{ \
+ ErrorStringObject (Format("%s.%s assign attempt for '%s' is not valid. Input rotation is { %s, %s, %s, %s }.", #classname, #varname, GetName(), FloatToString(value.x).c_str(), FloatToString(value.y).c_str(), FloatToString(value.z).c_str(), FloatToString(value.w).c_str()), this); \
+ return; \
+}
+
+#define ABORT_INVALID_ARG_QUATERNION(value,varname,methodname,classname) \
+if (!IsFinite(value)) \
+{ \
+ ErrorStringObject (Format("%s.%s(%s) assign attempt for '%s' is not valid. Input rotation is { %s, %s, %s, %s }.", #classname, #methodname, #varname, GetName(), FloatToString(value.x).c_str(), FloatToString(value.y).c_str(), FloatToString(value.z).c_str(), FloatToString(value.w).c_str()), this); \
+ return; \
+}
+#endif
+
+#else
+
+#define ABORT_INVALID_FLOAT(value,varname,classname)
+#define ABORT_INVALID_VECTOR2(value,varname,classname)
+#define ABORT_INVALID_VECTOR3(value,varname,classname)
+#define ABORT_INVALID_QUATERNION(value,varname,classname)
+
+#define ABORT_INVALID_ARG_FLOAT(value,varname,methodname,classname)
+#define ABORT_INVALID_ARG_VECTOR2(value,varname,methodname,classname)
+#define ABORT_INVALID_ARG_VECTOR3(value,varname,methodname,classname)
+#define ABORT_INVALID_ARG_QUATERNION(value,varname,methodname,classname)
+
+#endif
+#endif
diff --git a/Runtime/Utilities/WavFileUtility.cpp b/Runtime/Utilities/WavFileUtility.cpp
new file mode 100644
index 0000000..2d2471d
--- /dev/null
+++ b/Runtime/Utilities/WavFileUtility.cpp
@@ -0,0 +1,54 @@
+#include "UnityPrefix.h"
+#include "Configuration/UnityConfigure.h"
+
+#include "WavFileUtility.h"
+#include <stdio.h>
+
+static inline int fput2 (unsigned short v, FILE* f)
+{
+ return fwrite (&v, 2, 1, f);
+}
+
+static inline int fput4 (unsigned v, FILE* f)
+{
+ return fwrite (&v, 4, 1, f);
+}
+
+bool WriteWaveFile (char const* filename, int channels, int samplebits, int freq, void* data, int samples)
+{
+ FILE* wf = fopen (filename, "wb+");
+ if (!wf)
+ return false;
+
+ size_t datasize = channels * (samplebits / 8) * samples;
+
+#define C(x) if (x < 0) goto err
+
+ // RIFF chunk
+ C((fputs ("RIFF", wf)));
+ C((fput4 (datasize + (12-8) + 24 + 8, wf)));
+ C((fputs ("WAVE", wf)));
+
+ // FORMAT chunk
+ C((fputs ("fmt ", wf)));
+ C((fput4 (0x10, wf)));
+ C((fput2 (0x01, wf)));
+ C((fput2 (channels, wf)));
+ C((fput4 (freq, wf)));
+ C((fput4 (channels * samplebits * freq, wf)));
+ C((fput2 (samplebits >> 3, wf)));
+ C((fput2 (samplebits, wf)));
+
+ // DATA chunk
+ C((fputs ("data", wf)));
+ C((fput4 (datasize, wf)));
+ C((fwrite (data, datasize, 1, wf)));
+#undef C
+
+ fclose (wf);
+ return true;
+
+err:
+ fclose (wf);
+ return false;
+}
diff --git a/Runtime/Utilities/WavFileUtility.h b/Runtime/Utilities/WavFileUtility.h
new file mode 100644
index 0000000..ec1c243
--- /dev/null
+++ b/Runtime/Utilities/WavFileUtility.h
@@ -0,0 +1,6 @@
+#ifndef UNITY_WAV_FILE_UTILITY_H_
+#define UNITY_WAV_FILE_UTILITY_H_
+
+bool WriteWaveFile (char const* filename, int channels, int samplebits, int freq, void* data, int samples);
+
+#endif // UNITY_WAV_FILE_UTILITY_H_
diff --git a/Runtime/Utilities/Word.cpp b/Runtime/Utilities/Word.cpp
new file mode 100644
index 0000000..2057283
--- /dev/null
+++ b/Runtime/Utilities/Word.cpp
@@ -0,0 +1,659 @@
+#include "UnityPrefix.h"
+#include "Word.h"
+#include <limits.h>
+#include <stdio.h>
+#include "Runtime/Math/FloatConversion.h"
+#include "Annotations.h"
+
+#if !UNITY_EXTERNAL_TOOL
+#include "Runtime/Allocator/LinearAllocator.h"
+#endif
+
+using namespace std;
+
+bool BeginsWithCaseInsensitive (const std::string &s, const std::string &beginsWith)
+{
+ // Note that we don't want to convert the whole s string to lower case, simply because
+ // it might not be very efficient (imagine a string that has, e.g., 2 Mb chars), so we take
+ // only a substring that we need to compare with the given prefix.
+ return BeginsWith(ToLower(s.substr(0, beginsWith.length())), ToLower(beginsWith));
+}
+
+bool BeginsWith (const char* s, const char* beginsWith)
+{
+ return strncmp (s, beginsWith, strlen (beginsWith)) == 0;
+}
+
+
+#if !WEBPLUG
+bool IsStringNumber (const string& s)
+{
+ return IsStringNumber (s.c_str ());
+}
+#endif
+
+
+#if UNITY_EDITOR
+/*
+ Used by SemiNumericCompare:
+ if c[*index] is > '9', returns INT_MAX-265+c[*index] and increases *index by 1.
+ if '0' <= c[*index] <= '9' consumes all numeric characters and returns the numerical value + '0'
+ if c[*index] is < '0' returns (int)c[*index] and increases *index by 1;
+*/
+static int GetSNOrdinal(const char* c, int& index)
+{
+
+ if((unsigned char)c[index] < (unsigned char)'0')
+ return (unsigned char)c[index++];
+ else if ((unsigned char)c[index] > (unsigned char)'9')
+ return (INT_MAX-256) + (unsigned char)ToLower(c[index++]);
+ else
+ {
+ int atoi=0;
+ while (c[index] >= '0' && c[index] <= '9')
+ {
+ atoi = atoi*10 + (c[index++] - '0'); // TODO: clamp at INT_MAX-256
+ }
+ return atoi+'0';
+ }
+}
+
+// Human-like sorting.
+// Sorts strings alphabetically, but with numbers in strings numerically, so "xx11" comes after "xx2".
+int SemiNumericCompare(const char * str1, const char * str2)
+{
+ int i1 = 0;
+ int i2 = 0;
+ int o1, o2;
+
+ while ((o1 = GetSNOrdinal(str1, i1)) == (o2 = GetSNOrdinal(str2, i2)))
+ {
+ if (!o1)
+ return i2-i1; // to make strings like "xx1", "xx01" and "xx001" have a stable sorting (longest string first as in Finder)
+ }
+
+ return(o1 - o2);
+}
+#endif
+
+int StrNICmp(const char * str1, const char * str2, size_t n)
+{
+ const char * p1 = (char *) str1;
+ const char * p2 = (char *) str2;
+ char c1, c2;
+ size_t charsLeft=n;
+
+ if( n <= 0 )
+ return 0;
+
+ while ((c1 = ToLower (*p1)) == (c2 = ToLower (*p2)))
+ {
+ ++p1; ++p2; --charsLeft;
+ if (!charsLeft || !c1)
+ return 0;
+ }
+
+ return(c1 - c2);
+}
+
+int StrICmp(const char * str1, const char * str2)
+{
+ const char * p1 = (char *) str1;
+ const char * p2 = (char *) str2;
+ char c1, c2;
+
+ while ((c1 = ToLower (*p1)) == (c2 = ToLower (*p2)))
+ {
+ p1++; p2++;
+ if (!c1)
+ return 0;
+ }
+
+ return(c1 - c2);
+}
+
+int StrCmp(const char * str1, const char * str2)
+{
+ const char * p1 = (char *) str1;
+ const char * p2 = (char *) str2;
+ char c1, c2;
+
+ while ((c1 = (*p1)) == (c2 = (*p2)))
+ {
+ p1++; p2++;
+ if (!c1)
+ return 0;
+ }
+
+ return(c1 - c2);
+}
+
+
+#if !WEBPLUG
+bool IsStringNumber (const char* s)
+{
+ bool success = false;
+ bool hadPoint = false;
+ long i = 0;
+
+ while (s[i] != '\0')
+ {
+ switch (s[i])
+ {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+
+ success = true;
+
+ break;
+
+ case '.':
+ case ',':
+
+ // Make sure we only have one . or ,
+ if (hadPoint)
+ return false;
+ hadPoint = true;
+
+ break;
+
+ case '+':
+ case '-':
+
+ // + or - are only allowed at the beginning of the string
+ if (i != 0)
+ return false;
+ break;
+
+ default:
+ return false;
+ break;
+ }
+ i++;
+ }
+
+ return success;
+}
+#endif
+
+
+SInt32 StringToInt (char const* s)
+{
+ return atol (s);
+}
+
+template<typename T>
+inline string _ToString (const char* formatString, T value)
+{
+ char buf[255];
+
+ #ifdef WIN32
+ _snprintf
+ #else
+ snprintf
+ #endif
+ (buf, sizeof(buf), formatString, value);
+
+ return string (buf);
+}
+
+string IntToString (SInt32 i)
+{
+ return _ToString ("%i", i);
+}
+
+string UnsignedIntToString (UInt32 i)
+{
+ return _ToString ("%u", i);
+}
+
+string Int64ToString (SInt64 i)
+{
+ return _ToString ("%lli", i);
+}
+
+string UnsignedInt64ToString (UInt64 i)
+{
+ return _ToString ("%llu", i);
+}
+
+string DoubleToString (double i)
+{
+ return _ToString ("%f", i);
+}
+
+string FloatToString (float f, const char* precFormat)
+{
+ char buf[255];
+ if (IsNAN(f))
+ strcpy(buf, "NaN");
+ else if (IsMinusInf(f))
+ strcpy(buf, "-Infinity");
+ else if (IsPlusInf(f))
+ strcpy(buf, "Infinity");
+ else
+ sprintf (buf, precFormat, f);
+ return string (buf);
+}
+
+string SHA1ToString(unsigned char hash[20])
+{
+ char buffer[41];
+ for ( int i=0; i < 20; i++ )
+ sprintf(&buffer[i*2], "%.2x", hash[i]);
+ return std::string(buffer, 40);
+}
+
+string MD5ToString(unsigned char hash[16])
+{
+ char buffer[33];
+ for ( int i=0; i < 16; i++ )
+ sprintf(&buffer[i*2], "%.2x", hash[i]);
+ return std::string(buffer, 32);
+}
+
+// Parses simple float: optional sign, digits, period, digits.
+// No exponent or leading whitespace support.
+// No locale support.
+// We use it to be independent of locale, and because atof() did
+// show up in the shader parsing profile.
+float SimpleStringToFloat (const char* str, int* outLength)
+{
+ const char *p = str;
+
+ // optional sign
+ bool negative = false;
+ switch (*p) {
+ case '-': negative = true; // fall through to increment pointer
+ case '+': p++;
+ }
+
+ double number = 0.0;
+
+ // scan digits
+ while (IsDigit(*p))
+ {
+ number = number * 10.0 + (*p - '0');
+ p++;
+ }
+
+ // optional decimal part
+ if (*p == '.')
+ {
+ p++;
+
+ // scan digits after decimal point
+ double scaler = 0.1;
+ while (IsDigit(*p))
+ {
+ number += (*p - '0') * scaler;
+ scaler *= 0.1;
+ p++;
+ }
+ }
+
+ // apply sign
+ if (negative)
+ number = -number;
+
+ // Do not check for equality with atof() - results
+ // of that are dependent on locale.
+ //DebugAssertIf(!CompareApproximately(number, atof(str)));
+
+ if (outLength)
+ *outLength = (int) (p - str);
+
+ return float(number);
+}
+
+
+void VFormatBuffer (char* buffer, int size, const char* format, va_list ap)
+{
+ va_list zp;
+ va_copy (zp, ap);
+ vsnprintf (buffer, size, format, zp);
+ va_end (zp);
+}
+
+std::string VFormat (const char* format, va_list ap)
+{
+ va_list zp;
+ va_copy (zp, ap);
+ char buffer[1024 * 10];
+ vsnprintf (buffer, 1024 * 10, format, zp);
+ va_end (zp);
+ return buffer;
+}
+
+std::string Format (const char* format, ...)
+{
+ va_list va;
+ va_start (va, format);
+ std::string formatted = VFormat (format, va);
+ va_end (va);
+ return formatted;
+}
+
+#if UNITY_EDITOR || UNITY_FBX_IMPORTER
+bool AsciiToUTF8 (std::string& name)
+{
+ if (name.empty())
+ return true;
+
+ #if UNITY_OSX
+ CFStringRef str = CFStringCreateWithCString (NULL, name.c_str(), kCFStringEncodingASCII);
+ if (str != NULL)
+ {
+ bool res = false;
+
+ char* tempName;
+ ALLOC_TEMP(tempName, char, name.size() * 2);
+ if (CFStringGetCString(str, tempName, name.size() * 2, kCFStringEncodingUTF8))
+ {
+ name = tempName;
+ res = true;
+ }
+ CFRelease(str);
+ return res;
+ }
+
+ return false;
+
+ #elif UNITY_WIN
+
+ bool result = false;
+ int bufferSize = (int) name.size()*4+1;
+ wchar_t* wideBuffer;
+ ALLOC_TEMP(wideBuffer, wchar_t, bufferSize);
+ if( MultiByteToWideChar( CP_ACP, 0, name.c_str(), -1, wideBuffer, bufferSize ) )
+ {
+ char* buffer;
+ ALLOC_TEMP(buffer, char, bufferSize);
+ if( WideCharToMultiByte( CP_UTF8, 0, wideBuffer, -1, buffer, bufferSize, NULL, NULL ) )
+ {
+ name = buffer;
+ result = true;
+ }
+ }
+ return result;
+
+ #elif UNITY_LINUX
+
+ return true; // do nothing for now. Tests should show how much is this
+ // function needed.
+
+ #else
+ #error Unknown platform
+ #endif
+}
+
+std::string StripInvalidIdentifierCharacters (std::string str)
+{
+ for (unsigned int i=0;i<str.size();i++)
+ {
+ char c = str[i];
+ if (c == '~' || c == '&' || c == '%' || c == '|' || c == '$' || c == '<' || c == '>' || c == '/' || c == '\\')
+ str[i] = '_';
+ }
+ return str;
+}
+#endif
+
+void HexStringToBytes (char* str, size_t bytes, void *data)
+{
+ for (size_t i=0; i<bytes; i++)
+ {
+ UInt8 b;
+ char ch = str[2*i+0];
+ if (ch <= '9')
+ b = (ch - '0') << 4;
+ else
+ b = (ch - 'a' + 10) << 4;
+
+ ch = str[2*i+1];
+ if (ch <= '9')
+ b |= (ch - '0');
+ else
+ b |= (ch - 'a' + 10);
+
+ ((UInt8*)data)[i] = b;
+ }
+}
+
+void BytesToHexString (const void *data, size_t bytes, char* str)
+{
+ static const char kHexToLiteral[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
+ for (size_t i=0; i<bytes;i++)
+ {
+ UInt8 b = ((UInt8*)data)[i];
+ str[2*i+0] = kHexToLiteral[ b >> 4 ];
+ str[2*i+1] = kHexToLiteral[ b & 0xf ];
+ }
+}
+
+std::string BytesToHexString (const void* data, size_t numBytes)
+{
+ std::string result;
+ result.resize (numBytes * 2);
+ BytesToHexString (data, numBytes, &result[0]);
+ return result;
+}
+
+string FormatBytes(SInt64 b)
+{
+ AssertIf(sizeof(b) != 8);
+
+ if(b < 0)
+ return "Unknown";
+ if (b < 512)
+#if UNITY_64 && UNITY_LINUX
+ return Format("%ld B",b);
+#else
+ return Format("%lld B",b);
+#endif
+ if (b < 512*1024)
+ return Format("%01.1f KB",b / 1024.0);
+
+ b /= 1024;
+ if (b < 512*1024)
+ return Format("%01.1f MB", b / 1024.0);
+
+ b /= 1024;
+ return Format("%01.2f GB",b / 1024.0);
+}
+
+std::string Append (char const* a, std::string const& b)
+{
+ std::string r;
+ size_t asz = strlen (a);
+ r.reserve (asz + b.size ());
+ r.assign (a, asz);
+ r.append (b);
+ return r;
+}
+
+std::string Append (char const* a, char const* b)
+{
+ std::string r;
+ size_t asz = strlen (a);
+ size_t bsz = strlen (b);
+ r.reserve (asz + bsz);
+ r.assign (a, asz);
+ r.append (b, bsz);
+ return r;
+}
+
+std::string Append (std::string const& a, char const* b)
+{
+ std::string r;
+ size_t bsz = strlen(b);
+ r.reserve(a.size() + bsz);
+ r.assign(a);
+ r.append(b, bsz);
+ return r;
+}
+
+#if UNITY_OSX || UNITY_IPHONE
+CFStringRef StringToCFString (const std::string &str)
+{
+ return CFStringCreateWithCString(kCFAllocatorDefault, str.c_str(), kCFStringEncodingUTF8);
+}
+
+std::string CFStringToString (CFStringRef str)
+{
+ std::string output;
+ if (str)
+ {
+ int bufferSize = CFStringGetMaximumSizeForEncoding(CFStringGetLength(str), kCFStringEncodingUTF8) + 1;
+ char *buf;
+ MALLOC_TEMP( buf, bufferSize );
+ if (CFStringGetCString(str, buf, bufferSize, kCFStringEncodingUTF8))
+ output = buf;
+ }
+ return output;
+}
+#endif
+
+std::string Trim(const std::string &input, const std::string &ws)
+{
+ size_t startpos = input.find_first_not_of(ws); // Find the first character position after excluding leading blank spaces
+ size_t endpos = input.find_last_not_of(ws); // Find the first character position from reverse af
+
+ // if all spaces or empty return an empty string
+ if(( string::npos == startpos ) || ( string::npos == endpos))
+ {
+ return std::string(); // empty string
+ }
+ else
+ {
+ return input.substr( startpos, endpos-startpos+1 );
+ }
+}
+
+
+/*
+ Convert version string of form XX.X.XcXXX to UInt32 in Apple 'vers' representation
+ as described in Tech Note TN1132 (http://developer.apple.com/technotes/tn/pdf/tn1132.pdf)
+ eg.
+ 1.2.0
+ 1.2.1
+ 1.2.1a1
+ 1.2.1b1
+ 1.2.1r12
+ */
+int GetNumericVersion (char const* versionCString)
+{
+ if( (*versionCString)=='\0' )
+ return 0;
+
+ int major=0,minor=0,fix=0,type='r',release=0;
+ const char *spos=versionCString;
+ major = *(spos++) - '0';
+ if (*spos>='0' && *spos < '9')
+ major = major*10 + *(spos++)-'0';
+
+ if (*spos)
+ {
+ if (*spos=='.')
+ {
+ spos++;
+ if(*spos)
+ minor=*(spos++)-'0';
+ }
+ if (*spos)
+ {
+ if (*spos=='.')
+ {
+ spos++;
+ if (*spos)
+ fix=*(spos++)-'0';
+ }
+ if (*spos)
+ {
+ type = *(spos++);
+ if (*spos)
+ {
+ release = *(spos++)-'0';
+ if (*spos)
+ {
+ release = release*10+*(spos++)-'0';
+ if (*spos)
+ {
+ release=release*10+*(spos++)-'0';
+ }
+ }
+ }
+ }
+ }
+ }
+
+ unsigned int version = 0;
+ version |= ((major/10)%10)<<28;
+ version |= (major%10)<<24;
+ version |= (minor%10)<<20;
+ version |= (fix%10)<<16;
+ switch( type )
+ {
+ case 'D':
+ case 'd': version |= 0x2<<12; break;
+ case 'A':
+ case 'a': version |= 0x4<<12; break;
+ case 'B':
+ case 'b': version |= 0x6<<12; break;
+ case 'F':
+ case 'R':
+ case 'f':
+ case 'r': version |= 0x8<<12; break;
+ }
+ version |= ((release / 100)%10)<<8;
+ version |= ((release / 10) %10)<<4;
+ version |= release % 10;
+
+ AssertIf (((int)version) < 0);
+
+ return version;
+}
+
+void Split (const std::string s, char splitChar, std::vector<std::string> &parts)
+{
+ int n = 0, n1 = 0;
+ while ( 1 )
+ {
+ n1 = s.find (splitChar, n);
+ std::string p = s.substr (n, n1-n);
+ if ( p.length () )
+ {
+ parts.push_back (p);
+ }
+ if ( n1 == std::string::npos )
+ break;
+
+ n = n1 + 1;
+ }
+}
+
+void Split (const std::string s, const char* splitChars, std::vector<std::string> &parts)
+{
+ int n = 0, n1 = 0;
+ while ( 1 )
+ {
+ n1 = s.find_first_of (splitChars, n);
+ std::string p = s.substr (n, n1-n);
+ if ( p.length () )
+ {
+ parts.push_back (p);
+ }
+ if ( n1 == std::string::npos )
+ break;
+
+ n = n1 + 1;
+ }
+}
diff --git a/Runtime/Utilities/Word.h b/Runtime/Utilities/Word.h
new file mode 100644
index 0000000..d54ac5b
--- /dev/null
+++ b/Runtime/Utilities/Word.h
@@ -0,0 +1,227 @@
+#ifndef WORD_H
+#define WORD_H
+
+#include <string>
+#include <vector>
+#include <stdarg.h> // va_list
+#include <vector>
+#include "Annotations.h"
+#include "Runtime/Allocator/MemoryMacros.h"
+#include "Runtime/Modules/ExportModules.h"
+
+#if UNITY_OSX || UNITY_IPHONE
+ #include "CoreFoundation/CoreFoundation.h"
+#endif
+
+bool BeginsWithCaseInsensitive (const std::string &s, const std::string &beginsWith);
+
+bool BeginsWith (const char* s, const char* beginsWith);
+
+template<typename StringType>
+bool BeginsWith (const StringType &s, const StringType &beginsWith)
+{
+ return BeginsWith (s.c_str(), beginsWith.c_str());
+}
+template<typename StringType>
+bool BeginsWith (const StringType &s, const char *beginsWith)
+{
+ return BeginsWith (s.c_str(), beginsWith);
+}
+
+inline bool EndsWith (const char* str, int strLen, const char* sub, int subLen){
+ return (strLen >= subLen) && (strncmp (str+strLen-subLen, sub, subLen)==0);
+}
+
+template<typename StringType>
+bool EndsWith (const StringType& str, const StringType& sub)
+{
+ return EndsWith(str.c_str(), str.size(), sub.c_str(), sub.size());
+}
+
+template<typename StringType>
+bool EndsWith (const StringType& str, const char* endsWith)
+{
+ return EndsWith(str.c_str(), str.size(), endsWith, strlen(endsWith));
+}
+
+inline bool EndsWith (const char* s, const char* endsWith){
+ return EndsWith(s, strlen(s), endsWith, strlen(endsWith));
+}
+
+void ConcatCString( char* root, const char* append );
+#if !WEBPLUG
+bool IsStringNumber (const char* s);
+bool IsStringNumber (const std::string& s);
+#endif
+
+SInt32 StringToInt (char const* s);
+
+template <typename StringType>
+SInt32 StringToInt (const StringType& s)
+{
+ return StringToInt(s.c_str());
+}
+
+/// Replacement for atof is not dependent on locale settings for what to use as the decimal separator.
+/// Limited support but fast. It does'nt work for infinity, nan, but
+/// This function is lossy. Converting a string back and forth does not result in the same binary exact float representation.
+/// See FloatStringConversion.h for binary exact string<->float conversion functions.
+float SimpleStringToFloat (const char* str, int* outLength = NULL);
+
+std::string IntToString (SInt32 i);
+std::string UnsignedIntToString (UInt32 i);
+std::string Int64ToString (SInt64 i);
+std::string UnsignedInt64ToString (UInt64 i);
+std::string DoubleToString (double i);
+std::string EXPORT_COREMODULE FloatToString (float f, const char* precFormat = "%f");
+
+std::string SHA1ToString(unsigned char hash[20]);
+std::string MD5ToString(unsigned char hash[16]);
+
+int StrNICmp (const char* a, const char* b, size_t n);
+int StrICmp (const char* a, const char* b);
+inline int StrICmp(const UnityStr& str1, const UnityStr& str2) { return StrICmp (str1.c_str (), str2.c_str ()); }
+int StrCmp (const char* a, const char* b);
+inline int StrCmp(const std::string& str1, const std::string& str2) { return StrCmp (str1.c_str (), str2.c_str ()); }
+inline int StrICmp(const std::string& str1, const std::string& str2) { return StrICmp (str1.c_str (), str2.c_str ()); }
+
+#if UNITY_EDITOR
+int SemiNumericCompare(const char * str1, const char * str2);
+inline int SemiNumericCompare(const std::string& str1, const std::string& str2) { return SemiNumericCompare (str1.c_str (), str2.c_str ()); }
+#endif
+
+inline char ToLower (char v)
+{
+ if (v >= 'A' && v <= 'Z')
+ return static_cast<char>(v | 0x20);
+ else
+ return v;
+}
+
+inline char ToUpper (char v)
+{
+ if (v >= 'a' && v <= 'z')
+ return static_cast<char>(v & 0xdf);
+ else
+ return v;
+}
+
+template<typename StringType>
+StringType ToUpper (const StringType& input)
+{
+ StringType s = input;
+ for (typename StringType::iterator i= s.begin (); i != s.end ();i++)
+ *i = ToUpper (*i);
+ return s;
+}
+template<typename StringType>
+StringType ToLower (const StringType& input)
+{
+ StringType s = input;
+ for (typename StringType::iterator i= s.begin (); i != s.end ();i++)
+ *i = ToLower (*i);
+ return s;
+}
+template<typename StringType>
+void ToUpperInplace (StringType& input)
+{
+ for (typename StringType::iterator i= input.begin (); i != input.end ();i++)
+ *i = ToUpper (*i);
+}
+template<typename StringType>
+void ToLowerInplace (StringType& input)
+{
+ for (typename StringType::iterator i= input.begin (); i != input.end ();i++)
+ *i = ToLower (*i);
+}
+
+TAKES_PRINTF_ARGS(1,2) std::string EXPORT_COREMODULE Format (const char* format, ...);
+std::string VFormat (const char* format, va_list ap);
+
+void VFormatBuffer (char* buffer, int size, const char* format, va_list ap);
+template<typename StringType>
+inline TAKES_PRINTF_ARGS(1,2) StringType FormatString(const char* format, ...)
+{
+ char buffer[10*1024];
+ va_list va;
+ va_start( va, format );
+ VFormatBuffer (buffer, 10*1024, format, va);
+ return StringType(buffer);
+}
+
+std::string Append (char const* a, std::string const& b);
+EXPORT_COREMODULE std::string Append (char const* a, char const* b);
+std::string Append (std::string const& a, char const* b);
+
+inline bool IsDigit (char c) { return c >= '0' && c <= '9'; }
+inline bool IsAlpha (char c) { return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'); }
+inline bool IsSpace (char c) { return c == '\t' || c == '\n' || c == '\v' || c == '\f' || c == '\r' || c == ' '; }
+inline bool IsAlphaNumeric (char c) { return IsDigit (c) || IsAlpha (c); }
+
+template<typename alloc>
+void replace_string (std::basic_string<char, std::char_traits<char>, alloc>& target,
+ const std::basic_string<char, std::char_traits<char>, alloc>& search,
+ const std::basic_string<char, std::char_traits<char>, alloc>& replace, size_t startPos = 0)
+{
+ if (search.empty())
+ return;
+
+ typename std::basic_string<char, std::char_traits<char>, alloc>::size_type p = startPos;
+ while ((p = target.find (search, p)) != std::basic_string<char, std::char_traits<char>, alloc>::npos)
+ {
+ target.replace (p, search.size (), replace);
+ p += replace.size ();
+ }
+}
+
+template<typename StringType>
+void replace_string (StringType& target, const char* search, const StringType& replace, size_t startPos = 0)
+{
+ replace_string(target,StringType(search),replace, startPos);
+}
+template<typename StringType>
+void replace_string (StringType& target, const StringType& search, const char* replace, size_t startPos = 0)
+{
+ replace_string(target,search,StringType(replace), startPos);
+}
+template<typename StringType>
+void replace_string (StringType& target, const char* search, const char* replace, size_t startPos = 0)
+{
+ replace_string(target,StringType(search),StringType(replace), startPos);
+}
+
+#if UNITY_EDITOR || UNITY_FBX_IMPORTER
+/// Converts name to UTF8. Returns whether conversion was successful.
+/// If not successful name will not be touched
+bool AsciiToUTF8 (std::string& name);
+std::string StripInvalidIdentifierCharacters (std::string str);
+#endif
+
+std::string FormatBytes(SInt64 b);
+
+#if UNITY_OSX || UNITY_IPHONE
+CFStringRef StringToCFString (const std::string &str);
+std::string CFStringToString (CFStringRef str);
+#endif
+
+std::string Trim(const std::string &input, const std::string &ws=" \t");
+
+void HexStringToBytes (char* str, size_t numBytes, void *data);
+void BytesToHexString (const void *data, size_t numBytes, char* str);
+std::string BytesToHexString (const void* data, size_t numBytes);
+
+int GetNumericVersion (char const* versionCString);
+inline int GetNumericVersion (const std::string& versionString) { return GetNumericVersion (versionString.c_str()); }
+
+/// Split a string delimited by splitChar or any character in splitChars into parts.
+/// Parts is appended, not cleared.
+/// Empty parts are discarded.
+void Split (const std::string s, char splitChar, std::vector<std::string> &parts);
+void Split (const std::string s, const char* splitChars, std::vector<std::string> &parts);
+
+inline std::string QuoteString( const std::string& str )
+{
+ return '"' + str + '"';
+}
+
+#endif
diff --git a/Runtime/Utilities/WordTests.cpp b/Runtime/Utilities/WordTests.cpp
new file mode 100644
index 0000000..5d02ba2
--- /dev/null
+++ b/Runtime/Utilities/WordTests.cpp
@@ -0,0 +1,151 @@
+#include "UnityPrefix.h"
+
+#if ENABLE_UNIT_TESTS
+
+#include "Word.h"
+#include "Runtime/Testing/Testing.h"
+
+using namespace std;
+
+SUITE (WordTests)
+{
+ TEST (IntToString_Works)
+ {
+ CHECK (IntToString (123456) == "123456");
+ CHECK (IntToString (-123456) == "-123456");
+ }
+
+ TEST (Int64ToString_Works)
+ {
+ CHECK (Int64ToString (1099511627776) == "1099511627776");
+ CHECK (Int64ToString (-1099511627776) == "-1099511627776");
+ }
+
+ TEST (UnsignedIntToString_Works)
+ {
+ CHECK (IntToString (123456) == "123456");
+ }
+
+ TEST (UnsignedInt64ToString_Works)
+ {
+ CHECK (UnsignedInt64ToString (1099511627776) == "1099511627776");
+ }
+
+ TEST (Word_EndsWith)
+ {
+ CHECK(EndsWith("abc","c"));
+ CHECK(EndsWith("abc","bc"));
+ CHECK(EndsWith("abc","abc"));
+ CHECK(EndsWith("abc",""));
+ CHECK(!EndsWith("abc","d"));
+ CHECK(!EndsWith("abc","abcd"));
+ }
+
+ TEST (Word_IsStringNumber)
+ {
+ CHECK_EQUAL(true, IsStringNumber ("-1"));
+ CHECK_EQUAL(true, IsStringNumber ("+2"));
+ CHECK_EQUAL(false, IsStringNumber ("2+"));
+ CHECK_EQUAL(false, IsStringNumber ("a"));
+ CHECK_EQUAL(false, IsStringNumber ("1b"));
+ }
+
+ TEST (Word_ReplaceString)
+ {
+ string s;
+ s = "foo bar foo"; replace_string (s, "foo", "x"); CHECK_EQUAL("x bar x", s);
+ s = "foo bar foo"; replace_string (s, "", ""); CHECK_EQUAL("foo bar foo", s);
+ }
+
+ TEST (Word_SimpleStringToFloatWorks)
+ {
+ int len;
+ CHECK_EQUAL (0.0f, SimpleStringToFloat("0",&len)); CHECK_EQUAL(1,len);
+ CHECK_EQUAL (0.0f, SimpleStringToFloat("0.0",&len)); CHECK_EQUAL(3,len);
+ CHECK_EQUAL (0.0f, SimpleStringToFloat(".0",&len)); CHECK_EQUAL(2,len);
+ CHECK_EQUAL (12.05f, SimpleStringToFloat("12.05",&len)); CHECK_EQUAL(5,len);
+ CHECK_EQUAL (-3.5f, SimpleStringToFloat("-3.5",&len)); CHECK_EQUAL(4,len);
+ CHECK_EQUAL (3.14f, SimpleStringToFloat("3.14",&len)); CHECK_EQUAL(4,len);
+ CHECK_EQUAL (-1024.5f, SimpleStringToFloat("-1024.500",&len)); CHECK_EQUAL(9,len);
+ }
+
+ TEST (Word_Trim)
+ {
+ string s;
+ s=Trim(" \tspaces in front\n"); CHECK_EQUAL("spaces in front\n",s);
+ s=Trim("spaces behind \t \t\t"); CHECK_EQUAL("spaces behind",s);
+ s=Trim("\t\t\t\tspaces at both ends \t \t\t"); CHECK_EQUAL("spaces at both ends",s);
+ s=Trim(""); CHECK_EQUAL("",s);
+ s=Trim("\t\t\t \t \t"); CHECK_EQUAL("",s);
+ s=Trim("\n\n Custom Whitespace\r\n","\r\n"); CHECK_EQUAL(" Custom Whitespace",s);
+ }
+
+ TEST (Word_Split)
+ {
+ const int kNumtests = 6;
+ const int kMaxtokens = 3;
+ const char splitChar = ';';
+ const char* splitChars = ";/";
+
+ string inputs[kNumtests] =
+ {
+ "Normal;string;split",
+ "Adjacent;;separators",
+ "NoSeparators",
+ "EndWithSeparator;",
+ ";StartWithSeparator",
+ ";" // No non-separators
+ };
+
+ string inputsMulti[kNumtests] =
+ {
+ "Normal;string/split",
+ "Adjacent/;separators",
+ "NoSeparators",
+ "EndWithSeparator;/",
+ ";StartWithSeparator",
+ ";" // No non-separators
+ };
+
+ int outputSizes[kNumtests] =
+ {
+ 3, 2, 1, 1, 1, 0
+ };
+
+ string outputTokens[][kMaxtokens] =
+ {
+ { "Normal", "string", "split" },
+ { "Adjacent", "separators", "" },
+ { "NoSeparators", "", "" },
+ { "EndWithSeparator", "", "" },
+ { "StartWithSeparator", "", "" },
+ { "", "", "" }
+ };
+
+ for (int test = 0; test < kNumtests; ++test)
+ {
+ string s = inputs[test];
+ vector<string> tokens;
+ Split (inputs[test], splitChar, tokens);
+ CHECK_EQUAL (outputSizes[test], tokens.size ()); // Verify number of tokens
+ for (int token = 0; token < outputSizes[test]; ++token)
+ {
+ CHECK_EQUAL (outputTokens[test][token], tokens[token]); // Verify each token
+ }
+ }
+
+ for (int test = 0; test < kNumtests; ++test)
+ {
+ string s = inputs[test];
+ vector<string> tokens;
+ Split (inputsMulti[test], splitChars, tokens);
+ CHECK_EQUAL (outputSizes[test], tokens.size ()); // Verify number of tokens
+ for (int token = 0; token < outputSizes[test]; ++token)
+ {
+ CHECK_EQUAL (outputTokens[test][token], tokens[token]); // Verify each token
+ }
+ }
+ }
+}
+
+#endif // ENABLE_UNIT_TESTS
diff --git a/Runtime/Utilities/algorithm_utility.h b/Runtime/Utilities/algorithm_utility.h
new file mode 100644
index 0000000..1c56bb1
--- /dev/null
+++ b/Runtime/Utilities/algorithm_utility.h
@@ -0,0 +1,115 @@
+#ifndef ALGORITHM_UTILITY_H
+#define ALGORITHM_UTILITY_H
+
+#include <algorithm>
+#include <functional>
+#include "LogAssert.h"
+
+template<class T, class Func>
+void repeat (T& type, int count, Func func)
+{
+ int i;
+ for (i=0;i<count;i++)
+ func (type);
+}
+
+template<class C, class Func>
+Func for_each (C& c, Func f)
+{
+ return for_each (c.begin (), c.end (), f);
+}
+
+template<class C, class Func>
+void erase_if (C& c, Func f)
+{
+ c.erase (remove_if (c.begin (), c.end (), f), c.end ());
+}
+
+template<class C, class T>
+void erase (C& c, const T& t)
+{
+ c.erase (remove (c.begin (), c.end (), t), c.end ());
+}
+
+template<class C, class T>
+typename C::iterator find (C& c, const T& value)
+{
+ return find (c.begin (), c.end (), value);
+}
+
+template<class C, class Pred>
+typename C::iterator find_if (C& c, Pred p)
+{
+ return find_if (c.begin (), c.end (), p);
+}
+
+template <class T, class U>
+struct EqualTo
+ : std::binary_function<T, U, bool>
+{
+ bool operator()(const T& x, const U& y) const { return static_cast<bool>(x == y); }
+};
+
+// Returns the iterator to the last element
+template<class Container>
+typename Container::iterator last_iterator (Container& container)
+{
+ AssertIf (container.begin () == container.end ());
+ typename Container::iterator i = container.end ();
+ i--;
+ return i;
+}
+
+template<class Container>
+typename Container::const_iterator last_iterator (const Container& container)
+{
+ AssertIf (container.begin () == container.end ());
+ typename Container::const_iterator i = container.end ();
+ i--;
+ return i;
+}
+
+
+// Efficient "add or update" for STL maps.
+// For more details see item 24 on Effective STL.
+// Basically it avoids constructing default value only to
+// assign it later.
+template<typename MAP, typename K, typename V>
+bool add_or_update( MAP& m, const K& key, const V& val )
+{
+ typename MAP::iterator lb = m.lower_bound( key );
+ if( lb != m.end() && !m.key_comp()( key, lb->first ) )
+ {
+ // lb points to a pair with the given key, update pair's value
+ lb->second = val;
+ return false;
+ }
+ else
+ {
+ // no key exists, insert new pair
+ m.insert( lb, std::make_pair(key,val) );
+ return true;
+ }
+}
+
+template<class ForwardIterator>
+bool is_sorted (ForwardIterator begin, ForwardIterator end)
+{
+ for (ForwardIterator next = begin; begin != end && ++next != end; ++begin)
+ if (*next < *begin)
+ return false;
+
+ return true;
+}
+
+template<class ForwardIterator, class Predicate>
+bool is_sorted (ForwardIterator begin, ForwardIterator end, Predicate pred)
+{
+ for (ForwardIterator next = begin; begin != end && ++next != end; ++begin)
+ if (pred(*next, *begin))
+ return false;
+
+ return true;
+}
+
+#endif
diff --git a/Runtime/Utilities/delayed_set.h b/Runtime/Utilities/delayed_set.h
new file mode 100644
index 0000000..cdb8fd1
--- /dev/null
+++ b/Runtime/Utilities/delayed_set.h
@@ -0,0 +1,45 @@
+#ifndef DELAYED_SET_H
+#define DELAYED_SET_H
+
+#include <set>
+#include <vector>
+#include "MemoryPool.h"
+
+template <class T, class SetType = std::set<T, std::less<T> , memory_pool<T> > >
+class delayed_set : public SetType {
+ typedef typename std::vector<std::pair<bool, T> > delay_container;
+ delay_container m_Delayed;
+
+ public:
+
+ bool is_inserted (const T& object)
+ {
+ typename delay_container::reverse_iterator i;
+ for (i = m_Delayed.rbegin ();i != m_Delayed.rend ();i++)
+ {
+ if (i->second == object)
+ return i->first;
+ }
+ return this->find (object) != this->end ();
+ }
+
+ void remove_delayed (const T& obj) {
+ m_Delayed.push_back (std::pair<bool, T> (false, obj));
+ }
+ void add_delayed (const T& obj) {
+ m_Delayed.push_back (std::pair<bool, T> (true, obj));
+ }
+ int apply_delayed_size () const { return m_Delayed.size (); }
+ void apply_delayed () {
+ typename delay_container::iterator iter;
+ for (iter = m_Delayed.begin(); iter != m_Delayed.end(); iter++) {
+ if (iter->first)
+ SetType::insert (iter->second);
+ else
+ SetType::erase (iter->second);
+ }
+ m_Delayed.clear ();
+ }
+};
+
+#endif
diff --git a/Runtime/Utilities/dense_hash_map.h b/Runtime/Utilities/dense_hash_map.h
new file mode 100644
index 0000000..a45000e
--- /dev/null
+++ b/Runtime/Utilities/dense_hash_map.h
@@ -0,0 +1,243 @@
+// Copyright (c) 2005, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// ----
+// Author: Craig Silverstein
+//
+// This is just a very thin wrapper over densehashtable.h, just
+// like sgi stl's stl_hash_map is a very thin wrapper over
+// stl_hashtable. The major thing we define is operator[], because
+// we have a concept of a data_type which stl_hashtable doesn't
+// (it only has a key and a value).
+//
+// NOTE: this is exactly like sparse_hash_map.h, with the word
+// "sparse" replaced by "dense", except for the addition of
+// set_empty_key().
+//
+// YOU MUST CALL SET_EMPTY_KEY() IMMEDIATELY AFTER CONSTRUCTION.
+//
+// Otherwise your program will die in mysterious ways.
+//
+// In other respects, we adhere mostly to the STL semantics for
+// hash-map. One important exception is that insert() invalidates
+// iterators entirely. On the plus side, though, erase() doesn't
+// invalidate iterators at all, or even change the ordering of elements.
+//
+// Here are a few "power user" tips:
+//
+// 1) set_deleted_key():
+// If you want to use erase() you must call set_deleted_key(),
+// in addition to set_empty_key(), after construction.
+// The deleted and empty keys must differ.
+//
+// 2) resize(0):
+// When an item is deleted, its memory isn't freed right
+// away. This allows you to iterate over a hashtable,
+// and call erase(), without invalidating the iterator.
+// To force the memory to be freed, call resize(0).
+//
+// Guide to what kind of hash_map to use:
+// (1) dense_hash_map: fastest, uses the most memory
+// (2) sparse_hash_map: slowest, uses the least memory
+// (3) hash_map (STL): in the middle
+// Typically I use sparse_hash_map when I care about space and/or when
+// I need to save the hashtable on disk. I use hash_map otherwise. I
+// don't personally use dense_hash_map ever; the only use of
+// dense_hash_map I know of is to work around malloc() bugs in some
+// systems (dense_hash_map has a particularly simple allocation scheme).
+//
+// - dense_hash_map has, typically, a factor of 2 memory overhead (if your
+// data takes up X bytes, the hash_map uses X more bytes in overhead).
+// - sparse_hash_map has about 2 bits overhead per entry.
+// - sparse_hash_map can be 3-7 times slower than the others for lookup and,
+// especially, inserts. See time_hash_map.cc for details.
+//
+// See /usr/(local/)?doc/sparsehash-0.1/dense_hash_map.html
+// for information about how to use this class.
+
+#ifndef _DENSE_HASH_MAP_H_
+#define _DENSE_HASH_MAP_H_
+
+//#include <google/sparsehash/sparseconfig.h>
+#include <stdio.h> // for FILE * in read()/write()
+#include <algorithm> // for the default template args
+#include <functional> // for equal_to
+#include <memory> // for alloc<>
+#include <utility> // for pair<>
+//#include <ext/hash_fun.h> // defined in config.h
+#include "densehashtable.h"
+
+
+using std::pair;
+
+template <class Key, class T,
+ class HashFcn,
+ class EqualKey = std::equal_to<Key>,
+ class Alloc = std::allocator< std::pair<const Key, T> > >
+class dense_hash_map {
+ private:
+ // Apparently select1st is not stl-standard, so we define our own
+ struct SelectKey {
+ const Key& operator()(const pair<const Key, T>& p) const {
+ return p.first;
+ }
+ };
+
+ // The actual data
+ typedef dense_hashtable<pair<const Key, T>, Key, HashFcn,
+ SelectKey, EqualKey, Alloc> ht;
+ ht rep;
+
+ public:
+ typedef typename ht::key_type key_type;
+ typedef T data_type;
+ typedef T mapped_type;
+ typedef typename ht::value_type value_type;
+ typedef typename ht::hasher hasher;
+ typedef typename ht::key_equal key_equal;
+
+ typedef typename ht::size_type size_type;
+ typedef typename ht::difference_type difference_type;
+ typedef typename ht::pointer pointer;
+ typedef typename ht::const_pointer const_pointer;
+ typedef typename ht::reference reference;
+ typedef typename ht::const_reference const_reference;
+
+ typedef typename ht::iterator iterator;
+ typedef typename ht::const_iterator const_iterator;
+
+ // Iterator functions
+ iterator begin() { return rep.begin(); }
+ iterator end() { return rep.end(); }
+ const_iterator begin() const { return rep.begin(); }
+ const_iterator end() const { return rep.end(); }
+
+
+ // Accessor functions
+ hasher hash_funct() const { return rep.hash_funct(); }
+ key_equal key_eq() const { return rep.key_eq(); }
+
+
+ // Constructors
+ explicit dense_hash_map(size_type n = 0,
+ const hasher& hf = hasher(),
+ const key_equal& eql = key_equal())
+ : rep(n, hf, eql) { }
+
+ template <class InputIterator>
+ dense_hash_map(InputIterator f, InputIterator l,
+ size_type n = 0,
+ const hasher& hf = hasher(),
+ const key_equal& eql = key_equal())
+ : rep(n, hf, eql) {
+ rep.insert(f, l);
+ }
+ // We use the default copy constructor
+ // We use the default operator=()
+ // We use the default destructor
+
+ void clear() { rep.clear(); }
+ // This clears the hash map without resizing it down to the minimum
+ // bucket count, but rather keeps the number of buckets constant
+ void clear_no_resize() { rep.clear_no_resize(); }
+ void swap(dense_hash_map& hs) { rep.swap(hs.rep); }
+
+
+ // Functions concerning size
+ size_type size() const { return rep.size(); }
+ size_type max_size() const { return rep.max_size(); }
+ bool empty() const { return rep.empty(); }
+ size_type bucket_count() const { return rep.bucket_count(); }
+ size_type max_bucket_count() const { return rep.max_bucket_count(); }
+
+ void resize(size_type hint) { rep.resize(hint); }
+
+
+ // Lookup routines
+ iterator find(const key_type& key) { return rep.find(key); }
+ const_iterator find(const key_type& key) const { return rep.find(key); }
+
+ data_type& operator[](const key_type& key) { // This is our value-add!
+ iterator it = find(key);
+ if (it != end()) {
+ return it->second;
+ } else {
+ return insert(value_type(key, data_type())).first->second;
+ }
+ }
+
+ size_type count(const key_type& key) const { return rep.count(key); }
+
+ pair<iterator, iterator> equal_range(const key_type& key) {
+ return rep.equal_range(key);
+ }
+ pair<const_iterator, const_iterator> equal_range(const key_type& key) const {
+ return rep.equal_range(key);
+ }
+
+ // Insertion routines
+ pair<iterator, bool> insert(const value_type& obj) { return rep.insert(obj); }
+ template <class InputIterator>
+ void insert(InputIterator f, InputIterator l) { rep.insert(f, l); }
+ void insert(const_iterator f, const_iterator l) { rep.insert(f, l); }
+ // required for std::insert_iterator; the passed-in iterator is ignored
+ iterator insert(iterator, const value_type& obj) { return insert(obj).first; }
+
+
+ // Deletion and empty routines
+ // THESE ARE NON-STANDARD! I make you specify an "impossible" key
+ // value to identify deleted and empty buckets. You can change the
+ // deleted key as time goes on, or get rid of it entirely to be insert-only.
+ void set_empty_key(const key_type& key) { // YOU MUST CALL THIS!
+ rep.set_empty_key(value_type(key, data_type())); // rep wants a value
+ }
+ void set_deleted_key(const key_type& key) {
+ rep.set_deleted_key(value_type(key, data_type())); // rep wants a value
+ }
+ void clear_deleted_key() { rep.clear_deleted_key(); }
+
+ // These are standard
+ size_type erase(const key_type& key) { return rep.erase(key); }
+ void erase(iterator it) { rep.erase(it); }
+ void erase(iterator f, iterator l) { rep.erase(f, l); }
+
+
+ // Comparison
+ bool operator==(const dense_hash_map& hs) const { return rep == hs.rep; }
+ bool operator!=(const dense_hash_map& hs) const { return rep != hs.rep; }
+};
+
+// We need a global swap as well
+template <class Key, class T, class HashFcn, class EqualKey, class Alloc>
+inline void swap(dense_hash_map<Key, T, HashFcn, EqualKey, Alloc>& hm1,
+ dense_hash_map<Key, T, HashFcn, EqualKey, Alloc>& hm2) {
+ hm1.swap(hm2);
+}
+
+#endif /* _DENSE_HASH_MAP_H_ */
diff --git a/Runtime/Utilities/densehashtable.h b/Runtime/Utilities/densehashtable.h
new file mode 100644
index 0000000..763bce1
--- /dev/null
+++ b/Runtime/Utilities/densehashtable.h
@@ -0,0 +1,899 @@
+// Copyright (c) 2005, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// ---
+// Author: Craig Silverstein
+//
+// A dense hashtable is a particular implementation of
+// a hashtable: one that is meant to minimize memory allocation.
+// It does this by using an array to store all the data. We
+// steal a value from the key space to indicate "empty" array
+// elements (ie indices where no item lives) and another to indicate
+// "deleted" elements.
+//
+// (Note it is possible to change the value of the delete key
+// on the fly; you can even remove it, though after that point
+// the hashtable is insert_only until you set it again. The empty
+// value however can't be changed.)
+//
+// To minimize allocation and pointer overhead, we use internal
+// probing, in which the hashtable is a single table, and collisions
+// are resolved by trying to insert again in another bucket. The
+// most cache-efficient internal probing schemes are linear probing
+// (which suffers, alas, from clumping) and quadratic probing, which
+// is what we implement by default.
+//
+// Type requirements: value_type is required to be Copy Constructible
+// and Default Constructible. It is not required to be (and commonly
+// isn't) Assignable.
+//
+// You probably shouldn't use this code directly. Use
+// <google/dense_hash_map> or <google/dense_hash_set> instead.
+
+// You can change the following below:
+// HT_OCCUPANCY_FLT -- how full before we double size
+// HT_EMPTY_FLT -- how empty before we halve size
+// HT_MIN_BUCKETS -- default smallest bucket size
+//
+// How to decide what values to use?
+// HT_EMPTY_FLT's default of .4 * OCCUPANCY_FLT, is probably good.
+// HT_MIN_BUCKETS is probably unnecessary since you can specify
+// (indirectly) the starting number of buckets at construct-time.
+// For HT_OCCUPANCY_FLT, you can use this chart to try to trade-off
+// expected lookup time to the space taken up. By default, this
+// code uses quadratic probing, though you can change it to linear
+// via _JUMP below if you really want to.
+//
+// From http://www.augustana.ca/~mohrj/courses/1999.fall/csc210/lecture_notes/hashing.html
+// NUMBER OF PROBES / LOOKUP Successful Unsuccessful
+// Quadratic collision resolution 1 - ln(1-L) - L/2 1/(1-L) - L - ln(1-L)
+// Linear collision resolution [1+1/(1-L)]/2 [1+1/(1-L)2]/2
+//
+// -- HT_OCCUPANCY_FLT -- 0.10 0.50 0.60 0.75 0.80 0.90 0.99
+// QUADRATIC COLLISION RES.
+// probes/successful lookup 1.05 1.44 1.62 2.01 2.21 2.85 5.11
+// probes/unsuccessful lookup 1.11 2.19 2.82 4.64 5.81 11.4 103.6
+// LINEAR COLLISION RES.
+// probes/successful lookup 1.06 1.5 1.75 2.5 3.0 5.5 50.5
+// probes/unsuccessful lookup 1.12 2.5 3.6 8.5 13.0 50.0 5000.0
+
+#ifndef _DENSEHASHTABLE_H_
+#define _DENSEHASHTABLE_H_
+
+// The probing method
+// Linear probing
+// #define JUMP_(key, num_probes) ( 1 )
+// Quadratic-ish probing
+#define JUMP_(key, num_probes) ( num_probes )
+
+
+// Hashtable class, used to implement the hashed associative containers
+// hash_set and hash_map.
+
+//#include <google/sparsehash/sparseconfig.h>
+//#include <Assert.h>
+#include "LogAssert.h"
+#include <stdlib.h> // for abort()
+#include <algorithm> // For swap(), eg
+#include <iostream> // For cerr
+#include <memory> // For uninitialized_fill, uninitialized_copy
+#include <utility> // for pair<>
+#include <iterator> // for facts about iterator tags
+#include "type_traits.h" // for true_type, integral_constant, etc.
+
+using std::pair;
+
+template <class Value, class Key, class HashFcn,
+ class ExtractKey, class EqualKey, class Alloc>
+class dense_hashtable;
+
+template <class V, class K, class HF, class ExK, class EqK, class A>
+struct dense_hashtable_iterator;
+
+template <class V, class K, class HF, class ExK, class EqK, class A>
+struct dense_hashtable_const_iterator;
+
+// We're just an array, but we need to skip over empty and deleted elements
+template <class V, class K, class HF, class ExK, class EqK, class A>
+struct dense_hashtable_iterator {
+ public:
+ typedef dense_hashtable_iterator<V,K,HF,ExK,EqK,A> iterator;
+ typedef dense_hashtable_const_iterator<V,K,HF,ExK,EqK,A> const_iterator;
+
+ typedef std::forward_iterator_tag iterator_category;
+ typedef V value_type;
+ typedef std::ptrdiff_t difference_type;
+ typedef size_t size_type;
+ typedef V& reference; // Value
+ typedef V* pointer;
+
+ // "Real" constructor and default constructor
+ dense_hashtable_iterator(const dense_hashtable<V,K,HF,ExK,EqK,A> *h,
+ pointer it, pointer it_end, bool advance)
+ : ht(h), pos(it), end(it_end) {
+ if (advance) advance_past_empty_and_deleted();
+ }
+ dense_hashtable_iterator() { }
+ // The default destructor is fine; we don't define one
+ // The default operator= is fine; we don't define one
+
+ // Happy dereferencer
+ reference operator*() const { return *pos; }
+ pointer operator->() const { return &(operator*()); }
+
+ // Arithmetic. The only hard part is making sure that
+ // we're not on an empty or marked-deleted array element
+ void advance_past_empty_and_deleted() {
+ while ( pos != end && (ht->test_empty(*this) || ht->test_deleted(*this)) )
+ ++pos;
+ }
+ iterator& operator++() {
+ Assert(pos != end); ++pos; advance_past_empty_and_deleted(); return *this;
+ }
+ iterator operator++(int) { iterator tmp(*this); ++*this; return tmp; }
+
+ // Comparison.
+ bool operator==(const iterator& it) const { return pos == it.pos; }
+ bool operator!=(const iterator& it) const { return pos != it.pos; }
+
+
+ // The actual data
+ const dense_hashtable<V,K,HF,ExK,EqK,A> *ht;
+ pointer pos, end;
+};
+
+
+// Now do it all again, but with const-ness!
+template <class V, class K, class HF, class ExK, class EqK, class A>
+struct dense_hashtable_const_iterator {
+ public:
+ typedef dense_hashtable_iterator<V,K,HF,ExK,EqK,A> iterator;
+ typedef dense_hashtable_const_iterator<V,K,HF,ExK,EqK,A> const_iterator;
+
+ typedef std::forward_iterator_tag iterator_category;
+ typedef V value_type;
+ typedef std::ptrdiff_t difference_type;
+ typedef size_t size_type;
+ typedef const V& reference; // Value
+ typedef const V* pointer;
+
+ // "Real" constructor and default constructor
+ dense_hashtable_const_iterator(const dense_hashtable<V,K,HF,ExK,EqK,A> *h,
+ pointer it, pointer it_end, bool advance)
+ : ht(h), pos(it), end(it_end) {
+ if (advance) advance_past_empty_and_deleted();
+ }
+ dense_hashtable_const_iterator() { }
+ // This lets us convert regular iterators to const iterators
+ dense_hashtable_const_iterator(const iterator &it)
+ : ht(it.ht), pos(it.pos), end(it.end) { }
+ // The default destructor is fine; we don't define one
+ // The default operator= is fine; we don't define one
+
+ // Happy dereferencer
+ reference operator*() const { return *pos; }
+ pointer operator->() const { return &(operator*()); }
+
+ // Arithmetic. The only hard part is making sure that
+ // we're not on an empty or marked-deleted array element
+ void advance_past_empty_and_deleted() {
+ while ( pos != end && (ht->test_empty(*this) || ht->test_deleted(*this)) )
+ ++pos;
+ }
+ const_iterator& operator++() {
+ Assert(pos != end); ++pos; advance_past_empty_and_deleted(); return *this;
+ }
+ const_iterator operator++(int) { const_iterator tmp(*this); ++*this; return tmp; }
+
+ // Comparison.
+ bool operator==(const const_iterator& it) const { return pos == it.pos; }
+ bool operator!=(const const_iterator& it) const { return pos != it.pos; }
+
+
+ // The actual data
+ const dense_hashtable<V,K,HF,ExK,EqK,A> *ht;
+ pointer pos, end;
+};
+
+template <class Value, class Key, class HashFcn,
+ class ExtractKey, class EqualKey, class Alloc>
+class dense_hashtable {
+ public:
+ typedef Key key_type;
+ typedef Value value_type;
+ typedef HashFcn hasher;
+ typedef EqualKey key_equal;
+
+ typedef size_t size_type;
+ typedef std::ptrdiff_t difference_type;
+ typedef value_type* pointer;
+ typedef const value_type* const_pointer;
+ typedef value_type& reference;
+ typedef const value_type& const_reference;
+ typedef dense_hashtable_iterator<Value, Key, HashFcn,
+ ExtractKey, EqualKey, Alloc>
+ iterator;
+
+ typedef dense_hashtable_const_iterator<Value, Key, HashFcn,
+ ExtractKey, EqualKey, Alloc>
+ const_iterator;
+
+ // How full we let the table get before we resize. Knuth says .8 is
+ // good -- higher causes us to probe too much, though saves memory
+ static const float HT_OCCUPANCY_FLT; // = 0.8;
+
+ // How empty we let the table get before we resize lower.
+ // (0.0 means never resize lower.)
+ // It should be less than OCCUPANCY_FLT / 2 or we thrash resizing
+ static const float HT_EMPTY_FLT; // = 0.4 * HT_OCCUPANCY_FLT
+
+ // Minimum size we're willing to let hashtables be.
+ // Must be a power of two, and at least 4.
+ // Note, however, that for a given hashtable, the initial size is a
+ // function of the first constructor arg, and may be >HT_MIN_BUCKETS.
+ static const size_t HT_MIN_BUCKETS = 32;
+
+
+ // ITERATOR FUNCTIONS
+ iterator begin() { return iterator(this, table,
+ table + num_buckets, true); }
+ iterator end() { return iterator(this, table + num_buckets,
+ table + num_buckets, true); }
+ const_iterator begin() const { return const_iterator(this, table,
+ table+num_buckets,true);}
+ const_iterator end() const { return const_iterator(this, table + num_buckets,
+ table+num_buckets,true);}
+
+ // ACCESSOR FUNCTIONS for the things we templatize on, basically
+ hasher hash_funct() const { return hash; }
+ key_equal key_eq() const { return equals; }
+
+ // Annoyingly, we can't copy values around, because they might have
+ // const components (they're probably pair<const X, Y>). We use
+ // explicit destructor invocation and placement new to get around
+ // this. Arg.
+ private:
+ void set_value(value_type* dst, const value_type& src) {
+ dst->~value_type();
+ new(dst) value_type(src);
+ }
+
+ void destroy_buckets(size_type first, size_type last) {
+ for ( ; first != last; ++first)
+ table[first].~value_type();
+ }
+
+ // DELETE HELPER FUNCTIONS
+ // This lets the user describe a key that will indicate deleted
+ // table entries. This key should be an "impossible" entry --
+ // if you try to insert it for real, you won't be able to retrieve it!
+ // (NB: while you pass in an entire value, only the key part is looked
+ // at. This is just because I don't know how to assign just a key.)
+ private:
+ void squash_deleted() { // gets rid of any deleted entries we have
+ if ( num_deleted ) { // get rid of deleted before writing
+ dense_hashtable tmp(*this); // copying will get rid of deleted
+ swap(tmp); // now we are tmp
+ }
+ Assert(num_deleted == 0);
+ }
+
+ public:
+ void set_deleted_key(const value_type &val) {
+ // the empty indicator (if specified) and the deleted indicator
+ // must be different
+ Assert(!use_empty || !equals(get_key(val), get_key(emptyval)));
+ // It's only safe to change what "deleted" means if we purge deleted guys
+ squash_deleted();
+ use_deleted = true;
+ set_value(&delval, val);
+ }
+ void clear_deleted_key() {
+ squash_deleted();
+ use_deleted = false;
+ }
+
+ // These are public so the iterators can use them
+ // True if the item at position bucknum is "deleted" marker
+ bool test_deleted(size_type bucknum) const {
+ // The num_deleted test is crucial for read(): after read(), the ht values
+ // are garbage, and we don't want to think some of them are deleted.
+ return (use_deleted && num_deleted > 0 &&
+ equals(get_key(delval), get_key(table[bucknum])));
+ }
+ bool test_deleted(const iterator &it) const {
+ return (use_deleted && num_deleted > 0 &&
+ equals(get_key(delval), get_key(*it)));
+ }
+ bool test_deleted(const const_iterator &it) const {
+ return (use_deleted && num_deleted > 0 &&
+ equals(get_key(delval), get_key(*it)));
+ }
+ // Set it so test_deleted is true. true if object didn't used to be deleted
+ // See below (at erase()) to explain why we allow const_iterators
+ bool set_deleted(const_iterator &it) {
+ Assert(use_deleted); // bad if set_deleted_key() wasn't called
+ bool retval = !test_deleted(it);
+ // &* converts from iterator to value-type
+ set_value(const_cast<value_type*>(&(*it)), delval);
+ return retval;
+ }
+ // Set it so test_deleted is false. true if object used to be deleted
+ bool clear_deleted(const_iterator &it) {
+ Assert(use_deleted); // bad if set_deleted_key() wasn't called
+ // happens automatically when we assign something else in its place
+ return test_deleted(it);
+ }
+
+ // EMPTY HELPER FUNCTIONS
+ // This lets the user describe a key that will indicate empty (unused)
+ // table entries. This key should be an "impossible" entry --
+ // if you try to insert it for real, you won't be able to retrieve it!
+ // (NB: while you pass in an entire value, only the key part is looked
+ // at. This is just because I don't know how to assign just a key.)
+ public:
+ // These are public so the iterators can use them
+ // True if the item at position bucknum is "empty" marker
+ bool test_empty(size_type bucknum) const {
+ Assert(use_empty); // we always need to know what's empty!
+ return equals(get_key(emptyval), get_key(table[bucknum]));
+ }
+ bool test_empty(const iterator &it) const {
+ Assert(use_empty); // we always need to know what's empty!
+ return equals(get_key(emptyval), get_key(*it));
+ }
+ bool test_empty(const const_iterator &it) const {
+ Assert(use_empty); // we always need to know what's empty!
+ return equals(get_key(emptyval), get_key(*it));
+ }
+
+ private:
+ // You can either set a range empty or an individual element
+ void set_empty(size_type bucknum) {
+ Assert(use_empty);
+ set_value(&table[bucknum], emptyval);
+ }
+ void fill_range_with_empty(value_type* table_start, value_type* table_end) {
+ // Like set_empty(range), but doesn't destroy previous contents
+ std::uninitialized_fill(table_start, table_end, emptyval);
+ }
+ void set_empty(size_type buckstart, size_type buckend) {
+ Assert(use_empty);
+ destroy_buckets(buckstart, buckend);
+ fill_range_with_empty(table + buckstart, table + buckend);
+ }
+
+ public:
+ // TODO(csilvers): change all callers of this to pass in a key instead,
+ // and take a const key_type instead of const value_type.
+ void set_empty_key(const value_type &val) {
+ // Once you set the empty key, you can't change it
+ Assert(!use_empty);
+ // The deleted indicator (if specified) and the empty indicator
+ // must be different.
+ Assert(!use_deleted || !equals(get_key(val), get_key(delval)));
+ use_empty = true;
+ set_value(&emptyval, val);
+
+ Assert(!table); // must set before first use
+ // num_buckets was set in constructor even though table was NULL
+ table = _Alval.allocate(num_buckets);
+ Assert(table);
+ fill_range_with_empty(table, table + num_buckets);
+ }
+
+ // FUNCTIONS CONCERNING SIZE
+ public:
+ size_type size() const { return num_elements - num_deleted; }
+ // Buckets are always a power of 2
+ size_type max_size() const { return (size_type(-1) >> 1U) + 1; }
+ bool empty() const { return size() == 0; }
+ size_type bucket_count() const { return num_buckets; }
+ size_type max_bucket_count() const { return max_size(); }
+ size_type nonempty_bucket_count() const { return num_elements; }
+
+ private:
+ // Because of the above, size_type(-1) is never legal; use it for errors
+ static const size_type ILLEGAL_BUCKET = size_type(-1);
+
+ private:
+ // This is the smallest size a hashtable can be without being too crowded
+ // If you like, you can give a min #buckets as well as a min #elts
+ size_type min_size(size_type num_elts, size_type min_buckets_wanted) {
+ size_type sz = HT_MIN_BUCKETS; // min buckets allowed
+ while ( sz < min_buckets_wanted || num_elts >= sz * HT_OCCUPANCY_FLT )
+ sz *= 2;
+ return sz;
+ }
+
+ // Used after a string of deletes
+ void maybe_shrink() {
+ Assert(num_elements >= num_deleted);
+ Assert((bucket_count() & (bucket_count()-1)) == 0); // is a power of two
+ Assert(bucket_count() >= HT_MIN_BUCKETS);
+
+ if ( (num_elements-num_deleted) < shrink_threshold &&
+ bucket_count() > HT_MIN_BUCKETS ) {
+ size_type sz = bucket_count() / 2; // find how much we should shrink
+ while ( sz > HT_MIN_BUCKETS &&
+ (num_elements - num_deleted) < sz * HT_EMPTY_FLT )
+ sz /= 2; // stay a power of 2
+ dense_hashtable tmp(*this, sz); // Do the actual resizing
+ swap(tmp); // now we are tmp
+ }
+ consider_shrink = false; // because we just considered it
+ }
+
+ // We'll let you resize a hashtable -- though this makes us copy all!
+ // When you resize, you say, "make it big enough for this many more elements"
+ void resize_delta(size_type delta, size_type min_buckets_wanted = 0) {
+ if ( consider_shrink ) // see if lots of deletes happened
+ maybe_shrink();
+ if ( bucket_count() > min_buckets_wanted &&
+ (num_elements + delta) <= enlarge_threshold )
+ return; // we're ok as we are
+
+ // Sometimes, we need to resize just to get rid of all the
+ // "deleted" buckets that are clogging up the hashtable. So when
+ // deciding whether to resize, count the deleted buckets (which
+ // are currently taking up room). But later, when we decide what
+ // size to resize to, *don't* count deleted buckets, since they
+ // get discarded during the resize.
+ const size_type needed_size = min_size(num_elements + delta,
+ min_buckets_wanted);
+ if ( needed_size > bucket_count() ) { // we don't have enough buckets
+ const size_type resize_to = min_size(num_elements - num_deleted + delta,
+ min_buckets_wanted);
+ dense_hashtable tmp(*this, resize_to);
+ swap(tmp); // now we are tmp
+ }
+ }
+
+ // Increase number of buckets, assuming value_type has trivial copy
+ // constructor and destructor. (Really, we want it to have "trivial
+ // move", because that's what realloc does. But there's no way to
+ // capture that using type_traits, so we pretend that move(x, y) is
+ // equivalent to "x.~T(); new(x) T(y);" which is pretty much
+ // correct, if a bit conservative.)
+ void expand_array(size_t resize_to, dense_hash_map_traits::true_type) {
+ value_type* new_table = _Alval.allocate(resize_to);
+ Assert(new_table);
+ if(table)
+ {
+ std::uninitialized_copy(table, table + num_buckets, new_table);
+ _Alval.deallocate(table, num_buckets);
+ }
+ fill_range_with_empty(new_table + num_buckets, new_table + resize_to);
+ table = new_table;
+ Assert(table);
+ }
+
+ // Increase number of buckets, without special assumptions about value_type.
+ // TODO(austern): make this exception safe. Handle exceptions from
+ // value_type's copy constructor.
+ void expand_array(size_t resize_to, dense_hash_map_traits::false_type) {
+ value_type* new_table = _Alval.allocate(resize_to);
+ Assert(new_table);
+ std::uninitialized_copy(table, table + std::min(num_buckets,resize_to), new_table);
+ fill_range_with_empty(new_table + num_buckets, new_table + resize_to);
+ destroy_buckets(0, num_buckets);
+ _Alval.deallocate(table, num_buckets);
+ table = new_table;
+ }
+
+ // Used to actually do the rehashing when we grow/shrink a hashtable
+ void copy_from(const dense_hashtable &ht, size_type min_buckets_wanted = 0) {
+ clear(); // clear table, set num_deleted to 0
+
+ // If we need to change the size of our table, do it now
+ const size_type resize_to = min_size(ht.size(), min_buckets_wanted);
+ if ( resize_to > bucket_count() ) { // we don't have enough buckets
+ typedef dense_hash_map_traits::integral_constant<bool,
+ (dense_hash_map_traits::has_trivial_copy<value_type>::value &&
+ dense_hash_map_traits::has_trivial_destructor<value_type>::value)>
+ realloc_ok; // we pretend mv(x,y) == "x.~T(); new(x) T(y)"
+ expand_array(resize_to, realloc_ok());
+ num_buckets = resize_to;
+ reset_thresholds();
+ }
+
+ // We use a normal iterator to get non-deleted bcks from ht
+ // We could use insert() here, but since we know there are
+ // no duplicates and no deleted items, we can be more efficient
+ Assert((bucket_count() & (bucket_count()-1)) == 0); // a power of two
+ for ( const_iterator it = ht.begin(); it != ht.end(); ++it ) {
+ size_type num_probes = 0; // how many times we've probed
+ size_type bucknum;
+ const size_type bucket_count_minus_one = bucket_count() - 1;
+ for (bucknum = hash(get_key(*it)) & bucket_count_minus_one;
+ !test_empty(bucknum); // not empty
+ bucknum = (bucknum + JUMP_(key, num_probes)) & bucket_count_minus_one) {
+ ++num_probes;
+ Assert(num_probes < bucket_count()); // or else the hashtable is full
+ }
+ set_value(&table[bucknum], *it); // copies the value to here
+ num_elements++;
+ }
+ }
+
+ // Required by the spec for hashed associative container
+ public:
+ // Though the docs say this should be num_buckets, I think it's much
+ // more useful as req_elements. As a special feature, calling with
+ // req_elements==0 will cause us to shrink if we can, saving space.
+ void resize(size_type req_elements) { // resize to this or larger
+ if ( consider_shrink || req_elements == 0 )
+ maybe_shrink();
+ if ( req_elements > num_elements )
+ return resize_delta(req_elements - num_elements, 0);
+ }
+
+
+ // CONSTRUCTORS -- as required by the specs, we take a size,
+ // but also let you specify a hashfunction, key comparator,
+ // and key extractor. We also define a copy constructor and =.
+ // DESTRUCTOR -- needs to free the table
+ explicit dense_hashtable(size_type n = 0,
+ const HashFcn& hf = HashFcn(),
+ const EqualKey& eql = EqualKey(),
+ const ExtractKey& ext = ExtractKey())
+ : hash(hf), equals(eql), get_key(ext), num_deleted(0),
+ use_deleted(false), use_empty(false),
+ delval(), emptyval(),
+ table(NULL), num_buckets(min_size(0, n)), num_elements(0) {
+ // table is NULL until emptyval is set. However, we set num_buckets
+ // here so we know how much space to allocate once emptyval is set
+ reset_thresholds();
+ }
+
+ // As a convenience for resize(), we allow an optional second argument
+ // which lets you make this new hashtable a different size than ht
+ dense_hashtable(const dense_hashtable& ht, size_type min_buckets_wanted = 0)
+ : hash(ht.hash), equals(ht.equals), get_key(ht.get_key), num_deleted(0),
+ use_deleted(ht.use_deleted), use_empty(ht.use_empty),
+ delval(ht.delval), emptyval(ht.emptyval),
+ table(NULL), num_buckets(0),
+ num_elements(0) {
+ reset_thresholds();
+ copy_from(ht, min_buckets_wanted); // copy_from() ignores deleted entries
+ }
+
+ dense_hashtable& operator= (const dense_hashtable& ht) {
+ if (&ht == this) return *this; // don't copy onto ourselves
+ clear();
+ hash = ht.hash;
+ equals = ht.equals;
+ get_key = ht.get_key;
+ use_deleted = ht.use_deleted;
+ use_empty = ht.use_empty;
+ set_value(&delval, ht.delval);
+ set_value(&emptyval, ht.emptyval);
+ copy_from(ht); // sets num_deleted to 0 too
+ return *this;
+ }
+
+ ~dense_hashtable() {
+ if (table) {
+ destroy_buckets(0, num_buckets);
+ _Alval.deallocate(table, num_buckets);
+ }
+ }
+
+ // Many STL algorithms use swap instead of copy constructors
+ void swap(dense_hashtable& ht) {
+ std::swap(hash, ht.hash);
+ std::swap(equals, ht.equals);
+ std::swap(get_key, ht.get_key);
+ std::swap(num_deleted, ht.num_deleted);
+ std::swap(use_deleted, ht.use_deleted);
+ std::swap(use_empty, ht.use_empty);
+ { value_type tmp; // for annoying reasons, swap() doesn't work
+ set_value(&tmp, delval);
+ set_value(&delval, ht.delval);
+ set_value(&ht.delval, tmp);
+ }
+ { value_type tmp; // for annoying reasons, swap() doesn't work
+ set_value(&tmp, emptyval);
+ set_value(&emptyval, ht.emptyval);
+ set_value(&ht.emptyval, tmp);
+ }
+ std::swap(table, ht.table);
+ std::swap(num_buckets, ht.num_buckets);
+ std::swap(num_elements, ht.num_elements);
+ reset_thresholds();
+ ht.reset_thresholds();
+ }
+
+ // It's always nice to be able to clear a table without deallocating it
+ void clear() {
+ if (table)
+ destroy_buckets(0, num_buckets);
+
+ size_type old_bucket_count = num_buckets;
+ num_buckets = min_size(0,0); // our new size
+ reset_thresholds();
+ value_type* new_table = _Alval.allocate(num_buckets);
+ Assert(new_table);
+ if(table)
+ _Alval.deallocate(table, old_bucket_count);
+ table = new_table;
+ Assert(table);
+ fill_range_with_empty(table, table + num_buckets);
+ num_elements = 0;
+ num_deleted = 0;
+ }
+
+ // Clear the table without resizing it.
+ // Mimicks the stl_hashtable's behaviour when clear()-ing in that it
+ // does not modify the bucket count
+ void clear_no_resize() {
+ if (table) {
+ set_empty(0, num_buckets);
+ }
+ // don't consider to shrink before another erase()
+ reset_thresholds();
+ num_elements = 0;
+ num_deleted = 0;
+ }
+
+ // LOOKUP ROUTINES
+ private:
+ // Returns a pair of positions: 1st where the object is, 2nd where
+ // it would go if you wanted to insert it. 1st is ILLEGAL_BUCKET
+ // if object is not found; 2nd is ILLEGAL_BUCKET if it is.
+ // Note: because of deletions where-to-insert is not trivial: it's the
+ // first deleted bucket we see, as long as we don't find the key later
+ pair<size_type, size_type> find_position(const key_type &key) const {
+ size_type num_probes = 0; // how many times we've probed
+ const size_type bucket_count_minus_one = bucket_count() - 1;
+ size_type bucknum = hash(key) & bucket_count_minus_one;
+ size_type insert_pos = ILLEGAL_BUCKET; // where we would insert
+ while ( 1 ) { // probe until something happens
+ if ( test_empty(bucknum) ) { // bucket is empty
+ if ( insert_pos == ILLEGAL_BUCKET ) // found no prior place to insert
+ return pair<size_type,size_type>(ILLEGAL_BUCKET, bucknum);
+ else
+ return pair<size_type,size_type>(ILLEGAL_BUCKET, insert_pos);
+
+ } else if ( test_deleted(bucknum) ) {// keep searching, but mark to insert
+ if ( insert_pos == ILLEGAL_BUCKET )
+ insert_pos = bucknum;
+
+ } else if ( equals(key, get_key(table[bucknum])) ) {
+ return pair<size_type,size_type>(bucknum, ILLEGAL_BUCKET);
+ }
+ ++num_probes; // we're doing another probe
+ bucknum = (bucknum + JUMP_(key, num_probes)) & bucket_count_minus_one;
+ Assert(num_probes < bucket_count()); // don't probe too many times!
+ }
+ }
+
+ public:
+ iterator find(const key_type& key) {
+ if ( size() == 0 ) return end();
+ pair<size_type, size_type> pos = find_position(key);
+ if ( pos.first == ILLEGAL_BUCKET ) // alas, not there
+ return end();
+ else
+ return iterator(this, table + pos.first, table + num_buckets, false);
+ }
+
+ const_iterator find(const key_type& key) const {
+ if ( size() == 0 ) return end();
+ pair<size_type, size_type> pos = find_position(key);
+ if ( pos.first == ILLEGAL_BUCKET ) // alas, not there
+ return end();
+ else
+ return const_iterator(this, table + pos.first, table+num_buckets, false);
+ }
+
+ // Counts how many elements have key key. For maps, it's either 0 or 1.
+ size_type count(const key_type &key) const {
+ pair<size_type, size_type> pos = find_position(key);
+ return pos.first == ILLEGAL_BUCKET ? 0 : 1;
+ }
+
+ // Likewise, equal_range doesn't really make sense for us. Oh well.
+ pair<iterator,iterator> equal_range(const key_type& key) {
+ const iterator pos = find(key); // either an iterator or end
+ return pair<iterator,iterator>(pos, pos);
+ }
+ pair<const_iterator,const_iterator> equal_range(const key_type& key) const {
+ const const_iterator pos = find(key); // either an iterator or end
+ return pair<iterator,iterator>(pos, pos);
+ }
+
+
+ // INSERTION ROUTINES
+ private:
+ // If you know *this is big enough to hold obj, use this routine
+ pair<iterator, bool> insert_noresize(const value_type& obj) {
+ const pair<size_type,size_type> pos = find_position(get_key(obj));
+ if ( pos.first != ILLEGAL_BUCKET) { // object was already there
+ return pair<iterator,bool>(iterator(this, table + pos.first,
+ table + num_buckets, false),
+ false); // false: we didn't insert
+ } else { // pos.second says where to put it
+ if ( test_deleted(pos.second) ) { // just replace if it's been del.
+ const_iterator delpos(this, table + pos.second, // shrug:
+ table + num_buckets, false);// shouldn't need const
+ clear_deleted(delpos);
+ Assert( num_deleted > 0);
+ --num_deleted; // used to be, now it isn't
+ } else {
+ ++num_elements; // replacing an empty bucket
+ }
+ set_value(&table[pos.second], obj);
+ return pair<iterator,bool>(iterator(this, table + pos.second,
+ table + num_buckets, false),
+ true); // true: we did insert
+ }
+ }
+
+ public:
+ // This is the normal insert routine, used by the outside world
+ pair<iterator, bool> insert(const value_type& obj) {
+ resize_delta(1); // adding an object, grow if need be
+ return insert_noresize(obj);
+ }
+
+ // When inserting a lot at a time, we specialize on the type of iterator
+ template <class InputIterator>
+ void insert(InputIterator f, InputIterator l) {
+ // specializes on iterator type
+ insert(f, l, typename std::iterator_traits<InputIterator>::iterator_category());
+ }
+
+ // Iterator supports operator-, resize before inserting
+ template <class ForwardIterator>
+ void insert(ForwardIterator f, ForwardIterator l,
+ std::forward_iterator_tag) {
+ size_type n = std::distance(f, l); // TODO(csilvers): standard?
+ resize_delta(n);
+ for ( ; n > 0; --n, ++f)
+ insert_noresize(*f);
+ }
+
+ // Arbitrary iterator, can't tell how much to resize
+ template <class InputIterator>
+ void insert(InputIterator f, InputIterator l,
+ std::input_iterator_tag) {
+ for ( ; f != l; ++f)
+ insert(*f);
+ }
+
+
+ // DELETION ROUTINES
+ size_type erase(const key_type& key) {
+ const_iterator pos = find(key); // shrug: shouldn't need to be const
+ if ( pos != end() ) {
+ Assert(!test_deleted(pos)); // or find() shouldn't have returned it
+ set_deleted(pos);
+ ++num_deleted;
+ consider_shrink = true; // will think about shrink after next insert
+ return 1; // because we deleted one thing
+ } else {
+ return 0; // because we deleted nothing
+ }
+ }
+
+ // This is really evil: really it should be iterator, not const_iterator.
+ // But...the only reason keys are const is to allow lookup.
+ // Since that's a moot issue for deleted keys, we allow const_iterators
+ void erase(const_iterator pos) {
+ if ( pos == end() ) return; // sanity check
+ if ( set_deleted(pos) ) { // true if object has been newly deleted
+ ++num_deleted;
+ consider_shrink = true; // will think about shrink after next insert
+ }
+ }
+
+ void erase(const_iterator f, const_iterator l) {
+ for ( ; f != l; ++f) {
+ if ( set_deleted(f) ) // should always be true
+ ++num_deleted;
+ }
+ consider_shrink = true; // will think about shrink after next insert
+ }
+
+
+ // COMPARISON
+ bool operator==(const dense_hashtable& ht) const {
+ if (size() != ht.size()) {
+ return false;
+ } else if (this == &ht) {
+ return true;
+ } else {
+ // Iterate through the elements in "this" and see if the
+ // corresponding element is in ht
+ for ( const_iterator it = begin(); it != end(); ++it ) {
+ const_iterator it2 = ht.find(get_key(*it));
+ if ((it2 == ht.end()) || (*it != *it2)) {
+ return false;
+ }
+ }
+ return true;
+ }
+ }
+ bool operator!=(const dense_hashtable& ht) const {
+ return !(*this == ht);
+ }
+
+
+ private:
+ // The actual data
+ hasher hash; // required by hashed_associative_container
+ key_equal equals;
+ ExtractKey get_key;
+ size_type num_deleted; // how many occupied buckets are marked deleted
+ bool use_deleted; // false until delval has been set
+ bool use_empty; // you must do this before you start
+ value_type delval; // which key marks deleted entries
+ value_type emptyval; // which key marks unused entries
+ value_type *table;
+ size_type num_buckets;
+ size_type num_elements;
+ size_type shrink_threshold; // num_buckets * HT_EMPTY_FLT
+ size_type enlarge_threshold; // num_buckets * HT_OCCUPANCY_FLT
+ bool consider_shrink; // true if we should try to shrink before next insert
+
+ Alloc _Alval; // allocator object for value_type
+
+ void reset_thresholds() {
+ enlarge_threshold = static_cast<size_type>(num_buckets*HT_OCCUPANCY_FLT);
+ shrink_threshold = static_cast<size_type>(num_buckets*HT_EMPTY_FLT);
+ consider_shrink = false; // whatever caused us to reset already considered
+ }
+};
+
+// We need a global swap as well
+template <class V, class K, class HF, class ExK, class EqK, class A>
+inline void swap(dense_hashtable<V,K,HF,ExK,EqK,A> &x,
+ dense_hashtable<V,K,HF,ExK,EqK,A> &y) {
+ x.swap(y);
+}
+
+#undef JUMP_
+
+template <class V, class K, class HF, class ExK, class EqK, class A>
+const typename dense_hashtable<V,K,HF,ExK,EqK,A>::size_type
+ dense_hashtable<V,K,HF,ExK,EqK,A>::ILLEGAL_BUCKET;
+
+// How full we let the table get before we resize. Knuth says .8 is
+// good -- higher causes us to probe too much, though saves memory
+template <class V, class K, class HF, class ExK, class EqK, class A>
+const float dense_hashtable<V,K,HF,ExK,EqK,A>::HT_OCCUPANCY_FLT = 0.5f;
+
+// How empty we let the table get before we resize lower.
+// It should be less than OCCUPANCY_FLT / 2 or we thrash resizing
+template <class V, class K, class HF, class ExK, class EqK, class A>
+const float dense_hashtable<V,K,HF,ExK,EqK,A>::HT_EMPTY_FLT = 0.4f *
+dense_hashtable<V,K,HF,ExK,EqK,A>::HT_OCCUPANCY_FLT;
+
+#endif /* _DENSEHASHTABLE_H_ */
diff --git a/Runtime/Utilities/dynamic_array.h b/Runtime/Utilities/dynamic_array.h
new file mode 100644
index 0000000..90573d9
--- /dev/null
+++ b/Runtime/Utilities/dynamic_array.h
@@ -0,0 +1,340 @@
+#pragma once
+
+#include "Runtime/Allocator/MemoryMacros.h"
+#include "Runtime/Utilities/StaticAssert.h"
+
+#include <memory> // std::uninitialized_fill
+
+// dynamic_array - simplified version of std::vector<T>
+//
+// features:
+// . always uses memcpy for copying elements. Your data structures must be simple and can't have internal pointers / rely on copy constructor.
+// . EASTL like push_back(void) implementation
+// Existing std STL implementations implement insertion operations by copying from an element.
+// For example, resize(size() + 1) creates a throw-away temporary object.
+// There is no way in existing std STL implementations to add an element to a container without implicitly or
+// explicitly providing one to copy from (aside from some existing POD optimizations).
+// For expensive-to-construct objects this creates a potentially serious performance problem.
+// . grows X2 on reallocation
+// . small code footprint
+// . clear actually deallocates memory
+// . resize does NOT initialize members!
+//
+// Changelog:
+// Added pop_back()
+// Added assign()
+// Added clear() - frees the data, use resize(0) to clear w/o freeing
+// zero allocation for empty array
+//
+//
+template<typename T>
+struct AlignOfType
+{
+ enum { align = ALIGN_OF(T) };
+};
+
+
+template <typename T, size_t align = AlignOfType<T>::align, MemLabelIdentifier defaultLabel = kMemDynamicArrayId>
+struct dynamic_array
+{
+public:
+ typedef T* iterator;
+ typedef const T* const_iterator;
+ typedef T value_type;
+ typedef size_t size_type;
+ typedef size_t difference_type;
+ typedef T& reference;
+ typedef const T& const_reference;
+
+public:
+
+ dynamic_array() : m_data(NULL), m_label(defaultLabel, NULL), m_size(0), m_capacity(0)
+ {
+ m_label = MemLabelId(defaultLabel, GET_CURRENT_ALLOC_ROOT_HEADER());
+ }
+
+ dynamic_array(MemLabelRef label) : m_data(NULL), m_label(label), m_size(0), m_capacity(0)
+ {
+ }
+
+ explicit dynamic_array (size_t size, MemLabelRef label)
+ : m_label(label), m_size(size), m_capacity (size)
+ {
+ m_data = allocate (size);
+ }
+
+ dynamic_array (size_t size, T const& init_value, MemLabelRef label)
+ : m_label(label), m_size (size), m_capacity (size)
+ {
+ m_data = allocate (size);
+ std::uninitialized_fill (m_data, m_data + size, init_value);
+ }
+
+ ~dynamic_array()
+ {
+ if (owns_data())
+ m_data = deallocate(m_data);
+ }
+
+ dynamic_array(const dynamic_array& other) : m_capacity(0), m_size(0), m_label(other.m_label)
+ {
+ //m_label.SetRootHeader(GET_CURRENT_ALLOC_ROOT_HEADER());
+ m_data = NULL;
+ assign(other.begin(), other.end());
+ }
+
+ dynamic_array& operator=(const dynamic_array& other)
+ {
+ // should not allocate memory unless we have to
+ assign(other.begin(), other.end());
+ return *this;
+ }
+
+ void clear()
+ {
+ if (owns_data())
+ m_data = deallocate(m_data);
+ m_size = 0;
+ m_capacity = 0;
+ }
+
+ void assign(const_iterator begin, const_iterator end)
+ {
+ Assert(begin<=end);
+
+ resize_uninitialized(end-begin);
+ memcpy(m_data, begin, m_size * sizeof(T));
+ }
+
+ void erase(iterator input_begin, iterator input_end)
+ {
+ Assert(input_begin <= input_end);
+ Assert(input_begin >= begin());
+ Assert(input_end <= end());
+
+ size_t leftOverSize = end() - input_end;
+ memmove(input_begin, input_end, leftOverSize * sizeof(T));
+ m_size -= input_end - input_begin;
+ }
+
+ iterator erase(iterator position)
+ {
+ Assert(position >= begin());
+ Assert(position < end());
+
+ size_t leftOverSize = end() - position - 1;
+ memmove(position, position+1, leftOverSize * sizeof(T));
+ m_size -= 1;
+
+ return position;
+ }
+
+ iterator insert(iterator insert_before, const_iterator input_begin, const_iterator input_end)
+ {
+ Assert(input_begin <= input_end);
+ Assert(insert_before >= begin());
+ Assert(insert_before <= end());
+
+ // resize (make sure that insertBefore does not get invalid in the meantime because of a reallocation)
+ size_t insert_before_index = insert_before - begin();
+ size_t elements_to_be_moved = size() - insert_before_index;
+ resize_uninitialized((input_end - input_begin) + size());
+ insert_before = begin() + insert_before_index;
+
+ size_t insertsize = input_end - input_begin;
+ // move to the end of where the inserted data will be
+ memmove(insert_before + insertsize, insert_before, elements_to_be_moved * sizeof(T));
+ // inject input data in the hole we just created
+ memcpy(insert_before, input_begin, insertsize * sizeof(T));
+
+ return insert_before;
+ }
+
+ iterator insert(iterator insertBefore, const T& t) { return insert(insertBefore, &t, &t + 1); }
+
+ void swap(dynamic_array& other) throw()
+ {
+ if (m_data) UNITY_TRANSFER_OWNERSHIP_TO_HEADER(m_data, m_label, other.m_label.GetRootHeader());
+ if (other.m_data) UNITY_TRANSFER_OWNERSHIP_TO_HEADER(other.m_data, other.m_label, m_label.GetRootHeader());
+ std::swap(m_data, other.m_data);
+ std::swap(m_size, other.m_size);
+ std::swap(m_capacity, other.m_capacity);
+ std::swap(m_label, other.m_label);
+ }
+
+ T& push_back()
+ {
+ if (++m_size > capacity())
+ reserve(std::max<size_t>(capacity()*2, 1));
+ return back();
+ }
+
+ void push_back(const T& t)
+ {
+ push_back() = t;
+ }
+
+ void pop_back()
+ {
+ Assert(m_size >= 1);
+ m_size--;
+ }
+
+ void resize_uninitialized(size_t size, bool double_on_resize = false)
+ {
+ m_size = size;
+ if (m_size <= capacity())
+ return;
+
+ if(double_on_resize && size < capacity()*2)
+ size = capacity()*2;
+ reserve(size);
+ }
+
+ void resize_initialized(size_t size, const T& t = T(), bool double_on_resize = false)
+ {
+ if (size > capacity())
+ {
+ size_t requested_size = size;
+ if(double_on_resize && size < capacity()*2)
+ requested_size = capacity()*2;
+ reserve(requested_size);
+ }
+
+ if (size > m_size)
+ std::uninitialized_fill (m_data + m_size, m_data + size, t);
+ m_size = size;
+ }
+
+ void reserve(size_t inCapacity)
+ {
+ if (capacity() >= inCapacity)
+ return;
+
+ if (owns_data())
+ {
+ m_capacity = inCapacity;
+ m_data = reallocate(m_data, inCapacity);
+ }
+ else
+ {
+ T* newData = allocate(inCapacity);
+ memcpy(newData, m_data, m_size * sizeof(T));
+
+ // Invalidate old non-owned data, since using the data from two places is most likely a really really bad idea.
+ #if DEBUGMODE
+ memset(m_data, 0xCD, capacity() * sizeof(T));
+ #endif
+
+ m_capacity = inCapacity; // and clear reference bit
+ m_data = newData;
+ }
+ }
+
+ void assign_external (T* begin, T* end)
+ {
+ if (owns_data())
+ m_data = deallocate(m_data);
+ m_size = m_capacity = reinterpret_cast<value_type*> (end) - reinterpret_cast<value_type*> (begin);
+ Assert(m_size < k_reference_bit);
+ m_capacity |= k_reference_bit;
+ m_data = begin;
+ }
+
+ void set_owns_data (bool ownsData)
+ {
+ if (ownsData)
+ m_capacity &= ~k_reference_bit;
+ else
+ m_capacity |= k_reference_bit;
+ }
+
+ void shrink_to_fit()
+ {
+ if (owns_data())
+ {
+ m_capacity = m_size;
+ m_data = reallocate(m_data, m_size);
+ }
+ }
+
+ const T& back() const { Assert (m_size != 0); return m_data[m_size - 1]; }
+ const T& front() const { Assert (m_size != 0); return m_data[0]; }
+
+ T& back() { Assert (m_size != 0); return m_data[m_size - 1]; }
+ T& front() { Assert (m_size != 0); return m_data[0]; }
+
+ T* data () { return m_data; }
+ T const* data () const { return m_data; }
+
+ bool empty () const { return m_size == 0; }
+ size_t size() const { return m_size; }
+ size_t capacity() const { return m_capacity & ~k_reference_bit; }
+
+ T const& operator[] (size_t index) const { DebugAssert(index < m_size); return m_data[index]; }
+ T& operator[] (size_t index) { DebugAssert(index < m_size); return m_data[index]; }
+
+ T const* begin() const { return m_data; }
+ T* begin() { return m_data; }
+
+ T const* end() const { return m_data + m_size; }
+ T* end() { return m_data + m_size; }
+
+ bool owns_data() { return (m_capacity & k_reference_bit) == 0; }
+
+ bool equals(const dynamic_array& other)
+ {
+ if(m_size != other.m_size)
+ return false;
+
+ for( int i = 0; i < m_size; i++)
+ {
+ if (m_data[i] != other.m_data[i])
+ return false;
+ }
+
+ return true;
+ }
+
+ void set_memory_label (MemLabelRef label)
+ {
+ Assert(m_data == NULL);
+ m_label = label;
+ }
+
+private:
+
+ static const size_t k_reference_bit = (size_t)1 << (sizeof (size_t) * 8 - 1);
+
+ T* allocate (size_t size)
+ {
+ // If you are getting this error then you are trying to allocate memory for an incomplete type
+ CompileTimeAssert(sizeof(T) != 0, "incomplete type");
+ CompileTimeAssert(align != 0, "incomplete type");
+
+ return static_cast<T*> (UNITY_MALLOC_ALIGNED (m_label, size * sizeof(T), align));
+ }
+
+ T* deallocate (T* data)
+ {
+ Assert(owns_data());
+ UNITY_FREE (m_label, data);
+ return NULL;
+ }
+
+ T* reallocate (T* data, size_t size)
+ {
+ // If you are getting this error then you are trying to allocate memory for an incomplete type
+ CompileTimeAssert(sizeof(T) != 0, "incomplete type");
+ CompileTimeAssert(align != 0, "incomplete type");
+
+ Assert(owns_data());
+ int alignof = static_cast<int>(align);
+ return static_cast<T*> (UNITY_REALLOC_ALIGNED(m_label, data, size * sizeof(T), alignof));
+ }
+
+ T* m_data;
+ MemLabelId m_label;
+ size_t m_size;
+ size_t m_capacity;
+};
diff --git a/Runtime/Utilities/dynamic_array_tests.cpp b/Runtime/Utilities/dynamic_array_tests.cpp
new file mode 100644
index 0000000..8169fd9
--- /dev/null
+++ b/Runtime/Utilities/dynamic_array_tests.cpp
@@ -0,0 +1,254 @@
+#include "UnityPrefix.h"
+
+#if ENABLE_UNIT_TESTS
+
+#include "External/UnitTest++/src/UnitTest++.h"
+#include "Runtime/Utilities/dynamic_array.h"
+#include "Runtime/Utilities/ArrayUtility.h"
+
+
+TEST (DynamicArray)
+{
+ // no allocation for empty array
+ dynamic_array<int> array;
+ CHECK_EQUAL (0, array.capacity ());
+
+ // push_back allocates
+ int j = 1;
+ array.push_back (j);
+ CHECK_EQUAL (1, array.size ());
+ CHECK (array.capacity () > 0);
+
+ // push_back(void)
+ int& i = array.push_back ();
+ i = 666;
+ CHECK_EQUAL(666, array.back ());
+
+ // clear frees memory?
+ array.clear ();
+ CHECK_EQUAL (0, array.size ());
+ CHECK_EQUAL (0, array.capacity ());
+
+ // 3 item list
+ j = 6;
+ array.push_back (j);
+ j = 7;
+ array.push_back (j);
+ j = 8;
+ array.push_back (j);
+
+ CHECK_EQUAL (3, array.size ());
+
+ // swapping
+ dynamic_array<int> ().swap (array);
+ CHECK_EQUAL (0, array.capacity ());
+ CHECK_EQUAL (0, array.size ());
+
+ // reserve
+ array.reserve (1024);
+ CHECK_EQUAL (1024, array.capacity ());
+ CHECK_EQUAL (0, array.size ());
+
+ // copy assignment
+ dynamic_array<int> array1;
+ j = 888;
+ array1.push_back (j);
+
+ array = array1;
+
+ CHECK_EQUAL (1, array.size ());
+ CHECK_EQUAL (888, array.back ());
+ CHECK_EQUAL (1, array1.size ());
+ CHECK_EQUAL (888, array1.back ());
+}
+
+TEST (DynamicArrayMisc)
+{
+ // no allocation for empty array
+ dynamic_array<int> array;
+ CHECK_EQUAL (0, array.capacity ());
+ CHECK (array.owns_data ());
+ CHECK (array.begin () == array.end ());
+ CHECK (array.empty ());
+
+ // push_back allocates
+ int j = 1;
+ array.push_back (j);
+ CHECK_EQUAL (1, array.size ());
+ CHECK (array.capacity () > 0);
+
+ // push_back(void)
+ int& i = array.push_back ();
+ i = 666;
+ CHECK_EQUAL(666, array.back ());
+
+ // clear frees memory?
+ array.clear ();
+ CHECK_EQUAL (0, array.size ());
+ CHECK_EQUAL (0, array.capacity ());
+
+ // 3 item list
+ array.push_back (6);
+ array.push_back (7);
+ array.push_back (8);
+
+ CHECK_EQUAL (3, array.size ());
+
+ // swapping
+ dynamic_array<int> ().swap (array);
+ CHECK_EQUAL (0, array.capacity ());
+ CHECK_EQUAL (0, array.size ());
+
+ // reserve
+ array.reserve (1024);
+ CHECK_EQUAL (1024, array.capacity ());
+ CHECK_EQUAL (0, array.size ());
+
+ // copy assignment
+ dynamic_array<int> array1;
+ j = 888;
+ array1.push_back (j);
+
+ array = array1;
+
+ CHECK_EQUAL (1, array.size ());
+ CHECK_EQUAL (888, array.back ());
+ CHECK_EQUAL (1, array1.size ());
+ CHECK_EQUAL (888, array1.back ());
+}
+
+
+TEST (DynamicArrayErase)
+{
+ dynamic_array<int> arr;
+ arr.push_back(1);
+ arr.push_back(2);
+ arr.push_back(3);
+ arr.push_back(4);
+ arr.push_back(5);
+ dynamic_array<int>::iterator it;
+
+ // erase first elem
+ it = arr.erase(arr.begin());
+ CHECK_EQUAL (2, *it);
+ CHECK_EQUAL (4, arr.size());
+ CHECK_EQUAL (2, arr[0]);
+ CHECK_EQUAL (3, arr[1]);
+ CHECK_EQUAL (4, arr[2]);
+ CHECK_EQUAL (5, arr[3]);
+
+ // erase 2nd to last elem
+ it = arr.erase(arr.end()-2);
+ CHECK_EQUAL (5, *it);
+ CHECK_EQUAL (3, arr.size());
+ CHECK_EQUAL (2, arr[0]);
+ CHECK_EQUAL (3, arr[1]);
+ CHECK_EQUAL (5, arr[2]);
+
+ // erase last elem
+ it = arr.erase(arr.end()-1);
+ CHECK_EQUAL (arr.end(), it);
+ CHECK_EQUAL (2, arr.size());
+ CHECK_EQUAL (2, arr[0]);
+ CHECK_EQUAL (3, arr[1]);
+}
+
+
+TEST (DynamicArrayEraseRange)
+{
+ dynamic_array<int> vs;
+ vs.resize_uninitialized(5);
+
+ vs[0] = 0;
+ vs[1] = 1;
+ vs[2] = 2;
+ vs[3] = 3;
+ vs[4] = 4;
+
+ vs.erase(vs.begin() + 1, vs.begin() + 4);
+ CHECK_EQUAL (2, vs.size());
+ CHECK_EQUAL (5, vs.capacity());
+ CHECK_EQUAL (0, vs[0]);
+ CHECK_EQUAL (4, vs[1]);
+}
+
+static void VerifyConsecutiveIntArray (dynamic_array<int>& vs, int size, int capacity)
+{
+ CHECK_EQUAL (capacity, vs.capacity());
+ CHECK_EQUAL (size, vs.size());
+ for (int i=0;i<vs.size();i++)
+ CHECK_EQUAL (i, vs[i]);
+}
+
+TEST (DynamicArrayInsertOnEmpty)
+{
+ dynamic_array<int> vs;
+ int vals[] = { 0, 1 };
+
+ vs.insert(vs.begin(), vals, vals + ARRAY_SIZE(vals));
+
+ VerifyConsecutiveIntArray(vs, 2, 2);
+}
+
+
+TEST (DynamicArrayInsert)
+{
+ dynamic_array<int> vs;
+ vs.resize_uninitialized(5);
+
+ vs[0] = 0;
+ vs[1] = 1;
+ vs[2] = 4;
+ vs[3] = 5;
+ vs[4] = 6;
+
+ int vals[] = { 2, 3 };
+
+ // inser two values
+ vs.insert(vs.begin() + 2, vals, vals + ARRAY_SIZE(vals));
+ VerifyConsecutiveIntArray(vs, 7, 7);
+
+ // empty insert
+ vs.insert(vs.begin() + 2, vals, vals);
+
+ VerifyConsecutiveIntArray(vs, 7, 7);
+}
+
+TEST (DynamicArrayResize)
+{
+ dynamic_array<int> vs;
+ vs.resize_initialized(3, 2);
+ CHECK_EQUAL (3, vs.capacity());
+ CHECK_EQUAL (3, vs.size());
+ CHECK_EQUAL (2, vs[0]);
+ CHECK_EQUAL (2, vs[1]);
+ CHECK_EQUAL (2, vs[2]);
+
+ vs.resize_initialized(6, 3);
+ CHECK_EQUAL (6, vs.capacity());
+ CHECK_EQUAL (6, vs.size());
+ CHECK_EQUAL (2, vs[0]);
+ CHECK_EQUAL (2, vs[1]);
+ CHECK_EQUAL (2, vs[2]);
+ CHECK_EQUAL (3, vs[3]);
+ CHECK_EQUAL (3, vs[4]);
+ CHECK_EQUAL (3, vs[5]);
+
+ vs.resize_initialized(5, 3);
+ CHECK_EQUAL (6, vs.capacity());
+ CHECK_EQUAL (5, vs.size());
+ CHECK_EQUAL (2, vs[0]);
+ CHECK_EQUAL (2, vs[1]);
+ CHECK_EQUAL (2, vs[2]);
+ CHECK_EQUAL (3, vs[3]);
+ CHECK_EQUAL (3, vs[4]);
+
+ vs.resize_initialized(2, 3);
+ CHECK_EQUAL (6, vs.capacity());
+ CHECK_EQUAL (2, vs.size());
+ CHECK_EQUAL (2, vs[0]);
+ CHECK_EQUAL (2, vs[1]);
+}
+
+
+#endif // #if ENABLE_UNIT_TESTS
diff --git a/Runtime/Utilities/dynamic_bitset.h b/Runtime/Utilities/dynamic_bitset.h
new file mode 100644
index 0000000..7dadb2c
--- /dev/null
+++ b/Runtime/Utilities/dynamic_bitset.h
@@ -0,0 +1,1144 @@
+#ifndef DYNAMIC_BITSET_H
+#define DYNAMIC_BITSET_H
+// (C) Copyright Chuck Allison and Jeremy Siek 2001, 2002.
+//
+// Permission to copy, use, modify, sell and distribute this software
+// is granted provided this copyright notice appears in all
+// copies. This software is provided "as is" without express or
+// implied warranty, and with no claim as to its suitability for any
+// purpose.
+
+// With optimizations, bug fixes, and improvements by Gennaro Prota.
+
+// See http://www.boost.org/libs/dynamic_bitset for documentation.
+
+// -------------------------------------
+// CHANGE LOG:
+//
+// - corrected workaround for Dinkum lib's allocate() [GP]
+// - changed macro test for old iostreams [GP]
+// - removed #include <vector> for now. [JGS]
+// - Added __GNUC__ to compilers that cannot handle the constructor from basic_string. [JGS]
+// - corrected to_block_range [GP]
+// - corrected from_block_range [GP]
+// - Removed __GNUC__ from compilers that cannot handle the constructor
+// from basic_string and added the workaround suggested by GP. [JGS]
+// - Removed __BORLANDC__ from the #if around the basic_string
+// constructor. Luckily the fix by GP for g++ also fixes Borland. [JGS]
+
+#include <cassert>
+#include <string>
+#include <cstring> // for memset, memcpy, memcmp, etc.
+#include <algorithm> // for std::swap, std::min, std::copy, std::fill
+#include <memory> // for std::swap, std::min, std::copy, std::fill
+#include <stdlib.h>
+#include "LogAssert.h"
+
+namespace std
+{
+ typedef ::size_t size_t;
+}
+// (C) Copyright Chuck Allison and Jeremy Siek 2001, 2002.
+//
+// Permission to copy, use, modify, sell and distribute this software
+// is granted provided this copyright notice appears in all
+// copies. This software is provided "as is" without express or
+// implied warranty, and with no claim as to its suitability for any
+// purpose.
+
+// With optimizations by Gennaro Prota.
+
+ class dynamic_bitset_base
+ {
+ typedef std::size_t size_type;
+ public:
+#if defined(LINUX) && (defined (__LP64__) || defined(_AMD64_))
+ typedef unsigned int Block;
+#else
+ typedef unsigned long Block;
+#endif
+ enum { bits_per_block = 8 * sizeof(Block) };
+
+ dynamic_bitset_base()
+ : m_bits(0), m_num_bits(0), m_num_blocks(0) { }
+
+ dynamic_bitset_base(size_type num_bits) :
+ m_num_bits(num_bits),
+ m_num_blocks(calc_num_blocks(num_bits))
+ {
+ if (m_num_blocks != 0)
+ {
+ m_bits = new Block[m_num_blocks];
+ memset(m_bits, 0, m_num_blocks * sizeof(Block)); // G.P.S. ask to Jeremy
+ }
+ else
+ m_bits = 0;
+ }
+ ~dynamic_bitset_base() {
+ delete []m_bits;;
+ }
+
+ Block* m_bits;
+ size_type m_num_bits;
+ size_type m_num_blocks;
+
+ static size_type word(size_type bit) { return bit / bits_per_block; } // [gps]
+ static size_type offset(size_type bit){ return bit % bits_per_block; } // [gps]
+ static Block mask1(size_type bit) { return Block(1) << offset(bit); }
+ static Block mask0(size_type bit) { return ~(Block(1) << offset(bit)); }
+ static size_type calc_num_blocks(size_type num_bits)
+ { return (num_bits + bits_per_block - 1) / bits_per_block; }
+ };
+
+
+ // ------- count table implementation --------------
+
+ typedef unsigned char byte_t;
+
+ template <bool bogus = true>
+ struct bitcount {
+ typedef byte_t element_type;
+ static const byte_t table[];
+
+ };
+ //typedef count<true> table_t;
+
+
+ // the table: wrapped in a class template, so
+ // that it is only instantiated if/when needed
+ //
+ template <bool bogus>
+ const byte_t bitcount<bogus>::table[] =
+ {
+ // Automatically generated by GPTableGen.exe v.1.0
+ //
+ 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
+ 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
+ 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
+ 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
+ 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
+ 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
+ 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
+ 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8
+ };
+
+
+ // -------------------------------------------------------
+ template <typename BlockInputIterator>
+ std::size_t initial_num_blocks(BlockInputIterator first,
+ BlockInputIterator last)
+ {
+ std::size_t n = 0;
+ while (first != last)
+ ++first, ++n;
+ return n;
+ }
+
+class dynamic_bitset : public dynamic_bitset_base
+{
+ public:
+
+ typedef Block block_type;
+ typedef std::size_t size_type;
+ enum { bits_per_block = 8 * sizeof(Block) };
+
+ // reference to a bit
+ class reference
+ {
+ friend class dynamic_bitset;
+ dynamic_bitset* bs;
+ size_type bit;
+ reference(); // intentionally not implemented
+ reference(dynamic_bitset& bs_, size_type bit_) : bs(&bs_), bit(bit_){ }
+ public:
+ reference& operator=(bool value) // for b[i] = x
+ {
+ if (value)
+ bs->set(bit);
+ else
+ bs->reset(bit);
+ return *this;
+ }
+ reference& operator|=(bool value) // for b[i] |= x
+ {
+ if (value)
+ bs->set(bit);
+ return *this;
+ }
+ reference& operator&=(bool value) // for b[i] &= x
+ {
+ if (! (value && bs->test(bit)))
+ bs->reset(bit);
+ return *this;
+ }
+ reference& operator^=(bool value) // for b[i] ^= x
+ {
+ bs->set(bit, bs->test(bit) ^ value);
+ return *this;
+ }
+ reference& operator-=(bool value) // for b[i] -= x
+ {
+ if (!value)
+ bs->reset(bit);
+ return *this;
+ }
+ reference& operator=(const reference& j) // for b[i] = b[j]
+ {
+ if (j.bs->test(j.bit))
+ bs->set(bit);
+ else
+ bs->reset(bit);
+ return *this;
+ }
+ reference& operator|=(const reference& j) // for b[i] |= b[j]
+ {
+ if (j.bs->test(j.bit))
+ bs->set(bit);
+ return *this;
+ }
+ reference& operator&=(const reference& j) // for b[i] &= b[j]
+ {
+ if (! (j.bs->test(j.bit) && bs->test(bit)))
+ bs->reset(bit);
+ return *this;
+ }
+ reference& operator^=(const reference& j) // for b[i] ^= b[j]
+ {
+ bs->set(bit, bs->test(bit) ^ j.bs->test(j.bit));
+ return *this;
+ }
+ reference& operator-=(const reference& j) // for b[i] -= b[j]
+ {
+ if (!j.bs->test(j.bit))
+ bs->reset(bit);
+ return *this;
+ }
+ bool operator~() const // flips the bit
+ {
+ return ! bs->test(bit);
+ }
+ operator bool() const // for x = b[i]
+ {
+ return bs->test(bit);
+ }
+ reference& flip() // for b[i].flip();
+ {
+ bs->flip(bit);
+ return *this;
+ }
+ };
+ typedef bool const_reference;
+
+ dynamic_bitset ();
+ explicit
+ dynamic_bitset(size_type num_bits, unsigned long value = 0);
+
+ // The parenthesis around std::basic_string<CharT, Traits, Alloc>::npos
+ // in the code below are to avoid a g++ 3.2 bug and a Borland bug. -JGS
+ template <typename String>
+ explicit
+ dynamic_bitset(const String& s,
+ typename String::size_type pos = 0,
+ typename String::size_type n
+ = (String::npos))
+ : dynamic_bitset_base
+ (std::min(n, s.size() - pos))
+ {
+ // Locate sub string
+ AssertIf (pos > s.length());
+ from_string(s, pos, std::min(n, s.size() - pos));
+ }
+
+ // The first bit in *first is the least significant bit, and the
+ // last bit in the block just before *last is the most significant bit.
+ template <typename BlockInputIterator>
+ dynamic_bitset(BlockInputIterator first, BlockInputIterator last)
+ : dynamic_bitset_base
+ (initial_num_blocks(first, last)
+ * bits_per_block)
+ {
+ if (first != last) {
+ if (this->m_num_bits == 0) { // dealing with input iterators
+ this->append(first, last);
+ } else {
+ // dealing with forward iterators, memory has been allocated
+ for (std::size_t i = 0; first != last; ++first, ++i)
+ set_block_(i, *first);
+ }
+ }
+ }
+
+
+ // copy constructor
+ dynamic_bitset(const dynamic_bitset& b);
+
+ void swap(dynamic_bitset& b);
+
+ dynamic_bitset& operator=(const dynamic_bitset& b);
+
+ // size changing operations
+ void resize(size_type num_bits, bool value = false);
+ void clear();
+ void push_back(bool bit);
+ void append(Block block);
+
+ // This is declared inside the class to avoid compiler bugs.
+ template <typename BlockInputIterator>
+ void append(BlockInputIterator first, BlockInputIterator last)
+ {
+ if (first != last) {
+ std::size_t nblocks = initial_num_blocks(first, last);
+ if (nblocks == 0) { // dealing with input iterators
+ for (; first != last; ++first)
+ append(*first);
+ } else { // dealing with forward iterators
+ if (size() % bits_per_block == 0) {
+ std::size_t old_nblocks = this->m_num_blocks;
+ resize(size() + nblocks * bits_per_block);
+ for (std::size_t i = old_nblocks; first != last; ++first)
+ set_block_(i++, *first);
+ } else {
+ // probably should optimize this,
+ // but I'm sick of bit twiddling
+ for (; first != last; ++first)
+ append(*first);
+ }
+ }
+ }
+ }
+
+
+ // bitset operations
+ dynamic_bitset& operator&=(const dynamic_bitset& b);
+ dynamic_bitset& operator|=(const dynamic_bitset& b);
+ dynamic_bitset& operator^=(const dynamic_bitset& b);
+ dynamic_bitset& operator-=(const dynamic_bitset& b);
+ dynamic_bitset& operator<<=(size_type n);
+ dynamic_bitset& operator>>=(size_type n);
+ dynamic_bitset operator<<(size_type n) const;
+ dynamic_bitset operator>>(size_type n) const;
+
+ // basic bit operations
+ dynamic_bitset& set(size_type n, bool val = true);
+ dynamic_bitset& set();
+ dynamic_bitset& reset(size_type n);
+ dynamic_bitset& reset();
+ dynamic_bitset& flip(size_type n);
+ dynamic_bitset& flip();
+ bool test(size_type n) const;
+ bool any() const;
+ bool none() const;
+ dynamic_bitset operator~() const;
+ size_type count() const;
+
+ // subscript
+ reference operator[](size_type pos) { return reference(*this, pos); }
+ bool operator[](size_type pos) const
+ {
+ #if UNITY_EDITOR
+ if (pos < this->m_num_bits)
+ return test_(pos);
+ else
+ {
+ ErrorString("dynamic_bitset.test bit out of bounds");
+ return false;
+ }
+ #else
+ AssertIf(pos >= this->m_num_bits);
+ return test_(pos);
+ #endif
+ }
+
+ unsigned long to_ulong() const;
+
+ size_type size() const;
+ size_type num_blocks() const;
+
+ bool is_subset_of(const dynamic_bitset& a) const;
+ bool is_proper_subset_of(const dynamic_bitset& a) const;
+
+ void m_zero_unused_bits();
+
+
+private:
+ void set_(size_type bit);
+ bool set_(size_type bit, bool val);
+ void reset_(size_type bit);
+ bool test_(size_type bit) const;
+ void set_block_(size_type blocknum, Block b);
+
+public:
+
+ // This is templated on the whole String instead of just CharT,
+ // Traits, Alloc to avoid compiler bugs.
+ template <typename String>
+ void from_string(const String& s, typename String::size_type pos,
+ typename String::size_type rlen)
+ {
+ reset(); // bugfix [gps]
+ size_type const tot = std::min (rlen, s.length()); // bugfix [gps]
+
+ // Assumes string contains only 0's and 1's
+ for (size_type i = 0; i < tot; ++i) {
+ if (s[pos + tot - i - 1] == '1') {
+ set_(i);
+ } else {
+ AssertIf(s[pos + tot - i - 1] != '0');
+ }
+ }
+ }
+
+};
+
+// Global Functions:
+
+// comparison
+inline bool operator!=(const dynamic_bitset& a,
+ const dynamic_bitset& b);
+
+inline bool operator<=(const dynamic_bitset& a,
+ const dynamic_bitset& b);
+
+inline bool operator>(const dynamic_bitset& a,
+ const dynamic_bitset& b);
+
+inline bool operator>=(const dynamic_bitset& a,
+ const dynamic_bitset& b);
+
+// bitset operations
+inline dynamic_bitset
+operator&(const dynamic_bitset& b1,
+ const dynamic_bitset& b2);
+
+inline dynamic_bitset
+operator|(const dynamic_bitset& b1,
+ const dynamic_bitset& b2);
+
+inline dynamic_bitset
+operator^(const dynamic_bitset& b1,
+ const dynamic_bitset& b2);
+
+inline dynamic_bitset
+operator-(const dynamic_bitset& b1,
+ const dynamic_bitset& b2);
+
+
+template <typename String>
+void
+to_string(const dynamic_bitset& b,
+ String& s);
+
+template <typename BlockOutputIterator>
+void
+to_block_range(const dynamic_bitset& b,
+ BlockOutputIterator result);
+
+template <typename BlockIterator>
+inline void
+from_block_range(BlockIterator first, BlockIterator last,
+ dynamic_bitset& result);
+
+
+//=============================================================================
+// dynamic_bitset implementation
+
+
+//-----------------------------------------------------------------------------
+// constructors, etc.
+
+inline dynamic_bitset::dynamic_bitset()
+ : dynamic_bitset_base(0) { }
+
+inline dynamic_bitset::
+dynamic_bitset(size_type num_bits, unsigned long value)
+ : dynamic_bitset_base(num_bits)
+{
+ const size_type M = std::min(sizeof(unsigned long) * 8, num_bits);
+ for(size_type i = 0; i < M; ++i, value >>= 1) // [G.P.S.] to be optimized
+ if ( value & 0x1 )
+ set_(i);
+}
+
+// copy constructor
+inline dynamic_bitset::
+dynamic_bitset(const dynamic_bitset& b)
+ : dynamic_bitset_base(b.size())
+{
+ memcpy(this->m_bits, b.m_bits, this->m_num_blocks * sizeof(Block));
+}
+
+inline void dynamic_bitset::
+swap(dynamic_bitset& b)
+{
+ std::swap(this->m_bits, b.m_bits);
+ std::swap(this->m_num_bits, b.m_num_bits);
+ std::swap(this->m_num_blocks, b.m_num_blocks);
+}
+
+inline dynamic_bitset& dynamic_bitset::
+operator=(const dynamic_bitset& b)
+{
+ dynamic_bitset tmp(b);
+ this->swap(tmp);
+ return *this;
+}
+
+//-----------------------------------------------------------------------------
+// size changing operations
+
+inline void dynamic_bitset::
+resize(size_type num_bits, bool value)
+{
+ if (num_bits == size())
+ return;
+ if (num_bits == 0)
+ {
+ this->m_num_bits = 0;
+ this->m_num_blocks = 0;
+ delete this->m_bits;
+ this->m_bits = 0;
+ return;
+ }
+ size_type new_nblocks = this->calc_num_blocks(num_bits);
+ Block* d = new Block[new_nblocks];
+ if (num_bits < size()) { // shrink
+ std::copy(this->m_bits, this->m_bits + new_nblocks, d);
+ std::swap(d, this->m_bits);
+ delete []d;
+ } else { // grow
+ std::copy(this->m_bits, this->m_bits + this->m_num_blocks, d);
+ Block val = value? ~static_cast<Block>(0) : static_cast<Block>(0);
+ std::fill(d + this->m_num_blocks, d + new_nblocks, val);
+ std::swap(d, this->m_bits);
+ for (std::size_t i = this->m_num_bits;
+ i < this->m_num_blocks * bits_per_block; ++i)
+ set_(i, value);
+ if (d != 0)
+ delete []d;
+ }
+ this->m_num_bits = num_bits;
+ this->m_num_blocks = this->calc_num_blocks(num_bits);
+ m_zero_unused_bits();
+}
+
+inline void dynamic_bitset::
+clear()
+{
+ if (this->m_bits != 0) {
+ delete this->m_bits;
+ this->m_bits = 0;
+ this->m_num_bits = 0;
+ this->m_num_blocks = 0;
+ }
+}
+
+
+inline void dynamic_bitset::
+push_back(bool bit)
+{
+ this->resize(this->size() + 1);
+ set_(this->size() - 1, bit);
+}
+
+inline void dynamic_bitset::
+append(Block value)
+{
+ std::size_t old_size = size();
+ resize(old_size + bits_per_block);
+ if (size() % bits_per_block == 0)
+ set_block_(this->m_num_blocks - 1, value);
+ else {
+ // G.P.S. to be optimized
+ for (std::size_t i = old_size; i < size(); ++i, value >>= 1)
+ set_(i, value & 1);
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// bitset operations
+inline dynamic_bitset&
+dynamic_bitset::operator&=(const dynamic_bitset& rhs)
+{
+ AssertIf(size() != rhs.size());
+ for (size_type i = 0; i < this->m_num_blocks; ++i)
+ this->m_bits[i] &= rhs.m_bits[i];
+ return *this;
+}
+
+inline dynamic_bitset&
+dynamic_bitset::operator|=(const dynamic_bitset& rhs)
+{
+ AssertIf(size() != rhs.size());
+ for (size_type i = 0; i < this->m_num_blocks; ++i)
+ this->m_bits[i] |= rhs.m_bits[i];
+ m_zero_unused_bits();
+ return *this;
+}
+
+inline dynamic_bitset&
+dynamic_bitset::operator^=(const dynamic_bitset& rhs)
+{
+ AssertIf(size() != rhs.size());
+ for (size_type i = 0; i < this->m_num_blocks; ++i)
+ this->m_bits[i] ^= rhs.m_bits[i];
+ m_zero_unused_bits();
+ return *this;
+}
+
+inline dynamic_bitset&
+dynamic_bitset::operator-=(const dynamic_bitset& rhs)
+{
+ AssertIf(size() != rhs.size());
+ for (size_type i = 0; i < this->m_num_blocks; ++i)
+ this->m_bits[i] = this->m_bits[i] & ~rhs.m_bits[i];
+ m_zero_unused_bits();
+ return *this;
+}
+
+inline dynamic_bitset&
+dynamic_bitset::operator<<=(size_type n)
+{
+ if (n >= this->m_num_bits)
+ return reset();
+ //else
+ if (n > 0)
+ {
+ size_type const last = this->m_num_blocks - 1; // m_num_blocks is >= 1
+ size_type const div = n / bits_per_block; // div is <= last
+ size_type const r = n % bits_per_block;
+
+ // PRE: div != 0 or r != 0
+
+ if (r != 0) {
+
+ block_type const rs = bits_per_block - r;
+
+ for (size_type i = last-div; i>0; --i) {
+ this->m_bits[i+div] = (this->m_bits[i] << r) | (this->m_bits[i-1] >> rs);
+ }
+ this->m_bits[div] = this->m_bits[0] << r;
+
+ }
+ else {
+ for (size_type i = last-div; i>0; --i) {
+ this->m_bits[i+div] = this->m_bits[i];
+ }
+ this->m_bits[div] = this->m_bits[0];
+ }
+
+
+ // div blocks are zero filled at the less significant end
+ std::fill(this->m_bits, this->m_bits+div, static_cast<block_type>(0));
+
+
+ }
+
+ return *this;
+
+
+}
+
+
+
+
+
+
+
+// NOTE: this assumes that within a single block bits are
+// numbered from right to left. G.P.S.
+//
+// static Block offset(size_type bit)
+// { return bit % bits_per_block; }
+//
+//
+// In the implementation below the 'if (r != 0)' is logically
+// unnecessary. It's there as an optimization only: in fact
+// for r==0 the first branch becomes the second one with the
+// b[last-div] = b[last] >> r; statement that does the work of
+// the last iteration.
+//
+inline
+dynamic_bitset & dynamic_bitset::operator>>=(size_type n) {
+ if (n >= this->m_num_bits) {
+ return reset();
+ }
+ //else
+ if (n>0){
+
+ size_type const last = this->m_num_blocks - 1; // m_num_blocks is >= 1
+ size_type const div = n / bits_per_block; // div is <= last
+ size_type const r = n % bits_per_block;
+
+ // PRE: div != 0 or r != 0
+
+ if (r != 0) {
+
+ block_type const ls = bits_per_block - r;
+
+ for (size_type i = div; i < last; ++i) {
+ this->m_bits[i-div] = (this->m_bits[i] >> r) | (this->m_bits[i+1] << ls);
+ }
+ // r bits go to zero
+ this->m_bits[last-div] = this->m_bits[last] >> r;
+ }
+
+ else {
+ for (size_type i = div; i <= last; ++i) {
+ this->m_bits[i-div] = this->m_bits[i];
+ }
+ // note the '<=': the last iteration 'absorbs'
+ // this->m_bits[last-div] = this->m_bits[last] >> 0;
+ }
+
+
+
+ // div blocks are zero filled at the most significant end
+ std::fill(this->m_bits+(this->m_num_blocks-div), this->m_bits+this->m_num_blocks, static_cast<block_type>(0));
+ }
+
+ return *this;
+}
+
+
+
+
+
+
+
+inline dynamic_bitset
+dynamic_bitset::operator<<(size_type n) const
+{
+ dynamic_bitset r(*this);
+ return r <<= n;
+}
+
+inline dynamic_bitset
+dynamic_bitset::operator>>(size_type n) const
+{
+ dynamic_bitset r(*this);
+ return r >>= n;
+}
+
+
+//-----------------------------------------------------------------------------
+// basic bit operations
+
+inline dynamic_bitset&
+dynamic_bitset::set(size_type pos, bool val)
+{
+ AssertIf(pos >= this->m_num_bits);
+ set_(pos, val);
+ return *this;
+}
+
+inline dynamic_bitset&
+dynamic_bitset::set()
+{
+ if (this->m_num_bits > 0) {
+ using namespace std;
+ memset(this->m_bits, ~0u, this->m_num_blocks * sizeof(this->m_bits[0]));
+ m_zero_unused_bits();
+ }
+ return *this;
+}
+
+inline dynamic_bitset&
+dynamic_bitset::reset(size_type pos)
+{
+ AssertIf(pos >= this->m_num_bits);
+ reset_(pos);
+ return *this;
+}
+
+inline dynamic_bitset&
+dynamic_bitset::reset()
+{
+ if (this->m_num_bits > 0) {
+ using namespace std;
+ memset(this->m_bits, 0, this->m_num_blocks * sizeof(this->m_bits[0]));
+ }
+ return *this;
+}
+
+inline dynamic_bitset&
+dynamic_bitset::flip(size_type pos)
+{
+ AssertIf(pos >= this->m_num_bits);
+ this->m_bits[this->word(pos)] ^= this->mask1(pos);
+ return *this;
+}
+
+inline dynamic_bitset&
+dynamic_bitset::flip()
+{
+ for (size_type i = 0; i < this->m_num_blocks; ++i)
+ this->m_bits[i] = ~this->m_bits[i];
+ m_zero_unused_bits();
+ return *this;
+}
+
+inline bool dynamic_bitset::test(size_type pos) const
+{
+ #if UNITY_EDITOR
+ if (pos < this->m_num_bits)
+ return test_(pos);
+ else
+ {
+ ErrorString("dynamic_bitset.test bit out of bounds");
+ return false;
+ }
+ #else
+ AssertIf(pos >= this->m_num_bits);
+ return test_(pos);
+ #endif
+}
+
+inline bool dynamic_bitset::any() const
+{
+ for (size_type i = 0; i < this->m_num_blocks; ++i)
+ if (this->m_bits[i])
+ return 1;
+ return 0;
+}
+
+inline bool dynamic_bitset::none() const
+{
+ return !any();
+}
+
+inline dynamic_bitset
+dynamic_bitset::operator~() const
+{
+ dynamic_bitset b(*this);
+ b.flip();
+ return b;
+}
+
+
+/* snipped: [gps]
+
+The following is the straightforward implementation of count(), which
+we leave here in a comment for documentation purposes.
+
+template <typename Block, typename Allocator>
+typename dynamic_bitset::size_type
+dynamic_bitset::count() const
+{
+ size_type sum = 0;
+ for (size_type i = 0; i != this->m_num_bits; ++i)
+ if (test_(i))
+ ++sum;
+ return sum;
+}
+
+The actual algorithm used is based on using a lookup
+table.
+
+
+ The basic idea of the method is to pick up X bits at a time
+ from the internal array of blocks and consider those bits as
+ the binary representation of a number N. Then, to use a table
+ of 1<<X elements where table[N] is the number of '1' digits
+ in the binary representation of N (i.e. in our X bits).
+
+ Note that the table can be oversized (i.e. can even have more
+ than 1<<X elements; in that case only the first 1<<X will be
+ actually used) but it cannot be undersized.
+ In this implementation X is 8 (but can be easily changed: you
+ just have to change the definition of count<>::max_bits) and
+ the internal array of blocks is seen as an array of bytes: if
+ a byte has exactly 8 bits then it's enough to sum the value
+ of table[B] for each byte B. Otherwise 8 bits at a time are
+ 'extracted' from each byte by using another loop. As a further
+ efficiency consideration note that even if you have, let's say,
+ 32-bit chars the inner loop will not do 4 (i.e. 32/8) iterations,
+ unless you have at least one bit set in the highest 8 bits of the
+ byte.
+
+ Note also that the outmost if/else is not necessary but is there
+ to help the optimizer (and one of the two branches is always dead
+ code).
+
+ Aras: hardcoded table to be always max_bits=8. To help not so good compilers.
+
+*/
+
+
+inline dynamic_bitset::size_type
+dynamic_bitset::count() const
+{
+ const byte_t * p = reinterpret_cast<const byte_t*>(this->m_bits);
+ const byte_t * past_end = p + this->m_num_blocks * sizeof(Block);
+
+ size_type num = 0;
+
+ while (p < past_end) {
+ num += bitcount<>::table[*p];
+ ++p;
+ }
+
+ return num;
+}
+
+
+//-----------------------------------------------------------------------------
+// conversions
+
+// take as ref param instead?
+template <typename CharT, typename Alloc>
+void
+to_string(const dynamic_bitset& b,
+ std::basic_string<CharT, Alloc>& s)
+{
+ s.assign(b.size(), '0');
+ for (std::size_t i = 0; i < b.size(); ++i)
+ if (b.test(i)) // [G.P.S.]
+ s[b.size() - 1 - i] = '1';
+}
+
+
+// Differently from to_string this function dumps out
+// every bit of the internal representation (useful
+// for debugging purposes)
+//
+template <typename CharT, typename Alloc>
+void
+dump_to_string(const dynamic_bitset& b,
+ std::basic_string<CharT, Alloc>& s)
+{
+ std::size_t const len = b.m_num_blocks * (dynamic_bitset::bits_per_block);
+ s.assign(len, '0');
+ for (std::size_t i = 0; i != len; ++i)
+ if (b[i])// could use test_ here, but we have friend issues.-JGS
+ s[len - 1 - i] = '1';
+}
+
+
+
+template <typename BlockOutputIterator>
+void
+to_block_range(const dynamic_bitset& b,
+ BlockOutputIterator result)
+{
+ AssertIf(!(b.size() != 0 || b.num_blocks() == 0));
+ std::copy (b.m_bits, b.m_bits + b.m_num_blocks, result);
+}
+
+template <typename BlockIterator>
+inline void
+from_block_range(BlockIterator first, BlockIterator last,
+ dynamic_bitset& result)
+{
+ AssertIf(std::distance(first, last) != result.num_blocks());
+ std::copy (first, last, result.m_bits);
+ result.m_zero_unused_bits ();
+}
+
+inline dynamic_bitset::size_type
+dynamic_bitset::size() const
+{
+ return this->m_num_bits;
+}
+
+inline dynamic_bitset::size_type
+dynamic_bitset::num_blocks() const
+{
+ return this->m_num_blocks;
+}
+
+inline bool dynamic_bitset::
+is_subset_of(const dynamic_bitset& a) const
+{
+ AssertIf(this->size() != a.size());
+ for (size_type i = 0; i < this->m_num_blocks; ++i)
+ if (this->m_bits[i] & ~a.m_bits[i])
+ return false;
+ return true;
+}
+
+inline bool dynamic_bitset::
+is_proper_subset_of(const dynamic_bitset& a) const
+{
+ AssertIf(this->size() != a.size());
+ bool proper = false;
+ for (size_type i = 0; i < this->m_num_blocks; ++i) {
+ Block bt = this->m_bits[i], ba = a.m_bits[i];
+ if (ba & ~bt)
+ proper = true;
+ if (bt & ~ba)
+ return false;
+ }
+ return proper;
+}
+
+//-----------------------------------------------------------------------------
+// comparison
+
+inline bool operator==(const dynamic_bitset& a,
+ const dynamic_bitset& b)
+{
+ using namespace std;
+ return (a.m_num_bits == b.m_num_bits) &&
+ ((a.m_num_bits == 0) ||
+ !memcmp(a.m_bits, b.m_bits, a.m_num_blocks * sizeof(a.m_bits[0])));
+}
+
+inline bool operator!=(const dynamic_bitset& a,
+ const dynamic_bitset& b)
+{
+ return !(a == b);
+}
+
+inline bool operator<(const dynamic_bitset& a,
+ const dynamic_bitset& b)
+{
+ AssertIf(a.size() != b.size());
+ typedef dynamic_bitset::size_type size_type;
+
+ if (a.size() == 0)
+ return false;
+
+ // Since we are storing the most significant bit
+ // at pos == size() - 1, we need to do the memcmp in reverse.
+
+ // Compare a block at a time
+ for (size_type i = a.m_num_blocks - 1; i > 0; --i)
+ if (a.m_bits[i] < b.m_bits[i])
+ return true;
+ else if (a.m_bits[i] > b.m_bits[i])
+ return false;
+
+ if (a.m_bits[0] < b.m_bits[0])
+ return true;
+ else
+ return false;
+}
+
+inline bool operator<=(const dynamic_bitset& a,
+ const dynamic_bitset& b)
+{
+ return !(a > b);
+}
+
+inline bool operator>(const dynamic_bitset& a,
+ const dynamic_bitset& b)
+{
+ AssertIf(a.size() != b.size());
+ typedef dynamic_bitset::size_type size_type;
+
+ if (a.size() == 0)
+ return false;
+
+ // Since we are storing the most significant bit
+ // at pos == size() - 1, we need to do the memcmp in reverse.
+
+ // Compare a block at a time
+ for (size_type i = a.m_num_blocks - 1; i > 0; --i)
+ if (a.m_bits[i] < b.m_bits[i])
+ return false;
+ else if (a.m_bits[i] > b.m_bits[i])
+ return true;
+
+ if (a.m_bits[0] > b.m_bits[0])
+ return true;
+ else
+ return false;
+}
+
+inline bool operator>=(const dynamic_bitset& a,
+ const dynamic_bitset& b)
+{
+ return !(a < b);
+}
+
+//-----------------------------------------------------------------------------
+// bitset operations
+
+inline dynamic_bitset
+operator&(const dynamic_bitset& x,
+ const dynamic_bitset& y)
+{
+ dynamic_bitset b(x);
+ return b &= y;
+}
+
+inline dynamic_bitset
+operator|(const dynamic_bitset& x,
+ const dynamic_bitset& y)
+{
+ dynamic_bitset b(x);
+ return b |= y;
+}
+
+inline dynamic_bitset
+operator^(const dynamic_bitset& x,
+ const dynamic_bitset& y)
+{
+ dynamic_bitset b(x);
+ return b ^= y;
+}
+
+inline dynamic_bitset
+operator-(const dynamic_bitset& x,
+ const dynamic_bitset& y)
+{
+ dynamic_bitset b(x);
+ return b -= y;
+}
+
+
+//-----------------------------------------------------------------------------
+// private member functions
+
+inline void dynamic_bitset::
+set_(size_type bit)
+{
+ this->m_bits[this->word(bit)] |= this->mask1(bit);
+}
+
+inline void dynamic_bitset::
+set_block_(size_type blocknum, Block value)
+{
+ this->m_bits[blocknum] = value;
+}
+
+inline void dynamic_bitset::
+reset_(size_type b)
+{
+ this->m_bits[this->word(b)] &= this->mask0(b);
+}
+
+inline bool dynamic_bitset::test_(size_type b) const
+{
+ return (this->m_bits[this->word(b)] & this->mask1(b)) != static_cast<Block>(0);
+}
+
+inline bool dynamic_bitset::set_(size_type n, bool value)
+{
+ if (value)
+ set_(n);
+ else
+ reset_(n);
+ return value != static_cast<Block>(0);
+}
+
+
+// If size() is not a multiple of bits_per_block
+// then not all the bits in the last block are used.
+// This function resets the unused bits (convenient
+// for the implementation of many member functions)
+//
+inline void dynamic_bitset::m_zero_unused_bits()
+{
+ AssertIf (this->m_num_blocks != this->calc_num_blocks(this->m_num_bits));
+
+ // if != 0 this is the number of bits used in the last block
+ const size_type used_bits = this->m_num_bits % bits_per_block;
+
+ if (used_bits != 0)
+ this->m_bits[this->m_num_blocks - 1] &= ~(~static_cast<Block>(0) << used_bits);
+
+}
+
+#endif
diff --git a/Runtime/Utilities/dynamic_block_vector.h b/Runtime/Utilities/dynamic_block_vector.h
new file mode 100644
index 0000000..302847b
--- /dev/null
+++ b/Runtime/Utilities/dynamic_block_vector.h
@@ -0,0 +1,135 @@
+#pragma once
+
+#include "Runtime/Allocator/MemoryMacros.h"
+#include "Runtime/Utilities/dynamic_array.h"
+
+// dynamic_block_vector
+//
+// Allocates dynamic_arrays to hold the data in small blocks.
+// Growing pushbacks allocates one new block at a time.
+// Calls inplace constructor on all elements, and destroys the elements when removing from the list
+// Resize preserves the elements in the list and pushes default initialized elements to reach size
+// If resizing to something smaller, elements are popped (and destroyed) from the vector
+
+
+template <typename T>
+struct dynamic_block_vector
+{
+private:
+ typedef dynamic_array<T> internal_container;
+ typedef dynamic_array<internal_container*> container;
+
+public:
+
+ dynamic_block_vector (size_t allocationBlockSize)
+ : m_blockSize(allocationBlockSize), m_size(0), m_label(kMemDynamicArrayId, GET_CURRENT_ALLOC_ROOT_HEADER())
+ {
+ }
+
+ dynamic_block_vector (size_t allocationBlockSize, MemLabelId label)
+ : m_blockSize(allocationBlockSize), m_size(0), m_label(label)
+ {
+ }
+
+ dynamic_block_vector (const dynamic_block_vector& rhs)
+ : m_blockSize(rhs.m_blockSize), m_size(0), m_label(rhs.m_label)
+ {
+ *this = rhs;
+ }
+
+ ~dynamic_block_vector ()
+ {
+ clear();
+ }
+
+ void clear()
+ {
+ for(int i = 0; i < m_size; i++)
+ (*this)[i].~T();
+
+ for(int i = 0; i < m_data.size(); i++)
+ UNITY_DELETE(m_data[i],m_label);
+
+ m_data.clear();
+ m_size = 0;
+ }
+
+ void resize (size_t size)
+ {
+ while (m_size < size)
+ push_back();
+ while (m_size > size)
+ pop_back();
+ }
+
+ dynamic_block_vector& operator=(const dynamic_block_vector& other)
+ {
+ if(this == &other)
+ return *this;
+
+ clear();
+ for( int i = 0; i < other.size(); i++)
+ push_back(other[i]);
+ return *this;
+ }
+
+ template<class Iter>
+ void assign (Iter first, Iter last)
+ {
+ clear();
+ for( ; first != last; ++first)
+ push_back(*first);
+ }
+
+ void push_back ()
+ {
+ int outerindex = m_size/m_blockSize;
+ int innerindex = m_size%m_blockSize;
+ if(outerindex == m_data.size())
+ {
+ m_data.push_back(UNITY_NEW(internal_container,m_label)(m_blockSize,m_label));
+ }
+ new (&(*m_data[outerindex])[innerindex]) T();
+ m_size++;
+ }
+
+ void push_back (const T& t)
+ {
+ int outerindex = m_size/m_blockSize;
+ int innerindex = m_size%m_blockSize;
+ if(outerindex == m_data.size())
+ {
+ m_data.push_back(UNITY_NEW(internal_container,m_label)(m_blockSize,m_label));
+ }
+ new (&(*m_data[outerindex])[innerindex]) T(t);
+ m_size++;
+ }
+
+ void pop_back ()
+ {
+ (*this)[m_size-1].~T();
+ m_size--;
+ int outersize = m_size/m_blockSize + 1;
+ if (outersize < m_data.size())
+ {
+ UNITY_DELETE(m_data.back(),m_label);
+ m_data.pop_back();
+ }
+ }
+
+ size_t size () const { return m_size; }
+
+ T& back() { Assert (m_size != 0); return (*this)[m_size - 1]; }
+
+ T const& operator[] (size_t index) const { DebugAssert(index < m_size); return (*m_data[index/m_blockSize])[index%m_blockSize]; }
+ T& operator[] (size_t index) { DebugAssert(index < m_size); return (*m_data[index/m_blockSize])[index%m_blockSize]; }
+
+private:
+
+ container m_data;
+ MemLabelId m_label;
+ size_t m_size;
+ size_t m_blockSize;
+};
+
+
diff --git a/Runtime/Utilities/fixed_bitset.h b/Runtime/Utilities/fixed_bitset.h
new file mode 100644
index 0000000..b805ae7
--- /dev/null
+++ b/Runtime/Utilities/fixed_bitset.h
@@ -0,0 +1,53 @@
+#pragma once
+
+#include "Runtime/Utilities/StaticAssert.h"
+
+
+// Fixed size bitset; size (N) must be a multiple of 32.
+// Similar to dynamic_bitset, but does not do dynamic allocations and stuff.
+template<int N>
+class fixed_bitset {
+public:
+ enum { kBlockSize = 32, kBlockCount = N/kBlockSize };
+public:
+ fixed_bitset()
+ {
+ CompileTimeAssert(N % kBlockSize == 0, "size should be multiple fo 32");
+ CompileTimeAssert(sizeof(m_Bits[0])*8 == kBlockSize, "size of internal array type should be 4" );
+ for( int i = 0; i < kBlockCount; ++i )
+ m_Bits[i] = 0;
+ }
+ // note: default copy constructor and assignment operator are ok
+
+ void set( int index ) {
+ AssertIf( index < 0 || index >= N );
+ m_Bits[index/kBlockSize] |= 1 << (index & (kBlockSize-1));
+ }
+ void reset( int index ) {
+ AssertIf( index < 0 || index >= N );
+ m_Bits[index/kBlockSize] &= ~( 1 << (index & (kBlockSize-1)) );
+ }
+ bool test( int index ) const {
+ AssertIf( index < 0 || index >= N );
+ return m_Bits[index/kBlockSize] & ( 1 << (index & (kBlockSize-1)) );
+ }
+ void reset_all() {
+ memset( m_Bits, 0, sizeof(m_Bits) );
+ }
+
+ bool operator==( const fixed_bitset<N>& o ) const {
+ for( int i = 0; i < kBlockCount; ++i )
+ if( m_Bits[i] != o.m_Bits[i] )
+ return false;
+ return true;
+ }
+ bool operator!=( const fixed_bitset<N>& o ) const {
+ for( int i = 0; i < kBlockCount; ++i )
+ if( m_Bits[i] == o.m_Bits[i] )
+ return false;
+ return true;
+ }
+
+private:
+ UInt32 m_Bits[kBlockCount];
+};
diff --git a/Runtime/Utilities/sorted_vector.h b/Runtime/Utilities/sorted_vector.h
new file mode 100644
index 0000000..5f0576f
--- /dev/null
+++ b/Runtime/Utilities/sorted_vector.h
@@ -0,0 +1,358 @@
+#ifndef SORTED_VECTOR_H
+#define SORTED_VECTOR_H
+#include <vector>
+#include <algorithm>
+
+/// container optimization anschauen (compressed pair for valuecompare)
+
+
+template<class T, class Compare, class Allocator>
+class sorted_vector : private Compare
+{
+ public:
+
+ typedef std::vector<T, Allocator> container;
+ typedef typename container::iterator iterator;
+ typedef typename container::const_iterator const_iterator;
+ typedef typename container::reverse_iterator reverse_iterator;
+ typedef typename container::const_reverse_iterator const_reverse_iterator;
+ typedef typename container::value_type value_type;
+ typedef typename container::size_type size_type;
+ typedef typename container::difference_type difference_type;
+ typedef Compare value_compare;
+ typedef typename Allocator::reference reference;
+ typedef typename Allocator::const_reference const_reference;
+ typedef Allocator allocator_type;
+
+ value_compare const& get_compare () const { return *static_cast<value_compare const*> (this); }
+
+
+ sorted_vector (const Compare& comp, const Allocator& a)
+ : c (a), value_compare (comp) {}
+
+
+ bool empty() const { return c.empty(); }
+ size_type size() const { return c.size(); }
+ const value_type& front() const { return c.front(); }
+ const value_type& back() const { return c.front(); }
+
+ const_iterator begin ()const { return c.begin (); }
+ const_iterator end ()const { return c.end (); }
+ iterator begin () { return c.begin (); }
+ iterator end () { return c.end (); }
+ const_reverse_iterator rbegin ()const { return c.rbegin (); }
+ const_reverse_iterator rend ()const { return c.rend (); }
+ reverse_iterator rbegin () { return c.rbegin (); }
+ reverse_iterator rend () { return c.rend (); }
+
+ value_compare value_comp() const { return get_compare ();}
+/*
+ template <class InputIterator>
+ void insert_one (InputIterator first, InputIterator last)
+ {
+ c.reserve (size () + std::distance (first, last));
+ for (;first != last;first++)
+ insert_one (*i);
+ }
+
+ template<typename KeyT, typename MappedT>
+ MappedT& find_or_insert (const KeyT& k)
+ {
+ iterator i = lower_bound (k);
+ if (i == end () || get_compare () (k, *i))
+ return c.insert (i, std::make_pair<KeyT, MappedT> (k, MappedT ()))->second;
+ else
+ return i->second;
+ }
+*/
+
+ template<typename KeyT, typename MappedT>
+ void find_or_insert (MappedT*& mappedT, const KeyT& k)
+ {
+ iterator i = lower_bound (k);
+ if (i == end () || get_compare () (k, *i))
+ #if _MSC_VER >= 1700 // Microsoft Visual Studio 2012 workaround
+ mappedT = &c.insert (i, std::make_pair<KeyT, MappedT> (static_cast<KeyT&&>(const_cast<KeyT&>(k)), MappedT ()))->second;
+ #else
+ mappedT = &c.insert (i, std::make_pair<KeyT, MappedT> (k, MappedT ()))->second;
+ #endif
+ else
+ mappedT = &i->second;
+ }
+
+ std::pair<iterator, bool> insert_one (const value_type& x)
+ {
+ iterator i = lower_bound (x);
+ // is not included in container
+ if (i == end () || get_compare () (x, *i))
+ return std::make_pair (c.insert (i, x), true);
+ else
+ return std::make_pair (i, false);
+
+ }
+
+ void verify_duplicates_and_sorted () const
+ {
+ #if DEBUGMODE
+ // Check that there are no duplicates in the set
+ if (empty())
+ return;
+
+ const_iterator previous = c.begin();
+ const_iterator i = c.begin();
+ i++;
+ for (; i != c.end();++i)
+ {
+ Assert(get_compare ()(*previous, *i));
+ previous = i;
+ }
+ #endif
+ }
+
+
+ template<class CompareType>
+ size_type erase_one (const CompareType& x)
+ {
+ iterator i = lower_bound (x);
+ if (i == end () || get_compare () (x, *i))
+ return 0;
+ else
+ {
+ c.erase (i);
+ return 1;
+ }
+ }
+
+ void erase(iterator position) { c.erase (position);}
+ void erase(iterator first, iterator last) { c.erase (first, last); }
+ void swap(sorted_vector& x) { c.swap (x.c); }
+
+ void clear () { c.clear (); }
+
+ template<class T2>
+ size_type count_one (const T2& x)const
+ {
+ const_iterator i = lower_bound (x);
+ if (i == end () || get_compare () (x, *i))
+ return 0;
+ else
+ return 1;
+ }
+
+ template<class T2>
+ iterator find (const T2& x)
+ {
+ iterator i = lower_bound (x);
+ if (i == end () || get_compare () (x, *i))
+ return end ();
+ else
+ return i;
+ }
+
+ template<class T2>
+ const_iterator find (const T2& x) const
+ {
+ const_iterator i = lower_bound (x);
+ if (i == end () || get_compare () (x, *i))
+ return end ();
+ else
+ return i;
+ }
+
+ template<class T2>
+ iterator lower_bound (const T2& x)
+ {
+ return std::lower_bound (c.begin (), c.end (), x, get_compare ());
+ }
+
+ template<class T2>
+ const_iterator lower_bound (const T2& x) const
+ {
+ return std::lower_bound (c.begin (), c.end (), x, get_compare ());
+ }
+
+ template<class T2>
+ iterator upper_bound (const T2& x)
+ {
+ return std::lower_bound (c.begin (), c.end (), x, get_compare ());
+ }
+
+ template<class T2>
+ const_iterator upper_bound (const T2& x) const
+ {
+ return std::lower_bound (c.begin (), c.end (), x, get_compare ());
+ }
+
+ template<class T2>
+ std::pair<iterator, iterator> equal_range (const T2& x)
+ {
+ return std::equal_range (c.begin (), c.end (), x, get_compare ());
+ }
+
+ template<class T2>
+ std::pair<const_iterator, const_iterator> equal_range (const T2& x) const
+ {
+ return std::equal_range (c.begin (), c.end (), x, get_compare ());
+ }
+
+ void reserve (size_type n) { c.reserve (n); }
+
+ value_type& operator [] (int n) { return c[n]; }
+ const value_type& operator [] (int n) const { return c[n]; }
+
+ public:
+
+ container c;
+};
+
+template<class T, class Compare, class Allocator>
+class unsorted_vector : private Compare
+{
+ public:
+
+ typedef std::vector<T, Allocator> container;
+ typedef typename container::iterator iterator;
+ typedef typename container::const_iterator const_iterator;
+ typedef typename container::reverse_iterator reverse_iterator;
+ typedef typename container::const_reverse_iterator const_reverse_iterator;
+
+ typedef typename container::value_type value_type;
+ typedef typename container::size_type size_type;
+ typedef typename container::difference_type difference_type;
+ typedef Compare value_compare;
+ typedef typename Allocator::reference reference;
+ typedef typename Allocator::const_reference const_reference;
+ typedef Allocator allocator_type;
+
+ value_compare const& get_compare () const { return *static_cast<value_compare const*> (this); }
+
+ unsorted_vector (const Compare& comp, const Allocator& a)
+ : c (a), value_compare (comp) {}
+
+
+ bool empty() const { return c.empty(); }
+ size_type size() const { return c.size(); }
+ const value_type& front() const { return c.front(); }
+ const value_type& back() const { return c.front(); }
+
+ const_iterator begin ()const { return c.begin (); }
+ const_iterator end ()const { return c.end (); }
+ iterator begin () { return c.begin (); }
+ iterator end () { return c.end (); }
+ const_reverse_iterator rbegin ()const { return c.rbegin (); }
+ const_reverse_iterator rend ()const { return c.rend (); }
+ reverse_iterator rbegin () { return c.rbegin (); }
+ reverse_iterator rend () { return c.rend (); }
+
+ value_compare value_comp() const { return get_compare ();}
+/*
+ template <class InputIterator>
+ void insert_one (InputIterator first, InputIterator last)
+ {
+ c.reserve (size () + std::distance (first, last));
+ for (;first != last;first++)
+ insert_one (*i);
+ }
+*/
+ template<typename KeyT, typename MappedT>
+ void find_or_insert (MappedT*& mappedT, const KeyT& k)
+ {
+ iterator i = find (k);
+ if (i == end ())
+ {
+ c.push_back (std::make_pair<KeyT, MappedT> (k, MappedT ()));
+ mappedT = &(c.end () - 1)->second;
+ }
+ else
+ mappedT = &i->second;
+ }
+ /*
+ template<class Key, class Value>
+ Value& find_or_insert (const Key& k)
+ {
+ iterator i = find (k);
+ if (i == end ())
+ {
+ c.push_back (std::make_pair<Key, Value> (k, Value ()));
+ return (c.end () - 1)->second;
+ }
+ else
+ return i->second;
+ }
+ */
+ std::pair<iterator, bool> insert_one (const value_type& x)
+ {
+ iterator i = find (x);
+ // is not included in container
+ if (i == end ())
+ {
+ c.push_back (x);
+ return std::make_pair (c.end () - 1, true);
+ }
+ else
+ return std::make_pair (i, false);
+ }
+
+ template<class CompareType>
+ size_type erase_one (const CompareType& x)
+ {
+ iterator i = find (x);
+ if (i == end ())
+ return 0;
+ else
+ {
+ erase (i);
+ return 1;
+ }
+ }
+
+ void erase(iterator position) { *position = c.back (); c.pop_back (); }
+ void swap(unsorted_vector& x) { c.swap (x.c); }
+
+ void clear () { c.clear (); }
+
+ template<class T2>
+ size_type count_one (const T2& x)const
+ {
+ const_iterator i = find (x);
+ return i != end ();
+ }
+
+ template<class T2>
+ iterator find (const T2& x)
+ {
+ iterator b = c.begin ();
+ iterator e = c.end ();
+ for (;b != e;++b)
+ {
+ if (get_compare () (*b, x))
+ return b;
+ }
+ return e;
+ }
+
+ template<class T2>
+ const_iterator find (const T2& x) const
+ {
+ {
+ const_iterator b = c.begin ();
+ const_iterator e = c.end ();
+ for (;b != e;++b)
+ {
+ if (get_compare () (*b, x))
+ return b;
+ }
+ return e;
+ }
+
+ }
+ void reserve (size_type n) { c.reserve (n); }
+ value_type& operator [] (int n) { return c[n]; }
+ const value_type& operator [] (int n) const { return c[n]; }
+
+ public:
+
+ container c;
+};
+
+#endif
diff --git a/Runtime/Utilities/triple.h b/Runtime/Utilities/triple.h
new file mode 100644
index 0000000..4a3d5c1
--- /dev/null
+++ b/Runtime/Utilities/triple.h
@@ -0,0 +1,46 @@
+#pragma once
+
+// TEMPLATE STRUCT triple
+template<class T>
+struct triple
+{
+ // store a triple of values of the same type
+
+ triple()
+ : first(T()), second(T()), third(T())
+ { // construct from defaults
+ }
+
+ triple(const T& _Val1, const T& _Val2, const T& _Val3)
+ : first(_Val1), second(_Val2), third(_Val3)
+ { // construct from specified values
+ }
+
+ template<class otherT>
+ triple(const triple<otherT>& _Right)
+ : first(_Right.first), second(_Right.second), third(_Right.third)
+ { // construct from a compatible triple
+ }
+
+ T first; // the first stored value
+ T second; // the second stored value
+ T third; // the third stored value
+};
+
+template<class T>
+inline bool operator==(const triple<T>& _Left, const triple<T>& _Right)
+{ // test for triple equality
+ return (_Left.first == _Right.first && _Left.second == _Right.second && _Left.third == _Right.third);
+}
+
+template<class T>
+inline bool operator!=(const triple<T>& _Left, const triple<T>& _Right)
+{ // test for triple inequality
+ return (!(_Left == _Right));
+}
+
+template<class T>
+inline triple<T> make_triple(T _Val1, T _Val2, T _Val3)
+{ // return a triple composed from arguments
+ return (triple<T>(_Val1, _Val2, _Val3));
+} \ No newline at end of file
diff --git a/Runtime/Utilities/type_traits.h b/Runtime/Utilities/type_traits.h
new file mode 100644
index 0000000..edffc34
--- /dev/null
+++ b/Runtime/Utilities/type_traits.h
@@ -0,0 +1,250 @@
+// Copyright (c) 2006, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// ----
+// Author: Matt Austern
+//
+// Define a small subset of tr1 type traits. The traits we define are:
+// is_integral
+// is_floating_point
+// is_pointer
+// is_reference
+// is_pod
+// has_trivial_constructor
+// has_trivial_copy
+// has_trivial_assign
+// has_trivial_destructor
+// remove_const
+// remove_volatile
+// remove_cv
+// remove_reference
+// remove_pointer
+// is_convertible
+// We can add more type traits as required.
+
+#ifndef BASE_TYPE_TRAITS_H_
+#define BASE_TYPE_TRAITS_H_
+
+//#include <google/sparsehash/sparseconfig.h>
+#include <utility> // For pair
+
+namespace dense_hash_map_traits
+{
+
+// integral_constant, defined in tr1, is a wrapper for an integer
+// value. We don't really need this generality; we could get away
+// with hardcoding the integer type to bool. We use the fully
+// general integer_constant for compatibility with tr1.
+
+template<class T, T v>
+struct integral_constant {
+ static const T value = v;
+ typedef T value_type;
+ typedef integral_constant<T, v> type;
+};
+
+template <class T, T v> const T integral_constant<T, v>::value;
+
+// Abbreviations: true_type and false_type are structs that represent
+// boolean true and false values.
+typedef integral_constant<bool, true> true_type;
+typedef integral_constant<bool, false> false_type;
+
+// Types small_ and big_ are guaranteed such that sizeof(small_) <
+// sizeof(big_)
+typedef char small_;
+
+struct big_ {
+ char dummy[2];
+};
+
+// is_integral is false except for the built-in integer types.
+template <class T> struct is_integral : false_type { };
+template<> struct is_integral<bool> : true_type { };
+template<> struct is_integral<char> : true_type { };
+template<> struct is_integral<unsigned char> : true_type { };
+template<> struct is_integral<signed char> : true_type { };
+#if defined(_MSC_VER)
+// wchar_t is not by default a distinct type from unsigned short in
+// Microsoft C.
+// See http://msdn2.microsoft.com/en-us/library/dh8che7s(VS.80).aspx
+template<> struct is_integral<__wchar_t> : true_type { };
+#else
+template<> struct is_integral<wchar_t> : true_type { };
+#endif
+template<> struct is_integral<short> : true_type { };
+template<> struct is_integral<unsigned short> : true_type { };
+template<> struct is_integral<int> : true_type { };
+template<> struct is_integral<unsigned int> : true_type { };
+template<> struct is_integral<long> : true_type { };
+template<> struct is_integral<unsigned long> : true_type { };
+#ifdef HAVE_LONG_LONG
+template<> struct is_integral<long long> : true_type { };
+template<> struct is_integral<unsigned long long> : true_type { };
+#endif
+
+
+// is_floating_point is false except for the built-in floating-point types.
+template <class T> struct is_floating_point : false_type { };
+template<> struct is_floating_point<float> : true_type { };
+template<> struct is_floating_point<double> : true_type { };
+template<> struct is_floating_point<long double> : true_type { };
+
+
+// is_pointer is false except for pointer types.
+template <class T> struct is_pointer : false_type { };
+template <class T> struct is_pointer<T*> : true_type { };
+
+
+// is_reference is false except for reference types.
+template<typename T> struct is_reference : false_type {};
+template<typename T> struct is_reference<T&> : true_type {};
+
+
+// We can't get is_pod right without compiler help, so fail conservatively.
+// We will assume it's false except for arithmetic types and pointers,
+// and const versions thereof. Note that std::pair is not a POD.
+template <class T> struct is_pod
+ : integral_constant<bool, (is_integral<T>::value ||
+ is_floating_point<T>::value ||
+ is_pointer<T>::value)> { };
+template <class T> struct is_pod<const T> : is_pod<T> { };
+
+
+// We can't get has_trivial_constructor right without compiler help, so
+// fail conservatively. We will assume it's false except for: (1) types
+// for which is_pod is true. (2) std::pair of types with trivial
+// constructors. (3) array of a type with a trivial constructor.
+// (4) const versions thereof.
+template <class T> struct has_trivial_constructor : is_pod<T> { };
+template <class T, class U> struct has_trivial_constructor<std::pair<T, U> >
+ : integral_constant<bool,
+ (has_trivial_constructor<T>::value &&
+ has_trivial_constructor<U>::value)> { };
+template <class A, int N> struct has_trivial_constructor<A[N]>
+ : has_trivial_constructor<A> { };
+template <class T> struct has_trivial_constructor<const T>
+ : has_trivial_constructor<T> { };
+
+// We can't get has_trivial_copy right without compiler help, so fail
+// conservatively. We will assume it's false except for: (1) types
+// for which is_pod is true. (2) std::pair of types with trivial copy
+// constructors. (3) array of a type with a trivial copy constructor.
+// (4) const versions thereof.
+template <class T> struct has_trivial_copy : is_pod<T> { };
+template <class T, class U> struct has_trivial_copy<std::pair<T, U> >
+ : integral_constant<bool,
+ (has_trivial_copy<T>::value &&
+ has_trivial_copy<U>::value)> { };
+template <class A, int N> struct has_trivial_copy<A[N]>
+ : has_trivial_copy<A> { };
+template <class T> struct has_trivial_copy<const T> : has_trivial_copy<T> { };
+
+// We can't get has_trivial_assign right without compiler help, so fail
+// conservatively. We will assume it's false except for: (1) types
+// for which is_pod is true. (2) std::pair of types with trivial copy
+// constructors. (3) array of a type with a trivial assign constructor.
+template <class T> struct has_trivial_assign : is_pod<T> { };
+template <class T, class U> struct has_trivial_assign<std::pair<T, U> >
+ : integral_constant<bool,
+ (has_trivial_assign<T>::value &&
+ has_trivial_assign<U>::value)> { };
+template <class A, int N> struct has_trivial_assign<A[N]>
+ : has_trivial_assign<A> { };
+
+// We can't get has_trivial_destructor right without compiler help, so
+// fail conservatively. We will assume it's false except for: (1) types
+// for which is_pod is true. (2) std::pair of types with trivial
+// destructors. (3) array of a type with a trivial destructor.
+// (4) const versions thereof.
+template <class T> struct has_trivial_destructor : is_pod<T> { };
+template <class T, class U> struct has_trivial_destructor<std::pair<T, U> >
+ : integral_constant<bool,
+ (has_trivial_destructor<T>::value &&
+ has_trivial_destructor<U>::value)> { };
+template <class A, int N> struct has_trivial_destructor<A[N]>
+ : has_trivial_destructor<A> { };
+template <class T> struct has_trivial_destructor<const T>
+ : has_trivial_destructor<T> { };
+
+// Specified by TR1 [4.7.1]
+template<typename T> struct remove_const { typedef T type; };
+template<typename T> struct remove_const<T const> { typedef T type; };
+template<typename T> struct remove_volatile { typedef T type; };
+template<typename T> struct remove_volatile<T volatile> { typedef T type; };
+template<typename T> struct remove_cv {
+ typedef typename remove_const<typename remove_volatile<T>::type>::type type;
+};
+
+
+// Specified by TR1 [4.7.2]
+template<typename T> struct remove_reference { typedef T type; };
+template<typename T> struct remove_reference<T&> { typedef T type; };
+
+// Specified by TR1 [4.7.4] Pointer modifications.
+template<typename T> struct remove_pointer { typedef T type; };
+template<typename T> struct remove_pointer<T*> { typedef T type; };
+template<typename T> struct remove_pointer<T* const> { typedef T type; };
+template<typename T> struct remove_pointer<T* volatile> { typedef T type; };
+template<typename T> struct remove_pointer<T* const volatile> {
+ typedef T type; };
+
+// Specified by TR1 [4.6] Relationships between types
+#ifndef _MSC_VER
+namespace internal_type_traits {
+
+// This class is an implementation detail for is_convertible, and you
+// don't need to know how it works to use is_convertible. For those
+// who care: we declare two different functions, one whose argument is
+// of type To and one with a variadic argument list. We give them
+// return types of different size, so we can use sizeof to trick the
+// compiler into telling us which function it would have chosen if we
+// had called it with an argument of type From. See Alexandrescu's
+// _Modern C++ Design_ for more details on this sort of trick.
+
+template <typename From, typename To>
+struct ConvertHelper {
+ static small_ Test(To);
+ static big_ Test(...);
+ static From Create();
+};
+} // namespace internal
+
+// Inherits from true_type if From is convertible to To, false_type otherwise.
+template <typename From, typename To>
+struct is_convertible
+ : integral_constant<bool,
+ sizeof(internal_type_traits::ConvertHelper<From, To>::Test(
+ internal_type_traits::ConvertHelper<From, To>::Create()))
+ == sizeof(small_)> {
+};
+#endif
+
+}
+#endif // BASE_TYPE_TRAITS_H_
diff --git a/Runtime/Utilities/vector_map.h b/Runtime/Utilities/vector_map.h
new file mode 100644
index 0000000..9d3c936
--- /dev/null
+++ b/Runtime/Utilities/vector_map.h
@@ -0,0 +1,268 @@
+#ifndef VECTOR_MAP_H
+#define VECTOR_MAP_H
+
+#include "sorted_vector.h"
+#include <functional>
+
+// vector_map offers the same functionality as std::set
+// but it is implemented using sorted vectors.
+// sorted_vectors are smaller in used memory and can be faster due to cache coherence
+// However inserting or erasing elements can be O (N) instead of O (logN)
+// Usually you will want to use vector_set when you have a set which you use
+// much more often to find values than inserting them or if the set you use is very small
+// vector_map also offers the vector function reserve.
+// - also note that if you store an iterator to an element you are NOT guaranteed that this iterator
+// remains valid after you insert/erase other elements
+// - vector_map«s key is not const, but you are still not allowed to change the key without erasing/inserting it.
+
+
+template<class Key, class T, class Compare = std::less<Key>,
+ class Allocator = std::allocator<std::pair<Key, T> > >
+class vector_map
+{
+ public:
+
+ typedef Key key_type;
+ typedef T mapped_type;
+ typedef std::pair<Key,T> value_type;
+ typedef Compare key_compare;
+ typedef Allocator allocator_type;
+ typedef typename Allocator::reference reference;
+ typedef typename Allocator::const_reference const_reference;
+ typedef typename Allocator::size_type size_type;
+ typedef typename Allocator::difference_type difference_type;
+ typedef typename Allocator::pointer pointer;
+ typedef typename Allocator::const_pointer const_pointer;
+
+ class value_compare
+ : public std::binary_function<value_type,value_type,bool>
+ {
+ public:
+ bool operator()(const value_type& x, const value_type& y) const
+ {
+ return comp(x.first, y.first);
+ }
+ bool operator()(const key_type& x, const value_type& y) const
+ {
+ return comp(x, y.first);
+ }
+ bool operator()(const value_type& x, const key_type& y) const
+ {
+ return comp(x.first, y);
+ }
+
+ value_compare() {}
+ value_compare(Compare c) : comp(c) {}
+ protected:
+ Compare comp;
+
+
+ friend class vector_map;
+ };
+
+ typedef sorted_vector<value_type, value_compare, Allocator> container;
+ typedef typename container::container vector_container;
+ typedef typename container::iterator iterator;
+ typedef typename container::const_iterator const_iterator;
+ typedef typename container::reverse_iterator reverse_iterator;
+ typedef typename container::const_reverse_iterator const_reverse_iterator;
+
+ public:
+
+ // ctors
+ vector_map (const Compare& comp = Compare (), const Allocator& a = Allocator ())
+ : c (value_compare(comp), a)
+ { }
+
+ template <class InputIterator>
+ vector_map(InputIterator first, InputIterator last, const Compare& comp = Compare (), const Allocator& a = Allocator ())
+ : c (value_compare(comp), a)
+ { insert_one (first, last); }
+
+ // iterators
+ iterator begin() { return c.begin (); }
+ const_iterator begin() const { return c.begin (); }
+ iterator end() { return c.end (); }
+ const_iterator end() const { return c.end (); }
+ reverse_iterator rbegin() { return c.rbegin (); }
+ const_reverse_iterator rbegin() const { return c.rbegin (); }
+ reverse_iterator rend() { return c.rend (); }
+ const_reverse_iterator rend() const { return c.rend (); }
+
+ // capacity:
+ bool empty() const { return c.empty (); }
+ size_type size() const { return c.size (); }
+ size_type max_size() const { return c.max_size (); }
+
+ // modifiers:
+ std::pair<iterator, bool> insert(const value_type& x) { return c.insert_one (x); }
+
+ template <class InputIterator>
+ void insert(InputIterator first, InputIterator last) { c.insert_one(first, last); }
+
+ void erase(iterator position) { c.erase (position); }
+ size_type erase(const key_type& x) { return c.erase_one (x); }
+ void erase(iterator first, iterator last) { c.erase (first, last); }
+ void swap(vector_map& x) { c.swap (x.c); }
+ void clear() { c.clear (); }
+
+ // observers:
+ key_compare key_comp() const { return c.value_comp ().comp; }
+ value_compare value_comp() const { return c.value_comp (); }
+
+ // lib.map.ops map operations:
+ // CW has problems with the straightforward version
+// mapped_type& operator[] (const key_type& x) { return c.find_or_insert<key_type, mapped_type> (x); }
+ mapped_type& operator[] (const key_type& x) { mapped_type* temp; c.find_or_insert (temp, x); return *temp; }
+
+ iterator find(const key_type& x) { return c.find (x); }
+ const_iterator find(const key_type& x) const { return c.find (x); }
+ size_type count(const key_type& x) const { return c.count_one (x); }
+
+ iterator lower_bound(const key_type& x) { return c.lower_bound (x); }
+ const_iterator lower_bound(const key_type& x) const{ return c.lower_bound (x); }
+ iterator upper_bound(const key_type& x) { return c.upper_bound (x); }
+ const_iterator upper_bound(const key_type& x) const{ return c.upper_bound (x); }
+
+ std::pair<iterator,iterator> equal_range(const key_type& x) { return c.equal_range (x); }
+ std::pair<const_iterator,const_iterator> equal_range(const key_type& x) const { return c.equal_range (x); }
+
+ // vector specific operations
+ void reserve (size_type n) { c.reserve (n); }
+
+ vector_container& get_vector () { return c.c; }
+
+ void push_unsorted (const key_type& x, const mapped_type& value)
+ {
+ get_vector().push_back(std::make_pair(x, value));
+ }
+
+ void sort ()
+ {
+ std::sort(c.c.begin(), c.c.end(), c.value_comp ());
+ c.verify_duplicates_and_sorted ();
+ }
+
+ void verify_duplicates_and_sorted () const
+ {
+ c.verify_duplicates_and_sorted ();
+ }
+
+ private:
+
+ container c;
+};
+
+template<class Key, class T, class Compare = std::equal_to<Key>,
+ class Allocator = std::allocator<std::pair<Key, T> > >
+class us_vector_map
+{
+ public:
+
+ typedef Key key_type;
+ typedef T mapped_type;
+ typedef std::pair<Key,T> value_type;
+ typedef Compare key_compare;
+ typedef Allocator allocator_type;
+ typedef typename Allocator::reference reference;
+ typedef typename Allocator::const_reference const_reference;
+ typedef typename Allocator::size_type size_type;
+ typedef typename Allocator::difference_type difference_type;
+ typedef typename Allocator::pointer pointer;
+ typedef typename Allocator::const_pointer const_pointer;
+
+ class value_compare
+ : public std::binary_function<value_type,value_type,bool>
+ {
+ public:
+ bool operator()(const value_type& x, const value_type& y) const
+ {
+ return comp(x.first, y.first);
+ }
+ bool operator()(const key_type& x, const value_type& y) const
+ {
+ return comp(x, y.first);
+ }
+ bool operator()(const value_type& x, const key_type& y) const
+ {
+ return comp(x.first, y);
+ }
+ protected:
+ Compare comp;
+
+ value_compare() {}
+ value_compare(Compare c) : comp(c) {}
+
+ friend class us_vector_map;
+ };
+
+ typedef unsorted_vector<value_type, value_compare, Allocator> container;
+ typedef typename container::container vector_container;
+ typedef typename container::iterator iterator;
+ typedef typename container::const_iterator const_iterator;
+ typedef typename container::reverse_iterator reverse_iterator;
+ typedef typename container::const_reverse_iterator const_reverse_iterator;
+
+ public:
+
+ // ctors
+ us_vector_map (const Compare& comp = Compare (), const Allocator& a = Allocator ())
+ : c (value_compare(comp), a)
+ { }
+
+ template <class InputIterator>
+ us_vector_map(InputIterator first, InputIterator last, const Compare& comp = Compare (), const Allocator& a = Allocator ())
+ : c (value_compare(comp), a)
+ { insert_one (first, last); }
+
+ // iterators
+ iterator begin() { return c.begin (); }
+ const_iterator begin() const { return c.begin (); }
+ iterator end() { return c.end (); }
+ const_iterator end() const { return c.end (); }
+ reverse_iterator rbegin() { return c.rbegin (); }
+ const_reverse_iterator rbegin() const { return c.rbegin (); }
+ reverse_iterator rend() { return c.rend (); }
+ const_reverse_iterator rend() const { return c.rend (); }
+
+
+ // capacity:
+ bool empty() const { return c.empty (); }
+ size_type size() const { return c.size (); }
+ size_type max_size() const { return c.max_size (); }
+
+ // modifiers:
+ std::pair<iterator, bool> insert(const value_type& x){ return c.insert_one (x); }
+
+ template <class InputIterator>
+ void insert(InputIterator first, InputIterator last) { c.insert_one(first, last); }
+
+ void erase(iterator position) { c.erase (position); }
+ size_type erase(const key_type& x) { return c.erase_one (x); }
+ void swap(us_vector_map& x) { c.swap (x.c);}
+ void clear() { c.clear (); }
+
+ // observers:
+ key_compare key_comp() const { return c.value_comp ().comp; }
+ value_compare value_comp() const { return c.value_comp (); }
+
+ // lib.map.ops map operations:
+ // CW has problems with the straightforward version
+ // mapped_type& operator[] (const key_type& x) { return c.find_or_insert<key_type, mapped_type> (x); }
+ mapped_type& operator[] (const key_type& x) { mapped_type* temp; c.find_or_insert (temp, x); return *temp; }
+
+ iterator find(const key_type& x) { return c.find (x); }
+ const_iterator find(const key_type& x) const { return c.find (x); }
+ size_type count(const key_type& x) const { return c.count_one (x); }
+
+ // vector specific operations
+ void reserve (size_type n) { c.reserve (n); }
+
+ vector_container& get_vector () { return c.c; }
+
+ private:
+
+ container c;
+};
+
+#endif
diff --git a/Runtime/Utilities/vector_set.h b/Runtime/Utilities/vector_set.h
new file mode 100644
index 0000000..9d06e19
--- /dev/null
+++ b/Runtime/Utilities/vector_set.h
@@ -0,0 +1,229 @@
+#ifndef VECTOR_SET_H
+#define VECTOR_SET_H
+
+#include "sorted_vector.h"
+#include <functional>
+
+// vector_set offers the same functionality as std::set
+// but it is implemented using sorted vectors.
+// sorted_vectors are smaller in used memory and can be faster due to cache coherence
+// However inserting or erasing elements is O (N) instead of O (logN)
+// - Usually you will want to use vector_set when you have a set which you use
+// much more often to find values than inserting them or if the set you use is very small
+// vector_set also offers the vector function reserve.
+// - also note that if you store an iterator to an element you are NOT guaranteed that this iterator
+// remains valid after you insert/erase other elements
+
+template <class Key, class Compare = std::less<Key>, class Allocator = std::allocator<Key> >
+class vector_set
+{
+ public:
+
+ typedef sorted_vector<Key, Compare, Allocator> container;
+ typedef typename container::container vector_container;
+ typedef typename container::iterator iterator;
+ typedef typename container::const_iterator const_iterator;
+ typedef typename container::reverse_iterator reverse_iterator;
+ typedef typename container::const_reverse_iterator const_reverse_iterator;
+ typedef typename container::value_type value_type;
+ typedef typename container::value_type key_type;
+ typedef Compare key_compare;
+ typedef Compare value_compare;
+ typedef typename container::size_type size_type;
+ typedef typename container::difference_type difference_type;
+ typedef Allocator allocator_type;
+
+
+ vector_set (const Compare& comp = Compare (), const Allocator& a = Allocator())
+ : c (comp, a) { }
+
+ template <class InputIterator>
+ vector_set (InputIterator first, InputIterator last, const Compare& comp = Compare (), const Allocator& a = Allocator())
+ : c (comp, a)
+ {
+ assign(first, last);
+ }
+
+ // Assigns a range that is known to be sorted
+ template<class InputIterator>
+ void assign_sorted (InputIterator first, InputIterator last){ c.c.assign (first, last); c.verify_duplicates_and_sorted (); }
+
+ // Assigns a range
+ // Asserts if there are any duplicates in the input
+ template<class InputIterator>
+ void assign (InputIterator first, InputIterator last)
+ {
+ c.c.assign (first, last);
+
+ sort();
+ }
+
+ // Assigns a range
+ // clears any duplicate objects
+ template<class InputIterator>
+ void assign_clear_duplicates (InputIterator first, InputIterator last)
+ {
+ c.c.assign (first, last);
+ std::stable_sort(c.begin(), c.end(), c.value_comp());
+
+ // Check that there are no duplicates in the set
+ if (!empty())
+ {
+ Key* previous = &*c.begin();
+ iterator i = c.begin(); i++;
+ for (; i != c.end();)
+ {
+ if (*i < *previous || *previous < *i)
+ {
+ previous = &*i;
+ i++;
+ }
+ else
+ {
+ iterator e;
+ for (e=i;e!=c.end() && !(*e < *previous || *previous < *e);e++)
+ ;
+ c.erase(i, e);
+ }
+ }
+ }
+
+ c.verify_duplicates_and_sorted ();
+ }
+
+ bool empty () const { return c.empty (); }
+
+ iterator begin() { return c.begin (); }
+ const_iterator begin() const { return c.begin (); }
+ iterator end() { return c.end (); }
+ const_iterator end() const { return c.end (); }
+ reverse_iterator rbegin() { return c.rbegin (); }
+ const_reverse_iterator rbegin() const { return c.rbegin (); }
+ reverse_iterator rend() { return c.rend (); }
+ const_reverse_iterator rend() const { return c.rend (); }
+
+ size_type size() const { return c.size (); }
+ size_type max_size() const { return c.max_size (); }
+
+ std::pair<iterator,bool>insert(const value_type& x) { return c.insert_one (x); }
+
+ void erase(iterator position) { c.erase (position); }
+ size_type erase(const key_type& x) { return c.erase_one (x); }
+ void erase(iterator first, iterator last){ c.erase (first, last); }
+ void clear() { c.clear (); }
+
+ void swap(vector_set& x) { c.swap (x.c); }
+
+ // set operations:
+ iterator find(const key_type& x) { return c.find (x); }
+ const_iterator find(const key_type& x) const { return c.find (x); }
+ size_type count(const key_type& x) const { return c.count_one (x); }
+
+ iterator lower_bound(const key_type& x) { return c.lower_bound (x); }
+ const_iterator lower_bound(const key_type& x) const{ return c.lower_bound (x); }
+
+ iterator upper_bound(const key_type& x) { return c.upper_bound (x); }
+ const_iterator upper_bound(const key_type& x) const{ return c.upper_bound (x); }
+
+ std::pair<iterator,iterator> equal_range(const key_type& x) { return c.equal_range (x); }
+ std::pair<const_iterator, const_iterator> equal_range(const key_type& x) const { return c.equal_range (x); }
+
+ // vector specific operations
+ void reserve (size_type n) { c.reserve (n); }
+
+ value_type& operator [] (int n) { return c[n]; }
+ const value_type& operator [] (int n) const { return c[n]; }
+
+ vector_container& get_vector () { return c.c; }
+
+ void push_unsorted (const value_type& x)
+ {
+ get_vector().push_back(x);
+ }
+
+ void sort ()
+ {
+ std::sort(c.c.begin(), c.c.end(), c.value_comp ());
+ c.verify_duplicates_and_sorted ();
+ }
+
+ void verify_duplicates_and_sorted () const
+ {
+ c.verify_duplicates_and_sorted ();
+ }
+
+
+ public:
+
+ container c;
+};
+
+template <class Key, class Compare = std::equal_to<Key>, class Allocator = std::allocator<Key> >
+class us_vector_set
+{
+ public:
+
+ typedef unsorted_vector<Key, Compare, Allocator> container;
+ typedef typename container::container vector_container;
+ typedef typename container::iterator iterator;
+ typedef typename container::const_iterator const_iterator;
+ typedef typename container::reverse_iterator reverse_iterator;
+ typedef typename container::const_reverse_iterator const_reverse_iterator;
+ typedef typename container::value_type value_type;
+ typedef typename container::value_type key_type;
+ typedef Compare key_compare;
+ typedef Compare value_compare;
+ typedef typename container::size_type size_type;
+ typedef typename container::difference_type difference_type;
+ typedef Allocator allocator_type;
+
+
+ us_vector_set (const Compare& comp = Compare (), const Allocator& a = Allocator())
+ : c (comp, a) { }
+
+ template <class InputIterator>
+ us_vector_set (InputIterator first, InputIterator last, const Compare& comp = Compare (), const Allocator& a = Allocator())
+ : c (comp, a) { insert_one (first, last); }
+
+ bool empty () const { return c.empty (); }
+
+ iterator begin() { return c.begin (); }
+ const_iterator begin() const { return c.begin (); }
+ iterator end() { return c.end (); }
+ const_iterator end() const { return c.end (); }
+ reverse_iterator rbegin() { return c.rbegin (); }
+ const_reverse_iterator rbegin() const { return c.rbegin (); }
+ reverse_iterator rend() { return c.rend (); }
+ const_reverse_iterator rend() const { return c.rend (); }
+
+ size_type size() const { return c.size (); }
+ size_type max_size() const { return c.max_size (); }
+
+ std::pair<iterator,bool> insert(const value_type& x) { return c.insert_one (x); }
+
+ void erase(iterator position) { c.erase (position); }
+ size_type erase(const key_type& x) { return c.erase_one (x); }
+ void erase(iterator first, iterator last){ c.erase (first, last); }
+ void clear() { c.clear (); }
+
+ void swap(us_vector_set& x) { c.swap (x.c); }
+
+ // set operations:
+ iterator find(const key_type& x) { return c.find (x); }
+ const_iterator find(const key_type& x) const { return c.find (x); }
+ size_type count(const key_type& x) const { return c.count_one (x); }
+
+ // vector specific operations
+ void reserve (size_type n) { c.reserve (n); }
+
+ value_type& operator [] (int n) { return c[n]; }
+ const value_type& operator [] (int n) const { return c[n]; }
+
+ vector_container& get_vector () { return c.c; }
+
+ public:
+
+ container c;
+};
+
+#endif
diff --git a/Runtime/Utilities/vector_utility.h b/Runtime/Utilities/vector_utility.h
new file mode 100644
index 0000000..a632413
--- /dev/null
+++ b/Runtime/Utilities/vector_utility.h
@@ -0,0 +1,95 @@
+#ifndef VECTOR_UTILITY_H
+#define VECTOR_UTILITY_H
+
+// stl vector doesnt deallocate memory on itself.
+// push_back and resizing increases memory by a factor of 2, when growing
+// pop_back, resizing and even clear do not deallocate any memory
+// This of course wastes a lot of memory, fortunately there is the swap trick to
+// bring back memory usage to normality
+//#include <algorithm>
+
+template<class T>
+inline void trim_vector (T& v)
+{
+ if (v.capacity () != v.size ())
+ {
+ T temp = v;
+ temp.swap (v);
+ }
+}
+
+// The following functions do the same as their vector member function
+// equivalents, only they allocate exactly the requested amount of memory.
+
+template<class T>
+inline void resize_trimmed (T& v, unsigned int sz)
+{
+ // the vector is growing
+ if (sz > v.size ())
+ {
+ if (sz != v.capacity ())
+ {
+ T temp (v.get_allocator());
+ temp.reserve (sz);
+ temp.assign (v.begin (), v.end ());
+ temp.resize (sz);
+ temp.swap (v);
+ }
+ else
+ v.resize (sz);
+ }
+ // the vector is shrinking
+ else if (sz < v.size ())
+ {
+ T temp (v.begin (), v.begin () + sz, v.get_allocator());
+ temp.swap (v);
+ }
+}
+
+template<class T>
+inline void reserve_trimmed (T& v, unsigned int sz)
+{
+ if (sz != v.capacity ())
+ {
+ T temp;
+ temp.reserve (sz);
+ temp.assign (v.begin (), v.end ());
+ temp.swap (v);
+ }
+}
+
+template<class T>
+inline void clear_trimmed (T& v)
+{
+ T temp;
+ temp.swap (v);
+}
+
+template<class T>
+inline void push_back_trimmed (T& vec, const typename T::value_type& value)
+{
+ if (vec.size () + 1 != vec.capacity ())
+ {
+ T temp;
+ temp.reserve (vec.size () + 1);
+ temp.assign (vec.begin (), vec.end ());
+ temp.push_back (value);
+ temp.swap (vec);
+ }
+ else
+ vec.push_back (value);
+}
+
+template<class T>
+inline void erase_trimmed (T& vec, typename T::iterator i)
+{
+ AssertIf (i == vec.end ());
+
+ T temp;
+ temp.reserve (vec.size () - 1);
+ temp.assign (vec.begin (), i);
+ temp.insert (temp.end (), ++i, vec.end ());
+ vec.swap (temp);
+}
+
+#endif