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