better build program
[vg.git] / vg_msg.h
index 83bc0863f6f40942a44731c0c47e445a3079f6a2..b5fb4a482bf991ef4624b65ab93b89be33da51a6 100644 (file)
--- a/vg_msg.h
+++ b/vg_msg.h
@@ -24,8 +24,9 @@
  *   u8 data_buf[512];
  *   vg_msg data;
  *   vg_msg_init( &data, data_buf, 512 );
+ *
  *   vg_msg_wkvstr( &data, "kvstr", "someinfo" );
- *   vg_msg_wkvint( &data, "kvint", i32 value=200 );
+ *   vg_msg_wkvu32( &data, "kvint", 200 );
  *
  *   vg_msg_frame( &data, "person" );
  *      vg_msg_wkvstr( &data, "name", "jeff" );
@@ -33,7 +34,7 @@
  *   vg_msg_end_frame( &data );
  *
  *   vg_msg_frame( &data, "building" );
- *      vg_msg_wkvint( &data, "capacity", i32 value=1000 );
+ *      vg_msg_wkvu32( &data, "capacity", 1000 );
  *   vg_msg_end_frame( &data );
  *
  *   vg_msg_frame( &data, "person" );
  *
  *   u8 data_buf[512];
  *   u32 data_len;
+ *
  *   // read data_buf and data_len
+ *
  *   vg_msg data;
  *   vg_msg_init( &data, data_buf, data_len );
  *
+ *
+ * Reading data
+ * -----------------------------------------------------------------------------
+ *
+ * if( vg_msg_seekframe( &msg, "rows" ) ){
+ *    while( vg_msg_seekframe( &msg, NULL ) ){
+ *       vg_warn( "%s\n", vg_msg_readkvstr( &msg, "yedo" ) );
+ *       vg_msg_skip_frame( &msg );
+ *    }
+ * }
+ *
  * Reading back the stream linearly
  * -----------------------------------------------------------------------------
  * 
  *   vg_msg_cmd cmd;
  *   while( vg_msg_next( &data, &cmd ) ){
- *           if( cmd.code == k_vg_msg_code_frame ) printf( "{" );
- *      else if( cmd.code == k_vg_msg_code_endframe ) printf( "}" );
- *      esle if( cmd.code == k_vg_msg_code_kvstring )
+ *           if( cmd.code == k_vg_msg_frame ) printf( "{" );
+ *      else if( cmd.code == k_vg_msg_endframe ) printf( "}" );
+ *      esle if( cmd.code == k_vg_msg_kvstring )
  *         printf( "string: %s\n", cmd.value._buf );
  *   }
