The best of both worlds
authorhgn <hgodden00@gmail.com>
Tue, 1 Jul 2025 17:26:35 +0000 (17:26 +0000)
committerhgn <hgodden00@gmail.com>
Tue, 1 Jul 2025 17:26:35 +0000 (17:26 +0000)
vg_kv.c
vg_kv.h
vg_mem.c
vg_mem.h
vg_string.c

diff --git a/vg_kv.c b/vg_kv.c
index dd398e60c7f9d75d33940647575c71dbbf84b0ca..09dc8d0a20bc8c4fa2e7da6c26ac98623a8f2534 100644 (file)
--- a/vg_kv.c
+++ b/vg_kv.c
 #include "vg_kv.h"
 
-void vg_kv_parse( vg_kvs *out_kvs, const char *source, vg_stack_allocator *stack )
+void vg_kvs_init( vg_kvs *kvs, vg_stack_allocator *stack )
 {
-   out_kvs->source_buffer = source;
-   out_kvs->kv_count = 1;
-   out_kvs->kvs = vg_stack_allocate( stack, sizeof(vg_kv), 8, "KV Metadata buffer" );
-   vg_zero_mem( out_kvs->kvs, sizeof(vg_kv) );
-
-   u32 token_start=0,
-       token_length=0,
-       token_hash=0,
-       depth=0;
-   char delim = 0;
-
-   struct
+   vg_zero_mem( kvs, sizeof(vg_kvs) );
+   kvs->stack = stack;
+}
+
+static vg_kv *vg_kvs_newkv( vg_kvs *kvs )
+{
+   void *kv_page;
+   if( (kvs->kv_page_count == VG_KV_PAGE_COUNT) || (kvs->kv_page_offset == 0) )
+   {
+      u32 page_size = sizeof(vg_kv)*VG_KV_PAGE_COUNT;
+      kv_page = vg_stack_allocate( kvs->stack, page_size, 64, "KV Page" );
+      vg_zero_mem( kv_page, page_size );
+      kvs->kv_page_offset = vg_stack_offset( kvs->stack, kv_page );
+      kvs->kv_page_count = 0;
+      kvs->stat_memory_pages += page_size;
+   }
+   else
+      kv_page = vg_stack_pointer( kvs->stack, kvs->kv_page_offset );
+
+   vg_kv *kv = kv_page + kvs->kv_page_count * sizeof(vg_kv);
+   kvs->kv_page_count ++;
+   return kv;
+}
+
+static u32 vg_kv_string_append( vg_kvs *kvs, const char *string )
+{
+   if( string == NULL )
+      return 0;
+   char *buf = vg_stack_allocate( kvs->stack, strlen(string)+1, 1, "KV string (appended)" );
+   strcpy( buf, string );
+   return vg_stack_offset( kvs->stack, buf );
+}
+
+u32 vg_kv_append( vg_kvs *kvs, u32 parent_offset, const char *key, const char *value )
+{
+   vg_kv *kv = vg_kvs_newkv( kvs );
+   u32 key_offset = vg_kv_string_append( kvs, key ),
+       value_offset = vg_kv_string_append( kvs, value );
+
+   kv->key_info = (vg_strdjb2(key) & 0xFFFFF) | (key?(strlen(key)<<20):0) | (value?(0x1<<30):0);
+   kv->key_offset = key_offset;
+
+   if( value )
+   {
+      VG_ASSERT( key );
+      kv->value.offset_from_key = value_offset-key_offset;
+      kv->value.length = strlen(value);
+   }
+
+   u32 kv_offset = vg_stack_offset( kvs->stack, kv );
+   vg_kv *parent = vg_stack_pointer( kvs->stack, parent_offset );
+   if( parent->first_child_offset )
    {
-      u32 frame_id, latest_child_id;
+      u32 brother_offset = parent->first_child_offset;
+      while( 1 )
+      {
+         vg_kv *brother = vg_stack_pointer( kvs->stack, brother_offset );
+         if( brother->brother_offset )
+            brother_offset = brother->brother_offset;
+         else
+         {
+            brother->brother_offset = kv_offset;
+            break;
+         }
+      }
    }
-   frame_stack[ 64 ];
-   frame_stack[0].frame_id = 0;
-   frame_stack[0].latest_child_id = 0;
+   else parent->first_child_offset = kv_offset;
+   return kv_offset;
+}
 
