CmMemoryAllocator.h 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403
  1. #pragma once
  2. #undef min
  3. #undef max
  4. #include <boost/preprocessor.hpp>
  5. #include <atomic>
  6. namespace CamelotFramework
  7. {
  8. class MemoryAllocatorBase;
  9. class MemoryCounter
  10. {
  11. public:
  12. static CM_UTILITY_EXPORT UINT64 getNumAllocs()
  13. {
  14. return Allocs;
  15. }
  16. static CM_UTILITY_EXPORT UINT64 getNumFrees()
  17. {
  18. return Frees;
  19. }
  20. private:
  21. friend class MemoryAllocatorBase;
  22. // Threadlocal data can't be exported, so some magic to make it accessible from MemoryAllocator
  23. static CM_UTILITY_EXPORT void incAllocCount() { Allocs++; }
  24. static CM_UTILITY_EXPORT void incFreeCount() { Frees++; }
  25. static CM_THREADLOCAL UINT64 Allocs;
  26. static CM_THREADLOCAL UINT64 Frees;
  27. };
  28. class MemoryAllocatorBase
  29. {
  30. protected:
  31. static void incAllocCount() { MemoryCounter::incAllocCount(); }
  32. static void incFreeCount() { MemoryCounter::incFreeCount(); }
  33. };
  34. /**
  35. * @brief Memory allocator providing a generic implementation.
  36. * Specialize for specific categories as needed.
  37. */
  38. template<class T>
  39. class MemoryAllocator : public MemoryAllocatorBase
  40. {
  41. public:
  42. static inline void* allocate(UINT32 bytes)
  43. {
  44. #if CM_PROFILING_ENABLED
  45. incAllocCount();
  46. #endif
  47. return malloc(bytes);
  48. }
  49. static inline void* allocateArray(UINT32 bytes, UINT32 count)
  50. {
  51. #if CM_PROFILING_ENABLED
  52. incAllocCount();
  53. #endif
  54. return malloc(bytes * count);
  55. }
  56. static inline void free(void* ptr)
  57. {
  58. #if CM_PROFILING_ENABLED
  59. incFreeCount();
  60. #endif
  61. ::free(ptr);
  62. }
  63. static inline void freeArray(void* ptr, UINT32 count)
  64. {
  65. #if CM_PROFILING_ENABLED
  66. incFreeCount();
  67. #endif
  68. ::free(ptr);
  69. }
  70. };
  71. /**
  72. * @brief General allocator provided by the OS. Use for persistent long term allocations,
  73. * and allocations that don't happen often.
  74. */
  75. class GenAlloc
  76. { };
  77. /**
  78. * @brief Allocator used for allocating small amounts of temporary memory that
  79. * used and then quickly released
  80. */
  81. class ScratchAlloc
  82. { };
  83. /**
  84. * @brief Pool allocator that is only suited for allocating one specific type of data. Most useful when you are
  85. * often allocating one certain data type, with no specific allocation or deallocation order.
  86. */
  87. class PoolAlloc
  88. { };
  89. /**
  90. * @brief Allocates the specified number of bytes.
  91. */
  92. template<class Alloc>
  93. inline void* cm_alloc(UINT32 count)
  94. {
  95. return MemoryAllocator<Alloc>::allocate(count);
  96. }
  97. /**
  98. * @brief Allocates enough bytes to hold the specified type, but doesn't construct it.
  99. */
  100. template<class T, class Alloc>
  101. inline T* cm_alloc()
  102. {
  103. return (T*)MemoryAllocator<Alloc>::allocate(sizeof(T));
  104. }
  105. /**
  106. * @brief Creates and constructs an array of "count" elements.
  107. */
  108. template<class T, class Alloc>
  109. inline T* cm_newN(UINT32 count)
  110. {
  111. T* ptr = (T*)MemoryAllocator<Alloc>::allocateArray(sizeof(T), count);
  112. for(unsigned int i = 0; i < count; i++)
  113. new ((void*)&ptr[i]) T;
  114. return ptr;
  115. }
  116. /**
  117. * @brief Create a new object with the specified allocator and the specified parameters.
  118. */
  119. #define MAKE_CM_NEW(z, n, unused) \
  120. template<class Type, class Alloc BOOST_PP_ENUM_TRAILING_PARAMS(n, class T)> \
  121. Type* cm_new(BOOST_PP_ENUM_BINARY_PARAMS(n, T, &&t) ) { \
  122. return new (cm_alloc<Alloc>(sizeof(Type))) Type(std::forward<T0>(t0) BOOST_PP_REPEAT_FROM_TO(1, n, FORWARD_T, ~)); \
  123. }
  124. #define FORWARD_T(z, i, unused) \
  125. , std::forward<BOOST_PP_CAT(T, i)>(BOOST_PP_CAT(t, i))
  126. BOOST_PP_REPEAT_FROM_TO(1, 15, MAKE_CM_NEW, ~)
  127. #undef FORWARD_T
  128. #undef MAKE_CM_NEW
  129. // Create a new object with the specified allocator without any parameters
  130. // (Needs to be separate from parameter version so I don't unnecessarily zero-initialize POD types)
  131. template<class Type, class Alloc>
  132. Type* cm_new()
  133. {
  134. return new (cm_alloc<Alloc>(sizeof(Type))) Type;
  135. }
  136. /**
  137. * @brief Frees all the bytes allocated at the specified location.
  138. */
  139. template<class Alloc>
  140. inline void cm_free(void* ptr)
  141. {
  142. MemoryAllocator<Alloc>::free(ptr);
  143. }
  144. /**
  145. * @brief Destructs and frees the specified object.
  146. */
  147. template<class Alloc, class T>
  148. inline void cm_delete(T* ptr)
  149. {
  150. (ptr)->~T();
  151. MemoryAllocator<Alloc>::free(ptr);
  152. }
  153. /**
  154. * @brief Destructs and frees the specified array of objects.
  155. */
  156. template<class Alloc, class T>
  157. inline void cm_deleteN(T* ptr, UINT32 count)
  158. {
  159. for(unsigned int i = 0; i < count; i++)
  160. ptr[i].~T();
  161. MemoryAllocator<Alloc>::freeArray(ptr, count);
  162. }
  163. /*****************************************************************************/
  164. /* Default versions of all alloc/free/new/delete methods which call GenAlloc */
  165. /*****************************************************************************/
  166. /**
  167. * @brief Allocates the specified number of bytes.
  168. */
  169. inline void* cm_alloc(UINT32 count)
  170. {
  171. return MemoryAllocator<GenAlloc>::allocate(count);
  172. }
  173. /**
  174. * @brief Allocates enough bytes to hold the specified type, but doesn't construct it.
  175. */
  176. template<class T>
  177. inline T* cm_alloc()
  178. {
  179. return (T*)MemoryAllocator<GenAlloc>::allocate(sizeof(T));
  180. }
  181. /**
  182. * @brief Creates and constructs an array of "count" elements.
  183. */
  184. template<class T>
  185. inline T* cm_newN(UINT32 count)
  186. {
  187. T* ptr = (T*)MemoryAllocator<GenAlloc>::allocateArray(sizeof(T), count);
  188. for(unsigned int i = 0; i < count; i++)
  189. new ((void*)&ptr[i]) T;
  190. return ptr;
  191. }
  192. // Create a new object with the general allocator and the specified parameters.
  193. #define MAKE_CM_NEW(z, n, unused) \
  194. template<class Type BOOST_PP_ENUM_TRAILING_PARAMS(n, class T)> \
  195. Type* cm_new(BOOST_PP_ENUM_BINARY_PARAMS(n, T, &&t) ) { \
  196. return new (cm_alloc<GenAlloc>(sizeof(Type))) Type(std::forward<T0>(t0) BOOST_PP_REPEAT_FROM_TO(1, n, FORWARD_T, ~)); \
  197. }
  198. #define FORWARD_T(z, i, unused) \
  199. , std::forward<BOOST_PP_CAT(T, i)>(BOOST_PP_CAT(t, i))
  200. BOOST_PP_REPEAT_FROM_TO(1, 15, MAKE_CM_NEW, ~)
  201. #undef FORWARD_T
  202. #undef MAKE_CM_NEW
  203. // Create a new object with the general allocator without any parameters
  204. // (Needs to be separate from parameter version so I don't unnecessarily zero-initialize POD types)
  205. template<class Type>
  206. Type* cm_new()
  207. {
  208. return new (cm_alloc<GenAlloc>(sizeof(Type))) Type;
  209. }
  210. /**
  211. * @brief Frees all the bytes allocated at the specified location.
  212. */
  213. inline void cm_free(void* ptr)
  214. {
  215. MemoryAllocator<GenAlloc>::free(ptr);
  216. }
  217. /**
  218. * @brief Destructs and frees the specified object.
  219. */
  220. template<class T>
  221. inline void cm_delete(T* ptr)
  222. {
  223. (ptr)->~T();
  224. MemoryAllocator<GenAlloc>::free(ptr);
  225. }
  226. /**
  227. * @brief Destructs and frees the specified array of objects.
  228. */
  229. template<class T>
  230. inline void cm_deleteN(T* ptr, UINT32 count)
  231. {
  232. for(unsigned int i = 0; i < count; i++)
  233. ptr[i].~T();
  234. MemoryAllocator<GenAlloc>::freeArray(ptr, count);
  235. }
  236. /************************************************************************/
  237. /* MACRO VERSIONS */
  238. /* You will almost always want to use the template versions but in some */
  239. /* cases (private destructor) it is not possible. In which case you may */
  240. /* use these instead. */
  241. /************************************************************************/
  242. #define CM_PVT_DELETE(T, ptr) \
  243. (ptr)->~T(); \
  244. MemoryAllocator<GenAlloc>::free(ptr);
  245. #define CM_PVT_DELETE_A(T, ptr, Alloc) \
  246. (ptr)->~T(); \
  247. MemoryAllocator<Alloc>::free(ptr);
  248. }
  249. namespace CamelotFramework
  250. {
  251. // Allocator we can use in the standard library
  252. template <class T, class Alloc = GenAlloc>
  253. class StdAlloc
  254. {
  255. public:
  256. // type definitions
  257. typedef T value_type;
  258. typedef T* pointer;
  259. typedef const T* const_pointer;
  260. typedef T& reference;
  261. typedef const T& const_reference;
  262. typedef std::size_t size_type;
  263. typedef std::ptrdiff_t difference_type;
  264. // rebind allocator to type U
  265. template <class U>
  266. struct rebind
  267. {
  268. typedef StdAlloc<U, Alloc> other;
  269. };
  270. // return address of values
  271. pointer address (reference value) const
  272. {
  273. return &value;
  274. }
  275. const_pointer address (const_reference value) const
  276. {
  277. return &value;
  278. }
  279. /* constructors and destructor
  280. * - nothing to do because the allocator has no state
  281. */
  282. StdAlloc() throw()
  283. { }
  284. StdAlloc(const StdAlloc&) throw()
  285. { }
  286. template <class U>
  287. StdAlloc (const StdAlloc<U, Alloc>&) throw()
  288. { }
  289. ~StdAlloc() throw()
  290. { }
  291. // return maximum number of elements that can be allocated
  292. size_type max_size () const throw()
  293. {
  294. return std::numeric_limits<std::size_t>::max() / sizeof(T);
  295. }
  296. // allocate but don't initialize num elements of type T
  297. pointer allocate (size_type num, const void* = 0)
  298. {
  299. pointer ret = (pointer)(cm_alloc<Alloc>((UINT32)num*sizeof(T)));
  300. return ret;
  301. }
  302. // initialize elements of allocated storage p with value value
  303. void construct (pointer p, const T& value)
  304. {
  305. // initialize memory with placement new
  306. new((void*)p)T(value);
  307. }
  308. // destroy elements of initialized storage p
  309. void destroy (pointer p)
  310. {
  311. // destroy objects by calling their destructor
  312. p->~T();
  313. }
  314. // deallocate storage p of deleted elements
  315. void deallocate (pointer p, size_type num)
  316. {
  317. // print message and deallocate memory with global delete
  318. cm_free<Alloc>((void*)p);
  319. }
  320. };
  321. // return that all specializations of this allocator are interchangeable
  322. template <class T1, class T2, class Alloc>
  323. bool operator== (const StdAlloc<T1, Alloc>&,
  324. const StdAlloc<T2, Alloc>&) throw() {
  325. return true;
  326. }
  327. template <class T1, class T2, class Alloc>
  328. bool operator!= (const StdAlloc<T1, Alloc>&,
  329. const StdAlloc<T2, Alloc>&) throw() {
  330. return false;
  331. }
  332. }
  333. #include "CmMemStack.h"
  334. #include "CmMemAllocProfiler.h"