modal
[vg.git] / vg_msg.h
1 #ifndef VG_MSG_H
2 #define VG_MSG_H
3 #include "vg_stdint.h"
4 #include "vg_platform.h"
5
6 /*
7 * Example data:
8 * kvstr "someinfo"
9 * kvint 200
10 * frame "person"{
11 * name "jeff"
12 * country "england"
13 * }
14 * frame "building"{
15 * capacity 1000
16 * }
17 * frame "person"{
18 * country "wales"
19 * name "micheal"
20 * }
21 *
22 * Creating the data in code:
23 * -----------------------------------------------------------------------------
24 * u8 data_buf[512];
25 * vg_msg data;
26 * vg_msg_init( &data, data_buf, 512 );
27 * vg_msg_wkvstr( &data, "kvstr", "someinfo" );
28 * vg_msg_wkvint( &data, "kvint", i32 value=200 );
29 *
30 * vg_msg_frame( &data, "person" );
31 * vg_msg_wkvstr( &data, "name", "jeff" );
32 * vg_msg_wkvstr( &data, "country", "england" );
33 * vg_msg_end_frame( &data );
34 *
35 * vg_msg_frame( &data, "building" );
36 * vg_msg_wkvint( &data, "capacity", i32 value=1000 );
37 * vg_msg_end_frame( &data );
38 *
39 * vg_msg_frame( &data, "person" );
40 * vg_msg_wkvstr( &data, "country", "wales" );
41 * vg_msg_wkvstr( &data, "name", "micheal" );
42 * vg_msg_end_frame( &data );
43 *
44 * Saving the data out
45 * -----------------------------------------------------------------------------
46 *
47 * if( data.error == k_vg_msg_error_OK ){
48 * // write data_buf, for length data.cur
49 * }
50 *
51 * Load the data
52 * -----------------------------------------------------------------------------
53 *
54 * u8 data_buf[512];
55 * u32 data_len;
56 * // read data_buf and data_len
57 * vg_msg data;
58 * vg_msg_init( &data, data_buf, data_len );
59 *
60 * Reading back the stream linearly
61 * -----------------------------------------------------------------------------
62 *
63 * vg_msg_cmd cmd;
64 * while( vg_msg_next( &data, &cmd ) ){
65 * if( cmd.code == k_vg_msg_code_frame ) printf( "{" );
66 * else if( cmd.code == k_vg_msg_code_endframe ) printf( "}" );
67 * esle if( cmd.code == k_vg_msg_code_kvstring )
68 * printf( "string: %s\n", cmd.value._buf );
69 * }
70 *
71 * Reading back the stream as frames/nodes. this is obviously slower
72 * -----------------------------------------------------------------------------
73 *
74 * vg_msg person = data
75 * while( vg_msg_seekframe( &person, "person", VG_MSG_NEXT ) ){
76 * const char *name = vg_msg_seekkvstr(&person, "name", VG_MSG_NEXT);
77 * const char *country = vg_msg_seekkvstr(&person, "country", VG_MSG_FIRST);
78 *
79 * printf( "Guy '%s' is from '%s'\n", name, country );
80 * vg_msg_skip_frame(&person);
81 * }
82 *
83 * vg_msg building = root;
84 * if( vg_msg_seekframe( &building, "building", VG_MSG_FIRST ) ){
85 * vg_msg_cmd capacity = vg_msg_seekkv(&building, "capacity", VG_MSG_FIRST);
86 * if( capacity.code & k_vg_msg_code_signed )
87 * print( "building capacity: %d\n", capacity.value._i32 );
88 *
89 * vg_msg_skip_frame( &building );
90 * }
91 *
92 */
93
94 enum vg_msg_code{
95 k_vg_msg_code_end = 0,
96 k_vg_msg_code_frame = 1,
97 k_vg_msg_code_endframe = 2,
98 k_vg_msg_code_kv = 10,
99 k_vg_msg_code_kvstring = 11,
100 k_vg_msg_code_kvbin = 12,
101 k_vg_msg_code_signed = 0x80, /* byte sizes stored in lower 4 bits */
102 k_vg_msg_code_unsigned = 0x40,
103 k_vg_msg_code_float = 0x20,
104 k_vg_msg_code_integer = k_vg_msg_code_signed|k_vg_msg_code_unsigned
105 };
106
107 typedef struct vg_msg vg_msg;
108 typedef struct vg_msg_cmd vg_msg_cmd;
109 struct vg_msg{
110 u32 cur,max;
111 u8 *buf;
112 u32 depth;
113
114 /* reading */
115 u32 rframe_depth, rframe_cur;
116
117 enum vg_msg_error{
118 k_vg_msg_error_OK,
119 k_vg_msg_error_unbalanced,
120 k_vg_msg_error_overflow,
121 k_vg_msg_error_unhandled_cmd
122 }
123 error;
124 };
125
126 struct vg_msg_cmd{
127 u8 code;
128
129 const char *key;
130 u32 key_djb2;
131
132 union{ const void *_buf;
133 u8 _u8; i8 _i8;
134 u16 _u16; i16 _i16;
135 u32 _u32; i32 _i32; f32 _f32;
136 u64 _u64; i64 _i64; f64 _f64;
137 } value;
138 u32 value_djb2;
139 };
140
141 /* generic stream reset */
142 static void vg_msg_init( vg_msg *msg, u8 *buf, u32 max ){
143 msg->cur = 0;
144 msg->max = max;
145 msg->buf = buf;
146 msg->depth = 0;
147 msg->error = k_vg_msg_error_OK;
148 msg->rframe_depth = 0;
149 msg->rframe_cur = 0;
150 }
151
152 /* write or read a buffer from msg, rang checked. */
153 static void vg_msg_exchbuf( vg_msg *msg, int write, u8 *buf, u32 len ){
154 if( msg->error != k_vg_msg_error_OK ) return;
155 if( msg->cur+len > msg->max ){
156 msg->error = k_vg_msg_error_overflow;
157 return;
158 }
159 for( u32 i=0; i<len; i++ ){
160 if( write ) msg->buf[ msg->cur ++ ] = buf[i];
161 else buf[i] = msg->buf[ msg->cur ++ ];
162 }
163 }
164
165 /* write null terminated string to stream */
166 static void vg_msg_wstr( vg_msg *msg, const char *str ){
167 if( msg->error != k_vg_msg_error_OK ) return;
168 for( u32 i=0;; i++ ){
169 vg_msg_exchbuf( msg, 1, (u8[]){ str[i] }, 1 );
170 if( !str[i] ) break;
171 }
172 }
173
174 /* read null terminated string, range check and generate hash (djb2) */
175 static const char *vg_msg_rstr( vg_msg *msg, u32 *djb2 ){
176 if( msg->error != k_vg_msg_error_OK ) return 0;
177
178 u32 hash = 5381, c;
179 const char *str = (void *)(&msg->buf[ msg->cur ]);
180
181 while( (c = msg->buf[ msg->cur ++ ]) ){
182 if( msg->cur >= msg->max ){
183 msg->error = k_vg_msg_error_overflow;
184 return 0;
185 }
186 hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
187 }
188
189 *djb2 = hash;
190 return str;
191 }
192
193 /* begin a new frame in message stream */
194 static void vg_msg_frame( vg_msg *msg, const char *name ){
195 if( msg->error != k_vg_msg_error_OK ) return;
196
197 msg->depth ++;
198 vg_msg_exchbuf( msg, 1, (u8[]){ k_vg_msg_code_frame }, 1 );
199 vg_msg_wstr( msg, name );
200 }
201
202 /* end frame in message stream */
203 static void vg_msg_end_frame( vg_msg *msg ){
204 if( msg->error != k_vg_msg_error_OK ) return;
205 if( !msg->depth ){
206 msg->error = k_vg_msg_error_unbalanced;
207 return;
208 }
209 msg->depth --;
210 vg_msg_exchbuf( msg, 1, (u8[]){ k_vg_msg_code_endframe }, 1 );
211 }
212
213 /* write a KV string to stream */
214 static void vg_msg_wkvstr( vg_msg *msg, const char *key, const char *value ){
215 vg_msg_exchbuf( msg, 1, (u8[]){ k_vg_msg_code_kvstring }, 1 );
216 vg_msg_wstr( msg, key );
217 vg_msg_wstr( msg, value );
218 }
219
220 /* write a binary block to stream */
221 static void vg_msg_wkvbin( vg_msg *msg, const char *key, u8 *bin, u32 len ){
222 vg_msg_exchbuf( msg, 1, (u8[]){ k_vg_msg_code_kvbin }, 1 );
223 vg_msg_wstr( msg, key );
224 vg_msg_exchbuf( msg, 1, (u8 *)(&len), 4 );
225 vg_msg_exchbuf( msg, 1, bin, len );
226 }
227
228 /* write a generic sized kv */
229 static void vg__msg_wkvgen( vg_msg *msg, const char *key,
230 u8 basecode, void *value, u32 size ){
231 u8 code = basecode | size;
232 vg_msg_exchbuf( msg, 1, &code, 1 );
233 vg_msg_wstr( msg, key );
234 vg_msg_exchbuf( msg, 1, value, size );
235 }
236
237 /*
238 * macros to write sized integers and floats
239 *
240 * you use them like:
241 * vg_msg_wkvint( &msg, "key", u32 value=32 );
242 *
243 * this will write a 32 bit unsigned int to the stream
244 */
245
246 #define vg_msg_wkvint( MSGPTR, KEY, DECL ){ \
247 DECL; \
248 vg__msg_wkvgen(MSGPTR, KEY, k_vg_msg_code_signed, &value, sizeof(value));\
249 }
250 #define vg_msg_wkvuint( MSGPTR, KEY, DECL ){ \
251 DECL; \
252 vg__msg_wkvgen(MSGPTR, KEY, k_vg_msg_code_unsigned, &value, sizeof(value));\
253 }
254 #define vg_msg_wkvfloat( MSGPTR, KEY, DECL ){ \
255 DECL; \
256 vg__msg_wkvgen(MSGPTR, KEY, k_vg_msg_code_float, &value, sizeof(value));\
257 }
258
259 /*
260 * The stream reading interface
261 * -----------------------------------------------------------------------------
262 */
263
264 /* move the cursor through the next message. it will always read in the value or
265 * create an error if it runs of the end of the stream. every possible command
266 * must be handled in this function */
267 static int vg_msg_next( vg_msg *msg, vg_msg_cmd *cmd ){
268 vg_msg_exchbuf( msg, 0, &cmd->code, 1 );
269 if( msg->error != k_vg_msg_error_OK ) return 0;
270
271 if( cmd->code == k_vg_msg_code_frame ){
272 cmd->key = vg_msg_rstr( msg, &cmd->key_djb2 );
273 msg->depth ++;
274 }
275 else if( cmd->code == k_vg_msg_code_endframe ){
276 if( !msg->depth ){
277 msg->error = k_vg_msg_error_unbalanced;
278 return 0;
279 }
280 msg->depth --;
281 }
282 else if( cmd->code >= k_vg_msg_code_kv ){
283 cmd->key = vg_msg_rstr( msg, &cmd->key_djb2 );
284 cmd->value_djb2 = 0;
285 cmd->value._u64 = 0;
286
287 if( cmd->code & (k_vg_msg_code_float|k_vg_msg_code_unsigned|
288 k_vg_msg_code_signed )){
289 u8 len = cmd->code & 0xf;
290 vg_msg_exchbuf( msg, 0, (u8 *)(&cmd->value._u64), len );
291 }
292 else if( cmd->code == k_vg_msg_code_kvstring ){
293 cmd->value._buf = vg_msg_rstr( msg, &cmd->value_djb2 );
294 }
295 else if( cmd->code == k_vg_msg_code_kvbin ){
296 u32 len;
297 vg_msg_exchbuf( msg, 0, (u8 *)(&len), 4 );
298 if( msg->error != k_vg_msg_error_OK ) return 0;
299 cmd->value._buf = &msg->buf[ msg->cur ];
300 msg->cur += len;
301 if( msg->cur > msg->max ){
302 msg->error = k_vg_msg_error_overflow;
303 }
304 }
305 else{
306 msg->error = k_vg_msg_error_unhandled_cmd;
307 }
308 }
309 else{
310 msg->error = k_vg_msg_error_unhandled_cmd;
311 }
312
313 if( msg->error != k_vg_msg_error_OK ) return 0;
314 else return 1;
315 }
316
317 /* move through the frame(and subframes) until we fall out of it */
318 static int vg_msg_skip_frame( vg_msg *msg ){
319 vg_msg_cmd cmd;
320
321 u32 depth = msg->depth-1;
322 while( vg_msg_next( msg, &cmd ) ){
323 if( msg->depth == depth ) return 1;
324 }
325 return 0;
326 }
327
328 /*
329 * A more friendly but slower interface
330 * -----------------------------------------------------------------------------
331 */
332
333 enum vg_msg_dir{
334 k_vg_msg_first=0, /* reset the frame pointer and find the first thing */
335 k_vg_msg_next=1 /* get next item in the stream, wont find behind cursor */
336 };
337
338 /* reset frame pointer and depth to the start of the frame set by seekframe */
339 static void vg_msg_framereset( vg_msg *frame ){
340 frame->cur = frame->rframe_cur;
341 frame->depth = frame->rframe_depth;
342 }
343
344 /* moves to a frame, and sets the base frame in msg if it finds it */
345 static int vg_msg_seekframe( vg_msg *msg, const char *name,
346 enum vg_msg_dir dir )
347 {
348 if( dir == k_vg_msg_first ) vg_msg_framereset( msg );
349
350 vg_msg_cmd cmd;
351 while( vg_msg_next( msg, &cmd ) ){
352 if( msg->depth < msg->rframe_depth ){
353 vg_msg_framereset(msg);
354 return 0;
355 }
356 if( msg->depth != msg->rframe_depth+1 ) continue;
357 if( cmd.code == k_vg_msg_code_frame ){
358 if( VG_STRDJB2_EQ( name, cmd.key, cmd.key_djb2 ) ){
359 msg->rframe_cur = msg->cur;
360 msg->rframe_depth = msg->depth;
361 return 1;
362 }
363 }
364 }
365
366 return 0;
367 }
368
369 /* move to the kv named key, doesnt matter what type */
370 static vg_msg_cmd vg_msg_seekkv( vg_msg *msg, const char *key,
371 enum vg_msg_dir dir )
372 {
373 if( dir == k_vg_msg_first ) vg_msg_framereset( msg );
374
375 vg_msg_cmd kv;
376 kv.code = k_vg_msg_code_end;
377 kv.key = "";
378 kv.key_djb2 = 0;
379 kv.value._u64 = 0;
380 kv.key_djb2 = 0;
381
382 vg_msg_cmd cmd;
383 while( vg_msg_next( msg, &cmd ) ){
384 if( msg->depth < msg->rframe_depth ){
385 vg_msg_framereset(msg);
386 return kv;
387 }
388 if( msg->depth > msg->rframe_depth ) continue;
389 if( cmd.code > k_vg_msg_code_kv )
390 if( VG_STRDJB2_EQ( key, cmd.key, cmd.key_djb2 ) )
391 return cmd;
392 }
393
394 return kv;
395 }
396
397 /* helper for reading string kvs. returns NULL if not found */
398 static const char *vg_msg_seekkvstr( vg_msg *msg, const char *key,
399 enum vg_msg_dir dir )
400 {
401 vg_msg_cmd cmd = vg_msg_seekkv( msg, key, dir );
402 if( cmd.code == k_vg_msg_code_kvstring ) return cmd.value._buf;
403 else return NULL;
404 }
405
406
407 #endif /* VG_MSG_H */