msg api, mem api
[vg.git] / vg_mem.c
1 #pragma once
2
3 #include "vg_platform.h"
4 #include "vg_log.h"
5 #include "vg_mem.h"
6
7 #include <stdlib.h>
8 #include <malloc.h>
9
10 struct vg_global_mem vg_mem;
11
12 u32 vg_align8( u32 s )
13 {
14 u32 m = (s + 7) >> 3;
15 return m << 3;
16 }
17
18 u32 vg_align4( u32 s )
19 {
20 u32 m = (s + 3) >> 2;
21 return m << 2;
22 }
23
24 /* Returns allocator structure from data pointer */
25 vg_linear_allocator *vg_linear_header( void *data )
26 {
27 vg_linear_allocator *ptr = data;
28 ptr --;
29
30 return ptr;
31 }
32
33 /* allocate something from a linear allocator */
34 __attribute__((warn_unused_result))
35 void *_vg_linear_alloc( void *buffer, u32 size, const char *constr_name )
36 {
37 if( size % 8 )
38 size = vg_align8( size );
39
40 if( ((u64)buffer) % 8 )
41 vg_fatal_error( "unaligned buffer (%p)", buffer );
42
43 vg_linear_allocator *alloc = vg_linear_header( buffer );
44
45 if( (alloc->cur + size) > alloc->size )
46 {
47 vg_fatal_error( "linear allocator overflow (%u + %u > %u)\n",
48 alloc->cur, size, alloc->size );
49 }
50
51 if( alloc->flags & VG_MEMORY_SYSTEM )
52 if( (alloc->allocation_count + 1) > VG_MAX_ALLOCATIONS )
53 vg_fatal_error( "Max linear allocations reached" );
54
55 void *data;
56
57 if( vg_mem.use_libc_malloc && (alloc->flags & VG_MEMORY_SYSTEM) ){
58 data = malloc( size );
59
60 vg_allocation_meta *meta = &alloc->alloc_table[ alloc->allocation_count ];
61 meta->type = k_allocation_type_block;
62 meta->data = data;
63 meta->size = size;
64 meta->name = constr_name;
65 }
66 else{
67 data = buffer + alloc->cur;
68 }
69
70 u8 *bytes = data;
71 for( u32 i=0; i<size; i++ ){
72 bytes[i] = 0xfe;
73 }
74
75 alloc->allocation_count ++;
76 alloc->last_alloc = data;
77 alloc->last_alloc_size = size;
78 alloc->cur += size;
79
80 if( ((u64)data) % 8 ){
81 vg_fatal_error( "unaligned" );
82 }
83
84 return data;
85 }
86
87 /* resize latest block of memory from linear */
88 __attribute__((warn_unused_result))
89 void *vg_linear_resize( void *buffer, void *data, u32 newsize )
90 {
91 vg_linear_allocator *alloc = vg_linear_header( buffer );
92
93 if( newsize % 8 ){
94 vg_error( "alloc(%u) is not 8 byte aligned\n", newsize );
95 vg_print_backtrace();
96 newsize = vg_align8( newsize );
97 }
98
99 if( alloc->last_alloc != data )
100 vg_fatal_error( "This block has been fixed!" );
101
102 if( (alloc->cur - alloc->last_alloc_size + newsize) > alloc->size )
103 vg_fatal_error( "Cannot resize, overflow" );
104
105 alloc->cur -= alloc->last_alloc_size;
106 alloc->cur += newsize;
107 alloc->last_alloc_size = newsize;
108
109 if( vg_mem.use_libc_malloc && (alloc->flags & VG_MEMORY_SYSTEM) ){
110 data = realloc( data, newsize );
111 if( !data )
112 vg_fatal_error( "realloc failed" );
113
114 alloc->alloc_table[ alloc->allocation_count-1 ].data = data;
115 alloc->last_alloc = data;
116 return data;
117 }
118 else{
119 return data;
120 }
121 }
122
123 /* its possible to delete just the last item */
124 void vg_linear_del( void *buffer, void *data )
125 {
126 vg_linear_allocator *alloc = vg_linear_header( buffer );
127
128 if( alloc->last_alloc != data ){
129 vg_fatal_error( "This block has been fixed! Last alloc: %p, this: %p\n",
130 alloc->last_alloc, data );
131 }
132
133 if( vg_mem.use_libc_malloc && (alloc->flags & VG_MEMORY_SYSTEM) ){
134 vg_allocation_meta *meta = &alloc->alloc_table[alloc->allocation_count-1];
135 if( meta->type == k_allocation_type_linear )
136 vg_fatal_error( "Cannot free a linear allocator in this conext" );
137
138 free( data );
139 }
140
141 alloc->allocation_count --;
142 alloc->cur -= alloc->last_alloc_size;
143 alloc->last_alloc = NULL;
144 alloc->last_alloc_size = 0;
145 }
146
147 /* extend latest block of memory from linear */
148 __attribute__((warn_unused_result))
149 void *_vg_linear_extend( void *buffer, void *data, u32 extra,
150 const char *constr_name )
151 {
152 if( !data )
153 return _vg_linear_alloc( buffer, vg_align8(extra), constr_name );
154
155 vg_linear_allocator *alloc = vg_linear_header( buffer );
156
157 if( alloc->last_alloc != data )
158 vg_fatal_error( "This block has been fixed!" );
159
160 u32 new_size = alloc->last_alloc_size + extra;
161 return vg_linear_resize( buffer, data, vg_align8(new_size) );
162 }
163
164 /* get the current usage of allocator */
165 u32 vg_linear_get_cur( void *buffer )
166 {
167 vg_linear_allocator *alloc = vg_linear_header( buffer );
168 return alloc->cur;
169 }
170
171 /* get the capacity of allocator. */
172 u32 vg_linear_get_capacity( void *buffer )
173 {
174 vg_linear_allocator *alloc = vg_linear_header( buffer );
175 return alloc->size;
176 }
177
178 /* get the remaining size of the allocator */
179 u32 vg_linear_remaining( void *buffer )
180 {
181 vg_linear_allocator *alloc = vg_linear_header( buffer );
182 return alloc->size - alloc->cur;
183 }
184
185 /* yeet all memory from linear allocator */
186 void vg_linear_clear( void *buffer )
187 {
188 vg_linear_allocator *alloc = vg_linear_header( buffer );
189
190 /* libc mode we recursively free any allocations made */
191 if( vg_mem.use_libc_malloc && (alloc->flags & VG_MEMORY_SYSTEM) ){
192 for( u32 i=0; i<alloc->allocation_count; i++ ){
193 vg_allocation_meta *meta = &alloc->alloc_table[i];
194
195 if( meta->type == k_allocation_type_block ){
196 free( meta->data );
197 }
198 else{
199 vg_linear_clear( meta->data );
200 vg_linear_allocator *sub = vg_linear_header( meta->data );
201
202 free( sub->alloc_table );
203 free( sub );
204 }
205 }
206 }
207
208 alloc->last_alloc = NULL;
209 alloc->last_alloc_size = 0;
210 alloc->allocation_count = 0;
211 alloc->cur = 0;
212 }
213
214 /* allocate a FIXED SIZE linear allocator
215 *
216 * FIXME: there was a bug in vg's code that caused a race condition between
217 * two system allocations. make this IMPOSSIBLE by requiring a lock
218 * on the allocater to be passed between threads.
219 *
220 * luckily that bug only exists when using development tools, but still!
221 *
222 * this should then only be checked and turned on in debugging.
223 *
224 */
225 void *_vg_create_linear_allocator( void *lin_alloc, u32 size,
226 u16 flags, const char *constr_name)
227 {
228 if( sizeof( vg_linear_allocator ) != 32 )
229 vg_fatal_error( "Programming error" );
230
231 vg_linear_allocator *header;
232 u32 block_size = size + sizeof(vg_linear_allocator);
233
234 /* Creating it inside an existing one */
235 if( lin_alloc ){
236 vg_linear_allocator *alloc = vg_linear_header( lin_alloc );
237
238 if( alloc->cur + block_size > alloc->size )
239 vg_fatal_error( "Out of memory" );
240
241 if( alloc->allocation_count + 1 > VG_MAX_ALLOCATIONS )
242 vg_fatal_error( "Max allocations in linear allocator" );
243
244 if( (flags && VG_MEMORY_SYSTEM) && (alloc->flags & VG_MEMORY_REALTIME) )
245 vg_fatal_error( "Cannot declare realtime allocator inside systems"
246 " allocator" );
247
248 if( vg_mem.use_libc_malloc ){
249 vg_allocation_meta *meta =
250 &alloc->alloc_table[ alloc->allocation_count ];
251
252 if( flags & VG_MEMORY_REALTIME )
253 header = malloc( block_size );
254 else
255 header = malloc( sizeof(vg_linear_allocator) );
256
257 meta->data = header+1;
258 meta->type = k_allocation_type_linear;
259 meta->size = size;
260 meta->name = constr_name;
261 }
262 else{
263 header = lin_alloc + alloc->cur;
264 }
265
266 alloc->cur += block_size;
267 alloc->last_alloc = header;
268 alloc->last_alloc_size = block_size;
269 alloc->allocation_count ++;
270 }
271 else{
272 if( vg_mem.use_libc_malloc && (flags & VG_MEMORY_SYSTEM) )
273 header = malloc( sizeof(vg_linear_allocator) );
274 else
275 header = malloc( block_size );
276 }
277
278 header->allocation_count = 0;
279 header->cur = 0;
280 header->last_alloc = NULL;
281 header->last_alloc_size = 0;
282 header->size = size;
283 header->flags = flags;
284
285 if( vg_mem.use_libc_malloc && (flags & VG_MEMORY_SYSTEM) ){
286 u32 table_size = sizeof(vg_allocation_meta)*VG_MAX_ALLOCATIONS;
287 header->alloc_table = malloc( table_size );
288 }
289 else
290 header->alloc_table = NULL;
291
292 return header+1;
293 }
294
295 /* request all the memory we need in advance */
296 void vg_set_mem_quota( u32 size )
297 {
298 vg_mem.quota = size;
299 }
300
301 void vg_alloc_quota(void)
302 {
303 u32 size_scratch = 10*1024*1024;
304 u32 size = VG_MAX( vg_mem.quota, size_scratch );
305
306 vg_mem.rtmemory = _vg_create_linear_allocator( NULL, size, VG_MEMORY_SYSTEM,
307 "VG Root" );
308 vg_mem.scratch = _vg_create_linear_allocator( vg_mem.rtmemory,
309 size_scratch,
310 VG_MEMORY_SYSTEM,
311 "Scratch buffer" );
312 }
313
314 void vg_mem_log( void *lin_alloc, int depth, const char *name )
315 {
316 if( vg_mem.use_libc_malloc ){
317 vg_linear_allocator *alloc = vg_linear_header( lin_alloc );
318
319 u32 s = alloc->size;
320 f32 p = ((float)alloc->cur / (float)alloc->size) * 100.0f;
321
322 for( int i=0; i<depth; i++ ) printf( " " );
323 printf( "LA(%s): %u bytes, %f%% used\n", name, s, p );
324
325 if( alloc->flags & VG_MEMORY_SYSTEM ){
326 for( u32 i=0; i<alloc->allocation_count; i++ ){
327 vg_allocation_meta *meta = &alloc->alloc_table[i];
328
329 if( meta->type == k_allocation_type_block ){
330 for( int i=0; i<depth+1; i++ ) printf( " " );
331 printf( "B(%s): %u bytes\n", meta->name, meta->size );
332 }
333 else{
334 vg_mem_log( meta->data, depth +1, meta->name );
335 }
336 }
337 }
338 else{
339 for( int i=0; i<depth+1; i++ ) printf( " " );
340 printf( "<opaque memory> (UNTRACKED)\n" );
341 }
342 }
343 else{
344 vg_error( "allocations are not tracked (turn on libc mode)\n" );
345 }
346 }