1 /****************************************************************************
3 ** Copyright (C) 2018 Intel Corporation
5 ** Permission is hereby granted, free of charge, to any person obtaining a copy
6 ** of this software and associated documentation files (the "Software"), to deal
7 ** in the Software without restriction, including without limitation the rights
8 ** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 ** copies of the Software, and to permit persons to whom the Software is
10 ** furnished to do so, subject to the following conditions:
12 ** The above copyright notice and this permission notice shall be included in
13 ** all copies or substantial portions of the Software.
15 ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 ** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 ** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 ** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 ** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 ****************************************************************************/
26 #define _DEFAULT_SOURCE 1
27 #ifndef __STDC_LIMIT_MACROS
28 # define __STDC_LIMIT_MACROS 1
32 #include "cborinternal_p.h"
33 #include "compilersupport_p.h"
40 * \defgroup CborPretty Converting CBOR to text
41 * \brief Group of functions used to convert CBOR to text form.
43 * This group contains two functions that can be used to convert a \ref
44 * CborValue object to a text representation. This module attempts to follow
45 * the recommendations from RFC 7049 section 6 "Diagnostic Notation", though it
46 * has a few differences. They are noted below.
48 * TinyCBOR does not provide a way to convert from the text representation back
49 * to encoded form. To produce a text form meant to be parsed, CborToJson is
50 * recommended instead.
52 * Either of the functions in this section will attempt to convert exactly one
53 * CborValue object to text. Those functions may return any error documented
54 * for the functions for CborParsing. In addition, if the C standard library
55 * stream functions return with error, the text conversion will return with
58 * These functions also perform UTF-8 validation in CBOR text strings. If they
59 * encounter a sequence of bytes that is not permitted in UTF-8, they will return
60 * CborErrorInvalidUtf8TextString. That includes encoding of surrogate points
63 * \warning The output type produced by these functions is not guaranteed to
64 * remain stable. A future update of TinyCBOR may produce different output for
65 * the same input and parsers may be unable to handle it.
67 * \sa CborParsing, CborToJson, cbor_parser_init()
71 * \addtogroup CborPretty
73 * <h2 class="groupheader">Text format</h2>
75 * As described in RFC 7049 section 6 "Diagnostic Notation", the format is
76 * largely borrowed from JSON, but modified to suit CBOR's different data
77 * types. TinyCBOR makes further modifications to distinguish different, but
80 * CBOR values are currently encoded as follows:
81 * \par Integrals (unsigned and negative)
82 * Base-10 (decimal) text representation of the value
84 * <tt>"h'"</tt> followed by the Base16 (hex) representation of the binary data, followed by an ending quote (')
86 * C-style escaped string in quotes, with C11/C++11 escaping of Unicode codepoints above U+007F.
88 * Tag value, with the tagged value in parentheses. No special encoding of the tagged value is performed.
90 * <tt>"simple(nn)"</tt> where \c nn is the simple value
97 * \par Floating point:
98 * If NaN or infinite, the actual words \c NaN or \c infinite.
99 * Otherwise, the decimal representation with as many digits as necessary to ensure no loss of information.
100 * By default, float values are suffixed by "f" and half-float values suffixed by "f16" (doubles have no suffix).
101 * If the CborPrettyNumericEncodingIndicators flag is active, the values instead are encoded following the
102 * Section 6 recommended encoding indicators: float values are suffixed with "_2" and half-float with "_1".
103 * A decimal point is always present.
105 * Comma-separated list of elements, enclosed in square brackets ("[" and "]").
107 * Comma-separated list of key-value pairs, with the key and value separated
108 * by a colon (":"), enclosed in curly braces ("{" and "}").
110 * The CborPrettyFlags enumerator contains flags to control some aspects of the
112 * \par String fragmentation
113 * When the CborPrettyShowStringFragments option is active, text and byte
114 * strings that are transmitted in fragments are shown instead inside
115 * parentheses ("(" and ")") with no preceding number and each fragment is
116 * displayed individually. If a tag precedes the string, then the output
117 * will contain a double set of parentheses. If the option is not active,
118 * the fragments are merged together and the display will not show any
119 * difference from a string transmitted with determinate length.
120 * \par Encoding indicators
121 * Numbers and lengths in CBOR can be encoded in multiple representations.
122 * If the CborPrettyIndicateOverlongNumbers option is active, numbers
123 * and lengths that are transmitted in a longer encoding than necessary
124 * will be indicated, by appending an underscore ("_") to either the
125 * number or the opening bracket or brace, followed by a number
126 * indicating the CBOR additional information: 0 for 1 byte, 1 for 2
127 * bytes, 2 for 4 bytes and 3 for 8 bytes.
128 * If the CborPrettyIndicateIndeterminateLength option is active, maps,
129 * arrays and strings encoded with indeterminate length will be marked by
130 * an underscore after the opening bracket or brace or the string (if not
131 * showing fragments), without a number after it.
135 * \enum CborPrettyFlags
136 * The CborPrettyFlags enum contains flags that control the conversion of CBOR to text format.
138 * \value CborPrettyNumericEncodingIndicators Use numeric encoding indicators instead of textual for float and half-float.
139 * \value CborPrettyTextualEncodingIndicators Use textual encoding indicators for float ("f") and half-float ("f16").
140 * \value CborPrettyIndicateIndeterminateLength (default) Indicate when a map or array has indeterminate length.
141 * \value CborPrettyIndicateOverlongNumbers Indicate when a number or length was encoded with more bytes than needed.
142 * \value CborPrettyShowStringFragments If the byte or text string is transmitted in chunks, show each individually.
143 * \value CborPrettyMergeStringFragment Merge all chunked byte or text strings and display them in a single entry.
144 * \value CborPrettyDefaultFlags Default conversion flags.
147 #ifndef CBOR_NO_FLOATING_POINT
148 static inline bool convertToUint64(double v
, uint64_t *absolute
)
153 /* C11 standard section 6.3.1.4 "Real floating and integer" says:
155 * 1 When a finite value of real floating type is converted to an integer
156 * type other than _Bool, the fractional part is discarded (i.e., the
157 * value is truncated toward zero). If the value of the integral part
158 * cannot be represented by the integer type, the behavior is undefined.
160 * So we must perform a range check that v <= UINT64_MAX, but we can't use
161 * UINT64_MAX + 1.0 because the standard continues:
163 * 2 When a value of integer type is converted to a real floating type, if
164 * the value being converted can be represented exactly in the new type,
165 * it is unchanged. If the value being converted is in the range of
166 * values that can be represented but cannot be represented exactly, the
167 * result is either the nearest higher or nearest lower representable
168 * value, chosen in an implementation-defined manner.
170 supremum
= -2.0 * INT64_MIN
; /* -2 * (- 2^63) == 2^64 */
174 /* Now we can convert, these two conversions cannot be UB */
176 return *absolute
== v
;
180 static void printRecursionLimit(CborStreamFunction stream
, void *out
)
182 stream(out
, "<nesting too deep, recursion stopped>");
185 static CborError
hexDump(CborStreamFunction stream
, void *out
, const void *ptr
, size_t n
)
187 const uint8_t *buffer
= (const uint8_t *)ptr
;
188 CborError err
= CborNoError
;
190 err
= stream(out
, "%02" PRIx8
, *buffer
++);
195 /* This function decodes buffer as UTF-8 and prints as escaped UTF-16.
196 * On UTF-8 decoding error, it returns CborErrorInvalidUtf8TextString */
197 static CborError
utf8EscapedDump(CborStreamFunction stream
, void *out
, const void *ptr
, size_t n
)
199 const uint8_t *buffer
= (const uint8_t *)ptr
;
200 const uint8_t * const end
= buffer
+ n
;
201 CborError err
= CborNoError
;
203 while (buffer
< end
&& !err
) {
204 uint32_t uc
= get_utf8(&buffer
, end
);
206 return CborErrorInvalidUtf8TextString
;
209 /* single-byte UTF-8 */
210 unsigned char escaped
= (unsigned char)uc
;
211 if (uc
< 0x7f && uc
>= 0x20 && uc
!= '\\' && uc
!= '"') {
212 err
= stream(out
, "%c", (char)uc
);
216 /* print as an escape sequence */
239 err
= stream(out
, "\\%c", escaped
);
243 /* now print the sequence */
245 /* needs surrogate pairs */
246 err
= stream(out
, "\\u%04" PRIX32
"\\u%04" PRIX32
,
247 (uc
>> 10) + 0xd7c0, /* high surrogate */
248 (uc
% 0x0400) + 0xdc00);
251 /* no surrogate pair needed */
252 err
= stream(out
, "\\u%04" PRIX32
, uc
);
258 static const char *resolve_indicator(const uint8_t *ptr
, const uint8_t *end
, int flags
)
260 static const char indicators
[8][3] = {
261 "_0", "_1", "_2", "_3",
262 "", "", "", /* these are not possible */
265 const char *no_indicator
= indicators
[5]; /* empty string */
266 uint8_t additional_information
;
267 uint8_t expected_information
;
272 return NULL
; /* CborErrorUnexpectedEOF */
274 additional_information
= (*ptr
& SmallValueMask
);
275 if (additional_information
< Value8Bit
)
278 /* determine whether to show anything */
279 if ((flags
& CborPrettyIndicateIndeterminateLength
) &&
280 additional_information
== IndefiniteLength
)
281 return indicators
[IndefiniteLength
- Value8Bit
];
282 if ((flags
& CborPrettyIndicateOverlongNumbers
) == 0)
285 err
= _cbor_value_extract_number(&ptr
, end
, &value
);
287 return NULL
; /* CborErrorUnexpectedEOF */
289 expected_information
= Value8Bit
- 1;
290 if (value
>= Value8Bit
)
291 ++expected_information
;
293 ++expected_information
;
295 ++expected_information
;
296 if (value
> 0xffffffffU
)
297 ++expected_information
;
298 return expected_information
== additional_information
?
300 indicators
[additional_information
- Value8Bit
];
303 static const char *get_indicator(const CborValue
*it
, int flags
)
305 return resolve_indicator(it
->ptr
, it
->parser
->end
, flags
);
308 static CborError
value_to_pretty(CborStreamFunction stream
, void *out
, CborValue
*it
, int flags
, int recursionsLeft
);
309 static CborError
container_to_pretty(CborStreamFunction stream
, void *out
, CborValue
*it
, CborType containerType
,
310 int flags
, int recursionsLeft
)
312 const char *comma
= "";
313 CborError err
= CborNoError
;
315 if (!recursionsLeft
) {
316 printRecursionLimit(stream
, out
);
317 return err
; /* do allow the dumping to continue */
320 while (!cbor_value_at_end(it
) && !err
) {
321 err
= stream(out
, "%s", comma
);
325 err
= value_to_pretty(stream
, out
, it
, flags
, recursionsLeft
);
327 if (containerType
== CborArrayType
)
330 /* map: that was the key, so get the value */
332 err
= stream(out
, ": ");
334 err
= value_to_pretty(stream
, out
, it
, flags
, recursionsLeft
);
339 static CborError
value_to_pretty(CborStreamFunction stream
, void *out
, CborValue
*it
, int flags
, int recursionsLeft
)
341 CborError err
= CborNoError
;
342 CborType type
= cbor_value_get_type(it
);
348 const char *indicator
= get_indicator(it
, flags
);
349 const char *space
= *indicator
? " " : indicator
;
351 err
= stream(out
, "%c%s%s", type
== CborArrayType
? '[' : '{', indicator
, space
);
355 err
= cbor_value_enter_container(it
, &recursed
);
357 it
->ptr
= recursed
.ptr
;
358 return err
; /* parse error */
360 err
= container_to_pretty(stream
, out
, &recursed
, type
, flags
, recursionsLeft
- 1);
362 it
->ptr
= recursed
.ptr
;
363 return err
; /* parse error */
365 err
= cbor_value_leave_container(it
, &recursed
);
367 return err
; /* parse error */
369 return stream(out
, type
== CborArrayType
? "]" : "}");
372 case CborIntegerType
: {
374 cbor_value_get_raw_integer(it
, &val
); /* can't fail */
376 if (cbor_value_is_unsigned_integer(it
)) {
377 err
= stream(out
, "%" PRIu64
, val
);
379 /* CBOR stores the negative number X as -1 - X
380 * (that is, -1 is stored as 0, -2 as 1 and so forth) */
381 if (++val
) { /* unsigned overflow may happen */
382 err
= stream(out
, "-%" PRIu64
, val
);
385 * 0xffff`ffff`ffff`ffff + 1 =
386 * 0x1`0000`0000`0000`0000 = 18446744073709551616 (2^64) */
387 err
= stream(out
, "-18446744073709551616");
391 err
= stream(out
, "%s", get_indicator(it
, flags
));
395 case CborByteStringType
:
396 case CborTextStringType
: {
399 bool showingFragments
= (flags
& CborPrettyShowStringFragments
) && !cbor_value_is_length_known(it
);
400 const char *separator
= "";
403 const char *indicator
= NULL
;
405 if (type
== CborTextStringType
) {
406 close
= open
[0] = '"';
410 if (showingFragments
) {
411 err
= stream(out
, "(_ ");
413 err
= _cbor_value_prepare_string_iteration(it
);
415 err
= stream(out
, "%s", open
);
419 if (showingFragments
|| indicator
== NULL
) {
420 /* any iteration, except the second for a non-chunked string */
421 indicator
= resolve_indicator(it
->ptr
, it
->parser
->end
, flags
);
424 err
= _cbor_value_get_string_chunk(it
, &ptr
, &n
, it
);
428 if (!err
&& showingFragments
)
429 err
= stream(out
, "%s%s", separator
, open
);
431 err
= (type
== CborByteStringType
?
432 hexDump(stream
, out
, ptr
, n
) :
433 utf8EscapedDump(stream
, out
, ptr
, n
));
434 if (!err
&& showingFragments
) {
435 err
= stream(out
, "%c%s", close
, indicator
);
441 if (showingFragments
)
442 err
= stream(out
, ")");
444 err
= stream(out
, "%c%s", close
, indicator
);
451 cbor_value_get_tag(it
, &tag
); /* can't fail */
452 err
= stream(out
, "%" PRIu64
"%s(", tag
, get_indicator(it
, flags
));
454 err
= cbor_value_advance_fixed(it
);
455 if (!err
&& recursionsLeft
)
456 err
= value_to_pretty(stream
, out
, it
, flags
, recursionsLeft
- 1);
458 printRecursionLimit(stream
, out
);
460 err
= stream(out
, ")");
464 case CborSimpleType
: {
465 /* simple types can't fail and can't have overlong encoding */
467 cbor_value_get_simple_type(it
, &simple_type
);
468 err
= stream(out
, "simple(%" PRIu8
")", simple_type
);
473 err
= stream(out
, "null");
476 case CborUndefinedType
:
477 err
= stream(out
, "undefined");
480 case CborBooleanType
: {
482 cbor_value_get_boolean(it
, &val
); /* can't fail */
483 err
= stream(out
, val
? "true" : "false");
487 #ifndef CBOR_NO_FLOATING_POINT
488 case CborDoubleType
: {
497 cbor_value_get_float(it
, &f
);
499 suffix
= flags
& CborPrettyNumericEncodingIndicators
? "_2" : "f";
502 case CborHalfFloatType
:
503 #ifndef CBOR_NO_HALF_FLOAT_TYPE
504 cbor_value_get_half_float(it
, &f16
);
505 val
= decode_half(f16
);
506 suffix
= flags
& CborPrettyNumericEncodingIndicators
? "_1" : "f16";
509 err
= CborErrorUnsupportedType
;
513 cbor_value_get_double(it
, &val
);
517 if ((flags
& CborPrettyNumericEncodingIndicators
) == 0) {
519 if (r
== FP_NAN
|| r
== FP_INFINITE
)
523 if (convertToUint64(val
, &ival
)) {
524 /* this double value fits in a 64-bit integer, so show it as such
525 * (followed by a floating point suffix, to disambiguate) */
526 err
= stream(out
, "%s%" PRIu64
".%s", val
< 0 ? "-" : "", ival
, suffix
);
528 /* this number is definitely not a 64-bit integer */
529 err
= stream(out
, "%." DBL_DECIMAL_DIG_STR
"g%s", val
, suffix
);
536 case CborHalfFloatType
:
537 err
= CborErrorUnsupportedType
;
539 #endif /* !CBOR_NO_FLOATING_POINT */
541 case CborInvalidType
:
542 err
= stream(out
, "invalid");
545 return CborErrorUnknownType
;
549 err
= cbor_value_advance_fixed(it
);
554 * Converts the current CBOR type pointed by \a value to its textual
555 * representation and writes it to the stream by calling the \a streamFunction.
556 * If an error occurs, this function returns an error code similar to
559 * The textual representation can be controlled by the \a flags parameter (see
560 * \ref CborPrettyFlags for more information).
562 * If no error ocurred, this function advances \a value to the next element.
563 * Often, concatenating the text representation of multiple elements can be
564 * done by appending a comma to the output stream in between calls to this
567 * The \a streamFunction function will be called with the \a token value as the
568 * first parameter and a printf-style format string as the second, with a variable
569 * number of further parameters.
571 * \sa cbor_value_to_pretty(), cbor_value_to_json_advance()
573 CborError
cbor_value_to_pretty_stream(CborStreamFunction streamFunction
, void *token
, CborValue
*value
, int flags
)
575 return value_to_pretty(streamFunction
, token
, value
, flags
, CBOR_PARSER_MAX_RECURSIONS
);