1 /**************************************************************************
3 * Copyright 2013-2014 RAD Game Tools and Valve Software
4 * Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC
7 * Permission is hereby granted, free of charge, to any person obtaining a copy
8 * of this software and associated documentation files (the "Software"), to deal
9 * in the Software without restriction, including without limitation the rights
10 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 * copies of the Software, and to permit persons to whom the Software is
12 * furnished to do so, subject to the following conditions:
14 * The above copyright notice and this permission notice shall be included in
15 * all copies or substantial portions of the Software.
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25 **************************************************************************/
30 #include "vogl_core.h"
32 // Set to 1 to enable allocation debugging using rmalloc.c (much slower)
33 #ifndef VOGL_MALLOC_DEBUGGING
34 #define VOGL_MALLOC_DEBUGGING 0
37 #ifndef VOGL_MIN_ALLOC_ALIGNMENT
38 // malloc() is 8 bytes for glibc in 32-bit, and 16 in 64-bit
39 // must be at least sizeof(uint32) * 2
40 #define VOGL_MIN_ALLOC_ALIGNMENT sizeof(void *) * 2
43 #define VOGL_FUNCTIONIZE(a, b) a(b)
44 #define VOGL_STRINGIZE(a) #a
45 #define VOGL_INT2STRING(i) VOGL_FUNCTIONIZE(VOGL_STRINGIZE, i)
46 #define VOGL_FILE_POS_STRING __FILE__ ":" VOGL_INT2STRING(__LINE__)
48 #if VOGL_MALLOC_DEBUGGING
49 #define VOGL_MALLOC_FILE_LEN_STRING VOGL_FILE_POS_STRING
51 #define VOGL_MALLOC_FILE_LEN_STRING NULL
54 extern "C" void *vogl_realloc(const char *pFile_line, void *p, size_t new_size);
58 #if VOGL_64BIT_POINTERS
59 const uint64_t VOGL_MAX_POSSIBLE_HEAP_BLOCK_SIZE = 0x1000000000ULL; // 64GB should be big enough for anybody!
61 const uint32 VOGL_MAX_POSSIBLE_HEAP_BLOCK_SIZE = 0x7FFF0000U; // ~1.999GB
63 void vogl_init_heap();
65 void *vogl_tracked_malloc(const char *pFile_line, size_t size, size_t *pActual_size = NULL);
66 void *vogl_tracked_realloc(const char *pFile_line, void *p, size_t size, size_t *pActual_size = NULL);
67 void *vogl_tracked_calloc(const char *pFile_line, size_t count, size_t size, size_t *pActual_size = NULL);
68 void vogl_tracked_free(const char *pFile_line, void *p);
69 void vogl_tracked_check_heap(const char *pFile_line);
70 void vogl_tracked_print_stats(const char *pFile_line);
72 // C-style malloc/free/etc.
73 #define vogl_malloc(...) vogl::vogl_tracked_malloc(VOGL_MALLOC_FILE_LEN_STRING, __VA_ARGS__)
74 #define vogl_realloc(...) vogl::vogl_tracked_realloc(VOGL_MALLOC_FILE_LEN_STRING, __VA_ARGS__)
75 #define vogl_calloc(...) vogl::vogl_tracked_calloc(VOGL_MALLOC_FILE_LEN_STRING, __VA_ARGS__)
76 #define vogl_free(...) vogl::vogl_tracked_free(VOGL_MALLOC_FILE_LEN_STRING, __VA_ARGS__)
77 #define vogl_check_heap() vogl::vogl_tracked_check_heap(VOGL_FILE_POS_STRING)
78 #define vogl_print_heap_stats() vogl::vogl_tracked_print_stats(VOGL_FILE_POS_STRING)
80 size_t vogl_msize(void *p);
82 __attribute__((noreturn)) void vogl_mem_error(const char *pMsg, const char *pFile_line);
84 // C++ new/delete wrappers that automatically pass in the file/line
86 inline T *vogl_tracked_new(const char *pFile_line)
88 T *p = static_cast<T *>(vogl_tracked_malloc(pFile_line, sizeof(T)));
89 if (VOGL_IS_SCALAR_TYPE(T))
91 return helpers::construct(p);
94 template <typename T, typename A>
95 inline T *vogl_tracked_new(const char *pFile_line, const A &init0)
97 T *p = static_cast<T *>(vogl_tracked_malloc(pFile_line, sizeof(T)));
98 return new (static_cast<void *>(p)) T(init0);
101 template <typename T, typename A>
102 inline T *vogl_tracked_new(const char *pFile_line, A &init0)
104 T *p = static_cast<T *>(vogl_tracked_malloc(pFile_line, sizeof(T)));
105 return new (static_cast<void *>(p)) T(init0);
108 template <typename T, typename A, typename B>
109 inline T *vogl_tracked_new(const char *pFile_line, const A &init0, const B &init1)
111 T *p = static_cast<T *>(vogl_tracked_malloc(pFile_line, sizeof(T)));
112 return new (static_cast<void *>(p)) T(init0, init1);
115 template <typename T, typename A, typename B, typename C>
116 inline T *vogl_tracked_new(const char *pFile_line, const A &init0, const B &init1, const C &init2)
118 T *p = static_cast<T *>(vogl_tracked_malloc(pFile_line, sizeof(T)));
119 return new (static_cast<void *>(p)) T(init0, init1, init2);
122 template <typename T, typename A, typename B, typename C, typename D>
123 inline T *vogl_tracked_new(const char *pFile_line, const A &init0, const B &init1, const C &init2, const D &init3)
125 T *p = static_cast<T *>(vogl_tracked_malloc(pFile_line, sizeof(T)));
126 return new (static_cast<void *>(p)) T(init0, init1, init2, init3);
129 template <typename T, typename A, typename B, typename C, typename D, typename E>
130 inline T *vogl_tracked_new(const char *pFile_line, const A &init0, const B &init1, const C &init2, const D &init3, const E &init4)
132 T *p = static_cast<T *>(vogl_tracked_malloc(pFile_line, sizeof(T)));
133 return new (static_cast<void *>(p)) T(init0, init1, init2, init3, init4);
136 template <typename T, typename A, typename B, typename C, typename D, typename E, typename F>
137 inline T *vogl_tracked_new(const char *pFile_line, const A &init0, const B &init1, const C &init2, const D &init3, const E &init4, const F &init5)
139 T *p = static_cast<T *>(vogl_tracked_malloc(pFile_line, sizeof(T)));
140 return new (static_cast<void *>(p)) T(init0, init1, init2, init3, init4, init5);
143 template <typename T, typename A, typename B, typename C, typename D, typename E, typename F, typename G>
144 inline T *vogl_tracked_new(const char *pFile_line, const A &init0, const B &init1, const C &init2, const D &init3, const E &init4, const F &init5, const G &init6)
146 T *p = static_cast<T *>(vogl_tracked_malloc(pFile_line, sizeof(T)));
147 return new (static_cast<void *>(p)) T(init0, init1, init2, init3, init4, init5, init6);
150 template <typename T, typename A, typename B, typename C, typename D, typename E, typename F, typename G, typename H>
151 inline T *vogl_tracked_new(const char *pFile_line, const A &init0, const B &init1, const C &init2, const D &init3, const E &init4, const F &init5, const G &init6, const H &init7)
153 T *p = static_cast<T *>(vogl_tracked_malloc(pFile_line, sizeof(T)));
154 return new (static_cast<void *>(p)) T(init0, init1, init2, init3, init4, init5, init6, init7);
157 template <typename T, typename A, typename B, typename C, typename D, typename E, typename F, typename G, typename H, typename I>
158 inline T *vogl_tracked_new(const char *pFile_line, const A &init0, const B &init1, const C &init2, const D &init3, const E &init4, const F &init5, const G &init6, const H &init7, const I &init8)
160 T *p = static_cast<T *>(vogl_tracked_malloc(pFile_line, sizeof(T)));
161 return new (static_cast<void *>(p)) T(init0, init1, init2, init3, init4, init5, init6, init7, init8);
164 template <typename T, typename A, typename B, typename C, typename D, typename E, typename F, typename G, typename H, typename I, typename J>
165 inline T *vogl_tracked_new(const char *pFile_line, const A &init0, const B &init1, const C &init2, const D &init3, const E &init4, const F &init5, const G &init6, const H &init7, const I &init8, const J &init9)
167 T *p = static_cast<T *>(vogl_tracked_malloc(pFile_line, sizeof(T)));
168 return new (static_cast<void *>(p)) T(init0, init1, init2, init3, init4, init5, init6, init7, init8, init9);
171 template <typename T, typename A, typename B, typename C, typename D, typename E, typename F, typename G, typename H, typename I, typename J, typename K>
172 inline T *vogl_tracked_new(const char *pFile_line, const A &init0, const B &init1, const C &init2, const D &init3, const E &init4, const F &init5, const G &init6, const H &init7, const I &init8, const J &init9, const K &init10)
174 T *p = static_cast<T *>(vogl_tracked_malloc(pFile_line, sizeof(T)));
175 return new (static_cast<void *>(p)) T(init0, init1, init2, init3, init4, init5, init6, init7, init8, init9, init10);
178 template <typename T, typename A, typename B, typename C, typename D, typename E, typename F, typename G, typename H, typename I, typename J, typename K, typename L>
179 inline T *vogl_tracked_new(const char *pFile_line, const A &init0, const B &init1, const C &init2, const D &init3, const E &init4, const F &init5, const G &init6, const H &init7, const I &init8, const J &init9, const K &init10, const L &init11)
181 T *p = static_cast<T *>(vogl_tracked_malloc(pFile_line, sizeof(T)));
182 return new (static_cast<void *>(p)) T(init0, init1, init2, init3, init4, init5, init6, init7, init8, init9, init10, init11);
185 template <typename T, typename A, typename B, typename C, typename D, typename E, typename F, typename G, typename H, typename I, typename J, typename K, typename L, typename M>
186 inline T *vogl_tracked_new(const char *pFile_line, const A &init0, const B &init1, const C &init2, const D &init3, const E &init4, const F &init5, const G &init6, const H &init7, const I &init8, const J &init9, const K &init10, const L &init11, const M &init12)
188 T *p = static_cast<T *>(vogl_tracked_malloc(pFile_line, sizeof(T)));
189 return new (static_cast<void *>(p)) T(init0, init1, init2, init3, init4, init5, init6, init7, init8, init9, init10, init11, init12);
192 template <typename T, typename A, typename B, typename C, typename D, typename E, typename F, typename G, typename H, typename I, typename J, typename K, typename L, typename M, typename N>
193 inline T *vogl_tracked_new(const char *pFile_line, const A &init0, const B &init1, const C &init2, const D &init3, const E &init4, const F &init5, const G &init6, const H &init7, const I &init8, const J &init9, const K &init10, const L &init11, const M &init12, const N &init13)
195 T *p = static_cast<T *>(vogl_tracked_malloc(pFile_line, sizeof(T)));
196 return new (static_cast<void *>(p)) T(init0, init1, init2, init3, init4, init5, init6, init7, init8, init9, init10, init11, init12, init13);
199 #define vogl_new(type, ...) vogl::vogl_tracked_new<type>(VOGL_MALLOC_FILE_LEN_STRING, ##__VA_ARGS__)
201 template <typename T>
202 inline void vogl_tracked_delete(const char *pFile_line, T *p)
206 if (!VOGL_IS_SCALAR_TYPE(T))
208 helpers::destruct(p);
210 vogl_tracked_free(pFile_line, p);
214 #define vogl_delete(p) vogl::vogl_tracked_delete(VOGL_MALLOC_FILE_LEN_STRING, p)
216 // num is (obviously) limited to cUINT32_MAX, but the total allocated size in bytes can be > than this on x64
217 template <typename T>
218 inline T *vogl_tracked_new_array(const char *pFile_line, uint32 num)
226 uint64_t total = VOGL_MIN_ALLOC_ALIGNMENT + static_cast<uint64_t>(sizeof(T)) * num;
227 if ((total != static_cast<size_t>(total)) || (total > VOGL_MAX_POSSIBLE_HEAP_BLOCK_SIZE))
229 vogl_mem_error("vogl_new_array: Array too large!", pFile_line);
233 uint8 *q = static_cast<uint8 *>(vogl_tracked_malloc(pFile_line, static_cast<size_t>(total)));
235 T *p = reinterpret_cast<T *>(q + VOGL_MIN_ALLOC_ALIGNMENT);
237 reinterpret_cast<uint32 *>(p)[-1] = num;
238 reinterpret_cast<uint32 *>(p)[-2] = ~num;
240 if (!VOGL_IS_SCALAR_TYPE(T))
242 helpers::construct_array(p, num);
248 // num is limited to cUINT32_MAX
249 #define vogl_new_array(type, num) vogl::vogl_tracked_new_array<type>(VOGL_MALLOC_FILE_LEN_STRING, num)
251 template <typename T>
252 inline void vogl_tracked_delete_array(const char *pFile_line, T *p)
256 VOGL_ASSUME(VOGL_MIN_ALLOC_ALIGNMENT >= sizeof(uint32) * 2);
257 VOGL_ASSERT((uint64_t)p >= sizeof(uint32) * 2);
259 const uint32 num = reinterpret_cast<uint32 *>(p)[-1];
260 const uint32 num_check = reinterpret_cast<uint32 *>(p)[-2];
261 if ((num) && (num == ~num_check))
263 if (!VOGL_IS_SCALAR_TYPE(T))
265 helpers::destruct_array(p, num);
268 vogl_tracked_free(pFile_line, reinterpret_cast<uint8 *>(p) - VOGL_MIN_ALLOC_ALIGNMENT);
272 vogl_mem_error("Invalid ptr in call vogl_delete_array", pFile_line);
277 #define vogl_delete_array(p) vogl::vogl_tracked_delete_array(VOGL_MALLOC_FILE_LEN_STRING, p)
279 // Returns the actual size of the allocated block pointed to by p in BYTES, not objects.
280 // p must have been allocated using vogl_new_array.
281 inline size_t vogl_msize_array(void *p)
283 VOGL_ASSUME(VOGL_MIN_ALLOC_ALIGNMENT >= sizeof(uint32) * 2);
284 VOGL_ASSERT((uint64_t)p >= sizeof(uint32) * 2);
287 const uint32 num = reinterpret_cast<uint32 *>(p)[-1];
288 const uint32 num_check = reinterpret_cast<uint32 *>(p)[-2];
289 if ((num) && (num == ~num_check))
291 size_t actual_size = vogl_msize(reinterpret_cast<uint8 *>(p) - VOGL_MIN_ALLOC_ALIGNMENT);
292 if (actual_size <= VOGL_MIN_ALLOC_ALIGNMENT)
294 vogl_mem_error("vogl_msize() return value was too small in vogl_msize_array", VOGL_FILE_POS_STRING);
297 return actual_size - VOGL_MIN_ALLOC_ALIGNMENT;
301 vogl_mem_error("Invalid ptr in call vogl_msize_array", VOGL_FILE_POS_STRING);
310 #define VOGL_DEFINE_NEW_DELETE \
311 void *operator new(size_t size) \
313 void *p = vogl_malloc(size); \
315 vogl_mem_error("new: Out of memory!", VOGL_FILE_POS_STRING); \
318 void *operator new [](size_t size) { \
319 void* p = vogl_malloc(size); \
321 vogl_mem_error("new[]: Out of memory!", VOGL_FILE_POS_STRING); \
322 return p; } void operator delete(void *p_block) \
324 vogl::vogl_free(p_block); \
326 void operator delete [](void *p_block) \
328 vogl::vogl_free(p_block); \