Coverage Report

Created: 2025-07-18 06:26

/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
}