steamworks
[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 if( msg->error != k_vg_msg_error_OK ) return 0;
318
319 #ifdef VG_MSG_V1_SUPPORT
320 /* |sized| |count-1| |shift|
321 * 0 1 0 0 0 1 0 0 0x44 (1 byte, float[2]. So, never used anyway)
322 * converts to
323 * 1 0 0 0 0 0 1 0 0x82
324 */
325 if( cmd->code == 0x44 ) cmd->code = 0x82;
326 #endif
327 cmd->key_djb2 = 0;
328 if( msg->error != k_vg_msg_error_OK ) return 0;
329
330 if( cmd->code == k_vg_msg_frame ){
331 cmd->key = vg_msg_rstr( msg, &cmd->key_djb2 );
332 msg->cur.depth ++;
333 }
334 else if( cmd->code == k_vg_msg_endframe ){
335 if( !msg->cur.depth ){
336 msg->error = k_vg_msg_error_unbalanced;
337 return 0;
338 }
339 msg->cur.depth --;
340 }
341 else if( cmd->code >= k_vg_msg_kv ){
342 cmd->key = vg_msg_rstr( msg, &cmd->key_djb2 );
343 cmd->value_djb2 = 0;
344
345 if( cmd->code & k_vg_msg_type_base_bits ){
346 u32 bytes = vg_msg_cmd_bytecount( cmd->code );
347 cmd->value = &msg->buf[ msg->cur.co ];
348 msg->cur.co += bytes;
349 }
350 else if( cmd->code == k_vg_msg_kvstring ){
351 cmd->value = vg_msg_rstr( msg, &cmd->value_djb2 );
352 }
353 else if( cmd->code == k_vg_msg_kvbin ){
354 vg_msg_rbuf( msg, (u8 *)(&cmd->len), 4 );
355 if( msg->error != k_vg_msg_error_OK )
356 return 0;
357 cmd->value = &msg->buf[ msg->cur.co ];
358 msg->cur.co += cmd->len;
359 }
360 else
361 msg->error = k_vg_msg_error_unhandled_cmd;
362
363 if( msg->cur.co > msg->max )
364 msg->error = k_vg_msg_error_overflow;
365 }
366 else
367 msg->error = k_vg_msg_error_unhandled_cmd;
368
369 if( msg->error != k_vg_msg_error_OK )
370 return 0;
371 else
372 return 1;
373 }
374
375 /* move through the frame(and subframes) until we fall out of it */
376 static int vg_msg_skip_frame( vg_msg *msg ){
377 vg_msg_cmd cmd;
378
379 u32 start_depth = msg->cur.depth;
380 while( vg_msg_next( msg, &cmd ) )
381 if( msg->cur.depth < start_depth )
382 return 1;
383 return 0;
384 }
385
386 /*
387 * A more friendly but slower interface
388 * -----------------------------------------------------------------------------
389 */
390
391 /* moves to a frame,
392 * returns 0 if drops out of scope or ends.
393 */
394 static int vg_msg_seekframe( vg_msg *msg, const char *name ){
395 vg_msg_cursor orig = msg->cur;
396 vg_msg_cmd cmd;
397 while( vg_msg_next( msg, &cmd ) ){
398 if( msg->cur.depth < orig.depth ){
399 msg->cur = orig;
400 return 0;
401 }
402 if( msg->cur.depth != orig.depth+1 )
403 continue;
404 if( cmd.code == k_vg_msg_frame )
405 if( !name || VG_STRDJB2_EQ( name, cmd.key, cmd.key_djb2 ) )
406 return 1;
407 }
408
409 msg->cur = orig;
410 return 0;
411 }
412
413 /*
414 * Convert any type integral type to u64
415 */
416 static u64 vg_msg_cast_to_u64( const void *src, u8 src_base, u8 src_size ){
417 if( src_base == k_vg_msg_float ){
418 if( src_size == 4 ) return (u64)(*((f32*)src));
419 else if( src_size == 8 ) return (u64)(*((f64*)src));
420 else return 0;
421 }
422 else {
423 u64 a = 0;
424 memcpy( &a, src, src_size );
425 return a;
426 }
427 }
428
429 /*
430 * Convert any integral type to i64
431 */
432 static i64 vg_msg_cast_to_i64( const void *src, u8 src_base, u8 src_size ){
433 if( src_base == k_vg_msg_float ){
434 if( src_size == 4 ) return (i64)(*((f32*)src));
435 else if( src_size == 8 ) return (i64)(*((f64*)src));
436 else return 0;
437 }
438 else {
439 u64 a = 0;
440 memcpy( &a, src, src_size );
441
442 if( (src_base == k_vg_msg_signed) && (src_size != 8) ){
443 /* extend sign bit */
444 u64 sign_bit = 0x1llu << ((src_size*8)-1);
445 if( a & sign_bit )
446 a |= (0xffffffffffffffffllu << (64-__builtin_clzll( a )));
447 }
448
449 return *((i64*)&a);
450 }
451 }
452
453 /*
454 * Convert any integral type to f64
455 */
456 static f64 vg_msg_cast_to_f64( const void *src, u8 src_base, u8 src_size ){
457 if( src_base == k_vg_msg_float ){
458 if( src_size == 4 ) return (f64)(*((f32*)src));
459 else if( src_size == 8 ) return *((f64*)src);
460 else return 0.0;
461 }
462 else
463 return (f64)vg_msg_cast_to_i64( src, src_base, src_size );
464 }
465
466 /*
467 * Convert any full integral type code to another
468 * Passing in non-integral codes is undefined
469 */
470 static void vg_msg_cast( const void *src, u8 src_code, void *dst, u8 dst_code ){
471 if( src_code == dst_code ){
472 memcpy( dst, src, vg_msg_cmd_bytecount( src_code ) );
473 }
474 else {
475 u32 src_n = vg_msg_cmd_array_count( src_code ),
476 dst_n = vg_msg_cmd_array_count( dst_code ),
477 src_s = vg_msg_cmd_type_size( src_code ),
478 dst_s = vg_msg_cmd_type_size( dst_code ),
479 src_b = src_code & k_vg_msg_type_base_bits,
480 dst_b = dst_code & k_vg_msg_type_base_bits;
481
482 memset( dst, 0, dst_s * dst_n );
483
484 for( u32 i=0; i<VG_MIN(src_n,dst_n); i ++ ){
485 const void *ptr_s = src + i*src_s;
486 void *ptr_d = dst + i*dst_s;
487
488 if( dst_b == k_vg_msg_unsigned ){
489 u64 a = vg_msg_cast_to_u64( ptr_s, src_b, src_s );
490 if ( dst_s == 1 ) *((u8 *)ptr_d) = (u8 )a;
491 else if( dst_s == 2 ) *((u16*)ptr_d) = (u16)a;
492 else if( dst_s == 4 ) *((u32*)ptr_d) = (u32)a;
493 else if( dst_s == 8 ) *((u64*)ptr_d) = a;
494 }
495 else if( dst_b == k_vg_msg_signed ){
496 i64 a = vg_msg_cast_to_i64( ptr_s, src_b, src_s );
497 if ( dst_s == 1 ) *((i8 *)ptr_d) = (i8 )a;
498 else if( dst_s == 2 ) *((i16*)ptr_d) = (i16)a;
499 else if( dst_s == 4 ) *((i32*)ptr_d) = (i32)a;
500 else if( dst_s == 8 ) *((i64*)ptr_d) = a;
501 }
502 else {
503 f64 a = vg_msg_cast_to_f64( ptr_s, src_b, src_s );
504 if ( dst_s == 4 ) *((f32*)ptr_d) = (f32)a;
505 else if( dst_s == 8 ) *((f64*)ptr_d) = a;
506 }
507 }
508 }
509 }
510
511 /*
512 * search in current level from cursor, to find kv cmd
513 * returns 0 if not found
514 * Cursor does not move
515 * If found, the kv command is written to cmd
516 */
517 static int vg_msg_getkvcmd( vg_msg *msg, const char *key, vg_msg_cmd *cmd ){
518 vg_msg_cursor orig = msg->cur;
519 while( vg_msg_next( msg, cmd ) ){
520 if( msg->cur.depth < orig.depth ){
521 msg->cur = orig;
522 return 0;
523 }
524 if( msg->cur.depth > orig.depth )
525 continue;
526 if( cmd->code > k_vg_msg_kv ){
527 if( VG_STRDJB2_EQ( key, cmd->key, cmd->key_djb2 ) ){
528 msg->cur = orig;
529 return 1;
530 }
531 }
532 }
533
534 msg->cur = orig;
535 return 0;
536 }
537
538 /*
539 * Read a integral KV out to dst, and perform conversion if needed
540 * dst is always defined, if its not found its set to 0
541 */
542 static int vg_msg_getkvintg( vg_msg *msg, const char *key, u8 type, void *dst ){
543 vg_msg_cmd cmd;
544 if( vg_msg_getkvcmd( msg, key, &cmd ) ){
545 vg_msg_cast( cmd.value, cmd.code, dst, type );
546 return 1;
547 }
548 else {
549 memset( dst, 0, vg_msg_cmd_bytecount(type) );
550 return 0;
551 }
552 }
553
554 /* helper for reading string kvs. returns NULL if not found */
555 static const char *vg_msg_getkvstr( vg_msg *msg, const char *key ){
556 vg_msg_cmd cmd;
557 if( vg_msg_getkvcmd( msg, key, &cmd ) )
558 return cmd.value;
559 else
560 return NULL;
561 }
562
563 #define _GET_KV_INTG_HELPER( TYPE ) \
564 static TYPE vg_msg_getkv##TYPE( vg_msg *msg, const char *key, \
565 TYPE default_value ){ \
566 vg_msg_cmd cmd; \
567 if( vg_msg_getkvcmd( msg, key, &cmd ) ){ \
568 TYPE v; \
569 vg_msg_cast( cmd.value, cmd.code, &v, k_vg_msg_##TYPE ); \
570 return v; \
571 } \
572 else \
573 return default_value; \
574 }
575
576 #define _GET_KV_VEC_HELPER( TYPE ) \
577 static int vg_msg_getkv##TYPE( vg_msg *msg, const char *key, \
578 TYPE v, \
579 TYPE default_value ){ \
580 vg_msg_cmd cmd; \
581 if( vg_msg_getkvcmd( msg, key, &cmd ) ){ \
582 vg_msg_cast( cmd.value, cmd.code, v, k_vg_msg_##TYPE ); \
583 return 1; \
584 } \
585 else \
586 if ( default_value ) \
587 vg_msg_cast( default_value, k_vg_msg_##TYPE, v, k_vg_msg_##TYPE ); \
588 return 0; \
589 }
590
591 _GET_KV_INTG_HELPER( u8 )
592 _GET_KV_INTG_HELPER( u16 )
593 _GET_KV_INTG_HELPER( u32 )
594 _GET_KV_INTG_HELPER( u64 )
595 _GET_KV_INTG_HELPER( i8 )
596 _GET_KV_INTG_HELPER( i16 )
597 _GET_KV_INTG_HELPER( i32 )
598 _GET_KV_INTG_HELPER( i64 )
599 _GET_KV_INTG_HELPER( f32 )
600 _GET_KV_INTG_HELPER( f64 )
601 _GET_KV_VEC_HELPER( v2f )
602 _GET_KV_VEC_HELPER( v3f )
603 _GET_KV_VEC_HELPER( v4f )
604
605 /* debug the thing */
606 static void vg_msg_print( vg_msg *msg, u32 len ){
607 if( msg->error != k_vg_msg_error_OK ){
608 printf( "Message contains errors\n" );
609 return;
610 }
611
612 vg_msg b;
613 vg_msg_init( &b, msg->buf, len );
614
615 vg_msg_cmd cmd;
616 while( vg_msg_next( &b, &cmd ) ){
617 if( cmd.code == k_vg_msg_frame ){
618 for( u32 i=0; i<b.cur.depth-1; i++ ) printf( " " );
619 printf( "'%s'{\n", cmd.key );
620 }
621 else {
622 for( u32 i=0; i<b.cur.depth; i++ ) printf( " " );
623
624 if( cmd.code == k_vg_msg_endframe )
625 printf( "}\n" );
626 else if( cmd.code == k_vg_msg_kvstring )
627 printf( "'%s': '%s'\n", cmd.key, (char *)cmd.value );
628 else if( cmd.code == k_vg_msg_kvbin )
629 printf( "'%s': <binary data> (%u bytes)\n", cmd.key, cmd.len );
630 else {
631 u32 base = cmd.code & k_vg_msg_type_base_bits,
632 count = vg_msg_cmd_array_count( cmd.code ),
633 size = vg_msg_cmd_type_size( cmd.code );
634
635 printf( "'%s': ", cmd.key );
636
637 if( count > 1 ) printf( "'{' " );
638
639 for( u32 i=0; i<count; i++ ){
640 const void *p = cmd.value + size*i;
641
642 if( base == k_vg_msg_unsigned ){
643 printf(
644 #ifdef _WIN32
645 "%llu"
646 #else
647 "%lu"
648 #endif
649 , vg_msg_cast_to_u64( p, base, size ) );
650 }
651 else if( base == k_vg_msg_signed ){
652 printf(
653 #ifdef _WIN32
654 "%lld"
655 #else
656 "%ld"
657 #endif
658 , vg_msg_cast_to_i64( p, base, size ) );
659 }
660 else
661 printf( "%f", vg_msg_cast_to_f64( p, base, size ));
662
663 if( i+1<count ) printf(", ");
664 }
665
666 if( count > 1 ) printf( "'}'" );
667 printf( "\n" );
668 }
669 }
670 }
671 }
672
673 #endif /* VG_MSG_H */