-   u32 t0_start=0, t0_length=0, t0_hash=0;
+void vg_kv_parser_init( vg_kv_parser *parser, vg_kvs *out_kvs )
+{
+   vg_zero_mem( parser, sizeof(vg_kv_parser) );
+   parser->kvs = out_kvs;
 
-   for( u32 i=0; i<0xffffffff; i ++ )
+   vg_kv *root_kv = vg_kvs_newkv( parser->kvs );
+   out_kvs->root_offset = vg_stack_offset( parser->kvs->stack, root_kv );
+   parser->frame_stack[0].frame_offset = out_kvs->root_offset;
+}
+
+void vg_kv_link( vg_kv_parser *parser, u32 offset, u32 depth )
+{
+   u32 parent_offset = parser->frame_stack[ depth ].frame_offset;
+   vg_kv *parent = vg_stack_pointer( parser->kvs->stack, parent_offset );
+   if( parent->first_child_offset == 0 )
+      parent->first_child_offset = offset;
+
+   u32 brother_offset = parser->frame_stack[ depth ].latest_child_offset;
+   if( brother_offset )
    {
-      out_kvs->source_length = i;
+      vg_kv *brother = vg_stack_pointer( parser->kvs->stack, brother_offset );
+      brother->brother_offset = offset;
+   }
+   parser->frame_stack[ depth ].latest_child_offset = offset;
+}
+
+void vg_kv_parse_buffer( vg_kv_parser *parser, const char *buffer, u32 buffer_length )
+{
+   if( buffer_length == 0 )
+      buffer_length = 0xffffffff;
 
-      char c = source[i];
+   for( u32 i=0; i<buffer_length; i ++ )
+   {
+      parser->stat_source_characters ++;
+      char c = buffer[i];
       if( c == '\0' )
          break;
 
-      u32 link_id = 0, link_depth = 0;
+      bool is_control_character = 0;
+      if( parser->token0_deliminator )
+      {
+         if( c == parser->token0_deliminator )
+            is_control_character = 1;
+      }
+      else
+      {
+         if( c==' '||c=='\t'||c=='\r'||c=='\n'||c=='{'||c=='}' )
+            is_control_character = 1;
+      }
 
-      if( (delim && (c == delim)) || (!delim && (c==' '||c=='\t'||c=='\r'||c=='\n'||c=='{'||c=='}')) )
+      if( is_control_character )
       {
-         if( token_length )
+         if( parser->token0_length )
          {
-            token_length --;
-            if( t0_length )
+            parser->token0_length --;
+            parser->token0_buffer = vg_stack_extend_last( parser->kvs->stack, 1 );
+            parser->token0_buffer[ parser->token0_length ] = '\0';
+
+            if( parser->token1_length )
             {
-               /* pair */
-               link_id = out_kvs->kv_count ++;
-               link_depth = depth;
-
-               out_kvs->kvs = vg_stack_extend_last( stack, sizeof(vg_kv) );
-               vg_kv *kv = &out_kvs->kvs[ link_id ];
-               kv->key_info = (0xFFFFF & t0_hash) | (0x3FF & t0_length)<<20 | (0x1) << 30;
-               kv->key_offset = t0_start;
-               kv->brother_offset = 0; /* deffered */
-               kv->value.offset_from_key = token_start - t0_start;
-               kv->value.length = token_length;
-
-               t0_length = 0;
+               /* KV pair */
+               vg_kv *kv = vg_kvs_newkv( parser->kvs );
+               kv->key_info = (0xFFFFF & parser->token1_hash) | 
+                              (0x3FF & parser->token1_length)<<20 | 
+                              (0x1) << 30;
+               kv->key_offset = parser->token1_start_offset;
+               kv->value.offset_from_key = parser->token0_start_offset - parser->token1_start_offset;
+               kv->value.length = parser->token0_length;
+               parser->token1_length = 0;
+
+               vg_kv_link( parser, vg_stack_offset( parser->kvs->stack, kv ), parser->depth );
             }
             else
             {
-               t0_start = token_start;
-               t0_length = token_length;
-               t0_hash = token_hash;
+               /* shift */
+               parser->token1_start_offset = parser->token0_start_offset;
+               parser->token1_length = parser->token0_length;
+               parser->token1_hash = parser->token0_hash;
             }
 
-            token_length = 0;
+            parser->token0_length = 0;
          }
 
          if( c=='{'||c=='}'||c=='\n' )
          {
-            /* SYNTAX TOKEN */
             if( c == '{' )
             {
-               link_id = out_kvs->kv_count ++;
-               link_depth = depth;
-
-               out_kvs->kvs = vg_stack_extend_last( stack, sizeof(vg_kv) );
-               vg_kv *kv = &out_kvs->kvs[ link_id ];
-               kv->brother_offset = 0; /* deffered */
-               kv->children = 0;
-               if( t0_length )
+               vg_kv *kv = vg_kvs_newkv( parser->kvs );
+               if( parser->token1_length )
                {
-                  /* frame with name */
-                  kv->key_info = (0xFFFFF & t0_hash) | (0x3FF & t0_length) << 20;
-                  kv->key_offset = t0_start;
+                  kv->key_info = (0xFFFFF & parser->token1_hash) | (0x3FF & parser->token1_length) << 20;
+                  kv->key_offset = parser->token1_start_offset;
                }
                else
-               {
-                  /* frame with no name */
                   kv->key_info = 5381;
-                  kv->key_offset = 0;
-               }
 
-               t0_length = 0;
-               depth ++;
-               frame_stack[ depth ].latest_child_id = 0;
-               frame_stack[ depth ].frame_id = link_id;
+               u32 id = vg_stack_offset( parser->kvs->stack, kv ),
+                   depth = parser->depth;
+
+               parser->depth ++;
+               parser->frame_stack[ parser->depth ].latest_child_offset = 0;
+               parser->frame_stack[ parser->depth ].frame_offset = id;
+               vg_kv_link( parser, id, depth );
+               parser->token1_length = 0;
             }
             else if( c == '}' )
             {
-               if( depth )
-                  depth --;
-               t0_length = 0;
+               if( parser->depth )
+                  parser->depth --;
+               parser->token1_length = 0;
             }
          }
 
-         delim = 0;
+         parser->token0_deliminator = 0;
       }
       else
       {
-         if( token_length )
+         if( parser->token0_length )
          {
-            token_length ++;
-            token_hash = ((token_hash << 5) + token_hash) + (u32)c;
+            parser->token0_buffer = vg_stack_extend_last( parser->kvs->stack, 1 );
+            parser->token0_buffer[ parser->token0_length-1 ] = c;
+            parser->token0_length ++;
+            parser->token0_hash = ((parser->token0_hash << 5) + parser->token0_hash) + (u32)c;
+            parser->stat_memory_strings ++;
          }
          else
          {
-            if( c=='"'||c=='\'' )
+            if( c =='"' || c=='\'' )
             {
-               delim = c;
-               token_start = i+1;
-               token_length = 1;
-               token_hash = 5381;
+               parser->token0_buffer = vg_stack_allocate( parser->kvs->stack, 0, 1, "KV string" );
+               parser->token0_start_offset = vg_stack_offset( parser->kvs->stack, parser->token0_buffer );
+               parser->token0_deliminator = c;
+               parser->token0_hash = 5381;
+               parser->token0_length = 1;
             }
             else
             {
-               token_start = i;
-               token_length = 2;
-               token_hash = ((5381<<5)+5381) + (u32)c;
+               parser->token0_buffer = vg_stack_allocate( parser->kvs->stack, 1, 1, "KV string" );
+               parser->token0_start_offset = vg_stack_offset( parser->kvs->stack, parser->token0_buffer );
+               parser->token0_buffer[0] = c;
+               parser->token0_length = 2;
+               parser->token0_hash = ((5381<<5)+5381) + (u32)c;
+               parser->stat_memory_strings ++;
             }
          }
       }
-
-      if( link_id )
-      {
-         u32 parent_id = frame_stack[link_depth].frame_id;
-         vg_kv *parent = &out_kvs->kvs[ parent_id ];
-         parent->children ++;
-
-         u32 brother_id = frame_stack[link_depth].latest_child_id;
-         if( brother_id )
-         {
-            vg_kv *brother = &out_kvs->kvs[ brother_id ];
-            VG_ASSERT( brother->brother_offset == 0 );
-            brother->brother_offset = link_id - brother_id;
-         }
-         frame_stack[ link_depth ].latest_child_id = link_id;
-      }
    }
-}
+} 
 
