xref: /freebsd/contrib/libcbor/src/cbor.c (revision 9768746b)
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/malformated 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   /* Move the result before free */
104   cbor_item_t *result_item = context.root;
105   return result_item;
106 
107 error:
108   result->error.position = result->read;
109   // debug_print("Failed with decoder error %d at %d\n", result->error.code,
110   // result->error.position); cbor_describe(stack.top->item, stdout);
111   /* Free the stack */
112   while (stack.size > 0) {
113     cbor_decref(&stack.top->item);
114     _cbor_stack_pop(&stack);
115   }
116   return NULL;
117 }
118 
119 static cbor_item_t *_cbor_copy_int(cbor_item_t *item, bool negative) {
120   cbor_item_t *res;
121   switch (cbor_int_get_width(item)) {
122     case CBOR_INT_8:
123       res = cbor_build_uint8(cbor_get_uint8(item));
124       break;
125     case CBOR_INT_16:
126       res = cbor_build_uint16(cbor_get_uint16(item));
127       break;
128     case CBOR_INT_32:
129       res = cbor_build_uint32(cbor_get_uint32(item));
130       break;
131     case CBOR_INT_64:
132       res = cbor_build_uint64(cbor_get_uint64(item));
133       break;
134     default:
135       return NULL;
136   }
137 
138   if (negative) cbor_mark_negint(res);
139 
140   return res;
141 }
142 
143 static cbor_item_t *_cbor_copy_float_ctrl(cbor_item_t *item) {
144   switch (cbor_float_get_width(item)) {
145     case CBOR_FLOAT_0:
146       return cbor_build_ctrl(cbor_ctrl_value(item));
147     case CBOR_FLOAT_16:
148       return cbor_build_float2(cbor_float_get_float2(item));
149     case CBOR_FLOAT_32:
150       return cbor_build_float4(cbor_float_get_float4(item));
151     case CBOR_FLOAT_64:
152       return cbor_build_float8(cbor_float_get_float8(item));
153   }
154 
155   return NULL;
156 }
157 
158 cbor_item_t *cbor_copy(cbor_item_t *item) {
159   switch (cbor_typeof(item)) {
160     case CBOR_TYPE_UINT:
161       return _cbor_copy_int(item, false);
162     case CBOR_TYPE_NEGINT:
163       return _cbor_copy_int(item, true);
164     case CBOR_TYPE_BYTESTRING:
165       if (cbor_bytestring_is_definite(item)) {
166         return cbor_build_bytestring(cbor_bytestring_handle(item),
167                                      cbor_bytestring_length(item));
168       } else {
169         cbor_item_t *res = cbor_new_indefinite_bytestring();
170         for (size_t i = 0; i < cbor_bytestring_chunk_count(item); i++)
171           cbor_bytestring_add_chunk(
172               res,
173               cbor_move(cbor_copy(cbor_bytestring_chunks_handle(item)[i])));
174         return res;
175       }
176     case CBOR_TYPE_STRING:
177       if (cbor_string_is_definite(item)) {
178         return cbor_build_stringn((const char *)cbor_string_handle(item),
179                                   cbor_string_length(item));
180       } else {
181         cbor_item_t *res = cbor_new_indefinite_string();
182         for (size_t i = 0; i < cbor_string_chunk_count(item); i++)
183           cbor_string_add_chunk(
184               res, cbor_move(cbor_copy(cbor_string_chunks_handle(item)[i])));
185         return res;
186       }
187     case CBOR_TYPE_ARRAY: {
188       cbor_item_t *res;
189       if (cbor_array_is_definite(item))
190         res = cbor_new_definite_array(cbor_array_size(item));
191       else
192         res = cbor_new_indefinite_array();
193 
194       for (size_t i = 0; i < cbor_array_size(item); i++)
195         cbor_array_push(
196             res, cbor_move(cbor_copy(cbor_move(cbor_array_get(item, i)))));
197       return res;
198     }
199     case CBOR_TYPE_MAP: {
200       cbor_item_t *res;
201       if (cbor_map_is_definite(item))
202         res = cbor_new_definite_map(cbor_map_size(item));
203       else
204         res = cbor_new_indefinite_map();
205 
206       struct cbor_pair *it = cbor_map_handle(item);
207       for (size_t i = 0; i < cbor_map_size(item); i++)
208         cbor_map_add(res, (struct cbor_pair){
209                               .key = cbor_move(cbor_copy(it[i].key)),
210                               .value = cbor_move(cbor_copy(it[i].value))});
211       return res;
212     }
213     case CBOR_TYPE_TAG:
214       return cbor_build_tag(
215           cbor_tag_value(item),
216           cbor_move(cbor_copy(cbor_move(cbor_tag_item(item)))));
217     case CBOR_TYPE_FLOAT_CTRL:
218       return _cbor_copy_float_ctrl(item);
219   }
220 
221   return NULL;
222 }
223 
224 #if CBOR_PRETTY_PRINTER
225 
226 #include <inttypes.h>
227 #include <locale.h>
228 #include <stdlib.h>
229 #include <wchar.h>
230 
231 #define __STDC_FORMAT_MACROS
232 
233 static int _pow(int b, int ex) {
234   if (ex == 0) return 1;
235   int res = b;
236   while (--ex > 0) res *= b;
237   return res;
238 }
239 
240 static void _cbor_nested_describe(cbor_item_t *item, FILE *out, int indent) {
241   setlocale(LC_ALL, "");
242   switch (cbor_typeof(item)) {
243     case CBOR_TYPE_UINT: {
244       fprintf(out, "%*s[CBOR_TYPE_UINT] ", indent, " ");
245       fprintf(out, "Width: %dB, ", _pow(2, cbor_int_get_width(item)));
246       fprintf(out, "Value: %" PRIu64 "\n", cbor_get_int(item));
247       break;
248     };
249     case CBOR_TYPE_NEGINT: {
250       fprintf(out, "%*s[CBOR_TYPE_NEGINT] ", indent, " ");
251       fprintf(out, "Width: %dB, ", _pow(2, cbor_int_get_width(item)));
252       fprintf(out, "Value: -%" PRIu64 " -1\n", cbor_get_int(item));
253       break;
254     };
255     case CBOR_TYPE_BYTESTRING: {
256       fprintf(out, "%*s[CBOR_TYPE_BYTESTRING] ", indent, " ");
257       if (cbor_bytestring_is_indefinite(item)) {
258         fprintf(out, "Indefinite, with %zu chunks:\n",
259                 cbor_bytestring_chunk_count(item));
260         for (size_t i = 0; i < cbor_bytestring_chunk_count(item); i++)
261           _cbor_nested_describe(cbor_bytestring_chunks_handle(item)[i], out,
262                                 indent + 4);
263       } else {
264         fprintf(out, "Definite, length %zuB\n", cbor_bytestring_length(item));
265       }
266       break;
267     };
268     case CBOR_TYPE_STRING: {
269       fprintf(out, "%*s[CBOR_TYPE_STRING] ", indent, " ");
270       if (cbor_string_is_indefinite(item)) {
271         fprintf(out, "Indefinite, with %zu chunks:\n",
272                 cbor_string_chunk_count(item));
273         for (size_t i = 0; i < cbor_string_chunk_count(item); i++)
274           _cbor_nested_describe(cbor_string_chunks_handle(item)[i], out,
275                                 indent + 4);
276       } else {
277         fprintf(out, "Definite, length %zuB, %zu codepoints\n",
278                 cbor_string_length(item), cbor_string_codepoint_count(item));
279         /* Careful - this doesn't support multibyte characters! */
280         /* Printing those is out of the scope of this demo :) */
281         /* libICU is your friend */
282         fprintf(out, "%*s", indent + 4, " ");
283         /* XXX: no null at the end -> confused vprintf */
284         fwrite(cbor_string_handle(item), (int)cbor_string_length(item), 1, out);
285         fprintf(out, "\n");
286       }
287       break;
288     };
289     case CBOR_TYPE_ARRAY: {
290       fprintf(out, "%*s[CBOR_TYPE_ARRAY] ", indent, " ");
291       if (cbor_array_is_definite(item)) {
292         fprintf(out, "Definite, size: %zu\n", cbor_array_size(item));
293       } else {
294         fprintf(out, "Indefinite, size:  %zu\n", cbor_array_size(item));
295       }
296 
297       for (size_t i = 0; i < cbor_array_size(item); i++)
298         _cbor_nested_describe(cbor_array_handle(item)[i], out, indent + 4);
299       break;
300     };
301     case CBOR_TYPE_MAP: {
302       fprintf(out, "%*s[CBOR_TYPE_MAP] ", indent, " ");
303       if (cbor_map_is_definite(item)) {
304         fprintf(out, "Definite, size: %zu\n", cbor_map_size(item));
305       } else {
306         fprintf(out, "Indefinite, size:  %zu\n", cbor_map_size(item));
307       }
308 
309       for (size_t i = 0; i < cbor_map_size(item); i++) {
310         _cbor_nested_describe(cbor_map_handle(item)[i].key, out, indent + 4);
311         _cbor_nested_describe(cbor_map_handle(item)[i].value, out, indent + 4);
312       }
313       break;
314     };
315     case CBOR_TYPE_TAG: {
316       fprintf(out, "%*s[CBOR_TYPE_TAG] ", indent, " ");
317       fprintf(out, "Value: %" PRIu64 "\n", cbor_tag_value(item));
318       _cbor_nested_describe(cbor_move(cbor_tag_item(item)), out, indent + 4);
319       break;
320     };
321     case CBOR_TYPE_FLOAT_CTRL: {
322       fprintf(out, "%*s[CBOR_TYPE_FLOAT_CTRL] ", indent, " ");
323       if (cbor_float_ctrl_is_ctrl(item)) {
324         if (cbor_is_bool(item))
325           fprintf(out, "Bool: %s\n", cbor_get_bool(item) ? "true" : "false");
326         else if (cbor_is_undef(item))
327           fprintf(out, "Undefined\n");
328         else if (cbor_is_null(item))
329           fprintf(out, "Null\n");
330         else
331           fprintf(out, "Simple value %d\n", cbor_ctrl_value(item));
332       } else {
333         fprintf(out, "Width: %dB, ", _pow(2, cbor_float_get_width(item)));
334         fprintf(out, "value: %lf\n", cbor_float_get_float(item));
335       }
336       break;
337     };
338   }
339 }
340 
341 void cbor_describe(cbor_item_t *item, FILE *out) {
342   _cbor_nested_describe(item, out, 0);
343 }
344 
345 #endif
346