xref: /freebsd/contrib/libcbor/src/cbor/internal/builder_callbacks.c (revision 9f23cbd6cae82fd77edfad7173432fa8dccd0a95)
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 "builder_callbacks.h"
9 
10 #include <string.h>
11 
12 #include "../arrays.h"
13 #include "../bytestrings.h"
14 #include "../common.h"
15 #include "../floats_ctrls.h"
16 #include "../ints.h"
17 #include "../maps.h"
18 #include "../strings.h"
19 #include "../tags.h"
20 #include "unicode.h"
21 
22 // `_cbor_builder_append` takes ownership of `item`. If adding the item to
23 // parent container fails, `item` will be deallocated to prevent memory.
24 void _cbor_builder_append(cbor_item_t *item,
25                           struct _cbor_decoder_context *ctx) {
26   if (ctx->stack->size == 0) {
27     /* Top level item */
28     ctx->root = item;
29     return;
30   }
31   /* Part of a bigger structure */
32   switch (ctx->stack->top->item->type) {
33     // Handle Arrays and Maps since they can contain subitems of any type.
34     // Byte/string construction from chunks is handled in the respective chunk
35     // handlers.
36     case CBOR_TYPE_ARRAY: {
37       if (cbor_array_is_definite(ctx->stack->top->item)) {
38         // We don't need an explicit check for whether the item still belongs
39         // into this array because if there are extra items, they will cause a
40         // syntax error when decoded.
41         CBOR_ASSERT(ctx->stack->top->subitems > 0);
42         // This should never happen since the definite array should be
43         // preallocated for the expected number of items.
44         if (!cbor_array_push(ctx->stack->top->item, item)) {
45           ctx->creation_failed = true;
46           cbor_decref(&item);
47           break;
48         }
49         cbor_decref(&item);
50         ctx->stack->top->subitems--;
51         if (ctx->stack->top->subitems == 0) {
52           cbor_item_t *stack_item = ctx->stack->top->item;
53           _cbor_stack_pop(ctx->stack);
54           _cbor_builder_append(stack_item, ctx);
55         }
56       } else {
57         /* Indefinite array, don't bother with subitems */
58         if (!cbor_array_push(ctx->stack->top->item, item)) {
59           ctx->creation_failed = true;
60         }
61         cbor_decref(&item);
62       }
63       break;
64     }
65     case CBOR_TYPE_MAP: {
66       // Handle both definite and indefinite maps the same initially.
67       // Note: We use 0 and 1 subitems to distinguish between keys and values in
68       // indefinite items
69       if (ctx->stack->top->subitems % 2) {
70         /* Odd record, this is a value */
71         if (!_cbor_map_add_value(ctx->stack->top->item, item)) {
72           ctx->creation_failed = true;
73           cbor_decref(&item);
74           break;
75         }
76       } else {
77         /* Even record, this is a key */
78         if (!_cbor_map_add_key(ctx->stack->top->item, item)) {
79           ctx->creation_failed = true;
80           cbor_decref(&item);
81           break;
82         }
83       }
84       cbor_decref(&item);
85       if (cbor_map_is_definite(ctx->stack->top->item)) {
86         CBOR_ASSERT(ctx->stack->top->subitems > 0);
87         ctx->stack->top->subitems--;
88         if (ctx->stack->top->subitems == 0) {
89           cbor_item_t *map_entry = ctx->stack->top->item;
90           _cbor_stack_pop(ctx->stack);
91           _cbor_builder_append(map_entry, ctx);
92         }
93       } else {
94         ctx->stack->top->subitems ^=
95             1; /* Flip the indicator for indefinite items */
96       }
97       break;
98     }
99     case CBOR_TYPE_TAG: {
100       CBOR_ASSERT(ctx->stack->top->subitems == 1);
101       cbor_tag_set_item(ctx->stack->top->item, item);
102       cbor_decref(&item); /* Give up on our reference */
103       cbor_item_t *tagged_item = ctx->stack->top->item;
104       _cbor_stack_pop(ctx->stack);
105       _cbor_builder_append(tagged_item, ctx);
106       break;
107     }
108     // We have an item to append but nothing to append it to.
109     default: {
110       cbor_decref(&item);
111       ctx->syntax_error = true;
112     }
113   }
114 }
115 
116 #define CHECK_RES(ctx, res)        \
117   do {                             \
118     if (res == NULL) {             \
119       ctx->creation_failed = true; \
120       return;                      \
121     }                              \
122   } while (0)
123 
124 // Check that the length fits into size_t. If not, we cannot possibly allocate
125 // the required memory and should fail fast.
126 #define CHECK_LENGTH(ctx, length)  \
127   do {                             \
128     if (length > SIZE_MAX) {       \
129       ctx->creation_failed = true; \
130       return;                      \
131     }                              \
132   } while (0)
133 
134 #define PUSH_CTX_STACK(ctx, res, subitems)                     \
135   do {                                                         \
136     if (_cbor_stack_push(ctx->stack, res, subitems) == NULL) { \
137       cbor_decref(&res);                                       \
138       ctx->creation_failed = true;                             \
139     }                                                          \
140   } while (0)
141 
142 void cbor_builder_uint8_callback(void *context, uint8_t value) {
143   struct _cbor_decoder_context *ctx = context;
144   cbor_item_t *res = cbor_new_int8();
145   CHECK_RES(ctx, res);
146   cbor_mark_uint(res);
147   cbor_set_uint8(res, value);
148   _cbor_builder_append(res, ctx);
149 }
150 
151 void cbor_builder_uint16_callback(void *context, uint16_t value) {
152   struct _cbor_decoder_context *ctx = context;
153   cbor_item_t *res = cbor_new_int16();
154   CHECK_RES(ctx, res);
155   cbor_mark_uint(res);
156   cbor_set_uint16(res, value);
157   _cbor_builder_append(res, ctx);
158 }
159 
160 void cbor_builder_uint32_callback(void *context, uint32_t value) {
161   struct _cbor_decoder_context *ctx = context;
162   cbor_item_t *res = cbor_new_int32();
163   CHECK_RES(ctx, res);
164   cbor_mark_uint(res);
165   cbor_set_uint32(res, value);
166   _cbor_builder_append(res, ctx);
167 }
168 
169 void cbor_builder_uint64_callback(void *context, uint64_t value) {
170   struct _cbor_decoder_context *ctx = context;
171   cbor_item_t *res = cbor_new_int64();
172   CHECK_RES(ctx, res);
173   cbor_mark_uint(res);
174   cbor_set_uint64(res, value);
175   _cbor_builder_append(res, ctx);
176 }
177 
178 void cbor_builder_negint8_callback(void *context, uint8_t value) {
179   struct _cbor_decoder_context *ctx = context;
180   cbor_item_t *res = cbor_new_int8();
181   CHECK_RES(ctx, res);
182   cbor_mark_negint(res);
183   cbor_set_uint8(res, value);
184   _cbor_builder_append(res, ctx);
185 }
186 
187 void cbor_builder_negint16_callback(void *context, uint16_t value) {
188   struct _cbor_decoder_context *ctx = context;
189   cbor_item_t *res = cbor_new_int16();
190   CHECK_RES(ctx, res);
191   cbor_mark_negint(res);
192   cbor_set_uint16(res, value);
193   _cbor_builder_append(res, ctx);
194 }
195 
196 void cbor_builder_negint32_callback(void *context, uint32_t value) {
197   struct _cbor_decoder_context *ctx = context;
198   cbor_item_t *res = cbor_new_int32();
199   CHECK_RES(ctx, res);
200   cbor_mark_negint(res);
201   cbor_set_uint32(res, value);
202   _cbor_builder_append(res, ctx);
203 }
204 
205 void cbor_builder_negint64_callback(void *context, uint64_t value) {
206   struct _cbor_decoder_context *ctx = context;
207   cbor_item_t *res = cbor_new_int64();
208   CHECK_RES(ctx, res);
209   cbor_mark_negint(res);
210   cbor_set_uint64(res, value);
211   _cbor_builder_append(res, ctx);
212 }
213 
214 void cbor_builder_byte_string_callback(void *context, cbor_data data,
215                                        uint64_t length) {
216   struct _cbor_decoder_context *ctx = context;
217   CHECK_LENGTH(ctx, length);
218   unsigned char *new_handle = _cbor_malloc(length);
219   if (new_handle == NULL) {
220     ctx->creation_failed = true;
221     return;
222   }
223 
224   memcpy(new_handle, data, length);
225   cbor_item_t *new_chunk = cbor_new_definite_bytestring();
226 
227   if (new_chunk == NULL) {
228     _cbor_free(new_handle);
229     ctx->creation_failed = true;
230     return;
231   }
232 
233   cbor_bytestring_set_handle(new_chunk, new_handle, length);
234 
235   // If an indef bytestring is on the stack, extend it (if it were closed, it
236   // would have been popped). Handle any syntax errors upstream.
237   if (ctx->stack->size > 0 && cbor_isa_bytestring(ctx->stack->top->item) &&
238       cbor_bytestring_is_indefinite(ctx->stack->top->item)) {
239     if (!cbor_bytestring_add_chunk(ctx->stack->top->item, new_chunk)) {
240       ctx->creation_failed = true;
241     }
242     cbor_decref(&new_chunk);
243   } else {
244     _cbor_builder_append(new_chunk, ctx);
245   }
246 }
247 
248 void cbor_builder_byte_string_start_callback(void *context) {
249   struct _cbor_decoder_context *ctx = context;
250   cbor_item_t *res = cbor_new_indefinite_bytestring();
251   CHECK_RES(ctx, res);
252   PUSH_CTX_STACK(ctx, res, 0);
253 }
254 
255 void cbor_builder_string_callback(void *context, cbor_data data,
256                                   uint64_t length) {
257   struct _cbor_decoder_context *ctx = context;
258   CHECK_LENGTH(ctx, length);
259   struct _cbor_unicode_status unicode_status;
260   uint64_t codepoint_count =
261       _cbor_unicode_codepoint_count(data, length, &unicode_status);
262 
263   if (unicode_status.status != _CBOR_UNICODE_OK) {
264     ctx->syntax_error = true;
265     return;
266   }
267   CBOR_ASSERT(codepoint_count <= length);
268 
269   unsigned char *new_handle = _cbor_malloc(length);
270 
271   if (new_handle == NULL) {
272     ctx->creation_failed = true;
273     return;
274   }
275 
276   memcpy(new_handle, data, length);
277   cbor_item_t *new_chunk = cbor_new_definite_string();
278   if (new_chunk == NULL) {
279     _cbor_free(new_handle);
280     ctx->creation_failed = true;
281     return;
282   }
283   cbor_string_set_handle(new_chunk, new_handle, length);
284   new_chunk->metadata.string_metadata.codepoint_count = codepoint_count;
285 
286   // If an indef string is on the stack, extend it (if it were closed, it would
287   // have been popped). Handle any syntax errors upstream.
288   if (ctx->stack->size > 0 && cbor_isa_string(ctx->stack->top->item) &&
289       cbor_string_is_indefinite(ctx->stack->top->item)) {
290     if (!cbor_string_add_chunk(ctx->stack->top->item, new_chunk)) {
291       ctx->creation_failed = true;
292     }
293     cbor_decref(&new_chunk);
294   } else {
295     _cbor_builder_append(new_chunk, ctx);
296   }
297 }
298 
299 void cbor_builder_string_start_callback(void *context) {
300   struct _cbor_decoder_context *ctx = context;
301   cbor_item_t *res = cbor_new_indefinite_string();
302   CHECK_RES(ctx, res);
303   PUSH_CTX_STACK(ctx, res, 0);
304 }
305 
306 void cbor_builder_array_start_callback(void *context, uint64_t size) {
307   struct _cbor_decoder_context *ctx = context;
308   CHECK_LENGTH(ctx, size);
309   cbor_item_t *res = cbor_new_definite_array(size);
310   CHECK_RES(ctx, res);
311   if (size > 0) {
312     PUSH_CTX_STACK(ctx, res, size);
313   } else {
314     _cbor_builder_append(res, ctx);
315   }
316 }
317 
318 void cbor_builder_indef_array_start_callback(void *context) {
319   struct _cbor_decoder_context *ctx = context;
320   cbor_item_t *res = cbor_new_indefinite_array();
321   CHECK_RES(ctx, res);
322   PUSH_CTX_STACK(ctx, res, 0);
323 }
324 
325 void cbor_builder_indef_map_start_callback(void *context) {
326   struct _cbor_decoder_context *ctx = context;
327   cbor_item_t *res = cbor_new_indefinite_map();
328   CHECK_RES(ctx, res);
329   PUSH_CTX_STACK(ctx, res, 0);
330 }
331 
332 void cbor_builder_map_start_callback(void *context, uint64_t size) {
333   struct _cbor_decoder_context *ctx = context;
334   CHECK_LENGTH(ctx, size);
335   cbor_item_t *res = cbor_new_definite_map(size);
336   CHECK_RES(ctx, res);
337   if (size > 0) {
338     PUSH_CTX_STACK(ctx, res, size * 2);
339   } else {
340     _cbor_builder_append(res, ctx);
341   }
342 }
343 
344 /**
345  * Is the (partially constructed) item indefinite?
346  */
347 bool _cbor_is_indefinite(cbor_item_t *item) {
348   switch (item->type) {
349     case CBOR_TYPE_BYTESTRING:
350       return cbor_bytestring_is_indefinite(item);
351     case CBOR_TYPE_STRING:
352       return cbor_string_is_indefinite(item);
353     case CBOR_TYPE_ARRAY:
354       return cbor_array_is_indefinite(item);
355     case CBOR_TYPE_MAP:
356       return cbor_map_is_indefinite(item);
357     default:
358       return false;
359   }
360 }
361 
362 void cbor_builder_indef_break_callback(void *context) {
363   struct _cbor_decoder_context *ctx = context;
364   /* There must be an item to break out of*/
365   if (ctx->stack->size > 0) {
366     cbor_item_t *item = ctx->stack->top->item;
367     if (_cbor_is_indefinite(
368             item) && /* Only indefinite items can be terminated by 0xFF */
369         /* Special case: we cannot append up if an indefinite map is incomplete
370            (we are expecting a value). */
371         (item->type != CBOR_TYPE_MAP || ctx->stack->top->subitems % 2 == 0)) {
372       _cbor_stack_pop(ctx->stack);
373       _cbor_builder_append(item, ctx);
374       return;
375     }
376   }
377 
378   ctx->syntax_error = true;
379 }
380 
381 void cbor_builder_float2_callback(void *context, float value) {
382   struct _cbor_decoder_context *ctx = context;
383   cbor_item_t *res = cbor_new_float2();
384   CHECK_RES(ctx, res);
385   cbor_set_float2(res, value);
386   _cbor_builder_append(res, ctx);
387 }
388 
389 void cbor_builder_float4_callback(void *context, float value) {
390   struct _cbor_decoder_context *ctx = context;
391   cbor_item_t *res = cbor_new_float4();
392   CHECK_RES(ctx, res);
393   cbor_set_float4(res, value);
394   _cbor_builder_append(res, ctx);
395 }
396 
397 void cbor_builder_float8_callback(void *context, double value) {
398   struct _cbor_decoder_context *ctx = context;
399   cbor_item_t *res = cbor_new_float8();
400   CHECK_RES(ctx, res);
401   cbor_set_float8(res, value);
402   _cbor_builder_append(res, ctx);
403 }
404 
405 void cbor_builder_null_callback(void *context) {
406   struct _cbor_decoder_context *ctx = context;
407   cbor_item_t *res = cbor_new_null();
408   CHECK_RES(ctx, res);
409   _cbor_builder_append(res, ctx);
410 }
411 
412 void cbor_builder_undefined_callback(void *context) {
413   struct _cbor_decoder_context *ctx = context;
414   cbor_item_t *res = cbor_new_undef();
415   CHECK_RES(ctx, res);
416   _cbor_builder_append(res, ctx);
417 }
418 
419 void cbor_builder_boolean_callback(void *context, bool value) {
420   struct _cbor_decoder_context *ctx = context;
421   cbor_item_t *res = cbor_build_bool(value);
422   CHECK_RES(ctx, res);
423   _cbor_builder_append(res, ctx);
424 }
425 
426 void cbor_builder_tag_callback(void *context, uint64_t value) {
427   struct _cbor_decoder_context *ctx = context;
428   cbor_item_t *res = cbor_new_tag(value);
429   CHECK_RES(ctx, res);
430   PUSH_CTX_STACK(ctx, res, 1);
431 }
432