binary message system
[vg.git] / vg_msg.h
diff --git a/vg_msg.h b/vg_msg.h
new file mode 100644 (file)
index 0000000..cc38e85
--- /dev/null
+++ b/vg_msg.h
@@ -0,0 +1,206 @@
+#ifndef VG_MSG_H
+#define VG_MSG_H
+#include "vg_stdint.h"
+
+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
+};
+
+typedef struct vg_msg vg_msg;
+typedef struct vg_msg_cmd vg_msg_cmd;
+struct vg_msg{
+   u32 cur,max;
+   u8 *buf;
+   u32 depth;
+   
+   enum vg_msg_error{
+      k_vg_msg_error_OK,
+      k_vg_msg_error_unbalanced,
+      k_vg_msg_error_overflow,
+      k_vg_msg_error_unhandled_cmd
+   }
+   error;
+};
+
+struct vg_msg_cmd{
+   u8 code;
+
+   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;
+   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;
+}
+
+static void vg_msg_exchbuf( vg_msg *msg, int write, 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++ ){
+      if( write ) msg->buf[ msg->cur ++ ] = buf[i];
+      else        buf[i] = msg->buf[ msg->cur ++ ];
+   }
+}
+
+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 );
+      if( !str[i] ) break;
+   }
+}
+
+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 ]);
+
+   while( (c = msg->buf[ msg->cur ++ ]) ){
+      if( msg->cur >= msg->max ){
+         msg->error = k_vg_msg_error_overflow;
+         return 0;
+      }
+      hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
+   }
+   
+   *djb2 = hash;
+   return str;
+}
+
+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_wstr( msg, name );
+}
+
+static void vg_msg_end_frame( vg_msg *msg ){
+   if( msg->error != k_vg_msg_error_OK ) return;
+   if( !msg->depth ){
+      msg->error = k_vg_msg_error_unbalanced;
+      return;
+   }
+   msg->depth --;
+   vg_msg_exchbuf( msg, 1, (u8[]){ k_vg_msg_code_endframe }, 1 );
+}
+
+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_wstr( msg, key );
+   vg_msg_wstr( msg, value );
+}
+
+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_wstr( msg, key );
+   vg_msg_exchbuf( msg, 1, (u8 *)(&len), 4 );
+   vg_msg_exchbuf( msg, 1, bin, len );
+}
+
+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 );
+   vg_msg_wstr( msg, key );
+   vg_msg_exchbuf( msg, 1, value, size );
+}
+
+#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 int vg_msg_next( vg_msg *msg, vg_msg_cmd *cmd ){
+   vg_msg_exchbuf( msg, 0, &cmd->code, 1 );
+   if( msg->error != k_vg_msg_error_OK ) return 0;
+
+   if( cmd->code == k_vg_msg_code_frame ){
+      cmd->key = vg_msg_rstr( msg, &cmd->key_djb2 );
+      msg->depth ++;
+   }
+   else if( cmd->code == k_vg_msg_code_endframe ){
+      if( !msg->depth ){
+         msg->error = k_vg_msg_error_unbalanced;
+         return 0;
+      }
+      msg->depth --;
+   }
+   else if( cmd->code >= k_vg_msg_code_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 );
+      }
+      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_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{
+         msg->error = k_vg_msg_error_unhandled_cmd;
+      }
+   }
+   else{
+      msg->error = k_vg_msg_error_unhandled_cmd;
+   }
+
+   if( msg->error != k_vg_msg_error_OK ) return 0;
+   else return 1;
+}
+
+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;
+   }
+   return 0;
+}
+
+#endif /* VG_MSG_H */