Coverage Report

Created: 2026-08-31 06:43

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
 * \endrst
169
 *
170
 * @param custom_malloc malloc implementation
171
 * @param custom_realloc realloc implementation
172
 * @param custom_free free implementation
173
 */
174
CBOR_EXPORT void cbor_set_allocs(_cbor_malloc_t custom_malloc,
175
                                 _cbor_realloc_t custom_realloc,
176
                                 _cbor_free_t custom_free);
177
178
/*
179
 * ============================================================================
180
 * Type manipulation
181
 * ============================================================================
182
 */
183
184
/** Get the type of the item
185
 *
186
 * @param item
187
 * @return The type
188
 */
189
_CBOR_NODISCARD
190
CBOR_EXPORT cbor_type cbor_typeof(
191
    const cbor_item_t* item); /* Will be inlined iff link-time opt is enabled */
192
193
/* Standard CBOR Major item types */
194
195
/** Does the item have the appropriate major type?
196
 * @param item the item
197
 * @return Is the item an #CBOR_TYPE_UINT?
198
 */
199
_CBOR_NODISCARD
200
CBOR_EXPORT bool cbor_isa_uint(const cbor_item_t* item);
201
202
/** Does the item have the appropriate major type?
203
 * @param item the item
204
 * @return Is the item a #CBOR_TYPE_NEGINT?
205
 */
206
_CBOR_NODISCARD
207
CBOR_EXPORT bool cbor_isa_negint(const cbor_item_t* item);
208
209
/** Does the item have the appropriate major type?
210
 * @param item the item
211
 * @return Is the item a #CBOR_TYPE_BYTESTRING?
212
 */
213
_CBOR_NODISCARD
214
CBOR_EXPORT bool cbor_isa_bytestring(const cbor_item_t* item);
215
216
/** Does the item have the appropriate major type?
217
 * @param item the item
218
 * @return Is the item a #CBOR_TYPE_STRING?
219
 */
220
_CBOR_NODISCARD
221
CBOR_EXPORT bool cbor_isa_string(const cbor_item_t* item);
222
223
/** Does the item have the appropriate major type?
224
 * @param item the item
225
 * @return Is the item an #CBOR_TYPE_ARRAY?
226
 */
227
_CBOR_NODISCARD
228
CBOR_EXPORT bool cbor_isa_array(const cbor_item_t* item);
229
230
/** Does the item have the appropriate major type?
231
 * @param item the item
232
 * @return Is the item a #CBOR_TYPE_MAP?
233
 */
234
_CBOR_NODISCARD
235
CBOR_EXPORT bool cbor_isa_map(const cbor_item_t* item);
236
237
/** Does the item have the appropriate major type?
238
 * @param item the item
239
 * @return Is the item a #CBOR_TYPE_TAG?
240
 */
241
_CBOR_NODISCARD
242
CBOR_EXPORT bool cbor_isa_tag(const cbor_item_t* item);
243
244
/** Does the item have the appropriate major type?
245
 * @param item the item
246
 * @return Is the item a #CBOR_TYPE_FLOAT_CTRL?
247
 */
248
_CBOR_NODISCARD
249
CBOR_EXPORT bool cbor_isa_float_ctrl(const cbor_item_t* item);
250
251
/* Practical types with respect to their semantics (but not tag values) */
252
253
/** Is the item an integer, either positive or negative?
254
 * @param item the item
255
 * @return  Is the item an integer, either positive or negative?
256
 */
257
_CBOR_NODISCARD
258
CBOR_EXPORT bool cbor_is_int(const cbor_item_t* item);
259
260
/** Is the item an a floating point number?
261
 * @param item the item
262
 * @return  Is the item a floating point number?
263
 */
264
_CBOR_NODISCARD
265
CBOR_EXPORT bool cbor_is_float(const cbor_item_t* item);
266
267
/** Is the item an a boolean?
268
 * @param item the item
269
 * @return  Is the item a boolean?
270
 */
271
_CBOR_NODISCARD
272
CBOR_EXPORT bool cbor_is_bool(const cbor_item_t* item);
273
274
/** Does this item represent `null`
275
 *
276
 * \rst
277
 * .. warning::
278
 *   This is in no way related to the value of the pointer.
279
 *   Passing a null pointer will most likely result in a crash.
280
 * \endrst
281
 *
282
 * @param item the item
283
 * @return  Is the item (CBOR logical) null?
284
 */
285
_CBOR_NODISCARD
286
CBOR_EXPORT bool cbor_is_null(const cbor_item_t* item);
287
288
/** Does this item represent `undefined`
289
 *
290
 * \rst
291
 * .. warning::
292
 *   Care must be taken to distinguish nulls and undefined values in C.
293
 * \endrst
294
 *
295
 * @param item the item
296
 * @return Is the item (CBOR logical) undefined?
297
 */
298
_CBOR_NODISCARD
299
CBOR_EXPORT bool cbor_is_undef(const cbor_item_t* item);
300
301
/*
302
 * ============================================================================
303
 * Memory management
304
 * ============================================================================
305
 */
306
307
/** Increases the item's reference count by one
308
 *
309
 * Constant complexity; items referring to this one or items being
310
 * referred to are not updated.
311
 *
312
 * This function can be used to extend reference counting to client code.
313
 *
314
 * Defined `static inline` so the single-instruction increment is emitted
315
 * directly at the call site — meaningful for hot loops that build
316
 * #cbor_item_t trees, where every constructed item touches the refcount.
317
 *
318
 * @param item Reference to an item
319
 * @return The input \p item
320
 */
