| /* |
| * 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 |