#ifdef INTELLISENSE_DIRECTIVES
#	pragma once
#	include "context_cracking.h"
#	include "linkage.h"
#	include "macros.h"
#	include "platform.h"
#	include "base_types.h"
#endif

////////////////////////////////
//~ rjf: Units

#ifndef KILOBTYES
#define KILOBYTES( x ) (             ( x ) * ( S64 )( 1024 ) )
#endif
#ifndef MEGABYTES
#define MEGABYTES( x ) ( MD_KILOBYTES( x ) * ( S64 )( 1024 ) )
#endif
#ifndef GIGABYTES
#define GIGABYTES( x ) ( MD_MEGABYTES( x ) * ( S64 )( 1024 ) )
#endif
#ifndef TERABYTES
#define TERABYTES( x ) ( MD_GIGABYTES( x ) * ( S64 )( 1024 ) )
#endif

#ifndef KB
#define KB(n)  (((U64)(n)) << 10)
#endif
#ifndef MB
#define MB(n)  (((U64)(n)) << 20)
#endif
#ifndef GB
#define GB(n)  (((U64)(n)) << 30)
#endif
#ifndef TB
#define TB(n)  (((U64)(n)) << 40)
#endif

#ifndef thosuand
#define thousand(n)   ((n) * 1000)
#endif
#ifndef million
#define million(n)    ((n) * 1000000)
#endif
#ifndef billion
#define billion(n)    ((n) * 1000000000)
#endif

////////////////////////////////
//~ rjf: Type -> Alignment

#ifndef align_of
#	if COMPILER_MSVC
#		define align_of(T) __alignof(T)
#	elif COMPILER_CLANG
#		define align_of(T) __alignof(T)
#	elif COMPILER_GCC
#		define align_of(T) __alignof__(T)
#	else
#		error AlignOf not defined for this compiler.
#	endif
#endif

////////////////////////////////
//~ rjf: Member Offsets

#ifndef member
#define member(T, m)                    ( ((T*) 0)->m )
#endif
#ifndef offset_of
#define offset_of(T, m)                 int_from_ptr(& member(T, m))
#endif
#ifndef member_from_offset
#define member_from_offset(T, ptr, off) (T)  ((((U8 *) ptr) + (off)))
#endif
#ifndef cast_from_member
#define cast_from_member(T, m, ptr)     (T*) (((U8*)ptr) - offset_of(T, m))
#endif

////////////////////////////////
//~ rjf: Memory Operation Macros


// TODO(Ed): Review usage of memmove here...(I guess wanting to avoid overlap faults..)
#ifndef memory_copy
#	if OS_WINDOWS
		void* memcpy_intrinsic(void* dest, const void* src, size_t count) 
		{
			if (dest == NULL || src == NULL || count == 0) {
				return NULL;
			}

			__movsb((unsigned char*)dest, (const unsigned char*)src, count);
			return dest;
		}
#		define memory_copy(dst, src, size)        memcpy_intrinsic((dst), (src), (size))
#	else
#		define memory_copy(dst, src, size)        memmove((dst), (src), (size))
#	endif
#endif
#ifndef memory_set
#	if USE_VENDOR_MEMORY_OPS
#		define memory_set(dst, byte, size)    memset((dst), (byte), (size))
#	else
#		define memory_set(dst, byte, size)    mem_set((dst), (byte), (size))
#	endif
#endif
#ifndef memory_compare
#define memory_compare(a, b, size)     memcmp((a), (b), (size))
#endif
#ifndef memory_str_len
#define memory_str_len(ptr)            cstr_len(ptr)
#endif

#ifndef memory_copy_struct
#define memory_copy_struct(d, s)       memory_copy((d), (s), sizeof( *(d)))
#endif
#ifndef memory_copy_array
#define memory_copy_array(d, s)        memory_copy((d), (s), sizeof( d))
#endif
#ifndef memory_copy_type
#define memory_copy_type(d, s, c)      memory_copy((d), (s), sizeof( *(d)) * (c))
#endif

#ifndef memory_zero
#define memory_zero(s,z)               memory_set((s), 0, (z))
#endif
#ifndef memory_zero_struct
#define memory_zero_struct(s)          memory_zero((s), sizeof( *(s)))
#endif
#ifndef memory_zero_array
#define memory_zero_array(a)           memory_zero((a), sizeof(a))
#endif
#ifndef memory_zero_type
#define memory_zero_type(m, c)         memory_zero((m), sizeof( *(m)) * (c))
#endif

