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