xref: /freebsd/contrib/libcbor/src/cbor.c (revision e64bea71c21eb42e97aa615188ba91f6cce0d36d)
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 <stdbool.h>
9 #include <string.h>
10 
11 #include "cbor.h"
12 #include "cbor/internal/builder_callbacks.h"
13 #include "cbor/internal/loaders.h"
14 
15 cbor_item_t* cbor_load(cbor_data source, size_t source_size,
16                        struct cbor_load_result* result) {
17   /* Context stack */
18   static struct cbor_callbacks callbacks = {
19       .uint8 = &cbor_builder_uint8_callback,
20       .uint16 = &cbor_builder_uint16_callback,
21       .uint32 = &cbor_builder_uint32_callback,
22       .uint64 = &cbor_builder_uint64_callback,
23 
24       .negint8 = &cbor_builder_negint8_callback,
25       .negint16 = &cbor_builder_negint16_callback,
26       .negint32 = &cbor_builder_negint32_callback,
27       .negint64 = &cbor_builder_negint64_callback,
28 
29       .byte_string = &cbor_builder_byte_string_callback,
30       .byte_string_start = &cbor_builder_byte_string_start_callback,
31 
32       .string = &cbor_builder_string_callback,
33       .string_start = &cbor_builder_string_start_callback,
34 
35       .array_start = &cbor_builder_array_start_callback,
36       .indef_array_start = &cbor_builder_indef_array_start_callback,
37 
38       .map_start = &cbor_builder_map_start_callback,
39       .indef_map_start = &cbor_builder_indef_map_start_callback,
40 
41       .tag = &cbor_builder_tag_callback,
42 
43       .null = &cbor_builder_null_callback,
44       .undefined = &cbor_builder_undefined_callback,
45       .boolean = &cbor_builder_boolean_callback,
46       .float2 = &cbor_builder_float2_callback,
47       .float4 = &cbor_builder_float4_callback,
48       .float8 = &cbor_builder_float8_callback,
49       .indef_break = &cbor_builder_indef_break_callback};
50 
51   if (source_size == 0) {
52     result->error.code = CBOR_ERR_NODATA;
53     return NULL;
54   }
55   struct _cbor_stack stack = _cbor_stack_init();
56 
57   /* Target for callbacks */
58   struct _cbor_decoder_context context = (struct _cbor_decoder_context){
59       .stack = &stack, .creation_failed = false, .syntax_error = false};
60   struct cbor_decoder_result decode_result;
61   *result =
62       (struct cbor_load_result){.read = 0, .error = {.code = CBOR_ERR_NONE}};
63 
64   do {
65     if (source_size > result->read) { /* Check for overflows */
66       decode_result =
67           cbor_stream_decode(source + result->read, source_size - result->read,
68                              &callbacks, &context);
69     } else {
70       result->error = (struct cbor_error){.code = CBOR_ERR_NOTENOUGHDATA,
71                                           .position = result->read};
72       goto error;
73     }
74 
75     switch (decode_result.status) {
76       case CBOR_DECODER_FINISHED:
77         /* Everything OK */
78         {
79           result->read += decode_result.read;
80           break;
81         }
82       case CBOR_DECODER_NEDATA:
83         /* Data length doesn't match MTB expectation */
84         {
85           result->error.code = CBOR_ERR_NOTENOUGHDATA;
86           goto error;
87         }
88       case CBOR_DECODER_ERROR:
89         /* Reserved/malformed item */
90         {
91           result->error.code = CBOR_ERR_MALFORMATED;
92           goto error;
93         }
94     }
95 
96     if (context.creation_failed) {
97       /* Most likely unsuccessful allocation - our callback has failed */
98       result->error.code = CBOR_ERR_MEMERROR;
99       goto error;
100     } else if (context.syntax_error) {
101       result->error.code = CBOR_ERR_SYNTAXERROR;
102       goto error;
103     }
104   } while (stack.size > 0);
105 
106   return context.root;
107 
108 error:
109   result->error.position = result->read;
110   // debug_print("Failed with decoder error %d at %d\n", result->error.code,
111   // result->error.position); cbor_describe(stack.top->item, stdout);
112   /* Free the stack */
113   while (stack.size > 0) {
114     cbor_decref(&stack.top->item);
115     _cbor_stack_pop(&stack);
116   }
117   return NULL;
118 }
119 
120 static cbor_item_t* _cbor_copy_int(cbor_item_t* item, bool negative) {
121   CBOR_ASSERT(cbor_isa_uint(item) || cbor_isa_negint(item));
122   CBOR_ASSERT(cbor_int_get_width(item) >= CBOR_INT_8 &&
123               cbor_int_get_width(item) <= CBOR_INT_64);
124   cbor_item_t* res = NULL;
125   switch (cbor_int_get_width(item)) {
126     case CBOR_INT_8:
127       res = cbor_build_uint8(cbor_get_uint8(item));
128       break;
129     case CBOR_INT_16:
130       res = cbor_build_uint16(cbor_get_uint16(item));
131       break;
132     case CBOR_INT_32:
133       res = cbor_build_uint32(cbor_get_uint32(item));
134       break;
135     case CBOR_INT_64:
136       res = cbor_build_uint64(cbor_get_uint64(item));
137       break;
138   }
139 
140   if (negative) cbor_mark_negint(res);
141 
142   return res;
143 }
144 
145 static cbor_item_t* _cbor_copy_float_ctrl(cbor_item_t* item) {
146   CBOR_ASSERT(cbor_isa_float_ctrl(item));
147   CBOR_ASSERT(cbor_float_get_width(item) >= CBOR_FLOAT_0 &&
148               cbor_float_get_width(item) <= CBOR_FLOAT_64);
149   switch (cbor_float_get_width(item)) {
150     case CBOR_FLOAT_0:
151       return cbor_build_ctrl(cbor_ctrl_value(item));
152     case CBOR_FLOAT_16:
153       return cbor_build_float2(cbor_float_get_float2(item));
154     case CBOR_FLOAT_32:
155       return cbor_build_float4(cbor_float_get_float4(item));
156     case CBOR_FLOAT_64:
157       return cbor_build_float8(cbor_float_get_float8(item));
158     default:  // LCOV_EXCL_START
159       _CBOR_UNREACHABLE;
160       return NULL;  // LCOV_EXCL_START
161   }
162 }
163 
164 cbor_item_t* cbor_copy(cbor_item_t* item) {
165   CBOR_ASSERT_VALID_TYPE(cbor_typeof(item));
166   switch (cbor_typeof(item)) {
167     case CBOR_TYPE_UINT:
168       return _cbor_copy_int(item, false);
169     case CBOR_TYPE_NEGINT:
170       return _cbor_copy_int(item, true);
171     case CBOR_TYPE_BYTESTRING:
172       if (cbor_bytestring_is_definite(item)) {
173         return cbor_build_bytestring(cbor_bytestring_handle(item),
174                                      cbor_bytestring_length(item));
175       } else {
176         cbor_item_t* res = cbor_new_indefinite_bytestring();
177         if (res == NULL) {
178           return NULL;
179         }
180 
181         for (size_t i = 0; i < cbor_bytestring_chunk_count(item); i++) {
182           cbor_item_t* chunk_copy =
183               cbor_copy(cbor_bytestring_chunks_handle(item)[i]);
184           if (chunk_copy == NULL) {
185             cbor_decref(&res);
186             return NULL;
187           }
188           if (!cbor_bytestring_add_chunk(res, chunk_copy)) {
189             cbor_decref(&chunk_copy);
190             cbor_decref(&res);
191             return NULL;
192           }
193           cbor_decref(&chunk_copy);
194         }
195         return res;
196       }
197     case CBOR_TYPE_STRING:
198       if (cbor_string_is_definite(item)) {
199         return cbor_build_stringn((const char*)cbor_string_handle(item),
200                                   cbor_string_length(item));
201       } else {
202         cbor_item_t* res = cbor_new_indefinite_string();
203         if (res == NULL) {
204           return NULL;
205         }
206 
207         for (size_t i = 0; i < cbor_string_chunk_count(item); i++) {
208           cbor_item_t* chunk_copy =
209               cbor_copy(cbor_string_chunks_handle(item)[i]);
210           if (chunk_copy == NULL) {
211             cbor_decref(&res);
212             return NULL;
213           }
214           if (!cbor_string_add_chunk(res, chunk_copy)) {
215             cbor_decref(&chunk_copy);
216             cbor_decref(&res);
217             return NULL;
218           }
219           cbor_decref(&chunk_copy);
220         }
221         return res;
222       }
223     case CBOR_TYPE_ARRAY: {
224       cbor_item_t* res;
225       if (cbor_array_is_definite(item)) {
226         res = cbor_new_definite_array(cbor_array_size(item));
227       } else {
228         res = cbor_new_indefinite_array();
229       }
230       if (res == NULL) {
231         return NULL;
232       }
233 
234       for (size_t i = 0; i < cbor_array_size(item); i++) {
235         cbor_item_t* entry_copy = cbor_copy(cbor_move(cbor_array_get(item, i)));
236         if (entry_copy == NULL) {
237           cbor_decref(&res);
238           return NULL;
239         }
240         if (!cbor_array_push(res, entry_copy)) {
241           cbor_decref(&entry_copy);
242           cbor_decref(&res);
243           return NULL;
244         }
245         cbor_decref(&entry_copy);
246       }
247       return res;
248     }
249     case CBOR_TYPE_MAP: {
250       cbor_item_t* res;
251       if (cbor_map_is_definite(item)) {
252         res = cbor_new_definite_map(cbor_map_size(item));
253       } else {
254         res = cbor_new_indefinite_map();
255       }
256       if (res == NULL) {
257         return NULL;
258       }
259 
260       struct cbor_pair* it = cbor_map_handle(item);
261       for (size_t i = 0; i < cbor_map_size(item); i++) {
262         cbor_item_t* key_copy = cbor_copy(it[i].key);
263         if (key_copy == NULL) {
264           cbor_decref(&res);
265           return NULL;
266         }
267         cbor_item_t* value_copy = cbor_copy(it[i].value);
268         if (value_copy == NULL) {
269           cbor_decref(&res);
270           cbor_decref(&key_copy);
271           return NULL;
272         }
273         if (!cbor_map_add(res, (struct cbor_pair){.key = key_copy,
274                                                   .value = value_copy})) {
275           cbor_decref(&res);
276           cbor_decref(&key_copy);
277           cbor_decref(&value_copy);
278           return NULL;
279         }
280         cbor_decref(&key_copy);
281         cbor_decref(&value_copy);
282       }
283       return res;
284     }
285     case CBOR_TYPE_TAG: {
286       cbor_item_t* item_copy = cbor_copy(cbor_move(cbor_tag_item(item)));
287       if (item_copy == NULL) {
288         return NULL;
289       }
290       cbor_item_t* tag = cbor_build_tag(cbor_tag_value(item), item_copy);
291       cbor_decref(&item_copy);
292       return tag;
293     }
294     case CBOR_TYPE_FLOAT_CTRL:
295       return _cbor_copy_float_ctrl(item);
296     default:  // LCOV_EXCL_START
297       _CBOR_UNREACHABLE;
298       return NULL;  // LCOV_EXCL_STOP
299   }
300 }
301 
302 cbor_item_t* cbor_copy_definite(cbor_item_t* item) {
303   CBOR_ASSERT_VALID_TYPE(cbor_typeof(item));
304   switch (cbor_typeof(item)) {
305     case CBOR_TYPE_UINT:
306     case CBOR_TYPE_NEGINT:
307       return cbor_copy(item);
308     case CBOR_TYPE_BYTESTRING:
309       if (cbor_bytestring_is_definite(item)) {
310         return cbor_copy(item);
311       } else {
312         size_t total_length = 0;
313         for (size_t i = 0; i < cbor_bytestring_chunk_count(item); i++) {
314           total_length +=
315               cbor_bytestring_length(cbor_bytestring_chunks_handle(item)[i]);
316         }
317 
318         unsigned char* combined_data = _cbor_malloc(total_length);
319         if (combined_data == NULL) {
320           return NULL;
321         }
322 
323         size_t offset = 0;
324         for (size_t i = 0; i < cbor_bytestring_chunk_count(item); i++) {
325           cbor_item_t* chunk = cbor_bytestring_chunks_handle(item)[i];
326           memcpy(combined_data + offset, cbor_bytestring_handle(chunk),
327                  cbor_bytestring_length(chunk));
328           offset += cbor_bytestring_length(chunk);
329         }
330 
331         cbor_item_t* res = cbor_new_definite_bytestring();
332         cbor_bytestring_set_handle(res, combined_data, total_length);
333         return res;
334       }
335     case CBOR_TYPE_STRING:
336       if (cbor_string_is_definite(item)) {
337         return cbor_copy(item);
338       } else {
339         size_t total_length = 0;
340         for (size_t i = 0; i < cbor_string_chunk_count(item); i++) {
341           total_length +=
342               cbor_string_length(cbor_string_chunks_handle(item)[i]);
343         }
344 
345         unsigned char* combined_data = _cbor_malloc(total_length);
346         if (combined_data == NULL) {
347           return NULL;
348         }
349 
350         size_t offset = 0;
351         for (size_t i = 0; i < cbor_string_chunk_count(item); i++) {
352           cbor_item_t* chunk = cbor_string_chunks_handle(item)[i];
353           memcpy(combined_data + offset, cbor_string_handle(chunk),
354                  cbor_string_length(chunk));
355           offset += cbor_string_length(chunk);
356         }
357 
358         cbor_item_t* res = cbor_new_definite_string();
359         cbor_string_set_handle(res, combined_data, total_length);
360         return res;
361       }
362     case CBOR_TYPE_ARRAY: {
363       cbor_item_t* res = cbor_new_definite_array(cbor_array_size(item));
364       if (res == NULL) {
365         return NULL;
366       }
367 
368       for (size_t i = 0; i < cbor_array_size(item); i++) {
369         cbor_item_t* entry_copy =
370             cbor_copy_definite(cbor_array_handle(item)[i]);
371         if (entry_copy == NULL) {
372           cbor_decref(&res);
373           return NULL;
374         }
375         // Cannot fail since we have a definite array preallocated
376         // cppcheck-suppress syntaxError
377         const bool item_pushed _CBOR_UNUSED = cbor_array_push(res, entry_copy);
378         CBOR_ASSERT(item_pushed);
379         cbor_decref(&entry_copy);
380       }
381       return res;
382     }
383     case CBOR_TYPE_MAP: {
384       cbor_item_t* res;
385       res = cbor_new_definite_map(cbor_map_size(item));
386       if (res == NULL) {
387         return NULL;
388       }
389 
390       struct cbor_pair* it = cbor_map_handle(item);
391       for (size_t i = 0; i < cbor_map_size(item); i++) {
392         cbor_item_t* key_copy = cbor_copy_definite(it[i].key);
393         if (key_copy == NULL) {
394           cbor_decref(&res);
395           return NULL;
396         }
397         cbor_item_t* value_copy = cbor_copy_definite(it[i].value);
398         if (value_copy == NULL) {
399           cbor_decref(&res);
400           cbor_decref(&key_copy);
401           return NULL;
402         }
403         // Cannot fail since we have a definite map preallocated
404         // cppcheck-suppress syntaxError
405         const bool item_added _CBOR_UNUSED = cbor_map_add(
406             res, (struct cbor_pair){.key = key_copy, .value = value_copy});
407         CBOR_ASSERT(item_added);
408         cbor_decref(&key_copy);
409         cbor_decref(&value_copy);
410       }
411       return res;
412     }
413     case CBOR_TYPE_TAG: {
414       cbor_item_t* item_copy =
415           cbor_copy_definite(cbor_move(cbor_tag_item(item)));
416       if (item_copy == NULL) {
417         return NULL;
418       }
419       cbor_item_t* tag = cbor_build_tag(cbor_tag_value(item), item_copy);
420       cbor_decref(&item_copy);
421       return tag;
422     }
423     case CBOR_TYPE_FLOAT_CTRL:
424       return cbor_copy(item);
425     default:  // LCOV_EXCL_START
426       _CBOR_UNREACHABLE;
427       return NULL;  // LCOV_EXCL_STOP
428   }
429 }
430 
431 #if CBOR_PRETTY_PRINTER
432 
433 #include <inttypes.h>
434 #include <locale.h>
435 #include <wchar.h>
436 
437 #define __STDC_FORMAT_MACROS
438 
439 static int _pow(int b, int ex) {
440   if (ex == 0) return 1;
441   int res = b;
442   while (--ex > 0) res *= b;
443   return res;
444 }
445 
446 static void _cbor_type_marquee(FILE* out, char* label, int indent) {
447   fprintf(out, "%*.*s[%s] ", indent, indent, " ", label);
448 }
449 
450 static void _cbor_nested_describe(cbor_item_t* item, FILE* out, int indent) {
451   CBOR_ASSERT(cbor_typeof(item) >= CBOR_TYPE_UINT &&
452               cbor_typeof(item) <= CBOR_TYPE_FLOAT_CTRL);
453   const int indent_offset = 4;
454   switch (cbor_typeof(item)) {
455     case CBOR_TYPE_UINT: {
456       _cbor_type_marquee(out, "CBOR_TYPE_UINT", indent);
457       fprintf(out, "Width: %dB, ", _pow(2, cbor_int_get_width(item)));
458       fprintf(out, "Value: %" PRIu64 "\n", cbor_get_int(item));
459       break;
460     }
461     case CBOR_TYPE_NEGINT: {
462       _cbor_type_marquee(out, "CBOR_TYPE_NEGINT", indent);
463       fprintf(out, "Width: %dB, ", _pow(2, cbor_int_get_width(item)));
464       fprintf(out, "Value: -%" PRIu64 " - 1\n", cbor_get_int(item));
465       break;
466     }
467     case CBOR_TYPE_BYTESTRING: {
468       _cbor_type_marquee(out, "CBOR_TYPE_BYTESTRING", indent);
469       if (cbor_bytestring_is_indefinite(item)) {
470         fprintf(out, "Indefinite, Chunks: %zu, Chunk data:\n",
471                 cbor_bytestring_chunk_count(item));
472         for (size_t i = 0; i < cbor_bytestring_chunk_count(item); i++)
473           _cbor_nested_describe(cbor_bytestring_chunks_handle(item)[i], out,
474                                 indent + indent_offset);
475       } else {
476         const unsigned char* data = cbor_bytestring_handle(item);
477         fprintf(out, "Definite, Length: %zuB, Data:\n",
478                 cbor_bytestring_length(item));
479         fprintf(out, "%*s", indent + indent_offset, " ");
480         for (size_t i = 0; i < cbor_bytestring_length(item); i++)
481           fprintf(out, "%02x", (int)(data[i] & 0xff));
482         fprintf(out, "\n");
483       }
484       break;
485     }
486     case CBOR_TYPE_STRING: {
487       _cbor_type_marquee(out, "CBOR_TYPE_STRING", indent);
488       if (cbor_string_is_indefinite(item)) {
489         fprintf(out, "Indefinite, Chunks: %zu, Chunk data:\n",
490                 cbor_string_chunk_count(item));
491         for (size_t i = 0; i < cbor_string_chunk_count(item); i++)
492           _cbor_nested_describe(cbor_string_chunks_handle(item)[i], out,
493                                 indent + indent_offset);
494       } else {
495         fprintf(out, "Definite, Length: %zuB, Codepoints: %zu, Data:\n",
496                 cbor_string_length(item), cbor_string_codepoint_count(item));
497         fprintf(out, "%*s", indent + indent_offset, " ");
498         // Note: The string is not escaped, whitespace and control character
499         // will be printed in verbatim and take effect.
500         fwrite(cbor_string_handle(item), sizeof(unsigned char),
501                cbor_string_length(item), out);
502         fprintf(out, "\n");
503       }
504       break;
505     }
506     case CBOR_TYPE_ARRAY: {
507       _cbor_type_marquee(out, "CBOR_TYPE_ARRAY", indent);
508       if (cbor_array_is_definite(item)) {
509         fprintf(out, "Definite, Size: %zu, Contents:\n", cbor_array_size(item));
510       } else {
511         fprintf(out, "Indefinite, Size: %zu, Contents:\n",
512                 cbor_array_size(item));
513       }
514 
515       for (size_t i = 0; i < cbor_array_size(item); i++)
516         _cbor_nested_describe(cbor_array_handle(item)[i], out,
517                               indent + indent_offset);
518       break;
519     }
520     case CBOR_TYPE_MAP: {
521       _cbor_type_marquee(out, "CBOR_TYPE_MAP", indent);
522       if (cbor_map_is_definite(item)) {
523         fprintf(out, "Definite, Size: %zu, Contents:\n", cbor_map_size(item));
524       } else {
525         fprintf(out, "Indefinite, Size: %zu, Contents:\n", cbor_map_size(item));
526       }
527 
528       // TODO: Label and group keys and values
529       for (size_t i = 0; i < cbor_map_size(item); i++) {
530         fprintf(out, "%*sMap entry %zu\n", indent + indent_offset, " ", i);
531         _cbor_nested_describe(cbor_map_handle(item)[i].key, out,
532                               indent + 2 * indent_offset);
533         _cbor_nested_describe(cbor_map_handle(item)[i].value, out,
534                               indent + 2 * indent_offset);
535       }
536       break;
537     }
538     case CBOR_TYPE_TAG: {
539       _cbor_type_marquee(out, "CBOR_TYPE_TAG", indent);
540       fprintf(out, "Value: %" PRIu64 "\n", cbor_tag_value(item));
541       _cbor_nested_describe(cbor_move(cbor_tag_item(item)), out,
542                             indent + indent_offset);
543       break;
544     }
545     case CBOR_TYPE_FLOAT_CTRL: {
546       _cbor_type_marquee(out, "CBOR_TYPE_FLOAT_CTRL", indent);
547       if (cbor_float_ctrl_is_ctrl(item)) {
548         if (cbor_is_bool(item))
549           fprintf(out, "Bool: %s\n", cbor_get_bool(item) ? "true" : "false");
550         else if (cbor_is_undef(item))
551           fprintf(out, "Undefined\n");
552         else if (cbor_is_null(item))
553           fprintf(out, "Null\n");
554         else
555           fprintf(out, "Simple value: %d\n", cbor_ctrl_value(item));
556       } else {
557         fprintf(out, "Width: %dB, ", _pow(2, cbor_float_get_width(item)));
558         fprintf(out, "Value: %lf\n", cbor_float_get_float(item));
559       }
560       break;
561     }
562   }
563 }
564 
565 void cbor_describe(cbor_item_t* item, FILE* out) {
566   _cbor_nested_describe(item, out, 0);
567 }
568 
569 #endif
570