X-Git-Url: https://harrygodden.com/git/?p=vg.git;a=blobdiff_plain;f=vg_mem.c;fp=vg_mem.c;h=797407913bd65f36d6ef65d077642a004fdda1f4;hp=0000000000000000000000000000000000000000;hb=3b14f3dcd5bf9dd3c85144f2123d667bfa4bb63f;hpb=fce86711735b15bff37de0f70716808410fcf269 diff --git a/vg_mem.c b/vg_mem.c new file mode 100644 index 0000000..7974079 --- /dev/null +++ b/vg_mem.c @@ -0,0 +1,348 @@ +#pragma once + +#include "vg_platform.h" +#include "vg_log.h" +#include "vg_mem.h" + +#include +#include + +struct vg_global_mem vg_mem; + +u32 vg_align8( u32 s ) +{ + u32 m = (s + 7) >> 3; + return m << 3; +} + +u32 vg_align4( u32 s ) +{ + u32 m = (s + 3) >> 2; + return m << 2; +} + +/* Returns allocator structure from data pointer */ +vg_linear_allocator *vg_linear_header( void *data ) +{ + vg_linear_allocator *ptr = data; + ptr --; + + return ptr; +} + +/* allocate something from a linear allocator */ +__attribute__((warn_unused_result)) +void *_vg_linear_alloc( void *buffer, u32 size, const char *constr_name ) +{ + if( size % 8 ){ + vg_error( "alloc(%u) is not 8 byte aligned\n", size ); + vg_print_backtrace(); + size = vg_align8( size ); + } + if( ((u64)buffer) % 8 ){ + vg_fatal_error( "unaligned buffer (%p)", buffer ); + } + + vg_linear_allocator *alloc = vg_linear_header( buffer ); + + if( (alloc->cur + size) > alloc->size ){ + vg_fatal_error( "linear allocator overflow (%u + %u > %u)\n", + alloc->cur, size, alloc->size ); + } + + if( alloc->flags & VG_MEMORY_SYSTEM ) + if( (alloc->allocation_count + 1) > VG_MAX_ALLOCATIONS ) + vg_fatal_error( "Max linear allocations reached" ); + + void *data; + + if( vg_mem.use_libc_malloc && (alloc->flags & VG_MEMORY_SYSTEM) ){ + data = malloc( size ); + + vg_allocation_meta *meta = &alloc->alloc_table[ alloc->allocation_count ]; + meta->type = k_allocation_type_block; + meta->data = data; + meta->size = size; + meta->name = constr_name; + } + else{ + data = buffer + alloc->cur; + } + + u8 *bytes = data; + for( u32 i=0; iallocation_count ++; + alloc->last_alloc = data; + alloc->last_alloc_size = size; + alloc->cur += size; + + if( ((u64)data) % 8 ){ + vg_fatal_error( "unaligned" ); + } + + return data; +} + +/* resize latest block of memory from linear */ +__attribute__((warn_unused_result)) +void *vg_linear_resize( void *buffer, void *data, u32 newsize ) +{ + vg_linear_allocator *alloc = vg_linear_header( buffer ); + + if( newsize % 8 ){ + vg_error( "alloc(%u) is not 8 byte aligned\n", newsize ); + vg_print_backtrace(); + newsize = vg_align8( newsize ); + } + + if( alloc->last_alloc != data ) + vg_fatal_error( "This block has been fixed!" ); + + if( (alloc->cur - alloc->last_alloc_size + newsize) > alloc->size ) + vg_fatal_error( "Cannot resize, overflow" ); + + alloc->cur -= alloc->last_alloc_size; + alloc->cur += newsize; + alloc->last_alloc_size = newsize; + + if( vg_mem.use_libc_malloc && (alloc->flags & VG_MEMORY_SYSTEM) ){ + data = realloc( data, newsize ); + if( !data ) + vg_fatal_error( "realloc failed" ); + + alloc->alloc_table[ alloc->allocation_count-1 ].data = data; + alloc->last_alloc = data; + return data; + } + else{ + return data; + } +} + +/* its possible to delete just the last item */ +void vg_linear_del( void *buffer, void *data ) +{ + vg_linear_allocator *alloc = vg_linear_header( buffer ); + + if( alloc->last_alloc != data ){ + vg_fatal_error( "This block has been fixed! Last alloc: %p, this: %p\n", + alloc->last_alloc, data ); + } + + if( vg_mem.use_libc_malloc && (alloc->flags & VG_MEMORY_SYSTEM) ){ + vg_allocation_meta *meta = &alloc->alloc_table[alloc->allocation_count-1]; + if( meta->type == k_allocation_type_linear ) + vg_fatal_error( "Cannot free a linear allocator in this conext" ); + + free( data ); + } + + alloc->allocation_count --; + alloc->cur -= alloc->last_alloc_size; + alloc->last_alloc = NULL; + alloc->last_alloc_size = 0; +} + +/* extend latest block of memory from linear */ +__attribute__((warn_unused_result)) +void *_vg_linear_extend( void *buffer, void *data, u32 extra, + const char *constr_name ) +{ + if( !data ) + return _vg_linear_alloc( buffer, vg_align8(extra), constr_name ); + + vg_linear_allocator *alloc = vg_linear_header( buffer ); + + if( alloc->last_alloc != data ) + vg_fatal_error( "This block has been fixed!" ); + + u32 new_size = alloc->last_alloc_size + extra; + return vg_linear_resize( buffer, data, vg_align8(new_size) ); +} + +/* get the current usage of allocator */ +u32 vg_linear_get_cur( void *buffer ) +{ + vg_linear_allocator *alloc = vg_linear_header( buffer ); + return alloc->cur; +} + +/* get the capacity of allocator. */ +u32 vg_linear_get_capacity( void *buffer ) +{ + vg_linear_allocator *alloc = vg_linear_header( buffer ); + return alloc->size; +} + +/* get the remaining size of the allocator */ +u32 vg_linear_remaining( void *buffer ) +{ + vg_linear_allocator *alloc = vg_linear_header( buffer ); + return alloc->size - alloc->cur; +} + +/* yeet all memory from linear allocator */ +void vg_linear_clear( void *buffer ) +{ + vg_linear_allocator *alloc = vg_linear_header( buffer ); + + /* libc mode we recursively free any allocations made */ + if( vg_mem.use_libc_malloc && (alloc->flags & VG_MEMORY_SYSTEM) ){ + for( u32 i=0; iallocation_count; i++ ){ + vg_allocation_meta *meta = &alloc->alloc_table[i]; + + if( meta->type == k_allocation_type_block ){ + free( meta->data ); + } + else{ + vg_linear_clear( meta->data ); + vg_linear_allocator *sub = vg_linear_header( meta->data ); + + free( sub->alloc_table ); + free( sub ); + } + } + } + + alloc->last_alloc = NULL; + alloc->last_alloc_size = 0; + alloc->allocation_count = 0; + alloc->cur = 0; +} + +/* allocate a FIXED SIZE linear allocator + * + * FIXME: there was a bug in vg's code that caused a race condition between + * two system allocations. make this IMPOSSIBLE by requiring a lock + * on the allocater to be passed between threads. + * + * luckily that bug only exists when using development tools, but still! + * + * this should then only be checked and turned on in debugging. + * + */ +void *_vg_create_linear_allocator( void *lin_alloc, u32 size, + u16 flags, const char *constr_name) +{ + if( sizeof( vg_linear_allocator ) != 32 ) + vg_fatal_error( "Programming error" ); + + vg_linear_allocator *header; + u32 block_size = size + sizeof(vg_linear_allocator); + + /* Creating it inside an existing one */ + if( lin_alloc ){ + vg_linear_allocator *alloc = vg_linear_header( lin_alloc ); + + if( alloc->cur + block_size > alloc->size ) + vg_fatal_error( "Out of memory" ); + + if( alloc->allocation_count + 1 > VG_MAX_ALLOCATIONS ) + vg_fatal_error( "Max allocations in linear allocator" ); + + if( (flags && VG_MEMORY_SYSTEM) && (alloc->flags & VG_MEMORY_REALTIME) ) + vg_fatal_error( "Cannot declare realtime allocator inside systems" + " allocator" ); + + if( vg_mem.use_libc_malloc ){ + vg_allocation_meta *meta = + &alloc->alloc_table[ alloc->allocation_count ]; + + if( flags & VG_MEMORY_REALTIME ) + header = malloc( block_size ); + else + header = malloc( sizeof(vg_linear_allocator) ); + + meta->data = header+1; + meta->type = k_allocation_type_linear; + meta->size = size; + meta->name = constr_name; + } + else{ + header = lin_alloc + alloc->cur; + } + + alloc->cur += block_size; + alloc->last_alloc = header; + alloc->last_alloc_size = block_size; + alloc->allocation_count ++; + } + else{ + if( vg_mem.use_libc_malloc && (flags & VG_MEMORY_SYSTEM) ) + header = malloc( sizeof(vg_linear_allocator) ); + else + header = malloc( block_size ); + } + + header->allocation_count = 0; + header->cur = 0; + header->last_alloc = NULL; + header->last_alloc_size = 0; + header->size = size; + header->flags = flags; + + if( vg_mem.use_libc_malloc && (flags & VG_MEMORY_SYSTEM) ){ + u32 table_size = sizeof(vg_allocation_meta)*VG_MAX_ALLOCATIONS; + header->alloc_table = malloc( table_size ); + } + else + header->alloc_table = NULL; + + return header+1; +} + +/* request all the memory we need in advance */ +void vg_set_mem_quota( u32 size ) +{ + vg_mem.quota = size; +} + +void vg_alloc_quota(void) +{ + u32 size_scratch = 10*1024*1024; + u32 size = VG_MAX( vg_mem.quota, size_scratch ); + + vg_mem.rtmemory = _vg_create_linear_allocator( NULL, size, VG_MEMORY_SYSTEM, + "VG Root" ); + vg_mem.scratch = _vg_create_linear_allocator( vg_mem.rtmemory, + size_scratch, + VG_MEMORY_SYSTEM, + "Scratch buffer" ); +} + +void vg_mem_log( void *lin_alloc, int depth, const char *name ) +{ + if( vg_mem.use_libc_malloc ){ + vg_linear_allocator *alloc = vg_linear_header( lin_alloc ); + + u32 s = alloc->size; + f32 p = ((float)alloc->cur / (float)alloc->size) * 100.0f; + + for( int i=0; iflags & VG_MEMORY_SYSTEM ){ + for( u32 i=0; iallocation_count; i++ ){ + vg_allocation_meta *meta = &alloc->alloc_table[i]; + + if( meta->type == k_allocation_type_block ){ + for( int i=0; iname, meta->size ); + } + else{ + vg_mem_log( meta->data, depth +1, meta->name ); + } + } + } + else{ + for( int i=0; i (UNTRACKED)\n" ); + } + } + else{ + vg_error( "allocations are not tracked (turn on libc mode)\n" ); + } +}