// Copyright © Tavian Barnes // SPDX-License-Identifier: 0BSD /** * Configuration and feature/platform detection. */ #ifndef BFS_PRELUDE_H #define BFS_PRELUDE_H // Possible __STDC_VERSION__ values #define C95 199409L #define C99 199901L #define C11 201112L #define C17 201710L #define C23 202311L #include #if __STDC_VERSION__ < C23 # include # include # include #endif // bfs packaging configuration #include "config.h" #ifndef BFS_COMMAND # define BFS_COMMAND "bfs" #endif #ifndef BFS_HOMEPAGE # define BFS_HOMEPAGE "https://tavianator.com/projects/bfs.html" #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[]; // Check for system headers #ifdef __has_include #if __has_include() # define BFS_HAS_MNTENT_H true #endif #if __has_include() # define BFS_HAS_PATHS_H true #endif #if __has_include() # define BFS_HAS_SYS_EXTATTR_H true #endif #if __has_include() # define BFS_HAS_SYS_MKDEV_H true #endif #if __has_include() # define BFS_HAS_SYS_PARAM_H true #endif #if __has_include() # define BFS_HAS_SYS_SYSMACROS_H true #endif #if __has_include() # define BFS_HAS_SYS_XATTR_H true #endif #if __has_include() # define BFS_HAS_THREADS_H true #endif #if __has_include() # define BFS_HAS_UTIL_H true #endif #else // !__has_include #define BFS_HAS_MNTENT_H __GLIBC__ #define BFS_HAS_PATHS_H true #define BFS_HAS_SYS_EXTATTR_H __FreeBSD__ #define BFS_HAS_SYS_MKDEV_H false #define BFS_HAS_SYS_PARAM_H true #define BFS_HAS_SYS_SYSMACROS_H __GLIBC__ #define BFS_HAS_SYS_XATTR_H __linux__ #define BFS_HAS_THREADS_H (!__STDC_NO_THREADS__) #define BFS_HAS_UTIL_H __NetBSD__ #endif // !__has_include #ifndef BFS_USE_MNTENT_H # define BFS_USE_MNTENT_H BFS_HAS_MNTENT_H #endif #ifndef BFS_USE_PATHS_H # define BFS_USE_PATHS_H BFS_HAS_PATHS_H #endif #ifndef BFS_USE_SYS_EXTATTR_H # define BFS_USE_SYS_EXTATTR_H BFS_HAS_SYS_EXTATTR_H #endif #ifndef BFS_USE_SYS_MKDEV_H # define BFS_USE_SYS_MKDEV_H BFS_HAS_SYS_MKDEV_H #endif #ifndef BFS_USE_SYS_PARAM_H # define BFS_USE_SYS_PARAM_H BFS_HAS_SYS_PARAM_H #endif #ifndef BFS_USE_SYS_SYSMACROS_H # define BFS_USE_SYS_SYSMACROS_H BFS_HAS_SYS_SYSMACROS_H #endif #ifndef BFS_USE_SYS_XATTR_H # define BFS_USE_SYS_XATTR_H BFS_HAS_SYS_XATTR_H #endif #ifndef BFS_USE_THREADS_H # define BFS_USE_THREADS_H BFS_HAS_THREADS_H #endif #ifndef BFS_USE_UTIL_H # define BFS_USE_UTIL_H BFS_HAS_UTIL_H #endif // Stub out feature detection on old/incompatible compilers #ifndef __has_feature # define __has_feature(feat) false #endif #ifndef __has_c_attribute # define __has_c_attribute(attr) false #endif #ifndef __has_attribute # define __has_attribute(attr) false #endif // Platform detection // Get the definition of BSD if available #if BFS_USE_SYS_PARAM_H # include #endif #ifndef __GLIBC_PREREQ # define __GLIBC_PREREQ(maj, min) false #endif #ifndef __NetBSD_Prereq__ # define __NetBSD_Prereq__(maj, min, patch) false #endif // Fundamental utilities /** * Get the length of an array. */ #define countof(array) (sizeof(array) / sizeof(0[array])) /** * False sharing/destructive interference/largest cache line size. */ #ifdef __GCC_DESTRUCTIVE_SIZE # define FALSE_SHARING_SIZE __GCC_DESTRUCTIVE_SIZE #else # define FALSE_SHARING_SIZE 64 #endif /** * True sharing/constructive interference/smallest cache line size. */ #ifdef __GCC_CONSTRUCTIVE_SIZE # define TRUE_SHARING_SIZE __GCC_CONSTRUCTIVE_SIZE #else # define TRUE_SHARING_SIZE 64 #endif /** * Polyfill max_align_t if we don't already have it. */ #if !BFS_HAS_MAX_ALIGN_T typedef union { # ifdef __BIGGEST_ALIGNMENT__ alignas(__BIGGEST_ALIGNMENT__) char c; # else long double ld; long long ll; void *ptr; # endif } max_align_t; #endif /** * Alignment specifier that avoids false sharing. */ #define cache_align alignas(FALSE_SHARING_SIZE) // Wrappers for attributes /** * Silence warnings about switch/case fall-throughs. */ #if __has_attribute(fallthrough) # define fallthru __attribute__((fallthrough)) #else # define fallthru ((void)0) #endif /** * Silence warnings about unused declarations. */ #if __has_attribute(unused) # define attr_maybe_unused __attribute__((unused)) #else # define attr_maybe_unused #endif /** * Warn if a value is unused. */ #if __has_attribute(warn_unused_result) # define attr_nodiscard __attribute__((warn_unused_result)) #else # define attr_nodiscard #endif /** * Hint to avoid inlining a function. */ #if __has_attribute(noinline) # define attr_noinline __attribute__((noinline)) #else # define attr_noinline #endif /** * Hint that a function is unlikely to be called. */ #if __has_attribute(cold) # define attr_cold attr_noinline __attribute__((cold)) #else # define attr_cold attr_noinline #endif /** * Adds compiler warnings for bad printf()-style function calls, if supported. */ #if __has_attribute(format) # define attr_printf(fmt, args) __attribute__((format(printf, fmt, args))) #else # define attr_printf(fmt, args) #endif /** * Annotates functions that potentially modify and return format strings. */ #if __has_attribute(format_arg) # define attr_format_arg(arg) __attribute__((format_arg(arg))) #else # define attr_format_arg(args) #endif /** * Annotates allocator-like functions. */ #if __has_attribute(malloc) # if __GNUC__ >= 11 && !__OPTIMIZE__ // malloc(deallocator) disables inlining on GCC # define attr_malloc(...) attr_nodiscard __attribute__((malloc(__VA_ARGS__))) # else # define attr_malloc(...) attr_nodiscard __attribute__((malloc)) # endif #else # define attr_malloc(...) attr_nodiscard #endif /** * Specifies that a function returns allocations with a given alignment. */ #if __has_attribute(alloc_align) # define attr_alloc_align(param) __attribute__((alloc_align(param))) #else # define attr_alloc_align(param) #endif /** * Specifies that a function returns allocations with a given size. */ #if __has_attribute(alloc_size) # define attr_alloc_size(...) __attribute__((alloc_size(__VA_ARGS__))) #else # define attr_alloc_size(...) #endif /** * Shorthand for attr_alloc_align() and attr_alloc_size(). */ #define attr_aligned_alloc(align, ...) \ attr_alloc_align(align) \ attr_alloc_size(__VA_ARGS__) /** * Check if function multiversioning via GNU indirect functions (ifunc) is supported. */ #ifndef BFS_USE_TARGET_CLONES # if __has_attribute(target_clones) && (__GLIBC__ || __FreeBSD__) # define BFS_USE_TARGET_CLONES true # endif #endif /** * Apply the target_clones attribute, if available. */ #if BFS_USE_TARGET_CLONES # define attr_target_clones(...) __attribute__((target_clones(__VA_ARGS__))) #else # define attr_target_clones(...) #endif /** * Shorthand for multiple attributes at once. attr(a, b(c), d) is equivalent to * * attr_a * attr_b(c) * attr_d */ #define attr(...) \ attr__(attr_##__VA_ARGS__, none, none, none, none, none, none, none, none, none, ) /** * attr() helper. For exposition, pretend we support only 2 args, instead of 9. * There are a few cases: * * attr() * => attr__(attr_, none, none) * => attr_ => * attr_none => * attr_too_many_none() => * * attr(a) * => attr__(attr_a, none, none) * => attr_a => __attribute__((a)) * attr_none => * attr_too_many_none() => * * attr(a, b(c)) * => attr__(attr_a, b(c), none, none) * => attr_a => __attribute__((a)) * attr_b(c) => __attribute__((b(c))) * attr_too_many_none(none) => * * attr(a, b(c), d) * => attr__(attr_a, b(c), d, none, none) * => attr_a => __attribute__((a)) * attr_b(c) => __attribute__((b(c))) * attr_too_many_d(none, none) => error * * Some attribute names are the same as standard library functions, e.g. printf. * Standard libraries are permitted to define these functions as macros, like * * #define printf(...) __builtin_printf(__VA_ARGS__) * * The token paste in * * #define attr(...) attr__(attr_##__VA_ARGS__, none, none) * * is necessary to prevent macro expansion before evaluating attr__(). * Otherwise, we could get * * attr(printf(1, 2)) * => attr__(__builtin_printf(1, 2), none, none) * => attr____builtin_printf(1, 2) * => error */ #define attr__(a1, a2, a3, a4, a5, a6, a7, a8, a9, none, ...) \ a1 \ attr_##a2 \ attr_##a3 \ attr_##a4 \ attr_##a5 \ attr_##a6 \ attr_##a7 \ attr_##a8 \ attr_##a9 \ attr_too_many_##none(__VA_ARGS__) // Ignore `attr_none` from expanding 1-9 argument attr(a1, a2, ...) #define attr_none // Ignore `attr_` from expanding 0-argument attr() #define attr_ // Only trigger an error on more than 9 arguments #define attr_too_many_none(...) #endif // BFS_PRELUDE_H