summaryrefslogtreecommitdiff
path: root/Runtime/Allocator
diff options
context:
space:
mode:
Diffstat (limited to 'Runtime/Allocator')
-rw-r--r--Runtime/Allocator/AllocationHeader.h159
-rw-r--r--Runtime/Allocator/BaseAllocator.cpp21
-rw-r--r--Runtime/Allocator/BaseAllocator.h81
-rw-r--r--Runtime/Allocator/DualThreadAllocator.cpp263
-rw-r--r--Runtime/Allocator/DualThreadAllocator.h55
-rw-r--r--Runtime/Allocator/DynamicHeapAllocator.cpp486
-rw-r--r--Runtime/Allocator/DynamicHeapAllocator.h81
-rw-r--r--Runtime/Allocator/FixedHeapAllocator.cpp68
-rw-r--r--Runtime/Allocator/FixedHeapAllocator.h31
-rw-r--r--Runtime/Allocator/FixedSizeAllocator.h341
-rw-r--r--Runtime/Allocator/LinearAllocator.cpp38
-rw-r--r--Runtime/Allocator/LinearAllocator.h391
-rw-r--r--Runtime/Allocator/LowLevelDefaultAllocator.cpp33
-rw-r--r--Runtime/Allocator/LowLevelDefaultAllocator.h25
-rw-r--r--Runtime/Allocator/MemoryMacros.cpp85
-rw-r--r--Runtime/Allocator/MemoryMacros.h237
-rw-r--r--Runtime/Allocator/MemoryManager.cpp1659
-rw-r--r--Runtime/Allocator/MemoryManager.h118
-rw-r--r--Runtime/Allocator/STLAllocator.h146
-rw-r--r--Runtime/Allocator/StackAllocator.cpp213
-rw-r--r--Runtime/Allocator/StackAllocator.h114
-rw-r--r--Runtime/Allocator/TLSAllocator.cpp198
-rw-r--r--Runtime/Allocator/TLSAllocator.h55
-rw-r--r--Runtime/Allocator/UnityDefaultAllocator.cpp284
-rw-r--r--Runtime/Allocator/UnityDefaultAllocator.h70
-rw-r--r--Runtime/Allocator/tlsf/tlsf.c966
-rw-r--r--Runtime/Allocator/tlsf/tlsf.h52
-rw-r--r--Runtime/Allocator/tlsf/tlsfbits.h184
28 files changed, 6454 insertions, 0 deletions
diff --git a/Runtime/Allocator/AllocationHeader.h b/Runtime/Allocator/AllocationHeader.h
new file mode 100644
index 0000000..8d66159
--- /dev/null
+++ b/Runtime/Allocator/AllocationHeader.h
@@ -0,0 +1,159 @@
+#ifndef ALLOCATION_HEADER_H_
+#define ALLOCATION_HEADER_H_
+
+#if ENABLE_MEMORY_MANAGER
+
+#include "Runtime/Profiler/MemoryProfiler.h"
+
+#define USE_MEMORY_DEBUGGING (!UNITY_RELEASE && !(UNITY_LINUX && UNITY_64))
+#if UNITY_LINUX
+#pragma warning "FIXME LINUX: 64bit memory debugging"
+#endif
+
+/*
+Allocation Header:
+(12345hhhhhhhhmmmm____________) (1-n:paddingcount, h: header, m:memoryprofiler)
+hasPadding: 1 bit
+size: 31 bit
+*********USE_MEMORY_DEBUGGING*********
+ allocator: 16 bit
+ magicValue: 12 bit
+*********USE_MEMORY_DEBUGGING*********
+---------------
+Followed by x bytes requested by MemoryProfiler
+----------------------------------------
+Actual Allocated Data
+----------------------------------------
+*********USE_MEMORY_DEBUGGING*********
+Followed by a footer if memory guarding is enabled
+*********USE_MEMORY_DEBUGGING*********
+*/
+
+struct AllocationHeader
+{
+public:
+ static void Set(void* ptr, int id, int size, int padCount, int align);
+ static AllocationHeader* GetHeader(const void* ptr);
+ static ProfilerAllocationHeader* GetProfilerHeader(const void* ptr);
+
+ UInt32 GetPadding () const { return m_HasPadding ? *(((UInt32*)(this))-1) : 0; }
+ size_t GetRequestedSize () const { return m_AllocationSize; }
+
+ static size_t CalculateNeededAllocationSize( size_t size, int align );
+ static void ValidateIntegrity(void* ptr, int id, int align = -1);
+ static void* GetRealPointer( void* ptr );
+ static int GetRequiredPadding(void* realptr, int align);
+ static int GetHeaderSize();
+ static AllocationHeader* GetHeaderFromRealPointer( const void* realptr );
+
+ int GetOverheadSize();
+
+private:
+
+ // prev byte is padding count, if there is padding
+ UInt32 m_HasPadding : 1;
+ UInt32 m_AllocationSize : 31;
+
+#if USE_MEMORY_DEBUGGING
+ // DEBUG:
+ SInt16 m_AllocatorIdentifier;
+ UInt16 m_Magic : 12;
+ static const UInt32 kMagicValue = 0xDFA;
+ static const UInt32 kFooterSize = 4;
+#else
+ static const UInt32 kFooterSize = 0;
+#endif
+
+ // followed by x bytes used for memory profiling header (0 if no memory profiling)
+};
+
+inline void AllocationHeader::Set(void* ptr, int id, int size, int padCount, int align)
+{
+ AllocationHeader* header = GetHeader(ptr);
+ header->m_AllocationSize = size;
+ header->m_HasPadding = padCount != 0;
+ if(header->m_HasPadding)
+ *(((UInt32*)(header))-1) = padCount;
+#if USE_MEMORY_DEBUGGING
+ // set header
+ if(header->m_HasPadding) // set leading bytes
+ memset(((char*)header)-padCount,0xAA,padCount-4);
+
+ header->m_AllocatorIdentifier = id;
+ header->m_Magic = kMagicValue;
+ // set footer
+ memset(((char*)ptr)+size,0xFD,kFooterSize);
+#endif
+}
+
+inline ProfilerAllocationHeader* AllocationHeader::GetProfilerHeader( const void* ptr )
+{
+ return (ProfilerAllocationHeader*)( (const char*)ptr - MemoryProfiler::GetHeaderSize() );
+}
+
+inline AllocationHeader* AllocationHeader::GetHeader( const void* ptr )
+{
+ return (AllocationHeader*)( (const char*)ptr - GetHeaderSize() );
+}
+
+inline int AllocationHeader::GetHeaderSize()
+{
+ return sizeof(AllocationHeader) + MemoryProfiler::GetHeaderSize();
+}
+
+inline int AllocationHeader::GetRequiredPadding( void* realptr, int align )
+{
+ return align - ((((int)realptr + GetHeaderSize() - 1)&(align - 1)) + 1);
+}
+
+inline size_t AllocationHeader::CalculateNeededAllocationSize( size_t size, int align )
+{
+ int alignMask = align-1;
+ return size + GetHeaderSize() + kFooterSize + alignMask;
+}
+
+inline void* AllocationHeader::GetRealPointer( void* ptr )
+{
+ AllocationHeader* header = GetHeader(ptr);
+ int padCount = header->GetPadding();
+ return ((char*)header) - padCount;
+}
+
+inline int AllocationHeader::GetOverheadSize()
+{
+ int alignMask = kDefaultMemoryAlignment-1; // estimate
+ return GetHeaderSize() + kFooterSize + alignMask;
+}
+
+inline AllocationHeader* AllocationHeader::GetHeaderFromRealPointer( const void* realptr )
+{
+#if USE_MEMORY_DEBUGGING
+ unsigned char* ptr = (unsigned char*)realptr;
+ while(*ptr == 0xAA)
+ ptr++;
+ AllocationHeader* header = (AllocationHeader*)ptr;
+ if(header->m_Magic != kMagicValue)
+ header = (AllocationHeader*)(ptr+4);
+ return header;
+#else
+ return NULL;
+#endif
+}
+
+inline void AllocationHeader::ValidateIntegrity( void* ptr, int id, int /*align*/ )
+{
+#if USE_MEMORY_DEBUGGING
+ AllocationHeader* header = GetHeader(ptr);
+ Assert(header->m_Magic == AllocationHeader::kMagicValue);
+ Assert(id == -1 || id == header->m_AllocatorIdentifier);
+
+ int size = header->m_AllocationSize;
+ unsigned char* footer = ((unsigned char*)ptr)+size;
+ for(int i = 0; i < kFooterSize; i++, footer++)
+ Assert(*footer == 0xFD);
+#endif
+}
+
+#endif
+
+#endif
diff --git a/Runtime/Allocator/BaseAllocator.cpp b/Runtime/Allocator/BaseAllocator.cpp
new file mode 100644
index 0000000..6ce5d0e
--- /dev/null
+++ b/Runtime/Allocator/BaseAllocator.cpp
@@ -0,0 +1,21 @@
+#include "UnityPrefix.h"
+#include "BaseAllocator.h"
+
+void* BaseAllocator::Reallocate( void* p, size_t size, int align )
+{
+// ErrorString("Not implemented");
+ return 0;
+}
+
+static UInt32 g_IncrementIdentifier = 0x10;
+BaseAllocator::BaseAllocator(const char* name)
+: m_TotalRequestedBytes(0)
+, m_TotalReservedMemory(0)
+, m_BookKeepingMemoryUsage(0)
+, m_PeakRequestedBytes(0)
+, m_NumAllocations(0)
+, m_Name(name)
+{
+ m_AllocatorIdentifier = g_IncrementIdentifier++;
+}
+
diff --git a/Runtime/Allocator/BaseAllocator.h b/Runtime/Allocator/BaseAllocator.h
new file mode 100644
index 0000000..a60f95d
--- /dev/null
+++ b/Runtime/Allocator/BaseAllocator.h
@@ -0,0 +1,81 @@
+#ifndef BASE_ALLOCATOR_H_
+#define BASE_ALLOCATOR_H_
+
+#if UNITY_OSX || UNITY_LINUX || UNITY_BB10 || UNITY_TIZEN
+#include <stddef.h> // for size_t
+#endif
+
+#include "Runtime/Misc/AllocatorLabels.h"
+
+class BaseAllocator
+{
+public:
+ BaseAllocator(const char* name);
+ virtual ~BaseAllocator () {}
+
+ virtual void* Allocate (size_t size, int align) = 0;
+ virtual void* Reallocate (void* p, size_t size, int align);
+ virtual void Deallocate (void* p) = 0;
+
+ virtual bool Contains (const void* p) = 0;
+ virtual bool IsAssigned() const { return true; }
+ virtual bool CheckIntegrity() { return true; }
+ virtual bool ValidatePointer(void* /*ptr*/) { return true; }
+ // return the actual number of requests bytes
+ virtual size_t GetAllocatedMemorySize() const { return m_TotalRequestedBytes; }
+
+ // get total used size (including overhead allocations)
+ virtual size_t GetAllocatorSizeTotalUsed() const { return m_TotalRequestedBytes + m_BookKeepingMemoryUsage; }
+
+ // get the reserved size of the allocator (including all overhead memory allocated)
+ virtual size_t GetReservedSizeTotal() const { return m_TotalReservedMemory; }
+
+ // get the peak allocated size of the allocator
+ virtual size_t GetPeakAllocatedMemorySize() const { return m_PeakRequestedBytes; }
+
+ // return the free block count for each pow2
+ virtual void GetFreeBlockCount(int* /*freeCount*/, int /*size*/) { return; }
+ // return the used block count for each pow2
+ virtual void GetUsedBlockCount(int* /*usedCount*/, int /*size*/) { return; }
+
+ virtual size_t GetPtrSize(const void* /*ptr*/) const {return 0;}
+ // return NULL if allocator does not allocate the memory profile header
+ virtual ProfilerAllocationHeader* GetProfilerHeader(const void* /*ptr*/) const { return NULL; }
+
+ virtual const char* GetName() const { return m_Name; }
+
+ virtual void ThreadInitialize(BaseAllocator* /*allocator*/) {}
+ virtual void ThreadCleanup() {}
+
+ virtual void FrameMaintenance(bool /*cleanup*/) {}
+
+protected:
+ void RegisterAllocationData(size_t requestedSize, size_t overhead);
+ void RegisterDeallocationData(size_t requestedSize, size_t overhead);
+
+ const char* m_Name;
+ UInt32 m_AllocatorIdentifier;
+ size_t m_TotalRequestedBytes; // Memory requested by the allocator
+ size_t m_TotalReservedMemory; // All memory reserved by the allocator
+ size_t m_BookKeepingMemoryUsage; // memory used for bookkeeping (headers etc.)
+ size_t m_PeakRequestedBytes; // Memory requested by the allocator
+ UInt32 m_NumAllocations; // Allocation count
+
+};
+
+inline void BaseAllocator::RegisterAllocationData(size_t requestedSize, size_t overhead)
+{
+ m_TotalRequestedBytes += requestedSize;
+ m_BookKeepingMemoryUsage += overhead;
+ m_PeakRequestedBytes = m_TotalRequestedBytes > m_PeakRequestedBytes ? m_TotalRequestedBytes : m_PeakRequestedBytes;
+ m_NumAllocations++;
+}
+
+inline void BaseAllocator::RegisterDeallocationData(size_t requestedSize, size_t overhead)
+{
+ m_TotalRequestedBytes -= requestedSize;
+ m_BookKeepingMemoryUsage -= overhead;
+ m_NumAllocations--;
+}
+
+#endif
diff --git a/Runtime/Allocator/DualThreadAllocator.cpp b/Runtime/Allocator/DualThreadAllocator.cpp
new file mode 100644
index 0000000..921ecf9
--- /dev/null
+++ b/Runtime/Allocator/DualThreadAllocator.cpp
@@ -0,0 +1,263 @@
+#include "UnityPrefix.h"
+#include "DualThreadAllocator.h"
+#include "Runtime/Threads/Thread.h"
+#include "Runtime/Allocator/DynamicHeapAllocator.h"
+
+#if ENABLE_MEMORY_MANAGER
+
+class DelayedPointerDeletionManager
+{
+public:
+ DelayedPointerDeletionManager(BaseAllocator* mainAlloc, BaseAllocator* threadAlloc) :
+ m_HasPendingDeletes (0),
+ m_MainAlloctor (mainAlloc),
+ m_ThreadAlloctor (threadAlloc),
+ m_MainThreadPendingPointers(NULL),
+ m_MainThreadPendingCount (0),
+ m_MainThreadPendingReserved (0) {}
+ ~DelayedPointerDeletionManager(){ CleanupPendingMainThreadPointers(); }
+
+ bool HasPending() {return m_HasPendingDeletes != 0;}
+ void AddPointerToMainThreadDealloc( void* ptr) ;
+ void CleanupPendingMainThreadPointers() ;
+ void DeallocateLocalMemory();
+private:
+ void GrowPendingBuffer();
+ void** m_MainThreadPendingPointers;
+ UInt32 m_MainThreadPendingCount;
+ UInt32 m_MainThreadPendingReserved;
+
+ volatile int m_HasPendingDeletes;
+ BaseAllocator* m_MainAlloctor;
+ BaseAllocator* m_ThreadAlloctor;
+ Mutex m_MainThreadPendingPointersMutex;
+};
+
+void DelayedPointerDeletionManager::AddPointerToMainThreadDealloc( void* ptr)
+{
+ Mutex::AutoLock autolock(m_MainThreadPendingPointersMutex);
+ if(++m_MainThreadPendingCount > m_MainThreadPendingReserved)
+ GrowPendingBuffer();
+ m_MainThreadPendingPointers[m_MainThreadPendingCount-1] = ptr;
+ m_HasPendingDeletes = 1;
+}
+
+void DelayedPointerDeletionManager::CleanupPendingMainThreadPointers()
+{
+ Mutex::AutoLock autolock(m_MainThreadPendingPointersMutex);
+ Assert(Thread::CurrentThreadIsMainThread());
+ m_HasPendingDeletes = 0;
+
+ for(UInt32 i = 0; i < m_MainThreadPendingCount; ++i)
+ m_MainAlloctor->Deallocate(m_MainThreadPendingPointers[i]);
+ m_MainThreadPendingCount = 0;
+}
+
+void DelayedPointerDeletionManager::DeallocateLocalMemory()
+{
+ Assert(!m_HasPendingDeletes && m_MainThreadPendingCount == 0);
+ m_ThreadAlloctor->Deallocate(m_MainThreadPendingPointers);
+ m_MainThreadPendingPointers = NULL;
+ m_MainThreadPendingReserved = 0;
+};
+
+void DelayedPointerDeletionManager::GrowPendingBuffer()
+{
+ const UInt32 kInitialBufferSize = 128;
+ m_MainThreadPendingReserved = std::max(m_MainThreadPendingReserved*2, kInitialBufferSize);
+ m_MainThreadPendingPointers = (void**) m_ThreadAlloctor->Reallocate(m_MainThreadPendingPointers, m_MainThreadPendingReserved*sizeof(void*), kDefaultMemoryAlignment);
+}
+
+
+template <class UnderlyingAllocator>
+DualThreadAllocator<UnderlyingAllocator>::DualThreadAllocator(const char* name, BaseAllocator* mainAllocator, BaseAllocator* threadAllocator)
+: BaseAllocator(name)
+{
+ m_MainAllocator = (UnderlyingAllocator*)mainAllocator;
+ m_ThreadAllocator = (UnderlyingAllocator*)threadAllocator;
+ m_DelayedDeletion = NULL;
+}
+
+template <class UnderlyingAllocator>
+void DualThreadAllocator<UnderlyingAllocator>::ThreadCleanup()
+{
+ if(Thread::CurrentThreadIsMainThread())
+ UNITY_DELETE(m_DelayedDeletion, kMemManager);
+}
+
+template <class UnderlyingAllocator>
+void DualThreadAllocator<UnderlyingAllocator>::FrameMaintenance(bool cleanup)
+{
+ if(m_DelayedDeletion)
+ {
+ m_DelayedDeletion->CleanupPendingMainThreadPointers();
+ if(cleanup)
+ m_DelayedDeletion->DeallocateLocalMemory();
+ }
+}
+
+template <class UnderlyingAllocator>
+UnderlyingAllocator* DualThreadAllocator<UnderlyingAllocator>::GetCurrentAllocator()
+{
+ if(Thread::CurrentThreadIsMainThread())
+ return m_MainAllocator;
+ else
+ return m_ThreadAllocator;
+}
+
+
+template <class UnderlyingAllocator>
+void* DualThreadAllocator<UnderlyingAllocator>::Allocate( size_t size, int align )
+{
+ UnderlyingAllocator* alloc = GetCurrentAllocator();
+ bool isMainThread = alloc == m_MainAllocator;
+ if(isMainThread && m_DelayedDeletion && m_DelayedDeletion->HasPending())
+ m_DelayedDeletion->CleanupPendingMainThreadPointers();
+
+ return alloc->UnderlyingAllocator::Allocate(size, align);
+}
+
+template <class UnderlyingAllocator>
+void* DualThreadAllocator<UnderlyingAllocator>::Reallocate( void* p, size_t size, int align )
+{
+ UnderlyingAllocator* alloc = GetCurrentAllocator();
+
+ if(alloc->UnderlyingAllocator::Contains(p))
+ return alloc->UnderlyingAllocator::Reallocate(p, size, align);
+
+ UnderlyingAllocator* containingAlloc = NULL;
+ if (alloc == m_MainAllocator)
+ {
+ Assert(m_ThreadAllocator->UnderlyingAllocator::Contains(p));
+ containingAlloc = m_ThreadAllocator;
+ }
+ else
+ {
+ Assert(m_MainAllocator->UnderlyingAllocator::Contains(p));
+ containingAlloc = m_MainAllocator;
+ }
+
+ size_t oldSize = containingAlloc->UnderlyingAllocator::GetPtrSize(p);
+ void* ptr = alloc->UnderlyingAllocator::Allocate(size, align);
+ memcpy(ptr, p, std::min(size,oldSize));
+ Deallocate(p);
+ return ptr;
+}
+
+template <class UnderlyingAllocator>
+void DualThreadAllocator<UnderlyingAllocator>::Deallocate( void* p )
+{
+ UnderlyingAllocator* alloc = GetCurrentAllocator();
+
+ if(alloc->UnderlyingAllocator::Contains(p))
+ return alloc->UnderlyingAllocator::Deallocate(p);
+
+ if (alloc == m_MainAllocator)
+ {
+ DebugAssert(m_ThreadAllocator->UnderlyingAllocator::Contains(p));
+ m_ThreadAllocator->UnderlyingAllocator::Deallocate(p);
+ }
+ else
+ {
+ DebugAssert(m_MainAllocator->UnderlyingAllocator::Contains(p));
+ if(!m_DelayedDeletion)
+ {
+ SET_ALLOC_OWNER(NULL);
+ m_DelayedDeletion = UNITY_NEW(DelayedPointerDeletionManager(m_MainAllocator, m_ThreadAllocator), kMemManager);
+ }
+ m_DelayedDeletion->AddPointerToMainThreadDealloc(p);
+ }
+}
+
+template <class UnderlyingAllocator>
+bool DualThreadAllocator<UnderlyingAllocator>::TryDeallocate( void* p )
+{
+ UnderlyingAllocator* alloc = GetCurrentAllocator();
+
+ if(alloc->UnderlyingAllocator::Contains(p))
+ {
+ alloc->UnderlyingAllocator::Deallocate(p);
+ return true;
+ }
+
+ if (m_ThreadAllocator->UnderlyingAllocator::Contains(p))
+ {
+ m_ThreadAllocator->UnderlyingAllocator::Deallocate(p);
+ return true;
+ }
+ if(m_MainAllocator->UnderlyingAllocator::Contains(p))
+ {
+ if(!m_DelayedDeletion)
+ m_DelayedDeletion = UNITY_NEW(DelayedPointerDeletionManager(m_MainAllocator, m_ThreadAllocator), kMemManager);
+ m_DelayedDeletion->AddPointerToMainThreadDealloc(p);
+ return true;
+ }
+ return false;
+}
+
+
+template <class UnderlyingAllocator>
+bool DualThreadAllocator<UnderlyingAllocator>::Contains( const void* p )
+{
+ UnderlyingAllocator* alloc = GetCurrentAllocator();
+ if(alloc->UnderlyingAllocator::Contains(p))
+ return true;
+ if(m_ThreadAllocator->UnderlyingAllocator::Contains(p))
+ return true;
+ if(m_MainAllocator->UnderlyingAllocator::Contains(p))
+ return true;
+ return false;
+}
+
+template <class UnderlyingAllocator>
+size_t DualThreadAllocator<UnderlyingAllocator>::GetAllocatedMemorySize( ) const
+{
+ return m_MainAllocator->GetAllocatedMemorySize() + (m_ThreadAllocator?m_ThreadAllocator->GetAllocatedMemorySize():0);
+}
+
+template <class UnderlyingAllocator>
+size_t DualThreadAllocator<UnderlyingAllocator>::GetAllocatorSizeTotalUsed() const
+{
+ return m_MainAllocator->GetAllocatorSizeTotalUsed() + (m_ThreadAllocator?m_ThreadAllocator->GetAllocatorSizeTotalUsed():0);
+}
+
+template <class UnderlyingAllocator>
+size_t DualThreadAllocator<UnderlyingAllocator>::GetReservedSizeTotal() const
+{
+ return m_MainAllocator->GetReservedSizeTotal() + (m_ThreadAllocator?m_ThreadAllocator->GetReservedSizeTotal():0);
+}
+
+template <class UnderlyingAllocator>
+size_t DualThreadAllocator<UnderlyingAllocator>::GetPtrSize( const void* ptr ) const
+{
+ // all allocators have the same allocation header
+ return m_MainAllocator->UnderlyingAllocator::GetPtrSize(ptr);
+}
+
+template <class UnderlyingAllocator>
+ProfilerAllocationHeader* DualThreadAllocator<UnderlyingAllocator>::GetProfilerHeader( const void* ptr ) const
+{
+ return m_MainAllocator->UnderlyingAllocator::GetProfilerHeader(ptr);
+}
+
+
+template <class UnderlyingAllocator>
+bool DualThreadAllocator<UnderlyingAllocator>::CheckIntegrity()
+{
+ bool valid = m_ThreadAllocator->UnderlyingAllocator::CheckIntegrity();
+ if(Thread::CurrentThreadIsMainThread())
+ valid &= m_MainAllocator->UnderlyingAllocator::CheckIntegrity();
+ Assert(valid);
+ return valid;
+}
+
+template <class UnderlyingAllocator>
+bool DualThreadAllocator<UnderlyingAllocator>::ValidatePointer(void* ptr)
+{
+ UnderlyingAllocator* alloc = GetCurrentAllocator();
+ return alloc->UnderlyingAllocator::ValidatePointer(ptr);
+}
+
+template class DualThreadAllocator< DynamicHeapAllocator< LowLevelAllocator > >;
+
+#endif // #if ENABLE_MEMORY_MANAGER
diff --git a/Runtime/Allocator/DualThreadAllocator.h b/Runtime/Allocator/DualThreadAllocator.h
new file mode 100644
index 0000000..089b4ca
--- /dev/null
+++ b/Runtime/Allocator/DualThreadAllocator.h
@@ -0,0 +1,55 @@
+#ifndef DUALTHREAD_ALLOCATOR_H_
+#define DUALTHREAD_ALLOCATOR_H_
+
+#if ENABLE_MEMORY_MANAGER
+
+#include "Runtime/Allocator/BaseAllocator.h"
+#include "Runtime/Threads/ThreadSpecificValue.h"
+
+class DelayedPointerDeletionManager;
+
+// Dual Thread Allocator is an indirection to a real allocator
+
+// Has pointer to the main allocator (nonlocking)
+// Has pointer to the shared thread allocator (locking)
+
+template <class UnderlyingAllocator>
+class DualThreadAllocator : public BaseAllocator
+{
+public:
+ // when constructing it will be from the main thread
+ DualThreadAllocator(const char* name, BaseAllocator* mainAllocator, BaseAllocator* threadAllocator);
+ virtual ~DualThreadAllocator() {}
+
+ virtual void* Allocate(size_t size, int align);
+ virtual void* Reallocate (void* p, size_t size, int align);
+ virtual void Deallocate (void* p);
+ virtual bool Contains (const void* p);
+
+ virtual size_t GetAllocatedMemorySize() const;
+ virtual size_t GetAllocatorSizeTotalUsed() const;
+ virtual size_t GetReservedSizeTotal() const;
+
+ virtual size_t GetPtrSize(const void* ptr) const;
+ virtual ProfilerAllocationHeader* GetProfilerHeader(const void* ptr) const;
+
+ virtual void ThreadCleanup();
+
+ virtual bool CheckIntegrity();
+ virtual bool ValidatePointer(void* ptr);
+
+ bool TryDeallocate (void* p);
+
+ virtual void FrameMaintenance(bool cleanup);
+
+private:
+ UnderlyingAllocator* GetCurrentAllocator();
+
+ UnderlyingAllocator* m_MainAllocator;
+ UnderlyingAllocator* m_ThreadAllocator;
+
+ DelayedPointerDeletionManager* m_DelayedDeletion;
+};
+
+#endif
+#endif
diff --git a/Runtime/Allocator/DynamicHeapAllocator.cpp b/Runtime/Allocator/DynamicHeapAllocator.cpp
new file mode 100644
index 0000000..84ef6ef
--- /dev/null
+++ b/Runtime/Allocator/DynamicHeapAllocator.cpp
@@ -0,0 +1,486 @@
+#include "UnityPrefix.h"
+#include "DynamicHeapAllocator.h"
+#if ENABLE_MEMORY_MANAGER
+
+#include "Runtime/Allocator/AllocationHeader.h"
+#include "tlsf/tlsf.h"
+#include "Runtime/Utilities/BitUtility.h"
+#include "Runtime/Profiler/MemoryProfiler.h"
+#if UNITY_XENON
+#include "PlatformDependent/Xbox360/Source/XenonMemory.h"
+#endif
+#include "Runtime/Threads/Thread.h"
+#include "Runtime/Threads/AtomicOps.h"
+
+template<class LLAllocator>
+DynamicHeapAllocator<LLAllocator>::DynamicHeapAllocator(UInt32 poolIncrementSize, size_t splitLimit, bool useLocking, const char* name)
+: BaseAllocator(name), m_UseLocking(useLocking)
+{
+ m_SplitLimit = splitLimit;
+ m_RequestedPoolSize = poolIncrementSize;
+ m_FirstLargeAllocation = NULL;
+}
+
+template<class LLAllocator>
+DynamicHeapAllocator<LLAllocator>::~DynamicHeapAllocator()
+{
+ Mutex::AutoLock m(m_DHAMutex);
+
+ for(ListIterator<PoolElement> i=m_SmallTLSFPools.begin();i != m_SmallTLSFPools.end();i++)
+ {
+ PoolElement& pool = *i;
+ tlsf_destroy(pool.tlsfPool);
+ LLAllocator::Free(pool.memoryBase);
+ }
+ for(ListIterator<PoolElement> i=m_LargeTLSFPools.begin();i != m_LargeTLSFPools.end();i++)
+ {
+ PoolElement& pool = *i;
+ tlsf_destroy(pool.tlsfPool);
+ LLAllocator::Free(pool.memoryBase);
+ }
+}
+
+template<class LLAllocator>
+typename DynamicHeapAllocator<LLAllocator>::PoolElement& DynamicHeapAllocator<LLAllocator>::GetActivePool( size_t size )
+{
+ return GetPoolList( size ).front();
+}
+
+template<class LLAllocator>
+typename DynamicHeapAllocator<LLAllocator>::PoolList& DynamicHeapAllocator<LLAllocator>::GetPoolList( size_t size )
+{
+ return size < m_SplitLimit? m_SmallTLSFPools: m_LargeTLSFPools;
+}
+
+template<class LLAllocator>
+void* DynamicHeapAllocator<LLAllocator>::Allocate(size_t size, int align)
+{
+ if(m_UseLocking)
+ m_DHAMutex.Lock();
+
+ DebugAssert(align > 0 && align <= 16*1024 && IsPowerOfTwo(align));
+
+ size_t realSize = AllocationHeader::CalculateNeededAllocationSize(size, align);;
+
+ /// align size to tlsf block requirements
+ if(realSize > 32)
+ {
+ int tlsfalign = (1 << HighestBit(realSize >> 5))-1;
+ realSize = (realSize + tlsfalign) & ~tlsfalign;
+ }
+
+ char* ptr = NULL;
+ if(!GetPoolList(realSize).empty())
+ ptr = (char*)tlsf_malloc(GetActivePool(realSize).tlsfPool, realSize);
+ LargeAllocations* largeAlloc = NULL;
+ if(ptr == NULL)
+ {
+ // only try to make new tlsfBlocks if the amount is less than a 16th of the blocksize - else spill to LargeAllocations
+ if(size < m_RequestedPoolSize/4)
+ {
+ // not enough space in the current block.
+ // Iterate from the back, and find one that fits the allocation
+ // put the found block at the head of the list
+ PoolList& poolList = GetPoolList(realSize);
+ ListIterator<PoolElement> pool = poolList.end();
+ --pool;
+ while(pool != poolList.end()) // List wraps around so end is the element just before the head of the list
+ {
+ ptr = (char*)tlsf_malloc(pool->tlsfPool, realSize);
+ if(ptr != NULL)
+ {
+ // push_front removes the node from the list and reinserts it at the start
+ Mutex::AutoLock m(m_DHAMutex);
+ poolList.push_front(*pool);
+ break;
+ }
+ --pool;
+ }
+
+ if(ptr == 0)
+ {
+ int allocatePoolSize = m_RequestedPoolSize;
+ void* memoryBlock = NULL;
+
+ #if UNITY_ANDROID
+ // HACK:
+ // on android we reload libunity (and keep activity around)
+ // this results in leaks, as MemoryManager::m_InitialFallbackAllocator is never freed actually
+ // more to it: even if we free the mem - the hole left is taken by other mallocs
+ // so we cant reuse it on re-init, effectively allocing anew
+ // this hack can be removed when unity can be cleanly reloaded (sweet dreams)
+ static const int _InitialFallbackAllocMemBlock_Size = 1024*1024;
+ static bool _InitialFallbackAllocMemBlock_Taken = false;
+ static char _InitialFallbackAllocMemBlock[_InitialFallbackAllocMemBlock_Size];
+
+ if(!_InitialFallbackAllocMemBlock_Taken)
+ {
+ Assert(_InitialFallbackAllocMemBlock_Size == m_RequestedPoolSize);
+ _InitialFallbackAllocMemBlock_Taken = true;
+ memoryBlock = _InitialFallbackAllocMemBlock;
+ }
+ #endif
+
+ while(!memoryBlock && allocatePoolSize > size*2)
+ {
+ memoryBlock = LLAllocator::Malloc(allocatePoolSize);
+ if(!memoryBlock)
+ allocatePoolSize /= 2;
+ }
+
+ if(memoryBlock)
+ {
+ m_TotalReservedMemory += allocatePoolSize;
+ PoolElement* newPoolPtr = (PoolElement*)LLAllocator::Malloc(sizeof(PoolElement));
+ PoolElement& newPool = *new (newPoolPtr) PoolElement();
+ newPool.memoryBase = (char*)memoryBlock;
+ newPool.memorySize = allocatePoolSize;
+ newPool.tlsfPool = tlsf_create(memoryBlock, allocatePoolSize);
+ newPool.allocationCount = 0;
+ newPool.allocationSize = 0;
+
+ {
+ Mutex::AutoLock lock(m_DHAMutex);
+ poolList.push_front(newPool);
+ }
+
+ ptr = (char*)tlsf_malloc(GetActivePool(realSize).tlsfPool, realSize);
+ }
+ }
+ }
+ if(ptr == 0)
+ {
+ // large allocation that don't fit on a clean block
+ largeAlloc = (LargeAllocations*)LLAllocator::Malloc(sizeof(LargeAllocations));
+ largeAlloc->allocation = (char*)LLAllocator::Malloc(realSize);
+ if(largeAlloc->allocation == NULL)
+ {
+ printf_console("DynamicHeapAllocator out of memory - Could not get memory for large allocation");
+ if(m_UseLocking)
+ m_DHAMutex.Unlock();
+ return NULL;
+ }
+ largeAlloc->next = m_FirstLargeAllocation;
+ largeAlloc->size = size;
+ m_TotalReservedMemory += size;
+ {
+ Mutex::AutoLock lock(m_DHAMutex);
+ m_FirstLargeAllocation = largeAlloc;
+ }
+ ptr = largeAlloc->allocation;
+ }
+ }
+
+ if(!largeAlloc)
+ {
+ GetActivePool(realSize).allocationCount++;
+ GetActivePool(realSize).allocationSize+=size;
+ }
+
+ void* realPtr = AddHeaderAndFooter(ptr, size, align);
+ RegisterAllocation(realPtr);
+
+ if (largeAlloc)
+ largeAlloc->returnedPtr = realPtr;
+
+ if(m_UseLocking)
+ m_DHAMutex.Unlock();
+
+ return realPtr;
+}
+
+template<class LLAllocator>
+void* DynamicHeapAllocator<LLAllocator>::Reallocate (void* p, size_t size, int align)
+{
+ if (p == NULL)
+ return Allocate(size, align);
+
+ if(m_UseLocking)
+ m_DHAMutex.Lock();
+
+ AllocationHeader::ValidateIntegrity(p, m_AllocatorIdentifier, align);
+ RegisterDeallocation(p);
+
+ size_t oldSize = GetPtrSize(p);
+ size_t oldPadCount = AllocationHeader::GetHeader(p)->GetPadding();
+
+ void* realPtr = AllocationHeader::GetRealPointer(p);
+ size_t realSize = AllocationHeader::CalculateNeededAllocationSize(size, align);
+
+ PoolElement* allocedPool = FindPoolFromPtr(realPtr);
+ char* realNewPtr = NULL;
+ if(allocedPool != NULL)
+ {
+ realNewPtr = (char*)tlsf_realloc(allocedPool->tlsfPool, realPtr, realSize);
+ }
+
+ if(realNewPtr == NULL)
+ {
+ // if we didn't succeed doing a tlsf reallocation, just allocate, copy and delete
+ RegisterAllocation(p); // Reregister the allocation again
+ if(m_UseLocking)
+ m_DHAMutex.Unlock();
+ void* newPtr = Allocate(size, align);
+ if (newPtr != NULL)
+ memcpy(newPtr, p, ( oldSize < size ? oldSize : size ));
+ Deallocate(p);
+ return newPtr;
+ }
+
+ int newPadCount = AllocationHeader::GetRequiredPadding(realNewPtr, align);
+
+ if (newPadCount != oldPadCount){
+ // new ptr needs different align padding. move memory and repad
+ char* srcptr = realNewPtr + AllocationHeader::GetHeaderSize() + oldPadCount;
+ char* dstptr = realNewPtr + AllocationHeader::GetHeaderSize() + newPadCount;
+ memmove(dstptr, srcptr, ( oldSize < size ? oldSize : size ) );
+ }
+ void* newptr = AddHeaderAndFooter(realNewPtr, size, align);
+ RegisterAllocation(newptr);
+
+ if(m_UseLocking)
+ m_DHAMutex.Unlock();
+ return newptr;
+}
+
+template<class LLAllocator>
+void DynamicHeapAllocator<LLAllocator>::Deallocate (void* p)
+{
+ if (p == NULL)
+ return;
+
+ if(m_UseLocking)
+ m_DHAMutex.Lock();
+
+ AllocationHeader::ValidateIntegrity(p, m_AllocatorIdentifier);
+ RegisterDeallocation(p);
+
+ void* realpointer = AllocationHeader::GetRealPointer(p);
+
+ PoolElement* allocedPool = FindPoolFromPtr(realpointer);
+ if(allocedPool != NULL)
+ {
+ allocedPool->allocationCount--;
+ allocedPool->allocationSize-=GetPtrSize(p);
+ tlsf_free(allocedPool->tlsfPool, realpointer);
+ if(allocedPool->allocationCount == 0)
+ {
+ {
+ Mutex::AutoLock lock(m_DHAMutex);
+ allocedPool->RemoveFromList();
+ }
+ tlsf_destroy(allocedPool->tlsfPool);
+ LLAllocator::Free(allocedPool->memoryBase);
+ m_TotalReservedMemory -= allocedPool->memorySize;
+ allocedPool->~PoolElement();
+ LLAllocator::Free(allocedPool);
+ }
+ }
+ else
+ {
+ // is this a largeAllocation
+ LargeAllocations* alloc = m_FirstLargeAllocation;
+ LargeAllocations* prev = NULL;
+ while (alloc != NULL)
+ {
+ if (alloc->allocation == realpointer)
+ {
+ LLAllocator::Free(realpointer);
+ m_TotalReservedMemory -= alloc->size;
+ alloc->allocation = NULL;
+ alloc->size = 0;
+ {
+ Mutex::AutoLock lock(m_DHAMutex);
+ if(prev == NULL)
+ m_FirstLargeAllocation = alloc->next;
+ else
+ prev->next = alloc->next;
+ }
+ LLAllocator::Free(alloc);
+ if(m_UseLocking)
+ m_DHAMutex.Unlock();
+ return;
+ }
+ prev = alloc;
+ alloc = alloc->next;
+ }
+ ErrorString("Could not find reallocpointer in LargeAllocationlist");
+ }
+ if(m_UseLocking)
+ m_DHAMutex.Unlock();
+}
+
+template<class LLAllocator>
+bool DynamicHeapAllocator<LLAllocator>::ValidatePointer(void* ptr)
+{
+ AllocationHeader::ValidateIntegrity(ptr, -1, -1);
+ return true;
+}
+
+void AllocBlockValidate (void* memptr, size_t /*size*/, int isused, void* /*userptr*/)
+{
+ if (isused )
+ AllocationHeader::ValidateIntegrity(((char*)AllocationHeader::GetHeaderFromRealPointer(memptr))+AllocationHeader::GetHeaderSize(),-1,-1);
+}
+
+template<class LLAllocator>
+bool DynamicHeapAllocator<LLAllocator>::CheckIntegrity()
+{
+ Mutex::AutoLock m(m_DHAMutex);
+ for(ListIterator<PoolElement> i=m_SmallTLSFPools.begin();i != m_SmallTLSFPools.end();i++)
+ tlsf_check_heap(i->tlsfPool);
+ for(ListIterator<PoolElement> i=m_LargeTLSFPools.begin();i != m_LargeTLSFPools.end();i++)
+ tlsf_check_heap(i->tlsfPool);
+
+ for(ListIterator<PoolElement> i=m_SmallTLSFPools.begin();i != m_SmallTLSFPools.end();i++)
+ tlsf_walk_heap (i->tlsfPool, &AllocBlockValidate, NULL);
+ for(ListIterator<PoolElement> i=m_LargeTLSFPools.begin();i != m_LargeTLSFPools.end();i++)
+ tlsf_walk_heap (i->tlsfPool, &AllocBlockValidate, NULL);
+
+ return true;
+}
+
+struct BlockCounter
+{
+ int* blockCount;
+ int size;
+};
+
+void FreeBlockCount (void* /*memptr*/, size_t size, int isused, void* userptr)
+{
+ BlockCounter* counter = (BlockCounter*)userptr;
+ if (!isused )
+ {
+ int index = HighestBit(size);
+ index = index >= counter->size ? counter->size-1 : index;
+ counter->blockCount[index]++;
+ }
+}
+
+template<class LLAllocator>
+void DynamicHeapAllocator<LLAllocator>::GetFreeBlockCount( int* freeCount, int size )
+{
+ Mutex::AutoLock m(m_DHAMutex);
+ memset(freeCount, 0, size*sizeof(int));
+ BlockCounter counter = {freeCount, size };
+ for(ListIterator<PoolElement> i=m_SmallTLSFPools.begin();i != m_SmallTLSFPools.end();i++)
+ tlsf_walk_heap (i->tlsfPool, &FreeBlockCount, &counter);
+ for(ListIterator<PoolElement> i=m_LargeTLSFPools.begin();i != m_LargeTLSFPools.end();i++)
+ tlsf_walk_heap (i->tlsfPool, &FreeBlockCount, &counter);
+}
+
+void UsedBlockCount (void* /*memptr*/, size_t size, int isused, void* userptr)
+{
+ BlockCounter* counter = (BlockCounter*)userptr;
+ if (isused )
+ {
+ int index = HighestBit(size);
+ index = index >= counter->size ? counter->size-1 : index;
+ counter->blockCount[index]++;
+ }
+}
+
+template<class LLAllocator>
+void DynamicHeapAllocator<LLAllocator>::GetUsedBlockCount( int* usedCount, int size )
+{
+ Mutex::AutoLock m(m_DHAMutex);
+ BlockCounter counter = {usedCount, size };
+ for(ListIterator<PoolElement> i=m_SmallTLSFPools.begin();i != m_SmallTLSFPools.end();i++)
+ tlsf_walk_heap (i->tlsfPool, &UsedBlockCount, &counter);
+ for(ListIterator<PoolElement> i=m_LargeTLSFPools.begin();i != m_LargeTLSFPools.end();i++)
+ tlsf_walk_heap (i->tlsfPool, &UsedBlockCount, &counter);
+}
+
+template<class LLAllocator>
+typename DynamicHeapAllocator<LLAllocator>::PoolElement* DynamicHeapAllocator<LLAllocator>::FindPoolFromPtr( const void* ptr )
+{
+ for(ListIterator<PoolElement> i=m_SmallTLSFPools.begin();i != m_SmallTLSFPools.end();i++)
+ {
+ if (i->Contains(ptr))
+ return &*i;
+ }
+ for(ListIterator<PoolElement> i=m_LargeTLSFPools.begin();i != m_LargeTLSFPools.end();i++)
+ {
+ if (i->Contains(ptr))
+ return &*i;
+ }
+ return NULL;
+}
+
+
+template<class LLAlloctor>
+bool DynamicHeapAllocator<LLAlloctor>::Contains (const void* p)
+{
+ bool useLocking = m_UseLocking || !Thread::CurrentThreadIsMainThread();
+ if(useLocking)
+ m_DHAMutex.Lock();
+
+ if(FindPoolFromPtr(p) != NULL)
+ {
+ if(useLocking)
+ m_DHAMutex.Unlock();
+ return true;
+ }
+
+ // is this a largeAllocation
+ LargeAllocations* alloc = m_FirstLargeAllocation;
+ while (alloc != NULL)
+ {
+ if (alloc->returnedPtr == p)
+ {
+ if(useLocking)
+ m_DHAMutex.Unlock();
+ return true;
+ }
+ alloc = alloc->next;
+ }
+ if(useLocking)
+ m_DHAMutex.Unlock();
+ return false;
+
+}
+
+template<class LLAlloctor>
+void* DynamicHeapAllocator<LLAlloctor>::AddHeaderAndFooter( void* ptr, size_t size, int align ) const
+{
+ DebugAssert(align >= kDefaultMemoryAlignment && align <= 16*1024 && IsPowerOfTwo(align));
+ // calculate required padding for ptr to be aligned after header addition
+ // ppppppppHHHH***********
+ int padCount = AllocationHeader::GetRequiredPadding(ptr, align);
+ void* realPtr = ((char*)ptr) + (padCount + AllocationHeader::GetHeaderSize());
+ AllocationHeader::Set(realPtr, m_AllocatorIdentifier, size, padCount, align);
+ return realPtr;
+}
+
+template<class LLAlloctor>
+void DynamicHeapAllocator<LLAlloctor>::RegisterAllocation( const void* p )
+{
+ RegisterAllocationData(GetPtrSize(p), AllocationHeader::GetHeader(p)->GetOverheadSize());
+}
+
+template<class LLAlloctor>
+void DynamicHeapAllocator<LLAlloctor>::RegisterDeallocation( const void* p )
+{
+ RegisterDeallocationData(GetPtrSize(p), AllocationHeader::GetHeader(p)->GetOverheadSize());
+}
+
+template<class LLAlloctor>
+size_t DynamicHeapAllocator<LLAlloctor>::GetPtrSize( const void* ptr ) const
+{
+ return AllocationHeader::GetHeader(ptr)->GetRequestedSize();
+}
+
+template<class LLAlloctor>
+ProfilerAllocationHeader* DynamicHeapAllocator<LLAlloctor>::GetProfilerHeader(const void* ptr) const
+{
+ // LocalHeader:ProfilerHeader:Data
+ return AllocationHeader::GetProfilerHeader(ptr);
+}
+
+template class DynamicHeapAllocator<LowLevelAllocator>;
+
+#if UNITY_XENON && XBOX_USE_DEBUG_MEMORY
+template class DynamicHeapAllocator<LowLevelAllocatorDebugMem>;
+#endif
+#endif
+
diff --git a/Runtime/Allocator/DynamicHeapAllocator.h b/Runtime/Allocator/DynamicHeapAllocator.h
new file mode 100644
index 0000000..29aed9d
--- /dev/null
+++ b/Runtime/Allocator/DynamicHeapAllocator.h
@@ -0,0 +1,81 @@
+#ifndef DYNAMIC_HEAP_ALLOCATOR_H_
+#define DYNAMIC_HEAP_ALLOCATOR_H_
+
+#if ENABLE_MEMORY_MANAGER
+
+#include "BaseAllocator.h"
+#include "Runtime/Threads/Mutex.h"
+#include "Runtime/Allocator/LowLevelDefaultAllocator.h"
+#include "Runtime/Utilities/LinkedList.h"
+
+template<class LLAllocator>
+class DynamicHeapAllocator : public BaseAllocator
+{
+public:
+
+ DynamicHeapAllocator( UInt32 poolIncrementSize, size_t splitLimit, bool useLocking, const char* name);
+ ~DynamicHeapAllocator();
+
+ virtual void* Allocate (size_t size, int align);
+ virtual void* Reallocate (void* p, size_t size, int align);
+ virtual void Deallocate (void* p);
+ virtual bool Contains (const void* p);
+
+ virtual bool CheckIntegrity();
+ virtual bool ValidatePointer(void* ptr);
+
+ virtual size_t GetPtrSize(const void* ptr) const;
+
+ virtual ProfilerAllocationHeader* GetProfilerHeader(const void* ptr) const;
+
+ // return the free block count for each pow2
+ virtual void GetFreeBlockCount(int* freeCount, int size);
+ // return the used block count for each pow2
+ virtual void GetUsedBlockCount(int* usedCount, int size);
+
+private:
+ struct PoolElement : public ListElement
+ {
+ bool Contains(const void* ptr) const
+ {
+ return ptr >= memoryBase && ptr < memoryBase + memorySize;
+ }
+ void* tlsfPool;
+ char* memoryBase;
+ UInt32 memorySize;
+ UInt32 allocationCount;
+ UInt32 allocationSize;
+ };
+
+ typedef List<PoolElement> PoolList;
+
+ size_t m_SplitLimit;
+ PoolElement& GetActivePool(size_t size);
+ PoolList& GetPoolList(size_t size);
+
+ PoolList m_SmallTLSFPools;
+ PoolList m_LargeTLSFPools;
+
+ Mutex m_DHAMutex;
+ bool m_UseLocking;
+ size_t m_RequestedPoolSize;
+
+ struct LargeAllocations
+ {
+ LargeAllocations* next;
+ char* allocation;
+ void* returnedPtr;
+ size_t size;
+ };
+ LargeAllocations* m_FirstLargeAllocation;
+
+ PoolElement* FindPoolFromPtr(const void* ptr);
+
+ void RegisterAllocation(const void* p);
+ void RegisterDeallocation(const void* p);
+
+ void* AddHeaderAndFooter( void* ptr, size_t size, int align ) const;
+};
+
+#endif
+#endif
diff --git a/Runtime/Allocator/FixedHeapAllocator.cpp b/Runtime/Allocator/FixedHeapAllocator.cpp
new file mode 100644
index 0000000..1cc3c71
--- /dev/null
+++ b/Runtime/Allocator/FixedHeapAllocator.cpp
@@ -0,0 +1,68 @@
+#include "UnityPrefix.h"
+#include "FixedHeapAllocator.h"
+#include "tlsf/tlsf.h"
+
+#if FIXED_HEAP_ALLOC_COUNT_USED
+#define ALLOC_USED_MEM_INC(ptr) if (ptr) { m_nSizeUsed += tlsf_block_size(ptr); }
+#define ALLOC_USED_MEM_DEC(ptr) if (ptr) { m_nSizeUsed -= tlsf_block_size(ptr); }
+#else
+#define ALLOC_USED_MEM_INC(ptr)
+#define ALLOC_USED_MEM_DEC(ptr)
+#endif
+
+FixedHeapAllocator::FixedHeapAllocator(void* pMemoryBase, UInt32 nMemorySize, const char* name)
+: BaseAllocator(name)
+, m_TlsfPool(0)
+, m_pMemoryBase(pMemoryBase)
+, m_nMemorySize(nMemorySize)
+#if FIXED_HEAP_ALLOC_COUNT_USED
+, m_nSizeUsed(0)
+#endif
+{
+ m_TlsfPool = tlsf_create(pMemoryBase, nMemorySize);
+}
+
+
+FixedHeapAllocator::~FixedHeapAllocator()
+{
+ tlsf_destroy(m_TlsfPool);
+}
+
+void* FixedHeapAllocator::Allocate (size_t size, int align)
+{
+ void* addr = tlsf_memalign(m_TlsfPool, align, size);
+ ALLOC_USED_MEM_INC(addr);
+ return addr;
+}
+
+void* FixedHeapAllocator::Reallocate(void* p, size_t size, size_t align)
+{
+ ALLOC_USED_MEM_DEC(p);
+ void* addr = tlsf_realloc(m_TlsfPool, p, size);
+ ALLOC_USED_MEM_INC(addr);
+ return addr;
+}
+
+void FixedHeapAllocator::Deallocate(void* p)
+{
+ ALLOC_USED_MEM_DEC(p);
+ return tlsf_free(m_TlsfPool, p);
+}
+
+UInt32 FixedHeapAllocator::GetPtrSize(void* p)
+{
+ return (UInt32)tlsf_block_size(p);
+}
+
+bool FixedHeapAllocator::Contains(const void* p)
+{
+ return (p >= m_pMemoryBase && p < (char*)m_pMemoryBase + m_nMemorySize);
+}
+
+bool FixedHeapAllocator::CheckIntegrity()
+{
+ return tlsf_check_heap(m_TlsfPool);
+}
+
+#undef ALLOC_USED_MEM_INC
+#undef ALLOC_USED_MEM_DEC
diff --git a/Runtime/Allocator/FixedHeapAllocator.h b/Runtime/Allocator/FixedHeapAllocator.h
new file mode 100644
index 0000000..1dc48b5
--- /dev/null
+++ b/Runtime/Allocator/FixedHeapAllocator.h
@@ -0,0 +1,31 @@
+#ifndef HEAP_ALLOCATOR_H_
+#define HEAP_ALLOCATOR_H_
+
+#include "BaseAllocator.h"
+
+#define FIXED_HEAP_ALLOC_COUNT_USED !MASTER_BUILD
+
+class FixedHeapAllocator : public BaseAllocator
+{
+ void* m_TlsfPool;
+ void* m_pMemoryBase;
+ UInt32 m_nMemorySize;
+#if FIXED_HEAP_ALLOC_COUNT_USED
+ UInt32 m_nSizeUsed;
+#endif
+
+public:
+ FixedHeapAllocator(void* pMemoryBase, UInt32 nMemorySize, const char* name);
+ ~FixedHeapAllocator();
+
+ virtual void* Allocate (size_t size, int align);
+ virtual void* Reallocate (void* p, size_t size, size_t align);
+ virtual void Deallocate (void* p);
+
+ virtual bool Contains (const void* p);
+ virtual bool CheckIntegrity();
+
+ virtual UInt32 GetPtrSize(void* p);
+};
+
+#endif
diff --git a/Runtime/Allocator/FixedSizeAllocator.h b/Runtime/Allocator/FixedSizeAllocator.h
new file mode 100644
index 0000000..efc61b3
--- /dev/null
+++ b/Runtime/Allocator/FixedSizeAllocator.h
@@ -0,0 +1,341 @@
+
+#ifndef _ALLOCATOR_FIXEDSIZE_ALLOCATOR
+#define _ALLOCATOR_FIXEDSIZE_ALLOCATOR
+
+#include "Runtime/Allocator/MemoryMacros.h"
+#include "Runtime/Misc/AllocatorLabels.h"
+
+
+template <unsigned BlockSize>
+class
+FixedSizeAllocator
+{
+public:
+ FixedSizeAllocator(MemLabelId memLabel);
+ ~FixedSizeAllocator();
+
+ void* alloc();
+ void free( void* mem );
+
+
+ void reset();
+ void free_memory();
+
+ unsigned total_allocated() const;
+ unsigned total_free() const;
+ unsigned capacity() const;
+
+
+private:
+
+
+ // we can do this template parameter
+ static const UInt8 BlocksInChunk = 255;
+
+ struct
+ Chunk
+ {
+ UInt8 data[BlocksInChunk*BlockSize];
+
+ Chunk* next;
+
+ UInt8 first_available;
+ UInt8 total_available;
+ };
+
+ Chunk* m_Chunk;
+
+ // we can store pointers -- they will be updated anyway should the new chunk be added
+ Chunk* m_AllocChunk;
+ Chunk* m_DeallocChunk;
+
+ MemLabelId m_MemLabel;
+
+
+ void create_chunk();
+ void reset_chunk( Chunk* chunk );
+
+ void* alloc_from( Chunk* chunk );
+ void dealloc_from( void* mem, Chunk* chunk );
+
+ bool mem_from( void* mem, Chunk* chunk );
+};
+
+
+//------------------------------------------------------------------------------
+
+template <unsigned BlockSize>
+inline void
+FixedSizeAllocator<BlockSize>::reset_chunk( Chunk* chunk )
+{
+ AssertBreak(chunk);
+
+ chunk->first_available = 0;
+ chunk->total_available = BlocksInChunk;
+
+ // store index of next available block in-place, as first byte of the block
+
+ UInt8 next_available_i = 1;
+ UInt8* next_available_mem = (UInt8*)chunk->data;
+
+ while( next_available_i != BlocksInChunk )
+ {
+ *next_available_mem = next_available_i;
+
+ ++next_available_i;
+ next_available_mem += BlockSize;
+ }
+}
+
+
+//------------------------------------------------------------------------------
+
+template <unsigned BlockSize>
+inline void
+FixedSizeAllocator<BlockSize>::create_chunk()
+{
+ Chunk* newChunk = (Chunk*)UNITY_MALLOC(m_MemLabel, sizeof(Chunk));
+
+ reset_chunk(newChunk);
+ newChunk->next = 0;
+
+ if( m_Chunk )
+ {
+ Chunk* tail_chunk = m_Chunk;
+
+ while( tail_chunk->next )
+ tail_chunk = tail_chunk->next;
+
+ tail_chunk->next = newChunk;
+ }
+ else
+ {
+ m_Chunk = newChunk;
+ }
+
+ m_AllocChunk = m_DeallocChunk = newChunk;
+}
+
+
+//------------------------------------------------------------------------------
+
+template <unsigned BlockSize>
+inline
+FixedSizeAllocator<BlockSize>::FixedSizeAllocator(MemLabelId memLabel)
+ : m_Chunk(0),
+ m_AllocChunk(0),
+ m_DeallocChunk(0),
+ m_MemLabel(memLabel)
+{
+ // TODO: create chunk right away?
+ //create_chunk()
+}
+
+
+//------------------------------------------------------------------------------
+
+template <unsigned BlockSize>
+inline
+FixedSizeAllocator<BlockSize>::~FixedSizeAllocator()
+{
+ free_memory();
+}
+
+
+//------------------------------------------------------------------------------
+
+template <unsigned BlockSize>
+inline bool
+FixedSizeAllocator<BlockSize>::mem_from( void* mem, Chunk* chunk )
+{
+ return ( (UInt8*)mem >= (UInt8*)chunk->data
+ // TODO: align into account
+ && (UInt8*)mem < (UInt8*)chunk->data + BlocksInChunk*BlockSize );
+}
+
+
+//------------------------------------------------------------------------------
+
+template <unsigned BlockSize>
+inline void*
+FixedSizeAllocator<BlockSize>::alloc_from( Chunk* chunk )
+{
+ AssertBreak( chunk );
+ AssertBreak( chunk->total_available );
+
+ UInt8* ret = (UInt8*)chunk->data + chunk->first_available*BlockSize;
+
+ chunk->first_available = *ret;
+ --chunk->total_available;
+
+ return ret;
+}
+
+
+//------------------------------------------------------------------------------
+
+template <unsigned BlockSize>
+inline void
+FixedSizeAllocator<BlockSize>::dealloc_from( void* mem, Chunk* chunk )
+{
+ AssertBreak( chunk );
+ AssertBreak( mem_from(mem, chunk) );
+
+ UInt8* release_ptr = (UInt8*)mem;
+ AssertBreak( (release_ptr - (UInt8*)chunk->data) % BlockSize == 0 );
+
+ *release_ptr = chunk->first_available;
+ chunk->first_available = (release_ptr - (UInt8*)chunk->data) / BlockSize;
+
+ ++chunk->total_available;
+}
+
+
+//------------------------------------------------------------------------------
+
+template <unsigned BlockSize>
+inline void*
+FixedSizeAllocator<BlockSize>::alloc()
+{
+ if( m_AllocChunk == 0 || m_AllocChunk->total_available == 0 )
+ {
+ // fallback to linear search
+
+ m_AllocChunk = m_Chunk;
+ while( m_AllocChunk )
+ {
+ if( m_AllocChunk->total_available )
+ break;
+
+ m_AllocChunk = m_AllocChunk->next;
+ }
+
+ if( !m_AllocChunk )
+ create_chunk();
+ }
+
+ return alloc_from(m_AllocChunk);
+}
+
+
+//------------------------------------------------------------------------------
+
+template <unsigned BlockSize>
+inline void
+FixedSizeAllocator<BlockSize>::free( void* mem )
+{
+ AssertBreak(m_DeallocChunk);
+
+ if( !mem )
+ return;
+
+ if( !mem_from(mem, m_DeallocChunk) )
+ {
+ // we want to exploit possible locality
+ // but for now we store chunks in single-linked list
+ // fallback to simple linear search;
+
+ m_DeallocChunk = m_Chunk;
+ while( m_DeallocChunk )
+ {
+ if( mem_from(mem, m_DeallocChunk) )
+ break;
+
+ m_DeallocChunk = m_DeallocChunk->next;
+ }
+ }
+
+ AssertBreak(m_DeallocChunk);
+ dealloc_from( mem, m_DeallocChunk );
+}
+
+
+//------------------------------------------------------------------------------
+
+template <unsigned BlockSize>
+inline void
+FixedSizeAllocator<BlockSize>::reset()
+{
+ Chunk* target_chunk = m_Chunk;
+ while( target_chunk )
+ {
+ reset_chunk(target_chunk);
+ target_chunk = target_chunk->next;
+ }
+
+ m_AllocChunk = m_DeallocChunk = m_Chunk;
+}
+
+
+//------------------------------------------------------------------------------
+
+template <unsigned BlockSize>
+inline void
+FixedSizeAllocator<BlockSize>::free_memory()
+{
+ Chunk* target_chunk = m_Chunk;
+ while( target_chunk )
+ {
+ Chunk* nextChunk = target_chunk->next;
+ UNITY_FREE(m_MemLabel, target_chunk);
+
+ target_chunk = nextChunk;
+ }
+
+ m_AllocChunk = m_DeallocChunk = m_Chunk = 0;
+}
+
+
+//------------------------------------------------------------------------------
+
+template <unsigned BlockSize>
+inline unsigned
+FixedSizeAllocator<BlockSize>::capacity() const
+{
+ unsigned ret = 0;
+
+ Chunk* target_chunk = m_Chunk;
+ while( target_chunk )
+ {
+ ret += BlocksInChunk*BlockSize;
+ target_chunk = target_chunk->next;
+ }
+
+ return ret;
+}
+
+
+//------------------------------------------------------------------------------
+
+template <unsigned BlockSize>
+inline unsigned
+FixedSizeAllocator<BlockSize>::total_free() const
+{
+ unsigned ret = 0;
+
+ Chunk* target_chunk = m_Chunk;
+ while( target_chunk )
+ {
+ ret += target_chunk->total_available * BlockSize;
+ target_chunk = target_chunk->next;
+ }
+
+ return ret;
+
+}
+
+
+//------------------------------------------------------------------------------
+
+template <unsigned BlockSize>
+inline unsigned
+FixedSizeAllocator<BlockSize>::total_allocated() const
+{
+ return capacity() - total_free();
+}
+
+
+//==============================================================================
+
+#endif // _ALLOCATOR_FIXEDSIZE_ALLOCATOR
+
diff --git a/Runtime/Allocator/LinearAllocator.cpp b/Runtime/Allocator/LinearAllocator.cpp
new file mode 100644
index 0000000..9d0b23b
--- /dev/null
+++ b/Runtime/Allocator/LinearAllocator.cpp
@@ -0,0 +1,38 @@
+#include "UnityPrefix.h"
+#include "Configuration/UnityConfigure.h"
+#include "LinearAllocator.h"
+
+
+#if 0
+
+struct FwdAllocatorMarker {
+ FwdAllocatorMarker (ForwardLinearAllocator& al) : al_(al), p_(al.current ()) {}
+ ~FwdAllocatorMarker () { al_.rewind (p_); }
+
+ void* p_;
+ ForwardLinearAllocator& al_;
+};
+
+
+void TestLinearAllocator ()
+{
+ ForwardLinearAllocator la (32);
+
+ void* p0 = la.allocate (16);
+ void* p1 = la.allocate (16);
+
+ {
+ FwdAllocatorMarker m (la);
+ void* p2 = la.allocate (32);
+ }
+
+ void* c1 = la.current ();
+ {
+ FwdAllocatorMarker m (la);
+ void* p3 = la.allocate (8);
+ void* p4 = la.allocate (32);
+ void* p5 = la.allocate (32);
+ }
+ la.rewind (c1);
+}
+#endif
diff --git a/Runtime/Allocator/LinearAllocator.h b/Runtime/Allocator/LinearAllocator.h
new file mode 100644
index 0000000..7c742e4
--- /dev/null
+++ b/Runtime/Allocator/LinearAllocator.h
@@ -0,0 +1,391 @@
+#ifndef LINEAR_ALLOCATOR_H_
+#define LINEAR_ALLOCATOR_H_
+
+#include <cstddef>
+#include <list>
+#include "assert.h"
+#include "Configuration/UnityConfigure.h"
+#if UNITY_XENON
+#include <malloc.h>
+#endif
+#if UNITY_LINUX
+#include <stdint.h> // uintptr_t
+#endif
+
+#if ENABLE_THREAD_CHECK_IN_ALLOCS
+#include "Runtime/Threads/Thread.h"
+#endif
+#include "Runtime/Allocator/MemoryMacros.h"
+
+struct LinearAllocatorBase
+{
+ static const int kMinimalAlign = 4;
+
+ struct Block
+ {
+ char* m_Begin;
+ char* m_Current;
+ size_t m_Size;
+ MemLabelId m_Label;
+
+ void initialize (size_t size, MemLabelId label)
+ {
+ m_Label = label;
+ m_Current = m_Begin = (char*)UNITY_MALLOC(label,size);
+ m_Size = size;
+ }
+
+ void reset ()
+ {
+ m_Current = m_Begin;
+ }
+
+ void purge ()
+ {
+ UNITY_FREE(m_Label, m_Begin);
+ }
+
+ size_t used () const
+ {
+ return m_Current - m_Begin;
+ }
+
+ void* current () const
+ {
+ return m_Current;
+ }
+
+ size_t available () const
+ {
+ return m_Size - used ();
+ }
+
+ size_t padding (size_t alignment) const
+ {
+ size_t pad = ((uintptr_t)m_Current - 1 | alignment - 1) + 1 - (uintptr_t)m_Current;
+ return pad;
+ }
+
+ void* bump (size_t size)
+ {
+ Assert (size <= available());
+ char* p = m_Current;
+ m_Current += size;
+ return p;
+ }
+
+ void roll_back (size_t size)
+ {
+ Assert (used () >= size);
+ m_Current -= size;
+ }
+
+ bool belongs (const void* p)
+ {
+ //if (p >= m_Begin && p <= m_Begin + m_Size)
+ // return true;
+ //return false;
+
+ //return p >= m_Begin && p <= m_Begin + m_Size;
+
+ return (uintptr_t)p - (uintptr_t)m_Begin <= (uintptr_t)m_Size;
+ }
+
+ void set (void* p)
+ {
+ Assert (p >= m_Begin && p < m_Begin + m_Size);
+ m_Current = (char*)p;
+ }
+ };
+
+ typedef std::list<Block, STL_ALLOCATOR(kMemPoolAlloc, Block) > block_container;
+
+ LinearAllocatorBase (size_t blockSize, MemLabelId label)
+ : m_Blocks(), m_BlockSize (blockSize), m_AllocLabel (label)
+ {
+ }
+
+ void add_block (size_t size)
+ {
+ m_Blocks.push_back (Block());
+ size_t blockSize = size > m_BlockSize ? size : m_BlockSize;
+ m_Blocks.back ().initialize (blockSize, m_AllocLabel);
+ }
+
+ void purge (bool releaseAllBlocks = false)
+ {
+ if (m_Blocks.empty ())
+ return;
+
+ block_container::iterator begin = m_Blocks.begin ();
+
+ if (!releaseAllBlocks)
+ begin++;
+
+ for (block_container::iterator it = begin, end = m_Blocks.end (); it != end; ++it)
+ it->purge ();
+
+ m_Blocks.erase (begin, m_Blocks.end ());
+
+ if (!releaseAllBlocks)
+ m_Blocks.back ().reset ();
+ }
+
+ bool belongs (const void* p)
+ {
+ for (block_container::iterator it = m_Blocks.begin (), end = m_Blocks.end (); it != end; ++it)
+ {
+ if (it->belongs (p))
+ return true;
+ }
+
+ return false;
+ }
+
+ void* current () const
+ {
+ return m_Blocks.empty () ? 0 : m_Blocks.back ().current ();
+ }
+
+ void rewind (void* mark)
+ {
+ for (block_container::iterator it = m_Blocks.end (); it != m_Blocks.begin (); --it)
+ {
+ block_container::iterator tit = it;
+ --tit;
+
+ if (tit->belongs (mark)) {
+ tit->set (mark);
+
+ for (block_container::iterator temp = it; temp != m_Blocks.end (); ++temp)
+ temp->purge ();
+
+ m_Blocks.erase (it, m_Blocks.end ());
+ break;
+ }
+ }
+ }
+
+protected:
+ block_container m_Blocks;
+ size_t m_BlockSize;
+ MemLabelId m_AllocLabel;
+};
+
+
+struct ForwardLinearAllocator : public LinearAllocatorBase
+{
+#if ENABLE_THREAD_CHECK_IN_ALLOCS
+ Thread::ThreadID m_AllocThread, m_DeallocThread;
+ int m_Allocated;
+ bool m_RequireDeallocation;
+
+ void SetThreadIDs (Thread::ThreadID allocThread, Thread::ThreadID deallocThread)
+ {
+ m_AllocThread = allocThread;
+ m_DeallocThread = deallocThread;
+ }
+
+ void SetRequireDeallocation (bool v)
+ {
+ m_RequireDeallocation = v;
+ }
+#endif
+
+ ForwardLinearAllocator (size_t blockSize, MemLabelId label)
+ : LinearAllocatorBase (blockSize, label)
+#if ENABLE_THREAD_CHECK_IN_ALLOCS
+ , m_AllocThread (0), m_DeallocThread (0), m_Allocated(0), m_RequireDeallocation(false)
+#endif
+ {
+ }
+
+ ~ForwardLinearAllocator ()
+ {
+ purge (true);
+ }
+
+ size_t GetAllocatedBytes() const
+ {
+ size_t s = 0;
+ for (block_container::const_iterator it = m_Blocks.begin (); it != m_Blocks.end(); ++it)
+ s += it->used();
+ return s;
+ }
+
+ void* allocate (size_t size, size_t alignment = 4)
+ {
+#if ENABLE_THREAD_CHECK_IN_ALLOCS
+ ErrorIf (!Thread::EqualsCurrentThreadIDForAssert (m_AllocThread));
+ m_Allocated++;
+#endif
+// Assert (size == AlignUIntPtr (size, kMinimalAlign));
+
+ if (m_Blocks.empty ())
+ add_block (size);
+
+ Block* block = &m_Blocks.back ();
+ size_t padding = block->padding (alignment);
+
+ if (size + padding > block->available ()) {
+ add_block (size);
+ block = &m_Blocks.back ();
+ }
+
+ uintptr_t p = (uintptr_t)block->bump (size + padding);
+
+ return (void*)(p + padding);
+ }
+
+ void deallocate (void* dealloc)
+ {
+#if ENABLE_THREAD_CHECK_IN_ALLOCS
+ ErrorIf (Thread::GetCurrentThreadID () != m_DeallocThread);
+ m_Allocated--;
+#endif
+ }
+
+ void deallocate_no_thread_check (void* dealloc)
+ {
+#if ENABLE_THREAD_CHECK_IN_ALLOCS
+ m_Allocated--;
+#endif
+ }
+
+ void purge (bool releaseAllBlocks = false)
+ {
+#if ENABLE_THREAD_CHECK_IN_ALLOCS
+ ErrorIf (Thread::GetCurrentThreadID () != m_DeallocThread && m_DeallocThread != 0);
+ ErrorIf (m_RequireDeallocation && m_Allocated != 0);
+#endif
+ LinearAllocatorBase::purge (releaseAllBlocks);
+ }
+
+ void rewind (void* mark)
+ {
+#if ENABLE_THREAD_CHECK_IN_ALLOCS
+ ErrorIf (Thread::GetCurrentThreadID () != m_DeallocThread);
+#endif
+ LinearAllocatorBase::rewind (mark);
+ }
+
+ using LinearAllocatorBase::current;
+ using LinearAllocatorBase::belongs;
+};
+/*
+// std::allocator concept implementation for ForwardLinearAllocator objects.
+// use it to make STL use your *locally* created ForwardLinearAllocator object
+// example:
+// void HeavyMemoryAllocationFuncion ()
+// {
+// ForwardLinearAllocator fwdalloc (1024);
+// std::vector<int, forward_linear_allocator<int> > container (forward_linear_allocator<int>(fwdalloc));
+//
+// // use vector
+//
+// // memory is clean up automatically
+// }
+
+template<class T>
+class forward_linear_allocator
+{
+public:
+ typedef size_t size_type;
+ typedef 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 forward_linear_allocator<U> other; };
+
+ forward_linear_allocator(ForwardLinearAllocator& al) throw() : m_LinearAllocator (al) {}
+ forward_linear_allocator(const forward_linear_allocator& al) throw() : m_LinearAllocator (al.m_LinearAllocator) {}
+ template <class U> forward_linear_allocator(const forward_linear_allocator<U>& al) throw() : m_LinearAllocator (al.m_LinearAllocator) {}
+ ~forward_linear_allocator() throw() {}
+
+ pointer address(reference x) const { return &x; }
+ const_pointer address(const_reference x) const { return &x; }
+
+ pointer allocate(size_type count, void const* hint = 0)
+ { return (pointer)m_LinearAllocator.allocate (count * sizeof(T)); }
+ void deallocate(pointer p, size_type n)
+ { m_LinearAllocator.deallocate(p); }
+
+ size_type max_size() const throw()
+ { return 0x80000000; }
+
+ void construct(pointer p, const T& val)
+ { new (p) T( val ); }
+
+ void destroy(pointer p)
+ { p->~T(); }
+
+private:
+ ForwardLinearAllocator& m_LinearAllocator;
+};
+
+// this a global ForwardLinearAllocator object that can be used to allocate (and release) memory from
+// anywhere in the program. Caller is responsible for tracking memory used in total
+// (use ForwardLinearAllocator::current/ForwardLinearAllocator::rewind to save and restore memory pointer)
+extern ForwardLinearAllocator g_ForwardFrameAllocator;
+
+// std::allocator concept for global ForwardLinearAllocator object
+// this is just to save one indirection, otherwise forward_linear_allocator referencing a global ForwardLinearAllocator object could be used
+template<class T>
+class global_linear_allocator
+{
+public:
+ typedef size_t size_type;
+ typedef 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 global_linear_allocator<U> other; };
+
+ global_linear_allocator() {}
+ global_linear_allocator(const global_linear_allocator&) throw() {}
+ template <class U> global_linear_allocator(const global_linear_allocator<U>&) throw() {}
+ ~global_linear_allocator() throw() {}
+
+ pointer address(reference x) const { return &x; }
+ const_pointer address(const_reference x) const { return &x; }
+
+ pointer allocate(size_type count, void const* hint = 0)
+ { return (pointer)g_ForwardFrameAllocator.allocate (count * sizeof(T)); }
+ void deallocate(pointer p, size_type n)
+ { g_ForwardFrameAllocator.deallocate(p); }
+
+ bool operator==(global_linear_allocator const& a) const
+ { return true; }
+ bool operator!=(global_linear_allocator const& a) const
+ { return false; }
+
+ size_type max_size() const throw()
+ { return 0x7fffffff; }
+
+ void construct(pointer p, const T& val)
+ { new (p) T( val ); }
+
+ void destroy(pointer p)
+ { p->~T(); }
+};
+
+#define DECLARE_GLOBAL_LINEAR_ALLOCATOR_MEMBER_NEW_DELETE \
+public: \
+ inline void* operator new( size_t size ) { return g_ForwardFrameAllocator.allocate(size); } \
+ inline void operator delete( void* p ) { g_ForwardFrameAllocator.deallocate(p); }
+
+inline void* operator new (size_t size, ForwardLinearAllocator& al) { return al.allocate (size); }
+inline void* operator new [] (size_t size, ForwardLinearAllocator& al) { return al.allocate (size); }
+
+inline void operator delete (void* p, ForwardLinearAllocator& al) { }
+inline void operator delete [] (void* p, ForwardLinearAllocator& al) { }
+*/
+
+
+#endif
diff --git a/Runtime/Allocator/LowLevelDefaultAllocator.cpp b/Runtime/Allocator/LowLevelDefaultAllocator.cpp
new file mode 100644
index 0000000..010406c
--- /dev/null
+++ b/Runtime/Allocator/LowLevelDefaultAllocator.cpp
@@ -0,0 +1,33 @@
+#include "UnityPrefix.h"
+#include "LowLevelDefaultAllocator.h"
+#include "Runtime/Allocator/MemoryManager.h"
+
+#if ENABLE_MEMORY_MANAGER
+
+
+void* LowLevelAllocator::Malloc (size_t size) { return MemoryManager::LowLevelAllocate(size); }
+void* LowLevelAllocator::Realloc (void* ptr, size_t size) { return MemoryManager::LowLevelReallocate(ptr, size); }
+void LowLevelAllocator::Free (void* ptr) { MemoryManager::LowLevelFree(ptr); }
+
+
+#if UNITY_XENON
+#include "PlatformDependent/Xbox360/Source/XenonMemory.h"
+
+#if XBOX_USE_DEBUG_MEMORY
+// Uses debug memory on compatible devkits
+void* LowLevelAllocatorDebugMem::Malloc(size_t size)
+{
+ return DmDebugAlloc(size);
+}
+void* LowLevelAllocatorDebugMem::Realloc (void* ptr, size_t size)
+{
+ ErrorString("LowLevelAllocatorDebugMem::Realloc is not implemented.");
+ return 0;
+}
+void LowLevelAllocatorDebugMem::Free (void* ptr)
+{
+ DmDebugFree(ptr);
+}
+#endif // XBOX_USE_DEBUG_MEMORY
+#endif
+#endif
diff --git a/Runtime/Allocator/LowLevelDefaultAllocator.h b/Runtime/Allocator/LowLevelDefaultAllocator.h
new file mode 100644
index 0000000..ce9b852
--- /dev/null
+++ b/Runtime/Allocator/LowLevelDefaultAllocator.h
@@ -0,0 +1,25 @@
+#ifndef LOW_LEVEL_DEFAULT_ALLOCATOR_H_
+#define LOW_LEVEL_DEFAULT_ALLOCATOR_H_
+
+#if ENABLE_MEMORY_MANAGER
+
+class LowLevelAllocator
+{
+public:
+ static void* Malloc(size_t size);
+ static void* Realloc(void* ptr, size_t size);
+ static void Free(void* ptr);
+};
+
+#if UNITY_XENON
+class LowLevelAllocatorDebugMem
+{
+public:
+ static void* Malloc(size_t size);
+ static void* Realloc(void* ptr, size_t size);
+ static void Free(void* ptr);
+};
+#endif
+
+#endif
+#endif // LOW_LEVEL_DEFAULT_ALLOCATOR_H_
diff --git a/Runtime/Allocator/MemoryMacros.cpp b/Runtime/Allocator/MemoryMacros.cpp
new file mode 100644
index 0000000..75daeb8
--- /dev/null
+++ b/Runtime/Allocator/MemoryMacros.cpp
@@ -0,0 +1,85 @@
+#include "UnityPrefix.h"
+#include "Runtime/Profiler/MemoryProfiler.h"
+#include "Runtime/Allocator/MemoryManager.h"
+#include "Runtime/Allocator/MemoryMacros.h"
+
+#if ENABLE_MEM_PROFILER
+
+bool push_allocation_root(void* root, bool forcePush)
+{
+ return GetMemoryProfiler()?GetMemoryProfiler()->PushAllocationRoot(root, forcePush):false;
+}
+
+void pop_allocation_root()
+{
+ GetMemoryProfiler()->PopAllocationRoot();
+}
+
+ProfilerAllocationHeader* get_current_allocation_root_header_internal()
+{
+ return GetMemoryProfiler()?GetMemoryProfiler()->GetCurrentRootHeader():NULL;
+}
+
+ProfilerAllocationHeader* get_allocation_header_internal(void* ptr, MemLabelRef label)
+{
+ BaseAllocator* alloc = GetMemoryManager().GetAllocator(label);
+ return alloc->GetProfilerHeader(ptr);
+}
+
+void transfer_ownership_root_header(void* source, MemLabelRef label, ProfilerAllocationHeader* newRootHeader)
+{
+ if(GetMemoryManager().IsTempAllocatorLabel(label))
+ return;
+ GetMemoryProfiler()->TransferOwnership(source, GetMemoryManager().GetAllocator(label), newRootHeader);
+}
+
+void transfer_ownership(void* source, MemLabelRef label, const void* newroot)
+{
+ BaseAllocator* rootAlloc = GetMemoryManager().GetAllocatorContainingPtr(newroot);
+ ProfilerAllocationHeader* rootHeader = rootAlloc->GetProfilerHeader(newroot);
+ transfer_ownership_root_header(source, label, rootHeader);
+}
+
+void set_root_allocation(void* root, MemLabelRef label, const char* areaName, const char* objectName)
+{
+ pop_allocation_root();
+ GetMemoryProfiler ()->RegisterRootAllocation (root, GetMemoryManager().GetAllocator(label), areaName, objectName);
+}
+
+void assign_allocation_root(void* root, MemLabelRef label, const char* areaName, const char* objectName)
+{
+ GetMemoryProfiler ()->RegisterRootAllocation (root, GetMemoryManager().GetAllocator(label), areaName, objectName);
+}
+
+AllocationRootReference* get_root_reference_from_header(ProfilerAllocationHeader* root)
+{
+ return MemoryProfiler::GetRootReferenceFromHeader(root);
+}
+
+AllocationRootReference* copy_root_reference(AllocationRootReference* rootRef)
+{
+ if (rootRef)
+ rootRef->Retain();
+ return rootRef;
+}
+
+void release_root_reference(AllocationRootReference* rootRef)
+{
+ if (rootRef)
+ rootRef->Release();
+}
+
+ProfilerAllocationHeader* get_root_header_from_reference(AllocationRootReference* rootref)
+{
+ return rootref? rootref->root: NULL;
+}
+
+#endif
+
+void ValidateAllocatorIntegrity(MemLabelId label)
+{
+#if ENABLE_MEMORY_MANAGER
+ GetMemoryManager().GetAllocator(label)->CheckIntegrity();
+#endif
+}
+
diff --git a/Runtime/Allocator/MemoryMacros.h b/Runtime/Allocator/MemoryMacros.h
new file mode 100644
index 0000000..f7a85c6
--- /dev/null
+++ b/Runtime/Allocator/MemoryMacros.h
@@ -0,0 +1,237 @@
+#ifndef _MEMORY_MACROS_H_
+#define _MEMORY_MACROS_H_
+
+#include <new>
+#include "Runtime/Utilities/FileStripped.h"
+#include "Runtime/Misc/AllocatorLabels.h"
+
+
+#if defined(__GNUC__)
+ #define ALIGN_OF(T) __alignof__(T)
+ #define ALIGN_TYPE(val) __attribute__((aligned(val)))
+ #define FORCE_INLINE inline __attribute__ ((always_inline))
+#elif defined(_MSC_VER)
+ #define ALIGN_OF(T) __alignof(T)
+ #define ALIGN_TYPE(val) __declspec(align(val))
+ #define FORCE_INLINE __forceinline
+#else
+ #define ALIGN_TYPE(size)
+ #define FORCE_INLINE inline
+#endif
+
+
+#if ENABLE_MEMORY_MANAGER
+// These methods are added to be able to make some initial allocations that does not use the memory manager
+extern void* GetPreallocatedMemory(int size);
+# define HEAP_NEW(cls) new (GetPreallocatedMemory(sizeof(cls))) cls
+# define HEAP_DELETE(obj, cls) obj->~cls();
+#else
+# define HEAP_NEW(cls) new (UNITY_LL_ALLOC(kMemDefault,sizeof(cls),kDefaultMemoryAlignment)) cls
+# define HEAP_DELETE(obj, cls) {obj->~cls();UNITY_LL_FREE(kMemDefault,(void*)obj);}
+#endif
+
+enum
+{
+#if UNITY_OSX || UNITY_IPHONE || UNITY_ANDROID || UNITY_PS3 || UNITY_XENON || UNITY_BB10 || UNITY_TIZEN
+ kDefaultMemoryAlignment = 16
+#else
+ kDefaultMemoryAlignment = sizeof(void*)
+#endif
+};
+
+enum
+{
+ kAllocateOptionNone = 0, // Fatal: Show message box with out of memory error and quit application
+ kAllocateOptionReturnNullIfOutOfMemory = 1 // Returns null if allocation fails (doesn't show message box)
+};
+
+
+#if ENABLE_MEMORY_MANAGER
+
+// new override does not work on mac together with pace
+#if !(UNITY_OSX && UNITY_EDITOR) && !(UNITY_PLUGIN) && !UNITY_WEBGL && !UNITY_BB10 && !UNITY_TIZEN
+void* operator new (size_t size) throw();
+#if UNITY_PS3
+void* operator new (size_t size, size_t alignment) throw();
+void* operator new [] (size_t size, size_t alignment) throw();
+#endif
+void* operator new [] (size_t size) throw();
+void operator delete (void* p) throw();
+void operator delete [] (void* p) throw();
+
+void* operator new (size_t size, const std::nothrow_t&) throw();
+void* operator new [] (size_t size, const std::nothrow_t&) throw();
+void operator delete (void* p, const std::nothrow_t&) throw();
+void operator delete [] (void* p, const std::nothrow_t&) throw();
+#endif
+
+#endif
+
+#if ENABLE_MEM_PROFILER
+
+EXPORT_COREMODULE bool push_allocation_root(void* root, bool forcePush);
+EXPORT_COREMODULE void pop_allocation_root();
+EXPORT_COREMODULE ProfilerAllocationHeader* get_current_allocation_root_header_internal();
+EXPORT_COREMODULE void set_root_allocation(void* root, MemLabelRef label, const char* areaName, const char* objectName);
+EXPORT_COREMODULE void assign_allocation_root(void* root, MemLabelRef label, const char* areaName, const char* objectName);
+EXPORT_COREMODULE ProfilerAllocationHeader* get_allocation_header_internal(void* ptr, MemLabelRef label);
+EXPORT_COREMODULE AllocationRootReference* get_root_reference_from_header(ProfilerAllocationHeader* root);
+EXPORT_COREMODULE AllocationRootReference* copy_root_reference(AllocationRootReference* rootref);
+EXPORT_COREMODULE void release_root_reference(AllocationRootReference* rootRef);
+EXPORT_COREMODULE ProfilerAllocationHeader* get_root_header_from_reference(AllocationRootReference* rootref);
+
+class AutoScopeRoot
+{
+public:
+ AutoScopeRoot(void* root) { pushed = push_allocation_root(root, false); }
+ ~AutoScopeRoot() { if(pushed) pop_allocation_root(); }
+ bool pushed;
+};
+
+#define GET_CURRENT_ALLOC_ROOT_HEADER() get_current_allocation_root_header_internal()
+#define GET_ALLOC_HEADER(ptr, label) get_allocation_header_internal(ptr, label)
+#define SET_ALLOC_OWNER(root) AutoScopeRoot autoScopeRoot(root)
+#define UNITY_TRANSFER_OWNERSHIP(source, label, newroot) transfer_ownership(source, label, newroot)
+#define UNITY_TRANSFER_OWNERSHIP_TO_HEADER(source, label, newrootheader) transfer_ownership_root_header(source, label, newrootheader)
+
+template<typename T>
+inline T* set_allocation_root(T* root, MemLabelRef label, const char* areaName, const char* objectName)
+{
+ set_root_allocation(root, label, areaName, objectName);
+ return root;
+}
+
+#else
+
+#define GET_CURRENT_ALLOC_ROOT_HEADER() NULL
+#define GET_ALLOC_HEADER(ptr, label) NULL
+#define SET_ALLOC_OWNER(root) {}
+#define UNITY_TRANSFER_OWNERSHIP(source, label, newroot) {}
+#define UNITY_TRANSFER_OWNERSHIP_TO_HEADER(source, label, newrootheader) {}
+
+#endif
+
+EXPORT_COREMODULE void* operator new (size_t size, MemLabelRef label, bool set_root, int align, const char* file, int line);
+void* operator new [] (size_t size, MemLabelRef label, bool set_root, int align, const char* file, int line);
+EXPORT_COREMODULE void operator delete (void* p, MemLabelRef label, bool set_root, int align, const char* file, int line);
+void operator delete [] (void* p, MemLabelRef label, bool set_root, int align, const char* file, int line);
+
+EXPORT_COREMODULE void* malloc_internal(size_t size, int align, MemLabelRef label, int allocateOptions, const char* file, int line);
+EXPORT_COREMODULE void* calloc_internal(size_t count, size_t size, int align, MemLabelRef label, int allocateOptions, const char* file, int line);
+EXPORT_COREMODULE void* realloc_internal(void* ptr, size_t size, int align, MemLabelRef label, int allocateOptions, const char* file, int line);
+void free_internal(void* ptr);
+void EXPORT_COREMODULE free_alloc_internal(void* ptr, MemLabelRef label);
+
+void transfer_ownership(void* source, MemLabelRef label, const void* newroot);
+void transfer_ownership_root_header(void* source, MemLabelRef label, ProfilerAllocationHeader* newRootHeader);
+
+void register_external_gfx_allocation(void* ptr, size_t size, size_t related, const char* file, int line);
+void register_external_gfx_deallocation(void* ptr, const char* file, int line);
+
+#define UNITY_MALLOC(label, size) malloc_internal(size, kDefaultMemoryAlignment, label, kAllocateOptionNone, __FILE_STRIPPED__, __LINE__)
+#define UNITY_MALLOC_NULL(label, size) malloc_internal(size, kDefaultMemoryAlignment, label, kAllocateOptionReturnNullIfOutOfMemory, __FILE_STRIPPED__, __LINE__)
+#define UNITY_MALLOC_ALIGNED(label, size, align) malloc_internal(size, align, label, kAllocateOptionNone, __FILE_STRIPPED__, __LINE__)
+#define UNITY_MALLOC_ALIGNED_NULL(label, size, align) malloc_internal(size, align, label, kAllocateOptionReturnNullIfOutOfMemory, __FILE_STRIPPED__, __LINE__)
+#define UNITY_CALLOC(label, count, size) calloc_internal(count, size, kDefaultMemoryAlignment, label, kAllocateOptionNone, __FILE_STRIPPED__, __LINE__)
+#define UNITY_REALLOC_(label, ptr, size) realloc_internal(ptr, size, kDefaultMemoryAlignment, label, kAllocateOptionNone, __FILE_STRIPPED__, __LINE__)
+#define UNITY_REALLOC_ALIGNED(label, ptr, size, align) realloc_internal(ptr, size, align, label, kAllocateOptionNone, __FILE_STRIPPED__, __LINE__)
+#define UNITY_FREE(label, ptr) free_alloc_internal(ptr, label)
+
+#define REGISTER_EXTERNAL_GFX_ALLOCATION_REF(ptr, size, related) register_external_gfx_allocation((void*)ptr, size, (size_t)related, __FILE_STRIPPED__, __LINE__)
+#define REGISTER_EXTERNAL_GFX_DEALLOCATION(ptr) register_external_gfx_deallocation((void*)ptr, __FILE_STRIPPED__, __LINE__)
+
+template<typename T>
+inline void delete_internal(T* ptr, MemLabelRef label) { if (ptr) ptr->~T(); UNITY_FREE(label,ptr); }
+
+#define UNITY_NEW(type, label) new (label, false, kDefaultMemoryAlignment, __FILE_STRIPPED__, __LINE__) type
+#define UNITY_NEW_ALIGNED(type, label, align) new (label, false, align, __FILE_STRIPPED__, __LINE__) type
+#define UNITY_DELETE(ptr, label) { delete_internal(ptr, label); ptr = NULL; }
+
+#if ENABLE_MEM_PROFILER
+ #define UNITY_NEW_AS_ROOT(type, label, areaName, objectName) set_allocation_root(new (label, true, kDefaultMemoryAlignment, __FILE_STRIPPED__, __LINE__) type, label, areaName, objectName)
+ #define UNITY_NEW_AS_ROOT_ALIGNED(type, label, align, areaName, objectName) set_allocation_root(new (label, true, align, __FILE_STRIPPED__, __LINE__) type, label, areaName, objectName)
+ #define SET_PTR_AS_ROOT(ptr, label, areaName, objectName) assign_allocation_root(ptr, label, areaName, objectName)
+#else
+ #define UNITY_NEW_AS_ROOT(type, label, areaName, objectName) new (label, true, kDefaultMemoryAlignment, __FILE_STRIPPED__, __LINE__) type
+ #define UNITY_NEW_AS_ROOT_ALIGNED(type, label, align, areaName, objectName) set_allocation_root(new (label, true, align, __FILE_STRIPPED__, __LINE__) type, label, areaName, objectName)
+ #define SET_PTR_AS_ROOT(ptr, label, areaName, objectName) {}
+#endif
+
+// Deprecated -> Move to new Macros
+#define UNITY_ALLOC(label, size, align) UNITY_MALLOC_ALIGNED(label, size, align)
+#define UNITY_REALLOC(label, ptr, size, align) UNITY_REALLOC_ALIGNED(label, ptr, size, align)
+
+// Check the integrity of the allocator backing a label. Use this to track down memory overwrites
+void ValidateAllocatorIntegrity(MemLabelId label);
+
+#include "STLAllocator.h"
+
+/// ALLOC_TEMP allocates temporary memory that stays alive only inside the block it was allocated in.
+/// It will automatically get freed!
+/// (Watch out that you dont place ALLOC_TEMP inside an if block and use the memory after the if block.
+///
+/// eg.
+/// float* data;
+/// ALLOC_TEMP(data,float,500, kMemSkinning);
+
+#define kMAX_TEMP_STACK_SIZE 2000
+
+// Metrowerks debugger fucks up when we use alloca
+#if defined(__MWERKS__) && !UNITY_RELEASE
+#undef kMAX_TEMP_STACK_SIZE
+#define kMAX_TEMP_STACK_SIZE 0
+#endif
+
+inline void* AlignPtr (void* p, size_t alignment)
+{
+ size_t a = alignment - 1;
+ return (void*)(((size_t)p + a) & ~a);
+}
+
+struct FreeTempMemory
+{
+ FreeTempMemory() : m_Memory (NULL) { }
+ ~FreeTempMemory() { if (m_Memory) UNITY_FREE(kMemTempAlloc, m_Memory); }
+ void* m_Memory;
+};
+
+#define ALLOC_TEMP_ALIGNED(ptr,type,count,alignment) \
+ FreeTempMemory freeTempMemory_##ptr; \
+ { \
+ size_t allocSize = (count) * sizeof(type) + (alignment)-1; \
+ void* allocPtr = NULL; \
+ if (allocSize < kMAX_TEMP_STACK_SIZE) { \
+ if ((count) != 0) \
+ allocPtr = alloca(allocSize); \
+ } else { \
+ if ((count) != 0) { \
+ allocPtr = UNITY_MALLOC_ALIGNED(kMemTempAlloc, allocSize, kDefaultMemoryAlignment); \
+ freeTempMemory_##ptr.m_Memory = allocPtr; \
+ } \
+ } \
+ ptr = reinterpret_cast<type*> (AlignPtr(allocPtr, alignment)); \
+ } \
+ ANALYSIS_ASSUME(ptr)
+
+#define ALLOC_TEMP(ptr, type, count) \
+ ALLOC_TEMP_ALIGNED(ptr,type,count,kDefaultMemoryAlignment)
+
+#define MALLOC_TEMP(ptr, size) \
+ ALLOC_TEMP_ALIGNED(ptr,char,size,kDefaultMemoryAlignment)
+
+#define ALLOC_TEMP_MANUAL(type,count) \
+ (type*)UNITY_MALLOC_ALIGNED(kMemTempAlloc, (count) * sizeof (type), kDefaultMemoryAlignment)
+
+#define FREE_TEMP_MANUAL(ptr) \
+ UNITY_FREE(kMemTempAlloc, ptr)
+
+
+#if UNITY_XENON
+// Copies a specified number of bytes from a region of cached memory to a region of memory of an unspecified type.
+#define UNITY_MEMCPY(dest, src, count) XMemCpyStreaming(dest, src, count)
+#else
+#define UNITY_MEMCPY(dest, src, count) memcpy(dest, src, count)
+#endif
+
+
+#endif
diff --git a/Runtime/Allocator/MemoryManager.cpp b/Runtime/Allocator/MemoryManager.cpp
new file mode 100644
index 0000000..11d98b4
--- /dev/null
+++ b/Runtime/Allocator/MemoryManager.cpp
@@ -0,0 +1,1659 @@
+#include "UnityPrefix.h"
+#include "Runtime/Allocator/MemoryManager.h"
+#include "Runtime/Utilities/MemoryUtilities.h"
+#include "Runtime/Utilities/BitUtility.h"
+#include "Runtime/Threads/AtomicOps.h"
+#include "Runtime/Profiler/MemoryProfiler.h"
+
+// under new clang -fpermissive is no longer available, and it is quite strict about proper signatures of global new/delete
+// eg throw() is reserved for nonthrow only
+#define STRICTCPP_NEW_DELETE_SIGNATURES UNITY_OSX || UNITY_IPHONE
+#if STRICTCPP_NEW_DELETE_SIGNATURES
+ #define THROWING_NEW_THROW throw(std::bad_alloc)
+#else
+ #define THROWING_NEW_THROW throw()
+#endif
+
+
+
+#if UNITY_PS3
+#include "PlatformDependent/PS3Player/Allocator/PS3DlmallocAllocator.h"
+#endif
+
+#define STOMP_MEMORY !UNITY_PS3 && (!UNITY_RELEASE)
+
+#define kMemoryManagerOverhead ((sizeof(int) + kDefaultMemoryAlignment - 1) & ~(kDefaultMemoryAlignment-1))
+
+#if UNITY_XENON
+
+#include "PlatformDependent/Xbox360/Source/XenonMemory.h"
+
+#define UNITY_LL_ALLOC(l, s, a) xenon::trackedMalloc(s, a)
+#define UNITY_LL_REALLOC(l, p, s, a) xenon::trackedRealloc(p, s, a)
+#define UNITY_LL_FREE(l, p) xenon::trackedFree(p)
+
+#elif UNITY_PS3
+
+void* operator new (size_t size, size_t align) throw() { return GetMemoryManager().Allocate (size == 0 ? 4 : size, align, kMemNewDelete); }
+void* operator new [] (size_t size, size_t align) throw() { return GetMemoryManager().Allocate (size == 0 ? 4 : size, align, kMemNewDelete); }
+
+#include "PlatformDependent/PS3Player/Allocator/PS3Memory.h"
+
+#define UNITY_LL_ALLOC(l,s,a) PS3Memory::Alloc(l,s,a)
+#define UNITY_LL_REALLOC(l,p,s, a) PS3Memory::Realloc(l,p,s,a)
+#define UNITY_LL_FREE(l,p) PS3Memory::Free(l, p)
+
+#elif UNITY_ANDROID
+
+#define UNITY_LL_ALLOC(l,s,a) ::memalign(a, s)
+#define UNITY_LL_REALLOC(l,p,s,a) ::realloc(p, s)
+#define UNITY_LL_FREE(l,p) ::free(p)
+
+#else
+
+#define UNITY_LL_ALLOC(l,s,a) ::malloc(s)
+#define UNITY_LL_REALLOC(l,p,s,a) ::realloc(p, s)
+#define UNITY_LL_FREE(l,p) ::free(p)
+
+#endif
+
+void* operator new (size_t size, MemLabelRef label, bool set_root, int align, const char* file, int line)
+{
+#if ENABLE_MEM_PROFILER
+ bool root_was_set = false;
+ if (set_root) root_was_set = push_allocation_root(NULL, false);
+#endif
+ void* p = malloc_internal (size, align, label, kAllocateOptionNone, file, line);
+#if ENABLE_MEM_PROFILER
+ if (root_was_set) pop_allocation_root();
+ if (set_root) {
+ GetMemoryProfiler()->RegisterRootAllocation(p, GetMemoryManager().GetAllocator(label), NULL, NULL);
+ push_allocation_root(p, true);
+ }
+#endif
+ return p;
+}
+void* operator new [] (size_t size, MemLabelRef label, bool set_root, int align, const char* file, int line)
+{
+#if ENABLE_MEM_PROFILER
+ bool root_was_set = false;
+ if (set_root) root_was_set = push_allocation_root(NULL, false);
+#endif
+ void* p = malloc_internal (size, align, label, kAllocateOptionNone, file, line);
+#if ENABLE_MEM_PROFILER
+ if (root_was_set) pop_allocation_root();
+ if (set_root) {
+ GetMemoryProfiler()->RegisterRootAllocation(p, GetMemoryManager().GetAllocator(label), NULL, NULL);
+ push_allocation_root(p, true);
+ }
+#endif
+ return p;
+}
+void operator delete (void* p, MemLabelRef label, bool /*set_root*/, int /*align*/, const char* /*file*/, int /*line*/) { free_alloc_internal (p, label); }
+void operator delete [] (void* p, MemLabelRef label, bool /*set_root*/, int /*align*/, const char* /*file*/, int /*line*/) { free_alloc_internal (p, label); }
+
+#if ENABLE_MEMORY_MANAGER
+#include "Runtime/Allocator/UnityDefaultAllocator.h"
+#include "Runtime/Allocator/DynamicHeapAllocator.h"
+#include "Runtime/Allocator/LowLevelDefaultAllocator.h"
+#include "Runtime/Allocator/StackAllocator.h"
+#include "Runtime/Allocator/TLSAllocator.h"
+#include "Runtime/Allocator/DualThreadAllocator.h"
+#include "Runtime/Threads/Thread.h"
+#include "Runtime/Misc/Allocator.h"
+#include "Runtime/Utilities/Word.h"
+#include "Runtime/Threads/ThreadSpecificValue.h"
+
+#if UNITY_IPHONE
+ #include "PlatformDependent/iPhonePlayer/iPhoneNewLabelAllocator.h"
+#endif
+
+#include <map>
+
+typedef DualThreadAllocator< DynamicHeapAllocator< LowLevelAllocator > > MainThreadAllocator;
+typedef TLSAllocator< StackAllocator > TempTLSAllocator;
+
+static MemoryManager* g_MemoryManager = NULL;
+
+#if UNITY_FLASH
+ extern "C" void NativeExt_FreeMemManager(void* p){
+ GetMemoryManager().Deallocate (p, kMemNewDelete);
+ }
+#endif
+
+// new override does not work on mac together with pace
+#if !((UNITY_OSX || UNITY_LINUX) && UNITY_EDITOR) && !(UNITY_WEBGL) && !(UNITY_BB10) && !(UNITY_TIZEN)
+void* operator new (size_t size) THROWING_NEW_THROW { return GetMemoryManager().Allocate (size==0?4:size, kDefaultMemoryAlignment, kMemNewDelete, kAllocateOptionNone, "Overloaded New"); }
+void* operator new [] (size_t size) THROWING_NEW_THROW { return GetMemoryManager().Allocate (size==0?4:size, kDefaultMemoryAlignment, kMemNewDelete, kAllocateOptionNone, "Overloaded New[]"); }
+void operator delete (void* p) throw() { GetMemoryManager().Deallocate (p, kMemNewDelete); }
+void operator delete [] (void* p) throw() { GetMemoryManager().Deallocate (p, kMemNewDelete); }
+
+void* operator new (size_t size, const std::nothrow_t&) throw() { return GetMemoryManager().Allocate (size, kDefaultMemoryAlignment, kMemNewDelete, kAllocateOptionNone, "Overloaded New"); }
+void* operator new [] (size_t size, const std::nothrow_t&) throw() { return GetMemoryManager().Allocate (size, kDefaultMemoryAlignment, kMemNewDelete, kAllocateOptionNone, "Overloaded New[]"); };
+void operator delete (void* p, const std::nothrow_t&) throw() { GetMemoryManager().Deallocate (p, kMemNewDelete); }
+void operator delete [] (void* p, const std::nothrow_t&) throw() { GetMemoryManager().Deallocate (p, kMemNewDelete); }
+#endif
+
+#if UNITY_EDITOR
+static size_t kDynamicHeapChunkSize = 16*1024*1024;
+static size_t kTempAllocatorMainSize = 4*1024*1024;
+static size_t kTempAllocatorThreadSize = 64*1024;
+#elif UNITY_IPHONE || UNITY_ANDROID || UNITY_WII || UNITY_XENON || UNITY_PS3 || UNITY_FLASH || UNITY_WEBGL || UNITY_BB10 || UNITY_WP8 || UNITY_TIZEN
+# if !UNITY_IPHONE && !UNITY_WP8
+static size_t kDynamicHeapChunkSize = 1*1024*1024;
+# endif
+static size_t kTempAllocatorMainSize = 128*1024;
+static size_t kTempAllocatorThreadSize = 64*1024;
+#else
+// Win/osx/linux players
+static size_t kDynamicHeapChunkSize = 4*1024*1024;
+static size_t kTempAllocatorMainSize = 512*1024;
+static size_t kTempAllocatorThreadSize = 64*1024;
+#endif
+
+#if ENABLE_MEMORY_MANAGER
+
+
+void PrintShortMemoryStats(TEMP_STRING& str, MemLabelRef label);
+
+static int AlignUp(int value, int alignment)
+{
+ UInt32 ptr = value;
+ UInt32 bitMask = (alignment - 1);
+ UInt32 lowBits = ptr & bitMask;
+ UInt32 adjust = ((alignment - lowBits) & bitMask);
+ return adjust;
+}
+
+void* GetPreallocatedMemory(int size)
+{
+ const size_t numAllocators = 20; // should be configured per platform
+ const size_t additionalStaticMem = sizeof(UnityDefaultAllocator<void>) * numAllocators;
+
+ // preallocated memory for memorymanager and allocators
+ static const int preallocatedSize = sizeof(MemoryManager) + additionalStaticMem;
+#if UNITY_PS3
+ static char __attribute__((aligned(16))) g_MemoryBlockForMemoryManager[preallocatedSize];
+#elif UNITY_XENON
+ static char __declspec(align(16)) g_MemoryBlockForMemoryManager[preallocatedSize];
+#else
+ static char g_MemoryBlockForMemoryManager[preallocatedSize];
+#endif
+ static char* g_MemoryBlockPtr = g_MemoryBlockForMemoryManager;
+
+ size += AlignUp(size, 16);
+
+ void* ptr = g_MemoryBlockPtr;
+ g_MemoryBlockPtr+=size;
+ // Ensure that there is enough space on the preallocated block
+ if(g_MemoryBlockPtr > g_MemoryBlockForMemoryManager + preallocatedSize)
+ return NULL;
+ return ptr;
+}
+#endif
+
+#if UNITY_WIN && ENABLE_MEM_PROFILER
+#define _CRTBLD
+#include <..\crt\src\dbgint.h>
+_CRT_ALLOC_HOOK pfnOldCrtAllocHook;
+int catchMemoryAllocHook(int allocType, void *userData, size_t size, int blockType, long requestNumber, const unsigned char *filename, int lineNumber);
+#endif
+
+void* malloc_internal(size_t size, int align, MemLabelRef label, int allocateOptions, const char* file, int line)
+{
+ return GetMemoryManager ().Allocate(size, align, label, allocateOptions, file, line);
+}
+
+void* calloc_internal(size_t count, size_t size, int align, MemLabelRef label, int allocateOptions, const char* file, int line)
+{
+ void* ptr = GetMemoryManager ().Allocate(size*count, align, label, allocateOptions, file, line);
+ if (ptr) memset (ptr, 0, size*count);
+ return ptr;
+}
+
+void* realloc_internal(void* ptr, size_t size, int align, MemLabelRef label, int allocateOptions, const char* file, int line)
+{
+ return GetMemoryManager ().Reallocate(ptr, size, align, label, allocateOptions, file, line);
+}
+
+void free_internal(void* ptr)
+{
+ // used for mac, since malloc is not hooked from the start, so a number of mallocs will have passed through
+ // before we wrap. This results in pointers being freed, that were not allocated with the memorymanager
+ // therefore we need the alloc->Contains check()
+ GetMemoryManager().Deallocate (ptr);
+}
+
+void free_alloc_internal(void* ptr, MemLabelRef label)
+{
+ GetMemoryManager().Deallocate (ptr, label);
+}
+
+#if (UNITY_OSX && UNITY_EDITOR)
+
+#include <malloc/malloc.h>
+#include <mach/vm_map.h>
+void *(*systemMalloc)(malloc_zone_t *zone, size_t size);
+void *(*systemCalloc)(malloc_zone_t *zone, size_t num_items, size_t size);
+void *(*systemValloc)(malloc_zone_t *zone, size_t size);
+void *(*systemRealloc)(malloc_zone_t *zone, void* ptr, size_t size);
+void *(*systemMemalign)(malloc_zone_t *zone, size_t align, size_t size);
+void (*systemFree)(malloc_zone_t *zone, void *ptr);
+void (*systemFreeSize)(malloc_zone_t *zone, void *ptr, size_t size);
+
+void* my_malloc(malloc_zone_t *zone, size_t size)
+{
+ void* ptr = (*systemMalloc)(zone,size);
+ MemoryManager::m_LowLevelAllocated+=(*malloc_default_zone()->size)(zone, ptr);
+ return ptr;
+
+}
+
+void* my_calloc(malloc_zone_t *zone, size_t num_items, size_t size)
+{
+ void* ptr = (*systemCalloc)(zone,num_items,size);
+ MemoryManager::m_LowLevelAllocated+=(*malloc_default_zone()->size)(zone, ptr);
+ return ptr;
+
+}
+
+void* my_valloc(malloc_zone_t *zone, size_t size)
+{
+ void* ptr = (*systemValloc)(zone,size);
+ MemoryManager::m_LowLevelAllocated+=(*malloc_default_zone()->size)(zone, ptr);
+ return ptr;
+}
+
+void* my_realloc(malloc_zone_t *zone, void* ptr, size_t size)
+{
+ MemoryManager::m_LowLevelAllocated-=(*malloc_default_zone()->size)(zone, ptr);
+ void* newptr = (*systemRealloc)(zone,ptr,size);
+ MemoryManager::m_LowLevelAllocated+=(*malloc_default_zone()->size)(zone, newptr);
+ return newptr;
+}
+
+void* my_memalign(malloc_zone_t *zone, size_t align, size_t size)
+{
+ void* ptr = (*systemMemalign)(zone,align,size);
+ MemoryManager::m_LowLevelAllocated+=(*malloc_default_zone()->size)(zone, ptr);
+ return ptr;
+}
+
+void my_free(malloc_zone_t *zone, void *ptr)
+{
+ int oldsize = (*malloc_default_zone()->size)(zone,ptr);
+ MemoryManager::m_LowLevelAllocated-=oldsize;
+ systemFree(zone,ptr);
+}
+
+void my_free_definite_size(malloc_zone_t *zone, void *ptr, size_t size)
+{
+ MemoryManager::m_LowLevelAllocated-=(*malloc_default_zone()->size)(zone,ptr);
+ systemFreeSize(zone,ptr,size);
+}
+
+#endif
+
+void InitializeMemory()
+{
+ InitializeMemoryLabels();
+
+#if (UNITY_OSX && UNITY_EDITOR)
+
+ UInt32 osxversion = 0;
+ Gestalt(gestaltSystemVersion, (MacSInt32 *) &osxversion);
+
+ // overriding malloc_zone on osx 10.5 causes unity not to start up.
+ if(osxversion >= 0x01060)
+ {
+ malloc_zone_t* dz = malloc_default_zone();
+
+ systemMalloc = dz->malloc;
+ systemCalloc = dz->calloc;
+ systemValloc = dz->valloc;
+ systemRealloc = dz->realloc;
+ systemMemalign = dz->memalign;
+ systemFree = dz->free;
+ systemFreeSize = dz->free_definite_size;
+
+ if(dz->version>=8)
+ vm_protect(mach_task_self(), (uintptr_t)dz, sizeof(malloc_zone_t), 0, VM_PROT_READ | VM_PROT_WRITE);//remove the write protection
+
+ dz->malloc=&my_malloc;
+ dz->calloc=&my_calloc;
+ dz->valloc=&my_valloc;
+ dz->realloc=&my_realloc;
+ dz->memalign=&my_memalign;
+ dz->free=&my_free;
+ dz->free_definite_size=&my_free_definite_size;
+
+ if(dz->version>=8)
+ vm_protect(mach_task_self(), (uintptr_t)dz, sizeof(malloc_zone_t), 0, VM_PROT_READ);//put the write protection back
+
+ }
+#endif
+}
+
+MemoryManager& GetMemoryManager()
+{
+ if (g_MemoryManager == NULL){
+ InitializeMemory();
+ g_MemoryManager = HEAP_NEW(MemoryManager)();
+ }
+ return *g_MemoryManager ;
+}
+
+volatile long MemoryManager::m_LowLevelAllocated = 0;
+volatile long MemoryManager::m_RegisteredGfxDriverMemory = 0;
+
+#if ENABLE_MEMORY_MANAGER
+
+#if _DEBUG || UNITY_EDITOR
+UNITY_TLS_VALUE(bool) s_DisallowAllocationsOnThread;
+inline void MemoryManager::CheckDisalowAllocation()
+{
+ DebugAssert(IsActive());
+
+ // Some codepaths in Unity disallow allocations. For example when a GC is running all threads are stopped.
+ // If we allowed allocations we would allow for very hard to find race conditions.
+ // Thus we explicitly check for it in debug builds.
+ if (s_DisallowAllocationsOnThread)
+ {
+ s_DisallowAllocationsOnThread = false;
+ FatalErrorMsg("CheckDisalowAllocation. Allocating memory when it is not allowed to allocate memory.\n");
+ }
+}
+
+#else
+inline void MemoryManager::CheckDisalowAllocation()
+{
+}
+#endif
+
+MemoryManager::MemoryManager()
+: m_NumAllocators(0)
+, m_FrameTempAllocator(NULL)
+, m_IsInitialized(false)
+, m_IsActive(false)
+{
+#if UNITY_WIN && ENABLE_MEM_PROFILER
+// pfnOldCrtAllocHook = _CrtSetAllocHook(catchMemoryAllocHook);
+#endif
+
+ memset (m_Allocators, 0, sizeof(m_Allocators));
+ memset (m_MainAllocators, 0, sizeof(m_MainAllocators));
+ memset (m_ThreadAllocators, 0, sizeof(m_ThreadAllocators));
+ memset (m_AllocatorMap, 0, sizeof(m_AllocatorMap));
+
+ // Main thread will not have a valid TLSAlloc until ThreadInitialize() is called!
+#if UNITY_FLASH || UNITY_WEBGL
+ m_InitialFallbackAllocator = HEAP_NEW(UnityDefaultAllocator<LowLevelAllocator>) ("ALLOC_FALLBACK");
+#else
+ m_InitialFallbackAllocator = HEAP_NEW(DynamicHeapAllocator<LowLevelAllocator>) (1024*1024, 0, true,"ALLOC_FALLBACK");
+#endif
+
+ for (int i = 0; i < kMemLabelCount; i++)
+ m_AllocatorMap[i].alloc = m_InitialFallbackAllocator;
+}
+
+void MemoryManager::StaticInitialize()
+{
+ GetMemoryManager().ThreadInitialize();
+}
+
+void MemoryManager::StaticDestroy()
+{
+#if !UNITY_OSX
+ // not able to destroy profiler and memorymanager on osx because apple.coreaudio is still running a thread.
+ // FMOD is looking into shutting this down properly. Untill then, don't cleanup
+#if ENABLE_MEM_PROFILER
+ MemoryProfiler::StaticDestroy();
+#endif
+ GetMemoryManager().ThreadCleanup();
+#endif
+}
+
+void MemoryManager::InitializeMainThreadAllocators()
+{
+ m_FrameTempAllocator = HEAP_NEW(TempTLSAllocator)("ALLOC_TEMP_THREAD");
+
+#if (UNITY_WIN && !UNITY_WP8) || UNITY_OSX
+ BaseAllocator* defaultThreadAllocator = NULL;
+ m_MainAllocators[m_NumAllocators] = HEAP_NEW(DynamicHeapAllocator<LowLevelAllocator>) (kDynamicHeapChunkSize, 1024, false,"ALLOC_DEFAULT_MAIN");
+ m_ThreadAllocators[m_NumAllocators] = HEAP_NEW(DynamicHeapAllocator<LowLevelAllocator>) (1024*1024,1024, true,"ALLOC_DEFAULT_THREAD");
+ BaseAllocator* defaultAllocator = m_Allocators[m_NumAllocators] = HEAP_NEW(MainThreadAllocator)("ALLOC_DEFAULT", m_MainAllocators[m_NumAllocators], m_ThreadAllocators[m_NumAllocators]);
+ defaultThreadAllocator = m_ThreadAllocators[m_NumAllocators];
+ m_NumAllocators++;
+#else
+ BaseAllocator* defaultAllocator = m_Allocators[m_NumAllocators++] = HEAP_NEW(UnityDefaultAllocator<LowLevelAllocator>)("ALLOC_DEFAULT");
+#endif
+
+ for (int i = 0; i < kMemLabelCount; i++)
+ m_AllocatorMap[i].alloc = defaultAllocator;
+
+ m_AllocatorMap[kMemTempAllocId].alloc = m_FrameTempAllocator;
+ m_AllocatorMap[kMemStaticStringId].alloc = m_InitialFallbackAllocator;
+
+#if UNITY_IPHONE
+ m_AllocatorMap[kMemNewDeleteId].alloc = m_Allocators[m_NumAllocators++] = HEAP_NEW(IphoneNewLabelAllocator);
+#endif
+
+#if (UNITY_WIN && !UNITY_WP8) || UNITY_OSX
+ m_MainAllocators[m_NumAllocators] = HEAP_NEW(DynamicHeapAllocator<LowLevelAllocator>) (kDynamicHeapChunkSize,0, false,"ALLOC_GFX_MAIN");
+ m_ThreadAllocators[m_NumAllocators] = HEAP_NEW(DynamicHeapAllocator<LowLevelAllocator>) (1024*1024,0, true,"ALLOC_GFX_THREAD");
+ BaseAllocator* gfxAllocator = m_Allocators[m_NumAllocators] = HEAP_NEW(MainThreadAllocator)("ALLOC_GFX", m_MainAllocators[m_NumAllocators], m_ThreadAllocators[m_NumAllocators]);
+ BaseAllocator* gfxThreadAllocator = m_ThreadAllocators[m_NumAllocators];
+ m_NumAllocators++;
+
+ m_MainAllocators[m_NumAllocators] = HEAP_NEW(DynamicHeapAllocator<LowLevelAllocator>) (8*1024*1024,0, false,"ALLOC_CACHEOBJECTS_MAIN");
+ m_ThreadAllocators[m_NumAllocators] = HEAP_NEW(DynamicHeapAllocator<LowLevelAllocator>) (2*1024*1024,0, true,"ALLOC_CACHEOBJECTS_THREAD");
+ BaseAllocator* cacheAllocator = m_Allocators[m_NumAllocators] = HEAP_NEW(MainThreadAllocator)("ALLOC_CACHEOBJECTS", m_MainAllocators[m_NumAllocators], m_ThreadAllocators[m_NumAllocators]);
+ m_NumAllocators++;
+
+ m_MainAllocators[m_NumAllocators] = HEAP_NEW(DynamicHeapAllocator<LowLevelAllocator>) (kDynamicHeapChunkSize,0, false,"ALLOC_TYPETREE_MAIN");
+ m_ThreadAllocators[m_NumAllocators] = HEAP_NEW(DynamicHeapAllocator<LowLevelAllocator>) (1024*1024,0, true,"ALLOC_TYPETREE_THREAD");
+ BaseAllocator* typetreeAllocator = m_Allocators[m_NumAllocators] = HEAP_NEW(MainThreadAllocator)("ALLOC_TYPETREE", m_MainAllocators[m_NumAllocators], m_ThreadAllocators[m_NumAllocators]);
+ m_NumAllocators++;
+
+ m_MainAllocators[m_NumAllocators] = HEAP_NEW(DynamicHeapAllocator<LowLevelAllocator>) (4*1024*1024,0, false,"ALLOC_PROFILER_MAIN");
+ m_ThreadAllocators[m_NumAllocators] = HEAP_NEW(DynamicHeapAllocator<LowLevelAllocator>) (4*1024*1024,0, true,"ALLOC_PROFILER_THREAD");
+ BaseAllocator* profilerAllocator = m_Allocators[m_NumAllocators] = HEAP_NEW(MainThreadAllocator)("ALLOC_PROFILER", m_MainAllocators[m_NumAllocators], m_ThreadAllocators[m_NumAllocators]);
+ m_NumAllocators++;
+
+ m_AllocatorMap[kMemDynamicGeometryId].alloc
+ = m_AllocatorMap[kMemImmediateGeometryId].alloc
+ = m_AllocatorMap[kMemGeometryId].alloc
+ = m_AllocatorMap[kMemVertexDataId].alloc
+ = m_AllocatorMap[kMemBatchedGeometryId].alloc
+ = m_AllocatorMap[kMemTextureId].alloc = gfxAllocator;
+
+ m_AllocatorMap[kMemTypeTreeId].alloc = typetreeAllocator;
+
+ m_AllocatorMap[kMemThreadId].alloc = defaultThreadAllocator;
+ m_AllocatorMap[kMemGfxThreadId].alloc = gfxThreadAllocator;
+
+ m_AllocatorMap[kMemTextureCacheId].alloc = cacheAllocator;
+ m_AllocatorMap[kMemSerializationId].alloc = cacheAllocator;
+ m_AllocatorMap[kMemFileId].alloc = cacheAllocator;
+
+ m_AllocatorMap[kMemProfilerId].alloc = profilerAllocator;
+ m_AllocatorMap[kMemMemoryProfilerId].alloc = profilerAllocator;
+ m_AllocatorMap[kMemMemoryProfilerStringId].alloc = profilerAllocator;
+
+#elif UNITY_XENON
+#if 1
+ // DynamicHeapAllocator uses TLSF pools which are O(1) constant time for alloc/free
+ // It should be used for high alloc/dealloc traffic
+ BaseAllocator* dynAllocator = m_Allocators[m_NumAllocators++] = HEAP_NEW(DynamicHeapAllocator<LowLevelAllocator>)(32*1024*1024, 0, true, "ALLOC_TINYBLOCKS");
+ m_AllocatorMap[kMemBaseObjectId].alloc = dynAllocator;
+ m_AllocatorMap[kMemAnimationId].alloc = dynAllocator;
+ m_AllocatorMap[kMemSTLId].alloc = dynAllocator;
+ m_AllocatorMap[kMemNewDeleteId].alloc = dynAllocator;
+#else
+ BaseAllocator* gameObjectAllocator = m_Allocators[m_NumAllocators++] = HEAP_NEW(UnityDefaultAllocator<LowLevelAllocator>)("ALLOC_GAMEOBJECT");
+ BaseAllocator* gfxAllocator = m_Allocators[m_NumAllocators++] = HEAP_NEW(UnityDefaultAllocator<LowLevelAllocator>)("ALLOC_GFX");
+
+ BaseAllocator* profilerAllocator;
+#if XBOX_USE_DEBUG_MEMORY
+ if (xenon::GetIsDebugMemoryEnabled())
+ profilerAllocator = m_Allocators[m_NumAllocators++] = HEAP_NEW(DynamicHeapAllocator<LowLevelAllocatorDebugMem>)(16*1024*1024, 0, true, "ALLOC_PROFILER");
+ else
+#endif
+ profilerAllocator = m_Allocators[m_NumAllocators++] = HEAP_NEW(UnityDefaultAllocator<LowLevelAllocator>)("ALLOC_PROFILER");
+
+ m_AllocatorMap[kMemDynamicGeometryId].alloc
+ = m_AllocatorMap[kMemImmediateGeometryId].alloc
+ = m_AllocatorMap[kMemGeometryId].alloc
+ = m_AllocatorMap[kMemVertexDataId].alloc
+ = m_AllocatorMap[kMemBatchedGeometryId].alloc
+ = m_AllocatorMap[kMemTextureId].alloc = gfxAllocator;
+
+ m_AllocatorMap[kMemBaseObjectId].alloc = gameObjectAllocator;
+
+ m_AllocatorMap[kMemProfilerId].alloc = profilerAllocator;
+ m_AllocatorMap[kMemMemoryProfilerId].alloc = profilerAllocator;
+ m_AllocatorMap[kMemMemoryProfilerStringId].alloc = profilerAllocator;
+#endif
+
+#elif UNITY_PS3
+
+ BaseAllocator* gameObjectAllocator = m_Allocators[m_NumAllocators++] = HEAP_NEW(UnityDefaultAllocator<LowLevelAllocator>)("ALLOC_GAMEOBJECT");
+ BaseAllocator* gfxAllocator = m_Allocators[m_NumAllocators++] = HEAP_NEW(UnityDefaultAllocator<LowLevelAllocator>)("ALLOC_GFX");
+ BaseAllocator* profilerAllocator = m_Allocators[m_NumAllocators++] = HEAP_NEW(UnityDefaultAllocator<LowLevelAllocator>)("ALLOC_PROFILER");
+ BaseAllocator* vertexDataAllocator = m_Allocators[m_NumAllocators++] = HEAP_NEW(PS3DelayedReleaseAllocator("PS3_DELAYED_RELEASE_ALLOCATOR_PROXY", HEAP_NEW(PS3DlmallocAllocator)("PS3_DLMALLOC_ALLOCATOR")));
+ BaseAllocator* delayedReleaseAllocator = m_Allocators[m_NumAllocators++] = HEAP_NEW(PS3DelayedReleaseAllocator("PS3_DELAYED_RELEASE_ALLOCATOR_PROXY", HEAP_NEW(UnityDefaultAllocator<LowLevelAllocator>)("PS3_DELAYED_RELEASE_ALLOCATOR")));
+ BaseAllocator* ioMappedAllocator = m_Allocators[m_NumAllocators++] = HEAP_NEW(PS3DlmallocAllocator("PS3_IOMAPPED_ALLOCATOR"));
+
+ m_AllocatorMap[kMemDynamicGeometryId].alloc
+ = m_AllocatorMap[kMemImmediateGeometryId].alloc
+ = m_AllocatorMap[kMemGeometryId].alloc
+ = m_AllocatorMap[kMemVertexDataId].alloc
+ = m_AllocatorMap[kMemBatchedGeometryId].alloc
+ = m_AllocatorMap[kMemPS3RingBuffers.label].alloc
+ = m_AllocatorMap[kMemPS3RSXBuffers.label].alloc
+ = m_AllocatorMap[kMemTextureId].alloc = ioMappedAllocator;
+
+ m_AllocatorMap[kMemBaseObjectId].alloc = gameObjectAllocator;
+ m_AllocatorMap[kMemProfilerId].alloc =
+ m_AllocatorMap[kMemMemoryProfilerId].alloc =
+ m_AllocatorMap[kMemMemoryProfilerStringId].alloc = profilerAllocator;
+
+ m_AllocatorMap[kMemSkinningId].alloc =
+// m_AllocatorMap[kMemSkinningTempId].alloc =
+ m_AllocatorMap[kMemPS3DelayedReleaseId].alloc = delayedReleaseAllocator;
+
+ m_AllocatorMap[kMemVertexDataId].alloc = vertexDataAllocator;
+
+#else
+
+ BaseAllocator* gameObjectAllocator = m_Allocators[m_NumAllocators++] = HEAP_NEW(UnityDefaultAllocator<LowLevelAllocator>)("ALLOC_GAMEOBJECT");
+ BaseAllocator* gfxAllocator = m_Allocators[m_NumAllocators++] = HEAP_NEW(UnityDefaultAllocator<LowLevelAllocator>)("ALLOC_GFX");
+ BaseAllocator* profilerAllocator = m_Allocators[m_NumAllocators++] = HEAP_NEW(UnityDefaultAllocator<LowLevelAllocator>)("ALLOC_PROFILER");
+
+ m_AllocatorMap[kMemDynamicGeometryId].alloc
+ = m_AllocatorMap[kMemImmediateGeometryId].alloc
+ = m_AllocatorMap[kMemGeometryId].alloc
+ = m_AllocatorMap[kMemVertexDataId].alloc
+ = m_AllocatorMap[kMemBatchedGeometryId].alloc
+ = m_AllocatorMap[kMemTextureId].alloc = gfxAllocator;
+
+ m_AllocatorMap[kMemBaseObjectId].alloc = gameObjectAllocator;
+
+ m_AllocatorMap[kMemProfilerId].alloc
+ = m_AllocatorMap[kMemMemoryProfilerId].alloc
+ = m_AllocatorMap[kMemMemoryProfilerStringId].alloc = profilerAllocator;
+
+#endif
+
+ m_IsInitialized = true;
+ m_IsActive = true;
+
+#if ENABLE_MEM_PROFILER
+ MemoryProfiler::StaticInitialize();
+#endif
+
+ Assert(m_FrameTempAllocator);
+}
+
+MemoryManager::~MemoryManager()
+{
+ for(int i = 0; i < m_NumAllocators; i++)
+ {
+ Assert(m_Allocators[i]->GetAllocatedMemorySize() == 0);
+ }
+
+ ThreadCleanup();
+
+ for (int i = 0; i < m_NumAllocators; i++){
+ HEAP_DELETE (m_Allocators[i], BaseAllocator);
+ }
+#if UNITY_WIN
+#if ENABLE_MEM_PROFILER
+// _CrtSetAllocHook(pfnOldCrtAllocHook);
+#endif
+#endif
+}
+
+class MemoryManagerAutoDestructor
+{
+public:
+ MemoryManagerAutoDestructor(){}
+ ~MemoryManagerAutoDestructor()
+ {
+ //HEAP_DELETE(g_MemoryManager, MemoryManager);
+ }
+};
+
+MemoryManagerAutoDestructor g_MemoryManagerAutoDestructor;
+
+
+#else
+
+
+MemoryManager::MemoryManager()
+: m_NumAllocators(0)
+, m_FrameTempAllocator(NULL)
+{
+}
+
+MemoryManager::~MemoryManager()
+{
+}
+
+#endif
+
+#if UNITY_WIN
+#include <winnt.h>
+#define ON_WIN(x) x
+#else
+#define ON_WIN(x)
+#endif
+
+#if ENABLE_MEMORY_MANAGER
+
+void* MemoryManager::LowLevelAllocate(size_t size)
+{
+ ON_WIN( InterlockedExchangeAdd(&m_LowLevelAllocated, size) );
+ int* ptr = (int*)UNITY_LL_ALLOC(kMemDefault, size + kMemoryManagerOverhead, kDefaultMemoryAlignment);
+ if(ptr != NULL)
+ {
+ *ptr = size;
+ ptr += (kMemoryManagerOverhead >> 2);
+ }
+
+ return ptr;
+}
+
+void* MemoryManager::LowLevelCAllocate(size_t count, size_t size)
+{
+ ON_WIN( InterlockedExchangeAdd(&m_LowLevelAllocated, count*size) );
+ int allocSize = count*size + kMemoryManagerOverhead;
+ int* ptr = (int*)UNITY_LL_ALLOC(kMemDefault, allocSize, kDefaultMemoryAlignment);
+ if(ptr != NULL)
+ {
+ memset(ptr, 0, allocSize);
+ *ptr = count*size;
+ ptr += (kMemoryManagerOverhead >> 2);
+ }
+ return ptr;
+}
+
+void* MemoryManager::LowLevelReallocate( void* p, size_t size )
+{
+ int* ptr = (int*) p;
+ ptr -= kMemoryManagerOverhead >> 2;
+ ON_WIN( InterlockedExchangeAdd(&m_LowLevelAllocated, -*ptr) );
+ int* newptr = (int*)UNITY_LL_REALLOC(kMemDefault, ptr, size + kMemoryManagerOverhead, kDefaultMemoryAlignment);
+ if(newptr != NULL)
+ {
+ *newptr = size;
+ newptr += (kMemoryManagerOverhead >> 2);
+ }
+ ON_WIN( InterlockedExchangeAdd(&m_LowLevelAllocated, size) );
+ return newptr;
+}
+
+void MemoryManager::LowLevelFree(void* p){
+ if(p == NULL)
+ return;
+ int* ptr = (int*) p;
+ ptr -= kMemoryManagerOverhead >> 2;
+ ON_WIN( InterlockedExchangeAdd(&m_LowLevelAllocated, -*ptr) );
+ UNITY_LL_FREE(kMemDefault,ptr);
+}
+
+void MemoryManager::ThreadInitialize(size_t tempSize)
+{
+ int tempAllocatorSize = kTempAllocatorThreadSize;
+ if(Thread::CurrentThreadIsMainThread() && !m_IsInitialized)
+ {
+ InitializeMainThreadAllocators();
+ tempAllocatorSize = kTempAllocatorMainSize;
+ }
+
+ if(tempSize != 0)
+ tempAllocatorSize = tempSize;
+
+ StackAllocator* tempAllocator = UNITY_NEW(StackAllocator(tempAllocatorSize, "ALLOC_TEMP_THREAD"), kMemManager);
+ m_FrameTempAllocator->ThreadInitialize(tempAllocator);
+}
+
+void MemoryManager::ThreadCleanup()
+{
+ for(int i = 0; i < m_NumAllocators; i++)
+ m_Allocators[i]->ThreadCleanup();
+
+ if(Thread::CurrentThreadIsMainThread())
+ {
+ m_FrameTempAllocator->ThreadCleanup();
+ m_FrameTempAllocator = NULL;
+
+ m_IsActive = false;
+
+#if !UNITY_EDITOR
+ for(int i = 0; i < m_NumAllocators; i++)
+ {
+ HEAP_DELETE(m_Allocators[i], BaseAllocator);
+ if(m_MainAllocators[i])
+ HEAP_DELETE(m_MainAllocators[i], BaseAllocator);
+ if(m_ThreadAllocators[i])
+ HEAP_DELETE(m_ThreadAllocators[i], BaseAllocator);
+ m_Allocators[i] = 0;
+ m_MainAllocators[i] = 0;
+ m_ThreadAllocators[i] = 0;
+ }
+ m_NumAllocators = 0;
+#else
+ for(int i = 0; i < m_NumAllocators; i++)
+ {
+ if(m_MainAllocators[i])
+ m_Allocators[i] = m_MainAllocators[i];
+ }
+#endif
+ for (int i = 0; i < kMemLabelCount; i++)
+ m_AllocatorMap[i].alloc = m_InitialFallbackAllocator;
+
+ return;
+ }
+#if ENABLE_MEM_PROFILER
+ GetMemoryProfiler()->ThreadCleanup();
+#endif
+ m_FrameTempAllocator->ThreadCleanup();
+}
+
+void MemoryManager::FrameMaintenance(bool cleanup)
+{
+ m_FrameTempAllocator->FrameMaintenance(cleanup);
+ for(int i = 0; i < m_NumAllocators; i++)
+ m_Allocators[i]->FrameMaintenance(cleanup);
+}
+
+#else
+
+void* MemoryManager::LowLevelAllocate(int size)
+{
+ return UNITY_LL_ALLOC(kMemDefault,size+sizeof(int), 4);
+}
+
+void* MemoryManager::LowLevelCAllocate(int count, int size)
+{
+ int* ptr = (int*)UNITY_LL_ALLOC(kMemDefault,count*size, 4);
+ memset(ptr,0,count*size);
+ return ptr;
+}
+
+void* MemoryManager::LowLevelReallocate( void* p, int size )
+{
+ return UNITY_LL_REALLOC(kMemDefault,p, size,4);
+}
+
+void MemoryManager::LowLevelFree(void* p){
+ UNITY_LL_FREE(kMemDefault,p);
+}
+
+void MemoryManager::ThreadInitialize(size_t tempSize)
+{}
+
+void MemoryManager::ThreadCleanup()
+{}
+
+#endif
+
+void OutOfMemoryError( size_t size, int align, MemLabelRef label, int line, const char* file )
+{
+ TEMP_STRING str;
+ str.reserve(30*1024);
+ str += FormatString<TEMP_STRING>("Could not allocate memory: System out of memory!\n");
+ str += FormatString<TEMP_STRING>("Trying to allocate: %" PRINTF_SIZET_FORMAT "B with %d alignment. MemoryLabel: %s\n", size, align, GetMemoryManager().GetMemcatName(label));
+ str += FormatString<TEMP_STRING>("Allocation happend at: Line:%d in %s\n", line, file);
+ PrintShortMemoryStats(str, label);
+ // first call the plain printf_console, to make a printout that doesn't do callstack and other allocations
+ printf_console("%s", str.c_str());
+ // Then do a FatalErroString, that brings up a dialog and launches the bugreporter.
+ FatalErrorString(str.c_str());
+}
+
+inline void* CheckAllocation(void* ptr, size_t size, int align, MemLabelRef label, const char* file, int line)
+{
+ if(ptr == NULL)
+ OutOfMemoryError(size, align, label, line, file);
+ return ptr;
+}
+
+void MemoryManager::DisallowAllocationsOnThisThread()
+{
+#if _DEBUG || UNITY_EDITOR
+ s_DisallowAllocationsOnThread = true;
+#endif
+}
+void MemoryManager::ReallowAllocationsOnThisThread()
+{
+#if _DEBUG || UNITY_EDITOR
+ s_DisallowAllocationsOnThread = false;
+#endif
+}
+
+void* MemoryManager::Allocate(size_t size, int align, MemLabelRef label, int allocateOptions/* = kNone*/, const char* file /* = NULL */, int line /* = 0 */)
+{
+ DebugAssert(IsPowerOfTwo(align));
+ DebugAssert(align != 0);
+
+ align = ((align-1) | (kDefaultMemoryAlignment-1)) + 1; // Max(align, kDefaultMemoryAlignment)
+
+ // Fallback to backup allocator if we have not yet initialized the MemoryManager
+ if(!IsActive())
+ {
+ void* ptr = m_InitialFallbackAllocator->Allocate(size, align);
+ #if ENABLE_MEM_PROFILER
+ if (ptr)
+ MemoryProfiler::InitAllocation(ptr, m_InitialFallbackAllocator);
+ #endif
+ return ptr;
+ }
+
+ if (IsTempAllocatorLabel(label))
+ {
+ void* ptr = ((TempTLSAllocator*)m_FrameTempAllocator)->TempTLSAllocator::Allocate(size, align);
+ if(ptr)
+ return ptr;
+ // if tempallocator thread has not been initialized fallback to defualt
+ return Allocate(size, align, kMemDefault, allocateOptions, file, line);
+ }
+
+ BaseAllocator* alloc = GetAllocator(label);
+ CheckDisalowAllocation();
+
+ void* ptr = alloc->Allocate(size, align);
+
+ if ((allocateOptions & kAllocateOptionReturnNullIfOutOfMemory) && !ptr)
+ return NULL;
+
+ CheckAllocation( ptr, size, align, label, file, line );
+
+ #if ENABLE_MEM_PROFILER
+ RegisterAllocation(ptr, size, alloc, label, "Allocate", file, line);
+ #endif
+
+ DebugAssert(((int)ptr & (align-1)) == 0);
+
+#if STOMP_MEMORY
+ memset(ptr, 0xcd, size);
+#endif
+
+ return ptr;
+}
+
+void* MemoryManager::Reallocate(void* ptr, size_t size, int align, MemLabelRef label, int allocateOptions/* = kNone*/, const char* file /* = NULL */, int line /* = 0 */)
+{
+ DebugAssert(IsPowerOfTwo(align));
+ DebugAssert(align != 0);
+
+ if(ptr == NULL)
+ return Allocate(size,align,label,allocateOptions,file,line);
+
+ align = ((align-1) | (kDefaultMemoryAlignment-1)) + 1; // Max(align, kDefaultMemoryAlignment)
+
+ // Fallback to backup allocator if we have not yet initialized the MemoryManager
+ if(!IsActive())
+ return m_InitialFallbackAllocator->Reallocate(ptr, size, align);
+
+ if (IsTempAllocatorLabel(label))
+ {
+ void* newptr = ((TempTLSAllocator*)m_FrameTempAllocator)->TempTLSAllocator::Reallocate(ptr, size, align);
+ if(newptr)
+ return newptr;
+ // if tempallocator thread has not been initialized fallback to defualt
+ return Reallocate( ptr, size, align, kMemDefault, allocateOptions, file, line);
+ }
+
+ BaseAllocator* alloc = GetAllocator(label);
+ CheckDisalowAllocation();
+
+ if(ptr != NULL && !alloc->Contains(ptr))
+ {
+ // It wasn't the expected allocator that contained the pointer.
+ // allocate on the expected allocator and move the memory there
+ void* newptr = Allocate(size,align,label,allocateOptions,file,line);
+ if ((allocateOptions & kAllocateOptionReturnNullIfOutOfMemory) && !newptr)
+ return NULL;
+
+ int oldSize = GetAllocatorContainingPtr(ptr)->GetPtrSize(ptr);
+ memcpy(newptr, ptr, size<oldSize?size:oldSize);
+
+ Deallocate(ptr);
+ return newptr;
+ }
+
+ #if ENABLE_MEM_PROFILER
+ // register the deletion of the old allocation and extract the old root owner into the label
+ ProfilerAllocationHeader* root = RegisterDeallocation(ptr, alloc, label, "Reallocate");
+ #endif
+
+ void* newptr = alloc->Reallocate(ptr, size, align);
+
+ if ((allocateOptions & kAllocateOptionReturnNullIfOutOfMemory) && !newptr)
+ return NULL;
+
+ CheckAllocation( newptr, size, align, label, file, line );
+
+ #if ENABLE_MEM_PROFILER
+ RegisterAllocation(newptr, size, alloc, MemLabelId(label.label, root), "Reallocate", file, line);
+ #endif
+
+ DebugAssert(((int)newptr & (align-1)) == 0);
+
+ return newptr;
+}
+
+void MemoryManager::Deallocate(void* ptr, MemLabelRef label)
+{
+ if(ptr == NULL)
+ return;
+
+ if(!IsActive()) // if we are outside the scope of Initialize/Destroy - fallback
+ return Deallocate(ptr);
+
+ if (IsTempAllocatorLabel(label))
+ {
+ if ( ((TempTLSAllocator*)m_FrameTempAllocator)->TempTLSAllocator::TryDeallocate(ptr) )
+ return;
+ // If not found, Fallback to do a search for what allocator has the pointer
+ return Deallocate(ptr);
+ }
+
+ BaseAllocator* alloc = GetAllocator(label);
+ CheckDisalowAllocation();
+
+ if(!alloc->Contains(ptr))
+ return Deallocate(ptr);
+
+#if ENABLE_MEM_PROFILER
+ RegisterDeallocation(ptr, alloc, label, "Deallocate");
+#endif
+
+#if STOMP_MEMORY
+ memset32(ptr, 0xdeadbeef, alloc->GetPtrSize(ptr));
+#endif
+
+ alloc->Deallocate(ptr);
+}
+
+void MemoryManager::Deallocate(void* ptr)
+{
+ if (ptr == NULL)
+ return;
+
+ BaseAllocator* alloc = GetAllocatorContainingPtr(ptr);
+
+ if (alloc)
+ {
+ Assert (alloc != m_FrameTempAllocator);
+#if ENABLE_MEM_PROFILER
+ if(GetMemoryProfiler() && alloc != m_InitialFallbackAllocator)
+ {
+ size_t oldsize = alloc->GetPtrSize(ptr);
+ GetMemoryProfiler()->UnregisterAllocation(ptr, alloc, oldsize, NULL, kMemDefault);
+ //RegisterDeallocation(MemLabelId(labelid), oldsize); // since we don't have the label, we will not register this deallocation
+ if(m_LogAllocations)
+ printf_console("Deallocate (%8X): %11d\tTotal: %.2fMB (%d)\n", (unsigned int)ptr, -(int)oldsize, GetTotalAllocatedMemory() / (1024.0f * 1024.0f), (int)GetTotalAllocatedMemory());
+ }
+#endif
+#if STOMP_MEMORY
+ memset32(ptr, 0xdeadbeef, alloc->GetPtrSize(ptr));
+#endif
+ alloc->Deallocate(ptr);
+ }
+ else
+ {
+ if(IsActive())
+ UNITY_LL_FREE (kMemDefault, ptr);
+ //else ignore the deallocation, because the allocator is no longer around
+ }
+}
+
+struct ExternalAllocInfo
+{
+ size_t size;
+ size_t relatedID;
+ const char* file;
+ int line;
+};
+typedef UNITY_MAP(kMemMemoryProfiler,void*,ExternalAllocInfo ) ExternalAllocationMap;
+static ExternalAllocationMap* g_ExternalAllocations = NULL;
+Mutex g_ExternalAllocationLock;
+void register_external_gfx_allocation(void* ptr, size_t size, size_t related, const char* file, int line)
+{
+ Mutex::AutoLock autolock(g_ExternalAllocationLock);
+ if (g_ExternalAllocations == NULL)
+ {
+ SET_ALLOC_OWNER(NULL);
+ g_ExternalAllocations = new ExternalAllocationMap();
+ }
+ ExternalAllocationMap::iterator it = g_ExternalAllocations->find(ptr);
+ if (it != g_ExternalAllocations->end())
+ {
+ ErrorStringMsg("allocation 0x%p already registered @ %s:l%d size %d; now calling from %s:l%d size %d?",
+ ptr,
+ it->second.file, it->second.line, it->second.size,
+ file, line, size);
+ }
+
+ if (related == 0)
+ related = (size_t)ptr;
+
+ ExternalAllocInfo info;
+ info.size = size;
+ info.relatedID = related;
+ info.file = file;
+ info.line = line;
+ g_ExternalAllocations->insert(std::make_pair(ptr, info));
+ MemoryManager::m_RegisteredGfxDriverMemory += size;
+#if ENABLE_MEM_PROFILER
+ GetMemoryProfiler()->RegisterMemoryToID(related, size);
+#endif
+}
+
+void register_external_gfx_deallocation(void* ptr, const char* file, int line)
+{
+ if(ptr == NULL)
+ return;
+ Mutex::AutoLock autolock(g_ExternalAllocationLock);
+ if (g_ExternalAllocations == NULL)
+ g_ExternalAllocations = new ExternalAllocationMap();
+
+ ExternalAllocationMap::iterator it = g_ExternalAllocations->find(ptr);
+ if (it == g_ExternalAllocations->end())
+ {
+ // not registered
+ return;
+ }
+ size_t size = it->second.size;
+ size_t related = it->second.relatedID;
+ MemoryManager::m_RegisteredGfxDriverMemory -= size;
+ g_ExternalAllocations->erase(it);
+#if ENABLE_MEM_PROFILER
+ GetMemoryProfiler()->UnregisterMemoryToID(related,size);
+#endif
+}
+
+
+typedef UNITY_MAP(kMemDefault,MemLabelIdentifier, BaseAllocator*) CustomAllocators;
+CustomAllocators* g_CustomAllocators = NULL;
+int nextCustomAllocatorIndex = (MemLabelIdentifier)0x1000;
+
+BaseAllocator* MemoryManager::GetAllocatorContainingPtr(const void* ptr)
+{
+ if(m_FrameTempAllocator && m_FrameTempAllocator->Contains(ptr))
+ return m_FrameTempAllocator;
+
+ for(int i = 0; i < m_NumAllocators ; i++)
+ {
+ if(m_Allocators[i]->IsAssigned() && m_Allocators[i]->Contains(ptr))
+ return m_Allocators[i];
+ }
+
+ if(m_InitialFallbackAllocator->Contains(ptr))
+ return m_InitialFallbackAllocator;
+
+ if(g_CustomAllocators)
+ {
+ CustomAllocators::iterator it = g_CustomAllocators->begin();
+ for(;it != g_CustomAllocators->end(); ++it)
+ if(it->second->Contains(ptr))
+ return it->second;
+ }
+ return NULL;
+}
+
+const char* MemoryManager::GetAllocatorName( int i )
+{
+ return i < m_NumAllocators ? m_Allocators[i]->GetName() : "Custom";
+}
+
+const char* MemoryManager::GetMemcatName( MemLabelRef label )
+{
+ return label.label < kMemLabelCount ? MemLabelName[label.label] : "Custom";
+}
+
+MemLabelId MemoryManager::AddCustomAllocator(BaseAllocator* allocator)
+{
+ Assert(allocator->Contains(NULL) == false); // to assure that Contains does not just return true
+ MemLabelIdentifier label =(MemLabelIdentifier)(nextCustomAllocatorIndex++);
+ if(!g_CustomAllocators)
+ g_CustomAllocators = UNITY_NEW(CustomAllocators,kMemDefault);
+ (*g_CustomAllocators)[label] = allocator;
+ return MemLabelId(label, NULL);
+}
+
+void MemoryManager::RemoveCustomAllocator(BaseAllocator* allocator)
+{
+ if(!g_CustomAllocators)
+ return;
+
+ for(CustomAllocators::iterator it = g_CustomAllocators->begin();
+ it != g_CustomAllocators->end();
+ ++it)
+ {
+ if (it->second == allocator)
+ {
+ g_CustomAllocators->erase(it);
+ break;
+ }
+ }
+ if(g_CustomAllocators->empty())
+ UNITY_DELETE(g_CustomAllocators, kMemDefault);
+}
+
+BaseAllocator* MemoryManager::GetAllocatorAtIndex( int index )
+{
+ return m_Allocators[index];
+}
+
+BaseAllocator* MemoryManager::GetAllocator( MemLabelRef label )
+{
+ DebugAssert(!IsTempAllocatorLabel(label));
+ if(label.label < kMemLabelCount)
+ {
+ BaseAllocator* alloc = m_AllocatorMap[label.label].alloc;
+ return alloc;
+ }
+ else if(label.label == kMemLabelCount)
+ return NULL;
+
+ if(g_CustomAllocators)
+ {
+ CustomAllocators::iterator it = g_CustomAllocators->find(label.label);
+ if(it != g_CustomAllocators->end())
+ return (*it).second;
+ }
+
+ return NULL;
+}
+
+int MemoryManager::GetAllocatorIndex( BaseAllocator* alloc )
+{
+ for(int i = 0; i < m_NumAllocators; i++)
+ if(alloc == m_Allocators[i])
+ return i;
+
+ return m_NumAllocators;
+}
+
+size_t MemoryManager::GetTotalAllocatedMemory()
+{
+ size_t total = m_FrameTempAllocator->GetAllocatedMemorySize();
+ for(int i = 0; i < m_NumAllocators ; i++)
+ total += m_Allocators[i]->GetAllocatedMemorySize();
+ return total;
+}
+
+size_t MemoryManager::GetTotalReservedMemory()
+{
+ size_t total = m_FrameTempAllocator->GetReservedSizeTotal();
+ for(int i = 0; i < m_NumAllocators ; i++)
+ total += m_Allocators[i]->GetReservedSizeTotal();
+ return total;
+}
+
+size_t MemoryManager::GetTotalUnusedReservedMemory()
+{
+ return GetTotalReservedMemory() - GetTotalAllocatedMemory();;
+}
+
+int MemoryManager::GetAllocatorCount( )
+{
+ return m_NumAllocators;
+}
+
+size_t MemoryManager::GetAllocatedMemory( MemLabelRef label )
+{
+ return m_AllocatorMap[label.label].allocatedMemory;
+}
+
+size_t MemoryManager::GetTotalProfilerMemory()
+{
+ return GetAllocator(kMemProfiler)->GetAllocatedMemorySize();
+}
+
+int MemoryManager::GetAllocCount( MemLabelRef label )
+{
+ return m_AllocatorMap[label.label].numAllocs;
+}
+
+size_t MemoryManager::GetLargestAlloc( MemLabelRef label )
+{
+ return m_AllocatorMap[label.label].largestAlloc;
+}
+
+void MemoryManager::StartLoggingAllocations(size_t logAllocationsThreshold)
+{
+ m_LogAllocations = true;
+ m_LogAllocationsThreshold = logAllocationsThreshold;
+};
+
+void MemoryManager::StopLoggingAllocations()
+{
+ m_LogAllocations = false;
+};
+
+#if ENABLE_MEM_PROFILER
+void MemoryManager::RegisterAllocation(void* ptr, size_t size, BaseAllocator* alloc, MemLabelRef label, const char* function, const char* file, int line)
+{
+ if (GetMemoryProfiler())
+ {
+ DebugAssert(!IsTempAllocatorLabel(label));
+ if(label.label < kMemLabelCount)
+ {
+ m_AllocatorMap[label.label].allocatedMemory += size;
+ m_AllocatorMap[label.label].numAllocs++;
+ m_AllocatorMap[label.label].largestAlloc = std::max(m_AllocatorMap[label.label].largestAlloc, size);
+ }
+ GetMemoryProfiler()->RegisterAllocation(ptr, label, file, line, size);
+ if (m_LogAllocations && size >= m_LogAllocationsThreshold)
+ {
+ size_t totalAllocatedMemoryAfterAllocation = GetTotalAllocatedMemory();
+ printf_console( "%s (%p): %11" PRINTF_SIZET_FORMAT "\tTotal: %.2fMB (%" PRINTF_SIZET_FORMAT ") in %s:%d\n",
+ function, ptr, size, totalAllocatedMemoryAfterAllocation / (1024.0f * 1024.0f), totalAllocatedMemoryAfterAllocation, file, line
+ );
+ }
+ }
+}
+
+ProfilerAllocationHeader* MemoryManager::RegisterDeallocation(void* ptr, BaseAllocator* alloc, MemLabelRef label, const char* function)
+{
+ ProfilerAllocationHeader* relatedHeader = NULL;
+ if (ptr != NULL && GetMemoryProfiler())
+ {
+ DebugAssert(!IsTempAllocatorLabel(label));
+ size_t oldsize = alloc->GetPtrSize(ptr);
+ GetMemoryProfiler()->UnregisterAllocation(ptr, alloc, oldsize, &relatedHeader, label);
+ if(label.label < kMemLabelCount)
+ {
+ m_AllocatorMap[label.label].allocatedMemory -= oldsize;
+ m_AllocatorMap[label.label].numAllocs--;
+ }
+
+ if (m_LogAllocations && oldsize >= m_LogAllocationsThreshold)
+ {
+ size_t totalAllocatedMemoryAfterDeallocation = GetTotalAllocatedMemory() - oldsize;
+ printf_console( "%s (%p): %11" PRINTF_SIZET_FORMAT "\tTotal: %.2fMB (%" PRINTF_SIZET_FORMAT ")\n",
+ function, ptr, -(int)oldsize, totalAllocatedMemoryAfterDeallocation / (1024.0f * 1024.0f), totalAllocatedMemoryAfterDeallocation);
+ }
+ }
+ return relatedHeader;
+}
+#endif
+
+void PrintShortMemoryStats(TEMP_STRING& str, MemLabelRef label)
+{
+#if ENABLE_MEMORY_MANAGER
+ MemoryManager& mm = GetMemoryManager();
+ str += "Memory overview\n\n";
+ for(int i = 0; i < mm.GetAllocatorCount() ; i++)
+ {
+ BaseAllocator* alloc = mm.GetAllocatorAtIndex(i);
+ if(alloc == NULL)
+ continue;
+ str += FormatString<TEMP_STRING>( "\n[ %s ] used: %" PRINTF_SIZET_FORMAT "B | peak: %" PRINTF_SIZET_FORMAT "B | reserved: %" PRINTF_SIZET_FORMAT "B \n",
+ alloc->GetName(), alloc->GetAllocatedMemorySize(), alloc->GetPeakAllocatedMemorySize(), alloc->GetReservedSizeTotal());
+ }
+#endif
+}
+
+#if UNITY_WIN && ENABLE_MEM_PROFILER
+
+// this is only here to catch stray mallocs - UNITY_MALLOC should be used instead
+// use tls to mark if we are in our own malloc or not. register stray mallocs.
+int catchMemoryAllocHook(int allocType, void *userData, size_t size, int blockType, long requestNumber, const unsigned char *filename, int lineNumber)
+{
+ if(!GetMemoryProfiler())
+ return TRUE;
+
+ if (allocType == _HOOK_FREE)
+ {
+#ifdef _DEBUG
+ int headersize = sizeof(_CrtMemBlockHeader);
+ _CrtMemBlockHeader* header = (_CrtMemBlockHeader*)((size_t) userData - headersize);
+ GetMemoryProfiler()->UnregisterAllocation(NULL, NULL, header->nDataSize, NULL, kMemDefault);
+#endif
+ }
+ else
+ GetMemoryProfiler()->RegisterAllocation(NULL, MemLabelId(kMemLabelCount, NULL), NULL, 0, size);
+ return TRUE;
+}
+
+#endif
+
+
+
+#if ENABLE_UNIT_TESTS
+
+#include "External/UnitTest++/src/UnitTest++.h"
+
+SUITE (AtomicOpsTests)
+{
+ struct AtomicOpsFixture
+ {
+ AtomicOpsFixture()
+ {
+ }
+ ~AtomicOpsFixture()
+ {
+ }
+ };
+
+ TEST_FIXTURE(AtomicOpsFixture, AtomicExchange)
+ {
+#if UNITY_WIN || UNITY_XENON || UNITY_ANDROID
+ int i = 2;
+ CHECK_EQUAL( 2, AtomicExchange( &i, 3 ) );
+#endif // UNITY_WIN || UNITY_XENON || UNITY_ANDROID
+ }
+
+ TEST_FIXTURE(AtomicOpsFixture, AtomicCompareExchange)
+ {
+ int i = 1;
+ CHECK_EQUAL( true, AtomicCompareExchange( &i, 2, 1 ) );
+ }
+
+ TEST_FIXTURE(AtomicOpsFixture, AtomicDecrement)
+ {
+ int i = 2;
+ CHECK_EQUAL( 1, AtomicDecrement( &i ) );
+ }
+
+ TEST_FIXTURE(AtomicOpsFixture, AtomicIncrement)
+ {
+ int i = 0;
+ CHECK_EQUAL( 1, AtomicIncrement( &i ) );
+ }
+
+ TEST_FIXTURE(AtomicOpsFixture, AtomicSub)
+ {
+ int i = 2;
+ CHECK_EQUAL( 1, AtomicSub( &i, 1 ) );
+ }
+
+ TEST_FIXTURE(AtomicOpsFixture, AtomicAdd)
+ {
+ int i = 1;
+ CHECK_EQUAL( 2, AtomicAdd( &i, 1 ) );
+ }
+}
+
+#if ENABLE_MEMORY_MANAGER
+#include "Runtime/Profiler/TimeHelper.h"
+SUITE (MemoryManagerTests)
+{
+ struct MemoryManagerFixture
+ {
+ MemoryManagerFixture()
+ {
+ }
+ ~MemoryManagerFixture()
+ {
+ }
+ };
+
+
+ struct VirtualTestStructA{
+ VirtualTestStructA() {ptrA = UNITY_MALLOC(kMemDefault,1024*1024);}
+ virtual ~VirtualTestStructA() {UNITY_FREE(kMemDefault,ptrA);}
+ void* ptrA;
+ };
+ struct VirtualTestStructB : public VirtualTestStructA{
+ VirtualTestStructB() {ptrB = UNITY_MALLOC(kMemDefault,1024*1024);}
+ virtual ~VirtualTestStructB() {UNITY_FREE(kMemDefault,ptrB);}
+ void* ptrB;
+ };
+
+
+ struct TestStruct{
+ TestStruct() {ptr = UNITY_MALLOC(kMemDefault,1024*1024);}
+ ~TestStruct(){UNITY_FREE(kMemDefault,ptr);}
+ void* ptr;
+ };
+
+ TEST_FIXTURE(MemoryManagerFixture, NewDelete)
+ {
+ size_t memoryBefore = GetMemoryManager().GetAllocator(kMemDefault)->GetAllocatedMemorySize();
+
+ TestStruct* test = UNITY_NEW(TestStruct, kMemDefault);
+ // less or equal, since some platforms have larger default alignment that sizeof(void*)
+ CHECK(memoryBefore + sizeof(TestStruct) + 1024*1024 <= GetMemoryManager().GetAllocator(kMemDefault)->GetAllocatedMemorySize());
+ UNITY_DELETE (test, kMemDefault);
+
+ CHECK_EQUAL(memoryBefore,GetMemoryManager().GetAllocator(kMemDefault)->GetAllocatedMemorySize());
+
+ VirtualTestStructA* testA = (VirtualTestStructA*)UNITY_NEW(VirtualTestStructB, kMemDefault);
+ UNITY_DELETE (testA, kMemDefault);
+
+ CHECK_EQUAL(memoryBefore, GetMemoryManager().GetAllocator(kMemDefault)->GetAllocatedMemorySize());
+
+ /* This will fail the tests because of theFatalError in out of mem - but good for testing the OOM message
+ void* outofmem = UNITY_MALLOC(kMemDefault,((size_t)-1) - 1024); // has to be room for headers etc
+ CHECK_EQUAL((int)outofmem, 0);
+ CHECK_EQUAL(memoryBefore, GetMemoryManager().GetAllocator(kMemDefault)->GetAllocatedMemorySize());
+ UNITY_FREE(kMemDefault, outofmem);*/
+ }
+
+ TEST_FIXTURE(MemoryManagerFixture, CanAllocate)
+ {
+ UnityDefaultAllocator<LowLevelAllocator>* testAlloc = new UnityDefaultAllocator<LowLevelAllocator>("TestAlloc");
+ MemLabelId testAllocLabel = GetMemoryManager().AddCustomAllocator(testAlloc);
+ void* ptr = GetMemoryManager().Allocate(1024, 1, testAllocLabel);
+
+ int requestedSize = testAlloc->GetAllocatedMemorySize();
+ int overhead = testAlloc->GetAllocatorSizeTotalUsed() - requestedSize;
+ int overheadSize = testAlloc->GetOverheadSize(ptr);
+
+ CHECK_EQUAL(1024, requestedSize);
+ int page4size = 1<<(8-StaticLog2<kDefaultMemoryAlignment>::value);
+ CHECK_EQUAL(overheadSize + (128+1 + 128+1 + 32+1 + page4size+1)*sizeof(int*), overhead);
+
+ GetMemoryManager().Deallocate(ptr);
+
+ requestedSize = testAlloc->GetAllocatedMemorySize();
+ overhead = testAlloc->GetAllocatorSizeTotalUsed() - requestedSize;
+
+ CHECK_EQUAL(0, requestedSize);
+ CHECK_EQUAL(0, overhead);
+
+ GetMemoryManager().RemoveCustomAllocator(testAlloc);
+ }
+
+ TEST_FIXTURE(MemoryManagerFixture, CanAllocateAligned)
+ {
+ BaseAllocator* testAlloc = new UnityDefaultAllocator<LowLevelAllocator>("TestAlloc");
+ MemLabelId testAllocLabel = GetMemoryManager().AddCustomAllocator(testAlloc);
+ for(int i = 0; i < 100; i++)
+ {
+ int size = 1024 + ((i*20457)&1023);
+ int align = 1<<(1+((i*3)&7));
+ void* ptr = GetMemoryManager().Allocate(size, align, testAllocLabel);
+ ((int*)ptr)[0] = 0x89ABCDEF;
+ int requestedSize = testAlloc->GetAllocatedMemorySize();
+
+ CHECK_EQUAL(size, requestedSize);
+ CHECK_EQUAL(0, ((int)ptr)&(align-1));
+
+ int newsize = 1024 + ((i*236047)&1023);
+ ptr = GetMemoryManager().Reallocate(ptr, newsize, align, testAllocLabel);
+
+ requestedSize = testAlloc->GetAllocatedMemorySize();
+
+ CHECK_EQUAL(0x89ABCDEF, ((int*)ptr)[0]);
+ CHECK_EQUAL(newsize, requestedSize);
+ CHECK_EQUAL(0, ((int)ptr)&(align-1));
+
+ GetMemoryManager().Deallocate(ptr);
+
+ requestedSize = testAlloc->GetAllocatedMemorySize();
+
+ CHECK_EQUAL(0, requestedSize);
+ }
+ int requestedSize = testAlloc->GetAllocatedMemorySize();
+ int overhead = testAlloc->GetAllocatorSizeTotalUsed() - requestedSize;
+
+ CHECK_EQUAL(0, requestedSize);
+ CHECK_EQUAL(0, overhead);
+
+ GetMemoryManager().RemoveCustomAllocator(testAlloc);
+ }
+
+ TEST_FIXTURE(MemoryManagerFixture, CanTempAllocate)
+ {
+ GetMemoryManager().FrameMaintenance();
+ void* fillptr = UNITY_MALLOC(kMemTempAlloc, 128);
+ for(int i = 0; i < 1000; i++)
+ {
+ void* ptr = UNITY_MALLOC_ALIGNED(kMemTempAlloc, 128, 16);
+ UNITY_FREE(kMemTempAlloc, ptr);
+ }
+
+ void** ptrArray = (void**)UNITY_MALLOC(kMemTempAlloc,256*sizeof(void*));
+ for(int i = 0; i < 256; i++)
+ {
+ ptrArray[i] = UNITY_MALLOC_ALIGNED(kMemTempAlloc,16*1024,32);
+ }
+
+ for(int i = 0; i < 256; i++)
+ {
+ UNITY_FREE(kMemTempAlloc, ptrArray[i]);
+ }
+ UNITY_FREE(kMemTempAlloc, fillptr);
+ UNITY_FREE(kMemTempAlloc, ptrArray);
+ GetMemoryManager().FrameMaintenance();
+ }
+
+
+ #if !UNITY_64 //@TODO: seems to use TLSF which is disabled in 64 bit?
+ TEST_FIXTURE(MemoryManagerFixture, DynamicHeapReallocate)
+ {
+ DynamicHeapAllocator<LowLevelAllocator>* testAlloc = new DynamicHeapAllocator<LowLevelAllocator>(100*1024,0,true,"TestAlloc");
+ MemLabelId testAllocLabel = GetMemoryManager().AddCustomAllocator(testAlloc);
+ void* ptr = GetMemoryManager().Allocate(1024, 1, testAllocLabel);
+
+ CHECK_EQUAL(100*1024, testAlloc->GetReservedSizeTotal());
+ CHECK_EQUAL(1024, testAlloc->GetAllocatedMemorySize());
+
+ void* ptr2 = UNITY_MALLOC( testAllocLabel, 50*1024 );
+ CHECK_EQUAL(100*1024, testAlloc->GetReservedSizeTotal());
+ memset(ptr2,0x3B,50*1024);
+ void* ptr3 = UNITY_REALLOC_( testAllocLabel, ptr2, 100*1024 );
+ CHECK_EQUAL(200*1024, testAlloc->GetReservedSizeTotal());
+ for(int i = 0; i < 1024;i++)
+ CHECK_EQUAL(0x3B,((char*)ptr3)[i]);
+ for(int i = 49*1024; i < 50*1024;i++)
+ CHECK_EQUAL(0x3B,((char*)ptr3)[i]);
+ memset(ptr3,0x4C,100*1024);
+ void* ptr4 = UNITY_REALLOC_( testAllocLabel, ptr3, 101*1024 );
+ CHECK_EQUAL(201*1024, testAlloc->GetReservedSizeTotal());
+ for(int i = 0; i < 1024;i++)
+ CHECK_EQUAL(0x4C,((char*)ptr4)[i]);
+ for(int i = 99*1024; i < 100*1024;i++)
+ CHECK_EQUAL(0x4C,((char*)ptr4)[i]);
+
+ CHECK_EQUAL(1024 + 101*1024, testAlloc->GetAllocatedMemorySize());
+
+ GetMemoryManager().Deallocate(ptr4);
+ GetMemoryManager().Deallocate(ptr);
+
+ // empty pools are freed, so no memory should be left
+ CHECK_EQUAL(0, testAlloc->GetReservedSizeTotal());
+ CHECK_EQUAL(0, testAlloc->GetAllocatedMemorySize());
+
+ GetMemoryManager().RemoveCustomAllocator(testAlloc);
+ }
+ #endif
+
+ /*
+ TEST_FIXTURE(MemoryManagerFixture, TempAllocatePerformance)
+ {
+ int allocSizes[16] = {1234,345,763,34768,343,2345,45643,85335,3453,7843,2346,437,3475,23789,423743,4537};
+ GetMemoryManager().FrameMaintenance();
+ {
+ ABSOLUTE_TIME start = START_TIME;
+ for(int j = 0; j < 100000; j++)
+ {
+ void* fillptr1 = UNITY_MALLOC(kMemTempAlloc, 128);
+ void* fillptr2 = UNITY_MALLOC(kMemTempAlloc, 54);
+ void* fillptr3 = UNITY_MALLOC(kMemTempAlloc, 158);
+ for(int i = 0; i < 1000; i++)
+ {
+ void* ptr = UNITY_MALLOC_ALIGNED(kMemTempAlloc, allocSizes[i%16], 16);
+ UNITY_FREE(kMemTempAlloc, ptr);
+ }
+ UNITY_FREE(kMemTempAlloc, fillptr1);
+ UNITY_FREE(kMemTempAlloc, fillptr2);
+ UNITY_FREE(kMemTempAlloc, fillptr3);
+ }
+ printf_console("Elapsed time for 1M Temp allocs %.5fs\n", GetElapsedTimeInSeconds (start)/100.f);
+ }
+ {
+ ABSOLUTE_TIME start = START_TIME;
+ for(int j = 0; j < 1000; j++)
+ {
+ void* fillptr1 = UNITY_MALLOC(kMemDefault, 128);
+ void* fillptr2 = UNITY_MALLOC(kMemDefault, 54);
+ void* fillptr3 = UNITY_MALLOC(kMemDefault, 158);
+ for(int i = 0; i < 1000; i++)
+ {
+ void* ptr = UNITY_MALLOC_ALIGNED(kMemDefault, allocSizes[i%16], 16);
+ UNITY_FREE(kMemDefault, ptr);
+ }
+ UNITY_FREE(kMemDefault, fillptr1);
+ UNITY_FREE(kMemDefault, fillptr2);
+ UNITY_FREE(kMemDefault, fillptr3);
+ }
+ printf_console("Elapsed time for 1M DynHeap allocs %.5f s\n", GetElapsedTimeInSeconds (start));
+ }
+ {
+ ABSOLUTE_TIME start = START_TIME;
+ for(int j = 0; j < 10; j++)
+ {
+ void* fillptr1 = malloc(128);
+ void* fillptr2 = malloc(154);
+ void* fillptr3 = malloc(158);
+ for(int i = 0; i < 1000; i++)
+ {
+ void* ptr = malloc(allocSizes[i%16]);
+ free(ptr);
+ }
+ free(fillptr1);
+ free(fillptr2);
+ free(fillptr3);
+ }
+ printf_console("Elapsed time for 1M malloc/free %.5f s\n", GetElapsedTimeInSeconds (start)*100.f);
+ }
+ GetMemoryManager().FrameMaintenance();
+ }
+ */
+
+}
+#endif
+#endif
+
+
+#else // !ENABLE_MEMORY_MANAGER
+
+void register_external_gfx_allocation(void* ptr, size_t size, size_t related, const char* file, int line) { }
+void register_external_gfx_deallocation(void* ptr, const char* file, int line) { }
+
+
+void* malloc_internal(size_t size, int align, MemLabelRef label, int allocateOptions, const char* file, int line)
+{
+ return UNITY_LL_ALLOC(label, size, align);
+}
+
+void* calloc_internal(size_t count, size_t size, int align, MemLabelRef label, int allocateOptions, const char* file, int line)
+{
+ void* ptr = UNITY_LL_ALLOC(label, size * count, align);
+ memset (ptr, 0, size * count);
+ return ptr;
+}
+
+void* realloc_internal(void* ptr, size_t size, int align, MemLabelRef label, int allocateOptions, const char* file, int line)
+{
+ return UNITY_LL_REALLOC(label, ptr, size, align);
+}
+
+void free_alloc_internal(void* ptr, MemLabelRef label)
+{
+ UNITY_LL_FREE(label, ptr);
+}
+
+#if !((UNITY_OSX || UNITY_LINUX) && UNITY_EDITOR)
+void* operator new (size_t size) THROWING_NEW_THROW { return UNITY_LL_ALLOC (kMemNewDelete, size, kDefaultMemoryAlignment); }
+void* operator new [] (size_t size) THROWING_NEW_THROW { return UNITY_LL_ALLOC (kMemNewDelete, size, kDefaultMemoryAlignment); }
+void operator delete (void* p) throw() { UNITY_LL_FREE (kMemNewDelete, p); } // can't make allocator assumption, since ptr can be newed by other operator new
+void operator delete [] (void* p) throw() {UNITY_LL_FREE (kMemNewDelete, p); }
+
+void* operator new (size_t size, const std::nothrow_t&) throw() { return UNITY_LL_ALLOC (kMemNewDelete, size, kDefaultMemoryAlignment); }
+void* operator new [] (size_t size, const std::nothrow_t&) throw() { return UNITY_LL_ALLOC (kMemNewDelete, size, kDefaultMemoryAlignment); };
+void operator delete (void* p, const std::nothrow_t&) throw() { UNITY_LL_FREE (kMemNewDelete, p); }
+void operator delete [] (void* p, const std::nothrow_t&) throw() { UNITY_LL_FREE (kMemNewDelete, p); }
+#endif
+
+#endif
+
diff --git a/Runtime/Allocator/MemoryManager.h b/Runtime/Allocator/MemoryManager.h
new file mode 100644
index 0000000..b37292c
--- /dev/null
+++ b/Runtime/Allocator/MemoryManager.h
@@ -0,0 +1,118 @@
+#ifndef _MEMORY_MANAGER_H_
+#define _MEMORY_MANAGER_H_
+
+#include "Runtime/Misc/AllocatorLabels.h"
+#include "Runtime/Allocator/BaseAllocator.h"
+#include "Runtime/Utilities/FileStripped.h"
+
+#if UNITY_XENON
+#include "PlatformDependent/Xbox360/Source/XenonMemory.h"
+#endif
+
+#if ENABLE_MEMORY_MANAGER
+
+class MemoryManager
+{
+public:
+ MemoryManager();
+ ~MemoryManager();
+ static void StaticInitialize();
+ static void StaticDestroy();
+
+ void ThreadInitialize(size_t tempSize = 0);
+ void ThreadCleanup();
+
+ bool IsInitialized() {return m_IsInitialized;}
+ bool IsActive() {return m_IsActive;}
+
+ void* Allocate(size_t size, int align, MemLabelRef label, int allocateOptions = kAllocateOptionNone, const char* file = NULL, int line = 0);
+ void* Reallocate(void* ptr, size_t size, int align, MemLabelRef label, int allocateOptions = kAllocateOptionNone, const char* file = NULL, int line = 0);
+ void Deallocate(void* ptr);
+ void Deallocate(void* ptr, MemLabelRef label);
+
+ BaseAllocator* GetAllocator(MemLabelRef label);
+ int GetAllocatorIndex(BaseAllocator* alloc);
+ BaseAllocator* GetAllocatorAtIndex( int index );
+
+ MemLabelId AddCustomAllocator(BaseAllocator* allocator);
+ void RemoveCustomAllocator(BaseAllocator* allocator);
+
+ void FrameMaintenance(bool cleanup = false);
+
+ static void* LowLevelAllocate( size_t size );
+ static void* LowLevelCAllocate( size_t count, size_t size );
+ static void* LowLevelReallocate( void* p, size_t size );
+ static void LowLevelFree( void* p );
+
+ const char* GetAllocatorName( int i );
+ const char* GetMemcatName( MemLabelRef label );
+
+ size_t GetTotalAllocatedMemory();
+ size_t GetTotalUnusedReservedMemory();
+ size_t GetTotalReservedMemory();
+
+ size_t GetTotalProfilerMemory();
+
+ int GetAllocatorCount( );
+
+ size_t GetAllocatedMemory( MemLabelRef label );
+ int GetAllocCount( MemLabelRef label );
+ size_t GetLargestAlloc(MemLabelRef label);
+
+#if UNITY_XENON
+ size_t GetRegisteredGFXDriverMemory(){ return xenon::GetGfxMemoryAllocated();}
+#else
+ size_t GetRegisteredGFXDriverMemory(){ return m_RegisteredGfxDriverMemory;}
+#endif
+
+ void StartLoggingAllocations(size_t logAllocationsThreshold = 0);
+ void StopLoggingAllocations();
+
+ void DisallowAllocationsOnThisThread();
+ void ReallowAllocationsOnThisThread();
+ void CheckDisalowAllocation();
+
+ BaseAllocator* GetAllocatorContainingPtr(const void* ptr);
+
+ static inline bool IsTempAllocatorLabel( MemLabelRef label ) { return label.label == kMemTempAllocId; }
+
+ volatile static long m_LowLevelAllocated;
+ volatile static long m_RegisteredGfxDriverMemory;
+
+private:
+#if ENABLE_MEM_PROFILER
+ void RegisterAllocation(void* ptr, size_t size, BaseAllocator* alloc, MemLabelRef label, const char* function, const char* file, int line);
+ ProfilerAllocationHeader* RegisterDeallocation(void* ptr, BaseAllocator* alloc, MemLabelRef label, const char* function);
+#endif
+ void InitializeMainThreadAllocators();
+
+ static const int kMaxAllocators = 16;
+
+ BaseAllocator* m_FrameTempAllocator;
+ BaseAllocator* m_InitialFallbackAllocator;
+
+ BaseAllocator* m_Allocators[kMaxAllocators];
+ BaseAllocator* m_MainAllocators[kMaxAllocators];
+ BaseAllocator* m_ThreadAllocators[kMaxAllocators];
+
+ int m_NumAllocators;
+ bool m_LogAllocations;
+ bool m_IsInitialized;
+ bool m_IsActive;
+
+
+ size_t m_LogAllocationsThreshold;
+
+ struct LabelInfo
+ {
+ BaseAllocator* alloc;
+ size_t allocatedMemory;
+ int numAllocs;
+ size_t largestAlloc;
+ };
+ LabelInfo m_AllocatorMap[kMemLabelCount];
+};
+MemoryManager& GetMemoryManager();
+
+#endif
+#endif
diff --git a/Runtime/Allocator/STLAllocator.h b/Runtime/Allocator/STLAllocator.h
new file mode 100644
index 0000000..e0a6dd9
--- /dev/null
+++ b/Runtime/Allocator/STLAllocator.h
@@ -0,0 +1,146 @@
+#ifndef STL_ALLOCATOR_H_
+#define STL_ALLOCATOR_H_
+
+#include "Runtime/Allocator/BaseAllocator.h"
+#include "Runtime/Misc/AllocatorLabels.h"
+
+// Use STL_ALLOCATOR macro when declaring custom std::containers
+#define STL_ALLOCATOR(label, type) stl_allocator<type, label##Id>
+#define STL_ALLOCATOR_ALIGNED(label, type, align) stl_allocator<type, label##Id, align>
+
+#if UNITY_EXTERNAL_TOOL
+#define TEMP_STRING std::string
+#else
+#define TEMP_STRING std::basic_string<char, std::char_traits<char>, STL_ALLOCATOR(kMemTempAlloc, char) >
+#endif
+
+#define UNITY_STRING(label) std::basic_string<char, std::char_traits<char>, STL_ALLOCATOR(label, char) >
+#define UNITY_WSTRING(label) std::basic_string<wchar_t, std::char_traits<wchar_t>, STL_ALLOCATOR(label, wchar_t) >
+
+#define UNITY_LIST(label, type) std::list<type, STL_ALLOCATOR(label, type) >
+
+#define UNITY_SET(label, type) std::set<type, std::less<type>, STL_ALLOCATOR(label, type) >
+
+#define UNITY_MAP(label, key, value) std::map<key, value, std::less<key>, stl_allocator< std::pair< key const, value>, label##Id > >
+
+#define UNITY_VECTOR(label, type) std::vector<type, STL_ALLOCATOR(label, type) >
+#define UNITY_VECTOR_ALIGNED(label, type, align) std::vector<type, STL_ALLOCATOR_ALIGNED(label, type, align) >
+
+#define UNITY_TEMP_VECTOR(type) std::vector<type, STL_ALLOCATOR(kMemTempAlloc, type) >
+
+
+template<typename T, MemLabelIdentifier memlabel = kMemSTLId, int align = kDefaultMemoryAlignment >
+class stl_allocator
+{
+public:
+ typedef size_t size_type;
+ typedef ptrdiff_t difference_type;
+ typedef T* pointer;
+ typedef const T* const_pointer;
+ typedef T& reference;
+ typedef const T& const_reference;
+ typedef T value_type;
+
+#if ENABLE_MEM_PROFILER
+ AllocationRootReference* rootref;
+ ProfilerAllocationHeader* get_root_header() const { return get_root_header_from_reference(rootref); }
+#else
+ ProfilerAllocationHeader* get_root_header () const { return NULL; }
+#endif
+ template <typename U> struct rebind { typedef stl_allocator<U, memlabel, align> other; };
+
+ stl_allocator ()
+ {
+ IF_MEMORY_PROFILER_ENABLED( rootref = get_root_reference_from_header(GET_CURRENT_ALLOC_ROOT_HEADER()) );
+ }
+ stl_allocator (const stl_allocator& alloc) throw()
+ {
+ IF_MEMORY_PROFILER_ENABLED( rootref = copy_root_reference(alloc.rootref) );
+ }
+ template <typename U, MemLabelIdentifier _memlabel, int _align> stl_allocator (const stl_allocator<U, _memlabel, _align>& alloc) throw()
+ {
+ IF_MEMORY_PROFILER_ENABLED( rootref = copy_root_reference(alloc.rootref) );
+ }
+ ~stl_allocator () throw()
+ {
+ IF_MEMORY_PROFILER_ENABLED( release_root_reference(rootref) );
+ }
+
+ pointer address (reference x) const { return &x; }
+ const_pointer address (const_reference x) const { return &x; }
+
+ pointer allocate (size_type count, void const* /*hint*/ = 0)
+ {
+ return (pointer)UNITY_MALLOC_ALIGNED( MemLabelId(memlabel, get_root_header()), count * sizeof(T), align);
+ }
+ void deallocate (pointer p, size_type /*n*/)
+ {
+ UNITY_FREE(MemLabelId(memlabel, get_root_header()), p);
+ }
+
+ template <typename U, MemLabelIdentifier _memlabel, int _align>
+ bool operator== (stl_allocator<U, _memlabel, _align> const& a) const { return _memlabel == memlabel IF_MEMORY_PROFILER_ENABLED( && get_root_header() == a.get_root_header()); }
+ template <typename U, MemLabelIdentifier _memlabel, int _align>
+ bool operator!= (stl_allocator<U, _memlabel, _align> const& a) const { return _memlabel != memlabel IF_MEMORY_PROFILER_ENABLED( || get_root_header() != a.get_root_header()); }
+
+ size_type max_size () const throw() { return 0x7fffffff; }
+
+ void construct (pointer p, const T& val) { new (p) T(val); }
+
+ void destroy (pointer p) { p->~T(); }
+};
+
+#if !UNITY_EXTERNAL_TOOL && UNITY_WIN && !WEBPLUG
+#define string mystlstring
+#define wstring mystlwstring
+#include <string>
+#undef string
+#undef wstring
+
+namespace std{
+ typedef UNITY_STRING(kMemString) string;
+ typedef UNITY_WSTRING(kMemString) wstring;
+}
+#else
+#include <string>
+#endif
+
+#define UNITY_STR_IMPL(StringName,label) \
+class StringName : public UNITY_STRING(label) \
+{ \
+public: \
+ StringName():UNITY_STRING(label)(){} \
+ StringName(const char* str):UNITY_STRING(label)(str){} \
+ StringName(const char* str, int length):UNITY_STRING(label)(str,length){} \
+ StringName(const StringName& str):UNITY_STRING(label)(str.c_str(),str.length()){} \
+ template<typename alloc> \
+ StringName(const std::basic_string<char, std::char_traits<char>, alloc >& str):UNITY_STRING(label)(str.c_str(),str.length()){} \
+ template<typename alloc> \
+ operator std::basic_string<char, std::char_traits<char>, alloc > () const \
+ { \
+ return std::basic_string<char, std::char_traits<char>, alloc >(this->c_str(), this->length()); \
+ } \
+ template<typename alloc> \
+ StringName& operator=(const std::basic_string<char, std::char_traits<char>, alloc >& rhs) \
+ { \
+ assign(rhs.c_str(), rhs.length()); \
+ return *this; \
+ } \
+ template<typename alloc> \
+ bool operator==(const std::basic_string<char, std::char_traits<char>, alloc >& rhs) const \
+ { \
+ return length() == rhs.length() && strncmp(c_str(), rhs.c_str(), length()) == 0; \
+ } \
+ template<typename alloc> \
+ bool operator!=(const std::basic_string<char, std::char_traits<char>, alloc >& rhs) const \
+ { \
+ return length() != rhs.length() || strncmp(c_str(), rhs.c_str(), length()) != 0; \
+ } \
+};
+
+UNITY_STR_IMPL(UnityStr, kMemString);
+UNITY_STR_IMPL(StaticString, kMemStaticString);
+
+#define ProfilerString UNITY_STRING(kMemMemoryProfilerString)
+
+#endif
diff --git a/Runtime/Allocator/StackAllocator.cpp b/Runtime/Allocator/StackAllocator.cpp
new file mode 100644
index 0000000..ca874c0
--- /dev/null
+++ b/Runtime/Allocator/StackAllocator.cpp
@@ -0,0 +1,213 @@
+#include "UnityPrefix.h"
+#include "StackAllocator.h"
+
+#include "Runtime/Allocator/MemoryManager.h"
+#include "Runtime/Profiler/MemoryProfiler.h"
+
+StackAllocator::StackAllocator(int blockSize, const char* name)
+: BaseAllocator(name)
+, m_Block(NULL)
+, m_LastAlloc(NULL)
+, m_BlockSize(blockSize)
+{
+ #if ENABLE_MEMORY_MANAGER
+ m_Block = (char*)MemoryManager::LowLevelAllocate(m_BlockSize);
+ #else
+ m_Block = (char*)malloc(m_BlockSize);
+ #endif
+
+ m_TotalReservedMemory = blockSize;
+}
+
+StackAllocator::~StackAllocator()
+{
+ while(m_LastAlloc)
+ Deallocate(m_LastAlloc);
+
+ #if ENABLE_MEMORY_MANAGER
+ MemoryManager::LowLevelFree(m_Block);
+ #else
+ free(m_Block);
+ #endif
+}
+
+void* StackAllocator::Allocate (size_t size, int align)
+{
+ //1 byte alignment doesn't work for webgl; this is a fix(ish)....
+
+#if UNITY_WEBGL || UNITY_BB10
+ if(align % 8 != 0)
+ align = 8;
+#endif
+
+ size_t alignmask = align - 1;
+
+ // make header size a multiple
+ int alignedHeaderSize = (GetHeaderSize() + alignmask) & ~alignmask;
+ int paddedSize = (size + alignedHeaderSize + alignmask) & ~alignmask;
+
+ char* realPtr;
+
+ char* freePtr = (char*)AlignPtr(GetBufferFreePtr(), align);
+ size_t freeSize = m_BlockSize - (int)(freePtr - m_Block);
+
+ if ( InBlock(freePtr) && paddedSize < freeSize )
+ {
+ realPtr = freePtr;
+ }
+ else
+ {
+ // Spilled over. We have to allocate the memory default alloc
+ realPtr = (char*)UNITY_MALLOC_ALIGNED(kMemTempOverflow, paddedSize, align);
+ }
+ if(realPtr == NULL)
+ return NULL;
+
+ char* ptr = realPtr + alignedHeaderSize;
+ Header* h = ( (Header*)ptr )-1;
+ h->prevPtr = m_LastAlloc;
+ h->size = size;
+ h->deleted = 0;
+ h->realPtr = realPtr;
+ m_LastAlloc = ptr;
+
+ return ptr;
+}
+
+void* StackAllocator::Reallocate (void* p, size_t size, int align)
+{
+ if (p == NULL)
+ return Allocate(size, align);
+
+ char* freePtr = (char*)AlignPtr(GetBufferFreePtr(), align);
+ size_t freeSize = m_BlockSize - (int)(freePtr - m_Block);
+ size_t oldSize = GetPtrSize(p);
+
+ if ((p == m_LastAlloc || oldSize >= size) && InBlock(p)
+ && AlignPtr(p,align) == p
+ && oldSize + freeSize > size)
+ {
+ // just expand the top allocation of the stack to the realloc amount
+ Header* h = ( (Header*)p )-1;
+ h->size = size;
+ return p;
+ }
+ void* newPtr = NULL;
+ if (!InBlock(p))
+ {
+ size_t alignmask = align - 1;
+ int alignedHeaderSize = (GetHeaderSize() + alignmask) & ~alignmask;
+ int paddedSize = (size + alignedHeaderSize + alignmask) & ~alignmask;
+
+ char* realPtr = (char*)UNITY_REALLOC_ALIGNED(kMemTempOverflow, GetRealPtr(p), paddedSize, align);
+ if(realPtr == NULL)
+ return NULL;
+
+ newPtr = realPtr + alignedHeaderSize;
+ Header* h = ( (Header*)newPtr ) - 1;
+ h->size = size;
+ h->deleted = 0;
+ h->realPtr = realPtr;
+
+ if(m_LastAlloc == p)
+ m_LastAlloc = (char*)newPtr;
+ else
+ UpdateNextHeader(p, newPtr);
+ }
+ else
+ {
+ newPtr = Allocate(size, align);
+ if(newPtr != NULL)
+ memcpy(newPtr, p, std::min(size, oldSize));
+ Deallocate(p);
+ }
+ return newPtr;
+}
+
+void StackAllocator::Deallocate (void* p)
+{
+ if (p == m_LastAlloc){
+ m_LastAlloc = GetPrevAlloc(p);
+ if ( !InBlock(p) )
+ {
+ UNITY_FREE(kMemTempOverflow, GetRealPtr(p));
+ }
+
+ if (IsDeleted(m_LastAlloc))
+ Deallocate(m_LastAlloc);
+ }
+ else
+ {
+ SetDeleted(p);
+ }
+}
+
+bool StackAllocator::ContainsInternal (const void* p)
+{
+ // if no temp allocations (should hit here most often)
+ if (m_LastAlloc == NULL)
+ return false;
+
+ // test inblock
+ if (InBlock(p))
+ return true;
+
+ // test overflow allocations (should almost never happen)
+ void* ptr = m_LastAlloc;
+ while (ptr != NULL && !InBlock(ptr))
+ {
+ if (p == ptr)
+ return true;
+ ptr = GetPrevAlloc(ptr);
+ }
+ return false;
+}
+
+void StackAllocator::UpdateNextHeader(void* before, void* after)
+{
+ if (before == m_LastAlloc)
+ return;
+ void* ptr = m_LastAlloc;
+ while (ptr != NULL && !InBlock(ptr))
+ {
+ void* prevAlloc = GetPrevAlloc(ptr);
+ if (before == prevAlloc)
+ {
+ Header* h = ((Header*)ptr)-1;
+ h->prevPtr = (char*)after;
+ return;
+ }
+ ptr = prevAlloc;
+ }
+ FatalErrorString("Allocation no found in temp allocation list");
+}
+
+
+size_t StackAllocator::GetAllocatedMemorySize() const
+{
+ int total = 0;
+ void* ptr = m_LastAlloc;
+ while (ptr != NULL)
+ {
+ total += GetPtrSize(ptr);
+ ptr = GetPrevAlloc(ptr);
+ }
+ return total;
+}
+
+size_t StackAllocator::GetAllocatorSizeTotalUsed() const
+{
+ int total = 0;
+ void* ptr = m_LastAlloc;
+ while (ptr != NULL)
+ {
+ total += GetPtrSize(ptr)+GetHeaderSize();
+ ptr = GetPrevAlloc(ptr);
+ }
+ return total;
+}
+
+size_t StackAllocator::GetReservedSizeTotal() const
+{
+ return m_TotalReservedMemory;
+}
diff --git a/Runtime/Allocator/StackAllocator.h b/Runtime/Allocator/StackAllocator.h
new file mode 100644
index 0000000..96860f5
--- /dev/null
+++ b/Runtime/Allocator/StackAllocator.h
@@ -0,0 +1,114 @@
+#ifndef STACK_ALLOCATOR_H_
+#define STACK_ALLOCATOR_H_
+
+#include "BaseAllocator.h"
+
+class StackAllocator : public BaseAllocator
+{
+public:
+ StackAllocator(int blocksize, const char* name);
+ virtual ~StackAllocator ();
+
+ virtual void* Allocate (size_t size, int align);
+ virtual void* Reallocate (void* p, size_t size, int align);
+ virtual void Deallocate (void* p);
+ virtual bool Contains (const void* p);
+
+ virtual size_t GetPtrSize(const void* ptr) const;
+
+ virtual size_t GetAllocatedMemorySize() const;
+ virtual size_t GetAllocatorSizeTotalUsed() const;
+ virtual size_t GetReservedSizeTotal() const;
+
+private:
+ struct Header{
+ int deleted:1;
+ int size:31;
+ char* prevPtr;
+ void* realPtr;
+ };
+ char* m_Block;
+ int m_BlockSize;
+
+ char* m_LastAlloc;
+
+ //ThreadID owningThread;
+ bool InBlock ( const void* ptr ) const;
+ bool IsDeleted ( const void* ptr ) const;
+ void SetDeleted ( const void* ptr );
+ char* GetPrevAlloc ( const void* ptr ) const;
+ void* GetRealPtr( void* ptr ) const;
+ char* GetBufferFreePtr() const;
+
+ bool ContainsInternal (const void* p);
+
+ UInt32 GetHeaderSize() const;
+
+ void UpdateNextHeader(void* before, void* after);
+};
+
+inline bool StackAllocator::Contains (const void* p)
+{
+ // most common case. pointer being queried is the one about to be destroyed
+ if(p != NULL && p == m_LastAlloc)
+ return true;
+
+ return ContainsInternal(p);
+}
+
+inline char* StackAllocator::GetBufferFreePtr() const
+{
+ if (m_LastAlloc == NULL)
+ return m_Block;
+ Header* h = ((Header*)m_LastAlloc)-1;
+ return m_LastAlloc + h->size;
+}
+
+inline size_t StackAllocator::GetPtrSize( const void* ptr ) const
+{
+ Header* header = ( (Header*)ptr )-1;
+ return header->size;
+}
+
+inline void* StackAllocator::GetRealPtr( void* ptr ) const
+{
+ Header* header = ( (Header*)ptr )-1;
+ return header->realPtr;
+}
+
+inline char* StackAllocator::GetPrevAlloc(const void* ptr ) const
+{
+ if (ptr == NULL)
+ return NULL;
+ Header* h = ((Header*)ptr)-1;
+ return h->prevPtr;
+}
+
+inline UInt32 StackAllocator::GetHeaderSize() const
+{
+ return sizeof(Header);
+}
+
+inline bool StackAllocator::InBlock(const void* ptr) const
+{
+ return ptr >= m_Block && ptr < (m_Block + m_BlockSize);
+}
+
+inline bool StackAllocator::IsDeleted(const void* ptr ) const
+{
+ if (ptr == NULL)
+ return false;
+ Header* h = ((Header*)ptr)-1;
+ return h->deleted != 0;
+}
+
+inline void StackAllocator::SetDeleted(const void* ptr )
+{
+ if (ptr == NULL)
+ return;
+ Header* h = ((Header*)ptr)-1;
+ h->deleted = 1;
+}
+
+
+#endif
diff --git a/Runtime/Allocator/TLSAllocator.cpp b/Runtime/Allocator/TLSAllocator.cpp
new file mode 100644
index 0000000..00bf88b
--- /dev/null
+++ b/Runtime/Allocator/TLSAllocator.cpp
@@ -0,0 +1,198 @@
+#include "UnityPrefix.h"
+#include "TLSAllocator.h"
+#include "Runtime/Threads/Thread.h"
+#include "Runtime/Allocator/StackAllocator.h"
+
+#if ENABLE_MEMORY_MANAGER
+
+template <class UnderlyingAllocator>
+int TLSAllocator<UnderlyingAllocator>::s_NumberOfInstances = 0;
+
+template <class UnderlyingAllocator>
+UNITY_TLS_VALUE(UnderlyingAllocator*) TLSAllocator<UnderlyingAllocator>::m_UniqueThreadAllocator;
+
+template <class UnderlyingAllocator>
+TLSAllocator<UnderlyingAllocator>::TLSAllocator(const char* name)
+: BaseAllocator(name)
+{
+ if(s_NumberOfInstances != 0)
+ ErrorString("Only one instance of the TLS allocator is allowed because of TLS implementation");
+ s_NumberOfInstances++;
+ memset (m_ThreadTempAllocators, 0, sizeof(m_ThreadTempAllocators));
+}
+
+template <class UnderlyingAllocator>
+TLSAllocator<UnderlyingAllocator>::~TLSAllocator()
+{
+ s_NumberOfInstances--;
+}
+
+template <class UnderlyingAllocator>
+void TLSAllocator<UnderlyingAllocator>::ThreadInitialize(BaseAllocator *allocator)
+{
+ m_UniqueThreadAllocator = (UnderlyingAllocator*)allocator;
+
+ for(int i = 0; i < kMaxThreadTempAllocators; i++)
+ {
+ if(m_ThreadTempAllocators[i] == NULL)
+ {
+ m_ThreadTempAllocators[i] = (UnderlyingAllocator*) allocator;
+ break;
+ }
+ }
+
+}
+
+template <class UnderlyingAllocator>
+void TLSAllocator<UnderlyingAllocator>::ThreadCleanup()
+{
+ UnderlyingAllocator* allocator = m_UniqueThreadAllocator;
+ m_UniqueThreadAllocator = NULL;
+
+ for(int i = 0; i < kMaxThreadTempAllocators; i++)
+ {
+ if(m_ThreadTempAllocators[i] == allocator)
+ {
+ m_ThreadTempAllocators[i] = NULL;
+ break;
+ }
+ }
+ UNITY_DELETE(allocator, kMemManager);
+}
+
+template <class UnderlyingAllocator>
+void TLSAllocator<UnderlyingAllocator>::FrameMaintenance(bool cleanup)
+{
+ Assert(m_UniqueThreadAllocator->GetAllocatedMemorySize() == 0);
+}
+
+template <class UnderlyingAllocator>
+bool TLSAllocator<UnderlyingAllocator>::IsAssigned() const
+{
+ return m_UniqueThreadAllocator != NULL;
+}
+
+template <class UnderlyingAllocator>
+UnderlyingAllocator* TLSAllocator<UnderlyingAllocator>::GetCurrentAllocator()
+{
+ return m_UniqueThreadAllocator;
+}
+
+
+template <class UnderlyingAllocator>
+void* TLSAllocator<UnderlyingAllocator>::Allocate( size_t size, int align )
+{
+ UnderlyingAllocator* alloc = GetCurrentAllocator();
+ return alloc ? alloc->UnderlyingAllocator::Allocate(size, align) : NULL;
+}
+
+template <class UnderlyingAllocator>
+void* TLSAllocator<UnderlyingAllocator>::Reallocate( void* p, size_t size, int align )
+{
+ UnderlyingAllocator* alloc = GetCurrentAllocator();
+ if(!alloc)
+ return NULL;
+ if(alloc->UnderlyingAllocator::Contains(p))
+ return alloc->UnderlyingAllocator::Reallocate(p, size, align);
+
+ return NULL;
+}
+
+template <class UnderlyingAllocator>
+void TLSAllocator<UnderlyingAllocator>::Deallocate( void* p )
+{
+ UnderlyingAllocator* alloc = GetCurrentAllocator();
+ DebugAssert(alloc);
+ DebugAssert(alloc->UnderlyingAllocator::Contains(p));
+ return alloc->UnderlyingAllocator::Deallocate(p);
+}
+
+template <class UnderlyingAllocator>
+bool TLSAllocator<UnderlyingAllocator>::TryDeallocate( void* p )
+{
+ UnderlyingAllocator* alloc = GetCurrentAllocator();
+ if(!alloc)
+ return false;
+
+ if(!alloc->UnderlyingAllocator::Contains(p))
+ return false;
+
+ alloc->UnderlyingAllocator::Deallocate(p);
+ return true;
+}
+
+
+template <class UnderlyingAllocator>
+bool TLSAllocator<UnderlyingAllocator>::Contains( const void* p )
+{
+ UnderlyingAllocator* alloc = GetCurrentAllocator();
+ if(alloc && alloc->UnderlyingAllocator::Contains(p))
+ return true;
+ return false;
+}
+
+template <class UnderlyingAllocator>
+size_t TLSAllocator<UnderlyingAllocator>::GetAllocatedMemorySize( ) const
+{
+ size_t allocated = 0;
+ for(int i = 0; i < kMaxThreadTempAllocators; i++)
+ {
+ if(m_ThreadTempAllocators[i] != NULL)
+ allocated += m_ThreadTempAllocators[i]->UnderlyingAllocator::GetAllocatedMemorySize();
+ }
+ return allocated;
+}
+
+template <class UnderlyingAllocator>
+size_t TLSAllocator<UnderlyingAllocator>::GetAllocatorSizeTotalUsed() const
+{
+ size_t total = 0;
+ for(int i = 0; i < kMaxThreadTempAllocators; i++)
+ {
+ if(m_ThreadTempAllocators[i] != NULL)
+ total += m_ThreadTempAllocators[i]->UnderlyingAllocator::GetAllocatorSizeTotalUsed();
+ }
+ return total;
+}
+
+template <class UnderlyingAllocator>
+size_t TLSAllocator<UnderlyingAllocator>::GetReservedSizeTotal() const
+{
+ size_t total = 0;
+ for(int i = 0; i < kMaxThreadTempAllocators; i++)
+ {
+ if(m_ThreadTempAllocators[i] != NULL)
+ total += m_ThreadTempAllocators[i]->UnderlyingAllocator::GetReservedSizeTotal();
+ }
+ return total;
+}
+
+template <class UnderlyingAllocator>
+size_t TLSAllocator<UnderlyingAllocator>::GetPtrSize( const void* ptr ) const
+{
+ // all allocators have the same allocation header
+ return m_ThreadTempAllocators[0]->UnderlyingAllocator::GetPtrSize(ptr);
+}
+
+template <class UnderlyingAllocator>
+ProfilerAllocationHeader* TLSAllocator<UnderlyingAllocator>::GetProfilerHeader( const void* ptr ) const
+{
+ return m_ThreadTempAllocators[0]->UnderlyingAllocator::GetProfilerHeader(ptr);
+}
+
+
+template <class UnderlyingAllocator>
+bool TLSAllocator<UnderlyingAllocator>::CheckIntegrity()
+{
+ bool succes = true;
+ for(int i = 0; i < kMaxThreadTempAllocators; i++)
+ {
+ if(m_ThreadTempAllocators[i] != NULL)
+ succes &= m_ThreadTempAllocators[i]->UnderlyingAllocator::CheckIntegrity();
+ }
+ return succes;
+}
+
+template class TLSAllocator< StackAllocator >;
+
+#endif // #if ENABLE_MEMORY_MANAGER
diff --git a/Runtime/Allocator/TLSAllocator.h b/Runtime/Allocator/TLSAllocator.h
new file mode 100644
index 0000000..a7520ff
--- /dev/null
+++ b/Runtime/Allocator/TLSAllocator.h
@@ -0,0 +1,55 @@
+#ifndef TLS_ALLOCATOR_H_
+#define TLS_ALLOCATOR_H_
+
+#if ENABLE_MEMORY_MANAGER
+
+#include "Runtime/Allocator/BaseAllocator.h"
+#include "Runtime/Threads/ThreadSpecificValue.h"
+
+
+// TLS Allocator is an indirection to a real allocator
+// Has a tls value pointing to the threadspecific allocator if unique per thread.
+
+template <class UnderlyingAllocator>
+class TLSAllocator : public BaseAllocator
+{
+public:
+ // when constructing it will be from the main thread
+ TLSAllocator(const char* name);
+ virtual ~TLSAllocator();
+
+ virtual void* Allocate(size_t size, int align);
+ virtual void* Reallocate (void* p, size_t size, int align);
+ virtual void Deallocate (void* p);
+ virtual bool Contains (const void* p);
+
+ virtual size_t GetAllocatedMemorySize() const;
+ virtual size_t GetAllocatorSizeTotalUsed() const;
+ virtual size_t GetReservedSizeTotal() const;
+
+ virtual size_t GetPtrSize(const void* ptr) const;
+ virtual ProfilerAllocationHeader* GetProfilerHeader(const void* ptr) const;
+
+ virtual void ThreadInitialize(BaseAllocator* allocator);
+ virtual void ThreadCleanup();
+
+ virtual bool CheckIntegrity();
+
+ virtual bool IsAssigned() const;
+ bool TryDeallocate (void* p);
+
+ UnderlyingAllocator* GetCurrentAllocator();
+ virtual void FrameMaintenance(bool cleanup);
+
+private:
+ // because TLS values have to be static on some platforms, this is made static
+ // and only one instance of the TLS is allowed
+ static UNITY_TLS_VALUE(UnderlyingAllocator*) m_UniqueThreadAllocator; // the memorymanager holds the list of allocators
+ static int s_NumberOfInstances;
+
+ static const int kMaxThreadTempAllocators = 128;
+ UnderlyingAllocator* m_ThreadTempAllocators[kMaxThreadTempAllocators];
+};
+
+#endif
+#endif
diff --git a/Runtime/Allocator/UnityDefaultAllocator.cpp b/Runtime/Allocator/UnityDefaultAllocator.cpp
new file mode 100644
index 0000000..a2ec30c
--- /dev/null
+++ b/Runtime/Allocator/UnityDefaultAllocator.cpp
@@ -0,0 +1,284 @@
+#include "UnityPrefix.h"
+#include "UnityDefaultAllocator.h"
+
+#if ENABLE_MEMORY_MANAGER
+
+#include "Runtime/Allocator/AllocationHeader.h"
+#include "Runtime/Profiler/MemoryProfiler.h"
+#include "Runtime/Utilities/BitUtility.h"
+#include "Runtime/Allocator/MemoryManager.h"
+
+template<class LLAlloctor>
+UnityDefaultAllocator<LLAlloctor>::UnityDefaultAllocator(const char* name)
+: BaseAllocator(name)
+{
+ memset(m_PageAllocationList,0,sizeof(m_PageAllocationList));
+}
+
+template<class LLAlloctor>
+void* UnityDefaultAllocator<LLAlloctor>::Allocate (size_t size, int align)
+{
+ size_t realSize = AllocationHeader::CalculateNeededAllocationSize(size, align);
+ void* rawPtr = LLAlloctor::Malloc( realSize );
+ if(rawPtr == NULL)
+ return NULL;
+
+ void* realPtr = AddHeaderAndFooter(rawPtr, size, align);
+ RegisterAllocation(realPtr);
+
+ return realPtr;
+}
+
+template<class LLAlloctor>
+void* UnityDefaultAllocator<LLAlloctor>::Reallocate( void* p, size_t size, int align)
+{
+ if (p == NULL)
+ return Allocate(size, align);
+
+ AllocationHeader::ValidateIntegrity(p, m_AllocatorIdentifier, align);
+ RegisterDeallocation(p);
+
+ size_t oldSize = GetPtrSize(p);
+ size_t oldPadCount = AllocationHeader::GetHeader(p)->GetPadding();
+
+ void* realPtr = AllocationHeader::GetRealPointer(p);
+ size_t realSize = AllocationHeader::CalculateNeededAllocationSize(size, align);
+ char* rawPtr = (char*)LLAlloctor::Realloc(realPtr, realSize);
+ if(rawPtr == NULL)
+ return NULL;
+
+ int newPadCount = AllocationHeader::GetRequiredPadding(rawPtr, align);
+
+ if (newPadCount != oldPadCount){
+ // new ptr needs different align padding. move memory and repad
+ char* srcptr = rawPtr + AllocationHeader::GetHeaderSize() + oldPadCount;
+ char* dstptr = rawPtr + AllocationHeader::GetHeaderSize() + newPadCount;
+ memmove(dstptr, srcptr, ( oldSize < size ? oldSize : size ) );
+ }
+
+ void* newptr = AddHeaderAndFooter(rawPtr, size, align);
+ RegisterAllocation(newptr);
+
+ return newptr;
+}
+
+template<class LLAlloctor>
+void UnityDefaultAllocator<LLAlloctor>::Deallocate (void* p)
+{
+ if (p == NULL)
+ return;
+
+ AllocationHeader::ValidateIntegrity(p, m_AllocatorIdentifier);
+ RegisterDeallocation(p);
+
+ void* realpointer = AllocationHeader::GetRealPointer(p);
+
+ LLAlloctor::Free(realpointer);
+}
+
+template<class LLAlloctor>
+template<RequestType requestType>
+bool UnityDefaultAllocator<LLAlloctor>::AllocationPage(const void* p){
+
+ // A memory and performance optimization could be to register lone pointers in the array instead of setting the bit.
+ // when multiple pointers arrive, pull the pointer out, register it, and register the next. Requires some bookkeeping.
+ // bottom 2 bits of the pointer can be used for flags.
+ int pageAllocationListIndex = 0;
+ UInt32 val = (UInt32)p;
+
+ if(sizeof(void*) > sizeof(UInt32))
+ {
+ Assert(sizeof(void*) == sizeof(UInt64));
+ UInt32 highbits = (UInt32)((UInt64)(uintptr_t)(p) >> 32);
+ if(highbits != 0)
+ {
+ pageAllocationListIndex = -1;
+ for(int i = 0; i < kNumPageAllocationBlocks; i++)
+ if(m_PageAllocationList[i].m_HighBits == highbits)
+ pageAllocationListIndex = i;
+
+ if(pageAllocationListIndex == -1)
+ {
+ // highbits not found in the list. find a free list element
+ for(int i = 0; i < kNumPageAllocationBlocks; i++)
+ {
+ if(m_PageAllocationList[i].m_PageAllocations == NULL)
+ {
+ m_PageAllocationList[i].m_HighBits = highbits;
+ pageAllocationListIndex = i;
+ break;
+ }
+ }
+ if(requestType == kTest)
+ {
+ return false;
+ }
+ else
+ {
+ if(pageAllocationListIndex == -1)
+ ErrorString("Using memoryadresses from more that 16GB of memory");
+ }
+ }
+ }
+ }
+
+ int ****& pageAllocations = m_PageAllocationList[pageAllocationListIndex].m_PageAllocations;
+ int page1 = (val >> (32-kPage1Bits)) & ((1<<kPage1Bits)-1);
+ int page2 = (val >> (32-kPage1Bits-kPage2Bits)) & ((1<<kPage2Bits)-1);
+ int page3 = (val >> (32-kPage1Bits-kPage2Bits-kPage3Bits)) & ((1<<kPage3Bits)-1);
+ int page4 = (val >> (32-kPage1Bits-kPage2Bits-kPage3Bits-kPage4Bits)) & ((1<<kPage4Bits)-1);
+ int bitindex = (val >> kTargetBitsRepresentedPerBit) & 0x1F;
+
+ if(requestType == kUnregister){
+ Assert(pageAllocations != NULL);
+ Assert(pageAllocations[page1] != NULL);
+ Assert(pageAllocations[page1][page2] != NULL);
+ Assert(pageAllocations[page1][page2][page3] != NULL);
+
+ pageAllocations[page1][page2][page3][page4] &= ~(1<<bitindex);
+ if(--pageAllocations[page1][page2][page3][(1<<kPage4Bits)] == 0)
+ {
+ m_BookKeepingMemoryUsage -= ((1<<kPage4Bits)+1)*sizeof(int*);
+ MemoryManager::LowLevelFree (pageAllocations[page1][page2][page3]);
+ pageAllocations[page1][page2][page3] = NULL;
+ }
+ if(--pageAllocations[page1][page2][(1<<kPage3Bits)] == 0)
+ {
+ m_BookKeepingMemoryUsage -= ((1<<kPage3Bits)+1)*sizeof(int**);
+ MemoryManager::LowLevelFree (pageAllocations[page1][page2]);
+ pageAllocations[page1][page2] = NULL;
+ }
+ if(--pageAllocations[page1][(1<<kPage2Bits)] == 0)
+ {
+ m_BookKeepingMemoryUsage -= ((1<<kPage2Bits)+1)*sizeof(int***);
+ MemoryManager::LowLevelFree (pageAllocations[page1]);
+ pageAllocations[page1] = NULL;
+ }
+ if(--pageAllocations[(1<<kPage1Bits)] == 0)
+ {
+ m_BookKeepingMemoryUsage -= ((1<<kPage1Bits)+1)*sizeof(int***);
+ MemoryManager::LowLevelFree (pageAllocations);
+ pageAllocations = NULL;
+ }
+ return true;
+ }
+
+ if(pageAllocations == NULL)
+ {
+ if(requestType == kRegister)
+ {
+ pageAllocations = (int****)MemoryManager::LowLevelCAllocate((1<<kPage1Bits)+1,sizeof(int****));
+ m_BookKeepingMemoryUsage += ((1<<kPage1Bits)+1)*sizeof(int****);
+ pageAllocations[(1<<kPage1Bits)] = 0;
+ }
+ else
+ return false;
+ }
+ if(pageAllocations[page1] == NULL)
+ {
+ if(requestType == kRegister)
+ {
+ pageAllocations[page1] = (int***)MemoryManager::LowLevelCAllocate((1<<kPage2Bits)+1,sizeof(int***));
+ m_BookKeepingMemoryUsage += ((1<<kPage2Bits)+1)*sizeof(int***);
+ pageAllocations[page1][(1<<kPage2Bits)] = 0;
+ }
+ else
+ return false;
+ }
+ if(pageAllocations[page1][page2] == NULL)
+ {
+ if(requestType == kRegister)
+ {
+ pageAllocations[page1][page2] = (int**)MemoryManager::LowLevelCAllocate((1<<kPage3Bits)+1,sizeof(int**));
+ m_BookKeepingMemoryUsage += ((1<<kPage3Bits)+1)*sizeof(int**);
+ pageAllocations[page1][page2][(1<<kPage3Bits)] = 0;
+ }
+ else
+ return false;
+ }
+ if(pageAllocations[page1][page2][page3] == NULL)
+ {
+ if(requestType == kRegister)
+ {
+ pageAllocations[page1][page2][page3] = (int*)MemoryManager::LowLevelCAllocate((1<<kPage4Bits)+1,sizeof(int*));
+ m_BookKeepingMemoryUsage += ((1<<kPage4Bits)+1)*sizeof(int*);
+ pageAllocations[page1][page2][page3][(1<<kPage4Bits)] = 0;
+ }
+ else
+ return false;
+ }
+ if(requestType == kTest)
+ return (pageAllocations[page1][page2][page3][page4] & (1<<bitindex)) != 0;
+ pageAllocations[page1][page2][page3][(1<<kPage4Bits)]++;
+ pageAllocations[page1][page2][(1<<kPage3Bits)]++;
+ pageAllocations[page1][(1<<kPage2Bits)]++;
+ pageAllocations[(1<<kPage1Bits)]++;
+ Assert((pageAllocations[page1][page2][page3][page4] & (1<<bitindex)) == 0); // the bit for this pointer should not be set yet
+ pageAllocations[page1][page2][page3][page4] |= (1<<bitindex);
+ return true;
+
+}
+
+template<class LLAlloctor>
+void* UnityDefaultAllocator<LLAlloctor>::AddHeaderAndFooter( void* ptr, size_t size, int align ) const
+{
+ Assert(align >= kDefaultMemoryAlignment && align <= 16*1024 && IsPowerOfTwo(align));
+ // calculate required padding for ptr to be aligned after header addition
+ // ppppppppHHHH***********
+ int padCount = AllocationHeader::GetRequiredPadding(ptr, align);
+ void* realPtr = ((char*)ptr) + (padCount + AllocationHeader::GetHeaderSize());
+ AllocationHeader::Set(realPtr, m_AllocatorIdentifier, size, padCount, align);
+ return realPtr;
+}
+
+template<class LLAlloctor>
+void UnityDefaultAllocator<LLAlloctor>::RegisterAllocation( const void* p )
+{
+ Mutex::AutoLock lock(m_AllocLock);
+ const size_t ptrSize = GetPtrSize(p);
+ const int overheadSize = AllocationHeader::GetHeader(p)->GetOverheadSize();
+ RegisterAllocationData(ptrSize, overheadSize);
+ m_TotalReservedMemory += ptrSize + overheadSize;
+ AllocationPage<kRegister>(p);
+}
+
+template<class LLAlloctor>
+void UnityDefaultAllocator<LLAlloctor>::RegisterDeallocation( const void* p )
+{
+ Mutex::AutoLock lock(m_AllocLock);
+ const size_t ptrSize = GetPtrSize(p);
+ const int overheadSize = AllocationHeader::GetHeader(p)->GetOverheadSize();
+ RegisterDeallocationData(ptrSize, overheadSize);
+ m_TotalReservedMemory -= ptrSize + overheadSize;
+ AllocationPage<kUnregister>(p);
+}
+
+template<class LLAlloctor>
+bool UnityDefaultAllocator<LLAlloctor>::Contains (const void* p)
+{
+ Mutex::AutoLock lock(m_AllocLock);
+ return AllocationPage<kTest>(p);
+}
+
+template<class LLAlloctor>
+size_t UnityDefaultAllocator<LLAlloctor>::GetPtrSize( const void* ptr ) const
+{
+ return AllocationHeader::GetHeader(ptr)->GetRequestedSize();
+}
+
+template<class LLAlloctor>
+ProfilerAllocationHeader* UnityDefaultAllocator<LLAlloctor>::GetProfilerHeader(const void* ptr) const
+{
+ // LocalHeader:ProfilerHeader:Data
+ return AllocationHeader::GetProfilerHeader(ptr);
+}
+
+template<class LLAlloctor>
+int UnityDefaultAllocator<LLAlloctor>::GetOverheadSize(void* ptr)
+{
+ return AllocationHeader::GetHeader(ptr)->GetOverheadSize();
+}
+
+template class UnityDefaultAllocator<LowLevelAllocator>;
+
+#endif
diff --git a/Runtime/Allocator/UnityDefaultAllocator.h b/Runtime/Allocator/UnityDefaultAllocator.h
new file mode 100644
index 0000000..b26ce6b
--- /dev/null
+++ b/Runtime/Allocator/UnityDefaultAllocator.h
@@ -0,0 +1,70 @@
+#ifndef UNITY_DEFAULT_ALLOCATOR_H_
+#define UNITY_DEFAULT_ALLOCATOR_H_
+
+#if ENABLE_MEMORY_MANAGER
+
+#include "BaseAllocator.h"
+#include "Runtime/Threads/Mutex.h"
+#include "Runtime/Allocator/LowLevelDefaultAllocator.h"
+#include "Runtime/Utilities/BitUtility.h"
+
+enum RequestType
+{
+ kRegister,
+ kUnregister,
+ kTest
+};
+
+template<class LLAlloctor>
+class UnityDefaultAllocator : public BaseAllocator
+{
+public:
+ UnityDefaultAllocator(const char* name);
+
+ virtual void* Allocate (size_t size, int align);
+ virtual void* Reallocate (void* p, size_t size, int align);
+ virtual void Deallocate (void* p);
+ virtual bool Contains (const void* p);
+
+ virtual size_t GetPtrSize(const void* ptr) const;
+
+ virtual ProfilerAllocationHeader* GetProfilerHeader(const void* ptr) const;
+
+ static int GetOverheadSize(void* ptr);
+private:
+ // needs 30 bit (4byte aligned allocs and packed as bitarray) ( 1 byte -> 32Bytes, 4bytes rep 128Bytes(7bit))
+ enum
+ {
+ kTargetBitsRepresentedPerBit = StaticLog2<kDefaultMemoryAlignment>::value,
+ kTargetBitsRepresentedPerByte = 5 + kTargetBitsRepresentedPerBit,
+ kPage1Bits = 7, // 128*4Bytes = 512Bytes (page: 4GB/128 = 32MB per pointer)
+ kPage2Bits = 7, // 128*4Bytes = 512Bytes (page: 32MB/128 = 256KB per pointer)
+ kPage3Bits = 5, // 32*4Bytes = 128Bytes (page: 256K/32 = 8K per pointer)
+ kPage4Bits = 32-kPage1Bits-kPage2Bits-kPage3Bits-kTargetBitsRepresentedPerByte
+ };
+
+ struct PageAllocationElement
+ {
+ PageAllocationElement() : m_HighBits(0), m_PageAllocations(NULL){}
+ UInt32 m_HighBits;
+ int**** m_PageAllocations;
+ };
+
+ static const int kNumPageAllocationBlocks = 5; //each block represents 4gb of memory;
+ PageAllocationElement m_PageAllocationList[kNumPageAllocationBlocks];
+
+ template<RequestType requestType>
+ bool AllocationPage(const void* p);
+
+ Mutex m_AllocLock;
+
+ void RegisterAllocation(const void* p);
+ void RegisterDeallocation(const void* p);
+
+ void* AddHeaderAndFooter( void* ptr, size_t size, int align ) const;
+};
+
+#endif
+
+#endif
+
diff --git a/Runtime/Allocator/tlsf/tlsf.c b/Runtime/Allocator/tlsf/tlsf.c
new file mode 100644
index 0000000..9c3f32b
--- /dev/null
+++ b/Runtime/Allocator/tlsf/tlsf.c
@@ -0,0 +1,966 @@
+#include <assert.h>
+#include <limits.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "tlsf.h"
+#include "tlsfbits.h"
+
+/*
+** Constants.
+*/
+
+/* Public constants: may be modified. */
+enum tlsf_public
+{
+ /* log2 of number of linear subdivisions of block sizes. */
+ SL_INDEX_COUNT_LOG2 = 5,
+};
+
+/* Private constants: do not modify. */
+enum tlsf_private
+{
+#if defined (TLSF_64BIT)
+ /* All allocation sizes and addresses are aligned to 8 bytes. */
+ ALIGN_SIZE_LOG2 = 3,
+#else
+ /* All allocation sizes and addresses are aligned to 4 bytes. */
+ ALIGN_SIZE_LOG2 = 2,
+#endif
+ ALIGN_SIZE = (1 << ALIGN_SIZE_LOG2),
+
+ /*
+ ** We support allocations of sizes up to (1 << FL_INDEX_MAX) bits.
+ ** However, because we linearly subdivide the second-level lists, and
+ ** our minimum size granularity is 4 bytes, it doesn't make sense to
+ ** create first-level lists for sizes smaller than SL_INDEX_COUNT * 4,
+ ** or (1 << (SL_INDEX_COUNT_LOG2 + 2)) bytes, as there we will be
+ ** trying to split size ranges into more slots than we have available.
+ ** Instead, we calculate the minimum threshold size, and place all
+ ** blocks below that size into the 0th first-level list.
+ */
+
+#if defined (TLSF_64BIT)
+ /*
+ ** TODO: We can increase this to support larger sizes, at the expense
+ ** of more overhead in the TLSF structure.
+ */
+ FL_INDEX_MAX = 32,
+#else
+ FL_INDEX_MAX = 30,
+#endif
+ SL_INDEX_COUNT = (1 << SL_INDEX_COUNT_LOG2),
+ FL_INDEX_SHIFT = (SL_INDEX_COUNT_LOG2 + ALIGN_SIZE_LOG2),
+ FL_INDEX_COUNT = (FL_INDEX_MAX - FL_INDEX_SHIFT + 1),
+
+ SMALL_BLOCK_SIZE = (1 << FL_INDEX_SHIFT),
+};
+
+/*
+** Cast and min/max macros.
+*/
+
+#define tlsf_cast(t, exp) ((t) (exp))
+#define tlsf_min(a, b) ((a) < (b) ? (a) : (b))
+#define tlsf_max(a, b) ((a) > (b) ? (a) : (b))
+
+/*
+** Set assert macro, if it has not been provided by the user.
+*/
+#if !defined (tlsf_assert)
+#if defined(__flash__)
+//extern void tlsf_assert(int condition);
+#define tlsf_assert
+#else
+#define tlsf_assert assert
+#endif
+#endif
+
+/*
+** Static assertion mechanism.
+*/
+
+#define _tlsf_glue2(x, y) x ## y
+#define _tlsf_glue(x, y) _tlsf_glue2(x, y)
+#define tlsf_static_assert(exp) \
+ typedef char _tlsf_glue(static_assert, __LINE__) [(exp) ? 1 : -1]
+
+/* This code has been tested on 32- and 64-bit (LP/LLP) architectures. */
+tlsf_static_assert(sizeof(int) * CHAR_BIT == 32);
+tlsf_static_assert(sizeof(size_t) * CHAR_BIT >= 32);
+tlsf_static_assert(sizeof(size_t) * CHAR_BIT <= 64);
+
+/* SL_INDEX_COUNT must be <= number of bits in sl_bitmap's storage type. */
+tlsf_static_assert(sizeof(unsigned int) * CHAR_BIT >= SL_INDEX_COUNT);
+
+/* Ensure we've properly tuned our sizes. */
+tlsf_static_assert(ALIGN_SIZE == SMALL_BLOCK_SIZE / SL_INDEX_COUNT);
+
+/*
+** Data structures and associated constants.
+*/
+
+/*
+** Block header structure.
+**
+** There are several implementation subtleties involved:
+** - The prev_phys_block field is only valid if the previous block is free.
+** - The prev_phys_block field is actually stored at the end of the
+** previous block. It appears at the beginning of this structure only to
+** simplify the implementation.
+** - The next_free / prev_free fields are only valid if the block is free.
+*/
+typedef struct block_header_t
+{
+ /* Points to the previous physical block. */
+ struct block_header_t* prev_phys_block;
+
+ /* The size of this block, excluding the block header. */
+ size_t size;
+
+ /* Next and previous free blocks. */
+ struct block_header_t* next_free;
+ struct block_header_t* prev_free;
+} block_header_t;
+
+/*
+** Since block sizes are always at least a multiple of 4, the two least
+** significant bits of the size field are used to store the block status:
+** - bit 0: whether block is busy or free
+** - bit 1: whether previous block is busy or free
+*/
+static const size_t block_header_free_bit = 1 << 0;
+static const size_t block_header_prev_free_bit = 1 << 1;
+
+/*
+** The size of the block header exposed to used blocks is the size field.
+** The prev_phys_block field is stored *inside* the previous free block.
+*/
+static const size_t block_header_overhead = sizeof(size_t);
+
+/* User data starts directly after the size field in a used block. */
+static const size_t block_start_offset =
+ offsetof(block_header_t, size) + sizeof(size_t);
+
+/*
+** A free block must be large enough to store its header minus the size of
+** the prev_phys_block field, and no larger than the number of addressable
+** bits for FL_INDEX.
+*/
+static const size_t block_size_min =
+ sizeof(block_header_t) - sizeof(block_header_t*);
+static const size_t block_size_max = tlsf_cast(size_t, 1) << FL_INDEX_MAX;
+
+
+/* The TLSF pool structure. */
+typedef struct pool_t
+{
+ /* Empty lists point at this block to indicate they are free. */
+ block_header_t block_null;
+
+ /* Bitmaps for free lists. */
+ unsigned int fl_bitmap;
+ unsigned int sl_bitmap[FL_INDEX_COUNT];
+
+ /* Head of free lists. */
+ block_header_t* blocks[FL_INDEX_COUNT][SL_INDEX_COUNT];
+} pool_t;
+
+/* A type used for casting when doing pointer arithmetic. */
+typedef ptrdiff_t tlsfptr_t;
+
+/*
+** block_header_t member functions.
+*/
+
+static size_t block_size(const block_header_t* block)
+{
+ return block->size & ~(block_header_free_bit | block_header_prev_free_bit);
+}
+
+static void block_set_size(block_header_t* block, size_t size)
+{
+ const size_t oldsize = block->size;
+ block->size = size | (oldsize & (block_header_free_bit | block_header_prev_free_bit));
+}
+
+static int block_is_last(const block_header_t* block)
+{
+ return 0 == block_size(block);
+}
+
+static int block_is_free(const block_header_t* block)
+{
+ return tlsf_cast(int, block->size & block_header_free_bit);
+}
+
+static void block_set_free(block_header_t* block)
+{
+ block->size |= block_header_free_bit;
+}
+
+static void block_set_used(block_header_t* block)
+{
+ block->size &= ~block_header_free_bit;
+}
+
+static int block_is_prev_free(const block_header_t* block)
+{
+ return tlsf_cast(int, block->size & block_header_prev_free_bit);
+}
+
+static void block_set_prev_free(block_header_t* block)
+{
+ block->size |= block_header_prev_free_bit;
+}
+
+static void block_set_prev_used(block_header_t* block)
+{
+ block->size &= ~block_header_prev_free_bit;
+}
+
+static block_header_t* block_from_ptr(const void* ptr)
+{
+ return tlsf_cast(block_header_t*,
+ tlsf_cast(unsigned char*, ptr) - block_start_offset);
+}
+
+static void* block_to_ptr(const block_header_t* block)
+{
+ return tlsf_cast(void*,
+ tlsf_cast(unsigned char*, block) + block_start_offset);
+}
+
+/* Return location of next block after block of given size. */
+static block_header_t* offset_to_block(const void* ptr, size_t size)
+{
+ return tlsf_cast(block_header_t*, tlsf_cast(tlsfptr_t, ptr) + size);
+}
+
+/* Return location of previous block. */
+static block_header_t* block_prev(const block_header_t* block)
+{
+ return block->prev_phys_block;
+}
+
+/* Return location of next existing block. */
+static block_header_t* block_next(const block_header_t* block)
+{
+ block_header_t* next = offset_to_block(block_to_ptr(block),
+ block_size(block) - block_header_overhead);
+ tlsf_assert(!block_is_last(block));
+ return next;
+}
+
+/* Link a new block with its physical neighbor, return the neighbor. */
+static block_header_t* block_link_next(block_header_t* block)
+{
+ block_header_t* next = block_next(block);
+ next->prev_phys_block = block;
+ return next;
+}
+
+static void block_mark_as_free(block_header_t* block)
+{
+ /* Link the block to the next block, first. */
+ block_header_t* next = block_link_next(block);
+ block_set_prev_free(next);
+ block_set_free(block);
+}
+
+static void block_mark_as_used(block_header_t* block)
+{
+ block_header_t* next = block_next(block);
+ block_set_prev_used(next);
+ block_set_used(block);
+}
+
+static size_t align_up(size_t x, size_t align)
+{
+ tlsf_assert(0 == (align & (align - 1)) && "must align to a power of two");
+ return (x + (align - 1)) & ~(align - 1);
+}
+
+static size_t align_down(size_t x, size_t align)
+{
+ tlsf_assert(0 == (align & (align - 1)) && "must align to a power of two");
+ return x - (x & (align - 1));
+}
+
+static void* align_ptr(const void* ptr, size_t align)
+{
+ const tlsfptr_t aligned =
+ (tlsf_cast(tlsfptr_t, ptr) + (align - 1)) & ~(align - 1);
+ tlsf_assert(0 == (align & (align - 1)) && "must align to a power of two");
+ return tlsf_cast(void*, aligned);
+}
+
+/*
+** Adjust an allocation size to be aligned to word size, and no smaller
+** than internal minimum.
+*/
+static size_t adjust_request_size(size_t size, size_t align)
+{
+ size_t adjust = 0;
+ if (size && size < block_size_max)
+ {
+ const size_t aligned = align_up(size, align);
+ adjust = tlsf_max(aligned, block_size_min);
+ }
+ return adjust;
+}
+
+/*
+** TLSF utility functions. In most cases, these are direct translations of
+** the documentation found in the white paper.
+*/
+
+static void mapping_insert(size_t size, int* fli, int* sli)
+{
+ int fl, sl;
+ if (size < SMALL_BLOCK_SIZE)
+ {
+ /* Store small blocks in first list. */
+ fl = 0;
+ sl = tlsf_cast(int, size) / (SMALL_BLOCK_SIZE / SL_INDEX_COUNT);
+ }
+ else
+ {
+ fl = tlsf_fls_sizet(size);
+ sl = tlsf_cast(int, size >> (fl - SL_INDEX_COUNT_LOG2)) ^ (1 << SL_INDEX_COUNT_LOG2);
+ fl -= (FL_INDEX_SHIFT - 1);
+ }
+ *fli = fl;
+ *sli = sl;
+}
+
+/* This version rounds up to the next block size (for allocations) */
+static void mapping_search(size_t size, int* fli, int* sli)
+{
+ if (size >= (1 << SL_INDEX_COUNT_LOG2))
+ {
+ const size_t round = (1 << (tlsf_fls_sizet(size) - SL_INDEX_COUNT_LOG2)) - 1;
+ size += round;
+ }
+ mapping_insert(size, fli, sli);
+}
+
+static block_header_t* search_suitable_block(pool_t* pool, int* fli, int* sli)
+{
+ int fl = *fli;
+ int sl = *sli;
+
+ /*
+ ** First, search for a block in the list associated with the given
+ ** fl/sl index.
+ */
+ unsigned int sl_map = pool->sl_bitmap[fl] & (~0 << sl);
+ if (!sl_map)
+ {
+ /* No block exists. Search in the next largest first-level list. */
+ const unsigned int fl_map = pool->fl_bitmap & (~0 << (fl + 1));
+ if (!fl_map)
+ {
+ /* No free blocks available, memory has been exhausted. */
+ return 0;
+ }
+
+ fl = tlsf_ffs(fl_map);
+ *fli = fl;
+ sl_map = pool->sl_bitmap[fl];
+ }
+ tlsf_assert(sl_map && "internal error - second level bitmap is null");
+ sl = tlsf_ffs(sl_map);
+ *sli = sl;
+
+ /* Return the first block in the free list. */
+ return pool->blocks[fl][sl];
+}
+
+/* Remove a free block from the free list.*/
+static void remove_free_block(pool_t* pool, block_header_t* block, int fl, int sl)
+{
+ block_header_t* prev = block->prev_free;
+ block_header_t* next = block->next_free;
+ tlsf_assert(prev && "prev_free field can not be null");
+ tlsf_assert(next && "next_free field can not be null");
+ next->prev_free = prev;
+ prev->next_free = next;
+
+ /* If this block is the head of the free list, set new head. */
+ if (pool->blocks[fl][sl] == block)
+ {
+ pool->blocks[fl][sl] = next;
+
+ /* If the new head is null, clear the bitmap. */
+ if (next == &pool->block_null)
+ {
+ pool->sl_bitmap[fl] &= ~(1 << sl);
+
+ /* If the second bitmap is now empty, clear the fl bitmap. */
+ if (!pool->sl_bitmap[fl])
+ {
+ pool->fl_bitmap &= ~(1 << fl);
+ }
+ }
+ }
+}
+
+/* Insert a free block into the free block list. */
+static void insert_free_block(pool_t* pool, block_header_t* block, int fl, int sl)
+{
+ block_header_t* current = pool->blocks[fl][sl];
+ tlsf_assert(current && "free list cannot have a null entry");
+ tlsf_assert(block && "cannot insert a null entry into the free list");
+ block->next_free = current;
+ block->prev_free = &pool->block_null;
+ current->prev_free = block;
+
+ tlsf_assert(block_to_ptr(block) == align_ptr(block_to_ptr(block), ALIGN_SIZE)
+ && "block not aligned properly");
+ /*
+ ** Insert the new block at the head of the list, and mark the first-
+ ** and second-level bitmaps appropriately.
+ */
+ pool->blocks[fl][sl] = block;
+ pool->fl_bitmap |= (1 << fl);
+ pool->sl_bitmap[fl] |= (1 << sl);
+}
+
+/* Remove a given block from the free list. */
+static void block_remove(pool_t* pool, block_header_t* block)
+{
+ int fl, sl;
+ mapping_insert(block_size(block), &fl, &sl);
+ remove_free_block(pool, block, fl, sl);
+}
+
+/* Insert a given block into the free list. */
+static void block_insert(pool_t* pool, block_header_t* block)
+{
+ int fl, sl;
+ mapping_insert(block_size(block), &fl, &sl);
+ insert_free_block(pool, block, fl, sl);
+}
+
+static int block_can_split(block_header_t* block, size_t size)
+{
+ return block_size(block) >= sizeof(block_header_t) + size;
+}
+
+/* Split a block into two, the second of which is free. */
+static block_header_t* block_split(block_header_t* block, size_t size)
+{
+ /* Calculate the amount of space left in the remaining block. */
+ block_header_t* remaining =
+ offset_to_block(block_to_ptr(block), size - block_header_overhead);
+
+ const size_t remain_size = block_size(block) - (size + block_header_overhead);
+
+ tlsf_assert(block_to_ptr(remaining) == align_ptr(block_to_ptr(remaining), ALIGN_SIZE)
+ && "remaining block not aligned properly");
+
+ tlsf_assert(block_size(block) == remain_size + size + block_header_overhead);
+ block_set_size(remaining, remain_size);
+ tlsf_assert(block_size(remaining) >= block_size_min && "block split with invalid size");
+
+ block_set_size(block, size);
+ block_mark_as_free(remaining);
+
+ return remaining;
+}
+
+/* Absorb a free block's storage into an adjacent previous free block. */
+static block_header_t* block_absorb(block_header_t* prev, block_header_t* block)
+{
+ tlsf_assert(!block_is_last(prev) && "previous block can't be last!");
+ /* Note: Leaves flags untouched. */
+ prev->size += block_size(block) + block_header_overhead;
+ block_link_next(prev);
+ return prev;
+}
+
+/* Merge a just-freed block with an adjacent previous free block. */
+static block_header_t* block_merge_prev(pool_t* pool, block_header_t* block)
+{
+ if (block_is_prev_free(block))
+ {
+ block_header_t* prev = block_prev(block);
+ tlsf_assert(prev && "prev physical block can't be null");
+ tlsf_assert(block_is_free(prev) && "prev block is not free though marked as such");
+ block_remove(pool, prev);
+ block = block_absorb(prev, block);
+ }
+
+ return block;
+}
+
+/* Merge a just-freed block with an adjacent free block. */
+static block_header_t* block_merge_next(pool_t* pool, block_header_t* block)
+{
+ block_header_t* next = block_next(block);
+ tlsf_assert(next && "next physical block can't be null");
+
+ if (block_is_free(next))
+ {
+ tlsf_assert(!block_is_last(block) && "previous block can't be last!");
+ block_remove(pool, next);
+ block = block_absorb(block, next);
+ }
+
+ return block;
+}
+
+/* Trim any trailing block space off the end of a block, return to pool. */
+static void block_trim_free(pool_t* pool, block_header_t* block, size_t size)
+{
+ tlsf_assert(block_is_free(block) && "block must be free");
+ if (block_can_split(block, size))
+ {
+ block_header_t* remaining_block = block_split(block, size);
+ block_link_next(block);
+ block_set_prev_free(remaining_block);
+ block_insert(pool, remaining_block);
+ }
+}
+
+/* Trim any trailing block space off the end of a used block, return to pool. */
+static void block_trim_used(pool_t* pool, block_header_t* block, size_t size)
+{
+ tlsf_assert(!block_is_free(block) && "block must be used");
+ if (block_can_split(block, size))
+ {
+ /* If the next block is free, we must coalesce. */
+ block_header_t* remaining_block = block_split(block, size);
+ block_set_prev_used(remaining_block);
+
+ remaining_block = block_merge_next(pool, remaining_block);
+ block_insert(pool, remaining_block);
+ }
+}
+
+static block_header_t* block_trim_free_leading(pool_t* pool, block_header_t* block, size_t size)
+{
+ block_header_t* remaining_block = block;
+ if (block_can_split(block, size))
+ {
+ /* We want the 2nd block. */
+ remaining_block = block_split(block, size - block_header_overhead);
+ block_set_prev_free(remaining_block);
+
+ block_link_next(block);
+ block_insert(pool, block);
+ }
+
+ return remaining_block;
+}
+
+static block_header_t* block_locate_free(pool_t* pool, size_t size)
+{
+ int fl = 0, sl = 0;
+ block_header_t* block = 0;
+
+ if (size)
+ {
+ mapping_search(size, &fl, &sl);
+ block = search_suitable_block(pool, &fl, &sl);
+ }
+
+ if (block)
+ {
+ tlsf_assert(block_size(block) >= size);
+ remove_free_block(pool, block, fl, sl);
+ }
+
+ return block;
+}
+
+static void* block_prepare_used(pool_t* pool, block_header_t* block, size_t size)
+{
+ void* p = 0;
+ if (block)
+ {
+ block_trim_free(pool, block, size);
+ block_mark_as_used(block);
+ p = block_to_ptr(block);
+ }
+ return p;
+}
+
+/* Clear structure and point all empty lists at the null block. */
+static void pool_construct(pool_t* pool)
+{
+ int i, j;
+
+ pool->block_null.next_free = &pool->block_null;
+ pool->block_null.prev_free = &pool->block_null;
+
+ pool->fl_bitmap = 0;
+ for (i = 0; i < FL_INDEX_COUNT; ++i)
+ {
+ pool->sl_bitmap[i] = 0;
+ for (j = 0; j < SL_INDEX_COUNT; ++j)
+ {
+ pool->blocks[i][j] = &pool->block_null;
+ }
+ }
+}
+
+/*
+** Debugging utilities.
+*/
+
+typedef struct integrity_t
+{
+ int prev_status;
+ int status;
+} integrity_t;
+
+#define tlsf_insist(x) { tlsf_assert(x); if (!(x)) { status--; } }
+
+static void integrity_walker(void* ptr, size_t size, int used, void* user)
+{
+ block_header_t* block = block_from_ptr(ptr);
+ integrity_t* integ = tlsf_cast(integrity_t*, user);
+ const int this_prev_status = block_is_prev_free(block) ? 1 : 0;
+ const int this_status = block_is_free(block) ? 1 : 0;
+ const size_t this_block_size = block_size(block);
+
+ int status = 0;
+ tlsf_insist(integ->prev_status == this_prev_status && "prev status incorrect");
+ tlsf_insist(size == this_block_size && "block size incorrect");
+
+ integ->prev_status = this_status;
+ integ->status += status;
+}
+
+int tlsf_check_heap(tlsf_pool tlsf)
+{
+ int i, j;
+
+ pool_t* pool = tlsf_cast(pool_t*, tlsf);
+ int status = 0;
+
+ /* Check that the blocks are physically correct. */
+ integrity_t integ = { 0, 0 };
+ tlsf_walk_heap(tlsf, integrity_walker, &integ);
+ status = integ.status;
+
+ /* Check that the free lists and bitmaps are accurate. */
+ for (i = 0; i < FL_INDEX_COUNT; ++i)
+ {
+ for (j = 0; j < SL_INDEX_COUNT; ++j)
+ {
+ const int fl_map = pool->fl_bitmap & (1 << i);
+ const int sl_list = pool->sl_bitmap[i];
+ const int sl_map = sl_list & (1 << j);
+ const block_header_t* block = pool->blocks[i][j];
+
+ /* Check that first- and second-level lists agree. */
+ if (!fl_map)
+ {
+ tlsf_insist(!sl_map && "second-level map must be null");
+ }
+
+ if (!sl_map)
+ {
+ tlsf_insist(block == &pool->block_null && "block list must be null");
+ continue;
+ }
+
+ /* Check that there is at least one free block. */
+ tlsf_insist(sl_list && "no free blocks in second-level map");
+ tlsf_insist(block != &pool->block_null && "block should not be null");
+
+ while (block != &pool->block_null)
+ {
+ int fli, sli;
+ tlsf_insist(block_is_free(block) && "block should be free");
+ tlsf_insist(!block_is_prev_free(block) && "blocks should have coalesced");
+ tlsf_insist(!block_is_free(block_next(block)) && "blocks should have coalesced");
+ tlsf_insist(block_is_prev_free(block_next(block)) && "block should be free");
+ tlsf_insist(block_size(block) >= block_size_min && "block not minimum size");
+
+ mapping_insert(block_size(block), &fli, &sli);
+ tlsf_insist(fli == i && sli == j && "block size indexed in wrong list");
+ block = block->next_free;
+ }
+ }
+ }
+
+ return status;
+}
+
+#undef tlsf_insist
+
+static void default_walker(void* ptr, size_t size, int used, void* user)
+{
+ (void)user;
+ printf("\t%p %s size: %x (%p)\n", ptr, used ? "used" : "free", (unsigned int)size, block_from_ptr(ptr));
+}
+
+void tlsf_walk_heap(tlsf_pool pool, tlsf_walker walker, void* user)
+{
+ tlsf_walker heap_walker = walker ? walker : default_walker;
+ block_header_t* block =
+ offset_to_block(pool, sizeof(pool_t) - block_header_overhead);
+
+ while (block && !block_is_last(block))
+ {
+ heap_walker(
+ block_to_ptr(block),
+ block_size(block),
+ !block_is_free(block),
+ user);
+ block = block_next(block);
+ }
+}
+
+size_t tlsf_block_size(void* ptr)
+{
+ size_t size = 0;
+ if (ptr)
+ {
+ const block_header_t* block = block_from_ptr(ptr);
+ size = block_size(block);
+ }
+ return size;
+}
+
+/*
+** Overhead of the TLSF structures in a given memory block passed to
+** tlsf_create, equal to the size of a pool_t plus overhead of the initial
+** free block and the sentinel block.
+*/
+size_t tlsf_overhead()
+{
+ const size_t pool_overhead = sizeof(pool_t) + 2 * block_header_overhead;
+ return pool_overhead;
+}
+
+/*
+** TLSF main interface. Right out of the white paper.
+*/
+
+tlsf_pool tlsf_create(void* mem, size_t bytes)
+{
+ block_header_t* block;
+ block_header_t* next;
+
+ const size_t pool_overhead = tlsf_overhead();
+ const size_t pool_bytes = align_down(bytes - pool_overhead, ALIGN_SIZE);
+ pool_t* pool = tlsf_cast(pool_t*, mem);
+
+#if _DEBUG
+ /* Verify ffs/fls work properly. */
+ int rv = 0;
+ rv += (tlsf_ffs(0) == -1) ? 0 : 0x1;
+ rv += (tlsf_fls(0) == -1) ? 0 : 0x2;
+ rv += (tlsf_ffs(1) == 0) ? 0 : 0x4;
+ rv += (tlsf_fls(1) == 0) ? 0 : 0x8;
+ rv += (tlsf_ffs(0x80000000) == 31) ? 0 : 0x10;
+ rv += (tlsf_ffs(0x80008000) == 15) ? 0 : 0x20;
+ rv += (tlsf_fls(0x80000008) == 31) ? 0 : 0x40;
+ rv += (tlsf_fls(0x7FFFFFFF) == 30) ? 0 : 0x80;
+
+#if defined (TLSF_64BIT)
+ rv += (tlsf_fls_sizet(0x80000000) == 31) ? 0 : 0x100;
+ rv += (tlsf_fls_sizet(0x100000000) == 32) ? 0 : 0x200;
+ rv += (tlsf_fls_sizet(0xffffffffffffffff) == 63) ? 0 : 0x400;
+ if (rv)
+ {
+ printf("tlsf_create: %x ffs/fls tests failed!\n", rv);
+ return 0;
+ }
+#endif
+#endif
+
+ if (pool_bytes < block_size_min || pool_bytes > block_size_max)
+ {
+#if defined (TLSF_64BIT)
+ printf("tlsf_create: Pool size must be at least %d bytes.\n",
+ (unsigned int)(pool_overhead + block_size_min));
+#else
+ printf("tlsf_create: Pool size must be between %u and %u bytes.\n",
+ (unsigned int)(pool_overhead + block_size_min),
+ (unsigned int)(pool_overhead + block_size_max));
+#endif
+ return 0;
+ }
+
+ /* Construct a valid pool object. */
+ pool_construct(pool);
+
+ /*
+ ** Create the main free block. Offset the start of the block slightly
+ ** so that the prev_phys_block field falls inside of the pool
+ ** structure - it will never be used.
+ */
+ block = offset_to_block(
+ tlsf_cast(void*, pool), sizeof(pool_t) - block_header_overhead);
+ block_set_size(block, pool_bytes);
+ block_set_free(block);
+ block_set_prev_used(block);
+ block_insert(pool, block);
+
+ /* Split the block to create a zero-size pool sentinel block. */
+ next = block_link_next(block);
+ block_set_size(next, 0);
+ block_set_used(next);
+ block_set_prev_free(next);
+
+ return tlsf_cast(tlsf_pool, pool);
+}
+
+void tlsf_destroy(tlsf_pool pool)
+{
+ /* Nothing to do. */
+ pool = pool;
+}
+
+void* tlsf_malloc(tlsf_pool tlsf, size_t size)
+{
+ pool_t* pool = tlsf_cast(pool_t*, tlsf);
+ const size_t adjust = adjust_request_size(size, ALIGN_SIZE);
+ block_header_t* block = block_locate_free(pool, adjust);
+ return block_prepare_used(pool, block, adjust);
+}
+
+void* tlsf_memalign(tlsf_pool tlsf, size_t align, size_t size)
+{
+ pool_t* pool = tlsf_cast(pool_t*, tlsf);
+ const size_t adjust = adjust_request_size(size, ALIGN_SIZE);
+
+ /*
+ ** We must allocate an additional minimum block size bytes so that if
+ ** our free block will leave an alignment gap which is smaller, we can
+ ** trim a leading free block and release it back to the heap. We must
+ ** do this because the previous physical block is in use, therefore
+ ** the prev_phys_block field is not valid, and we can't simply adjust
+ ** the size of that block.
+ */
+ const size_t gap_minimum = sizeof(block_header_t);
+ const size_t size_with_gap = adjust_request_size(adjust + align + gap_minimum, align);
+
+ /* If alignment is less than or equals base alignment, we're done. */
+ const size_t aligned_size = (align <= ALIGN_SIZE) ? adjust : size_with_gap;
+
+ block_header_t* block = block_locate_free(pool, aligned_size);
+
+ /* This can't be a static assert. */
+ tlsf_assert(sizeof(block_header_t) == block_size_min + block_header_overhead);
+
+ if (block)
+ {
+ void* ptr = block_to_ptr(block);
+ void* aligned = align_ptr(ptr, align);
+ size_t gap = tlsf_cast(size_t,
+ tlsf_cast(tlsfptr_t, aligned) - tlsf_cast(tlsfptr_t, ptr));
+
+ /* If gap size is too small, offset to next aligned boundary. */
+ if (gap && gap < gap_minimum)
+ {
+ const size_t gap_remain = gap_minimum - gap;
+ const size_t offset = tlsf_max(gap_remain, align);
+ const void* next_aligned = tlsf_cast(void*,
+ tlsf_cast(tlsfptr_t, aligned) + offset);
+
+ aligned = align_ptr(next_aligned, align);
+ gap = tlsf_cast(size_t,
+ tlsf_cast(tlsfptr_t, aligned) - tlsf_cast(tlsfptr_t, ptr));
+ }
+
+ if (gap)
+ {
+ tlsf_assert(gap >= gap_minimum && "gap size too small");
+ block = block_trim_free_leading(pool, block, gap);
+ }
+ }
+
+ return block_prepare_used(pool, block, adjust);
+}
+
+void tlsf_free(tlsf_pool tlsf, void* ptr)
+{
+ /* Don't attempt to free a NULL pointer. */
+ if (ptr)
+ {
+ pool_t* pool = tlsf_cast(pool_t*, tlsf);
+ block_header_t* block = block_from_ptr(ptr);
+ block_mark_as_free(block);
+ block = block_merge_prev(pool, block);
+ block = block_merge_next(pool, block);
+ block_insert(pool, block);
+ }
+}
+
+/*
+** The TLSF block information provides us with enough information to
+** provide a reasonably intelligent implementation of realloc, growing or
+** shrinking the currently allocated block as required.
+**
+** This routine handles the somewhat esoteric edge cases of realloc:
+** - a non-zero size with a null pointer will behave like malloc
+** - a zero size with a non-null pointer will behave like free
+** - a request that cannot be satisfied will leave the original buffer
+** untouched
+** - an extended buffer size will leave the newly-allocated area with
+** contents undefined
+*/
+void* tlsf_realloc(tlsf_pool tlsf, void* ptr, size_t size)
+{
+ pool_t* pool = tlsf_cast(pool_t*, tlsf);
+ void* p = 0;
+
+ /* Zero-size requests are treated as free. */
+ if (ptr && size == 0)
+ {
+ tlsf_free(tlsf, ptr);
+ }
+ /* Requests with NULL pointers are treated as malloc. */
+ else if (!ptr)
+ {
+ p = tlsf_malloc(tlsf, size);
+ }
+ else
+ {
+ block_header_t* block = block_from_ptr(ptr);
+ block_header_t* next = block_next(block);
+
+ const size_t cursize = block_size(block);
+ const size_t combined = cursize + block_size(next) + block_header_overhead;
+ const size_t adjust = adjust_request_size(size, ALIGN_SIZE);
+
+ /*
+ ** If the next block is used, or when combined with the current
+ ** block, does not offer enough space, we must reallocate and copy.
+ */
+ if (adjust > cursize && (!block_is_free(next) || adjust > combined))
+ {
+ p = tlsf_malloc(tlsf, size);
+ if (p)
+ {
+ const size_t minsize = tlsf_min(cursize, size);
+ memcpy(p, ptr, minsize);
+ tlsf_free(tlsf, ptr);
+ }
+ }
+ else
+ {
+ /* Do we need to expand to the next block? */
+ if (adjust > cursize)
+ {
+ block_merge_next(pool, block);
+ block_mark_as_used(block);
+ }
+
+ /* Trim the resulting block and return the original pointer. */
+ block_trim_used(pool, block, adjust);
+ p = ptr;
+ }
+ }
+
+ return p;
+}
diff --git a/Runtime/Allocator/tlsf/tlsf.h b/Runtime/Allocator/tlsf/tlsf.h
new file mode 100644
index 0000000..de7f90b
--- /dev/null
+++ b/Runtime/Allocator/tlsf/tlsf.h
@@ -0,0 +1,52 @@
+#ifndef INCLUDED_tlsf
+#define INCLUDED_tlsf
+
+/*
+** Two Level Segregated Fit memory allocator, version 1.9.
+** Written by Matthew Conte, and placed in the Public Domain.
+** http://tlsf.baisoku.org
+**
+** Based on the original documentation by Miguel Masmano:
+** http://rtportal.upv.es/rtmalloc/allocators/tlsf/index.shtml
+**
+** Please see the accompanying Readme.txt for implementation
+** notes and caveats.
+**
+** This implementation was written to the specification
+** of the document, therefore no GPL restrictions apply.
+*/
+
+#include <stddef.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+/* Create/destroy a memory pool. */
+typedef void* tlsf_pool;
+tlsf_pool tlsf_create(void* mem, size_t bytes);
+void tlsf_destroy(tlsf_pool pool);
+
+/* malloc/memalign/realloc/free replacements. */
+void* tlsf_malloc(tlsf_pool pool, size_t bytes);
+void* tlsf_memalign(tlsf_pool pool, size_t align, size_t bytes);
+void* tlsf_realloc(tlsf_pool pool, void* ptr, size_t size);
+void tlsf_free(tlsf_pool pool, void* ptr);
+
+/* Debugging. */
+typedef void (*tlsf_walker)(void* ptr, size_t size, int used, void* user);
+void tlsf_walk_heap(tlsf_pool pool, tlsf_walker walker, void* user);
+/* Returns nonzero if heap check fails. */
+int tlsf_check_heap(tlsf_pool pool);
+
+/* Returns internal block size, not original request size */
+size_t tlsf_block_size(void* ptr);
+
+/* Overhead of per-pool internal structures. */
+size_t tlsf_overhead();
+
+#if defined(__cplusplus)
+};
+#endif
+
+#endif
diff --git a/Runtime/Allocator/tlsf/tlsfbits.h b/Runtime/Allocator/tlsf/tlsfbits.h
new file mode 100644
index 0000000..a41a959
--- /dev/null
+++ b/Runtime/Allocator/tlsf/tlsfbits.h
@@ -0,0 +1,184 @@
+#ifndef INCLUDED_tlsfbits
+#define INCLUDED_tlsfbits
+
+#if defined(__cplusplus)
+#define tlsf_decl inline
+#else
+#define tlsf_decl static
+#endif
+
+/*
+** Architecture-specific bit manipulation routines.
+**
+** TLSF achieves O(1) cost for malloc and free operations by limiting
+** the search for a free block to a free list of guaranteed size
+** adequate to fulfill the request, combined with efficient free list
+** queries using bitmasks and architecture-specific bit-manipulation
+** routines.
+**
+** Most modern processors provide instructions to count leading zeroes
+** in a word, find the lowest and highest set bit, etc. These
+** specific implementations will be used when available, falling back
+** to a reasonably efficient generic implementation.
+**
+** NOTE: TLSF spec relies on ffs/fls returning value 0..31.
+** ffs/fls return 1-32 by default, returning 0 for error.
+*/
+
+/*
+** Detect whether or not we are building for a 32- or 64-bit (LP/LLP)
+** architecture. There is no reliable portable method at compile-time.
+*/
+
+// native client uses ILP32, even when building for x86_64, so make sure
+// that pointer sizes are treated as 32 bits.
+#if !defined(__native_client__)
+#if defined (__alpha__) || defined (__ia64__) || defined (__x86_64__) \
+ || defined (_WIN64) || defined (__LP64__) || defined (__LLP64__)
+#define TLSF_64BIT
+#endif
+#endif
+/*
+** gcc 3.4 and above have builtin support, specialized for architecture.
+** Some compilers masquerade as gcc; patchlevel test filters them out.
+*/
+#if defined (__GNUC__) && (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)) \
+ && defined (__GNUC_PATCHLEVEL__)
+
+tlsf_decl int tlsf_ffs(unsigned int word)
+{
+ return __builtin_ffs(word) - 1;
+}
+
+tlsf_decl int tlsf_fls(unsigned int word)
+{
+ const int bit = word ? 32 - __builtin_clz(word) : 0;
+ return bit - 1;
+}
+
+#elif defined (_MSC_VER) && defined (_M_IX86) && (_MSC_VER >= 1400)
+/* Microsoft Visual C++ 2005 support on x86 architectures. */
+
+#include <intrin.h>
+
+#pragma intrinsic(_BitScanReverse)
+#pragma intrinsic(_BitScanForward)
+
+tlsf_decl int tlsf_fls(unsigned int word)
+{
+ unsigned long index;
+ return _BitScanReverse(&index, word) ? index : -1;
+}
+
+tlsf_decl int tlsf_ffs(unsigned int word)
+{
+ unsigned long index;
+ return _BitScanForward(&index, word) ? index : -1;
+}
+
+#elif defined (_MSC_VER) && defined (_M_PPC)
+/* Microsoft Visual C++ support on PowerPC architectures. */
+
+#include <ppcintrinsics.h>
+
+tlsf_decl int tlsf_fls(unsigned int word)
+{
+ const int bit = 32 - _CountLeadingZeros(word);
+ return bit - 1;
+}
+
+tlsf_decl int tlsf_ffs(unsigned int word)
+{
+ const unsigned int reverse = word & (~word + 1);
+ const int bit = 32 - _CountLeadingZeros(reverse);
+ return bit - 1;
+}
+
+#elif defined (__ARMCC_VERSION)
+/* RealView Compilation Tools for ARM */
+
+tlsf_decl int tlsf_ffs(unsigned int word)
+{
+ const unsigned int reverse = word & (~word + 1);
+ const int bit = 32 - __clz(reverse);
+ return bit - 1;
+}
+
+tlsf_decl int tlsf_fls(unsigned int word)
+{
+ const int bit = word ? 32 - __clz(word) : 0;
+ return bit - 1;
+}
+
+#elif defined (__ghs__)
+/* Green Hills support for PowerPC */
+
+#include <ppc_ghs.h>
+
+tlsf_decl int tlsf_ffs(unsigned int word)
+{
+ const unsigned int reverse = word & (~word + 1);
+ const int bit = 32 - __CLZ32(reverse);
+ return bit - 1;
+}
+
+tlsf_decl int tlsf_fls(unsigned int word)
+{
+ const int bit = word ? 32 - __CLZ32(word) : 0;
+ return bit - 1;
+}
+
+#else
+/* Fall back to generic implementation. */
+
+tlsf_decl int tlsf_fls_generic(unsigned int word)
+{
+ int bit = 32;
+
+ if (!word) bit -= 1;
+ if (!(word & 0xffff0000)) { word <<= 16; bit -= 16; }
+ if (!(word & 0xff000000)) { word <<= 8; bit -= 8; }
+ if (!(word & 0xf0000000)) { word <<= 4; bit -= 4; }
+ if (!(word & 0xc0000000)) { word <<= 2; bit -= 2; }
+ if (!(word & 0x80000000)) { word <<= 1; bit -= 1; }
+
+ return bit;
+}
+
+/* Implement ffs in terms of fls. */
+tlsf_decl int tlsf_ffs(unsigned int word)
+{
+ return tlsf_fls_generic(word & (~word + 1)) - 1;
+}
+
+tlsf_decl int tlsf_fls(unsigned int word)
+{
+ return tlsf_fls_generic(word) - 1;
+}
+
+#endif
+
+/* Possibly 64-bit version of tlsf_fls. */
+#if defined (TLSF_64BIT)
+tlsf_decl int tlsf_fls_sizet(size_t size)
+{
+ int high = (int)(size >> 32);
+ int bits = 0;
+ if (high)
+ {
+ bits = 32 + tlsf_fls(high);
+ }
+ else
+ {
+ bits = tlsf_fls((int)size & 0xffffffff);
+
+ }
+ return bits;
+}
+#else
+#define tlsf_fls_sizet tlsf_fls
+#endif
+
+#undef tlsf_decl
+
+#endif