#ifndef memory_match
#define memory_match(a, b, z)          (memory_compare((a), (b), (z)) == 0)
#endif
#ifndef memory_match_struct
#define memory_match_struct(a, b)      memory_match((a), (b), sizeof(*(a)))
#endif
#ifndef memory_match_array
#define memory_match_array(a, b)       memory_match((a), (b), sizeof(a))
#endif

#ifndef memory_read
#define memory_read(T, p, e)          ( ((p) + sizeof(T) <= (e)) ? ( *(T*)(p)) : (0) )
#endif
#ifndef memory_consume
#define memory_consume(T, p, e)       ( ((p) + sizeof(T) <= (e)) ? ((p) += sizeof(T), *(T*)((p) - sizeof(T))) : ((p) = (e),0) )
#endif

////////////////////////////////
//~ rjf: Memory Functions

inline B32
memory_is_zero(void* ptr, U64 size)
{
	B32 result = 1;
	
	// break down size
	U64 extra  = (size & 0x7);
	U64 count8 = (size >> 3);
	
	// check with 8-byte stride
	U64* p64 = (U64*)ptr;
	if (result)
	{
		for (U64 i = 0; i < count8; i += 1, p64 += 1) {
			if (*p64 != 0){
				result = 0;
				goto done;
			}
		}
	}
	
	// check extra
	if (result)
	{
		U8* p8 = (U8*)p64;
		for (U64 i = 0; i < extra; i += 1, p8 += 1) {
			if (*p8 != 0) {
				result = 0;
				goto done;
			}
		}
	}
	
done:;
	return(result);
}

inline
void* mem_move( void* destination, void const* source, SSIZE byte_count )
{
	if ( destination == NULL )
	{
		return NULL;
	}

	U8*       dest_ptr = rcast( U8*, destination);
	U8 const* src_ptr  = rcast( U8 const*, source);

	if ( dest_ptr == src_ptr )
		return dest_ptr;

	// NOTE: Non-overlapping
	if ( src_ptr + byte_count <= dest_ptr || dest_ptr + byte_count <= src_ptr )  {  
		return memory_copy( dest_ptr, src_ptr, byte_count );
	}

	if ( dest_ptr < src_ptr )
	{
		if ( scast(UPTR, src_ptr) % size_of( SSIZE ) == scast(UPTR, dest_ptr) % size_of( SSIZE ) )
		{
			while ( pcast( UPTR, dest_ptr) % size_of( SSIZE ) )
			{
				if ( ! byte_count-- )
					return destination;

				*dest_ptr++ = *src_ptr++;
			}
			while ( byte_count >= size_of( SSIZE ) )
			{
				* rcast(SSIZE*, dest_ptr)  = * rcast(SSIZE const*, src_ptr);
				byte_count -= size_of( SSIZE );
				dest_ptr   += size_of( SSIZE );
				src_ptr    += size_of( SSIZE );
			}
		}
		for ( ; byte_count; byte_count-- )
			*dest_ptr++ = *src_ptr++;
	}
	else
	{
		if ( ( scast(UPTR, src_ptr) % size_of( SSIZE ) ) == ( scast(UPTR, dest_ptr) % size_of( SSIZE ) ) )
		{
			while ( scast(UPTR, dest_ptr + byte_count ) % size_of( SSIZE ) )
			{
				if ( ! byte_count-- )
					return destination;

				dest_ptr[ byte_count ] = src_ptr[ byte_count ];
			}
			while ( byte_count >= size_of( SSIZE ) )
			{
				byte_count                              -= size_of( SSIZE );
				* rcast(SSIZE*, dest_ptr + byte_count )  = * rcast( SSIZE const*, src_ptr + byte_count );
			}
		}
		while ( byte_count )
			byte_count--, dest_ptr[ byte_count ] = src_ptr[ byte_count ];
	}

	return destination;
}

