xref: /freebsd/contrib/libcbor/src/cbor.c (revision bdd1243d)
1 /*
2  * Copyright (c) 2014-2020 Pavel Kalvoda <me@pavelkalvoda.com>
3  *
4  * libcbor is free software; you can redistribute it and/or modify
5  * it under the terms of the MIT license. See LICENSE for details.
6  */
7 
8 #include "cbor.h"
9 #include "cbor/internal/builder_callbacks.h"
10 #include "cbor/internal/loaders.h"
11 
12 cbor_item_t *cbor_load(cbor_data source, size_t source_size,
13                        struct cbor_load_result *result) {
14   /* Context stack */
15   static struct cbor_callbacks callbacks = {
16       .uint8 = &cbor_builder_uint8_callback,
17       .uint16 = &cbor_builder_uint16_callback,
18       .uint32 = &cbor_builder_uint32_callback,
19       .uint64 = &cbor_builder_uint64_callback,
20 
21       .negint8 = &cbor_builder_negint8_callback,
22       .negint16 = &cbor_builder_negint16_callback,
23       .negint32 = &cbor_builder_negint32_callback,
24       .negint64 = &cbor_builder_negint64_callback,
25 
26       .byte_string = &cbor_builder_byte_string_callback,
27       .byte_string_start = &cbor_builder_byte_string_start_callback,
28 
29       .string = &cbor_builder_string_callback,
30       .string_start = &cbor_builder_string_start_callback,
31 
32       .array_start = &cbor_builder_array_start_callback,
33       .indef_array_start = &cbor_builder_indef_array_start_callback,
34 
35       .map_start = &cbor_builder_map_start_callback,
36       .indef_map_start = &cbor_builder_indef_map_start_callback,
37 
38       .tag = &cbor_builder_tag_callback,
39 
40       .null = &cbor_builder_null_callback,
41       .undefined = &cbor_builder_undefined_callback,
42       .boolean = &cbor_builder_boolean_callback,
43       .float2 = &cbor_builder_float2_callback,
44       .float4 = &cbor_builder_float4_callback,
45       .float8 = &cbor_builder_float8_callback,
46       .indef_break = &cbor_builder_indef_break_callback};
47 
48   if (source_size == 0) {
49     result->error.code = CBOR_ERR_NODATA;
50     return NULL;
51   }
52   struct _cbor_stack stack = _cbor_stack_init();
53 
54   /* Target for callbacks */
55   struct _cbor_decoder_context context = (struct _cbor_decoder_context){
56       .stack = &stack, .creation_failed = false, .syntax_error = false};
57   struct cbor_decoder_result decode_result;
58   *result =
59       (struct cbor_load_result){.read = 0, .error = {.code = CBOR_ERR_NONE}};
60 
61   do {
62     if (source_size > result->read) { /* Check for overflows */
63       decode_result =
64           cbor_stream_decode(source + result->read, source_size - result->read,
65                              &callbacks, &context);
66     } else {
67       result->error = (struct cbor_error){.code = CBOR_ERR_NOTENOUGHDATA,
68                                           .position = result->read};
69       goto error;
70     }
71 
72     switch (decode_result.status) {
73       case CBOR_DECODER_FINISHED:
74         /* Everything OK */
75         {
76           result->read += decode_result.read;
77           break;
78         }
79       case CBOR_DECODER_NEDATA:
80         /* Data length doesn't match MTB expectation */
81         {
82           result->error.code = CBOR_ERR_NOTENOUGHDATA;
83           goto error;
84         }
85       case CBOR_DECODER_ERROR:
86         /* Reserved/malformed item */
87         {
88           result->error.code = CBOR_ERR_MALFORMATED;
89           goto error;
90         }
91     }
92 
93     if (context.creation_failed) {
94       /* Most likely unsuccessful allocation - our callback has failed */
95       result->error.code = CBOR_ERR_MEMERROR;
96       goto error;
97     } else if (context.syntax_error) {
98       result->error.code = CBOR_ERR_SYNTAXERROR;
99       goto error;
100     }
101   } while (stack.size > 0);
102 
103   return context.root;
104 
105 error:
106   result->error.position = result->read;
107   // debug_print("Failed with decoder error %d at %d\n", result->error.code,
108   // result->error.position); cbor_describe(stack.top->item, stdout);
109   /* Free the stack */
110   while (stack.size > 0) {
111     cbor_decref(&stack.top->item);
112     _cbor_stack_pop(&stack);
113   }
114   return NULL;
115 }
116 
117 static cbor_item_t *_cbor_copy_int(cbor_item_t *item, bool negative) {
118   cbor_item_t *res;
119   switch (cbor_int_get_width(item)) {
120     case CBOR_INT_8:
121       res = cbor_build_uint8(cbor_get_uint8(item));
122       break;
123     case CBOR_INT_16:
124       res = cbor_build_uint16(cbor_get_uint16(item));
125       break;
126     case CBOR_INT_32:
127       res = cbor_build_uint32(cbor_get_uint32(item));
128       break;
129     case CBOR_INT_64:
130       res = cbor_build_uint64(cbor_get_uint64(item));
131       break;
132   }
133 
134   if (negative) cbor_mark_negint(res);
135 
136   return res;
137 }
138 
139 static cbor_item_t *_cbor_copy_float_ctrl(cbor_item_t *item) {
140   // cppcheck-suppress missingReturn
141   switch (cbor_float_get_width(item)) {
142     case CBOR_FLOAT_0:
143       return cbor_build_ctrl(cbor_ctrl_value(item));
144     case CBOR_FLOAT_16:
145       return cbor_build_float2(cbor_float_get_float2(item));
146     case CBOR_FLOAT_32:
147       return cbor_build_float4(cbor_float_get_float4(item));
148     case CBOR_FLOAT_64:
149       return cbor_build_float8(cbor_float_get_float8(item));
150   }
151 }
152 
153 cbor_item_t *cbor_copy(cbor_item_t *item) {
154   // cppcheck-suppress missingReturn
155   switch (cbor_typeof(item)) {
156     case CBOR_TYPE_UINT:
157       return _cbor_copy_int(item, false);
158     case CBOR_TYPE_NEGINT:
159       return _cbor_copy_int(item, true);
160     case CBOR_TYPE_BYTESTRING:
161       if (cbor_bytestring_is_definite(item)) {
162         return cbor_build_bytestring(cbor_bytestring_handle(item),
163                                      cbor_bytestring_length(item));
164       } else {
165         cbor_item_t *res = cbor_new_indefinite_bytestring();
166         if (res == NULL) {
167           return NULL;
168         }
169 
170         for (size_t i = 0; i < cbor_bytestring_chunk_count(item); i++) {
171           cbor_item_t *chunk_copy =
172               cbor_copy(cbor_bytestring_chunks_handle(item)[i]);
173           if (chunk_copy == NULL) {
174             cbor_decref(&res);
175             return NULL;
176           }
177           if (!cbor_bytestring_add_chunk(res, chunk_copy)) {
178             cbor_decref(&chunk_copy);
179             cbor_decref(&res);
180             return NULL;
181           }
182           cbor_decref(&chunk_copy);
183         }
184         return res;
185       }
186     case CBOR_TYPE_STRING:
187       if (cbor_string_is_definite(item)) {
188         return cbor_build_stringn((const char *)cbor_string_handle(item),
189                                   cbor_string_length(item));
190       } else {
191         cbor_item_t *res = cbor_new_indefinite_string();
192         if (res == NULL) {
193           return NULL;
194         }
195 
196         for (size_t i = 0; i < cbor_string_chunk_count(item); i++) {
197           cbor_item_t *chunk_copy =
198               cbor_copy(cbor_string_chunks_handle(item)[i]);
199           if (chunk_copy == NULL) {
200             cbor_decref(&res);
201             return NULL;
202           }
203           if (!cbor_string_add_chunk(res, chunk_copy)) {
204             cbor_decref(&chunk_copy);
205             cbor_decref(&res);
206             return NULL;
207           }
208           cbor_decref(&chunk_copy);
209         }
210         return res;
211       }
212     case CBOR_TYPE_ARRAY: {
213       cbor_item_t *res;
214       if (cbor_array_is_definite(item)) {
215         res = cbor_new_definite_array(cbor_array_size(item));
216       } else {
217         res = cbor_new_indefinite_array();
218       }
219       if (res == NULL) {
220         return NULL;
221       }
222 
223       for (size_t i = 0; i < cbor_array_size(item); i++) {
224         cbor_item_t *entry_copy = cbor_copy(cbor_move(cbor_array_get(item, i)));
225         if (entry_copy == NULL) {
226           cbor_decref(&res);
227           return NULL;
228         }
229         if (!cbor_array_push(res, entry_copy)) {
230           cbor_decref(&entry_copy);
231           cbor_decref(&res);
232           return NULL;
233         }
234         cbor_decref(&entry_copy);
235       }
236       return res;
237     }
238     case CBOR_TYPE_MAP: {
239       cbor_item_t *res;
240       if (cbor_map_is_definite(item)) {
241         res = cbor_new_definite_map(cbor_map_size(item));
242       } else {
243         res = cbor_new_indefinite_map();
244       }
245       if (res == NULL) {
246         return NULL;
247       }
248 
249       struct cbor_pair *it = cbor_map_handle(item);
250       for (size_t i = 0; i < cbor_map_size(item); i++) {
251         cbor_item_t *key_copy = cbor_copy(it[i].key);
252         if (key_copy == NULL) {
253           cbor_decref(&res);
254           return NULL;
255         }
256         cbor_item_t *value_copy = cbor_copy(it[i].value);
257         if (value_copy == NULL) {
258           cbor_decref(&res);
259           cbor_decref(&key_copy);
260           return NULL;
261         }
262         if (!cbor_map_add(res, (struct cbor_pair){.key = key_copy,
263                                                   .value = value_copy})) {
264           cbor_decref(&res);
265           cbor_decref(&key_copy);
266           cbor_decref(&value_copy);
267           return NULL;
268         }
269         cbor_decref(&key_copy);
270         cbor_decref(&value_copy);
271       }
272       return res;
273     }
274     case CBOR_TYPE_TAG: {
275       cbor_item_t *item_copy = cbor_copy(cbor_move(cbor_tag_item(item)));
276       if (item_copy == NULL) {
277         return NULL;
278       }
279       cbor_item_t *tag = cbor_build_tag(cbor_tag_value(item), item_copy);
280       cbor_decref(&item_copy);
281       return tag;
282     }
283     case CBOR_TYPE_FLOAT_CTRL:
284       return _cbor_copy_float_ctrl(item);
285   }
286 }
287 
288 #if CBOR_PRETTY_PRINTER
289 
290 #include <inttypes.h>
291 #include <locale.h>
292 #include <stdlib.h>
293 #include <wchar.h>
294 
295 #define __STDC_FORMAT_MACROS
296 
297 static int _pow(int b, int ex) {
298   if (ex == 0) return 1;
299   int res = b;
300   while (--ex > 0) res *= b;
301   return res;
302 }
303 
304 static void _cbor_nested_describe(cbor_item_t *item, FILE *out, int indent) {
305   setlocale(LC_ALL, "");
306   switch (cbor_typeof(item)) {
307     case CBOR_TYPE_UINT: {
308       fprintf(out, "%*s[CBOR_TYPE_UINT] ", indent, " ");
309       fprintf(out, "Width: %dB, ", _pow(2, cbor_int_get_width(item)));
310       fprintf(out, "Value: %" PRIu64 "\n", cbor_get_int(item));
311       break;
312     }
313     case CBOR_TYPE_NEGINT: {
314       fprintf(out, "%*s[CBOR_TYPE_NEGINT] ", indent, " ");
315       fprintf(out, "Width: %dB, ", _pow(2, cbor_int_get_width(item)));
316       fprintf(out, "Value: -%" PRIu64 " -1\n", cbor_get_int(item));
317       break;
318     }
319     case CBOR_TYPE_BYTESTRING: {
320       fprintf(out, "%*s[CBOR_TYPE_BYTESTRING] ", indent, " ");
321       if (cbor_bytestring_is_indefinite(item)) {
322         fprintf(out, "Indefinite, with %zu chunks:\n",
323                 cbor_bytestring_chunk_count(item));
324         for (size_t i = 0; i < cbor_bytestring_chunk_count(item); i++)
325           _cbor_nested_describe(cbor_bytestring_chunks_handle(item)[i], out,
326                                 indent + 4);
327       } else {
328         fprintf(out, "Definite, length %zuB\n", cbor_bytestring_length(item));
329       }
330       break;
331     }
332     case CBOR_TYPE_STRING: {
333       fprintf(out, "%*s[CBOR_TYPE_STRING] ", indent, " ");
334       if (cbor_string_is_indefinite(item)) {
335         fprintf(out, "Indefinite, with %zu chunks:\n",
336                 cbor_string_chunk_count(item));
337         for (size_t i = 0; i < cbor_string_chunk_count(item); i++)
338           _cbor_nested_describe(cbor_string_chunks_handle(item)[i], out,
339                                 indent + 4);
340       } else {
341         fprintf(out, "Definite, length %zuB, %zu codepoints\n",
342                 cbor_string_length(item), cbor_string_codepoint_count(item));
343         /* Careful - this doesn't support multibyte characters! */
344         /* Printing those is out of the scope of this demo :) */
345         /* libICU is your friend */
346         fprintf(out, "%*s", indent + 4, " ");
347         /* XXX: no null at the end -> confused vprintf */
348         fwrite(cbor_string_handle(item), (int)cbor_string_length(item), 1, out);
349         fprintf(out, "\n");
350       }
351       break;
352     }
353     case CBOR_TYPE_ARRAY: {
354       fprintf(out, "%*s[CBOR_TYPE_ARRAY] ", indent, " ");
355       if (cbor_array_is_definite(item)) {
356         fprintf(out, "Definite, size: %zu\n", cbor_array_size(item));
357       } else {
358         fprintf(out, "Indefinite, size:  %zu\n", cbor_array_size(item));
359       }
360 
361       for (size_t i = 0; i < cbor_array_size(item); i++)
362         _cbor_nested_describe(cbor_array_handle(item)[i], out, indent + 4);
363       break;
364     }
365     case CBOR_TYPE_MAP: {
366       fprintf(out, "%*s[CBOR_TYPE_MAP] ", indent, " ");
367       if (cbor_map_is_definite(item)) {
368         fprintf(out, "Definite, size: %zu\n", cbor_map_size(item));
369       } else {
370         fprintf(out, "Indefinite, size:  %zu\n", cbor_map_size(item));
371       }
372 
373       for (size_t i = 0; i < cbor_map_size(item); i++) {
374         _cbor_nested_describe(cbor_map_handle(item)[i].key, out, indent + 4);
375         _cbor_nested_describe(cbor_map_handle(item)[i].value, out, indent + 4);
376       }
377       break;
378     }
379     case CBOR_TYPE_TAG: {
380       fprintf(out, "%*s[CBOR_TYPE_TAG] ", indent, " ");
381       fprintf(out, "Value: %" PRIu64 "\n", cbor_tag_value(item));
382       _cbor_nested_describe(cbor_move(cbor_tag_item(item)), out, indent + 4);
383       break;
384     }
385     case CBOR_TYPE_FLOAT_CTRL: {
386       fprintf(out, "%*s[CBOR_TYPE_FLOAT_CTRL] ", indent, " ");
387       if (cbor_float_ctrl_is_ctrl(item)) {
388         if (cbor_is_bool(item))
389           fprintf(out, "Bool: %s\n", cbor_get_bool(item) ? "true" : "false");
390         else if (cbor_is_undef(item))
391           fprintf(out, "Undefined\n");
392         else if (cbor_is_null(item))
393           fprintf(out, "Null\n");
394         else
395           fprintf(out, "Simple value %d\n", cbor_ctrl_value(item));
396       } else {
397         fprintf(out, "Width: %dB, ", _pow(2, cbor_float_get_width(item)));
398         fprintf(out, "value: %lf\n", cbor_float_get_float(item));
399       }
400       break;
401     }
402   }
403 }
404 
405 void cbor_describe(cbor_item_t *item, FILE *out) {
406   _cbor_nested_describe(item, out, 0);
407 }
408 
409 #endif
410