-u32 vg_kv_type( vg_kvs *kvs, u32 kv_id ){ return (kvs->kvs[ kv_id ].key_info >> 30) & 0x3; }
+u32 vg_kv_type( vg_kvs *kvs, u32 kv_offset )
+{
+   vg_kv *kv = vg_stack_pointer( kvs->stack, kv_offset );
+   return (kv->key_info >> 30) & 0x3;
+}
 
-const char *vg_kv_key( vg_kvs *kvs, u32 kv_id, u32 *out_length )
+const char *vg_kv_key( vg_kvs *kvs, u32 kv_offset, u32 *out_length )
 {
-   vg_kv *kv = &kvs->kvs[ kv_id ];
+   vg_kv *kv = vg_stack_pointer( kvs->stack, kv_offset );
    *out_length = (kv->key_info >> 20) & 0x3FF;
-   return (*out_length)? kvs->source_buffer + kv->key_offset: NULL;
+   return (*out_length)? vg_stack_pointer( kvs->stack, kv->key_offset ): NULL;
 }
 
-const char *vg_kv_value( vg_kvs *kvs, u32 kv_id, u32 *out_length )
+const char *vg_kv_value( vg_kvs *kvs, u32 kv_offset, u32 *out_length )
 { 
-   u32 type = vg_kv_type( kvs, kv_id );
-   if( type == 0x0 )
+   if( vg_kv_type( kvs, kv_offset ) == 0x0 )
       return NULL;
    else
    {
-      vg_kv *kv = &kvs->kvs[ kv_id ];
+      vg_kv *kv = vg_stack_pointer( kvs->stack, kv_offset );
       *out_length = kv->value.length;
-      return kvs->source_buffer + (kv->key_offset + kv->value.offset_from_key);
+      return vg_stack_pointer( kvs->stack, kv->key_offset + kv->value.offset_from_key );
    }
 }
 
