Home | History | Annotate | Line # | Download | only in mDNSShared
general.h revision 1.1.1.1.4.2
      1 /*
      2  * Copyright (c) 2023-2024 Apple Inc. All rights reserved.
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *     https://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #ifndef MDNS_GENERAL_H
     18 #define MDNS_GENERAL_H
     19 
     20 /*!
     21  *	@brief
     22  *		Evaluates to non-zero if compiling for a particular platform.
     23  *
     24  *	@param PLATFORM_NAME
     25  *		The name of the platform, e.g., APPLE.
     26  */
     27 #define MDNS_PLATFORM(PLATFORM_NAME)	MDNS_PLATFORM_PRIVATE_DEFINITION_ ## PLATFORM_NAME ()
     28 
     29 /*!
     30  *	@brief
     31  *		Evaluates to non-zero if compiling for Apple OSes.
     32  *
     33  *	@discussion
     34  *		`__APPLE__` is defined when compiling for Apple, see
     35  *		<https://developer.apple.com/library/archive/documentation/Porting/Conceptual/PortingUnix/compiling/compiling.html>.
     36  */
     37 #if defined(__APPLE__) && __APPLE__
     38 	#define MDNS_PLATFORM_PRIVATE_DEFINITION_APPLE()	1
     39 #else
     40 	#define MDNS_PLATFORM_PRIVATE_DEFINITION_APPLE()	0
     41 #endif
     42 
     43 #if MDNS_PLATFORM(APPLE)
     44 	#include <TargetConditionals.h>
     45 #endif
     46 
     47 /*!
     48  *	@brief
     49  *		Evaluates to non-zero if compiling for a particular OS.
     50  *
     51  *	@param OS_NAME
     52  *		The name of the OS, e.g., macOS, iOS, etc.
     53  */
     54 #define MDNS_OS(OS_NAME)	MDNS_OS_PRIVATE_DEFINITION_ ## OS_NAME ()
     55 
     56 /*!
     57  *	@brief
     58  *		Evaluates to non-zero if compiling for macOS.
     59  *
     60  *	@discussion
     61  *		Use `MDNS_OS(macOS)` instead of using this macro directly.
     62  */
     63 #if defined(TARGET_OS_OSX) && TARGET_OS_OSX
     64 	#define MDNS_OS_PRIVATE_DEFINITION_macOS()	1
     65 #else
     66 	#define MDNS_OS_PRIVATE_DEFINITION_macOS()	0
     67 #endif
     68 
     69 /*!
     70  *	@brief
     71  *		Evaluates to non-zero if compiling for iOS.
     72  *
     73  *	@discussion
     74  *		Use `MDNS_OS(iOS)` instead of using this macro directly.
     75  */
     76 #if defined(TARGET_OS_IOS) && TARGET_OS_IOS
     77 	#define MDNS_OS_PRIVATE_DEFINITION_iOS()	1
     78 #else
     79 	#define MDNS_OS_PRIVATE_DEFINITION_iOS()	0
     80 #endif
     81 
     82 /*!
     83  *	@brief
     84  *		Evaluates to non-zero if compiling for watchOS.
     85  *
     86  *	@discussion
     87  *		Use `MDNS_OS(watchOS)` instead of using this macro directly.
     88  */
     89 #if defined(TARGET_OS_WATCH) && TARGET_OS_WATCH
     90 	#define MDNS_OS_PRIVATE_DEFINITION_watchOS()	1
     91 #else
     92 	#define MDNS_OS_PRIVATE_DEFINITION_watchOS()	0
     93 #endif
     94 
     95 /*!
     96  *	@brief
     97  *		Evaluates to non-zero if compiling for tvOS.
     98  *
     99  *	@discussion
    100  *		Use `MDNS_OS(tvOS)` instead of using this macro directly.
    101  */
    102 #if defined(TARGET_OS_TV) && TARGET_OS_TV
    103 	#define MDNS_OS_PRIVATE_DEFINITION_tvOS()	1
    104 #else
    105 	#define MDNS_OS_PRIVATE_DEFINITION_tvOS()	0
    106 #endif
    107 
    108 // Time conversion constants
    109 
    110 #define MDNS_NANOSECONDS_PER_SECOND		1000000000
    111 #define MDNS_MILLISECONDS_PER_SECOND	1000
    112 #define MDNS_MILLISECONDS_PER_MINUTE	(MDNS_MILLISECONDS_PER_SECOND * MDNS_SECONDS_PER_MINUTE)
    113 #define MDNS_MILLISECONDS_PER_HOUR		(MDNS_MILLISECONDS_PER_SECOND * MDNS_SECONDS_PER_HOUR)
    114 #define MDNS_SECONDS_PER_MINUTE			60
    115 #define MDNS_SECONDS_PER_HOUR			(MDNS_SECONDS_PER_MINUTE * MDNS_MINUTES_PER_HOUR)
    116 #define MDNS_SECONDS_PER_DAY			(MDNS_SECONDS_PER_HOUR * MDNS_HOUR_PER_DAY)
    117 #define MDNS_MINUTES_PER_HOUR			60
    118 #define MDNS_HOUR_PER_DAY				24
    119 
    120 // Clang's __has_*() builtin macros are defined as zero if not defined.
    121 
    122 #if !defined(__has_attribute)
    123 	#define __has_attribute(X)	0
    124 #endif
    125 #if !defined(__has_extension)
    126 	#define __has_extension(X)	0
    127 #endif
    128 #if !defined(__has_feature)
    129 	#define __has_feature(X)	0
    130 #endif
    131 
    132 /*!
    133  *	@brief
    134  *		Evaluates to non-zero if the compiler is Clang.
    135  *
    136  *	@discussion
    137  *		__clang__ is defined when compiling with Clang, see
    138  *		<https://clang.llvm.org/docs/LanguageExtensions.html#builtin-macros>.
    139  */
    140 #if defined(__clang__)
    141 	#define MDNS_COMPILER_IS_CLANG() 1
    142 #else
    143 	#define MDNS_COMPILER_IS_CLANG() 0
    144 #endif
    145 
    146 /*!
    147  *	@brief
    148  *		Evaluates to non-zero if the compiler is Clang and its version is at least a specified version.
    149  *
    150  *	@param MAJOR
    151  *		The specified version's major number.
    152  *
    153  *	@param MINOR
    154  *		The specified version's minor number.
    155  *
    156  *	@param PATCH_LEVEL
    157  *		The specified version's patch level.
    158  *
    159  *	@discussion
    160  *		Clang version numbers are of the form "<major number>.<minor number>.<patch level>". See
    161  *		<https://clang.llvm.org/docs/LanguageExtensions.html#builtin-macros>
    162  */
    163 #if MDNS_COMPILER_IS_CLANG()
    164 	#define MDNS_CLANG_VERSION_IS_AT_LEAST(MAJOR, MINOR, PATCH_LEVEL) (						\
    165 		(__clang_major__ > (MAJOR)) || (													\
    166 			(__clang_major__ == (MAJOR)) && (												\
    167 				(__clang_minor__ > (MINOR)) || (											\
    168 					(__clang_minor__ == (MINOR)) && (__clang_patchlevel__ >= (PATCH_LEVEL))	\
    169 				)																			\
    170 			)																				\
    171 		)																					\
    172 	)
    173 #else
    174 	#define MDNS_CLANG_VERSION_IS_AT_LEAST(MAJOR, MINOR, PATCH_LEVEL) 0
    175 #endif
    176 
    177 /*!
    178  *	@brief
    179  *		Stringizes the argument and passes it to the _Pragma() operator, which takes a string literal argument.
    180  *
    181  *	@param ARG
    182  *		The argument.
    183  *
    184  *	@discussion
    185  *		Useful for escaping double quotes. For example,
    186  *
    187  *			MDNS_PRAGMA_WITH_STRINGIZED_ARGUMENT(clang diagnostic ignored "-Wpadded")
    188  *
    189  *		turns into
    190  *
    191  *			_Pragma("clang diagnostic ignored \"-Wpadded\"")
    192  *
    193  *		See <https://gcc.gnu.org/onlinedocs/cpp/Pragmas.html>.
    194  */
    195 #define MDNS_PRAGMA_WITH_STRINGIZED_ARGUMENT(ARG)	_Pragma(#ARG)
    196 
    197 /*!
    198  *	@brief
    199  *		For Clang, starts ignoring the specified warning diagnostic flag.
    200  *
    201  *	@param WARNING
    202  *		The warning diagnostic flag.
    203  *
    204  *	@discussion
    205  *		Use MDNS_CLANG_IGNORE_WARNING_END() to undo the effect of this macro.
    206  */
    207 #if MDNS_COMPILER_IS_CLANG()
    208 	#define MDNS_CLANG_IGNORE_WARNING_BEGIN(WARNING)	\
    209 		_Pragma("clang diagnostic push")				\
    210 		MDNS_PRAGMA_WITH_STRINGIZED_ARGUMENT(clang diagnostic ignored #WARNING)
    211 #else
    212 	#define MDNS_CLANG_IGNORE_WARNING_BEGIN(WARNING)
    213 #endif
    214 
    215 /*!
    216  *	@brief
    217  *		Use to undo the effect of a previous MDNS_CLANG_IGNORE_WARNING_BEGIN().
    218  */
    219 #if MDNS_COMPILER_IS_CLANG()
    220 	#define MDNS_CLANG_IGNORE_WARNING_END()	_Pragma("clang diagnostic pop")
    221 #else
    222 	#define MDNS_CLANG_IGNORE_WARNING_END()
    223 #endif
    224 
    225 /*!
    226  *	@brief
    227  *		An alternative version of MDNS_CLANG_IGNORE_WARNING_BEGIN() that looks nicer when used among statements.
    228  *
    229  *	@discussion
    230  *		This version looks nicer when used among C statements. Here's an example:
    231  *
    232  *			mdns_clang_ignore_warning_begin(-Wformat-nonliteral);
    233  *			const int n = vsnprintf(dst, len, fmt, args);
    234  *			mdns_clang_ignore_warning_end();
    235  */
    236 #define mdns_clang_ignore_warning_begin(WARNING)	\
    237 	MDNS_CLANG_IGNORE_WARNING_BEGIN(WARNING)		\
    238 	do {} while (0)
    239 
    240 /*!
    241  *	@brief
    242  *		An alternative version of MDNS_CLANG_IGNORE_WARNING_END() that looks nicer when used among statements.
    243  *
    244  *	@discussion
    245  *		This version looks nicer when used among C statements. Here's an example:
    246  *
    247  *			mdns_clang_ignore_warning_begin(-Wformat-nonliteral);
    248  *			const int n = vsnprintf(dst, len, fmt, args);
    249  *			mdns_clang_ignore_warning_end();
    250  */
    251 #define mdns_clang_ignore_warning_end()	\
    252 	MDNS_CLANG_IGNORE_WARNING_END()		\
    253 	do {} while (0)
    254 
    255 /*!
    256  *	@brief
    257  *		For Clang, starts ignoring the -Wunaligned-access warning diagnostic flag.
    258  *
    259  *	@discussion
    260  *		The -Wunaligned-access is new in clang version 14.0.3. This macro allow us to conditionally ignore
    261  *		-Wunaligned-access with Clang 14.0.3 or later. This avoids -Wunknown-warning-option warnings with
    262  *		earlier Clang versions, which don't recognize -Wunaligned-access.
    263  */
    264 #if MDNS_CLANG_VERSION_IS_AT_LEAST(14, 0, 3)
    265 	#define MDNS_CLANG_IGNORE_UNALIGNED_ACCESS_WARNING_BEGIN()	MDNS_CLANG_IGNORE_WARNING_BEGIN(-Wunaligned-access)
    266 #else
    267 	#define MDNS_CLANG_IGNORE_UNALIGNED_ACCESS_WARNING_BEGIN()
    268 #endif
    269 
    270 /*!
    271  *	@brief
    272  *		Undoes the effect of a previous MDNS_CLANG_IGNORE_UNALIGNED_ACCESS_WARNING_BEGIN().
    273  */
    274 #if MDNS_CLANG_VERSION_IS_AT_LEAST(14, 0, 3)
    275 	#define MDNS_CLANG_IGNORE_UNALIGNED_ACCESS_WARNING_END()	MDNS_CLANG_IGNORE_WARNING_END()
    276 #else
    277 	#define MDNS_CLANG_IGNORE_UNALIGNED_ACCESS_WARNING_END()
    278 #endif
    279 
    280 /*!
    281  *	@brief
    282  *		For Clang, starts ignoring the -Wincompatible-function-pointer-types-strict warning diagnostic flag.
    283  *
    284  *	@discussion
    285  *		-Wincompatible-function-pointer-types-strict is like -Wincompatible-function-pointer-types, but is more
    286  *		strict in that it warns about function pointer types that are not identical but are still compatible.
    287  *
    288  *		The -Wincompatible-function-pointer-types-strict is new in clang version 16.0.0 (see
    289  *		https://releases.llvm.org/16.0.0/tools/clang/docs/ReleaseNotes.html). This macro allow us to
    290  *		conditionally ignore -Wincompatible-function-pointer-types-strict with Clang 16.0.0 or later. This
    291  *		avoids -Wunknown-warning-option warnings with earlier Clang versions, which don't recognize
    292  *		-Wincompatible-function-pointer-types-strict.
    293  */
    294 #if MDNS_CLANG_VERSION_IS_AT_LEAST(16, 0, 0)
    295 	#define MDNS_CLANG_IGNORE_INCOMPATIBLE_FUNCTION_POINTER_TYPES_STRICT_WARNING_BEGIN() \
    296 		MDNS_CLANG_IGNORE_WARNING_BEGIN(-Wincompatible-function-pointer-types-strict)
    297 #else
    298 	#define MDNS_CLANG_IGNORE_INCOMPATIBLE_FUNCTION_POINTER_TYPES_STRICT_WARNING_BEGIN()
    299 #endif
    300 
    301 /*!
    302  *	@brief
    303  *		Undoes the effect of a previous
    304  *		MDNS_CLANG_IGNORE_INCOMPATIBLE_FUNCTION_POINTER_TYPES_STRICT_WARNING_BEGIN().
    305  */
    306 #if MDNS_CLANG_VERSION_IS_AT_LEAST(16, 0, 0)
    307 	#define MDNS_CLANG_IGNORE_INCOMPATIBLE_FUNCTION_POINTER_TYPES_STRICT_WARNING_END()	MDNS_CLANG_IGNORE_WARNING_END()
    308 #else
    309 	#define MDNS_CLANG_IGNORE_INCOMPATIBLE_FUNCTION_POINTER_TYPES_STRICT_WARNING_END()
    310 #endif
    311 
    312 /*!
    313  *	@brief
    314  *		For Clang, treats the specified warning diagnostic flag as an error.
    315  *
    316  *	@param WARNING
    317  *		The warning diagnostic flag.
    318  *
    319  *	@discussion
    320  *		Use MDNS_CLANG_TREAT_WARNING_AS_ERROR_END() to undo the effect of this macro.
    321  */
    322 #if MDNS_COMPILER_IS_CLANG()
    323 	#define MDNS_CLANG_TREAT_WARNING_AS_ERROR_BEGIN(WARNING)	\
    324 		_Pragma("clang diagnostic push")						\
    325 		MDNS_PRAGMA_WITH_STRINGIZED_ARGUMENT(clang diagnostic error #WARNING)
    326 #else
    327 	#define MDNS_CLANG_TREAT_WARNING_AS_ERROR_BEGIN(WARNING)
    328 #endif
    329 
    330 /*!
    331  *	@brief
    332  *		Undoes the effect of a previous MDNS_CLANG_TREAT_WARNING_AS_ERROR_BEGIN().
    333  */
    334 #if MDNS_COMPILER_IS_CLANG()
    335 	#define MDNS_CLANG_TREAT_WARNING_AS_ERROR_END()	_Pragma("clang diagnostic pop")
    336 #else
    337 	#define MDNS_CLANG_TREAT_WARNING_AS_ERROR_END()
    338 #endif
    339 
    340 /*!
    341  *	@brief
    342  *		For Clang, specifies that pointers without a nullability qualifier are _Nonnull.
    343  *
    344  *	@discussion
    345  *		See <https://clang.llvm.org/docs/AttributeReference.html#nullability-attributes>.
    346  */
    347 #if (MDNS_COMPILER_IS_CLANG() && __has_feature(assume_nonnull))
    348 	#define MDNS_ASSUME_NONNULL_BEGIN	_Pragma("clang assume_nonnull begin")
    349 #else
    350 	#define MDNS_ASSUME_NONNULL_BEGIN
    351 #endif
    352 
    353 /*!
    354  *	@brief
    355  *		Undoes the effect of a previous MDNS_ASSUME_NONNULL_BEGIN.
    356  */
    357 #if (MDNS_COMPILER_IS_CLANG() && __has_feature(assume_nonnull))
    358 	#define MDNS_ASSUME_NONNULL_END	_Pragma("clang assume_nonnull end")
    359 #else
    360 	#define MDNS_ASSUME_NONNULL_END
    361 #endif
    362 
    363 /*!
    364  *	@brief
    365  *		If supported, an attribute for closed enumeration definitions.
    366  *
    367  *	@discussion
    368  *		See <https://clang.llvm.org/docs/AttributeReference.html#enum-extensibility>.
    369  */
    370 #if __has_attribute(enum_extensibility)
    371 	#define MDNS_ENUM_ATTR_CLOSED	__attribute__((enum_extensibility(closed)))
    372 #else
    373 	#define MDNS_ENUM_ATTR_CLOSED
    374 #endif
    375 
    376 /*!
    377  *	@brief
    378  *		If supported, defines a fixed-width closed enumeration.
    379  *
    380  *	@param NAME
    381  *		The name of the enumeration.
    382  *
    383  *	@param UNDERLYING_TYPE
    384  *		The enumeration's underlying type.
    385  *
    386  *	@param ...
    387  *		The enumerator list.
    388  *
    389  *	@discussion
    390  *		See <https://clang.llvm.org/docs/LanguageExtensions.html#enumerations-with-a-fixed-underlying-type> and
    391  *		<https://clang.llvm.org/docs/AttributeReference.html#enum-extensibility>.
    392  */
    393 #if (__has_feature(objc_fixed_enum) || __has_extension(cxx_fixed_enum) || __has_extension(cxx_strong_enums))
    394 	#define MDNS_CLOSED_ENUM(NAME, UNDERLYING_TYPE, ...)	\
    395 		typedef enum : UNDERLYING_TYPE {					\
    396 			__VA_ARGS__										\
    397 		} MDNS_ENUM_ATTR_CLOSED NAME
    398 #else
    399 	#define MDNS_CLOSED_ENUM(NAME, UNDERLYING_TYPE, ...)	\
    400 		typedef UNDERLYING_TYPE NAME;						\
    401 		enum NAME ## _enum {								\
    402 			__VA_ARGS__										\
    403 		} MDNS_ENUM_ATTR_CLOSED
    404 #endif
    405 
    406 /*!
    407  *	@brief
    408  *		If supported, an attribute for flag-like enumeration definitions.
    409  *
    410  *	@discussion
    411  *		See <https://clang.llvm.org/docs/AttributeReference.html#flag-enum>.
    412  */
    413 #if __has_attribute(flag_enum)
    414 	#define MDNS_ENUM_ATTR_FLAG	__attribute__((flag_enum))
    415 #else
    416 	#define MDNS_ENUM_ATTR_FLAG
    417 #endif
    418 
    419 /*!
    420  *	@brief
    421  *		If supported, defines a fixed-width closed flag-like enumeration.
    422  *
    423  *	@param NAME
    424  *		The name of the enumeration.
    425  *
    426  *	@param UNDERLYING_TYPE
    427  *		The enumeration's underlying type.
    428  *
    429  *	@param ...
    430  *		The enumeratior list.
    431  *
    432  *	@discussion
    433  *		See <https://clang.llvm.org/docs/LanguageExtensions.html#enumerations-with-a-fixed-underlying-type> and
    434  *		<https://clang.llvm.org/docs/AttributeReference.html#flag-enum>.
    435  */
    436 #if (__has_feature(objc_fixed_enum) || __has_extension(cxx_fixed_enum) || __has_extension(cxx_strong_enums))
    437 	#define MDNS_CLOSED_OPTIONS(NAME, UNDERLYING_TYPE, ...)	\
    438 		typedef enum : UNDERLYING_TYPE {					\
    439 			__VA_ARGS__										\
    440 		} MDNS_ENUM_ATTR_CLOSED MDNS_ENUM_ATTR_FLAG NAME
    441 #else
    442 	#define MDNS_CLOSED_OPTIONS(NAME, UNDERLYING_TYPE, ...)	\
    443 		typedef UNDERLYING_TYPE NAME;						\
    444 		enum NAME ## _enum {								\
    445 			__VA_ARGS__										\
    446 		} MDNS_ENUM_ATTR_CLOSED MDNS_ENUM_ATTR_FLAG
    447 #endif
    448 
    449 /*!
    450  *	@brief
    451  *		For compatibility with C++, marks the beginning of C function declarations.
    452  *
    453  *	@discussion
    454  *		See <https://en.cppreference.com/w/cpp/language/language_linkage>.
    455  */
    456 #if defined(__cplusplus)
    457 	#define MDNS_C_DECLARATIONS_BEGIN	extern "C" {
    458 #else
    459 	#define MDNS_C_DECLARATIONS_BEGIN
    460 #endif
    461 
    462 /*!
    463  *	@brief
    464  *		For compatibility with C++, marks the end of C function declarations.
    465  *
    466  *	@discussion
    467  *		This is the counterpart to MDNS_C_DECLARATIONS_BEGIN.
    468  */
    469 #if defined(__cplusplus)
    470 	#define MDNS_C_DECLARATIONS_END	}
    471 #else
    472 	#define MDNS_C_DECLARATIONS_END
    473 #endif
    474 
    475 /*!
    476  *	@brief
    477  *		Evaluates to non-zero if the compiler conforms to a specific minimum C standard.
    478  *
    479  *	@param STANDARD
    480  *		The C standard.
    481  */
    482 #define MDNS_C_STANDARD_IS_AT_LEAST(STANDARD)	MDNS_C_STANDARD_PRIVATE_DEFINITION_IS_AT_LEAST_ ## STANDARD ()
    483 
    484 /*!
    485  *	@brief
    486  *		Evaluates to non-zero if the compiler confroms to the C99 standard or later.
    487  *
    488  *	@discussion
    489  *		__STDC_VERSION__ is a predefined macro that expands to 199901L for the C99 standard. See
    490  *		<https://en.cppreference.com/w/c/preprocessor/replace>.
    491  *
    492  *		Use `MDNS_C_STANDARD_IS_AT_LEAST(C99)` instead of using this macro directly.
    493  */
    494 #if defined(__STDC_VERSION__)
    495 	#define MDNS_C_STANDARD_PRIVATE_DEFINITION_IS_AT_LEAST_C99()	(__STDC_VERSION__ >= 199901L)
    496 #else
    497 	#define MDNS_C_STANDARD_PRIVATE_DEFINITION_IS_AT_LEAST_C99()	0
    498 #endif
    499 
    500 /*!
    501  *	@brief
    502  *		Evaluates to non-zero if the compiler confroms to the C11 standard or later.
    503  *
    504  *	@discussion
    505  *		__STDC_VERSION__ is a predefined macro that expands to 201112L for the C11 standard. See
    506  *		<https://en.cppreference.com/w/c/preprocessor/replace>.
    507  *
    508  *		Use `MDNS_C_STANDARD_IS_AT_LEAST(C11)` instead of using this macro directly.
    509  */
    510 #if defined(__STDC_VERSION__)
    511 	#define MDNS_C_STANDARD_PRIVATE_DEFINITION_IS_AT_LEAST_C11()	(__STDC_VERSION__ >= 201112L)
    512 #else
    513 	#define MDNS_C_STANDARD_PRIVATE_DEFINITION_IS_AT_LEAST_C11()	0
    514 #endif
    515 
    516 /*!
    517  *	@brief
    518  *		Evaluates to non-zero if the compiler conforms to a specific minimum C++ standard.
    519  *
    520  *	@param STANDARD
    521  *		The C standard.
    522  */
    523 #define MDNS_CPP_STANDARD_IS_AT_LEAST(STANDARD)	MDNS_CPP_STANDARD_PRIVATE_DEFINITION_IS_AT_LEAST_ ## STANDARD ()
    524 
    525 /*!
    526  *	@brief
    527  *		Evaluates to non-zero if the compiler confroms to the C++11 standard or later.
    528  *
    529  *	@discussion
    530  *		__cplusplus is a predefined macro that expands to 201103L for the C++11 standard. See
    531  *		<https://en.cppreference.com/w/cpp/preprocessor/replace>.
    532  *
    533  *		Use `MDNS_CPP_STANDARD_IS_AT_LEAST(CPP11)` instead of using this macro directly.
    534  */
    535 #if defined(__cplusplus)
    536 	#define MDNS_CPP_STANDARD_PRIVATE_DEFINITION_IS_AT_LEAST_CPP11()	(__cplusplus >= 201103L)
    537 #else
    538 	#define MDNS_CPP_STANDARD_PRIVATE_DEFINITION_IS_AT_LEAST_CPP11()	0
    539 #endif
    540 
    541 /*!
    542  *	@brief
    543  *		Causes a compile-time error if an expression evaluates to false.
    544  *
    545  *	@param EXPRESSION
    546  *		The expression.
    547  *
    548  *	@param MESSAGE
    549  *		If supported, a sting literal to include as a diagnostic message if the expression evaluates to false.
    550  */
    551 #if MDNS_C_STANDARD_IS_AT_LEAST(C11)
    552 	#define mdns_compile_time_check(EXPRESSION, MESSAGE)	_Static_assert(EXPRESSION, MESSAGE)
    553 #elif MDNS_CPP_STANDARD_IS_AT_LEAST(CPP11)
    554 	#define mdns_compile_time_check(EXPRESSION, MESSAGE)	static_assert(EXPRESSION, MESSAGE)
    555 #elif defined(__cplusplus)
    556 	#define	mdns_compile_time_check(EXPRESSION, MESSAGE) \
    557 		extern "C" int mdns_compile_time_check_failed[(EXPRESSION) ? 1 : -1]
    558 #else
    559 	#define	mdns_compile_time_check(EXPRESSION, MESSAGE) \
    560 		extern int mdns_compile_time_check_failed[(EXPRESSION) ? 1 : -1]
    561 #endif
    562 
    563 /*!
    564  *	@brief
    565  *		Causes a compile-time error if an expression evaluates to false.
    566  *
    567  *	@param EXPRESSION
    568  *		The expression.
    569  *
    570  *	@discussion
    571  *		This macro is meant to be used in a local scope, i.e., inside of a function or a block. For the global
    572  *		scope, use `mdns_compile_time_check()`.
    573  *
    574  *		The fallback implementation is based on code from
    575  *		<https://www.drdobbs.com/compile-time-assertions/184401873>.
    576  */
    577 #if MDNS_C_STANDARD_IS_AT_LEAST(C11)
    578 	#define mdns_compile_time_check_local(EXPRESSION)	_Static_assert(EXPRESSION, "Compile-time assertion failed.")
    579 #elif MDNS_CPP_STANDARD_IS_AT_LEAST(CPP11)
    580 	#define mdns_compile_time_check_local(EXPRESSION)	static_assert(EXPRESSION, "Compile-time assertion failed.")
    581 #else
    582 	#define mdns_compile_time_check_local(EXPRESSION)								\
    583 		do {																		\
    584 			enum {																	\
    585 				mdns_compile_time_check_local_failed = 1 / ((EXPRESSION) ? 1 : 0)	\
    586 			};																		\
    587 		} while (0)
    588 #endif
    589 
    590 /*!
    591  *	@brief
    592  *		Determines at compile-time if the size of a type exceeds a specified maximum.
    593  *
    594  *	@param TYPE
    595  *		The type.
    596  *
    597  *	@param MAX_SIZE
    598  *		The maximum size in bytes.
    599  */
    600 #define mdns_compile_time_max_size_check(TYPE, MAX_SIZE) \
    601 	mdns_compile_time_check(sizeof(TYPE) <= MAX_SIZE, "The size of " # TYPE " exceeds max size of '" # MAX_SIZE "'.")
    602 
    603 /*!
    604  *	@brief
    605  *		Determines the size of an array's element type.
    606  *
    607  *	@param ARRAY
    608  *		The array.
    609  */
    610 #define mdns_sizeof_element(ARRAY)	sizeof(ARRAY[0])
    611 
    612 /*!
    613  *	@brief
    614  *		Determines the size of a member variable from a struct or union.
    615  *
    616  *	@param TYPE
    617  *		The type name of the struct or union.
    618  *
    619  *	@param MEMBER
    620  *		The name of the member variable.
    621  */
    622 #define mdns_sizeof_member(TYPE, MEMBER)	sizeof(((TYPE *)0)->MEMBER)
    623 
    624 /*!
    625  *	@brief
    626  *		Determines the number of elements in an array.
    627  *
    628  *	@param ARRAY
    629  *		The array.
    630  */
    631 #define mdns_countof(ARRAY)	(sizeof(ARRAY) / mdns_sizeof_element(ARRAY))
    632 
    633 /*!
    634  *	@brief
    635  *		If an expression evaluates to false, transfers control to a goto label.
    636  *
    637  *	@param EXPRESSION
    638  *		The expression.
    639  *
    640  *	@param LABEL
    641  *		The location's goto label.
    642  *
    643  *	@discussion
    644  *		No debugging information is logged.
    645  */
    646 #define mdns_require_quiet(EXPRESSION, LABEL)	\
    647 	do {										\
    648 		if (!(EXPRESSION)) {					\
    649 			goto LABEL;							\
    650 		}										\
    651 	} while (0)
    652 
    653 /*!
    654  *	@brief
    655  *		If an expression evaluates to false, executes an action, then transfers control to a goto label.
    656  *
    657  *	@param EXPRESSION
    658  *		The expression.
    659  *
    660  *	@param LABEL
    661  *		The goto label.
    662  *
    663  *	@param ACTION
    664  *		The code to execute.
    665  *
    666  *	@discussion
    667  *		No debugging information is logged.
    668  */
    669 #define mdns_require_action_quiet(EXPRESSION, LABEL, ACTION)	\
    670 	do {														\
    671 		if (!(EXPRESSION)) {									\
    672 			{													\
    673 				ACTION;											\
    674 			}													\
    675 			goto LABEL;											\
    676 		}														\
    677 	} while (0)
    678 
    679 /*!
    680  *	@brief
    681  *		If an error code is non-zero, transfers control to a goto label.
    682  *
    683  *	@param ERROR
    684  *		The error code.
    685  *
    686  *	@param LABEL
    687  *		The location's goto label.
    688  *
    689  *	@discussion
    690  *		No debugging information is logged.
    691  */
    692 #define mdns_require_noerr_quiet(ERROR, LABEL)	mdns_require_quiet(!(ERROR), LABEL)
    693 
    694 /*!
    695  *	@brief
    696  *		If an error code is non-zero, executes an action, then transfers control to a goto label.
    697  *
    698  *	@param ERROR
    699  *		The error code.
    700  *
    701  *	@param LABEL
    702  *		The location's goto label.
    703  *
    704  *	@param ACTION
    705  *		The code to execute.
    706  *
    707  *	@discussion
    708  *		No debugging information is logged.
    709  */
    710 #define mdns_require_noerr_action_quiet(ERROR, LABEL, ACTION)	mdns_require_action_quiet(!(ERROR), LABEL, ACTION)
    711 
    712 /*!
    713  *	@brief
    714  *		Returns from the current function if an expression evaluates to false.
    715  *
    716  *	@param EXPRESSION
    717  *		The expression.
    718  */
    719 #define mdns_require_return(EXPRESSION)	\
    720 	do {								\
    721 		if (!(EXPRESSION)) {			\
    722 			return;						\
    723 		}								\
    724 	} while (0)
    725 
    726 /*!
    727  *	@brief
    728  *		If an expression evaluates to false, executes an action, then returns.
    729  *
    730  *	@param EXPRESSION
    731  *		The expression.
    732  *
    733  *	@param ACTION
    734  *		The code to execute.
    735  *
    736  *	@discussion
    737  *		No debugging information is logged.
    738  */
    739 #define mdns_require_return_action(EXPRESSION, ACTION)	\
    740 	do {												\
    741 		if (!(EXPRESSION)) {							\
    742 			{											\
    743 				ACTION;									\
    744 			}											\
    745 			return;										\
    746 		}												\
    747 	} while (0)
    748 
    749 /*!
    750  *	@brief
    751  *		Returns from the current function with a specified value if an expression evaluates to false.
    752  *
    753  *	@param EXPRESSION
    754  *		The expression.
    755  *
    756  *	@param VALUE
    757  *		The return value.
    758  */
    759 #define mdns_require_return_value(EXPRESSION, VALUE)	\
    760 	do {												\
    761 		if (!(EXPRESSION)) {							\
    762 			return (VALUE);								\
    763 		}												\
    764 	} while (0)
    765 
    766 /*!
    767  *	@brief
    768  *		Returns from the current function with a specified return value if an error code is non-zero.
    769  *
    770  *	@param ERROR
    771  *		The error code.
    772  *
    773  *	@param VALUE
    774  *		The return value.
    775  */
    776 #define mdns_require_noerr_return_value(ERROR, VALUE)	mdns_require_return_value(!(ERROR), VALUE)
    777 
    778 /*!
    779  *	@brief
    780  *		Assigns a value to a variable if the variable's address isn't NULL.
    781  *
    782  *	@param VARIABLE_ADDR
    783  *		The variable's address.
    784  *
    785  *	@param VALUE
    786  *		The value.
    787  */
    788 #define mdns_assign(VARIABLE_ADDR, VALUE)	\
    789 	do {									\
    790 		if (VARIABLE_ADDR) {				\
    791 			*(VARIABLE_ADDR) = (VALUE);		\
    792 		}									\
    793 	} while (0)
    794 
    795 /*!
    796  *	@brief
    797  *		Declares an array of bytes that is meant to be used as explicit padding at the end of a struct.
    798  *
    799  *	@param BYTE_COUNT
    800  *		The size of the array in number of bytes.
    801  *
    802  *	@discussion
    803  *		This explicit padding is meant to be used as the final member variable of a struct to eliminate the
    804  *		-Wpadded warning about a struct's overall size being implicitly padded up to an alignment boundary.
    805  *		In other words, in place of such implicit padding, use this explicit padding instead.
    806  */
    807 #define MDNS_STRUCT_PAD(BYTE_COUNT)							\
    808 	MDNS_CLANG_IGNORE_WARNING_BEGIN(-Wzero-length-array)	\
    809 	char _mdns_unused_padding[(BYTE_COUNT)]					\
    810 	MDNS_CLANG_IGNORE_WARNING_END()
    811 
    812 /*!
    813  *	@brief
    814  *		Like MDNS_STRUCT_PAD(), except that the amount of padding is specified for 64-bit and 32-bit platforms.
    815  *
    816  *	@param BYTE_COUNT_64
    817  *		The amount of padding in number of bytes to use on 64-bit platforms.
    818  *
    819  *	@param BYTE_COUNT_32
    820  *		The amount of padding in number of bytes to use on 32-bit platforms.
    821  *
    822  *	@discussion
    823  *		This macro assumes that pointers on 64-bit platforms are eight bytes in size and that pointers on 32-bit
    824  *		platforms are four bytes in size.
    825  *
    826  *		If the size of a pointer is something other than eight or four bytes, then a compiler error will occur.
    827  */
    828 #define MDNS_STRUCT_PAD_64_32(BYTE_COUNT_64, BYTE_COUNT_32)	\
    829 	MDNS_STRUCT_PAD(										\
    830 		(sizeof(void *) == 8) ? BYTE_COUNT_64 :				\
    831 		(sizeof(void *) == 4) ? BYTE_COUNT_32 : -1			\
    832 	)
    833 
    834 /*!
    835  *	@brief
    836  *		Compile-time check to ensure that a struct that uses MDNS_STRUCT_PAD() or MDNS_STRUCT_PAD_64_32() hasn't
    837  *		specified too much padding.
    838  *
    839  *	@param STRUCT_TYPE
    840  *		The struct type.
    841  *
    842  *	@discussion
    843  *		There's too much padding if the padding's size is greater than or equal to the struct's alignment
    844  *		requirement. This is because the point of MDNS_STRUCT_PAD() and MDNS_STRUCT_PAD_64_32() is to explicitly
    845  *		pad a struct up to a multiple of the struct's alignment requirement. Violating this check would
    846  *		unnecessarily increase the size of the struct.
    847  */
    848 #define MDNS_GENERAL_STRUCT_PAD_CHECK(STRUCT_TYPE)															\
    849 	mdns_compile_time_check(mdns_sizeof_member(STRUCT_TYPE, _mdns_unused_padding) < _Alignof(STRUCT_TYPE),	\
    850 		"Padding exceeds alignment of '" # STRUCT_TYPE "', so the amount of padding is excessive.")
    851 
    852 /*!
    853  *	@brief
    854  *		Retains a Core Foundation object if the specified object reference is non-NULL.
    855  *
    856  *	@param OBJ
    857  *		A reference to the object to retain.
    858  *
    859  *	@discussion
    860  *		The object reference is explicitly compared against NULL to avoid a warning from the Clang analyzer's
    861  *		osx.NumberObjectConversion checker. See
    862  *		<https://clang.llvm.org/docs/analyzer/checkers.html#osx-numberobjectconversion-c-c-objc>.
    863  */
    864 #define mdns_cf_retain_null_safe(OBJ)	\
    865 	do {								\
    866 		if ((OBJ) != NULL) {			\
    867 			CFRetain((OBJ));			\
    868 		}								\
    869 	} while (0)
    870 
    871 /*!
    872  *	@brief
    873  *		Releases the Core Foundation object referenced by a pointer.
    874  *
    875  *	@param OBJ_PTR
    876  *		The address of the pointer that either references a Core Foundation object or references NULL.
    877  *
    878  *	@discussion
    879  *		If the pointer contains a non-NULL reference, then the pointer will be set to NULL after releasing the
    880  *		object.
    881  *
    882  *		The object reference is explicitly compared against NULL to avoid a warning from the Clang analyzer's
    883  *		osx.NumberObjectConversion checker. See
    884  *		<https://clang.llvm.org/docs/analyzer/checkers.html#osx-numberobjectconversion-c-c-objc>.
    885  */
    886 #define mdns_cf_forget(OBJ_PTR)		\
    887 	do {							\
    888 		if (*(OBJ_PTR) != NULL) {	\
    889 			CFRelease(*(OBJ_PTR));	\
    890 			*(OBJ_PTR) = NULL;		\
    891 		}							\
    892 	} while (0)
    893 
    894 /*!
    895  *	@brief
    896  *		Alternative to the `default` label in a switch statement that covers all enumeration values.
    897  *
    898  *	@discussion
    899  *		Use `MDNS_COVERED_SWITCH_DEFAULT` instead of `default` to avoid the `-Wcovered-switch-default` warning
    900  *		in a switch statement that covers all enumeration values. This macro is useful when strict enforcement
    901  *		of the `-Wswitch-default` warning compels us to include a default label in such switch statements.
    902  */
    903 #if MDNS_COMPILER_IS_CLANG()
    904 	#define MDNS_COVERED_SWITCH_DEFAULT								\
    905 		MDNS_CLANG_IGNORE_WARNING_BEGIN(-Wcovered-switch-default)	\
    906 		default														\
    907 		MDNS_CLANG_IGNORE_WARNING_END()
    908 #else
    909 	#define MDNS_COVERED_SWITCH_DEFAULT	default
    910 #endif
    911 
    912 /*!
    913  *	@brief
    914  *		The static keyword for array parameters for C99 or later.
    915  *
    916  *	@discussion
    917  *		See <https://en.cppreference.com/w/c/language/operator_other#Function_call>.
    918  */
    919 #if MDNS_C_STANDARD_IS_AT_LEAST(C99)
    920 	#define MDNS_STATIC_ARRAY_PARAM	static
    921 #else
    922 	#define MDNS_STATIC_ARRAY_PARAM
    923 #endif
    924 
    925 /*!
    926  *	@brief
    927  *		The size of a Universally Unique Identifier (UUID) in bytes.
    928  *
    929  *	@discussion
    930  *		See <https://datatracker.ietf.org/doc/html/rfc4122#section-4.1>.
    931  */
    932 #define MDNS_UUID_SIZE	16
    933 
    934 #endif	// MDNS_GENERAL_H
    935