summaryrefslogtreecommitdiffstats
path: root/src/bfs.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/bfs.h')
-rw-r--r--src/bfs.h138
1 files changed, 96 insertions, 42 deletions
diff --git a/src/bfs.h b/src/bfs.h
index 91c6540..bb83df2 100644
--- a/src/bfs.h
+++ b/src/bfs.h
@@ -8,6 +8,9 @@
#ifndef BFS_H
#define BFS_H
+#include <assert.h> // For __GLIBC__
+#include <stddef.h> // For offsetof
+
// Standard versions
/** Possible __STDC_VERSION__ values. */
@@ -33,10 +36,15 @@
#ifndef BFS_COMMAND
# define BFS_COMMAND "bfs"
#endif
+
#ifndef BFS_HOMEPAGE
# define BFS_HOMEPAGE "https://tavianator.com/projects/bfs.html"
#endif
+#ifndef BFS_LINT
+# define BFS_LINT false
+#endif
+
// This is a symbol instead of a literal so we don't have to rebuild everything
// when the version number changes
extern const char bfs_version[];
@@ -48,15 +56,36 @@ extern const char bfs_cflags[];
extern const char bfs_ldflags[];
extern const char bfs_ldlibs[];
-// Get __GLIBC__
-#include <assert.h>
-
// Fundamental utilities
/**
- * Get the length of an array.
+ * Given `ptr = &t->member`, return `t`.
+ */
+#define container_of(ptr, type, member) \
+ (container_of_typecheck(ptr, type, member), \
+ (type *)((char *)ptr - offsetof(type, member)))
+
+#define container_of_typecheck(ptr, type, field) \
+ (void)sizeof(ptr - &((type *)NULL)->field)
+
+/**
+ * A preprocessor conditional.
+ *
+ * BFS_VA_IF(A)(B)(C) => B
+ * BFS_VA_IF( )(B)(C) => C
*/
-#define countof(...) (sizeof(__VA_ARGS__) / sizeof(0[__VA_ARGS__]))
+#define BFS_VA_IF(...) BFS_VA_IF_AB ## __VA_OPT__(C)
+// BFS_VA_IF(A)(B)(C) => BFS_VA_IF_ABC(B)(C)
+// BFS_VA_IF( )(B)(C) => BFS_VA_IF_AB(B)(C)
+
+#define BFS_VA_IF_ABC(...) __VA_ARGS__ BFS_VA_IGNORE
+// BFS_VA_IF_ABC(B)(C) => B BFS_VA_IGNORE(C)
+
+#define BFS_VA_IF_AB(...) BFS_VA_REPEAT
+// BFS_VA_IF_AB(B)(C) => BFS_VA_REPEAT(C)
+
+#define BFS_VA_IGNORE(...)
+#define BFS_VA_REPEAT(...) __VA_ARGS__
/**
* False sharing/destructive interference/largest cache line size.
@@ -84,19 +113,12 @@ extern const char bfs_ldlibs[];
// Wrappers for attributes
/**
- * Silence warnings about switch/case fall-throughs.
- */
-#if __has_attribute(fallthrough)
-# define _fallthrough __attribute__((fallthrough))
-#else
-# define _fallthrough ((void)0)
-#endif
-
-/**
* Silence warnings about unused declarations.
*/
-#if __has_attribute(unused)
-# define _maybe_unused __attribute__((unused))
+#if __has_c_attribute(maybe_unused)
+# define _maybe_unused maybe_unused
+#elif __has_c_attribute(gnu::unused)
+# define _maybe_unused gnu::unused
#else
# define _maybe_unused
#endif
@@ -104,8 +126,10 @@ extern const char bfs_ldlibs[];
/**
* Warn if a value is unused.
*/
-#if __has_attribute(warn_unused_result)
-# define _nodiscard __attribute__((warn_unused_result))
+#if __has_c_attribute(nodiscard)
+# define _nodiscard nodiscard
+#elif __has_c_attribute(gnu::warn_unused_result)
+# define _nodiscard gnu::warn_unused_result
#else
# define _nodiscard
#endif
@@ -113,35 +137,38 @@ extern const char bfs_ldlibs[];
/**
* Hint to avoid inlining a function.
*/
-#if __has_attribute(noinline)
-# define _noinline __attribute__((noinline))
+#if __has_c_attribute(gnu::noinline)
+# define _noinline gnu::noinline
#else
# define _noinline
#endif
/**
- * Marks a non-returning function.
+ * Hint that a function is unlikely to be called.
*/
-#if __STDC_VERSION__ >= C23
-# define _noreturn [[noreturn]]
+#if __has_c_attribute(gnu::cold)
+# define _cold _noinline, gnu::cold
#else
-# define _noreturn _Noreturn
+# define _cold _noinline
#endif
/**
- * Hint that a function is unlikely to be called.
+ * Marks a non-returning function.
*/
-#if __has_attribute(cold)
-# define _cold _noinline __attribute__((cold))
+#if __has_c_attribute(noreturn)
+# define _noreturn noreturn
+#elif __has_c_attribute(gnu::noreturn)
+# define _noreturn gnu::noreturn
#else
-# define _cold _noinline
+# define _noreturn
#endif
+
/**
* Adds compiler warnings for bad printf()-style function calls, if supported.
*/
-#if __has_attribute(format)
-# define _printf(fmt, args) __attribute__((format(printf, fmt, args)))
+#if __has_c_attribute(gnu::format)
+# define _printf(fmt, args) gnu::format(printf, fmt, args)
#else
# define _printf(fmt, args)
#endif
@@ -149,8 +176,8 @@ extern const char bfs_ldlibs[];
/**
* Annotates functions that potentially modify and return format strings.
*/
-#if __has_attribute(format_arg)
-# define _format_arg(arg) __attribute__((format_arg(arg)))
+#if __has_c_attribute(gnu::format_arg)
+# define _format_arg(arg) gnu::format_arg(arg)
#else
# define _format_arg(arg)
#endif
@@ -158,11 +185,11 @@ extern const char bfs_ldlibs[];
/**
* Annotates allocator-like functions.
*/
-#if __has_attribute(malloc)
+#if __has_c_attribute(gnu::malloc)
# if __GNUC__ >= 11 && !__OPTIMIZE__ // malloc(deallocator) disables inlining on GCC
-# define _malloc(...) _nodiscard __attribute__((malloc(__VA_ARGS__)))
+# define _malloc(...) _nodiscard, gnu::malloc(__VA_ARGS__)
# else
-# define _malloc(...) _nodiscard __attribute__((malloc))
+# define _malloc(...) _nodiscard, gnu::malloc
# endif
#else
# define _malloc(...) _nodiscard
@@ -171,8 +198,8 @@ extern const char bfs_ldlibs[];
/**
* Specifies that a function returns allocations with a given alignment.
*/
-#if __has_attribute(alloc_align)
-# define _alloc_align(param) __attribute__((alloc_align(param)))
+#if __has_c_attribute(gnu::alloc_align)
+# define _alloc_align(param) gnu::alloc_align(param)
#else
# define _alloc_align(param)
#endif
@@ -180,8 +207,8 @@ extern const char bfs_ldlibs[];
/**
* Specifies that a function returns allocations with a given size.
*/
-#if __has_attribute(alloc_size)
-# define _alloc_size(...) __attribute__((alloc_size(__VA_ARGS__)))
+#if __has_c_attribute(gnu::alloc_size)
+# define _alloc_size(...) gnu::alloc_size(__VA_ARGS__)
#else
# define _alloc_size(...)
#endif
@@ -189,7 +216,7 @@ extern const char bfs_ldlibs[];
/**
* Shorthand for _alloc_align() and _alloc_size().
*/
-#define _aligned_alloc(align, ...) _alloc_align(align) _alloc_size(__VA_ARGS__)
+#define _aligned_alloc(align, ...) _alloc_align(align), _alloc_size(__VA_ARGS__)
/**
* Check if function multiversioning via GNU indirect functions (ifunc) is supported.
@@ -197,8 +224,13 @@ extern const char bfs_ldlibs[];
* Disabled on TSan due to https://github.com/google/sanitizers/issues/342.
*/
#ifndef BFS_USE_TARGET_CLONES
-# if __has_attribute(target_clones) && (__GLIBC__ || __FreeBSD__) && !__SANITIZE_THREAD__
+# if __has_c_attribute(gnu::target_clones) \
+ && (__GLIBC__ || __FreeBSD__) \
+ && !__SANITIZE_THREAD__ \
+ && !__SANITIZE_TYPE__
# define BFS_USE_TARGET_CLONES true
+# else
+# define BFS_USE_TARGET_CLONES false
# endif
#endif
@@ -206,9 +238,31 @@ extern const char bfs_ldlibs[];
* Apply the target_clones attribute, if available.
*/
#if BFS_USE_TARGET_CLONES
-# define _target_clones(...) __attribute__((target_clones(__VA_ARGS__)))
+# define _target_clones(...) gnu::target_clones(__VA_ARGS__)
#else
# define _target_clones(...)
#endif
+/**
+ * Mark the size of a flexible array member.
+ */
+#if __has_c_attribute(clang::counted_by)
+# define _counted_by(...) clang::counted_by(__VA_ARGS__)
+#elif __has_c_attribute(gnu::counted_by)
+# define _counted_by(...) gnu::counted_by(__VA_ARGS__)
+#else
+# define _counted_by(...)
+#endif
+
+/**
+ * Optimization hint to not unroll a loop.
+ */
+#if BFS_HAS_PRAGMA_NOUNROLL
+# define _nounroll _Pragma("nounroll")
+#elif __GNUC__ && !__clang__
+# define _nounroll _Pragma("GCC unroll 0")
+#else
+# define _nounroll
+#endif
+
#endif // BFS_H