6 #include "vg_platform.h"
11 VG_STATIC
void vg_print_backtrace(void);
13 #define VG_MAX_ALLOCATIONS 128
14 #define VG_FUZZ_ALLOCATIONS
16 typedef struct vg_linear_allocator vg_linear_allocator
;
17 typedef struct vg_allocation_meta vg_allocation_meta
;
28 struct vg_allocation_meta
34 k_allocation_type_block
= 0,
35 k_allocation_type_linear
= 1
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 */
43 /* systems memory cannot be declared inside realtime memory regions */
46 * Stored just behind the array. 32 bytes.
49 struct vg_linear_allocator
57 vg_allocation_meta
*alloc_table
;
60 /* 32 bit pointers! */
66 VG_STATIC u32
vg_align8( u32 s
);
67 VG_STATIC u32
vg_align4( u32 s
);
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
);
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
);
78 /* its possible to delete just the last item */
79 VG_STATIC
void vg_linear_del( void *buffer
, void *data
);
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
);
86 /* get the current usage of allocator */
87 VG_STATIC u32
vg_linear_get_cur( void *buffer
);
89 /* get the capacity of allocator. */
90 VG_STATIC u32
vg_linear_get_capacity( void *buffer
);
92 /* get the remaining size of the allocator */
93 VG_STATIC u32
vg_linear_remaining( void *buffer
);
95 /* yeet all memory from linear allocator */
96 VG_STATIC
void vg_linear_clear( void *buffer
);
98 /* request all the memory we need in advance */
99 VG_STATIC
void vg_set_mem_quota( u32 size
);
101 /* essentially init() */
102 VG_STATIC
void vg_alloc_quota(void);
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
);
107 #define VG_MEM_MCSTR(S) VG_MEM_MCSTR2(S)
108 #define VG_MEM_MCSTR2(S) #S
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__) )
118 * ----------------------------------------
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
, ...);
125 VG_STATIC u32
vg_align8( u32 s
)
127 u32 m
= (s
+ 7) >> 3;
131 VG_STATIC u32
vg_align4( u32 s
)
133 u32 m
= (s
+ 3) >> 2;
137 /* Returns allocator structure from data pointer */
138 static vg_linear_allocator
*vg_linear_header( void *data
)
140 vg_linear_allocator
*ptr
= data
;
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
)
152 vg_error( "alloc(%u) is not 8 byte aligned\n", size
);
153 vg_print_backtrace();
154 size
= vg_align8( size
);
157 if( ((u32
)buffer
) % 8 ){
159 if( ((u64
)buffer
) % 8 ){
161 vg_fatal_error( "unaligned buffer (%p)", buffer
);
164 vg_linear_allocator
*alloc
= vg_linear_header( buffer
);
166 if( (alloc
->cur
+ size
) > alloc
->size
){
167 vg_fatal_error( "linear allocator overflow (%u + %u > %u)\n",
168 alloc
->cur
, size
, alloc
->size
);
171 if( alloc
->flags
& VG_MEMORY_SYSTEM
)
172 if( (alloc
->allocation_count
+ 1) > VG_MAX_ALLOCATIONS
)
173 vg_fatal_error( "Max linear allocations reached" );
177 if( vg_mem
.use_libc_malloc
&& (alloc
->flags
& VG_MEMORY_SYSTEM
) ){
178 data
= malloc( size
);
180 vg_allocation_meta
*meta
= &alloc
->alloc_table
[ alloc
->allocation_count
];
181 meta
->type
= k_allocation_type_block
;
184 meta
->name
= constr_name
;
187 data
= buffer
+ alloc
->cur
;
191 for( u32 i
=0; i
<size
; i
++ ){
195 alloc
->allocation_count
++;
196 alloc
->last_alloc
= data
;
197 alloc
->last_alloc_size
= size
;
201 if( ((u32
)data
) % 8 ){
203 if( ((u64
)data
) % 8 ){
205 vg_fatal_error( "unaligned" );
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
)
215 vg_linear_allocator
*alloc
= vg_linear_header( buffer
);
218 vg_error( "alloc(%u) is not 8 byte aligned\n", newsize
);
219 vg_print_backtrace();
220 newsize
= vg_align8( newsize
);
223 if( alloc
->last_alloc
!= data
)
224 vg_fatal_error( "This block has been fixed!" );
226 if( (alloc
->cur
- alloc
->last_alloc_size
+ newsize
) > alloc
->size
)
227 vg_fatal_error( "Cannot resize, overflow" );
229 alloc
->cur
-= alloc
->last_alloc_size
;
230 alloc
->cur
+= newsize
;
231 alloc
->last_alloc_size
= newsize
;
233 if( vg_mem
.use_libc_malloc
&& (alloc
->flags
& VG_MEMORY_SYSTEM
) ){
234 data
= realloc( data
, newsize
);
236 vg_fatal_error( "realloc failed" );
238 alloc
->alloc_table
[ alloc
->allocation_count
-1 ].data
= data
;
239 alloc
->last_alloc
= data
;
247 /* its possible to delete just the last item */
248 VG_STATIC
void vg_linear_del( void *buffer
, void *data
)
250 vg_linear_allocator
*alloc
= vg_linear_header( buffer
);
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
);
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" );
265 alloc
->allocation_count
--;
266 alloc
->cur
-= alloc
->last_alloc_size
;
267 alloc
->last_alloc
= NULL
;
268 alloc
->last_alloc_size
= 0;
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
)
277 return _vg_linear_alloc( buffer
, extra
, constr_name
);
279 vg_linear_allocator
*alloc
= vg_linear_header( buffer
);
281 if( alloc
->last_alloc
!= data
)
282 vg_fatal_error( "This block has been fixed!" );
284 u32 new_size
= alloc
->last_alloc_size
+ extra
;
285 return vg_linear_resize( buffer
, data
, new_size
);
288 /* get the current usage of allocator */
289 VG_STATIC u32
vg_linear_get_cur( void *buffer
)
291 vg_linear_allocator
*alloc
= vg_linear_header( buffer
);
295 /* get the capacity of allocator. */
296 VG_STATIC u32
vg_linear_get_capacity( void *buffer
)
298 vg_linear_allocator
*alloc
= vg_linear_header( buffer
);
302 /* get the remaining size of the allocator */
303 VG_STATIC u32
vg_linear_remaining( void *buffer
)
305 vg_linear_allocator
*alloc
= vg_linear_header( buffer
);
306 return alloc
->size
- alloc
->cur
;
309 /* yeet all memory from linear allocator */
310 VG_STATIC
void vg_linear_clear( void *buffer
)
312 vg_linear_allocator
*alloc
= vg_linear_header( buffer
);
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
];
319 if( meta
->type
== k_allocation_type_block
){
323 vg_linear_clear( meta
->data
);
324 vg_linear_allocator
*sub
= vg_linear_header( meta
->data
);
326 free( sub
->alloc_table
);
332 alloc
->last_alloc
= NULL
;
333 alloc
->last_alloc_size
= 0;
334 alloc
->allocation_count
= 0;
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
)
342 assert( sizeof( vg_linear_allocator
) == 32 );
344 vg_linear_allocator
*header
;
345 u32 block_size
= size
+ sizeof(vg_linear_allocator
);
347 /* Creating it inside an existing one */
349 vg_linear_allocator
*alloc
= vg_linear_header( lin_alloc
);
351 if( alloc
->cur
+ block_size
> alloc
->size
)
352 vg_fatal_error( "Out of memory" );
354 if( alloc
->allocation_count
+ 1 > VG_MAX_ALLOCATIONS
)
355 vg_fatal_error( "Max allocations in linear allocator" );
357 if( (flags
&& VG_MEMORY_SYSTEM
) && (alloc
->flags
& VG_MEMORY_REALTIME
) )
358 vg_fatal_error( "Cannot declare realtime allocator inside systems"
361 if( vg_mem
.use_libc_malloc
){
362 vg_allocation_meta
*meta
=
363 &alloc
->alloc_table
[ alloc
->allocation_count
];
365 if( flags
& VG_MEMORY_REALTIME
)
366 header
= malloc( block_size
);
368 header
= malloc( sizeof(vg_linear_allocator
) );
370 meta
->data
= header
+1;
371 meta
->type
= k_allocation_type_linear
;
373 meta
->name
= constr_name
;
376 header
= lin_alloc
+ alloc
->cur
;
379 alloc
->cur
+= block_size
;
380 alloc
->last_alloc
= header
;
381 alloc
->last_alloc_size
= block_size
;
382 alloc
->allocation_count
++;
385 if( vg_mem
.use_libc_malloc
&& (flags
& VG_MEMORY_SYSTEM
) )
386 header
= malloc( sizeof(vg_linear_allocator
) );
388 header
= malloc( block_size
);
391 header
->allocation_count
= 0;
393 header
->last_alloc
= NULL
;
394 header
->last_alloc_size
= 0;
396 header
->flags
= flags
;
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
);
403 header
->alloc_table
= NULL
;
408 /* request all the memory we need in advance */
409 VG_STATIC
void vg_set_mem_quota( u32 size
)
414 VG_STATIC
void vg_alloc_quota(void)
416 u32 size_scratch
= 10*1024*1024;
417 u32 size
= VG_MAX( vg_mem
.quota
, size_scratch
);
419 vg_mem
.rtmemory
= _vg_create_linear_allocator( NULL
, size
, VG_MEMORY_SYSTEM
,
421 vg_mem
.scratch
= _vg_create_linear_allocator( vg_mem
.rtmemory
,
427 VG_STATIC
void vg_mem_log( void *lin_alloc
, int depth
, const char *name
)
429 if( vg_mem
.use_libc_malloc
){
430 vg_linear_allocator
*alloc
= vg_linear_header( lin_alloc
);
433 f32 p
= ((float)alloc
->cur
/ (float)alloc
->size
) * 100.0f
;
435 for( int i
=0; i
<depth
; i
++ ) printf( " " );
436 printf( "LA(%s): %u bytes, %f%% used\n", name
, s
, p
);
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
];
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
);
447 vg_mem_log( meta
->data
, depth
+1, meta
->name
);
452 for( int i
=0; i
<depth
+1; i
++ ) printf( " " );
453 printf( "<opaque memory> (UNTRACKED)\n" );
457 vg_error( "allocations are not tracked (turn on libc mode)\n" );
461 #endif /* VG_MEM_H */