blob: c5fdf4d73abb28b257a05d956eff6600a7c4cce1 [file] [log] [blame] [edit]
/*
* 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