-u32 vg_kv_child_count( vg_kvs *kvs, u32 kv_id )
+u32 vg_kv_next( vg_kvs *kvs, u32 kv_offset )
 {
-   u32 type = vg_kv_type( kvs, kv_id );
-   if( type == 0x0 )
-      return kvs->kvs[ kv_id ].children;
-   else
-      return 0;
+   vg_kv *kv = vg_stack_pointer( kvs->stack, kv_offset );
+   return kv->brother_offset;
 }
 
-u32 vg_kv_child( vg_kvs *kvs, u32 root_id, u32 index )
+u32 vg_kv_child( vg_kvs *kvs, u32 root_offset, u32 index )
 {
-   VG_ASSERT( index == 0 );
-
-   if( vg_kv_child_count( kvs, root_id ) )
-      return root_id +1;
-   else
-      return 0;
-}
+   if( vg_kv_type( kvs, root_offset ) == 0x0 )
+   {
+      vg_kv *parent = vg_stack_pointer( kvs->stack, root_offset );
+      u32 offset = parent->first_child_offset;
 
-u32 vg_kv_next( vg_kvs *kvs, u32 kv_id )
-{
-   u32 offset = kvs->kvs[ kv_id ].brother_offset;
-   if( offset )
-      return kv_id + offset;
-   else 
-      return 0;
+      for( u32 i=0; (i<index) && offset; i ++ )
+      {
+         vg_kv *kv = vg_stack_pointer( kvs->stack, offset );
+         offset = kv->brother_offset;
+      }
+      return offset;
+   }
+   else return 0;
 }
 
-u32 vg_kv_find( vg_kvs *kvs, u32 root_id, const char *key )
+u32 vg_kv_find( vg_kvs *kvs, u32 root_offset, const char *key )
 {
    u32 hash = vg_strdjb2( key );
-   u32 child_id = vg_kv_child( kvs, root_id, 0 );
-   while( child_id )
+   u32 child_offset = vg_kv_child( kvs, root_offset, 0 );
+   while( child_offset )
    {
-      vg_kv *kv = &kvs->kvs[ child_id ];
+      vg_kv *kv = vg_stack_pointer( kvs->stack, child_offset );
+      u32 key_length;
+      const char *child_key = vg_kv_key( kvs, child_offset, &key_length );
       if( ((kv->key_info ^ hash) & 0xFFFFF) == 0 )
       {
          u32 key_length;
-         const char *child_key = vg_kv_key( kvs, child_id, &key_length );
+         const char *child_key = vg_kv_key( kvs, child_offset, &key_length );
          if( child_key )
          {
             for( u32 i=0; i<key_length; i ++ )
                if( child_key[i] != key[i] )
                   goto next;
 
-            return child_id;
+            return child_offset;
          }
       }
 
- next:child_id = vg_kv_next( kvs, child_id );
+ next:child_offset = vg_kv_next( kvs, child_offset );
    }
-
    return 0;
 }
 
