File: ws_assert.h

package info (click to toggle)
wireshark 4.6.4-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 351,436 kB
  • sloc: ansic: 3,103,613; cpp: 129,736; xml: 100,978; python: 56,510; perl: 24,575; sh: 5,874; lex: 4,383; pascal: 4,304; makefile: 164; ruby: 113; objc: 91; tcl: 35
file content (157 lines) | stat: -rw-r--r-- 6,006 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
/** @file
 *
 * Wireshark - Network traffic analyzer
 * By Gerald Combs <gerald@wireshark.org>
 * Copyright 1998 Gerald Combs
 *
 * SPDX-License-Identifier: GPL-2.0-or-later
 */

#ifndef __WS_ASSERT_H__
#define __WS_ASSERT_H__

#include <ws_symbol_export.h>
#include <ws_attributes.h>
#include <stdbool.h>
#include <string.h>
#include <wsutil/wslog.h>
#include <wsutil/wmem/wmem.h>

/*
 * XXX - WS_ASSERT_ENABLED is tested in various if statements
 * below, so that we don't test various assertions unless
 * assertions are enabled. Compilers will often partially
 * evaluate (CONSTANT && (expression)) at compile time, so
 * that if CONSTANT is 0 the rest of the test isn't evaluated
 * and assumed to result in a false result, with the code in
 * the if branch being removed, and if CONSTANT is 1, the
 * code is treated as an if that tests the expression.
 *
 * This could mean that, if "defined but not used" tests are
 * being done, any variable tested in the expression may be warked
 * as "defined but not used" if WS_ASSERT_ENABLED is 0, causing
 * a pile of warnings if the variable isn't marked as unused
 * (especially true of parametre variables).
 *
 * However, some compilers - Clang, in my tests, and probably GCC,
 * due to tests in builds not failing - treate "if (0 && (expression))"
 * specially, pretending hat all variables in the expression are used,
 * even if they aren't used in the generated code. (At least in
 * Apple clang version 15.0.0 (clang-1500.1.0.2.5), it must be
 * exactly 0 - (0) doesn't have the same effect.)
 *
 * That's all very well, but, unfortunately Microsoft Visual Studio's
 * C compiler doesn't do that, so the variables have to be marked as
 * unused, which may cause warnings "used, but marked as unused"
 * warnings if the code is compiled with assertions enabled.
 */
#if defined(ENABLE_ASSERT)
#define WS_ASSERT_ENABLED       1
#elif defined(NDEBUG)
#define WS_ASSERT_ENABLED       0
#else
#define WS_ASSERT_ENABLED       1
#endif

#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */

/*
 * We don't want to execute the expression without assertions because
 * it might be time and space costly and the goal here is to optimize for
 * that case. However removing it completely is not good enough
 * because it might generate many unused variable warnings. So we use
 * if (false) and let the compiler optimize away the dead execution branch.
 */
#define ws_assert_if_active(active, expr) \
        do {                                                \
            if ((active) && !(expr))                        \
                ws_error("assertion failed: %s", #expr);    \
        } while (0)

/*
 * ws_abort_if_fail() is not conditional on having assertions enabled.
 * Usually used to appease a static analyzer.
 */
#define ws_abort_if_fail(expr) \
        ws_assert_if_active(true, expr)

/*
 * ws_assert() cannot produce side effects, otherwise code will
 * behave differently because of having assertions enabled/disabled, and
 * probably introduce some difficult to track bugs.
 */
#define ws_assert(expr) \
        ws_assert_if_active(WS_ASSERT_ENABLED, expr)


#define ws_assert_streq(s1, s2) \
        ws_assert((s1) && (s2) && strcmp((s1), (s2)) == 0)

#define ws_assert_utf8(str, len) \
        do {                                                            \
            const char *__assert_endptr;                                \
            if (WS_ASSERT_ENABLED &&                                    \
                        !g_utf8_validate(str, len, &__assert_endptr)) { \
                ws_log_utf8_full(LOG_DOMAIN_UTF_8, LOG_LEVEL_ERROR,     \
                                    __FILE__, __LINE__, __func__,       \
                                    str, len, __assert_endptr);         \
            }                                                           \
        } while (0)

/*
 * We don't want to disable ws_assert_not_reached() with (optional) assertions
 * disabled.
 * That would blast compiler warnings everywhere for no benefit, not
 * even a miniscule performance gain. Reaching this function is always
 * a programming error and will unconditionally abort execution.
 *
 * Note: With g_assert_not_reached() if the compiler supports unreachable
 * built-ins (which recent versions of GCC and MSVC do) there is no warning
 * blast with g_assert_not_reached() and G_DISABLE_ASSERT. However if that
 * is not the case then g_assert_not_reached() is simply (void)0 and that
 * causes the spurious warnings, because the compiler can't tell anymore
 * that a certain code path is not used. We avoid that with
 * ws_assert_not_reached(). There is no reason to ever use a no-op here.
 */
#define ws_assert_not_reached() \
        ws_error("assertion \"not reached\" failed")

/*
 * These macros can be used as an alternative to ws_assert() to
 * assert some condition on function arguments. This must only be used
 * to catch programming errors, in situations where an assertion is
 * appropriate. And it should only be used if failing the condition
 * doesn't necessarily lead to an inconsistent state for the program.
 *
 * It is possible to set the fatal log domain to "InvalidArg" to abort
 * execution for debugging purposes, if one of these checks fail.
 */

#define ws_warn_badarg(str) \
    ws_log_full(LOG_DOMAIN_EINVAL, LOG_LEVEL_WARNING, \
                    __FILE__, __LINE__, __func__, \
                    "invalid argument: %s", str)

#define ws_return_str_if(expr, scope) \
        do { \
            if (WS_ASSERT_ENABLED && (expr)) { \
                ws_warn_badarg(#expr); \
                return wmem_strdup_printf(scope, "(invalid argument: %s)", #expr); \
            } \
        } while (0)

#define ws_return_val_if(expr, val) \
        do { \
            if (WS_ASSERT_ENABLED && (expr)) { \
                ws_warn_badarg(#expr); \
                return (val); \
            } \
        } while (0)

#ifdef __cplusplus
}
#endif /* __cplusplus */

#endif /* __WS_ASSERT_H__ */