- *
- * Reading back the stream as frames/nodes. this is obviously slower
- * -----------------------------------------------------------------------------
- *
- * vg_msg person = data
- * while( vg_msg_seekframe( &person, "person", VG_MSG_NEXT ) ){
- *    const char *name = vg_msg_seekkvstr(&person, "name", VG_MSG_NEXT);
- *    const char *country = vg_msg_seekkvstr(&person, "country", VG_MSG_FIRST);
- *
- *    printf( "Guy '%s' is from '%s'\n", name, country );
- *    vg_msg_skip_frame(&person);
- * }
- *
- * vg_msg building = root;
- * if( vg_msg_seekframe( &building, "building", VG_MSG_FIRST ) ){
- *    vg_msg_cmd capacity = vg_msg_seekkv(&building, "capacity", VG_MSG_FIRST);
- *    if( capacity.code & k_vg_msg_code_signed )
- *       print( "building capacity: %d\n", capacity.value._i32 );
- *
- *    vg_msg_skip_frame( &building );
- * }
- *
  */
 
 enum vg_msg_code{
-   k_vg_msg_code_end      = 0,
-   k_vg_msg_code_frame    = 1,
-   k_vg_msg_code_endframe = 2,
-   k_vg_msg_code_kv       = 10,
-   k_vg_msg_code_kvstring = 11,
-   k_vg_msg_code_kvbin    = 12,
-   k_vg_msg_code_signed   = 0x80,   /* byte sizes stored in lower 4 bits */
-   k_vg_msg_code_unsigned = 0x40,
-   k_vg_msg_code_float    = 0x20,
-   k_vg_msg_code_integer  = k_vg_msg_code_signed|k_vg_msg_code_unsigned
+   /* low types */
+   k_vg_msg_end      = 0,
+   k_vg_msg_frame    = 1,
+   k_vg_msg_endframe = 2,
+   k_vg_msg_kv       = 10,
+   k_vg_msg_kvstring = 11,
+   k_vg_msg_kvbin    = 12,
+
+   /* variable sized types */
+   k_vg_msg_float    = 0x40,
+   k_vg_msg_unsigned = 0x80,
+   k_vg_msg_signed   = 0xC0,
+
+   /* masks */
+   k_vg_msg_array_count_bits = 0x3C,
+   k_vg_msg_type_size_bits   = 0x03,
+   k_vg_msg_type_base_bits   = 0xC0,
+   k_vg_msg_type_bits        = k_vg_msg_type_base_bits|k_vg_msg_type_size_bits,
+
+   /* sizes */
+   k_vg_msg_8b  = 0x00,
+   k_vg_msg_16b = 0x01,
+   k_vg_msg_32b = 0x02,
+   k_vg_msg_64b = 0x03,
+
+   /* common types */
+   k_vg_msg_u8  = k_vg_msg_unsigned|k_vg_msg_8b,
+   k_vg_msg_u16 = k_vg_msg_unsigned|k_vg_msg_16b,
+   k_vg_msg_u32 = k_vg_msg_unsigned|k_vg_msg_32b,
+   k_vg_msg_u64 = k_vg_msg_unsigned|k_vg_msg_64b,
+   k_vg_msg_i8  = k_vg_msg_signed  |k_vg_msg_8b,
+   k_vg_msg_i16 = k_vg_msg_signed  |k_vg_msg_16b,
+   k_vg_msg_i32 = k_vg_msg_signed  |k_vg_msg_32b,
+   k_vg_msg_i64 = k_vg_msg_signed  |k_vg_msg_64b,
+   k_vg_msg_f32 = k_vg_msg_float   |k_vg_msg_32b,
+   k_vg_msg_f64 = k_vg_msg_float   |k_vg_msg_64b,
+
+   k_vg_msg_v2f = k_vg_msg_float   |k_vg_msg_32b | (1<<2),
+   k_vg_msg_v3f = k_vg_msg_float   |k_vg_msg_32b | (2<<2),
+   k_vg_msg_v4f = k_vg_msg_float   |k_vg_msg_32b | (3<<2)
 };
 
 typedef struct vg_msg vg_msg;
 typedef struct vg_msg_cmd vg_msg_cmd;
