a5d183235ec4ee555436fba9cafb4ba3c2b5d816
[vg.git] / src / vg / vg_mem.h
1 #ifndef VG_MEM_H
2 #define VG_MEM_H
3
4 #include "vg_stdint.h"
5 #include "vg_platform.h"
6
7 #include <stdlib.h>
8 #include <malloc.h>
9
10 #define VG_MAX_ALLOCATIONS 64
11 #define VG_FUZZ_ALLOCATIONS
12 #define VG_DEBUG_ALLOCATIONS
13
14 typedef struct vg_linear_allocator vg_linear_allocator;
15 typedef struct vg_allocation_meta vg_allocation_meta;
16
17 struct
18 {
19 void *rtmemory,
20 *scratch;
21
22 int use_libc_malloc;
23 }
24 static vg_mem;
25
26 struct vg_allocation_meta
27 {
28 void *data;
29 enum allocation_type
30 {
31 k_allocation_type_block = 0,
32 k_allocation_type_linear = 1
33 }
34 type;
35 };
36
37 #define VG_MEMORY_SYSTEM 0x1 /* systems memory, slow and low counts */
38 #define VG_MEMORY_REALTIME 0x2 /* per-frame. no max allocs, only size. fast */
39
40 /* systems memory cannot be declared inside realtime memory regions */
41
42 /*
43 * Stored just behind the array. 32 bytes.
44 */
45 #pragma pack(push,1)
46 struct vg_linear_allocator
47 {
48 u32 size;
49 /* */
50 /* */
51 /* */
52 u32 cur;
53 /* */
54 /* */
55 /* */
56 u16 allocation_count;
57 /* */
58 u16 flags;
59 /* */
60 u32 last_alloc_size;
61 /* */
62 /* */
63 /* */
64 void *last_alloc;
65 /* */
66 /* */
67 /* */
68 /* */
69 /* */
70 /* */
71 /* */
72 vg_allocation_meta *alloc_table;
73 /* */
74 /* */
75 /* */
76 /* */
77 /* */
78 /* */
79 /* */
80 };
81 #pragma pack(pop)
82
83 VG_STATIC void vg_error(const char *fmt, ...);
84 VG_STATIC void vg_info(const char *fmt, ...);
85
86 /* Returns allocator structure from data pointer */
87 static vg_linear_allocator *vg_linear_header( void *data )
88 {
89 vg_linear_allocator *ptr = data;
90 ptr --;
91
92 return ptr;
93 }
94
95 /* allocate something from a linear allocator */
96 __attribute__((warn_unused_result))
97 VG_STATIC void *vg_linear_alloc( void *buffer, u32 size )
98 {
99 vg_linear_allocator *alloc = vg_linear_header( buffer );
100
101 if( (alloc->cur + size) > alloc->size )
102 {
103 vg_error( "%u + %u > %u\n", alloc->cur, size, alloc->size );
104 vg_fatal_exit_loop( "linear allocator overflow" );
105 }
106
107 if( alloc->flags & VG_MEMORY_SYSTEM )
108 if( (alloc->allocation_count + 1) > VG_MAX_ALLOCATIONS )
109 vg_fatal_exit_loop( "Max linear allocations reached" );
110
111 void *data;
112
113 if( vg_mem.use_libc_malloc && (alloc->flags & VG_MEMORY_SYSTEM) )
114 {
115 data = malloc( size );
116
117 #ifdef VG_DEBUG_ALLOCATIONS
118 vg_info( "malloc: %p[%u]\n", data, size );
119 #endif
120
121 vg_allocation_meta *meta = &alloc->alloc_table[ alloc->allocation_count ];
122 meta->type = k_allocation_type_block;
123 meta->data = data;
124 }
125 else
126 {
127 data = buffer + alloc->cur;
128 }
129
130 alloc->allocation_count ++;
131 alloc->last_alloc = data;
132 alloc->last_alloc_size = size;
133 alloc->cur += size;
134
135 return data;
136 }
137
138
139 /* resize latest block of memory from linear */
140 __attribute__((warn_unused_result))
141 VG_STATIC void *vg_linear_resize( void *buffer, void *data, u32 newsize )
142 {
143 vg_linear_allocator *alloc = vg_linear_header( buffer );
144
145 if( alloc->last_alloc != data )
146 vg_fatal_exit_loop( "This block has been fixed!" );
147
148 if( (alloc->cur - alloc->last_alloc_size + newsize) > alloc->size )
149 vg_fatal_exit_loop( "Cannot resize, overflow" );
150
151 alloc->cur -= alloc->last_alloc_size;
152 alloc->cur += newsize;
153 alloc->last_alloc_size = newsize;
154
155 if( vg_mem.use_libc_malloc && (alloc->flags & VG_MEMORY_SYSTEM) )
156 {
157 data = realloc( data, newsize );
158 if( !data )
159 vg_fatal_exit_loop( "realloc failed" );
160
161 alloc->alloc_table[ alloc->allocation_count-1 ].data = data;
162 alloc->last_alloc = data;
163
164 #ifdef VG_DEBUG_ALLOCATIONS
165 vg_info( "realloc: %p (->%u)\n", data, newsize );
166 #endif
167 return data;
168 }
169 else
170 {
171 return data;
172 }
173 }
174
175 /* its possible to delete just the last item */
176 VG_STATIC void vg_linear_del( void *buffer, void *data )
177 {
178 vg_linear_allocator *alloc = vg_linear_header( buffer );
179
180 if( alloc->last_alloc != data )
181 vg_fatal_exit_loop( "This block has been fixed!" );
182
183 if( vg_mem.use_libc_malloc && (alloc->flags & VG_MEMORY_SYSTEM) )
184 {
185 vg_allocation_meta *meta = &alloc->alloc_table[alloc->allocation_count-1];
186 if( meta->type == k_allocation_type_linear )
187 vg_fatal_exit_loop( "Cannot free a linear allocator in this conext" );
188
189 free( data );
190 }
191
192 alloc->allocation_count --;
193 alloc->cur -= alloc->last_alloc_size;
194 alloc->last_alloc = NULL;
195 alloc->last_alloc_size = 0;
196 }
197
198 /* extend latest block of memory from linear */
199 __attribute__((warn_unused_result))
200 VG_STATIC void *vg_linear_extend( void *buffer, void *data, u32 extra )
201 {
202 vg_linear_allocator *alloc = vg_linear_header( buffer );
203
204 if( alloc->last_alloc != data )
205 vg_fatal_exit_loop( "This block has been fixed!" );
206
207 u32 new_size = alloc->last_alloc_size + extra;
208 return vg_linear_resize( buffer, data, new_size );
209 }
210
211 /* get the current usage of allocator */
212 VG_STATIC u32 vg_linear_get_cur( void *buffer )
213 {
214 vg_linear_allocator *alloc = vg_linear_header( buffer );
215 return alloc->cur;
216 }
217
218 /* get the capacity of allocator. TODO, should return free space? */
219 VG_STATIC u32 vg_linear_get_capacity( void *buffer )
220 {
221 vg_linear_allocator *alloc = vg_linear_header( buffer );
222 return alloc->size;
223 }
224
225 /* yeet all memory from linear allocator */
226 VG_STATIC void vg_linear_clear( void *buffer )
227 {
228 vg_linear_allocator *alloc = vg_linear_header( buffer );
229
230 #ifdef VG_DEBUG_ALLOCATIONS
231 if( alloc->flags & VG_MEMORY_SYSTEM )
232 vg_info( "linear_clear on %p\n", alloc );
233 #endif
234
235 /* libc mode we recursively free any allocations made */
236 if( vg_mem.use_libc_malloc && (alloc->flags & VG_MEMORY_SYSTEM) )
237 {
238 for( u32 i=0; i<alloc->allocation_count; i++ )
239 {
240 vg_allocation_meta *meta = &alloc->alloc_table[i];
241
242 if( meta->type == k_allocation_type_block )
243 {
244 free( meta->data );
245
246 #ifdef VG_DEBUG_ALLOCATIONS
247 vg_info( "free(%p) [%d]\n", meta->data, meta->type );
248 #endif
249 }
250 else
251 {
252 vg_linear_clear( meta->data );
253 vg_linear_allocator *sub = vg_linear_header( meta->data );
254
255 #ifdef VG_DEBUG_ALLOCATIONS
256 vg_info( "free(%p) [alloc_table]\n", sub->alloc_table );
257 vg_info( "free(%p) [%d]\n", sub, meta->type );
258 #endif
259
260 free( sub->alloc_table );
261 free( sub );
262 }
263 }
264 }
265
266 alloc->last_alloc = NULL;
267 alloc->last_alloc_size = 0;
268 alloc->allocation_count = 0;
269 alloc->cur = 0;
270 }
271
272 /* allocate a FIXED SIZE linear allocator */
273 VG_STATIC void *vg_create_linear_allocator( void *lin_alloc, u32 size,
274 u16 flags )
275 {
276 assert( sizeof( vg_linear_allocator ) == 32 );
277
278 vg_linear_allocator *header;
279 u32 block_size = size + sizeof(vg_linear_allocator);
280
281 /* Creating it inside an existing one */
282 if( lin_alloc )
283 {
284 vg_linear_allocator *alloc = vg_linear_header( lin_alloc );
285
286 if( alloc->cur + block_size > alloc->size )
287 vg_fatal_exit_loop( "Out of memory" );
288
289 if( alloc->allocation_count + 1 > VG_MAX_ALLOCATIONS )
290 vg_fatal_exit_loop( "Max allocations in linear allocator" );
291
292 if( (flags && VG_MEMORY_SYSTEM) && (alloc->flags & VG_MEMORY_REALTIME) )
293 vg_fatal_exit_loop( "Cannot declare realtime allocator inside systems"
294 " allocator" );
295
296 if( vg_mem.use_libc_malloc )
297 {
298 vg_allocation_meta *meta =
299 &alloc->alloc_table[ alloc->allocation_count ];
300
301 if( flags & VG_MEMORY_REALTIME )
302 header = malloc( block_size );
303 else
304 header = malloc( sizeof(vg_linear_allocator) );
305
306 #ifdef VG_DEBUG_ALLOCATIONS
307 vg_info( "linear_create(sub) malloc(%p)\n", header );
308 #endif
309
310 meta->data = header+1;
311 meta->type = k_allocation_type_linear;
312 }
313 else
314 {
315 header = lin_alloc + alloc->cur;
316 }
317
318 alloc->cur += block_size;
319 alloc->last_alloc = NULL; /* cant resize this block! */
320 alloc->last_alloc_size = block_size;
321
322 alloc->allocation_count ++;
323 }
324 else
325 {
326 if( vg_mem.use_libc_malloc && (flags & VG_MEMORY_SYSTEM) )
327 header = malloc( sizeof(vg_linear_allocator) );
328 else
329 header = malloc( block_size );
330
331 #ifdef VG_DEBUG_ALLOCATIONS
332 vg_info( "linear_create malloc(%p)\n", header );
333 #endif
334 }
335
336 header->allocation_count = 0;
337 header->cur = 0;
338 header->last_alloc = NULL;
339 header->last_alloc_size = 0;
340 header->size = size;
341 header->flags = flags;
342
343 if( vg_mem.use_libc_malloc && (flags & VG_MEMORY_SYSTEM) )
344 {
345 u32 table_size = sizeof(vg_allocation_meta)*VG_MAX_ALLOCATIONS;
346 header->alloc_table = malloc( table_size );
347 #ifdef VG_DEBUG_ALLOCATIONS
348 vg_info( "linear_create(%p) [alloc_table]\n", header->alloc_table );
349 #endif
350 }
351 else
352 header->alloc_table = NULL;
353
354 return header+1;
355 }
356
357 /* request all the memory we need in advance */
358 VG_STATIC void vg_prealloc_quota( u32 size )
359 {
360 u32 size_scratch = 10*1024*1024;
361 size = VG_MAX( size, size_scratch );
362
363 vg_mem.rtmemory = vg_create_linear_allocator( NULL, size, VG_MEMORY_SYSTEM );
364 vg_mem.scratch = vg_create_linear_allocator( vg_mem.rtmemory,
365 size_scratch,
366 VG_MEMORY_SYSTEM );
367 }
368
369 #endif /* VG_MEM_H */
370
371 #if 0
372
373 #ifndef VG_MEM_H
374 #define VG_MEM_H
375
376 #include "vg_stdint.h"
377 #include "vg_platform.h"
378
379 #include <stdlib.h>
380 #include <malloc.h>
381
382 typedef struct vg_linear_allocator vg_linear_allocator;
383
384 struct
385 {
386 void *rtmemory,
387 *scratch;
388 }
389 static vg_mem;
390
391 struct vg_linear_allocator
392 {
393 u32 size, cur;
394
395 /* allows temporarily extendable buffers */
396 void *last_alloc;
397 u32 last_alloc_size;
398 };
399
400 /*
401 * TODO: Fallback on libc
402 */
403
404 //#define VG_USE_MALLOC
405
406 VG_STATIC void vg_error(const char *fmt, ...);
407
408 /* allocate something from a linear allocator */
409 __attribute__((warn_unused_result))
410 VG_STATIC void *vg_linear_alloc( void *allocator, u32 size )
411 {
412 size += 7;
413 size >>= 3;
414 size <<= 3;
415
416 if( allocator == NULL )
417 vg_fatal_exit_loop( "Null allocator" );
418
419 vg_linear_allocator *allocptr = allocator;
420 allocptr --;
421
422 if( allocptr->cur + size > allocptr->size )
423 {
424 vg_error( "%u(current) + %u(alloc) > %u(max)\n",
425 allocptr->cur, size, allocptr->size );
426
427 vg_fatal_exit_loop( "Linear allocator out of memory" );
428 }
429
430 #ifdef VG_USE_MALLOC
431 void *data = malloc(size);
432 #else
433 void *data = allocator + allocptr->cur;
434 #endif
435
436 allocptr->cur += size;
437 allocptr->last_alloc_size = size;
438 allocptr->last_alloc = data;
439
440 return data;
441 }
442
443 /* resize latest block of memory from linear */
444 __attribute__((warn_unused_result))
445 VG_STATIC void *vg_linear_resize( void *allocator, void *data, u32 newsize )
446 {
447 vg_linear_allocator *allocptr = allocator;
448 allocptr --;
449
450 if( allocptr->last_alloc == data )
451 {
452 allocptr->cur -= allocptr->last_alloc_size;
453
454 #ifdef VG_USE_MALLOC
455 allocptr->cur += newsize;
456 void *newdata = realloc( data, newsize );
457 allocptr->last_alloc = newdata;
458 allocptr->last_alloc_size = newsize;
459
460 return newdata;
461 #else
462 return vg_linear_alloc( allocator, newsize );
463 #endif
464 }
465 else
466 {
467 vg_error( "%p != %p\n", allocptr->last_alloc, data );
468 vg_fatal_exit_loop( "Cannot resize this buffer anymore" );
469 }
470
471 return NULL;
472 }
473
474 VG_STATIC void vg_linear_del( void *allocator, void *data )
475 {
476 #ifdef VG_USE_MALLOC
477 free(data);
478 #else
479 void *ignore = vg_linear_resize( allocator, data, 0 );
480 #endif
481
482 vg_linear_allocator *allocptr = allocator;
483 allocptr --;
484
485 allocptr->last_alloc = NULL;
486 allocptr->last_alloc_size = 0;
487 }
488
489 /* extend latest block of memory from linear */
490 __attribute__((warn_unused_result))
491 VG_STATIC void *vg_linear_extend( void *allocator, void *data, u32 extra )
492 {
493 vg_linear_allocator *allocptr = allocator;
494 allocptr --;
495
496 return vg_linear_resize( allocator, data, allocptr->last_alloc_size+extra );
497 }
498
499 /* get the current usage of allocator */
500 VG_STATIC u32 vg_linear_get_cur( void *allocator )
501 {
502 vg_linear_allocator *allocptr = allocator;
503 allocptr --;
504
505 return allocptr->cur;
506 }
507
508 /* get the capacity of allocator */
509 VG_STATIC u32 vg_linear_get_capacity( void *allocator )
510 {
511 vg_linear_allocator *allocptr = allocator;
512 allocptr --;
513
514 return allocptr->size;
515 }
516
517 #if 0
518 /* get the size of the last allocated thing */
519 VG_STATIC u32 vg_linear_last_size( void *allocator )
520 {
521 vg_linear_allocator *allocptr = allocator;
522 allocptr --;
523
524 return allocptr->last_alloc_size;
525 }
526 #endif
527
528 /* yeet all memory from linear allocator */
529 VG_STATIC void vg_linear_clear( void *allocator )
530 {
531 vg_linear_allocator *allocptr = allocator;
532 allocptr --;
533
534 allocptr->last_alloc = NULL;
535 allocptr->last_alloc_size = 0;
536 allocptr->cur = 0;
537 }
538
539 /* allocate a FIXED SIZE linear allocator */
540 VG_STATIC void *vg_create_linear_allocator( void *lin_alloc, u32 size )
541 {
542 u32 total = size + sizeof(vg_linear_allocator);
543 vg_linear_allocator *allocptr;
544
545 if( lin_alloc == NULL )
546 {
547 allocptr = malloc( total );
548
549 if( allocptr == NULL )
550 vg_fatal_exit_loop( "Create linear: Malloc failed" );
551 }
552 else
553 {
554 vg_linear_allocator *parent = lin_alloc;
555 parent --;
556
557 allocptr = vg_linear_alloc( lin_alloc, total );
558
559 #if 0
560 parent->last_alloc = allocptr+1;
561 parent->last_alloc_size = total;
562 #else
563 parent->last_alloc = NULL;
564 parent->last_alloc_size = total;
565 #endif
566 }
567
568 allocptr->size = size;
569 allocptr->cur = 0;
570 allocptr->last_alloc = NULL;
571 allocptr->last_alloc_size = 0;
572
573 void *data = allocptr+1;
574 return data;
575 }
576
577 /* request all the memory we need in advance */
578 VG_STATIC void vg_prealloc_quota( u32 size )
579 {
580 size = VG_MAX( size, 20*1024*1024 );
581 vg_mem.rtmemory = vg_create_linear_allocator( NULL, size );
582 vg_mem.scratch = vg_create_linear_allocator( vg_mem.rtmemory, 10*1024*1024 );
583 }
584
585 #endif /* VG_MEM_H */
586
587 #endif