#include "vg_kv.h"
+void vg_kv_parse( vg_kvs *out_kvs, const char *source, 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
+ {
+ u32 frame_id, latest_child_id;
+ }
+ frame_stack[ 64 ];
+ frame_stack[0].frame_id = 0;
+ frame_stack[0].latest_child_id = 0;
+
+ u32 t0_start=0, t0_length=0, t0_hash=0;
+
+ for( u32 i=0; i<0xffffffff; i ++ )
+ {
+ out_kvs->source_length = i;
+
+ char c = source[i];
+ if( c == '\0' )
+ break;
+
+ u32 link_id = 0, link_depth = 0;
+
+ if( (delim && (c == delim)) || (!delim && (c==' '||c=='\t'||c=='\r'||c=='\n'||c=='{'||c=='}')) )
+ {
+ if( token_length )
+ {
+ token_length --;
+ if( t0_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;
+ }
+ else
+ {
+ t0_start = token_start;
+ t0_length = token_length;
+ t0_hash = token_hash;
+ }
+
+ token_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 )
+ {
+ /* frame with name */
+ kv->key_info = (0xFFFFF & t0_hash) | (0x3FF & t0_length) << 20;
+ kv->key_offset = t0_start;
+ }
+ 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;
+ }
+ else if( c == '}' )
+ {
+ if( depth )
+ depth --;
+ t0_length = 0;
+ }
+ }
+
+ delim = 0;
+ }
+ else
+ {
+ if( token_length )
+ {
+ token_length ++;
+ token_hash = ((token_hash << 5) + token_hash) + (u32)c;
+ }
+ else
+ {
+ if( c=='"'||c=='\'' )
+ {
+ delim = c;
+ token_start = i+1;
+ token_length = 1;
+ token_hash = 5381;
+ }
+ else
+ {
+ token_start = i;
+ token_length = 2;
+ token_hash = ((5381<<5)+5381) + (u32)c;
+ }
+ }
+ }
+
+ 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; }
+
+const char *vg_kv_key( vg_kvs *kvs, u32 kv_id, u32 *out_length )
+{
+ vg_kv *kv = &kvs->kvs[ kv_id ];
+ *out_length = (kv->key_info >> 20) & 0x3FF;
+ return kvs->source_buffer + kv->key_offset;
+}
+
+const char *vg_kv_value( vg_kvs *kvs, u32 kv_id, u32 *out_length )
+{
+ u32 type = vg_kv_type( kvs, kv_id );
+ if( type == 0x0 )
+ return NULL;
+ else
+ {
+ vg_kv *kv = &kvs->kvs[ kv_id ];
+ *out_length = kv->value.length;
+ return kvs->source_buffer + (kv->key_offset + kv->value.offset_from_key);
+ }
+}
+
+u32 vg_kv_child_count( vg_kvs *kvs, u32 kv_id )
+{
+ u32 type = vg_kv_type( kvs, kv_id );
+ if( type == 0x0 )
+ return kvs->kvs[ kv_id ].children;
+ else
+ return 0;
+}
+
+u32 vg_kv_child( vg_kvs *kvs, u32 root_id, u32 index )
+{
+ VG_ASSERT( index == 0 );
+
+ if( vg_kv_child_count( kvs, root_id ) )
+ return root_id +1;
+ else
+ return 0;
+}
+
+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;
+}
+
+u32 vg_kv_find( vg_kvs *kvs, u32 root_id, const char *key )
+{
+ u32 hash = vg_strdjb2( key );
+ u32 child_id = vg_kv_child( kvs, root_id, 0 );
+ while( child_id )
+ {
+ vg_kv *kv = &kvs->kvs[ child_id ];
+ if( ((kv->key_info ^ hash) & 0xFFFFF) == 0 )
+ {
+ u32 key_length;
+ const char *child_key = vg_kv_key( kvs, child_id, &key_length );
+ if( child_key )
+ {
+ for( u32 i=0; i<key_length; i ++ )
+ if( child_key[i] != key[i] )
+ goto next;
+
+ return child_id;
+ }
+ }
+
+ next:child_id = vg_kv_next( kvs, child_id );
+ }
+
+ return 0;
+}
+
+void vg_kv_print_info( vg_kvs *kvs )
+{
+ vg_low( "Compression ratio: %.2f%%\n", ((f32)(kvs->kv_count * sizeof(vg_kv)) / (f32)kvs->source_length )*100.0f );
+}
+
+void vg_kv_print_tree( vg_kvs *kvs, u32 root_id, u32 depth )
+{
+ VG_ASSERT( vg_kv_type( kvs, root_id ) == 0x0 );
+
+ const char *k_whitespace = " ";
+
+ u32 root_len;
+ const char *root_str = vg_kv_key( kvs, root_id, &root_len );
+ vg_info( "%.*s\"%.*s\"\n", depth*2, k_whitespace, root_len, root_str );
+ vg_info( "%.*s{\n", depth*2, k_whitespace );
+ depth ++;
+
+ u32 child_id = vg_kv_child( kvs, root_id, 0 );
+ while( child_id )
+ {
+ if( vg_kv_type( kvs, child_id ) == 0x0 )
+ vg_kv_print_tree( kvs, child_id, depth );
+ else
+ {
+ u32 key_len;
+ const char *key_str = vg_kv_key( kvs, child_id, &key_len );
+
+ u32 value_len;
+ const char *value_str = vg_kv_value( kvs, child_id, &value_len );
+
+ if( key_str && value_str )
+ vg_info( "%.*s\"%.*s\" \"%.*s\"\n", depth*2, k_whitespace, key_len, key_str, value_len, value_str );
+ else
+ vg_error( "%.*s<KV ERROR>\n", depth*2, k_whitespace );
+ }
+
+ child_id = vg_kv_next( kvs, child_id );
+ }
+
+ depth --;
+ vg_info( "%.*s}\n", depth*2, k_whitespace );
+}