/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * Copyright (C) 2012 Colin Walters . * Copyright (C) 2014 Red Hat, Inc. */ #ifndef __NM_MACROS_INTERNAL_H__ #define __NM_MACROS_INTERNAL_H__ #include #include #include #include #include #include /*****************************************************************************/ /* most of our code is single-threaded with a mainloop. Hence, we usually don't need * any thread-safety. Sometimes, we do need thread-safety (nm-logging), but we can * avoid locking if we are on the main-thread by: * * - modifications of shared data is done infrequently and only from the * main-thread (nm_logging_setup()) * - read-only access is done frequently (nm_logging_enabled()) * - from the main-thread, we can do that without locking (because * all modifications are also done on the main thread. * - from other threads, we need locking. But this is expected to be * done infrequently too. Important is the lock-free fast-path on the * main-thread. * * By defining NM_THREAD_SAFE_ON_MAIN_THREAD you indicate that this code runs * on the main-thread. It is by default defined to "1". If you have code that * is also used on another thread, redefine the define to 0 (to opt in into * the slow-path). */ #define NM_THREAD_SAFE_ON_MAIN_THREAD 1 /*****************************************************************************/ #include "nm-glib.h" /*****************************************************************************/ /* Historically, our cleanup macros come from a long gone library * libgsystem, hence the "gs_" prefix. We still keep using them, * although maybe we should drop them and use our respective nm_auto* * macros (TODO). * * GLib also has g_auto() since 2.44. First of all, we still don't * depend on 2.44, so we would have add compat implementations to * "nm-glib.h" or bump the version. * Also, they work differently (nm_auto_unref_hashtable vs g_auto(GHashTable)). * If we were to switch to g_auto(), the change would be slightly more complicated * than replacing one macro with another (but still easy). * However, the reason for using our nm_auto* macros is that we also want cleanup * macros in libnm-std-aux, which has no glib dependency. So we still would have * some nm_auto* macros mixed with g_auto(). Instead, we consistently use * nm_auto* macros (and the gs_* aliases). * * Note that c-stdaux also brings cleanup macros like _c_cleanup_(c_freep). * We use c-stdaux like a proper internal library, so we could instead switch * from nm_auto* macros to _c_cleanup_(). Unlike glib, c-stdaux is used by * libnm-std-aux. Again, _c_cleanup_ follows a different pattern both from * nm_auto* and g_auto(). */ #define gs_free nm_auto_g_free #define gs_unref_object nm_auto_unref_object #define gs_unref_variant nm_auto_unref_variant #define gs_unref_array nm_auto_unref_array #define gs_unref_ptrarray nm_auto_unref_ptrarray #define gs_unref_hashtable nm_auto_unref_hashtable #define gs_unref_bytes nm_auto_unref_bytes #define gs_strfreev nm_auto_strfreev #define gs_free_error nm_auto_free_error /*****************************************************************************/ NM_AUTO_DEFINE_FCN_VOID0(void *, _nm_auto_g_free, g_free); #define nm_auto_g_free nm_auto(_nm_auto_g_free) NM_AUTO_DEFINE_FCN_VOID0(GObject *, _nm_auto_unref_object, g_object_unref); #define nm_auto_unref_object nm_auto(_nm_auto_unref_object) NM_AUTO_DEFINE_FCN0(GVariant *, _nm_auto_unref_variant, g_variant_unref); #define nm_auto_unref_variant nm_auto(_nm_auto_unref_variant) NM_AUTO_DEFINE_FCN0(GArray *, _nm_auto_unref_array, g_array_unref); #define nm_auto_unref_array nm_auto(_nm_auto_unref_array) NM_AUTO_DEFINE_FCN0(GPtrArray *, _nm_auto_unref_ptrarray, g_ptr_array_unref); #define nm_auto_unref_ptrarray nm_auto(_nm_auto_unref_ptrarray) NM_AUTO_DEFINE_FCN0(GHashTable *, _nm_auto_unref_hashtable, g_hash_table_unref); #define nm_auto_unref_hashtable nm_auto(_nm_auto_unref_hashtable) NM_AUTO_DEFINE_FCN0(GSList *, _nm_auto_free_slist, g_slist_free); #define nm_auto_free_slist nm_auto(_nm_auto_free_slist) NM_AUTO_DEFINE_FCN0(GBytes *, _nm_auto_unref_bytes, g_bytes_unref); #define nm_auto_unref_bytes nm_auto(_nm_auto_unref_bytes) NM_AUTO_DEFINE_FCN0(char **, _nm_auto_strfreev, g_strfreev); #define nm_auto_strfreev nm_auto(_nm_auto_strfreev) NM_AUTO_DEFINE_FCN0(GError *, _nm_auto_free_error, g_error_free); #define nm_auto_free_error nm_auto(_nm_auto_free_error) NM_AUTO_DEFINE_FCN0(GKeyFile *, _nm_auto_unref_keyfile, g_key_file_unref); #define nm_auto_unref_keyfile nm_auto(_nm_auto_unref_keyfile) NM_AUTO_DEFINE_FCN0(GVariantIter *, _nm_auto_free_variant_iter, g_variant_iter_free); #define nm_auto_free_variant_iter nm_auto(_nm_auto_free_variant_iter) NM_AUTO_DEFINE_FCN0(GVariantBuilder *, _nm_auto_unref_variant_builder, g_variant_builder_unref); #define nm_auto_unref_variant_builder nm_auto(_nm_auto_unref_variant_builder) #define nm_auto_clear_variant_builder nm_auto(g_variant_builder_clear) NM_AUTO_DEFINE_FCN0(GList *, _nm_auto_free_list, g_list_free); #define nm_auto_free_list nm_auto(_nm_auto_free_list) NM_AUTO_DEFINE_FCN0(GChecksum *, _nm_auto_checksum_free, g_checksum_free); #define nm_auto_free_checksum nm_auto(_nm_auto_checksum_free) #define nm_auto_unset_gvalue nm_auto(g_value_unset) NM_AUTO_DEFINE_FCN_VOID0(void *, _nm_auto_unref_gtypeclass, g_type_class_unref); #define nm_auto_unref_gtypeclass nm_auto(_nm_auto_unref_gtypeclass) NM_AUTO_DEFINE_FCN0(GByteArray *, _nm_auto_unref_bytearray, g_byte_array_unref); #define nm_auto_unref_bytearray nm_auto(_nm_auto_unref_bytearray) NM_AUTO_DEFINE_FCN0(GDateTime *, _nm_auto_unref_gdatetime, g_date_time_unref); #define nm_auto_unref_gdatetime nm_auto(_nm_auto_unref_gdatetime) static inline void _nm_auto_free_gstring(GString **str) { if (*str) g_string_free(*str, TRUE); } #define nm_auto_free_gstring nm_auto(_nm_auto_free_gstring) NM_AUTO_DEFINE_FCN0(GSource *, _nm_auto_unref_gsource, g_source_unref); #define nm_auto_unref_gsource nm_auto(_nm_auto_unref_gsource) NM_AUTO_DEFINE_FCN0(guint, _nm_auto_remove_source, g_source_remove); #define nm_auto_remove_source nm_auto(_nm_auto_remove_source) NM_AUTO_DEFINE_FCN0(GIOChannel *, _nm_auto_unref_io_channel, g_io_channel_unref); #define nm_auto_unref_io_channel nm_auto(_nm_auto_unref_io_channel) NM_AUTO_DEFINE_FCN0(GMainLoop *, _nm_auto_unref_gmainloop, g_main_loop_unref); #define nm_auto_unref_gmainloop nm_auto(_nm_auto_unref_gmainloop) NM_AUTO_DEFINE_FCN0(GOptionContext *, _nm_auto_free_option_context, g_option_context_free); #define nm_auto_free_option_context nm_auto(_nm_auto_free_option_context) static inline void _nm_auto_freev(gpointer ptr) { gpointer **p = ptr; gpointer *_ptr; if (*p) { for (_ptr = *p; *_ptr; _ptr++) g_free(*_ptr); g_free(*p); } } /* g_free a NULL terminated array of pointers, with also freeing each * pointer with g_free(). It essentially does the same as * gs_strfreev / g_strfreev(), but not restricted to strv arrays. */ #define nm_auto_freev nm_auto(_nm_auto_freev) /*****************************************************************************/ #define _NM_MACRO_COMMA_IF_ARGS(...) \ _NM_MACRO_CALL(G_PASTE(__NM_MACRO_COMMA_IF_ARGS_, NM_NARG_MAX1(__VA_ARGS__)), __VA_ARGS__) #define __NM_MACRO_COMMA_IF_ARGS_0() #define __NM_MACRO_COMMA_IF_ARGS_1(...) , /*****************************************************************************/ /* http://stackoverflow.com/a/11172679 */ #define _NM_UTILS_MACRO_FIRST(...) __NM_UTILS_MACRO_FIRST_HELPER(__VA_ARGS__, throwaway) #define __NM_UTILS_MACRO_FIRST_HELPER(first, ...) first #define _NM_UTILS_MACRO_REST(...) \ _NM_MACRO_CALL(G_PASTE(__NM_UTILS_MACRO_REST_, NM_NARG_MAX2(__VA_ARGS__)), __VA_ARGS__) #define __NM_UTILS_MACRO_REST_0() #define __NM_UTILS_MACRO_REST_1(first) #define __NM_UTILS_MACRO_REST_2(first, ...) , __VA_ARGS__ /*****************************************************************************/ /** * NM_G_ERROR_MSG: * @error: (nullable): the #GError instance * * All functions must follow the convention that when they * return a failure, they must also set the GError to a valid * message. For external API however, we want to be extra * careful before accessing the error instance. Use NM_G_ERROR_MSG() * which is safe to use on NULL. * * Returns: the error message. **/ static inline const char * NM_G_ERROR_MSG(GError *error) { return error ? (error->message ?: "(null)") : "(no-error)"; } /*****************************************************************************/ #if _NM_CC_SUPPORT_GENERIC #define _NM_CONSTCAST_FULL_1(type, obj_expr, obj) \ (_Generic((obj_expr), \ const void *: ((const type *) (obj)), \ void *: ((type *) (obj)), \ const type *: ((const type *) (obj)), \ type *: ((type *) (obj)))) #define _NM_CONSTCAST_FULL_2(type, obj_expr, obj, alias_type2) \ (_Generic((obj_expr), \ const void *: ((const type *) (obj)), \ void *: ((type *) (obj)), \ const alias_type2 *: ((const type *) (obj)), \ alias_type2 *: ((type *) (obj)), \ const type *: ((const type *) (obj)), \ type *: ((type *) (obj)))) #define _NM_CONSTCAST_FULL_3(type, obj_expr, obj, alias_type2, alias_type3) \ (_Generic((obj_expr), \ const void *: ((const type *) (obj)), \ void *: ((type *) (obj)), \ const alias_type2 *: ((const type *) (obj)), \ alias_type2 *: ((type *) (obj)), \ const alias_type3 *: ((const type *) (obj)), \ alias_type3 *: ((type *) (obj)), \ const type *: ((const type *) (obj)), \ type *: ((type *) (obj)))) #define _NM_CONSTCAST_FULL_4(type, obj_expr, obj, alias_type2, alias_type3, alias_type4) \ (_Generic((obj_expr), \ const void *: ((const type *) (obj)), \ void *: ((type *) (obj)), \ const alias_type2 *: ((const type *) (obj)), \ alias_type2 *: ((type *) (obj)), \ const alias_type3 *: ((const type *) (obj)), \ alias_type3 *: ((type *) (obj)), \ const alias_type4 *: ((const type *) (obj)), \ alias_type4 *: ((type *) (obj)), \ const type *: ((const type *) (obj)), \ type *: ((type *) (obj)))) #define _NM_CONSTCAST_FULL_x(type, obj_expr, obj, n, ...) \ (_NM_CONSTCAST_FULL_##n(type, obj_expr, obj, ##__VA_ARGS__)) #define _NM_CONSTCAST_FULL_y(type, obj_expr, obj, n, ...) \ (_NM_CONSTCAST_FULL_x(type, obj_expr, obj, n, ##__VA_ARGS__)) #define NM_CONSTCAST_FULL(type, obj_expr, obj, ...) \ (_NM_CONSTCAST_FULL_y(type, obj_expr, obj, NM_NARG(dummy, ##__VA_ARGS__), ##__VA_ARGS__)) #else #define NM_CONSTCAST_FULL(type, obj_expr, obj, ...) ((type *) (obj)) #endif #define NM_CONSTCAST(type, obj, ...) NM_CONSTCAST_FULL(type, (obj), (obj), ##__VA_ARGS__) #if _NM_CC_SUPPORT_GENERIC #define NM_UNCONST_PTR(type, arg) \ _Generic((arg), const type *: ((type *) (arg)), type *: ((type *) (arg))) #else #define NM_UNCONST_PTR(type, arg) ((type *) (arg)) #endif #if _NM_CC_SUPPORT_GENERIC #define NM_UNCONST_PPTR(type, arg) \ _Generic((arg), \ const type **: ((type **) (arg)), \ type **: ((type **) (arg)), \ const type *const *: ((type **) (arg)), \ type *const *: ((type **) (arg))) #else #define NM_UNCONST_PPTR(type, arg) ((type **) (arg)) #endif #define NM_GOBJECT_CAST(type, obj, is_check, ...) \ ({ \ const void *_obj = (obj); \ \ nm_assert(_obj || (is_check(_obj))); \ NM_CONSTCAST_FULL(type, (obj), _obj, GObject, ##__VA_ARGS__); \ }) #define NM_GOBJECT_CAST_NON_NULL(type, obj, is_check, ...) \ ({ \ const void *_obj = (obj); \ \ nm_assert(is_check(_obj)); \ NM_CONSTCAST_FULL(type, (obj), _obj, GObject, ##__VA_ARGS__); \ }) #define NM_ENSURE_NOT_NULL(ptr) \ ({ \ typeof(ptr) _ptr = (ptr); \ \ nm_assert(_ptr != NULL); \ _ptr; \ }) /* Casts (arg) to (type**), but also having a compile time check that * the arg is some sort of pointer to a pointer. * * The only purpose of this macro is some additional compile time safety, * that the argument is a pointer to pointer. But then it will C cast any kind * of such argument. */ #define NM_CAST_PPTR(type, arg) \ ({ \ _NM_ENSURE_POINTER(*(arg)); \ (type **) (arg); \ }) #if _NM_CC_SUPPORT_GENERIC /* these macros cast (value) to * - "const char **" (for "MC", mutable-const) * - "const char *const*" (for "CC", const-const) * The point is to do this cast, but only accepting pointers * that are compatible already. * * The problem is, if you add a function like g_strdupv(), the input * argument is not modified (CC), but you want to make it work also * for "char **". C doesn't allow this form of casting (for good reasons), * so the function makes a choice like g_strdupv(char**). That means, * every time you want to call it with a const argument, you need to * explicitly cast it. * * These macros do the cast, but they only accept a compatible input * type, otherwise they will fail compilation. */ #define NM_CAST_STRV_MC(value) \ (_Generic((value), \ const char **: (const char **) (value), \ char **: (const char **) (value), \ void *: (const char **) (value))) #define NM_CAST_STRV_CC(value) \ (_Generic((value), \ const char *const *: (const char *const *) (value), \ const char **: (const char *const *) (value), \ char *const *: (const char *const *) (value), \ char **: (const char *const *) (value), \ const void *: (const char *const *) (value), \ void *: (const char *const *) (value))) #else #define NM_CAST_STRV_MC(value) ((const char **) (value)) #define NM_CAST_STRV_CC(value) ((const char *const *) (value)) #endif #if _NM_CC_SUPPORT_GENERIC #define NM_PROPAGATE_CONST(test_expr, ptr) \ (_Generic((test_expr), \ const typeof(*(test_expr)) *: ((const typeof(*(ptr)) *) (ptr)), \ default: (_Generic((test_expr), typeof(*(test_expr)) *: (ptr))))) #else #define NM_PROPAGATE_CONST(test_expr, ptr) (ptr) #endif /* with the way it is implemented, the caller may or may not pass a trailing * ',' and it will work. However, this makes the macro unsuitable for initializing * an array. */ #define NM_MAKE_STRV(...) \ ((const char *const[(sizeof(((const char *const[]) {__VA_ARGS__})) / sizeof(const char *)) \ + 1]){__VA_ARGS__}) /*****************************************************************************/ /* NM_CACHED_QUARK() returns the GQuark for @string, but caches * it in a static variable to speed up future lookups. * * @string must be a string literal. */ #define NM_CACHED_QUARK(string) \ ({ \ static GQuark _nm_cached_quark = 0; \ \ (G_LIKELY(_nm_cached_quark != 0) \ ? _nm_cached_quark \ : (_nm_cached_quark = g_quark_from_static_string("" string ""))); \ }) /* NM_CACHED_QUARK_FCN() is essentially the same as G_DEFINE_QUARK * with two differences: * - @string must be a quoted string-literal * - @fcn must be the full function name, while G_DEFINE_QUARK() appends * "_quark" to the function name. * Both properties of G_DEFINE_QUARK() are non favorable, because you can no * longer grep for string/fcn -- unless you are aware that you are searching * for G_DEFINE_QUARK() and omit quotes / append _quark(). With NM_CACHED_QUARK_FCN(), * ctags/cscope can locate the use of @fcn (though it doesn't recognize that * NM_CACHED_QUARK_FCN() defines it). */ #define NM_CACHED_QUARK_FCN(string, fcn) \ GQuark fcn(void) \ { \ return NM_CACHED_QUARK(string); \ } \ _NM_DUMMY_STRUCT_FOR_TRAILING_SEMICOLON /*****************************************************************************/ static inline GString * nm_gstring_prepare(GString **l) { if (*l) g_string_set_size(*l, 0); else *l = g_string_sized_new(30); return *l; } static inline GString * nm_gstring_add_space_delimiter(GString *str) { if (str->len > 0) g_string_append_c(str, ' '); return str; } static inline gboolean nm_str_is_empty(const char *str) { /* %NULL is also accepted, and also "empty". */ return !str || !str[0]; } static inline const char * nm_str_not_empty(const char *str) { return !nm_str_is_empty(str) ? str : NULL; } static inline char * nm_strdup_not_empty(const char *str) { return !nm_str_is_empty(str) ? g_strdup(str) : NULL; } static inline char * nm_str_truncate(char *str) { /* This is trivial, and is only useful in a macro, to * ensure that we access the macro argument only once. */ nm_assert(str); str[0] = '\0'; return str; } static inline char * nm_str_realloc(char *str) { gs_free char *s = str; /* Returns a new clone of @str and frees @str. The point is that @str * possibly points to a larger chunck of memory. We want to freshly allocate * a buffer. * * We could use realloc(), but that might not do anything or leave * @str in its memory pool for chunks of a different size (bad for * fragmentation). * * This is only useful when we want to keep the buffer around for a long * time and want to re-allocate a more optimal buffer. */ return g_strdup(s); } /*****************************************************************************/ #define NM_PRINT_FMT_QUOTED2(cond, prefix, str, str_else) \ (cond) ? (prefix) : "", (cond) ? (str) : (str_else) #define NM_PRINT_FMT_QUOTED(cond, prefix, str, suffix, str_else) \ (cond) ? (prefix) : "", (cond) ? (str) : (str_else), (cond) ? (suffix) : "" #define NM_PRINT_FMT_QUOTE_STRING(arg) NM_PRINT_FMT_QUOTED((arg), "\"", (arg), "\"", "(null)") #define NM_PRINT_FMT_QUOTE_REF_STRING(arg) \ NM_PRINT_FMT_QUOTED((arg), "\"", (arg)->str, "\"", "(null)") /*****************************************************************************/ /* redefine assertions to use g_assert*() */ #undef _nm_assert_fail #define _nm_assert_fail(msg) \ G_STMT_START \ { \ g_assertion_message_expr(G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, "" msg ""); \ _nm_unreachable_code(); \ } \ G_STMT_END #undef _NM_ASSERT_FAIL_ENABLED #ifndef G_DISABLE_ASSERT #define _NM_ASSERT_FAIL_ENABLED 1 #else #define _NM_ASSERT_FAIL_ENABLED 0 #endif /* Usage: * * if (NM_MORE_ASSERT_ONCE (5)) { extra_check (); } * * This will only run the check once, and only if NM_MORE_ASSERTS is >= than * more_assert_level. */ #define NM_MORE_ASSERT_ONCE(more_assert_level) \ ((NM_MORE_ASSERTS >= (more_assert_level)) && ({ \ static volatile int _assert_once = 0; \ \ G_STATIC_ASSERT_EXPR((more_assert_level) > 0); \ \ G_UNLIKELY(_assert_once == 0 && g_atomic_int_compare_and_exchange(&_assert_once, 0, 1)); \ })) /*****************************************************************************/ #define NM_GOBJECT_PROPERTIES_DEFINE_BASE_FULL(suffix, ...) \ typedef enum { \ PROP_0##suffix, \ __VA_ARGS__ _PROPERTY_ENUMS_LAST##suffix, \ } _PropertyEnums##suffix; \ static GParamSpec *obj_properties##suffix[_PROPERTY_ENUMS_LAST##suffix] = { \ NULL, \ } #define NM_GOBJECT_PROPERTIES_DEFINE_NOTIFY(suffix, obj_type) \ static inline void _nm_gobject_notify_together_full_v##suffix( \ obj_type *obj, \ const _PropertyEnums##suffix *props, \ guint n) \ { \ GObject *const gobj = (GObject *) obj; \ GParamSpec *pspec_first = NULL; \ gboolean frozen = FALSE; \ \ nm_assert(G_IS_OBJECT(obj)); \ nm_assert(n > 0); \ \ while (n-- > 0) { \ const _PropertyEnums##suffix prop = *props++; \ GParamSpec *pspec; \ \ if (prop == PROP_0##suffix) \ continue; \ \ nm_assert((gsize) prop < G_N_ELEMENTS(obj_properties##suffix)); \ pspec = obj_properties##suffix[prop]; \ nm_assert(pspec); \ \ if (!frozen) { \ if (!pspec_first) { \ pspec_first = pspec; \ continue; \ } \ frozen = TRUE; \ g_object_freeze_notify(gobj); \ g_object_notify_by_pspec(gobj, pspec_first); \ } \ g_object_notify_by_pspec(gobj, pspec); \ } \ \ if (frozen) \ g_object_thaw_notify(gobj); \ else if (pspec_first) \ g_object_notify_by_pspec(gobj, pspec_first); \ } \ \ _nm_unused static inline void _notify##suffix(obj_type *obj, _PropertyEnums##suffix prop) \ { \ _nm_gobject_notify_together_full_v##suffix(obj, &prop, 1); \ } \ _NM_DUMMY_STRUCT_FOR_TRAILING_SEMICOLON #define NM_GOBJECT_PROPERTIES_DEFINE_BASE(...) \ NM_GOBJECT_PROPERTIES_DEFINE_BASE_FULL(, __VA_ARGS__); #define NM_GOBJECT_PROPERTIES_DEFINE_FULL(suffix, obj_type, ...) \ NM_GOBJECT_PROPERTIES_DEFINE_BASE_FULL(suffix, __VA_ARGS__); \ NM_GOBJECT_PROPERTIES_DEFINE_NOTIFY(suffix, obj_type) #define NM_GOBJECT_PROPERTIES_DEFINE(obj_type, ...) \ NM_GOBJECT_PROPERTIES_DEFINE_FULL(, obj_type, __VA_ARGS__) /* invokes _notify() for all arguments (of type _PropertyEnums). Note, that if * there are more than one prop arguments, this will involve a freeze/thaw * of GObject property notifications. */ #define nm_gobject_notify_together_full(suffix, obj, ...) \ G_STMT_START \ { \ const _PropertyEnums##suffix _props[] = {__VA_ARGS__}; \ \ G_STATIC_ASSERT(G_N_ELEMENTS(_props) == NM_NARG(__VA_ARGS__)); \ \ _nm_gobject_notify_together_full_v##suffix(obj, _props, G_N_ELEMENTS(_props)); \ } \ G_STMT_END #define nm_gobject_notify_together(obj, ...) nm_gobject_notify_together_full(, obj, __VA_ARGS__) /*****************************************************************************/ #define _NM_GET_PRIVATE(self, type, is_check, ...) \ (&(NM_GOBJECT_CAST_NON_NULL(type, (self), is_check, ##__VA_ARGS__)->_priv)) #if _NM_CC_SUPPORT_AUTO_TYPE #define _NM_GET_PRIVATE_PTR(self, type, is_check, ...) \ ({ \ _nm_auto_type _self_get_private = \ NM_GOBJECT_CAST_NON_NULL(type, (self), is_check, ##__VA_ARGS__); \ \ NM_PROPAGATE_CONST(_self_get_private, _self_get_private->_priv); \ }) #else #define _NM_GET_PRIVATE_PTR(self, type, is_check, ...) \ (NM_GOBJECT_CAST_NON_NULL(type, (self), is_check, ##__VA_ARGS__)->_priv) #endif /*****************************************************************************/ /* Unfortunately, G_TYPE_CHECK_INSTANCE_CAST() just does a direct cast, * which can trigger a "-Wcast-align" warning, especially when casting * a GObject pointer to the desired type. * * _NM_G_TYPE_CHECK_INSTANCE_CAST() avoids that warning. * * Since the entire point of G_TYPE_CHECK_INSTANCE_CAST_() is to do a * runtime check on the type (with conditional assertions via G_DISABLE_CAST_CHECKS), * we already assert that the gtype is right, and the alignment is also * expected to be right. * * See https://gitlab.gnome.org/GNOME/glib/-/merge_requests/3139 */ #define _NM_G_TYPE_CHECK_INSTANCE_CAST(instance, g_type, c_type) \ G_TYPE_CHECK_INSTANCE_CAST(((void *) (instance)), (g_type), c_type) /*****************************************************************************/ static inline gpointer nm_g_object_ref(gpointer obj) { /* g_object_ref() doesn't accept NULL. */ if (obj) g_object_ref(obj); return obj; } #define nm_g_object_ref(obj) ((typeof(obj)) nm_g_object_ref(obj)) static inline void nm_g_object_unref(gpointer obj) { /* g_object_unref() doesn't accept NULL. Usually, we workaround that * by using g_clear_object(), but sometimes that is not convenient * (for example as destroy function for a hash table that can contain * NULL values). */ if (obj) g_object_unref(obj); } /* Assigns GObject @obj to destination @pp, and takes an additional ref. * The previous value of @pp is unrefed. * * It makes sure to first increase the ref-count of @obj, and handles %NULL * @obj correctly. * */ #define nm_g_object_ref_set(pp, obj) \ ({ \ typeof(*(pp)) *const _pp = (pp); \ typeof(*_pp) const _obj = (obj); \ typeof(*_pp) _p; \ gboolean _changed = FALSE; \ \ nm_assert(!_pp || !*_pp || G_IS_OBJECT(*_pp)); \ nm_assert(!_obj || G_IS_OBJECT(_obj)); \ \ if (_pp && ((_p = *_pp) != _obj)) { \ nm_g_object_ref(_obj); \ *_pp = _obj; \ nm_g_object_unref(_p); \ _changed = TRUE; \ } \ _changed; \ }) #define nm_g_object_ref_set_take(pp, obj) \ ({ \ typeof(*(pp)) *const _pp = (pp); \ typeof(*_pp) const _obj = (obj); \ typeof(*_pp) _p; \ gboolean _changed = FALSE; \ \ nm_assert(!_pp || !*_pp || G_IS_OBJECT(*_pp)); \ nm_assert(!_obj || G_IS_OBJECT(_obj)); \ \ if (_pp && ((_p = *_pp) != _obj)) { \ *_pp = _obj; \ nm_g_object_unref(_p); \ _changed = TRUE; \ } else \ nm_g_object_unref(_obj); \ _changed; \ }) /* basically, replaces * g_clear_pointer (&location, g_free) * with * nm_clear_g_free (&location) * * Another advantage is that by using a macro and typeof(), it is more * typesafe and gives you for example a compiler warning when pp is a const * pointer or points to a const-pointer. */ #define nm_clear_g_free(pp) nm_clear_pointer(pp, g_free) /* Our nm_clear_pointer() is more typesafe than g_clear_pointer() and * should be preferred. * * For g_clear_object() that is not the case (because g_object_unref() * anyway takes a void pointer). So using g_clear_object() is fine. * * Still have a nm_clear_g_object() because that returns a boolean * indication whether anything was cleared. */ #define nm_clear_g_object(pp) nm_clear_pointer(pp, g_object_unref) /** * nm_clear_error: * @err: a pointer to pointer to a #GError. * * This is like g_clear_error(). The only difference is * that this is an inline function. */ static inline void nm_clear_error(GError **err) { if (err && *err) { g_error_free(*err); *err = NULL; } } static inline void nm_g_error_free(GError *err) { if (err) g_error_free(err); } /* Patch g_clear_error() to use nm_clear_error(), which is inlineable * and visible to the compiler. For example gs_free_error attribute only * frees the error after checking that it's not %NULL. So, in many cases * the compiler knows that gs_free_error has no effect and can optimize * the call away. By making g_clear_error() inlineable, we give the compiler * more chance to detect that the function actually has no effect. */ #define g_clear_error(ptr) nm_clear_error(ptr) static inline gboolean nm_clear_g_source(guint *id) { guint v; if (id && (v = *id)) { *id = 0; g_source_remove(v); return TRUE; } return FALSE; } static inline gboolean nm_clear_g_signal_handler(gpointer self, gulong *id) { gulong v; if (id && (v = *id)) { *id = 0; g_signal_handler_disconnect(self, v); return TRUE; } return FALSE; } static inline gboolean nm_clear_g_variant(GVariant **variant) { GVariant *v; if (variant && (v = *variant)) { *variant = NULL; g_variant_unref(v); return TRUE; } return FALSE; } static inline gboolean nm_clear_g_string(GString **ptr) { GString *s; if (ptr && (s = *ptr)) { *ptr = NULL; g_string_free(s, TRUE); }; return FALSE; } static inline gboolean nm_clear_g_cancellable(GCancellable **cancellable) { GCancellable *v; if (cancellable && (v = *cancellable)) { *cancellable = NULL; g_cancellable_cancel(v); g_object_unref(v); return TRUE; } return FALSE; } /* If @cancellable_id is not 0, clear it and call g_cancellable_disconnect(). * @cancellable may be %NULL, if there is nothing to disconnect. * * It's like nm_clear_g_signal_handler(), except that it uses g_cancellable_disconnect() * instead of g_signal_handler_disconnect(). * * Note the warning in glib documentation about dead-lock and what g_cancellable_disconnect() * actually does. */ static inline gboolean nm_clear_g_cancellable_disconnect(GCancellable *cancellable, gulong *cancellable_id) { gulong id; if (cancellable_id && (id = *cancellable_id) != 0) { *cancellable_id = 0; g_cancellable_disconnect(cancellable, id); return TRUE; } return FALSE; } /*****************************************************************************/ static inline const char * nm_dbus_path_not_empty(const char *str) { nm_assert(!str || str[0] == '/'); return !str || (str[0] == '/' && str[1] == '\0') ? NULL : str; } /*****************************************************************************/ /* GVariantType is basically a C string. But G_VARIANT_TYPE() is not suitable * to initialize a static variable (because it evaluates a function check that * the string is valid). Add an alternative macro that does the plain cast. * * Here you loose the assertion check that G_VARIANT_TYPE() to ensure the * string is valid. */ #define NM_G_VARIANT_TYPE(fmt) ((const GVariantType *) ("" fmt "")) static inline GVariant * nm_g_variant_ref(GVariant *v) { if (v) g_variant_ref(v); return v; } static inline GVariant * nm_g_variant_ref_sink(GVariant *v) { if (v) g_variant_ref_sink(v); return v; } static inline void nm_g_variant_unref(GVariant *v) { if (v) g_variant_unref(v); } static inline GVariant * nm_g_variant_take_ref(GVariant *v) { if (v) g_variant_take_ref(v); return v; } static inline gboolean nm_g_variant_equal(GVariant *a, GVariant *b) { return (a == b) || (a && b && g_variant_equal(a, b)); } /*****************************************************************************/ #define NM_DIV_ROUND_UP(x, y) \ ({ \ const typeof(x) _x = (x); \ const typeof(y) _y = (y); \ \ (_x / _y + !!(_x % _y)); \ }) /*****************************************************************************/ #define NM_UTILS_LOOKUP_DEFAULT(v) return (v) #define NM_UTILS_LOOKUP_DEFAULT_WARN(v) g_return_val_if_reached(v) #define NM_UTILS_LOOKUP_DEFAULT_NM_ASSERT(v) \ { \ nm_assert_not_reached(); \ return (v); \ } #define NM_UTILS_LOOKUP_ITEM(v, n) \ case v: \ return (n); #define NM_UTILS_LOOKUP_STR_ITEM(v, n) NM_UTILS_LOOKUP_ITEM(v, "" n "") #define NM_UTILS_LOOKUP_ITEM_IGNORE(v) \ case v: \ break; #define NM_UTILS_LOOKUP_ITEM_IGNORE_OTHER() \ default: \ break; #define NM_UTILS_LOOKUP_DEFINE(fcn_name, lookup_type, result_type, unknown_val, ...) \ result_type fcn_name(lookup_type val) \ { \ switch (val) { \ NM_VA_ARGS_JOIN(, __VA_ARGS__) \ }; \ { \ unknown_val; \ } \ } \ _NM_DUMMY_STRUCT_FOR_TRAILING_SEMICOLON #define NM_UTILS_LOOKUP_STR_DEFINE(fcn_name, lookup_type, unknown_val, ...) \ NM_UTILS_LOOKUP_DEFINE(fcn_name, lookup_type, const char *, unknown_val, __VA_ARGS__) /* Call the string-lookup-table function @fcn_name. If the function returns * %NULL, the numeric index is converted to string using a alloca() buffer. * Beware: this macro uses alloca(). */ #define NM_UTILS_LOOKUP_STR_A(fcn_name, idx) \ ({ \ typeof(idx) _idx = (idx); \ const char *_s; \ \ _s = fcn_name(_idx); \ if (!_s) { \ _s = g_alloca(30); \ \ g_snprintf((char *) _s, 30, "(%lld)", (long long) _idx); \ } \ _s; \ }) /*****************************************************************************/ /* check if @flags has exactly one flag (@check) set. You should call this * only with @check being a compile time constant and a power of two. */ #define NM_FLAGS_HAS(flags, check) \ (G_STATIC_ASSERT_EXPR((check) > 0 && ((check) & ((check) - 1)) == 0), \ NM_FLAGS_ANY((flags), (check))) #define NM_FLAGS_ANY(flags, check) (((flags) & (check)) != 0) #define NM_FLAGS_ALL(flags, check) \ ({ \ const typeof(check) _check = (check); \ \ (((flags) & (_check)) == (_check)); \ }) #define NM_FLAGS_SET(flags, val) \ ({ \ const typeof(flags) _flags = (flags); \ typeof(_flags) _val = (val); \ \ (void) (FALSE && (flags) == (val)); \ \ (_flags | _val); \ }) #define NM_FLAGS_UNSET(flags, val) \ ({ \ const typeof(flags) _flags = (flags); \ typeof(_flags) _val = (val); \ \ (void) (FALSE && (flags) == (val)); \ \ (_flags & (~_val)); \ }) #define NM_FLAGS_ASSIGN(flags, val, assign) \ ({ \ const typeof(flags) _flags = (flags); \ typeof(_flags) _val = (val); \ \ (void) (FALSE && (flags) == (val)); \ \ (assign) ? (_flags | _val) : (_flags & (~_val)); \ }) #define NM_FLAGS_ASSIGN_MASK(flags, mask, val) \ ({ \ const typeof(flags) _flags = (flags); \ typeof(_flags) _mask = (mask); \ typeof(_flags) _val = (val); \ \ (void) (FALSE && (flags) == (val)); \ (void) (FALSE && (flags) == (mask)); \ \ ((_flags & (~_mask)) | (_val & _mask)); \ }) /*****************************************************************************/ #if defined(__GNUC__) && (__GNUC__ >= 12) #define _NM_BACKPORT_SYMBOL_IMPL(version, \ return_type, \ orig_func, \ versioned_func, \ args_typed, \ args) \ return_type versioned_func args_typed; \ \ __attribute__((__symver__( \ G_STRINGIFY(orig_func) "@" G_STRINGIFY(version)))) return_type versioned_func args_typed \ { \ return orig_func args; \ } \ return_type orig_func args_typed; #else #define _NM_BACKPORT_SYMBOL_IMPL(version, \ return_type, \ orig_func, \ versioned_func, \ args_typed, \ args) \ return_type versioned_func args_typed; \ _nm_externally_visible return_type versioned_func args_typed \ { \ return orig_func args; \ } \ return_type orig_func args_typed; \ __asm__(".symver " G_STRINGIFY(versioned_func) ", " G_STRINGIFY(orig_func) "@" G_STRINGIFY( \ version)) #endif #define NM_BACKPORT_SYMBOL(version, return_type, func, args_typed, args) \ _NM_BACKPORT_SYMBOL_IMPL(version, return_type, func, _##func##_##version, args_typed, args) /*****************************************************************************/ /* mirrors g_ascii_isspace() and what we consider spaces in general. */ #define NM_ASCII_SPACES " \n\t\r\f" /* Like NM_ASCII_SPACES, but without "\f" (0x0c, Formfeed Page Break). This is * what for example systemd calls WHITESPACE and what it uses to tokenize the * kernel command line. */ #define NM_ASCII_WHITESPACES " \n\t\r" /* mirrors 's isspace() with C locale. It's like NM_ASCII_SPACES but * additionally also considers '\v' (vertical tab). */ #define NM_ASCII_SPACES_CTYPE NM_ASCII_SPACES "\v" /* mirrors kernel's isspace() from "include/linux/ctype.h", which treats as * space the common ASCII spaces, including '\v' (vertical tab), but also * '\240' (non-breaking space, NBSP in Latin-1). */ #define NM_ASCII_SPACES_KERNEL NM_ASCII_SPACES_CTYPE "\240" static inline gboolean nm_ascii_is_whitespace(char ch) { /* Checks whether @ch is in NM_ASCII_WHITESPACES. * Similar to g_ascii_isspace(), however this one does not accept '\f'. * This is the same as systemd's strchr(WHITESPACE, ch). */ return NM_IN_SET(ch, ' ', '\n', '\t', '\r'); } static inline gboolean nm_ascii_is_space_kernel(char ch) { /* Checks whether @ch is in NM_ASCII_SPACES_KERNEL. */ return NM_IN_SET(ch, ' ', '\n', '\t', '\r', '\f', '\v', '\240'); } #define NM_ASCII_NEWLINE "\n\r" static inline gboolean nm_ascii_is_newline(char ch) { /* This is the same as systemd's (!!strchr(NEWLINE, ch)). */ return NM_IN_SET(ch, '\n', '\t'); } static inline gboolean nm_ascii_is_regular_char(char ch) { /* Checks whether "ch" is "regular", which basically * means it's either a digit, a alpha, or some special * characters that are suitable for base64 encoding. * * The meaning of what "regular" means is not well defined, * but it's used to validate the keys for "ovs.external-ids" * dictionary. */ switch (ch) { case 'a' ... 'z': case 'A' ... 'Z': case '0' ... '9': case '-': case '_': case '+': case '/': case '=': case '.': return TRUE; default: return FALSE; } } #define nm_str_skip_leading_spaces(str) \ ({ \ typeof(*(str)) *_str_sls = (str); \ _nm_unused const char *const _str_type_check = _str_sls; \ \ if (_str_sls) { \ while (g_ascii_isspace(_str_sls[0])) \ _str_sls++; \ } \ _str_sls; \ }) static inline char * nm_strstrip(char *str) { /* g_strstrip doesn't like NULL. */ return str ? g_strstrip(str) : NULL; } static inline char * nm_strstrip_dup(const char *str) { gsize l; char *s; if (!str) return NULL; str = nm_str_skip_leading_spaces(str); l = strlen(str); while (l > 0 && g_ascii_isspace(str[l - 1])) l--; s = g_new(char, l + 1); memcpy(s, str, l); s[l] = '\0'; return s; } static inline const char * nm_strstrip_avoid_copy(const char *str, char **str_free) { gsize l; char *s; nm_assert(str_free && !*str_free); if (!str) return NULL; str = nm_str_skip_leading_spaces(str); l = strlen(str); if (l == 0 || !g_ascii_isspace(str[l - 1])) return str; while (l > 0 && g_ascii_isspace(str[l - 1])) l--; s = g_new(char, l + 1); memcpy(s, str, l); s[l] = '\0'; *str_free = s; return s; } #define nm_strstrip_avoid_copy_a(alloca_maxlen, str, out_str_free) \ ({ \ const char *_str_ssac = (str); \ char **_out_str_free_ssac = (out_str_free); \ \ G_STATIC_ASSERT_EXPR((alloca_maxlen) > 0); \ \ nm_assert(_out_str_free_ssac || ((alloca_maxlen) > (str ? strlen(str) : 0u))); \ nm_assert(!_out_str_free_ssac || !*_out_str_free_ssac); \ \ if (_str_ssac) { \ _str_ssac = nm_str_skip_leading_spaces(_str_ssac); \ if (_str_ssac[0] != '\0') { \ gsize _l = strlen(_str_ssac); \ \ if (g_ascii_isspace(_str_ssac[--_l])) { \ while (_l > 0 && g_ascii_isspace(_str_ssac[_l - 1])) { \ _l--; \ } \ _str_ssac = nm_strndup_a((alloca_maxlen), _str_ssac, _l, _out_str_free_ssac); \ } \ } \ } \ \ _str_ssac; \ }) static inline gboolean nm_str_is_stripped(const char *str) { if (str && str[0]) { if (g_ascii_isspace(str[0]) || g_ascii_isspace(str[strlen(str) - 1])) return FALSE; } return TRUE; } /* g_ptr_array_sort()'s compare function takes pointers to the * value. Thus, you cannot use strcmp directly. You can use * nm_strcmp_p(). * * Like strcmp(), this function is not forgiving to accept %NULL. */ static inline int nm_strcmp_p(gconstpointer a, gconstpointer b) { const char *s1 = *((const char **) a); const char *s2 = *((const char **) b); return strcmp(s1, s2); } /*****************************************************************************/ static inline int _NM_IN_STRSET_EVAL_op_streq_ascii_case(const char *x1, const char *x) { return x && g_ascii_strcasecmp(x1, x) == 0; } #define _NM_IN_STRSET_EVAL_OP_STREQ_ASCII_CASE(x, idx, op_arg) \ _NM_IN_STRSET_EVAL_op_streq_ascii_case(_x1, x) #define NM_IN_STRSET_ASCII_CASE(x1, ...) \ _NM_IN_STRSET_EVAL(||, _NM_IN_STRSET_EVAL_OP_STREQ_ASCII_CASE, x1, __VA_ARGS__) #define NM_STR_HAS_SUFFIX_ASCII_CASE(str, suffix) \ ({ \ const char *const _str_has_suffix = (str); \ size_t _l; \ \ nm_assert(strlen(suffix) == NM_STRLEN(suffix)); \ \ (_str_has_suffix && ((_l = strlen(_str_has_suffix)) >= NM_STRLEN(suffix)) \ && (g_ascii_strcasecmp(&_str_has_suffix[_l - NM_STRLEN(suffix)], "" suffix "") == 0)); \ }) #define NM_STR_HAS_SUFFIX_ASCII_CASE_WITH_MORE(str, suffix) \ ({ \ const char *const _str_has_suffix = (str); \ size_t _l; \ \ nm_assert(strlen(suffix) == NM_STRLEN(suffix)); \ \ (_str_has_suffix && ((_l = strlen(_str_has_suffix)) > NM_STRLEN(suffix)) \ && (g_ascii_strcasecmp(&_str_has_suffix[_l - NM_STRLEN(suffix)], "" suffix "") == 0)); \ }) /*****************************************************************************/ #define nm_g_slice_free(ptr) g_slice_free(typeof(*(ptr)), ptr) /*****************************************************************************/ static inline int nm_memcmp_n(gconstpointer p1, gsize len1, gconstpointer p2, gsize len2, gsize element_size) { nm_assert(element_size > 0); NM_CMP_DIRECT(len1, len2); if (len1 > 0) NM_CMP_DIRECT_MEMCMP(p1, p2, len1 * element_size); return 0; } static inline int nm_memeq_n(gconstpointer p1, gsize len1, gconstpointer p2, gsize len2, gsize element_size) { return nm_memcmp_n(p1, len1, p2, len2, element_size) == 0; } /*****************************************************************************/ /* like g_memdup(). The difference is that the @size argument is of type * gsize, while g_memdup() has type guint. Since, the size of container types * like GArray is guint as well, this means trying to g_memdup() an * array, * g_memdup (array->data, array->len * sizeof (ElementType)) * will lead to integer overflow, if there are more than G_MAXUINT/sizeof(ElementType) * bytes. That seems unnecessarily dangerous to me. * nm_memdup() avoids that, because its size argument is always large enough * to contain all data that a GArray can hold. * * Another minor difference to g_memdup() is that the glib version also * returns %NULL if @data is %NULL. E.g. g_memdup(NULL, 1) * gives %NULL, but nm_memdup(NULL, 1) crashes. I think that * is desirable, because @size MUST be correct at all times. @size * may be zero, but one must not claim to have non-zero bytes when * passing a %NULL @data pointer. */ static inline gpointer nm_memdup(gconstpointer data, gsize size) { gpointer p; if (size == 0) return NULL; p = g_malloc(size); memcpy(p, data, size); return p; } static inline gpointer nm_memdup_nul(gconstpointer data, gsize size) { gpointer p; /* Like systemd's memdup_suffix0() and kernel's kmemdup_nul(). * * This: * - never returns NULL * - always has one NUL byte after the @size data. Thus, * the actually allocated buffer is size+1. * * This is like nm_memdup() except: * - never returns NULL * - always returns one NUL character appended to the data. * * This is like g_strndup(), except * - never returns NULL. * - g_strndup() treats the src pointer as a NUL terminated string, * so if src is shorter than size, the rest is filled with padding. * Essentially, it uses strncpy() to copy the input which does the * truncation. If @data contains no NUL byte in teh first @size bytes, * it behaves the same as g_strndup(). */ p = g_malloc(size + 1u); nm_memcpy(p, data, size); ((char *) p)[size] = '\0'; return p; } #define nm_malloc_maybe_a(alloca_maxlen, bytes, to_free) \ ({ \ const gsize _bytes = (bytes); \ typeof(to_free) _to_free = (to_free); \ typeof(*_to_free) _ptr; \ \ G_STATIC_ASSERT_EXPR((alloca_maxlen) <= 500u); \ G_STATIC_ASSERT_EXPR((alloca_maxlen) > 0u); \ nm_assert(_to_free && !*_to_free); \ \ if (G_LIKELY(_bytes <= (alloca_maxlen))) { \ _ptr = _bytes > 0u ? g_alloca(_bytes) : NULL; \ } else { \ _ptr = g_malloc(_bytes); \ *_to_free = _ptr; \ }; \ \ _ptr; \ }) #define nm_malloc0_maybe_a(alloca_maxlen, bytes, to_free) \ ({ \ const gsize _bytes = (bytes); \ typeof(to_free) _to_free = (to_free); \ typeof(*_to_free) _ptr; \ \ G_STATIC_ASSERT_EXPR((alloca_maxlen) <= 500u); \ G_STATIC_ASSERT_EXPR((alloca_maxlen) > 0u); \ nm_assert(_to_free && !*_to_free); \ \ if (G_LIKELY(_bytes <= (alloca_maxlen))) { \ if (_bytes > 0u) { \ _ptr = g_alloca(_bytes); \ memset(_ptr, 0, _bytes); \ } else \ _ptr = NULL; \ } else { \ _ptr = g_malloc0(_bytes); \ *_to_free = _ptr; \ }; \ \ _ptr; \ }) #define nm_memdup_maybe_a(alloca_maxlen, data, size, to_free) \ ({ \ const gsize _size = (size); \ typeof(to_free) _to_free_md = (to_free); \ typeof(*_to_free_md) _ptr_md = NULL; \ \ nm_assert(_to_free_md && !*_to_free_md); \ \ if (_size > 0u) { \ _ptr_md = nm_malloc_maybe_a((alloca_maxlen), _size, _to_free_md); \ memcpy(_ptr_md, (data), _size); \ } \ \ _ptr_md; \ }) static inline char * _nm_strndup_a_step(char *s, const char *str, gsize len) { NM_PRAGMA_WARNING_DISABLE("-Wstringop-truncation"); NM_PRAGMA_WARNING_DISABLE("-Wstringop-overflow"); if (len > 0) strncpy(s, str, len); s[len] = '\0'; return s; NM_PRAGMA_WARNING_REENABLE; NM_PRAGMA_WARNING_REENABLE; } /* Similar to g_strndup(), however, if the string (including the terminating * NUL char) fits into alloca_maxlen, this will alloca() the memory. * * It's a mix of strndup() and strndupa(), but deciding based on @alloca_maxlen * which one to use. * * In case malloc() is necessary, @out_str_free will be set (this string * must be freed afterwards). It is permissible to pass %NULL as @out_str_free, * if you ensure that len < alloca_maxlen. * * Note that just like g_strndup(), this always returns a buffer with @len + 1 * bytes, even if strlen(@str) is shorter than that (NUL terminated early). We fill * the buffer with strncpy(), which means, that @str is copied up to the first * NUL character and then filled with NUL characters. */ #define nm_strndup_a(alloca_maxlen, str, len, out_str_free) \ ({ \ const gsize _alloca_maxlen_snd = (alloca_maxlen); \ const char *const _str_snd = (str); \ const gsize _len_snd = (len); \ char **const _out_str_free_snd = (out_str_free); \ char *_s_snd; \ \ G_STATIC_ASSERT_EXPR((alloca_maxlen) <= 300); \ \ if (_out_str_free_snd && _len_snd >= _alloca_maxlen_snd) { \ _s_snd = g_malloc(_len_snd + 1); \ *_out_str_free_snd = _s_snd; \ } else { \ g_assert(_len_snd < _alloca_maxlen_snd); \ _s_snd = g_alloca(_len_snd + 1); \ } \ _nm_strndup_a_step(_s_snd, _str_snd, _len_snd); \ }) #define nm_strdup_maybe_a(alloca_maxlen, str, out_str_free) \ ({ \ const char *const _str_snd = (str); \ \ (char *) nm_memdup_maybe_a(alloca_maxlen, \ _str_snd, \ _str_snd ? strlen(_str_snd) + 1u : 0u, \ out_str_free); \ }) /*****************************************************************************/ /* generic macro to convert an int to a (heap allocated) string. * * Usually, an inline function nm_strdup_int64() would be enough. However, * that cannot be used for guint64. So, we would also need nm_strdup_uint64(). * This causes subtle error potential, because the caller needs to ensure to * use the right one (and compiler isn't going to help as it silently casts). * * Instead, this generic macro is supposed to handle all integers correctly. */ #if _NM_CC_SUPPORT_GENERIC #define nm_strdup_int(val) \ _Generic((val), \ char: g_strdup_printf("%d", (int) (val)), \ \ signed char: g_strdup_printf("%d", (signed) (val)), \ signed short: g_strdup_printf("%d", (signed) (val)), \ signed: g_strdup_printf("%d", (signed) (val)), \ signed long: g_strdup_printf("%ld", (signed long) (val)), \ signed long long: g_strdup_printf("%lld", (signed long long) (val)), \ \ unsigned char: g_strdup_printf("%u", (unsigned) (val)), \ unsigned short: g_strdup_printf("%u", (unsigned) (val)), \ unsigned: g_strdup_printf("%u", (unsigned) (val)), \ unsigned long: g_strdup_printf("%lu", (unsigned long) (val)), \ unsigned long long: g_strdup_printf("%llu", (unsigned long long) (val))) #else #define nm_strdup_int(val) \ ((sizeof(val) == sizeof(guint64) && ((typeof(val)) -1) > 0) \ ? g_strdup_printf("%" G_GUINT64_FORMAT, (guint64) (val)) \ : g_strdup_printf("%" G_GINT64_FORMAT, (gint64) (val))) #endif /*****************************************************************************/ static inline guint nm_encode_version(guint major, guint minor, guint micro) { /* analog to the preprocessor macro NM_ENCODE_VERSION(). */ return (major << 16) | (minor << 8) | micro; } static inline void nm_decode_version(guint version, guint *major, guint *minor, guint *micro) { *major = (version & 0xFFFF0000u) >> 16; *minor = (version & 0x0000FF00u) >> 8; *micro = (version & 0x000000FFu); } /*****************************************************************************/ /* taken from systemd's DECIMAL_STR_MAX() * * Returns the number of chars needed to format variables of the * specified type as a decimal string. Adds in extra space for a * negative '-' prefix (hence works correctly on signed * types). Includes space for the trailing NUL. */ #define NM_DECIMAL_STR_MAX(type) \ (2 \ + (sizeof(type) <= 1 ? 3 \ : sizeof(type) <= 2 ? 5 \ : sizeof(type) <= 4 ? 10 \ : sizeof(type) <= 8 ? 20 \ : sizeof(int[-2 * (sizeof(type) > 8)]))) /*****************************************************************************/ /* if @str is NULL, return "(null)". Otherwise, allocate a buffer using * alloca() of and fill it with @str. @str will be quoted with double quote. * If @str is longer then @trunc_at, the string is truncated and the closing * quote is instead '^' to indicate truncation. * * Thus, the maximum stack allocated buffer will be @trunc_at+3. The maximum * buffer size must be a constant and not larger than 300. */ #define nm_strquote_a(trunc_at, str) \ ({ \ const char *const _str = (str); \ \ (_str ? ({ \ const gsize _trunc_at = (trunc_at); \ const gsize _strlen_trunc = NM_MIN(strlen(_str), _trunc_at); \ char *_buf; \ \ G_STATIC_ASSERT_EXPR((trunc_at) <= 300); \ \ _buf = g_alloca(_strlen_trunc + 3); \ _buf[0] = '"'; \ memcpy(&_buf[1], _str, _strlen_trunc); \ _buf[_strlen_trunc + 1] = _str[_strlen_trunc] ? '^' : '"'; \ _buf[_strlen_trunc + 2] = '\0'; \ _buf; \ }) \ : "(null)"); \ }) #define nm_sprintf_buf(buf, format, ...) \ ({ \ char *_buf = (buf); \ int _buf_len; \ \ /* some static assert trying to ensure that the buffer is statically allocated. * It disallows a buffer size of sizeof(gpointer) to catch that. */ \ G_STATIC_ASSERT(G_N_ELEMENTS(buf) == sizeof(buf) && sizeof(buf) != sizeof(char *)); \ _buf_len = g_snprintf(_buf, sizeof(buf), "" format "", ##__VA_ARGS__); \ nm_assert(_buf_len < sizeof(buf)); \ _buf; \ }) #define nm_vsprintf_buf_or_alloc(format, va_last_arg, sbuf_stack, out_sbuf_heap, out_len) \ ({ \ const char *const _format = (format); \ char *const _sbuf_stack = (sbuf_stack); \ char **const _out_sbuf_heap = (out_sbuf_heap); \ gsize *const _out_len = (out_len); \ const char *_msg; \ va_list _va_args; \ int _l; \ \ G_STATIC_ASSERT_EXPR(G_N_ELEMENTS(sbuf_stack) > sizeof(_sbuf_stack)); \ \ va_start(_va_args, va_last_arg); \ _l = g_vsnprintf(_sbuf_stack, sizeof(sbuf_stack), _format, _va_args); \ va_end(_va_args); \ \ nm_assert(_l >= 0 && _l < G_MAXINT); \ \ if ((gsize) _l >= sizeof(sbuf_stack)) { \ const gsize _l2 = ((gsize) _l) + 1u; \ char *_sbuf_heap; \ \ /* Don't use g_strdup_vprintf() here either, because that also needs * to first determine the length (which is commonly does by printing * to a stack allocated buffer of size 1. We already know the required * size. */ \ \ _sbuf_heap = g_malloc(_l2); \ \ va_start(_va_args, va_last_arg); \ _l = g_vsnprintf(_sbuf_heap, _l2, _format, _va_args); \ va_end(_va_args); \ \ nm_assert(_l >= 0 && ((gsize) _l) == _l2 - 1u); \ \ _msg = _sbuf_heap; \ *_out_sbuf_heap = _sbuf_heap; \ } else { \ _msg = _sbuf_stack; \ *_out_sbuf_heap = NULL; \ } \ \ nm_assert(strlen(_msg) == (gsize) _l); \ NM_SET_OUT(_out_len, (gsize) _l); \ \ _msg; \ }) /* it is "unsafe" because @bufsize must not be a constant expression and * there is no check at compiletime. Regardless of that, the buffer size * must not be larger than 300 bytes, as this gets stack allocated. */ #define nm_sprintf_buf_unsafe_a(bufsize, format, ...) \ ({ \ char *_buf; \ int _buf_len; \ typeof(bufsize) _bufsize = (bufsize); \ \ nm_assert(_bufsize <= 300); \ \ _buf = g_alloca(_bufsize); \ _buf_len = g_snprintf(_buf, _bufsize, "" format "", ##__VA_ARGS__); \ nm_assert(_buf_len >= 0 && _buf_len < _bufsize); \ _buf; \ }) #define nm_sprintf_bufa(bufsize, format, ...) \ ({ \ G_STATIC_ASSERT_EXPR((bufsize) <= 300); \ nm_sprintf_buf_unsafe_a((bufsize), format, ##__VA_ARGS__); \ }) /* aims to alloca() a buffer and fill it with printf(format, name). * Note that format must not contain any format specifier except * "%s". * If the resulting string would be too large for stack allocation, * it allocates a buffer with g_malloc() and assigns it to *p_val_to_free. */ #define nm_construct_name_a(format, name, p_val_to_free) \ ({ \ const char *const _name = (name); \ char **const _p_val_to_free = (p_val_to_free); \ const gsize _name_len = strlen(_name); \ char *_buf2; \ \ nm_assert(_p_val_to_free && !*_p_val_to_free); \ if (NM_STRLEN(format) <= 290 && _name_len < (gsize) (290 - NM_STRLEN(format))) \ _buf2 = nm_sprintf_buf_unsafe_a(NM_STRLEN(format) + _name_len, format, _name); \ else { \ _buf2 = g_strdup_printf(format, _name); \ *_p_val_to_free = _buf2; \ } \ (const char *) _buf2; \ }) /*****************************************************************************/ #define nm_va_args_one_ptr(last) \ ({ \ va_list _va_args; \ gpointer _ptr; \ \ va_start(_va_args, (last)); \ _ptr = va_arg(_va_args, gpointer); \ va_end(_va_args); \ _ptr; \ }) /*****************************************************************************/ #ifdef _G_BOOLEAN_EXPR /* g_assert() uses G_LIKELY(), which in turn uses _G_BOOLEAN_EXPR(). * As glib's implementation uses a local variable _g_boolean_var_, * we cannot do * g_assert (some_macro ()); * where some_macro() itself expands to ({g_assert(); ...}). * In other words, you cannot have a g_assert() inside a g_assert() * without getting a -Werror=shadow failure. * * Workaround that by re-defining _G_BOOLEAN_EXPR() **/ #undef _G_BOOLEAN_EXPR #define _G_BOOLEAN_EXPR(expr) NM_BOOLEAN_EXPR(expr) #endif /*****************************************************************************/ #define NM_PID_T_INVAL ((pid_t) - 1) /*****************************************************************************/ NM_AUTO_DEFINE_FCN_VOID0(GMutex *, _nm_auto_unlock_g_mutex, g_mutex_unlock); #define nm_auto_unlock_g_mutex nm_auto(_nm_auto_unlock_g_mutex) #define _NM_G_MUTEX_LOCKED(lock, uniq) \ _nm_unused nm_auto_unlock_g_mutex GMutex *NM_UNIQ_T(nm_lock, uniq) = ({ \ GMutex *const _lock = (lock); \ \ g_mutex_lock(_lock); \ _lock; \ }) #define NM_G_MUTEX_LOCKED(lock) _NM_G_MUTEX_LOCKED(lock, NM_UNIQ) /*****************************************************************************/ static inline GObject * nm_g_object_freeze_notify(gpointer obj) { if (obj) g_object_freeze_notify(obj); return obj; } static inline void nm_g_object_thaw_notify_clear(GObject **p_obj) { nm_clear_pointer(p_obj, g_object_thaw_notify); } #define nm_auto_g_object_thaw_notify nm_auto(nm_g_object_thaw_notify_clear) /*****************************************************************************/ #endif /* __NM_MACROS_INTERNAL_H__ */