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