summary refs log tree commit diff
path: root/src/c-stdaux
diff options
context:
space:
mode:
authorMichael Biebl <biebl@debian.org>2024-05-05 00:07:30 +0200
committerMichael Biebl <biebl@debian.org>2024-05-05 00:07:30 +0200
commit34bb501be08aa2b313d88e67d6e0a7e0a3f9cfa6 (patch)
tree4e6220877828be4c6f261de09ec0cb2d80e32389 /src/c-stdaux
parentbba2e4b4de668db525cbfdfc35292e5a0b51671a (diff)
New upstream version 1.47.90 upstream/1.47.90
Diffstat (limited to 'src/c-stdaux')
-rw-r--r--src/c-stdaux/.editorconfig11
-rw-r--r--src/c-stdaux/.github/workflows/ci.yml37
-rw-r--r--src/c-stdaux/.readthedocs.yaml20
-rw-r--r--src/c-stdaux/AUTHORS43
-rw-r--r--src/c-stdaux/NEWS.md106
-rw-r--r--src/c-stdaux/README.md54
-rw-r--r--src/c-stdaux/meson.build110
-rw-r--r--src/c-stdaux/meson_options.txt7
-rw-r--r--src/c-stdaux/src/docs/api.rst5
-rw-r--r--src/c-stdaux/src/docs/conf.py45
-rw-r--r--src/c-stdaux/src/docs/index.rst14
-rw-r--r--src/c-stdaux/src/docs/requirements.txt3
-rw-r--r--src/c-stdaux/src/libcstdaux.sym6
-rw-r--r--src/c-stdaux/src/meson.build43
-rw-r--r--src/c-stdaux/src/test-api.c322
-rw-r--r--src/c-stdaux/src/test-basic.c624
16 files changed, 1450 insertions, 0 deletions
diff --git a/src/c-stdaux/.editorconfig b/src/c-stdaux/.editorconfig
new file mode 100644
index 00000000..b10bb4f3
--- /dev/null
+++ b/src/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/src/c-stdaux/.github/workflows/ci.yml b/src/c-stdaux/.github/workflows/ci.yml
new file mode 100644
index 00000000..70a673f7
--- /dev/null
+++ b/src/c-stdaux/.github/workflows/ci.yml
@@ -0,0 +1,37 @@
+name: Continuous Integration
+
+on:
+  push:
+  pull_request:
+  schedule:
+  - cron:  '0 0 * * *'
+
+jobs:
+  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-windows:
+    name: Windows CI
+    uses: bus1/cabuild/.github/workflows/ci-c-util.yml@v1
+    with:
+      cabuild_ref: "v1"
+      linux: false
+      windows: true
+  ci-docs:
+    name: Documentation CI
+    uses: bus1/cabuild/.github/workflows/ci-sphinx.yml@v1
+    with:
+      source: "./src/docs"
diff --git a/src/c-stdaux/.readthedocs.yaml b/src/c-stdaux/.readthedocs.yaml
new file mode 100644
index 00000000..5bbe6ac1
--- /dev/null
+++ b/src/c-stdaux/.readthedocs.yaml
@@ -0,0 +1,20 @@
+# Read the Docs configuration file
+
+version: 2
+
+build:
+  apt_packages:
+  - "clang"
+  os: "ubuntu-22.04"
+  tools:
+    python: "3"
+
+formats: "all"
+
+python:
+  install:
+  - requirements: "src/docs/requirements.txt"
+  system_packages: true
+
+sphinx:
+  configuration: "src/docs/conf.py"
diff --git a/src/c-stdaux/AUTHORS b/src/c-stdaux/AUTHORS
new file mode 100644
index 00000000..0f2a73fd
--- /dev/null
+++ b/src/c-stdaux/AUTHORS
@@ -0,0 +1,43 @@
+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-2022 Red Hat, Inc.
+
+AUTHORS: (ordered alphabetically)
+        David Rheinsberg <david.rheinsberg@gmail.com>
+        Evgeny Vereshchagin <evvers@ya.ru>
+        Jan Engelhardt <jengelh@inai.de>
+        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>
diff --git a/src/c-stdaux/NEWS.md b/src/c-stdaux/NEWS.md
new file mode 100644
index 00000000..8e7b3c71
--- /dev/null
+++ b/src/c-stdaux/NEWS.md
@@ -0,0 +1,106 @@
+# c-stdaux - Auxiliary macros and functions for the C standard library
+
+## CHANGES WITH 1.4.0:
+
+        * New compiler-builtin c_assume_aligned() allows hinting alignment
+          to the compiler and thus improving code generation. For targets
+          without such builtins, the function will be a no-op.
+
+        * A new set of memory-load operations is added: c_load_*()
+          This includes support for reading unaligned & aligned memory,
+          big-endian & little-endian data, and various standard sizes.
+          The helpers are basically a pointer cast to `uintX_t*` and a
+          dereference operation, but they guarantee that strict aliasing
+          rules, as well as alignment requirements are followed.
+
+        Contributions from: David Rheinsberg, Jan Engelhardt, Tom Gundersen
+
+        - Dußlingen, 2023-01-12
+
+## CHANGES WITH 1.3.0:
+
+        * Microsoft Windows is now supported as a target platform.
+
+        * The `C_COMPILER_*` and `C_OS_*` pre-processor constants now
+          allow identifying the used compiler as well as the target OS.
+
+        * The new `_c_always_inline_` annotation allows telling compilers
+          to inline a function unless technically not possible.
+
+        * Split c-stdaux.h into modules and include them from the root
+          header for backwards compatibility. Inclusion of the new modules
+          is guarded by the `C_COMPILER_*` and `C_OS_*` macros to prevent
+          them from being used on unspported platforms. A direct include
+          of the respective modules allows overriding that behavior.
+
+          The new modules provide the same functionality as before on the
+          previously supported linux platforms. With the support of other
+          platforms, individual modules might not be available, or generic
+          functions might provide a stub that provides the same runtime
+          behavior, but possibly with fewer diagnostics.
+
+        * Rework `c_assert()` to avoid context-expressions and instead use
+          the ternary-operator to check for the assertion.
+
+        * Improve `c_{un,}likely()` to support constant-folding as well as
+          -Wparantheses diagnostics if supported by the compiler. This adds
+          `_c_boolean_expr_()` as a helper to achieve this.
+
+        Contributions from: David Rheinsberg, Thomas Haller
+
+        - Dußlingen, 2022-12-15
+
+## CHANGES WITH 1.2.0:
+
+        * Add c_memcmp() as a safe wrapper around memcmp(3) that supports
+          empty arenas as NULL pointers.
+
+        * Add an API documentation renderer based on the sphinx docutils
+          suite. The documentation is available on readthedocs.org.
+
+        * Drop stdatomic.h from the public includes. This was not used by
+          any of the dependent projects, but breaks builds on older GCC
+          compilers. While this is technically an API break, no breakage
+          has been discovered in our tests, and thus we deemed it reasonable
+          to proceed without version bump.
+
+        Contributions from: David Rheinsberg, Thomas Haller
+
+        - Dußlingen, 2022-07-22
+
+## 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.
+
+        Contributions from: David Rheinsberg, Lorenzo Arena, Michele Dionisio,
+                            Yuri Chornoivan
+
+        - Dußlingen, 2022-05-12
diff --git a/src/c-stdaux/README.md b/src/c-stdaux/README.md
new file mode 100644
index 00000000..985193c5
--- /dev/null
+++ b/src/c-stdaux/README.md
@@ -0,0 +1,54 @@
+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 widespread compilers like gcc and clang.
+
+### Project
+
+ * **Website**: <https://c-util.github.io/c-stdaux>
+ * **Documentation**: <https://c-stdaux.readthedocs.io>
+ * **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.60`
+ * `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/src/c-stdaux/meson.build b/src/c-stdaux/meson.build
new file mode 100644
index 00000000..6af822be
--- /dev/null
+++ b/src/c-stdaux/meson.build
@@ -0,0 +1,110 @@
+#
+# Global Project Setup
+#
+
+project(
+        'c-stdaux',
+        'c',
+        default_options: [
+                'c_std=c11'
+        ],
+        license: 'Apache',
+        meson_version: '>=0.60.0',
+        version: '1.4.0',
+)
+major = meson.project_version().split('.')[0]
+project_description = 'Auxiliary macros and functions for the C standard library'
+
+mod_pkgconfig = import('pkgconfig')
+
+#
+# CFLAGS
+#
+# We have a set of compiler flags for GCC and CLANG which adjust their warnings
+# and behavior to our coding-style.
+#
+# This variable is exported to dependent projects via meson for them to use as
+# 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',
+
+        # Prevent CLANG from complaining that alignof-expressions are GNU-only.
+        '-Wno-gnu-alignof-expression',
+        # Never complain about *MAYBE* uninit variables. This is very flaky and
+        # produces bogus results with LTO.
+        '-Wno-maybe-uninitialized',
+        # Do not complain about unknown GCC/CLANG warnings.
+        '-Wno-unknown-warning-option',
+        # There is no standardized way to mark unused arguments, so never
+        # complain about them.
+        '-Wno-unused-parameter',
+
+        # Preprocessor evaluations often lead to warnings about comparisons
+        # that are always true/false. Make sure they do not break a build but
+        # keep them on for diagnostics.
+        '-Wno-error=type-limits',
+        # As we use designated field-initializers, this warning should never
+        # trigger, but still does on GCC in combination with some other
+        # preprocessor checks. Lets just make sure it does not break builds.
+        '-Wno-error=missing-field-initializers',
+
+        # Warn if we ever use `__DATE__` and similar in our build. We want
+        # reproducible builds.
+        '-Wdate-time',
+        # We strictly follow decl-before-statements, so check it.
+        '-Wdeclaration-after-statement',
+        # More strict logical-op sanity checks.
+        '-Wlogical-op',
+        # Loudly complain about missing include-directories.
+        '-Wmissing-include-dirs',
+        # We want hints about noreturn functions, so warn about them.
+        '-Wmissing-noreturn',
+        # Warn if an extern-decl is inside a function. We want imports as
+        # global attributes, never as local ones.
+        '-Wnested-externs',
+        # Warn about redundant declarations. We want declarations in headers
+        # and want them to be unique.
+        '-Wredundant-decls',
+        # Warn about shadowed variables so we do not accidentally override
+        # variables of parent scopes and thus confuse macros.
+        '-Wshadow',
+        # Warn about aliasing violations. Level-3 produces the least false
+        # positives, but is the slowest. Force it to avoid breaking -Werror
+        # builds.
+        '-Wstrict-aliasing=3',
+        # Suggest 'noreturn' attributes. They are useful, we want them!
+        '-Wsuggest-attribute=noreturn',
+        # Warn about undefined identifiers in preprocessor conditionals.
+        '-Wundef',
+        # Make sure literal strings are considered 'const'.
+        '-Wwrite-strings',
+)
+assert(not ''.join(cflags).contains(' '), 'Malformed compiler flags.')
+add_project_arguments(cflags, language: 'c')
+
+#
+# Version Scripts
+#
+
+use_version_scripts = get_option('version-scripts')
+if use_version_scripts == 'auto'
+        use_version_scripts = meson.get_compiler('c').has_link_argument(
+                '-Wl,--version-script=' + (meson.current_source_dir() / 'src/libcstdaux.sym')
+        ) ? 'yes' : 'no'
+endif
+
+#
+# Subdir Delegation
+#
+
+subdir('src')
+
+#
+# Meson Subproject Configuration
+#
+
+meson.override_dependency('libcstdaux-'+major, libcstdaux_dep, static: true)
diff --git a/src/c-stdaux/meson_options.txt b/src/c-stdaux/meson_options.txt
new file mode 100644
index 00000000..82fdaf78
--- /dev/null
+++ b/src/c-stdaux/meson_options.txt
@@ -0,0 +1,7 @@
+option(
+        'version-scripts',
+        choices: ['yes', 'no', 'auto'],
+        description: 'Enable GNU-version-scripts for linking',
+        type: 'combo',
+        value: 'auto',
+)
diff --git a/src/c-stdaux/src/docs/api.rst b/src/c-stdaux/src/docs/api.rst
new file mode 100644
index 00000000..a25e9fad
--- /dev/null
+++ b/src/c-stdaux/src/docs/api.rst
@@ -0,0 +1,5 @@
+API
+===
+
+.. c:autodoc:: c-stdaux.h c-stdaux-generic.h c-stdaux-gnuc.h c-stdaux-unix.h
+   :transform: kerneldoc
diff --git a/src/c-stdaux/src/docs/conf.py b/src/c-stdaux/src/docs/conf.py
new file mode 100644
index 00000000..b036efa5
--- /dev/null
+++ b/src/c-stdaux/src/docs/conf.py
@@ -0,0 +1,45 @@
+#
+# 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_clang += ["-I" + os.path.abspath("..")]
+cautodoc_clang += ["-DC_COMPILER_DOCS"]
+
+cautodoc_root = os.path.abspath('..')
+
+cautodoc_transformations = {
+    'kerneldoc': capidocs.kerneldoc.hawkmoth_converter,
+}
+
+# HTML Options
+
+html_theme = 'sphinx_rtd_theme'
diff --git a/src/c-stdaux/src/docs/index.rst b/src/c-stdaux/src/docs/index.rst
new file mode 100644
index 00000000..5ad99374
--- /dev/null
+++ b/src/c-stdaux/src/docs/index.rst
@@ -0,0 +1,14 @@
+Introduction
+============
+
+The **c-stdaux** project contains support-macros and auxiliary functions around
+the functionality of common C standard libraries. This includes helpers for the
+ISO-C Standard Library, but also other common specifications like POSIX or
+common extended features of wide-spread compilers like gcc and clang.
+
+.. toctree::
+   :caption: Library Documentation
+   :hidden:
+
+   self
+   api
diff --git a/src/c-stdaux/src/docs/requirements.txt b/src/c-stdaux/src/docs/requirements.txt
new file mode 100644
index 00000000..97872db1
--- /dev/null
+++ b/src/c-stdaux/src/docs/requirements.txt
@@ -0,0 +1,3 @@
+c-apidocs>=0.0.2
+clang>=6
+hawkmoth>=0.7
diff --git a/src/c-stdaux/src/libcstdaux.sym b/src/c-stdaux/src/libcstdaux.sym
new file mode 100644
index 00000000..2073665f
--- /dev/null
+++ b/src/c-stdaux/src/libcstdaux.sym
@@ -0,0 +1,6 @@
+LIBCSTDAUX_1 {
+global:
+        c_internal_dummy;
+local:
+       *;
+};
diff --git a/src/c-stdaux/src/meson.build b/src/c-stdaux/src/meson.build
new file mode 100644
index 00000000..f0efa90b
--- /dev/null
+++ b/src/c-stdaux/src/meson.build
@@ -0,0 +1,43 @@
+#
+# 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_vars = {
+        'cflags': ' '.join(cflags),
+        'version-scripts': use_version_scripts,
+}
+
+libcstdaux_dep = declare_dependency(
+        include_directories: include_directories('.'),
+        variables: libcstdaux_vars,
+        version: meson.project_version(),
+)
+
+if not meson.is_subproject()
+        install_headers(
+                'c-stdaux.h',
+                'c-stdaux-generic.h',
+                'c-stdaux-gnuc.h',
+                'c-stdaux-unix.h',
+        )
+
+        mod_pkgconfig.generate(
+                description: project_description,
+                filebase: 'libcstdaux-'+major,
+                name: 'libcstdaux',
+                unescaped_variables: libcstdaux_vars,
+                version: meson.project_version(),
+        )
+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/c-stdaux/src/test-api.c b/src/c-stdaux/src/test-api.c
new file mode 100644
index 00000000..e52d42c2
--- /dev/null
+++ b/src/c-stdaux/src/test-api.c
@@ -0,0 +1,322 @@
+/*
+ * 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"
+
+#if defined(C_MODULE_GENERIC)
+
+static inline _c_always_inline_ int always_inline_fn(void) { return 0; }
+_c_public_ int c_internal_public_fn(void);
+_c_public_ int c_internal_public_fn(void) { return 0; }
+
+static void cleanup_fn(int p) { (void)p; }
+static void direct_cleanup_fn(int p) { (void)p; }
+C_DEFINE_CLEANUP(int, cleanup_fn);
+C_DEFINE_DIRECT_CLEANUP(int, direct_cleanup_fn);
+
+static void test_api_generic(void) {
+        /* C_COMPILER_* */
+        {
+#ifdef __clang__
+                c_assert(C_COMPILER_CLANG);
+#endif
+#ifdef __GNUC__
+                c_assert(C_COMPILER_GNUC);
+#endif
+#ifdef _MSC_VER
+                c_assert(C_COMPILER_MSVC);
+#endif
+        }
+
+        /* C_OS_* */
+        {
+#ifdef __linux__
+                c_assert(C_OS_LINUX);
+#endif
+#ifdef __APPLE__
+                c_assert(C_OS_MACOS);
+#endif
+#ifdef _WIN32
+                c_assert(C_OS_WINDOWS);
+#endif
+        }
+
+        /* _c_always_inline_ */
+        {
+                c_assert(!always_inline_fn());
+        }
+
+        /* _c_boolean_expr_ */
+        {
+                c_assert(_c_boolean_expr_(true));
+        }
+
+        /* _c_likely_ */
+        {
+                c_assert(_c_likely_(true));
+        }
+
+        /* _c_public_ */
+        {
+                c_assert(!c_internal_public_fn());
+        }
+
+        /* _c_unlikely_ */
+        {
+                c_assert(!_c_unlikely_(false));
+        }
+
+        /* 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_assume_aligned */
+        {
+                _Alignas(16) uint8_t data[8] = { 0 };
+
+                c_assert(c_assume_aligned(data, 16, 0));
+        }
+
+        /* c_assert */
+        {
+                c_assert(true);
+        }
+
+        /* c_load */
+        {
+                uint64_t data[128] = { 0 };
+
+                c_assert(c_load(uint64_t, le, aligned, data, 0) == 0);
+        }
+
+        /* C_DEFINE_CLEANUP / C_DEFINE_DIRECT_CLEANUP */
+        {
+                int v = 0;
+
+                cleanup_fnp(&v);
+                direct_cleanup_fnp(&v);
+        }
+
+        /* test availability of C symbols */
+        {
+                void *fns[] = {
+                        (void *)c_errno,
+                        (void *)c_memset,
+                        (void *)c_memzero,
+                        (void *)c_memcpy,
+                        (void *)c_load_8,
+                        (void *)c_load_16be_unaligned,
+                        (void *)c_load_16be_aligned,
+                        (void *)c_load_16le_unaligned,
+                        (void *)c_load_16le_aligned,
+                        (void *)c_load_32be_unaligned,
+                        (void *)c_load_32be_aligned,
+                        (void *)c_load_32le_unaligned,
+                        (void *)c_load_32le_aligned,
+                        (void *)c_load_64be_unaligned,
+                        (void *)c_load_64be_aligned,
+                        (void *)c_load_64le_unaligned,
+                        (void *)c_load_64le_aligned,
+                        (void *)c_free,
+                        (void *)c_fclose,
+                        (void *)c_freep,
+                        (void *)c_fclosep,
+                };
+                size_t i;
+
+                for (i = 0; i < sizeof(fns) / sizeof(*fns); ++i)
+                        c_assert(!!fns[i]);
+        }
+}
+
+#else /* C_MODULE_GENERIC */
+
+static void test_api_generic(void) {
+}
+
+#endif /* C_MODULE_GENERIC */
+
+#if defined(C_MODULE_GNUC)
+
+static _c_const_ int const_fn(void) { return 0; }
+static _c_deprecated_ _c_unused_ int deprecated_fn(void) { return 0; }
+_c_hidden_ int c_internal_hidden_fn(void);
+_c_hidden_ int c_internal_hidden_fn(void) { return 0; }
+static _c_printf_(1, 2) int printf_fn(const _c_unused_ char *f, ...) { return 0; }
+static _c_pure_ int pure_fn(void) { return 0; }
+static _c_sentinel_ int sentinel_fn(const _c_unused_ char *f, ...) { return 0; }
+static _c_unused_ int unused_fn(void) { return 0; }
+
+static void test_api_gnuc(void) {
+        /* _c_cleanup_ */
+        {
+                _c_cleanup_(c_freep) void *foo = NULL;
+                c_assert(!foo);
+        }
+
+        /* _c_const_ */
+        {
+                c_assert(!const_fn());
+        }
+
+        /* _c_deprecated_ */
+        {
+                /* see deprecated_fn() */
+        }
+
+        /* _c_hidden_ */
+        {
+                c_assert(!c_internal_hidden_fn());
+        }
+
+        /* _c_packed_ */
+        {
+                struct _c_packed_ FooBar {
+                        int member;
+                } foobar = {};
+
+                c_assert(!foobar.member);
+        }
+
+        /* _c_printf_ */
+        {
+                c_assert(!printf_fn("%d", 1));
+        }
+
+        /* _c_pure_ */
+        {
+                c_assert(!pure_fn());
+        }
+
+        /* _c_sentinel_ */
+        {
+                c_assert(!sentinel_fn("", NULL));
+        }
+
+        /* _c_unused_ */
+        {
+                c_assert(!unused_fn());
+        }
+
+        /* C_EXPR_ASSERT */
+        {
+                int v = C_EXPR_ASSERT(0, true, "");
+
+                c_assert(!v);
+        }
+
+        /* C_CC_MACRO1, C_CC_MACRO2, C_CC_MACRO3 */
+        {
+#define MACRO_REAL(_x1, _x2, _x3) ((_x1 + _x2 + _x3) * 0)
+#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);
+        }
+}
+
+#else /* C_MODULE_GNUC */
+
+static void test_api_gnuc(void) {
+}
+
+#endif /* C_MODULE_GNUC */
+
+#if defined(C_MODULE_UNIX)
+
+static void test_api_unix(void) {
+        /* test availability of C symbols */
+        {
+                void *fns[] = {
+                        (void *)c_close,
+                        (void *)c_closedir,
+                        (void *)c_closep,
+                        (void *)c_closedirp,
+                };
+                size_t i;
+
+                for (i = 0; i < sizeof(fns) / sizeof(*fns); ++i)
+                        c_assert(!!fns[i]);
+        }
+}
+
+#else /* C_MODULE_UNIX */
+
+static void test_api_unix(void) {
+}
+
+#endif /* C_MODULE_UNIX */
+
+int main(void) {
+        test_api_generic();
+        test_api_gnuc();
+        test_api_unix();
+        return 0;
+}
diff --git a/src/c-stdaux/src/test-basic.c b/src/c-stdaux/src/test-basic.c
new file mode 100644
index 00000000..1d16a828
--- /dev/null
+++ b/src/c-stdaux/src/test-basic.c
@@ -0,0 +1,624 @@
+/*
+ * 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 "c-stdaux.h"
+
+#if defined(C_MODULE_GENERIC)
+
+static int check_cassert_unreachable(int switch_val) {
+    int result;
+
+    /* Check whether this triggers a "-Wsometimes-uninitialized" warning or
+     * whether the compiler recognizes c_assert(0) as unreachable code. */
+    switch (switch_val) {
+    case 1: result = 1; break;
+    case 2: result = 2; break;
+    default: c_assert(0);
+    }
+
+    return result;
+}
+
+static void test_basic_generic(int non_constant_expr) {
+        /*
+         * Verify `_c_boolean_expr_` evaluates expressions to a boolean value
+         * and correctly works on all platforms.
+         */
+        {
+                int v = 0;
+
+                c_assert(_c_boolean_expr_(0) == 0);
+                c_assert(_c_boolean_expr_(1) == 1);
+                c_assert(_c_boolean_expr_(2) == 1);
+                c_assert(_c_boolean_expr_(INT_MIN) == 1);
+                c_assert(_c_boolean_expr_(INT_MAX) == 1);
+
+                /* verify no double-evaluation takes place */
+                c_assert(_c_boolean_expr_(v++) == 0);
+                c_assert(_c_boolean_expr_(v) == 1);
+
+#if defined(C_COMPILER_GNUC)
+                c_assert(__builtin_constant_p(_c_boolean_expr_(1)));
+                c_assert(!__builtin_constant_p(_c_boolean_expr_(non_constant_expr)));
+#endif
+        }
+
+        /*
+         * Test that _c_likely_() and _c_unlikely_() can deal with constant
+         * expressions.
+         */
+        {
+#if defined(C_COMPILER_GNUC)
+                c_assert(__builtin_constant_p(_c_likely_(1)));
+                c_assert(__builtin_constant_p(_c_unlikely_(1)));
+                c_assert(!__builtin_constant_p(_c_likely_(non_constant_expr)));
+                c_assert(!__builtin_constant_p(_c_unlikely_(non_constant_expr)));
+#endif
+        }
+
+        /*
+         * 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);
+                }
+                {
+                        /* 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);
+                }
+#if defined(C_MODULE_GNUC)
+                {
+                        /*
+                         * Make sure both produce different names, even though they're
+                         * exactly the same expression.
+                         */
+                        _c_unused_ int C_VAR(sub, __COUNTER__), C_VAR(sub, __COUNTER__);
+                }
+#endif
+        }
+
+#if defined(C_MODULE_GNUC)
+        /*
+         * Verify that c_free*() works as expected. Since we want to support
+         * running under valgrind, there is no easy way to verify the
+         * correctness of free(). Hence, we simply rely on valgrind to catch
+         * the leaks.
+         */
+        {
+                int i;
+
+                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);
+        }
+#endif
+
+#if defined(C_MODULE_UNIX)
+        /*
+         * Test c_fclose() and c_fclosep(). This uses the same logic as the
+         * tests for c_close() (i.e., sparse FD allocation).
+         */
+        {
+                int r, i, fd, tmp[2];
+                FILE *f;
+
+                r = pipe(tmp);
+                c_assert(r >= 0);
+                fd = tmp[0];
+                c_close(tmp[1]);
+
+                f = fdopen(fd, "r");
+                c_assert(f);
+
+                /* verify c_fclose() returns NULL */
+                f = c_fclose(f);
+                c_assert(!f);
+
+                /* verify c_fclose() deals fine with NULL */
+                c_assert(!c_fclose(NULL));
+
+                /* make sure c_flosep() deals fine with NULL */
+                {
+                        _c_cleanup_(c_fclosep) _c_unused_ FILE *t = (void *)0xdeadbeef;
+                        t = NULL;
+                }
+
+                /*
+                 * Make sure the c_fclose() earlier worked, by allocating the
+                 * FD again and relying on the same FD number to be reused. Do
+                 * this twice, to verify that the c_fclosep() in the cleanup
+                 * path works as well.
+                 */
+                for (i = 0; i < 2; ++i) {
+                        _c_cleanup_(c_fclosep) _c_unused_ FILE *t = NULL;
+                        int tfd;
+
+                        r = pipe(tmp);
+                        c_assert(r >= 0);
+                        tfd = tmp[0];
+                        c_close(tmp[1]);
+
+                        c_assert(tfd == fd); /* the same as before */
+                        t = fdopen(tfd, "r");
+                        c_assert(t);
+                }
+        }
+#endif
+
+        /*
+         * 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();
+
+                /*
+                 * Use the `check_cassert_unreachable()` helper to verify the
+                 * compiler does not complain about unreachable code when
+                 * `c_assert(0)` is used.
+                 */
+                c_assert(check_cassert_unreachable(1) == 1);
+                c_assert(check_cassert_unreachable(2) == 2);
+        }
+
+        /*
+         * 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);
+
+                strtol("0xfffffffffffffffffffffffffffffffff", NULL, 0);
+                c_assert(errno == ERANGE);
+                c_assert(c_errno() == errno);
+
+                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);
+        }
+
+        /*
+         * Test c_memcmp() with.
+         */
+        {
+                uint64_t v1 = (uint64_t)-1, v2 = (uint64_t)0;
+
+                c_assert(c_memcmp(NULL, NULL, 0) == 0);
+                c_assert(c_memcmp(&v1, &v2, 0) == 0);
+                c_assert(c_memcmp(&v1, &v2, 8) != 0);
+        }
+
+        /*
+         * Test c_load*() and its mapping to c_load_*() functions.
+         */
+        {
+                _Alignas(8) uint8_t data[16] = {
+                        0, 0, 0, 0,
+                        0, 0, 0, 0,
+                        1, 2, 3, 4,
+                        5, 6, 7, 8,
+                };
+
+                c_assert(c_load_8(data, 7) == 0);
+                c_assert(c_load_8(data, 8) == 1);
+                c_assert(c_load(uint16_t, be, unaligned, data, 7) == UINT16_C(0x0001));
+                c_assert(c_load(uint16_t, be, aligned, data, 8) == UINT16_C(0x0102));
+                c_assert(c_load(uint16_t, le, unaligned, data, 7) == UINT16_C(0x0100));
+                c_assert(c_load(uint16_t, le, aligned, data, 8) == UINT16_C(0x0201));
+                c_assert(c_load(uint32_t, be, unaligned, data, 7) == UINT32_C(0x00010203));
+                c_assert(c_load(uint32_t, be, aligned, data, 8) == UINT32_C(0x01020304));
+                c_assert(c_load(uint32_t, le, unaligned, data, 7) == UINT32_C(0x03020100));
+                c_assert(c_load(uint32_t, le, aligned, data, 8) == UINT32_C(0x04030201));
+                c_assert(c_load(uint64_t, be, unaligned, data, 7) == UINT64_C(0x0001020304050607));
+                c_assert(c_load(uint64_t, be, aligned, data, 8) == UINT64_C(0x0102030405060708));
+                c_assert(c_load(uint64_t, le, unaligned, data, 7) == UINT64_C(0x0706050403020100));
+                c_assert(c_load(uint64_t, le, aligned, data, 8) == UINT64_C(0x0807060504030201));
+        }
+}
+
+#else /* C_MODULE_GENERIC */
+
+static void test_basic_generic(int non_constant_expr) {
+        (void)non_constant_expr;
+}
+
+#endif /* C_MODULE_GENERIC */
+
+#if defined(C_MODULE_GNUC)
+
+static void test_basic_gnuc(int non_constant_expr) {
+        /*
+         * 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 array-size helper. This simply computes the number of elements
+         * of an array, instead of the binary size.
+         */
+        {
+                int bar[8];
+
+                static_assert(C_ARRAY_SIZE(bar) == 8, "");
+                c_assert(__builtin_constant_p(C_ARRAY_SIZE(bar)));
+        }
+
+        /*
+         * Test decimal-representation calculator. Make sure it is
+         * type-independent and just uses the size of the type to calculate how
+         * many bytes are needed to print that integer in decimal form. Also
+         * verify that it is a constant expression.
+         */
+        {
+                static_assert(C_DECIMAL_MAX(char) == 4, "");
+                static_assert(C_DECIMAL_MAX(signed char) == 4, "");
+                static_assert(C_DECIMAL_MAX(unsigned char) == 4, "");
+                static_assert(C_DECIMAL_MAX(unsigned long) == (sizeof(long) == 8 ? 21 : 11), "");
+                static_assert(C_DECIMAL_MAX(unsigned long long) == 21, "");
+                static_assert(C_DECIMAL_MAX(int32_t) == 11, "");
+                static_assert(C_DECIMAL_MAX(uint32_t) == 11, "");
+                static_assert(C_DECIMAL_MAX(uint64_t) == 21, "");
+        }
+
+        /*
+         * Test c_container_of(). We cannot test for type-safety, nor for
+         * other invalid uses, as they'd require negative compile-testing.
+         * However, we can test that the macro yields the correct values under
+         * normal use.
+         */
+        {
+                struct foobar {
+                        int a;
+                        char b;
+                } sub = {};
+
+                c_assert(&sub == c_container_of(&sub.a, struct foobar, a));
+                c_assert(&sub == c_container_of(&sub.b, struct foobar, b));
+                c_assert(&sub == c_container_of((const char *)&sub.b, struct foobar, b));
+
+                c_assert(!c_container_of(NULL, struct foobar, b));
+        }
+
+        /*
+         * Test min/max macros. Especially check that macro arguments are never
+         * evaluated multiple times, and if both arguments are constant, the
+         * return value is constant as well.
+         */
+        {
+                int foo;
+
+                foo = 0;
+                c_assert(c_max(1, 5) == 5);
+                c_assert(c_max(-1, 5) == 5);
+                c_assert(c_max(-1, -5) == -1);
+                c_assert(c_max(foo++, -1) == 0);
+                c_assert(foo == 1);
+                c_assert(c_max(foo++, foo++) > 0);
+                c_assert(foo == 3);
+
+                c_assert(__builtin_constant_p(c_max(1, 5)));
+                c_assert(!__builtin_constant_p(c_max(1, non_constant_expr)));
+
+                foo = 0;
+                c_assert(c_min(1, 5) == 1);
+                c_assert(c_min(-1, 5) == -1);
+                c_assert(c_min(-1, -5) == -5);
+                c_assert(c_min(foo++, 1) == 0);
+                c_assert(foo == 1);
+                c_assert(c_min(foo++, foo++) > 0);
+                c_assert(foo == 3);
+
+                c_assert(__builtin_constant_p(c_min(1, 5)));
+                c_assert(!__builtin_constant_p(c_min(1, non_constant_expr)));
+        }
+
+        /*
+         * Test c_less_by(), c_clamp(). Make sure they
+         * evaluate arguments exactly once, and yield a constant expression,
+         * if all arguments are constant.
+         */
+        {
+                int foo;
+
+                foo = 8;
+                c_assert(c_less_by(1, 5) == 0);
+                c_assert(c_less_by(5, 1) == 4);
+                c_assert(c_less_by(foo++, 1) == 7);
+                c_assert(foo == 9);
+                c_assert(c_less_by(foo++, foo++) >= 0);
+                c_assert(foo == 11);
+
+                c_assert(__builtin_constant_p(c_less_by(1, 5)));
+                c_assert(!__builtin_constant_p(c_less_by(1, non_constant_expr)));
+
+                foo = 8;
+                c_assert(c_clamp(foo, 1, 5) == 5);
+                c_assert(c_clamp(foo, 9, 20) == 9);
+                c_assert(c_clamp(foo++, 1, 5) == 5);
+                c_assert(foo == 9);
+                c_assert(c_clamp(foo++, foo++, foo++) >= 0);
+                c_assert(foo == 12);
+
+                c_assert(__builtin_constant_p(c_clamp(0, 1, 5)));
+                c_assert(!__builtin_constant_p(c_clamp(1, 0, non_constant_expr)));
+        }
+
+        /*
+         * Div Round Up: Normal division, but round up to next integer, instead
+         * of clipping. Also verify that it does not suffer from the integer
+         * overflow in the prevalent, alternative implementation:
+         *      [(x + y - 1) / y].
+         */
+        {
+                int i, j, foo;
+
+#define TEST_ALT_DIV(_x, _y) (((_x) + (_y) - 1) / (_y))
+                foo = 8;
+                c_assert(c_div_round_up(0, 5) == 0);
+                c_assert(c_div_round_up(1, 5) == 1);
+                c_assert(c_div_round_up(5, 5) == 1);
+                c_assert(c_div_round_up(6, 5) == 2);
+                c_assert(c_div_round_up(foo++, 1) == 8);
+                c_assert(foo == 9);
+                c_assert(c_div_round_up(foo++, foo++) >= 0);
+                c_assert(foo == 11);
+
+                c_assert(__builtin_constant_p(c_div_round_up(1, 5)));
+                c_assert(!__builtin_constant_p(c_div_round_up(1, non_constant_expr)));
+
+                /* alternative calculation is [(x + y - 1) / y], but it may overflow */
+                for (i = 0; i <= 0xffff; ++i) {
+                        for (j = 1; j <= 0xff; ++j)
+                                c_assert(c_div_round_up(i, j) == TEST_ALT_DIV(i, j));
+                        for (j = 0xff00; j <= 0xffff; ++j)
+                                c_assert(c_div_round_up(i, j) == TEST_ALT_DIV(i, j));
+                }
+
+                /* make sure it doesn't suffer from high overflow */
+                c_assert(UINT32_C(0xfffffffa) % 10 == 0);
+                c_assert(UINT32_C(0xfffffffa) / 10 == UINT32_C(429496729));
+                c_assert(c_div_round_up(UINT32_C(0xfffffffa), 10) == UINT32_C(429496729));
+                c_assert(TEST_ALT_DIV(UINT32_C(0xfffffffa), 10) == 0); /* overflow */
+
+                c_assert(UINT32_C(0xfffffffd) % 10 == 3);
+                c_assert(UINT32_C(0xfffffffd) / 10 == UINT32_C(429496729));
+                c_assert(c_div_round_up(UINT32_C(0xfffffffd), 10) == UINT32_C(429496730));
+                c_assert(TEST_ALT_DIV(UINT32_C(0xfffffffd), 10) == 0);
+#undef TEST_ALT_DIV
+        }
+
+        /*
+         * Align to multiple of: Test the alignment macro. Check that it does
+         * not suffer from incorrect integer overflows, neither should it
+         * exceed the boundaries of the input type.
+         */
+        {
+                c_assert(c_align_to(UINT32_C(0), 1) == 0);
+                c_assert(c_align_to(UINT32_C(0), 2) == 0);
+                c_assert(c_align_to(UINT32_C(0), 4) == 0);
+                c_assert(c_align_to(UINT32_C(0), 8) == 0);
+                c_assert(c_align_to(UINT32_C(1), 8) == 8);
+
+                c_assert(c_align_to(UINT32_C(0xffffffff), 8) == 0);
+                c_assert(c_align_to(UINT32_C(0xfffffff1), 8) == 0xfffffff8);
+                c_assert(c_align_to(UINT32_C(0xfffffff1), 8) == 0xfffffff8);
+
+                c_assert(__builtin_constant_p(c_align_to(16, 8)));
+                c_assert(!__builtin_constant_p(c_align_to(non_constant_expr, 8)));
+                c_assert(!__builtin_constant_p(c_align_to(16, non_constant_expr)));
+                c_assert(!__builtin_constant_p(c_align_to(16, non_constant_expr ? 8 : 16)));
+                c_assert(__builtin_constant_p(c_align_to(16, 7 + 1)));
+                c_assert(c_align_to(15, non_constant_expr ? 8 : 16) == 16);
+        }
+}
+
+#else /* C_MODULE_GNUC */
+
+static void test_basic_gnuc(int unused0) {
+        (void)unused0;
+}
+
+#endif /* C_MODULE_GNUC */
+
+#if defined(C_MODULE_UNIX)
+
+static void test_basic_unix(void) {
+        /*
+         * Test c_close*(), rely on sparse FD allocation. Make sure all the
+         * helpers actually close the fd, and cope fine with negative numbers.
+         */
+        {
+                int r, i, fd1, fd2, tmp[2];
+
+                r = pipe(tmp);
+                c_assert(r >= 0);
+                fd1 = tmp[0];
+                fd2 = tmp[1];
+
+                /* verify c_close() returns -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);
+                c_assert(c_close(-16) == -1);
+
+                /* make sure c_closep() deals fine with negative FDs */
+                {
+                        _c_cleanup_(c_closep) _c_unused_ 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) _c_unused_ int t1 = -1, t2 = -1;
+
+                        r = pipe(tmp);
+                        c_assert(r >= 0);
+                        t1 = tmp[0];
+                        t2 = tmp[1];
+
+                        c_assert(t1 == fd1);
+                        c_assert(t2 == fd2);
+                }
+        }
+}
+
+#else /* C_MODULE_UNIX */
+
+static void test_basic_unix(void) {
+}
+
+#endif /* C_MODULE_UNIX */
+
+int main(int argc, char **argv) {
+        (void)argv;
+        test_basic_generic(argc);
+        test_basic_gnuc(argc);
+        test_basic_unix();
+        return 0;
+}