321
0
static inline cbor_item_t* cbor_incref(cbor_item_t* item) {
322
0
  item->refcount++;
323
0
  return item;
324
0
}
325
326
/** Slow path of #cbor_decref: performs the actual deallocation once the
327
 * reference count reaches zero. Do not call directly; use #cbor_decref.
328
 *
329
 * Split from #cbor_decref so the fast path (decrement + zero check) can
330
 * inline while the recursive free logic — which dominates code size —
331
 * stays out of line.
332
 */
333
CBOR_EXPORT void _cbor_decref_free(cbor_item_t* item);
334
335
/** Decreases the item's reference count by one, deallocating the item if
336
 * needed
337
 *
338
 * In case the item is deallocated, the reference count of all items this
339
 * item references will also be #cbor_decref 'ed recursively.
340
 *
341
 * The fast path (decrement, check for zero) is `static inline`; only the
342
 * uncommon deallocation branch calls into #_cbor_decref_free. This keeps
343
 * every non-final decref a couple of instructions at the call site.
344
 *
345
 * @param item Reference to an item. Will be set to `NULL` if deallocated
346
 */
347
2.49k
static inline void cbor_decref(cbor_item_t** item_ref) {
348
2.49k
  cbor_item_t* item = *item_ref;
349
2.49k
  CBOR_ASSERT(item->refcount > 0);
350
2.49k
  if (--item->refcount == 0) {
351
2.49k
    _cbor_decref_free(item);
352
    *item_ref = NULL;
353
2.49k
  }
354
2.49k
}
355
356
/** Decreases the item's reference count by one, deallocating the item if
357
 * needed
358
 *
359
 * Convenience wrapper for #cbor_decref when its set-to-null behavior is
360
 * not needed
361
 *
362
 * @param item Reference to an item
363
 */
364
CBOR_EXPORT void cbor_intermediate_decref(cbor_item_t* item);
365
366
/** Get the item's reference count
367
 *
368
 * \rst
369
 * .. warning::
370
 *   This does *not* account for transitive references.
371
 * \endrst
372
 *
373
 * @todo Add some inline examples for reference counting
374
 *
375
 * @param item the item
376
 * @return the reference count
377
 */
378
_CBOR_NODISCARD
379
CBOR_EXPORT size_t cbor_refcount(const cbor_item_t* item);
380
381
/** Provides CPP-like move construct
382
 *
383
 * Decreases the reference count by one, but does not deallocate the item
384
 * even if its refcount reaches zero. This is useful for passing
385
 * intermediate values to functions that increase reference count. Should
386
 * only be used with functions that `incref` their arguments.
387
 *
388
 * \rst
389
 * .. warning::
390
 *   If the item is moved without correctly increasing the
391
 *   reference count afterwards, the memory will be leaked.
392
 * \endrst
393
 *
394
 * @param item Reference to an item
395
 * @return the item with reference count decreased by one
396
 */
397
_CBOR_NODISCARD
398
CBOR_EXPORT cbor_item_t* cbor_move(cbor_item_t* item);
399
400
/** Compares two items for structural (encoding-level) equality
401
 *
402
 * Two items are structurally equal when they would produce identical bytes
403
 * if serialized by a correct CBOR encoder that preserves the in-memory
404
 * representation. Every aspect of the encoding counts:
405
 *
406
 * - **Major type**: `CBOR_TYPE_UINT` and `CBOR_TYPE_NEGINT` are distinct even
407
 *   if the argument bytes happen to be the same.
408
 * - **Integer encoding width**: `cbor_build_uint8(1)` and
409
 * `cbor_build_uint16(1)` are *not* structurally equal, even though both
410
 * represent the integer 1. Use the integer value accessors (`cbor_get_uint64`
411
 * etc.) if you need value-level comparison.
412
 * - **Definite vs. indefinite length**: a definite-length array and an
413
 *   indefinite-length array with the same elements are *not* structurally
414
 * equal.
415
 * - **Chunk boundaries**: two indefinite bytestrings or strings that carry the
416
 *   same bytes but split them across a different number of chunks are *not*
417
 *   structurally equal.
418
 * - **Map entry order**: maps are compared positionally — entry at index i in
419
 *   \p item1 must match entry at index i in \p item2. Maps that carry the same
420
 *   key-value pairs in a different order are *not* structurally equal. (CBOR
421
 *   maps are unordered in the data model; for data-model equality see RFC 8949
422
 *   §5.6.1.)
423
 * - **Float encoding width**: `cbor_build_float2(1.5)` and
424
 *   `cbor_build_float4(1.5)` are *not* structurally equal.
425
 * - **NaN payload**: floating-point NaN values with different bit patterns are
426
 *   *not* structurally equal.
427
 * - **Tag number**: tags with different tag numbers are *not* structurally
428
 * equal.
429
 *
430
 * Runs in time linear in the encoded byte size of the items and performs no
431
 * additional memory allocations.
432
 *
433
 * \rst
434
 * .. note::
435
 *   This function implements *structural* equality, not the data-model equality
436
 *   defined by RFC 8949. In the CBOR data model, encoding width is invisible
437
 *   and maps are unordered sets of key-value pairs. If you need data-model
438
 *   equality (e.g., ``uint8(1) == uint64(1)``), you must implement it on top
439
 *   of this library.
440
 * \endrst
441
 *
442
 * Neither \p item1 nor \p item2 may be `NULL`. Passing `NULL` is a
443
 * programming error and will trigger an assertion failure in debug builds.
444
 *
445
 * @param item1 First item; must not be `NULL`
446
 * @param item2 Second item; must not be `NULL`
447
 * @return `true` if \p item1 and \p item2 are structurally equal
448
 */
449
_CBOR_NODISCARD
450
CBOR_EXPORT bool cbor_structurally_equal(const cbor_item_t* item1,
451
                                         const cbor_item_t* item2);
452
453
#ifdef __cplusplus
454
}
455
#endif
456
457
#endif  // LIBCBOR_COMMON_H