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
) {
152 /* C11 standard section 6.3.1.4 "Real floating and integer" says:
154 * 1 When a finite value of real floating type is converted to an integer
155 * type other than _Bool, the fractional part is discarded (i.e., the
156 * value is truncated toward zero). If the value of the integral part
157 * cannot be represented by the integer type, the behavior is undefined.
159 * So we must perform a range check that v <= UINT64_MAX, but we can't use
160 * UINT64_MAX + 1.0 because the standard continues:
162 * 2 When a value of integer type is converted to a real floating type, if
163 * the value being converted can be represented exactly in the new type,
164 * it is unchanged. If the value being converted is in the range of
165 * values that can be represented but cannot be represented exactly, the
166 * result is either the nearest higher or nearest lower representable
167 * value, chosen in an implementation-defined manner.
169 supremum
= -2.0 * INT64_MIN
; /* -2 * (- 2^63) == 2^64 */
173 /* Now we can convert, these two conversions cannot be UB */
175 return *absolute
== v
;
179 static void printRecursionLimit(CborStreamFunction stream
, void *out
) {
180 stream(out
, "<nesting too deep, recursion stopped>");
183 static CborError
hexDump(CborStreamFunction stream
, void *out
, const void *ptr
, size_t n
) {
184 const uint8_t *buffer
= (const uint8_t *)ptr
;
185 CborError err
= CborNoError
;
187 err
= stream(out
, "%02" PRIx8
, *buffer
++);
192 /* This function decodes buffer as UTF-8 and prints as escaped UTF-16.
193 * On UTF-8 decoding error, it returns CborErrorInvalidUtf8TextString */
194 static CborError
utf8EscapedDump(CborStreamFunction stream
, void *out
, const void *ptr
, size_t n
) {
195 const uint8_t *buffer
= (const uint8_t *)ptr
;
196 const uint8_t *const end
= buffer
+ n
;
197 CborError err
= CborNoError
;
199 while (buffer
< end
&& !err
) {
200 uint32_t uc
= get_utf8(&buffer
, end
);
202 return CborErrorInvalidUtf8TextString
;
205 /* single-byte UTF-8 */
206 unsigned char escaped
= (unsigned char)uc
;
207 if (uc
< 0x7f && uc
>= 0x20 && uc
!= '\\' && uc
!= '"') {
208 err
= stream(out
, "%c", (char)uc
);
212 /* print as an escape sequence */
235 err
= stream(out
, "\\%c", escaped
);
239 /* now print the sequence */
241 /* needs surrogate pairs */
242 err
= stream(out
, "\\u%04" PRIX32
"\\u%04" PRIX32
,
243 (uc
>> 10) + 0xd7c0, /* high surrogate */
244 (uc
% 0x0400) + 0xdc00);
247 /* no surrogate pair needed */
248 err
= stream(out
, "\\u%04" PRIX32
, uc
);
254 static const char *resolve_indicator(const uint8_t *ptr
, const uint8_t *end
, int flags
) {
255 static const char indicators
[8][3] = {
256 "_0", "_1", "_2", "_3",
257 "", "", "", /* these are not possible */
260 const char *no_indicator
= indicators
[5]; /* empty string */
261 uint8_t additional_information
;
262 uint8_t expected_information
;
267 return NULL
; /* CborErrorUnexpectedEOF */
269 additional_information
= (*ptr
& SmallValueMask
);
270 if (additional_information
< Value8Bit
)
273 /* determine whether to show anything */
274 if ((flags
& CborPrettyIndicateIndeterminateLength
) &&
275 additional_information
== IndefiniteLength
)
276 return indicators
[IndefiniteLength
- Value8Bit
];
277 if ((flags
& CborPrettyIndicateOverlongNumbers
) == 0)
280 err
= _cbor_value_extract_number(&ptr
, end
, &value
);
282 return NULL
; /* CborErrorUnexpectedEOF */
284 expected_information
= Value8Bit
- 1;
285 if (value
>= Value8Bit
)
286 ++expected_information
;
288 ++expected_information
;
290 ++expected_information
;
291 if (value
> 0xffffffffU
)
292 ++expected_information
;
293 return expected_information
== additional_information
?
295 indicators
[additional_information
- Value8Bit
];
298 static const char *get_indicator(const CborValue
*it
, int flags
) {
299 return resolve_indicator(it
->ptr
, it
->parser
->end
, flags
);
302 static CborError
value_to_pretty(CborStreamFunction stream
, void *out
, CborValue
*it
, int flags
, int recursionsLeft
);
303 static CborError
container_to_pretty(CborStreamFunction stream
, void *out
, CborValue
*it
, CborType containerType
,
304 int flags
, int recursionsLeft
) {
305 const char *comma
= "";
306 CborError err
= CborNoError
;
308 if (!recursionsLeft
) {
309 printRecursionLimit(stream
, out
);
310 return err
; /* do allow the dumping to continue */
313 while (!cbor_value_at_end(it
) && !err
) {
314 err
= stream(out
, "%s", comma
);
318 err
= value_to_pretty(stream
, out
, it
, flags
, recursionsLeft
);
320 if (containerType
== CborArrayType
)
323 /* map: that was the key, so get the value */
325 err
= stream(out
, ": ");
327 err
= value_to_pretty(stream
, out
, it
, flags
, recursionsLeft
);
332 static CborError
value_to_pretty(CborStreamFunction stream
, void *out
, CborValue
*it
, int flags
, int recursionsLeft
) {
333 CborError err
= CborNoError
;
334 CborType type
= cbor_value_get_type(it
);
340 const char *indicator
= get_indicator(it
, flags
);
341 const char *space
= *indicator
? " " : indicator
;
343 err
= stream(out
, "%c%s%s", type
== CborArrayType
? '[' : '{', indicator
, space
);
347 err
= cbor_value_enter_container(it
, &recursed
);
349 it
->ptr
= recursed
.ptr
;
350 return err
; /* parse error */
352 err
= container_to_pretty(stream
, out
, &recursed
, type
, flags
, recursionsLeft
- 1);
354 it
->ptr
= recursed
.ptr
;
355 return err
; /* parse error */
357 err
= cbor_value_leave_container(it
, &recursed
);
359 return err
; /* parse error */
361 return stream(out
, type
== CborArrayType
? "]" : "}");
364 case CborIntegerType
: {
366 cbor_value_get_raw_integer(it
, &val
); /* can't fail */
368 if (cbor_value_is_unsigned_integer(it
)) {
369 err
= stream(out
, "%" PRIu64
, val
);
371 /* CBOR stores the negative number X as -1 - X
372 * (that is, -1 is stored as 0, -2 as 1 and so forth) */
373 if (++val
) { /* unsigned overflow may happen */
374 err
= stream(out
, "-%" PRIu64
, val
);
377 * 0xffff`ffff`ffff`ffff + 1 =
378 * 0x1`0000`0000`0000`0000 = 18446744073709551616 (2^64) */
379 err
= stream(out
, "-18446744073709551616");
383 err
= stream(out
, "%s", get_indicator(it
, flags
));
387 case CborByteStringType
:
388 case CborTextStringType
: {
391 bool showingFragments
= (flags
& CborPrettyShowStringFragments
) && !cbor_value_is_length_known(it
);
392 const char *separator
= "";
395 const char *indicator
= NULL
;
397 if (type
== CborTextStringType
) {
398 close
= open
[0] = '"';
402 if (showingFragments
) {
403 err
= stream(out
, "(_ ");
405 err
= _cbor_value_prepare_string_iteration(it
);
407 err
= stream(out
, "%s", open
);
411 if (showingFragments
|| indicator
== NULL
) {
412 /* any iteration, except the second for a non-chunked string */
413 indicator
= resolve_indicator(it
->ptr
, it
->parser
->end
, flags
);
416 err
= _cbor_value_get_string_chunk(it
, &ptr
, &n
, it
);
420 if (!err
&& showingFragments
)
421 err
= stream(out
, "%s%s", separator
, open
);
423 err
= (type
== CborByteStringType
?
424 hexDump(stream
, out
, ptr
, n
) :
425 utf8EscapedDump(stream
, out
, ptr
, n
));
426 if (!err
&& showingFragments
) {
427 err
= stream(out
, "%c%s", close
, indicator
);
433 if (showingFragments
)
434 err
= stream(out
, ")");
436 err
= stream(out
, "%c%s", close
, indicator
);
443 cbor_value_get_tag(it
, &tag
); /* can't fail */
444 err
= stream(out
, "%" PRIu64
"%s(", tag
, get_indicator(it
, flags
));
446 err
= cbor_value_advance_fixed(it
);
447 if (!err
&& recursionsLeft
)
448 err
= value_to_pretty(stream
, out
, it
, flags
, recursionsLeft
- 1);
450 printRecursionLimit(stream
, out
);
452 err
= stream(out
, ")");
456 case CborSimpleType
: {
457 /* simple types can't fail and can't have overlong encoding */
459 cbor_value_get_simple_type(it
, &simple_type
);
460 err
= stream(out
, "simple(%" PRIu8
")", simple_type
);
465 err
= stream(out
, "null");
468 case CborUndefinedType
:
469 err
= stream(out
, "undefined");
472 case CborBooleanType
: {
474 cbor_value_get_boolean(it
, &val
); /* can't fail */
475 err
= stream(out
, val
? "true" : "false");
479 #ifndef CBOR_NO_FLOATING_POINT
480 case CborDoubleType
: {
489 cbor_value_get_float(it
, &f
);
491 suffix
= (flags
& CborPrettyNumericEncodingIndicators
) ? "_2" : "f";
494 case CborHalfFloatType
:
495 #ifndef CBOR_NO_HALF_FLOAT_TYPE
496 cbor_value_get_half_float(it
, &f16
);
497 val
= decode_half(f16
);
498 suffix
= (flags
& CborPrettyNumericEncodingIndicators
) ? "_1" : "f16";
501 err
= CborErrorUnsupportedType
;
505 cbor_value_get_double(it
, &val
);
509 if ((flags
& CborPrettyNumericEncodingIndicators
) == 0) {
511 if (r
== FP_NAN
|| r
== FP_INFINITE
)
515 if (convertToUint64(val
, &ival
)) {
516 /* this double value fits in a 64-bit integer, so show it as such
517 * (followed by a floating point suffix, to disambiguate) */
518 err
= stream(out
, "%s%" PRIu64
".%s", val
< 0 ? "-" : "", ival
, suffix
);
520 /* this number is definitely not a 64-bit integer */
521 err
= stream(out
, "%." DBL_DECIMAL_DIG_STR
"g%s", val
, suffix
);
528 case CborHalfFloatType
:
529 err
= CborErrorUnsupportedType
;
531 #endif /* !CBOR_NO_FLOATING_POINT */
533 case CborInvalidType
:
534 err
= stream(out
, "invalid");
537 return CborErrorUnknownType
;
541 err
= cbor_value_advance_fixed(it
);
546 * Converts the current CBOR type pointed by \a value to its textual
547 * representation and writes it to the stream by calling the \a streamFunction.
548 * If an error occurs, this function returns an error code similar to
551 * The textual representation can be controlled by the \a flags parameter (see
552 * \ref CborPrettyFlags for more information).
554 * If no error ocurred, this function advances \a value to the next element.
555 * Often, concatenating the text representation of multiple elements can be
556 * done by appending a comma to the output stream in between calls to this
559 * The \a streamFunction function will be called with the \a token value as the
560 * first parameter and a printf-style format string as the second, with a variable
561 * number of further parameters.
563 * \sa cbor_value_to_pretty(), cbor_value_to_json_advance()
565 CborError
cbor_value_to_pretty_stream(CborStreamFunction streamFunction
, void *token
, CborValue
*value
, int flags
) {
566 return value_to_pretty(streamFunction
, token
, value
, flags
, CBOR_PARSER_MAX_RECURSIONS
);