summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Herrmann <dh.herrmann@gmail.com>2015-10-25 15:03:26 +0100
committerDavid Herrmann <dh.herrmann@gmail.com>2015-10-26 12:08:33 +0100
commit6d2c0c6e8e39e8cd7ae14a3cf27d9f2bd76749f2 (patch)
tree6736cb42dd360ce9b6477ac833bd4e23e0aae53c
parent0dcd56db5e8fef6ab9034aa9b521e010ffa283c5 (diff)
WIPwip
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
-rw-r--r--.gitignore30
-rw-r--r--.vimrc4
-rw-r--r--COPYING0
-rw-r--r--Makefile.am107
-rwxr-xr-xautogen.sh31
-rw-r--r--configure.ac145
-rw-r--r--m4/attributes.m477
-rw-r--r--src/bus1-macro.h629
-rw-r--r--src/bus1-sys.c32
-rw-r--r--src/bus1-sys.h49
-rw-r--r--src/bus1ctl.c22
-rw-r--r--src/test-macro.c392
-rw-r--r--src/test-perf.c230
13 files changed, 1748 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..08bc506
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,30 @@
+*.a
+*.la
+*.lo
+*.log
+*.o
+*.stamp
+*.swp
+*.trs
+*~
+
+.config.args
+.deps/
+.dirstamp
+.libs/
+/*.tar.xz
+Makefile
+Makefile.in
+autom4te.cache/
+aclocal.m4
+build-aux/
+bus1ctl
+config.h
+config.h.in
+config.log
+config.status
+configure
+m4/
+stamp-*
+test-macro
+test-perf
diff --git a/.vimrc b/.vimrc
new file mode 100644
index 0000000..366fbdc
--- /dev/null
+++ b/.vimrc
@@ -0,0 +1,4 @@
+" 'set exrc' in ~/.vimrc will read .vimrc from the current directory
+set tabstop=8
+set shiftwidth=8
+set expandtab
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/COPYING
diff --git a/Makefile.am b/Makefile.am
new file mode 100644
index 0000000..8923add
--- /dev/null
+++ b/Makefile.am
@@ -0,0 +1,107 @@
+# ------------------------------------------------------------------------------
+# main
+
+ACLOCAL_AMFLAGS = -I m4 ${ACLOCAL_FLAGS}
+AM_MAKEFLAGS = --no-print-directory
+AUTOMAKE_OPTIONS = color-tests parallel-tests
+
+GCC_COLORS ?= 'ooh, shiny!'
+export GCC_COLORS
+
+SUBDIRS = .
+
+# remove targets if the command fails
+.DELETE_ON_ERROR:
+
+# keep intermediate files
+.SECONDARY:
+
+# Keep the test-suite.log and Makefile around at all times
+.PRECIOUS: $(TEST_SUITE_LOG) Makefile
+
+CLEANFILES = $(BUILT_SOURCES)
+DISTCLEANFILES =
+EXTRA_DIST =
+BUILT_SOURCES =
+lib_LIBRARIES =
+noinst_LIBRARIES =
+bin_PROGRAMS =
+noinst_PROGRAMS =
+check_PROGRAMS =
+TESTS =
+default_tests =
+
+AM_CPPFLAGS = \
+ -include $(top_builddir)/config.h \
+ -I $(top_srcdir)/src \
+ -I $(top_builddir)/src \
+ $(OUR_CPPFLAGS)
+
+AM_CFLAGS = $(OUR_CFLAGS)
+AM_LDFLAGS = $(OUR_LDFLAGS)
+
+# ------------------------------------------------------------------------------
+# libbus1
+# XXX: for now static, should be made dynamic later on
+
+noinst_LIBRARIES += \
+ libbus1.a
+
+libbus1_a_SOURCES = \
+ src/bus1-macro.h \
+ src/bus1-sys.c \
+ src/bus1-sys.h
+
+libbus1_a_CFLAGS = \
+ $(AM_CFLAGS)
+
+# ------------------------------------------------------------------------------
+# bus1ctl
+
+bin_PROGRAMS += \
+ bus1ctl
+
+bus1ctl_SOURCES = \
+ src/bus1ctl.c
+
+bus1ctl_CFLAGS = \
+ $(AM_CFLAGS)
+
+bus1ctl_LDADD = \
+ libbus1.a
+
+# ------------------------------------------------------------------------------
+# test-macro
+
+default_tests += \
+ test-macro
+
+test_macro_SOURCES = \
+ src/test-macro.c
+
+test_macro_CFLAGS = \
+ $(AM_CFLAGS)
+
+test_macro_LDADD = \
+ libbus1.a
+
+# ------------------------------------------------------------------------------
+# test-perf
+
+default_tests += \
+ test-perf
+
+test_perf_SOURCES = \
+ src/test-perf.c
+
+test_perf_CFLAGS = \
+ $(AM_CFLAGS)
+
+test_perf_LDADD = \
+ libbus1.a
+
+# ------------------------------------------------------------------------------
+# test suite
+
+check_PROGRAMS += $(default_tests)
+TESTS += $(default_tests)
diff --git a/autogen.sh b/autogen.sh
new file mode 100755
index 0000000..d969f8d
--- /dev/null
+++ b/autogen.sh
@@ -0,0 +1,31 @@
+#!/bin/sh
+
+set -e
+
+oldpwd=$(pwd)
+topdir=$(dirname $0)
+cd $topdir
+
+autoreconf --force --install --symlink
+
+if [ -f "$topdir/.config.args" ]; then
+ args="$args $(cat $topdir/.config.args)"
+fi
+
+cd $oldpwd
+
+if [ "x$1" = "xc" ]; then
+ $topdir/configure --enable-debug $args
+ make clean
+elif [ "x$1" = "xl" ]; then
+ $topdir/configure CC=clang $args
+ make clean
+else
+ echo
+ echo "----------------------------------------------------------------"
+ echo "Initialized build system. For a common configuration please run:"
+ echo "----------------------------------------------------------------"
+ echo
+ echo "$topdir/configure $args"
+ echo
+fi
diff --git a/configure.ac b/configure.ac
new file mode 100644
index 0000000..5f9aaa1
--- /dev/null
+++ b/configure.ac
@@ -0,0 +1,145 @@
+# ------------------------------------------------------------------------------
+# main
+
+AC_PREREQ([2.64])
+
+AC_INIT([libbus1],
+ [1],
+ [http://www.github.com/bus1/libbus1],
+ [libbus1],
+ [http://www.github.com/bus1/libbus1])
+
+AC_CONFIG_SRCDIR([src/bus1-macro.h])
+AC_CONFIG_MACRO_DIR([m4])
+AC_CONFIG_HEADERS([config.h])
+AC_CONFIG_AUX_DIR([build-aux])
+
+AC_USE_SYSTEM_EXTENSIONS
+AC_SYS_LARGEFILE
+AM_MAINTAINER_MODE([enable])
+AM_INIT_AUTOMAKE([foreign 1.11 -Wall -Wno-portability silent-rules tar-pax no-dist-gzip dist-xz subdir-objects parallel-tests])
+AM_SILENT_RULES([yes])
+AC_CANONICAL_HOST
+AC_DEFINE_UNQUOTED([CANONICAL_HOST], "$host", [Canonical host string.])
+
+AC_PROG_MKDIR_P
+AC_PROG_LN_S
+AC_PROG_SED
+AC_PROG_GREP
+AC_PROG_AWK
+AC_PROG_RANLIB
+AC_PROG_CC_C99
+AM_PROG_AR
+
+AC_PATH_PROG([M4], [m4])
+AC_PATH_PROG([XSLTPROC], [xsltproc])
+
+AS_IF([! ln --relative --help > /dev/null 2>&1], [AC_MSG_ERROR([*** ln doesn't support --relative ***])])
+
+# ------------------------------------------------------------------------------
+# arguments
+
+AC_ARG_ENABLE(debug, AS_HELP_STRING([--enable-debug], [enable debug options]))
+
+# ------------------------------------------------------------------------------
+# toolchain
+
+CC_CHECK_FLAGS_APPEND([with_cflags], [CFLAGS], [\
+ -pipe \
+ -Wall \
+ -Wextra \
+ -Wno-inline \
+ -Wundef \
+ "-Wformat=2 -Wformat-security -Wformat-nonliteral" \
+ -Wlogical-op \
+ -Wsign-compare \
+ -Wmissing-include-dirs \
+ -Wold-style-definition \
+ -Wpointer-arith \
+ -Winit-self \
+ -Wdeclaration-after-statement \
+ -Wfloat-equal \
+ -Wsuggest-attribute=noreturn \
+ -Wmissing-prototypes \
+ -Wstrict-prototypes \
+ -Wredundant-decls \
+ -Wmissing-declarations \
+ -Wmissing-noreturn \
+ -Wshadow \
+ -Wendif-labels \
+ -Wstrict-aliasing=2 \
+ -Wwrite-strings \
+ -Wno-long-long \
+ -Wno-overlength-strings \
+ -Wno-unused-parameter \
+ -Wno-missing-field-initializers \
+ -Wno-unused-result \
+ -Werror=overflow \
+ -Wdate-time \
+ -Wnested-externs \
+ -ffast-math \
+ -fno-common \
+ -fdiagnostics-show-option \
+ -fno-strict-aliasing \
+ -fvisibility=hidden \
+ -ffunction-sections \
+ -fdata-sections \
+ -fstack-protector \
+ -fstack-protector-strong \
+ -fPIE \
+ --param=ssp-buffer-size=4])
+
+AS_CASE([$CC], [*clang*],
+ [CC_CHECK_FLAGS_APPEND([with_cppflags], [CPPFLAGS], [ \
+ -Wno-typedef-redefinition \
+ -Wno-gnu-variable-sized-type-not-at-end \
+ ])])
+
+CC_CHECK_FLAGS_APPEND([with_ldflags], [LDFLAGS], [\
+ -Wl,--as-needed \
+ -Wl,--no-undefined \
+ -Wl,--gc-sections \
+ -Wl,-z,relro \
+ -Wl,-z,now \
+ -pie])
+
+AS_IF([test "x$enable_debug" = "xyes"],
+ [
+ CC_CHECK_FLAGS_APPEND([with_cflags], [CFLAGS], [ \
+ -g \
+ -O0 \
+ -ftrapv])
+ ], [
+ CC_CHECK_FLAGS_APPEND([with_cppflags], [CPPFLAGS], [\
+ -Wp,-D_FORTIFY_SOURCE=2])
+ ])
+
+AC_SUBST([OUR_CFLAGS], "$with_cflags")
+AC_SUBST([OUR_CPPFLAGS], "$with_cppflags")
+AC_SUBST([OUR_LDFLAGS], "$with_ldflags")
+
+# ------------------------------------------------------------------------------
+# system features
+
+# This makes sure pkg.m4 is available.
+m4_pattern_forbid([^_?PKG_[A-Z_]+$],[*** pkg.m4 missing, please install pkg-config])
+
+# ------------------------------------------------------------------------------
+# report
+
+AC_CONFIG_FILES([Makefile])
+
+AC_OUTPUT
+AC_MSG_RESULT([
+ $PACKAGE_NAME $VERSION
+ debug: ${enable_debug}
+
+ prefix: ${prefix}
+ exec_prefix: ${exec_prefix}
+ includedir: ${includedir}
+ libdir: ${libdir}
+
+ CFLAGS: ${OUR_CFLAGS} ${CFLAGS}
+ CPPFLAGS: ${OUR_CPPFLAGS} ${CPPFLAGS}
+ LDFLAGS: ${OUR_LDFLAGS} ${LDFLAGS}
+])
diff --git a/m4/attributes.m4 b/m4/attributes.m4
new file mode 100644
index 0000000..e145f49
--- /dev/null
+++ b/m4/attributes.m4
@@ -0,0 +1,77 @@
+dnl Macros to check the presence of generic (non-typed) symbols.
+dnl Copyright (c) 2006-2008 Diego Pettenò <flameeyes@gmail.com>
+dnl Copyright (c) 2006-2008 xine project
+dnl Copyright (c) 2012 Lucas De Marchi <lucas.de.marchi@gmail.com>
+dnl
+dnl This program is free software; you can redistribute it and/or modify
+dnl it under the terms of the GNU General Public License as published by
+dnl the Free Software Foundation; either version 2, or (at your option)
+dnl any later version.
+dnl
+dnl This program is distributed in the hope that it will be useful,
+dnl but WITHOUT ANY WARRANTY; without even the implied warranty of
+dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+dnl GNU General Public License for more details.
+dnl
+dnl You should have received a copy of the GNU General Public License
+dnl along with this program; if not, write to the Free Software
+dnl Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+dnl 02110-1301, USA.
+dnl
+dnl As a special exception, the copyright owners of the
+dnl macro gives unlimited permission to copy, distribute and modify the
+dnl configure scripts that are the output of Autoconf when processing the
+dnl Macro. You need not follow the terms of the GNU General Public
+dnl License when using or distributing such scripts, even though portions
+dnl of the text of the Macro appear in them. The GNU General Public
+dnl License (GPL) does govern all other use of the material that
+dnl constitutes the Autoconf Macro.
+dnl
+dnl This special exception to the GPL applies to versions of the
+dnl Autoconf Macro released by this project. When you make and
+dnl distribute a modified version of the Autoconf Macro, you may extend
+dnl this special exception to the GPL to apply to your modified version as
+dnl well.
+
+dnl Check if FLAG in ENV-VAR is supported by compiler and append it
+dnl to WHERE-TO-APPEND variable. Note that we invert -Wno-* checks to
+dnl -W* as gcc cannot test for negated warnings.
+dnl CC_CHECK_FLAG_APPEND([WHERE-TO-APPEND], [ENV-VAR], [FLAG])
+
+AC_DEFUN([CC_CHECK_FLAG_APPEND], [
+ AC_CACHE_CHECK([if $CC supports flag $3 in envvar $2],
+ AS_TR_SH([cc_cv_$2_$3]),
+ [eval "AS_TR_SH([cc_save_$2])='${$2}'"
+ eval "AS_TR_SH([$2])='-Werror `echo "$3" | sed 's/^-Wno-/-W/'`'"
+ AC_LINK_IFELSE([AC_LANG_SOURCE([int main(void) { return 0; } ])],
+ [eval "AS_TR_SH([cc_cv_$2_$3])='yes'"],
+ [eval "AS_TR_SH([cc_cv_$2_$3])='no'"])
+ eval "AS_TR_SH([$2])='$cc_save_$2'"])
+
+ AS_IF([eval test x$]AS_TR_SH([cc_cv_$2_$3])[ = xyes],
+ [eval "$1='${$1} $3'"])
+])
+
+dnl CC_CHECK_FLAGS_APPEND([WHERE-TO-APPEND], [ENV-VAR], [FLAG1 FLAG2])
+AC_DEFUN([CC_CHECK_FLAGS_APPEND], [
+ for flag in $3; do
+ CC_CHECK_FLAG_APPEND($1, $2, $flag)
+ done
+])
+
+dnl Check if the flag is supported by linker (cacheable)
+dnl CC_CHECK_LDFLAGS([FLAG], [ACTION-IF-FOUND],[ACTION-IF-NOT-FOUND])
+AC_DEFUN([CC_CHECK_LDFLAGS], [
+ AC_CACHE_CHECK([if $CC supports $1 flag],
+ AS_TR_SH([cc_cv_ldflags_$1]),
+ [ac_save_LDFLAGS="$LDFLAGS"
+ LDFLAGS="$LDFLAGS $1"
+ AC_LINK_IFELSE([int main() { return 1; }],
+ [eval "AS_TR_SH([cc_cv_ldflags_$1])='yes'"],
+ [eval "AS_TR_SH([cc_cv_ldflags_$1])="])
+ LDFLAGS="$ac_save_LDFLAGS"
+ ])
+
+ AS_IF([eval test x$]AS_TR_SH([cc_cv_ldflags_$1])[ = xyes],
+ [$2], [$3])
+])
diff --git a/src/bus1-macro.h b/src/bus1-macro.h
new file mode 100644
index 0000000..1185326
--- /dev/null
+++ b/src/bus1-macro.h
@@ -0,0 +1,629 @@
+#pragma once
+
+/***
+ This file is part of bus1. See COPYING for details.
+
+ bus1 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.
+
+ bus1 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 bus1; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+/*
+ * Macros
+ * XXX: Add description
+ */
+
+#include <assert.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <sys/resource.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * We require:
+ * sizeof(void*) == sizeof(long)
+ * sizeof(long) == 4 || sizeof(long) == 8
+ * sizeof(int) == 4
+ * The linux kernel requires the same from the toolchain, so this should work
+ * just fine.
+ */
+#if __SIZEOF_POINTER__ != __SIZEOF_LONG__
+# error "sizeof(void*) != sizeof(long)"
+#elif __SIZEOF_LONG__ != 4 && __SIZEOF_LONG__ != 8
+# error "sizeof(long) != 4 && sizeof(long) != 8"
+#elif __SIZEOF_INT__ != 4
+# error "sizeof(int) != 4"
+#endif
+
+/*
+ * Shortcuts for gcc attributes. See GCC manual for details. We do not prefix
+ * them as they're 1-on-1 mappings to the GCC equivalents.
+ */
+#define _alloc_(...) __attribute__((alloc_size(__VA_ARGS__)))
+#define _cleanup_(_x) __attribute__((cleanup(_x)))
+#define _const_ __attribute__((const))
+#define _deprecated_ __attribute__((deprecated))
+#define _hidden_ __attribute__((visibility("hidden")))
+#define _likely_(_x) (__builtin_expect(!!(_x), 1))
+#define _malloc_ __attribute__((malloc))
+#define _packed_ __attribute__((packed))
+#define _printf_(_a, _b) __attribute__((format (printf, _a, _b)))
+#define _public_ __attribute__((visibility("default")))
+#define _pure_ __attribute__((pure))
+#define _sentinel_ __attribute__((sentinel))
+#define _unlikely_(_x) (__builtin_expect(!!(_x), 0))
+#define _unused_ __attribute__((unused))
+#define _weak_ __attribute__((weak))
+#define _weakref_(_x) __attribute__((weakref(#_x)))
+
+/**
+ * B1_TYPE_MATCH() - match two variables/types for unqualified equality
+ * @_a: first variable/type
+ * @_b: second variable/type
+ *
+ * Compare two types, or types of two variables, for equality. Note that type
+ * qualifiers are not respected by this comparison. Hence, only the actual
+ * underlying types are compared.
+ *
+ * Return: 1 if both unqualified types are equal, 0 if not.
+ */
+#define B1_TYPE_MATCH(_a, _b) __builtin_types_compatible_p(__typeof__(_a), __typeof__(_b))
+
+/**
+ * B1_TYPE_IS_ARRAY() - evaluate whether given variable is of an array type
+ * @_a: variable/type to evaluate
+ *
+ * This function checks whether a given variable is an array type. Note that
+ * the passed argument must either be an array or pointer, otherwise, this will
+ * generate a syntax error.
+ *
+ * Note that "&a[0]" degrades an array to a pointer, and as such compares
+ * unequal to "a" if it is an array. This is unique to array types.
+ *
+ * Return: 1 if it is an array type, 0 if not.
+ */
+#define B1_TYPE_IS_ARRAY(_a) (!B1_TYPE_MATCH(__typeof__(_a), &(*(__typeof__(_a)*)0)[0]))
+
+/**
+ * B1_CC_IF() - conditional expression at compile time
+ * @_cond: condition
+ * @_if: if-clause
+ * @_else: else-clause
+ *
+ * This is a compile-time if-else-statement. Depending on whether the constant
+ * expression @_cond is true or false, this evaluates to the passed clause. The
+ * other clause is *not* evaluated, however, it may be checked for syntax
+ * errors and *constant* expressions are evaluated.
+ *
+ * Return: Evaluates to either if-clause or else-clause, depending on whether
+ * the condition is true. The other clause is *not* evaluated.
+ */
+#define B1_CC_IF(_cond, _if, _else) __builtin_choose_expr(!!(_cond), _if, _else)
+
+/**
+ * B1_CC_IS_CONST() - check whether a value is known at compile time
+ * @_expr: expression
+ *
+ * This checks whether the value of @_expr is known at compile time. Note that
+ * a negative result does not mean that it is *NOT* known. However, it means
+ * that it cannot be guaranteed to be constant at compile time. Hence, false
+ * negatives are possible.
+ *
+ * This macro *always* evaluates to a constant expression, regardless whether
+ * the passed expression is constant.
+ *
+ * The passed in expression is *never* evaluated. Hence, it can safely be used
+ * in combination with B1_CC_IF() to avoid multiple evaluations of macro
+ * parameters.
+ *
+ * Return: 1 if constant, 0 if not.
+ */
+#define B1_CC_IS_CONST(_expr) __builtin_constant_p(_expr)
+
+/**
+ * B1_CC_ASSERT_MSG() - compile time assertion
+ * @_cond: condition
+ * @_msg: message to make the compiler print
+ *
+ * This is a compile-time assertion that can be used in any (constant)
+ * expression. If @_cond evalutes to true, this is equivalent to a void
+ * expression. If @_cond is false, this will cause a compiler error and print
+ * @_msg into the compile log.
+ *
+ * XXX: Find some gcc hack to print @_msg while keeping the macro a constant
+ * expression.
+ *
+ * Return: This macro evaluates to a void expression.
+ */
+#define B1_CC_ASSERT_MSG(_cond, _msg) ((void)B1_CC_ASSERT1_MSG((_cond), _msg))
+
+/**
+ * B1_CC_ASSERT1_MSG() - compile time assertion
+ * @_cond: condition
+ * @_msg: message to make the compiler print
+ *
+ * This is the same as B1_CC_ASSERT_MSG(), but evaluates to constant 1.
+ *
+ * Return: This macro evaluates to constant 1.
+ */
+#define B1_CC_ASSERT1_MSG(_cond, _msg) (sizeof(int[!(_cond) * -1]) * 0 + 1)
+
+/**
+ * B1_CC_ASSERT() - compile time assertion
+ * @_cond: condition
+ *
+ * Same as B1_CC_ASSERT_MSG() but prints the condition as error message.
+ *
+ * Return: This macro evaluates to a void expression.
+ */
+#define B1_CC_ASSERT(_cond) ((void)B1_CC_ASSERT1(_cond))
+
+/**
+ * B1_CC_ASSERT1() - compile time assertion
+ * @_cond: condition
+ *
+ * This is the same as B1_CC_ASSERT(), but evaluates to constant 1.
+ *
+ * Return: This macro evaluates to constant 1.
+ */
+#define B1_CC_ASSERT1(_cond) B1_CC_ASSERT1_MSG((_cond), #_cond)
+
+/**
+ * B1_CC_ASSERT_TO() - compile time assertion with explicit return value
+ * @_cond: condition to assert
+ * @_expr: expression to yield
+ *
+ * This is equivalent to B1_CC_ASSERT1(_cond), but yields a return value of
+ * @_expr, rather than constant 1.
+ *
+ * In case the compile-time assertion is false, this causes a compile-time
+ * error and *also* evaluates as a void expression (and as such usually causes
+ * a followup compile time error).
+ *
+ * Note that usually you'd do something like:
+ * (ASSERT(cond), expr)
+ * thus using the comma-operator to yield a specific value. However,
+ * suprisingly STD-C does *not* define the comma operator as constant
+ * expression. Hence, we have to use B1_CC_IF() to yield the same result.
+ *
+ * Return: This macro evaluates to @_expr.
+ */
+#define B1_CC_ASSERT_TO(_cond, _expr) B1_CC_IF(B1_CC_ASSERT1(_cond), (_expr), ((void)0))
+
+/**
+ * B1_CC_STATIC_ASSERT() - static compile time assertion
+ * @_cond: condition
+ *
+ * This is equivalent to B1_CC_ASSERT(), but evaluates to a statement instead
+ * of an expression. This allows usage in file-context, where STD-C does not
+ * allow plain expressions. If you need a custom compiler message to print, use
+ * static_assert() directly (it's STD-C11).
+ *
+ * Return: This macro evaluates to a statement.
+ */
+#define B1_CC_STATIC_ASSERT(_cond) static_assert((_cond), #_cond)
+
+/**
+ * B1_CC_STRINGIFY() - stringify a token, but evaluate it first
+ * @_x: token to evaluate and stringify
+ *
+ * Return: Evaluates to a constant string literal
+ */
+#define B1_CC_STRINGIFY(_x) B1_INTERNAL_CC_STRINGIFY(_x)
+#define B1_INTERNAL_CC_STRINGIFY(_x) #_x
+
+/**
+ * B1_CC_CONCATENATE() - concatenate two tokens, but evaluate them first
+ * @_x: first token
+ * @_y: second token
+ *
+ * Return: Evaluates to a constant identifier
+ */
+#define B1_CC_CONCATENATE(_x, _y) B1_INTERNAL_CC_CONCATENATE(_x, _y)
+#define B1_INTERNAL_CC_CONCATENATE(_x, _y) _x ## _y
+
+/**
+ * B1_CC_ARRAY_SIZE() - calculate number of array elements at compile time
+ * @_x: array to calculate size of
+ *
+ * Return: Evaluates to a constant integer expression
+ */
+#define B1_CC_ARRAY_SIZE(_x) B1_CC_ASSERT_TO(B1_TYPE_IS_ARRAY(_x), sizeof(_x) / sizeof((_x)[0]))
+
+/**
+ * B1_CC_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 B1_CC_DECIMAL_MAX(_type) \
+ _Generic((_type){ 0 }, \
+ char: B1_INTERNAL_CC_DECIMAL_MAX(_type), \
+ signed char: B1_INTERNAL_CC_DECIMAL_MAX(_type), \
+ unsigned char: B1_INTERNAL_CC_DECIMAL_MAX(_type), \
+ signed short: B1_INTERNAL_CC_DECIMAL_MAX(_type), \
+ unsigned short: B1_INTERNAL_CC_DECIMAL_MAX(_type), \
+ signed int: B1_INTERNAL_CC_DECIMAL_MAX(_type), \
+ unsigned int: B1_INTERNAL_CC_DECIMAL_MAX(_type), \
+ signed long: B1_INTERNAL_CC_DECIMAL_MAX(_type), \
+ unsigned long: B1_INTERNAL_CC_DECIMAL_MAX(_type), \
+ signed long long: B1_INTERNAL_CC_DECIMAL_MAX(_type), \
+ unsigned long long: B1_INTERNAL_CC_DECIMAL_MAX(_type))
+#define B1_INTERNAL_CC_DECIMAL_MAX(_type) \
+ (1 + (sizeof(_type) <= 1 ? 3 : \
+ sizeof(_type) <= 2 ? 5 : \
+ sizeof(_type) <= 4 ? 10 : \
+ B1_CC_ASSERT_TO(sizeof(_type) <= 8, 20)))
+
+/**
+ * B1_CC_UNIQUE - generate unique compile-time integer
+ *
+ * This evaluates to a unique compile-time integer. Each occurrence of this
+ * macro in the *preprocessed* C-code resolves to a different, unique integer.
+ * Internally, it uses the __COUNTER__ gcc extension, and as such all
+ * occurrences generate a dense set of integers.
+ *
+ * Return: This evaluates to an integer literal
+ */
+#define B1_CC_UNIQUE __COUNTER__
+
+/**
+ * B1_VAR() - generate unique variable name
+ * @_x: name of variable
+ * @_uniq: unique prefix, usually provided by @B1_CC_UNIQUE
+ *
+ * This macro shall be used to generate unique variable names, that will not be
+ * shadowed by recursive macro invocations. It is effectively a
+ * B1_CC_CONCATENATE of both arguments, but also provides a globally separated
+ * prefix and makes the code better readable.
+ *
+ * This helper may be used by macro implementations that might reasonable well
+ * be called in a stacked fasion, like:
+ * b1_max(foo, b1_max(bar, baz))
+ * Such a stacked call of b1_max() might cause compiler warnings of shadowed
+ * variables in the definition of b1_max(). By using B1_VAR(), such warnings
+ * can be silenced as each evaluation of b1_max() uses unique variable names.
+ *
+ * Return: This evaluates to a constant identifier
+ */
+#define B1_VAR(_x, _uniq) B1_CC_CONCATENATE(b1_var_unique_prefix_, B1_CC_CONCATENATE(_uniq, _x))
+
+/**
+ * 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
+ */
+#define b1_container_of(_ptr, _type, _member) b1_internal_container_of(B1_CC_UNIQUE, (_ptr), _type, _member)
+#define b1_internal_container_of(_uniq, _ptr, _type, _member) \
+ __extension__ ({ \
+ const __typeof__( ((_type*)0)->_member ) *B1_VAR(A, _uniq) = (_ptr); \
+ (_ptr) ? (_type*)( (char*)B1_VAR(A, _uniq) - offsetof(_type, _member) ) : NULL; \
+ })
+
+/**
+ * b1_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.
+ *
+ * Return: Maximum of both values is returned.
+ */
+#define b1_max(_a, _b) b1_internal_max(B1_CC_UNIQUE, (_a), B1_CC_UNIQUE, (_b))
+#define b1_internal_max(_aq, _a, _bq, _b) \
+ B1_CC_IF( \
+ (B1_CC_IS_CONST(_a) && B1_CC_IS_CONST(_b)), \
+ ((_a) > (_b) ? (_a) : (_b)), \
+ __extension__ ({ \
+ const __auto_type B1_VAR(A, _aq) = (_a); \
+ const __auto_type B1_VAR(B, _bq) = (_b); \
+ B1_VAR(A, _aq) > B1_VAR(B, _bq) ? B1_VAR(A, _aq) : B1_VAR(B, _bq); \
+ }))
+
+/**
+ * b1_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.
+ *
+ * Return: Minimum of both values is returned.
+ */
+#define b1_min(_a, _b) b1_internal_min(B1_CC_UNIQUE, (_a), B1_CC_UNIQUE, (_b))
+#define b1_internal_min(_aq, _a, _bq, _b) \
+ B1_CC_IF( \
+ (B1_CC_IS_CONST(_a) && B1_CC_IS_CONST(_b)), \
+ ((_a) < (_b) ? (_a) : (_b)), \
+ __extension__ ({ \
+ const __auto_type B1_VAR(A, _aq) = (_a); \
+ const __auto_type B1_VAR(B, _bq) = (_b); \
+ B1_VAR(A, _aq) < B1_VAR(B, _bq) ? B1_VAR(A, _aq) : B1_VAR(B, _bq); \
+ }))
+
+/**
+ * b1_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.
+ *
+ * Return: This computes [_a - _b], if [_a > _b]. Otherwise, 0 is returned.
+ */
+#define b1_less_by(_a, _b) b1_internal_less_by(B1_CC_UNIQUE, (_a), B1_CC_UNIQUE, (_b))
+#define b1_internal_less_by(_aq, _a, _bq, _b) \
+ B1_CC_IF( \
+ (B1_CC_IS_CONST(_a) && B1_CC_IS_CONST(_b)), \
+ ((_a) > (_b) ? ((_a) - (_b)) : 0), \
+ __extension__ ({ \
+ const __auto_type B1_VAR(A, _aq) = (_a); \
+ const __auto_type B1_VAR(B, _bq) = (_b); \
+ B1_VAR(A, _aq) > B1_VAR(B, _bq) ? B1_VAR(A, _aq) - B1_VAR(B, _bq) : 0; \
+ }))
+
+/**
+ * b1_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.
+ *
+ * Return: Clamped integer value.
+ */
+#define b1_clamp(_x, _low, _high) b1_internal_clamp(B1_CC_UNIQUE, (_x), B1_CC_UNIQUE, (_low), B1_CC_UNIQUE, (_high))
+#define b1_internal_clamp(_xq, _x, _lowq, _low, _highq, _high) \
+ B1_CC_IF( \
+ (B1_CC_IS_CONST(_x) && B1_CC_IS_CONST(_low) && B1_CC_IS_CONST(_high)), \
+ ((_x) > (_high) ? \
+ (_high) : \
+ (_x) < (_low) ? \
+ (_low) : \
+ (_x)), \
+ __extension__ ({ \
+ const __auto_type B1_VAR(X, _xq) = (_x); \
+ const __auto_type B1_VAR(LOW, _lowq) = (_low); \
+ const __auto_type B1_VAR(HIGH, _highq) = (_high); \
+ B1_VAR(X, _xq) > B1_VAR(HIGH, _highq) ? \
+ B1_VAR(HIGH, _highq) : \
+ B1_VAR(X, _xq) < B1_VAR(LOW, _lowq) ? \
+ B1_VAR(LOW, _lowq) : \
+ B1_VAR(X, _xq); \
+ }))
+
+/**
+ * b1_negative_errno() - return negative 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 b1_negative_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: Negative error code is returned.
+ */
+static inline int b1_negative_errno(void) {
+ return _likely_(errno > 0) ? -errno : -EINVAL;
+}
+
+/**
+ * b1_math_clz() - count leading zeroes
+ * @_val: value to count leading zeroes of
+ *
+ * This counts the leading zeroes of the binary representation of @_val. Note
+ * that @_val must be of an integer type greater than, or equal to, 'unsigned
+ * int'. Also note that a value of 0 produces an undefined result (see your CPU
+ * instruction manual for details why).
+ *
+ * This macro evaluates the argument exactly once, and if the input is
+ * constant, it also evaluates to a constant expression.
+ *
+ * Note that this macro calculates the number of leading zeroes within the
+ * scope of the integer type of @_val. That is, if the input is a 32bit type
+ * with value 1, it yields 31. But if it is a 64bit type with the same value 1,
+ * it yields 63.
+ *
+ * Return: Evaluates to an 'int', the number of leading zeroes.
+ */
+#define b1_math_clz(_val) \
+ _Generic((_val), \
+ unsigned int: __builtin_clz(_val), \
+ unsigned long: __builtin_clzl(_val), \
+ unsigned long long: __builtin_clzll(_val))
+
+/**
+ * b1_math_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.
+ *
+ * Return: The quotient is returned.
+ */
+#define b1_math_div_round_up(_x, _y) b1_internal_math_div_round_up(B1_CC_UNIQUE, (_x), B1_CC_UNIQUE, (_y))
+#define b1_internal_math_div_round_up(_xq, _x, _yq, _y) \
+ B1_CC_IF( \
+ (B1_CC_IS_CONST(_x) && B1_CC_IS_CONST(_y)), \
+ ((_x) / (_y) + !!((_x) % (_y))), \
+ __extension__ ({ \
+ const __auto_type B1_VAR(X, _xq) = (_x); \
+ const __auto_type B1_VAR(Y, _yq) = (_y); \
+ (B1_VAR(X, _xq) / B1_VAR(Y, _yq) + \
+ !!(B1_VAR(X, _xq) % B1_VAR(Y, _yq))); \
+ }))
+
+/**
+ * b1_math_align_to() - align value to
+ * @_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. In case @_to is a constant expression,
+ * this macro places a compile-time assertion on the popcount of @_to, to
+ * verify it is a power of 2.
+ *
+ * Return: @_val aligned to a multiple of @_to
+ */
+#define b1_math_align_to(_val, _to) b1_internal_math_align_to((_val), B1_CC_UNIQUE, (_to))
+#define b1_internal_math_align_to(_val, _toq, _to) \
+ B1_CC_IF( \
+ B1_CC_IS_CONST(_to), \
+ B1_CC_ASSERT_TO(__builtin_popcountll(B1_CC_IF(B1_CC_IS_CONST(_to), (_to), 1)) == 1, \
+ (((_val) + (_to) - 1) & ~((_to) - 1))), \
+ __extension__ ({ \
+ const __auto_type B1_VAR(to, _toq) = (_to); \
+ ((_val) + B1_VAR(to, _toq) - 1) & ~(B1_VAR(to, _toq) - 1); \
+ }))
+
+/**
+ * b1_math_align() - align to native size
+ * @_val: value to align
+ *
+ * This is the same as b1_math_align_to((_val), __SIZEOF_POINTER__).
+ *
+ * Return: @_val aligned to the native size
+ */
+#define b1_math_align(_val) b1_math_align_to((_val), __SIZEOF_POINTER__)
+
+/**
+ * b1_math_align8() - align value to multiple of 8
+ * @_val: value to align
+ *
+ * This is the same as b1_math_align_to((_val), 8).
+ *
+ * Return: @_val aligned to a multiple of 8.
+ */
+#define b1_math_align8(_val) b1_math_align_to((_val), 8)
+
+/**
+ * b1_math_align_power2() - align value to next power of 2
+ * @_val: value to align
+ *
+ * This aligns @_val to the next higher power of 2. If it already is a power of
+ * 2, the value is returned unchanged. 0 is treated as power of 2 (so 0 yields
+ * 0). Furthermore, on overflow, this yields 0 as well.
+ *
+ * Note that this always operates within the bounds of the type of @_val.
+ *
+ * Return: @_val aligned to the next higher power of 2
+ */
+#define b1_math_align_power2(_val) b1_internal_math_align_power2(B1_CC_UNIQUE, (_val))
+#define b1_internal_math_align_power2(_vq, _v) \
+ __extension__ ({ \
+ __auto_type B1_VAR(v, _vq) = (_v); \
+ /* cannot use ?: as gcc cannot do const-folding then (apparently..) */ \
+ if (B1_VAR(v, _vq) == 1) /* clz(0) is undefined */ \
+ B1_VAR(v, _vq) = 1; \
+ else if (b1_math_clz(B1_VAR(v, _vq) - 1) < 1) /* shift overflow is undefined */ \
+ B1_VAR(v, _vq) = 0; \
+ else \
+ B1_VAR(v, _vq) = ((__typeof__(B1_VAR(v, _vq)))1) << \
+ (sizeof(B1_VAR(v, _vq)) * 8 - b1_math_clz(B1_VAR(v, _vq) - 1)); \
+ B1_VAR(v, _vq); \
+ })
+
+/*
+ * XXX: move to separate header
+ * XXX: write test suite
+ *
+ * String helpers
+ */
+
+#include <string.h>
+
+_pure_ static inline bool b1_str_equal(const char *a, const char *b) {
+ return (!a || !b) ? (a == b) : !strcmp(a, b);
+}
+
+_pure_ static inline char *b1_str_prefix(const char *str, const char *prefix) {
+ size_t l = strlen(prefix);
+ return !strncmp(str, prefix, l) ? (char *)str + l : NULL;
+}
+
+/*
+ * XXX: move to separate header
+ * XXX: write test suite
+ *
+ * Micro-second helpers
+ */
+
+#include <sys/time.h>
+#include <time.h>
+
+/* stores up to 584,942.417355 years */
+typedef uint64_t b1_usec;
+
+#define b1_usec_from_nsec(_nsec) ((_nsec) / UINT64_C(1000))
+#define b1_usec_from_msec(_msec) ((_msec) * UINT64_C(1000))
+#define b1_usec_from_sec(_sec) b1_usec_from_msec((_sec) * UINT64_C(1000))
+#define b1_usec_from_timespec(_ts) (b1_usec_from_sec((_ts)->tv_sec) + b1_usec_from_nsec((_ts)->tv_nsec))
+#define b1_usec_from_timeval(_tv) (b1_usec_from_sec((_tv)->tv_sec) + (_tv)->tv_usec)
+
+static inline b1_usec b1_usec_from_clock(clockid_t clock) {
+ struct timespec ts;
+ int r;
+
+ r = clock_gettime(clock, &ts);
+ assert(r >= 0);
+ return b1_usec_from_timespec(&ts);
+}
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/src/bus1-sys.c b/src/bus1-sys.c
new file mode 100644
index 0000000..ebeeba1
--- /dev/null
+++ b/src/bus1-sys.c
@@ -0,0 +1,32 @@
+/***
+ This file is part of bus1. See COPYING for details.
+
+ bus1 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.
+
+ bus1 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 bus1; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <linux/memfd.h>
+#include <stdlib.h>
+#include <sys/resource.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include "bus1-macro.h"
+#include "bus1-sys.h"
+
+int b1_sys_memfd_create(const char *name, unsigned int flags) {
+#ifndef __NR_memfd_create
+ static_assert(false, "System lacks memfd_create(2) syscall");
+#endif
+ return syscall(__NR_memfd_create, name, flags);
+}
diff --git a/src/bus1-sys.h b/src/bus1-sys.h
new file mode 100644
index 0000000..b50ec5d
--- /dev/null
+++ b/src/bus1-sys.h
@@ -0,0 +1,49 @@
+#pragma once
+
+/***
+ This file is part of bus1. See COPYING for details.
+
+ bus1 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.
+
+ bus1 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 bus1; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+/*
+ * Syscall helpers
+ * XXX: Add description
+ */
+
+#include <assert.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <stdlib.h>
+#include "bus1-macro.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * b1_sys_memfd_create() - wrapper for memfd_create(2) syscall
+ * @name: name for memfd inode
+ * @flags: memfd flags
+ *
+ * This is a wrapper for the memfd_create(2) syscall. Currently, no user-space
+ * wrapper is exported by any libc.
+ *
+ * Return: New memfd file-descriptor on success, -1 on failure.
+ */
+int b1_sys_memfd_create(const char *name, unsigned int flags);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/src/bus1ctl.c b/src/bus1ctl.c
new file mode 100644
index 0000000..3f35850
--- /dev/null
+++ b/src/bus1ctl.c
@@ -0,0 +1,22 @@
+/***
+ This file is part of bus1. See COPYING for details.
+
+ bus1 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.
+
+ bus1 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 bus1; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <stdlib.h>
+
+int main(int argc, char **argv) {
+ return 0;
+}
diff --git a/src/test-macro.c b/src/test-macro.c
new file mode 100644
index 0000000..7d76e86
--- /dev/null
+++ b/src/test-macro.c
@@ -0,0 +1,392 @@
+/***
+ This file is part of bus1. See COPYING for details.
+
+ bus1 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.
+
+ bus1 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 bus1; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "bus1-macro.h"
+
+/* test B1_TYPE_* helpers */
+static void test_type(void) {
+ int foo, bar, array[16] = {};
+ long baz;
+
+ /* test type-matching with mixed explicit types and variables */
+ B1_CC_ASSERT(B1_TYPE_MATCH(foo, int));
+ B1_CC_ASSERT(B1_TYPE_MATCH(int, foo));
+ B1_CC_ASSERT(B1_TYPE_MATCH(int, int));
+ B1_CC_ASSERT(B1_TYPE_MATCH(foo, foo));
+ B1_CC_ASSERT(B1_TYPE_MATCH(foo, bar));
+ B1_CC_ASSERT(B1_TYPE_MATCH(bar, foo));
+ B1_CC_ASSERT(B1_TYPE_MATCH(baz, long));
+ B1_CC_ASSERT(!B1_TYPE_MATCH(bar, long));
+ B1_CC_ASSERT(!B1_TYPE_MATCH(long, bar));
+ B1_CC_ASSERT(!B1_TYPE_MATCH(int, long));
+ B1_CC_ASSERT(!B1_TYPE_MATCH(int, baz));
+ B1_CC_ASSERT(!B1_TYPE_MATCH(baz, int));
+ B1_CC_ASSERT(!B1_TYPE_MATCH(foo, baz));
+
+ /* test type-matching with qualifiers */
+ B1_CC_ASSERT(B1_TYPE_MATCH(int, const int));
+ B1_CC_ASSERT(B1_TYPE_MATCH(const int, const int));
+ B1_CC_ASSERT(B1_TYPE_MATCH(const int, int));
+ B1_CC_ASSERT(!B1_TYPE_MATCH(int, int*));
+ B1_CC_ASSERT(!B1_TYPE_MATCH(int[], int*));
+ B1_CC_ASSERT(!B1_TYPE_MATCH(int*, int**));
+ B1_CC_ASSERT(B1_TYPE_MATCH(int*, int*));
+ B1_CC_ASSERT(B1_TYPE_MATCH(int**, int**));
+ B1_CC_ASSERT(B1_TYPE_MATCH(int[], int[]));
+
+ /* test array verification */
+ B1_CC_ASSERT(B1_TYPE_IS_ARRAY(int[]));
+ B1_CC_ASSERT(B1_TYPE_IS_ARRAY(int*[]));
+ B1_CC_ASSERT(!B1_TYPE_IS_ARRAY(int*));
+ B1_CC_ASSERT(!B1_TYPE_IS_ARRAY(int**));
+
+ /* silence 'unused variable' warnings */
+ foo = 1;
+ bar = 1;
+ baz = 1;
+ assert(foo == bar);
+ assert(foo == baz);
+ assert(sizeof(array) == sizeof(*array) * 16);
+}
+
+/* test static assertions with/without messages on file-context */
+B1_CC_STATIC_ASSERT(sizeof(int) > 0);
+static_assert(sizeof(int) <= sizeof(long), "Custom error message");
+
+/* test B1_CC_* helpers */
+static void test_cc(int non_constant_expr) {
+ int foo = -16;
+ int bar[8];
+
+ /*
+ * Test static assertions with/without messsages on function-context.
+ * Those assert-helpers evaluate to a statement, and as such must be
+ * valid in function-context.
+ */
+ B1_CC_STATIC_ASSERT(true);
+ static_assert(true, "Custom error message");
+
+ /*
+ * Test static assertion in expressions with/without messages. Those
+ * helpers evaluate to constant expressions, and as such can be used in
+ * any expression.
+ * We also explicitly initialize static variables to test whether the
+ * values are true constant expressions.
+ */
+ foo = (B1_CC_ASSERT(true), 5);
+ foo = (B1_CC_ASSERT_MSG(true, "Custom error message"), foo + 1);
+ B1_CC_ASSERT(B1_CC_ASSERT1(8) == 1);
+ assert(foo == 6);
+
+ {
+ /* constant expression */
+ static int sub = B1_CC_ASSERT_TO(true, 16);
+ assert(sub == 16);
+ }
+
+ /*
+ * Test compile-time conditions. The B1_CC_IF() macro allows evaluation
+ * at compile-time, and as such yields exactly one of the code-blocks
+ * passed to it (depending on whether the expression is true).
+ */
+ foo = B1_CC_IF(false, foo + 0, foo + 1);
+ foo = B1_CC_IF(true, foo + 4, foo + 8);
+ assert(foo == 11);
+
+ /*
+ * Test constant-expr checks.
+ * The B1_CC_IS_CONST() macro allows verifying whether an expression is
+ * constant. The return value of the macro itself is constant, and as
+ * such can be used for constant expressions itself.
+ */
+ B1_CC_ASSERT(B1_CC_IS_CONST(5));
+ B1_CC_ASSERT(!B1_CC_IS_CONST(non_constant_expr));
+ B1_CC_ASSERT(B1_CC_IS_CONST(B1_CC_IS_CONST(non_constant_expr)));
+ B1_CC_ASSERT(!B1_CC_IS_CONST(foo++)); /* *NOT* evaluated */
+ assert(foo == 11);
+
+ /*
+ * 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
+ assert(!strcmp("foobar", B1_CC_STRINGIFY(foobar)));
+ assert(!strcmp("foobar", B1_CC_STRINGIFY(TEST_TOKEN)));
+ assert(!strcmp("foobar", B1_CC_STRINGIFY(B1_CC_CONCATENATE(foo, bar))));
+ assert(!strcmp("foobarfoobar", B1_CC_STRINGIFY(B1_CC_CONCATENATE(TEST_TOKEN, foobar))));
+ assert(!strcmp("foobarfoobar", B1_CC_STRINGIFY(B1_CC_CONCATENATE(foobar, TEST_TOKEN))));
+#undef TEST_TOKEN
+
+ /*
+ * Test array-size helper. This simply computes the number of elements
+ * of an array, instead of the binary size.
+ */
+ B1_CC_ASSERT(B1_CC_ARRAY_SIZE(bar) == 8);
+ B1_CC_ASSERT(B1_CC_IS_CONST(B1_CC_ARRAY_SIZE(bar)));
+
+ /*
+ * Test unique compile-time value. The B1_CC_UNIQUE value evaluates to
+ * a compile-time unique value for each time it is used. Hence, it can
+ * never compare equal to itself, furthermore, it's evaluated at
+ * compile-time, not pre-processor time!
+ */
+#define TEST_UNIQUE_MACRO B1_CC_UNIQUE
+ assert(B1_CC_UNIQUE != B1_CC_UNIQUE);
+ assert(TEST_UNIQUE_MACRO != TEST_UNIQUE_MACRO);
+ assert(B1_CC_UNIQUE != TEST_UNIQUE_MACRO);
+#undef TEST_UNIQUE_MACRO
+
+ /*
+ * 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.
+ */
+ B1_CC_ASSERT(B1_CC_DECIMAL_MAX(int32_t) == 11);
+ B1_CC_ASSERT(B1_CC_DECIMAL_MAX(uint32_t) == 11);
+ B1_CC_ASSERT(B1_CC_DECIMAL_MAX(uint64_t) == 21);
+ B1_CC_ASSERT(B1_CC_IS_CONST(B1_CC_DECIMAL_MAX(int32_t)));
+}
+
+static void test_misc(int non_constant_expr) {
+ int foo;
+
+ /*
+ * Test b1_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;
+ int b;
+ } sub = {};
+
+ B1_CC_ASSERT(&sub == b1_container_of(&sub.a, struct foobar, a));
+ B1_CC_ASSERT(&sub == b1_container_of(&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;
+ assert(b1_max(1, 5) == 5);
+ assert(b1_max(-1, 5) == 5);
+ assert(b1_max(-1, -5) == -1);
+ assert(b1_max(foo++, -1) == 0);
+ assert(foo == 1);
+ assert(b1_max(foo++, foo++) > 0);
+ assert(foo == 3);
+
+ B1_CC_ASSERT(B1_CC_IS_CONST(b1_max(1, 5)));
+ B1_CC_ASSERT(!B1_CC_IS_CONST(b1_max(1, non_constant_expr)));
+
+ {
+ static int sub = b1_max(1, 5);
+ assert(sub == 5);
+ }
+
+ foo = 0;
+ assert(b1_min(1, 5) == 1);
+ assert(b1_min(-1, 5) == -1);
+ assert(b1_min(-1, -5) == -5);
+ assert(b1_min(foo++, 1) == 0);
+ assert(foo == 1);
+ assert(b1_min(foo++, foo++) > 0);
+ assert(foo == 3);
+
+ B1_CC_ASSERT(B1_CC_IS_CONST(b1_min(1, 5)));
+ B1_CC_ASSERT(!B1_CC_IS_CONST(b1_min(1, non_constant_expr)));
+
+ {
+ static int sub = b1_min(1, 5);
+ assert(sub == 1);
+ }
+
+ /*
+ * Test b1_less_by(), b1_clamp(). Make sure they
+ * evaluate arguments exactly once, and yield a constant expression,
+ * if all arguments are constant.
+ */
+ foo = 8;
+ assert(b1_less_by(1, 5) == 0);
+ assert(b1_less_by(5, 1) == 4);
+ assert(b1_less_by(foo++, 1) == 7);
+ assert(foo == 9);
+ assert(b1_less_by(foo++, foo++) >= 0);
+ assert(foo == 11);
+
+ B1_CC_ASSERT(B1_CC_IS_CONST(b1_less_by(1, 5)));
+ B1_CC_ASSERT(!B1_CC_IS_CONST(b1_less_by(1, non_constant_expr)));
+
+ foo = 8;
+ assert(b1_clamp(foo, 1, 5) == 5);
+ assert(b1_clamp(foo, 9, 20) == 9);
+ assert(b1_clamp(foo++, 1, 5) == 5);
+ assert(foo == 9);
+ assert(b1_clamp(foo++, foo++, foo++) >= 0);
+ assert(foo == 12);
+
+ B1_CC_ASSERT(B1_CC_IS_CONST(b1_clamp(0, 1, 5)));
+ B1_CC_ASSERT(!B1_CC_IS_CONST(b1_clamp(1, 0, non_constant_expr)));
+
+ /*
+ * Test b1_negative_errno(). Do this by writing a code-path where gcc
+ * couldn't assume that 'errno' is negative, but the helper does
+ * provide such clue.
+ */
+ errno = ENOSYS;
+ {
+ int uninitialized, sub = b1_negative_errno();
+
+ if (sub < 0)
+ assert(sub == -errno);
+ else
+ assert(uninitialized == 0); /* never evaluated */
+ }
+}
+
+static void test_math(int non_constant_expr) {
+ int i, j, foo;
+
+ /*
+ * Count Leading Zeroes: The b1_math_clz() macro is a type-generic
+ * variant of clz(). It counts leading zeroes of an integer. The result
+ * highly depends on the integer-width of the input. Make sure it
+ * selects the correct implementation.
+ * Also note: clz(0) is undefined!
+ */
+ B1_CC_ASSERT(b1_math_clz(UINT32_C(1)) == 31);
+ B1_CC_ASSERT(b1_math_clz(UINT64_C(1)) == 63);
+
+ B1_CC_ASSERT(b1_math_clz(UINT32_C(-1)) == 0);
+ B1_CC_ASSERT(b1_math_clz(UINT32_C(-1) + 2) == 31);
+
+ B1_CC_ASSERT(b1_math_clz((uint64_t)UINT32_C(-1)) == 32);
+ B1_CC_ASSERT(b1_math_clz((uint64_t)UINT32_C(-1) + 2) == 31);
+
+ /* make sure it's compile-time constant */
+ B1_CC_ASSERT(B1_CC_IS_CONST(b1_math_clz(1U)));
+ B1_CC_ASSERT(!B1_CC_IS_CONST(b1_math_clz((unsigned int)non_constant_expr)));
+ {
+ static const int sub = b1_math_clz(1U);
+ B1_CC_ASSERT(sub == 31);
+ }
+
+ /*
+ * 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].
+ */
+#define TEST_ALT_DIV(_x, _y) (((_x) + (_y) - 1) / (_y))
+ foo = 8;
+ assert(b1_math_div_round_up(0, 5) == 0);
+ assert(b1_math_div_round_up(1, 5) == 1);
+ assert(b1_math_div_round_up(5, 5) == 1);
+ assert(b1_math_div_round_up(6, 5) == 2);
+ assert(b1_math_div_round_up(foo++, 1) == 8);
+ assert(foo == 9);
+ assert(b1_math_div_round_up(foo++, foo++) >= 0);
+ assert(foo == 11);
+
+ B1_CC_ASSERT(B1_CC_IS_CONST(b1_math_div_round_up(1, 5)));
+ B1_CC_ASSERT(!B1_CC_IS_CONST(b1_math_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)
+ assert(b1_math_div_round_up(i, j) == TEST_ALT_DIV(i, j));
+ for (j = 0xff00; j <= 0xffff; ++j)
+ assert(b1_math_div_round_up(i, j) == TEST_ALT_DIV(i, j));
+ }
+
+ /* make sure it doesn't suffer from high overflow */
+ assert(UINT32_C(0xfffffffa) % 10 == 0);
+ assert(UINT32_C(0xfffffffa) / 10 == UINT32_C(429496729));
+ assert(b1_math_div_round_up(UINT32_C(0xfffffffa), 10) == UINT32_C(429496729));
+ assert(TEST_ALT_DIV(UINT32_C(0xfffffffa), 10) == 0); /* overflow */
+
+ assert(UINT32_C(0xfffffffd) % 10 == 3);
+ assert(UINT32_C(0xfffffffd) / 10 == UINT32_C(429496729));
+ assert(b1_math_div_round_up(UINT32_C(0xfffffffd), 10) == UINT32_C(429496730));
+ 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.
+ */
+ assert(b1_math_align_to(UINT32_C(0), 1) == 0);
+ assert(b1_math_align_to(UINT32_C(0), 2) == 0);
+ assert(b1_math_align_to(UINT32_C(0), 4) == 0);
+ assert(b1_math_align_to(UINT32_C(0), 8) == 0);
+ assert(b1_math_align_to(UINT32_C(1), 8) == 8);
+
+ assert(b1_math_align_to(UINT32_C(0xffffffff), 8) == 0);
+ assert(b1_math_align_to(UINT32_C(0xfffffff1), 8) == 0xfffffff8);
+ assert(b1_math_align_to(UINT32_C(0xfffffff1), 8) == 0xfffffff8);
+
+ B1_CC_ASSERT(B1_CC_IS_CONST(b1_math_align_to(16, 8)));
+ B1_CC_ASSERT(!B1_CC_IS_CONST(b1_math_align_to(non_constant_expr, 8)));
+ B1_CC_ASSERT(!B1_CC_IS_CONST(b1_math_align_to(16, non_constant_expr)));
+ B1_CC_ASSERT(!B1_CC_IS_CONST(b1_math_align_to(16, non_constant_expr ? 8 : 16)));
+ B1_CC_ASSERT(B1_CC_IS_CONST(b1_math_align_to(16, 7 + 1)));
+ assert(b1_math_align_to(15, non_constant_expr ? 8 : 16) == 16);
+
+ for (i = 0; i < 0xffff; ++i) {
+ assert(b1_math_align(i) == b1_math_align_to(i, (int)sizeof(void*)));
+ assert(b1_math_align8(i) == b1_math_align_to(i, 8));
+ }
+
+ /*
+ * Align Power2: The b1_math_align_power2() macro aligns passed values to the
+ * next power of 2. Special cases: 0->0, overflow->0
+ * Also make sure it never performs an up-cast on overflow.
+ */
+ assert(b1_math_align_power2(UINT32_C(0)) == 0);
+ assert(b1_math_align_power2(UINT32_C(0x80000001)) == 0);
+ assert(b1_math_align_power2(UINT64_C(0)) == 0);
+ assert(b1_math_align_power2(UINT64_C(0x8000000000000001)) == 0);
+
+ assert(b1_math_align_power2((uint64_t)UINT32_C(0)) == 0);
+ assert(b1_math_align_power2((uint64_t)UINT32_C(0x80000001)) == UINT64_C(0x100000000));
+
+ assert(b1_math_align_power2(UINT32_C(1)) == 1);
+ assert(b1_math_align_power2(UINT32_C(2)) == 2);
+ assert(b1_math_align_power2(UINT32_C(3)) == 4);
+ assert(b1_math_align_power2(UINT32_C(4)) == 4);
+ assert(b1_math_align_power2(UINT32_C(5)) == 8);
+ assert(b1_math_align_power2(UINT32_C(0x80000000)) == UINT32_C(0x80000000));
+}
+
+int main(int argc, char **argv) {
+ test_type();
+ test_cc(argc);
+ test_misc(argc);
+ test_math(argc);
+
+ return 0;
+}
diff --git a/src/test-perf.c b/src/test-perf.c
new file mode 100644
index 0000000..716be89
--- /dev/null
+++ b/src/test-perf.c
@@ -0,0 +1,230 @@
+/***
+ This file is part of bus1. See COPYING for details.
+
+ bus1 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.
+
+ bus1 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 bus1; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <fcntl.h>
+#include <linux/memfd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/uio.h>
+#include "bus1-macro.h"
+#include "bus1-sys.h"
+
+#define TEST_BUFSIZE (4096LL * 4096LL) /* 4096 pages */
+
+typedef struct {
+ uint32_t arg1;
+ uint32_t arg2;
+ uint64_t arg3;
+ uint64_t size;
+ uint8_t blob[];
+} TestMessage;
+
+static void test_message_write1(int fd, void *map, const TestMessage *args) {
+ memcpy(map, args, sizeof(*args) + args->size);
+}
+
+static void test_message_write2(int fd, void *map, const TestMessage *args) {
+ TestMessage *m = map;
+
+ m->arg1 = args->arg1;
+ m->arg2 = args->arg2;
+ m->arg3 = args->arg3;
+ m->size = args->size;
+ memcpy(m->blob, args->blob, args->size);
+}
+
+static void test_message_write3(int fd, void *map, const TestMessage *args) {
+ TestMessage *m;
+ uint64_t size;
+ int r;
+
+ size = sizeof(*m) + args->size;
+ m = alloca(size);
+ m->arg1 = args->arg1;
+ m->arg2 = args->arg2;
+ m->arg3 = args->arg3;
+ m->size = args->size;
+ memcpy(m->blob, args->blob, args->size);
+
+ r = pwrite(fd, m, size, 0);
+ assert(r >= 0 && (uint64_t)r == size);
+}
+
+static void test_message_write4(int fd, void *map, const TestMessage *args) {
+ struct iovec vec[2];
+ TestMessage *m;
+ int r;
+
+ m = alloca(sizeof(*m));
+ m->arg1 = args->arg1;
+ m->arg2 = args->arg2;
+ m->arg3 = args->arg3;
+ m->size = args->size;
+
+ vec[0].iov_base = (void *)m;
+ vec[0].iov_len = sizeof(*m);
+ vec[1].iov_base = (void *)args->blob;
+ vec[1].iov_len = args->size;
+
+ r = pwritev(fd, vec, 2, 0);
+ assert(r >= 0 && (uint64_t)r == sizeof(*m) + args->size);
+}
+
+static void test_message_write5(int fd, void *map, const TestMessage *args) {
+ struct iovec vec[5];
+ int r;
+
+ vec[0].iov_base = (void *)&args->arg1;
+ vec[0].iov_len = sizeof(args->arg1);
+ vec[1].iov_base = (void *)&args->arg2;
+ vec[1].iov_len = sizeof(args->arg2);
+ vec[2].iov_base = (void *)&args->arg3;
+ vec[2].iov_len = sizeof(args->arg3);
+ vec[3].iov_base = (void *)&args->size;
+ vec[3].iov_len = sizeof(args->size);
+ vec[4].iov_base = (void *)args->blob;
+ vec[4].iov_len = args->size;
+
+ r = pwritev(fd, vec, 5, 0);
+ assert(r >= 0 && (uint64_t)r == sizeof(TestMessage) + args->size);
+}
+
+static void (* const test_xmitters[]) (int fd, void *map, const TestMessage *args) = {
+ test_message_write1,
+ test_message_write2,
+ test_message_write3,
+ test_message_write4,
+ test_message_write5,
+};
+
+static void test_message_validate(const void *map, const TestMessage *args) {
+ const TestMessage *m = map;
+
+ assert(args->arg1 == m->arg1);
+ assert(args->arg2 == m->arg2);
+ assert(args->arg3 == m->arg3);
+ assert(args->size == m->size);
+ assert(!memcmp(args->blob, m->blob, args->size));
+}
+
+static void test_message_xmit(int fd, void *map, const TestMessage *args, unsigned int xmitter) {
+ assert(xmitter < B1_CC_ARRAY_SIZE(test_xmitters));
+
+ test_xmitters[xmitter](fd, map, args);
+ test_message_validate(map, args);
+}
+
+static void test_xmit(int fd, void *map, unsigned int xmitter, uint64_t times, uint64_t size) {
+ static struct {
+ TestMessage m;
+ uint8_t blob[4096 * 4096];
+ } m = {
+ .m = {
+ .arg1 = UINT32_C(0xabcdabcd),
+ .arg2 = UINT32_C(0xffffffff),
+ .arg3 = UINT64_C(0xff00ff00ff00ff00),
+ },
+ .blob = {},
+ };
+ uint64_t i;
+
+ assert(size <= sizeof(m.blob));
+
+ m.m.size = size;
+ for (i = 0; i < times; ++i)
+ test_message_xmit(fd, map, &m.m, xmitter);
+}
+
+static void test_run_one(int fd, void *map, unsigned int xmitter, uint64_t times, uint64_t size) {
+ b1_usec time_start, time_end;
+
+ fprintf(stderr, "Run: times:%" PRIu64 " size:%" PRIu64 "\n", times, size);
+
+ /* do some test runs to initialize caches; don't account them */
+ memset(map, 0, TEST_BUFSIZE);
+ test_xmit(fd, map, xmitter, times / 10, size);
+
+ /* do real tests and measure time */
+ time_start = b1_usec_from_clock(CLOCK_THREAD_CPUTIME_ID);
+ test_xmit(fd, map, xmitter, times, size);
+ time_end = b1_usec_from_clock(CLOCK_THREAD_CPUTIME_ID);
+
+ /* print result table */
+ printf("%" PRIu64 " %d %" PRIu64 "\n", size, xmitter, time_end - time_start);
+}
+
+static void test_run_all(int fd, void *map, unsigned int xmitter, uint64_t times) {
+ unsigned int size;
+
+ /*
+ * Run test suite with different blob-sizes. First we start from size 1
+ * to 128, doubling on each iteration. Then from 128 to 64k we just add
+ * 128 each round to get better details.
+ */
+
+ for (size = 1; size <= 128; size <<= 1)
+ test_run_one(fd, map, xmitter, times, size);
+ for ( ; size <= 4096 << 4; size += 128)
+ test_run_one(fd, map, xmitter, times, size);
+}
+
+static void test_transaction(unsigned int xmitter) {
+ int memfd, r;
+ uint8_t *map;
+ long i;
+
+ /* create memfd and pre-allocate buffer space */
+ memfd = b1_sys_memfd_create("test-file", MFD_CLOEXEC);
+ assert(memfd >= 0);
+
+ r = fallocate(memfd, 0, 0, TEST_BUFSIZE);
+ assert(r >= 0);
+
+ map = mmap(NULL, TEST_BUFSIZE, PROT_READ | PROT_WRITE, MAP_SHARED, memfd, 0);
+ assert(map != MAP_FAILED);
+
+ /* access the whole buffer to do some random caching and fault in pages */
+ for (i = 0; i < TEST_BUFSIZE; ++i)
+ assert(map[i] == 0);
+
+ /* run tests; each one 10k times */
+ test_run_all(memfd, map, xmitter, 10UL * 1000UL);
+
+ /* cleanup */
+ munmap(map, TEST_BUFSIZE);
+ close(memfd);
+}
+
+int main(int argc, char **argv) {
+ unsigned int xmitter;
+
+ if (argc != 2) {
+ fprintf(stderr, "Usage: %s <#xmitter>\n", program_invocation_short_name);
+ return 77;
+ }
+
+ xmitter = atoi(argv[1]);
+ if (xmitter >= B1_CC_ARRAY_SIZE(test_xmitters)) {
+ fprintf(stderr, "Invalid xmitter (available: %zu)\n", B1_CC_ARRAY_SIZE(test_xmitters));
+ return 77;
+ }
+
+ test_transaction(xmitter);
+ return 0;
+}