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