simplify gitignore
[vg.git] / src / vg / vg_mem.h
index a3c7cd6e8d92b343b2627e0d1a29efb343e1d296..3a7a49da06814fe9c2c7d1d1b082ee6cc6a8ed5a 100644 (file)
 #ifndef VG_MEM_H
 #define VG_MEM_H
 
+#include "vg.h"
 #include "vg_stdint.h"
 #include "vg_platform.h"
 
 #include <stdlib.h>
 #include <malloc.h>
 
+#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_linear_allocator
+struct vg_allocation_meta
 {
-   u32 size, cur;
-
-   /* allows temporarily extendable buffers */
-   void *last_alloc;
-   u32 last_alloc_size;
+   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 */
+
 /* 
- * TODO: Fallback on libc
+ * Stored just behind the array. 32 bytes.
  */
+#pragma pack(push,1)
+struct vg_linear_allocator
+{
+   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)
 
+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 )
 {
-   size += 7;
-   size >>= 3;
-   size <<= 3;
+   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( allocator == NULL )
-      vg_fatal_exit_loop( "Null allocator" );
+   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( 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" );
 
-   if( allocptr->last_alloc == data )
+   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 );
 
-   vg_linear_allocator *allocptr = allocator;
-   allocptr --;
+   if( alloc->last_alloc != data )
+      vg_fatal_exit_loop( "This block has been fixed!" );
 
-   allocptr->last_alloc = NULL;
-   allocptr->last_alloc_size = 0;
+   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 );
+   }
+
+   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 );
+
+   if( alloc->last_alloc != data )
+      vg_fatal_exit_loop( "This block has been fixed!" );
 
-   return vg_linear_resize( allocator, data, allocptr->last_alloc_size+extra );
+   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 );
+
+   /* 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 );
+         }
+      }
+   }
 
-   allocptr->last_alloc = NULL;
-   allocptr->last_alloc_size = 0;
-   allocptr->cur = 0;
+   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( allocptr == NULL )
-            vg_fatal_exit_loop( "Create linear: Malloc failed" );
+      if( alloc->allocation_count + 1 > VG_MAX_ALLOCATIONS )
+         vg_fatal_exit_loop( "Max allocations in linear allocator" );
 
-         allow_once = 0;
+      if( (flags && VG_MEMORY_SYSTEM) && (alloc->flags & VG_MEMORY_REALTIME) )
+         vg_fatal_exit_loop( "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;
       }
       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 */