inline
void* mem_set( void* destination, U8 fill_byte, SSIZE byte_count )
{
	if ( destination == NULL )
	{
		return NULL;
	}

	SSIZE align_offset;
	U8*   dest_ptr  = rcast( U8*, destination);
	U32   fill_word = ( ( U32 )-1 ) / 255 * fill_byte;

	if ( byte_count == 0 )
		return destination;

	dest_ptr[ 0 ] = dest_ptr[ byte_count - 1 ] = fill_byte;
	if ( byte_count < 3 )
		return destination;

	dest_ptr[ 1 ] = dest_ptr[ byte_count - 2 ] = fill_byte;
	dest_ptr[ 2 ] = dest_ptr[ byte_count - 3 ] = fill_byte;
	if ( byte_count < 7 )
		return destination;

	dest_ptr[ 3 ] = dest_ptr[ byte_count - 4 ] = fill_byte;
	if ( byte_count < 9 )
		return destination;

	align_offset  = -scast(SPTR, dest_ptr ) & 3;
	dest_ptr     += align_offset;
	byte_count   -= align_offset;
	byte_count   &= -4;

	* rcast( U32*, ( dest_ptr + 0              ) ) = fill_word;
	* rcast( U32*, ( dest_ptr + byte_count - 4 ) ) = fill_word;
	if ( byte_count < 9 )
		return destination;

	* rcast( U32*, dest_ptr + 4 )               = fill_word;
	* rcast( U32*, dest_ptr + 8 )               = fill_word;
	* rcast( U32*, dest_ptr + byte_count - 12 ) = fill_word;
	* rcast( U32*, dest_ptr + byte_count - 8 )  = fill_word;
	if ( byte_count < 25 )
		return destination;

	* rcast( U32*, dest_ptr + 12 )              = fill_word;
	* rcast( U32*, dest_ptr + 16 )              = fill_word;
	* rcast( U32*, dest_ptr + 20 )              = fill_word;
	* rcast( U32*, dest_ptr + 24 )              = fill_word;
	* rcast( U32*, dest_ptr + byte_count - 28 ) = fill_word;
	* rcast( U32*, dest_ptr + byte_count - 24 ) = fill_word;
	* rcast( U32*, dest_ptr + byte_count - 20 ) = fill_word;
	* rcast( U32*, dest_ptr + byte_count - 16 ) = fill_word;

	align_offset  = 24 + scast(UPTR, dest_ptr ) & 4;
	dest_ptr     += align_offset;
	byte_count   -= align_offset;

	{
		U64 fill_doubleword = ( scast( U64, fill_word) << 32 ) | fill_word;
		while ( byte_count > 31 )
		{
			* rcast( U64*, dest_ptr + 0 )  = fill_doubleword;
			* rcast( U64*, dest_ptr + 8 )  = fill_doubleword;
			* rcast( U64*, dest_ptr + 16 ) = fill_doubleword;
			* rcast( U64*, dest_ptr + 24 ) = fill_doubleword;

			byte_count -= 32;
			dest_ptr += 32;
		}
	}

	return destination;
}

////////////////////////////////
//~ rjf: Atomic Operations

#ifndef ins_atomic_u64_eval
#	if OS_WINDOWS
#		if ARCH_X64
#			define ins_atomic_u64_eval(x)                 InterlockedAdd64((volatile __int64 *)(x), 0)
#			define ins_atomic_u64_inc_eval(x)             InterlockedIncrement64((volatile __int64 *)(x))
#			define ins_atomic_u64_dec_eval(x)             InterlockedDecrement64((volatile __int64 *)(x))
#			define ins_atomic_u64_eval_assign(x,c)        InterlockedExchange64((volatile __int64 *)(x), (c))
#			define ins_atomic_u64_add_eval(x,c)           InterlockedAdd64((volatile __int64 *)(x), c)
#			define ins_atomic_u64_eval_cond_assign(x,k,c) InterlockedCompareExchange64((volatile __int64 *)(x), (k), (c))
#			define ins_atomic_u32_eval(x,c)               InterlockedAdd((volatile LONG *)(x), 0)
#			define ins_atomic_u32_eval_assign(x,c)        InterlockedExchange((volatile LONG *)(x), (c))
#			define ins_atomic_u32_eval_cond_assign(x,k,c) InterlockedCompareExchange((volatile LONG *)(x), (k), (c))
#			define ins_atomic_ptr_eval_assign(x,c)        (void*) ins_atomic_u64_eval_assign((volatile __int64 *)(x), (__int64)(c))
#		else
#			error Atomic intrinsics not defined for this operating system / architecture combination.
#		endif
#	elif OS_LINUX
#		if ARCH_X64
#			define ins_atomic_u64_inc_eval(x) __sync_fetch_and_add((volatile U64 *)(x), 1)
#		else
#			error Atomic intrinsics not defined for this operating system / architecture combination.
#		endif
#	else
#		error Atomic intrinsics not defined for this operating system.
#	endif
#endif

