tinycbor: Update import to v0.4
[platform/upstream/iotivity.git] / extlibs / tinycbor / tinycbor / tools / json2cbor / json2cbor.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 _POSIX_C_SOURCE 200809L
26 #define _GNU_SOURCE
27 #include "cbor.h"
28 #include "compilersupport_p.h"
29
30 #include <cJSON.h>
31
32 #include <errno.h>
33 #include <math.h>
34 #include <inttypes.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <unistd.h>
39
40 static const char meta_data_marker[] = "$cbor";
41 uint8_t *buffer;
42 size_t buffersize;
43 bool usingMetaData = false;
44
45 struct MetaData {
46     CborTag tag;
47     union {
48         const char *v;
49         uint8_t simpleType;
50     };
51     CborType t;
52     bool tagged;
53 };
54
55 uint8_t *decode_base64_generic(const char *string, size_t *len, const int8_t reverse_alphabet[256])
56 {
57     *len = ((strlen(string) + 3) & ~3) * 3 / 4;
58     uint8_t *buffer = malloc(*len);
59     if (buffer == NULL)
60         return NULL;
61
62     uint8_t *out = buffer;
63     const uint8_t *in = (const uint8_t *)string;
64     bool done = false;
65     while (!done) {
66         if (reverse_alphabet[in[0]] < 0 || reverse_alphabet[in[1]] < 0) {
67             if (in[0] == '\0')
68                 done = true;
69             break;
70         }
71
72         uint32_t val = reverse_alphabet[in[0]] << 18;
73         val |= reverse_alphabet[in[1]] << 12;
74         if (in[2] == '=' || in[2] == '\0') {
75             if (in[2] == '=' && (in[3] != '=' || in[4] != '\0'))
76                 break;
77             val >>= 12;
78             done = true;
79         } else if (in[3] == '=' || in[3] == '\0') {
80             if (in[3] == '=' && in[4] != '\0')
81                 break;
82             val >>= 6;
83             val |= reverse_alphabet[in[2]];
84             done = true;
85         } else {
86             val |= reverse_alphabet[in[2]] << 6;
87             val |= reverse_alphabet[in[3]];
88         }
89
90         *out++ = val >> 16;
91         *out++ = val >> 8;
92         *out++ = val;
93         in += 4;
94     }
95
96     if (!done) {
97         free(buffer);
98         return NULL;
99     }
100     *len = out - buffer;
101     return buffer;
102 }
103
104 uint8_t *decode_base64(const char *string, size_t *len)
105 {
106     static const int8_t reverse_alphabet[256] = {
107         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
108         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
109         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63,
110         52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1,
111         -1,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14,
112         15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
113         -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
114         41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1,
115         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
116         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
117         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
118         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
119         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
120         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
121         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
122         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
123     };
124     return decode_base64_generic(string, len, reverse_alphabet);
125 }
126
127 uint8_t *decode_base64url(const char *string, size_t *len)
128 {
129     static const int8_t reverse_alphabet[256] = {
130         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
131         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
132         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1,
133         52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1,
134         -1,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14,
135         15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, 63,
136         -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
137         41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1,
138         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
139         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
140         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
141         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
142         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
143         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
144         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
145         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
146     };
147     return decode_base64_generic(string, len, reverse_alphabet);
148 }
149
150 uint8_t *decode_base16(const char *string, size_t *len)
151 {
152     *len = strlen(string) / 2;
153     uint8_t *buffer = malloc(*len);
154     if (buffer == NULL)
155         return NULL;
156
157     for (size_t i = 0; i < *len; ++i) {
158         char c = string[i * 2];
159         if (c >= '0' && c <= '9') {
160             buffer[i] = (c - '0') << 4;
161         } else if ((c | 0x20) >= 'a' && (c | 0x20) <= 'f') {
162             buffer[i] = ((c | 0x20) - 'a' + 10) << 4;
163         } else {
164             free(buffer);
165             return NULL;
166         }
167
168         c = string[i * 2 + 1];
169         if (c >= '0' && c <= '9') {
170             buffer[i] |= (c - '0');
171         } else if ((c | 0x20) >= 'a' && (c | 0x20) <= 'f') {
172             buffer[i] |= ((c | 0x20) - 'a' + 10);
173         } else {
174             free(buffer);
175             return NULL;
176         }
177     }
178
179     return buffer;
180 }
181
182 size_t get_cjson_size_limited(cJSON *container)
183 {
184     // cJSON_GetArraySize is O(n), so don't go too far
185     unsigned s = 0;
186     cJSON *item;
187     for (item = container->child; item; item = item->next) {
188         if (++s > 255)
189             return CborIndefiniteLength;
190     }
191     return s;
192 }
193
194 cJSON *get_meta_data(cJSON *object, cJSON *item)
195 {
196     cJSON *meta;
197     char *metadatakey;
198
199     if (asprintf(&metadatakey, "%s%s", item->string, meta_data_marker) < 0 || metadatakey == NULL)
200         return NULL;
201     meta = cJSON_GetObjectItem(object, metadatakey);
202     free(metadatakey);
203     return meta;
204 }
205
206 struct MetaData parse_meta_data(cJSON *md)
207 {
208     struct MetaData result = { 0, {NULL}, CborInvalidType, false };
209     if (md == NULL || md->type != cJSON_Object)
210         return result;
211
212     for (md = md->child; md; md = md->next) {
213         if (strcmp(md->string, "tag") == 0) {
214             if (md->type != cJSON_String || sscanf(md->valuestring, "%" PRIu64, &result.tag) < 0)
215                 fprintf(stderr, "json2cbor: could not parse tag: %s\n", md->valuestring);
216             else
217                 result.tagged = true;
218         } else if (strcmp(md->string, "t") == 0) {
219             result.t = md->valueint;
220         } else if (strcmp(md->string, "v") == 0) {
221             if (md->type == cJSON_Number)
222                 result.simpleType = md->valueint;
223             else
224                 result.v = md->valuestring;
225         }
226     }
227     return result;
228 }
229
230 CborError decode_json(cJSON *json, CborEncoder *encoder);
231 CborError decode_json_with_metadata(cJSON *item, CborEncoder *encoder, struct MetaData md)
232 {
233     switch (md.t) {
234     case CborIntegerType: {
235         // integer that has more than 53 bits of precision
236         uint64_t v;
237         bool positive = *md.v++ == '+';
238         if (sscanf(md.v, "%" PRIx64, &v) < 0) {
239             fprintf(stderr, "json2cbor: could not parse number: %s\n", md.v);
240             break;
241         }
242         return positive ? cbor_encode_uint(encoder, v) : cbor_encode_negative_int(encoder, v);
243     }
244
245     case CborByteStringType: {
246         uint8_t *data;
247         size_t len;
248         if (md.tag == CborExpectedBase64Tag)
249             data = decode_base64(item->valuestring, &len);
250         else if (md.tag == CborExpectedBase16Tag)
251             data = decode_base16(item->valuestring, &len);
252         else if (md.tag == CborNegativeBignumTag)
253             data = decode_base64url(item->valuestring + 1, &len);
254         else
255             data = decode_base64url(item->valuestring, &len);
256
257         if (data != NULL) {
258             CborError err = cbor_encode_byte_string(encoder, data, len);
259             free(data);
260             return err;
261         }
262         fprintf(stderr, "json2cbor: could not decode encoded byte string: %s\n", item->valuestring);
263         break;
264     }
265
266     case CborSimpleType:
267         return cbor_encode_simple_value(encoder, md.simpleType);
268
269     case CborUndefinedType:
270         return cbor_encode_undefined(encoder);
271
272     case CborHalfFloatType:
273     case CborFloatType:
274     case CborDoubleType: {
275         unsigned short half;
276         double v;
277         if (!md.v) {
278             v = item->valuedouble;
279         } else if (strcmp(md.v, "nan") == 0) {
280             v = NAN;
281         } else if (strcmp(md.v, "-inf") == 0) {
282             v = -INFINITY;
283         } else if (strcmp(md.v, "inf") == 0) {
284             v = INFINITY;
285         } else {
286             fprintf(stderr, "json2cbor: invalid floating-point value: %s\n", md.v);
287             break;
288         }
289
290         // we can't get an OOM here because the metadata makes up for space
291         // (the smallest metadata is "$cbor":{"t":250} (17 bytes)
292         return (md.t == CborDoubleType) ? cbor_encode_double(encoder, v) :
293                (md.t == CborFloatType) ? cbor_encode_float(encoder, v) :
294                                          (half = encode_half(v), cbor_encode_half_float(encoder, &half));
295     }
296
297     default:
298         fprintf(stderr, "json2cbor: invalid CBOR type: %d\n", md.t);
299     case CborInvalidType:
300         break;
301     }
302
303     return decode_json(item, encoder);
304 }
305
306 CborError decode_json(cJSON *json, CborEncoder *encoder)
307 {
308     CborEncoder container;
309     CborError err;
310     cJSON *item;
311
312     switch (json->type) {
313     case cJSON_False:
314     case cJSON_True:
315         return cbor_encode_boolean(encoder, json->type == cJSON_True);
316
317     case cJSON_NULL:
318         return cbor_encode_null(encoder);
319
320     case cJSON_Number:
321         if ((double)json->valueint == json->valuedouble)
322             return cbor_encode_int(encoder, json->valueint);
323 encode_double:
324         // the only exception that JSON is larger: floating point numbers
325         container = *encoder;   // save the state
326         err = cbor_encode_double(encoder, json->valuedouble);
327
328         if (err == CborErrorOutOfMemory) {
329             buffersize += 1024;
330             uint8_t *newbuffer = realloc(buffer, buffersize);
331             if (newbuffer == NULL)
332                 return err;
333
334             *encoder = container;   // restore state
335             encoder->data.ptr = newbuffer + (container.data.ptr - buffer);
336             encoder->end = newbuffer + buffersize;
337             buffer = newbuffer;
338             goto encode_double;
339         }
340         return err;
341
342     case cJSON_String:
343         return cbor_encode_text_stringz(encoder, json->valuestring);
344
345     default:
346         return CborErrorUnknownType;
347
348     case cJSON_Array:
349         err = cbor_encoder_create_array(encoder, &container, get_cjson_size_limited(json));
350         if (err)
351             return err;
352         for (item = json->child; item; item = item->next) {
353             err = decode_json(item, &container);
354             if (err)
355                 return err;
356         }
357         return cbor_encoder_close_container_checked(encoder, &container);
358
359     case cJSON_Object:
360         err = cbor_encoder_create_map(encoder, &container,
361                                       usingMetaData ? CborIndefiniteLength : get_cjson_size_limited(json));
362         if (err)
363             return err;
364
365         for (item = json->child ; item; item = item->next) {
366             if (usingMetaData && strlen(item->string) > strlen(meta_data_marker)
367                     && strcmp(item->string + strlen(item->string) - 5, meta_data_marker) == 0)
368                 continue;
369
370             err = cbor_encode_text_stringz(&container, item->string);
371             if (err)
372                 return err;
373
374             if (usingMetaData) {
375                 cJSON *meta = get_meta_data(json, item);
376                 struct MetaData md = parse_meta_data(meta);
377                 if (md.tagged) {
378                     err = cbor_encode_tag(&container, md.tag);
379                     if (err)
380                         return err;
381                 }
382
383                 err = decode_json_with_metadata(item, &container, md);
384             } else {
385                 err = decode_json(item, &container);
386             }
387             if (err)
388                 return err;
389         }
390
391         return cbor_encoder_close_container_checked(encoder, &container);
392     }
393 }
394
395 int main(int argc, char **argv)
396 {
397     int c;
398     while ((c = getopt(argc, argv, "M")) != -1) {
399         switch (c) {
400         case 'M':
401             usingMetaData = true;
402             break;
403
404         case '?':
405             fprintf(stderr, "Unknown option -%c.\n", optopt);
406             // fall through
407         case 'h':
408             puts("Usage: json2cbor [OPTION]... [FILE]...\n"
409                  "Reads JSON content from FILE and convert to CBOR.\n"
410                  "\n"
411                  "Options:\n"
412                  " -M       Interpret metadata added by cbordump tool\n"
413                  "");
414             return c == '?' ? EXIT_FAILURE : EXIT_SUCCESS;
415         }
416     }
417
418     FILE *in;
419     const char *fname = argv[optind];
420     if (fname && strcmp(fname, "-") != 0) {
421         in = fopen(fname, "r");
422         if (!in) {
423             perror("open");
424             return EXIT_FAILURE;
425         }
426     } else {
427         in = stdin;
428         fname = "-";
429     }
430
431     /* 1. read the file */
432     off_t fsize;
433     if (fseeko(in, 0, SEEK_END) == 0 && (fsize = ftello(in)) >= 0) {
434         buffersize = fsize + 1;
435         buffer = malloc(buffersize);
436         if (buffer == NULL) {
437             perror("malloc");
438             return EXIT_FAILURE;
439         }
440
441         rewind(in);
442         fsize = fread(buffer, 1, fsize, in);
443         buffer[fsize] = '\0';
444     } else {
445         const unsigned chunk = 16384;
446         buffersize = 0;
447         buffer = NULL;
448         do {    // it the hard way
449             buffer = realloc(buffer, buffersize + chunk);
450             if (buffer == NULL)
451                 perror("malloc");
452
453             buffersize += fread(buffer + buffersize, 1, chunk, in);
454         } while (!feof(in) && !ferror(in));
455         buffer[buffersize] = '\0';
456     }
457
458     if (ferror(in)) {
459         perror("read");
460         return EXIT_FAILURE;
461     }
462     if (in != stdin)
463         fclose(in);
464
465     /* 2. parse as JSON */
466     cJSON *doc = cJSON_ParseWithOpts((char *)buffer, NULL, true);
467     if (doc == NULL) {
468         fprintf(stderr, "json2cbor: %s: could not parse.\n", fname);
469         return EXIT_FAILURE;
470     }
471
472     /* 3. encode as CBOR */
473     // We're going to reuse the buffer, as CBOR is usually shorter than the equivalent JSON
474     CborEncoder encoder;
475     cbor_encoder_init(&encoder, buffer, buffersize, 0);
476     CborError err = decode_json(doc, &encoder);
477
478     cJSON_Delete(doc);
479
480     if (err) {
481         fprintf(stderr, "json2cbor: %s: error encoding to CBOR: %s\n", fname,
482                 cbor_error_string(err));
483         return EXIT_FAILURE;
484     }
485
486     fwrite(buffer, 1, encoder.data.ptr - buffer, stdout);
487     free(buffer);
488     return EXIT_SUCCESS;
489 }