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"
40 static const char meta_data_marker[] = "$cbor";
43 bool usingMetaData = false;
55 uint8_t *decode_base64_generic(const char *string, size_t *len, const int8_t reverse_alphabet[256])
57 *len = ((strlen(string) + 3) & ~3) * 3 / 4;
58 uint8_t *buffer = malloc(*len);
62 uint8_t *out = buffer;
63 const uint8_t *in = (const uint8_t *)string;
66 if (reverse_alphabet[in[0]] < 0 || reverse_alphabet[in[1]] < 0) {
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'))
79 } else if (in[3] == '=' || in[3] == '\0') {
80 if (in[3] == '=' && in[4] != '\0')
83 val |= reverse_alphabet[in[2]];
86 val |= reverse_alphabet[in[2]] << 6;
87 val |= reverse_alphabet[in[3]];
104 uint8_t *decode_base64(const char *string, size_t *len)
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
124 return decode_base64_generic(string, len, reverse_alphabet);
127 uint8_t *decode_base64url(const char *string, size_t *len)
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
147 return decode_base64_generic(string, len, reverse_alphabet);
150 uint8_t *decode_base16(const char *string, size_t *len)
152 *len = strlen(string) / 2;
153 uint8_t *buffer = malloc(*len);
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;
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);
182 size_t get_cjson_size_limited(cJSON *container)
184 // cJSON_GetArraySize is O(n), so don't go too far
187 for (item = container->child; item; item = item->next) {
189 return CborIndefiniteLength;
194 cJSON *get_meta_data(cJSON *object, cJSON *item)
199 if (asprintf(&metadatakey, "%s%s", item->string, meta_data_marker) < 0 || metadatakey == NULL)
201 meta = cJSON_GetObjectItem(object, metadatakey);
206 struct MetaData parse_meta_data(cJSON *md)
208 struct MetaData result = { 0, {NULL}, CborInvalidType, false };
209 if (md == NULL || md->type != cJSON_Object)
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);
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;
224 result.v = md->valuestring;
230 CborError decode_json(cJSON *json, CborEncoder *encoder);
231 CborError decode_json_with_metadata(cJSON *item, CborEncoder *encoder, struct MetaData md)
234 case CborIntegerType: {
235 // integer that has more than 53 bits of precision
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);
242 return positive ? cbor_encode_uint(encoder, v) : cbor_encode_negative_int(encoder, v);
245 case CborByteStringType: {
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);
255 data = decode_base64url(item->valuestring, &len);
258 CborError err = cbor_encode_byte_string(encoder, data, len);
262 fprintf(stderr, "json2cbor: could not decode encoded byte string: %s\n", item->valuestring);
267 return cbor_encode_simple_value(encoder, md.simpleType);
269 case CborUndefinedType:
270 return cbor_encode_undefined(encoder);
272 case CborHalfFloatType:
274 case CborDoubleType: {
278 v = item->valuedouble;
279 } else if (strcmp(md.v, "nan") == 0) {
281 } else if (strcmp(md.v, "-inf") == 0) {
283 } else if (strcmp(md.v, "inf") == 0) {
286 fprintf(stderr, "json2cbor: invalid floating-point value: %s\n", md.v);
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));
298 fprintf(stderr, "json2cbor: invalid CBOR type: %d\n", md.t);
299 case CborInvalidType:
303 return decode_json(item, encoder);
306 CborError decode_json(cJSON *json, CborEncoder *encoder)
308 CborEncoder container;
312 switch (json->type) {
315 return cbor_encode_boolean(encoder, json->type == cJSON_True);
318 return cbor_encode_null(encoder);
321 if ((double)json->valueint == json->valuedouble)
322 return cbor_encode_int(encoder, json->valueint);
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);
328 if (err == CborErrorOutOfMemory) {
330 uint8_t *newbuffer = realloc(buffer, buffersize);
331 if (newbuffer == NULL)
334 *encoder = container; // restore state
335 encoder->data.ptr = newbuffer + (container.data.ptr - buffer);
336 encoder->end = newbuffer + buffersize;
343 return cbor_encode_text_stringz(encoder, json->valuestring);
346 return CborErrorUnknownType;
349 err = cbor_encoder_create_array(encoder, &container, get_cjson_size_limited(json));
352 for (item = json->child; item; item = item->next) {
353 err = decode_json(item, &container);
357 return cbor_encoder_close_container_checked(encoder, &container);
360 err = cbor_encoder_create_map(encoder, &container,
361 usingMetaData ? CborIndefiniteLength : get_cjson_size_limited(json));
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)
370 err = cbor_encode_text_stringz(&container, item->string);
375 cJSON *meta = get_meta_data(json, item);
376 struct MetaData md = parse_meta_data(meta);
378 err = cbor_encode_tag(&container, md.tag);
383 err = decode_json_with_metadata(item, &container, md);
385 err = decode_json(item, &container);
391 return cbor_encoder_close_container_checked(encoder, &container);
395 int main(int argc, char **argv)
398 while ((c = getopt(argc, argv, "M")) != -1) {
401 usingMetaData = true;
405 fprintf(stderr, "Unknown option -%c.\n", optopt);
408 puts("Usage: json2cbor [OPTION]... [FILE]...\n"
409 "Reads JSON content from FILE and convert to CBOR.\n"
412 " -M Interpret metadata added by cbordump tool\n"
414 return c == '?' ? EXIT_FAILURE : EXIT_SUCCESS;
419 const char *fname = argv[optind];
420 if (fname && strcmp(fname, "-") != 0) {
421 in = fopen(fname, "r");
431 /* 1. read the file */
433 if (fseeko(in, 0, SEEK_END) == 0 && (fsize = ftello(in)) >= 0) {
434 buffersize = fsize + 1;
435 buffer = malloc(buffersize);
436 if (buffer == NULL) {
442 fsize = fread(buffer, 1, fsize, in);
443 buffer[fsize] = '\0';
445 const unsigned chunk = 16384;
448 do { // it the hard way
449 buffer = realloc(buffer, buffersize + chunk);
453 buffersize += fread(buffer + buffersize, 1, chunk, in);
454 } while (!feof(in) && !ferror(in));
455 buffer[buffersize] = '\0';
465 /* 2. parse as JSON */
466 cJSON *doc = cJSON_ParseWithOpts((char *)buffer, NULL, true);
468 fprintf(stderr, "json2cbor: %s: could not parse.\n", fname);
472 /* 3. encode as CBOR */
473 // We're going to reuse the buffer, as CBOR is usually shorter than the equivalent JSON
475 cbor_encoder_init(&encoder, buffer, buffersize, 0);
476 CborError err = decode_json(doc, &encoder);
481 fprintf(stderr, "json2cbor: %s: error encoding to CBOR: %s\n", fname,
482 cbor_error_string(err));
486 fwrite(buffer, 1, encoder.data.ptr - buffer, stdout);