#pragma once #include #include #include #include #include #ifdef __has_feature #if __has_feature(address_sanitizer) #define USE_ASAN 1 #endif #endif #if defined(__SANITIZE_ADDRESS__) #define USE_ASAN 1 #endif // Types typedef int8_t S8; typedef int16_t S16; typedef int32_t S32; typedef int64_t S64; typedef uint8_t U8; typedef uint16_t U16; typedef uint32_t U32; typedef uint64_t U64; typedef uintptr_t UMM; typedef intptr_t IMM; typedef float F32; typedef double F64; #if defined(__FLT128_MANT_DIG__) typedef _Float128 F128; #elif defined(__SIZEOF_FLOAT128__) typedef __float128 F128; #endif #ifdef __SIZEOF_INT128__ typedef __int128 S128; typedef unsigned __int128 U128; #endif #define U8_Max ((U8) 0xff) #define U16_Max ((U16)0xffff) #define U32_Max ((U32)0xffffffff) #define U64_Max ((U64)0xffffffffffffffffULL) // Duh .. #define U8_Min ((U8)0x0) #define U16_Min ((U16)0x0) #define U32_Min ((U32)0x0) #define U64_Min ((U64)0x0) #define S8_Max ((S8) 0x7f) #define S16_Max ((S16)0x7fff) #define S32_Max ((S32)0x7fffffff) #define S64_Max ((S64)0x7fffffffffffffffLL) #define S8_Min ((S8) 0x80) #define S16_Min ((S16)0x8000) #define S32_Min ((S32)0x80000000) #define S64_Min ((S64)0x8000000000000000LL) // asserts #define Trap() __builtin_trap() #define Abort() __builtin_abort() #define Assert(x) do { if (!(x)) { Trap(); }} while(0) #define NotImplemented Assert(!"Not Implemented") #define ShouldNotHappen Assert(!"This should not happen") #define StaticAssert(expr) typedef int STATIC_ASSERT_FAILED[(expr) ? 1 : -1] // misc utilities #define Max(a, b) \ ({ \ __typeof__(a) _amx = (a); \ __typeof__(b) _bmx = (b); \ _amx > _bmx ? _amx : _bmx;\ }) #define Min(a, b) \ ({ \ __typeof__(a) _amn = (a); \ __typeof__(b) _bmn = (b); \ _amn < _bmn ? _amn : _bmn; \ }) #define ArraySize(a) (sizeof(a) / sizeof((a)[0])) #define Swap(a, b) \ do { \ __typeof__(a) tmp_ = (a); \ (a) = (b); \ (b) = tmp_; \ } while (0) #define ContainerOf(ptr, type, member) \ ({ \ typeof(((type *)0)->member) *__mptr = (ptr); \ (type *)((void *)(unsigned char *)__mptr - offsetof(type, member)); \ }) #define ConstContainerOf(ptr, type, member) \ ({ \ const typeof(((const type *)0)->member) *__mptr = (ptr); \ (const type *)((const void *)(const unsigned char *)__mptr - offsetof(const type, member)); \ }) // `align` must be a power of two. // // These keep the result in the *same integer type as the first argument* #define AlignUp(val, align) \ ({ \ __typeof__(val) AlignUp_v_ = (val); \ __typeof__(val) AlignUp_a_ = (__typeof__(val))(align); \ (__typeof__(val))((AlignUp_v_ + (AlignUp_a_ - 1)) & \ ~(__typeof__(val))(AlignUp_a_ - 1)); \ }) #define AlignDown(val, align) \ ({ \ __typeof__(val) AlignDown_v_ = (val); \ __typeof__(val) AlignDown_a_ = (__typeof__(val))(align); \ (__typeof__(val))(AlignDown_v_ & ~(__typeof__(val))(AlignDown_a_ - 1));\ }) #define IsPow2(x) \ ({ \ __typeof__(x) IsPow2_x_ = (x); \ IsPow2_x_ && !(IsPow2_x_ & (__typeof__(x))(IsPow2_x_ - 1)); \ }) #define IsPow2OrZero(x) \ ({ \ __typeof__(x) IsPow2OrZero_x_ = (x); \ !(IsPow2OrZero_x_ & (__typeof__(x))(IsPow2OrZero_x_ - 1)); \ }) #define IsAlignedTo(x, align) \ ({ \ __typeof__(x) IsAlignedTo_x_ = (x); \ __typeof__(x) IsAlignedTo_a_ = (__typeof__(x))(align); \ IsPow2(IsAlignedTo_a_) && \ !(IsAlignedTo_x_ & (__typeof__(x))(IsAlignedTo_a_ - 1)); \ }) #define GetBit(val, idx) (((val) >> (idx)) & 1) // Get n_bits starting at idx going left #define GetBits(val, n_bits, idx) \ (((n_bits) == 0 ? 0 : ((value) >> (idx)) & ((((__typeof__(val))(1) << (n_bits)) - 1))) #define STRINGIFY(x) STRINGIFY_HLP(x) #define STRINGIFY_HLP(x) #x #define SRC_LOCATION __FILE__ ":" STRINGIFY(__LINE__) #ifdef DEBUG #define TRACEF(fmt, ...) \ do { \ fprintf(stdout, "%s (%s)\t" fmt, SRC_LOCATION, __FUNCTION__, \ ##__VA_ARGS__); \ fflush(stdout); \ } while (0) #else #define TRACEF(fmt, ...) \ do { \ } while (0) #endif /** * Ceiling of X / y , or round up x / y * (e.g 100/9 == 12, 3/2 == 2) * x + y must not overflow. * * @param x numerator, must be positive * @param y denominator * @return x/y rounded up */ #define CeilDiv(x, y) (((x) + ((y) - 1)) / (y)) /** * Round x up to nearest multiple of y: * (((x) + (y) - 1) - (((x) + (y) - 1) % (y))) * e.g. RoundUp(30,48) == 48 * e.g. RoundUp(49,48) == 96 * * @param x numerator, must be positive * @param y denominator * @return x rounded up to nearest multiple of y */ #define RoundUpToMultiple(x, y) \ ({ \ __typeof__(x) _x = (x); \ __typeof__(y) _y = (y); \ Assert(_y != 0); \ __typeof__(x) _tmp = _x + _y - 1; \ _tmp - _tmp % _y; \ }) /** * Round x down to nearest multiple of y * e.g. RoundDown(30,48) == 0 * e.g. RoundDown(49,48) == 48 * * @param x numerator, must be positive * @param y denominator * @return x rounded down nearest multiple of y */ #define RoundDownToMultiple(x, y) ((x) / (y) * (y)) #define Clamp(val, min, max) (((val) < (min)) ? (min) : ((val) > (max)) ? (max) : (val)) #ifdef __GNUC__ #define INLINE inline __attribute__((always_inline, flatten)) #else #define INLINE inline #endif #ifdef __cplusplus #define C_LINKAGE_BEGIN extern "C" { #define C_LINKAGE_END } #define C_LINKAGE extern "C" #else #define C_LINKAGE_BEGIN #define C_LINKAGE_END #define C_LINKAGE #endif /** Indicate the variable is unused. */ #define UNUSED(x) x __attribute__((unused)) // sizes #define Bytes(n) ((U64)(n)) #define KB(n) (((U64)(n)) << 10) #define MB(n) (((U64)(n)) << 20) #define GB(n) (((U64)(n)) << 30) #define TB(n) (((U64)(n)) << 40) #define Thousand(n) (((U64)(n) * 1000)) #define Million(n) (((U64)(n) * 1000000)) #define Billion(n) (((U64)(n) * 1000000000)) // memory #define MemSet(ptr, val, len) memset(ptr, val, len) #ifdef USE_ASAN #define MemoryZero(ptr, len) memset(ptr, 0, len) // explicity bzero is usually not being intercepted #else #define MemoryZero(ptr, len) explicit_bzero(ptr, len) #endif #define MemMove(dst, src, len) memmove(dst, src, len) #define MemCopy(dst, src, len) memcpy(dst, src, len) #define MemAppend(dst, src, len) mempcpy(dst, src, len) #define MemCompare(first, second, len) memcmp(first, second, len) static INLINE U8 *MemFind(U8 *mem, U64 mem_len, U8 *needle, U64 needle_len) { if (needle_len > mem_len) { return NULL; } for (U64 i = 0 ; i + needle_len <= mem_len; i++) { if (MemCompare(&mem[i], needle, needle_len) == 0) { return &mem[i]; } } return NULL; } // atomics #define Atomic(type) _Atomic(type) #define AtomicSub(ptr, val) __atomic_fetch_sub((ptr), (val), __ATOMIC_SEQ_CST) #define AtomicAdd(ptr, val) __atomic_fetch_add((ptr), (val), __ATOMIC_SEQ_CST) #define AtomicDec(ptr) AtomicSub((ptr), 1) #define AtomicInc(ptr) AtomicAdd((ptr), 1) #define AtomicGet(ptr) __c11_atomic_load((ptr), __ATOMIC_SEQ_CST) #define AtomicCAS(ptr, oldval, newval) \ __atomic_compare_exchange((ptr), (oldval), (newval),0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST) #define MemoryBarrier() __atomic_thread_fence(UC_ORDER_CST) #define CompilerBarrier() __asm__ __volatile__("": : :"memory") // Saturating operations #define SaturatingAdd(a, b, type, max_val)\ ({type res ;\ if (__builtin_add_overflow(a, b, &res)) {\ res = max_val;}\ res;}) #define SaturatingSub(a, b, type)\ ({type res ;\ if (__builtin_sub_overflow(a, b, &res)) {\ res = 0;}\ res;}) #define SaturatingMult(a, b, type, max_val)\ ({type res ;\ if (__builtin_mul_overflow(a, b, &res)) {\ res = max_val;}\ res;}) static INLINE U8 SaturatingAddU8(U8 a, U8 b) { return SaturatingAdd(a, b, U8, U8_Max); } static INLINE U8 SaturatingSubU8(U8 a, U8 b) { return SaturatingSub(a, b, U8); } static INLINE U8 SaturatingMult8(U8 a, U8 b) { return SaturatingMult(a, b, U8, U8_Max); } static INLINE U16 SaturatingAdd16(U16 a, U16 b) { return SaturatingAdd(a, b, U16, U16_Max); } static INLINE U16 SaturatingSubU16(U16 a, U16 b) { return SaturatingSub(a, b, U16); } static INLINE U16 SaturatingMultU16(U16 a, U16 b) { return SaturatingMult(a, b, U16, U16_Max); } static INLINE U32 SaturatingAdd32(U32 a, U32 b) { return SaturatingAdd(a, b, U32, U32_Max); } static INLINE U32 SaturatingSub32(U32 a, U32 b) { return SaturatingSub(a, b, U32); } static INLINE U32 SaturatingMult32(U32 a, U32 b) { return SaturatingMult(a, b, U32, U32_Max); } static INLINE U64 SaturatingAdd64(U64 a, U64 b) { return SaturatingAdd(a, b, U64, U64_Max); } static INLINE U64 SaturatingSubU64(U64 a, U64 b) { return SaturatingSub(a, b, U64); } static INLINE U64 SaturatingMult64(U64 a, U64 b) { return SaturatingMult(a, b, U64, U64_Max); } // Allocators typedef enum AllocOperation AllocOperation; enum AllocOperation { Alloc_Op_Aligned = 1, Alloc_Op_Free, Alloc_Op_Free_All, }; typedef struct Allocator Allocator; typedef void *(*AllocOpFunc)(Allocator *allocator, AllocOperation operation, void *memory, U32 size, U32 align); struct Allocator { AllocOpFunc op_func_ptr; void *data; }; static INLINE void *allocator_alloc_aligned(Allocator *allocator, U32 size, U32 align) { return allocator->op_func_ptr(allocator, Alloc_Op_Aligned, NULL, size, align); } static INLINE void allocator_free(Allocator *allocator, void *memory) { allocator->op_func_ptr(allocator, Alloc_Op_Free, memory, 0, 0); } static INLINE void allocator_free_all(Allocator *allocator) { allocator->op_func_ptr(allocator, Alloc_Op_Free_All, NULL, 0, 0); } #define allocator_push_type(allocator, type_)\ (allocator)->op_func_ptr((allocator), Alloc_Op_Aligned, NULL, sizeof(type_), _Alignof(type_)) #define allocator_push_array(allocator, type_, nr_elements)\ (allocator)->op_func_ptr((allocator), Alloc_Op_Aligned, NULL, sizeof(type_) * (nr_elements), _Alignof(type_) ) #define allocator_push(allocator, size, align)\ (allocator)->op_func_ptr((allocator), Alloc_Op_Aligned, NULL, size, align) // address-sanitizer helpers (-fsanitize=address) #ifdef USE_ASAN #include #define ASAN_POISON_MEMORY_REGION(addr, size) \ __asan_poison_memory_region((addr), (size)) #define ASAN_UNPOISON_MEMORY_REGION(addr, size) \ __asan_unpoison_memory_region((addr), (size)) #else #define ASAN_POISON_MEMORY_REGION(addr, size) \ ((void)(addr), (void)(size)) #define ASAN_UNPOISON_MEMORY_REGION(addr, size) \ ((void)(addr), (void)(size)) #endif // linked lists /* SList - singly linked lists "list" struct must have a "*first" and "**last" member to entrys "first"/"last" structs must have a "*next" member */ /** * Initialize a list * * @param list struct pointer with a *first and a **last member to an entry struct */ #define SListInit(list) \ do { \ (list)->first = NULL; \ (list)->last = &(list)->first; \ } while (0) /** * Adds entry to the head of a list * * @param list struct pointer with a *first and a **last member to an entry struct * @param entry struct pointer with a *next member to an entry struct */ #define SListAddFirst(list, entry) \ do { \ (entry)->next = (list)->first; \ if ((entry)->next == NULL) { \ (list)->last = &(entry)->next; \ } \ (list)->first = (entry); \ } while (0) /** * Adds entry to the tail of a list * * @param list struct pointer with a *first and a **last member to an entry struct * @param entry struct pointer with a *next member to an entry struct */ #define SListAddLast(list, entry) \ do { \ (entry)->next = NULL; \ *(list)->last = (entry); \ (list)->last = &(entry)->next; \ } while (0) /** * Remove head from list, list MUST not be empty * * @param entry struct pointer with a *next member to an entry struct */ #define SListRemoveFirst(list) \ do { \ (list)->first = (list)->first->next; \ if ((list)->first == NULL) { \ (list)->last = &(list)->first; \ } \ } while (0) #define SListIsEmpty(list) ((list)->first == NULL) #define SListResetNode(node) ((node)->next = NULL) /* DList - doubly linked lists "list" struct must have a "*first" and "*last" member to entrys "first"/"last" structs must have a "*next" and a "*prev" member */ /** * Initialize a list * * @param list struct pointer with a *first and a *last member to an entry struct */ #define DListInit(list) \ do { \ (list)->first = NULL; \ (list)->last = NULL; \ } while (0) /** * Adds entry to the head of a list * * @param list struct pointer with a *first and a *last member to an entry struct * @param entry struct pointer with a *next and *prev member to an entry struct */ #define DListAddFirst(list, entry) \ do { \ (entry)->next = (list)->first; \ (entry)->prev = NULL; \ if ((list)->first != NULL) { \ (list)->first->prev = (entry); \ } else { \ (list)->last = (entry); \ } \ (list)->first = (entry); \ } while (0) /** * Adds entry to the tail of a list * * @param list struct pointer with a *first and a *last member to an entry struct * @param entry struct pointer with a *next and *prev member to an entry struct */ #define DListAddLast(list, entry) \ do { \ (entry)->prev = (list)->last; \ (entry)->next = NULL; \ if ((list)->last != NULL) { \ (list)->last->next = (entry); \ } else { \ (list)->first = (entry); \ } \ (list)->last = (entry); \ } while (0) /** * Remove first entry from list, list MUST not be empty * * @param entry struct pointer with a *next and *prev member to an entry struct */ #define DListRemoveFirst(list) \ do { \ (list)->first = (list)->first->next; \ if ((list)->first != NULL) { \ (list)->first->prev = NULL; \ } else { \ (list)->last = NULL; \ } \ } while (0) /** * Remove last entry from list, list MUST not be empty * * @param entry struct pointer with a *next and *prev member to an entry struct */ #define DListRemoveLast(list) \ do { \ (list)->last = (list)->last->prev; \ if ((list)->last != NULL) { \ (list)->last->next = NULL; \ } else { \ (list)->first = NULL; \ } \ } while (0) /** * Remove last entry from list, entry must be in the list * * @param entry struct pointer with a *next and *prev member to an entry struct */ #define DListRemove(list, entry) \ do { \ if ((entry)->next != NULL) { \ (entry)->next->prev = (entry)->prev; \ } else { \ (list)->last = (entry)->prev; \ } \ if ((entry)->prev != NULL) { \ (entry)->prev->next = (entry)->next; \ } else { \ (list)->first = (entry)->next; \ } \ } while (0) #define DListIsEmpty(list) ((list)->first == NULL) #define DListResetNode(node) ((node)->next = (node)->prev = NULL)