Coverage Report

Created: 2026-09-28 06:32

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