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
cbor_serialize(const cbor_item_t * item,unsigned char * buffer,size_t buffer_size)20 size_t cbor_serialize(const cbor_item_t *item, unsigned char *buffer,
21 size_t buffer_size) {
22 // cppcheck-suppress missingReturn
23 switch (cbor_typeof(item)) {
24 case CBOR_TYPE_UINT:
25 return cbor_serialize_uint(item, buffer, buffer_size);
26 case CBOR_TYPE_NEGINT:
27 return cbor_serialize_negint(item, buffer, buffer_size);
28 case CBOR_TYPE_BYTESTRING:
29 return cbor_serialize_bytestring(item, buffer, buffer_size);
30 case CBOR_TYPE_STRING:
31 return cbor_serialize_string(item, buffer, buffer_size);
32 case CBOR_TYPE_ARRAY:
33 return cbor_serialize_array(item, buffer, buffer_size);
34 case CBOR_TYPE_MAP:
35 return cbor_serialize_map(item, buffer, buffer_size);
36 case CBOR_TYPE_TAG:
37 return cbor_serialize_tag(item, buffer, buffer_size);
38 case CBOR_TYPE_FLOAT_CTRL:
39 return cbor_serialize_float_ctrl(item, buffer, buffer_size);
40 }
41 }
42
43 /** Largest integer that can be encoded as embedded in the item leading byte. */
44 const uint64_t kMaxEmbeddedInt = 23;
45
46 /** How many bytes will a tag for a nested item of a given `size` take when
47 * encoded.*/
_cbor_encoded_header_size(uint64_t size)48 size_t _cbor_encoded_header_size(uint64_t size) {
49 if (size <= kMaxEmbeddedInt)
50 return 1;
51 else if (size <= UINT8_MAX)
52 return 2;
53 else if (size <= UINT16_MAX)
54 return 3;
55 else if (size <= UINT32_MAX)
56 return 5;
57 else
58 return 9;
59 }
60
cbor_serialized_size(const cbor_item_t * item)61 size_t cbor_serialized_size(const cbor_item_t *item) {
62 // cppcheck-suppress missingReturn
63 switch (cbor_typeof(item)) {
64 case CBOR_TYPE_UINT:
65 case CBOR_TYPE_NEGINT:
66 switch (cbor_int_get_width(item)) {
67 case CBOR_INT_8:
68 if (cbor_get_uint8(item) <= kMaxEmbeddedInt) return 1;
69 return 2;
70 case CBOR_INT_16:
71 return 3;
72 case CBOR_INT_32:
73 return 5;
74 case CBOR_INT_64:
75 return 9;
76 }
77 // Note: We do not _cbor_safe_signaling_add zero-length definite strings,
78 // they would cause zeroes to propagate. All other items are at least one
79 // byte.
80 case CBOR_TYPE_BYTESTRING: {
81 if (cbor_bytestring_is_definite(item)) {
82 size_t header_size =
83 _cbor_encoded_header_size(cbor_bytestring_length(item));
84 if (cbor_bytestring_length(item) == 0) return header_size;
85 return _cbor_safe_signaling_add(header_size,
86 cbor_bytestring_length(item));
87 }
88 size_t indef_bytestring_size = 2; // Leading byte + break
89 cbor_item_t **chunks = cbor_bytestring_chunks_handle(item);
90 for (size_t i = 0; i < cbor_bytestring_chunk_count(item); i++) {
91 indef_bytestring_size = _cbor_safe_signaling_add(
92 indef_bytestring_size, cbor_serialized_size(chunks[i]));
93 }
94 return indef_bytestring_size;
95 }
96 case CBOR_TYPE_STRING: {
97 if (cbor_string_is_definite(item)) {
98 size_t header_size =
99 _cbor_encoded_header_size(cbor_string_length(item));
100 if (cbor_string_length(item) == 0) return header_size;
101 return _cbor_safe_signaling_add(header_size, cbor_string_length(item));
102 }
103 size_t indef_string_size = 2; // Leading byte + break
104 cbor_item_t **chunks = cbor_string_chunks_handle(item);
105 for (size_t i = 0; i < cbor_string_chunk_count(item); i++) {
106 indef_string_size = _cbor_safe_signaling_add(
107 indef_string_size, cbor_serialized_size(chunks[i]));
108 }
109 return indef_string_size;
110 }
111 case CBOR_TYPE_ARRAY: {
112 size_t array_size = cbor_array_is_definite(item)
113 ? _cbor_encoded_header_size(cbor_array_size(item))
114 : 2; // Leading byte + break
115 cbor_item_t **items = cbor_array_handle(item);
116 for (size_t i = 0; i < cbor_array_size(item); i++) {
117 array_size = _cbor_safe_signaling_add(array_size,
118 cbor_serialized_size(items[i]));
119 }
120 return array_size;
121 }
122 case CBOR_TYPE_MAP: {
123 size_t map_size = cbor_map_is_definite(item)
124 ? _cbor_encoded_header_size(cbor_map_size(item))
125 : 2; // Leading byte + break
126 struct cbor_pair *items = cbor_map_handle(item);
127 for (size_t i = 0; i < cbor_map_size(item); i++) {
128 map_size = _cbor_safe_signaling_add(
129 map_size,
130 _cbor_safe_signaling_add(cbor_serialized_size(items[i].key),
131 cbor_serialized_size(items[i].value)));
132 }
133 return map_size;
134 }
135 case CBOR_TYPE_TAG: {
136 return _cbor_safe_signaling_add(
137 _cbor_encoded_header_size(cbor_tag_value(item)),
138 cbor_serialized_size(cbor_move(cbor_tag_item(item))));
139 }
140 case CBOR_TYPE_FLOAT_CTRL:
141 switch (cbor_float_get_width(item)) {
142 case CBOR_FLOAT_0:
143 return _cbor_encoded_header_size(cbor_ctrl_value(item));
144 case CBOR_FLOAT_16:
145 return 3;
146 case CBOR_FLOAT_32:
147 return 5;
148 case CBOR_FLOAT_64:
149 return 9;
150 }
151 }
152 }
153
cbor_serialize_alloc(const cbor_item_t * item,unsigned char ** buffer,size_t * buffer_size)154 size_t cbor_serialize_alloc(const cbor_item_t *item, unsigned char **buffer,
155 size_t *buffer_size) {
156 *buffer = NULL;
157 size_t serialized_size = cbor_serialized_size(item);
158 if (serialized_size == 0) {
159 if (buffer_size != NULL) *buffer_size = 0;
160 return 0;
161 }
162 *buffer = _cbor_malloc(serialized_size);
163 if (*buffer == NULL) {
164 if (buffer_size != NULL) *buffer_size = 0;
165 return 0;
166 }
167
168 size_t written = cbor_serialize(item, *buffer, serialized_size);
169 CBOR_ASSERT(written == serialized_size);
170 if (buffer_size != NULL) *buffer_size = serialized_size;
171 return written;
172 }
173
cbor_serialize_uint(const cbor_item_t * item,unsigned char * buffer,size_t buffer_size)174 size_t cbor_serialize_uint(const cbor_item_t *item, unsigned char *buffer,
175 size_t buffer_size) {
176 CBOR_ASSERT(cbor_isa_uint(item));
177 // cppcheck-suppress missingReturn
178 switch (cbor_int_get_width(item)) {
179 case CBOR_INT_8:
180 return cbor_encode_uint8(cbor_get_uint8(item), buffer, buffer_size);
181 case CBOR_INT_16:
182 return cbor_encode_uint16(cbor_get_uint16(item), buffer, buffer_size);
183 case CBOR_INT_32:
184 return cbor_encode_uint32(cbor_get_uint32(item), buffer, buffer_size);
185 case CBOR_INT_64:
186 return cbor_encode_uint64(cbor_get_uint64(item), buffer, buffer_size);
187 }
188 }
189
cbor_serialize_negint(const cbor_item_t * item,unsigned char * buffer,size_t buffer_size)190 size_t cbor_serialize_negint(const cbor_item_t *item, unsigned char *buffer,
191 size_t buffer_size) {
192 CBOR_ASSERT(cbor_isa_negint(item));
193 // cppcheck-suppress missingReturn
194 switch (cbor_int_get_width(item)) {
195 case CBOR_INT_8:
196 return cbor_encode_negint8(cbor_get_uint8(item), buffer, buffer_size);
197 case CBOR_INT_16:
198 return cbor_encode_negint16(cbor_get_uint16(item), buffer, buffer_size);
199 case CBOR_INT_32:
200 return cbor_encode_negint32(cbor_get_uint32(item), buffer, buffer_size);
201 case CBOR_INT_64:
202 return cbor_encode_negint64(cbor_get_uint64(item), buffer, buffer_size);
203 }
204 }
205
cbor_serialize_bytestring(const cbor_item_t * item,unsigned char * buffer,size_t buffer_size)206 size_t cbor_serialize_bytestring(const cbor_item_t *item, unsigned char *buffer,
207 size_t buffer_size) {
208 CBOR_ASSERT(cbor_isa_bytestring(item));
209 if (cbor_bytestring_is_definite(item)) {
210 size_t length = cbor_bytestring_length(item);
211 size_t written = cbor_encode_bytestring_start(length, buffer, buffer_size);
212 if (written > 0 && (buffer_size - written >= length)) {
213 memcpy(buffer + written, cbor_bytestring_handle(item), length);
214 return written + length;
215 }
216 return 0;
217 } else {
218 CBOR_ASSERT(cbor_bytestring_is_indefinite(item));
219 size_t chunk_count = cbor_bytestring_chunk_count(item);
220 size_t written = cbor_encode_indef_bytestring_start(buffer, buffer_size);
221 if (written == 0) return 0;
222
223 cbor_item_t **chunks = cbor_bytestring_chunks_handle(item);
224 for (size_t i = 0; i < chunk_count; i++) {
225 size_t chunk_written = cbor_serialize_bytestring(
226 chunks[i], buffer + written, buffer_size - written);
227 if (chunk_written == 0) return 0;
228 written += chunk_written;
229 }
230
231 size_t break_written =
232 cbor_encode_break(buffer + written, buffer_size - written);
233 if (break_written == 0) return 0;
234 return written + break_written;
235 }
236 }
237
cbor_serialize_string(const cbor_item_t * item,unsigned char * buffer,size_t buffer_size)238 size_t cbor_serialize_string(const cbor_item_t *item, unsigned char *buffer,
239 size_t buffer_size) {
240 CBOR_ASSERT(cbor_isa_string(item));
241 if (cbor_string_is_definite(item)) {
242 size_t length = cbor_string_length(item);
243 size_t written = cbor_encode_string_start(length, buffer, buffer_size);
244 if (written && (buffer_size - written >= length)) {
245 memcpy(buffer + written, cbor_string_handle(item), length);
246 return written + length;
247 }
248 return 0;
249 } else {
250 CBOR_ASSERT(cbor_string_is_indefinite(item));
251 size_t chunk_count = cbor_string_chunk_count(item);
252 size_t written = cbor_encode_indef_string_start(buffer, buffer_size);
253 if (written == 0) return 0;
254
255 cbor_item_t **chunks = cbor_string_chunks_handle(item);
256 for (size_t i = 0; i < chunk_count; i++) {
257 size_t chunk_written = cbor_serialize_string(chunks[i], buffer + written,
258 buffer_size - written);
259 if (chunk_written == 0) return 0;
260 written += chunk_written;
261 }
262
263 size_t break_written =
264 cbor_encode_break(buffer + written, buffer_size - written);
265 if (break_written == 0) return 0;
266 return written + break_written;
267 }
268 }
269
cbor_serialize_array(const cbor_item_t * item,unsigned char * buffer,size_t buffer_size)270 size_t cbor_serialize_array(const cbor_item_t *item, unsigned char *buffer,
271 size_t buffer_size) {
272 CBOR_ASSERT(cbor_isa_array(item));
273 size_t size = cbor_array_size(item), written = 0;
274 cbor_item_t **handle = cbor_array_handle(item);
275 if (cbor_array_is_definite(item)) {
276 written = cbor_encode_array_start(size, buffer, buffer_size);
277 } else {
278 CBOR_ASSERT(cbor_array_is_indefinite(item));
279 written = cbor_encode_indef_array_start(buffer, buffer_size);
280 }
281 if (written == 0) return 0;
282
283 for (size_t i = 0; i < size; i++) {
284 size_t item_written =
285 cbor_serialize(*(handle++), buffer + written, buffer_size - written);
286 if (item_written == 0) return 0;
287 written += item_written;
288 }
289
290 if (cbor_array_is_definite(item)) {
291 return written;
292 } else {
293 CBOR_ASSERT(cbor_array_is_indefinite(item));
294 size_t break_written =
295 cbor_encode_break(buffer + written, buffer_size - written);
296 if (break_written == 0) return 0;
297 return written + break_written;
298 }
299 }
300
cbor_serialize_map(const cbor_item_t * item,unsigned char * buffer,size_t buffer_size)301 size_t cbor_serialize_map(const cbor_item_t *item, unsigned char *buffer,
302 size_t buffer_size) {
303 CBOR_ASSERT(cbor_isa_map(item));
304 size_t size = cbor_map_size(item), written = 0;
305 struct cbor_pair *handle = cbor_map_handle(item);
306
307 if (cbor_map_is_definite(item)) {
308 written = cbor_encode_map_start(size, buffer, buffer_size);
309 } else {
310 CBOR_ASSERT(cbor_map_is_indefinite(item));
311 written = cbor_encode_indef_map_start(buffer, buffer_size);
312 }
313 if (written == 0) return 0;
314
315 for (size_t i = 0; i < size; i++) {
316 size_t item_written =
317 cbor_serialize(handle->key, buffer + written, buffer_size - written);
318 if (item_written == 0) {
319 return 0;
320 }
321 written += item_written;
322 item_written = cbor_serialize((handle++)->value, buffer + written,
323 buffer_size - written);
324 if (item_written == 0) return 0;
325 written += item_written;
326 }
327
328 if (cbor_map_is_definite(item)) {
329 return written;
330 } else {
331 CBOR_ASSERT(cbor_map_is_indefinite(item));
332 size_t break_written =
333 cbor_encode_break(buffer + written, buffer_size - written);
334 if (break_written == 0) return 0;
335 return written + break_written;
336 }
337 }
338
cbor_serialize_tag(const cbor_item_t * item,unsigned char * buffer,size_t buffer_size)339 size_t cbor_serialize_tag(const cbor_item_t *item, unsigned char *buffer,
340 size_t buffer_size) {
341 CBOR_ASSERT(cbor_isa_tag(item));
342 size_t written = cbor_encode_tag(cbor_tag_value(item), buffer, buffer_size);
343 if (written == 0) return 0;
344
345 size_t item_written = cbor_serialize(cbor_move(cbor_tag_item(item)),
346 buffer + written, buffer_size - written);
347 if (item_written == 0) return 0;
348 return written + item_written;
349 }
350
cbor_serialize_float_ctrl(const cbor_item_t * item,unsigned char * buffer,size_t buffer_size)351 size_t cbor_serialize_float_ctrl(const cbor_item_t *item, unsigned char *buffer,
352 size_t buffer_size) {
353 CBOR_ASSERT(cbor_isa_float_ctrl(item));
354 // cppcheck-suppress missingReturn
355 switch (cbor_float_get_width(item)) {
356 case CBOR_FLOAT_0:
357 /* CTRL - special treatment */
358 return cbor_encode_ctrl(cbor_ctrl_value(item), buffer, buffer_size);
359 case CBOR_FLOAT_16:
360 return cbor_encode_half(cbor_float_get_float2(item), buffer, buffer_size);
361 case CBOR_FLOAT_32:
362 return cbor_encode_single(cbor_float_get_float4(item), buffer,
363 buffer_size);
364 case CBOR_FLOAT_64:
365 return cbor_encode_double(cbor_float_get_float8(item), buffer,
366 buffer_size);
367 }
368 }
369