/// Macros header
///
/// # Features
/// * `_EVAL_DEBUG_ONLY_STMTS` - Evaluate expressions passed to macros that are no-ops when not in debug mode.
# ifndef _MACROS_H
# define _MACROS_H
# include <stdio.h>
# include <stdlib.h>
# include <stdint.h>
# include <stdarg.h>
# include <assert.h>
// Attribute macros
# define _pure __attribute__((const))
# define _readonly __attribute__((pure))
# define _cold __attribute__((cold))
# define _hot __attribute__((hot))
# define _dead __attribute__((unused))
# ifndef __cplusplus
# define noreturn __attribute__((noreturn))
# define _deprecated __attribute__((deprecated))
# define deprecated_message(str) __attribute__((deprecated(str)))
# define _deprecated_msg(str) deprecated_message(str)
# endif
# define noinline __attribute__((noinline))
# define always_inline __attribute__((always_inline))
# define force_inline __attribute__((gnu_inline))
# ifdef DEBUG
# define _mixin static inline force_inline
# else
# define _mixin extern inline force_inline
# endif
# define unrestrict __attribute__((may_alias))
# define _shared unrestrict
# ifdef __cplusplus
# define restrict __restrict__
# endif
// Function conventions
# define _cconv__mixin _mixin
# define _cconv__pure _pure
# define _cconv__readonly _readonly
# define _cconv__cold _cold
# define _cconv__dead _dead
# define _cconv__unused _dead
# define _cconv__hot _hot
# define _cconv__entry __attribute__((constructor))
# define _cconv__exit __attribute__((destructor))
// Allocates
# define _cconv__alloc __attribute__((malloc))
// Infallible alloc
# define _cconv__ialloc __attribute__((malloc)) __attribute__((returns_nonnull))
# define _cconv__nonnull __attribute__((returns_nonnull))
# define _callcv(name) _cconv__ ## name
# define _callconv(name) _callcv(name)
# define _callat_entry _callcv(entry)
# define _callat_exit _callcv(exit)
# define _callat(when) _callat_ ## when
// Argument attribute
// Used like: `int *pOUT output, const int *pIN input`
# define pOUT restrict
# define pIN const restrict
# define pINOUT restrict
// Used like: `int _OUT output, const int _IN input`
# define _IN *pIN
# define _OUT *pOUT
# define _INOUT *pINOUT
// Type macros
# define AS(x, t) ((t)(x))
# ifdef __cplusplus
# define let auto
# define var(x) decltype(x)
# else
# define let __auto_type
# define var(x) __typeof((x))
# endif
// Control flow macros
# define LIKELY(x) __builtin_expect(!!(x), 1)
# define UNLIKELY(x) __builtin_expect(!!(x), 0)
# define ifL(x) if(LIKELY((x)))
# define ifU(x) if(UNLIKELY((x)))
# define COLD_EXPR(expr) ({ \
goto _cold_expr ; \
UNREACHABLE_UNSAFE ( ) ; \
_cold_expr : \
__attribute__ ( ( cold ) ) ; \
expr ; \
} )
# define HOT_EXPR(expr) ({ \
goto _hot_expr ; \
UNREACHABLE_UNSAFE ( ) ; \
_hot_expr : \
__attribute__ ( ( hot ) ) ; \
expr ; \
} )
// Use like: COLD_BLOCK { ... }
// XXX: I'm not exactly sure if these hacks work.. Should test.
# define COLD_BLOCK if(UNLIKELY(1))
// Use like: HOT_BLOCK { ... }
# define HOT_BLOCK if(LIKELY(1))
/// Equivalent to GNU C `x ?: y` operator.
# define TERN_OR(x, y) ({ let _x = (x); _x ? _x : (y); })
// Statement macros
# define _no_op ((void)0)
# define IGNORE(x) ((void)(x))
_mixin void _drain_val ( void * x , . . . ) { IGNORE ( x ) ; } // This compiles to no-op on -O0 and -O2+ due to `gnu_inline` (_mixin)
# define _drain(...) _drain_val(NULL, __VA_ARGS__)
# define IGNORE_ALL(...) _drain(__VA_ARGS__)
// Allocation macros
# define box(t) aligned_alloc(_Alignof(t), sizeof(t))
# define box_value(v) ({ let _box = box(var(v)); *_box = (v); _box; })
# define unbox_value(v) ({ let _v = (v); let _res = *_v; free(_v); _res; })
# define stackalloc(t) __builtin_alloca_with_align(sizeof(t), _Alignof(t))
// Function macros
// debug-only statements
# ifdef DEBUG
# define DBG(x) x
# else
# define DBG(x) ((void)0)
# endif
// Assertions
# define assert_msg(expr, ...) ( (expr) ? 1 : FATAL(__VA_ARGS__))
# define UNREACHABLE_UNSAFE() __builtin_unreachable()
# ifdef DEBUG
# define UNREACHABLE() do { COLD_EXPR(FATAL("unreachable code entered")); } while(0)
# else
# define UNREACHABLE() UNREACHABLE_UNSAFE()
# endif
# ifdef DEBUG
# define debug_assert(x) assert((x))
# elif defined(_EVAL_DEBUG_ONLY_STMTS)
# define debug_assert(x) IGNORE(x)
# else
# define debug_assert(x) _no_op
# endif
# define assert_eq(x,y) assert((x) == (y))
# define assert_ne(x,y) assert((x) != (y))
# define debug_assert_eq(x,y) debug_assert((x) == (y))
# define debug_assert_ne(x,y) debug_assert((x) != (y))
# ifdef DEBUG
# define debug_static_assert(x, msg) _Static_assert((x), msg)
# else
# define debug_static_assert(x, msg) _Static_assert(1, msg)
# endif
# define static_assert_eq(x,y,msg) _Static_assert((x) == (y), msg)
# define static_assert_ne(x,y,msg) _Static_assert((x) != (y), msg)
// Bitmasking
# define _BSWAP(x) __builtin_bswa x
# define BSWAP_16(x) _BSWAP(p16) (AS((x), uint16_t))
# define BSWAP_32(x) _BSWAP(p32) (AS((x), uint32_t))
# define BSWAP_64(x) _BSWAP(p64) (AS((x), uint64_t))
# define BSWAP_128(x) _BSWAP(p128)(AS((x), uint128_t))
# undef _BSWAP
# define _BSWAP(n) __builtin_bswap ## n
# define bswap(x) _Generic((x), \
uint64_t : _BSWAP ( 64 ) , int64_t : _BSWAP ( 64 ) , \
uint32_t : _BSWAP ( 32 ) , int32_t : _BSWAP ( 32 ) , \
uint16_t : _BSWAP ( 16 ) , int16_t : _BSWAP ( 16 ) , \
__uint128_t : _BSWAP ( 128 ) , __int128_t : _BSWAP ( 128 ) ) \
( ( x ) )
static_assert_eq ( bswap ( 128lu ) , 9223372036854775808lu , " bswap128 (lu) failed (1) " ) ;
static_assert_eq ( 128lu , bswap ( 9223372036854775808lu ) , " bswap128 (lu) failed (2) " ) ;
static_assert_eq ( bswap ( bswap ( 128lu ) ) , 128 , " bswap128 (lu) failed (3) " ) ;
// Transmute
# define TRANSMUTE(val, type) ({ union _trans { var(val) input; type output; }; \
_Static_assert ( sizeof ( union _trans ) = = sizeof ( type ) , " Cannot transmute values of different sizes " ) ; \
/* XXX: Do we need do check this for equality? Can we re-word it or do we even need it at all? _Static_assert(_Alignof(union _trans) == _Alignof(type), "Cannot transmute values of different alignments");*/ \
union _trans _trans__value = { . input = ( val ) , } ; \
_trans__value . output ; } )
// Trace message output
# include "trace.h"
# define TRACEx(L, X, msg, ...) (_t_fprintf(TRACE_LEVEL_ ## L, stderr, "[" X "] " __FILE__ ":%d->%s(): " msg "\n", __LINE__, __func__, ## __VA_ARGS__))
# ifdef DEBUG
# define dprintf(msg, ...) TRACEx(DEBUG, "debug", msg, ## __VA_ARGS__)
# elif defined(_EVAL_DEBUG_ONLY_STMTS)
# define dprintf(msg, ...) _drain(msg, ## __VA_ARGS__)
# endif
# define TRACE(msg, ...) TRACEx(DEBUG, "trace", msg, ## __VA_ARGS__)
# define INFO(msg, ...) TRACEx(INFO, "info", msg, ## __VA_ARGS__)
# define WARN(msg, ...) TRACEx(WARN, "warning", msg, ## __VA_ARGS__)
# define ERROR(msg, ...) TRACEx(ERROR, "error", msg, ## __VA_ARGS__)
# define FATAL(msg, ...) (TRACEx(FATAL, "FATAL", msg, ## __VA_ARGS__), abort())
# define TODO(x, ...) FATAL("function %s() is unimplemented: " x, __func__, ## __VA_ARGS__)
// Debug testing
# define TEST_OK 1
# define TEST_FAILED 0
# define TESTFUN(name) _test__ ## name
# define CALLTEST(name) (assert_msg(TESTFUN(name)() == TEST_OK, "Test `" #name "` failed"), TRACE("Test `" #name "` passed!"))
# define DEFTEST(name) int _test__ ## name(void)
# define TEST_ASSERT(x) do { if (!(x)) {ERROR("Test `%s' assertion failure: `" #x "`", __func__ + 7); return TEST_FAILED;} } while(0)
# define TEST_ASSERT_EQ(x,y) TEST_ASSERT( (x) == (y) )
# define TEST_ASSERT_NE(x,y) TEST_ASSERT( (x) != (y) )
// Version macros
# define _AS_u32(x) ((uint32_t)(x))
# define VER_COMP_MAJ 24u
# define VER_COMP_MIN 16u
# define VER_COMP_BF 8u
# define VER_COMP_REV 0u
/// Create a `uint32_t` from these version componants
# define VERSION(maj,min,bf,rev) _AS_u32( _AS_u32(maj) << 24u | _AS_u32(min) << 16u | _AS_u32(bf) << 8u | _AS_u32(rev) )
/// Mask `ver` to retain only the version component specified by `mask`
///
/// # Example
/// `_Static_assert( (VERSION_COMP(VERSION(1,2,3,4), VER_COMP_MIN) >> VER_COMP_MIN) == 2u)`
# define VERSION_COMP(ver, mask) _AS_u32( _AS_u32(ver) & (0xffu << _AS_u32(mask)) /*>> _AS_u32(mask)*/ )
# define VERSION_MAJ(ver) _AS_u32(VERSION_COMP((ver), 24u) >> 24u)
# define VERSION_MIN(ver) _AS_u32(VERSION_COMP((ver), 16u) >> 16u)
# define VERSION_BF(ver) _AS_u32(VERSION_COMP((ver), 8u) >> 8u)
# define VERSION_REV(ver) _AS_u32(_AS_u32(ver) & 0xffu)
_Static_assert ( ( VERSION_COMP ( VERSION ( 1 , 2 , 3 , 4 ) , VER_COMP_MIN ) > > VER_COMP_MIN ) = = 2u , " invalid native version spec " ) ;
// Misc.
# define PTR_ASSIGN(ptr, val) ( (ptr) ? (*(ptr) = (val), (ptr)) : (ptr) )
# define PTR_ASSIGNv(ptr, val) ({ let _ptr = (ptr); let _val = (val); PTR_ASSIGN(_ptr, _val); })
# endif /* _MACROS_H */