summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Haller <thaller@redhat.com>2019-04-14 11:16:55 +0200
committerThomas Haller <thaller@redhat.com>2019-04-14 11:16:55 +0200
commit21141429e5895c6dead6d835fee1f838d6612aab (patch)
tree9aa7feb4d52724cf86006339116caf5a81e10940
downloadNetworkManager-21141429e5895c6dead6d835fee1f838d6612aab.tar.gz
Squashed 'shared/c-stdaux/' content from commit 11930d2592
git-subtree-dir: shared/c-stdaux git-subtree-split: 11930d259212605a15430523472ef54e0c7654ee
-rwxr-xr-x.cherryci/ci-test12
-rw-r--r--.editorconfig11
-rw-r--r--.travis.yml21
-rw-r--r--AUTHORS38
-rw-r--r--NEWS.md11
-rw-r--r--README.md53
-rw-r--r--meson.build15
-rw-r--r--src/c-stdaux.h544
-rw-r--r--src/meson.build31
-rw-r--r--src/test-api.c209
-rw-r--r--src/test-basic.c432
11 files changed, 1377 insertions, 0 deletions
diff --git a/.cherryci/ci-test b/.cherryci/ci-test
new file mode 100755
index 0000000000..8ce5f5f70e
--- /dev/null
+++ b/.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/.editorconfig b/.editorconfig
new file mode 100644
index 0000000000..b10bb4f3f8
--- /dev/null
+++ b/.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/.travis.yml b/.travis.yml
new file mode 100644
index 0000000000..99a7bb9461
--- /dev/null
+++ b/.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/AUTHORS b/AUTHORS
new file mode 100644
index 0000000000..6015c4f31c
--- /dev/null
+++ b/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/NEWS.md b/NEWS.md
new file mode 100644
index 0000000000..d51af4cf60
--- /dev/null
+++ b/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/README.md b/README.md
new file mode 100644
index 0000000000..51bff9778f
--- /dev/null
+++ b/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/meson.build b/meson.build
new file mode 100644
index 0000000000..c8c5da536f
--- /dev/null
+++ b/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/src/c-stdaux.h b/src/c-stdaux.h
new file mode 100644
index 0000000000..a02aed9c08
--- /dev/null
+++ b/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/src/meson.build b/src/meson.build
new file mode 100644
index 0000000000..f6db824d24
--- /dev/null
+++ b/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/src/test-api.c b/src/test-api.c
new file mode 100644
index 0000000000..fb500660b0
--- /dev/null
+++ b/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/src/test-basic.c b/src/test-basic.c
new file mode 100644
index 0000000000..291a8e199b
--- /dev/null
+++ b/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;
+}