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