/*
**
** TODO: instructions will go here
**
*/

#if !defined(MR4TH_BASE_H)
#define MR4TH_BASE_H 1

////////////////////////////////////////////////
////////////////////////////////////////////////
//////////////////   MACROS   //////////////////
////////////////////////////////////////////////
////////////////////////////////////////////////

////////////////////////////////
// Build Mode Defaults & Checks

#if !defined(MR4TH_DISABLE_UNITY_BUILD)
# define MR4TH_DISABLE_UNITY_BUILD 0
#endif
#if !defined(MR4TH_CORE_MODE)
# define MR4TH_CORE_MODE 0
#endif
#if !defined(MR4TH_PLUGIN_MODE)
# define MR4TH_PLUGIN_MODE 0
#endif
#if !defined(MR4TH_STANDALONE_MODE)
# define MR4TH_STANDALONE_MODE 0
#endif
#if !defined(MR4TH_PROFILING_PROVIDER)
# define MR4TH_PROFILING_PROVIDER 0
#endif

#if !MR4TH_CORE_MODE && !MR4TH_PLUGIN_MODE && !MR4TH_STANDALONE_MODE
# undef MR4TH_STANDALONE_MODE
# define MR4TH_STANDALONE_MODE 1
#endif

#if (MR4TH_CORE_MODE && MR4TH_PLUGIN_MODE)
# error conflicing mr4th build mode
#endif
#if (MR4TH_CORE_MODE && MR4TH_STANDALONE_MODE)
# error conflicing mr4th build mode
#endif
#if (MR4TH_PLUGIN_MODE && MR4TH_STANDALONE_MODE)
# error conflicing mr4th build mode
#endif

////////////////////////////////
// Context Cracking

// development settings
#if !defined(MR4TH_ASSERTS)
# define MR4TH_ASSERTS 0
#endif
#if !defined(MR4TH_SANITIZER)
# define MR4TH_SANITIZER 0
#endif
#if !defined(MR4TH_PROFILING_MANUAL)
# define MR4TH_PROFILING_MANUAL 0
#endif
#if !defined(MR4TH_PROFILING_AUTO)
# define MR4TH_PROFILING_AUTO 0
#endif

#if defined(MR4TH_PROFILING_USER)
# error the user should not set MR4TH_PROFILING_USER, instead set MR4TH_PROFILING_MANUAL or MR4TH_PROFILING_AUTO
#endif

#if MR4TH_PROFILING_MANUAL || MR4TH_PROFILING_AUTO
# define MR4TH_PROFILING_USER 1
#else
# define MR4TH_PROFILING_USER 0
#endif

// untangle compiler, os, & architecture
#if defined(__clang__)
# define COMPILER_CLANG 1

# if defined(_WIN32)
#  define OS_WINDOWS 1
# elif defined(__gnu_linux__)
#  define OS_LINUX 1
# elif defined(__APPLE__) && defined(__MACH__)
#  define OS_MAC 1
# else
#  error missing OS detection
# endif

# if defined(__amd64__)
#  define ARCH_X64 1
// TODO(allen): verify this works on clang
# elif defined(__i386__)
#  define ARCH_X86 1
// TODO(allen): verify this works on clang
# elif defined(__arm__)
#  define ARCH_ARM 1
// TODO(allen): verify this works on clang
# elif defined(__aarch64__)
#  define ARCH_ARM64 1
# else
#  error missing ARCH detection
# endif

#elif defined(_MSC_VER)
# define COMPILER_CL 1

# if defined(_WIN32)
#  define OS_WINDOWS 1
# else
#  error missing OS detection
# endif

# if defined(_M_AMD64)
#  define ARCH_X64 1
# elif defined(_M_I86)
#  define ARCH_X86 1
# elif defined(_M_ARM)
#  define ARCH_ARM 1
// TODO(allen): ARM64?
# else
#  error missing ARCH detection
# endif

#elif defined(__GNUC__)
# define COMPILER_GCC 1

# if defined(_WIN32)
#  define OS_WINDOWS 1
# elif defined(__gnu_linux__)
#  define OS_LINUX 1
# elif defined(__APPLE__) && defined(__MACH__)
#  define OS_MAC 1
# else
#  error missing OS detection
# endif

# if defined(__amd64__)
#  define ARCH_X64 1
# elif defined(__i386__)
#  define ARCH_X86 1
# elif defined(__arm__)
#  define ARCH_ARM 1
# elif defined(__aarch64__)
#  define ARCH_ARM64 1
# else
#  error missing ARCH detection
# endif

#else
# error no context cracking for this compiler
#endif

#if !defined(COMPILER_CL)
# define COMPILER_CL 0
#endif
#if !defined(COMPILER_CLANG)
# define COMPILER_CLANG 0
#endif
#if !defined(COMPILER_GCC)
# define COMPILER_GCC 0
#endif
#if !defined(OS_WINDOWS)
# define OS_WINDOWS 0
#endif
#if !defined(OS_LINUX)
# define OS_LINUX 0
#endif
#if !defined(OS_MAC)
# define OS_MAC 0
#endif
#if !defined(ARCH_X64)
# define ARCH_X64 0
#endif
#if !defined(ARCH_X86)
# define ARCH_X86 0
#endif
#if !defined(ARCH_ARM)
# define ARCH_ARM 0
#endif
#if !defined(ARCH_ARM64)
# define ARCH_ARM64 0
#endif


// language
#if defined(__cplusplus)
# define LANG_CXX 1
#else
# define LANG_C 1
#endif

#if !defined(LANG_CXX)
# define LANG_CXX 0
#endif
#if !defined(LANG_C)
# define LANG_C 0
#endif


// language version: C
#if LANG_C
# if !defined(__STDC_VERSION__)
#  define LANGVER_C90 1
# elif (__STDC_VERSION__ == 199901)
#  define LANGVER_C99 1
# elif (__STDC_VERSION__ == 201112)
#  define LANGVER_C11 1
# elif (__STDC_VERSION__ == 201710)
#  define LANGVER_C17 1
# elif (__STDC_VERSION__ == 202000)
#  define LANGVER_C23 1
# else
#  error unrecorgnized C version
# endif
#endif

#if !defined(LANGVER_C90)
# define LANGVER_C90 0
#endif
#if !defined(LANGVER_C99)
# define LANGVER_C99 0
#endif
#if !defined(LANGVER_C11)
# define LANGVER_C11 0
#endif
#if !defined(LANGVER_C17)
# define LANGVER_C17 0
#endif
#if !defined(LANGVER_C23)
# define LANGVER_C23 0
#endif


// language version: C++
#if LANG_CXX
# if (__cplusplus == 199711)
#  define LANGVER_CXX98 1
# elif (__cplusplus == 201103)
#  define LANGVER_CXX11 1
# elif (__cplusplus == 201402)
#  define LANGVER_CXX14 1
# elif (__cplusplus == 201703)
#  define LANGVER_CXX17 1
# elif (__cplusplus == 202002)
#  define LANGVER_CXX20 1
# elif (__cplusplus == 202302)
#  define LANGVER_CXX23 1
# else
#  error unrecorgnized C++ version
# endif
#endif

#if !defined(LANGVER_CXX98)
# define LANGVER_CXX98 0
#endif
#if !defined(LANGVER_CXX11)
# define LANGVER_CXX11 0
#endif
#if !defined(LANGVER_CXX14)
# define LANGVER_CXX14 0
#endif
#if !defined(LANGVER_CXX17)
# define LANGVER_CXX17 0
#endif
#if !defined(LANGVER_CXX20)
# define LANGVER_CXX20 0
#endif
#if !defined(LANGVER_CXX23)
# define LANGVER_CXX23 0
#endif

// determine intrinsics mode
#if OS_WINDOWS
# if COMPILER_CL || COMPILER_CLANG
#  define INTRINSICS_MICROSOFT 1
# endif
#endif
#if OS_LINUX
# if COMPILER_CLANG || COMPILER_GCC
#  define INTRINSICS_BUILT_IN 1
# endif
#endif

#if !defined(INTRINSICS_MICROSOFT)
# define INTRINSICS_MICROSOFT 0
#endif
#if !defined(INTRINSICS_BUILT_IN)
# define INTRINSICS_BUILT_IN 0
#endif

// setup pointer size macro
#if ARCH_X64 || ARCH_ARM64
# define ARCH_ADDRSIZE 64
#else
# define ARCH_ADDRSIZE 32
#endif


