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