-typedef struct vg_kv_write vg_kv_write;
-struct vg_kv_write
-{
-   FILE *fp;
-   u32 depth;
-};
-
-void vg_kv_write_indent( vg_kv_write *w )
+static void vg_kv_write_indent( vg_kv_write *w )
 {
    for( u32 i=0; i<w->depth; i ++ )
       fputc( ' ', w->fp );
 }
 
-void vg_kv_write_string( vg_kv_write *w, const char *string, u32 length )
+static void vg_kv_write_string( vg_kv_write *w, const char *string, u32 length )
 {
    if( length == 0 )
       length = 0xffffffff;
@@ -304,43 +370,45 @@ void vg_kv_write_kv( vg_kv_write *w, const char *key, u32 key_len, const char *v
    fputc( '\n', w->fp );
 }
 
-#if 0
-void vg_kv_print_info( vg_kvs *kvs )
+void vg_kv_parser_print_info( vg_kv_parser *parser )
 {
-   vg_low( "Compression ratio: %.2f%%\n", ((f32)(kvs->kv_count * sizeof(vg_kv)) / (f32)kvs->source_length )*100.0f );
+   vg_low( "KV stats\n"
+           "  bytes pages: %u\n"
+           "  bytes strings: %u\n"
+           "  source characters: %u. compression ratio: %.2f%%\n", parser->kvs->stat_memory_pages,
+           parser->stat_memory_strings,
+           parser->stat_source_characters, 
+           (f32)(parser->stat_memory_strings+parser->kvs->stat_memory_pages) /
+                                             (f32)parser->stat_source_characters * 100.0f );
 }
 
-void vg_kv_print_tree( vg_kv_write *w, vg_kvs *kvs, u32 root_id )
+void vg_kv_print_tree( vg_kv_write *w, vg_kvs *kvs, u32 root_offset )
 {
-   VG_ASSERT( vg_kv_type( kvs, root_id ) == 0x0 );
+   VG_ASSERT( vg_kv_type( kvs, root_offset ) == 0x0 );
 
    u32 root_len;
-   const char *root_str = vg_kv_key( kvs, root_id, &root_len );
+   const char *root_str = vg_kv_key( kvs, root_offset, &root_len );
    vg_kv_write_block( w, root_str, root_len );
 
-   u32 child_id = vg_kv_child( kvs, root_id, 0 );
-   while( child_id )
+   u32 child_offset = vg_kv_child( kvs, root_offset, 0 );
+   while( child_offset )
    {
-      if( vg_kv_type( kvs, child_id ) == 0x0 )
-         vg_kv_print_tree( w, kvs, child_id );
+      if( vg_kv_type( kvs, child_offset ) == 0x0 )
+         vg_kv_print_tree( w, kvs, child_offset );
       else
       {
          u32 key_len;
-         const char *key_str = vg_kv_key( kvs, child_id, &key_len );
+         const char *key_str = vg_kv_key( kvs, child_offset, &key_len );
 
          u32 value_len;
-         const char *value_str = vg_kv_value( kvs, child_id, &value_len );
+         const char *value_str = vg_kv_value( kvs, child_offset, &value_len );
 
          if( key_str && value_str )
             vg_kv_write_kv( w, key_str, key_len, value_str, value_len );
       }
 
-      child_id = vg_kv_next( kvs, child_id );
+      child_offset = vg_kv_next( kvs, child_offset );
    }
 
    vg_kv_end_block( w );
-#if 0
-   vg_info( "%.*s}\n", depth*2, k_whitespace );
-#endif
 }
-#endif
diff --git a/vg_kv.h b/vg_kv.h
index 0e0214d1bd829c33a39a83fdafb4df2b19c79aef..41c97515652c22a8bb63fbe90f036b6e33dec7ca 100644 (file)
--- a/vg_kv.h
+++ b/vg_kv.h
@@ -1,22 +1,93 @@
 #pragma once
 #include "vg_platform.h"
+#include "vg_mem.h"
+
+#define VG_KV_PAGE_COUNT 32
+
+/* define VG_KV_SUPPORT_LEGACY_MSG to automatically convert from old vg_msg stream to plain text KVs */
 
-typedef struct vg_kvs vg_kvs;
 typedef struct vg_kv vg_kv;
-struct vg_kvs
+typedef struct vg_kvs vg_kvs;
+typedef struct vg_kv_parser vg_kv_parser;
+typedef struct vg_kv_write vg_kv_write;
+
+/* Initialize kvs set and set the allocator for it */
+void vg_kvs_init( vg_kvs *kvs, vg_stack_allocator *stack );
+
+/* Initialize KV parser ready to accept buffer fragments
+ *  out_kvs must be initialized
+ * 
+ * vg_kv_parse_buffer takes a text buffer, if its null terminated, buffer_length can be 0
+ */
+void vg_kv_parser_init( vg_kv_parser *parser, vg_kvs *out_kvs );
+void vg_kv_parse_buffer( vg_kv_parser *parser, const char *buffer, u32 buffer_length );
+void vg_kv_link( vg_kv_parser *parser, u32 offset, u32 depth );
+
+/* returns the type of this KV.
+ *  0: frame
+ *  1: kv pair
+ */
+u32 vg_kv_type( vg_kvs *kvs, u32 kv_offset );
+
+/* get key / values associated with KV pair or only key for a frame. */
+const char *vg_kv_key( vg_kvs *kvs, u32 kv_offset, u32 *out_length );
+const char *vg_kv_value( vg_kvs *kvs, u32 kv_offset, u32 *out_length );
+
+/* get the child KV at index, returns 0 if out of range */
+u32 vg_kv_child( vg_kvs *kvs, u32 root_offset, u32 index );
+u32 vg_kv_next( vg_kvs *kvs, u32 kv_offset );
+u32 vg_kv_find( vg_kvs *kvs, u32 root_offset, const char *key );
+
+/* editing kvs
+ *  if value is NULL, it appends a named frame
+ *  if key is NULL, value must also be NULL. it appends a nameless frame
+ *  if both key and value are set, it appends a KV pair
+ *  returns the new KV offset
+ */
+u32 vg_kv_append( vg_kvs *kvs, u32 parent_offset, const char *key, const char *value )
+
+/* Writing KV files. w should be initialized with depth 0, and fp to a valid C stream pointer */
+void vg_kv_write_block( vg_kv_write *w, const char *name, u32 name_length );
+void vg_kv_end_block( vg_kv_write *w );
+void vg_kv_write_kv( vg_kv_write *w, const char *key, u32 key_len, const char *value, u32 value_len );
+
+/* Just for analysis */
+void vg_kv_parser_print_info( vg_kv_parser *parser );
+void vg_kv_print_tree( vg_kv_write *w, vg_kvs *kvs, u32 root_offset );
+
+struct vg_kv_parser
 {
-   const char *source_buffer;
-   u32 source_length;
+   vg_kvs *kvs;
+   u32 token0_start_offset, token0_length, token0_hash,
+       token1_start_offset, token1_length, token1_hash;
+
+   u32 stat_memory_strings,
+       stat_source_characters;
+
+   char token0_deliminator;
+   char *token0_buffer;
+   u32 depth;
+
+   struct
+   {
+      u32 frame_offset, latest_child_offset;
+   }
+   frame_stack[64];
+};
 
-   vg_kv *kvs;
-   u32 kv_count;
+struct vg_kvs
+{
+   vg_stack_allocator *stack;
+   u32 root_offset;
+   u32 kv_page_offset, kv_page_count;
+   u32 stat_memory_pages;
 };
 
 struct vg_kv
 {
    u32 key_info;      /* 20 bit hash, 10 bit key length, 2 bit type */
-   u32 key_offset;    /* 32 bit, indexes into the raw text buffer  */
-   u32 brother_offset;/* 24 bits, relative jump past all descendents to the next key. If 0, no brothers */
+   u32 key_offset;    /* 32 bit, indexes kvs->stack.data. same for any other _offset field */
+   u32 brother_offset;
 
    union
    {
@@ -26,6 +97,12 @@ struct vg_kv
       }
       value;
 
-      u32 children; /* 24 bits */
+      u32 first_child_offset;
    };
 };
