Squashed 'src/c-stdaux/' changes from 99fe83cd5698..1407a1fb2754

1407a1fb2754 ci: add clang to RTD scripts
8404c4ca6d06 ci: build documentation
044c65c2e134 docs: add sphinx-based API documentation
ad8449068d96 ci: use ci-c-util for macos runs
179987035687 build: adjust for v1.1.0 release
55d787178c84 build: prepare v1.1.0 release
29ca943e636a c-stdaux: avoid NULL arithmetic even in dead-code
c6358e956c29 c-stdaux: avoid NULL-dereference in constant expressions
7fab258bdf6a c-stdaux: improve kerneldoc comments
434b75a796c0 build: export 'version-scripts' configuration
33d56a6aecb9 c-stdaux: encapsulate C_EXPR_ASSERT()
aca7ee0ece60 ci: add macos run
0aa338b1f0de test: prefer pipe() over eventfd() for portability
adda5ff3e9d9 build: export cflags via pkg-config
7a8493bebc59 api: add c_memcpy()
a01615aefe48 build: prepare v1.0.0
1685fc39db3d api: provide c_memzero()
1257244f886a api: add c_memset()

git-subtree-dir: src/c-stdaux
git-subtree-split: 1407a1fb275494f9efc1abbef2fd19856fb1f43d
This commit is contained in:
Thomas Haller 2022-07-06 10:15:51 +02:00
parent 1c260f5a96
commit 9d1772bd73
15 changed files with 692 additions and 182 deletions

View File

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

20
.readthedocs.yaml Normal file
View File

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

View File

@ -34,5 +34,9 @@ COPYRIGHT: (ordered alphabetically)
AUTHORS: (ordered alphabetically)
David Rheinsberg <david.rheinsberg@gmail.com>
Evgeny Vereshchagin <evvers@ya.ru>
Lorenzo Arena <lorenzo.arena@powersoft.com>
Michele Dionisio <michele.dionisio@gmail.com>
Thomas Haller <thaller@redhat.com>
Tom Gundersen <teg@jklm.no>
Yuri Chornoivan <yurchor@ukr.net>

37
NEWS.md
View File

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

View File

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

7
meson_options.txt Normal file
View File

@ -0,0 +1,7 @@
option(
'version-scripts',
choices: ['yes', 'no', 'auto'],
description: 'Enable GNU-version-scripts for linking',
type: 'combo',
value: 'auto',
)

View File

@ -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 <assert.h>
#include <dirent.h>
@ -48,44 +69,152 @@ extern "C" {
#include <time.h>
#include <unistd.h>
/*
* 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); \

5
src/docs/api.rst Normal file
View File

@ -0,0 +1,5 @@
API
===
.. c:autodoc:: c-*.h
:transform: kerneldoc

43
src/docs/conf.py Normal file
View File

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

14
src/docs/index.rst Normal file
View File

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

View File

@ -0,0 +1,3 @@
c-apidocs>=0.0.2
clang>=6
hawkmoth>=0.7

6
src/libcstdaux.sym Normal file
View File

@ -0,0 +1,6 @@
LIBCSTDAUX_1 {
global:
c_internal_dummy;
local:
*;
};

View File

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

View File

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

View File

@ -7,7 +7,6 @@
#undef NDEBUG
#include <stdlib.h>
#include <sys/eventfd.h>
#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);
}