////////////////////////////////
//~ rjf: Linked List Building Macros

//- rjf: linked list macro helpers

#ifndef check_nil
#define check_nil(nil, p) ((p) == 0 || (p) == nil)
#endif
#ifndef set_nil
#define set_nil(nil, p)   ((p) = nil)
#endif

//- rjf: doubly-linked-lists

#ifndef MD_LINKED_LIST_PURE_MACRO
#define MD_LINKED_LIST_PURE_MACRO 0
#endif

#ifndef dll_insert_npz
// TODO(Ed): Review...
inline void
dll__insert_npz(
	void*  nil, 
	void** f, void** f_prev,
	void** l, void** l_next,
	void*  p, void** p_next, void** p_next_prev,
	void*  n, void** n_prev, void** n_next 
)
{
	if (check_nil(nil, *f)) {
		*f      = n;
		*l      = n;
		*n_prev = nil;
		*n_next = nil;
	}
	else
	{
		if (check_nil(nil, p)) {
			*n_next = *f;
			*f_prev =  n;
			*f      =  n;
			*n_prev =  nil;
		}
		else 
		{
			if (p == *l) {
				*l_next =  n;
				*n_prev = *l;
				*l      =  n;
				*n_next =  nil;
			}
			else
			{
				if ( ! check_nil(nil, p) && check_nil(nil, *p_next)) {
					*p_next_prev = n;
				}
				*n_next = *p_next;
				*p_next =  n;
				*n_prev =  p;
			}
		}
	}
}

#if ! MD_LINKED_LIST_PURE_MACRO
// insert next-previous with nil
#define dll_insert_npz(nil, f, l, p, n, next, prev) dll__insert_npz(nil, &f, &f->prev, &l, &l->next, p, &p->next, &p->next->prev, n, &n->prev, &n->next)
#else
// insert next-previous with nil
#define dll_insert_npz(nil, f, l, p, n, next, prev)                        \
(                                                                          \
	check_nil(nil, f) ? (                                                  \
		(f) = (l) = (n),                                                   \
		set_nil(nil, (n)->next),                                           \
		set_nil(nil, (n)->prev)                                            \
	)                                                                      \
	: (                                                                    \
		check_nil(nil, p) ? (                                              \
			(n)->next = (f),                                               \
			(f)->prev = (n),                                               \
			(f) = (n),                                                     \
			set_nil(nil,(n)->prev)                                         \
		)                                                                  \
		: ((p) == (l)) ? (                                                 \
				(l)->next = (n),                                           \
				(n)->prev = (l),                                           \
				(l) = (n),                                                 \
				set_nil(nil, (n)->next)                                    \
			)                                                              \
			: (                                                            \
				( 	                                                       \
					( ! check_nil(nil, p) && check_nil(nil, (p)->next) ) ? \
						(0)                                                \
					:	( (p)->next->prev = (n) )                          \
				),                                                         \
				((n)->next = (p)->next),                                   \
				((p)->next = (n)),                                         \
				((n)->prev = (p))                                          \
			)                                                              \
	)                                                                      \
)
// ! MD_LINKED_LIST_PURE_MACRO
#endif
// dll_insert_npz
#endif

#ifndef dll_push_back_npz
// push-back next-previous with nil
#define dll_push_back_npz(nil, f, l, n, next, prev)  dll_insert_npz(nil, f, l, l, n, next, prev)
#endif

#ifndef dll_push_front_npz
// push-fornt next-previous with nil
#define dll_push_front_npz(nil, f, l, n, next, prev) dll_insert_npz(nil, l, f, f, n, prev, next)
#endif

