Merge branch 'upstream' into tizen
[platform/upstream/iotivity.git] / extlibs / tinycbor / tinycbor / src / cborpretty.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 #include "cbor.h"
27 #include "compilersupport_p.h"
28
29 #include <float.h>
30 #include <inttypes.h>
31 #include <math.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35
36 static int hexDump(FILE *out, const uint8_t *buffer, size_t n)
37 {
38     while (n--) {
39         int r = fprintf(out, "%02" PRIx8, *buffer++);
40         if (r < 0)
41             return r;
42     }
43     return 0;   // should be n * 2, but we don't have the original n anymore
44 }
45
46 /* This function decodes buffer as UTF-8 and prints as escaped UTF-16.
47  * On UTF-8 decoding error, it returns CborErrorInvalidUtf8TextString */
48 static int utf8EscapedDump(FILE *out, const char *buffer, size_t n)
49 {
50     uint32_t uc;
51     while (n--) {
52         uc = (uint8_t)*buffer++;
53         if (uc < 0x80) {
54             // single-byte UTF-8
55             if (uc < 0x7f && uc >= 0x20 && uc != '\\' && uc != '"') {
56                 if (fprintf(out, "%c", (char)uc) < 0)
57                     return CborErrorIO;
58                 continue;
59             }
60
61             // print as an escape sequence
62             char escaped = (char)uc;
63             switch (uc) {
64             case '"':
65             case '\\':
66                 break;
67             case '\b':
68                 escaped = 'b';
69                 break;
70             case '\f':
71                 escaped = 'f';
72                 break;
73             case '\n':
74                 escaped = 'n';
75                 break;
76             case '\r':
77                 escaped = 'r';
78                 break;
79             case '\t':
80                 escaped = 't';
81                 break;
82             default:
83                 goto print_utf16;
84             }
85             if (fprintf(out, "\\%c", escaped) < 0)
86                 return CborErrorIO;
87             continue;
88         }
89
90         // multi-byte UTF-8, decode it
91         unsigned charsNeeded;
92         uint32_t min_uc;
93         if (unlikely(uc <= 0xC1))
94             return CborErrorInvalidUtf8TextString;
95         if (uc < 0xE0) {
96             /* two-byte UTF-8 */
97             charsNeeded = 2;
98             min_uc = 0x80;
99             uc &= 0x1f;
100         } else if (uc < 0xF0) {
101             /* three-byte UTF-8 */
102             charsNeeded = 3;
103             min_uc = 0x800;
104             uc &= 0x0f;
105         } else if (uc < 0xF5) {
106             /* four-byte UTF-8 */
107             charsNeeded = 4;
108             min_uc = 0x10000;
109             uc &= 0x07;
110         } else {
111             return CborErrorInvalidUtf8TextString;
112         }
113
114         if (n < charsNeeded - 1)
115             return CborErrorInvalidUtf8TextString;
116
117         // first continuation character
118         uint8_t b = (uint8_t)*buffer++;
119         if ((b & 0xc0) != 0x80)
120             return CborErrorInvalidUtf8TextString;
121         uc <<= 6;
122         uc |= b & 0x3f;
123
124         if (charsNeeded > 2) {
125             // second continuation character
126             b = (uint8_t)*buffer++;
127             if ((b & 0xc0) != 0x80)
128                 return CborErrorInvalidUtf8TextString;
129             uc <<= 6;
130             uc |= b & 0x3f;
131
132             if (charsNeeded > 3) {
133                 // third continuation character
134                 b = (uint8_t)*buffer++;
135                 if ((b & 0xc0) != 0x80)
136                     return CborErrorInvalidUtf8TextString;
137                 uc <<= 6;
138                 uc |= b & 0x3f;
139             }
140         }
141
142         // overlong sequence? surrogate pair? out or range?
143         if (uc < min_uc || uc - 0xd800U < 2048U || uc > 0x10ffff)
144             return CborErrorInvalidUtf8TextString;
145
146         // now print the sequence
147         if (charsNeeded > 3) {
148             // needs surrogate pairs
149             if (fprintf(out, "\\u%04" PRIX32 "\\u%04" PRIX32,
150                         (uc >> 10) + 0xd7c0,    // high surrogate
151                         (uc % 0x0400) + 0xdc00) < 0)
152                 return CborErrorIO;
153         } else {
154 print_utf16:
155             // no surrogate pair needed
156             if (fprintf(out, "\\u%04" PRIX32, uc) < 0)
157                 return CborErrorIO;
158         }
159     }
160     return CborNoError;
161 }
162
163 static CborError value_to_pretty(FILE *out, CborValue *it);
164 static CborError container_to_pretty(FILE *out, CborValue *it, CborType containerType)
165 {
166     const char *comma = "";
167     while (!cbor_value_at_end(it)) {
168         if (fprintf(out, "%s", comma) < 0)
169             return CborErrorIO;
170         comma = ", ";
171
172         CborError err = value_to_pretty(out, it);
173         if (err)
174             return err;
175
176         if (containerType == CborArrayType)
177             continue;
178
179         // map: that was the key, so get the value
180         if (fprintf(out, ": ") < 0)
181             return CborErrorIO;
182         err = value_to_pretty(out, it);
183         if (err)
184             return err;
185     }
186     return CborNoError;
187 }
188
189 static CborError value_to_pretty(FILE *out, CborValue *it)
190 {
191     CborError err;
192     CborType type = cbor_value_get_type(it);
193     switch (type) {
194     case CborArrayType:
195     case CborMapType: {
196         // recursive type
197         CborValue recursed;
198
199         if (fprintf(out, type == CborArrayType ? "[" : "{") < 0)
200             return CborErrorIO;
201         if (!cbor_value_is_length_known(it)) {
202             if (fprintf(out, "_ ") < 0)
203                 return CborErrorIO;
204         }
205
206         err = cbor_value_enter_container(it, &recursed);
207         if (err) {
208             it->ptr = recursed.ptr;
209             return err;       // parse error
210         }
211         err = container_to_pretty(out, &recursed, type);
212         if (err) {
213             it->ptr = recursed.ptr;
214             return err;       // parse error
215         }
216         err = cbor_value_leave_container(it, &recursed);
217         if (err)
218             return err;       // parse error
219
220         if (fprintf(out, type == CborArrayType ? "]" : "}") < 0)
221             return CborErrorIO;
222         return CborNoError;
223     }
224
225     case CborIntegerType: {
226         uint64_t val;
227         cbor_value_get_raw_integer(it, &val);    // can't fail
228
229         if (cbor_value_is_unsigned_integer(it)) {
230             if (fprintf(out, "%" PRIu64, val) < 0)
231                 return CborErrorIO;
232         } else {
233             // CBOR stores the negative number X as -1 - X
234             // (that is, -1 is stored as 0, -2 as 1 and so forth)
235             if (++val) {                // unsigned overflow may happen
236                 if (fprintf(out, "-%" PRIu64, val) < 0)
237                     return CborErrorIO;
238             } else {
239                 // overflown
240                 //   0xffff`ffff`ffff`ffff + 1 =
241                 // 0x1`0000`0000`0000`0000 = 18446744073709551616 (2^64)
242                 if (fprintf(out, "-18446744073709551616") < 0)
243                     return CborErrorIO;
244             }
245         }
246         break;
247     }
248
249     case CborByteStringType:{
250         size_t n = 0;
251         uint8_t *buffer;
252         err = cbor_value_dup_byte_string(it, &buffer, &n, it);
253         if (err)
254             return err;
255
256         bool failed = fprintf(out, "h'") < 0 || hexDump(out, buffer, n) < 0 || fprintf(out, "'") < 0;
257         free(buffer);
258         return failed ? CborErrorIO : CborNoError;
259     }
260
261     case CborTextStringType: {
262         size_t n = 0;
263         char *buffer;
264         err = cbor_value_dup_text_string(it, &buffer, &n, it);
265         if (err)
266             return err;
267
268         err = CborNoError;
269         bool failed = fprintf(out, "\"") < 0
270                       || (err = utf8EscapedDump(out, buffer, n)) != CborNoError
271                       || fprintf(out, "\"") < 0;
272         free(buffer);
273         return err != CborNoError ? err :
274                                     failed ? CborErrorIO : CborNoError;
275     }
276
277     case CborTagType: {
278         CborTag tag;
279         cbor_value_get_tag(it, &tag);       // can't fail
280         if (fprintf(out, "%" PRIu64 "(", tag) < 0)
281             return CborErrorIO;
282         err = cbor_value_advance_fixed(it);
283         if (err)
284             return err;
285         err = value_to_pretty(out, it);
286         if (err)
287             return err;
288         if (fprintf(out, ")") < 0)
289             return CborErrorIO;
290         return CborNoError;
291     }
292
293     case CborSimpleType: {
294         uint8_t simple_type;
295         cbor_value_get_simple_type(it, &simple_type);  // can't fail
296         if (fprintf(out, "simple(%" PRIu8 ")", simple_type) < 0)
297             return CborErrorIO;
298         break;
299     }
300
301     case CborNullType:
302         if (fprintf(out, "null") < 0)
303             return CborErrorIO;
304         break;
305
306     case CborUndefinedType:
307         if (fprintf(out, "undefined") < 0)
308             return CborErrorIO;
309         break;
310
311     case CborBooleanType: {
312         bool val;
313         cbor_value_get_boolean(it, &val);       // can't fail
314         if (fprintf(out, val ? "true" : "false") < 0)
315             return CborErrorIO;
316         break;
317     }
318
319     case CborDoubleType: {
320         const char *suffix;
321         double val;
322         if (false) {
323             float f;
324     case CborFloatType:
325             cbor_value_get_float(it, &f);
326             val = f;
327             suffix = "f";
328         } else if (false) {
329             uint16_t f16;
330     case CborHalfFloatType:
331             cbor_value_get_half_float(it, &f16);
332             val = decode_half(f16);
333             suffix = "f16";
334         } else {
335             cbor_value_get_double(it, &val);
336             suffix = "";
337         }
338
339         int r = fpclassify(val);
340         if (r == FP_NAN || r == FP_INFINITE)
341             suffix = "";
342
343         uint64_t ival = (uint64_t)fabs(val);
344         if (ival == fabs(val)) {
345             // this double value fits in a 64-bit integer, so show it as such
346             // (followed by a floating point suffix, to disambiguate)
347             r = fprintf(out, "%s%" PRIu64 ".%s", val < 0 ? "-" : "", ival, suffix);
348         } else {
349             // this number is definitely not a 64-bit integer
350             r = fprintf(out, "%." DBL_DECIMAL_DIG_STR "g%s", val, suffix);
351         }
352         if (r < 0)
353             return CborErrorIO;
354         break;
355     }
356
357     case CborInvalidType:
358         if (fprintf(out, "invalid") < 0)
359             return CborErrorIO;
360         return CborErrorUnknownType;
361     }
362
363     err = cbor_value_advance_fixed(it);
364     return err;
365 }
366
367 CborError cbor_value_to_pretty_advance(FILE *out, CborValue *value)
368 {
369     return value_to_pretty(out, value);
370 }