tinycbor: Import v0.2.1
[contrib/iotivity.git] / extlibs / tinycbor / tinycbor / src / cbortojson.c
1 /****************************************************************************
2 **
3 ** Copyright (C) 2015 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 _GNU_SOURCE 1
27 #define _POSIX_C_SOURCE 200809L
28 #include "cbor.h"
29 #include "cborjson.h"
30 #include "compilersupport_p.h"
31 #include "math_support_p.h"
32
33 #include <float.h>
34 #include <inttypes.h>
35 #include <math.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39
40 extern FILE *open_memstream(char **bufptr, size_t *sizeptr);
41
42 enum ConversionStatusFlags {
43     TypeWasNotNative            = 0x100,    // anything but strings, boolean, null, arrays and maps
44     TypeWasTagged               = 0x200,
45     NumberPrecisionWasLost      = 0x400,
46     NumberWasNaN                = 0x800,
47     NumberWasInfinite           = 0x1000,
48     NumberWasNegative           = 0x2000,   // always used with NumberWasInifite or NumberWasTooBig
49
50     FinalTypeMask               = 0xff
51 };
52
53 typedef struct ConversionStatus {
54     CborTag lastTag;
55     uint64_t originalNumber;
56     int flags;
57 } ConversionStatus;
58
59 static CborError value_to_json(FILE *out, CborValue *it, int flags, CborType type, ConversionStatus *status);
60
61 static CborError dump_bytestring_base16(char **result, CborValue *it)
62 {
63     static const char characters[] = "0123456789abcdef";
64     size_t i;
65     size_t n = 0;
66     uint8_t *buffer;
67     CborError err = cbor_value_calculate_string_length(it, &n);
68     if (err)
69         return err;
70
71     // a Base16 (hex) output is twice as big as our buffer
72     buffer = (uint8_t *)malloc(n * 2 + 1);
73     *result = (char *)buffer;
74
75     // let cbor_value_copy_byte_string know we have an extra byte for the terminating NUL
76     ++n;
77     err = cbor_value_copy_byte_string(it, buffer + n - 1, &n, it);
78     assert(err == CborNoError);
79
80     for (i = 0; i < n; ++i) {
81         uint8_t byte = buffer[n + i];
82         buffer[2*i]     = characters[byte >> 4];
83         buffer[2*i + 1] = characters[byte & 0xf];
84     }
85     return CborNoError;
86 }
87
88 static CborError generic_dump_base64(char **result, CborValue *it, const char alphabet[65])
89 {
90     size_t n = 0, i;
91     uint8_t *buffer, *out, *in;
92     CborError err = cbor_value_calculate_string_length(it, &n);
93     if (err)
94         return err;
95
96     // a Base64 output (untruncated) has 4 bytes for every 3 in the input
97     size_t len = (n + 5) / 3 * 4;
98     out = buffer = (uint8_t *)malloc(len + 1);
99     *result = (char *)buffer;
100
101     // we read our byte string at the tail end of the buffer
102     // so we can do an in-place conversion while iterating forwards
103     in = buffer + len - n;
104
105     // let cbor_value_copy_byte_string know we have an extra byte for the terminating NUL
106     ++n;
107     err = cbor_value_copy_byte_string(it, in, &n, it);
108     assert(err == CborNoError);
109
110     uint_least32_t val = 0;
111     for (i = 0; n - i >= 3; i += 3) {
112         // read 3 bytes x 8 bits = 24 bits
113         if (false) {
114 #ifdef __GNUC__
115         } else if (i) {
116             __builtin_memcpy(&val, in + i - 1, sizeof(val));
117             val = cbor_ntohl(val);
118 #endif
119         } else {
120             val = (in[i] << 16) | (in[i + 1] << 8) | in[i + 2];
121         }
122
123         // write 4 chars x 6 bits = 24 bits
124         *out++ = alphabet[(val >> 18) & 0x3f];
125         *out++ = alphabet[(val >> 12) & 0x3f];
126         *out++ = alphabet[(val >> 6) & 0x3f];
127         *out++ = alphabet[val & 0x3f];
128     }
129
130     // maybe 1 or 2 bytes left
131     if (n - i) {
132         // we can read in[i + 1] even if it's past the end of the string because
133         // we know (by construction) that it's a NUL byte
134 #ifdef __GNUC__
135         uint16_t val16;
136         __builtin_memcpy(&val16, in + i, sizeof(val16));
137         val = cbor_ntohs(val16);
138 #else
139         val = (in[i] << 8) | in[i + 1];
140 #endif
141         val <<= 8;
142
143         // the 65th character in the alphabet is our filler: either '=' or '\0'
144         out[4] = '\0';
145         out[3] = alphabet[64];
146         if (n - i == 2) {
147             // write the third char in 3 chars x 6 bits = 18 bits
148             out[2] = alphabet[(val >> 6) & 0x3f];
149         } else {
150             out[2] = alphabet[64];  // filler
151         }
152         out[1] = alphabet[(val >> 12) & 0x3f];
153         out[0] = alphabet[(val >> 18) & 0x3f];
154     } else {
155         out[0] = '\0';
156     }
157
158     return CborNoError;
159 }
160
161 static CborError dump_bytestring_base64(char **result, CborValue *it)
162 {
163     static const char alphabet[] = "ABCDEFGH" "IJKLMNOP" "QRSTUVWX" "YZabcdef"
164                                    "ghijklmn" "opqrstuv" "wxyz0123" "456789+/" "=";
165     return generic_dump_base64(result, it, alphabet);
166 }
167
168 static CborError dump_bytestring_base64url(char **result, CborValue *it)
169 {
170     static const char alphabet[] = "ABCDEFGH" "IJKLMNOP" "QRSTUVWX" "YZabcdef"
171                                    "ghijklmn" "opqrstuv" "wxyz0123" "456789-_";
172     return generic_dump_base64(result, it, alphabet);
173 }
174
175 static CborError add_value_metadata(FILE *out, CborType type, const ConversionStatus *status)
176 {
177     int flags = status->flags;
178     if (flags & TypeWasTagged) {
179         // extract the tagged type, which may be JSON native
180         type = flags & FinalTypeMask;
181         flags &= ~(FinalTypeMask | TypeWasTagged);
182
183         if (fprintf(out, "\"tag\":\"%" PRIu64 "\"%s", status->lastTag,
184                     flags & ~TypeWasTagged ? "," : "") < 0)
185             return CborErrorIO;
186     }
187
188     if (!flags)
189         return CborNoError;
190
191     // print at least the type
192     if (fprintf(out, "\"t\":%d", type) < 0)
193         return CborErrorIO;
194
195     if (flags & NumberWasNaN)
196         if (fprintf(out, ",\"v\":\"nan\"") < 0)
197             return CborErrorIO;
198     if (flags & NumberWasInfinite)
199         if (fprintf(out, ",\"v\":\"%sinf\"", flags & NumberWasNegative ? "-" : "") < 0)
200             return CborErrorIO;
201     if (flags & NumberPrecisionWasLost)
202         if (fprintf(out, ",\"v\":\"%c%" PRIx64 "\"", flags & NumberWasNegative ? '-' : '+',
203                     status->originalNumber) < 0)
204             return CborErrorIO;
205     if (type == CborSimpleType)
206         if (fprintf(out, ",\"v\":%d", (int)status->originalNumber) < 0)
207             return CborErrorIO;
208     return CborNoError;
209 }
210
211 static CborError find_tagged_type(CborValue *it, CborTag *tag, CborType *type)
212 {
213     CborError err = CborNoError;
214     *type = cbor_value_get_type(it);
215     while (*type == CborTagType) {
216         cbor_value_get_tag(it, tag);    // can't fail
217         err = cbor_value_advance_fixed(it);
218         if (err)
219             return err;
220
221         *type = cbor_value_get_type(it);
222     }
223     return err;
224 }
225
226 static CborError tagged_value_to_json(FILE *out, CborValue *it, int flags, ConversionStatus *status)
227 {
228     CborTag tag;
229     CborError err;
230
231     if (flags & CborConvertTagsToObjects) {
232         cbor_value_get_tag(it, &tag);       // can't fail
233         err = cbor_value_advance_fixed(it);
234         if (err)
235             return err;
236
237         if (fprintf(out, "{\"tag%" PRIu64 "\":", tag) < 0)
238             return CborErrorIO;
239
240         CborType type = cbor_value_get_type(it);
241         err = value_to_json(out, it, flags, type, status);
242         if (err)
243             return err;
244         if (flags & CborConvertAddMetadata && status->flags) {
245             if (fprintf(out, ",\"tag%" PRIu64 "$cbor\":{", tag) < 0 ||
246                     add_value_metadata(out, type, status) != CborNoError ||
247                     fputc('}', out) < 0)
248                 return CborErrorIO;
249         }
250         if (fputc('}', out) < 0)
251             return CborErrorIO;
252         status->flags = TypeWasNotNative | CborTagType;
253         return CborNoError;
254     }
255
256     CborType type;
257     err = find_tagged_type(it, &status->lastTag, &type);
258     if (err)
259         return err;
260     tag = status->lastTag;
261
262     // special handling of byte strings?
263     if (type == CborByteStringType && (flags & CborConvertByteStringsToBase64Url) == 0 &&
264             (tag == CborNegativeBignumTag || tag == CborExpectedBase16Tag || tag == CborExpectedBase64Tag)) {
265         char *str;
266         char *pre = "";
267
268         if (tag == CborNegativeBignumTag) {
269             pre = "~";
270             err = dump_bytestring_base64url(&str, it);
271         } else if (tag == CborExpectedBase64Tag) {
272             err = dump_bytestring_base64(&str, it);
273         } else { // tag == CborExpectedBase16Tag
274             err = dump_bytestring_base16(&str, it);
275         }
276         if (err)
277             return err;
278         err = fprintf(out, "\"%s%s\"", pre, str) < 0 ? CborErrorIO : CborNoError;
279         free(str);
280         status->flags = TypeWasNotNative | TypeWasTagged | CborByteStringType;
281         return err;
282     }
283
284     // no special handling
285     err = value_to_json(out, it, flags, type, status);
286     status->flags |= TypeWasTagged | type;
287     return err;
288 }
289
290 static CborError stringify_map_key(char **key, CborValue *it, int flags, CborType type)
291 {
292     (void)flags;    // unused
293     (void)type;     // unused
294     size_t size;
295
296     FILE *memstream = open_memstream(key, &size);
297     if (memstream == NULL)
298         return CborErrorOutOfMemory;        // could also be EMFILE, but it's unlikely
299     CborError err = cbor_value_to_pretty_advance(memstream, it);
300
301     if (unlikely(fclose(memstream) < 0 || *key == NULL))
302         return CborErrorInternalError;
303     return err;
304 }
305
306 static CborError array_to_json(FILE *out, CborValue *it, int flags, ConversionStatus *status)
307 {
308     const char *comma = "";
309     while (!cbor_value_at_end(it)) {
310         if (fprintf(out, "%s", comma) < 0)
311             return CborErrorIO;
312         comma = ",";
313
314         CborError err = value_to_json(out, it, flags, cbor_value_get_type(it), status);
315         if (err)
316             return err;
317     }
318     return CborNoError;
319 }
320
321 static CborError map_to_json(FILE *out, CborValue *it, int flags, ConversionStatus *status)
322 {
323     const char *comma = "";
324     CborError err;
325     while (!cbor_value_at_end(it)) {
326         char *key;
327         if (fprintf(out, "%s", comma) < 0)
328             return CborErrorIO;
329         comma = ",";
330
331         CborType keyType = cbor_value_get_type(it);
332         if (likely(keyType == CborTextStringType)) {
333             size_t n = 0;
334             err = cbor_value_dup_text_string(it, &key, &n, it);
335         } else if (flags & CborConvertStringifyMapKeys) {
336             err = stringify_map_key(&key, it, flags, keyType);
337         } else {
338             return CborErrorJsonObjectKeyNotString;
339         }
340         if (err)
341             return err;
342
343         // first, print the key
344         if (fprintf(out, "\"%s\":", key) < 0)
345             return CborErrorIO;
346
347         // then, print the value
348         CborType valueType = cbor_value_get_type(it);
349         err = value_to_json(out, it, flags, valueType, status);
350
351         // finally, print any metadata we may have
352         if (flags & CborConvertAddMetadata) {
353             if (!err && keyType != CborTextStringType) {
354                 if (fprintf(out, ",\"%s$keycbordump\":true", key) < 0)
355                     err = CborErrorIO;
356             }
357             if (!err && status->flags) {
358                 if (fprintf(out, ",\"%s$cbor\":{", key) < 0 ||
359                         add_value_metadata(out, valueType, status) != CborNoError ||
360                         fputc('}', out) < 0)
361                     err = CborErrorIO;
362             }
363         }
364
365         free(key);
366         if (err)
367             return err;
368     }
369     return CborNoError;
370 }
371
372 static CborError value_to_json(FILE *out, CborValue *it, int flags, CborType type, ConversionStatus *status)
373 {
374     CborError err;
375     status->flags = 0;
376
377     switch (type) {
378     case CborArrayType:
379     case CborMapType: {
380         // recursive type
381         CborValue recursed;
382         err = cbor_value_enter_container(it, &recursed);
383         if (err) {
384             it->ptr = recursed.ptr;
385             return err;       // parse error
386         }
387         if (fputc(type == CborArrayType ? '[' : '{', out) < 0)
388             return CborErrorIO;
389
390         err = (type == CborArrayType) ?
391                   array_to_json(out, &recursed, flags, status) :
392                   map_to_json(out, &recursed, flags, status);
393         if (err) {
394             it->ptr = recursed.ptr;
395             return err;       // parse error
396         }
397
398         if (fputc(type == CborArrayType ? ']' : '}', out) < 0)
399             return CborErrorIO;
400         err = cbor_value_leave_container(it, &recursed);
401         if (err)
402             return err;       // parse error
403
404         status->flags = 0;    // reset, there are never conversion errors for us
405         return CborNoError;
406     }
407
408     case CborIntegerType: {
409         double num;     // JS numbers are IEEE double precision
410         uint64_t val;
411         cbor_value_get_raw_integer(it, &val);    // can't fail
412         num = (double)val;
413
414         if (cbor_value_is_negative_integer(it)) {
415             num = -num - 1;                     // convert to negative
416             if ((uint64_t)(-num - 1) != val) {
417                 status->flags = NumberPrecisionWasLost | NumberWasNegative;
418                 status->originalNumber = val;
419             }
420         } else {
421             if ((uint64_t)num != val) {
422                 status->flags = NumberPrecisionWasLost;
423                 status->originalNumber = val;
424             }
425         }
426         if (fprintf(out, "%.0f", num) < 0)  // this number has no fraction, so no decimal points please
427             return CborErrorIO;
428         break;
429     }
430
431     case CborByteStringType:
432     case CborTextStringType: {
433         char *str;
434         if (type == CborByteStringType) {
435             err = dump_bytestring_base64url(&str, it);
436             status->flags = TypeWasNotNative;
437         } else {
438             size_t n = 0;
439             err = cbor_value_dup_text_string(it, &str, &n, it);
440         }
441         if (err)
442             return err;
443         err = (fprintf(out, "\"%s\"", str) < 0) ? CborErrorIO : CborNoError;
444         free(str);
445         return err;
446     }
447
448     case CborTagType:
449         return tagged_value_to_json(out, it, flags, status);
450
451     case CborSimpleType: {
452         uint8_t simple_type;
453         cbor_value_get_simple_type(it, &simple_type);  // can't fail
454         status->flags = TypeWasNotNative;
455         status->originalNumber = simple_type;
456         if (fprintf(out, "\"simple(%" PRIu8 ")\"", simple_type) < 0)
457             return CborErrorIO;
458         break;
459     }
460
461     case CborNullType:
462         if (fprintf(out, "null") < 0)
463             return CborErrorIO;
464         break;
465
466     case CborUndefinedType:
467         status->flags = TypeWasNotNative;
468         if (fprintf(out, "\"undefined\"") < 0)
469             return CborErrorIO;
470         break;
471
472     case CborBooleanType: {
473         bool val;
474         cbor_value_get_boolean(it, &val);       // can't fail
475         if (fprintf(out, val ? "true" : "false") < 0)
476             return CborErrorIO;
477         break;
478     }
479
480     case CborDoubleType: {
481         double val;
482         if (false) {
483             float f;
484     case CborFloatType:
485             status->flags = TypeWasNotNative;
486             cbor_value_get_float(it, &f);
487             val = f;
488         } else if (false) {
489             uint16_t f16;
490     case CborHalfFloatType:
491             status->flags = TypeWasNotNative;
492             cbor_value_get_half_float(it, &f16);
493             val = decode_half(f16);
494         } else {
495             cbor_value_get_double(it, &val);
496         }
497
498         int r = fpclassify(val);
499         if (r == FP_NAN || r == FP_INFINITE) {
500             if (fprintf(out, "null") < 0)
501                 return CborErrorIO;
502             status->flags |= r == FP_NAN ? NumberWasNaN :
503                                            NumberWasInfinite | (val < 0 ? NumberWasNegative : 0);
504         } else {
505             uint64_t ival = (uint64_t)fabs(val);
506             if ((double)ival == fabs(val)) {
507                 // print as integer so we get the full precision
508                 r = fprintf(out, "%s%" PRIu64, val < 0 ? "-" : "", ival);
509                 status->flags |= TypeWasNotNative;   // mark this integer number as a double
510             } else {
511                 // this number is definitely not a 64-bit integer
512                 r = fprintf(out, "%." DBL_DECIMAL_DIG_STR "g", val);
513             }
514             if (r < 0)
515                 return CborErrorIO;
516         }
517         break;
518     }
519
520     case CborInvalidType:
521         return CborErrorUnknownType;
522     }
523
524     return cbor_value_advance_fixed(it);
525 }
526
527 CborError cbor_value_to_json_advance(FILE *out, CborValue *value, int flags)
528 {
529     ConversionStatus status;
530     return value_to_json(out, value, flags, cbor_value_get_type(value), &status);
531 }