fixed mistake in quaternion normalize
[vg.git] / vg_msg.h
index cc38e859cc0a9b34eb217e9c88da2af3d24ded87..ce012a6fc7bc6e0883b4df78b66a07992c613b45 100644 (file)
--- a/vg_msg.h
+++ b/vg_msg.h
 #ifndef VG_MSG_H
 #define VG_MSG_H
 #include "vg_stdint.h"
+#include "vg_platform.h"
+
+/*
+ * Example data:
+ *   kvstr "someinfo"
+ *   kvint 200
+ *   frame "person"{
+ *      name "jeff"
+ *      country "england"
+ *   }
+ *   frame "building"{
+ *      capacity 1000
+ *   }
+ *   frame "person"{
+ *      country "wales"
+ *      name "micheal"
+ *   }
+ *
+ * Creating the data in code:
+ * -----------------------------------------------------------------------------
+ *   u8 data_buf[512];
+ *   vg_msg data = {0};
+ *   data.buf = data_buf;
+ *   data.max = sizeof(data_buf);
+ *   vg_msg_wkvstr( &data, "kvstr", "someinfo" );
+ *   vg_msg_wkvint( &data, "kvint", i32 value=200 );
+ *
+ *   vg_msg_frame( &data, "person" );
+ *      vg_msg_wkvstr( &data, "name", "jeff" );
+ *      vg_msg_wkvstr( &data, "country", "england" );
+ *   vg_msg_end_frame( &data );
+ *
+ *   vg_msg_frame( &data, "building" );
+ *      vg_msg_wkvint( &data, "capacity", i32 value=1000 );
+ *   vg_msg_end_frame( &data );
+ *
+ *   vg_msg_frame( &data, "person" );
+ *      vg_msg_wkvstr( &data, "country", "wales" );
+ *      vg_msg_wkvstr( &data, "name", "micheal" );
+ *   vg_msg_end_frame( &data );
+ *
+ * Saving the data out
+ * -----------------------------------------------------------------------------
+ *
+ *   if( data.error == k_vg_msg_error_OK ){
+ *      // write data_buf, for length data.cur
+ *   }
+ *
+ * Load the data
+ * -----------------------------------------------------------------------------
+ *
+ *   u8 data_buf[512];
+ *   u32 data_len;
+ *
+ *   // read data_buf and data_len
+ *
+ *   vg_msg data = {0};
+ *   data.buf = data_buf;
+ *   data.len = data_len;
+ *
+ * Reading back the stream linearly
+ * -----------------------------------------------------------------------------
+ * 
+ *   vg_msg_cmd cmd;
+ *   while( vg_msg_next( &data, &cmd ) ){
+ *           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_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
+   /* 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_size_bits        = 0x03,
+   k_vg_msg_sized_type_bits  = 0xC0,
+
+   /* helpers */
+   k_vg_msg_8b  = 0x00,
+   k_vg_msg_16b = 0x01,
+   k_vg_msg_32b = 0x02,
+   k_vg_msg_64b = 0x03,
 };
 
 typedef struct vg_msg vg_msg;
 typedef struct vg_msg_cmd vg_msg_cmd;
 struct vg_msg{
-   u32 cur,max;
+   u32 cur,len,max;
    u8 *buf;
    u32 depth;
+
+   /* reading */
+   u32 rframe_depth, rframe_cur;
    
    enum vg_msg_error{
       k_vg_msg_error_OK,
@@ -36,43 +146,47 @@ 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;
 };
 
-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;
+/* 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+len > msg->max ){
+      msg->error = k_vg_msg_error_overflow;
+      return;
+   }
+
+   for( u32 i=0; i<len; i++ ){
+      msg->buf[ msg->cur ++ ] = buf[i];
+      msg->len ++;
+   }
 }
 
-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+len > msg->len ){
       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 ++ ];
    }
 }
 
+/* write null terminated string to stream */
 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;
    }
 }
 
