Coverage Report

Created: 2026-11-12 06:43

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/work/include/cbor/common.h
Line
Count
Source
1
/*
2
 * SPDX-FileCopyrightText: 2014-2026 Pavel Kalvoda <me@pavelkalvoda.com>
3
 * SPDX-License-Identifier: MIT
4
 */
5
6
#ifndef LIBCBOR_COMMON_H
7
#define LIBCBOR_COMMON_H
8
9
#include <assert.h>
10
#include <stdbool.h>
11
#include <stddef.h>
12
#include <stdint.h>
13
#include <stdlib.h>
14
#include <string.h>
15
16
#include "cbor/cbor_export.h"
17
#include "cbor/configuration.h"
18
#include "data.h"
19
20
#ifdef __cplusplus
21
extern "C" {
22
23
/**
24
 * C99 is not a subset of C++ -- 'restrict' qualifier is not a part of the
25
 * language. This is a workaround to keep it in C headers -- compilers allow
26
 * linking non-restrict signatures with restrict implementations.
27
 *
28
 * If you know a nicer way, please do let me know.
29
 */
30
#define CBOR_RESTRICT_POINTER
31
32
#else
33
34
// MSVC + C++ workaround
35
#define CBOR_RESTRICT_POINTER CBOR_RESTRICT_SPECIFIER
36
37
#endif
38
39
static const uint8_t cbor_major_version = CBOR_MAJOR_VERSION;
40
static const uint8_t cbor_minor_version = CBOR_MINOR_VERSION;
41
static const uint8_t cbor_patch_version = CBOR_PATCH_VERSION;
42
43
#define CBOR_VERSION               \
44
  _CBOR_TO_STR(CBOR_MAJOR_VERSION) \
45
  "." _CBOR_TO_STR(CBOR_MINOR_VERSION) "." _CBOR_TO_STR(CBOR_PATCH_VERSION)
46
#define CBOR_HEX_VERSION \
47
  ((CBOR_MAJOR_VERSION << 16) | (CBOR_MINOR_VERSION << 8) | CBOR_PATCH_VERSION)
48
49
/* http://stackoverflow.com/questions/1644868/c-define-macro-for-debug-printing
50
 */
51
#ifdef DEBUG
52
#include <stdio.h>
53
#define _cbor_debug_print(fmt, ...)                                     \
54
  do {                                                                  \
55
    if (DEBUG)                                                          \
56
      fprintf(stderr, "%s:%d:%s(): " fmt, __FILE__, __LINE__, __func__, \
57
              __VA_ARGS__);                                             \
58
  } while (0)
59
extern bool _cbor_enable_assert;
60
// Like `assert`, but can be dynamically disabled in tests to allow testing
61
// invalid behaviors.
62
#define CBOR_ASSERT(e) assert(!_cbor_enable_assert || (e))
63
#define _CBOR_TEST_DISABLE_ASSERT(block) \
64
  do {                                   \
65
    _cbor_enable_assert = false;         \
66
    block _cbor_enable_assert = true;    \
67
  } while (0)
68
#else
69
#define debug_print(fmt, ...) \
70
  do {                        \
71
  } while (0)
72
#define CBOR_ASSERT(e)
73
#define _CBOR_TEST_DISABLE_ASSERT(block) \
74
  do {                                   \
75
    block                                \
76
  } while (0)
77
#endif
78
79
#define CBOR_ASSERT_VALID_TYPE(item_type) \
80
  CBOR_ASSERT(item_type >= CBOR_TYPE_UINT && item_type <= CBOR_TYPE_FLOAT_CTRL);
81
82
#define _CBOR_TO_STR_(x) #x
83
#define _CBOR_TO_STR(x) _CBOR_TO_STR_(x) /* enables proper double expansion */
84
85
#ifdef CBOR_HAS_NODISCARD_ATTRIBUTE
86
#define CBOR_NODISCARD [[nodiscard]]
87
#else
88
#define CBOR_NODISCARD
89
#endif
90
91
#ifdef __GNUC__
92
#define _CBOR_UNUSED __attribute__((__unused__))
93
// Fall back to __attribute__((warn_unused_result)) if we don't have
94
// [[nodiscard]]
95
#ifdef CBOR_HAS_NODISCARD_ATTRIBUTE
96
#define _CBOR_NODISCARD CBOR_NODISCARD
97
#else
98
#define _CBOR_NODISCARD __attribute__((warn_unused_result))
99
#endif
100
#elif defined(_MSC_VER)
101
#define _CBOR_UNUSED __pragma(warning(suppress : 4100 4101))
102
#define _CBOR_NODISCARD
103
#else
104
#define _CBOR_UNUSED
105
#define _CBOR_NODISCARD
106
#endif
107
108
#ifdef CBOR_HAS_BUILTIN_UNREACHABLE
109
#define _CBOR_UNREACHABLE __builtin_unreachable()
110
#else
111
#define _CBOR_UNREACHABLE
112
#endif
113
114
typedef void* (*_cbor_malloc_t)(size_t);
115
typedef void* (*_cbor_realloc_t)(void*, size_t);
116
typedef void (*_cbor_free_t)(void*);
117
118
CBOR_EXPORT extern _cbor_malloc_t _cbor_malloc;
119
CBOR_EXPORT extern _cbor_realloc_t _cbor_realloc;
120
CBOR_EXPORT extern _cbor_free_t _cbor_free;
121
122
// Macro to short-circuit builder functions when memory allocation fails
123
#define _CBOR_NOTNULL(cbor_item) \
124
  do {                           \
125
    if (cbor_item == NULL) {     \
126
      return NULL;               \
127
    }                            \
128
  } while (0)
129
130
// Macro to short-circuit builders when memory allocation of nested data
131
// fails
132
#define _CBOR_DEPENDENT_NOTNULL(cbor_item, pointer) \
133
  do {                                              \
134
    if (pointer == NULL) {                          \
135
      _cbor_free(cbor_item);                        \
136
      return NULL;                                  \
137
    }                                               \
138
  } while (0)
139
140
/** Sets the memory management routines to use.
141
 *
142
 * By default, libcbor will use the standard library `malloc`, `realloc`,
143
 * and `free`.
144
 *
145
 * A custom allocator is the intended mechanism for limiting memory
146
 * consumption when parsing untrusted CBOR data. Because definite-length
147
 * collections pre-allocate storage sized by the declared element count,
148
 * a crafted input can request a very large allocation before any element
149
 * data is read. A capping `custom_malloc` that returns NULL above a chosen
150
 * threshold will cause `cbor_load` to return `CBOR_ERR_MEMERROR` instead of
151
 * attempting the allocation. See `examples/capped_alloc.c` for a
152
 * self-contained demonstration.
153
 *
154
 * \rst
155
 * .. warning::
156
 *   This function modifies the global state and should
157
 *   therefore be used accordingly. Changing the memory handlers while
158
 *   allocated items exist will result in a ``free``/``malloc`` mismatch.
159
 *   This function is not thread safe with respect to both itself and all
160
 *   the other *libcbor* functions that work with the heap.
161
 *
162
 * .. note::
163
 *   `realloc` implementation must correctly support `NULL`
164
 *   reallocation (see e.g. http://en.cppreference.com/w/c/memory/realloc)
165
 *
166
 * .. note::
167
 *   `free` implementation must accept `NULL` as a no-op (as the standard
168
 *   `free` does). libcbor does not allocate storage for empty items and
169
 *   will pass their `NULL` data pointer to `free` when they are deallocated.
170
 *
171
 * \endrst
172
 *
173
 * @param custom_malloc malloc implementation
174
 * @param custom_realloc realloc implementation
175
 * @param custom_free free implementation
176
 */
177
CBOR_EXPORT void cbor_set_allocs(_cbor_malloc_t custom_malloc,
178
                                 _cbor_realloc_t custom_realloc,
179
                                 _cbor_free_t custom_free);
180
181
/*
182
 * ============================================================================
183
 * Type manipulation
184
 * ============================================================================
185
 */
186
187
/** Get the type of the item
188
 *
189
 * @param item
190
 * @return The type
191
 */
192
_CBOR_NODISCARD
193
CBOR_EXPORT cbor_type cbor_typeof(
194
    const cbor_item_t* item); /* Will be inlined iff link-time opt is enabled */
195
196
/* Standard CBOR Major item types */
197
198
/** Does the item have the appropriate major type?
199
 * @param item the item
200
 * @return Is the item an #CBOR_TYPE_UINT?
201
 */
202
_CBOR_NODISCARD
203
CBOR_EXPORT bool cbor_isa_uint(const cbor_item_t* item);
204
205
/** Does the item have the appropriate major type?
206
 * @param item the item
207
 * @return Is the item a #CBOR_TYPE_NEGINT?
208
 */
209
_CBOR_NODISCARD
210
CBOR_EXPORT bool cbor_isa_negint(const cbor_item_t* item);
211
212
/** Does the item have the appropriate major type?
213
 * @param item the item
214
 * @return Is the item a #CBOR_TYPE_BYTESTRING?
215
 */
216
_CBOR_NODISCARD
217
CBOR_EXPORT bool cbor_isa_bytestring(const cbor_item_t* item);
218
219
/** Does the item have the appropriate major type?
220
 * @param item the item
221
 * @return Is the item a #CBOR_TYPE_STRING?
222
 */
223
_CBOR_NODISCARD
224
CBOR_EXPORT bool cbor_isa_string(const cbor_item_t* item);
225
226
/** Does the item have the appropriate major type?
227
 * @param item the item
228
 * @return Is the item an #CBOR_TYPE_ARRAY?
229
 */
230
_CBOR_NODISCARD
231
CBOR_EXPORT bool cbor_isa_array(const cbor_item_t* item);
232
233
/** Does the item have the appropriate major type?
234
 * @param item the item
235
 * @return Is the item a #CBOR_TYPE_MAP?
236
 */
237
_CBOR_NODISCARD
238
CBOR_EXPORT bool cbor_isa_map(const cbor_item_t* item);
239
240
/** Does the item have the appropriate major type?
241
 * @param item the item
242
 * @return Is the item a #CBOR_TYPE_TAG?
243
 */
244
_CBOR_NODISCARD
245
CBOR_EXPORT bool cbor_isa_tag(const cbor_item_t* item);
246
247
/** Does the item have the appropriate major type?
248
 * @param item the item
249
 * @return Is the item a #CBOR_TYPE_FLOAT_CTRL?
250
 */
251
_CBOR_NODISCARD
252
CBOR_EXPORT bool cbor_isa_float_ctrl(const cbor_item_t* item);
253
254
/* Practical types with respect to their semantics (but not tag values) */
255
256
/** Is the item an integer, either positive or negative?
257
 * @param item the item
258
 * @return  Is the item an integer, either positive or negative?
259
 */
260
_CBOR_NODISCARD
261
CBOR_EXPORT bool cbor_is_int(const cbor_item_t* item);
262
263
/** Is the item an a floating point number?
264
 * @param item the item
265
 * @return  Is the item a floating point number?
266
 */
267
_CBOR_NODISCARD
268
CBOR_EXPORT bool cbor_is_float(const cbor_item_t* item);
269
270
/** Is the item an a boolean?
271
 * @param item the item
272
 * @return  Is the item a boolean?
273
 */
274
_CBOR_NODISCARD
275
CBOR_EXPORT bool cbor_is_bool(const cbor_item_t* item);
276
277
/** Does this item represent `null`
278
 *
279
 * \rst
280
 * .. warning::
281
 *   This is in no way related to the value of the pointer.
282
 *   Passing a null pointer will most likely result in a crash.
283
 * \endrst
284
 *
285
 * @param item the item
286
 * @return  Is the item (CBOR logical) null?
287
 */
288
_CBOR_NODISCARD
289
CBOR_EXPORT bool cbor_is_null(const cbor_item_t* item);
290
291
/** Does this item represent `undefined`
292
 *
293
 * \rst
294
 * .. warning::
295
 *   Care must be taken to distinguish nulls and undefined values in C.
296
 * \endrst
297
 *
298
 * @param item the item
299
 * @return Is the item (CBOR logical) undefined?
300
 */
301
_CBOR_NODISCARD
302
CBOR_EXPORT bool cbor_is_undef(const cbor_item_t* item);
303
304
/*
305
 * ============================================================================
306
 * Memory management
307
 * ============================================================================
308
 */
309
310
/** Increases the item's reference count by one
311
 *
312
 * Constant complexity; items referring to this one or items being
313
 * referred to are not updated.
314
 *
315
 * This function can be used to extend reference counting to client code.
316
 *
317
 * Defined `static inline` so the single-instruction increment is emitted
318
 * directly at the call site — meaningful for hot loops that build
319
 * #cbor_item_t trees, where every constructed item touches the refcount.
320
 *
321
 * @param item Reference to an item
322
 * @return The input \p item
323
 */
324
0
static inline cbor_item_t* cbor_incref(cbor_item_t* item) {
325
0
  item->refcount++;
326
0
  return item;
327
0
}
328
329
/** Slow path of #cbor_decref: performs the actual deallocation once the
330
 * reference count reaches zero. Do not call directly; use #cbor_decref.
331
 *
332
 * Split from #cbor_decref so the fast path (decrement + zero check) can
333
 * inline while the recursive free logic — which dominates code size —
334
 * stays out of line.
335
 */
336
CBOR_EXPORT void _cbor_decref_free(cbor_item_t* item);
337
338
/** Decreases the item's reference count by one, deallocating the item if
339
 * needed
340
 *
341
 * In case the item is deallocated, the reference count of all items this
342
 * item references will also be #cbor_decref 'ed recursively.
343
 *
344
 * The fast path (decrement, check for zero) is `static inline`; only the
345
 * uncommon deallocation branch calls into #_cbor_decref_free. This keeps
346
 * every non-final decref a couple of instructions at the call site.
347
 *
348
 * @param item_ref Reference to an item. Will be set to `NULL` if deallocated
349
 */
350
2.45k
static inline void cbor_decref(cbor_item_t** item_ref) {
351
2.45k
  cbor_item_t* item = *item_ref;
352
2.45k
  CBOR_ASSERT(item->refcount > 0);
353
2.45k
  if (--item->refcount == 0) {
354
2.45k
    _cbor_decref_free(item);
355
    *item_ref = NULL;
356
2.45k
  }
357
2.45k
}
358
359
/** Decreases the item's reference count by one, deallocating the item if
360
 * needed
361
 *
362
 * Convenience wrapper for #cbor_decref when its set-to-null behavior is
363
 * not needed
364
 *
365
 * @param item Reference to an item
366
 */
367
CBOR_EXPORT void cbor_intermediate_decref(cbor_item_t* item);
368
369
/** Get the item's reference count
370
 *
371
 * \rst
372
 * .. warning::
373
 *   This does *not* account for transitive references.
374
 * \endrst
375
 *
376
 * @todo Add some inline examples for reference counting
377
 *
378
 * @param item the item
379
 * @return the reference count
380
 */
381
_CBOR_NODISCARD
382
CBOR_EXPORT size_t cbor_refcount(const cbor_item_t* item);
383
384
/** Provides CPP-like move construct
385
 *
386
 * Decreases the reference count by one, but does not deallocate the item
387
 * even if its refcount reaches zero. This is useful for passing
388
 * intermediate values to functions that increase reference count. Should
389
 * only be used with functions that `incref` their arguments.
390
 *
391
 * \rst
392
 * .. warning::
393
 *   If the item is moved without correctly increasing the
394
 *   reference count afterwards, the memory will be leaked.
395
 * \endrst
396
 *
397
 * @param item Reference to an item, or `NULL`.
398
 * @return the item with reference count decreased by one, or `NULL` if
399
 * \p item is `NULL`
400
 */
401
_CBOR_NODISCARD
402
CBOR_EXPORT cbor_item_t* cbor_move(cbor_item_t* item);
403
404
/** Compares two items for structural (encoding-level) equality
405
 *
406
 * Two items are structurally equal when they would produce identical bytes
407
 * if serialized by a correct CBOR encoder that preserves the in-memory
408
 * representation. Every aspect of the encoding counts:
409
 *
410
 * - **Major type**: `CBOR_TYPE_UINT` and `CBOR_TYPE_NEGINT` are distinct even
411
 *   if the argument bytes happen to be the same.
412
 * - **Integer encoding width**: `cbor_build_uint8(1)` and
413
 * `cbor_build_uint16(1)` are *not* structurally equal, even though both
414
 * represent the integer 1. Use the integer value accessors (`cbor_get_uint64`
415
 * etc.) if you need value-level comparison.
416
 * - **Definite vs. indefinite length**: a definite-length array and an
417
 *   indefinite-length array with the same elements are *not* structurally
418
 * equal.
419
 * - **Chunk boundaries**: two indefinite bytestrings or strings that carry the
420
 *   same bytes but split them across a different number of chunks are *not*
421
 *   structurally equal.
422
 * - **Map entry order**: maps are compared positionally — entry at index i in
423
 *   \p item1 must match entry at index i in \p item2. Maps that carry the same
424
 *   key-value pairs in a different order are *not* structurally equal. (CBOR
425
 *   maps are unordered in the data model; for data-model equality see RFC 8949
426
 *   §5.6.1.)
427
 * - **Float encoding width**: `cbor_build_float2(1.5)` and
428
 *   `cbor_build_float4(1.5)` are *not* structurally equal.
429
 * - **NaN payload**: floating-point NaN values with different bit patterns are
430
 *   *not* structurally equal.
431
 * - **Tag number**: tags with different tag numbers are *not* structurally
432
 * equal.
433
 *
434
 * Runs in time linear in the encoded byte size of the items and performs no
435
 * additional memory allocations.
436
 *
437
 * \rst
438
 * .. note::
439
 *   This function implements *structural* equality, not the data-model equality
440
 *   defined by RFC 8949. In the CBOR data model, encoding width is invisible
441
 *   and maps are unordered sets of key-value pairs. If you need data-model
442
 *   equality (e.g., ``uint8(1) == uint64(1)``), you must implement it on top
443
 *   of this library.
444
 * \endrst
445
 *
446
 * Neither \p item1 nor \p item2 may be `NULL`. Passing `NULL` is a
447
 * programming error and will trigger an assertion failure in debug builds.
448
 *
449
 * @param item1 First item; must not be `NULL`
450
 * @param item2 Second item; must not be `NULL`
451
 * @return `true` if \p item1 and \p item2 are structurally equal
452
 */
453
_CBOR_NODISCARD
454
CBOR_EXPORT bool cbor_structurally_equal(const cbor_item_t* item1,
455
                                         const cbor_item_t* item2);
456
457
#ifdef __cplusplus
458
}
459
#endif
460
461
#endif  // LIBCBOR_COMMON_H