1/*
2 * Copyright (c) 2014-2019 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
19void _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// TODO: refactor this to take the parameter name, this is way too magical
91#define CHECK_RES                  \
92  do {                             \
93    if (res == NULL) {             \
94      ctx->creation_failed = true; \
95      return;                      \
96    }                              \
97  } while (0)
98
99void cbor_builder_uint8_callback(void *context, uint8_t value) {
100  struct _cbor_decoder_context *ctx = context;
101  cbor_item_t *res = cbor_new_int8();
102  CHECK_RES;
103  cbor_mark_uint(res);
104  cbor_set_uint8(res, value);
105  _cbor_builder_append(res, ctx);
106}
107
108void cbor_builder_uint16_callback(void *context, uint16_t value) {
109  struct _cbor_decoder_context *ctx = context;
110  cbor_item_t *res = cbor_new_int16();
111  CHECK_RES;
112  cbor_mark_uint(res);
113  cbor_set_uint16(res, value);
114  _cbor_builder_append(res, ctx);
115}
116
117void cbor_builder_uint32_callback(void *context, uint32_t value) {
118  struct _cbor_decoder_context *ctx = context;
119  cbor_item_t *res = cbor_new_int32();
120  CHECK_RES;
121  cbor_mark_uint(res);
122  cbor_set_uint32(res, value);
123  _cbor_builder_append(res, ctx);
124}
125
126void cbor_builder_uint64_callback(void *context, uint64_t value) {
127  struct _cbor_decoder_context *ctx = context;
128  cbor_item_t *res = cbor_new_int64();
129  CHECK_RES;
130  cbor_mark_uint(res);
131  cbor_set_uint64(res, value);
132  _cbor_builder_append(res, ctx);
133}
134
135void cbor_builder_negint8_callback(void *context, uint8_t value) {
136  struct _cbor_decoder_context *ctx = context;
137  cbor_item_t *res = cbor_new_int8();
138  CHECK_RES;
139  cbor_mark_negint(res);
140  cbor_set_uint8(res, value);
141  _cbor_builder_append(res, ctx);
142}
143
144void cbor_builder_negint16_callback(void *context, uint16_t value) {
145  struct _cbor_decoder_context *ctx = context;
146  cbor_item_t *res = cbor_new_int16();
147  cbor_mark_negint(res);
148  cbor_set_uint16(res, value);
149  _cbor_builder_append(res, ctx);
150}
151
152void cbor_builder_negint32_callback(void *context, uint32_t value) {
153  struct _cbor_decoder_context *ctx = context;
154  cbor_item_t *res = cbor_new_int32();
155  CHECK_RES;
156  cbor_mark_negint(res);
157  cbor_set_uint32(res, value);
158  _cbor_builder_append(res, ctx);
159}
160
161void cbor_builder_negint64_callback(void *context, uint64_t value) {
162  struct _cbor_decoder_context *ctx = context;
163  cbor_item_t *res = cbor_new_int64();
164  CHECK_RES;
165  cbor_mark_negint(res);
166  cbor_set_uint64(res, value);
167  _cbor_builder_append(res, ctx);
168}
169
170void cbor_builder_byte_string_callback(void *context, cbor_data data,
171                                       size_t length) {
172  struct _cbor_decoder_context *ctx = context;
173  unsigned char *new_handle = _CBOR_MALLOC(length);
174  if (new_handle == NULL) {
175    ctx->creation_failed = true;
176    return;
177  }
178
179  memcpy(new_handle, data, length);
180  cbor_item_t *res = cbor_new_definite_bytestring();
181
182  if (res == NULL) {
183    _CBOR_FREE(new_handle);
184    ctx->creation_failed = true;
185    return;
186  }
187
188  cbor_bytestring_set_handle(res, new_handle, length);
189
190  if (ctx->stack->size > 0 && cbor_isa_bytestring(ctx->stack->top->item)) {
191    if (cbor_bytestring_is_indefinite(ctx->stack->top->item)) {
192      cbor_bytestring_add_chunk(ctx->stack->top->item, cbor_move(res));
193    } else {
194      cbor_decref(&res);
195      ctx->syntax_error = true;
196    }
197  } else {
198    _cbor_builder_append(res, ctx);
199  }
200}
201
202void cbor_builder_byte_string_start_callback(void *context) {
203  struct _cbor_decoder_context *ctx = context;
204  cbor_item_t *res = cbor_new_indefinite_bytestring();
205  CHECK_RES;
206  _cbor_stack_push(ctx->stack, res, 0);
207}
208
209void cbor_builder_string_callback(void *context, cbor_data data,
210                                  size_t length) {
211  struct _cbor_decoder_context *ctx = context;
212  struct _cbor_unicode_status unicode_status;
213
214  size_t codepoint_count =
215      _cbor_unicode_codepoint_count(data, length, &unicode_status);
216
217  if (unicode_status.status == _CBOR_UNICODE_BADCP) {
218    ctx->syntax_error = true;
219    return;
220  }
221
222  unsigned char *new_handle = _CBOR_MALLOC(length);
223
224  if (new_handle == NULL) {
225    ctx->creation_failed = true;
226    return;
227  }
228
229  memcpy(new_handle, data, length);
230  cbor_item_t *res = cbor_new_definite_string();
231  cbor_string_set_handle(res, new_handle, length);
232  res->metadata.string_metadata.codepoint_count = codepoint_count;
233
234  /* Careful here: order matters */
235  if (ctx->stack->size > 0 && cbor_isa_string(ctx->stack->top->item)) {
236    if (cbor_string_is_indefinite(ctx->stack->top->item)) {
237      cbor_string_add_chunk(ctx->stack->top->item, cbor_move(res));
238    } else {
239      cbor_decref(&res);
240      ctx->syntax_error = true;
241    }
242  } else {
243    _cbor_builder_append(res, ctx);
244  }
245}
246
247void cbor_builder_string_start_callback(void *context) {
248  struct _cbor_decoder_context *ctx = context;
249  cbor_item_t *res = cbor_new_indefinite_string();
250  CHECK_RES;
251  _cbor_stack_push(ctx->stack, res, 0);
252}
253
254void cbor_builder_array_start_callback(void *context, size_t size) {
255  struct _cbor_decoder_context *ctx = context;
256  cbor_item_t *res = cbor_new_definite_array(size);
257  CHECK_RES;
258  if (size > 0) {
259    _cbor_stack_push(ctx->stack, res, size);
260  } else {
261    _cbor_builder_append(res, ctx);
262  }
263}
264
265void cbor_builder_indef_array_start_callback(void *context) {
266  struct _cbor_decoder_context *ctx = context;
267  cbor_item_t *res = cbor_new_indefinite_array();
268  CHECK_RES;
269  _cbor_stack_push(ctx->stack, res, 0);
270}
271
272void cbor_builder_indef_map_start_callback(void *context) {
273  struct _cbor_decoder_context *ctx = context;
274  cbor_item_t *res = cbor_new_indefinite_map();
275  CHECK_RES;
276  _cbor_stack_push(ctx->stack, res, 0);
277}
278
279void cbor_builder_map_start_callback(void *context, size_t size) {
280  struct _cbor_decoder_context *ctx = context;
281  cbor_item_t *res = cbor_new_definite_map(size);
282  CHECK_RES;
283  if (size > 0) {
284    _cbor_stack_push(ctx->stack, res, size * 2);
285  } else {
286    _cbor_builder_append(res, ctx);
287  }
288}
289
290/**
291 * Is the (partially constructed) item indefinite?
292 */
293bool _cbor_is_indefinite(cbor_item_t *item) {
294  switch (item->type) {
295    case CBOR_TYPE_BYTESTRING:
296      return item->metadata.bytestring_metadata.type ==
297             _CBOR_METADATA_INDEFINITE;
298    case CBOR_TYPE_STRING:
299      return item->metadata.string_metadata.type == _CBOR_METADATA_INDEFINITE;
300    case CBOR_TYPE_ARRAY:
301      return item->metadata.array_metadata.type == _CBOR_METADATA_INDEFINITE;
302    case CBOR_TYPE_MAP:
303      return item->metadata.map_metadata.type == _CBOR_METADATA_INDEFINITE;
304    default:
305      return false;
306  }
307}
308
309void cbor_builder_indef_break_callback(void *context) {
310  struct _cbor_decoder_context *ctx = context;
311  /* There must be an item to break out of*/
312  if (ctx->stack->size > 0) {
313    cbor_item_t *item = ctx->stack->top->item;
314    if (_cbor_is_indefinite(
315            item) && /* Only indefinite items can be terminated by 0xFF */
316        /* Special case: we cannot append up if an indefinite map is incomplete
317           (we are expecting a value). */
318        (item->type != CBOR_TYPE_MAP || ctx->stack->top->subitems % 2 == 0)) {
319      _cbor_stack_pop(ctx->stack);
320      _cbor_builder_append(item, ctx);
321      return;
322    }
323  }
324
325  ctx->syntax_error = true;
326}
327
328void cbor_builder_float2_callback(void *context, float value) {
329  struct _cbor_decoder_context *ctx = context;
330  cbor_item_t *res = cbor_new_float2();
331  cbor_set_float2(res, value);
332  _cbor_builder_append(res, ctx);
333}
334
335void cbor_builder_float4_callback(void *context, float value) {
336  struct _cbor_decoder_context *ctx = context;
337  cbor_item_t *res = cbor_new_float4();
338  CHECK_RES;
339  cbor_set_float4(res, value);
340  _cbor_builder_append(res, ctx);
341}
342
343void cbor_builder_float8_callback(void *context, double value) {
344  struct _cbor_decoder_context *ctx = context;
345  cbor_item_t *res = cbor_new_float8();
346  CHECK_RES;
347  cbor_set_float8(res, value);
348  _cbor_builder_append(res, ctx);
349}
350
351void cbor_builder_null_callback(void *context) {
352  struct _cbor_decoder_context *ctx = context;
353  cbor_item_t *res = cbor_new_null();
354  CHECK_RES;
355  _cbor_builder_append(res, ctx);
356}
357
358void cbor_builder_undefined_callback(void *context) {
359  struct _cbor_decoder_context *ctx = context;
360  cbor_item_t *res = cbor_new_undef();
361  CHECK_RES;
362  _cbor_builder_append(res, ctx);
363}
364
365void cbor_builder_boolean_callback(void *context, bool value) {
366  struct _cbor_decoder_context *ctx = context;
367  cbor_item_t *res = cbor_build_bool(value);
368  CHECK_RES;
369  _cbor_builder_append(res, ctx);
370}
371
372void cbor_builder_tag_callback(void *context, uint64_t value) {
373  struct _cbor_decoder_context *ctx = context;
374  cbor_item_t *res = cbor_new_tag(value);
375  CHECK_RES;
376  _cbor_stack_push(ctx->stack, res, 1);
377}
378