build system revision
[vg.git] / vg_mem.c
diff --git a/vg_mem.c b/vg_mem.c
new file mode 100644 (file)
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 <stdlib.h>
+#include <malloc.h>
+
+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; i<size; i++ ){
+      bytes[i] = 0xfe;
+   }
+
+   alloc->allocation_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; i<alloc->allocation_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; i<depth; i++ ) printf( "  " );
+      printf( "LA(%s): %u bytes, %f%% used\n", name, s, p );
+
+      if( alloc->flags & VG_MEMORY_SYSTEM ){
+         for( u32 i=0; i<alloc->allocation_count; i++ ){
+            vg_allocation_meta *meta = &alloc->alloc_table[i];
+            
+            if( meta->type == k_allocation_type_block ){
+               for( int i=0; i<depth+1; i++ ) printf( "  " );
+               printf( "B(%s): %u bytes\n", meta->name, meta->size );
+            }
+            else{
+               vg_mem_log( meta->data, depth +1, meta->name );
+            }
+         }
+      }
+      else{
+         for( int i=0; i<depth+1; i++ ) printf( "  " );
+         printf( "<opaque memory> (UNTRACKED)\n" );
+      }
+   }
+   else{
+      vg_error( "allocations are not tracked (turn on libc mode)\n" );
+   }
+}