#ifndef dll_remove_npz
inline void
dll__remove_npz(
	void*  nil, 
	void** f, 
	void** l, void* l_prev, 
	void*  n, void* n_next, void** n_next_prev, 
	          void* n_prev, void** n_prev_next
)
{
	if (n == *f) {
		*f = n_next;
	}
	if (n == *l) {
		*l = l_prev;
	}
	if (check_nil(nil, n_prev)) {
		*n_prev_next = n_next;
	}
	if (! check_nil(nil, n_next)) {
		*n_next_prev = n_prev;
	}
}

#if ! MD_LINKED_LIST_PURE_MACRO
// remove next-previous with nil
#define dll_remove_npz(nil, f, l, n, next, prev) dll__remove_npz(nil, &f, &l, l->prev, n, n->next, &n->next->prev, n->prev, &n->prev->next)
#else
// remove next-previous with nil
#define dll_remove_npz(nil, f, l, n, next, prev) \
(                                                \
	(                                            \
		(n) == (f) ?                             \
			(f) = (n)->next                      \
		: 	(0)                                  \
	),                                           \
	(                                            \
		(n) == (l) ?                             \
			(l) = (l)->prev                      \
		:	(0)                                  \
	),                                           \
	(                                            \
		check_nil(nil,(n)->prev) ?               \
			(0)                                  \
		: 	((n)->prev->next = (n)->next)        \
	),                                           \
	(                                            \
		check_nil(nil,(n)->next) ?               \
			(0)                                  \
		:	((n)->next->prev = (n)->prev)        \
	)                                            \
)
// ! MD_LINKED_LIST_PURE_MACRO
#endif
// dll_remove_npz
#endif

//- rjf: singly-linked, doubly-headed lists (queues)

#ifndef sll_queue_push_nz
inline void
sll__queue_push_nz(
	void* nil, 
	void** f, 
	void** l, void** l_next, 
	void*  n, void** n_next
) 
{
	if (check_nil(nil, *f)) {
		*f      = n;
		*l      = n;
		*n_next = nil;
	}
	else {
		*l_next = n;
		*l      = n;
		*n_next = nil;
	}
}

// queue-push next with nil
#if ! MD_LINKED_LIST_PURE_MACRO
#define sll_queue_push_nz(nil, f, l, n, next) sll__queue_push_nz(nil, &f, &l, &l->next, n, &n->next)
#else
#define sll_queue_push_nz(nil, f, l, n, next) \
(                                             \
	check_nil(nil, f) ? (                     \
		(f) = (l) = (n),                      \
		set_nil(nil, (n)->next)               \
	)                                         \
	: (                                       \
		(l)->next=(n),                        \
		(l) = (n),                            \
		set_nil(nil,(n)->next)                \
	)                                         \
)
// ! MD_LINKED_LIST_PURE_MACRO
#endif
// sll_queue_push_nz
#endif

#ifndef sll_queue_push_front_nz
inline void
sll__queue_push_front_nz(void* nil, void** f, void** l, void* n, void** n_next) {
	if (check_nil(nil, *f)) {
		*f      = n;
		*l      = n;
		*n_next = nil;
	}
	else {
		*n_next = f;
		*f      = n;
	}
}

// queue-push-front next with nil
#if ! MD_LINKED_LIST_PURE_MACRO
#define sll_queue_push_front_nz(nil, f, l, n, next) sll__queue_push_front_nz(nil, &f, &l, n, &n->next)
#else
#define sll_queue_push_front_nz(nil, f, l, n, next) \
(                                                   \
	check_nil(nil, f) ? (                           \
		(f) = (l) = (n),                            \
		set_nil(nil,(n)->next)                      \
	)                                               \
	: (                                             \
		(n)->next = (f),                            \
		(f) = (n)                                   \
	)                                               \
)
// ! MD_LINKED_LIST_PURE_MACRO
#endif
#endif

#ifndef sll_queue_pop_nz
inline void
sll__queue_pop_nz(void* nil, void** f, void* f_next, void** l)
{
	if (*f == *l) {
		*f = nil;
		*l = nil;
	}
	else {
		*f = f_next;
	}
}