+/* read null terminated string, range check and generate hash (djb2) */
 static const char *vg_msg_rstr( vg_msg *msg, u32 *djb2 ){
    if( msg->error != k_vg_msg_error_OK ) return 0;
 
@@ -91,14 +205,37 @@ static const char *vg_msg_rstr( vg_msg *msg, u32 *djb2 ){
    return str;
 }
 
+/* begin a new frame in message stream */
 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 );
+   vg_msg_wbuf( msg, (u8[]){ k_vg_msg_frame }, 1 );
    vg_msg_wstr( msg, name );
 }
 
+/* shift right side of cursor by len */
+static void vg_msg_makeroom( vg_msg *msg, u32 len ){
+   if( msg->error != k_vg_msg_error_OK ) return;
+   if( msg->len + len > msg->max ){
+      msg->error = k_vg_msg_error_overflow;
+      return;
+   }
+   u32 i = msg->len-msg->cur;
+   while( i --> 0 ){
+      msg->buf[ msg->cur+len+i ] = msg->buf[ msg->cur+i ];
+   }
+   msg->len += len;
+}
+
+/* paste data into msg, at the cursor location */
+static void vg_msg_insert( vg_msg *msg, vg_msg *data ){
+   vg_msg_makeroom( msg, data->len );
+   vg_msg_wbuf( msg, data->buf, data->len );
+   msg->depth += data->depth;
+}
+
+/* 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 ){
@@ -106,93 +243,115 @@ static void vg_msg_end_frame( vg_msg *msg ){
       return;
    }
    msg->depth --;
-   vg_msg_exchbuf( msg, 1, (u8[]){ k_vg_msg_code_endframe }, 1 );
+   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 );
+}
+
+/* get the byte count of a sized code */
+static u32 vg_msg_sized_bytecount( u8 code ){
+   u32 size  = 0x1 << (code & k_vg_msg_size_bits),
+       count = ((code & k_vg_msg_array_count_bits)>>2) + 1;
+   return size * count;
 }
 
-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 );
+/* write a sized type */
+static void vg_msg_wkvnum( vg_msg *msg, const char *key,
+                           u8 type, u8 count, void *data ){
+   u8 code = type | ((count-1)<<2);
+
+   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_sized_bytecount(code) );
 }
 
-#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));\
-   }
+static void vg_msg_wkvu32( vg_msg *msg, const char *key, u32 value ){
+   vg_msg_wkvnum( msg, key, k_vg_msg_unsigned|k_vg_msg_32b, 1, &value );
+}
+
+static void vg_msg_wkvu64( vg_msg *msg, const char *key, u64 value ){
+   vg_msg_wkvnum( msg, key, k_vg_msg_unsigned|k_vg_msg_64b, 1, &value );
+}
+
+/*
+ * The stream reading interface
+ * -----------------------------------------------------------------------------
+ */
 
