#pragma once #undef min #undef max #include #include namespace CamelotFramework { class MemoryAllocatorBase; class MemoryCounter { public: static CM_UTILITY_EXPORT UINT64 getNumAllocs() { return Allocs; } static CM_UTILITY_EXPORT UINT64 getNumFrees() { return Frees; } private: friend class MemoryAllocatorBase; // Threadlocal data can't be exported, so some magic to make it accessible from MemoryAllocator static CM_UTILITY_EXPORT void incAllocCount() { Allocs++; } static CM_UTILITY_EXPORT void incFreeCount() { Frees++; } static CM_THREADLOCAL UINT64 Allocs; static CM_THREADLOCAL UINT64 Frees; }; class MemoryAllocatorBase { protected: static void incAllocCount() { MemoryCounter::incAllocCount(); } static void incFreeCount() { MemoryCounter::incFreeCount(); } }; /** * @brief Memory allocator providing a generic implementation. * Specialize for specific categories as needed. */ template class MemoryAllocator : public MemoryAllocatorBase { public: static inline void* allocate(UINT32 bytes) { #if CM_PROFILING_ENABLED incAllocCount(); #endif return malloc(bytes); } static inline void* allocateArray(UINT32 bytes, UINT32 count) { #if CM_PROFILING_ENABLED incAllocCount(); #endif return malloc(bytes * count); } static inline void free(void* ptr) { #if CM_PROFILING_ENABLED incFreeCount(); #endif ::free(ptr); } static inline void freeArray(void* ptr, UINT32 count) { #if CM_PROFILING_ENABLED incFreeCount(); #endif ::free(ptr); } }; /** * @brief General allocator provided by the OS. Use for persistent long term allocations, * and allocations that don't happen often. */ class GenAlloc { }; /** * @brief Allocator used for allocating small amounts of temporary memory that * used and then quickly released */ class ScratchAlloc { }; /** * @brief Pool allocator that is only suited for allocating one specific type of data. Most useful when you are * often allocating one certain data type, with no specific allocation or deallocation order. */ class PoolAlloc { }; /** * @brief Allocates the specified number of bytes. */ template inline void* cm_alloc(UINT32 count) { return MemoryAllocator::allocate(count); } /** * @brief Allocates enough bytes to hold the specified type, but doesn't construct it. */ template inline T* cm_alloc() { return (T*)MemoryAllocator::allocate(sizeof(T)); } /** * @brief Creates and constructs an array of "count" elements. */ template inline T* cm_newN(UINT32 count) { T* ptr = (T*)MemoryAllocator::allocateArray(sizeof(T), count); for(unsigned int i = 0; i < count; i++) new ((void*)&ptr[i]) T; return ptr; } /** * @brief Create a new object with the specified allocator and the specified parameters. */ #define MAKE_CM_NEW(z, n, unused) \ template \ Type* cm_new(BOOST_PP_ENUM_BINARY_PARAMS(n, T, &&t) ) { \ return new (cm_alloc(sizeof(Type))) Type(std::forward(t0) BOOST_PP_REPEAT_FROM_TO(1, n, FORWARD_T, ~)); \ } #define FORWARD_T(z, i, unused) \ , std::forward(BOOST_PP_CAT(t, i)) BOOST_PP_REPEAT_FROM_TO(1, 15, MAKE_CM_NEW, ~) #undef FORWARD_T #undef MAKE_CM_NEW // Create a new object with the specified allocator without any parameters // (Needs to be separate from parameter version so I don't unnecessarily zero-initialize POD types) template Type* cm_new() { return new (cm_alloc(sizeof(Type))) Type; } /** * @brief Frees all the bytes allocated at the specified location. */ template inline void cm_free(void* ptr) { MemoryAllocator::free(ptr); } /** * @brief Destructs and frees the specified object. */ template inline void cm_delete(T* ptr) { (ptr)->~T(); MemoryAllocator::free(ptr); } /** * @brief Destructs and frees the specified array of objects. */ template inline void cm_deleteN(T* ptr, UINT32 count) { for(unsigned int i = 0; i < count; i++) ptr[i].~T(); MemoryAllocator::freeArray(ptr, count); } /*****************************************************************************/ /* Default versions of all alloc/free/new/delete methods which call GenAlloc */ /*****************************************************************************/ /** * @brief Allocates the specified number of bytes. */ inline void* cm_alloc(UINT32 count) { return MemoryAllocator::allocate(count); } /** * @brief Allocates enough bytes to hold the specified type, but doesn't construct it. */ template inline T* cm_alloc() { return (T*)MemoryAllocator::allocate(sizeof(T)); } /** * @brief Creates and constructs an array of "count" elements. */ template inline T* cm_newN(UINT32 count) { T* ptr = (T*)MemoryAllocator::allocateArray(sizeof(T), count); for(unsigned int i = 0; i < count; i++) new ((void*)&ptr[i]) T; return ptr; } // Create a new object with the general allocator and the specified parameters. #define MAKE_CM_NEW(z, n, unused) \ template \ Type* cm_new(BOOST_PP_ENUM_BINARY_PARAMS(n, T, &&t) ) { \ return new (cm_alloc(sizeof(Type))) Type(std::forward(t0) BOOST_PP_REPEAT_FROM_TO(1, n, FORWARD_T, ~)); \ } #define FORWARD_T(z, i, unused) \ , std::forward(BOOST_PP_CAT(t, i)) BOOST_PP_REPEAT_FROM_TO(1, 15, MAKE_CM_NEW, ~) #undef FORWARD_T #undef MAKE_CM_NEW // Create a new object with the general allocator without any parameters // (Needs to be separate from parameter version so I don't unnecessarily zero-initialize POD types) template Type* cm_new() { return new (cm_alloc(sizeof(Type))) Type; } /** * @brief Frees all the bytes allocated at the specified location. */ inline void cm_free(void* ptr) { MemoryAllocator::free(ptr); } /** * @brief Destructs and frees the specified object. */ template inline void cm_delete(T* ptr) { (ptr)->~T(); MemoryAllocator::free(ptr); } /** * @brief Destructs and frees the specified array of objects. */ template inline void cm_deleteN(T* ptr, UINT32 count) { for(unsigned int i = 0; i < count; i++) ptr[i].~T(); MemoryAllocator::freeArray(ptr, count); } /************************************************************************/ /* MACRO VERSIONS */ /* You will almost always want to use the template versions but in some */ /* cases (private destructor) it is not possible. In which case you may */ /* use these instead. */ /************************************************************************/ #define CM_PVT_DELETE(T, ptr) \ (ptr)->~T(); \ MemoryAllocator::free(ptr); #define CM_PVT_DELETE_A(T, ptr, Alloc) \ (ptr)->~T(); \ MemoryAllocator::free(ptr); } namespace CamelotFramework { // Allocator we can use in the standard library template class StdAlloc { public: // type definitions typedef T value_type; typedef T* pointer; typedef const T* const_pointer; typedef T& reference; typedef const T& const_reference; typedef std::size_t size_type; typedef std::ptrdiff_t difference_type; // rebind allocator to type U template struct rebind { typedef StdAlloc other; }; // return address of values pointer address (reference value) const { return &value; } const_pointer address (const_reference value) const { return &value; } /* constructors and destructor * - nothing to do because the allocator has no state */ StdAlloc() throw() { } StdAlloc(const StdAlloc&) throw() { } template StdAlloc (const StdAlloc&) throw() { } ~StdAlloc() throw() { } // return maximum number of elements that can be allocated size_type max_size () const throw() { return std::numeric_limits::max() / sizeof(T); } // allocate but don't initialize num elements of type T pointer allocate (size_type num, const void* = 0) { pointer ret = (pointer)(cm_alloc((UINT32)num*sizeof(T))); return ret; } // initialize elements of allocated storage p with value value void construct (pointer p, const T& value) { // initialize memory with placement new new((void*)p)T(value); } // destroy elements of initialized storage p void destroy (pointer p) { // destroy objects by calling their destructor p->~T(); } // deallocate storage p of deleted elements void deallocate (pointer p, size_type num) { // print message and deallocate memory with global delete cm_free((void*)p); } }; // return that all specializations of this allocator are interchangeable template bool operator== (const StdAlloc&, const StdAlloc&) throw() { return true; } template bool operator!= (const StdAlloc&, const StdAlloc&) throw() { return false; } } #include "CmMemStack.h" #include "CmMemAllocProfiler.h"