105105 * A dot is always present.
106106 * \par Arrays:
107107 * Comma-separated list of elements, enclosed in square brackets ("[" and "]").
108- * If the array length is indeterminate, an underscore ("_") appears immediately after the opening bracket.
109108 * \par Maps:
110109 * Comma-separated list of key-value pairs, with the key and value separated
111110 * by a colon (":"), enclosed in curly braces ("{" and "}").
112- * If the map length is indeterminate, an underscore ("_") appears immediately after the opening brace.
111+ *
112+ * The CborPrettyFlags enumerator contains flags to control some aspects of the
113+ * encoding:
114+ * \par String fragmentation
115+ * When the CborPrettyShowStringFragments option is active, text and byte
116+ * strings that are transmitted in fragments are shown instead inside
117+ * parentheses ("(" and ")") with no preceding number and each fragment is
118+ * displayed individually. If a tag precedes the string, then the output
119+ * will contain a double set of parentheses. If the option is not active,
120+ * the fragments are merged together and the display will not show any
121+ * difference from a string transmitted with determinate length.
122+ * \par Encoding indicators
123+ * Numbers and lengths in CBOR can be encoded in multiple representations.
124+ * If the CborPrettyIndicateOverlongNumbers option is active, numbers
125+ * and lengths that are transmitted in a longer encoding than necessary
126+ * will be indicated, by appending an underscore ("_") to either the
127+ * number or the opening bracket or brace, followed by a number
128+ * indicating the CBOR additional information: 0 for 1 byte, 1 for 2
129+ * bytes, 2 for 4 bytes and 3 for 8 bytes.
130+ * If the CborPrettyIndicateIndetermineLength option is active, maps,
131+ * arrays and strings encoded with indeterminate length will be marked by
132+ * an underscore after the opening bracket or brace or the string (if not
133+ * showing fragments), without a number after it.
113134 */
114135
115136/**
118139 *
119140 * \value CborPrettyNumericEncodingIndicators Use numeric encoding indicators instead of textual for float and half-float.
120141 * \value CborPrettyTextualEncodingIndicators Use textual encoding indicators for float ("f") and half-float ("f16").
142+ * \value CborPrettyIndicateIndetermineLength Indicate when a map or array has indeterminate length.
143+ * \value CborPrettyIndicateOverlongNumbers Indicate when a number or length was encoded with more bytes than needed.
121144 * \value CborPrettyShowStringFragments If the byte or text string is transmitted in chunks, show each individually.
122145 * \value CborPrettyMergeStringFragment Merge all chunked byte or text strings and display them in a single entry.
123146 * \value CborPrettyDefaultFlags Default conversion flags.
@@ -253,6 +276,56 @@ static CborError utf8EscapedDump(FILE *out, const void *ptr, size_t n)
253276 return CborNoError ;
254277}
255278
279+ static const char * resolve_indicator (const uint8_t * ptr , const uint8_t * end , int flags )
280+ {
281+ static const char indicators [8 ][3 ] = {
282+ "_0" , "_1" , "_2" , "_3" ,
283+ "" , "" , "" , /* these are not possible */
284+ "_"
285+ };
286+ const char * no_indicator = indicators [5 ]; /* empty string */
287+ uint8_t additional_information ;
288+ uint8_t expected_information ;
289+ uint64_t value ;
290+ CborError err ;
291+
292+ if (ptr == end )
293+ return NULL ; /* CborErrorUnexpectedEOF */
294+
295+ additional_information = (* ptr & SmallValueMask );
296+ if (additional_information < Value8Bit )
297+ return no_indicator ;
298+
299+ /* determine whether to show anything */
300+ if ((flags & CborPrettyIndicateIndetermineLength ) &&
301+ additional_information == IndefiniteLength )
302+ return indicators [IndefiniteLength - Value8Bit ];
303+ if ((flags & CborPrettyIndicateOverlongNumbers ) == 0 )
304+ return no_indicator ;
305+
306+ err = _cbor_value_extract_number (& ptr , end , & value );
307+ if (err )
308+ return NULL ; /* CborErrorUnexpectedEOF */
309+
310+ expected_information = Value8Bit - 1 ;
311+ if (value >= Value8Bit )
312+ ++ expected_information ;
313+ if (value > 0xffU )
314+ ++ expected_information ;
315+ if (value > 0xffffU )
316+ ++ expected_information ;
317+ if (value > 0xffffffffU )
318+ ++ expected_information ;
319+ return expected_information == additional_information ?
320+ no_indicator :
321+ indicators [additional_information - Value8Bit ];
322+ }
323+
324+ static const char * get_indicator (const CborValue * it , int flags )
325+ {
326+ return resolve_indicator (it -> ptr , it -> parser -> end , flags );
327+ }
328+
256329static CborError value_to_pretty (FILE * out , CborValue * it , int flags );
257330static CborError container_to_pretty (FILE * out , CborValue * it , CborType containerType , int flags )
258331{
@@ -288,13 +361,11 @@ static CborError value_to_pretty(FILE *out, CborValue *it, int flags)
288361 case CborMapType : {
289362 /* recursive type */
290363 CborValue recursed ;
364+ const char * indicator = get_indicator (it , flags );
365+ const char * space = * indicator ? " " : indicator ;
291366
292- if (fprintf (out , type == CborArrayType ? "[" : "{" ) < 0 )
367+ if (fprintf (out , "%c%s%s" , type == CborArrayType ? '[' : '{' , indicator , space ) < 0 )
293368 return CborErrorIO ;
294- if (!cbor_value_is_length_known (it )) {
295- if (fprintf (out , "_ " ) < 0 )
296- return CborErrorIO ;
297- }
298369
299370 err = cbor_value_enter_container (it , & recursed );
300371 if (err ) {
@@ -336,6 +407,8 @@ static CborError value_to_pretty(FILE *out, CborValue *it, int flags)
336407 return CborErrorIO ;
337408 }
338409 }
410+ if (fprintf (out , "%s" , get_indicator (it , flags )) < 0 )
411+ return CborErrorIO ;
339412 break ;
340413 }
341414
@@ -347,16 +420,30 @@ static CborError value_to_pretty(FILE *out, CborValue *it, int flags)
347420 const char * separator = "" ;
348421 char close = '\'' ;
349422 char open [3 ] = "h'" ;
423+ const char * indicator = NULL ;
350424
351425 if (type == CborTextStringType ) {
352426 close = open [0 ] = '"' ;
353427 open [1 ] = '\0' ;
354428 }
355429
356- if (fputs (showingFragments ? "(_ " : open , out ) < 0 )
357- return CborErrorIO ;
430+ if (showingFragments ) {
431+ if (fputs ("(_ " , out ) < 0 )
432+ return CborErrorIO ;
433+ err = _cbor_value_prepare_string_iteration (it );
434+ if (err )
435+ return err ;
436+ } else {
437+ if (fputs (open , out ) < 0 )
438+ return CborErrorIO ;
439+ }
358440
359441 while (1 ) {
442+ if (showingFragments || indicator == NULL ) {
443+ /* any iteration, except the second for a non-chunked string */
444+ indicator = resolve_indicator (it -> ptr , it -> parser -> end , flags );
445+ }
446+
360447 err = _cbor_value_get_string_chunk (it , & ptr , & n , it );
361448 if (err )
362449 return err ;
@@ -369,21 +456,23 @@ static CborError value_to_pretty(FILE *out, CborValue *it, int flags)
369456 if (err )
370457 return err ;
371458 if (showingFragments ) {
372- if (fputc ( close , out ) < 0 )
459+ if (fprintf ( out , "%c%s" , close , indicator ) < 0 )
373460 return CborErrorIO ;
374461 separator = ", " ;
375462 }
376463 }
377464
378- if (fputc (showingFragments ? ')' : close , out ) < 0 )
465+ if (showingFragments && fputc (')' , out ) < 0 )
466+ return CborErrorIO ;
467+ if (!showingFragments && fprintf (out , "%c%s" , close , indicator ) < 0 )
379468 return CborErrorIO ;
380469 return CborNoError ;
381470 }
382471
383472 case CborTagType : {
384473 CborTag tag ;
385474 cbor_value_get_tag (it , & tag ); /* can't fail */
386- if (fprintf (out , "%" PRIu64 "(" , tag ) < 0 )
475+ if (fprintf (out , "%" PRIu64 "%s (" , tag , get_indicator ( it , flags ) ) < 0 )
387476 return CborErrorIO ;
388477 err = cbor_value_advance_fixed (it );
389478 if (err )
@@ -397,8 +486,9 @@ static CborError value_to_pretty(FILE *out, CborValue *it, int flags)
397486 }
398487
399488 case CborSimpleType : {
489+ /* simple types can't fail and can't have overlong encoding */
400490 uint8_t simple_type ;
401- cbor_value_get_simple_type (it , & simple_type ); /* can't fail */
491+ cbor_value_get_simple_type (it , & simple_type );
402492 if (fprintf (out , "simple(%" PRIu8 ")" , simple_type ) < 0 )
403493 return CborErrorIO ;
404494 break ;
0 commit comments