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