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 "serialization.h"
9#include <string.h>
10#include "cbor/arrays.h"
11#include "cbor/bytestrings.h"
12#include "cbor/floats_ctrls.h"
13#include "cbor/ints.h"
14#include "cbor/maps.h"
15#include "cbor/strings.h"
16#include "cbor/tags.h"
17#include "encoding.h"
18#include "internal/memory_utils.h"
19
20size_t cbor_serialize(const cbor_item_t *item, unsigned char *buffer,
21                      size_t buffer_size) {
22  switch (cbor_typeof(item)) {
23    case CBOR_TYPE_UINT:
24      return cbor_serialize_uint(item, buffer, buffer_size);
25    case CBOR_TYPE_NEGINT:
26      return cbor_serialize_negint(item, buffer, buffer_size);
27    case CBOR_TYPE_BYTESTRING:
28      return cbor_serialize_bytestring(item, buffer, buffer_size);
29    case CBOR_TYPE_STRING:
30      return cbor_serialize_string(item, buffer, buffer_size);
31    case CBOR_TYPE_ARRAY:
32      return cbor_serialize_array(item, buffer, buffer_size);
33    case CBOR_TYPE_MAP:
34      return cbor_serialize_map(item, buffer, buffer_size);
35    case CBOR_TYPE_TAG:
36      return cbor_serialize_tag(item, buffer, buffer_size);
37    case CBOR_TYPE_FLOAT_CTRL:
38      return cbor_serialize_float_ctrl(item, buffer, buffer_size);
39    default:
40      return 0;
41  }
42}
43
44size_t cbor_serialize_alloc(const cbor_item_t *item, unsigned char **buffer,
45                            size_t *buffer_size) {
46  size_t bfr_size = 32;
47  unsigned char *bfr = _CBOR_MALLOC(bfr_size), *tmp_bfr;
48  if (bfr == NULL) {
49    return 0;
50  }
51
52  size_t written;
53
54  /* This is waaay too optimistic - figure out something smarter (eventually) */
55  while ((written = cbor_serialize(item, bfr, bfr_size)) == 0) {
56    if (!_cbor_safe_to_multiply(CBOR_BUFFER_GROWTH, bfr_size)) {
57      _CBOR_FREE(bfr);
58      return 0;
59    }
60
61    tmp_bfr = _CBOR_REALLOC(bfr, bfr_size *= 2);
62
63    if (tmp_bfr == NULL) {
64      _CBOR_FREE(bfr);
65      return 0;
66    }
67    bfr = tmp_bfr;
68  }
69  *buffer = bfr;
70  *buffer_size = bfr_size;
71  return written;
72}
73
74size_t cbor_serialize_uint(const cbor_item_t *item, unsigned char *buffer,
75                           size_t buffer_size) {
76  assert(cbor_isa_uint(item));
77  switch (cbor_int_get_width(item)) {
78    case CBOR_INT_8:
79      return cbor_encode_uint8(cbor_get_uint8(item), buffer, buffer_size);
80    case CBOR_INT_16:
81      return cbor_encode_uint16(cbor_get_uint16(item), buffer, buffer_size);
82    case CBOR_INT_32:
83      return cbor_encode_uint32(cbor_get_uint32(item), buffer, buffer_size);
84    case CBOR_INT_64:
85      return cbor_encode_uint64(cbor_get_uint64(item), buffer, buffer_size);
86    default:
87      return 0;
88  }
89}
90
91size_t cbor_serialize_negint(const cbor_item_t *item, unsigned char *buffer,
92                             size_t buffer_size) {
93  assert(cbor_isa_negint(item));
94  switch (cbor_int_get_width(item)) {
95    case CBOR_INT_8:
96      return cbor_encode_negint8(cbor_get_uint8(item), buffer, buffer_size);
97    case CBOR_INT_16:
98      return cbor_encode_negint16(cbor_get_uint16(item), buffer, buffer_size);
99    case CBOR_INT_32:
100      return cbor_encode_negint32(cbor_get_uint32(item), buffer, buffer_size);
101    case CBOR_INT_64:
102      return cbor_encode_negint64(cbor_get_uint64(item), buffer, buffer_size);
103    default:
104      return 0;
105  }
106}
107
108size_t cbor_serialize_bytestring(const cbor_item_t *item, unsigned char *buffer,
109                                 size_t buffer_size) {
110  assert(cbor_isa_bytestring(item));
111  if (cbor_bytestring_is_definite(item)) {
112    size_t length = cbor_bytestring_length(item);
113    size_t written = cbor_encode_bytestring_start(length, buffer, buffer_size);
114    if (written && (buffer_size - written >= length)) {
115      memcpy(buffer + written, cbor_bytestring_handle(item), length);
116      return written + length;
117    } else
118      return 0;
119  } else {
120    assert(cbor_bytestring_is_indefinite(item));
121    size_t chunk_count = cbor_bytestring_chunk_count(item);
122    size_t written = cbor_encode_indef_bytestring_start(buffer, buffer_size);
123
124    if (written == 0) return 0;
125
126    cbor_item_t **chunks = cbor_bytestring_chunks_handle(item);
127    for (size_t i = 0; i < chunk_count; i++) {
128      size_t chunk_written = cbor_serialize_bytestring(
129          chunks[i], buffer + written, buffer_size - written);
130      if (chunk_written == 0)
131        return 0;
132      else
133        written += chunk_written;
134    }
135    if (cbor_encode_break(buffer + written, buffer_size - written) > 0)
136      return written + 1;
137    else
138      return 0;
139  }
140}
141
142size_t cbor_serialize_string(const cbor_item_t *item, unsigned char *buffer,
143                             size_t buffer_size) {
144  assert(cbor_isa_string(item));
145  if (cbor_string_is_definite(item)) {
146    size_t length = cbor_string_length(item);
147    size_t written = cbor_encode_string_start(length, buffer, buffer_size);
148    if (written && (buffer_size - written >= length)) {
149      memcpy(buffer + written, cbor_string_handle(item), length);
150      return written + length;
151    } else
152      return 0;
153  } else {
154    assert(cbor_string_is_indefinite(item));
155    size_t chunk_count = cbor_string_chunk_count(item);
156    size_t written = cbor_encode_indef_string_start(buffer, buffer_size);
157
158    if (written == 0) return 0;
159
160    cbor_item_t **chunks = cbor_string_chunks_handle(item);
161    for (size_t i = 0; i < chunk_count; i++) {
162      size_t chunk_written = cbor_serialize_string(chunks[i], buffer + written,
163                                                   buffer_size - written);
164      if (chunk_written == 0)
165        return 0;
166      else
167        written += chunk_written;
168    }
169    if (cbor_encode_break(buffer + written, buffer_size - written) > 0)
170      return written + 1;
171    else
172      return 0;
173  }
174}
175
176size_t cbor_serialize_array(const cbor_item_t *item, unsigned char *buffer,
177                            size_t buffer_size) {
178  assert(cbor_isa_array(item));
179  size_t size = cbor_array_size(item), written = 0;
180  cbor_item_t **handle = cbor_array_handle(item);
181  if (cbor_array_is_definite(item)) {
182    written = cbor_encode_array_start(size, buffer, buffer_size);
183  } else {
184    assert(cbor_array_is_indefinite(item));
185    written = cbor_encode_indef_array_start(buffer, buffer_size);
186  }
187  if (written == 0) return 0;
188
189  size_t item_written;
190  for (size_t i = 0; i < size; i++) {
191    item_written =
192        cbor_serialize(*(handle++), buffer + written, buffer_size - written);
193    if (item_written == 0)
194      return 0;
195    else
196      written += item_written;
197  }
198
199  if (cbor_array_is_definite(item)) {
200    return written;
201  } else {
202    assert(cbor_array_is_indefinite(item));
203    item_written = cbor_encode_break(buffer + written, buffer_size - written);
204    if (item_written == 0)
205      return 0;
206    else
207      return written + 1;
208  }
209}
210
211size_t cbor_serialize_map(const cbor_item_t *item, unsigned char *buffer,
212                          size_t buffer_size) {
213  assert(cbor_isa_map(item));
214  size_t size = cbor_map_size(item), written = 0;
215  struct cbor_pair *handle = cbor_map_handle(item);
216
217  if (cbor_map_is_definite(item)) {
218    written = cbor_encode_map_start(size, buffer, buffer_size);
219  } else {
220    assert(cbor_map_is_indefinite(item));
221    written = cbor_encode_indef_map_start(buffer, buffer_size);
222  }
223  if (written == 0) return 0;
224
225  size_t item_written;
226  for (size_t i = 0; i < size; i++) {
227    item_written =
228        cbor_serialize(handle->key, buffer + written, buffer_size - written);
229    if (item_written == 0)
230      return 0;
231    else
232      written += item_written;
233    item_written = cbor_serialize((handle++)->value, buffer + written,
234                                  buffer_size - written);
235    if (item_written == 0)
236      return 0;
237    else
238      written += item_written;
239  }
240
241  if (cbor_map_is_definite(item)) {
242    return written;
243  } else {
244    assert(cbor_map_is_indefinite(item));
245    item_written = cbor_encode_break(buffer + written, buffer_size - written);
246    if (item_written == 0)
247      return 0;
248    else
249      return written + 1;
250  }
251}
252
253size_t cbor_serialize_tag(const cbor_item_t *item, unsigned char *buffer,
254                          size_t buffer_size) {
255  assert(cbor_isa_tag(item));
256  size_t written = cbor_encode_tag(cbor_tag_value(item), buffer, buffer_size);
257  if (written == 0) return 0;
258
259  size_t item_written = cbor_serialize(cbor_tag_item(item), buffer + written,
260                                       buffer_size - written);
261  if (item_written == 0)
262    return 0;
263  else
264    return written + item_written;
265}
266
267size_t cbor_serialize_float_ctrl(const cbor_item_t *item, unsigned char *buffer,
268                                 size_t buffer_size) {
269  assert(cbor_isa_float_ctrl(item));
270  switch (cbor_float_get_width(item)) {
271    case CBOR_FLOAT_0:
272      /* CTRL - special treatment */
273      return cbor_encode_ctrl(cbor_ctrl_value(item), buffer, buffer_size);
274    case CBOR_FLOAT_16:
275      return cbor_encode_half(cbor_float_get_float2(item), buffer, buffer_size);
276    case CBOR_FLOAT_32:
277      return cbor_encode_single(cbor_float_get_float4(item), buffer,
278                                buffer_size);
279    case CBOR_FLOAT_64:
280      return cbor_encode_double(cbor_float_get_float8(item), buffer,
281                                buffer_size);
282  }
283
284  /* Should never happen - make the compiler happy */
285  return 0;
286}
287