#pragma once #undef min #undef max #include /** @addtogroup Memory * @{ */ namespace BansheeEngine { class MemoryAllocatorBase; /** @cond INTERNAL */ /** * Thread safe class used for storing total number of memory allocations and deallocations, primarily for statistic * purposes. */ class MemoryCounter { public: static BS_UTILITY_EXPORT UINT64 getNumAllocs() { return Allocs; } static BS_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 BS_UTILITY_EXPORT void incAllocCount() { Allocs++; } static BS_UTILITY_EXPORT void incFreeCount() { Frees++; } static BS_THREADLOCAL UINT64 Allocs; static BS_THREADLOCAL UINT64 Frees; }; /** Base class all memory allocators need to inherit. Provides allocation and free counting. */ class MemoryAllocatorBase { protected: static void incAllocCount() { MemoryCounter::incAllocCount(); } static void incFreeCount() { MemoryCounter::incFreeCount(); } }; /** * Memory allocator providing a generic implementation. Specialize for specific categories as needed. * * @note For example you might implement a pool allocator for specific types in order * to reduce allocation overhead. By default standard malloc/free are used. */ template class MemoryAllocator : public MemoryAllocatorBase { public: static void* allocate(size_t bytes) { #if BS_PROFILING_ENABLED incAllocCount(); #endif return malloc(bytes); } static void* allocateArray(size_t bytes, UINT32 count) { #if BS_PROFILING_ENABLED incAllocCount(); #endif return malloc(bytes * count); } static void free(void* ptr) { #if BS_PROFILING_ENABLED incFreeCount(); #endif ::free(ptr); } static void freeArray(void* ptr, UINT32 count) { #if BS_PROFILING_ENABLED incFreeCount(); #endif ::free(ptr); } }; /** * General allocator provided by the OS. Use for persistent long term allocations, and allocations that don't * happen often. */ class GenAlloc { }; /** @endcond */ /** Allocates the specified number of bytes. */ template inline void* bs_alloc(UINT32 count) { return MemoryAllocator::allocate(count); } /** Allocates enough bytes to hold the specified type, but doesn't construct it. */ template inline T* bs_alloc() { return (T*)MemoryAllocator::allocate(sizeof(T)); } /** Creates and constructs an array of @p count elements. */ template inline T* bs_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 specified allocator and the specified parameters. */ template Type* bs_new(Args &&...args) { return new (bs_alloc(sizeof(Type))) Type(std::forward(args)...); } /** Frees all the bytes allocated at the specified location. */ template inline void bs_free(void* ptr) { MemoryAllocator::free(ptr); } /** Destructs and frees the specified object. */ template inline void bs_delete(T* ptr) { (ptr)->~T(); MemoryAllocator::free(ptr); } /** Destructs and frees the specified array of objects. */ template inline void bs_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 */ /*****************************************************************************/ /** Allocates the specified number of bytes. */ inline void* bs_alloc(UINT32 count) { return MemoryAllocator::allocate(count); } /** Allocates enough bytes to hold the specified type, but doesn't construct it. */ template inline T* bs_alloc() { return (T*)MemoryAllocator::allocate(sizeof(T)); } /** Allocates enough bytes to hold an array of @p count elements the specified type, but doesn't construct them. */ template inline T* bs_allocN(UINT32 count) { return (T*)MemoryAllocator::allocate(count * sizeof(T)); } /** Creates and constructs an array of @p count elements. */ template inline T* bs_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 specified allocator and the specified parameters. */ template Type* bs_new(Args &&...args) { return new (bs_alloc(sizeof(Type))) Type(std::forward(args)...); } /** Frees all the bytes allocated at the specified location. */ inline void bs_free(void* ptr) { MemoryAllocator::free(ptr); } /************************************************************************/ /* 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 BS_PVT_DELETE(T, ptr) \ (ptr)->~T(); \ MemoryAllocator::free(ptr); #define BS_PVT_DELETE_A(T, ptr, Alloc) \ (ptr)->~T(); \ MemoryAllocator::free(ptr); } namespace BansheeEngine { /** @cond INTERNAL */ /** Allocator for the standard library that internally uses Banshee memory allocator. */ template class StdAlloc { public: typedef T value_type; StdAlloc() noexcept {} template StdAlloc(const StdAlloc&) noexcept {} template bool operator==(const StdAlloc&) const noexcept { return true; } template bool operator!=(const StdAlloc&) const noexcept { return false; } /** Allocate but don't initialize number elements of type T. */ T* allocate(const size_t num) const { if (num == 0) return nullptr; if (num > static_cast(-1) / sizeof(T)) throw std::bad_array_new_length(); void* const pv = bs_alloc((UINT32)(num * sizeof(T))); if (!pv) throw std::bad_alloc(); return static_cast(pv); } /** Deallocate storage p of deleted elements. */ void deallocate(T* p, size_t num) const noexcept { bs_free((void*)p); } }; /** @endcond */ } /** @} */ #include "BsMemStack.h" #include "BsGlobalFrameAlloc.h" #include "BsMemAllocProfiler.h"