diff options
author | Thomas Haller <thaller@redhat.com> | 2019-04-14 11:16:55 +0200 |
---|---|---|
committer | Thomas Haller <thaller@redhat.com> | 2019-04-14 11:45:49 +0200 |
commit | 5eab3c99873aa7aa1dd0ab642837fa0155a52a1e (patch) | |
tree | 5af4f49d7f8555efbcb1cc02e3cc3747f5cb3731 | |
parent | 606980c3eac239ad82a6ab256408c1a13df032e7 (diff) | |
parent | 21141429e5895c6dead6d835fee1f838d6612aab (diff) | |
download | NetworkManager-5eab3c99873aa7aa1dd0ab642837fa0155a52a1e.tar.gz |
shared/c-stdaux: merge initial import of 'shared/c-stdaux'
Imported c-stdaux code with command:
git subtree add --prefix shared/c-stdaux git@github.com:c-util/c-stdaux.git master --squash
To update the library use:
git subtree pull --prefix shared/c-stdaux git@github.com:c-util/c-stdaux.git master --squash
-rwxr-xr-x | shared/c-stdaux/.cherryci/ci-test | 12 | ||||
-rw-r--r-- | shared/c-stdaux/.editorconfig | 11 | ||||
-rw-r--r-- | shared/c-stdaux/.travis.yml | 21 | ||||
-rw-r--r-- | shared/c-stdaux/AUTHORS | 38 | ||||
-rw-r--r-- | shared/c-stdaux/NEWS.md | 11 | ||||
-rw-r--r-- | shared/c-stdaux/README.md | 53 | ||||
-rw-r--r-- | shared/c-stdaux/meson.build | 15 | ||||
-rw-r--r-- | shared/c-stdaux/src/c-stdaux.h | 544 | ||||
-rw-r--r-- | shared/c-stdaux/src/meson.build | 31 | ||||
-rw-r--r-- | shared/c-stdaux/src/test-api.c | 209 | ||||
-rw-r--r-- | shared/c-stdaux/src/test-basic.c | 432 |
11 files changed, 1377 insertions, 0 deletions
diff --git a/shared/c-stdaux/.cherryci/ci-test b/shared/c-stdaux/.cherryci/ci-test new file mode 100755 index 0000000000..8ce5f5f70e --- /dev/null +++ b/shared/c-stdaux/.cherryci/ci-test @@ -0,0 +1,12 @@ +#!/bin/bash + +set -e + +rm -Rf "./ci-build" +mkdir "./ci-build" +cd "./ci-build" + +${CHERRY_LIB_MESONSETUP} . "${CHERRY_LIB_SRCDIR}" +${CHERRY_LIB_NINJABUILD} +${CHERRY_LIB_MESONTEST} +(( ! CHERRY_LIB_VALGRIND )) || ${CHERRY_LIB_MESONTEST} "--wrapper=${CHERRY_LIB_VALGRINDWRAP}" diff --git a/shared/c-stdaux/.editorconfig b/shared/c-stdaux/.editorconfig new file mode 100644 index 0000000000..b10bb4f3f8 --- /dev/null +++ b/shared/c-stdaux/.editorconfig @@ -0,0 +1,11 @@ +root = true + +[*] +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true +charset = utf-8 + +[*.{c,h}] +indent_style = space +indent_size = 8 diff --git a/shared/c-stdaux/.travis.yml b/shared/c-stdaux/.travis.yml new file mode 100644 index 0000000000..99a7bb9461 --- /dev/null +++ b/shared/c-stdaux/.travis.yml @@ -0,0 +1,21 @@ +os: linux +dist: trusty +language: c + +services: + - docker + +before_install: + - curl -O -L "https://raw.githubusercontent.com/cherry-pick/cherry-images/v1/scripts/vmrun" + - curl -O -L "https://raw.githubusercontent.com/cherry-pick/cherry-ci/v1/scripts/cherryci" + - chmod +x "./vmrun" "./cherryci" + +jobs: + include: + - stage: test + script: + - ./vmrun -- ../src/cherryci -d ../src/.cherryci -s c-util -m + - script: + - ./vmrun -T armv7hl -- ../src/cherryci -d ../src/.cherryci -s c-util + - script: + - ./vmrun -T i686 -- ../src/cherryci -d ../src/.cherryci -s c-util diff --git a/shared/c-stdaux/AUTHORS b/shared/c-stdaux/AUTHORS new file mode 100644 index 0000000000..6015c4f31c --- /dev/null +++ b/shared/c-stdaux/AUTHORS @@ -0,0 +1,38 @@ +LICENSE: + This project is dual-licensed under both the Apache License, Version + 2.0, and the GNU Lesser General Public License, Version 2.1+. + +AUTHORS-ASL: + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +AUTHORS-LGPL: + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; If not, see <http://www.gnu.org/licenses/>. + +COPYRIGHT: (ordered alphabetically) + Copyright (C) 2018-2019 Red Hat, Inc. + +AUTHORS: (ordered alphabetically) + David Rheinsberg <david.rheinsberg@gmail.com> + Thomas Haller <thaller@redhat.com> + Tom Gundersen <teg@jklm.no> diff --git a/shared/c-stdaux/NEWS.md b/shared/c-stdaux/NEWS.md new file mode 100644 index 0000000000..d51af4cf60 --- /dev/null +++ b/shared/c-stdaux/NEWS.md @@ -0,0 +1,11 @@ +# c-stdaux - Auxiliary macros and functions for the C standard library + +## CHANGES WITH 1: + + * Initial release of c-stdaux. + + * TBD + + Contributions from: TBD + + - TBD, YYYY-MM-DD diff --git a/shared/c-stdaux/README.md b/shared/c-stdaux/README.md new file mode 100644 index 0000000000..51bff9778f --- /dev/null +++ b/shared/c-stdaux/README.md @@ -0,0 +1,53 @@ +c-stdaux +======== + +Auxiliary macros and functions for the C standard library + +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. + +### Project + + * **Website**: <https://c-util.github.io/c-stdaux> + * **Bug Tracker**: <https://github.com/c-util/c-stdaux/issues> + +### Requirements + +The requirements for this project are: + + * `libc` (e.g., `glibc >= 2.16`) + +At build-time, the following software is required: + + * `meson >= 0.41` + * `pkg-config >= 0.29` + +### Build + +The meson build-system is used for this project. Contact upstream +documentation for detailed help. In most situations the following +commands are sufficient to build and install from source: + +```sh +mkdir build +cd build +meson setup .. +ninja +meson test +ninja install +``` + +No custom configuration options are available. + +### Repository: + + - **web**: <https://github.com/c-util/c-stdaux> + - **https**: `https://github.com/c-util/c-stdaux.git` + - **ssh**: `git@github.com:c-util/c-stdaux.git` + +### License: + + - **Apache-2.0** OR **LGPL-2.1-or-later** + - See AUTHORS file for details. diff --git a/shared/c-stdaux/meson.build b/shared/c-stdaux/meson.build new file mode 100644 index 0000000000..c8c5da536f --- /dev/null +++ b/shared/c-stdaux/meson.build @@ -0,0 +1,15 @@ +project( + 'c-stdaux', + 'c', + version: '1', + license: 'Apache', + default_options: [ + 'c_std=c11' + ], +) +project_description = 'Auxiliary macros and functions for the C standard library' + +add_project_arguments('-D_GNU_SOURCE', language: 'c') +mod_pkgconfig = import('pkgconfig') + +subdir('src') diff --git a/shared/c-stdaux/src/c-stdaux.h b/shared/c-stdaux/src/c-stdaux.h new file mode 100644 index 0000000000..a02aed9c08 --- /dev/null +++ b/shared/c-stdaux/src/c-stdaux.h @@ -0,0 +1,544 @@ +#pragma once + +/* + * 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. + * + * Most of the helpers provided here provide aliases for common library and + * compiler features. Furthermore, several helpers simply provide other calling + * conventions than their standard counterparts (e.g., they allow for NULL to + * be passed with an object length of 0 where it makes sense to accept empty + * input). + * + * The namespace used by this project is: + * + * * `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) + * + * * `C_*` for all constants, as well as macros that may not be safe against + * double evaluation. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#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 <stdatomic.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> + +/* + * 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. + */ +#define _c_cleanup_(_x) __attribute__((__cleanup__(_x))) +#define _c_const_ __attribute__((__const__)) +#define _c_deprecated_ __attribute__((__deprecated__)) +#define _c_hidden_ __attribute__((__visibility__("hidden"))) +#define _c_likely_(_x) (__builtin_expect(!!(_x), 1)) +#define _c_packed_ __attribute__((__packed__)) +#define _c_printf_(_a, _b) __attribute__((__format__(printf, _a, _b))) +#define _c_public_ __attribute__((__visibility__("default"))) +#define _c_pure_ __attribute__((__pure__)) +#define _c_sentinel_ __attribute__((__sentinel__)) +#define _c_unlikely_(_x) (__builtin_expect(!!(_x), 0)) +#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 + * + * 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) \ + /* indentation and line-split to get better diagnostics */ \ + (__builtin_choose_expr( \ + !!(1 + 0 * sizeof( \ + struct { \ +_Static_assert(_assertion, _message); \ + } \ + )), \ + (_expr), \ + ((void)0) \ + )) + +/** + * 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_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: + * + * 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. + * + * 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_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_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__); \ + })) + +/** + * 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 the decimal + * representation of an integer + * @_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 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 + * + * 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_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 > 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 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; +} + +/* + * Common Destructors + * + * 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: + * + * foo = c_free(foo); + * + * or + * + * 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. + */ + +static inline void *c_free(void *p) { + free(p); + return NULL; +} + +static inline int c_close(int fd) { + if (fd >= 0) + close(fd); + return -1; +} + +static inline FILE *c_fclose(FILE *f) { + if (f) + fclose(f); + return NULL; +} + +static inline DIR *c_closedir(DIR *d) { + if (d) + closedir(d); + return NULL; +} + +/* + * 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. + */ + +#define C_DEFINE_CLEANUP(_type, _func) \ + static inline void _func ## p(_type *p) { \ + if (*p) \ + _func(*p); \ + } struct c_internal_trailing_semicolon + +#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); + +#ifdef __cplusplus +} +#endif diff --git a/shared/c-stdaux/src/meson.build b/shared/c-stdaux/src/meson.build new file mode 100644 index 0000000000..f6db824d24 --- /dev/null +++ b/shared/c-stdaux/src/meson.build @@ -0,0 +1,31 @@ +# +# target: libcstdaux.so +# (No .so is built so far, since we are header-only. This might change in the +# future, if we add more complex helpers.) +# + +libcstdaux_dep = declare_dependency( + include_directories: include_directories('.'), + version: meson.project_version(), +) + +if not meson.is_subproject() + install_headers('c-stdaux.h') + + mod_pkgconfig.generate( + version: meson.project_version(), + name: 'libcstdaux', + filebase: 'libcstdaux', + description: project_description, + ) +endif + +# +# target: test-* +# + +test_api = executable('test-api', ['test-api.c'], dependencies: libcstdaux_dep) +test('API Symbol Visibility', test_api) + +test_basic = executable('test-basic', ['test-basic.c'], dependencies: libcstdaux_dep) +test('Basic API Behavior', test_basic) diff --git a/shared/c-stdaux/src/test-api.c b/shared/c-stdaux/src/test-api.c new file mode 100644 index 0000000000..fb500660b0 --- /dev/null +++ b/shared/c-stdaux/src/test-api.c @@ -0,0 +1,209 @@ +/* + * API Visibility Tests + * This verifies the visibility and availability of the exported API. + */ + +#undef NDEBUG +#include <stdio.h> +#include <stdlib.h> +#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) { return 0; } +static _c_printf_(1, 2) int printf_fn(const char *f, ...) { return 0; } +_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 char *f, ...) { return 0; } +static _c_unused_ int unused_fn(void) { return 0; } + +static void cleanup_fn(int p) {} +static void direct_cleanup_fn(int p) {} +C_DEFINE_CLEANUP(int, cleanup_fn); +C_DEFINE_DIRECT_CLEANUP(int, direct_cleanup_fn); + +static void test_api_macros(void) { + /* _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_likely_ */ + { + 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); + + c_assert(!strcmp(v, "foobar")); + } + + /* C_CONCATENATE */ + { + int C_CONCATENATE(a, b) = 0; + + c_assert(!ab); + } + + /* C_EXPAND */ + { + int x[] = { C_EXPAND((0, 1)) }; + + c_assert(sizeof(x) / sizeof(*x) == 2); + } + + /* C_VAR */ + { + int C_VAR = 0; c_assert(!C_VAR); /* must be on the same line */ + } + + /* C_CC_MACRO1, C_CC_MACRO2, C_CC_MACRO3 */ + { +#define MACRO_REAL(_x1, _x2, _x3) ((_x1 + _x2 + _x3) * 0) +#define MACRO1(_x1) C_CC_MACRO1(MACRO_REAL, _x1, 0, 0) +#define MACRO2(_x1, _x2) C_CC_MACRO2(MACRO_REAL, _x1, _x2, 0) +#define MACRO3(_x1, _x2, _x3) C_CC_MACRO3(MACRO_REAL, _x1, _x2, _x3) + c_assert(!MACRO1(1)); + c_assert(!MACRO2(1, 1)); + c_assert(!MACRO3(1, 1, 1)); +#undef MACRO3 +#undef MACRO2 +#undef MACRO1 + } + + /* C_ARRAY_SIZE */ + { + int v[] = { 0, 1, 2 }; + c_assert(C_ARRAY_SIZE(v) == 3); + } + + /* C_DECIMAL_MAX */ + { + c_assert(C_DECIMAL_MAX(uint8_t) == 4); + } + + /* c_container_of */ + { + struct FooBarContainer { + int member; + } v = {}; + + c_assert(c_container_of(&v.member, struct FooBarContainer, member) == &v); + } + + /* c_max, c_min, c_less_by, c_clamp, c_div_round_up */ + { + c_assert(c_max(0, 0) == 0); + c_assert(c_min(0, 0) == 0); + c_assert(c_less_by(0, 0) == 0); + c_assert(c_clamp(0, 0, 0) == 0); + c_assert(c_div_round_up(1, 1) == 1); + } + + /* c_align_to */ + { + c_assert(c_align_to(0, 0) == 0); + } + + /* c_assert */ + { + c_assert(true); + } + + /* C_DEFINE_CLEANUP / C_DEFINE_DIRECT_CLEANUP */ + { + int v = 0; + + cleanup_fnp(&v); + direct_cleanup_fnp(&v); + } +} + +static void test_api_functions(void) { + void *fns[] = { + (void *)c_errno, + (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; + + for (i = 0; i < sizeof(fns) / sizeof(*fns); ++i) + c_assert(!!fns[i]); +} + +int main(int argc, char **argv) { + test_api_macros(); + test_api_functions(); + return 0; +} diff --git a/shared/c-stdaux/src/test-basic.c b/shared/c-stdaux/src/test-basic.c new file mode 100644 index 0000000000..291a8e199b --- /dev/null +++ b/shared/c-stdaux/src/test-basic.c @@ -0,0 +1,432 @@ +/* + * Tests for Basic Functionality + * + * This runs same basic verification that each feature does what we expect it + * to do. More elaborate tests and/or stress-tests are not included here. + */ + +#undef NDEBUG +#include <stdlib.h> +#include <sys/eventfd.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); + } + + /* + * Test stringify/concatenation helpers. Also make sure to test that + * the passed arguments are evaluated first, before they're stringified + * and/or concatenated. + */ + { +#define TEST_TOKEN foobar + c_assert(!strcmp("foobar", C_STRINGIFY(foobar))); + c_assert(!strcmp("foobar", C_STRINGIFY(TEST_TOKEN))); + c_assert(!strcmp("foobar", C_STRINGIFY(C_CONCATENATE(foo, bar)))); + c_assert(!strcmp("foobarfoobar", C_STRINGIFY(C_CONCATENATE(TEST_TOKEN, foobar)))); + c_assert(!strcmp("foobarfoobar", C_STRINGIFY(C_CONCATENATE(foobar, TEST_TOKEN)))); +#undef TEST_TOKEN + } + + /* + * Test tuple expansion. This is used to strip tuple-wrappers in the + * pre-processor. + * We make sure that it works with {0,1,2}-tuples, as well as only + * strips a single layer. + */ + { + /* + * strcmp() might be a macro, so make sure we get a proper C + * expression below. Otherwise, C_EXPAND() cannot be used that + * way (since it would evaluate to a single macro argument). + */ + int (*f) (const char *, const char *) = strcmp; + + c_assert(!f(C_EXPAND(()) "foobar", "foo" "bar")); + c_assert(!f(C_EXPAND(("foobar")), "foo" "bar")); + c_assert(!f(C_EXPAND(("foobar", "foo" "bar")))); + c_assert(!f C_EXPAND((("foobar", "foo" "bar")))); + } + + /* + * Test C_VAR() macro. It's sole purpose is to create a valid C + * identifier given a single argument (which itself must be a valid + * identifier). + * Just test that we can declare variables with it and use it in + * expressions. + */ + { + { + int C_VAR(sub, UNIQUE) = 5; + /* make sure the variable name does not clash */ + int sub = 12, subUNIQUE = 12, UNIQUEsub = 12; + + c_assert(7 + C_VAR(sub, UNIQUE) == sub); + 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); + } + { + /* verify C_VAR() with no argument works line-based */ + int C_VAR(); C_VAR() = 5; c_assert(C_VAR() == 5); + } + } + + /* + * 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)); + } + + /* + * 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 prevalant, 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)); + } + + /* 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); + } + + /* + * Test c_assert(). Make sure side-effects are always evaluated, and + * variables are marked as used regardless of NDEBUG. + */ + { + int v1 = 0, v2 = 0; + +#define NDEBUG 1 + c_assert(!v1); + if (v1) + abort(); + c_assert(++v1); + if (v1 != 1) + abort(); +#undef NDEBUG + c_assert(!v2); + if (v2) + abort(); + c_assert(++v2); + if (v2 != 1) + abort(); + } + + /* + * Test c_errno(). Simply verify that the correct value is returned. It + * must always be >0 and equivalent to `errno' if set. + */ + { + c_assert(c_errno() > 0); + + close(-1); + c_assert(c_errno() == errno); + + errno = 0; + c_assert(c_errno() != errno); + } +} + +/* + * Tests for: + * - c_free*() + * - c_close*() + * - c_fclose*() + * - c_closedir*() + */ +static void test_destructors(void) { + int i; + + /* + * 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. + */ + { + 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; + + 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); + } + + /* + * Test c_close*(), rely on sparse FD allocation. Make sure all the + * helpers actually close the fd, and cope fine with negative numbers. + */ + { + int fd; + + fd = eventfd(0, EFD_CLOEXEC); + c_assert(fd >= 0); + + /* verify c_close() returns -1 */ + c_assert(c_close(fd) == -1); + + /* verify c_close() deals fine with negative fds */ + c_assert(c_close(-1) == -1); + c_assert(c_close(-16) == -1); + + /* make sure c_closep() deals fine with negative FDs */ + { + _c_cleanup_(c_closep) int t = 0; + t = -1; + } + + /* + * Make sure the c_close() 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_closep() in the cleanup + * path works as well. + */ + for (i = 0; i < 2; ++i) { + _c_cleanup_(c_closep) int t = -1; + + t = eventfd(0, EFD_CLOEXEC); + c_assert(t >= 0); + c_assert(t == fd); + } + } + + /* + * Test c_fclose() and c_fclosep(). This uses the same logic as the + * tests for c_close() (i.e., sparse FD allocation). + */ + { + FILE *f; + int fd; + + fd = eventfd(0, EFD_CLOEXEC); + c_assert(fd >= 0); + + 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) 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) FILE *t = NULL; + int tfd; + + tfd = eventfd(0, EFD_CLOEXEC); + c_assert(tfd >= 0); + c_assert(tfd == fd); /* the same as before */ + + t = fdopen(tfd, "r"); + c_assert(t); + } + } +} + +int main(int argc, char **argv) { + test_misc(argc); + test_destructors(); + return 0; +} |