// queue-pop next with nil
#if ! MD_LINKED_LIST_PURE_MACRO
#define sll_queue_pop_nz(nil, f, l, next) sll__queue_pop_nz(nil, &f, f->next, &l)
#else
#define sll_queue_pop_nz(nil, f, l, next) \
(                                         \
	(f) == (l) ? (                        \
		set_nil(nil,f),                   \
		set_nil(nil,l)                    \
	)                                     \
	: (                                   \
		(f)=(f)->next                     \
	)                                     \
)
// ! MD_LINKED_LIST_PURE_MACRO
#endif
// sll_queue_pop_nz
#endif

//- rjf: singly-linked, singly-headed lists (stacks)

#ifndef sll_stack_push_n
#define sll_stack_push_n(f,n,next) ( (n)->next = (f), (f) = (n) )
#endif
#ifndef sll_stack_pop_n
#define sll_stack_pop_n(f,next)    ( (f) = (f)->next )
#endif

//- rjf: doubly-linked-list helpers

#ifndef dll_insert_np
#define dll_insert_np(f, l, p, n, next, prev)  dll_insert_npz    (0, f, l, p, n,    next, prev)
#endif
#ifndef dll_push_back_np
#define dll_push_back_np(f, l, n, next, prev)  dll_push_back_npz (0, f, l, n, next,       prev)
#endif
#ifndef dll_push_front_np
#define dll_push_front_np(f, l, n, next, prev) dll_push_front_npz(0, f, l, n, next,       prev)
#endif
#ifndef dll_remove_np
#define dll_remove_np(f, l, n, next, prev)     dll_remove_npz    (0, f, l, n, next,       prev)
#endif
#ifndef dll_insert
#define dll_insert(f, l, p, n)                 dll_insert_npz    (0, f, l, p, n,    next, prev)
#endif
#ifndef dll_push_back
#define dll_push_back(f, l, n)                 dll_push_back_npz (0, f, l, n, next, prev)
#endif
#ifndef dll_push_front
#define dll_push_front(f, l, n)                dll_push_front_npz(0, f, l, n, next, prev)
#endif
#ifndef dll_remove
#define dll_remove(f, l, n)                    dll_remove_npz    (0, f, l, n, next, prev)
#endif

//- rjf: singly-linked, doubly-headed list helpers

#ifndef sll_queue_push_n
#define sll_queue_push_n(f, l, n, next)       sll_queue_push_nz      (0, f, l, n, next)
#endif
#ifndef sll_queue_push_front_n
#define sll_queue_push_front_n(f, l, n, next) sll_queue_push_front_nz(0, f, l, n, next)
#endif
#ifndef sll_queue_pop_n
#define sll_queue_pop_n(f, l, next)           sll_queue_pop_nzs      (0, f, l,    next)
#endif
#ifndef sll_queue_push
#define sll_queue_push(f, l, n)               sll_queue_push_nz      (0, f, l, n, next)
#endif
#ifndef sll_queue_push_front
#define sll_queue_push_front(f, l ,n)         sll_queue_push_front_nz(0, f, l, n, next)
#endif
#ifndef sll_queue_pop
#define sll_queue_pop(f, l)                   sll_queue_pop_nz       (0, f, l,    next)
#endif

//- rjf: singly-linked, singly-headed list helpers

#ifndef sll_stack_push
#define sll_stack_push(f, n) sll_stack_push_n(f, n, next)
#endif
#ifndef sll_stack_pop
#define sll_stack_pop(f)     sll_stack_pop_n (f,    next)
#endif

////////////////////////////////
//~ rjf: Address Sanitizer Markup

#ifndef NO_ASAN
#	if COMPILER_MSVC
#		if defined(__SANITIZE_ADDRESS__)
#			define ASAN_ENABLED 1
#	 		define NO_ASAN __declspec(no_sanitize_address)
#		else
#	 		define NO_ASAN
#		endif
#	elif COMPILER_CLANG
#		if defined(__has_feature)
#	 		if __has_feature(address_sanitizer) || defined(__SANITIZE_ADDRESS__)
#	  		define ASAN_ENABLED 1
#	 		endif
#		endif
#		define NO_ASAN __attribute__((no_sanitize("address")))
#	else
#		define NO_ASAN
#	endif
#endif

#ifndef asan_poison_memory_region
#	if MD_ASAN_ENABLED
#		pragma comment(lib, "clang_rt.asan-x86_64.lib")
		MD_C_API void __asan_poison_memory_region(void const volatile *addr, size_t size);
		MD_C_API void __asan_unpoison_memory_region(void const volatile *addr, size_t size);

