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 ****************************************************************************/
27 #define _POSIX_C_SOURCE 200809L
30 #include "compilersupport_p.h"
31 #include "math_support_p.h"
40 extern FILE *open_memstream(char **bufptr, size_t *sizeptr);
42 enum ConversionStatusFlags {
43 TypeWasNotNative = 0x100, // anything but strings, boolean, null, arrays and maps
44 TypeWasTagged = 0x200,
45 NumberPrecisionWasLost = 0x400,
47 NumberWasInfinite = 0x1000,
48 NumberWasNegative = 0x2000, // always used with NumberWasInifite or NumberWasTooBig
53 typedef struct ConversionStatus {
55 uint64_t originalNumber;
59 static CborError value_to_json(FILE *out, CborValue *it, int flags, CborType type, ConversionStatus *status);
61 static CborError dump_bytestring_base16(char **result, CborValue *it)
63 static const char characters[] = "0123456789abcdef";
67 CborError err = cbor_value_calculate_string_length(it, &n);
71 // a Base16 (hex) output is twice as big as our buffer
72 buffer = (uint8_t *)malloc(n * 2 + 1);
73 *result = (char *)buffer;
75 // let cbor_value_copy_byte_string know we have an extra byte for the terminating NUL
77 err = cbor_value_copy_byte_string(it, buffer + n - 1, &n, it);
78 assert(err == CborNoError);
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];
88 static CborError generic_dump_base64(char **result, CborValue *it, const char alphabet[65])
91 uint8_t *buffer, *out, *in;
92 CborError err = cbor_value_calculate_string_length(it, &n);
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;
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;
105 // let cbor_value_copy_byte_string know we have an extra byte for the terminating NUL
107 err = cbor_value_copy_byte_string(it, in, &n, it);
108 assert(err == CborNoError);
110 uint_least32_t val = 0;
111 for (i = 0; n - i >= 3; i += 3) {
112 // read 3 bytes x 8 bits = 24 bits
116 __builtin_memcpy(&val, in + i - 1, sizeof(val));
117 val = cbor_ntohl(val);
120 val = (in[i] << 16) | (in[i + 1] << 8) | in[i + 2];
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];
130 // maybe 1 or 2 bytes left
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
136 __builtin_memcpy(&val16, in + i, sizeof(val16));
137 val = cbor_ntohs(val16);
139 val = (in[i] << 8) | in[i + 1];
143 // the 65th character in the alphabet is our filler: either '=' or '\0'
145 out[3] = alphabet[64];
147 // write the third char in 3 chars x 6 bits = 18 bits
148 out[2] = alphabet[(val >> 6) & 0x3f];
150 out[2] = alphabet[64]; // filler
152 out[1] = alphabet[(val >> 12) & 0x3f];
153 out[0] = alphabet[(val >> 18) & 0x3f];
161 static CborError dump_bytestring_base64(char **result, CborValue *it)
163 static const char alphabet[] = "ABCDEFGH" "IJKLMNOP" "QRSTUVWX" "YZabcdef"
164 "ghijklmn" "opqrstuv" "wxyz0123" "456789+/" "=";
165 return generic_dump_base64(result, it, alphabet);
168 static CborError dump_bytestring_base64url(char **result, CborValue *it)
170 static const char alphabet[] = "ABCDEFGH" "IJKLMNOP" "QRSTUVWX" "YZabcdef"
171 "ghijklmn" "opqrstuv" "wxyz0123" "456789-_";
172 return generic_dump_base64(result, it, alphabet);
175 static CborError add_value_metadata(FILE *out, CborType type, const ConversionStatus *status)
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);
183 if (fprintf(out, "\"tag\":\"%" PRIu64 "\"%s", status->lastTag,
184 flags & ~TypeWasTagged ? "," : "") < 0)
191 // print at least the type
192 if (fprintf(out, "\"t\":%d", type) < 0)
195 if (flags & NumberWasNaN)
196 if (fprintf(out, ",\"v\":\"nan\"") < 0)
198 if (flags & NumberWasInfinite)
199 if (fprintf(out, ",\"v\":\"%sinf\"", flags & NumberWasNegative ? "-" : "") < 0)
201 if (flags & NumberPrecisionWasLost)
202 if (fprintf(out, ",\"v\":\"%c%" PRIx64 "\"", flags & NumberWasNegative ? '-' : '+',
203 status->originalNumber) < 0)
205 if (type == CborSimpleType)
206 if (fprintf(out, ",\"v\":%d", (int)status->originalNumber) < 0)
211 static CborError find_tagged_type(CborValue *it, CborTag *tag, CborType *type)
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);
221 *type = cbor_value_get_type(it);
226 static CborError tagged_value_to_json(FILE *out, CborValue *it, int flags, ConversionStatus *status)
231 if (flags & CborConvertTagsToObjects) {
232 cbor_value_get_tag(it, &tag); // can't fail
233 err = cbor_value_advance_fixed(it);
237 if (fprintf(out, "{\"tag%" PRIu64 "\":", tag) < 0)
240 CborType type = cbor_value_get_type(it);
241 err = value_to_json(out, it, flags, type, status);
244 if (flags & CborConvertAddMetadata && status->flags) {
245 if (fprintf(out, ",\"tag%" PRIu64 "$cbor\":{", tag) < 0 ||
246 add_value_metadata(out, type, status) != CborNoError ||
250 if (fputc('}', out) < 0)
252 status->flags = TypeWasNotNative | CborTagType;
257 err = find_tagged_type(it, &status->lastTag, &type);
260 tag = status->lastTag;
262 // special handling of byte strings?
263 if (type == CborByteStringType && (flags & CborConvertByteStringsToBase64Url) == 0 &&
264 (tag == CborNegativeBignumTag || tag == CborExpectedBase16Tag || tag == CborExpectedBase64Tag)) {
268 if (tag == CborNegativeBignumTag) {
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);
278 err = fprintf(out, "\"%s%s\"", pre, str) < 0 ? CborErrorIO : CborNoError;
280 status->flags = TypeWasNotNative | TypeWasTagged | CborByteStringType;
284 // no special handling
285 err = value_to_json(out, it, flags, type, status);
286 status->flags |= TypeWasTagged | type;
290 static CborError stringify_map_key(char **key, CborValue *it, int flags, CborType type)
292 (void)flags; // unused
293 (void)type; // unused
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);
301 if (unlikely(fclose(memstream) < 0 || *key == NULL))
302 return CborErrorInternalError;
306 static CborError array_to_json(FILE *out, CborValue *it, int flags, ConversionStatus *status)
308 const char *comma = "";
309 while (!cbor_value_at_end(it)) {
310 if (fprintf(out, "%s", comma) < 0)
314 CborError err = value_to_json(out, it, flags, cbor_value_get_type(it), status);
321 static CborError map_to_json(FILE *out, CborValue *it, int flags, ConversionStatus *status)
323 const char *comma = "";
325 while (!cbor_value_at_end(it)) {
327 if (fprintf(out, "%s", comma) < 0)
331 CborType keyType = cbor_value_get_type(it);
332 if (likely(keyType == CborTextStringType)) {
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);
338 return CborErrorJsonObjectKeyNotString;
343 // first, print the key
344 if (fprintf(out, "\"%s\":", key) < 0)
347 // then, print the value
348 CborType valueType = cbor_value_get_type(it);
349 err = value_to_json(out, it, flags, valueType, status);
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)
357 if (!err && status->flags) {
358 if (fprintf(out, ",\"%s$cbor\":{", key) < 0 ||
359 add_value_metadata(out, valueType, status) != CborNoError ||
372 static CborError value_to_json(FILE *out, CborValue *it, int flags, CborType type, ConversionStatus *status)
382 err = cbor_value_enter_container(it, &recursed);
384 it->ptr = recursed.ptr;
385 return err; // parse error
387 if (fputc(type == CborArrayType ? '[' : '{', out) < 0)
390 err = (type == CborArrayType) ?
391 array_to_json(out, &recursed, flags, status) :
392 map_to_json(out, &recursed, flags, status);
394 it->ptr = recursed.ptr;
395 return err; // parse error
398 if (fputc(type == CborArrayType ? ']' : '}', out) < 0)
400 err = cbor_value_leave_container(it, &recursed);
402 return err; // parse error
404 status->flags = 0; // reset, there are never conversion errors for us
408 case CborIntegerType: {
409 double num; // JS numbers are IEEE double precision
411 cbor_value_get_raw_integer(it, &val); // can't fail
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;
421 if ((uint64_t)num != val) {
422 status->flags = NumberPrecisionWasLost;
423 status->originalNumber = val;
426 if (fprintf(out, "%.0f", num) < 0) // this number has no fraction, so no decimal points please
431 case CborByteStringType:
432 case CborTextStringType: {
434 if (type == CborByteStringType) {
435 err = dump_bytestring_base64url(&str, it);
436 status->flags = TypeWasNotNative;
439 err = cbor_value_dup_text_string(it, &str, &n, it);
443 err = (fprintf(out, "\"%s\"", str) < 0) ? CborErrorIO : CborNoError;
449 return tagged_value_to_json(out, it, flags, status);
451 case CborSimpleType: {
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)
462 if (fprintf(out, "null") < 0)
466 case CborUndefinedType:
467 status->flags = TypeWasNotNative;
468 if (fprintf(out, "\"undefined\"") < 0)
472 case CborBooleanType: {
474 cbor_value_get_boolean(it, &val); // can't fail
475 if (fprintf(out, val ? "true" : "false") < 0)
480 case CborDoubleType: {
485 status->flags = TypeWasNotNative;
486 cbor_value_get_float(it, &f);
490 case CborHalfFloatType:
491 status->flags = TypeWasNotNative;
492 cbor_value_get_half_float(it, &f16);
493 val = decode_half(f16);
495 cbor_value_get_double(it, &val);
498 int r = fpclassify(val);
499 if (r == FP_NAN || r == FP_INFINITE) {
500 if (fprintf(out, "null") < 0)
502 status->flags |= r == FP_NAN ? NumberWasNaN :
503 NumberWasInfinite | (val < 0 ? NumberWasNegative : 0);
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
511 // this number is definitely not a 64-bit integer
512 r = fprintf(out, "%." DBL_DECIMAL_DIG_STR "g", val);
520 case CborInvalidType:
521 return CborErrorUnknownType;
524 return cbor_value_advance_fixed(it);
527 CborError cbor_value_to_json_advance(FILE *out, CborValue *value, int flags)
529 ConversionStatus status;
530 return value_to_json(out, value, flags, cbor_value_get_type(value), &status);