From a6a9f622f4d961aec830d180cb3a711584df4760 Mon Sep 17 00:00:00 2001 From: Ed_ Date: Mon, 13 Jan 2025 22:03:12 -0500 Subject: [PATCH] using zpl for stb_truetype allocation extension instead of gb.h (trying to gamble a build with mac) --- thirdparty/stb/lib/stb_truetype.lib | Bin 380552 -> 944930 bytes thirdparty/stb/src/gb/gb.h | 10827 ----------- thirdparty/stb/src/stb_truetype.h | 14 +- thirdparty/stb/src/zpl/zpl.h | 19055 ++++++++++++++++++++ thirdparty/stb/truetype/stb_truetype.odin | 10 +- vefontcache/parser.odin | 6 +- 6 files changed, 19070 insertions(+), 10842 deletions(-) delete mode 100644 thirdparty/stb/src/gb/gb.h create mode 100644 thirdparty/stb/src/zpl/zpl.h diff --git a/thirdparty/stb/lib/stb_truetype.lib b/thirdparty/stb/lib/stb_truetype.lib index 6d2c66604be0b605d0dd99b7643c83fe802dc463..fb5a44962aa8d0bf3cbaf55e9516acc852044e9d 100644 GIT binary patch literal 944930 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 z0!)AjFaajO1egF5U;<2l2`~XBzyz286JP>NfC(@GCcp%k025#WOn?b60Vco%m;e)C z0!)AjFaajO1egF5U;<2l2`~XBzyz286JP>NfC(@GCcp%k025#WOn?b60Vco%m;e)C z0!)AjFaajO1egF5U;<2l2`~XBzyz286JP>NfC(@GCcp%k025#WOn?b60Vco%m;e)C z0!)AjFaajO1egF5U;<2l2`~XBzyz286JP>NfC(@GCcp%k025#WOn?b60Vco%m;e)C z0!)AjFaajO1egF5U;<2l2`~XBzyz286JP>NfC(@GCcp%k025#WOn?b60Vco%m;e)C z0!)AjFaajO1egF5U;<2l2`~XBzyz286JP>NfC(@GCcp%k025#WOn?b60Vco%m;e)C z0!)AjFaajO1egF5U;<2l2`~XBzyz286JP>NfC(@GCcp%k025#WOn?b60Vco%m;e)C z0!)AjFaajO1egF5U;<2l2`~XBzyz286JP>NfC(@GCcp%k025#WOn?b60Vco%m;e)C z0!)AjFaajO1egF5U;<2l2`~XBzyz286JP>NfC(@GCcp%k025#WOn?b60Vco%m;e)C z0!)AjFaajO1egF5U;<2l2`~XBzyz286JP>NfC(@GCcp%k025#WOn?b60Vco%m;e)C z0!)AjFaajO1egF5U;<2l2`~XBzyz286JP>NfC(@GCcp%k025#WOn?b60Vco%m;e)C z0!)AjFaajO1egF5U;<2l2`~XBzyz286JP>NfC(@GCcp%k025#WOn?b60Vco%m;e)C z0!)AjFaajO1egF5U;<2l2`~XBzyz286JP>NfC(@GCcp%k025#WOn?b60Vco%m;e)C z0!)AjFaajO1egF5U;<2l2`~XBzyz286JP>NfC(@GCcp%k025#WOn?b60Vco%m;e)C z0!)AjFaajO1egF5U;<2l2`~XBzyz286JP>NfC(@GCcp%k025#WOn?b60Vco%m;e)C z0!)AjFaajO1egF5U;<2l2`~XBzyz286JP>NfC(@GCcp%k025#WOn?b60Vco%m;e)C z0!)AjFaajO1egF5U;<2l2`~XBzyz286JP>NfC(@GCcp%k025#WOn?b60Vco%m;e)C z0!)AjFaajO1egF5U;<2l2`~XBzyz286JP>NfC(@GCcp%k025#WOn?b60Vco%m;e)C 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�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%0AS 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¬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

gs$G@q3uHwXnZFviBPh4~BhTqL4jUONMV4e6` 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 *b@IgCVq~F5^pNY?3>ca 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 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 ) }