diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d54b120011..0b905f5683 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,11 +7,24 @@ on: - cron: '0 0 * * *' jobs: - ci: - name: CI with Default Configuration + ci-linux: + name: Linux CI uses: bus1/cabuild/.github/workflows/ci-c-util.yml@v1 with: cabuild_ref: "v1" + linux: true m32: true matrixmode: true valgrind: true + ci-macos: + name: MacOS CI + uses: bus1/cabuild/.github/workflows/ci-c-util.yml@v1 + with: + cabuild_ref: "v1" + linux: false + macos: true + ci-docs: + name: Documentation CI + uses: bus1/cabuild/.github/workflows/ci-sphinx.yml@main + with: + source: "./src/docs" diff --git a/.readthedocs.yaml b/.readthedocs.yaml new file mode 100644 index 0000000000..5bbe6ac1fd --- /dev/null +++ b/.readthedocs.yaml @@ -0,0 +1,20 @@ +# Read the Docs configuration file + +version: 2 + +build: + apt_packages: + - "clang" + os: "ubuntu-22.04" + tools: + python: "3" + +formats: "all" + +python: + install: + - requirements: "src/docs/requirements.txt" + system_packages: true + +sphinx: + configuration: "src/docs/conf.py" diff --git a/AUTHORS b/AUTHORS index 555ecb5581..4b108f1901 100644 --- a/AUTHORS +++ b/AUTHORS @@ -34,5 +34,9 @@ COPYRIGHT: (ordered alphabetically) AUTHORS: (ordered alphabetically) David Rheinsberg + Evgeny Vereshchagin + Lorenzo Arena + Michele Dionisio Thomas Haller Tom Gundersen + Yuri Chornoivan diff --git a/NEWS.md b/NEWS.md index d51af4cf60..4954a47876 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,11 +1,38 @@ # c-stdaux - Auxiliary macros and functions for the C standard library -## CHANGES WITH 1: +## CHANGES WITH 1.1.0: + + * Add c_memcpy() as a safe wrapper around memcpy(3) that supports + empty arenas as NULL pointers. + + * Support building on MacOS-X. + + * Rework the apidoc comments and properly document the entire API. + + * Export 'version-scripts' configuration variable alongside the + existing 'cflags' variable. It defines whether c-stdaux was built + with GNU-linker version-scripts, or not. Dependent projects can + use this to decide whether to use version-scripts or not. + Additionally, the new 'version-scripts' meson-option allows + specifying whether to use version-scripts, auto-detect whether to + enable it, or disable it. + + * Fix the export of `cflags` to also be exported in pkg-config, not + just meson subprojects. + + * Avoid NULL-pointers in compile-time macros. This silences possible + false-positives from code sanitizers that otherwise trip over the + NULL pointer dereferences. + + Contributions from: David Rheinsberg, Evgeny Vereshchagin + + - Brno, 2022-06-22 + +## CHANGES WITH 1.0.0: * Initial release of c-stdaux. - * TBD + Contributions from: David Rheinsberg, Lorenzo Arena, Michele Dionisio, + Yuri Chornoivan - Contributions from: TBD - - - TBD, YYYY-MM-DD + - Dußlingen, 2022-05-12 diff --git a/meson.build b/meson.build index 3054e25073..fb3217e61f 100644 --- a/meson.build +++ b/meson.build @@ -1,3 +1,7 @@ +# +# Global Project Setup +# + project( 'c-stdaux', 'c', @@ -6,7 +10,7 @@ project( ], license: 'Apache', meson_version: '>=0.60.0', - version: '1.0.0', + version: '1.1.0', ) major = meson.project_version().split('.')[0] project_description = 'Auxiliary macros and functions for the C standard library' @@ -23,6 +27,7 @@ mod_pkgconfig = import('pkgconfig') # well. Since these exports are limited to strings, we need to be careful that # the individual entries do not contain spaces (see the assertion below). # + cflags = meson.get_compiler('c').get_supported_arguments( # Enable GNU features of our dependencies. See feature_test_macros(7). '-D_GNU_SOURCE', @@ -81,6 +86,25 @@ cflags = meson.get_compiler('c').get_supported_arguments( assert(not ''.join(cflags).contains(' '), 'Malformed compiler flags.') add_project_arguments(cflags, language: 'c') +# +# Version Scripts +# + +use_version_scripts = get_option('version-scripts') +if use_version_scripts == 'auto' + use_version_scripts = meson.get_compiler('c').has_link_argument( + '-Wl,--version-script=' + (meson.current_source_dir() / 'src/libcstdaux.sym') + ) ? 'yes' : 'no' +endif + +# +# Subdir Delegation +# + subdir('src') +# +# Meson Subproject Configuration +# + meson.override_dependency('libcstdaux-'+major, libcstdaux_dep, static: true) diff --git a/meson_options.txt b/meson_options.txt new file mode 100644 index 0000000000..82fdaf7814 --- /dev/null +++ b/meson_options.txt @@ -0,0 +1,7 @@ +option( + 'version-scripts', + choices: ['yes', 'no', 'auto'], + description: 'Enable GNU-version-scripts for linking', + type: 'combo', + value: 'auto', +) diff --git a/src/c-stdaux.h b/src/c-stdaux.h index 6d43027c03..6eb0a22d03 100644 --- a/src/c-stdaux.h +++ b/src/c-stdaux.h @@ -1,12 +1,23 @@ #pragma once /* - * Auxiliary macros and functions for the C standard library + * c-stdaux: Auxiliary macros and functions for the C standard library * - * The `c-stdaux.h` header contains a collection of auxiliary macros and helper - * functions around the functionality provided by the different C standard - * library implementations, as well as other specifications implemented by - * them. + * Main public header of the c-stdaux library. All includes of this header are + * part of the API! + */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * DOC: + * + * The ``c-stdaux.h`` header contains a collection of auxiliary macros and + * helper functions around the functionality provided by the different C + * standard library implementations, as well as other specifications + * implemented by them. * * Most of the helpers provided here provide aliases for common library and * compiler features. Furthermore, several helpers simply provide other calling @@ -16,17 +27,27 @@ * * The namespace used by this project is: * - * * `c_*` for all common C symbols or definitions that behave like proper C + * - ``c_*`` for all common C symbols or definitions that behave like proper C * entities (e.g., macros that protect against double-evaluation would use - * lower-case names) + * lower-case names). * - * * `C_*` for all constants, as well as macros that may not be safe against + * - ``C_*`` for all constants, as well as macros that may not be safe against * double evaluation. + * + * - ``c_internal_*`` and ``C_INTERNAL_*`` for all internal symbols that + * should not be invoked by the caller and are not part of the API + * guarantees. */ +/**/ -#ifdef __cplusplus -extern "C" { -#endif +/** + * 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 @@ -48,44 +69,152 @@ extern "C" { #include #include -/* - * Shortcuts for gcc attributes. See GCC manual for details. They're 1-to-1 - * mappings to the GCC equivalents. No additional magic here. They are - * supported by other compilers as well. +/** + * 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"))) -#define _c_likely_(_x) (__builtin_expect(!!(_x), 1)) + +/** + * _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__)) -#define _c_unlikely_(_x) (__builtin_expect(!!(_x), 0)) + +/** + * _c_unused_() - Unused attribute + * + * Alias for ``__attribute__((__unused__))``. + */ #define _c_unused_ __attribute__((__unused__)) /** - * C_EXPR_ASSERT() - create expression with assertion - * @_expr: expression to evaluate to - * @_assertion: arbitrary assertion - * @_message: message associated with the assertion + * DOC: Compiler Intrinsics * - * 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. + * 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_EXPR_ASSERT(_expr, _assertion, _message) (_expr) +# define C_INTERNAL_EXPR_ASSERT(_expr, _assertion, _message) (_expr) #else -# define C_EXPR_ASSERT(_expr, _assertion, _message) \ +# define C_INTERNAL_EXPR_ASSERT(_expr, _assertion, _message) \ /* indentation and line-split to get better diagnostics */ \ (__builtin_choose_expr( \ !!(1 + 0 * sizeof( \ @@ -99,8 +228,8 @@ _Static_assert(_assertion, _message); \ #endif /** - * C_STRINGIFY() - stringify a token, but evaluate it first - * @_x: token to evaluate and stringify + * C_STRINGIFY() - Stringify a token, but evaluate it first + * @_x: Token to evaluate and stringify * * Return: Evaluates to a constant string literal */ @@ -108,9 +237,9 @@ _Static_assert(_assertion, _message); \ #define C_INTERNAL_STRINGIFY(_x) #_x /** - * C_CONCATENATE() - concatenate two tokens, but evaluate them first - * @_x: first token - * @_y: second token + * C_CONCATENATE() - Concatenate two tokens, but evaluate them first + * @_x: First token + * @_y: Second token * * Return: Evaluates to a constant identifier */ @@ -118,8 +247,8 @@ _Static_assert(_assertion, _message); \ #define C_INTERNAL_CONCATENATE(_x, _y) _x ## _y /** - * C_EXPAND() - expand a tuple to a series of its values - * @_x: tuple to expand + * C_EXPAND() - Expand a tuple to a series of its values + * @_x: Tuple to expand * * Return: Evaluates to the expanded tuple */ @@ -127,29 +256,32 @@ _Static_assert(_assertion, _message); \ #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 + * 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_CONCATENATE of both arguments, but also provides a globally separated - * prefix and makes the code better readable. + * :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 + * 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 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_max() might cause compiler warnings of shadowed - * variables in the definition of c_max(). By using C_VAR(), such warnings - * can be silenced as each evaluation of c_max() uses unique variable names. + * 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. */ @@ -159,28 +291,28 @@ _Static_assert(_assertion, _message); \ #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 + * 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... + * 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. + * - 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. + * - 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 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. + * - have properly typed arguments as ``C_CC_MACRO1`` stores the original + * arguments in an ``__auto_type`` temporary variable. * - * Return: Result of @_call is returned. + * 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, ...) \ @@ -193,15 +325,15 @@ _Static_assert(_assertion, _message); \ })) /** - * 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 + * 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_CC_MACRO1(). + * This is the 2-argument equivalent of :c:macro:`C_CC_MACRO1()`. * - * Return: Result of @_call is returned. + * 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, ...) \ @@ -215,16 +347,16 @@ _Static_assert(_assertion, _message); \ })) /** - * 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 + * 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_CC_MACRO1(). + * This is the 3-argument equivalent of :c:macro:`C_CC_MACRO1()`. * - * Return: Result of @_call is returned. + * 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, ...) \ @@ -239,8 +371,17 @@ _Static_assert(_assertion, _message); \ })) /** - * C_ARRAY_SIZE() - calculate number of array elements at compile time - * @_x: array to calculate size of + * 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. */ @@ -258,9 +399,8 @@ _Static_assert(_assertion, _message); \ ) /** - * C_DECIMAL_MAX() - calculate maximum length of the decimal - * representation of an integer - * @_type: integer variable/type + * 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 @@ -292,28 +432,41 @@ _Static_assert(_assertion, _message); \ ) /** - * c_container_of() - cast a member of a structure out to the containing structure - * @_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 + * 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 + * 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) \ - __extension__ ({ \ - /* trigger warning if types do not match */ \ - (void)(&((_type *)0)->_member == (_ptr)); \ - _ptr ? (_type*)( (char*)_ptr - offsetof(_type, _member) ) : NULL; \ - }) + 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 + * 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 @@ -329,9 +482,9 @@ _Static_assert(_assertion, _message); \ #define C_MAX(_a, _b) ((_a) > (_b) ? (_a) : (_b)) /** - * c_min() - compute minimum of two values - * @_a: value A - * @_b: value 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 @@ -347,32 +500,32 @@ _Static_assert(_assertion, _message); \ #define C_MIN(_a, _b) ((_a) < (_b) ? (_a) : (_b)) /** - * c_less_by() - calculate clamped difference of two values - * @_a: minuend - * @_b: subtrahend + * 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. + * 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. + * 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 + * 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. + * 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 @@ -384,18 +537,18 @@ _Static_assert(_assertion, _message); \ #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 + * 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 + * 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 + * **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 + * ``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. * @@ -409,34 +562,35 @@ _Static_assert(_assertion, _message); \ #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 + * 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. + * 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. + * Note that ``_to`` must be a power of 2, otherwise the behavior will not + * match expectations. * - * Return: @_val aligned to a multiple of @_to + * 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 + * 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 + * 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 + * 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. @@ -447,13 +601,13 @@ _Static_assert(_assertion, _message); \ }) /** - * c_errno() - return valid errno + * c_errno() - Return valid errno * - * This helper should be used to shut up gcc if you know 'errno' is valid (ie., - * errno is > 0). Instead of "return -errno;", use - * "return -c_errno();" It will suppress bogus gcc warnings in case it assumes - * 'errno' might be 0 (or <0) and thus the caller's error-handling might not be - * triggered. + * 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 @@ -470,62 +624,179 @@ static inline int c_errno(void) { return _c_likely_(errno > 0) ? errno : ENOTRECOVERABLE; } -/* - * Common Destructors +/** + * 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 * - * Followingly, there're a bunch of common 'static inline' destructors, which - * simply call the function that they're named after, but return "INVALID" - * instead of "void". This allows direct assignment to any member-field and/or - * variable they're defined in, like: + * 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. * - * foo = c_free(foo); + * 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 * - * or + * 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. * - * foo->bar = c_close(foo->bar); + * 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; +} + +/** + * 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; } -/* - * Common Cleanup Helpers +/** + * DOC: Common Cleanup Helpers * - * A bunch of _c_cleanup_(foobarp) helpers that are used all over the place. - * Note that all of those have the "if (IS_INVALID(foobar))" check inline, so - * compilers can optimize most of the cleanup-paths in a function. However, if - * the function they call already does this _inline_, then it might be skipped. + * 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); \ diff --git a/src/docs/api.rst b/src/docs/api.rst new file mode 100644 index 0000000000..6cb51827ae --- /dev/null +++ b/src/docs/api.rst @@ -0,0 +1,5 @@ +API +=== + +.. c:autodoc:: c-*.h + :transform: kerneldoc diff --git a/src/docs/conf.py b/src/docs/conf.py new file mode 100644 index 0000000000..65ffb00e84 --- /dev/null +++ b/src/docs/conf.py @@ -0,0 +1,43 @@ +# +# Sphinx Documentation Configuration +# + +import re +import os +import sys + +import capidocs.kerneldoc +import hawkmoth + +# Global Setup + +project = 'c-stdaux' + +author = 'C-Util Community' +copyright = '2022, C-Util Community' + +# Hawkmoth C-Audodoc Setup + +capidocs.kerneldoc.hawkmoth_conf() + +# Extensions + +exclude_patterns = [] + +extensions = [ + 'hawkmoth', +] + +# Hawkmoth Options + +cautodoc_clang = capidocs.kerneldoc.hawkmoth_include_args() + +cautodoc_root = os.path.abspath('..') + +cautodoc_transformations = { + 'kerneldoc': capidocs.kerneldoc.hawkmoth_converter, +} + +# HTML Options + +html_theme = 'sphinx_rtd_theme' diff --git a/src/docs/index.rst b/src/docs/index.rst new file mode 100644 index 0000000000..5ad99374de --- /dev/null +++ b/src/docs/index.rst @@ -0,0 +1,14 @@ +Introduction +============ + +The **c-stdaux** project contains support-macros and auxiliary functions around +the functionality of common C standard libraries. This includes helpers for the +ISO-C Standard Library, but also other common specifications like POSIX or +common extended features of wide-spread compilers like gcc and clang. + +.. toctree:: + :caption: Library Documentation + :hidden: + + self + api diff --git a/src/docs/requirements.txt b/src/docs/requirements.txt new file mode 100644 index 0000000000..97872db187 --- /dev/null +++ b/src/docs/requirements.txt @@ -0,0 +1,3 @@ +c-apidocs>=0.0.2 +clang>=6 +hawkmoth>=0.7 diff --git a/src/libcstdaux.sym b/src/libcstdaux.sym new file mode 100644 index 0000000000..2073665f1a --- /dev/null +++ b/src/libcstdaux.sym @@ -0,0 +1,6 @@ +LIBCSTDAUX_1 { +global: + c_internal_dummy; +local: + *; +}; diff --git a/src/meson.build b/src/meson.build index a9322d4daa..9d78af9c98 100644 --- a/src/meson.build +++ b/src/meson.build @@ -4,9 +4,14 @@ # future, if we add more complex helpers.) # +libcstdaux_vars = { + 'cflags': ' '.join(cflags), + 'version-scripts': use_version_scripts, +} + libcstdaux_dep = declare_dependency( include_directories: include_directories('.'), - variables: { 'cflags': ' '.join(cflags) }, + variables: libcstdaux_vars, version: meson.project_version(), ) @@ -17,6 +22,7 @@ if not meson.is_subproject() description: project_description, filebase: 'libcstdaux-'+major, name: 'libcstdaux', + unescaped_variables: libcstdaux_vars, version: meson.project_version(), ) endif diff --git a/src/test-api.c b/src/test-api.c index cddbb70dac..44be1395a9 100644 --- a/src/test-api.c +++ b/src/test-api.c @@ -189,6 +189,9 @@ static void test_api_macros(void) { 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, diff --git a/src/test-basic.c b/src/test-basic.c index 15499353cb..e5e3f907ed 100644 --- a/src/test-basic.c +++ b/src/test-basic.c @@ -7,7 +7,6 @@ #undef NDEBUG #include -#include #include "c-stdaux.h" /* @@ -138,6 +137,8 @@ static void test_misc(int non_constant_expr) { 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)); } /* @@ -304,6 +305,58 @@ static void test_misc(int non_constant_expr) { errno = 0; c_assert(c_errno() != errno); } + + /* + * Test c_memset(). Simply verify its most basic behavior, as well as + * calling it on empty regions. + */ + { + uint64_t v = (uint64_t)-1; + size_t n; + void *p; + + /* try filling with 0 and 0xff */ + c_assert(v == (uint64_t)-1); + c_memset(&v, 0, sizeof(v)); + c_assert(v == (uint64_t)0); + c_memset(&v, 0xff, sizeof(v)); + c_assert(v == (uint64_t)-1); + + /* + * Try tricking the optimizer into thinking @p cannot be NULL, + * as normal `memset(3)` would allow. + */ + p = NULL; + n = 0; + c_memset(p, 0, n); + if (p) + abort(); + c_assert(p == NULL); + } + + /* + * Test c_memzero(). Simply verify it can clear a trivial area to 0. + */ + { + uint64_t v = (uint64_t)-1; + + c_assert(v == (uint64_t)-1); + c_memzero(&v, sizeof(v)); + c_assert(v == (uint64_t)0); + } + + /* + * Test c_memcpy() with a simple 8-byte copy. + */ + { + uint64_t v1 = (uint64_t)-1, v2 = (uint64_t)0; + + c_assert(v1 == (uint64_t)-1); + c_memcpy(&v1, &v2, sizeof(v1)); + c_assert(v1 == (uint64_t)0); + + c_memcpy(NULL, NULL, 0); + } } /* @@ -345,13 +398,16 @@ static void test_destructors(void) { * helpers actually close the fd, and cope fine with negative numbers. */ { - int fd; + int r, fd1, fd2, tmp[2]; - fd = eventfd(0, EFD_CLOEXEC); - c_assert(fd >= 0); + r = pipe(tmp); + c_assert(r >= 0); + fd1 = tmp[0]; + fd2 = tmp[1]; /* verify c_close() returns -1 */ - c_assert(c_close(fd) == -1); + c_assert(c_close(fd1) == -1); + c_assert(c_close(fd2) == -1); /* verify c_close() deals fine with negative fds */ c_assert(c_close(-1) == -1); @@ -370,11 +426,15 @@ static void test_destructors(void) { * path works as well. */ for (i = 0; i < 2; ++i) { - _c_cleanup_(c_closep) _c_unused_ int t = -1; + _c_cleanup_(c_closep) _c_unused_ int t1 = -1, t2 = -1; - t = eventfd(0, EFD_CLOEXEC); - c_assert(t >= 0); - c_assert(t == fd); + r = pipe(tmp); + c_assert(r >= 0); + t1 = tmp[0]; + t2 = tmp[1]; + + c_assert(t1 == fd1); + c_assert(t2 == fd2); } } @@ -383,11 +443,13 @@ static void test_destructors(void) { * tests for c_close() (i.e., sparse FD allocation). */ { + int r, fd, tmp[2]; FILE *f; - int fd; - fd = eventfd(0, EFD_CLOEXEC); - c_assert(fd >= 0); + r = pipe(tmp); + c_assert(r >= 0); + fd = tmp[0]; + c_close(tmp[1]); f = fdopen(fd, "r"); c_assert(f); @@ -415,10 +477,12 @@ static void test_destructors(void) { _c_cleanup_(c_fclosep) _c_unused_ FILE *t = NULL; int tfd; - tfd = eventfd(0, EFD_CLOEXEC); - c_assert(tfd >= 0); - c_assert(tfd == fd); /* the same as before */ + 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); }