i32r, q_dist fix
[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 *
28 * vg_msg_wkvstr( &data, "kvstr", "someinfo" );
29 * vg_msg_wkvu32( &data, "kvint", 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_wkvu32( &data, "capacity", 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;
61 * vg_msg_init( &data, data_buf, data_len );
62 *
63 *
64 * Reading data
65 * -----------------------------------------------------------------------------
66 *
67 * if( vg_msg_seekframe( &msg, "rows" ) ){
68 * while( vg_msg_seekframe( &msg, NULL ) ){
69 * vg_warn( "%s\n", vg_msg_readkvstr( &msg, "yedo" ) );
70 * vg_msg_skip_frame( &msg );
71 * }
72 * }
73 *
74 * Reading back the stream linearly
75 * -----------------------------------------------------------------------------
76 *
77 * vg_msg_cmd cmd;
78 * while( vg_msg_next( &data, &cmd ) ){
79 * if( cmd.code == k_vg_msg_frame ) printf( "{" );
80 * else if( cmd.code == k_vg_msg_endframe ) printf( "}" );
81 * esle if( cmd.code == k_vg_msg_kvstring )
82 * printf( "string: %s\n", cmd.value._buf );
83 * }
84 */
85
86 enum vg_msg_code{
87 /* low types */
88 k_vg_msg_end = 0,
89 k_vg_msg_frame = 1,
90 k_vg_msg_endframe = 2,
91 k_vg_msg_kv = 10,
92 k_vg_msg_kvstring = 11,
93 k_vg_msg_kvbin = 12,
94
95 /* variable sized types */
96 k_vg_msg_float = 0x40,
97 k_vg_msg_unsigned = 0x80,
98 k_vg_msg_signed = 0xC0,
99
100 /* masks */
101 k_vg_msg_array_count_bits = 0x3C,
102 k_vg_msg_type_size_bits = 0x03,
103 k_vg_msg_type_base_bits = 0xC0,
104 k_vg_msg_type_bits = k_vg_msg_type_base_bits|k_vg_msg_type_size_bits,
105
106 /* sizes */
107 k_vg_msg_8b = 0x00,
108 k_vg_msg_16b = 0x01,
109 k_vg_msg_32b = 0x02,
110 k_vg_msg_64b = 0x03,
111
112 /* common types */
113 k_vg_msg_u8 = k_vg_msg_unsigned|k_vg_msg_8b,
114 k_vg_msg_u16 = k_vg_msg_unsigned|k_vg_msg_16b,
115 k_vg_msg_u32 = k_vg_msg_unsigned|k_vg_msg_32b,
116 k_vg_msg_u64 = k_vg_msg_unsigned|k_vg_msg_64b,
117 k_vg_msg_i8 = k_vg_msg_signed |k_vg_msg_8b,
118 k_vg_msg_i16 = k_vg_msg_signed |k_vg_msg_16b,
119 k_vg_msg_i32 = k_vg_msg_signed |k_vg_msg_32b,
120 k_vg_msg_i64 = k_vg_msg_signed |k_vg_msg_64b,
121 k_vg_msg_f32 = k_vg_msg_float |k_vg_msg_32b,
122 k_vg_msg_f64 = k_vg_msg_float |k_vg_msg_64b,
123
124 k_vg_msg_v2f = k_vg_msg_float |k_vg_msg_32b | (1<<2),
125 k_vg_msg_v3f = k_vg_msg_float |k_vg_msg_32b | (2<<2),
126 k_vg_msg_v4f = k_vg_msg_float |k_vg_msg_32b | (3<<2)
127 };
128
129 typedef struct vg_msg vg_msg;
130 typedef struct vg_msg_cmd vg_msg_cmd;
131 typedef struct vg_msg_cursor vg_msg_cursor;
132 struct vg_msg{
133 u32 max;
134 u8 *buf;
135
136 /* reading */
137 struct vg_msg_cursor {
138 u32 co, depth;
139 }
140 cur;
141
142 enum vg_msg_error{
143 k_vg_msg_error_OK,
144 k_vg_msg_error_unbalanced,
145 k_vg_msg_error_overflow,
146 k_vg_msg_error_unhandled_cmd
147 }
148 error;
149 };
150
151 struct vg_msg_cmd{
152 u8 code;
153
154 const char *key;
155 u32 key_djb2;
156
157 const void *value;
158 u32 value_djb2;
159
160 u32 len; /* set if binary type */
161 };
162
163 /* write a buffer from msg, rang checked. */
164 static void vg_msg_wbuf( vg_msg *msg, u8 *buf, u32 len ){
165 if( msg->error != k_vg_msg_error_OK ) return;
166 if( msg->cur.co+len > msg->max ){
167 msg->error = k_vg_msg_error_overflow;
168 return;
169 }
170
171 for( u32 i=0; i<len; i++ ){
172 msg->buf[ msg->cur.co ++ ] = buf[i];
173 }
174 }
175
176 /* read a buffer from msg, rang checked. */
177 static void vg_msg_rbuf( vg_msg *msg, u8 *buf, u32 len ){
178 if( msg->error != k_vg_msg_error_OK ) return;
179 if( msg->cur.co+len > msg->max ){
180 msg->error = k_vg_msg_error_overflow;
181 return;
182 }
183
184 for( u32 i=0; i<len; i++ ){
185 buf[i] = msg->buf[ msg->cur.co ++ ];
186 }
187 }
188
189 /* write null terminated string to stream */
190 static void vg_msg_wstr( vg_msg *msg, const char *str ){
191 if( msg->error != k_vg_msg_error_OK ) return;
192 for( u32 i=0;; i++ ){
193 vg_msg_wbuf( msg, (u8[]){ str[i] }, 1 );
194 if( !str[i] ) break;
195 }
196 }
197
198 /* read null terminated string, range check and generate hash (djb2) */
199 static const char *vg_msg_rstr( vg_msg *msg, u32 *djb2 ){
200 if( msg->error != k_vg_msg_error_OK ) return 0;
201
202 u32 hash = 5381, c;
203 const char *str = (void *)(&msg->buf[ msg->cur.co ]);
204
205 while( (c = msg->buf[ msg->cur.co ++ ]) ){
206 if( msg->cur.co >= msg->max ){
207 msg->error = k_vg_msg_error_overflow;
208 return 0;
209 }
210 hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
211 }
212
213 *djb2 = hash;
214 return str;
215 }
216
217 /* begin a new frame in message stream */
218 static void vg_msg_frame( vg_msg *msg, const char *name ){
219 if( msg->error != k_vg_msg_error_OK ) return;
220
221 msg->cur.depth ++;
222 vg_msg_wbuf( msg, (u8[]){ k_vg_msg_frame }, 1 );
223 vg_msg_wstr( msg, name );
224 }
225
226 /* end frame in message stream */
227 static void vg_msg_end_frame( vg_msg *msg ){
228 if( msg->error != k_vg_msg_error_OK ) return;
229 if( !msg->cur.depth ){
230 msg->error = k_vg_msg_error_unbalanced;
231 return;
232 }
233 msg->cur.depth --;
234 vg_msg_wbuf( msg, (u8[]){ k_vg_msg_endframe }, 1 );
235 }
236
237 /* write a KV string to stream */
238 static void vg_msg_wkvstr( vg_msg *msg, const char *key, const char *value ){
239 vg_msg_wbuf( msg, (u8[]){ k_vg_msg_kvstring }, 1 );
240 vg_msg_wstr( msg, key );
241 vg_msg_wstr( msg, value );
242 }
243
244 /* write a binary block to stream */
245 static void vg_msg_wkvbin( vg_msg *msg, const char *key, u8 *bin, u32 len ){
246 vg_msg_wbuf( msg, (u8[]){ k_vg_msg_kvbin }, 1 );
247 vg_msg_wstr( msg, key );
248 vg_msg_wbuf( msg, (u8 *)(&len), 4 );
249 vg_msg_wbuf( msg, bin, len );
250 }
251
252 static u32 vg_msg_cmd_array_count( u8 code ){
253 return ((code & k_vg_msg_array_count_bits)>>2) + 1;
254 }
255
256 static u32 vg_msg_cmd_type_size( u8 code ){
257 return 0x1 << (code & k_vg_msg_type_size_bits);
258 }
259
260 /* get the byte count of a sized code */
261 static u32 vg_msg_cmd_bytecount( u8 code ){
262 return vg_msg_cmd_array_count( code ) * vg_msg_cmd_type_size( code );
263 }
264
265 /* write a sized type */
266 static void vg_msg_wkvnum( vg_msg *msg, const char *key,
267 u8 type, u8 count, void *data ){
268 u8 code = type | ((count-1)<<2);
269
270 vg_msg_wbuf( msg, &code, 1 );
271 vg_msg_wstr( msg, key );
272 vg_msg_wbuf( msg, data, vg_msg_cmd_bytecount(code) );
273 }
274
275 #define _WRITE_KV_INTG_HELPER( TYPE ) \
276 static void vg_msg_wkv##TYPE( vg_msg *msg, const char *key, TYPE v ){ \
277 vg_msg_wkvnum( msg, key, k_vg_msg_##TYPE, 1, &v ); \
278 }
279
280 #define _WRITE_KV_VEC_HELPER( TYPE ) \
281 static void vg_msg_wkv##TYPE( vg_msg *msg, const char *key, TYPE v ){ \
282 vg_msg_wkvnum( msg, key, k_vg_msg_##TYPE, 1, v ); \
283 }
284
285 _WRITE_KV_INTG_HELPER( u8 )
286 _WRITE_KV_INTG_HELPER( u16 )
287 _WRITE_KV_INTG_HELPER( u32 )
288 _WRITE_KV_INTG_HELPER( u64 )
289 _WRITE_KV_INTG_HELPER( i8 )
290 _WRITE_KV_INTG_HELPER( i16 )
291 _WRITE_KV_INTG_HELPER( i32 )
292 _WRITE_KV_INTG_HELPER( i64 )
293 _WRITE_KV_INTG_HELPER( f32 )
294 _WRITE_KV_INTG_HELPER( f64 )
295 _WRITE_KV_VEC_HELPER( v2f )
296 _WRITE_KV_VEC_HELPER( v3f )
297 _WRITE_KV_VEC_HELPER( v4f )
298
299 static void vg_msg_init( vg_msg *msg, u8 *buffer, u32 len ){
300 msg->buf = buffer;
301 msg->cur.co = 0;
302 msg->cur.depth = 0;
303 msg->error = k_vg_msg_error_OK;
304 msg->max = len;
305 }
306
307 /*
308 * The stream reading interface
309 * -----------------------------------------------------------------------------
310 */
311
312 /* move the cursor through the next message. it will always read in the value or
313 * create an error if it runs of the end of the stream. every possible command
314 * must be handled in this function */
315 static int vg_msg_next( vg_msg *msg, vg_msg_cmd *cmd ){
316 vg_msg_rbuf( msg, &cmd->code, 1 );
317
318 #ifdef VG_MSG_V1_SUPPORT
319 /* |sized| |count-1| |shift|
320 * 0 1 0 0 0 1 0 0 0x44 (1 byte, float[2]. So, never used anyway)
321 * converts to
322 * 1 0 0 0 0 0 1 0 0x82
323 */
324 if( cmd->code == 0x44 ) cmd->code = 0x82;
325 #endif
326 cmd->key_djb2 = 0;
327 if( msg->error != k_vg_msg_error_OK ) return 0;
328
329 if( cmd->code == k_vg_msg_frame ){
330 cmd->key = vg_msg_rstr( msg, &cmd->key_djb2 );
331 msg->cur.depth ++;
332 }
333 else if( cmd->code == k_vg_msg_endframe ){
334 if( !msg->cur.depth ){
335 msg->error = k_vg_msg_error_unbalanced;
336 return 0;
337 }
338 msg->cur.depth --;
339 }
340 else if( cmd->code >= k_vg_msg_kv ){
341 cmd->key = vg_msg_rstr( msg, &cmd->key_djb2 );
342 cmd->value_djb2 = 0;
343
344 if( cmd->code & k_vg_msg_type_base_bits ){
345 u32 bytes = vg_msg_cmd_bytecount( cmd->code );
346 cmd->value = &msg->buf[ msg->cur.co ];
347 msg->cur.co += bytes;
348 }
349 else if( cmd->code == k_vg_msg_kvstring ){
350 cmd->value = vg_msg_rstr( msg, &cmd->value_djb2 );
351 }
352 else if( cmd->code == k_vg_msg_kvbin ){
353 vg_msg_rbuf( msg, (u8 *)(&cmd->len), 4 );
354 if( msg->error != k_vg_msg_error_OK )
355 return 0;
356 cmd->value = &msg->buf[ msg->cur.co ];
357 msg->cur.co += cmd->len;
358 }
359 else
360 msg->error = k_vg_msg_error_unhandled_cmd;
361
362 if( msg->cur.co > msg->max )
363 msg->error = k_vg_msg_error_overflow;
364 }
365 else
366 msg->error = k_vg_msg_error_unhandled_cmd;
367
368 if( msg->error != k_vg_msg_error_OK )
369 return 0;
370 else
371 return 1;
372 }
373
374 /* move through the frame(and subframes) until we fall out of it */
375 static int vg_msg_skip_frame( vg_msg *msg ){
376 vg_msg_cmd cmd;
377
378 u32 start_depth = msg->cur.depth;
379 while( vg_msg_next( msg, &cmd ) )
380 if( msg->cur.depth < start_depth )
381 return 1;
382 return 0;
383 }
384
385 /*
386 * A more friendly but slower interface
387 * -----------------------------------------------------------------------------
388 */
389
390 /* moves to a frame,
391 * returns 0 if drops out of scope or ends.
392 */
393 static int vg_msg_seekframe( vg_msg *msg, const char *name ){
394 vg_msg_cursor orig = msg->cur;
395 vg_msg_cmd cmd;
396 while( vg_msg_next( msg, &cmd ) ){
397 if( msg->cur.depth < orig.depth ){
398 msg->cur = orig;
399 return 0;
400 }
401 if( msg->cur.depth != orig.depth+1 )
402 continue;
403 if( cmd.code == k_vg_msg_frame )
404 if( !name || VG_STRDJB2_EQ( name, cmd.key, cmd.key_djb2 ) )
405 return 1;
406 }
407
408 msg->cur = orig;
409 return 0;
410 }
411
412 /*
413 * Convert any type integral type to u64
414 */
415 static u64 vg_msg_cast_to_u64( const void *src, u8 src_base, u8 src_size ){
416 if( src_base == k_vg_msg_float ){
417 if( src_size == 4 ) return (u64)(*((f32*)src));
418 else if( src_size == 8 ) return (u64)(*((f64*)src));
419 else return 0;
420 }
421 else {
422 u64 a = 0;
423 memcpy( &a, src, src_size );
424 return a;
425 }
426 }
427
428 /*
429 * Convert any integral type to i64
430 */
431 static i64 vg_msg_cast_to_i64( const void *src, u8 src_base, u8 src_size ){
432 if( src_base == k_vg_msg_float ){
433 if( src_size == 4 ) return (i64)(*((f32*)src));
434 else if( src_size == 8 ) return (i64)(*((f64*)src));
435 else return 0;
436 }
437 else {
438 u64 a = 0;
439 memcpy( &a, src, src_size );
440
441 if( (src_base == k_vg_msg_signed) && (src_size != 8) ){
442 /* extend sign bit */
443 u64 sign_bit = 0x1llu << ((src_size*8)-1);
444 if( a & sign_bit )
445 a |= (0xffffffffffffffffllu << (64-__builtin_clzll( a )));
446 }
447
448 return *((i64*)&a);
449 }
450 }
451
452 /*
453 * Convert any integral type to f64
454 */
455 static f64 vg_msg_cast_to_f64( const void *src, u8 src_base, u8 src_size ){
456 if( src_base == k_vg_msg_float ){
457 if( src_size == 4 ) return (f64)(*((f32*)src));
458 else if( src_size == 8 ) return *((f64*)src);
459 else return 0.0;
460 }
461 else
462 return (f64)vg_msg_cast_to_i64( src, src_base, src_size );
463 }
464
465 /*
466 * Convert any full integral type code to another
467 * Passing in non-integral codes is undefined
468 */
469 static void vg_msg_cast( const void *src, u8 src_code, void *dst, u8 dst_code ){
470 if( src_code == dst_code ){
471 memcpy( dst, src, vg_msg_cmd_bytecount( src_code ) );
472 }
473 else {
474 u32 src_n = vg_msg_cmd_array_count( src_code ),
475 dst_n = vg_msg_cmd_array_count( dst_code ),
476 src_s = vg_msg_cmd_type_size( src_code ),
477 dst_s = vg_msg_cmd_type_size( dst_code ),
478 src_b = src_code & k_vg_msg_type_base_bits,
479 dst_b = dst_code & k_vg_msg_type_base_bits;
480
481 memset( dst, 0, dst_s * dst_n );
482
483 for( u32 i=0; i<VG_MIN(src_n,dst_n); i ++ ){
484 const void *ptr_s = src + i*src_s;
485 void *ptr_d = dst + i*dst_s;
486
487 if( dst_b == k_vg_msg_unsigned ){
488 u64 a = vg_msg_cast_to_u64( ptr_s, src_b, src_s );
489 if ( dst_s == 1 ) *((u8 *)ptr_d) = (u8 )a;
490 else if( dst_s == 2 ) *((u16*)ptr_d) = (u16)a;
491 else if( dst_s == 4 ) *((u32*)ptr_d) = (u32)a;
492 else if( dst_s == 8 ) *((u64*)ptr_d) = a;
493 }
494 else if( dst_b == k_vg_msg_signed ){
495 i64 a = vg_msg_cast_to_i64( ptr_s, src_b, src_s );
496 if ( dst_s == 1 ) *((i8 *)ptr_d) = (i8 )a;
497 else if( dst_s == 2 ) *((i16*)ptr_d) = (i16)a;
498 else if( dst_s == 4 ) *((i32*)ptr_d) = (i32)a;
499 else if( dst_s == 8 ) *((i64*)ptr_d) = a;
500 }
501 else {
502 f64 a = vg_msg_cast_to_f64( ptr_s, src_b, src_s );
503 if ( dst_s == 4 ) *((f32*)ptr_d) = (f32)a;
504 else if( dst_s == 8 ) *((f64*)ptr_d) = a;
505 }
506 }
507 }
508 }
509
510 /*
511 * search in current level from cursor, to find kv cmd
512 * returns 0 if not found
513 * Cursor does not move
514 * If found, the kv command is written to cmd
515 */
516 static int vg_msg_getkvcmd( vg_msg *msg, const char *key, vg_msg_cmd *cmd ){
517 vg_msg_cursor orig = msg->cur;
518 while( vg_msg_next( msg, cmd ) ){
519 if( msg->cur.depth < orig.depth ){
520 msg->cur = orig;
521 return 0;
522 }
523 if( msg->cur.depth > orig.depth )
524 continue;
525 if( cmd->code > k_vg_msg_kv ){
526 if( VG_STRDJB2_EQ( key, cmd->key, cmd->key_djb2 ) ){
527 msg->cur = orig;
528 return 1;
529 }
530 }
531 }
532
533 msg->cur = orig;
534 return 0;
535 }
536
537 /*
538 * Read a integral KV out to dst, and perform conversion if needed
539 * dst is always defined, if its not found its set to 0
540 */
541 static int vg_msg_getkvintg( vg_msg *msg, const char *key, u8 type, void *dst ){
542 vg_msg_cmd cmd;
543 if( vg_msg_getkvcmd( msg, key, &cmd ) ){
544 vg_msg_cast( cmd.value, cmd.code, dst, type );
545 return 1;
546 }
547 else {
548 memset( dst, 0, vg_msg_cmd_bytecount(type) );
549 return 0;
550 }
551 }
552
553 /* helper for reading string kvs. returns NULL if not found */
554 static const char *vg_msg_getkvstr( vg_msg *msg, const char *key ){
555 vg_msg_cmd cmd;
556 if( vg_msg_getkvcmd( msg, key, &cmd ) )
557 return cmd.value;
558 else
559 return NULL;
560 }
561
562 #define _GET_KV_INTG_HELPER( TYPE ) \
563 static TYPE vg_msg_getkv##TYPE( vg_msg *msg, const char *key, \
564 TYPE default_value ){ \
565 vg_msg_cmd cmd; \
566 if( vg_msg_getkvcmd( msg, key, &cmd ) ){ \
567 TYPE v; \
568 vg_msg_cast( cmd.value, cmd.code, &v, k_vg_msg_##TYPE ); \
569 return v; \
570 } \
571 else \
572 return default_value; \
573 }
574
575 #define _GET_KV_VEC_HELPER( TYPE ) \
576 static int vg_msg_getkv##TYPE( vg_msg *msg, const char *key, \
577 TYPE v, \
578 TYPE default_value ){ \
579 vg_msg_cmd cmd; \
580 if( vg_msg_getkvcmd( msg, key, &cmd ) ){ \
581 vg_msg_cast( cmd.value, cmd.code, v, k_vg_msg_##TYPE ); \
582 return 1; \
583 } \
584 else \
585 if ( default_value ) \
586 vg_msg_cast( default_value, k_vg_msg_##TYPE, v, k_vg_msg_##TYPE ); \
587 return 0; \
588 }
589
590 _GET_KV_INTG_HELPER( u8 )
591 _GET_KV_INTG_HELPER( u16 )
592 _GET_KV_INTG_HELPER( u32 )
593 _GET_KV_INTG_HELPER( u64 )
594 _GET_KV_INTG_HELPER( i8 )
595 _GET_KV_INTG_HELPER( i16 )
596 _GET_KV_INTG_HELPER( i32 )
597 _GET_KV_INTG_HELPER( i64 )
598 _GET_KV_INTG_HELPER( f32 )
599 _GET_KV_INTG_HELPER( f64 )
600 _GET_KV_VEC_HELPER( v2f )
601 _GET_KV_VEC_HELPER( v3f )
602 _GET_KV_VEC_HELPER( v4f )
603
604 /* debug the thing */
605 static void vg_msg_print( vg_msg *msg, u32 len ){
606 if( msg->error != k_vg_msg_error_OK ){
607 printf( "Message contains errors\n" );
608 return;
609 }
610
611 vg_msg b;
612 vg_msg_init( &b, msg->buf, len );
613
614 vg_msg_cmd cmd;
615 while( vg_msg_next( &b, &cmd ) ){
616 if( cmd.code == k_vg_msg_frame ){
617 for( u32 i=0; i<b.cur.depth-1; i++ ) printf( " " );
618 printf( "'%s'{\n", cmd.key );
619 }
620 else {
621 for( u32 i=0; i<b.cur.depth; i++ ) printf( " " );
622
623 if( cmd.code == k_vg_msg_endframe )
624 printf( "}\n" );
625 else if( cmd.code == k_vg_msg_kvstring )
626 printf( "'%s': '%s'\n", cmd.key, (char *)cmd.value );
627 else if( cmd.code == k_vg_msg_kvbin )
628 printf( "'%s': <binary data> (%u bytes)\n", cmd.key, cmd.len );
629 else {
630 u32 base = cmd.code & k_vg_msg_type_base_bits,
631 count = vg_msg_cmd_array_count( cmd.code ),
632 size = vg_msg_cmd_type_size( cmd.code );
633
634 printf( "'%s': ", cmd.key );
635
636 if( count > 1 ) printf( "'{' " );
637
638 for( u32 i=0; i<count; i++ ){
639 const void *p = cmd.value + size*i;
640
641 if( base == k_vg_msg_unsigned )
642 printf( "%lu", vg_msg_cast_to_u64( p, base, size ) );
643 else if( base == k_vg_msg_signed )
644 printf( "%ld", vg_msg_cast_to_i64( p, base, size ) );
645 else
646 printf( "%f", vg_msg_cast_to_f64( p, base, size ));
647
648 if( i+1<count ) printf(", ");
649 }
650
651 if( count > 1 ) printf( "'}'" );
652 printf( "\n" );
653 }
654 }
655 }
656 }
657
658 #endif /* VG_MSG_H */