#define ARCH_LITTLE_ENDIAN 1
#define ARCH_BIG_ENDIAN    0

// TODO(allen): target image format

////////////////////////////////
// Macros: Symbol Markers

// linkage, storage, & other "attribute" like things

#define MR4TH_SYMBOL_STATIC static

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

#if OS_WINDOWS
# define MR4TH_SYMBOL_EXPORT __declspec(dllexport)
#elif OS_LINUX || OS_MAC
# define MR4TH_SYMBOL_EXPORT __attribute__((visibility("default")))
#else
# error MR4TH_SYMBOL_EXPORT not defined for this OS
#endif

#if OS_WINDOWS
# define MR4TH_SYM_IMPORT __declspec(dllimport)
#elif OS_LINUX || OS_MAC
# define MR4TH_SYM_IMPORT
#else
# error MR4TH_SYMBOL_IMPORT not defined for this OS
#endif

#if COMPILER_CL
# define MR4TH_THREADVAR __declspec(thread)
#elif COMPILER_CLANG || COMPILER_GCC
# define MR4TH_THREADVAR __thread
#else
# error MR4TH_THREADVAR not defined for this compiler
#endif

#if COMPILER_CLANG || COMPILER_GCC
# if OS_MAC
#  define MR4TH_SEC_R(N) __attribute__((__section__("__TEXT,"N)))
#  define MR4TH_SEC_RW(N) __attribute__((__section__("__READ,"N)))
# else
#  define MR4TH_SEC_R(N) __attribute__((__section__(N)))
#  define MR4TH_SEC_RW(N) __attribute__((__section__(N)))
# endif
#elif COMPILER_CL
# define MR4TH_SEC_R(N) __declspec(allocate(N))
# define MR4TH_SEC_RW(N) __declspec(allocate(N))
#else
# error MR4TH_SEC_R/MR4TH_SEC_RW not defined for this compiler/OS
#endif

#if COMPILER_CLANG || COMPILER_GCC
#if OS_WINDOWS
# define MR4TH_READ_ONLY MR4TH_SEC_R(".rdata") const
#else
# define MR4TH_READ_ONLY MR4TH_SEC_R(".rodata") const
#endif
#elif COMPILER_CL
# pragma section(".m4rdata",read)
# define MR4TH_READ_ONLY MR4TH_SEC_R(".m4rdata")
#else
# define MR4TH_READ_ONLY
# error MR4TH_READ_ONLY not defined for this compiler
#endif

// symbol kinds

#if MR4TH_DISABLE_UNITY_BUILD
# define MR4TH_SYMBOL MR4TH_SYMBOL_LINK
#else
# define MR4TH_SYMBOL MR4TH_SYMBOL_STATIC
#endif

#if MR4TH_CORE_MODE
# define MR4TH_SYMBOL_MUST_SHARE MR4TH_SYMBOL_EXPORT
#elif MR4TH_PLUGIN_MODE
# define MR4TH_SYMBOL_MUST_SHARE MR4TH_SYMBOL_IMPORT
#else
# define MR4TH_SYMBOL_MUST_SHARE MR4TH_SYMBOL
#endif

// align-as

#if COMPILER_CL
# define MR4TH_ALIGN_AS_LIT(n) __declspec(align(n))
#elif COMPILER_CLANG || COMPILER_GCC
# define MR4TH_ALIGN_AS_LIT(n) __attribute__(( __aligned__(n) ))
#else
# error MR4TH_ALIGN_AS_LIT not defined for this compiler
#endif

// do-not-eliminate

#if OS_WINDOWS

# define MR4TH_DO_NOT_ELIMINATE__S0(N) __pragma(comment(linker,"/include:" #N))
# define MR4TH_DO_NOT_ELIMINATE__S1(N) MR4TH_DO_NOT_ELIMINATE__S0(N)
# define MR4TH_DO_NOT_ELIMINATE(N)     MR4TH_DO_NOT_ELIMINATE__S1(N)

#else

# define MR4TH_DO_NOT_ELIMINATE(N)

#endif

// before-main abstraction

#if OS_WINDOWS

# pragma section(".CRT$XCU", read)

# if LANG_CXX

