1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 Intel Corporation
4 **
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:
11 **
12 ** The above copyright notice and this permission notice shall be included in
13 ** all copies or substantial portions of the Software.
14 **
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
21 ** THE SOFTWARE.
22 **
23 ****************************************************************************/
24
25 #define _BSD_SOURCE 1
26 #define _DEFAULT_SOURCE 1
27 #define _GNU_SOURCE 1
28 #define _POSIX_C_SOURCE 200809L
29 #ifndef __STDC_LIMIT_MACROS
30 # define __STDC_LIMIT_MACROS 1
31 #endif
32
33 #include "tinycbor/cbor.h"
34 #include "tinycbor/cborjson.h"
35 #include "tinycbor/compilersupport_p.h"
36 #include "tinycbor/math_support_p.h"
37
38 #include <float.h>
39 #include <inttypes.h>
40 #ifndef CBOR_NO_FLOATING_POINT
41 #include <math.h>
42 #endif
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <string.h>
46
47
48 /**
49 * \defgroup CborToJson Converting CBOR to JSON
50 * \brief Group of functions used to convert CBOR to JSON.
51 *
52 * This group contains two functions that are can be used to convert one
53 * CborValue object to an equivalent JSON representation. This module attempts
54 * to follow the recommendations from RFC 7049 section 4.1 "Converting from
55 * CBOR to JSON", though it has a few differences. They are noted below.
56 *
57 * These functions produce a "minified" JSON output, with no spacing,
58 * indentation or line breaks. If those are necessary, they need to be applied
59 * in a post-processing phase.
60 *
61 * Note that JSON cannot support all CBOR types with fidelity, so the
62 * conversion is usually lossy. For that reason, TinyCBOR supports adding a set
63 * of metadata JSON values that can be used by a JSON-to-CBOR converter to
64 * restore the original data types.
65 *
66 * The TinyCBOR library does not provide a way to convert from JSON
67 * representation back to encoded form. However, it provides a tool called
68 * \c json2cbor which can be used for that purpose. That tool supports the
69 * metadata format that these functions may produce.
70 *
71 * Either of the functions in this section will attempt to convert exactly one
72 * CborValue object to JSON. Those functions may return any error documented
73 * for the functions for CborParsing. In addition, if the C standard library
74 * stream functions return with error, the text conversion will return with
75 * error CborErrorIO.
76 *
77 * These functions also perform UTF-8 validation in CBOR text strings. If they
78 * encounter a sequence of bytes that not permitted in UTF-8, they will return
79 * CborErrorInvalidUtf8TextString. That includes encoding of surrogate points
80 * in UTF-8.
81 *
82 * \warning The metadata produced by these functions is not guaranteed to
83 * remain stable. A future update of TinyCBOR may produce different output for
84 * the same input and parsers may be unable to handle them.
85 *
86 * \sa CborParsing, CborPretty, cbor_parser_init()
87 */
88
89 /**
90 * \addtogroup CborToJson
91 * @{
92 * <h2 class="groupheader">Conversion limitations</h2>
93 *
94 * When converting from CBOR to JSON, there may be information loss. This
95 * section lists the possible scenarios.
96 *
97 * \par Number precision:
98 * ALL JSON numbers, due to its JavaScript heritage, are IEEE 754
99 * double-precision floating point. This means JSON is not capable of
100 * representing integers numbers outside the range [-(2<sup>53</sup>)+1,
101 * 2<sup>53</sup>-1] and is not capable of representing NaN or infinite. If the
102 * CBOR data contains a number outside the valid range, the conversion will
103 * lose precision. If the input was NaN or infinite, the result of the
104 * conversion will be "null". In addition, the distinction between half-,
105 * single- and double-precision is lost.
106 *
107 * \par
108 * If enabled, the original value and original type are stored in the metadata.
109 *
110 * \par Non-native types:
111 * CBOR's type system is richer than JSON's, which means some data values
112 * cannot be represented when converted to JSON. The conversion silently turns
113 * them into strings: CBOR simple types become "simple(nn)" where \c nn is the
114 * simple type's value, with the exception of CBOR undefined, which becomes
115 * "undefined", while CBOR byte strings are converted to an Base16, Base64, or
116 * Base64url encoding
117 *
118 * \par
119 * If enabled, the original type is stored in the metadata.
120 *
121 * \par Presence of tags:
122 * JSON has no support for tagged values, so by default tags are dropped when
123 * converting to JSON. However, if the CborConvertObeyByteStringTags option is
124 * active (default), then certain known tags are honored and are used to format
125 * the conversion of the tagged byte string to JSON.
126 *
127 * \par
128 * If the CborConvertTagsToObjects option is active, then the tag and the
129 * tagged value are converted to to a JSON object. Otherwise, if enabled, the
130 * last (innermost) tag is stored in the metadata.
131 *
132 * \par Non-string keys in maps:
133 * JSON requires all Object keys to be strings, while CBOR does not. By
134 * default, if a non-string key is found, the conversion fails with error
135 * CborErrorJsonObjectKeyNotString. If the CborConvertStringifyMapKeys option
136 * is active, then the conversion attempts to create a string representation
137 * using CborPretty. Note that the \c json2cbor tool is not able to parse this
138 * back to the original form.
139 *
140 * \par Duplicate keys in maps:
141 * Neither JSON nor CBOR allow duplicated keys, but current TinyCBOR does not
142 * validate that this is the case. If there are duplicated keys in the input,
143 * they will be repeated in the output, which may JSON tools may flag as
144 * invalid. In addition to that, if the CborConvertStringifyMapKeys option is
145 * active, it is possible that a non-string key in a CBOR map will be converted
146 * to a string form that is identical to another key.
147 *
148 * \par
149 * When metadata support is active, the conversion will add extra key-value
150 * pairs to the JSON output so it can store the metadata. It is possible that
151 * the keys for the metadata clash with existing keys in the JSON map.
152 */
153
154 extern FILE *open_memstream(char **bufptr, size_t *sizeptr);
155
156 enum ConversionStatusFlags {
157 TypeWasNotNative = 0x100, /* anything but strings, boolean, null, arrays and maps */
158 TypeWasTagged = 0x200,
159 NumberPrecisionWasLost = 0x400,
160 NumberWasNaN = 0x800,
161 NumberWasInfinite = 0x1000,
162 NumberWasNegative = 0x2000, /* always used with NumberWasInifite or NumberWasTooBig */
163
164 FinalTypeMask = 0xff
165 };
166
167 typedef struct ConversionStatus {
168 CborTag lastTag;
169 uint64_t originalNumber;
170 int flags;
171 } ConversionStatus;
172
173 static CborError value_to_json(FILE *out, CborValue *it, int flags, CborType type, ConversionStatus *status);
174
dump_bytestring_base16(char ** result,CborValue * it)175 static CborError dump_bytestring_base16(char **result, CborValue *it)
176 {
177 static const char characters[] = "0123456789abcdef";
178 size_t i;
179 size_t n = 0;
180 uint8_t *buffer;
181 CborError err = cbor_value_calculate_string_length(it, &n);
182 if (err)
183 return err;
184
185 /* a Base16 (hex) output is twice as big as our buffer */
186 buffer = (uint8_t *)malloc(n * 2 + 1);
187 *result = (char *)buffer;
188
189 /* let cbor_value_copy_byte_string know we have an extra byte for the terminating NUL */
190 ++n;
191 err = cbor_value_copy_byte_string(it, buffer + n - 1, &n, it);
192 assert(err == CborNoError);
193
194 for (i = 0; i < n; ++i) {
195 uint8_t byte = buffer[n + i];
196 buffer[2*i] = characters[byte >> 4];
197 buffer[2*i + 1] = characters[byte & 0xf];
198 }
199 return CborNoError;
200 }
201
generic_dump_base64(char ** result,CborValue * it,const char alphabet[65])202 static CborError generic_dump_base64(char **result, CborValue *it, const char alphabet[65])
203 {
204 size_t n = 0, i;
205 uint8_t *buffer, *out, *in;
206 CborError err = cbor_value_calculate_string_length(it, &n);
207 if (err)
208 return err;
209
210 /* a Base64 output (untruncated) has 4 bytes for every 3 in the input */
211 size_t len = (n + 5) / 3 * 4;
212 out = buffer = (uint8_t *)malloc(len + 1);
213 *result = (char *)buffer;
214
215 /* we read our byte string at the tail end of the buffer
216 * so we can do an in-place conversion while iterating forwards */
217 in = buffer + len - n;
218
219 /* let cbor_value_copy_byte_string know we have an extra byte for the terminating NUL */
220 ++n;
221 err = cbor_value_copy_byte_string(it, in, &n, it);
222 assert(err == CborNoError);
223
224 uint_least32_t val = 0;
225 for (i = 0; n - i >= 3; i += 3) {
226 /* read 3 bytes x 8 bits = 24 bits */
227 if (false) {
228 #ifdef __GNUC__
229 } else if (i) {
230 __builtin_memcpy(&val, in + i - 1, sizeof(val));
231 val = cbor_ntohl(val);
232 #endif
233 } else {
234 val = (in[i] << 16) | (in[i + 1] << 8) | in[i + 2];
235 }
236
237 /* write 4 chars x 6 bits = 24 bits */
238 *out++ = alphabet[(val >> 18) & 0x3f];
239 *out++ = alphabet[(val >> 12) & 0x3f];
240 *out++ = alphabet[(val >> 6) & 0x3f];
241 *out++ = alphabet[val & 0x3f];
242 }
243
244 /* maybe 1 or 2 bytes left */
245 if (n - i) {
246 /* we can read in[i + 1] even if it's past the end of the string because
247 * we know (by construction) that it's a NUL byte */
248 #ifdef __GNUC__
249 uint16_t val16;
250 __builtin_memcpy(&val16, in + i, sizeof(val16));
251 val = cbor_ntohs(val16);
252 #else
253 val = (in[i] << 8) | in[i + 1];
254 #endif
255 val <<= 8;
256
257 /* the 65th character in the alphabet is our filler: either '=' or '\0' */
258 out[4] = '\0';
259 out[3] = alphabet[64];
260 if (n - i == 2) {
261 /* write the third char in 3 chars x 6 bits = 18 bits */
262 out[2] = alphabet[(val >> 6) & 0x3f];
263 } else {
264 out[2] = alphabet[64]; /* filler */
265 }
266 out[1] = alphabet[(val >> 12) & 0x3f];
267 out[0] = alphabet[(val >> 18) & 0x3f];
268 } else {
269 out[0] = '\0';
270 }
271
272 return CborNoError;
273 }
274
dump_bytestring_base64(char ** result,CborValue * it)275 static CborError dump_bytestring_base64(char **result, CborValue *it)
276 {
277 static const char alphabet[] = "ABCDEFGH" "IJKLMNOP" "QRSTUVWX" "YZabcdef"
278 "ghijklmn" "opqrstuv" "wxyz0123" "456789+/" "=";
279 return generic_dump_base64(result, it, alphabet);
280 }
281
dump_bytestring_base64url(char ** result,CborValue * it)282 static CborError dump_bytestring_base64url(char **result, CborValue *it)
283 {
284 static const char alphabet[] = "ABCDEFGH" "IJKLMNOP" "QRSTUVWX" "YZabcdef"
285 "ghijklmn" "opqrstuv" "wxyz0123" "456789-_";
286 return generic_dump_base64(result, it, alphabet);
287 }
288
add_value_metadata(FILE * out,CborType type,const ConversionStatus * status)289 static CborError add_value_metadata(FILE *out, CborType type, const ConversionStatus *status)
290 {
291 int flags = status->flags;
292 if (flags & TypeWasTagged) {
293 /* extract the tagged type, which may be JSON native */
294 type = flags & FinalTypeMask;
295 flags &= ~(FinalTypeMask | TypeWasTagged);
296
297 if (fprintf(out, "\"tag\":\"%" PRIu64 "\"%s", status->lastTag,
298 flags & ~TypeWasTagged ? "," : "") < 0)
299 return CborErrorIO;
300 }
301
302 if (!flags)
303 return CborNoError;
304
305 /* print at least the type */
306 if (fprintf(out, "\"t\":%d", type) < 0)
307 return CborErrorIO;
308
309 if (flags & NumberWasNaN)
310 if (fprintf(out, ",\"v\":\"nan\"") < 0)
311 return CborErrorIO;
312 if (flags & NumberWasInfinite)
313 if (fprintf(out, ",\"v\":\"%sinf\"", flags & NumberWasNegative ? "-" : "") < 0)
314 return CborErrorIO;
315 if (flags & NumberPrecisionWasLost)
316 if (fprintf(out, ",\"v\":\"%c%" PRIx64 "\"", flags & NumberWasNegative ? '-' : '+',
317 status->originalNumber) < 0)
318 return CborErrorIO;
319 if (type == CborSimpleType)
320 if (fprintf(out, ",\"v\":%d", (int)status->originalNumber) < 0)
321 return CborErrorIO;
322 return CborNoError;
323 }
324
find_tagged_type(CborValue * it,CborTag * tag,CborType * type)325 static CborError find_tagged_type(CborValue *it, CborTag *tag, CborType *type)
326 {
327 CborError err = CborNoError;
328 *type = cbor_value_get_type(it);
329 while (*type == CborTagType) {
330 cbor_value_get_tag(it, tag); /* can't fail */
331 err = cbor_value_advance_fixed(it);
332 if (err)
333 return err;
334
335 *type = cbor_value_get_type(it);
336 }
337 return err;
338 }
339
tagged_value_to_json(FILE * out,CborValue * it,int flags,ConversionStatus * status)340 static CborError tagged_value_to_json(FILE *out, CborValue *it, int flags, ConversionStatus *status)
341 {
342 CborTag tag;
343 CborError err;
344
345 if (flags & CborConvertTagsToObjects) {
346 cbor_value_get_tag(it, &tag); /* can't fail */
347 err = cbor_value_advance_fixed(it);
348 if (err)
349 return err;
350
351 if (fprintf(out, "{\"tag%" PRIu64 "\":", tag) < 0)
352 return CborErrorIO;
353
354 CborType type = cbor_value_get_type(it);
355 err = value_to_json(out, it, flags, type, status);
356 if (err)
357 return err;
358 if (flags & CborConvertAddMetadata && status->flags) {
359 if (fprintf(out, ",\"tag%" PRIu64 "$cbor\":{", tag) < 0 ||
360 add_value_metadata(out, type, status) != CborNoError ||
361 fputc('}', out) < 0)
362 return CborErrorIO;
363 }
364 if (fputc('}', out) < 0)
365 return CborErrorIO;
366 status->flags = TypeWasNotNative | CborTagType;
367 return CborNoError;
368 }
369
370 CborType type;
371 err = find_tagged_type(it, &status->lastTag, &type);
372 if (err)
373 return err;
374 tag = status->lastTag;
375
376 /* special handling of byte strings? */
377 if (type == CborByteStringType && (flags & CborConvertByteStringsToBase64Url) == 0 &&
378 (tag == CborNegativeBignumTag || tag == CborExpectedBase16Tag || tag == CborExpectedBase64Tag)) {
379 char *str;
380 char *pre = "";
381
382 if (tag == CborNegativeBignumTag) {
383 pre = "~";
384 err = dump_bytestring_base64url(&str, it);
385 } else if (tag == CborExpectedBase64Tag) {
386 err = dump_bytestring_base64(&str, it);
387 } else { /* tag == CborExpectedBase16Tag */
388 err = dump_bytestring_base16(&str, it);
389 }
390 if (err)
391 return err;
392 err = fprintf(out, "\"%s%s\"", pre, str) < 0 ? CborErrorIO : CborNoError;
393 free(str);
394 status->flags = TypeWasNotNative | TypeWasTagged | CborByteStringType;
395 return err;
396 }
397
398 /* no special handling */
399 err = value_to_json(out, it, flags, type, status);
400 status->flags |= TypeWasTagged | type;
401 return err;
402 }
403
stringify_map_key(char ** key,CborValue * it,int flags,CborType type)404 static CborError stringify_map_key(char **key, CborValue *it, int flags, CborType type)
405 {
406 (void)flags; /* unused */
407 (void)type; /* unused */
408 #ifdef WITHOUT_OPEN_MEMSTREAM
409 (void)key; /* unused */
410 (void)it; /* unused */
411 return CborErrorJsonNotImplemented;
412 #else
413 size_t size;
414
415 FILE *memstream = open_memstream(key, &size);
416 if (memstream == NULL)
417 return CborErrorOutOfMemory; /* could also be EMFILE, but it's unlikely */
418 CborError err = cbor_value_to_pretty_advance(memstream, it);
419
420 if (unlikely(fclose(memstream) < 0 || *key == NULL))
421 return CborErrorInternalError;
422 return err;
423 #endif
424 }
425
array_to_json(FILE * out,CborValue * it,int flags,ConversionStatus * status)426 static CborError array_to_json(FILE *out, CborValue *it, int flags, ConversionStatus *status)
427 {
428 const char *comma = "";
429 while (!cbor_value_at_end(it)) {
430 if (fprintf(out, "%s", comma) < 0)
431 return CborErrorIO;
432 comma = ",";
433
434 CborError err = value_to_json(out, it, flags, cbor_value_get_type(it), status);
435 if (err)
436 return err;
437 }
438 return CborNoError;
439 }
440
map_to_json(FILE * out,CborValue * it,int flags,ConversionStatus * status)441 static CborError map_to_json(FILE *out, CborValue *it, int flags, ConversionStatus *status)
442 {
443 const char *comma = "";
444 CborError err;
445 while (!cbor_value_at_end(it)) {
446 char *key;
447 if (fprintf(out, "%s", comma) < 0)
448 return CborErrorIO;
449 comma = ",";
450
451 CborType keyType = cbor_value_get_type(it);
452 if (likely(keyType == CborTextStringType)) {
453 size_t n = 0;
454 err = cbor_value_dup_text_string(it, &key, &n, it);
455 } else if (flags & CborConvertStringifyMapKeys) {
456 err = stringify_map_key(&key, it, flags, keyType);
457 } else {
458 return CborErrorJsonObjectKeyNotString;
459 }
460 if (err)
461 return err;
462
463 /* first, print the key */
464 if (fprintf(out, "\"%s\":", key) < 0)
465 return CborErrorIO;
466
467 /* then, print the value */
468 CborType valueType = cbor_value_get_type(it);
469 err = value_to_json(out, it, flags, valueType, status);
470
471 /* finally, print any metadata we may have */
472 if (flags & CborConvertAddMetadata) {
473 if (!err && keyType != CborTextStringType) {
474 if (fprintf(out, ",\"%s$keycbordump\":true", key) < 0)
475 err = CborErrorIO;
476 }
477 if (!err && status->flags) {
478 if (fprintf(out, ",\"%s$cbor\":{", key) < 0 ||
479 add_value_metadata(out, valueType, status) != CborNoError ||
480 fputc('}', out) < 0)
481 err = CborErrorIO;
482 }
483 }
484
485 free(key);
486 if (err)
487 return err;
488 }
489 return CborNoError;
490 }
491
value_to_json(FILE * out,CborValue * it,int flags,CborType type,ConversionStatus * status)492 static CborError value_to_json(FILE *out, CborValue *it, int flags, CborType type, ConversionStatus *status)
493 {
494 CborError err;
495 status->flags = 0;
496
497 switch (type) {
498 case CborArrayType:
499 case CborMapType: {
500 /* recursive type */
501 CborValue recursed;
502 err = cbor_value_enter_container(it, &recursed);
503 if (err) {
504 it->offset = recursed.offset;
505 return err; /* parse error */
506 }
507 if (fputc(type == CborArrayType ? '[' : '{', out) < 0)
508 return CborErrorIO;
509
510 err = (type == CborArrayType) ?
511 array_to_json(out, &recursed, flags, status) :
512 map_to_json(out, &recursed, flags, status);
513 if (err) {
514 it->offset = recursed.offset;
515 return err; /* parse error */
516 }
517
518 if (fputc(type == CborArrayType ? ']' : '}', out) < 0)
519 return CborErrorIO;
520 err = cbor_value_leave_container(it, &recursed);
521 if (err)
522 return err; /* parse error */
523
524 status->flags = 0; /* reset, there are never conversion errors for us */
525 return CborNoError;
526 }
527
528 case CborIntegerType: {
529 double num; /* JS numbers are IEEE double precision */
530 uint64_t val;
531 cbor_value_get_raw_integer(it, &val); /* can't fail */
532 num = (double)val;
533
534 if (cbor_value_is_negative_integer(it)) {
535 num = -num - 1; /* convert to negative */
536 if ((uint64_t)(-num - 1) != val) {
537 status->flags = NumberPrecisionWasLost | NumberWasNegative;
538 status->originalNumber = val;
539 }
540 } else {
541 if ((uint64_t)num != val) {
542 status->flags = NumberPrecisionWasLost;
543 status->originalNumber = val;
544 }
545 }
546 if (fprintf(out, "%.0f", num) < 0) /* this number has no fraction, so no decimal points please */
547 return CborErrorIO;
548 break;
549 }
550
551 case CborByteStringType:
552 case CborTextStringType: {
553 char *str;
554 if (type == CborByteStringType) {
555 err = dump_bytestring_base64url(&str, it);
556 status->flags = TypeWasNotNative;
557 } else {
558 size_t n = 0;
559 err = cbor_value_dup_text_string(it, &str, &n, it);
560 }
561 if (err)
562 return err;
563 err = (fprintf(out, "\"%s\"", str) < 0) ? CborErrorIO : CborNoError;
564 free(str);
565 return err;
566 }
567
568 case CborTagType:
569 return tagged_value_to_json(out, it, flags, status);
570
571 case CborSimpleType: {
572 uint8_t simple_type;
573 cbor_value_get_simple_type(it, &simple_type); /* can't fail */
574 status->flags = TypeWasNotNative;
575 status->originalNumber = simple_type;
576 if (fprintf(out, "\"simple(%" PRIu8 ")\"", simple_type) < 0)
577 return CborErrorIO;
578 break;
579 }
580
581 case CborNullType:
582 if (fprintf(out, "null") < 0)
583 return CborErrorIO;
584 break;
585
586 case CborUndefinedType:
587 status->flags = TypeWasNotNative;
588 if (fprintf(out, "\"undefined\"") < 0)
589 return CborErrorIO;
590 break;
591
592 case CborBooleanType: {
593 bool val;
594 cbor_value_get_boolean(it, &val); /* can't fail */
595 if (fprintf(out, val ? "true" : "false") < 0)
596 return CborErrorIO;
597 break;
598 }
599 #ifndef CBOR_NO_FLOATING_POINT
600 case CborDoubleType: {
601 double val;
602 if (false) {
603 float f;
604 case CborFloatType:
605 status->flags = TypeWasNotNative;
606 cbor_value_get_float(it, &f);
607 val = f;
608 #ifndef CBOR_NO_HALF_FLOAT_TYPE
609 } else if (false) {
610 uint16_t f16;
611 case CborHalfFloatType:
612 status->flags = TypeWasNotNative;
613 cbor_value_get_half_float(it, &f16);
614 val = decode_half(f16);
615 #endif
616 } else {
617 cbor_value_get_double(it, &val);
618 }
619
620 int r = fpclassify(val);
621 if (r == FP_NAN || r == FP_INFINITE) {
622 if (fprintf(out, "null") < 0)
623 return CborErrorIO;
624 status->flags |= r == FP_NAN ? NumberWasNaN :
625 NumberWasInfinite | (val < 0 ? NumberWasNegative : 0);
626 } else {
627 uint64_t ival = (uint64_t)fabs(val);
628 if ((double)ival == fabs(val)) {
629 /* print as integer so we get the full precision */
630 r = fprintf(out, "%s%" PRIu64, val < 0 ? "-" : "", ival);
631 status->flags |= TypeWasNotNative; /* mark this integer number as a double */
632 } else {
633 /* this number is definitely not a 64-bit integer */
634 r = fprintf(out, "%." DBL_DECIMAL_DIG_STR "g", val);
635 }
636 if (r < 0)
637 return CborErrorIO;
638 }
639 break;
640 }
641 #endif
642
643 case CborInvalidType:
644 default:
645 return CborErrorUnknownType;
646 }
647
648 return cbor_value_advance_fixed(it);
649 }
650
651 /**
652
653 * \enum CborToJsonFlags
654 * The CborToJsonFlags enum contains flags that control the conversion of CBOR to JSON.
655 *
656 * \value CborConvertAddMetadata Adds metadata to facilitate restoration of the original CBOR data.
657 * \value CborConvertTagsToObjects Converts CBOR tags to JSON objects
658 * \value CborConvertIgnoreTags (default) Ignore CBOR tags, except for byte strings
659 * \value CborConvertObeyByteStringTags (default) Honor formatting of CBOR byte strings if so tagged
660 * \value CborConvertByteStringsToBase64Url Force the conversion of all CBOR byte strings to Base64url encoding, despite any tags
661 * \value CborConvertRequireMapStringKeys (default) Require CBOR map keys to be strings, failing the conversion if they are not
662 * \value CborConvertStringifyMapKeys Convert non-string keys in CBOR maps to a string form
663 * \value CborConvertDefaultFlags Default conversion flags.
664 */
665
666 /**
667 * \fn CborError cbor_value_to_json(FILE *out, const CborValue *value, int flags)
668 *
669 * Converts the current CBOR type pointed by \a value to JSON and writes that
670 * to the \a out stream. If an error occurs, this function returns an error
671 * code similar to CborParsing. The \a flags parameter indicates one of the
672 * flags from CborToJsonFlags that control the conversion.
673 *
674 * \sa cbor_value_to_json_advance(), cbor_value_to_pretty()
675 */
676
677 /**
678 * Converts the current CBOR type pointed by \a value to JSON and writes that
679 * to the \a out stream. If an error occurs, this function returns an error
680 * code similar to CborParsing. The \a flags parameter indicates one of the
681 * flags from CborToJsonFlags that control the conversion.
682 *
683 * If no error ocurred, this function advances \a value to the next element.
684 *
685 * \sa cbor_value_to_json(), cbor_value_to_pretty_advance()
686 */
cbor_value_to_json_advance(FILE * out,CborValue * value,int flags)687 CborError cbor_value_to_json_advance(FILE *out, CborValue *value, int flags)
688 {
689 ConversionStatus status;
690 return value_to_json(out, value, flags, cbor_value_get_type(value), &status);
691 }
692
693 /** @} */
694