+typedef struct vg_msg_cursor vg_msg_cursor;
 struct vg_msg{
-   u32 cur,max;
+   u32 max;
    u8 *buf;
-   u32 depth;
 
    /* reading */
-   u32 rframe_depth, rframe_cur;
+   struct vg_msg_cursor {
+      u32 co, depth;
+   }
+   cur;
    
    enum vg_msg_error{
       k_vg_msg_error_OK,
@@ -129,36 +154,35 @@ struct vg_msg_cmd{
    const char *key;
    u32 key_djb2;
 
-   union{ const void *_buf;
-          u8  _u8;  i8  _i8;
-          u16 _u16; i16 _i16;
-          u32 _u32; i32 _i32; f32 _f32; 
-          u64 _u64; i64 _i64; f64 _f64;
-         } value;
+   const void *value;
    u32 value_djb2;
+
+   u32 len; /* set if binary type */
 };
 
-/* generic stream reset */
-static void vg_msg_init( vg_msg *msg, u8 *buf, u32 max ){
-   msg->cur = 0;
-   msg->max = max;
-   msg->buf = buf;
-   msg->depth = 0;
-   msg->error = k_vg_msg_error_OK;
-   msg->rframe_depth = 0;
-   msg->rframe_cur = 0;
+/* write a buffer from msg, rang checked. */
+static void vg_msg_wbuf( vg_msg *msg, u8 *buf, u32 len ){
+   if( msg->error != k_vg_msg_error_OK ) return;
+   if( msg->cur.co+len > msg->max ){
+      msg->error = k_vg_msg_error_overflow;
+      return;
+   }
+
+   for( u32 i=0; i<len; i++ ){
+      msg->buf[ msg->cur.co ++ ] = buf[i];
+   }
 }
 
-/* write or read a buffer from msg, rang checked. */
-static void vg_msg_exchbuf( vg_msg *msg, int write, u8 *buf, u32 len ){
+/* read a buffer from msg, rang checked. */
+static void vg_msg_rbuf( vg_msg *msg, u8 *buf, u32 len ){
    if( msg->error != k_vg_msg_error_OK ) return;
-   if( msg->cur+len > msg->max ){
+   if( msg->cur.co+len > msg->max ){
       msg->error = k_vg_msg_error_overflow;
       return;
    }
+
    for( u32 i=0; i<len; i++ ){
-      if( write ) msg->buf[ msg->cur ++ ] = buf[i];
-      else        buf[i] = msg->buf[ msg->cur ++ ];
+      buf[i] = msg->buf[ msg->cur.co ++ ];
    }
 }
 
@@ -166,7 +190,7 @@ static void vg_msg_exchbuf( vg_msg *msg, int write, u8 *buf, u32 len ){
 static void vg_msg_wstr( vg_msg *msg, const char *str ){
    if( msg->error != k_vg_msg_error_OK ) return;
    for( u32 i=0;; i++ ){
-      vg_msg_exchbuf( msg, 1, (u8[]){ str[i] }, 1 );
+      vg_msg_wbuf( msg, (u8[]){ str[i] }, 1 );
       if( !str[i] ) break;
    }
 }
@@ -176,10 +200,10 @@ static const char *vg_msg_rstr( vg_msg *msg, u32 *djb2 ){
    if( msg->error != k_vg_msg_error_OK ) return 0;
 
    u32 hash = 5381, c;
-   const char *str = (void *)(&msg->buf[ msg->cur ]);
+   const char *str = (void *)(&msg->buf[ msg->cur.co ]);
 
-   while( (c = msg->buf[ msg->cur ++ ]) ){
-      if( msg->cur >= msg->max ){
+   while( (c = msg->buf[ msg->cur.co ++ ]) ){
+      if( msg->cur.co >= msg->max ){
          msg->error = k_vg_msg_error_overflow;
          return 0;
       }
@@ -194,67 +218,96 @@ static const char *vg_msg_rstr( vg_msg *msg, u32 *djb2 ){
 static void vg_msg_frame( vg_msg *msg, const char *name ){
    if( msg->error != k_vg_msg_error_OK ) return;
 
-   msg->depth ++;
-   vg_msg_exchbuf( msg, 1, (u8[]){ k_vg_msg_code_frame }, 1 );
+   msg->cur.depth ++;
+   vg_msg_wbuf( msg, (u8[]){ k_vg_msg_frame }, 1 );
    vg_msg_wstr( msg, name );
 }
 
 /* end frame in message stream */
 static void vg_msg_end_frame( vg_msg *msg ){
    if( msg->error != k_vg_msg_error_OK ) return;
-   if( !msg->depth ){
+   if( !msg->cur.depth ){
       msg->error = k_vg_msg_error_unbalanced;
       return;
    }
-   msg->depth --;
-   vg_msg_exchbuf( msg, 1, (u8[]){ k_vg_msg_code_endframe }, 1 );
+   msg->cur.depth --;
+   vg_msg_wbuf( msg, (u8[]){ k_vg_msg_endframe }, 1 );
 }
 
 /* write a KV string to stream */
 static void vg_msg_wkvstr( vg_msg *msg, const char *key, const char *value ){
-   vg_msg_exchbuf( msg, 1, (u8[]){ k_vg_msg_code_kvstring }, 1 );
+   vg_msg_wbuf( msg, (u8[]){ k_vg_msg_kvstring }, 1 );
    vg_msg_wstr( msg, key );
    vg_msg_wstr( msg, value );
 }
 
 /* write a binary block to stream */
 static void vg_msg_wkvbin( vg_msg *msg, const char *key, u8 *bin, u32 len ){
-   vg_msg_exchbuf( msg, 1, (u8[]){ k_vg_msg_code_kvbin }, 1 );
+   vg_msg_wbuf( msg, (u8[]){ k_vg_msg_kvbin }, 1 );
    vg_msg_wstr( msg, key );
-   vg_msg_exchbuf( msg, 1, (u8 *)(&len), 4 );
-   vg_msg_exchbuf( msg, 1, bin, len );
+   vg_msg_wbuf( msg, (u8 *)(&len), 4 );
+   vg_msg_wbuf( msg, bin, len );
+}
+
+static u32 vg_msg_cmd_array_count( u8 code ){
+   return ((code & k_vg_msg_array_count_bits)>>2) + 1;
+}
+
+static u32 vg_msg_cmd_type_size( u8 code ){
+   return 0x1 << (code & k_vg_msg_type_size_bits);
 }
 
-/* write a generic sized kv */
-static void vg__msg_wkvgen( vg_msg *msg, const char *key, 
-                            u8 basecode, void *value, u32 size ){
-   u8 code = basecode | size;
-   vg_msg_exchbuf( msg, 1, &code, 1 );
+/* get the byte count of a sized code */
+static u32 vg_msg_cmd_bytecount( u8 code ){
+   return vg_msg_cmd_array_count( code ) * vg_msg_cmd_type_size( code );
+}
+
+static u8 vg_msg_count_bits( u32 count ){
+   assert( (count <= 16) && count );
+   return ((count-1)<<2);
+}
+
+/* write a sized type */
+static void vg_msg_wkvnum( vg_msg *msg, const char *key,
+                           u8 type, u8 count, void *data ){
+   u8 code = type | vg_msg_count_bits(count);
+
+   vg_msg_wbuf( msg, &code, 1 );
    vg_msg_wstr( msg, key );
-   vg_msg_exchbuf( msg, 1, value, size );
+   vg_msg_wbuf( msg, data, vg_msg_cmd_bytecount(code) );
 }
 
-/* 
- * macros to write sized integers and floats
- *
- * you use them like:
- *   vg_msg_wkvint( &msg, "key", u32 value=32 );
- *
- * this will write a 32 bit unsigned int to the stream
- */
+#define _WRITE_KV_INTG_HELPER( TYPE ) \
+static void vg_msg_wkv##TYPE( vg_msg *msg, const char *key, TYPE v ){ \
+   vg_msg_wkvnum( msg, key, k_vg_msg_##TYPE, 1, &v ); \
+}
 
-#define vg_msg_wkvint( MSGPTR, KEY, DECL ){ \
-   DECL; \
-   vg__msg_wkvgen(MSGPTR, KEY, k_vg_msg_code_signed, &value, sizeof(value));\
-   }
-#define vg_msg_wkvuint( MSGPTR, KEY, DECL ){ \
-   DECL; \
-   vg__msg_wkvgen(MSGPTR, KEY, k_vg_msg_code_unsigned, &value, sizeof(value));\
-   }
-#define vg_msg_wkvfloat( MSGPTR, KEY, DECL ){ \
-   DECL; \
-   vg__msg_wkvgen(MSGPTR, KEY, k_vg_msg_code_float, &value, sizeof(value));\
-   }
+#define _WRITE_KV_VEC_HELPER( TYPE ) \
+static void vg_msg_wkv##TYPE( vg_msg *msg, const char *key, TYPE v ){ \
+   vg_msg_wkvnum( msg, key, k_vg_msg_##TYPE, 1, v ); \
+}
+
+_WRITE_KV_INTG_HELPER( u8 )
+_WRITE_KV_INTG_HELPER( u16 )
+_WRITE_KV_INTG_HELPER( u32 )
+_WRITE_KV_INTG_HELPER( u64 )
+_WRITE_KV_INTG_HELPER( i8 )
+_WRITE_KV_INTG_HELPER( i16 )
+_WRITE_KV_INTG_HELPER( i32 )
+_WRITE_KV_INTG_HELPER( i64 )
+_WRITE_KV_INTG_HELPER( f32 )
+_WRITE_KV_INTG_HELPER( f64 )
+_WRITE_KV_VEC_HELPER( v2f )
+_WRITE_KV_VEC_HELPER( v3f )
+_WRITE_KV_VEC_HELPER( v4f )
+
+static void vg_msg_init( vg_msg *msg, u8 *buffer, u32 len ){
+   msg->buf = buffer;
+   msg->cur.co = 0;
+   msg->cur.depth = 0;
+   msg->error = k_vg_msg_error_OK;
+   msg->max = len;
+}
 
 /*
  * The stream reading interface
@@ -265,63 +318,73 @@ static void vg__msg_wkvgen( vg_msg *msg, const char *key,
  * create an error if it runs of the end of the stream. every possible command
  * must be handled in this function */
 static int vg_msg_next( vg_msg *msg, vg_msg_cmd *cmd ){
-   vg_msg_exchbuf( msg, 0, &cmd->code, 1 );
+   vg_msg_rbuf( msg, &cmd->code, 1 );
+   if( msg->error != k_vg_msg_error_OK ) return 0;
+
+#ifdef VG_MSG_V1_SUPPORT
+  /* |sized|  |count-1| |shift|
+   * 0     1  0  0  0 1 0     0  0x44 (1 byte, float[2]. So, never used anyway)
+   * converts to
+   * 1     0  0  0  0 0 1     0  0x82
+   */
+   if( cmd->code == 0x44 ) cmd->code = 0x82;
+#endif
+   cmd->key_djb2 = 0;
    if( msg->error != k_vg_msg_error_OK ) return 0;
 
-   if( cmd->code == k_vg_msg_code_frame ){
+   if( cmd->code == k_vg_msg_frame ){
       cmd->key = vg_msg_rstr( msg, &cmd->key_djb2 );
-      msg->depth ++;
+      msg->cur.depth ++;
    }
-   else if( cmd->code == k_vg_msg_code_endframe ){
-      if( !msg->depth ){
+   else if( cmd->code == k_vg_msg_endframe ){
+      if( !msg->cur.depth ){
          msg->error = k_vg_msg_error_unbalanced;
          return 0;
       }
-      msg->depth --;
+      msg->cur.depth --;
    }
-   else if( cmd->code >= k_vg_msg_code_kv ){
+   else if( cmd->code >= k_vg_msg_kv ){
       cmd->key = vg_msg_rstr( msg, &cmd->key_djb2 );
       cmd->value_djb2 = 0;
-      cmd->value._u64 = 0;
 
-      if( cmd->code & (k_vg_msg_code_float|k_vg_msg_code_unsigned|
-                       k_vg_msg_code_signed )){
-         u8 len = cmd->code & 0xf;
-         vg_msg_exchbuf( msg, 0, (u8 *)(&cmd->value._u64), len );
+      if( cmd->code & k_vg_msg_type_base_bits ){
+         u32 bytes = vg_msg_cmd_bytecount( cmd->code );
+         cmd->value = &msg->buf[ msg->cur.co ];
+         msg->cur.co += bytes;
       }
-      else if( cmd->code == k_vg_msg_code_kvstring ){
-         cmd->value._buf = vg_msg_rstr( msg, &cmd->value_djb2 );
+      else if( cmd->code == k_vg_msg_kvstring ){
+         cmd->value = vg_msg_rstr( msg, &cmd->value_djb2 );
       }
-      else if( cmd->code == k_vg_msg_code_kvbin ){
-         u32 len;
-         vg_msg_exchbuf( msg, 0, (u8 *)(&len), 4 );
-         if( msg->error != k_vg_msg_error_OK ) return 0;
-         cmd->value._buf = &msg->buf[ msg->cur ];
-         msg->cur += len;
-         if( msg->cur > msg->max ){
-            msg->error = k_vg_msg_error_overflow;
-         }
+      else if( cmd->code == k_vg_msg_kvbin ){
+         vg_msg_rbuf( msg, (u8 *)(&cmd->len), 4 );
+         if( msg->error != k_vg_msg_error_OK ) 
+            return 0;
+         cmd->value = &msg->buf[ msg->cur.co ];
+         msg->cur.co += cmd->len;
       }
-      else{
+      else
          msg->error = k_vg_msg_error_unhandled_cmd;
-      }
+
+      if( msg->cur.co > msg->max )
+         msg->error = k_vg_msg_error_overflow;
    }
-   else{
+   else
       msg->error = k_vg_msg_error_unhandled_cmd;
-   }
 
-   if( msg->error != k_vg_msg_error_OK ) return 0;
-   else return 1;
+   if( msg->error != k_vg_msg_error_OK ) 
+      return 0;
+   else 
+      return 1;
 }
 
 /* move through the frame(and subframes) until we fall out of it */
 static int vg_msg_skip_frame( vg_msg *msg ){
    vg_msg_cmd cmd;
 
-   u32 depth = msg->depth-1;
-   while( vg_msg_next( msg, &cmd ) ){
-      if( msg->depth == depth ) return 1;
-   }
+   u32 start_depth = msg->cur.depth;
+   while( vg_msg_next( msg, &cmd ) )
+      if( msg->cur.depth < start_depth ) 
+         return 1;
    return 0;
 }
 
@@ -330,78 +393,286 @@ static int vg_msg_skip_frame( vg_msg *msg ){
  * -----------------------------------------------------------------------------
  */
 
-enum vg_msg_dir{
-   k_vg_msg_first=0, /* reset the frame pointer and find the first thing */
-   k_vg_msg_next=1   /* get next item in the stream, wont find behind cursor */
-};
-
-/* reset frame pointer and depth to the start of the frame set by seekframe */
-static void vg_msg_framereset( vg_msg *frame ){
-   frame->cur = frame->rframe_cur;
-   frame->depth = frame->rframe_depth;
-}
-
-/* moves to a frame, and sets the base frame in msg if it finds it */
-static int vg_msg_seekframe( vg_msg *msg, const char *name, 
-                             enum vg_msg_dir dir )
-{
-   if( dir == k_vg_msg_first ) vg_msg_framereset( msg );
-   
+/* moves to a frame, 
+ * returns 0 if drops out of scope or ends. 
+ */
+static int vg_msg_seekframe( vg_msg *msg, const char *name ){
+   vg_msg_cursor orig = msg->cur;
    vg_msg_cmd cmd;
    while( vg_msg_next( msg, &cmd ) ){
-      if( msg->depth < msg->rframe_depth ){
-         vg_msg_framereset(msg);
+      if( msg->cur.depth < orig.depth ){
+         msg->cur = orig;
          return 0;
       }
-      if( msg->depth != msg->rframe_depth+1 ) continue;
-      if( cmd.code == k_vg_msg_code_frame ){
-         if( VG_STRDJB2_EQ( name, cmd.key, cmd.key_djb2 ) ){
-            msg->rframe_cur = msg->cur;
-            msg->rframe_depth = msg->depth;
+      if( msg->cur.depth != orig.depth+1 ) 
+         continue;
+      if( cmd.code == k_vg_msg_frame )
+         if( !name || VG_STRDJB2_EQ( name, cmd.key, cmd.key_djb2 ) )
             return 1;
-         }
-      }
    }
 
+   msg->cur = orig;
    return 0;
 }
 
-/* move to the kv named key, doesnt matter what type */
-static vg_msg_cmd vg_msg_seekkv( vg_msg *msg, const char *key,
-                                 enum vg_msg_dir dir )
-{
-   if( dir == k_vg_msg_first ) vg_msg_framereset( msg );
+/* 
+ * Convert any type integral type to u64
+ */
+static u64 vg_msg_cast_to_u64( const void *src, u8 src_base, u8 src_size ){
+   if( src_base == k_vg_msg_float ){
+      if( src_size == 4 )      return (u64)(*((f32*)src));
+      else if( src_size == 8 ) return (u64)(*((f64*)src));
+      else                     return 0;
+   }
+   else {
+      u64 a = 0;
+      memcpy( &a, src, src_size );
+      return a;
+   }
+}
 
-   vg_msg_cmd kv;
-   kv.code = k_vg_msg_code_end;
-   kv.key = "";
-   kv.key_djb2 = 0;
-   kv.value._u64 = 0;
-   kv.key_djb2 = 0;
+/*
+ * Convert any integral type to i64 
+ */
+static i64 vg_msg_cast_to_i64( const void *src, u8 src_base, u8 src_size ){
+   if( src_base == k_vg_msg_float ){
+      if( src_size == 4 )      return (i64)(*((f32*)src));
+      else if( src_size == 8 ) return (i64)(*((f64*)src));
+      else                     return 0;
+   }
+   else {
+      u64 a = 0;
+      memcpy( &a, src, src_size );
+
+      if( (src_base == k_vg_msg_signed) && (src_size != 8) ){
+         /* extend sign bit */
+         u64 sign_bit = 0x1llu << ((src_size*8)-1);
+         if( a & sign_bit )
+            a |= (0xffffffffffffffffllu << (64-__builtin_clzll( a )));
+      }
 
-   vg_msg_cmd cmd;
-   while( vg_msg_next( msg, &cmd ) ){
-      if( msg->depth < msg->rframe_depth ){
-         vg_msg_framereset(msg);
-         return kv;
+      return *((i64*)&a);
+   }
+}
+
+/*
+ * Convert any integral type to f64
+ */
+static f64 vg_msg_cast_to_f64( const void *src, u8 src_base, u8 src_size ){
+   if( src_base == k_vg_msg_float ){
+      if( src_size == 4 )      return (f64)(*((f32*)src));
+      else if( src_size == 8 ) return *((f64*)src);
+      else                     return 0.0;
+   }
+   else 
+      return (f64)vg_msg_cast_to_i64( src, src_base, src_size );
+}
+
+/*
+ * Convert any full integral type code to another
+ * Passing in non-integral codes is undefined
+ */
+static void vg_msg_cast( const void *src, u8 src_code, void *dst, u8 dst_code ){
+   if( src_code == dst_code ){
+      memcpy( dst, src, vg_msg_cmd_bytecount( src_code ) );
+   }
+   else {
+      u32 src_n = vg_msg_cmd_array_count( src_code ),
+          dst_n = vg_msg_cmd_array_count( dst_code ),
+          src_s = vg_msg_cmd_type_size( src_code ),
+          dst_s = vg_msg_cmd_type_size( dst_code ),
+          src_b = src_code & k_vg_msg_type_base_bits,
+          dst_b = dst_code & k_vg_msg_type_base_bits;
+
+      memset( dst, 0, dst_s * dst_n );
+
+      for( u32 i=0; i<VG_MIN(src_n,dst_n); i ++ ){
+         const void *ptr_s = src + i*src_s;
+         void *ptr_d = dst + i*dst_s;
+
+         if( dst_b == k_vg_msg_unsigned ){
+            u64 a = vg_msg_cast_to_u64( ptr_s, src_b, src_s );
+            if     ( dst_s == 1 ) *((u8 *)ptr_d) = (u8 )a;
+            else if( dst_s == 2 ) *((u16*)ptr_d) = (u16)a;
+            else if( dst_s == 4 ) *((u32*)ptr_d) = (u32)a;
+            else if( dst_s == 8 ) *((u64*)ptr_d) = a;
+         }
+         else if( dst_b == k_vg_msg_signed ){
+            i64 a = vg_msg_cast_to_i64( ptr_s, src_b, src_s );
+            if     ( dst_s == 1 ) *((i8 *)ptr_d) = (i8 )a;
+            else if( dst_s == 2 ) *((i16*)ptr_d) = (i16)a;
+            else if( dst_s == 4 ) *((i32*)ptr_d) = (i32)a;
+            else if( dst_s == 8 ) *((i64*)ptr_d) = a;
+         }
+         else {
+            f64 a = vg_msg_cast_to_f64( ptr_s, src_b, src_s );
+            if     ( dst_s == 4 ) *((f32*)ptr_d) = (f32)a;
+            else if( dst_s == 8 ) *((f64*)ptr_d) = a;
+         }
+      }
+   }
+}
+
+/*
+ * search in current level from cursor, to find kv cmd
+ * returns 0 if not found
+ * Cursor does not move
+ * If found, the kv command is written to cmd
+ */
+static int vg_msg_getkvcmd( vg_msg *msg, const char *key, vg_msg_cmd *cmd ){
+   vg_msg_cursor orig = msg->cur;
+   while( vg_msg_next( msg, cmd ) ){
+      if( msg->cur.depth < orig.depth ){
+         msg->cur = orig;
+         return 0;
+      }
+      if( msg->cur.depth > orig.depth ) 
+         continue;
+      if( cmd->code > k_vg_msg_kv ){
+         if( VG_STRDJB2_EQ( key, cmd->key, cmd->key_djb2 ) ){
+            msg->cur = orig;
+            return 1;
+         }
       }
-      if( msg->depth > msg->rframe_depth ) continue;
-      if( cmd.code > k_vg_msg_code_kv )
-         if( VG_STRDJB2_EQ( key, cmd.key, cmd.key_djb2 ) ) 
-            return cmd;
    }
+   msg->error = k_vg_msg_error_OK;
+   msg->cur = orig;
+   return 0;
+}
 
-   return kv;
+/*
+ * Read a integral KV out to dst, and perform conversion if needed
+ * dst is always defined, if its not found its set to 0
+ */
+static int vg_msg_getkvintg( vg_msg *msg, const char *key, u8 type, void *dst ){
+   vg_msg_cmd cmd;
+   if( vg_msg_getkvcmd( msg, key, &cmd ) ){
+      vg_msg_cast( cmd.value, cmd.code, dst, type );
+      return 1;
+   }
+   else {
+      memset( dst, 0, vg_msg_cmd_bytecount(type) );
+      return 0;
+   }
 }
 
 /* helper for reading string kvs. returns NULL if not found */
-static const char *vg_msg_seekkvstr( vg_msg *msg, const char *key, 
-                                     enum vg_msg_dir dir )
-{
-   vg_msg_cmd cmd = vg_msg_seekkv( msg, key, dir );
-   if( cmd.code == k_vg_msg_code_kvstring ) return cmd.value._buf;
-   else return NULL;
+static const char *vg_msg_getkvstr( vg_msg *msg, const char *key ){
+   vg_msg_cmd cmd;
+   if( vg_msg_getkvcmd( msg, key, &cmd ) )
+      return cmd.value;
+   else 
+      return NULL;
+}
+
+#define _GET_KV_INTG_HELPER( TYPE ) \
+static TYPE vg_msg_getkv##TYPE( vg_msg *msg, const char *key, \
+                                TYPE default_value ){ \
+   vg_msg_cmd cmd; \
+   if( vg_msg_getkvcmd( msg, key, &cmd ) ){ \
+      TYPE v; \
+      vg_msg_cast( cmd.value, cmd.code, &v, k_vg_msg_##TYPE ); \
+      return v; \
+   } \
+   else \
+      return default_value; \
+}
+
+#define _GET_KV_VEC_HELPER( TYPE ) \
+static int vg_msg_getkv##TYPE( vg_msg *msg, const char *key, \
+                               TYPE v, \
+                               TYPE default_value ){ \
+   vg_msg_cmd cmd; \
+   if( vg_msg_getkvcmd( msg, key, &cmd ) ){ \
+      vg_msg_cast( cmd.value, cmd.code, v, k_vg_msg_##TYPE ); \
+      return 1; \
+   } \
+   else \
+      if ( default_value ) \
+         vg_msg_cast( default_value, k_vg_msg_##TYPE, v, k_vg_msg_##TYPE ); \
+   return 0; \
 }
 
+_GET_KV_INTG_HELPER( u8 )
+_GET_KV_INTG_HELPER( u16 )
+_GET_KV_INTG_HELPER( u32 )
+_GET_KV_INTG_HELPER( u64 )
+_GET_KV_INTG_HELPER( i8 )
+_GET_KV_INTG_HELPER( i16 )
+_GET_KV_INTG_HELPER( i32 )
+_GET_KV_INTG_HELPER( i64 )
+_GET_KV_INTG_HELPER( f32 )
+_GET_KV_INTG_HELPER( f64 )
+_GET_KV_VEC_HELPER( v2f )
+_GET_KV_VEC_HELPER( v3f )
+_GET_KV_VEC_HELPER( v4f )
+
+/* debug the thing */
+static void vg_msg_print( vg_msg *msg, u32 len ){
+   if( msg->error != k_vg_msg_error_OK ){
+      printf( "Message contains errors\n" );
+      return;
+   }
+
+   vg_msg b;
+   vg_msg_init( &b, msg->buf, len );
+
+   vg_msg_cmd cmd;
+   while( vg_msg_next( &b, &cmd ) ){
+      if( cmd.code == k_vg_msg_frame ){
+         for( u32 i=0; i<b.cur.depth-1; i++ ) printf( "  " );
+         printf( "'%s'{\n", cmd.key );
+      }
+      else {
+         for( u32 i=0; i<b.cur.depth; i++ ) printf( "  " );
+
+         if( cmd.code == k_vg_msg_endframe )
+            printf( "}\n" );
+         else if( cmd.code == k_vg_msg_kvstring )
+            printf( "'%s': '%s'\n", cmd.key, (char *)cmd.value );
+         else if( cmd.code == k_vg_msg_kvbin )
+            printf( "'%s': <binary data> (%u bytes)\n", cmd.key, cmd.len );
+         else {
+            u32 base = cmd.code & k_vg_msg_type_base_bits,
+                count = vg_msg_cmd_array_count( cmd.code ),
+                size = vg_msg_cmd_type_size( cmd.code );
+
+            printf( "'%s': ", cmd.key );
+
+            if( count > 1 ) printf( "{ " );
+
+            for( u32 i=0; i<count; i++ ){
+               const void *p = cmd.value + size*i;
+
+               if( base == k_vg_msg_unsigned ){
+                  printf( 
+#ifdef _WIN32
+                  "%llu"
+#else
+                  "%lu"
+#endif
+                  , vg_msg_cast_to_u64( p, base, size ) );
+               }
+               else if( base == k_vg_msg_signed ){
+                  printf(
+#ifdef _WIN32
+                  "%lld"
+#else
+                  "%ld"
+#endif
+                  , vg_msg_cast_to_i64( p, base, size ) );
+               }
+               else 
+                  printf( "%f", vg_msg_cast_to_f64( p, base, size ));
+
+               if( i+1<count ) printf(", ");
+            }
+
+            if( count > 1 ) printf( " }" );
+            printf( "\n" );
+         }
+      }
+   }
+}
 
 #endif /* VG_MSG_H */