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