#  define MR4TH_BEFORE_MAIN_(n) \
static void n(void);                          \
__declspec(allocate(".CRT$XCU"))              \
__pragma(comment(linker,"/include:" #n "__")) \
extern "C" void (*n##__)(void);               \
void (*n##__)(void) = n;                      \
static void n(void)

# else

#  define MR4TH_BEFORE_MAIN_(n) \
static void n(void);                          \
__declspec(allocate(".CRT$XCU"))              \
__pragma(comment(linker,"/include:" #n "__")) \
void (*n##__)(void) = n;                      \
static void n(void)

# endif

# define MR4TH_BEFORE_MAIN(n) MR4TH_BEFORE_MAIN_(n)

#elif OS_LINUX

# define MR4TH_BEFORE_MAIN(n) \
__attribute__((constructor)) static void n(void)

#else
# error MR4TH_BEFORE_MAIN missing for this OS
#endif

// sanitizer

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

#if MR4TH_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


////////////////////////////////
// Macros: Common Expressions

// macro writing utilities

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

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

// assert

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

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

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

// memory operations

#define MemoryZero(p,z)      memory_zero((p), (z))
#define MemoryZeroStruct(p)  MemoryZero((p), sizeof(*(p)))
#define MemoryZeroArray(p)   MemoryZero((p), sizeof(p))
#define MemoryZeroRange(f,o) MemoryZero((f), (U8*)(o) - (U8*)(f))

#define MemoryMatch(a,b,z) (memory_match((a),(b),(z)))

#define MemoryCopy(d,s,z)     memory_move((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)))

// misc expressions

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

#define IntFromPtr(p) (UAddr)(p)
#define PtrFromInt(n) (void*)((UAddr)(n))
#define PtrDif(a,b) ((U8*)(a) - (U8*)(b))

#define PtrInArray(p,a) ((a) <= (p) && (p) < ArrayOpl(a))

#define Member(T,m) (((T*)0)->m)
#define AddrOfMember(T,m)   (void*)(&Member(T,m))
#define OffsetOfMember(T,m) IntFromPtr(&Member(T,m))
#define OffsetOfMemberV(s,m) PtrDif(&s->m, s)
#define MemberAddr(T,m) AddrOfMember(T,m)
#define MemberOff(T,m)  OffsetOfMember(T,m)

#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 Swap(T,a,b) Stmnt( T t_ = (a); (a) = (b); (b) = t_; )

#define SignedIntFromCompare(a,b) (S32)( ((b)<(a)) - ((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 CeilIntDiv(n,d) (((n) + (d) - 1)/(d))

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

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

#define AsciiID4(a,b,c,d) (((d) << 24) | ((c) << 16) | ((b) << 8) | (a))

#if LANG_CXX
# define CLiteral(T) T
#else
# define CLiteral(T) (T)
#endif

////////////////////////////////
// Macros: Linked Lists

#define DLLPushBack_NPZ(f,l,n,next,prev,nil)\
(((f) == (nil))?\
((f)=(l)=(n),(n)->next=(n)->prev=(nil)):\
((n)->prev=(l),(l)->next=(n),(l)=(n),(n)->next=(nil)))

#define DLLPushBack(f,l,n) DLLPushBack_NPZ(f,l,n,next,prev,0)
#define DLLPushFront(f,l,n) DLLPushBack_NPZ(l,f,n,prev,next,0)

#define DLLInsert_NPZ(f,l,p,n,next,prev,nil) \
(((p) != (l))?\
((n)->next = (p)->next,\
(n)->prev = (p),\
(p)->next->prev = (n),\
(p)->next = (n))\
:((n)->next = (nil),\
(n)->prev = (l),\
(l)->next = (n),\
(l) = (n)))

#define DLLInsert(f,l,p,n) DLLInsert_NPZ(f,l,p,n,next,prev,0)

#define DLLRemove_NPZ(f,l,n,next,prev,nil)\
((f)==(n)?\
((f)==(l)?\
((f)=(l)=(nil)):\
((f)=(f)->next,(f)->prev=(nil))):\
(l)==(n)?\
((l)=(l)->prev,(l)->next=(nil)):\
((n)->next->prev=(n)->prev,\
(n)->prev->next=(n)->next))

#define DLLRemove(f,l,n) DLLRemove_NPZ(f,l,n,next,prev,0)

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

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

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

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

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

////////////////////////////////
// Macros: Static Configurations

#if !defined(MR4TH_MEM_COMMIT_BLOCK_SIZE)
# define MR4TH_MEM_COMMIT_BLOCK_SIZE MB(64)
#endif

#if !defined(MR4TH_MEM_INITIAL_COMMIT)
# define MR4TH_MEM_INITIAL_COMMIT KB(4)
#endif

#if !defined(MR4TH_MEM_MAX_ALIGN)
# define MR4TH_MEM_MAX_ALIGN 64
#endif

#if !defined(MR4TH_MEM_SCRATCH_POOL_COUNT)
# define MR4TH_MEM_SCRATCH_POOL_COUNT 2
#endif

#if !defined(MR4TH_STREAM_ALLOC_SIZE)
# define MR4TH_STREAM_ALLOC_SIZE MB(16)
#endif


////////////////////////////////////////////////
////////////////////////////////////////////////
//////////////////   TYPES   ///////////////////
////////////////////////////////////////////////
////////////////////////////////////////////////

////////////////////////////////
// Types: Basic

#include <stddef.h>
#include <stdint.h>
#include <stdarg.h>

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 S8  B8;
typedef S16 B16;
typedef S32 B32;
typedef S64 B64;
typedef float  F32;
typedef double F64;

#if ARCH_ADDRSIZE == 32
typedef U32 UAddr;
typedef S32 SAddr;
#elif ARCH_ADDRSIZE == 64
typedef U64 UAddr;
typedef S64 SAddr;
#else
# error UAddr and SAddr not defined for this architecture
#endif

typedef void VoidFunc(void);

////////////////////////////////
// Types: Symbolic Constants

typedef enum Axis{
  Axis_X = 0,
  Axis_Y = 1,
  Axis_Z = 2,
  Axis_W = 3
} Axis;

typedef enum Side{
  Side_Min = 0,
  Side_Max = 1
} Side;

typedef enum OperatingSystem{
  OperatingSystem_Null    = 0,
  OperatingSystem_Windows = 1,
  OperatingSystem_Linux   = 2,
  OperatingSystem_Mac     = 3,
  OperatingSystem_COUNT
} OperatingSystem;

typedef enum Architecture{
  Architecture_Null  = 0,
  Architecture_X64   = 1,
  Architecture_X86   = 2,
  Architecture_Arm   = 3,
  Architecture_Arm64 = 4,
  Architecture_COUNT
} Architecture;

typedef enum Month{
  Month_Jan =  1,
  Month_Feb =  2,
  Month_Mar =  3,
  Month_Apr =  4,
  Month_May =  5,
  Month_Jun =  6,
  Month_Jul =  7,
  Month_Aug =  8,
  Month_Sep =  9,
  Month_Oct = 10,
  Month_Nov = 11,
  Month_Dec = 12,
} Month;

typedef enum DayOfWeek{
  DayOfWeek_Sunday    = 0,
  DayOfWeek_Monday    = 1,
  DayOfWeek_Tuesday   = 2,
  DayOfWeek_Wednesday = 3,
  DayOfWeek_Thursday  = 4,
  DayOfWeek_Friday    = 5,
  DayOfWeek_Saturday  = 6
} DayOfWeek;

typedef U32 DataAccessFlags;
enum{
  DataAccessFlag_Read    = (1 << 0),
  DataAccessFlag_Write   = (1 << 1),
  DataAccessFlag_Execute = (1 << 2),
};

////////////////////////////////
// Types: Compound Types

typedef struct V2F32{
  F32 x;
  F32 y;
} V2F32;

typedef struct V3F32{
  F32 x;
  F32 y;
  F32 z;
} V3F32;

typedef struct V4F32{
  F32 x;
  F32 y;
  F32 z;
  F32 w;
} V4F32;

typedef struct RangeF32{
  F32 min;
  F32 max;
} RangeF32;

typedef struct RectF32{
  union{
    struct{
      F32 x0;
      F32 y0;
      F32 x1;
      F32 y1;
    };
    struct{
      V2F32 p0;
      V2F32 p1;
    };
    V2F32 p[2];
  };
} RectF32;

typedef struct V2S32{
  union{
    struct{
      S32 x;
      S32 y;
    };
    S32 v[2];
  };
} V2S32;

typedef struct V3S32{
  S32 x;
  S32 y;
  S32 z;
} V3S32;

typedef struct V4S32{
  S32 x;
  S32 y;
  S32 z;
  S32 w;
} V4S32;

typedef struct RangeS32{
  S32 min;
  S32 max;
} RangeS32;

typedef struct RectS32{
  union{
    struct{
      S32 x0;
      S32 y0;
      S32 x1;
      S32 y1;
    };
    struct{
      V2S32 p0;
      V2S32 p1;
    };
    V2S32 p[2];
  };
} RectS32;

////////////////////////////////
// Types: Time

typedef U64 DenseTime;

typedef struct DateTime{
  U16 msec; // [0,999]
  U8 sec;   // [0,60]
  U8 min;   // [0,59]
  U8 hour;  // [0,23]
  U8 day;   // [1,31]
  U8 mon;   // [1,12]
  S16 year; // 1 = 1 CE; 2020 = 2020 CE; 0 = 1 BCE; -100 = 101 BCE; etc.
} DateTime;

////////////////////////////////
// Types: File Properties

typedef U32 FilePropertyFlags;
enum{
  FilePropertyFlag_IsFolder = (1 << 0),
};

typedef struct FileProperties{
  U64 size;
  FilePropertyFlags flags;
  DenseTime create_time;
  DenseTime modify_time;
  DataAccessFlags access;
} FileProperties;


////////////////////////////////
// Types: Arena

typedef struct Arena{
  struct Arena *current;
  struct Arena *prev;
  U64 default_reserve_size;
  U16 alignment;
  B8  growing;
  U8  filler[5];
  U64 base_pos;
  U64 chunk_cap;
  U64 chunk_pos;
  U64 chunk_commit_pos;
} Arena;

typedef struct ArenaTemp{
  Arena *arena;
  U64 pos;
} ArenaTemp;


////////////////////////////////
// Types: String

typedef struct String8{
  U8 *str;
  U64 size;
} String8;

typedef struct String8Node{
  struct String8Node *next;
  String8 string;
} String8Node;

typedef struct String8List{
  String8Node *first;
  String8Node *last;
  U64 node_count;
  U64 total_size;
} String8List;

typedef struct StringJoin{
  String8 pre;
  String8 mid;
  String8 post;
} StringJoin;

typedef U32 StringMatchFlags;
enum{
  StringMatchFlag_NoCase      = (1 << 0),
  StringMatchFlag_PrefixMatch = (1 << 1),
};

typedef struct String16{
  U16 *str;
  U64 size;
} String16;

typedef struct String32{
  U32 *str;
  U64 size;
} String32;

#define str8_expand(s) (int)((s).size), ((s).str)

typedef struct StringDecode{
  U32 codepoint;
  U32 size;
} StringDecode;


////////////////////////////////
// Types: Stream

typedef struct STREAM_Node{
  struct STREAM_Node *next;
  U64 size;
  U8 data[0];
} STREAM_Node;

typedef struct STREAM{
  Arena *arena;
  U64 clear_pos;
  STREAM_Node *first_node;
  STREAM_Node *last_node;
  U64 buffer_cap;
  U64 total_size;
  STREAM_Node *unused_node;
} STREAM;

typedef void STREAM_Handle;


////////////////////////////////
// Types: Buffer

typedef struct BUFFER{
  void *base;
  U64 size;
  U64 commit_pos;
} BUFFER;

#define BUFFER_TYPED(T, name) union{ BUFFER buffer; T *ptr; } name


////////////////////////////////
// Types: Hash Table

typedef struct HASH_Node{
  struct HASH_Node *next;
  U64 hash;
  U64 val;
} HASH_Node;


////////////////////////////////
// Types: Log

typedef void LOG_LogToProc(void *uptr, String8 str);

typedef struct LOG_Node{
  struct LOG_Node *next;
  U64 pos;
  String8List log;
  LOG_LogToProc *logto;
  void *uptr;
} LOG_Node;

typedef struct LOG_ThreadVars{
  Arena    *arena;
  LOG_Node *stack;
} LOG_ThreadVars;


////////////////////////////////
// Types: Error

typedef struct ER_Node{
  struct ER_Node *next;
  U64 pos;
  String8 error;
} ER_Node;

typedef struct ER_ThreadVars{
  Arena   *arena;
  ER_Node *stack;
  U64      over_stack;
} ER_ThreadVars;


////////////////////////////////////////////////
////////////////////////////////////////////////
////////////////   CONSTANTS   /////////////////
////////////////////////////////////////////////
////////////////////////////////////////////////

#define min_S8  ((S8) 0x80)
#define min_S16 ((S16)0x8000)
#define min_S32 ((S32)0x80000000)
#define min_S64 ((S64)0x8000000000000000llu)

#define max_S8  ((S8) 0x7f)
#define max_S16 ((S16)0x7fff)
#define max_S32 ((S32)0x7fffffff)
#define max_S64 ((S64)0x7fffffffffffffffllu)

#define max_U8  0xff
#define max_U16 0xffff
#define max_U32 0xffffffff
#define max_U64 0xffffffffffffffffllu

#define machine_epsilon_F32 1.1920929e-7f
#define pi_F32              3.14159265359f
#define tau_F32             6.28318530718f
#define e_F32               2.71828182846f
#define gold_big_F32        1.61803398875f
#define gold_small_F32      0.61803398875f

#define machine_epsilon_F64 2.220446e-16
#define pi_F64              3.14159265359
#define tau_F64             6.28318530718
#define e_F64               2.71828182846
#define gold_big_F64        1.61803398875
#define gold_small_F64      0.61803398875

#define     inf_F32_as_U32 0x7f800000
#define neg_inf_F32_as_U32 0xff800000
#define     inf_F64_as_U64 0x7ff0000000000000
#define neg_inf_F64_as_U64 0xfff0000000000000

#define set_inf_F32(x)     (*(U32*)(&(x)) = inf_F32_as_U32)
#define set_neg_inf_F32(x) (*(U32*)(&(x)) = neg_inf_F32_as_U32)
#define set_inf_F64(x)     (*(U64*)(&(x)) = inf_F64_as_U64)
#define set_neg_inf_F64(x) (*(U64*)(&(x)) = neg_inf_F64_as_U64)


////////////////////////////////////////////////
////////////////////////////////////////////////
////////////////   INTRINSICS   ////////////////
////////////////////////////////////////////////
////////////////////////////////////////////////

#if INTRINSICS_MICROSOFT

# include <intrin.h>
# define intrinsic_rdtsc() __rdtsc()
# define intrinsic_maxbit(o,x) _BitScanReverse64((o),(x))

#elif INTRINSICS_BUILT_IN

# define intrinsic_rdtsc() __rdtsc()
# define intrinsic_maxbit(o,x) (*(o) = __builtin_clzll(x),(x)!=0)

#endif


////////////////////////////////////////////////
////////////////////////////////////////////////
///////////////   DECLARATIONS   ///////////////
////////////////////////////////////////////////
////////////////////////////////////////////////

////////////////////////////////
// Functions: Numerical/Math

// float infinity/NaN

MR4TH_SYMBOL B32 is_inf_or_nan_F32(F32 x);
MR4TH_SYMBOL B32 is_inf_or_nan_F64(F64 x);

// float signs, rounding, and modulus

MR4TH_SYMBOL F32 abs_F32(F32 x);
MR4TH_SYMBOL F32 sign_F32(F32 x);

MR4TH_SYMBOL F32 trunc_F32(F32 x);
MR4TH_SYMBOL F32 floor_F32(F32 x);
MR4TH_SYMBOL F32 ceil_F32(F32 x);
MR4TH_SYMBOL F32 nearest_F32(F32 x);
MR4TH_SYMBOL F32 mod_F32(F32 x, F32 m);
MR4TH_SYMBOL F32 frac_F32(F32 x);

MR4TH_SYMBOL F64 abs_F64(F64 x);
MR4TH_SYMBOL F64 sign_F64(F64 x);

MR4TH_SYMBOL F64 trunc_F64(F64 x);
MR4TH_SYMBOL F64 floor_F64(F64 x);
MR4TH_SYMBOL F64 ceil_F64(F64 x);
MR4TH_SYMBOL F64 nearest_F64(F64 x);
MR4TH_SYMBOL F64 mod_F64(F64 x, F64 m);
MR4TH_SYMBOL F64 frac_F64(F64 x);

// transcendental functions

MR4TH_SYMBOL F32 sin_F32(F32 x);
MR4TH_SYMBOL F32 cos_F32(F32 x);
MR4TH_SYMBOL F32 tan_F32(F32 x);
MR4TH_SYMBOL F32 atan_F32(F32 x);
MR4TH_SYMBOL F32 atan2_F32(F32 x, F32 y);

MR4TH_SYMBOL F32 sqrt_F32(F32 x);
MR4TH_SYMBOL F32 inv_sqrt_F32(F32 x);
MR4TH_SYMBOL F32 ln_F32(F32 x);
MR4TH_SYMBOL F32 pow_F32(F32 base, F32 x);

MR4TH_SYMBOL F64 sin_F64(F64 x);
MR4TH_SYMBOL F64 cos_F64(F64 x);
MR4TH_SYMBOL F64 tan_F64(F64 x);
MR4TH_SYMBOL F64 atan_F64(F64 x);
MR4TH_SYMBOL F64 atan2_F64(F64 x, F64 y);

MR4TH_SYMBOL F64 sqrt_F64(F64 x);
MR4TH_SYMBOL F64 inv_sqrt_F64(F64 x);
MR4TH_SYMBOL F64 ln_F64(F64 x);
MR4TH_SYMBOL F64 pow_F64(F64 base, F64 x);

// linear interpolation

MR4TH_SYMBOL F32 lerp(F32 a, F32 t, F32 b);
MR4TH_SYMBOL F32 unlerp(F32 a, F32 x, F32 b);

// integer rounding & truncating

MR4TH_SYMBOL U32 next_pow2_U32(U32 x);
MR4TH_SYMBOL S32 sign_extend_S32(S32 x, U32 bitidx);

MR4TH_SYMBOL U64 next_pow2_U64(U32 x);
MR4TH_SYMBOL S64 sign_extend_S64(S64 x, U32 bitidx);

////////////////////////////////
// Functions: Compound Types

// vector macros

#define v2(x,y)     CLiteral(V2F32){(x),(y)}
#define v3(x,y,z)   CLiteral(V3F32){(x),(y),(z)}
#define v4(x,y,z,w) CLiteral(V4F32){(x),(y),(z),(w)}

#define v2s32(x,y)     CLiteral(V2S32){(x),(y)}
#define v3s32(x,y,z)   CLiteral(V2S32){(x),(y),(z)}
#define v4s32(x,y,z,w) CLiteral(V2S32){(x),(y),(z),(w)}

#define v2_expanded(v) ((v).x), ((v).y)
#define v3_expanded(v) ((v).x), ((v).y), ((v).z)
#define v4_expanded(v) ((v).x), ((v).y), ((v).z), ((v).w)
#define rect_expanded(r) ((r).x0), ((r).y0), ((r).x1), ((r).y1)

#define v2_exop_scalar(sc, op, v) ((sc) op ((v).x)), ((sc) op ((v).y))
#define v3_exop_scalar(sc, op, v) ((sc) op ((v).x)), ((sc) op ((v).y)), ((sc) op ((v).z))
#define v4_exop_scalar(sc, op, v) ((sc) op ((v).x)), ((sc) op ((v).y)), ((sc) op ((v).z)), ((sc) op ((v).w))

#define v2_exop(v1, op, v2) ((v1).x op ((v2).x)), ((v1).y op ((v2).y))
#define v3_exop(v1, op, v2) ((v1).x op ((v2).x)), ((v1).y op ((v2).y)), ((v1).z op ((v2).z))
#define v4_exop(v1, op, v2) ((v1).x op ((v2).x)), ((v1).y op ((v2).y)), ((v1).z op ((v2).z)), ((v1).w op ((v2).w))

#define v2_op_scalar(sc, op, v) CLiteral(V2F32){ v2_exop_scalar(sc,op,v) }
#define v3_op_scalar(sc, op, v) CLiteral(V3F32){ v3_exop_scalar(sc,op,v) }
#define v4_op_scalar(sc, op, v) CLiteral(V4F32){ v4_exop_scalar(sc,op,v) }

#define v2_op(v1, op, v2) CLiteral(V2F32){ v2_exop(v1,op,v2) }
#define v3_op(v1, op, v2) CLiteral(V3F32){ v3_exop(v1,op,v2) }
#define v4_op(v1, op, v2) CLiteral(V4F32){ v4_exop(v1,op,v2) }

#define sc_lerp(s1, l, s2) ((s1) + (l)*((s2) - (s1)))
#define v2_lerp(v1, l, v2) v2(sc_lerp((v1).x, l, (v2).x),\
sc_lerp((v1).y, l, (v2).y) )
#define v3_lerp(v1, l, v2) v3(sc_lerp((v1).x, l, (v2).x),\
sc_lerp((v1).y, l, (v2).y),\
sc_lerp((v1).z, l, (v2).z) )
#define v4_lerp(v1, l, v2) v4(sc_lerp((v1).x, l, (v2).x),\
sc_lerp((v1).y, l, (v2).y),\
sc_lerp((v1).z, l, (v2).z),\
sc_lerp((v1).w, l, (v2).w) )

#define v2_dim_from_rect(r) CLiteral(V2F32){ (r).x1 - (r).x0, (r).y1 - (r).y0 }

// range macros

#define rangef32(min,max) CLiteral(RangeF32){(min),(max)}

// 2d vectors

MR4TH_SYMBOL V2F32 v2_polar(F32 theta, F32 radius);
MR4TH_SYMBOL F32   v2_angle_from_v(V2F32 v);

MR4TH_SYMBOL V2F32 v2_unit(V2F32 v);

MR4TH_SYMBOL V2F32 v2_from_v2s32(V2S32 v);

// 3d vectors

MR4TH_SYMBOL V3F32 v3_spherical(F32 theta_xz, F32 theta_yz, F32 radius);
MR4TH_SYMBOL V3F32 v3_cross(V3F32 a, V3F32 b);

MR4TH_SYMBOL V3F32 v3_unit(V3F32 v);

// 4x4 matrix

MR4TH_SYMBOL B32  mat4x4_inv(F32 *in, F32 *out);
MR4TH_SYMBOL void mat4x4_mul(F32 *a, F32 *b, F32 *out);

////////////////////////////////
// Functions: Memory Operations

MR4TH_SYMBOL void memory_zero(void *ptr, U64 size);
MR4TH_SYMBOL void memory_fill(void *ptr, U64 size, U8 fillbyte);
MR4TH_SYMBOL B32  memory_match(void *a, void *b, U64 size);
MR4TH_SYMBOL void*memory_move(void *a, void *b, U64 size);

////////////////////////////////
// Functions: Symbolic Constants

MR4TH_SYMBOL OperatingSystem operating_system_from_context(void);
MR4TH_SYMBOL Architecture architecture_from_context(void);

MR4TH_SYMBOL char* string_from_operating_system(OperatingSystem os);
MR4TH_SYMBOL char* string_from_architecture(Architecture arch);
MR4TH_SYMBOL char* string_from_month(Month month);
MR4TH_SYMBOL char* string_from_day_of_week(DayOfWeek day_of_week);

////////////////////////////////
// Functions: Time

MR4TH_SYMBOL DenseTime dense_time_from_date_time(DateTime *date_time);
MR4TH_SYMBOL DateTime  date_time_from_dense_time(DenseTime dense_time);

////////////////////////////////
// Functions: Arenas

// arena core

MR4TH_SYMBOL_MUST_SHARE Arena* arena_new(U64 reserve_size, U64 alignment, B32 growing);
MR4TH_SYMBOL_MUST_SHARE void   arena_release(Arena *arena);
MR4TH_SYMBOL_MUST_SHARE void*  arena_push_no_zero(Arena *arena, U64 size);
MR4TH_SYMBOL_MUST_SHARE void   arena_pop_to(Arena *arena, U64 pos);
MR4TH_SYMBOL_MUST_SHARE U64    arena_current_pos(Arena *arena);
MR4TH_SYMBOL_MUST_SHARE U64    arena_current_align(Arena *arena);
MR4TH_SYMBOL_MUST_SHARE void   arena_set_align(Arena *arena, U64 alignment);

MR4TH_SYMBOL_MUST_SHARE ArenaTemp arena_get_scratch(Arena **conflict_array, U32 count);

// arena helpers

MR4TH_SYMBOL Arena* arena_alloc(void);
MR4TH_SYMBOL void*  arena_push(Arena *arena, U64 size);
MR4TH_SYMBOL void   arena_pop_amount(Arena *arena, U64 amount);
MR4TH_SYMBOL void   arena_align(Arena *arena, U64 pow2_align);

#define push_array(a,T,c) (T*)arena_push((a), sizeof(T)*(c))
#define push_array_no_zero(a,T,c) (T*)arena_push_no_zero((a), sizeof(T)*(c))

#define push_array_copy(a,T,c,p) (T*)(MemoryCopy(push_array(a,T,c), (p), sizeof(T)*(c)))

MR4TH_SYMBOL ArenaTemp arena_begin_temp(Arena *arena);
MR4TH_SYMBOL void      arena_end_temp(ArenaTemp *temp);

#define arena_release_scratch(temp) arena_end_temp(temp)

////////////////////////////////
// Functions: Strings

// characters

MR4TH_SYMBOL B32 char_is_whitespace(U8 c);
MR4TH_SYMBOL B32 char_is_slash(U8 c);
MR4TH_SYMBOL B32 char_is_digit(U8 c);

MR4TH_SYMBOL U8 char_to_uppercase(U8 c);
MR4TH_SYMBOL U8 char_to_lowercase(U8 c);

// in-place constructors

#define str8(p,z) CLiteral(String8){(U8*)(p), (z)}

MR4TH_SYMBOL String8 str8_range(U8 *first, U8 *opl);
MR4TH_SYMBOL String8 str8_cstring(U8 *cstr);
MR4TH_SYMBOL String8 str8_cstring_capped(U8 *cstr, U8 *opl);

MR4TH_SYMBOL String16 str16_cstring(U16 *cstr);

// literal constructors

#define str8_lit(s) CLiteral(String8){(U8*)(s), sizeof(s) - 1}
#define str8_lit_const(s) {(U8*)(s), sizeof(s) - 1}

// compound constructors

MR4TH_SYMBOL void str8_list_push(Arena *arena, String8List *list, String8 string);
MR4TH_SYMBOL void str8_list_push_front(Arena *arena, String8List *list, String8 string);

MR4TH_SYMBOL String8List str8_list_copy(Arena *arena, String8List *list);
MR4TH_SYMBOL String8     str8_join(Arena *arena, String8List *list, StringJoin *optional_join);
MR4TH_SYMBOL String8List str8_split(Arena *arena, String8 string, U8 *split_characters, U32 count);

MR4TH_SYMBOL String8 str8_pushfv(Arena *arena, char *fmt, va_list args);
MR4TH_SYMBOL String8 str8_pushf(Arena *arena, char *fmt, ...);
MR4TH_SYMBOL void    str8_list_pushf(Arena *arena, String8List *list, char *fmt, ...);

MR4TH_SYMBOL String8 str8_push_copy(Arena *arena, String8 string);

// substrings

MR4TH_SYMBOL String8 str8_prefix(String8 str, U64 size);
MR4TH_SYMBOL String8 str8_chop(String8 str, U64 amount);
MR4TH_SYMBOL String8 str8_postfix(String8 str, U64 size);
MR4TH_SYMBOL String8 str8_skip(String8 str, U64 amount);

MR4TH_SYMBOL String8 str8_skip_chop_whitespace(String8 str);

// hash

MR4TH_SYMBOL U64 str8_hash(String8 str);

// compare

MR4TH_SYMBOL B32 str8_match(String8 a, String8 b, StringMatchFlags flags);

// path helpers

MR4TH_SYMBOL String8 str8_chop_last_slash(String8 string);
MR4TH_SYMBOL String8 str8_file_name_from_path(String8 full_file_name);
MR4TH_SYMBOL String8 str8_base_name_from_file_name(String8 file_name);

// stylized constructors

MR4TH_SYMBOL String8 str8_join_flags(Arena *arena, String8List *list);

// unicode

MR4TH_SYMBOL StringDecode str_decode_utf8(U8 *str, U32 cap);
MR4TH_SYMBOL U32          str_encode_utf8(U8 *dst, U32 codepoint);
MR4TH_SYMBOL StringDecode str_decode_utf16(U16 *str, U32 cap);
MR4TH_SYMBOL U32          str_encode_utf16(U16 *dst, U32 codepoint);

MR4TH_SYMBOL String32     str32_from_str8(Arena *arena, String8 string);
MR4TH_SYMBOL String8      str8_from_str32(Arena *arena, String32 string);
MR4TH_SYMBOL String16     str16_from_str8(Arena *arena, String8 string);
MR4TH_SYMBOL String8      str8_from_str16(Arena *arena, String16 string);

// numeric conversion

MR4TH_SYMBOL B32 str8_is_u64(String8 string, U32 radix);

MR4TH_SYMBOL U64 u64_from_str8(String8 string, U32 radix);
MR4TH_SYMBOL U64 u64_from_str8_c_syntax(String8 string);
MR4TH_SYMBOL S64 s64_from_str8_c_syntax(String8 string);
MR4TH_SYMBOL F64 f64_from_str8(String8 string);

////////////////////////////////
// Functions: Stream

// stream core

MR4TH_SYMBOL_MUST_SHARE STREAM* stream_new(void);
MR4TH_SYMBOL_MUST_SHARE void    stream_release(STREAM *stream);

MR4TH_SYMBOL_MUST_SHARE void stream_clear(STREAM *stream);
MR4TH_SYMBOL_MUST_SHARE void stream_write(STREAM *stream, String8 data);
MR4TH_SYMBOL_MUST_SHARE U8*  stream_alloc(STREAM *stream, U64 size);

MR4TH_SYMBOL_MUST_SHARE U64            stream_total_size(STREAM *stream);
MR4TH_SYMBOL_MUST_SHARE STREAM_Handle* stream_next_chunk(STREAM *stream, STREAM_Handle *handle, String8 *chunk_out);

// stream helpers

MR4TH_SYMBOL String8 stream_read(Arena *arena, STREAM *stream);

MR4TH_SYMBOL void stream_printfv(STREAM *stream, char *fmt, va_list args);
MR4TH_SYMBOL void stream_printf(STREAM *stream, char *fmt, ...);

////////////////////////////////
// Functions: Buffer

MR4TH_SYMBOL void buffer_alloc(BUFFER *buffer, U64 reserve_size);
MR4TH_SYMBOL void buffer_release(BUFFER *buffer);
MR4TH_SYMBOL void buffer_reset(BUFFER *buffer);

MR4TH_SYMBOL B32  buffer_commit_off__call(BUFFER *buffer, U64 off);

#define buffer_commit_off(b, off)                   \
((((b)->commit_pos < (off) && (off) <= (b)->size)? \
(buffer_commit_off__call((b),(off))):((off) <= (b)->size)))

#define buffer_typed_commit_idx(b, idx) \
buffer_commit_off(&(b)->buffer, (idx)*sizeof(*(b)->ptr))

////////////////////////////////
// Types: Hash Table

MR4TH_SYMBOL void       hash_buckets_init(BUFFER *buckets, U64 size);
MR4TH_SYMBOL HASH_Node* hash_buckets_first(BUFFER *buckets, U64 hash);
MR4TH_SYMBOL void       hash_buckets_expand(BUFFER *buckets, U64 capacity);
MR4TH_SYMBOL void       hash_buckets_insert(Arena *arena, BUFFER *buckets, U64 hash, U64 val);

////////////////////////////////
// Functions: Log

MR4TH_SYMBOL void    log_accum_begin(LOG_LogToProc *proc, void *uptr);
MR4TH_SYMBOL B32     log_gathering(void);
MR4TH_SYMBOL void    log_emit(String8 message);
MR4TH_SYMBOL void    log_emitf(char *fmt, ...);
MR4TH_SYMBOL String8 log_accum_end(Arena *arena);

////////////////////////////////
// Functions: Errors

MR4TH_SYMBOL void    er_accum_begin(void);
MR4TH_SYMBOL void    er_emit(String8 error);
MR4TH_SYMBOL void    er_emitf(char *fmt, ...);
MR4TH_SYMBOL String8 er_accum_end(Arena *arena);

#define er_emit_lit(s) er_emit(str8_lit(s))


////////////////////////////////////////////////
////////////////////////////////////////////////
/////////   DECLARATIONS PROFILING   ///////////
////////////////////////////////////////////////
////////////////////////////////////////////////

////////////////////////////////
// Profiling: Implementable Interface

#if MR4TH_PROFILING_USER || MR4TH_PROFILING_PROVIDER

MR4TH_SYMBOL_LINK void prof_open(char *name);
MR4TH_SYMBOL_LINK void prof_close(void);
MR4TH_SYMBOL_LINK void prof_thread_begin(void);
MR4TH_SYMBOL_LINK void prof_thread_end(void);
MR4TH_SYMBOL_LINK void prof_thread_flush(void);
MR4TH_SYMBOL_LINK void prof_begin(char *name, U32 len);
MR4TH_SYMBOL_LINK void prof_end(void);

#endif

////////////////////////////////
// Profiling: User Interface

#if MR4TH_PROFILING_USER
# define ProfOpen(n)       prof_open((char*)(n))
# define ProfClose()       prof_close()
# define ProfThreadBegin() prof_thread_begin()
# define ProfThreadEnd()   prof_thread_end()
# define ProfThreadFlush() prof_thread_flush()
# define ProfBegin(n)      prof_begin((n), sizeof(n) - 1)
# define ProfEnd()         prof_end()
#else
# define ProfOpen(n)
# define ProfClose()
# define ProfThreadBegin()
# define ProfThreadEnd()
# define ProfThreadFlush()
# define ProfBegin(n)
# define ProfEnd()
#endif

#if MR4TH_PROFILING_MANUAL
# define ProfBeginManual(n) ProfBegin(n)
# define ProfEndManual()    ProfEnd()
#else
# define ProfBeginManual(n)
# define ProfEndManual()
#endif

#define ProfBeginFunc() ProfBeginManual(__FUNCTION__)
#define ProfEndFunc()   ProfEndManual()



////////////////////////////////////////////////
////////////////////////////////////////////////
///////   DECLARATIONS OS ABSTRACTION   ////////
////////////////////////////////////////////////
////////////////////////////////////////////////

////////////////////////////////
// Shared Types

typedef struct OS_FileIter{
  U8 v[640];
} OS_FileIter;

typedef enum OS_SystemPath{
  OS_SystemPath_CurrentDirectory = 0,
  OS_SystemPath_Binary           = 1,
  OS_SystemPath_UserData         = 2,
  OS_SystemPath_TempData         = 3,
  OS_SystemPath_COUNT
} OS_SystemPath;

typedef void OS_Library;

typedef struct OS_VRange{
  void *addr;
  U64 size;
} OS_VRange;

////////////////////////////////
// Implementable Functions: Process Setup

MR4TH_SYMBOL void        os_main_init(int argc, char **argv);
MR4TH_SYMBOL String8List os_command_line_arguments(void);
MR4TH_SYMBOL void        os_exit_process(U32 code);

////////////////////////////////
// Implementable Functions: Memory Functions

MR4TH_SYMBOL void* os_memory_reserve(U64 size);
MR4TH_SYMBOL B32   os_memory_commit(void *ptr, U64 size);
MR4TH_SYMBOL void  os_memory_decommit(void *ptr, U64 size);
MR4TH_SYMBOL void  os_memory_release(void *ptr, U64 size);

MR4TH_SYMBOL B32   os_memory_protect(void *ptr, U64 size, DataAccessFlags access_flags);

////////////////////////////////
// Implementable Functions: File Handling

MR4TH_SYMBOL String8 os_file_read(Arena *arena, String8 file_name);

typedef String8 OS_FileWriteCallback(void *udata);
MR4TH_SYMBOL B32 os_file_write_callback(String8 file_name, void *udata, OS_FileWriteCallback *callback);

MR4TH_SYMBOL FileProperties os_file_properties(String8 file_name);

MR4TH_SYMBOL B32 os_file_delete(String8 file_name);
MR4TH_SYMBOL B32 os_file_rename(String8 og_name, String8 new_name);
MR4TH_SYMBOL B32 os_file_make_directory(String8 path);
MR4TH_SYMBOL B32 os_file_delete_directory(String8 path);

MR4TH_SYMBOL OS_FileIter os_file_iter_init(Arena *arena, String8 path);
MR4TH_SYMBOL B32         os_file_iter_next(Arena *arena, OS_FileIter *iter,
                                           String8 *name_out, FileProperties *prop_out);
MR4TH_SYMBOL void        os_file_iter_end(OS_FileIter *iter);

MR4TH_SYMBOL String8 os_file_path(Arena *arena, OS_SystemPath path);

MR4TH_SYMBOL void    os_set_current_directory(String8 path);

////////////////////////////////
// Implementable Functions: Time

MR4TH_SYMBOL DateTime os_now_universal_time(void);
MR4TH_SYMBOL DateTime os_local_time_from_universal(DateTime *date_time);
MR4TH_SYMBOL DateTime os_universal_time_from_local(DateTime *date_time);

MR4TH_SYMBOL U32  os_time_stamp_32_from_date_time(DateTime *date_time);

MR4TH_SYMBOL U64  os_now_ticks(void);
MR4TH_SYMBOL void os_get_microseconds_per_tick(U64 *usecs_out, U64 *ticks_out);
MR4TH_SYMBOL void os_sleep_milliseconds(U32 t);

////////////////////////////////
// Implementable Functions: Libraries

MR4TH_SYMBOL OS_Library* os_lib_load(String8 path);
MR4TH_SYMBOL VoidFunc*   os_lib_get_proc(OS_Library *lib, char *name);
MR4TH_SYMBOL void        os_lib_release(OS_Library *lib);

MR4TH_SYMBOL OS_Library* os_lib_from_addr(void *addr);
MR4TH_SYMBOL OS_VRange   os_lib_image_range(OS_Library *lib);

////////////////////////////////
// Implementable Functions: Entropy

MR4TH_SYMBOL void os_get_entropy(void *data, U64 size);

////////////////////////////////
// Helper Functions: File Handling

MR4TH_SYMBOL B32 os_file_write(String8 file_name, String8 data);
MR4TH_SYMBOL B32 os_file_write_list(String8 file_name, String8Node *first_node);
MR4TH_SYMBOL B32 os_file_write_stream(String8 file_name, STREAM *stream);

////////////////////////////////
// Helper Functions: Libraries

MR4TH_SYMBOL OS_VRange os_this_image(void);



/*******************************
**      Begin Base Win32      **
*******************************/

#if OS_WINDOWS

////////////////////////////////
// Win32 Includes

#include <Windows.h>
#include <userenv.h>
#include <psapi.h>

////////////////////////////////
// Win32 Types

typedef struct W32_FileIter{
  HANDLE handle;
  WIN32_FIND_DATAW find_data;
  B32 done;
} W32_FileIter;
StaticAssert(sizeof(W32_FileIter) <= sizeof(OS_FileIter), w32_fileiter);

////////////////////////////////
// Win32 Functions: Specialized Init for WinMain

MR4TH_SYMBOL void w32_WinMain_init(HINSTANCE hInstance,
                                   HINSTANCE hPrevInstance,
                                   LPSTR     lpCmdLine,
                                   int       nShowCmd);

MR4TH_SYMBOL HINSTANCE w32_get_instance(void);

////////////////////////////////
// Win32 Functions: Time Helpers

MR4TH_SYMBOL DateTime   w32_date_time_from_system_time(SYSTEMTIME *in);
MR4TH_SYMBOL SYSTEMTIME w32_system_time_from_date_time(DateTime *in);
MR4TH_SYMBOL DenseTime  w32_dense_time_from_file_time(FILETIME *file_time);

////////////////////////////////
// Win32 Functions: File Helpers

MR4TH_SYMBOL FilePropertyFlags w32_prop_flags_from_attribs(DWORD attribs);
MR4TH_SYMBOL DataAccessFlags   w32_access_from_attributes(DWORD attribs);

////////////////////////////////
// Win32 Helper Macro

#define W32_PROC_ADDR(v,m,s) (*(PROC*)(&(v))) = GetProcAddress((m),(s))

#endif /* OS_WINDOWS */

/*******************************
**       End Base Win32       **
*******************************/



/*******************************
**      Begin Base Linux      **
*******************************/

#if OS_LINUX

////////////////////////////////
// Linux Includes

#include <stdlib.h>
#include <stdio.h>
#include <linux/limits.h>
#include <limits.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/random.h>
#include <time.h>
#include <dirent.h>
#include <dlfcn.h>
#include <errno.h>
#include <string.h>

////////////////////////////////
// Linux Types

typedef struct LNX_FileIter{
  DIR *dir;
  struct dirent *dp;
  String8 path;
} LNX_FileIter;
StaticAssert(sizeof(LNX_FileIter) <= sizeof(OS_FileIter), lnx_fileiter);

////////////////////////////////
// Linux Functions: Time Helpers

MR4TH_SYMBOL DateTime   lnx_date_time_from_tm(struct tm *tm);
MR4TH_SYMBOL DenseTime  lnx_dense_time_from_time_t(time_t time);
MR4TH_SYMBOL struct tm  lnx_tm_from_date_time(DateTime *date_time);

////////////////////////////////
// Linux Functions: File Helpers

MR4TH_SYMBOL FilePropertyFlags lnx_prop_flags_from_mode(mode_t mode);
MR4TH_SYMBOL DataAccessFlags   lnx_access_from_mode(mode_t mode);
MR4TH_SYMBOL FileProperties    lnx_file_properties_from_stat(struct stat *st);

MR4TH_SYMBOL String8 lnx_proc_file_read(Arena *arena, char *path);

#endif /* OS_LINUX */

/*******************************
**       End Base Linux       **
*******************************/


////////////////////////////////////////////////
////////////////////////////////////////////////
/////////////////   M4 Printf   ////////////////
////////////////////////////////////////////////
////////////////////////////////////////////////

#if 0
// TODO(allen): develop this idea further

typedef struct M4_FormatterFields{
  S32 precision;
} M4_FormatterFields;

typedef void M4_Formatter(STREAM *stream, M4_FormatterFields *fields, va_list va);

typedef struct M4_FormatTable{
  M4_Formatter *formatters[26];
} M4_FormatTable;

MR4TH_SYMBOL m4_set_format_table(M4_FormatTable *table);

#endif


// stb_sprintf.h STB_SPRINTF_H_INCLUDE

/*
Single file sprintf replacement.

Originally written by Jeff Roberts at RAD Game Tools - 2015/10/20.
Hereby placed in public domain.

This is a full sprintf replacement that supports everything that
the C runtime sprintfs support, including float/double, 64-bit integers,
hex floats, field parameters (%*.*d stuff), length reads backs, etc.

Why would you need this if sprintf already exists?  Well, first off,
it's *much* faster (see below). It's also much smaller than the CRT
versions code-space-wise. We've also added some simple improvements
that are super handy (commas in thousands, callbacks at buffer full,
for example). Finally, the format strings for MSVC and GCC differ
for 64-bit integers (among other small things), so this lets you use
the same format strings in cross platform code.

It uses the standard single file trick of being both the header file
and the source itself. If you just include it normally, you just get
the header file function definitions. To get the code, you include
it from a C or C++ file and define STB_SPRINTF_IMPLEMENTATION first.

It only uses va_args macros from the C runtime to do it's work. It
does cast doubles to S64s and shifts and divides U64s, which does
drag in CRT code on most platforms.

It compiles to roughly 8K with float support, and 4K without.
As a comparison, when using MSVC static libs, calling sprintf drags
in 16K.

API:
====
int stbsp_sprintf( char * buf, char const * fmt, ... )
int stbsp_snprintf( char * buf, int count, char const * fmt, ... )
  Convert an arg list into a buffer.  stbsp_snprintf always returns
  a zero-terminated string (unlike regular snprintf).

int stbsp_vsprintf( char * buf, char const * fmt, va_list va )
int stbsp_vsnprintf( char * buf, int count, char const * fmt, va_list va )
  Convert a va_list arg list into a buffer.  stbsp_vsnprintf always returns
  a zero-terminated string (unlike regular snprintf).

int stbsp_vsprintfcb( STBSP_SPRINTFCB * callback, void * user, char * buf, char const * fmt, va_list va )
    typedef char * STBSP_SPRINTFCB( char const * buf, void * user, int len );
  Convert into a buffer, calling back every STB_SPRINTF_MIN chars.
  Your callback can then copy the chars out, print them or whatever.
  This function is actually the workhorse for everything else.
  The buffer you pass in must hold at least STB_SPRINTF_MIN characters.
    // you return the next buffer to use or 0 to stop converting

void stbsp_set_separators( char comma, char period )
  Set the comma and period characters to use.

FLOATS/DOUBLES:
===============
This code uses a internal float->ascii conversion method that uses
doubles with error correction (double-doubles, for ~105 bits of
precision).  This conversion is round-trip perfect - that is, an atof
of the values output here will give you the bit-exact double back.

One difference is that our insignificant digits will be different than
with MSVC or GCC (but they don't match each other either).  We also
don't attempt to find the minimum length matching float (pre-MSVC15
doesn't either).

If you don't need float or doubles at all, define STB_SPRINTF_NOFLOAT
and you'll save 4K of code space.
NOTE(allen): MODIFICATION - I've hard coded that this copy *does* use floats

64-BIT INTS:
============
This library also supports 64-bit integers and you can use MSVC style or
GCC style indicators (%I64d or %lld).  It supports the C99 specifiers
for size_t and ptr_diff_t (%jd %zd) as well.

EXTRAS:
=======
Like some GCCs, for integers and floats, you can use a ' (single quote)
specifier and commas will be inserted on the thousands: "%'d" on 12345
would print 12,345.

For integers and floats, you can use a "$" specifier and the number
will be converted to float and then divided to get kilo, mega, giga or
tera and then printed, so "%$d" 1000 is "1.0 k", "%$.2d" 2536000 is
"2.53 M", etc. For byte values, use two $:s, like "%$$d" to turn
2536000 to "2.42 Mi". If you prefer JEDEC suffixes to SI ones, use three
$:s: "%$$$d" -> "2.42 M". To remove the space between the number and the
suffix, add "_" specifier: "%_$d" -> "2.53M".

In addition to octal and hexadecimal conversions, you can print
integers in binary: "%b" for 256 would print 100.

PERFORMANCE vs MSVC 2008 32-/64-bit (GCC is even slower than MSVC):
===================================================================
"%d" across all 32-bit ints (4.8x/4.0x faster than 32-/64-bit MSVC)
"%24d" across all 32-bit ints (4.5x/4.2x faster)
"%x" across all 32-bit ints (4.5x/3.8x faster)
"%08x" across all 32-bit ints (4.3x/3.8x faster)
"%f" across e-10 to e+10 floats (7.3x/6.0x faster)
"%e" across e-10 to e+10 floats (8.1x/6.0x faster)
"%g" across e-10 to e+10 floats (10.0x/7.1x faster)
"%f" for values near e-300 (7.9x/6.5x faster)
"%f" for values near e+300 (10.0x/9.1x faster)
"%e" for values near e-300 (10.1x/7.0x faster)
"%e" for values near e+300 (9.2x/6.0x faster)
"%.320f" for values near e-300 (12.6x/11.2x faster)
"%a" for random values (8.6x/4.3x faster)
"%I64d" for 64-bits with 32-bit values (4.8x/3.4x faster)
"%I64d" for 64-bits > 32-bit values (4.9x/5.5x faster)
"%s%s%s" for 64 char strings (7.1x/7.3x faster)
"...512 char string..." ( 35.0x/32.5x faster!)
*/

#if defined(__clang__)
#if defined(__has_feature) && defined(__has_attribute)
#if __has_feature(address_sanitizer)
#if __has_attribute(__no_sanitize__)
#define STBSP__ASAN __attribute__((__no_sanitize__("address")))
#elif __has_attribute(__no_sanitize_address__)
#define STBSP__ASAN __attribute__((__no_sanitize_address__))
#elif __has_attribute(__no_address_safety_analysis__)
#define STBSP__ASAN __attribute__((__no_address_safety_analysis__))
#endif
#endif
#endif
#elif defined(__GNUC__) && (__GNUC__ >= 5 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))
#if defined(__SANITIZE_ADDRESS__) && __SANITIZE_ADDRESS__
#define STBSP__ASAN __attribute__((__no_sanitize_address__))
#endif
#endif

#ifndef STBSP__ASAN
#define STBSP__ASAN
#endif

#ifdef STB_SPRINTF_STATIC
#define STBSP__PUBLICDEC static
#define STBSP__PUBLICDEF static STBSP__ASAN
#else
#ifdef __cplusplus
#define STBSP__PUBLICDEC extern "C"
#define STBSP__PUBLICDEF extern "C" STBSP__ASAN
#else
#define STBSP__PUBLICDEC extern
#define STBSP__PUBLICDEF STBSP__ASAN
#endif
#endif

#if defined(__has_attribute)
#if __has_attribute(format)
#define STBSP__ATTRIBUTE_FORMAT(fmt,va) __attribute__((format(printf,fmt,va)))
#endif
#endif

#ifndef STBSP__ATTRIBUTE_FORMAT
#define STBSP__ATTRIBUTE_FORMAT(fmt,va)
#endif

#ifdef _MSC_VER
#define STBSP__NOTUSED(v)  (void)(v)
#else
#define STBSP__NOTUSED(v)  (void)sizeof(v)
#endif

//#include <stddef.h> // size_t, ptrdiff_t

#ifndef STB_SPRINTF_MIN
#define STB_SPRINTF_MIN 512 // how many characters per callback
#endif
typedef char *STBSP_SPRINTFCB(const char *buf, void *user, int len);

STBSP__PUBLICDEC int  m4_vsprintf(char *buf, char const *fmt, va_list va);
STBSP__PUBLICDEC int  m4_vsnprintf(char *buf, int count, char const *fmt, va_list va);
STBSP__PUBLICDEC int  m4_sprintf(char *buf, char const *fmt, ...) STBSP__ATTRIBUTE_FORMAT(2,3);
STBSP__PUBLICDEC int  m4_snprintf(char *buf, int count, char const *fmt, ...) STBSP__ATTRIBUTE_FORMAT(3,4);

STBSP__PUBLICDEC int  m4_vsprintfcb(STBSP_SPRINTFCB *callback, void *user, char *buf, char const *fmt, va_list va);
STBSP__PUBLICDEC void m4_set_separators(char comma, char period);

/*
** NOTE(allen): sprintf modifications notes
**
** Legend for specifier allocation:
**  [ ] - specifier is free for use
**  [#] - used by stb_sprintf default
**  [$] - used by built-in custom printing logic
**
**  Specifier allocation:
**   a[#] - hex float
**   A[#] - hex float
**   b[#] - binary
**   B[#] - binary
**   c[#] - char
**   C[ ]
**   d[#] - integer
**   D[ ]
**   e[#] - float
**   E[#] - float
**   f[#] - float
**   F[ ]
**   g[#] - float
**   G[#] - float
**   h[#] - half width
**   H[ ]
**   i[#] - integer
**   I[#] - size[Microsoft-Style]
**   j[#] - size[size_t]
**   J[ ]
**   k[ ]
**   K[ ]
**   l[#] - size[long]/size[long long]
**   L[ ]
**   m[ ]
**   M[ ]
**   n[#] - write-bytes
**   N[$] - indentation
**   o[#] - octal
**   O[ ]
**   p[ ]
**   P[ ]
**   q[ ]
**   Q[ ]
**   r[ ]
**   R[$]
**   s[#] - cstr
**   S[$] - String8
**   t[#] - size[ptrdiff_t]
**   T[ ]
**   u[#] - unsigned integer
**   U[ ]
**   v[ ]
**   V[ ]
**   w[ ]
**   W[ ]
**   x[#] - hex
**   X[#] - hex
**   y[ ]
**   Y[ ]
**   z[#] - size[size_t]
**   Z[ ]
**   0[#] - leading zeroes
**   1[ ]
**   2[ ]
**   3[ ]
**   4[ ]
**   5[ ]
**   6[ ]
**   7[ ]
**   8[ ]
**   9[ ]
**   $[$] - memory size (modified from stb_sprintf.h)
**   ?[ ]
**
*/

#endif /* MR4TH_BASE_H */

/*
** TODO:
** [ ] m4_printf plugins experiment
**
** [ ] unicode support
**     [ ] high performance encode/decode factorization
**     [ ] unicode string conversions should take string lists "Better unicode conversions here"
** [ ] math implementation replacements
** [ ] clib elimination option
**     [x] manually implement memset,memcmp,memmove
**         [x] basic C implementations
**         [ ] high performance implementations (hand written assembly perhaps? -- or at least use vector intrinsics)
**     [ ] necessary stubs for clib elimination
** [ ] Test out the usage process
**     [ ] default (unity build standalone)
**     [ ] standalone multi-unit
**     [ ] core & plugin
** [ ] Write instructions
** [ ] Write examples
**
** [ ] Multi-threading primitives
** [ ] Thread-safe STREAM
*/
