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