/* date = August 19th 2023 1:25 pm */

#ifndef BASE_MACROS_H
#define BASE_MACROS_H

////////////////////////////////
// NOTE(allen): Helper Macros

#define Stmnt(S) do{ S }while(0)

#if !defined(AssertBreak)
# define AssertBreak() (*(volatile int*)0 = 0)
#endif

#if ENABLE_ASSERT
# define Assert(c) Stmnt( if (!(c)){ AssertBreak(); } )
#else
# define Assert(c)
#endif

#define StaticAssert(c,l) typedef U8 Glue(l,__LINE__) [(c)?1:-1]

#define Stringify_(S) #S
#define Stringify(S) Stringify_(S)
#define Glue_(A,B) A##B
#define Glue(A,B) Glue_(A,B)

#define ArrayCount(a) (sizeof(a)/sizeof(*(a)))

#define IntFromPtr(p) (U64)((U8*)p - (U8*)0)
#define PtrFromInt(n) (void*)((U8*)0 + (n))

#define Member(T,m) (((T*)0)->m)
#define OffsetOfMember(T,m) IntFromPtr(&Member(T,m))

#define Implies(a,b) (!(a) || (b))

#define Min(a,b) (((a)<(b))?(a):(b))
#define Max(a,b) (((a)>(b))?(a):(b))
#define Clamp(a,x,b) (((x)<(a))?(a):\
((b)<(x))?(b):(x))
#define ClampTop(a,b) Min(a,b)
#define ClampBot(a,b) Max(a,b)

#define AlignUpPow2(x,p) (((x) + (p) - 1)&~((p) - 1))
#define AlignDownPow2(x,p) ((x)&~((p) - 1))
#define IsPow2OrZero(x) (((x)&((x)-1)) == 0)

#define MagicP(T,x,s) ((T)(x) << (s))
#define MagicU32(a,b,c,d) \
(MagicP(U32,a,0) | MagicP(U32,b,8) | MagicP(U32,c,16) | MagicP(U32,d,24))

#define Boolify(x) ((x) != 0)

#define KB(x) ((x) << 10)
#define MB(x) ((x) << 20)
#define GB(x) ((x) << 30)
#define TB(x) ((U64)(x) << 40llu)

#define Thousand(x) ((x)*1000)
#define Million(x)  ((x)*1000000llu)
#define Billion(x)  ((x)*1000000000llu)
#define Trillion(x) ((x)*1000000000000llu)

#if LANG_CXX
# define c_linkage extern "C"
#else
# define c_linkage extern
#endif

#if COMPILER_CL
# define threadvar __declspec(thread)
#elif COMPILER_CLANG
# define threadvar __thread
#else
# error threadvar defined for this compiler
#endif

#if COMPILER_CL
# define shared_export __declspec(dllexport)
#elif COMPILER_CLANG
# define shared_export __declspec(dllexport)
#else
# error shared_export not defined for this compiler
#endif

#if COMPILER_CLANG
# define noinstrument __attribute__((no_instrument_function))
#else
# define noinstrument
#endif

// TODO(allen): clean up this situation
#define link_global    c_linkage
#define link_threadvar c_linkage threadvar
#define global   static
#define local    static
#define function static
#define link_function   c_linkage
#define shared_function c_linkage shared_export
// TODO(allen): set this to be *real* read only data
#define read_only

#include <string.h>
#define MemoryZero(p,z)      memset((p), 0, (z))
#define MemoryZeroStruct(p)  MemoryZero((p), sizeof(*(p)))
#define MemoryZeroArray(p)   MemoryZero((p), sizeof(p))
#define MemoryZeroTyped(p,c) MemoryZero((p), sizeof(*(p))*(c))

#define MemoryMatch(a,b,z) (memcmp((a),(b),(z)) == 0)

#define MemoryCopy(d,s,z) memmove((d), (s), (z))
#define MemoryCopyStruct(d,s) MemoryCopy((d),(s),\
Min(sizeof(*(d)),sizeof(*(s))))
#define MemoryCopyArray(d,s)  MemoryCopy((d),(s),Min(sizeof(s),sizeof(d)))
#define MemoryCopyTyped(d,s,c) MemoryCopy((d),(s),\
Min(sizeof(*(d)),sizeof(*(s)))*(c))

#if COMPILER_CLANG && ENABLE_SANITIZER
# include <sanitizer/asan_interface.h>
#endif

#if ENABLE_SANITIZER
# define AsanPoison(p,z)   __asan_poison_memory_region((p),(z))
# define AsanUnpoison(p,z) __asan_unpoison_memory_region((p),(z))
#else
# define AsanPoison(p,z)
# define AsanUnpoison(p,z)
#endif

#define AsciiID4(a,b,c,d) (((d) << 24) | ((c) << 16) | ((b) << 8) | (a))
#define ExpandAsciiID(x) (int)(sizeof(x)), (char*)(&(x))

////////////////////////////////
// NOTE(allen): Linked List Macros

#define DLLPushBack_NP(f,l,n,next,prev) ((f)==0?\
((f)=(l)=(n),(n)->next=(n)->prev=0):\
((n)->prev=(l),(l)->next=(n),(l)=(n),(n)->next=0))
#define DLLPushBack(f,l,n) DLLPushBack_NP(f,l,n,next,prev)

#define DLLPushFront(f,l,n) DLLPushBack_NP(l,f,n,prev,next)

#define DLLRemove_NP(f,l,n,next,prev) ((f)==(n)?\
((f)==(l)?\
((f)=(l)=(0)):\
((f)=(f)->next,(f)->prev=0)):\
(l)==(n)?\
((l)=(l)->prev,(l)->next=0):\
((n)->next->prev=(n)->prev,\
(n)->prev->next=(n)->next))
#define DLLRemove(f,l,n) DLLRemove_NP(f,l,n,next,prev)

#define SLLQueuePush_N(f,l,n,next) (((f)==0?\
(f)=(l)=(n):\
((l)->next=(n),(l)=(n))),\
(n)->next=0)
#define SLLQueuePush(f,l,n) SLLQueuePush_N(f,l,n,next)

#define SLLQueuePushFront_N(f,l,n,next) ((f)==0?\
((f)=(l)=(n),(n)->next=0):\
((n)->next=(f),(f)=(n)))
#define SLLQueuePushFront(f,l,n) SLLQueuePushFront_N(f,l,n,next)

#define SLLQueuePop_N(f,l,next) ((f)==(l)?\
(f)=(l)=0:\
((f)=(f)->next))
#define SLLQueuePop(f,l) SLLQueuePop_N(f,l,next)

#define SLLStackPush_N(f,n,next) ((n)->next=(f),(f)=(n))
#define SLLStackPush(f,n) SLLStackPush_N(f,n,next)

#define SLLStackPop_N(f,next) ((f)==0?0:\
((f)=(f)->next))
#define SLLStackPop(f) SLLStackPop_N(f,next)

#endif //BASE_MACROS_H