+/* move the cursor through the next message. it will always read in the value or
+ * 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 );
+
+#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 ++;
    }
-   else if( cmd->code == k_vg_msg_code_endframe ){
+   else if( cmd->code == k_vg_msg_endframe ){
       if( !msg->depth ){
          msg->error = k_vg_msg_error_unbalanced;
          return 0;
       }
       msg->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_sized_type_bits ){
+         u32 bytes = vg_msg_sized_bytecount( cmd->code );
+         cmd->value = &msg->buf[ msg->cur ];
+         msg->cur += 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 ){
+      else if( cmd->code == k_vg_msg_kvbin ){
          u32 len;
-         vg_msg_exchbuf( msg, 0, (u8 *)(&len), 4 );
+         vg_msg_rbuf( msg, (u8 *)(&len), 4 );
          if( msg->error != k_vg_msg_error_OK ) return 0;
-         cmd->value._buf = &msg->buf[ msg->cur ];
+         cmd->value = &msg->buf[ msg->cur ];
          msg->cur += len;
-         if( msg->cur > msg->max ){
-            msg->error = k_vg_msg_error_overflow;
-         }
       }
-      else{
+      else
          msg->error = k_vg_msg_error_unhandled_cmd;
-      }
+
+      if( msg->cur > 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;
 }
 
+/* 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;
 
@@ -203,4 +362,142 @@ static int vg_msg_skip_frame( vg_msg *msg ){
    return 0;
 }
 
+/*
+ * A more friendly but slower interface 
+ * -----------------------------------------------------------------------------
+ */
+
+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 );
+   
+   vg_msg_cmd cmd;
+   while( vg_msg_next( msg, &cmd ) ){
+      if( msg->depth < msg->rframe_depth ){
+         vg_msg_framereset(msg);
+         return 0;
+      }
+      if( msg->depth != msg->rframe_depth+1 ) continue;
+      if( cmd.code == k_vg_msg_frame ){
+         if( VG_STRDJB2_EQ( name, cmd.key, cmd.key_djb2 ) ){
+            msg->rframe_cur = msg->cur;
+            msg->rframe_depth = msg->depth;
+            return 1;
+         }
+      }
+   }
+
+   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 );
+
+   vg_msg_cmd kv;
+   kv.code = k_vg_msg_end;
+   kv.key = "";
+   kv.key_djb2 = 0;
+   kv.value = NULL;
+   kv.value_djb2 = 0;
+
+   vg_msg_cmd cmd;
+   while( vg_msg_next( msg, &cmd ) ){
+      if( msg->depth < msg->rframe_depth ){
+         vg_msg_framereset(msg);
+         return kv;
+      }
+      if( msg->depth > msg->rframe_depth ) continue;
+      if( cmd.code > k_vg_msg_kv )
+         if( VG_STRDJB2_EQ( key, cmd.key, cmd.key_djb2 ) ) 
+            return cmd;
+   }
+
+   return kv;
+}
+
+/* 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_kvstring ) return cmd.value;
+   else return NULL;
+}
+
+static u64 vg_msg_read_as_u64( vg_msg_cmd *cmd ){
+   u8 sized_type = cmd->code & k_vg_msg_sized_type_bits;
+
+   u64 result = 0x00;
+   u8 *dst = (void *)(&result);
+   const u8 *src = cmd->value;
+
+   if( (sized_type == k_vg_msg_unsigned) || (sized_type == k_vg_msg_signed) ){
+      u32 bytes = vg_msg_sized_bytecount( cmd->code );
+      if( bytes > 8 ) bytes = 8;
+      for( u32 i=0; i<bytes; i ++ ) dst[i] = src[i];
+   }
+
+   return result;
+}
+
+static void 
+vg_msg_convert_num( vg_msg_cmd *cmd, u8 type, u32 count, void *result ){
+   u8 code = type | ((count-1)<<2);
+   if( code != cmd->code ) return;
+
+   const u8 *src = cmd->value;
+   u8 *dst = result;
+
+   u32 bytes = vg_msg_sized_bytecount( cmd->code );
+   for( u32 i=0; i<bytes; i ++ ) dst[i] = src[i];
+}
+
+static u32 vg_msg_seekkvu32( vg_msg *msg, const char *key, enum vg_msg_dir dir )
+{
+   vg_msg_cmd cmd = vg_msg_seekkv( msg, key, dir );
+   return vg_msg_read_as_u64( &cmd );
+}
+
+/* debug the thing */
+static void vg_msg_print( vg_msg *msg ){
+   vg_msg b = *msg;
+   b.cur = 0;
+
+   vg_msg_cmd cmd;
+   while( vg_msg_next( &b, &cmd ) ){
+      if( cmd.code == k_vg_msg_frame ){
+         for( u32 i=0; i<b.depth-1; i++ ) printf( "  " );
+         printf( "'%s'[%u]{\n", cmd.key, cmd.key_djb2 );
+      }
+      else {
+         for( u32 i=0; i<b.depth; i++ ) printf( "  " );
+
+         if( cmd.code == k_vg_msg_endframe )
+            printf( "}\n" );
+         else if( cmd.code == k_vg_msg_kvstring ){
+            printf( "'%s'[%u]: '%s'[%u]\n", cmd.key, cmd.key_djb2,
+                    (char *)cmd.value, cmd.value_djb2 );
+         }
+         else
+            printf( "'%s'[%u]: <binary data>\n", cmd.key, cmd.key_djb2 );
+      }
+   }
+}
+
 #endif /* VG_MSG_H */