summaryrefslogtreecommitdiffstats
path: root/src/lib/macros.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/macros.h')
-rw-r--r--src/lib/macros.h302
1 files changed, 302 insertions, 0 deletions
diff --git a/src/lib/macros.h b/src/lib/macros.h
new file mode 100644
index 0000000..8cd159f
--- /dev/null
+++ b/src/lib/macros.h
@@ -0,0 +1,302 @@
+#ifndef MACROS_H
+#define MACROS_H
+
+/* several useful macros, mostly from glib.h */
+
+#ifndef NULL
+# define NULL ((void *)0)
+#endif
+
+#ifndef FALSE
+# define FALSE (!1)
+#endif
+
+#ifndef TRUE
+# define TRUE (!FALSE)
+#endif
+
+#define N_ELEMENTS(arr) \
+ (sizeof(arr) / sizeof((arr)[0]))
+
+#define MEM_ALIGN(size) \
+ (((size) + MEM_ALIGN_SIZE-1) & ~((size_t) MEM_ALIGN_SIZE-1))
+
+#define PTR_OFFSET(ptr, offset) \
+ ((void *) (((uintptr_t) (ptr)) + ((size_t) (offset))))
+#define CONST_PTR_OFFSET(ptr, offset) \
+ ((const void *) (((uintptr_t) (ptr)) + ((size_t) (offset))))
+
+#define container_of(ptr, type, name) \
+ (type *)((char *)(ptr) - offsetof(type, name) + \
+ COMPILE_ERROR_IF_TYPES_NOT_COMPATIBLE(ptr, &((type *) 0)->name))
+
+/* Don't use simply MIN/MAX, as they're often defined elsewhere in include
+ files that are included after this file generating tons of warnings. */
+#define I_MIN(a, b) (((a) < (b)) ? (a) : (b))
+#define I_MAX(a, b) (((a) > (b)) ? (a) : (b))
+
+/* make it easier to cast from/to pointers. assumes that
+ sizeof(uintptr_t) == sizeof(void *) and they're both the largest datatypes
+ that are allowed to be used. so, long long isn't safe with these. */
+#define POINTER_CAST(i) \
+ ((void *) (((uintptr_t)NULL) + (i)))
+#define POINTER_CAST_TO(p, type) \
+ ((type)(uintptr_t)(p))
+
+/* Define VA_COPY() to do the right thing for copying va_list variables.
+ config.h may have already defined VA_COPY as va_copy or __va_copy. */
+#ifndef VA_COPY
+# if defined (__GNUC__) && defined (__PPC__) && \
+ (defined (_CALL_SYSV) || defined (_WIN32))
+# define VA_COPY(ap1, ap2) (*(ap1) = *(ap2))
+# elif defined (VA_COPY_AS_ARRAY)
+# define VA_COPY(ap1, ap2) memmove ((ap1), (ap2), sizeof (va_list))
+# else /* va_list is a pointer */
+# define VA_COPY(ap1, ap2) ((ap1) = (ap2))
+# endif /* va_list is a pointer */
+#endif
+
+/* Provide convenience macros for handling structure
+ * fields through their offsets.
+ */
+#define STRUCT_MEMBER_P(struct_p, struct_offset) \
+ ((void *) ((char *) (struct_p) + (long) (struct_offset)))
+#define CONST_STRUCT_MEMBER_P(struct_p, struct_offset) \
+ ((const void *) ((const char *) (struct_p) + (long) (struct_offset)))
+
+/* Provide simple macro statement wrappers:
+ STMT_START { statements; } STMT_END;
+ can be used as a single statement, as in
+ if (x) STMT_START { ... } STMT_END; else ... */
+#if !(defined (STMT_START) && defined (STMT_END))
+# define STMT_START do
+# define STMT_END while (0)
+#endif
+
+/* Provide macros to feature the GCC function attribute. */
+#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ > 4)
+# define ATTRS_DEFINED
+# define ATTR_FORMAT(format_idx, arg_idx) \
+ __attribute__((format (printf, format_idx, arg_idx)))
+# define ATTR_FORMAT_ARG(arg_idx) \
+ __attribute__((format_arg (arg_idx)))
+# define ATTR_SCANF(format_idx, arg_idx) \
+ __attribute__((format (scanf, format_idx, arg_idx)))
+# define ATTR_STRFTIME(format_idx) \
+ __attribute__((format (strftime, format_idx, 0)))
+# define ATTR_UNUSED __attribute__((unused))
+# define ATTR_NORETURN __attribute__((noreturn))
+# define ATTR_CONST __attribute__((const))
+# define ATTR_PURE __attribute__((pure))
+#else
+# define ATTR_FORMAT(format_idx, arg_idx)
+# define ATTR_FORMAT_ARG(arg_idx)
+# define ATTR_SCANF(format_idx, arg_idx)
+# define ATTR_STRFTIME(format_idx)
+# define ATTR_UNUSED
+# define ATTR_NORETURN
+# define ATTR_CONST
+# define ATTR_PURE
+#endif
+#ifdef HAVE_ATTR_NULL
+# define ATTR_NULL(...) __attribute__((null(__VA_ARGS__)))
+#else
+# define ATTR_NULL(...)
+#endif
+#ifdef HAVE_ATTR_NOWARN_UNUSED_RESULT
+# define ATTR_NOWARN_UNUSED_RESULT __attribute__((nowarn_unused_result))
+#else
+# define ATTR_NOWARN_UNUSED_RESULT
+#endif
+#if __GNUC__ > 2
+# define ATTR_MALLOC __attribute__((malloc))
+#else
+# define ATTR_MALLOC
+#endif
+#if __GNUC__ > 3
+/* GCC 4.0 and later */
+# define ATTR_WARN_UNUSED_RESULT __attribute__((warn_unused_result))
+# define ATTR_SENTINEL __attribute__((sentinel))
+#else
+# define ATTR_WARN_UNUSED_RESULT
+# define ATTR_SENTINEL
+#endif
+#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3)
+/* GCC 4.3 and later */
+# define ATTR_HOT __attribute__((hot))
+# define ATTR_COLD __attribute__((cold))
+#else
+# define ATTR_HOT
+# define ATTR_COLD
+#endif
+#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 9)
+/* GCC 4.9 and later */
+# define ATTR_RETURNS_NONNULL __attribute__((returns_nonnull))
+#else
+# define ATTR_RETURNS_NONNULL
+#endif
+#ifdef HAVE_ATTR_DEPRECATED
+# define ATTR_DEPRECATED(str) __attribute__((deprecated(str)))
+#else
+# define ATTR_DEPRECATED(str)
+#endif
+
+/* Macros to provide type safety for callback functions' context parameters.
+ This is used like:
+
+ // safe-api.h file:
+ typedef void safe_callback_t(struct foo *foo);
+
+ void safe_run(safe_callback_t *callback, void *context);
+ #define safe_run((safe_callback_t *)callback, \
+ TRUE ? context : CALLBACK_TYPECHECK(callback, void (*)(typeof(context))))
+
+ // safe-api.c file:
+ #undef safe_run
+ void safe_run(safe_callback_t *callback, void *context)
+ {
+ callback(context);
+ }
+
+ // in caller code:
+ static void callback(struct foo *foo);
+ struct foo *foo = ...;
+ safe_run(callback, foo);
+
+ The first step is to create the callback function in a normal way. Type
+ safety is added to it by creating a macro that overrides the function and
+ checks the callback type safety using CALLBACK_TYPECHECK().
+
+ The CALLBACK_TYPECHECK() macro works by giving a compiling failure if the
+ provided callback function isn't compatible with the specified function
+ type parameter. The function type parameter must use typeof(context) in
+ place of the "void *context" parameter, but otherwise use exactly the same
+ function type as what the callback is. The macro then casts the given
+ callback function into the type with "void *context".
+*/
+#ifdef HAVE_TYPE_CHECKS
+# define CALLBACK_TYPECHECK(callback, type) \
+ (COMPILE_ERROR_IF_TRUE(!__builtin_types_compatible_p( \
+ typeof(&callback), type)) ? 1 : 0)
+#else
+# define CALLBACK_TYPECHECK(callback, type) 0
+#endif
+
+#if (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ > 0)) && \
+ !defined(__cplusplus) && !defined(STATIC_CHECKER)
+# define COMPILE_ERROR_IF_TRUE(condition) \
+ (sizeof(char[1 - 2 * ((condition) ? 1 : 0)]) > 0 ? FALSE : FALSE)
+#else
+# define COMPILE_ERROR_IF_TRUE(condition) FALSE
+#endif
+
+#ifdef HAVE_TYPE_CHECKS
+# define COMPILE_ERROR_IF_TYPES_NOT_COMPATIBLE(_a, _b) \
+ COMPILE_ERROR_IF_TRUE( \
+ !__builtin_types_compatible_p(typeof(_a), typeof(_b)))
+#define COMPILE_ERROR_IF_TYPES2_NOT_COMPATIBLE(_a1, _a2, _b) \
+ COMPILE_ERROR_IF_TRUE( \
+ !__builtin_types_compatible_p(typeof(_a1), typeof(_b)) && \
+ !__builtin_types_compatible_p(typeof(_a2), typeof(_b)))
+# define TYPE_CHECKS(return_type, checks, func) \
+ (FALSE ? (return_type)(checks) : (func))
+#else
+# define COMPILE_ERROR_IF_TYPES_NOT_COMPATIBLE(_a, _b) 0
+# define COMPILE_ERROR_IF_TYPES2_NOT_COMPATIBLE(_a1, _a2, _b) 0
+# define TYPE_CHECKS(return_type, checks, func) (func)
+#endif
+
+#if __GNUC__ > 2
+# define unlikely(expr) (__builtin_expect((expr) ? 1 : 0, 0) != 0)
+# define likely(expr) (__builtin_expect((expr) ? 1 : 0, 1) != 0)
+#else
+# define unlikely(expr) expr
+# define likely(expr) expr
+#endif
+
+#if defined(__clang__) && ((__clang_major__ > 4) || (__clang_major__ == 3 && __clang_minor__ >= 9))
+# define ATTR_UNSIGNED_WRAPS __attribute__((no_sanitize("integer")))
+#else
+# define ATTR_UNSIGNED_WRAPS
+#endif
+
+/* Provide macros for error handling. */
+#ifdef DISABLE_ASSERTS
+# define i_assert(expr)
+#else
+# define i_assert(expr) STMT_START{ \
+ if (unlikely(!(expr))) \
+ i_panic("file %s: line %d (%s): assertion failed: (%s)", \
+ __FILE__, \
+ __LINE__, \
+ __func__, \
+ #expr); }STMT_END
+#endif
+
+/* Convenience macro to test the versions of dovecot. */
+#define DOVECOT_PREREQ(maj, min, micro) \
+ ((DOVECOT_VERSION_MAJOR << 24) + \
+ (DOVECOT_VERSION_MINOR << 16) + \
+ DOVECOT_VERSION_MICRO >= ((maj) << 24) + ((min) << 16) + (micro))
+
+#ifdef __cplusplus
+# undef STATIC_ARRAY
+# define STATIC_ARRAY
+#endif
+
+/* Convenience wrappers for initializing a struct with zeros, although it can
+ be used for replacing other memset()s also.
+
+ // NOTE: This is the correct way to zero the whole array
+ char arr[5]; i_zero(&arr);
+ // This will give compiler error (or zero only the first element):
+ char arr[5]; i_zero(arr);
+*/
+#define i_zero(p) \
+ memset(p, 0 + COMPILE_ERROR_IF_TRUE(sizeof(p) > sizeof(void *)), sizeof(*(p)))
+#define i_zero_safe(p) \
+ safe_memset(p, 0 + COMPILE_ERROR_IF_TRUE(sizeof(p) > sizeof(void *)), sizeof(*(p)))
+
+#define ST_CHANGED(st_a, st_b) \
+ ((st_a).st_mtime != (st_b).st_mtime || \
+ ST_MTIME_NSEC(st_a) != ST_MTIME_NSEC(st_b) || \
+ (st_a).st_size != (st_b).st_size || \
+ (st_a).st_ino != (st_b).st_ino)
+
+#ifdef HAVE_UNDEFINED_SANITIZER
+# define ATTR_NO_SANITIZE(x) __attribute__((no_sanitize((x))))
+#else
+# define ATTR_NO_SANITIZE(x)
+#endif
+
+/* gcc and clang do this differently, see
+ https://gcc.gnu.org/onlinedocs/gcc-10.2.0/gcc/Common-Function-Attributes.html */
+#ifdef HAVE_FSANITIZE_UNDEFINED
+# ifdef __clang__
+# define ATTR_NO_SANITIZE_UNDEFINED ATTR_NO_SANITIZE("undefined")
+# else
+# define ATTR_NO_SANITIZE_UNDEFINED __attribute__((no_sanitize_undefined))
+# endif
+#else
+# define ATTR_NO_SANITIZE_UNDEFINED
+#endif
+
+#ifdef HAVE_FSANITIZE_INTEGER
+# define ATTR_NO_SANITIZE_INTEGER ATTR_NO_SANITIZE("integer")
+# define ATTR_NO_SANITIZE_IMPLICIT_CONVERSION ATTR_NO_SANITIZE("implicit-conversion")
+#else
+# define ATTR_NO_SANITIZE_INTEGER
+# define ATTR_NO_SANITIZE_IMPLICIT_CONVERSION
+#endif
+
+/* negate enumeration flags in a way that avoids implicit conversion */
+#ifndef STATIC_CHECKER
+# define ENUM_NEGATE(x) \
+ ((unsigned int)(~(x)) + COMPILE_ERROR_IF_TRUE(sizeof((x)) > sizeof(int) || (x) < 0 || (x) > INT_MAX))
+#else
+/* clang scan-build keeps complaining about x > 2147483647 case, so disable the
+ sizeof check. */
+# define ENUM_NEGATE(x) ((unsigned int)(~(x)))
+#endif
+
+#endif