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