1 | //************************************ bs::framework - Copyright 2018 Marko Pintera **************************************// |
2 | //*********** Licensed under the MIT license. See LICENSE.md for full terms. This notice is not to be removed. ***********// |
3 | #pragma once |
4 | #undef min |
5 | #undef max |
6 | |
7 | #include <new> |
8 | #include <limits> |
9 | #include <cstdint> |
10 | #include <utility> |
11 | |
12 | #if BS_PLATFORM == BS_PLATFORM_LINUX |
13 | # include <malloc.h> |
14 | #endif |
15 | |
16 | namespace bs |
17 | { |
18 | class MemoryAllocatorBase; |
19 | |
20 | /** @addtogroup Internal-Utility |
21 | * @{ |
22 | */ |
23 | |
24 | /** @addtogroup Memory-Internal |
25 | * @{ |
26 | */ |
27 | |
28 | #if BS_PLATFORM == BS_PLATFORM_WIN32 |
29 | inline void* platformAlignedAlloc16(size_t size) |
30 | { |
31 | return _aligned_malloc(size, 16); |
32 | } |
33 | |
34 | inline void platformAlignedFree16(void* ptr) |
35 | { |
36 | _aligned_free(ptr); |
37 | } |
38 | |
39 | inline void* platformAlignedAlloc(size_t size, size_t alignment) |
40 | { |
41 | return _aligned_malloc(size, alignment); |
42 | } |
43 | |
44 | inline void platformAlignedFree(void* ptr) |
45 | { |
46 | _aligned_free(ptr); |
47 | } |
48 | #elif BS_PLATFORM == BS_PLATFORM_LINUX || BS_PLATFORM == BS_PLATFORM_ANDROID |
49 | inline void* platformAlignedAlloc16(size_t size) |
50 | { |
51 | return ::memalign(16, size); |
52 | } |
53 | |
54 | inline void platformAlignedFree16(void* ptr) |
55 | { |
56 | ::free(ptr); |
57 | } |
58 | |
59 | inline void* platformAlignedAlloc(size_t size, size_t alignment) |
60 | { |
61 | return ::memalign(alignment, size); |
62 | } |
63 | |
64 | inline void platformAlignedFree(void* ptr) |
65 | { |
66 | ::free(ptr); |
67 | } |
68 | #else // 16 byte aligment by default |
69 | inline void* platformAlignedAlloc16(size_t size) |
70 | { |
71 | return ::malloc(size); |
72 | } |
73 | |
74 | inline void platformAlignedFree16(void* ptr) |
75 | { |
76 | ::free(ptr); |
77 | } |
78 | |
79 | inline void* platformAlignedAlloc(size_t size, size_t alignment) |
80 | { |
81 | void* data = ::malloc(size + (alignment - 1) + sizeof(void*)); |
82 | if (data == nullptr) |
83 | return nullptr; |
84 | |
85 | char* alignedData = ((char*)data) + sizeof(void*); |
86 | alignedData += (alignment - ((uintptr_t)alignedData) & (alignment - 1)) & (alignment - 1); |
87 | |
88 | ((void**)alignedData)[-1] = data; |
89 | return alignedData; |
90 | } |
91 | |
92 | inline void platformAlignedFree(void* ptr) |
93 | { |
94 | // TODO: Document how this works. |
95 | ::free(((void**)ptr)[-1]); |
96 | } |
97 | #endif |
98 | |
99 | /** |
100 | * Thread safe class used for storing total number of memory allocations and deallocations, primarily for statistic |
101 | * purposes. |
102 | */ |
103 | class MemoryCounter |
104 | { |
105 | public: |
106 | static BS_UTILITY_EXPORT uint64_t getNumAllocs() |
107 | { |
108 | return Allocs; |
109 | } |
110 | |
111 | static BS_UTILITY_EXPORT uint64_t getNumFrees() |
112 | { |
113 | return Frees; |
114 | } |
115 | |
116 | private: |
117 | friend class MemoryAllocatorBase; |
118 | |
119 | // Threadlocal data can't be exported, so some magic to make it accessible from MemoryAllocator |
120 | static BS_UTILITY_EXPORT void incAllocCount() { ++Allocs; } |
121 | static BS_UTILITY_EXPORT void incFreeCount() { ++Frees; } |
122 | |
123 | static BS_THREADLOCAL uint64_t Allocs; |
124 | static BS_THREADLOCAL uint64_t Frees; |
125 | }; |
126 | |
127 | /** Base class all memory allocators need to inherit. Provides allocation and free counting. */ |
128 | class MemoryAllocatorBase |
129 | { |
130 | protected: |
131 | static void incAllocCount() { MemoryCounter::incAllocCount(); } |
132 | static void incFreeCount() { MemoryCounter::incFreeCount(); } |
133 | }; |
134 | |
135 | /** |
136 | * Memory allocator providing a generic implementation. Specialize for specific categories as needed. |
137 | * |
138 | * @note For example you might implement a pool allocator for specific types in order |
139 | * to reduce allocation overhead. By default standard malloc/free are used. |
140 | */ |
141 | template<class T> |
142 | class MemoryAllocator : public MemoryAllocatorBase |
143 | { |
144 | public: |
145 | /** Allocates @p bytes bytes. */ |
146 | static void* allocate(size_t bytes) |
147 | { |
148 | #if BS_PROFILING_ENABLED |
149 | incAllocCount(); |
150 | #endif |
151 | |
152 | return malloc(bytes); |
153 | } |
154 | |
155 | /** |
156 | * Allocates @p bytes and aligns them to the specified boundary (in bytes). If the aligment is less or equal to |
157 | * 16 it is more efficient to use the allocateAligned16() alternative of this method. Alignment must be power of two. |
158 | */ |
159 | static void* allocateAligned(size_t bytes, size_t alignment) |
160 | { |
161 | #if BS_PROFILING_ENABLED |
162 | incAllocCount(); |
163 | #endif |
164 | |
165 | return platformAlignedAlloc(bytes, alignment); |
166 | } |
167 | |
168 | /** Allocates @p bytes and aligns them to a 16 byte boundary. */ |
169 | static void* allocateAligned16(size_t bytes) |
170 | { |
171 | #if BS_PROFILING_ENABLED |
172 | incAllocCount(); |
173 | #endif |
174 | |
175 | return platformAlignedAlloc16(bytes); |
176 | } |
177 | |
178 | /** Frees the memory at the specified location. */ |
179 | static void free(void* ptr) |
180 | { |
181 | #if BS_PROFILING_ENABLED |
182 | incFreeCount(); |
183 | #endif |
184 | |
185 | ::free(ptr); |
186 | } |
187 | |
188 | /** Frees memory allocated with allocateAligned() */ |
189 | static void freeAligned(void* ptr) |
190 | { |
191 | #if BS_PROFILING_ENABLED |
192 | incFreeCount(); |
193 | #endif |
194 | |
195 | platformAlignedFree(ptr); |
196 | } |
197 | |
198 | /** Frees memory allocated with allocateAligned16() */ |
199 | static void freeAligned16(void* ptr) |
200 | { |
201 | #if BS_PROFILING_ENABLED |
202 | incFreeCount(); |
203 | #endif |
204 | |
205 | platformAlignedFree16(ptr); |
206 | } |
207 | }; |
208 | |
209 | /** |
210 | * General allocator provided by the OS. Use for persistent long term allocations, and allocations that don't |
211 | * happen often. |
212 | */ |
213 | class GenAlloc |
214 | { }; |
215 | |
216 | /** @} */ |
217 | /** @} */ |
218 | |
219 | /** @addtogroup Memory |
220 | * @{ |
221 | */ |
222 | |
223 | /** Allocates the specified number of bytes. */ |
224 | template<class Alloc> |
225 | void* bs_alloc(size_t count) |
226 | { |
227 | return MemoryAllocator<Alloc>::allocate(count); |
228 | } |
229 | |
230 | /** Allocates enough bytes to hold the specified type, but doesn't construct it. */ |
231 | template<class T, class Alloc> |
232 | T* bs_alloc() |
233 | { |
234 | return (T*)MemoryAllocator<Alloc>::allocate(sizeof(T)); |
235 | } |
236 | |
237 | /** Creates and constructs an array of @p count elements. */ |
238 | template<class T, class Alloc> |
239 | T* bs_newN(size_t count) |
240 | { |
241 | T* ptr = (T*)MemoryAllocator<Alloc>::allocate(sizeof(T) * count); |
242 | |
243 | for(size_t i = 0; i < count; ++i) |
244 | new (&ptr[i]) T; |
245 | |
246 | return ptr; |
247 | } |
248 | |
249 | /** Create a new object with the specified allocator and the specified parameters. */ |
250 | template<class Type, class Alloc, class... Args> |
251 | Type* bs_new(Args &&...args) |
252 | { |
253 | return new (bs_alloc<Type, Alloc>()) Type(std::forward<Args>(args)...); |
254 | } |
255 | |
256 | /** Frees all the bytes allocated at the specified location. */ |
257 | template<class Alloc> |
258 | void bs_free(void* ptr) |
259 | { |
260 | MemoryAllocator<Alloc>::free(ptr); |
261 | } |
262 | |
263 | /** Destructs and frees the specified object. */ |
264 | template<class T, class Alloc = GenAlloc> |
265 | void bs_delete(T* ptr) |
266 | { |
267 | (ptr)->~T(); |
268 | |
269 | MemoryAllocator<Alloc>::free(ptr); |
270 | } |
271 | |
272 | /** Callable struct that acts as a proxy for bs_delete */ |
273 | template<class T, class Alloc = GenAlloc> |
274 | struct Deleter |
275 | { |
276 | constexpr Deleter() noexcept = default; |
277 | |
278 | /** Constructor enabling deleter conversion and therefore polymorphism with smart points (if they use the same allocator). */ |
279 | template <class T2, std::enable_if_t<std::is_convertible<T2*, T*>::value, int> = 0> |
280 | constexpr Deleter(const Deleter<T2, Alloc>& other) noexcept { } |
281 | |
282 | void operator()(T* ptr) const |
283 | { |
284 | bs_delete<T, Alloc>(ptr); |
285 | } |
286 | }; |
287 | |
288 | /** Destructs and frees the specified array of objects. */ |
289 | template<class T, class Alloc = GenAlloc> |
290 | void bs_deleteN(T* ptr, size_t count) |
291 | { |
292 | for(size_t i = 0; i < count; ++i) |
293 | ptr[i].~T(); |
294 | |
295 | MemoryAllocator<Alloc>::free(ptr); |
296 | } |
297 | |
298 | /*****************************************************************************/ |
299 | /* Default versions of all alloc/free/new/delete methods which call GenAlloc */ |
300 | /*****************************************************************************/ |
301 | |
302 | /** Allocates the specified number of bytes. */ |
303 | inline void* bs_alloc(size_t count) |
304 | { |
305 | return MemoryAllocator<GenAlloc>::allocate(count); |
306 | } |
307 | |
308 | /** Allocates enough bytes to hold the specified type, but doesn't construct it. */ |
309 | template<class T> |
310 | T* bs_alloc() |
311 | { |
312 | return (T*)MemoryAllocator<GenAlloc>::allocate(sizeof(T)); |
313 | } |
314 | |
315 | /** |
316 | * Allocates the specified number of bytes aligned to the provided boundary. Boundary is in bytes and must be a power |
317 | * of two. |
318 | */ |
319 | inline void* bs_alloc_aligned(size_t count, size_t align) |
320 | { |
321 | return MemoryAllocator<GenAlloc>::allocateAligned(count, align); |
322 | } |
323 | |
324 | |
325 | /** Allocates the specified number of bytes aligned to a 16 bytes boundary. */ |
326 | inline void* bs_alloc_aligned16(size_t count) |
327 | { |
328 | return MemoryAllocator<GenAlloc>::allocateAligned16(count); |
329 | } |
330 | |
331 | /** Allocates enough bytes to hold an array of @p count elements the specified type, but doesn't construct them. */ |
332 | template<class T> |
333 | T* bs_allocN(size_t count) |
334 | { |
335 | return (T*)MemoryAllocator<GenAlloc>::allocate(count * sizeof(T)); |
336 | } |
337 | |
338 | /** Creates and constructs an array of @p count elements. */ |
339 | template<class T> |
340 | T* bs_newN(size_t count) |
341 | { |
342 | T* ptr = (T*)MemoryAllocator<GenAlloc>::allocate(count * sizeof(T)); |
343 | |
344 | for(size_t i = 0; i < count; ++i) |
345 | new (&ptr[i]) T; |
346 | |
347 | return ptr; |
348 | } |
349 | |
350 | /** Create a new object with the specified allocator and the specified parameters. */ |
351 | template<class Type, class... Args> |
352 | Type* bs_new(Args &&...args) |
353 | { |
354 | return new (bs_alloc<Type, GenAlloc>()) Type(std::forward<Args>(args)...); |
355 | } |
356 | |
357 | /** Frees all the bytes allocated at the specified location. */ |
358 | inline void bs_free(void* ptr) |
359 | { |
360 | MemoryAllocator<GenAlloc>::free(ptr); |
361 | } |
362 | |
363 | /** Frees memory previously allocated with bs_alloc_aligned(). */ |
364 | inline void bs_free_aligned(void* ptr) |
365 | { |
366 | MemoryAllocator<GenAlloc>::freeAligned(ptr); |
367 | } |
368 | |
369 | /** Frees memory previously allocated with bs_alloc_aligned16(). */ |
370 | inline void bs_free_aligned16(void* ptr) |
371 | { |
372 | MemoryAllocator<GenAlloc>::freeAligned16(ptr); |
373 | } |
374 | |
375 | /************************************************************************/ |
376 | /* MACRO VERSIONS */ |
377 | /* You will almost always want to use the template versions but in some */ |
378 | /* cases (private destructor) it is not possible. In which case you may */ |
379 | /* use these instead. */ |
380 | /************************************************************************/ |
381 | #define BS_PVT_DELETE(T, ptr) \ |
382 | (ptr)->~T(); \ |
383 | MemoryAllocator<GenAlloc>::free(ptr); |
384 | |
385 | #define BS_PVT_DELETE_A(T, ptr, Alloc) \ |
386 | (ptr)->~T(); \ |
387 | MemoryAllocator<Alloc>::free(ptr); |
388 | |
389 | /** @} */ |
390 | /** @addtogroup Internal-Utility |
391 | * @{ |
392 | */ |
393 | |
394 | /** @addtogroup Memory-Internal |
395 | * @{ |
396 | */ |
397 | |
398 | /** Allocator for the standard library that internally uses bsf memory allocator. */ |
399 | template <class T, class Alloc = GenAlloc> |
400 | class StdAlloc |
401 | { |
402 | public: |
403 | using value_type = T; |
404 | using pointer = value_type*; |
405 | using const_pointer = const value_type*; |
406 | using reference = value_type&; |
407 | using const_reference = const value_type&; |
408 | using size_type = std::size_t; |
409 | using difference_type = std::ptrdiff_t; |
410 | |
411 | constexpr StdAlloc() = default; |
412 | constexpr StdAlloc(StdAlloc&&) = default; |
413 | constexpr StdAlloc(const StdAlloc&) = default; |
414 | template<class U, class Alloc2> constexpr StdAlloc(const StdAlloc<U, Alloc2>&) { }; |
415 | template<class U, class Alloc2> constexpr bool operator==(const StdAlloc<U, Alloc2>&) const noexcept { return true; } |
416 | template<class U, class Alloc2> constexpr bool operator!=(const StdAlloc<U, Alloc2>&) const noexcept { return false; } |
417 | |
418 | template<class U> class rebind { public: using other = StdAlloc<U, Alloc>; }; |
419 | |
420 | /** Allocate but don't initialize number elements of type T. */ |
421 | static T* allocate(const size_t num) |
422 | { |
423 | if (num == 0) |
424 | return nullptr; |
425 | |
426 | if (num > std::numeric_limits<size_t>::max() / sizeof(T)) |
427 | return nullptr; // Error |
428 | |
429 | void* const pv = bs_alloc<Alloc>(num * sizeof(T)); |
430 | if (!pv) |
431 | return nullptr; // Error |
432 | |
433 | return static_cast<T*>(pv); |
434 | } |
435 | |
436 | /** Deallocate storage p of deleted elements. */ |
437 | static void deallocate(pointer p, size_type) |
438 | { |
439 | bs_free<Alloc>(p); |
440 | } |
441 | |
442 | static constexpr size_t max_size() { return std::numeric_limits<size_type>::max() / sizeof(T); } |
443 | static constexpr void destroy(pointer p) { p->~T(); } |
444 | |
445 | template<class... Args> |
446 | static void construct(pointer p, Args&&... args) { new(p) T(std::forward<Args>(args)...); } |
447 | }; |
448 | |
449 | /** @} */ |
450 | /** @} */ |
451 | } |
452 | |
453 | #include "Allocators/BsStackAlloc.h" |
454 | #include "Allocators/BsFreeAlloc.h" |
455 | #include "Allocators/BsFrameAlloc.h" |
456 | #include "Allocators/BsStaticAlloc.h" |
457 | #include "Allocators/BsMemAllocProfiler.h" |
458 | |