better build program
[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 if( ((u64)buffer) % 8 ){
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 if( ((u64)data) % 8 ){
201 vg_fatal_error( "unaligned" );
202 }
203
204 return data;
205 }
206
207 /* resize latest block of memory from linear */
208 __attribute__((warn_unused_result))
209 static void *vg_linear_resize( void *buffer, void *data, u32 newsize )
210 {
211 vg_linear_allocator *alloc = vg_linear_header( buffer );
212
213 if( newsize % 8 ){
214 vg_error( "alloc(%u) is not 8 byte aligned\n", newsize );
215 vg_print_backtrace();
216 newsize = vg_align8( newsize );
217 }
218
219 if( alloc->last_alloc != data )
220 vg_fatal_error( "This block has been fixed!" );
221
222 if( (alloc->cur - alloc->last_alloc_size + newsize) > alloc->size )
223 vg_fatal_error( "Cannot resize, overflow" );
224
225 alloc->cur -= alloc->last_alloc_size;
226 alloc->cur += newsize;
227 alloc->last_alloc_size = newsize;
228
229 if( vg_mem.use_libc_malloc && (alloc->flags & VG_MEMORY_SYSTEM) ){
230 data = realloc( data, newsize );
231 if( !data )
232 vg_fatal_error( "realloc failed" );
233
234 alloc->alloc_table[ alloc->allocation_count-1 ].data = data;
235 alloc->last_alloc = data;
236 return data;
237 }
238 else{
239 return data;
240 }
241 }
242
243 /* its possible to delete just the last item */
244 static void vg_linear_del( void *buffer, void *data )
245 {
246 vg_linear_allocator *alloc = vg_linear_header( buffer );
247
248 if( alloc->last_alloc != data ){
249 vg_fatal_error( "This block has been fixed! Last alloc: %p, this: %p\n",
250 alloc->last_alloc, data );
251 }
252
253 if( vg_mem.use_libc_malloc && (alloc->flags & VG_MEMORY_SYSTEM) ){
254 vg_allocation_meta *meta = &alloc->alloc_table[alloc->allocation_count-1];
255 if( meta->type == k_allocation_type_linear )
256 vg_fatal_error( "Cannot free a linear allocator in this conext" );
257
258 free( data );
259 }
260
261 alloc->allocation_count --;
262 alloc->cur -= alloc->last_alloc_size;
263 alloc->last_alloc = NULL;
264 alloc->last_alloc_size = 0;
265 }
266
267 /* extend latest block of memory from linear */
268 __attribute__((warn_unused_result))
269 static void *_vg_linear_extend( void *buffer, void *data, u32 extra,
270 const char *constr_name )
271 {
272 if( !data )
273 return _vg_linear_alloc( buffer, vg_align8(extra), constr_name );
274
275 vg_linear_allocator *alloc = vg_linear_header( buffer );
276
277 if( alloc->last_alloc != data )
278 vg_fatal_error( "This block has been fixed!" );
279
280 u32 new_size = alloc->last_alloc_size + extra;
281 return vg_linear_resize( buffer, data, vg_align8(new_size) );
282 }
283
284 /* get the current usage of allocator */
285 static u32 vg_linear_get_cur( void *buffer )
286 {
287 vg_linear_allocator *alloc = vg_linear_header( buffer );
288 return alloc->cur;
289 }
290
291 /* get the capacity of allocator. */
292 static u32 vg_linear_get_capacity( void *buffer )
293 {
294 vg_linear_allocator *alloc = vg_linear_header( buffer );
295 return alloc->size;
296 }
297
298 /* get the remaining size of the allocator */
299 static u32 vg_linear_remaining( void *buffer )
300 {
301 vg_linear_allocator *alloc = vg_linear_header( buffer );
302 return alloc->size - alloc->cur;
303 }
304
305 /* yeet all memory from linear allocator */
306 static void vg_linear_clear( void *buffer )
307 {
308 vg_linear_allocator *alloc = vg_linear_header( buffer );
309
310 /* libc mode we recursively free any allocations made */
311 if( vg_mem.use_libc_malloc && (alloc->flags & VG_MEMORY_SYSTEM) ){
312 for( u32 i=0; i<alloc->allocation_count; i++ ){
313 vg_allocation_meta *meta = &alloc->alloc_table[i];
314
315 if( meta->type == k_allocation_type_block ){
316 free( meta->data );
317 }
318 else{
319 vg_linear_clear( meta->data );
320 vg_linear_allocator *sub = vg_linear_header( meta->data );
321
322 free( sub->alloc_table );
323 free( sub );
324 }
325 }
326 }
327
328 alloc->last_alloc = NULL;
329 alloc->last_alloc_size = 0;
330 alloc->allocation_count = 0;
331 alloc->cur = 0;
332 }
333
334 /* allocate a FIXED SIZE linear allocator */
335 static void *_vg_create_linear_allocator( void *lin_alloc, u32 size,
336 u16 flags, const char *constr_name)
337 {
338 assert( sizeof( vg_linear_allocator ) == 32 );
339
340 vg_linear_allocator *header;
341 u32 block_size = size + sizeof(vg_linear_allocator);
342
343 /* Creating it inside an existing one */
344 if( lin_alloc ){
345 vg_linear_allocator *alloc = vg_linear_header( lin_alloc );
346
347 if( alloc->cur + block_size > alloc->size )
348 vg_fatal_error( "Out of memory" );
349
350 if( alloc->allocation_count + 1 > VG_MAX_ALLOCATIONS )
351 vg_fatal_error( "Max allocations in linear allocator" );
352
353 if( (flags && VG_MEMORY_SYSTEM) && (alloc->flags & VG_MEMORY_REALTIME) )
354 vg_fatal_error( "Cannot declare realtime allocator inside systems"
355 " allocator" );
356
357 if( vg_mem.use_libc_malloc ){
358 vg_allocation_meta *meta =
359 &alloc->alloc_table[ alloc->allocation_count ];
360
361 if( flags & VG_MEMORY_REALTIME )
362 header = malloc( block_size );
363 else
364 header = malloc( sizeof(vg_linear_allocator) );
365
366 meta->data = header+1;
367 meta->type = k_allocation_type_linear;
368 meta->size = size;
369 meta->name = constr_name;
370 }
371 else{
372 header = lin_alloc + alloc->cur;
373 }
374
375 alloc->cur += block_size;
376 alloc->last_alloc = header;
377 alloc->last_alloc_size = block_size;
378 alloc->allocation_count ++;
379 }
380 else{
381 if( vg_mem.use_libc_malloc && (flags & VG_MEMORY_SYSTEM) )
382 header = malloc( sizeof(vg_linear_allocator) );
383 else
384 header = malloc( block_size );
385 }
386
387 header->allocation_count = 0;
388 header->cur = 0;
389 header->last_alloc = NULL;
390 header->last_alloc_size = 0;
391 header->size = size;
392 header->flags = flags;
393
394 if( vg_mem.use_libc_malloc && (flags & VG_MEMORY_SYSTEM) ){
395 u32 table_size = sizeof(vg_allocation_meta)*VG_MAX_ALLOCATIONS;
396 header->alloc_table = malloc( table_size );
397 }
398 else
399 header->alloc_table = NULL;
400
401 return header+1;
402 }
403
404 /* request all the memory we need in advance */
405 static void vg_set_mem_quota( u32 size )
406 {
407 vg_mem.quota = size;
408 }
409
410 static void vg_alloc_quota(void)
411 {
412 u32 size_scratch = 10*1024*1024;
413 u32 size = VG_MAX( vg_mem.quota, size_scratch );
414
415 vg_mem.rtmemory = _vg_create_linear_allocator( NULL, size, VG_MEMORY_SYSTEM,
416 "VG Root" );
417 vg_mem.scratch = _vg_create_linear_allocator( vg_mem.rtmemory,
418 size_scratch,
419 VG_MEMORY_SYSTEM,
420 "Scratch buffer" );
421 }
422
423 static void vg_mem_log( void *lin_alloc, int depth, const char *name )
424 {
425 if( vg_mem.use_libc_malloc ){
426 vg_linear_allocator *alloc = vg_linear_header( lin_alloc );
427
428 u32 s = alloc->size;
429 f32 p = ((float)alloc->cur / (float)alloc->size) * 100.0f;
430
431 for( int i=0; i<depth; i++ ) printf( " " );
432 printf( "LA(%s): %u bytes, %f%% used\n", name, s, p );
433
434 if( alloc->flags & VG_MEMORY_SYSTEM ){
435 for( u32 i=0; i<alloc->allocation_count; i++ ){
436 vg_allocation_meta *meta = &alloc->alloc_table[i];
437
438 if( meta->type == k_allocation_type_block ){
439 for( int i=0; i<depth+1; i++ ) printf( " " );
440 printf( "B(%s): %u bytes\n", meta->name, meta->size );
441 }
442 else{
443 vg_mem_log( meta->data, depth +1, meta->name );
444 }
445 }
446 }
447 else{
448 for( int i=0; i<depth+1; i++ ) printf( " " );
449 printf( "<opaque memory> (UNTRACKED)\n" );
450 }
451 }
452 else{
453 vg_error( "allocations are not tracked (turn on libc mode)\n" );
454 }
455 }
456
457 #endif /* VG_MEM_H */