/* * Copyright (c) 2023-2024 Apple Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef MDNS_GENERAL_H #define MDNS_GENERAL_H /*! * @brief * Evaluates to non-zero if compiling for a particular platform. * * @param PLATFORM_NAME * The name of the platform, e.g., APPLE. */ #define MDNS_PLATFORM(PLATFORM_NAME) MDNS_PLATFORM_PRIVATE_DEFINITION_ ## PLATFORM_NAME () /*! * @brief * Evaluates to non-zero if compiling for Apple OSes. * * @discussion * `__APPLE__` is defined when compiling for Apple, see * <https://developer.apple.com/library/archive/documentation/Porting/Conceptual/PortingUnix/compiling/compiling.html>. */ #if defined(__APPLE__) && __APPLE__ #define MDNS_PLATFORM_PRIVATE_DEFINITION_APPLE() 1 #else #define MDNS_PLATFORM_PRIVATE_DEFINITION_APPLE() 0 #endif #if MDNS_PLATFORM(APPLE) #include <TargetConditionals.h> #endif /*! * @brief * Evaluates to non-zero if compiling for a particular OS. * * @param OS_NAME * The name of the OS, e.g., macOS, iOS, etc. */ #define MDNS_OS(OS_NAME) MDNS_OS_PRIVATE_DEFINITION_ ## OS_NAME () /*! * @brief * Evaluates to non-zero if compiling for macOS. * * @discussion * Use `MDNS_OS(macOS)` instead of using this macro directly. */ #if defined(TARGET_OS_OSX) && TARGET_OS_OSX #define MDNS_OS_PRIVATE_DEFINITION_macOS() 1 #else #define MDNS_OS_PRIVATE_DEFINITION_macOS() 0 #endif /*! * @brief * Evaluates to non-zero if compiling for iOS. * * @discussion * Use `MDNS_OS(iOS)` instead of using this macro directly. */ #if defined(TARGET_OS_IOS) && TARGET_OS_IOS #define MDNS_OS_PRIVATE_DEFINITION_iOS() 1 #else #define MDNS_OS_PRIVATE_DEFINITION_iOS() 0 #endif /*! * @brief * Evaluates to non-zero if compiling for watchOS. * * @discussion * Use `MDNS_OS(watchOS)` instead of using this macro directly. */ #if defined(TARGET_OS_WATCH) && TARGET_OS_WATCH #define MDNS_OS_PRIVATE_DEFINITION_watchOS() 1 #else #define MDNS_OS_PRIVATE_DEFINITION_watchOS() 0 #endif /*! * @brief * Evaluates to non-zero if compiling for tvOS. * * @discussion * Use `MDNS_OS(tvOS)` instead of using this macro directly. */ #if defined(TARGET_OS_TV) && TARGET_OS_TV #define MDNS_OS_PRIVATE_DEFINITION_tvOS() 1 #else #define MDNS_OS_PRIVATE_DEFINITION_tvOS() 0 #endif // Time conversion constants #define MDNS_NANOSECONDS_PER_SECOND 1000000000 #define MDNS_MILLISECONDS_PER_SECOND 1000 #define MDNS_MILLISECONDS_PER_MINUTE (MDNS_MILLISECONDS_PER_SECOND * MDNS_SECONDS_PER_MINUTE) #define MDNS_MILLISECONDS_PER_HOUR (MDNS_MILLISECONDS_PER_SECOND * MDNS_SECONDS_PER_HOUR) #define MDNS_SECONDS_PER_MINUTE 60 #define MDNS_SECONDS_PER_HOUR (MDNS_SECONDS_PER_MINUTE * MDNS_MINUTES_PER_HOUR) #define MDNS_SECONDS_PER_DAY (MDNS_SECONDS_PER_HOUR * MDNS_HOUR_PER_DAY) #define MDNS_MINUTES_PER_HOUR 60 #define MDNS_HOUR_PER_DAY 24 // Clang's __has_*() builtin macros are defined as zero if not defined. #if !defined(__has_attribute) #define __has_attribute(X) 0 #endif #if !defined(__has_extension) #define __has_extension(X) 0 #endif #if !defined(__has_feature) #define __has_feature(X) 0 #endif /*! * @brief * Evaluates to non-zero if the compiler is Clang. * * @discussion * __clang__ is defined when compiling with Clang, see * <https://clang.llvm.org/docs/LanguageExtensions.html#builtin-macros>. */ #if defined(__clang__) #define MDNS_COMPILER_IS_CLANG() 1 #else #define MDNS_COMPILER_IS_CLANG() 0 #endif /*! * @brief * Evaluates to non-zero if the compiler is Clang and its version is at least a specified version. * * @param MAJOR * The specified version's major number. * * @param MINOR * The specified version's minor number. * * @param PATCH_LEVEL * The specified version's patch level. * * @discussion * Clang version numbers are of the form "<major number>.<minor number>.<patch level>". See * <https://clang.llvm.org/docs/LanguageExtensions.html#builtin-macros> */ #if MDNS_COMPILER_IS_CLANG() #define MDNS_CLANG_VERSION_IS_AT_LEAST(MAJOR, MINOR, PATCH_LEVEL) ( \ (__clang_major__ > (MAJOR)) || ( \ (__clang_major__ == (MAJOR)) && ( \ (__clang_minor__ > (MINOR)) || ( \ (__clang_minor__ == (MINOR)) && (__clang_patchlevel__ >= (PATCH_LEVEL)) \ ) \ ) \ ) \ ) #else #define MDNS_CLANG_VERSION_IS_AT_LEAST(MAJOR, MINOR, PATCH_LEVEL) 0 #endif /*! * @brief * Stringizes the argument and passes it to the _Pragma() operator, which takes a string literal argument. * * @param ARG * The argument. * * @discussion * Useful for escaping double quotes. For example, * * MDNS_PRAGMA_WITH_STRINGIZED_ARGUMENT(clang diagnostic ignored "-Wpadded") * * turns into * * _Pragma("clang diagnostic ignored \"-Wpadded\"") * * See <https://gcc.gnu.org/onlinedocs/cpp/Pragmas.html>. */ #define MDNS_PRAGMA_WITH_STRINGIZED_ARGUMENT(ARG) _Pragma(#ARG) /*! * @brief * For Clang, starts ignoring the specified warning diagnostic flag. * * @param WARNING * The warning diagnostic flag. * * @discussion * Use MDNS_CLANG_IGNORE_WARNING_END() to undo the effect of this macro. */ #if MDNS_COMPILER_IS_CLANG() #define MDNS_CLANG_IGNORE_WARNING_BEGIN(WARNING) \ _Pragma("clang diagnostic push") \ MDNS_PRAGMA_WITH_STRINGIZED_ARGUMENT(clang diagnostic ignored #WARNING) #else #define MDNS_CLANG_IGNORE_WARNING_BEGIN(WARNING) #endif /*! * @brief * Use to undo the effect of a previous MDNS_CLANG_IGNORE_WARNING_BEGIN(). */ #if MDNS_COMPILER_IS_CLANG() #define MDNS_CLANG_IGNORE_WARNING_END() _Pragma("clang diagnostic pop") #else #define MDNS_CLANG_IGNORE_WARNING_END() #endif /*! * @brief * An alternative version of MDNS_CLANG_IGNORE_WARNING_BEGIN() that looks nicer when used among statements. * * @discussion * This version looks nicer when used among C statements. Here's an example: * * mdns_clang_ignore_warning_begin(-Wformat-nonliteral); * const int n = vsnprintf(dst, len, fmt, args); * mdns_clang_ignore_warning_end(); */ #define mdns_clang_ignore_warning_begin(WARNING) \ MDNS_CLANG_IGNORE_WARNING_BEGIN(WARNING) \ do {} while (0) /*! * @brief * An alternative version of MDNS_CLANG_IGNORE_WARNING_END() that looks nicer when used among statements. * * @discussion * This version looks nicer when used among C statements. Here's an example: * * mdns_clang_ignore_warning_begin(-Wformat-nonliteral); * const int n = vsnprintf(dst, len, fmt, args); * mdns_clang_ignore_warning_end(); */ #define mdns_clang_ignore_warning_end() \ MDNS_CLANG_IGNORE_WARNING_END() \ do {} while (0) /*! * @brief * For Clang, starts ignoring the -Wunaligned-access warning diagnostic flag. * * @discussion * The -Wunaligned-access is new in clang version 14.0.3. This macro allow us to conditionally ignore * -Wunaligned-access with Clang 14.0.3 or later. This avoids -Wunknown-warning-option warnings with * earlier Clang versions, which don't recognize -Wunaligned-access. */ #if MDNS_CLANG_VERSION_IS_AT_LEAST(14, 0, 3) #define MDNS_CLANG_IGNORE_UNALIGNED_ACCESS_WARNING_BEGIN() MDNS_CLANG_IGNORE_WARNING_BEGIN(-Wunaligned-access) #else #define MDNS_CLANG_IGNORE_UNALIGNED_ACCESS_WARNING_BEGIN() #endif /*! * @brief * Undoes the effect of a previous MDNS_CLANG_IGNORE_UNALIGNED_ACCESS_WARNING_BEGIN(). */ #if MDNS_CLANG_VERSION_IS_AT_LEAST(14, 0, 3) #define MDNS_CLANG_IGNORE_UNALIGNED_ACCESS_WARNING_END() MDNS_CLANG_IGNORE_WARNING_END() #else #define MDNS_CLANG_IGNORE_UNALIGNED_ACCESS_WARNING_END() #endif /*! * @brief * For Clang, starts ignoring the -Wincompatible-function-pointer-types-strict warning diagnostic flag. * * @discussion * -Wincompatible-function-pointer-types-strict is like -Wincompatible-function-pointer-types, but is more * strict in that it warns about function pointer types that are not identical but are still compatible. * * The -Wincompatible-function-pointer-types-strict is new in clang version 16.0.0 (see * https://releases.llvm.org/16.0.0/tools/clang/docs/ReleaseNotes.html). This macro allow us to * conditionally ignore -Wincompatible-function-pointer-types-strict with Clang 16.0.0 or later. This * avoids -Wunknown-warning-option warnings with earlier Clang versions, which don't recognize * -Wincompatible-function-pointer-types-strict. */ #if MDNS_CLANG_VERSION_IS_AT_LEAST(16, 0, 0) #define MDNS_CLANG_IGNORE_INCOMPATIBLE_FUNCTION_POINTER_TYPES_STRICT_WARNING_BEGIN() \ MDNS_CLANG_IGNORE_WARNING_BEGIN(-Wincompatible-function-pointer-types-strict) #else #define MDNS_CLANG_IGNORE_INCOMPATIBLE_FUNCTION_POINTER_TYPES_STRICT_WARNING_BEGIN() #endif /*! * @brief * Undoes the effect of a previous * MDNS_CLANG_IGNORE_INCOMPATIBLE_FUNCTION_POINTER_TYPES_STRICT_WARNING_BEGIN(). */ #if MDNS_CLANG_VERSION_IS_AT_LEAST(16, 0, 0) #define MDNS_CLANG_IGNORE_INCOMPATIBLE_FUNCTION_POINTER_TYPES_STRICT_WARNING_END() MDNS_CLANG_IGNORE_WARNING_END() #else #define MDNS_CLANG_IGNORE_INCOMPATIBLE_FUNCTION_POINTER_TYPES_STRICT_WARNING_END() #endif /*! * @brief * For Clang, treats the specified warning diagnostic flag as an error. * * @param WARNING * The warning diagnostic flag. * * @discussion * Use MDNS_CLANG_TREAT_WARNING_AS_ERROR_END() to undo the effect of this macro. */ #if MDNS_COMPILER_IS_CLANG() #define MDNS_CLANG_TREAT_WARNING_AS_ERROR_BEGIN(WARNING) \ _Pragma("clang diagnostic push") \ MDNS_PRAGMA_WITH_STRINGIZED_ARGUMENT(clang diagnostic error #WARNING) #else #define MDNS_CLANG_TREAT_WARNING_AS_ERROR_BEGIN(WARNING) #endif /*! * @brief * Undoes the effect of a previous MDNS_CLANG_TREAT_WARNING_AS_ERROR_BEGIN(). */ #if MDNS_COMPILER_IS_CLANG() #define MDNS_CLANG_TREAT_WARNING_AS_ERROR_END() _Pragma("clang diagnostic pop") #else #define MDNS_CLANG_TREAT_WARNING_AS_ERROR_END() #endif /*! * @brief * For Clang, specifies that pointers without a nullability qualifier are _Nonnull. * * @discussion * See <https://clang.llvm.org/docs/AttributeReference.html#nullability-attributes>. */ #if (MDNS_COMPILER_IS_CLANG() && __has_feature(assume_nonnull)) #define MDNS_ASSUME_NONNULL_BEGIN _Pragma("clang assume_nonnull begin") #else #define MDNS_ASSUME_NONNULL_BEGIN #endif /*! * @brief * Undoes the effect of a previous MDNS_ASSUME_NONNULL_BEGIN. */ #if (MDNS_COMPILER_IS_CLANG() && __has_feature(assume_nonnull)) #define MDNS_ASSUME_NONNULL_END _Pragma("clang assume_nonnull end") #else #define MDNS_ASSUME_NONNULL_END #endif /*! * @brief * If supported, an attribute for closed enumeration definitions. * * @discussion * See <https://clang.llvm.org/docs/AttributeReference.html#enum-extensibility>. */ #if __has_attribute(enum_extensibility) #define MDNS_ENUM_ATTR_CLOSED __attribute__((enum_extensibility(closed))) #else #define MDNS_ENUM_ATTR_CLOSED #endif /*! * @brief * If supported, defines a fixed-width closed enumeration. * * @param NAME * The name of the enumeration. * * @param UNDERLYING_TYPE * The enumeration's underlying type. * * @param ... * The enumerator list. * * @discussion * See <https://clang.llvm.org/docs/LanguageExtensions.html#enumerations-with-a-fixed-underlying-type> and * <https://clang.llvm.org/docs/AttributeReference.html#enum-extensibility>. */ #if (__has_feature(objc_fixed_enum) || __has_extension(cxx_fixed_enum) || __has_extension(cxx_strong_enums)) #define MDNS_CLOSED_ENUM(NAME, UNDERLYING_TYPE, ...) \ typedef enum : UNDERLYING_TYPE { \ __VA_ARGS__ \ } MDNS_ENUM_ATTR_CLOSED NAME #else #define MDNS_CLOSED_ENUM(NAME, UNDERLYING_TYPE, ...) \ typedef UNDERLYING_TYPE NAME; \ enum NAME ## _enum { \ __VA_ARGS__ \ } MDNS_ENUM_ATTR_CLOSED #endif /*! * @brief * If supported, an attribute for flag-like enumeration definitions. * * @discussion * See <https://clang.llvm.org/docs/AttributeReference.html#flag-enum>. */ #if __has_attribute(flag_enum) #define MDNS_ENUM_ATTR_FLAG __attribute__((flag_enum)) #else #define MDNS_ENUM_ATTR_FLAG #endif /*! * @brief * If supported, defines a fixed-width closed flag-like enumeration. * * @param NAME * The name of the enumeration. * * @param UNDERLYING_TYPE * The enumeration's underlying type. * * @param ... * The enumeratior list. * * @discussion * See <https://clang.llvm.org/docs/LanguageExtensions.html#enumerations-with-a-fixed-underlying-type> and * <https://clang.llvm.org/docs/AttributeReference.html#flag-enum>. */ #if (__has_feature(objc_fixed_enum) || __has_extension(cxx_fixed_enum) || __has_extension(cxx_strong_enums)) #define MDNS_CLOSED_OPTIONS(NAME, UNDERLYING_TYPE, ...) \ typedef enum : UNDERLYING_TYPE { \ __VA_ARGS__ \ } MDNS_ENUM_ATTR_CLOSED MDNS_ENUM_ATTR_FLAG NAME #else #define MDNS_CLOSED_OPTIONS(NAME, UNDERLYING_TYPE, ...) \ typedef UNDERLYING_TYPE NAME; \ enum NAME ## _enum { \ __VA_ARGS__ \ } MDNS_ENUM_ATTR_CLOSED MDNS_ENUM_ATTR_FLAG #endif /*! * @brief * For compatibility with C++, marks the beginning of C function declarations. * * @discussion * See <https://en.cppreference.com/w/cpp/language/language_linkage>. */ #if defined(__cplusplus) #define MDNS_C_DECLARATIONS_BEGIN extern "C" { #else #define MDNS_C_DECLARATIONS_BEGIN #endif /*! * @brief * For compatibility with C++, marks the end of C function declarations. * * @discussion * This is the counterpart to MDNS_C_DECLARATIONS_BEGIN. */ #if defined(__cplusplus) #define MDNS_C_DECLARATIONS_END } #else #define MDNS_C_DECLARATIONS_END #endif /*! * @brief * Evaluates to non-zero if the compiler conforms to a specific minimum C standard. * * @param STANDARD * The C standard. */ #define MDNS_C_STANDARD_IS_AT_LEAST(STANDARD) MDNS_C_STANDARD_PRIVATE_DEFINITION_IS_AT_LEAST_ ## STANDARD () /*! * @brief * Evaluates to non-zero if the compiler confroms to the C99 standard or later. * * @discussion * __STDC_VERSION__ is a predefined macro that expands to 199901L for the C99 standard. See * <https://en.cppreference.com/w/c/preprocessor/replace>. * * Use `MDNS_C_STANDARD_IS_AT_LEAST(C99)` instead of using this macro directly. */ #if defined(__STDC_VERSION__) #define MDNS_C_STANDARD_PRIVATE_DEFINITION_IS_AT_LEAST_C99() (__STDC_VERSION__ >= 199901L) #else #define MDNS_C_STANDARD_PRIVATE_DEFINITION_IS_AT_LEAST_C99() 0 #endif /*! * @brief * Evaluates to non-zero if the compiler confroms to the C11 standard or later. * * @discussion * __STDC_VERSION__ is a predefined macro that expands to 201112L for the C11 standard. See * <https://en.cppreference.com/w/c/preprocessor/replace>. * * Use `MDNS_C_STANDARD_IS_AT_LEAST(C11)` instead of using this macro directly. */ #if defined(__STDC_VERSION__) #define MDNS_C_STANDARD_PRIVATE_DEFINITION_IS_AT_LEAST_C11() (__STDC_VERSION__ >= 201112L) #else #define MDNS_C_STANDARD_PRIVATE_DEFINITION_IS_AT_LEAST_C11() 0 #endif /*! * @brief * Evaluates to non-zero if the compiler conforms to a specific minimum C++ standard. * * @param STANDARD * The C standard. */ #define MDNS_CPP_STANDARD_IS_AT_LEAST(STANDARD) MDNS_CPP_STANDARD_PRIVATE_DEFINITION_IS_AT_LEAST_ ## STANDARD () /*! * @brief * Evaluates to non-zero if the compiler confroms to the C++11 standard or later. * * @discussion * __cplusplus is a predefined macro that expands to 201103L for the C++11 standard. See * <https://en.cppreference.com/w/cpp/preprocessor/replace>. * * Use `MDNS_CPP_STANDARD_IS_AT_LEAST(CPP11)` instead of using this macro directly. */ #if defined(__cplusplus) #define MDNS_CPP_STANDARD_PRIVATE_DEFINITION_IS_AT_LEAST_CPP11() (__cplusplus >= 201103L) #else #define MDNS_CPP_STANDARD_PRIVATE_DEFINITION_IS_AT_LEAST_CPP11() 0 #endif /*! * @brief * Causes a compile-time error if an expression evaluates to false. * * @param EXPRESSION * The expression. * * @param MESSAGE * If supported, a sting literal to include as a diagnostic message if the expression evaluates to false. */ #if MDNS_C_STANDARD_IS_AT_LEAST(C11) #define mdns_compile_time_check(EXPRESSION, MESSAGE) _Static_assert(EXPRESSION, MESSAGE) #elif MDNS_CPP_STANDARD_IS_AT_LEAST(CPP11) #define mdns_compile_time_check(EXPRESSION, MESSAGE) static_assert(EXPRESSION, MESSAGE) #elif defined(__cplusplus) #define mdns_compile_time_check(EXPRESSION, MESSAGE) \ extern "C" int mdns_compile_time_check_failed[(EXPRESSION) ? 1 : -1] #else #define mdns_compile_time_check(EXPRESSION, MESSAGE) \ extern int mdns_compile_time_check_failed[(EXPRESSION) ? 1 : -1] #endif /*! * @brief * Causes a compile-time error if an expression evaluates to false. * * @param EXPRESSION * The expression. * * @discussion * This macro is meant to be used in a local scope, i.e., inside of a function or a block. For the global * scope, use `mdns_compile_time_check()`. * * The fallback implementation is based on code from * <https://www.drdobbs.com/compile-time-assertions/184401873>. */ #if MDNS_C_STANDARD_IS_AT_LEAST(C11) #define mdns_compile_time_check_local(EXPRESSION) _Static_assert(EXPRESSION, "Compile-time assertion failed.") #elif MDNS_CPP_STANDARD_IS_AT_LEAST(CPP11) #define mdns_compile_time_check_local(EXPRESSION) static_assert(EXPRESSION, "Compile-time assertion failed.") #else #define mdns_compile_time_check_local(EXPRESSION) \ do { \ enum { \ mdns_compile_time_check_local_failed = 1 / ((EXPRESSION) ? 1 : 0) \ }; \ } while (0) #endif /*! * @brief * Determines at compile-time if the size of a type exceeds a specified maximum. * * @param TYPE * The type. * * @param MAX_SIZE * The maximum size in bytes. */ #define mdns_compile_time_max_size_check(TYPE, MAX_SIZE) \ mdns_compile_time_check(sizeof(TYPE) <= MAX_SIZE, "The size of " # TYPE " exceeds max size of '" # MAX_SIZE "'.") /*! * @brief * Determines the size of an array's element type. * * @param ARRAY * The array. */ #define mdns_sizeof_element(ARRAY) sizeof(ARRAY[0]) /*! * @brief * Determines the size of a member variable from a struct or union. * * @param TYPE * The type name of the struct or union. * * @param MEMBER * The name of the member variable. */ #define mdns_sizeof_member(TYPE, MEMBER) sizeof(((TYPE *)0)->MEMBER) /*! * @brief * Determines the number of elements in an array. * * @param ARRAY * The array. */ #define mdns_countof(ARRAY) (sizeof(ARRAY) / mdns_sizeof_element(ARRAY)) /*! * @brief * If an expression evaluates to false, transfers control to a goto label. * * @param EXPRESSION * The expression. * * @param LABEL * The location's goto label. * * @discussion * No debugging information is logged. */ #define mdns_require_quiet(EXPRESSION, LABEL) \ do { \ if (!(EXPRESSION)) { \ goto LABEL; \ } \ } while (0) /*! * @brief * If an expression evaluates to false, executes an action, then transfers control to a goto label. * * @param EXPRESSION * The expression. * * @param LABEL * The goto label. * * @param ACTION * The code to execute. * * @discussion * No debugging information is logged. */ #define mdns_require_action_quiet(EXPRESSION, LABEL, ACTION) \ do { \ if (!(EXPRESSION)) { \ { \ ACTION; \ } \ goto LABEL; \ } \ } while (0) /*! * @brief * If an error code is non-zero, transfers control to a goto label. * * @param ERROR * The error code. * * @param LABEL * The location's goto label. * * @discussion * No debugging information is logged. */ #define mdns_require_noerr_quiet(ERROR, LABEL) mdns_require_quiet(!(ERROR), LABEL) /*! * @brief * If an error code is non-zero, executes an action, then transfers control to a goto label. * * @param ERROR * The error code. * * @param LABEL * The location's goto label. * * @param ACTION * The code to execute. * * @discussion * No debugging information is logged. */ #define mdns_require_noerr_action_quiet(ERROR, LABEL, ACTION) mdns_require_action_quiet(!(ERROR), LABEL, ACTION) /*! * @brief * Returns from the current function if an expression evaluates to false. * * @param EXPRESSION * The expression. */ #define mdns_require_return(EXPRESSION) \ do { \ if (!(EXPRESSION)) { \ return; \ } \ } while (0) /*! * @brief * If an expression evaluates to false, executes an action, then returns. * * @param EXPRESSION * The expression. * * @param ACTION * The code to execute. * * @discussion * No debugging information is logged. */ #define mdns_require_return_action(EXPRESSION, ACTION) \ do { \ if (!(EXPRESSION)) { \ { \ ACTION; \ } \ return; \ } \ } while (0) /*! * @brief * Returns from the current function with a specified value if an expression evaluates to false. * * @param EXPRESSION * The expression. * * @param VALUE * The return value. */ #define mdns_require_return_value(EXPRESSION, VALUE) \ do { \ if (!(EXPRESSION)) { \ return (VALUE); \ } \ } while (0) /*! * @brief * Returns from the current function with a specified return value if an error code is non-zero. * * @param ERROR * The error code. * * @param VALUE * The return value. */ #define mdns_require_noerr_return_value(ERROR, VALUE) mdns_require_return_value(!(ERROR), VALUE) /*! * @brief * Assigns a value to a variable if the variable's address isn't NULL. * * @param VARIABLE_ADDR * The variable's address. * * @param VALUE * The value. */ #define mdns_assign(VARIABLE_ADDR, VALUE) \ do { \ if (VARIABLE_ADDR) { \ *(VARIABLE_ADDR) = (VALUE); \ } \ } while (0) /*! * @brief * Declares an array of bytes that is meant to be used as explicit padding at the end of a struct. * * @param BYTE_COUNT * The size of the array in number of bytes. * * @discussion * This explicit padding is meant to be used as the final member variable of a struct to eliminate the * -Wpadded warning about a struct's overall size being implicitly padded up to an alignment boundary. * In other words, in place of such implicit padding, use this explicit padding instead. */ #define MDNS_STRUCT_PAD(BYTE_COUNT) \ MDNS_CLANG_IGNORE_WARNING_BEGIN(-Wzero-length-array) \ char _mdns_unused_padding[(BYTE_COUNT)] \ MDNS_CLANG_IGNORE_WARNING_END() /*! * @brief * Like MDNS_STRUCT_PAD(), except that the amount of padding is specified for 64-bit and 32-bit platforms. * * @param BYTE_COUNT_64 * The amount of padding in number of bytes to use on 64-bit platforms. * * @param BYTE_COUNT_32 * The amount of padding in number of bytes to use on 32-bit platforms. * * @discussion * This macro assumes that pointers on 64-bit platforms are eight bytes in size and that pointers on 32-bit * platforms are four bytes in size. * * If the size of a pointer is something other than eight or four bytes, then a compiler error will occur. */ #define MDNS_STRUCT_PAD_64_32(BYTE_COUNT_64, BYTE_COUNT_32) \ MDNS_STRUCT_PAD( \ (sizeof(void *) == 8) ? BYTE_COUNT_64 : \ (sizeof(void *) == 4) ? BYTE_COUNT_32 : -1 \ ) /*! * @brief * Compile-time check to ensure that a struct that uses MDNS_STRUCT_PAD() or MDNS_STRUCT_PAD_64_32() hasn't * specified too much padding. * * @param STRUCT_TYPE * The struct type. * * @discussion * There's too much padding if the padding's size is greater than or equal to the struct's alignment * requirement. This is because the point of MDNS_STRUCT_PAD() and MDNS_STRUCT_PAD_64_32() is to explicitly * pad a struct up to a multiple of the struct's alignment requirement. Violating this check would * unnecessarily increase the size of the struct. */ #define MDNS_GENERAL_STRUCT_PAD_CHECK(STRUCT_TYPE) \ mdns_compile_time_check(mdns_sizeof_member(STRUCT_TYPE, _mdns_unused_padding) < _Alignof(STRUCT_TYPE), \ "Padding exceeds alignment of '" # STRUCT_TYPE "', so the amount of padding is excessive.") /*! * @brief * Retains a Core Foundation object if the specified object reference is non-NULL. * * @param OBJ * A reference to the object to retain. * * @discussion * The object reference is explicitly compared against NULL to avoid a warning from the Clang analyzer's * osx.NumberObjectConversion checker. See * <https://clang.llvm.org/docs/analyzer/checkers.html#osx-numberobjectconversion-c-c-objc>. */ #define mdns_cf_retain_null_safe(OBJ) \ do { \ if ((OBJ) != NULL) { \ CFRetain((OBJ)); \ } \ } while (0) /*! * @brief * Releases the Core Foundation object referenced by a pointer. * * @param OBJ_PTR * The address of the pointer that either references a Core Foundation object or references NULL. * * @discussion * If the pointer contains a non-NULL reference, then the pointer will be set to NULL after releasing the * object. * * The object reference is explicitly compared against NULL to avoid a warning from the Clang analyzer's * osx.NumberObjectConversion checker. See * <https://clang.llvm.org/docs/analyzer/checkers.html#osx-numberobjectconversion-c-c-objc>. */ #define mdns_cf_forget(OBJ_PTR) \ do { \ if (*(OBJ_PTR) != NULL) { \ CFRelease(*(OBJ_PTR)); \ *(OBJ_PTR) = NULL; \ } \ } while (0) /*! * @brief * Alternative to the `default` label in a switch statement that covers all enumeration values. * * @discussion * Use `MDNS_COVERED_SWITCH_DEFAULT` instead of `default` to avoid the `-Wcovered-switch-default` warning * in a switch statement that covers all enumeration values. This macro is useful when strict enforcement * of the `-Wswitch-default` warning compels us to include a default label in such switch statements. */ #if MDNS_COMPILER_IS_CLANG() #define MDNS_COVERED_SWITCH_DEFAULT \ MDNS_CLANG_IGNORE_WARNING_BEGIN(-Wcovered-switch-default) \ default \ MDNS_CLANG_IGNORE_WARNING_END() #else #define MDNS_COVERED_SWITCH_DEFAULT default #endif /*! * @brief * The static keyword for array parameters for C99 or later. * * @discussion * See <https://en.cppreference.com/w/c/language/operator_other#Function_call>. */ #if MDNS_C_STANDARD_IS_AT_LEAST(C99) #define MDNS_STATIC_ARRAY_PARAM static #else #define MDNS_STATIC_ARRAY_PARAM #endif /*! * @brief * The size of a Universally Unique Identifier (UUID) in bytes. * * @discussion * See <https://datatracker.ietf.org/doc/html/rfc4122#section-4.1>. */ #define MDNS_UUID_SIZE 16 #endif // MDNS_GENERAL_H