Home | History | Annotate | Line # | Download | only in isc
      1 /*	$NetBSD: util.h,v 1.19 2026/04/08 00:16:16 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 #include <isc/attributes.h>
     33 
     34 /***
     35  *** Clang Compatibility Macros
     36  ***/
     37 
     38 #if !defined(__has_feature)
     39 #define __has_feature(x) 0
     40 #endif /* if !defined(__has_feature) */
     41 
     42 /***
     43  *** General Macros.
     44  ***/
     45 
     46 #define MOVE_OWNERSHIP(source)                             \
     47 	({                                                 \
     48 		__typeof__(source) __ownership = (source); \
     49 		(source) = NULL;                           \
     50 		__ownership;                               \
     51 	})
     52 
     53 /*%
     54  * Legacy way how to hide unused function arguments, don't use in
     55  * the new code, rather use the ISC_ATTR_UNUSED macro that expands
     56  * to either C23's [[maybe_unused]] or __attribute__((__unused__)).
     57  *
     58  * \code
     59  * int
     60  * foo(ISC_ATTR_UNUSED char *bar) {
     61  *         ...;
     62  * }
     63  * \endcode
     64  */
     65 #define UNUSED(x) (void)(x)
     66 
     67 #if __GNUC__ >= 8 && !defined(__clang__)
     68 #define ISC_NONSTRING __attribute__((nonstring))
     69 #else /* if __GNUC__ >= 8 && !defined(__clang__) */
     70 #define ISC_NONSTRING
     71 #endif /* __GNUC__ */
     72 
     73 #if HAVE_FUNC_ATTRIBUTE_CONSTRUCTOR && HAVE_FUNC_ATTRIBUTE_DESTRUCTOR
     74 #define ISC_CONSTRUCTOR __attribute__((constructor))
     75 #define ISC_DESTRUCTOR	__attribute__((destructor))
     76 #else
     77 #define ISC_CONSTRUCTOR
     78 #define ISC_DESTRUCTOR
     79 #endif
     80 
     81 /*%
     82  * The opposite: silent warnings about stored values which are never read.
     83  */
     84 #define POST(x) (void)(x)
     85 
     86 #define ISC_MAX(a, b) ((a) > (b) ? (a) : (b))
     87 #define ISC_MIN(a, b) ((a) < (b) ? (a) : (b))
     88 
     89 #define ISC_CLAMP(v, x, y) ((v) < (x) ? (x) : ((v) > (y) ? (y) : (v)))
     90 
     91 #define ISC_MAX3(a, b, c) ISC_MAX(ISC_MAX((a), (b)), (c))
     92 
     93 /*%
     94  * The UNCONST() macro can be used to omit warnings produced by certain
     95  * compilers when operating with pointers declared with the const type qual-
     96  * ifier in a context without such qualifier.  Examples include passing a
     97  * pointer declared with the const qualifier to a function without such
     98  * qualifier, and variable assignment from a const pointer to a non-const
     99  * pointer.
    100  *
    101  * As the macro may hide valid errors, their usage is not recommended
    102  * unless there is a well-thought reason for a cast.  A typical use case for
    103  * __UNCONST() involve an API that does not follow the so-called ``const
    104  * correctness'' even if it would be appropriate.
    105  */
    106 #define UNCONST(ptr) ((void *)(uintptr_t)(ptr))
    107 
    108 #define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
    109 
    110 /*
    111  * Optional return values, or out-arguments
    112  */
    113 #define SET_IF_NOT_NULL(obj, val) \
    114 	if ((obj) != NULL) {      \
    115 		*(obj) = (val);   \
    116 	}
    117 
    118 /*%
    119  * Get the allocation size for a struct with a flexible array member
    120  * containing `count` elements. The struct is identified by a pointer,
    121  * typically the one that points to (or will point to) the allocation.
    122  */
    123 #define STRUCT_FLEX_SIZE(pointer, member, count) \
    124 	(sizeof(*(pointer)) + sizeof(*(pointer)->member) * (count))
    125 
    126 /*%
    127  * Use this in translation units that would otherwise be empty, to
    128  * suppress compiler warnings.
    129  */
    130 #define EMPTY_TRANSLATION_UNIT extern int isc__empty;
    131 
    132 /*%
    133  * We use macros instead of calling the routines directly because
    134  * the capital letters make the locking stand out.
    135  */
    136 
    137 #ifdef ISC_UTIL_TRACEON
    138 #define ISC_UTIL_TRACE(a) a
    139 #include <stdio.h> /* Required for fprintf/stderr when tracing. */
    140 #else		   /* ifdef ISC_UTIL_TRACEON */
    141 #define ISC_UTIL_TRACE(a)
    142 #endif /* ifdef ISC_UTIL_TRACEON */
    143 
    144 #include <isc/result.h> /* Contractual promise. */
    145 
    146 #define SPINLOCK(sp)                                                           \
    147 	{                                                                      \
    148 		ISC_UTIL_TRACE(fprintf(stderr, "SPINLOCKING %p %s %d\n", (sp), \
    149 				       __FILE__, __LINE__));                   \
    150 		isc_spinlock_lock((sp));                                       \
    151 		ISC_UTIL_TRACE(fprintf(stderr, "SPINLOCKED %p %s %d\n", (sp),  \
    152 				       __FILE__, __LINE__));                   \
    153 	}
    154 #define SPINUNLOCK(sp)                                                    \
    155 	{                                                                 \
    156 		isc_spinlock_unlock((sp));                                \
    157 		ISC_UTIL_TRACE(fprintf(stderr, "SPINUNLOCKED %p %s %d\n", \
    158 				       (sp), __FILE__, __LINE__));        \
    159 	}
    160 
    161 #define LOCK(lp)                                                           \
    162 	{                                                                  \
    163 		ISC_UTIL_TRACE(fprintf(stderr, "LOCKING %p %s %d\n", (lp), \
    164 				       __FILE__, __LINE__));               \
    165 		isc_mutex_lock((lp));                                      \
    166 		ISC_UTIL_TRACE(fprintf(stderr, "LOCKED %p %s %d\n", (lp),  \
    167 				       __FILE__, __LINE__));               \
    168 	}
    169 #define UNLOCK(lp)                                                          \
    170 	{                                                                   \
    171 		isc_mutex_unlock((lp));                                     \
    172 		ISC_UTIL_TRACE(fprintf(stderr, "UNLOCKED %p %s %d\n", (lp), \
    173 				       __FILE__, __LINE__));                \
    174 	}
    175 
    176 #define BROADCAST(cvp)                                                        \
    177 	{                                                                     \
    178 		ISC_UTIL_TRACE(fprintf(stderr, "BROADCAST %p %s %d\n", (cvp), \
    179 				       __FILE__, __LINE__));                  \
    180 		isc_condition_broadcast((cvp));                               \
    181 	}
    182 #define SIGNAL(cvp)                                                        \
    183 	{                                                                  \
    184 		ISC_UTIL_TRACE(fprintf(stderr, "SIGNAL %p %s %d\n", (cvp), \
    185 				       __FILE__, __LINE__));               \
    186 		isc_condition_signal((cvp));                               \
    187 	}
    188 #define WAIT(cvp, lp)                                                         \
    189 	{                                                                     \
    190 		ISC_UTIL_TRACE(fprintf(stderr, "WAIT %p LOCK %p %s %d\n",     \
    191 				       (cvp), (lp), __FILE__, __LINE__));     \
    192 		isc_condition_wait((cvp), (lp));                              \
    193 		ISC_UTIL_TRACE(fprintf(stderr, "WAITED %p LOCKED %p %s %d\n", \
    194 				       (cvp), (lp), __FILE__, __LINE__));     \
    195 	}
    196 
    197 /*
    198  * isc_condition_waituntil can return ISC_R_TIMEDOUT, so we
    199  * don't RUNTIME_CHECK the result.
    200  *
    201  *  XXX Also, can't really debug this then...
    202  */
    203 
    204 #define WAITUNTIL(cvp, lp, tp) isc_condition_waituntil((cvp), (lp), (tp))
    205 
    206 #define RWLOCK(lp, t)                                                         \
    207 	{                                                                     \
    208 		ISC_UTIL_TRACE(fprintf(stderr, "RWLOCK %p, %d %s %d\n", (lp), \
    209 				       (t), __FILE__, __LINE__));             \
    210 		isc_rwlock_lock((lp), (t));                                   \
    211 		ISC_UTIL_TRACE(fprintf(stderr, "RWLOCKED %p, %d %s %d\n",     \
    212 				       (lp), (t), __FILE__, __LINE__));       \
    213 	}
    214 #define RWUNLOCK(lp, t)                                                   \
    215 	{                                                                 \
    216 		ISC_UTIL_TRACE(fprintf(stderr, "RWUNLOCK %p, %d %s %d\n", \
    217 				       (lp), (t), __FILE__, __LINE__));   \
    218 		isc_rwlock_unlock((lp), (t));                             \
    219 	}
    220 
    221 #define RDLOCK(lp)   RWLOCK(lp, isc_rwlocktype_read)
    222 #define RDUNLOCK(lp) RWUNLOCK(lp, isc_rwlocktype_read)
    223 #define WRLOCK(lp)   RWLOCK(lp, isc_rwlocktype_write)
    224 #define WRUNLOCK(lp) RWUNLOCK(lp, isc_rwlocktype_write)
    225 
    226 #define UPGRADELOCK(lock, locktype)                                         \
    227 	{                                                                   \
    228 		if (locktype == isc_rwlocktype_read) {                      \
    229 			if (isc_rwlock_tryupgrade(lock) == ISC_R_SUCCESS) { \
    230 				locktype = isc_rwlocktype_write;            \
    231 			} else {                                            \
    232 				RWUNLOCK(lock, locktype);                   \
    233 				locktype = isc_rwlocktype_write;            \
    234 				RWLOCK(lock, locktype);                     \
    235 			}                                                   \
    236 		}                                                           \
    237 		INSIST(locktype == isc_rwlocktype_write);                   \
    238 	}
    239 
    240 /*
    241  * List Macros.
    242  */
    243 #include <isc/list.h> /* Contractual promise. */
    244 
    245 #define LIST(type)		       ISC_LIST(type)
    246 #define INIT_LIST(type)		       ISC_LIST_INIT(type)
    247 #define LINK(type)		       ISC_LINK(type)
    248 #define INIT_LINK(elt, link)	       ISC_LINK_INIT(elt, link)
    249 #define HEAD(list)		       ISC_LIST_HEAD(list)
    250 #define TAIL(list)		       ISC_LIST_TAIL(list)
    251 #define EMPTY(list)		       ISC_LIST_EMPTY(list)
    252 #define PREV(elt, link)		       ISC_LIST_PREV(elt, link)
    253 #define NEXT(elt, link)		       ISC_LIST_NEXT(elt, link)
    254 #define APPEND(list, elt, link)	       ISC_LIST_APPEND(list, elt, link)
    255 #define PREPEND(list, elt, link)       ISC_LIST_PREPEND(list, elt, link)
    256 #define UNLINK(list, elt, link)	       ISC_LIST_UNLINK(list, elt, link)
    257 #define ENQUEUE(list, elt, link)       ISC_LIST_APPEND(list, elt, link)
    258 #define DEQUEUE(list, elt, link)       ISC_LIST_UNLINK(list, elt, link)
    259 #define INSERTBEFORE(li, b, e, ln)     ISC_LIST_INSERTBEFORE(li, b, e, ln)
    260 #define INSERTAFTER(li, a, e, ln)      ISC_LIST_INSERTAFTER(li, a, e, ln)
    261 #define APPENDLIST(list1, list2, link) ISC_LIST_APPENDLIST(list1, list2, link)
    262 
    263 /*%
    264  * Performance
    265  */
    266 
    267 /* GCC defines __SANITIZE_ADDRESS__, so reuse the macro for clang */
    268 #if __has_feature(address_sanitizer)
    269 #define __SANITIZE_ADDRESS__ 1
    270 #endif /* if __has_feature(address_sanitizer) */
    271 
    272 #if __SANITIZE_ADDRESS__
    273 #define ISC_NO_SANITIZE_ADDRESS __attribute__((no_sanitize("address")))
    274 #else /* if __SANITIZE_ADDRESS__ */
    275 #define ISC_NO_SANITIZE_ADDRESS
    276 #endif /* if __SANITIZE_ADDRESS__ */
    277 
    278 #if __has_feature(thread_sanitizer)
    279 #define __SANITIZE_THREAD__ 1
    280 #endif /* if __has_feature(thread_sanitizer) */
    281 
    282 #if __SANITIZE_THREAD__
    283 #define ISC_NO_SANITIZE_THREAD __attribute__((no_sanitize("thread")))
    284 #else /* if __SANITIZE_THREAD__ */
    285 #define ISC_NO_SANITIZE_THREAD
    286 #endif /* if __SANITIZE_THREAD__ */
    287 
    288 #ifndef __lint__
    289 #if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR >= 6)
    290 #define STATIC_ASSERT(cond, msg) _Static_assert(cond, msg)
    291 #elif __has_feature(c_static_assert)
    292 #define STATIC_ASSERT(cond, msg) _Static_assert(cond, msg)
    293 #else /* if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR >= 6) */
    294 
    295 /* Courtesy of Joseph Quinsey: https://godbolt.org/z/K9RvWS */
    296 #define TOKENPASTE(a, b)	a##b /* "##" is the "Token Pasting Operator" */
    297 #define EXPAND_THEN_PASTE(a, b) TOKENPASTE(a, b) /* expand then paste */
    298 #define STATIC_ASSERT(x, msg) \
    299 	enum { EXPAND_THEN_PASTE(ASSERT_line_, __LINE__) = 1 / ((msg) && (x)) }
    300 #endif /* if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR >= 6) */
    301 #else
    302 #define STATIC_ASSERT(cond, msg)
    303 #endif
    304 
    305 #ifdef UNIT_TESTING
    306 extern void
    307 mock_assert(const int result, const char *const expression,
    308 	    const char *const file, const int line);
    309 /*
    310  *	Allow clang to determine that the following code is not reached
    311  *	by calling abort() if the condition fails.  The abort() will
    312  *	never be executed as mock_assert() and _assert_true() longjmp
    313  *	or exit if the condition is false.
    314  */
    315 #define REQUIRE(expression)                                                   \
    316 	((!(expression))                                                      \
    317 		 ? (mock_assert(0, #expression, __FILE__, __LINE__), abort()) \
    318 		 : (void)0)
    319 #define ENSURE(expression)                                                    \
    320 	((!(int)(expression))                                                 \
    321 		 ? (mock_assert(0, #expression, __FILE__, __LINE__), abort()) \
    322 		 : (void)0)
    323 #define INSIST(expression)                                                    \
    324 	((!(expression))                                                      \
    325 		 ? (mock_assert(0, #expression, __FILE__, __LINE__), abort()) \
    326 		 : (void)0)
    327 #define INVARIANT(expression)                                                 \
    328 	((!(expression))                                                      \
    329 		 ? (mock_assert(0, #expression, __FILE__, __LINE__), abort()) \
    330 		 : (void)0)
    331 #define UNREACHABLE() \
    332 	(mock_assert(0, "unreachable", __FILE__, __LINE__), abort())
    333 #define _assert_true(c, e, f, l) \
    334 	((c) ? (void)0 : (_assert_true(0, e, f, l), abort()))
    335 #define _assert_int_equal(a, b, f, l) \
    336 	(((a) == (b)) ? (void)0 : (_assert_int_equal(a, b, f, l), abort()))
    337 #define _assert_int_not_equal(a, b, f, l) \
    338 	(((a) != (b)) ? (void)0 : (_assert_int_not_equal(a, b, f, l), abort()))
    339 #else			    /* UNIT_TESTING */
    340 
    341 /*
    342  * Assertions
    343  */
    344 #include <isc/assertions.h> /* Contractual promise. */
    345 
    346 /*% Require Assertion */
    347 #define REQUIRE(e)   ISC_REQUIRE(e)
    348 /*% Ensure Assertion */
    349 #define ENSURE(e)    ISC_ENSURE(e)
    350 /*% Insist Assertion */
    351 #define INSIST(e)    ISC_INSIST(e)
    352 /*% Invariant Assertion */
    353 #define INVARIANT(e) ISC_INVARIANT(e)
    354 
    355 #define UNREACHABLE() ISC_UNREACHABLE()
    356 
    357 #endif /* UNIT_TESTING */
    358 
    359 /*
    360  * Errors
    361  */
    362 #include <errno.h> /* for errno */
    363 
    364 #include <isc/error.h>	/* Contractual promise. */
    365 #include <isc/strerr.h> /* for ISC_STRERRORSIZE */
    366 
    367 #define UNEXPECTED_ERROR(...) \
    368 	isc_error_unexpected(__FILE__, __LINE__, __func__, __VA_ARGS__)
    369 
    370 #define FATAL_ERROR(...) \
    371 	isc_error_fatal(__FILE__, __LINE__, __func__, __VA_ARGS__)
    372 
    373 #define REPORT_SYSERROR(report, err, fmt, ...)                        \
    374 	{                                                             \
    375 		char strerr[ISC_STRERRORSIZE];                        \
    376 		strerror_r(err, strerr, sizeof(strerr));              \
    377 		report(__FILE__, __LINE__, __func__, fmt ": %s (%d)", \
    378 		       ##__VA_ARGS__, strerr, err);                   \
    379 	}
    380 
    381 #define UNEXPECTED_SYSERROR(err, ...) \
    382 	REPORT_SYSERROR(isc_error_unexpected, err, __VA_ARGS__)
    383 
    384 #define FATAL_SYSERROR(err, ...) \
    385 	REPORT_SYSERROR(isc_error_fatal, err, __VA_ARGS__)
    386 
    387 #ifdef UNIT_TESTING
    388 
    389 #define RUNTIME_CHECK(cond) \
    390 	((cond) ? (void)0   \
    391 		: (mock_assert(0, #cond, __FILE__, __LINE__), abort()))
    392 
    393 #else /* UNIT_TESTING */
    394 
    395 #define RUNTIME_CHECK(cond) \
    396 	((cond) ? (void)0 : FATAL_ERROR("RUNTIME_CHECK(%s) failed", #cond))
    397 
    398 #endif /* UNIT_TESTING */
    399 
    400 /*
    401  * Check for ISC_R_SUCCESS. On any other result, jump to a cleanup
    402  * label. (This macro requires the function to define `result`
    403  * and `cleanup:`.)
    404  */
    405 #define CHECK(r)                             \
    406 	do {                                 \
    407 		result = (r);                \
    408 		if (result != ISC_R_SUCCESS) \
    409 			goto cleanup;        \
    410 	} while (0)
    411 
    412 /*
    413  * Check for ISC_R_SUCCESS and continue if found. For any other
    414  * result, return the result.
    415  */
    416 #define RETERR(x)                        \
    417 	do {                             \
    418 		isc_result_t _r = (x);   \
    419 		if (_r != ISC_R_SUCCESS) \
    420 			return ((_r));   \
    421 	} while (0)
    422 
    423 /*%
    424  * Runtime check which logs the error value returned by a POSIX Threads
    425  * function and the error string that corresponds to it
    426  */
    427 #define PTHREADS_RUNTIME_CHECK(func, ret)           \
    428 	if ((ret) != 0) {                           \
    429 		FATAL_SYSERROR(ret, "%s()", #func); \
    430 	}
    431 
    432 /*%
    433  * Alignment
    434  */
    435 #ifdef __GNUC__
    436 #define ISC_ALIGN(x, a) (((x) + (a) - 1) & ~((typeof(x))(a) - 1))
    437 #else /* ifdef __GNUC__ */
    438 #define ISC_ALIGN(x, a) (((x) + (a) - 1) & ~((uintmax_t)(a) - 1))
    439 #endif /* ifdef __GNUC__ */
    440 
    441 /*%
    442  * Swap
    443  */
    444 #define ISC_SWAP(a, b)                    \
    445 	{                                 \
    446 		typeof(a) __tmp_swap = a; \
    447 		a = b;                    \
    448 		b = __tmp_swap;           \
    449 	}
    450