1/*	$NetBSD: util.h,v 1.15 2024/02/21 22:52:31 christos Exp $	*/
2
3/*
4 * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
5 *
6 * SPDX-License-Identifier: MPL-2.0
7 *
8 * This Source Code Form is subject to the terms of the Mozilla Public
9 * License, v. 2.0. If a copy of the MPL was not distributed with this
10 * file, you can obtain one at https://mozilla.org/MPL/2.0/.
11 *
12 * See the COPYRIGHT file distributed with this work for additional
13 * information regarding copyright ownership.
14 */
15
16#pragma once
17
18#include <inttypes.h>
19
20/*! \file isc/util.h
21 * NOTE:
22 *
23 * This file is not to be included from any <isc/???.h> (or other) library
24 * files.
25 *
26 * \brief
27 * Including this file puts several macros in your name space that are
28 * not protected (as all the other ISC functions/macros do) by prepending
29 * ISC_ or isc_ to the name.
30 */
31
32/***
33 *** Clang Compatibility Macros
34 ***/
35
36#if !defined(__has_attribute)
37#define __has_attribute(x) 0
38#endif /* if !defined(__has_attribute) */
39
40#if !defined(__has_c_attribute)
41#define __has_c_attribute(x) 0
42#endif /* if !defined(__has_c_attribute) */
43
44#if !defined(__has_feature)
45#define __has_feature(x) 0
46#endif /* if !defined(__has_feature) */
47
48/***
49 *** General Macros.
50 ***/
51
52/*%
53 * Use this to hide unused function arguments.
54 * \code
55 * int
56 * foo(char *bar)
57 * {
58 *	UNUSED(bar);
59 * }
60 * \endcode
61 */
62#define UNUSED(x) (void)(x)
63
64#if __GNUC__ >= 8 && !defined(__clang__)
65#define ISC_NONSTRING __attribute__((nonstring))
66#else /* if __GNUC__ >= 8 && !defined(__clang__) */
67#define ISC_NONSTRING
68#endif /* __GNUC__ */
69
70#if __has_c_attribute(fallthrough)
71#define FALLTHROUGH [[fallthrough]]
72#elif __GNUC__ >= 7 && !defined(__clang__)
73#define FALLTHROUGH __attribute__((fallthrough))
74#else
75/* clang-format off */
76#define FALLTHROUGH do {} while (0) /* FALLTHROUGH */
77/* clang-format on */
78#endif
79
80#if HAVE_FUNC_ATTRIBUTE_CONSTRUCTOR && HAVE_FUNC_ATTRIBUTE_DESTRUCTOR
81#define ISC_CONSTRUCTOR __attribute__((constructor))
82#define ISC_DESTRUCTOR	__attribute__((destructor))
83#else
84#define ISC_CONSTRUCTOR
85#define ISC_DESTRUCTOR
86#endif
87
88/*%
89 * The opposite: silent warnings about stored values which are never read.
90 */
91#define POST(x) (void)(x)
92
93#define ISC_MAX(a, b) ((a) > (b) ? (a) : (b))
94#define ISC_MIN(a, b) ((a) < (b) ? (a) : (b))
95
96#define ISC_CLAMP(v, x, y) ((v) < (x) ? (x) : ((v) > (y) ? (y) : (v)))
97
98/*%
99 * Use this to remove the const qualifier of a variable to assign it to
100 * a non-const variable or pass it as a non-const function argument ...
101 * but only when you are sure it won't then be changed!
102 * This is necessary to sometimes shut up some compilers
103 * (as with gcc -Wcast-qual) when there is just no other good way to avoid the
104 * situation.
105 */
106#define DE_CONST(konst, var)           \
107	do {                           \
108		union {                \
109			const void *k; \
110			void	   *v; \
111		} _u;                  \
112		_u.k = konst;          \
113		var = _u.v;            \
114	} while (0)
115
116#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
117
118/*%
119 * Use this in translation units that would otherwise be empty, to
120 * suppress compiler warnings.
121 */
122#define EMPTY_TRANSLATION_UNIT extern int isc__empty;
123
124/*%
125 * We use macros instead of calling the routines directly because
126 * the capital letters make the locking stand out.
127 * We RUNTIME_CHECK for success since in general there's no way
128 * for us to continue if they fail.
129 */
130
131#ifdef ISC_UTIL_TRACEON
132#define ISC_UTIL_TRACE(a) a
133#include <stdio.h> /* Required for fprintf/stderr when tracing. */
134#else		   /* ifdef ISC_UTIL_TRACEON */
135#define ISC_UTIL_TRACE(a)
136#endif /* ifdef ISC_UTIL_TRACEON */
137
138#include <isc/result.h> /* Contractual promise. */
139
140#define LOCK(lp)                                                           \
141	do {                                                               \
142		ISC_UTIL_TRACE(fprintf(stderr, "LOCKING %p %s %d\n", (lp), \
143				       __FILE__, __LINE__));               \
144		RUNTIME_CHECK(isc_mutex_lock((lp)) == ISC_R_SUCCESS);      \
145		ISC_UTIL_TRACE(fprintf(stderr, "LOCKED %p %s %d\n", (lp),  \
146				       __FILE__, __LINE__));               \
147	} while (0)
148#define UNLOCK(lp)                                                          \
149	do {                                                                \
150		RUNTIME_CHECK(isc_mutex_unlock((lp)) == ISC_R_SUCCESS);     \
151		ISC_UTIL_TRACE(fprintf(stderr, "UNLOCKED %p %s %d\n", (lp), \
152				       __FILE__, __LINE__));                \
153	} while (0)
154
155#define BROADCAST(cvp)                                                        \
156	do {                                                                  \
157		ISC_UTIL_TRACE(fprintf(stderr, "BROADCAST %p %s %d\n", (cvp), \
158				       __FILE__, __LINE__));                  \
159		RUNTIME_CHECK(isc_condition_broadcast((cvp)) ==               \
160			      ISC_R_SUCCESS);                                 \
161	} while (0)
162#define SIGNAL(cvp)                                                          \
163	do {                                                                 \
164		ISC_UTIL_TRACE(fprintf(stderr, "SIGNAL %p %s %d\n", (cvp),   \
165				       __FILE__, __LINE__));                 \
166		RUNTIME_CHECK(isc_condition_signal((cvp)) == ISC_R_SUCCESS); \
167	} while (0)
168#define WAIT(cvp, lp)                                                         \
169	do {                                                                  \
170		ISC_UTIL_TRACE(fprintf(stderr, "WAIT %p LOCK %p %s %d\n",     \
171				       (cvp), (lp), __FILE__, __LINE__));     \
172		RUNTIME_CHECK(isc_condition_wait((cvp), (lp)) ==              \
173			      ISC_R_SUCCESS);                                 \
174		ISC_UTIL_TRACE(fprintf(stderr, "WAITED %p LOCKED %p %s %d\n", \
175				       (cvp), (lp), __FILE__, __LINE__));     \
176	} while (0)
177
178/*
179 * isc_condition_waituntil can return ISC_R_TIMEDOUT, so we
180 * don't RUNTIME_CHECK the result.
181 *
182 *  XXX Also, can't really debug this then...
183 */
184
185#define WAITUNTIL(cvp, lp, tp) isc_condition_waituntil((cvp), (lp), (tp))
186
187#define RWLOCK(lp, t)                                                         \
188	do {                                                                  \
189		ISC_UTIL_TRACE(fprintf(stderr, "RWLOCK %p, %d %s %d\n", (lp), \
190				       (t), __FILE__, __LINE__));             \
191		RUNTIME_CHECK(isc_rwlock_lock((lp), (t)) == ISC_R_SUCCESS);   \
192		ISC_UTIL_TRACE(fprintf(stderr, "RWLOCKED %p, %d %s %d\n",     \
193				       (lp), (t), __FILE__, __LINE__));       \
194	} while (0)
195#define RWUNLOCK(lp, t)                                                       \
196	do {                                                                  \
197		ISC_UTIL_TRACE(fprintf(stderr, "RWUNLOCK %p, %d %s %d\n",     \
198				       (lp), (t), __FILE__, __LINE__));       \
199		RUNTIME_CHECK(isc_rwlock_unlock((lp), (t)) == ISC_R_SUCCESS); \
200	} while (0)
201
202/*
203 * List Macros.
204 */
205#include <isc/list.h> /* Contractual promise. */
206
207#define LIST(type)		       ISC_LIST(type)
208#define INIT_LIST(type)		       ISC_LIST_INIT(type)
209#define LINK(type)		       ISC_LINK(type)
210#define INIT_LINK(elt, link)	       ISC_LINK_INIT(elt, link)
211#define HEAD(list)		       ISC_LIST_HEAD(list)
212#define TAIL(list)		       ISC_LIST_TAIL(list)
213#define EMPTY(list)		       ISC_LIST_EMPTY(list)
214#define PREV(elt, link)		       ISC_LIST_PREV(elt, link)
215#define NEXT(elt, link)		       ISC_LIST_NEXT(elt, link)
216#define APPEND(list, elt, link)	       ISC_LIST_APPEND(list, elt, link)
217#define PREPEND(list, elt, link)       ISC_LIST_PREPEND(list, elt, link)
218#define UNLINK(list, elt, link)	       ISC_LIST_UNLINK(list, elt, link)
219#define ENQUEUE(list, elt, link)       ISC_LIST_APPEND(list, elt, link)
220#define DEQUEUE(list, elt, link)       ISC_LIST_UNLINK(list, elt, link)
221#define INSERTBEFORE(li, b, e, ln)     ISC_LIST_INSERTBEFORE(li, b, e, ln)
222#define INSERTAFTER(li, a, e, ln)      ISC_LIST_INSERTAFTER(li, a, e, ln)
223#define APPENDLIST(list1, list2, link) ISC_LIST_APPENDLIST(list1, list2, link)
224
225/*%
226 * Performance
227 */
228
229/* GCC defines __SANITIZE_ADDRESS__, so reuse the macro for clang */
230#if __has_feature(address_sanitizer)
231#define __SANITIZE_ADDRESS__ 1
232#endif /* if __has_feature(address_sanitizer) */
233
234#if __has_feature(thread_sanitizer)
235#define __SANITIZE_THREAD__ 1
236#endif /* if __has_feature(thread_sanitizer) */
237
238#if __SANITIZE_THREAD__
239#define ISC_NO_SANITIZE_THREAD __attribute__((no_sanitize("thread")))
240#else /* if __SANITIZE_THREAD__ */
241#define ISC_NO_SANITIZE_THREAD
242#endif /* if __SANITIZE_THREAD__ */
243
244#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR >= 6)
245#define STATIC_ASSERT(cond, msg) _Static_assert(cond, msg)
246#elif __has_feature(c_static_assert)
247#define STATIC_ASSERT(cond, msg) _Static_assert(cond, msg)
248#else /* if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR >= 6) */
249
250/* Courtesy of Joseph Quinsey: https://godbolt.org/z/K9RvWS */
251#define TOKENPASTE(a, b)	a##b /* "##" is the "Token Pasting Operator" */
252#define EXPAND_THEN_PASTE(a, b) TOKENPASTE(a, b) /* expand then paste */
253#define STATIC_ASSERT(x, msg) \
254	enum { EXPAND_THEN_PASTE(ASSERT_line_, __LINE__) = 1 / ((msg) && (x)) }
255#endif /* if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR >= 6) */
256
257#ifdef UNIT_TESTING
258extern void
259mock_assert(const int result, const char *const expression,
260	    const char *const file, const int line);
261/*
262 *	Allow clang to determine that the following code is not reached
263 *	by calling abort() if the condition fails.  The abort() will
264 *	never be executed as mock_assert() and _assert_true() longjmp
265 *	or exit if the condition is false.
266 */
267#define REQUIRE(expression)                                                   \
268	((!(expression))                                                      \
269		 ? (mock_assert(0, #expression, __FILE__, __LINE__), abort()) \
270		 : (void)0)
271#define ENSURE(expression)                                                    \
272	((!(int)(expression))                                                 \
273		 ? (mock_assert(0, #expression, __FILE__, __LINE__), abort()) \
274		 : (void)0)
275#define INSIST(expression)                                                    \
276	((!(expression))                                                      \
277		 ? (mock_assert(0, #expression, __FILE__, __LINE__), abort()) \
278		 : (void)0)
279#define INVARIANT(expression)                                                 \
280	((!(expression))                                                      \
281		 ? (mock_assert(0, #expression, __FILE__, __LINE__), abort()) \
282		 : (void)0)
283#define UNREACHABLE() \
284	(mock_assert(0, "unreachable", __FILE__, __LINE__), abort())
285#define _assert_true(c, e, f, l) \
286	((c) ? (void)0 : (_assert_true(0, e, f, l), abort()))
287#define _assert_int_equal(a, b, f, l) \
288	(((a) == (b)) ? (void)0 : (_assert_int_equal(a, b, f, l), abort()))
289#define _assert_int_not_equal(a, b, f, l) \
290	(((a) != (b)) ? (void)0 : (_assert_int_not_equal(a, b, f, l), abort()))
291#else			    /* UNIT_TESTING */
292
293/*
294 * Assertions
295 */
296#include <isc/assertions.h> /* Contractual promise. */
297
298/*% Require Assertion */
299#define REQUIRE(e)   ISC_REQUIRE(e)
300/*% Ensure Assertion */
301#define ENSURE(e)    ISC_ENSURE(e)
302/*% Insist Assertion */
303#define INSIST(e)    ISC_INSIST(e)
304/*% Invariant Assertion */
305#define INVARIANT(e) ISC_INVARIANT(e)
306
307#define UNREACHABLE() ISC_UNREACHABLE()
308
309#endif /* UNIT_TESTING */
310
311/*
312 * Errors
313 */
314#include <isc/error.h>	/* Contractual promise. */
315#include <isc/strerr.h> /* for ISC_STRERRORSIZE */
316
317#define UNEXPECTED_ERROR(...) \
318	isc_error_unexpected(__FILE__, __LINE__, __func__, __VA_ARGS__)
319
320#define FATAL_ERROR(...) \
321	isc_error_fatal(__FILE__, __LINE__, __func__, __VA_ARGS__)
322
323#define REPORT_SYSERROR(report, err, fmt, ...)                        \
324	{                                                             \
325		char strerr[ISC_STRERRORSIZE];                        \
326		strerror_r(err, strerr, sizeof(strerr));              \
327		report(__FILE__, __LINE__, __func__, fmt ": %s (%d)", \
328		       ##__VA_ARGS__, strerr, err);                   \
329	}
330
331#define UNEXPECTED_SYSERROR(err, ...) \
332	REPORT_SYSERROR(isc_error_unexpected, err, __VA_ARGS__)
333
334#define FATAL_SYSERROR(err, ...) \
335	REPORT_SYSERROR(isc_error_fatal, err, __VA_ARGS__)
336
337#ifdef UNIT_TESTING
338
339#define RUNTIME_CHECK(cond) \
340	((cond) ? (void)0   \
341		: (mock_assert(0, #cond, __FILE__, __LINE__), abort()))
342
343#else /* UNIT_TESTING */
344
345#define RUNTIME_CHECK(cond) \
346	((cond) ? (void)0 : FATAL_ERROR("RUNTIME_CHECK(%s) failed", #cond))
347
348#endif /* UNIT_TESTING */
349
350/*%
351 * Time
352 */
353#define TIME_NOW(tp) RUNTIME_CHECK(isc_time_now((tp)) == ISC_R_SUCCESS)
354#define TIME_NOW_HIRES(tp) \
355	RUNTIME_CHECK(isc_time_now_hires((tp)) == ISC_R_SUCCESS)
356
357/*%
358 * Alignment
359 */
360#ifdef __GNUC__
361#define ISC_ALIGN(x, a) (((x) + (a)-1) & ~((typeof(x))(a)-1))
362#else /* ifdef __GNUC__ */
363#define ISC_ALIGN(x, a) (((x) + (a)-1) & ~((uintmax_t)(a)-1))
364#endif /* ifdef __GNUC__ */
365
366/*%
367 * Misc
368 */
369#include <isc/deprecated.h>
370
371/*%
372 * Swap
373 */
374#define ISC_SWAP(a, b)                    \
375	{                                 \
376		typeof(a) __tmp_swap = a; \
377		a = b;                    \
378		b = __tmp_swap;           \
379	}
380