xref: /freebsd/contrib/libcbor/src/cbor/serialization.c (revision e64bea71c21eb42e97aa615188ba91f6cce0d36d)
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   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.*/
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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