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