diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d35e113865..70a673f754 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -23,6 +23,13 @@ jobs: cabuild_ref: "v1" linux: false macos: true + ci-windows: + name: Windows CI + uses: bus1/cabuild/.github/workflows/ci-c-util.yml@v1 + with: + cabuild_ref: "v1" + linux: false + windows: true ci-docs: name: Documentation CI uses: bus1/cabuild/.github/workflows/ci-sphinx.yml@v1 diff --git a/src/c-stdaux-generic.h b/src/c-stdaux-generic.h new file mode 100644 index 0000000000..da67015186 --- /dev/null +++ b/src/c-stdaux-generic.h @@ -0,0 +1,471 @@ +#pragma once + +/* + * c-stdaux-generic: Generic auxiliary macros and functions + * + * This header contains all generic features of c-stdaux.h, which are not + * specific to a target platform. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +/* Documented alongside target properties. */ +#define C_MODULE_GENERIC 1 + +/** + * DOC: Target Properties + * + * Since multiple target compilers and systems are supported, c-stdaux + * exports a set of symbols that identify the target of the current compilation. + * The following pre-processor constants are defined (and evaluate to ``1``) if + * the current compilation targets the specific system. Note that multiple + * constants might be defined at the same time if compatibility to multiple + * targets is available. + * + * - ``C_COMPILER_CLANG``: The compiling software is compatible to the CLang + * LLVM Compiler. + * - ``C_COMPILER_DOCS``: The compilation is part of generating documentation. + * - ``C_COMPILER_GNUC``: The compiling software is compatible to the GNU C + * Compiler. + * - ``C_COMPILER_MSVC``: The compiling software is compatible to Microsoft + * Visual Studio (use ``_MSC_VER`` to check for specific version support). + * - ``C_OS_LINUX``: The target system is compatible to Linux. + * - ``C_OS_MACOS``: The target system is compatible to Apple MacOS. + * - ``C_OS_WINDOWS``: The target system is compatible to Microsoft Windows. + * - ``C_MODULE_GENERIC``: The `*-generic.h` module was included. + * - ``C_MODULE_GNUC``: The `*-gnuc.h` module was included. + * - ``C_MODULE_UNIX``: The `*-unix.h` module was included. + * + * Note that other exported symbols might depend on one of these constants to + * be set in order to be exposed. See the documentation of each symbol for + * details. Furthermore, if stub implementations do not violate the guarantees + * of a symbol, they will be provided for targets that do not provide the + * necessary infrastructure (e.g., ``_c_likely_()`` is a no-op on MSVC). + */ +/**/ + +#if defined(__clang__) +# define C_COMPILER_CLANG 1 +#endif + +/* #define C_COMPILER_DOCS 1 */ + +#if defined(__GNUC__) +# define C_COMPILER_GNUC 1 +#endif + +#if defined(_MSC_VER) +# define C_COMPILER_MSVC 1 +#endif + +#if defined(__linux__) +# define C_OS_LINUX 1 +#endif + +#if defined(__MACH__) && defined(__APPLE__) +# define C_OS_MACOS 1 +#endif + +#if defined(_WIN32) || defined(_WIN64) +# define C_OS_WINDOWS 1 +#endif + +/** + * DOC: Guaranteed STD-C Includes + * + * c-stdaux includes a set of C Standard Library headers. All those includes + * are guaranteed and part of the API. See the actual header for a + * comprehensive list. + */ +/**/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * DOC: Generic Compiler Intrinsics + * + * This section provides access to compiler extensions and intrinsics which are + * either portable or have generic fallbacks. + */ +/**/ + +/** + * _c_always_inline_() - Always-inline attribute + * + * Annotate a symbol to be inlined more aggressively. On GNUC targets this is + * an alias for ``__attribute__((__always_inline__))``. On MSVC targets this is + * and alias for ``__forceinline``. On other systems, this is a no-op. + */ +#define _c_always_inline_ _c_internal_always_inline_ +#if defined(C_COMPILER_GNUC) +# define _c_internal_always_inline_ __attribute__((__always_inline__)) +#elif defined(C_COMPILER_MSVC) +# define _c_internal_always_inline_ __forceinline +#else +# define _c_internal_always_inline_ +#endif + +/** + * _c_likely_() - Likely attribute + * @_x: Expression to evaluate + * + * Alias for ``__builtin_expect(!!(_x), 1)``. + * + * Return: The expression ``!!_x`` is evaluated and returned. + */ +#define _c_likely_(_x) _c_internal_likely_(_x) +#if defined(C_COMPILER_GNUC) +# define _c_internal_likely_(_x) (__builtin_expect(!!(_x), 1)) +#else +# define _c_internal_likely_(_x) (!!(_x)) +#endif + +/** + * _c_public_() - Public attribute + * + * Mark a symbol definition as public, to be exported by the linker. On + * GNUC-compatible systems, this is an alias for + * ``__attribute__((__visibility__("default")))``. On all other systems, this + * is a no-op. + * + * Note that this explicitly does not resolve to ``__declspec(dllexport)`` on + * MSVC targets, since that would require knowing whether to compile for export + * or inport and whether to compile for static or dynamic linking. Instead, + * the ``_c_public_`` attribute is meant to be used unconditionally on + * definition only. For MSVC exports, we recommend module definition files. + */ +#define _c_public_ _c_internal_public_ +#if defined(C_COMPILER_GNUC) +# define _c_internal_public_ __attribute__((__visibility__("default"))) +#else +# define _c_internal_public_ +#endif + +/** + * _c_unlikely_() - Unlikely attribute + * @_x: Expression to evaluate + * + * Alias for ``__builtin_expect(!!(_x), 0)``. + * + * Return: The expression ``!!_x`` is evaluated and returned. + */ +#define _c_unlikely_(_x) _c_internal_unlikely_(_x) +#if defined(C_COMPILER_GNUC) +# define _c_internal_unlikely_(_x) (__builtin_expect(!!(_x), 0)) +#else +# define _c_internal_unlikely_(_x) (!!(_x)) +#endif + +/** + * DOC: Generic Utility Macros + * + * A set of utility macros which is portable to all supported platforms or has + * generic fallback variants. + */ +/**/ + +/** + * C_STRINGIFY() - Stringify a token, but evaluate it first + * @_x: Token to evaluate and stringify + * + * Return: Evaluates to a constant string literal + */ +#define C_STRINGIFY(_x) C_INTERNAL_STRINGIFY(_x) +#define C_INTERNAL_STRINGIFY(_x) #_x + +/** + * C_CONCATENATE() - Concatenate two tokens, but evaluate them first + * @_x: First token + * @_y: Second token + * + * Return: Evaluates to a constant identifier + */ +#define C_CONCATENATE(_x, _y) C_INTERNAL_CONCATENATE(_x, _y) +#define C_INTERNAL_CONCATENATE(_x, _y) _x ## _y + +/** + * C_EXPAND() - Expand a tuple to a series of its values + * @_x: Tuple to expand + * + * Return: Evaluates to the expanded tuple + */ +#define C_EXPAND(_x) C_INTERNAL_EXPAND _x +#define C_INTERNAL_EXPAND(...) __VA_ARGS__ + +/** + * C_VAR() - Generate unique variable name + * @_x: Name of variable, optional + * @_uniq: Unique prefix, usually provided by ``__COUNTER__``, optional + * + * This macro shall be used to generate unique variable names, that will not be + * shadowed by recursive macro invocations. It is effectively a + * :c:macro:`C_CONCATENATE` of both arguments, but also provides a globally + * separated prefix and makes the code better readable. + * + * The second argument is optional. If not given, ``__LINE__`` is implied, and + * as such the macro will generate the same identifier if used multiple times + * on the same code-line (or within a macro). This should be used if recursive + * calls into the macro are not expected. In fact, no argument is necessary in + * this case, as a mere ``C_VAR`` will evaluate to a valid variable name. + * + * This helper may be used by macro implementations that might reasonable well + * be called in a stacked fasion, like: + * + * .. code-block:: c + * + * c_max(foo, c_max(bar, baz)) + * + * Such a stacked call of :c:macro:`c_max()` might cause compiler warnings of + * shadowed variables in the definition of :c:macro:`c_max()`. By using + * ``C_VAR()``, such warnings can be silenced as each evaluation of + * :c:macro:`c_max()` uses unique variable names. + * + * Return: This evaluates to a constant identifier. + */ +#define C_VAR(...) C_INTERNAL_VAR(__VA_ARGS__, 2, 1) +#define C_INTERNAL_VAR(_x, _uniq, _num, ...) C_VAR ## _num (_x, _uniq) +#define C_VAR1(_x, _unused) C_VAR2(_x, C_CONCATENATE(line, __LINE__)) +#define C_VAR2(_x, _uniq) C_CONCATENATE(c_internal_var_unique_, C_CONCATENATE(_uniq, _x)) + +/** + * DOC: Generic Standard Library Utilities + * + * The C Standard Library lacks some crucial and basic support functions. This + * section describes the set of helpers provided as extension to the standard + * library. + */ +/**/ + +/** + * c_assert() - Runtime assertions + * @_x: Result of an expression + * + * This function behaves like the standard ``assert(3)`` macro. That is, if + * ``NDEBUG`` is defined, it is a no-op. In all other cases it will assert that + * the result of the passed expression is true. + * + * Unlike the standard ``assert(3)`` macro, this function always evaluates its + * argument. This means side-effects will always be evaluated! However, if the + * macro is used with constant expressions, the compiler will be able to + * optimize it away. + */ +#define c_assert(_x) ( \ + (bool)(_x) \ + ? assert(true && #_x) \ + : assert(false && #_x) \ + ) + +/** + * c_errno() - Return valid errno + * + * This helper should be used to silence warnings if you know ``errno`` is valid + * (ie., ``errno`` is greater than 0). Instead of ``return -errno;``, use + * ``return -c_errno();`` It will suppress bogus warnings in case the compiler + * assumes ``errno`` might be 0 (or smaller than 0) and thus the caller's + * error-handling might not be triggered. + * + * This helper should be avoided whenever possible. However, occasionally we + * really want to silence warnings (especially with static/inline functions). In + * those cases, the compiler usually cannot deduce that some error paths are + * guaranteed to be taken. Hence, making the return value explicit allows it to + * better optimize the code. + * + * Note that you really should never use this helper to work around broken libc + * calls or syscalls, not setting 'errno' correctly. + * + * Return: Positive error code is returned. + */ +static inline int c_errno(void) { + return _c_likely_(errno > 0) ? errno : ENOTRECOVERABLE; +} + +/** + * c_memset() - Fill memory region with constant byte + * @p: Pointer to memory region, if non-empty + * @c: Value to fill with + * @n: Size of the memory region in bytes + * + * This function works like ``memset(3)`` if ``n`` is non-zero. If ``n`` is + * zero, this function is a no-op. Therefore, unlike ``memset(3)`` it is safe + * to call this function with ``NULL`` as ``p`` if ``n`` is 0. + * + * Return: ``p`` is returned. + */ +static inline void *c_memset(void *p, int c, size_t n) { + if (n > 0) + memset(p, c, n); + return p; +} + +/** + * c_memzero() - Clear memory area + * @p: Pointer to memory region, if non-empty + * @n: Size of the memory region in bytes + * + * Clear a memory area to 0. If the memory area is empty, this is a no-op. + * Similar to ``c_memset()``, this function allows ``p`` to be ``NULL`` if the + * area is empty. + * + * Return: ``p`` is returned. + */ +static inline void *c_memzero(void *p, size_t n) { + return c_memset(p, 0, n); +} + +/** + * c_memcpy() - Copy memory area + * @dst: Pointer to target area + * @src: Pointer to source area + * @n: Length of area to copy + * + * Copy the memory of size ``n`` from ``src`` to ``dst``, just as ``memcpy(3)`` + * does, except this function allows either to be ``NULL`` if ``n`` is zero. In + * the latter case, the operation is a no-op. + * + * Return: ``p`` is returned. + */ +static inline void *c_memcpy(void *dst, const void *src, size_t n) { + if (n > 0) + memcpy(dst, src, n); + return dst; +} + +/** + * c_memcmp() - Compare memory areas + * @s1: Pointer to one area + * @s2: Pointer to other area + * @n: Length of area to compare + * + * Compare the memory of size ``n`` of ``s1`` and ``s2``, just as ``memcmp(3)`` + * does, except this function allows either to be ``NULL`` if ``n`` is zero. + * + * Return: Comparison result for ordering is returned. + */ +static inline int c_memcmp(const void *s1, const void *s2, size_t n) { + if (n > 0) + return memcmp(s1, s2, n); + return 0; +} + +/** + * DOC: Generic Destructors + * + * A set of destructors is provided which extends standard library destructors + * to adhere to some adjuvant rules. In particular, they return an invalid + * value of the particular object, rather than void. This allows direct + * assignment to any member-field and/or variable they are defined in, like: + * + * .. code-block:: c + * + * foo = c_free(foo); + * foo->bar = c_fclose(foo->bar); + * + * Furthermore, all those destructors can be safely called with the "INVALID" + * value as argument, and they will be a no-op. + */ +/**/ + +/** + * c_free() - Destructor-wrapper for free() + * @p: Value to pass to destructor, or NULL + * + * Wrapper around ``free()``, but always returns ``NULL``. + * + * Return: NULL is returned. + */ +static inline void *c_free(void *p) { + free(p); + return NULL; +} + +/** + * c_fclose() - Destructor-wrapper for fclose() + * @f: File handle to pass to destructor, or NULL + * + * Wrapper around ``fclose()``, but a no-op if ``NULL`` is passed. Always + * returns ``NULL``. + * + * Return: NULL is returned. + */ +static inline FILE *c_fclose(FILE *f) { + if (f) + fclose(f); + return NULL; +} + +/** + * DOC: Generic Cleanup Helpers + * + * A set of helpers that aid in creating functions suitable for use with + * :c:macro:`_c_cleanup_()`. Furthermore, a collection of predefined cleanup + * functions of a set of standard library objects ready for use with + * :c:macro:`_c_cleanup_()`. + * Those cleanup helpers are always suffixed with a ``p``. + * + * The helpers that are provided are: + * + * - ``c_freep()``: Wrapper around :c:func:`c_free()`. + * - ``c_fclosep()``: Wrapper around :c:func:`c_fclose()`. + */ +/**/ + +/** + * C_DEFINE_CLEANUP() - Define cleanup helper + * @_type: Type of object to cleanup + * @_func: Destructor of the respective type + * + * Define a C static inline function that takes a single argument of type + * `_type` and calls `_func` on it, if its dereferenced value of its argument + * evaluates to true. Otherwise, it is a no-op. + * + * This macro allows for very simple and fast creation of cleanup helpers for + * use with ``_c_cleanup_()``, based on any destructor and type you provide to + * it. + */ +#define C_DEFINE_CLEANUP(_type, _func) \ + static inline void _func ## p(_type *p) { \ + if (*p) \ + _func(*p); \ + } struct c_internal_trailing_semicolon + +/** + * C_DEFINE_DIRECT_CLEANUP() - Define direct cleanup helper + * @_type: Type of object to cleanup + * @_func: Destructor of the respective type + * + * This works like :c:macro:`C_DEFINE_CLEANUP()` but does not check the + * dereferenced value of its argument. It always unconditionally invokes the + * destructor. + */ +#define C_DEFINE_DIRECT_CLEANUP(_type, _func) \ + static inline void _func ## p(_type *p) { \ + _func(*p); \ + } struct c_internal_trailing_semicolon + +static inline void c_freep(void *p) { + /* + * `foobar **` does not coerce to `void **`, so we need `void *` as + * argument type, and then we dereference manually. + */ + c_free(*(void **)p); +} + +C_DEFINE_CLEANUP(FILE *, c_fclose); + +#ifdef __cplusplus +} +#endif diff --git a/src/c-stdaux-gnuc.h b/src/c-stdaux-gnuc.h new file mode 100644 index 0000000000..df323efdfa --- /dev/null +++ b/src/c-stdaux-gnuc.h @@ -0,0 +1,435 @@ +#pragma once + +/* + * c-stdaux-gnuc: GNUC-specific auxiliary macros and functions + * + * This header contains all GNUC-specific features of c-stdaux.h, usually only + * available when compiled via GCC or clang. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +/* Documented alongside target properties. */ +#define C_MODULE_GNUC 1 + +/** + * DOC: GNUC Compiler Attributes + * + * The GCC compiler uses the ``__attribute__((__xyz__()))`` syntax to annotate + * language entities with special attributes. Aliases are provided by this + * header which map one-to-one to the respective compiler attributes. + * + * These attributes are not supported by all compilers, but are always provided + * by this header. They are pre-processor macros and do not affect the + * compilation, unless used. Note that most compilers support these, not just + * GCC. + */ +/**/ + +/** + * _c_cleanup_() - Cleanup attribute + * @_x: Cleanup function to use + * + * Alias for ``__attribute__((__cleanup__(_x)))``. + */ +#define _c_cleanup_(_x) __attribute__((__cleanup__(_x))) + +/** + * _c_const_() - Const attribute + * + * Alias for ``__attribute__((__const__))``. + */ +#define _c_const_ __attribute__((__const__)) + +/** + * _c_deprecated_() - Deprecated attribute + * + * Alias for ``__attribute__((__deprecated__))``. + */ +#define _c_deprecated_ __attribute__((__deprecated__)) + +/** + * _c_hidden_() - Hidden attribute + * + * Alias for ``__attribute__((__visibility__("hidden")))``. + */ +#define _c_hidden_ __attribute__((__visibility__("hidden"))) + +/** + * _c_packed_() - Packed attribute + * + * Alias for ``__attribute__((__packed__))``. + */ +#define _c_packed_ __attribute__((__packed__)) + +/** + * _c_printf_() - Printf attribute + * @_a: Format expression argument index + * @_b: First format-parameter argument index + * + * Alias for ``__attribute__((__format__(printf, _a, _b)))``. + */ +#define _c_printf_(_a, _b) __attribute__((__format__(printf, _a, _b))) + +/** + * _c_pure_() - Pure attribute + * + * Alias for ``__attribute__((__pure__))``. + */ +#define _c_pure_ __attribute__((__pure__)) + +/** + * _c_sentinel_() - Sentinel attribute + * + * Alias for ``__attribute__((__sentinel__))``. + */ +#define _c_sentinel_ __attribute__((__sentinel__)) + +/** + * _c_unused_() - Unused attribute + * + * Alias for ``__attribute__((__unused__))``. + */ +#define _c_unused_ __attribute__((__unused__)) + +/** + * DOC: GNUC-Specific Utility Macros + * + * A set of utility macros is provided which aids in creating safe macros + * suitable for use in other pre-processor statements as well as in C + * expressions. + */ +/**/ + +/** + * C_EXPR_ASSERT() - Create expression with assertion + * @_expr: Expression to evaluate to + * @_assertion: Arbitrary assertion + * @_message: Message associated with the assertion + * + * This macro simply evaluates to ``_expr``. That is, it can be used in any + * context that expects an expression like ``_expr``. Additionally, it takes an + * assertion as ``_assertion`` and evaluates it through ``_Static_assert()``, + * using ``_message`` as debug message. + * + * The ``_Static_assert()`` builtin of C11 is defined as statement and thus + * cannot be used in expressions. This macro circumvents this restriction. + * + * Return: Evaluates to ``_expr``. + */ +#define C_EXPR_ASSERT(_expr, _assertion, _message) C_INTERNAL_EXPR_ASSERT((_expr), (_assertion), _message) +#if defined(__COVERITY__) // Coverity cannot const-fold __builtin_choose_expr() +# define C_INTERNAL_EXPR_ASSERT(_expr, _assertion, _message) (_expr) +#else +# define C_INTERNAL_EXPR_ASSERT(_expr, _assertion, _message) \ + /* indentation and line-split to get better diagnostics */ \ + (__builtin_choose_expr( \ + !!(1 + 0 * sizeof( \ + struct { \ +_Static_assert(_assertion, _message); \ + } \ + )), \ + (_expr), \ + ((void)0) \ + )) +#endif + +/** + * C_CC_MACRO1() - Provide safe environment to a macro + * @_call: Macro to call + * @_x1: First argument + * @...: Further arguments to forward unmodified to ``_call`` + * + * This function simplifies the implementation of macros. Whenever you + * implement a macro, provide the internal macro name as ``_call`` and its + * argument as ``_x1``. Inside of your internal macro, you... + * + * - are safe against multiple evaluation errors, since ``C_CC_MACRO1`` + * will store the initial parameters in temporary variables. + * + * - support constant folding, as ``C_CC_MACRO1`` takes care to invoke your + * macro with the original values, if they are compile-time constant. + * + * - have unique variable names for recursive callers and will not run into + * variable-shadowing-warnings accidentally. + * + * - have properly typed arguments as ``C_CC_MACRO1`` stores the original + * arguments in an ``__auto_type`` temporary variable. + * + * Return: Result of ``_call`` is returned. + */ +#define C_CC_MACRO1(_call, _x1, ...) C_INTERNAL_CC_MACRO1(_call, __COUNTER__, (_x1), ## __VA_ARGS__) +#define C_INTERNAL_CC_MACRO1(_call, _x1q, _x1, ...) \ + __builtin_choose_expr( \ + __builtin_constant_p(_x1), \ + _call(_x1, ## __VA_ARGS__), \ + __extension__ ({ \ + const __auto_type C_VAR(X1, _x1q) = (_x1); \ + _call(C_VAR(X1, _x1q), ## __VA_ARGS__); \ + })) + +/** + * C_CC_MACRO2() - Provide safe environment to a macro + * @_call: Macro to call + * @_x1: First argument + * @_x2: Second argument + * @...: Further arguments to forward unmodified to ``_call`` + * + * This is the 2-argument equivalent of :c:macro:`C_CC_MACRO1()`. + * + * Return: Result of ``_call`` is returned. + */ +#define C_CC_MACRO2(_call, _x1, _x2, ...) C_INTERNAL_CC_MACRO2(_call, __COUNTER__, (_x1), __COUNTER__, (_x2), ## __VA_ARGS__) +#define C_INTERNAL_CC_MACRO2(_call, _x1q, _x1, _x2q, _x2, ...) \ + __builtin_choose_expr( \ + (__builtin_constant_p(_x1) && __builtin_constant_p(_x2)), \ + _call((_x1), (_x2), ## __VA_ARGS__), \ + __extension__ ({ \ + const __auto_type C_VAR(X1, _x1q) = (_x1); \ + const __auto_type C_VAR(X2, _x2q) = (_x2); \ + _call(C_VAR(X1, _x1q), C_VAR(X2, _x2q), ## __VA_ARGS__); \ + })) + +/** + * C_CC_MACRO3() - Provide safe environment to a macro + * @_call: Macro to call + * @_x1: First argument + * @_x2: Second argument + * @_x3: Third argument + * @...: Further arguments to forward unmodified to ``_call`` + * + * This is the 3-argument equivalent of :c:macro:`C_CC_MACRO1()`. + * + * Return: Result of ``_call`` is returned. + */ +#define C_CC_MACRO3(_call, _x1, _x2, _x3, ...) C_INTERNAL_CC_MACRO3(_call, __COUNTER__, (_x1), __COUNTER__, (_x2), __COUNTER__, (_x3), ## __VA_ARGS__) +#define C_INTERNAL_CC_MACRO3(_call, _x1q, _x1, _x2q, _x2, _x3q, _x3, ...) \ + __builtin_choose_expr( \ + (__builtin_constant_p(_x1) && __builtin_constant_p(_x2) && __builtin_constant_p(_x3)), \ + _call((_x1), (_x2), (_x3), ## __VA_ARGS__), \ + __extension__ ({ \ + const __auto_type C_VAR(X1, _x1q) = (_x1); \ + const __auto_type C_VAR(X2, _x2q) = (_x2); \ + const __auto_type C_VAR(X3, _x3q) = (_x3); \ + _call(C_VAR(X1, _x1q), C_VAR(X2, _x2q), C_VAR(X3, _x3q), ## __VA_ARGS__); \ + })) + +/** + * DOC: Standard Library Utilities + * + * The C Standard Library lacks some crucial and basic support functions. This + * section describes the set of helpers provided as extension to the standard + * library. + */ +/**/ + +/** + * C_ARRAY_SIZE() - Calculate number of array elements at compile time + * @_x: Array to calculate size of + * + * Return: Evaluates to a constant integer expression. + */ +#define C_ARRAY_SIZE(_x) \ + C_EXPR_ASSERT(sizeof(_x) / sizeof((_x)[0]), \ + /* \ + * Verify that `_x' is an array, not a pointer. Rely on \ + * `&_x[0]' degrading arrays to pointers. \ + */ \ + !__builtin_types_compatible_p( \ + __typeof__(_x), \ + __typeof__(&(*(__typeof__(_x)*)0)[0]) \ + ), \ + "C_ARRAY_SIZE() called with non-array argument" \ + ) + +/** + * C_DECIMAL_MAX() - Calculate maximum length of a decimal representation + * @_type: Integer variable/type + * + * This calculates the bytes required for the decimal representation of an + * integer of the given type. It accounts for a possible +/- prefix, but it + * does *NOT* include the trailing terminating zero byte. + * + * Return: Evaluates to a constant integer expression + */ +#define C_DECIMAL_MAX(_arg) \ + (_Generic((__typeof__(_arg)){ 0 }, \ + char: C_INTERNAL_DECIMAL_MAX(sizeof(char)), \ + signed char: C_INTERNAL_DECIMAL_MAX(sizeof(signed char)), \ + unsigned char: C_INTERNAL_DECIMAL_MAX(sizeof(unsigned char)), \ + signed short: C_INTERNAL_DECIMAL_MAX(sizeof(signed short)), \ + unsigned short: C_INTERNAL_DECIMAL_MAX(sizeof(unsigned short)), \ + signed int: C_INTERNAL_DECIMAL_MAX(sizeof(signed int)), \ + unsigned int: C_INTERNAL_DECIMAL_MAX(sizeof(unsigned int)), \ + signed long: C_INTERNAL_DECIMAL_MAX(sizeof(signed long)), \ + unsigned long: C_INTERNAL_DECIMAL_MAX(sizeof(unsigned long)), \ + signed long long: C_INTERNAL_DECIMAL_MAX(sizeof(signed long long)), \ + unsigned long long: C_INTERNAL_DECIMAL_MAX(sizeof(unsigned long long)))) +#define C_INTERNAL_DECIMAL_MAX(_bytes) \ + C_EXPR_ASSERT( \ + 1 + ((_bytes) <= 1 ? 3 : \ + (_bytes) <= 2 ? 5 : \ + (_bytes) <= 4 ? 10 : \ + 20), \ + (_bytes) <= 8, \ + "Invalid use of C_INTERNAL_DECIMAL_MAX()" \ + ) + +/** + * c_container_of() - Cast a member of a structure out to the containing type + * @_ptr: Pointer to the member or NULL + * @_type: Type of the container struct this is embedded in + * @_member: Name of the member within the struct + * + * This uses ``offsetof(3)`` to turn a pointer to a structure-member into a + * pointer to the surrounding structure. + * + * Return: Pointer to the surrounding object. + */ +#define c_container_of(_ptr, _type, _member) C_CC_MACRO1(C_CONTAINER_OF, (_ptr), _type, _member) +#define C_CONTAINER_OF(_ptr, _type, _member) \ + C_EXPR_ASSERT( \ + (_ptr ? (_type*)c_internal_container_of((void *)_ptr, offsetof(_type, _member)) : NULL), \ + __builtin_types_compatible_p( \ + __typeof__(*(_ptr)), \ + __typeof__(((_type){})._member) \ + ) || __builtin_types_compatible_p( \ + __typeof__(_ptr), \ + __typeof__(NULL) \ + ), \ + "Invalid use of C_CONTAINER_OF()" \ + ) +static inline void *c_internal_container_of(void *ptr, size_t offset) { + /* + * Arithmetic on NULL is UB, even if in dead-code. Hide it in a proper + * C function, so the macro never emits it as code. + */ + return (char *)ptr - offset; +} + +/** + * c_max() - Compute maximum of two values + * @_a: Value A + * @_b: Value B + * + * Calculate the maximum of both passed values. Both arguments are evaluated + * exactly once, under all circumstances. Furthermore, if both values are + * constant expressions, the result will be constant as well. + * + * The comparison of their values is performed with the types given by the + * caller. It is the caller's responsibility to convert them to suitable types + * if necessary. + * + * Return: Maximum of both values is returned. + */ +#define c_max(_a, _b) C_CC_MACRO2(C_MAX, (_a), (_b)) +#define C_MAX(_a, _b) ((_a) > (_b) ? (_a) : (_b)) + +/** + * c_min() - Compute minimum of two values + * @_a: Value A + * @_b: Value B + * + * Calculate the minimum of both passed values. Both arguments are evaluated + * exactly once, under all circumstances. Furthermore, if both values are + * constant expressions, the result will be constant as well. + * + * The comparison of their values is performed with the types given by the + * caller. It is the caller's responsibility to convert them to suitable types + * if necessary. + * + * Return: Minimum of both values is returned. + */ +#define c_min(_a, _b) C_CC_MACRO2(C_MIN, (_a), (_b)) +#define C_MIN(_a, _b) ((_a) < (_b) ? (_a) : (_b)) + +/** + * c_less_by() - Calculate clamped difference of two values + * @_a: Minuend + * @_b: Subtrahend + * + * Calculate ``_a - _b``, but clamp the result to 0. Both arguments are + * evaluated exactly once, under all circumstances. Furthermore, if both values + * are constant expressions, the result will be constant as well. + * + * The comparison of their values is performed with the types given by the + * caller. It is the caller's responsibility to convert them to suitable types + * if necessary. + * + * Return: This computes ``_a - _b``, if ``_a > _b``. Otherwise, 0 is returned. + */ +#define c_less_by(_a, _b) C_CC_MACRO2(C_LESS_BY, (_a), (_b)) +#define C_LESS_BY(_a, _b) ((_a) > (_b) ? (_a) - (_b) : 0) + +/** + * c_clamp() - Clamp value to lower and upper boundary + * @_x: Value to clamp + * @_low: Lower boundary + * @_high: Higher boundary + * + * This clamps ``_x`` to the lower and higher bounds given as ``_low`` and + * ``_high``. All arguments are evaluated exactly once, and yield a constant + * expression if all arguments are constant as well. + * + * The comparison of their values is performed with the types given by the + * caller. It is the caller's responsibility to convert them to suitable types + * if necessary. + * + * Return: Clamped integer value. + */ +#define c_clamp(_x, _low, _high) C_CC_MACRO3(C_CLAMP, (_x), (_low), (_high)) +#define C_CLAMP(_x, _low, _high) ((_x) > (_high) ? (_high) : (_x) < (_low) ? (_low) : (_x)) + +/** + * c_div_round_up() - Calculate integer quotient but round up + * @_x: Dividend + * @_y: Divisor + * + * Calculates ``x / y`` but rounds up the result to the next integer. All + * arguments are evaluated exactly once, and yield a constant expression if all + * arguments are constant. + * + * **Note:** + * ``(x + y - 1) / y`` suffers from an integer overflow, even though the + * computation should be possible in the given type. Therefore, we use + * ``x / y + !!(x % y)``. Note that on most CPUs a division returns both the + * quotient and the remainder, so both should be equally fast. Furthermore, if + * the divisor is a power of two, the compiler will optimize it, anyway. + * + * The operationsare performed with the types given by the caller. It is the + * caller's responsibility to convert the arguments to suitable types if + * necessary. + * + * Return: The quotient is returned. + */ +#define c_div_round_up(_x, _y) C_CC_MACRO2(C_DIV_ROUND_UP, (_x), (_y)) +#define C_DIV_ROUND_UP(_x, _y) ((_x) / (_y) + !!((_x) % (_y))) + +/** + * c_align_to() - Align value to a multiple + * @_val: Value to align + * @_to: Align to multiple of this + * + * This aligns ``_val`` to a multiple of ``_to``. If ``_val`` is already a + * multiple of ``_to``, ``_val`` is returned unchanged. This function operates + * within the boundaries of the type of ``_val`` and ``_to``. Make sure to cast + * them if needed. + * + * The arguments of this macro are evaluated exactly once. If both arguments + * are a constant expression, this also yields a constant return value. + * + * Note that ``_to`` must be a power of 2, otherwise the behavior will not + * match expectations. + * + * Return: ``_val`` aligned to a multiple of ``_to``. + */ +#define c_align_to(_val, _to) C_CC_MACRO2(C_ALIGN_TO, (_val), (_to)) +#define C_ALIGN_TO(_val, _to) (((_val) + (_to) - 1) & ~((_to) - 1)) + +#ifdef __cplusplus +} +#endif diff --git a/src/c-stdaux-unix.h b/src/c-stdaux-unix.h new file mode 100644 index 0000000000..b3699ac6e5 --- /dev/null +++ b/src/c-stdaux-unix.h @@ -0,0 +1,102 @@ +#pragma once + +/* + * c-stdaux-unix: Unix-specific auxiliary macros and functions + * + * This header contains all unix-specific features of c-stdaux.h, usually only + * available on unix-like platforms. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +/* Documented alongside target properties. */ +#define C_MODULE_UNIX 1 + +/** + * DOC: Guaranteed Unix Includes + * + * c-stdaux-unix includes a set of Unix headers. All those includes are + * guaranteed and part of the API. See the actual header for a comprehensive + * list. + */ +/**/ + +#include +#include +#include +#include +#include + +/** + * DOC: Common Unix Destructors + * + * A set of destructors is provided which extends standard library destructors + * to adhere to some adjuvant rules. In particular, they return an invalid + * value of the particular object, rather than void. This allows direct + * assignment to any member-field and/or variable they are defined in, like: + * + * .. code-block:: c + * + * foo->bar = c_close(foo->bar); + * + * Furthermore, all those destructors can be safely called with the "INVALID" + * value as argument, and they will be a no-op. + */ +/**/ + +/** + * c_close() - Destructor-wrapper for close() + * @fd: File-descriptor to pass to destructor, or negative value + * + * Wrapper around ``close()``, but a no-op if a negative value is provided. + * Always returns ``-1``. + * + * Return: -1 is returned. + */ +static inline int c_close(int fd) { + if (fd >= 0) + close(fd); + return -1; +} + +/** + * c_closedir() - Destructor-wrapper for closedir() + * @d: Directory handle to pass to destructor, or NULL + * + * Wrapper around ``closedir()``, but a no-op if ``NULL`` is passed. Always + * returns ``NULL``. + * + * Return: NULL is returned. + */ +static inline DIR *c_closedir(DIR *d) { + if (d) + closedir(d); + return NULL; +} + +/** + * DOC: Common Cleanup Helpers + * + * A set of helpers that aid in creating functions suitable for use with + * :c:macro:`_c_cleanup_()`. Furthermore, a collection of predefined cleanup + * functions of a set of standard library objects ready for use with + * :c:macro:`_c_cleanup_()`. + * Those cleanup helpers are always suffixed with a ``p``. + * + * The helpers that are provided are: + * + * - ``c_closep()``: Wrapper around :c:func:`c_close()`. + * - ``c_closedirp()``: Wrapper around :c:func:`c_closedir()`. + */ +/**/ + +C_DEFINE_DIRECT_CLEANUP(int, c_close); +C_DEFINE_CLEANUP(DIR *, c_closedir); + +#ifdef __cplusplus +} +#endif diff --git a/src/c-stdaux.h b/src/c-stdaux.h index 80db7f9a41..0eccb20fde 100644 --- a/src/c-stdaux.h +++ b/src/c-stdaux.h @@ -40,795 +40,15 @@ extern "C" { */ /**/ -/** - * DOC: Guaranteed Includes - * - * The ``c-stdaux.h`` header includes a set of C Standard Library headers as - * well as UNIX headers. All those includes are guaranteed and part of the API. - * See the actual header for a comprehensive list. - */ -/**/ +#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/** - * DOC: Compiler Attributes - * - * The GCC compiler uses the ``__attribute__((__xyz__()))`` syntax to annotate - * language entities with special attributes. Aliases are provided by this - * header which map one-to-one to the respective compiler attributes. - * - * These attributes are not supported by all compilers, but are always provided - * by this header. They are pre-processor macros and do not affect the - * compilation, unless used. Note that most compilers support these, not just - * GCC. - */ -/**/ - -/** - * _c_cleanup_() - Cleanup attribute - * @_x: Cleanup function to use - * - * Alias for ``__attribute__((__cleanup__(_x)))``. - */ -#define _c_cleanup_(_x) __attribute__((__cleanup__(_x))) - -/** - * _c_const_() - Const attribute - * - * Alias for ``__attribute__((__const__))``. - */ -#define _c_const_ __attribute__((__const__)) - -/** - * _c_deprecated_() - Deprecated attribute - * - * Alias for ``__attribute__((__deprecated__))``. - */ -#define _c_deprecated_ __attribute__((__deprecated__)) - -/** - * _c_hidden_() - Hidden attribute - * - * Alias for ``__attribute__((__visibility__("hidden")))``. - */ -#define _c_hidden_ __attribute__((__visibility__("hidden"))) - -/** - * _c_packed_() - Packed attribute - * - * Alias for ``__attribute__((__packed__))``. - */ -#define _c_packed_ __attribute__((__packed__)) - -/** - * _c_printf_() - Printf attribute - * @_a: Format expression argument index - * @_b: First format-parameter argument index - * - * Alias for ``__attribute__((__format__(printf, _a, _b)))``. - */ -#define _c_printf_(_a, _b) __attribute__((__format__(printf, _a, _b))) - -/** - * _c_public_() - Public attribute - * - * Alias for ``__attribute__((__visibility__("default")))``. - */ -#define _c_public_ __attribute__((__visibility__("default"))) - -/** - * _c_pure_() - Pure attribute - * - * Alias for ``__attribute__((__pure__))``. - */ -#define _c_pure_ __attribute__((__pure__)) - -/** - * _c_sentinel_() - Sentinel attribute - * - * Alias for ``__attribute__((__sentinel__))``. - */ -#define _c_sentinel_ __attribute__((__sentinel__)) - -/** - * _c_unused_() - Unused attribute - * - * Alias for ``__attribute__((__unused__))``. - */ -#define _c_unused_ __attribute__((__unused__)) - -/** - * DOC: Compiler Intrinsics - * - * Aliases for common compiler extensions and intrinsics are provided similar - * to the compiler attributes. They are pure preprocessor aliases and do not - * affect compilation unless used. - */ -/**/ - -/** - * _c_likely_() - Likely attribute - * @_x: Expression to evaluate - * - * Alias for ``__builtin_expect(!!(_x), 1)``. - * - * Return: The expression ``_x`` is evaluated and returned. - */ -#define _c_likely_(_x) (__builtin_expect(!!(_x), 1)) - -/** - * _c_unlikely_() - Unlikely attribute - * @_x: Expression to evaluate - * - * Alias for ``__builtin_expect(!!(_x), 0)``. - * - * Return: The expression ``_x`` is evaluated and returned. - */ -#define _c_unlikely_(_x) (__builtin_expect(!!(_x), 0)) - -/** - * DOC: Utility Macros - * - * A set of utility macros is provided which aids in creating safe macros - * suitable for use in other pre-processor statements as well as in C - * expressions. - */ -/**/ - -/** - * C_EXPR_ASSERT() - Create expression with assertion - * @_expr: Expression to evaluate to - * @_assertion: Arbitrary assertion - * @_message: Message associated with the assertion - * - * This macro simply evaluates to ``_expr``. That is, it can be used in any - * context that expects an expression like ``_expr``. Additionally, it takes an - * assertion as ``_assertion`` and evaluates it through ``_Static_assert()``, - * using ``_message`` as debug message. - * - * The ``_Static_assert()`` builtin of C11 is defined as statement and thus - * cannot be used in expressions. This macro circumvents this restriction. - * - * Return: Evaluates to ``_expr``. - */ -#define C_EXPR_ASSERT(_expr, _assertion, _message) C_INTERNAL_EXPR_ASSERT((_expr), (_assertion), _message) -#if defined(__COVERITY__) // Coverity cannot const-fold __builtin_choose_expr() -# define C_INTERNAL_EXPR_ASSERT(_expr, _assertion, _message) (_expr) -#else -# define C_INTERNAL_EXPR_ASSERT(_expr, _assertion, _message) \ - /* indentation and line-split to get better diagnostics */ \ - (__builtin_choose_expr( \ - !!(1 + 0 * sizeof( \ - struct { \ -_Static_assert(_assertion, _message); \ - } \ - )), \ - (_expr), \ - ((void)0) \ - )) +#if defined(C_COMPILER_GNUC) +# include #endif -/** - * C_STRINGIFY() - Stringify a token, but evaluate it first - * @_x: Token to evaluate and stringify - * - * Return: Evaluates to a constant string literal - */ -#define C_STRINGIFY(_x) C_INTERNAL_STRINGIFY(_x) -#define C_INTERNAL_STRINGIFY(_x) #_x - -/** - * C_CONCATENATE() - Concatenate two tokens, but evaluate them first - * @_x: First token - * @_y: Second token - * - * Return: Evaluates to a constant identifier - */ -#define C_CONCATENATE(_x, _y) C_INTERNAL_CONCATENATE(_x, _y) -#define C_INTERNAL_CONCATENATE(_x, _y) _x ## _y - -/** - * C_EXPAND() - Expand a tuple to a series of its values - * @_x: Tuple to expand - * - * Return: Evaluates to the expanded tuple - */ -#define C_EXPAND(_x) C_INTERNAL_EXPAND _x -#define C_INTERNAL_EXPAND(...) __VA_ARGS__ - -/** - * C_VAR() - Generate unique variable name - * @_x: Name of variable, optional - * @_uniq: Unique prefix, usually provided by ``__COUNTER__``, optional - * - * This macro shall be used to generate unique variable names, that will not be - * shadowed by recursive macro invocations. It is effectively a - * :c:macro:`C_CONCATENATE` of both arguments, but also provides a globally - * separated prefix and makes the code better readable. - * - * The second argument is optional. If not given, ``__LINE__`` is implied, and - * as such the macro will generate the same identifier if used multiple times - * on the same code-line (or within a macro). This should be used if recursive - * calls into the macro are not expected. In fact, no argument is necessary in - * this case, as a mere ``C_VAR`` will evaluate to a valid variable name. - * - * This helper may be used by macro implementations that might reasonable well - * be called in a stacked fasion, like: - * - * .. code-block:: c - * - * c_max(foo, c_max(bar, baz)) - * - * Such a stacked call of :c:macro:`c_max()` might cause compiler warnings of - * shadowed variables in the definition of :c:macro:`c_max()`. By using - * ``C_VAR()``, such warnings can be silenced as each evaluation of - * :c:macro:`c_max()` uses unique variable names. - * - * Return: This evaluates to a constant identifier. - */ -#define C_VAR(...) C_INTERNAL_VAR(__VA_ARGS__, 2, 1) -#define C_INTERNAL_VAR(_x, _uniq, _num, ...) C_VAR ## _num (_x, _uniq) -#define C_VAR1(_x, _unused) C_VAR2(_x, C_CONCATENATE(line, __LINE__)) -#define C_VAR2(_x, _uniq) C_CONCATENATE(c_internal_var_unique_, C_CONCATENATE(_uniq, _x)) - -/** - * C_CC_MACRO1() - Provide safe environment to a macro - * @_call: Macro to call - * @_x1: First argument - * @...: Further arguments to forward unmodified to ``_call`` - * - * This function simplifies the implementation of macros. Whenever you - * implement a macro, provide the internal macro name as ``_call`` and its - * argument as ``_x1``. Inside of your internal macro, you... - * - * - are safe against multiple evaluation errors, since ``C_CC_MACRO1`` - * will store the initial parameters in temporary variables. - * - * - support constant folding, as ``C_CC_MACRO1`` takes care to invoke your - * macro with the original values, if they are compile-time constant. - * - * - have unique variable names for recursive callers and will not run into - * variable-shadowing-warnings accidentally. - * - * - have properly typed arguments as ``C_CC_MACRO1`` stores the original - * arguments in an ``__auto_type`` temporary variable. - * - * Return: Result of ``_call`` is returned. - */ -#define C_CC_MACRO1(_call, _x1, ...) C_INTERNAL_CC_MACRO1(_call, __COUNTER__, (_x1), ## __VA_ARGS__) -#define C_INTERNAL_CC_MACRO1(_call, _x1q, _x1, ...) \ - __builtin_choose_expr( \ - __builtin_constant_p(_x1), \ - _call(_x1, ## __VA_ARGS__), \ - __extension__ ({ \ - const __auto_type C_VAR(X1, _x1q) = (_x1); \ - _call(C_VAR(X1, _x1q), ## __VA_ARGS__); \ - })) - -/** - * C_CC_MACRO2() - Provide safe environment to a macro - * @_call: Macro to call - * @_x1: First argument - * @_x2: Second argument - * @...: Further arguments to forward unmodified to ``_call`` - * - * This is the 2-argument equivalent of :c:macro:`C_CC_MACRO1()`. - * - * Return: Result of ``_call`` is returned. - */ -#define C_CC_MACRO2(_call, _x1, _x2, ...) C_INTERNAL_CC_MACRO2(_call, __COUNTER__, (_x1), __COUNTER__, (_x2), ## __VA_ARGS__) -#define C_INTERNAL_CC_MACRO2(_call, _x1q, _x1, _x2q, _x2, ...) \ - __builtin_choose_expr( \ - (__builtin_constant_p(_x1) && __builtin_constant_p(_x2)), \ - _call((_x1), (_x2), ## __VA_ARGS__), \ - __extension__ ({ \ - const __auto_type C_VAR(X1, _x1q) = (_x1); \ - const __auto_type C_VAR(X2, _x2q) = (_x2); \ - _call(C_VAR(X1, _x1q), C_VAR(X2, _x2q), ## __VA_ARGS__); \ - })) - -/** - * C_CC_MACRO3() - Provide safe environment to a macro - * @_call: Macro to call - * @_x1: First argument - * @_x2: Second argument - * @_x3: Third argument - * @...: Further arguments to forward unmodified to ``_call`` - * - * This is the 3-argument equivalent of :c:macro:`C_CC_MACRO1()`. - * - * Return: Result of ``_call`` is returned. - */ -#define C_CC_MACRO3(_call, _x1, _x2, _x3, ...) C_INTERNAL_CC_MACRO3(_call, __COUNTER__, (_x1), __COUNTER__, (_x2), __COUNTER__, (_x3), ## __VA_ARGS__) -#define C_INTERNAL_CC_MACRO3(_call, _x1q, _x1, _x2q, _x2, _x3q, _x3, ...) \ - __builtin_choose_expr( \ - (__builtin_constant_p(_x1) && __builtin_constant_p(_x2) && __builtin_constant_p(_x3)), \ - _call((_x1), (_x2), (_x3), ## __VA_ARGS__), \ - __extension__ ({ \ - const __auto_type C_VAR(X1, _x1q) = (_x1); \ - const __auto_type C_VAR(X2, _x2q) = (_x2); \ - const __auto_type C_VAR(X3, _x3q) = (_x3); \ - _call(C_VAR(X1, _x1q), C_VAR(X2, _x2q), C_VAR(X3, _x3q), ## __VA_ARGS__); \ - })) - -/** - * DOC: Standard Library Utilities - * - * The C Standard Library lacks some crucial and basic support functions. This - * section describes the set of helpers provided as extension to the standard - * library. - */ -/**/ - -/** - * C_ARRAY_SIZE() - Calculate number of array elements at compile time - * @_x: Array to calculate size of - * - * Return: Evaluates to a constant integer expression. - */ -#define C_ARRAY_SIZE(_x) \ - C_EXPR_ASSERT(sizeof(_x) / sizeof((_x)[0]), \ - /* \ - * Verify that `_x' is an array, not a pointer. Rely on \ - * `&_x[0]' degrading arrays to pointers. \ - */ \ - !__builtin_types_compatible_p( \ - __typeof__(_x), \ - __typeof__(&(*(__typeof__(_x)*)0)[0]) \ - ), \ - "C_ARRAY_SIZE() called with non-array argument" \ - ) - -/** - * C_DECIMAL_MAX() - Calculate maximum length of a decimal representation - * @_type: Integer variable/type - * - * This calculates the bytes required for the decimal representation of an - * integer of the given type. It accounts for a possible +/- prefix, but it - * does *NOT* include the trailing terminating zero byte. - * - * Return: Evaluates to a constant integer expression - */ -#define C_DECIMAL_MAX(_arg) \ - (_Generic((__typeof__(_arg)){ 0 }, \ - char: C_INTERNAL_DECIMAL_MAX(sizeof(char)), \ - signed char: C_INTERNAL_DECIMAL_MAX(sizeof(signed char)), \ - unsigned char: C_INTERNAL_DECIMAL_MAX(sizeof(unsigned char)), \ - signed short: C_INTERNAL_DECIMAL_MAX(sizeof(signed short)), \ - unsigned short: C_INTERNAL_DECIMAL_MAX(sizeof(unsigned short)), \ - signed int: C_INTERNAL_DECIMAL_MAX(sizeof(signed int)), \ - unsigned int: C_INTERNAL_DECIMAL_MAX(sizeof(unsigned int)), \ - signed long: C_INTERNAL_DECIMAL_MAX(sizeof(signed long)), \ - unsigned long: C_INTERNAL_DECIMAL_MAX(sizeof(unsigned long)), \ - signed long long: C_INTERNAL_DECIMAL_MAX(sizeof(signed long long)), \ - unsigned long long: C_INTERNAL_DECIMAL_MAX(sizeof(unsigned long long)))) -#define C_INTERNAL_DECIMAL_MAX(_bytes) \ - C_EXPR_ASSERT( \ - 1 + ((_bytes) <= 1 ? 3 : \ - (_bytes) <= 2 ? 5 : \ - (_bytes) <= 4 ? 10 : \ - 20), \ - (_bytes) <= 8, \ - "Invalid use of C_INTERNAL_DECIMAL_MAX()" \ - ) - -/** - * c_container_of() - Cast a member of a structure out to the containing type - * @_ptr: Pointer to the member or NULL - * @_type: Type of the container struct this is embedded in - * @_member: Name of the member within the struct - * - * This uses ``offsetof(3)`` to turn a pointer to a structure-member into a - * pointer to the surrounding structure. - * - * Return: Pointer to the surrounding object. - */ -#define c_container_of(_ptr, _type, _member) C_CC_MACRO1(C_CONTAINER_OF, (_ptr), _type, _member) -#define C_CONTAINER_OF(_ptr, _type, _member) \ - C_EXPR_ASSERT( \ - (_ptr ? (_type*)c_internal_container_of((void *)_ptr, offsetof(_type, _member)) : NULL), \ - __builtin_types_compatible_p( \ - __typeof__(*(_ptr)), \ - __typeof__(((_type){})._member) \ - ) || __builtin_types_compatible_p( \ - __typeof__(_ptr), \ - __typeof__(NULL) \ - ), \ - "Invalid use of C_CONTAINER_OF()" \ - ) -static inline void *c_internal_container_of(void *ptr, size_t offset) { - /* - * Arithmetic on NULL is UB, even if in dead-code. Hide it in a proper - * C function, so the macro never emits it as code. - */ - return (char *)ptr - offset; -} - -/** - * c_max() - Compute maximum of two values - * @_a: Value A - * @_b: Value B - * - * Calculate the maximum of both passed values. Both arguments are evaluated - * exactly once, under all circumstances. Furthermore, if both values are - * constant expressions, the result will be constant as well. - * - * The comparison of their values is performed with the types given by the - * caller. It is the caller's responsibility to convert them to suitable types - * if necessary. - * - * Return: Maximum of both values is returned. - */ -#define c_max(_a, _b) C_CC_MACRO2(C_MAX, (_a), (_b)) -#define C_MAX(_a, _b) ((_a) > (_b) ? (_a) : (_b)) - -/** - * c_min() - Compute minimum of two values - * @_a: Value A - * @_b: Value B - * - * Calculate the minimum of both passed values. Both arguments are evaluated - * exactly once, under all circumstances. Furthermore, if both values are - * constant expressions, the result will be constant as well. - * - * The comparison of their values is performed with the types given by the - * caller. It is the caller's responsibility to convert them to suitable types - * if necessary. - * - * Return: Minimum of both values is returned. - */ -#define c_min(_a, _b) C_CC_MACRO2(C_MIN, (_a), (_b)) -#define C_MIN(_a, _b) ((_a) < (_b) ? (_a) : (_b)) - -/** - * c_less_by() - Calculate clamped difference of two values - * @_a: Minuend - * @_b: Subtrahend - * - * Calculate ``_a - _b``, but clamp the result to 0. Both arguments are - * evaluated exactly once, under all circumstances. Furthermore, if both values - * are constant expressions, the result will be constant as well. - * - * The comparison of their values is performed with the types given by the - * caller. It is the caller's responsibility to convert them to suitable types - * if necessary. - * - * Return: This computes ``_a - _b``, if ``_a > _b``. Otherwise, 0 is returned. - */ -#define c_less_by(_a, _b) C_CC_MACRO2(C_LESS_BY, (_a), (_b)) -#define C_LESS_BY(_a, _b) ((_a) > (_b) ? (_a) - (_b) : 0) - -/** - * c_clamp() - Clamp value to lower and upper boundary - * @_x: Value to clamp - * @_low: Lower boundary - * @_high: Higher boundary - * - * This clamps ``_x`` to the lower and higher bounds given as ``_low`` and - * ``_high``. All arguments are evaluated exactly once, and yield a constant - * expression if all arguments are constant as well. - * - * The comparison of their values is performed with the types given by the - * caller. It is the caller's responsibility to convert them to suitable types - * if necessary. - * - * Return: Clamped integer value. - */ -#define c_clamp(_x, _low, _high) C_CC_MACRO3(C_CLAMP, (_x), (_low), (_high)) -#define C_CLAMP(_x, _low, _high) ((_x) > (_high) ? (_high) : (_x) < (_low) ? (_low) : (_x)) - -/** - * c_div_round_up() - Calculate integer quotient but round up - * @_x: Dividend - * @_y: Divisor - * - * Calculates ``x / y`` but rounds up the result to the next integer. All - * arguments are evaluated exactly once, and yield a constant expression if all - * arguments are constant. - * - * **Note:** - * ``(x + y - 1) / y`` suffers from an integer overflow, even though the - * computation should be possible in the given type. Therefore, we use - * ``x / y + !!(x % y)``. Note that on most CPUs a division returns both the - * quotient and the remainder, so both should be equally fast. Furthermore, if - * the divisor is a power of two, the compiler will optimize it, anyway. - * - * The operationsare performed with the types given by the caller. It is the - * caller's responsibility to convert the arguments to suitable types if - * necessary. - * - * Return: The quotient is returned. - */ -#define c_div_round_up(_x, _y) C_CC_MACRO2(C_DIV_ROUND_UP, (_x), (_y)) -#define C_DIV_ROUND_UP(_x, _y) ((_x) / (_y) + !!((_x) % (_y))) - -/** - * c_align_to() - Align value to a multiple - * @_val: Value to align - * @_to: Align to multiple of this - * - * This aligns ``_val`` to a multiple of ``_to``. If ``_val`` is already a - * multiple of ``_to``, ``_val`` is returned unchanged. This function operates - * within the boundaries of the type of ``_val`` and ``_to``. Make sure to cast - * them if needed. - * - * The arguments of this macro are evaluated exactly once. If both arguments - * are a constant expression, this also yields a constant return value. - * - * Note that ``_to`` must be a power of 2, otherwise the behavior will not - * match expectations. - * - * Return: ``_val`` aligned to a multiple of ``_to``. - */ -#define c_align_to(_val, _to) C_CC_MACRO2(C_ALIGN_TO, (_val), (_to)) -#define C_ALIGN_TO(_val, _to) (((_val) + (_to) - 1) & ~((_to) - 1)) - -/** - * c_assert() - Runtime assertions - * @expr_result: Result of an expression - * - * This function behaves like the standard ``assert(3)`` macro. That is, if - * ``NDEBUG`` is defined, it is a no-op. In all other cases it will assert that - * the result of the passed expression is true. - * - * Unlike the standard ``assert(3)`` macro, this function always evaluates its - * argument. This means side-effects will always be evaluated! However, if the - * macro is used with constant expressions, the compiler will be able to - * optimize it away. - */ -#define c_assert(_x) ({ \ - const _c_unused_ bool c_assert_result = (_x); \ - assert(c_assert_result && #_x); \ - }) - -/** - * c_errno() - Return valid errno - * - * This helper should be used to shut up gcc if you know ``errno`` is valid - * (ie., ``errno`` is greater than 0). Instead of ``return -errno;``, use - * ``return -c_errno();`` It will suppress bogus gcc warnings in case it - * assumes ``errno`` might be 0 (or smaller than 0) and thus the caller's - * error-handling might not be triggered. - * - * This helper should be avoided whenever possible. However, occasionally we - * really want to shut up gcc (especially with static/inline functions). In - * those cases, gcc usually cannot deduce that some error paths are guaranteed - * to be taken. Hence, making the return value explicit allows gcc to better - * optimize the code. - * - * Note that you really should never use this helper to work around broken libc - * calls or syscalls, not setting 'errno' correctly. - * - * Return: Positive error code is returned. - */ -static inline int c_errno(void) { - return _c_likely_(errno > 0) ? errno : ENOTRECOVERABLE; -} - -/** - * c_memset() - Fill memory region with constant byte - * @p: Pointer to memory region, if non-empty - * @c: Value to fill with - * @n: Size of the memory region in bytes - * - * This function works like ``memset(3)`` if ``n`` is non-zero. If ``n`` is - * zero, this function is a no-op. Therefore, unlike ``memset(3)`` it is safe - * to call this function with ``NULL`` as ``p`` if ``n`` is 0. - * - * Return: ``p`` is returned. - */ -static inline void *c_memset(void *p, int c, size_t n) { - if (n > 0) - memset(p, c, n); - return p; -} - -/** - * c_memzero() - Clear memory area - * @p: Pointer to memory region, if non-empty - * @n: Size of the memory region in bytes - * - * Clear a memory area to 0. If the memory area is empty, this is a no-op. - * Similar to ``c_memset()``, this function allows ``p`` to be ``NULL`` if the - * area is empty. - * - * Return: ``p`` is returned. - */ -static inline void *c_memzero(void *p, size_t n) { - return c_memset(p, 0, n); -} - -/** - * c_memcpy() - Copy memory area - * @dst: Pointer to target area - * @src: Pointer to source area - * @n: Length of area to copy - * - * Copy the memory of size ``n`` from ``src`` to ``dst``, just as ``memcpy(3)`` - * does, except this function allows either to be ``NULL`` if ``n`` is zero. In - * the latter case, the operation is a no-op. - * - * Return: ``p`` is returned. - */ -static inline void *c_memcpy(void *dst, const void *src, size_t n) { - if (n > 0) - memcpy(dst, src, n); - return dst; -} - -/** - * c_memcmp() - Compare memory areas - * @s1: Pointer to one area - * @s2: Pointer to other area - * @n: Length of area to compare - * - * Compare the memory of size ``n`` of ``s1`` and ``s2``, just as ``memcmp(3)`` - * does, except this function allows either to be ``NULL`` if ``n`` is zero. - * - * Return: Comparison result for ordering is returned. - */ -static inline int c_memcmp(const void *s1, const void *s2, size_t n) { - if (n > 0) - return memcmp(s1, s2, n); - return 0; -} - -/** - * DOC: Common Destructors - * - * A set of destructors is provided which extends standard library destructors - * to adhere to some adjuvant rules. In particular, they return an invalid - * value of the particular object, rather than void. This allows direct - * assignment to any member-field and/or variable they are defined in, like: - * - * .. code-block:: c - * - * foo = c_free(foo); - * foo->bar = c_close(foo->bar); - * - * Furthermore, all those destructors can be safely called with the "INVALID" - * value as argument, and they will be a no-op. - */ -/**/ - -/** - * c_free() - Destructor-wrapper for free() - * @p: Value to pass to destructor, or NULL - * - * Wrapper around ``free()``, but always returns ``NULL``. - * - * Return: NULL is returned. - */ -static inline void *c_free(void *p) { - free(p); - return NULL; -} - -/** - * c_close() - Destructor-wrapper for close() - * @fd: File-descriptor to pass to destructor, or negative value - * - * Wrapper around ``close()``, but a no-op if a negative value is provided. - * Always returns ``-1``. - * - * Return: -1 is returned. - */ -static inline int c_close(int fd) { - if (fd >= 0) - close(fd); - return -1; -} - -/** - * c_fclose() - Destructor-wrapper for fclose() - * @f: File handle to pass to destructor, or NULL - * - * Wrapper around ``fclose()``, but a no-op if ``NULL`` is passed. Always - * returns ``NULL``. - * - * Return: NULL is returned. - */ -static inline FILE *c_fclose(FILE *f) { - if (f) - fclose(f); - return NULL; -} - -/** - * c_closedir() - Destructor-wrapper for closedir) - * @d: Directory handle to pass to destructor, or NULL - * - * Wrapper around ``closedir()``, but a no-op if ``NULL`` is passed. Always - * returns ``NULL``. - * - * Return: NULL is returned. - */ -static inline DIR *c_closedir(DIR *d) { - if (d) - closedir(d); - return NULL; -} - -/** - * DOC: Common Cleanup Helpers - * - * A set of helpers that aid in creating functions suitable for use with - * :c:macro:`_c_cleanup_()`. Furthermore, a collection of predefined cleanup - * functions of a set of standard library objects ready for use with - * :c:macro:`_c_cleanup_()`. - * Those cleanup helpers are always suffixed with a ``p``. - * - * The helpers that are provided are: - * - * - ``c_freep()``: Wrapper around :c:func:`c_free()`. - * - ``c_closep()``: Wrapper around :c:func:`c_close()`. - * - ``c_fclosep()``: Wrapper around :c:func:`c_fclose()`. - * - ``c_closedirp()``: Wrapper around :c:func:`c_closedir()`. - */ -/**/ - -/** - * C_DEFINE_CLEANUP() - Define cleanup helper - * @_type: Type of object to cleanup - * @_func: Destructor of the respective type - * - * Define a C static inline function that takes a single argument of type - * `_type` and calls `_func` on it, if its dereferenced value of its argument - * evaluates to true. Otherwise, it is a no-op. - * - * This macro allows for very simple and fast creation of cleanup helpers for - * use with ``_c_cleanup_()``, based on any destructor and type you provide to - * it. - */ -#define C_DEFINE_CLEANUP(_type, _func) \ - static inline void _func ## p(_type *p) { \ - if (*p) \ - _func(*p); \ - } struct c_internal_trailing_semicolon - -/** - * C_DEFINE_DIRECT_CLEANUP() - Define direct cleanup helper - * @_type: Type of object to cleanup - * @_func: Destructor of the respective type - * - * This works like :c:macro:`C_DEFINE_CLEANUP()` but does not check the - * dereferenced value of its argument. It always unconditionally invokes the - * destructor. - */ -#define C_DEFINE_DIRECT_CLEANUP(_type, _func) \ - static inline void _func ## p(_type *p) { \ - _func(*p); \ - } struct c_internal_trailing_semicolon - -static inline void c_freep(void *p) { - /* - * `foobar **` does not coerce to `void **`, so we need `void *` as - * argument type, and then we dereference manually. - */ - c_free(*(void **)p); -} - -C_DEFINE_DIRECT_CLEANUP(int, c_close); -C_DEFINE_CLEANUP(FILE *, c_fclose); -C_DEFINE_CLEANUP(DIR *, c_closedir); +#if defined(C_OS_LINUX) || defined(C_OS_MACOS) +# include +#endif #ifdef __cplusplus } diff --git a/src/docs/api.rst b/src/docs/api.rst index 6cb51827ae..a25e9fadf2 100644 --- a/src/docs/api.rst +++ b/src/docs/api.rst @@ -1,5 +1,5 @@ API === -.. c:autodoc:: c-*.h +.. c:autodoc:: c-stdaux.h c-stdaux-generic.h c-stdaux-gnuc.h c-stdaux-unix.h :transform: kerneldoc diff --git a/src/docs/conf.py b/src/docs/conf.py index 65ffb00e84..b036efa563 100644 --- a/src/docs/conf.py +++ b/src/docs/conf.py @@ -31,6 +31,8 @@ extensions = [ # Hawkmoth Options cautodoc_clang = capidocs.kerneldoc.hawkmoth_include_args() +cautodoc_clang += ["-I" + os.path.abspath("..")] +cautodoc_clang += ["-DC_COMPILER_DOCS"] cautodoc_root = os.path.abspath('..') diff --git a/src/meson.build b/src/meson.build index 9d78af9c98..f0efa90b3f 100644 --- a/src/meson.build +++ b/src/meson.build @@ -16,7 +16,12 @@ libcstdaux_dep = declare_dependency( ) if not meson.is_subproject() - install_headers('c-stdaux.h') + install_headers( + 'c-stdaux.h', + 'c-stdaux-generic.h', + 'c-stdaux-gnuc.h', + 'c-stdaux-unix.h', + ) mod_pkgconfig.generate( description: project_description, diff --git a/src/test-api.c b/src/test-api.c index 44be1395a9..b2f7ab1163 100644 --- a/src/test-api.c +++ b/src/test-api.c @@ -9,42 +9,41 @@ #include #include "c-stdaux.h" -static _c_const_ int const_fn(void) { return 0; } -static _c_deprecated_ _c_unused_ int deprecated_fn(void) { return 0; } -_c_hidden_ int c_internal_hidden_fn(void); -_c_hidden_ int c_internal_hidden_fn(void) { return 0; } -static _c_printf_(1, 2) int printf_fn(const _c_unused_ char *f, ...) { return 0; } +#if defined(C_MODULE_GENERIC) + _c_public_ int c_internal_public_fn(void); _c_public_ int c_internal_public_fn(void) { return 0; } -static _c_pure_ int pure_fn(void) { return 0; } -static _c_sentinel_ int sentinel_fn(const _c_unused_ char *f, ...) { return 0; } -static _c_unused_ int unused_fn(void) { return 0; } -static void cleanup_fn(_c_unused_ int p) {} -static void direct_cleanup_fn(_c_unused_ int p) {} +static void cleanup_fn(int p) { (void)p; } +static void direct_cleanup_fn(int p) { (void)p; } C_DEFINE_CLEANUP(int, cleanup_fn); C_DEFINE_DIRECT_CLEANUP(int, direct_cleanup_fn); -static void test_api_macros(void) { - /* _c_cleanup_ */ +static void test_api_generic(void) { + /* C_COMPILER_* */ { - _c_cleanup_(c_freep) void *foo = NULL; - c_assert(!foo); +#ifdef __clang__ + c_assert(C_COMPILER_CLANG); +#endif +#ifdef __GNUC__ + c_assert(C_COMPILER_GNUC); +#endif +#ifdef _MSC_VER + c_assert(C_COMPILER_MSVC); +#endif } - /* _c_const_ */ + /* C_OS_* */ { - c_assert(!const_fn()); - } - - /* _c_deprecated_ */ - { - /* see deprecated_fn() */ - } - - /* _c_hidden_ */ - { - c_assert(!c_internal_hidden_fn()); +#ifdef __linux__ + c_assert(C_OS_LINUX); +#endif +#ifdef __APPLE__ + c_assert(C_OS_MACOS); +#endif +#ifdef _WIN32 + c_assert(C_OS_WINDOWS); +#endif } /* _c_likely_ */ @@ -52,52 +51,16 @@ static void test_api_macros(void) { c_assert(_c_likely_(true)); } - /* _c_packed_ */ - { - struct _c_packed_ FooBar { - int member; - } foobar = {}; - - c_assert(!foobar.member); - } - - /* _c_printf_ */ - { - c_assert(!printf_fn("%d", 1)); - } - /* _c_public_ */ { c_assert(!c_internal_public_fn()); } - /* _c_pure_ */ - { - c_assert(!pure_fn()); - } - - /* _c_sentinel_ */ - { - c_assert(!sentinel_fn("", NULL)); - } - /* _c_unlikely_ */ { c_assert(!_c_unlikely_(false)); } - /* _c_unused_ */ - { - c_assert(!unused_fn()); - } - - /* C_EXPR_ASSERT */ - { - int v = C_EXPR_ASSERT(0, true, ""); - - c_assert(!v); - } - /* C_STRINGIFY */ { const char v[] = C_STRINGIFY(foobar); @@ -124,6 +87,120 @@ static void test_api_macros(void) { int C_VAR = 0; c_assert(!C_VAR); /* must be on the same line */ } + /* c_assert */ + { + c_assert(true); + } + + /* C_DEFINE_CLEANUP / C_DEFINE_DIRECT_CLEANUP */ + { + int v = 0; + + cleanup_fnp(&v); + direct_cleanup_fnp(&v); + } + + /* test availability of C symbols */ + { + void *fns[] = { + (void *)c_errno, + (void *)c_memset, + (void *)c_memzero, + (void *)c_memcpy, + (void *)c_free, + (void *)c_fclose, + (void *)c_freep, + (void *)c_fclosep, + }; + size_t i; + + for (i = 0; i < sizeof(fns) / sizeof(*fns); ++i) + c_assert(!!fns[i]); + } +} + +#else /* C_MODULE_GENERIC */ + +static void test_api_generic(void) { +} + +#endif /* C_MODULE_GENERIC */ + +#if defined(C_MODULE_GNUC) + +static inline _c_always_inline_ int always_inline_fn(void) { return 0; } +static _c_const_ int const_fn(void) { return 0; } +static _c_deprecated_ _c_unused_ int deprecated_fn(void) { return 0; } +_c_hidden_ int c_internal_hidden_fn(void); +_c_hidden_ int c_internal_hidden_fn(void) { return 0; } +static _c_printf_(1, 2) int printf_fn(const _c_unused_ char *f, ...) { return 0; } +static _c_pure_ int pure_fn(void) { return 0; } +static _c_sentinel_ int sentinel_fn(const _c_unused_ char *f, ...) { return 0; } +static _c_unused_ int unused_fn(void) { return 0; } + +static void test_api_gnuc(void) { + /* _c_always_inline_ */ + { + c_assert(!always_inline_fn()); + } + + /* _c_cleanup_ */ + { + _c_cleanup_(c_freep) void *foo = NULL; + c_assert(!foo); + } + + /* _c_const_ */ + { + c_assert(!const_fn()); + } + + /* _c_deprecated_ */ + { + /* see deprecated_fn() */ + } + + /* _c_hidden_ */ + { + c_assert(!c_internal_hidden_fn()); + } + + /* _c_packed_ */ + { + struct _c_packed_ FooBar { + int member; + } foobar = {}; + + c_assert(!foobar.member); + } + + /* _c_printf_ */ + { + c_assert(!printf_fn("%d", 1)); + } + + /* _c_pure_ */ + { + c_assert(!pure_fn()); + } + + /* _c_sentinel_ */ + { + c_assert(!sentinel_fn("", NULL)); + } + + /* _c_unused_ */ + { + c_assert(!unused_fn()); + } + + /* C_EXPR_ASSERT */ + { + int v = C_EXPR_ASSERT(0, true, ""); + + c_assert(!v); + } + /* C_CC_MACRO1, C_CC_MACRO2, C_CC_MACRO3 */ { #define MACRO_REAL(_x1, _x2, _x3) ((_x1 + _x2 + _x3) * 0) @@ -171,44 +248,43 @@ static void test_api_macros(void) { { c_assert(c_align_to(0, 0) == 0); } +} - /* c_assert */ +#else /* C_MODULE_GNUC */ + +static void test_api_gnuc(void) { +} + +#endif /* C_MODULE_GNUC */ + +#if defined(C_MODULE_UNIX) + +static void test_api_unix(void) { + /* test availability of C symbols */ { - c_assert(true); - } + void *fns[] = { + (void *)c_close, + (void *)c_closedir, + (void *)c_closep, + (void *)c_closedirp, + }; + size_t i; - /* C_DEFINE_CLEANUP / C_DEFINE_DIRECT_CLEANUP */ - { - int v = 0; - - cleanup_fnp(&v); - direct_cleanup_fnp(&v); + for (i = 0; i < sizeof(fns) / sizeof(*fns); ++i) + c_assert(!!fns[i]); } } -static void test_api_functions(void) { - void *fns[] = { - (void *)c_errno, - (void *)c_memset, - (void *)c_memzero, - (void *)c_memcpy, - (void *)c_free, - (void *)c_close, - (void *)c_fclose, - (void *)c_closedir, - (void *)c_freep, - (void *)c_closep, - (void *)c_fclosep, - (void *)c_closedirp, - }; - size_t i; +#else /* C_MODULE_UNIX */ - for (i = 0; i < sizeof(fns) / sizeof(*fns); ++i) - c_assert(!!fns[i]); +static void test_api_unix(void) { } +#endif /* C_MODULE_UNIX */ + int main(void) { - test_api_macros(); - test_api_functions(); + test_api_generic(); + test_api_gnuc(); + test_api_unix(); return 0; } diff --git a/src/test-basic.c b/src/test-basic.c index 28eb458973..0cb656e0cf 100644 --- a/src/test-basic.c +++ b/src/test-basic.c @@ -9,22 +9,9 @@ #include #include "c-stdaux.h" -/* - * Tests for all remaining helpers - */ -static void test_misc(int non_constant_expr) { - int foo; - - /* - * Test the C_EXPR_ASSERT() macro to work in static and non-static - * environments, and evaluate exactly to its passed expression. - */ - { - static int v = C_EXPR_ASSERT(1, true, ""); - - c_assert(v == 1); - } +#if defined(C_MODULE_GENERIC) +static void test_basic_generic(void) { /* * Test stringify/concatenation helpers. Also make sure to test that * the passed arguments are evaluated first, before they're stringified @@ -77,13 +64,6 @@ static void test_misc(int non_constant_expr) { c_assert(sub == subUNIQUE); c_assert(sub == UNIQUEsub); } - { - /* - * Make sure both produce different names, even though they're - * exactly the same expression. - */ - _c_unused_ int C_VAR(sub, __COUNTER__), C_VAR(sub, __COUNTER__); - } { /* verify C_VAR() with single argument works line-based */ int C_VAR(sub); C_VAR(sub) = 5; c_assert(C_VAR(sub) == 5); @@ -92,182 +72,96 @@ static void test_misc(int non_constant_expr) { /* verify C_VAR() with no argument works line-based */ int C_VAR(); C_VAR() = 5; c_assert(C_VAR() == 5); } +#if defined(C_MODULE_GNUC) + { + /* + * Make sure both produce different names, even though they're + * exactly the same expression. + */ + _c_unused_ int C_VAR(sub, __COUNTER__), C_VAR(sub, __COUNTER__); + } +#endif } +#if defined(C_MODULE_GNUC) /* - * Test array-size helper. This simply computes the number of elements - * of an array, instead of the binary size. + * Verify that c_free*() works as expected. Since we want to support + * running under valgrind, there is no easy way to verify the + * correctness of free(). Hence, we simply rely on valgrind to catch + * the leaks. */ { - int bar[8]; + int i; - static_assert(C_ARRAY_SIZE(bar) == 8, ""); - c_assert(__builtin_constant_p(C_ARRAY_SIZE(bar))); - } + for (i = 0; i < 16; ++i) { + _c_cleanup_(c_freep) void *foo; + _c_cleanup_(c_freep) int **bar; /* supports any type */ + size_t sz = 128 * 1024; - /* - * Test decimal-representation calculator. Make sure it is - * type-independent and just uses the size of the type to calculate how - * many bytes are needed to print that integer in decimal form. Also - * verify that it is a constant expression. - */ - { - static_assert(C_DECIMAL_MAX(char) == 4, ""); - static_assert(C_DECIMAL_MAX(signed char) == 4, ""); - static_assert(C_DECIMAL_MAX(unsigned char) == 4, ""); - static_assert(C_DECIMAL_MAX(unsigned long) == (sizeof(long) == 8 ? 21 : 11), ""); - static_assert(C_DECIMAL_MAX(unsigned long long) == 21, ""); - static_assert(C_DECIMAL_MAX(int32_t) == 11, ""); - static_assert(C_DECIMAL_MAX(uint32_t) == 11, ""); - static_assert(C_DECIMAL_MAX(uint64_t) == 21, ""); - } + foo = malloc(sz); + c_assert(foo); - /* - * Test c_container_of(). We cannot test for type-safety, nor for - * other invalid uses, as they'd require negative compile-testing. - * However, we can test that the macro yields the correct values under - * normal use. - */ - { - struct foobar { - int a; - char b; - } sub = {}; - - c_assert(&sub == c_container_of(&sub.a, struct foobar, a)); - c_assert(&sub == c_container_of(&sub.b, struct foobar, b)); - c_assert(&sub == c_container_of((const char *)&sub.b, struct foobar, b)); - - c_assert(!c_container_of(NULL, struct foobar, b)); - } - - /* - * Test min/max macros. Especially check that macro arguments are never - * evaluated multiple times, and if both arguments are constant, the - * return value is constant as well. - */ - { - foo = 0; - c_assert(c_max(1, 5) == 5); - c_assert(c_max(-1, 5) == 5); - c_assert(c_max(-1, -5) == -1); - c_assert(c_max(foo++, -1) == 0); - c_assert(foo == 1); - c_assert(c_max(foo++, foo++) > 0); - c_assert(foo == 3); - - c_assert(__builtin_constant_p(c_max(1, 5))); - c_assert(!__builtin_constant_p(c_max(1, non_constant_expr))); - - foo = 0; - c_assert(c_min(1, 5) == 1); - c_assert(c_min(-1, 5) == -1); - c_assert(c_min(-1, -5) == -5); - c_assert(c_min(foo++, 1) == 0); - c_assert(foo == 1); - c_assert(c_min(foo++, foo++) > 0); - c_assert(foo == 3); - - c_assert(__builtin_constant_p(c_min(1, 5))); - c_assert(!__builtin_constant_p(c_min(1, non_constant_expr))); - } - - /* - * Test c_less_by(), c_clamp(). Make sure they - * evaluate arguments exactly once, and yield a constant expression, - * if all arguments are constant. - */ - { - foo = 8; - c_assert(c_less_by(1, 5) == 0); - c_assert(c_less_by(5, 1) == 4); - c_assert(c_less_by(foo++, 1) == 7); - c_assert(foo == 9); - c_assert(c_less_by(foo++, foo++) >= 0); - c_assert(foo == 11); - - c_assert(__builtin_constant_p(c_less_by(1, 5))); - c_assert(!__builtin_constant_p(c_less_by(1, non_constant_expr))); - - foo = 8; - c_assert(c_clamp(foo, 1, 5) == 5); - c_assert(c_clamp(foo, 9, 20) == 9); - c_assert(c_clamp(foo++, 1, 5) == 5); - c_assert(foo == 9); - c_assert(c_clamp(foo++, foo++, foo++) >= 0); - c_assert(foo == 12); - - c_assert(__builtin_constant_p(c_clamp(0, 1, 5))); - c_assert(!__builtin_constant_p(c_clamp(1, 0, non_constant_expr))); - } - - /* - * Div Round Up: Normal division, but round up to next integer, instead - * of clipping. Also verify that it does not suffer from the integer - * overflow in the prevalent, alternative implementation: - * [(x + y - 1) / y]. - */ - { - int i, j; - -#define TEST_ALT_DIV(_x, _y) (((_x) + (_y) - 1) / (_y)) - foo = 8; - c_assert(c_div_round_up(0, 5) == 0); - c_assert(c_div_round_up(1, 5) == 1); - c_assert(c_div_round_up(5, 5) == 1); - c_assert(c_div_round_up(6, 5) == 2); - c_assert(c_div_round_up(foo++, 1) == 8); - c_assert(foo == 9); - c_assert(c_div_round_up(foo++, foo++) >= 0); - c_assert(foo == 11); - - c_assert(__builtin_constant_p(c_div_round_up(1, 5))); - c_assert(!__builtin_constant_p(c_div_round_up(1, non_constant_expr))); - - /* alternative calculation is [(x + y - 1) / y], but it may overflow */ - for (i = 0; i <= 0xffff; ++i) { - for (j = 1; j <= 0xff; ++j) - c_assert(c_div_round_up(i, j) == TEST_ALT_DIV(i, j)); - for (j = 0xff00; j <= 0xffff; ++j) - c_assert(c_div_round_up(i, j) == TEST_ALT_DIV(i, j)); + bar = malloc(sz); + c_assert(bar); + bar = c_free(bar); + c_assert(!bar); } - /* make sure it doesn't suffer from high overflow */ - c_assert(UINT32_C(0xfffffffa) % 10 == 0); - c_assert(UINT32_C(0xfffffffa) / 10 == UINT32_C(429496729)); - c_assert(c_div_round_up(UINT32_C(0xfffffffa), 10) == UINT32_C(429496729)); - c_assert(TEST_ALT_DIV(UINT32_C(0xfffffffa), 10) == 0); /* overflow */ - - c_assert(UINT32_C(0xfffffffd) % 10 == 3); - c_assert(UINT32_C(0xfffffffd) / 10 == UINT32_C(429496729)); - c_assert(c_div_round_up(UINT32_C(0xfffffffd), 10) == UINT32_C(429496730)); - c_assert(TEST_ALT_DIV(UINT32_C(0xfffffffd), 10) == 0); -#undef TEST_ALT_DIV + c_assert(c_free(NULL) == NULL); } +#endif +#if defined(C_MODULE_UNIX) /* - * Align to multiple of: Test the alignment macro. Check that it does - * not suffer from incorrect integer overflows, neither should it - * exceed the boundaries of the input type. + * Test c_fclose() and c_fclosep(). This uses the same logic as the + * tests for c_close() (i.e., sparse FD allocation). */ { - c_assert(c_align_to(UINT32_C(0), 1) == 0); - c_assert(c_align_to(UINT32_C(0), 2) == 0); - c_assert(c_align_to(UINT32_C(0), 4) == 0); - c_assert(c_align_to(UINT32_C(0), 8) == 0); - c_assert(c_align_to(UINT32_C(1), 8) == 8); + int r, i, fd, tmp[2]; + FILE *f; - c_assert(c_align_to(UINT32_C(0xffffffff), 8) == 0); - c_assert(c_align_to(UINT32_C(0xfffffff1), 8) == 0xfffffff8); - c_assert(c_align_to(UINT32_C(0xfffffff1), 8) == 0xfffffff8); + r = pipe(tmp); + c_assert(r >= 0); + fd = tmp[0]; + c_close(tmp[1]); - c_assert(__builtin_constant_p(c_align_to(16, 8))); - c_assert(!__builtin_constant_p(c_align_to(non_constant_expr, 8))); - c_assert(!__builtin_constant_p(c_align_to(16, non_constant_expr))); - c_assert(!__builtin_constant_p(c_align_to(16, non_constant_expr ? 8 : 16))); - c_assert(__builtin_constant_p(c_align_to(16, 7 + 1))); - c_assert(c_align_to(15, non_constant_expr ? 8 : 16) == 16); + f = fdopen(fd, "r"); + c_assert(f); + + /* verify c_fclose() returns NULL */ + f = c_fclose(f); + c_assert(!f); + + /* verify c_fclose() deals fine with NULL */ + c_assert(!c_fclose(NULL)); + + /* make sure c_flosep() deals fine with NULL */ + { + _c_cleanup_(c_fclosep) _c_unused_ FILE *t = (void *)0xdeadbeef; + t = NULL; + } + + /* + * Make sure the c_fclose() earlier worked, by allocating the + * FD again and relying on the same FD number to be reused. Do + * this twice, to verify that the c_fclosep() in the cleanup + * path works as well. + */ + for (i = 0; i < 2; ++i) { + _c_cleanup_(c_fclosep) _c_unused_ FILE *t = NULL; + int tfd; + + r = pipe(tmp); + c_assert(r >= 0); + tfd = tmp[0]; + c_close(tmp[1]); + + c_assert(tfd == fd); /* the same as before */ + t = fdopen(tfd, "r"); + c_assert(t); + } } +#endif /* * Test c_assert(). Make sure side-effects are always evaluated, and @@ -299,7 +193,8 @@ static void test_misc(int non_constant_expr) { { c_assert(c_errno() > 0); - close(-1); + strtol("0xfffffffffffffffffffffffffffffffff", NULL, 0); + c_assert(errno == ERANGE); c_assert(c_errno() == errno); errno = 0; @@ -370,46 +265,223 @@ static void test_misc(int non_constant_expr) { } } -/* - * Tests for: - * - c_free*() - * - c_close*() - * - c_fclose*() - * - c_closedir*() - */ -static void test_destructors(void) { - int i; +#else /* C_MODULE_GENERIC */ +static void test_basic_generic(void) { +} + +#endif /* C_MODULE_GENERIC */ + +#if defined(C_MODULE_GNUC) + +static void test_basic_gnuc(int non_constant_expr) { /* - * Verify that c_free*() works as expected. Since we want to support - * running under valgrind, there is no easy way to verify the - * correctness of free(). Hence, we simply rely on valgrind to catch - * the leaks. + * Test the C_EXPR_ASSERT() macro to work in static and non-static + * environments, and evaluate exactly to its passed expression. */ { - for (i = 0; i < 16; ++i) { - _c_cleanup_(c_freep) void *foo; - _c_cleanup_(c_freep) int **bar; /* supports any type */ - size_t sz = 128 * 1024; + static int v = C_EXPR_ASSERT(1, true, ""); - foo = malloc(sz); - c_assert(foo); - - bar = malloc(sz); - c_assert(bar); - bar = c_free(bar); - c_assert(!bar); - } - - c_assert(c_free(NULL) == NULL); + c_assert(v == 1); } + /* + * Test array-size helper. This simply computes the number of elements + * of an array, instead of the binary size. + */ + { + int bar[8]; + + static_assert(C_ARRAY_SIZE(bar) == 8, ""); + c_assert(__builtin_constant_p(C_ARRAY_SIZE(bar))); + } + + /* + * Test decimal-representation calculator. Make sure it is + * type-independent and just uses the size of the type to calculate how + * many bytes are needed to print that integer in decimal form. Also + * verify that it is a constant expression. + */ + { + static_assert(C_DECIMAL_MAX(char) == 4, ""); + static_assert(C_DECIMAL_MAX(signed char) == 4, ""); + static_assert(C_DECIMAL_MAX(unsigned char) == 4, ""); + static_assert(C_DECIMAL_MAX(unsigned long) == (sizeof(long) == 8 ? 21 : 11), ""); + static_assert(C_DECIMAL_MAX(unsigned long long) == 21, ""); + static_assert(C_DECIMAL_MAX(int32_t) == 11, ""); + static_assert(C_DECIMAL_MAX(uint32_t) == 11, ""); + static_assert(C_DECIMAL_MAX(uint64_t) == 21, ""); + } + + /* + * Test c_container_of(). We cannot test for type-safety, nor for + * other invalid uses, as they'd require negative compile-testing. + * However, we can test that the macro yields the correct values under + * normal use. + */ + { + struct foobar { + int a; + char b; + } sub = {}; + + c_assert(&sub == c_container_of(&sub.a, struct foobar, a)); + c_assert(&sub == c_container_of(&sub.b, struct foobar, b)); + c_assert(&sub == c_container_of((const char *)&sub.b, struct foobar, b)); + + c_assert(!c_container_of(NULL, struct foobar, b)); + } + + /* + * Test min/max macros. Especially check that macro arguments are never + * evaluated multiple times, and if both arguments are constant, the + * return value is constant as well. + */ + { + int foo; + + foo = 0; + c_assert(c_max(1, 5) == 5); + c_assert(c_max(-1, 5) == 5); + c_assert(c_max(-1, -5) == -1); + c_assert(c_max(foo++, -1) == 0); + c_assert(foo == 1); + c_assert(c_max(foo++, foo++) > 0); + c_assert(foo == 3); + + c_assert(__builtin_constant_p(c_max(1, 5))); + c_assert(!__builtin_constant_p(c_max(1, non_constant_expr))); + + foo = 0; + c_assert(c_min(1, 5) == 1); + c_assert(c_min(-1, 5) == -1); + c_assert(c_min(-1, -5) == -5); + c_assert(c_min(foo++, 1) == 0); + c_assert(foo == 1); + c_assert(c_min(foo++, foo++) > 0); + c_assert(foo == 3); + + c_assert(__builtin_constant_p(c_min(1, 5))); + c_assert(!__builtin_constant_p(c_min(1, non_constant_expr))); + } + + /* + * Test c_less_by(), c_clamp(). Make sure they + * evaluate arguments exactly once, and yield a constant expression, + * if all arguments are constant. + */ + { + int foo; + + foo = 8; + c_assert(c_less_by(1, 5) == 0); + c_assert(c_less_by(5, 1) == 4); + c_assert(c_less_by(foo++, 1) == 7); + c_assert(foo == 9); + c_assert(c_less_by(foo++, foo++) >= 0); + c_assert(foo == 11); + + c_assert(__builtin_constant_p(c_less_by(1, 5))); + c_assert(!__builtin_constant_p(c_less_by(1, non_constant_expr))); + + foo = 8; + c_assert(c_clamp(foo, 1, 5) == 5); + c_assert(c_clamp(foo, 9, 20) == 9); + c_assert(c_clamp(foo++, 1, 5) == 5); + c_assert(foo == 9); + c_assert(c_clamp(foo++, foo++, foo++) >= 0); + c_assert(foo == 12); + + c_assert(__builtin_constant_p(c_clamp(0, 1, 5))); + c_assert(!__builtin_constant_p(c_clamp(1, 0, non_constant_expr))); + } + + /* + * Div Round Up: Normal division, but round up to next integer, instead + * of clipping. Also verify that it does not suffer from the integer + * overflow in the prevalent, alternative implementation: + * [(x + y - 1) / y]. + */ + { + int i, j, foo; + +#define TEST_ALT_DIV(_x, _y) (((_x) + (_y) - 1) / (_y)) + foo = 8; + c_assert(c_div_round_up(0, 5) == 0); + c_assert(c_div_round_up(1, 5) == 1); + c_assert(c_div_round_up(5, 5) == 1); + c_assert(c_div_round_up(6, 5) == 2); + c_assert(c_div_round_up(foo++, 1) == 8); + c_assert(foo == 9); + c_assert(c_div_round_up(foo++, foo++) >= 0); + c_assert(foo == 11); + + c_assert(__builtin_constant_p(c_div_round_up(1, 5))); + c_assert(!__builtin_constant_p(c_div_round_up(1, non_constant_expr))); + + /* alternative calculation is [(x + y - 1) / y], but it may overflow */ + for (i = 0; i <= 0xffff; ++i) { + for (j = 1; j <= 0xff; ++j) + c_assert(c_div_round_up(i, j) == TEST_ALT_DIV(i, j)); + for (j = 0xff00; j <= 0xffff; ++j) + c_assert(c_div_round_up(i, j) == TEST_ALT_DIV(i, j)); + } + + /* make sure it doesn't suffer from high overflow */ + c_assert(UINT32_C(0xfffffffa) % 10 == 0); + c_assert(UINT32_C(0xfffffffa) / 10 == UINT32_C(429496729)); + c_assert(c_div_round_up(UINT32_C(0xfffffffa), 10) == UINT32_C(429496729)); + c_assert(TEST_ALT_DIV(UINT32_C(0xfffffffa), 10) == 0); /* overflow */ + + c_assert(UINT32_C(0xfffffffd) % 10 == 3); + c_assert(UINT32_C(0xfffffffd) / 10 == UINT32_C(429496729)); + c_assert(c_div_round_up(UINT32_C(0xfffffffd), 10) == UINT32_C(429496730)); + c_assert(TEST_ALT_DIV(UINT32_C(0xfffffffd), 10) == 0); +#undef TEST_ALT_DIV + } + + /* + * Align to multiple of: Test the alignment macro. Check that it does + * not suffer from incorrect integer overflows, neither should it + * exceed the boundaries of the input type. + */ + { + c_assert(c_align_to(UINT32_C(0), 1) == 0); + c_assert(c_align_to(UINT32_C(0), 2) == 0); + c_assert(c_align_to(UINT32_C(0), 4) == 0); + c_assert(c_align_to(UINT32_C(0), 8) == 0); + c_assert(c_align_to(UINT32_C(1), 8) == 8); + + c_assert(c_align_to(UINT32_C(0xffffffff), 8) == 0); + c_assert(c_align_to(UINT32_C(0xfffffff1), 8) == 0xfffffff8); + c_assert(c_align_to(UINT32_C(0xfffffff1), 8) == 0xfffffff8); + + c_assert(__builtin_constant_p(c_align_to(16, 8))); + c_assert(!__builtin_constant_p(c_align_to(non_constant_expr, 8))); + c_assert(!__builtin_constant_p(c_align_to(16, non_constant_expr))); + c_assert(!__builtin_constant_p(c_align_to(16, non_constant_expr ? 8 : 16))); + c_assert(__builtin_constant_p(c_align_to(16, 7 + 1))); + c_assert(c_align_to(15, non_constant_expr ? 8 : 16) == 16); + } +} + +#else /* C_MODULE_GNUC */ + +static void test_basic_gnuc(int unused0) { + (void)unused0; +} + +#endif /* C_MODULE_GNUC */ + +#if defined(C_MODULE_UNIX) + +static void test_basic_unix(void) { /* * Test c_close*(), rely on sparse FD allocation. Make sure all the * helpers actually close the fd, and cope fine with negative numbers. */ { - int r, fd1, fd2, tmp[2]; + int r, i, fd1, fd2, tmp[2]; r = pipe(tmp); c_assert(r >= 0); @@ -448,60 +520,19 @@ static void test_destructors(void) { c_assert(t2 == fd2); } } - - /* - * Test c_fclose() and c_fclosep(). This uses the same logic as the - * tests for c_close() (i.e., sparse FD allocation). - */ - { - int r, fd, tmp[2]; - FILE *f; - - r = pipe(tmp); - c_assert(r >= 0); - fd = tmp[0]; - c_close(tmp[1]); - - f = fdopen(fd, "r"); - c_assert(f); - - /* verify c_fclose() returns NULL */ - f = c_fclose(f); - c_assert(!f); - - /* verify c_fclose() deals fine with NULL */ - c_assert(!c_fclose(NULL)); - - /* make sure c_flosep() deals fine with NULL */ - { - _c_cleanup_(c_fclosep) _c_unused_ FILE *t = (void *)0xdeadbeef; - t = NULL; - } - - /* - * Make sure the c_fclose() earlier worked, by allocating the - * FD again and relying on the same FD number to be reused. Do - * this twice, to verify that the c_fclosep() in the cleanup - * path works as well. - */ - for (i = 0; i < 2; ++i) { - _c_cleanup_(c_fclosep) _c_unused_ FILE *t = NULL; - int tfd; - - r = pipe(tmp); - c_assert(r >= 0); - tfd = tmp[0]; - c_close(tmp[1]); - - c_assert(tfd == fd); /* the same as before */ - t = fdopen(tfd, "r"); - c_assert(t); - } - } } -int main(int argc, _c_unused_ char **argv) { - test_misc(argc); - test_destructors(); +#else /* C_MODULE_UNIX */ + +static void test_basic_unix(void) { +} + +#endif /* C_MODULE_UNIX */ + +int main(int argc, char **argv) { + (void)argv; + test_basic_generic(); + test_basic_gnuc(argc); + test_basic_unix(); return 0; }