Squashed 'src/c-stdaux/' changes from 4e78ffaea49d..2d3877aabd7d

2d3877aabd7d docs: avoid duplicate headers
ba751b517888 c-stdaux: be more consistent with #ifdef
9796f4a63a4b c-stdaux: move _c_always_inline_ to *-generic
34067b3a5f4f c-stdaux: avoid declspec-fallback for _c_public_
82b82245cf36 c-stdaux: expose _c_public_ in *-generic
37fa624afcd6 docs: set C_COMPILER_DOCS
7197bc75f829 docs: add ./src to include path
34ed5b2c4b52 test-basic: avoid _c_unused_
00cc51c99c64 test-basic: fix *_gnuc() fallback to have an argument
6a9262c168f7 test-basic: use strtol() over close() to set errno
807d4a704757 test-basic: guard cleanup-tests by GNUC
13f65ad8c27c test-basic: separate tests by module
fdf399ef7f5b test-api: only test for available APIs
1f9cfe8e3b2f c-stdaux: export C_MODULE_*
65bf768151e3 c-stdaux: move GNUC-macros into separate module
6549fa0eb8f3 c-stdaux: extract unix'ish code into separate module
d69c3c0fe7ee c-stdaux: split off portable code
132d82a37607 c-stdaux: add C_COMPILER_DOCS documentation
053b2d9f1c11 c-stdaux: avoid ctx-expr in c_assert()
e75f32c2e046 c-stdaux: fix typo in c_assert() docs
d75a2350ae22 c-stdaux: stub likely/unlikely as fallback
eb90a0d0fced c-stdaux: fix documentation of likely/unlikely
57f332c53184 c-stdaux: fix typo in c_closedir() docs
f3d6b60400d3 c-stdaux: add _c_always_inline_
8d017b02cf12 c-stdaux: provide target identification
3d8f78f964ff ci: enable windows builds

git-subtree-dir: src/c-stdaux
git-subtree-split: 2d3877aabd7d0e813f4a153ac262ee83b3c04793
This commit is contained in:
Thomas Haller 2022-11-23 18:00:35 +01:00
parent e622986359
commit 634547635b
10 changed files with 1490 additions and 1141 deletions

View file

@ -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

471
src/c-stdaux-generic.h Normal file
View file

@ -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 <assert.h>
#include <errno.h>
#include <inttypes.h>
#include <limits.h>
#include <stdalign.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdnoreturn.h>
#include <string.h>
#include <time.h>
/**
* 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

435
src/c-stdaux-gnuc.h Normal file
View file

@ -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 <c-stdaux-generic.h>
/* 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

102
src/c-stdaux-unix.h Normal file
View file

@ -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 <c-stdaux-generic.h>
/* 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 <dirent.h>
#include <fcntl.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
/**
* 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

View file

@ -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 <c-stdaux-generic.h>
#include <assert.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
#include <limits.h>
#include <stdalign.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdnoreturn.h>
#include <string.h>
#include <sys/time.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>
/**
* 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 <c-stdaux-gnuc.h>
#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 <c-stdaux-unix.h>
#endif
#ifdef __cplusplus
}

View file

@ -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

View file

@ -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('..')

View file

@ -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,

View file

@ -9,42 +9,41 @@
#include <string.h>
#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;
}

View file

@ -9,22 +9,9 @@
#include <stdlib.h>
#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;
}