cleanup memory file
[vg.git] / vg_mem.h
1 #ifndef VG_MEM_H
2 #define VG_MEM_H
3
4 #include "vg.h"
5 #include "vg_stdint.h"
6 #include "vg_platform.h"
7
8 #include <stdlib.h>
9 #include <malloc.h>
10
11 VG_STATIC void vg_print_backtrace(void);
12
13 #define VG_MAX_ALLOCATIONS 128
14 #define VG_FUZZ_ALLOCATIONS
15
16 typedef struct vg_linear_allocator vg_linear_allocator;
17 typedef struct vg_allocation_meta vg_allocation_meta;
18
19 struct{
20 void *rtmemory,
21 *scratch;
22
23 int use_libc_malloc;
24 u32 quota;
25 }
26 static vg_mem;
27
28 struct vg_allocation_meta
29 {
30 const char *name;
31 void *data;
32 u32 size;
33 enum allocation_type{
34 k_allocation_type_block = 0,
35 k_allocation_type_linear = 1
36 }
37 type;
38 };
39
40 #define VG_MEMORY_SYSTEM 0x1 /* systems memory, slow and low counts */
41 #define VG_MEMORY_REALTIME 0x2 /* per-frame. no max allocs, only size. fast */
42
43 /* systems memory cannot be declared inside realtime memory regions */
44
45 /*
46 * Stored just behind the array. 32 bytes.
47 */
48 #pragma pack(push,1)
49 struct vg_linear_allocator
50 {
51 u32 size;
52 u32 cur;
53 u16 allocation_count;
54 u16 flags;
55 u32 last_alloc_size;
56 void *last_alloc;
57 vg_allocation_meta *alloc_table;
58
59 #ifdef _WIN32
60 /* 32 bit pointers! */
61 u8 padding[ 8 ];
62 #endif
63 };
64 #pragma pack(pop)
65
66 VG_STATIC u32 vg_align8( u32 s );
67 VG_STATIC u32 vg_align4( u32 s );
68
69 /* allocate something from a linear allocator */
70 __attribute__((warn_unused_result))
71 VG_STATIC void *_vg_linear_alloc( void *buffer, u32 size,
72 const char *constr_name );
73
74 /* resize latest block of memory from linear */
75 __attribute__((warn_unused_result))
76 VG_STATIC void *vg_linear_resize( void *buffer, void *data, u32 newsize );
77
78 /* its possible to delete just the last item */
79 VG_STATIC void vg_linear_del( void *buffer, void *data );
80
81 /* extend latest block of memory from linear */
82 __attribute__((warn_unused_result))
83 VG_STATIC void *_vg_linear_extend( void *buffer, void *data, u32 extra,
84 const char *constr_name );
85
86 /* get the current usage of allocator */
87 VG_STATIC u32 vg_linear_get_cur( void *buffer );
88
89 /* get the capacity of allocator. */
90 VG_STATIC u32 vg_linear_get_capacity( void *buffer );
91
92 /* get the remaining size of the allocator */
93 VG_STATIC u32 vg_linear_remaining( void *buffer );
94
95 /* yeet all memory from linear allocator */
96 VG_STATIC void vg_linear_clear( void *buffer );
97
98 /* request all the memory we need in advance */
99 VG_STATIC void vg_set_mem_quota( u32 size );
100
101 /* essentially init() */
102 VG_STATIC void vg_alloc_quota(void);
103
104 /* print out tree of current memory used. only works with libc mode */
105 VG_STATIC void vg_mem_log( void *lin_alloc, int depth, const char *name );
106
107 #define VG_MEM_MCSTR(S) VG_MEM_MCSTR2(S)
108 #define VG_MEM_MCSTR2(S) #S
109
110 #define vg_linear_alloc(...) \
111 _vg_linear_alloc( __VA_ARGS__, __FILE__":"VG_MEM_MCSTR(__LINE__) )
112 #define vg_linear_extend(...) \
113 _vg_linear_extend( __VA_ARGS__, __FILE__":"VG_MEM_MCSTR(__LINE__) )
114 #define vg_create_linear_allocator(...) \
115 _vg_create_linear_allocator( __VA_ARGS__, __FILE__":"VG_MEM_MCSTR(__LINE__) )
116
117 /* implementation
118 * ----------------------------------------
119 */
120
121 VG_STATIC void vg_fatal_error( const char *fmt, ... );
122 VG_STATIC void vg_error(const char *fmt, ...);
123 VG_STATIC void vg_info(const char *fmt, ...);
124
125 VG_STATIC u32 vg_align8( u32 s )
126 {
127 u32 m = (s + 7) >> 3;
128 return m << 3;
129 }
130
131 VG_STATIC u32 vg_align4( u32 s )
132 {
133 u32 m = (s + 3) >> 2;
134 return m << 2;
135 }
136
137 /* Returns allocator structure from data pointer */
138 static vg_linear_allocator *vg_linear_header( void *data )
139 {
140 vg_linear_allocator *ptr = data;
141 ptr --;
142
143 return ptr;
144 }
145
146 /* allocate something from a linear allocator */
147 __attribute__((warn_unused_result))
148 VG_STATIC void *_vg_linear_alloc( void *buffer, u32 size,
149 const char *constr_name )
150 {
151 if( size % 8 ){
152 vg_error( "alloc(%u) is not 8 byte aligned\n", size );
153 vg_print_backtrace();
154 size = vg_align8( size );
155 }
156 #ifdef _WIN32
157 if( ((u32)buffer) % 8 ){
158 #else
159 if( ((u64)buffer) % 8 ){
160 #endif
161 vg_fatal_error( "unaligned buffer (%p)", buffer );
162 }
163
164 vg_linear_allocator *alloc = vg_linear_header( buffer );
165
166 if( (alloc->cur + size) > alloc->size ){
167 vg_fatal_error( "linear allocator overflow (%u + %u > %u)\n",
168 alloc->cur, size, alloc->size );
169 }
170
171 if( alloc->flags & VG_MEMORY_SYSTEM )
172 if( (alloc->allocation_count + 1) > VG_MAX_ALLOCATIONS )
173 vg_fatal_error( "Max linear allocations reached" );
174
175 void *data;
176
177 if( vg_mem.use_libc_malloc && (alloc->flags & VG_MEMORY_SYSTEM) ){
178 data = malloc( size );
179
180 vg_allocation_meta *meta = &alloc->alloc_table[ alloc->allocation_count ];
181 meta->type = k_allocation_type_block;
182 meta->data = data;
183 meta->size = size;
184 meta->name = constr_name;
185 }
186 else{
187 data = buffer + alloc->cur;
188 }
189
190 u8 *bytes = data;
191 for( u32 i=0; i<size; i++ ){
192 bytes[i] = 0xfe;
193 }
194
195 alloc->allocation_count ++;
196 alloc->last_alloc = data;
197 alloc->last_alloc_size = size;
198 alloc->cur += size;
199
200 #ifdef _WIN32
201 if( ((u32)data) % 8 ){
202 #else
203 if( ((u64)data) % 8 ){
204 #endif
205 vg_fatal_error( "unaligned" );
206 }
207
208 return data;
209 }
210
211 /* resize latest block of memory from linear */
212 __attribute__((warn_unused_result))
213 VG_STATIC void *vg_linear_resize( void *buffer, void *data, u32 newsize )
214 {
215 vg_linear_allocator *alloc = vg_linear_header( buffer );
216
217 if( newsize % 8 ){
218 vg_error( "alloc(%u) is not 8 byte aligned\n", newsize );
219 vg_print_backtrace();
220 newsize = vg_align8( newsize );
221 }
222
223 if( alloc->last_alloc != data )
224 vg_fatal_error( "This block has been fixed!" );
225
226 if( (alloc->cur - alloc->last_alloc_size + newsize) > alloc->size )
227 vg_fatal_error( "Cannot resize, overflow" );
228
229 alloc->cur -= alloc->last_alloc_size;
230 alloc->cur += newsize;
231 alloc->last_alloc_size = newsize;
232
233 if( vg_mem.use_libc_malloc && (alloc->flags & VG_MEMORY_SYSTEM) ){
234 data = realloc( data, newsize );
235 if( !data )
236 vg_fatal_error( "realloc failed" );
237
238 alloc->alloc_table[ alloc->allocation_count-1 ].data = data;
239 alloc->last_alloc = data;
240 return data;
241 }
242 else{
243 return data;
244 }
245 }
246
247 /* its possible to delete just the last item */
248 VG_STATIC void vg_linear_del( void *buffer, void *data )
249 {
250 vg_linear_allocator *alloc = vg_linear_header( buffer );
251
252 if( alloc->last_alloc != data ){
253 vg_fatal_error( "This block has been fixed! Last alloc: %p, this: %p\n",
254 alloc->last_alloc, data );
255 }
256
257 if( vg_mem.use_libc_malloc && (alloc->flags & VG_MEMORY_SYSTEM) ){
258 vg_allocation_meta *meta = &alloc->alloc_table[alloc->allocation_count-1];
259 if( meta->type == k_allocation_type_linear )
260 vg_fatal_error( "Cannot free a linear allocator in this conext" );
261
262 free( data );
263 }
264
265 alloc->allocation_count --;
266 alloc->cur -= alloc->last_alloc_size;
267 alloc->last_alloc = NULL;
268 alloc->last_alloc_size = 0;
269 }
270
271 /* extend latest block of memory from linear */
272 __attribute__((warn_unused_result))
273 VG_STATIC void *_vg_linear_extend( void *buffer, void *data, u32 extra,
274 const char *constr_name )
275 {
276 if( !data )
277 return _vg_linear_alloc( buffer, extra, constr_name );
278
279 vg_linear_allocator *alloc = vg_linear_header( buffer );
280
281 if( alloc->last_alloc != data )
282 vg_fatal_error( "This block has been fixed!" );
283
284 u32 new_size = alloc->last_alloc_size + extra;
285 return vg_linear_resize( buffer, data, new_size );
286 }
287
288 /* get the current usage of allocator */
289 VG_STATIC u32 vg_linear_get_cur( void *buffer )
290 {
291 vg_linear_allocator *alloc = vg_linear_header( buffer );
292 return alloc->cur;
293 }
294
295 /* get the capacity of allocator. */
296 VG_STATIC u32 vg_linear_get_capacity( void *buffer )
297 {
298 vg_linear_allocator *alloc = vg_linear_header( buffer );
299 return alloc->size;
300 }
301
302 /* get the remaining size of the allocator */
303 VG_STATIC u32 vg_linear_remaining( void *buffer )
304 {
305 vg_linear_allocator *alloc = vg_linear_header( buffer );
306 return alloc->size - alloc->cur;
307 }
308
309 /* yeet all memory from linear allocator */
310 VG_STATIC void vg_linear_clear( void *buffer )
311 {
312 vg_linear_allocator *alloc = vg_linear_header( buffer );
313
314 /* libc mode we recursively free any allocations made */
315 if( vg_mem.use_libc_malloc && (alloc->flags & VG_MEMORY_SYSTEM) ){
316 for( u32 i=0; i<alloc->allocation_count; i++ ){
317 vg_allocation_meta *meta = &alloc->alloc_table[i];
318
319 if( meta->type == k_allocation_type_block ){
320 free( meta->data );
321 }
322 else{
323 vg_linear_clear( meta->data );
324 vg_linear_allocator *sub = vg_linear_header( meta->data );
325
326 free( sub->alloc_table );
327 free( sub );
328 }
329 }
330 }
331
332 alloc->last_alloc = NULL;
333 alloc->last_alloc_size = 0;
334 alloc->allocation_count = 0;
335 alloc->cur = 0;
336 }
337
338 /* allocate a FIXED SIZE linear allocator */
339 VG_STATIC void *_vg_create_linear_allocator( void *lin_alloc, u32 size,
340 u16 flags, const char *constr_name)
341 {
342 assert( sizeof( vg_linear_allocator ) == 32 );
343
344 vg_linear_allocator *header;
345 u32 block_size = size + sizeof(vg_linear_allocator);
346
347 /* Creating it inside an existing one */
348 if( lin_alloc ){
349 vg_linear_allocator *alloc = vg_linear_header( lin_alloc );
350
351 if( alloc->cur + block_size > alloc->size )
352 vg_fatal_error( "Out of memory" );
353
354 if( alloc->allocation_count + 1 > VG_MAX_ALLOCATIONS )
355 vg_fatal_error( "Max allocations in linear allocator" );
356
357 if( (flags && VG_MEMORY_SYSTEM) && (alloc->flags & VG_MEMORY_REALTIME) )
358 vg_fatal_error( "Cannot declare realtime allocator inside systems"
359 " allocator" );
360
361 if( vg_mem.use_libc_malloc ){
362 vg_allocation_meta *meta =
363 &alloc->alloc_table[ alloc->allocation_count ];
364
365 if( flags & VG_MEMORY_REALTIME )
366 header = malloc( block_size );
367 else
368 header = malloc( sizeof(vg_linear_allocator) );
369
370 meta->data = header+1;
371 meta->type = k_allocation_type_linear;
372 meta->size = size;
373 meta->name = constr_name;
374 }
375 else{
376 header = lin_alloc + alloc->cur;
377 }
378
379 alloc->cur += block_size;
380 alloc->last_alloc = header;
381 alloc->last_alloc_size = block_size;
382 alloc->allocation_count ++;
383 }
384 else{
385 if( vg_mem.use_libc_malloc && (flags & VG_MEMORY_SYSTEM) )
386 header = malloc( sizeof(vg_linear_allocator) );
387 else
388 header = malloc( block_size );
389 }
390
391 header->allocation_count = 0;
392 header->cur = 0;
393 header->last_alloc = NULL;
394 header->last_alloc_size = 0;
395 header->size = size;
396 header->flags = flags;
397
398 if( vg_mem.use_libc_malloc && (flags & VG_MEMORY_SYSTEM) ){
399 u32 table_size = sizeof(vg_allocation_meta)*VG_MAX_ALLOCATIONS;
400 header->alloc_table = malloc( table_size );
401 }
402 else
403 header->alloc_table = NULL;
404
405 return header+1;
406 }
407
408 /* request all the memory we need in advance */
409 VG_STATIC void vg_set_mem_quota( u32 size )
410 {
411 vg_mem.quota = size;
412 }
413
414 VG_STATIC void vg_alloc_quota(void)
415 {
416 u32 size_scratch = 10*1024*1024;
417 u32 size = VG_MAX( vg_mem.quota, size_scratch );
418
419 vg_mem.rtmemory = _vg_create_linear_allocator( NULL, size, VG_MEMORY_SYSTEM,
420 "VG Root" );
421 vg_mem.scratch = _vg_create_linear_allocator( vg_mem.rtmemory,
422 size_scratch,
423 VG_MEMORY_SYSTEM,
424 "Scratch buffer" );
425 }
426
427 VG_STATIC void vg_mem_log( void *lin_alloc, int depth, const char *name )
428 {
429 if( vg_mem.use_libc_malloc ){
430 vg_linear_allocator *alloc = vg_linear_header( lin_alloc );
431
432 u32 s = alloc->size;
433 f32 p = ((float)alloc->cur / (float)alloc->size) * 100.0f;
434
435 for( int i=0; i<depth; i++ ) printf( " " );
436 printf( "LA(%s): %u bytes, %f%% used\n", name, s, p );
437
438 if( alloc->flags & VG_MEMORY_SYSTEM ){
439 for( u32 i=0; i<alloc->allocation_count; i++ ){
440 vg_allocation_meta *meta = &alloc->alloc_table[i];
441
442 if( meta->type == k_allocation_type_block ){
443 for( int i=0; i<depth+1; i++ ) printf( " " );
444 printf( "B(%s): %u bytes\n", meta->name, meta->size );
445 }
446 else{
447 vg_mem_log( meta->data, depth +1, meta->name );
448 }
449 }
450 }
451 else{
452 for( int i=0; i<depth+1; i++ ) printf( " " );
453 printf( "<opaque memory> (UNTRACKED)\n" );
454 }
455 }
456 else{
457 vg_error( "allocations are not tracked (turn on libc mode)\n" );
458 }
459 }
460
461 #endif /* VG_MEM_H */