49d9e37e86b8af8d515a64a164d64873e6d6f59f
[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
8 #include <stdlib.h>
9 #include <malloc.h>
10
11 VG_STATIC void vg_print_backtrace(void);
12
13 #define VG_MAX_ALLOCATIONS 128
14 #define VG_FUZZ_ALLOCATIONS
15
16 typedef struct vg_linear_allocator vg_linear_allocator;
17 typedef struct vg_allocation_meta vg_allocation_meta;
18
19 struct
20 {
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 void *data;
32 enum allocation_type
33 {
34 k_allocation_type_block = 0,
35 k_allocation_type_linear = 1
36 }
37 type;
38 };
39
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 */
42
43 /* systems memory cannot be declared inside realtime memory regions */
44
45 /*
46 * Stored just behind the array. 32 bytes.
47 */
48 #pragma pack(push,1)
49 struct vg_linear_allocator
50 {
51 u32 size;
52 /* */
53 /* */
54 /* */
55 u32 cur;
56 /* */
57 /* */
58 /* */
59 u16 allocation_count;
60 /* */
61 u16 flags;
62 /* */
63 u32 last_alloc_size;
64 /* */
65 /* */
66 /* */
67 void *last_alloc;
68 /* */
69 /* */
70 /* */
71 /* */
72 /* */
73 /* */
74 /* */
75 vg_allocation_meta *alloc_table;
76 /* */
77 /* */
78 /* */
79 /* */
80 /* */
81 /* */
82 /* */
83
84 #ifdef _WIN32
85 /* 32 bit pointers! */
86 u8 padding[ 8 ];
87 #endif
88 };
89 #pragma pack(pop)
90
91 VG_STATIC void vg_fatal_exit_loop( const char *error );
92 VG_STATIC void vg_error(const char *fmt, ...);
93 VG_STATIC void vg_info(const char *fmt, ...);
94
95 VG_STATIC u32 vg_align8( u32 s )
96 {
97 u32 m = (s + 7) >> 3;
98 return m << 3;
99 }
100
101 VG_STATIC u32 vg_align4( u32 s )
102 {
103 u32 m = (s + 3) >> 2;
104 return m << 2;
105 }
106
107 /* Returns allocator structure from data pointer */
108 static vg_linear_allocator *vg_linear_header( void *data )
109 {
110 vg_linear_allocator *ptr = data;
111 ptr --;
112
113 return ptr;
114 }
115
116 /* allocate something from a linear allocator */
117 __attribute__((warn_unused_result))
118 VG_STATIC void *vg_linear_alloc( void *buffer, u32 size )
119 {
120 if( size % 8 ){
121 vg_error( "alloc(%u) is not 8 byte aligned\n", size );
122 vg_print_backtrace();
123 size = vg_align8( size );
124 }
125 #ifdef _WIN32
126 if( ((u32)buffer) % 8 ){
127 #else
128 if( ((u64)buffer) % 8 ){
129 #endif
130 vg_error( "buffer: %p\n", buffer );
131 vg_fatal_exit_loop( "unaligned buffer" );
132 }
133
134 vg_linear_allocator *alloc = vg_linear_header( buffer );
135
136 if( (alloc->cur + size) > alloc->size ){
137 vg_error( "%u + %u > %u\n", alloc->cur, size, alloc->size );
138 vg_fatal_exit_loop( "linear allocator overflow" );
139 }
140
141 if( alloc->flags & VG_MEMORY_SYSTEM )
142 if( (alloc->allocation_count + 1) > VG_MAX_ALLOCATIONS )
143 vg_fatal_exit_loop( "Max linear allocations reached" );
144
145 void *data;
146
147 if( vg_mem.use_libc_malloc && (alloc->flags & VG_MEMORY_SYSTEM) ){
148 data = malloc( size );
149
150 vg_allocation_meta *meta = &alloc->alloc_table[ alloc->allocation_count ];
151 meta->type = k_allocation_type_block;
152 meta->data = data;
153 }
154 else{
155 data = buffer + alloc->cur;
156 }
157
158 u8 *bytes = data;
159 for( u32 i=0; i<size; i++ ){
160 bytes[i] = 0xae;
161 }
162
163 alloc->allocation_count ++;
164 alloc->last_alloc = data;
165 alloc->last_alloc_size = size;
166 alloc->cur += size;
167
168 #ifdef _WIN32
169 if( ((u32)data) % 8 ){
170 #else
171 if( ((u64)data) % 8 ){
172 #endif
173 vg_fatal_exit_loop( "unaligned" );
174 }
175
176 return data;
177 }
178
179 /* resize latest block of memory from linear */
180 __attribute__((warn_unused_result))
181 VG_STATIC void *vg_linear_resize( void *buffer, void *data, u32 newsize )
182 {
183 vg_linear_allocator *alloc = vg_linear_header( buffer );
184
185 if( newsize % 8 ){
186 vg_error( "alloc(%u) is not 8 byte aligned\n", newsize );
187 vg_print_backtrace();
188 newsize = vg_align8( newsize );
189 }
190
191 if( alloc->last_alloc != data )
192 vg_fatal_exit_loop( "This block has been fixed!" );
193
194 if( (alloc->cur - alloc->last_alloc_size + newsize) > alloc->size )
195 vg_fatal_exit_loop( "Cannot resize, overflow" );
196
197 alloc->cur -= alloc->last_alloc_size;
198 alloc->cur += newsize;
199 alloc->last_alloc_size = newsize;
200
201 if( vg_mem.use_libc_malloc && (alloc->flags & VG_MEMORY_SYSTEM) ){
202 data = realloc( data, newsize );
203 if( !data )
204 vg_fatal_exit_loop( "realloc failed" );
205
206 alloc->alloc_table[ alloc->allocation_count-1 ].data = data;
207 alloc->last_alloc = data;
208 return data;
209 }
210 else{
211 return data;
212 }
213 }
214
215 /* its possible to delete just the last item */
216 VG_STATIC void vg_linear_del( void *buffer, void *data )
217 {
218 vg_linear_allocator *alloc = vg_linear_header( buffer );
219
220 if( alloc->last_alloc != data )
221 vg_fatal_exit_loop( "This block has been fixed!" );
222
223 if( vg_mem.use_libc_malloc && (alloc->flags & VG_MEMORY_SYSTEM) ){
224 vg_allocation_meta *meta = &alloc->alloc_table[alloc->allocation_count-1];
225 if( meta->type == k_allocation_type_linear )
226 vg_fatal_exit_loop( "Cannot free a linear allocator in this conext" );
227
228 free( data );
229 }
230
231 alloc->allocation_count --;
232 alloc->cur -= alloc->last_alloc_size;
233 alloc->last_alloc = NULL;
234 alloc->last_alloc_size = 0;
235 }
236
237 /* extend latest block of memory from linear */
238 __attribute__((warn_unused_result))
239 VG_STATIC void *vg_linear_extend( void *buffer, void *data, u32 extra )
240 {
241 if( !data )
242 return vg_linear_alloc( buffer, extra );
243
244 vg_linear_allocator *alloc = vg_linear_header( buffer );
245
246 if( alloc->last_alloc != data )
247 vg_fatal_exit_loop( "This block has been fixed!" );
248
249 u32 new_size = alloc->last_alloc_size + extra;
250 return vg_linear_resize( buffer, data, new_size );
251 }
252
253 /* get the current usage of allocator */
254 VG_STATIC u32 vg_linear_get_cur( void *buffer )
255 {
256 vg_linear_allocator *alloc = vg_linear_header( buffer );
257 return alloc->cur;
258 }
259
260 /* get the capacity of allocator. */
261 VG_STATIC u32 vg_linear_get_capacity( void *buffer )
262 {
263 vg_linear_allocator *alloc = vg_linear_header( buffer );
264 return alloc->size;
265 }
266
267 /* get the remaining size of the allocator */
268 VG_STATIC u32 vg_linear_remaining( void *buffer )
269 {
270 vg_linear_allocator *alloc = vg_linear_header( buffer );
271 return alloc->size - alloc->cur;
272 }
273
274 /* yeet all memory from linear allocator */
275 VG_STATIC void vg_linear_clear( void *buffer )
276 {
277 vg_linear_allocator *alloc = vg_linear_header( buffer );
278
279 /* libc mode we recursively free any allocations made */
280 if( vg_mem.use_libc_malloc && (alloc->flags & VG_MEMORY_SYSTEM) ){
281 for( u32 i=0; i<alloc->allocation_count; i++ ){
282 vg_allocation_meta *meta = &alloc->alloc_table[i];
283
284 if( meta->type == k_allocation_type_block ){
285 free( meta->data );
286 }
287 else{
288 vg_linear_clear( meta->data );
289 vg_linear_allocator *sub = vg_linear_header( meta->data );
290
291 free( sub->alloc_table );
292 free( sub );
293 }
294 }
295 }
296
297 alloc->last_alloc = NULL;
298 alloc->last_alloc_size = 0;
299 alloc->allocation_count = 0;
300 alloc->cur = 0;
301 }
302
303 /* allocate a FIXED SIZE linear allocator */
304 VG_STATIC void *vg_create_linear_allocator( void *lin_alloc, u32 size,
305 u16 flags )
306 {
307 assert( sizeof( vg_linear_allocator ) == 32 );
308
309 vg_linear_allocator *header;
310 u32 block_size = size + sizeof(vg_linear_allocator);
311
312 /* Creating it inside an existing one */
313 if( lin_alloc ){
314 vg_linear_allocator *alloc = vg_linear_header( lin_alloc );
315
316 if( alloc->cur + block_size > alloc->size )
317 vg_fatal_exit_loop( "Out of memory" );
318
319 if( alloc->allocation_count + 1 > VG_MAX_ALLOCATIONS )
320 vg_fatal_exit_loop( "Max allocations in linear allocator" );
321
322 if( (flags && VG_MEMORY_SYSTEM) && (alloc->flags & VG_MEMORY_REALTIME) )
323 vg_fatal_exit_loop( "Cannot declare realtime allocator inside systems"
324 " allocator" );
325
326 if( vg_mem.use_libc_malloc ){
327 vg_allocation_meta *meta =
328 &alloc->alloc_table[ alloc->allocation_count ];
329
330 if( flags & VG_MEMORY_REALTIME )
331 header = malloc( block_size );
332 else
333 header = malloc( sizeof(vg_linear_allocator) );
334
335 meta->data = header+1;
336 meta->type = k_allocation_type_linear;
337 }
338 else{
339 header = lin_alloc + alloc->cur;
340 }
341
342 alloc->cur += block_size;
343 alloc->last_alloc = NULL; /* cant resize this block! */
344 alloc->last_alloc_size = block_size;
345
346 alloc->allocation_count ++;
347 }
348 else{
349 if( vg_mem.use_libc_malloc && (flags & VG_MEMORY_SYSTEM) )
350 header = malloc( sizeof(vg_linear_allocator) );
351 else
352 header = malloc( block_size );
353 }
354
355 header->allocation_count = 0;
356 header->cur = 0;
357 header->last_alloc = NULL;
358 header->last_alloc_size = 0;
359 header->size = size;
360 header->flags = flags;
361
362 if( vg_mem.use_libc_malloc && (flags & VG_MEMORY_SYSTEM) ){
363 u32 table_size = sizeof(vg_allocation_meta)*VG_MAX_ALLOCATIONS;
364 header->alloc_table = malloc( table_size );
365 }
366 else
367 header->alloc_table = NULL;
368
369 return header+1;
370 }
371
372 /* request all the memory we need in advance */
373 VG_STATIC void vg_set_mem_quota( u32 size )
374 {
375 vg_mem.quota = size;
376 }
377
378 VG_STATIC void vg_alloc_quota(void)
379 {
380 u32 size_scratch = 10*1024*1024;
381 u32 size = VG_MAX( vg_mem.quota, size_scratch );
382
383 vg_mem.rtmemory = vg_create_linear_allocator( NULL, size, VG_MEMORY_SYSTEM );
384 vg_mem.scratch = vg_create_linear_allocator( vg_mem.rtmemory,
385 size_scratch,
386 VG_MEMORY_SYSTEM );
387 }
388
389 #endif /* VG_MEM_H */