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