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