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