+
+struct vg_kv_write
+{
+   FILE *fp;
+   u32 depth;
+};
index fa75fd043a90066c134aa86c4900486293322391..92b9ef50c8eb9d4e8b0965cf65008758be60b0c6 100644 (file)
--- a/vg_mem.c
+++ b/vg_mem.c
@@ -98,13 +98,18 @@ static void vg_stack_growmaybe( vg_stack_allocator *stack, u32 new_usage )
 
 void *vg_stack_allocate( vg_stack_allocator *stack, u32 size, u32 alignment, const char *debug_name )
 {
-   VG_ASSERT( (alignment >= 1) && (alignment <= 8) );
+   VG_ASSERT( (alignment >= 1) && (alignment <= 64) );
    VG_ASSERT( ~stack->used & 0x10000000 );
    VG_ASSERT( ~size        & 0x10000000 );
 
    u32 block_size = size;
    if( stack->flags & VG_STACK_ALLOCATOR_METADATA )
    {
+      if( alignment > 8 )
+      {
+         vg_fatal_error( "Allocation wants %u bytes alignment, but allocator has METADATA flag set."
+                         "  Alignment with this flag is always 8!\n", alignment );
+      }
       block_size += sizeof( vg_allocation_meta );
       alignment = 8;
    }
@@ -161,6 +166,16 @@ void vg_stack_set_flags( vg_stack_allocator *stack, u16 append_flags )
    stack->flags |= append_flags;
 }
 
