]> git.cworth.org Git - vogl/blob - src/voglcore/vogl_mem.h
Initial vogl checkin
[vogl] / src / voglcore / vogl_mem.h
1 /**************************************************************************
2  *
3  * Copyright 2013-2014 RAD Game Tools and Valve Software
4  * Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC
5  * All Rights Reserved.
6  *
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:
13  *
14  * The above copyright notice and this permission notice shall be included in
15  * all copies or substantial portions of the Software.
16  *
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
23  * THE SOFTWARE.
24  *
25  **************************************************************************/
26
27 // File: vogl_mem.h
28 #pragma once
29
30 #include "vogl_core.h"
31
32 // Set to 1 to enable allocation debugging using rmalloc.c (much slower)
33 #ifndef VOGL_MALLOC_DEBUGGING
34 #define VOGL_MALLOC_DEBUGGING 0
35 #endif
36
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
41 #endif
42
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__)
47
48 #if VOGL_MALLOC_DEBUGGING
49 #define VOGL_MALLOC_FILE_LEN_STRING VOGL_FILE_POS_STRING
50 #else
51 #define VOGL_MALLOC_FILE_LEN_STRING NULL
52 #endif
53
54 extern "C" void *vogl_realloc(const char *pFile_line, void *p, size_t new_size);
55
56 namespace vogl
57 {
58 #if VOGL_64BIT_POINTERS
59     const uint64_t VOGL_MAX_POSSIBLE_HEAP_BLOCK_SIZE = 0x1000000000ULL; // 64GB should be big enough for anybody!
60 #else
61     const uint32 VOGL_MAX_POSSIBLE_HEAP_BLOCK_SIZE = 0x7FFF0000U; // ~1.999GB
62 #endif
63     void vogl_init_heap();
64
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);
71
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)
79
80     size_t vogl_msize(void *p);
81
82     __attribute__((noreturn)) void vogl_mem_error(const char *pMsg, const char *pFile_line);
83
84     // C++ new/delete wrappers that automatically pass in the file/line
85     template <typename T>
86     inline T *vogl_tracked_new(const char *pFile_line)
87     {
88         T *p = static_cast<T *>(vogl_tracked_malloc(pFile_line, sizeof(T)));
89         if (VOGL_IS_SCALAR_TYPE(T))
90             return p;
91         return helpers::construct(p);
92     }
93
94     template <typename T, typename A>
95     inline T *vogl_tracked_new(const char *pFile_line, const A &init0)
96     {
97         T *p = static_cast<T *>(vogl_tracked_malloc(pFile_line, sizeof(T)));
98         return new (static_cast<void *>(p)) T(init0);
99     }
100
101     template <typename T, typename A>
102     inline T *vogl_tracked_new(const char *pFile_line, A &init0)
103     {
104         T *p = static_cast<T *>(vogl_tracked_malloc(pFile_line, sizeof(T)));
105         return new (static_cast<void *>(p)) T(init0);
106     }
107
108     template <typename T, typename A, typename B>
109     inline T *vogl_tracked_new(const char *pFile_line, const A &init0, const B &init1)
110     {
111         T *p = static_cast<T *>(vogl_tracked_malloc(pFile_line, sizeof(T)));
112         return new (static_cast<void *>(p)) T(init0, init1);
113     }
114
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)
117     {
118         T *p = static_cast<T *>(vogl_tracked_malloc(pFile_line, sizeof(T)));
119         return new (static_cast<void *>(p)) T(init0, init1, init2);
120     }
121
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)
124     {
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);
127     }
128
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)
131     {
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);
134     }
135
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)
138     {
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);
141     }
142
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)
145     {
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);
148     }
149
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)
152     {
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);
155     }
156
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)
159     {
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);
162     }
163
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)
166     {
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);
169     }
170
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)
173     {
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);
176     }
177
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)
180     {
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);
183     }
184
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)
187     {
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);
190     }
191
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)
194     {
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);
197     }
198
199 #define vogl_new(type, ...) vogl::vogl_tracked_new<type>(VOGL_MALLOC_FILE_LEN_STRING, ##__VA_ARGS__)
200
201     template <typename T>
202     inline void vogl_tracked_delete(const char *pFile_line, T *p)
203     {
204         if (p)
205         {
206             if (!VOGL_IS_SCALAR_TYPE(T))
207             {
208                 helpers::destruct(p);
209             }
210             vogl_tracked_free(pFile_line, p);
211         }
212     }
213
214 #define vogl_delete(p) vogl::vogl_tracked_delete(VOGL_MALLOC_FILE_LEN_STRING, p)
215
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)
219     {
220         if (!num)
221         {
222             VOGL_ASSERT_ALWAYS;
223             num = 1;
224         }
225
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))
228         {
229             vogl_mem_error("vogl_new_array: Array too large!", pFile_line);
230             return NULL;
231         }
232
233         uint8 *q = static_cast<uint8 *>(vogl_tracked_malloc(pFile_line, static_cast<size_t>(total)));
234
235         T *p = reinterpret_cast<T *>(q + VOGL_MIN_ALLOC_ALIGNMENT);
236
237         reinterpret_cast<uint32 *>(p)[-1] = num;
238         reinterpret_cast<uint32 *>(p)[-2] = ~num;
239
240         if (!VOGL_IS_SCALAR_TYPE(T))
241         {
242             helpers::construct_array(p, num);
243         }
244
245         return p;
246     }
247
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)
250
251     template <typename T>
252     inline void vogl_tracked_delete_array(const char *pFile_line, T *p)
253     {
254         if (p)
255         {
256             VOGL_ASSUME(VOGL_MIN_ALLOC_ALIGNMENT >= sizeof(uint32) * 2);
257             VOGL_ASSERT((uint64_t)p >= sizeof(uint32) * 2);
258
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))
262             {
263                 if (!VOGL_IS_SCALAR_TYPE(T))
264                 {
265                     helpers::destruct_array(p, num);
266                 }
267
268                 vogl_tracked_free(pFile_line, reinterpret_cast<uint8 *>(p) - VOGL_MIN_ALLOC_ALIGNMENT);
269             }
270             else
271             {
272                 vogl_mem_error("Invalid ptr in call vogl_delete_array", pFile_line);
273             }
274         }
275     }
276
277 #define vogl_delete_array(p) vogl::vogl_tracked_delete_array(VOGL_MALLOC_FILE_LEN_STRING, p)
278
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)
282     {
283         VOGL_ASSUME(VOGL_MIN_ALLOC_ALIGNMENT >= sizeof(uint32) * 2);
284         VOGL_ASSERT((uint64_t)p >= sizeof(uint32) * 2);
285         if (p)
286         {
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))
290             {
291                 size_t actual_size = vogl_msize(reinterpret_cast<uint8 *>(p) - VOGL_MIN_ALLOC_ALIGNMENT);
292                 if (actual_size <= VOGL_MIN_ALLOC_ALIGNMENT)
293                 {
294                     vogl_mem_error("vogl_msize() return value was too small in vogl_msize_array", VOGL_FILE_POS_STRING);
295                     return 0;
296                 }
297                 return actual_size - VOGL_MIN_ALLOC_ALIGNMENT;
298             }
299             else
300             {
301                 vogl_mem_error("Invalid ptr in call vogl_msize_array", VOGL_FILE_POS_STRING);
302             }
303         }
304
305         return 0;
306     }
307
308 } // namespace vogl
309
310 #define VOGL_DEFINE_NEW_DELETE                                               \
311     void *operator new(size_t size)                                            \
312     {                                                                          \
313         void *p = vogl_malloc(size);                                         \
314         if (!p)                                                                \
315             vogl_mem_error("new: Out of memory!", VOGL_FILE_POS_STRING);   \
316         return p;                                                              \
317     }                                                                          \
318     void *operator new [](size_t size) { \
319                 void* p = vogl_malloc(size); \
320       if (!p) \
321                         vogl_mem_error("new[]: Out of memory!", VOGL_FILE_POS_STRING); \
322       return p; } void operator delete(void *p_block) \
323     {                                                                          \
324         vogl::vogl_free(p_block);                                          \
325     }                                                                          \
326     void operator delete [](void *p_block)                                     \
327     { \
328       vogl::vogl_free(p_block);                                   \
329     }