28ae5c8c52e0f6ad0233851017c6cd758eb21484
[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 = {0};
26 * data.buf = data_buf;
27 * data.max = sizeof(data_buf);
28 * vg_msg_wkvstr( &data, "kvstr", "someinfo" );
29 * vg_msg_wkvint( &data, "kvint", i32 value=200 );
30 *
31 * vg_msg_frame( &data, "person" );
32 * vg_msg_wkvstr( &data, "name", "jeff" );
33 * vg_msg_wkvstr( &data, "country", "england" );
34 * vg_msg_end_frame( &data );
35 *
36 * vg_msg_frame( &data, "building" );
37 * vg_msg_wkvint( &data, "capacity", i32 value=1000 );
38 * vg_msg_end_frame( &data );
39 *
40 * vg_msg_frame( &data, "person" );
41 * vg_msg_wkvstr( &data, "country", "wales" );
42 * vg_msg_wkvstr( &data, "name", "micheal" );
43 * vg_msg_end_frame( &data );
44 *
45 * Saving the data out
46 * -----------------------------------------------------------------------------
47 *
48 * if( data.error == k_vg_msg_error_OK ){
49 * // write data_buf, for length data.cur
50 * }
51 *
52 * Load the data
53 * -----------------------------------------------------------------------------
54 *
55 * u8 data_buf[512];
56 * u32 data_len;
57 *
58 * // read data_buf and data_len
59 *
60 * vg_msg data = {0};
61 * data.buf = data_buf;
62 * data.len = data_len;
63 *
64 * Reading back the stream linearly
65 * -----------------------------------------------------------------------------
66 *
67 * vg_msg_cmd cmd;
68 * while( vg_msg_next( &data, &cmd ) ){
69 * if( cmd.code == k_vg_msg_frame ) printf( "{" );
70 * else if( cmd.code == k_vg_msg_endframe ) printf( "}" );
71 * esle if( cmd.code == k_vg_msg_kvstring )
72 * printf( "string: %s\n", cmd.value._buf );
73 * }
74 *
75 * Reading back the stream as frames/nodes. this is obviously slower
76 * -----------------------------------------------------------------------------
77 *
78 * vg_msg person = data
79 * while( vg_msg_seekframe( &person, "person", VG_MSG_NEXT ) ){
80 * const char *name = vg_msg_seekkvstr(&person, "name", VG_MSG_NEXT);
81 * const char *country = vg_msg_seekkvstr(&person, "country", VG_MSG_FIRST);
82 *
83 * printf( "Guy '%s' is from '%s'\n", name, country );
84 * vg_msg_skip_frame(&person);
85 * }
86 *
87 * vg_msg building = root;
88 * if( vg_msg_seekframe( &building, "building", VG_MSG_FIRST ) ){
89 * vg_msg_cmd capacity = vg_msg_seekkv(&building, "capacity", VG_MSG_FIRST);
90 * if( capacity.code & k_vg_msg_signed )
91 * print( "building capacity: %d\n", capacity.value._i32 );
92 *
93 * vg_msg_skip_frame( &building );
94 * }
95 *
96 */
97
98 enum vg_msg_code{
99 /* low types */
100 k_vg_msg_end = 0,
101 k_vg_msg_frame = 1,
102 k_vg_msg_endframe = 2,
103 k_vg_msg_kv = 10,
104 k_vg_msg_kvstring = 11,
105 k_vg_msg_kvbin = 12,
106
107 /* variable sized types */
108 k_vg_msg_float = 0x40,
109 k_vg_msg_unsigned = 0x80,
110 k_vg_msg_signed = 0xC0,
111
112 /* masks */
113 k_vg_msg_array_count_bits = 0x3C,
114 k_vg_msg_size_bits = 0x03,
115 k_vg_msg_sized_type_bits = 0xC0,
116
117 /* helpers */
118 k_vg_msg_8b = 0x00,
119 k_vg_msg_16b = 0x01,
120 k_vg_msg_32b = 0x02,
121 k_vg_msg_64b = 0x03,
122 };
123
124 typedef struct vg_msg vg_msg;
125 typedef struct vg_msg_cmd vg_msg_cmd;
126 struct vg_msg{
127 u32 cur,len,max;
128 u8 *buf;
129 u32 depth;
130
131 /* reading */
132 u32 rframe_depth, rframe_cur;
133
134 enum vg_msg_error{
135 k_vg_msg_error_OK,
136 k_vg_msg_error_unbalanced,
137 k_vg_msg_error_overflow,
138 k_vg_msg_error_unhandled_cmd
139 }
140 error;
141 };
142
143 struct vg_msg_cmd{
144 u8 code;
145
146 const char *key;
147 u32 key_djb2;
148
149 const void *value;
150 u32 value_djb2;
151 };
152
153 /* write a buffer from msg, rang checked. */
154 static void vg_msg_wbuf( vg_msg *msg, u8 *buf, u32 len ){
155 if( msg->error != k_vg_msg_error_OK ) return;
156 if( msg->cur+len > msg->max ){
157 msg->error = k_vg_msg_error_overflow;
158 return;
159 }
160
161 for( u32 i=0; i<len; i++ ){
162 msg->buf[ msg->cur ++ ] = buf[i];
163 msg->len ++;
164 }
165 }
166
167 /* read a buffer from msg, rang checked. */
168 static void vg_msg_rbuf( vg_msg *msg, u8 *buf, u32 len ){
169 if( msg->error != k_vg_msg_error_OK ) return;
170 if( msg->cur+len > msg->len ){
171 msg->error = k_vg_msg_error_overflow;
172 return;
173 }
174
175 for( u32 i=0; i<len; i++ ){
176 buf[i] = msg->buf[ msg->cur ++ ];
177 }
178 }
179
180 /* write null terminated string to stream */
181 static void vg_msg_wstr( vg_msg *msg, const char *str ){
182 if( msg->error != k_vg_msg_error_OK ) return;
183 for( u32 i=0;; i++ ){
184 vg_msg_wbuf( msg, (u8[]){ str[i] }, 1 );
185 if( !str[i] ) break;
186 }
187 }
188
189 /* read null terminated string, range check and generate hash (djb2) */
190 static const char *vg_msg_rstr( vg_msg *msg, u32 *djb2 ){
191 if( msg->error != k_vg_msg_error_OK ) return 0;
192
193 u32 hash = 5381, c;
194 const char *str = (void *)(&msg->buf[ msg->cur ]);
195
196 while( (c = msg->buf[ msg->cur ++ ]) ){
197 if( msg->cur >= msg->max ){
198 msg->error = k_vg_msg_error_overflow;
199 return 0;
200 }
201 hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
202 }
203
204 *djb2 = hash;
205 return str;
206 }
207
208 /* begin a new frame in message stream */
209 static void vg_msg_frame( vg_msg *msg, const char *name ){
210 if( msg->error != k_vg_msg_error_OK ) return;
211
212 msg->depth ++;
213 vg_msg_wbuf( msg, (u8[]){ k_vg_msg_frame }, 1 );
214 vg_msg_wstr( msg, name );
215 }
216
217 /* shift right side of cursor by len */
218 static void vg_msg_makeroom( vg_msg *msg, u32 len ){
219 if( msg->error != k_vg_msg_error_OK ) return;
220 if( msg->len + len > msg->max ){
221 msg->error = k_vg_msg_error_overflow;
222 return;
223 }
224 u32 i = msg->len-msg->cur;
225 while( i --> 0 ){
226 msg->buf[ msg->cur+len+i ] = msg->buf[ msg->cur+i ];
227 }
228 msg->len += len;
229 }
230
231 /* paste data into msg, at the cursor location */
232 static void vg_msg_insert( vg_msg *msg, vg_msg *data ){
233 vg_msg_makeroom( msg, data->len );
234 vg_msg_wbuf( msg, data->buf, data->len );
235 msg->depth += data->depth;
236 }
237
238 /* end frame in message stream */
239 static void vg_msg_end_frame( vg_msg *msg ){
240 if( msg->error != k_vg_msg_error_OK ) return;
241 if( !msg->depth ){
242 msg->error = k_vg_msg_error_unbalanced;
243 return;
244 }
245 msg->depth --;
246 vg_msg_wbuf( msg, (u8[]){ k_vg_msg_endframe }, 1 );
247 }
248
249 /* write a KV string to stream */
250 static void vg_msg_wkvstr( vg_msg *msg, const char *key, const char *value ){
251 vg_msg_wbuf( msg, (u8[]){ k_vg_msg_kvstring }, 1 );
252 vg_msg_wstr( msg, key );
253 vg_msg_wstr( msg, value );
254 }
255
256 /* write a binary block to stream */
257 static void vg_msg_wkvbin( vg_msg *msg, const char *key, u8 *bin, u32 len ){
258 vg_msg_wbuf( msg, (u8[]){ k_vg_msg_kvbin }, 1 );
259 vg_msg_wstr( msg, key );
260 vg_msg_wbuf( msg, (u8 *)(&len), 4 );
261 vg_msg_wbuf( msg, bin, len );
262 }
263
264 /* get the byte count of a sized code */
265 static u32 vg_msg_sized_bytecount( u8 code ){
266 u32 size = 0x1 << (code & k_vg_msg_size_bits),
267 count = ((code & k_vg_msg_array_count_bits)>>2) + 1;
268 return size * count;
269 }
270
271 /* write a sized type */
272 static void vg_msg_wkvnum( vg_msg *msg, const char *key,
273 u8 type, u8 count, void *data ){
274 u8 code = type | ((count-1)<<2);
275
276 vg_msg_wbuf( msg, &code, 1 );
277 vg_msg_wstr( msg, key );
278 vg_msg_wbuf( msg, data, vg_msg_sized_bytecount(code) );
279 }
280
281 static void vg_msg_wkvu32( vg_msg *msg, const char *key, u32 value ){
282 vg_msg_wkvnum( msg, key, k_vg_msg_unsigned|k_vg_msg_32b, 1, &value );
283 }
284
285 static void vg_msg_wkvu64( vg_msg *msg, const char *key, u64 value ){
286 vg_msg_wkvnum( msg, key, k_vg_msg_unsigned|k_vg_msg_64b, 1, &value );
287 }
288
289 /*
290 * The stream reading interface
291 * -----------------------------------------------------------------------------
292 */
293
294 /* move the cursor through the next message. it will always read in the value or
295 * create an error if it runs of the end of the stream. every possible command
296 * must be handled in this function */
297 static int vg_msg_next( vg_msg *msg, vg_msg_cmd *cmd ){
298 vg_msg_rbuf( msg, &cmd->code, 1 );
299
300 #ifdef VG_MSG_V1_SUPPORT
301 /* |sized| |count-1| |shift|
302 * 0 1 0 0 0 1 0 0 0x44 (1 byte, float[2]. So, never used anyway)
303 * converts to
304 * 1 0 0 0 0 0 1 0 0x82
305 */
306 if( cmd->code == 0x44 ) cmd->code = 0x82;
307 #endif
308
309 if( msg->error != k_vg_msg_error_OK ) return 0;
310
311 if( cmd->code == k_vg_msg_frame ){
312 cmd->key = vg_msg_rstr( msg, &cmd->key_djb2 );
313 msg->depth ++;
314 }
315 else if( cmd->code == k_vg_msg_endframe ){
316 if( !msg->depth ){
317 msg->error = k_vg_msg_error_unbalanced;
318 return 0;
319 }
320 msg->depth --;
321 }
322 else if( cmd->code >= k_vg_msg_kv ){
323 cmd->key = vg_msg_rstr( msg, &cmd->key_djb2 );
324 cmd->value_djb2 = 0;
325
326 if( cmd->code & k_vg_msg_sized_type_bits ){
327 u32 bytes = vg_msg_sized_bytecount( cmd->code );
328 cmd->value = &msg->buf[ msg->cur ];
329 msg->cur += bytes;
330 }
331 else if( cmd->code == k_vg_msg_kvstring ){
332 cmd->value = vg_msg_rstr( msg, &cmd->value_djb2 );
333 }
334 else if( cmd->code == k_vg_msg_kvbin ){
335 u32 len;
336 vg_msg_rbuf( msg, (u8 *)(&len), 4 );
337 if( msg->error != k_vg_msg_error_OK ) return 0;
338 cmd->value = &msg->buf[ msg->cur ];
339 msg->cur += len;
340 }
341 else
342 msg->error = k_vg_msg_error_unhandled_cmd;
343
344 if( msg->cur > msg->max )
345 msg->error = k_vg_msg_error_overflow;
346 }
347 else
348 msg->error = k_vg_msg_error_unhandled_cmd;
349
350 if( msg->error != k_vg_msg_error_OK ) return 0;
351 else return 1;
352 }
353
354 /* move through the frame(and subframes) until we fall out of it */
355 static int vg_msg_skip_frame( vg_msg *msg ){
356 vg_msg_cmd cmd;
357
358 u32 depth = msg->depth-1;
359 while( vg_msg_next( msg, &cmd ) ){
360 if( msg->depth == depth ) return 1;
361 }
362 return 0;
363 }
364
365 /*
366 * A more friendly but slower interface
367 * -----------------------------------------------------------------------------
368 */
369
370 enum vg_msg_dir{
371 k_vg_msg_first=0, /* reset the frame pointer and find the first thing */
372 k_vg_msg_next=1 /* get next item in the stream, wont find behind cursor */
373 };
374
375 /* reset frame pointer and depth to the start of the frame set by seekframe */
376 static void vg_msg_framereset( vg_msg *frame ){
377 frame->cur = frame->rframe_cur;
378 frame->depth = frame->rframe_depth;
379 }
380
381 /* moves to a frame, and sets the base frame in msg if it finds it */
382 static int vg_msg_seekframe( vg_msg *msg, const char *name,
383 enum vg_msg_dir dir )
384 {
385 if( dir == k_vg_msg_first ) vg_msg_framereset( msg );
386
387 vg_msg_cmd cmd;
388 while( vg_msg_next( msg, &cmd ) ){
389 if( msg->depth < msg->rframe_depth ){
390 vg_msg_framereset(msg);
391 return 0;
392 }
393 if( msg->depth != msg->rframe_depth+1 ) continue;
394 if( cmd.code == k_vg_msg_frame ){
395 if( VG_STRDJB2_EQ( name, cmd.key, cmd.key_djb2 ) ){
396 msg->rframe_cur = msg->cur;
397 msg->rframe_depth = msg->depth;
398 return 1;
399 }
400 }
401 }
402
403 return 0;
404 }
405
406 /* move to the kv named key, doesnt matter what type */
407 static vg_msg_cmd vg_msg_seekkv( vg_msg *msg, const char *key,
408 enum vg_msg_dir dir )
409 {
410 if( dir == k_vg_msg_first ) vg_msg_framereset( msg );
411
412 vg_msg_cmd kv;
413 kv.code = k_vg_msg_end;
414 kv.key = "";
415 kv.key_djb2 = 0;
416 kv.value = NULL;
417 kv.value_djb2 = 0;
418
419 vg_msg_cmd cmd;
420 while( vg_msg_next( msg, &cmd ) ){
421 if( msg->depth < msg->rframe_depth ){
422 vg_msg_framereset(msg);
423 return kv;
424 }
425 if( msg->depth > msg->rframe_depth ) continue;
426 if( cmd.code > k_vg_msg_kv )
427 if( VG_STRDJB2_EQ( key, cmd.key, cmd.key_djb2 ) )
428 return cmd;
429 }
430
431 return kv;
432 }
433
434 /* helper for reading string kvs. returns NULL if not found */
435 static const char *vg_msg_seekkvstr( vg_msg *msg, const char *key,
436 enum vg_msg_dir dir )
437 {
438 vg_msg_cmd cmd = vg_msg_seekkv( msg, key, dir );
439 if( cmd.code == k_vg_msg_kvstring ) return cmd.value;
440 else return NULL;
441 }
442
443 static u64 vg_msg_read_as_u64( vg_msg_cmd *cmd ){
444 u8 sized_type = cmd->code & k_vg_msg_sized_type_bits;
445
446 u64 result = 0x00;
447 u8 *dst = (void *)(&result);
448 const u8 *src = cmd->value;
449
450 if( (sized_type == k_vg_msg_unsigned) || (sized_type == k_vg_msg_signed) ){
451 u32 bytes = vg_msg_sized_bytecount( cmd->code );
452 if( bytes > 8 ) bytes = 8;
453 for( u32 i=0; i<bytes; i ++ ) dst[i] = src[i];
454 }
455
456 return result;
457 }
458
459 static void
460 vg_msg_convert_num( vg_msg_cmd *cmd, u8 type, u32 count, void *result ){
461 u8 code = type | ((count-1)<<2);
462 if( code != cmd->code ) return;
463
464 const u8 *src = cmd->value;
465 u8 *dst = result;
466
467 u32 bytes = vg_msg_sized_bytecount( cmd->code );
468 for( u32 i=0; i<bytes; i ++ ) dst[i] = src[i];
469 }
470
471 static u32 vg_msg_seekkvu32( vg_msg *msg, const char *key, enum vg_msg_dir dir )
472 {
473 vg_msg_cmd cmd = vg_msg_seekkv( msg, key, dir );
474 return vg_msg_read_as_u64( &cmd );
475 }
476
477 /* debug the thing */
478 static void vg_msg_print( vg_msg *msg ){
479 vg_msg b = *msg;
480 b.cur = 0;
481
482 vg_msg_cmd cmd;
483 while( vg_msg_next( &b, &cmd ) ){
484 if( cmd.code == k_vg_msg_frame ){
485 for( u32 i=0; i<b.depth-1; i++ ) printf( " " );
486 printf( "'%s'[%u]{\n", cmd.key, cmd.key_djb2 );
487 }
488 else {
489 for( u32 i=0; i<b.depth; i++ ) printf( " " );
490
491 if( cmd.code == k_vg_msg_endframe )
492 printf( "}\n" );
493 else if( cmd.code == k_vg_msg_kvstring ){
494 printf( "'%s'[%u]: '%s'[%u]\n", cmd.key, cmd.key_djb2,
495 (char *)cmd.value, cmd.value_djb2 );
496 }
497 else
498 printf( "'%s'[%u]: <binary data>\n", cmd.key, cmd.key_djb2 );
499 }
500 }
501 }
502
503 #endif /* VG_MSG_H */