#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;
+ * vg_msg_init( &data, data_buf, 512 );
+ * 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;
+ * vg_msg_init( &data, data_buf, data_len );
+ *
+ * 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 )
+ * 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_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_float = 0x20,
+ k_vg_msg_code_integer = k_vg_msg_code_signed|k_vg_msg_code_unsigned
};
typedef struct vg_msg vg_msg;
u32 cur,max;
u8 *buf;
u32 depth;
+
+ /* reading */
+ u32 rframe_depth, rframe_cur;
enum vg_msg_error{
k_vg_msg_error_OK,
u32 value_djb2;
};
+/* 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 or read a buffer from msg, rang checked. */
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 ){
}
}
+/* 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++ ){
}
}
+/* 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;
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;
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 ){
vg_msg_exchbuf( msg, 1, (u8[]){ k_vg_msg_code_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_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_wstr( msg, key );
vg_msg_exchbuf( msg, 1, bin, len );
}
+/* 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, value, size );
}
+/*
+ * 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 vg_msg_wkvint( MSGPTR, KEY, DECL ){ \
DECL; \
vg__msg_wkvgen(MSGPTR, KEY, k_vg_msg_code_signed, &value, sizeof(value));\
vg__msg_wkvgen(MSGPTR, KEY, k_vg_msg_code_float, &value, sizeof(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 );
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;
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_code_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_code_end;
+ kv.key = "";
+ kv.key_djb2 = 0;
+ kv.value._u64 = 0;
+ kv.key_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_code_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_code_kvstring ) return cmd.value._buf;
+ else return NULL;
+}
+
+
#endif /* VG_MSG_H */