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