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