X-Git-Url: https://harrygodden.com/git/?a=blobdiff_plain;f=src%2Fvg%2Fvg_mem.h;h=3a7a49da06814fe9c2c7d1d1b082ee6cc6a8ed5a;hb=3dd767bb10e6fee9cbffeb185d1a9685810c17b5;hp=1ef1e77260eecd2e49ebbb4f069b6167a241352a;hpb=5df88af6730a8b9c4ef698070bb729866ed2e597;p=vg.git diff --git a/src/vg/vg_mem.h b/src/vg/vg_mem.h index 1ef1e77..3a7a49d 100644 --- a/src/vg/vg_mem.h +++ b/src/vg/vg_mem.h @@ -1,196 +1,355 @@ #ifndef VG_MEM_H #define VG_MEM_H +#include "vg.h" #include "vg_stdint.h" #include "vg_platform.h" #include +#include + +#define VG_MAX_ALLOCATIONS 64 +#define VG_FUZZ_ALLOCATIONS typedef struct vg_linear_allocator vg_linear_allocator; +typedef struct vg_allocation_meta vg_allocation_meta; struct { void *rtmemory, *scratch; + + int use_libc_malloc; + u32 quota; } static vg_mem; +struct vg_allocation_meta +{ + void *data; + enum allocation_type + { + k_allocation_type_block = 0, + k_allocation_type_linear = 1 + } + type; +}; + +#define VG_MEMORY_SYSTEM 0x1 /* systems memory, slow and low counts */ +#define VG_MEMORY_REALTIME 0x2 /* per-frame. no max allocs, only size. fast */ + +/* systems memory cannot be declared inside realtime memory regions */ + +/* + * Stored just behind the array. 32 bytes. + */ #pragma pack(push,1) struct vg_linear_allocator { - u32 size, cur; - - /* allows temporarily extendable buffers */ - void *last_alloc; + u32 size; + /* */ + /* */ + /* */ + u32 cur; + /* */ + /* */ + /* */ + u16 allocation_count; + /* */ + u16 flags; + /* */ u32 last_alloc_size; + /* */ + /* */ + /* */ + void *last_alloc; + /* */ + /* */ + /* */ + /* */ + /* */ + /* */ + /* */ + vg_allocation_meta *alloc_table; + /* */ + /* */ + /* */ + /* */ + /* */ + /* */ + /* */ + +#ifdef _WIN32 + /* 32 bit pointers! */ + u8 padding[ 8 ]; +#endif }; #pragma pack(pop) -/* - * TODO: Fallback on libc - * TODO: 8 byte alignment - */ - +VG_STATIC void vg_fatal_exit_loop( const char *error ); VG_STATIC void vg_error(const char *fmt, ...); +VG_STATIC void vg_info(const char *fmt, ...); + +/* Returns allocator structure from data pointer */ +static 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)) -VG_STATIC void *vg_linear_alloc( void *allocator, u32 size ) +VG_STATIC void *vg_linear_alloc( void *buffer, u32 size ) { - if( allocator == NULL ) - vg_fatal_exit_loop( "Null allocator" ); + vg_linear_allocator *alloc = vg_linear_header( buffer ); + + if( (alloc->cur + size) > alloc->size ) + { + vg_error( "%u + %u > %u\n", alloc->cur, size, alloc->size ); + vg_fatal_exit_loop( "linear allocator overflow" ); + } + + if( alloc->flags & VG_MEMORY_SYSTEM ) + if( (alloc->allocation_count + 1) > VG_MAX_ALLOCATIONS ) + vg_fatal_exit_loop( "Max linear allocations reached" ); - vg_linear_allocator *allocptr = allocator; - allocptr --; + void *data; - if( allocptr->cur + size > allocptr->size ) + if( vg_mem.use_libc_malloc && (alloc->flags & VG_MEMORY_SYSTEM) ) { - vg_error( "%u(current) + %u(alloc) > %u(max)\n", - allocptr->cur, size, allocptr->size ); + data = malloc( size ); - vg_fatal_exit_loop( "Linear allocator out of memory" ); + vg_allocation_meta *meta = &alloc->alloc_table[ alloc->allocation_count ]; + meta->type = k_allocation_type_block; + meta->data = data; + } + else + { + data = buffer + alloc->cur; } - void *data = allocator + allocptr->cur; - allocptr->cur += size; - allocptr->last_alloc = data; - allocptr->last_alloc_size = size; + alloc->allocation_count ++; + alloc->last_alloc = data; + alloc->last_alloc_size = size; + alloc->cur += size; return data; } + /* resize latest block of memory from linear */ __attribute__((warn_unused_result)) -VG_STATIC void *vg_linear_resize( void *allocator, void *data, u32 newsize ) +VG_STATIC void *vg_linear_resize( void *buffer, void *data, u32 newsize ) { - vg_linear_allocator *allocptr = allocator; - allocptr --; + vg_linear_allocator *alloc = vg_linear_header( buffer ); - if( allocptr->last_alloc == data ) + if( alloc->last_alloc != data ) + vg_fatal_exit_loop( "This block has been fixed!" ); + + if( (alloc->cur - alloc->last_alloc_size + newsize) > alloc->size ) + vg_fatal_exit_loop( "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) ) { - allocptr->cur -= allocptr->last_alloc_size; - return vg_linear_alloc( allocator, newsize ); + data = realloc( data, newsize ); + if( !data ) + vg_fatal_exit_loop( "realloc failed" ); + + alloc->alloc_table[ alloc->allocation_count-1 ].data = data; + alloc->last_alloc = data; + return data; } else { - vg_fatal_exit_loop( "Cannot resize this buffer anymore" ); + return data; } - - return NULL; } -VG_STATIC void vg_linear_del( void *allocator, void *data ) +/* its possible to delete just the last item */ +VG_STATIC void vg_linear_del( void *buffer, void *data ) { - void *ignore = vg_linear_resize( allocator, data, 0 ); + vg_linear_allocator *alloc = vg_linear_header( buffer ); + + if( alloc->last_alloc != data ) + vg_fatal_exit_loop( "This block has been fixed!" ); - vg_linear_allocator *allocptr = allocator; - allocptr --; + 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_exit_loop( "Cannot free a linear allocator in this conext" ); + + free( data ); + } - allocptr->last_alloc = NULL; - allocptr->last_alloc_size = 0; + 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)) -VG_STATIC void *vg_linear_extend( void *allocator, void *data, u32 extra ) +VG_STATIC void *vg_linear_extend( void *buffer, void *data, u32 extra ) { - vg_linear_allocator *allocptr = allocator; - allocptr --; + vg_linear_allocator *alloc = vg_linear_header( buffer ); - return vg_linear_resize( allocator, data, allocptr->last_alloc_size+extra ); + if( alloc->last_alloc != data ) + vg_fatal_exit_loop( "This block has been fixed!" ); + + u32 new_size = alloc->last_alloc_size + extra; + return vg_linear_resize( buffer, data, new_size ); } /* get the current usage of allocator */ -VG_STATIC u32 vg_linear_get_cur( void *allocator ) +VG_STATIC u32 vg_linear_get_cur( void *buffer ) { - vg_linear_allocator *allocptr = allocator; - allocptr --; - - return allocptr->cur; + vg_linear_allocator *alloc = vg_linear_header( buffer ); + return alloc->cur; } -/* get the capacity of allocator */ -VG_STATIC u32 vg_linear_get_capacity( void *allocator ) +/* get the capacity of allocator. */ +VG_STATIC u32 vg_linear_get_capacity( void *buffer ) { - vg_linear_allocator *allocptr = allocator; - allocptr --; - - return allocptr->size; + vg_linear_allocator *alloc = vg_linear_header( buffer ); + return alloc->size; } -/* get the size of the last allocated thing */ -VG_STATIC u32 vg_linear_last_size( void *allocator ) +/* get the remaining size of the allocator */ +VG_STATIC u32 vg_linear_remaining( void *buffer ) { - vg_linear_allocator *allocptr = allocator; - allocptr --; - - return allocptr->last_alloc_size; + vg_linear_allocator *alloc = vg_linear_header( buffer ); + return alloc->size - alloc->cur; } /* yeet all memory from linear allocator */ -VG_STATIC void vg_linear_clear( void *allocator ) +VG_STATIC void vg_linear_clear( void *buffer ) { - vg_linear_allocator *allocptr = allocator; - allocptr --; + vg_linear_allocator *alloc = vg_linear_header( buffer ); - allocptr->last_alloc = NULL; - allocptr->last_alloc_size = 0; - allocptr->cur = 0; + /* 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 */ -VG_STATIC void *vg_create_linear_allocator( void *lin_alloc, u32 size ) +VG_STATIC void *vg_create_linear_allocator( void *lin_alloc, u32 size, + u16 flags ) { - u32 total = size + sizeof(vg_linear_allocator); - vg_linear_allocator *allocptr; + assert( sizeof( vg_linear_allocator ) == 32 ); - if( lin_alloc == NULL ) + vg_linear_allocator *header; + u32 block_size = size + sizeof(vg_linear_allocator); + + /* Creating it inside an existing one */ + if( lin_alloc ) { - static int allow_once = 1; + vg_linear_allocator *alloc = vg_linear_header( lin_alloc ); - if( allow_once ) - { - allocptr = malloc( total ); + if( alloc->cur + block_size > alloc->size ) + vg_fatal_exit_loop( "Out of memory" ); + + if( alloc->allocation_count + 1 > VG_MAX_ALLOCATIONS ) + vg_fatal_exit_loop( "Max allocations in linear allocator" ); - if( allocptr == NULL ) - vg_fatal_exit_loop( "Create linear: Malloc failed" ); + if( (flags && VG_MEMORY_SYSTEM) && (alloc->flags & VG_MEMORY_REALTIME) ) + vg_fatal_exit_loop( "Cannot declare realtime allocator inside systems" + " allocator" ); - allow_once = 0; + 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; } else - vg_fatal_exit_loop( "Shouldnt call this twice!" ); + { + header = lin_alloc + alloc->cur; + } + + alloc->cur += block_size; + alloc->last_alloc = NULL; /* cant resize this block! */ + alloc->last_alloc_size = block_size; + + alloc->allocation_count ++; } else { - vg_linear_allocator *parent = lin_alloc; - parent --; + if( vg_mem.use_libc_malloc && (flags & VG_MEMORY_SYSTEM) ) + header = malloc( sizeof(vg_linear_allocator) ); + else + header = malloc( block_size ); + } - allocptr = vg_linear_alloc( lin_alloc, total ); + header->allocation_count = 0; + header->cur = 0; + header->last_alloc = NULL; + header->last_alloc_size = 0; + header->size = size; + header->flags = flags; -#if 0 - parent->last_alloc = allocptr+1; - parent->last_alloc_size = total; -#else - parent->last_alloc = NULL; - parent->last_alloc_size = total; -#endif + 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 ); } - - allocptr->size = size; - allocptr->cur = 0; - allocptr->last_alloc = NULL; - allocptr->last_alloc_size = 0; - - void *data = allocptr+1; - return data; + else + header->alloc_table = NULL; + + return header+1; } /* request all the memory we need in advance */ -VG_STATIC void vg_prealloc_quota( u32 size ) +VG_STATIC void vg_set_mem_quota( u32 size ) { - size = VG_MAX( size, 20*1024*1024 ); - vg_mem.rtmemory = vg_create_linear_allocator( NULL, size ); - vg_mem.scratch = vg_create_linear_allocator( vg_mem.rtmemory, 10*1024*1024 ); + vg_mem.quota = size; +} + +VG_STATIC 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_mem.scratch = vg_create_linear_allocator( vg_mem.rtmemory, + size_scratch, + VG_MEMORY_SYSTEM ); } #endif /* VG_MEM_H */