1 /****************************************************************************
3 ** Copyright (C) 2015 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 ****************************************************************************/
25 #define _POSIX_C_SOURCE 200809L
28 #include "compilersupport_p.h"
30 #if defined (__TIZENRT__)
31 #include <apps/netutils/cJSON.h>
44 static const char meta_data_marker[] = "$cbor";
47 bool usingMetaData = false;
59 uint8_t *decode_base64_generic(const char *string, size_t *len, const int8_t reverse_alphabet[256])
61 *len = ((strlen(string) + 3) & ~3) * 3 / 4;
62 uint8_t *buffer = malloc(*len);
66 uint8_t *out = buffer;
67 const uint8_t *in = (const uint8_t *)string;
70 if (reverse_alphabet[in[0]] < 0 || reverse_alphabet[in[1]] < 0) {
76 uint32_t val = reverse_alphabet[in[0]] << 18;
77 val |= reverse_alphabet[in[1]] << 12;
78 if (in[2] == '=' || in[2] == '\0') {
79 if (in[2] == '=' && (in[3] != '=' || in[4] != '\0'))
83 } else if (in[3] == '=' || in[3] == '\0') {
84 if (in[3] == '=' && in[4] != '\0')
87 val |= reverse_alphabet[in[2]];
90 val |= reverse_alphabet[in[2]] << 6;
91 val |= reverse_alphabet[in[3]];
108 uint8_t *decode_base64(const char *string, size_t *len)
110 static const int8_t reverse_alphabet[256] = {
111 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
112 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
113 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63,
114 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1,
115 -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
116 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
117 -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
118 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -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 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
124 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
125 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
126 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
128 return decode_base64_generic(string, len, reverse_alphabet);
131 uint8_t *decode_base64url(const char *string, size_t *len)
133 static const int8_t reverse_alphabet[256] = {
134 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
135 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
136 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1,
137 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1,
138 -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
139 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, 63,
140 -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
141 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -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 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
147 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
148 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
149 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
151 return decode_base64_generic(string, len, reverse_alphabet);
154 uint8_t *decode_base16(const char *string, size_t *len)
156 *len = strlen(string) / 2;
157 uint8_t *buffer = malloc(*len);
161 for (size_t i = 0; i < *len; ++i) {
162 char c = string[i * 2];
163 if (c >= '0' && c <= '9') {
164 buffer[i] = (c - '0') << 4;
165 } else if ((c | 0x20) >= 'a' && (c | 0x20) <= 'f') {
166 buffer[i] = ((c | 0x20) - 'a' + 10) << 4;
172 c = string[i * 2 + 1];
173 if (c >= '0' && c <= '9') {
174 buffer[i] |= (c - '0');
175 } else if ((c | 0x20) >= 'a' && (c | 0x20) <= 'f') {
176 buffer[i] |= ((c | 0x20) - 'a' + 10);
186 size_t get_cjson_size_limited(cJSON *container)
188 // cJSON_GetArraySize is O(n), so don't go too far
191 for (item = container->child; item; item = item->next) {
193 return CborIndefiniteLength;
198 cJSON *get_meta_data(cJSON *object, cJSON *item)
203 if (asprintf(&metadatakey, "%s%s", item->string, meta_data_marker) < 0 || metadatakey == NULL)
205 meta = cJSON_GetObjectItem(object, metadatakey);
210 struct MetaData parse_meta_data(cJSON *md)
212 struct MetaData result = { 0, {NULL}, CborInvalidType, false };
213 if (md == NULL || md->type != cJSON_Object)
216 for (md = md->child; md; md = md->next) {
217 if (strcmp(md->string, "tag") == 0) {
218 if (md->type != cJSON_String || sscanf(md->valuestring, "%" PRIu64, &result.tag) < 0)
219 fprintf(stderr, "json2cbor: could not parse tag: %s\n", md->valuestring);
221 result.tagged = true;
222 } else if (strcmp(md->string, "t") == 0) {
223 result.t = md->valueint;
224 } else if (strcmp(md->string, "v") == 0) {
225 if (md->type == cJSON_Number)
226 result.simpleType = md->valueint;
228 result.v = md->valuestring;
234 CborError decode_json(cJSON *json, CborEncoder *encoder);
235 CborError decode_json_with_metadata(cJSON *item, CborEncoder *encoder, struct MetaData md)
238 case CborIntegerType: {
239 // integer that has more than 53 bits of precision
241 bool positive = *md.v++ == '+';
242 if (sscanf(md.v, "%" PRIx64, &v) < 0) {
243 fprintf(stderr, "json2cbor: could not parse number: %s\n", md.v);
246 return positive ? cbor_encode_uint(encoder, v) : cbor_encode_negative_int(encoder, v);
249 case CborByteStringType: {
252 if (md.tag == CborExpectedBase64Tag)
253 data = decode_base64(item->valuestring, &len);
254 else if (md.tag == CborExpectedBase16Tag)
255 data = decode_base16(item->valuestring, &len);
256 else if (md.tag == CborNegativeBignumTag)
257 data = decode_base64url(item->valuestring + 1, &len);
259 data = decode_base64url(item->valuestring, &len);
262 CborError err = cbor_encode_byte_string(encoder, data, len);
266 fprintf(stderr, "json2cbor: could not decode encoded byte string: %s\n", item->valuestring);
271 return cbor_encode_simple_value(encoder, md.simpleType);
273 case CborUndefinedType:
274 return cbor_encode_undefined(encoder);
276 case CborHalfFloatType:
278 case CborDoubleType: {
282 v = item->valuedouble;
283 } else if (strcmp(md.v, "nan") == 0) {
285 } else if (strcmp(md.v, "-inf") == 0) {
287 } else if (strcmp(md.v, "inf") == 0) {
290 fprintf(stderr, "json2cbor: invalid floating-point value: %s\n", md.v);
294 // we can't get an OOM here because the metadata makes up for space
295 // (the smallest metadata is "$cbor":{"t":250} (17 bytes)
296 return (md.t == CborDoubleType) ? cbor_encode_double(encoder, v) :
297 (md.t == CborFloatType) ? cbor_encode_float(encoder, v) :
298 (half = encode_half(v), cbor_encode_half_float(encoder, &half));
302 fprintf(stderr, "json2cbor: invalid CBOR type: %d\n", md.t);
303 case CborInvalidType:
307 return decode_json(item, encoder);
310 CborError decode_json(cJSON *json, CborEncoder *encoder)
312 CborEncoder container;
316 switch (json->type) {
319 return cbor_encode_boolean(encoder, json->type == cJSON_True);
322 return cbor_encode_null(encoder);
325 if ((double)json->valueint == json->valuedouble)
326 return cbor_encode_int(encoder, json->valueint);
328 // the only exception that JSON is larger: floating point numbers
329 container = *encoder; // save the state
330 err = cbor_encode_double(encoder, json->valuedouble);
332 if (err == CborErrorOutOfMemory) {
334 uint8_t *newbuffer = realloc(buffer, buffersize);
335 if (newbuffer == NULL)
338 *encoder = container; // restore state
339 encoder->ptr = newbuffer + (container.ptr - buffer);
340 encoder->end = newbuffer + buffersize;
347 return cbor_encode_text_stringz(encoder, json->valuestring);
350 return CborErrorUnknownType;
353 err = cbor_encoder_create_array(encoder, &container, get_cjson_size_limited(json));
356 for (item = json->child; item; item = item->next) {
357 err = decode_json(item, &container);
361 return cbor_encoder_close_container_checked(encoder, &container);
364 err = cbor_encoder_create_map(encoder, &container,
365 usingMetaData ? CborIndefiniteLength : get_cjson_size_limited(json));
369 for (item = json->child ; item; item = item->next) {
370 if (usingMetaData && strlen(item->string) > strlen(meta_data_marker)
371 && strcmp(item->string + strlen(item->string) - 5, meta_data_marker) == 0)
374 err = cbor_encode_text_stringz(&container, item->string);
379 cJSON *meta = get_meta_data(json, item);
380 struct MetaData md = parse_meta_data(meta);
382 err = cbor_encode_tag(&container, md.tag);
387 err = decode_json_with_metadata(item, &container, md);
389 err = decode_json(item, &container);
395 return cbor_encoder_close_container_checked(encoder, &container);
399 int main(int argc, char **argv)
402 while ((c = getopt(argc, argv, "M")) != -1) {
405 usingMetaData = true;
409 fprintf(stderr, "Unknown option -%c.\n", optopt);
412 puts("Usage: json2cbor [OPTION]... [FILE]...\n"
413 "Reads JSON content from FILE and convert to CBOR.\n"
416 " -M Interpret metadata added by cbordump tool\n"
418 return c == '?' ? EXIT_FAILURE : EXIT_SUCCESS;
423 const char *fname = argv[optind];
424 if (fname && strcmp(fname, "-") != 0) {
425 in = fopen(fname, "r");
435 /* 1. read the file */
437 if (fseeko(in, 0, SEEK_END) == 0 && (fsize = ftello(in)) >= 0) {
438 buffersize = fsize + 1;
439 buffer = malloc(buffersize);
440 if (buffer == NULL) {
446 fsize = fread(buffer, 1, fsize, in);
447 buffer[fsize] = '\0';
449 const unsigned chunk = 16384;
452 do { // it the hard way
453 buffer = realloc(buffer, buffersize + chunk);
457 buffersize += fread(buffer + buffersize, 1, chunk, in);
458 } while (!feof(in) && !ferror(in));
459 buffer[buffersize] = '\0';
469 /* 2. parse as JSON */
470 cJSON *doc = cJSON_ParseWithOpts((char *)buffer, NULL, true);
472 fprintf(stderr, "json2cbor: %s: could not parse.\n", fname);
476 /* 3. encode as CBOR */
477 // We're going to reuse the buffer, as CBOR is usually shorter than the equivalent JSON
479 cbor_encoder_init(&encoder, buffer, buffersize, 0);
480 CborError err = decode_json(doc, &encoder);
485 fprintf(stderr, "json2cbor: %s: error encoding to CBOR: %s\n", fname,
486 cbor_error_string(err));
490 fwrite(buffer, 1, encoder.ptr - buffer, stdout);