diff --git a/thirdparty/stb/lib/stb_truetype.lib b/thirdparty/stb/lib/stb_truetype.lib index 6d2c666..fb5a449 100644 Binary files a/thirdparty/stb/lib/stb_truetype.lib and b/thirdparty/stb/lib/stb_truetype.lib differ diff --git a/thirdparty/stb/src/gb/gb.h b/thirdparty/stb/src/gb/gb.h deleted file mode 100644 index 9be470c..0000000 --- a/thirdparty/stb/src/gb/gb.h +++ /dev/null @@ -1,10827 +0,0 @@ -/* gb.h - v0.33 - Ginger Bill's C Helper Library - public domain - - no warranty implied; use at your own risk - - This is a single header file with a bunch of useful stuff - to replace the C/C++ standard library - -=========================================================================== - YOU MUST - - #define GB_IMPLEMENTATION - - in EXACTLY _one_ C or C++ file that includes this header, BEFORE the - include like this: - - #define GB_IMPLEMENTATION - #include "gb.h" - - All other files should just #include "gb.h" without #define - - - If you want the platform layer, YOU MUST - - #define GB_PLATFORM - - BEFORE the include like this: - - #define GB_PLATFORM - #include "gb.h" - -=========================================================================== - -LICENSE - This software is dual-licensed to the public domain and under the following - license: you are granted a perpetual, irrevocable license to copy, modify, - publish, and distribute this file as you see fit. - -WARNING - - This library is _slightly_ experimental and features may not work as expected. - - This also means that many functions are not documented. - -CREDITS - Written by Ginger Bill - -TODOS - - Remove CRT dependency for people who want that - - But do I really? - - Or make it only depend on the really needed stuff? - - Older compiler support? - - How old do you wanna go? - - Only support C90+extension and C99 not pure C89. - - File handling - - All files to be UTF-8 (even on windows) - - Better Virtual Memory handling - - Generic Heap Allocator (tcmalloc/dlmalloc/?) - - Fixed Heap Allocator - - Better UTF support and conversion - - Free List, best fit rather than first fit - - More date & time functions - -VERSION HISTORY - 0.33 - Minor fixes - 0.32 - Minor fixes - 0.31 - Add gb_file_remove - 0.30 - Changes to gbThread (and gbMutex on Windows) - 0.29 - Add extras for gbString - 0.28 - Handle UCS2 correctly in Win32 part - 0.27 - OSX fixes and Linux gbAffinity - 0.26d - Minor changes to how gbFile works - 0.26c - gb_str_to_f* fix - 0.26b - Minor fixes - 0.26a - gbString Fix - 0.26 - Default allocator flags and generic hash table - 0.25a - Fix UTF-8 stuff - 0.25 - OS X gbPlatform Support (missing some things) - 0.24b - Compile on OSX (excluding platform part) - 0.24a - Minor additions - 0.24 - Enum convention change - 0.23 - Optional Windows.h removal (because I'm crazy) - 0.22a - Remove gbVideoMode from gb_platform_init_* - 0.22 - gbAffinity - (Missing Linux version) - 0.21 - Platform Layer Restructuring - 0.20 - Improve file io - 0.19 - Clipboard Text - 0.18a - Controller vibration - 0.18 - Raw keyboard and mouse input for WIN32 - 0.17d - Fixed printf bug for strings - 0.17c - Compile as 32 bit - 0.17b - Change formating style because why not? - 0.17a - Dropped C90 Support (For numerous reasons) - 0.17 - Instantiated Hash Table - 0.16a - Minor code layout changes - 0.16 - New file API and improved platform layer - 0.15d - Linux Experimental Support (DON'T USE IT PLEASE) - 0.15c - Linux Experimental Support (DON'T USE IT) - 0.15b - C90 Support - 0.15a - gb_atomic(32|64)_spin_(lock|unlock) - 0.15 - Recursive "Mutex"; Key States; gbRandom - 0.14 - Better File Handling and better printf (WIN32 Only) - 0.13 - Highly experimental platform layer (WIN32 Only) - 0.12b - Fix minor file bugs - 0.12a - Compile as C++ - 0.12 - New File Handing System! No stdio or stdlib! (WIN32 Only) - 0.11a - Add string precision and width (experimental) - 0.11 - Started making stdio & stdlib optional (Not tested much) - 0.10c - Fix gb_endian_swap32() - 0.10b - Probable timing bug for gb_time_now() - 0.10a - Work on multiple compilers - 0.10 - Scratch Memory Allocator - 0.09a - Faster Mutex and the Free List is slightly improved - 0.09 - Basic Virtual Memory System and Dreadful Free List allocator - 0.08a - Fix *_appendv bug - 0.08 - Huge Overhaul! - 0.07a - Fix alignment in gb_heap_allocator_proc - 0.07 - Hash Table and Hashing Functions - 0.06c - Better Documentation - 0.06b - OS X Support - 0.06a - Linux Support - 0.06 - Windows GCC Support and MSVC x86 Support - 0.05b - Formatting - 0.05a - Minor function name changes - 0.05 - Radix Sort for unsigned integers (TODO: Other primitives) - 0.04 - Better UTF support and search/sort procs - 0.03 - Completely change procedure naming convention - 0.02a - Bug fixes - 0.02 - Change naming convention and gbArray(Type) - 0.01 - Initial Version -*/ - - -#ifndef GB_INCLUDE_GB_H -#define GB_INCLUDE_GB_H - -#if defined(__cplusplus) -extern "C" { -#endif - -#if defined(__cplusplus) - #define GB_EXTERN extern "C" -#else - #define GB_EXTERN extern -#endif - -#if defined(_WIN32) - #define GB_DLL_EXPORT GB_EXTERN __declspec(dllexport) - #define GB_DLL_IMPORT GB_EXTERN __declspec(dllimport) -#else - #define GB_DLL_EXPORT GB_EXTERN __attribute__((visibility("default"))) - #define GB_DLL_IMPORT GB_EXTERN -#endif - -// NOTE(bill): Redefine for DLL, etc. -#ifndef GB_DEF - #ifdef GB_STATIC - #define GB_DEF static - #else - #define GB_DEF extern - #endif -#endif - -#if defined(_WIN64) || defined(__x86_64__) || defined(_M_X64) || defined(__64BIT__) || defined(__powerpc64__) || defined(__ppc64__) - #ifndef GB_ARCH_64_BIT - #define GB_ARCH_64_BIT 1 - #endif -#else - // NOTE(bill): I'm only supporting 32 bit and 64 bit systems - #ifndef GB_ARCH_32_BIT - #define GB_ARCH_32_BIT 1 - #endif -#endif - - -#ifndef GB_ENDIAN_ORDER -#define GB_ENDIAN_ORDER - // TODO(bill): Is the a good way or is it better to test for certain compilers and macros? - #define GB_IS_BIG_ENDIAN (!*(u8*)&(u16){1}) - #define GB_IS_LITTLE_ENDIAN (!GB_IS_BIG_ENDIAN) -#endif - -#if defined(_WIN32) || defined(_WIN64) - #ifndef GB_SYSTEM_WINDOWS - #define GB_SYSTEM_WINDOWS 1 - #endif -#elif defined(__APPLE__) && defined(__MACH__) - #ifndef GB_SYSTEM_OSX - #define GB_SYSTEM_OSX 1 - #endif -#elif defined(__unix__) - #ifndef GB_SYSTEM_UNIX - #define GB_SYSTEM_UNIX 1 - #endif - - #if defined(__linux__) - #ifndef GB_SYSTEM_LINUX - #define GB_SYSTEM_LINUX 1 - #endif - #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) - #ifndef GB_SYSTEM_FREEBSD - #define GB_SYSTEM_FREEBSD 1 - #endif - #else - #error This UNIX operating system is not supported - #endif -#else - #error This operating system is not supported -#endif - -#if defined(_MSC_VER) - #define GB_COMPILER_MSVC 1 -#elif defined(__GNUC__) - #define GB_COMPILER_GCC 1 -#elif defined(__clang__) - #define GB_COMPILER_CLANG 1 -#else - #error Unknown compiler -#endif - -#if defined(_M_IX86) || defined(_M_X64) || defined(__i386__) || defined(__x86_64__) - #ifndef GB_CPU_X86 - #define GB_CPU_X86 1 - #endif - #ifndef GB_CACHE_LINE_SIZE - #define GB_CACHE_LINE_SIZE 64 - #endif - -#elif defined(_M_PPC) || defined(__powerpc__) || defined(__powerpc64__) - #ifndef GB_CPU_PPC - #define GB_CPU_PPC 1 - #endif - #ifndef GB_CACHE_LINE_SIZE - #define GB_CACHE_LINE_SIZE 128 - #endif - -#elif defined(__arm__) || defined(__aarch64__) - #ifndef GB_CPU_ARM - #define GB_CPU_ARM 1 - #endif - #ifndef GB_CACHE_LINE_SIZE - #define GB_CACHE_LINE_SIZE 64 - #endif - -#elif defined(__MIPSEL__) || defined(__mips_isa_rev) - #ifndef GB_CPU_MIPS - #define GB_CPU_MIPS 1 - #endif - #ifndef GB_CACHE_LINE_SIZE - #define GB_CACHE_LINE_SIZE 64 - #endif - -#else - #error Unknown CPU Type -#endif - - - -#ifndef GB_STATIC_ASSERT - #define GB_STATIC_ASSERT3(cond, msg) typedef char static_assertion_##msg[(!!(cond))*2-1] - // NOTE(bill): Token pasting madness!! - #define GB_STATIC_ASSERT2(cond, line) GB_STATIC_ASSERT3(cond, static_assertion_at_line_##line) - #define GB_STATIC_ASSERT1(cond, line) GB_STATIC_ASSERT2(cond, line) - #define GB_STATIC_ASSERT(cond) GB_STATIC_ASSERT1(cond, __LINE__) -#endif - - -//////////////////////////////////////////////////////////////// -// -// Headers -// -// - -#if defined(_WIN32) && !defined(__MINGW32__) - #ifndef _CRT_SECURE_NO_WARNINGS - #define _CRT_SECURE_NO_WARNINGS - #endif -#endif - -#if defined(GB_SYSTEM_UNIX) - #define _GNU_SOURCE - #define _LARGEFILE64_SOURCE -#endif - - -// TODO(bill): How many of these headers do I really need? -// #include -#if !defined(GB_SYSTEM_WINDOWS) - #include - #include -#endif - - - -#if defined(GB_SYSTEM_WINDOWS) - #if !defined(GB_NO_WINDOWS_H) - #define NOMINMAX 1 - #define WIN32_LEAN_AND_MEAN 1 - #define WIN32_MEAN_AND_LEAN 1 - #define VC_EXTRALEAN 1 - #include - #undef NOMINMAX - #undef WIN32_LEAN_AND_MEAN - #undef WIN32_MEAN_AND_LEAN - #undef VC_EXTRALEAN - #endif - - #include // NOTE(bill): _aligned_*() - #include -#else - #include - #include - #include - #include - #ifndef _IOSC11_SOURCE - #define _IOSC11_SOURCE - #endif - #include // NOTE(bill): malloc on linux - #include - #if !defined(GB_SYSTEM_OSX) - #include - #endif - #include - #include - #include - #include - #include - - #if defined(GB_CPU_X86) - #include - #endif -#endif - -#if defined(GB_SYSTEM_OSX) - #include - #include - #include - #include - #include - #include - #include - #include -#endif - -#if defined(GB_SYSTEM_UNIX) - #include -#endif - - -//////////////////////////////////////////////////////////////// -// -// Base Types -// -// - -#if defined(GB_COMPILER_MSVC) - #if _MSC_VER < 1300 - typedef unsigned char u8; - typedef signed char i8; - typedef unsigned short u16; - typedef signed short i16; - typedef unsigned int u32; - typedef signed int i32; - #else - typedef unsigned __int8 u8; - typedef signed __int8 i8; - typedef unsigned __int16 u16; - typedef signed __int16 i16; - typedef unsigned __int32 u32; - typedef signed __int32 i32; - #endif - typedef unsigned __int64 u64; - typedef signed __int64 i64; -#else - #include - typedef uint8_t u8; - typedef int8_t i8; - typedef uint16_t u16; - typedef int16_t i16; - typedef uint32_t u32; - typedef int32_t i32; - typedef uint64_t u64; - typedef int64_t i64; -#endif - -GB_STATIC_ASSERT(sizeof(u8) == sizeof(i8)); -GB_STATIC_ASSERT(sizeof(u16) == sizeof(i16)); -GB_STATIC_ASSERT(sizeof(u32) == sizeof(i32)); -GB_STATIC_ASSERT(sizeof(u64) == sizeof(i64)); - -GB_STATIC_ASSERT(sizeof(u8) == 1); -GB_STATIC_ASSERT(sizeof(u16) == 2); -GB_STATIC_ASSERT(sizeof(u32) == 4); -GB_STATIC_ASSERT(sizeof(u64) == 8); - -typedef size_t usize; -typedef ptrdiff_t isize; - -GB_STATIC_ASSERT(sizeof(usize) == sizeof(isize)); - -// NOTE(bill): (u)intptr is only here for semantic reasons really as this library will only support 32/64 bit OSes. -// NOTE(bill): Are there any modern OSes (not 16 bit) where intptr != isize ? -#if defined(_WIN64) - typedef signed __int64 intptr; - typedef unsigned __int64 uintptr; -#elif defined(_WIN32) - // NOTE(bill); To mark types changing their size, e.g. intptr - #ifndef _W64 - #if !defined(__midl) && (defined(_X86_) || defined(_M_IX86)) && _MSC_VER >= 1300 - #define _W64 __w64 - #else - #define _W64 - #endif - #endif - - typedef _W64 signed int intptr; - typedef _W64 unsigned int uintptr; -#else - typedef uintptr_t uintptr; - typedef intptr_t intptr; -#endif - -GB_STATIC_ASSERT(sizeof(uintptr) == sizeof(intptr)); - -typedef float f32; -typedef double f64; - -GB_STATIC_ASSERT(sizeof(f32) == 4); -GB_STATIC_ASSERT(sizeof(f64) == 8); - -typedef i32 Rune; // NOTE(bill): Unicode codepoint -#define GB_RUNE_INVALID cast(Rune)(0xfffd) -#define GB_RUNE_MAX cast(Rune)(0x0010ffff) -#define GB_RUNE_BOM cast(Rune)(0xfeff) -#define GB_RUNE_EOF cast(Rune)(-1) - - -typedef i8 b8; -typedef i16 b16; -typedef i32 b32; // NOTE(bill): Prefer this!!! - -// NOTE(bill): Get true and false -#if !defined(__cplusplus) - #if (defined(_MSC_VER) && _MSC_VER < 1800) || (!defined(_MSC_VER) && !defined(__STDC_VERSION__)) - #ifndef true - #define true (0 == 0) - #endif - #ifndef false - #define false (0 != 0) - #endif - typedef b8 bool; - #else - #include - #endif -#endif - -// NOTE(bill): These do are not prefixed with gb because the types are not. -#ifndef U8_MIN -#define U8_MIN 0u -#define U8_MAX 0xffu -#define I8_MIN (-0x7f - 1) -#define I8_MAX 0x7f - -#define U16_MIN 0u -#define U16_MAX 0xffffu -#define I16_MIN (-0x7fff - 1) -#define I16_MAX 0x7fff - -#define U32_MIN 0u -#define U32_MAX 0xffffffffu -#define I32_MIN (-0x7fffffff - 1) -#define I32_MAX 0x7fffffff - -#define U64_MIN 0ull -#define U64_MAX 0xffffffffffffffffull -#define I64_MIN (-0x7fffffffffffffffll - 1) -#define I64_MAX 0x7fffffffffffffffll - -#if defined(GB_ARCH_32_BIT) - #define USIZE_MIX U32_MIN - #define USIZE_MAX U32_MAX - - #define ISIZE_MIX S32_MIN - #define ISIZE_MAX S32_MAX -#elif defined(GB_ARCH_64_BIT) - #define USIZE_MIX U64_MIN - #define USIZE_MAX U64_MAX - - #define ISIZE_MIX I64_MIN - #define ISIZE_MAX I64_MAX -#else - #error Unknown architecture size. This library only supports 32 bit and 64 bit architectures. -#endif - -#define F32_MIN 1.17549435e-38f -#define F32_MAX 3.40282347e+38f - -#define F64_MIN 2.2250738585072014e-308 -#define F64_MAX 1.7976931348623157e+308 - -#endif - -#ifndef NULL - #if defined(__cplusplus) - #if __cplusplus >= 201103L - #define NULL nullptr - #else - #define NULL 0 - #endif - #else - #define NULL ((void *)0) - #endif -#endif - -// TODO(bill): Is this enough to get inline working? -#if !defined(__cplusplus) - #if defined(_MSC_VER) && _MSC_VER <= 1800 - #define inline __inline - #elif !defined(__STDC_VERSION__) - #define inline __inline__ - #else - #define inline - #endif -#endif - -#if !defined(gb_restrict) - #if defined(_MSC_VER) - #define gb_restrict __restrict - #elif defined(__STDC_VERSION__) - #define gb_restrict restrict - #else - #define gb_restrict - #endif -#endif - -// TODO(bill): Should force inline be a separate keyword and gb_inline be inline? -#if !defined(gb_inline) - #if defined(_MSC_VER) - #if _MSC_VER < 1300 - #define gb_inline - #else - #define gb_inline __forceinline - #endif - #elif (__GNUC__) - #define gb_inline inline - #else - #define gb_inline __attribute__ ((__always_inline__)) - #endif -#endif - -#if !defined(gb_no_inline) - #if defined(_MSC_VER) - #define gb_no_inline __declspec(noinline) - #else - #define gb_no_inline __attribute__ ((noinline)) - #endif -#endif - - -#if !defined(gb_thread_local) - #if defined(_MSC_VER) && _MSC_VER >= 1300 - #define gb_thread_local __declspec(thread) - #elif defined(__GNUC__) - #define gb_thread_local __thread - #else - #define gb_thread_local thread_local - #endif -#endif - - -// NOTE(bill): Easy to grep -// NOTE(bill): Not needed in macros -#ifndef cast -#define cast(Type) (Type) -#endif - -// NOTE(bill): Because a signed sizeof is more useful -#ifndef gb_size_of -#define gb_size_of(x) (isize)(sizeof(x)) -#endif - -#ifndef gb_count_of -#define gb_count_of(x) ((gb_size_of(x)/gb_size_of(0[x])) / ((isize)(!(gb_size_of(x) % gb_size_of(0[x]))))) -#endif - -#ifndef gb_offset_of -#define gb_offset_of(Type, element) ((isize)&(((Type *)0)->element)) -#endif - -#if defined(__cplusplus) -#ifndef gb_align_of - #if __cplusplus >= 201103L - #define gb_align_of(Type) (isize)alignof(Type) - #else -extern "C++" { - // NOTE(bill): Fucking Templates! - template struct gbAlignment_Trick { char c; T member; }; - #define gb_align_of(Type) gb_offset_of(gbAlignment_Trick, member) -} - #endif -#endif -#else - #ifndef gb_align_of - #define gb_align_of(Type) gb_offset_of(struct { char c; Type member; }, member) - #endif -#endif - -// NOTE(bill): I do wish I had a type_of that was portable -#ifndef gb_swap -#define gb_swap(Type, a, b) do { Type tmp = (a); (a) = (b); (b) = tmp; } while (0) -#endif - -// NOTE(bill): Because static means 3/4 different things in C/C++. Great design (!) -#ifndef gb_global -#define gb_global static // Global variables -#define gb_internal static // Internal linkage -#define gb_local_persist static // Local Persisting variables -#endif - - -#ifndef gb_unused - #if defined(_MSC_VER) - #define gb_unused(x) (__pragma(warning(suppress:4100))(x)) - #elif defined (__GCC__) - #define gb_unused(x) __attribute__((__unused__))(x) - #else - #define gb_unused(x) ((void)(gb_size_of(x))) - #endif -#endif - - - - -//////////////////////////////////////////////////////////////// -// -// Defer statement -// Akin to D's SCOPE_EXIT or -// similar to Go's defer but scope-based -// -// NOTE: C++11 (and above) only! -// -#if !defined(GB_NO_DEFER) && defined(__cplusplus) && ((defined(_MSC_VER) && _MSC_VER >= 1400) || (__cplusplus >= 201103L)) -extern "C++" { - // NOTE(bill): Stupid fucking templates - template struct gbRemoveReference { typedef T Type; }; - template struct gbRemoveReference { typedef T Type; }; - template struct gbRemoveReference { typedef T Type; }; - - /// NOTE(bill): "Move" semantics - invented because the C++ committee are idiots (as a collective not as indiviuals (well a least some aren't)) - template inline T &&gb_forward(typename gbRemoveReference::Type &t) { return static_cast(t); } - template inline T &&gb_forward(typename gbRemoveReference::Type &&t) { return static_cast(t); } - template inline T &&gb_move (T &&t) { return static_cast::Type &&>(t); } - template - struct gbprivDefer { - F f; - gbprivDefer(F &&f) : f(gb_forward(f)) {} - ~gbprivDefer() { f(); } - }; - template gbprivDefer gb__defer_func(F &&f) { return gbprivDefer(gb_forward(f)); } - - #define GB_DEFER_1(x, y) x##y - #define GB_DEFER_2(x, y) GB_DEFER_1(x, y) - #define GB_DEFER_3(x) GB_DEFER_2(x, __COUNTER__) - #define defer(code) auto GB_DEFER_3(_defer_) = gb__defer_func([&]()->void{code;}) -} - -// Example -#if 0 - gbMutex m; - gb_mutex_init(&m); - { - gb_mutex_lock(&m); - defer (gb_mutex_unlock(&m)); - - ... - } -#endif - -#endif - - -//////////////////////////////////////////////////////////////// -// -// Macro Fun! -// -// - -#ifndef GB_JOIN_MACROS -#define GB_JOIN_MACROS - #define GB_JOIN2_IND(a, b) a##b - - #define GB_JOIN2(a, b) GB_JOIN2_IND(a, b) - #define GB_JOIN3(a, b, c) GB_JOIN2(GB_JOIN2(a, b), c) - #define GB_JOIN4(a, b, c, d) GB_JOIN2(GB_JOIN2(GB_JOIN2(a, b), c), d) -#endif - - -#ifndef GB_BIT -#define GB_BIT(x) (1<<(x)) -#endif - -#ifndef gb_min -#define gb_min(a, b) ((a) < (b) ? (a) : (b)) -#endif - -#ifndef gb_max -#define gb_max(a, b) ((a) > (b) ? (a) : (b)) -#endif - -#ifndef gb_min3 -#define gb_min3(a, b, c) gb_min(gb_min(a, b), c) -#endif - -#ifndef gb_max3 -#define gb_max3(a, b, c) gb_max(gb_max(a, b), c) -#endif - -#ifndef gb_clamp -#define gb_clamp(x, lower, upper) gb_min(gb_max((x), (lower)), (upper)) -#endif - -#ifndef gb_clamp01 -#define gb_clamp01(x) gb_clamp((x), 0, 1) -#endif - -#ifndef gb_is_between -#define gb_is_between(x, lower, upper) (((lower) <= (x)) && ((x) <= (upper))) -#endif - -#ifndef gb_abs -#define gb_abs(x) ((x) < 0 ? -(x) : (x)) -#endif - -/* NOTE(bill): Very useful bit setting */ -#ifndef GB_MASK_SET -#define GB_MASK_SET(var, set, mask) do { \ - if (set) (var) |= (mask); \ - else (var) &= ~(mask); \ -} while (0) -#endif - - -// NOTE(bill): Some compilers support applying printf-style warnings to user functions. -#if defined(__clang__) || defined(__GNUC__) -#define GB_PRINTF_ARGS(FMT) __attribute__((format(printf, FMT, (FMT+1)))) -#else -#define GB_PRINTF_ARGS(FMT) -#endif - -//////////////////////////////////////////////////////////////// -// -// Debug -// -// - - -#ifndef GB_DEBUG_TRAP - #if defined(_MSC_VER) - #if _MSC_VER < 1300 - #define GB_DEBUG_TRAP() __asm int 3 /* Trap to debugger! */ - #else - #define GB_DEBUG_TRAP() __debugbreak() - #endif - #else - #define GB_DEBUG_TRAP() __builtin_trap() - #endif -#endif - -#ifndef GB_ASSERT_MSG -#define GB_ASSERT_MSG(cond, msg, ...) do { \ - if (!(cond)) { \ - gb_assert_handler("Assertion Failure", #cond, __FILE__, cast(i64)__LINE__, msg, ##__VA_ARGS__); \ - GB_DEBUG_TRAP(); \ - } \ -} while (0) -#endif - -#ifndef GB_ASSERT -#define GB_ASSERT(cond) GB_ASSERT_MSG(cond, NULL) -#endif - -#ifndef GB_ASSERT_NOT_NULL -#define GB_ASSERT_NOT_NULL(ptr) GB_ASSERT_MSG((ptr) != NULL, #ptr " must not be NULL") -#endif - -// NOTE(bill): Things that shouldn't happen with a message! -#ifndef GB_PANIC -#define GB_PANIC(msg, ...) do { \ - gb_assert_handler("Panic", NULL, __FILE__, cast(i64)__LINE__, msg, ##__VA_ARGS__); \ - GB_DEBUG_TRAP(); \ -} while (0) -#endif - -GB_DEF void gb_assert_handler(char const *prefix, char const *condition, char const *file, i32 line, char const *msg, ...); - - - -//////////////////////////////////////////////////////////////// -// -// Memory -// -// - - -GB_DEF b32 gb_is_power_of_two(isize x); - -GB_DEF void * gb_align_forward(void *ptr, isize alignment); - -GB_DEF void * gb_pointer_add (void *ptr, isize bytes); -GB_DEF void * gb_pointer_sub (void *ptr, isize bytes); -GB_DEF void const *gb_pointer_add_const(void const *ptr, isize bytes); -GB_DEF void const *gb_pointer_sub_const(void const *ptr, isize bytes); -GB_DEF isize gb_pointer_diff (void const *begin, void const *end); - - -GB_DEF void gb_zero_size(void *ptr, isize size); -#ifndef gb_zero_item -#define gb_zero_item(t) gb_zero_size((t), gb_size_of(*(t))) // NOTE(bill): Pass pointer of struct -#define gb_zero_array(a, count) gb_zero_size((a), gb_size_of(*(a))*count) -#endif - -GB_DEF void * gb_memcopy (void *dest, void const *source, isize size); -GB_DEF void * gb_memmove (void *dest, void const *source, isize size); -GB_DEF void * gb_memset (void *data, u8 byte_value, isize size); -GB_DEF i32 gb_memcompare(void const *s1, void const *s2, isize size); -GB_DEF void gb_memswap (void *i, void *j, isize size); -GB_DEF void const *gb_memchr (void const *data, u8 byte_value, isize size); -GB_DEF void const *gb_memrchr (void const *data, u8 byte_value, isize size); - - -#ifndef gb_memcopy_array -#define gb_memcopy_array(dst, src, count) gb_memcopy((dst), (src), gb_size_of(*(dst))*(count)) -#endif - -#ifndef gb_memmove_array -#define gb_memmove_array(dst, src, count) gb_memmove((dst), (src), gb_size_of(*(dst))*(count)) -#endif - -// NOTE(bill): Very similar to doing `*cast(T *)(&u)` -#ifndef GB_BIT_CAST -#define GB_BIT_CAST(dest, source) do { \ - GB_STATIC_ASSERT(gb_size_of(*(dest)) <= gb_size_of(source)); \ - gb_memcopy((dest), &(source), gb_size_of(*dest)); \ -} while (0) -#endif - - - - -#ifndef gb_kilobytes -#define gb_kilobytes(x) ( (x) * (i64)(1024)) -#define gb_megabytes(x) (gb_kilobytes(x) * (i64)(1024)) -#define gb_gigabytes(x) (gb_megabytes(x) * (i64)(1024)) -#define gb_terabytes(x) (gb_gigabytes(x) * (i64)(1024)) -#endif - - - - -// Atomics - -// TODO(bill): Be specific with memory order? -// e.g. relaxed, acquire, release, acquire_release - -#if defined(GB_COMPILER_MSVC) -typedef struct gbAtomic32 { i32 volatile value; } gbAtomic32; -typedef struct gbAtomic64 { i64 volatile value; } gbAtomic64; -typedef struct gbAtomicPtr { void *volatile value; } gbAtomicPtr; -#else - #if defined(GB_ARCH_32_BIT) - #define GB_ATOMIC_PTR_ALIGNMENT 4 - #elif defined(GB_ARCH_64_BIT) - #define GB_ATOMIC_PTR_ALIGNMENT 8 - #else - #error Unknown architecture - #endif - -typedef struct gbAtomic32 { i32 volatile value; } __attribute__ ((aligned(4))) gbAtomic32; -typedef struct gbAtomic64 { i64 volatile value; } __attribute__ ((aligned(8))) gbAtomic64; -typedef struct gbAtomicPtr { void *volatile value; } __attribute__ ((aligned(GB_ATOMIC_PTR_ALIGNMENT))) gbAtomicPtr; -#endif - -GB_DEF i32 gb_atomic32_load (gbAtomic32 const volatile *a); -GB_DEF void gb_atomic32_store (gbAtomic32 volatile *a, i32 value); -GB_DEF i32 gb_atomic32_compare_exchange(gbAtomic32 volatile *a, i32 expected, i32 desired); -GB_DEF i32 gb_atomic32_exchanged (gbAtomic32 volatile *a, i32 desired); -GB_DEF i32 gb_atomic32_fetch_add (gbAtomic32 volatile *a, i32 operand); -GB_DEF i32 gb_atomic32_fetch_and (gbAtomic32 volatile *a, i32 operand); -GB_DEF i32 gb_atomic32_fetch_or (gbAtomic32 volatile *a, i32 operand); -GB_DEF b32 gb_atomic32_spin_lock (gbAtomic32 volatile *a, isize time_out); // NOTE(bill): time_out = -1 as default -GB_DEF void gb_atomic32_spin_unlock (gbAtomic32 volatile *a); -GB_DEF b32 gb_atomic32_try_acquire_lock(gbAtomic32 volatile *a); - - -GB_DEF i64 gb_atomic64_load (gbAtomic64 const volatile *a); -GB_DEF void gb_atomic64_store (gbAtomic64 volatile *a, i64 value); -GB_DEF i64 gb_atomic64_compare_exchange(gbAtomic64 volatile *a, i64 expected, i64 desired); -GB_DEF i64 gb_atomic64_exchanged (gbAtomic64 volatile *a, i64 desired); -GB_DEF i64 gb_atomic64_fetch_add (gbAtomic64 volatile *a, i64 operand); -GB_DEF i64 gb_atomic64_fetch_and (gbAtomic64 volatile *a, i64 operand); -GB_DEF i64 gb_atomic64_fetch_or (gbAtomic64 volatile *a, i64 operand); -GB_DEF b32 gb_atomic64_spin_lock (gbAtomic64 volatile *a, isize time_out); // NOTE(bill): time_out = -1 as default -GB_DEF void gb_atomic64_spin_unlock (gbAtomic64 volatile *a); -GB_DEF b32 gb_atomic64_try_acquire_lock(gbAtomic64 volatile *a); - - -GB_DEF void *gb_atomic_ptr_load (gbAtomicPtr const volatile *a); -GB_DEF void gb_atomic_ptr_store (gbAtomicPtr volatile *a, void *value); -GB_DEF void *gb_atomic_ptr_compare_exchange(gbAtomicPtr volatile *a, void *expected, void *desired); -GB_DEF void *gb_atomic_ptr_exchanged (gbAtomicPtr volatile *a, void *desired); -GB_DEF void *gb_atomic_ptr_fetch_add (gbAtomicPtr volatile *a, void *operand); -GB_DEF void *gb_atomic_ptr_fetch_and (gbAtomicPtr volatile *a, void *operand); -GB_DEF void *gb_atomic_ptr_fetch_or (gbAtomicPtr volatile *a, void *operand); -GB_DEF b32 gb_atomic_ptr_spin_lock (gbAtomicPtr volatile *a, isize time_out); // NOTE(bill): time_out = -1 as default -GB_DEF void gb_atomic_ptr_spin_unlock (gbAtomicPtr volatile *a); -GB_DEF b32 gb_atomic_ptr_try_acquire_lock(gbAtomicPtr volatile *a); - - -// Fences -GB_DEF void gb_yield_thread(void); -GB_DEF void gb_mfence (void); -GB_DEF void gb_sfence (void); -GB_DEF void gb_lfence (void); - - -#if defined(GB_SYSTEM_WINDOWS) -typedef struct gbSemaphore { void *win32_handle; } gbSemaphore; -#elif defined(GB_SYSTEM_OSX) -typedef struct gbSemaphore { semaphore_t osx_handle; } gbSemaphore; -#elif defined(GB_SYSTEM_UNIX) -typedef struct gbSemaphore { sem_t unix_handle; } gbSemaphore; -#else -#error -#endif - -GB_DEF void gb_semaphore_init (gbSemaphore *s); -GB_DEF void gb_semaphore_destroy(gbSemaphore *s); -GB_DEF void gb_semaphore_post (gbSemaphore *s, i32 count); -GB_DEF void gb_semaphore_release(gbSemaphore *s); // NOTE(bill): gb_semaphore_post(s, 1) -GB_DEF void gb_semaphore_wait (gbSemaphore *s); - - -// Mutex -typedef struct gbMutex { -#if defined(GB_SYSTEM_WINDOWS) - CRITICAL_SECTION win32_critical_section; -#else - pthread_mutex_t pthread_mutex; - pthread_mutexattr_t pthread_mutexattr; -#endif -} gbMutex; - -GB_DEF void gb_mutex_init (gbMutex *m); -GB_DEF void gb_mutex_destroy (gbMutex *m); -GB_DEF void gb_mutex_lock (gbMutex *m); -GB_DEF b32 gb_mutex_try_lock(gbMutex *m); -GB_DEF void gb_mutex_unlock (gbMutex *m); - -// NOTE(bill): If you wanted a Scoped Mutex in C++, why not use the defer() construct? -// No need for a silly wrapper class and it's clear! -#if 0 -gbMutex m = {0}; -gb_mutex_init(&m); -{ - gb_mutex_lock(&m); - defer (gb_mutex_unlock(&m)); - - // Do whatever as the mutex is now scoped based! -} -#endif - - -typedef struct gbThread gbThread; - -#define GB_THREAD_PROC(name) isize name(gbThread *thread) -typedef GB_THREAD_PROC(gbThreadProc); - -struct gbThread { -#if defined(GB_SYSTEM_WINDOWS) - void * win32_handle; -#else - pthread_t posix_handle; -#endif - - gbThreadProc *proc; - void * user_data; - isize user_index; - isize return_value; - - gbSemaphore semaphore; - isize stack_size; - b32 volatile is_running; -}; - -GB_DEF void gb_thread_init (gbThread *t); -GB_DEF void gb_thread_destroy (gbThread *t); -GB_DEF void gb_thread_start (gbThread *t, gbThreadProc *proc, void *data); -GB_DEF void gb_thread_start_with_stack(gbThread *t, gbThreadProc *proc, void *data, isize stack_size); -GB_DEF void gb_thread_join (gbThread *t); -GB_DEF b32 gb_thread_is_running (gbThread const *t); -GB_DEF u32 gb_thread_current_id (void); -GB_DEF void gb_thread_set_name (gbThread *t, char const *name); - - -// NOTE(bill): Thread Merge Operation -// Based on Sean Barrett's stb_sync -typedef struct gbSync { - i32 target; // Target Number of threads - i32 current; // Threads to hit - i32 waiting; // Threads waiting - - gbMutex start; - gbMutex mutex; - gbSemaphore release; -} gbSync; - -GB_DEF void gb_sync_init (gbSync *s); -GB_DEF void gb_sync_destroy (gbSync *s); -GB_DEF void gb_sync_set_target (gbSync *s, i32 count); -GB_DEF void gb_sync_release (gbSync *s); -GB_DEF i32 gb_sync_reach (gbSync *s); -GB_DEF void gb_sync_reach_and_wait(gbSync *s); - - - -#if defined(GB_SYSTEM_WINDOWS) - -typedef struct gbAffinity { - b32 is_accurate; - isize core_count; - isize thread_count; - #define GB_WIN32_MAX_THREADS (8 * gb_size_of(usize)) - usize core_masks[GB_WIN32_MAX_THREADS]; - -} gbAffinity; - -#elif defined(GB_SYSTEM_OSX) -typedef struct gbAffinity { - b32 is_accurate; - isize core_count; - isize thread_count; - isize threads_per_core; -} gbAffinity; - -#elif defined(GB_SYSTEM_LINUX) -typedef struct gbAffinity { - b32 is_accurate; - isize core_count; - isize thread_count; - isize threads_per_core; -} gbAffinity; -#else -#error TODO(bill): Unknown system -#endif - -GB_DEF void gb_affinity_init (gbAffinity *a); -GB_DEF void gb_affinity_destroy(gbAffinity *a); -GB_DEF b32 gb_affinity_set (gbAffinity *a, isize core, isize thread); -GB_DEF isize gb_affinity_thread_count_for_core(gbAffinity *a, isize core); - - - - -//////////////////////////////////////////////////////////////// -// -// Virtual Memory -// -// - -typedef struct gbVirtualMemory { - void *data; - isize size; -} gbVirtualMemory; - -GB_DEF gbVirtualMemory gb_virtual_memory(void *data, isize size); -GB_DEF gbVirtualMemory gb_vm_alloc (void *addr, isize size); -GB_DEF b32 gb_vm_free (gbVirtualMemory vm); -GB_DEF gbVirtualMemory gb_vm_trim (gbVirtualMemory vm, isize lead_size, isize size); -GB_DEF b32 gb_vm_purge (gbVirtualMemory vm); -GB_DEF isize gb_virtual_memory_page_size(isize *alignment_out); - - - - -//////////////////////////////////////////////////////////////// -// -// Custom Allocation -// -// - -typedef enum gbAllocationType { - gbAllocation_Alloc, - gbAllocation_Free, - gbAllocation_FreeAll, - gbAllocation_Resize, -} gbAllocationType; - -// NOTE(bill): This is useful so you can define an allocator of the same type and parameters -#define GB_ALLOCATOR_PROC(name) \ -void *name(void *allocator_data, gbAllocationType type, \ - isize size, isize alignment, \ - void *old_memory, isize old_size, \ - u64 flags) -typedef GB_ALLOCATOR_PROC(gbAllocatorProc); - -typedef struct gbAllocator { - gbAllocatorProc *proc; - void * data; -} gbAllocator; - -typedef enum gbAllocatorFlag { - gbAllocatorFlag_ClearToZero = GB_BIT(0), -} gbAllocatorFlag; - -// TODO(bill): Is this a decent default alignment? -#ifndef GB_DEFAULT_MEMORY_ALIGNMENT -#define GB_DEFAULT_MEMORY_ALIGNMENT (2 * gb_size_of(void *)) -#endif - -#ifndef GB_DEFAULT_ALLOCATOR_FLAGS -#define GB_DEFAULT_ALLOCATOR_FLAGS (gbAllocatorFlag_ClearToZero) -#endif - -GB_DEF void *gb_alloc_align (gbAllocator a, isize size, isize alignment); -GB_DEF void *gb_alloc (gbAllocator a, isize size); -GB_DEF void gb_free (gbAllocator a, void *ptr); -GB_DEF void gb_free_all (gbAllocator a); -GB_DEF void *gb_resize (gbAllocator a, void *ptr, isize old_size, isize new_size); -GB_DEF void *gb_resize_align(gbAllocator a, void *ptr, isize old_size, isize new_size, isize alignment); -// TODO(bill): For gb_resize, should the use need to pass the old_size or only the new_size? - -GB_DEF void *gb_alloc_copy (gbAllocator a, void const *src, isize size); -GB_DEF void *gb_alloc_copy_align(gbAllocator a, void const *src, isize size, isize alignment); -GB_DEF char *gb_alloc_str (gbAllocator a, char const *str); -GB_DEF char *gb_alloc_str_len (gbAllocator a, char const *str, isize len); - - -// NOTE(bill): These are very useful and the type cast has saved me from numerous bugs -#ifndef gb_alloc_item -#define gb_alloc_item(allocator_, Type) (Type *)gb_alloc(allocator_, gb_size_of(Type)) -#define gb_alloc_array(allocator_, Type, count) (Type *)gb_alloc(allocator_, gb_size_of(Type) * (count)) -#endif - -// NOTE(bill): Use this if you don't need a "fancy" resize allocation -GB_DEF void *gb_default_resize_align(gbAllocator a, void *ptr, isize old_size, isize new_size, isize alignment); - - - -// TODO(bill): Probably use a custom heap allocator system that doesn't depend on malloc/free -// Base it off TCMalloc or something else? Or something entirely custom? -GB_DEF gbAllocator gb_heap_allocator(void); -GB_DEF GB_ALLOCATOR_PROC(gb_heap_allocator_proc); - -// NOTE(bill): Yep, I use my own allocator system! -#ifndef gb_malloc -#define gb_malloc(sz) gb_alloc(gb_heap_allocator(), sz) -#define gb_mfree(ptr) gb_free(gb_heap_allocator(), ptr) -#endif - - - -// -// Arena Allocator -// -typedef struct gbArena { - gbAllocator backing; - void * physical_start; - isize total_size; - isize total_allocated; - isize temp_count; -} gbArena; - -GB_DEF void gb_arena_init_from_memory (gbArena *arena, void *start, isize size); -GB_DEF void gb_arena_init_from_allocator(gbArena *arena, gbAllocator backing, isize size); -GB_DEF void gb_arena_init_sub (gbArena *arena, gbArena *parent_arena, isize size); -GB_DEF void gb_arena_free (gbArena *arena); - -GB_DEF isize gb_arena_alignment_of (gbArena *arena, isize alignment); -GB_DEF isize gb_arena_size_remaining(gbArena *arena, isize alignment); -GB_DEF void gb_arena_check (gbArena *arena); - - -// Allocation Types: alloc, free_all, resize -GB_DEF gbAllocator gb_arena_allocator(gbArena *arena); -GB_DEF GB_ALLOCATOR_PROC(gb_arena_allocator_proc); - - - -typedef struct gbTempArenaMemory { - gbArena *arena; - isize original_count; -} gbTempArenaMemory; - -GB_DEF gbTempArenaMemory gb_temp_arena_memory_begin(gbArena *arena); -GB_DEF void gb_temp_arena_memory_end (gbTempArenaMemory tmp_mem); - - - - - - - -// -// Pool Allocator -// - - -typedef struct gbPool { - gbAllocator backing; - void * physical_start; - void * free_list; - isize block_size; - isize block_align; - isize total_size; -} gbPool; - -GB_DEF void gb_pool_init (gbPool *pool, gbAllocator backing, isize num_blocks, isize block_size); -GB_DEF void gb_pool_init_align(gbPool *pool, gbAllocator backing, isize num_blocks, isize block_size, isize block_align); -GB_DEF void gb_pool_free (gbPool *pool); - -// Allocation Types: alloc, free -GB_DEF gbAllocator gb_pool_allocator(gbPool *pool); -GB_DEF GB_ALLOCATOR_PROC(gb_pool_allocator_proc); - - - -// NOTE(bill): Used for allocators to keep track of sizes -typedef struct gbAllocationHeader { - isize size; -} gbAllocationHeader; - -GB_DEF gbAllocationHeader *gb_allocation_header (void *data); -GB_DEF void gb_allocation_header_fill(gbAllocationHeader *header, void *data, isize size); - -// TODO(bill): Find better way of doing this without #if #elif etc. -#if defined(GB_ARCH_32_BIT) -#define GB_ISIZE_HIGH_BIT 0x80000000 -#elif defined(GB_ARCH_64_BIT) -#define GB_ISIZE_HIGH_BIT 0x8000000000000000ll -#else -#error -#endif - -// -// Free List Allocator -// - -// IMPORTANT TODO(bill): Thoroughly test the free list allocator! -// NOTE(bill): This is a very shitty free list as it just picks the first free block not the best size -// as I am just being lazy. Also, I will probably remove it later; it's only here because why not?! -// -// NOTE(bill): I may also complete remove this if I completely implement a fixed heap allocator - -typedef struct gbFreeListBlock gbFreeListBlock; -struct gbFreeListBlock { - gbFreeListBlock *next; - isize size; -}; - -typedef struct gbFreeList { - void * physical_start; - isize total_size; - - gbFreeListBlock *curr_block; - - isize total_allocated; - isize allocation_count; -} gbFreeList; - -GB_DEF void gb_free_list_init (gbFreeList *fl, void *start, isize size); -GB_DEF void gb_free_list_init_from_allocator(gbFreeList *fl, gbAllocator backing, isize size); - -// Allocation Types: alloc, free, free_all, resize -GB_DEF gbAllocator gb_free_list_allocator(gbFreeList *fl); -GB_DEF GB_ALLOCATOR_PROC(gb_free_list_allocator_proc); - - - -// -// Scratch Memory Allocator - Ring Buffer Based Arena -// - -typedef struct gbScratchMemory { - void *physical_start; - isize total_size; - void *alloc_point; - void *free_point; -} gbScratchMemory; - -GB_DEF void gb_scratch_memory_init (gbScratchMemory *s, void *start, isize size); -GB_DEF b32 gb_scratch_memory_is_in_use(gbScratchMemory *s, void *ptr); - - -// Allocation Types: alloc, free, free_all, resize -GB_DEF gbAllocator gb_scratch_allocator(gbScratchMemory *s); -GB_DEF GB_ALLOCATOR_PROC(gb_scratch_allocator_proc); - -// TODO(bill): Stack allocator -// TODO(bill): Fixed heap allocator -// TODO(bill): General heap allocator. Maybe a TCMalloc like clone? - - -//////////////////////////////////////////////////////////////// -// -// Sort & Search -// -// - -#define GB_COMPARE_PROC(name) int name(void const *a, void const *b) -typedef GB_COMPARE_PROC(gbCompareProc); - -#define GB_COMPARE_PROC_PTR(def) GB_COMPARE_PROC((*def)) - -// Producure pointers -// NOTE(bill): The offset parameter specifies the offset in the structure -// e.g. gb_i32_cmp(gb_offset_of(Thing, value)) -// Use 0 if it's just the type instead. - -GB_DEF GB_COMPARE_PROC_PTR(gb_i16_cmp (isize offset)); -GB_DEF GB_COMPARE_PROC_PTR(gb_i32_cmp (isize offset)); -GB_DEF GB_COMPARE_PROC_PTR(gb_i64_cmp (isize offset)); -GB_DEF GB_COMPARE_PROC_PTR(gb_isize_cmp(isize offset)); -GB_DEF GB_COMPARE_PROC_PTR(gb_str_cmp (isize offset)); -GB_DEF GB_COMPARE_PROC_PTR(gb_f32_cmp (isize offset)); -GB_DEF GB_COMPARE_PROC_PTR(gb_f64_cmp (isize offset)); -GB_DEF GB_COMPARE_PROC_PTR(gb_char_cmp (isize offset)); - -// TODO(bill): Better sorting algorithms -// NOTE(bill): Uses quick sort for large arrays but insertion sort for small -#define gb_sort_array(array, count, compare_proc) gb_sort(array, count, gb_size_of(*(array)), compare_proc) -GB_DEF void gb_sort(void *base, isize count, isize size, gbCompareProc compare_proc); - -// NOTE(bill): the count of temp == count of items -#define gb_radix_sort(Type) gb_radix_sort_##Type -#define GB_RADIX_SORT_PROC(Type) void gb_radix_sort(Type)(Type *items, Type *temp, isize count) - -GB_DEF GB_RADIX_SORT_PROC(u8); -GB_DEF GB_RADIX_SORT_PROC(u16); -GB_DEF GB_RADIX_SORT_PROC(u32); -GB_DEF GB_RADIX_SORT_PROC(u64); - - -// NOTE(bill): Returns index or -1 if not found -#define gb_binary_search_array(array, count, key, compare_proc) gb_binary_search(array, count, gb_size_of(*(array)), key, compare_proc) -GB_DEF isize gb_binary_search(void const *base, isize count, isize size, void const *key, gbCompareProc compare_proc); - -#define gb_shuffle_array(array, count) gb_shuffle(array, count, gb_size_of(*(array))) -GB_DEF void gb_shuffle(void *base, isize count, isize size); - -#define gb_reverse_array(array, count) gb_reverse(array, count, gb_size_of(*(array))) -GB_DEF void gb_reverse(void *base, isize count, isize size); - -//////////////////////////////////////////////////////////////// -// -// Char Functions -// -// - -GB_DEF char gb_char_to_lower (char c); -GB_DEF char gb_char_to_upper (char c); -GB_DEF b32 gb_char_is_space (char c); -GB_DEF b32 gb_char_is_digit (char c); -GB_DEF b32 gb_char_is_hex_digit (char c); -GB_DEF b32 gb_char_is_alpha (char c); -GB_DEF b32 gb_char_is_alphanumeric(char c); -GB_DEF i32 gb_digit_to_int (char c); -GB_DEF i32 gb_hex_digit_to_int (char c); - -// NOTE(bill): ASCII only -GB_DEF void gb_str_to_lower(char *str); -GB_DEF void gb_str_to_upper(char *str); - -GB_DEF isize gb_strlen (char const *str); -GB_DEF isize gb_strnlen(char const *str, isize max_len); -GB_DEF i32 gb_strcmp (char const *s1, char const *s2); -GB_DEF i32 gb_strncmp(char const *s1, char const *s2, isize len); -GB_DEF char *gb_strcpy (char *dest, char const *source); -GB_DEF char *gb_strncpy(char *dest, char const *source, isize len); -GB_DEF isize gb_strlcpy(char *dest, char const *source, isize len); -GB_DEF char *gb_strrev (char *str); // NOTE(bill): ASCII only - -// NOTE(bill): A less fucking crazy strtok! -GB_DEF char const *gb_strtok(char *output, char const *src, char const *delimit); - -GB_DEF b32 gb_str_has_prefix(char const *str, char const *prefix); -GB_DEF b32 gb_str_has_suffix(char const *str, char const *suffix); - -GB_DEF char const *gb_char_first_occurence(char const *str, char c); -GB_DEF char const *gb_char_last_occurence (char const *str, char c); - -GB_DEF void gb_str_concat(char *dest, isize dest_len, - char const *src_a, isize src_a_len, - char const *src_b, isize src_b_len); - -GB_DEF u64 gb_str_to_u64(char const *str, char **end_ptr, i32 base); // TODO(bill): Support more than just decimal and hexadecimal -GB_DEF i64 gb_str_to_i64(char const *str, char **end_ptr, i32 base); // TODO(bill): Support more than just decimal and hexadecimal -GB_DEF f32 gb_str_to_f32(char const *str, char **end_ptr); -GB_DEF f64 gb_str_to_f64(char const *str, char **end_ptr); -GB_DEF void gb_i64_to_str(i64 value, char *string, i32 base); -GB_DEF void gb_u64_to_str(u64 value, char *string, i32 base); - - -//////////////////////////////////////////////////////////////// -// -// UTF-8 Handling -// -// - -// NOTE(bill): Does not check if utf-8 string is valid -GB_DEF isize gb_utf8_strlen (u8 const *str); -GB_DEF isize gb_utf8_strnlen(u8 const *str, isize max_len); - -// NOTE(bill): Windows doesn't handle 8 bit filenames well ('cause Micro$hit) -GB_DEF u16 *gb_utf8_to_ucs2 (u16 *buffer, isize len, u8 const *str); -GB_DEF u8 * gb_ucs2_to_utf8 (u8 *buffer, isize len, u16 const *str); -GB_DEF u16 *gb_utf8_to_ucs2_buf(u8 const *str); // NOTE(bill): Uses locally persisting buffer -GB_DEF u8 * gb_ucs2_to_utf8_buf(u16 const *str); // NOTE(bill): Uses locally persisting buffer - -// NOTE(bill): Returns size of codepoint in bytes -GB_DEF isize gb_utf8_decode (u8 const *str, isize str_len, Rune *codepoint); -GB_DEF isize gb_utf8_codepoint_size(u8 const *str, isize str_len); -GB_DEF isize gb_utf8_encode_rune (u8 buf[4], Rune r); - -//////////////////////////////////////////////////////////////// -// -// gbString - C Read-Only-Compatible -// -// -/* -Reasoning: - - By default, strings in C are null terminated which means you have to count - the number of character up to the null character to calculate the length. - Many "better" C string libraries will create a struct for a string. - i.e. - - struct String { - Allocator allocator; - size_t length; - size_t capacity; - char * cstring; - }; - - This library tries to augment normal C strings in a better way that is still - compatible with C-style strings. - - +--------+-----------------------+-----------------+ - | Header | Binary C-style String | Null Terminator | - +--------+-----------------------+-----------------+ - | - +-> Pointer returned by functions - - Due to the meta-data being stored before the string pointer and every gb string - having an implicit null terminator, gb strings are full compatible with c-style - strings and read-only functions. - -Advantages: - - * gb strings can be passed to C-style string functions without accessing a struct - member of calling a function, i.e. - - gb_printf("%s\n", gb_str); - - Many other libraries do either of these: - - gb_printf("%s\n", string->cstr); - gb_printf("%s\n", get_cstring(string)); - - * You can access each character just like a C-style string: - - gb_printf("%c %c\n", str[0], str[13]); - - * gb strings are singularly allocated. The meta-data is next to the character - array which is better for the cache. - -Disadvantages: - - * In the C version of these functions, many return the new string. i.e. - str = gb_string_appendc(str, "another string"); - This could be changed to gb_string_appendc(&str, "another string"); but I'm still not sure. - - * This is incompatible with "gb_string.h" strings -*/ - -#if 0 -#define GB_IMPLEMENTATION -#include "gb.h" -int main(int argc, char **argv) { - gbString str = gb_string_make("Hello"); - gbString other_str = gb_string_make_length(", ", 2); - str = gb_string_append(str, other_str); - str = gb_string_appendc(str, "world!"); - - gb_printf("%s\n", str); // Hello, world! - - gb_printf("str length = %d\n", gb_string_length(str)); - - str = gb_string_set(str, "Potato soup"); - gb_printf("%s\n", str); // Potato soup - - str = gb_string_set(str, "Hello"); - other_str = gb_string_set(other_str, "Pizza"); - if (gb_strings_are_equal(str, other_str)) - gb_printf("Not called\n"); - else - gb_printf("Called\n"); - - str = gb_string_set(str, "Ab.;!...AHello World ??"); - str = gb_string_trim(str, "Ab.;!. ?"); - gb_printf("%s\n", str); // "Hello World" - - gb_string_free(str); - gb_string_free(other_str); - - return 0; -} -#endif - -// TODO(bill): Should this be a wrapper to gbArray(char) or this extra type safety better? -typedef char *gbString; - -// NOTE(bill): If you only need a small string, just use a standard c string or change the size from isize to u16, etc. -typedef struct gbStringHeader { - gbAllocator allocator; - isize length; - isize capacity; -} gbStringHeader; - -#define GB_STRING_HEADER(str) (cast(gbStringHeader *)(str) - 1) - -GB_DEF gbString gb_string_make_reserve (gbAllocator a, isize capacity); -GB_DEF gbString gb_string_make (gbAllocator a, char const *str); -GB_DEF gbString gb_string_make_length (gbAllocator a, void const *str, isize num_bytes); -GB_DEF void gb_string_free (gbString str); -GB_DEF gbString gb_string_duplicate (gbAllocator a, gbString const str); -GB_DEF isize gb_string_length (gbString const str); -GB_DEF isize gb_string_capacity (gbString const str); -GB_DEF isize gb_string_available_space(gbString const str); -GB_DEF void gb_string_clear (gbString str); -GB_DEF gbString gb_string_append (gbString str, gbString const other); -GB_DEF gbString gb_string_append_length (gbString str, void const *other, isize num_bytes); -GB_DEF gbString gb_string_appendc (gbString str, char const *other); -GB_DEF gbString gb_string_append_rune (gbString str, Rune r); -GB_DEF gbString gb_string_append_fmt (gbString str, char const *fmt, ...); -GB_DEF gbString gb_string_set (gbString str, char const *cstr); -GB_DEF gbString gb_string_make_space_for (gbString str, isize add_len); -GB_DEF isize gb_string_allocation_size(gbString const str); -GB_DEF b32 gb_string_are_equal (gbString const lhs, gbString const rhs); -GB_DEF gbString gb_string_trim (gbString str, char const *cut_set); -GB_DEF gbString gb_string_trim_space (gbString str); // Whitespace ` \t\r\n\v\f` - - - -//////////////////////////////////////////////////////////////// -// -// Fixed Capacity Buffer (POD Types) -// -// -// gbBuffer(Type) works like gbString or gbArray where the actual type is just a pointer to the first -// element. -// - -typedef struct gbBufferHeader { - isize count; - isize capacity; -} gbBufferHeader; - -#define gbBuffer(Type) Type * - -#define GB_BUFFER_HEADER(x) (cast(gbBufferHeader *)(x) - 1) -#define gb_buffer_count(x) (GB_BUFFER_HEADER(x)->count) -#define gb_buffer_capacity(x) (GB_BUFFER_HEADER(x)->capacity) - -#define gb_buffer_init(x, allocator, cap) do { \ - void **nx = cast(void **)&(x); \ - gbBufferHeader *gb__bh = cast(gbBufferHeader *)gb_alloc((allocator), (cap)*gb_size_of(*(x))); \ - gb__bh->count = 0; \ - gb__bh->capacity = cap; \ - *nx = cast(void *)(gb__bh+1); \ -} while (0) - - -#define gb_buffer_free(x, allocator) (gb_free(allocator, GB_BUFFER_HEADER(x))) - -#define gb_buffer_append(x, item) do { (x)[gb_buffer_count(x)++] = (item); } while (0) - -#define gb_buffer_appendv(x, items, item_count) do { \ - GB_ASSERT(gb_size_of(*(items)) == gb_size_of(*(x))); \ - GB_ASSERT(gb_buffer_count(x)+item_count <= gb_buffer_capacity(x)); \ - gb_memcopy(&(x)[gb_buffer_count(x)], (items), gb_size_of(*(x))*(item_count)); \ - gb_buffer_count(x) += (item_count); \ -} while (0) - -#define gb_buffer_pop(x) do { GB_ASSERT(gb_buffer_count(x) > 0); gb_buffer_count(x)--; } while (0) -#define gb_buffer_clear(x) do { gb_buffer_count(x) = 0; } while (0) - - - -//////////////////////////////////////////////////////////////// -// -// Dynamic Array (POD Types) -// -// NOTE(bill): I know this is a macro hell but C is an old (and shit) language with no proper arrays -// Also why the fuck not?! It fucking works! And it has custom allocation, which is already better than C++! -// -// gbArray(Type) works like gbString or gbBuffer where the actual type is just a pointer to the first -// element. -// - - - -// Available Procedures for gbArray(Type) -// gb_array_init -// gb_array_free -// gb_array_set_capacity -// gb_array_grow -// gb_array_append -// gb_array_appendv -// gb_array_pop -// gb_array_clear -// gb_array_resize -// gb_array_reserve -// - -#if 0 // Example -void foo(void) { - isize i; - int test_values[] = {4, 2, 1, 7}; - gbAllocator a = gb_heap_allocator(); - gbArray(int) items; - - gb_array_init(items, a); - - gb_array_append(items, 1); - gb_array_append(items, 4); - gb_array_append(items, 9); - gb_array_append(items, 16); - - items[1] = 3; // Manually set value - // NOTE: No array bounds checking - - for (i = 0; i < items.count; i++) - gb_printf("%d\n", items[i]); - // 1 - // 3 - // 9 - // 16 - - gb_array_clear(items); - - gb_array_appendv(items, test_values, gb_count_of(test_values)); - for (i = 0; i < items.count; i++) - gb_printf("%d\n", items[i]); - // 4 - // 2 - // 1 - // 7 - - gb_array_free(items); -} -#endif - -typedef struct gbArrayHeader { - gbAllocator allocator; - isize count; - isize capacity; -} gbArrayHeader; - -// NOTE(bill): This thing is magic! -#define gbArray(Type) Type * - -#ifndef GB_ARRAY_GROW_FORMULA -#define GB_ARRAY_GROW_FORMULA(x) (2*(x) + 8) -#endif - -GB_STATIC_ASSERT(GB_ARRAY_GROW_FORMULA(0) > 0); - -#define GB_ARRAY_HEADER(x) (cast(gbArrayHeader *)(x) - 1) -#define gb_array_allocator(x) (GB_ARRAY_HEADER(x)->allocator) -#define gb_array_count(x) (GB_ARRAY_HEADER(x)->count) -#define gb_array_capacity(x) (GB_ARRAY_HEADER(x)->capacity) - -// TODO(bill): Have proper alignment! -#define gb_array_init_reserve(x, allocator_, cap) do { \ - void **gb__array_ = cast(void **)&(x); \ - gbArrayHeader *gb__ah = cast(gbArrayHeader *)gb_alloc(allocator_, gb_size_of(gbArrayHeader)+gb_size_of(*(x))*(cap)); \ - gb__ah->allocator = allocator_; \ - gb__ah->count = 0; \ - gb__ah->capacity = cap; \ - *gb__array_ = cast(void *)(gb__ah+1); \ -} while (0) - -// NOTE(bill): Give it an initial default capacity -#define gb_array_init(x, allocator) gb_array_init_reserve(x, allocator, GB_ARRAY_GROW_FORMULA(0)) - -#define gb_array_free(x) do { \ - gbArrayHeader *gb__ah = GB_ARRAY_HEADER(x); \ - gb_free(gb__ah->allocator, gb__ah); \ -} while (0) - -#define gb_array_set_capacity(x, capacity) do { \ - if (x) { \ - void **gb__array_ = cast(void **)&(x); \ - *gb__array_ = gb__array_set_capacity((x), (capacity), gb_size_of(*(x))); \ - } \ -} while (0) - -// NOTE(bill): Do not use the thing below directly, use the macro -GB_DEF void *gb__array_set_capacity(void *array, isize capacity, isize element_size); - - -// TODO(bill): Decide on a decent growing formula for gbArray -#define gb_array_grow(x, min_capacity) do { \ - isize new_capacity = GB_ARRAY_GROW_FORMULA(gb_array_capacity(x)); \ - if (new_capacity < (min_capacity)) \ - new_capacity = (min_capacity); \ - gb_array_set_capacity(x, new_capacity); \ -} while (0) - - -#define gb_array_append(x, item) do { \ - if (gb_array_capacity(x) < gb_array_count(x)+1) \ - gb_array_grow(x, 0); \ - (x)[gb_array_count(x)++] = (item); \ -} while (0) - -#define gb_array_appendv(x, items, item_count) do { \ - gbArrayHeader *gb__ah = GB_ARRAY_HEADER(x); \ - GB_ASSERT(gb_size_of((items)[0]) == gb_size_of((x)[0])); \ - if (gb__ah->capacity < gb__ah->count+(item_count)) \ - gb_array_grow(x, gb__ah->count+(item_count)); \ - gb_memcopy(&(x)[gb__ah->count], (items), gb_size_of((x)[0])*(item_count));\ - gb__ah->count += (item_count); \ -} while (0) - - - -#define gb_array_pop(x) do { GB_ASSERT(GB_ARRAY_HEADER(x)->count > 0); GB_ARRAY_HEADER(x)->count--; } while (0) -#define gb_array_clear(x) do { GB_ARRAY_HEADER(x)->count = 0; } while (0) - -#define gb_array_resize(x, new_count) do { \ - if (GB_ARRAY_HEADER(x)->capacity < (new_count)) \ - gb_array_grow(x, (new_count)); \ - GB_ARRAY_HEADER(x)->count = (new_count); \ -} while (0) - - -#define gb_array_reserve(x, new_capacity) do { \ - if (GB_ARRAY_HEADER(x)->capacity < (new_capacity)) \ - gb_array_set_capacity(x, new_capacity); \ -} while (0) - - - - - -//////////////////////////////////////////////////////////////// -// -// Hashing and Checksum Functions -// -// - -GB_EXTERN u32 gb_adler32(void const *data, isize len); - -GB_EXTERN u32 gb_crc32(void const *data, isize len); -GB_EXTERN u64 gb_crc64(void const *data, isize len); - -GB_EXTERN u32 gb_fnv32 (void const *data, isize len); -GB_EXTERN u64 gb_fnv64 (void const *data, isize len); -GB_EXTERN u32 gb_fnv32a(void const *data, isize len); -GB_EXTERN u64 gb_fnv64a(void const *data, isize len); - -// NOTE(bill): Default seed of 0x9747b28c -// NOTE(bill): I prefer using murmur64 for most hashes -GB_EXTERN u32 gb_murmur32(void const *data, isize len); -GB_EXTERN u64 gb_murmur64(void const *data, isize len); - -GB_EXTERN u32 gb_murmur32_seed(void const *data, isize len, u32 seed); -GB_EXTERN u64 gb_murmur64_seed(void const *data, isize len, u64 seed); - - -//////////////////////////////////////////////////////////////// -// -// Instantiated Hash Table -// -// This is an attempt to implement a templated hash table -// NOTE(bill): The key is aways a u64 for simplicity and you will _probably_ _never_ need anything bigger. -// -// Hash table type and function declaration, call: GB_TABLE_DECLARE(PREFIX, NAME, N, VALUE) -// Hash table function definitions, call: GB_TABLE_DEFINE(NAME, N, VALUE) -// -// PREFIX - a prefix for function prototypes e.g. extern, static, etc. -// NAME - Name of the Hash Table -// FUNC - the name will prefix function names -// VALUE - the type of the value to be stored -// -// NOTE(bill): I really wish C had decent metaprogramming capabilities (and no I don't mean C++'s templates either) -// - -typedef struct gbHashTableFindResult { - isize hash_index; - isize entry_prev; - isize entry_index; -} gbHashTableFindResult; - -#define GB_TABLE(PREFIX, NAME, FUNC, VALUE) \ - GB_TABLE_DECLARE(PREFIX, NAME, FUNC, VALUE); \ - GB_TABLE_DEFINE(NAME, FUNC, VALUE); - -#define GB_TABLE_DECLARE(PREFIX, NAME, FUNC, VALUE) \ -typedef struct GB_JOIN2(NAME,Entry) { \ - u64 key; \ - isize next; \ - VALUE value; \ -} GB_JOIN2(NAME,Entry); \ -\ -typedef struct NAME { \ - gbArray(isize) hashes; \ - gbArray(GB_JOIN2(NAME,Entry)) entries; \ -} NAME; \ -\ -PREFIX void GB_JOIN2(FUNC,init) (NAME *h, gbAllocator a); \ -PREFIX void GB_JOIN2(FUNC,destroy) (NAME *h); \ -PREFIX VALUE * GB_JOIN2(FUNC,get) (NAME *h, u64 key); \ -PREFIX void GB_JOIN2(FUNC,set) (NAME *h, u64 key, VALUE value); \ -PREFIX void GB_JOIN2(FUNC,grow) (NAME *h); \ -PREFIX void GB_JOIN2(FUNC,rehash) (NAME *h, isize new_count); \ - - - - - -#define GB_TABLE_DEFINE(NAME, FUNC, VALUE) \ -void GB_JOIN2(FUNC,init)(NAME *h, gbAllocator a) { \ - gb_array_init(h->hashes, a); \ - gb_array_init(h->entries, a); \ -} \ -\ -void GB_JOIN2(FUNC,destroy)(NAME *h) { \ - if (h->entries) gb_array_free(h->entries); \ - if (h->hashes) gb_array_free(h->hashes); \ -} \ -\ -gb_internal isize GB_JOIN2(FUNC,_add_entry)(NAME *h, u64 key) { \ - isize index; \ - GB_JOIN2(NAME,Entry) e = {0}; \ - e.key = key; \ - e.next = -1; \ - index = gb_array_count(h->entries); \ - gb_array_append(h->entries, e); \ - return index; \ -} \ -\ -gb_internal gbHashTableFindResult GB_JOIN2(FUNC,_find)(NAME *h, u64 key) { \ - gbHashTableFindResult r = {-1, -1, -1}; \ - if (gb_array_count(h->hashes) > 0) { \ - r.hash_index = key % gb_array_count(h->hashes); \ - r.entry_index = h->hashes[r.hash_index]; \ - while (r.entry_index >= 0) { \ - if (h->entries[r.entry_index].key == key) \ - return r; \ - r.entry_prev = r.entry_index; \ - r.entry_index = h->entries[r.entry_index].next; \ - } \ - } \ - return r; \ -} \ -\ -gb_internal b32 GB_JOIN2(FUNC,_full)(NAME *h) { \ - return 0.75f * gb_array_count(h->hashes) < gb_array_count(h->entries); \ -} \ -\ -void GB_JOIN2(FUNC,grow)(NAME *h) { \ - isize new_count = GB_ARRAY_GROW_FORMULA(gb_array_count(h->entries)); \ - GB_JOIN2(FUNC,rehash)(h, new_count); \ -} \ -\ -void GB_JOIN2(FUNC,rehash)(NAME *h, isize new_count) { \ - isize i, j; \ - NAME nh = {0}; \ - GB_JOIN2(FUNC,init)(&nh, gb_array_allocator(h->hashes)); \ - gb_array_resize(nh.hashes, new_count); \ - gb_array_reserve(nh.entries, gb_array_count(h->entries)); \ - for (i = 0; i < new_count; i++) \ - nh.hashes[i] = -1; \ - for (i = 0; i < gb_array_count(h->entries); i++) { \ - GB_JOIN2(NAME,Entry) *e; \ - gbHashTableFindResult fr; \ - if (gb_array_count(nh.hashes) == 0) \ - GB_JOIN2(FUNC,grow)(&nh); \ - e = &h->entries[i]; \ - fr = GB_JOIN2(FUNC,_find)(&nh, e->key); \ - j = GB_JOIN2(FUNC,_add_entry)(&nh, e->key); \ - if (fr.entry_prev < 0) \ - nh.hashes[fr.hash_index] = j; \ - else \ - nh.entries[fr.entry_prev].next = j; \ - nh.entries[j].next = fr.entry_index; \ - nh.entries[j].value = e->value; \ - if (GB_JOIN2(FUNC,_full)(&nh)) \ - GB_JOIN2(FUNC,grow)(&nh); \ - } \ - GB_JOIN2(FUNC,destroy)(h); \ - h->hashes = nh.hashes; \ - h->entries = nh.entries; \ -} \ -\ -VALUE *GB_JOIN2(FUNC,get)(NAME *h, u64 key) { \ - isize index = GB_JOIN2(FUNC,_find)(h, key).entry_index; \ - if (index >= 0) \ - return &h->entries[index].value; \ - return NULL; \ -} \ -\ -void GB_JOIN2(FUNC,set)(NAME *h, u64 key, VALUE value) { \ - isize index; \ - gbHashTableFindResult fr; \ - if (gb_array_count(h->hashes) == 0) \ - GB_JOIN2(FUNC,grow)(h); \ - fr = GB_JOIN2(FUNC,_find)(h, key); \ - if (fr.entry_index >= 0) { \ - index = fr.entry_index; \ - } else { \ - index = GB_JOIN2(FUNC,_add_entry)(h, key); \ - if (fr.entry_prev >= 0) { \ - h->entries[fr.entry_prev].next = index; \ - } else { \ - h->hashes[fr.hash_index] = index; \ - } \ - } \ - h->entries[index].value = value; \ - if (GB_JOIN2(FUNC,_full)(h)) \ - GB_JOIN2(FUNC,grow)(h); \ -} \ - - - - -//////////////////////////////////////////////////////////////// -// -// File Handling -// - - -typedef u32 gbFileMode; -typedef enum gbFileModeFlag { - gbFileMode_Read = GB_BIT(0), - gbFileMode_Write = GB_BIT(1), - gbFileMode_Append = GB_BIT(2), - gbFileMode_Rw = GB_BIT(3), - - gbFileMode_Modes = gbFileMode_Read | gbFileMode_Write | gbFileMode_Append | gbFileMode_Rw, -} gbFileModeFlag; - -// NOTE(bill): Only used internally and for the file operations -typedef enum gbSeekWhenceType { - gbSeekWhence_Begin = 0, - gbSeekWhence_Current = 1, - gbSeekWhence_End = 2, -} gbSeekWhenceType; - -typedef enum gbFileError { - gbFileError_None, - gbFileError_Invalid, - gbFileError_InvalidFilename, - gbFileError_Exists, - gbFileError_NotExists, - gbFileError_Permission, - gbFileError_TruncationFailure, -} gbFileError; - -typedef union gbFileDescriptor { - void * p; - intptr i; - uintptr u; -} gbFileDescriptor; - -typedef struct gbFileOperations gbFileOperations; - -#define GB_FILE_OPEN_PROC(name) gbFileError name(gbFileDescriptor *fd, gbFileOperations *ops, gbFileMode mode, char const *filename) -#define GB_FILE_READ_AT_PROC(name) b32 name(gbFileDescriptor fd, void *buffer, isize size, i64 offset, isize *bytes_read) -#define GB_FILE_WRITE_AT_PROC(name) b32 name(gbFileDescriptor fd, void const *buffer, isize size, i64 offset, isize *bytes_written) -#define GB_FILE_SEEK_PROC(name) b32 name(gbFileDescriptor fd, i64 offset, gbSeekWhenceType whence, i64 *new_offset) -#define GB_FILE_CLOSE_PROC(name) void name(gbFileDescriptor fd) -typedef GB_FILE_OPEN_PROC(gbFileOpenProc); -typedef GB_FILE_READ_AT_PROC(gbFileReadProc); -typedef GB_FILE_WRITE_AT_PROC(gbFileWriteProc); -typedef GB_FILE_SEEK_PROC(gbFileSeekProc); -typedef GB_FILE_CLOSE_PROC(gbFileCloseProc); - -struct gbFileOperations { - gbFileReadProc *read_at; - gbFileWriteProc *write_at; - gbFileSeekProc *seek; - gbFileCloseProc *close; -}; - -extern gbFileOperations const gbDefaultFileOperations; - - -// typedef struct gbDirInfo { -// u8 *buf; -// isize buf_count; -// isize buf_pos; -// } gbDirInfo; - -typedef u64 gbFileTime; - -typedef struct gbFile { - gbFileOperations ops; - gbFileDescriptor fd; - char const * filename; - gbFileTime last_write_time; - // gbDirInfo * dir_info; // TODO(bill): Get directory info -} gbFile; - -// TODO(bill): gbAsyncFile - -typedef enum gbFileStandardType { - gbFileStandard_Input, - gbFileStandard_Output, - gbFileStandard_Error, - - gbFileStandard_Count, -} gbFileStandardType; - -GB_DEF gbFile *const gb_file_get_standard(gbFileStandardType std); - -GB_DEF gbFileError gb_file_create (gbFile *file, char const *filename); -GB_DEF gbFileError gb_file_open (gbFile *file, char const *filename); -GB_DEF gbFileError gb_file_open_mode (gbFile *file, gbFileMode mode, char const *filename); -GB_DEF gbFileError gb_file_new (gbFile *file, gbFileDescriptor fd, gbFileOperations ops, char const *filename); -GB_DEF b32 gb_file_read_at_check (gbFile *file, void *buffer, isize size, i64 offset, isize *bytes_read); -GB_DEF b32 gb_file_write_at_check(gbFile *file, void const *buffer, isize size, i64 offset, isize *bytes_written); -GB_DEF b32 gb_file_read_at (gbFile *file, void *buffer, isize size, i64 offset); -GB_DEF b32 gb_file_write_at (gbFile *file, void const *buffer, isize size, i64 offset); -GB_DEF i64 gb_file_seek (gbFile *file, i64 offset); -GB_DEF i64 gb_file_seek_to_end (gbFile *file); -GB_DEF i64 gb_file_skip (gbFile *file, i64 bytes); // NOTE(bill): Skips a certain amount of bytes -GB_DEF i64 gb_file_tell (gbFile *file); -GB_DEF gbFileError gb_file_close (gbFile *file); -GB_DEF b32 gb_file_read (gbFile *file, void *buffer, isize size); -GB_DEF b32 gb_file_write (gbFile *file, void const *buffer, isize size); -GB_DEF i64 gb_file_size (gbFile *file); -GB_DEF char const *gb_file_name (gbFile *file); -GB_DEF gbFileError gb_file_truncate (gbFile *file, i64 size); -GB_DEF b32 gb_file_has_changed (gbFile *file); // NOTE(bill): Changed since lasted checked -// TODO(bill): -// gbFileError gb_file_temp(gbFile *file); -// - -typedef struct gbFileContents { - gbAllocator allocator; - void * data; - isize size; -} gbFileContents; - - -GB_DEF gbFileContents gb_file_read_contents(gbAllocator a, b32 zero_terminate, char const *filepath); -GB_DEF void gb_file_free_contents(gbFileContents *fc); - - -// TODO(bill): Should these have different na,es as they do not take in a gbFile * ??? -GB_DEF b32 gb_file_exists (char const *filepath); -GB_DEF gbFileTime gb_file_last_write_time(char const *filepath); -GB_DEF b32 gb_file_copy (char const *existing_filename, char const *new_filename, b32 fail_if_exists); -GB_DEF b32 gb_file_move (char const *existing_filename, char const *new_filename); -GB_DEF b32 gb_file_remove (char const *filename); - - -#ifndef GB_PATH_SEPARATOR - #if defined(GB_SYSTEM_WINDOWS) - #define GB_PATH_SEPARATOR '\\' - #else - #define GB_PATH_SEPARATOR '/' - #endif -#endif - -GB_DEF b32 gb_path_is_absolute (char const *path); -GB_DEF b32 gb_path_is_relative (char const *path); -GB_DEF b32 gb_path_is_root (char const *path); -GB_DEF char const *gb_path_base_name (char const *path); -GB_DEF char const *gb_path_extension (char const *path); -GB_DEF char * gb_path_get_full_name(gbAllocator a, char const *path); - - -//////////////////////////////////////////////////////////////// -// -// Printing -// -// - -GB_DEF isize gb_printf (char const *fmt, ...) GB_PRINTF_ARGS(1); -GB_DEF isize gb_printf_va (char const *fmt, va_list va); -GB_DEF isize gb_printf_err (char const *fmt, ...) GB_PRINTF_ARGS(1); -GB_DEF isize gb_printf_err_va (char const *fmt, va_list va); -GB_DEF isize gb_fprintf (gbFile *f, char const *fmt, ...) GB_PRINTF_ARGS(2); -GB_DEF isize gb_fprintf_va (gbFile *f, char const *fmt, va_list va); - -GB_DEF char *gb_bprintf (char const *fmt, ...) GB_PRINTF_ARGS(1); // NOTE(bill): A locally persisting buffer is used internally -GB_DEF char *gb_bprintf_va (char const *fmt, va_list va); // NOTE(bill): A locally persisting buffer is used internally -GB_DEF isize gb_snprintf (char *str, isize n, char const *fmt, ...) GB_PRINTF_ARGS(3); -GB_DEF isize gb_snprintf_va(char *str, isize n, char const *fmt, va_list va); - -//////////////////////////////////////////////////////////////// -// -// DLL Handling -// -// - -typedef void *gbDllHandle; -typedef void (*gbDllProc)(void); - -GB_DEF gbDllHandle gb_dll_load (char const *filepath); -GB_DEF void gb_dll_unload (gbDllHandle dll); -GB_DEF gbDllProc gb_dll_proc_address(gbDllHandle dll, char const *proc_name); - - -//////////////////////////////////////////////////////////////// -// -// Time -// -// - -GB_DEF u64 gb_rdtsc (void); -GB_DEF f64 gb_time_now (void); // NOTE(bill): This is only for relative time e.g. game loops -GB_DEF u64 gb_utc_time_now(void); // NOTE(bill): Number of microseconds since 1601-01-01 UTC -GB_DEF void gb_sleep_ms (u32 ms); - - -//////////////////////////////////////////////////////////////// -// -// Miscellany -// -// - -typedef struct gbRandom { - u32 offsets[8]; - u32 value; -} gbRandom; - -// NOTE(bill): Generates from numerous sources to produce a decent pseudo-random seed -GB_DEF void gb_random_init (gbRandom *r); -GB_DEF u32 gb_random_gen_u32 (gbRandom *r); -GB_DEF u32 gb_random_gen_u32_unique(gbRandom *r); -GB_DEF u64 gb_random_gen_u64 (gbRandom *r); // NOTE(bill): (gb_random_gen_u32() << 32) | gb_random_gen_u32() -GB_DEF isize gb_random_gen_isize (gbRandom *r); -GB_DEF i64 gb_random_range_i64 (gbRandom *r, i64 lower_inc, i64 higher_inc); -GB_DEF isize gb_random_range_isize (gbRandom *r, isize lower_inc, isize higher_inc); -GB_DEF f64 gb_random_range_f64 (gbRandom *r, f64 lower_inc, f64 higher_inc); - - - - -GB_DEF void gb_exit (u32 code); -GB_DEF void gb_yield (void); -GB_DEF void gb_set_env (char const *name, char const *value); -GB_DEF void gb_unset_env(char const *name); - -GB_DEF u16 gb_endian_swap16(u16 i); -GB_DEF u32 gb_endian_swap32(u32 i); -GB_DEF u64 gb_endian_swap64(u64 i); - -GB_DEF isize gb_count_set_bits(u64 mask); - -//////////////////////////////////////////////////////////////// -// -// Platform Stuff -// -// - -#if defined(GB_PLATFORM) - -// NOTE(bill): -// Coordiate system - +ve x - left to right -// - +ve y - bottom to top -// - Relative to window - -// TODO(bill): Proper documentation for this with code examples - -// Window Support - Complete -// OS X Support - Missing: -// * Sofware framebuffer -// * (show|hide) window -// * show_cursor -// * toggle (fullscreen|borderless) -// * set window position -// * Clipboard -// * GameControllers -// Linux Support - None -// Other OS Support - None - -#ifndef GB_MAX_GAME_CONTROLLER_COUNT -#define GB_MAX_GAME_CONTROLLER_COUNT 4 -#endif - -typedef enum gbKeyType { - gbKey_Unknown = 0, // Unhandled key - - // NOTE(bill): Allow the basic printable keys to be aliased with their chars - gbKey_0 = '0', - gbKey_1, - gbKey_2, - gbKey_3, - gbKey_4, - gbKey_5, - gbKey_6, - gbKey_7, - gbKey_8, - gbKey_9, - - gbKey_A = 'A', - gbKey_B, - gbKey_C, - gbKey_D, - gbKey_E, - gbKey_F, - gbKey_G, - gbKey_H, - gbKey_I, - gbKey_J, - gbKey_K, - gbKey_L, - gbKey_M, - gbKey_N, - gbKey_O, - gbKey_P, - gbKey_Q, - gbKey_R, - gbKey_S, - gbKey_T, - gbKey_U, - gbKey_V, - gbKey_W, - gbKey_X, - gbKey_Y, - gbKey_Z, - - gbKey_Lbracket = '[', - gbKey_Rbracket = ']', - gbKey_Semicolon = ';', - gbKey_Comma = ',', - gbKey_Period = '.', - gbKey_Quote = '\'', - gbKey_Slash = '/', - gbKey_Backslash = '\\', - gbKey_Grave = '`', - gbKey_Equals = '=', - gbKey_Minus = '-', - gbKey_Space = ' ', - - gbKey__Pad = 128, // NOTE(bill): make sure ASCII is reserved - - gbKey_Escape, // Escape - gbKey_Lcontrol, // Left Control - gbKey_Lshift, // Left Shift - gbKey_Lalt, // Left Alt - gbKey_Lsystem, // Left OS specific: window (Windows and Linux), apple/cmd (MacOS X), ... - gbKey_Rcontrol, // Right Control - gbKey_Rshift, // Right Shift - gbKey_Ralt, // Right Alt - gbKey_Rsystem, // Right OS specific: window (Windows and Linux), apple/cmd (MacOS X), ... - gbKey_Menu, // Menu - gbKey_Return, // Return - gbKey_Backspace, // Backspace - gbKey_Tab, // Tabulation - gbKey_Pageup, // Page up - gbKey_Pagedown, // Page down - gbKey_End, // End - gbKey_Home, // Home - gbKey_Insert, // Insert - gbKey_Delete, // Delete - gbKey_Plus, // + - gbKey_Subtract, // - - gbKey_Multiply, // * - gbKey_Divide, // / - gbKey_Left, // Left arrow - gbKey_Right, // Right arrow - gbKey_Up, // Up arrow - gbKey_Down, // Down arrow - gbKey_Numpad0, // Numpad 0 - gbKey_Numpad1, // Numpad 1 - gbKey_Numpad2, // Numpad 2 - gbKey_Numpad3, // Numpad 3 - gbKey_Numpad4, // Numpad 4 - gbKey_Numpad5, // Numpad 5 - gbKey_Numpad6, // Numpad 6 - gbKey_Numpad7, // Numpad 7 - gbKey_Numpad8, // Numpad 8 - gbKey_Numpad9, // Numpad 9 - gbKey_NumpadDot, // Numpad . - gbKey_NumpadEnter, // Numpad Enter - gbKey_F1, // F1 - gbKey_F2, // F2 - gbKey_F3, // F3 - gbKey_F4, // F4 - gbKey_F5, // F5 - gbKey_F6, // F6 - gbKey_F7, // F7 - gbKey_F8, // F8 - gbKey_F9, // F8 - gbKey_F10, // F10 - gbKey_F11, // F11 - gbKey_F12, // F12 - gbKey_F13, // F13 - gbKey_F14, // F14 - gbKey_F15, // F15 - gbKey_Pause, // Pause - - gbKey_Count, -} gbKeyType; - -/* TODO(bill): Change name? */ -typedef u8 gbKeyState; -typedef enum gbKeyStateFlag { - gbKeyState_Down = GB_BIT(0), - gbKeyState_Pressed = GB_BIT(1), - gbKeyState_Released = GB_BIT(2) -} gbKeyStateFlag; - -GB_DEF void gb_key_state_update(gbKeyState *s, b32 is_down); - -typedef enum gbMouseButtonType { - gbMouseButton_Left, - gbMouseButton_Middle, - gbMouseButton_Right, - gbMouseButton_X1, - gbMouseButton_X2, - - gbMouseButton_Count -} gbMouseButtonType; - -typedef enum gbControllerAxisType { - gbControllerAxis_LeftX, - gbControllerAxis_LeftY, - gbControllerAxis_RightX, - gbControllerAxis_RightY, - gbControllerAxis_LeftTrigger, - gbControllerAxis_RightTrigger, - - gbControllerAxis_Count -} gbControllerAxisType; - -typedef enum gbControllerButtonType { - gbControllerButton_Up, - gbControllerButton_Down, - gbControllerButton_Left, - gbControllerButton_Right, - gbControllerButton_A, - gbControllerButton_B, - gbControllerButton_X, - gbControllerButton_Y, - gbControllerButton_LeftShoulder, - gbControllerButton_RightShoulder, - gbControllerButton_Back, - gbControllerButton_Start, - gbControllerButton_LeftThumb, - gbControllerButton_RightThumb, - - gbControllerButton_Count -} gbControllerButtonType; - -typedef struct gbGameController { - b16 is_connected, is_analog; - - f32 axes[gbControllerAxis_Count]; - gbKeyState buttons[gbControllerButton_Count]; -} gbGameController; - -#if defined(GB_SYSTEM_WINDOWS) - typedef struct _XINPUT_GAMEPAD XINPUT_GAMEPAD; - typedef struct _XINPUT_STATE XINPUT_STATE; - typedef struct _XINPUT_VIBRATION XINPUT_VIBRATION; - - #define GB_XINPUT_GET_STATE(name) unsigned long __stdcall name(unsigned long dwUserIndex, XINPUT_STATE *pState) - typedef GB_XINPUT_GET_STATE(gbXInputGetStateProc); - - #define GB_XINPUT_SET_STATE(name) unsigned long __stdcall name(unsigned long dwUserIndex, XINPUT_VIBRATION *pVibration) - typedef GB_XINPUT_SET_STATE(gbXInputSetStateProc); -#endif - - -typedef enum gbWindowFlag { - gbWindow_Fullscreen = GB_BIT(0), - gbWindow_Hidden = GB_BIT(1), - gbWindow_Borderless = GB_BIT(2), - gbWindow_Resizable = GB_BIT(3), - gbWindow_Minimized = GB_BIT(4), - gbWindow_Maximized = GB_BIT(5), - gbWindow_FullscreenDesktop = gbWindow_Fullscreen | gbWindow_Borderless, -} gbWindowFlag; - -typedef enum gbRendererType { - gbRenderer_Opengl, - gbRenderer_Software, - - gbRenderer_Count, -} gbRendererType; - - - -#if defined(GB_SYSTEM_WINDOWS) && !defined(_WINDOWS_) -typedef struct tagBITMAPINFOHEADER { - unsigned long biSize; - long biWidth; - long biHeight; - u16 biPlanes; - u16 biBitCount; - unsigned long biCompression; - unsigned long biSizeImage; - long biXPelsPerMeter; - long biYPelsPerMeter; - unsigned long biClrUsed; - unsigned long biClrImportant; -} BITMAPINFOHEADER, *PBITMAPINFOHEADER; -typedef struct tagRGBQUAD { - u8 rgbBlue; - u8 rgbGreen; - u8 rgbRed; - u8 rgbReserved; -} RGBQUAD; -typedef struct tagBITMAPINFO { - BITMAPINFOHEADER bmiHeader; - RGBQUAD bmiColors[1]; -} BITMAPINFO, *PBITMAPINFO; -#endif - -typedef struct gbPlatform { - b32 is_initialized; - - void *window_handle; - i32 window_x, window_y; - i32 window_width, window_height; - u32 window_flags; - b16 window_is_closed, window_has_focus; - -#if defined(GB_SYSTEM_WINDOWS) - void *win32_dc; -#elif defined(GB_SYSTEM_OSX) - void *osx_autorelease_pool; // TODO(bill): Is this really needed? -#endif - - gbRendererType renderer_type; - union { - struct { - void * context; - i32 major; - i32 minor; - b16 core, compatible; - gbDllHandle dll_handle; - } opengl; - - // NOTE(bill): Software rendering - struct { -#if defined(GB_SYSTEM_WINDOWS) - BITMAPINFO win32_bmi; -#endif - void * memory; - isize memory_size; - i32 pitch; - i32 bits_per_pixel; - } sw_framebuffer; - }; - - gbKeyState keys[gbKey_Count]; - struct { - gbKeyState control; - gbKeyState alt; - gbKeyState shift; - } key_modifiers; - - Rune char_buffer[256]; - isize char_buffer_count; - - b32 mouse_clip; - i32 mouse_x, mouse_y; - i32 mouse_dx, mouse_dy; // NOTE(bill): Not raw mouse movement - i32 mouse_raw_dx, mouse_raw_dy; // NOTE(bill): Raw mouse movement - f32 mouse_wheel_delta; - gbKeyState mouse_buttons[gbMouseButton_Count]; - - gbGameController game_controllers[GB_MAX_GAME_CONTROLLER_COUNT]; - - f64 curr_time; - f64 dt_for_frame; - b32 quit_requested; - -#if defined(GB_SYSTEM_WINDOWS) - struct { - gbXInputGetStateProc *get_state; - gbXInputSetStateProc *set_state; - } xinput; -#endif -} gbPlatform; - - -typedef struct gbVideoMode { - i32 width, height; - i32 bits_per_pixel; -} gbVideoMode; - -GB_DEF gbVideoMode gb_video_mode (i32 width, i32 height, i32 bits_per_pixel); -GB_DEF b32 gb_video_mode_is_valid (gbVideoMode mode); -GB_DEF gbVideoMode gb_video_mode_get_desktop (void); -GB_DEF isize gb_video_mode_get_fullscreen_modes(gbVideoMode *modes, isize max_mode_count); // NOTE(bill): returns mode count -GB_DEF GB_COMPARE_PROC(gb_video_mode_cmp); // NOTE(bill): Sort smallest to largest (Ascending) -GB_DEF GB_COMPARE_PROC(gb_video_mode_dsc_cmp); // NOTE(bill): Sort largest to smallest (Descending) - - -// NOTE(bill): Software rendering -GB_DEF b32 gb_platform_init_with_software (gbPlatform *p, char const *window_title, i32 width, i32 height, u32 window_flags); -// NOTE(bill): OpenGL Rendering -GB_DEF b32 gb_platform_init_with_opengl (gbPlatform *p, char const *window_title, i32 width, i32 height, u32 window_flags, i32 major, i32 minor, b32 core, b32 compatible); -GB_DEF void gb_platform_update (gbPlatform *p); -GB_DEF void gb_platform_display (gbPlatform *p); -GB_DEF void gb_platform_destroy (gbPlatform *p); -GB_DEF void gb_platform_show_cursor (gbPlatform *p, b32 show); -GB_DEF void gb_platform_set_mouse_position (gbPlatform *p, i32 x, i32 y); -GB_DEF void gb_platform_set_controller_vibration (gbPlatform *p, isize index, f32 left_motor, f32 right_motor); -GB_DEF b32 gb_platform_has_clipboard_text (gbPlatform *p); -GB_DEF void gb_platform_set_clipboard_text (gbPlatform *p, char const *str); -GB_DEF char *gb_platform_get_clipboard_text (gbPlatform *p, gbAllocator a); -GB_DEF void gb_platform_set_window_position (gbPlatform *p, i32 x, i32 y); -GB_DEF void gb_platform_set_window_title (gbPlatform *p, char const *title, ...) GB_PRINTF_ARGS(2); -GB_DEF void gb_platform_toggle_fullscreen (gbPlatform *p, b32 fullscreen_desktop); -GB_DEF void gb_platform_toggle_borderless (gbPlatform *p); -GB_DEF void gb_platform_make_opengl_context_current(gbPlatform *p); -GB_DEF void gb_platform_show_window (gbPlatform *p); -GB_DEF void gb_platform_hide_window (gbPlatform *p); - - -#endif // GB_PLATFORM - -#if defined(__cplusplus) -} -#endif - -#endif // GB_INCLUDE_GB_H - - - - - - -//////////////////////////////////////////////////////////////// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// Implementation -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// It's turtles all the way down! -//////////////////////////////////////////////////////////////// -#if defined(GB_IMPLEMENTATION) && !defined(GB_IMPLEMENTATION_DONE) -#define GB_IMPLEMENTATION_DONE - -#if defined(__cplusplus) -extern "C" { -#endif - - -#if defined(GB_COMPILER_MSVC) && !defined(_WINDOWS_) - //////////////////////////////////////////////////////////////// - // - // Bill's Mini Windows.h - // - // - - #define WINAPI __stdcall - #define WINAPIV __cdecl - #define CALLBACK __stdcall - #define MAX_PATH 260 - #define CCHDEVICENAME 32 - #define CCHFORMNAME 32 - - typedef unsigned long DWORD; - typedef int WINBOOL; - #ifndef XFree86Server - #ifndef __OBJC__ - typedef WINBOOL BOOL; - #else - #define BOOL WINBOOL - #endif - typedef unsigned char BYTE; - #endif - typedef unsigned short WORD; - typedef float FLOAT; - typedef int INT; - typedef unsigned int UINT; - typedef short SHORT; - typedef long LONG; - typedef long long LONGLONG; - typedef unsigned short USHORT; - typedef unsigned long ULONG; - typedef unsigned long long ULONGLONG; - - typedef UINT WPARAM; - typedef LONG LPARAM; - typedef LONG LRESULT; - #ifndef _HRESULT_DEFINED - typedef LONG HRESULT; - #define _HRESULT_DEFINED - #endif - #ifndef XFree86Server - typedef WORD ATOM; - #endif /* XFree86Server */ - typedef void *HANDLE; - typedef HANDLE HGLOBAL; - typedef HANDLE HLOCAL; - typedef HANDLE GLOBALHANDLE; - typedef HANDLE LOCALHANDLE; - typedef void *HGDIOBJ; - - #define DECLARE_HANDLE(name) typedef HANDLE name - DECLARE_HANDLE(HACCEL); - DECLARE_HANDLE(HBITMAP); - DECLARE_HANDLE(HBRUSH); - DECLARE_HANDLE(HCOLORSPACE); - DECLARE_HANDLE(HDC); - DECLARE_HANDLE(HGLRC); - DECLARE_HANDLE(HDESK); - DECLARE_HANDLE(HENHMETAFILE); - DECLARE_HANDLE(HFONT); - DECLARE_HANDLE(HICON); - DECLARE_HANDLE(HKEY); - typedef HKEY *PHKEY; - DECLARE_HANDLE(HMENU); - DECLARE_HANDLE(HMETAFILE); - DECLARE_HANDLE(HINSTANCE); - typedef HINSTANCE HMODULE; - DECLARE_HANDLE(HPALETTE); - DECLARE_HANDLE(HPEN); - DECLARE_HANDLE(HRGN); - DECLARE_HANDLE(HRSRC); - DECLARE_HANDLE(HSTR); - DECLARE_HANDLE(HTASK); - DECLARE_HANDLE(HWND); - DECLARE_HANDLE(HWINSTA); - DECLARE_HANDLE(HKL); - DECLARE_HANDLE(HRAWINPUT); - DECLARE_HANDLE(HMONITOR); - #undef DECLARE_HANDLE - - typedef int HFILE; - typedef HICON HCURSOR; - typedef DWORD COLORREF; - typedef int (WINAPI *FARPROC)(); - typedef int (WINAPI *NEARPROC)(); - typedef int (WINAPI *PROC)(); - typedef LRESULT (CALLBACK *WNDPROC)(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); - - #if defined(_WIN64) - typedef unsigned __int64 ULONG_PTR; - typedef signed __int64 LONG_PTR; - #else - typedef unsigned long ULONG_PTR; - typedef signed long LONG_PTR; - #endif - typedef ULONG_PTR DWORD_PTR; - - typedef struct tagRECT { - LONG left; - LONG top; - LONG right; - LONG bottom; - } RECT; - typedef struct tagRECTL { - LONG left; - LONG top; - LONG right; - LONG bottom; - } RECTL; - typedef struct tagPOINT { - LONG x; - LONG y; - } POINT; - typedef struct tagSIZE { - LONG cx; - LONG cy; - } SIZE; - typedef struct tagPOINTS { - SHORT x; - SHORT y; - } POINTS; - typedef struct _SECURITY_ATTRIBUTES { - DWORD nLength; - HANDLE lpSecurityDescriptor; - BOOL bInheritHandle; - } SECURITY_ATTRIBUTES; - typedef enum _LOGICAL_PROCESSOR_RELATIONSHIP { - RelationProcessorCore, - RelationNumaNode, - RelationCache, - RelationProcessorPackage, - RelationGroup, - RelationAll = 0xffff - } LOGICAL_PROCESSOR_RELATIONSHIP; - typedef enum _PROCESSOR_CACHE_TYPE { - CacheUnified, - CacheInstruction, - CacheData, - CacheTrace - } PROCESSOR_CACHE_TYPE; - typedef struct _CACHE_DESCRIPTOR { - BYTE Level; - BYTE Associativity; - WORD LineSize; - DWORD Size; - PROCESSOR_CACHE_TYPE Type; - } CACHE_DESCRIPTOR; - typedef struct _SYSTEM_LOGICAL_PROCESSOR_INFORMATION { - ULONG_PTR ProcessorMask; - LOGICAL_PROCESSOR_RELATIONSHIP Relationship; - union { - struct { - BYTE Flags; - } ProcessorCore; - struct { - DWORD NodeNumber; - } NumaNode; - CACHE_DESCRIPTOR Cache; - ULONGLONG Reserved[2]; - }; - } SYSTEM_LOGICAL_PROCESSOR_INFORMATION; - typedef struct _MEMORY_BASIC_INFORMATION { - void *BaseAddress; - void *AllocationBase; - DWORD AllocationProtect; - usize RegionSize; - DWORD State; - DWORD Protect; - DWORD Type; - } MEMORY_BASIC_INFORMATION; - typedef struct _SYSTEM_INFO { - union { - DWORD dwOemId; - struct { - WORD wProcessorArchitecture; - WORD wReserved; - }; - }; - DWORD dwPageSize; - void * lpMinimumApplicationAddress; - void * lpMaximumApplicationAddress; - DWORD_PTR dwActiveProcessorMask; - DWORD dwNumberOfProcessors; - DWORD dwProcessorType; - DWORD dwAllocationGranularity; - WORD wProcessorLevel; - WORD wProcessorRevision; - } SYSTEM_INFO; - typedef union _LARGE_INTEGER { - struct { - DWORD LowPart; - LONG HighPart; - }; - struct { - DWORD LowPart; - LONG HighPart; - } u; - LONGLONG QuadPart; - } LARGE_INTEGER; - typedef union _ULARGE_INTEGER { - struct { - DWORD LowPart; - DWORD HighPart; - }; - struct { - DWORD LowPart; - DWORD HighPart; - } u; - ULONGLONG QuadPart; - } ULARGE_INTEGER; - - typedef struct _OVERLAPPED { - ULONG_PTR Internal; - ULONG_PTR InternalHigh; - union { - struct { - DWORD Offset; - DWORD OffsetHigh; - }; - void *Pointer; - }; - HANDLE hEvent; - } OVERLAPPED; - typedef struct _FILETIME { - DWORD dwLowDateTime; - DWORD dwHighDateTime; - } FILETIME; - typedef struct _WIN32_FIND_DATAW { - DWORD dwFileAttributes; - FILETIME ftCreationTime; - FILETIME ftLastAccessTime; - FILETIME ftLastWriteTime; - DWORD nFileSizeHigh; - DWORD nFileSizeLow; - DWORD dwReserved0; - DWORD dwReserved1; - wchar_t cFileName[MAX_PATH]; - wchar_t cAlternateFileName[14]; - } WIN32_FIND_DATAW; - typedef struct _WIN32_FILE_ATTRIBUTE_DATA { - DWORD dwFileAttributes; - FILETIME ftCreationTime; - FILETIME ftLastAccessTime; - FILETIME ftLastWriteTime; - DWORD nFileSizeHigh; - DWORD nFileSizeLow; - } WIN32_FILE_ATTRIBUTE_DATA; - typedef enum _GET_FILEEX_INFO_LEVELS { - GetFileExInfoStandard, - GetFileExMaxInfoLevel - } GET_FILEEX_INFO_LEVELS; - typedef struct tagRAWINPUTHEADER { - DWORD dwType; - DWORD dwSize; - HANDLE hDevice; - WPARAM wParam; - } RAWINPUTHEADER; - typedef struct tagRAWINPUTDEVICE { - USHORT usUsagePage; - USHORT usUsage; - DWORD dwFlags; - HWND hwndTarget; - } RAWINPUTDEVICE; - typedef struct tagRAWMOUSE { - WORD usFlags; - union { - ULONG ulButtons; - struct { - WORD usButtonFlags; - WORD usButtonData; - }; - }; - ULONG ulRawButtons; - LONG lLastX; - LONG lLastY; - ULONG ulExtraInformation; - } RAWMOUSE; - typedef struct tagRAWKEYBOARD { - WORD MakeCode; - WORD Flags; - WORD Reserved; - WORD VKey; - UINT Message; - ULONG ExtraInformation; - } RAWKEYBOARD; - typedef struct tagRAWHID { - DWORD dwSizeHid; - DWORD dwCount; - BYTE bRawData[1]; - } RAWHID; - typedef struct tagRAWINPUT { - RAWINPUTHEADER header; - union { - RAWMOUSE mouse; - RAWKEYBOARD keyboard; - RAWHID hid; - } data; - } RAWINPUT; - typedef struct tagWNDCLASSEXW { - UINT cbSize; - UINT style; - WNDPROC lpfnWndProc; - INT cbClsExtra; - INT cbWndExtra; - HINSTANCE hInstance; - HICON hIcon; - HCURSOR hCursor; - HANDLE hbrBackground; - wchar_t const *lpszMenuName; - wchar_t const *lpszClassName; - HICON hIconSm; - } WNDCLASSEXW; - typedef struct _POINTL { - LONG x; - LONG y; - } POINTL; - typedef struct _devicemodew { - wchar_t dmDeviceName[CCHDEVICENAME]; - WORD dmSpecVersion; - WORD dmDriverVersion; - WORD dmSize; - WORD dmDriverExtra; - DWORD dmFields; - union { - struct { - short dmOrientation; - short dmPaperSize; - short dmPaperLength; - short dmPaperWidth; - short dmScale; - short dmCopies; - short dmDefaultSource; - short dmPrintQuality; - }; - struct { - POINTL dmPosition; - DWORD dmDisplayOrientation; - DWORD dmDisplayFixedOutput; - }; - }; - short dmColor; - short dmDuplex; - short dmYResolution; - short dmTTOption; - short dmCollate; - wchar_t dmFormName[CCHFORMNAME]; - WORD dmLogPixels; - DWORD dmBitsPerPel; - DWORD dmPelsWidth; - DWORD dmPelsHeight; - union { - DWORD dmDisplayFlags; - DWORD dmNup; - }; - DWORD dmDisplayFrequency; - #if (WINVER >= 0x0400) - DWORD dmICMMethod; - DWORD dmICMIntent; - DWORD dmMediaType; - DWORD dmDitherType; - DWORD dmReserved1; - DWORD dmReserved2; - #if (WINVER >= 0x0500) || (_WIN32_WINNT >= 0x0400) - DWORD dmPanningWidth; - DWORD dmPanningHeight; - #endif - #endif - } DEVMODEW; - typedef struct tagPIXELFORMATDESCRIPTOR { - WORD nSize; - WORD nVersion; - DWORD dwFlags; - BYTE iPixelType; - BYTE cColorBits; - BYTE cRedBits; - BYTE cRedShift; - BYTE cGreenBits; - BYTE cGreenShift; - BYTE cBlueBits; - BYTE cBlueShift; - BYTE cAlphaBits; - BYTE cAlphaShift; - BYTE cAccumBits; - BYTE cAccumRedBits; - BYTE cAccumGreenBits; - BYTE cAccumBlueBits; - BYTE cAccumAlphaBits; - BYTE cDepthBits; - BYTE cStencilBits; - BYTE cAuxBuffers; - BYTE iLayerType; - BYTE bReserved; - DWORD dwLayerMask; - DWORD dwVisibleMask; - DWORD dwDamageMask; - } PIXELFORMATDESCRIPTOR; - typedef struct tagMSG { // msg - HWND hwnd; - UINT message; - WPARAM wParam; - LPARAM lParam; - DWORD time; - POINT pt; - } MSG; - typedef struct tagWINDOWPLACEMENT { - UINT length; - UINT flags; - UINT showCmd; - POINT ptMinPosition; - POINT ptMaxPosition; - RECT rcNormalPosition; - } WINDOWPLACEMENT; - typedef struct tagMONITORINFO { - DWORD cbSize; - RECT rcMonitor; - RECT rcWork; - DWORD dwFlags; - } MONITORINFO; - - #define INFINITE 0xffffffffl - #define INVALID_HANDLE_VALUE ((void *)(intptr)(-1)) - - - typedef DWORD WINAPI THREAD_START_ROUTINE(void *parameter); - - GB_DLL_IMPORT DWORD WINAPI GetLastError (void); - GB_DLL_IMPORT BOOL WINAPI CloseHandle (HANDLE object); - GB_DLL_IMPORT HANDLE WINAPI CreateSemaphoreA (SECURITY_ATTRIBUTES *semaphore_attributes, LONG initial_count, - LONG maximum_count, char const *name); - GB_DLL_IMPORT BOOL WINAPI ReleaseSemaphore (HANDLE semaphore, LONG release_count, LONG *previous_count); - GB_DLL_IMPORT DWORD WINAPI WaitForSingleObject(HANDLE handle, DWORD milliseconds); - GB_DLL_IMPORT HANDLE WINAPI CreateThread (SECURITY_ATTRIBUTES *semaphore_attributes, usize stack_size, - THREAD_START_ROUTINE *start_address, void *parameter, - DWORD creation_flags, DWORD *thread_id); - GB_DLL_IMPORT DWORD WINAPI GetThreadId (HANDLE handle); - GB_DLL_IMPORT void WINAPI RaiseException (DWORD, DWORD, DWORD, ULONG_PTR const *); - - - GB_DLL_IMPORT BOOL WINAPI GetLogicalProcessorInformation(SYSTEM_LOGICAL_PROCESSOR_INFORMATION *buffer, DWORD *return_length); - GB_DLL_IMPORT DWORD_PTR WINAPI SetThreadAffinityMask(HANDLE thread, DWORD_PTR check_mask); - GB_DLL_IMPORT HANDLE WINAPI GetCurrentThread(void); - - #define PAGE_NOACCESS 0x01 - #define PAGE_READONLY 0x02 - #define PAGE_READWRITE 0x04 - #define PAGE_WRITECOPY 0x08 - #define PAGE_EXECUTE 0x10 - #define PAGE_EXECUTE_READ 0x20 - #define PAGE_EXECUTE_READWRITE 0x40 - #define PAGE_EXECUTE_WRITECOPY 0x80 - #define PAGE_GUARD 0x100 - #define PAGE_NOCACHE 0x200 - #define PAGE_WRITECOMBINE 0x400 - - #define MEM_COMMIT 0x1000 - #define MEM_RESERVE 0x2000 - #define MEM_DECOMMIT 0x4000 - #define MEM_RELEASE 0x8000 - #define MEM_FREE 0x10000 - #define MEM_PRIVATE 0x20000 - #define MEM_MAPPED 0x40000 - #define MEM_RESET 0x80000 - #define MEM_TOP_DOWN 0x100000 - #define MEM_LARGE_PAGES 0x20000000 - #define MEM_4MB_PAGES 0x80000000 - - - - - GB_DLL_IMPORT void * WINAPI VirtualAlloc (void *addr, usize size, DWORD allocation_type, DWORD protect); - GB_DLL_IMPORT usize WINAPI VirtualQuery (void const *address, MEMORY_BASIC_INFORMATION *buffer, usize length); - GB_DLL_IMPORT BOOL WINAPI VirtualFree (void *address, usize size, DWORD free_type); - GB_DLL_IMPORT void WINAPI GetSystemInfo(SYSTEM_INFO *system_info); - - - #ifndef VK_UNKNOWN - #define VK_UNKNOWN 0 - #define VK_LBUTTON 0x01 - #define VK_RBUTTON 0x02 - #define VK_CANCEL 0x03 - #define VK_MBUTTON 0x04 - #define VK_XBUTTON1 0x05 - #define VK_XBUTTON2 0x06 - #define VK_BACK 0x08 - #define VK_TAB 0x09 - #define VK_CLEAR 0x0C - #define VK_RETURN 0x0D - #define VK_SHIFT 0x10 - #define VK_CONTROL 0x11 // CTRL key - #define VK_MENU 0x12 // ALT key - #define VK_PAUSE 0x13 // PAUSE key - #define VK_CAPITAL 0x14 // CAPS LOCK key - #define VK_KANA 0x15 // Input Method Editor (IME) Kana mode - #define VK_HANGUL 0x15 // IME Hangul mode - #define VK_JUNJA 0x17 // IME Junja mode - #define VK_FINAL 0x18 // IME final mode - #define VK_HANJA 0x19 // IME Hanja mode - #define VK_KANJI 0x19 // IME Kanji mode - #define VK_ESCAPE 0x1B // ESC key - #define VK_CONVERT 0x1C // IME convert - #define VK_NONCONVERT 0x1D // IME nonconvert - #define VK_ACCEPT 0x1E // IME accept - #define VK_MODECHANGE 0x1F // IME mode change request - #define VK_SPACE 0x20 // SPACE key - #define VK_PRIOR 0x21 // PAGE UP key - #define VK_NEXT 0x22 // PAGE DOWN key - #define VK_END 0x23 // END key - #define VK_HOME 0x24 // HOME key - #define VK_LEFT 0x25 // LEFT ARROW key - #define VK_UP 0x26 // UP ARROW key - #define VK_RIGHT 0x27 // RIGHT ARROW key - #define VK_DOWN 0x28 // DOWN ARROW key - #define VK_SELECT 0x29 // SELECT key - #define VK_PRINT 0x2A // PRINT key - #define VK_EXECUTE 0x2B // EXECUTE key - #define VK_SNAPSHOT 0x2C // PRINT SCREEN key - #define VK_INSERT 0x2D // INS key - #define VK_DELETE 0x2E // DEL key - #define VK_HELP 0x2F // HELP key - #define VK_0 0x30 - #define VK_1 0x31 - #define VK_2 0x32 - #define VK_3 0x33 - #define VK_4 0x34 - #define VK_5 0x35 - #define VK_6 0x36 - #define VK_7 0x37 - #define VK_8 0x38 - #define VK_9 0x39 - #define VK_A 0x41 - #define VK_B 0x42 - #define VK_C 0x43 - #define VK_D 0x44 - #define VK_E 0x45 - #define VK_F 0x46 - #define VK_G 0x47 - #define VK_H 0x48 - #define VK_I 0x49 - #define VK_J 0x4A - #define VK_K 0x4B - #define VK_L 0x4C - #define VK_M 0x4D - #define VK_N 0x4E - #define VK_O 0x4F - #define VK_P 0x50 - #define VK_Q 0x51 - #define VK_R 0x52 - #define VK_S 0x53 - #define VK_T 0x54 - #define VK_U 0x55 - #define VK_V 0x56 - #define VK_W 0x57 - #define VK_X 0x58 - #define VK_Y 0x59 - #define VK_Z 0x5A - #define VK_LWIN 0x5B // Left Windows key (Microsoft Natural keyboard) - #define VK_RWIN 0x5C // Right Windows key (Natural keyboard) - #define VK_APPS 0x5D // Applications key (Natural keyboard) - #define VK_SLEEP 0x5F // Computer Sleep key - // Num pad keys - #define VK_NUMPAD0 0x60 - #define VK_NUMPAD1 0x61 - #define VK_NUMPAD2 0x62 - #define VK_NUMPAD3 0x63 - #define VK_NUMPAD4 0x64 - #define VK_NUMPAD5 0x65 - #define VK_NUMPAD6 0x66 - #define VK_NUMPAD7 0x67 - #define VK_NUMPAD8 0x68 - #define VK_NUMPAD9 0x69 - #define VK_MULTIPLY 0x6A - #define VK_ADD 0x6B - #define VK_SEPARATOR 0x6C - #define VK_SUBTRACT 0x6D - #define VK_DECIMAL 0x6E - #define VK_DIVIDE 0x6F - #define VK_F1 0x70 - #define VK_F2 0x71 - #define VK_F3 0x72 - #define VK_F4 0x73 - #define VK_F5 0x74 - #define VK_F6 0x75 - #define VK_F7 0x76 - #define VK_F8 0x77 - #define VK_F9 0x78 - #define VK_F10 0x79 - #define VK_F11 0x7A - #define VK_F12 0x7B - #define VK_F13 0x7C - #define VK_F14 0x7D - #define VK_F15 0x7E - #define VK_F16 0x7F - #define VK_F17 0x80 - #define VK_F18 0x81 - #define VK_F19 0x82 - #define VK_F20 0x83 - #define VK_F21 0x84 - #define VK_F22 0x85 - #define VK_F23 0x86 - #define VK_F24 0x87 - #define VK_NUMLOCK 0x90 - #define VK_SCROLL 0x91 - #define VK_LSHIFT 0xA0 - #define VK_RSHIFT 0xA1 - #define VK_LCONTROL 0xA2 - #define VK_RCONTROL 0xA3 - #define VK_LMENU 0xA4 - #define VK_RMENU 0xA5 - #define VK_BROWSER_BACK 0xA6 // Windows 2000/XP: Browser Back key - #define VK_BROWSER_FORWARD 0xA7 // Windows 2000/XP: Browser Forward key - #define VK_BROWSER_REFRESH 0xA8 // Windows 2000/XP: Browser Refresh key - #define VK_BROWSER_STOP 0xA9 // Windows 2000/XP: Browser Stop key - #define VK_BROWSER_SEARCH 0xAA // Windows 2000/XP: Browser Search key - #define VK_BROWSER_FAVORITES 0xAB // Windows 2000/XP: Browser Favorites key - #define VK_BROWSER_HOME 0xAC // Windows 2000/XP: Browser Start and Home key - #define VK_VOLUME_MUTE 0xAD // Windows 2000/XP: Volume Mute key - #define VK_VOLUME_DOWN 0xAE // Windows 2000/XP: Volume Down key - #define VK_VOLUME_UP 0xAF // Windows 2000/XP: Volume Up key - #define VK_MEDIA_NEXT_TRACK 0xB0 // Windows 2000/XP: Next Track key - #define VK_MEDIA_PREV_TRACK 0xB1 // Windows 2000/XP: Previous Track key - #define VK_MEDIA_STOP 0xB2 // Windows 2000/XP: Stop Media key - #define VK_MEDIA_PLAY_PAUSE 0xB3 // Windows 2000/XP: Play/Pause Media key - #define VK_MEDIA_LAUNCH_MAIL 0xB4 // Windows 2000/XP: Start Mail key - #define VK_MEDIA_LAUNCH_MEDIA_SELECT 0xB5 // Windows 2000/XP: Select Media key - #define VK_MEDIA_LAUNCH_APP1 0xB6 // VK_LAUNCH_APP1 (B6) Windows 2000/XP: Start Application 1 key - #define VK_MEDIA_LAUNCH_APP2 0xB7 // VK_LAUNCH_APP2 (B7) Windows 2000/XP: Start Application 2 key - #define VK_OEM_1 0xBA - #define VK_OEM_PLUS 0xBB - #define VK_OEM_COMMA 0xBC - #define VK_OEM_MINUS 0xBD - #define VK_OEM_PERIOD 0xBE - #define VK_OEM_2 0xBF - #define VK_OEM_3 0xC0 - #define VK_OEM_4 0xDB - #define VK_OEM_5 0xDC - #define VK_OEM_6 0xDD - #define VK_OEM_7 0xDE - #define VK_OEM_8 0xDF - #define VK_OEM_102 0xE2 - #define VK_PROCESSKEY 0xE5 - #define VK_PACKET 0xE7 - #define VK_ATTN 0xF6 // Attn key - #define VK_CRSEL 0xF7 // CrSel key - #define VK_EXSEL 0xF8 // ExSel key - #define VK_EREOF 0xF9 // Erase EOF key - #define VK_PLAY 0xFA // Play key - #define VK_ZOOM 0xFB // Zoom key - #define VK_NONAME 0xFC // Reserved for future use - #define VK_PA1 0xFD // VK_PA1 (FD) PA1 key - #define VK_OEM_CLEAR 0xFE // Clear key - #endif // VK_UNKNOWN - - - - #define GENERIC_READ 0x80000000 - #define GENERIC_WRITE 0x40000000 - #define GENERIC_EXECUTE 0x20000000 - #define GENERIC_ALL 0x10000000 - #define FILE_SHARE_READ 0x00000001 - #define FILE_SHARE_WRITE 0x00000002 - #define FILE_SHARE_DELETE 0x00000004 - #define CREATE_NEW 1 - #define CREATE_ALWAYS 2 - #define OPEN_EXISTING 3 - #define OPEN_ALWAYS 4 - #define TRUNCATE_EXISTING 5 - #define FILE_ATTRIBUTE_READONLY 0x00000001 - #define FILE_ATTRIBUTE_NORMAL 0x00000080 - #define FILE_ATTRIBUTE_TEMPORARY 0x00000100 - #define ERROR_FILE_NOT_FOUND 2l - #define ERROR_ACCESS_DENIED 5L - #define ERROR_NO_MORE_FILES 18l - #define ERROR_FILE_EXISTS 80l - #define ERROR_ALREADY_EXISTS 183l - #define STD_INPUT_HANDLE ((DWORD)-10) - #define STD_OUTPUT_HANDLE ((DWORD)-11) - #define STD_ERROR_HANDLE ((DWORD)-12) - - GB_DLL_IMPORT int MultiByteToWideChar(UINT code_page, DWORD flags, char const * multi_byte_str, int multi_byte_len, wchar_t const *wide_char_str, int wide_char_len); - GB_DLL_IMPORT int WideCharToMultiByte(UINT code_page, DWORD flags, wchar_t const *wide_char_str, int wide_char_len, char const * multi_byte_str, int multi_byte_len); - GB_DLL_IMPORT BOOL WINAPI SetFilePointerEx(HANDLE file, LARGE_INTEGER distance_to_move, - LARGE_INTEGER *new_file_pointer, DWORD move_method); - GB_DLL_IMPORT BOOL WINAPI ReadFile (HANDLE file, void *buffer, DWORD bytes_to_read, DWORD *bytes_read, OVERLAPPED *overlapped); - GB_DLL_IMPORT BOOL WINAPI WriteFile (HANDLE file, void const *buffer, DWORD bytes_to_write, DWORD *bytes_written, OVERLAPPED *overlapped); - GB_DLL_IMPORT HANDLE WINAPI CreateFileW (wchar_t const *path, DWORD desired_access, DWORD share_mode, - SECURITY_ATTRIBUTES *, DWORD creation_disposition, - DWORD flags_and_attributes, HANDLE template_file); - GB_DLL_IMPORT HANDLE WINAPI GetStdHandle (DWORD std_handle); - GB_DLL_IMPORT BOOL WINAPI GetFileSizeEx (HANDLE file, LARGE_INTEGER *size); - GB_DLL_IMPORT BOOL WINAPI SetEndOfFile (HANDLE file); - GB_DLL_IMPORT HANDLE WINAPI FindFirstFileW (wchar_t const *path, WIN32_FIND_DATAW *data); - GB_DLL_IMPORT BOOL WINAPI FindClose (HANDLE find_file); - GB_DLL_IMPORT BOOL WINAPI GetFileAttributesExW(wchar_t const *path, GET_FILEEX_INFO_LEVELS info_level_id, WIN32_FILE_ATTRIBUTE_DATA *data); - GB_DLL_IMPORT BOOL WINAPI CopyFileW(wchar_t const *old_f, wchar_t const *new_f, BOOL fail_if_exists); - GB_DLL_IMPORT BOOL WINAPI MoveFileW(wchar_t const *old_f, wchar_t const *new_f); - - GB_DLL_IMPORT HMODULE WINAPI LoadLibraryA (char const *filename); - GB_DLL_IMPORT BOOL WINAPI FreeLibrary (HMODULE module); - GB_DLL_IMPORT FARPROC WINAPI GetProcAddress(HMODULE module, char const *name); - - GB_DLL_IMPORT BOOL WINAPI QueryPerformanceFrequency(LARGE_INTEGER *frequency); - GB_DLL_IMPORT BOOL WINAPI QueryPerformanceCounter (LARGE_INTEGER *counter); - GB_DLL_IMPORT void WINAPI GetSystemTimeAsFileTime (FILETIME *system_time_as_file_time); - GB_DLL_IMPORT void WINAPI Sleep(DWORD milliseconds); - GB_DLL_IMPORT void WINAPI ExitProcess(UINT exit_code); - - GB_DLL_IMPORT BOOL WINAPI SetEnvironmentVariableA(char const *name, char const *value); - - - #define WM_NULL 0x0000 - #define WM_CREATE 0x0001 - #define WM_DESTROY 0x0002 - #define WM_MOVE 0x0003 - #define WM_SIZE 0x0005 - #define WM_ACTIVATE 0x0006 - #define WM_SETFOCUS 0x0007 - #define WM_KILLFOCUS 0x0008 - #define WM_ENABLE 0x000A - #define WM_SETREDRAW 0x000B - #define WM_SETTEXT 0x000C - #define WM_GETTEXT 0x000D - #define WM_GETTEXTLENGTH 0x000E - #define WM_PAINT 0x000F - #define WM_CLOSE 0x0010 - #define WM_QUERYENDSESSION 0x0011 - #define WM_QUERYOPEN 0x0013 - #define WM_ENDSESSION 0x0016 - #define WM_QUIT 0x0012 - #define WM_ERASEBKGND 0x0014 - #define WM_SYSCOLORCHANGE 0x0015 - #define WM_SHOWWINDOW 0x0018 - #define WM_WININICHANGE 0x001A - #define WM_SETTINGCHANGE WM_WININICHANGE - #define WM_DEVMODECHANGE 0x001B - #define WM_ACTIVATEAPP 0x001C - #define WM_FONTCHANGE 0x001D - #define WM_TIMECHANGE 0x001E - #define WM_CANCELMODE 0x001F - #define WM_SETCURSOR 0x0020 - #define WM_MOUSEACTIVATE 0x0021 - #define WM_CHILDACTIVATE 0x0022 - #define WM_QUEUESYNC 0x0023 - #define WM_GETMINMAXINFO 0x0024 - #define WM_PAINTICON 0x0026 - #define WM_ICONERASEBKGND 0x0027 - #define WM_NEXTDLGCTL 0x0028 - #define WM_SPOOLERSTATUS 0x002A - #define WM_DRAWITEM 0x002B - #define WM_MEASUREITEM 0x002C - #define WM_DELETEITEM 0x002D - #define WM_VKEYTOITEM 0x002E - #define WM_CHARTOITEM 0x002F - #define WM_SETFONT 0x0030 - #define WM_GETFONT 0x0031 - #define WM_SETHOTKEY 0x0032 - #define WM_GETHOTKEY 0x0033 - #define WM_QUERYDRAGICON 0x0037 - #define WM_COMPAREITEM 0x0039 - #define WM_GETOBJECT 0x003D - #define WM_COMPACTING 0x0041 - #define WM_COMMNOTIFY 0x0044 /* no longer suported */ - #define WM_WINDOWPOSCHANGING 0x0046 - #define WM_WINDOWPOSCHANGED 0x0047 - #define WM_POWER 0x0048 - #define WM_COPYDATA 0x004A - #define WM_CANCELJOURNAL 0x004B - #define WM_NOTIFY 0x004E - #define WM_INPUTLANGCHANGEREQUEST 0x0050 - #define WM_INPUTLANGCHANGE 0x0051 - #define WM_TCARD 0x0052 - #define WM_HELP 0x0053 - #define WM_USERCHANGED 0x0054 - #define WM_NOTIFYFORMAT 0x0055 - #define WM_CONTEXTMENU 0x007B - #define WM_STYLECHANGING 0x007C - #define WM_STYLECHANGED 0x007D - #define WM_DISPLAYCHANGE 0x007E - #define WM_GETICON 0x007F - #define WM_SETICON 0x0080 - #define WM_INPUT 0x00FF - #define WM_KEYFIRST 0x0100 - #define WM_KEYDOWN 0x0100 - #define WM_KEYUP 0x0101 - #define WM_CHAR 0x0102 - #define WM_DEADCHAR 0x0103 - #define WM_SYSKEYDOWN 0x0104 - #define WM_SYSKEYUP 0x0105 - #define WM_SYSCHAR 0x0106 - #define WM_SYSDEADCHAR 0x0107 - #define WM_UNICHAR 0x0109 - #define WM_KEYLAST 0x0109 - #define WM_APP 0x8000 - - - #define RID_INPUT 0x10000003 - - #define RIM_TYPEMOUSE 0x00000000 - #define RIM_TYPEKEYBOARD 0x00000001 - #define RIM_TYPEHID 0x00000002 - - #define RI_KEY_MAKE 0x0000 - #define RI_KEY_BREAK 0x0001 - #define RI_KEY_E0 0x0002 - #define RI_KEY_E1 0x0004 - #define RI_MOUSE_WHEEL 0x0400 - - #define RIDEV_NOLEGACY 0x00000030 - - #define MAPVK_VK_TO_VSC 0 - #define MAPVK_VSC_TO_VK 1 - #define MAPVK_VK_TO_CHAR 2 - #define MAPVK_VSC_TO_VK_EX 3 - - GB_DLL_IMPORT BOOL WINAPI RegisterRawInputDevices(RAWINPUTDEVICE const *raw_input_devices, UINT num_devices, UINT size); - GB_DLL_IMPORT UINT WINAPI GetRawInputData(HRAWINPUT raw_input, UINT ui_command, void *data, UINT *size, UINT size_header); - GB_DLL_IMPORT UINT WINAPI MapVirtualKeyW(UINT code, UINT map_type); - - - #define CS_DBLCLKS 0x0008 - #define CS_VREDRAW 0x0001 - #define CS_HREDRAW 0x0002 - - #define MB_OK 0x0000l - #define MB_ICONSTOP 0x0010l - #define MB_YESNO 0x0004l - #define MB_HELP 0x4000l - #define MB_ICONEXCLAMATION 0x0030l - - GB_DLL_IMPORT LRESULT WINAPI DefWindowProcW(HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam); - GB_DLL_IMPORT HGDIOBJ WINAPI GetStockObject(int object); - GB_DLL_IMPORT HMODULE WINAPI GetModuleHandleW(wchar_t const *); - GB_DLL_IMPORT ATOM WINAPI RegisterClassExW(WNDCLASSEXW const *wcx); // u16 == ATOM - GB_DLL_IMPORT int WINAPI MessageBoxW(void *wnd, wchar_t const *text, wchar_t const *caption, unsigned int type); - - - #define DM_BITSPERPEL 0x00040000l - #define DM_PELSWIDTH 0x00080000l - #define DM_PELSHEIGHT 0x00100000l - - #define CDS_FULLSCREEN 0x4 - #define DISP_CHANGE_SUCCESSFUL 0 - #define IDYES 6 - - #define WS_VISIBLE 0x10000000 - #define WS_THICKFRAME 0x00040000 - #define WS_MAXIMIZE 0x01000000 - #define WS_MAXIMIZEBOX 0x00010000 - #define WS_MINIMIZE 0x20000000 - #define WS_MINIMIZEBOX 0x00020000 - #define WS_POPUP 0x80000000 - #define WS_OVERLAPPED 0 - #define WS_OVERLAPPEDWINDOW 0xcf0000 - #define CW_USEDEFAULT 0x80000000 - #define WS_BORDER 0x800000 - #define WS_CAPTION 0xc00000 - #define WS_SYSMENU 0x80000 - - #define HWND_NOTOPMOST (HWND)(-2) - #define HWND_TOPMOST (HWND)(-1) - #define HWND_TOP (HWND)(+0) - #define HWND_BOTTOM (HWND)(+1) - #define SWP_NOSIZE 0x0001 - #define SWP_NOMOVE 0x0002 - #define SWP_NOZORDER 0x0004 - #define SWP_NOREDRAW 0x0008 - #define SWP_NOACTIVATE 0x0010 - #define SWP_FRAMECHANGED 0x0020 - #define SWP_SHOWWINDOW 0x0040 - #define SWP_HIDEWINDOW 0x0080 - #define SWP_NOCOPYBITS 0x0100 - #define SWP_NOOWNERZORDER 0x0200 - #define SWP_NOSENDCHANGING 0x0400 - - #define SW_HIDE 0 - #define SW_SHOWNORMAL 1 - #define SW_NORMAL 1 - #define SW_SHOWMINIMIZED 2 - #define SW_SHOWMAXIMIZED 3 - #define SW_MAXIMIZE 3 - #define SW_SHOWNOACTIVATE 4 - #define SW_SHOW 5 - #define SW_MINIMIZE 6 - #define SW_SHOWMINNOACTIVE 7 - #define SW_SHOWNA 8 - #define SW_RESTORE 9 - #define SW_SHOWDEFAULT 10 - #define SW_FORCEMINIMIZE 11 - #define SW_MAX 11 - - #define ENUM_CURRENT_SETTINGS cast(DWORD)-1 - #define ENUM_REGISTRY_SETTINGS cast(DWORD)-2 - - GB_DLL_IMPORT LONG WINAPI ChangeDisplaySettingsW(DEVMODEW *dev_mode, DWORD flags); - GB_DLL_IMPORT BOOL WINAPI AdjustWindowRect(RECT *rect, DWORD style, BOOL enu); - GB_DLL_IMPORT HWND WINAPI CreateWindowExW(DWORD ex_style, wchar_t const *class_name, wchar_t const *window_name, - DWORD style, int x, int y, int width, int height, HWND wnd_parent, - HMENU menu, HINSTANCE instance, void *param); - GB_DLL_IMPORT HMODULE WINAPI GetModuleHandleW(wchar_t const *); - GB_DLL_IMPORT HDC GetDC(HANDLE); - GB_DLL_IMPORT BOOL WINAPI GetWindowPlacement(HWND hWnd, WINDOWPLACEMENT *lpwndpl); - GB_DLL_IMPORT BOOL GetMonitorInfoW(HMONITOR hMonitor, MONITORINFO *lpmi); - GB_DLL_IMPORT HMONITOR MonitorFromWindow(HWND hwnd, DWORD dwFlags); - GB_DLL_IMPORT LONG WINAPI SetWindowLongW(HWND hWnd, int nIndex, LONG dwNewLong); - GB_DLL_IMPORT BOOL WINAPI SetWindowPos(HWND hWnd, HWND hWndInsertAfter, int X, int Y, int cx, int cy, UINT uFlags); - GB_DLL_IMPORT BOOL WINAPI SetWindowPlacement(HWND hWnd, WINDOWPLACEMENT const *lpwndpl); - GB_DLL_IMPORT BOOL WINAPI ShowWindow(HWND hWnd, int nCmdShow); - GB_DLL_IMPORT LONG_PTR WINAPI GetWindowLongPtrW(HWND wnd, int index); - - GB_DLL_IMPORT BOOL EnumDisplaySettingsW(wchar_t const *lpszDeviceName, DWORD iModeNum, DEVMODEW *lpDevMode); - GB_DLL_IMPORT void * WINAPI GlobalLock(HGLOBAL hMem); - GB_DLL_IMPORT BOOL WINAPI GlobalUnlock(HGLOBAL hMem); - GB_DLL_IMPORT HGLOBAL WINAPI GlobalAlloc(UINT uFlags, usize dwBytes); - GB_DLL_IMPORT HANDLE WINAPI GetClipboardData(UINT uFormat); - GB_DLL_IMPORT BOOL WINAPI IsClipboardFormatAvailable(UINT format); - GB_DLL_IMPORT BOOL WINAPI OpenClipboard(HWND hWndNewOwner); - GB_DLL_IMPORT BOOL WINAPI EmptyClipboard(void); - GB_DLL_IMPORT BOOL WINAPI CloseClipboard(void); - GB_DLL_IMPORT HANDLE WINAPI SetClipboardData(UINT uFormat, HANDLE hMem); - - #define PFD_TYPE_RGBA 0 - #define PFD_TYPE_COLORINDEX 1 - #define PFD_MAIN_PLANE 0 - #define PFD_OVERLAY_PLANE 1 - #define PFD_UNDERLAY_PLANE (-1) - #define PFD_DOUBLEBUFFER 1 - #define PFD_STEREO 2 - #define PFD_DRAW_TO_WINDOW 4 - #define PFD_DRAW_TO_BITMAP 8 - #define PFD_SUPPORT_GDI 16 - #define PFD_SUPPORT_OPENGL 32 - #define PFD_GENERIC_FORMAT 64 - #define PFD_NEED_PALETTE 128 - #define PFD_NEED_SYSTEM_PALETTE 0x00000100 - #define PFD_SWAP_EXCHANGE 0x00000200 - #define PFD_SWAP_COPY 0x00000400 - #define PFD_SWAP_LAYER_BUFFERS 0x00000800 - #define PFD_GENERIC_ACCELERATED 0x00001000 - #define PFD_DEPTH_DONTCARE 0x20000000 - #define PFD_DOUBLEBUFFER_DONTCARE 0x40000000 - #define PFD_STEREO_DONTCARE 0x80000000 - - #define GWLP_USERDATA -21 - - #define GWL_ID -12 - #define GWL_STYLE -16 - - GB_DLL_IMPORT BOOL WINAPI SetPixelFormat (HDC hdc, int pixel_format, PIXELFORMATDESCRIPTOR const *pfd); - GB_DLL_IMPORT int WINAPI ChoosePixelFormat(HDC hdc, PIXELFORMATDESCRIPTOR const *pfd); - GB_DLL_IMPORT HGLRC WINAPI wglCreateContext (HDC hdc); - GB_DLL_IMPORT BOOL WINAPI wglMakeCurrent (HDC hdc, HGLRC hglrc); - GB_DLL_IMPORT PROC WINAPI wglGetProcAddress(char const *str); - GB_DLL_IMPORT BOOL WINAPI wglDeleteContext (HGLRC hglrc); - - GB_DLL_IMPORT BOOL WINAPI SetForegroundWindow(HWND hWnd); - GB_DLL_IMPORT HWND WINAPI SetFocus(HWND hWnd); - GB_DLL_IMPORT LONG_PTR WINAPI SetWindowLongPtrW(HWND hWnd, int nIndex, LONG_PTR dwNewLong); - GB_DLL_IMPORT BOOL WINAPI GetClientRect(HWND hWnd, RECT *lpRect); - GB_DLL_IMPORT BOOL WINAPI IsIconic(HWND hWnd); - GB_DLL_IMPORT HWND WINAPI GetFocus(void); - GB_DLL_IMPORT int WINAPI ShowCursor(BOOL bShow); - GB_DLL_IMPORT SHORT WINAPI GetAsyncKeyState(int key); - GB_DLL_IMPORT BOOL WINAPI GetCursorPos(POINT *lpPoint); - GB_DLL_IMPORT BOOL WINAPI SetCursorPos(int x, int y); - GB_DLL_IMPORT BOOL ScreenToClient(HWND hWnd, POINT *lpPoint); - GB_DLL_IMPORT BOOL ClientToScreen(HWND hWnd, POINT *lpPoint); - GB_DLL_IMPORT BOOL WINAPI MoveWindow(HWND hWnd, int X, int Y, int nWidth, int nHeight, BOOL bRepaint); - GB_DLL_IMPORT BOOL WINAPI SetWindowTextW(HWND hWnd, wchar_t const *lpString); - GB_DLL_IMPORT DWORD WINAPI GetWindowLongW(HWND hWnd, int nIndex); - - - - - #define PM_NOREMOVE 0 - #define PM_REMOVE 1 - - GB_DLL_IMPORT BOOL WINAPI PeekMessageW(MSG *lpMsg, HWND hWnd, UINT wMsgFilterMin, UINT wMsgFilterMax, UINT wRemoveMsg); - GB_DLL_IMPORT BOOL WINAPI TranslateMessage(MSG const *lpMsg); - GB_DLL_IMPORT LRESULT WINAPI DispatchMessageW(MSG const *lpMsg); - - typedef enum - { - DIB_RGB_COLORS = 0x00, - DIB_PAL_COLORS = 0x01, - DIB_PAL_INDICES = 0x02 - } DIBColors; - - #define SRCCOPY (u32)0x00CC0020 - #define SRCPAINT (u32)0x00EE0086 - #define SRCAND (u32)0x008800C6 - #define SRCINVERT (u32)0x00660046 - #define SRCERASE (u32)0x00440328 - #define NOTSRCCOPY (u32)0x00330008 - #define NOTSRCERASE (u32)0x001100A6 - #define MERGECOPY (u32)0x00C000CA - #define MERGEPAINT (u32)0x00BB0226 - #define PATCOPY (u32)0x00F00021 - #define PATPAINT (u32)0x00FB0A09 - #define PATINVERT (u32)0x005A0049 - #define DSTINVERT (u32)0x00550009 - #define BLACKNESS (u32)0x00000042 - #define WHITENESS (u32)0x00FF0062 - - GB_DLL_IMPORT BOOL WINAPI SwapBuffers(HDC hdc); - GB_DLL_IMPORT BOOL WINAPI DestroyWindow(HWND hWnd); - GB_DLL_IMPORT int StretchDIBits(HDC hdc, int XDest, int YDest, int nDestWidth, int nDestHeight, - int XSrc, int YSrc, int nSrcWidth, int nSrcHeight, - void const *lpBits, /*BITMAPINFO*/void const *lpBitsInfo, UINT iUsage, DWORD dwRop); - // IMPORTANT TODO(bill): FIX THIS!!!! -#endif // Bill's Mini Windows.h - - - -#if defined(__GCC__) || defined(__GNUC__) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wattributes" -#pragma GCC diagnostic ignored "-Wmissing-braces" -#endif - -#if defined(_MSC_VER) -#pragma warning(push) -#pragma warning(disable:4201) -#pragma warning(disable:4127) // Conditional expression is constant -#endif - -void gb_assert_handler(char const *prefix, char const *condition, char const *file, i32 line, char const *msg, ...) { - gb_printf_err("%s(%d): %s: ", file, line, prefix); - if (condition) - gb_printf_err( "`%s` ", condition); - if (msg) { - va_list va; - va_start(va, msg); - gb_printf_err_va(msg, va); - va_end(va); - } - gb_printf_err("\n"); -} - -b32 gb_is_power_of_two(isize x) { - if (x <= 0) - return false; - return !(x & (x-1)); -} - -gb_inline void *gb_align_forward(void *ptr, isize alignment) { - uintptr p; - - GB_ASSERT(gb_is_power_of_two(alignment)); - - p = cast(uintptr)ptr; - return cast(void *)((p + (alignment-1)) &~ (alignment-1)); -} - - - -gb_inline void * gb_pointer_add (void *ptr, isize bytes) { return cast(void *)(cast(u8 *)ptr + bytes); } -gb_inline void * gb_pointer_sub (void *ptr, isize bytes) { return cast(void *)(cast(u8 *)ptr - bytes); } -gb_inline void const *gb_pointer_add_const(void const *ptr, isize bytes) { return cast(void const *)(cast(u8 const *)ptr + bytes); } -gb_inline void const *gb_pointer_sub_const(void const *ptr, isize bytes) { return cast(void const *)(cast(u8 const *)ptr - bytes); } -gb_inline isize gb_pointer_diff (void const *begin, void const *end) { return cast(isize)(cast(u8 const *)end - cast(u8 const *)begin); } - -gb_inline void gb_zero_size(void *ptr, isize size) { gb_memset(ptr, 0, size); } - - -#if defined(_MSC_VER) -#pragma intrinsic(__movsb) -#endif - -gb_inline void *gb_memcopy(void *dest, void const *source, isize n) { -#if defined(_MSC_VER) - if (dest == NULL) { - return NULL; - } - // TODO(bill): Is this good enough? - __movsb(cast(u8 *)dest, cast(u8 *)source, n); -// #elif defined(GB_SYSTEM_OSX) || defined(GB_SYSTEM_UNIX) - // NOTE(zangent): I assume there's a reason this isn't being used elsewhere, - // but casting pointers as arguments to an __asm__ call is considered an - // error on MacOS and (I think) Linux - // TODO(zangent): Figure out how to refactor the asm code so it works on MacOS, - // since this is probably not the way the author intended this to work. - // memcpy(dest, source, n); -#elif defined(GB_CPU_X86) - if (dest == NULL) { - return NULL; - } - - void *dest_copy = dest; - __asm__ __volatile__("rep movsb" : "+D"(dest_copy), "+S"(source), "+c"(n) : : "memory"); -#else - u8 *d = cast(u8 *)dest; - u8 const *s = cast(u8 const *)source; - u32 w, x; - - if (dest == NULL) { - return NULL; - } - - for (; cast(uintptr)s % 4 && n; n--) { - *d++ = *s++; - } - - if (cast(uintptr)d % 4 == 0) { - for (; n >= 16; - s += 16, d += 16, n -= 16) { - *cast(u32 *)(d+ 0) = *cast(u32 *)(s+ 0); - *cast(u32 *)(d+ 4) = *cast(u32 *)(s+ 4); - *cast(u32 *)(d+ 8) = *cast(u32 *)(s+ 8); - *cast(u32 *)(d+12) = *cast(u32 *)(s+12); - } - if (n & 8) { - *cast(u32 *)(d+0) = *cast(u32 *)(s+0); - *cast(u32 *)(d+4) = *cast(u32 *)(s+4); - d += 8; - s += 8; - } - if (n&4) { - *cast(u32 *)(d+0) = *cast(u32 *)(s+0); - d += 4; - s += 4; - } - if (n&2) { - *d++ = *s++; *d++ = *s++; - } - if (n&1) { - *d = *s; - } - return dest; - } - - if (n >= 32) { - #if __BYTE_ORDER == __BIG_ENDIAN - #define LS << - #define RS >> - #else - #define LS >> - #define RS << - #endif - switch (cast(uintptr)d % 4) { - case 1: { - w = *cast(u32 *)s; - *d++ = *s++; - *d++ = *s++; - *d++ = *s++; - n -= 3; - while (n > 16) { - x = *cast(u32 *)(s+1); - *cast(u32 *)(d+0) = (w LS 24) | (x RS 8); - w = *cast(u32 *)(s+5); - *cast(u32 *)(d+4) = (x LS 24) | (w RS 8); - x = *cast(u32 *)(s+9); - *cast(u32 *)(d+8) = (w LS 24) | (x RS 8); - w = *cast(u32 *)(s+13); - *cast(u32 *)(d+12) = (x LS 24) | (w RS 8); - - s += 16; - d += 16; - n -= 16; - } - } break; - case 2: { - w = *cast(u32 *)s; - *d++ = *s++; - *d++ = *s++; - n -= 2; - while (n > 17) { - x = *cast(u32 *)(s+2); - *cast(u32 *)(d+0) = (w LS 16) | (x RS 16); - w = *cast(u32 *)(s+6); - *cast(u32 *)(d+4) = (x LS 16) | (w RS 16); - x = *cast(u32 *)(s+10); - *cast(u32 *)(d+8) = (w LS 16) | (x RS 16); - w = *cast(u32 *)(s+14); - *cast(u32 *)(d+12) = (x LS 16) | (w RS 16); - - s += 16; - d += 16; - n -= 16; - } - } break; - case 3: { - w = *cast(u32 *)s; - *d++ = *s++; - n -= 1; - while (n > 18) { - x = *cast(u32 *)(s+3); - *cast(u32 *)(d+0) = (w LS 8) | (x RS 24); - w = *cast(u32 *)(s+7); - *cast(u32 *)(d+4) = (x LS 8) | (w RS 24); - x = *cast(u32 *)(s+11); - *cast(u32 *)(d+8) = (w LS 8) | (x RS 24); - w = *cast(u32 *)(s+15); - *cast(u32 *)(d+12) = (x LS 8) | (w RS 24); - - s += 16; - d += 16; - n -= 16; - } - } break; - default: break; // NOTE(bill): Do nowt! - } - #undef LS - #undef RS - if (n & 16) { - *d++ = *s++; *d++ = *s++; *d++ = *s++; *d++ = *s++; - *d++ = *s++; *d++ = *s++; *d++ = *s++; *d++ = *s++; - *d++ = *s++; *d++ = *s++; *d++ = *s++; *d++ = *s++; - *d++ = *s++; *d++ = *s++; *d++ = *s++; *d++ = *s++; - } - if (n & 8) { - *d++ = *s++; *d++ = *s++; *d++ = *s++; *d++ = *s++; - *d++ = *s++; *d++ = *s++; *d++ = *s++; *d++ = *s++; - } - if (n & 4) { - *d++ = *s++; *d++ = *s++; *d++ = *s++; *d++ = *s++; - } - if (n & 2) { - *d++ = *s++; *d++ = *s++; - } - if (n & 1) { - *d = *s; - } - } - -#endif - return dest; -} - -gb_inline void *gb_memmove(void *dest, void const *source, isize n) { - u8 *d = cast(u8 *)dest; - u8 const *s = cast(u8 const *)source; - - if (dest == NULL) { - return NULL; - } - - if (d == s) { - return d; - } - if (s+n <= d || d+n <= s) { // NOTE(bill): Non-overlapping - return gb_memcopy(d, s, n); - } - - if (d < s) { - if (cast(uintptr)s % gb_size_of(isize) == cast(uintptr)d % gb_size_of(isize)) { - while (cast(uintptr)d % gb_size_of(isize)) { - if (!n--) return dest; - *d++ = *s++; - } - while (n>=gb_size_of(isize)) { - *cast(isize *)d = *cast(isize *)s; - n -= gb_size_of(isize); - d += gb_size_of(isize); - s += gb_size_of(isize); - } - } - for (; n; n--) *d++ = *s++; - } else { - if ((cast(uintptr)s % gb_size_of(isize)) == (cast(uintptr)d % gb_size_of(isize))) { - while (cast(uintptr)(d+n) % gb_size_of(isize)) { - if (!n--) - return dest; - d[n] = s[n]; - } - while (n >= gb_size_of(isize)) { - n -= gb_size_of(isize); - *cast(isize *)(d+n) = *cast(isize *)(s+n); - } - } - while (n) n--, d[n] = s[n]; - } - - return dest; -} - -gb_inline void *gb_memset(void *dest, u8 c, isize n) { - u8 *s = cast(u8 *)dest; - isize k; - u32 c32 = ((u32)-1)/255 * c; - - if (dest == NULL) { - return NULL; - } - - if (n == 0) - return dest; - s[0] = s[n-1] = c; - if (n < 3) - return dest; - s[1] = s[n-2] = c; - s[2] = s[n-3] = c; - if (n < 7) - return dest; - s[3] = s[n-4] = c; - if (n < 9) - return dest; - - k = -cast(intptr)s & 3; - s += k; - n -= k; - n &= -4; - - *cast(u32 *)(s+0) = c32; - *cast(u32 *)(s+n-4) = c32; - if (n < 9) { - return dest; - } - *cast(u32 *)(s + 4) = c32; - *cast(u32 *)(s + 8) = c32; - *cast(u32 *)(s+n-12) = c32; - *cast(u32 *)(s+n- 8) = c32; - if (n < 25) { - return dest; - } - *cast(u32 *)(s + 12) = c32; - *cast(u32 *)(s + 16) = c32; - *cast(u32 *)(s + 20) = c32; - *cast(u32 *)(s + 24) = c32; - *cast(u32 *)(s+n-28) = c32; - *cast(u32 *)(s+n-24) = c32; - *cast(u32 *)(s+n-20) = c32; - *cast(u32 *)(s+n-16) = c32; - - k = 24 + (cast(uintptr)s & 4); - s += k; - n -= k; - - - { - u64 c64 = (cast(u64)c32 << 32) | c32; - while (n > 31) { - *cast(u64 *)(s+0) = c64; - *cast(u64 *)(s+8) = c64; - *cast(u64 *)(s+16) = c64; - *cast(u64 *)(s+24) = c64; - - n -= 32; - s += 32; - } - } - - return dest; -} - -gb_inline i32 gb_memcompare(void const *s1, void const *s2, isize size) { - // TODO(bill): Heavily optimize - u8 const *s1p8 = cast(u8 const *)s1; - u8 const *s2p8 = cast(u8 const *)s2; - - if (s1 == NULL || s2 == NULL) { - return 0; - } - - while (size--) { - if (*s1p8 != *s2p8) { - return (*s1p8 - *s2p8); - } - s1p8++, s2p8++; - } - return 0; -} - -void gb_memswap(void *i, void *j, isize size) { - if (i == j) return; - - if (size == 4) { - gb_swap(u32, *cast(u32 *)i, *cast(u32 *)j); - } else if (size == 8) { - gb_swap(u64, *cast(u64 *)i, *cast(u64 *)j); - } else if (size < 8) { - u8 *a = cast(u8 *)i; - u8 *b = cast(u8 *)j; - if (a != b) { - while (size--) { - gb_swap(u8, *a, *b); - a++, b++; - } - } - } else { - char buffer[256]; - - // TODO(bill): Is the recursion ever a problem? - while (size > gb_size_of(buffer)) { - gb_memswap(i, j, gb_size_of(buffer)); - i = gb_pointer_add(i, gb_size_of(buffer)); - j = gb_pointer_add(j, gb_size_of(buffer)); - size -= gb_size_of(buffer); - } - - gb_memcopy(buffer, i, size); - gb_memcopy(i, j, size); - gb_memcopy(j, buffer, size); - } -} - -#define GB__ONES (cast(usize)-1/U8_MAX) -#define GB__HIGHS (GB__ONES * (U8_MAX/2+1)) -#define GB__HAS_ZERO(x) ((x)-GB__ONES & ~(x) & GB__HIGHS) - - -void const *gb_memchr(void const *data, u8 c, isize n) { - u8 const *s = cast(u8 const *)data; - while ((cast(uintptr)s & (sizeof(usize)-1)) && - n && *s != c) { - s++; - n--; - } - if (n && *s != c) { - isize const *w; - isize k = GB__ONES * c; - w = cast(isize const *)s; - while (n >= gb_size_of(isize) && !GB__HAS_ZERO(*w ^ k)) { - w++; - n -= gb_size_of(isize); - } - s = cast(u8 const *)w; - while (n && *s != c) { - s++; - n--; - } - } - - return n ? cast(void const *)s : NULL; -} - - -void const *gb_memrchr(void const *data, u8 c, isize n) { - u8 const *s = cast(u8 const *)data; - while (n--) { - if (s[n] == c) - return cast(void const *)(s + n); - } - return NULL; -} - - - -gb_inline void *gb_alloc_align (gbAllocator a, isize size, isize alignment) { return a.proc(a.data, gbAllocation_Alloc, size, alignment, NULL, 0, GB_DEFAULT_ALLOCATOR_FLAGS); } -gb_inline void *gb_alloc (gbAllocator a, isize size) { return gb_alloc_align(a, size, GB_DEFAULT_MEMORY_ALIGNMENT); } -gb_inline void gb_free (gbAllocator a, void *ptr) { if (ptr != NULL) a.proc(a.data, gbAllocation_Free, 0, 0, ptr, 0, GB_DEFAULT_ALLOCATOR_FLAGS); } -gb_inline void gb_free_all (gbAllocator a) { a.proc(a.data, gbAllocation_FreeAll, 0, 0, NULL, 0, GB_DEFAULT_ALLOCATOR_FLAGS); } -gb_inline void *gb_resize (gbAllocator a, void *ptr, isize old_size, isize new_size) { return gb_resize_align(a, ptr, old_size, new_size, GB_DEFAULT_MEMORY_ALIGNMENT); } -gb_inline void *gb_resize_align(gbAllocator a, void *ptr, isize old_size, isize new_size, isize alignment) { return a.proc(a.data, gbAllocation_Resize, new_size, alignment, ptr, old_size, GB_DEFAULT_ALLOCATOR_FLAGS); } - -gb_inline void *gb_alloc_copy (gbAllocator a, void const *src, isize size) { - return gb_memcopy(gb_alloc(a, size), src, size); -} -gb_inline void *gb_alloc_copy_align(gbAllocator a, void const *src, isize size, isize alignment) { - return gb_memcopy(gb_alloc_align(a, size, alignment), src, size); -} - -gb_inline char *gb_alloc_str(gbAllocator a, char const *str) { - return gb_alloc_str_len(a, str, gb_strlen(str)); -} - -gb_inline char *gb_alloc_str_len(gbAllocator a, char const *str, isize len) { - char *result; - result = cast(char *)gb_alloc_copy(a, str, len+1); - result[len] = '\0'; - return result; -} - - -gb_inline void *gb_default_resize_align(gbAllocator a, void *old_memory, isize old_size, isize new_size, isize alignment) { - if (!old_memory) return gb_alloc_align(a, new_size, alignment); - - if (new_size == 0) { - gb_free(a, old_memory); - return NULL; - } - - if (new_size < old_size) - new_size = old_size; - - if (old_size == new_size) { - return old_memory; - } else { - void *new_memory = gb_alloc_align(a, new_size, alignment); - if (!new_memory) return NULL; - gb_memmove(new_memory, old_memory, gb_min(new_size, old_size)); - gb_free(a, old_memory); - return new_memory; - } -} - - - - -//////////////////////////////////////////////////////////////// -// -// Concurrency -// -// -// IMPORTANT TODO(bill): Use compiler intrinsics for the atomics - -#if defined(GB_COMPILER_MSVC) && !defined(GB_COMPILER_CLANG) -gb_inline i32 gb_atomic32_load (gbAtomic32 const volatile *a) { return a->value; } -gb_inline void gb_atomic32_store(gbAtomic32 volatile *a, i32 value) { a->value = value; } - -gb_inline i32 gb_atomic32_compare_exchange(gbAtomic32 volatile *a, i32 expected, i32 desired) { - return _InterlockedCompareExchange(cast(long volatile *)a, desired, expected); -} -gb_inline i32 gb_atomic32_exchanged(gbAtomic32 volatile *a, i32 desired) { - return _InterlockedExchange(cast(long volatile *)a, desired); -} -gb_inline i32 gb_atomic32_fetch_add(gbAtomic32 volatile *a, i32 operand) { - return _InterlockedExchangeAdd(cast(long volatile *)a, operand); -} -gb_inline i32 gb_atomic32_fetch_and(gbAtomic32 volatile *a, i32 operand) { - return _InterlockedAnd(cast(long volatile *)a, operand); -} -gb_inline i32 gb_atomic32_fetch_or(gbAtomic32 volatile *a, i32 operand) { - return _InterlockedOr(cast(long volatile *)a, operand); -} - -gb_inline i64 gb_atomic64_load(gbAtomic64 const volatile *a) { -#if defined(GB_ARCH_64_BIT) - return a->value; -#elif GB_CPU_X86 - // NOTE(bill): The most compatible way to get an atomic 64-bit load on x86 is with cmpxchg8b - i64 result; - __asm { - mov esi, a; - mov ebx, eax; - mov ecx, edx; - lock cmpxchg8b [esi]; - mov dword ptr result, eax; - mov dword ptr result[4], edx; - } - return result; -#else -#error TODO(bill): atomics for this CPU -#endif -} - -gb_inline void gb_atomic64_store(gbAtomic64 volatile *a, i64 value) { -#if defined(GB_ARCH_64_BIT) - a->value = value; -#elif GB_CPU_X86 - // NOTE(bill): The most compatible way to get an atomic 64-bit store on x86 is with cmpxchg8b - __asm { - mov esi, a; - mov ebx, dword ptr value; - mov ecx, dword ptr value[4]; - retry: - cmpxchg8b [esi]; - jne retry; - } -#else -#error TODO(bill): atomics for this CPU -#endif -} - -gb_inline i64 gb_atomic64_compare_exchange(gbAtomic64 volatile *a, i64 expected, i64 desired) { - return _InterlockedCompareExchange64(cast(i64 volatile *)a, desired, expected); -} - -gb_inline i64 gb_atomic64_exchanged(gbAtomic64 volatile *a, i64 desired) { -#if defined(GB_ARCH_64_BIT) - return _InterlockedExchange64(cast(i64 volatile *)a, desired); -#elif GB_CPU_X86 - i64 expected = a->value; - for (;;) { - i64 original = _InterlockedCompareExchange64(cast(i64 volatile *)a, desired, expected); - if (original == expected) - return original; - expected = original; - } -#else -#error TODO(bill): atomics for this CPU -#endif -} - -gb_inline i64 gb_atomic64_fetch_add(gbAtomic64 volatile *a, i64 operand) { -#if defined(GB_ARCH_64_BIT) - return _InterlockedExchangeAdd64(cast(i64 volatile *)a, operand); -#elif GB_CPU_X86 - i64 expected = a->value; - for (;;) { - i64 original = _InterlockedCompareExchange64(cast(i64 volatile *)a, expected + operand, expected); - if (original == expected) - return original; - expected = original; - } -#else -#error TODO(bill): atomics for this CPU -#endif -} - -gb_inline i64 gb_atomic64_fetch_and(gbAtomic64 volatile *a, i64 operand) { -#if defined(GB_ARCH_64_BIT) - return _InterlockedAnd64(cast(i64 volatile *)a, operand); -#elif GB_CPU_X86 - i64 expected = a->value; - for (;;) { - i64 original = _InterlockedCompareExchange64(cast(i64 volatile *)a, expected & operand, expected); - if (original == expected) - return original; - expected = original; - } -#else -#error TODO(bill): atomics for this CPU -#endif -} - -gb_inline i64 gb_atomic64_fetch_or(gbAtomic64 volatile *a, i64 operand) { -#if defined(GB_ARCH_64_BIT) - return _InterlockedOr64(cast(i64 volatile *)a, operand); -#elif GB_CPU_X86 - i64 expected = a->value; - for (;;) { - i64 original = _InterlockedCompareExchange64(cast(i64 volatile *)a, expected | operand, expected); - if (original == expected) - return original; - expected = original; - } -#else -#error TODO(bill): atomics for this CPU -#endif -} - - - -#elif defined(GB_CPU_X86) - -gb_inline i32 gb_atomic32_load (gbAtomic32 const volatile *a) { return a->value; } -gb_inline void gb_atomic32_store(gbAtomic32 volatile *a, i32 value) { a->value = value; } - -gb_inline i32 gb_atomic32_compare_exchange(gbAtomic32 volatile *a, i32 expected, i32 desired) { - i32 original; - __asm__ volatile( - "lock; cmpxchgl %2, %1" - : "=a"(original), "+m"(a->value) - : "q"(desired), "0"(expected) - ); - return original; -} - -gb_inline i32 gb_atomic32_exchanged(gbAtomic32 volatile *a, i32 desired) { - // NOTE(bill): No lock prefix is necessary for xchgl - i32 original; - __asm__ volatile( - "xchgl %0, %1" - : "=r"(original), "+m"(a->value) - : "0"(desired) - ); - return original; -} - -gb_inline i32 gb_atomic32_fetch_add(gbAtomic32 volatile *a, i32 operand) { - i32 original; - __asm__ volatile( - "lock; xaddl %0, %1" - : "=r"(original), "+m"(a->value) - : "0"(operand) - ); - return original; -} - -gb_inline i32 gb_atomic32_fetch_and(gbAtomic32 volatile *a, i32 operand) { - i32 original; - i32 tmp; - __asm__ volatile( - "1: movl %1, %0\n" - " movl %0, %2\n" - " andl %3, %2\n" - " lock; cmpxchgl %2, %1\n" - " jne 1b" - : "=&a"(original), "+m"(a->value), "=&r"(tmp) - : "r"(operand) - ); - return original; -} - -gb_inline i32 gb_atomic32_fetch_or(gbAtomic32 volatile *a, i32 operand) { - i32 original; - i32 temp; - __asm__ volatile( - "1: movl %1, %0\n" - " movl %0, %2\n" - " orl %3, %2\n" - " lock; cmpxchgl %2, %1\n" - " jne 1b" - : "=&a"(original), "+m"(a->value), "=&r"(temp) - : "r"(operand) - ); - return original; -} - - -gb_inline i64 gb_atomic64_load(gbAtomic64 const volatile *a) { -#if defined(GB_ARCH_64_BIT) - return a->value; -#else - i64 original; - __asm__ volatile( - "movl %%ebx, %%eax\n" - "movl %%ecx, %%edx\n" - "lock; cmpxchg8b %1" - : "=&A"(original) - : "m"(a->value) - ); - return original; -#endif -} - -gb_inline void gb_atomic64_store(gbAtomic64 volatile *a, i64 value) { -#if defined(GB_ARCH_64_BIT) - a->value = value; -#else - i64 expected = a->value; - __asm__ volatile( - "1: cmpxchg8b %0\n" - " jne 1b" - : "=m"(a->value) - : "b"((i32)value), "c"((i32)(value >> 32)), "A"(expected) - ); -#endif -} - -gb_inline i64 gb_atomic64_compare_exchange(gbAtomic64 volatile *a, i64 expected, i64 desired) { -#if defined(GB_ARCH_64_BIT) - i64 original; - __asm__ volatile( - "lock; cmpxchgq %2, %1" - : "=a"(original), "+m"(a->value) - : "q"(desired), "0"(expected) - ); - return original; -#else - i64 original; - __asm__ volatile( - "lock; cmpxchg8b %1" - : "=A"(original), "+m"(a->value) - : "b"((i32)desired), "c"((i32)(desired >> 32)), "0"(expected) - ); - return original; -#endif -} - -gb_inline i64 gb_atomic64_exchanged(gbAtomic64 volatile *a, i64 desired) { -#if defined(GB_ARCH_64_BIT) - i64 original; - __asm__ volatile( - "xchgq %0, %1" - : "=r"(original), "+m"(a->value) - : "0"(desired) - ); - return original; -#else - i64 original = a->value; - for (;;) { - i64 previous = gb_atomic64_compare_exchange(a, original, desired); - if (original == previous) - return original; - original = previous; - } -#endif -} - -gb_inline i64 gb_atomic64_fetch_add(gbAtomic64 volatile *a, i64 operand) { -#if defined(GB_ARCH_64_BIT) - i64 original; - __asm__ volatile( - "lock; xaddq %0, %1" - : "=r"(original), "+m"(a->value) - : "0"(operand) - ); - return original; -#else - for (;;) { - i64 original = a->value; - if (gb_atomic64_compare_exchange(a, original, original + operand) == original) - return original; - } -#endif -} - -gb_inline i64 gb_atomic64_fetch_and(gbAtomic64 volatile *a, i64 operand) { -#if defined(GB_ARCH_64_BIT) - i64 original; - i64 tmp; - __asm__ volatile( - "1: movq %1, %0\n" - " movq %0, %2\n" - " andq %3, %2\n" - " lock; cmpxchgq %2, %1\n" - " jne 1b" - : "=&a"(original), "+m"(a->value), "=&r"(tmp) - : "r"(operand) - ); - return original; -#else - for (;;) { - i64 original = a->value; - if (gb_atomic64_compare_exchange(a, original, original & operand) == original) - return original; - } -#endif -} - -gb_inline i64 gb_atomic64_fetch_or(gbAtomic64 volatile *a, i64 operand) { -#if defined(GB_ARCH_64_BIT) - i64 original; - i64 temp; - __asm__ volatile( - "1: movq %1, %0\n" - " movq %0, %2\n" - " orq %3, %2\n" - " lock; cmpxchgq %2, %1\n" - " jne 1b" - : "=&a"(original), "+m"(a->value), "=&r"(temp) - : "r"(operand) - ); - return original; -#else - for (;;) { - i64 original = a->value; - if (gb_atomic64_compare_exchange(a, original, original | operand) == original) - return original; - } -#endif -} - -#else -#error TODO(bill): Implement Atomics for this CPU -#endif - -gb_inline b32 gb_atomic32_spin_lock(gbAtomic32 volatile *a, isize time_out) { - i32 old_value = gb_atomic32_compare_exchange(a, 1, 0); - i32 counter = 0; - while (old_value != 0 && (time_out < 0 || counter++ < time_out)) { - gb_yield_thread(); - old_value = gb_atomic32_compare_exchange(a, 1, 0); - gb_mfence(); - } - return old_value == 0; -} -gb_inline void gb_atomic32_spin_unlock(gbAtomic32 volatile *a) { - gb_atomic32_store(a, 0); - gb_mfence(); -} - -gb_inline b32 gb_atomic64_spin_lock(gbAtomic64 volatile *a, isize time_out) { - i64 old_value = gb_atomic64_compare_exchange(a, 1, 0); - i64 counter = 0; - while (old_value != 0 && (time_out < 0 || counter++ < time_out)) { - gb_yield_thread(); - old_value = gb_atomic64_compare_exchange(a, 1, 0); - gb_mfence(); - } - return old_value == 0; -} - -gb_inline void gb_atomic64_spin_unlock(gbAtomic64 volatile *a) { - gb_atomic64_store(a, 0); - gb_mfence(); -} - -gb_inline b32 gb_atomic32_try_acquire_lock(gbAtomic32 volatile *a) { - i32 old_value; - gb_yield_thread(); - old_value = gb_atomic32_compare_exchange(a, 1, 0); - gb_mfence(); - return old_value == 0; -} - -gb_inline b32 gb_atomic64_try_acquire_lock(gbAtomic64 volatile *a) { - i64 old_value; - gb_yield_thread(); - old_value = gb_atomic64_compare_exchange(a, 1, 0); - gb_mfence(); - return old_value == 0; -} - - - -#if defined(GB_ARCH_32_BIT) - -gb_inline void *gb_atomic_ptr_load(gbAtomicPtr const volatile *a) { - return cast(void *)cast(intptr)gb_atomic32_load(cast(gbAtomic32 const volatile *)a); -} -gb_inline void gb_atomic_ptr_store(gbAtomicPtr volatile *a, void *value) { - gb_atomic32_store(cast(gbAtomic32 volatile *)a, cast(i32)cast(intptr)value); -} -gb_inline void *gb_atomic_ptr_compare_exchange(gbAtomicPtr volatile *a, void *expected, void *desired) { - return cast(void *)cast(intptr)gb_atomic32_compare_exchange(cast(gbAtomic32 volatile *)a, cast(i32)cast(intptr)expected, cast(i32)cast(intptr)desired); -} -gb_inline void *gb_atomic_ptr_exchanged(gbAtomicPtr volatile *a, void *desired) { - return cast(void *)cast(intptr)gb_atomic32_exchanged(cast(gbAtomic32 volatile *)a, cast(i32)cast(intptr)desired); -} -gb_inline void *gb_atomic_ptr_fetch_add(gbAtomicPtr volatile *a, void *operand) { - return cast(void *)cast(intptr)gb_atomic32_fetch_add(cast(gbAtomic32 volatile *)a, cast(i32)cast(intptr)operand); -} -gb_inline void *gb_atomic_ptr_fetch_and(gbAtomicPtr volatile *a, void *operand) { - return cast(void *)cast(intptr)gb_atomic32_fetch_and(cast(gbAtomic32 volatile *)a, cast(i32)cast(intptr)operand); -} -gb_inline void *gb_atomic_ptr_fetch_or(gbAtomicPtr volatile *a, void *operand) { - return cast(void *)cast(intptr)gb_atomic32_fetch_or(cast(gbAtomic32 volatile *)a, cast(i32)cast(intptr)operand); -} -gb_inline b32 gb_atomic_ptr_spin_lock(gbAtomicPtr volatile *a, isize time_out) { - return gb_atomic32_spin_lock(cast(gbAtomic32 volatile *)a, time_out); -} -gb_inline void gb_atomic_ptr_spin_unlock(gbAtomicPtr volatile *a) { - gb_atomic32_spin_unlock(cast(gbAtomic32 volatile *)a); -} -gb_inline b32 gb_atomic_ptr_try_acquire_lock(gbAtomicPtr volatile *a) { - return gb_atomic32_try_acquire_lock(cast(gbAtomic32 volatile *)a); -} - -#elif defined(GB_ARCH_64_BIT) - -gb_inline void *gb_atomic_ptr_load(gbAtomicPtr const volatile *a) { - return cast(void *)cast(intptr)gb_atomic64_load(cast(gbAtomic64 const volatile *)a); -} -gb_inline void gb_atomic_ptr_store(gbAtomicPtr volatile *a, void *value) { - gb_atomic64_store(cast(gbAtomic64 volatile *)a, cast(i64)cast(intptr)value); -} -gb_inline void *gb_atomic_ptr_compare_exchange(gbAtomicPtr volatile *a, void *expected, void *desired) { - return cast(void *)cast(intptr)gb_atomic64_compare_exchange(cast(gbAtomic64 volatile *)a, cast(i64)cast(intptr)expected, cast(i64)cast(intptr)desired); -} -gb_inline void *gb_atomic_ptr_exchanged(gbAtomicPtr volatile *a, void *desired) { - return cast(void *)cast(intptr)gb_atomic64_exchanged(cast(gbAtomic64 volatile *)a, cast(i64)cast(intptr)desired); -} -gb_inline void *gb_atomic_ptr_fetch_add(gbAtomicPtr volatile *a, void *operand) { - return cast(void *)cast(intptr)gb_atomic64_fetch_add(cast(gbAtomic64 volatile *)a, cast(i64)cast(intptr)operand); -} -gb_inline void *gb_atomic_ptr_fetch_and(gbAtomicPtr volatile *a, void *operand) { - return cast(void *)cast(intptr)gb_atomic64_fetch_and(cast(gbAtomic64 volatile *)a, cast(i64)cast(intptr)operand); -} -gb_inline void *gb_atomic_ptr_fetch_or(gbAtomicPtr volatile *a, void *operand) { - return cast(void *)cast(intptr)gb_atomic64_fetch_or(cast(gbAtomic64 volatile *)a, cast(i64)cast(intptr)operand); -} -gb_inline b32 gb_atomic_ptr_spin_lock(gbAtomicPtr volatile *a, isize time_out) { - return gb_atomic64_spin_lock(cast(gbAtomic64 volatile *)a, time_out); -} -gb_inline void gb_atomic_ptr_spin_unlock(gbAtomicPtr volatile *a) { - gb_atomic64_spin_unlock(cast(gbAtomic64 volatile *)a); -} -gb_inline b32 gb_atomic_ptr_try_acquire_lock(gbAtomicPtr volatile *a) { - return gb_atomic64_try_acquire_lock(cast(gbAtomic64 volatile *)a); -} -#endif - - -gb_inline void gb_yield_thread(void) { -#if defined(GB_SYSTEM_WINDOWS) - _mm_pause(); -#elif defined(GB_SYSTEM_OSX) - __asm__ volatile ("" : : : "memory"); -#elif defined(GB_CPU_X86) - _mm_pause(); -#else -#error Unknown architecture -#endif -} - -gb_inline void gb_mfence(void) { -#if defined(GB_SYSTEM_WINDOWS) - _ReadWriteBarrier(); -#elif defined(GB_SYSTEM_OSX) - __sync_synchronize(); -#elif defined(GB_CPU_X86) - _mm_mfence(); -#else -#error Unknown architecture -#endif -} - -gb_inline void gb_sfence(void) { -#if defined(GB_SYSTEM_WINDOWS) - _WriteBarrier(); -#elif defined(GB_SYSTEM_OSX) - __asm__ volatile ("" : : : "memory"); -#elif defined(GB_CPU_X86) - _mm_sfence(); -#else -#error Unknown architecture -#endif -} - -gb_inline void gb_lfence(void) { -#if defined(GB_SYSTEM_WINDOWS) - _ReadBarrier(); -#elif defined(GB_SYSTEM_OSX) - __asm__ volatile ("" : : : "memory"); -#elif defined(GB_CPU_X86) - _mm_lfence(); -#else -#error Unknown architecture -#endif -} - - -gb_inline void gb_semaphore_release(gbSemaphore *s) { gb_semaphore_post(s, 1); } - -#if defined(GB_SYSTEM_WINDOWS) - gb_inline void gb_semaphore_init (gbSemaphore *s) { s->win32_handle = CreateSemaphoreA(NULL, 0, I32_MAX, NULL); } - gb_inline void gb_semaphore_destroy(gbSemaphore *s) { CloseHandle(s->win32_handle); } - gb_inline void gb_semaphore_post (gbSemaphore *s, i32 count) { ReleaseSemaphore(s->win32_handle, count, NULL); } - gb_inline void gb_semaphore_wait (gbSemaphore *s) { WaitForSingleObject(s->win32_handle, INFINITE); } - -#elif defined(GB_SYSTEM_OSX) - gb_inline void gb_semaphore_init (gbSemaphore *s) { semaphore_create(mach_task_self(), &s->osx_handle, SYNC_POLICY_FIFO, 0); } - gb_inline void gb_semaphore_destroy(gbSemaphore *s) { semaphore_destroy(mach_task_self(), s->osx_handle); } - gb_inline void gb_semaphore_post (gbSemaphore *s, i32 count) { while (count --> 0) semaphore_signal(s->osx_handle); } - gb_inline void gb_semaphore_wait (gbSemaphore *s) { semaphore_wait(s->osx_handle); } - -#elif defined(GB_SYSTEM_UNIX) - gb_inline void gb_semaphore_init (gbSemaphore *s) { sem_init(&s->unix_handle, 0, 0); } - gb_inline void gb_semaphore_destroy(gbSemaphore *s) { sem_destroy(&s->unix_handle); } - gb_inline void gb_semaphore_post (gbSemaphore *s, i32 count) { while (count --> 0) sem_post(&s->unix_handle); } - gb_inline void gb_semaphore_wait (gbSemaphore *s) { int i; do { i = sem_wait(&s->unix_handle); } while (i == -1 && errno == EINTR); } - -#else -#error -#endif - -gb_inline void gb_mutex_init(gbMutex *m) { -#if defined(GB_SYSTEM_WINDOWS) - InitializeCriticalSection(&m->win32_critical_section); -#else - pthread_mutexattr_init(&m->pthread_mutexattr); - pthread_mutexattr_settype(&m->pthread_mutexattr, PTHREAD_MUTEX_RECURSIVE); - pthread_mutex_init(&m->pthread_mutex, &m->pthread_mutexattr); -#endif -} - -gb_inline void gb_mutex_destroy(gbMutex *m) { -#if defined(GB_SYSTEM_WINDOWS) - DeleteCriticalSection(&m->win32_critical_section); -#else - pthread_mutex_destroy(&m->pthread_mutex); -#endif -} - -gb_inline void gb_mutex_lock(gbMutex *m) { -#if defined(GB_SYSTEM_WINDOWS) - EnterCriticalSection(&m->win32_critical_section); -#else - pthread_mutex_lock(&m->pthread_mutex); -#endif -} - -gb_inline b32 gb_mutex_try_lock(gbMutex *m) { -#if defined(GB_SYSTEM_WINDOWS) - return TryEnterCriticalSection(&m->win32_critical_section) != 0; -#else - return pthread_mutex_trylock(&m->pthread_mutex) == 0; -#endif -} - -gb_inline void gb_mutex_unlock(gbMutex *m) { -#if defined(GB_SYSTEM_WINDOWS) - LeaveCriticalSection(&m->win32_critical_section); -#else - pthread_mutex_unlock(&m->pthread_mutex); -#endif -} - - - - - - - -void gb_thread_init(gbThread *t) { - gb_zero_item(t); -#if defined(GB_SYSTEM_WINDOWS) - t->win32_handle = INVALID_HANDLE_VALUE; -#else - t->posix_handle = 0; -#endif - gb_semaphore_init(&t->semaphore); -} - -void gb_thread_destroy(gbThread *t) { - if (t->is_running) gb_thread_join(t); - gb_semaphore_destroy(&t->semaphore); -} - - -gb_inline void gb__thread_run(gbThread *t) { - gb_semaphore_release(&t->semaphore); - t->return_value = t->proc(t); -} - -#if defined(GB_SYSTEM_WINDOWS) - gb_inline DWORD __stdcall gb__thread_proc(void *arg) { - gbThread *t = cast(gbThread *)arg; - gb__thread_run(t); - t->is_running = false; - return 0; - } -#else - gb_inline void * gb__thread_proc(void *arg) { - gbThread *t = cast(gbThread *)arg; - gb__thread_run(t); - t->is_running = false; - return NULL; - } -#endif - -gb_inline void gb_thread_start(gbThread *t, gbThreadProc *proc, void *user_data) { gb_thread_start_with_stack(t, proc, user_data, 0); } - -gb_inline void gb_thread_start_with_stack(gbThread *t, gbThreadProc *proc, void *user_data, isize stack_size) { - GB_ASSERT(!t->is_running); - GB_ASSERT(proc != NULL); - t->proc = proc; - t->user_data = user_data; - t->stack_size = stack_size; - t->is_running = true; - -#if defined(GB_SYSTEM_WINDOWS) - t->win32_handle = CreateThread(NULL, stack_size, gb__thread_proc, t, 0, NULL); - GB_ASSERT_MSG(t->win32_handle != NULL, "CreateThread: GetLastError"); -#else - { - pthread_attr_t attr; - pthread_attr_init(&attr); - pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); - if (stack_size != 0) { - pthread_attr_setstacksize(&attr, stack_size); - } - pthread_create(&t->posix_handle, &attr, gb__thread_proc, t); - pthread_attr_destroy(&attr); - } -#endif - - gb_semaphore_wait(&t->semaphore); -} - -gb_inline void gb_thread_join(gbThread *t) { - if (!t->is_running) return; - -#if defined(GB_SYSTEM_WINDOWS) - WaitForSingleObject(t->win32_handle, INFINITE); - CloseHandle(t->win32_handle); - t->win32_handle = INVALID_HANDLE_VALUE; -#else - pthread_join(t->posix_handle, NULL); - t->posix_handle = 0; -#endif - t->is_running = false; -} - -gb_inline b32 gb_thread_is_running(gbThread const *t) { return t->is_running != 0; } - -gb_inline u32 gb_thread_current_id(void) { - u32 thread_id; -#if defined(GB_SYSTEM_WINDOWS) - #if defined(GB_ARCH_32_BIT) && defined(GB_CPU_X86) - thread_id = (cast(u32 *)__readfsdword(24))[9]; - #elif defined(GB_ARCH_64_BIT) && defined(GB_CPU_X86) - thread_id = (cast(u32 *)__readgsqword(48))[18]; - #else - thread_id = GetCurrentThreadId(); - #endif - -#elif defined(GB_SYSTEM_OSX) && defined(GB_ARCH_64_BIT) - thread_id = pthread_mach_thread_np(pthread_self()); -#elif defined(GB_ARCH_32_BIT) && defined(GB_CPU_X86) - __asm__("mov %%gs:0x08,%0" : "=r"(thread_id)); -#elif defined(GB_ARCH_64_BIT) && defined(GB_CPU_X86) - __asm__("mov %%fs:0x10,%0" : "=r"(thread_id)); -#else - #error Unsupported architecture for gb_thread_current_id() -#endif - - return thread_id; -} - - - -void gb_thread_set_name(gbThread *t, char const *name) { -#if defined(GB_COMPILER_MSVC) - #pragma pack(push, 8) - typedef struct { - DWORD type; - char const *name; - DWORD id; - DWORD flags; - } gbprivThreadName; - #pragma pack(pop) - gbprivThreadName tn; - tn.type = 0x1000; - tn.name = name; - tn.id = GetThreadId(cast(HANDLE)t->win32_handle); - tn.flags = 0; - - __try { - RaiseException(0x406d1388, 0, gb_size_of(tn)/4, cast(ULONG_PTR *)&tn); - } __except(1 /*EXCEPTION_EXECUTE_HANDLER*/) { - } - -#elif defined(GB_SYSTEM_WINDOWS) && !defined(GB_COMPILER_MSVC) - // IMPORTANT TODO(bill): Set thread name for GCC/Clang on windows - return; -#elif defined(GB_SYSTEM_OSX) - // TODO(bill): Test if this works - pthread_setname_np(name); -#else - // TODO(bill): Test if this works - pthread_setname_np(t->posix_handle, name); -#endif -} - - - - -void gb_sync_init(gbSync *s) { - gb_zero_item(s); - gb_mutex_init(&s->mutex); - gb_mutex_init(&s->start); - gb_semaphore_init(&s->release); -} - -void gb_sync_destroy(gbSync *s) { - if (s->waiting) - GB_PANIC("Cannot destroy while threads are waiting!"); - - gb_mutex_destroy(&s->mutex); - gb_mutex_destroy(&s->start); - gb_semaphore_destroy(&s->release); -} - -void gb_sync_set_target(gbSync *s, i32 count) { - gb_mutex_lock(&s->start); - - gb_mutex_lock(&s->mutex); - GB_ASSERT(s->target == 0); - s->target = count; - s->current = 0; - s->waiting = 0; - gb_mutex_unlock(&s->mutex); -} - -void gb_sync_release(gbSync *s) { - if (s->waiting) { - gb_semaphore_release(&s->release); - } else { - s->target = 0; - gb_mutex_unlock(&s->start); - } -} - -i32 gb_sync_reach(gbSync *s) { - i32 n; - gb_mutex_lock(&s->mutex); - GB_ASSERT(s->current < s->target); - n = ++s->current; // NOTE(bill): Record this value to avoid possible race if `return s->current` was done - if (s->current == s->target) - gb_sync_release(s); - gb_mutex_unlock(&s->mutex); - return n; -} - -void gb_sync_reach_and_wait(gbSync *s) { - gb_mutex_lock(&s->mutex); - GB_ASSERT(s->current < s->target); - s->current++; - if (s->current == s->target) { - gb_sync_release(s); - gb_mutex_unlock(&s->mutex); - } else { - s->waiting++; // NOTE(bill): Waiting, so one more waiter - gb_mutex_unlock(&s->mutex); // NOTE(bill): Release the mutex to other threads - - gb_semaphore_wait(&s->release); // NOTE(bill): Wait for merge completion - - gb_mutex_lock(&s->mutex); // NOTE(bill): On merge completion, lock mutex - s->waiting--; // NOTE(bill): Done waiting - gb_sync_release(s); // NOTE(bill): Restart the next waiter - gb_mutex_unlock(&s->mutex); - } -} - - - - - - - - -gb_inline gbAllocator gb_heap_allocator(void) { - gbAllocator a; - a.proc = gb_heap_allocator_proc; - a.data = NULL; - return a; -} - -GB_ALLOCATOR_PROC(gb_heap_allocator_proc) { - void *ptr = NULL; - gb_unused(allocator_data); - gb_unused(old_size); -// TODO(bill): Throughly test! - switch (type) { -#if defined(GB_COMPILER_MSVC) - case gbAllocation_Alloc: - ptr = _aligned_malloc(size, alignment); - if (flags & gbAllocatorFlag_ClearToZero) - gb_zero_size(ptr, size); - break; - case gbAllocation_Free: - _aligned_free(old_memory); - break; - case gbAllocation_Resize: - ptr = _aligned_realloc(old_memory, size, alignment); - break; - -#elif defined(GB_SYSTEM_LINUX) - // TODO(bill): *nix version that's decent - case gbAllocation_Alloc: { - ptr = aligned_alloc(alignment, size); - // ptr = malloc(size+alignment); - - if (flags & gbAllocatorFlag_ClearToZero) { - gb_zero_size(ptr, size); - } - } break; - - case gbAllocation_Free: { - free(old_memory); - } break; - - case gbAllocation_Resize: { - // ptr = realloc(old_memory, size); - ptr = gb_default_resize_align(gb_heap_allocator(), old_memory, old_size, size, alignment); - } break; -#else - // TODO(bill): *nix version that's decent - case gbAllocation_Alloc: { - posix_memalign(&ptr, alignment, size); - - if (flags & gbAllocatorFlag_ClearToZero) { - gb_zero_size(ptr, size); - } - } break; - - case gbAllocation_Free: { - free(old_memory); - } break; - - case gbAllocation_Resize: { - ptr = gb_default_resize_align(gb_heap_allocator(), old_memory, old_size, size, alignment); - } break; -#endif - - case gbAllocation_FreeAll: - break; - } - - return ptr; -} - - -#if defined(GB_SYSTEM_WINDOWS) -void gb_affinity_init(gbAffinity *a) { - SYSTEM_LOGICAL_PROCESSOR_INFORMATION *start_processor_info = NULL; - DWORD length = 0; - b32 result = GetLogicalProcessorInformation(NULL, &length); - - gb_zero_item(a); - - if (!result && GetLastError() == 122l /*ERROR_INSUFFICIENT_BUFFER*/ && length > 0) { - start_processor_info = cast(SYSTEM_LOGICAL_PROCESSOR_INFORMATION *)gb_alloc(gb_heap_allocator(), length); - result = GetLogicalProcessorInformation(start_processor_info, &length); - if (result) { - SYSTEM_LOGICAL_PROCESSOR_INFORMATION *end_processor_info, *processor_info; - - a->is_accurate = true; - a->core_count = 0; - a->thread_count = 0; - end_processor_info = cast(SYSTEM_LOGICAL_PROCESSOR_INFORMATION *)gb_pointer_add(start_processor_info, length); - - for (processor_info = start_processor_info; - processor_info < end_processor_info; - processor_info++) { - if (processor_info->Relationship == RelationProcessorCore) { - isize thread = gb_count_set_bits(processor_info->ProcessorMask); - if (thread == 0) { - a->is_accurate = false; - } else if (a->thread_count + thread > GB_WIN32_MAX_THREADS) { - a->is_accurate = false; - } else { - GB_ASSERT(a->core_count <= a->thread_count && - a->thread_count < GB_WIN32_MAX_THREADS); - a->core_masks[a->core_count++] = processor_info->ProcessorMask; - a->thread_count += thread; - } - } - } - } - - gb_free(gb_heap_allocator(), start_processor_info); - } - - GB_ASSERT(a->core_count <= a->thread_count); - if (a->thread_count == 0) { - a->is_accurate = false; - a->core_count = 1; - a->thread_count = 1; - a->core_masks[0] = 1; - } - -} -void gb_affinity_destroy(gbAffinity *a) { - gb_unused(a); -} - - -b32 gb_affinity_set(gbAffinity *a, isize core, isize thread) { - usize available_mask, check_mask = 1; - GB_ASSERT(thread < gb_affinity_thread_count_for_core(a, core)); - - available_mask = a->core_masks[core]; - for (;;) { - if ((available_mask & check_mask) != 0) { - if (thread-- == 0) { - usize result = SetThreadAffinityMask(GetCurrentThread(), check_mask); - return result != 0; - } - } - check_mask <<= 1; // NOTE(bill): Onto the next bit - } -} - -isize gb_affinity_thread_count_for_core(gbAffinity *a, isize core) { - GB_ASSERT(core >= 0 && core < a->core_count); - return gb_count_set_bits(a->core_masks[core]); -} - -#elif defined(GB_SYSTEM_OSX) -void gb_affinity_init(gbAffinity *a) { - usize count = 0; - usize count_size = sizeof(count); - - a->is_accurate = false; - a->thread_count = 1; - a->core_count = 1; - a->threads_per_core = 1; - - if (sysctlbyname("hw.logicalcpu", &count, &count_size, NULL, 0) == 0) { - if (count > 0) { - a->thread_count = count; - // Get # of physical cores - if (sysctlbyname("hw.physicalcpu", &count, &count_size, NULL, 0) == 0) { - if (count > 0) { - a->core_count = count; - a->threads_per_core = a->thread_count / count; - if (a->threads_per_core < 1) - a->threads_per_core = 1; - else - a->is_accurate = true; - } - } - } - } - -} - -void gb_affinity_destroy(gbAffinity *a) { - gb_unused(a); -} - -b32 gb_affinity_set(gbAffinity *a, isize core, isize thread_index) { - isize index; - thread_t thread; - thread_affinity_policy_data_t info; - kern_return_t result; - - GB_ASSERT(core < a->core_count); - GB_ASSERT(thread_index < a->threads_per_core); - - index = core * a->threads_per_core + thread_index; - thread = mach_thread_self(); - info.affinity_tag = cast(integer_t)index; - result = thread_policy_set(thread, THREAD_AFFINITY_POLICY, cast(thread_policy_t)&info, THREAD_AFFINITY_POLICY_COUNT); - return result == KERN_SUCCESS; -} - -isize gb_affinity_thread_count_for_core(gbAffinity *a, isize core) { - GB_ASSERT(core >= 0 && core < a->core_count); - return a->threads_per_core; -} - -#elif defined(GB_SYSTEM_LINUX) -// IMPORTANT TODO(bill): This gbAffinity stuff for linux needs be improved a lot! -// NOTE(zangent): I have to read /proc/cpuinfo to get the number of threads per core. -#include - -void gb_affinity_init(gbAffinity *a) { - b32 accurate = true; - isize threads = 0; - - a->thread_count = 1; - a->core_count = sysconf(_SC_NPROCESSORS_ONLN); - a->threads_per_core = 1; - - - if(a->core_count <= 0) { - a->core_count = 1; - accurate = false; - } - - // Parsing /proc/cpuinfo to get the number of threads per core. - // NOTE(zangent): This calls the CPU's threads "cores", although the wording - // is kind of weird. This should be right, though. - - FILE* cpu_info = fopen("/proc/cpuinfo", "r"); - - if (cpu_info != NULL) { - for (;;) { - // The 'temporary char'. Everything goes into this char, - // so that we can check against EOF at the end of this loop. - char c; - -#define AF__CHECK(letter) ((c = getc(cpu_info)) == letter) - if (AF__CHECK('c') && AF__CHECK('p') && AF__CHECK('u') && AF__CHECK(' ') && - AF__CHECK('c') && AF__CHECK('o') && AF__CHECK('r') && AF__CHECK('e') && AF__CHECK('s')) { - // We're on a CPU info line. - while (!AF__CHECK(EOF)) { - if (c == '\n') { - break; - } else if (c < '0' || '9' > c) { - continue; - } - threads = threads * 10 + (c - '0'); - } - break; - } else { - while (!AF__CHECK('\n')) { - if (c==EOF) { - break; - } - } - } - if (c == EOF) { - break; - } -#undef AF__CHECK - } - - fclose(cpu_info); - } - - if (threads == 0) { - threads = 1; - accurate = false; - } - - a->threads_per_core = threads; - a->thread_count = a->threads_per_core * a->core_count; - a->is_accurate = accurate; - -} - -void gb_affinity_destroy(gbAffinity *a) { - gb_unused(a); -} - -b32 gb_affinity_set(gbAffinity *a, isize core, isize thread_index) { - return true; -} - -isize gb_affinity_thread_count_for_core(gbAffinity *a, isize core) { - GB_ASSERT(0 <= core && core < a->core_count); - return a->threads_per_core; -} -#else -#error TODO(bill): Unknown system -#endif - - - - - - - - - -//////////////////////////////////////////////////////////////// -// -// Virtual Memory -// -// - -gbVirtualMemory gb_virtual_memory(void *data, isize size) { - gbVirtualMemory vm; - vm.data = data; - vm.size = size; - return vm; -} - - -#if defined(GB_SYSTEM_WINDOWS) -gb_inline gbVirtualMemory gb_vm_alloc(void *addr, isize size) { - gbVirtualMemory vm; - GB_ASSERT(size > 0); - vm.data = VirtualAlloc(addr, size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); - vm.size = size; - return vm; -} - -gb_inline b32 gb_vm_free(gbVirtualMemory vm) { - MEMORY_BASIC_INFORMATION info; - while (vm.size > 0) { - if (VirtualQuery(vm.data, &info, gb_size_of(info)) == 0) - return false; - if (info.BaseAddress != vm.data || - info.AllocationBase != vm.data || - info.State != MEM_COMMIT || info.RegionSize > cast(usize)vm.size) { - return false; - } - if (VirtualFree(vm.data, 0, MEM_RELEASE) == 0) - return false; - vm.data = gb_pointer_add(vm.data, info.RegionSize); - vm.size -= info.RegionSize; - } - return true; -} - -gb_inline gbVirtualMemory gb_vm_trim(gbVirtualMemory vm, isize lead_size, isize size) { - gbVirtualMemory new_vm = {0}; - void *ptr; - GB_ASSERT(vm.size >= lead_size + size); - - ptr = gb_pointer_add(vm.data, lead_size); - - gb_vm_free(vm); - new_vm = gb_vm_alloc(ptr, size); - if (new_vm.data == ptr) - return new_vm; - if (new_vm.data) - gb_vm_free(new_vm); - return new_vm; -} - -gb_inline b32 gb_vm_purge(gbVirtualMemory vm) { - VirtualAlloc(vm.data, vm.size, MEM_RESET, PAGE_READWRITE); - // NOTE(bill): Can this really fail? - return true; -} - -isize gb_virtual_memory_page_size(isize *alignment_out) { - SYSTEM_INFO info; - GetSystemInfo(&info); - if (alignment_out) *alignment_out = info.dwAllocationGranularity; - return info.dwPageSize; -} - -#else - -#ifndef MAP_ANONYMOUS -#define MAP_ANONYMOUS MAP_ANON -#endif - -gb_inline gbVirtualMemory gb_vm_alloc(void *addr, isize size) { - gbVirtualMemory vm; - GB_ASSERT(size > 0); - vm.data = mmap(addr, size, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); - vm.size = size; - return vm; -} - -gb_inline b32 gb_vm_free(gbVirtualMemory vm) { - munmap(vm.data, vm.size); - return true; -} - -gb_inline gbVirtualMemory gb_vm_trim(gbVirtualMemory vm, isize lead_size, isize size) { - void *ptr; - isize trail_size; - GB_ASSERT(vm.size >= lead_size + size); - - ptr = gb_pointer_add(vm.data, lead_size); - trail_size = vm.size - lead_size - size; - - if (lead_size != 0) - gb_vm_free(gb_virtual_memory(vm.data, lead_size)); - if (trail_size != 0) - gb_vm_free(gb_virtual_memory(ptr, trail_size)); - return gb_virtual_memory(ptr, size); - -} - -gb_inline b32 gb_vm_purge(gbVirtualMemory vm) { - int err = madvise(vm.data, vm.size, MADV_DONTNEED); - return err != 0; -} - -isize gb_virtual_memory_page_size(isize *alignment_out) { - // TODO(bill): Is this always true? - isize result = cast(isize)sysconf(_SC_PAGE_SIZE); - if (alignment_out) *alignment_out = result; - return result; -} - -#endif - - - - -//////////////////////////////////////////////////////////////// -// -// Custom Allocation -// -// - - -// -// Arena Allocator -// - -gb_inline void gb_arena_init_from_memory(gbArena *arena, void *start, isize size) { - arena->backing.proc = NULL; - arena->backing.data = NULL; - arena->physical_start = start; - arena->total_size = size; - arena->total_allocated = 0; - arena->temp_count = 0; -} - -gb_inline void gb_arena_init_from_allocator(gbArena *arena, gbAllocator backing, isize size) { - arena->backing = backing; - arena->physical_start = gb_alloc(backing, size); // NOTE(bill): Uses default alignment - arena->total_size = size; - arena->total_allocated = 0; - arena->temp_count = 0; -} - -gb_inline void gb_arena_init_sub(gbArena *arena, gbArena *parent_arena, isize size) { gb_arena_init_from_allocator(arena, gb_arena_allocator(parent_arena), size); } - - -gb_inline void gb_arena_free(gbArena *arena) { - if (arena->backing.proc) { - gb_free(arena->backing, arena->physical_start); - arena->physical_start = NULL; - } -} - - -gb_inline isize gb_arena_alignment_of(gbArena *arena, isize alignment) { - isize alignment_offset, result_pointer, mask; - GB_ASSERT(gb_is_power_of_two(alignment)); - - alignment_offset = 0; - result_pointer = cast(isize)arena->physical_start + arena->total_allocated; - mask = alignment - 1; - if (result_pointer & mask) - alignment_offset = alignment - (result_pointer & mask); - - return alignment_offset; -} - -gb_inline isize gb_arena_size_remaining(gbArena *arena, isize alignment) { - isize result = arena->total_size - (arena->total_allocated + gb_arena_alignment_of(arena, alignment)); - return result; -} - -gb_inline void gb_arena_check(gbArena *arena) { GB_ASSERT(arena->temp_count == 0); } - - - - - - -gb_inline gbAllocator gb_arena_allocator(gbArena *arena) { - gbAllocator allocator; - allocator.proc = gb_arena_allocator_proc; - allocator.data = arena; - return allocator; -} - -GB_ALLOCATOR_PROC(gb_arena_allocator_proc) { - gbArena *arena = cast(gbArena *)allocator_data; - void *ptr = NULL; - - gb_unused(old_size); - - switch (type) { - case gbAllocation_Alloc: { - void *end = gb_pointer_add(arena->physical_start, arena->total_allocated); - isize total_size = size + alignment; - - // NOTE(bill): Out of memory - if (arena->total_allocated + total_size > cast(isize)arena->total_size) { - gb_printf_err("Arena out of memory\n"); - return NULL; - } - - ptr = gb_align_forward(end, alignment); - arena->total_allocated += total_size; - if (flags & gbAllocatorFlag_ClearToZero) - gb_zero_size(ptr, size); - } break; - - case gbAllocation_Free: - // NOTE(bill): Free all at once - // Use Temp_Arena_Memory if you want to free a block - break; - - case gbAllocation_FreeAll: - arena->total_allocated = 0; - break; - - case gbAllocation_Resize: { - // TODO(bill): Check if ptr is on top of stack and just extend - gbAllocator a = gb_arena_allocator(arena); - ptr = gb_default_resize_align(a, old_memory, old_size, size, alignment); - } break; - } - return ptr; -} - - -gb_inline gbTempArenaMemory gb_temp_arena_memory_begin(gbArena *arena) { - gbTempArenaMemory tmp; - tmp.arena = arena; - tmp.original_count = arena->total_allocated; - arena->temp_count++; - return tmp; -} - -gb_inline void gb_temp_arena_memory_end(gbTempArenaMemory tmp) { - GB_ASSERT_MSG(tmp.arena->total_allocated >= tmp.original_count, - "%td >= %td", tmp.arena->total_allocated, tmp.original_count); - GB_ASSERT(tmp.arena->temp_count > 0); - tmp.arena->total_allocated = tmp.original_count; - tmp.arena->temp_count--; -} - - - - -// -// Pool Allocator -// - - -gb_inline void gb_pool_init(gbPool *pool, gbAllocator backing, isize num_blocks, isize block_size) { - gb_pool_init_align(pool, backing, num_blocks, block_size, GB_DEFAULT_MEMORY_ALIGNMENT); -} - -void gb_pool_init_align(gbPool *pool, gbAllocator backing, isize num_blocks, isize block_size, isize block_align) { - isize actual_block_size, pool_size, block_index; - void *data, *curr; - uintptr *end; - - gb_zero_item(pool); - - pool->backing = backing; - pool->block_size = block_size; - pool->block_align = block_align; - - actual_block_size = block_size + block_align; - pool_size = num_blocks * actual_block_size; - - data = gb_alloc_align(backing, pool_size, block_align); - - // NOTE(bill): Init intrusive freelist - curr = data; - for (block_index = 0; block_index < num_blocks-1; block_index++) { - uintptr *next = cast(uintptr *)curr; - *next = cast(uintptr)curr + actual_block_size; - curr = gb_pointer_add(curr, actual_block_size); - } - - end = cast(uintptr *)curr; - *end = cast(uintptr)NULL; - - pool->physical_start = data; - pool->free_list = data; -} - -gb_inline void gb_pool_free(gbPool *pool) { - if (pool->backing.proc) { - gb_free(pool->backing, pool->physical_start); - } -} - - -gb_inline gbAllocator gb_pool_allocator(gbPool *pool) { - gbAllocator allocator; - allocator.proc = gb_pool_allocator_proc; - allocator.data = pool; - return allocator; -} -GB_ALLOCATOR_PROC(gb_pool_allocator_proc) { - gbPool *pool = cast(gbPool *)allocator_data; - void *ptr = NULL; - - gb_unused(old_size); - - switch (type) { - case gbAllocation_Alloc: { - uintptr next_free; - GB_ASSERT(size == pool->block_size); - GB_ASSERT(alignment == pool->block_align); - GB_ASSERT(pool->free_list != NULL); - - next_free = *cast(uintptr *)pool->free_list; - ptr = pool->free_list; - pool->free_list = cast(void *)next_free; - pool->total_size += pool->block_size; - if (flags & gbAllocatorFlag_ClearToZero) - gb_zero_size(ptr, size); - } break; - - case gbAllocation_Free: { - uintptr *next; - if (old_memory == NULL) return NULL; - - next = cast(uintptr *)old_memory; - *next = cast(uintptr)pool->free_list; - pool->free_list = old_memory; - pool->total_size -= pool->block_size; - } break; - - case gbAllocation_FreeAll: - // TODO(bill): - break; - - case gbAllocation_Resize: - // NOTE(bill): Cannot resize - GB_PANIC("You cannot resize something allocated by with a pool."); - break; - } - - return ptr; -} - - - - - -gb_inline gbAllocationHeader *gb_allocation_header(void *data) { - isize *p = cast(isize *)data; - while (p[-1] == cast(isize)(-1)) { - p--; - } - return cast(gbAllocationHeader *)p - 1; -} - -gb_inline void gb_allocation_header_fill(gbAllocationHeader *header, void *data, isize size) { - isize *ptr; - header->size = size; - ptr = cast(isize *)(header + 1); - while (cast(void *)ptr < data) { - *ptr++ = cast(isize)(-1); - } -} - - - -// -// Free List Allocator -// - -gb_inline void gb_free_list_init(gbFreeList *fl, void *start, isize size) { - GB_ASSERT(size > gb_size_of(gbFreeListBlock)); - - fl->physical_start = start; - fl->total_size = size; - fl->curr_block = cast(gbFreeListBlock *)start; - fl->curr_block->size = size; - fl->curr_block->next = NULL; -} - - -gb_inline void gb_free_list_init_from_allocator(gbFreeList *fl, gbAllocator backing, isize size) { - void *start = gb_alloc(backing, size); - gb_free_list_init(fl, start, size); -} - - - -gb_inline gbAllocator gb_free_list_allocator(gbFreeList *fl) { - gbAllocator a; - a.proc = gb_free_list_allocator_proc; - a.data = fl; - return a; -} - -GB_ALLOCATOR_PROC(gb_free_list_allocator_proc) { - gbFreeList *fl = cast(gbFreeList *)allocator_data; - void *ptr = NULL; - - GB_ASSERT_NOT_NULL(fl); - - switch (type) { - case gbAllocation_Alloc: { - gbFreeListBlock *prev_block = NULL; - gbFreeListBlock *curr_block = fl->curr_block; - - while (curr_block) { - isize total_size; - gbAllocationHeader *header; - - total_size = size + alignment + gb_size_of(gbAllocationHeader); - - if (curr_block->size < total_size) { - prev_block = curr_block; - curr_block = curr_block->next; - continue; - } - - if (curr_block->size - total_size <= gb_size_of(gbAllocationHeader)) { - total_size = curr_block->size; - - if (prev_block) - prev_block->next = curr_block->next; - else - fl->curr_block = curr_block->next; - } else { - // NOTE(bill): Create a new block for the remaining memory - gbFreeListBlock *next_block; - next_block = cast(gbFreeListBlock *)gb_pointer_add(curr_block, total_size); - - GB_ASSERT(cast(void *)next_block < gb_pointer_add(fl->physical_start, fl->total_size)); - - next_block->size = curr_block->size - total_size; - next_block->next = curr_block->next; - - if (prev_block) - prev_block->next = next_block; - else - fl->curr_block = next_block; - } - - - // TODO(bill): Set Header Info - header = cast(gbAllocationHeader *)curr_block; - ptr = gb_align_forward(header+1, alignment); - gb_allocation_header_fill(header, ptr, size); - - fl->total_allocated += total_size; - fl->allocation_count++; - - - if (flags & gbAllocatorFlag_ClearToZero) - gb_zero_size(ptr, size); - return ptr; - } - // NOTE(bill): if ptr == NULL, ran out of free list memory! FUCK! - return NULL; - } break; - - case gbAllocation_Free: { - gbAllocationHeader *header = gb_allocation_header(old_memory); - isize block_size = header->size; - uintptr block_start, block_end; - gbFreeListBlock *prev_block = NULL; - gbFreeListBlock *curr_block = fl->curr_block; - - block_start = cast(uintptr)header; - block_end = cast(uintptr)block_start + block_size; - - while (curr_block) { - if (cast(uintptr)curr_block >= block_end) - break; - prev_block = curr_block; - curr_block = curr_block->next; - } - - if (prev_block == NULL) { - prev_block = cast(gbFreeListBlock *)block_start; - prev_block->size = block_size; - prev_block->next = fl->curr_block; - - fl->curr_block = prev_block; - } else if ((cast(uintptr)prev_block + prev_block->size) == block_start) { - prev_block->size += block_size; - } else { - gbFreeListBlock *tmp = cast(gbFreeListBlock *)block_start; - tmp->size = block_size; - tmp->next = prev_block->next; - prev_block->next = tmp; - - prev_block = tmp; - } - - if (curr_block && (cast(uintptr)curr_block == block_end)) { - prev_block->size += curr_block->size; - prev_block->next = curr_block->next; - } - - fl->allocation_count--; - fl->total_allocated -= block_size; - } break; - - case gbAllocation_FreeAll: - gb_free_list_init(fl, fl->physical_start, fl->total_size); - break; - - case gbAllocation_Resize: - ptr = gb_default_resize_align(gb_free_list_allocator(fl), old_memory, old_size, size, alignment); - break; - } - - return ptr; -} - - - -void gb_scratch_memory_init(gbScratchMemory *s, void *start, isize size) { - s->physical_start = start; - s->total_size = size; - s->alloc_point = start; - s->free_point = start; -} - - -b32 gb_scratch_memory_is_in_use(gbScratchMemory *s, void *ptr) { - if (s->free_point == s->alloc_point) return false; - if (s->alloc_point > s->free_point) - return ptr >= s->free_point && ptr < s->alloc_point; - return ptr >= s->free_point || ptr < s->alloc_point; -} - - -gbAllocator gb_scratch_allocator(gbScratchMemory *s) { - gbAllocator a; - a.proc = gb_scratch_allocator_proc; - a.data = s; - return a; -} - -GB_ALLOCATOR_PROC(gb_scratch_allocator_proc) { - gbScratchMemory *s = cast(gbScratchMemory *)allocator_data; - void *ptr = NULL; - GB_ASSERT_NOT_NULL(s); - - switch (type) { - case gbAllocation_Alloc: { - void *pt = s->alloc_point; - gbAllocationHeader *header = cast(gbAllocationHeader *)pt; - void *data = gb_align_forward(header+1, alignment); - void *end = gb_pointer_add(s->physical_start, s->total_size); - - GB_ASSERT(alignment % 4 == 0); - size = ((size + 3)/4)*4; - pt = gb_pointer_add(pt, size); - - // NOTE(bill): Wrap around - if (pt > end) { - header->size = gb_pointer_diff(header, end) | GB_ISIZE_HIGH_BIT; - pt = s->physical_start; - header = cast(gbAllocationHeader *)pt; - data = gb_align_forward(header+1, alignment); - pt = gb_pointer_add(pt, size); - } - - if (!gb_scratch_memory_is_in_use(s, pt)) { - gb_allocation_header_fill(header, pt, gb_pointer_diff(header, pt)); - s->alloc_point = cast(u8 *)pt; - ptr = data; - } - - if (flags & gbAllocatorFlag_ClearToZero) - gb_zero_size(ptr, size); - } break; - - case gbAllocation_Free: { - if (old_memory) { - void *end = gb_pointer_add(s->physical_start, s->total_size); - if (old_memory < s->physical_start || old_memory >= end) { - GB_ASSERT(false); - } else { - // NOTE(bill): Mark as free - gbAllocationHeader *h = gb_allocation_header(old_memory); - GB_ASSERT((h->size & GB_ISIZE_HIGH_BIT) == 0); - h->size = h->size | GB_ISIZE_HIGH_BIT; - - while (s->free_point != s->alloc_point) { - gbAllocationHeader *header = cast(gbAllocationHeader *)s->free_point; - if ((header->size & GB_ISIZE_HIGH_BIT) == 0) - break; - - s->free_point = gb_pointer_add(s->free_point, h->size & (~GB_ISIZE_HIGH_BIT)); - if (s->free_point == end) - s->free_point = s->physical_start; - } - } - } - } break; - - case gbAllocation_FreeAll: - s->alloc_point = s->physical_start; - s->free_point = s->physical_start; - break; - - case gbAllocation_Resize: - ptr = gb_default_resize_align(gb_scratch_allocator(s), old_memory, old_size, size, alignment); - break; - } - - return ptr; -} - - - - - - -//////////////////////////////////////////////////////////////// -// -// Sorting -// -// - -// TODO(bill): Should I make all the macros local? - -#define GB__COMPARE_PROC(Type) \ -gb_global isize gb__##Type##_cmp_offset; GB_COMPARE_PROC(gb__##Type##_cmp) { \ - Type const p = *cast(Type const *)gb_pointer_add_const(a, gb__##Type##_cmp_offset); \ - Type const q = *cast(Type const *)gb_pointer_add_const(b, gb__##Type##_cmp_offset); \ - return p < q ? -1 : p > q; \ -} \ -GB_COMPARE_PROC_PTR(gb_##Type##_cmp(isize offset)) { \ - gb__##Type##_cmp_offset = offset; \ - return &gb__##Type##_cmp; \ -} - - -GB__COMPARE_PROC(i16); -GB__COMPARE_PROC(i32); -GB__COMPARE_PROC(i64); -GB__COMPARE_PROC(isize); -GB__COMPARE_PROC(f32); -GB__COMPARE_PROC(f64); -GB__COMPARE_PROC(char); - -// NOTE(bill): str_cmp is special as it requires a funny type and funny comparison -gb_global isize gb__str_cmp_offset; GB_COMPARE_PROC(gb__str_cmp) { - char const *p = *cast(char const **)gb_pointer_add_const(a, gb__str_cmp_offset); - char const *q = *cast(char const **)gb_pointer_add_const(b, gb__str_cmp_offset); - return gb_strcmp(p, q); -} -GB_COMPARE_PROC_PTR(gb_str_cmp(isize offset)) { - gb__str_cmp_offset = offset; - return &gb__str_cmp; -} - -#undef GB__COMPARE_PROC - - - - -// TODO(bill): Make user definable? -#define GB__SORT_STACK_SIZE 64 -#define GB__SORT_INSERT_SORT_THRESHOLD 8 - -#define GB__SORT_PUSH(_base, _limit) do { \ - stack_ptr[0] = (_base); \ - stack_ptr[1] = (_limit); \ - stack_ptr += 2; \ -} while (0) - - -#define GB__SORT_POP(_base, _limit) do { \ - stack_ptr -= 2; \ - (_base) = stack_ptr[0]; \ - (_limit) = stack_ptr[1]; \ -} while (0) - - - -void gb_sort(void *base_, isize count, isize size, gbCompareProc cmp) { - u8 *i, *j; - u8 *base = cast(u8 *)base_; - u8 *limit = base + count*size; - isize threshold = GB__SORT_INSERT_SORT_THRESHOLD * size; - - // NOTE(bill): Prepare the stack - u8 *stack[GB__SORT_STACK_SIZE] = {0}; - u8 **stack_ptr = stack; - - for (;;) { - if ((limit-base) > threshold) { - // NOTE(bill): Quick sort - i = base + size; - j = limit - size; - - gb_memswap(((limit-base)/size/2) * size + base, base, size); - if (cmp(i, j) > 0) gb_memswap(i, j, size); - if (cmp(base, j) > 0) gb_memswap(base, j, size); - if (cmp(i, base) > 0) gb_memswap(i, base, size); - - for (;;) { - do i += size; while (cmp(i, base) < 0); - do j -= size; while (cmp(j, base) > 0); - if (i > j) break; - gb_memswap(i, j, size); - } - - gb_memswap(base, j, size); - - if (j - base > limit - i) { - GB__SORT_PUSH(base, j); - base = i; - } else { - GB__SORT_PUSH(i, limit); - limit = j; - } - } else { - // NOTE(bill): Insertion sort - for (j = base, i = j+size; - i < limit; - j = i, i += size) { - for (; cmp(j, j+size) > 0; j -= size) { - gb_memswap(j, j+size, size); - if (j == base) break; - } - } - - if (stack_ptr == stack) break; // NOTE(bill): Sorting is done! - GB__SORT_POP(base, limit); - } - } -} - -#undef GB__SORT_PUSH -#undef GB__SORT_POP - - -#define GB_RADIX_SORT_PROC_GEN(Type) GB_RADIX_SORT_PROC(Type) { \ - Type *source = items; \ - Type *dest = temp; \ - isize byte_index, i, byte_max = 8*gb_size_of(Type); \ - for (byte_index = 0; byte_index < byte_max; byte_index += 8) { \ - isize offsets[256] = {0}; \ - isize total = 0; \ - /* NOTE(bill): First pass - count how many of each key */ \ - for (i = 0; i < count; i++) { \ - Type radix_value = source[i]; \ - Type radix_piece = (radix_value >> byte_index) & 0xff; \ - offsets[radix_piece]++; \ - } \ - /* NOTE(bill): Change counts to offsets */ \ - for (i = 0; i < gb_count_of(offsets); i++) { \ - isize skcount = offsets[i]; \ - offsets[i] = total; \ - total += skcount; \ - } \ - /* NOTE(bill): Second pass - place elements into the right location */ \ - for (i = 0; i < count; i++) { \ - Type radix_value = source[i]; \ - Type radix_piece = (radix_value >> byte_index) & 0xff; \ - dest[offsets[radix_piece]++] = source[i]; \ - } \ - gb_swap(Type *, source, dest); \ - } \ -} - -GB_RADIX_SORT_PROC_GEN(u8); -GB_RADIX_SORT_PROC_GEN(u16); -GB_RADIX_SORT_PROC_GEN(u32); -GB_RADIX_SORT_PROC_GEN(u64); - -gb_inline isize gb_binary_search(void const *base, isize count, isize size, void const *key, gbCompareProc compare_proc) { - isize start = 0; - isize end = count; - - while (start < end) { - isize mid = start + (end-start)/2; - isize result = compare_proc(key, cast(u8 *)base + mid*size); - if (result < 0) - end = mid; - else if (result > 0) - start = mid+1; - else - return mid; - } - - return -1; -} - -void gb_shuffle(void *base, isize count, isize size) { - u8 *a; - isize i, j; - gbRandom random; gb_random_init(&random); - - a = cast(u8 *)base + (count-1) * size; - for (i = count; i > 1; i--) { - j = gb_random_gen_isize(&random) % i; - gb_memswap(a, cast(u8 *)base + j*size, size); - a -= size; - } -} - -void gb_reverse(void *base, isize count, isize size) { - isize i, j = count-1; - for (i = 0; i < j; i++, j++) { - gb_memswap(cast(u8 *)base + i*size, cast(u8 *)base + j*size, size); - } -} - - - -//////////////////////////////////////////////////////////////// -// -// Char things -// -// - - - - -gb_inline char gb_char_to_lower(char c) { - if (c >= 'A' && c <= 'Z') - return 'a' + (c - 'A'); - return c; -} - -gb_inline char gb_char_to_upper(char c) { - if (c >= 'a' && c <= 'z') - return 'A' + (c - 'a'); - return c; -} - -gb_inline b32 gb_char_is_space(char c) { - if (c == ' ' || - c == '\t' || - c == '\n' || - c == '\r' || - c == '\f' || - c == '\v') - return true; - return false; -} - -gb_inline b32 gb_char_is_digit(char c) { - if (c >= '0' && c <= '9') - return true; - return false; -} - -gb_inline b32 gb_char_is_hex_digit(char c) { - if (gb_char_is_digit(c) || - (c >= 'a' && c <= 'f') || - (c >= 'A' && c <= 'F')) - return true; - return false; -} - -gb_inline b32 gb_char_is_alpha(char c) { - if ((c >= 'A' && c <= 'Z') || - (c >= 'a' && c <= 'z')) - return true; - return false; -} - -gb_inline b32 gb_char_is_alphanumeric(char c) { - return gb_char_is_alpha(c) || gb_char_is_digit(c); -} - -gb_inline i32 gb_digit_to_int(char c) { - return gb_char_is_digit(c) ? c - '0' : c - 'W'; -} - -gb_inline i32 gb_hex_digit_to_int(char c) { - if (gb_char_is_digit(c)) - return gb_digit_to_int(c); - else if (gb_is_between(c, 'a', 'f')) - return c - 'a' + 10; - else if (gb_is_between(c, 'A', 'F')) - return c - 'A' + 10; - return -1; -} - - - - -gb_inline void gb_str_to_lower(char *str) { - if (!str) return; - while (*str) { - *str = gb_char_to_lower(*str); - str++; - } -} - -gb_inline void gb_str_to_upper(char *str) { - if (!str) return; - while (*str) { - *str = gb_char_to_upper(*str); - str++; - } -} - - -gb_inline isize gb_strlen(char const *str) { - char const *begin = str; - isize const *w; - if (str == NULL) { - return 0; - } - while (cast(uintptr)str % sizeof(usize)) { - if (!*str) - return str - begin; - str++; - } - w = cast(isize const *)str; - while (!GB__HAS_ZERO(*w)) { - w++; - } - str = cast(char const *)w; - while (*str) { - str++; - } - return str - begin; -} - -gb_inline isize gb_strnlen(char const *str, isize max_len) { - char const *end = cast(char const *)gb_memchr(str, 0, max_len); - if (end) { - return end - str; - } - return max_len; -} - -gb_inline isize gb_utf8_strlen(u8 const *str) { - isize count = 0; - for (; *str; count++) { - u8 c = *str; - isize inc = 0; - if (c < 0x80) inc = 1; - else if ((c & 0xe0) == 0xc0) inc = 2; - else if ((c & 0xf0) == 0xe0) inc = 3; - else if ((c & 0xf8) == 0xf0) inc = 4; - else return -1; - - str += inc; - } - return count; -} - -gb_inline isize gb_utf8_strnlen(u8 const *str, isize max_len) { - isize count = 0; - for (; *str && max_len > 0; count++) { - u8 c = *str; - isize inc = 0; - if (c < 0x80) inc = 1; - else if ((c & 0xe0) == 0xc0) inc = 2; - else if ((c & 0xf0) == 0xe0) inc = 3; - else if ((c & 0xf8) == 0xf0) inc = 4; - else return -1; - - str += inc; - max_len -= inc; - } - return count; -} - - -gb_inline i32 gb_strcmp(char const *s1, char const *s2) { - while (*s1 && (*s1 == *s2)) { - s1++, s2++; - } - return *(u8 *)s1 - *(u8 *)s2; -} - -gb_inline char *gb_strcpy(char *dest, char const *source) { - GB_ASSERT_NOT_NULL(dest); - if (source) { - char *str = dest; - while (*source) *str++ = *source++; - } - return dest; -} - - -gb_inline char *gb_strncpy(char *dest, char const *source, isize len) { - GB_ASSERT_NOT_NULL(dest); - if (source) { - char *str = dest; - while (len > 0 && *source) { - *str++ = *source++; - len--; - } - while (len > 0) { - *str++ = '\0'; - len--; - } - } - return dest; -} - -gb_inline isize gb_strlcpy(char *dest, char const *source, isize len) { - isize result = 0; - GB_ASSERT_NOT_NULL(dest); - if (source) { - char const *source_start = source; - char *str = dest; - while (len > 0 && *source) { - *str++ = *source++; - len--; - } - while (len > 0) { - *str++ = '\0'; - len--; - } - - result = source - source_start; - } - return result; -} - -gb_inline char *gb_strrev(char *str) { - isize len = gb_strlen(str); - char *a = str + 0; - char *b = str + len-1; - len /= 2; - while (len--) { - gb_swap(char, *a, *b); - a++, b--; - } - return str; -} - - - - -gb_inline i32 gb_strncmp(char const *s1, char const *s2, isize len) { - for (; len > 0; - s1++, s2++, len--) { - if (*s1 != *s2) { - return ((s1 < s2) ? -1 : +1); - } else if (*s1 == '\0') { - return 0; - } - } - return 0; -} - - -gb_inline char const *gb_strtok(char *output, char const *src, char const *delimit) { - while (*src && gb_char_first_occurence(delimit, *src) != NULL) { - *output++ = *src++; - } - - *output = 0; - return *src ? src+1 : src; -} - -gb_inline b32 gb_str_has_prefix(char const *str, char const *prefix) { - while (*prefix) { - if (*str++ != *prefix++) { - return false; - } - } - return true; -} - -gb_inline b32 gb_str_has_suffix(char const *str, char const *suffix) { - isize i = gb_strlen(str); - isize j = gb_strlen(suffix); - if (j <= i) { - return gb_strcmp(str+i-j, suffix) == 0; - } - return false; -} - - - - -gb_inline char const *gb_char_first_occurence(char const *s, char c) { - char ch = c; - for (; *s != ch; s++) { - if (*s == '\0') { - return NULL; - } - } - return s; -} - - -gb_inline char const *gb_char_last_occurence(char const *s, char c) { - char const *result = NULL; - do { - if (*s == c) { - result = s; - } - } while (*s++); - - return result; -} - - - -gb_inline void gb_str_concat(char *dest, isize dest_len, - char const *src_a, isize src_a_len, - char const *src_b, isize src_b_len) { - GB_ASSERT(dest_len >= src_a_len+src_b_len+1); - if (dest) { - gb_memcopy(dest, src_a, src_a_len); - gb_memcopy(dest+src_a_len, src_b, src_b_len); - dest[src_a_len+src_b_len] = '\0'; - } -} - - -gb_internal isize gb__scan_i64(char const *text, i32 base, i64 *value) { - char const *text_begin = text; - i64 result = 0; - b32 negative = false; - - if (*text == '-') { - negative = true; - text++; - } - - if (base == 16 && gb_strncmp(text, "0x", 2) == 0) { - text += 2; - } - - for (;;) { - i64 v; - if (gb_char_is_digit(*text)) { - v = *text - '0'; - } else if (base == 16 && gb_char_is_hex_digit(*text)) { - v = gb_hex_digit_to_int(*text); - } else { - break; - } - - result *= base; - result += v; - text++; - } - - if (value) { - if (negative) result = -result; - *value = result; - } - - return (text - text_begin); -} - -gb_internal isize gb__scan_u64(char const *text, i32 base, u64 *value) { - char const *text_begin = text; - u64 result = 0; - - if (base == 16 && gb_strncmp(text, "0x", 2) == 0) { - text += 2; - } - - for (;;) { - u64 v; - if (gb_char_is_digit(*text)) { - v = *text - '0'; - } else if (base == 16 && gb_char_is_hex_digit(*text)) { - v = gb_hex_digit_to_int(*text); - } else { - break; - } - - result *= base; - result += v; - text++; - } - - if (value) *value = result; - return (text - text_begin); -} - - -// TODO(bill): Make better -u64 gb_str_to_u64(char const *str, char **end_ptr, i32 base) { - isize len; - u64 value = 0; - - if (!base) { - if ((gb_strlen(str) > 2) && (gb_strncmp(str, "0x", 2) == 0)) { - base = 16; - } else { - base = 10; - } - } - - len = gb__scan_u64(str, base, &value); - if (end_ptr) *end_ptr = (char *)str + len; - return value; -} - -i64 gb_str_to_i64(char const *str, char **end_ptr, i32 base) { - isize len; - i64 value; - - if (!base) { - if ((gb_strlen(str) > 2) && (gb_strncmp(str, "0x", 2) == 0)) { - base = 16; - } else { - base = 10; - } - } - - len = gb__scan_i64(str, base, &value); - if (end_ptr) *end_ptr = (char *)str + len; - return value; -} - -// TODO(bill): Are these good enough for characters? -gb_global char const gb__num_to_char_table[] = - "0123456789" - "ABCDEFGHIJKLMNOPQRSTUVWXYZ" - "abcdefghijklmnopqrstuvwxyz" - "@$"; - -gb_inline void gb_i64_to_str(i64 value, char *string, i32 base) { - char *buf = string; - b32 negative = false; - u64 v; - if (value < 0) { - negative = true; - value = -value; - } - v = cast(u64)value; - if (v != 0) { - while (v > 0) { - *buf++ = gb__num_to_char_table[v % base]; - v /= base; - } - } else { - *buf++ = '0'; - } - if (negative) { - *buf++ = '-'; - } - *buf = '\0'; - gb_strrev(string); -} - - - -gb_inline void gb_u64_to_str(u64 value, char *string, i32 base) { - char *buf = string; - - if (value) { - while (value > 0) { - *buf++ = gb__num_to_char_table[value % base]; - value /= base; - } - } else { - *buf++ = '0'; - } - *buf = '\0'; - - gb_strrev(string); -} - -gb_inline f32 gb_str_to_f32(char const *str, char **end_ptr) { - f64 f = gb_str_to_f64(str, end_ptr); - f32 r = cast(f32)f; - return r; -} - -gb_inline f64 gb_str_to_f64(char const *str, char **end_ptr) { - f64 result, value, sign, scale; - i32 frac; - - while (gb_char_is_space(*str)) { - str++; - } - - sign = 1.0; - if (*str == '-') { - sign = -1.0; - str++; - } else if (*str == '+') { - str++; - } - - for (value = 0.0; gb_char_is_digit(*str); str++) { - value = value * 10.0 + (*str-'0'); - } - - if (*str == '.') { - f64 pow10 = 10.0; - str++; - while (gb_char_is_digit(*str)) { - value += (*str-'0') / pow10; - pow10 *= 10.0; - str++; - } - } - - frac = 0; - scale = 1.0; - if ((*str == 'e') || (*str == 'E')) { - u32 exp; - - str++; - if (*str == '-') { - frac = 1; - str++; - } else if (*str == '+') { - str++; - } - - for (exp = 0; gb_char_is_digit(*str); str++) { - exp = exp * 10 + (*str-'0'); - } - if (exp > 308) exp = 308; - - while (exp >= 50) { scale *= 1e50; exp -= 50; } - while (exp >= 8) { scale *= 1e8; exp -= 8; } - while (exp > 0) { scale *= 10.0; exp -= 1; } - } - - result = sign * (frac ? (value / scale) : (value * scale)); - - if (end_ptr) *end_ptr = cast(char *)str; - - return result; -} - - - - - - - -gb_inline void gb__set_string_length (gbString str, isize len) { GB_STRING_HEADER(str)->length = len; } -gb_inline void gb__set_string_capacity(gbString str, isize cap) { GB_STRING_HEADER(str)->capacity = cap; } - - -gbString gb_string_make_reserve(gbAllocator a, isize capacity) { - isize header_size = gb_size_of(gbStringHeader); - void *ptr = gb_alloc(a, header_size + capacity + 1); - - gbString str; - gbStringHeader *header; - - if (ptr == NULL) return NULL; - gb_zero_size(ptr, header_size + capacity + 1); - - str = cast(char *)ptr + header_size; - header = GB_STRING_HEADER(str); - header->allocator = a; - header->length = 0; - header->capacity = capacity; - str[capacity] = '\0'; - - return str; -} - - -gb_inline gbString gb_string_make(gbAllocator a, char const *str) { - isize len = str ? gb_strlen(str) : 0; - return gb_string_make_length(a, str, len); -} - -gbString gb_string_make_length(gbAllocator a, void const *init_str, isize num_bytes) { - isize header_size = gb_size_of(gbStringHeader); - void *ptr = gb_alloc(a, header_size + num_bytes + 1); - - gbString str; - gbStringHeader *header; - - if (ptr == NULL) return NULL; - if (!init_str) gb_zero_size(ptr, header_size + num_bytes + 1); - - str = cast(char *)ptr + header_size; - header = GB_STRING_HEADER(str); - header->allocator = a; - header->length = num_bytes; - header->capacity = num_bytes; - if (num_bytes && init_str) { - gb_memcopy(str, init_str, num_bytes); - } - str[num_bytes] = '\0'; - - return str; -} - -gb_inline void gb_string_free(gbString str) { - if (str) { - gbStringHeader *header = GB_STRING_HEADER(str); - gb_free(header->allocator, header); - } - -} - -gb_inline gbString gb_string_duplicate(gbAllocator a, gbString const str) { return gb_string_make_length(a, str, gb_string_length(str)); } - -gb_inline isize gb_string_length (gbString const str) { return GB_STRING_HEADER(str)->length; } -gb_inline isize gb_string_capacity(gbString const str) { return GB_STRING_HEADER(str)->capacity; } - -gb_inline isize gb_string_available_space(gbString const str) { - gbStringHeader *h = GB_STRING_HEADER(str); - if (h->capacity > h->length) { - return h->capacity - h->length; - } - return 0; -} - - -gb_inline void gb_string_clear(gbString str) { gb__set_string_length(str, 0); str[0] = '\0'; } - -gb_inline gbString gb_string_append(gbString str, gbString const other) { return gb_string_append_length(str, other, gb_string_length(other)); } - -gbString gb_string_append_length(gbString str, void const *other, isize other_len) { - if (other_len > 0) { - isize curr_len = gb_string_length(str); - - str = gb_string_make_space_for(str, other_len); - if (str == NULL) { - return NULL; - } - - gb_memcopy(str + curr_len, other, other_len); - str[curr_len + other_len] = '\0'; - gb__set_string_length(str, curr_len + other_len); - } - return str; -} - -gb_inline gbString gb_string_appendc(gbString str, char const *other) { - return gb_string_append_length(str, other, gb_strlen(other)); -} - -gbString gb_string_append_rune(gbString str, Rune r) { - if (r >= 0) { - u8 buf[8] = {0}; - isize len = gb_utf8_encode_rune(buf, r); - return gb_string_append_length(str, buf, len); - } - return str; -} - -gbString gb_string_append_fmt(gbString str, char const *fmt, ...) { - isize res; - char buf[4096] = {0}; - va_list va; - va_start(va, fmt); - res = gb_snprintf_va(buf, gb_count_of(buf)-1, fmt, va)-1; - va_end(va); - return gb_string_append_length(str, buf, res); -} - - - -gbString gb_string_set(gbString str, char const *cstr) { - isize len = gb_strlen(cstr); - if (gb_string_capacity(str) < len) { - str = gb_string_make_space_for(str, len - gb_string_length(str)); - if (str == NULL) { - return NULL; - } - } - - gb_memcopy(str, cstr, len); - str[len] = '\0'; - gb__set_string_length(str, len); - - return str; -} - - - -gbString gb_string_make_space_for(gbString str, isize add_len) { - isize available = gb_string_available_space(str); - - // NOTE(bill): Return if there is enough space left - if (available >= add_len) { - return str; - } else { - isize new_len, old_size, new_size; - void *ptr, *new_ptr; - gbAllocator a = GB_STRING_HEADER(str)->allocator; - gbStringHeader *header; - - new_len = gb_string_length(str) + add_len; - ptr = GB_STRING_HEADER(str); - old_size = gb_size_of(gbStringHeader) + gb_string_length(str) + 1; - new_size = gb_size_of(gbStringHeader) + new_len + 1; - - new_ptr = gb_resize(a, ptr, old_size, new_size); - if (new_ptr == NULL) return NULL; - - header = cast(gbStringHeader *)new_ptr; - header->allocator = a; - - str = cast(gbString)(header+1); - gb__set_string_capacity(str, new_len); - - return str; - } -} - -gb_inline isize gb_string_allocation_size(gbString const str) { - isize cap = gb_string_capacity(str); - return gb_size_of(gbStringHeader) + cap; -} - - -gb_inline b32 gb_string_are_equal(gbString const lhs, gbString const rhs) { - isize lhs_len, rhs_len, i; - lhs_len = gb_string_length(lhs); - rhs_len = gb_string_length(rhs); - if (lhs_len != rhs_len) { - return false; - } - - for (i = 0; i < lhs_len; i++) { - if (lhs[i] != rhs[i]) { - return false; - } - } - - return true; -} - - -gbString gb_string_trim(gbString str, char const *cut_set) { - char *start, *end, *start_pos, *end_pos; - isize len; - - start_pos = start = str; - end_pos = end = str + gb_string_length(str) - 1; - - while (start_pos <= end && gb_char_first_occurence(cut_set, *start_pos)) { - start_pos++; - } - while (end_pos > start_pos && gb_char_first_occurence(cut_set, *end_pos)) { - end_pos--; - } - - len = cast(isize)((start_pos > end_pos) ? 0 : ((end_pos - start_pos)+1)); - - if (str != start_pos) - gb_memmove(str, start_pos, len); - str[len] = '\0'; - - gb__set_string_length(str, len); - - return str; -} - -gb_inline gbString gb_string_trim_space(gbString str) { return gb_string_trim(str, " \t\r\n\v\f"); } - - - - -//////////////////////////////////////////////////////////////// -// -// Windows UTF-8 Handling -// -// - - -u16 *gb_utf8_to_ucs2(u16 *buffer, isize len, u8 const *str) { - Rune c; - isize i = 0; - len--; - while (*str) { - if (i >= len) - return NULL; - if (!(*str & 0x80)) { - buffer[i++] = *str++; - } else if ((*str & 0xe0) == 0xc0) { - if (*str < 0xc2) - return NULL; - c = (*str++ & 0x1f) << 6; - if ((*str & 0xc0) != 0x80) - return NULL; - buffer[i++] = cast(u16)(c + (*str++ & 0x3f)); - } else if ((*str & 0xf0) == 0xe0) { - if (*str == 0xe0 && - (str[1] < 0xa0 || str[1] > 0xbf)) - return NULL; - if (*str == 0xed && str[1] > 0x9f) // str[1] < 0x80 is checked below - return NULL; - c = (*str++ & 0x0f) << 12; - if ((*str & 0xc0) != 0x80) - return NULL; - c += (*str++ & 0x3f) << 6; - if ((*str & 0xc0) != 0x80) - return NULL; - buffer[i++] = cast(u16)(c + (*str++ & 0x3f)); - } else if ((*str & 0xf8) == 0xf0) { - if (*str > 0xf4) - return NULL; - if (*str == 0xf0 && (str[1] < 0x90 || str[1] > 0xbf)) - return NULL; - if (*str == 0xf4 && str[1] > 0x8f) // str[1] < 0x80 is checked below - return NULL; - c = (*str++ & 0x07) << 18; - if ((*str & 0xc0) != 0x80) - return NULL; - c += (*str++ & 0x3f) << 12; - if ((*str & 0xc0) != 0x80) - return NULL; - c += (*str++ & 0x3f) << 6; - if ((*str & 0xc0) != 0x80) - return NULL; - c += (*str++ & 0x3f); - // UTF-8 encodings of values used in surrogate pairs are invalid - if ((c & 0xfffff800) == 0xd800) - return NULL; - if (c >= 0x10000) { - c -= 0x10000; - if (i+2 > len) - return NULL; - buffer[i++] = 0xd800 | (0x3ff & (c>>10)); - buffer[i++] = 0xdc00 | (0x3ff & (c )); - } - } else { - return NULL; - } - } - buffer[i] = 0; - return buffer; -} - -u8 *gb_ucs2_to_utf8(u8 *buffer, isize len, u16 const *str) { - isize i = 0; - len--; - while (*str) { - if (*str < 0x80) { - if (i+1 > len) - return NULL; - buffer[i++] = (char) *str++; - } else if (*str < 0x800) { - if (i+2 > len) - return NULL; - buffer[i++] = cast(char)(0xc0 + (*str >> 6)); - buffer[i++] = cast(char)(0x80 + (*str & 0x3f)); - str += 1; - } else if (*str >= 0xd800 && *str < 0xdc00) { - Rune c; - if (i+4 > len) - return NULL; - c = ((str[0] - 0xd800) << 10) + ((str[1]) - 0xdc00) + 0x10000; - buffer[i++] = cast(char)(0xf0 + (c >> 18)); - buffer[i++] = cast(char)(0x80 + ((c >> 12) & 0x3f)); - buffer[i++] = cast(char)(0x80 + ((c >> 6) & 0x3f)); - buffer[i++] = cast(char)(0x80 + ((c ) & 0x3f)); - str += 2; - } else if (*str >= 0xdc00 && *str < 0xe000) { - return NULL; - } else { - if (i+3 > len) - return NULL; - buffer[i++] = 0xe0 + (*str >> 12); - buffer[i++] = 0x80 + ((*str >> 6) & 0x3f); - buffer[i++] = 0x80 + ((*str ) & 0x3f); - str += 1; - } - } - buffer[i] = 0; - return buffer; -} - -u16 *gb_utf8_to_ucs2_buf(u8 const *str) { // NOTE(bill): Uses locally persisting buffer - gb_local_persist u16 buf[4096]; - return gb_utf8_to_ucs2(buf, gb_count_of(buf), str); -} - -u8 *gb_ucs2_to_utf8_buf(u16 const *str) { // NOTE(bill): Uses locally persisting buffer - gb_local_persist u8 buf[4096]; - return gb_ucs2_to_utf8(buf, gb_count_of(buf), str); -} - - - -gb_global u8 const gb__utf8_first[256] = { - 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, // 0x00-0x0F - 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, // 0x10-0x1F - 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, // 0x20-0x2F - 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, // 0x30-0x3F - 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, // 0x40-0x4F - 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, // 0x50-0x5F - 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, // 0x60-0x6F - 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, // 0x70-0x7F - 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, // 0x80-0x8F - 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, // 0x90-0x9F - 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, // 0xA0-0xAF - 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, // 0xB0-0xBF - 0xf1, 0xf1, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, // 0xC0-0xCF - 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, // 0xD0-0xDF - 0x13, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x23, 0x03, 0x03, // 0xE0-0xEF - 0x34, 0x04, 0x04, 0x04, 0x44, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, // 0xF0-0xFF -}; - - -typedef struct gbUtf8AcceptRange { - u8 lo, hi; -} gbUtf8AcceptRange; - -gb_global gbUtf8AcceptRange const gb__utf8_accept_ranges[] = { - {0x80, 0xbf}, - {0xa0, 0xbf}, - {0x80, 0x9f}, - {0x90, 0xbf}, - {0x80, 0x8f}, -}; - - -isize gb_utf8_decode(u8 const *str, isize str_len, Rune *codepoint_out) { - isize width = 0; - Rune codepoint = GB_RUNE_INVALID; - - if (str_len > 0) { - u8 s0 = str[0]; - u8 x = gb__utf8_first[s0], sz; - u8 b1, b2, b3; - gbUtf8AcceptRange accept; - if (x >= 0xf0) { - Rune mask = (cast(Rune)x << 31) >> 31; - codepoint = (cast(Rune)s0 & (~mask)) | (GB_RUNE_INVALID & mask); - width = 1; - goto end; - } - if (s0 < 0x80) { - codepoint = s0; - width = 1; - goto end; - } - - sz = x&7; - accept = gb__utf8_accept_ranges[x>>4]; - if (str_len < gb_size_of(sz)) - goto invalid_codepoint; - - b1 = str[1]; - if (b1 < accept.lo || accept.hi < b1) - goto invalid_codepoint; - - if (sz == 2) { - codepoint = (cast(Rune)s0&0x1f)<<6 | (cast(Rune)b1&0x3f); - width = 2; - goto end; - } - - b2 = str[2]; - if (!gb_is_between(b2, 0x80, 0xbf)) - goto invalid_codepoint; - - if (sz == 3) { - codepoint = (cast(Rune)s0&0x1f)<<12 | (cast(Rune)b1&0x3f)<<6 | (cast(Rune)b2&0x3f); - width = 3; - goto end; - } - - b3 = str[3]; - if (!gb_is_between(b3, 0x80, 0xbf)) - goto invalid_codepoint; - - codepoint = (cast(Rune)s0&0x07)<<18 | (cast(Rune)b1&0x3f)<<12 | (cast(Rune)b2&0x3f)<<6 | (cast(Rune)b3&0x3f); - width = 4; - goto end; - - invalid_codepoint: - codepoint = GB_RUNE_INVALID; - width = 1; - } - -end: - if (codepoint_out) *codepoint_out = codepoint; - return width; -} - -isize gb_utf8_codepoint_size(u8 const *str, isize str_len) { - isize i = 0; - for (; i < str_len && str[i]; i++) { - if ((str[i] & 0xc0) != 0x80) - break; - } - return i+1; -} - -isize gb_utf8_encode_rune(u8 buf[4], Rune r) { - u32 i = cast(u32)r; - u8 mask = 0x3f; - if (i <= (1<<7)-1) { - buf[0] = cast(u8)r; - return 1; - } - if (i <= (1<<11)-1) { - buf[0] = 0xc0 | cast(u8)(r>>6); - buf[1] = 0x80 | (cast(u8)(r)&mask); - return 2; - } - - // Invalid or Surrogate range - if (i > GB_RUNE_MAX || - gb_is_between(i, 0xd800, 0xdfff)) { - r = GB_RUNE_INVALID; - - buf[0] = 0xe0 | cast(u8)(r>>12); - buf[1] = 0x80 | (cast(u8)(r>>6)&mask); - buf[2] = 0x80 | (cast(u8)(r)&mask); - return 3; - } - - if (i <= (1<<16)-1) { - buf[0] = 0xe0 | cast(u8)(r>>12); - buf[1] = 0x80 | (cast(u8)(r>>6)&mask); - buf[2] = 0x80 | (cast(u8)(r)&mask); - return 3; - } - - buf[0] = 0xf0 | cast(u8)(r>>18); - buf[1] = 0x80 | (cast(u8)(r>>12)&mask); - buf[2] = 0x80 | (cast(u8)(r>>6)&mask); - buf[3] = 0x80 | (cast(u8)(r)&mask); - return 4; -} - - - - -//////////////////////////////////////////////////////////////// -// -// gbArray -// -// - - -gb_no_inline void *gb__array_set_capacity(void *array, isize capacity, isize element_size) { - gbArrayHeader *h = GB_ARRAY_HEADER(array); - - GB_ASSERT(element_size > 0); - - if (capacity == h->capacity) - return array; - - if (capacity < h->count) { - if (h->capacity < capacity) { - isize new_capacity = GB_ARRAY_GROW_FORMULA(h->capacity); - if (new_capacity < capacity) - new_capacity = capacity; - gb__array_set_capacity(array, new_capacity, element_size); - } - h->count = capacity; - } - - { - isize size = gb_size_of(gbArrayHeader) + element_size*capacity; - gbArrayHeader *nh = cast(gbArrayHeader *)gb_alloc(h->allocator, size); - gb_memmove(nh, h, gb_size_of(gbArrayHeader) + element_size*h->count); - nh->allocator = h->allocator; - nh->count = h->count; - nh->capacity = capacity; - gb_free(h->allocator, h); - return nh+1; - } -} - - -//////////////////////////////////////////////////////////////// -// -// Hashing functions -// -// - -u32 gb_adler32(void const *data, isize len) { - u32 const MOD_ALDER = 65521; - u32 a = 1, b = 0; - isize i, block_len; - u8 const *bytes = cast(u8 const *)data; - - block_len = len % 5552; - - while (len) { - for (i = 0; i+7 < block_len; i += 8) { - a += bytes[0], b += a; - a += bytes[1], b += a; - a += bytes[2], b += a; - a += bytes[3], b += a; - a += bytes[4], b += a; - a += bytes[5], b += a; - a += bytes[6], b += a; - a += bytes[7], b += a; - - bytes += 8; - } - for (; i < block_len; i++) { - a += *bytes++, b += a; - } - - a %= MOD_ALDER, b %= MOD_ALDER; - len -= block_len; - block_len = 5552; - } - - return (b << 16) | a; -} - - -gb_global u32 const GB__CRC32_TABLE[256] = { - 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, - 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, - 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, - 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, - 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, - 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, - 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, - 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, - 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, - 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, - 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, - 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, - 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, - 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, - 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, - 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, - 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, - 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, - 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, - 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, - 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, - 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, - 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, - 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, - 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, - 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, - 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, - 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, - 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, - 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, - 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, - 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, - 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, - 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, - 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, - 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, - 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, - 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, - 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, - 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, - 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, - 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, - 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, - 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, - 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, - 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, - 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, - 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, - 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, - 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, - 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, - 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, - 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, - 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, - 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, - 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, - 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, - 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, - 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, - 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, - 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, - 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, - 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, - 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d, -}; - -gb_global u64 const GB__CRC64_TABLE[256] = { - 0x0000000000000000ull, 0x42f0e1eba9ea3693ull, 0x85e1c3d753d46d26ull, 0xc711223cfa3e5bb5ull, - 0x493366450e42ecdfull, 0x0bc387aea7a8da4cull, 0xccd2a5925d9681f9ull, 0x8e224479f47cb76aull, - 0x9266cc8a1c85d9beull, 0xd0962d61b56fef2dull, 0x17870f5d4f51b498ull, 0x5577eeb6e6bb820bull, - 0xdb55aacf12c73561ull, 0x99a54b24bb2d03f2ull, 0x5eb4691841135847ull, 0x1c4488f3e8f96ed4ull, - 0x663d78ff90e185efull, 0x24cd9914390bb37cull, 0xe3dcbb28c335e8c9ull, 0xa12c5ac36adfde5aull, - 0x2f0e1eba9ea36930ull, 0x6dfeff5137495fa3ull, 0xaaefdd6dcd770416ull, 0xe81f3c86649d3285ull, - 0xf45bb4758c645c51ull, 0xb6ab559e258e6ac2ull, 0x71ba77a2dfb03177ull, 0x334a9649765a07e4ull, - 0xbd68d2308226b08eull, 0xff9833db2bcc861dull, 0x388911e7d1f2dda8ull, 0x7a79f00c7818eb3bull, - 0xcc7af1ff21c30bdeull, 0x8e8a101488293d4dull, 0x499b3228721766f8ull, 0x0b6bd3c3dbfd506bull, - 0x854997ba2f81e701ull, 0xc7b97651866bd192ull, 0x00a8546d7c558a27ull, 0x4258b586d5bfbcb4ull, - 0x5e1c3d753d46d260ull, 0x1cecdc9e94ace4f3ull, 0xdbfdfea26e92bf46ull, 0x990d1f49c77889d5ull, - 0x172f5b3033043ebfull, 0x55dfbadb9aee082cull, 0x92ce98e760d05399ull, 0xd03e790cc93a650aull, - 0xaa478900b1228e31ull, 0xe8b768eb18c8b8a2ull, 0x2fa64ad7e2f6e317ull, 0x6d56ab3c4b1cd584ull, - 0xe374ef45bf6062eeull, 0xa1840eae168a547dull, 0x66952c92ecb40fc8ull, 0x2465cd79455e395bull, - 0x3821458aada7578full, 0x7ad1a461044d611cull, 0xbdc0865dfe733aa9ull, 0xff3067b657990c3aull, - 0x711223cfa3e5bb50ull, 0x33e2c2240a0f8dc3ull, 0xf4f3e018f031d676ull, 0xb60301f359dbe0e5ull, - 0xda050215ea6c212full, 0x98f5e3fe438617bcull, 0x5fe4c1c2b9b84c09ull, 0x1d14202910527a9aull, - 0x93366450e42ecdf0ull, 0xd1c685bb4dc4fb63ull, 0x16d7a787b7faa0d6ull, 0x5427466c1e109645ull, - 0x4863ce9ff6e9f891ull, 0x0a932f745f03ce02ull, 0xcd820d48a53d95b7ull, 0x8f72eca30cd7a324ull, - 0x0150a8daf8ab144eull, 0x43a04931514122ddull, 0x84b16b0dab7f7968ull, 0xc6418ae602954ffbull, - 0xbc387aea7a8da4c0ull, 0xfec89b01d3679253ull, 0x39d9b93d2959c9e6ull, 0x7b2958d680b3ff75ull, - 0xf50b1caf74cf481full, 0xb7fbfd44dd257e8cull, 0x70eadf78271b2539ull, 0x321a3e938ef113aaull, - 0x2e5eb66066087d7eull, 0x6cae578bcfe24bedull, 0xabbf75b735dc1058ull, 0xe94f945c9c3626cbull, - 0x676dd025684a91a1ull, 0x259d31cec1a0a732ull, 0xe28c13f23b9efc87ull, 0xa07cf2199274ca14ull, - 0x167ff3eacbaf2af1ull, 0x548f120162451c62ull, 0x939e303d987b47d7ull, 0xd16ed1d631917144ull, - 0x5f4c95afc5edc62eull, 0x1dbc74446c07f0bdull, 0xdaad56789639ab08ull, 0x985db7933fd39d9bull, - 0x84193f60d72af34full, 0xc6e9de8b7ec0c5dcull, 0x01f8fcb784fe9e69ull, 0x43081d5c2d14a8faull, - 0xcd2a5925d9681f90ull, 0x8fdab8ce70822903ull, 0x48cb9af28abc72b6ull, 0x0a3b7b1923564425ull, - 0x70428b155b4eaf1eull, 0x32b26afef2a4998dull, 0xf5a348c2089ac238ull, 0xb753a929a170f4abull, - 0x3971ed50550c43c1ull, 0x7b810cbbfce67552ull, 0xbc902e8706d82ee7ull, 0xfe60cf6caf321874ull, - 0xe224479f47cb76a0ull, 0xa0d4a674ee214033ull, 0x67c58448141f1b86ull, 0x253565a3bdf52d15ull, - 0xab1721da49899a7full, 0xe9e7c031e063acecull, 0x2ef6e20d1a5df759ull, 0x6c0603e6b3b7c1caull, - 0xf6fae5c07d3274cdull, 0xb40a042bd4d8425eull, 0x731b26172ee619ebull, 0x31ebc7fc870c2f78ull, - 0xbfc9838573709812ull, 0xfd39626eda9aae81ull, 0x3a28405220a4f534ull, 0x78d8a1b9894ec3a7ull, - 0x649c294a61b7ad73ull, 0x266cc8a1c85d9be0ull, 0xe17dea9d3263c055ull, 0xa38d0b769b89f6c6ull, - 0x2daf4f0f6ff541acull, 0x6f5faee4c61f773full, 0xa84e8cd83c212c8aull, 0xeabe6d3395cb1a19ull, - 0x90c79d3fedd3f122ull, 0xd2377cd44439c7b1ull, 0x15265ee8be079c04ull, 0x57d6bf0317edaa97ull, - 0xd9f4fb7ae3911dfdull, 0x9b041a914a7b2b6eull, 0x5c1538adb04570dbull, 0x1ee5d94619af4648ull, - 0x02a151b5f156289cull, 0x4051b05e58bc1e0full, 0x87409262a28245baull, 0xc5b073890b687329ull, - 0x4b9237f0ff14c443ull, 0x0962d61b56fef2d0ull, 0xce73f427acc0a965ull, 0x8c8315cc052a9ff6ull, - 0x3a80143f5cf17f13ull, 0x7870f5d4f51b4980ull, 0xbf61d7e80f251235ull, 0xfd913603a6cf24a6ull, - 0x73b3727a52b393ccull, 0x31439391fb59a55full, 0xf652b1ad0167feeaull, 0xb4a25046a88dc879ull, - 0xa8e6d8b54074a6adull, 0xea16395ee99e903eull, 0x2d071b6213a0cb8bull, 0x6ff7fa89ba4afd18ull, - 0xe1d5bef04e364a72ull, 0xa3255f1be7dc7ce1ull, 0x64347d271de22754ull, 0x26c49cccb40811c7ull, - 0x5cbd6cc0cc10fafcull, 0x1e4d8d2b65facc6full, 0xd95caf179fc497daull, 0x9bac4efc362ea149ull, - 0x158e0a85c2521623ull, 0x577eeb6e6bb820b0ull, 0x906fc95291867b05ull, 0xd29f28b9386c4d96ull, - 0xcedba04ad0952342ull, 0x8c2b41a1797f15d1ull, 0x4b3a639d83414e64ull, 0x09ca82762aab78f7ull, - 0x87e8c60fded7cf9dull, 0xc51827e4773df90eull, 0x020905d88d03a2bbull, 0x40f9e43324e99428ull, - 0x2cffe7d5975e55e2ull, 0x6e0f063e3eb46371ull, 0xa91e2402c48a38c4ull, 0xebeec5e96d600e57ull, - 0x65cc8190991cb93dull, 0x273c607b30f68faeull, 0xe02d4247cac8d41bull, 0xa2dda3ac6322e288ull, - 0xbe992b5f8bdb8c5cull, 0xfc69cab42231bacfull, 0x3b78e888d80fe17aull, 0x7988096371e5d7e9ull, - 0xf7aa4d1a85996083ull, 0xb55aacf12c735610ull, 0x724b8ecdd64d0da5ull, 0x30bb6f267fa73b36ull, - 0x4ac29f2a07bfd00dull, 0x08327ec1ae55e69eull, 0xcf235cfd546bbd2bull, 0x8dd3bd16fd818bb8ull, - 0x03f1f96f09fd3cd2ull, 0x41011884a0170a41ull, 0x86103ab85a2951f4ull, 0xc4e0db53f3c36767ull, - 0xd8a453a01b3a09b3ull, 0x9a54b24bb2d03f20ull, 0x5d45907748ee6495ull, 0x1fb5719ce1045206ull, - 0x919735e51578e56cull, 0xd367d40ebc92d3ffull, 0x1476f63246ac884aull, 0x568617d9ef46bed9ull, - 0xe085162ab69d5e3cull, 0xa275f7c11f7768afull, 0x6564d5fde549331aull, 0x279434164ca30589ull, - 0xa9b6706fb8dfb2e3ull, 0xeb46918411358470ull, 0x2c57b3b8eb0bdfc5ull, 0x6ea7525342e1e956ull, - 0x72e3daa0aa188782ull, 0x30133b4b03f2b111ull, 0xf7021977f9cceaa4ull, 0xb5f2f89c5026dc37ull, - 0x3bd0bce5a45a6b5dull, 0x79205d0e0db05dceull, 0xbe317f32f78e067bull, 0xfcc19ed95e6430e8ull, - 0x86b86ed5267cdbd3ull, 0xc4488f3e8f96ed40ull, 0x0359ad0275a8b6f5ull, 0x41a94ce9dc428066ull, - 0xcf8b0890283e370cull, 0x8d7be97b81d4019full, 0x4a6acb477bea5a2aull, 0x089a2aacd2006cb9ull, - 0x14dea25f3af9026dull, 0x562e43b4931334feull, 0x913f6188692d6f4bull, 0xd3cf8063c0c759d8ull, - 0x5dedc41a34bbeeb2ull, 0x1f1d25f19d51d821ull, 0xd80c07cd676f8394ull, 0x9afce626ce85b507ull, -}; - -u32 gb_crc32(void const *data, isize len) { - isize remaining; - u32 result = ~(cast(u32)0); - u8 const *c = cast(u8 const *)data; - for (remaining = len; remaining--; c++) { - result = (result >> 8) ^ (GB__CRC32_TABLE[(result ^ *c) & 0xff]); - } - return ~result; -} - -u64 gb_crc64(void const *data, isize len) { - isize remaining; - u64 result = ~(cast(u64)0); - u8 const *c = cast(u8 const *)data; - for (remaining = len; remaining--; c++) { - result = (result >> 8) ^ (GB__CRC64_TABLE[(result ^ *c) & 0xff]); - } - return ~result; -} - -u32 gb_fnv32(void const *data, isize len) { - isize i; - u32 h = 0x811c9dc5; - u8 const *c = cast(u8 const *)data; - - for (i = 0; i < len; i++) { - h = (h * 0x01000193) ^ c[i]; - } - - return h; -} - -u64 gb_fnv64(void const *data, isize len) { - isize i; - u64 h = 0xcbf29ce484222325ull; - u8 const *c = cast(u8 const *)data; - - for (i = 0; i < len; i++) { - h = (h * 0x100000001b3ll) ^ c[i]; - } - - return h; -} - -u32 gb_fnv32a(void const *data, isize len) { - isize i; - u32 h = 0x811c9dc5; - u8 const *c = cast(u8 const *)data; - - for (i = 0; i < len; i++) { - h = (h ^ c[i]) * 0x01000193; - } - - return h; -} - -u64 gb_fnv64a(void const *data, isize len) { - isize i; - u64 h = 0xcbf29ce484222325ull; - u8 const *c = cast(u8 const *)data; - - for (i = 0; i < len; i++) { - h = (h ^ c[i]) * 0x100000001b3ll; - } - - return h; -} - -gb_inline u32 gb_murmur32(void const *data, isize len) { return gb_murmur32_seed(data, len, 0x9747b28c); } -gb_inline u64 gb_murmur64(void const *data, isize len) { return gb_murmur64_seed(data, len, 0x9747b28c); } - -u32 gb_murmur32_seed(void const *data, isize len, u32 seed) { - u32 const c1 = 0xcc9e2d51; - u32 const c2 = 0x1b873593; - u32 const r1 = 15; - u32 const r2 = 13; - u32 const m = 5; - u32 const n = 0xe6546b64; - - isize i, nblocks = len / 4; - u32 hash = seed, k1 = 0; - u32 const *blocks = cast(u32 const*)data; - u8 const *tail = cast(u8 const *)(data) + nblocks*4; - - for (i = 0; i < nblocks; i++) { - u32 k = blocks[i]; - k *= c1; - k = (k << r1) | (k >> (32 - r1)); - k *= c2; - - hash ^= k; - hash = ((hash << r2) | (hash >> (32 - r2))) * m + n; - } - - switch (len & 3) { - case 3: - k1 ^= tail[2] << 16; - case 2: - k1 ^= tail[1] << 8; - case 1: - k1 ^= tail[0]; - - k1 *= c1; - k1 = (k1 << r1) | (k1 >> (32 - r1)); - k1 *= c2; - hash ^= k1; - } - - hash ^= len; - hash ^= (hash >> 16); - hash *= 0x85ebca6b; - hash ^= (hash >> 13); - hash *= 0xc2b2ae35; - hash ^= (hash >> 16); - - return hash; -} - -u64 gb_murmur64_seed(void const *data_, isize len, u64 seed) { -#if defined(GB_ARCH_64_BIT) - u64 const m = 0xc6a4a7935bd1e995ULL; - i32 const r = 47; - - u64 h = seed ^ (len * m); - - u64 const *data = cast(u64 const *)data_; - u8 const *data2 = cast(u8 const *)data_; - u64 const* end = data + (len / 8); - - while (data != end) { - u64 k = *data++; - - k *= m; - k ^= k >> r; - k *= m; - - h ^= k; - h *= m; - } - - switch (len & 7) { - case 7: h ^= cast(u64)(data2[6]) << 48; - case 6: h ^= cast(u64)(data2[5]) << 40; - case 5: h ^= cast(u64)(data2[4]) << 32; - case 4: h ^= cast(u64)(data2[3]) << 24; - case 3: h ^= cast(u64)(data2[2]) << 16; - case 2: h ^= cast(u64)(data2[1]) << 8; - case 1: h ^= cast(u64)(data2[0]); - h *= m; - }; - - h ^= h >> r; - h *= m; - h ^= h >> r; - - return h; -#else - u64 h; - u32 const m = 0x5bd1e995; - i32 const r = 24; - - u32 h1 = cast(u32)(seed) ^ cast(u32)(len); - u32 h2 = cast(u32)(seed >> 32); - - u32 const *data = cast(u32 const *)data_; - - while (len >= 8) { - u32 k1, k2; - k1 = *data++; - k1 *= m; - k1 ^= k1 >> r; - k1 *= m; - h1 *= m; - h1 ^= k1; - len -= 4; - - k2 = *data++; - k2 *= m; - k2 ^= k2 >> r; - k2 *= m; - h2 *= m; - h2 ^= k2; - len -= 4; - } - - if (len >= 4) { - u32 k1 = *data++; - k1 *= m; - k1 ^= k1 >> r; - k1 *= m; - h1 *= m; - h1 ^= k1; - len -= 4; - } - - switch (len) { - case 3: h2 ^= (cast(u8 const *)data)[2] << 16; - case 2: h2 ^= (cast(u8 const *)data)[1] << 8; - case 1: h2 ^= (cast(u8 const *)data)[0] << 0; - h2 *= m; - }; - - h1 ^= h2 >> 18; - h1 *= m; - h2 ^= h1 >> 22; - h2 *= m; - h1 ^= h2 >> 17; - h1 *= m; - h2 ^= h1 >> 19; - h2 *= m; - - h = h1; - h = (h << 32) | h2; - - return h; -#endif -} - - - - - - - -//////////////////////////////////////////////////////////////// -// -// File Handling -// -// - -#if defined(GB_SYSTEM_WINDOWS) - - gb_internal wchar_t *gb__alloc_utf8_to_ucs2(gbAllocator a, char const *text, isize *w_len_) { - wchar_t *w_text = NULL; - isize len = 0, w_len = 0, w_len1 = 0; - if (text == NULL) { - if (w_len_) *w_len_ = w_len; - return NULL; - } - len = gb_strlen(text); - if (len == 0) { - if (w_len_) *w_len_ = w_len; - return NULL; - } - w_len = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, text, cast(int)len, NULL, 0); - if (w_len == 0) { - if (w_len_) *w_len_ = w_len; - return NULL; - } - w_text = gb_alloc_array(a, wchar_t, w_len+1); - w_len1 = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, text, cast(int)len, w_text, cast(int)w_len); - if (w_len1 == 0) { - gb_free(a, w_text); - if (w_len_) *w_len_ = 0; - return NULL; - } - w_text[w_len] = 0; - if (w_len_) *w_len_ = w_len; - return w_text; - } - - gb_internal GB_FILE_SEEK_PROC(gb__win32_file_seek) { - LARGE_INTEGER li_offset; - li_offset.QuadPart = offset; - if (!SetFilePointerEx(fd.p, li_offset, &li_offset, whence)) { - return false; - } - - if (new_offset) *new_offset = li_offset.QuadPart; - return true; - } - - gb_internal GB_FILE_READ_AT_PROC(gb__win32_file_read) { - b32 result = false; - DWORD size_ = cast(DWORD)(size > I32_MAX ? I32_MAX : size); - DWORD bytes_read_; - gb__win32_file_seek(fd, offset, gbSeekWhence_Begin, NULL); - if (ReadFile(fd.p, buffer, size_, &bytes_read_, NULL)) { - if (bytes_read) *bytes_read = bytes_read_; - result = true; - } - - return result; - } - - gb_internal GB_FILE_WRITE_AT_PROC(gb__win32_file_write) { - DWORD size_ = cast(DWORD)(size > I32_MAX ? I32_MAX : size); - DWORD bytes_written_; - gb__win32_file_seek(fd, offset, gbSeekWhence_Begin, NULL); - if (WriteFile(fd.p, buffer, size_, &bytes_written_, NULL)) { - if (bytes_written) *bytes_written = bytes_written_; - return true; - } - return false; - } - - gb_internal GB_FILE_CLOSE_PROC(gb__win32_file_close) { - CloseHandle(fd.p); - } - - gbFileOperations const gbDefaultFileOperations = { - gb__win32_file_read, - gb__win32_file_write, - gb__win32_file_seek, - gb__win32_file_close - }; - - gb_no_inline GB_FILE_OPEN_PROC(gb__win32_file_open) { - DWORD desired_access; - DWORD creation_disposition; - void *handle; - wchar_t *w_text; - - switch (mode & gbFileMode_Modes) { - case gbFileMode_Read: - desired_access = GENERIC_READ; - creation_disposition = OPEN_EXISTING; - break; - case gbFileMode_Write: - desired_access = GENERIC_WRITE; - creation_disposition = CREATE_ALWAYS; - break; - case gbFileMode_Append: - desired_access = GENERIC_WRITE; - creation_disposition = OPEN_ALWAYS; - break; - case gbFileMode_Read | gbFileMode_Rw: - desired_access = GENERIC_READ | GENERIC_WRITE; - creation_disposition = OPEN_EXISTING; - break; - case gbFileMode_Write | gbFileMode_Rw: - desired_access = GENERIC_READ | GENERIC_WRITE; - creation_disposition = CREATE_ALWAYS; - break; - case gbFileMode_Append | gbFileMode_Rw: - desired_access = GENERIC_READ | GENERIC_WRITE; - creation_disposition = OPEN_ALWAYS; - break; - default: - GB_PANIC("Invalid file mode"); - return gbFileError_Invalid; - } - - w_text = gb__alloc_utf8_to_ucs2(gb_heap_allocator(), filename, NULL); - if (w_text == NULL) { - return gbFileError_InvalidFilename; - } - handle = CreateFileW(w_text, - desired_access, - FILE_SHARE_READ|FILE_SHARE_DELETE, NULL, - creation_disposition, FILE_ATTRIBUTE_NORMAL, NULL); - - gb_free(gb_heap_allocator(), w_text); - - if (handle == INVALID_HANDLE_VALUE) { - DWORD err = GetLastError(); - switch (err) { - case ERROR_FILE_NOT_FOUND: return gbFileError_NotExists; - case ERROR_FILE_EXISTS: return gbFileError_Exists; - case ERROR_ALREADY_EXISTS: return gbFileError_Exists; - case ERROR_ACCESS_DENIED: return gbFileError_Permission; - } - return gbFileError_Invalid; - } - - if (mode & gbFileMode_Append) { - LARGE_INTEGER offset = {0}; - if (!SetFilePointerEx(handle, offset, NULL, gbSeekWhence_End)) { - CloseHandle(handle); - return gbFileError_Invalid; - } - } - - fd->p = handle; - *ops = gbDefaultFileOperations; - return gbFileError_None; - } - -#else // POSIX - gb_internal GB_FILE_SEEK_PROC(gb__posix_file_seek) { - #if defined(GB_SYSTEM_OSX) - i64 res = lseek(fd.i, offset, whence); - #else - i64 res = lseek64(fd.i, offset, whence); - #endif - if (res < 0) return false; - if (new_offset) *new_offset = res; - return true; - } - - gb_internal GB_FILE_READ_AT_PROC(gb__posix_file_read) { - isize res = pread(fd.i, buffer, size, offset); - if (res < 0) return false; - if (bytes_read) *bytes_read = res; - return true; - } - - gb_internal GB_FILE_WRITE_AT_PROC(gb__posix_file_write) { - isize res; - i64 curr_offset = 0; - gb__posix_file_seek(fd, 0, gbSeekWhence_Current, &curr_offset); - if (curr_offset == offset) { - // NOTE(bill): Writing to stdout et al. doesn't like pwrite for numerous reasons - res = write(cast(int)fd.i, buffer, size); - } else { - res = pwrite(cast(int)fd.i, buffer, size, offset); - } - if (res < 0) return false; - if (bytes_written) *bytes_written = res; - return true; - } - - - gb_internal GB_FILE_CLOSE_PROC(gb__posix_file_close) { - close(fd.i); - } - - gbFileOperations const gbDefaultFileOperations = { - gb__posix_file_read, - gb__posix_file_write, - gb__posix_file_seek, - gb__posix_file_close - }; - - gb_no_inline GB_FILE_OPEN_PROC(gb__posix_file_open) { - i32 os_mode; - switch (mode & gbFileMode_Modes) { - case gbFileMode_Read: - os_mode = O_RDONLY; - break; - case gbFileMode_Write: - os_mode = O_WRONLY | O_CREAT | O_TRUNC; - break; - case gbFileMode_Append: - os_mode = O_WRONLY | O_APPEND | O_CREAT; - break; - case gbFileMode_Read | gbFileMode_Rw: - os_mode = O_RDWR; - break; - case gbFileMode_Write | gbFileMode_Rw: - os_mode = O_RDWR | O_CREAT | O_TRUNC; - break; - case gbFileMode_Append | gbFileMode_Rw: - os_mode = O_RDWR | O_APPEND | O_CREAT; - break; - default: - GB_PANIC("Invalid file mode"); - return gbFileError_Invalid; - } - - fd->i = open(filename, os_mode, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH); - if (fd->i < 0) { - // TODO(bill): More file errors - return gbFileError_Invalid; - } - - *ops = gbDefaultFileOperations; - return gbFileError_None; - } - -#endif - - - -gbFileError gb_file_new(gbFile *f, gbFileDescriptor fd, gbFileOperations ops, char const *filename) { - gbFileError err = gbFileError_None; - isize len = gb_strlen(filename); - - // gb_printf_err("gb_file_new: %s\n", filename); - - f->ops = ops; - f->fd = fd; - f->filename = gb_alloc_array(gb_heap_allocator(), char, len+1); - gb_memcopy(cast(char *)f->filename, cast(char *)filename, len+1); - f->last_write_time = gb_file_last_write_time(f->filename); - - return err; -} - - - -gbFileError gb_file_open_mode(gbFile *f, gbFileMode mode, char const *filename) { - gbFileError err; -#if defined(GB_SYSTEM_WINDOWS) - err = gb__win32_file_open(&f->fd, &f->ops, mode, filename); -#else - err = gb__posix_file_open(&f->fd, &f->ops, mode, filename); -#endif - if (err == gbFileError_None) { - return gb_file_new(f, f->fd, f->ops, filename); - } - return err; -} - -gbFileError gb_file_close(gbFile *f) { - if (f == NULL) { - return gbFileError_Invalid; - } - -#if defined(GB_COMPILER_MSVC) - if (f->filename != NULL) { - gb_free(gb_heap_allocator(), cast(char *)f->filename); - } -#else - // TODO HACK(bill): Memory Leak!!! -#endif - -#if defined(GB_SYSTEM_WINDOWS) - if (f->fd.p == INVALID_HANDLE_VALUE) { - return gbFileError_Invalid; - } -#else - if (f->fd.i < 0) { - return gbFileError_Invalid; - } -#endif - - if (!f->ops.read_at) f->ops = gbDefaultFileOperations; - f->ops.close(f->fd); - - return gbFileError_None; -} - -gb_inline b32 gb_file_read_at_check(gbFile *f, void *buffer, isize size, i64 offset, isize *bytes_read) { - if (!f->ops.read_at) f->ops = gbDefaultFileOperations; - return f->ops.read_at(f->fd, buffer, size, offset, bytes_read); -} - -gb_inline b32 gb_file_write_at_check(gbFile *f, void const *buffer, isize size, i64 offset, isize *bytes_written) { - if (!f->ops.read_at) f->ops = gbDefaultFileOperations; - return f->ops.write_at(f->fd, buffer, size, offset, bytes_written); -} - - -gb_inline b32 gb_file_read_at(gbFile *f, void *buffer, isize size, i64 offset) { - return gb_file_read_at_check(f, buffer, size, offset, NULL); -} - -gb_inline b32 gb_file_write_at(gbFile *f, void const *buffer, isize size, i64 offset) { - return gb_file_write_at_check(f, buffer, size, offset, NULL); -} - -gb_inline i64 gb_file_seek(gbFile *f, i64 offset) { - i64 new_offset = 0; - if (!f->ops.read_at) f->ops = gbDefaultFileOperations; - f->ops.seek(f->fd, offset, gbSeekWhence_Begin, &new_offset); - return new_offset; -} - -gb_inline i64 gb_file_seek_to_end(gbFile *f) { - i64 new_offset = 0; - if (!f->ops.read_at) f->ops = gbDefaultFileOperations; - f->ops.seek(f->fd, 0, gbSeekWhence_End, &new_offset); - return new_offset; -} - -// NOTE(bill): Skips a certain amount of bytes -gb_inline i64 gb_file_skip(gbFile *f, i64 bytes) { - i64 new_offset = 0; - if (!f->ops.read_at) f->ops = gbDefaultFileOperations; - f->ops.seek(f->fd, bytes, gbSeekWhence_Current, &new_offset); - return new_offset; -} - -gb_inline i64 gb_file_tell(gbFile *f) { - i64 new_offset = 0; - if (!f->ops.read_at) f->ops = gbDefaultFileOperations; - f->ops.seek(f->fd, 0, gbSeekWhence_Current, &new_offset); - return new_offset; -} -gb_inline b32 gb_file_read (gbFile *f, void *buffer, isize size) { return gb_file_read_at(f, buffer, size, gb_file_tell(f)); } -gb_inline b32 gb_file_write(gbFile *f, void const *buffer, isize size) { return gb_file_write_at(f, buffer, size, gb_file_tell(f)); } - - -gbFileError gb_file_create(gbFile *f, char const *filename) { - return gb_file_open_mode(f, gbFileMode_Write|gbFileMode_Rw, filename); -} - - -gbFileError gb_file_open(gbFile *f, char const *filename) { - return gb_file_open_mode(f, gbFileMode_Read, filename); -} - - -char const *gb_file_name(gbFile *f) { return f->filename ? f->filename : ""; } - -gb_inline b32 gb_file_has_changed(gbFile *f) { - b32 result = false; - gbFileTime last_write_time = gb_file_last_write_time(f->filename); - if (f->last_write_time != last_write_time) { - result = true; - f->last_write_time = last_write_time; - } - return result; -} - -// TODO(bill): Is this a bad idea? -gb_global b32 gb__std_file_set = false; -gb_global gbFile gb__std_files[gbFileStandard_Count] = {{0}}; - - -#if defined(GB_SYSTEM_WINDOWS) - -gb_inline gbFile *const gb_file_get_standard(gbFileStandardType std) { - if (!gb__std_file_set) { - #define GB__SET_STD_FILE(type, v) gb__std_files[type].fd.p = v; gb__std_files[type].ops = gbDefaultFileOperations - GB__SET_STD_FILE(gbFileStandard_Input, GetStdHandle(STD_INPUT_HANDLE)); - GB__SET_STD_FILE(gbFileStandard_Output, GetStdHandle(STD_OUTPUT_HANDLE)); - GB__SET_STD_FILE(gbFileStandard_Error, GetStdHandle(STD_ERROR_HANDLE)); - #undef GB__SET_STD_FILE - gb__std_file_set = true; - } - return &gb__std_files[std]; -} - -gb_inline i64 gb_file_size(gbFile *f) { - LARGE_INTEGER size; - GetFileSizeEx(f->fd.p, &size); - return size.QuadPart; -} - -gbFileError gb_file_truncate(gbFile *f, i64 size) { - gbFileError err = gbFileError_None; - i64 prev_offset = gb_file_tell(f); - gb_file_seek(f, size); - if (!SetEndOfFile(f)) { - err = gbFileError_TruncationFailure; - } - gb_file_seek(f, prev_offset); - return err; -} - - -b32 gb_file_exists(char const *name) { - WIN32_FIND_DATAW data; - wchar_t *w_text; - void *handle; - b32 found = false; - gbAllocator a = gb_heap_allocator(); - - w_text = gb__alloc_utf8_to_ucs2(a, name, NULL); - if (w_text == NULL) { - return false; - } - handle = FindFirstFileW(w_text, &data); - gb_free(a, w_text); - found = handle != INVALID_HANDLE_VALUE; - if (found) FindClose(handle); - return found; -} - -#else // POSIX - -gb_inline gbFile *const gb_file_get_standard(gbFileStandardType std) { - if (!gb__std_file_set) { - #define GB__SET_STD_FILE(type, v) gb__std_files[type].fd.i = v; gb__std_files[type].ops = gbDefaultFileOperations - GB__SET_STD_FILE(gbFileStandard_Input, 0); - GB__SET_STD_FILE(gbFileStandard_Output, 1); - GB__SET_STD_FILE(gbFileStandard_Error, 2); - #undef GB__SET_STD_FILE - gb__std_file_set = true; - } - return &gb__std_files[std]; -} - -gb_inline i64 gb_file_size(gbFile *f) { - i64 size = 0; - i64 prev_offset = gb_file_tell(f); - gb_file_seek_to_end(f); - size = gb_file_tell(f); - gb_file_seek(f, prev_offset); - return size; -} - -gb_inline gbFileError gb_file_truncate(gbFile *f, i64 size) { - gbFileError err = gbFileError_None; - int i = ftruncate(f->fd.i, size); - if (i != 0) err = gbFileError_TruncationFailure; - return err; -} - -gb_inline b32 gb_file_exists(char const *name) { - return access(name, F_OK) != -1; -} -#endif - - - -#if defined(GB_SYSTEM_WINDOWS) -gbFileTime gb_file_last_write_time(char const *filepath) { - ULARGE_INTEGER li = {0}; - FILETIME last_write_time = {0}; - WIN32_FILE_ATTRIBUTE_DATA data = {0}; - gbAllocator a = gb_heap_allocator(); - - wchar_t *w_text = gb__alloc_utf8_to_ucs2(a, filepath, NULL); - if (w_text == NULL) { - return 0; - } - - if (GetFileAttributesExW(w_text, GetFileExInfoStandard, &data)) { - last_write_time = data.ftLastWriteTime; - } - gb_free(a, w_text); - - li.LowPart = last_write_time.dwLowDateTime; - li.HighPart = last_write_time.dwHighDateTime; - return cast(gbFileTime)li.QuadPart; -} - - -gb_inline b32 gb_file_copy(char const *existing_filename, char const *new_filename, b32 fail_if_exists) { - wchar_t *w_old = NULL; - wchar_t *w_new = NULL; - gbAllocator a = gb_heap_allocator(); - b32 result = false; - - w_old = gb__alloc_utf8_to_ucs2(a, existing_filename, NULL); - if (w_old == NULL) { - return false; - } - w_new = gb__alloc_utf8_to_ucs2(a, new_filename, NULL); - if (w_new != NULL) { - result = CopyFileW(w_old, w_new, fail_if_exists); - } - gb_free(a, w_new); - gb_free(a, w_old); - return result; -} - -gb_inline b32 gb_file_move(char const *existing_filename, char const *new_filename) { - wchar_t *w_old = NULL; - wchar_t *w_new = NULL; - gbAllocator a = gb_heap_allocator(); - b32 result = false; - - w_old = gb__alloc_utf8_to_ucs2(a, existing_filename, NULL); - if (w_old == NULL) { - return false; - } - w_new = gb__alloc_utf8_to_ucs2(a, new_filename, NULL); - if (w_new != NULL) { - result = MoveFileW(w_old, w_new); - } - gb_free(a, w_new); - gb_free(a, w_old); - return result; -} - -b32 gb_file_remove(char const *filename) { - wchar_t *w_filename = NULL; - gbAllocator a = gb_heap_allocator(); - b32 result = false; - w_filename = gb__alloc_utf8_to_ucs2(a, filename, NULL); - if (w_filename == NULL) { - return false; - } - result = DeleteFileW(w_filename); - gb_free(a, w_filename); - return result; -} - - - -#else - -gbFileTime gb_file_last_write_time(char const *filepath) { - time_t result = 0; - struct stat file_stat; - - if (stat(filepath, &file_stat) == 0) { - result = file_stat.st_mtime; - } - - return cast(gbFileTime)result; -} - - -gb_inline b32 gb_file_copy(char const *existing_filename, char const *new_filename, b32 fail_if_exists) { -#if defined(GB_SYSTEM_OSX) - return copyfile(existing_filename, new_filename, NULL, COPYFILE_DATA) == 0; -#else - isize size; - int existing_fd = open(existing_filename, O_RDONLY, 0); - int new_fd = open(new_filename, O_WRONLY|O_CREAT, 0666); - - struct stat stat_existing; - fstat(existing_fd, &stat_existing); - - size = sendfile(new_fd, existing_fd, 0, stat_existing.st_size); - - close(new_fd); - close(existing_fd); - - return size == stat_existing.st_size; -#endif -} - -gb_inline b32 gb_file_move(char const *existing_filename, char const *new_filename) { - if (link(existing_filename, new_filename) == 0) { - return unlink(existing_filename) != -1; - } - return false; -} - -b32 gb_file_remove(char const *filename) { -#if defined(GB_SYSTEM_OSX) - return unlink(filename) != -1; -#else - return remove(filename) == 0; -#endif -} - - -#endif - - - - - -gbFileContents gb_file_read_contents(gbAllocator a, b32 zero_terminate, char const *filepath) { - gbFileContents result = {0}; - gbFile file = {0}; - - result.allocator = a; - - if (gb_file_open(&file, filepath) == gbFileError_None) { - isize file_size = cast(isize)gb_file_size(&file); - if (file_size > 0) { - result.data = gb_alloc(a, zero_terminate ? file_size+1 : file_size); - result.size = file_size; - gb_file_read_at(&file, result.data, result.size, 0); - if (zero_terminate) { - u8 *str = cast(u8 *)result.data; - str[file_size] = '\0'; - } - } - gb_file_close(&file); - } - - return result; -} - -void gb_file_free_contents(gbFileContents *fc) { - GB_ASSERT_NOT_NULL(fc->data); - gb_free(fc->allocator, fc->data); - fc->data = NULL; - fc->size = 0; -} - - - - - -gb_inline b32 gb_path_is_absolute(char const *path) { - b32 result = false; - GB_ASSERT_NOT_NULL(path); -#if defined(GB_SYSTEM_WINDOWS) - result == (gb_strlen(path) > 2) && - gb_char_is_alpha(path[0]) && - (path[1] == ':' && path[2] == GB_PATH_SEPARATOR); -#else - result = (gb_strlen(path) > 0 && path[0] == GB_PATH_SEPARATOR); -#endif - return result; -} - -gb_inline b32 gb_path_is_relative(char const *path) { return !gb_path_is_absolute(path); } - -gb_inline b32 gb_path_is_root(char const *path) { - b32 result = false; - GB_ASSERT_NOT_NULL(path); -#if defined(GB_SYSTEM_WINDOWS) - result = gb_path_is_absolute(path) && (gb_strlen(path) == 3); -#else - result = gb_path_is_absolute(path) && (gb_strlen(path) == 1); -#endif - return result; -} - -gb_inline char const *gb_path_base_name(char const *path) { - char const *ls; - GB_ASSERT_NOT_NULL(path); - ls = gb_char_last_occurence(path, '/'); - return (ls == NULL) ? path : ls+1; -} - -gb_inline char const *gb_path_extension(char const *path) { - char const *ld; - GB_ASSERT_NOT_NULL(path); - ld = gb_char_last_occurence(path, '.'); - return (ld == NULL) ? NULL : ld+1; -} - - -#if !defined(_WINDOWS_) && defined(GB_SYSTEM_WINDOWS) -GB_DLL_IMPORT DWORD WINAPI GetFullPathNameA(char const *lpFileName, DWORD nBufferLength, char *lpBuffer, char **lpFilePart); -GB_DLL_IMPORT DWORD WINAPI GetFullPathNameW(wchar_t const *lpFileName, DWORD nBufferLength, wchar_t *lpBuffer, wchar_t **lpFilePart); -#endif - -char *gb_path_get_full_name(gbAllocator a, char const *path) { -#if defined(GB_SYSTEM_WINDOWS) -// TODO(bill): Make UTF-8 - wchar_t *w_path = NULL; - wchar_t *w_fullpath = NULL; - isize w_len = 0; - isize new_len = 0; - isize new_len1 = 0; - char *new_path = 0; - w_path = gb__alloc_utf8_to_ucs2(gb_heap_allocator(), path, NULL); - if (w_path == NULL) { - return NULL; - } - w_len = GetFullPathNameW(w_path, 0, NULL, NULL); - if (w_len == 0) { - return NULL; - } - w_fullpath = gb_alloc_array(gb_heap_allocator(), wchar_t, w_len+1); - GetFullPathNameW(w_path, cast(int)w_len, w_fullpath, NULL); - w_fullpath[w_len] = 0; - gb_free(gb_heap_allocator(), w_path); - - new_len = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, w_fullpath, cast(int)w_len, NULL, 0, NULL, NULL); - if (new_len == 0) { - gb_free(gb_heap_allocator(), w_fullpath); - return NULL; - } - new_path = gb_alloc_array(a, char, new_len+1); - new_len1 = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, w_fullpath, cast(int)w_len, new_path, cast(int)new_len, NULL, NULL); - if (new_len1 == 0) { - gb_free(gb_heap_allocator(), w_fullpath); - gb_free(a, new_path); - return NULL; - } - new_path[new_len] = 0; - return new_path; -#else - char *p, *result, *fullpath = NULL; - isize len; - p = realpath(path, NULL); - fullpath = p; - if (p == NULL) { - // NOTE(bill): File does not exist - fullpath = cast(char *)path; - } - - len = gb_strlen(fullpath); - - result = gb_alloc_array(a, char, len + 1); - gb_memmove(result, fullpath, len); - result[len] = 0; - free(p); - - return result; -#endif -} - - - - - -//////////////////////////////////////////////////////////////// -// -// Printing -// -// - - -isize gb_printf(char const *fmt, ...) { - isize res; - va_list va; - va_start(va, fmt); - res = gb_printf_va(fmt, va); - va_end(va); - return res; -} - - -isize gb_printf_err(char const *fmt, ...) { - isize res; - va_list va; - va_start(va, fmt); - res = gb_printf_err_va(fmt, va); - va_end(va); - return res; -} - -isize gb_fprintf(struct gbFile *f, char const *fmt, ...) { - isize res; - va_list va; - va_start(va, fmt); - res = gb_fprintf_va(f, fmt, va); - va_end(va); - return res; -} - -char *gb_bprintf(char const *fmt, ...) { - va_list va; - char *str; - va_start(va, fmt); - str = gb_bprintf_va(fmt, va); - va_end(va); - return str; -} - -isize gb_snprintf(char *str, isize n, char const *fmt, ...) { - isize res; - va_list va; - va_start(va, fmt); - res = gb_snprintf_va(str, n, fmt, va); - va_end(va); - return res; -} - - - -gb_inline isize gb_printf_va(char const *fmt, va_list va) { - return gb_fprintf_va(gb_file_get_standard(gbFileStandard_Output), fmt, va); -} - -gb_inline isize gb_printf_err_va(char const *fmt, va_list va) { - return gb_fprintf_va(gb_file_get_standard(gbFileStandard_Error), fmt, va); -} - -gb_inline isize gb_fprintf_va(struct gbFile *f, char const *fmt, va_list va) { - gb_local_persist char buf[4096]; - isize len = gb_snprintf_va(buf, gb_size_of(buf), fmt, va); - gb_file_write(f, buf, len-1); // NOTE(bill): prevent extra whitespace - return len; -} - - -gb_inline char *gb_bprintf_va(char const *fmt, va_list va) { - gb_local_persist char buffer[4096]; - gb_snprintf_va(buffer, gb_size_of(buffer), fmt, va); - return buffer; -} - - -enum { - gbFmt_Minus = GB_BIT(0), - gbFmt_Plus = GB_BIT(1), - gbFmt_Alt = GB_BIT(2), - gbFmt_Space = GB_BIT(3), - gbFmt_Zero = GB_BIT(4), - - gbFmt_Char = GB_BIT(5), - gbFmt_Short = GB_BIT(6), - gbFmt_Int = GB_BIT(7), - gbFmt_Long = GB_BIT(8), - gbFmt_Llong = GB_BIT(9), - gbFmt_Size = GB_BIT(10), - gbFmt_Intptr = GB_BIT(11), - - gbFmt_Unsigned = GB_BIT(12), - gbFmt_Lower = GB_BIT(13), - gbFmt_Upper = GB_BIT(14), - - - gbFmt_Done = GB_BIT(30), - - gbFmt_Ints = gbFmt_Char|gbFmt_Short|gbFmt_Int|gbFmt_Long|gbFmt_Llong|gbFmt_Size|gbFmt_Intptr -}; - -typedef struct { - i32 base; - i32 flags; - i32 width; - i32 precision; -} gbprivFmtInfo; - - -gb_internal isize gb__print_string(char *text, isize max_len, gbprivFmtInfo *info, char const *str) { - // TODO(bill): Get precision and width to work correctly. How does it actually work?! - // TODO(bill): This looks very buggy indeed. - isize res = 0, len; - isize remaining = max_len; - - if (info && info->precision >= 0) { - len = gb_strnlen(str, info->precision); - } else { - len = gb_strlen(str); - } - - if (info && (info->width == 0 || info->flags & gbFmt_Minus)) { - if (info->precision > 0) { - len = info->precision < len ? info->precision : len; - } - - res += gb_strlcpy(text, str, len); - - if (info->width > res) { - isize padding = info->width - len; - char pad = (info->flags & gbFmt_Zero) ? '0' : ' '; - while (padding --> 0 && remaining --> 0) { - *text++ = pad, res++; - } - } - } else { - if (info && (info->width > res)) { - isize padding = info->width - len; - char pad = (info->flags & gbFmt_Zero) ? '0' : ' '; - while (padding --> 0 && remaining --> 0) { - *text++ = pad, res++; - } - } - - res += gb_strlcpy(text, str, len); - } - - - if (info) { - if (info->flags & gbFmt_Upper) { - gb_str_to_upper(text); - } else if (info->flags & gbFmt_Lower) { - gb_str_to_lower(text); - } - } - - return res; -} - -gb_internal isize gb__print_char(char *text, isize max_len, gbprivFmtInfo *info, char arg) { - char str[2] = ""; - str[0] = arg; - return gb__print_string(text, max_len, info, str); -} - - -gb_internal isize gb__print_i64(char *text, isize max_len, gbprivFmtInfo *info, i64 value) { - char num[130]; - gb_i64_to_str(value, num, info ? info->base : 10); - return gb__print_string(text, max_len, info, num); -} - -gb_internal isize gb__print_u64(char *text, isize max_len, gbprivFmtInfo *info, u64 value) { - char num[130]; - gb_u64_to_str(value, num, info ? info->base : 10); - return gb__print_string(text, max_len, info, num); -} - - -gb_internal isize gb__print_f64(char *text, isize max_len, gbprivFmtInfo *info, f64 arg) { - // TODO(bill): Handle exponent notation - isize width, len, remaining = max_len; - char *text_begin = text; - - if (arg) { - u64 value; - if (arg < 0) { - if (remaining > 1) { - *text = '-', remaining--; - } - text++; - arg = -arg; - } else if (info->flags & gbFmt_Minus) { - if (remaining > 1) { - *text = '+', remaining--; - } - text++; - } - - value = cast(u64)arg; - len = gb__print_u64(text, remaining, NULL, value); - text += len; - - if (len >= remaining) { - remaining = gb_min(remaining, 1); - } else { - remaining -= len; - } - arg -= value; - - if (info->precision < 0) { - info->precision = 6; - } - - if ((info->flags & gbFmt_Alt) || info->precision > 0) { - i64 mult = 10; - if (remaining > 1) { - *text = '.', remaining--; - } - text++; - while (info->precision-- > 0) { - value = cast(u64)(arg * mult); - len = gb__print_u64(text, remaining, NULL, value); - text += len; - if (len >= remaining) { - remaining = gb_min(remaining, 1); - } else { - remaining -= len; - } - arg -= cast(f64)value / mult; - mult *= 10; - } - } - } else { - if (remaining > 1) { - *text = '0', remaining--; - } - text++; - if (info->flags & gbFmt_Alt) { - if (remaining > 1) { - *text = '.', remaining--; - } - text++; - } - } - - width = info->width - (text - text_begin); - if (width > 0) { - char fill = (info->flags & gbFmt_Zero) ? '0' : ' '; - char *end = text+remaining-1; - len = (text - text_begin); - - for (len = (text - text_begin); len--; ) { - if ((text_begin+len+width) < end) { - *(text_begin+len+width) = *(text_begin+len); - } - } - - len = width; - text += len; - if (len >= remaining) { - remaining = gb_min(remaining, 1); - } else { - remaining -= len; - } - - while (len--) { - if (text_begin+len < end) { - text_begin[len] = fill; - } - } - } - - return (text - text_begin); -} - - - -gb_no_inline isize gb_snprintf_va(char *text, isize max_len, char const *fmt, va_list va) { - char const *text_begin = text; - isize remaining = max_len, res; - - while (*fmt) { - gbprivFmtInfo info = {0}; - isize len = 0; - info.precision = -1; - - while (*fmt && *fmt != '%' && remaining) { - *text++ = *fmt++; - } - - if (*fmt == '%') { - do { - switch (*++fmt) { - case '-': info.flags |= gbFmt_Minus; break; - case '+': info.flags |= gbFmt_Plus; break; - case '#': info.flags |= gbFmt_Alt; break; - case ' ': info.flags |= gbFmt_Space; break; - case '0': info.flags |= gbFmt_Zero; break; - default: info.flags |= gbFmt_Done; break; - } - } while (!(info.flags & gbFmt_Done)); - } - - // NOTE(bill): Optional Width - if (*fmt == '*') { - int width = va_arg(va, int); - if (width < 0) { - info.flags |= gbFmt_Minus; - info.width = -width; - } else { - info.width = width; - } - fmt++; - } else { - info.width = cast(i32)gb_str_to_i64(fmt, cast(char **)&fmt, 10); - } - - // NOTE(bill): Optional Precision - if (*fmt == '.') { - fmt++; - if (*fmt == '*') { - info.precision = va_arg(va, int); - fmt++; - } else { - info.precision = cast(i32)gb_str_to_i64(fmt, cast(char **)&fmt, 10); - } - info.flags &= ~gbFmt_Zero; - } - - - switch (*fmt++) { - case 'h': - if (*fmt == 'h') { // hh => char - info.flags |= gbFmt_Char; - fmt++; - } else { // h => short - info.flags |= gbFmt_Short; - } - break; - - case 'l': - if (*fmt == 'l') { // ll => long long - info.flags |= gbFmt_Llong; - fmt++; - } else { // l => long - info.flags |= gbFmt_Long; - } - break; - - break; - - case 'z': // NOTE(bill): usize - info.flags |= gbFmt_Unsigned; - // fallthrough - case 't': // NOTE(bill): isize - info.flags |= gbFmt_Size; - break; - - default: fmt--; break; - } - - - switch (*fmt) { - case 'u': - info.flags |= gbFmt_Unsigned; - // fallthrough - case 'd': - case 'i': - info.base = 10; - break; - - case 'o': - info.base = 8; - break; - - case 'x': - info.base = 16; - info.flags |= (gbFmt_Unsigned | gbFmt_Lower); - break; - - case 'X': - info.base = 16; - info.flags |= (gbFmt_Unsigned | gbFmt_Upper); - break; - - case 'f': - case 'F': - case 'g': - case 'G': - len = gb__print_f64(text, remaining, &info, va_arg(va, f64)); - break; - - case 'a': - case 'A': - // TODO(bill): - break; - - case 'c': - len = gb__print_char(text, remaining, &info, cast(char)va_arg(va, int)); - break; - - case 's': - len = gb__print_string(text, remaining, &info, va_arg(va, char *)); - break; - - case 'p': - info.base = 16; - info.flags |= (gbFmt_Lower|gbFmt_Unsigned|gbFmt_Alt|gbFmt_Intptr); - break; - - case '%': - len = gb__print_char(text, remaining, &info, '%'); - break; - - default: fmt--; break; - } - - fmt++; - - if (info.base != 0) { - if (info.flags & gbFmt_Unsigned) { - u64 value = 0; - switch (info.flags & gbFmt_Ints) { - case gbFmt_Char: value = cast(u64)cast(u8) va_arg(va, int); break; - case gbFmt_Short: value = cast(u64)cast(u16)va_arg(va, int); break; - case gbFmt_Long: value = cast(u64)va_arg(va, unsigned long); break; - case gbFmt_Llong: value = cast(u64)va_arg(va, unsigned long long); break; - case gbFmt_Size: value = cast(u64)va_arg(va, usize); break; - case gbFmt_Intptr: value = cast(u64)va_arg(va, uintptr); break; - default: value = cast(u64)va_arg(va, unsigned int); break; - } - - len = gb__print_u64(text, remaining, &info, value); - - } else { - i64 value = 0; - switch (info.flags & gbFmt_Ints) { - case gbFmt_Char: value = cast(i64)cast(i8) va_arg(va, int); break; - case gbFmt_Short: value = cast(i64)cast(i16)va_arg(va, int); break; - case gbFmt_Long: value = cast(i64)va_arg(va, long); break; - case gbFmt_Llong: value = cast(i64)va_arg(va, long long); break; - case gbFmt_Size: value = cast(i64)va_arg(va, usize); break; - case gbFmt_Intptr: value = cast(i64)va_arg(va, uintptr); break; - default: value = cast(i64)va_arg(va, int); break; - } - - len = gb__print_i64(text, remaining, &info, value); - } - } - - - text += len; - if (len >= remaining) { - remaining = gb_min(remaining, 1); - } else { - remaining -= len; - } - } - - *text++ = '\0'; - res = (text - text_begin); - return (res >= max_len || res < 0) ? -1 : res; -} - - -//////////////////////////////////////////////////////////////// -// -// DLL Handling -// -// - -#if defined(GB_SYSTEM_WINDOWS) - -gbDllHandle gb_dll_load(char const *filepath) { - return cast(gbDllHandle)LoadLibraryA(filepath); -} -gb_inline void gb_dll_unload (gbDllHandle dll) { FreeLibrary(cast(HMODULE)dll); } -gb_inline gbDllProc gb_dll_proc_address(gbDllHandle dll, char const *proc_name) { return cast(gbDllProc)GetProcAddress(cast(HMODULE)dll, proc_name); } - -#else // POSIX - -gbDllHandle gb_dll_load(char const *filepath) { - // TODO(bill): Should this be RTLD_LOCAL? - return cast(gbDllHandle)dlopen(filepath, RTLD_LAZY|RTLD_GLOBAL); -} - -gb_inline void gb_dll_unload (gbDllHandle dll) { dlclose(dll); } -gb_inline gbDllProc gb_dll_proc_address(gbDllHandle dll, char const *proc_name) { return cast(gbDllProc)dlsym(dll, proc_name); } - -#endif - - -//////////////////////////////////////////////////////////////// -// -// Time -// -// - -#if defined(GB_COMPILER_MSVC) && !defined(__clang__) - gb_inline u64 gb_rdtsc(void) { return __rdtsc(); } -#elif defined(__i386__) - gb_inline u64 gb_rdtsc(void) { - u64 x; - __asm__ volatile (".byte 0x0f, 0x31" : "=A" (x)); - return x; - } -#elif defined(__x86_64__) - gb_inline u64 gb_rdtsc(void) { - u32 hi, lo; - __asm__ __volatile__ ("rdtsc" : "=a"(lo), "=d"(hi)); - return (cast(u64)lo) | ((cast(u64)hi)<<32); - } -#elif defined(__powerpc__) - gb_inline u64 gb_rdtsc(void) { - u64 result = 0; - u32 upper, lower,tmp; - __asm__ volatile( - "0: \n" - "\tmftbu %0 \n" - "\tmftb %1 \n" - "\tmftbu %2 \n" - "\tcmpw %2,%0 \n" - "\tbne 0b \n" - : "=r"(upper),"=r"(lower),"=r"(tmp) - ); - result = upper; - result = result<<32; - result = result|lower; - - return result; - } -#endif - -#if defined(GB_SYSTEM_WINDOWS) - - gb_inline f64 gb_time_now(void) { - gb_local_persist LARGE_INTEGER win32_perf_count_freq = {0}; - f64 result; - LARGE_INTEGER counter; - if (!win32_perf_count_freq.QuadPart) { - QueryPerformanceFrequency(&win32_perf_count_freq); - GB_ASSERT(win32_perf_count_freq.QuadPart != 0); - } - - QueryPerformanceCounter(&counter); - - result = counter.QuadPart / cast(f64)(win32_perf_count_freq.QuadPart); - return result; - } - - gb_inline u64 gb_utc_time_now(void) { - FILETIME ft; - ULARGE_INTEGER li; - - GetSystemTimeAsFileTime(&ft); - li.LowPart = ft.dwLowDateTime; - li.HighPart = ft.dwHighDateTime; - - return li.QuadPart/10; - } - - gb_inline void gb_sleep_ms(u32 ms) { Sleep(ms); } - -#else - - gb_global f64 gb__timebase = 0.0; - gb_global u64 gb__timestart = 0; - - gb_inline f64 gb_time_now(void) { -#if defined(GB_SYSTEM_OSX) - f64 result; - - if (!gb__timestart) { - mach_timebase_info_data_t tb = {0}; - mach_timebase_info(&tb); - gb__timebase = tb.numer; - gb__timebase /= tb.denom; - gb__timestart = mach_absolute_time(); - } - - // NOTE(bill): mach_absolute_time() returns things in nanoseconds - result = 1.0e-9 * (mach_absolute_time() - gb__timestart) * gb__timebase; - return result; -#else - struct timespec t; - f64 result; - - // IMPORTANT TODO(bill): THIS IS A HACK - clock_gettime(1 /*CLOCK_MONOTONIC*/, &t); - result = t.tv_sec + 1.0e-9 * t.tv_nsec; - return result; -#endif - } - - gb_inline u64 gb_utc_time_now(void) { - struct timespec t; -#if defined(GB_SYSTEM_OSX) - clock_serv_t cclock; - mach_timespec_t mts; - host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock); - clock_get_time(cclock, &mts); - mach_port_deallocate(mach_task_self(), cclock); - t.tv_sec = mts.tv_sec; - t.tv_nsec = mts.tv_nsec; -#else - // IMPORTANT TODO(bill): THIS IS A HACK - clock_gettime(0 /*CLOCK_REALTIME*/, &t); -#endif - return cast(u64)t.tv_sec * 1000000ull + t.tv_nsec/1000 + 11644473600000000ull; - } - - gb_inline void gb_sleep_ms(u32 ms) { - struct timespec req = {cast(time_t)ms/1000, cast(long)((ms%1000)*1000000)}; - struct timespec rem = {0, 0}; - nanosleep(&req, &rem); - } - -#endif - - - -//////////////////////////////////////////////////////////////// -// -// Miscellany -// -// - -gb_global gbAtomic32 gb__random_shared_counter = {0}; - -gb_internal u32 gb__get_noise_from_time(void) { - u32 accum = 0; - f64 start, remaining, end, curr = 0; - u64 interval = 100000ll; - - start = gb_time_now(); - remaining = (interval - cast(u64)(interval*start)%interval) / cast(f64)interval; - end = start + remaining; - - do { - curr = gb_time_now(); - accum += cast(u32)curr; - } while (curr >= end); - return accum; -} - -// NOTE(bill): Partly from http://preshing.com/20121224/how-to-generate-a-sequence-of-unique-random-integers/ -// But the generation is even more random-er-est - -gb_internal gb_inline u32 gb__permute_qpr(u32 x) { - gb_local_persist u32 const prime = 4294967291; // 2^32 - 5 - if (x >= prime) { - return x; - } else { - u32 residue = cast(u32)(cast(u64) x * x) % prime; - if (x <= prime / 2) { - return residue; - } else { - return prime - residue; - } - } -} - -gb_internal gb_inline u32 gb__permute_with_offset(u32 x, u32 offset) { - return (gb__permute_qpr(x) + offset) ^ 0x5bf03635; -} - - -void gb_random_init(gbRandom *r) { - u64 time, tick; - isize i, j; - u32 x = 0; - r->value = 0; - - r->offsets[0] = gb__get_noise_from_time(); - r->offsets[1] = gb_atomic32_fetch_add(&gb__random_shared_counter, 1); - r->offsets[2] = gb_thread_current_id(); - r->offsets[3] = gb_thread_current_id() * 3 + 1; - time = gb_utc_time_now(); - r->offsets[4] = cast(u32)(time >> 32); - r->offsets[5] = cast(u32)time; - r->offsets[6] = gb__get_noise_from_time(); - tick = gb_rdtsc(); - r->offsets[7] = cast(u32)(tick ^ (tick >> 32)); - - for (j = 0; j < 4; j++) { - for (i = 0; i < gb_count_of(r->offsets); i++) { - r->offsets[i] = x = gb__permute_with_offset(x, r->offsets[i]); - } - } -} - -u32 gb_random_gen_u32(gbRandom *r) { - u32 x = r->value; - u32 carry = 1; - isize i; - for (i = 0; i < gb_count_of(r->offsets); i++) { - x = gb__permute_with_offset(x, r->offsets[i]); - if (carry > 0) { - carry = ++r->offsets[i] ? 0 : 1; - } - } - - r->value = x; - return x; -} - -u32 gb_random_gen_u32_unique(gbRandom *r) { - u32 x = r->value; - isize i; - r->value++; - for (i = 0; i < gb_count_of(r->offsets); i++) { - x = gb__permute_with_offset(x, r->offsets[i]); - } - - return x; -} - -u64 gb_random_gen_u64(gbRandom *r) { - return ((cast(u64)gb_random_gen_u32(r)) << 32) | gb_random_gen_u32(r); -} - - -isize gb_random_gen_isize(gbRandom *r) { - u64 u = gb_random_gen_u64(r); - return *cast(isize *)&u; -} - - - - -i64 gb_random_range_i64(gbRandom *r, i64 lower_inc, i64 higher_inc) { - u64 u = gb_random_gen_u64(r); - i64 i = *cast(i64 *)&u; - i64 diff = higher_inc-lower_inc+1; - i %= diff; - i += lower_inc; - return i; -} - -isize gb_random_range_isize(gbRandom *r, isize lower_inc, isize higher_inc) { - u64 u = gb_random_gen_u64(r); - isize i = *cast(isize *)&u; - isize diff = higher_inc-lower_inc+1; - i %= diff; - i += lower_inc; - return i; -} - -// NOTE(bill): Semi-cc'ed from gb_math to remove need for fmod and math.h -f64 gb__copy_sign64(f64 x, f64 y) { - i64 ix, iy; - ix = *(i64 *)&x; - iy = *(i64 *)&y; - - ix &= 0x7fffffffffffffff; - ix |= iy & 0x8000000000000000; - return *cast(f64 *)&ix; -} - -f64 gb__floor64 (f64 x) { return cast(f64)((x >= 0.0) ? cast(i64)x : cast(i64)(x-0.9999999999999999)); } -f64 gb__ceil64 (f64 x) { return cast(f64)((x < 0) ? cast(i64)x : (cast(i64)x)+1); } -f64 gb__round64 (f64 x) { return cast(f64)((x >= 0.0) ? gb__floor64(x + 0.5) : gb__ceil64(x - 0.5)); } -f64 gb__remainder64(f64 x, f64 y) { return x - (gb__round64(x/y)*y); } -f64 gb__abs64 (f64 x) { return x < 0 ? -x : x; } -f64 gb__sign64 (f64 x) { return x < 0 ? -1.0 : +1.0; } - -f64 gb__mod64(f64 x, f64 y) { - f64 result; - y = gb__abs64(y); - result = gb__remainder64(gb__abs64(x), y); - if (gb__sign64(result)) result += y; - return gb__copy_sign64(result, x); -} - - -f64 gb_random_range_f64(gbRandom *r, f64 lower_inc, f64 higher_inc) { - u64 u = gb_random_gen_u64(r); - f64 f = *cast(f64 *)&u; - f64 diff = higher_inc-lower_inc+1.0; - f = gb__mod64(f, diff); - f += lower_inc; - return f; -} - - - -#if defined(GB_SYSTEM_WINDOWS) -gb_inline void gb_exit(u32 code) { ExitProcess(code); } -#else -gb_inline void gb_exit(u32 code) { exit(code); } -#endif - -gb_inline void gb_yield(void) { -#if defined(GB_SYSTEM_WINDOWS) - Sleep(0); -#else - sched_yield(); -#endif -} - -gb_inline void gb_set_env(char const *name, char const *value) { -#if defined(GB_SYSTEM_WINDOWS) - // TODO(bill): Should this be a Wide version? - SetEnvironmentVariableA(name, value); -#else - setenv(name, value, 1); -#endif -} - -gb_inline void gb_unset_env(char const *name) { -#if defined(GB_SYSTEM_WINDOWS) - // TODO(bill): Should this be a Wide version? - SetEnvironmentVariableA(name, NULL); -#else - unsetenv(name); -#endif -} - - -gb_inline u16 gb_endian_swap16(u16 i) { - return (i>>8) | (i<<8); -} - -gb_inline u32 gb_endian_swap32(u32 i) { - return (i>>24) |(i<<24) | - ((i&0x00ff0000u)>>8) | ((i&0x0000ff00u)<<8); -} - -gb_inline u64 gb_endian_swap64(u64 i) { - return (i>>56) | (i<<56) | - ((i&0x00ff000000000000ull)>>40) | ((i&0x000000000000ff00ull)<<40) | - ((i&0x0000ff0000000000ull)>>24) | ((i&0x0000000000ff0000ull)<<24) | - ((i&0x000000ff00000000ull)>>8) | ((i&0x00000000ff000000ull)<<8); -} - - -gb_inline isize gb_count_set_bits(u64 mask) { - isize count = 0; - while (mask) { - count += (mask & 1); - mask >>= 1; - } - return count; -} - - - - - - -//////////////////////////////////////////////////////////////// -// -// Platform -// -// - -#if defined(GB_PLATFORM) - -gb_inline void gb_key_state_update(gbKeyState *s, b32 is_down) { - b32 was_down = (*s & gbKeyState_Down) != 0; - is_down = is_down != 0; // NOTE(bill): Make sure it's a boolean - GB_MASK_SET(*s, is_down, gbKeyState_Down); - GB_MASK_SET(*s, !was_down && is_down, gbKeyState_Pressed); - GB_MASK_SET(*s, was_down && !is_down, gbKeyState_Released); -} - -#if defined(GB_SYSTEM_WINDOWS) - -#ifndef ERROR_DEVICE_NOT_CONNECTED -#define ERROR_DEVICE_NOT_CONNECTED 1167 -#endif - -GB_XINPUT_GET_STATE(gbXInputGetState_Stub) { - gb_unused(dwUserIndex); gb_unused(pState); - return ERROR_DEVICE_NOT_CONNECTED; -} -GB_XINPUT_SET_STATE(gbXInputSetState_Stub) { - gb_unused(dwUserIndex); gb_unused(pVibration); - return ERROR_DEVICE_NOT_CONNECTED; -} - - -gb_internal gb_inline f32 gb__process_xinput_stick_value(i16 value, i16 dead_zone_threshold) { - f32 result = 0; - - if (value < -dead_zone_threshold) { - result = cast(f32) (value + dead_zone_threshold) / (32768.0f - dead_zone_threshold); - } else if (value > dead_zone_threshold) { - result = cast(f32) (value - dead_zone_threshold) / (32767.0f - dead_zone_threshold); - } - - return result; -} - -gb_internal void gb__platform_resize_dib_section(gbPlatform *p, i32 width, i32 height) { - if ((p->renderer_type == gbRenderer_Software) && - !(p->window_width == width && p->window_height == height)) { - BITMAPINFO bmi = {0}; - - if (width == 0 || height == 0) { - return; - } - - p->window_width = width; - p->window_height = height; - - // TODO(bill): Is this slow to get the desktop mode everytime? - p->sw_framebuffer.bits_per_pixel = gb_video_mode_get_desktop().bits_per_pixel; - p->sw_framebuffer.pitch = (p->sw_framebuffer.bits_per_pixel * width / 8); - - bmi.bmiHeader.biSize = gb_size_of(bmi.bmiHeader); - bmi.bmiHeader.biWidth = width; - bmi.bmiHeader.biHeight = height; // NOTE(bill): -ve is top-down, +ve is bottom-up - bmi.bmiHeader.biPlanes = 1; - bmi.bmiHeader.biBitCount = cast(u16)p->sw_framebuffer.bits_per_pixel; - bmi.bmiHeader.biCompression = 0 /*BI_RGB*/; - - p->sw_framebuffer.win32_bmi = bmi; - - - if (p->sw_framebuffer.memory) { - gb_vm_free(gb_virtual_memory(p->sw_framebuffer.memory, p->sw_framebuffer.memory_size)); - } - - { - isize memory_size = p->sw_framebuffer.pitch * height; - gbVirtualMemory vm = gb_vm_alloc(0, memory_size); - p->sw_framebuffer.memory = vm.data; - p->sw_framebuffer.memory_size = vm.size; - } - } -} - - -gb_internal gbKeyType gb__win32_from_vk(unsigned int key) { - // NOTE(bill): Letters and numbers are defined the same for VK_* and GB_* - if (key >= 'A' && key < 'Z') return cast(gbKeyType)key; - if (key >= '0' && key < '9') return cast(gbKeyType)key; - switch (key) { - case VK_ESCAPE: return gbKey_Escape; - - case VK_LCONTROL: return gbKey_Lcontrol; - case VK_LSHIFT: return gbKey_Lshift; - case VK_LMENU: return gbKey_Lalt; - case VK_LWIN: return gbKey_Lsystem; - case VK_RCONTROL: return gbKey_Rcontrol; - case VK_RSHIFT: return gbKey_Rshift; - case VK_RMENU: return gbKey_Ralt; - case VK_RWIN: return gbKey_Rsystem; - case VK_MENU: return gbKey_Menu; - - case VK_OEM_4: return gbKey_Lbracket; - case VK_OEM_6: return gbKey_Rbracket; - case VK_OEM_1: return gbKey_Semicolon; - case VK_OEM_COMMA: return gbKey_Comma; - case VK_OEM_PERIOD: return gbKey_Period; - case VK_OEM_7: return gbKey_Quote; - case VK_OEM_2: return gbKey_Slash; - case VK_OEM_5: return gbKey_Backslash; - case VK_OEM_3: return gbKey_Grave; - case VK_OEM_PLUS: return gbKey_Equals; - case VK_OEM_MINUS: return gbKey_Minus; - - case VK_SPACE: return gbKey_Space; - case VK_RETURN: return gbKey_Return; - case VK_BACK: return gbKey_Backspace; - case VK_TAB: return gbKey_Tab; - - case VK_PRIOR: return gbKey_Pageup; - case VK_NEXT: return gbKey_Pagedown; - case VK_END: return gbKey_End; - case VK_HOME: return gbKey_Home; - case VK_INSERT: return gbKey_Insert; - case VK_DELETE: return gbKey_Delete; - - case VK_ADD: return gbKey_Plus; - case VK_SUBTRACT: return gbKey_Subtract; - case VK_MULTIPLY: return gbKey_Multiply; - case VK_DIVIDE: return gbKey_Divide; - - case VK_LEFT: return gbKey_Left; - case VK_RIGHT: return gbKey_Right; - case VK_UP: return gbKey_Up; - case VK_DOWN: return gbKey_Down; - - case VK_NUMPAD0: return gbKey_Numpad0; - case VK_NUMPAD1: return gbKey_Numpad1; - case VK_NUMPAD2: return gbKey_Numpad2; - case VK_NUMPAD3: return gbKey_Numpad3; - case VK_NUMPAD4: return gbKey_Numpad4; - case VK_NUMPAD5: return gbKey_Numpad5; - case VK_NUMPAD6: return gbKey_Numpad6; - case VK_NUMPAD7: return gbKey_Numpad7; - case VK_NUMPAD8: return gbKey_Numpad8; - case VK_NUMPAD9: return gbKey_Numpad9; - case VK_SEPARATOR: return gbKey_NumpadEnter; - case VK_DECIMAL: return gbKey_NumpadDot; - - case VK_F1: return gbKey_F1; - case VK_F2: return gbKey_F2; - case VK_F3: return gbKey_F3; - case VK_F4: return gbKey_F4; - case VK_F5: return gbKey_F5; - case VK_F6: return gbKey_F6; - case VK_F7: return gbKey_F7; - case VK_F8: return gbKey_F8; - case VK_F9: return gbKey_F9; - case VK_F10: return gbKey_F10; - case VK_F11: return gbKey_F11; - case VK_F12: return gbKey_F12; - case VK_F13: return gbKey_F13; - case VK_F14: return gbKey_F14; - case VK_F15: return gbKey_F15; - - case VK_PAUSE: return gbKey_Pause; - } - return gbKey_Unknown; -} -LRESULT CALLBACK gb__win32_window_callback(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { - // NOTE(bill): Silly callbacks - gbPlatform *platform = cast(gbPlatform *)GetWindowLongPtrW(hWnd, GWLP_USERDATA); - b32 window_has_focus = (platform != NULL) && platform->window_has_focus; - - if (msg == WM_CREATE) { // NOTE(bill): Doesn't need the platform - // NOTE(bill): https://msdn.microsoft.com/en-us/library/windows/desktop/ms645536(v=vs.85).aspx - RAWINPUTDEVICE rid[2] = {0}; - - // NOTE(bill): Keyboard - rid[0].usUsagePage = 0x01; - rid[0].usUsage = 0x06; - rid[0].dwFlags = 0x00000030/*RIDEV_NOLEGACY*/; // NOTE(bill): Do not generate legacy messages such as WM_KEYDOWN - rid[0].hwndTarget = hWnd; - - // NOTE(bill): Mouse - rid[1].usUsagePage = 0x01; - rid[1].usUsage = 0x02; - rid[1].dwFlags = 0; // NOTE(bill): adds HID mouse and also allows legacy mouse messages to allow for window movement etc. - rid[1].hwndTarget = hWnd; - - if (RegisterRawInputDevices(rid, gb_count_of(rid), gb_size_of(rid[0])) == false) { - DWORD err = GetLastError(); - GB_PANIC("Failed to initialize raw input device for win32." - "Err: %u", err); - } - } - - if (!platform) { - return DefWindowProcW(hWnd, msg, wParam, lParam); - } - - switch (msg) { - case WM_CLOSE: - case WM_DESTROY: - platform->window_is_closed = true; - return 0; - - case WM_QUIT: { - platform->quit_requested = true; - } break; - - case WM_UNICHAR: { - if (window_has_focus) { - if (wParam == '\r') { - wParam = '\n'; - } - // TODO(bill): Does this need to be thread-safe? - platform->char_buffer[platform->char_buffer_count++] = cast(Rune)wParam; - } - } break; - - - case WM_INPUT: { - RAWINPUT raw = {0}; - unsigned int size = gb_size_of(RAWINPUT); - - if (!GetRawInputData(cast(HRAWINPUT)lParam, RID_INPUT, &raw, &size, gb_size_of(RAWINPUTHEADER))) { - return 0; - } - switch (raw.header.dwType) { - case RIM_TYPEKEYBOARD: { - // NOTE(bill): Many thanks to https://blog.molecular-matters.com/2011/09/05/properly-handling-keyboard-input/ - // for the - RAWKEYBOARD *raw_kb = &raw.data.keyboard; - unsigned int vk = raw_kb->VKey; - unsigned int scan_code = raw_kb->MakeCode; - unsigned int flags = raw_kb->Flags; - // NOTE(bill): e0 and e1 are escape sequences used for certain special keys, such as PRINT and PAUSE/BREAK. - // NOTE(bill): http://www.win.tue.nl/~aeb/linux/kbd/scancodes-1.html - b32 is_e0 = (flags & RI_KEY_E0) != 0; - b32 is_e1 = (flags & RI_KEY_E1) != 0; - b32 is_up = (flags & RI_KEY_BREAK) != 0; - b32 is_down = !is_up; - - // TODO(bill): Should I handle scan codes? - - if (vk == 255) { - // NOTE(bill): Discard "fake keys" - return 0; - } else if (vk == VK_SHIFT) { - // NOTE(bill): Correct left/right shift - vk = MapVirtualKeyW(scan_code, MAPVK_VSC_TO_VK_EX); - } else if (vk == VK_NUMLOCK) { - // NOTE(bill): Correct PAUSE/BREAK and NUM LOCK and set the extended bit - scan_code = MapVirtualKeyW(vk, MAPVK_VK_TO_VSC) | 0x100; - } - - if (is_e1) { - // NOTE(bill): Escaped sequences, turn vk into the correct scan code - // except for VK_PAUSE (it's a bug) - if (vk == VK_PAUSE) { - scan_code = 0x45; - } else { - scan_code = MapVirtualKeyW(vk, MAPVK_VK_TO_VSC); - } - } - - switch (vk) { - case VK_CONTROL: vk = (is_e0) ? VK_RCONTROL : VK_LCONTROL; break; - case VK_MENU: vk = (is_e0) ? VK_RMENU : VK_LMENU; break; - - case VK_RETURN: if (is_e0) vk = VK_SEPARATOR; break; // NOTE(bill): Numpad return - case VK_DELETE: if (!is_e0) vk = VK_DECIMAL; break; // NOTE(bill): Numpad dot - case VK_INSERT: if (!is_e0) vk = VK_NUMPAD0; break; - case VK_HOME: if (!is_e0) vk = VK_NUMPAD7; break; - case VK_END: if (!is_e0) vk = VK_NUMPAD1; break; - case VK_PRIOR: if (!is_e0) vk = VK_NUMPAD9; break; - case VK_NEXT: if (!is_e0) vk = VK_NUMPAD3; break; - - // NOTE(bill): The standard arrow keys will always have their e0 bit set, but the - // corresponding keys on the NUMPAD will not. - case VK_LEFT: if (!is_e0) vk = VK_NUMPAD4; break; - case VK_RIGHT: if (!is_e0) vk = VK_NUMPAD6; break; - case VK_UP: if (!is_e0) vk = VK_NUMPAD8; break; - case VK_DOWN: if (!is_e0) vk = VK_NUMPAD2; break; - - // NUMPAD 5 doesn't have its e0 bit set - case VK_CLEAR: if (!is_e0) vk = VK_NUMPAD5; break; - } - - // NOTE(bill): Set appropriate key state flags - gb_key_state_update(&platform->keys[gb__win32_from_vk(vk)], is_down); - - } break; - case RIM_TYPEMOUSE: { - RAWMOUSE *raw_mouse = &raw.data.mouse; - u16 flags = raw_mouse->usButtonFlags; - long dx = +raw_mouse->lLastX; - long dy = -raw_mouse->lLastY; - - if (flags & RI_MOUSE_WHEEL) { - platform->mouse_wheel_delta = cast(i16)raw_mouse->usButtonData; - } - - platform->mouse_raw_dx = dx; - platform->mouse_raw_dy = dy; - } break; - } - } break; - - default: break; - } - - return DefWindowProcW(hWnd, msg, wParam, lParam); -} - - -typedef void *wglCreateContextAttribsARB_Proc(void *hDC, void *hshareContext, int const *attribList); - - -b32 gb__platform_init(gbPlatform *p, char const *window_title, gbVideoMode mode, gbRendererType type, u32 window_flags) { - WNDCLASSEXW wc = {gb_size_of(WNDCLASSEXW)}; - DWORD ex_style = 0, style = 0; - RECT wr; - u16 title_buffer[256] = {0}; // TODO(bill): gb_local_persist this? - - wc.style = CS_HREDRAW | CS_VREDRAW; // | CS_OWNDC - wc.lpfnWndProc = gb__win32_window_callback; - wc.hbrBackground = cast(HBRUSH)GetStockObject(0/*WHITE_BRUSH*/); - wc.lpszMenuName = NULL; - wc.lpszClassName = L"gb-win32-wndclass"; // TODO(bill): Is this enough? - wc.hInstance = GetModuleHandleW(NULL); - - if (RegisterClassExW(&wc) == 0) { - MessageBoxW(NULL, L"Failed to register the window class", L"ERROR", MB_OK | MB_ICONEXCLAMATION); - return false; - } - - if ((window_flags & gbWindow_Fullscreen) && !(window_flags & gbWindow_Borderless)) { - DEVMODEW screen_settings = {gb_size_of(DEVMODEW)}; - screen_settings.dmPelsWidth = mode.width; - screen_settings.dmPelsHeight = mode.height; - screen_settings.dmBitsPerPel = mode.bits_per_pixel; - screen_settings.dmFields = DM_BITSPERPEL|DM_PELSWIDTH|DM_PELSHEIGHT; - - if (ChangeDisplaySettingsW(&screen_settings, CDS_FULLSCREEN) != DISP_CHANGE_SUCCESSFUL) { - if (MessageBoxW(NULL, L"The requested fullscreen mode is not supported by\n" - L"your video card. Use windowed mode instead?", - L"", - MB_YESNO|MB_ICONEXCLAMATION) == IDYES) { - window_flags &= ~gbWindow_Fullscreen; - } else { - mode = gb_video_mode_get_desktop(); - screen_settings.dmPelsWidth = mode.width; - screen_settings.dmPelsHeight = mode.height; - screen_settings.dmBitsPerPel = mode.bits_per_pixel; - ChangeDisplaySettingsW(&screen_settings, CDS_FULLSCREEN); - } - } - } - - - // ex_style = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE; - // style = WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_VISIBLE | WS_THICKFRAME | WS_SYSMENU | WS_MAXIMIZEBOX | WS_MINIMIZEBOX; - - style |= WS_VISIBLE; - - if (window_flags & gbWindow_Hidden) style &= ~WS_VISIBLE; - if (window_flags & gbWindow_Resizable) style |= WS_THICKFRAME | WS_MAXIMIZEBOX; - if (window_flags & gbWindow_Maximized) style |= WS_MAXIMIZE; - if (window_flags & gbWindow_Minimized) style |= WS_MINIMIZE; - - // NOTE(bill): Completely ignore the given mode and just change it - if (window_flags & gbWindow_FullscreenDesktop) { - mode = gb_video_mode_get_desktop(); - } - - if ((window_flags & gbWindow_Fullscreen) || (window_flags & gbWindow_Borderless)) { - style |= WS_POPUP; - } else { - style |= WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX; - } - - - wr.left = 0; - wr.top = 0; - wr.right = mode.width; - wr.bottom = mode.height; - AdjustWindowRect(&wr, style, false); - - p->window_flags = window_flags; - p->window_handle = CreateWindowExW(ex_style, - wc.lpszClassName, - cast(wchar_t const *)gb_utf8_to_ucs2(title_buffer, gb_size_of(title_buffer), window_title), - style, - CW_USEDEFAULT, CW_USEDEFAULT, - wr.right - wr.left, wr.bottom - wr.top, - 0, 0, - GetModuleHandleW(NULL), - NULL); - - if (!p->window_handle) { - MessageBoxW(NULL, L"Window creation failed", L"Error", MB_OK|MB_ICONEXCLAMATION); - return false; - } - - p->win32_dc = GetDC(cast(HWND)p->window_handle); - - p->renderer_type = type; - switch (p->renderer_type) { - case gbRenderer_Opengl: { - wglCreateContextAttribsARB_Proc *wglCreateContextAttribsARB; - i32 attribs[8] = {0}; - isize c = 0; - - PIXELFORMATDESCRIPTOR pfd = {gb_size_of(PIXELFORMATDESCRIPTOR)}; - pfd.nVersion = 1; - pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER; - pfd.iPixelType = PFD_TYPE_RGBA; - pfd.cColorBits = 32; - pfd.cAlphaBits = 8; - pfd.cDepthBits = 24; - pfd.cStencilBits = 8; - pfd.iLayerType = PFD_MAIN_PLANE; - - SetPixelFormat(cast(HDC)p->win32_dc, ChoosePixelFormat(cast(HDC)p->win32_dc, &pfd), NULL); - p->opengl.context = cast(void *)wglCreateContext(cast(HDC)p->win32_dc); - wglMakeCurrent(cast(HDC)p->win32_dc, cast(HGLRC)p->opengl.context); - - if (p->opengl.major > 0) { - attribs[c++] = 0x2091; // WGL_CONTEXT_MAJOR_VERSION_ARB - attribs[c++] = gb_max(p->opengl.major, 1); - } - if (p->opengl.major > 0 && p->opengl.minor >= 0) { - attribs[c++] = 0x2092; // WGL_CONTEXT_MINOR_VERSION_ARB - attribs[c++] = gb_max(p->opengl.minor, 0); - } - - if (p->opengl.core) { - attribs[c++] = 0x9126; // WGL_CONTEXT_PROFILE_MASK_ARB - attribs[c++] = 0x0001; // WGL_CONTEXT_CORE_PROFILE_BIT_ARB - } else if (p->opengl.compatible) { - attribs[c++] = 0x9126; // WGL_CONTEXT_PROFILE_MASK_ARB - attribs[c++] = 0x0002; // WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB - } - attribs[c++] = 0; // NOTE(bill): tells the proc that this is the end of attribs - - wglCreateContextAttribsARB = cast(wglCreateContextAttribsARB_Proc *)wglGetProcAddress("wglCreateContextAttribsARB"); - if (wglCreateContextAttribsARB) { - HGLRC rc = cast(HGLRC)wglCreateContextAttribsARB(p->win32_dc, 0, attribs); - if (rc && wglMakeCurrent(cast(HDC)p->win32_dc, rc)) { - p->opengl.context = rc; - } else { - // TODO(bill): Handle errors from GetLastError - // ERROR_INVALID_VERSION_ARB 0x2095 - // ERROR_INVALID_PROFILE_ARB 0x2096 - } - } - - } break; - - case gbRenderer_Software: - gb__platform_resize_dib_section(p, mode.width, mode.height); - break; - - default: - GB_PANIC("Unknown window type"); - break; - } - - SetForegroundWindow(cast(HWND)p->window_handle); - SetFocus(cast(HWND)p->window_handle); - SetWindowLongPtrW(cast(HWND)p->window_handle, GWLP_USERDATA, cast(LONG_PTR)p); - - p->window_width = mode.width; - p->window_height = mode.height; - - if (p->renderer_type == gbRenderer_Opengl) { - p->opengl.dll_handle = gb_dll_load("opengl32.dll"); - } - - { // Load XInput - // TODO(bill): What other dlls should I look for? - gbDllHandle xinput_library = gb_dll_load("xinput1_4.dll"); - p->xinput.get_state = gbXInputGetState_Stub; - p->xinput.set_state = gbXInputSetState_Stub; - - if (!xinput_library) xinput_library = gb_dll_load("xinput9_1_0.dll"); - if (!xinput_library) xinput_library = gb_dll_load("xinput1_3.dll"); - if (!xinput_library) { - // TODO(bill): Proper Diagnostic - gb_printf_err("XInput could not be loaded. Controllers will not work!\n"); - } else { - p->xinput.get_state = cast(gbXInputGetStateProc *)gb_dll_proc_address(xinput_library, "XInputGetState"); - p->xinput.set_state = cast(gbXInputSetStateProc *)gb_dll_proc_address(xinput_library, "XInputSetState"); - } - } - - // Init keys - gb_zero_array(p->keys, gb_count_of(p->keys)); - - p->is_initialized = true; - return true; -} - -gb_inline b32 gb_platform_init_with_software(gbPlatform *p, char const *window_title, - i32 width, i32 height, u32 window_flags) { - gbVideoMode mode; - mode.width = width; - mode.height = height; - mode.bits_per_pixel = 32; - return gb__platform_init(p, window_title, mode, gbRenderer_Software, window_flags); -} - -gb_inline b32 gb_platform_init_with_opengl(gbPlatform *p, char const *window_title, - i32 width, i32 height, u32 window_flags, i32 major, i32 minor, b32 core, b32 compatible) { - gbVideoMode mode; - mode.width = width; - mode.height = height; - mode.bits_per_pixel = 32; - p->opengl.major = major; - p->opengl.minor = minor; - p->opengl.core = cast(b16)core; - p->opengl.compatible = cast(b16)compatible; - return gb__platform_init(p, window_title, mode, gbRenderer_Opengl, window_flags); -} - -#ifndef _XINPUT_H_ -typedef struct _XINPUT_GAMEPAD { - u16 wButtons; - u8 bLeftTrigger; - u8 bRightTrigger; - u16 sThumbLX; - u16 sThumbLY; - u16 sThumbRX; - u16 sThumbRY; -} XINPUT_GAMEPAD; - -typedef struct _XINPUT_STATE { - DWORD dwPacketNumber; - XINPUT_GAMEPAD Gamepad; -} XINPUT_STATE; - -typedef struct _XINPUT_VIBRATION { - u16 wLeftMotorSpeed; - u16 wRightMotorSpeed; -} XINPUT_VIBRATION; - -#define XINPUT_GAMEPAD_DPAD_UP 0x00000001 -#define XINPUT_GAMEPAD_DPAD_DOWN 0x00000002 -#define XINPUT_GAMEPAD_DPAD_LEFT 0x00000004 -#define XINPUT_GAMEPAD_DPAD_RIGHT 0x00000008 -#define XINPUT_GAMEPAD_START 0x00000010 -#define XINPUT_GAMEPAD_BACK 0x00000020 -#define XINPUT_GAMEPAD_LEFT_THUMB 0x00000040 -#define XINPUT_GAMEPAD_RIGHT_THUMB 0x00000080 -#define XINPUT_GAMEPAD_LEFT_SHOULDER 0x0100 -#define XINPUT_GAMEPAD_RIGHT_SHOULDER 0x0200 -#define XINPUT_GAMEPAD_A 0x1000 -#define XINPUT_GAMEPAD_B 0x2000 -#define XINPUT_GAMEPAD_X 0x4000 -#define XINPUT_GAMEPAD_Y 0x8000 -#define XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE 7849 -#define XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE 8689 -#define XINPUT_GAMEPAD_TRIGGER_THRESHOLD 30 -#endif - -#ifndef XUSER_MAX_COUNT -#define XUSER_MAX_COUNT 4 -#endif - -void gb_platform_update(gbPlatform *p) { - isize i; - - { // NOTE(bill): Set window state - // TODO(bill): Should this be moved to gb__win32_window_callback ? - RECT window_rect; - i32 x, y, w, h; - - GetClientRect(cast(HWND)p->window_handle, &window_rect); - x = window_rect.left; - y = window_rect.top; - w = window_rect.right - window_rect.left; - h = window_rect.bottom - window_rect.top; - - if ((p->window_width != w) || (p->window_height != h)) { - if (p->renderer_type == gbRenderer_Software) { - gb__platform_resize_dib_section(p, w, h); - } - } - - - p->window_x = x; - p->window_y = y; - p->window_width = w; - p->window_height = h; - GB_MASK_SET(p->window_flags, IsIconic(cast(HWND)p->window_handle) != 0, gbWindow_Minimized); - - p->window_has_focus = GetFocus() == cast(HWND)p->window_handle; - } - - { // NOTE(bill): Set mouse position - POINT mouse_pos; - DWORD win_button_id[gbMouseButton_Count] = { - VK_LBUTTON, - VK_MBUTTON, - VK_RBUTTON, - VK_XBUTTON1, - VK_XBUTTON2, - }; - - // NOTE(bill): This needs to be GetAsyncKeyState as RAWMOUSE doesn't aways work for some odd reason - // TODO(bill): Try and get RAWMOUSE to work for key presses - for (i = 0; i < gbMouseButton_Count; i++) { - gb_key_state_update(p->mouse_buttons+i, GetAsyncKeyState(win_button_id[i]) < 0); - } - - GetCursorPos(&mouse_pos); - ScreenToClient(cast(HWND)p->window_handle, &mouse_pos); - { - i32 x = mouse_pos.x; - i32 y = p->window_height-1 - mouse_pos.y; - p->mouse_dx = x - p->mouse_x; - p->mouse_dy = y - p->mouse_y; - p->mouse_x = x; - p->mouse_y = y; - } - - if (p->mouse_clip) { - b32 update = false; - i32 x = p->mouse_x; - i32 y = p->mouse_y; - if (p->mouse_x < 0) { - x = 0; - update = true; - } else if (p->mouse_y > p->window_height-1) { - y = p->window_height-1; - update = true; - } - - if (p->mouse_y < 0) { - y = 0; - update = true; - } else if (p->mouse_x > p->window_width-1) { - x = p->window_width-1; - update = true; - } - - if (update) { - gb_platform_set_mouse_position(p, x, y); - } - } - - - } - - - // NOTE(bill): Set Key/Button states - if (p->window_has_focus) { - p->char_buffer_count = 0; // TODO(bill): Reset buffer count here or else where? - - // NOTE(bill): Need to update as the keys only get updates on events - for (i = 0; i < gbKey_Count; i++) { - b32 is_down = (p->keys[i] & gbKeyState_Down) != 0; - gb_key_state_update(&p->keys[i], is_down); - } - - p->key_modifiers.control = p->keys[gbKey_Lcontrol] | p->keys[gbKey_Rcontrol]; - p->key_modifiers.alt = p->keys[gbKey_Lalt] | p->keys[gbKey_Ralt]; - p->key_modifiers.shift = p->keys[gbKey_Lshift] | p->keys[gbKey_Rshift]; - - } - - { // NOTE(bill): Set Controller states - isize max_controller_count = XUSER_MAX_COUNT; - if (max_controller_count > gb_count_of(p->game_controllers)) { - max_controller_count = gb_count_of(p->game_controllers); - } - - for (i = 0; i < max_controller_count; i++) { - gbGameController *controller = &p->game_controllers[i]; - XINPUT_STATE controller_state = {0}; - if (p->xinput.get_state(cast(DWORD)i, &controller_state) != 0) { - // NOTE(bill): The controller is not available - controller->is_connected = false; - } else { - // NOTE(bill): This controller is plugged in - // TODO(bill): See if ControllerState.dwPacketNumber increments too rapidly - XINPUT_GAMEPAD *pad = &controller_state.Gamepad; - - controller->is_connected = true; - - // TODO(bill): This is a square deadzone, check XInput to verify that the deadzone is "round" and do round deadzone processing. - controller->axes[gbControllerAxis_LeftX] = gb__process_xinput_stick_value(pad->sThumbLX, XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE); - controller->axes[gbControllerAxis_LeftY] = gb__process_xinput_stick_value(pad->sThumbLY, XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE); - controller->axes[gbControllerAxis_RightX] = gb__process_xinput_stick_value(pad->sThumbRX, XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE); - controller->axes[gbControllerAxis_RightY] = gb__process_xinput_stick_value(pad->sThumbRY, XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE); - - controller->axes[gbControllerAxis_LeftTrigger] = cast(f32)pad->bLeftTrigger / 255.0f; - controller->axes[gbControllerAxis_RightTrigger] = cast(f32)pad->bRightTrigger / 255.0f; - - - if ((controller->axes[gbControllerAxis_LeftX] != 0.0f) || - (controller->axes[gbControllerAxis_LeftY] != 0.0f)) { - controller->is_analog = true; - } - - #define GB__PROCESS_DIGITAL_BUTTON(button_type, xinput_button) \ - gb_key_state_update(&controller->buttons[button_type], (pad->wButtons & xinput_button) == xinput_button) - - GB__PROCESS_DIGITAL_BUTTON(gbControllerButton_A, XINPUT_GAMEPAD_A); - GB__PROCESS_DIGITAL_BUTTON(gbControllerButton_B, XINPUT_GAMEPAD_B); - GB__PROCESS_DIGITAL_BUTTON(gbControllerButton_X, XINPUT_GAMEPAD_X); - GB__PROCESS_DIGITAL_BUTTON(gbControllerButton_Y, XINPUT_GAMEPAD_Y); - GB__PROCESS_DIGITAL_BUTTON(gbControllerButton_LeftShoulder, XINPUT_GAMEPAD_LEFT_SHOULDER); - GB__PROCESS_DIGITAL_BUTTON(gbControllerButton_RightShoulder, XINPUT_GAMEPAD_RIGHT_SHOULDER); - GB__PROCESS_DIGITAL_BUTTON(gbControllerButton_Start, XINPUT_GAMEPAD_START); - GB__PROCESS_DIGITAL_BUTTON(gbControllerButton_Back, XINPUT_GAMEPAD_BACK); - GB__PROCESS_DIGITAL_BUTTON(gbControllerButton_Left, XINPUT_GAMEPAD_DPAD_LEFT); - GB__PROCESS_DIGITAL_BUTTON(gbControllerButton_Right, XINPUT_GAMEPAD_DPAD_RIGHT); - GB__PROCESS_DIGITAL_BUTTON(gbControllerButton_Down, XINPUT_GAMEPAD_DPAD_DOWN); - GB__PROCESS_DIGITAL_BUTTON(gbControllerButton_Up, XINPUT_GAMEPAD_DPAD_UP); - GB__PROCESS_DIGITAL_BUTTON(gbControllerButton_LeftThumb, XINPUT_GAMEPAD_LEFT_THUMB); - GB__PROCESS_DIGITAL_BUTTON(gbControllerButton_RightThumb, XINPUT_GAMEPAD_RIGHT_THUMB); - #undef GB__PROCESS_DIGITAL_BUTTON - } - } - } - - { // NOTE(bill): Process pending messages - MSG message; - for (;;) { - BOOL is_okay = PeekMessageW(&message, 0, 0, 0, PM_REMOVE); - if (!is_okay) break; - - switch (message.message) { - case WM_QUIT: - p->quit_requested = true; - break; - - default: - TranslateMessage(&message); - DispatchMessageW(&message); - break; - } - } - } -} - -void gb_platform_display(gbPlatform *p) { - if (p->renderer_type == gbRenderer_Opengl) { - SwapBuffers(cast(HDC)p->win32_dc); - } else if (p->renderer_type == gbRenderer_Software) { - StretchDIBits(cast(HDC)p->win32_dc, - 0, 0, p->window_width, p->window_height, - 0, 0, p->window_width, p->window_height, - p->sw_framebuffer.memory, - &p->sw_framebuffer.win32_bmi, - DIB_RGB_COLORS, SRCCOPY); - } else { - GB_PANIC("Invalid window rendering type"); - } - - { - f64 prev_time = p->curr_time; - f64 curr_time = gb_time_now(); - p->dt_for_frame = curr_time - prev_time; - p->curr_time = curr_time; - } -} - - -void gb_platform_destroy(gbPlatform *p) { - if (p->renderer_type == gbRenderer_Opengl) { - wglDeleteContext(cast(HGLRC)p->opengl.context); - } else if (p->renderer_type == gbRenderer_Software) { - gb_vm_free(gb_virtual_memory(p->sw_framebuffer.memory, p->sw_framebuffer.memory_size)); - } - - DestroyWindow(cast(HWND)p->window_handle); -} - -void gb_platform_show_cursor(gbPlatform *p, b32 show) { - gb_unused(p); - ShowCursor(show); -} - -void gb_platform_set_mouse_position(gbPlatform *p, i32 x, i32 y) { - POINT point; - point.x = cast(LONG)x; - point.y = cast(LONG)(p->window_height-1 - y); - ClientToScreen(cast(HWND)p->window_handle, &point); - SetCursorPos(point.x, point.y); - - p->mouse_x = point.x; - p->mouse_y = p->window_height-1 - point.y; -} - - - -void gb_platform_set_controller_vibration(gbPlatform *p, isize index, f32 left_motor, f32 right_motor) { - if (gb_is_between(index, 0, GB_MAX_GAME_CONTROLLER_COUNT-1)) { - XINPUT_VIBRATION vibration = {0}; - left_motor = gb_clamp01(left_motor); - right_motor = gb_clamp01(right_motor); - vibration.wLeftMotorSpeed = cast(WORD)(65535 * left_motor); - vibration.wRightMotorSpeed = cast(WORD)(65535 * right_motor); - - p->xinput.set_state(cast(DWORD)index, &vibration); - } -} - - -void gb_platform_set_window_position(gbPlatform *p, i32 x, i32 y) { - RECT rect; - i32 width, height; - - GetClientRect(cast(HWND)p->window_handle, &rect); - width = rect.right - rect.left; - height = rect.bottom - rect.top; - MoveWindow(cast(HWND)p->window_handle, x, y, width, height, false); -} - -void gb_platform_set_window_title(gbPlatform *p, char const *title, ...) { - u16 buffer[256] = {0}; - char str[512] = {0}; - va_list va; - va_start(va, title); - gb_snprintf_va(str, gb_size_of(str), title, va); - va_end(va); - - if (str[0] != '\0') { - SetWindowTextW(cast(HWND)p->window_handle, cast(wchar_t const *)gb_utf8_to_ucs2(buffer, gb_size_of(buffer), str)); - } -} - -void gb_platform_toggle_fullscreen(gbPlatform *p, b32 fullscreen_desktop) { - // NOTE(bill): From the man himself, Raymond Chen! (Modified for my need.) - HWND handle = cast(HWND)p->window_handle; - DWORD style = cast(DWORD)GetWindowLongW(handle, GWL_STYLE); - WINDOWPLACEMENT placement; - - if (style & WS_OVERLAPPEDWINDOW) { - MONITORINFO monitor_info = {gb_size_of(monitor_info)}; - if (GetWindowPlacement(handle, &placement) && - GetMonitorInfoW(MonitorFromWindow(handle, 1), &monitor_info)) { - style &= ~WS_OVERLAPPEDWINDOW; - if (fullscreen_desktop) { - style &= ~WS_CAPTION; - style |= WS_POPUP; - } - SetWindowLongW(handle, GWL_STYLE, style); - SetWindowPos(handle, HWND_TOP, - monitor_info.rcMonitor.left, monitor_info.rcMonitor.top, - monitor_info.rcMonitor.right - monitor_info.rcMonitor.left, - monitor_info.rcMonitor.bottom - monitor_info.rcMonitor.top, - SWP_NOOWNERZORDER | SWP_FRAMECHANGED); - - if (fullscreen_desktop) { - p->window_flags |= gbWindow_FullscreenDesktop; - } else { - p->window_flags |= gbWindow_Fullscreen; - } - } - } else { - style &= ~WS_POPUP; - style |= WS_OVERLAPPEDWINDOW | WS_CAPTION; - SetWindowLongW(handle, GWL_STYLE, style); - SetWindowPlacement(handle, &placement); - SetWindowPos(handle, 0, 0, 0, 0, 0, - SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | - SWP_NOOWNERZORDER | SWP_FRAMECHANGED); - - p->window_flags &= ~gbWindow_Fullscreen; - } -} - -void gb_platform_toggle_borderless(gbPlatform *p) { - HWND handle = cast(HWND)p->window_handle; - DWORD style = GetWindowLongW(handle, GWL_STYLE); - b32 is_borderless = (style & WS_POPUP) != 0; - - GB_MASK_SET(style, is_borderless, WS_OVERLAPPEDWINDOW | WS_CAPTION); - GB_MASK_SET(style, !is_borderless, WS_POPUP); - - SetWindowLongW(handle, GWL_STYLE, style); - - GB_MASK_SET(p->window_flags, !is_borderless, gbWindow_Borderless); -} - - - -gb_inline void gb_platform_make_opengl_context_current(gbPlatform *p) { - if (p->renderer_type == gbRenderer_Opengl) { - wglMakeCurrent(cast(HDC)p->win32_dc, cast(HGLRC)p->opengl.context); - } -} - -gb_inline void gb_platform_show_window(gbPlatform *p) { - ShowWindow(cast(HWND)p->window_handle, SW_SHOW); - p->window_flags &= ~gbWindow_Hidden; -} - -gb_inline void gb_platform_hide_window(gbPlatform *p) { - ShowWindow(cast(HWND)p->window_handle, SW_HIDE); - p->window_flags |= gbWindow_Hidden; -} - -gb_inline gbVideoMode gb_video_mode_get_desktop(void) { - DEVMODEW win32_mode = {gb_size_of(win32_mode)}; - EnumDisplaySettingsW(NULL, ENUM_CURRENT_SETTINGS, &win32_mode); - return gb_video_mode(win32_mode.dmPelsWidth, win32_mode.dmPelsHeight, win32_mode.dmBitsPerPel); -} - -isize gb_video_mode_get_fullscreen_modes(gbVideoMode *modes, isize max_mode_count) { - DEVMODEW win32_mode = {gb_size_of(win32_mode)}; - i32 count; - for (count = 0; - count < max_mode_count && EnumDisplaySettingsW(NULL, count, &win32_mode); - count++) { - modes[count] = gb_video_mode(win32_mode.dmPelsWidth, win32_mode.dmPelsHeight, win32_mode.dmBitsPerPel); - } - - gb_sort_array(modes, count, gb_video_mode_dsc_cmp); - return count; -} - - - -b32 gb_platform_has_clipboard_text(gbPlatform *p) { - b32 result = false; - - if (IsClipboardFormatAvailable(1/*CF_TEXT*/) && - OpenClipboard(cast(HWND)p->window_handle)) { - HANDLE mem = GetClipboardData(1/*CF_TEXT*/); - if (mem) { - char *str = cast(char *)GlobalLock(mem); - if (str && str[0] != '\0') { - result = true; - } - GlobalUnlock(mem); - } else { - return false; - } - - CloseClipboard(); - } - - return result; -} - -// TODO(bill): Handle UTF-8 -void gb_platform_set_clipboard_text(gbPlatform *p, char const *str) { - if (OpenClipboard(cast(HWND)p->window_handle)) { - isize i, len = gb_strlen(str)+1; - - HANDLE mem = cast(HANDLE)GlobalAlloc(0x0002/*GMEM_MOVEABLE*/, len); - if (mem) { - char *dst = cast(char *)GlobalLock(mem); - if (dst) { - for (i = 0; str[i]; i++) { - // TODO(bill): Does this cause a buffer overflow? - // NOTE(bill): Change \n to \r\n 'cause windows - if (str[i] == '\n' && (i == 0 || str[i-1] != '\r')) { - *dst++ = '\r'; - } - *dst++ = str[i]; - } - *dst = 0; - } - GlobalUnlock(mem); - } - - EmptyClipboard(); - if (!SetClipboardData(1/*CF_TEXT*/, mem)) { - return; - } - CloseClipboard(); - } -} - -// TODO(bill): Handle UTF-8 -char *gb_platform_get_clipboard_text(gbPlatform *p, gbAllocator a) { - char *text = NULL; - - if (IsClipboardFormatAvailable(1/*CF_TEXT*/) && - OpenClipboard(cast(HWND)p->window_handle)) { - HANDLE mem = GetClipboardData(1/*CF_TEXT*/); - if (mem) { - char *str = cast(char *)GlobalLock(mem); - text = gb_alloc_str(a, str); - GlobalUnlock(mem); - } else { - return NULL; - } - - CloseClipboard(); - } - - return text; -} - -#elif defined(GB_SYSTEM_OSX) - -#include -#include -#include -#include - -#if __LP64__ || (TARGET_OS_EMBEDDED && !TARGET_OS_IPHONE) || TARGET_OS_WIN32 || NS_BUILD_32_LIKE_64 - #define NSIntegerEncoding "q" - #define NSUIntegerEncoding "L" -#else - #define NSIntegerEncoding "i" - #define NSUIntegerEncoding "I" -#endif - -#ifdef __OBJC__ - #import -#else - typedef CGPoint NSPoint; - typedef CGSize NSSize; - typedef CGRect NSRect; - - extern id NSApp; - extern id const NSDefaultRunLoopMode; -#endif - -#if defined(__OBJC__) && __has_feature(objc_arc) -#error TODO(bill): Cannot compile as objective-c code just yet! -#endif - -// ABI is a bit different between platforms -#ifdef __arm64__ -#define abi_objc_msgSend_stret objc_msgSend -#else -#define abi_objc_msgSend_stret objc_msgSend_stret -#endif -#ifdef __i386__ -#define abi_objc_msgSend_fpret objc_msgSend_fpret -#else -#define abi_objc_msgSend_fpret objc_msgSend -#endif - -#define objc_msgSend_id ((id (*)(id, SEL))objc_msgSend) -#define objc_msgSend_void ((void (*)(id, SEL))objc_msgSend) -#define objc_msgSend_void_id ((void (*)(id, SEL, id))objc_msgSend) -#define objc_msgSend_void_bool ((void (*)(id, SEL, BOOL))objc_msgSend) -#define objc_msgSend_id_char_const ((id (*)(id, SEL, char const *))objc_msgSend) - -gb_internal NSUInteger gb__osx_application_should_terminate(id self, SEL _sel, id sender) { - // NOTE(bill): Do nothing - return 0; -} - -gb_internal void gb__osx_window_will_close(id self, SEL _sel, id notification) { - NSUInteger value = true; - object_setInstanceVariable(self, "closed", cast(void *)value); -} - -gb_internal void gb__osx_window_did_become_key(id self, SEL _sel, id notification) { - gbPlatform *p = NULL; - object_getInstanceVariable(self, "gbPlatform", cast(void **)&p); - if (p) { - // TODO(bill): - } -} - -b32 gb__platform_init(gbPlatform *p, char const *window_title, gbVideoMode mode, gbRendererType type, u32 window_flags) { - if (p->is_initialized) { - return true; - } - // Init Platform - { // Initial OSX State - Class appDelegateClass; - b32 resultAddProtoc, resultAddMethod; - id dgAlloc, dg, menubarAlloc, menubar; - id appMenuItemAlloc, appMenuItem; - id appMenuAlloc, appMenu; - - #if defined(ARC_AVAILABLE) - #error TODO(bill): This code should be compiled as C for now - #else - id poolAlloc = objc_msgSend_id(cast(id)objc_getClass("NSAutoreleasePool"), sel_registerName("alloc")); - p->osx_autorelease_pool = objc_msgSend_id(poolAlloc, sel_registerName("init")); - #endif - - objc_msgSend_id(cast(id)objc_getClass("NSApplication"), sel_registerName("sharedApplication")); - ((void (*)(id, SEL, NSInteger))objc_msgSend)(NSApp, sel_registerName("setActivationPolicy:"), 0); - - appDelegateClass = objc_allocateClassPair((Class)objc_getClass("NSObject"), "AppDelegate", 0); - resultAddProtoc = class_addProtocol(appDelegateClass, objc_getProtocol("NSApplicationDelegate")); - assert(resultAddProtoc); - resultAddMethod = class_addMethod(appDelegateClass, sel_registerName("applicationShouldTerminate:"), cast(IMP)gb__osx_application_should_terminate, NSUIntegerEncoding "@:@"); - assert(resultAddMethod); - dgAlloc = objc_msgSend_id(cast(id)appDelegateClass, sel_registerName("alloc")); - dg = objc_msgSend_id(dgAlloc, sel_registerName("init")); - #ifndef ARC_AVAILABLE - objc_msgSend_void(dg, sel_registerName("autorelease")); - #endif - - objc_msgSend_void_id(NSApp, sel_registerName("setDelegate:"), dg); - objc_msgSend_void(NSApp, sel_registerName("finishLaunching")); - - menubarAlloc = objc_msgSend_id(cast(id)objc_getClass("NSMenu"), sel_registerName("alloc")); - menubar = objc_msgSend_id(menubarAlloc, sel_registerName("init")); - #ifndef ARC_AVAILABLE - objc_msgSend_void(menubar, sel_registerName("autorelease")); - #endif - - appMenuItemAlloc = objc_msgSend_id(cast(id)objc_getClass("NSMenuItem"), sel_registerName("alloc")); - appMenuItem = objc_msgSend_id(appMenuItemAlloc, sel_registerName("init")); - #ifndef ARC_AVAILABLE - objc_msgSend_void(appMenuItem, sel_registerName("autorelease")); - #endif - - objc_msgSend_void_id(menubar, sel_registerName("addItem:"), appMenuItem); - ((id (*)(id, SEL, id))objc_msgSend)(NSApp, sel_registerName("setMainMenu:"), menubar); - - appMenuAlloc = objc_msgSend_id(cast(id)objc_getClass("NSMenu"), sel_registerName("alloc")); - appMenu = objc_msgSend_id(appMenuAlloc, sel_registerName("init")); - #ifndef ARC_AVAILABLE - objc_msgSend_void(appMenu, sel_registerName("autorelease")); - #endif - - { - id processInfo = objc_msgSend_id(cast(id)objc_getClass("NSProcessInfo"), sel_registerName("processInfo")); - id appName = objc_msgSend_id(processInfo, sel_registerName("processName")); - - id quitTitlePrefixString = objc_msgSend_id_char_const(cast(id)objc_getClass("NSString"), sel_registerName("stringWithUTF8String:"), "Quit "); - id quitTitle = ((id (*)(id, SEL, id))objc_msgSend)(quitTitlePrefixString, sel_registerName("stringByAppendingString:"), appName); - - id quitMenuItemKey = objc_msgSend_id_char_const(cast(id)objc_getClass("NSString"), sel_registerName("stringWithUTF8String:"), "q"); - id quitMenuItemAlloc = objc_msgSend_id(cast(id)objc_getClass("NSMenuItem"), sel_registerName("alloc")); - id quitMenuItem = ((id (*)(id, SEL, id, SEL, id))objc_msgSend)(quitMenuItemAlloc, sel_registerName("initWithTitle:action:keyEquivalent:"), quitTitle, sel_registerName("terminate:"), quitMenuItemKey); - #ifndef ARC_AVAILABLE - objc_msgSend_void(quitMenuItem, sel_registerName("autorelease")); - #endif - - objc_msgSend_void_id(appMenu, sel_registerName("addItem:"), quitMenuItem); - objc_msgSend_void_id(appMenuItem, sel_registerName("setSubmenu:"), appMenu); - } - } - - { // Init Window - NSRect rect = {{0, 0}, {cast(CGFloat)mode.width, cast(CGFloat)mode.height}}; - id windowAlloc, window, wdgAlloc, wdg, contentView, titleString; - Class WindowDelegateClass; - b32 resultAddProtoc, resultAddIvar, resultAddMethod; - - windowAlloc = objc_msgSend_id(cast(id)objc_getClass("NSWindow"), sel_registerName("alloc")); - window = ((id (*)(id, SEL, NSRect, NSUInteger, NSUInteger, BOOL))objc_msgSend)(windowAlloc, sel_registerName("initWithContentRect:styleMask:backing:defer:"), rect, 15, 2, NO); - #ifndef ARC_AVAILABLE - objc_msgSend_void(window, sel_registerName("autorelease")); - #endif - - // when we are not using ARC, than window will be added to autorelease pool - // so if we close it by hand (pressing red button), we don't want it to be released for us - // so it will be released by autorelease pool later - objc_msgSend_void_bool(window, sel_registerName("setReleasedWhenClosed:"), NO); - - WindowDelegateClass = objc_allocateClassPair((Class)objc_getClass("NSObject"), "WindowDelegate", 0); - resultAddProtoc = class_addProtocol(WindowDelegateClass, objc_getProtocol("NSWindowDelegate")); - GB_ASSERT(resultAddProtoc); - resultAddIvar = class_addIvar(WindowDelegateClass, "closed", gb_size_of(NSUInteger), rint(log2(gb_size_of(NSUInteger))), NSUIntegerEncoding); - GB_ASSERT(resultAddIvar); - resultAddIvar = class_addIvar(WindowDelegateClass, "gbPlatform", gb_size_of(void *), rint(log2(gb_size_of(void *))), "ˆv"); - GB_ASSERT(resultAddIvar); - resultAddMethod = class_addMethod(WindowDelegateClass, sel_registerName("windowWillClose:"), cast(IMP)gb__osx_window_will_close, "v@:@"); - GB_ASSERT(resultAddMethod); - resultAddMethod = class_addMethod(WindowDelegateClass, sel_registerName("windowDidBecomeKey:"), cast(IMP)gb__osx_window_did_become_key, "v@:@"); - GB_ASSERT(resultAddMethod); - wdgAlloc = objc_msgSend_id(cast(id)WindowDelegateClass, sel_registerName("alloc")); - wdg = objc_msgSend_id(wdgAlloc, sel_registerName("init")); - #ifndef ARC_AVAILABLE - objc_msgSend_void(wdg, sel_registerName("autorelease")); - #endif - - objc_msgSend_void_id(window, sel_registerName("setDelegate:"), wdg); - - contentView = objc_msgSend_id(window, sel_registerName("contentView")); - - { - NSPoint point = {20, 20}; - ((void (*)(id, SEL, NSPoint))objc_msgSend)(window, sel_registerName("cascadeTopLeftFromPoint:"), point); - } - - titleString = objc_msgSend_id_char_const(cast(id)objc_getClass("NSString"), sel_registerName("stringWithUTF8String:"), window_title); - objc_msgSend_void_id(window, sel_registerName("setTitle:"), titleString); - - if (type == gbRenderer_Opengl) { - // TODO(bill): Make sure this works correctly - u32 opengl_hex_version = (p->opengl.major << 12) | (p->opengl.minor << 8); - u32 gl_attribs[] = { - 8, 24, // NSOpenGLPFAColorSize, 24, - 11, 8, // NSOpenGLPFAAlphaSize, 8, - 5, // NSOpenGLPFADoubleBuffer, - 73, // NSOpenGLPFAAccelerated, - //72, // NSOpenGLPFANoRecovery, - //55, 1, // NSOpenGLPFASampleBuffers, 1, - //56, 4, // NSOpenGLPFASamples, 4, - 99, opengl_hex_version, // NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion3_2Core, - 0 - }; - - id pixel_format_alloc, pixel_format; - id opengl_context_alloc, opengl_context; - - pixel_format_alloc = objc_msgSend_id(cast(id)objc_getClass("NSOpenGLPixelFormat"), sel_registerName("alloc")); - pixel_format = ((id (*)(id, SEL, const uint32_t*))objc_msgSend)(pixel_format_alloc, sel_registerName("initWithAttributes:"), gl_attribs); - #ifndef ARC_AVAILABLE - objc_msgSend_void(pixel_format, sel_registerName("autorelease")); - #endif - - opengl_context_alloc = objc_msgSend_id(cast(id)objc_getClass("NSOpenGLContext"), sel_registerName("alloc")); - opengl_context = ((id (*)(id, SEL, id, id))objc_msgSend)(opengl_context_alloc, sel_registerName("initWithFormat:shareContext:"), pixel_format, nil); - #ifndef ARC_AVAILABLE - objc_msgSend_void(opengl_context, sel_registerName("autorelease")); - #endif - - objc_msgSend_void_id(opengl_context, sel_registerName("setView:"), contentView); - objc_msgSend_void_id(window, sel_registerName("makeKeyAndOrderFront:"), window); - objc_msgSend_void_bool(window, sel_registerName("setAcceptsMouseMovedEvents:"), YES); - - - p->window_handle = cast(void *)window; - p->opengl.context = cast(void *)opengl_context; - } else { - GB_PANIC("TODO(bill): Software rendering"); - } - - { - id blackColor = objc_msgSend_id(cast(id)objc_getClass("NSColor"), sel_registerName("blackColor")); - objc_msgSend_void_id(window, sel_registerName("setBackgroundColor:"), blackColor); - objc_msgSend_void_bool(NSApp, sel_registerName("activateIgnoringOtherApps:"), YES); - } - object_setInstanceVariable(wdg, "gbPlatform", cast(void *)p); - - p->is_initialized = true; - } - - return true; -} - -// NOTE(bill): Software rendering -b32 gb_platform_init_with_software(gbPlatform *p, char const *window_title, i32 width, i32 height, u32 window_flags) { - GB_PANIC("TODO(bill): Software rendering in not yet implemented on OS X\n"); - return gb__platform_init(p, window_title, gb_video_mode(width, height, 32), gbRenderer_Software, window_flags); -} -// NOTE(bill): OpenGL Rendering -b32 gb_platform_init_with_opengl(gbPlatform *p, char const *window_title, i32 width, i32 height, u32 window_flags, - i32 major, i32 minor, b32 core, b32 compatible) { - - p->opengl.major = major; - p->opengl.minor = minor; - p->opengl.core = core; - p->opengl.compatible = compatible; - return gb__platform_init(p, window_title, gb_video_mode(width, height, 32), gbRenderer_Opengl, window_flags); -} - -// NOTE(bill): Reverse engineering can be fun!!! -gb_internal gbKeyType gb__osx_from_key_code(u16 key_code) { - switch (key_code) { - default: return gbKey_Unknown; - // NOTE(bill): WHO THE FUCK DESIGNED THIS VIRTUAL KEY CODE SYSTEM?! - // THEY ARE FUCKING IDIOTS! - case 0x1d: return gbKey_0; - case 0x12: return gbKey_1; - case 0x13: return gbKey_2; - case 0x14: return gbKey_3; - case 0x15: return gbKey_4; - case 0x17: return gbKey_5; - case 0x16: return gbKey_6; - case 0x1a: return gbKey_7; - case 0x1c: return gbKey_8; - case 0x19: return gbKey_9; - - case 0x00: return gbKey_A; - case 0x0b: return gbKey_B; - case 0x08: return gbKey_C; - case 0x02: return gbKey_D; - case 0x0e: return gbKey_E; - case 0x03: return gbKey_F; - case 0x05: return gbKey_G; - case 0x04: return gbKey_H; - case 0x22: return gbKey_I; - case 0x26: return gbKey_J; - case 0x28: return gbKey_K; - case 0x25: return gbKey_L; - case 0x2e: return gbKey_M; - case 0x2d: return gbKey_N; - case 0x1f: return gbKey_O; - case 0x23: return gbKey_P; - case 0x0c: return gbKey_Q; - case 0x0f: return gbKey_R; - case 0x01: return gbKey_S; - case 0x11: return gbKey_T; - case 0x20: return gbKey_U; - case 0x09: return gbKey_V; - case 0x0d: return gbKey_W; - case 0x07: return gbKey_X; - case 0x10: return gbKey_Y; - case 0x06: return gbKey_Z; - - case 0x21: return gbKey_Lbracket; - case 0x1e: return gbKey_Rbracket; - case 0x29: return gbKey_Semicolon; - case 0x2b: return gbKey_Comma; - case 0x2f: return gbKey_Period; - case 0x27: return gbKey_Quote; - case 0x2c: return gbKey_Slash; - case 0x2a: return gbKey_Backslash; - case 0x32: return gbKey_Grave; - case 0x18: return gbKey_Equals; - case 0x1b: return gbKey_Minus; - case 0x31: return gbKey_Space; - - case 0x35: return gbKey_Escape; // Escape - case 0x3b: return gbKey_Lcontrol; // Left Control - case 0x38: return gbKey_Lshift; // Left Shift - case 0x3a: return gbKey_Lalt; // Left Alt - case 0x37: return gbKey_Lsystem; // Left OS specific: window (Windows and Linux), apple/cmd (MacOS X), ... - case 0x3e: return gbKey_Rcontrol; // Right Control - case 0x3c: return gbKey_Rshift; // Right Shift - case 0x3d: return gbKey_Ralt; // Right Alt - // case 0x37: return gbKey_Rsystem; // Right OS specific: window (Windows and Linux), apple/cmd (MacOS X), ... - case 0x6e: return gbKey_Menu; // Menu - case 0x24: return gbKey_Return; // Return - case 0x33: return gbKey_Backspace; // Backspace - case 0x30: return gbKey_Tab; // Tabulation - case 0x74: return gbKey_Pageup; // Page up - case 0x79: return gbKey_Pagedown; // Page down - case 0x77: return gbKey_End; // End - case 0x73: return gbKey_Home; // Home - case 0x72: return gbKey_Insert; // Insert - case 0x75: return gbKey_Delete; // Delete - case 0x45: return gbKey_Plus; // + - case 0x4e: return gbKey_Subtract; // - - case 0x43: return gbKey_Multiply; // * - case 0x4b: return gbKey_Divide; // / - case 0x7b: return gbKey_Left; // Left arrow - case 0x7c: return gbKey_Right; // Right arrow - case 0x7e: return gbKey_Up; // Up arrow - case 0x7d: return gbKey_Down; // Down arrow - case 0x52: return gbKey_Numpad0; // Numpad 0 - case 0x53: return gbKey_Numpad1; // Numpad 1 - case 0x54: return gbKey_Numpad2; // Numpad 2 - case 0x55: return gbKey_Numpad3; // Numpad 3 - case 0x56: return gbKey_Numpad4; // Numpad 4 - case 0x57: return gbKey_Numpad5; // Numpad 5 - case 0x58: return gbKey_Numpad6; // Numpad 6 - case 0x59: return gbKey_Numpad7; // Numpad 7 - case 0x5b: return gbKey_Numpad8; // Numpad 8 - case 0x5c: return gbKey_Numpad9; // Numpad 9 - case 0x41: return gbKey_NumpadDot; // Numpad . - case 0x4c: return gbKey_NumpadEnter; // Numpad Enter - case 0x7a: return gbKey_F1; // F1 - case 0x78: return gbKey_F2; // F2 - case 0x63: return gbKey_F3; // F3 - case 0x76: return gbKey_F4; // F4 - case 0x60: return gbKey_F5; // F5 - case 0x61: return gbKey_F6; // F6 - case 0x62: return gbKey_F7; // F7 - case 0x64: return gbKey_F8; // F8 - case 0x65: return gbKey_F9; // F8 - case 0x6d: return gbKey_F10; // F10 - case 0x67: return gbKey_F11; // F11 - case 0x6f: return gbKey_F12; // F12 - case 0x69: return gbKey_F13; // F13 - case 0x6b: return gbKey_F14; // F14 - case 0x71: return gbKey_F15; // F15 - // case : return gbKey_Pause; // Pause // NOTE(bill): Not possible on OS X - } -} - -gb_internal void gb__osx_on_cocoa_event(gbPlatform *p, id event, id window) { - if (!event) { - return; - } else if (objc_msgSend_id(window, sel_registerName("delegate"))) { - NSUInteger event_type = ((NSUInteger (*)(id, SEL))objc_msgSend)(event, sel_registerName("type")); - switch (event_type) { - case 1: gb_key_state_update(&p->mouse_buttons[gbMouseButton_Left], true); break; // NSLeftMouseDown - case 2: gb_key_state_update(&p->mouse_buttons[gbMouseButton_Left], false); break; // NSLeftMouseUp - case 3: gb_key_state_update(&p->mouse_buttons[gbMouseButton_Right], true); break; // NSRightMouseDown - case 4: gb_key_state_update(&p->mouse_buttons[gbMouseButton_Right], false); break; // NSRightMouseUp - case 25: { // NSOtherMouseDown - // TODO(bill): Test thoroughly - NSInteger number = ((NSInteger (*)(id, SEL))objc_msgSend)(event, sel_registerName("buttonNumber")); - if (number == 2) gb_key_state_update(&p->mouse_buttons[gbMouseButton_Middle], true); - if (number == 3) gb_key_state_update(&p->mouse_buttons[gbMouseButton_X1], true); - if (number == 4) gb_key_state_update(&p->mouse_buttons[gbMouseButton_X2], true); - } break; - case 26: { // NSOtherMouseUp - NSInteger number = ((NSInteger (*)(id, SEL))objc_msgSend)(event, sel_registerName("buttonNumber")); - if (number == 2) gb_key_state_update(&p->mouse_buttons[gbMouseButton_Middle], false); - if (number == 3) gb_key_state_update(&p->mouse_buttons[gbMouseButton_X1], false); - if (number == 4) gb_key_state_update(&p->mouse_buttons[gbMouseButton_X2], false); - - } break; - - // TODO(bill): Scroll wheel - case 22: { // NSScrollWheel - CGFloat dx = ((CGFloat (*)(id, SEL))abi_objc_msgSend_fpret)(event, sel_registerName("scrollingDeltaX")); - CGFloat dy = ((CGFloat (*)(id, SEL))abi_objc_msgSend_fpret)(event, sel_registerName("scrollingDeltaY")); - BOOL precision_scrolling = ((BOOL (*)(id, SEL))objc_msgSend)(event, sel_registerName("hasPreciseScrollingDeltas")); - if (precision_scrolling) { - dx *= 0.1f; - dy *= 0.1f; - } - // TODO(bill): Handle sideways - p->mouse_wheel_delta = dy; - // p->mouse_wheel_dy = dy; - // gb_printf("%f %f\n", dx, dy); - } break; - - case 12: { // NSFlagsChanged - #if 0 - // TODO(bill): Reverse engineer this properly - NSUInteger modifiers = ((NSUInteger (*)(id, SEL))objc_msgSend)(event, sel_registerName("modifierFlags")); - u32 upper_mask = (modifiers & 0xffff0000ul) >> 16; - b32 shift = (upper_mask & 0x02) != 0; - b32 control = (upper_mask & 0x04) != 0; - b32 alt = (upper_mask & 0x08) != 0; - b32 command = (upper_mask & 0x10) != 0; - #endif - - // gb_printf("%u\n", keys.mask); - // gb_printf("%x\n", cast(u32)modifiers); - } break; - - case 10: { // NSKeyDown - u16 key_code; - - id input_text = objc_msgSend_id(event, sel_registerName("characters")); - char const *input_text_utf8 = ((char const *(*)(id, SEL))objc_msgSend)(input_text, sel_registerName("UTF8String")); - p->char_buffer_count = gb_strnlen(input_text_utf8, gb_size_of(p->char_buffer)); - gb_memcopy(p->char_buffer, input_text_utf8, p->char_buffer_count); - - key_code = ((unsigned short (*)(id, SEL))objc_msgSend)(event, sel_registerName("keyCode")); - gb_key_state_update(&p->keys[gb__osx_from_key_code(key_code)], true); - } break; - - case 11: { // NSKeyUp - u16 key_code = ((unsigned short (*)(id, SEL))objc_msgSend)(event, sel_registerName("keyCode")); - gb_key_state_update(&p->keys[gb__osx_from_key_code(key_code)], false); - } break; - - default: break; - } - - objc_msgSend_void_id(NSApp, sel_registerName("sendEvent:"), event); - } -} - - -void gb_platform_update(gbPlatform *p) { - id window, key_window, content_view; - NSRect original_frame; - - window = cast(id)p->window_handle; - key_window = objc_msgSend_id(NSApp, sel_registerName("keyWindow")); - p->window_has_focus = key_window == window; // TODO(bill): Is this right - - - if (p->window_has_focus) { - isize i; - p->char_buffer_count = 0; // TODO(bill): Reset buffer count here or else where? - - // NOTE(bill): Need to update as the keys only get updates on events - for (i = 0; i < gbKey_Count; i++) { - b32 is_down = (p->keys[i] & gbKeyState_Down) != 0; - gb_key_state_update(&p->keys[i], is_down); - } - - for (i = 0; i < gbMouseButton_Count; i++) { - b32 is_down = (p->mouse_buttons[i] & gbKeyState_Down) != 0; - gb_key_state_update(&p->mouse_buttons[i], is_down); - } - - } - - { // Handle Events - id distant_past = objc_msgSend_id(cast(id)objc_getClass("NSDate"), sel_registerName("distantPast")); - id event = ((id (*)(id, SEL, NSUInteger, id, id, BOOL))objc_msgSend)(NSApp, sel_registerName("nextEventMatchingMask:untilDate:inMode:dequeue:"), NSUIntegerMax, distant_past, NSDefaultRunLoopMode, YES); - gb__osx_on_cocoa_event(p, event, window); - } - - if (p->window_has_focus) { - p->key_modifiers.control = p->keys[gbKey_Lcontrol] | p->keys[gbKey_Rcontrol]; - p->key_modifiers.alt = p->keys[gbKey_Lalt] | p->keys[gbKey_Ralt]; - p->key_modifiers.shift = p->keys[gbKey_Lshift] | p->keys[gbKey_Rshift]; - } - - { // Check if window is closed - id wdg = objc_msgSend_id(window, sel_registerName("delegate")); - if (!wdg) { - p->window_is_closed = false; - } else { - NSUInteger value = 0; - object_getInstanceVariable(wdg, "closed", cast(void **)&value); - p->window_is_closed = (value != 0); - } - } - - - - content_view = objc_msgSend_id(window, sel_registerName("contentView")); - original_frame = ((NSRect (*)(id, SEL))abi_objc_msgSend_stret)(content_view, sel_registerName("frame")); - - { // Window - NSRect frame = original_frame; - frame = ((NSRect (*)(id, SEL, NSRect))abi_objc_msgSend_stret)(content_view, sel_registerName("convertRectToBacking:"), frame); - p->window_width = frame.size.width; - p->window_height = frame.size.height; - frame = ((NSRect (*)(id, SEL, NSRect))abi_objc_msgSend_stret)(window, sel_registerName("convertRectToScreen:"), frame); - p->window_x = frame.origin.x; - p->window_y = frame.origin.y; - } - - { // Mouse - NSRect frame = original_frame; - NSPoint mouse_pos = ((NSPoint (*)(id, SEL))objc_msgSend)(window, sel_registerName("mouseLocationOutsideOfEventStream")); - mouse_pos.x = gb_clamp(mouse_pos.x, 0, frame.size.width-1); - mouse_pos.y = gb_clamp(mouse_pos.y, 0, frame.size.height-1); - - { - i32 x = mouse_pos.x; - i32 y = mouse_pos.y; - p->mouse_dx = x - p->mouse_x; - p->mouse_dy = y - p->mouse_y; - p->mouse_x = x; - p->mouse_y = y; - } - - if (p->mouse_clip) { - b32 update = false; - i32 x = p->mouse_x; - i32 y = p->mouse_y; - if (p->mouse_x < 0) { - x = 0; - update = true; - } else if (p->mouse_y > p->window_height-1) { - y = p->window_height-1; - update = true; - } - - if (p->mouse_y < 0) { - y = 0; - update = true; - } else if (p->mouse_x > p->window_width-1) { - x = p->window_width-1; - update = true; - } - - if (update) { - gb_platform_set_mouse_position(p, x, y); - } - } - } - - { // TODO(bill): Controllers - - } - - // TODO(bill): Is this in the correct place? - objc_msgSend_void(NSApp, sel_registerName("updateWindows")); - if (p->renderer_type == gbRenderer_Opengl) { - objc_msgSend_void(cast(id)p->opengl.context, sel_registerName("update")); - gb_platform_make_opengl_context_current(p); - } -} - -void gb_platform_display(gbPlatform *p) { - // TODO(bill): Do more - if (p->renderer_type == gbRenderer_Opengl) { - gb_platform_make_opengl_context_current(p); - objc_msgSend_void(cast(id)p->opengl.context, sel_registerName("flushBuffer")); - } else if (p->renderer_type == gbRenderer_Software) { - // TODO(bill): - } else { - GB_PANIC("Invalid window rendering type"); - } - - { - f64 prev_time = p->curr_time; - f64 curr_time = gb_time_now(); - p->dt_for_frame = curr_time - prev_time; - p->curr_time = curr_time; - } -} - -void gb_platform_destroy(gbPlatform *p) { - gb_platform_make_opengl_context_current(p); - - objc_msgSend_void(cast(id)p->window_handle, sel_registerName("close")); - - #if defined(ARC_AVAILABLE) - // TODO(bill): autorelease pool - #else - objc_msgSend_void(cast(id)p->osx_autorelease_pool, sel_registerName("drain")); - #endif -} - -void gb_platform_show_cursor(gbPlatform *p, b32 show) { - if (show ) { - // objc_msgSend_void(class_registerName("NSCursor"), sel_registerName("unhide")); - } else { - // objc_msgSend_void(class_registerName("NSCursor"), sel_registerName("hide")); - } -} - -void gb_platform_set_mouse_position(gbPlatform *p, i32 x, i32 y) { - // TODO(bill): - CGPoint pos = {cast(CGFloat)x, cast(CGFloat)y}; - pos.x += p->window_x; - pos.y += p->window_y; - CGWarpMouseCursorPosition(pos); -} - -void gb_platform_set_controller_vibration(gbPlatform *p, isize index, f32 left_motor, f32 right_motor) { - // TODO(bill): -} - -b32 gb_platform_has_clipboard_text(gbPlatform *p) { - // TODO(bill): - return false; -} - -void gb_platform_set_clipboard_text(gbPlatform *p, char const *str) { - // TODO(bill): -} - -char *gb_platform_get_clipboard_text(gbPlatform *p, gbAllocator a) { - // TODO(bill): - return NULL; -} - -void gb_platform_set_window_position(gbPlatform *p, i32 x, i32 y) { - // TODO(bill): -} - -void gb_platform_set_window_title(gbPlatform *p, char const *title, ...) { - id title_string; - char buf[256] = {0}; - va_list va; - va_start(va, title); - gb_snprintf_va(buf, gb_count_of(buf), title, va); - va_end(va); - - title_string = objc_msgSend_id_char_const(cast(id)objc_getClass("NSString"), sel_registerName("stringWithUTF8String:"), buf); - objc_msgSend_void_id(cast(id)p->window_handle, sel_registerName("setTitle:"), title_string); -} - -void gb_platform_toggle_fullscreen(gbPlatform *p, b32 fullscreen_desktop) { - // TODO(bill): -} - -void gb_platform_toggle_borderless(gbPlatform *p) { - // TODO(bill): -} - -void gb_platform_make_opengl_context_current(gbPlatform *p) { - objc_msgSend_void(cast(id)p->opengl.context, sel_registerName("makeCurrentContext")); -} - -void gb_platform_show_window(gbPlatform *p) { - // TODO(bill): -} - -void gb_platform_hide_window(gbPlatform *p) { - // TODO(bill): -} - -i32 gb__osx_mode_bits_per_pixel(CGDisplayModeRef mode) { - i32 bits_per_pixel = 0; - CFStringRef pixel_encoding = CGDisplayModeCopyPixelEncoding(mode); - if(CFStringCompare(pixel_encoding, CFSTR(IO32BitDirectPixels), kCFCompareCaseInsensitive) == kCFCompareEqualTo) { - bits_per_pixel = 32; - } else if(CFStringCompare(pixel_encoding, CFSTR(IO16BitDirectPixels), kCFCompareCaseInsensitive) == kCFCompareEqualTo) { - bits_per_pixel = 16; - } else if(CFStringCompare(pixel_encoding, CFSTR(IO8BitIndexedPixels), kCFCompareCaseInsensitive) == kCFCompareEqualTo) { - bits_per_pixel = 8; - } - CFRelease(pixel_encoding); - - return bits_per_pixel; -} - -i32 gb__osx_display_bits_per_pixel(CGDirectDisplayID display) { - CGDisplayModeRef mode = CGDisplayCopyDisplayMode(display); - i32 bits_per_pixel = gb__osx_mode_bits_per_pixel(mode); - CGDisplayModeRelease(mode); - return bits_per_pixel; -} - -gbVideoMode gb_video_mode_get_desktop(void) { - CGDirectDisplayID display = CGMainDisplayID(); - return gb_video_mode(CGDisplayPixelsWide(display), - CGDisplayPixelsHigh(display), - gb__osx_display_bits_per_pixel(display)); -} - - -isize gb_video_mode_get_fullscreen_modes(gbVideoMode *modes, isize max_mode_count) { - CFArrayRef cg_modes = CGDisplayCopyAllDisplayModes(CGMainDisplayID(), NULL); - CFIndex i, count; - if (cg_modes == NULL) { - return 0; - } - - count = gb_min(CFArrayGetCount(cg_modes), max_mode_count); - for (i = 0; i < count; i++) { - CGDisplayModeRef cg_mode = cast(CGDisplayModeRef)CFArrayGetValueAtIndex(cg_modes, i); - modes[i] = gb_video_mode(CGDisplayModeGetWidth(cg_mode), - CGDisplayModeGetHeight(cg_mode), - gb__osx_mode_bits_per_pixel(cg_mode)); - } - - CFRelease(cg_modes); - - gb_sort_array(modes, count, gb_video_mode_dsc_cmp); - return cast(isize)count; -} - -#endif - - -// TODO(bill): OSX Platform Layer -// NOTE(bill): Use this as a guide so there is no need for Obj-C https://github.com/jimon/osx_app_in_plain_c - -gb_inline gbVideoMode gb_video_mode(i32 width, i32 height, i32 bits_per_pixel) { - gbVideoMode m; - m.width = width; - m.height = height; - m.bits_per_pixel = bits_per_pixel; - return m; -} - -gb_inline b32 gb_video_mode_is_valid(gbVideoMode mode) { - gb_local_persist gbVideoMode modes[256] = {0}; - gb_local_persist isize mode_count = 0; - gb_local_persist b32 is_set = false; - isize i; - - if (!is_set) { - mode_count = gb_video_mode_get_fullscreen_modes(modes, gb_count_of(modes)); - is_set = true; - } - - for (i = 0; i < mode_count; i++) { - gb_printf("%d %d\n", modes[i].width, modes[i].height); - } - - return gb_binary_search_array(modes, mode_count, &mode, gb_video_mode_cmp) >= 0; -} - -GB_COMPARE_PROC(gb_video_mode_cmp) { - gbVideoMode const *x = cast(gbVideoMode const *)a; - gbVideoMode const *y = cast(gbVideoMode const *)b; - - if (x->bits_per_pixel == y->bits_per_pixel) { - if (x->width == y->width) { - return x->height < y->height ? -1 : x->height > y->height; - } - return x->width < y->width ? -1 : x->width > y->width; - } - return x->bits_per_pixel < y->bits_per_pixel ? -1 : +1; -} - -GB_COMPARE_PROC(gb_video_mode_dsc_cmp) { - return gb_video_mode_cmp(b, a); -} - -#endif // defined(GB_PLATFORM) - - - - -#if defined(GB_COMPILER_MSVC) -#pragma warning(pop) -#endif - -#if defined(__GCC__) || defined(__GNUC__) -#pragma GCC diagnostic pop -#endif - - -#if defined(__cplusplus) -} -#endif - -#endif // GB_IMPLEMENTATION diff --git a/thirdparty/stb/src/stb_truetype.h b/thirdparty/stb/src/stb_truetype.h index 2ca3ff9..6c82c29 100644 --- a/thirdparty/stb/src/stb_truetype.h +++ b/thirdparty/stb/src/stb_truetype.h @@ -415,9 +415,9 @@ int main(int arg, char **argv) #pragma region ODIN: CUSTOM ALLOCATOR #ifdef STB_TRUETYPE_IMPLEMENTATION -#define GB_IMPLEMENTATION +#define ZPL_IMPLEMENTATION #endif -#include "gb/gb.h" +#include "zpl/zpl.h" #ifdef STBTT_STATIC #define STBTT_DEF static @@ -429,21 +429,21 @@ int main(int arg, char **argv) extern "C" { #endif -STBTT_DEF void stbtt_SetAllocator( gbAllocator allocator ); +STBTT_DEF void stbtt_SetAllocator( zpl_allocator allocator ); #ifdef __cplusplus } #endif #ifndef STBTT_malloc -#define STBTT_malloc(x,u) ((void)(u), gb_alloc(stbtt__allocator, x)) -#define STBTT_free(x,u) ((void)(u), gb_free(stbtt__allocator, x)) +#define STBTT_malloc(x,u) ((void)(u), zpl_alloc(stbtt__allocator, x)) +#define STBTT_free(x,u) ((void)(u), zpl_free(stbtt__allocator, x)) #endif #ifdef STB_TRUETYPE_IMPLEMENTATION -gb_global gbAllocator stbtt__allocator = { gb_heap_allocator_proc, NULL }; +zpl_global zpl_allocator stbtt__allocator = { zpl_heap_allocator_proc, NULL }; -STBTT_DEF void stbtt_SetAllocator( gbAllocator allocator ) { +STBTT_DEF void stbtt_SetAllocator( zpl_allocator allocator ) { stbtt__allocator = allocator; } #endif diff --git a/thirdparty/stb/src/zpl/zpl.h b/thirdparty/stb/src/zpl/zpl.h new file mode 100644 index 0000000..d90720a --- /dev/null +++ b/thirdparty/stb/src/zpl/zpl.h @@ -0,0 +1,19055 @@ +/** + zpl - Pushing the boundaries of simplicity. + +Usage: +# define ZPL_IMPLEMENTATION exactly in ONE source file right BEFORE including the library, like: + +# define ZPL_IMPLEMENTATION +# include "zpl.h" + + You can also use a lightweight version of zpl by using ZPL_NANO, like: + +# define ZPL_IMPLEMENTATION +# define ZPL_NANO +# include "zpl.h" + + There is also a distribution that provides only the essential modules, you can enable it by defining ZPL_PICO. + Currently, the distro offers: preprocessor helpers, debug module, memory API (except vm) and collections. + Some of these modules used to depend on zpl_printf, but they use the standard library if the distro is enabled now. + +# define ZPL_IMPLEMENTATION +# define ZPL_PICO +# include "zpl.h" + +Options: + ZPL_EXPOSE_TYPES - exposes all zpl defined types to the global namespace. This means type such as `zpl_u32` is now available as `u32` globally. + ZPL_DEFINE_NULL_MACRO - to let zpl define what NULL stands for in case it is undefined. + ZPL_NO_MATH_H - disables the use of math.h library and replaces it with custom routines or SIMD. + ZPL_HEAP_ANALYSIS - enables heap allocator analysis tools + ZPL_PARSER_DISABLE_ANALYSIS - disables the extra parsing logic that would collect more information about node's formatting and structure. + this is useful in scenarios where a raw parsing performance is preferred over a more complex analysis. + It is not recommended to serialise data back since we lack the extra information about the original source document. + +GitHub: + https://github.com/zpl-c/zpl + +Version History: + 19.5.0 - math: implemented hypot(x) function (Arin-Grigoras) + 19.4.1 - file: zpl_file_read_contents now ignores folders. + 19.4.0 - json: introduce support for ZPL_JSON_INDENT_STYLE_COMPACT output (rheatley-pervasid) + - fix SJSON value parse not detecting EOF correctly when analysing delimiter used. + example: "foo=123,bar=456" would produce 1 extra garbage field. + 19.3.1 - adt: zpl_adt_parse_number exit early on false hex value assumption + 19.3.0 - socket: new socket api cross-platform layer + 19.2.0 - file: add macros for standard i/o wrappers (ZPL_STDIO_IN, ...) + 19.1.1 - thread: return error values for POSIX calls + 19.1.0 - parser: introduce a new URI parser module + 19.0.4 - fix: zpl_buffer_copy_init missing macros (thanks Ed_ on Discord) + 19.0.3 - fix: zpl_str_skip_literal reading before start of string (mbenniston) + 19.0.2 - fixed ZPL_ISIZE_MIN and MAX for 32-bit architectures (mrossetti) + 19.0.1 - Fixed zpl_array_fill ZPL_ASSERT off-by-one error + 19.0.0 - Check all results of zpl_alloc() when using JSON parser/writer (rheatley-pervasid) + + 18.1.5 - set parent to parsed JSON nodes (fixed) + - fix zpl_json/csv_write_string off-by-one issue + 18.1.4 - fix zpl_random_gen_isize/zpl_random_range_isize 32bit overflow + 18.1.3 - set parent to parsed JSON nodes + 18.1.2 - fix zpl sort procs + 18.1.1 - updated table _clear method + 18.1.0 - added table _clear method + 18.0.4 - fix memory arena alignment & added tests + 18.0.3 - fix emscripten support + 18.0.2 - fix global-buffer-overflow in print module + - raise ZPL_PRINTF_MAXLEN to 64kb + 18.0.1 - fix ADT parser wrongly assuming that an IP address is a real number + 18.0.0 - removed coroutines module + - removed timer module + - rename zpl_adt_get -> zpl_adt_query + + 17.0.0 - ADT API changes + zpl_adt_inset_* -> zpl_adt_append_* + zpl_adt_node now holds a parent field, methods no longer require a pointer to the parent + methods are now documented + - add zpl_thread_init_nowait (gaopeng) + + 16.1.1 - fix scientific notation parsing + 16.1.0 - introduce ZPL_PARSER_DISABLE_ANALYSIS that disables extra parsing capabilities to offer better raw performance + at a cost of lack of node metadata. + 16.0.0 - introduce a new zpl_adt_query method for flexible data retrieval + "a/b/c" navigates through objects "a" and "b" to get to "c" + "arr/[foo=123]/bar" iterates over "arr" to find any object with param "foo" that matches the value "123", then gets its field called "bar" + "arr/3" retrieves the 4th element in "arr" + "arr/[apple]" retrieves the first element of value "apple" in "arr" + - fix memory leak when parsing a json array (gaopeng) + - add zpl_strntok (gaopeng) + - add zpl_semaphore_trywait (gaopeng) + + 15.0.3 - fix zpl_sign call in math failing to compile + on macos devices + 15.0.2 - zpl_sign0 was introduced + 15.0.1 - hashtable performance improvements + - zpl_sign(0) returns 0 + 15.0.0 - Rework zpl ring buffer + - various code improvements + + 14.1.7 - fix zpl_random_range_i64 + - set thread's is_running before we start a thread + 14.1.6 - remove windows.h dependency for header part + 14.1.5 - fix array append_at + 14.1.4 - Fix win32 missing CRITICAL_SECTION definition if + - ZPL_NO_WINDOWS_H is defined + 14.1.0 - add hashtable map_mut method + 14.0.1 - fix zpl_array_remove_at boundary bug + 14.0.0 - heap memory allocator analysis + + 13.4.1 - adt optimizations + 13.4.0 - new adt manipulation methods + 13.3.3 - fix zpl_str_skip_literal bug + 13.3.2 - escape strings in parser output + 13.3.1 - number parsing improvements + 13.3.0 - csv parse numbers + 13.2.0 - hashtable _map function + 13.1.5 - ZPL_DEBUG_TRAP for tcc + 13.1.4 - potential csv ub fix + 13.1.3 - tcc support improvements + 13.1.2 - fix ast -> adt filename + 13.1.1 - fix emscripten support + 13.1.0 - abstract data tree naming update + 13.0.0 - text parsers refactor + + 12.8.0 - zpl_opts improvements + 12.7.0 - math improvements + 12.6.2 - remove register usage (BeastLe9enD) + 12.6.1 - improve tar null padding code + 12.6.0 - introduce zpl_align_forward_u64/i64 + 12.5.1 - small type casting fixes + 12.5.0 - add zpl_asprintf + 12.4.0 - zpl_printf improvements + 12.3.2 - allow zpl_path_dirlist to include symlinks, but don't enter them + 12.3.1 - avoid symbolic link cycle in zpl_path_dirlist + 12.3.0 - add TAR archiving support + 12.2.1 - fix zpl_random_gen_f64 + 12.2.0 - Add zpl_array_fill and zpl_array_appendv_at + 12.1.0 - Add rectangle partitioning + 12.0.1 - Optimize zpl_strlen + 12.0.0 - JSON API revamp + improvements + + 11.3.0 - JSON zpl_json_str_to_flt + cleanup + 11.2.5 - fix small atomics typo + 11.2.4 - JSON rewrite core parser + 11.2.2 - JSON rewrite comment handling + 11.2.1 - JSON zero-initialise node + 11.2.0 - JSON API improvements + 11.1.2 - Improve atomics + 11.1.1 - Fix zpl_json_write_string providing incorrect length + 11.1.0 - Introduce new ZPL_PICO distro + 11.0.11 - remove stdatomic.h include + 11.0.10 - get rid of c11 atomics lib + 11.0.9 - stringlib uses ZPL_PRINTF_MAXLEN now + - zpl_printf family is now thread-safe + 11.0.7 - Add ZPL_PRINTF_MAXLEN + 11.0.6 - Fix zpl_printf left padding bug + 11.0.4 - Disable ZPL_NO_MATH_H on TinyC + 11.0.3 - Added support for TinyC compiler + 11.0.2 - Fixes for Apple M1 chip + 11.0.0 - New jobs system + - Rewrite the timer module + - zpl_ring rework + + 10.13.0 - Initial ARM threading support + 10.12.1 - Fix missing zpL_alloc_str + 10.12.0 - Add zpl_crc64 + 10.11.1 - Fix zpl_time_utc_ms on 32-bit OSes + 10.11.0 - Added zpl_file_stream_buf + 10.10.3 - Math type-punning fixes + 10.10.1 - Fix memory writing issue + new write-only in-situ flag + 10.10.0 - Implement memory streaming API + 10.9.1 - Support ARMv6, ARMv7 and ARMv8-a builds + 10.9.0 - Improve the time API + 10.8.3 - zpl_file_close tempfile Windows fixes + 10.8.2 - zpl_file_temp disallow some operations + 10.8.1 - zpl_file_temp Windows fixes + 10.8.0 - Implemented zpl_json_write_string + 10.7.1 - Fix zpl_file_temp platform bug + 10.7.0 - Add zpl_file_write_contents + 10.6.6 - Fix type mismatch in Jobs system + 10.6.0 - Remove event system + 10.5.8 - Remove zpl__memcpy_4byte + 10.5.7 - zpl_file_new is now OS-agnostic constructor + 10.5.6 - Fix coroutine creation + 10.5.5 - Jobs system uses zpl_f32 for priority setting + 10.5.4 - zpl_buffer_free no longer takes the 2nd argument (allocator) + 10.5.3 - Removed crc64 and annotated some hashing methods + 10.5.2 - Don't expose ZPL types anymore + 10.5.1 - Fixed zpl_rdtsc for Emscripten + 10.5.0 - Changed casts to memcopy in random methods, added embed cmd + 10.4.1 - Jobs system now enqueues jobs with def priority of 1.0 + 10.4.0 - [META] version bump + 10.3.0 - Pool allocator now supports zpl_free_all + 10.2.0 - [META] version bump + 10.1.0 - Additional math methods (thanks to funZX and msmshazan) + 10.0.15 - WIP Emscripten fixes + 10.0.14 - FreeBSD support + 10.0.13 - OpenBSD support + 10.0.12 - Cygwin fixes + 10.0.11 - Tweak module dependencies + 10.0.10 - Fix zero-allocation regression in filesystem module + 10.0.9 - Fix multi-compilation unit builds + 10.0.8 - Fix zpl_printf "%0d" format specifier + 10.0.4 - Flush tester output to fix ordering + 10.0.3 - Fix ZPL_STATIC_ASSERT under MSVC + 10.0.0 - Major overhaul of the library + + 9.8.10 - JSON fix array-based documents with objects + 9.8.9 - JSON document structured as array now properly recognizes the root object as array. + 9.8.8 - Fixed an incorrect parsing of empty array nodes. + 9.8.7 - Improve FreeBSD support + 9.8.6 - WIP: Handle inlined methods properly + 9.8.5 - Fix incorrect usage of EOF and opts dependency on JSON5 module's methods + 9.8.4 - Fix MSVC ZPL_NO_MATH_H code branch using incorrect methods internally + 9.8.3 - Fix MinGW GCC related issue with zpl_printf %lld format + 9.8.2 - Fix VS C4190 issue + 9.8.1 - Fix several C++ type casting quirks + 9.8.0 - Incorporated OpenGL into ZPL core as an optional module + 9.7.0 - Added co-routine module + 9.6.0 - Added process module for creation and manipulation + 9.5.2 - zpl_printf family now prints (null) on NULL string arguments + 9.5.1 - Fixed JSON5 real number export support + indentation fixes + 9.5.0 - Added base64 encode/decode methods + 9.4.10- Small enum style changes + 9.4.9 - Remove #undef for cast and hard_cast (sorry) + 9.4.8 - Fix quote-less JSON node name resolution + 9.4.7 - Additional change to the code + 9.4.6 - Fix issue where zpl_json_find would have false match on substrings + 9.4.5 - Mistakes were made, fixed compilation errors + 9.4.3 - Fix old API shenanigans + 9.4.2 - Fix small API typos + 9.4.1 - Reordered JSON5 constants to integrate better with conditions + 9.4.0 - JSON5 API changes made to zpl_json_find + 9.3.0 - Change how zpl uses basic types internally + 9.2.0 - Directory listing was added. Check dirlist_api.c test for more info + 9.1.1 - Fix WIN32_LEAN_AND_MEAN redefinition properly + 9.1.0 - get_env rework and fixes + 9.0.3 - Small fixes and removals + 9.0.0 - New documentation format, removed deprecated code, changed styles + + 8.14.1 - Fix string library + 8.14.0 - Added zpl_re_match_all + 8.13.0 - Update system command API + 8.12.6 - Fix warning in CLI options parser + 8.12.5 - Support parametric options preceding positionals + 8.12.4 - Fixed opts positionals ordering + 8.12.3 - Fixed incorrect handling of flags preceding positionals + 8.12.2 - JSON parsing remark added + 8.12.1 - Fixed a lot of important stuff + 8.12.0 - Added helper constructors for containers + 8.11.2 - Fix bug in opts module + 8.11.1 - Small code improvements + 8.11.0 - Ported regex processor from https://github.com/gingerBill/gb/ and applied fixes on top of it + 8.10.2 - Fix zpl_strtok + 8.10.1 - Replace zpl_strchr by zpl_char_last_occurence + 8.10.0 - Added zpl_strchr + 8.9.0 - API improvements for JSON5 parser + 8.8.4 - Add support for SJSON formatting http://bitsquid.blogspot.com/2009/10/simplified-json-notation.html + + 6.8.3 - JSON5 exp fix + 6.8.2 - Bugfixes applied from gb + 6.8.1 - Performance improvements for JSON5 parser + 6.8.0 - zpl.h is now generated by build.py + 6.7.0 - Several fixes and added switches + 6.6.0 - Several significant changes made to the repository + 6.5.0 - Ported platform layer from https://github.com/gingerBill/gb/ + 6.4.1 - Use zpl_strlen in zpl_strdup + 6.4.0 - Deprecated zpl_buffer_free and added zpl_array_end, zpl_buffer_end + 6.3.0 - Added zpl_strdup + 6.2.1 - Remove math redundancies + 6.2.0 - Integrated zpl_math.h into zpl.h + 6.1.1 - Added direct.h include for win c++ dir methods + 6.1.0 - Added zpl_path_mkdir, zpl_path_rmdir, and few new zplFileErrors + 6.0.4 - More MSVC(++) satisfaction by fixing warnings + 6.0.3 - Satisfy MSVC by fixing a warning + 6.0.2 - Fixed warnings for json5 i64 printfs + 6.0.1 - Fixed warnings for particual win compiler in dirlist method + 6.0.0 - New build, include/ was renamed to code/ + + 5.8.3 - Naming fixes + 5.8.2 - Job system now supports prioritized tasks + 5.8.1 - Renames zpl_pad to zpl_ring + 5.8.0 - Added instantiated scratch pad (circular buffer) + 5.7.2 - Added Windows support for zpl_path_dirlist + 5.7.1 - Fixed few things in job system + macOS support for zpl_path_dirlist + 5.7.0 - Added a job system (zpl_thread_pool) + 5.6.5 - Fixes extra error cases for zpl_opts when input is: + - missing a value for an option, + - having an extra value for a flag (e.g. --enable-log shouldn't get a value.) + 5.6.4 - Several tweaks to the zpl_opts API + 5.6.3 - Added support for flags without values + 5.6.2 - Improve error handling for zpl_opts + 5.6.1 - Added support for strings with spaces in zpl_opts + 5.6.0 - Added zpl_opts for CLI argument parsing + 5.5.1 - Fixed time method for win + 5.5.0 - Integrate JSON5 writer into the core + 5.4.0 - Improved storage support for numbers in JSON5 parser + 5.3.0 - Integrated zpl_json into ZPL + 5.2.0 - Added zpl_string_sprintf + 5.1.1 - Added zpl_system_command_nores for output-less execution + 5.1.0 - Added event handler + 5.0.4 - Fix alias for zpl_list + 5.0.3 - Finalizing syntax changes + 5.0.2 - Fix segfault when using zpl_stack_memory + 5.0.1 - Small code improvements + 5.0.0 - Project structure changes + + 4.7.2 - Got rid of size arg for zpl_str_split_lines + 4.7.1 - Added an example + 4.7.0 - Added zpl_path_dirlist + 4.6.1 - zpl_memcopy x86 patch from upstream + 4.6.0 - Added few string-related functions + 4.5.9 - Error fixes + 4.5.8 - Warning fixes + 4.5.7 - Fixed timer loops. zpl_time* related functions work with seconds now + 4.5.6 - Fixed zpl_time_now() for Windows and Linux + 4.5.5 - Small cosmetic changes + 4.5.4 - Fixed issue when zpl_list_add would break the links + - when adding a new item between nodes + 4.5.3 - Fixed malformed enum values + 4.5.1 - Fixed some warnings + 4.5.0 - Added zpl_array_append_at + 4.4.0 - Added zpl_array_back, zpl_array_front + 4.3.0 - Added zpl_list + 4.2.0 - Added zpl_system_command_str + 4.1.2 - GG, fixed small compilation error + 4.1.1 - Fixed possible security issue in zpl_system_command + 4.1.0 - Added zpl_string_make_reserve and small fixes + 4.0.2 - Warning fix for _LARGEFILE64_SOURCE + 4.0.1 - include stdlib.h for getenv (temp) + 4.0.0 - ARM support, coding style changes and various improvements + + 3.4.1 - zpl_memcopy now uses memcpy for ARM arch-family + 3.4.0 - Removed obsolete code + 3.3.4 - Added Travis CI config + 3.3.3 - Small macro formatting changes + ZPL_SYSTEM_IOS + 3.3.2 - Fixes for android arm + 3.3.1 - Fixed some type cast warnings + 3.3.0 - Added Android support + 3.1.5 - Renamed userptr to user_data in timer + 3.1.4 - Fix for zpl_buffer not allocating correctly + 3.1.2 - Small fix in zpl_memcompare + 3.1.1 - Added char* conversion for data field in zpl_array_header + 3.1.0 - Added data field to zpl_array_header + 3.0.7 - Added timer userptr as argument to callback + 3.0.6 - Small changes + 3.0.5 - Fixed compilation for emscripten + 3.0.4 - Small fixes for tiny cpp warnings + 3.0.3 - Small fixes for various cpp warnings and errors + 3.0.2 - Fixed linux part, and removed trailing spaces + 3.0.1 - Small bugfix in zpl_file_open + 3.0.0 - Added several fixes and features + + 2.4.0 - Added remove to hash table + 2.3.3 - Removed redundant code + 2.3.2 - Eliminated extra warnings + 2.3.1 - Warning hunt + 2.3.0 - Added the ability to copy array/buffer and fixed bug in hash table. + 2.2.1 - Used tmpfile() for Windows + 2.2.0 - Added zpl_file_temp + 2.1.1 - Very small fix (forgive me) + 2.1.0 - Added the ability to resize bitstream + 2.0.8 - Small adjustments + 2.0.7 - MinGW related fixes + 2.0.0 - New NPM based version + + 1.2.2 - Small fix + 1.2.1 - Macro fixes + 1.2.0 - Added zpl_async macro + 1.1.0 - Added timer feature + 1.0.0 - Initial version + + +License: + This Software is dual licensed under the following licenses: + + Unlicense + This is free and unencumbered software released into the public domain. + + Anyone is free to copy, modify, publish, use, compile, sell, or + distribute this software, either in source code form or as a compiled + binary, for any purpose, commercial or non-commercial, and by any + means. + + In jurisdictions that recognize copyright laws, the author or authors + of this software dedicate any and all copyright interest in the + software to the public domain. We make this dedication for the benefit + of the public at large and to the detriment of our heirs and + successors. We intend this dedication to be an overt act of + relinquishment in perpetuity of all present and future rights to this + software under copyright law. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR + OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. + + For more information, please refer to + + BSD 3-Clause + Copyright (c) 2016-2021 Dominik Madarász. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + 3. Neither the name of the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef ZPL_H +#define ZPL_H + +#define ZPL_VERSION_MAJOR 19 +#define ZPL_VERSION_MINOR 5 +#define ZPL_VERSION_PATCH 0 +#define ZPL_VERSION_PRE "" + + // file: zpl_hedley.h + + /* Hedley - https://nemequ.github.io/hedley + * Created by Evan Nemerson + * + * To the extent possible under law, the author(s) have dedicated all + * copyright and related and neighboring rights to this software to + * the public domain worldwide. This software is distributed without + * any warranty. + * + * For details, see . + * SPDX-License-Identifier: CC0-1.0 + */ + + #if !defined(ZPL_HEDLEY_VERSION) || (ZPL_HEDLEY_VERSION < 12) + #if defined(ZPL_HEDLEY_VERSION) + # undef ZPL_HEDLEY_VERSION + #endif + #define ZPL_HEDLEY_VERSION 12 + + #if defined(ZPL_STRINGIFY_EX) + # undef ZPL_STRINGIFY_EX + #endif + #define ZPL_STRINGIFY_EX(x) #x + + #if defined(ZPL_STRINGIFY) + # undef ZPL_STRINGIFY + #endif + #define ZPL_STRINGIFY(x) ZPL_STRINGIFY_EX(x) + + #if defined(ZPL_CONCAT_EX) + # undef ZPL_CONCAT_EX + #endif + #define ZPL_CONCAT_EX(a,b) a##b + + #if defined(ZPL_CONCAT) + # undef ZPL_CONCAT + #endif + #define ZPL_CONCAT(a,b) ZPL_CONCAT_EX(a,b) + + #if defined(ZPL_VERSION_ENCODE) + # undef ZPL_VERSION_ENCODE + #endif + #define ZPL_VERSION_ENCODE(major,minor,patch) (((major) * 1000000) + ((minor) * 1000) + (patch)) + + #if defined(ZPL_VERSION_DECODE_MAJOR) + # undef ZPL_VERSION_DECODE_MAJOR + #endif + #define ZPL_VERSION_DECODE_MAJOR(version) ((version) / 1000000) + + #if defined(ZPL_VERSION_DECODE_MINOR) + # undef ZPL_VERSION_DECODE_MINOR + #endif + #define ZPL_VERSION_DECODE_MINOR(version) (((version) % 1000000) / 1000) + + #if defined(ZPL_VERSION_DECODE_PATCH) + # undef ZPL_VERSION_DECODE_PATCH + #endif + #define ZPL_VERSION_DECODE_PATCH(version) ((version) % 1000) + + #if defined(ZPL_VERSION_CHECK) + # undef ZPL_VERSION_CHECK + #endif + #define ZPL_VERSION_CHECK(major,minor,patch) (ZPL_VERSION_ENCODE(major,minor,patch) <= ZPL_VERSION) + + #if defined(ZPL_GNUC_VERSION) + # undef ZPL_GNUC_VERSION + #endif + #if defined(__GNUC__) && defined(__GNUC_PATCHLEVEL__) + # define ZPL_GNUC_VERSION ZPL_VERSION_ENCODE(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__) + #elif defined(__GNUC__) + # define ZPL_GNUC_VERSION ZPL_VERSION_ENCODE(__GNUC__, __GNUC_MINOR__, 0) + #endif + + #if defined(ZPL_GNUC_VERSION_CHECK) + # undef ZPL_GNUC_VERSION_CHECK + #endif + #if defined(ZPL_GNUC_VERSION) + # define ZPL_GNUC_VERSION_CHECK(major,minor,patch) (ZPL_GNUC_VERSION >= ZPL_VERSION_ENCODE(major, minor, patch)) + #else + # define ZPL_GNUC_VERSION_CHECK(major,minor,patch) (0) + #endif + + #if defined(ZPL_MSVC_VERSION) + # undef ZPL_MSVC_VERSION + #endif + #if defined(_MSC_FULL_VER) && (_MSC_FULL_VER >= 140000000) + # define ZPL_MSVC_VERSION ZPL_VERSION_ENCODE(_MSC_FULL_VER / 10000000, (_MSC_FULL_VER % 10000000) / 100000, (_MSC_FULL_VER % 100000) / 100) + #elif defined(_MSC_FULL_VER) + # define ZPL_MSVC_VERSION ZPL_VERSION_ENCODE(_MSC_FULL_VER / 1000000, (_MSC_FULL_VER % 1000000) / 10000, (_MSC_FULL_VER % 10000) / 10) + #elif defined(_MSC_VER) + # define ZPL_MSVC_VERSION ZPL_VERSION_ENCODE(_MSC_VER / 100, _MSC_VER % 100, 0) + #endif + + #if defined(ZPL_MSVC_VERSION_CHECK) + # undef ZPL_MSVC_VERSION_CHECK + #endif + #if !defined(_MSC_VER) + # define ZPL_MSVC_VERSION_CHECK(major,minor,patch) (0) + #elif defined(_MSC_VER) && (_MSC_VER >= 1400) + # define ZPL_MSVC_VERSION_CHECK(major,minor,patch) (_MSC_FULL_VER >= ((major * 10000000) + (minor * 100000) + (patch))) + #elif defined(_MSC_VER) && (_MSC_VER >= 1200) + # define ZPL_MSVC_VERSION_CHECK(major,minor,patch) (_MSC_FULL_VER >= ((major * 1000000) + (minor * 10000) + (patch))) + #else + # define ZPL_MSVC_VERSION_CHECK(major,minor,patch) (_MSC_VER >= ((major * 100) + (minor))) + #endif + + #if defined(ZPL_INTEL_VERSION) + # undef ZPL_INTEL_VERSION + #endif + #if defined(__INTEL_COMPILER) && defined(__INTEL_COMPILER_UPDATE) + # define ZPL_INTEL_VERSION ZPL_VERSION_ENCODE(__INTEL_COMPILER / 100, __INTEL_COMPILER % 100, __INTEL_COMPILER_UPDATE) + #elif defined(__INTEL_COMPILER) + # define ZPL_INTEL_VERSION ZPL_VERSION_ENCODE(__INTEL_COMPILER / 100, __INTEL_COMPILER % 100, 0) + #endif + + #if defined(ZPL_INTEL_VERSION_CHECK) + # undef ZPL_INTEL_VERSION_CHECK + #endif + #if defined(ZPL_INTEL_VERSION) + # define ZPL_INTEL_VERSION_CHECK(major,minor,patch) (ZPL_INTEL_VERSION >= ZPL_VERSION_ENCODE(major, minor, patch)) + #else + # define ZPL_INTEL_VERSION_CHECK(major,minor,patch) (0) + #endif + + #if defined(ZPL_PGI_VERSION) + # undef ZPL_PGI_VERSION + #endif + #if defined(__PGI) && defined(__PGIC__) && defined(__PGIC_MINOR__) && defined(__PGIC_PATCHLEVEL__) + # define ZPL_PGI_VERSION ZPL_VERSION_ENCODE(__PGIC__, __PGIC_MINOR__, __PGIC_PATCHLEVEL__) + #endif + + #if defined(ZPL_PGI_VERSION_CHECK) + # undef ZPL_PGI_VERSION_CHECK + #endif + #if defined(ZPL_PGI_VERSION) + # define ZPL_PGI_VERSION_CHECK(major,minor,patch) (ZPL_PGI_VERSION >= ZPL_VERSION_ENCODE(major, minor, patch)) + #else + # define ZPL_PGI_VERSION_CHECK(major,minor,patch) (0) + #endif + + #if defined(ZPL_SUNPRO_VERSION) + # undef ZPL_SUNPRO_VERSION + #endif + #if defined(__SUNPRO_C) && (__SUNPRO_C > 0x1000) + # define ZPL_SUNPRO_VERSION ZPL_VERSION_ENCODE((((__SUNPRO_C >> 16) & 0xf) * 10) + ((__SUNPRO_C >> 12) & 0xf), (((__SUNPRO_C >> 8) & 0xf) * 10) + ((__SUNPRO_C >> 4) & 0xf), (__SUNPRO_C & 0xf) * 10) + #elif defined(__SUNPRO_C) + # define ZPL_SUNPRO_VERSION ZPL_VERSION_ENCODE((__SUNPRO_C >> 8) & 0xf, (__SUNPRO_C >> 4) & 0xf, (__SUNPRO_C) & 0xf) + #elif defined(__SUNPRO_CC) && (__SUNPRO_CC > 0x1000) + # define ZPL_SUNPRO_VERSION ZPL_VERSION_ENCODE((((__SUNPRO_CC >> 16) & 0xf) * 10) + ((__SUNPRO_CC >> 12) & 0xf), (((__SUNPRO_CC >> 8) & 0xf) * 10) + ((__SUNPRO_CC >> 4) & 0xf), (__SUNPRO_CC & 0xf) * 10) + #elif defined(__SUNPRO_CC) + # define ZPL_SUNPRO_VERSION ZPL_VERSION_ENCODE((__SUNPRO_CC >> 8) & 0xf, (__SUNPRO_CC >> 4) & 0xf, (__SUNPRO_CC) & 0xf) + #endif + + #if defined(ZPL_SUNPRO_VERSION_CHECK) + # undef ZPL_SUNPRO_VERSION_CHECK + #endif + #if defined(ZPL_SUNPRO_VERSION) + # define ZPL_SUNPRO_VERSION_CHECK(major,minor,patch) (ZPL_SUNPRO_VERSION >= ZPL_VERSION_ENCODE(major, minor, patch)) + #else + # define ZPL_SUNPRO_VERSION_CHECK(major,minor,patch) (0) + #endif + + #if defined(ZPL_EMSCRIPTEN_VERSION) + # undef ZPL_EMSCRIPTEN_VERSION + #endif + #if defined(__EMSCRIPTEN__) + # define ZPL_EMSCRIPTEN_VERSION ZPL_VERSION_ENCODE(__EMSCRIPTEN_major__, __EMSCRIPTEN_minor__, __EMSCRIPTEN_tiny__) + #endif + + #if defined(ZPL_EMSCRIPTEN_VERSION_CHECK) + # undef ZPL_EMSCRIPTEN_VERSION_CHECK + #endif + #if defined(ZPL_EMSCRIPTEN_VERSION) + # define ZPL_EMSCRIPTEN_VERSION_CHECK(major,minor,patch) (ZPL_EMSCRIPTEN_VERSION >= ZPL_VERSION_ENCODE(major, minor, patch)) + #else + # define ZPL_EMSCRIPTEN_VERSION_CHECK(major,minor,patch) (0) + #endif + + #if defined(ZPL_ARM_VERSION) + # undef ZPL_ARM_VERSION + #endif + #if defined(__CC_ARM) && defined(__ARMCOMPILER_VERSION) + # define ZPL_ARM_VERSION ZPL_VERSION_ENCODE(__ARMCOMPILER_VERSION / 1000000, (__ARMCOMPILER_VERSION % 1000000) / 10000, (__ARMCOMPILER_VERSION % 10000) / 100) + #elif defined(__CC_ARM) && defined(__ARMCC_VERSION) + # define ZPL_ARM_VERSION ZPL_VERSION_ENCODE(__ARMCC_VERSION / 1000000, (__ARMCC_VERSION % 1000000) / 10000, (__ARMCC_VERSION % 10000) / 100) + #endif + + #if defined(ZPL_ARM_VERSION_CHECK) + # undef ZPL_ARM_VERSION_CHECK + #endif + #if defined(ZPL_ARM_VERSION) + # define ZPL_ARM_VERSION_CHECK(major,minor,patch) (ZPL_ARM_VERSION >= ZPL_VERSION_ENCODE(major, minor, patch)) + #else + # define ZPL_ARM_VERSION_CHECK(major,minor,patch) (0) + #endif + + #if defined(ZPL_IBM_VERSION) + # undef ZPL_IBM_VERSION + #endif + #if defined(__ibmxl__) + # define ZPL_IBM_VERSION ZPL_VERSION_ENCODE(__ibmxl_version__, __ibmxl_release__, __ibmxl_modification__) + #elif defined(__xlC__) && defined(__xlC_ver__) + # define ZPL_IBM_VERSION ZPL_VERSION_ENCODE(__xlC__ >> 8, __xlC__ & 0xff, (__xlC_ver__ >> 8) & 0xff) + #elif defined(__xlC__) + # define ZPL_IBM_VERSION ZPL_VERSION_ENCODE(__xlC__ >> 8, __xlC__ & 0xff, 0) + #endif + + #if defined(ZPL_IBM_VERSION_CHECK) + # undef ZPL_IBM_VERSION_CHECK + #endif + #if defined(ZPL_IBM_VERSION) + # define ZPL_IBM_VERSION_CHECK(major,minor,patch) (ZPL_IBM_VERSION >= ZPL_VERSION_ENCODE(major, minor, patch)) + #else + # define ZPL_IBM_VERSION_CHECK(major,minor,patch) (0) + #endif + + #if defined(ZPL_TI_VERSION) + # undef ZPL_TI_VERSION + #endif + #if \ + defined(__TI_COMPILER_VERSION__) && \ + ( \ + defined(__TMS470__) || defined(__TI_ARM__) || \ + defined(__MSP430__) || \ + defined(__TMS320C2000__) \ + ) + # if (__TI_COMPILER_VERSION__ >= 16000000) + # define ZPL_TI_VERSION ZPL_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) + # endif + #endif + + #if defined(ZPL_TI_VERSION_CHECK) + # undef ZPL_TI_VERSION_CHECK + #endif + #if defined(ZPL_TI_VERSION) + # define ZPL_TI_VERSION_CHECK(major,minor,patch) (ZPL_TI_VERSION >= ZPL_VERSION_ENCODE(major, minor, patch)) + #else + # define ZPL_TI_VERSION_CHECK(major,minor,patch) (0) + #endif + + #if defined(ZPL_TI_CL2000_VERSION) + # undef ZPL_TI_CL2000_VERSION + #endif + #if defined(__TI_COMPILER_VERSION__) && defined(__TMS320C2000__) + # define ZPL_TI_CL2000_VERSION ZPL_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) + #endif + + #if defined(ZPL_TI_CL2000_VERSION_CHECK) + # undef ZPL_TI_CL2000_VERSION_CHECK + #endif + #if defined(ZPL_TI_CL2000_VERSION) + # define ZPL_TI_CL2000_VERSION_CHECK(major,minor,patch) (ZPL_TI_CL2000_VERSION >= ZPL_VERSION_ENCODE(major, minor, patch)) + #else + # define ZPL_TI_CL2000_VERSION_CHECK(major,minor,patch) (0) + #endif + + #if defined(ZPL_TI_CL430_VERSION) + # undef ZPL_TI_CL430_VERSION + #endif + #if defined(__TI_COMPILER_VERSION__) && defined(__MSP430__) + # define ZPL_TI_CL430_VERSION ZPL_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) + #endif + + #if defined(ZPL_TI_CL430_VERSION_CHECK) + # undef ZPL_TI_CL430_VERSION_CHECK + #endif + #if defined(ZPL_TI_CL430_VERSION) + # define ZPL_TI_CL430_VERSION_CHECK(major,minor,patch) (ZPL_TI_CL430_VERSION >= ZPL_VERSION_ENCODE(major, minor, patch)) + #else + # define ZPL_TI_CL430_VERSION_CHECK(major,minor,patch) (0) + #endif + + #if defined(ZPL_TI_ARMCL_VERSION) + # undef ZPL_TI_ARMCL_VERSION + #endif + #if defined(__TI_COMPILER_VERSION__) && (defined(__TMS470__) || defined(__TI_ARM__)) + # define ZPL_TI_ARMCL_VERSION ZPL_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) + #endif + + #if defined(ZPL_TI_ARMCL_VERSION_CHECK) + # undef ZPL_TI_ARMCL_VERSION_CHECK + #endif + #if defined(ZPL_TI_ARMCL_VERSION) + # define ZPL_TI_ARMCL_VERSION_CHECK(major,minor,patch) (ZPL_TI_ARMCL_VERSION >= ZPL_VERSION_ENCODE(major, minor, patch)) + #else + # define ZPL_TI_ARMCL_VERSION_CHECK(major,minor,patch) (0) + #endif + + #if defined(ZPL_TI_CL6X_VERSION) + # undef ZPL_TI_CL6X_VERSION + #endif + #if defined(__TI_COMPILER_VERSION__) && defined(__TMS320C6X__) + # define ZPL_TI_CL6X_VERSION ZPL_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) + #endif + + #if defined(ZPL_TI_CL6X_VERSION_CHECK) + # undef ZPL_TI_CL6X_VERSION_CHECK + #endif + #if defined(ZPL_TI_CL6X_VERSION) + # define ZPL_TI_CL6X_VERSION_CHECK(major,minor,patch) (ZPL_TI_CL6X_VERSION >= ZPL_VERSION_ENCODE(major, minor, patch)) + #else + # define ZPL_TI_CL6X_VERSION_CHECK(major,minor,patch) (0) + #endif + + #if defined(ZPL_TI_CL7X_VERSION) + # undef ZPL_TI_CL7X_VERSION + #endif + #if defined(__TI_COMPILER_VERSION__) && defined(__C7000__) + # define ZPL_TI_CL7X_VERSION ZPL_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) + #endif + + #if defined(ZPL_TI_CL7X_VERSION_CHECK) + # undef ZPL_TI_CL7X_VERSION_CHECK + #endif + #if defined(ZPL_TI_CL7X_VERSION) + # define ZPL_TI_CL7X_VERSION_CHECK(major,minor,patch) (ZPL_TI_CL7X_VERSION >= ZPL_VERSION_ENCODE(major, minor, patch)) + #else + # define ZPL_TI_CL7X_VERSION_CHECK(major,minor,patch) (0) + #endif + + #if defined(ZPL_TI_CLPRU_VERSION) + # undef ZPL_TI_CLPRU_VERSION + #endif + #if defined(__TI_COMPILER_VERSION__) && defined(__PRU__) + # define ZPL_TI_CLPRU_VERSION ZPL_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) + #endif + + #if defined(ZPL_TI_CLPRU_VERSION_CHECK) + # undef ZPL_TI_CLPRU_VERSION_CHECK + #endif + #if defined(ZPL_TI_CLPRU_VERSION) + # define ZPL_TI_CLPRU_VERSION_CHECK(major,minor,patch) (ZPL_TI_CLPRU_VERSION >= ZPL_VERSION_ENCODE(major, minor, patch)) + #else + # define ZPL_TI_CLPRU_VERSION_CHECK(major,minor,patch) (0) + #endif + + #if defined(ZPL_CRAY_VERSION) + # undef ZPL_CRAY_VERSION + #endif + #if defined(_CRAYC) + # if defined(_RELEASE_PATCHLEVEL) + # define ZPL_CRAY_VERSION ZPL_VERSION_ENCODE(_RELEASE_MAJOR, _RELEASE_MINOR, _RELEASE_PATCHLEVEL) + # else + # define ZPL_CRAY_VERSION ZPL_VERSION_ENCODE(_RELEASE_MAJOR, _RELEASE_MINOR, 0) + # endif + #endif + + #if defined(ZPL_CRAY_VERSION_CHECK) + # undef ZPL_CRAY_VERSION_CHECK + #endif + #if defined(ZPL_CRAY_VERSION) + # define ZPL_CRAY_VERSION_CHECK(major,minor,patch) (ZPL_CRAY_VERSION >= ZPL_VERSION_ENCODE(major, minor, patch)) + #else + # define ZPL_CRAY_VERSION_CHECK(major,minor,patch) (0) + #endif + + #if defined(ZPL_IAR_VERSION) + # undef ZPL_IAR_VERSION + #endif + #if defined(__IAR_SYSTEMS_ICC__) + # if __VER__ > 1000 + # define ZPL_IAR_VERSION ZPL_VERSION_ENCODE((__VER__ / 1000000), ((__VER__ / 1000) % 1000), (__VER__ % 1000)) + # else + # define ZPL_IAR_VERSION ZPL_VERSION_ENCODE(VER / 100, __VER__ % 100, 0) + # endif + #endif + + #if defined(ZPL_IAR_VERSION_CHECK) + # undef ZPL_IAR_VERSION_CHECK + #endif + #if defined(ZPL_IAR_VERSION) + # define ZPL_IAR_VERSION_CHECK(major,minor,patch) (ZPL_IAR_VERSION >= ZPL_VERSION_ENCODE(major, minor, patch)) + #else + # define ZPL_IAR_VERSION_CHECK(major,minor,patch) (0) + #endif + + #if defined(ZPL_TINYC_VERSION) + # undef ZPL_TINYC_VERSION + #endif + #if defined(__TINYC__) + # define ZPL_TINYC_VERSION ZPL_VERSION_ENCODE(__TINYC__ / 1000, (__TINYC__ / 100) % 10, __TINYC__ % 100) + #endif + + #if defined(ZPL_TINYC_VERSION_CHECK) + # undef ZPL_TINYC_VERSION_CHECK + #endif + #if defined(ZPL_TINYC_VERSION) + # define ZPL_TINYC_VERSION_CHECK(major,minor,patch) (ZPL_TINYC_VERSION >= ZPL_VERSION_ENCODE(major, minor, patch)) + #else + # define ZPL_TINYC_VERSION_CHECK(major,minor,patch) (0) + #endif + + #if defined(ZPL_DMC_VERSION) + # undef ZPL_DMC_VERSION + #endif + #if defined(__DMC__) + # define ZPL_DMC_VERSION ZPL_VERSION_ENCODE(__DMC__ >> 8, (__DMC__ >> 4) & 0xf, __DMC__ & 0xf) + #endif + + #if defined(ZPL_DMC_VERSION_CHECK) + # undef ZPL_DMC_VERSION_CHECK + #endif + #if defined(ZPL_DMC_VERSION) + # define ZPL_DMC_VERSION_CHECK(major,minor,patch) (ZPL_DMC_VERSION >= ZPL_VERSION_ENCODE(major, minor, patch)) + #else + # define ZPL_DMC_VERSION_CHECK(major,minor,patch) (0) + #endif + + #if defined(ZPL_COMPCERT_VERSION) + # undef ZPL_COMPCERT_VERSION + #endif + #if defined(__COMPCERT_VERSION__) + # define ZPL_COMPCERT_VERSION ZPL_VERSION_ENCODE(__COMPCERT_VERSION__ / 10000, (__COMPCERT_VERSION__ / 100) % 100, __COMPCERT_VERSION__ % 100) + #endif + + #if defined(ZPL_COMPCERT_VERSION_CHECK) + # undef ZPL_COMPCERT_VERSION_CHECK + #endif + #if defined(ZPL_COMPCERT_VERSION) + # define ZPL_COMPCERT_VERSION_CHECK(major,minor,patch) (ZPL_COMPCERT_VERSION >= ZPL_VERSION_ENCODE(major, minor, patch)) + #else + # define ZPL_COMPCERT_VERSION_CHECK(major,minor,patch) (0) + #endif + + #if defined(ZPL_PELLES_VERSION) + # undef ZPL_PELLES_VERSION + #endif + #if defined(__POCC__) + # define ZPL_PELLES_VERSION ZPL_VERSION_ENCODE(__POCC__ / 100, __POCC__ % 100, 0) + #endif + + #if defined(ZPL_PELLES_VERSION_CHECK) + # undef ZPL_PELLES_VERSION_CHECK + #endif + #if defined(ZPL_PELLES_VERSION) + # define ZPL_PELLES_VERSION_CHECK(major,minor,patch) (ZPL_PELLES_VERSION >= ZPL_VERSION_ENCODE(major, minor, patch)) + #else + # define ZPL_PELLES_VERSION_CHECK(major,minor,patch) (0) + #endif + + #if defined(ZPL_GCC_VERSION) + # undef ZPL_GCC_VERSION + #endif + #if \ + defined(ZPL_GNUC_VERSION) && \ + !defined(__clang__) && \ + !defined(ZPL_INTEL_VERSION) && \ + !defined(ZPL_PGI_VERSION) && \ + !defined(ZPL_ARM_VERSION) && \ + !defined(ZPL_TI_VERSION) && \ + !defined(ZPL_TI_ARMCL_VERSION) && \ + !defined(ZPL_TI_CL430_VERSION) && \ + !defined(ZPL_TI_CL2000_VERSION) && \ + !defined(ZPL_TI_CL6X_VERSION) && \ + !defined(ZPL_TI_CL7X_VERSION) && \ + !defined(ZPL_TI_CLPRU_VERSION) && \ + !defined(__COMPCERT__) + # define ZPL_GCC_VERSION ZPL_GNUC_VERSION + #endif + + #if defined(ZPL_GCC_VERSION_CHECK) + # undef ZPL_GCC_VERSION_CHECK + #endif + #if defined(ZPL_GCC_VERSION) + # define ZPL_GCC_VERSION_CHECK(major,minor,patch) (ZPL_GCC_VERSION >= ZPL_VERSION_ENCODE(major, minor, patch)) + #else + # define ZPL_GCC_VERSION_CHECK(major,minor,patch) (0) + #endif + + #if defined(ZPL_HAS_ATTRIBUTE) + # undef ZPL_HAS_ATTRIBUTE + #endif + #if defined(__has_attribute) + # define ZPL_HAS_ATTRIBUTE(attribute) __has_attribute(attribute) + #else + # define ZPL_HAS_ATTRIBUTE(attribute) (0) + #endif + + #if defined(ZPL_GNUC_HAS_ATTRIBUTE) + # undef ZPL_GNUC_HAS_ATTRIBUTE + #endif + #if defined(__has_attribute) + # define ZPL_GNUC_HAS_ATTRIBUTE(attribute,major,minor,patch) __has_attribute(attribute) + #else + # define ZPL_GNUC_HAS_ATTRIBUTE(attribute,major,minor,patch) ZPL_GNUC_VERSION_CHECK(major,minor,patch) + #endif + + #if defined(ZPL_GCC_HAS_ATTRIBUTE) + # undef ZPL_GCC_HAS_ATTRIBUTE + #endif + #if defined(__has_attribute) + # define ZPL_GCC_HAS_ATTRIBUTE(attribute,major,minor,patch) __has_attribute(attribute) + #else + # define ZPL_GCC_HAS_ATTRIBUTE(attribute,major,minor,patch) ZPL_GCC_VERSION_CHECK(major,minor,patch) + #endif + + #if defined(ZPL_HAS_CPP_ATTRIBUTE) + # undef ZPL_HAS_CPP_ATTRIBUTE + #endif + #if \ + defined(__has_cpp_attribute) && \ + defined(__cplusplus) && \ + (!defined(ZPL_SUNPRO_VERSION) || ZPL_SUNPRO_VERSION_CHECK(5,15,0)) + # define ZPL_HAS_CPP_ATTRIBUTE(attribute) __has_cpp_attribute(attribute) + #else + # define ZPL_HAS_CPP_ATTRIBUTE(attribute) (0) + #endif + + #if defined(ZPL_HAS_CPP_ATTRIBUTE_NS) + # undef ZPL_HAS_CPP_ATTRIBUTE_NS + #endif + #if !defined(__cplusplus) || !defined(__has_cpp_attribute) + # define ZPL_HAS_CPP_ATTRIBUTE_NS(ns,attribute) (0) + #elif \ + !defined(ZPL_PGI_VERSION) && \ + !defined(ZPL_IAR_VERSION) && \ + (!defined(ZPL_SUNPRO_VERSION) || ZPL_SUNPRO_VERSION_CHECK(5,15,0)) && \ + (!defined(ZPL_MSVC_VERSION) || ZPL_MSVC_VERSION_CHECK(19,20,0)) + # define ZPL_HAS_CPP_ATTRIBUTE_NS(ns,attribute) ZPL_HAS_CPP_ATTRIBUTE(ns::attribute) + #else + # define ZPL_HAS_CPP_ATTRIBUTE_NS(ns,attribute) (0) + #endif + + #if defined(ZPL_GNUC_HAS_CPP_ATTRIBUTE) + # undef ZPL_GNUC_HAS_CPP_ATTRIBUTE + #endif + #if defined(__has_cpp_attribute) && defined(__cplusplus) + # define ZPL_GNUC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) __has_cpp_attribute(attribute) + #else + # define ZPL_GNUC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) ZPL_GNUC_VERSION_CHECK(major,minor,patch) + #endif + + #if defined(ZPL_GCC_HAS_CPP_ATTRIBUTE) + # undef ZPL_GCC_HAS_CPP_ATTRIBUTE + #endif + #if defined(__has_cpp_attribute) && defined(__cplusplus) + # define ZPL_GCC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) __has_cpp_attribute(attribute) + #else + # define ZPL_GCC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) ZPL_GCC_VERSION_CHECK(major,minor,patch) + #endif + + #if defined(ZPL_HAS_BUILTIN) + # undef ZPL_HAS_BUILTIN + #endif + #if defined(__has_builtin) + # define ZPL_HAS_BUILTIN(builtin) __has_builtin(builtin) + #else + # define ZPL_HAS_BUILTIN(builtin) (0) + #endif + + #if defined(ZPL_GNUC_HAS_BUILTIN) + # undef ZPL_GNUC_HAS_BUILTIN + #endif + #if defined(__has_builtin) + # define ZPL_GNUC_HAS_BUILTIN(builtin,major,minor,patch) __has_builtin(builtin) + #else + # define ZPL_GNUC_HAS_BUILTIN(builtin,major,minor,patch) ZPL_GNUC_VERSION_CHECK(major,minor,patch) + #endif + + #if defined(ZPL_GCC_HAS_BUILTIN) + # undef ZPL_GCC_HAS_BUILTIN + #endif + #if defined(__has_builtin) + # define ZPL_GCC_HAS_BUILTIN(builtin,major,minor,patch) __has_builtin(builtin) + #else + # define ZPL_GCC_HAS_BUILTIN(builtin,major,minor,patch) ZPL_GCC_VERSION_CHECK(major,minor,patch) + #endif + + #if defined(ZPL_HAS_FEATURE) + # undef ZPL_HAS_FEATURE + #endif + #if defined(__has_feature) + # define ZPL_HAS_FEATURE(feature) __has_feature(feature) + #else + # define ZPL_HAS_FEATURE(feature) (0) + #endif + + #if defined(ZPL_GNUC_HAS_FEATURE) + # undef ZPL_GNUC_HAS_FEATURE + #endif + #if defined(__has_feature) + # define ZPL_GNUC_HAS_FEATURE(feature,major,minor,patch) __has_feature(feature) + #else + # define ZPL_GNUC_HAS_FEATURE(feature,major,minor,patch) ZPL_GNUC_VERSION_CHECK(major,minor,patch) + #endif + + #if defined(ZPL_GCC_HAS_FEATURE) + # undef ZPL_GCC_HAS_FEATURE + #endif + #if defined(__has_feature) + # define ZPL_GCC_HAS_FEATURE(feature,major,minor,patch) __has_feature(feature) + #else + # define ZPL_GCC_HAS_FEATURE(feature,major,minor,patch) ZPL_GCC_VERSION_CHECK(major,minor,patch) + #endif + + #if defined(ZPL_HAS_EXTENSION) + # undef ZPL_HAS_EXTENSION + #endif + #if defined(__has_extension) + # define ZPL_HAS_EXTENSION(extension) __has_extension(extension) + #else + # define ZPL_HAS_EXTENSION(extension) (0) + #endif + + #if defined(ZPL_GNUC_HAS_EXTENSION) + # undef ZPL_GNUC_HAS_EXTENSION + #endif + #if defined(__has_extension) + # define ZPL_GNUC_HAS_EXTENSION(extension,major,minor,patch) __has_extension(extension) + #else + # define ZPL_GNUC_HAS_EXTENSION(extension,major,minor,patch) ZPL_GNUC_VERSION_CHECK(major,minor,patch) + #endif + + #if defined(ZPL_GCC_HAS_EXTENSION) + # undef ZPL_GCC_HAS_EXTENSION + #endif + #if defined(__has_extension) + # define ZPL_GCC_HAS_EXTENSION(extension,major,minor,patch) __has_extension(extension) + #else + # define ZPL_GCC_HAS_EXTENSION(extension,major,minor,patch) ZPL_GCC_VERSION_CHECK(major,minor,patch) + #endif + + #if defined(ZPL_HAS_DECLSPEC_ATTRIBUTE) + # undef ZPL_HAS_DECLSPEC_ATTRIBUTE + #endif + #if defined(__has_declspec_attribute) + # define ZPL_HAS_DECLSPEC_ATTRIBUTE(attribute) __has_declspec_attribute(attribute) + #else + # define ZPL_HAS_DECLSPEC_ATTRIBUTE(attribute) (0) + #endif + + #if defined(ZPL_GNUC_HAS_DECLSPEC_ATTRIBUTE) + # undef ZPL_GNUC_HAS_DECLSPEC_ATTRIBUTE + #endif + #if defined(__has_declspec_attribute) + # define ZPL_GNUC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) __has_declspec_attribute(attribute) + #else + # define ZPL_GNUC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) ZPL_GNUC_VERSION_CHECK(major,minor,patch) + #endif + + #if defined(ZPL_GCC_HAS_DECLSPEC_ATTRIBUTE) + # undef ZPL_GCC_HAS_DECLSPEC_ATTRIBUTE + #endif + #if defined(__has_declspec_attribute) + # define ZPL_GCC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) __has_declspec_attribute(attribute) + #else + # define ZPL_GCC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) ZPL_GCC_VERSION_CHECK(major,minor,patch) + #endif + + #if defined(ZPL_HAS_WARNING) + # undef ZPL_HAS_WARNING + #endif + #if defined(__has_warning) + # define ZPL_HAS_WARNING(warning) __has_warning(warning) + #else + # define ZPL_HAS_WARNING(warning) (0) + #endif + + #if defined(ZPL_GNUC_HAS_WARNING) + # undef ZPL_GNUC_HAS_WARNING + #endif + #if defined(__has_warning) + # define ZPL_GNUC_HAS_WARNING(warning,major,minor,patch) __has_warning(warning) + #else + # define ZPL_GNUC_HAS_WARNING(warning,major,minor,patch) ZPL_GNUC_VERSION_CHECK(major,minor,patch) + #endif + + #if defined(ZPL_GCC_HAS_WARNING) + # undef ZPL_GCC_HAS_WARNING + #endif + #if defined(__has_warning) + # define ZPL_GCC_HAS_WARNING(warning,major,minor,patch) __has_warning(warning) + #else + # define ZPL_GCC_HAS_WARNING(warning,major,minor,patch) ZPL_GCC_VERSION_CHECK(major,minor,patch) + #endif + + /* ZPL_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_ is for + ZPL INTERNAL USE ONLY. API subject to change without notice. */ + #if defined(ZPL_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_) + # undef ZPL_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_ + #endif + #if defined(__cplusplus) + # if ZPL_HAS_WARNING("-Wc++98-compat") + # if ZPL_HAS_WARNING("-Wc++17-extensions") + # define ZPL_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(xpr) \ + ZPL_DIAGNOSTIC_PUSH \ + _Pragma("clang diagnostic ignored \"-Wc++98-compat\"") \ + _Pragma("clang diagnostic ignored \"-Wc++17-extensions\"") \ + xpr \ + ZPL_DIAGNOSTIC_POP + # else + # define ZPL_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(xpr) \ + ZPL_DIAGNOSTIC_PUSH \ + _Pragma("clang diagnostic ignored \"-Wc++98-compat\"") \ + xpr \ + ZPL_DIAGNOSTIC_POP + # endif + # endif + #endif + #if !defined(ZPL_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_) + # define ZPL_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(x) x + #endif + + #if defined(ZPL_CONST_CAST) + # undef ZPL_CONST_CAST + #endif + #if defined(__cplusplus) + # define ZPL_CONST_CAST(T, expr) (const_cast(expr)) + #elif \ + ZPL_HAS_WARNING("-Wcast-qual") || \ + ZPL_GCC_VERSION_CHECK(4,6,0) || \ + ZPL_INTEL_VERSION_CHECK(13,0,0) + # define ZPL_CONST_CAST(T, expr) (__extension__ ({ \ + ZPL_DIAGNOSTIC_PUSH \ + ZPL_DIAGNOSTIC_DISABLE_CAST_QUAL \ + ((T) (expr)); \ + ZPL_DIAGNOSTIC_POP \ + })) + #else + # define ZPL_CONST_CAST(T, expr) ((T) (expr)) + #endif + + #if defined(ZPL_REINTERPRET_CAST) + # undef ZPL_REINTERPRET_CAST + #endif + #if defined(__cplusplus) + # define ZPL_REINTERPRET_CAST(T, expr) (reinterpret_cast(expr)) + #else + # define ZPL_REINTERPRET_CAST(T, expr) ((T) (expr)) + #endif + + #if defined(ZPL_STATIC_CAST) + # undef ZPL_STATIC_CAST + #endif + #if defined(__cplusplus) + # define ZPL_STATIC_CAST(T, expr) (static_cast(expr)) + #else + # define ZPL_STATIC_CAST(T, expr) ((T) (expr)) + #endif + + #if defined(ZPL_CPP_CAST) + # undef ZPL_CPP_CAST + #endif + #if defined(__cplusplus) + # if ZPL_HAS_WARNING("-Wold-style-cast") + # define ZPL_CPP_CAST(T, expr) \ + ZPL_DIAGNOSTIC_PUSH \ + _Pragma("clang diagnostic ignored \"-Wold-style-cast\"") \ + ((T) (expr)) \ + ZPL_DIAGNOSTIC_POP + # elif ZPL_IAR_VERSION_CHECK(8,3,0) + # define ZPL_CPP_CAST(T, expr) \ + ZPL_DIAGNOSTIC_PUSH \ + _Pragma("diag_suppress=Pe137") \ + ZPL_DIAGNOSTIC_POP \ + # else + # define ZPL_CPP_CAST(T, expr) ((T) (expr)) + # endif + #else + # define ZPL_CPP_CAST(T, expr) (expr) + #endif + + #if \ + (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) || \ + defined(__clang__) || \ + ZPL_GCC_VERSION_CHECK(3,0,0) || \ + ZPL_INTEL_VERSION_CHECK(13,0,0) || \ + ZPL_IAR_VERSION_CHECK(8,0,0) || \ + ZPL_PGI_VERSION_CHECK(18,4,0) || \ + ZPL_ARM_VERSION_CHECK(4,1,0) || \ + ZPL_TI_VERSION_CHECK(15,12,0) || \ + ZPL_TI_ARMCL_VERSION_CHECK(4,7,0) || \ + ZPL_TI_CL430_VERSION_CHECK(2,0,1) || \ + ZPL_TI_CL2000_VERSION_CHECK(6,1,0) || \ + ZPL_TI_CL6X_VERSION_CHECK(7,0,0) || \ + ZPL_TI_CL7X_VERSION_CHECK(1,2,0) || \ + ZPL_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + ZPL_CRAY_VERSION_CHECK(5,0,0) || \ + ZPL_TINYC_VERSION_CHECK(0,9,17) || \ + ZPL_SUNPRO_VERSION_CHECK(8,0,0) || \ + (ZPL_IBM_VERSION_CHECK(10,1,0) && defined(__C99_PRAGMA_OPERATOR)) + # define ZPL_PRAGMA(value) _Pragma(#value) + #elif ZPL_MSVC_VERSION_CHECK(15,0,0) + # define ZPL_PRAGMA(value) __pragma(value) + #else + # define ZPL_PRAGMA(value) + #endif + + #if defined(ZPL_DIAGNOSTIC_PUSH) + # undef ZPL_DIAGNOSTIC_PUSH + #endif + #if defined(ZPL_DIAGNOSTIC_PUSH_WARNLEVEL) + # undef ZPL_DIAGNOSTIC_PUSH_WARNLEVEL + #endif + #if defined(ZPL_DIAGNOSTIC_POP) + # undef ZPL_DIAGNOSTIC_POP + #endif + #if defined(__clang__) + # define ZPL_DIAGNOSTIC_PUSH _Pragma("clang diagnostic push") + # define ZPL_DIAGNOSTIC_PUSH_WARNLEVEL(x) + # define ZPL_DIAGNOSTIC_POP _Pragma("clang diagnostic pop") + #elif ZPL_INTEL_VERSION_CHECK(13,0,0) + # define ZPL_DIAGNOSTIC_PUSH _Pragma("warning(push)") + # define ZPL_DIAGNOSTIC_PUSH_WARNLEVEL(x) + # define ZPL_DIAGNOSTIC_POP _Pragma("warning(pop)") + #elif ZPL_GCC_VERSION_CHECK(4,6,0) + # define ZPL_DIAGNOSTIC_PUSH _Pragma("GCC diagnostic push") + # define ZPL_DIAGNOSTIC_PUSH_WARNLEVEL(x) + # define ZPL_DIAGNOSTIC_POP _Pragma("GCC diagnostic pop") + #elif ZPL_MSVC_VERSION_CHECK(15,0,0) + # define ZPL_DIAGNOSTIC_PUSH __pragma(warning(push)) + # define ZPL_DIAGNOSTIC_PUSH_WARNLEVEL(x) __pragma(warning(push, x)) + # define ZPL_DIAGNOSTIC_POP __pragma(warning(pop)) + #elif ZPL_ARM_VERSION_CHECK(5,6,0) + # define ZPL_DIAGNOSTIC_PUSH _Pragma("push") + # define ZPL_DIAGNOSTIC_POP _Pragma("pop") + #elif \ + ZPL_TI_VERSION_CHECK(15,12,0) || \ + ZPL_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + ZPL_TI_CL430_VERSION_CHECK(4,4,0) || \ + ZPL_TI_CL6X_VERSION_CHECK(8,1,0) || \ + ZPL_TI_CL7X_VERSION_CHECK(1,2,0) || \ + ZPL_TI_CLPRU_VERSION_CHECK(2,1,0) + # define ZPL_DIAGNOSTIC_PUSH _Pragma("diag_push") + # define ZPL_DIAGNOSTIC_POP _Pragma("diag_pop") + #elif ZPL_PELLES_VERSION_CHECK(2,90,0) + # define ZPL_DIAGNOSTIC_PUSH _Pragma("warning(push)") + # define ZPL_DIAGNOSTIC_POP _Pragma("warning(pop)") + #else + # define ZPL_DIAGNOSTIC_PUSH + # define ZPL_DIAGNOSTIC_POP + #endif + + #if defined(ZPL_DIAGNOSTIC_DISABLE_DEPRECATED) + # undef ZPL_DIAGNOSTIC_DISABLE_DEPRECATED + #endif + #if ZPL_HAS_WARNING("-Wdeprecated-declarations") + # define ZPL_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("clang diagnostic ignored \"-Wdeprecated-declarations\"") + #elif ZPL_INTEL_VERSION_CHECK(13,0,0) + # define ZPL_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("warning(disable:1478 1786)") + #elif ZPL_PGI_VERSION_CHECK(17,10,0) + # define ZPL_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress 1215,1444") + #elif ZPL_GCC_VERSION_CHECK(4,3,0) + # define ZPL_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"") + #elif ZPL_MSVC_VERSION_CHECK(15,0,0) + # define ZPL_DIAGNOSTIC_DISABLE_DEPRECATED __pragma(warning(disable:4996)) + #elif \ + ZPL_TI_VERSION_CHECK(15,12,0) || \ + (ZPL_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + ZPL_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (ZPL_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + ZPL_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (ZPL_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + ZPL_TI_CL430_VERSION_CHECK(4,3,0) || \ + (ZPL_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + ZPL_TI_CL6X_VERSION_CHECK(7,5,0) || \ + ZPL_TI_CL7X_VERSION_CHECK(1,2,0) || \ + ZPL_TI_CLPRU_VERSION_CHECK(2,1,0) + # define ZPL_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress 1291,1718") + #elif ZPL_SUNPRO_VERSION_CHECK(5,13,0) && !defined(__cplusplus) + # define ZPL_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("error_messages(off,E_DEPRECATED_ATT,E_DEPRECATED_ATT_MESS)") + #elif ZPL_SUNPRO_VERSION_CHECK(5,13,0) && defined(__cplusplus) + # define ZPL_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("error_messages(off,symdeprecated,symdeprecated2)") + #elif ZPL_IAR_VERSION_CHECK(8,0,0) + # define ZPL_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress=Pe1444,Pe1215") + #elif ZPL_PELLES_VERSION_CHECK(2,90,0) + # define ZPL_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("warn(disable:2241)") + #else + # define ZPL_DIAGNOSTIC_DISABLE_DEPRECATED + #endif + + #if defined(ZPL_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS) + # undef ZPL_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS + #endif + #if ZPL_HAS_WARNING("-Wunknown-pragmas") + # define ZPL_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("clang diagnostic ignored \"-Wunknown-pragmas\"") + #elif ZPL_INTEL_VERSION_CHECK(13,0,0) + # define ZPL_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("warning(disable:161)") + #elif ZPL_PGI_VERSION_CHECK(17,10,0) + # define ZPL_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 1675") + #elif ZPL_GCC_VERSION_CHECK(4,3,0) + # define ZPL_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("GCC diagnostic ignored \"-Wunknown-pragmas\"") + #elif ZPL_MSVC_VERSION_CHECK(15,0,0) + # define ZPL_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS __pragma(warning(disable:4068)) + #elif \ + ZPL_TI_VERSION_CHECK(16,9,0) || \ + ZPL_TI_CL6X_VERSION_CHECK(8,0,0) || \ + ZPL_TI_CL7X_VERSION_CHECK(1,2,0) || \ + ZPL_TI_CLPRU_VERSION_CHECK(2,3,0) + # define ZPL_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 163") + #elif ZPL_TI_CL6X_VERSION_CHECK(8,0,0) + # define ZPL_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 163") + #elif ZPL_IAR_VERSION_CHECK(8,0,0) + # define ZPL_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress=Pe161") + #else + # define ZPL_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS + #endif + + #if defined(ZPL_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES) + # undef ZPL_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES + #endif + #if ZPL_HAS_WARNING("-Wunknown-attributes") + # define ZPL_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("clang diagnostic ignored \"-Wunknown-attributes\"") + #elif ZPL_GCC_VERSION_CHECK(4,6,0) + # define ZPL_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"") + #elif ZPL_INTEL_VERSION_CHECK(17,0,0) + # define ZPL_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("warning(disable:1292)") + #elif ZPL_MSVC_VERSION_CHECK(19,0,0) + # define ZPL_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES __pragma(warning(disable:5030)) + #elif ZPL_PGI_VERSION_CHECK(17,10,0) + # define ZPL_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress 1097") + #elif ZPL_SUNPRO_VERSION_CHECK(5,14,0) && defined(__cplusplus) + # define ZPL_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("error_messages(off,attrskipunsup)") + #elif \ + ZPL_TI_VERSION_CHECK(18,1,0) || \ + ZPL_TI_CL6X_VERSION_CHECK(8,3,0) || \ + ZPL_TI_CL7X_VERSION_CHECK(1,2,0) + # define ZPL_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress 1173") + #elif ZPL_IAR_VERSION_CHECK(8,0,0) + # define ZPL_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress=Pe1097") + #else + # define ZPL_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES + #endif + + #if defined(ZPL_DIAGNOSTIC_DISABLE_CAST_QUAL) + # undef ZPL_DIAGNOSTIC_DISABLE_CAST_QUAL + #endif + #if ZPL_HAS_WARNING("-Wcast-qual") + # define ZPL_DIAGNOSTIC_DISABLE_CAST_QUAL _Pragma("clang diagnostic ignored \"-Wcast-qual\"") + #elif ZPL_INTEL_VERSION_CHECK(13,0,0) + # define ZPL_DIAGNOSTIC_DISABLE_CAST_QUAL _Pragma("warning(disable:2203 2331)") + #elif ZPL_GCC_VERSION_CHECK(3,0,0) + # define ZPL_DIAGNOSTIC_DISABLE_CAST_QUAL _Pragma("GCC diagnostic ignored \"-Wcast-qual\"") + #else + # define ZPL_DIAGNOSTIC_DISABLE_CAST_QUAL + #endif + + #if defined(ZPL_DEPRECATED) + # undef ZPL_DEPRECATED + #endif + #if defined(ZPL_DEPRECATED_FOR) + # undef ZPL_DEPRECATED_FOR + #endif + #if defined(__cplusplus) && (__cplusplus >= 201402L) + # define ZPL_DEPRECATED(since) ZPL_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[deprecated("Since " #since)]]) + # define ZPL_DEPRECATED_FOR(since, replacement) ZPL_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[deprecated("Since " #since "; use " #replacement)]]) + #elif \ + ZPL_HAS_EXTENSION(attribute_deprecated_with_message) || \ + ZPL_GCC_VERSION_CHECK(4,5,0) || \ + ZPL_INTEL_VERSION_CHECK(13,0,0) || \ + ZPL_ARM_VERSION_CHECK(5,6,0) || \ + ZPL_SUNPRO_VERSION_CHECK(5,13,0) || \ + ZPL_PGI_VERSION_CHECK(17,10,0) || \ + ZPL_TI_VERSION_CHECK(18,1,0) || \ + ZPL_TI_ARMCL_VERSION_CHECK(18,1,0) || \ + ZPL_TI_CL6X_VERSION_CHECK(8,3,0) || \ + ZPL_TI_CL7X_VERSION_CHECK(1,2,0) || \ + ZPL_TI_CLPRU_VERSION_CHECK(2,3,0) + # define ZPL_DEPRECATED(since) __attribute__((__deprecated__("Since " #since))) + # define ZPL_DEPRECATED_FOR(since, replacement) __attribute__((__deprecated__("Since " #since "; use " #replacement))) + #elif \ + ZPL_HAS_ATTRIBUTE(deprecated) || \ + ZPL_GCC_VERSION_CHECK(3,1,0) || \ + ZPL_ARM_VERSION_CHECK(4,1,0) || \ + ZPL_TI_VERSION_CHECK(15,12,0) || \ + (ZPL_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + ZPL_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (ZPL_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + ZPL_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (ZPL_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + ZPL_TI_CL430_VERSION_CHECK(4,3,0) || \ + (ZPL_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + ZPL_TI_CL6X_VERSION_CHECK(7,5,0) || \ + ZPL_TI_CL7X_VERSION_CHECK(1,2,0) || \ + ZPL_TI_CLPRU_VERSION_CHECK(2,1,0) + # define ZPL_DEPRECATED(since) __attribute__((__deprecated__)) + # define ZPL_DEPRECATED_FOR(since, replacement) __attribute__((__deprecated__)) + #elif ZPL_MSVC_VERSION_CHECK(14,0,0) + # define ZPL_DEPRECATED(since) __declspec(deprecated("Since " # since)) + # define ZPL_DEPRECATED_FOR(since, replacement) __declspec(deprecated("Since " #since "; use " #replacement)) + #elif \ + ZPL_MSVC_VERSION_CHECK(13,10,0) || \ + ZPL_PELLES_VERSION_CHECK(6,50,0) + # define ZPL_DEPRECATED(since) __declspec(deprecated) + # define ZPL_DEPRECATED_FOR(since, replacement) __declspec(deprecated) + #elif ZPL_IAR_VERSION_CHECK(8,0,0) + # define ZPL_DEPRECATED(since) _Pragma("deprecated") + # define ZPL_DEPRECATED_FOR(since, replacement) _Pragma("deprecated") + #else + # define ZPL_DEPRECATED(since) + # define ZPL_DEPRECATED_FOR(since, replacement) + #endif + + #if defined(ZPL_UNAVAILABLE) + # undef ZPL_UNAVAILABLE + #endif + #if \ + ZPL_HAS_ATTRIBUTE(warning) || \ + ZPL_GCC_VERSION_CHECK(4,3,0) || \ + ZPL_INTEL_VERSION_CHECK(13,0,0) + # define ZPL_UNAVAILABLE(available_since) __attribute__((__warning__("Not available until " #available_since))) + #else + # define ZPL_UNAVAILABLE(available_since) + #endif + + #if defined(ZPL_WARN_UNUSED_RESULT) + # undef ZPL_WARN_UNUSED_RESULT + #endif + #if defined(ZPL_WARN_UNUSED_RESULT_MSG) + # undef ZPL_WARN_UNUSED_RESULT_MSG + #endif + #if (ZPL_HAS_CPP_ATTRIBUTE(nodiscard) >= 201907L) + # define ZPL_WARN_UNUSED_RESULT ZPL_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard]]) + # define ZPL_WARN_UNUSED_RESULT_MSG(msg) ZPL_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard(msg)]]) + #elif ZPL_HAS_CPP_ATTRIBUTE(nodiscard) + # define ZPL_WARN_UNUSED_RESULT ZPL_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard]]) + # define ZPL_WARN_UNUSED_RESULT_MSG(msg) ZPL_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard]]) + #elif \ + ZPL_HAS_ATTRIBUTE(warn_unused_result) || \ + ZPL_GCC_VERSION_CHECK(3,4,0) || \ + ZPL_INTEL_VERSION_CHECK(13,0,0) || \ + ZPL_TI_VERSION_CHECK(15,12,0) || \ + (ZPL_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + ZPL_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (ZPL_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + ZPL_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (ZPL_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + ZPL_TI_CL430_VERSION_CHECK(4,3,0) || \ + (ZPL_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + ZPL_TI_CL6X_VERSION_CHECK(7,5,0) || \ + ZPL_TI_CL7X_VERSION_CHECK(1,2,0) || \ + ZPL_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + (ZPL_SUNPRO_VERSION_CHECK(5,15,0) && defined(__cplusplus)) || \ + ZPL_PGI_VERSION_CHECK(17,10,0) + # define ZPL_WARN_UNUSED_RESULT __attribute__((__warn_unused_result__)) + # define ZPL_WARN_UNUSED_RESULT_MSG(msg) __attribute__((__warn_unused_result__)) + #elif defined(_Check_return_) /* SAL */ + # define ZPL_WARN_UNUSED_RESULT _Check_return_ + # define ZPL_WARN_UNUSED_RESULT_MSG(msg) _Check_return_ + #else + # define ZPL_WARN_UNUSED_RESULT + # define ZPL_WARN_UNUSED_RESULT_MSG(msg) + #endif + + #if defined(ZPL_SENTINEL) + # undef ZPL_SENTINEL + #endif + #if \ + ZPL_HAS_ATTRIBUTE(sentinel) || \ + ZPL_GCC_VERSION_CHECK(4,0,0) || \ + ZPL_INTEL_VERSION_CHECK(13,0,0) || \ + ZPL_ARM_VERSION_CHECK(5,4,0) + # define ZPL_SENTINEL(position) __attribute__((__sentinel__(position))) + #else + # define ZPL_SENTINEL(position) + #endif + + #if defined(ZPL_NO_RETURN) + # undef ZPL_NO_RETURN + #endif + #if ZPL_IAR_VERSION_CHECK(8,0,0) + # define ZPL_NO_RETURN __noreturn + #elif ZPL_INTEL_VERSION_CHECK(13,0,0) + # define ZPL_NO_RETURN __attribute__((__noreturn__)) + #elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L + # define ZPL_NO_RETURN _Noreturn + #elif defined(__cplusplus) && (__cplusplus >= 201103L) + # define ZPL_NO_RETURN ZPL_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[noreturn]]) + #elif \ + ZPL_HAS_ATTRIBUTE(noreturn) || \ + ZPL_GCC_VERSION_CHECK(3,2,0) || \ + ZPL_SUNPRO_VERSION_CHECK(5,11,0) || \ + ZPL_ARM_VERSION_CHECK(4,1,0) || \ + ZPL_IBM_VERSION_CHECK(10,1,0) || \ + ZPL_TI_VERSION_CHECK(15,12,0) || \ + (ZPL_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + ZPL_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (ZPL_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + ZPL_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (ZPL_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + ZPL_TI_CL430_VERSION_CHECK(4,3,0) || \ + (ZPL_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + ZPL_TI_CL6X_VERSION_CHECK(7,5,0) || \ + ZPL_TI_CL7X_VERSION_CHECK(1,2,0) || \ + ZPL_TI_CLPRU_VERSION_CHECK(2,1,0) + # define ZPL_NO_RETURN __attribute__((__noreturn__)) + #elif ZPL_SUNPRO_VERSION_CHECK(5,10,0) + # define ZPL_NO_RETURN _Pragma("does_not_return") + #elif ZPL_MSVC_VERSION_CHECK(13,10,0) + # define ZPL_NO_RETURN __declspec(noreturn) + #elif ZPL_TI_CL6X_VERSION_CHECK(6,0,0) && defined(__cplusplus) + # define ZPL_NO_RETURN _Pragma("FUNC_NEVER_RETURNS;") + #elif ZPL_COMPCERT_VERSION_CHECK(3,2,0) + # define ZPL_NO_RETURN __attribute((noreturn)) + #elif ZPL_PELLES_VERSION_CHECK(9,0,0) + # define ZPL_NO_RETURN __declspec(noreturn) + #else + # define ZPL_NO_RETURN + #endif + + #if defined(ZPL_NO_ESCAPE) + # undef ZPL_NO_ESCAPE + #endif + #if ZPL_HAS_ATTRIBUTE(noescape) + # define ZPL_NO_ESCAPE __attribute__((__noescape__)) + #else + # define ZPL_NO_ESCAPE + #endif + + #if defined(ZPL_UNREACHABLE) + # undef ZPL_UNREACHABLE + #endif + #if defined(ZPL_UNREACHABLE_RETURN) + # undef ZPL_UNREACHABLE_RETURN + #endif + #if defined(ZPL_ASSUME) + # undef ZPL_ASSUME + #endif + #if \ + ZPL_MSVC_VERSION_CHECK(13,10,0) || \ + ZPL_INTEL_VERSION_CHECK(13,0,0) + # define ZPL_ASSUME(expr) __assume(expr) + #elif ZPL_HAS_BUILTIN(__builtin_assume) + # define ZPL_ASSUME(expr) __builtin_assume(expr) + #elif \ + ZPL_TI_CL2000_VERSION_CHECK(6,2,0) || \ + ZPL_TI_CL6X_VERSION_CHECK(4,0,0) + # if defined(__cplusplus) + # define ZPL_ASSUME(expr) std::_nassert(expr) + # else + # define ZPL_ASSUME(expr) _nassert(expr) + # endif + #endif + #if \ + (ZPL_HAS_BUILTIN(__builtin_unreachable) && (!defined(ZPL_ARM_VERSION))) || \ + ZPL_GCC_VERSION_CHECK(4,5,0) || \ + ZPL_PGI_VERSION_CHECK(18,10,0) || \ + ZPL_INTEL_VERSION_CHECK(13,0,0) || \ + ZPL_IBM_VERSION_CHECK(13,1,5) + # define ZPL_UNREACHABLE() __builtin_unreachable() + #elif defined(ZPL_ASSUME) + # define ZPL_UNREACHABLE() ZPL_ASSUME(0) + #endif + #if !defined(ZPL_ASSUME) + # if defined(ZPL_UNREACHABLE) + # define ZPL_ASSUME(expr) ZPL_STATIC_CAST(void, ((expr) ? 1 : (ZPL_UNREACHABLE(), 1))) + # else + # define ZPL_ASSUME(expr) ZPL_STATIC_CAST(void, expr) + # endif + #endif + #if defined(ZPL_UNREACHABLE) + # if \ + ZPL_TI_CL2000_VERSION_CHECK(6,2,0) || \ + ZPL_TI_CL6X_VERSION_CHECK(4,0,0) + # define ZPL_UNREACHABLE_RETURN(value) return (ZPL_STATIC_CAST(void, ZPL_ASSUME(0)), (value)) + # else + # define ZPL_UNREACHABLE_RETURN(value) ZPL_UNREACHABLE() + # endif + #else + # define ZPL_UNREACHABLE_RETURN(value) return (value) + #endif + #if !defined(ZPL_UNREACHABLE) + # define ZPL_UNREACHABLE() ZPL_ASSUME(0) + #endif + + ZPL_DIAGNOSTIC_PUSH + #if ZPL_HAS_WARNING("-Wpedantic") + # pragma clang diagnostic ignored "-Wpedantic" + #endif + #if ZPL_HAS_WARNING("-Wc++98-compat-pedantic") && defined(__cplusplus) + # pragma clang diagnostic ignored "-Wc++98-compat-pedantic" + #endif + #if ZPL_GCC_HAS_WARNING("-Wvariadic-macros",4,0,0) + # if defined(__clang__) + # pragma clang diagnostic ignored "-Wvariadic-macros" + # elif defined(ZPL_GCC_VERSION) + # pragma GCC diagnostic ignored "-Wvariadic-macros" + # endif + #endif + #if defined(ZPL_NON_NULL) + # undef ZPL_NON_NULL + #endif + #if \ + ZPL_HAS_ATTRIBUTE(nonnull) || \ + ZPL_GCC_VERSION_CHECK(3,3,0) || \ + ZPL_INTEL_VERSION_CHECK(13,0,0) || \ + ZPL_ARM_VERSION_CHECK(4,1,0) + # define ZPL_NON_NULL(...) __attribute__((__nonnull__(__VA_ARGS__))) + #else + # define ZPL_NON_NULL(...) + #endif + ZPL_DIAGNOSTIC_POP + + #if defined(ZPL_PRINTF_FORMAT) + # undef ZPL_PRINTF_FORMAT + #endif + #if defined(__MINGW32__) && ZPL_GCC_HAS_ATTRIBUTE(format,4,4,0) && !defined(__USE_MINGW_ANSI_STDIO) + # define ZPL_PRINTF_FORMAT(string_idx,first_to_check) __attribute__((__format__(ms_printf, string_idx, first_to_check))) + #elif defined(__MINGW32__) && ZPL_GCC_HAS_ATTRIBUTE(format,4,4,0) && defined(__USE_MINGW_ANSI_STDIO) + # define ZPL_PRINTF_FORMAT(string_idx,first_to_check) __attribute__((__format__(gnu_printf, string_idx, first_to_check))) + #elif \ + ZPL_HAS_ATTRIBUTE(format) || \ + ZPL_GCC_VERSION_CHECK(3,1,0) || \ + ZPL_INTEL_VERSION_CHECK(13,0,0) || \ + ZPL_ARM_VERSION_CHECK(5,6,0) || \ + ZPL_IBM_VERSION_CHECK(10,1,0) || \ + ZPL_TI_VERSION_CHECK(15,12,0) || \ + (ZPL_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + ZPL_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (ZPL_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + ZPL_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (ZPL_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + ZPL_TI_CL430_VERSION_CHECK(4,3,0) || \ + (ZPL_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + ZPL_TI_CL6X_VERSION_CHECK(7,5,0) || \ + ZPL_TI_CL7X_VERSION_CHECK(1,2,0) || \ + ZPL_TI_CLPRU_VERSION_CHECK(2,1,0) + # define ZPL_PRINTF_FORMAT(string_idx,first_to_check) __attribute__((__format__(__printf__, string_idx, first_to_check))) + #elif ZPL_PELLES_VERSION_CHECK(6,0,0) + # define ZPL_PRINTF_FORMAT(string_idx,first_to_check) __declspec(vaformat(printf,string_idx,first_to_check)) + #else + # define ZPL_PRINTF_FORMAT(string_idx,first_to_check) + #endif + + #if defined(ZPL_CONSTEXPR) + # undef ZPL_CONSTEXPR + #endif + #if defined(__cplusplus) + # if __cplusplus >= 201103L + # define ZPL_CONSTEXPR ZPL_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(constexpr) + # endif + #endif + #if !defined(ZPL_CONSTEXPR) + # define ZPL_CONSTEXPR + #endif + + #if defined(ZPL_PREDICT) + # undef ZPL_PREDICT + #endif + #if defined(ZPL_LIKELY) + # undef ZPL_LIKELY + #endif + #if defined(ZPL_UNLIKELY) + # undef ZPL_UNLIKELY + #endif + #if defined(ZPL_UNPREDICTABLE) + # undef ZPL_UNPREDICTABLE + #endif + #if ZPL_HAS_BUILTIN(__builtin_unpredictable) + # define ZPL_UNPREDICTABLE(expr) __builtin_unpredictable((expr)) + #endif + #if \ + ZPL_HAS_BUILTIN(__builtin_expect_with_probability) || \ + ZPL_GCC_VERSION_CHECK(9,0,0) + # define ZPL_PREDICT(expr, value, probability) __builtin_expect_with_probability( (expr), (value), (probability)) + # define ZPL_PREDICT_TRUE(expr, probability) __builtin_expect_with_probability(!!(expr), 1 , (probability)) + # define ZPL_PREDICT_FALSE(expr, probability) __builtin_expect_with_probability(!!(expr), 0 , (probability)) + # define ZPL_LIKELY(expr) __builtin_expect (!!(expr), 1 ) + # define ZPL_UNLIKELY(expr) __builtin_expect (!!(expr), 0 ) + #elif \ + ZPL_HAS_BUILTIN(__builtin_expect) || \ + ZPL_GCC_VERSION_CHECK(3,0,0) || \ + ZPL_INTEL_VERSION_CHECK(13,0,0) || \ + (ZPL_SUNPRO_VERSION_CHECK(5,15,0) && defined(__cplusplus)) || \ + ZPL_ARM_VERSION_CHECK(4,1,0) || \ + ZPL_IBM_VERSION_CHECK(10,1,0) || \ + ZPL_TI_VERSION_CHECK(15,12,0) || \ + ZPL_TI_ARMCL_VERSION_CHECK(4,7,0) || \ + ZPL_TI_CL430_VERSION_CHECK(3,1,0) || \ + ZPL_TI_CL2000_VERSION_CHECK(6,1,0) || \ + ZPL_TI_CL6X_VERSION_CHECK(6,1,0) || \ + ZPL_TI_CL7X_VERSION_CHECK(1,2,0) || \ + ZPL_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + ZPL_TINYC_VERSION_CHECK(0,9,27) || \ + ZPL_CRAY_VERSION_CHECK(8,1,0) + # define ZPL_PREDICT(expr, expected, probability) \ + (((probability) >= 0.9) ? __builtin_expect((expr), (expected)) : (ZPL_STATIC_CAST(void, expected), (expr))) + # define ZPL_PREDICT_TRUE(expr, probability) \ + (__extension__ ({ \ + double zpl_probability_ = (probability); \ + ((zpl_probability_ >= 0.9) ? __builtin_expect(!!(expr), 1) : ((zpl_probability_ <= 0.1) ? __builtin_expect(!!(expr), 0) : !!(expr))); \ + })) + # define ZPL_PREDICT_FALSE(expr, probability) \ + (__extension__ ({ \ + double zpl_probability_ = (probability); \ + ((zpl_probability_ >= 0.9) ? __builtin_expect(!!(expr), 0) : ((zpl_probability_ <= 0.1) ? __builtin_expect(!!(expr), 1) : !!(expr))); \ + })) + # define ZPL_LIKELY(expr) __builtin_expect(!!(expr), 1) + # define ZPL_UNLIKELY(expr) __builtin_expect(!!(expr), 0) + #else + # define ZPL_PREDICT(expr, expected, probability) (ZPL_STATIC_CAST(void, expected), (expr)) + # define ZPL_PREDICT_TRUE(expr, probability) (!!(expr)) + # define ZPL_PREDICT_FALSE(expr, probability) (!!(expr)) + # define ZPL_LIKELY(expr) (!!(expr)) + # define ZPL_UNLIKELY(expr) (!!(expr)) + #endif + #if !defined(ZPL_UNPREDICTABLE) + # define ZPL_UNPREDICTABLE(expr) ZPL_PREDICT(expr, 1, 0.5) + #endif + + #if defined(ZPL_MALLOC) + # undef ZPL_MALLOC + #endif + #if \ + ZPL_HAS_ATTRIBUTE(malloc) || \ + ZPL_GCC_VERSION_CHECK(3,1,0) || \ + ZPL_INTEL_VERSION_CHECK(13,0,0) || \ + ZPL_SUNPRO_VERSION_CHECK(5,11,0) || \ + ZPL_ARM_VERSION_CHECK(4,1,0) || \ + ZPL_IBM_VERSION_CHECK(12,1,0) || \ + ZPL_TI_VERSION_CHECK(15,12,0) || \ + (ZPL_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + ZPL_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (ZPL_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + ZPL_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (ZPL_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + ZPL_TI_CL430_VERSION_CHECK(4,3,0) || \ + (ZPL_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + ZPL_TI_CL6X_VERSION_CHECK(7,5,0) || \ + ZPL_TI_CL7X_VERSION_CHECK(1,2,0) || \ + ZPL_TI_CLPRU_VERSION_CHECK(2,1,0) + # define ZPL_MALLOC __attribute__((__malloc__)) + #elif ZPL_SUNPRO_VERSION_CHECK(5,10,0) + # define ZPL_MALLOC _Pragma("returns_new_memory") + #elif ZPL_MSVC_VERSION_CHECK(14, 0, 0) + # define ZPL_MALLOC __declspec(restrict) + #else + # define ZPL_MALLOC + #endif + + #if defined(ZPL_PURE) + # undef ZPL_PURE + #endif + #if \ + ZPL_HAS_ATTRIBUTE(pure) || \ + ZPL_GCC_VERSION_CHECK(2,96,0) || \ + ZPL_INTEL_VERSION_CHECK(13,0,0) || \ + ZPL_SUNPRO_VERSION_CHECK(5,11,0) || \ + ZPL_ARM_VERSION_CHECK(4,1,0) || \ + ZPL_IBM_VERSION_CHECK(10,1,0) || \ + ZPL_TI_VERSION_CHECK(15,12,0) || \ + (ZPL_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + ZPL_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (ZPL_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + ZPL_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (ZPL_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + ZPL_TI_CL430_VERSION_CHECK(4,3,0) || \ + (ZPL_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + ZPL_TI_CL6X_VERSION_CHECK(7,5,0) || \ + ZPL_TI_CL7X_VERSION_CHECK(1,2,0) || \ + ZPL_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + ZPL_PGI_VERSION_CHECK(17,10,0) + # define ZPL_PURE __attribute__((__pure__)) + #elif ZPL_SUNPRO_VERSION_CHECK(5,10,0) + # define ZPL_PURE _Pragma("does_not_write_global_data") + #elif defined(__cplusplus) && \ + ( \ + ZPL_TI_CL430_VERSION_CHECK(2,0,1) || \ + ZPL_TI_CL6X_VERSION_CHECK(4,0,0) || \ + ZPL_TI_CL7X_VERSION_CHECK(1,2,0) \ + ) + # define ZPL_PURE _Pragma("FUNC_IS_PURE;") + #else + # define ZPL_PURE + #endif + + #if defined(ZPL_CONST) + # undef ZPL_CONST + #endif + #if \ + ZPL_HAS_ATTRIBUTE(const) || \ + ZPL_GCC_VERSION_CHECK(2,5,0) || \ + ZPL_INTEL_VERSION_CHECK(13,0,0) || \ + ZPL_SUNPRO_VERSION_CHECK(5,11,0) || \ + ZPL_ARM_VERSION_CHECK(4,1,0) || \ + ZPL_IBM_VERSION_CHECK(10,1,0) || \ + ZPL_TI_VERSION_CHECK(15,12,0) || \ + (ZPL_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + ZPL_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (ZPL_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + ZPL_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (ZPL_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + ZPL_TI_CL430_VERSION_CHECK(4,3,0) || \ + (ZPL_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + ZPL_TI_CL6X_VERSION_CHECK(7,5,0) || \ + ZPL_TI_CL7X_VERSION_CHECK(1,2,0) || \ + ZPL_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + ZPL_PGI_VERSION_CHECK(17,10,0) + # define ZPL_CONST __attribute__((__const__)) + #elif \ + ZPL_SUNPRO_VERSION_CHECK(5,10,0) + # define ZPL_CONST _Pragma("no_side_effect") + #else + # define ZPL_CONST ZPL_PURE + #endif + + #if defined(ZPL_RESTRICT) + # undef ZPL_RESTRICT + #endif + #if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && !defined(__cplusplus) + # define ZPL_RESTRICT restrict + #elif \ + ZPL_GCC_VERSION_CHECK(3,1,0) || \ + ZPL_MSVC_VERSION_CHECK(14,0,0) || \ + ZPL_INTEL_VERSION_CHECK(13,0,0) || \ + ZPL_ARM_VERSION_CHECK(4,1,0) || \ + ZPL_IBM_VERSION_CHECK(10,1,0) || \ + ZPL_PGI_VERSION_CHECK(17,10,0) || \ + ZPL_TI_CL430_VERSION_CHECK(4,3,0) || \ + ZPL_TI_CL2000_VERSION_CHECK(6,2,4) || \ + ZPL_TI_CL6X_VERSION_CHECK(8,1,0) || \ + ZPL_TI_CL7X_VERSION_CHECK(1,2,0) || \ + (ZPL_SUNPRO_VERSION_CHECK(5,14,0) && defined(__cplusplus)) || \ + ZPL_IAR_VERSION_CHECK(8,0,0) || \ + defined(__clang__) + # define ZPL_RESTRICT __restrict + #elif ZPL_SUNPRO_VERSION_CHECK(5,3,0) && !defined(__cplusplus) + # define ZPL_RESTRICT _Restrict + #else + # define ZPL_RESTRICT + #endif + + #if defined(ZPL_INLINE) + # undef ZPL_INLINE + #endif + #if \ + (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) || \ + (defined(__cplusplus) && (__cplusplus >= 199711L)) + # define ZPL_INLINE inline + #elif \ + defined(ZPL_GCC_VERSION) || \ + ZPL_ARM_VERSION_CHECK(6,2,0) + # define ZPL_INLINE __inline__ + #elif \ + ZPL_MSVC_VERSION_CHECK(12,0,0) || \ + ZPL_ARM_VERSION_CHECK(4,1,0) || \ + ZPL_TI_ARMCL_VERSION_CHECK(5,1,0) || \ + ZPL_TI_CL430_VERSION_CHECK(3,1,0) || \ + ZPL_TI_CL2000_VERSION_CHECK(6,2,0) || \ + ZPL_TI_CL6X_VERSION_CHECK(8,0,0) || \ + ZPL_TI_CL7X_VERSION_CHECK(1,2,0) || \ + ZPL_TI_CLPRU_VERSION_CHECK(2,1,0) + # define ZPL_INLINE __inline + #else + # define ZPL_INLINE + #endif + + #if defined(ZPL_ALWAYS_INLINE) + # undef ZPL_ALWAYS_INLINE + #endif + #if \ + ZPL_HAS_ATTRIBUTE(always_inline) || \ + ZPL_GCC_VERSION_CHECK(4,0,0) || \ + ZPL_INTEL_VERSION_CHECK(13,0,0) || \ + ZPL_SUNPRO_VERSION_CHECK(5,11,0) || \ + ZPL_ARM_VERSION_CHECK(4,1,0) || \ + ZPL_IBM_VERSION_CHECK(10,1,0) || \ + ZPL_TI_VERSION_CHECK(15,12,0) || \ + (ZPL_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + ZPL_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (ZPL_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + ZPL_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (ZPL_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + ZPL_TI_CL430_VERSION_CHECK(4,3,0) || \ + (ZPL_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + ZPL_TI_CL6X_VERSION_CHECK(7,5,0) || \ + ZPL_TI_CL7X_VERSION_CHECK(1,2,0) || \ + ZPL_TI_CLPRU_VERSION_CHECK(2,1,0) + # define ZPL_ALWAYS_INLINE __attribute__((__always_inline__)) ZPL_INLINE + #elif ZPL_MSVC_VERSION_CHECK(12,0,0) + # define ZPL_ALWAYS_INLINE __forceinline + #elif defined(__cplusplus) && \ + ( \ + ZPL_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + ZPL_TI_CL430_VERSION_CHECK(4,3,0) || \ + ZPL_TI_CL2000_VERSION_CHECK(6,4,0) || \ + ZPL_TI_CL6X_VERSION_CHECK(6,1,0) || \ + ZPL_TI_CL7X_VERSION_CHECK(1,2,0) || \ + ZPL_TI_CLPRU_VERSION_CHECK(2,1,0) \ + ) + # define ZPL_ALWAYS_INLINE _Pragma("FUNC_ALWAYS_INLINE;") + #elif ZPL_IAR_VERSION_CHECK(8,0,0) + # define ZPL_ALWAYS_INLINE _Pragma("inline=forced") + #else + # define ZPL_ALWAYS_INLINE ZPL_INLINE + #endif + + #undef ZPL_ALWAYS_INLINE + #define ZPL_ALWAYS_INLINE ZPL_INLINE + + #if defined(ZPL_NEVER_INLINE) + # undef ZPL_NEVER_INLINE + #endif + #if \ + ZPL_HAS_ATTRIBUTE(noinline) || \ + ZPL_GCC_VERSION_CHECK(4,0,0) || \ + ZPL_INTEL_VERSION_CHECK(13,0,0) || \ + ZPL_SUNPRO_VERSION_CHECK(5,11,0) || \ + ZPL_ARM_VERSION_CHECK(4,1,0) || \ + ZPL_IBM_VERSION_CHECK(10,1,0) || \ + ZPL_TI_VERSION_CHECK(15,12,0) || \ + (ZPL_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + ZPL_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (ZPL_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + ZPL_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (ZPL_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + ZPL_TI_CL430_VERSION_CHECK(4,3,0) || \ + (ZPL_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + ZPL_TI_CL6X_VERSION_CHECK(7,5,0) || \ + ZPL_TI_CL7X_VERSION_CHECK(1,2,0) || \ + ZPL_TI_CLPRU_VERSION_CHECK(2,1,0) + # define ZPL_NEVER_INLINE __attribute__((__noinline__)) + #elif ZPL_MSVC_VERSION_CHECK(13,10,0) + # define ZPL_NEVER_INLINE __declspec(noinline) + #elif ZPL_PGI_VERSION_CHECK(10,2,0) + # define ZPL_NEVER_INLINE _Pragma("noinline") + #elif ZPL_TI_CL6X_VERSION_CHECK(6,0,0) && defined(__cplusplus) + # define ZPL_NEVER_INLINE _Pragma("FUNC_CANNOT_INLINE;") + #elif ZPL_IAR_VERSION_CHECK(8,0,0) + # define ZPL_NEVER_INLINE _Pragma("inline=never") + #elif ZPL_COMPCERT_VERSION_CHECK(3,2,0) + # define ZPL_NEVER_INLINE __attribute((noinline)) + #elif ZPL_PELLES_VERSION_CHECK(9,0,0) + # define ZPL_NEVER_INLINE __declspec(noinline) + #else + # define ZPL_NEVER_INLINE + #endif + + #if defined(ZPL_PRIVATE) + # undef ZPL_PRIVATE + #endif + #if defined(ZPL_PUBLIC) + # undef ZPL_PUBLIC + #endif + #if defined(ZPL_IMPORT) + # undef ZPL_IMPORT + #endif + #if defined(_WIN32) || defined(__CYGWIN__) + # define ZPL_PRIVATE + # define ZPL_PUBLIC __declspec(dllexport) + # define ZPL_IMPORT __declspec(dllimport) + #else + # if \ + ZPL_HAS_ATTRIBUTE(visibility) || \ + ZPL_GCC_VERSION_CHECK(3,3,0) || \ + ZPL_SUNPRO_VERSION_CHECK(5,11,0) || \ + ZPL_INTEL_VERSION_CHECK(13,0,0) || \ + ZPL_ARM_VERSION_CHECK(4,1,0) || \ + ZPL_IBM_VERSION_CHECK(13,1,0) || \ + ( \ + defined(__TI_EABI__) && \ + ( \ + (ZPL_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + ZPL_TI_CL6X_VERSION_CHECK(7,5,0) \ + ) \ + ) + # define ZPL_PRIVATE __attribute__((__visibility__("hidden"))) + # define ZPL_PUBLIC __attribute__((__visibility__("default"))) + # else + # define ZPL_PRIVATE + # define ZPL_PUBLIC + # endif + # define ZPL_IMPORT extern + #endif + + #if defined(ZPL_NO_THROW) + # undef ZPL_NO_THROW + #endif + #if \ + ZPL_HAS_ATTRIBUTE(nothrow) || \ + ZPL_GCC_VERSION_CHECK(3,3,0) || \ + ZPL_INTEL_VERSION_CHECK(13,0,0) + # define ZPL_NO_THROW __attribute__((__nothrow__)) + #elif \ + ZPL_MSVC_VERSION_CHECK(13,1,0) || \ + ZPL_ARM_VERSION_CHECK(4,1,0) + # define ZPL_NO_THROW __declspec(nothrow) + #else + # define ZPL_NO_THROW + #endif + + #if defined(ZPL_FALL_THROUGH) + # undef ZPL_FALL_THROUGH + #endif + #if ZPL_GNUC_HAS_ATTRIBUTE(fallthrough,7,0,0) && !defined(ZPL_PGI_VERSION) + # define ZPL_FALL_THROUGH __attribute__((__fallthrough__)) + #elif ZPL_HAS_CPP_ATTRIBUTE_NS(clang,fallthrough) + # define ZPL_FALL_THROUGH ZPL_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[clang::fallthrough]]) + #elif ZPL_HAS_CPP_ATTRIBUTE(fallthrough) + # define ZPL_FALL_THROUGH ZPL_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[fallthrough]]) + #elif defined(__fallthrough) /* SAL */ + # define ZPL_FALL_THROUGH __fallthrough + #else + # define ZPL_FALL_THROUGH + #endif + + #if defined(ZPL_RETURNS_NON_NULL) + # undef ZPL_RETURNS_NON_NULL + #endif + #if \ + ZPL_HAS_ATTRIBUTE(returns_nonnull) || \ + ZPL_GCC_VERSION_CHECK(4,9,0) + # define ZPL_RETURNS_NON_NULL __attribute__((__returns_nonnull__)) + #elif defined(_Ret_notnull_) /* SAL */ + # define ZPL_RETURNS_NON_NULL _Ret_notnull_ + #else + # define ZPL_RETURNS_NON_NULL + #endif + + #if defined(ZPL_ARRAY_PARAM) + # undef ZPL_ARRAY_PARAM + #endif + #if \ + defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && \ + !defined(__STDC_NO_VLA__) && \ + !defined(__cplusplus) && \ + !defined(ZPL_PGI_VERSION) && \ + !defined(ZPL_TINYC_VERSION) + # define ZPL_ARRAY_PARAM(name) (name) + #else + # define ZPL_ARRAY_PARAM(name) + #endif + + #if defined(ZPL_IS_CONSTANT) + # undef ZPL_IS_CONSTANT + #endif + #if defined(ZPL_REQUIRE_CONSTEXPR) + # undef ZPL_REQUIRE_CONSTEXPR + #endif + /* ZPL_IS_CONSTEXPR_ is for + ZPL INTERNAL USE ONLY. API subject to change without notice. */ + #if defined(ZPL_IS_CONSTEXPR_) + # undef ZPL_IS_CONSTEXPR_ + #endif + #if \ + ZPL_HAS_BUILTIN(__builtin_constant_p) || \ + ZPL_GCC_VERSION_CHECK(3,4,0) || \ + ZPL_INTEL_VERSION_CHECK(13,0,0) || \ + ZPL_TINYC_VERSION_CHECK(0,9,19) || \ + ZPL_ARM_VERSION_CHECK(4,1,0) || \ + ZPL_IBM_VERSION_CHECK(13,1,0) || \ + ZPL_TI_CL6X_VERSION_CHECK(6,1,0) || \ + (ZPL_SUNPRO_VERSION_CHECK(5,10,0) && !defined(__cplusplus)) || \ + ZPL_CRAY_VERSION_CHECK(8,1,0) + # define ZPL_IS_CONSTANT(expr) __builtin_constant_p(expr) + #endif + #if !defined(__cplusplus) + # if \ + ZPL_HAS_BUILTIN(__builtin_types_compatible_p) || \ + ZPL_GCC_VERSION_CHECK(3,4,0) || \ + ZPL_INTEL_VERSION_CHECK(13,0,0) || \ + ZPL_IBM_VERSION_CHECK(13,1,0) || \ + ZPL_CRAY_VERSION_CHECK(8,1,0) || \ + ZPL_ARM_VERSION_CHECK(5,4,0) || \ + ZPL_TINYC_VERSION_CHECK(0,9,24) + # if defined(__INTPTR_TYPE__) + # define ZPL_IS_CONSTEXPR_(expr) __builtin_types_compatible_p(__typeof__((1 ? (void*) ((__INTPTR_TYPE__) ((expr) * 0)) : (int*) 0)), int*) + # else + # include + # define ZPL_IS_CONSTEXPR_(expr) __builtin_types_compatible_p(__typeof__((1 ? (void*) ((intptr_t) ((expr) * 0)) : (int*) 0)), int*) + # endif + # elif \ + ( \ + defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) && \ + !defined(ZPL_SUNPRO_VERSION) && \ + !defined(ZPL_PGI_VERSION) && \ + !defined(ZPL_IAR_VERSION)) || \ + ZPL_HAS_EXTENSION(c_generic_selections) || \ + ZPL_GCC_VERSION_CHECK(4,9,0) || \ + ZPL_INTEL_VERSION_CHECK(17,0,0) || \ + ZPL_IBM_VERSION_CHECK(12,1,0) || \ + ZPL_ARM_VERSION_CHECK(5,3,0) + # if defined(__INTPTR_TYPE__) + # define ZPL_IS_CONSTEXPR_(expr) _Generic((1 ? (void*) ((__INTPTR_TYPE__) ((expr) * 0)) : (int*) 0), int*: 1, void*: 0) + # else + # include + # define ZPL_IS_CONSTEXPR_(expr) _Generic((1 ? (void*) ((intptr_t) * 0) : (int*) 0), int*: 1, void*: 0) + # endif + # elif \ + defined(ZPL_GCC_VERSION) || \ + defined(ZPL_INTEL_VERSION) || \ + defined(ZPL_TINYC_VERSION) || \ + defined(ZPL_TI_ARMCL_VERSION) || \ + ZPL_TI_CL430_VERSION_CHECK(18,12,0) || \ + defined(ZPL_TI_CL2000_VERSION) || \ + defined(ZPL_TI_CL6X_VERSION) || \ + defined(ZPL_TI_CL7X_VERSION) || \ + defined(ZPL_TI_CLPRU_VERSION) || \ + defined(__clang__) + # define ZPL_IS_CONSTEXPR_(expr) ( \ + sizeof(void) != \ + sizeof(*( \ + 1 ? \ + ((void*) ((expr) * 0L) ) : \ + ((struct { char v[sizeof(void) * 2]; } *) 1) \ + ) \ + ) \ + ) + # endif + #endif + #if defined(ZPL_IS_CONSTEXPR_) + # if !defined(ZPL_IS_CONSTANT) + # define ZPL_IS_CONSTANT(expr) ZPL_IS_CONSTEXPR_(expr) + # endif + # define ZPL_REQUIRE_CONSTEXPR(expr) (ZPL_IS_CONSTEXPR_(expr) ? (expr) : (-1)) + #else + # if !defined(ZPL_IS_CONSTANT) + # define ZPL_IS_CONSTANT(expr) (0) + # endif + # define ZPL_REQUIRE_CONSTEXPR(expr) (expr) + #endif + + #if defined(ZPL_BEGIN_C_DECLS) + # undef ZPL_BEGIN_C_DECLS + #endif + #if defined(ZPL_END_C_DECLS) + # undef ZPL_END_C_DECLS + #endif + #if defined(ZPL_C_DECL) + # undef ZPL_C_DECL + #endif + #if defined(__cplusplus) + # define ZPL_BEGIN_C_DECLS extern "C" { + # define ZPL_END_C_DECLS } + # define ZPL_C_DECL extern "C" + #else + # define ZPL_BEGIN_C_DECLS + # define ZPL_END_C_DECLS + # define ZPL_C_DECL + #endif + + #if defined(ZPL_STATIC_ASSERT) + # undef ZPL_STATIC_ASSERT + #endif + #if \ + !defined(__cplusplus) && ( \ + (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L)) || \ + ZPL_HAS_FEATURE(c_static_assert) || \ + ZPL_GCC_VERSION_CHECK(6,0,0) || \ + ZPL_INTEL_VERSION_CHECK(13,0,0) || \ + defined(_Static_assert) \ + ) + # define ZPL_STATIC_ASSERT(expr, message) _Static_assert(expr, message) + #elif \ + (defined(__cplusplus) && (__cplusplus >= 201103L)) || \ + ZPL_MSVC_VERSION_CHECK(16,0,0) + # define ZPL_STATIC_ASSERT(expr, message) ZPL_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(static_assert(expr, message)) + #else + # define ZPL_STATIC_ASSERT3(cond, msg) typedef char static_assertion_##msg[(!!(cond)) * 2 - 1] + # define ZPL_STATIC_ASSERT2(cond, line) ZPL_STATIC_ASSERT3(cond, static_assertion_at_line_##line) + # define ZPL_STATIC_ASSERT1(cond, line) ZPL_STATIC_ASSERT2(cond, line) + # define ZPL_STATIC_ASSERT(cond, unused) ZPL_STATIC_ASSERT1(cond, __LINE__) + #endif + + #if defined(ZPL_NULL) + # undef ZPL_NULL + #endif + #if defined(__cplusplus) + # if __cplusplus >= 201103L + # define ZPL_NULL ZPL_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(nullptr) + # elif defined(NULL) + # define ZPL_NULL NULL + # else + # define ZPL_NULL ZPL_STATIC_CAST(void*, 0) + # endif + #elif defined(NULL) + # define ZPL_NULL NULL + #else + # define ZPL_NULL ((void*) 0) + #endif + + #if defined(ZPL_MESSAGE) + # undef ZPL_MESSAGE + #endif + #if ZPL_HAS_WARNING("-Wunknown-pragmas") + # define ZPL_MESSAGE(msg) \ + ZPL_DIAGNOSTIC_PUSH \ + ZPL_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS \ + ZPL_PRAGMA(message msg) \ + ZPL_DIAGNOSTIC_POP + #elif \ + ZPL_GCC_VERSION_CHECK(4,4,0) || \ + ZPL_INTEL_VERSION_CHECK(13,0,0) + # define ZPL_MESSAGE(msg) ZPL_PRAGMA(message msg) + #elif ZPL_CRAY_VERSION_CHECK(5,0,0) + # define ZPL_MESSAGE(msg) ZPL_PRAGMA(_CRI message msg) + #elif ZPL_IAR_VERSION_CHECK(8,0,0) + # define ZPL_MESSAGE(msg) ZPL_PRAGMA(message(msg)) + #elif ZPL_PELLES_VERSION_CHECK(2,0,0) + # define ZPL_MESSAGE(msg) ZPL_PRAGMA(message(msg)) + #else + # define ZPL_MESSAGE(msg) + #endif + + #if defined(ZPL_WARNING) + # undef ZPL_WARNING + #endif + #if ZPL_HAS_WARNING("-Wunknown-pragmas") + # define ZPL_WARNING(msg) \ + ZPL_DIAGNOSTIC_PUSH \ + ZPL_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS \ + ZPL_PRAGMA(clang warning msg) \ + ZPL_DIAGNOSTIC_POP + #elif \ + ZPL_GCC_VERSION_CHECK(4,8,0) || \ + ZPL_PGI_VERSION_CHECK(18,4,0) || \ + ZPL_INTEL_VERSION_CHECK(13,0,0) + # define ZPL_WARNING(msg) ZPL_PRAGMA(GCC warning msg) + #elif ZPL_MSVC_VERSION_CHECK(15,0,0) + # define ZPL_WARNING(msg) ZPL_PRAGMA(message(msg)) + #else + # define ZPL_WARNING(msg) ZPL_MESSAGE(msg) + #endif + + #if defined(ZPL_REQUIRE) + # undef ZPL_REQUIRE + #endif + #if defined(ZPL_REQUIRE_MSG) + # undef ZPL_REQUIRE_MSG + #endif + #if ZPL_HAS_ATTRIBUTE(diagnose_if) + # if ZPL_HAS_WARNING("-Wgcc-compat") + # define ZPL_REQUIRE(expr) \ + ZPL_DIAGNOSTIC_PUSH \ + _Pragma("clang diagnostic ignored \"-Wgcc-compat\"") \ + __attribute__((diagnose_if(!(expr), #expr, "error"))) \ + ZPL_DIAGNOSTIC_POP + # define ZPL_REQUIRE_MSG(expr,msg) \ + ZPL_DIAGNOSTIC_PUSH \ + _Pragma("clang diagnostic ignored \"-Wgcc-compat\"") \ + __attribute__((diagnose_if(!(expr), msg, "error"))) \ + ZPL_DIAGNOSTIC_POP + # else + # define ZPL_REQUIRE(expr) __attribute__((diagnose_if(!(expr), #expr, "error"))) + # define ZPL_REQUIRE_MSG(expr,msg) __attribute__((diagnose_if(!(expr), msg, "error"))) + # endif + #else + # define ZPL_REQUIRE(expr) + # define ZPL_REQUIRE_MSG(expr,msg) + #endif + + #if defined(ZPL_FLAGS) + # undef ZPL_FLAGS + #endif + #if ZPL_HAS_ATTRIBUTE(flag_enum) + # define ZPL_FLAGS __attribute__((__flag_enum__)) + #endif + + #if defined(ZPL_FLAGS_CAST) + # undef ZPL_FLAGS_CAST + #endif + #if ZPL_INTEL_VERSION_CHECK(19,0,0) + # define ZPL_FLAGS_CAST(T, expr) (__extension__ ({ \ + ZPL_DIAGNOSTIC_PUSH \ + _Pragma("warning(disable:188)") \ + ((T) (expr)); \ + ZPL_DIAGNOSTIC_POP \ + })) + #else + # define ZPL_FLAGS_CAST(T, expr) ZPL_STATIC_CAST(T, expr) + #endif + + #if defined(ZPL_EMPTY_BASES) + # undef ZPL_EMPTY_BASES + #endif + #if ZPL_MSVC_VERSION_CHECK(19,0,23918) && !ZPL_MSVC_VERSION_CHECK(20,0,0) + # define ZPL_EMPTY_BASES __declspec(empty_bases) + #else + # define ZPL_EMPTY_BASES + #endif + + /* Remaining macros are deprecated. */ + + #if defined(ZPL_GCC_NOT_CLANG_VERSION_CHECK) + # undef ZPL_GCC_NOT_CLANG_VERSION_CHECK + #endif + #if defined(__clang__) + # define ZPL_GCC_NOT_CLANG_VERSION_CHECK(major,minor,patch) (0) + #else + # define ZPL_GCC_NOT_CLANG_VERSION_CHECK(major,minor,patch) ZPL_GCC_VERSION_CHECK(major,minor,patch) + #endif + + #if defined(ZPL_CLANG_HAS_ATTRIBUTE) + # undef ZPL_CLANG_HAS_ATTRIBUTE + #endif + #define ZPL_CLANG_HAS_ATTRIBUTE(attribute) ZPL_HAS_ATTRIBUTE(attribute) + + #if defined(ZPL_CLANG_HAS_CPP_ATTRIBUTE) + # undef ZPL_CLANG_HAS_CPP_ATTRIBUTE + #endif + #define ZPL_CLANG_HAS_CPP_ATTRIBUTE(attribute) ZPL_HAS_CPP_ATTRIBUTE(attribute) + + #if defined(ZPL_CLANG_HAS_BUILTIN) + # undef ZPL_CLANG_HAS_BUILTIN + #endif + #define ZPL_CLANG_HAS_BUILTIN(builtin) ZPL_HAS_BUILTIN(builtin) + + #if defined(ZPL_CLANG_HAS_FEATURE) + # undef ZPL_CLANG_HAS_FEATURE + #endif + #define ZPL_CLANG_HAS_FEATURE(feature) ZPL_HAS_FEATURE(feature) + + #if defined(ZPL_CLANG_HAS_EXTENSION) + # undef ZPL_CLANG_HAS_EXTENSION + #endif + #define ZPL_CLANG_HAS_EXTENSION(extension) ZPL_HAS_EXTENSION(extension) + + #if defined(ZPL_CLANG_HAS_DECLSPEC_DECLSPEC_ATTRIBUTE) + # undef ZPL_CLANG_HAS_DECLSPEC_DECLSPEC_ATTRIBUTE + #endif + #define ZPL_CLANG_HAS_DECLSPEC_ATTRIBUTE(attribute) ZPL_HAS_DECLSPEC_ATTRIBUTE(attribute) + + #if defined(ZPL_CLANG_HAS_WARNING) + # undef ZPL_CLANG_HAS_WARNING + #endif + #define ZPL_CLANG_HAS_WARNING(warning) ZPL_HAS_WARNING(warning) + + #endif /* !defined(ZPL_HEDLEY_VERSION) || (ZPL_HEDLEY_VERSION < X) */ + +#define ZPL_VERSION ZPL_VERSION_ENCODE(ZPL_VERSION_MAJOR, ZPL_VERSION_MINOR, ZPL_VERSION_PATCH) + +#ifdef ZPL_IMPL +# ifndef ZPL_IMPLEMENTATION +# define ZPL_IMPLEMENTATION +# endif +#endif + +#if defined(__cplusplus) && !defined(ZPL_EXTERN) +# define ZPL_EXTERN extern "C" +#else +# define ZPL_EXTERN extern +#endif + +#ifndef ZPL_DEF +# if defined(ZPL_SHARED_LIB) +# ifdef ZPL_IMPLEMENTATION +# define ZPL_DEF ZPL_PUBLIC +# else +# define ZPL_DEF ZPL_IMPORT +# endif +# elif defined(ZPL_STATIC_LIB) +# ifdef ZPL_IMPLEMENTATION +# define ZPL_DEF +# else +# define ZPL_DEF ZPL_EXTERN +# endif +# elif defined(ZPL_STATIC) +# define ZPL_DEF static +# else +# define ZPL_DEF ZPL_EXTERN +# endif +#endif + +#ifndef ZPL_DEF_INLINE +# if defined(ZPL_STATIC) +# define ZPL_DEF_INLINE +# define ZPL_IMPL_INLINE +# else +# define ZPL_DEF_INLINE static +# define ZPL_IMPL_INLINE static inline +# endif +#endif + +/* builtin overrides */ +#if defined(__TINYC__) || defined(__EMSCRIPTEN__) +# if defined(ZPL_ENFORCE_THREADING) +# define ZPL_ENABLE_THREADING +# else +# define ZPL_DISABLE_THREADING +# endif +#endif + +/* Distributions */ +#ifndef ZPL_CUSTOM_MODULES + /* default distribution */ +# define ZPL_MODULE_ESSENTIALS +# define ZPL_MODULE_CORE +# define ZPL_MODULE_TIMER +# define ZPL_MODULE_HASHING +# define ZPL_MODULE_REGEX +# define ZPL_MODULE_EVENT +# define ZPL_MODULE_DLL +# define ZPL_MODULE_OPTS +# define ZPL_MODULE_PROCESS +# define ZPL_MODULE_MATH +# define ZPL_MODULE_THREADING +# define ZPL_MODULE_JOBS +# define ZPL_MODULE_PARSER +# define ZPL_MODULE_SOCKET + + /* zpl nano distribution */ +# if defined(ZPL_NANO) || defined(ZPL_PICO) +# undef ZPL_MODULE_TIMER +# undef ZPL_MODULE_HASHING +# undef ZPL_MODULE_REGEX +# undef ZPL_MODULE_EVENT +# undef ZPL_MODULE_DLL +# undef ZPL_MODULE_OPTS +# undef ZPL_MODULE_PROCESS +# undef ZPL_MODULE_MATH +# undef ZPL_MODULE_THREADING +# undef ZPL_MODULE_JOBS +# undef ZPL_MODULE_PARSER +# undef ZPL_MODULE_SOCKET +# endif + +# if defined(ZPL_PICO) +# undef ZPL_MODULE_CORE +# endif + + /* module enabling overrides */ +# if defined(ZPL_ENABLE_CORE) && !defined(ZPL_MODULE_CORE) +# define ZPL_MODULE_CORE +# endif +# if defined(ZPL_ENABLE_HASHING) && !defined(ZPL_MODULE_HASHING) +# define ZPL_MODULE_HASHING +# endif +# if defined(ZPL_ENABLE_REGEX) && !defined(ZPL_MODULE_REGEX) +# define ZPL_MODULE_REGEX +# endif +# if defined(ZPL_ENABLE_DLL) && !defined(ZPL_MODULE_DLL) +# define ZPL_MODULE_DLL +# endif +# if defined(ZPL_ENABLE_OPTS) && !defined(ZPL_MODULE_OPTS) +# define ZPL_MODULE_OPTS +# endif +# if defined(ZPL_ENABLE_PROCESS) && !defined(ZPL_MODULE_PROCESS) +# define ZPL_MODULE_PROCESS +# endif +# if defined(ZPL_ENABLE_MATH) && !defined(ZPL_MODULE_MATH) +# define ZPL_MODULE_MATH +# endif +# if defined(ZPL_ENABLE_THREADING) && !defined(ZPL_MODULE_THREADING) +# define ZPL_MODULE_THREADING +# endif +# if defined(ZPL_ENABLE_JOBS) && !defined(ZPL_MODULE_JOBS) +# ifndef ZPL_MODULE_THREADING +# define ZPL_MODULE_THREADING /* dependency */ +# endif +# define ZPL_MODULE_JOBS +# endif +# if defined(ZPL_ENABLE_PARSER) && !defined(ZPL_MODULE_PARSER) +# define ZPL_MODULE_PARSER +# endif +# if defined(ZPL_ENABLE_SOCKET) && !defined(ZPL_MODULE_SOCKET) +# define ZPL_MODULE_SOCKET +# endif + + /* module disabling overrides */ +# if defined(ZPL_DISABLE_CORE) && defined(ZPL_MODULE_CORE) +# undef ZPL_MODULE_CORE +# endif +# if defined(ZPL_DISABLE_HASHING) && defined(ZPL_MODULE_HASHING) +# undef ZPL_MODULE_HASHING +# endif +# if defined(ZPL_DISABLE_REGEX) && defined(ZPL_MODULE_REGEX) +# undef ZPL_MODULE_REGEX +# endif +# if defined(ZPL_DISABLE_DLL) && defined(ZPL_MODULE_DLL) +# undef ZPL_MODULE_DLL +# endif +# if defined(ZPL_DISABLE_OPTS) && defined(ZPL_MODULE_OPTS) +# undef ZPL_MODULE_OPTS +# endif +# if defined(ZPL_DISABLE_PROCESS) && defined(ZPL_MODULE_PROCESS) +# undef ZPL_MODULE_PROCESS +# endif +# if defined(ZPL_DISABLE_MATH) && defined(ZPL_MODULE_MATH) +# undef ZPL_MODULE_MATH +# endif +# if defined(ZPL_DISABLE_THREADING) && defined(ZPL_MODULE_THREADING) +# ifdef ZPL_MODULE_JOBS +# undef ZPL_MODULE_JOBS /* user */ +# endif +# undef ZPL_MODULE_THREADING +# endif +# if defined(ZPL_DISABLE_JOBS) && defined(ZPL_MODULE_JOBS) +# undef ZPL_MODULE_JOBS +# endif +# if defined(ZPL_DISABLE_PARSER) && defined(ZPL_MODULE_PARSER) +# undef ZPL_MODULE_PARSER +# endif +# if defined(ZPL_DISABLE_SOCKET) && defined(ZPL_MODULE_SOCKET) +# undef ZPL_MODULE_SOCKET +# endif +#endif + +#if defined(__GCC__) || defined(__GNUC__) || defined(__clang__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wunused-function" +# pragma GCC diagnostic ignored "-Wmissing-field-initializers" +# pragma GCC diagnostic ignored "-Wimplicit-fallthrough" +#endif + +#if defined(_MSC_VER) +# pragma warning(push) +# pragma warning(disable : 4201) +# pragma warning(disable : 4127) // Conditional expression is constant +#endif + +/* general purpose includes */ + + // file: header/core/system.h + + + ZPL_BEGIN_C_DECLS + + /* Platform architecture */ + + #if defined(_WIN64) || defined(__x86_64__) || defined(_M_X64) || defined(__64BIT__) || defined(__powerpc64__) || \ + defined(__ppc64__) || defined(__aarch64__) + # ifndef ZPL_ARCH_64_BIT + # define ZPL_ARCH_64_BIT 1 + # endif + #else + # ifndef ZPL_ARCH_32_BIT + # define ZPL_ARCH_32_BIT 1 + # endif + #endif + + /* Platform endiannes */ + + #ifndef ZPL_ENDIAN_ORDER + # define ZPL_ENDIAN_ORDER + # define ZPL_IS_BIG_ENDIAN (!*(zpl_u8 *)&(zpl_u16){ 1 }) + # define ZPL_IS_LITTLE_ENDIAN (!ZPL_IS_BIG_ENDIAN) + #endif + + /* Platform OS */ + + #if defined(_WIN32) || defined(_WIN64) + # ifndef ZPL_SYSTEM_WINDOWS + # define ZPL_SYSTEM_WINDOWS 1 + # endif + #elif defined(__APPLE__) && defined(__MACH__) + # ifndef ZPL_SYSTEM_OSX + # define ZPL_SYSTEM_OSX 1 + # endif + # ifndef ZPL_SYSTEM_MACOS + # define ZPL_SYSTEM_MACOS 1 + # endif + # include + # if TARGET_IPHONE_SIMULATOR == 1 || TARGET_OS_IPHONE == 1 + # ifndef ZPL_SYSTEM_IOS + # define ZPL_SYSTEM_IOS 1 + # endif + # endif + #elif defined(__unix__) + # ifndef ZPL_SYSTEM_UNIX + # define ZPL_SYSTEM_UNIX 1 + # endif + # if defined(ANDROID) || defined(__ANDROID__) + # ifndef ZPL_SYSTEM_ANDROID + # define ZPL_SYSTEM_ANDROID 1 + # endif + # ifndef ZPL_SYSTEM_LINUX + # define ZPL_SYSTEM_LINUX 1 + # endif + # elif defined(__linux__) + # ifndef ZPL_SYSTEM_LINUX + # define ZPL_SYSTEM_LINUX 1 + # endif + # elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) + # ifndef ZPL_SYSTEM_FREEBSD + # define ZPL_SYSTEM_FREEBSD 1 + # endif + # elif defined(__OpenBSD__) + # ifndef ZPL_SYSTEM_OPENBSD + # define ZPL_SYSTEM_OPENBSD 1 + # endif + # elif defined(__EMSCRIPTEN__) + # ifndef ZPL_SYSTEM_EMSCRIPTEN + # define ZPL_SYSTEM_EMSCRIPTEN 1 + # endif + # elif defined(__CYGWIN__) + # ifndef ZPL_SYSTEM_CYGWIN + # define ZPL_SYSTEM_CYGWIN 1 + # endif + # else + # error This UNIX operating system is not supported + # endif + #else + # error This operating system is not supported + #endif + + /* Platform compiler */ + + #if defined(_MSC_VER) + # define ZPL_COMPILER_MSVC 1 + #elif defined(__GNUC__) + # define ZPL_COMPILER_GCC 1 + #elif defined(__clang__) + # define ZPL_COMPILER_CLANG 1 + #elif defined(__MINGW32__) + # define ZPL_COMPILER_MINGW 1 + #elif defined(__TINYC__) + # define ZPL_COMPILER_TINYC 1 + #else + # error Unknown compiler + #endif + + /* Platform CPU */ + + #if defined(__arm__) || defined(__aarch64__) || defined(__ARM_ARCH) + # ifndef ZPL_CPU_ARM + # define ZPL_CPU_ARM 1 + # endif + # ifndef ZPL_CACHE_LINE_SIZE + # define ZPL_CACHE_LINE_SIZE 64 + # endif + #elif defined(_M_IX86) || defined(_M_X64) || defined(__i386__) || defined(__x86_64__) || defined(ZPL_SYSTEM_EMSCRIPTEN) + # ifndef ZPL_CPU_X86 + # define ZPL_CPU_X86 1 + # endif + # ifndef ZPL_CACHE_LINE_SIZE + # define ZPL_CACHE_LINE_SIZE 64 + # endif + #elif defined(_M_PPC) || defined(__powerpc__) || defined(__powerpc64__) + # ifndef ZPL_CPU_PPC + # define ZPL_CPU_PPC 1 + # endif + # ifndef ZPL_CACHE_LINE_SIZE + # define ZPL_CACHE_LINE_SIZE 128 + # endif + #elif defined(__MIPSEL__) || defined(__mips_isa_rev) + # ifndef ZPL_CPU_MIPS + # define ZPL_CPU_MIPS 1 + # endif + # ifndef ZPL_CACHE_LINE_SIZE + # define ZPL_CACHE_LINE_SIZE 64 + # endif + #else + # error Unknown CPU Type + #endif + + // TODO(ZaKlaus): Find a better way to get this flag in MinGW. + #if (defined(ZPL_COMPILER_GCC) && !defined(WC_ERR_INVALID_CHARS)) || defined(ZPL_COMPILER_TINYC) + # define WC_ERR_INVALID_CHARS 0x0080 + #endif + + #if defined(ZPL_COMPILER_GCC) && defined(ZPL_SYSTEM_WINDOWS) + # ifndef ZPL_COMPILER_MINGW + # define ZPL_COMPILER_MINGW // assume we use mingw as a compiler + # endif + #endif + + #if defined(ZPL_SYSTEM_UNIX) + # ifndef _GNU_SOURCE + # define _GNU_SOURCE + # endif + + # ifndef _LARGEFILE64_SOURCE + # define _LARGEFILE64_SOURCE + # endif + #endif + + #if ZPL_GNUC_VERSION_CHECK(3, 3, 0) + # define ZPL_INFINITY (__builtin_inff()) + # define ZPL_NAN (__builtin_nanf("")) + #elif defined(ZPL_COMPILER_MSVC) + + # if !defined(ZPL__HACK_INFINITY) + typedef union zpl__msvc_inf_hack { + unsigned __int8 bytes[4]; + float value; + } zpl__msvc_inf_hack; + static union zpl__msvc_inf_hack ZPL__INFINITY_HACK = {{0x00, 0x00, 0x80, 0x7F}}; + # define ZPL__HACK_INFINITY (ZPL__INFINITY_HACK.value) + # endif + + # define ZPL_INFINITY (ZPL__HACK_INFINITY) + # define ZPL_NAN (0) + #else + # define ZPL_INFINITY (1e10000f) + # define ZPL_NAN (0.0f / 0.0f) + #endif + + ZPL_END_C_DECLS + +#include +#include + +#if defined(ZPL_SYSTEM_WINDOWS) +# include +#endif + + // file: header/essentials/types.h + + + ZPL_BEGIN_C_DECLS + + /* Basic types */ + + #if defined(ZPL_COMPILER_MSVC) + # if _MSC_VER < 1300 + typedef unsigned char zpl_u8; + typedef signed char zpl_i8; + typedef unsigned short zpl_u16; + typedef signed short zpl_i16; + typedef unsigned int zpl_u32; + typedef signed int zpl_i32; + # else + typedef unsigned __int8 zpl_u8; + typedef signed __int8 zpl_i8; + typedef unsigned __int16 zpl_u16; + typedef signed __int16 zpl_i16; + typedef unsigned __int32 zpl_u32; + typedef signed __int32 zpl_i32; + # endif + typedef unsigned __int64 zpl_u64; + typedef signed __int64 zpl_i64; + #else + # include + + typedef uint8_t zpl_u8; + typedef int8_t zpl_i8; + typedef uint16_t zpl_u16; + typedef int16_t zpl_i16; + typedef uint32_t zpl_u32; + typedef int32_t zpl_i32; + typedef uint64_t zpl_u64; + typedef int64_t zpl_i64; + #endif + + ZPL_STATIC_ASSERT(sizeof(zpl_u8) == sizeof(zpl_i8), "sizeof(zpl_u8) != sizeof(zpl_i8)"); + ZPL_STATIC_ASSERT(sizeof(zpl_u16) == sizeof(zpl_i16), "sizeof(zpl_u16) != sizeof(zpl_i16)"); + ZPL_STATIC_ASSERT(sizeof(zpl_u32) == sizeof(zpl_i32), "sizeof(zpl_u32) != sizeof(zpl_i32)"); + ZPL_STATIC_ASSERT(sizeof(zpl_u64) == sizeof(zpl_i64), "sizeof(zpl_u64) != sizeof(zpl_i64)"); + + ZPL_STATIC_ASSERT(sizeof(zpl_u8) == 1, "sizeof(zpl_u8) != 1"); + ZPL_STATIC_ASSERT(sizeof(zpl_u16) == 2, "sizeof(zpl_u16) != 2"); + ZPL_STATIC_ASSERT(sizeof(zpl_u32) == 4, "sizeof(zpl_u32) != 4"); + ZPL_STATIC_ASSERT(sizeof(zpl_u64) == 8, "sizeof(zpl_u64) != 8"); + + typedef size_t zpl_usize; + typedef ptrdiff_t zpl_isize; + + ZPL_STATIC_ASSERT(sizeof(zpl_usize) == sizeof(zpl_isize), "sizeof(zpl_usize) != sizeof(zpl_isize)"); + + // NOTE: (u)zpl_intptr is only here for semantic reasons really as this library will only support 32/64 bit OSes. + #if defined(_WIN64) + typedef signed __int64 zpl_intptr; + typedef unsigned __int64 zpl_uintptr; + #elif defined(_WIN32) + // NOTE; To mark types changing their size, e.g. zpl_intptr + # ifndef _W64 + # if !defined(__midl) && (defined(_X86_) || defined(_M_IX86)) && _MSC_VER >= 1300 + # define _W64 __w64 + # else + # define _W64 + # endif + # endif + typedef _W64 signed int zpl_intptr; + typedef _W64 unsigned int zpl_uintptr; + #else + typedef uintptr_t zpl_uintptr; + typedef intptr_t zpl_intptr; + #endif + + ZPL_STATIC_ASSERT(sizeof(zpl_uintptr) == sizeof(zpl_intptr), "sizeof(zpl_uintptr) != sizeof(zpl_intptr)"); + + typedef float zpl_f32; + typedef double zpl_f64; + + ZPL_STATIC_ASSERT(sizeof(zpl_f32) == 4, "sizeof(zpl_f32) != 4"); + ZPL_STATIC_ASSERT(sizeof(zpl_f64) == 8, "sizeof(zpl_f64) != 8"); + + typedef zpl_i32 zpl_rune; // NOTE: Unicode codepoint + typedef zpl_i32 zpl_char32; + #define ZPL_RUNE_INVALID cast(zpl_rune)(0xfffd) + #define ZPL_RUNE_MAX cast(zpl_rune)(0x0010ffff) + #define ZPL_RUNE_BOM cast(zpl_rune)(0xfeff) + #define ZPL_RUNE_EOF cast(zpl_rune)(-1) + + typedef zpl_i8 zpl_b8; + typedef zpl_i16 zpl_b16; + typedef zpl_i32 zpl_b32; + + #if !defined(__cplusplus) + # if (defined(_MSC_VER) && _MSC_VER < 1800) || (!defined(_MSC_VER) && !defined(__STDC_VERSION__)) + # ifndef true + # define true(0 == 0) + # endif + # ifndef false + # define false(0 != 0) + # endif + + typedef zpl_b8 bool; + # else + # include + # endif + #endif + + #ifndef ZPL_U8_MIN + # define ZPL_U8_MIN 0u + # define ZPL_U8_MAX 0xffu + # define ZPL_I8_MIN (-0x7f - 1) + # define ZPL_I8_MAX 0x7f + + # define ZPL_U16_MIN 0u + # define ZPL_U16_MAX 0xffffu + # define ZPL_I16_MIN (-0x7fff - 1) + # define ZPL_I16_MAX 0x7fff + + # define ZPL_U32_MIN 0u + # define ZPL_U32_MAX 0xffffffffu + # define ZPL_I32_MIN (-0x7fffffff - 1) + # define ZPL_I32_MAX 0x7fffffff + + # define ZPL_U64_MIN 0ull + # define ZPL_U64_MAX 0xffffffffffffffffull + # define ZPL_I64_MIN (-0x7fffffffffffffffll - 1) + # define ZPL_I64_MAX 0x7fffffffffffffffll + + # if defined(ZPL_ARCH_32_BIT) + # define ZPL_USIZE_MIN ZPL_U32_MIN + # define ZPL_USIZE_MAX ZPL_U32_MAX + # define ZPL_ISIZE_MIN ZPL_I32_MIN + # define ZPL_ISIZE_MAX ZPL_I32_MAX + # elif defined(ZPL_ARCH_64_BIT) + # define ZPL_USIZE_MIN ZPL_U64_MIN + # define ZPL_USIZE_MAX ZPL_U64_MAX + # define ZPL_ISIZE_MIN ZPL_I64_MIN + # define ZPL_ISIZE_MAX ZPL_I64_MAX + # else + # error Unknown architecture size. This library only supports 32 bit and 64 bit architectures. + # endif + + # define ZPL_F32_MIN 1.17549435e-38f + # define ZPL_F32_MAX 3.40282347e+38f + + # define ZPL_F64_MIN 2.2250738585072014e-308 + # define ZPL_F64_MAX 1.7976931348623157e+308 + #endif + + #ifdef ZPL_DEFINE_NULL_MACRO + # ifndef NULL + # define NULL ZPL_NULL + # endif + #endif + + ZPL_END_C_DECLS + // file: header/essentials/helpers.h + + /* Various macro based helpers */ + + ZPL_BEGIN_C_DECLS + + #ifndef cast + # define cast(Type) (Type) + #endif + + #ifndef zpl_size_of + # define zpl_size_of(x) (zpl_isize)(sizeof(x)) + #endif + + #ifndef zpl_count_of + # define zpl_count_of(x) ((zpl_size_of(x) / zpl_size_of(0 [x])) / ((zpl_isize)(!(zpl_size_of(x) % zpl_size_of(0 [x]))))) + #endif + + #ifndef zpl_offset_of + #if defined(_MSC_VER) || defined(ZPL_COMPILER_TINYC) + # define zpl_offset_of(Type, element) ((zpl_isize) & (((Type *)0)->element)) + #else + # define zpl_offset_of(Type, element) __builtin_offsetof(Type, element) + #endif + #endif + + #if defined(__cplusplus) + # ifndef zpl_align_of + # if __cplusplus >= 201103L + # define zpl_align_of(Type) (zpl_isize)alignof(Type) + # else + extern "C++" { + template struct zpl_alignment_trick { + char c; + T member; + }; + } + # define zpl_align_of(Type) zpl_offset_of(zpl_alignment_trick, member) + # endif + # endif + #else + # ifndef zpl_align_of + # define zpl_align_of(Type) \ + zpl_offset_of( \ + struct { \ + char c; \ + Type member; \ + }, \ + member) + # endif + #endif + + #ifndef zpl_swap + # define zpl_swap(Type, a, b) \ + do { \ + Type tmp = (a); \ + (a) = (b); \ + (b) = tmp; \ + } while (0) + #endif + + + + #ifndef zpl_global + # define zpl_global static // Global variables + #endif + + #ifndef zpl_internal + # define zpl_internal static // Internal linkage + #endif + + #ifndef zpl_local_persist + # define zpl_local_persist static // Local Persisting variables + #endif + + #ifndef zpl_unused + # if defined(_MSC_VER) + # define zpl_unused(x) (__pragma(warning(suppress : 4100))(x)) + # elif defined(__GCC__) + # define zpl_unused(x) __attribute__((__unused__))(x) + # else + # define zpl_unused(x) ((void)(zpl_size_of(x))) + # endif + #endif + + + #ifndef ZPL_JOIN_MACROS + # define ZPL_JOIN_MACROS + + # define ZPL_JOIN2 ZPL_CONCAT + # define ZPL_JOIN3(a, b, c) ZPL_JOIN2(ZPL_JOIN2(a, b), c) + # define ZPL_JOIN4(a, b, c, d) ZPL_JOIN2(ZPL_JOIN2(ZPL_JOIN2(a, b), c), d) + #endif + + #ifndef ZPL_BIT + # define ZPL_BIT(x) (1 << (x)) + #endif + + #ifndef zpl_min + # define zpl_min(a, b) ((a) < (b) ? (a) : (b)) + #endif + + #ifndef zpl_max + # define zpl_max(a, b) ((a) > (b) ? (a) : (b)) + #endif + + #ifndef zpl_min3 + # define zpl_min3(a, b, c) zpl_min(zpl_min(a, b), c) + #endif + + #ifndef zpl_max3 + # define zpl_max3(a, b, c) zpl_max(zpl_max(a, b), c) + #endif + + #ifndef zpl_clamp + # define zpl_clamp(x, lower, upper) zpl_min(zpl_max((x), (lower)), (upper)) + #endif + + #ifndef zpl_clamp01 + # define zpl_clamp01(x) zpl_clamp((x), 0, 1) + #endif + + #ifndef zpl_is_between + # define zpl_is_between(x, lower, upper) (((lower) <= (x)) && ((x) <= (upper))) + #endif + + #ifndef zpl_is_between_limit + # define zpl_is_between_limit(x, lower, upper) (((lower) <= (x)) && ((x) < (upper))) + #endif + + #ifndef zpl_step + #define zpl_step(x,y) (((x)/(y))*(y)) + #endif + + #ifndef zpl_abs + # define zpl_abs(x) ((x) < 0 ? -(x) : (x)) + #endif + + #ifndef ZPL_MASK_SET + # define ZPL_MASK_SET(var, set, mask) \ + do { \ + if (set) \ + (var) |= (mask); \ + else \ + (var) &= ~(mask); \ + } while (0) + #endif + + // Multiline string literals in C99! + #ifndef ZPL_MULTILINE + # define ZPL_MULTILINE(...) #__VA_ARGS__ + #endif + + ZPL_END_C_DECLS + +#if defined(ZPL_MODULE_ESSENTIALS) + // file: header/essentials/debug.h + + /* Debugging stuff */ + + ZPL_BEGIN_C_DECLS + + #ifndef ZPL_DEBUG_TRAP + # if defined(_MSC_VER) + # if _MSC_VER < 1300 + # define ZPL_DEBUG_TRAP( ) __asm int 3 /* Trap to debugger! */ + # else + # define ZPL_DEBUG_TRAP( ) __debugbreak( ) + # endif + # elif defined(ZPL_COMPILER_TINYC) + # define ZPL_DEBUG_TRAP( ) zpl_exit(1) + # else + # define ZPL_DEBUG_TRAP( ) __builtin_trap( ) + # endif + #endif + + #ifndef ZPL_ASSERT_MSG + # define ZPL_ASSERT_MSG(cond, msg, ...) \ + do { \ + if (!(cond)) { \ + zpl_assert_handler(#cond, __FILE__, cast(zpl_i64) __LINE__, msg, ##__VA_ARGS__); \ + ZPL_DEBUG_TRAP( ); \ + } \ + } while (0) + #endif + + #ifndef ZPL_ASSERT + # define ZPL_ASSERT(cond) ZPL_ASSERT_MSG(cond, NULL) + #endif + + #ifndef ZPL_ASSERT_NOT_NULL + # define ZPL_ASSERT_NOT_NULL(ptr) ZPL_ASSERT_MSG((ptr) != NULL, #ptr " must not be NULL") + #endif + + // NOTE: Things that shouldn't happen with a message! + #ifndef ZPL_PANIC + # define ZPL_PANIC(msg, ...) ZPL_ASSERT_MSG(0, msg, ##__VA_ARGS__) + #endif + + #ifndef ZPL_NOT_IMPLEMENTED + # define ZPL_NOT_IMPLEMENTED ZPL_PANIC("not implemented") + #endif + + /* Functions */ + + ZPL_DEF void zpl_assert_handler(char const *condition, char const *file, zpl_i32 line, char const *msg, ...); + ZPL_DEF zpl_i32 zpl_assert_crash(char const *condition); + ZPL_DEF void zpl_exit(zpl_u32 code); + + ZPL_END_C_DECLS + // file: header/essentials/memory.h + + /** @file mem.c + @brief Memory manipulation and helpers. + @defgroup memman Memory management + + Consists of pointer arithmetic methods, virtual memory management and custom memory allocators. + + @{ + */ + + ZPL_BEGIN_C_DECLS + + //! Checks if value is power of 2. + ZPL_DEF_INLINE zpl_b32 zpl_is_power_of_two(zpl_isize x); + + //! Aligns address to specified alignment. + ZPL_DEF_INLINE void *zpl_align_forward(void *ptr, zpl_isize alignment); + + //! Aligns value to a specified alignment. + ZPL_DEF_INLINE zpl_i64 zpl_align_forward_i64(zpl_i64 value, zpl_isize alignment); + + //! Aligns value to a specified alignment. + ZPL_DEF_INLINE zpl_u64 zpl_align_forward_u64(zpl_u64 value, zpl_usize alignment); + + //! Moves pointer forward by bytes. + ZPL_DEF_INLINE void *zpl_pointer_add(void *ptr, zpl_isize bytes); + + //! Moves pointer backward by bytes. + ZPL_DEF_INLINE void *zpl_pointer_sub(void *ptr, zpl_isize bytes); + + //! Moves pointer forward by bytes. + ZPL_DEF_INLINE void const *zpl_pointer_add_const(void const *ptr, zpl_isize bytes); + + //! Moves pointer backward by bytes. + ZPL_DEF_INLINE void const *zpl_pointer_sub_const(void const *ptr, zpl_isize bytes); + + //! Calculates difference between two addresses. + ZPL_DEF_INLINE zpl_isize zpl_pointer_diff(void const *begin, void const *end); + + #define zpl_ptr_add zpl_pointer_add + #define zpl_ptr_sub zpl_pointer_sub + #define zpl_ptr_add_const zpl_pointer_add_const + #define zpl_ptr_sub_const zpl_pointer_sub_const + #define zpl_ptr_diff zpl_pointer_diff + + //! Clears up memory at location by specified size. + + //! @param ptr Memory location to clear up. + //! @param size The size to clear up with. + ZPL_DEF_INLINE void zpl_zero_size(void *ptr, zpl_isize size); + + #ifndef zpl_zero_item + //! Clears up an item. + #define zpl_zero_item(t) zpl_zero_size((t), zpl_size_of(*(t))) // NOTE: Pass pointer of struct + + //! Clears up an array. + #define zpl_zero_array(a, count) zpl_zero_size((a), zpl_size_of(*(a)) * count) + #endif + + //! Copy memory from source to destination. + ZPL_DEF_INLINE void *zpl_memmove(void *dest, void const *source, zpl_isize size); + + //! Set constant value at memory location with specified size. + ZPL_DEF_INLINE void *zpl_memset(void *data, zpl_u8 byte_value, zpl_isize size); + + //! Compare two memory locations with specified size. + ZPL_DEF_INLINE zpl_i32 zpl_memcompare(void const *s1, void const *s2, zpl_isize size); + + //! Swap memory contents between 2 locations with size. + ZPL_DEF void zpl_memswap(void *i, void *j, zpl_isize size); + + //! Search for a constant value within the size limit at memory location. + ZPL_DEF void const *zpl_memchr(void const *data, zpl_u8 byte_value, zpl_isize size); + + //! Search for a constant value within the size limit at memory location in backwards. + ZPL_DEF void const *zpl_memrchr(void const *data, zpl_u8 byte_value, zpl_isize size); + + //! Copy non-overlapping memory from source to destination. + ZPL_DEF void *zpl_memcopy(void *dest, void const *source, zpl_isize size); + + #ifndef zpl_memcopy_array + + //! Copy non-overlapping array. + #define zpl_memcopy_array(dst, src, count) zpl_memcopy((dst), (src), zpl_size_of(*(dst)) * (count)) + #endif + + //! Copy an array. + #ifndef zpl_memmove_array + #define zpl_memmove_array(dst, src, count) zpl_memmove((dst), (src), zpl_size_of(*(dst)) * (count)) + #endif + + #ifndef ZPL_BIT_CAST + #define ZPL_BIT_CAST(dest, source) \ + do { \ + ZPL_STATIC_ASSERT(zpl_size_of(*(dest)) <= zpl_size_of(source), "zpl_size_of(*(dest)) !<= zpl_size_of(source)");\ + zpl_memcopy((dest), &(source), zpl_size_of(*dest)); \ + } while (0) + #endif + + #ifndef zpl_kilobytes + #define zpl_kilobytes(x) ((x) * (zpl_i64)(1024)) + #define zpl_megabytes(x) (zpl_kilobytes(x) * (zpl_i64)(1024)) + #define zpl_gigabytes(x) (zpl_megabytes(x) * (zpl_i64)(1024)) + #define zpl_terabytes(x) (zpl_gigabytes(x) * (zpl_i64)(1024)) + #endif + + + /* inlines */ + + #define ZPL__ONES (cast(zpl_usize) - 1 / ZPL_U8_MAX) + #define ZPL__HIGHS (ZPL__ONES * (ZPL_U8_MAX / 2 + 1)) + #define ZPL__HAS_ZERO(x) (((x)-ZPL__ONES) & ~(x)&ZPL__HIGHS) + + ZPL_IMPL_INLINE void *zpl_align_forward(void *ptr, zpl_isize alignment) { + zpl_uintptr p; + + ZPL_ASSERT(zpl_is_power_of_two(alignment)); + + p = cast(zpl_uintptr) ptr; + return cast(void *)((p + (alignment - 1)) & ~(alignment - 1)); + } + + ZPL_IMPL_INLINE zpl_i64 zpl_align_forward_i64(zpl_i64 value, zpl_isize alignment) { + return value + (alignment - value % alignment) % alignment; + } + + ZPL_IMPL_INLINE zpl_u64 zpl_align_forward_u64(zpl_u64 value, zpl_usize alignment) { + return value + (alignment - value % alignment) % alignment; + } + + ZPL_IMPL_INLINE void *zpl_pointer_add(void *ptr, zpl_isize bytes) { return cast(void *)(cast(zpl_u8 *) ptr + bytes); } + ZPL_IMPL_INLINE void *zpl_pointer_sub(void *ptr, zpl_isize bytes) { return cast(void *)(cast(zpl_u8 *) ptr - bytes); } + ZPL_IMPL_INLINE void const *zpl_pointer_add_const(void const *ptr, zpl_isize bytes) { + return cast(void const *)(cast(zpl_u8 const *) ptr + bytes); + } + ZPL_IMPL_INLINE void const *zpl_pointer_sub_const(void const *ptr, zpl_isize bytes) { + return cast(void const *)(cast(zpl_u8 const *) ptr - bytes); + } + ZPL_IMPL_INLINE zpl_isize zpl_pointer_diff(void const *begin, void const *end) { + return cast(zpl_isize)(cast(zpl_u8 const *) end - cast(zpl_u8 const *) begin); + } + + ZPL_IMPL_INLINE void zpl_zero_size(void *ptr, zpl_isize size) { zpl_memset(ptr, 0, size); } + + #if defined(_MSC_VER) && !defined(__clang__) + #pragma intrinsic(__movsb) + #endif + + ZPL_IMPL_INLINE void *zpl_memmove(void *dest, void const *source, zpl_isize n) { + if (dest == NULL) { return NULL; } + + zpl_u8 *d = cast(zpl_u8 *) dest; + zpl_u8 const *s = cast(zpl_u8 const *) source; + + if (d == s) return d; + if (s + n <= d || d + n <= s) // NOTE: Non-overlapping + return zpl_memcopy(d, s, n); + + if (d < s) { + if (cast(zpl_uintptr) s % zpl_size_of(zpl_isize) == cast(zpl_uintptr) d % zpl_size_of(zpl_isize)) { + while (cast(zpl_uintptr) d % zpl_size_of(zpl_isize)) { + if (!n--) return dest; + *d++ = *s++; + } + while (n >= zpl_size_of(zpl_isize)) { + *cast(zpl_isize *) d = *cast(zpl_isize *) s; + n -= zpl_size_of(zpl_isize); + d += zpl_size_of(zpl_isize); + s += zpl_size_of(zpl_isize); + } + } + for (; n; n--) *d++ = *s++; + } else { + if ((cast(zpl_uintptr) s % zpl_size_of(zpl_isize)) == (cast(zpl_uintptr) d % zpl_size_of(zpl_isize))) { + while (cast(zpl_uintptr)(d + n) % zpl_size_of(zpl_isize)) { + if (!n--) return dest; + d[n] = s[n]; + } + while (n >= zpl_size_of(zpl_isize)) { + n -= zpl_size_of(zpl_isize); + *cast(zpl_isize *)(d + n) = *cast(zpl_isize *)(s + n); + } + } + while (n) n--, d[n] = s[n]; + } + + return dest; + } + + ZPL_IMPL_INLINE void *zpl_memset(void *dest, zpl_u8 c, zpl_isize n) { + if (dest == NULL) { return NULL; } + + zpl_u8 *s = cast(zpl_u8 *) dest; + zpl_isize k; + zpl_u32 c32 = ((zpl_u32)-1) / 255 * c; + + if (n == 0) return dest; + s[0] = s[n - 1] = c; + if (n < 3) return dest; + s[1] = s[n - 2] = c; + s[2] = s[n - 3] = c; + if (n < 7) return dest; + s[3] = s[n - 4] = c; + if (n < 9) return dest; + + k = -cast(zpl_intptr) s & 3; + s += k; + n -= k; + n &= -4; + + *cast(zpl_u32 *)(s + 0) = c32; + *cast(zpl_u32 *)(s + n - 4) = c32; + if (n < 9) return dest; + *cast(zpl_u32 *)(s + 4) = c32; + *cast(zpl_u32 *)(s + 8) = c32; + *cast(zpl_u32 *)(s + n - 12) = c32; + *cast(zpl_u32 *)(s + n - 8) = c32; + if (n < 25) return dest; + *cast(zpl_u32 *)(s + 12) = c32; + *cast(zpl_u32 *)(s + 16) = c32; + *cast(zpl_u32 *)(s + 20) = c32; + *cast(zpl_u32 *)(s + 24) = c32; + *cast(zpl_u32 *)(s + n - 28) = c32; + *cast(zpl_u32 *)(s + n - 24) = c32; + *cast(zpl_u32 *)(s + n - 20) = c32; + *cast(zpl_u32 *)(s + n - 16) = c32; + + k = 24 + (cast(zpl_uintptr) s & 4); + s += k; + n -= k; + + { + zpl_u64 c64 = (cast(zpl_u64) c32 << 32) | c32; + while (n > 31) { + *cast(zpl_u64 *)(s + 0) = c64; + *cast(zpl_u64 *)(s + 8) = c64; + *cast(zpl_u64 *)(s + 16) = c64; + *cast(zpl_u64 *)(s + 24) = c64; + + n -= 32; + s += 32; + } + } + + return dest; + } + + ZPL_IMPL_INLINE zpl_i32 zpl_memcompare(void const *s1, void const *s2, zpl_isize size) { + zpl_u8 const *s1p8 = cast(zpl_u8 const *) s1; + zpl_u8 const *s2p8 = cast(zpl_u8 const *) s2; + + if (s1 == NULL || s2 == NULL) { return 0; } + + while (size--) { + zpl_isize d; + if ((d = (*s1p8++ - *s2p8++)) != 0) return cast(zpl_i32) d; + } + return 0; + } + + ZPL_IMPL_INLINE zpl_b32 zpl_is_power_of_two(zpl_isize x) { + if (x <= 0) return false; + return !(x & (x - 1)); + } + + ZPL_END_C_DECLS + // file: header/essentials/memory_custom.h + + //////////////////////////////////////////////////////////////// + // + // Custom Allocation + // + // + + ZPL_BEGIN_C_DECLS + + typedef enum zpl_alloc_type { + ZPL_ALLOCATION_ALLOC, + ZPL_ALLOCATION_FREE, + ZPL_ALLOCATION_FREE_ALL, + ZPL_ALLOCATION_RESIZE, + } zpl_alloc_type; + + // NOTE: This is useful so you can define an allocator of the same type and parameters + #define ZPL_ALLOCATOR_PROC(name) \ + void *name(void *allocator_data, zpl_alloc_type type, zpl_isize size, zpl_isize alignment, void *old_memory, \ + zpl_isize old_size, zpl_u64 flags) + typedef ZPL_ALLOCATOR_PROC(zpl_allocator_proc); + + + typedef struct zpl_allocator { + zpl_allocator_proc *proc; + void *data; + } zpl_allocator; + + typedef enum zpl_alloc_flag { + ZPL_ALLOCATOR_FLAG_CLEAR_TO_ZERO = ZPL_BIT(0), + } zpl_alloc_flag; + + #ifndef ZPL_DEFAULT_MEMORY_ALIGNMENT + #define ZPL_DEFAULT_MEMORY_ALIGNMENT (2 * zpl_size_of(void *)) + #endif + + #ifndef ZPL_DEFAULT_ALLOCATOR_FLAGS + #define ZPL_DEFAULT_ALLOCATOR_FLAGS (ZPL_ALLOCATOR_FLAG_CLEAR_TO_ZERO) + #endif + + //! Allocate memory with specified alignment. + ZPL_DEF_INLINE void *zpl_alloc_align(zpl_allocator a, zpl_isize size, zpl_isize alignment); + + //! Allocate memory with default alignment. + ZPL_DEF_INLINE void *zpl_alloc(zpl_allocator a, zpl_isize size); + + //! Free allocated memory. + ZPL_DEF_INLINE void zpl_free(zpl_allocator a, void *ptr); + + //! Free all memory allocated by an allocator. + ZPL_DEF_INLINE void zpl_free_all(zpl_allocator a); + + //! Resize an allocated memory. + ZPL_DEF_INLINE void *zpl_resize(zpl_allocator a, void *ptr, zpl_isize old_size, zpl_isize new_size); + + //! Resize an allocated memory with specified alignment. + ZPL_DEF_INLINE void *zpl_resize_align(zpl_allocator a, void *ptr, zpl_isize old_size, zpl_isize new_size, zpl_isize alignment); + + //! Allocate memory and copy data into it. + ZPL_DEF_INLINE void *zpl_alloc_copy(zpl_allocator a, void const *src, zpl_isize size); + + //! Allocate memory with specified alignment and copy data into it. + ZPL_DEF_INLINE void *zpl_alloc_copy_align(zpl_allocator a, void const *src, zpl_isize size, zpl_isize alignment); + + //! Allocate memory for null-terminated C-String. + ZPL_DEF char *zpl_alloc_str(zpl_allocator a, char const *str); + + //! Allocate memory for C-String with specified size. + ZPL_DEF_INLINE char *zpl_alloc_str_len(zpl_allocator a, char const *str, zpl_isize len); + + #ifndef zpl_alloc_item + + //! Allocate memory for an item. + #define zpl_alloc_item(allocator_, Type) (Type *)zpl_alloc(allocator_, zpl_size_of(Type)) + + //! Allocate memory for an array of items. + #define zpl_alloc_array(allocator_, Type, count) (Type *)zpl_alloc(allocator_, zpl_size_of(Type) * (count)) + #endif + + /* heap memory analysis tools */ + /* define ZPL_HEAP_ANALYSIS to enable this feature */ + /* call zpl_heap_stats_init at the beginning of the entry point */ + /* you can call zpl_heap_stats_check near the end of the execution to validate any possible leaks */ + ZPL_DEF void zpl_heap_stats_init(void); + ZPL_DEF zpl_isize zpl_heap_stats_used_memory(void); + ZPL_DEF zpl_isize zpl_heap_stats_alloc_count(void); + ZPL_DEF void zpl_heap_stats_check(void); + + //! Allocate/Resize memory using default options. + + //! Use this if you don't need a "fancy" resize allocation + ZPL_DEF_INLINE void *zpl_default_resize_align(zpl_allocator a, void *ptr, zpl_isize old_size, zpl_isize new_size, zpl_isize alignment); + + //! The heap allocator backed by operating system's memory manager. + ZPL_DEF_INLINE zpl_allocator zpl_heap_allocator(void); + ZPL_DEF ZPL_ALLOCATOR_PROC(zpl_heap_allocator_proc); + + #ifndef zpl_malloc + + //! Helper to allocate memory using heap allocator. + #define zpl_malloc(sz) zpl_alloc(zpl_heap_allocator( ), sz) + + //! Helper to free memory allocated by heap allocator. + #define zpl_mfree(ptr) zpl_free(zpl_heap_allocator( ), ptr) + + //! Alias to heap allocator. + #define zpl_heap zpl_heap_allocator + #endif + + // + // Arena Allocator + // + + typedef struct zpl_arena { + zpl_allocator backing; + void *physical_start; + zpl_isize total_size; + zpl_isize total_allocated; + zpl_isize temp_count; + } zpl_arena; + + //! Initialize memory arena from existing memory region. + ZPL_DEF_INLINE void zpl_arena_init_from_memory(zpl_arena *arena, void *start, zpl_isize size); + + //! Initialize memory arena using existing memory allocator. + ZPL_DEF_INLINE void zpl_arena_init_from_allocator(zpl_arena *arena, zpl_allocator backing, zpl_isize size); + + //! Initialize memory arena within an existing parent memory arena. + ZPL_DEF_INLINE void zpl_arena_init_sub(zpl_arena *arena, zpl_arena *parent_arena, zpl_isize size); + + //! Release the memory used by memory arena. + ZPL_DEF_INLINE void zpl_arena_free(zpl_arena *arena); + + + //! Retrieve memory arena's aligned allocation address. + ZPL_DEF_INLINE zpl_isize zpl_arena_alignment_of(zpl_arena *arena, zpl_isize alignment); + + //! Retrieve memory arena's remaining size. + ZPL_DEF_INLINE zpl_isize zpl_arena_size_remaining(zpl_arena *arena, zpl_isize alignment); + + //! Check whether memory arena has any temporary snapshots. + ZPL_DEF_INLINE void zpl_arena_check(zpl_arena *arena); + + //! Allocation Types: alloc, free_all, resize + ZPL_DEF_INLINE zpl_allocator zpl_arena_allocator(zpl_arena *arena); + ZPL_DEF ZPL_ALLOCATOR_PROC(zpl_arena_allocator_proc); + + + typedef struct zpl_arena_snapshot { + zpl_arena *arena; + zpl_isize original_count; + } zpl_arena_snapshot; + + //! Capture a snapshot of used memory in a memory arena. + ZPL_DEF_INLINE zpl_arena_snapshot zpl_arena_snapshot_begin(zpl_arena *arena); + + //! Reset memory arena's usage by a captured snapshot. + ZPL_DEF_INLINE void zpl_arena_snapshot_end(zpl_arena_snapshot tmp_mem); + + // + // Pool Allocator + // + + + typedef struct zpl_pool { + zpl_allocator backing; + void *physical_start; + void *free_list; + zpl_isize block_size; + zpl_isize block_align; + zpl_isize total_size; + zpl_isize num_blocks; + } zpl_pool; + + + //! Initialize pool allocator. + ZPL_DEF_INLINE void zpl_pool_init(zpl_pool *pool, zpl_allocator backing, zpl_isize num_blocks, zpl_isize block_size); + + //! Initialize pool allocator with specific block alignment. + ZPL_DEF void zpl_pool_init_align(zpl_pool *pool, zpl_allocator backing, zpl_isize num_blocks, zpl_isize block_size, + zpl_isize block_align); + + //! Release the resources used by pool allocator. + ZPL_DEF_INLINE void zpl_pool_free(zpl_pool *pool); + + //! Allocation Types: alloc, free + ZPL_DEF_INLINE zpl_allocator zpl_pool_allocator(zpl_pool *pool); + ZPL_DEF ZPL_ALLOCATOR_PROC(zpl_pool_allocator_proc); + + // + // Scratch Memory Allocator - Ring Buffer Based Arena + // + + typedef struct zpl_allocation_header_ev { + zpl_isize size; + } zpl_allocation_header_ev; + + ZPL_DEF_INLINE zpl_allocation_header_ev *zpl_allocation_header(void *data); + ZPL_DEF_INLINE void zpl_allocation_header_fill(zpl_allocation_header_ev *header, void *data, zpl_isize size); + + #if defined(ZPL_ARCH_32_BIT) + #define ZPL_ISIZE_HIGH_BIT 0x80000000 + #elif defined(ZPL_ARCH_64_BIT) + #define ZPL_ISIZE_HIGH_BIT 0x8000000000000000ll + #else + #error + #endif + + typedef struct zpl_scratch_memory { + void *physical_start; + zpl_isize total_size; + void *alloc_point; + void *free_point; + } zpl_scratch_memory; + + //! Initialize ring buffer arena. + ZPL_DEF void zpl_scratch_memory_init(zpl_scratch_memory *s, void *start, zpl_isize size); + + //! Check whether ring buffer arena is in use. + ZPL_DEF zpl_b32 zpl_scratch_memory_is_in_use(zpl_scratch_memory *s, void *ptr); + + //! Allocation Types: alloc, free, free_all, resize + ZPL_DEF zpl_allocator zpl_scratch_allocator(zpl_scratch_memory *s); + ZPL_DEF ZPL_ALLOCATOR_PROC(zpl_scratch_allocator_proc); + + // + // Stack Memory Allocator + // + + + typedef struct zpl_stack_memory { + zpl_allocator backing; + + void *physical_start; + zpl_usize total_size; + zpl_usize allocated; + } zpl_stack_memory; + + //! Initialize stack allocator from existing memory. + ZPL_DEF_INLINE void zpl_stack_memory_init_from_memory(zpl_stack_memory *s, void *start, zpl_isize size); + + //! Initialize stack allocator using existing memory allocator. + ZPL_DEF_INLINE void zpl_stack_memory_init(zpl_stack_memory *s, zpl_allocator backing, zpl_isize size); + + //! Check whether stack allocator is in use. + ZPL_DEF_INLINE zpl_b32 zpl_stack_memory_is_in_use(zpl_stack_memory *s, void *ptr); + + //! Release the resources used by stack allocator. + ZPL_DEF_INLINE void zpl_stack_memory_free(zpl_stack_memory *s); + + //! Allocation Types: alloc, free, free_all + ZPL_DEF_INLINE zpl_allocator zpl_stack_allocator(zpl_stack_memory *s); + ZPL_DEF ZPL_ALLOCATOR_PROC(zpl_stack_allocator_proc); + + /* inlines */ + + ZPL_IMPL_INLINE void *zpl_alloc_align(zpl_allocator a, zpl_isize size, zpl_isize alignment) { + return a.proc(a.data, ZPL_ALLOCATION_ALLOC, size, alignment, NULL, 0, ZPL_DEFAULT_ALLOCATOR_FLAGS); + } + ZPL_IMPL_INLINE void *zpl_alloc(zpl_allocator a, zpl_isize size) { + return zpl_alloc_align(a, size, ZPL_DEFAULT_MEMORY_ALIGNMENT); + } + ZPL_IMPL_INLINE void zpl_free(zpl_allocator a, void *ptr) { + if (ptr != NULL) a.proc(a.data, ZPL_ALLOCATION_FREE, 0, 0, ptr, 0, ZPL_DEFAULT_ALLOCATOR_FLAGS); + } + ZPL_IMPL_INLINE void zpl_free_all(zpl_allocator a) { + a.proc(a.data, ZPL_ALLOCATION_FREE_ALL, 0, 0, NULL, 0, ZPL_DEFAULT_ALLOCATOR_FLAGS); + } + ZPL_IMPL_INLINE void *zpl_resize(zpl_allocator a, void *ptr, zpl_isize old_size, zpl_isize new_size) { + return zpl_resize_align(a, ptr, old_size, new_size, ZPL_DEFAULT_MEMORY_ALIGNMENT); + } + ZPL_IMPL_INLINE void *zpl_resize_align(zpl_allocator a, void *ptr, zpl_isize old_size, zpl_isize new_size, zpl_isize alignment) { + return a.proc(a.data, ZPL_ALLOCATION_RESIZE, new_size, alignment, ptr, old_size, ZPL_DEFAULT_ALLOCATOR_FLAGS); + } + + ZPL_IMPL_INLINE void *zpl_alloc_copy(zpl_allocator a, void const *src, zpl_isize size) { + return zpl_memcopy(zpl_alloc(a, size), src, size); + } + ZPL_IMPL_INLINE void *zpl_alloc_copy_align(zpl_allocator a, void const *src, zpl_isize size, zpl_isize alignment) { + return zpl_memcopy(zpl_alloc_align(a, size, alignment), src, size); + } + + ZPL_IMPL_INLINE char *zpl_alloc_str_len(zpl_allocator a, char const *str, zpl_isize len) { + char *result; + result = cast(char *) zpl_alloc(a, len + 1); + zpl_memmove(result, str, len); + result[len] = '\0'; + return result; + } + + ZPL_IMPL_INLINE void *zpl_default_resize_align(zpl_allocator a, void *old_memory, zpl_isize old_size, zpl_isize new_size, + zpl_isize alignment) { + if (!old_memory) return zpl_alloc_align(a, new_size, alignment); + + if (new_size == 0) { + zpl_free(a, old_memory); + return NULL; + } + + if (new_size < old_size) new_size = old_size; + + if (old_size == new_size) { + return old_memory; + } else { + void *new_memory = zpl_alloc_align(a, new_size, alignment); + if (!new_memory) return NULL; + zpl_memmove(new_memory, old_memory, zpl_min(new_size, old_size)); + zpl_free(a, old_memory); + return new_memory; + } + } + + // + // Heap Allocator + // + + ZPL_IMPL_INLINE zpl_allocator zpl_heap_allocator(void) { + zpl_allocator a; + a.proc = zpl_heap_allocator_proc; + a.data = NULL; + return a; + } + + // + // Arena Allocator + // + + ZPL_IMPL_INLINE void zpl_arena_init_from_memory(zpl_arena *arena, void *start, zpl_isize size) { + arena->backing.proc = NULL; + arena->backing.data = NULL; + arena->physical_start = start; + arena->total_size = size; + arena->total_allocated = 0; + arena->temp_count = 0; + } + + ZPL_IMPL_INLINE void zpl_arena_init_from_allocator(zpl_arena *arena, zpl_allocator backing, zpl_isize size) { + arena->backing = backing; + arena->physical_start = zpl_alloc(backing, size); // NOTE: Uses default alignment + arena->total_size = size; + arena->total_allocated = 0; + arena->temp_count = 0; + } + + ZPL_IMPL_INLINE void zpl_arena_init_sub(zpl_arena *arena, zpl_arena *parent_arena, zpl_isize size) { + zpl_arena_init_from_allocator(arena, zpl_arena_allocator(parent_arena), size); + } + + ZPL_IMPL_INLINE void zpl_arena_free(zpl_arena *arena) { + if (arena->backing.proc) { + zpl_free(arena->backing, arena->physical_start); + arena->physical_start = NULL; + } + } + + ZPL_IMPL_INLINE zpl_isize zpl_arena_alignment_of(zpl_arena *arena, zpl_isize alignment) { + zpl_isize alignment_offset, result_pointer, mask; + ZPL_ASSERT(zpl_is_power_of_two(alignment)); + + alignment_offset = 0; + result_pointer = cast(zpl_isize) arena->physical_start + arena->total_allocated; + mask = alignment - 1; + if (result_pointer & mask) alignment_offset = alignment - (result_pointer & mask); + + return alignment_offset; + } + + ZPL_IMPL_INLINE zpl_isize zpl_arena_size_remaining(zpl_arena *arena, zpl_isize alignment) { + zpl_isize result = arena->total_size - (arena->total_allocated + zpl_arena_alignment_of(arena, alignment)); + return result; + } + + ZPL_IMPL_INLINE void zpl_arena_check(zpl_arena *arena) { ZPL_ASSERT(arena->temp_count == 0); } + + ZPL_IMPL_INLINE zpl_allocator zpl_arena_allocator(zpl_arena *arena) { + zpl_allocator allocator; + allocator.proc = zpl_arena_allocator_proc; + allocator.data = arena; + return allocator; + } + + ZPL_IMPL_INLINE zpl_arena_snapshot zpl_arena_snapshot_begin(zpl_arena *arena) { + zpl_arena_snapshot tmp; + tmp.arena = arena; + tmp.original_count = arena->total_allocated; + arena->temp_count++; + return tmp; + } + + ZPL_IMPL_INLINE void zpl_arena_snapshot_end(zpl_arena_snapshot tmp) { + ZPL_ASSERT(tmp.arena->total_allocated >= tmp.original_count); + ZPL_ASSERT(tmp.arena->temp_count > 0); + tmp.arena->total_allocated = tmp.original_count; + tmp.arena->temp_count--; + } + + // + // Pool Allocator + // + + ZPL_IMPL_INLINE void zpl_pool_init(zpl_pool *pool, zpl_allocator backing, zpl_isize num_blocks, zpl_isize block_size) { + zpl_pool_init_align(pool, backing, num_blocks, block_size, ZPL_DEFAULT_MEMORY_ALIGNMENT); + } + + ZPL_IMPL_INLINE void zpl_pool_free(zpl_pool *pool) { + if (pool->backing.proc) { zpl_free(pool->backing, pool->physical_start); } + } + + ZPL_IMPL_INLINE zpl_allocator zpl_pool_allocator(zpl_pool *pool) { + zpl_allocator allocator; + allocator.proc = zpl_pool_allocator_proc; + allocator.data = pool; + return allocator; + } + + ZPL_IMPL_INLINE zpl_allocation_header_ev *zpl_allocation_header(void *data) { + zpl_isize *p = cast(zpl_isize *) data; + while (p[-1] == cast(zpl_isize)(-1)) p--; + return cast(zpl_allocation_header_ev *) p - 1; + } + + ZPL_IMPL_INLINE void zpl_allocation_header_fill(zpl_allocation_header_ev *header, void *data, zpl_isize size) { + zpl_isize *ptr; + header->size = size; + ptr = cast(zpl_isize *)(header + 1); + while (cast(void *) ptr < data) *ptr++ = cast(zpl_isize)(-1); + } + + // + // Stack Memory Allocator + // + + #define ZPL_STACK_ALLOC_OFFSET sizeof(zpl_u64) + ZPL_STATIC_ASSERT(ZPL_STACK_ALLOC_OFFSET == 8, "ZPL_STACK_ALLOC_OFFSET != 8"); + + ZPL_IMPL_INLINE void zpl_stack_memory_init_from_memory(zpl_stack_memory *s, void *start, zpl_isize size) { + s->physical_start = start; + s->total_size = size; + s->allocated = 0; + } + + ZPL_IMPL_INLINE void zpl_stack_memory_init(zpl_stack_memory *s, zpl_allocator backing, zpl_isize size) { + s->backing = backing; + s->physical_start = zpl_alloc(backing, size); + s->total_size = size; + s->allocated = 0; + } + + ZPL_IMPL_INLINE zpl_b32 zpl_stack_memory_is_in_use(zpl_stack_memory *s, void *ptr) { + if (s->allocated == 0) return false; + + if (ptr > s->physical_start && ptr < zpl_pointer_add(s->physical_start, s->total_size)) { return true; } + + return false; + } + + ZPL_IMPL_INLINE void zpl_stack_memory_free(zpl_stack_memory *s) { + if (s->backing.proc) { + zpl_free(s->backing, s->physical_start); + s->physical_start = NULL; + } + } + + ZPL_IMPL_INLINE zpl_allocator zpl_stack_allocator(zpl_stack_memory *s) { + zpl_allocator a; + a.proc = zpl_stack_allocator_proc; + a.data = s; + return a; + } + + ZPL_END_C_DECLS + // file: header/essentials/collections/array.h + + //////////////////////////////////////////////////////////////// + // + // Dynamic Array (POD Types) + // + // zpl_array(Type) works like zpl_string or zpl_buffer where the actual type is just a pointer to the first + // element. + // + // Available Procedures for zpl_array(Type) + // zpl_array_init + // zpl_array_free + // zpl_array_set_capacity + // zpl_array_grow + // zpl_array_append + // zpl_array_appendv + // zpl_array_pop + // zpl_array_clear + // zpl_array_back + // zpl_array_front + // zpl_array_resize + // zpl_array_reserve + // + + #if 0 // Example + void foo(void) { + zpl_isize i; + int test_values[] = {4, 2, 1, 7}; + zpl_allocator a = zpl_heap_allocator(); + zpl_array(int) items; + + zpl_array_init(items, a); + + zpl_array_append(items, 1); + zpl_array_append(items, 4); + zpl_array_append(items, 9); + zpl_array_append(items, 16); + + items[1] = 3; // Manually set value + // NOTE: No array bounds checking + + for (i = 0; i < items.count; i++) + zpl_printf("%d\n", items[i]); + // 1 + // 3 + // 9 + // 16 + + zpl_array_clear(items); + + zpl_array_appendv(items, test_values, zpl_count_of(test_values)); + for (i = 0; i < items.count; i++) + zpl_printf("%d\n", items[i]); + // 4 + // 2 + // 1 + // 7 + + zpl_array_free(items); + } + #endif + + + ZPL_BEGIN_C_DECLS + + typedef struct zpl_array_header { + zpl_isize elem_size; + zpl_isize count; + zpl_isize capacity; + zpl_allocator allocator; + } zpl_array_header; + + #define zpl_array(Type) Type * + + #define zpl_array_make(Type, Name, allocator) Type *Name; zpl_array_init(Name, allocator) + + #ifndef ZPL_ARRAY_GROW_FORMULA + #define ZPL_ARRAY_GROW_FORMULA(x) (2 * (x) + 8) + #endif + + ZPL_STATIC_ASSERT(ZPL_ARRAY_GROW_FORMULA(0) > 0, "ZPL_ARRAY_GROW_FORMULA(0) <= 0"); + + #define ZPL_ARRAY_HEADER(x) (cast(zpl_array_header *)(x) - 1) + #define zpl_array_allocator(x) (ZPL_ARRAY_HEADER(x)->allocator) + #define zpl_array_elem_size(x) (ZPL_ARRAY_HEADER(x)->elem_size) + #define zpl_array_count(x) (ZPL_ARRAY_HEADER(x)->count) + #define zpl_array_capacity(x) (ZPL_ARRAY_HEADER(x)->capacity) + #define zpl_array_end(x) (x + (zpl_array_count(x) - 1)) + + ZPL_IMPL_INLINE zpl_b8 zpl__array_init_reserve(void **zpl__array_, zpl_allocator allocator_, zpl_isize elem_size, zpl_isize cap) { + zpl_array_header *zpl__ah = + cast(zpl_array_header *) zpl_alloc(allocator_, zpl_size_of(zpl_array_header) + elem_size * cap); + if (!zpl__ah) return false; + zpl__ah->allocator = allocator_; + zpl__ah->elem_size = elem_size; + zpl__ah->count = 0; + zpl__ah->capacity = cap; + *zpl__array_ = cast(void *)(zpl__ah + 1); + return true; + } + + #define zpl_array_init_reserve(x, allocator_, cap) zpl__array_init_reserve(cast(void **) & (x), allocator_, zpl_size_of(*(x)), (cap)) + + // NOTE: Give it an initial default capacity + #define zpl_array_init(x, allocator) zpl_array_init_reserve(x, allocator, ZPL_ARRAY_GROW_FORMULA(0)) + + #define zpl_array_free(x) \ + do { \ + if (x) { \ + zpl_array_header *zpl__ah = ZPL_ARRAY_HEADER(x); \ + zpl_free(zpl__ah->allocator, zpl__ah); \ + } \ + } while (0) + + ZPL_IMPL_INLINE zpl_b8 zpl__array_set_capacity(void **array, zpl_isize capacity) { + zpl_array_header *h = ZPL_ARRAY_HEADER(*array); + if (capacity == h->capacity) return true; + if (capacity < h->count) h->count = capacity; + zpl_isize size = zpl_size_of(zpl_array_header) + h->elem_size * capacity; + zpl_array_header *nh = cast(zpl_array_header *) zpl_alloc(h->allocator, size); + if (!nh) return false; + zpl_memmove(nh, h, zpl_size_of(zpl_array_header) + h->elem_size * h->count); + nh->allocator = h->allocator; + nh->elem_size = h->elem_size; + nh->count = h->count; + nh->capacity = capacity; + zpl_free(h->allocator, h); + *array = nh + 1; + return true; + } + + #define zpl_array_set_capacity(x, capacity) zpl__array_set_capacity(cast(void **) & (x), (capacity)) + + ZPL_IMPL_INLINE zpl_b8 zpl__array_grow(void **x, zpl_isize min_capacity) { + zpl_isize new_capacity = ZPL_ARRAY_GROW_FORMULA(zpl_array_capacity(*x)); + if (new_capacity < min_capacity) new_capacity = min_capacity; + return zpl__array_set_capacity(x, new_capacity); + } + + #define zpl_array_grow(x, min_capacity) zpl__array_grow(cast(void **) & (x), (min_capacity)) + + ZPL_IMPL_INLINE zpl_b8 zpl__array_append_helper(void **x) { + if (zpl_array_capacity(*x) < zpl_array_count(*x) + 1) { + if (!zpl__array_grow(x, 0)) return false; + } + return true; + } + + #define zpl_array_append(x, item) (zpl__array_append_helper(cast(void **) & (x)) && (((x)[zpl_array_count(x)++] = (item)), true)) + + ZPL_IMPL_INLINE zpl_b8 zpl__array_append_at_helper(void **x, zpl_isize ind) { + if (ind >= zpl_array_count(*x)) ind = zpl_array_count(*x) - 1; + if (ind < 0) ind = 0; + if (zpl_array_capacity(*x) < zpl_array_count(*x) + 1) { + if (!zpl__array_grow(x, 0)) return false; + } + zpl_i8 *s = (cast(zpl_i8*)*x) + ind*zpl_array_elem_size(*x); + zpl_memmove(s + zpl_array_elem_size(*x), s, zpl_array_elem_size(*x) * (zpl_array_count(*x) - ind)); + return true; + } + + #define zpl_array_append_at(x, item, ind) (zpl__array_append_at_helper(cast(void **) & (x), (ind)) && (((x)[ind] = (item)), zpl_array_count(x)++, true)) + + ZPL_IMPL_INLINE zpl_b8 zpl__array_appendv(void **x, void *items, zpl_isize item_size, zpl_isize item_count) { + ZPL_ASSERT(item_size == zpl_array_elem_size(*x)); + if (zpl_array_capacity(*x) < zpl_array_count(*x) + item_count) { + if (!zpl__array_grow(x, zpl_array_count(*x) + item_count)) return false; + } + zpl_memcopy((cast(zpl_i8*)*x) + zpl_array_count(*x)*zpl_array_elem_size(*x), items, zpl_array_elem_size(*x) * item_count); + zpl_array_count(*x) += item_count; + return true; + } + + #define zpl_array_appendv(x, items, item_count) zpl__array_appendv(cast(void **) & (x), (items), zpl_size_of((items)[0]), (item_count)) + + ZPL_IMPL_INLINE zpl_b8 zpl__array_appendv_at(void **x, void *items, zpl_isize item_size, zpl_isize item_count, zpl_isize ind) { + if (ind >= zpl_array_count(*x)) return zpl__array_appendv(x, items, item_size, item_count); + ZPL_ASSERT(item_size == zpl_array_elem_size(*x)); + if (zpl_array_capacity(*x) < zpl_array_count(*x) + item_count) { + if (!zpl__array_grow(x, zpl_array_count(*x) + item_count)) return false; + } + zpl_memmove((cast(zpl_i8*)*x) + (ind + item_count)*zpl_array_elem_size(*x), + (cast(zpl_i8*)*x) + ind*zpl_array_elem_size(*x), zpl_array_elem_size(*x) * (zpl_array_count(*x) - ind)); + zpl_memcopy((cast(zpl_i8*)*x) + ind*zpl_array_elem_size(*x), items, zpl_array_elem_size(*x) * item_count); + zpl_array_count(*x) += item_count; + return true; + } + + #define zpl_array_appendv_at(x, items, item_count, ind) zpl__array_appendv_at(cast(void **) & (x), (items), zpl_size_of((items)[0]), (item_count), (ind)) + + #define zpl_array_fill(x, begin, end, value) \ + do { \ + ZPL_ASSERT((begin) >= 0 && (end) <= zpl_array_count(x)); \ + ZPL_ASSERT(zpl_size_of(value) == zpl_size_of((x)[0])); \ + for (zpl_isize i = (begin); i < (end); i++) { x[i] = value; } \ + } while (0) + + #define zpl_array_remove_at(x, index) \ + do { \ + zpl_array_header *zpl__ah = ZPL_ARRAY_HEADER(x); \ + ZPL_ASSERT(index < zpl__ah->count); \ + zpl_memmove(x + index, x + index + 1, zpl_size_of(x[0]) * (zpl__ah->count - index - 1)); \ + --zpl__ah->count; \ + } while (0) + + ZPL_IMPL_INLINE zpl_b8 zpl__array_copy_init(void **y, void **x) { + if (!zpl__array_init_reserve(y, zpl_array_allocator(*x), zpl_array_elem_size(*x), zpl_array_capacity(*x))) + return false; + zpl_memcopy(*y, *x, zpl_array_capacity(*x) * zpl_array_elem_size(*x)); + zpl_array_count(*y) = zpl_array_count(*x); + return true; + } + + #define zpl_array_copy_init(y, x) zpl__array_copy_init(cast(void **) & (y), cast(void **) & (x)) + + #define zpl_array_pop(x) \ + do { \ + ZPL_ASSERT(ZPL_ARRAY_HEADER(x)->count > 0); \ + ZPL_ARRAY_HEADER(x)->count--; \ + } while (0) + #define zpl_array_back(x) x[ZPL_ARRAY_HEADER(x)->count - 1] + #define zpl_array_front(x) x[0] + #define zpl_array_clear(x) \ + do { ZPL_ARRAY_HEADER(x)->count = 0; } while (0) + + ZPL_IMPL_INLINE zpl_b8 zpl__array_resize(void **x, zpl_isize new_count) { + if (ZPL_ARRAY_HEADER(*x)->capacity < new_count) { + if (!zpl__array_grow(x, new_count)) return false; + } + ZPL_ARRAY_HEADER(*x)->count = new_count; + return true; + } + + #define zpl_array_resize(x, new_count) zpl__array_resize(cast(void **) & (x), (new_count)) + + ZPL_IMPL_INLINE zpl_b8 zpl__array_reserve(void **x, zpl_isize new_capacity) { + if (ZPL_ARRAY_HEADER(*x)->capacity < new_capacity) return zpl__array_set_capacity(x, new_capacity); + return true; + } + + #define zpl_array_reserve(x, new_capacity) zpl__array_reserve(cast(void **) & (x), (new_capacity)) + + ZPL_END_C_DECLS + // file: header/essentials/collections/buffer.h + + //////////////////////////////////////////////////////////////// + // + // Fixed Capacity Buffer (POD Types) + // + // + // zpl_buffer(Type) works like zpl_string or zpl_array where the actual type is just a pointer to the first + // element. + // + // Available Procedures for zpl_buffer(Type) + // zpl_buffer_init + // zpl_buffer_free + // zpl_buffer_append + // zpl_buffer_appendv + // zpl_buffer_pop + // zpl_buffer_clear + + + ZPL_BEGIN_C_DECLS + + typedef struct zpl_buffer_header { + zpl_allocator backing; + zpl_isize count; + zpl_isize capacity; + } zpl_buffer_header; + + #define zpl_buffer(Type) Type * + + #define zpl_buffer_make(Type, Name, allocator, cap) Type *Name; zpl_buffer_init(Name, allocator, cap) + + #define ZPL_BUFFER_HEADER(x) (cast(zpl_buffer_header *)(x) - 1) + #define zpl_buffer_count(x) (ZPL_BUFFER_HEADER(x)->count) + #define zpl_buffer_capacity(x) (ZPL_BUFFER_HEADER(x)->capacity) + #define zpl_buffer_end(x) (x + (zpl_buffer_count(x) - 1)) + #define zpl_buffer_allocator(x) (ZPL_BUFFER_HEADER(x)->backing) + + #define zpl_buffer_init(x, allocator, cap) \ + do { \ + void **nx = cast(void **) & (x); \ + zpl_buffer_header *zpl__bh = \ + cast(zpl_buffer_header *) zpl_alloc((allocator), sizeof(zpl_buffer_header) + (cap)*zpl_size_of(*(x))); \ + zpl__bh->backing = allocator; \ + zpl__bh->count = 0; \ + zpl__bh->capacity = cap; \ + *nx = cast(void *)(zpl__bh + 1); \ + } while (0) + + #define zpl_buffer_free(x) (zpl_free(ZPL_BUFFER_HEADER(x)->backing, ZPL_BUFFER_HEADER(x))) + + #define zpl_buffer_append(x, item) \ + do { (x)[zpl_buffer_count(x)++] = (item); } while (0) + + #define zpl_buffer_appendv(x, items, item_count) \ + do { \ + ZPL_ASSERT(zpl_size_of(*(items)) == zpl_size_of(*(x))); \ + ZPL_ASSERT(zpl_buffer_count(x) + item_count <= zpl_buffer_capacity(x)); \ + zpl_memcopy(&(x)[zpl_buffer_count(x)], (items), zpl_size_of(*(x)) * (item_count)); \ + zpl_buffer_count(x) += (item_count); \ + } while (0) + + #define zpl_buffer_copy_init(y, x) \ + do { \ + zpl_buffer_init(y, zpl_buffer_allocator(x), zpl_buffer_capacity(x)); \ + zpl_memcopy(y, x, zpl_buffer_capacity(x) * zpl_size_of(*x)); \ + zpl_buffer_count(y) = zpl_buffer_count(x); \ + } while (0) + + #define zpl_buffer_pop(x) \ + do { \ + ZPL_ASSERT(zpl_buffer_count(x) > 0); \ + zpl_buffer_count(x)--; \ + } while (0) + #define zpl_buffer_clear(x) \ + do { zpl_buffer_count(x) = 0; } while (0) + + ZPL_END_C_DECLS + // file: header/essentials/collections/list.h + + //////////////////////////////////////////////////////////////// + // + // Linked List + // + // zpl_list encapsulates pointer to data and points to the next and the previous element in the list. + // + // Available Procedures for zpl_list + // zpl_list_init + // zpl_list_add + // zpl_list_remove + + + ZPL_BEGIN_C_DECLS + + #if 0 + #define ZPL_IMPLEMENTATION + #include "zpl.h" + int main(void) + { + zpl_list s, *head, *cursor; + zpl_list_init(&s, "it is optional to call init: "); + head = cursor = &s; + + // since we can construct an element implicitly this way + // the second field gets overwritten once we add it to a list. + zpl_list a = {"hello"}; + cursor = zpl_list_add(cursor, &a); + + zpl_list b = {"world"}; + cursor = zpl_list_add(cursor, &b); + + zpl_list c = {"!!! OK"}; + cursor = zpl_list_add(cursor, &c); + + for (zpl_list *l=head; l; l=l->next) { + zpl_printf("%s ", cast(char *)l->ptr); + } + zpl_printf("\n"); + + return 0; + } + #endif + + + typedef struct zpl__list { + void const *ptr; + struct zpl__list *next, *prev; + } zpl_list; + + ZPL_DEF_INLINE void zpl_list_init(zpl_list *list, void const *ptr); + ZPL_DEF_INLINE zpl_list *zpl_list_add(zpl_list *list, zpl_list *item); + + // NOTE(zaklaus): Returns a pointer to the next node (or NULL if the removed node has no trailing node.) + ZPL_DEF_INLINE zpl_list *zpl_list_remove(zpl_list *list); + + + ZPL_IMPL_INLINE void zpl_list_init(zpl_list *list, void const *ptr) { + zpl_list list_ = { 0 }; + *list = list_; + list->ptr = ptr; + } + + ZPL_IMPL_INLINE zpl_list *zpl_list_add(zpl_list *list, zpl_list *item) { + item->next = NULL; + + if (list->next) { item->next = list->next; } + + list->next = item; + item->prev = list; + return item; + } + + ZPL_IMPL_INLINE zpl_list *zpl_list_remove(zpl_list *list) { + if (list->prev) { list->prev->next = list->next; } + + return list->next; + } + + ZPL_END_C_DECLS + // file: header/essentials/collections/ring.h + + //////////////////////////////////////////////////////////////// + // + // Instantiated Circular buffer + // + + /* + Buffer type and function declaration, call: ZPL_RING_DECLARE(PREFIX, FUNC, VALUE) + Buffer function definitions, call: ZPL_RING_DEFINE(PREFIX, FUNC, VALUE) + + PREFIX - a prefix for function prototypes e.g. extern, static, etc. + FUNC - the name will prefix function names + VALUE - the type of the value to be stored + + funcname_init(VALUE * pad, zpl_allocator a, zpl_isize max_size) + funcname_free(VALUE * pad) + funcname_full(VALUE * pad) + funcname_empty(VALUE * pad) + funcname_append(VALUE * pad, type data) + funcname_append_array(VALUE * pad, zpl_array(type) data) + funcname_get(VALUE * pad) + funcname_get_array(VALUE * pad, zpl_usize max_size, zpl_allocator a) + */ + ZPL_BEGIN_C_DECLS + + #define ZPL_RING(PREFIX, FUNC, VALUE) \ + ZPL_RING_DECLARE(PREFIX, FUNC, VALUE); \ + ZPL_RING_DEFINE(FUNC, VALUE); + + #define ZPL_RING_DECLARE(prefix,func,type) \ + typedef struct { \ + zpl_allocator backing; \ + zpl_buffer(type) buf; \ + zpl_usize head, tail; \ + zpl_usize capacity; \ + } ZPL_JOIN2(func, type); \ + \ + prefix void ZPL_JOIN2(func, init)(ZPL_JOIN2(func, type) * pad, zpl_allocator a, zpl_isize max_size); \ + prefix void ZPL_JOIN2(func, free)(ZPL_JOIN2(func, type) * pad); \ + prefix zpl_b32 ZPL_JOIN2(func, full)(ZPL_JOIN2(func, type) * pad); \ + prefix zpl_b32 ZPL_JOIN2(func, empty)(ZPL_JOIN2(func, type) * pad); \ + prefix void ZPL_JOIN2(func, append)(ZPL_JOIN2(func, type) * pad, type data); \ + prefix void ZPL_JOIN2(func, append_array)(ZPL_JOIN2(func, type) * pad, zpl_array(type) data); \ + prefix type *ZPL_JOIN2(func, get)(ZPL_JOIN2(func, type) * pad); \ + prefix zpl_array(type) \ + ZPL_JOIN2(func, get_array)(ZPL_JOIN2(func, type) * pad, zpl_usize max_size, zpl_allocator a); + + #define ZPL_RING_DEFINE(func,type) \ + void ZPL_JOIN2(func, init)(ZPL_JOIN2(func, type) * pad, zpl_allocator a, zpl_isize max_size) { \ + ZPL_JOIN2(func, type) pad_ = { 0 }; \ + *pad = pad_; \ + \ + pad->backing = a; \ + zpl_buffer_init(pad->buf, a, max_size + 1); \ + pad->capacity = max_size + 1; \ + pad->head = pad->tail = 0; \ + } \ + void ZPL_JOIN2(func, free)(ZPL_JOIN2(func, type) * pad) { \ + zpl_buffer_free(pad->buf); \ + } \ + \ + zpl_b32 ZPL_JOIN2(func, full)(ZPL_JOIN2(func, type) * pad) { \ + return ((pad->head + 1) % pad->capacity) == pad->tail; \ + } \ + \ + zpl_b32 ZPL_JOIN2(func, empty)(ZPL_JOIN2(func, type) * pad) { return pad->head == pad->tail; } \ + \ + void ZPL_JOIN2(func, append)(ZPL_JOIN2(func, type) * pad, type data) { \ + pad->buf[pad->head] = data; \ + pad->head = (pad->head + 1) % pad->capacity; \ + \ + if (pad->head == pad->tail) { pad->tail = (pad->tail + 1) % pad->capacity; } \ + } \ + \ + void ZPL_JOIN2(func, append_array)(ZPL_JOIN2(func, type) * pad, zpl_array(type) data) { \ + zpl_usize c = zpl_array_count(data); \ + for (zpl_usize i = 0; i < c; ++i) { ZPL_JOIN2(func, append)(pad, data[i]); } \ + } \ + \ + type *ZPL_JOIN2(func, get)(ZPL_JOIN2(func, type) * pad) { \ + if (ZPL_JOIN2(func, empty)(pad)) { return NULL; } \ + \ + type *data = &pad->buf[pad->tail]; \ + pad->tail = (pad->tail + 1) % pad->capacity; \ + \ + return data; \ + } \ + \ + zpl_array(type) \ + ZPL_JOIN2(func, get_array)(ZPL_JOIN2(func, type) * pad, zpl_usize max_size, zpl_allocator a) { \ + zpl_array(type) vals = 0; \ + zpl_array_init(vals, a); \ + while (--max_size && !ZPL_JOIN2(func, empty)(pad)) { \ + zpl_array_append(vals, *ZPL_JOIN2(func, get)(pad)); \ + } \ + return vals; \ + } + + ZPL_END_C_DECLS + // file: header/essentials/collections/hashtable.h + + /** @file hashtable.c + @brief Instantiated hash table + @defgroup hashtable Instantiated hash table + + + This is an attempt to implement a templated hash table + NOTE: The key is always a zpl_u64 for simplicity and you will _probably_ _never_ need anything bigger. + + Hash table type and function declaration, call: ZPL_TABLE_DECLARE(PREFIX, NAME, FUNC, VALUE) + Hash table function definitions, call: ZPL_TABLE_DEFINE(NAME, FUNC, VALUE) + + PREFIX - a prefix for function prototypes e.g. extern, static, etc. + NAME - Name of the Hash Table + FUNC - the name will prefix function names + VALUE - the type of the value to be stored + + tablename_init(NAME * h, zpl_allocator a); + tablename_destroy(NAME * h); + tablename_get(NAME * h, zpl_u64 key); + tablename_set(NAME * h, zpl_u64 key, VALUE value); + tablename_grow(NAME * h); + tablename_map(NAME * h, void (*map_proc)(zpl_u64 key, VALUE value)) + tablename_map_mut(NAME * h, void (*map_proc)(zpl_u64 key, VALUE * value)) + tablename_rehash(NAME * h, zpl_isize new_count); + tablename_remove(NAME * h, zpl_u64 key); + + @{ + */ + + + ZPL_BEGIN_C_DECLS + + typedef struct zpl_hash_table_find_result { + zpl_isize hash_index; + zpl_isize entry_prev; + zpl_isize entry_index; + } zpl_hash_table_find_result; + + /** + * Combined macro for a quick delcaration + definition + */ + + #define ZPL_TABLE(PREFIX, NAME, FUNC, VALUE) \ + ZPL_TABLE_DECLARE(PREFIX, NAME, FUNC, VALUE); \ + ZPL_TABLE_DEFINE(NAME, FUNC, VALUE); + + /** + * Table delcaration macro that generates the interface + */ + + #define ZPL_TABLE_DECLARE(PREFIX, NAME, FUNC, VALUE) \ + typedef struct ZPL_JOIN2(NAME, Entry) { \ + zpl_u64 key; \ + zpl_isize next; \ + VALUE value; \ + } ZPL_JOIN2(NAME, Entry); \ + \ + typedef struct NAME { \ + zpl_array(zpl_isize) hashes; \ + zpl_array(ZPL_JOIN2(NAME, Entry)) entries; \ + } NAME; \ + \ + PREFIX void ZPL_JOIN2(FUNC, init) (NAME *h, zpl_allocator a); \ + PREFIX void ZPL_JOIN2(FUNC, destroy) (NAME *h); \ + PREFIX void ZPL_JOIN2(FUNC, clear) (NAME *h); \ + PREFIX VALUE *ZPL_JOIN2(FUNC, get) (NAME *h, zpl_u64 key); \ + PREFIX zpl_isize ZPL_JOIN2(FUNC, slot) (NAME *h, zpl_u64 key); \ + PREFIX void ZPL_JOIN2(FUNC, set) (NAME *h, zpl_u64 key, VALUE value); \ + PREFIX void ZPL_JOIN2(FUNC, grow) (NAME *h); \ + PREFIX void ZPL_JOIN2(FUNC, rehash) (NAME *h, zpl_isize new_count); \ + PREFIX void ZPL_JOIN2(FUNC, rehash_fast) (NAME *h); \ + PREFIX void ZPL_JOIN2(FUNC, map) (NAME *h, void (*map_proc) (zpl_u64 key, VALUE value)); \ + PREFIX void ZPL_JOIN2(FUNC, map_mut) (NAME *h, void (*map_proc) (zpl_u64 key, VALUE * value)); \ + PREFIX void ZPL_JOIN2(FUNC, remove) (NAME *h, zpl_u64 key); \ + PREFIX void ZPL_JOIN2(FUNC, remove_entry) (NAME *h, zpl_isize idx); + + /** + * Table definition interfaces that generates the implementation + */ + + #define ZPL_TABLE_DEFINE(NAME, FUNC, VALUE) \ + void ZPL_JOIN2(FUNC, init)(NAME * h, zpl_allocator a) { \ + zpl_array_init(h->hashes, a); \ + zpl_array_init(h->entries, a); \ + } \ + \ + void ZPL_JOIN2(FUNC, destroy)(NAME * h) { \ + if (h->entries) zpl_array_free(h->entries); \ + if (h->hashes) zpl_array_free(h->hashes); \ + } \ + \ + void ZPL_JOIN2(FUNC, clear)(NAME * h) { \ + for (int i = 0; i < zpl_array_count(h->hashes); i++) h->hashes[i] = -1; \ + zpl_array_clear(h->entries); \ + } \ + \ + zpl_isize ZPL_JOIN2(FUNC, slot)(NAME * h, zpl_u64 key) { \ + for (zpl_isize i = 0; i < zpl_array_count(h->entries); i++) { \ + if (h->entries[i].key == key) { \ + return i; \ + } \ + } \ + return -1; \ + } \ + \ + zpl_internal zpl_isize ZPL_JOIN2(FUNC, _add_entry)(NAME * h, zpl_u64 key) { \ + zpl_isize index; \ + ZPL_JOIN2(NAME, Entry) e = { 0 }; \ + e.key = key; \ + e.next = -1; \ + index = zpl_array_count(h->entries); \ + zpl_array_append(h->entries, e); \ + return index; \ + } \ + \ + zpl_internal zpl_hash_table_find_result ZPL_JOIN2(FUNC, _find)(NAME * h, zpl_u64 key) { \ + zpl_hash_table_find_result r = { -1, -1, -1 }; \ + if (zpl_array_count(h->hashes) > 0) { \ + r.hash_index = key % zpl_array_count(h->hashes); \ + r.entry_index = h->hashes[r.hash_index]; \ + while (r.entry_index >= 0) { \ + if (h->entries[r.entry_index].key == key) return r; \ + r.entry_prev = r.entry_index; \ + r.entry_index = h->entries[r.entry_index].next; \ + } \ + } \ + return r; \ + } \ + \ + zpl_internal zpl_b32 ZPL_JOIN2(FUNC, _full)(NAME * h) { \ + return 0.75f * zpl_array_count(h->hashes) < zpl_array_count(h->entries); \ + } \ + \ + void ZPL_JOIN2(FUNC, grow)(NAME * h) { \ + zpl_isize new_count = ZPL_ARRAY_GROW_FORMULA(zpl_array_count(h->entries)); \ + ZPL_JOIN2(FUNC, rehash)(h, new_count); \ + } \ + \ + void ZPL_JOIN2(FUNC, rehash)(NAME * h, zpl_isize new_count) { \ + zpl_isize i, j; \ + NAME nh = { 0 }; \ + ZPL_JOIN2(FUNC, init)(&nh, zpl_array_allocator(h->hashes)); \ + zpl_array_resize(nh.hashes, new_count); \ + zpl_array_reserve(nh.entries, zpl_array_count(h->entries)); \ + for (i = 0; i < new_count; i++) nh.hashes[i] = -1; \ + for (i = 0; i < zpl_array_count(h->entries); i++) { \ + ZPL_JOIN2(NAME, Entry) * e; \ + zpl_hash_table_find_result fr; \ + if (zpl_array_count(nh.hashes) == 0) ZPL_JOIN2(FUNC, grow)(&nh); \ + e = &h->entries[i]; \ + fr = ZPL_JOIN2(FUNC, _find)(&nh, e->key); \ + j = ZPL_JOIN2(FUNC, _add_entry)(&nh, e->key); \ + if (fr.entry_prev < 0) \ + nh.hashes[fr.hash_index] = j; \ + else \ + nh.entries[fr.entry_prev].next = j; \ + nh.entries[j].next = fr.entry_index; \ + nh.entries[j].value = e->value; \ + } \ + ZPL_JOIN2(FUNC, destroy)(h); \ + h->hashes = nh.hashes; \ + h->entries = nh.entries; \ + } \ + \ + void ZPL_JOIN2(FUNC, rehash_fast)(NAME * h) { \ + zpl_isize i; \ + for (i = 0; i < zpl_array_count(h->entries); i++) h->entries[i].next = -1; \ + for (i = 0; i < zpl_array_count(h->hashes); i++) h->hashes[i] = -1; \ + for (i = 0; i < zpl_array_count(h->entries); i++) { \ + ZPL_JOIN2(NAME, Entry) * e; \ + zpl_hash_table_find_result fr; \ + e = &h->entries[i]; \ + fr = ZPL_JOIN2(FUNC, _find)(h, e->key); \ + if (fr.entry_prev < 0) \ + h->hashes[fr.hash_index] = i; \ + else \ + h->entries[fr.entry_prev].next = i; \ + } \ + } \ + \ + VALUE *ZPL_JOIN2(FUNC, get)(NAME * h, zpl_u64 key) { \ + zpl_isize index = ZPL_JOIN2(FUNC, _find)(h, key).entry_index; \ + if (index >= 0) return &h->entries[index].value; \ + return NULL; \ + } \ + \ + void ZPL_JOIN2(FUNC, remove)(NAME * h, zpl_u64 key) { \ + zpl_hash_table_find_result fr = ZPL_JOIN2(FUNC, _find)(h, key); \ + if (fr.entry_index >= 0) { \ + zpl_array_remove_at(h->entries, fr.entry_index); \ + ZPL_JOIN2(FUNC, rehash_fast)(h); \ + } \ + } \ + \ + void ZPL_JOIN2(FUNC, remove_entry)(NAME * h, zpl_isize idx) { \ + zpl_array_remove_at(h->entries, idx); \ + } \ + \ + void ZPL_JOIN2(FUNC, map)(NAME * h, void (*map_proc)(zpl_u64 key, VALUE value)) { \ + ZPL_ASSERT_NOT_NULL(h); \ + ZPL_ASSERT_NOT_NULL(map_proc); \ + for (zpl_isize i = 0; i < zpl_array_count(h->entries); ++i) { \ + map_proc(h->entries[i].key, h->entries[i].value); \ + } \ + } \ + \ + void ZPL_JOIN2(FUNC, map_mut)(NAME * h, void (*map_proc)(zpl_u64 key, VALUE * value)) { \ + ZPL_ASSERT_NOT_NULL(h); \ + ZPL_ASSERT_NOT_NULL(map_proc); \ + for (zpl_isize i = 0; i < zpl_array_count(h->entries); ++i) { \ + map_proc(h->entries[i].key, &h->entries[i].value); \ + } \ + } \ + \ + void ZPL_JOIN2(FUNC, set)(NAME * h, zpl_u64 key, VALUE value) { \ + zpl_isize index; \ + zpl_hash_table_find_result fr; \ + if (zpl_array_count(h->hashes) == 0) ZPL_JOIN2(FUNC, grow)(h); \ + fr = ZPL_JOIN2(FUNC, _find)(h, key); \ + if (fr.entry_index >= 0) { \ + index = fr.entry_index; \ + } else { \ + index = ZPL_JOIN2(FUNC, _add_entry)(h, key); \ + if (fr.entry_prev >= 0) { \ + h->entries[fr.entry_prev].next = index; \ + } else { \ + h->hashes[fr.hash_index] = index; \ + } \ + } \ + h->entries[index].value = value; \ + if (ZPL_JOIN2(FUNC, _full)(h)) ZPL_JOIN2(FUNC, grow)(h); \ + } + + //! @} + + ZPL_END_C_DECLS +# if defined(ZPL_MODULE_CORE) + // file: header/core/memory_virtual.h + + + //////////////////////////////////////////////////////////////// + // + // Virtual Memory + // + // + + ZPL_BEGIN_C_DECLS + + typedef struct zpl_virtual_memory { + void *data; + zpl_isize size; + } zpl_virtual_memory; + + //! Initialize virtual memory from existing data. + ZPL_DEF zpl_virtual_memory zpl_vm(void *data, zpl_isize size); + + //! Allocate virtual memory at address with size. + + //! @param addr The starting address of the region to reserve. If NULL, it lets operating system to decide where to allocate it. + //! @param size The size to serve. + ZPL_DEF zpl_virtual_memory zpl_vm_alloc(void *addr, zpl_isize size); + + //! Release the virtual memory. + ZPL_DEF zpl_b32 zpl_vm_free(zpl_virtual_memory vm); + + //! Trim virtual memory. + ZPL_DEF zpl_virtual_memory zpl_vm_trim(zpl_virtual_memory vm, zpl_isize lead_size, zpl_isize size); + + //! Purge virtual memory. + ZPL_DEF zpl_b32 zpl_vm_purge(zpl_virtual_memory vm); + + //! Retrieve VM's page size and alignment. + ZPL_DEF zpl_isize zpl_virtual_memory_page_size(zpl_isize *alignment_out); + + ZPL_END_C_DECLS + // file: header/core/string.h + + /** @file string.c + @brief String operations and library + @defgroup string String library + + Offers methods for c-string manipulation, but also a string library based on gb_string, which is c-string friendly. + + @{ + */ + + //////////////////////////////////////////////////////////////// + // + // Char Functions + // + // + + + ZPL_BEGIN_C_DECLS + + ZPL_DEF_INLINE char zpl_char_to_lower(char c); + ZPL_DEF_INLINE char zpl_char_to_upper(char c); + ZPL_DEF_INLINE zpl_b32 zpl_char_is_space(char c); + ZPL_DEF_INLINE zpl_b32 zpl_char_is_digit(char c); + ZPL_DEF_INLINE zpl_b32 zpl_char_is_hex_digit(char c); + ZPL_DEF_INLINE zpl_b32 zpl_char_is_alpha(char c); + ZPL_DEF_INLINE zpl_b32 zpl_char_is_alphanumeric(char c); + ZPL_DEF_INLINE zpl_i32 zpl_digit_to_int(char c); + ZPL_DEF_INLINE zpl_i32 zpl_hex_digit_to_int(char c); + ZPL_DEF_INLINE zpl_u8 zpl_char_to_hex_digit(char c); + ZPL_DEF_INLINE zpl_b32 zpl_char_is_control(char c); + + // NOTE: ASCII only + ZPL_DEF_INLINE void zpl_str_to_lower(char *str); + ZPL_DEF_INLINE void zpl_str_to_upper(char *str); + + ZPL_DEF_INLINE char const *zpl_str_trim(char const *str, zpl_b32 catch_newline); + ZPL_DEF_INLINE char const *zpl_str_skip(char const *str, char c); + ZPL_DEF_INLINE char const *zpl_str_skip_any(char const *str, char const*char_list); + ZPL_DEF_INLINE char const *zpl_str_skip_literal(char const *str, char c); + ZPL_DEF_INLINE char const *zpl_str_control_skip(char const *str, char c); + + ZPL_DEF_INLINE zpl_isize zpl_strlen(const char *str); + ZPL_DEF_INLINE zpl_isize zpl_strnlen(const char *str, zpl_isize max_len); + ZPL_DEF_INLINE zpl_i32 zpl_strcmp(const char *s1, const char *s2); + ZPL_DEF_INLINE zpl_i32 zpl_strncmp(const char *s1, const char *s2, zpl_isize len); + ZPL_DEF_INLINE char *zpl_strcpy(char *dest, const char *source); + ZPL_DEF_INLINE char *zpl_strcat(char *dest, const char *source); + ZPL_DEF_INLINE char *zpl_strncpy(char *dest, const char *source, zpl_isize len); + ZPL_DEF_INLINE zpl_isize zpl_strlcpy(char *dest, const char *source, zpl_isize len); + ZPL_DEF_INLINE char *zpl_strrev(char *str); // NOTE: ASCII only + ZPL_DEF_INLINE const char *zpl_strtok(char *output, const char *src, const char *delimit); + ZPL_DEF_INLINE const char *zpl_strntok(char *output, zpl_isize len, const char *src, const char *delimit); + + ZPL_DEF_INLINE char *zpl_strdup(zpl_allocator a, char *src, zpl_isize max_len); + ZPL_DEF_INLINE char **zpl_str_split_lines(zpl_allocator alloc, char *source, zpl_b32 strip_whitespace); + + #define zpl_str_expand(str) str, zpl_strlen(str) + #define zpl_str_advance_while(str, cond) \ + do { \ + ++str; \ + } while ((cond)); + + ZPL_DEF_INLINE zpl_b32 zpl_str_has_prefix(const char *str, const char *prefix); + ZPL_DEF_INLINE zpl_b32 zpl_str_has_suffix(const char *str, const char *suffix); + + ZPL_DEF_INLINE const char *zpl_char_first_occurence(const char *str, char c); + ZPL_DEF_INLINE const char *zpl_char_last_occurence(const char *str, char c); + #define zpl_strchr zpl_char_first_occurence + + ZPL_DEF_INLINE void zpl_str_concat(char *dest, zpl_isize dest_len, const char *src_a, zpl_isize src_a_len, const char *src_b, zpl_isize src_b_len); + + ZPL_DEF zpl_u64 zpl_str_to_u64(const char *str, char **end_ptr, zpl_i32 base); + ZPL_DEF zpl_i64 zpl_str_to_i64(const char *str, char **end_ptr, zpl_i32 base); + ZPL_DEF zpl_f64 zpl_str_to_f64(const char *str, char **end_ptr); + ZPL_DEF void zpl_i64_to_str(zpl_i64 value, char *string, zpl_i32 base); + ZPL_DEF void zpl_u64_to_str(zpl_u64 value, char *string, zpl_i32 base); + + ZPL_DEF_INLINE zpl_f32 zpl_str_to_f32(const char *str, char **end_ptr); + + //////////////////////////////////////////////////////////////// + // + // UTF-8 Handling + // + // + + // NOTE: Does not check if utf-8 string is valid + ZPL_IMPL_INLINE zpl_isize zpl_utf8_strlen(zpl_u8 const *str); + ZPL_IMPL_INLINE zpl_isize zpl_utf8_strnlen(zpl_u8 const *str, zpl_isize max_len); + + // NOTE: Windows doesn't handle 8 bit filenames well + ZPL_DEF zpl_u16 *zpl_utf8_to_ucs2(zpl_u16 *buffer, zpl_isize len, zpl_u8 const *str); + ZPL_DEF zpl_u8 *zpl_ucs2_to_utf8(zpl_u8 *buffer, zpl_isize len, zpl_u16 const *str); + ZPL_DEF zpl_u16 *zpl_utf8_to_ucs2_buf(zpl_u8 const *str); // NOTE: Uses locally persisting buffer + ZPL_DEF zpl_u8 *zpl_ucs2_to_utf8_buf(zpl_u16 const *str); // NOTE: Uses locally persisting buffer + + // NOTE: Returns size of codepoint in bytes + ZPL_DEF zpl_isize zpl_utf8_decode(zpl_u8 const *str, zpl_isize str_len, zpl_rune *codepoint); + ZPL_DEF zpl_isize zpl_utf8_codepoint_size(zpl_u8 const *str, zpl_isize str_len); + ZPL_DEF zpl_isize zpl_utf8_encode_rune(zpl_u8 buf[4], zpl_rune r); + + /* inlines */ + + ZPL_IMPL_INLINE char zpl_char_to_lower(char c) { + if (c >= 'A' && c <= 'Z') return 'a' + (c - 'A'); + return c; + } + + ZPL_IMPL_INLINE char zpl_char_to_upper(char c) { + if (c >= 'a' && c <= 'z') return 'A' + (c - 'a'); + return c; + } + + ZPL_IMPL_INLINE zpl_b32 zpl_char_is_space(char c) { + if (c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\f' || c == '\v') return true; + return false; + } + + ZPL_IMPL_INLINE zpl_b32 zpl_char_is_digit(char c) { + if (c >= '0' && c <= '9') return true; + return false; + } + + ZPL_IMPL_INLINE zpl_b32 zpl_char_is_hex_digit(char c) { + if (zpl_char_is_digit(c) || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')) return true; + return false; + } + + ZPL_IMPL_INLINE zpl_b32 zpl_char_is_alpha(char c) { + if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')) return true; + return false; + } + + ZPL_IMPL_INLINE zpl_b32 zpl_char_is_alphanumeric(char c) { return zpl_char_is_alpha(c) || zpl_char_is_digit(c); } + + ZPL_IMPL_INLINE zpl_i32 zpl_digit_to_int(char c) { return zpl_char_is_digit(c) ? c - '0' : c - 'W'; } + + ZPL_IMPL_INLINE zpl_i32 zpl_hex_digit_to_int(char c) { + if (zpl_char_is_digit(c)) + return zpl_digit_to_int(c); + else if (zpl_is_between(c, 'a', 'f')) + return c - 'a' + 10; + else if (zpl_is_between(c, 'A', 'F')) + return c - 'A' + 10; + return -1; + } + + ZPL_IMPL_INLINE zpl_u8 zpl_char_to_hex_digit(char c) { + if (c >= '0' && c <= '9') + return (zpl_u8)(c - '0'); + if (c >= 'a' && c <= 'f') + return (zpl_u8)(c - 'a'); + if (c >= 'A' && c <= 'F') + return (zpl_u8)(c - 'A'); + return 0; + } + + ZPL_IMPL_INLINE void zpl_str_to_lower(char *str) { + if (!str) return; + while (*str) { + *str = zpl_char_to_lower(*str); + str++; + } + } + + ZPL_IMPL_INLINE void zpl_str_to_upper(char *str) { + if (!str) return; + while (*str) { + *str = zpl_char_to_upper(*str); + str++; + } + } + + ZPL_IMPL_INLINE zpl_isize zpl_strlen(const char *str) { + if (str == NULL) { return 0; } + const char *p = str; + while (*str) str++; + return str-p; + } + + ZPL_IMPL_INLINE zpl_isize zpl_strnlen(const char *str, zpl_isize max_len) { + const char *end = cast(const char *) zpl_memchr(str, 0, max_len); + if (end) return end - str; + return max_len; + } + + ZPL_IMPL_INLINE zpl_isize zpl_utf8_strlen(zpl_u8 const *str) { + zpl_isize count = 0; + for (; *str; count++) { + zpl_u8 c = *str; + zpl_isize inc = 0; + if (c < 0x80) + inc = 1; + else if ((c & 0xe0) == 0xc0) + inc = 2; + else if ((c & 0xf0) == 0xe0) + inc = 3; + else if ((c & 0xf8) == 0xf0) + inc = 4; + else + return -1; + + str += inc; + } + return count; + } + + ZPL_IMPL_INLINE zpl_isize zpl_utf8_strnlen(zpl_u8 const *str, zpl_isize max_len) { + zpl_isize count = 0; + for (; *str && max_len > 0; count++) { + zpl_u8 c = *str; + zpl_isize inc = 0; + if (c < 0x80) + inc = 1; + else if ((c & 0xe0) == 0xc0) + inc = 2; + else if ((c & 0xf0) == 0xe0) + inc = 3; + else if ((c & 0xf8) == 0xf0) + inc = 4; + else + return -1; + + str += inc; + max_len -= inc; + } + return count; + } + + ZPL_IMPL_INLINE zpl_i32 zpl_strcmp(const char *s1, const char *s2) { + while (*s1 && (*s1 == *s2)) { s1++, s2++; } + return *(zpl_u8 *)s1 - *(zpl_u8 *)s2; + } + + ZPL_IMPL_INLINE char *zpl_strcpy(char *dest, const char *source) { + ZPL_ASSERT_NOT_NULL(dest); + if (source) { + char *str = dest; + while (*source) *str++ = *source++; + } + return dest; + } + + ZPL_IMPL_INLINE char *zpl_strcat(char *dest, const char *source) { + ZPL_ASSERT_NOT_NULL(dest); + if (source) { + char *str = dest; + while (*str) ++str; + while (*source) *str++ = *source++; + } + return dest; + } + + ZPL_IMPL_INLINE char *zpl_strncpy(char *dest, const char *source, zpl_isize len) { + ZPL_ASSERT_NOT_NULL(dest); + if (source) { + char *str = dest; + while (len > 0 && *source) { + *str++ = *source++; + len--; + } + while (len > 0) { + *str++ = '\0'; + len--; + } + } + return dest; + } + + ZPL_IMPL_INLINE zpl_isize zpl_strlcpy(char *dest, const char *source, zpl_isize len) { + zpl_isize result = 0; + ZPL_ASSERT_NOT_NULL(dest); + if (source) { + const char *source_start = source; + char *str = dest; + while (len > 0 && *source) { + *str++ = *source++; + len--; + } + while (len > 0) { + *str++ = '\0'; + len--; + } + + result = source - source_start; + } + return result; + } + + ZPL_IMPL_INLINE char *zpl_strrev(char *str) { + zpl_isize len = zpl_strlen(str); + char *a = str + 0; + char *b = str + len - 1; + len /= 2; + while (len--) { + zpl_swap(char, *a, *b); + a++, b--; + } + return str; + } + + ZPL_IMPL_INLINE zpl_i32 zpl_strncmp(const char *s1, const char *s2, zpl_isize len) { + for (; len > 0; s1++, s2++, len--) { + if (*s1 != *s2) + return ((s1 < s2) ? -1 : +1); + else if (*s1 == '\0') + return 0; + } + return 0; + } + + ZPL_IMPL_INLINE const char *zpl_strtok(char *output, const char *src, const char *delimit) { + while (*src && zpl_char_first_occurence(delimit, *src) == NULL) *output++ = *src++; + + *output = 0; + return *src ? src + 1 : src; + } + + ZPL_IMPL_INLINE const char *zpl_strntok(char *output, zpl_isize len, const char *src, const char *delimit) { + ZPL_ASSERT(len > 0); + *(output+len-1) = 0; + while (*src && zpl_char_first_occurence(delimit, *src) == NULL && len > 0) { + *output++ = *src++; + len --; + } + + if (len > 0) + *output = 0; + return *src ? src + 1 : src; + } + + ZPL_IMPL_INLINE zpl_b32 zpl_char_is_control(char c) { + return !!zpl_strchr("\"\\/bfnrt", c); + } + + ZPL_IMPL_INLINE zpl_b32 zpl__is_special_char(char c) { return !!zpl_strchr("<>:/", c); } + ZPL_IMPL_INLINE zpl_b32 zpl__is_assign_char(char c) { return !!zpl_strchr(":=|", c); } + ZPL_IMPL_INLINE zpl_b32 zpl__is_delim_char(char c) { return !!zpl_strchr(",|\n", c); } + + + ZPL_IMPL_INLINE char const *zpl_str_control_skip(char const *str, char c) { + while ((*str && *str != c) || (*(str - 1) == '\\' && *str == c && zpl_char_is_control(c))) { ++str; } + + return str; + } + + + ZPL_IMPL_INLINE zpl_b32 zpl_str_has_prefix(const char *str, const char *prefix) { + while (*prefix) { + if (*str++ != *prefix++) return false; + } + return true; + } + + ZPL_IMPL_INLINE zpl_b32 zpl_str_has_suffix(const char *str, const char *suffix) { + zpl_isize i = zpl_strlen(str); + zpl_isize j = zpl_strlen(suffix); + if (j <= i) return zpl_strcmp(str + i - j, suffix) == 0; + return false; + } + + ZPL_IMPL_INLINE const char *zpl_char_first_occurence(const char *s, char c) { + char ch = c; + for (; *s != ch; s++) { + if (*s == '\0') return NULL; + } + return s; + } + + ZPL_IMPL_INLINE const char *zpl_char_last_occurence(const char *s, char c) { + char *result = (char*)NULL; + do { + if (*s == c) result = (char *)s; + } while (*s++); + + return result; + } + + ZPL_IMPL_INLINE char const *zpl_str_trim(char const *str, zpl_b32 catch_newline) + { + while (*str && zpl_char_is_space(*str) && (!catch_newline || (catch_newline && *str != '\n'))) { ++str; } + return str; + } + + ZPL_IMPL_INLINE char const *zpl_str_skip(char const *str, char c) { + while (*str && *str != c) { ++str; } + return str; + } + + ZPL_IMPL_INLINE char const *zpl_str_skip_any(char const *str, char const*char_list) { + char const *closest_ptr = cast(char const *) zpl_ptr_add((void*)str, zpl_strlen(str)); + zpl_isize char_list_count = zpl_strlen(char_list); + for (zpl_isize i = 0; i < char_list_count; i++) { + char const *p = zpl_str_skip(str, char_list[i]); + closest_ptr = zpl_min(closest_ptr, p); + } + return closest_ptr; + } + + ZPL_IMPL_INLINE char const *zpl_str_skip_literal(char const *str, char c) { + if (*str == '\0' || *str == c) + return str; + str++; + + while ((*str && *str != c) || (*str == c && *(str-1) == '\\')) { ++str; } + return str; + } + + ZPL_IMPL_INLINE void zpl_str_concat(char *dest, zpl_isize dest_len, const char *src_a, zpl_isize src_a_len, const char *src_b, + zpl_isize src_b_len) { + ZPL_ASSERT(dest_len >= src_a_len + src_b_len + 1); + if (dest) { + zpl_memcopy(dest, src_a, src_a_len); + zpl_memcopy(dest + src_a_len, src_b, src_b_len); + dest[src_a_len + src_b_len] = '\0'; + } + } + + ZPL_IMPL_INLINE zpl_f32 zpl_str_to_f32(const char *str, char **end_ptr) { + zpl_f64 f = zpl_str_to_f64(str, end_ptr); + zpl_f32 r = cast(zpl_f32) f; + return r; + } + + ZPL_IMPL_INLINE char *zpl_strdup(zpl_allocator a, char *src, zpl_isize max_len) { + ZPL_ASSERT_NOT_NULL(src); + zpl_isize len = zpl_strlen(src); + char *dest = cast(char *) zpl_alloc(a, max_len); + zpl_memset(dest + len, 0, max_len - len); + zpl_strncpy(dest, src, max_len); + + return dest; + } + + ZPL_IMPL_INLINE char **zpl_str_split_lines(zpl_allocator alloc, char *source, zpl_b32 strip_whitespace) { + char **lines = NULL, *p = source, *pd = p; + zpl_array_init(lines, alloc); + + while (*p) { + if (*pd == '\n') { + *pd = 0; + if (*(pd - 1) == '\r') *(pd - 1) = 0; + if (strip_whitespace && (pd - p) == 0) { + p = pd + 1; + continue; + } + zpl_array_append(lines, p); + p = pd + 1; + } + ++pd; + } + return lines; + } + + ZPL_END_C_DECLS + // file: header/core/stringlib.h + + + ZPL_BEGIN_C_DECLS + + typedef char *zpl_string; + + typedef struct zpl_string_header { + zpl_allocator allocator; + zpl_isize length; + zpl_isize capacity; + } zpl_string_header; + + #define ZPL_STRING_HEADER(str) (cast(zpl_string_header *)(str) - 1) + + ZPL_DEF zpl_string zpl_string_make_reserve(zpl_allocator a, zpl_isize capacity); + ZPL_DEF zpl_string zpl_string_make_length(zpl_allocator a, void const *str, zpl_isize num_bytes); + ZPL_DEF zpl_string zpl_string_sprintf(zpl_allocator a, char *buf, zpl_isize num_bytes, const char *fmt, ...); + ZPL_DEF zpl_string zpl_string_sprintf_buf(zpl_allocator a, const char *fmt, ...); // NOTE: Uses locally persistent buffer + ZPL_DEF zpl_string zpl_string_append_length(zpl_string str, void const *other, zpl_isize num_bytes); + ZPL_DEF zpl_string zpl_string_appendc(zpl_string str, const char *other); + ZPL_DEF zpl_string zpl_string_join(zpl_allocator a, const char **parts, zpl_isize count, const char *glue); + ZPL_DEF zpl_string zpl_string_set(zpl_string str, const char *cstr); + ZPL_DEF zpl_string zpl_string_make_space_for(zpl_string str, zpl_isize add_len); + ZPL_DEF zpl_isize zpl_string_allocation_size(zpl_string const str); + ZPL_DEF zpl_b32 zpl_string_are_equal(zpl_string const lhs, zpl_string const rhs); + ZPL_DEF zpl_string zpl_string_trim(zpl_string str, const char *cut_set); + ZPL_DEF zpl_string zpl_string_append_rune(zpl_string str, zpl_rune r); + ZPL_DEF zpl_string zpl_string_append_fmt(zpl_string str, const char *fmt, ...); + + ZPL_DEF_INLINE zpl_string zpl_string_make(zpl_allocator a, const char *str); + ZPL_DEF_INLINE void zpl_string_free(zpl_string str); + ZPL_DEF_INLINE void zpl_string_clear(zpl_string str); + ZPL_DEF_INLINE zpl_string zpl_string_duplicate(zpl_allocator a, zpl_string const str); + ZPL_DEF_INLINE zpl_isize zpl_string_length(zpl_string const str); + ZPL_DEF_INLINE zpl_isize zpl_string_capacity(zpl_string const str); + ZPL_DEF_INLINE zpl_isize zpl_string_available_space(zpl_string const str); + ZPL_DEF_INLINE zpl_string zpl_string_append(zpl_string str, zpl_string const other); + ZPL_DEF_INLINE zpl_string zpl_string_trim_space(zpl_string str); // Whitespace ` \t\r\n\v\f` + ZPL_DEF_INLINE void zpl__set_string_length(zpl_string str, zpl_isize len); + ZPL_DEF_INLINE void zpl__set_string_capacity(zpl_string str, zpl_isize cap); + + ZPL_IMPL_INLINE void zpl__set_string_length(zpl_string str, zpl_isize len) { ZPL_STRING_HEADER(str)->length = len; } + ZPL_IMPL_INLINE void zpl__set_string_capacity(zpl_string str, zpl_isize cap) { ZPL_STRING_HEADER(str)->capacity = cap; } + ZPL_IMPL_INLINE zpl_string zpl_string_make(zpl_allocator a, const char *str) { + zpl_isize len = str ? zpl_strlen(str) : 0; + return zpl_string_make_length(a, str, len); + } + + ZPL_IMPL_INLINE void zpl_string_free(zpl_string str) { + if (str) { + zpl_string_header *header = ZPL_STRING_HEADER(str); + zpl_free(header->allocator, header); + } + } + + ZPL_IMPL_INLINE zpl_string zpl_string_duplicate(zpl_allocator a, zpl_string const str) { + return zpl_string_make_length(a, str, zpl_string_length(str)); + } + + ZPL_IMPL_INLINE zpl_isize zpl_string_length(zpl_string const str) { return ZPL_STRING_HEADER(str)->length; } + ZPL_IMPL_INLINE zpl_isize zpl_string_capacity(zpl_string const str) { return ZPL_STRING_HEADER(str)->capacity; } + + ZPL_IMPL_INLINE zpl_isize zpl_string_available_space(zpl_string const str) { + zpl_string_header *h = ZPL_STRING_HEADER(str); + if (h->capacity > h->length) return h->capacity - h->length; + return 0; + } + + ZPL_IMPL_INLINE void zpl_string_clear(zpl_string str) { + zpl__set_string_length(str, 0); + str[0] = '\0'; + } + + ZPL_IMPL_INLINE zpl_string zpl_string_append(zpl_string str, zpl_string const other) { + return zpl_string_append_length(str, other, zpl_string_length(other)); + } + + ZPL_IMPL_INLINE zpl_string zpl_string_trim_space(zpl_string str) { return zpl_string_trim(str, " \t\r\n\v\f"); } + + + ZPL_END_C_DECLS + // file: header/core/file.h + + /** @file file.c + @brief File handling + @defgroup fileio File handling + + File I/O operations as well as path and folder structure manipulation methods. With threading enabled, it also offers async read/write methods. + + @{ + */ + + ZPL_BEGIN_C_DECLS + + typedef zpl_u32 zpl_file_mode; + + typedef enum zpl_file_mode_flag { + ZPL_FILE_MODE_READ = ZPL_BIT(0), + ZPL_FILE_MODE_WRITE = ZPL_BIT(1), + ZPL_FILE_MODE_APPEND = ZPL_BIT(2), + ZPL_FILE_MODE_RW = ZPL_BIT(3), + ZPL_FILE_MODES = ZPL_FILE_MODE_READ | ZPL_FILE_MODE_WRITE | ZPL_FILE_MODE_APPEND | ZPL_FILE_MODE_RW, + } zpl_file_mode_flag; + + // NOTE: Only used internally and for the file operations + typedef enum zpl_seek_whence_type { + ZPL_SEEK_WHENCE_BEGIN = 0, + ZPL_SEEK_WHENCE_CURRENT = 1, + ZPL_SEEK_WHENCE_END = 2, + } zpl_seek_whence_type; + + typedef enum zpl_file_error { + ZPL_FILE_ERROR_NONE, + ZPL_FILE_ERROR_INVALID, + ZPL_FILE_ERROR_INVALID_FILENAME, + ZPL_FILE_ERROR_EXISTS, + ZPL_FILE_ERROR_NOT_EXISTS, + ZPL_FILE_ERROR_PERMISSION, + ZPL_FILE_ERROR_TRUNCATION_FAILURE, + ZPL_FILE_ERROR_NOT_EMPTY, + ZPL_FILE_ERROR_NAME_TOO_LONG, + ZPL_FILE_ERROR_UNKNOWN, + } zpl_file_error; + + typedef union zpl_file_descriptor { + void *p; + zpl_intptr i; + zpl_uintptr u; + } zpl_file_descriptor; + + typedef struct zpl_file_operations zpl_file_operations; + + #define ZPL_FILE_OPEN_PROC(name) zpl_file_error name(zpl_file_descriptor *fd, zpl_file_operations *ops, zpl_file_mode mode, char const *filename) + #define ZPL_FILE_READ_AT_PROC(name) zpl_b32 name(zpl_file_descriptor fd, void *buffer, zpl_isize size, zpl_i64 offset, zpl_isize *bytes_read, zpl_b32 stop_at_newline) + #define ZPL_FILE_WRITE_AT_PROC(name) zpl_b32 name(zpl_file_descriptor fd, void const *buffer, zpl_isize size, zpl_i64 offset, zpl_isize *bytes_written) + #define ZPL_FILE_SEEK_PROC(name) zpl_b32 name(zpl_file_descriptor fd, zpl_i64 offset, zpl_seek_whence_type whence, zpl_i64 *new_offset) + #define ZPL_FILE_CLOSE_PROC(name) void name(zpl_file_descriptor fd) + + typedef ZPL_FILE_OPEN_PROC(zpl_file_open_proc); + typedef ZPL_FILE_READ_AT_PROC(zpl_file_read_proc); + typedef ZPL_FILE_WRITE_AT_PROC(zpl_file_write_proc); + typedef ZPL_FILE_SEEK_PROC(zpl_file_seek_proc); + typedef ZPL_FILE_CLOSE_PROC(zpl_file_close_proc); + + struct zpl_file_operations { + zpl_file_read_proc *read_at; + zpl_file_write_proc *write_at; + zpl_file_seek_proc *seek; + zpl_file_close_proc *close; + }; + + extern zpl_file_operations const zpl_default_file_operations; + + typedef zpl_u64 zpl_file_time; + typedef enum zpl_dir_type { + ZPL_DIR_TYPE_FILE, + ZPL_DIR_TYPE_FOLDER, + ZPL_DIR_TYPE_UNKNOWN, + } zpl_dir_type; + + struct zpl_dir_info; + + typedef struct zpl_dir_entry { + char const *filename; + struct zpl_dir_info *dir_info; + zpl_u8 type; + } zpl_dir_entry; + + typedef struct zpl_dir_info { + char const *fullpath; + zpl_dir_entry *entries; // zpl_array + + // Internals + char **filenames; // zpl_array + zpl_string buf; + } zpl_dir_info; + + typedef struct zpl_file { + zpl_file_operations ops; + zpl_file_descriptor fd; + zpl_b32 is_temp; + + char const *filename; + zpl_file_time last_write_time; + zpl_dir_entry *dir; + } zpl_file; + + typedef enum zpl_file_standard_type { + ZPL_FILE_STANDARD_INPUT, + ZPL_FILE_STANDARD_OUTPUT, + ZPL_FILE_STANDARD_ERROR, + + ZPL_FILE_STANDARD_COUNT, + } zpl_file_standard_type; + + #define ZPL_STDIO_IN zpl_file_get_standard(ZPL_FILE_STANDARD_INPUT) + #define ZPL_STDIO_OUT zpl_file_get_standard(ZPL_FILE_STANDARD_OUTPUT) + #define ZPL_STDIO_ERR zpl_file_get_standard(ZPL_FILE_STANDARD_ERROR) + + /** + * Get standard file I/O. + * @param std Check zpl_file_standard_type + * @return File handle to standard I/O + */ + ZPL_DEF zpl_file *zpl_file_get_standard(zpl_file_standard_type std); + + /** + * Connects a system handle to a zpl file. + * @param file Pointer to zpl file + * @param handle Low-level OS handle to connect + */ + ZPL_DEF void zpl_file_connect_handle(zpl_file *file, void *handle); + + /** + * Creates a new file + * @param file + * @param filename + */ + ZPL_DEF zpl_file_error zpl_file_create(zpl_file *file, char const *filename); + + /** + * Opens a file + * @param file + * @param filename + */ + ZPL_DEF zpl_file_error zpl_file_open(zpl_file *file, char const *filename); + + /** + * Opens a file using a specified mode + * @param file + * @param mode Access mode to use + * @param filename + */ + ZPL_DEF zpl_file_error zpl_file_open_mode(zpl_file *file, zpl_file_mode mode, char const *filename); + + /** + * Constructs a new file from data + * @param file + * @param fd Low-level file descriptor to use + * @param ops File operations to rely upon + * @param filename + */ + ZPL_DEF zpl_file_error zpl_file_new(zpl_file *file, zpl_file_descriptor fd, zpl_file_operations ops, char const *filename); + + /** + * Returns a size of the file + * @param file + * @return File size + */ + ZPL_DEF zpl_i64 zpl_file_size(zpl_file *file); + + /** + * Returns the currently opened file's name + * @param file + */ + ZPL_DEF char const *zpl_file_name(zpl_file *file); + + /** + * Truncates the file by a specified size + * @param file + * @param size Size to truncate + */ + ZPL_DEF zpl_file_error zpl_file_truncate(zpl_file *file, zpl_i64 size); + + /** + * Checks whether a file's been changed since the last check + * @param file + */ + ZPL_DEF zpl_b32 zpl_file_has_changed(zpl_file *file); + + /** + * Retrieves a directory listing relative to the file + * @param file + */ + ZPL_DEF void zpl_file_dirinfo_refresh(zpl_file *file); + + /** + * Creates a temporary file + * @param file + */ + ZPL_DEF zpl_file_error zpl_file_temp(zpl_file *file); + + /** + * Closes the file + * @param file + */ + ZPL_DEF zpl_file_error zpl_file_close(zpl_file *file); + + /** + * Reads file safely + * @param file + * @param buffer Buffer to read to + * @param size Size to read + * @param offset Offset to read from + * @param bytes_read How much data we've actually read + */ + ZPL_DEF_INLINE zpl_b32 zpl_file_read_at_check(zpl_file *file, void *buffer, zpl_isize size, zpl_i64 offset, zpl_isize *bytes_read); + + /** + * Writes to file safely + * @param file + * @param buffer Buffer to read from + * @param size Size to write + * @param offset Offset to write to + * @param bytes_written How much data we've actually written + */ + ZPL_DEF_INLINE zpl_b32 zpl_file_write_at_check(zpl_file *file, void const *buffer, zpl_isize size, zpl_i64 offset, zpl_isize *bytes_written); + + + /** + * Reads file at a specific offset + * @param file + * @param buffer Buffer to read to + * @param size Size to read + * @param offset Offset to read from + * @param bytes_read How much data we've actually read + */ + ZPL_DEF_INLINE zpl_b32 zpl_file_read_at(zpl_file *file, void *buffer, zpl_isize size, zpl_i64 offset); + + /** + * Writes to file at a specific offset + * @param file + * @param buffer Buffer to read from + * @param size Size to write + * @param offset Offset to write to + * @param bytes_written How much data we've actually written + */ + ZPL_DEF_INLINE zpl_b32 zpl_file_write_at(zpl_file *file, void const *buffer, zpl_isize size, zpl_i64 offset); + + /** + * Seeks the file cursor from the beginning of file to a specific position + * @param file + * @param offset Offset to seek to + */ + ZPL_DEF_INLINE zpl_i64 zpl_file_seek(zpl_file *file, zpl_i64 offset); + + /** + * Seeks the file cursor to the end of the file + * @param file + */ + ZPL_DEF_INLINE zpl_i64 zpl_file_seek_to_end(zpl_file *file); + + /** + * Skips N bytes at the current position + * @param file + * @param bytes Bytes to skip + */ + ZPL_DEF_INLINE zpl_i64 zpl_file_skip(zpl_file *file, zpl_i64 bytes); // NOTE: Skips a certain amount of bytes + + /** + * Returns the length from the beginning of the file we've read so far + * @param file + * @return Our current position in file + */ + ZPL_DEF_INLINE zpl_i64 zpl_file_tell(zpl_file *file); + + /** + * Reads from a file + * @param file + * @param buffer Buffer to read to + * @param size Size to read + */ + ZPL_DEF_INLINE zpl_b32 zpl_file_read(zpl_file *file, void *buffer, zpl_isize size); + + /** + * Writes to a file + * @param file + * @param buffer Buffer to read from + * @param size Size to read + */ + ZPL_DEF_INLINE zpl_b32 zpl_file_write(zpl_file *file, void const *buffer, zpl_isize size); + + + typedef struct zpl_file_contents { + zpl_allocator allocator; + void *data; + zpl_isize size; + } zpl_file_contents; + + /** + * Reads the whole file contents + * @param a Allocator to use + * @param zero_terminate End the read data with null terminator + * @param filepath Path to the file + * @return File contents data + */ + ZPL_DEF zpl_file_contents zpl_file_read_contents(zpl_allocator a, zpl_b32 zero_terminate, char const *filepath); + + /** + * Frees the file content data previously read + * @param fc + */ + ZPL_DEF void zpl_file_free_contents(zpl_file_contents *fc); + + /** + * Writes content to a file + */ + ZPL_DEF zpl_b32 zpl_file_write_contents(char const* filepath, void const* buffer, zpl_isize size, zpl_file_error* err); + + /** + * Reads the file as array of lines + * + * Make sure you free both the returned buffer and the lines (zpl_array) + * @param alloc Allocator to use + * @param lines Reference to zpl_array container we store lines to + * @param filename Path to the file + * @param strip_whitespace Strip whitespace when we split to lines? + * @return File content we've read itself + */ + ZPL_DEF char *zpl_file_read_lines(zpl_allocator alloc, zpl_array(char *)*lines, char const *filename, zpl_b32 strip_whitespace); + + //! @} + + /* inlines */ + + + ZPL_IMPL_INLINE zpl_b32 zpl_file_read_at_check(zpl_file *f, void *buffer, zpl_isize size, zpl_i64 offset, zpl_isize *bytes_read) { + if (!f->ops.read_at) f->ops = zpl_default_file_operations; + return f->ops.read_at(f->fd, buffer, size, offset, bytes_read, false); + } + + ZPL_IMPL_INLINE zpl_b32 zpl_file_write_at_check(zpl_file *f, void const *buffer, zpl_isize size, zpl_i64 offset, zpl_isize *bytes_written) { + if (!f->ops.read_at) f->ops = zpl_default_file_operations; + return f->ops.write_at(f->fd, buffer, size, offset, bytes_written); + } + + ZPL_IMPL_INLINE zpl_b32 zpl_file_read_at(zpl_file *f, void *buffer, zpl_isize size, zpl_i64 offset) { + return zpl_file_read_at_check(f, buffer, size, offset, NULL); + } + + ZPL_IMPL_INLINE zpl_b32 zpl_file_write_at(zpl_file *f, void const *buffer, zpl_isize size, zpl_i64 offset) { + return zpl_file_write_at_check(f, buffer, size, offset, NULL); + } + + ZPL_IMPL_INLINE zpl_i64 zpl_file_seek(zpl_file *f, zpl_i64 offset) { + zpl_i64 new_offset = 0; + if (!f->ops.read_at) f->ops = zpl_default_file_operations; + f->ops.seek(f->fd, offset, ZPL_SEEK_WHENCE_BEGIN, &new_offset); + return new_offset; + } + + ZPL_IMPL_INLINE zpl_i64 zpl_file_seek_to_end(zpl_file *f) { + zpl_i64 new_offset = 0; + if (!f->ops.read_at) f->ops = zpl_default_file_operations; + f->ops.seek(f->fd, 0, ZPL_SEEK_WHENCE_END, &new_offset); + return new_offset; + } + + // NOTE: Skips a certain amount of bytes + ZPL_IMPL_INLINE zpl_i64 zpl_file_skip(zpl_file *f, zpl_i64 bytes) { + zpl_i64 new_offset = 0; + if (!f->ops.read_at) f->ops = zpl_default_file_operations; + f->ops.seek(f->fd, bytes, ZPL_SEEK_WHENCE_CURRENT, &new_offset); + return new_offset; + } + + ZPL_IMPL_INLINE zpl_i64 zpl_file_tell(zpl_file *f) { + zpl_i64 new_offset = 0; + if (!f->ops.read_at) f->ops = zpl_default_file_operations; + f->ops.seek(f->fd, 0, ZPL_SEEK_WHENCE_CURRENT, &new_offset); + return new_offset; + } + + ZPL_IMPL_INLINE zpl_b32 zpl_file_read(zpl_file *f, void *buffer, zpl_isize size) { + zpl_i64 cur_offset = zpl_file_tell(f); + zpl_b32 result = zpl_file_read_at(f, buffer, size, zpl_file_tell(f)); + zpl_file_seek(f, cur_offset + size); + return result; + } + + ZPL_IMPL_INLINE zpl_b32 zpl_file_write(zpl_file *f, void const *buffer, zpl_isize size) { + zpl_i64 cur_offset = zpl_file_tell(f); + zpl_b32 result = zpl_file_write_at(f, buffer, size, zpl_file_tell(f)); + zpl_file_seek(f, cur_offset + size); + return result; + } + + ZPL_END_C_DECLS + // file: header/core/file_stream.h + + /** @file file_stream.c + @brief File stream + @defgroup fileio File stream + + File streaming operations on memory. + + @{ + */ + + ZPL_BEGIN_C_DECLS + + typedef enum { + /* Allows us to write to the buffer directly. Beware: you can not append a new data! */ + ZPL_FILE_STREAM_WRITABLE = ZPL_BIT(0), + + /* Clones the input buffer so you can write (zpl_file_write*) data into it. */ + /* Since we work with a clone, the buffer size can dynamically grow as well. */ + ZPL_FILE_STREAM_CLONE_WRITABLE = ZPL_BIT(1), + } zpl_file_stream_flags; + + /** + * Opens a new memory stream + * @param file + * @param allocator + */ + ZPL_DEF zpl_b8 zpl_file_stream_new(zpl_file* file, zpl_allocator allocator); + + /** + * Opens a memory stream over an existing buffer + * @param file + * @param allocator + * @param buffer Memory to create stream from + * @param size Buffer's size + * @param flags + */ + ZPL_DEF zpl_b8 zpl_file_stream_open(zpl_file* file, zpl_allocator allocator, zpl_u8 *buffer, zpl_isize size, zpl_file_stream_flags flags); + + /** + * Retrieves the stream's underlying buffer and buffer size. + * @param file memory stream + * @param size (Optional) buffer size + */ + ZPL_DEF zpl_u8 *zpl_file_stream_buf(zpl_file* file, zpl_isize *size); + + extern zpl_file_operations const zpl_memory_file_operations; + + //! @} + + ZPL_END_C_DECLS + // file: header/core/file_misc.h + + + ZPL_BEGIN_C_DECLS + + #ifndef ZPL_PATH_SEPARATOR + # if defined(ZPL_SYSTEM_WINDOWS) + # define ZPL_PATH_SEPARATOR '\\' + # else + # define ZPL_PATH_SEPARATOR '/' + # endif + #endif + + #ifndef ZPL_MAX_PATH + # if defined(ZPL_SYSTEM_WINDOWS) + # define ZPL_MAX_PATH MAX_PATH + # elif defined(ZPL_SYSTEM_UNIX) && !defined(ZPL_SYSTEM_EMSCRIPTEN) + # define ZPL_MAX_PATH PATH_MAX + # else + # define ZPL_MAX_PATH 4096 + # endif + #endif + + /** + * Checks if file/directory exists + * @param filepath + */ + ZPL_DEF zpl_b32 zpl_fs_exists(char const *filepath); + + /** + * Retrieves node's type (file, folder, ...) + * @param path + */ + ZPL_DEF zpl_u8 zpl_fs_get_type(char const *path); + + /** + * Retrieves file's last write time + * @param filepath + */ + ZPL_DEF zpl_file_time zpl_fs_last_write_time(char const *filepath); + + /** + * Copies the file to a directory + * @param existing_filename + * @param new_filename + * @param fail_if_exists + */ + ZPL_DEF zpl_b32 zpl_fs_copy(char const *existing_filename, char const *new_filename, zpl_b32 fail_if_exists); + + /** + * Moves the file to a directory + * @param existing_filename + * @param new_filename + */ + ZPL_DEF zpl_b32 zpl_fs_move(char const *existing_filename, char const *new_filename); + + /** + * Removes a file from a directory + * @param filename + */ + ZPL_DEF zpl_b32 zpl_fs_remove(char const *filename); + + ZPL_DEF_INLINE zpl_b32 zpl_path_is_absolute(char const *path); + ZPL_DEF_INLINE zpl_b32 zpl_path_is_relative(char const *path); + ZPL_DEF_INLINE zpl_b32 zpl_path_is_root(char const *path); + + ZPL_DEF_INLINE char const *zpl_path_base_name(char const *path); + ZPL_DEF_INLINE char const *zpl_path_extension(char const *path); + + ZPL_DEF void zpl_path_fix_slashes(char *path); + + ZPL_DEF zpl_file_error zpl_path_mkdir(char const *path, zpl_i32 mode); + ZPL_DEF zpl_isize zpl_path_mkdir_recursive(char const *path, zpl_i32 mode); + ZPL_DEF zpl_file_error zpl_path_rmdir(char const *path); + + ZPL_DEF char *zpl_path_get_full_name(zpl_allocator a, char const *path); + + /** + * Returns file paths terminated by newline (\n) + * @param alloc [description] + * @param dirname [description] + * @param recurse [description] + * @return [description] + */ + ZPL_DEF /*zpl_string*/char * zpl_path_dirlist(zpl_allocator alloc, char const *dirname, zpl_b32 recurse); + + /** + * Initialize dirinfo from specified path + * @param dir [description] + * @param path [description] + */ + ZPL_DEF void zpl_dirinfo_init(zpl_dir_info *dir, char const *path); + ZPL_DEF void zpl_dirinfo_free(zpl_dir_info *dir); + + /** + * Analyze the entry's dirinfo + * @param dir_entry [description] + */ + ZPL_DEF void zpl_dirinfo_step(zpl_dir_entry *dir_entry); + + + /* inlines */ + + ZPL_IMPL_INLINE zpl_b32 zpl_path_is_absolute(char const *path) { + zpl_b32 result = false; + ZPL_ASSERT_NOT_NULL(path); + #if defined(ZPL_SYSTEM_WINDOWS) + result = (zpl_strlen(path) > 2) && zpl_char_is_alpha(path[0]) && (path[1] == ':' && path[2] == ZPL_PATH_SEPARATOR); + #else + result = (zpl_strlen(path) > 0 && path[0] == ZPL_PATH_SEPARATOR); + #endif + return result; + } + + ZPL_IMPL_INLINE zpl_b32 zpl_path_is_relative(char const *path) { return !zpl_path_is_absolute(path); } + + ZPL_IMPL_INLINE zpl_b32 zpl_path_is_root(char const *path) { + zpl_b32 result = false; + ZPL_ASSERT_NOT_NULL(path); + #if defined(ZPL_SYSTEM_WINDOWS) + result = zpl_path_is_absolute(path) && (zpl_strlen(path) == 3); + #else + result = zpl_path_is_absolute(path) && (zpl_strlen(path) == 1); + #endif + return result; + } + + ZPL_IMPL_INLINE char const *zpl_path_base_name(char const *path) { + char const *ls; + ZPL_ASSERT_NOT_NULL(path); + zpl_path_fix_slashes((char *)path); + ls = zpl_char_last_occurence(path, ZPL_PATH_SEPARATOR); + return (ls == NULL) ? path : ls + 1; + } + + ZPL_IMPL_INLINE char const *zpl_path_extension(char const *path) { + char const *ld; + ZPL_ASSERT_NOT_NULL(path); + ld = zpl_char_last_occurence(path, '.'); + return (ld == NULL) ? NULL : ld + 1; + } + + ZPL_END_C_DECLS + // file: header/core/file_tar.h + + /** @file file_tar.c + @brief Tar archiving module + @defgroup fileio Tar module + + Allows to easily pack/unpack files. + Based on: https://github.com/rxi/microtar/ + + Disclaimer: The pack method does not support file permissions nor GID/UID information. Only regular files are supported. + Use zpl_tar_pack_dir to pack an entire directory recursively. Empty folders are ignored. + + @{ + */ + + + ZPL_BEGIN_C_DECLS + + typedef enum { + ZPL_TAR_ERROR_NONE, + ZPL_TAR_ERROR_INTERRUPTED, + ZPL_TAR_ERROR_IO_ERROR, + ZPL_TAR_ERROR_BAD_CHECKSUM, + ZPL_TAR_ERROR_FILE_NOT_FOUND, + ZPL_TAR_ERROR_INVALID_INPUT, + } zpl_tar_errors; + + typedef enum { + ZPL_TAR_TYPE_REGULAR = '0', + ZPL_TAR_TYPE_LINK = '1', + ZPL_TAR_TYPE_SYMBOL = '2', + ZPL_TAR_TYPE_CHR = '3', + ZPL_TAR_TYPE_BLK = '4', + ZPL_TAR_TYPE_DIR = '5', + ZPL_TAR_TYPE_FIFO = '6' + } zpl_tar_file_type; + + typedef struct { + char type; + char *path; + zpl_i64 offset; + zpl_i64 length; + zpl_isize error; + } zpl_tar_record; + + #define ZPL_TAR_UNPACK_PROC(name) zpl_isize name(zpl_file *archive, zpl_tar_record *file, void* user_data) + typedef ZPL_TAR_UNPACK_PROC(zpl_tar_unpack_proc); + + /** + * @brief Packs a list of files + * Packs a list of provided files. Note that this method only supports regular files + * and does not provide extended info such as GID/UID or permissions. + * @param archive archive we pack files into + * @param paths list of files + * @param paths_len number of files provided + * @return error + */ + ZPL_DEF zpl_isize zpl_tar_pack(zpl_file *archive, char const **paths, zpl_isize paths_len); + + /** + * @brief Packs an entire directory + * Packs an entire directory of files recursively. + * @param archive archive we pack files to + * @param path folder to pack + * @param alloc memory allocator to use (ex. zpl_heap()) + * @return error + */ + ZPL_DEF zpl_isize zpl_tar_pack_dir(zpl_file *archive, char const *path, zpl_allocator alloc); + + /** + * @brief Unpacks an existing archive + * Unpacks an existing archive. Users provide a callback in which information about file is provided. + * Library does not unpack files to the filesystem nor reads any file data. + * @param archive archive we unpack files from + * @param unpack_proc callback we call per each file parsed + * @param user_data user provided data + * @return error + */ + ZPL_DEF zpl_isize zpl_tar_unpack(zpl_file *archive, zpl_tar_unpack_proc *unpack_proc, void *user_data); + + /** + * @brief Unpacks an existing archive into directory + * Unpacks an existing archive into directory. The folder structure will be re-created automatically. + * @param archive archive we unpack files from + * @param dest directory to unpack files to + * @return error + */ + ZPL_DEF_INLINE zpl_isize zpl_tar_unpack_dir(zpl_file *archive, char const *dest); + + ZPL_DEF ZPL_TAR_UNPACK_PROC(zpl_tar_default_list_file); + ZPL_DEF ZPL_TAR_UNPACK_PROC(zpl_tar_default_unpack_file); + + //! @} + + ZPL_IMPL_INLINE zpl_isize zpl_tar_unpack_dir(zpl_file *archive, char const *dest) { + return zpl_tar_unpack(archive, zpl_tar_default_unpack_file, cast(void*)dest); + } + + ZPL_END_C_DECLS + // file: header/core/print.h + + /** @file print.c + @brief Printing methods + @defgroup print Printing methods + + Various printing methods. + @{ + */ + + ZPL_BEGIN_C_DECLS + + #ifndef ZPL_PRINTF_MAXLEN + #define ZPL_PRINTF_MAXLEN 65536 + #endif + + ZPL_DEF zpl_isize zpl_printf(char const *fmt, ...); + ZPL_DEF zpl_isize zpl_printf_va(char const *fmt, va_list va); + ZPL_DEF zpl_isize zpl_printf_err(char const *fmt, ...); + ZPL_DEF zpl_isize zpl_printf_err_va(char const *fmt, va_list va); + ZPL_DEF zpl_isize zpl_fprintf(zpl_file *f, char const *fmt, ...); + ZPL_DEF zpl_isize zpl_fprintf_va(zpl_file *f, char const *fmt, va_list va); + + // NOTE: A locally persisting buffer is used internally + ZPL_DEF char *zpl_bprintf(char const *fmt, ...); + + // NOTE: A locally persisting buffer is used internally + ZPL_DEF char *zpl_bprintf_va(char const *fmt, va_list va); + + ZPL_DEF zpl_isize zpl_asprintf(zpl_allocator allocator, char **buffer, char const *fmt, ...); + ZPL_DEF zpl_isize zpl_asprintf_va(zpl_allocator allocator, char **buffer, char const *fmt, va_list va); + + ZPL_DEF zpl_isize zpl_snprintf(char *str, zpl_isize n, char const *fmt, ...); + ZPL_DEF zpl_isize zpl_snprintf_va(char *str, zpl_isize n, char const *fmt, va_list va); + + ZPL_END_C_DECLS + // file: header/core/time.h + + /** @file time.c + @brief Time helper methods. + @defgroup time Time helpers + + Helper methods for retrieving the current time in many forms under different precisions. It also offers a simple to use timer library. + + @{ + */ + + + ZPL_BEGIN_C_DECLS + + //! Return CPU timestamp. + ZPL_DEF zpl_u64 zpl_rdtsc(void); + + //! Return relative time (in seconds) since the application start. + ZPL_DEF zpl_f64 zpl_time_rel(void); + + //! Return relative time since the application start. + ZPL_DEF zpl_u64 zpl_time_rel_ms(void); + + //! Return time (in seconds) since 1601-01-01 UTC. + ZPL_DEF zpl_f64 zpl_time_utc(void); + + //! Return time since 1601-01-01 UTC. + ZPL_DEF zpl_u64 zpl_time_utc_ms(void); + + //! Return local system time since 1601-01-01 + ZPL_DEF zpl_u64 zpl_time_tz_ms(void); + + //! Return local system time in seconds since 1601-01-01 + ZPL_DEF zpl_f64 zpl_time_tz(void); + + //! Convert Win32 epoch (1601-01-01 UTC) to UNIX (1970-01-01 UTC) + ZPL_DEF_INLINE zpl_u64 zpl_time_win32_to_unix(zpl_u64 ms); + + //! Convert UNIX (1970-01-01 UTC) to Win32 epoch (1601-01-01 UTC) + ZPL_DEF_INLINE zpl_u64 zpl_time_unix_to_win32(zpl_u64 ms); + + //! Sleep for specified number of milliseconds. + ZPL_DEF void zpl_sleep_ms(zpl_u32 ms); + + //! Sleep for specified number of seconds. + ZPL_DEF_INLINE void zpl_sleep(zpl_f32 s); + + // Deprecated methods + ZPL_DEPRECATED_FOR(10.9.0, zpl_time_rel) + ZPL_DEF_INLINE zpl_f64 zpl_time_now(void); + + ZPL_DEPRECATED_FOR(10.9.0, zpl_time_utc) + ZPL_DEF_INLINE zpl_f64 zpl_utc_time_now(void); + + + #ifndef ZPL__UNIX_TO_WIN32_EPOCH + #define ZPL__UNIX_TO_WIN32_EPOCH 11644473600000ull + #endif + + ZPL_IMPL_INLINE zpl_u64 zpl_time_win32_to_unix(zpl_u64 ms) { + return ms - ZPL__UNIX_TO_WIN32_EPOCH; + } + + ZPL_IMPL_INLINE zpl_u64 zpl_time_unix_to_win32(zpl_u64 ms) { + return ms + ZPL__UNIX_TO_WIN32_EPOCH; + } + + ZPL_IMPL_INLINE void zpl_sleep(zpl_f32 s) { + zpl_sleep_ms((zpl_u32)(s * 1000)); + } + + ZPL_IMPL_INLINE zpl_f64 zpl_time_now() { + return zpl_time_rel(); + } + + ZPL_IMPL_INLINE zpl_f64 zpl_utc_time_now() { + return zpl_time_utc(); + } + + ZPL_END_C_DECLS + // file: header/core/random.h + + + ZPL_BEGIN_C_DECLS + + typedef struct zpl_random { + zpl_u32 offsets[8]; + zpl_u32 value; + } zpl_random; + + // NOTE: Generates from numerous sources to produce a decent pseudo-random seed + ZPL_DEF void zpl_random_init(zpl_random *r); + ZPL_DEF zpl_u32 zpl_random_gen_u32(zpl_random *r); + ZPL_DEF zpl_u32 zpl_random_gen_u32_unique(zpl_random *r); + ZPL_DEF zpl_u64 zpl_random_gen_u64(zpl_random *r); // NOTE: (zpl_random_gen_u32() << 32) | zpl_random_gen_u32() + ZPL_DEF zpl_isize zpl_random_gen_isize(zpl_random *r); + ZPL_DEF zpl_i64 zpl_random_range_i64(zpl_random *r, zpl_i64 lower_inc, zpl_i64 higher_inc); + ZPL_DEF zpl_isize zpl_random_range_isize(zpl_random *r, zpl_isize lower_inc, zpl_isize higher_inc); + ZPL_DEF zpl_f64 zpl_random_range_f64(zpl_random *r, zpl_f64 lower_inc, zpl_f64 higher_inc); + + ZPL_END_C_DECLS + // file: header/core/misc.h + + /** @file misc.c + @brief Various other stuff + @defgroup misc Various other stuff + + Methods that don't belong anywhere but are still very useful in many occasions. + + @{ + */ + + ZPL_BEGIN_C_DECLS + + ZPL_DEF void zpl_yield(void); + + //! Returns allocated buffer + ZPL_DEF const char *zpl_get_env(const char *name); + ZPL_DEF const char *zpl_get_env_buf(const char *name); + ZPL_DEF zpl_string zpl_get_env_str(const char *name); + ZPL_DEF void zpl_set_env(const char *name, const char *value); + ZPL_DEF void zpl_unset_env(const char *name); + + ZPL_DEF zpl_u32 zpl_system_command(const char *command, zpl_usize buffer_len, char *buffer); + ZPL_DEF zpl_string zpl_system_command_str(const char *command, zpl_allocator backing); + + ZPL_DEF_INLINE zpl_u16 zpl_endian_swap16(zpl_u16 i); + ZPL_DEF_INLINE zpl_u32 zpl_endian_swap32(zpl_u32 i); + ZPL_DEF_INLINE zpl_u64 zpl_endian_swap64(zpl_u64 i); + + ZPL_DEF_INLINE zpl_isize zpl_count_set_bits(zpl_u64 mask); + + //! @} + //$$ + + ZPL_IMPL_INLINE zpl_u16 zpl_endian_swap16(zpl_u16 i) { + return (i>>8) | (i<<8); + } + + ZPL_IMPL_INLINE zpl_u32 zpl_endian_swap32(zpl_u32 i) { + return (i>>24) |(i<<24) | + ((i&0x00ff0000u)>>8) | ((i&0x0000ff00u)<<8); + } + + ZPL_IMPL_INLINE zpl_u64 zpl_endian_swap64(zpl_u64 i) { + return (i>>56) | (i<<56) | + ((i&0x00ff000000000000ull)>>40) | ((i&0x000000000000ff00ull)<<40) | + ((i&0x0000ff0000000000ull)>>24) | ((i&0x0000000000ff0000ull)<<24) | + ((i&0x000000ff00000000ull)>>8) | ((i&0x00000000ff000000ull)<<8); + } + + ZPL_IMPL_INLINE zpl_i32 zpl_next_pow2(zpl_i32 x) { + x--; + x |= x >> 1; + x |= x >> 2; + x |= x >> 4; + x |= x >> 8; + x |= x >> 16; + return x + 1; + } + + ZPL_IMPL_INLINE void zpl_bit_set(zpl_u32* x, zpl_u32 bit) { *x = *x | (1 << bit); } + ZPL_IMPL_INLINE zpl_b8 zpl_bit_get(zpl_u32 x, zpl_u32 bit) { return (x & (1 << bit)); } + ZPL_IMPL_INLINE void zpl_bit_reset(zpl_u32* x, zpl_u32 bit) { *x = *x & ~(1 << bit); } + + ZPL_IMPL_INLINE zpl_isize zpl_count_set_bits(zpl_u64 mask) { + zpl_isize count = 0; + while (mask) { + count += (mask & 1); + mask >>= 1; + } + return count; + } + + ZPL_END_C_DECLS + // file: header/core/sort.h + + /** @file sort.c + @brief Sorting and searching methods. + @defgroup sort Sorting and searching + + Methods for sorting arrays using either Quick/Merge-sort combo or Radix sort. It also contains simple implementation of binary search, as well as an easy to use API to define your own comparators. + + @{ + */ + + ZPL_BEGIN_C_DECLS + + #define ZPL_COMPARE_PROC(name) int name(void const *a, void const *b) + typedef ZPL_COMPARE_PROC(zpl_compare_proc); + + #define ZPL_COMPARE_PROC_PTR(def) ZPL_COMPARE_PROC((*def)) + + // Procedure pointers + // NOTE: The offset parameter specifies the offset in the structure + // e.g. zpl_i32_cmp(zpl_offset_of(Thing, value)) + // Use 0 if it's just the type instead. + + ZPL_DEF ZPL_COMPARE_PROC_PTR(zpl_i16_cmp(zpl_isize offset)); + ZPL_DEF ZPL_COMPARE_PROC_PTR(zpl_u8_cmp(zpl_isize offset)); + ZPL_DEF ZPL_COMPARE_PROC_PTR(zpl_i32_cmp(zpl_isize offset)); + ZPL_DEF ZPL_COMPARE_PROC_PTR(zpl_i64_cmp(zpl_isize offset)); + ZPL_DEF ZPL_COMPARE_PROC_PTR(zpl_isize_cmp(zpl_isize offset)); + ZPL_DEF ZPL_COMPARE_PROC_PTR(zpl_str_cmp(zpl_isize offset)); + ZPL_DEF ZPL_COMPARE_PROC_PTR(zpl_f32_cmp(zpl_isize offset)); + ZPL_DEF ZPL_COMPARE_PROC_PTR(zpl_f64_cmp(zpl_isize offset)); + + // TODO: Better sorting algorithms + + //! Sorts an array. + + //! Uses quick sort for large arrays but insertion sort for small ones. + #define zpl_sort_array(array, count, compare_proc) zpl_sort(array, count, zpl_size_of(*(array)), compare_proc) + + //! Perform sorting operation on a memory location with a specified item count and size. + ZPL_DEF void zpl_sort(void *base, zpl_isize count, zpl_isize size, zpl_compare_proc compare_proc); + + // NOTE: the count of temp == count of items + #define zpl_radix_sort(Type) zpl_radix_sort_##Type + #define ZPL_RADIX_SORT_PROC(Type) void zpl_radix_sort(Type)(zpl_##Type * items, zpl_##Type * temp, zpl_isize count) + + ZPL_DEF ZPL_RADIX_SORT_PROC(u8); + ZPL_DEF ZPL_RADIX_SORT_PROC(u16); + ZPL_DEF ZPL_RADIX_SORT_PROC(u32); + ZPL_DEF ZPL_RADIX_SORT_PROC(u64); + + //! Performs binary search on an array. + + //! Returns index or -1 if not found + #define zpl_binary_search_array(array, count, key, compare_proc) \ + zpl_binary_search(array, count, zpl_size_of(*(array)), key, compare_proc) + + //! Performs binary search on a memory location with specified item count and size. + ZPL_DEF_INLINE zpl_isize zpl_binary_search(void const *base, zpl_isize count, zpl_isize size, void const *key, + zpl_compare_proc compare_proc); + + #define zpl_shuffle_array(array, count) zpl_shuffle(array, count, zpl_size_of(*(array))) + + //! Shuffles a memory. + ZPL_DEF void zpl_shuffle(void *base, zpl_isize count, zpl_isize size); + + #define zpl_reverse_array(array, count) zpl_reverse(array, count, zpl_size_of(*(array))) + + //! Reverses memory's contents + ZPL_DEF void zpl_reverse(void *base, zpl_isize count, zpl_isize size); + + //! @} + + + ZPL_IMPL_INLINE zpl_isize zpl_binary_search(void const *base, zpl_isize count, zpl_isize size, void const *key, + zpl_compare_proc compare_proc) { + zpl_isize start = 0; + zpl_isize end = count; + + while (start < end) { + zpl_isize mid = start + (end - start) / 2; + zpl_isize result = compare_proc(key, cast(zpl_u8 *) base + mid * size); + if (result < 0) + end = mid; + else if (result > 0) + start = mid + 1; + else + return mid; + } + + return -1; + } + + ZPL_END_C_DECLS +# endif +#endif + +#if defined(ZPL_MODULE_HASHING) + // file: header/hashing.h + + /** @file hashing.c + @brief Hashing and Checksum Functions + @defgroup hashing Hashing and Checksum Functions + + Several hashing methods used by zpl internally but possibly useful outside of it. Contains: adler32, crc32/64, fnv32/64/a and murmur32/64 + + @{ + */ + + + ZPL_BEGIN_C_DECLS + + ZPL_DEF zpl_u32 zpl_adler32(void const *data, zpl_isize len); + + ZPL_DEF zpl_u32 zpl_crc32(void const *data, zpl_isize len); + ZPL_DEF zpl_u64 zpl_crc64(void const *data, zpl_isize len); + + // These use FNV-1 algorithm + ZPL_DEF zpl_u32 zpl_fnv32(void const *data, zpl_isize len); + ZPL_DEF zpl_u64 zpl_fnv64(void const *data, zpl_isize len); + ZPL_DEF zpl_u32 zpl_fnv32a(void const *data, zpl_isize len); + ZPL_DEF zpl_u64 zpl_fnv64a(void const *data, zpl_isize len); + + ZPL_DEF zpl_u8 *zpl_base64_encode(zpl_allocator a, void const *data, zpl_isize len); + ZPL_DEF zpl_u8 *zpl_base64_decode(zpl_allocator a, void const *data, zpl_isize len); + + //! Based on MurmurHash3 + ZPL_DEF zpl_u32 zpl_murmur32_seed(void const *data, zpl_isize len, zpl_u32 seed); + + //! Based on MurmurHash2 + ZPL_DEF zpl_u64 zpl_murmur64_seed(void const *data, zpl_isize len, zpl_u64 seed); + + //! Default seed of 0x9747b28c + ZPL_DEF_INLINE zpl_u32 zpl_murmur32(void const *data, zpl_isize len); + + //! Default seed of 0x9747b28c + ZPL_DEF_INLINE zpl_u64 zpl_murmur64(void const *data, zpl_isize len); + + //! @} + + ZPL_IMPL_INLINE zpl_u32 zpl_murmur32(void const *data, zpl_isize len) { return zpl_murmur32_seed(data, len, 0x9747b28c); } + ZPL_IMPL_INLINE zpl_u64 zpl_murmur64(void const *data, zpl_isize len) { return zpl_murmur64_seed(data, len, 0x9747b28c); } + + ZPL_END_C_DECLS +#endif + +#if defined(ZPL_MODULE_REGEX) + // file: header/regex.h + + /** @file regex.c + @brief Regular expressions parser. + @defgroup regex Regex processor + + Port of gb_regex with several bugfixes applied. This is a simple regex library and is fast to perform. + + Supported Matching: + @n ^ - Beginning of string + @n $ - End of string + @n . - Match one (anything) + @n | - Branch (or) + @n () - Capturing group + @n [] - Any character included in set + @n [^] - Any character excluded from set + @n + - One or more (greedy) + @n +? - One or more (non-greedy) + @n * - Zero or more (greedy) + @n *? - Zero or more (non-greedy) + @n ? - Zero or once + @n [BACKSLASH]XX - Hex decimal digit (must be 2 digits) + @n [BACKSLASH]meta - Meta character + @n [BACKSLASH]s - Whitespace + @n [BACKSLASH]S - Not whitespace + @n [BACKSLASH]d - Digit + @n [BACKSLASH]D - Not digit + @n [BACKSLASH]a - Alphabetic character + @n [BACKSLASH]l - Lower case letter + @n [BACKSLASH]u - Upper case letter + @n [BACKSLASH]w - Word + @n [BACKSLASH]W - Not word + @n [BACKSLASH]x - Hex Digit + @n [BACKSLASH]p - Printable ASCII character + @n --Whitespace-- + @n [BACKSLASH]t - Tab + @n [BACKSLASH]n - New line + @n [BACKSLASH]r - Return carriage + @n [BACKSLASH]v - Vertical Tab + @n [BACKSLASH]f - Form feed + + @{ + */ + + ZPL_BEGIN_C_DECLS + + typedef struct zpl_re { + zpl_allocator backing; + zpl_isize capture_count; + char *buf; + zpl_isize buf_len, buf_cap; + zpl_b32 can_realloc; + } zpl_re; + + typedef struct zpl_re_capture { + char const *str; + zpl_isize len; + } zpl_re_capture; + + #define zplRegexError zpl_regex_error + typedef enum zpl_regex_error { + ZPL_RE_ERROR_NONE, + ZPL_RE_ERROR_NO_MATCH, + ZPL_RE_ERROR_TOO_LONG, + ZPL_RE_ERROR_MISMATCHED_CAPTURES, + ZPL_RE_ERROR_MISMATCHED_BLOCKS, + ZPL_RE_ERROR_BRANCH_FAILURE, + ZPL_RE_ERROR_INVALID_QUANTIFIER, + ZPL_RE_ERROR_INTERNAL_FAILURE, + } zpl_regex_error; + + //! Compile regex pattern. + ZPL_DEF zpl_regex_error zpl_re_compile(zpl_re *re, zpl_allocator backing, char const *pattern, zpl_isize pattern_len); + + //! Compile regex pattern using a buffer. + ZPL_DEF zpl_regex_error zpl_re_compile_from_buffer(zpl_re *re, char const *pattern, zpl_isize pattern_len, void *buffer, zpl_isize buffer_len); + + //! Destroy regex object. + ZPL_DEF void zpl_re_destroy(zpl_re *re); + + //! Retrieve number of retrievable captures. + ZPL_DEF zpl_isize zpl_re_capture_count(zpl_re *re); + + //! Match input string and output captures of the occurence. + ZPL_DEF zpl_b32 zpl_re_match(zpl_re *re, char const *str, zpl_isize str_len, zpl_re_capture *captures, zpl_isize max_capture_count, zpl_isize *offset); + + //! Match all occurences in an input string and output them into captures. Array of captures is allocated on the heap and needs to be freed afterwards. + ZPL_DEF zpl_b32 zpl_re_match_all(zpl_re *re, char const *str, zpl_isize str_len, zpl_isize max_capture_count, zpl_re_capture **out_captures); + + ZPL_END_C_DECLS +#endif + +#if defined(ZPL_MODULE_DLL) + // file: header/dll.h + + /** @file dll.c + @brief DLL Handling + @defgroup dll DLL handling + + @{ + */ + + ZPL_BEGIN_C_DECLS + + typedef void *zpl_dll_handle; + typedef void (*zpl_dll_proc)(void); + + ZPL_DEF zpl_dll_handle zpl_dll_load(char const *filepath); + ZPL_DEF void zpl_dll_unload(zpl_dll_handle dll); + ZPL_DEF zpl_dll_proc zpl_dll_proc_address(zpl_dll_handle dll, char const *proc_name); + + //! @} + + ZPL_END_C_DECLS +#endif + +#if defined(ZPL_MODULE_OPTS) + // file: header/opts.h + + /** @file opts.c + @brief CLI options processor + @defgroup cli CLI options processor + + Opts is a CLI options parser, it can parse flags, switches and arguments from command line + and offers an easy way to express input errors as well as the ability to display help screen. + + @{ + */ + + ZPL_BEGIN_C_DECLS + + typedef enum { + ZPL_OPTS_STRING, + ZPL_OPTS_FLOAT, + ZPL_OPTS_FLAG, + ZPL_OPTS_INT, + } zpl_opts_types; + + typedef struct { + char const *name, *lname, *desc; + zpl_u8 type; + zpl_b32 met, pos; + + //! values + union { + zpl_string text; + zpl_i64 integer; + zpl_f64 real; + }; + } zpl_opts_entry; + + typedef enum { + ZPL_OPTS_ERR_VALUE, + ZPL_OPTS_ERR_OPTION, + ZPL_OPTS_ERR_EXTRA_VALUE, + ZPL_OPTS_ERR_MISSING_VALUE, + } zpl_opts_err_type; + + typedef struct { + char *val; + zpl_u8 type; + } zpl_opts_err; + + typedef struct { + zpl_allocator alloc; + zpl_opts_entry *entries; ///< zpl_array + zpl_opts_err *errors; ///< zpl_array + zpl_opts_entry **positioned; ///< zpl_array + char const *appname; + } zpl_opts; + + //! Initializes options parser. + + //! Initializes CLI options parser using specified memory allocator and provided application name. + //! @param opts Options parser to initialize. + //! @param allocator Memory allocator to use. (ex. zpl_heap()) + //! @param app Application name displayed in help screen. + ZPL_DEF void zpl_opts_init(zpl_opts *opts, zpl_allocator allocator, char const *app); + + //! Releases the resources used by options parser. + ZPL_DEF void zpl_opts_free(zpl_opts *opts); + + //! Registers an option. + + //! Registers an option with its short and long name, specifies option's type and its description. + //! @param opts Options parser to add to. + //! @param lname Shorter name of option. (ex. "f") + //! @param name Full name of option. (ex. "foo") Note that rest of the module uses longer names to manipulate opts. + //! @param desc Description shown in the help screen. + //! @param type Option's type (see zpl_opts_types) + //! @see zpl_opts_types + ZPL_DEF void zpl_opts_add(zpl_opts *opts, char const *name, char const *lname, const char *desc, zpl_u8 type); + + //! Registers option as positional. + + //! Registers added option as positional, so that we can pass it anonymously. Arguments are expected on the command input in the same order they were registered as. + //! @param opts + //! @param name Name of already registered option. + ZPL_DEF void zpl_opts_positional_add(zpl_opts *opts, char const *name); + + //! Compiles CLI arguments. + + // This method takes CLI arguments as input and processes them based on rules that were set up. + //! @param opts + //! @param argc Argument count in an array. + //! @param argv Array of arguments. + ZPL_DEF zpl_b32 zpl_opts_compile(zpl_opts *opts, int argc, char **argv); + + //! Prints out help screen. + + //! Prints out help screen with example usage of application as well as with all the flags available. + ZPL_DEF void zpl_opts_print_help(zpl_opts *opts); + + //! Prints out parsing errors. + + //! Prints out possible errors caused by CLI input. + ZPL_DEF void zpl_opts_print_errors(zpl_opts *opts); + + //! Fetches a string from an option. + + //! @param opts + //! @param name Name of an option. + //! @param fallback Fallback string we return if option wasn't found. + ZPL_DEF zpl_string zpl_opts_string(zpl_opts *opts, char const *name, char const *fallback); + + //! Fetches a real number from an option. + + //! @param opts + //! @param name Name of an option. + //! @param fallback Fallback real number we return if option was not found. + ZPL_DEF zpl_f64 zpl_opts_real(zpl_opts *opts, char const *name, zpl_f64 fallback); + + //! Fetches an integer number from an option. + + //! @param opts + //! @param name Name of an option. + //! @param fallback Fallback integer number we return if option was not found. + ZPL_DEF zpl_i64 zpl_opts_integer(zpl_opts *opts, char const *name, zpl_i64 fallback); + + //! Checks whether an option was used. + + //! @param opts + //! @param name Name of an option. + ZPL_DEF zpl_b32 zpl_opts_has_arg(zpl_opts *opts, char const *name); + + //! Checks whether all positionals have been passed in. + ZPL_DEF zpl_b32 zpl_opts_positionals_filled(zpl_opts *opts); + + //! @} + + ZPL_END_C_DECLS +#endif + +#if defined(ZPL_MODULE_PROCESS) + // file: header/process.h + + /** @file process.c + @brief Process creation and manipulation methods + @defgroup process Process creation and manipulation methods + + Gives you the ability to create a new process, wait for it to end or terminate it. + It also exposes standard I/O with configurable options. + + @{ + */ + + ZPL_BEGIN_C_DECLS + // TODO(zaklaus): Add Linux support + + typedef enum { + ZPL_PR_OPTS_COMBINE_STD_OUTPUT = ZPL_BIT(1), + ZPL_PR_OPTS_INHERIT_ENV = ZPL_BIT(2), + ZPL_PR_OPTS_CUSTOM_ENV = ZPL_BIT(3), + } zpl_pr_opts; + + typedef struct { + zpl_file in, out, err; + void *f_stdin, *f_stdout, *f_stderr; + #ifdef ZPL_SYSTEM_WINDOWS + void *win32_handle; + #else + // todo + #endif + } zpl_pr; + + typedef struct { + char *con_title; + char *workdir; + + zpl_isize env_count; + char **env; // format: "var=name" + + zpl_u32 posx, posy; + zpl_u32 resx, resy; + zpl_u32 bufx, bufy; + zpl_u32 fill_attr; + zpl_u32 flags; + zpl_b32 show_window; + } zpl_pr_si; + + ZPL_DEF zpl_i32 zpl_pr_create(zpl_pr *process, const char **args, zpl_isize argc, zpl_pr_si si, zpl_pr_opts options); + ZPL_DEF void zpl_pr_destroy(zpl_pr *process); + ZPL_DEF void zpl_pr_terminate(zpl_pr *process, zpl_i32 err_code); + ZPL_DEF zpl_i32 zpl_pr_join(zpl_pr *process); + + //! @} + ZPL_END_C_DECLS +#endif + +#if defined(ZPL_MODULE_MATH) + // file: header/math.h + + /** @file math.c + @brief Math operations + @defgroup math Math operations + + OpenGL gamedev friendly library for math. + + @{ + */ + + ZPL_BEGIN_C_DECLS + + typedef union zpl_vec2 { + struct { + zpl_f32 x, y; + }; + struct { + zpl_f32 s, t; + }; + zpl_f32 e[2]; + } zpl_vec2; + + typedef union zpl_vec3 { + struct { + zpl_f32 x, y, z; + }; + struct { + zpl_f32 r, g, b; + }; + struct { + zpl_f32 s, t, p; + }; + + zpl_vec2 xy; + zpl_vec2 st; + zpl_f32 e[3]; + } zpl_vec3; + + typedef union zpl_vec4 { + struct { + zpl_f32 x, y, z, w; + }; + struct { + zpl_f32 r, g, b, a; + }; + struct { + zpl_f32 s, t, p, q; + }; + struct { + zpl_vec2 xy, zw; + }; + struct { + zpl_vec2 st, pq; + }; + zpl_vec3 xyz; + zpl_vec3 rgb; + zpl_f32 e[4]; + } zpl_vec4; + + typedef union zpl_mat2 { + struct { + zpl_vec2 x, y; + }; + zpl_vec2 col[2]; + zpl_f32 e[4]; + } zpl_mat2; + + typedef union zpl_mat3 { + struct { + zpl_vec3 x, y, z; + }; + zpl_vec3 col[3]; + zpl_f32 e[9]; + } zpl_mat3; + + typedef union zpl_mat4 { + struct { + zpl_vec4 x, y, z, w; + }; + zpl_vec4 col[4]; + zpl_f32 e[16]; + } zpl_mat4; + + typedef union zpl_quat { + struct { + zpl_f32 x, y, z, w; + }; + zpl_vec4 xyzw; + zpl_vec3 xyz; + zpl_f32 e[4]; + } zpl_quat; + + typedef union zpl_plane { + struct { + zpl_f32 a, b, c, d; + }; + zpl_vec4 xyzw; + zpl_vec3 n; + zpl_f32 e[4]; + } zpl_plane; + + typedef struct zpl_frustum { + zpl_plane x1; + zpl_plane x2; + zpl_plane y1; + zpl_plane y2; + zpl_plane z1; + zpl_plane z2; + } zpl_frustum; + + typedef zpl_f32 zpl_float2[2]; + typedef zpl_f32 zpl_float3[3]; + typedef zpl_f32 zpl_float4[4]; + + typedef struct zpl_rect2 { + zpl_vec2 pos, dim; + } zpl_rect2; + typedef struct zpl_rect3 { + zpl_vec3 pos, dim; + } zpl_rect3; + + typedef struct zpl_aabb2 { + zpl_vec2 min, max; + } zpl_aabb2; + typedef struct zpl_aabb3 { + zpl_vec3 min, max; + } zpl_aabb3; + + typedef short zpl_half; + + #ifndef ZPL_CONSTANTS + #define ZPL_CONSTANTS + #define ZPL_EPSILON 1.19209290e-7f + #define ZPL_ZERO 0.0f + #define ZPL_ONE 1.0f + #define ZPL_TWO_THIRDS 0.666666666666666666666666666666666666667f + + #define ZPL_TAU 6.28318530717958647692528676655900576f + #define ZPL_PI 3.14159265358979323846264338327950288f + #define ZPL_ONE_OVER_TAU 0.636619772367581343075535053490057448f + #define ZPL_ONE_OVER_PI 0.159154943091895335768883763372514362f + + #define ZPL_TAU_OVER_2 3.14159265358979323846264338327950288f + #define ZPL_TAU_OVER_4 1.570796326794896619231321691639751442f + #define ZPL_TAU_OVER_8 0.785398163397448309615660845819875721f + + #define ZPL_E 2.71828182845904523536f + #define ZPL_SQRT_TWO 1.41421356237309504880168872420969808f + #define ZPL_SQRT_THREE 1.73205080756887729352744634150587236f + #define ZPL_SQRT_FIVE 2.23606797749978969640917366873127623f + + #define ZPL_LOG_TWO 0.693147180559945309417232121458176568f + #define ZPL_LOG_TEN 2.30258509299404568401799145468436421f + #endif // ZPL_CONSTANTS + + #ifndef zpl_square + #define zpl_square(x) ((x) * (x)) + #endif + + #ifndef zpl_cube + #define zpl_cube(x) ((x) * (x) * (x)) + #endif + + #ifndef zpl_sign + #define zpl_sign(x) ((x) >= 0.0f ? 1.0f : -1.0f) + #endif + + #ifndef zpl_sign0 + #define zpl_sign0(x) ((x == 0.0f) ? 0.0f : ((x) >= 0.0f ? 1.0f : -1.0f)) + #endif + + ZPL_DEF zpl_f32 zpl_to_radians(zpl_f32 degrees); + ZPL_DEF zpl_f32 zpl_to_degrees(zpl_f32 radians); + + /* NOTE: Because to interpolate angles */ + ZPL_DEF zpl_f32 zpl_angle_diff(zpl_f32 radians_a, zpl_f32 radians_b); + + ZPL_DEF zpl_f32 zpl_copy_sign(zpl_f32 x, zpl_f32 y); + ZPL_DEF zpl_f32 zpl_remainder(zpl_f32 x, zpl_f32 y); + ZPL_DEF zpl_f32 zpl_mod(zpl_f32 x, zpl_f32 y); + ZPL_DEF zpl_f64 zpl_copy_sign64(zpl_f64 x, zpl_f64 y); + ZPL_DEF zpl_f64 zpl_floor64(zpl_f64 x); + ZPL_DEF zpl_f64 zpl_ceil64(zpl_f64 x); + ZPL_DEF zpl_f64 zpl_round64(zpl_f64 x); + ZPL_DEF zpl_f64 zpl_remainder64(zpl_f64 x, zpl_f64 y); + ZPL_DEF zpl_f64 zpl_abs64(zpl_f64 x); + ZPL_DEF zpl_f64 zpl_sign64(zpl_f64 x); + ZPL_DEF zpl_f64 zpl_mod64(zpl_f64 x, zpl_f64 y); + ZPL_DEF zpl_f32 zpl_sqrt(zpl_f32 a); + ZPL_DEF zpl_f32 zpl_rsqrt(zpl_f32 a); + ZPL_DEF zpl_f32 zpl_quake_rsqrt(zpl_f32 a); /* NOTE: It's probably better to use 1.0f/zpl_sqrt(a) + * And for simd, there is usually isqrt functions too! + */ + ZPL_DEF zpl_f32 zpl_hypot(zpl_f32 x, zpl_f32 y); + ZPL_DEF zpl_f32 zpl_sin(zpl_f32 radians); + ZPL_DEF zpl_f32 zpl_cos(zpl_f32 radians); + ZPL_DEF zpl_f32 zpl_tan(zpl_f32 radians); + ZPL_DEF zpl_f32 zpl_arcsin(zpl_f32 a); + ZPL_DEF zpl_f32 zpl_arccos(zpl_f32 a); + ZPL_DEF zpl_f32 zpl_arctan(zpl_f32 a); + ZPL_DEF zpl_f32 zpl_arctan2(zpl_f32 y, zpl_f32 x); + + ZPL_DEF zpl_f32 zpl_exp(zpl_f32 x); + ZPL_DEF zpl_f32 zpl_exp2(zpl_f32 x); + ZPL_DEF zpl_f32 zpl_log(zpl_f32 x); + ZPL_DEF zpl_f32 zpl_log2(zpl_f32 x); + ZPL_DEF zpl_f32 zpl_fast_exp(zpl_f32 x); /* NOTE: Only valid from -1 <= x <= +1 */ + ZPL_DEF zpl_f32 zpl_fast_exp2(zpl_f32 x); /* NOTE: Only valid from -1 <= x <= +1 */ + ZPL_DEF zpl_f32 zpl_pow(zpl_f32 x, zpl_f32 y); /* x^y */ + + ZPL_DEF zpl_f32 zpl_round(zpl_f32 x); + ZPL_DEF zpl_f32 zpl_floor(zpl_f32 x); + ZPL_DEF zpl_f32 zpl_ceil(zpl_f32 x); + + ZPL_DEF zpl_f32 zpl_half_to_float(zpl_half value); + ZPL_DEF zpl_half zpl_float_to_half(zpl_f32 value); + + ZPL_DEF zpl_vec2 zpl_vec2f_zero(void); + ZPL_DEF zpl_vec2 zpl_vec2f(zpl_f32 x, zpl_f32 y); + ZPL_DEF zpl_vec2 zpl_vec2fv(zpl_f32 x[2]); + + ZPL_DEF zpl_vec3 zpl_vec3f_zero(void); + ZPL_DEF zpl_vec3 zpl_vec3f(zpl_f32 x, zpl_f32 y, zpl_f32 z); + ZPL_DEF zpl_vec3 zpl_vec3fv(zpl_f32 x[3]); + + ZPL_DEF zpl_vec4 zpl_vec4f_zero(void); + ZPL_DEF zpl_vec4 zpl_vec4f(zpl_f32 x, zpl_f32 y, zpl_f32 z, zpl_f32 w); + ZPL_DEF zpl_vec4 zpl_vec4fv(zpl_f32 x[4]); + + ZPL_DEF zpl_f32 zpl_vec2_max(zpl_vec2 v); + ZPL_DEF zpl_f32 zpl_vec2_side(zpl_vec2 p, zpl_vec2 q, zpl_vec2 r); + ZPL_DEF void zpl_vec2_add(zpl_vec2 *d, zpl_vec2 v0, zpl_vec2 v1); + ZPL_DEF void zpl_vec2_sub(zpl_vec2 *d, zpl_vec2 v0, zpl_vec2 v1); + ZPL_DEF void zpl_vec2_mul(zpl_vec2 *d, zpl_vec2 v, zpl_f32 s); + ZPL_DEF void zpl_vec2_div(zpl_vec2 *d, zpl_vec2 v, zpl_f32 s); + + ZPL_DEF zpl_f32 zpl_vec3_max(zpl_vec3 v); + ZPL_DEF void zpl_vec3_add(zpl_vec3 *d, zpl_vec3 v0, zpl_vec3 v1); + ZPL_DEF void zpl_vec3_sub(zpl_vec3 *d, zpl_vec3 v0, zpl_vec3 v1); + ZPL_DEF void zpl_vec3_mul(zpl_vec3 *d, zpl_vec3 v, zpl_f32 s); + ZPL_DEF void zpl_vec3_div(zpl_vec3 *d, zpl_vec3 v, zpl_f32 s); + + ZPL_DEF void zpl_vec4_add(zpl_vec4 *d, zpl_vec4 v0, zpl_vec4 v1); + ZPL_DEF void zpl_vec4_sub(zpl_vec4 *d, zpl_vec4 v0, zpl_vec4 v1); + ZPL_DEF void zpl_vec4_mul(zpl_vec4 *d, zpl_vec4 v, zpl_f32 s); + ZPL_DEF void zpl_vec4_div(zpl_vec4 *d, zpl_vec4 v, zpl_f32 s); + + ZPL_DEF void zpl_vec2_addeq(zpl_vec2 *d, zpl_vec2 v); + ZPL_DEF void zpl_vec2_subeq(zpl_vec2 *d, zpl_vec2 v); + ZPL_DEF void zpl_vec2_muleq(zpl_vec2 *d, zpl_f32 s); + ZPL_DEF void zpl_vec2_diveq(zpl_vec2 *d, zpl_f32 s); + + ZPL_DEF void zpl_vec3_addeq(zpl_vec3 *d, zpl_vec3 v); + ZPL_DEF void zpl_vec3_subeq(zpl_vec3 *d, zpl_vec3 v); + ZPL_DEF void zpl_vec3_muleq(zpl_vec3 *d, zpl_f32 s); + ZPL_DEF void zpl_vec3_diveq(zpl_vec3 *d, zpl_f32 s); + + ZPL_DEF void zpl_vec4_addeq(zpl_vec4 *d, zpl_vec4 v); + ZPL_DEF void zpl_vec4_subeq(zpl_vec4 *d, zpl_vec4 v); + ZPL_DEF void zpl_vec4_muleq(zpl_vec4 *d, zpl_f32 s); + ZPL_DEF void zpl_vec4_diveq(zpl_vec4 *d, zpl_f32 s); + + ZPL_DEF zpl_f32 zpl_vec2_dot(zpl_vec2 v0, zpl_vec2 v1); + ZPL_DEF zpl_f32 zpl_vec3_dot(zpl_vec3 v0, zpl_vec3 v1); + ZPL_DEF zpl_f32 zpl_vec4_dot(zpl_vec4 v0, zpl_vec4 v1); + + ZPL_DEF void zpl_vec2_cross(zpl_f32 *d, zpl_vec2 v0, zpl_vec2 v1); + ZPL_DEF void zpl_vec3_cross(zpl_vec3 *d, zpl_vec3 v0, zpl_vec3 v1); + + ZPL_DEF zpl_f32 zpl_vec2_mag2(zpl_vec2 v); + ZPL_DEF zpl_f32 zpl_vec3_mag2(zpl_vec3 v); + ZPL_DEF zpl_f32 zpl_vec4_mag2(zpl_vec4 v); + + ZPL_DEF zpl_f32 zpl_vec2_mag(zpl_vec2 v); + ZPL_DEF zpl_f32 zpl_vec3_mag(zpl_vec3 v); + ZPL_DEF zpl_f32 zpl_vec4_mag(zpl_vec4 v); + + ZPL_DEF void zpl_vec2_norm(zpl_vec2 *d, zpl_vec2 v); + ZPL_DEF void zpl_vec3_norm(zpl_vec3 *d, zpl_vec3 v); + ZPL_DEF void zpl_vec4_norm(zpl_vec4 *d, zpl_vec4 v); + + ZPL_DEF void zpl_vec2_norm0(zpl_vec2 *d, zpl_vec2 v); + ZPL_DEF void zpl_vec3_norm0(zpl_vec3 *d, zpl_vec3 v); + ZPL_DEF void zpl_vec4_norm0(zpl_vec4 *d, zpl_vec4 v); + + ZPL_DEF void zpl_vec2_reflect(zpl_vec2 *d, zpl_vec2 i, zpl_vec2 n); + ZPL_DEF void zpl_vec3_reflect(zpl_vec3 *d, zpl_vec3 i, zpl_vec3 n); + ZPL_DEF void zpl_vec2_refract(zpl_vec2 *d, zpl_vec2 i, zpl_vec2 n, zpl_f32 eta); + ZPL_DEF void zpl_vec3_refract(zpl_vec3 *d, zpl_vec3 i, zpl_vec3 n, zpl_f32 eta); + + ZPL_DEF zpl_f32 zpl_vec2_aspect_ratio(zpl_vec2 v); + + ZPL_DEF void zpl_mat2_identity(zpl_mat2 *m); + ZPL_DEF void zpl_float22_identity(zpl_f32 m[2][2]); + + ZPL_DEF void zpl_mat2_transpose(zpl_mat2 *m); + ZPL_DEF void zpl_mat2_mul(zpl_mat2 *out, zpl_mat2 *m1, zpl_mat2 *m2); + ZPL_DEF void zpl_mat2_mul_vec2(zpl_vec2 *out, zpl_mat2 *m, zpl_vec2 in); + ZPL_DEF void zpl_mat2_inverse(zpl_mat2 *out, zpl_mat2 *in); + ZPL_DEF zpl_f32 zpl_mat2_determinate(zpl_mat2 *m); + + ZPL_DEF zpl_mat2 *zpl_mat2_v(zpl_vec2 m[2]); + ZPL_DEF zpl_mat2 *zpl_mat2_f(zpl_f32 m[2][2]); + ZPL_DEF zpl_float2 *zpl_float22_m(zpl_mat2 *m); + ZPL_DEF zpl_float2 *zpl_float22_v(zpl_vec2 m[2]); + ZPL_DEF zpl_float2 *zpl_float22_4(zpl_f32 m[4]); + + ZPL_DEF void zpl_float22_transpose(zpl_f32 (*vec)[2]); + ZPL_DEF void zpl_float22_mul(zpl_f32 (*out)[2], zpl_f32 (*mat1)[2], zpl_f32 (*mat2)[2]); + ZPL_DEF void zpl_float22_mul_vec2(zpl_vec2 *out, zpl_f32 m[2][2], zpl_vec2 in); + + ZPL_DEF void zpl_mat3_identity(zpl_mat3 *m); + ZPL_DEF void zpl_float33_identity(zpl_f32 m[3][3]); + + ZPL_DEF void zpl_mat3_transpose(zpl_mat3 *m); + ZPL_DEF void zpl_mat3_mul(zpl_mat3 *out, zpl_mat3 *m1, zpl_mat3 *m2); + ZPL_DEF void zpl_mat3_mul_vec3(zpl_vec3 *out, zpl_mat3 *m, zpl_vec3 in); + ZPL_DEF void zpl_mat3_inverse(zpl_mat3 *out, zpl_mat3 *in); + ZPL_DEF zpl_f32 zpl_mat3_determinate(zpl_mat3 *m); + + ZPL_DEF zpl_mat3 *zpl_mat3_v(zpl_vec3 m[3]); + ZPL_DEF zpl_mat3 *zpl_mat3_f(zpl_f32 m[3][3]); + + ZPL_DEF zpl_float3 *zpl_float33_m(zpl_mat3 *m); + ZPL_DEF zpl_float3 *zpl_float33_v(zpl_vec3 m[3]); + ZPL_DEF zpl_float3 *zpl_float33_9(zpl_f32 m[9]); + + ZPL_DEF void zpl_float33_transpose(zpl_f32 (*vec)[3]); + ZPL_DEF void zpl_float33_mul(zpl_f32 (*out)[3], zpl_f32 (*mat1)[3], zpl_f32 (*mat2)[3]); + ZPL_DEF void zpl_float33_mul_vec3(zpl_vec3 *out, zpl_f32 m[3][3], zpl_vec3 in); + + ZPL_DEF void zpl_mat4_identity(zpl_mat4 *m); + ZPL_DEF void zpl_float44_identity(zpl_f32 m[4][4]); + ZPL_DEF void zpl_mat4_copy(zpl_mat4* out, zpl_mat4* m); + + ZPL_DEF void zpl_mat4_transpose(zpl_mat4 *m); + ZPL_DEF void zpl_mat4_mul(zpl_mat4 *out, zpl_mat4 *m1, zpl_mat4 *m2); + ZPL_DEF void zpl_mat4_mul_vec4(zpl_vec4 *out, zpl_mat4 *m, zpl_vec4 in); + ZPL_DEF void zpl_mat4_inverse(zpl_mat4 *out, zpl_mat4 *in); + + ZPL_DEF zpl_mat4 *zpl_mat4_v(zpl_vec4 m[4]); + ZPL_DEF zpl_mat4 *zpl_mat4_f(zpl_f32 m[4][4]); + + ZPL_DEF zpl_float4 *zpl_float44_m(zpl_mat4 *m); + ZPL_DEF zpl_float4 *zpl_float44_v(zpl_vec4 m[4]); + ZPL_DEF zpl_float4 *zpl_float44_16(zpl_f32 m[16]); + + ZPL_DEF void zpl_float44_transpose(zpl_f32 (*vec)[4]); + ZPL_DEF void zpl_float44_mul(zpl_f32 (*out)[4], zpl_f32 (*mat1)[4], zpl_f32 (*mat2)[4]); + ZPL_DEF void zpl_float44_mul_vec4(zpl_vec4 *out, zpl_f32 m[4][4], zpl_vec4 in); + + ZPL_DEF void zpl_mat4_axis_angle(zpl_mat4* out, zpl_vec3 v, zpl_f32 angle_radians); + ZPL_DEF void zpl_mat4_to_translate(zpl_mat4* out, zpl_vec3 v); + ZPL_DEF void zpl_mat4_to_rotate(zpl_mat4* out, zpl_vec3 v, zpl_f32 angle_radians); + ZPL_DEF void zpl_mat4_to_scale(zpl_mat4* out, zpl_vec3 v); + ZPL_DEF void zpl_mat4_to_scalef(zpl_mat4* out, zpl_f32 s); + ZPL_DEF void zpl_mat4_translate(zpl_mat4* out, zpl_vec3 v); + ZPL_DEF void zpl_mat4_rotate(zpl_mat4* out, zpl_vec3 v, zpl_f32 angle_radians); + ZPL_DEF void zpl_mat4_scale(zpl_mat4* out, zpl_vec3 v); + ZPL_DEF void zpl_mat4_scalef(zpl_mat4 *out, zpl_f32 s); + ZPL_DEF void zpl_mat4_ortho2d(zpl_mat4 *out, zpl_f32 left, zpl_f32 right, zpl_f32 bottom, zpl_f32 top); + ZPL_DEF void zpl_mat4_ortho3d(zpl_mat4 *out, zpl_f32 left, zpl_f32 right, zpl_f32 bottom, zpl_f32 top, zpl_f32 z_near, zpl_f32 z_far); + ZPL_DEF void zpl_mat4_perspective(zpl_mat4 *out, zpl_f32 fovy, zpl_f32 aspect, zpl_f32 z_near, zpl_f32 z_far); + ZPL_DEF void zpl_mat4_infinite_perspective(zpl_mat4 *out, zpl_f32 fovy, zpl_f32 aspect, zpl_f32 z_near); + + ZPL_DEF void zpl_mat4_ortho2d_dx(zpl_mat4 *out, zpl_f32 left, zpl_f32 right, zpl_f32 bottom, zpl_f32 top); + ZPL_DEF void zpl_mat4_ortho3d_dx(zpl_mat4 *out, zpl_f32 left, zpl_f32 right, zpl_f32 bottom, zpl_f32 top, zpl_f32 z_near, zpl_f32 z_far); + ZPL_DEF void zpl_mat4_perspective_dx(zpl_mat4 *out, zpl_f32 fovy, zpl_f32 aspect, zpl_f32 z_near, zpl_f32 z_far); + ZPL_DEF void zpl_mat4_infinite_perspective_dx(zpl_mat4 *out, zpl_f32 fovy, zpl_f32 aspect, zpl_f32 z_near); + + ZPL_DEF void zpl_mat4_look_at(zpl_mat4 *out, zpl_vec3 eye, zpl_vec3 centre, zpl_vec3 up); + + ZPL_DEF void zpl_mat4_look_at_lh(zpl_mat4 *out, zpl_vec3 eye, zpl_vec3 centre, zpl_vec3 up); + + ZPL_DEF zpl_quat zpl_quatf(zpl_f32 x, zpl_f32 y, zpl_f32 z, zpl_f32 w); + ZPL_DEF zpl_quat zpl_quatfv(zpl_f32 e[4]); + ZPL_DEF zpl_quat zpl_quat_axis_angle(zpl_vec3 axis, zpl_f32 angle_radians); + ZPL_DEF zpl_quat zpl_quat_euler_angles(zpl_f32 pitch, zpl_f32 yaw, zpl_f32 roll); + ZPL_DEF zpl_quat zpl_quat_identity(void); + + ZPL_DEF void zpl_quat_add(zpl_quat *d, zpl_quat q0, zpl_quat q1); + ZPL_DEF void zpl_quat_sub(zpl_quat *d, zpl_quat q0, zpl_quat q1); + ZPL_DEF void zpl_quat_mul(zpl_quat *d, zpl_quat q0, zpl_quat q1); + ZPL_DEF void zpl_quat_div(zpl_quat *d, zpl_quat q0, zpl_quat q1); + + ZPL_DEF void zpl_quat_mulf(zpl_quat *d, zpl_quat q, zpl_f32 s); + ZPL_DEF void zpl_quat_divf(zpl_quat *d, zpl_quat q, zpl_f32 s); + + ZPL_DEF void zpl_quat_addeq(zpl_quat *d, zpl_quat q); + ZPL_DEF void zpl_quat_subeq(zpl_quat *d, zpl_quat q); + ZPL_DEF void zpl_quat_muleq(zpl_quat *d, zpl_quat q); + ZPL_DEF void zpl_quat_diveq(zpl_quat *d, zpl_quat q); + + ZPL_DEF void zpl_quat_muleqf(zpl_quat *d, zpl_f32 s); + ZPL_DEF void zpl_quat_diveqf(zpl_quat *d, zpl_f32 s); + + ZPL_DEF zpl_f32 zpl_quat_dot(zpl_quat q0, zpl_quat q1); + ZPL_DEF zpl_f32 zpl_quat_mag(zpl_quat q); + + ZPL_DEF void zpl_quat_norm(zpl_quat *d, zpl_quat q); + ZPL_DEF void zpl_quat_conj(zpl_quat *d, zpl_quat q); + ZPL_DEF void zpl_quat_inverse(zpl_quat *d, zpl_quat q); + + ZPL_DEF void zpl_quat_axis(zpl_vec3 *axis, zpl_quat q); + ZPL_DEF zpl_f32 zpl_quat_angle(zpl_quat q); + + ZPL_DEF zpl_f32 zpl_quat_pitch(zpl_quat q); + ZPL_DEF zpl_f32 zpl_quat_yaw(zpl_quat q); + ZPL_DEF zpl_f32 zpl_quat_roll(zpl_quat q); + + /* NOTE: Rotate v by q */ + ZPL_DEF void zpl_quat_rotate_vec3(zpl_vec3 *d, zpl_quat q, zpl_vec3 v); + ZPL_DEF void zpl_mat4_from_quat(zpl_mat4 *out, zpl_quat q); + ZPL_DEF void zpl_quat_from_mat4(zpl_quat *out, zpl_mat4 *m); + + /* Plane math. */ + ZPL_DEF zpl_f32 zpl_plane_distance(zpl_plane* p, zpl_vec3 v); + + /* Frustum culling. */ + ZPL_DEF void zpl_frustum_create(zpl_frustum* out, zpl_mat4* camera, zpl_mat4* proj); + ZPL_DEF zpl_b8 zpl_frustum_sphere_inside(zpl_frustum* frustum, zpl_vec3 center, zpl_f32 radius); + ZPL_DEF zpl_b8 zpl_frustum_point_inside(zpl_frustum* frustum, zpl_vec3 point); + ZPL_DEF zpl_b8 zpl_frustum_box_inside(zpl_frustum* frustum, zpl_aabb3 box); + + /* Interpolations */ + ZPL_DEF zpl_f32 zpl_lerp(zpl_f32 a, zpl_f32 b, zpl_f32 t); + ZPL_DEF zpl_f32 zpl_unlerp(zpl_f32 t, zpl_f32 a, zpl_f32 b); + ZPL_DEF zpl_f32 zpl_smooth_step(zpl_f32 a, zpl_f32 b, zpl_f32 t); + ZPL_DEF zpl_f32 zpl_smoother_step(zpl_f32 a, zpl_f32 b, zpl_f32 t); + + ZPL_DEF void zpl_vec2_lerp(zpl_vec2 *d, zpl_vec2 a, zpl_vec2 b, zpl_f32 t); + ZPL_DEF void zpl_vec3_lerp(zpl_vec3 *d, zpl_vec3 a, zpl_vec3 b, zpl_f32 t); + ZPL_DEF void zpl_vec4_lerp(zpl_vec4 *d, zpl_vec4 a, zpl_vec4 b, zpl_f32 t); + + ZPL_DEF void zpl_vec2_cslerp(zpl_vec2 *d, zpl_vec2 a, zpl_vec2 v0, zpl_vec2 b, zpl_vec2 v1, zpl_f32 t); + ZPL_DEF void zpl_vec3_cslerp(zpl_vec3 *d, zpl_vec3 a, zpl_vec3 v0, zpl_vec3 b, zpl_vec3 v1, zpl_f32 t); + ZPL_DEF void zpl_vec2_dcslerp(zpl_vec2 *d, zpl_vec2 a, zpl_vec2 v0, zpl_vec2 b, zpl_vec2 v1, zpl_f32 t); + ZPL_DEF void zpl_vec3_dcslerp(zpl_vec3 *d, zpl_vec3 a, zpl_vec3 v0, zpl_vec3 b, zpl_vec3 v1, zpl_f32 t); + + ZPL_DEF void zpl_quat_lerp(zpl_quat *d, zpl_quat a, zpl_quat b, zpl_f32 t); + ZPL_DEF void zpl_quat_nlerp(zpl_quat *d, zpl_quat a, zpl_quat b, zpl_f32 t); + ZPL_DEF void zpl_quat_slerp(zpl_quat *d, zpl_quat a, zpl_quat b, zpl_f32 t); + ZPL_DEF void zpl_quat_nquad(zpl_quat *d, zpl_quat p, zpl_quat a, zpl_quat b, zpl_quat q, zpl_f32 t); + ZPL_DEF void zpl_quat_squad(zpl_quat *d, zpl_quat p, zpl_quat a, zpl_quat b, zpl_quat q, zpl_f32 t); + ZPL_DEF void zpl_quat_slerp_approx(zpl_quat *d, zpl_quat a, zpl_quat b, zpl_f32 t); + ZPL_DEF void zpl_quat_squad_approx(zpl_quat *d, zpl_quat p, zpl_quat a, zpl_quat b, zpl_quat q, zpl_f32 t); + + /* rects */ + ZPL_DEF zpl_rect2 zpl_rect2f(zpl_vec2 pos, zpl_vec2 dim); + ZPL_DEF zpl_rect3 zpl_rect3f(zpl_vec3 pos, zpl_vec3 dim); + + ZPL_DEF zpl_aabb2 zpl_aabb2f(zpl_f32 minx, zpl_f32 miny, zpl_f32 maxx, zpl_f32 maxy); + ZPL_DEF zpl_aabb3 zpl_aabb3f(zpl_f32 minx, zpl_f32 miny, zpl_f32 minz, zpl_f32 maxx, zpl_f32 maxy, zpl_f32 maxz); + + ZPL_DEF zpl_aabb2 zpl_aabb2_rect2(zpl_rect2 a); + ZPL_DEF zpl_aabb3 zpl_aabb3_rect3(zpl_rect3 a); + ZPL_DEF zpl_rect2 zpl_rect2_aabb2(zpl_aabb2 a); + ZPL_DEF zpl_rect3 zpl_rect3_aabb3(zpl_aabb3 a); + + ZPL_DEF int zpl_rect2_contains(zpl_rect2 a, zpl_f32 x, zpl_f32 y); + ZPL_DEF int zpl_rect2_contains_vec2(zpl_rect2 a, zpl_vec2 p); + ZPL_DEF int zpl_rect2_intersects(zpl_rect2 a, zpl_rect2 b); + ZPL_DEF int zpl_rect2_intersection_result(zpl_rect2 a, zpl_rect2 b, zpl_rect2 *intersection); + ZPL_DEF int zpl_aabb2_contains(zpl_aabb2 a, zpl_f32 x, zpl_f32 y); + ZPL_DEF int zpl_aabb3_contains(zpl_aabb3 a, zpl_f32 x, zpl_f32 y, zpl_f32 z); + + /* rectangle partitioning: based on https://halt.software/dead-simple-layouts/ */ + ZPL_DEF zpl_aabb2 zpl_aabb2_cut_left(zpl_aabb2 *a, zpl_f32 b); + ZPL_DEF zpl_aabb2 zpl_aabb2_cut_right(zpl_aabb2 *a, zpl_f32 b); + ZPL_DEF zpl_aabb2 zpl_aabb2_cut_top(zpl_aabb2 *a, zpl_f32 b); + ZPL_DEF zpl_aabb2 zpl_aabb2_cut_bottom(zpl_aabb2 *a, zpl_f32 b); + + ZPL_DEF zpl_aabb2 zpl_aabb2_get_left(const zpl_aabb2 *a, zpl_f32 b); + ZPL_DEF zpl_aabb2 zpl_aabb2_get_right(const zpl_aabb2 *a, zpl_f32 b); + ZPL_DEF zpl_aabb2 zpl_aabb2_get_top(const zpl_aabb2 *a, zpl_f32 b); + ZPL_DEF zpl_aabb2 zpl_aabb2_get_bottom(const zpl_aabb2 *a, zpl_f32 b); + + ZPL_DEF zpl_aabb2 zpl_aabb2_add_left(const zpl_aabb2 *a, zpl_f32 b); + ZPL_DEF zpl_aabb2 zpl_aabb2_add_right(const zpl_aabb2 *a, zpl_f32 b); + ZPL_DEF zpl_aabb2 zpl_aabb2_add_top(const zpl_aabb2 *a, zpl_f32 b); + ZPL_DEF zpl_aabb2 zpl_aabb2_add_bottom(const zpl_aabb2 *a, zpl_f32 b); + + ZPL_DEF zpl_aabb2 zpl_aabb2_contract(const zpl_aabb2 *a, zpl_f32 b); + ZPL_DEF zpl_aabb2 zpl_aabb2_expand(const zpl_aabb2 *a, zpl_f32 b); + + //! @} + ZPL_END_C_DECLS + #if defined(__cplusplus) + ZPL_INLINE bool operator==(zpl_vec2 a, zpl_vec2 b) { return (a.x == b.x) && (a.y == b.y); } + ZPL_INLINE bool operator!=(zpl_vec2 a, zpl_vec2 b) { return !operator==(a, b); } + + ZPL_INLINE zpl_vec2 operator+(zpl_vec2 a) { return a; } + ZPL_INLINE zpl_vec2 operator-(zpl_vec2 a) { zpl_vec2 r = {-a.x, -a.y}; return r; } + + ZPL_INLINE zpl_vec2 operator+(zpl_vec2 a, zpl_vec2 b) { zpl_vec2 r; zpl_vec2_add(&r, a, b); return r; } + ZPL_INLINE zpl_vec2 operator-(zpl_vec2 a, zpl_vec2 b) { zpl_vec2 r; zpl_vec2_sub(&r, a, b); return r; } + + ZPL_INLINE zpl_vec2 operator*(zpl_vec2 a, float scalar) { zpl_vec2 r; zpl_vec2_mul(&r, a, scalar); return r; } + ZPL_INLINE zpl_vec2 operator*(float scalar, zpl_vec2 a) { return operator*(a, scalar); } + + ZPL_INLINE zpl_vec2 operator/(zpl_vec2 a, float scalar) { return operator*(a, 1.0f/scalar); } + + /* Hadamard Product */ + ZPL_INLINE zpl_vec2 operator*(zpl_vec2 a, zpl_vec2 b) { zpl_vec2 r = {a.x*b.x, a.y*b.y}; return r; } + ZPL_INLINE zpl_vec2 operator/(zpl_vec2 a, zpl_vec2 b) { zpl_vec2 r = {a.x/b.x, a.y/b.y}; return r; } + + ZPL_INLINE zpl_vec2 &operator+=(zpl_vec2 &a, zpl_vec2 b) { return (a = a + b); } + ZPL_INLINE zpl_vec2 &operator-=(zpl_vec2 &a, zpl_vec2 b) { return (a = a - b); } + ZPL_INLINE zpl_vec2 &operator*=(zpl_vec2 &a, float scalar) { return (a = a * scalar); } + ZPL_INLINE zpl_vec2 &operator/=(zpl_vec2 &a, float scalar) { return (a = a / scalar); } + + + ZPL_INLINE bool operator==(zpl_vec3 a, zpl_vec3 b) { return (a.x == b.x) && (a.y == b.y) && (a.z == b.z); } + ZPL_INLINE bool operator!=(zpl_vec3 a, zpl_vec3 b) { return !operator==(a, b); } + + ZPL_INLINE zpl_vec3 operator+(zpl_vec3 a) { return a; } + ZPL_INLINE zpl_vec3 operator-(zpl_vec3 a) { zpl_vec3 r = {-a.x, -a.y, -a.z}; return r; } + + ZPL_INLINE zpl_vec3 operator+(zpl_vec3 a, zpl_vec3 b) { zpl_vec3 r; zpl_vec3_add(&r, a, b); return r; } + ZPL_INLINE zpl_vec3 operator-(zpl_vec3 a, zpl_vec3 b) { zpl_vec3 r; zpl_vec3_sub(&r, a, b); return r; } + + ZPL_INLINE zpl_vec3 operator*(zpl_vec3 a, float scalar) { zpl_vec3 r; zpl_vec3_mul(&r, a, scalar); return r; } + ZPL_INLINE zpl_vec3 operator*(float scalar, zpl_vec3 a) { return operator*(a, scalar); } + + ZPL_INLINE zpl_vec3 operator/(zpl_vec3 a, float scalar) { return operator*(a, 1.0f/scalar); } + + /* Hadamard Product */ + ZPL_INLINE zpl_vec3 operator*(zpl_vec3 a, zpl_vec3 b) { zpl_vec3 r = {a.x*b.x, a.y*b.y, a.z*b.z}; return r; } + ZPL_INLINE zpl_vec3 operator/(zpl_vec3 a, zpl_vec3 b) { zpl_vec3 r = {a.x/b.x, a.y/b.y, a.z/b.z}; return r; } + + ZPL_INLINE zpl_vec3 &operator+=(zpl_vec3 &a, zpl_vec3 b) { return (a = a + b); } + ZPL_INLINE zpl_vec3 &operator-=(zpl_vec3 &a, zpl_vec3 b) { return (a = a - b); } + ZPL_INLINE zpl_vec3 &operator*=(zpl_vec3 &a, float scalar) { return (a = a * scalar); } + ZPL_INLINE zpl_vec3 &operator/=(zpl_vec3 &a, float scalar) { return (a = a / scalar); } + + + ZPL_INLINE bool operator==(zpl_vec4 a, zpl_vec4 b) { return (a.x == b.x) && (a.y == b.y) && (a.z == b.z) && (a.w == b.w); } + ZPL_INLINE bool operator!=(zpl_vec4 a, zpl_vec4 b) { return !operator==(a, b); } + + ZPL_INLINE zpl_vec4 operator+(zpl_vec4 a) { return a; } + ZPL_INLINE zpl_vec4 operator-(zpl_vec4 a) { zpl_vec4 r = {-a.x, -a.y, -a.z, -a.w}; return r; } + + ZPL_INLINE zpl_vec4 operator+(zpl_vec4 a, zpl_vec4 b) { zpl_vec4 r; zpl_vec4_add(&r, a, b); return r; } + ZPL_INLINE zpl_vec4 operator-(zpl_vec4 a, zpl_vec4 b) { zpl_vec4 r; zpl_vec4_sub(&r, a, b); return r; } + + ZPL_INLINE zpl_vec4 operator*(zpl_vec4 a, float scalar) { zpl_vec4 r; zpl_vec4_mul(&r, a, scalar); return r; } + ZPL_INLINE zpl_vec4 operator*(float scalar, zpl_vec4 a) { return operator*(a, scalar); } + + ZPL_INLINE zpl_vec4 operator/(zpl_vec4 a, float scalar) { return operator*(a, 1.0f/scalar); } + + /* Hadamard Product */ + ZPL_INLINE zpl_vec4 operator*(zpl_vec4 a, zpl_vec4 b) { zpl_vec4 r = {a.x*b.x, a.y*b.y, a.z*b.z, a.w*b.w}; return r; } + ZPL_INLINE zpl_vec4 operator/(zpl_vec4 a, zpl_vec4 b) { zpl_vec4 r = {a.x/b.x, a.y/b.y, a.z/b.z, a.w/b.w}; return r; } + + ZPL_INLINE zpl_vec4 &operator+=(zpl_vec4 &a, zpl_vec4 b) { return (a = a + b); } + ZPL_INLINE zpl_vec4 &operator-=(zpl_vec4 &a, zpl_vec4 b) { return (a = a - b); } + ZPL_INLINE zpl_vec4 &operator*=(zpl_vec4 &a, float scalar) { return (a = a * scalar); } + ZPL_INLINE zpl_vec4 &operator/=(zpl_vec4 &a, float scalar) { return (a = a / scalar); } + + + ZPL_INLINE zpl_mat2 operator+(zpl_mat2 const &a, zpl_mat2 const &b) { + int i, j; + zpl_mat2 r = {0}; + for (j = 0; j < 2; j++) { + for (i = 0; i < 2; i++) + r.e[2*j+i] = a.e[2*j+i] + b.e[2*j+i]; + } + return r; + } + + ZPL_INLINE zpl_mat2 operator-(zpl_mat2 const &a, zpl_mat2 const &b) { + int i, j; + zpl_mat2 r = {0}; + for (j = 0; j < 2; j++) { + for (i = 0; i < 2; i++) + r.e[2*j+i] = a.e[2*j+i] - b.e[2*j+i]; + } + return r; + } + + ZPL_INLINE zpl_mat2 operator*(zpl_mat2 const &a, zpl_mat2 const &b) { zpl_mat2 r; zpl_mat2_mul(&r, (zpl_mat2 *)&a, (zpl_mat2 *)&b); return r; } + ZPL_INLINE zpl_vec2 operator*(zpl_mat2 const &a, zpl_vec2 v) { zpl_vec2 r; zpl_mat2_mul_vec2(&r, (zpl_mat2 *)&a, v); return r; } + ZPL_INLINE zpl_mat2 operator*(zpl_mat2 const &a, float scalar) { + zpl_mat2 r = {0}; + int i; + for (i = 0; i < 2*2; i++) r.e[i] = a.e[i] * scalar; + return r; + } + ZPL_INLINE zpl_mat2 operator*(float scalar, zpl_mat2 const &a) { return operator*(a, scalar); } + ZPL_INLINE zpl_mat2 operator/(zpl_mat2 const &a, float scalar) { return operator*(a, 1.0f/scalar); } + + ZPL_INLINE zpl_mat2& operator+=(zpl_mat2& a, zpl_mat2 const &b) { return (a = a + b); } + ZPL_INLINE zpl_mat2& operator-=(zpl_mat2& a, zpl_mat2 const &b) { return (a = a - b); } + ZPL_INLINE zpl_mat2& operator*=(zpl_mat2& a, zpl_mat2 const &b) { return (a = a * b); } + + + + ZPL_INLINE zpl_mat3 operator+(zpl_mat3 const &a, zpl_mat3 const &b) { + int i, j; + zpl_mat3 r = {0}; + for (j = 0; j < 3; j++) { + for (i = 0; i < 3; i++) + r.e[3*j+i] = a.e[3*j+i] + b.e[3*j+i]; + } + return r; + } + + ZPL_INLINE zpl_mat3 operator-(zpl_mat3 const &a, zpl_mat3 const &b) { + int i, j; + zpl_mat3 r = {0}; + for (j = 0; j < 3; j++) { + for (i = 0; i < 3; i++) + r.e[3*j+i] = a.e[3*j+i] - b.e[3*j+i]; + } + return r; + } + + ZPL_INLINE zpl_mat3 operator*(zpl_mat3 const &a, zpl_mat3 const &b) { zpl_mat3 r; zpl_mat3_mul(&r, (zpl_mat3 *)&a, (zpl_mat3 *)&b); return r; } + ZPL_INLINE zpl_vec3 operator*(zpl_mat3 const &a, zpl_vec3 v) { zpl_vec3 r; zpl_mat3_mul_vec3(&r, (zpl_mat3 *)&a, v); return r; } + ZPL_INLINE zpl_mat3 operator*(zpl_mat3 const &a, float scalar) { + zpl_mat3 r = {0}; + int i; + for (i = 0; i < 3*3; i++) r.e[i] = a.e[i] * scalar; + return r; + } + ZPL_INLINE zpl_mat3 operator*(float scalar, zpl_mat3 const &a) { return operator*(a, scalar); } + ZPL_INLINE zpl_mat3 operator/(zpl_mat3 const &a, float scalar) { return operator*(a, 1.0f/scalar); } + + ZPL_INLINE zpl_mat3& operator+=(zpl_mat3& a, zpl_mat3 const &b) { return (a = a + b); } + ZPL_INLINE zpl_mat3& operator-=(zpl_mat3& a, zpl_mat3 const &b) { return (a = a - b); } + ZPL_INLINE zpl_mat3& operator*=(zpl_mat3& a, zpl_mat3 const &b) { return (a = a * b); } + + + + ZPL_INLINE zpl_mat4 operator+(zpl_mat4 const &a, zpl_mat4 const &b) { + int i, j; + zpl_mat4 r = {0}; + for (j = 0; j < 4; j++) { + for (i = 0; i < 4; i++) + r.e[4*j+i] = a.e[4*j+i] + b.e[4*j+i]; + } + return r; + } + + ZPL_INLINE zpl_mat4 operator-(zpl_mat4 const &a, zpl_mat4 const &b) { + int i, j; + zpl_mat4 r = {0}; + for (j = 0; j < 4; j++) { + for (i = 0; i < 4; i++) + r.e[4*j+i] = a.e[4*j+i] - b.e[4*j+i]; + } + return r; + } + + ZPL_INLINE zpl_mat4 operator*(zpl_mat4 const &a, zpl_mat4 const &b) { zpl_mat4 r; zpl_mat4_mul(&r, (zpl_mat4 *)&a, (zpl_mat4 *)&b); return r; } + ZPL_INLINE zpl_vec4 operator*(zpl_mat4 const &a, zpl_vec4 v) { zpl_vec4 r; zpl_mat4_mul_vec4(&r, (zpl_mat4 *)&a, v); return r; } + ZPL_INLINE zpl_mat4 operator*(zpl_mat4 const &a, float scalar) { + zpl_mat4 r = {0}; + int i; + for (i = 0; i < 4*4; i++) r.e[i] = a.e[i] * scalar; + return r; + } + ZPL_INLINE zpl_mat4 operator*(float scalar, zpl_mat4 const &a) { return operator*(a, scalar); } + ZPL_INLINE zpl_mat4 operator/(zpl_mat4 const &a, float scalar) { return operator*(a, 1.0f/scalar); } + + ZPL_INLINE zpl_mat4& operator+=(zpl_mat4 &a, zpl_mat4 const &b) { return (a = a + b); } + ZPL_INLINE zpl_mat4& operator-=(zpl_mat4 &a, zpl_mat4 const &b) { return (a = a - b); } + ZPL_INLINE zpl_mat4& operator*=(zpl_mat4 &a, zpl_mat4 const &b) { return (a = a * b); } + + + + ZPL_INLINE bool operator==(zpl_quat a, zpl_quat b) { return a.xyzw == b.xyzw; } + ZPL_INLINE bool operator!=(zpl_quat a, zpl_quat b) { return !operator==(a, b); } + + ZPL_INLINE zpl_quat operator+(zpl_quat q) { return q; } + ZPL_INLINE zpl_quat operator-(zpl_quat q) { return zpl_quatf(-q.x, -q.y, -q.z, -q.w); } + + ZPL_INLINE zpl_quat operator+(zpl_quat a, zpl_quat b) { zpl_quat r; zpl_quat_add(&r, a, b); return r; } + ZPL_INLINE zpl_quat operator-(zpl_quat a, zpl_quat b) { zpl_quat r; zpl_quat_sub(&r, a, b); return r; } + + ZPL_INLINE zpl_quat operator*(zpl_quat a, zpl_quat b) { zpl_quat r; zpl_quat_mul(&r, a, b); return r; } + ZPL_INLINE zpl_quat operator*(zpl_quat q, float s) { zpl_quat r; zpl_quat_mulf(&r, q, s); return r; } + ZPL_INLINE zpl_quat operator*(float s, zpl_quat q) { return operator*(q, s); } + ZPL_INLINE zpl_quat operator/(zpl_quat q, float s) { zpl_quat r; zpl_quat_divf(&r, q, s); return r; } + + ZPL_INLINE zpl_quat &operator+=(zpl_quat &a, zpl_quat b) { zpl_quat_addeq(&a, b); return a; } + ZPL_INLINE zpl_quat &operator-=(zpl_quat &a, zpl_quat b) { zpl_quat_subeq(&a, b); return a; } + ZPL_INLINE zpl_quat &operator*=(zpl_quat &a, zpl_quat b) { zpl_quat_muleq(&a, b); return a; } + ZPL_INLINE zpl_quat &operator/=(zpl_quat &a, zpl_quat b) { zpl_quat_diveq(&a, b); return a; } + + ZPL_INLINE zpl_quat &operator*=(zpl_quat &a, float b) { zpl_quat_muleqf(&a, b); return a; } + ZPL_INLINE zpl_quat &operator/=(zpl_quat &a, float b) { zpl_quat_diveqf(&a, b); return a; } + + /* Rotate v by a */ + ZPL_INLINE zpl_vec3 operator*(zpl_quat q, zpl_vec3 v) { zpl_vec3 r; zpl_quat_rotate_vec3(&r, q, v); return r; } + #endif +#endif + +#if defined(ZPL_MODULE_PARSER) + // file: header/adt.h + + ZPL_BEGIN_C_DECLS + + typedef enum zpl_adt_type { + ZPL_ADT_TYPE_UNINITIALISED, /* node was not initialised, this is a programming error! */ + ZPL_ADT_TYPE_ARRAY, + ZPL_ADT_TYPE_OBJECT, + ZPL_ADT_TYPE_STRING, + ZPL_ADT_TYPE_MULTISTRING, + ZPL_ADT_TYPE_INTEGER, + ZPL_ADT_TYPE_REAL, + } zpl_adt_type; + + typedef enum zpl_adt_props { + ZPL_ADT_PROPS_NONE, + ZPL_ADT_PROPS_NAN, + ZPL_ADT_PROPS_NAN_NEG, + ZPL_ADT_PROPS_INFINITY, + ZPL_ADT_PROPS_INFINITY_NEG, + ZPL_ADT_PROPS_FALSE, + ZPL_ADT_PROPS_TRUE, + ZPL_ADT_PROPS_NULL, + ZPL_ADT_PROPS_IS_EXP, + ZPL_ADT_PROPS_IS_HEX, + + // Used internally so that people can fill in real numbers they plan to write. + ZPL_ADT_PROPS_IS_PARSED_REAL, + } zpl_adt_props; + + typedef enum zpl_adt_naming_style { + ZPL_ADT_NAME_STYLE_DOUBLE_QUOTE, + ZPL_ADT_NAME_STYLE_SINGLE_QUOTE, + ZPL_ADT_NAME_STYLE_NO_QUOTES, + } zpl_adt_naming_style; + + typedef enum zpl_adt_assign_style { + ZPL_ADT_ASSIGN_STYLE_COLON, + ZPL_ADT_ASSIGN_STYLE_EQUALS, + ZPL_ADT_ASSIGN_STYLE_LINE, + } zpl_adt_assign_style; + + typedef enum zpl_adt_delim_style { + ZPL_ADT_DELIM_STYLE_COMMA, + ZPL_ADT_DELIM_STYLE_LINE, + ZPL_ADT_DELIM_STYLE_NEWLINE, + } zpl_adt_delim_style; + + typedef enum zpl_adt_error { + ZPL_ADT_ERROR_NONE, + ZPL_ADT_ERROR_INTERNAL, + ZPL_ADT_ERROR_ALREADY_CONVERTED, + ZPL_ADT_ERROR_INVALID_TYPE, + ZPL_ADT_ERROR_OUT_OF_MEMORY, + } zpl_adt_error; + + typedef struct zpl_adt_node { + char const *name; + struct zpl_adt_node *parent; + + /* properties */ + zpl_u8 type :4; + zpl_u8 props :4; + #ifndef ZPL_PARSER_DISABLE_ANALYSIS + zpl_u8 cfg_mode :1; + zpl_u8 name_style :2; + zpl_u8 assign_style:2; + zpl_u8 delim_style :2; + zpl_u8 delim_line_width :4; + zpl_u8 assign_line_width:4; + #endif + + /* adt data */ + union { + char const *string; + struct zpl_adt_node *nodes; ///< zpl_array + struct { + union { + zpl_f64 real; + zpl_i64 integer; + }; + + #ifndef ZPL_PARSER_DISABLE_ANALYSIS + /* number analysis */ + zpl_i32 base; + zpl_i32 base2; + zpl_u8 base2_offset:4; + zpl_i8 exp :4; + zpl_u8 neg_zero :1; + zpl_u8 lead_digit:1; + #endif + }; + }; + } zpl_adt_node; + + /* ADT NODE LIMITS + * delimiter and assignment segment width is limited to 128 whitespace symbols each. + * real number limits decimal position to 128 places. + * real number exponent is limited to 64 digits. + */ + + /** + * @brief Initialise an ADT object or array + * + * @param node + * @param backing Memory allocator used for descendants + * @param name Node's name + * @param is_array + * @return error code + */ + ZPL_DEF zpl_u8 zpl_adt_make_branch(zpl_adt_node *node, zpl_allocator backing, char const *name, zpl_b32 is_array); + + /** + * @brief Destroy an ADT branch and its descendants + * + * @param node + * @return error code + */ + ZPL_DEF zpl_u8 zpl_adt_destroy_branch(zpl_adt_node *node); + + /** + * @brief Initialise an ADT leaf + * + * @param node + * @param name Node's name + * @param type Node's type (use zpl_adt_make_branch for container nodes) + * @return error code + */ + ZPL_DEF zpl_u8 zpl_adt_make_leaf(zpl_adt_node *node, char const *name, zpl_u8 type); + + + /** + * @brief Fetch a node using provided URI string. + * + * This method uses a basic syntax to fetch a node from the ADT. The following features are available + * to retrieve the data: + * + * - "a/b/c" navigates through objects "a" and "b" to get to "c" + * - "arr/[foo=123]/bar" iterates over "arr" to find any object with param "foo" that matches the value "123", then gets its field called "bar" + * - "arr/3" retrieves the 4th element in "arr" + * - "arr/[apple]" retrieves the first element of value "apple" in "arr" + * + * @param node ADT node + * @param uri Locator string as described above + * @return zpl_adt_node* + * + * @see code/apps/examples/json_get.c + */ + ZPL_DEF zpl_adt_node *zpl_adt_query(zpl_adt_node *node, char const *uri); + + /** + * @brief Find a field node within an object by the given name. + * + * @param node + * @param name + * @param deep_search Perform search recursively + * @return zpl_adt_node * node + */ + ZPL_DEF zpl_adt_node *zpl_adt_find(zpl_adt_node *node, char const *name, zpl_b32 deep_search); + + /** + * @brief Allocate an unitialised node within a container at a specified index. + * + * @param parent + * @param index + * @return zpl_adt_node * node + */ + ZPL_DEF zpl_adt_node *zpl_adt_alloc_at(zpl_adt_node *parent, zpl_isize index); + + /** + * @brief Allocate an unitialised node within a container. + * + * @param parent + * @return zpl_adt_node * node + */ + ZPL_DEF zpl_adt_node *zpl_adt_alloc(zpl_adt_node *parent); + + /** + * @brief Move an existing node to a new container at a specified index. + * + * @param node + * @param new_parent + * @param index + * @return zpl_adt_node * node + */ + ZPL_DEF zpl_adt_node *zpl_adt_move_node_at(zpl_adt_node *node, zpl_adt_node *new_parent, zpl_isize index); + + /** + * @brief Move an existing node to a new container. + * + * @param node + * @param new_parent + * @return zpl_adt_node * node + */ + ZPL_DEF zpl_adt_node *zpl_adt_move_node(zpl_adt_node *node, zpl_adt_node *new_parent); + + /** + * @brief Swap two nodes. + * + * @param node + * @param other_node + * @return + */ + ZPL_DEF void zpl_adt_swap_nodes(zpl_adt_node *node, zpl_adt_node *other_node); + + /** + * @brief Remove node from container. + * + * @param node + * @return + */ + ZPL_DEF void zpl_adt_remove_node(zpl_adt_node *node); + + /** + * @brief Initialise a node as an object + * + * @param obj + * @param name + * @param backing + * @return + */ + ZPL_DEF zpl_b8 zpl_adt_set_obj(zpl_adt_node *obj, char const *name, zpl_allocator backing); + + /** + * @brief Initialise a node as an array + * + * @param obj + * @param name + * @param backing + * @return + */ + ZPL_DEF zpl_b8 zpl_adt_set_arr(zpl_adt_node *obj, char const *name, zpl_allocator backing); + + /** + * @brief Initialise a node as a string + * + * @param obj + * @param name + * @param value + * @return + */ + ZPL_DEF zpl_b8 zpl_adt_set_str(zpl_adt_node *obj, char const *name, char const *value); + + /** + * @brief Initialise a node as a float + * + * @param obj + * @param name + * @param value + * @return + */ + ZPL_DEF zpl_b8 zpl_adt_set_flt(zpl_adt_node *obj, char const *name, zpl_f64 value); + + /** + * @brief Initialise a node as a signed integer + * + * @param obj + * @param name + * @param value + * @return + */ + ZPL_DEF zpl_b8 zpl_adt_set_int(zpl_adt_node *obj, char const *name, zpl_i64 value); + + /** + * @brief Append a new node to a container as an object + * + * @param parent + * @param name + * @return* + */ + ZPL_DEF zpl_adt_node *zpl_adt_append_obj(zpl_adt_node *parent, char const *name); + + /** + * @brief Append a new node to a container as an array + * + * @param parent + * @param name + * @return* + */ + ZPL_DEF zpl_adt_node *zpl_adt_append_arr(zpl_adt_node *parent, char const *name); + + /** + * @brief Append a new node to a container as a string + * + * @param parent + * @param name + * @param value + * @return* + */ + ZPL_DEF zpl_adt_node *zpl_adt_append_str(zpl_adt_node *parent, char const *name, char const *value); + + /** + * @brief Append a new node to a container as a float + * + * @param parent + * @param name + * @param value + * @return* + */ + ZPL_DEF zpl_adt_node *zpl_adt_append_flt(zpl_adt_node *parent, char const *name, zpl_f64 value); + + /** + * @brief Append a new node to a container as a signed integer + * + * @param parent + * @param name + * @param value + * @return* + */ + ZPL_DEF zpl_adt_node *zpl_adt_append_int(zpl_adt_node *parent, char const *name, zpl_i64 value); + + /* parser helpers */ + + /** + * @brief Parses a text and stores the result into an unitialised node. + * + * @param node + * @param base + * @return* + */ + ZPL_DEF char *zpl_adt_parse_number(zpl_adt_node *node, char* base); + + /** + * @brief Parses a text and stores the result into an unitialised node. + * This function expects the entire input to be a number. + * + * @param node + * @param base + * @return* + */ + ZPL_DEF char *zpl_adt_parse_number_strict(zpl_adt_node *node, char* base_str); + + /** + * @brief Parses and converts an existing string node into a number. + * + * @param node + * @return + */ + ZPL_DEF zpl_adt_error zpl_adt_str_to_number(zpl_adt_node *node); + + /** + * @brief Parses and converts an existing string node into a number. + * This function expects the entire input to be a number. + * + * @param node + * @return + */ + ZPL_DEF zpl_adt_error zpl_adt_str_to_number_strict(zpl_adt_node *node); + + /** + * @brief Prints a number into a file stream. + * + * The provided file handle can also be a memory mapped stream. + * + * @see zpl_file_stream_new + * @param file + * @param node + * @return + */ + ZPL_DEF zpl_adt_error zpl_adt_print_number(zpl_file *file, zpl_adt_node *node); + + /** + * @brief Prints a string into a file stream. + * + * The provided file handle can also be a memory mapped stream. + * + * @see zpl_file_stream_new + * @param file + * @param node + * @param escaped_chars + * @param escape_symbol + * @return + */ + ZPL_DEF zpl_adt_error zpl_adt_print_string(zpl_file *file, zpl_adt_node *node, char const *escaped_chars, char const *escape_symbol); + + /* extensions */ + + #if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L + #define zpl_adt_append(parent, name, value) _Generic((value), \ + char*: zpl_adt_append_str, \ + char const*: zpl_adt_append_str, \ + zpl_f64: zpl_adt_append_flt, \ + default: zpl_adt_append_int)(parent, name, value) + #define zpl_adt_set(obj, name, value) _Generic((value), \ + char*: zpl_adt_set_str, \ + char const*: zpl_adt_set_str, \ + zpl_f64: zpl_adt_set_flt, \ + default: zpl_adt_set_int)(obj, name, value) + #endif + + /* deprecated */ + + ZPL_DEPRECATED_FOR(18.0.0, zpl_adt_query) + ZPL_IMPL_INLINE zpl_adt_node *zpl_adt_get(zpl_adt_node *node, char const *uri) { + return zpl_adt_query(node, uri); + } + + ZPL_DEPRECATED_FOR(13.3.0, zpl_adt_str_to_number) + ZPL_IMPL_INLINE void zpl_adt_str_to_flt(zpl_adt_node *node) { + (void)zpl_adt_str_to_number(node); + } + + ZPL_DEPRECATED_FOR(17.0.0, zpl_adt_append_obj) + ZPL_IMPL_INLINE zpl_adt_node *zpl_adt_inset_obj(zpl_adt_node *parent, char const *name) { + return zpl_adt_append_obj(parent, name); + } + + ZPL_DEPRECATED_FOR(17.0.0, zpl_adt_append_arr) + ZPL_IMPL_INLINE zpl_adt_node *zpl_adt_inset_arr(zpl_adt_node *parent, char const *name) { + return zpl_adt_append_arr(parent, name); + } + + ZPL_DEPRECATED_FOR(17.0.0, zpl_adt_append_str) + ZPL_IMPL_INLINE zpl_adt_node *zpl_adt_inset_str(zpl_adt_node *parent, char const *name, char const *value) { + return zpl_adt_append_str(parent, name, value); + } + + ZPL_DEPRECATED_FOR(17.0.0, zpl_adt_append_flt) + ZPL_IMPL_INLINE zpl_adt_node *zpl_adt_inset_flt(zpl_adt_node *parent, char const *name, zpl_f64 value) { + return zpl_adt_append_flt(parent, name, value); + } + + ZPL_DEPRECATED_FOR(17.0.0, zpl_adt_append_int) + ZPL_IMPL_INLINE zpl_adt_node *zpl_adt_inset_int(zpl_adt_node *parent, char const *name, zpl_i64 value) { + return zpl_adt_append_int(parent, name, value); + } + + ZPL_END_C_DECLS + + /* parsers */ + // file: header/parsers/json.h + + + ZPL_BEGIN_C_DECLS + + typedef enum zpl_json_error { + ZPL_JSON_ERROR_NONE, + ZPL_JSON_ERROR_INTERNAL, + ZPL_JSON_ERROR_INVALID_NAME, + ZPL_JSON_ERROR_INVALID_VALUE, + ZPL_JSON_ERROR_INVALID_ASSIGNMENT, + ZPL_JSON_ERROR_UNKNOWN_KEYWORD, + ZPL_JSON_ERROR_ARRAY_LEFT_OPEN, + ZPL_JSON_ERROR_OBJECT_END_PAIR_MISMATCHED, + ZPL_JSON_ERROR_OUT_OF_MEMORY, + } zpl_json_error; + + typedef enum zpl_json_indent_style { + ZPL_JSON_INDENT_STYLE_COMPACT = -1000, + } zpl_json_indent_style; + + typedef zpl_adt_node zpl_json_object; + + ZPL_DEF zpl_u8 zpl_json_parse(zpl_json_object *root, char *text, zpl_allocator allocator); + ZPL_DEF void zpl_json_free(zpl_json_object *obj); + ZPL_DEF zpl_b8 zpl_json_write(zpl_file *file, zpl_json_object *obj, zpl_isize indent); + ZPL_DEF zpl_string zpl_json_write_string(zpl_allocator a, zpl_json_object *obj, zpl_isize indent); + + ZPL_END_C_DECLS + // file: header/parsers/csv.h + + + ZPL_BEGIN_C_DECLS + + typedef enum zpl_csv_error { + ZPL_CSV_ERROR_NONE, + ZPL_CSV_ERROR_INTERNAL, + ZPL_CSV_ERROR_UNEXPECTED_END_OF_INPUT, + ZPL_CSV_ERROR_MISMATCHED_ROWS, + } zpl_csv_error; + + typedef zpl_adt_node zpl_csv_object; + + ZPL_DEF_INLINE zpl_u8 zpl_csv_parse(zpl_csv_object *root, char *text, zpl_allocator allocator, zpl_b32 has_header); + ZPL_DEF zpl_u8 zpl_csv_parse_delimiter(zpl_csv_object *root, char *text, zpl_allocator allocator, zpl_b32 has_header, char delim); + ZPL_DEF void zpl_csv_free(zpl_csv_object *obj); + + ZPL_DEF_INLINE void zpl_csv_write(zpl_file *file, zpl_csv_object *obj); + ZPL_DEF_INLINE zpl_string zpl_csv_write_string(zpl_allocator a, zpl_csv_object *obj); + ZPL_DEF void zpl_csv_write_delimiter(zpl_file *file, zpl_csv_object *obj, char delim); + ZPL_DEF zpl_string zpl_csv_write_string_delimiter(zpl_allocator a, zpl_csv_object *obj, char delim); + + /* inline */ + + ZPL_IMPL_INLINE zpl_u8 zpl_csv_parse(zpl_csv_object *root, char *text, zpl_allocator allocator, zpl_b32 has_header) { + return zpl_csv_parse_delimiter(root, text, allocator, has_header, ','); + } + + ZPL_IMPL_INLINE void zpl_csv_write(zpl_file *file, zpl_csv_object *obj) { + zpl_csv_write_delimiter(file, obj, ','); + } + + ZPL_IMPL_INLINE zpl_string zpl_csv_write_string(zpl_allocator a, zpl_csv_object *obj) { + return zpl_csv_write_string_delimiter(a, obj, ','); + } + + + ZPL_END_C_DECLS + // file: header/parsers/uri.h + + + ZPL_BEGIN_C_DECLS + + typedef zpl_adt_node zpl_uri_object; + + typedef enum zpl_uri_error { + ZPL_URI_ERROR_NONE, + ZPL_URI_ERROR_INTERNAL, + } zpl_uri_error; + + ZPL_DEF zpl_u8 zpl_uri_init(zpl_adt_node *root, char *origin, zpl_allocator a); + ZPL_DEF zpl_u8 zpl_uri_parse(zpl_adt_node *root, char *text, zpl_allocator a); + ZPL_DEF void zpl_uri_write(zpl_file *f, zpl_adt_node *obj); + ZPL_DEF zpl_string zpl_uri_write_string(zpl_allocator a, zpl_adt_node *obj); + ZPL_DEF void zpl_uri_free(zpl_adt_node *obj); + + ZPL_END_C_DECLS +#endif + +#if defined(ZPL_MODULE_SOCKET) + // file: header/socket.h + + ZPL_BEGIN_C_DECLS + + typedef int zpl_socket; + + typedef struct zpl_socket_addr { + char data[128]; + } zpl_socket_addr; + + typedef enum zpl_socket_protocol { + ZPL_SOCKET_TCP = 0, + ZPL_SOCKET_UDP = 1, + } zpl_socket_protocol; + + typedef enum zpl_socket_mode { + ZPL_SOCKET_BIND = 0, + ZPL_SOCKET_CONNECT = 1, + } zpl_socket_mode; + + typedef enum zpl_socket_flags { + ZPL_SOCKET_DEFAULT = 0, + ZPL_SOCKET_NON_BLOCKING = 0x01, + ZPL_SOCKET_NO_DELAY = 0x02, + } zpl_socket_flags; + + /** + * Initializes socket functionality + * @return 0 on success + */ + ZPL_DEF zpl_i32 zpl_socket_init(void); + + /** + * Creates a new socket configured according to the given parameters + * @param protocol Protocol of the socket, either ZPL_SOCKET_TCP or ZPL_SOCKET_UDP for TCP or UDP respectively + * @param mode Mode of the socket (bind or connect), either ZPL_SOCKET_BIND or ZPL_SOCKET_CONNECT + * @param flags Configuration flags, either ZPL_SOCKET_DEFAULT or a bitwise combination of flags + * @param host Host/address as a string, can be IPv4, IPv6, etc... + * @param service Service/port as a string, e.g. "1728" or "http" + * @return socket handle, or -1 on failure + */ + ZPL_DEF zpl_socket zpl_socket_create(zpl_i32 protocol, zpl_i32 mode, char flags, const char *host, const char *service); + + /** + * Closes the given socket + * @param socket Socket handle + */ + ZPL_DEF void zpl_socket_close(zpl_socket socket); + + /** + * Terminates socket functionality + */ + ZPL_DEF void zpl_socket_terminate(void); + + /** + * Configures the given socket (must be ZPL_SOCKET_TCP + ZPL_SOCKET_BIND) to listen for new connections with the given backlog + * @param socket Socket handle + * @param backlog Size of the backlog + * @return 0 on success, non-zero on failure + */ + ZPL_DEF zpl_i32 zpl_socket_listen(zpl_socket socket, zpl_i32 backlog); + + /** + * Uses the given socket (must be zpl_socket_listen) to accept a new incoming connection, optionally returning its address + * @param socket Socket handle + * @param addr Pointer to zpl_socket_addr to store the address + * @return a socket handle for the new connection, or -1 on failure (e.g. if there are no new connections) + */ + ZPL_DEF zpl_socket zpl_socket_accept(zpl_socket socket, zpl_socket_addr *addr); + + /** + * Writes the address the given socket is bound to into the given address pointer, useful when automatically assigning a port + * @param socket Socket handle + * @param addr Pointer to zpl_socket_addr to store the address + * @return 0 on success, non-zero on failure + */ + ZPL_DEF zpl_i32 zpl_socket_get_address(zpl_socket socket, zpl_socket_addr *addr); + + /** + * Writes the host/address and service/port of the given address into given buffers (pointer + size), one buffer may be NULL + * @param addr Pointer to zpl_socket_addr containing the address + * @param host Buffer to store the host/address string + * @param host_size Size of the host buffer + * @param service Buffer to store the service/port string + * @param service_size Size of the service buffer + * @return 0 on success, non-zero on failure + */ + ZPL_DEF zpl_i32 zpl_socket_get_address_info(zpl_socket_addr *addr, char *host, zpl_i32 host_size, char *service, zpl_i32 service_size); + + /** + * Uses the given socket (either ZPL_SOCKET_CONNECT or returned by zpl_socket_accept) to send the given data + * @param socket Socket handle + * @param data Pointer to the data to be sent + * @param size Size of the data + * @return how much data was actually sent (may be less than data size), or -1 on failure + */ + ZPL_DEF zpl_i32 zpl_socket_send(zpl_socket socket, const char *data, zpl_i32 size); + + /** + * Receives data using the given socket (either ZPL_SOCKET_CONNECT or returned by zpl_socket_accept) into the given buffer + * @param socket Socket handle + * @param buffer Pointer to the buffer to receive the data + * @param size Size of the buffer + * @return the number of bytes received, 0 on orderly shutdown, or -1 on failure (e.g. no data to receive) + */ + ZPL_DEF zpl_i32 zpl_socket_receive(zpl_socket socket, char *buffer, zpl_i32 size); + + /** + * Uses the given socket to send the given data to the given zpl_socket_addr + * @param socket Socket handle + * @param addr Pointer to zpl_socket_addr containing the address to send the data to + * @param data Pointer to the data to be sent + * @param size Size of the data + * @return how much data was actually sent (may be less than data size), or -1 on failure + */ + ZPL_DEF zpl_i32 zpl_socket_send_to(zpl_socket socket, zpl_socket_addr *addr, const char *data, zpl_i32 size); + + /** + * Receives data using the given socket into the given buffer, optionally returning the sender's address + * @param socket Socket handle + * @param addr Pointer to zpl_socket_addr to store the sender's address + * @param buffer Pointer to the buffer to receive the data + * @param size Size of the buffer + * @return the number of bytes received, 0 on orderly shutdown, or -1 on failure (e.g. no data to receive) + */ + ZPL_DEF zpl_i32 zpl_socket_receive_from(zpl_socket socket, zpl_socket_addr *addr, char *buffer, zpl_i32 size); + + /** + * Waits either until the given socket has new data to receive or the given time (in seconds) has passed + * @param socket Socket handle + * @param time Time to wait in seconds + * @return 1 if new data is available, 0 if timeout was reached, and -1 on error + */ + ZPL_DEF zpl_i32 zpl_socket_select(zpl_socket socket, zpl_f64 time); + + /** + * Waits either until a socket in the given list has new data to receive or the given time (in seconds) has passed + * @param sockets Array of socket handles + * @param count Number of sockets in the array + * @param time Time to wait in seconds + * @return 1 or more if new data is available, 0 if timeout was reached, and -1 on error + */ + ZPL_DEF zpl_i32 zpl_socket_multi_select(zpl_socket *sockets, zpl_i32 count, zpl_f64 time); + + ZPL_END_C_DECLS +#endif + +#if defined(ZPL_MODULE_THREADING) +# if defined(ZPL_SYSTEM_UNIX) || defined(ZPL_SYSTEM_MACOS) +# include +# endif + +# if !defined(zpl_thread_local) +# if defined(_MSC_VER) && _MSC_VER >= 1300 +# define zpl_thread_local __declspec(thread) +# elif defined(__GNUC__) +# define zpl_thread_local __thread +# elif defined(__TINYC__) +# define zpl_thread_local +# else +# define zpl_thread_local thread_local +# endif +# endif + + // file: header/threading/atomic.h + + // Atomics + + // TODO: Be specific with memory order? + // e.g. relaxed, acquire, release, acquire_release + + #if !defined(__STDC_NO_ATOMICS__) && !defined(__cplusplus) && !defined(ZPL_COMPILER_MSVC) && !defined(ZPL_COMPILER_TINYC) + # define zpl_atomic(X) volatile _Atomic(X) + #else + // TODO: Fix once C++ guys bother to add C atomics to std. + //# include + # define zpl_atomic(X) volatile X /*std::atomic*/ + #endif + + #if defined(__STDC_NO_ATOMICS__) || defined(__cplusplus) || defined(ZPL_COMPILER_MSVC) + #define zpl_atomicarg(X) volatile X + #else + #define zpl_atomicarg(X) X + #endif + + ZPL_BEGIN_C_DECLS + + #if defined(ZPL_COMPILER_MSVC) + typedef struct zpl_atomic32 { zpl_atomic(zpl_i32) value; } zpl_atomic32; + typedef struct zpl_atomic64 { zpl_atomic(zpl_i64) value; } zpl_atomic64; + typedef struct zpl_atomic_ptr { zpl_atomic(void*) value; } zpl_atomic_ptr; + #else + # if defined(ZPL_ARCH_32_BIT) + # define ZPL_ATOMIC_PTR_ALIGNMENT 4 + # elif defined(ZPL_ARCH_64_BIT) + # define ZPL_ATOMIC_PTR_ALIGNMENT 8 + # else + # error Unknown architecture + # endif + + typedef struct zpl_atomic32 { zpl_atomic(zpl_i32) value; } __attribute__ ((aligned(4))) zpl_atomic32; + typedef struct zpl_atomic64 { zpl_atomic(zpl_i64) value; } __attribute__ ((aligned(8))) zpl_atomic64; + typedef struct zpl_atomic_ptr { zpl_atomic(void*) value; } __attribute__ ((aligned(ZPL_ATOMIC_PTR_ALIGNMENT))) zpl_atomic_ptr; + #endif + + ZPL_DEF zpl_i32 zpl_atomic32_load (zpl_atomic32 const *a); + ZPL_DEF void zpl_atomic32_store (zpl_atomic32 *a, zpl_atomicarg(zpl_i32) value); + ZPL_DEF zpl_i32 zpl_atomic32_compare_exchange(zpl_atomic32 *a, zpl_atomicarg(zpl_i32) expected, zpl_atomicarg(zpl_i32) desired); + ZPL_DEF zpl_i32 zpl_atomic32_exchange (zpl_atomic32 *a, zpl_atomicarg(zpl_i32) desired); + ZPL_DEF zpl_i32 zpl_atomic32_fetch_add (zpl_atomic32 *a, zpl_atomicarg(zpl_i32) operand); + ZPL_DEF zpl_i32 zpl_atomic32_fetch_and (zpl_atomic32 *a, zpl_atomicarg(zpl_i32) operand); + ZPL_DEF zpl_i32 zpl_atomic32_fetch_or (zpl_atomic32 *a, zpl_atomicarg(zpl_i32) operand); + ZPL_DEF zpl_b32 zpl_atomic32_spin_lock (zpl_atomic32 *a, zpl_isize time_out); // NOTE: time_out = -1 as default + ZPL_DEF void zpl_atomic32_spin_unlock (zpl_atomic32 *a); + ZPL_DEF zpl_b32 zpl_atomic32_try_acquire_lock(zpl_atomic32 *a); + + + ZPL_DEF zpl_i64 zpl_atomic64_load (zpl_atomic64 const *a); + ZPL_DEF void zpl_atomic64_store (zpl_atomic64 *a, zpl_atomicarg(zpl_i64) value); + ZPL_DEF zpl_i64 zpl_atomic64_compare_exchange(zpl_atomic64 *a, zpl_atomicarg(zpl_i64) expected, zpl_atomicarg(zpl_i64) desired); + ZPL_DEF zpl_i64 zpl_atomic64_exchange (zpl_atomic64 *a, zpl_atomicarg(zpl_i64) desired); + ZPL_DEF zpl_i64 zpl_atomic64_fetch_add (zpl_atomic64 *a, zpl_atomicarg(zpl_i64) operand); + ZPL_DEF zpl_i64 zpl_atomic64_fetch_and (zpl_atomic64 *a, zpl_atomicarg(zpl_i64) operand); + ZPL_DEF zpl_i64 zpl_atomic64_fetch_or (zpl_atomic64 *a, zpl_atomicarg(zpl_i64) operand); + ZPL_DEF zpl_b32 zpl_atomic64_spin_lock (zpl_atomic64 *a, zpl_isize time_out); // NOTE: time_out = -1 as default + ZPL_DEF void zpl_atomic64_spin_unlock (zpl_atomic64 *a); + ZPL_DEF zpl_b32 zpl_atomic64_try_acquire_lock(zpl_atomic64 *a); + + + ZPL_DEF void *zpl_atomic_ptr_load (zpl_atomic_ptr const *a); + ZPL_DEF void zpl_atomic_ptr_store (zpl_atomic_ptr *a, zpl_atomicarg(void *)value); + ZPL_DEF void *zpl_atomic_ptr_compare_exchange(zpl_atomic_ptr *a, zpl_atomicarg(void *)expected, zpl_atomicarg(void *)desired); + ZPL_DEF void *zpl_atomic_ptr_exchange (zpl_atomic_ptr *a, zpl_atomicarg(void *)desired); + ZPL_DEF void *zpl_atomic_ptr_fetch_add (zpl_atomic_ptr *a, zpl_atomicarg(void *)operand); + ZPL_DEF void *zpl_atomic_ptr_fetch_and (zpl_atomic_ptr *a, zpl_atomicarg(void *)operand); + ZPL_DEF void *zpl_atomic_ptr_fetch_or (zpl_atomic_ptr *a, zpl_atomicarg(void *)operand); + ZPL_DEF zpl_b32 zpl_atomic_ptr_spin_lock (zpl_atomic_ptr *a, zpl_isize time_out); // NOTE: time_out = -1 as default + ZPL_DEF void zpl_atomic_ptr_spin_unlock (zpl_atomic_ptr *a); + ZPL_DEF zpl_b32 zpl_atomic_ptr_try_acquire_lock(zpl_atomic_ptr *a); + + ZPL_END_C_DECLS + // file: header/threading/fence.h + + // Fences + + ZPL_BEGIN_C_DECLS + + ZPL_DEF void zpl_yield_thread(void); + ZPL_DEF void zpl_mfence (void); + ZPL_DEF void zpl_sfence (void); + ZPL_DEF void zpl_lfence (void); + + ZPL_END_C_DECLS + // file: header/threading/sem.h + + + ZPL_BEGIN_C_DECLS + + #if defined(ZPL_SYSTEM_MACOS) + # include + #elif defined(ZPL_SYSTEM_UNIX) + # include + #endif + + #if defined(ZPL_SYSTEM_WINDOWS) + typedef struct zpl_semaphore { void *win32_handle; } zpl_semaphore; + #elif defined(ZPL_SYSTEM_MACOS) + typedef struct zpl_semaphore { semaphore_t osx_handle; } zpl_semaphore; + #elif defined(ZPL_SYSTEM_UNIX) + typedef struct zpl_semaphore { sem_t unix_handle; } zpl_semaphore; + #else + # error + #endif + + ZPL_DEF void zpl_semaphore_init (zpl_semaphore *s); + ZPL_DEF void zpl_semaphore_destroy(zpl_semaphore *s); + ZPL_DEF void zpl_semaphore_post (zpl_semaphore *s, zpl_i32 count); + ZPL_DEF void zpl_semaphore_release(zpl_semaphore *s); // NOTE: zpl_semaphore_post(s, 1) + ZPL_DEF void zpl_semaphore_wait (zpl_semaphore *s); + ZPL_DEF zpl_i32 zpl_semaphore_trywait(zpl_semaphore *s); + + ZPL_END_C_DECLS + // file: header/threading/mutex.h + + + ZPL_BEGIN_C_DECLS + + typedef struct zpl_mutex { + #if defined(ZPL_SYSTEM_WINDOWS) + zpl_u64 win32_critical_section[sizeof(zpl_usize) / 2 + 1]; + #else + pthread_mutex_t pthread_mutex; + #endif + } zpl_mutex; + + ZPL_DEF zpl_b32 zpl_mutex_init (zpl_mutex *m); + ZPL_DEF zpl_b32 zpl_mutex_destroy (zpl_mutex *m); + ZPL_DEF void zpl_mutex_lock (zpl_mutex *m); + ZPL_DEF zpl_b32 zpl_mutex_try_lock(zpl_mutex *m); + ZPL_DEF void zpl_mutex_unlock (zpl_mutex *m); + + ZPL_END_C_DECLS + // file: header/threading/thread.h + + #ifdef ZPL_EDITOR + #include + #else + struct zpl_thread; + #endif + + ZPL_BEGIN_C_DECLS + + typedef zpl_isize (*zpl_thread_proc)(struct zpl_thread *thread); + + typedef struct zpl_thread { + #if defined(ZPL_SYSTEM_WINDOWS) + void * win32_handle; + #else + pthread_t posix_handle; + #endif + + zpl_thread_proc proc; + void * user_data; + zpl_isize user_index; + zpl_isize return_value; + + zpl_semaphore semaphore; + zpl_isize stack_size; + zpl_b32 is_running; + zpl_b32 nowait; + } zpl_thread; + + ZPL_DEF void zpl_thread_init (zpl_thread *t); + ZPL_DEF void zpl_thread_init_nowait (zpl_thread *t); + ZPL_DEF void zpl_thread_destroy (zpl_thread *t); + ZPL_DEF zpl_b32 zpl_thread_start (zpl_thread *t, zpl_thread_proc proc, void *data); + ZPL_DEF zpl_b32 zpl_thread_start_with_stack(zpl_thread *t, zpl_thread_proc proc, void *data, zpl_isize stack_size); + ZPL_DEF void zpl_thread_join (zpl_thread *t); + ZPL_DEF zpl_b32 zpl_thread_is_running (zpl_thread const *t); + ZPL_DEF zpl_u32 zpl_thread_current_id (void); + ZPL_DEF void zpl_thread_set_name (zpl_thread *t, char const *name); + + ZPL_END_C_DECLS + // file: header/threading/sync.h + + // NOTE: Thread Merge Operation + // Based on Sean Barrett's stb_sync + + ZPL_BEGIN_C_DECLS + + typedef struct zpl_sync { + zpl_i32 target; // Target Number of threads + zpl_i32 current; // Threads to hit + zpl_i32 waiting; // Threads waiting + + zpl_mutex start; + zpl_mutex mutex; + zpl_semaphore release; + } zpl_sync; + + ZPL_DEF void zpl_sync_init (zpl_sync *s); + ZPL_DEF void zpl_sync_destroy (zpl_sync *s); + ZPL_DEF void zpl_sync_set_target (zpl_sync *s, zpl_i32 count); + ZPL_DEF void zpl_sync_release (zpl_sync *s); + ZPL_DEF zpl_i32 zpl_sync_reach (zpl_sync *s); + ZPL_DEF void zpl_sync_reach_and_wait(zpl_sync *s); + + ZPL_END_C_DECLS + // file: header/threading/affinity.h + + + ZPL_BEGIN_C_DECLS + + #if defined(ZPL_SYSTEM_WINDOWS) || defined (ZPL_SYSTEM_CYGWIN) + + typedef struct zpl_affinity { + zpl_b32 is_accurate; + zpl_isize core_count; + zpl_isize thread_count; + + # define ZPL_WIN32_MAX_THREADS (8 * zpl_size_of(zpl_usize)) + zpl_usize core_masks[ZPL_WIN32_MAX_THREADS]; + } zpl_affinity; + + #elif defined(ZPL_SYSTEM_OSX) + + typedef struct zpl_affinity { + zpl_b32 is_accurate; + zpl_isize core_count; + zpl_isize thread_count; + zpl_isize threads_per_core; + } zpl_affinity; + + #elif defined(ZPL_SYSTEM_LINUX) || defined(ZPL_SYSTEM_FREEBSD) || defined(ZPL_SYSTEM_EMSCRIPTEN) || defined(ZPL_SYSTEM_OPENBSD) + + typedef struct zpl_affinity { + zpl_b32 is_accurate; + zpl_isize core_count; + zpl_isize thread_count; + zpl_isize threads_per_core; + } zpl_affinity; + + #else + # error TODO: Unknown system + #endif + + ZPL_DEF void zpl_affinity_init (zpl_affinity *a); + ZPL_DEF void zpl_affinity_destroy(zpl_affinity *a); + ZPL_DEF zpl_b32 zpl_affinity_set (zpl_affinity *a, zpl_isize core, zpl_isize thread); + ZPL_DEF zpl_isize zpl_affinity_thread_count_for_core(zpl_affinity *a, zpl_isize core); + + ZPL_END_C_DECLS + +# if defined(ZPL_MODULE_JOBS) + // file: header/jobs.h + + /** @file threadpool.c + @brief Job system + @defgroup jobs Job system + + This job system follows thread pool pattern to minimize the costs of thread initialization. + It reuses fixed number of threads to process variable number of jobs. + + @{ + */ + + ZPL_BEGIN_C_DECLS + + typedef void (*zpl_jobs_proc)(void *data); + + #define ZPL_INVALID_JOB ZPL_U32_MAX + + #ifndef ZPL_JOBS_MAX_QUEUE + #define ZPL_JOBS_MAX_QUEUE 100 + #endif + + #ifdef ZPL_JOBS_ENABLE_DEBUG + #define ZPL_JOBS_DEBUG + #endif + + typedef enum { + ZPL_JOBS_STATUS_READY, + ZPL_JOBS_STATUS_BUSY, + ZPL_JOBS_STATUS_WAITING, + ZPL_JOBS_STATUS_TERM, + } zpl_jobs_status; + + typedef enum { + ZPL_JOBS_PRIORITY_REALTIME, + ZPL_JOBS_PRIORITY_HIGH, + ZPL_JOBS_PRIORITY_NORMAL, + ZPL_JOBS_PRIORITY_LOW, + ZPL_JOBS_PRIORITY_IDLE, + ZPL_JOBS_MAX_PRIORITIES, + } zpl_jobs_priority; + + typedef struct { + zpl_jobs_proc proc; + void *data; + } zpl_thread_job; + + ZPL_RING_DECLARE(extern, zpl__jobs_ring_, zpl_thread_job); + + typedef struct { + zpl_thread thread; + zpl_atomic32 status; + zpl_thread_job job; + #ifdef ZPL_JOBS_DEBUG + zpl_u32 hits; + zpl_u32 idle; + #endif + } zpl_thread_worker; + + typedef struct { + zpl__jobs_ring_zpl_thread_job jobs; ///< zpl_ring + zpl_u32 chance; + #ifdef ZPL_JOBS_DEBUG + zpl_u32 hits; + #endif + } zpl_thread_queue; + + typedef struct { + zpl_allocator alloc; + zpl_u32 max_threads, max_jobs, counter; + zpl_thread_worker *workers; ///< zpl_buffer + zpl_thread_queue queues[ZPL_JOBS_MAX_PRIORITIES]; + } zpl_jobs_system; + + //! Initialize thread pool with specified amount of fixed threads. + ZPL_DEF void zpl_jobs_init(zpl_jobs_system *pool, zpl_allocator a, zpl_u32 max_threads); + + //! Initialize thread pool with specified amount of fixed threads and custom job limit. + ZPL_DEF void zpl_jobs_init_with_limit(zpl_jobs_system *pool, zpl_allocator a, zpl_u32 max_threads, zpl_u32 max_jobs); + + //! Release the resources use by thread pool. + ZPL_DEF void zpl_jobs_free(zpl_jobs_system *pool); + + //! Enqueue a job with specified data and custom priority. + ZPL_DEF zpl_b32 zpl_jobs_enqueue_with_priority(zpl_jobs_system *pool, zpl_jobs_proc proc, void *data, zpl_jobs_priority priority); + + //! Enqueue a job with specified data. + ZPL_DEF zpl_b32 zpl_jobs_enqueue(zpl_jobs_system *pool, zpl_jobs_proc proc, void *data); + + //! Check if the work queue is empty. + ZPL_DEF zpl_b32 zpl_jobs_empty(zpl_jobs_system *pool, zpl_jobs_priority priority); + + ZPL_DEF zpl_b32 zpl_jobs_empty_all(zpl_jobs_system *pool); + ZPL_DEF zpl_b32 zpl_jobs_full_all(zpl_jobs_system *pool); + + //! Check if the work queue is full. + ZPL_DEF zpl_b32 zpl_jobs_full(zpl_jobs_system *pool, zpl_jobs_priority priority); + + //! Check if all workers are done. + ZPL_DEF zpl_b32 zpl_jobs_done(zpl_jobs_system *pool); + + //! Process all jobs and check all threads. Should be called by Main Thread in a tight loop. + ZPL_DEF zpl_b32 zpl_jobs_process(zpl_jobs_system *pool); + + ZPL_END_C_DECLS +# endif +#else +# if !defined(zpl_thread_local) +# define zpl_thread_local +# endif +#endif + +#if defined(ZPL_COMPILER_MSVC) +# pragma warning(pop) +#endif + +#if defined(__GCC__) || defined(__GNUC__) || defined(__clang__) +# pragma GCC diagnostic pop +#endif + +#if defined(ZPL_IMPLEMENTATION) && !defined(ZPL_IMPLEMENTATION_DONE) +#define ZPL_IMPLEMENTATION_DONE + +#if defined(__GCC__) || defined(__GNUC__) || defined(__clang__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wattributes" +# pragma GCC diagnostic ignored "-Wunused-value" +# pragma GCC diagnostic ignored "-Wunused-function" +# pragma GCC diagnostic ignored "-Wwrite-strings" +# pragma GCC diagnostic ignored "-Wunused-parameter" +# pragma GCC diagnostic ignored "-Wdeprecated-declarations" +# pragma GCC diagnostic ignored "-Wmissing-braces" +# pragma GCC diagnostic ignored "-Wmissing-field-initializers" +# pragma GCC diagnostic ignored "-Wimplicit-fallthrough" +# pragma GCC diagnostic ignored "-Wignored-qualifiers" +#endif + +#if defined(_MSC_VER) +# pragma warning(push) +# pragma warning(disable : 4201) +# pragma warning(disable : 4996) // Disable deprecated POSIX functions warning +# pragma warning(disable : 4127) // Conditional expression is constant +# pragma warning(disable : 4204) // non-constant field initializer +# pragma warning(disable : 4756) // -INFINITY +# pragma warning(disable : 4056) // -INFINITY +# pragma warning(disable : 4702) // unreachable code +#endif + +/* general purpose includes */ + +#include + +// NOTE: Ensure we use standard methods for these calls if we use ZPL_PICO +#if !defined(ZPL_PICO_CUSTOM_ROUTINES) +# if !defined(ZPL_MODULE_CORE) +# define zpl__strlen strlen +# define zpl__printf_err(fmt, ...) fprintf(stderr, fmt, __VA_ARGS__) +# define zpl__printf_err_va(fmt, va) vfprintf(stderr, fmt, va) +# else +# define zpl__strlen zpl_strlen +# define zpl__printf_err(fmt, ...) zpl_printf_err(fmt, __VA_ARGS__) +# define zpl__printf_err_va(fmt, va) zpl_printf_err_va(fmt, va) +# endif +#endif + +#include + +#if defined(ZPL_SYSTEM_UNIX) || defined(ZPL_SYSTEM_MACOS) +# include +#elif defined(ZPL_SYSTEM_WINDOWS) +# if !defined(ZPL_NO_WINDOWS_H) +# ifndef WIN32_LEAN_AND_MEAN +# ifndef NOMINMAX +# define NOMINMAX +# endif + +# define WIN32_LEAN_AND_MEAN +# define WIN32_MEAN_AND_LEAN +# define VC_EXTRALEAN +# endif +# include +# undef NOMINMAX +# undef WIN32_LEAN_AND_MEAN +# undef WIN32_MEAN_AND_LEAN +# undef VC_EXTRALEAN +# endif +#endif + +#if defined(ZPL_MODULE_ESSENTIALS) + // file: source/essentials/debug.c + + + ZPL_BEGIN_C_DECLS + + void zpl_assert_handler(char const *condition, char const *file, zpl_i32 line, char const *msg, ...) { + zpl__printf_err("%s:(%d): Assert Failure: ", file, line); + + if (condition) zpl__printf_err("`%s` ", condition); + + if (msg) { + va_list va; + va_start(va, msg); + zpl__printf_err_va(msg, va); + va_end(va); + } + + zpl__printf_err("%s", "\n"); + } + + zpl_i32 zpl_assert_crash(char const *condition) { + ZPL_PANIC(condition); + return 0; + } + + #if defined(ZPL_SYSTEM_UNIX) || defined(ZPL_SYSTEM_MACOS) + # include + #endif + + #if defined(ZPL_SYSTEM_WINDOWS) + void zpl_exit(zpl_u32 code) { ExitProcess(code); } + #else + # include + void zpl_exit(zpl_u32 code) { exit(code); } + #endif + + ZPL_END_C_DECLS + // file: source/essentials/memory.c + + + #include + + ZPL_BEGIN_C_DECLS + + + void zpl_memswap(void *i, void *j, zpl_isize size) { + if (i == j) return; + + if (size == 4) { + zpl_swap(zpl_u32, *cast(zpl_u32 *) i, *cast(zpl_u32 *) j); + } else if (size == 8) { + zpl_swap(zpl_u64, *cast(zpl_u64 *) i, *cast(zpl_u64 *) j); + } else if (size < 8) { + zpl_u8 *a = cast(zpl_u8 *) i; + zpl_u8 *b = cast(zpl_u8 *) j; + if (a != b) { + while (size--) { zpl_swap(zpl_u8, *a++, *b++); } + } + } else { + char buffer[256]; + + while (size > zpl_size_of(buffer)) { + zpl_memswap(i, j, zpl_size_of(buffer)); + i = zpl_pointer_add(i, zpl_size_of(buffer)); + j = zpl_pointer_add(j, zpl_size_of(buffer)); + size -= zpl_size_of(buffer); + } + + zpl_memcopy(buffer, i, size); + zpl_memcopy(i, j, size); + zpl_memcopy(j, buffer, size); + } + } + + void const *zpl_memchr(void const *data, zpl_u8 c, zpl_isize n) { + zpl_u8 const *s = cast(zpl_u8 const *) data; + while ((cast(zpl_uintptr) s & (sizeof(zpl_usize) - 1)) && n && *s != c) { + s++; + n--; + } + if (n && *s != c) { + zpl_isize const *w; + zpl_isize k = ZPL__ONES * c; + w = cast(zpl_isize const *) s; + while (n >= zpl_size_of(zpl_isize) && !ZPL__HAS_ZERO(*w ^ k)) { + w++; + n -= zpl_size_of(zpl_isize); + } + s = cast(zpl_u8 const *) w; + while (n && *s != c) { + s++; + n--; + } + } + + return n ? cast(void const *) s : NULL; + } + + void const *zpl_memrchr(void const *data, zpl_u8 c, zpl_isize n) { + zpl_u8 const *s = cast(zpl_u8 const *) data; + while (n--) { + if (s[n] == c) return cast(void const *)(s + n); + } + return NULL; + } + + void *zpl_memcopy(void *dest, void const *source, zpl_isize n) { + if (dest == NULL) { return NULL; } + + return memcpy(dest, source, n); + + // TODO: Re-work the whole method + #if 0 + #if defined(_MSC_VER) + __movsb(cast(zpl_u8 *) dest, cast(zpl_u8 *) source, n); + #elif defined(ZPL_CPU_X86) && !defined(ZPL_SYSTEM_EMSCRIPTEN) + zpl_u8 *__dest8 = cast(zpl_u8 *) dest; + zpl_u8 *__source8 = cast(zpl_u8 *) source; + __asm__ __volatile__("rep movsb" : "+D"(__dest8), "+S"(__source8), "+c"(n) : : "memory"); + #elif defined(ZPL_CPU_ARM) + return memcpy(dest, source, n); + #else + zpl_u8 *d = cast(zpl_u8 *) dest; + zpl_u8 const *s = cast(zpl_u8 const *) source; + zpl_u32 w, x; + + for (; cast(zpl_uintptr) s % 4 && n; n--) *d++ = *s++; + + if (cast(zpl_uintptr) d % 4 == 0) { + for (; n >= 16; s += 16, d += 16, n -= 16) { + *cast(zpl_u32 *)(d + 0) = *cast(zpl_u32 *)(s + 0); + *cast(zpl_u32 *)(d + 4) = *cast(zpl_u32 *)(s + 4); + *cast(zpl_u32 *)(d + 8) = *cast(zpl_u32 *)(s + 8); + *cast(zpl_u32 *)(d + 12) = *cast(zpl_u32 *)(s + 12); + } + if (n & 8) { + *cast(zpl_u32 *)(d + 0) = *cast(zpl_u32 *)(s + 0); + *cast(zpl_u32 *)(d + 4) = *cast(zpl_u32 *)(s + 4); + d += 8; + s += 8; + } + if (n & 4) { + *cast(zpl_u32 *)(d + 0) = *cast(zpl_u32 *)(s + 0); + d += 4; + s += 4; + } + if (n & 2) { + *d++ = *s++; + *d++ = *s++; + } + if (n & 1) { *d = *s; } + return dest; + } + + if (n >= 32) { + #if __BYTE_ORDER == __BIG_ENDIAN + #define LS << + #define RS >> + #else + #define LS >> + #define RS << + #endif + switch (cast(zpl_uintptr) d % 4) { + case 1: { + w = *cast(zpl_u32 *) s; + *d++ = *s++; + *d++ = *s++; + *d++ = *s++; + n -= 3; + while (n > 16) { + x = *cast(zpl_u32 *)(s + 1); + *cast(zpl_u32 *)(d + 0) = (w LS 24) | (x RS 8); + w = *cast(zpl_u32 *)(s + 5); + *cast(zpl_u32 *)(d + 4) = (x LS 24) | (w RS 8); + x = *cast(zpl_u32 *)(s + 9); + *cast(zpl_u32 *)(d + 8) = (w LS 24) | (x RS 8); + w = *cast(zpl_u32 *)(s + 13); + *cast(zpl_u32 *)(d + 12) = (x LS 24) | (w RS 8); + + s += 16; + d += 16; + n -= 16; + } + } break; + case 2: { + w = *cast(zpl_u32 *) s; + *d++ = *s++; + *d++ = *s++; + n -= 2; + while (n > 17) { + x = *cast(zpl_u32 *)(s + 2); + *cast(zpl_u32 *)(d + 0) = (w LS 16) | (x RS 16); + w = *cast(zpl_u32 *)(s + 6); + *cast(zpl_u32 *)(d + 4) = (x LS 16) | (w RS 16); + x = *cast(zpl_u32 *)(s + 10); + *cast(zpl_u32 *)(d + 8) = (w LS 16) | (x RS 16); + w = *cast(zpl_u32 *)(s + 14); + *cast(zpl_u32 *)(d + 12) = (x LS 16) | (w RS 16); + + s += 16; + d += 16; + n -= 16; + } + } break; + case 3: { + w = *cast(zpl_u32 *) s; + *d++ = *s++; + n -= 1; + while (n > 18) { + x = *cast(zpl_u32 *)(s + 3); + *cast(zpl_u32 *)(d + 0) = (w LS 8) | (x RS 24); + w = *cast(zpl_u32 *)(s + 7); + *cast(zpl_u32 *)(d + 4) = (x LS 8) | (w RS 24); + x = *cast(zpl_u32 *)(s + 11); + *cast(zpl_u32 *)(d + 8) = (w LS 8) | (x RS 24); + w = *cast(zpl_u32 *)(s + 15); + *cast(zpl_u32 *)(d + 12) = (x LS 8) | (w RS 24); + + s += 16; + d += 16; + n -= 16; + } + } break; + default: break; // NOTE: Do nowt! + } + #undef LS + #undef RS + if (n & 16) { + *d++ = *s++; + *d++ = *s++; + *d++ = *s++; + *d++ = *s++; + *d++ = *s++; + *d++ = *s++; + *d++ = *s++; + *d++ = *s++; + *d++ = *s++; + *d++ = *s++; + *d++ = *s++; + *d++ = *s++; + *d++ = *s++; + *d++ = *s++; + *d++ = *s++; + *d++ = *s++; + } + if (n & 8) { + *d++ = *s++; + *d++ = *s++; + *d++ = *s++; + *d++ = *s++; + *d++ = *s++; + *d++ = *s++; + *d++ = *s++; + *d++ = *s++; + } + if (n & 4) { + *d++ = *s++; + *d++ = *s++; + *d++ = *s++; + *d++ = *s++; + } + if (n & 2) { + *d++ = *s++; + *d++ = *s++; + } + if (n & 1) { *d = *s; } + } + + #endif + #endif + + return dest; + } + + ZPL_END_C_DECLS + // file: source/essentials/memory_custom.c + + + #ifndef _IOSC11_SOURCE + #define _IOSC11_SOURCE + #endif + + #include + + #if defined(ZPL_SYSTEM_WINDOWS) + # include + #endif + + // include errno.h for MinGW + #if defined(ZPL_COMPILER_GCC) || (defined(ZPL_COMPILER_TINYC) && defined(ZPL_SYSTEM_WINDOWS)) + # include + #endif + + #if defined(ZPL_COMPILER_MINGW) + # ifdef __MINGW32__ + # define _aligned_malloc __mingw_aligned_malloc + # define _aligned_free __mingw_aligned_free + # endif //MINGW + #endif + + ZPL_BEGIN_C_DECLS + + char *zpl_alloc_str(zpl_allocator a, char const *str) { + return zpl_alloc_str_len(a, str, zpl__strlen(str)); + } + + //////////////////////////////////////////////////////////////// + // + // Custom Allocation + // + // + + // + // Heap Allocator + // + + #define ZPL_HEAP_STATS_MAGIC 0xDEADC0DE + + typedef struct zpl__heap_stats { + zpl_u32 magic; + zpl_isize used_memory; + zpl_isize alloc_count; + } zpl__heap_stats; + + zpl_global zpl__heap_stats zpl__heap_stats_info; + + void zpl_heap_stats_init(void) { + zpl_zero_item(&zpl__heap_stats_info); + zpl__heap_stats_info.magic = ZPL_HEAP_STATS_MAGIC; + } + zpl_isize zpl_heap_stats_used_memory(void) { + ZPL_ASSERT_MSG(zpl__heap_stats_info.magic == ZPL_HEAP_STATS_MAGIC, "zpl_heap_stats is not initialised yet, call zpl_heap_stats_init first!"); + return zpl__heap_stats_info.used_memory; + } + zpl_isize zpl_heap_stats_alloc_count(void) { + ZPL_ASSERT_MSG(zpl__heap_stats_info.magic == ZPL_HEAP_STATS_MAGIC, "zpl_heap_stats is not initialised yet, call zpl_heap_stats_init first!"); + return zpl__heap_stats_info.alloc_count; + } + void zpl_heap_stats_check(void) { + ZPL_ASSERT_MSG(zpl__heap_stats_info.magic == ZPL_HEAP_STATS_MAGIC, "zpl_heap_stats is not initialised yet, call zpl_heap_stats_init first!"); + ZPL_ASSERT(zpl__heap_stats_info.used_memory == 0); + ZPL_ASSERT(zpl__heap_stats_info.alloc_count == 0); + } + + typedef struct zpl__heap_alloc_info { + zpl_isize size; + void *physical_start; + } zpl__heap_alloc_info; + + ZPL_ALLOCATOR_PROC(zpl_heap_allocator_proc) { + void *ptr = NULL; + zpl_unused(allocator_data); + zpl_unused(old_size); + if (!alignment) alignment = ZPL_DEFAULT_MEMORY_ALIGNMENT; + + # ifdef ZPL_HEAP_ANALYSIS + zpl_isize alloc_info_size = zpl_size_of(zpl__heap_alloc_info); + zpl_isize alloc_info_remainder = (alloc_info_size % alignment); + zpl_isize track_size = zpl_max(alloc_info_size, alignment) + alloc_info_remainder; + switch (type) { + case ZPL_ALLOCATION_FREE: { + if (!old_memory) break; + zpl__heap_alloc_info *alloc_info = cast(zpl__heap_alloc_info *)old_memory - 1; + zpl__heap_stats_info.used_memory -= alloc_info->size; + zpl__heap_stats_info.alloc_count--; + old_memory = alloc_info->physical_start; + } break; + case ZPL_ALLOCATION_ALLOC: { + size += track_size; + } break; + default: break; + } + # endif + + switch (type) { + #if defined(ZPL_COMPILER_MSVC) || (defined(ZPL_COMPILER_GCC) && defined(ZPL_SYSTEM_WINDOWS)) || (defined(ZPL_COMPILER_TINYC) && defined(ZPL_SYSTEM_WINDOWS)) + case ZPL_ALLOCATION_ALLOC: + ptr = _aligned_malloc(size, alignment); + if (flags & ZPL_ALLOCATOR_FLAG_CLEAR_TO_ZERO) zpl_zero_size(ptr, size); + break; + case ZPL_ALLOCATION_FREE: _aligned_free(old_memory); break; + case ZPL_ALLOCATION_RESIZE: { + zpl_allocator a = zpl_heap_allocator(); + ptr = zpl_default_resize_align(a, old_memory, old_size, size, alignment); + } break; + + #elif defined(ZPL_SYSTEM_LINUX) && !defined(ZPL_CPU_ARM) && !defined(ZPL_COMPILER_TINYC) + case ZPL_ALLOCATION_ALLOC: { + ptr = aligned_alloc(alignment, (size + alignment - 1) & ~(alignment - 1)); + + if (flags & ZPL_ALLOCATOR_FLAG_CLEAR_TO_ZERO) { zpl_zero_size(ptr, size); } + } break; + + case ZPL_ALLOCATION_FREE: { + free(old_memory); + } break; + + case ZPL_ALLOCATION_RESIZE: { + zpl_allocator a = zpl_heap_allocator(); + ptr = zpl_default_resize_align(a, old_memory, old_size, size, alignment); + } break; + #else + case ZPL_ALLOCATION_ALLOC: { + posix_memalign(&ptr, alignment, size); + + if (flags & ZPL_ALLOCATOR_FLAG_CLEAR_TO_ZERO) { zpl_zero_size(ptr, size); } + } break; + + case ZPL_ALLOCATION_FREE: { + free(old_memory); + } break; + + case ZPL_ALLOCATION_RESIZE: { + zpl_allocator a = zpl_heap_allocator( ); + ptr = zpl_default_resize_align(a, old_memory, old_size, size, alignment); + } break; + #endif + + case ZPL_ALLOCATION_FREE_ALL: break; + } + + # ifdef ZPL_HEAP_ANALYSIS + if (type == ZPL_ALLOCATION_ALLOC) { + zpl__heap_alloc_info *alloc_info = cast(zpl__heap_alloc_info *)(cast(char *)ptr + alloc_info_remainder); + zpl_zero_item(alloc_info); + alloc_info->size = size - track_size; + alloc_info->physical_start = ptr; + ptr = cast(void*)(alloc_info + 1); + zpl__heap_stats_info.used_memory += alloc_info->size; + zpl__heap_stats_info.alloc_count++; + } + # endif + + return ptr; + } + + // + // Arena Allocator + // + + ZPL_ALLOCATOR_PROC(zpl_arena_allocator_proc) { + zpl_arena *arena = cast(zpl_arena *) allocator_data; + void *ptr = NULL; + + zpl_unused(old_size); + + switch (type) { + case ZPL_ALLOCATION_ALLOC: { + void *end = zpl_pointer_add(arena->physical_start, arena->total_allocated); + zpl_isize total_size = zpl_align_forward_i64(size, alignment); + + // NOTE: Out of memory + if (arena->total_allocated + total_size > cast(zpl_isize) arena->total_size) { + return NULL; + } + + ptr = zpl_align_forward(end, alignment); + arena->total_allocated += total_size; + if (flags & ZPL_ALLOCATOR_FLAG_CLEAR_TO_ZERO) zpl_zero_size(ptr, size); + } break; + + case ZPL_ALLOCATION_FREE: + // NOTE: Free all at once + // Use Temp_Arena_Memory if you want to free a block + break; + + case ZPL_ALLOCATION_FREE_ALL: arena->total_allocated = 0; break; + + case ZPL_ALLOCATION_RESIZE: { + // TODO: Check if ptr is on top of stack and just extend + zpl_allocator a = zpl_arena_allocator(arena); + ptr = zpl_default_resize_align(a, old_memory, old_size, size, alignment); + } break; + } + return ptr; + } + + // + // Pool Allocator + // + + void zpl_pool_init_align(zpl_pool *pool, zpl_allocator backing, zpl_isize num_blocks, zpl_isize block_size, zpl_isize block_align) { + zpl_isize actual_block_size, pool_size, block_index; + void *data, *curr; + zpl_uintptr *end; + + zpl_zero_item(pool); + + pool->backing = backing; + pool->block_size = block_size; + pool->block_align = block_align; + pool->num_blocks = num_blocks; + + actual_block_size = block_size + block_align; + pool_size = num_blocks * actual_block_size; + + data = zpl_alloc_align(backing, pool_size, block_align); + + // NOTE: Init intrusive freelist + curr = data; + for (block_index = 0; block_index < num_blocks - 1; block_index++) { + zpl_uintptr *next = cast(zpl_uintptr *) curr; + *next = cast(zpl_uintptr) curr + actual_block_size; + curr = zpl_pointer_add(curr, actual_block_size); + } + + end = cast(zpl_uintptr *) curr; + *end = cast(zpl_uintptr) NULL; + + pool->physical_start = data; + pool->free_list = data; + } + + ZPL_ALLOCATOR_PROC(zpl_pool_allocator_proc) { + zpl_pool *pool = cast(zpl_pool *) allocator_data; + void *ptr = NULL; + + zpl_unused(old_size); + + switch (type) { + case ZPL_ALLOCATION_ALLOC: { + zpl_uintptr next_free; + ZPL_ASSERT(size == pool->block_size); + ZPL_ASSERT(alignment == pool->block_align); + ZPL_ASSERT(pool->free_list != NULL); + + next_free = *cast(zpl_uintptr *) pool->free_list; + ptr = pool->free_list; + pool->free_list = cast(void *) next_free; + pool->total_size += pool->block_size; + if (flags & ZPL_ALLOCATOR_FLAG_CLEAR_TO_ZERO) zpl_zero_size(ptr, size); + } break; + + case ZPL_ALLOCATION_FREE: { + zpl_uintptr *next; + if (old_memory == NULL) return NULL; + + next = cast(zpl_uintptr *) old_memory; + *next = cast(zpl_uintptr) pool->free_list; + pool->free_list = old_memory; + pool->total_size -= pool->block_size; + } break; + + case ZPL_ALLOCATION_FREE_ALL: { + zpl_isize actual_block_size, block_index; + void *curr; + zpl_uintptr *end; + + actual_block_size = pool->block_size + pool->block_align; + pool->total_size = 0; + + // NOTE: Init intrusive freelist + curr = pool->physical_start; + for (block_index = 0; block_index < pool->num_blocks - 1; block_index++) { + zpl_uintptr *next = cast(zpl_uintptr *) curr; + *next = cast(zpl_uintptr) curr + actual_block_size; + curr = zpl_pointer_add(curr, actual_block_size); + } + + end = cast(zpl_uintptr *) curr; + *end = cast(zpl_uintptr) NULL; + pool->free_list = pool->physical_start; + } break; + + case ZPL_ALLOCATION_RESIZE: + // NOTE: Cannot resize + ZPL_PANIC("You cannot resize something allocated by with a pool."); + break; + } + + return ptr; + } + + + // + // Scratch Memory Allocator + // + + void zpl_scratch_memory_init(zpl_scratch_memory *s, void *start, zpl_isize size) { + s->physical_start = start; + s->total_size = size; + s->alloc_point = start; + s->free_point = start; + } + + zpl_b32 zpl_scratch_memory_is_in_use(zpl_scratch_memory *s, void *ptr) { + if (s->free_point == s->alloc_point) return false; + if (s->alloc_point > s->free_point) return ptr >= s->free_point && ptr < s->alloc_point; + return ptr >= s->free_point || ptr < s->alloc_point; + } + + zpl_allocator zpl_scratch_allocator(zpl_scratch_memory *s) { + zpl_allocator a; + a.proc = zpl_scratch_allocator_proc; + a.data = s; + return a; + } + + ZPL_ALLOCATOR_PROC(zpl_scratch_allocator_proc) { + zpl_scratch_memory *s = cast(zpl_scratch_memory *) allocator_data; + void *ptr = NULL; + ZPL_ASSERT_NOT_NULL(s); + + switch (type) { + case ZPL_ALLOCATION_ALLOC: { + void *pt = s->alloc_point; + zpl_allocation_header_ev *header = cast(zpl_allocation_header_ev *) pt; + void *data = zpl_align_forward(header + 1, alignment); + void *end = zpl_pointer_add(s->physical_start, s->total_size); + + ZPL_ASSERT(alignment % 4 == 0); + size = ((size + 3) / 4) * 4; + pt = zpl_pointer_add(pt, size); + + // NOTE: Wrap around + if (pt > end) { + header->size = zpl_pointer_diff(header, end) | ZPL_ISIZE_HIGH_BIT; + pt = s->physical_start; + header = cast(zpl_allocation_header_ev *) pt; + data = zpl_align_forward(header + 1, alignment); + pt = zpl_pointer_add(pt, size); + } + + if (!zpl_scratch_memory_is_in_use(s, pt)) { + zpl_allocation_header_fill(header, pt, zpl_pointer_diff(header, pt)); + s->alloc_point = cast(zpl_u8 *) pt; + ptr = data; + } + + if (flags & ZPL_ALLOCATOR_FLAG_CLEAR_TO_ZERO) zpl_zero_size(ptr, size); + } break; + + case ZPL_ALLOCATION_FREE: { + if (old_memory) { + void *end = zpl_pointer_add(s->physical_start, s->total_size); + if (old_memory < s->physical_start || old_memory >= end) { + ZPL_ASSERT(false); + } else { + // NOTE: Mark as free + zpl_allocation_header_ev *h = zpl_allocation_header(old_memory); + ZPL_ASSERT((h->size & ZPL_ISIZE_HIGH_BIT) == 0); + h->size = h->size | ZPL_ISIZE_HIGH_BIT; + + while (s->free_point != s->alloc_point) { + zpl_allocation_header_ev *header = cast(zpl_allocation_header_ev *) s->free_point; + if ((header->size & ZPL_ISIZE_HIGH_BIT) == 0) break; + + s->free_point = zpl_pointer_add(s->free_point, h->size & (~ZPL_ISIZE_HIGH_BIT)); + if (s->free_point == end) s->free_point = s->physical_start; + } + } + } + } break; + + case ZPL_ALLOCATION_FREE_ALL: + s->alloc_point = s->physical_start; + s->free_point = s->physical_start; + break; + + case ZPL_ALLOCATION_RESIZE: + ptr = zpl_default_resize_align(zpl_scratch_allocator(s), old_memory, old_size, size, alignment); + break; + } + + return ptr; + } + + // + // Stack Memory Allocator + // + ZPL_ALLOCATOR_PROC(zpl_stack_allocator_proc) { + zpl_stack_memory *s = cast(zpl_stack_memory *) allocator_data; + void *ptr = NULL; + ZPL_ASSERT_NOT_NULL(s); + zpl_unused(old_size); + zpl_unused(flags); + + switch (type) { + case ZPL_ALLOCATION_ALLOC: { + size += ZPL_STACK_ALLOC_OFFSET; + zpl_u64 alloc_offset = s->allocated; + + void *curr = + cast(zpl_u64 *) zpl_align_forward(cast(zpl_u64 *) zpl_pointer_add(s->physical_start, s->allocated), alignment); + + if (cast(zpl_u64 *) zpl_pointer_add(curr, size) > cast(zpl_u64 *) zpl_pointer_add(s->physical_start, s->total_size)) { + if (s->backing.proc) { + void *old_start = s->physical_start; + s->physical_start = + zpl_resize_align(s->backing, s->physical_start, s->total_size, s->total_size + size, alignment); + curr = cast(zpl_u64 *) + zpl_align_forward(cast(zpl_u64 *) zpl_pointer_add(s->physical_start, s->allocated), alignment); + s->total_size = zpl_pointer_diff(old_start, s->physical_start); + } else { + ZPL_PANIC("Can not resize stack's memory! Allocator not defined!"); + } + } + + s->allocated = zpl_pointer_diff(s->physical_start, curr) + size; + + *(zpl_u64 *)curr = alloc_offset; + curr = zpl_pointer_add(curr, ZPL_STACK_ALLOC_OFFSET); + + ptr = curr; + } break; + + case ZPL_ALLOCATION_FREE: { + if (old_memory) { + void *curr = old_memory; + curr = zpl_pointer_sub(curr, ZPL_STACK_ALLOC_OFFSET); + + zpl_u64 alloc_offset = *(zpl_u64 *)curr; + s->allocated = (zpl_usize)alloc_offset; + } + } break; + + case ZPL_ALLOCATION_FREE_ALL: { + s->allocated = 0; + } break; + + case ZPL_ALLOCATION_RESIZE: { + ZPL_PANIC("You cannot resize something allocated by a stack."); + } break; + } + return ptr; + } + + ZPL_END_C_DECLS +# if defined(ZPL_MODULE_CORE) + // file: source/core/memory_virtual.c + + //////////////////////////////////////////////////////////////// + // + // Virtual Memory + // + // + + ZPL_BEGIN_C_DECLS + + zpl_virtual_memory zpl_vm(void *data, zpl_isize size) { + zpl_virtual_memory vm; + vm.data = data; + vm.size = size; + return vm; + } + + #if defined(ZPL_SYSTEM_WINDOWS) + zpl_virtual_memory zpl_vm_alloc(void *addr, zpl_isize size) { + zpl_virtual_memory vm; + ZPL_ASSERT(size > 0); + vm.data = VirtualAlloc(addr, size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); + vm.size = size; + return vm; + } + + zpl_b32 zpl_vm_free(zpl_virtual_memory vm) { + MEMORY_BASIC_INFORMATION info; + while (vm.size > 0) { + if (VirtualQuery(vm.data, &info, zpl_size_of(info)) == 0) return false; + if (info.BaseAddress != vm.data || info.AllocationBase != vm.data || info.State != MEM_COMMIT || + info.RegionSize > cast(zpl_usize) vm.size) { + return false; + } + if (VirtualFree(vm.data, 0, MEM_RELEASE) == 0) return false; + vm.data = zpl_pointer_add(vm.data, info.RegionSize); + vm.size -= info.RegionSize; + } + return true; + } + + zpl_virtual_memory zpl_vm_trim(zpl_virtual_memory vm, zpl_isize lead_size, zpl_isize size) { + zpl_virtual_memory new_vm = { 0 }; + void *ptr; + ZPL_ASSERT(vm.size >= lead_size + size); + + ptr = zpl_pointer_add(vm.data, lead_size); + + zpl_vm_free(vm); + new_vm = zpl_vm_alloc(ptr, size); + if (new_vm.data == ptr) return new_vm; + if (new_vm.data) zpl_vm_free(new_vm); + return new_vm; + } + + zpl_b32 zpl_vm_purge(zpl_virtual_memory vm) { + VirtualAlloc(vm.data, vm.size, MEM_RESET, PAGE_READWRITE); + // NOTE: Can this really fail? + return true; + } + + zpl_isize zpl_virtual_memory_page_size(zpl_isize *alignment_out) { + SYSTEM_INFO info; + GetSystemInfo(&info); + if (alignment_out) *alignment_out = info.dwAllocationGranularity; + return info.dwPageSize; + } + + #else + # include + + # ifndef MAP_ANONYMOUS + # define MAP_ANONYMOUS MAP_ANON + # endif + + zpl_virtual_memory zpl_vm_alloc(void *addr, zpl_isize size) { + zpl_virtual_memory vm; + ZPL_ASSERT(size > 0); + vm.data = mmap(addr, size, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); + vm.size = size; + return vm; + } + + zpl_b32 zpl_vm_free(zpl_virtual_memory vm) { + munmap(vm.data, vm.size); + return true; + } + + zpl_virtual_memory zpl_vm_trim(zpl_virtual_memory vm, zpl_isize lead_size, zpl_isize size) { + void *ptr; + zpl_isize trail_size; + ZPL_ASSERT(vm.size >= lead_size + size); + + ptr = zpl_pointer_add(vm.data, lead_size); + trail_size = vm.size - lead_size - size; + + if (lead_size != 0) zpl_vm_free(zpl_vm(vm.data, lead_size)); + if (trail_size != 0) zpl_vm_free(zpl_vm(ptr, trail_size)); + return zpl_vm(ptr, size); + } + + zpl_b32 zpl_vm_purge(zpl_virtual_memory vm) { + int err = madvise(vm.data, vm.size, MADV_DONTNEED); + return err != 0; + } + + zpl_isize zpl_virtual_memory_page_size(zpl_isize *alignment_out) { + // TODO: Is this always true? + zpl_isize result = cast(zpl_isize) sysconf(_SC_PAGE_SIZE); + if (alignment_out) *alignment_out = result; + return result; + } + + #endif + + ZPL_END_C_DECLS + // file: source/core/string.c + + //////////////////////////////////////////////////////////////// + // + // Char things + // + // + + ZPL_BEGIN_C_DECLS + + zpl_internal zpl_isize zpl__scan_zpl_i64(const char *text, zpl_i32 base, zpl_i64 *value) { + const char *text_begin = text; + zpl_i64 result = 0; + zpl_b32 negative = false; + + if (*text == '-') { + negative = true; + text++; + } + + if (base == 16 && zpl_strncmp(text, "0x", 2) == 0) text += 2; + + for (;;) { + zpl_i64 v; + if (zpl_char_is_digit(*text)) + v = *text - '0'; + else if (base == 16 && zpl_char_is_hex_digit(*text)) + v = zpl_hex_digit_to_int(*text); + else + break; + + result *= base; + result += v; + text++; + } + + if (value) { + if (negative) result = -result; + *value = result; + } + + return (text - text_begin); + } + + zpl_internal zpl_isize zpl__scan_zpl_u64(const char *text, zpl_i32 base, zpl_u64 *value) { + const char *text_begin = text; + zpl_u64 result = 0; + + if (base == 16 && zpl_strncmp(text, "0x", 2) == 0) text += 2; + + for (;;) { + zpl_u64 v; + if (zpl_char_is_digit(*text)) + v = *text - '0'; + else if (base == 16 && zpl_char_is_hex_digit(*text)) + v = zpl_hex_digit_to_int(*text); + else { + break; + } + + result *= base; + result += v; + text++; + } + + if (value) *value = result; + + return (text - text_begin); + } + + // TODO: Make better + zpl_u64 zpl_str_to_u64(const char *str, char **end_ptr, zpl_i32 base) { + zpl_isize len; + zpl_u64 value = 0; + + if (!base) { + if ((zpl_strlen(str) > 2) && (zpl_strncmp(str, "0x", 2) == 0)) + base = 16; + else + base = 10; + } + + len = zpl__scan_zpl_u64(str, base, &value); + if (end_ptr) *end_ptr = (char *)str + len; + return value; + } + + zpl_i64 zpl_str_to_i64(const char *str, char **end_ptr, zpl_i32 base) { + zpl_isize len; + zpl_i64 value; + + if (!base) { + if ((zpl_strlen(str) > 2) && (zpl_strncmp(str, "0x", 2) == 0)) + base = 16; + else + base = 10; + } + + len = zpl__scan_zpl_i64(str, base, &value); + if (end_ptr) *end_ptr = (char *)str + len; + return value; + } + + // TODO: Are these good enough for characters? + zpl_global const char zpl__num_to_char_table[] = "0123456789" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "@$"; + + void zpl_i64_to_str(zpl_i64 value, char *string, zpl_i32 base) { + char *buf = string; + zpl_b32 negative = false; + zpl_u64 v; + + if (value < 0) { + negative = true; + value = -value; + } + + v = cast(zpl_u64) value; + if (v != 0) { + while (v > 0) { + *buf++ = zpl__num_to_char_table[v % base]; + v /= base; + } + } else { + *buf++ = '0'; + } + if (negative) *buf++ = '-'; + *buf = '\0'; + zpl_strrev(string); + } + + void zpl_u64_to_str(zpl_u64 value, char *string, zpl_i32 base) { + char *buf = string; + + if (value) { + while (value > 0) { + *buf++ = zpl__num_to_char_table[value % base]; + value /= base; + } + } else { + *buf++ = '0'; + } + *buf = '\0'; + + zpl_strrev(string); + } + + zpl_f64 zpl_str_to_f64(const char *str, char **end_ptr) { + zpl_f64 result, value, sign, scale; + zpl_i32 frac; + + while (zpl_char_is_space(*str)) { str++; } + + sign = 1.0; + if (*str == '-') { + sign = -1.0; + str++; + } else if (*str == '+') { + str++; + } + + for (value = 0.0; zpl_char_is_digit(*str); str++) { value = value * 10.0 + (*str - '0'); } + + if (*str == '.') { + zpl_f64 pow10 = 10.0; + str++; + while (zpl_char_is_digit(*str)) { + value += (*str - '0') / pow10; + pow10 *= 10.0; + str++; + } + } + + frac = 0; + scale = 1.0; + if ((*str == 'e') || (*str == 'E')) { + zpl_u32 exp; + + str++; + if (*str == '-') { + frac = 1; + str++; + } else if (*str == '+') { + str++; + } + + for (exp = 0; zpl_char_is_digit(*str); str++) { exp = exp * 10 + (*str - '0'); } + if (exp > 308) exp = 308; + + while (exp >= 50) { + scale *= 1e50; + exp -= 50; + } + while (exp >= 8) { + scale *= 1e8; + exp -= 8; + } + while (exp > 0) { + scale *= 10.0; + exp -= 1; + } + } + + result = sign * (frac ? (value / scale) : (value * scale)); + + if (end_ptr) *end_ptr = cast(char *) str; + + return result; + } + + + + //////////////////////////////////////////////////////////////// + // + // Windows UTF-8 Handling + // + // + + zpl_u16 *zpl_utf8_to_ucs2(zpl_u16 *buffer, zpl_isize len, zpl_u8 const *str) { + zpl_rune c; + zpl_isize i = 0; + len--; + while (*str) { + if (i >= len) return NULL; + if (!(*str & 0x80)) { + buffer[i++] = *str++; + } else if ((*str & 0xe0) == 0xc0) { + if (*str < 0xc2) return NULL; + c = (*str++ & 0x1f) << 6; + if ((*str & 0xc0) != 0x80) return NULL; + buffer[i++] = cast(zpl_u16)(c + (*str++ & 0x3f)); + } else if ((*str & 0xf0) == 0xe0) { + if (*str == 0xe0 && (str[1] < 0xa0 || str[1] > 0xbf)) return NULL; + if (*str == 0xed && str[1] > 0x9f) // str[1] < 0x80 is checked below + return NULL; + c = (*str++ & 0x0f) << 12; + if ((*str & 0xc0) != 0x80) return NULL; + c += (*str++ & 0x3f) << 6; + if ((*str & 0xc0) != 0x80) return NULL; + buffer[i++] = cast(zpl_u16)(c + (*str++ & 0x3f)); + } else if ((*str & 0xf8) == 0xf0) { + if (*str > 0xf4) return NULL; + if (*str == 0xf0 && (str[1] < 0x90 || str[1] > 0xbf)) return NULL; + if (*str == 0xf4 && str[1] > 0x8f) // str[1] < 0x80 is checked below + return NULL; + c = (*str++ & 0x07) << 18; + if ((*str & 0xc0) != 0x80) return NULL; + c += (*str++ & 0x3f) << 12; + if ((*str & 0xc0) != 0x80) return NULL; + c += (*str++ & 0x3f) << 6; + if ((*str & 0xc0) != 0x80) return NULL; + c += (*str++ & 0x3f); + // UTF-8 encodings of values used in surrogate pairs are invalid + if ((c & 0xfffff800) == 0xd800) return NULL; + if (c >= 0x10000) { + c -= 0x10000; + if (i + 2 > len) return NULL; + buffer[i++] = 0xd800 | (0x3ff & (c >> 10)); + buffer[i++] = 0xdc00 | (0x3ff & (c)); + } + } else { + return NULL; + } + } + buffer[i] = 0; + return buffer; + } + + zpl_u8 *zpl_ucs2_to_utf8(zpl_u8 *buffer, zpl_isize len, zpl_u16 const *str) { + zpl_isize i = 0; + len--; + while (*str) { + if (*str < 0x80) { + if (i + 1 > len) return NULL; + buffer[i++] = (char)*str++; + } else if (*str < 0x800) { + if (i + 2 > len) return NULL; + buffer[i++] = cast(char)(0xc0 + (*str >> 6)); + buffer[i++] = cast(char)(0x80 + (*str & 0x3f)); + str += 1; + } else if (*str >= 0xd800 && *str < 0xdc00) { + zpl_rune c; + if (i + 4 > len) return NULL; + c = ((str[0] - 0xd800) << 10) + ((str[1]) - 0xdc00) + 0x10000; + buffer[i++] = cast(char)(0xf0 + (c >> 18)); + buffer[i++] = cast(char)(0x80 + ((c >> 12) & 0x3f)); + buffer[i++] = cast(char)(0x80 + ((c >> 6) & 0x3f)); + buffer[i++] = cast(char)(0x80 + ((c)&0x3f)); + str += 2; + } else if (*str >= 0xdc00 && *str < 0xe000) { + return NULL; + } else { + if (i + 3 > len) return NULL; + buffer[i++] = 0xe0 + (*str >> 12); + buffer[i++] = 0x80 + ((*str >> 6) & 0x3f); + buffer[i++] = 0x80 + ((*str) & 0x3f); + str += 1; + } + } + buffer[i] = 0; + return buffer; + } + + zpl_u16 *zpl_utf8_to_ucs2_buf(zpl_u8 const *str) { // NOTE: Uses locally persisting buffer + zpl_local_persist zpl_u16 buf[4096]; + return zpl_utf8_to_ucs2(buf, zpl_count_of(buf), str); + } + + zpl_u8 *zpl_ucs2_to_utf8_buf(zpl_u16 const *str) { // NOTE: Uses locally persisting buffer + zpl_local_persist zpl_u8 buf[4096]; + return zpl_ucs2_to_utf8(buf, zpl_count_of(buf), str); + } + + zpl_global zpl_u8 const zpl__utf8_first[256] = { + 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, // 0x00-0x0F + 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, // 0x10-0x1F + 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, // 0x20-0x2F + 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, // 0x30-0x3F + 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, // 0x40-0x4F + 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, // 0x50-0x5F + 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, // 0x60-0x6F + 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, // 0x70-0x7F + 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, // 0x80-0x8F + 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, // 0x90-0x9F + 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, // 0xA0-0xAF + 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, // 0xB0-0xBF + 0xf1, 0xf1, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, // 0xC0-0xCF + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, // 0xD0-0xDF + 0x13, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x23, 0x03, 0x03, // 0xE0-0xEF + 0x34, 0x04, 0x04, 0x04, 0x44, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, // 0xF0-0xFF + }; + + + typedef struct zpl_utf8_accept_range { + zpl_u8 lo, hi; + } zpl_utf8_accept_range; + + zpl_global zpl_utf8_accept_range const zpl__utf8_accept_ranges[] = { + { 0x80, 0xbf }, { 0xa0, 0xbf }, { 0x80, 0x9f }, { 0x90, 0xbf }, { 0x80, 0x8f }, + }; + + zpl_isize zpl_utf8_decode(zpl_u8 const *str, zpl_isize str_len, zpl_rune *codepoint_out) { + + zpl_isize width = 0; + zpl_rune codepoint = ZPL_RUNE_INVALID; + + if (str_len > 0) { + zpl_u8 s0 = str[0]; + zpl_u8 x = zpl__utf8_first[s0], sz; + zpl_u8 b1, b2, b3; + zpl_utf8_accept_range accept; + if (x >= 0xf0) { + zpl_rune mask = (cast(zpl_rune) x << 31) >> 31; + codepoint = (cast(zpl_rune) s0 & (~mask)) | (ZPL_RUNE_INVALID & mask); + width = 1; + goto end; + } + if (s0 < 0x80) { + codepoint = s0; + width = 1; + goto end; + } + + sz = x & 7; + accept = zpl__utf8_accept_ranges[x >> 4]; + if (str_len < sz) goto invalid_codepoint; + + b1 = str[1]; + if (b1 < accept.lo || accept.hi < b1) goto invalid_codepoint; + + if (sz == 2) { + codepoint = (cast(zpl_rune) s0 & 0x1f) << 6 | (cast(zpl_rune) b1 & 0x3f); + width = 2; + goto end; + } + + b2 = str[2]; + if (!zpl_is_between(b2, 0x80, 0xbf)) goto invalid_codepoint; + + if (sz == 3) { + codepoint = (cast(zpl_rune) s0 & 0x1f) << 12 | (cast(zpl_rune) b1 & 0x3f) << 6 | (cast(zpl_rune) b2 & 0x3f); + width = 3; + goto end; + } + + b3 = str[3]; + if (!zpl_is_between(b3, 0x80, 0xbf)) goto invalid_codepoint; + + codepoint = (cast(zpl_rune) s0 & 0x07) << 18 | (cast(zpl_rune) b1 & 0x3f) << 12 | (cast(zpl_rune) b2 & 0x3f) << 6 | + (cast(zpl_rune) b3 & 0x3f); + width = 4; + goto end; + + invalid_codepoint: + codepoint = ZPL_RUNE_INVALID; + width = 1; + } + + end: + if (codepoint_out) *codepoint_out = codepoint; + return width; + } + + zpl_isize zpl_utf8_codepoint_size(zpl_u8 const *str, zpl_isize str_len) { + zpl_isize i = 0; + for (; i < str_len && str[i]; i++) { + if ((str[i] & 0xc0) != 0x80) break; + } + return i + 1; + } + + zpl_isize zpl_utf8_encode_rune(zpl_u8 buf[4], zpl_rune r) { + zpl_u32 i = cast(zpl_u32) r; + zpl_u8 mask = 0x3f; + if (i <= (1 << 7) - 1) { + buf[0] = cast(zpl_u8) r; + return 1; + } + if (i <= (1 << 11) - 1) { + buf[0] = 0xc0 | cast(zpl_u8)(r >> 6); + buf[1] = 0x80 | (cast(zpl_u8)(r) & mask); + return 2; + } + + // Invalid or Surrogate range + if (i > ZPL_RUNE_MAX || zpl_is_between(i, 0xd800, 0xdfff)) { + r = ZPL_RUNE_INVALID; + + buf[0] = 0xe0 | cast(zpl_u8)(r >> 12); + buf[1] = 0x80 | (cast(zpl_u8)(r >> 6) & mask); + buf[2] = 0x80 | (cast(zpl_u8)(r) & mask); + return 3; + } + + if (i <= (1 << 16) - 1) { + buf[0] = 0xe0 | cast(zpl_u8)(r >> 12); + buf[1] = 0x80 | (cast(zpl_u8)(r >> 6) & mask); + buf[2] = 0x80 | (cast(zpl_u8)(r) & mask); + return 3; + } + + buf[0] = 0xf0 | cast(zpl_u8)(r >> 18); + buf[1] = 0x80 | (cast(zpl_u8)(r >> 12) & mask); + buf[2] = 0x80 | (cast(zpl_u8)(r >> 6) & mask); + buf[3] = 0x80 | (cast(zpl_u8)(r) & mask); + return 4; + } + + ZPL_END_C_DECLS + // file: source/core/stringlib.c + + + ZPL_BEGIN_C_DECLS + + zpl_string zpl_string_make_reserve(zpl_allocator a, zpl_isize capacity) { + zpl_isize header_size = zpl_size_of(zpl_string_header); + void *ptr = zpl_alloc(a, header_size + capacity + 1); + + zpl_string str; + zpl_string_header *header; + + if (ptr == NULL) return NULL; + zpl_zero_size(ptr, header_size + capacity + 1); + + str = cast(char *) ptr + header_size; + header = ZPL_STRING_HEADER(str); + header->allocator = a; + header->length = 0; + header->capacity = capacity; + str[capacity] = '\0'; + + return str; + } + + + zpl_string zpl_string_make_length(zpl_allocator a, void const *init_str, zpl_isize num_bytes) { + zpl_isize header_size = zpl_size_of(zpl_string_header); + void *ptr = zpl_alloc(a, header_size + num_bytes + 1); + + zpl_string str; + zpl_string_header *header; + + if (ptr == NULL) return NULL; + if (!init_str) zpl_zero_size(ptr, header_size + num_bytes + 1); + + str = cast(char *) ptr + header_size; + header = ZPL_STRING_HEADER(str); + header->allocator = a; + header->length = num_bytes; + header->capacity = num_bytes; + if (num_bytes && init_str) zpl_memcopy(str, init_str, num_bytes); + str[num_bytes] = '\0'; + + return str; + } + + zpl_string zpl_string_sprintf_buf(zpl_allocator a, const char *fmt, ...) { + zpl_local_persist zpl_thread_local char buf[ZPL_PRINTF_MAXLEN] = { 0 }; + va_list va; + va_start(va, fmt); + zpl_snprintf_va(buf, ZPL_PRINTF_MAXLEN, fmt, va); + va_end(va); + + return zpl_string_make(a, buf); + } + + zpl_string zpl_string_sprintf(zpl_allocator a, char *buf, zpl_isize num_bytes, const char *fmt, ...) { + va_list va; + va_start(va, fmt); + zpl_snprintf_va(buf, num_bytes, fmt, va); + va_end(va); + + return zpl_string_make(a, buf); + } + + zpl_string zpl_string_append_length(zpl_string str, void const *other, zpl_isize other_len) { + if (other_len > 0) { + zpl_isize curr_len = zpl_string_length(str); + + str = zpl_string_make_space_for(str, other_len); + if (str == NULL) return NULL; + + zpl_memcopy(str + curr_len, other, other_len); + str[curr_len + other_len] = '\0'; + zpl__set_string_length(str, curr_len + other_len); + } + return str; + } + + ZPL_ALWAYS_INLINE zpl_string zpl_string_appendc(zpl_string str, const char *other) { + return zpl_string_append_length(str, other, zpl_strlen(other)); + } + + ZPL_ALWAYS_INLINE zpl_string zpl_string_join(zpl_allocator a, const char **parts, zpl_isize count, const char *glue) { + zpl_string ret; + zpl_isize i; + + ret = zpl_string_make(a, NULL); + + for (i=0; i= add_len) { + return str; + } else { + zpl_isize new_len, old_size, new_size; + void *ptr, *new_ptr; + zpl_allocator a = ZPL_STRING_HEADER(str)->allocator; + zpl_string_header *header; + + new_len = zpl_string_length(str) + add_len; + ptr = ZPL_STRING_HEADER(str); + old_size = zpl_size_of(zpl_string_header) + zpl_string_length(str) + 1; + new_size = zpl_size_of(zpl_string_header) + new_len + 1; + + new_ptr = zpl_resize(a, ptr, old_size, new_size); + if (new_ptr == NULL) return NULL; + + header = cast(zpl_string_header *) new_ptr; + header->allocator = a; + + str = cast(zpl_string)(header + 1); + zpl__set_string_capacity(str, new_len); + + return str; + } + } + + zpl_isize zpl_string_allocation_size(zpl_string const str) { + zpl_isize cap = zpl_string_capacity(str); + return zpl_size_of(zpl_string_header) + cap; + } + + zpl_b32 zpl_string_are_equal(zpl_string const lhs, zpl_string const rhs) { + zpl_isize lhs_len, rhs_len, i; + lhs_len = zpl_string_length(lhs); + rhs_len = zpl_string_length(rhs); + if (lhs_len != rhs_len) return false; + + for (i = 0; i < lhs_len; i++) { + if (lhs[i] != rhs[i]) return false; + } + + return true; + } + + zpl_string zpl_string_trim(zpl_string str, const char *cut_set) { + char *start, *end, *start_pos, *end_pos; + zpl_isize len; + + start_pos = start = str; + end_pos = end = str + zpl_string_length(str) - 1; + + while (start_pos <= end && zpl_char_first_occurence(cut_set, *start_pos)) start_pos++; + while (end_pos > start_pos && zpl_char_first_occurence(cut_set, *end_pos)) end_pos--; + + len = cast(zpl_isize)((start_pos > end_pos) ? 0 : ((end_pos - start_pos) + 1)); + + if (str != start_pos) zpl_memmove(str, start_pos, len); + str[len] = '\0'; + + zpl__set_string_length(str, len); + + return str; + } + + zpl_string zpl_string_append_rune(zpl_string str, zpl_rune r) { + if (r >= 0) { + zpl_u8 buf[8] = { 0 }; + zpl_isize len = zpl_utf8_encode_rune(buf, r); + return zpl_string_append_length(str, buf, len); + } + + return str; + } + + zpl_string zpl_string_append_fmt(zpl_string str, const char *fmt, ...) { + zpl_isize res; + char buf[ZPL_PRINTF_MAXLEN] = { 0 }; + va_list va; + va_start(va, fmt); + res = zpl_snprintf_va(buf, zpl_count_of(buf) - 1, fmt, va) - 1; + va_end(va); + return zpl_string_append_length(str, buf, res); + } + + ZPL_END_C_DECLS + // file: source/core/file.c + + + //////////////////////////////////////////////////////////////// + // + // File Handling + // + // + #include + + #ifdef ZPL_SYSTEM_MACOS + # include + #endif + + #ifdef ZPL_SYSTEM_CYGWIN + # include + #endif + + #if defined(ZPL_SYSTEM_WINDOWS) && !defined(ZPL_COMPILER_GCC) + #include + #endif + + ZPL_BEGIN_C_DECLS + + #if defined(ZPL_SYSTEM_WINDOWS) || defined (ZPL_SYSTEM_CYGWIN) + + zpl_internal wchar_t *zpl__alloc_utf8_to_ucs2(zpl_allocator a, char const *text, zpl_isize *w_len_) { + wchar_t *w_text = NULL; + zpl_isize len = 0, w_len = 0, w_len1 = 0; + if (text == NULL) { + if (w_len_) *w_len_ = w_len; + return NULL; + } + len = zpl_strlen(text); + if (len == 0) { + if (w_len_) *w_len_ = w_len; + return NULL; + } + w_len = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, text, cast(int) len, NULL, 0); + if (w_len == 0) { + if (w_len_) *w_len_ = w_len; + return NULL; + } + w_text = zpl_alloc_array(a, wchar_t, w_len + 1); + w_len1 = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, text, cast(int) len, w_text, cast(int) w_len); + if (w_len1 == 0) { + zpl_free(a, w_text); + if (w_len_) *w_len_ = 0; + return NULL; + } + w_text[w_len] = 0; + if (w_len_) *w_len_ = w_len; + return w_text; + } + + zpl_internal ZPL_FILE_SEEK_PROC(zpl__win32_file_seek) { + LARGE_INTEGER li_offset; + li_offset.QuadPart = offset; + if (!SetFilePointerEx(fd.p, li_offset, &li_offset, whence)) { return false; } + + if (new_offset) *new_offset = li_offset.QuadPart; + return true; + } + + zpl_internal ZPL_FILE_READ_AT_PROC(zpl__win32_file_read) { + zpl_unused(stop_at_newline); + zpl_b32 result = false; + zpl__win32_file_seek(fd, offset, ZPL_SEEK_WHENCE_BEGIN, NULL); + DWORD size_ = cast(DWORD)(size > ZPL_I32_MAX ? ZPL_I32_MAX : size); + DWORD bytes_read_; + if (ReadFile(fd.p, buffer, size_, &bytes_read_, NULL)) { + if (bytes_read) *bytes_read = bytes_read_; + result = true; + } + + return result; + } + + zpl_internal ZPL_FILE_WRITE_AT_PROC(zpl__win32_file_write) { + DWORD size_ = cast(DWORD)(size > ZPL_I32_MAX ? ZPL_I32_MAX : size); + DWORD bytes_written_; + zpl__win32_file_seek(fd, offset, ZPL_SEEK_WHENCE_BEGIN, NULL); + if (WriteFile(fd.p, buffer, size_, &bytes_written_, NULL)) { + if (bytes_written) *bytes_written = bytes_written_; + return true; + } + return false; + } + + zpl_internal ZPL_FILE_CLOSE_PROC(zpl__win32_file_close) { CloseHandle(fd.p); } + + zpl_file_operations const zpl_default_file_operations = { zpl__win32_file_read, zpl__win32_file_write, + zpl__win32_file_seek, zpl__win32_file_close }; + + ZPL_NEVER_INLINE ZPL_FILE_OPEN_PROC(zpl__win32_file_open) { + DWORD desired_access; + DWORD creation_disposition; + void *handle; + wchar_t *w_text; + + switch (mode & ZPL_FILE_MODES) { + case ZPL_FILE_MODE_READ: + desired_access = GENERIC_READ; + creation_disposition = OPEN_EXISTING; + break; + case ZPL_FILE_MODE_WRITE: + desired_access = GENERIC_WRITE; + creation_disposition = CREATE_ALWAYS; + break; + case ZPL_FILE_MODE_APPEND: + desired_access = GENERIC_WRITE; + creation_disposition = OPEN_ALWAYS; + break; + case ZPL_FILE_MODE_READ | ZPL_FILE_MODE_RW: + desired_access = GENERIC_READ | GENERIC_WRITE; + creation_disposition = OPEN_EXISTING; + break; + case ZPL_FILE_MODE_WRITE | ZPL_FILE_MODE_RW: + desired_access = GENERIC_READ | GENERIC_WRITE; + creation_disposition = CREATE_ALWAYS; + break; + case ZPL_FILE_MODE_APPEND | ZPL_FILE_MODE_RW: + desired_access = GENERIC_READ | GENERIC_WRITE; + creation_disposition = OPEN_ALWAYS; + break; + default: ZPL_PANIC("Invalid file mode"); return ZPL_FILE_ERROR_INVALID; + } + + w_text = zpl__alloc_utf8_to_ucs2(zpl_heap_allocator( ), filename, NULL); + handle = CreateFileW(w_text, desired_access, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, creation_disposition, + FILE_ATTRIBUTE_NORMAL, NULL); + + zpl_free(zpl_heap_allocator( ), w_text); + + if (handle == INVALID_HANDLE_VALUE) { + DWORD err = GetLastError( ); + switch (err) { + case ERROR_FILE_NOT_FOUND: return ZPL_FILE_ERROR_NOT_EXISTS; + case ERROR_FILE_EXISTS: return ZPL_FILE_ERROR_EXISTS; + case ERROR_ALREADY_EXISTS: return ZPL_FILE_ERROR_EXISTS; + case ERROR_ACCESS_DENIED: return ZPL_FILE_ERROR_PERMISSION; + } + return ZPL_FILE_ERROR_INVALID; + } + + if (mode & ZPL_FILE_MODE_APPEND) { + LARGE_INTEGER offset = { 0 }; + if (!SetFilePointerEx(handle, offset, NULL, ZPL_SEEK_WHENCE_END)) { + CloseHandle(handle); + return ZPL_FILE_ERROR_INVALID; + } + } + + fd->p = handle; + *ops = zpl_default_file_operations; + return ZPL_FILE_ERROR_NONE; + } + + #else // POSIX + # include + + zpl_internal ZPL_FILE_SEEK_PROC(zpl__posix_file_seek) { + # if defined(ZPL_SYSTEM_OSX) + zpl_i64 res = lseek(fd.i, offset, whence); + # else // TODO(ZaKlaus): @fixme lseek64 + zpl_i64 res = lseek(fd.i, offset, whence); + # endif + if (res < 0) return false; + if (new_offset) *new_offset = res; + return true; + } + + zpl_internal ZPL_FILE_READ_AT_PROC(zpl__posix_file_read) { + zpl_unused(stop_at_newline); + zpl_isize res = pread(fd.i, buffer, size, offset); + if (res < 0) return false; + if (bytes_read) *bytes_read = res; + return true; + } + + zpl_internal ZPL_FILE_WRITE_AT_PROC(zpl__posix_file_write) { + zpl_isize res; + zpl_i64 curr_offset = 0; + zpl__posix_file_seek(fd, 0, ZPL_SEEK_WHENCE_CURRENT, &curr_offset); + if (curr_offset == offset) { + // NOTE: Writing to stdout et al. doesn't like pwrite for numerous reasons + res = write(cast(int) fd.i, buffer, size); + } else { + res = pwrite(cast(int) fd.i, buffer, size, offset); + } + if (res < 0) return false; + if (bytes_written) *bytes_written = res; + return true; + } + + zpl_internal ZPL_FILE_CLOSE_PROC(zpl__posix_file_close) { close(fd.i); } + + zpl_file_operations const zpl_default_file_operations = { zpl__posix_file_read, zpl__posix_file_write, + zpl__posix_file_seek, zpl__posix_file_close }; + + ZPL_NEVER_INLINE ZPL_FILE_OPEN_PROC(zpl__posix_file_open) { + zpl_i32 os_mode; + switch (mode & ZPL_FILE_MODES) { + case ZPL_FILE_MODE_READ: os_mode = O_RDONLY; break; + case ZPL_FILE_MODE_WRITE: os_mode = O_WRONLY | O_CREAT | O_TRUNC; break; + case ZPL_FILE_MODE_APPEND: os_mode = O_WRONLY | O_APPEND | O_CREAT; break; + case ZPL_FILE_MODE_READ | ZPL_FILE_MODE_RW: os_mode = O_RDWR; break; + case ZPL_FILE_MODE_WRITE | ZPL_FILE_MODE_RW: os_mode = O_RDWR | O_CREAT | O_TRUNC; break; + case ZPL_FILE_MODE_APPEND | ZPL_FILE_MODE_RW: os_mode = O_RDWR | O_APPEND | O_CREAT; break; + default: ZPL_PANIC("Invalid file mode"); return ZPL_FILE_ERROR_INVALID; + } + + fd->i = open(filename, os_mode, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH); + if (fd->i < 0) { + // TODO: More file errors + return ZPL_FILE_ERROR_INVALID; + } + + *ops = zpl_default_file_operations; + return ZPL_FILE_ERROR_NONE; + } + + #endif + + zpl_file_error zpl_file_new(zpl_file *f, zpl_file_descriptor fd, zpl_file_operations ops, char const *filename) { + zpl_file_error err = ZPL_FILE_ERROR_NONE; + zpl_isize len = zpl_strlen(filename); + + f->ops = ops; + f->fd = fd; + f->dir = NULL; + f->last_write_time = 0; + f->filename = zpl_alloc_array(zpl_heap_allocator( ), char, len + 1); + zpl_memcopy(cast(char *) f->filename, cast(char *) filename, len + 1); + + return err; + } + + zpl_file_error zpl_file_open_mode(zpl_file *f, zpl_file_mode mode, char const *filename) { + zpl_file file_ = {0}; + *f = file_; + zpl_file_error err; + #if defined(ZPL_SYSTEM_WINDOWS) || defined(ZPL_SYSTEM_CYGWIN) + err = zpl__win32_file_open(&f->fd, &f->ops, mode, filename); + #else + err = zpl__posix_file_open(&f->fd, &f->ops, mode, filename); + #endif + if (err == ZPL_FILE_ERROR_NONE) return zpl_file_new(f, f->fd, f->ops, filename); + return err; + } + + zpl_internal void zpl__dirinfo_free_entry(zpl_dir_entry *entry); + + zpl_file_error zpl_file_close(zpl_file *f) { + if (!f) return ZPL_FILE_ERROR_INVALID; + + if (f->filename) zpl_free(zpl_heap_allocator( ), cast(char *) f->filename); + + #if defined(ZPL_SYSTEM_WINDOWS) + if (f->fd.p == INVALID_HANDLE_VALUE) return ZPL_FILE_ERROR_INVALID; + #else + if (f->fd.i < 0) return ZPL_FILE_ERROR_INVALID; + #endif + + if (f->is_temp) + { + f->ops.close(f->fd); + return ZPL_FILE_ERROR_NONE; + } + + if (!f->ops.read_at) f->ops = zpl_default_file_operations; + f->ops.close(f->fd); + + if (f->dir) { + zpl__dirinfo_free_entry(f->dir); + zpl_mfree(f->dir); + f->dir = NULL; + } + + return ZPL_FILE_ERROR_NONE; + } + + + zpl_file_error zpl_file_create(zpl_file *f, char const *filename) { + return zpl_file_open_mode(f, ZPL_FILE_MODE_WRITE | ZPL_FILE_MODE_RW, filename); + } + + zpl_file_error zpl_file_open(zpl_file *f, char const *filename) { + return zpl_file_open_mode(f, ZPL_FILE_MODE_READ, filename); + } + + char const *zpl_file_name(zpl_file *f) { return f->filename ? f->filename : ""; } + + zpl_b32 zpl_file_has_changed(zpl_file *f) { + if (f->is_temp) + return false; + zpl_b32 result = false; + zpl_file_time last_write_time = zpl_fs_last_write_time(f->filename); + if (f->last_write_time != last_write_time) { + result = true; + f->last_write_time = last_write_time; + } + return result; + } + + // TODO: Is this a bad idea? + zpl_global zpl_b32 zpl__std_file_set = false; + zpl_global zpl_file zpl__std_files[ZPL_FILE_STANDARD_COUNT] = { { 0 } }; + + #if defined(ZPL_SYSTEM_WINDOWS) || defined(ZPL_SYSTEM_CYGWIN) + + zpl_file *zpl_file_get_standard(zpl_file_standard_type std) { + if (!zpl__std_file_set) { + #define ZPL__SET_STD_FILE(type, v) \ + zpl__std_files[type].fd.p = v; \ + zpl__std_files[type].ops = zpl_default_file_operations + ZPL__SET_STD_FILE(ZPL_FILE_STANDARD_INPUT, GetStdHandle(STD_INPUT_HANDLE)); + ZPL__SET_STD_FILE(ZPL_FILE_STANDARD_OUTPUT, GetStdHandle(STD_OUTPUT_HANDLE)); + ZPL__SET_STD_FILE(ZPL_FILE_STANDARD_ERROR, GetStdHandle(STD_ERROR_HANDLE)); + #undef ZPL__SET_STD_FILE + zpl__std_file_set = true; + } + return &zpl__std_files[std]; + } + + void zpl_file_connect_handle(zpl_file *file, void *handle) { + ZPL_ASSERT_NOT_NULL(file); + ZPL_ASSERT_NOT_NULL(handle); + + if (file->is_temp) + return; + + zpl_zero_item(file); + + file->fd.p = handle; + file->ops = zpl_default_file_operations; + } + + zpl_file_error zpl_file_truncate(zpl_file *f, zpl_i64 size) { + zpl_file_error err = ZPL_FILE_ERROR_NONE; + zpl_i64 prev_offset = zpl_file_tell(f); + zpl_file_seek(f, size); + if (!SetEndOfFile(f)) err = ZPL_FILE_ERROR_TRUNCATION_FAILURE; + zpl_file_seek(f, prev_offset); + return err; + } + + zpl_b32 zpl_fs_exists(char const *name) { + WIN32_FIND_DATAW data; + wchar_t *w_text; + void *handle; + zpl_b32 found = false; + zpl_allocator a = zpl_heap_allocator( ); + + w_text = zpl__alloc_utf8_to_ucs2(a, name, NULL); + if (w_text == NULL) { return false; } + handle = FindFirstFileW(w_text, &data); + zpl_free(a, w_text); + found = handle != INVALID_HANDLE_VALUE; + if (found) FindClose(handle); + return found; + } + + #else // POSIX + + zpl_file *zpl_file_get_standard(zpl_file_standard_type std) { + if (!zpl__std_file_set) { + #define ZPL__SET_STD_FILE(type, v) \ + zpl__std_files[type].fd.i = v; \ + zpl__std_files[type].ops = zpl_default_file_operations + ZPL__SET_STD_FILE(ZPL_FILE_STANDARD_INPUT, 0); + ZPL__SET_STD_FILE(ZPL_FILE_STANDARD_OUTPUT, 1); + ZPL__SET_STD_FILE(ZPL_FILE_STANDARD_ERROR, 2); + #undef ZPL__SET_STD_FILE + zpl__std_file_set = true; + } + return &zpl__std_files[std]; + } + + zpl_file_error zpl_file_truncate(zpl_file *f, zpl_i64 size) { + zpl_file_error err = ZPL_FILE_ERROR_NONE; + int i = ftruncate(f->fd.i, size); + if (i != 0) err = ZPL_FILE_ERROR_TRUNCATION_FAILURE; + return err; + } + + zpl_b32 zpl_fs_exists(char const *name) { return access(name, F_OK) != -1; } + + #endif + + zpl_i64 zpl_file_size(zpl_file *f) { + zpl_i64 size = 0; + zpl_i64 prev_offset = zpl_file_tell(f); + zpl_file_seek_to_end(f); + size = zpl_file_tell(f); + zpl_file_seek(f, prev_offset); + return size; + } + + zpl_file_error zpl_file_temp(zpl_file *file) { + zpl_zero_item(file); + FILE *fd = NULL; + + #if (defined(ZPL_SYSTEM_WINDOWS) && !defined(ZPL_SYSTEM_TINYC)) && !defined(ZPL_COMPILER_GCC) + errno_t errcode = tmpfile_s(&fd); + + if (errcode != 0) { + fd = NULL; + } + #else + fd = tmpfile(); + #endif + + if (fd == NULL) { return ZPL_FILE_ERROR_INVALID; } + + #if defined(ZPL_SYSTEM_WINDOWS) && !defined(ZPL_COMPILER_GCC) + file->fd.i = _get_osfhandle(_fileno(fd)); + #else + file->fd.i = fileno(fd); + #endif + file->ops = zpl_default_file_operations; + file->is_temp = true; + return ZPL_FILE_ERROR_NONE; + } + + zpl_file_contents zpl_file_read_contents(zpl_allocator a, zpl_b32 zero_terminate, char const *filepath) { + zpl_file_contents result = { 0 }; + zpl_file file = { 0 }; + + result.allocator = a; + + zpl_u8 entry_type = zpl_fs_get_type(filepath); + + /* ignore folders */ + if (entry_type == ZPL_DIR_TYPE_FOLDER) { + return result; + } + + if (zpl_file_open(&file, filepath) == ZPL_FILE_ERROR_NONE) { + zpl_isize file_size = cast(zpl_isize) zpl_file_size(&file); + if (file_size > 0) { + result.data = zpl_alloc(a, zero_terminate ? file_size + 1 : file_size); + result.size = file_size; + zpl_file_read_at(&file, result.data, result.size, 0); + if (zero_terminate) { + zpl_u8 *str = cast(zpl_u8 *) result.data; + str[file_size] = '\0'; + } + } + zpl_file_close(&file); + } + + return result; + } + + void zpl_file_free_contents(zpl_file_contents *fc) { + ZPL_ASSERT_NOT_NULL(fc->data); + zpl_free(fc->allocator, fc->data); + fc->data = NULL; + fc->size = 0; + } + + zpl_b32 zpl_file_write_contents(char const* filepath, void const* buffer, zpl_isize size, zpl_file_error* err) { + zpl_file f = { 0 }; + zpl_file_error open_err; + zpl_b32 write_ok; + open_err = zpl_file_open_mode(&f, ZPL_FILE_MODE_WRITE, filepath); + + if (open_err != ZPL_FILE_ERROR_NONE) + { + if (err) + *err = open_err; + + return false; + } + + write_ok = zpl_file_write(&f, buffer, size); + zpl_file_close(&f); + return write_ok; + } + + char *zpl_file_read_lines(zpl_allocator alloc, zpl_array(char *)*lines, char const *filename, zpl_b32 strip_whitespace) { + zpl_file f = { 0 }; + zpl_file_open(&f, filename); + zpl_isize fsize = (zpl_isize)zpl_file_size(&f); + + char *contents = (char *)zpl_alloc(alloc, fsize + 1); + zpl_file_read(&f, contents, fsize); + contents[fsize] = 0; + *lines = zpl_str_split_lines(alloc, contents, strip_whitespace); + zpl_file_close(&f); + + return contents; + } + + #if !defined(_WINDOWS_) && defined(ZPL_SYSTEM_WINDOWS) + ZPL_IMPORT DWORD WINAPI GetFullPathNameA(char const *lpFileName, DWORD nBufferLength, char *lpBuffer, char **lpFilePart); + ZPL_IMPORT DWORD WINAPI GetFullPathNameW(wchar_t const *lpFileName, DWORD nBufferLength, wchar_t *lpBuffer, wchar_t **lpFilePart); + #endif + + ZPL_END_C_DECLS + // file: source/core/file_stream.c + + + //////////////////////////////////////////////////////////////// + // + // Memory streaming + // + // + + ZPL_BEGIN_C_DECLS + + typedef struct { + zpl_u8 magic; + zpl_u8 *buf; //< zpl_array OR plain buffer if we can't write + zpl_isize cursor; + zpl_allocator alloc; + + zpl_file_stream_flags flags; + zpl_isize cap; + } zpl__memory_fd; + + #define ZPL__FILE_STREAM_FD_MAGIC 37 + + ZPL_DEF_INLINE zpl_file_descriptor zpl__file_stream_fd_make(zpl__memory_fd* d); + ZPL_DEF_INLINE zpl__memory_fd *zpl__file_stream_from_fd(zpl_file_descriptor fd); + + ZPL_IMPL_INLINE zpl_file_descriptor zpl__file_stream_fd_make(zpl__memory_fd* d) { + zpl_file_descriptor fd = {0}; + fd.p = (void*)d; + return fd; + } + + ZPL_IMPL_INLINE zpl__memory_fd *zpl__file_stream_from_fd(zpl_file_descriptor fd) { + zpl__memory_fd *d = (zpl__memory_fd*)fd.p; + ZPL_ASSERT(d->magic == ZPL__FILE_STREAM_FD_MAGIC); + return d; + } + + zpl_b8 zpl_file_stream_new(zpl_file* file, zpl_allocator allocator) { + ZPL_ASSERT_NOT_NULL(file); + zpl__memory_fd *d = (zpl__memory_fd*)zpl_alloc(allocator, zpl_size_of(zpl__memory_fd)); + if (!d) return false; + zpl_zero_item(file); + d->magic = ZPL__FILE_STREAM_FD_MAGIC; + d->alloc = allocator; + d->flags = ZPL_FILE_STREAM_CLONE_WRITABLE; + d->cap = 0; + if (!zpl_array_init(d->buf, allocator)) return false; + file->ops = zpl_memory_file_operations; + file->fd = zpl__file_stream_fd_make(d); + file->dir = NULL; + file->last_write_time = 0; + file->filename = NULL; + file->is_temp = true; + return true; + } + zpl_b8 zpl_file_stream_open(zpl_file* file, zpl_allocator allocator, zpl_u8 *buffer, zpl_isize size, zpl_file_stream_flags flags) { + ZPL_ASSERT_NOT_NULL(file); + zpl__memory_fd *d = (zpl__memory_fd*)zpl_alloc(allocator, zpl_size_of(zpl__memory_fd)); + if (!d) return false; + zpl_zero_item(file); + d->magic = ZPL__FILE_STREAM_FD_MAGIC; + d->alloc = allocator; + d->flags = flags; + if (d->flags & ZPL_FILE_STREAM_CLONE_WRITABLE) { + if (!zpl_array_init_reserve(d->buf, allocator, size)) return false; + zpl_memcopy(d->buf, buffer, size); + d->cap = zpl_array_count(d->buf) = size; + } else { + d->buf = buffer; + d->cap = size; + } + file->ops = zpl_memory_file_operations; + file->fd = zpl__file_stream_fd_make(d); + file->dir = NULL; + file->last_write_time = 0; + file->filename = NULL; + file->is_temp = true; + return true; + } + + zpl_u8 *zpl_file_stream_buf(zpl_file* file, zpl_isize *size) { + ZPL_ASSERT_NOT_NULL(file); + zpl__memory_fd *d = zpl__file_stream_from_fd(file->fd); + if (size) *size = d->cap; + return d->buf; + } + + zpl_internal ZPL_FILE_SEEK_PROC(zpl__memory_file_seek) { + zpl__memory_fd *d = zpl__file_stream_from_fd(fd); + zpl_isize buflen = d->cap; + + if (whence == ZPL_SEEK_WHENCE_BEGIN) + d->cursor = 0; + else if (whence == ZPL_SEEK_WHENCE_END) + d->cursor = buflen; + + d->cursor = zpl_max(0, zpl_clamp(d->cursor + offset, 0, buflen)); + if (new_offset) *new_offset = d->cursor; + return true; + } + + zpl_internal ZPL_FILE_READ_AT_PROC(zpl__memory_file_read) { + zpl_unused(stop_at_newline); + zpl__memory_fd *d = zpl__file_stream_from_fd(fd); + zpl_memcopy(buffer, d->buf + offset, size); + if (bytes_read) *bytes_read = size; + return true; + } + + zpl_internal ZPL_FILE_WRITE_AT_PROC(zpl__memory_file_write) { + zpl__memory_fd *d = zpl__file_stream_from_fd(fd); + if (!(d->flags & (ZPL_FILE_STREAM_CLONE_WRITABLE|ZPL_FILE_STREAM_WRITABLE))) + return false; + zpl_isize buflen = d->cap; + zpl_isize extralen = zpl_max(0, size-(buflen-offset)); + zpl_isize rwlen = size-extralen; + zpl_isize new_cap = buflen+extralen; + if (d->flags & ZPL_FILE_STREAM_CLONE_WRITABLE) { + if(zpl_array_capacity(d->buf) < new_cap) { + if (!zpl_array_grow(d->buf, (zpl_i64)(new_cap))) return false; + } + } + zpl_memcopy(d->buf + offset, buffer, rwlen); + + if ((d->flags & ZPL_FILE_STREAM_CLONE_WRITABLE) && extralen > 0) { + zpl_memcopy(d->buf + offset + rwlen, zpl_ptr_add_const(buffer, rwlen), extralen); + d->cap = zpl_array_count(d->buf) = new_cap; + } else { + extralen = 0; + } + + if (bytes_written) *bytes_written = (rwlen+extralen); + return true; + } + + zpl_internal ZPL_FILE_CLOSE_PROC(zpl__memory_file_close) { + zpl__memory_fd *d = zpl__file_stream_from_fd(fd); + zpl_allocator alloc = d->alloc; + if (d->flags & ZPL_FILE_STREAM_CLONE_WRITABLE) + zpl_array_free(d->buf); + zpl_free(alloc, d); + } + + zpl_file_operations const zpl_memory_file_operations = { zpl__memory_file_read, zpl__memory_file_write, + zpl__memory_file_seek, zpl__memory_file_close }; + + ZPL_END_C_DECLS + // file: source/core/file_misc.c + + + #if defined(ZPL_SYSTEM_UNIX) || defined(ZPL_SYSTEM_MACOS) + # include + #endif + + #if defined(ZPL_SYSTEM_UNIX) && !defined(ZPL_SYSTEM_FREEBSD) && !defined(ZPL_SYSTEM_OPENBSD) && !defined(ZPL_SYSTEM_CYGWIN) && !defined(ZPL_SYSTEM_EMSCRIPTEN) + # include + #endif + + #if defined(ZPL_SYSTEM_WINDOWS) + # include + # include + #endif + + #if defined(ZPL_SYSTEM_CYGWIN) + # include + # include + # include + #endif + + ZPL_BEGIN_C_DECLS + + + #if defined(ZPL_SYSTEM_WINDOWS) || defined(ZPL_SYSTEM_CYGWIN) + zpl_file_time zpl_fs_last_write_time(char const *filepath) { + ULARGE_INTEGER li = { 0 }; + FILETIME last_write_time = { 0 }; + WIN32_FILE_ATTRIBUTE_DATA data = { 0 }; + zpl_allocator a = zpl_heap_allocator( ); + + wchar_t *w_text = zpl__alloc_utf8_to_ucs2(a, filepath, NULL); + if (w_text == NULL) { return 0; } + if (GetFileAttributesExW(w_text, GetFileExInfoStandard, &data)) last_write_time = data.ftLastWriteTime; + + zpl_free(a, w_text); + + li.LowPart = last_write_time.dwLowDateTime; + li.HighPart = last_write_time.dwHighDateTime; + return cast(zpl_file_time) li.QuadPart; + } + + zpl_b32 zpl_fs_copy(char const *existing_filename, char const *new_filename, zpl_b32 fail_if_exists) { + zpl_b32 result = false; + zpl_allocator a = zpl_heap_allocator( ); + + wchar_t *w_old = zpl__alloc_utf8_to_ucs2(a, existing_filename, NULL); + if (w_old == NULL) { return false; } + + wchar_t *w_new = zpl__alloc_utf8_to_ucs2(a, new_filename, NULL); + if (w_new != NULL) { result = CopyFileW(w_old, w_new, fail_if_exists); } + + zpl_free(a, w_old); + zpl_free(a, w_new); + return result; + } + + zpl_b32 zpl_fs_move(char const *existing_filename, char const *new_filename) { + zpl_b32 result = false; + zpl_allocator a = zpl_heap_allocator( ); + + wchar_t *w_old = zpl__alloc_utf8_to_ucs2(a, existing_filename, NULL); + if (w_old == NULL) { return false; } + + wchar_t *w_new = zpl__alloc_utf8_to_ucs2(a, new_filename, NULL); + if (w_new != NULL) { result = MoveFileW(w_old, w_new); } + + zpl_free(a, w_old); + zpl_free(a, w_new); + return result; + } + + zpl_b32 zpl_fs_remove(char const *filename) { + zpl_b32 result = false; + zpl_allocator a = zpl_heap_allocator( ); + + wchar_t *w_filename = zpl__alloc_utf8_to_ucs2(a, filename, NULL); + if (w_filename == NULL) { return false; } + + result = DeleteFileW(w_filename); + + zpl_free(a, w_filename); + return result; + } + + #else + + zpl_file_time zpl_fs_last_write_time(char const *filepath) { + time_t result = 0; + struct stat file_stat; + + if (stat(filepath, &file_stat)) result = file_stat.st_mtime; + + return cast(zpl_file_time) result; + } + + # if defined(ZPL_SYSTEM_FREEBSD) + # include + # include + # include + # endif + + + zpl_b32 zpl_fs_copy(char const *existing_filename, char const *new_filename, zpl_b32 fail_if_exists) { + zpl_unused(fail_if_exists); + # if defined(ZPL_SYSTEM_OSX) + return copyfile(existing_filename, new_filename, NULL, COPYFILE_DATA) == 0; + # elif defined(ZPL_SYSTEM_OPENBSD) + ZPL_NOT_IMPLEMENTED; + return 0; + # elif defined(ZPL_SYSTEM_EMSCRIPTEN) + ZPL_NOT_IMPLEMENTED; + return 0; + # else + int existing_fd = open(existing_filename, O_RDONLY, 0); + struct stat stat_existing; + fstat(existing_fd, &stat_existing); + + zpl_isize size; + int new_fd = open(new_filename, O_WRONLY | O_CREAT, stat_existing.st_mode); + + # if defined(ZPL_SYSTEM_FREEBSD) + size = sendfile(new_fd, existing_fd, 0, stat_existing.st_size, NULL, 0, 0); + # else + size = sendfile(new_fd, existing_fd, 0, stat_existing.st_size); + # endif + + close(new_fd); + close(existing_fd); + + return size == stat_existing.st_size; + # endif + } + + zpl_b32 zpl_fs_move(char const *existing_filename, char const *new_filename) { + if (link(existing_filename, new_filename) == 0) { return (unlink(existing_filename) != -1); } + return false; + } + + zpl_b32 zpl_fs_remove(char const *filename) { + # if defined(ZPL_SYSTEM_OSX) || defined(ZPL_SYSTEM_EMSCRIPTEN) + return (unlink(filename) != -1); + # else + return (remove(filename) == 0); + # endif + } + + #endif + + char *zpl_path_get_full_name(zpl_allocator a, char const *path) { + #if defined(ZPL_SYSTEM_WINDOWS) + wchar_t *w_path = NULL; + wchar_t *w_fullpath = NULL; + zpl_isize w_len = 0; + zpl_isize new_len = 0; + zpl_isize new_len1 = 0; + char *new_path = 0; + + w_path = zpl__alloc_utf8_to_ucs2(zpl_heap_allocator( ), path, NULL); + if (w_path == NULL) { return NULL; } + + w_len = GetFullPathNameW(w_path, 0, NULL, NULL); + if (w_len == 0) { return NULL; } + + w_fullpath = zpl_alloc_array(zpl_heap_allocator( ), wchar_t, w_len + 1); + GetFullPathNameW(w_path, cast(int) w_len, w_fullpath, NULL); + w_fullpath[w_len] = 0; + + zpl_free(zpl_heap_allocator( ), w_path); + + new_len = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, w_fullpath, cast(int) w_len, NULL, 0, NULL, NULL); + + if (new_len == 0) { + zpl_free(zpl_heap_allocator( ), w_fullpath); + return NULL; + } + + new_path = zpl_alloc_array(a, char, new_len); + new_len1 = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, w_fullpath, cast(int) w_len, new_path, + cast(int) new_len, NULL, NULL); + + if (new_len1 == 0) { + zpl_free(zpl_heap_allocator( ), w_fullpath); + zpl_free(a, new_path); + return NULL; + } + + new_path[new_len] = 0; + return new_path; + #else + char *p, *result, *fullpath = NULL; + zpl_isize len; + p = realpath(path, NULL); + fullpath = p; + if (p == NULL) { + // NOTE(bill): File does not exist + fullpath = cast(char *) path; + } + + len = zpl_strlen(fullpath); + + result = zpl_alloc_array(a, char, len + 1); + zpl_memmove(result, fullpath, len); + result[len] = 0; + zpl_free(a, p); + + return result; + #endif + } + + zpl_file_error zpl_path_mkdir(char const *path, zpl_i32 mode) { + zpl_i32 error = 0; + #if defined(ZPL_SYSTEM_WINDOWS) + error = _wmkdir((const wchar_t *)zpl_utf8_to_ucs2_buf((const zpl_u8 *)path)); + #else + error = mkdir(path, (mode_t)mode); + #endif + + if (error == 0) { return ZPL_FILE_ERROR_NONE; } + + switch (errno) { + case EPERM: + case EACCES: return ZPL_FILE_ERROR_PERMISSION; + case EEXIST: return ZPL_FILE_ERROR_EXISTS; + case ENAMETOOLONG: return ZPL_FILE_ERROR_NAME_TOO_LONG; + } + + return ZPL_FILE_ERROR_UNKNOWN; + } + + zpl_isize zpl_path_mkdir_recursive(char const *path, zpl_i32 mode) { + char tmp[ZPL_MAX_PATH] = {0}; + char *p = 0; + zpl_isize len = zpl_strlen(path); + + if (len > zpl_size_of(tmp)-1) { + return -1; + } + zpl_strcpy(tmp, path); + zpl_path_fix_slashes(tmp); + for (p = tmp + 1; *p; p++) { + if (*p == ZPL_PATH_SEPARATOR) { + *p = 0; + zpl_path_mkdir(tmp, mode); + *p = ZPL_PATH_SEPARATOR; + } + } + zpl_path_mkdir(tmp, mode); + return 0; + } + + zpl_file_error zpl_path_rmdir(char const *path) { + zpl_i32 error = 0; + #if defined(ZPL_SYSTEM_WINDOWS) + error = _wrmdir((const wchar_t *)zpl_utf8_to_ucs2_buf((const zpl_u8 *)path)); + #else + error = rmdir(path); + #endif + + if (error == 0) { return ZPL_FILE_ERROR_NONE; } + + switch (errno) { + case EPERM: + case EACCES: return ZPL_FILE_ERROR_PERMISSION; + case ENOENT: return ZPL_FILE_ERROR_NOT_EXISTS; + case ENOTEMPTY: return ZPL_FILE_ERROR_NOT_EMPTY; + case ENAMETOOLONG: return ZPL_FILE_ERROR_NAME_TOO_LONG; + } + + return ZPL_FILE_ERROR_UNKNOWN; + } + + void zpl__file_direntry(zpl_allocator alloc, char const *dirname, zpl_string *output, zpl_b32 recurse) { + #if defined(ZPL_SYSTEM_UNIX) || defined(ZPL_SYSTEM_OSX) + DIR *d, *cd; + struct dirent *dir; + d = opendir(dirname); + + if (d) { + while ((dir = readdir(d))) { + if (dir == 0) break; + if (!zpl_strncmp(dir->d_name, "..", 2)) continue; + if (dir->d_name[0] == '.' && dir->d_name[1] == 0) continue; + + zpl_string dirpath = zpl_string_make(alloc, dirname); + dirpath = zpl_string_appendc(dirpath, "/"); + dirpath = zpl_string_appendc(dirpath, dir->d_name); + + *output = zpl_string_appendc(*output, dirpath); + *output = zpl_string_appendc(*output, "\n"); + + if (recurse && (cd = opendir(dirpath)) != NULL && dir->d_type == DT_DIR) { zpl__file_direntry(alloc, dirpath, output, recurse); } + zpl_string_free(dirpath); + } + } + #elif defined(ZPL_SYSTEM_WINDOWS) + zpl_usize length = zpl_strlen(dirname); + struct _wfinddata_t data; + zpl_intptr findhandle; + + char directory[MAX_PATH] = { 0 }; + zpl_strncpy(directory, dirname, length); + + // keeping it native + for (zpl_usize i = 0; i < length; i++) { + if (directory[i] == '/') directory[i] = '\\'; + } + + // remove trailing slashses + if (directory[length - 1] == '\\') { directory[length - 1] = '\0'; } + + // attach search pattern + zpl_string findpath = zpl_string_make(alloc, directory); + findpath = zpl_string_appendc(findpath, "\\"); + findpath = zpl_string_appendc(findpath, "*"); + + findhandle = _wfindfirst((const wchar_t *)zpl_utf8_to_ucs2_buf((const zpl_u8 *)findpath), &data); + zpl_string_free(findpath); + + if (findhandle != -1) { + do { + char *filename = (char *)zpl_ucs2_to_utf8_buf((const zpl_u16 *)data.name); + if (!zpl_strncmp(filename, "..", 2)) continue; + if (filename[0] == '.' && filename[1] == 0) continue; + + zpl_string dirpath = zpl_string_make(alloc, directory); + dirpath = zpl_string_appendc(dirpath, "\\"); + dirpath = zpl_string_appendc(dirpath, filename); + DWORD attrs = GetFileAttributesW((const wchar_t *)zpl_utf8_to_ucs2_buf((const zpl_u8 *)dirpath)); + + *output = zpl_string_appendc(*output, dirpath); + *output = zpl_string_appendc(*output, "\n"); + + if (recurse && (data.attrib & _A_SUBDIR) && !(attrs & FILE_ATTRIBUTE_REPARSE_POINT)) { zpl__file_direntry(alloc, dirpath, output, recurse); } + + zpl_string_free(dirpath); + } while (_wfindnext(findhandle, &data) != -1); + _findclose(findhandle); + } + #else + // TODO: Implement other OSes + #endif + } + + zpl_string zpl_path_dirlist(zpl_allocator alloc, char const *dirname, zpl_b32 recurse) { + zpl_string buf = zpl_string_make_reserve(alloc, 4); + zpl__file_direntry(alloc, dirname, &buf, recurse); + return buf; + } + + void zpl_dirinfo_init(zpl_dir_info *dir, char const *path) { + ZPL_ASSERT_NOT_NULL(dir); + + zpl_dir_info dir_ = {0}; + *dir = dir_; + dir->fullpath = (char const*)zpl_malloc(zpl_strlen(path)); + zpl_strcpy((char *)dir->fullpath, path); + + + zpl_string dirlist = zpl_path_dirlist(zpl_heap(), path, false); + char **files=zpl_str_split_lines(zpl_heap(), dirlist, false); + dir->filenames = files; + dir->buf = dirlist; + + zpl_array_init(dir->entries, zpl_heap()); + + for (zpl_i32 i=0; ientries, entry); + } + } + + zpl_internal void zpl__dirinfo_free_entry(zpl_dir_entry *entry) { + if (entry->dir_info) { + zpl_dirinfo_free(entry->dir_info); + zpl_mfree(entry->dir_info); + entry->dir_info = NULL; + } + } + + void zpl_dirinfo_free(zpl_dir_info *dir) { + ZPL_ASSERT_NOT_NULL(dir); + + for (zpl_isize i = 0; i < zpl_array_count(dir->entries); ++i) { + zpl__dirinfo_free_entry(dir->entries + i); + } + + zpl_array_free(dir->entries); + zpl_array_free(dir->filenames); + zpl_string_free(dir->buf); + zpl_mfree((void *)dir->fullpath); + } + + + zpl_u8 zpl_fs_get_type(char const *path) { + #ifdef ZPL_SYSTEM_WINDOWS + DWORD attrs = GetFileAttributesW((const wchar_t *)zpl_utf8_to_ucs2_buf((const zpl_u8 *)path)); + + if (attrs == INVALID_FILE_ATTRIBUTES) { + return ZPL_DIR_TYPE_UNKNOWN; + } + + if (attrs & FILE_ATTRIBUTE_DIRECTORY) + return ZPL_DIR_TYPE_FOLDER; + else + return ZPL_DIR_TYPE_FILE; + + #else + struct stat s; + if( stat(path,&s) == 0 ) + { + if(s.st_mode & S_IFDIR) + return ZPL_DIR_TYPE_FOLDER; + else + return ZPL_DIR_TYPE_FILE; + } + #endif + + return ZPL_DIR_TYPE_UNKNOWN; + } + + void zpl_dirinfo_step(zpl_dir_entry *entry) { + if (entry->dir_info) { + zpl__dirinfo_free_entry(entry); + } + + entry->dir_info = (zpl_dir_info *)zpl_malloc(sizeof(zpl_dir_info)); + zpl_dir_info dir_ = {0}; + *entry->dir_info = dir_; + + zpl_local_persist char buf[128] = {0}; + char const *path = entry->filename; + + if (entry->type != ZPL_DIR_TYPE_FOLDER) { + zpl_path_fix_slashes((char *)path); + char const* slash = zpl_char_last_occurence(path, ZPL_PATH_SEPARATOR); + zpl_strncpy(buf, path, slash-path); + path = buf; + } + + zpl_dirinfo_init(entry->dir_info, path); + } + + void zpl_file_dirinfo_refresh(zpl_file *file) { + if (file->is_temp) + return; + + if (file->dir) { + zpl__dirinfo_free_entry(file->dir); + zpl_mfree(file->dir); + file->dir = NULL; + } + + file->dir = (zpl_dir_entry *)zpl_malloc(sizeof(zpl_dir_entry)); + zpl_dir_entry dir_ = {0}; + *file->dir = dir_; + file->dir->filename = file->filename; + file->dir->type = ZPL_DIR_TYPE_FILE; + + zpl_dirinfo_step(file->dir); + } + + void zpl_path_fix_slashes(char *path) { + #ifdef ZPL_SYSTEM_WINDOWS + char *p = path; + + while (*p != '\0') { + if (*p == '/') + *p = '\\'; + + ++p; + } + #endif + } + + ZPL_END_C_DECLS + // file: source/core/file_tar.c + + + typedef struct { + char name[100]; + char mode[8]; + char owner[8]; + char group[8]; + char size[12]; + char mtime[12]; + char checksum[8]; + char type; + char linkname[100]; + char _padding[255]; + } zpl__tar_header; + + zpl_internal zpl_usize zpl__tar_checksum(zpl__tar_header *hr) { + zpl_usize i; + zpl_usize res = 256; + zpl_u8 *p = cast(zpl_u8*)(hr); + for (i = 0; i < cast(zpl_usize)zpl_offset_of(zpl__tar_header, checksum); i++) + res += p[i]; + for (i = cast(zpl_usize)zpl_offset_of(zpl__tar_header, type); i < cast(zpl_usize)zpl_size_of(zpl__tar_header); i++) + res += p[i]; + return res; + } + + zpl_internal zpl_b32 zpl__tar_write_null(zpl_file *archive, zpl_isize cnt) { + char *out = zpl_bprintf("%*r", cnt, '\0'); + if (!zpl_file_write(archive, out, cnt)) + return 0; + return 1; + } + + zpl_isize zpl_tar_pack(zpl_file *archive, char const **paths, zpl_isize paths_len) { + ZPL_ASSERT_NOT_NULL(archive); + ZPL_ASSERT_NOT_NULL(paths); + + for (zpl_isize i = 0; i < paths_len; i++) { + ZPL_ASSERT_NOT_NULL(paths[i]); + zpl__tar_header hr = {0}; + zpl_file file; + zpl_file_error ferr = zpl_file_open_mode(&file, ZPL_FILE_MODE_READ, paths[i]); + if (ferr == ZPL_FILE_ERROR_NOT_EXISTS) { + return -(ZPL_TAR_ERROR_FILE_NOT_FOUND); + } else if (ferr != ZPL_FILE_ERROR_NONE) { + return -(ZPL_TAR_ERROR_IO_ERROR); + } + + zpl_i64 file_size = zpl_file_size(&file); + zpl_snprintf(hr.name, 12, "%s", paths[i]); + zpl_snprintf(hr.size, 12, "%o", file_size); + zpl_snprintf(hr.mode, 8, "%o", 0664); + zpl_snprintf(hr.mtime, 12, "%o", zpl_fs_last_write_time(paths[i])); + hr.type = ZPL_TAR_TYPE_REGULAR; + zpl_snprintf(hr.checksum, 8, "%o", zpl__tar_checksum(&hr)); + + zpl_file_write(archive, cast(void*)(&hr), zpl_size_of(zpl__tar_header)); + + // write data + { + zpl_i64 remaining_data = file_size; + zpl_i64 total_data = zpl_align_forward_i64(remaining_data, 512); + zpl_i64 padding = (total_data-file_size); + char buf[4096] = {0}; + zpl_i64 pos = 0; + zpl_isize bytes_read = 0; + do { + if (!zpl_file_read_at_check(&file, buf, 4096, pos, &bytes_read)) { + zpl_file_close(&file); + return -(ZPL_TAR_ERROR_IO_ERROR); + } else if (bytes_read == 0) { + break; + } + + zpl_file_write(archive, buf, bytes_read); + pos += bytes_read; + remaining_data -= bytes_read; + } + while (remaining_data > 0); + + if (padding > 0) { + if (!zpl__tar_write_null(archive, padding)) { + zpl_file_close(&file); + return -(ZPL_TAR_ERROR_IO_ERROR); + } + } + } + + zpl_file_close(&file); + } + + if (!zpl__tar_write_null(archive, zpl_size_of(zpl__tar_header) * 2)) { + return -(ZPL_TAR_ERROR_IO_ERROR); + } + + return 0; + } + + zpl_isize zpl_tar_pack_dir(zpl_file *archive, char const *path, zpl_allocator alloc) { + zpl_string filelst = zpl_path_dirlist(alloc, path, true); + char const **files = cast(char const**)zpl_str_split_lines(alloc, filelst, false); + zpl_isize err = zpl_tar_pack(archive, files, zpl_array_count(files)); + zpl_string_free(filelst); + zpl_array_free(files); + return err; + } + + zpl_isize zpl_tar_unpack(zpl_file *archive, zpl_tar_unpack_proc *unpack_proc, void *user_data) { + ZPL_ASSERT_NOT_NULL(archive); + ZPL_ASSERT_NOT_NULL(unpack_proc); + + zpl_i64 pos = zpl_file_tell(archive); + zpl__tar_header hr = {0}; + zpl_isize err = ZPL_TAR_ERROR_NONE; + + do { + if (!zpl_file_read(archive, cast(void*)&hr, zpl_size_of(hr))) { + err = ZPL_TAR_ERROR_IO_ERROR; + break; + } + else if (*hr.checksum == 0) { + break; + } + pos = zpl_file_tell(archive); + + zpl_tar_record rec = {0}; + rec.type = hr.type; + rec.path = hr.name; + rec.offset = pos; + rec.length = zpl_str_to_i64(hr.size, 0, 8); + rec.error = ZPL_TAR_ERROR_NONE; + + zpl_usize checksum1 = cast(zpl_usize)(zpl_str_to_i64(hr.checksum, 0, 8)); + zpl_usize checksum2 = zpl__tar_checksum(&hr); + rec.error = (checksum1 != checksum2) ? cast(zpl_isize)ZPL_TAR_ERROR_BAD_CHECKSUM : rec.error; + + rec.error = unpack_proc(archive, &rec, user_data); + + if (rec.error > 0) { + err = ZPL_TAR_ERROR_INTERRUPTED; + break; + } + + /* tar rounds files to 512 byte boundary */ + zpl_file_seek(archive, pos + zpl_align_forward_i64(rec.length, 512)); + } + while(err == ZPL_TAR_ERROR_NONE); + + return -(err); + } + + ZPL_TAR_UNPACK_PROC(zpl_tar_default_list_file) { + (void)archive; + (void)user_data; + if (file->error != ZPL_TAR_ERROR_NONE) + return 0; /* skip file */ + + if (file->type != ZPL_TAR_TYPE_REGULAR) + return 0; /* we only care about regular files */ + + /* proceed as usual */ + zpl_printf("name: %s, offset: %d, length: %d\n", file->path, file->offset, file->length); + return 0; + } + + ZPL_TAR_UNPACK_PROC(zpl_tar_default_unpack_file) { + if (file->error != ZPL_TAR_ERROR_NONE) + return 0; /* skip file */ + + if (file->type != ZPL_TAR_TYPE_REGULAR) + return 0; /* we only care about regular files */ + + if (!zpl_strncmp(file->path, "..", 2)) + return 0; + + char tmp[ZPL_MAX_PATH] = {0}; + char *base_path = cast(char*)user_data; + zpl_isize base_len = zpl_strlen(base_path); + zpl_isize len = zpl_strlen(file->path); + ZPL_ASSERT(base_len+len-2 < ZPL_MAX_PATH); /* todo: account for missing leading path sep */ + + zpl_strcpy(tmp, base_path); + zpl_path_fix_slashes(tmp); /* todo: need to do twice as base_path is checked before concat */ + + if (*tmp && tmp[base_len-1] != ZPL_PATH_SEPARATOR) { + char sep[2] = {ZPL_PATH_SEPARATOR, 0}; + zpl_strcat(tmp, sep); + } + zpl_strcat(tmp, file->path); + zpl_path_fix_slashes(tmp); + + const char *last_slash = zpl_char_last_occurence(tmp, ZPL_PATH_SEPARATOR); + + if (last_slash) { + zpl_isize i = cast(zpl_isize)(last_slash-tmp); + tmp[i] = 0; + zpl_path_mkdir_recursive(tmp, 0755); + tmp[i] = ZPL_PATH_SEPARATOR; + } + + zpl_file f; + zpl_file_create(&f, tmp); + { + char buf[4096] = {0}; + zpl_isize remaining_data = file->length; + zpl_isize bytes_read = 0; + zpl_i64 pos = file->offset; + do { + if (!zpl_file_read_at_check(archive, buf, zpl_min(4096, remaining_data), pos, &bytes_read)) { + zpl_file_close(&f); + return 1; + } else if (bytes_read == 0) { + break; + } + + zpl_file_write(&f, buf, bytes_read); + pos += bytes_read; + remaining_data -= bytes_read; + } + while (remaining_data > 0); + } + zpl_file_close(&f); + return 0; + } + // file: source/core/print.c + + + ZPL_BEGIN_C_DECLS + + zpl_isize zpl_printf_va(char const *fmt, va_list va) { + return zpl_fprintf_va(zpl_file_get_standard(ZPL_FILE_STANDARD_OUTPUT), fmt, va); + } + + zpl_isize zpl_printf_err_va(char const *fmt, va_list va) { + return zpl_fprintf_va(zpl_file_get_standard(ZPL_FILE_STANDARD_ERROR), fmt, va); + } + + zpl_isize zpl_fprintf_va(struct zpl_file *f, char const *fmt, va_list va) { + zpl_local_persist zpl_thread_local char buf[ZPL_PRINTF_MAXLEN]; + zpl_isize len = zpl_snprintf_va(buf, zpl_size_of(buf), fmt, va); + zpl_b32 res = zpl_file_write(f, buf, len - 1); // NOTE: prevent extra whitespace + return res ? len : -1; + } + + char *zpl_bprintf_va(char const *fmt, va_list va) { + zpl_local_persist zpl_thread_local char buffer[ZPL_PRINTF_MAXLEN]; + zpl_snprintf_va(buffer, zpl_size_of(buffer), fmt, va); + return buffer; + } + + zpl_isize zpl_asprintf_va(zpl_allocator allocator, char **buffer, char const *fmt, va_list va) { + zpl_local_persist zpl_thread_local char tmp[ZPL_PRINTF_MAXLEN]; + ZPL_ASSERT_NOT_NULL(buffer); + zpl_isize res; + res = zpl_snprintf_va(tmp, zpl_size_of(tmp), fmt, va); + *buffer = zpl_alloc_str(allocator, tmp); + return res; + } + + zpl_isize zpl_printf(char const *fmt, ...) { + zpl_isize res; + va_list va; + va_start(va, fmt); + res = zpl_printf_va(fmt, va); + va_end(va); + return res; + } + + zpl_isize zpl_printf_err(char const *fmt, ...) { + zpl_isize res; + va_list va; + va_start(va, fmt); + res = zpl_printf_err_va(fmt, va); + va_end(va); + return res; + } + + zpl_isize zpl_fprintf(struct zpl_file *f, char const *fmt, ...) { + zpl_isize res; + va_list va; + va_start(va, fmt); + res = zpl_fprintf_va(f, fmt, va); + va_end(va); + return res; + } + + char *zpl_bprintf(char const *fmt, ...) { + va_list va; + char *str; + va_start(va, fmt); + str = zpl_bprintf_va(fmt, va); + va_end(va); + return str; + } + + zpl_isize zpl_asprintf(zpl_allocator allocator, char **buffer, char const *fmt, ...) { + zpl_isize res; + va_list va; + va_start(va, fmt); + res = zpl_asprintf_va(allocator, buffer, fmt, va); + va_end(va); + return res; + } + + zpl_isize zpl_snprintf(char *str, zpl_isize n, char const *fmt, ...) { + zpl_isize res; + va_list va; + va_start(va, fmt); + res = zpl_snprintf_va(str, n, fmt, va); + va_end(va); + return res; + } + + + enum { + ZPL_FMT_MINUS = ZPL_BIT(0), + ZPL_FMT_PLUS = ZPL_BIT(1), + ZPL_FMT_ALT = ZPL_BIT(2), + ZPL_FMT_SPACE = ZPL_BIT(3), + ZPL_FMT_ZERO = ZPL_BIT(4), + + ZPL_FMT_CHAR = ZPL_BIT(5), + ZPL_FMT_SHORT = ZPL_BIT(6), + ZPL_FMT_INT = ZPL_BIT(7), + ZPL_FMT_LONG = ZPL_BIT(8), + ZPL_FMT_LLONG = ZPL_BIT(9), + ZPL_FMT_SIZE = ZPL_BIT(10), + ZPL_FMT_INTPTR = ZPL_BIT(11), + + ZPL_FMT_UNSIGNED = ZPL_BIT(12), + ZPL_FMT_LOWER = ZPL_BIT(13), + ZPL_FMT_UPPER = ZPL_BIT(14), + ZPL_FMT_WIDTH = ZPL_BIT(15), + + ZPL_FMT_DONE = ZPL_BIT(30), + + ZPL_FMT_INTS = + ZPL_FMT_CHAR | ZPL_FMT_SHORT | ZPL_FMT_INT | + ZPL_FMT_LONG | ZPL_FMT_LLONG | ZPL_FMT_SIZE | ZPL_FMT_INTPTR + }; + + typedef struct { + zpl_i32 base; + zpl_i32 flags; + zpl_i32 width; + zpl_i32 precision; + } zpl__format_info; + + zpl_internal zpl_isize zpl__print_string(char *text, zpl_isize max_len, zpl__format_info *info, char const *str) { + zpl_isize res = 0, len = 0; + zpl_isize remaining = max_len; + char *begin = text; + + if (str == NULL && max_len >= 6) { + res += zpl_strlcpy(text, "(null)", 6); + return res; + } + + if (info && info->precision >= 0) + len = zpl_strnlen(str, info->precision); + else + len = zpl_strlen(str); + + if (info && (info->width == 0 && info->flags & ZPL_FMT_WIDTH)) { + return res; + } + + if (info && (info->width == 0 || info->flags & ZPL_FMT_MINUS)) { + if (info->precision > 0) len = info->precision < len ? info->precision : len; + if (res+len > max_len) return res; + res += zpl_strlcpy(text, str, len); + text += res; + + if (info->width > res) { + zpl_isize padding = info->width - len; + + char pad = (info->flags & ZPL_FMT_ZERO) ? '0' : ' '; + while (padding-- > 0 && remaining-- > 0) *text++ = pad, res++; + } + } else { + if (info && (info->width > res)) { + zpl_isize padding = info->width - len; + char pad = (info->flags & ZPL_FMT_ZERO) ? '0' : ' '; + while (padding-- > 0 && remaining-- > 0) *text++ = pad, res++; + } + + if (res+len > max_len) return res; + res += zpl_strlcpy(text, str, len); + } + + if (info) { + if (info->flags & ZPL_FMT_UPPER) + zpl_str_to_upper(begin); + else if (info->flags & ZPL_FMT_LOWER) + zpl_str_to_lower(begin); + } + + return res; + } + + zpl_internal zpl_isize zpl__print_char(char *text, zpl_isize max_len, zpl__format_info *info, char arg) { + char str[2] = ""; + str[0] = arg; + return zpl__print_string(text, max_len, info, str); + } + + zpl_internal zpl_isize zpl__print_repeated_char(char *text, zpl_isize max_len, zpl__format_info *info, char arg) { + zpl_isize res = 0; + zpl_i32 rem = (info) ? (info->width > 0) ? info->width : 1 : 1; + res = rem; + while (rem-- > 0) *text++ = arg; + + return res; + } + + zpl_internal zpl_isize zpl__print_i64(char *text, zpl_isize max_len, zpl__format_info *info, zpl_i64 value) { + char num[130]; + zpl_i64_to_str(value, num, info ? info->base : 10); + return zpl__print_string(text, max_len, info, num); + } + + zpl_internal zpl_isize zpl__print_u64(char *text, zpl_isize max_len, zpl__format_info *info, zpl_u64 value) { + char num[130]; + zpl_u64_to_str(value, num, info ? info->base : 10); + return zpl__print_string(text, max_len, info, num); + } + + zpl_internal zpl_isize zpl__print_f64(char *text, zpl_isize max_len, zpl__format_info *info, zpl_b32 is_hexadecimal, zpl_f64 arg) { + // TODO: Handle exponent notation + zpl_isize width, len, remaining = max_len; + char *text_begin = text; + + if (arg) { + zpl_u64 value; + if (arg < 0) { + if (remaining > 1) *text = '-', remaining--; + text++; + arg = -arg; + } else if (info->flags & ZPL_FMT_MINUS) { + if (remaining > 1) *text = '+', remaining--; + text++; + } + + value = cast(zpl_u64) arg; + len = zpl__print_u64(text, remaining, NULL, value); + text += len; + + if (len >= remaining) + remaining = zpl_min(remaining, 1); + else + remaining -= len; + arg -= value; + + if (info->precision < 0) info->precision = 6; + + if ((info->flags & ZPL_FMT_ALT) || info->precision > 0) { + zpl_i64 mult = 10; + if (remaining > 1) *text = '.', remaining--; + text++; + while (info->precision-- > 0) { + value = cast(zpl_u64)(arg * mult); + len = zpl__print_u64(text, remaining, NULL, value); + text += len; + if (len >= remaining) + remaining = zpl_min(remaining, 1); + else + remaining -= len; + arg -= cast(zpl_f64) value / mult; + mult *= 10; + } + } + } else { + if (remaining > 1) *text = '0', remaining--; + text++; + if (info->flags & ZPL_FMT_ALT) { + if (remaining > 1) *text = '.', remaining--; + text++; + } + } + + width = info->width - (text - text_begin); + if (width > 0) { + char fill = (info->flags & ZPL_FMT_ZERO) ? '0' : ' '; + char *end = text + remaining - 1; + len = (text - text_begin); + + for (len = (text - text_begin); len--;) { + if ((text_begin + len + width) < end) *(text_begin + len + width) = *(text_begin + len); + } + + len = width; + text += len; + if (len >= remaining) + remaining = zpl_min(remaining, 1); + else + remaining -= len; + + while (len--) { + if (text_begin + len < end) text_begin[len] = fill; + } + } + + return (text - text_begin); + } + + ZPL_NEVER_INLINE zpl_isize zpl_snprintf_va(char *text, zpl_isize max_len, char const *fmt, va_list va) { + char const *text_begin = text; + zpl_isize remaining = max_len, res; + + while (*fmt) { + zpl__format_info info = { 0 }; + zpl_isize len = 0; + info.precision = -1; + + while (*fmt && *fmt != '%' && remaining) *text++ = *fmt++; + + if (*fmt == '%') { + do { + switch (*++fmt) { + case '-': {info.flags |= ZPL_FMT_MINUS; break;} + case '+': {info.flags |= ZPL_FMT_PLUS; break;} + case '#': {info.flags |= ZPL_FMT_ALT; break;} + case ' ': {info.flags |= ZPL_FMT_SPACE; break;} + case '0': {info.flags |= (ZPL_FMT_ZERO|ZPL_FMT_WIDTH); break;} + default: {info.flags |= ZPL_FMT_DONE; break;} + } + } while (!(info.flags & ZPL_FMT_DONE)); + } + + // NOTE: Optional Width + if (*fmt == '*') { + int width = va_arg(va, int); + if (width < 0) { + info.flags |= ZPL_FMT_MINUS; + info.width = -width; + } else { + info.width = width; + } + info.flags |= ZPL_FMT_WIDTH; + fmt++; + } else { + info.width = cast(zpl_i32) zpl_str_to_i64(fmt, cast(char **) & fmt, 10); + if (info.width != 0) { + info.flags |= ZPL_FMT_WIDTH; + } + } + + // NOTE: Optional Precision + if (*fmt == '.') { + fmt++; + if (*fmt == '*') { + info.precision = va_arg(va, int); + fmt++; + } else { + info.precision = cast(zpl_i32) zpl_str_to_i64(fmt, cast(char **) & fmt, 10); + } + info.flags &= ~ZPL_FMT_ZERO; + } + + switch (*fmt++) { + case 'h': + if (*fmt == 'h') { // hh => char + info.flags |= ZPL_FMT_CHAR; + fmt++; + } else { // h => short + info.flags |= ZPL_FMT_SHORT; + } + break; + + case 'l': + if (*fmt == 'l') { // ll => long long + info.flags |= ZPL_FMT_LLONG; + fmt++; + } else { // l => long + info.flags |= ZPL_FMT_LONG; + } + break; + + break; + + case 'z': // NOTE: zpl_usize + info.flags |= ZPL_FMT_UNSIGNED; + // fallthrough + case 't': // NOTE: zpl_isize + info.flags |= ZPL_FMT_SIZE; + break; + + default: fmt--; break; + } + + switch (*fmt) { + case 'u': + info.flags |= ZPL_FMT_UNSIGNED; + // fallthrough + case 'd': + case 'i': info.base = 10; break; + + case 'o': info.base = 8; break; + + case 'x': + info.base = 16; + info.flags |= (ZPL_FMT_UNSIGNED | ZPL_FMT_LOWER); + break; + + case 'X': + info.base = 16; + info.flags |= (ZPL_FMT_UNSIGNED | ZPL_FMT_UPPER); + break; + + case 'f': + case 'F': + case 'g': + case 'G': len = zpl__print_f64(text, remaining, &info, 0, va_arg(va, zpl_f64)); break; + + case 'a': + case 'A': + len = zpl__print_f64(text, remaining, &info, 1, va_arg(va, zpl_f64)); break; + + case 'c': len = zpl__print_char(text, remaining, &info, cast(char) va_arg(va, int)); break; + + case 's': len = zpl__print_string(text, remaining, &info, va_arg(va, char *)); break; + + case 'r': len = zpl__print_repeated_char(text, remaining, &info, va_arg(va, int)); break; + + case 'p': + info.base = 16; + info.flags |= (ZPL_FMT_LOWER | ZPL_FMT_UNSIGNED | ZPL_FMT_ALT | ZPL_FMT_INTPTR); + break; + + case '%': len = zpl__print_char(text, remaining, &info, '%'); break; + + default: fmt--; break; + } + + fmt++; + + if (info.base != 0) { + if (info.flags & ZPL_FMT_UNSIGNED) { + zpl_u64 value = 0; + switch (info.flags & ZPL_FMT_INTS) { + case ZPL_FMT_CHAR: value = cast(zpl_u64) cast(zpl_u8) va_arg(va, int); break; + case ZPL_FMT_SHORT: value = cast(zpl_u64) cast(zpl_u16) va_arg(va, int); break; + case ZPL_FMT_LONG: value = cast(zpl_u64) va_arg(va, unsigned long); break; + case ZPL_FMT_LLONG: value = cast(zpl_u64) va_arg(va, unsigned long long); break; + case ZPL_FMT_SIZE: value = cast(zpl_u64) va_arg(va, zpl_usize); break; + case ZPL_FMT_INTPTR: value = cast(zpl_u64) va_arg(va, zpl_uintptr); break; + default: value = cast(zpl_u64) va_arg(va, unsigned int); break; + } + + len = zpl__print_u64(text, remaining, &info, value); + + } else { + zpl_i64 value = 0; + switch (info.flags & ZPL_FMT_INTS) { + case ZPL_FMT_CHAR: value = cast(zpl_i64) cast(zpl_i8) va_arg(va, int); break; + case ZPL_FMT_SHORT: value = cast(zpl_i64) cast(zpl_i16) va_arg(va, int); break; + case ZPL_FMT_LONG: value = cast(zpl_i64) va_arg(va, long); break; + case ZPL_FMT_LLONG: value = cast(zpl_i64) va_arg(va, long long); break; + case ZPL_FMT_SIZE: value = cast(zpl_i64) va_arg(va, zpl_usize); break; + case ZPL_FMT_INTPTR: value = cast(zpl_i64) va_arg(va, zpl_uintptr); break; + default: value = cast(zpl_i64) va_arg(va, int); break; + } + + len = zpl__print_i64(text, remaining, &info, value); + } + } + + text += len; + if (len >= remaining) + remaining = zpl_min(remaining, 1); + else + remaining -= len; + } + + *text++ = '\0'; + res = (text - text_begin); + return (res >= max_len || res < 0) ? -1 : res; + } + + ZPL_END_C_DECLS + // file: source/core/time.c + + + #if defined(ZPL_SYSTEM_MACOS) || ZPL_SYSTEM_UNIX + # include + # include + #endif + + #if defined(ZPL_SYSTEM_MACOS) + # include + # include + # include + #endif + + #if defined(ZPL_SYSTEM_EMSCRIPTEN) + # include + #endif + + #if defined(ZPL_SYSTEM_WINDOWS) + # include + #endif + + ZPL_BEGIN_C_DECLS + + //! @} + //$$ + //////////////////////////////////////////////////////////////// + // + // Time + // + // + + #if defined(ZPL_COMPILER_MSVC) && !defined(__clang__) + zpl_u64 zpl_rdtsc(void) { return __rdtsc( ); } + #elif defined(__i386__) + zpl_u64 zpl_rdtsc(void) { + zpl_u64 x; + __asm__ volatile(".byte 0x0f, 0x31" : "=A"(x)); + return x; + } + #elif defined(__x86_64__) + zpl_u64 zpl_rdtsc(void) { + zpl_u32 hi, lo; + __asm__ __volatile__("rdtsc" : "=a"(lo), "=d"(hi)); + return (cast(zpl_u64) lo) | ((cast(zpl_u64) hi) << 32); + } + #elif defined(__powerpc__) + zpl_u64 zpl_rdtsc(void) { + zpl_u64 result = 0; + zpl_u32 upper, lower, tmp; + __asm__ volatile("0: \n" + "\tmftbu %0 \n" + "\tmftb %1 \n" + "\tmftbu %2 \n" + "\tcmpw %2,%0 \n" + "\tbne 0b \n" + : "=r"(upper), "=r"(lower), "=r"(tmp)); + result = upper; + result = result << 32; + result = result | lower; + + return result; + } + #elif defined(ZPL_SYSTEM_EMSCRIPTEN) + zpl_u64 zpl_rdtsc(void) { + return (zpl_u64)(emscripten_get_now() * 1e+6); + } + #elif defined(ZPL_CPU_ARM) && !defined(ZPL_COMPILER_TINYC) + zpl_u64 zpl_rdtsc(void) { + # if defined(__aarch64__) + int64_t r = 0; + asm volatile("mrs %0, cntvct_el0" : "=r"(r)); + # elif (__ARM_ARCH >= 6) + uint32_t r = 0; + uint32_t pmccntr; + uint32_t pmuseren; + uint32_t pmcntenset; + + // Read the user mode perf monitor counter access permissions. + asm volatile("mrc p15, 0, %0, c9, c14, 0" : "=r"(pmuseren)); + if (pmuseren & 1) { // Allows reading perfmon counters for user mode code. + asm volatile("mrc p15, 0, %0, c9, c12, 1" : "=r"(pmcntenset)); + if (pmcntenset & 0x80000000ul) { // Is it counting? + asm volatile("mrc p15, 0, %0, c9, c13, 0" : "=r"(pmccntr)); + // The counter is set up to count every 64th cycle + return ((int64_t)pmccntr) * 64; // Should optimize to << 6 + } + } + # else + # error "No suitable method for zpl_rdtsc for this cpu type" + # endif + + return r; + } + #else + zpl_u64 zpl_rdtsc(void) { + ZPL_PANIC("zpl_rdtsc is not supported on this particular setup"); + return -0; + } + #endif + + #if defined(ZPL_SYSTEM_WINDOWS) || defined(ZPL_SYSTEM_CYGWIN) + + zpl_u64 zpl_time_rel_ms(void) { + zpl_local_persist LARGE_INTEGER win32_perf_count_freq = { 0 }; + zpl_u64 result; + LARGE_INTEGER counter; + zpl_local_persist LARGE_INTEGER win32_perf_counter = { 0 }; + if (!win32_perf_count_freq.QuadPart) { + QueryPerformanceFrequency(&win32_perf_count_freq); + ZPL_ASSERT(win32_perf_count_freq.QuadPart != 0); + QueryPerformanceCounter(&win32_perf_counter); + } + + QueryPerformanceCounter(&counter); + + result = (counter.QuadPart - win32_perf_counter.QuadPart) * 1000 / (win32_perf_count_freq.QuadPart); + return result; + } + + zpl_u64 zpl_time_utc_ms(void) { + FILETIME ft; + ULARGE_INTEGER li; + + GetSystemTimeAsFileTime(&ft); + li.LowPart = ft.dwLowDateTime; + li.HighPart = ft.dwHighDateTime; + + return li.QuadPart / 1000; + } + + zpl_u64 zpl_time_tz_ms(void) { + FILETIME ft; + SYSTEMTIME st, lst; + ULARGE_INTEGER li; + + GetSystemTime(&st); + SystemTimeToTzSpecificLocalTime(NULL, &st, &lst); + SystemTimeToFileTime(&lst, &ft); + li.LowPart = ft.dwLowDateTime; + li.HighPart = ft.dwHighDateTime; + + return li.QuadPart / 1000; + } + + void zpl_sleep_ms(zpl_u32 ms) { Sleep(ms); } + + #else + + # if defined(ZPL_SYSTEM_LINUX) || defined(ZPL_SYSTEM_FREEBSD) || defined(ZPL_SYSTEM_OPENBSD) || defined(ZPL_SYSTEM_EMSCRIPTEN) + zpl_u64 zpl__unix_gettime(void) { + struct timespec t; + zpl_u64 result; + + clock_gettime(1 /*CLOCK_MONOTONIC*/, &t); + result = 1000 * t.tv_sec + 1.0e-6 * t.tv_nsec; + return result; + } + # endif + + zpl_u64 zpl_time_rel_ms(void) { + # if defined(ZPL_SYSTEM_OSX) + zpl_u64 result; + + zpl_local_persist zpl_u64 timebase = 0; + zpl_local_persist zpl_u64 timestart = 0; + + if (!timestart) { + mach_timebase_info_data_t tb = { 0 }; + mach_timebase_info(&tb); + timebase = tb.numer; + timebase /= tb.denom; + timestart = mach_absolute_time(); + } + + // NOTE: mach_absolute_time() returns things in nanoseconds + result = 1.0e-6 * (mach_absolute_time() - timestart) * timebase; + return result; + # else + zpl_local_persist zpl_u64 unix_timestart = 0.0; + + if (!unix_timestart) { unix_timestart = zpl__unix_gettime( ); } + + zpl_u64 now = zpl__unix_gettime( ); + + return (now - unix_timestart); + # endif + } + + zpl_u64 zpl_time_utc_ms(void) { + struct timespec t; + # if defined(ZPL_SYSTEM_OSX) + clock_serv_t cclock; + mach_timespec_t mts; + host_get_clock_service(mach_host_self( ), CALENDAR_CLOCK, &cclock); + clock_get_time(cclock, &mts); + mach_port_deallocate(mach_task_self( ), cclock); + t.tv_sec = mts.tv_sec; + t.tv_nsec = mts.tv_nsec; + # else + clock_gettime(0 /*CLOCK_REALTIME*/, &t); + # endif + return ((zpl_u64)t.tv_sec * 1000 + t.tv_nsec * 1e-6 + ZPL__UNIX_TO_WIN32_EPOCH); + } + + void zpl_sleep_ms(zpl_u32 ms) { + struct timespec req = { cast(time_t)(ms * 1e-3), cast(long)((ms % 1000) * 1e6) }; + struct timespec rem = { 0, 0 }; + nanosleep(&req, &rem); + } + + zpl_u64 zpl_time_tz_ms(void) { + struct tm t; + zpl_u64 result = zpl_time_utc_ms() - ZPL__UNIX_TO_WIN32_EPOCH; + zpl_u16 ms = result % 1000; + result *= 1e-3; + localtime_r((const time_t*)&result, &t); + result = (zpl_u64)mktime(&t); + return (result - timezone + t.tm_isdst * 3600) * 1000 + ms + ZPL__UNIX_TO_WIN32_EPOCH; + } + #endif + + zpl_f64 zpl_time_rel(void) { + return (zpl_f64)(zpl_time_rel_ms() * 1e-3); + } + + zpl_f64 zpl_time_utc(void) { + return (zpl_f64)(zpl_time_utc_ms() * 1e-3); + } + + zpl_f64 zpl_time_tz(void) { + return (zpl_f64)(zpl_time_tz_ms() * 1e-3); + } + + + + ZPL_END_C_DECLS + // file: source/core/random.c + + + ZPL_BEGIN_C_DECLS + + #if defined(ZPL_MODULE_THREADING) + zpl_global zpl_atomic32 zpl__random_shared_counter = {0}; + #else + zpl_global zpl_i32 zpl__random_shared_counter = 0; + #endif + + zpl_internal zpl_u32 zpl__get_noise_from_time(void) { + zpl_u32 accum = 0; + zpl_f64 start, remaining, end, curr = 0; + zpl_u64 interval = 100000ll; + + start = zpl_time_rel(); + remaining = (interval - cast(zpl_u64)(interval*start)%interval) / cast(zpl_f64)interval; + end = start + remaining; + + do { + curr = zpl_time_rel(); + accum += cast(zpl_u32)curr; + } while (curr >= end); + return accum; + } + + // NOTE: Partly from http://preshing.com/20121224/how-to-generate-a-sequence-of-unique-random-integers/ + // But the generation is even more random-er-est + + zpl_internal ZPL_ALWAYS_INLINE zpl_u32 zpl__permute_qpr(zpl_u32 x) { + zpl_local_persist zpl_u32 const prime = 4294967291; // 2^32 - 5 + if (x >= prime) { + return x; + } else { + zpl_u32 residue = cast(zpl_u32)(cast(zpl_u64) x * x) % prime; + if (x <= prime / 2) + return residue; + else + return prime - residue; + } + } + + zpl_internal ZPL_ALWAYS_INLINE zpl_u32 zpl__permute_with_offset(zpl_u32 x, zpl_u32 offset) { + return (zpl__permute_qpr(x) + offset) ^ 0x5bf03635; + } + + + void zpl_random_init(zpl_random *r) { + zpl_u64 time, tick; + zpl_isize i, j; + zpl_u32 x = 0; + r->value = 0; + + r->offsets[0] = zpl__get_noise_from_time(); + #ifdef ZPL_MODULE_THREADING + r->offsets[1] = zpl_atomic32_fetch_add(&zpl__random_shared_counter, 1); + r->offsets[2] = zpl_thread_current_id(); + r->offsets[3] = zpl_thread_current_id() * 3 + 1; + #else + r->offsets[1] = zpl__random_shared_counter++; + r->offsets[2] = 0; + r->offsets[3] = 1; + #endif + time = zpl_time_tz_ms(); + r->offsets[4] = cast(zpl_u32)(time >> 32); + r->offsets[5] = cast(zpl_u32)time; + r->offsets[6] = zpl__get_noise_from_time(); + tick = zpl_rdtsc(); + r->offsets[7] = cast(zpl_u32)(tick ^ (tick >> 32)); + + for (j = 0; j < 4; j++) { + for (i = 0; i < zpl_count_of(r->offsets); i++) { + r->offsets[i] = x = zpl__permute_with_offset(x, r->offsets[i]); + } + } + } + + zpl_u32 zpl_random_gen_u32(zpl_random *r) { + zpl_u32 x = r->value; + zpl_u32 carry = 1; + zpl_isize i; + for (i = 0; i < zpl_count_of(r->offsets); i++) { + x = zpl__permute_with_offset(x, r->offsets[i]); + if (carry > 0) { + carry = ++r->offsets[i] ? 0 : 1; + } + } + + r->value = x; + return x; + } + + zpl_u32 zpl_random_gen_u32_unique(zpl_random *r) { + zpl_u32 x = r->value; + zpl_isize i; + r->value++; + for (i = 0; i < zpl_count_of(r->offsets); i++) { + x = zpl__permute_with_offset(x, r->offsets[i]); + } + + return x; + } + + zpl_u64 zpl_random_gen_u64(zpl_random *r) { + return ((cast(zpl_u64)zpl_random_gen_u32(r)) << 32) | zpl_random_gen_u32(r); + } + + + zpl_isize zpl_random_gen_isize(zpl_random *r) { + #if defined(ZPL_ARCH_32_BIT) + zpl_u32 u = zpl_random_gen_u32(r); + #else + zpl_u64 u = zpl_random_gen_u64(r); + #endif + zpl_isize i; + zpl_memcopy(&i, &u, zpl_size_of(u)); + return i; + } + + + zpl_i64 zpl_random_range_i64(zpl_random *r, zpl_i64 lower_inc, zpl_i64 higher_inc) { + zpl_u64 u = zpl_random_gen_u64(r); + zpl_i64 diff = higher_inc-lower_inc+1; + u %= diff; + zpl_i64 i; + zpl_memcopy(&i, &u, zpl_size_of(u)); + i += lower_inc; + return i; + } + + zpl_isize zpl_random_range_isize(zpl_random *r, zpl_isize lower_inc, zpl_isize higher_inc) { + #if defined(ZPL_ARCH_32_BIT) + zpl_u32 u = zpl_random_gen_u32(r); + #else + zpl_u64 u = zpl_random_gen_u64(r); + #endif + zpl_isize diff = higher_inc-lower_inc+1; + u %= diff; + zpl_isize i; + zpl_memcopy(&i, &u, zpl_size_of(u)); + i += lower_inc; + return i; + } + + ZPL_ALWAYS_INLINE zpl_f64 zpl__random_copy_sign64(zpl_f64 x, zpl_f64 y) { + zpl_i64 ix=0, iy=0; + zpl_memcopy(&ix, &x, zpl_size_of(zpl_i64)); + zpl_memcopy(&iy, &y, zpl_size_of(zpl_i64)); + + ix &= 0x7fffffffffffffff; + ix |= iy & 0x8000000000000000; + + zpl_f64 r = 0.0; + zpl_memcopy(&r, &ix, zpl_size_of(zpl_f64)); + return r; + } + + zpl_f64 zpl_random_range_f64(zpl_random *r, zpl_f64 lower_inc, zpl_f64 higher_inc) { + zpl_f64 f = cast(zpl_f64)zpl_random_gen_u64(r) / cast(zpl_f64)ZPL_U64_MAX; + zpl_f64 diff = higher_inc-lower_inc; + + f *= diff; + f += lower_inc; + return f; + } + + ZPL_END_C_DECLS + // file: source/core/misc.c + + + ZPL_BEGIN_C_DECLS + + void zpl_yield(void) { + # if defined(ZPL_SYSTEM_WINDOWS) + Sleep(0); + # else + sched_yield(); + # endif + } + + const char *zpl_get_env(const char *name) { + char *buffer = NULL; + const char *ptr = zpl_get_env_buf(name); + + if (ptr == NULL) { + return NULL; + } + + zpl_isize ptr_size = zpl_strlen(ptr); + buffer = (char *)zpl_malloc(ptr_size * sizeof(char)+1); + zpl_memcopy((char *)buffer, ptr, ptr_size+1); + return buffer; + } + + const char *zpl_get_env_buf(const char *name) { + # ifdef ZPL_SYSTEM_WINDOWS + zpl_local_persist wchar_t wbuffer[32767] = {0}; + zpl_local_persist char buffer[32767] = {0}; + + if (!GetEnvironmentVariableW( + cast(LPCWSTR)zpl_utf8_to_ucs2_buf(cast(const zpl_u8 *)name), + cast(LPWSTR)wbuffer, 32767)) { + return NULL; + } + + zpl_ucs2_to_utf8(cast(zpl_u8*)buffer, 32767, cast(const zpl_u16*)wbuffer); + + return (const char *)buffer; + # else + return (const char *)getenv(name); + # endif + } + + zpl_string zpl_get_env_str(const char *name) { + const char *buf = zpl_get_env_buf(name); + + if (buf == NULL) { + return NULL; + } + + zpl_string str = zpl_string_make(zpl_heap(), buf); + return str; + } + + void zpl_set_env(const char *name, const char *value) { + # if defined(ZPL_SYSTEM_WINDOWS) + SetEnvironmentVariableA(name, value); + # else + setenv(name, value, 1); + # endif + } + + void zpl_unset_env(const char *name) { + # if defined(ZPL_SYSTEM_WINDOWS) + SetEnvironmentVariableA(name, NULL); + # else + unsetenv(name); + # endif + } + + #if !defined(ZPL_SYSTEM_WINDOWS) + extern char **environ; + #endif + + zpl_u32 zpl_system_command(const char *command, zpl_usize buffer_len, char *buffer) { + # if defined(ZPL_SYSTEM_EMSCRIPTEN) + ZPL_PANIC("zpl_system_command not supported"); + # else + + # if defined(ZPL_SYSTEM_WINDOWS) + FILE *handle = _popen(command, "r"); + # else + FILE *handle = popen(command, "r"); + # endif + + if(!handle) return 0; + + int c; + zpl_usize i=0; + while ((c = getc(handle)) != EOF && i++ < buffer_len) { + *buffer++ = c; + } + + # if defined(ZPL_SYSTEM_WINDOWS) + _pclose(handle); + # else + pclose(handle); + # endif + + # endif + + return 1; + } + + zpl_string zpl_system_command_str(const char *command, zpl_allocator backing) { + # if defined(ZPL_SYSTEM_EMSCRIPTEN) + ZPL_PANIC("zpl_system_command not supported"); + # else + + # if defined(ZPL_SYSTEM_WINDOWS) + FILE *handle = _popen(command, "r"); + # else + FILE *handle = popen(command, "r"); + # endif + + if(!handle) return NULL; + + zpl_string output = zpl_string_make_reserve(backing, 4); + + int c; + while ((c = getc(handle)) != EOF) { + char ins[2] = {(char)c,0}; + output = zpl_string_appendc(output, ins); + } + + # if defined(ZPL_SYSTEM_WINDOWS) + _pclose(handle); + # else + pclose(handle); + # endif + return output; + # endif + return NULL; + } + + ZPL_END_C_DECLS + // file: source/core/sort.c + + + ZPL_BEGIN_C_DECLS + + #define ZPL__COMPARE_PROC(Type) \ + zpl_global zpl_isize Type##__cmp_offset; \ + ZPL_COMPARE_PROC(Type##__cmp) { \ + Type const p = *cast(Type const *) zpl_pointer_add_const(a, Type##__cmp_offset); \ + Type const q = *cast(Type const *) zpl_pointer_add_const(b, Type##__cmp_offset); \ + return p < q ? -1 : p > q; \ + } \ + ZPL_COMPARE_PROC_PTR(Type##_cmp(zpl_isize offset)) { \ + Type##__cmp_offset = offset; \ + return &Type##__cmp; \ + } + + ZPL__COMPARE_PROC(zpl_u8); + ZPL__COMPARE_PROC(zpl_i16); + ZPL__COMPARE_PROC(zpl_i32); + ZPL__COMPARE_PROC(zpl_i64); + ZPL__COMPARE_PROC(zpl_isize); + ZPL__COMPARE_PROC(zpl_f32); + ZPL__COMPARE_PROC(zpl_f64); + + // NOTE: str_cmp is special as it requires a funny type and funny comparison + zpl_global zpl_isize zpl__str_cmp_offset; + ZPL_COMPARE_PROC(zpl__str_cmp) { + char const *p = *cast(char const **) zpl_pointer_add_const(a, zpl__str_cmp_offset); + char const *q = *cast(char const **) zpl_pointer_add_const(b, zpl__str_cmp_offset); + return zpl_strcmp(p, q); + } + ZPL_COMPARE_PROC_PTR(zpl_str_cmp(zpl_isize offset)) { + zpl__str_cmp_offset = offset; + return &zpl__str_cmp; + } + + #undef ZPL__COMPARE_PROC + + // TODO: Make user definable? + #define ZPL__SORT_STACK_SIZE 64 + #define zpl__SORT_INSERT_SORT_TRESHOLD 8 + + #define ZPL__SORT_PUSH(_base, _limit) \ + do { \ + stack_ptr[0] = (_base); \ + stack_ptr[1] = (_limit); \ + stack_ptr += 2; \ + } while (0) + + #define ZPL__SORT_POP(_base, _limit) \ + do { \ + stack_ptr -= 2; \ + (_base) = stack_ptr[0]; \ + (_limit) = stack_ptr[1]; \ + } while (0) + + void zpl_sort(void *base_, zpl_isize count, zpl_isize size, zpl_compare_proc cmp) { + zpl_u8 *i, *j; + zpl_u8 *base = cast(zpl_u8 *) base_; + zpl_u8 *limit = base + count * size; + zpl_isize threshold = zpl__SORT_INSERT_SORT_TRESHOLD * size; + + // NOTE: Prepare the stack + zpl_u8 *stack[ZPL__SORT_STACK_SIZE] = { 0 }; + zpl_u8 **stack_ptr = stack; + + for (;;) { + if ((limit - base) > threshold) { + // NOTE: Quick sort + i = base + size; + j = limit - size; + + zpl_memswap(((limit - base) / size / 2) * size + base, base, size); + if (cmp(i, j) > 0) zpl_memswap(i, j, size); + if (cmp(base, j) > 0) zpl_memswap(base, j, size); + if (cmp(i, base) > 0) zpl_memswap(i, base, size); + + for (;;) { + do + i += size; + while (cmp(i, base) < 0); + do + j -= size; + while (cmp(j, base) > 0); + if (i > j) break; + zpl_memswap(i, j, size); + } + + zpl_memswap(base, j, size); + + if (j - base > limit - i) { + ZPL__SORT_PUSH(base, j); + base = i; + } else { + ZPL__SORT_PUSH(i, limit); + limit = j; + } + } else { + // NOTE: Insertion sort + for (j = base, i = j + size; i < limit; j = i, i += size) { + for (; cmp(j, j + size) > 0; j -= size) { + zpl_memswap(j, j + size, size); + if (j == base) break; + } + } + + if (stack_ptr == stack) break; // NOTE: Sorting is done! + ZPL__SORT_POP(base, limit); + } + } + } + + #undef ZPL__SORT_PUSH + #undef ZPL__SORT_POP + + #define ZPL_RADIX_SORT_PROC_GEN(Type) \ + ZPL_RADIX_SORT_PROC(Type) { \ + zpl_##Type *source = items; \ + zpl_##Type *dest = temp; \ + zpl_isize byte_index, i, byte_max = 8 * zpl_size_of(zpl_##Type); \ + for (byte_index = 0; byte_index < byte_max; byte_index += 8) { \ + zpl_isize offsets[256] = { 0 }; \ + zpl_isize total = 0; \ + /* NOTE: First pass - count how many of each key */ \ + for (i = 0; i < count; i++) { \ + zpl_##Type radix_value = source[i]; \ + zpl_##Type radix_piece = (radix_value >> byte_index) & 0xff; \ + offsets[radix_piece]++; \ + } \ + /* NOTE: Change counts to offsets */ \ + for (i = 0; i < zpl_count_of(offsets); i++) { \ + zpl_isize skcount = offsets[i]; \ + offsets[i] = total; \ + total += skcount; \ + } \ + /* NOTE: Second pass - place elements into the right location */ \ + for (i = 0; i < count; i++) { \ + zpl_##Type radix_value = source[i]; \ + zpl_##Type radix_piece = (radix_value >> byte_index) & 0xff; \ + dest[offsets[radix_piece]++] = source[i]; \ + } \ + zpl_swap(zpl_##Type *, source, dest); \ + } \ + } + + ZPL_RADIX_SORT_PROC_GEN(u8); + ZPL_RADIX_SORT_PROC_GEN(u16); + ZPL_RADIX_SORT_PROC_GEN(u32); + ZPL_RADIX_SORT_PROC_GEN(u64); + + void zpl_shuffle(void *base, zpl_isize count, zpl_isize size) { + zpl_u8 *a; + zpl_isize i, j; + zpl_random random; + zpl_random_init(&random); + + a = cast(zpl_u8 *) base + (count - 1) * size; + for (i = count; i > 1; i--) { + j = zpl_random_gen_isize(&random) % i; + zpl_memswap(a, cast(zpl_u8 *) base + j * size, size); + a -= size; + } + } + + void zpl_reverse(void *base, zpl_isize count, zpl_isize size) { + zpl_isize i, j = count - 1; + for (i = 0; i < j; i++, j++) zpl_memswap(cast(zpl_u8 *) base + i * size, cast(zpl_u8 *) base + j * size, size); + } + + ZPL_END_C_DECLS +# endif +#endif + +#if defined(ZPL_MODULE_HASHING) + // file: source/hashing.c + + //////////////////////////////////////////////////////////////// + // + // Hashing functions + // + // + + ZPL_BEGIN_C_DECLS + + zpl_u32 zpl_adler32(void const *data, zpl_isize len) { + zpl_u32 const MOD_ALDER = 65521; + zpl_u32 a = 1, b = 0; + zpl_isize i, block_len; + zpl_u8 const *bytes = cast(zpl_u8 const *) data; + + block_len = len % 5552; + + while (len) { + for (i = 0; i + 7 < block_len; i += 8) { + a += bytes[0], b += a; + a += bytes[1], b += a; + a += bytes[2], b += a; + a += bytes[3], b += a; + a += bytes[4], b += a; + a += bytes[5], b += a; + a += bytes[6], b += a; + a += bytes[7], b += a; + + bytes += 8; + } + for (; i < block_len; i++) a += *bytes++, b += a; + + a %= MOD_ALDER, b %= MOD_ALDER; + len -= block_len; + block_len = 5552; + } + + return (b << 16) | a; + } + + zpl_global zpl_u32 const zpl__crc32_table[256] = { + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, 0x0edb8832, + 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, + 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 0x136c9856, 0x646ba8c0, 0xfd62f97a, + 0x8a65c9ec, 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, + 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, + 0x45df5c75, 0xdcd60dcf, 0xabd13d59, 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, + 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 0x2f6f7c87, 0x58684c11, 0xc1611dab, + 0xb6662d3d, 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, + 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, 0x6b6b51f4, + 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, + 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, 0x4db26158, 0x3ab551ce, 0xa3bc0074, + 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, + 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525, + 0x206f85b3, 0xb966d409, 0xce61e49f, 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, + 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, + 0x73dc1683, 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, + 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, 0xfed41b76, + 0x89d32be0, 0x10da7a5a, 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, + 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, + 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, + 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, + 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, + 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, + 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, + 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, 0xa00ae278, + 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, + 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, 0xbdbdf21c, 0xcabac28a, 0x53b39330, + 0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, + 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d, + }; + + zpl_global zpl_u64 const zpl__crc64_table[256] = { + 0x0000000000000000ull, 0x7ad870c830358979ull, 0xf5b0e190606b12f2ull, 0x8f689158505e9b8bull, 0xc038e5739841b68full, 0xbae095bba8743ff6ull, + 0x358804e3f82aa47dull, 0x4f50742bc81f2d04ull, 0xab28ecb46814fe75ull, 0xd1f09c7c5821770cull, 0x5e980d24087fec87ull, 0x24407dec384a65feull, + 0x6b1009c7f05548faull, 0x11c8790fc060c183ull, 0x9ea0e857903e5a08ull, 0xe478989fa00bd371ull, 0x7d08ff3b88be6f81ull, 0x07d08ff3b88be6f8ull, + 0x88b81eabe8d57d73ull, 0xf2606e63d8e0f40aull, 0xbd301a4810ffd90eull, 0xc7e86a8020ca5077ull, 0x4880fbd87094cbfcull, 0x32588b1040a14285ull, + 0xd620138fe0aa91f4ull, 0xacf86347d09f188dull, 0x2390f21f80c18306ull, 0x594882d7b0f40a7full, 0x1618f6fc78eb277bull, 0x6cc0863448deae02ull, + 0xe3a8176c18803589ull, 0x997067a428b5bcf0ull, 0xfa11fe77117cdf02ull, 0x80c98ebf2149567bull, + 0x0fa11fe77117cdf0ull, 0x75796f2f41224489ull, 0x3a291b04893d698dull, 0x40f16bccb908e0f4ull, 0xcf99fa94e9567b7full, 0xb5418a5cd963f206ull, + 0x513912c379682177ull, 0x2be1620b495da80eull, 0xa489f35319033385ull, 0xde51839b2936bafcull, 0x9101f7b0e12997f8ull, 0xebd98778d11c1e81ull, + 0x64b116208142850aull, 0x1e6966e8b1770c73ull, 0x8719014c99c2b083ull, 0xfdc17184a9f739faull, 0x72a9e0dcf9a9a271ull, 0x08719014c99c2b08ull, + 0x4721e43f0183060cull, 0x3df994f731b68f75ull, 0xb29105af61e814feull, 0xc849756751dd9d87ull, 0x2c31edf8f1d64ef6ull, 0x56e99d30c1e3c78full, + 0xd9810c6891bd5c04ull, 0xa3597ca0a188d57dull, 0xec09088b6997f879ull, 0x96d1784359a27100ull, 0x19b9e91b09fcea8bull, 0x636199d339c963f2ull, + 0xdf7adabd7a6e2d6full, 0xa5a2aa754a5ba416ull, 0x2aca3b2d1a053f9dull, 0x50124be52a30b6e4ull, 0x1f423fcee22f9be0ull, 0x659a4f06d21a1299ull, + 0xeaf2de5e82448912ull, 0x902aae96b271006bull, 0x74523609127ad31aull, 0x0e8a46c1224f5a63ull, 0x81e2d7997211c1e8ull, 0xfb3aa75142244891ull, + 0xb46ad37a8a3b6595ull, 0xceb2a3b2ba0eececull, 0x41da32eaea507767ull, 0x3b024222da65fe1eull, 0xa2722586f2d042eeull, 0xd8aa554ec2e5cb97ull, + 0x57c2c41692bb501cull, 0x2d1ab4dea28ed965ull, 0x624ac0f56a91f461ull, 0x1892b03d5aa47d18ull, 0x97fa21650afae693ull, 0xed2251ad3acf6feaull, + 0x095ac9329ac4bc9bull, 0x7382b9faaaf135e2ull, 0xfcea28a2faafae69ull, 0x8632586aca9a2710ull, 0xc9622c4102850a14ull, 0xb3ba5c8932b0836dull, + 0x3cd2cdd162ee18e6ull, 0x460abd1952db919full, 0x256b24ca6b12f26dull, 0x5fb354025b277b14ull, 0xd0dbc55a0b79e09full, 0xaa03b5923b4c69e6ull, + 0xe553c1b9f35344e2ull, 0x9f8bb171c366cd9bull, 0x10e3202993385610ull, 0x6a3b50e1a30ddf69ull, 0x8e43c87e03060c18ull, 0xf49bb8b633338561ull, + 0x7bf329ee636d1eeaull, 0x012b592653589793ull, 0x4e7b2d0d9b47ba97ull, 0x34a35dc5ab7233eeull, 0xbbcbcc9dfb2ca865ull, 0xc113bc55cb19211cull, + 0x5863dbf1e3ac9decull, 0x22bbab39d3991495ull, 0xadd33a6183c78f1eull, 0xd70b4aa9b3f20667ull, 0x985b3e827bed2b63ull, 0xe2834e4a4bd8a21aull, + 0x6debdf121b863991ull, 0x1733afda2bb3b0e8ull, 0xf34b37458bb86399ull, 0x8993478dbb8deae0ull, 0x06fbd6d5ebd3716bull, 0x7c23a61ddbe6f812ull, + 0x3373d23613f9d516ull, 0x49aba2fe23cc5c6full, 0xc6c333a67392c7e4ull, 0xbc1b436e43a74e9dull, 0x95ac9329ac4bc9b5ull, 0xef74e3e19c7e40ccull, + 0x601c72b9cc20db47ull, 0x1ac40271fc15523eull, 0x5594765a340a7f3aull, 0x2f4c0692043ff643ull, 0xa02497ca54616dc8ull, 0xdafce7026454e4b1ull, + 0x3e847f9dc45f37c0ull, 0x445c0f55f46abeb9ull, 0xcb349e0da4342532ull, 0xb1eceec59401ac4bull, 0xfebc9aee5c1e814full, 0x8464ea266c2b0836ull, + 0x0b0c7b7e3c7593bdull, 0x71d40bb60c401ac4ull, 0xe8a46c1224f5a634ull, 0x927c1cda14c02f4dull, 0x1d148d82449eb4c6ull, 0x67ccfd4a74ab3dbfull, + 0x289c8961bcb410bbull, 0x5244f9a98c8199c2ull, 0xdd2c68f1dcdf0249ull, 0xa7f41839ecea8b30ull, 0x438c80a64ce15841ull, 0x3954f06e7cd4d138ull, + 0xb63c61362c8a4ab3ull, 0xcce411fe1cbfc3caull, 0x83b465d5d4a0eeceull, 0xf96c151de49567b7ull, 0x76048445b4cbfc3cull, 0x0cdcf48d84fe7545ull, + 0x6fbd6d5ebd3716b7ull, 0x15651d968d029fceull, 0x9a0d8ccedd5c0445ull, 0xe0d5fc06ed698d3cull, 0xaf85882d2576a038ull, 0xd55df8e515432941ull, + 0x5a3569bd451db2caull, 0x20ed197575283bb3ull, 0xc49581ead523e8c2ull, 0xbe4df122e51661bbull, 0x3125607ab548fa30ull, 0x4bfd10b2857d7349ull, + 0x04ad64994d625e4dull, 0x7e7514517d57d734ull, 0xf11d85092d094cbfull, 0x8bc5f5c11d3cc5c6ull, 0x12b5926535897936ull, 0x686de2ad05bcf04full, + 0xe70573f555e26bc4ull, 0x9ddd033d65d7e2bdull, 0xd28d7716adc8cfb9ull, 0xa85507de9dfd46c0ull, 0x273d9686cda3dd4bull, 0x5de5e64efd965432ull, + 0xb99d7ed15d9d8743ull, 0xc3450e196da80e3aull, 0x4c2d9f413df695b1ull, 0x36f5ef890dc31cc8ull, 0x79a59ba2c5dc31ccull, 0x037deb6af5e9b8b5ull, + 0x8c157a32a5b7233eull, 0xf6cd0afa9582aa47ull, 0x4ad64994d625e4daull, 0x300e395ce6106da3ull, 0xbf66a804b64ef628ull, 0xc5bed8cc867b7f51ull, + 0x8aeeace74e645255ull, 0xf036dc2f7e51db2cull, 0x7f5e4d772e0f40a7ull, 0x05863dbf1e3ac9deull, 0xe1fea520be311aafull, 0x9b26d5e88e0493d6ull, + 0x144e44b0de5a085dull, 0x6e963478ee6f8124ull, 0x21c640532670ac20ull, 0x5b1e309b16452559ull, 0xd476a1c3461bbed2ull, 0xaeaed10b762e37abull, + 0x37deb6af5e9b8b5bull, 0x4d06c6676eae0222ull, 0xc26e573f3ef099a9ull, 0xb8b627f70ec510d0ull, 0xf7e653dcc6da3dd4ull, 0x8d3e2314f6efb4adull, + 0x0256b24ca6b12f26ull, 0x788ec2849684a65full, 0x9cf65a1b368f752eull, 0xe62e2ad306bafc57ull, 0x6946bb8b56e467dcull, 0x139ecb4366d1eea5ull, + 0x5ccebf68aecec3a1ull, 0x2616cfa09efb4ad8ull, 0xa97e5ef8cea5d153ull, 0xd3a62e30fe90582aull, 0xb0c7b7e3c7593bd8ull, 0xca1fc72bf76cb2a1ull, + 0x45775673a732292aull, 0x3faf26bb9707a053ull, 0x70ff52905f188d57ull, 0x0a2722586f2d042eull, 0x854fb3003f739fa5ull, 0xff97c3c80f4616dcull, + 0x1bef5b57af4dc5adull, 0x61372b9f9f784cd4ull, 0xee5fbac7cf26d75full, 0x9487ca0fff135e26ull, 0xdbd7be24370c7322ull, 0xa10fceec0739fa5bull, + 0x2e675fb4576761d0ull, 0x54bf2f7c6752e8a9ull, 0xcdcf48d84fe75459ull, 0xb71738107fd2dd20ull, 0x387fa9482f8c46abull, 0x42a7d9801fb9cfd2ull, + 0x0df7adabd7a6e2d6ull, 0x772fdd63e7936bafull, 0xf8474c3bb7cdf024ull, 0x829f3cf387f8795dull, 0x66e7a46c27f3aa2cull, 0x1c3fd4a417c62355ull, + 0x935745fc4798b8deull, 0xe98f353477ad31a7ull, 0xa6df411fbfb21ca3ull, 0xdc0731d78f8795daull, 0x536fa08fdfd90e51ull, 0x29b7d047efec8728ull, + }; + + zpl_u32 zpl_crc32(void const *data, zpl_isize len) { + zpl_isize remaining; + zpl_u32 result = ~(cast(zpl_u32) 0); + zpl_u8 const *c = cast(zpl_u8 const *) data; + for (remaining = len; remaining--; c++) result = (result >> 8) ^ (zpl__crc32_table[(result ^ *c) & 0xff]); + return ~result; + } + + zpl_u64 zpl_crc64(void const *data, zpl_isize len) { + zpl_isize remaining; + zpl_u64 result = (cast(zpl_u64)0); + zpl_u8 const *c = cast(zpl_u8 const *) data; + for (remaining = len; remaining--; c++) result = (result >> 8) ^ (zpl__crc64_table[(result ^ *c) & 0xff]); + return result; + } + + zpl_u32 zpl_fnv32(void const *data, zpl_isize len) { + zpl_isize i; + zpl_u32 h = 0x811c9dc5; + zpl_u8 const *c = cast(zpl_u8 const *) data; + + for (i = 0; i < len; i++) h = (h * 0x01000193) ^ c[i]; + + return h; + } + + zpl_u64 zpl_fnv64(void const *data, zpl_isize len) { + zpl_isize i; + zpl_u64 h = 0xcbf29ce484222325ull; + zpl_u8 const *c = cast(zpl_u8 const *) data; + + for (i = 0; i < len; i++) h = (h * 0x100000001b3ll) ^ c[i]; + + return h; + } + + zpl_u32 zpl_fnv32a(void const *data, zpl_isize len) { + zpl_isize i; + zpl_u32 h = 0x811c9dc5; + zpl_u8 const *c = cast(zpl_u8 const *) data; + + for (i = 0; i < len; i++) h = (h ^ c[i]) * 0x01000193; + + return h; + } + + zpl_u64 zpl_fnv64a(void const *data, zpl_isize len) { + zpl_isize i; + zpl_u64 h = 0xcbf29ce484222325ull; + zpl_u8 const *c = cast(zpl_u8 const *) data; + + for (i = 0; i < len; i++) h = (h ^ c[i]) * 0x100000001b3ll; + + return h; + } + + // base64 implementation based on https://nachtimwald.com/2017/11/18/base64-encode-and-decode-in-c/ + // + zpl_global zpl_u8 zpl__base64_chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + + + /* generated table based on: */ + #if 0 + void zpl__base64_decode_table() { + zpl_i32 inv[80]; + zpl_isize i; + + zpl_memset(inv, -1, zpl_size_of(inv)); + + for (i=0; i < zpl_size_of(zpl__base64_chars)-1; i++) { + inv[zpl__base64_chars[i]-43] = i; + } + } + #endif + /* === */ + zpl_global zpl_i32 zpl__base64_dec_table[] = { + 62, -1, -1, -1, 63, 52, 53, 54, 55, 56, 57, 58, + 59, 60, 61, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, + 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, + 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, -1, 26, 27, 28, + 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, + 43, 44, 45, 46, 47, 48, 49, 50, 51 }; + + zpl_isize zpl__base64_encoded_size(zpl_isize len) { + zpl_isize ret = len; + + if (len % 3 != 0) { + ret += 3 - (len % 3); + } + + ret /= 3; + ret *= 4; + + return ret; + } + + zpl_isize zpl__base64_decoded_size(void const *data) { + zpl_isize len, ret, i; + const zpl_u8 *s = cast(const zpl_u8 *)data; + + if (s == NULL) { + return 0; + } + + len = zpl_strlen(cast(const char*)s); + ret = len / 4 * 3; + + for (i=len; i-- > 0;) { + if (s[i] == '=') { + ret--; + } else { + break; + } + } + + return ret; + } + + zpl_b32 zpl__base64_valid_char(zpl_u8 c) { + if (c >= '0' && c <= '9') + return true; + if (c >= 'A' && c <= 'Z') + return true; + if (c >= 'a' && c <= 'z') + return true; + if (c == '+' || c == '/' || c == '=') + return true; + + return false; + } + + zpl_u8 *zpl_base64_encode(zpl_allocator a, void const *data, zpl_isize len) { + const zpl_u8 *s = cast(const zpl_u8*)data; + zpl_u8 *ret = NULL; + zpl_isize enc_len, i, j, v; + + if (data == NULL || len == 0) { + return NULL; + } + + enc_len = zpl__base64_encoded_size(len); + ret = cast(zpl_u8 *)zpl_alloc(a, enc_len+1); + ret[enc_len] = 0; + + for (i=0, j=0; i < len; i+=3, j+=4) { + v = s[i]; + v = (i+1 < len) ? (v << 8 | s[i+1]) : (v << 8); + v = (i+2 < len) ? (v << 8 | s[i+2]) : (v << 8); + + ret[j] = zpl__base64_chars[(v >> 18) & 0x3F]; + ret[j+1] = zpl__base64_chars[(v >> 12) & 0x3F]; + + if (i+1 < len) + ret[j+2] = zpl__base64_chars[(v >> 6) & 0x3F]; + + else ret[j+2] = '='; + + if (i+2 < len) + ret[j+3] = zpl__base64_chars[v & 0x3F]; + + else ret[j+3] = '='; + + } + + return ret; + } + + zpl_u8 *zpl_base64_decode(zpl_allocator a, void const *data, zpl_isize len) { + const zpl_u8 *s = cast(const zpl_u8*)data; + zpl_u8 *ret = NULL; + zpl_isize alen, i, j, v; + + if (data == NULL) { + return NULL; + } + + alen = zpl__base64_decoded_size(s); + ret = cast(zpl_u8 *)zpl_alloc(a, alen+1); + + ZPL_ASSERT_NOT_NULL(ret); + + ret[alen] = 0; + + for (i=0; i> 16) & 0xFF; + + if (s[i+2] != '=') + ret[j+1] = (v >> 8) & 0xFF; + + if (s[i+3] != '=') + ret[j+2] = v & 0xFF; + } + + return ret; + } + + zpl_u32 zpl_murmur32_seed(void const *data, zpl_isize len, zpl_u32 seed) { + zpl_u32 const c1 = 0xcc9e2d51; + zpl_u32 const c2 = 0x1b873593; + zpl_u32 const r1 = 15; + zpl_u32 const r2 = 13; + zpl_u32 const m = 5; + zpl_u32 const n = 0xe6546b64; + + zpl_isize i, nblocks = len / 4; + zpl_u32 hash = seed, k1 = 0; + zpl_u32 const *blocks = cast(zpl_u32 const *) data; + zpl_u8 const *tail = cast(zpl_u8 const *)(data) + nblocks * 4; + + for (i = 0; i < nblocks; i++) { + zpl_u32 k = blocks[i]; + k *= c1; + k = (k << r1) | (k >> (32 - r1)); + k *= c2; + + hash ^= k; + hash = ((hash << r2) | (hash >> (32 - r2))) * m + n; + } + + switch (len & 3) { + case 3: k1 ^= tail[2] << 16; + case 2: k1 ^= tail[1] << 8; + case 1: + k1 ^= tail[0]; + + k1 *= c1; + k1 = (k1 << r1) | (k1 >> (32 - r1)); + k1 *= c2; + hash ^= k1; + } + + hash ^= len; + hash ^= (hash >> 16); + hash *= 0x85ebca6b; + hash ^= (hash >> 13); + hash *= 0xc2b2ae35; + hash ^= (hash >> 16); + + return hash; + } + + zpl_u64 zpl_murmur64_seed(void const *data_, zpl_isize len, zpl_u64 seed) { + zpl_u64 const m = 0xc6a4a7935bd1e995ULL; + zpl_i32 const r = 47; + + zpl_u64 h = seed ^ (len * m); + + zpl_u64 const *data = cast(zpl_u64 const *) data_; + zpl_u8 const *data2 = cast(zpl_u8 const *) data_; + zpl_u64 const *end = data + (len / 8); + + while (data != end) { + zpl_u64 k = *data++; + + k *= m; + k ^= k >> r; + k *= m; + + h ^= k; + h *= m; + } + + switch (len & 7) { + case 7: h ^= cast(zpl_u64)(data2[6]) << 48; + case 6: h ^= cast(zpl_u64)(data2[5]) << 40; + case 5: h ^= cast(zpl_u64)(data2[4]) << 32; + case 4: h ^= cast(zpl_u64)(data2[3]) << 24; + case 3: h ^= cast(zpl_u64)(data2[2]) << 16; + case 2: h ^= cast(zpl_u64)(data2[1]) << 8; + case 1: h ^= cast(zpl_u64)(data2[0]); + h *= m; + }; + + h ^= h >> r; + h *= m; + h ^= h >> r; + + return h; + } + + ZPL_END_C_DECLS +#endif + +#if defined(ZPL_MODULE_REGEX) + // file: source/regex.c + + + ZPL_BEGIN_C_DECLS + + typedef enum zplreOp { + ZPL_RE_OP_BEGIN_CAPTURE, + ZPL_RE_OP_END_CAPTURE, + + ZPL_RE_OP_BEGINNING_OF_LINE, + ZPL_RE_OP_END_OF_LINE, + + ZPL_RE_OP_EXACT_MATCH, + ZPL_RE_OP_META_MATCH, + + ZPL_RE_OP_ANY, + ZPL_RE_OP_ANY_OF, + ZPL_RE_OP_ANY_BUT, + + ZPL_RE_OP_ZERO_OR_MORE, + ZPL_RE_OP_ONE_OR_MORE, + ZPL_RE_OP_ZERO_OR_MORE_SHORTEST, + ZPL_RE_OP_ONE_OR_MORE_SHORTEST, + ZPL_RE_OP_ZERO_OR_ONE, + + ZPL_RE_OP_BRANCH_START, + ZPL_RE_OP_BRANCH_END + } zplreOp; + + typedef enum zplreCode { + ZPL_RE_CODE_NULL = 0x0000, + ZPL_RE_CODE_WHITESPACE = 0x0100, + ZPL_RE_CODE_NOT_WHITESPACE = 0x0200, + ZPL_RE_CODE_DIGIT = 0x0300, + ZPL_RE_CODE_NOT_DIGIT = 0x0400, + ZPL_RE_CODE_ALPHA = 0x0500, + ZPL_RE_CODE_LOWER = 0x0600, + ZPL_RE_CODE_UPPER = 0x0700, + ZPL_RE_CODE_WORD = 0x0800, + ZPL_RE_CODE_NOT_WORD = 0x0900, + + ZPL_RE_CODE_XDIGIT = 0x0a00, + ZPL_RE_CODE_PRINTABLE = 0x0b00, + } zplreCode; + + typedef struct { + zpl_isize op, offset; + } zpl_re_ctx; + + enum { + ZPL_RE__NO_MATCH = -1, + ZPL_RE__INTERNAL_FAILURE = -2, + }; + + static char const ZPL_RE__META_CHARS[] = "^$()[].*+?|\\"; + static char const ZPL_RE__WHITESPACE[] = " \r\t\n\v\f"; + #define ZPL_RE__LITERAL(str) (str), zpl_size_of(str)-1 + + static zpl_re_ctx zpl_re__exec_single(zpl_re *re, zpl_isize op, char const *str, zpl_isize str_len, zpl_isize offset, zpl_re_capture *captures, zpl_isize max_capture_count); + static zpl_re_ctx zpl_re__exec(zpl_re *re, zpl_isize op, char const *str, zpl_isize str_len, zpl_isize offset, zpl_re_capture *captures, zpl_isize max_capture_count); + + static zpl_re_ctx zpl_re__ctx_no_match(zpl_isize op) { + zpl_re_ctx c; + c.op = op; + c.offset = ZPL_RE__NO_MATCH; + return c; + } + + static zpl_re_ctx zpl_re__ctx_internal_failure(zpl_isize op) { + zpl_re_ctx c; + c.op = op; + c.offset = ZPL_RE__INTERNAL_FAILURE; + return c; + } + + static zpl_u8 zpl_re__hex(char const *s) { + return ((zpl_char_to_hex_digit(*s) << 4) & 0xf0) | (zpl_char_to_hex_digit(*(s+1)) & 0x0f); + } + + static zpl_isize zpl_re__strfind(char const *s, zpl_isize len, char c, zpl_isize offset) { + if (offset < len) { + char const *found = (char const *)zpl_memchr(s+offset, c, len-offset); + if (found) + return found - s; + } + + return -1; + } + + static zpl_b32 zpl_re__match_escape(char c, int code) { + switch (code) { + case ZPL_RE_CODE_NULL: return c == 0; + case ZPL_RE_CODE_WHITESPACE: return zpl_re__strfind(ZPL_RE__LITERAL(ZPL_RE__WHITESPACE), c, 0) >= 0; + case ZPL_RE_CODE_NOT_WHITESPACE: return zpl_re__strfind(ZPL_RE__LITERAL(ZPL_RE__WHITESPACE), c, 0) < 0; + case ZPL_RE_CODE_DIGIT: return (c >= '0' && c <= '9'); + case ZPL_RE_CODE_NOT_DIGIT: return !(c >= '0' && c <= '9'); + case ZPL_RE_CODE_ALPHA: return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'); + case ZPL_RE_CODE_LOWER: return (c >= 'a' && c <= 'z'); + case ZPL_RE_CODE_UPPER: return (c >= 'A' && c <= 'Z'); + + /* TODO(bill): Make better? */ + case ZPL_RE_CODE_WORD: return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '_'; + case ZPL_RE_CODE_NOT_WORD: return !((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '_'); + + /* TODO(bill): Maybe replace with between tests? */ + case ZPL_RE_CODE_XDIGIT: return zpl_re__strfind(ZPL_RE__LITERAL("0123456789ABCDEFabcdef"), c, 0) >= 0; + case ZPL_RE_CODE_PRINTABLE: return c >= 0x20 && c <= 0x7e; + default: break; + } + + return 0; + } + + static zpl_re_ctx zpl_re__consume(zpl_re *re, zpl_isize op, char const *str, zpl_isize str_len, zpl_isize offset, zpl_re_capture *captures, zpl_isize max_capture_count, zpl_b32 is_greedy) + { + zpl_re_ctx c, best_c, next_c; + + c.op = op; + c.offset = offset; + + best_c.op = ZPL_RE__NO_MATCH; + best_c.offset = offset; + + for (;;) { + c = zpl_re__exec_single(re, op, str, str_len, c.offset, 0, 0); + if (c.offset > str_len || c.offset == -1) break; + if (c.op >= re->buf_len) return c; + + next_c = zpl_re__exec(re, c.op, str, str_len, c.offset, captures, max_capture_count); + if (next_c.offset <= str_len) { + if (captures) + zpl_re__exec(re, c.op, str, str_len, c.offset, captures, max_capture_count); + + best_c = next_c; + if (!is_greedy) break; + } + + if (best_c.op > re->buf_len) + best_c.op = c.op; + + } + + return best_c; + } + + static zpl_re_ctx zpl_re__exec_single(zpl_re *re, zpl_isize op, char const *str, zpl_isize str_len, zpl_isize offset, zpl_re_capture *captures, zpl_isize max_capture_count) { + zpl_re_ctx ctx; + zpl_isize buffer_len; + zpl_isize match_len; + zpl_isize next_op; + zpl_isize skip; + + switch (re->buf[op++]) { + case ZPL_RE_OP_BEGIN_CAPTURE: { + zpl_u8 capture = re->buf[op++]; + if (captures && (capture < max_capture_count)) + captures[capture].str = str + offset; + } break; + + case ZPL_RE_OP_END_CAPTURE: { + zpl_u8 capture = re->buf[op++]; + if (captures && (capture < max_capture_count)) + captures[capture].len = (str + offset) - captures[capture].str; + } break; + + case ZPL_RE_OP_BEGINNING_OF_LINE: { + if (offset != 0) + return zpl_re__ctx_no_match(op); + } break; + + case ZPL_RE_OP_END_OF_LINE: { + if (offset != str_len) + return zpl_re__ctx_no_match(op); + } break; + + case ZPL_RE_OP_BRANCH_START: { + skip = re->buf[op++]; + ctx = zpl_re__exec(re, op, str, str_len, offset, captures, max_capture_count); + if (ctx.offset <= str_len) { + offset = ctx.offset; + op = ctx.op; + } else { + ctx = zpl_re__exec(re, op + skip, str, str_len, offset, captures, max_capture_count); + offset = ctx.offset; + op = ctx.op; + } + } break; + + case ZPL_RE_OP_BRANCH_END: { + skip = re->buf[op++]; + op += skip; + } break; + + case ZPL_RE_OP_ANY: { + if (offset < str_len) { + offset++; + break; + } + return zpl_re__ctx_no_match(op); + } break; + + case ZPL_RE_OP_ANY_OF: { + zpl_isize i; + char cin = str[offset]; + buffer_len = re->buf[op++]; + + if (offset >= str_len) + return zpl_re__ctx_no_match(op + buffer_len); + + for (i = 0; i < buffer_len; i++) { + char cmatch = (char)re->buf[op+i]; + if (!cmatch) { + i++; + if (zpl_re__match_escape(cin, re->buf[op+i] << 8)) + break; + } else if (cin == cmatch) { + break; + } + } + + if (i == buffer_len) + return zpl_re__ctx_no_match(op + buffer_len); + + offset++; + op += buffer_len; + } break; + + case ZPL_RE_OP_ANY_BUT: { + zpl_isize i; + char cin = str[offset]; + buffer_len = re->buf[op++]; + + if (offset >= str_len) + return zpl_re__ctx_no_match(op + buffer_len); + + for (i = 0; i < buffer_len; i++) { + char cmatch = (char)re->buf[op + i]; + if (!cmatch) { + i++; + if (zpl_re__match_escape(cin, re->buf[op+i] << 8)) + return zpl_re__ctx_no_match(op + buffer_len); + } else if (cin == cmatch) { + return zpl_re__ctx_no_match(op + buffer_len); + } + } + + offset++; + op += buffer_len; + } break; + + case ZPL_RE_OP_EXACT_MATCH: { + match_len = re->buf[op++]; + + if ((match_len > (str_len - offset)) || + zpl_strncmp(str+offset, (const char*)re->buf + op, match_len) != 0) + return zpl_re__ctx_no_match(op + match_len); + + op += match_len; + offset += match_len; + } break; + + case ZPL_RE_OP_META_MATCH: { + char cin = (char)re->buf[op++]; + char cmatch = str[offset++]; + + if (!cin) { + if (zpl_re__match_escape(cmatch, re->buf[op++] << 8)) + break; + } + else if (cin == cmatch) break; + + return zpl_re__ctx_no_match(op); + } break; + + case ZPL_RE_OP_ZERO_OR_MORE: { + ctx = zpl_re__consume(re, op, str, str_len, offset, captures, max_capture_count, 1); + offset = ctx.offset; + op = ctx.op; + } break; + + case ZPL_RE_OP_ONE_OR_MORE: { + ctx = zpl_re__exec_single(re, op, str, str_len, offset, captures, max_capture_count); + + if (ctx.offset > str_len) + return ctx; + + ctx = zpl_re__consume(re, op, str, str_len, offset, captures, max_capture_count, 1); + offset = ctx.offset; + op = ctx.op; + } break; + + case ZPL_RE_OP_ZERO_OR_MORE_SHORTEST: { + ctx = zpl_re__consume(re, op, str, str_len, offset, captures, max_capture_count, 0); + offset = ctx.offset; + op = ctx.op; + } break; + + case ZPL_RE_OP_ONE_OR_MORE_SHORTEST: { + ctx = zpl_re__exec_single(re, op, str, str_len, offset, captures, max_capture_count); + + if (ctx.offset > str_len) + return ctx; + + ctx = zpl_re__consume(re, op, str, str_len, offset, captures, max_capture_count, 0); + offset = ctx.offset; + op = ctx.op; + } break; + + case ZPL_RE_OP_ZERO_OR_ONE: { + ctx = zpl_re__exec_single(re, op, str, str_len, offset, captures, max_capture_count); + + if (ctx.offset <= str_len) { + zpl_re_ctx possible_ctx = zpl_re__exec(re, ctx.op, str, str_len, ctx.offset, captures, max_capture_count); + + if (possible_ctx.offset <= str_len) { + op = possible_ctx.op; + offset = possible_ctx.offset; + break; + } + } + + next_op = ctx.op; + ctx = zpl_re__exec(re, next_op, str, str_len, offset, captures, max_capture_count); + + if (ctx.offset <= str_len) { + op = ctx.op; + offset = ctx.offset; + break; + } + return zpl_re__ctx_no_match(op); + } break; + + default: { + return zpl_re__ctx_internal_failure(op); + } break; + } + + ctx.op = op; + ctx.offset = offset; + + return ctx; + } + + static zpl_re_ctx zpl_re__exec(zpl_re *re, zpl_isize op, char const *str, zpl_isize str_len, zpl_isize offset, zpl_re_capture *captures, zpl_isize max_capture_count) { + zpl_re_ctx c; + c.op = op; + c.offset = offset; + + while (c.op < re->buf_len) { + c = zpl_re__exec_single(re, c.op, str, str_len, c.offset, captures, max_capture_count); + + if (c.offset > str_len || c.offset == -1) + break; + } + + return c; + } + + static zpl_regex_error zpl_re__emit_ops(zpl_re *re, zpl_isize op_count, ...) { + va_list va; + + if (re->buf_len + op_count > re->buf_cap) { + if (!re->can_realloc) { + return ZPL_RE_ERROR_TOO_LONG; + } + else { + zpl_isize new_cap = (re->buf_cap*2) + op_count; + re->buf = (char *)zpl_resize(re->backing, re->buf, re->buf_cap, new_cap); + re->buf_cap = new_cap; + } + } + + va_start(va, op_count); + for (zpl_isize i = 0; i < op_count; i++) + { + zpl_i32 v = va_arg(va, zpl_i32); + if (v > 256) + return ZPL_RE_ERROR_TOO_LONG; + re->buf[re->buf_len++] = (char)v; + } + va_end(va); + + return ZPL_RE_ERROR_NONE; + } + + static zpl_regex_error zpl_re__emit_ops_buffer(zpl_re *re, zpl_isize op_count, char const *buffer) { + if (re->buf_len + op_count > re->buf_cap) { + if (!re->can_realloc) { + return ZPL_RE_ERROR_TOO_LONG; + } + else { + zpl_isize new_cap = (re->buf_cap*2) + op_count; + re->buf = (char *)zpl_resize(re->backing, re->buf, re->buf_cap, new_cap); + re->buf_cap = new_cap; + } + } + + for (zpl_isize i = 0; i < op_count; i++) + { + re->buf[re->buf_len++] = buffer[i]; + } + + return ZPL_RE_ERROR_NONE; + } + + static int zpl_re__encode_escape(char code) { + switch (code) { + default: break; /* NOTE(bill): It's a normal character */ + + /* TODO(bill): Are there anymore? */ + case 't': return '\t'; + case 'n': return '\n'; + case 'r': return '\r'; + case 'f': return '\f'; + case 'v': return '\v'; + + case '0': return ZPL_RE_CODE_NULL; + + case 's': return ZPL_RE_CODE_WHITESPACE; + case 'S': return ZPL_RE_CODE_NOT_WHITESPACE; + + case 'd': return ZPL_RE_CODE_DIGIT; + case 'D': return ZPL_RE_CODE_NOT_DIGIT; + + case 'a': return ZPL_RE_CODE_ALPHA; + case 'l': return ZPL_RE_CODE_LOWER; + case 'u': return ZPL_RE_CODE_UPPER; + + case 'w': return ZPL_RE_CODE_WORD; + case 'W': return ZPL_RE_CODE_NOT_WORD; + + case 'x': return ZPL_RE_CODE_XDIGIT; + case 'p': return ZPL_RE_CODE_PRINTABLE; + } + return code; + } + + static zpl_regex_error zpl_re__parse_group(zpl_re *re, char const *pattern, zpl_isize len, zpl_isize offset, zpl_isize *new_offset) { + zpl_regex_error err = ZPL_RE_ERROR_NONE; + char buffer[256] = {0}; + zpl_isize buffer_len = 0, buffer_cap = zpl_size_of(buffer); + zpl_b32 closed = 0; + zplreOp op = ZPL_RE_OP_ANY_OF; + + if (pattern[offset] == '^') { + offset++; + op = ZPL_RE_OP_ANY_BUT; + } + + while(!closed && + err == ZPL_RE_ERROR_NONE && + offset < len) + { + if (pattern[offset] == ']') { + err = zpl_re__emit_ops(re, 2, (zpl_i32)op, (zpl_i32)buffer_len); + if (err) break; + + err = zpl_re__emit_ops_buffer(re, buffer_len, (const char*)buffer); + if (err) break; + offset++; + closed = 1; + break; + } + + if (buffer_len >= buffer_cap) + return ZPL_RE_ERROR_TOO_LONG; + + if (pattern[offset] == '\\') { + offset++; + + if ((offset + 1 < len) && zpl_char_is_hex_digit(*(pattern+offset))) { + buffer[buffer_len++] = zpl_re__hex((pattern+offset)); + offset++; + } + else if (offset < len) { + zpl_i32 code = zpl_re__encode_escape(pattern[offset]); + + if (!code || code > 0xff) { + buffer[buffer_len++] = 0; + + if (buffer_len >= buffer_cap) + return ZPL_RE_ERROR_TOO_LONG; + + buffer[buffer_len++] = (code >> 8) & 0xff; + } + else { + buffer[buffer_len++] = code & 0xff; + } + } + } + else { + buffer[buffer_len++] = (unsigned char)pattern[offset]; + } + + offset++; + } + + if (err) return err; + if (!closed) return ZPL_RE_ERROR_MISMATCHED_BLOCKS; + if (new_offset) *new_offset = offset; + return ZPL_RE_ERROR_NONE; + } + + static zpl_regex_error zpl_re__compile_quantifier(zpl_re *re, zpl_isize last_buf_len, unsigned char quantifier) { + zpl_regex_error err; + zpl_isize move_size; + + if ((re->buf[last_buf_len] == ZPL_RE_OP_EXACT_MATCH) && + (re->buf[last_buf_len+1] > 1)) + { + unsigned char last_char = re->buf[re->buf_len-1]; + + re->buf[last_buf_len+1]--; + re->buf_len--; + err = zpl_re__emit_ops(re, 4, (zpl_i32)quantifier, (zpl_i32)ZPL_RE_OP_EXACT_MATCH, 1, (zpl_i32)last_char); + if (err) return err; + return ZPL_RE_ERROR_NONE; + } + + move_size = re->buf_len - last_buf_len + 1; + + err = zpl_re__emit_ops(re, 1, 0); + if (err) return err; + + zpl_memmove(re->buf+last_buf_len+1, re->buf+last_buf_len, move_size); + re->buf[last_buf_len] = quantifier; + + return ZPL_RE_ERROR_NONE; + } + + static zpl_regex_error zpl_re__parse(zpl_re *re, char const *pattern, zpl_isize len, zpl_isize offset, zpl_isize level, zpl_isize *new_offset) { + zpl_regex_error err = ZPL_RE_ERROR_NONE; + zpl_isize last_buf_len = re->buf_len; + zpl_isize branch_begin = re->buf_len; + zpl_isize branch_op = -1; + + while (offset < len) { + switch (pattern[offset++]) { + case '^': { + err = zpl_re__emit_ops(re, 1, ZPL_RE_OP_BEGINNING_OF_LINE); + if (err) return err; + } break; + + case '$': { + err = zpl_re__emit_ops(re, 1, ZPL_RE_OP_END_OF_LINE); + if (err) return err; + } break; + + case '(': { + zpl_isize capture = re->capture_count++; + last_buf_len = re->buf_len; + err = zpl_re__emit_ops(re, 2, ZPL_RE_OP_BEGIN_CAPTURE, (zpl_i32)capture); + if (err) return err; + + err = zpl_re__parse(re, pattern, len, offset, level+1, &offset); + + if ((offset > len) || (pattern[offset-1] != ')')) + return ZPL_RE_ERROR_MISMATCHED_CAPTURES; + + err = zpl_re__emit_ops(re, 2, ZPL_RE_OP_END_CAPTURE, (zpl_i32)capture); + if (err) return err; + } break; + + case ')': { + if (branch_op != -1) + re->buf[branch_op + 1] = (unsigned char)(re->buf_len - (branch_op+2)); + + if (level == 0) + return ZPL_RE_ERROR_MISMATCHED_CAPTURES; + + if (new_offset) *new_offset = offset; + return ZPL_RE_ERROR_NONE; + } break; + + case '[': { + last_buf_len = re->buf_len; + err = zpl_re__parse_group(re, pattern, len, offset, &offset); + if (offset > len) + return err; + } break; + + /* NOTE(bill): Branching magic! */ + case '|': { + if (branch_begin >= re->buf_len) { + return ZPL_RE_ERROR_BRANCH_FAILURE; + } else { + zpl_isize size = re->buf_len - branch_begin; + err = zpl_re__emit_ops(re, 4, 0, 0, ZPL_RE_OP_BRANCH_END, 0); + if (err) return err; + + zpl_memmove(re->buf + branch_begin + 2, re->buf + branch_begin, size); + re->buf[branch_begin] = ZPL_RE_OP_BRANCH_START; + re->buf[branch_begin+1] = (size+2) & 0xff; + branch_op = re->buf_len-2; + } + } break; + + case '.': { + last_buf_len = re->buf_len; + err = zpl_re__emit_ops(re, 1, ZPL_RE_OP_ANY); + if (err) return err; + } break; + + case '*': + case '+': + { + unsigned char quantifier = ZPL_RE_OP_ONE_OR_MORE; + if (pattern[offset-1] == '*') + quantifier = ZPL_RE_OP_ZERO_OR_MORE; + + if (last_buf_len >= re->buf_len) + return ZPL_RE_ERROR_INVALID_QUANTIFIER; + if ((re->buf[last_buf_len] < ZPL_RE_OP_EXACT_MATCH) || + (re->buf[last_buf_len] > ZPL_RE_OP_ANY_BUT)) + return ZPL_RE_ERROR_INVALID_QUANTIFIER; + + if ((offset < len) && (pattern[offset] == '?')) { + quantifier = ZPL_RE_OP_ONE_OR_MORE_SHORTEST; + offset++; + } + + err = zpl_re__compile_quantifier(re, last_buf_len, quantifier); + if (err) return err; + } break; + + case '?': { + if (last_buf_len >= re->buf_len) + return ZPL_RE_ERROR_INVALID_QUANTIFIER; + if ((re->buf[last_buf_len] < ZPL_RE_OP_EXACT_MATCH) || + (re->buf[last_buf_len] > ZPL_RE_OP_ANY_BUT)) + return ZPL_RE_ERROR_INVALID_QUANTIFIER; + + err = zpl_re__compile_quantifier(re, last_buf_len, + (unsigned char)ZPL_RE_OP_ZERO_OR_ONE); + if (err) return err; + } break; + + case '\\': { + last_buf_len = re->buf_len; + if ((offset+1 < len) && zpl_char_is_hex_digit(*(pattern+offset))) { + unsigned char hex_value = zpl_re__hex((pattern+offset)); + offset += 2; + err = zpl_re__emit_ops(re, 2, ZPL_RE_OP_META_MATCH, (int)hex_value); + if (err) return err; + } else if (offset < len) { + int code = zpl_re__encode_escape(pattern[offset++]); + if (!code || (code > 0xff)) { + err = zpl_re__emit_ops(re, 3, ZPL_RE_OP_META_MATCH, 0, (int)((code >> 8) & 0xff)); + if (err) return err; + } else { + err = zpl_re__emit_ops(re, 2, ZPL_RE_OP_META_MATCH, (int)code); + if (err) return err; + } + } + } break; + + /* NOTE(bill): Exact match */ + default: { + char const *match_start; + zpl_isize size = 0; + offset--; + match_start = pattern+offset; + while ((offset < len) && + (zpl_re__strfind(ZPL_RE__LITERAL(ZPL_RE__META_CHARS), pattern[offset], 0) < 0)) { + size++, offset++; + } + + last_buf_len = re->buf_len; + err = zpl_re__emit_ops(re, 2, ZPL_RE_OP_EXACT_MATCH, (int)size); + if (err) return err; + err = zpl_re__emit_ops_buffer(re, size, (char const *)match_start); + if (err) return err; + } break; + } + } + + if (new_offset) *new_offset = offset; + return ZPL_RE_ERROR_NONE; + } + + zpl_regex_error zpl_re_compile_from_buffer(zpl_re *re, char const *pattern, zpl_isize pattern_len, void *buffer, zpl_isize buffer_len) { + zpl_regex_error err; + re->capture_count = 0; + re->buf = (char *)buffer; + re->buf_len = 0; + re->buf_cap = re->buf_len; + re->can_realloc = 0; + + err = zpl_re__parse(re, pattern, pattern_len, 0, 0, 0); + return err; + } + + zpl_regex_error zpl_re_compile(zpl_re *re, zpl_allocator backing, char const *pattern, zpl_isize pattern_len) { + zpl_regex_error err; + zpl_isize cap = pattern_len+128; + zpl_isize offset = 0; + + re->backing = backing; + re->capture_count = 0; + re->buf = (char *)zpl_alloc(backing, cap); + re->buf_len = 0; + re->buf_cap = cap; + re->can_realloc = 1; + + err = zpl_re__parse(re, pattern, pattern_len, 0, 0, &offset); + + if (offset != pattern_len) + zpl_free(backing, re->buf); + + return err; + } + + zpl_isize zpl_re_capture_count(zpl_re *re) { return re->capture_count; } + + zpl_b32 zpl_re_match(zpl_re *re, char const *str, zpl_isize len, zpl_re_capture *captures, zpl_isize max_capture_count, zpl_isize *offset) { + if (re && re->buf_len > 0) { + if (re->buf[0] == ZPL_RE_OP_BEGINNING_OF_LINE) { + zpl_re_ctx c = zpl_re__exec(re, 0, str, len, 0, captures, max_capture_count); + if (c.offset >= 0 && c.offset <= len) { if (offset) *offset = c.offset; return 1; }; + if (c.offset == ZPL_RE__INTERNAL_FAILURE) return 0; + } else { + zpl_isize i; + for (i = 0; i < len; i++) { + zpl_re_ctx c = zpl_re__exec(re, 0, str, len, i, captures, max_capture_count); + if (c.offset >= 0 && c.offset <= len) { if (offset) *offset = c.offset; return 1; }; + if (c.offset == ZPL_RE__INTERNAL_FAILURE) return 0; + } + } + return 0; + } + return 1; + } + + + zpl_b32 zpl_re_match_all(zpl_re *re, char const *str, zpl_isize str_len, zpl_isize max_capture_count, + zpl_re_capture **out_captures) + { + char *end = (char *)str + str_len; + char *p = (char *)str; + + zpl_buffer_make(zpl_re_capture, cps, zpl_heap(), max_capture_count); + + zpl_isize offset = 0; + + while (p < end) + { + zpl_b32 ok = zpl_re_match(re, p, end - p, cps, max_capture_count, &offset); + if (!ok) { + zpl_buffer_free(cps); + return false; + } + + p += offset; + + for (zpl_isize i = 0; i < max_capture_count; i++) { + zpl_array_append(*out_captures, cps[i]); + } + } + + zpl_buffer_free(cps); + + return true; + } + + ZPL_END_C_DECLS +#endif + +#if defined(ZPL_MODULE_DLL) + // file: source/dll.c + + + #if defined(ZPL_SYSTEM_UNIX) || defined(ZPL_SYSTEM_MACOS) + # include + #endif + + ZPL_BEGIN_C_DECLS + + //////////////////////////////////////////////////////////////// + // + // DLL Handling + // + // + + #if defined(ZPL_SYSTEM_WINDOWS) + zpl_dll_handle zpl_dll_load(char const *filepath) { + return cast(zpl_dll_handle) LoadLibraryA(filepath); + } + + void zpl_dll_unload(zpl_dll_handle dll) { + FreeLibrary(cast(HMODULE) dll); + } + + zpl_dll_proc zpl_dll_proc_address(zpl_dll_handle dll, char const *proc_name) { + return cast(zpl_dll_proc) GetProcAddress(cast(HMODULE) dll, proc_name); + } + + #else // POSIX + + zpl_dll_handle zpl_dll_load(char const *filepath) { + return cast(zpl_dll_handle) dlopen(filepath, RTLD_LAZY | RTLD_GLOBAL); + } + + void zpl_dll_unload(zpl_dll_handle dll) { + dlclose(dll); + } + + zpl_dll_proc zpl_dll_proc_address(zpl_dll_handle dll, char const *proc_name) { + return cast(zpl_dll_proc) dlsym(dll, proc_name); + } + + #endif + + ZPL_END_C_DECLS +#endif + +#if defined(ZPL_MODULE_OPTS) + // file: source/opts.c + + //////////////////////////////////////////////////////////////// + // + // CLI Options + // + // + + ZPL_BEGIN_C_DECLS + + void zpl_opts_init(zpl_opts *opts, zpl_allocator a, char const *app) { + zpl_opts opts_ = { 0 }; + *opts = opts_; + opts->alloc = a; + opts->appname = app; + + zpl_array_init(opts->entries, a); + zpl_array_init(opts->positioned, a); + zpl_array_init(opts->errors, a); + } + + void zpl_opts_free(zpl_opts *opts) { + for (zpl_i32 i = 0; i < zpl_array_count(opts->entries); ++i) { + zpl_opts_entry *e = opts->entries + i; + if (e->type == ZPL_OPTS_STRING) { + zpl_string_free(e->text); + } + } + + zpl_array_free(opts->entries); + zpl_array_free(opts->positioned); + zpl_array_free(opts->errors); + } + + void zpl_opts_add(zpl_opts *opts, char const *name, char const *lname, const char *desc, zpl_u8 type) { + zpl_opts_entry e = { 0 }; + + e.name = name; + e.lname = lname; + e.desc = desc; + e.type = type; + e.met = false; + e.pos = false; + + zpl_array_append(opts->entries, e); + } + + zpl_opts_entry *zpl__opts_find(zpl_opts *opts, char const *name, zpl_usize len, zpl_b32 longname) { + zpl_opts_entry *e = 0; + + for (int i = 0; i < zpl_array_count(opts->entries); ++i) { + e = opts->entries + i; + char const *n = (longname ? e->lname : e->name); + if(!n) continue; + + if (zpl_strnlen(name, len) == zpl_strlen(n) && !zpl_strncmp(n, name, len)) { return e; } + } + + return NULL; + } + + void zpl_opts_positional_add(zpl_opts *opts, char const *name) { + zpl_opts_entry *e = zpl__opts_find(opts, name, zpl_strlen(name), true); + + if (e) { + e->pos = true; + zpl_array_append_at(opts->positioned, e, 0); + } + } + + zpl_b32 zpl_opts_positionals_filled(zpl_opts *opts) { return zpl_array_count(opts->positioned) == 0; } + + zpl_string zpl_opts_string(zpl_opts *opts, char const *name, char const *fallback) { + zpl_opts_entry *e = zpl__opts_find(opts, name, zpl_strlen(name), true); + + return (char *)((e && e->met) ? e->text : fallback); + } + + zpl_f64 zpl_opts_real(zpl_opts *opts, char const *name, zpl_f64 fallback) { + zpl_opts_entry *e = zpl__opts_find(opts, name, zpl_strlen(name), true); + + return (e && e->met) ? e->real : fallback; + } + + zpl_i64 zpl_opts_integer(zpl_opts *opts, char const *name, zpl_i64 fallback) { + zpl_opts_entry *e = zpl__opts_find(opts, name, zpl_strlen(name), true); + + return (e && e->met) ? e->integer : fallback; + } + + void zpl__opts_set_value(zpl_opts *opts, zpl_opts_entry *t, char *b) { + t->met = true; + + switch (t->type) { + case ZPL_OPTS_STRING: { + t->text = zpl_string_make(opts->alloc, b); + } break; + + case ZPL_OPTS_FLOAT: { + t->real = zpl_str_to_f64(b, NULL); + } break; + + case ZPL_OPTS_INT: { + t->integer = zpl_str_to_i64(b, NULL, 10); + } break; + } + + for (zpl_isize i=0; i < zpl_array_count(opts->positioned); i++) { + if (!zpl_strcmp(opts->positioned[i]->lname, t->lname)) { + zpl_array_remove_at(opts->positioned, i); + break; + } + } + } + + zpl_b32 zpl_opts_has_arg(zpl_opts *opts, char const *name) { + zpl_opts_entry *e = zpl__opts_find(opts, name, zpl_strlen(name), true); + + if (e) { return e->met; } + + return false; + } + + void zpl_opts_print_help(zpl_opts *opts) { + zpl_printf("USAGE: %s", opts->appname); + + for (zpl_isize i = zpl_array_count(opts->entries); i >= 0; --i) { + zpl_opts_entry *e = opts->entries + i; + + if (e->pos == (zpl_b32) true) { zpl_printf(" [%s]", e->lname); } + } + + zpl_printf("\nOPTIONS:\n"); + + for (zpl_isize i = 0; i < zpl_array_count(opts->entries); ++i) { + zpl_opts_entry *e = opts->entries + i; + + if(e->name) { + if(e->lname) { zpl_printf("\t-%s, --%s: %s\n", e->name, e->lname, e->desc); } + else { zpl_printf("\t-%s: %s\n", e->name, e->desc); } + } else { zpl_printf("\t--%s: %s\n", e->lname, e->desc); } + } + } + + void zpl_opts_print_errors(zpl_opts *opts) { + for (int i = 0; i < zpl_array_count(opts->errors); ++i) { + zpl_opts_err *err = (opts->errors + i); + + zpl_printf("ERROR: "); + + switch (err->type) { + case ZPL_OPTS_ERR_OPTION: zpl_printf("Invalid option \"%s\"", err->val); break; + + case ZPL_OPTS_ERR_VALUE: zpl_printf("Invalid value \"%s\"", err->val); break; + + case ZPL_OPTS_ERR_MISSING_VALUE: zpl_printf("Missing value for option \"%s\"", err->val); break; + + case ZPL_OPTS_ERR_EXTRA_VALUE: zpl_printf("Extra value for option \"%s\"", err->val); break; + } + + zpl_printf("\n"); + } + } + + void zpl__opts_push_error(zpl_opts *opts, char *b, zpl_u8 errtype) { + zpl_opts_err err = { 0 }; + err.val = b; + err.type = errtype; + zpl_array_append(opts->errors, err); + } + + zpl_b32 zpl_opts_compile(zpl_opts *opts, int argc, char **argv) { + zpl_b32 had_errors = false; + for (int i = 1; i < argc; ++i) { + char *p = argv[i]; + + if (*p) { + p = cast(char *)zpl_str_trim(p, false); + if (*p == '-') { + zpl_opts_entry *t = 0; + zpl_b32 checkln = false; + if (*(p + 1) == '-') { + checkln = true; + ++p; + } + + char *b = p + 1, *e = b; + + while (zpl_char_is_alphanumeric(*e) || *e == '-' || *e == '_') { ++e; } + + t = zpl__opts_find(opts, b, (e - b), checkln); + + if (t) { + char *ob = b; + b = e; + + /**/ if (*e == '=') { + if (t->type == ZPL_OPTS_FLAG) { + *e = '\0'; + zpl__opts_push_error(opts, ob, ZPL_OPTS_ERR_EXTRA_VALUE); + had_errors = true; + continue; + } + + b = e = e + 1; + } else if (*e == '\0') { + char *sp = argv[i+1]; + + if (sp && *sp != '-' && (zpl_array_count(opts->positioned) < 1 || t->type != ZPL_OPTS_FLAG)) { + if (t->type == ZPL_OPTS_FLAG) { + zpl__opts_push_error(opts, b, ZPL_OPTS_ERR_EXTRA_VALUE); + had_errors = true; + continue; + } + + p = sp; + b = e = sp; + ++i; + } else { + if (t->type != ZPL_OPTS_FLAG) { + zpl__opts_push_error(opts, ob, ZPL_OPTS_ERR_MISSING_VALUE); + had_errors = true; + continue; + } + t->met = true; + continue; + } + } + + e = cast(char *)zpl_str_control_skip(e, '\0'); + zpl__opts_set_value(opts, t, b); + } else { + zpl__opts_push_error(opts, b, ZPL_OPTS_ERR_OPTION); + had_errors = true; + } + } else if (zpl_array_count(opts->positioned)) { + zpl_opts_entry *l = zpl_array_back(opts->positioned); + zpl_array_pop(opts->positioned); + zpl__opts_set_value(opts, l, p); + } else { + zpl__opts_push_error(opts, p, ZPL_OPTS_ERR_VALUE); + had_errors = true; + } + } + } + return !had_errors; + } + + ZPL_END_C_DECLS +#endif + +#if defined(ZPL_MODULE_PROCESS) + // file: source/process.c + + //////////////////////////////////////////////////////////////// + // + // Process creation and manipulation methods + // + // + + ZPL_BEGIN_C_DECLS + + static ZPL_ALWAYS_INLINE void zpl__pr_close_file_handle(zpl_file *f) { + ZPL_ASSERT_NOT_NULL(f); + f->fd.p = NULL; + } + + static ZPL_ALWAYS_INLINE void zpl__pr_close_file_handles(zpl_pr *process) { + ZPL_ASSERT_NOT_NULL(process); + + zpl__pr_close_file_handle(&process->in); + zpl__pr_close_file_handle(&process->out); + zpl__pr_close_file_handle(&process->err); + + process->f_stdin = process->f_stdout = process->f_stderr = NULL; + + #ifdef ZPL_SYSTEM_WINDOWS + process->win32_handle = NULL; + #else + ZPL_NOT_IMPLEMENTED; + #endif + } + + enum { + ZPL_PR_HANDLE_MODE_READ, + ZPL_PR_HANDLE_MODE_WRITE, + ZPL_PR_HANDLE_MODES, + }; + + void *zpl__pr_open_handle(zpl_u8 type, const char *mode, void **handle) { + #ifdef ZPL_SYSTEM_WINDOWS + void *pipes[ZPL_PR_HANDLE_MODES]; + zpl_i32 fd; + + const zpl_u32 flag_inherit = 0x00000001; + SECURITY_ATTRIBUTES sa = {zpl_size_of(sa), 0, 1}; + + if (!CreatePipe(&pipes[0], &pipes[1], cast(LPSECURITY_ATTRIBUTES)&sa, 0)) { + return NULL; + } + + if (!SetHandleInformation(pipes[type], flag_inherit, 0)) { + return NULL; + } + + fd = _open_osfhandle(cast(zpl_intptr)pipes[type], 0); + + if (fd != -1) { + *handle = pipes[1-type]; + return _fdopen(fd, mode); + } + + return NULL; + #else + ZPL_NOT_IMPLEMENTED; + return NULL; + #endif + } + + zpl_i32 zpl_pr_create(zpl_pr *process, const char **args, zpl_isize argc, zpl_pr_si si, zpl_pr_opts options) { + ZPL_ASSERT_NOT_NULL(process); + zpl_zero_item(process); + + #ifdef ZPL_SYSTEM_WINDOWS + zpl_string cli, env; + zpl_b32 c_env=false; + STARTUPINFOW psi = {0}; + PROCESS_INFORMATION pi = {0}; + zpl_i32 err_code = 0; + zpl_allocator a = zpl_heap(); + const zpl_u32 use_std_handles = 0x00000100; + + psi.cb = zpl_size_of(psi); + psi.dwFlags = use_std_handles | si.flags; + + if (options & ZPL_PR_OPTS_CUSTOM_ENV) { + env = zpl_string_join(zpl_heap(), cast(const char**)si.env, si.env_count, "\0\0"); + env = zpl_string_appendc(env, "\0"); + c_env = true; + } + else if (!(options & ZPL_PR_OPTS_INHERIT_ENV)) { + env = (zpl_string)"\0\0\0\0"; + } else { + env = (zpl_string)NULL; + } + + process->f_stdin = zpl__pr_open_handle(ZPL_PR_HANDLE_MODE_WRITE, "wb", &psi.hStdInput); + process->f_stdout = zpl__pr_open_handle(ZPL_PR_HANDLE_MODE_READ, "rb", &psi.hStdOutput); + + if (options & ZPL_PR_OPTS_COMBINE_STD_OUTPUT) { + process->f_stderr = process->f_stdout; + psi.hStdError = psi.hStdOutput; + } else { + process->f_stderr = zpl__pr_open_handle(ZPL_PR_HANDLE_MODE_READ, "rb", &psi.hStdError); + } + + cli = zpl_string_join(zpl_heap(), args, argc, " "); + + psi.dwX = si.posx; + psi.dwY = si.posy; + psi.dwXSize = si.resx; + psi.dwYSize = si.resy; + psi.dwXCountChars = si.bufx; + psi.dwYCountChars = si.bufy; + psi.dwFillAttribute = si.fill_attr; + psi.wShowWindow = si.show_window; + + wchar_t *w_cli = zpl__alloc_utf8_to_ucs2(a, cli, NULL); + wchar_t *w_workdir = zpl__alloc_utf8_to_ucs2(a, si.workdir, NULL); + + if (!CreateProcessW( + NULL, + w_cli, + NULL, + NULL, + 1, + 0, + env, + w_workdir, + cast(LPSTARTUPINFOW)&psi, + cast(LPPROCESS_INFORMATION)&pi + )) { + err_code = -1; + goto pr_free_data; + } + + process->win32_handle = pi.hProcess; + CloseHandle(pi.hThread); + + zpl_file_connect_handle(&process->in, process->f_stdin); + zpl_file_connect_handle(&process->out, process->f_stdout); + zpl_file_connect_handle(&process->err, process->f_stderr); + + pr_free_data: + zpl_string_free(cli); + zpl_free(a, w_cli); + zpl_free(a, w_workdir); + + if (c_env) + zpl_string_free(env); + + return err_code; + + #else + ZPL_NOT_IMPLEMENTED; + return -1; + #endif + } + + + zpl_i32 zpl_pr_join(zpl_pr *process) { + zpl_i32 ret_code; + + ZPL_ASSERT_NOT_NULL(process); + + #ifdef ZPL_SYSTEM_WINDOWS + if (process->f_stdin) { + fclose(cast(FILE *)process->f_stdin); + } + + WaitForSingleObject(process->win32_handle, INFINITE); + + if (!GetExitCodeProcess(process->win32_handle, cast(LPDWORD)&ret_code)) { + zpl_pr_destroy(process); + return -1; + } + + zpl_pr_destroy(process); + + return ret_code; + #else + ZPL_NOT_IMPLEMENTED; + ret_code = -1; + return ret_code; + #endif + } + + void zpl_pr_destroy(zpl_pr *process) { + ZPL_ASSERT_NOT_NULL(process); + + #ifdef ZPL_SYSTEM_WINDOWS + if (process->f_stdin) { + fclose(cast(FILE *)process->f_stdin); + } + + fclose(cast(FILE *)process->f_stdout); + + if (process->f_stderr != process->f_stdout) { + fclose(cast(FILE *)process->f_stderr); + } + + CloseHandle(process->win32_handle); + + zpl__pr_close_file_handles(process); + #else + ZPL_NOT_IMPLEMENTED; + #endif + } + + void zpl_pr_terminate(zpl_pr *process, zpl_i32 err_code) { + ZPL_ASSERT_NOT_NULL(process); + + #ifdef ZPL_SYSTEM_WINDOWS + TerminateProcess(process->win32_handle, cast(UINT)err_code); + zpl_pr_destroy(process); + #else + ZPL_NOT_IMPLEMENTED; + #endif + } + + ZPL_END_C_DECLS +#endif + +#if defined(ZPL_MODULE_MATH) + // file: source/math.c + + + #if defined(ZPL_COMPILER_TINYC) && defined(ZPL_NO_MATH_H) + #undef ZPL_NO_MATH_H + #endif + + #if !defined(ZPL_NO_MATH_H) + # include + #endif + + ZPL_BEGIN_C_DECLS + + //////////////////////////////////////////////////////////////// + // + // Math + // + + zpl_f32 zpl_to_radians(zpl_f32 degrees) { return degrees * ZPL_TAU / 360.0f; } + zpl_f32 zpl_to_degrees(zpl_f32 radians) { return radians * 360.0f / ZPL_TAU; } + + zpl_f32 zpl_angle_diff(zpl_f32 radians_a, zpl_f32 radians_b) { + zpl_f32 delta = zpl_mod(radians_b - radians_a, ZPL_TAU); + delta = zpl_mod(delta + 1.5f * ZPL_TAU, ZPL_TAU); + delta -= 0.5f * ZPL_TAU; + return delta; + } + + zpl_f32 zpl_copy_sign(zpl_f32 x, zpl_f32 y) { + zpl_i32 ix, iy; + zpl_f32 r; + zpl_memcopy(&ix, &x, zpl_size_of(x)); + zpl_memcopy(&iy, &y, zpl_size_of(y)); + + ix &= 0x7fffffff; + ix |= iy & 0x80000000; + zpl_memcopy(&r, &ix, zpl_size_of(ix)); + return r; + } + + zpl_f32 zpl_remainder(zpl_f32 x, zpl_f32 y) { return x - (zpl_round(x / y) * y); } + + zpl_f32 zpl_mod(zpl_f32 x, zpl_f32 y) { + zpl_f32 result; + y = zpl_abs(y); + result = zpl_remainder(zpl_abs(x), y); + if (zpl_sign(result) > 0.0f) result += y; + return zpl_copy_sign(result, x); + } + + zpl_f64 zpl_copy_sign64(zpl_f64 x, zpl_f64 y) { + zpl_i64 ix, iy; + zpl_f64 r; + zpl_memcopy(&ix, &x, zpl_size_of(x)); + zpl_memcopy(&iy, &y, zpl_size_of(y)); + + ix &= 0x7fffffffffffffff; + ix |= iy & 0x8000000000000000; + zpl_memcopy(&r, &ix, zpl_size_of(ix)); + return r; + } + + zpl_f64 zpl_floor64(zpl_f64 x) { return cast(zpl_f64)((x >= 0.0) ? cast(zpl_i64) x : cast(zpl_i64)(x - 0.9999999999999999)); } + zpl_f64 zpl_ceil64(zpl_f64 x) { return cast(zpl_f64)((x < 0) ? cast(zpl_i64) x : (cast(zpl_i64) x) + 1); } + zpl_f64 zpl_round64(zpl_f64 x) { return cast(zpl_f64)((x >= 0.0) ? zpl_floor64(x + 0.5) : zpl_ceil64(x - 0.5)); } + zpl_f64 zpl_remainder64(zpl_f64 x, zpl_f64 y) { return x - (zpl_round64(x / y) * y); } + zpl_f64 zpl_abs64(zpl_f64 x) { return x < 0 ? -x : x; } + zpl_f64 zpl_sign64(zpl_f64 x) { return x < 0 ? -1.0 : +1.0; } + + zpl_f64 zpl_mod64(zpl_f64 x, zpl_f64 y) { + zpl_f64 result; + y = zpl_abs64(y); + result = zpl_remainder64(zpl_abs64(x), y); + if (zpl_sign64(result)) result += y; + return zpl_copy_sign64(result, x); + } + + zpl_f32 zpl_quake_rsqrt(zpl_f32 a) { + union { + int i; + zpl_f32 f; + } t; + zpl_f32 x2; + zpl_f32 const three_halfs = 1.5f; + + x2 = a * 0.5f; + t.f = a; + t.i = 0x5f375a86 - (t.i >> 1); /* What the fuck? */ + t.f = t.f * (three_halfs - (x2 * t.f * t.f)); /* 1st iteration */ + t.f = t.f * (three_halfs - (x2 * t.f * t.f)); /* 2nd iteration, this can be removed */ + + return t.f; + } + + #if defined(ZPL_NO_MATH_H) + # if defined(_MSC_VER) + + zpl_f32 zpl_rsqrt(zpl_f32 a) { return _mm_cvtss_f32(_mm_rsqrt_ss(_mm_set_ss(a))); } + zpl_f32 zpl_sqrt(zpl_f32 a) { return _mm_cvtss_f32(_mm_sqrt_ss(_mm_set_ss(a))); }; + + zpl_f32 zpl_sin(zpl_f32 a) { + static zpl_f32 const a0 = +1.91059300966915117e-31f; + static zpl_f32 const a1 = +1.00086760103908896f; + static zpl_f32 const a2 = -1.21276126894734565e-2f; + static zpl_f32 const a3 = -1.38078780785773762e-1f; + static zpl_f32 const a4 = -2.67353392911981221e-2f; + static zpl_f32 const a5 = +2.08026600266304389e-2f; + static zpl_f32 const a6 = -3.03996055049204407e-3f; + static zpl_f32 const a7 = +1.38235642404333740e-4f; + return a0 + a * (a1 + a * (a2 + a * (a3 + a * (a4 + a * (a5 + a * (a6 + a * a7)))))); + } + + zpl_f32 zpl_cos(zpl_f32 a) { + static zpl_f32 const a0 = +1.00238601909309722f; + static zpl_f32 const a1 = -3.81919947353040024e-2f; + static zpl_f32 const a2 = -3.94382342128062756e-1f; + static zpl_f32 const a3 = -1.18134036025221444e-1f; + static zpl_f32 const a4 = +1.07123798512170878e-1f; + static zpl_f32 const a5 = -1.86637164165180873e-2f; + static zpl_f32 const a6 = +9.90140908664079833e-4f; + static zpl_f32 const a7 = -5.23022132118824778e-14f; + return a0 + a * (a1 + a * (a2 + a * (a3 + a * (a4 + a * (a5 + a * (a6 + a * a7)))))); + } + + zpl_f32 zpl_tan(zpl_f32 radians) { + zpl_f32 rr = radians * radians; + zpl_f32 a = 9.5168091e-03f; + a *= rr; + a += 2.900525e-03f; + a *= rr; + a += 2.45650893e-02f; + a *= rr; + a += 5.33740603e-02f; + a *= rr; + a += 1.333923995e-01f; + a *= rr; + a += 3.333314036e-01f; + a *= rr; + a += 1.0f; + a *= radians; + return a; + } + + zpl_f32 zpl_arcsin(zpl_f32 a) { return zpl_arctan2(a, zpl_sqrt((1.0f + a) * (1.0f - a))); } + zpl_f32 zpl_arccos(zpl_f32 a) { return zpl_arctan2(zpl_sqrt((1.0f + a) * (1.0f - a)), a); } + + zpl_f32 zpl_arctan(zpl_f32 a) { + zpl_f32 u = a * a; + zpl_f32 u2 = u * u; + zpl_f32 u3 = u2 * u; + zpl_f32 u4 = u3 * u; + zpl_f32 f = 1.0f + 0.33288950512027f * u - 0.08467922817644f * u2 + 0.03252232640125f * u3 - 0.00749305860992f * u4; + return a / f; + } + + zpl_f32 zpl_arctan2(zpl_f32 y, zpl_f32 x) { + if (zpl_abs(x) > zpl_abs(y)) { + zpl_f32 a = zpl_arctan(y / x); + if (x > 0.0f) + return a; + else + return y > 0.0f ? a + ZPL_TAU_OVER_2 : a - ZPL_TAU_OVER_2; + } else { + zpl_f32 a = zpl_arctan(x / y); + if (x > 0.0f) + return y > 0.0f ? ZPL_TAU_OVER_4 - a : -ZPL_TAU_OVER_4 - a; + else + return y > 0.0f ? ZPL_TAU_OVER_4 + a : -ZPL_TAU_OVER_4 + a; + } + } + + zpl_f32 zpl_exp(zpl_f32 a) { + union { + zpl_f32 f; + int i; + } u, v; + u.i = (int)(6051102 * a + 1056478197); + v.i = (int)(1056478197 - 6051102 * a); + return u.f / v.f; + } + + zpl_f32 zpl_log(zpl_f32 a) { + union { + zpl_f32 f; + int i; + } u = { a }; + return (u.i - 1064866805) * 8.262958405176314e-8f; /* 1 / 12102203.0; */ + } + + zpl_f32 zpl_pow(zpl_f32 a, zpl_f32 b) { + int flipped = 0, e; + zpl_f32 f, r = 1.0f; + if (b < 0) { + flipped = 1; + b = -b; + } + + e = (int)b; + f = zpl_exp(b - e); + + while (e) { + if (e & 1) r *= a; + a *= a; + e >>= 1; + } + + r *= f; + return flipped ? 1.0f / r : r; + } + # else + + zpl_f32 zpl_rsqrt(zpl_f32 a) { return 1.0f / __builtin_sqrt(a); } + zpl_f32 zpl_sqrt(zpl_f32 a) { return __builtin_sqrt(a); } + zpl_f32 zpl_sin(zpl_f32 radians) { return __builtin_sinf(radians); } + zpl_f32 zpl_cos(zpl_f32 radians) { return __builtin_cosf(radians); } + zpl_f32 zpl_tan(zpl_f32 radians) { return __builtin_tanf(radians); } + zpl_f32 zpl_arcsin(zpl_f32 a) { return __builtin_asinf(a); } + zpl_f32 zpl_arccos(zpl_f32 a) { return __builtin_acosf(a); } + zpl_f32 zpl_arctan(zpl_f32 a) { return __builtin_atanf(a); } + zpl_f32 zpl_arctan2(zpl_f32 y, zpl_f32 x) { return __builtin_atan2f(y, x); } + + zpl_f32 zpl_exp(zpl_f32 x) { return __builtin_expf(x); } + zpl_f32 zpl_log(zpl_f32 x) { return __builtin_logf(x); } + + // TODO: Should this be zpl_exp(y * zpl_log(x)) ??? + zpl_f32 zpl_pow(zpl_f32 x, zpl_f32 y) { return __builtin_powf(x, y); } + zpl_f32 zpl_hypot(zpl_f32 x, zpl_f32 y){ return __builtin_sqrt(zpl_square(x) + zpl_square(y)); } + + # endif + #else + zpl_f32 zpl_rsqrt(zpl_f32 a) { return 1.0f / sqrtf(a); } + zpl_f32 zpl_sqrt(zpl_f32 a) { return sqrtf(a); }; + zpl_f32 zpl_sin(zpl_f32 radians) { return sinf(radians); }; + zpl_f32 zpl_cos(zpl_f32 radians) { return cosf(radians); }; + zpl_f32 zpl_tan(zpl_f32 radians) { return tanf(radians); }; + zpl_f32 zpl_arcsin(zpl_f32 a) { return asinf(a); }; + zpl_f32 zpl_arccos(zpl_f32 a) { return acosf(a); }; + zpl_f32 zpl_arctan(zpl_f32 a) { return atanf(a); }; + zpl_f32 zpl_arctan2(zpl_f32 y, zpl_f32 x) { return atan2f(y, x); }; + + zpl_f32 zpl_exp(zpl_f32 x) { return expf(x); } + zpl_f32 zpl_log(zpl_f32 x) { return logf(x); } + zpl_f32 zpl_pow(zpl_f32 x, zpl_f32 y) { return powf(x, y); } + zpl_f32 zpl_hypot(zpl_f32 x, zpl_f32 y) { return sqrtf(zpl_square(x) + zpl_square(y)); } + #endif + + zpl_f32 zpl_exp2(zpl_f32 x) { return zpl_exp(ZPL_LOG_TWO * x); } + zpl_f32 zpl_log2(zpl_f32 x) { return zpl_log(x) / ZPL_LOG_TWO; } + + zpl_f32 zpl_fast_exp(zpl_f32 x) { + /* NOTE: Only works in the range -1 <= x <= +1 */ + zpl_f32 e = 1.0f + x * (1.0f + x * 0.5f * (1.0f + x * 0.3333333333f * (1.0f + x * 0.25f * (1.0f + x * 0.2f)))); + return e; + } + + zpl_f32 zpl_fast_exp2(zpl_f32 x) { return zpl_fast_exp(ZPL_LOG_TWO * x); } + + zpl_f32 zpl_round(zpl_f32 x) { return (float)((x >= 0.0f) ? zpl_floor(x + 0.5f) : zpl_ceil(x - 0.5f)); } + zpl_f32 zpl_floor(zpl_f32 x) { return (float)((x >= 0.0f) ? (int)x : (int)(x - 0.9999999999999999f)); } + zpl_f32 zpl_ceil(zpl_f32 x) { return (float)((x < 0.0f) ? (int)x : ((int)x) + 1); } + + zpl_f32 zpl_half_to_float(zpl_half value) { + union { + unsigned int i; + zpl_f32 f; + } result; + int s = (value >> 15) & 0x001; + int e = (value >> 10) & 0x01f; + int m = value & 0x3ff; + + if (e == 0) { + if (m == 0) { + /* Plus or minus zero */ + result.i = (unsigned int)(s << 31); + return result.f; + } else { + /* Denormalized number */ + while (!(m & 0x00000400)) { + m <<= 1; + e -= 1; + } + + e += 1; + m &= ~0x00000400; + } + } else if (e == 31) { + if (m == 0) { + /* Positive or negative infinity */ + result.i = (unsigned int)((s << 31) | 0x7f800000); + return result.f; + } else { + /* Nan */ + result.i = (unsigned int)((s << 31) | 0x7f800000 | (m << 13)); + return result.f; + } + } + + e = e + (127 - 15); + m = m << 13; + + result.i = (unsigned int)((s << 31) | (e << 23) | m); + return result.f; + } + + zpl_half zpl_float_to_half(zpl_f32 value) { + union { + unsigned int i; + zpl_f32 f; + } v; + int i, s, e, m; + + v.f = value; + i = (int)v.i; + + s = (i >> 16) & 0x00008000; + e = ((i >> 23) & 0x000000ff) - (127 - 15); + m = i & 0x007fffff; + + if (e <= 0) { + if (e < -10) return (zpl_half)s; + m = (m | 0x00800000) >> (1 - e); + + if (m & 0x00001000) m += 0x00002000; + + return (zpl_half)(s | (m >> 13)); + } else if (e == 0xff - (127 - 15)) { + if (m == 0) { + return (zpl_half)(s | 0x7c00); /* NOTE: infinity */ + } else { + /* NOTE: NAN */ + m >>= 13; + return (zpl_half)(s | 0x7c00 | m | (m == 0)); + } + } else { + if (m & 0x00001000) { + m += 0x00002000; + if (m & 0x00800000) { + m = 0; + e += 1; + } + } + + if (e > 30) { + zpl_f32 volatile f = 1e12f; + int j; + for (j = 0; j < 10; j++) f *= f; /* NOTE: Cause overflow */ + + return (zpl_half)(s | 0x7c00); + } + + return (zpl_half)(s | (e << 10) | (m >> 13)); + } + } + + #define ZPL_VEC2_2OP(a, c, post) \ + a->x = c.x post; \ + a->y = c.y post; + + #define ZPL_VEC2_3OP(a, b, op, c, post) \ + a->x = b.x op c.x post; \ + a->y = b.y op c.y post; + + #define ZPL_VEC3_2OP(a, c, post) \ + a->x = c.x post; \ + a->y = c.y post; \ + a->z = c.z post; + + #define ZPL_VEC3_3OP(a, b, op, c, post) \ + a->x = b.x op c.x post; \ + a->y = b.y op c.y post; \ + a->z = b.z op c.z post; + + #define ZPL_VEC4_2OP(a, c, post) \ + a->x = c.x post; \ + a->y = c.y post; \ + a->z = c.z post; \ + a->w = c.w post; + + #define ZPL_VEC4_3OP(a, b, op, c, post) \ + a->x = b.x op c.x post; \ + a->y = b.y op c.y post; \ + a->z = b.z op c.z post; \ + a->w = b.w op c.w post; + + zpl_vec2 zpl_vec2f_zero(void) { + zpl_vec2 v = { 0, 0 }; + return v; + } + zpl_vec2 zpl_vec2f(zpl_f32 x, zpl_f32 y) { + zpl_vec2 v; + v.x = x; + v.y = y; + return v; + } + zpl_vec2 zpl_vec2fv(zpl_f32 x[2]) { + zpl_vec2 v; + v.x = x[0]; + v.y = x[1]; + return v; + } + + zpl_vec3 zpl_vec3f_zero(void) { + zpl_vec3 v = { 0, 0, 0 }; + return v; + } + zpl_vec3 zpl_vec3f(zpl_f32 x, zpl_f32 y, zpl_f32 z) { + zpl_vec3 v; + v.x = x; + v.y = y; + v.z = z; + return v; + } + zpl_vec3 zpl_vec3fv(zpl_f32 x[3]) { + zpl_vec3 v; + v.x = x[0]; + v.y = x[1]; + v.z = x[2]; + return v; + } + + zpl_vec4 zpl_vec4f_zero(void) { + zpl_vec4 v = { 0, 0, 0, 0 }; + return v; + } + zpl_vec4 zpl_vec4f(zpl_f32 x, zpl_f32 y, zpl_f32 z, zpl_f32 w) { + zpl_vec4 v; + v.x = x; + v.y = y; + v.z = z; + v.w = w; + return v; + } + zpl_vec4 zpl_vec4fv(zpl_f32 x[4]) { + zpl_vec4 v; + v.x = x[0]; + v.y = x[1]; + v.z = x[2]; + v.w = x[3]; + return v; + } + + zpl_f32 zpl_vec2_max(zpl_vec2 v) { return zpl_max(v.x, v.y); } + zpl_f32 zpl_vec2_side(zpl_vec2 p, zpl_vec2 q, zpl_vec2 r) { return ((q.x - p.x) * (r.y - p.y) - (r.x - p.x) * (q.y - p.y)); } + + void zpl_vec2_add(zpl_vec2 *d, zpl_vec2 v0, zpl_vec2 v1) { ZPL_VEC2_3OP(d, v0, +, v1, +0); } + void zpl_vec2_sub(zpl_vec2 *d, zpl_vec2 v0, zpl_vec2 v1) { ZPL_VEC2_3OP(d, v0, -, v1, +0); } + void zpl_vec2_mul(zpl_vec2 *d, zpl_vec2 v, zpl_f32 s) { ZPL_VEC2_2OP(d, v, *s); } + void zpl_vec2_div(zpl_vec2 *d, zpl_vec2 v, zpl_f32 s) { ZPL_VEC2_2OP(d, v, / s); } + + zpl_f32 zpl_vec3_max(zpl_vec3 v) { return zpl_max3(v.x, v.y, v.z); } + + void zpl_vec3_add(zpl_vec3 *d, zpl_vec3 v0, zpl_vec3 v1) { ZPL_VEC3_3OP(d, v0, +, v1, +0); } + void zpl_vec3_sub(zpl_vec3 *d, zpl_vec3 v0, zpl_vec3 v1) { ZPL_VEC3_3OP(d, v0, -, v1, +0); } + void zpl_vec3_mul(zpl_vec3 *d, zpl_vec3 v, zpl_f32 s) { ZPL_VEC3_2OP(d, v, *s); } + void zpl_vec3_div(zpl_vec3 *d, zpl_vec3 v, zpl_f32 s) { ZPL_VEC3_2OP(d, v, / s); } + + void zpl_vec4_add(zpl_vec4 *d, zpl_vec4 v0, zpl_vec4 v1) { ZPL_VEC4_3OP(d, v0, +, v1, +0); } + void zpl_vec4_sub(zpl_vec4 *d, zpl_vec4 v0, zpl_vec4 v1) { ZPL_VEC4_3OP(d, v0, -, v1, +0); } + void zpl_vec4_mul(zpl_vec4 *d, zpl_vec4 v, zpl_f32 s) { ZPL_VEC4_2OP(d, v, *s); } + void zpl_vec4_div(zpl_vec4 *d, zpl_vec4 v, zpl_f32 s) { ZPL_VEC4_2OP(d, v, / s); } + + void zpl_vec2_addeq(zpl_vec2 *d, zpl_vec2 v) { ZPL_VEC2_3OP(d, (*d), +, v, +0); } + void zpl_vec2_subeq(zpl_vec2 *d, zpl_vec2 v) { ZPL_VEC2_3OP(d, (*d), -, v, +0); } + void zpl_vec2_muleq(zpl_vec2 *d, zpl_f32 s) { ZPL_VEC2_2OP(d, (*d), *s); } + void zpl_vec2_diveq(zpl_vec2 *d, zpl_f32 s) { ZPL_VEC2_2OP(d, (*d), / s); } + + void zpl_vec3_addeq(zpl_vec3 *d, zpl_vec3 v) { ZPL_VEC3_3OP(d, (*d), +, v, +0); } + void zpl_vec3_subeq(zpl_vec3 *d, zpl_vec3 v) { ZPL_VEC3_3OP(d, (*d), -, v, +0); } + void zpl_vec3_muleq(zpl_vec3 *d, zpl_f32 s) { ZPL_VEC3_2OP(d, (*d), *s); } + void zpl_vec3_diveq(zpl_vec3 *d, zpl_f32 s) { ZPL_VEC3_2OP(d, (*d), / s); } + + void zpl_vec4_addeq(zpl_vec4 *d, zpl_vec4 v) { ZPL_VEC4_3OP(d, (*d), +, v, +0); } + void zpl_vec4_subeq(zpl_vec4 *d, zpl_vec4 v) { ZPL_VEC4_3OP(d, (*d), -, v, +0); } + void zpl_vec4_muleq(zpl_vec4 *d, zpl_f32 s) { ZPL_VEC4_2OP(d, (*d), *s); } + void zpl_vec4_diveq(zpl_vec4 *d, zpl_f32 s) { ZPL_VEC4_2OP(d, (*d), / s); } + + #undef ZPL_VEC2_2OP + #undef ZPL_VEC2_3OP + #undef ZPL_VEC3_3OP + #undef ZPL_VEC3_2OP + #undef ZPL_VEC4_2OP + #undef ZPL_VEC4_3OP + + zpl_f32 zpl_vec2_dot(zpl_vec2 v0, zpl_vec2 v1) { return v0.x * v1.x + v0.y * v1.y; } + zpl_f32 zpl_vec3_dot(zpl_vec3 v0, zpl_vec3 v1) { return v0.x * v1.x + v0.y * v1.y + v0.z * v1.z; } + zpl_f32 zpl_vec4_dot(zpl_vec4 v0, zpl_vec4 v1) { return v0.x * v1.x + v0.y * v1.y + v0.z * v1.z + v0.w * v1.w; } + + void zpl_vec2_cross(zpl_f32 *d, zpl_vec2 v0, zpl_vec2 v1) { *d = v0.x * v1.y - v1.x * v0.y; } + void zpl_vec3_cross(zpl_vec3 *d, zpl_vec3 v0, zpl_vec3 v1) { + d->x = v0.y * v1.z - v0.z * v1.y; + d->y = v0.z * v1.x - v0.x * v1.z; + d->z = v0.x * v1.y - v0.y * v1.x; + } + + zpl_f32 zpl_vec2_mag2(zpl_vec2 v) { return zpl_vec2_dot(v, v); } + zpl_f32 zpl_vec3_mag2(zpl_vec3 v) { return zpl_vec3_dot(v, v); } + zpl_f32 zpl_vec4_mag2(zpl_vec4 v) { return zpl_vec4_dot(v, v); } + + /* TODO: Create custom sqrt function */ + zpl_f32 zpl_vec2_mag(zpl_vec2 v) { return zpl_sqrt(zpl_vec2_dot(v, v)); } + zpl_f32 zpl_vec3_mag(zpl_vec3 v) { return zpl_sqrt(zpl_vec3_dot(v, v)); } + zpl_f32 zpl_vec4_mag(zpl_vec4 v) { return zpl_sqrt(zpl_vec4_dot(v, v)); } + + void zpl_vec2_norm(zpl_vec2 *d, zpl_vec2 v) { + zpl_f32 inv_mag = zpl_rsqrt(zpl_vec2_dot(v, v)); + zpl_vec2_mul(d, v, inv_mag); + } + void zpl_vec3_norm(zpl_vec3 *d, zpl_vec3 v) { + zpl_f32 inv_mag = zpl_rsqrt(zpl_vec3_dot(v, v)); + zpl_vec3_mul(d, v, inv_mag); + } + void zpl_vec4_norm(zpl_vec4 *d, zpl_vec4 v) { + zpl_f32 inv_mag = zpl_rsqrt(zpl_vec4_dot(v, v)); + zpl_vec4_mul(d, v, inv_mag); + } + + void zpl_vec2_norm0(zpl_vec2 *d, zpl_vec2 v) { + zpl_f32 mag = zpl_vec2_mag(v); + if (mag > 0) + zpl_vec2_div(d, v, mag); + else + *d = zpl_vec2f_zero( ); + } + void zpl_vec3_norm0(zpl_vec3 *d, zpl_vec3 v) { + zpl_f32 mag = zpl_vec3_mag(v); + if (mag > 0) + zpl_vec3_div(d, v, mag); + else + *d = zpl_vec3f_zero( ); + } + void zpl_vec4_norm0(zpl_vec4 *d, zpl_vec4 v) { + zpl_f32 mag = zpl_vec4_mag(v); + if (mag > 0) + zpl_vec4_div(d, v, mag); + else + *d = zpl_vec4f_zero( ); + } + + void zpl_vec2_reflect(zpl_vec2 *d, zpl_vec2 i, zpl_vec2 n) { + zpl_vec2 b = n; + zpl_vec2_muleq(&b, 2.0f * zpl_vec2_dot(n, i)); + zpl_vec2_sub(d, i, b); + } + + void zpl_vec3_reflect(zpl_vec3 *d, zpl_vec3 i, zpl_vec3 n) { + zpl_vec3 b = n; + zpl_vec3_muleq(&b, 2.0f * zpl_vec3_dot(n, i)); + zpl_vec3_sub(d, i, b); + } + + void zpl_vec2_refract(zpl_vec2 *d, zpl_vec2 i, zpl_vec2 n, zpl_f32 eta) { + zpl_vec2 a, b; + zpl_f32 dv, k; + + dv = zpl_vec2_dot(n, i); + k = 1.0f - eta * eta * (1.0f - dv * dv); + zpl_vec2_mul(&a, i, eta); + zpl_vec2_mul(&b, n, eta * dv * zpl_sqrt(k)); + zpl_vec2_sub(d, a, b); + zpl_vec2_muleq(d, (float)(k >= 0.0f)); + } + + void zpl_vec3_refract(zpl_vec3 *d, zpl_vec3 i, zpl_vec3 n, zpl_f32 eta) { + zpl_vec3 a, b; + zpl_f32 dv, k; + + dv = zpl_vec3_dot(n, i); + k = 1.0f - eta * eta * (1.0f - dv * dv); + zpl_vec3_mul(&a, i, eta); + zpl_vec3_mul(&b, n, eta * dv * zpl_sqrt(k)); + zpl_vec3_sub(d, a, b); + zpl_vec3_muleq(d, (float)(k >= 0.0f)); + } + + zpl_f32 zpl_vec2_aspect_ratio(zpl_vec2 v) { return (v.y < 0.0001f) ? 0.0f : v.x / v.y; } + + void zpl_mat2_transpose(zpl_mat2 *m) { zpl_float22_transpose(zpl_float22_m(m)); } + void zpl_mat2_identity(zpl_mat2 *m) { zpl_float22_identity(zpl_float22_m(m)); } + void zpl_mat2_mul(zpl_mat2 *out, zpl_mat2 *m1, zpl_mat2 *m2) { + zpl_float22_mul(zpl_float22_m(out), zpl_float22_m(m1), zpl_float22_m(m2)); + } + + void zpl_float22_identity(zpl_f32 m[2][2]) { + m[0][0] = 1; + m[0][1] = 0; + m[1][0] = 0; + m[1][1] = 1; + } + + void zpl_mat2_copy(zpl_mat2* out, zpl_mat2* m) { + zpl_memcopy(out, m, sizeof(zpl_mat3)); + } + + void zpl_mat2_mul_vec2(zpl_vec2 *out, zpl_mat2 *m, zpl_vec2 in) { zpl_float22_mul_vec2(out, zpl_float22_m(m), in); } + + zpl_mat2 *zpl_mat2_v(zpl_vec2 m[2]) { return (zpl_mat2 *)m; } + zpl_mat2 *zpl_mat2_f(zpl_f32 m[2][2]) { return (zpl_mat2 *)m; } + + zpl_float2 *zpl_float22_m(zpl_mat2 *m) { return (zpl_float2 *)m; } + zpl_float2 *zpl_float22_v(zpl_vec2 m[2]) { return (zpl_float2 *)m; } + zpl_float2 *zpl_float22_4(zpl_f32 m[4]) { return (zpl_float2 *)m; } + + void zpl_float22_transpose(zpl_f32 (*vec)[2]) { + int i, j; + for (j = 0; j < 2; j++) { + for (i = j + 1; i < 2; i++) { + zpl_f32 t = vec[i][j]; + vec[i][j] = vec[j][i]; + vec[j][i] = t; + } + } + } + + void zpl_float22_mul(zpl_f32 (*out)[2], zpl_f32 (*mat1)[2], zpl_f32 (*mat2)[2]) { + int i, j; + zpl_f32 temp1[2][2], temp2[2][2]; + if (mat1 == out) { + zpl_memcopy(temp1, mat1, sizeof(temp1)); + mat1 = temp1; + } + if (mat2 == out) { + zpl_memcopy(temp2, mat2, sizeof(temp2)); + mat2 = temp2; + } + for (j = 0; j < 2; j++) { + for (i = 0; i < 2; i++) { out[j][i] = mat1[0][i] * mat2[j][0] + mat1[1][i] * mat2[j][1]; } + } + } + + void zpl_float22_mul_vec2(zpl_vec2 *out, zpl_f32 m[2][2], zpl_vec2 v) { + out->x = m[0][0] * v.x + m[0][1] * v.y; + out->y = m[1][0] * v.x + m[1][1] * v.y; + } + + zpl_f32 zpl_mat2_determinate(zpl_mat2 *m) { + zpl_float2 *e = zpl_float22_m(m); + return e[0][0] * e[1][1] - e[1][0] * e[0][1]; + } + + void zpl_mat2_inverse(zpl_mat2 *out, zpl_mat2 *in) { + zpl_float2 *o = zpl_float22_m(out); + zpl_float2 *i = zpl_float22_m(in); + + zpl_f32 ood = 1.0f / zpl_mat2_determinate(in); + + o[0][0] = +i[1][1] * ood; + o[0][1] = -i[0][1] * ood; + o[1][0] = -i[1][0] * ood; + o[1][1] = +i[0][0] * ood; + } + + void zpl_mat3_transpose(zpl_mat3 *m) { zpl_float33_transpose(zpl_float33_m(m)); } + void zpl_mat3_identity(zpl_mat3 *m) { zpl_float33_identity(zpl_float33_m(m)); } + + void zpl_mat3_copy(zpl_mat3* out, zpl_mat3* m) { + zpl_memcopy(out, m, sizeof(zpl_mat3)); + } + + void zpl_mat3_mul(zpl_mat3 *out, zpl_mat3 *m1, zpl_mat3 *m2) { + zpl_float33_mul(zpl_float33_m(out), zpl_float33_m(m1), zpl_float33_m(m2)); + } + + void zpl_float33_identity(zpl_f32 m[3][3]) { + m[0][0] = 1; + m[0][1] = 0; + m[0][2] = 0; + m[1][0] = 0; + m[1][1] = 1; + m[1][2] = 0; + m[2][0] = 0; + m[2][1] = 0; + m[2][2] = 1; + } + + void zpl_mat3_mul_vec3(zpl_vec3 *out, zpl_mat3 *m, zpl_vec3 in) { zpl_float33_mul_vec3(out, zpl_float33_m(m), in); } + + zpl_mat3 *zpl_mat3_v(zpl_vec3 m[3]) { return (zpl_mat3 *)m; } + zpl_mat3 *zpl_mat3_f(zpl_f32 m[3][3]) { return (zpl_mat3 *)m; } + + zpl_float3 *zpl_float33_m(zpl_mat3 *m) { return (zpl_float3 *)m; } + zpl_float3 *zpl_float33_v(zpl_vec3 m[3]) { return (zpl_float3 *)m; } + zpl_float3 *zpl_float33_9(zpl_f32 m[9]) { return (zpl_float3 *)m; } + + void zpl_float33_transpose(zpl_f32 (*vec)[3]) { + int i, j; + for (j = 0; j < 3; j++) { + for (i = j + 1; i < 3; i++) { + zpl_f32 t = vec[i][j]; + vec[i][j] = vec[j][i]; + vec[j][i] = t; + } + } + } + + void zpl_float33_mul(zpl_f32 (*out)[3], zpl_f32 (*mat1)[3], zpl_f32 (*mat2)[3]) { + int i, j; + zpl_f32 temp1[3][3], temp2[3][3]; + if (mat1 == out) { + zpl_memcopy(temp1, mat1, sizeof(temp1)); + mat1 = temp1; + } + if (mat2 == out) { + zpl_memcopy(temp2, mat2, sizeof(temp2)); + mat2 = temp2; + } + for (j = 0; j < 3; j++) { + for (i = 0; i < 3; i++) { + out[j][i] = mat1[0][i] * mat2[j][0] + mat1[1][i] * mat2[j][1] + mat1[2][i] * mat2[j][2]; + } + } + } + + void zpl_float33_mul_vec3(zpl_vec3 *out, zpl_f32 m[3][3], zpl_vec3 v) { + out->x = m[0][0] * v.x + m[0][1] * v.y + m[0][2] * v.z; + out->y = m[1][0] * v.x + m[1][1] * v.y + m[1][2] * v.z; + out->z = m[2][0] * v.x + m[2][1] * v.y + m[2][2] * v.z; + } + + zpl_f32 zpl_mat3_determinate(zpl_mat3 *m) { + zpl_float3 *e = zpl_float33_m(m); + zpl_f32 d = + +e[0][0] * (e[1][1] * e[2][2] - e[1][2] * e[2][1]) + -e[0][1] * (e[1][0] * e[2][2] - e[1][2] * e[2][0]) + +e[0][2] * (e[1][0] * e[2][1] - e[1][1] * e[2][0]); + return d; + } + + void zpl_mat3_inverse(zpl_mat3 *out, zpl_mat3 *in) { + zpl_float3 *o = zpl_float33_m(out); + zpl_float3 *i = zpl_float33_m(in); + + zpl_f32 ood = 1.0f / zpl_mat3_determinate(in); + + o[0][0] = +(i[1][1] * i[2][2] - i[2][1] * i[1][2]) * ood; + o[0][1] = -(i[1][0] * i[2][2] - i[2][0] * i[1][2]) * ood; + o[0][2] = +(i[1][0] * i[2][1] - i[2][0] * i[1][1]) * ood; + o[1][0] = -(i[0][1] * i[2][2] - i[2][1] * i[0][2]) * ood; + o[1][1] = +(i[0][0] * i[2][2] - i[2][0] * i[0][2]) * ood; + o[1][2] = -(i[0][0] * i[2][1] - i[2][0] * i[0][1]) * ood; + o[2][0] = +(i[0][1] * i[1][2] - i[1][1] * i[0][2]) * ood; + o[2][1] = -(i[0][0] * i[1][2] - i[1][0] * i[0][2]) * ood; + o[2][2] = +(i[0][0] * i[1][1] - i[1][0] * i[0][1]) * ood; + } + + void zpl_mat4_transpose(zpl_mat4 *m) { zpl_float44_transpose(zpl_float44_m(m)); } + void zpl_mat4_identity(zpl_mat4 *m) { zpl_float44_identity(zpl_float44_m(m)); } + + void zpl_mat4_copy(zpl_mat4* out, zpl_mat4* m) { + zpl_memcopy(out, m, sizeof(zpl_mat4)); + } + + + void zpl_mat4_mul(zpl_mat4 *out, zpl_mat4 *m1, zpl_mat4 *m2) { + zpl_float44_mul(zpl_float44_m(out), zpl_float44_m(m1), zpl_float44_m(m2)); + } + + void zpl_float44_identity(zpl_f32 m[4][4]) { + m[0][0] = 1; + m[0][1] = 0; + m[0][2] = 0; + m[0][3] = 0; + m[1][0] = 0; + m[1][1] = 1; + m[1][2] = 0; + m[1][3] = 0; + m[2][0] = 0; + m[2][1] = 0; + m[2][2] = 1; + m[2][3] = 0; + m[3][0] = 0; + m[3][1] = 0; + m[3][2] = 0; + m[3][3] = 1; + } + + void zpl_mat4_mul_vec4(zpl_vec4 *out, zpl_mat4 *m, zpl_vec4 in) { zpl_float44_mul_vec4(out, zpl_float44_m(m), in); } + + zpl_mat4 *zpl_mat4_v(zpl_vec4 m[4]) { return (zpl_mat4 *)m; } + zpl_mat4 *zpl_mat4_f(zpl_f32 m[4][4]) { return (zpl_mat4 *)m; } + + zpl_float4 *zpl_float44_m(zpl_mat4 *m) { return (zpl_float4 *)m; } + zpl_float4 *zpl_float44_v(zpl_vec4 m[4]) { return (zpl_float4 *)m; } + zpl_float4 *zpl_float44_16(zpl_f32 m[16]) { return (zpl_float4 *)m; } + + void zpl_float44_transpose(zpl_f32 (*vec)[4]) { + zpl_f32 tmp; + tmp = vec[1][0]; + vec[1][0] = vec[0][1]; + vec[0][1] = tmp; + tmp = vec[2][0]; + vec[2][0] = vec[0][2]; + vec[0][2] = tmp; + tmp = vec[3][0]; + vec[3][0] = vec[0][3]; + vec[0][3] = tmp; + tmp = vec[2][1]; + vec[2][1] = vec[1][2]; + vec[1][2] = tmp; + tmp = vec[3][1]; + vec[3][1] = vec[1][3]; + vec[1][3] = tmp; + tmp = vec[3][2]; + vec[3][2] = vec[2][3]; + vec[2][3] = tmp; + } + + void zpl_float44_mul(zpl_f32 (*out)[4], zpl_f32 (*mat1)[4], zpl_f32 (*mat2)[4]) { + int i, j; + zpl_f32 temp1[4][4], temp2[4][4]; + if (mat1 == out) { + zpl_memcopy(temp1, mat1, sizeof(temp1)); + mat1 = temp1; + } + if (mat2 == out) { + zpl_memcopy(temp2, mat2, sizeof(temp2)); + mat2 = temp2; + } + for (j = 0; j < 4; j++) { + for (i = 0; i < 4; i++) { + out[j][i] = + mat1[0][i] * mat2[j][0] + mat1[1][i] * mat2[j][1] + +mat1[2][i] * mat2[j][2] + mat1[3][i] * mat2[j][3]; + } + } + } + + void zpl_float44_mul_vec4(zpl_vec4 *out, zpl_f32 m[4][4], zpl_vec4 v) { + out->x = m[0][0] * v.x + m[1][0] * v.y + m[2][0] * v.z + m[3][0] * v.w; + out->y = m[0][1] * v.x + m[1][1] * v.y + m[2][1] * v.z + m[3][1] * v.w; + out->z = m[0][2] * v.x + m[1][2] * v.y + m[2][2] * v.z + m[3][2] * v.w; + out->w = m[0][3] * v.x + m[1][3] * v.y + m[2][3] * v.z + m[3][3] * v.w; + } + + void zpl_mat4_inverse(zpl_mat4 *out, zpl_mat4 *in) { + zpl_float4 *o = zpl_float44_m(out); + zpl_float4 *m = zpl_float44_m(in); + + zpl_f32 ood; + + zpl_f32 sf00 = m[2][2] * m[3][3] - m[3][2] * m[2][3]; + zpl_f32 sf01 = m[2][1] * m[3][3] - m[3][1] * m[2][3]; + zpl_f32 sf02 = m[2][1] * m[3][2] - m[3][1] * m[2][2]; + zpl_f32 sf03 = m[2][0] * m[3][3] - m[3][0] * m[2][3]; + zpl_f32 sf04 = m[2][0] * m[3][2] - m[3][0] * m[2][2]; + zpl_f32 sf05 = m[2][0] * m[3][1] - m[3][0] * m[2][1]; + zpl_f32 sf06 = m[1][2] * m[3][3] - m[3][2] * m[1][3]; + zpl_f32 sf07 = m[1][1] * m[3][3] - m[3][1] * m[1][3]; + zpl_f32 sf08 = m[1][1] * m[3][2] - m[3][1] * m[1][2]; + zpl_f32 sf09 = m[1][0] * m[3][3] - m[3][0] * m[1][3]; + zpl_f32 sf10 = m[1][0] * m[3][2] - m[3][0] * m[1][2]; + zpl_f32 sf11 = m[1][1] * m[3][3] - m[3][1] * m[1][3]; + zpl_f32 sf12 = m[1][0] * m[3][1] - m[3][0] * m[1][1]; + zpl_f32 sf13 = m[1][2] * m[2][3] - m[2][2] * m[1][3]; + zpl_f32 sf14 = m[1][1] * m[2][3] - m[2][1] * m[1][3]; + zpl_f32 sf15 = m[1][1] * m[2][2] - m[2][1] * m[1][2]; + zpl_f32 sf16 = m[1][0] * m[2][3] - m[2][0] * m[1][3]; + zpl_f32 sf17 = m[1][0] * m[2][2] - m[2][0] * m[1][2]; + zpl_f32 sf18 = m[1][0] * m[2][1] - m[2][0] * m[1][1]; + + o[0][0] = +(m[1][1] * sf00 - m[1][2] * sf01 + m[1][3] * sf02); + o[1][0] = -(m[1][0] * sf00 - m[1][2] * sf03 + m[1][3] * sf04); + o[2][0] = +(m[1][0] * sf01 - m[1][1] * sf03 + m[1][3] * sf05); + o[3][0] = -(m[1][0] * sf02 - m[1][1] * sf04 + m[1][2] * sf05); + + o[0][1] = -(m[0][1] * sf00 - m[0][2] * sf01 + m[0][3] * sf02); + o[1][1] = +(m[0][0] * sf00 - m[0][2] * sf03 + m[0][3] * sf04); + o[2][1] = -(m[0][0] * sf01 - m[0][1] * sf03 + m[0][3] * sf05); + o[3][1] = +(m[0][0] * sf02 - m[0][1] * sf04 + m[0][2] * sf05); + + o[0][2] = +(m[0][1] * sf06 - m[0][2] * sf07 + m[0][3] * sf08); + o[1][2] = -(m[0][0] * sf06 - m[0][2] * sf09 + m[0][3] * sf10); + o[2][2] = +(m[0][0] * sf11 - m[0][1] * sf09 + m[0][3] * sf12); + o[3][2] = -(m[0][0] * sf08 - m[0][1] * sf10 + m[0][2] * sf12); + + o[0][3] = -(m[0][1] * sf13 - m[0][2] * sf14 + m[0][3] * sf15); + o[1][3] = +(m[0][0] * sf13 - m[0][2] * sf16 + m[0][3] * sf17); + o[2][3] = -(m[0][0] * sf14 - m[0][1] * sf16 + m[0][3] * sf18); + o[3][3] = +(m[0][0] * sf15 - m[0][1] * sf17 + m[0][2] * sf18); + + ood = 1.0f / (m[0][0] * o[0][0] + m[0][1] * o[1][0] + m[0][2] * o[2][0] + m[0][3] * o[3][0]); + + o[0][0] *= ood; o[1][0] *= ood; o[2][0] *= ood; o[3][0] *= ood; + o[0][1] *= ood; o[1][1] *= ood; o[2][1] *= ood; o[3][1] *= ood; + o[0][2] *= ood; o[1][2] *= ood; o[2][2] *= ood; o[3][2] *= ood; + o[0][3] *= ood; o[1][3] *= ood; o[2][3] *= ood; o[3][3] *= ood; + } + + void zpl_mat4_axis_angle(zpl_mat4 *out, zpl_vec3 v, zpl_f32 angle_radians) { + zpl_f32 c, s; + zpl_vec3 axis, t; + zpl_float4 *rot; + + c = zpl_cos(angle_radians); + s = zpl_sin(angle_radians); + + zpl_vec3_norm(&axis, v); + zpl_vec3_mul(&t, axis, 1.0f - c); + + zpl_mat4_identity(out); + rot = zpl_float44_m(out); + + rot[0][0] = c + t.x * axis.x; + rot[0][1] = 0 + t.x * axis.y + s * axis.z; + rot[0][2] = 0 + t.x * axis.z - s * axis.y; + rot[0][3] = 0; + + rot[1][0] = 0 + t.y * axis.x - s * axis.z; + rot[1][1] = c + t.y * axis.y; + rot[1][2] = 0 + t.y * axis.z + s * axis.x; + rot[1][3] = 0; + + rot[2][0] = 0 + t.z * axis.x + s * axis.y; + rot[2][1] = 0 + t.z * axis.y - s * axis.x; + rot[2][2] = c + t.z * axis.z; + rot[2][3] = 0; + } + + void zpl_mat4_to_translate(zpl_mat4* out, zpl_vec3 v) { + zpl_mat4_identity(out); + out->col[3].xyz = v; + } + + void zpl_mat4_to_rotate(zpl_mat4* out, zpl_vec3 v, zpl_f32 angle_radians) { + zpl_mat4_axis_angle(out, v, angle_radians); + } + + void zpl_mat4_to_scale(zpl_mat4* out, zpl_vec3 v) { + zpl_mat4_identity(out); + out->col[0].x = v.x; + out->col[1].y = v.y; + out->col[2].z = v.z; + } + void zpl_mat4_to_scalef(zpl_mat4* out, zpl_f32 s) { + zpl_mat4_identity(out); + out->col[0].x = s; + out->col[1].y = s; + out->col[2].z = s; + } + + void zpl_mat4_translate(zpl_mat4* m, zpl_vec3 v) { + zpl_mat4 mm; + zpl_mat4_to_translate(&mm, v); + zpl_mat4_mul(m, m, &mm); + } + + void zpl_mat4_rotate(zpl_mat4* m, zpl_vec3 v, zpl_f32 angle_radians) { + zpl_mat4 mm; + zpl_mat4_axis_angle(&mm,v, angle_radians); + zpl_mat4_mul(m, m, &mm); + } + + void zpl_mat4_scale(zpl_mat4* m, zpl_vec3 v) { + zpl_mat4 mm; + zpl_mat4_to_scale(&mm, v); + zpl_mat4_mul(m, m, &mm); + } + + void zpl_mat4_scalef(zpl_mat4* m, zpl_f32 s) { + zpl_mat4 mm; + zpl_mat4_to_scalef(&mm, s); + zpl_mat4_mul(m, m, &mm); + } + + void zpl_mat4_ortho2d(zpl_mat4 *out, zpl_f32 left, zpl_f32 right, zpl_f32 bottom, zpl_f32 top) { + zpl_float4 *m; + zpl_mat4_identity(out); + m = zpl_float44_m(out); + + m[0][0] = 2.0f / (right - left); + m[1][1] = 2.0f / (top - bottom); + m[2][2] = -1.0f; + m[3][0] = -(right + left) / (right - left); + m[3][1] = -(top + bottom) / (top - bottom); + } + + void zpl_mat4_ortho3d(zpl_mat4 *out, zpl_f32 left, zpl_f32 right, zpl_f32 bottom, zpl_f32 top, zpl_f32 z_near, zpl_f32 z_far) { + zpl_float4 *m; + zpl_mat4_identity(out); + m = zpl_float44_m(out); + + m[0][0] = +2.0f / (right - left); + m[1][1] = +2.0f / (top - bottom); + m[2][2] = -2.0f / (z_far - z_near); + m[3][0] = -(right + left) / (right - left); + m[3][1] = -(top + bottom) / (top - bottom); + m[3][2] = -(z_far + z_near) / (z_far - z_near); + } + + void zpl_mat4_perspective(zpl_mat4 *out, zpl_f32 fovy, zpl_f32 aspect, zpl_f32 z_near, zpl_f32 z_far) { + zpl_f32 tan_half_fovy = zpl_tan(0.5f * fovy); + zpl_mat4 zero_mat = { 0 }; + zpl_float4 *m = zpl_float44_m(out); + *out = zero_mat; + + m[0][0] = 1.0f / (aspect * tan_half_fovy); + m[1][1] = 1.0f / (tan_half_fovy); + m[2][2] = -(z_far + z_near) / (z_far - z_near); + m[2][3] = -1.0f; + m[3][2] = -2.0f * z_far * z_near / (z_far - z_near); + } + + void zpl_mat4_infinite_perspective(zpl_mat4 *out, zpl_f32 fovy, zpl_f32 aspect, zpl_f32 z_near) { + zpl_f32 range = zpl_tan(0.5f * fovy) * z_near; + zpl_f32 left = -range * aspect; + zpl_f32 right = range * aspect; + zpl_f32 bottom = -range; + zpl_f32 top = range; + zpl_mat4 zero_mat = { 0 }; + zpl_float4 *m = zpl_float44_m(out); + *out = zero_mat; + + m[0][0] = (2.0f * z_near) / (right - left); + m[1][1] = (2.0f * z_near) / (top - bottom); + m[2][2] = -1.0f; + m[2][3] = -1.0f; + m[3][2] = -2.0f * z_near; + } + + void zpl_mat4_ortho2d_dx(zpl_mat4 *out, zpl_f32 left, zpl_f32 right, zpl_f32 bottom, zpl_f32 top) { + zpl_float4 *m; + zpl_mat4_identity(out); + m = zpl_float44_m(out); + + m[0][0] = 2.0f / (right - left); + m[1][1] = 2.0f / (top - bottom); + m[2][2] = -1.0f; + m[3][0] = -(right + left) / (right - left); + m[3][1] = -(top + bottom) / (top - bottom); + } + + void zpl_mat4_ortho3d_dx(zpl_mat4 *out, zpl_f32 left, zpl_f32 right, zpl_f32 bottom, zpl_f32 top, zpl_f32 z_near, zpl_f32 z_far) { + zpl_float4 *m; + zpl_mat4_identity(out); + m = zpl_float44_m(out); + + m[0][0] = +2.0f / (right - left); + m[1][1] = +2.0f / (top - bottom); + m[2][2] = -1.0f / (z_far - z_near); + m[3][0] = -(right + left) / (right - left); + m[3][1] = -(top + bottom) / (top - bottom); + m[3][2] = -( z_near) / (z_far - z_near); + } + + void zpl_mat4_perspective_dx(zpl_mat4 *out, zpl_f32 fovy, zpl_f32 aspect, zpl_f32 z_near, zpl_f32 z_far) { + zpl_f32 tan_half_fovy = zpl_tan(0.5f * fovy); + zpl_mat4 zero_mat = { 0 }; + zpl_float4 *m = zpl_float44_m(out); + *out = zero_mat; + + m[0][0] = 1.0f / (aspect * tan_half_fovy); + m[1][1] = 1.0f / (tan_half_fovy); + m[2][2] = -(z_far ) / (z_far - z_near); + m[2][3] = -1.0f; + m[3][2] = - z_near / (z_far - z_near); + } + + void zpl_mat4_infinite_perspective_dx(zpl_mat4 *out, zpl_f32 fovy, zpl_f32 aspect, zpl_f32 z_near) { + zpl_f32 tan_half_fovy = zpl_tan(0.5f * fovy); + zpl_mat4 zero_mat = { 0 }; + zpl_float4 *m = zpl_float44_m(out); + *out = zero_mat; + + m[0][0] = 1.0f / (aspect * tan_half_fovy); + m[1][1] = 1.0f / (tan_half_fovy); + m[2][2] = -1.0f; + m[2][3] = -1.0f; + m[3][2] = - z_near; + } + + + + void zpl_mat4_look_at(zpl_mat4 *out, zpl_vec3 eye, zpl_vec3 centre, zpl_vec3 up) { + zpl_vec3 f, s, u; + zpl_float4 *m; + + zpl_vec3_sub(&f, centre, eye); + zpl_vec3_norm(&f, f); + + zpl_vec3_cross(&s, f, up); + zpl_vec3_norm(&s, s); + + zpl_vec3_cross(&u, s, f); + + zpl_mat4_identity(out); + m = zpl_float44_m(out); + + m[0][0] = +s.x; + m[1][0] = +s.y; + m[2][0] = +s.z; + + m[0][1] = +u.x; + m[1][1] = +u.y; + m[2][1] = +u.z; + + m[0][2] = -f.x; + m[1][2] = -f.y; + m[2][2] = -f.z; + + m[3][0] = -zpl_vec3_dot(s, eye); + m[3][1] = -zpl_vec3_dot(u, eye); + m[3][2] = +zpl_vec3_dot(f, eye); + } + + void zpl_mat4_look_at_lh(zpl_mat4 *out, zpl_vec3 eye, zpl_vec3 centre, zpl_vec3 up) { + zpl_vec3 f, s, u; + zpl_float4 *m; + + zpl_vec3_sub(&f, centre, eye); + zpl_vec3_norm(&f, f); + + zpl_vec3_cross(&s, up, f); + zpl_vec3_norm(&s, s); + + zpl_vec3_cross(&u, f, s); + + zpl_mat4_identity(out); + m = zpl_float44_m(out); + + m[0][0] = +s.x; + m[1][0] = +s.y; + m[2][0] = +s.z; + + m[0][1] = +u.x; + m[1][1] = +u.y; + m[2][1] = +u.z; + + m[0][2] = +f.x; + m[1][2] = +f.y; + m[2][2] = +f.z; + + m[3][0] = -zpl_vec3_dot(s, eye); + m[3][1] = -zpl_vec3_dot(u, eye); + m[3][2] = -zpl_vec3_dot(f, eye); + } + + zpl_quat zpl_quatf(zpl_f32 x, zpl_f32 y, zpl_f32 z, zpl_f32 w) { + zpl_quat q; + q.x = x; + q.y = y; + q.z = z; + q.w = w; + return q; + } + zpl_quat zpl_quatfv(zpl_f32 e[4]) { + zpl_quat q; + q.x = e[0]; + q.y = e[1]; + q.z = e[2]; + q.w = e[3]; + return q; + } + + zpl_quat zpl_quat_axis_angle(zpl_vec3 axis, zpl_f32 angle_radians) { + zpl_quat q; + zpl_vec3_norm(&q.xyz, axis); + zpl_vec3_muleq(&q.xyz, zpl_sin(0.5f * angle_radians)); + q.w = zpl_cos(0.5f * angle_radians); + return q; + } + + zpl_quat zpl_quat_euler_angles(zpl_f32 pitch, zpl_f32 yaw, zpl_f32 roll) { + /* TODO: Do without multiplication, i.e. make it faster */ + zpl_quat q, p, y, r; + p = zpl_quat_axis_angle(zpl_vec3f(1, 0, 0), pitch); + y = zpl_quat_axis_angle(zpl_vec3f(0, 1, 0), yaw); + r = zpl_quat_axis_angle(zpl_vec3f(0, 0, 1), roll); + + zpl_quat_mul(&q, y, p); + zpl_quat_muleq(&q, r); + + return q; + } + + zpl_quat zpl_quat_identity(void) { + zpl_quat q = { 0, 0, 0, 1 }; + return q; + } + + void zpl_quat_add(zpl_quat *d, zpl_quat q0, zpl_quat q1) { zpl_vec4_add(&d->xyzw, q0.xyzw, q1.xyzw); } + void zpl_quat_sub(zpl_quat *d, zpl_quat q0, zpl_quat q1) { zpl_vec4_sub(&d->xyzw, q0.xyzw, q1.xyzw); } + + void zpl_quat_mul(zpl_quat *d, zpl_quat q0, zpl_quat q1) { + d->x = q0.w * q1.x + q0.x * q1.w + q0.y * q1.z - q0.z * q1.y; + d->y = q0.w * q1.y - q0.x * q1.z + q0.y * q1.w + q0.z * q1.x; + d->z = q0.w * q1.z + q0.x * q1.y - q0.y * q1.x + q0.z * q1.w; + d->w = q0.w * q1.w - q0.x * q1.x - q0.y * q1.y - q0.z * q1.z; + } + + void zpl_quat_div(zpl_quat *d, zpl_quat q0, zpl_quat q1) { + zpl_quat iq1; + zpl_quat_inverse(&iq1, q1); + zpl_quat_mul(d, q0, iq1); + } + + void zpl_quat_mulf(zpl_quat *d, zpl_quat q0, zpl_f32 s) { zpl_vec4_mul(&d->xyzw, q0.xyzw, s); } + void zpl_quat_divf(zpl_quat *d, zpl_quat q0, zpl_f32 s) { zpl_vec4_div(&d->xyzw, q0.xyzw, s); } + + void zpl_quat_addeq(zpl_quat *d, zpl_quat q) { zpl_vec4_addeq(&d->xyzw, q.xyzw); } + void zpl_quat_subeq(zpl_quat *d, zpl_quat q) { zpl_vec4_subeq(&d->xyzw, q.xyzw); } + void zpl_quat_muleq(zpl_quat *d, zpl_quat q) { zpl_quat_mul(d, *d, q); } + void zpl_quat_diveq(zpl_quat *d, zpl_quat q) { zpl_quat_div(d, *d, q); } + + void zpl_quat_muleqf(zpl_quat *d, zpl_f32 s) { zpl_vec4_muleq(&d->xyzw, s); } + void zpl_quat_diveqf(zpl_quat *d, zpl_f32 s) { zpl_vec4_diveq(&d->xyzw, s); } + + zpl_f32 zpl_quat_dot(zpl_quat q0, zpl_quat q1) { + zpl_f32 r = zpl_vec3_dot(q0.xyz, q1.xyz) + q0.w * q1.w; + return r; + } + zpl_f32 zpl_quat_mag(zpl_quat q) { + zpl_f32 r = zpl_sqrt(zpl_quat_dot(q, q)); + return r; + } + + void zpl_quat_norm(zpl_quat *d, zpl_quat q) { zpl_quat_divf(d, q, zpl_quat_mag(q)); } + + void zpl_quat_conj(zpl_quat *d, zpl_quat q) { + d->xyz = zpl_vec3f(-q.x, -q.y, -q.z); + d->w = q.w; + } + void zpl_quat_inverse(zpl_quat *d, zpl_quat q) { + zpl_quat_conj(d, q); + zpl_quat_diveqf(d, zpl_quat_dot(q, q)); + } + + void zpl_quat_axis(zpl_vec3 *axis, zpl_quat q) { + zpl_quat n; + zpl_quat_norm(&n, q); + zpl_vec3_div(axis, n.xyz, zpl_sin(zpl_arccos(q.w))); + } + + zpl_f32 zpl_quat_angle(zpl_quat q) { + zpl_f32 mag = zpl_quat_mag(q); + zpl_f32 c = q.w * (1.0f / mag); + zpl_f32 angle = 2.0f * zpl_arccos(c); + return angle; + } + + zpl_f32 zpl_quat_roll(zpl_quat q) { + return zpl_arctan2(2.0f * q.x * q.y + q.z * q.w, q.x * q.x + q.w * q.w - q.y * q.y - q.z * q.z); + } + zpl_f32 zpl_quat_pitch(zpl_quat q) { + return zpl_arctan2(2.0f * q.y * q.z + q.w * q.x, q.w * q.w - q.x * q.x - q.y * q.y + q.z * q.z); + } + zpl_f32 zpl_quat_yaw(zpl_quat q) { return zpl_arcsin(-2.0f * (q.x * q.z - q.w * q.y)); } + + void zpl_quat_rotate_vec3(zpl_vec3 *d, zpl_quat q, zpl_vec3 v) { + /* zpl_vec3 t = 2.0f * cross(q.xyz, v); + * *d = q.w*t + v + cross(q.xyz, t); + */ + zpl_vec3 t, p; + zpl_vec3_cross(&t, q.xyz, v); + zpl_vec3_muleq(&t, 2.0f); + + zpl_vec3_cross(&p, q.xyz, t); + + zpl_vec3_mul(d, t, q.w); + zpl_vec3_addeq(d, v); + zpl_vec3_addeq(d, p); + } + + void zpl_mat4_from_quat(zpl_mat4 *out, zpl_quat q) { + zpl_float4 *m; + zpl_quat a; + zpl_f32 xx, yy, zz, xy, xz, yz, wx, wy, wz; + + zpl_quat_norm(&a, q); + xx = a.x * a.x; + yy = a.y * a.y; + zz = a.z * a.z; + xy = a.x * a.y; + xz = a.x * a.z; + yz = a.y * a.z; + wx = a.w * a.x; + wy = a.w * a.y; + wz = a.w * a.z; + + zpl_mat4_identity(out); + m = zpl_float44_m(out); + + m[0][0] = 1.0f - 2.0f * (yy + zz); + m[0][1] = 2.0f * (xy + wz); + m[0][2] = 2.0f * (xz - wy); + + m[1][0] = 2.0f * (xy - wz); + m[1][1] = 1.0f - 2.0f * (xx + zz); + m[1][2] = 2.0f * (yz + wx); + + m[2][0] = 2.0f * (xz + wy); + m[2][1] = 2.0f * (yz - wx); + m[2][2] = 1.0f - 2.0f * (xx + yy); + } + + void zpl_quat_from_mat4(zpl_quat *out, zpl_mat4 *mat) { + zpl_float4 *m; + zpl_f32 four_x_squared_minus_1, four_y_squared_minus_1, four_z_squared_minus_1, four_w_squared_minus_1, + four_biggest_squared_minus_1; + int biggest_index = 0; + zpl_f32 biggest_value, mult; + + m = zpl_float44_m(mat); + + four_x_squared_minus_1 = m[0][0] - m[1][1] - m[2][2]; + four_y_squared_minus_1 = m[1][1] - m[0][0] - m[2][2]; + four_z_squared_minus_1 = m[2][2] - m[0][0] - m[1][1]; + four_w_squared_minus_1 = m[0][0] + m[1][1] + m[2][2]; + + four_biggest_squared_minus_1 = four_w_squared_minus_1; + if (four_x_squared_minus_1 > four_biggest_squared_minus_1) { + four_biggest_squared_minus_1 = four_x_squared_minus_1; + biggest_index = 1; + } + if (four_y_squared_minus_1 > four_biggest_squared_minus_1) { + four_biggest_squared_minus_1 = four_y_squared_minus_1; + biggest_index = 2; + } + if (four_z_squared_minus_1 > four_biggest_squared_minus_1) { + four_biggest_squared_minus_1 = four_z_squared_minus_1; + biggest_index = 3; + } + + biggest_value = zpl_sqrt(four_biggest_squared_minus_1 + 1.0f) * 0.5f; + mult = 0.25f / biggest_value; + + switch (biggest_index) { + case 0: + out->w = biggest_value; + out->x = (m[1][2] - m[2][1]) * mult; + out->y = (m[2][0] - m[0][2]) * mult; + out->z = (m[0][1] - m[1][0]) * mult; + break; + case 1: + out->w = (m[1][2] - m[2][1]) * mult; + out->x = biggest_value; + out->y = (m[0][1] + m[1][0]) * mult; + out->z = (m[2][0] + m[0][2]) * mult; + break; + case 2: + out->w = (m[2][0] - m[0][2]) * mult; + out->x = (m[0][1] + m[1][0]) * mult; + out->y = biggest_value; + out->z = (m[1][2] + m[2][1]) * mult; + break; + case 3: + out->w = (m[0][1] - m[1][0]) * mult; + out->x = (m[2][0] + m[0][2]) * mult; + out->y = (m[1][2] + m[2][1]) * mult; + out->z = biggest_value; + break; + } + } + + zpl_f32 zpl_plane_distance(zpl_plane* p, zpl_vec3 v) { + return (p->a * v.x + p->b * v.y + p->c * v.z + p->d); + } + + void zpl_frustum_create(zpl_frustum* out, zpl_mat4* camera, zpl_mat4* proj) { + zpl_mat4 pv; + + zpl_mat4_mul(&pv, camera, proj); + + zpl_plane* fp = 0; + zpl_f32 rmag; + + fp = &out->x1; + fp->a = pv.x.w + pv.x.x; + fp->b = pv.y.w + pv.x.y; + fp->c = pv.z.w + pv.x.z; + fp->d = pv.w.w + pv.x.w; + + rmag = zpl_rsqrt(zpl_square(fp->a) + zpl_square(fp->b) + zpl_square(fp->c)); + + fp->a *= rmag; + fp->b *= rmag; + fp->c *= rmag; + fp->d *= rmag; + + fp = &out->x2; + + fp->a = pv.x.w - pv.x.x; + fp->b = pv.y.w - pv.x.y; + fp->c = pv.z.w - pv.x.z; + fp->d = pv.w.w - pv.x.w; + + rmag = zpl_rsqrt(zpl_square(fp->a) + zpl_square(fp->b) + zpl_square(fp->c)); + + fp->a *= rmag; + fp->b *= rmag; + fp->c *= rmag; + fp->d *= rmag; + + fp = &out->y1; + + fp->a = pv.x.w - pv.y.x; + fp->b = pv.y.w - pv.y.y; + fp->c = pv.z.w - pv.y.w; + fp->d = pv.w.w - pv.y.z; + + rmag = zpl_rsqrt(zpl_square(fp->a) + zpl_square(fp->b) + zpl_square(fp->c)); + + fp->a *= rmag; + fp->b *= rmag; + fp->c *= rmag; + fp->d *= rmag; + + fp = &out->y2; + + fp->a = pv.x.w + pv.y.x; + fp->b = pv.y.w + pv.y.y; + fp->c = pv.z.w + pv.y.z; + fp->d = pv.w.w + pv.y.w; + + rmag = zpl_rsqrt(zpl_square(fp->a) + zpl_square(fp->b) + zpl_square(fp->c)); + + fp->a *= rmag; + fp->b *= rmag; + fp->c *= rmag; + fp->d *= rmag;; + + fp = &out->z1; + + fp->a = pv.x.w + pv.z.x; + fp->b = pv.y.w + pv.z.y; + fp->c = pv.z.w + pv.z.z; + fp->d = pv.w.w + pv.z.w; + + rmag = zpl_rsqrt(zpl_square(fp->a) + zpl_square(fp->b) + zpl_square(fp->c)); + + fp->a *= rmag; + fp->b *= rmag; + fp->c *= rmag; + fp->d *= rmag; + + fp = &out->z2; + + fp->a = pv.x.w - pv.z.x; + fp->b = pv.y.w - pv.z.y; + fp->c = pv.z.w - pv.z.z; + fp->d = pv.w.w - pv.z.w; + + rmag = zpl_rsqrt(zpl_square(fp->a) + zpl_square(fp->b) + zpl_square(fp->c)); + + fp->a *= rmag; + fp->b *= rmag; + fp->c *= rmag; + fp->d *= rmag; + } + + zpl_b8 zpl_frustum_sphere_inside(zpl_frustum* frustum, zpl_vec3 center, zpl_f32 radius) { + if (zpl_plane_distance(&frustum->x1, center) <= -radius) return 0; + if (zpl_plane_distance(&frustum->x2, center) <= -radius) return 0; + if (zpl_plane_distance(&frustum->y1, center) <= -radius) return 0; + if (zpl_plane_distance(&frustum->y2, center) <= -radius) return 0; + if (zpl_plane_distance(&frustum->z1, center) <= -radius) return 0; + if (zpl_plane_distance(&frustum->z2, center) <= -radius) return 0; + + return 1; + } + + zpl_b8 zpl_frustum_point_inside(zpl_frustum* frustum, zpl_vec3 point) { + return zpl_frustum_sphere_inside(frustum, point, 0.0f); + } + + zpl_b8 zpl_frustum_box_inside(zpl_frustum* frustum, zpl_aabb3 aabb) { + zpl_vec3 box, center; + zpl_vec3 v, b; + zpl_vec3_sub(&box, aabb.max, aabb.min); + zpl_vec3_diveq(&box, 2.0f); + zpl_vec3_add(¢er, aabb.min, box); + + b = zpl_vec3f(-box.x, -box.y, -box.z); + zpl_vec3_add(&v, b, center); + + if (zpl_frustum_point_inside(frustum, v)) return 1; + + b = zpl_vec3f(+box.x, -box.y, -box.z); + zpl_vec3_add(&v, b, center); + + if (zpl_frustum_point_inside(frustum, v)) return 1; + + b = zpl_vec3f(-box.x, +box.y, -box.z); + zpl_vec3_add(&v, b, center); + + if (zpl_frustum_point_inside(frustum, v)) return 1; + + b = zpl_vec3f(+box.x, +box.y, -box.z); + zpl_vec3_add(&v, b, center); + + if (zpl_frustum_point_inside(frustum, v)) return 1; + + b = zpl_vec3f(+box.x, +box.y, +box.z); + zpl_vec3_add(&v, b, center); + + if (zpl_frustum_point_inside(frustum, v)) return 1; + + b = zpl_vec3f(-box.x, +box.y, +box.z); + zpl_vec3_add(&v, b, center); + + if (zpl_frustum_point_inside(frustum, v)) return 1; + + b = zpl_vec3f(-box.x, -box.y, +box.z); + zpl_vec3_add(&v, b, center); + + if (zpl_frustum_point_inside(frustum, v)) return 1; + + b = zpl_vec3f(+box.x, -box.y, +box.z); + zpl_vec3_add(&v, b, center); + + if (zpl_frustum_point_inside(frustum, v)) return 1; + + return 0; + } + + zpl_f32 zpl_lerp(zpl_f32 a, zpl_f32 b, zpl_f32 t) { return a * (1.0f - t) + b * t; } + zpl_f32 zpl_unlerp(zpl_f32 t, zpl_f32 a, zpl_f32 b) { return (t - a) / (b - a); } + zpl_f32 zpl_smooth_step(zpl_f32 a, zpl_f32 b, zpl_f32 t) { + zpl_f32 x = (t - a) / (b - a); + return x * x * (3.0f - 2.0f * x); + } + zpl_f32 zpl_smoother_step(zpl_f32 a, zpl_f32 b, zpl_f32 t) { + zpl_f32 x = (t - a) / (b - a); + return x * x * x * (x * (6.0f * x - 15.0f) + 10.0f); + } + + #define ZPL_VEC_LERPN(N, d, a, b, t) \ + zpl_vec##N db; \ + zpl_vec##N##_sub(&db, b, a); \ + zpl_vec##N##_muleq(&db, t); \ + zpl_vec##N##_add(d, a, db) + + void zpl_vec2_lerp(zpl_vec2 *d, zpl_vec2 a, zpl_vec2 b, zpl_f32 t) { ZPL_VEC_LERPN(2, d, a, b, t); } + void zpl_vec3_lerp(zpl_vec3 *d, zpl_vec3 a, zpl_vec3 b, zpl_f32 t) { ZPL_VEC_LERPN(3, d, a, b, t); } + void zpl_vec4_lerp(zpl_vec4 *d, zpl_vec4 a, zpl_vec4 b, zpl_f32 t) { ZPL_VEC_LERPN(4, d, a, b, t); } + + #undef ZPL_VEC_LERPN + + void zpl_vec2_cslerp(zpl_vec2 *d, zpl_vec2 a, zpl_vec2 v0, zpl_vec2 b, zpl_vec2 v1, zpl_f32 t) { + zpl_f32 t2 = t * t; + zpl_f32 ti = (t - 1); + zpl_f32 ti2 = ti * ti; + + zpl_f32 h00 = (1 + 2 * t) * ti2; + zpl_f32 h10 = t * ti2; + zpl_f32 h01 = t2 * (3 - 2 * t); + zpl_f32 h11 = t2 * ti; + + d->x = h00 * a.x + h10 * v0.x + h01 * b.x + h11 * v1.x; + d->y = h00 * a.y + h10 * v0.y + h01 * b.y + h11 * v1.y; + } + + void zpl_vec3_cslerp(zpl_vec3 *d, zpl_vec3 a, zpl_vec3 v0, zpl_vec3 b, zpl_vec3 v1, zpl_f32 t) { + zpl_f32 t2 = t * t; + zpl_f32 ti = (t - 1); + zpl_f32 ti2 = ti * ti; + + zpl_f32 h00 = (1 + 2 * t) * ti2; + zpl_f32 h10 = t * ti2; + zpl_f32 h01 = t2 * (3 - 2 * t); + zpl_f32 h11 = t2 * ti; + + d->x = h00 * a.x + h10 * v0.x + h01 * b.x + h11 * v1.x; + d->y = h00 * a.y + h10 * v0.y + h01 * b.y + h11 * v1.y; + d->z = h00 * a.z + h10 * v0.z + h01 * b.z + h11 * v1.z; + } + + void zpl_vec2_dcslerp(zpl_vec2 *d, zpl_vec2 a, zpl_vec2 v0, zpl_vec2 b, zpl_vec2 v1, zpl_f32 t) { + zpl_f32 t2 = t * t; + + zpl_f32 dh00 = 6 * t2 - 6 * t; + zpl_f32 dh10 = 3 * t2 - 4 * t + 1; + zpl_f32 dh01 = -6 * t2 + 6 * t; + zpl_f32 dh11 = 3 * t2 - 2 * t; + + d->x = dh00 * a.x + dh10 * v0.x + dh01 * b.x + dh11 * v1.x; + d->y = dh00 * a.y + dh10 * v0.y + dh01 * b.y + dh11 * v1.y; + } + + void zpl_vec3_dcslerp(zpl_vec3 *d, zpl_vec3 a, zpl_vec3 v0, zpl_vec3 b, zpl_vec3 v1, zpl_f32 t) { + zpl_f32 t2 = t * t; + + zpl_f32 dh00 = 6 * t2 - 6 * t; + zpl_f32 dh10 = 3 * t2 - 4 * t + 1; + zpl_f32 dh01 = -6 * t2 + 6 * t; + zpl_f32 dh11 = 3 * t2 - 2 * t; + + d->x = dh00 * a.x + dh10 * v0.x + dh01 * b.x + dh11 * v1.x; + d->y = dh00 * a.y + dh10 * v0.y + dh01 * b.y + dh11 * v1.y; + d->z = dh00 * a.z + dh10 * v0.z + dh01 * b.z + dh11 * v1.z; + } + + void zpl_quat_lerp(zpl_quat *d, zpl_quat a, zpl_quat b, zpl_f32 t) { zpl_vec4_lerp(&d->xyzw, a.xyzw, b.xyzw, t); } + void zpl_quat_nlerp(zpl_quat *d, zpl_quat a, zpl_quat b, zpl_f32 t) { + zpl_quat_lerp(d, a, b, t); + zpl_quat_norm(d, *d); + } + + void zpl_quat_slerp(zpl_quat *d, zpl_quat a, zpl_quat b, zpl_f32 t) { + zpl_quat x, y, z; + zpl_f32 cos_theta, angle; + zpl_f32 s1, s0, is; + + z = b; + cos_theta = zpl_quat_dot(a, b); + + if (cos_theta < 0.0f) { + z = zpl_quatf(-b.x, -b.y, -b.z, -b.w); + cos_theta = -cos_theta; + } + + if (cos_theta > 1.0f) { + /* NOTE: Use lerp not nlerp as it's not a real angle or they are not normalized */ + zpl_quat_lerp(d, a, b, t); + } + + angle = zpl_arccos(cos_theta); + + s1 = zpl_sin((1.0f - t) * angle); + s0 = zpl_sin(t * angle); + is = 1.0f / zpl_sin(angle); + zpl_quat_mulf(&x, a, s1); + zpl_quat_mulf(&y, z, s0); + zpl_quat_add(d, x, y); + zpl_quat_muleqf(d, is); + } + + void zpl_quat_slerp_approx(zpl_quat *d, zpl_quat a, zpl_quat b, zpl_f32 t) { + /* NOTE: Derived by taylor expanding the geometric interpolation equation + * Even works okay for nearly anti-parallel versors!!! + */ + /* NOTE: Extra interations cannot be used as they require angle^4 which is not worth it to approximate */ + zpl_f32 tp = t + (1.0f - zpl_quat_dot(a, b)) / 3.0f * t * (-2.0f * t * t + 3.0f * t - 1.0f); + zpl_quat_nlerp(d, a, b, tp); + } + + void zpl_quat_nquad(zpl_quat *d, zpl_quat p, zpl_quat a, zpl_quat b, zpl_quat q, zpl_f32 t) { + zpl_quat x, y; + zpl_quat_nlerp(&x, p, q, t); + zpl_quat_nlerp(&y, a, b, t); + zpl_quat_nlerp(d, x, y, 2.0f * t * (1.0f - t)); + } + + void zpl_quat_squad(zpl_quat *d, zpl_quat p, zpl_quat a, zpl_quat b, zpl_quat q, zpl_f32 t) { + zpl_quat x, y; + zpl_quat_slerp(&x, p, q, t); + zpl_quat_slerp(&y, a, b, t); + zpl_quat_slerp(d, x, y, 2.0f * t * (1.0f - t)); + } + + void zpl_quat_squad_approx(zpl_quat *d, zpl_quat p, zpl_quat a, zpl_quat b, zpl_quat q, zpl_f32 t) { + zpl_quat x, y; + zpl_quat_slerp_approx(&x, p, q, t); + zpl_quat_slerp_approx(&y, a, b, t); + zpl_quat_slerp_approx(d, x, y, 2.0f * t * (1.0f - t)); + } + + zpl_rect2 zpl_rect2f(zpl_vec2 pos, zpl_vec2 dim) { + zpl_rect2 r; + r.pos = pos; + r.dim = dim; + return r; + } + + zpl_rect3 zpl_rect3f(zpl_vec3 pos, zpl_vec3 dim) { + zpl_rect3 r; + r.pos = pos; + r.dim = dim; + return r; + } + + zpl_aabb2 zpl_aabb2f(zpl_f32 minx, zpl_f32 miny, zpl_f32 maxx, zpl_f32 maxy) { + zpl_aabb2 r; + r.min = zpl_vec2f(minx, miny); + r.max = zpl_vec2f(maxx, maxy); + return r; + } + zpl_aabb3 zpl_aabb3f(zpl_f32 minx, zpl_f32 miny, zpl_f32 minz, zpl_f32 maxx, zpl_f32 maxy, zpl_f32 maxz) { + zpl_aabb3 r; + r.min = zpl_vec3f(minx, miny, minz); + r.max = zpl_vec3f(maxx, maxy, maxz); + return r; + } + + zpl_aabb2 zpl_aabb2_rect2(zpl_rect2 a) { + zpl_aabb2 r; + r.min = a.pos; + zpl_vec2_add(&r.max, a.pos, a.dim); + return r; + } + zpl_aabb3 zpl_aabb3_rect3(zpl_rect3 a) { + zpl_aabb3 r; + r.min = a.pos; + zpl_vec3_add(&r.max, a.pos, a.dim); + return r; + } + + zpl_rect2 zpl_rect2_aabb2(zpl_aabb2 a) { + zpl_rect2 r; + r.pos = a.min; + zpl_vec2_sub(&r.dim, a.max, a.min); + return r; + } + zpl_rect3 zpl_rect3_aabb3(zpl_aabb3 a) { + zpl_rect3 r; + r.pos = a.min; + zpl_vec3_sub(&r.dim, a.max, a.min); + return r; + } + + int zpl_rect2_contains(zpl_rect2 a, zpl_f32 x, zpl_f32 y) { + zpl_f32 min_x = zpl_min(a.pos.x, a.pos.x + a.dim.x); + zpl_f32 max_x = zpl_max(a.pos.x, a.pos.x + a.dim.x); + zpl_f32 min_y = zpl_min(a.pos.y, a.pos.y + a.dim.y); + zpl_f32 max_y = zpl_max(a.pos.y, a.pos.y + a.dim.y); + int result = (x >= min_x) & (x < max_x) & (y >= min_y) & (y < max_y); + return result; + } + + int zpl_rect2_contains_vec2(zpl_rect2 a, zpl_vec2 p) { return zpl_rect2_contains(a, p.x, p.y); } + + int zpl_rect2_intersects(zpl_rect2 a, zpl_rect2 b) { + zpl_rect2 r = { 0 }; + return zpl_rect2_intersection_result(a, b, &r); + } + + int zpl_rect2_intersection_result(zpl_rect2 a, zpl_rect2 b, zpl_rect2 *intersection) { + zpl_f32 a_min_x = zpl_min(a.pos.x, a.pos.x + a.dim.x); + zpl_f32 a_max_x = zpl_max(a.pos.x, a.pos.x + a.dim.x); + zpl_f32 a_min_y = zpl_min(a.pos.y, a.pos.y + a.dim.y); + zpl_f32 a_max_y = zpl_max(a.pos.y, a.pos.y + a.dim.y); + + zpl_f32 b_min_x = zpl_min(b.pos.x, b.pos.x + b.dim.x); + zpl_f32 b_max_x = zpl_max(b.pos.x, b.pos.x + b.dim.x); + zpl_f32 b_min_y = zpl_min(b.pos.y, b.pos.y + b.dim.y); + zpl_f32 b_max_y = zpl_max(b.pos.y, b.pos.y + b.dim.y); + + zpl_f32 x0 = zpl_max(a_min_x, b_min_x); + zpl_f32 y0 = zpl_max(a_min_y, b_min_y); + zpl_f32 x1 = zpl_min(a_max_x, b_max_x); + zpl_f32 y1 = zpl_min(a_max_y, b_max_y); + + if ((x0 < x1) && (y0 < y1)) { + zpl_rect2 r = zpl_rect2f(zpl_vec2f(x0, y0), zpl_vec2f(x1 - x0, y1 - y0)); + *intersection = r; + return 1; + } else { + zpl_rect2 r = { 0 }; + *intersection = r; + return 0; + } + } + + int zpl_aabb2_contains(zpl_aabb2 a, zpl_f32 x, zpl_f32 y) { + return (zpl_is_between_limit(x, a.min.x, a.max.x) && zpl_is_between_limit(y, a.min.y, a.max.y)); + } + + int zpl_aabb3_contains(zpl_aabb3 a, zpl_f32 x, zpl_f32 y, zpl_f32 z) { + return (zpl_is_between_limit(x, a.min.x, a.max.x) && zpl_is_between_limit(y, a.min.y, a.max.y) && zpl_is_between_limit(z, a.min.z, a.max.z)); + } + + zpl_aabb2 zpl_aabb2_cut_left(zpl_aabb2 *a, zpl_f32 b) { + zpl_f32 minx = a->min.x; + a->min.x = zpl_min(a->max.x, a->min.x + b); + return zpl_aabb2f(minx, a->min.y, a->min.x, a->max.y); + } + zpl_aabb2 zpl_aabb2_cut_right(zpl_aabb2 *a, zpl_f32 b) { + zpl_f32 maxx = a->max.x; + a->max.x = zpl_max(a->min.x, a->max.x - b); + return zpl_aabb2f(a->max.x, a->min.y, maxx, a->max.y); + } + zpl_aabb2 zpl_aabb2_cut_top(zpl_aabb2 *a, zpl_f32 b) { + zpl_f32 miny = a->min.y; + a->min.y = zpl_min(a->max.y, a->min.y + b); + return zpl_aabb2f(a->min.x, miny, a->max.x, a->min.y); + } + zpl_aabb2 zpl_aabb2_cut_bottom(zpl_aabb2 *a, zpl_f32 b) { + zpl_f32 maxy = a->max.y; + a->max.y = zpl_max(a->min.y, a->max.y - b); + return zpl_aabb2f(a->min.x, a->max.y, a->max.x, maxy); + } + + zpl_aabb2 zpl_aabb2_get_left(const zpl_aabb2 *a, zpl_f32 b) { + zpl_f32 minx = a->min.x; + zpl_f32 aminx = zpl_min(a->max.x, a->min.x + b); + return zpl_aabb2f(minx, a->min.y, aminx, a->max.y); + } + zpl_aabb2 zpl_aabb2_get_right(const zpl_aabb2 *a, zpl_f32 b) { + zpl_f32 maxx = a->max.x; + zpl_f32 amaxx = zpl_max(a->min.x, a->max.x - b); + return zpl_aabb2f(amaxx, a->min.y, maxx, a->max.y); + } + zpl_aabb2 zpl_aabb2_get_top(const zpl_aabb2 *a, zpl_f32 b) { + zpl_f32 miny = a->min.y; + zpl_f32 aminy = zpl_min(a->max.y, a->min.y + b); + return zpl_aabb2f(a->min.x, miny, a->max.x, aminy); + } + zpl_aabb2 zpl_aabb2_get_bottom(const zpl_aabb2 *a, zpl_f32 b) { + zpl_f32 maxy = a->max.y; + zpl_f32 amaxy = zpl_max(a->min.y, a->max.y - b); + return zpl_aabb2f(a->min.x, amaxy, a->max.x, maxy); + } + + zpl_aabb2 zpl_aabb2_add_left(const zpl_aabb2 *a, zpl_f32 b) { + return zpl_aabb2f(a->min.x-b, a->min.y, a->min.x, a->max.y); + } + zpl_aabb2 zpl_aabb2_add_right(const zpl_aabb2 *a, zpl_f32 b) { + return zpl_aabb2f(a->max.x, a->min.y, a->max.x+b, a->max.y); + } + zpl_aabb2 zpl_aabb2_add_top(const zpl_aabb2 *a, zpl_f32 b) { + return zpl_aabb2f(a->min.x, a->min.y-b, a->max.x, a->min.y); + } + zpl_aabb2 zpl_aabb2_add_bottom(const zpl_aabb2 *a, zpl_f32 b) { + return zpl_aabb2f(a->min.x, a->max.y, a->max.x, a->max.y+b); + } + + zpl_aabb2 zpl_aabb2_contract(const zpl_aabb2 *a, zpl_f32 b) { + zpl_aabb2 r = *a; + zpl_vec2 vb = zpl_vec2f(b, b); + zpl_vec2_addeq(&r.min, vb); + zpl_vec2_subeq(&r.max, vb); + + if (zpl_vec2_mag2(r.min) > zpl_vec2_mag2(r.max)) { + return zpl_aabb2f(0,0,0,0); + } + return r; + } + zpl_aabb2 zpl_aabb2_expand(const zpl_aabb2 *a, zpl_f32 b) { + return zpl_aabb2_contract(a, -b); + } + + ZPL_END_C_DECLS +#endif + +#if defined(ZPL_MODULE_THREADING) + // file: source/threading/fence.c + + + ZPL_BEGIN_C_DECLS + + #if defined(_MSC_VER) + /* Microsoft C/C++-compatible compiler */ + # include + #elif defined(__GNUC__) && (defined(__x86_64__) || defined(__i386__)) + /* GCC-compatible compiler, targeting x86/x86-64 */ + # include + #elif defined(__GNUC__) && defined(__ARM_NEON__) + /* GCC-compatible compiler, targeting ARM with NEON */ + # include + #elif defined(__GNUC__) && defined(__IWMMXT__) + /* GCC-compatible compiler, targeting ARM with WMMX */ + # include + #elif (defined(__GNUC__) || defined(__xlC__)) && (defined(__VEC__) || defined(__ALTIVEC__)) + /* XLC or GCC-compatible compiler, targeting PowerPC with VMX/VSX */ + # include + #elif defined(__GNUC__) && defined(__SPE__) + /* GCC-compatible compiler, targeting PowerPC with SPE */ + # include + #endif + + void zpl_yield_thread(void) { + # if defined(ZPL_SYSTEM_WINDOWS) + _mm_pause(); + # elif defined(ZPL_SYSTEM_OSX) || defined(ZPL_COMPILER_TINYC) + __asm__ volatile ("" : : : "memory"); + # elif defined(ZPL_CPU_X86) + _mm_pause(); + # endif + } + + void zpl_mfence(void) { + # if defined(ZPL_SYSTEM_WINDOWS) + _ReadWriteBarrier(); + # elif defined(ZPL_COMPILER_TINYC) + __asm__ volatile ("" : : : "memory"); + # elif defined(ZPL_SYSTEM_OSX) + __sync_synchronize(); + # elif defined(ZPL_CPU_X86) + _mm_mfence(); + # endif + } + + void zpl_sfence(void) { + # if defined(ZPL_SYSTEM_WINDOWS) + _WriteBarrier(); + # elif defined(ZPL_SYSTEM_OSX) || defined(ZPL_COMPILER_TINYC) + __asm__ volatile ("" : : : "memory"); + # elif defined(ZPL_CPU_X86) + _mm_sfence(); + # endif + } + + void zpl_lfence(void) { + # if defined(ZPL_SYSTEM_WINDOWS) + _ReadBarrier(); + # elif defined(ZPL_SYSTEM_OSX) || defined(ZPL_COMPILER_TINYC) + __asm__ volatile ("" : : : "memory"); + # elif defined(ZPL_CPU_X86) + _mm_lfence(); + # endif + } + + ZPL_END_C_DECLS + // file: source/threading/atomic.c + + + ZPL_BEGIN_C_DECLS + + //////////////////////////////////////////////////////////////// + // + // Concurrency + // + // + // IMPORTANT TODO: Use compiler intrinsics for the atomics + + #if defined(ZPL_COMPILER_MSVC) && !defined(ZPL_COMPILER_CLANG) + zpl_i32 zpl_atomic32_load (zpl_atomic32 const *a) { return a->value; } + void zpl_atomic32_store(zpl_atomic32 *a, zpl_atomicarg(zpl_i32) value) { a->value = value; } + + zpl_i32 zpl_atomic32_compare_exchange(zpl_atomic32 *a, zpl_atomicarg(zpl_i32) expected, zpl_atomicarg(zpl_i32) desired) { + return _InterlockedCompareExchange(cast(long *)a, desired, expected); + } + zpl_i32 zpl_atomic32_exchange(zpl_atomic32 *a, zpl_atomicarg(zpl_i32) desired) { + return _InterlockedExchange(cast(long *)a, desired); + } + zpl_i32 zpl_atomic32_fetch_add(zpl_atomic32 *a, zpl_atomicarg(zpl_i32) operand) { + return _InterlockedExchangeAdd(cast(long *)a, operand); + } + zpl_i32 zpl_atomic32_fetch_and(zpl_atomic32 *a, zpl_atomicarg(zpl_i32) operand) { + return _InterlockedAnd(cast(long *)a, operand); + } + zpl_i32 zpl_atomic32_fetch_or(zpl_atomic32 *a, zpl_atomicarg(zpl_i32) operand) { + return _InterlockedOr(cast(long *)a, operand); + } + + zpl_i64 zpl_atomic64_load(zpl_atomic64 const *a) { + # if defined(ZPL_ARCH_64_BIT) + return a->value; + # elif ZPL_CPU_X86 + // NOTE: The most compatible way to get an atomic 64-bit load on x86 is with cmpxchg8b + zpl_atomicarg(zpl_i64) result; + __asm { + mov esi, a; + mov ebx, eax; + mov ecx, edx; + lock cmpxchg8b [esi]; + mov dword ptr result, eax; + mov dword ptr result[4], edx; + } + return result; + # else + # error TODO: atomics for this CPU + # endif + } + + void zpl_atomic64_store(zpl_atomic64 *a, zpl_atomicarg(zpl_i64) value) { + # if defined(ZPL_ARCH_64_BIT) + a->value = value; + # elif ZPL_CPU_X86 + // NOTE: The most compatible way to get an atomic 64-bit store on x86 is with cmpxchg8b + __asm { + mov esi, a; + mov ebx, dword ptr value; + mov ecx, dword ptr value[4]; + retry: + cmpxchg8b [esi]; + jne retry; + } + # else + # error TODO: atomics for this CPU + # endif + } + + zpl_i64 zpl_atomic64_compare_exchange(zpl_atomic64 *a, zpl_atomicarg(zpl_i64) expected, zpl_atomicarg(zpl_i64) desired) { + return _InterlockedCompareExchange64(cast(zpl_atomicarg(zpl_i64) *)a, desired, expected); + } + + zpl_i64 zpl_atomic64_exchange(zpl_atomic64 *a, zpl_atomicarg(zpl_i64) desired) { + # if defined(ZPL_ARCH_64_BIT) + return _InterlockedExchange64(cast(zpl_atomicarg(zpl_i64) *)a, desired); + # elif ZPL_CPU_X86 + zpl_atomicarg(zpl_i64) expected = a->value; + for (;;) { + zpl_atomicarg(zpl_i64) original = _InterlockedCompareExchange64(cast(zpl_atomicarg(zpl_i64) *)a, desired, expected); + if (original == expected) + return original; + expected = original; + } + # else + # error TODO: atomics for this CPU + # endif + } + + zpl_i64 zpl_atomic64_fetch_add(zpl_atomic64 *a, zpl_atomicarg(zpl_i64) operand) { + # if defined(ZPL_ARCH_64_BIT) + return _InterlockedExchangeAdd64(cast(zpl_atomicarg(zpl_i64) *)a, operand); + # elif ZPL_CPU_X86 + zpl_atomicarg(zpl_i64) expected = a->value; + for (;;) { + zpl_atomicarg(zpl_i64) original = _InterlockedCompareExchange64(cast(zpl_atomicarg(zpl_i64) *)a, expected + operand, expected); + if (original == expected) + return original; + expected = original; + } + # else + # error TODO: atomics for this CPU + # endif + } + + zpl_i64 zpl_atomic64_fetch_and(zpl_atomic64 *a, zpl_atomicarg(zpl_i64) operand) { + # if defined(ZPL_ARCH_64_BIT) + return _InterlockedAnd64(cast(zpl_atomicarg(zpl_i64) *)a, operand); + # elif ZPL_CPU_X86 + zpl_atomicarg(zpl_i64) expected = a->value; + for (;;) { + zpl_atomicarg(zpl_i64) original = _InterlockedCompareExchange64(cast(zpl_atomicarg(zpl_i64) *)a, expected & operand, expected); + if (original == expected) + return original; + expected = original; + } + # else + # error TODO: atomics for this CPU + # endif + } + + zpl_i64 zpl_atomic64_fetch_or(zpl_atomic64 *a, zpl_atomicarg(zpl_i64) operand) { + # if defined(ZPL_ARCH_64_BIT) + return _InterlockedOr64(cast(zpl_atomicarg(zpl_i64) *)a, operand); + # elif ZPL_CPU_X86 + zpl_atomicarg(zpl_i64) expected = a->value; + for (;;) { + zpl_atomicarg(zpl_i64) original = _InterlockedCompareExchange64(cast(zpl_atomicarg(zpl_i64) *)a, expected | operand, expected); + if (original == expected) + return original; + expected = original; + } + # else + # error TODO: atomics for this CPU + # endif + } + + #elif defined(ZPL_CPU_X86) + + zpl_i32 zpl_atomic32_load (zpl_atomic32 const *a) { return a->value; } + void zpl_atomic32_store(zpl_atomic32 *a, zpl_atomicarg(zpl_i32) value) { a->value = value; } + + zpl_i32 zpl_atomic32_compare_exchange(zpl_atomic32 *a, zpl_atomicarg(zpl_i32) expected, zpl_atomicarg(zpl_i32) desired) { + zpl_atomicarg(zpl_i32) original; + __asm__( + "lock; cmpxchgl %2, %1" + : "=a"(original), "+m"(a->value) + : "q"(desired), "0"(expected) + ); + return original; + } + + zpl_i32 zpl_atomic32_exchange(zpl_atomic32 *a, zpl_atomicarg(zpl_i32) desired) { + // NOTE: No lock prefix is necessary for xchgl + zpl_atomicarg(zpl_i32) original; + __asm__( + "xchgl %0, %1" + : "=r"(original), "+m"(a->value) + : "0"(desired) + ); + return original; + } + + zpl_i32 zpl_atomic32_fetch_add(zpl_atomic32 *a, zpl_atomicarg(zpl_i32) operand) { + zpl_atomicarg(zpl_i32) original; + __asm__( + "lock; xaddl %0, %1" + : "=r"(original), "+m"(a->value) + : "0"(operand) + ); + return original; + } + + zpl_i32 zpl_atomic32_fetch_and(zpl_atomic32 *a, zpl_atomicarg(zpl_i32) operand) { + zpl_atomicarg(zpl_i32) original; + zpl_atomicarg(zpl_i32) tmp; + __asm__( + "1: movl %1, %0\n" + " movl %0, %2\n" + " andl %3, %2\n" + " lock; cmpxchgl %2, %1\n" + " jne 1b" + : "=&a"(original), "+m"(a->value), "=&r"(tmp) + : "r"(operand) + ); + return original; + } + + zpl_i32 zpl_atomic32_fetch_or(zpl_atomic32 *a, zpl_atomicarg(zpl_i32) operand) { + zpl_atomicarg(zpl_i32) original; + zpl_atomicarg(zpl_i32) temp; + __asm__( + "1: movl %1, %0\n" + " movl %0, %2\n" + " orl %3, %2\n" + " lock; cmpxchgl %2, %1\n" + " jne 1b" + : "=&a"(original), "+m"(a->value), "=&r"(temp) + : "r"(operand) + ); + return original; + } + + + zpl_i64 zpl_atomic64_load(zpl_atomic64 const *a) { + # if defined(ZPL_ARCH_64_BIT) + return a->value; + # else + zpl_atomicarg(zpl_i64) original; + __asm__( + "movl %%ebx, %%eax\n" + "movl %%ecx, %%edx\n" + "lock; cmpxchg8b %1" + : "=&A"(original) + : "m"(a->value) + ); + return original; + # endif + } + + void zpl_atomic64_store(zpl_atomic64 *a, zpl_atomicarg(zpl_i64) value) { + # if defined(ZPL_ARCH_64_BIT) + a->value = value; + # else + zpl_atomicarg(zpl_i64) expected = a->value; + __asm__( + "1: cmpxchg8b %0\n" + " jne 1b" + : "=m"(a->value) + : "b"((zpl_atomicarg(zpl_i32))value), "c"((zpl_atomicarg(zpl_i32))(value >> 32)), "A"(expected) + ); + # endif + } + + zpl_i64 zpl_atomic64_compare_exchange(zpl_atomic64 *a, zpl_atomicarg(zpl_i64) expected, zpl_atomicarg(zpl_i64) desired) { + # if defined(ZPL_ARCH_64_BIT) + zpl_atomicarg(zpl_i64) original; + __asm__( + "lock; cmpxchgq %2, %1" + : "=a"(original), "+m"(a->value) + : "q"(desired), "0"(expected) + ); + return original; + # else + zpl_atomicarg(zpl_i64) original; + __asm__( + "lock; cmpxchg8b %1" + : "=A"(original), "+m"(a->value) + : "b"((zpl_atomicarg(zpl_i32))desired), "c"((zpl_atomicarg(zpl_i32))(desired >> 32)), "0"(expected) + ); + return original; + # endif + } + + zpl_i64 zpl_atomic64_exchange(zpl_atomic64 *a, zpl_atomicarg(zpl_i64) desired) { + # if defined(ZPL_ARCH_64_BIT) + zpl_atomicarg(zpl_i64) original; + __asm__( + "xchgq %0, %1" + : "=r"(original), "+m"(a->value) + : "0"(desired) + ); + return original; + # else + zpl_atomicarg(zpl_i64) original = a->value; + for (;;) { + zpl_atomicarg(zpl_i64) previous = zpl_atomic64_compare_exchange(a, original, desired); + if (original == previous) + return original; + original = previous; + } + # endif + } + + zpl_i64 zpl_atomic64_fetch_add(zpl_atomic64 *a, zpl_atomicarg(zpl_i64) operand) { + # if defined(ZPL_ARCH_64_BIT) + zpl_atomicarg(zpl_i64) original; + __asm__( + "lock; xaddq %0, %1" + : "=r"(original), "+m"(a->value) + : "0"(operand) + ); + return original; + # else + for (;;) { + zpl_atomicarg(zpl_i64) original = a->value; + if (zpl_atomic64_compare_exchange(a, original, original + operand) == original) + return original; + } + # endif + } + + zpl_i64 zpl_atomic64_fetch_and(zpl_atomic64 *a, zpl_atomicarg(zpl_i64) operand) { + # if defined(ZPL_ARCH_64_BIT) + zpl_atomicarg(zpl_i64) original; + zpl_atomicarg(zpl_i64) tmp; + __asm__( + "1: movq %1, %0\n" + " movq %0, %2\n" + " andq %3, %2\n" + " lock; cmpxchgq %2, %1\n" + " jne 1b" + : "=&a"(original), "+m"(a->value), "=&r"(tmp) + : "r"(operand) + ); + return original; + # else + for (;;) { + zpl_atomicarg(zpl_i64) original = a->value; + if (zpl_atomic64_compare_exchange(a, original, original & operand) == original) + return original; + } + # endif + } + + zpl_i64 zpl_atomic64_fetch_or(zpl_atomic64 *a, zpl_atomicarg(zpl_i64) operand) { + # if defined(ZPL_ARCH_64_BIT) + zpl_atomicarg(zpl_i64) original; + zpl_atomicarg(zpl_i64) temp; + __asm__( + "1: movq %1, %0\n" + " movq %0, %2\n" + " orq %3, %2\n" + " lock; cmpxchgq %2, %1\n" + " jne 1b" + : "=&a"(original), "+m"(a->value), "=&r"(temp) + : "r"(operand) + ); + return original; + # else + for (;;) { + zpl_atomicarg(zpl_i64) original = a->value; + if (zpl_atomic64_compare_exchange(a, original, original | operand) == original) + return original; + } + # endif + } + + #elif !defined(ZPL_COMPILER_MSVC) + zpl_i32 zpl_atomic32_load (zpl_atomic32 const *a) { + return __atomic_load_n((zpl_i32*)&a->value, __ATOMIC_SEQ_CST); + } + void zpl_atomic32_store(zpl_atomic32 *a, zpl_atomicarg(zpl_i32) value) { + __atomic_store((zpl_i32*)&a->value, (zpl_i32*)&value, __ATOMIC_SEQ_CST); + } + + zpl_i32 zpl_atomic32_compare_exchange(zpl_atomic32 *a, zpl_atomicarg(zpl_i32) expected, zpl_atomicarg(zpl_i32) desired) { + return __atomic_compare_exchange_n((zpl_i32*)&a->value, (zpl_i32*)&expected, desired, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); + } + + zpl_i32 zpl_atomic32_exchange(zpl_atomic32 *a, zpl_atomicarg(zpl_i32) desired) { + return __atomic_exchange_n((zpl_i32*)&a->value, desired, __ATOMIC_SEQ_CST); + } + + zpl_i32 zpl_atomic32_fetch_add(zpl_atomic32 *a, zpl_atomicarg(zpl_i32) operand) { + return __atomic_fetch_add((zpl_i32*)&a->value, operand, __ATOMIC_SEQ_CST); + } + + zpl_i32 zpl_atomic32_fetch_and(zpl_atomic32 *a, zpl_atomicarg(zpl_i32) operand) { + return __atomic_fetch_and((zpl_i32*)&a->value, operand, __ATOMIC_SEQ_CST); + } + + zpl_i32 zpl_atomic32_fetch_or(zpl_atomic32 *a, zpl_atomicarg(zpl_i32) operand) { + return __atomic_fetch_or((zpl_i32*)&a->value, operand, __ATOMIC_SEQ_CST); + } + + zpl_i64 zpl_atomic64_load(zpl_atomic64 const *a) { + return __atomic_load_n((zpl_i64*)&a->value, __ATOMIC_SEQ_CST); + } + + void zpl_atomic64_store(zpl_atomic64 *a, zpl_atomicarg(zpl_i64) value) { + __atomic_store((zpl_i64*)&a->value, (zpl_i64*)&value, __ATOMIC_SEQ_CST); + } + + zpl_i64 zpl_atomic64_compare_exchange(zpl_atomic64 *a, zpl_atomicarg(zpl_i64) expected, zpl_atomicarg(zpl_i64) desired) { + return __atomic_compare_exchange_n((zpl_i64*)&a->value, (zpl_i64*)&expected, desired, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); + } + + zpl_i64 zpl_atomic64_exchange(zpl_atomic64 *a, zpl_atomicarg(zpl_i64) desired) { + return __atomic_exchange_n((zpl_i64*)&a->value, desired, __ATOMIC_SEQ_CST); + } + + zpl_i64 zpl_atomic64_fetch_add(zpl_atomic64 *a, zpl_atomicarg(zpl_i64) operand) { + return __atomic_fetch_add((zpl_i64*)&a->value, operand, __ATOMIC_SEQ_CST); + } + + zpl_i64 zpl_atomic64_fetch_and(zpl_atomic64 *a, zpl_atomicarg(zpl_i64) operand) { + return __atomic_fetch_and((zpl_i64*)&a->value, operand, __ATOMIC_SEQ_CST); + } + + zpl_i64 zpl_atomic64_fetch_or(zpl_atomic64 *a, zpl_atomicarg(zpl_i64) operand) { + return __atomic_fetch_or((zpl_i64*)&a->value, operand, __ATOMIC_SEQ_CST); + } + + #else + # error TODO: Implement Atomics for this CPU + #endif + + + + zpl_b32 zpl_atomic32_spin_lock(zpl_atomic32 *a, zpl_isize time_out) { + zpl_atomicarg(zpl_i32) old_value = zpl_atomic32_compare_exchange(a, 1, 0); + zpl_i32 counter = 0; + while (old_value != 0 && (time_out < 0 || counter++ < time_out)) { + zpl_yield_thread(); + old_value = zpl_atomic32_compare_exchange(a, 1, 0); + zpl_mfence(); + } + return old_value == 0; + } + + void zpl_atomic32_spin_unlock(zpl_atomic32 *a) { + zpl_atomic32_store(a, 0); + zpl_mfence(); + } + + zpl_b32 zpl_atomic64_spin_lock(zpl_atomic64 *a, zpl_isize time_out) { + zpl_atomicarg(zpl_i64) old_value = zpl_atomic64_compare_exchange(a, 1, 0); + zpl_atomicarg(zpl_i64) counter = 0; + while (old_value != 0 && (time_out < 0 || counter++ < time_out)) { + zpl_yield_thread(); + old_value = zpl_atomic64_compare_exchange(a, 1, 0); + zpl_mfence(); + } + return old_value == 0; + } + + void zpl_atomic64_spin_unlock(zpl_atomic64 *a) { + zpl_atomic64_store(a, 0); + zpl_mfence(); + } + + zpl_b32 zpl_atomic32_try_acquire_lock(zpl_atomic32 *a) { + zpl_atomicarg(zpl_i32) old_value; + zpl_yield_thread(); + old_value = zpl_atomic32_compare_exchange(a, 1, 0); + zpl_mfence(); + return old_value == 0; + } + + zpl_b32 zpl_atomic64_try_acquire_lock(zpl_atomic64 *a) { + zpl_atomicarg(zpl_i64) old_value; + zpl_yield_thread(); + old_value = zpl_atomic64_compare_exchange(a, 1, 0); + zpl_mfence(); + return old_value == 0; + } + + + + #if defined(ZPL_ARCH_32_BIT) + + void* zpl_atomic_ptr_load(zpl_atomic_ptr const *a) { + return (void *)cast(zpl_intptr)zpl_atomic32_load(cast(zpl_atomic32 const *)a); + } + void zpl_atomic_ptr_store(zpl_atomic_ptr *a, zpl_atomicarg(void *)value) { + zpl_atomic32_store(cast(zpl_atomic32 *)a, cast(zpl_atomicarg(zpl_i32))cast(zpl_intptr)value); + } + void* zpl_atomic_ptr_compare_exchange(zpl_atomic_ptr *a, zpl_atomicarg(void *)expected, zpl_atomicarg(void *)desired) { + return (void *)cast(zpl_intptr)zpl_atomic32_compare_exchange(cast(zpl_atomic32 *)a, cast(zpl_atomicarg(zpl_i32))cast(zpl_intptr)expected, cast(zpl_atomicarg(zpl_i32))cast(zpl_intptr)desired); + } + void* zpl_atomic_ptr_exchange(zpl_atomic_ptr *a, zpl_atomicarg(void *)desired) { + return (void *)cast(zpl_intptr)zpl_atomic32_exchange(cast(zpl_atomic32 *)a, cast(zpl_atomicarg(zpl_i32))cast(zpl_intptr)desired); + } + void* zpl_atomic_ptr_fetch_add(zpl_atomic_ptr *a, zpl_atomicarg(void *)operand) { + return (void *)cast(zpl_intptr)zpl_atomic32_fetch_add(cast(zpl_atomic32 *)a, cast(zpl_atomicarg(zpl_i32))cast(zpl_intptr)operand); + } + void* zpl_atomic_ptr_fetch_and(zpl_atomic_ptr *a, zpl_atomicarg(void *)operand) { + return (void *)cast(zpl_intptr)zpl_atomic32_fetch_and(cast(zpl_atomic32 *)a, cast(zpl_atomicarg(zpl_i32))cast(zpl_intptr)operand); + } + void* zpl_atomic_ptr_fetch_or(zpl_atomic_ptr *a, zpl_atomicarg(void *)operand) { + return (void *)cast(zpl_intptr)zpl_atomic32_fetch_or(cast(zpl_atomic32 *)a, cast(zpl_atomicarg(zpl_i32))cast(zpl_intptr)operand); + } + zpl_b32 zpl_atomic_ptr_spin_lock(zpl_atomic_ptr *a, zpl_isize time_out) { + return zpl_atomic32_spin_lock(cast(zpl_atomic32 *)a, time_out); + } + void zpl_atomic_ptr_spin_unlock(zpl_atomic_ptr *a) { + zpl_atomic32_spin_unlock(cast(zpl_atomic32 *)a); + } + zpl_b32 zpl_atomic_ptr_try_acquire_lock(zpl_atomic_ptr *a) { + return zpl_atomic32_try_acquire_lock(cast(zpl_atomic32 *)a); + } + + #elif defined(ZPL_ARCH_64_BIT) + + void* zpl_atomic_ptr_load(zpl_atomic_ptr const *a) { + return (void *)cast(zpl_intptr)zpl_atomic64_load(cast(zpl_atomic64 const *)a); + } + void zpl_atomic_ptr_store(zpl_atomic_ptr *a, zpl_atomicarg(void *)value) { + zpl_atomic64_store(cast(zpl_atomic64 *)a, cast(zpl_i64)cast(zpl_intptr)value); + } + void* zpl_atomic_ptr_compare_exchange(zpl_atomic_ptr *a, zpl_atomicarg(void *)expected, zpl_atomicarg(void *)desired) { + return (void *)cast(zpl_intptr)zpl_atomic64_compare_exchange(cast(zpl_atomic64 *)a, cast(zpl_i64)cast(zpl_intptr)expected, cast(zpl_i64)cast(zpl_intptr)desired); + } + void* zpl_atomic_ptr_exchange(zpl_atomic_ptr *a, zpl_atomicarg(void *)desired) { + return (void *)cast(zpl_intptr)zpl_atomic64_exchange(cast(zpl_atomic64 *)a, cast(zpl_i64)cast(zpl_intptr)desired); + } + void* zpl_atomic_ptr_fetch_add(zpl_atomic_ptr *a, zpl_atomicarg(void *)operand) { + return (void *)cast(zpl_intptr)zpl_atomic64_fetch_add(cast(zpl_atomic64 *)a, cast(zpl_i64)cast(zpl_intptr)operand); + } + void* zpl_atomic_ptr_fetch_and(zpl_atomic_ptr *a, zpl_atomicarg(void *)operand) { + return (void *)cast(zpl_intptr)zpl_atomic64_fetch_and(cast(zpl_atomic64 *)a, cast(zpl_i64)cast(zpl_intptr)operand); + } + void* zpl_atomic_ptr_fetch_or(zpl_atomic_ptr *a, zpl_atomicarg(void *)operand) { + return (void *)cast(zpl_intptr)zpl_atomic64_fetch_or(cast(zpl_atomic64 *)a, cast(zpl_i64)cast(zpl_intptr)operand); + } + zpl_b32 zpl_atomic_ptr_spin_lock(zpl_atomic_ptr *a, zpl_isize time_out) { + return zpl_atomic64_spin_lock(cast(zpl_atomic64 *)a, time_out); + } + void zpl_atomic_ptr_spin_unlock(zpl_atomic_ptr *a) { + zpl_atomic64_spin_unlock(cast(zpl_atomic64 *)a); + } + zpl_b32 zpl_atomic_ptr_try_acquire_lock(zpl_atomic_ptr *a) { + return zpl_atomic64_try_acquire_lock(cast(zpl_atomic64 *)a); + } + + #endif + + ZPL_END_C_DECLS + // file: source/threading/sem.c + + + ZPL_BEGIN_C_DECLS + + void zpl_semaphore_release(zpl_semaphore *s) { zpl_semaphore_post(s, 1); } + + #if defined(ZPL_SYSTEM_WINDOWS) + + void zpl_semaphore_init (zpl_semaphore *s) { s->win32_handle = CreateSemaphoreA(NULL, 0, ZPL_I32_MAX, NULL); } + void zpl_semaphore_destroy(zpl_semaphore *s) { CloseHandle(s->win32_handle); } + void zpl_semaphore_post (zpl_semaphore *s, zpl_i32 count) { ReleaseSemaphore(s->win32_handle, count, NULL); } + void zpl_semaphore_wait (zpl_semaphore *s) { WaitForSingleObject(s->win32_handle, INFINITE); } + zpl_i32 zpl_semaphore_trywait(zpl_semaphore *s) { int r = WaitForSingleObject(s->win32_handle, 0); return r; } + + #elif defined(ZPL_SYSTEM_OSX) + + void zpl_semaphore_init (zpl_semaphore *s) { semaphore_create(mach_task_self(), &s->osx_handle, SYNC_POLICY_FIFO, 0); } + void zpl_semaphore_destroy(zpl_semaphore *s) { semaphore_destroy(mach_task_self(), s->osx_handle); } + void zpl_semaphore_post (zpl_semaphore *s, zpl_i32 count) { while (count --> 0) semaphore_signal(s->osx_handle); } + void zpl_semaphore_wait (zpl_semaphore *s) { semaphore_wait(s->osx_handle); } + zpl_i32 zpl_semaphore_trywait(zpl_semaphore *s) { mach_timespec_t t; t.tv_sec = t.tv_nsec = 0; kern_return_t r = semaphore_timedwait(s->osx_handle, t); return r; } + + #elif defined(ZPL_SYSTEM_UNIX) + + void zpl_semaphore_init (zpl_semaphore *s) { sem_init(&s->unix_handle, 0, 0); } + void zpl_semaphore_destroy(zpl_semaphore *s) { sem_destroy(&s->unix_handle); } + void zpl_semaphore_post (zpl_semaphore *s, zpl_i32 count) { while (count --> 0) sem_post(&s->unix_handle); } + void zpl_semaphore_wait (zpl_semaphore *s) { int i; do { i = sem_wait(&s->unix_handle); } while (i == -1 && errno == EINTR); } + zpl_i32 zpl_semaphore_trywait(zpl_semaphore *s) { int r = sem_trywait(&s->unix_handle); return r; } + + #else + # error Semaphores for this OS are not implemented + #endif + + ZPL_END_C_DECLS + // file: source/threading/mutex.c + + + ZPL_BEGIN_C_DECLS + + zpl_b32 zpl_mutex_init(zpl_mutex *m) { + # if defined(ZPL_SYSTEM_WINDOWS) + InitializeCriticalSection((CRITICAL_SECTION*)m->win32_critical_section); + return 1; + # else + return pthread_mutex_init(&m->pthread_mutex, NULL); + # endif + } + + zpl_b32 zpl_mutex_destroy(zpl_mutex *m) { + # if defined(ZPL_SYSTEM_WINDOWS) + DeleteCriticalSection((CRITICAL_SECTION*)m->win32_critical_section); + return 1; + # else + return pthread_mutex_destroy(&m->pthread_mutex); + # endif + } + + void zpl_mutex_lock(zpl_mutex *m) { + # if defined(ZPL_SYSTEM_WINDOWS) + EnterCriticalSection((CRITICAL_SECTION*)m->win32_critical_section); + # else + pthread_mutex_lock(&m->pthread_mutex); + # endif + } + + zpl_b32 zpl_mutex_try_lock(zpl_mutex *m) { + # if defined(ZPL_SYSTEM_WINDOWS) + return TryEnterCriticalSection((CRITICAL_SECTION*)m->win32_critical_section); + # else + return pthread_mutex_trylock(&m->pthread_mutex); + # endif + } + + void zpl_mutex_unlock(zpl_mutex *m) { + # if defined(ZPL_SYSTEM_WINDOWS) + LeaveCriticalSection((CRITICAL_SECTION*)m->win32_critical_section); + # else + pthread_mutex_unlock(&m->pthread_mutex); + # endif + } + + ZPL_END_C_DECLS + // file: source/threading/thread.c + + + ZPL_BEGIN_C_DECLS + + zpl_b32 zpl_thread_is_running(zpl_thread const *t) { return t->is_running != 0; } + + void zpl_thread_init_nowait(zpl_thread *t) { + zpl_zero_item(t); + + # if defined(ZPL_SYSTEM_WINDOWS) + t->win32_handle = INVALID_HANDLE_VALUE; + # endif + + t->nowait = true; + } + + void zpl_thread_init(zpl_thread *t) { + zpl_thread_init_nowait(t); + + t->nowait = false; + zpl_semaphore_init(&t->semaphore); + } + + void zpl_thread_destroy(zpl_thread *t) { + # if defined(ZPL_SYSTEM_WINDOWS) + if (t->win32_handle != INVALID_HANDLE_VALUE) + zpl_thread_join(t); + # else + if (t->posix_handle) + zpl_thread_join(t); + # endif + if (!t->nowait) + zpl_semaphore_destroy(&t->semaphore); + } + + static void zpl__thread_run(zpl_thread *t) { + if (!t->nowait) + zpl_semaphore_release(&t->semaphore); + t->return_value = t->proc(t); + } + + #if defined(ZPL_SYSTEM_WINDOWS) + static DWORD __stdcall zpl__thread_proc(void *arg) { + zpl_thread *t = cast(zpl_thread *)arg; + t->is_running = true; + zpl__thread_run(t); + t->is_running = false; + return 0; + } + #else + static void *zpl__thread_proc(void *arg) { + zpl_thread *t = cast(zpl_thread *)arg; + t->is_running = true; + zpl__thread_run(t); + t->is_running = false; + return NULL; + } + #endif + + zpl_b32 zpl_thread_start(zpl_thread *t, zpl_thread_proc proc, void *user_data) { + return zpl_thread_start_with_stack(t, proc, user_data, 0); + } + + zpl_b32 zpl_thread_start_with_stack(zpl_thread *t, zpl_thread_proc proc, void *user_data, zpl_isize stack_size) { + ZPL_ASSERT(!t->is_running); + ZPL_ASSERT(proc != NULL); + t->proc = proc; + t->user_data = user_data; + t->stack_size = stack_size; + + # if defined(ZPL_SYSTEM_WINDOWS) + t->win32_handle = CreateThread(NULL, stack_size, zpl__thread_proc, t, 0, NULL); + ZPL_ASSERT_MSG(t->win32_handle != NULL, "CreateThread: GetLastError"); + # else + { + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); + if (stack_size != 0) + pthread_attr_setstacksize(&attr, stack_size); + zpl_b32 res = pthread_create(&t->posix_handle, &attr, zpl__thread_proc, t); + if (res != 0) { + return res; + } + pthread_attr_destroy(&attr); + } + # endif + if (!t->nowait) + zpl_semaphore_wait(&t->semaphore); + return 1; + } + + void zpl_thread_join(zpl_thread *t) { + # if defined(ZPL_SYSTEM_WINDOWS) + WaitForSingleObject(t->win32_handle, INFINITE); + CloseHandle(t->win32_handle); + t->win32_handle = INVALID_HANDLE_VALUE; + # else + pthread_join(t->posix_handle, NULL); + t->posix_handle = 0; + # endif + } + + zpl_u32 zpl_thread_current_id(void) { + zpl_u32 thread_id; + # if defined(ZPL_SYSTEM_WINDOWS) + # if defined(ZPL_ARCH_32_BIT) && defined(ZPL_CPU_X86) + thread_id = (cast(zpl_u32 *)__readfsdword(24))[9]; + # elif defined(ZPL_ARCH_64_BIT) && defined(ZPL_CPU_X86) + thread_id = (cast(zpl_u32 *)__readgsqword(48))[18]; + # else + thread_id = GetCurrentThreadId(); + # endif + + # elif defined(ZPL_SYSTEM_OSX) && defined(ZPL_ARCH_64_BIT) + thread_id = pthread_mach_thread_np(pthread_self()); + # elif defined(ZPL_ARCH_32_BIT) && defined(ZPL_CPU_X86) + __asm__("mov %%gs:0x08,%0" : "=r"(thread_id)); + # elif defined(ZPL_ARCH_64_BIT) && defined(ZPL_CPU_X86) + __asm__("mov %%fs:0x10,%0" : "=r"(thread_id)); + # elif defined(__ARM_ARCH) + thread_id = pthread_self(); + # else + # error Unsupported architecture for zpl_thread_current_id() + # endif + + return thread_id; + } + + void zpl_thread_set_name(zpl_thread *t, char const *name) { + # if defined(ZPL_COMPILER_MSVC) + # pragma pack(push, 8) + typedef struct { + DWORD type; + char const *name; + DWORD id; + DWORD flags; + } zplprivThreadName; + # pragma pack(pop) + + zplprivThreadName tn; + tn.type = 0x1000; + tn.name = name; + tn.id = GetThreadId(cast(HANDLE)t->win32_handle); + tn.flags = 0; + + __try { + RaiseException(0x406d1388, 0, zpl_size_of(tn)/4, cast(ULONG_PTR *)&tn); + } __except(1 /*EXCEPTION_EXECUTE_HANDLER*/) { + } + + # elif defined(ZPL_SYSTEM_WINDOWS) && !defined(ZPL_COMPILER_MSVC) + zpl_unused(t); + zpl_unused(name); + // IMPORTANT TODO: Set thread name for GCC/Clang on windows + return; + # elif defined(ZPL_SYSTEM_OSX) + // TODO: Test if this works + pthread_setname_np(name); + # else + zpl_unused(t); + zpl_unused(name); + // TODO: Test if this works + // pthread_set_name_np(t->posix_handle, name); + # endif + } + + ZPL_END_C_DECLS + // file: source/threading/sync.c + + + ZPL_BEGIN_C_DECLS + + void zpl_sync_init(zpl_sync *s) { + zpl_zero_item(s); + zpl_mutex_init(&s->mutex); + zpl_mutex_init(&s->start); + zpl_semaphore_init(&s->release); + } + + void zpl_sync_destroy(zpl_sync *s) { + if (s->waiting) { + ZPL_PANIC("Cannot destroy while threads are waiting!"); + } + + zpl_mutex_destroy(&s->mutex); + zpl_mutex_destroy(&s->start); + zpl_semaphore_destroy(&s->release); + } + + void zpl_sync_set_target(zpl_sync *s, zpl_i32 count) { + zpl_mutex_lock(&s->start); + + zpl_mutex_lock(&s->mutex); + ZPL_ASSERT(s->target == 0); + s->target = count; + s->current = 0; + s->waiting = 0; + zpl_mutex_unlock(&s->mutex); + } + + void zpl_sync_release(zpl_sync *s) { + if (s->waiting) { + zpl_semaphore_release(&s->release); + } else { + s->target = 0; + zpl_mutex_unlock(&s->start); + } + } + + zpl_i32 zpl_sync_reach(zpl_sync *s) { + zpl_i32 n; + zpl_mutex_lock(&s->mutex); + ZPL_ASSERT(s->current < s->target); + n = ++s->current; // NOTE: Record this value to avoid possible race if `return s->current` was done + if (s->current == s->target) + zpl_sync_release(s); + zpl_mutex_unlock(&s->mutex); + return n; + } + + void zpl_sync_reach_and_wait(zpl_sync *s) { + zpl_mutex_lock(&s->mutex); + ZPL_ASSERT(s->current < s->target); + s->current++; + if (s->current == s->target) { + zpl_sync_release(s); + zpl_mutex_unlock(&s->mutex); + } else { + s->waiting++; // NOTE: Waiting, so one more waiter + zpl_mutex_unlock(&s->mutex); // NOTE: Release the mutex to other threads + zpl_semaphore_wait(&s->release); // NOTE: Wait for merge completion + zpl_mutex_lock(&s->mutex); // NOTE: On merge completion, lock mutex + s->waiting--; // NOTE: Done waiting + zpl_sync_release(s); // NOTE: Restart the next waiter + zpl_mutex_unlock(&s->mutex); + } + } + + ZPL_END_C_DECLS + // file: source/threading/affinity.c + + + #if defined(ZPL_SYSTEM_MACOS) + # include + #endif + + ZPL_BEGIN_C_DECLS + + #if defined(ZPL_SYSTEM_WINDOWS) || defined(ZPL_SYSTEM_CYGWIN) + + void zpl_affinity_init(zpl_affinity *a) { + SYSTEM_LOGICAL_PROCESSOR_INFORMATION *start_processor_info = NULL; + DWORD length = 0; + zpl_b32 result = GetLogicalProcessorInformation(NULL, &length); + + zpl_zero_item(a); + + if (!result && GetLastError() == 122l /*ERROR_INSUFFICIENT_BUFFER*/ && length > 0) { + start_processor_info = cast(SYSTEM_LOGICAL_PROCESSOR_INFORMATION *)zpl_alloc(zpl_heap_allocator(), length); + result = GetLogicalProcessorInformation(start_processor_info, &length); + if (result) { + SYSTEM_LOGICAL_PROCESSOR_INFORMATION *end_processor_info, *processor_info; + + a->is_accurate = true; + a->core_count = 0; + a->thread_count = 0; + end_processor_info = cast(SYSTEM_LOGICAL_PROCESSOR_INFORMATION *)zpl_pointer_add(start_processor_info, length); + + for (processor_info = start_processor_info; + processor_info < end_processor_info; + processor_info++) { + if (processor_info->Relationship == RelationProcessorCore) { + zpl_isize thread = zpl_count_set_bits(processor_info->ProcessorMask); + if (thread == 0) { + a->is_accurate = false; + } else if (a->thread_count + thread > ZPL_WIN32_MAX_THREADS) { + a->is_accurate = false; + } else { + ZPL_ASSERT(a->core_count <= a->thread_count && + a->thread_count < ZPL_WIN32_MAX_THREADS); + a->core_masks[a->core_count++] = processor_info->ProcessorMask; + a->thread_count += thread; + } + } + } + } + + zpl_free(zpl_heap_allocator(), start_processor_info); + } + + ZPL_ASSERT(a->core_count <= a->thread_count); + if (a->thread_count == 0) { + a->is_accurate = false; + a->core_count = 1; + a->thread_count = 1; + a->core_masks[0] = 1; + } + + } + + void zpl_affinity_destroy(zpl_affinity *a) { + zpl_unused(a); + } + + zpl_b32 zpl_affinity_set(zpl_affinity *a, zpl_isize core, zpl_isize thread) { + zpl_usize available_mask, check_mask = 1; + ZPL_ASSERT(thread < zpl_affinity_thread_count_for_core(a, core)); + + available_mask = a->core_masks[core]; + for (;;) { + if ((available_mask & check_mask) != 0) { + if (thread-- == 0) { + zpl_usize result = SetThreadAffinityMask(GetCurrentThread(), check_mask); + return result != 0; + } + } + check_mask <<= 1; // NOTE: Onto the next bit + } + } + + zpl_isize zpl_affinity_thread_count_for_core(zpl_affinity *a, zpl_isize core) { + ZPL_ASSERT(core >= 0 && core < a->core_count); + return zpl_count_set_bits(a->core_masks[core]); + } + + #elif defined(ZPL_SYSTEM_MACOS) + void zpl_affinity_init(zpl_affinity *a) { + zpl_usize count, count_size = zpl_size_of(count); + + a->is_accurate = false; + a->thread_count = 1; + a->core_count = 1; + a->threads_per_core = 1; + + if (sysctlbyname("hw.logicalcpu", &count, &count_size, NULL, 0) == 0) { + if (count > 0) { + a->thread_count = count; + // Get # of physical cores + if (sysctlbyname("hw.physicalcpu", &count, &count_size, NULL, 0) == 0) { + if (count > 0) { + a->core_count = count; + a->threads_per_core = a->thread_count / count; + if (a->threads_per_core < 1) + a->threads_per_core = 1; + else + a->is_accurate = true; + } + } + } + } + + } + + void zpl_affinity_destroy(zpl_affinity *a) { + zpl_unused(a); + } + + zpl_b32 zpl_affinity_set(zpl_affinity *a, zpl_isize core, zpl_isize thread_index) { + zpl_isize index; + thread_t thread; + thread_affinity_policy_data_t info; + kern_return_t result; + + ZPL_ASSERT(core < a->core_count); + ZPL_ASSERT(thread_index < a->threads_per_core); + + index = core * a->threads_per_core + thread_index; + thread = mach_thread_self(); + info.affinity_tag = cast(integer_t)index; + result = thread_policy_set(thread, THREAD_AFFINITY_POLICY, cast(thread_policy_t)&info, THREAD_AFFINITY_POLICY_COUNT); + return result == KERN_SUCCESS; + } + + zpl_isize zpl_affinity_thread_count_for_core(zpl_affinity *a, zpl_isize core) { + ZPL_ASSERT(core >= 0 && core < a->core_count); + return a->threads_per_core; + } + + #elif defined(ZPL_SYSTEM_LINUX) || defined(ZPL_SYSTEM_FREEBSD) || defined(ZPL_SYSTEM_OPENBSD) + void zpl_affinity_init(zpl_affinity *a) { + a->core_count = sysconf(_SC_NPROCESSORS_ONLN); + a->threads_per_core = 1; + + a->is_accurate = a->core_count > 0; + a->core_count = a->is_accurate ? a->core_count : 1; + a->thread_count = a->core_count; + } + + void zpl_affinity_destroy(zpl_affinity *a) { + zpl_unused(a); + } + + zpl_b32 zpl_affinity_set(zpl_affinity * a, zpl_isize core, zpl_isize thread_index) { + zpl_unused(a); + zpl_unused(core); + zpl_unused(thread_index); + return true; + } + + zpl_isize zpl_affinity_thread_count_for_core(zpl_affinity *a, zpl_isize core) { + ZPL_ASSERT(0 <= core && core < a->core_count); + return a->threads_per_core; + } + + #elif defined(ZPL_SYSTEM_EMSCRIPTEN) + # error No affinity implementation for Emscripten + #else + # error TODO: Unknown system + #endif + + ZPL_END_C_DECLS + +# if defined(ZPL_MODULE_JOBS) + // file: source/jobs.c + + /////////////////////////////////////////////////////////////// + // + // Thread Pool + // + + ZPL_BEGIN_C_DECLS + + ZPL_RING_DEFINE(zpl__jobs_ring_, zpl_thread_job); + + zpl_global const zpl_u32 zpl__jobs_chances[ZPL_JOBS_MAX_PRIORITIES] = { + 2, 3, 5, 7, 11 + }; + + zpl_isize zpl__jobs_entry(struct zpl_thread *thread) { + zpl_thread_worker *tw = (zpl_thread_worker *)thread->user_data; + + for (;;) { + zpl_u32 status = zpl_atomic32_load(&tw->status); + + switch (status) { + case ZPL_JOBS_STATUS_READY: { + zpl_atomic32_store(&tw->status, ZPL_JOBS_STATUS_BUSY); + tw->job.proc(tw->job.data); + zpl_atomic32_compare_exchange(&tw->status, ZPL_JOBS_STATUS_BUSY, ZPL_JOBS_STATUS_WAITING); + + # ifdef ZPL_JOBS_DEBUG + ++tw->hits; + # endif + } break; + + case ZPL_JOBS_STATUS_WAITING: { + # ifdef ZPL_JOBS_DEBUG + ++tw->idle; + # endif + zpl_yield(); + } break; + + case ZPL_JOBS_STATUS_TERM: { + return 0; + } break; + } + } + + return 0; + } + + void zpl_jobs_init(zpl_jobs_system *pool, zpl_allocator a, zpl_u32 max_threads) { + zpl_jobs_init_with_limit(pool, a, max_threads, ZPL_JOBS_MAX_QUEUE); + } + + void zpl_jobs_init_with_limit(zpl_jobs_system *pool, zpl_allocator a, zpl_u32 max_threads, zpl_u32 max_jobs) { + zpl_jobs_system pool_ = { 0 }; + *pool = pool_; + + pool->alloc = a; + pool->max_threads = max_threads; + pool->max_jobs = max_jobs; + pool->counter = 0; + + zpl_buffer_init(pool->workers, a, max_threads); + + for (zpl_usize i = 0; i < ZPL_JOBS_MAX_PRIORITIES; ++i) { + zpl_thread_queue *q = &pool->queues[i]; + zpl__jobs_ring_init(&q->jobs, a, max_jobs); + q->chance = zpl__jobs_chances[i]; + } + + for (zpl_usize i = 0; i < max_threads; ++i) { + zpl_thread_worker worker_ = { 0 }; + zpl_thread_worker *tw = pool->workers + i; + *tw = worker_; + + zpl_thread_init(&tw->thread); + zpl_atomic32_store(&tw->status, ZPL_JOBS_STATUS_WAITING); + zpl_thread_start(&tw->thread, zpl__jobs_entry, (void *)tw); + } + } + + void zpl_jobs_free(zpl_jobs_system *pool) { + for (zpl_usize i = 0; i < pool->max_threads; ++i) { + zpl_thread_worker *tw = pool->workers + i; + + zpl_atomic32_store(&tw->status, ZPL_JOBS_STATUS_TERM); + zpl_thread_destroy(&tw->thread); + } + + zpl_buffer_free(pool->workers); + + for (zpl_usize i = 0; i < ZPL_JOBS_MAX_PRIORITIES; ++i) { + zpl_thread_queue *q = &pool->queues[i]; + zpl__jobs_ring_free(&q->jobs); + } + } + + zpl_b32 zpl_jobs_enqueue_with_priority(zpl_jobs_system *pool, zpl_jobs_proc proc, void *data, zpl_jobs_priority priority) { + ZPL_ASSERT(priority >= 0 && priority < ZPL_JOBS_MAX_PRIORITIES); + ZPL_ASSERT_NOT_NULL(proc); + zpl_thread_job job = {0}; + job.proc = proc; + job.data = data; + + if (!zpl_jobs_full(pool, priority)) { + zpl__jobs_ring_append(&pool->queues[priority].jobs, job); + return true; + } + return false; + } + + zpl_b32 zpl_jobs_enqueue(zpl_jobs_system *pool, zpl_jobs_proc proc, void *data) { + return zpl_jobs_enqueue_with_priority(pool, proc, data, ZPL_JOBS_PRIORITY_NORMAL); + } + + zpl_b32 zpl_jobs_empty(zpl_jobs_system *pool, zpl_jobs_priority priority) { + ZPL_ASSERT(priority >= 0 && priority < ZPL_JOBS_MAX_PRIORITIES); + return zpl__jobs_ring_empty(&pool->queues[priority].jobs); + } + + zpl_b32 zpl_jobs_full(zpl_jobs_system *pool, zpl_jobs_priority priority) { + ZPL_ASSERT(priority >= 0 && priority < ZPL_JOBS_MAX_PRIORITIES); + return zpl__jobs_ring_full(&pool->queues[priority].jobs); + } + + zpl_b32 zpl_jobs_done(zpl_jobs_system *pool) { + for (zpl_usize i = 0; i < pool->max_threads; ++i) { + zpl_thread_worker *tw = pool->workers + i; + if (zpl_atomic32_load(&tw->status) != ZPL_JOBS_STATUS_WAITING) { + return false; + } + } + + return zpl_jobs_empty_all(pool); + } + + zpl_b32 zpl_jobs_empty_all(zpl_jobs_system *pool) { + for (zpl_usize i = 0; i < ZPL_JOBS_MAX_PRIORITIES; ++i) { + if (!zpl_jobs_empty(pool, (zpl_jobs_priority)i)) { + return false; + } + } + return true; + } + + zpl_b32 zpl_jobs_full_all(zpl_jobs_system *pool) { + for (zpl_usize i = 0; i < ZPL_JOBS_MAX_PRIORITIES; ++i) { + if (!zpl_jobs_full(pool, (zpl_jobs_priority)i)) { + return false; + } + } + return true; + } + + zpl_b32 zpl_jobs_process(zpl_jobs_system *pool) { + if (zpl_jobs_empty_all(pool)) { + return false; + } + // NOTE: Process the jobs + for (zpl_usize i = 0; i < pool->max_threads; ++i) { + zpl_thread_worker *tw = pool->workers + i; + zpl_u32 status = zpl_atomic32_load(&tw->status); + zpl_b32 last_empty = false; + + if (status == ZPL_JOBS_STATUS_WAITING) { + for (zpl_usize j = 0; j < ZPL_JOBS_MAX_PRIORITIES; ++j) { + zpl_thread_queue *q = &pool->queues[j]; + if (zpl_jobs_empty(pool, (zpl_jobs_priority)j)) { + last_empty = (j+1 == ZPL_JOBS_MAX_PRIORITIES); + continue; + } + if (!last_empty && ((pool->counter++ % q->chance) != 0)) { + continue; + } + + last_empty = false; + tw->job = *zpl__jobs_ring_get(&q->jobs); + zpl_atomic32_store(&tw->status, ZPL_JOBS_STATUS_READY); + # ifdef ZPL_JOBS_DEBUG + ++q->hits; + # endif + break; + } + } + } + + return true; + } + + ZPL_END_C_DECLS +# endif +#endif + +#if defined(ZPL_MODULE_PARSER) + // file: source/adt.c + + ZPL_BEGIN_C_DECLS + + #define zpl__adt_fprintf(s_, fmt_, ...) \ + do { \ + if (zpl_fprintf(s_, fmt_, ##__VA_ARGS__) < 0) return ZPL_ADT_ERROR_OUT_OF_MEMORY; \ + } while (0) + + zpl_u8 zpl_adt_make_branch(zpl_adt_node *node, zpl_allocator backing, char const *name, zpl_b32 is_array) { + zpl_u8 type = ZPL_ADT_TYPE_OBJECT; + if (is_array) { + type = ZPL_ADT_TYPE_ARRAY; + } + zpl_adt_node *parent = node->parent; + zpl_zero_item(node); + node->type = type; + node->name = name; + node->parent = parent; + if (!zpl_array_init(node->nodes, backing)) + return ZPL_ADT_ERROR_OUT_OF_MEMORY; + return 0; + } + + zpl_u8 zpl_adt_destroy_branch(zpl_adt_node *node) { + ZPL_ASSERT_NOT_NULL(node); + if ((node->type == ZPL_ADT_TYPE_OBJECT || node->type == ZPL_ADT_TYPE_ARRAY) && node->nodes) { + for (zpl_isize i = 0; i < zpl_array_count(node->nodes); ++i) { zpl_adt_destroy_branch(node->nodes + i); } + + zpl_array_free(node->nodes); + } + return 0; + } + + zpl_u8 zpl_adt_make_leaf(zpl_adt_node *node, char const *name, zpl_u8 type) { + ZPL_ASSERT(type != ZPL_ADT_TYPE_OBJECT && type != ZPL_ADT_TYPE_ARRAY); + zpl_adt_node *parent = node->parent; + zpl_zero_item(node); + node->type = type; + node->name = name; + node->parent = parent; + return 0; + } + + zpl_adt_node *zpl_adt_find(zpl_adt_node *node, char const *name, zpl_b32 deep_search) { + if (node->type != ZPL_ADT_TYPE_OBJECT) { + return NULL; + } + + for (zpl_isize i = 0; i < zpl_array_count(node->nodes); i++) { + if (!zpl_strcmp(node->nodes[i].name, name)) { + return (node->nodes + i); + } + } + + if (deep_search) { + for (zpl_isize i = 0; i < zpl_array_count(node->nodes); i++) { + zpl_adt_node *res = zpl_adt_find(node->nodes + i, name, deep_search); + + if (res != NULL) + return res; + } + } + + return NULL; + } + + zpl_internal zpl_adt_node *zpl__adt_get_value(zpl_adt_node *node, char const *value) { + switch (node->type) { + case ZPL_ADT_TYPE_MULTISTRING: + case ZPL_ADT_TYPE_STRING: { + if (node->string && !zpl_strcmp(node->string, value)) { + return node; + } + } break; + case ZPL_ADT_TYPE_INTEGER: + case ZPL_ADT_TYPE_REAL: { + char back[4096]={0}; + zpl_file tmp; + + /* allocate a file descriptor for a memory-mapped number to string conversion, input source buffer is not cloned, however. */ + zpl_file_stream_open(&tmp, zpl_heap(), (zpl_u8*)back, zpl_size_of(back), ZPL_FILE_STREAM_WRITABLE); + zpl_adt_print_number(&tmp, node); + + zpl_isize fsize=0; + zpl_u8* buf = zpl_file_stream_buf(&tmp, &fsize); + + if (!zpl_strcmp((char const *)buf, value)) { + zpl_file_close(&tmp); + return node; + } + + zpl_file_close(&tmp); + } break; + default: break; /* node doesn't support value based lookup */ + } + + return NULL; + } + + zpl_internal zpl_adt_node *zpl__adt_get_field(zpl_adt_node *node, char *name, char *value) { + for (zpl_isize i = 0; i < zpl_array_count(node->nodes); i++) { + if (!zpl_strcmp(node->nodes[i].name, name)) { + zpl_adt_node *child = &node->nodes[i]; + if (zpl__adt_get_value(child, value)) { + return node; /* this object does contain a field of a specified value! */ + } + } + } + + return NULL; + } + + zpl_adt_node *zpl_adt_query(zpl_adt_node *node, char const *uri) { + ZPL_ASSERT_NOT_NULL(uri); + + if (*uri == '/') { + uri++; + } + + if (*uri == 0) { + return node; + } + + if (!node || (node->type != ZPL_ADT_TYPE_OBJECT && node->type != ZPL_ADT_TYPE_ARRAY)) { + return NULL; + } + + #if defined ZPL_ADT_URI_DEBUG || 0 + zpl_printf("uri: %s\n", uri); + #endif + + char *p=(char*)uri, *b=p, *e=p; + zpl_adt_node *found_node=NULL; + + b = p; + p = e = (char*)zpl_str_skip(p, '/'); + char *buf = zpl_bprintf("%.*s", (int)(e - b), b); + + /* handle field value lookup */ + if (*b == '[') { + char *l_p=buf+1,*l_b=l_p,*l_e=l_p,*l_b2=l_p,*l_e2=l_p; + l_e = (char*)zpl_str_skip(l_p, '='); + l_e2 = (char*)zpl_str_skip(l_p, ']'); + + if ((!*l_e && node->type != ZPL_ADT_TYPE_ARRAY) || !*l_e2) { + ZPL_ASSERT_MSG(0, "Invalid field value lookup"); + return NULL; + } + + *l_e2 = 0; + + /* [field=value] */ + if (*l_e) { + *l_e = 0; + l_b2 = l_e+1; + + /* run a value comparison against our own fields */ + if (node->type == ZPL_ADT_TYPE_OBJECT) { + found_node = zpl__adt_get_field(node, l_b, l_b2); + } + + /* run a value comparison against any child that is an object node */ + else if (node->type == ZPL_ADT_TYPE_ARRAY) { + for (zpl_isize i = 0; i < zpl_array_count(node->nodes); i++) { + zpl_adt_node *child = &node->nodes[i]; + if (child->type != ZPL_ADT_TYPE_OBJECT) { + continue; + } + + found_node = zpl__adt_get_field(child, l_b, l_b2); + + if (found_node) + break; + } + } + } + /* [value] */ + else { + for (zpl_isize i = 0; i < zpl_array_count(node->nodes); i++) { + zpl_adt_node *child = &node->nodes[i]; + if (zpl__adt_get_value(child, l_b2)) { + found_node = child; + break; /* we found a matching value in array, ignore the rest of it */ + } + } + } + + /* go deeper if uri continues */ + if (*e) { + return zpl_adt_query(found_node, e+1); + } + } + /* handle field name lookup */ + else if (node->type == ZPL_ADT_TYPE_OBJECT) { + found_node = zpl_adt_find(node, buf, false); + + /* go deeper if uri continues */ + if (*e) { + return zpl_adt_query(found_node, e+1); + } + } + /* handle array index lookup */ + else { + zpl_isize idx = (zpl_isize)zpl_str_to_i64(buf, NULL, 10); + if (idx >= 0 && idx < zpl_array_count(node->nodes)) { + found_node = &node->nodes[idx]; + + /* go deeper if uri continues */ + if (*e) { + return zpl_adt_query(found_node, e+1); + } + } + } + + return found_node; + } + + zpl_adt_node *zpl_adt_alloc_at(zpl_adt_node *parent, zpl_isize index) { + if (!parent || (parent->type != ZPL_ADT_TYPE_OBJECT && parent->type != ZPL_ADT_TYPE_ARRAY)) { + return NULL; + } + + if (!parent->nodes) + return NULL; + + if (index < 0 || index > zpl_array_count(parent->nodes)) + return NULL; + + zpl_adt_node o = {0}; + o.parent = parent; + if (!zpl_array_append_at(parent->nodes, o, index)) + return NULL; + + return parent->nodes + index; + } + + zpl_adt_node *zpl_adt_alloc(zpl_adt_node *parent) { + if (!parent || (parent->type != ZPL_ADT_TYPE_OBJECT && parent->type != ZPL_ADT_TYPE_ARRAY)) { + return NULL; + } + + if (!parent->nodes) + return NULL; + + return zpl_adt_alloc_at(parent, zpl_array_count(parent->nodes)); + } + + + zpl_b8 zpl_adt_set_obj(zpl_adt_node *obj, char const *name, zpl_allocator backing) { + return zpl_adt_make_branch(obj, backing, name, 0); + } + zpl_b8 zpl_adt_set_arr(zpl_adt_node *obj, char const *name, zpl_allocator backing) { + return zpl_adt_make_branch(obj, backing, name, 1); + } + zpl_b8 zpl_adt_set_str(zpl_adt_node *obj, char const *name, char const *value) { + zpl_adt_make_leaf(obj, name, ZPL_ADT_TYPE_STRING); + obj->string = value; + return true; + } + zpl_b8 zpl_adt_set_flt(zpl_adt_node *obj, char const *name, zpl_f64 value) { + zpl_adt_make_leaf(obj, name, ZPL_ADT_TYPE_REAL); + obj->real = value; + return true; + } + zpl_b8 zpl_adt_set_int(zpl_adt_node *obj, char const *name, zpl_i64 value) { + zpl_adt_make_leaf(obj, name, ZPL_ADT_TYPE_INTEGER); + obj->integer = value; + return true; + } + + zpl_adt_node *zpl_adt_move_node_at(zpl_adt_node *node, zpl_adt_node *new_parent, zpl_isize index) { + ZPL_ASSERT_NOT_NULL(node); + ZPL_ASSERT_NOT_NULL(new_parent); + zpl_adt_node *old_parent = node->parent; + zpl_adt_node *new_node = zpl_adt_alloc_at(new_parent, index); + *new_node = *node; + new_node->parent = new_parent; + if (old_parent) { + zpl_adt_remove_node(node); + } + return new_node; + } + + zpl_adt_node *zpl_adt_move_node(zpl_adt_node *node, zpl_adt_node *new_parent) { + ZPL_ASSERT_NOT_NULL(node); + ZPL_ASSERT_NOT_NULL(new_parent); + ZPL_ASSERT(new_parent->type == ZPL_ADT_TYPE_ARRAY || new_parent->type == ZPL_ADT_TYPE_OBJECT); + return zpl_adt_move_node_at(node, new_parent, zpl_array_count(new_parent->nodes)); + } + + void zpl_adt_swap_nodes(zpl_adt_node *node, zpl_adt_node *other_node) { + ZPL_ASSERT_NOT_NULL(node); + ZPL_ASSERT_NOT_NULL(other_node); + zpl_adt_node *parent = node->parent; + zpl_adt_node *other_parent = other_node->parent; + zpl_isize index = (zpl_pointer_diff(parent->nodes, node) / zpl_size_of(zpl_adt_node)); + zpl_isize index2 = (zpl_pointer_diff(other_parent->nodes, other_node) / zpl_size_of(zpl_adt_node)); + zpl_adt_node temp = parent->nodes[index]; + temp.parent = other_parent; + other_parent->nodes[index2].parent = parent; + parent->nodes[index] = other_parent->nodes[index2]; + other_parent->nodes[index2] = temp; + } + + void zpl_adt_remove_node(zpl_adt_node *node) { + ZPL_ASSERT_NOT_NULL(node); + ZPL_ASSERT_NOT_NULL(node->parent); + zpl_adt_node *parent = node->parent; + zpl_isize index = (zpl_pointer_diff(parent->nodes, node) / zpl_size_of(zpl_adt_node)); + zpl_array_remove_at(parent->nodes, index); + } + + + zpl_adt_node *zpl_adt_append_obj(zpl_adt_node *parent, char const *name) { + zpl_adt_node *o = zpl_adt_alloc(parent); + if (!o) return NULL; + if (zpl_adt_set_obj(o, name, ZPL_ARRAY_HEADER(parent->nodes)->allocator)) { + zpl_adt_remove_node(o); + return NULL; + } + return o; + } + zpl_adt_node *zpl_adt_append_arr(zpl_adt_node *parent, char const *name) { + zpl_adt_node *o = zpl_adt_alloc(parent); + if (!o) return NULL; + if (zpl_adt_set_arr(o, name, ZPL_ARRAY_HEADER(parent->nodes)->allocator)) { + zpl_adt_remove_node(o); + return NULL; + } + return o; + } + zpl_adt_node *zpl_adt_append_str(zpl_adt_node *parent, char const *name, char const *value) { + zpl_adt_node *o = zpl_adt_alloc(parent); + if (!o) return NULL; + zpl_adt_set_str(o, name, value); + return o; + } + zpl_adt_node *zpl_adt_append_flt(zpl_adt_node *parent, char const *name, zpl_f64 value) { + zpl_adt_node *o = zpl_adt_alloc(parent); + if (!o) return NULL; + zpl_adt_set_flt(o, name, value); + return o; + } + zpl_adt_node *zpl_adt_append_int(zpl_adt_node *parent, char const *name, zpl_i64 value) { + zpl_adt_node *o = zpl_adt_alloc(parent); + if (!o) return NULL; + zpl_adt_set_int(o, name, value); + return o; + } + + /* parser helpers */ + char *zpl_adt_parse_number_strict(zpl_adt_node *node, char* base_str) { + ZPL_ASSERT_NOT_NULL(node); + ZPL_ASSERT_NOT_NULL(base_str); + char *p = base_str, *e = p; + + while (*e) + ++e; + + while (*p && (zpl_strchr("eE.+-", *p) || zpl_char_is_hex_digit(*p))) { + ++p; + } + + if (p >= e) { + return zpl_adt_parse_number(node, base_str); + } + + return base_str; + } + + char *zpl_adt_parse_number(zpl_adt_node *node, char* base_str) { + ZPL_ASSERT_NOT_NULL(node); + ZPL_ASSERT_NOT_NULL(base_str); + char *p = base_str, *e = p; + + zpl_i32 base=0; + zpl_i32 base2=0; + zpl_u8 base2_offset=0; + zpl_i8 exp=0,orig_exp=0; + zpl_u8 neg_zero=0; + zpl_u8 lead_digit=0; + zpl_u8 node_type=0; + zpl_u8 node_props=0; + + /* skip false positives and special cases */ + if (!!zpl_strchr("eE", *p) || (!!zpl_strchr(".+-", *p) && !zpl_char_is_hex_digit(*(p+1)) && *(p+1) != '.')) { + return ++base_str; + } + + node_type = ZPL_ADT_TYPE_INTEGER; + neg_zero = false; + + zpl_isize ib = 0; + char buf[48] = { 0 }; + + if (*e == '+') + ++e; + else if (*e == '-') { + buf[ib++] = *e++; + } + + if (*e == '.') { + node_type = ZPL_ADT_TYPE_REAL; + node_props = ZPL_ADT_PROPS_IS_PARSED_REAL; + lead_digit = false; + buf[ib++] = '0'; + do { + buf[ib++] = *e; + } while (zpl_char_is_digit(*++e)); + } else { + if (!zpl_strncmp(e, "0x", 2) || !zpl_strncmp(e, "0X", 2)) { node_props = ZPL_ADT_PROPS_IS_HEX; } + + /* bail if ZPL_ADT_PROPS_IS_HEX is unset but we get 'x' on input */ + if (zpl_char_to_lower(*e) == 'x' && (node_props != ZPL_ADT_PROPS_IS_HEX)) { + return ++base_str; + } + + while (zpl_char_is_hex_digit(*e) || zpl_char_to_lower(*e) == 'x') { buf[ib++] = *e++; } + + if (*e == '.') { + node_type = ZPL_ADT_TYPE_REAL; + lead_digit = true; + zpl_u32 step = 0; + + do { + buf[ib++] = *e; + ++step; + } while (zpl_char_is_digit(*++e)); + + if (step < 2) { buf[ib++] = '0'; } + } + } + + /* check if we have a dot here, this is a false positive (IP address, ...) */ + if (*e == '.') { + return ++base_str; + } + + zpl_f32 eb = 10; + char expbuf[6] = { 0 }; + zpl_isize expi = 0; + + if (*e && !!zpl_strchr("eE", *e)) { + ++e; + if (*e == '+' || *e == '-' || zpl_char_is_digit(*e)) { + if (*e == '-') { eb = 0.1f; } + if (!zpl_char_is_digit(*e)) { ++e; } + while (zpl_char_is_digit(*e)) { expbuf[expi++] = *e++; } + } + + orig_exp = exp = (zpl_u8)zpl_str_to_i64(expbuf, NULL, 10); + } + + if (node_type == ZPL_ADT_TYPE_INTEGER) { + node->integer = zpl_str_to_i64(buf, 0, 0); + #ifndef ZPL_PARSER_DISABLE_ANALYSIS + /* special case: negative zero */ + if (node->integer == 0 && buf[0] == '-') { + neg_zero = true; + } + #endif + while (orig_exp-- > 0) { node->integer *= (zpl_i64)eb; } + } else { + node->real = zpl_str_to_f64(buf, 0); + + #ifndef ZPL_PARSER_DISABLE_ANALYSIS + char *q = buf, *base_string = q, *base_string2 = q; + base_string = cast(char *)zpl_str_skip(base_string, '.'); + *base_string = '\0'; + base_string2 = base_string + 1; + char *base_string_off = base_string2; + while (*base_string_off++ == '0') base2_offset++; + + base = (zpl_i32)zpl_str_to_i64(q, 0, 0); + base2 = (zpl_i32)zpl_str_to_i64(base_string2, 0, 0); + if (exp) { + exp = exp * (!(eb == 10.0f) ? -1 : 1); + node_props = ZPL_ADT_PROPS_IS_EXP; + } + + /* special case: negative zero */ + if (base == 0 && buf[0] == '-') { + neg_zero = true; + } + #endif + while (orig_exp-- > 0) { node->real *= eb; } + } + + node->type = node_type; + node->props = node_props; + + #ifndef ZPL_PARSER_DISABLE_ANALYSIS + node->base = base; + node->base2 = base2; + node->base2_offset = base2_offset; + node->exp = exp; + node->neg_zero = neg_zero; + node->lead_digit = lead_digit; + #else + zpl_unused(base); + zpl_unused(base2); + zpl_unused(base2_offset); + zpl_unused(exp); + zpl_unused(neg_zero); + zpl_unused(lead_digit); + #endif + return e; + } + + zpl_adt_error zpl_adt_print_number(zpl_file *file, zpl_adt_node *node) { + ZPL_ASSERT_NOT_NULL(file); + ZPL_ASSERT_NOT_NULL(node); + if (node->type != ZPL_ADT_TYPE_INTEGER && node->type != ZPL_ADT_TYPE_REAL) { + return ZPL_ADT_ERROR_INVALID_TYPE; + } + + #ifndef ZPL_PARSER_DISABLE_ANALYSIS + if (node->neg_zero) { + zpl__adt_fprintf(file, "-"); + } + #endif + + switch (node->type) { + case ZPL_ADT_TYPE_INTEGER: { + if (node->props == ZPL_ADT_PROPS_IS_HEX) { + zpl__adt_fprintf(file, "0x%llx", (long long)node->integer); + } else { + zpl__adt_fprintf(file, "%lld", (long long)node->integer); + } + } break; + + case ZPL_ADT_TYPE_REAL: { + if (node->props == ZPL_ADT_PROPS_NAN) { + zpl__adt_fprintf(file, "NaN"); + } else if (node->props == ZPL_ADT_PROPS_NAN_NEG) { + zpl__adt_fprintf(file, "-NaN"); + } else if (node->props == ZPL_ADT_PROPS_INFINITY) { + zpl__adt_fprintf(file, "Infinity"); + } else if (node->props == ZPL_ADT_PROPS_INFINITY_NEG) { + zpl__adt_fprintf(file, "-Infinity"); + } else if (node->props == ZPL_ADT_PROPS_TRUE) { + zpl__adt_fprintf(file, "true"); + } else if (node->props == ZPL_ADT_PROPS_FALSE) { + zpl__adt_fprintf(file, "false"); + } else if (node->props == ZPL_ADT_PROPS_NULL) { + zpl__adt_fprintf(file, "null"); + #ifndef ZPL_PARSER_DISABLE_ANALYSIS + } else if (node->props == ZPL_ADT_PROPS_IS_EXP) { + zpl__adt_fprintf(file, "%lld.%0*d%llde%lld", (long long)node->base, node->base2_offset, 0, (long long)node->base2, (long long)node->exp); + } else if (node->props == ZPL_ADT_PROPS_IS_PARSED_REAL) { + if (!node->lead_digit) + zpl__adt_fprintf(file, ".%0*d%lld", node->base2_offset, 0, (long long)node->base2); + else + zpl__adt_fprintf(file, "%lld.%0*d%lld", (long long int)node->base2_offset, 0, (int)node->base, (long long)node->base2); + #endif + } else { + zpl__adt_fprintf(file, "%f", node->real); + } + } break; + } + + return ZPL_ADT_ERROR_NONE; + } + + zpl_adt_error zpl_adt_print_string(zpl_file *file, zpl_adt_node *node, char const *escaped_chars, char const *escape_symbol) { + ZPL_ASSERT_NOT_NULL(file); + ZPL_ASSERT_NOT_NULL(node); + ZPL_ASSERT_NOT_NULL(escaped_chars); + if (node->type != ZPL_ADT_TYPE_STRING && node->type != ZPL_ADT_TYPE_MULTISTRING) { + return ZPL_ADT_ERROR_INVALID_TYPE; + } + + /* escape string */ + char const* p = node->string, *b = p; + + if (!p) + return ZPL_ADT_ERROR_NONE; + + do { + p = zpl_str_skip_any(p, escaped_chars); + zpl__adt_fprintf(file, "%.*s", zpl_ptr_diff(b, p), b); + if (*p && !!zpl_strchr(escaped_chars, *p)) { + zpl__adt_fprintf(file, "%s%c", escape_symbol, *p); + p++; + } + b = p; + } while (*p); + + return ZPL_ADT_ERROR_NONE; + } + + zpl_adt_error zpl_adt_str_to_number(zpl_adt_node *node) { + ZPL_ASSERT(node); + + if (node->type == ZPL_ADT_TYPE_REAL || node->type == ZPL_ADT_TYPE_INTEGER) return ZPL_ADT_ERROR_ALREADY_CONVERTED; /* this is already converted/parsed */ + if (node->type != ZPL_ADT_TYPE_STRING && node->type != ZPL_ADT_TYPE_MULTISTRING) { + return ZPL_ADT_ERROR_INVALID_TYPE; + } + + zpl_adt_parse_number(node, (char *)node->string); + + return ZPL_ADT_ERROR_NONE; + } + + zpl_adt_error zpl_adt_str_to_number_strict(zpl_adt_node *node) { + ZPL_ASSERT(node); + + if (node->type == ZPL_ADT_TYPE_REAL || node->type == ZPL_ADT_TYPE_INTEGER) return ZPL_ADT_ERROR_ALREADY_CONVERTED; /* this is already converted/parsed */ + if (node->type != ZPL_ADT_TYPE_STRING && node->type != ZPL_ADT_TYPE_MULTISTRING) { + return ZPL_ADT_ERROR_INVALID_TYPE; + } + + zpl_adt_parse_number_strict(node, (char *)node->string); + + return ZPL_ADT_ERROR_NONE; + } + + #undef zpl__adt_fprintf + + ZPL_END_C_DECLS + + /* parsers */ + // file: source/parsers/json.c + + //////////////////////////////////////////////////////////////// + // + // JSON5 Parser + // + // + + + #ifdef ZPL_JSON_DEBUG + #define ZPL_JSON_ASSERT(msg) ZPL_PANIC(msg) + #else + #define ZPL_JSON_ASSERT(msg) + #endif + + ZPL_BEGIN_C_DECLS + + char *zpl__json_parse_object(zpl_adt_node *obj, char *base, zpl_allocator a, zpl_u8 *err_code); + char *zpl__json_parse_array(zpl_adt_node *obj, char *base, zpl_allocator a, zpl_u8 *err_code); + char *zpl__json_parse_value(zpl_adt_node *obj, char *base, zpl_allocator a, zpl_u8 *err_code); + char *zpl__json_parse_name(zpl_adt_node *obj, char *base, zpl_u8 *err_code); + char *zpl__json_trim(char *base, zpl_b32 catch_newline); + zpl_b8 zpl__json_write_value(zpl_file *f, zpl_adt_node *o, zpl_adt_node *t, zpl_isize indent, zpl_b32 is_inline, zpl_b32 is_last); + + #define zpl__json_fprintf(s_, fmt_, ...) \ + do { \ + if (zpl_fprintf(s_, fmt_, ##__VA_ARGS__) < 0) return false; \ + } while (0) + + #define zpl___ind(x) if (x > 0) zpl__json_fprintf(f, "%*r", x, ' '); + + zpl_u8 zpl_json_parse(zpl_adt_node *root, char *text, zpl_allocator a) { + zpl_u8 err_code = ZPL_JSON_ERROR_NONE; + ZPL_ASSERT(root); + ZPL_ASSERT(text); + zpl_zero_item(root); + text = zpl__json_trim(text, true); + + #ifndef ZPL_PARSER_DISABLE_ANALYSIS + if (!zpl_strchr("{[", *text)) { + root->cfg_mode = true; + } + #endif + + zpl__json_parse_object(root, text, a, &err_code); + return err_code; + } + + void zpl_json_free(zpl_adt_node *obj) { + zpl_adt_destroy_branch(obj); + } + + zpl_string zpl_json_write_string(zpl_allocator a, zpl_adt_node *obj, zpl_isize indent) { + zpl_file tmp; + if (!zpl_file_stream_new(&tmp, a)) + return NULL; + if (!zpl_json_write(&tmp, obj, indent)) + return NULL; + zpl_isize fsize; + zpl_u8* buf = zpl_file_stream_buf(&tmp, &fsize); + zpl_string output = zpl_string_make_length(a, (char *)buf, fsize); + zpl_file_close(&tmp); + return output; + } + + /* private */ + + #define zpl__json_append_node(x, item)\ + do {\ + if (!zpl_array_append(x, item)) {\ + *err_code = ZPL_JSON_ERROR_OUT_OF_MEMORY;\ + return NULL;\ + }\ + if (item.type == ZPL_ADT_TYPE_OBJECT || item.type == ZPL_ADT_TYPE_ARRAY) {\ + for (zpl_isize i = 0; i < zpl_array_count(item.nodes); i++)\ + item.nodes[i].parent = zpl_array_end(x);\ + }\ + } while (0); + + static ZPL_ALWAYS_INLINE zpl_b32 zpl__json_is_assign_char(char c) { return !!zpl_strchr(":=|", c); } + static ZPL_ALWAYS_INLINE zpl_b32 zpl__json_is_delim_char(char c) { return !!zpl_strchr(",|\n", c); } + static ZPL_ALWAYS_INLINE const char *zpl__json_string_space(zpl_isize indent) { return indent == ZPL_JSON_INDENT_STYLE_COMPACT ? "" : " "; } + static ZPL_ALWAYS_INLINE const char *zpl__json_string_eol(zpl_adt_node *o, zpl_isize indent) { + zpl_b8 force_new_line = false; + #ifndef ZPL_PARSER_DISABLE_ANALYSIS + force_new_line = (o->delim_style != ZPL_ADT_DELIM_STYLE_COMMA); + #endif + return !force_new_line && indent == ZPL_JSON_INDENT_STYLE_COMPACT ? "" : "\n"; + } + ZPL_DEF_INLINE zpl_b32 zpl__json_validate_name(char const *str, char *err); + + #define jx(x) !zpl_char_is_hex_digit(str[x]) + ZPL_IMPL_INLINE zpl_b32 zpl__json_validate_name(char const *str, char *err) { + while (*str) { + /* todo: refactor name validation. */ + if ((str[0] == '\\' && !zpl_char_is_control(str[1])) && + (str[0] == '\\' && jx(1) && jx(2) && jx(3) && jx(4))) { + if (err) *err = *str; + return false; + } + + ++str; + } + + return true; + } + #undef jx + + char *zpl__json_parse_array(zpl_adt_node *obj, char *base, zpl_allocator a, zpl_u8 *err_code) { + ZPL_ASSERT(obj && base); + char *p = base; + + obj->type = ZPL_ADT_TYPE_ARRAY; + if (!zpl_array_init(obj->nodes, a)) { + *err_code = ZPL_JSON_ERROR_OUT_OF_MEMORY; + return NULL; + } + + while (*p) { + p = zpl__json_trim(p, false); + + if (*p == ']') { + return p; + } + + zpl_adt_node elem = { 0 }; + p = zpl__json_parse_value(&elem, p, a, err_code); + + if (*err_code != ZPL_JSON_ERROR_NONE) { return NULL; } + + zpl__json_append_node(obj->nodes, elem); + + p = zpl__json_trim(p, false); + + if (*p == ',') { + ++p; + continue; + } else { + if (*p != ']') { + ZPL_JSON_ASSERT("end of array unfulfilled"); + *err_code = ZPL_JSON_ERROR_ARRAY_LEFT_OPEN; + return NULL; + } + return p; + } + } + + *err_code = ZPL_JSON_ERROR_INTERNAL; + return NULL; + } + + char *zpl__json_parse_value(zpl_adt_node *obj, char *base, zpl_allocator a, zpl_u8 *err_code) { + ZPL_ASSERT(obj && base); + char *p = base, *b = p, *e = p; + + /* handle quoted strings */ + if (!!zpl_strchr("`\"'", *p)) { + char c = *p; + obj->type = (c == '`') ? ZPL_ADT_TYPE_MULTISTRING : ZPL_ADT_TYPE_STRING; + b = e = p + 1; + obj->string = b; + e = cast(char *)zpl_str_skip_literal(e, c); + *e = '\0', p = e + 1; + } else if (zpl_char_is_alpha(*p) || (*p == '-' && !zpl_char_is_digit(*(p + 1)))) { + /* handle constants */ + if (zpl_str_has_prefix(p, "true")) { + obj->type = ZPL_ADT_TYPE_REAL; + obj->props = ZPL_ADT_PROPS_TRUE; + obj->real = 1; + p += 4; + } else if (zpl_str_has_prefix(p, "false")) { + obj->type = ZPL_ADT_TYPE_REAL; + obj->props = ZPL_ADT_PROPS_FALSE; + obj->real = 0; + p += 5; + } else if (zpl_str_has_prefix(p, "null")) { + obj->type = ZPL_ADT_TYPE_REAL; + obj->props = ZPL_ADT_PROPS_NULL; + obj->real = 0; + p += 4; + } else if (zpl_str_has_prefix(p, "Infinity")) { + obj->type = ZPL_ADT_TYPE_REAL; + obj->real = ZPL_INFINITY; + obj->props = ZPL_ADT_PROPS_INFINITY; + p += 8; + } else if (zpl_str_has_prefix(p, "-Infinity")) { + obj->type = ZPL_ADT_TYPE_REAL; + obj->real = -ZPL_INFINITY; + obj->props = ZPL_ADT_PROPS_INFINITY_NEG; + p += 9; + } else if (zpl_str_has_prefix(p, "NaN")) { + obj->type = ZPL_ADT_TYPE_REAL; + obj->real = ZPL_NAN; + obj->props = ZPL_ADT_PROPS_NAN; + p += 3; + } else if (zpl_str_has_prefix(p, "-NaN")) { + obj->type = ZPL_ADT_TYPE_REAL; + obj->real = -ZPL_NAN; + obj->props = ZPL_ADT_PROPS_NAN_NEG; + p += 4; + } else { + ZPL_JSON_ASSERT("unknown keyword"); + *err_code = ZPL_JSON_ERROR_UNKNOWN_KEYWORD; + return NULL; + } + } else if (zpl_char_is_digit(*p) || *p == '+' || *p == '-' || *p == '.') { + /* handle numbers */ + /* defer operation to our helper method. */ + p = zpl_adt_parse_number(obj, p); + } else if (!!zpl_strchr("[{", *p)) { + /* handle compound objects */ + p = zpl__json_parse_object(obj, p, a, err_code); + ++p; + } + + return p; + } + + char *zpl__json_parse_object(zpl_adt_node *obj, char *base, zpl_allocator a, zpl_u8 *err_code) { + ZPL_ASSERT(obj && base); + char *p = base; + + p = zpl__json_trim(p, false); + /**/ if (*p == '{') { ++p; } + else if (*p == '[') { /* special case for when we call this func on an array. */ + ++p; + obj->type = ZPL_ADT_TYPE_ARRAY; + return zpl__json_parse_array(obj, p, a, err_code); + } + + if (!zpl_array_init(obj->nodes, a)) { + *err_code = ZPL_JSON_ERROR_OUT_OF_MEMORY; + return NULL; + } + obj->type = ZPL_ADT_TYPE_OBJECT; + + do { + zpl_adt_node node = { 0 }; + p = zpl__json_trim(p, false); + if (*p == '}' && obj->type == ZPL_ADT_TYPE_OBJECT) return p; + else if (*p == ']' && obj->type == ZPL_ADT_TYPE_ARRAY) return p; + else if (!!zpl_strchr("}]", *p)) { + ZPL_JSON_ASSERT("mismatched end pair"); + *err_code = ZPL_JSON_ERROR_OBJECT_END_PAIR_MISMATCHED; + return NULL; + } + + /* First, we parse the key, then we proceed to the value itself. */ + p = zpl__json_parse_name(&node, p, err_code); + if (err_code && *err_code != ZPL_JSON_ERROR_NONE) { return NULL; } + p = zpl__json_trim(p + 1, false); + p = zpl__json_parse_value(&node, p, a, err_code); + if (err_code && *err_code != ZPL_JSON_ERROR_NONE) { return NULL; } + + zpl__json_append_node(obj->nodes, node); + + char *end_p = p; zpl_unused(end_p); + p = zpl__json_trim(p, true); + + /* this code analyses the keyvalue pair delimiter used in the packet. */ + if (zpl__json_is_delim_char(*p)) { + #ifndef ZPL_PARSER_DISABLE_ANALYSIS + zpl_adt_node *n = zpl_array_end(obj->nodes); + n->delim_style = ZPL_ADT_DELIM_STYLE_COMMA; + + if (*p == '\n') + n->delim_style = ZPL_ADT_DELIM_STYLE_NEWLINE; + else if (*p == '|') { + n->delim_style = ZPL_ADT_DELIM_STYLE_LINE; + n->delim_line_width = cast(zpl_u8)(p-end_p); + } + #endif + if (*p == 0) { + return p; + } + ++p; + } + p = zpl__json_trim(p, false); + } while (*p); + return p; + } + + char *zpl__json_parse_name(zpl_adt_node *node, char *base, zpl_u8 *err_code) { + char *p = base, *b = p, *e = p; + zpl_u8 name_style=0; + + if (*p == '"' || *p == '\'' || zpl_char_is_alpha(*p) || *p == '_' || *p == '$') { + if (*p == '"' || *p == '\'') { + #ifndef ZPL_PARSER_DISABLE_ANALYSIS + if (*p == '"') { + node->name_style = ZPL_ADT_NAME_STYLE_DOUBLE_QUOTE; + } else if (*p == '\'') { + node->name_style = ZPL_ADT_NAME_STYLE_SINGLE_QUOTE; + } + #endif + char c = *p; + b = ++p; + e = cast(char *)zpl_str_control_skip(b, c); + node->name = b; + + /* we can safely null-terminate here, since "e" points to the quote pair end. */ + *e++ = '\0'; + } + else { + b = e = p; + zpl_str_advance_while(e, *e && (zpl_char_is_alphanumeric(*e) || *e == '_') && !zpl_char_is_space(*e) && !zpl__json_is_assign_char(*e)); + node->name = b; + name_style = ZPL_ADT_NAME_STYLE_NO_QUOTES; + /* we defer null-termination as it can potentially wipe our assign char as well. */ + } + + char *assign_p = e; zpl_unused(assign_p); + p = zpl__json_trim(e, false); + #ifndef ZPL_PARSER_DISABLE_ANALYSIS + node->assign_line_width = cast(zpl_u8)(p-assign_p); + #endif + + if (*p && !zpl__json_is_assign_char(*p)) { + ZPL_JSON_ASSERT("invalid assignment"); + *err_code = ZPL_JSON_ERROR_INVALID_ASSIGNMENT; + return NULL; + } + else + { + #ifndef ZPL_PARSER_DISABLE_ANALYSIS + if (*p == '=') + node->assign_style = ZPL_ADT_ASSIGN_STYLE_EQUALS; + else if (*p == '|') + node->assign_style = ZPL_ADT_ASSIGN_STYLE_LINE; + else node->assign_style = ZPL_ADT_ASSIGN_STYLE_COLON; + #endif + } + + /* since we already know the assign style, we can cut it here for unquoted names */ + if (name_style == ZPL_ADT_NAME_STYLE_NO_QUOTES && *e) { + *e = '\0'; + #ifndef ZPL_PARSER_DISABLE_ANALYSIS + node->name_style = name_style; + #endif + } + } + + if (node->name && !zpl__json_validate_name(node->name, NULL)) { + ZPL_JSON_ASSERT("invalid name"); + *err_code = ZPL_JSON_ERROR_INVALID_NAME; + return NULL; + } + + return p; + } + + char *zpl__json_trim(char *base, zpl_b32 catch_newline) { + ZPL_ASSERT_NOT_NULL(base); + char *p = base; + do { + if (zpl_str_has_prefix(p, "//")) { + const char *e = zpl_str_skip(p, '\n'); + p += (e-p); + } + else if (zpl_str_has_prefix(p, "/*")) { + const char *e = zpl_str_skip(p+2, '*'); + if (*e && *(e+1) == '/') { + e+=2; /* advance past end comment block */ + p += (e-p); + } + } + else if (*p == '\n' && catch_newline) { + return p; + } + else if (!zpl_char_is_space(*p)) { + return p; + } + } while (*p++); + return NULL; + } + + zpl_b8 zpl_json_write(zpl_file *f, zpl_adt_node *o, zpl_isize indent) { + if (!o) + return true; + + ZPL_ASSERT(o->type == ZPL_ADT_TYPE_OBJECT || o->type == ZPL_ADT_TYPE_ARRAY); + + zpl___ind(indent - 4); + #ifndef ZPL_PARSER_DISABLE_ANALYSIS + if (!o->cfg_mode) + #else + if (1) + #endif + zpl__json_fprintf(f, "%c%s", o->type == ZPL_ADT_TYPE_OBJECT ? '{' : '[', zpl__json_string_eol(o, indent)); + else + { + indent -= 4; + } + + if (o->nodes) { + zpl_isize cnt = zpl_array_count(o->nodes); + + for (int i = 0; i < cnt; ++i) { + if (!zpl__json_write_value(f, o->nodes + i, o, indent, false, !(i < cnt - 1))) return false; + } + } + + zpl___ind(indent); + + if (indent > 0) { + zpl__json_fprintf(f, "%c", o->type == ZPL_ADT_TYPE_OBJECT ? '}' : ']'); + } else { + #ifndef ZPL_PARSER_DISABLE_ANALYSIS + if (!o->cfg_mode) + #endif + zpl__json_fprintf(f, "%c%s", o->type == ZPL_ADT_TYPE_OBJECT ? '}' : ']', zpl__json_string_eol(o, indent)); + } + + return true; + } + + zpl_b8 zpl__json_write_value(zpl_file *f, zpl_adt_node *o, zpl_adt_node *t, zpl_isize indent, zpl_b32 is_inline, zpl_b32 is_last) { + zpl_adt_node *node = o; + if (indent != ZPL_JSON_INDENT_STYLE_COMPACT) indent += 4; + + if (!is_inline) { + zpl___ind(indent); + + if (t->type != ZPL_ADT_TYPE_ARRAY) { + #ifndef ZPL_PARSER_DISABLE_ANALYSIS + switch (node->name_style) { + case ZPL_ADT_NAME_STYLE_DOUBLE_QUOTE: { + zpl__json_fprintf(f, "\"%s\"", node->name); + } break; + + case ZPL_ADT_NAME_STYLE_SINGLE_QUOTE: { + zpl__json_fprintf(f, "\'%s\'", node->name); + } break; + + case ZPL_ADT_NAME_STYLE_NO_QUOTES: { + zpl__json_fprintf(f, "%s", node->name); + } break; + } + + if (o->assign_style == ZPL_ADT_ASSIGN_STYLE_COLON) + zpl__json_fprintf(f, ":%s", zpl__json_string_space(indent)); + else { + if (indent != ZPL_JSON_INDENT_STYLE_COMPACT) + zpl___ind(zpl_max(o->assign_line_width, 1)); + + if (o->assign_style == ZPL_ADT_ASSIGN_STYLE_EQUALS) + zpl__json_fprintf(f, "=%s", zpl__json_string_space(indent)); + else if (o->assign_style == ZPL_ADT_ASSIGN_STYLE_LINE) { + zpl__json_fprintf(f, "|%s", zpl__json_string_space(indent)); + } + } + #else + zpl__json_fprintf(f, "\"%s\":%s", node->name, zpl__json_string_space(indent)); + #endif + } + } + + switch (node->type) { + case ZPL_ADT_TYPE_STRING: { + zpl__json_fprintf(f, "\""); + if (zpl_adt_print_string(f, node, "\"", "\\")) return false; + zpl__json_fprintf(f, "\""); + } break; + + case ZPL_ADT_TYPE_MULTISTRING: { + zpl__json_fprintf(f, "`"); + if (zpl_adt_print_string(f, node, "`", "\\")) return false; + zpl__json_fprintf(f, "`"); + } break; + + case ZPL_ADT_TYPE_ARRAY: { + zpl__json_fprintf(f, "["); + zpl_isize elemn = zpl_array_count(node->nodes); + for (int j = 0; j < elemn; ++j) { + zpl_isize ind = ((node->nodes + j)->type == ZPL_ADT_TYPE_OBJECT || (node->nodes + j)->type == ZPL_ADT_TYPE_ARRAY) ? 0 : -4; + if (!zpl__json_write_value(f, node->nodes + j, o, indent == ZPL_JSON_INDENT_STYLE_COMPACT ? indent : ind, true, true)) return false; + + if (j < elemn - 1) { zpl__json_fprintf(f, ", "); } + } + zpl__json_fprintf(f, "]"); + } break; + + case ZPL_ADT_TYPE_REAL: + case ZPL_ADT_TYPE_INTEGER: { + if (zpl_adt_print_number(f, node)) return false; + } break; + + case ZPL_ADT_TYPE_OBJECT: { + if (!zpl_json_write(f, node, indent)) return false; + } break; + } + + if (!is_inline) { + #ifndef ZPL_PARSER_DISABLE_ANALYSIS + if (o->delim_style != ZPL_ADT_DELIM_STYLE_COMMA) { + if (o->delim_style == ZPL_ADT_DELIM_STYLE_NEWLINE) + zpl__json_fprintf(f, "\n"); + else if (o->delim_style == ZPL_ADT_DELIM_STYLE_LINE) { + zpl___ind(o->delim_line_width); + zpl__json_fprintf(f, "|\n"); + } + } + else { + if (!is_last) { + zpl__json_fprintf(f, ",%s", zpl__json_string_eol(o, indent)); + } else { + zpl__json_fprintf(f, "%s", zpl__json_string_eol(o, indent)); + } + } + #else + if (!is_last) { + zpl__json_fprintf(f, ",%s", zpl__json_string_eol(o, indent)); + } else { + zpl__json_fprintf(f, "%s", zpl__json_string_eol(o, indent)); + } + #endif + } + + return true; + } + + #undef zpl__json_fprintf + #undef zpl___ind + #undef zpl__json_append_node + + ZPL_END_C_DECLS + // file: source/parsers/csv.c + + + #ifdef ZPL_CSV_DEBUG + #define ZPL_CSV_ASSERT(msg) ZPL_PANIC(msg) + #else + #define ZPL_CSV_ASSERT(msg) + #endif + + + ZPL_BEGIN_C_DECLS + + zpl_u8 zpl_csv_parse_delimiter(zpl_csv_object *root, char *text, zpl_allocator allocator, zpl_b32 has_header, char delim) { + zpl_csv_error err = ZPL_CSV_ERROR_NONE; + ZPL_ASSERT_NOT_NULL(root); + ZPL_ASSERT_NOT_NULL(text); + zpl_zero_item(root); + zpl_adt_make_branch(root, allocator, NULL, has_header ? false : true); + char *p = text, *b = p, *e = p; + zpl_isize colc = 0, total_colc = 0; + + do { + char d = 0; + p = cast(char *)zpl_str_trim(p, false); + if (*p == 0) break; + zpl_adt_node row_item = {0}; + row_item.type = ZPL_ADT_TYPE_STRING; + #ifndef ZPL_PARSER_DISABLE_ANALYSIS + row_item.name_style = ZPL_ADT_NAME_STYLE_NO_QUOTES; + #endif + + /* handle string literals */ + if (*p == '"') { + p = b = e = p+1; + row_item.string = b; + #ifndef ZPL_PARSER_DISABLE_ANALYSIS + row_item.name_style = ZPL_ADT_NAME_STYLE_DOUBLE_QUOTE; + #endif + do { + e = cast(char *)zpl_str_skip(e, '"'); + if (*e && *(e+1) == '"') { + e += 2; + } + else break; + } while (*e); + if (*e == 0) { + ZPL_CSV_ASSERT("unmatched quoted string"); + err = ZPL_CSV_ERROR_UNEXPECTED_END_OF_INPUT; + return err; + } + *e = 0; + p = cast(char *)zpl_str_trim(e+1, true); + d = *p; + + /* unescape escaped quotes (so that unescaped text escapes :) */ + { + char *ep = b; + do { + if (*ep == '"' && *(ep+1) == '"') { + zpl_memmove(ep, ep+1, zpl_strlen(ep)); + } + ep++; + } while (*ep); + } + } + else if (*p == delim) { + d = *p; + row_item.string = ""; + } + else if (*p) { + /* regular data */ + b = e = p; + row_item.string = b; + do { + e++; + } while (*e && *e != delim && *e != '\n'); + if (*e) { + p = cast(char *)zpl_str_trim(e, true); + while (zpl_char_is_space(*(e-1))) { e--; } + d = *p; + *e = 0; + } + else { + d = 0; + p = e; + } + + /* check if number and process if so */ + zpl_b32 skip_number = false; + char *num_p = b; + do { + if (!zpl_char_is_hex_digit(*num_p) && (!zpl_strchr("+-.eExX", *num_p))) { + skip_number = true; + break; + } + } while (*num_p++); + + if (!skip_number) { + zpl_adt_str_to_number(&row_item); + } + } + + if (colc >= zpl_array_count(root->nodes)) { + zpl_adt_append_arr(root, NULL); + } + + zpl_array_append(root->nodes[colc].nodes, row_item); + + if (d == delim) { + colc++; + p++; + } + else if (d == '\n' || d == 0) { + /* check if number of rows is not mismatched */ + if (total_colc < colc) total_colc = colc; + else if (total_colc != colc) { + ZPL_CSV_ASSERT("mismatched rows"); + err = ZPL_CSV_ERROR_MISMATCHED_ROWS; + return err; + } + colc = 0; + if (d != 0) p++; + } + } while(*p); + + if (zpl_array_count(root->nodes) == 0) { + ZPL_CSV_ASSERT("unexpected end of input. stream is empty."); + err = ZPL_CSV_ERROR_UNEXPECTED_END_OF_INPUT; + return err; + } + + /* consider first row as a header. */ + if (has_header) { + for (zpl_isize i = 0; i < zpl_array_count(root->nodes); i++) { + zpl_csv_object *col = root->nodes + i; + zpl_csv_object *hdr = col->nodes; + col->name = hdr->string; + zpl_array_remove_at(col->nodes, 0); + } + } + + return err; + } + void zpl_csv_free(zpl_csv_object *obj) { + zpl_adt_destroy_branch(obj); + } + + void zpl__csv_write_record(zpl_file *file, zpl_csv_object *node) { + switch (node->type) { + case ZPL_ADT_TYPE_STRING: { + #ifndef ZPL_PARSER_DISABLE_ANALYSIS + switch (node->name_style) { + case ZPL_ADT_NAME_STYLE_DOUBLE_QUOTE: { + zpl_fprintf(file, "\""); + zpl_adt_print_string(file, node, "\"", "\""); + zpl_fprintf(file, "\""); + } break; + + case ZPL_ADT_NAME_STYLE_NO_QUOTES: { + #endif + zpl_fprintf(file, "%s", node->string); + #ifndef ZPL_PARSER_DISABLE_ANALYSIS + } break; + } + #endif + } break; + + case ZPL_ADT_TYPE_REAL: + case ZPL_ADT_TYPE_INTEGER: { + zpl_adt_print_number(file, node); + } break; + } + } + + void zpl__csv_write_header(zpl_file *file, zpl_csv_object *header) { + zpl_csv_object temp = *header; + temp.string = temp.name; + temp.type = ZPL_ADT_TYPE_STRING; + zpl__csv_write_record(file, &temp); + } + + void zpl_csv_write_delimiter(zpl_file *file, zpl_csv_object *obj, char delimiter) { + ZPL_ASSERT_NOT_NULL(file); + ZPL_ASSERT_NOT_NULL(obj); + ZPL_ASSERT(obj->nodes); + zpl_isize cols = zpl_array_count(obj->nodes); + if (cols == 0) return; + + zpl_isize rows = zpl_array_count(obj->nodes[0].nodes); + if (rows == 0) return; + + zpl_b32 has_headers = obj->nodes[0].name != NULL; + + if (has_headers) { + for (zpl_isize i = 0; i < cols; i++) { + zpl__csv_write_header(file, &obj->nodes[i]); + if (i+1 != cols) { + zpl_fprintf(file, "%c", delimiter); + } + } + zpl_fprintf(file, "\n"); + } + + for (zpl_isize r = 0; r < rows; r++) { + for (zpl_isize i = 0; i < cols; i++) { + zpl__csv_write_record(file, &obj->nodes[i].nodes[r]); + if (i+1 != cols) { + zpl_fprintf(file, "%c", delimiter); + } + } + zpl_fprintf(file, "\n"); + } + } + + zpl_string zpl_csv_write_string_delimiter(zpl_allocator a, zpl_csv_object *obj, char delimiter) { + zpl_file tmp; + zpl_file_stream_new(&tmp, a); + zpl_csv_write_delimiter(&tmp, obj, delimiter); + zpl_isize fsize; + zpl_u8* buf = zpl_file_stream_buf(&tmp, &fsize); + zpl_string output = zpl_string_make_length(a, (char *)buf, fsize); + zpl_file_close(&tmp); + return output; + } + + ZPL_END_C_DECLS + // file: source/parsers/uri.c + + ZPL_BEGIN_C_DECLS + + void zpl__uri_decode_str(char *text) { + char buf[ZPL_PRINTF_MAXLEN] = {0}, *p = buf; + char *tp = text; + + char ch = -1; + while (*tp) { + ch = *tp++; + + if (ch != '%') { + if (ch == '+') { + *(p++) = ' '; + } else { + *(p++) = ch; + } + } else { + char hex[3] = {0}; + hex[0] = tp[0]; + hex[1] = tp[1]; + char b = (char)zpl_str_to_i64(hex, NULL, 16); + *(p++) = b; + tp += 2; + } + } + + zpl_strcpy(text, buf); + text[p-buf] = 0; + } + + zpl_u8 zpl_uri_init(zpl_adt_node *root, char *origin, zpl_allocator a) { + zpl_u8 err_code = ZPL_URI_ERROR_NONE; + ZPL_ASSERT_NOT_NULL(root); + zpl_zero_item(root); + zpl_adt_set_obj(root, NULL, a); + if (origin) + zpl_adt_append_str(root, "__zpl_origin__", origin); + return err_code; + } + + zpl_u8 zpl_uri_parse(zpl_adt_node *root, char *text, zpl_allocator a) { + zpl_u8 err_code = ZPL_URI_ERROR_NONE; + ZPL_ASSERT_NOT_NULL(root); + ZPL_ASSERT_NOT_NULL(text); + zpl_zero_item(root); + text = (char *)zpl_str_trim(text, false); + + zpl_adt_set_obj(root, NULL, a); + + char *p = text, *b = p; + + if (*p == 0) { + return err_code; + } + + // NOTE(zaklaus): grab URI origin + if (*p != '?') { + while (*p && *p != '?' && !zpl_char_is_space(*p)) {++p;} + char c = *p; + *p = 0; + + zpl_adt_append_str(root, "__zpl_origin__", b); + + if (!c) { + // NOTE(zaklaus): URI has no query params, bail + return err_code; + } + } + + b = ++p; + + // NOTE(zaklaus): extract query params + while (*p && !zpl_char_is_space(*p)) { + // NOTE(zaklaus): get param name + b = p; + while (*p && (*p != '&' && *p != '?' && *p != '=' && !zpl_char_is_space(*p))) { ++p; } + char c = *p; + *p = 0; + + char *field_name = b; + char *field_value = ""; + zpl__uri_decode_str(field_name); + + if (c == '=') { + // NOTE(zaklaus): read param value + ++p; + b = p; + while (*p && (*p != '&' && *p != '?' && !zpl_char_is_space(*p))) { ++p; } + c = *p; + *p = 0; + + field_value = b; + zpl__uri_decode_str(field_value); + zpl_adt_node *obj = zpl_adt_append_str(root, field_name, field_value); + zpl_adt_str_to_number_strict(obj); + } else { + zpl_adt_node *obj = zpl_adt_append_flt(root, field_name, 1); + obj->props = ZPL_ADT_PROPS_TRUE; + } + + + if (!c) { + break; + } + + ++p; + } + + return err_code; + } + + zpl_adt_error zpl__uri_print_str(zpl_file *file, const char *text) { + ZPL_ASSERT_NOT_NULL(file); + + if (!text) { + return ZPL_ADT_ERROR_NONE; + } + + const char *p = text; + char buf[10] = {0}; + zpl_u8 ch; + + while (*p) { + ch = (zpl_u8) *p++; + if (ch == ' ') { + zpl_fprintf(file, "%s", "+"); + } else if (zpl_char_is_alphanumeric(ch) || zpl_strchr("-_.~", ch)) { + zpl_fprintf(file, "%c", ch); + } else { + zpl_snprintf(buf, zpl_size_of(buf), "%02X", ch); + zpl_fprintf(file, "%c%s", '%', buf); + } + } + + return ZPL_ADT_ERROR_NONE; + } + + void zpl_uri_write(zpl_file *f, zpl_adt_node *obj) { + ZPL_ASSERT_NOT_NULL(f); + ZPL_ASSERT_NOT_NULL(obj); + ZPL_ASSERT(obj->type == ZPL_ADT_TYPE_OBJECT); + + zpl_adt_node *origin = NULL; + + // NOTE(zaklaus): write URI origin if available + { + origin = zpl_adt_query(obj, "__zpl_origin__"); + if (origin) { + zpl_fprintf(f, origin->string); + } + } + + // NOTE(zaklaus): write params + if (zpl_array_count(obj->nodes) > 0) + zpl_fprintf(f, "%s", "?"); + + for (zpl_isize i = 0; i < zpl_array_count(obj->nodes); i++) { + zpl_adt_node *n = (obj->nodes+i); + if (origin == n) continue; + + zpl__uri_print_str(f, n->name); + + if (n->type == ZPL_ADT_TYPE_STRING) { + zpl_fprintf(f, "%c", '='); + zpl__uri_print_str(f, n->string); + } else if (n->type == ZPL_ADT_TYPE_INTEGER || n->type == ZPL_ADT_TYPE_REAL) { + if (n->props != ZPL_ADT_PROPS_TRUE) { + zpl_fprintf(f, "%c", '='); + // TODO: ensure the output is URI-encoded + zpl_adt_print_number(f, n); + } + } + + if (i+1 < zpl_array_count(obj->nodes)) { + zpl_fprintf(f, "%s", "&"); + } + } + } + + void zpl_uri_free(zpl_adt_node *obj) { + zpl_adt_destroy_branch(obj); + } + + zpl_string zpl_uri_write_string(zpl_allocator a, zpl_adt_node *obj) { + zpl_file tmp; + zpl_file_stream_new(&tmp, a); + zpl_uri_write(&tmp, obj); + zpl_isize fsize; + zpl_u8* buf = zpl_file_stream_buf(&tmp, &fsize); + zpl_string output = zpl_string_make_length(a, (char *)buf, fsize); + zpl_file_close(&tmp); + return output; + } + + ZPL_END_C_DECLS +#endif + +#if defined(ZPL_MODULE_SOCKET) + // file: source/socket.c + + ZPL_BEGIN_C_DECLS + + #if defined(ZPL_SYSTEM_WINDOWS) + # include + # pragma comment(lib, "ws2_32.lib") + typedef int socklen_t; + #else //unix + # include + # include + # include + # include + #ifndef TCP_NODELAY + # include + # include + # endif + #endif + + ZPL_DEF zpl_socket zpl_socket_init(void) { + # if defined(ZPL_SYSTEM_WINDOWS) + WSADATA winsock_data = {0}; + return WSAStartup(MAKEWORD(2, 2), &winsock_data) != NO_ERROR; + # endif + return 0; + } + + ZPL_DEF zpl_socket zpl_socket_create(zpl_i32 protocol, zpl_i32 mode, char flags, const char *host, const char *service) { + struct addrinfo *result, hints = { + (mode == ZPL_SOCKET_BIND) ? AI_PASSIVE : 0, + AF_UNSPEC, + (protocol == ZPL_SOCKET_UDP) ? SOCK_DGRAM : SOCK_STREAM, + 0, 0, 0, 0, 0 + }; + + if (getaddrinfo(host, service, &hints, &result) != 0) { + return -1; + } + # if defined(ZPL_SYSTEM_WINDOWS) + zpl_socket sock = (zpl_socket)socket(result->ai_family, result->ai_socktype, result->ai_protocol); + if (sock == INVALID_SOCKET) { + freeaddrinfo(result); + return -1; + } + if (sock > INT_MAX) { + closesocket(sock); + freeaddrinfo(result); + return -1; + } + # else + zpl_socket sock = socket(result->ai_family, result->ai_socktype, result->ai_protocol); + if (sock == -1) { + freeaddrinfo(result); + return -1; + } + # endif + + if (result->ai_family == AF_INET6) { + zpl_i32 no = 0; + setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&no, sizeof(no)); + } + + if (protocol == ZPL_SOCKET_TCP) { + int nodelay = (flags & ZPL_SOCKET_NO_DELAY); + setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (char *)&nodelay, sizeof(nodelay)); + } + + if (mode == ZPL_SOCKET_BIND) { + if (bind(sock, result->ai_addr, (int)result->ai_addrlen) != 0) { + freeaddrinfo(result); + return -1; + } + } + + if (flags & ZPL_SOCKET_NON_BLOCKING) { + # if defined(ZPL_SYSTEM_WINDOWS) + DWORD non_blocking = 1; + if (ioctlsocket(sock, FIONBIO, &non_blocking)) { + freeaddrinfo(result); + return -1; + } + # else + if (fcntl(sock, F_SETFL, O_NONBLOCK) == -1) { + freeaddrinfo(result); + return -1; + } + # endif + } + + if (mode == ZPL_SOCKET_CONNECT) { + if (connect(sock, result->ai_addr, (int)result->ai_addrlen) != 0 && !(flags & ZPL_SOCKET_NON_BLOCKING)) { + freeaddrinfo(result); + return -1; + } + } + + freeaddrinfo(result); + return sock; + } + + ZPL_DEF void zpl_socket_close(zpl_socket socket) { + # if defined(ZPL_SYSTEM_WINDOWS) + closesocket(socket); + # else + close(socket); + # endif + } + + ZPL_DEF void zpl_socket_terminate(void) { + # if defined(ZPL_SYSTEM_WINDOWS) + WSACleanup(); + # endif + } + + ZPL_DEF zpl_i32 zpl_socket_listen(zpl_socket socket, zpl_i32 backlog) { + return listen(socket, backlog); + } + + ZPL_DEF zpl_socket zpl_socket_accept(zpl_socket socket, zpl_socket_addr *addr) { + # if defined(ZPL_SYSTEM_WINDOWS) + int len = sizeof(*addr); + zpl_socket sock = (zpl_socket)accept(socket, (struct sockaddr *)addr, &len); + if (sock == INVALID_SOCKET) { + return -1; + } + if (sock > INT_MAX) { + closesocket(sock); + return -1; + } + # else + socklen_t len = sizeof(*addr); + zpl_socket sock = accept(socket, (struct sockaddr *)addr, &len); + if (sock == -1) { + return -1; + } + # endif + return sock; + } + + ZPL_DEF zpl_i32 zpl_socket_get_address(zpl_socket socket, zpl_socket_addr *addr) { + return getsockname(socket, (struct sockaddr *)addr, (socklen_t *)sizeof(*addr)); + } + + ZPL_DEF zpl_i32 zpl_socket_get_address_info(zpl_socket_addr *addr, char *host, zpl_i32 host_size, char *service, zpl_i32 service_size) { + return getnameinfo((struct sockaddr *)addr, (socklen_t)sizeof(*addr), host, host_size, service, service_size, 0); + } + + ZPL_DEF zpl_i32 zpl_socket_send(zpl_socket socket, const char *data, zpl_i32 size) { + return send(socket, data, size, 0); + } + + ZPL_DEF zpl_i32 zpl_socket_receive(zpl_socket socket, char *buffer, zpl_i32 size) { + return recv(socket, buffer, size, 0); + } + + ZPL_DEF zpl_i32 zpl_socket_send_to(zpl_socket socket, zpl_socket_addr *addr, const char *data, zpl_i32 size) { + return sendto(socket, data, size, 0, (struct sockaddr *)addr, (socklen_t)sizeof(*addr)); + } + + ZPL_DEF zpl_i32 zpl_socket_receive_from(zpl_socket socket, zpl_socket_addr *addr, char *buffer, zpl_i32 size) { + return recvfrom(socket, buffer, size, 0, (struct sockaddr *)addr, (socklen_t *)sizeof(*addr)); + } + + ZPL_DEF zpl_i32 zpl_socket_select(zpl_socket socket, zpl_f64 time) { + fd_set fds; + struct timeval tv; + + FD_ZERO(&fds); + if (socket > -1) FD_SET(socket, &fds); + + tv.tv_sec = (long)time; + tv.tv_usec = (long)((time - tv.tv_sec) * 1000000); + + return select(socket + 1, &fds, 0, 0, &tv); + } + + ZPL_DEF zpl_i32 zpl_socket_multi_select(zpl_socket *sockets, zpl_i32 count, zpl_f64 time) { + fd_set fds; + struct timeval tv; + zpl_i32 i, max = -1; + + FD_ZERO(&fds); + for (i = 0; i < count; ++i) { + if (sockets[i] > max) max = sockets[i]; + if (sockets[i] > -1) FD_SET(sockets[i], &fds); + } + + tv.tv_sec = (long)time; + tv.tv_usec = (long)((time - tv.tv_sec) * 1000000); + + return select(max + 1, &fds, 0, 0, &tv); + } + + ZPL_END_C_DECLS +#endif + +#if defined(ZPL_COMPILER_MSVC) +# pragma warning(pop) +#endif + +#if defined(__GCC__) || defined(__GNUC__) || defined(__clang__) +# pragma GCC diagnostic pop +#endif + +#endif // ZPL_IMPLEMENTATION + +#if !defined(ZPL_PICO_CUSTOM_ROUTINES) +# undef zpl__printf_err +# undef zpl__printf_err_va +# undef zpl__strlen +#endif + +#if defined(ZPL_EXPOSE_TYPES) + typedef zpl_u8 u8; + typedef zpl_i8 i8; + typedef zpl_u16 u16; + typedef zpl_i16 i16; + typedef zpl_u32 u32; + typedef zpl_i32 i32; + typedef zpl_u64 u64; + typedef zpl_i64 i64; + typedef zpl_b8 b8; + typedef zpl_b16 b16; + typedef zpl_b32 b32; + typedef zpl_f32 f32; + typedef zpl_f64 f64; + typedef zpl_rune rune; + typedef zpl_usize usize; + typedef zpl_isize isize; + typedef zpl_uintptr uintptr; + typedef zpl_intptr intptr; +#endif // ZPL_EXPOSE_TYPES + +#endif // ZPL_H + +// TOC: +// zpl.h +// zpl_hedley.h +// header/opts.h +// header/essentials/helpers.h +// header/essentials/memory.h +// header/essentials/memory_custom.h +// header/essentials/types.h +// header/essentials/collections/buffer.h +// header/essentials/collections/list.h +// header/essentials/collections/hashtable.h +// header/essentials/collections/ring.h +// header/essentials/collections/array.h +// header/essentials/debug.h +// header/process.h +// header/threading/fence.h +// header/threading/mutex.h +// header/threading/sync.h +// header/threading/affinity.h +// header/threading/atomic.h +// header/threading/thread.h +// header/threading/sem.h +// header/math.h +// header/jobs.h +// header/parsers/json.h +// header/parsers/csv.h +// header/parsers/uri.h +// header/dll.h +// header/adt.h +// header/core/file_tar.h +// header/core/memory_virtual.h +// header/core/random.h +// header/core/file_stream.h +// header/core/string.h +// header/core/misc.h +// header/core/file.h +// header/core/stringlib.h +// header/core/sort.h +// header/core/print.h +// header/core/system.h +// header/core/file_misc.h +// header/core/time.h +// header/hashing.h +// header/regex.h +// header/socket.h +// source/hashing.c +// source/adt.c +// source/process.c +// source/essentials/debug.c +// source/essentials/memory_custom.c +// source/essentials/memory.c +// source/dll.c +// source/regex.c +// source/threading/mutex.c +// source/threading/affinity.c +// source/threading/atomic.c +// source/threading/sync.c +// source/threading/thread.c +// source/threading/fence.c +// source/threading/sem.c +// source/parsers/csv.c +// source/parsers/uri.c +// source/parsers/json.c +// source/jobs.c +// source/core/file_stream.c +// source/core/stringlib.c +// source/core/misc.c +// source/core/file_misc.c +// source/core/file.c +// source/core/memory_virtual.c +// source/core/print.c +// source/core/time.c +// source/core/string.c +// source/core/random.c +// source/core/sort.c +// source/core/file_tar.c +// source/opts.c +// source/math.c +// source/socket.c diff --git a/thirdparty/stb/truetype/stb_truetype.odin b/thirdparty/stb/truetype/stb_truetype.odin index f128ad0..59bbcc4 100644 --- a/thirdparty/stb/truetype/stb_truetype.odin +++ b/thirdparty/stb/truetype/stb_truetype.odin @@ -40,27 +40,27 @@ when ODIN_ARCH == .wasm32 || ODIN_ARCH == .wasm64p32 { // CUSTOM: ODIN COMPATIBLE ALLOCATOR //----------------------------------------------------------------------------- -gbAllocationType :: enum(i32) { +zpl_allocator_type :: enum(i32) { Alloc, Free, FreeAll, Resize, } -gbAllocatorProc :: #type proc(allocator_data: rawptr, type: gbAllocationType, +zpl_allocator_proc :: #type proc(allocator_data: rawptr, type: zpl_allocator_type, size: c.ssize_t, alignment: c.ssize_t, old_memory: rawptr, old_size: c.ssize_t, flags : c.ulonglong ) -> rawptr -gbAllocator :: struct { - procedure: gbAllocatorProc, +zpl_allocator :: struct { + procedure: zpl_allocator_proc, data: rawptr, } @(default_calling_convention="c", link_prefix="stbtt_") foreign stbtt { - SetAllocator :: proc(allocator : gbAllocator) --- + SetAllocator :: proc(allocator : zpl_allocator) --- } //----------------------------------------------------------------------------- diff --git a/vefontcache/parser.odin b/vefontcache/parser.odin index 9815870..631f02a 100644 --- a/vefontcache/parser.odin +++ b/vefontcache/parser.odin @@ -61,7 +61,7 @@ Parser_Context :: struct { parser_stbtt_allocator_proc :: proc( allocator_data : rawptr, - type : stbtt.gbAllocationType, + type : stbtt.zpl_allocator_type, size : c.ssize_t, alignment : c.ssize_t, old_memory : rawptr, @@ -86,13 +86,13 @@ parser_init :: proc( ctx : ^Parser_Context, kind : Parser_Kind, allocator := con ctx.kind = kind ctx.lib_backing = allocator - stbtt_allocator := stbtt.gbAllocator { parser_stbtt_allocator_proc, & ctx.lib_backing } + stbtt_allocator := stbtt.zpl_allocator { parser_stbtt_allocator_proc, & ctx.lib_backing } stbtt.SetAllocator( stbtt_allocator ) } parser_reload :: proc( ctx : ^Parser_Context, allocator := context.allocator) { ctx.lib_backing = allocator - stbtt_allocator := stbtt.gbAllocator { parser_stbtt_allocator_proc, & ctx.lib_backing } + stbtt_allocator := stbtt.zpl_allocator { parser_stbtt_allocator_proc, & ctx.lib_backing } stbtt.SetAllocator( stbtt_allocator ) }