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 "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 
20 size_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 
44 size_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 
74 size_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 
91 size_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 
108 size_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 
142 size_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 
176 size_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 
211 size_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 
253 size_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_move(cbor_tag_item(item)),
260                                        buffer + written, buffer_size - written);
261   if (item_written == 0)
262     return 0;
263   else
264     return written + item_written;
265 }
266 
267 size_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