+u32 vg_stack_offset( vg_stack_allocator *stack, void *pointer )
+{
+   return pointer - stack->data;
+}
+
+void *vg_stack_pointer( vg_stack_allocator *stack, u32 offset )
+{
+   return stack->data + offset;
+}
+
 void vg_stack_set_flags_last( vg_stack_allocator *stack, u16 append_flags )
 {
    if( !(stack->flags & VG_STACK_ALLOCATOR_METADATA) )
@@ -222,3 +237,40 @@ static void vg_mem_print_size( u32 bytes, char buf[32] )
    else
       snprintf( buf, 32, "%ub", bytes );
 }
+
+void vg_mem_dumphex( FILE *fp, void *buffer, u32 offset, u32 bytes )
+{
+   fprintf( fp, "buffer at %p, offset +%u, length %u\n", buffer, offset, bytes );
+   fprintf( fp, "----------------------------------------------------------------------------\n" );
+   
+   u32 x = 0;
+   for( u32 i=0; i<bytes; i ++ )
+   {
+      if( x == 0 )
+         fprintf( fp, "+0x%06x | ", i );
+
+      u8 byte = ((u8 *)buffer)[ offset + i ];
+      fprintf( fp, "%02x ", byte );
+
+      x++;
+      if( x == 16 || (i+1==bytes) )
+      {
+         for( u32 j=0; j<16-x; j ++ )
+            fputs( "   ", fp );
+         
+         for( u32 j=0; j<x; j ++ )
+         {
+            u8 charbyte = ((u8 *)buffer)[ offset + i - (x-1) + j ];
+            if( charbyte >= 33 && charbyte <= 126 )
+               fputc( charbyte, fp );
+            else
+               fputc( charbyte == 0? '.': ' ', fp );
+         }
+
+         fputc( '\n', fp );
+         x = 0;
+      }
+   }
+
+   fprintf( fp, "----------------------------------------------------------------------------\n" );
+}
index 34dbe5961927c941a48fcbb8fab75174be45fc68..f955a486d517b558ddd61865365ad21159631521 100644 (file)
--- a/vg_mem.h
+++ b/vg_mem.h
@@ -49,6 +49,8 @@ struct vg_stack_allocator
    void *data;
 };
 
+void vg_mem_dumphex( FILE *fp, void *buffer, u32 offset, u32 bytes );
+
 /* NOT USED IN THIS FILE */
 u32 vg_align16( u32 s );
 u32 vg_align8( u32 s );
@@ -72,6 +74,8 @@ void *vg_stack_resize_last( vg_stack_allocator *stack, u32 new_size );
 
 __attribute__((warn_unused_result))
 void *vg_stack_extend_last( vg_stack_allocator *stack, u32 extra_bytes );
+u32 vg_stack_offset( vg_stack_allocator *stack, void *pointer );
+void *vg_stack_pointer( vg_stack_allocator *stack, u32 offset );
 
 /*
  * The following are availible if allocator has the flag METADATA set
index 8fec7c499b7da7854fd536062c3c883a75812e63..ba6d21310bac0dc7b1a0208b7794d9d9d6a3bd66 100644 (file)
@@ -239,8 +239,9 @@ u32 vg_strdjb2( const char *str )
 {
    u32 hash = 5381, c;
 
-   while( (c = *str++) )
-      hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
+   if( str )
+      while( (c = *str++) )
+         hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
 
    return hash;
 }