6 #include "vg_platform.h"
12 static void vg_print_backtrace(void);
14 #define VG_MAX_ALLOCATIONS 128
15 #define VG_FUZZ_ALLOCATIONS
17 typedef struct vg_linear_allocator vg_linear_allocator
;
18 typedef struct vg_allocation_meta vg_allocation_meta
;
29 struct vg_allocation_meta
35 k_allocation_type_block
= 0,
36 k_allocation_type_linear
= 1
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 */
44 /* systems memory cannot be declared inside realtime memory regions */
47 * Stored just behind the array. 32 bytes.
50 struct vg_linear_allocator
58 vg_allocation_meta
*alloc_table
;
61 /* 32 bit pointers! */
67 static u32
vg_align8( u32 s
);
68 static u32
vg_align4( u32 s
);
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
);
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
);
79 /* its possible to delete just the last item */
80 static void vg_linear_del( void *buffer
, void *data
);
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
);
87 /* get the current usage of allocator */
88 static u32
vg_linear_get_cur( void *buffer
);
90 /* get the capacity of allocator. */
91 static u32
vg_linear_get_capacity( void *buffer
);
93 /* get the remaining size of the allocator */
94 static u32
vg_linear_remaining( void *buffer
);
96 /* yeet all memory from linear allocator */
97 static void vg_linear_clear( void *buffer
);
99 /* request all the memory we need in advance */
100 static void vg_set_mem_quota( u32 size
);
102 /* essentially init() */
103 static void vg_alloc_quota(void);
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
);
108 #define VG_MEM_MCSTR(S) VG_MEM_MCSTR2(S)
109 #define VG_MEM_MCSTR2(S) #S
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__) )
119 * ----------------------------------------
122 static void vg_fatal_error( const char *fmt
, ... );
125 static void vg_error(const char *fmt
, ...);
126 static void vg_info(const char *fmt
, ...);
129 static u32
vg_align8( u32 s
)
131 u32 m
= (s
+ 7) >> 3;
135 static u32
vg_align4( u32 s
)
137 u32 m
= (s
+ 3) >> 2;
141 /* Returns allocator structure from data pointer */
142 static vg_linear_allocator
*vg_linear_header( void *data
)
144 vg_linear_allocator
*ptr
= data
;
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
)
156 vg_error( "alloc(%u) is not 8 byte aligned\n", size
);
157 vg_print_backtrace();
158 size
= vg_align8( size
);
161 if( ((u32
)buffer
) % 8 ){
163 if( ((u64
)buffer
) % 8 ){
165 vg_fatal_error( "unaligned buffer (%p)", buffer
);
168 vg_linear_allocator
*alloc
= vg_linear_header( buffer
);
170 if( (alloc
->cur
+ size
) > alloc
->size
){
171 vg_fatal_error( "linear allocator overflow (%u + %u > %u)\n",
172 alloc
->cur
, size
, alloc
->size
);
175 if( alloc
->flags
& VG_MEMORY_SYSTEM
)
176 if( (alloc
->allocation_count
+ 1) > VG_MAX_ALLOCATIONS
)
177 vg_fatal_error( "Max linear allocations reached" );
181 if( vg_mem
.use_libc_malloc
&& (alloc
->flags
& VG_MEMORY_SYSTEM
) ){
182 data
= malloc( size
);
184 vg_allocation_meta
*meta
= &alloc
->alloc_table
[ alloc
->allocation_count
];
185 meta
->type
= k_allocation_type_block
;
188 meta
->name
= constr_name
;
191 data
= buffer
+ alloc
->cur
;
195 for( u32 i
=0; i
<size
; i
++ ){
199 alloc
->allocation_count
++;
200 alloc
->last_alloc
= data
;
201 alloc
->last_alloc_size
= size
;
205 if( ((u32
)data
) % 8 ){
207 if( ((u64
)data
) % 8 ){
209 vg_fatal_error( "unaligned" );
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
)
219 vg_linear_allocator
*alloc
= vg_linear_header( buffer
);
222 vg_error( "alloc(%u) is not 8 byte aligned\n", newsize
);
223 vg_print_backtrace();
224 newsize
= vg_align8( newsize
);
227 if( alloc
->last_alloc
!= data
)
228 vg_fatal_error( "This block has been fixed!" );
230 if( (alloc
->cur
- alloc
->last_alloc_size
+ newsize
) > alloc
->size
)
231 vg_fatal_error( "Cannot resize, overflow" );
233 alloc
->cur
-= alloc
->last_alloc_size
;
234 alloc
->cur
+= newsize
;
235 alloc
->last_alloc_size
= newsize
;
237 if( vg_mem
.use_libc_malloc
&& (alloc
->flags
& VG_MEMORY_SYSTEM
) ){
238 data
= realloc( data
, newsize
);
240 vg_fatal_error( "realloc failed" );
242 alloc
->alloc_table
[ alloc
->allocation_count
-1 ].data
= data
;
243 alloc
->last_alloc
= data
;
251 /* its possible to delete just the last item */
252 static void vg_linear_del( void *buffer
, void *data
)
254 vg_linear_allocator
*alloc
= vg_linear_header( buffer
);
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
);
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" );
269 alloc
->allocation_count
--;
270 alloc
->cur
-= alloc
->last_alloc_size
;
271 alloc
->last_alloc
= NULL
;
272 alloc
->last_alloc_size
= 0;
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
)
281 return _vg_linear_alloc( buffer
, extra
, constr_name
);
283 vg_linear_allocator
*alloc
= vg_linear_header( buffer
);
285 if( alloc
->last_alloc
!= data
)
286 vg_fatal_error( "This block has been fixed!" );
288 u32 new_size
= alloc
->last_alloc_size
+ extra
;
289 return vg_linear_resize( buffer
, data
, new_size
);
292 /* get the current usage of allocator */
293 static u32
vg_linear_get_cur( void *buffer
)
295 vg_linear_allocator
*alloc
= vg_linear_header( buffer
);
299 /* get the capacity of allocator. */
300 static u32
vg_linear_get_capacity( void *buffer
)
302 vg_linear_allocator
*alloc
= vg_linear_header( buffer
);
306 /* get the remaining size of the allocator */
307 static u32
vg_linear_remaining( void *buffer
)
309 vg_linear_allocator
*alloc
= vg_linear_header( buffer
);
310 return alloc
->size
- alloc
->cur
;
313 /* yeet all memory from linear allocator */
314 static void vg_linear_clear( void *buffer
)
316 vg_linear_allocator
*alloc
= vg_linear_header( buffer
);
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
];
323 if( meta
->type
== k_allocation_type_block
){
327 vg_linear_clear( meta
->data
);
328 vg_linear_allocator
*sub
= vg_linear_header( meta
->data
);
330 free( sub
->alloc_table
);
336 alloc
->last_alloc
= NULL
;
337 alloc
->last_alloc_size
= 0;
338 alloc
->allocation_count
= 0;
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
)
346 assert( sizeof( vg_linear_allocator
) == 32 );
348 vg_linear_allocator
*header
;
349 u32 block_size
= size
+ sizeof(vg_linear_allocator
);
351 /* Creating it inside an existing one */
353 vg_linear_allocator
*alloc
= vg_linear_header( lin_alloc
);
355 if( alloc
->cur
+ block_size
> alloc
->size
)
356 vg_fatal_error( "Out of memory" );
358 if( alloc
->allocation_count
+ 1 > VG_MAX_ALLOCATIONS
)
359 vg_fatal_error( "Max allocations in linear allocator" );
361 if( (flags
&& VG_MEMORY_SYSTEM
) && (alloc
->flags
& VG_MEMORY_REALTIME
) )
362 vg_fatal_error( "Cannot declare realtime allocator inside systems"
365 if( vg_mem
.use_libc_malloc
){
366 vg_allocation_meta
*meta
=
367 &alloc
->alloc_table
[ alloc
->allocation_count
];
369 if( flags
& VG_MEMORY_REALTIME
)
370 header
= malloc( block_size
);
372 header
= malloc( sizeof(vg_linear_allocator
) );
374 meta
->data
= header
+1;
375 meta
->type
= k_allocation_type_linear
;
377 meta
->name
= constr_name
;
380 header
= lin_alloc
+ alloc
->cur
;
383 alloc
->cur
+= block_size
;
384 alloc
->last_alloc
= header
;
385 alloc
->last_alloc_size
= block_size
;
386 alloc
->allocation_count
++;
389 if( vg_mem
.use_libc_malloc
&& (flags
& VG_MEMORY_SYSTEM
) )
390 header
= malloc( sizeof(vg_linear_allocator
) );
392 header
= malloc( block_size
);
395 header
->allocation_count
= 0;
397 header
->last_alloc
= NULL
;
398 header
->last_alloc_size
= 0;
400 header
->flags
= flags
;
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
);
407 header
->alloc_table
= NULL
;
412 /* request all the memory we need in advance */
413 static void vg_set_mem_quota( u32 size
)
418 static void vg_alloc_quota(void)
420 u32 size_scratch
= 10*1024*1024;
421 u32 size
= VG_MAX( vg_mem
.quota
, size_scratch
);
423 vg_mem
.rtmemory
= _vg_create_linear_allocator( NULL
, size
, VG_MEMORY_SYSTEM
,
425 vg_mem
.scratch
= _vg_create_linear_allocator( vg_mem
.rtmemory
,
431 static void vg_mem_log( void *lin_alloc
, int depth
, const char *name
)
433 if( vg_mem
.use_libc_malloc
){
434 vg_linear_allocator
*alloc
= vg_linear_header( lin_alloc
);
437 f32 p
= ((float)alloc
->cur
/ (float)alloc
->size
) * 100.0f
;
439 for( int i
=0; i
<depth
; i
++ ) printf( " " );
440 printf( "LA(%s): %u bytes, %f%% used\n", name
, s
, p
);
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
];
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
);
451 vg_mem_log( meta
->data
, depth
+1, meta
->name
);
456 for( int i
=0; i
<depth
+1; i
++ ) printf( " " );
457 printf( "<opaque memory> (UNTRACKED)\n" );
461 vg_error( "allocations are not tracked (turn on libc mode)\n" );
465 #endif /* VG_MEM_H */