#		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
#endif

////////////////////////////////
//~ rjf: Misc. Helper Macros

#ifndef array_count
#define array_count(a) (sizeof(a) / sizeof((a)[0]))
#endif

#ifndef ceil_integer_div
#define ceil_integer_div(a,b) (((a) + (b) - 1) / (b))
#endif

#ifndef swap
#define swap(T, a, b) do { T t__ = a; a = b; b = t__; } while(0)
#endif

#ifndef int_from_ptr
#	if ARCH_64BIT
#		define int_from_ptr(ptr) ((U64)(ptr))
#	elif ARCH_32BIT
#		define int_from_ptr(ptr) ((U32)(ptr))
#	else
#		error Missing pointer-to-integer cast for this architecture.
#	endif
#endif

#ifndef ptr_from_int
#define ptr_from_int(i) (void*)((U8*)0 + (i))
#endif

#ifndef compose_64bit
#define compose_64bit(a,b)    ((((U64)a) << 32) | ((U64)b));
#endif
#ifndef align_pow2
#define align_pow2(x,b)      (((x) + (b) - 1) & ( ~((b) - 1)))
#endif
#ifndef align_down_pow2
#define align_down_pow2(x,b) ((x) & (~((b) - 1)))
#endif
#ifndef align_pad_pow2
#define align_pad_pow2(x,b)  ((0-(x)) & ((b) - 1))
#endif
#ifndef is_pow2
#define is_pow2(x)           ((x) != 0 && ((x ) & ((x) - 1)) == 0)
#endif
#ifndef is_pow2_or_zero
#define is_pow2_or_zero(x)   ((((x) - 1) & (x)) == 0)
#endif

#ifndef extract_bit
#define extract_bit(word, idx) (((word) >> (idx)) & 1)
#endif

#ifndef zero_struct
#	if LANG_CPP
#		define zero_struct {}
#	else
#		define zero_struct {0}
#	endif
#endif

#ifndef this_function_name
#	if COMPILER_MSVC && COMPILER_MSVC_YEAR < 2015
#		define this_function_name "unknown"
#	else
#		define this_function_name __func__
#	endif
#endif

#ifndef read_only
#	if COMPILER_MSVC || (COMPILER_CLANG && OS_WINDOWS)
#		pragma section(".rdata$", read)
#		define read_only __declspec(allocate(".rdata$"))
#	elif (COMPILER_CLANG && OS_LINUX)
#		define read_only __attribute__((section(".rodata")))
#	else
		// NOTE(rjf): I don't know of a useful way to do this in GCC land.
		// __attribute__((section(".rodata"))) looked promising, but it introduces a
		// strange warning about malformed section attributes, and it doesn't look
		// like writing to that section reliably produces access violations, strangely
		// enough. (It does on Clang)
#		define read_only
#	endif
#endif

#ifndef local_persist
#define local_persist static
#endif

#if COMPILER_MSVC
#	define thread_static __declspec(thread)
#elif COMPILER_CLANG || COMPILER_GCC
#	define thread_static __thread
#endif

#if COMPILER_CPP
// Already Defined
#elif COMPILER_C && __STDC_VERSION__ >= 201112L
#	define thread_local _Thread_local
#elif COMPILER_MSVC
#	define thread_local __declspec(thread)
#elif COMPILER_CLANG
#	define thread_local __thread
#else
#	error "No thread local support"
#endif


////////////////////////////////
//~ rjf: Safe Casts

inline U16
safe_cast_u16(U32 x) {
	assert_always(x <= MAX_U16);
	U16 result = (U16)x;
	return result;
}

inline U32
safe_cast_u32(U64 x) {
	assert_always(x <= MAX_U32);
	U32 result = (U32)x;
	return result;
}

inline S32
safe_cast_s32(S64 x) {
	assert_always(x <= MAX_S32);
	S32 result = (S32)x;
	return result;
}

////////////////////////////////
//~ rjf: Large Base Type Functions

inline U128 u128_zero (void)           { U128 v = {0};      return v; }
inline U128 u128_make (U64 v0, U64 v1) { U128 v = {v0, v1}; return v; }
inline B32  u128_match(U128 a, U128 b) { return memory_match_struct(&a, &b); }

////////////////////////////////
//~ rjf: Bit Patterns

inline U32 u32_from_u64_saturate(U64 x) { U32 x32 = (x > MAX_U32) ? MAX_U32 : (U32)x; return(x32); }

inline U64
u64_up_to_pow2(U64 x) {
	if (x == 0) {
		x = 1;
	}
	else {
		x -= 1;
		x |= (x >> 1);
		x |= (x >> 2);
		x |= (x >> 4);
		x |= (x >> 8);
		x |= (x >> 16);
		x |= (x >> 32);
		x += 1;
	}
	return(x);
}

inline S32
extend_sign32(U32 x, U32 size) {
  U32 high_bit = size * 8;
  U32 shift    = 32 - high_bit;
  S32 result   = ((S32)x << shift) >> shift;
  return result;
}

inline S64
extend_sign64(U64 x, U64 size) {
  U64 high_bit = size * 8;
  U64 shift    = 64 - high_bit;
  S64 result   = ((S64)x << shift) >> shift;
  return result;
}

inline F32 inf32    (void) { union { U32 u; F32 f; } x; x.u = EXPONENT32;          return(x.f); }
inline F32 neg_inf32(void) { union { U32 u; F32 f; } x; x.u = SIGN32 | EXPONENT32; return(x.f); }

inline U16
bswap_u16(U16 x)
{
  U16 result = (((x & 0xFF00) >> 8) |
                ((x & 0x00FF) << 8));
  return result;
}

inline U32
bswap_u32(U32 x)
{
  U32 result = (((x & 0xFF000000) >> 24) |
                ((x & 0x00FF0000) >> 8)  |
                ((x & 0x0000FF00) << 8)  |
                ((x & 0x000000FF) << 24));
  return result;
}

inline U64
bswap_u64(U64 x)
{
  // TODO(nick): naive bswap, replace with something that is faster like an intrinsic
  U64 result = (((x & 0xFF00000000000000ULL) >> 56) |
                ((x & 0x00FF000000000000ULL) >> 40) |
                ((x & 0x0000FF0000000000ULL) >> 24) |
                ((x & 0x000000FF00000000ULL) >> 8)  |
                ((x & 0x00000000FF000000ULL) << 8)  |
                ((x & 0x0000000000FF0000ULL) << 24) |
                ((x & 0x000000000000FF00ULL) << 40) |
                ((x & 0x00000000000000FFULL) << 56));
  return result;
}

#if ARCH_LITTLE_ENDIAN
# define from_be_u16(x) bswap_u16(x)
# define from_be_u32(x) bswap_u32(x)
# define from_be_u64(x) bswap_u64(x)
#else
# define from_be_u16(x) (x)
# define from_be_u32(x) (x)
# define from_be_u64(x) (x)
#endif

#if COMPILER_MSVC || (COMPILER_CLANG && OS_WINDOWS)
	inline U64 count_bits_set16(U16 val) { return __popcnt16(val); }
	inline U64 count_bits_set32(U32 val) { return __popcnt  (val); }
	inline U64 count_bits_set64(U64 val) { return __popcnt64(val); }

	inline U64 ctz32(U32 mask) { unsigned long idx; _BitScanForward  (&idx, mask); return idx; }
	inline U64 ctz64(U64 mask) { unsigned long idx; _BitScanForward64(&idx, mask); return idx; }
	inline U64 clz32(U32 mask) { unsigned long idx; _BitScanReverse  (&idx, mask); return 31 - idx; }
	inline U64 clz64(U64 mask) { unsigned long idx; _BitScanReverse64(&idx, mask); return 63 - idx; }
#elif COMPILER_CLANG || COMPILER_GCC
	inline U64 count_bits_set16(U16 val) { NotImplemented; return 0; }
	inline U64 count_bits_set32(U32 val) { NotImplemented; return 0; }
	inline U64 count_bits_set64(U64 val) { NotImplemented; return 0; }

	inline U64 ctz32(U32 val) { NotImplemented; return 0; }
	inline U64 ctz64(U32 val) { NotImplemented; return 0; }
	inline U64 clz32(U32 val) { NotImplemented; return 0; }
	inline U64 clz64(U64 val) { NotImplemented; return 0; }
#else
#	error "Bit intrinsic functions not defined for this compiler."
#endif
