]> www.git.momoyon.org Git - commonlib.git/commitdiff
[log_file] Handle %.*s
authorahmedsamyh <ahmedsamyh10@gmail.com>
Wed, 11 Sep 2024 18:19:38 +0000 (23:19 +0500)
committerahmedsamyh <ahmedsamyh10@gmail.com>
Wed, 11 Sep 2024 18:19:38 +0000 (23:19 +0500)
commonlib.h

index cab9bc33175104af9e9e40551913dc91098bcf97..5b34ac3f287a089abf2a90bcb364e5e5a6608cda 100644 (file)
-#ifndef _STDLIB_H_
-#define _STDLIB_H_
-#include <stdio.h>
-#include <stdbool.h>
-#include <stdarg.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include <ctype.h>
-#include <windows.h>
-#include <locale.h>
-
-//
-// External libs --------------------------------------------------
-//
-
-// stb_ds.h:
-
-/* stb_ds.h - v0.67 - public domain data structures - Sean Barrett 2019
-
-   This is a single-header-file library that provides easy-to-use
-   dynamic arrays and hash tables for C (also works in C++).
-
-   For a gentle introduction:
-      http://nothings.org/stb_ds
-
-   To use this library, do this in *one* C or C++ file:
-      #define STB_DS_IMPLEMENTATION
-      #include "stb_ds.h"
-
-TABLE OF CONTENTS
-
-  Table of Contents
-  Compile-time options
-  License
-  Documentation
-  Notes
-  Notes - Dynamic arrays
-  Notes - Hash maps
-  Credits
-
-COMPILE-TIME OPTIONS
-
-  #define STBDS_NO_SHORT_NAMES
-
-     This flag needs to be set globally.
-
-     By default stb_ds exposes shorter function names that are not qualified
-     with the "stbds_" prefix. If these names conflict with the names in your
-     code, define this flag.
-
-  #define STBDS_SIPHASH_2_4
-
-     This flag only needs to be set in the file containing #define STB_DS_IMPLEMENTATION.
-
-     By default stb_ds.h hashes using a weaker variant of SipHash and a custom hash for
-     4- and 8-byte keys. On 64-bit platforms, you can define the above flag to force
-     stb_ds.h to use specification-compliant SipHash-2-4 for all keys. Doing so makes
-     hash table insertion about 20% slower on 4- and 8-byte keys, 5% slower on
-     64-byte keys, and 10% slower on 256-byte keys on my test computer.
-
-  #define STBDS_REALLOC(context,ptr,size) better_realloc
-  #define STBDS_FREE(context,ptr)         better_free
-
-     These defines only need to be set in the file containing #define STB_DS_IMPLEMENTATION.
-
-     By default stb_ds uses stdlib realloc() and free() for memory management. You can
-     substitute your own functions instead by defining these symbols. You must either
-     define both, or neither. Note that at the moment, 'context' will always be NULL.
-     @TODO add an array/hash initialization function that takes a memory context pointer.
-
-  #define STBDS_UNIT_TESTS
-
-     Defines a function stbds_unit_tests() that checks the functioning of the data structures.
-
-  Note that on older versions of gcc (e.g. 5.x.x) you may need to build with '-std=c++0x'
-     (or equivalentally '-std=c++11') when using anonymous structures as seen on the web
-     page or in STBDS_UNIT_TESTS.
-
-LICENSE
-
-  Placed in the public domain and also MIT licensed.
-  See end of file for detailed license information.
-
-DOCUMENTATION
-
-  Dynamic Arrays
-
-    Non-function interface:
-
-      Declare an empty dynamic array of type T
-        T* foo = NULL;
-
-      Access the i'th item of a dynamic array 'foo' of type T, T* foo:
-        foo[i]
-
-    Functions (actually macros)
-
-      arrfree:
-        void arrfree(T*);
-          Frees the array.
-
-      arrlen:
-        ptrdiff_t arrlen(T*);
-          Returns the number of elements in the array.
-
-      arrlenu:
-        size_t arrlenu(T*);
-          Returns the number of elements in the array as an unsigned type.
-
-      arrpop:
-        T arrpop(T* a)
-          Removes the final element of the array and returns it.
-
-      arrput:
-        T arrput(T* a, T b);
-          Appends the item b to the end of array a. Returns b.
-
-      arrins:
-        T arrins(T* a, int p, T b);
-          Inserts the item b into the middle of array a, into a[p],
-          moving the rest of the array over. Returns b.
-
-      arrinsn:
-        void arrinsn(T* a, int p, int n);
-          Inserts n uninitialized items into array a starting at a[p],
-          moving the rest of the array over.
-
-      arraddnptr:
-        T* arraddnptr(T* a, int n)
-          Appends n uninitialized items onto array at the end.
-          Returns a pointer to the first uninitialized item added.
-
-      arraddnindex:
-        size_t arraddnindex(T* a, int n)
-          Appends n uninitialized items onto array at the end.
-          Returns the index of the first uninitialized item added.
-
-      arrdel:
-        void arrdel(T* a, int p);
-          Deletes the element at a[p], moving the rest of the array over.
-
-      arrdeln:
-        void arrdeln(T* a, int p, int n);
-          Deletes n elements starting at a[p], moving the rest of the array over.
-
-      arrdelswap:
-        void arrdelswap(T* a, int p);
-          Deletes the element at a[p], replacing it with the element from
-          the end of the array. O(1) performance.
-
-      arrsetlen:
-        void arrsetlen(T* a, int n);
-          Changes the length of the array to n. Allocates uninitialized
-          slots at the end if necessary.
-
-      arrsetcap:
-        size_t arrsetcap(T* a, int n);
-          Sets the length of allocated storage to at least n. It will not
-          change the length of the array.
-
-      arrcap:
-        size_t arrcap(T* a);
-          Returns the number of total elements the array can contain without
-          needing to be reallocated.
-
-  Hash maps & String hash maps
-
-    Given T is a structure type: struct { TK key; TV value; }. Note that some
-    functions do not require TV value and can have other fields. For string
-    hash maps, TK must be 'char *'.
-
-    Special interface:
-
-      stbds_rand_seed:
-        void stbds_rand_seed(size_t seed);
-          For security against adversarially chosen data, you should seed the
-          library with a strong random number. Or at least seed it with time().
-
-      stbds_hash_string:
-        size_t stbds_hash_string(char *str, size_t seed);
-          Returns a hash value for a string.
-
-      stbds_hash_bytes:
-        size_t stbds_hash_bytes(void *p, size_t len, size_t seed);
-          These functions hash an arbitrary number of bytes. The function
-          uses a custom hash for 4- and 8-byte data, and a weakened version
-          of SipHash for everything else. On 64-bit platforms you can get
-          specification-compliant SipHash-2-4 on all data by defining
-          STBDS_SIPHASH_2_4, at a significant cost in speed.
-
-    Non-function interface:
-
-      Declare an empty hash map of type T
-        T* foo = NULL;
-
-      Access the i'th entry in a hash table T* foo:
-        foo[i]
-
-    Function interface (actually macros):
-
-      hmfree
-      shfree
-        void hmfree(T*);
-        void shfree(T*);
-          Frees the hashmap and sets the pointer to NULL.
-
-      hmlen
-      shlen
-        ptrdiff_t hmlen(T*)
-        ptrdiff_t shlen(T*)
-          Returns the number of elements in the hashmap.
-
-      hmlenu
-      shlenu
-        size_t hmlenu(T*)
-        size_t shlenu(T*)
-          Returns the number of elements in the hashmap.
-
-      hmgeti
-      shgeti
-      hmgeti_ts
-        ptrdiff_t hmgeti(T*, TK key)
-        ptrdiff_t shgeti(T*, char* key)
-        ptrdiff_t hmgeti_ts(T*, TK key, ptrdiff_t tempvar)
-          Returns the index in the hashmap which has the key 'key', or -1
-          if the key is not present.
-
-      hmget
-      hmget_ts
-      shget
-        TV hmget(T*, TK key)
-        TV shget(T*, char* key)
-        TV hmget_ts(T*, TK key, ptrdiff_t tempvar)
-          Returns the value corresponding to 'key' in the hashmap.
-          The structure must have a 'value' field
-
-      hmgets
-      shgets
-        T hmgets(T*, TK key)
-        T shgets(T*, char* key)
-          Returns the structure corresponding to 'key' in the hashmap.
-
-      hmgetp
-      shgetp
-      hmgetp_ts
-      hmgetp_null
-      shgetp_null
-        T* hmgetp(T*, TK key)
-        T* shgetp(T*, char* key)
-        T* hmgetp_ts(T*, TK key, ptrdiff_t tempvar)
-        T* hmgetp_null(T*, TK key)
-        T* shgetp_null(T*, char *key)
-          Returns a pointer to the structure corresponding to 'key' in
-          the hashmap. Functions ending in "_null" return NULL if the key
-          is not present in the hashmap; the others return a pointer to a
-          structure holding the default value (but not the searched-for key).
-
-      hmdefault
-      shdefault
-        TV hmdefault(T*, TV value)
-        TV shdefault(T*, TV value)
-          Sets the default value for the hashmap, the value which will be
-          returned by hmget/shget if the key is not present.
-
-      hmdefaults
-      shdefaults
-        TV hmdefaults(T*, T item)
-        TV shdefaults(T*, T item)
-          Sets the default struct for the hashmap, the contents which will be
-          returned by hmgets/shgets if the key is not present.
-
-      hmput
-      shput
-        TV hmput(T*, TK key, TV value)
-        TV shput(T*, char* key, TV value)
-          Inserts a <key,value> pair into the hashmap. If the key is already
-          present in the hashmap, updates its value.
-
-      hmputs
-      shputs
-        T hmputs(T*, T item)
-        T shputs(T*, T item)
-          Inserts a struct with T.key into the hashmap. If the struct is already
-          present in the hashmap, updates it.
-
-      hmdel
-      shdel
-        int hmdel(T*, TK key)
-        int shdel(T*, char* key)
-          If 'key' is in the hashmap, deletes its entry and returns 1.
-          Otherwise returns 0.
-
-    Function interface (actually macros) for strings only:
-
-      sh_new_strdup
-        void sh_new_strdup(T*);
-          Overwrites the existing pointer with a newly allocated
-          string hashmap which will automatically allocate and free
-          each string key using realloc/free
-
-      sh_new_arena
-        void sh_new_arena(T*);
-          Overwrites the existing pointer with a newly allocated
-          string hashmap which will automatically allocate each string
-          key to a string arena. Every string key ever used by this
-          hash table remains in the arena until the arena is freed.
-          Additionally, any key which is deleted and reinserted will
-          be allocated multiple times in the string arena.
-
-NOTES
-
-  * These data structures are realloc'd when they grow, and the macro
-    "functions" write to the provided pointer. This means: (a) the pointer
-    must be an lvalue, and (b) the pointer to the data structure is not
-    stable, and you must maintain it the same as you would a realloc'd
-    pointer. For example, if you pass a pointer to a dynamic array to a
-    function which updates it, the function must return back the new
-    pointer to the caller. This is the price of trying to do this in C.
-
-  * The following are the only functions that are thread-safe on a single data
-    structure, i.e. can be run in multiple threads simultaneously on the same
-    data structure
-        hmlen        shlen
-        hmlenu       shlenu
-        hmget_ts     shget_ts
-        hmgeti_ts    shgeti_ts
-        hmgets_ts    shgets_ts
-
-  * You iterate over the contents of a dynamic array and a hashmap in exactly
-    the same way, using arrlen/hmlen/shlen:
-
-      for (i=0; i < arrlen(foo); ++i)
-         ... foo[i] ...
-
-  * All operations except arrins/arrdel are O(1) amortized, but individual
-    operations can be slow, so these data structures may not be suitable
-    for real time use. Dynamic arrays double in capacity as needed, so
-    elements are copied an average of once. Hash tables double/halve
-    their size as needed, with appropriate hysteresis to maintain O(1)
-    performance.
-
-NOTES - DYNAMIC ARRAY
-
-  * If you know how long a dynamic array is going to be in advance, you can avoid
-    extra memory allocations by using arrsetlen to allocate it to that length in
-    advance and use foo[n] while filling it out, or arrsetcap to allocate the memory
-    for that length and use arrput/arrpush as normal.
-
-  * Unlike some other versions of the dynamic array, this version should
-    be safe to use with strict-aliasing optimizations.
-
-NOTES - HASH MAP
-
-  * For compilers other than GCC and clang (e.g. Visual Studio), for hmput/hmget/hmdel
-    and variants, the key must be an lvalue (so the macro can take the address of it).
-    Extensions are used that eliminate this requirement if you're using C99 and later
-    in GCC or clang, or if you're using C++ in GCC. But note that this can make your
-    code less portable.
-
-  * To test for presence of a key in a hashmap, just do 'hmgeti(foo,key) >= 0'.
-
-  * The iteration order of your data in the hashmap is determined solely by the
-    order of insertions and deletions. In particular, if you never delete, new
-    keys are always added at the end of the array. This will be consistent
-    across all platforms and versions of the library. However, you should not
-    attempt to serialize the internal hash table, as the hash is not consistent
-    between different platforms, and may change with future versions of the library.
-
-  * Use sh_new_arena() for string hashmaps that you never delete from. Initialize
-    with NULL if you're managing the memory for your strings, or your strings are
-    never freed (at least until the hashmap is freed). Otherwise, use sh_new_strdup().
-    @TODO: make an arena variant that garbage collects the strings with a trivial
-    copy collector into a new arena whenever the table shrinks / rebuilds. Since
-    current arena recommendation is to only use arena if it never deletes, then
-    this can just replace current arena implementation.
-
-  * If adversarial input is a serious concern and you're on a 64-bit platform,
-    enable STBDS_SIPHASH_2_4 (see the 'Compile-time options' section), and pass
-    a strong random number to stbds_rand_seed.
-
-  * The default value for the hash table is stored in foo[-1], so if you
-    use code like 'hmget(T,k)->value = 5' you can accidentally overwrite
-    the value stored by hmdefault if 'k' is not present.
-
-CREDITS
-
-  Sean Barrett -- library, idea for dynamic array API/implementation
-  Per Vognsen  -- idea for hash table API/implementation
-  Rafael Sachetto -- arrpop()
-  github:HeroicKatora -- arraddn() reworking
-
-  Bugfixes:
-    Andy Durdin
-    Shane Liesegang
-    Vinh Truong
-    Andreas Molzer
-    github:hashitaku
-    github:srdjanstipic
-    Macoy Madson
-    Andreas Vennstrom
-    Tobias Mansfield-Williams
-*/
-
-#ifdef STBDS_UNIT_TESTS
-#define _CRT_SECURE_NO_WARNINGS
-#endif
-
-#ifndef INCLUDE_STB_DS_H
-#define INCLUDE_STB_DS_H
-
-#include <stddef.h>
-#include <string.h>
-
-#ifndef STBDS_NO_SHORT_NAMES
-#define arrlen      stbds_arrlen
-#define arrlenu     stbds_arrlenu
-#define arrput      stbds_arrput
-#define arrpush     stbds_arrput
-#define arrpop      stbds_arrpop
-#define arrfree     stbds_arrfree
-#define arraddn     stbds_arraddn // deprecated, use one of the following instead:
-#define arraddnptr  stbds_arraddnptr
-#define arraddnindex stbds_arraddnindex
-#define arrsetlen   stbds_arrsetlen
-#define arrlast     stbds_arrlast
-#define arrins      stbds_arrins
-#define arrinsn     stbds_arrinsn
-#define arrdel      stbds_arrdel
-#define arrdeln     stbds_arrdeln
-#define arrdelswap  stbds_arrdelswap
-#define arrcap      stbds_arrcap
-#define arrsetcap   stbds_arrsetcap
-
-#define hmput       stbds_hmput
-#define hmputs      stbds_hmputs
-#define hmget       stbds_hmget
-#define hmget_ts    stbds_hmget_ts
-#define hmgets      stbds_hmgets
-#define hmgetp      stbds_hmgetp
-#define hmgetp_ts   stbds_hmgetp_ts
-#define hmgetp_null stbds_hmgetp_null
-#define hmgeti      stbds_hmgeti
-#define hmgeti_ts   stbds_hmgeti_ts
-#define hmdel       stbds_hmdel
-#define hmlen       stbds_hmlen
-#define hmlenu      stbds_hmlenu
-#define hmfree      stbds_hmfree
-#define hmdefault   stbds_hmdefault
-#define hmdefaults  stbds_hmdefaults
-
-#define shput       stbds_shput
-#define shputi      stbds_shputi
-#define shputs      stbds_shputs
-#define shget       stbds_shget
-#define shgeti      stbds_shgeti
-#define shgets      stbds_shgets
-#define shgetp      stbds_shgetp
-#define shgetp_null stbds_shgetp_null
-#define shdel       stbds_shdel
-#define shlen       stbds_shlen
-#define shlenu      stbds_shlenu
-#define shfree      stbds_shfree
-#define shdefault   stbds_shdefault
-#define shdefaults  stbds_shdefaults
-#define sh_new_arena  stbds_sh_new_arena
-#define sh_new_strdup stbds_sh_new_strdup
-
-#define stralloc    stbds_stralloc
-#define strreset    stbds_strreset
-#endif
-
-#if defined(STBDS_REALLOC) && !defined(STBDS_FREE) || !defined(STBDS_REALLOC) && defined(STBDS_FREE)
-#error "You must define both STBDS_REALLOC and STBDS_FREE, or neither."
-#endif
-#if !defined(STBDS_REALLOC) && !defined(STBDS_FREE)
-#include <stdlib.h>
-#define STBDS_REALLOC(c,p,s) realloc(p,s)
-#define STBDS_FREE(c,p)      free(p)
-#endif
-
-#ifdef _MSC_VER
-#define STBDS_NOTUSED(v)  (void)(v)
-#else
-#define STBDS_NOTUSED(v)  (void)sizeof(v)
-#endif
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-// for security against attackers, seed the library with a random number, at least time() but stronger is better
-extern void stbds_rand_seed(size_t seed);
-
-// these are the hash functions used internally if you want to test them or use them for other purposes
-extern size_t stbds_hash_bytes(void *p, size_t len, size_t seed);
-extern size_t stbds_hash_string(char *str, size_t seed);
-
-// this is a simple string arena allocator, initialize with e.g. 'stbds_string_arena my_arena={0}'.
-typedef struct stbds_string_arena stbds_string_arena;
-extern char * stbds_stralloc(stbds_string_arena *a, char *str);
-extern void   stbds_strreset(stbds_string_arena *a);
-
-// have to #define STBDS_UNIT_TESTS to call this
-extern void stbds_unit_tests(void);
-
-///////////////
-//
-// Everything below here is implementation details
-//
-
-extern void * stbds_arrgrowf(void *a, size_t elemsize, size_t addlen, size_t min_cap);
-extern void   stbds_arrfreef(void *a);
-extern void   stbds_hmfree_func(void *p, size_t elemsize);
-extern void * stbds_hmget_key(void *a, size_t elemsize, void *key, size_t keysize, int mode);
-extern void * stbds_hmget_key_ts(void *a, size_t elemsize, void *key, size_t keysize, ptrdiff_t *temp, int mode);
-extern void * stbds_hmput_default(void *a, size_t elemsize);
-extern void * stbds_hmput_key(void *a, size_t elemsize, void *key, size_t keysize, int mode);
-extern void * stbds_hmdel_key(void *a, size_t elemsize, void *key, size_t keysize, size_t keyoffset, int mode);
-extern void * stbds_shmode_func(size_t elemsize, int mode);
-
-#ifdef __cplusplus
-}
-#endif
-
-#if defined(__GNUC__) || defined(__clang__)
-#define STBDS_HAS_TYPEOF
-#ifdef __cplusplus
-//#define STBDS_HAS_LITERAL_ARRAY  // this is currently broken for clang
-#endif
-#endif
-
-#if !defined(__cplusplus)
-#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
-#define STBDS_HAS_LITERAL_ARRAY
-#endif
-#endif
-
-// this macro takes the address of the argument, but on gcc/clang can accept rvalues
-#if defined(STBDS_HAS_LITERAL_ARRAY) && defined(STBDS_HAS_TYPEOF)
-  #if __clang__
-  #define STBDS_ADDRESSOF(typevar, value)     ((__typeof__(typevar)[1]){value}) // literal array decays to pointer to value
-  #else
-  #define STBDS_ADDRESSOF(typevar, value)     ((typeof(typevar)[1]){value}) // literal array decays to pointer to value
-  #endif
-#else
-#define STBDS_ADDRESSOF(typevar, value)     &(value)
-#endif
-
-#define STBDS_OFFSETOF(var,field)           ((char *) &(var)->field - (char *) (var))
-
-#define stbds_header(t)  ((stbds_array_header *) (t) - 1)
-#define stbds_temp(t)    stbds_header(t)->temp
-#define stbds_temp_key(t) (*(char **) stbds_header(t)->hash_table)
-
-#define stbds_arrsetcap(a,n)   (stbds_arrgrow(a,0,n))
-#define stbds_arrsetlen(a,n)   ((stbds_arrcap(a) < (size_t) (n) ? stbds_arrsetcap((a),(size_t)(n)),0 : 0), (a) ? stbds_header(a)->length = (size_t) (n) : 0)
-#define stbds_arrcap(a)        ((a) ? stbds_header(a)->capacity : 0)
-#define stbds_arrlen(a)        ((a) ? (ptrdiff_t) stbds_header(a)->length : 0)
-#define stbds_arrlenu(a)       ((a) ?             stbds_header(a)->length : 0)
-#define stbds_arrput(a,v)      (stbds_arrmaybegrow(a,1), (a)[stbds_header(a)->length++] = (v))
-#define stbds_arrpush          stbds_arrput  // synonym
-#define stbds_arrpop(a)        (stbds_header(a)->length--, (a)[stbds_header(a)->length])
-#define stbds_arraddn(a,n)     ((void)(stbds_arraddnindex(a, n)))    // deprecated, use one of the following instead:
-#define stbds_arraddnptr(a,n)  (stbds_arrmaybegrow(a,n), (n) ? (stbds_header(a)->length += (n), &(a)[stbds_header(a)->length-(n)]) : (a))
-#define stbds_arraddnindex(a,n)(stbds_arrmaybegrow(a,n), (n) ? (stbds_header(a)->length += (n), stbds_header(a)->length-(n)) : stbds_arrlen(a))
-#define stbds_arraddnoff       stbds_arraddnindex
-#define stbds_arrlast(a)       ((a)[stbds_header(a)->length-1])
-#define stbds_arrfree(a)       ((void) ((a) ? STBDS_FREE(NULL,stbds_header(a)) : (void)0), (a)=NULL)
-#define stbds_arrdel(a,i)      stbds_arrdeln(a,i,1)
-#define stbds_arrdeln(a,i,n)   (memmove(&(a)[i], &(a)[(i)+(n)], sizeof *(a) * (stbds_header(a)->length-(n)-(i))), stbds_header(a)->length -= (n))
-#define stbds_arrdelswap(a,i)  ((a)[i] = stbds_arrlast(a), stbds_header(a)->length -= 1)
-#define stbds_arrinsn(a,i,n)   (stbds_arraddn((a),(n)), memmove(&(a)[(i)+(n)], &(a)[i], sizeof *(a) * (stbds_header(a)->length-(n)-(i))))
-#define stbds_arrins(a,i,v)    (stbds_arrinsn((a),(i),1), (a)[i]=(v))
-
-#define stbds_arrmaybegrow(a,n)  ((!(a) || stbds_header(a)->length + (n) > stbds_header(a)->capacity) \
-                                  ? (stbds_arrgrow(a,n,0),0) : 0)
-
-#define stbds_arrgrow(a,b,c)   ((a) = stbds_arrgrowf_wrapper((a), sizeof *(a), (b), (c)))
-
-#define stbds_hmput(t, k, v) \
-    ((t) = stbds_hmput_key_wrapper((t), sizeof *(t), (void*) STBDS_ADDRESSOF((t)->key, (k)), sizeof (t)->key, 0),   \
-     (t)[stbds_temp((t)-1)].key = (k),    \
-     (t)[stbds_temp((t)-1)].value = (v))
-
-#define stbds_hmputs(t, s) \
-    ((t) = stbds_hmput_key_wrapper((t), sizeof *(t), &(s).key, sizeof (s).key, STBDS_HM_BINARY), \
-     (t)[stbds_temp((t)-1)] = (s))
-
-#define stbds_hmgeti(t,k) \
-    ((t) = stbds_hmget_key_wrapper((t), sizeof *(t), (void*) STBDS_ADDRESSOF((t)->key, (k)), sizeof (t)->key, STBDS_HM_BINARY), \
-      stbds_temp((t)-1))
-
-#define stbds_hmgeti_ts(t,k,temp) \
-    ((t) = stbds_hmget_key_ts_wrapper((t), sizeof *(t), (void*) STBDS_ADDRESSOF((t)->key, (k)), sizeof (t)->key, &(temp), STBDS_HM_BINARY), \
-      (temp))
-
-#define stbds_hmgetp(t, k) \
-    ((void) stbds_hmgeti(t,k), &(t)[stbds_temp((t)-1)])
-
-#define stbds_hmgetp_ts(t, k, temp) \
-    ((void) stbds_hmgeti_ts(t,k,temp), &(t)[temp])
-
-#define stbds_hmdel(t,k) \
-    (((t) = stbds_hmdel_key_wrapper((t),sizeof *(t), (void*) STBDS_ADDRESSOF((t)->key, (k)), sizeof (t)->key, STBDS_OFFSETOF((t),key), STBDS_HM_BINARY)),(t)?stbds_temp((t)-1):0)
-
-#define stbds_hmdefault(t, v) \
-    ((t) = stbds_hmput_default_wrapper((t), sizeof *(t)), (t)[-1].value = (v))
-
-#define stbds_hmdefaults(t, s) \
-    ((t) = stbds_hmput_default_wrapper((t), sizeof *(t)), (t)[-1] = (s))
-
-#define stbds_hmfree(p)        \
-    ((void) ((p) != NULL ? stbds_hmfree_func((p)-1,sizeof*(p)),0 : 0),(p)=NULL)
-
-#define stbds_hmgets(t, k)    (*stbds_hmgetp(t,k))
-#define stbds_hmget(t, k)     (stbds_hmgetp(t,k)->value)
-#define stbds_hmget_ts(t, k, temp)  (stbds_hmgetp_ts(t,k,temp)->value)
-#define stbds_hmlen(t)        ((t) ? (ptrdiff_t) stbds_header((t)-1)->length-1 : 0)
-#define stbds_hmlenu(t)       ((t) ?             stbds_header((t)-1)->length-1 : 0)
-#define stbds_hmgetp_null(t,k)  (stbds_hmgeti(t,k) == -1 ? NULL : &(t)[stbds_temp((t)-1)])
-
-#define stbds_shput(t, k, v) \
-    ((t) = stbds_hmput_key_wrapper((t), sizeof *(t), (void*) (k), sizeof (t)->key, STBDS_HM_STRING),   \
-     (t)[stbds_temp((t)-1)].value = (v))
-
-#define stbds_shputi(t, k, v) \
-    ((t) = stbds_hmput_key_wrapper((t), sizeof *(t), (void*) (k), sizeof (t)->key, STBDS_HM_STRING),   \
-     (t)[stbds_temp((t)-1)].value = (v), stbds_temp((t)-1))
-
-#define stbds_shputs(t, s) \
-    ((t) = stbds_hmput_key_wrapper((t), sizeof *(t), (void*) (s).key, sizeof (s).key, STBDS_HM_STRING), \
-     (t)[stbds_temp((t)-1)] = (s), \
-     (t)[stbds_temp((t)-1)].key = stbds_temp_key((t)-1)) // above line overwrites whole structure, so must rewrite key here if it was allocated internally
-
-#define stbds_pshput(t, p) \
-    ((t) = stbds_hmput_key_wrapper((t), sizeof *(t), (void*) (p)->key, sizeof (p)->key, STBDS_HM_PTR_TO_STRING), \
-     (t)[stbds_temp((t)-1)] = (p))
-
-#define stbds_shgeti(t,k) \
-     ((t) = stbds_hmget_key_wrapper((t), sizeof *(t), (void*) (k), sizeof (t)->key, STBDS_HM_STRING), \
-      stbds_temp((t)-1))
-
-#define stbds_pshgeti(t,k) \
-     ((t) = stbds_hmget_key_wrapper((t), sizeof *(t), (void*) (k), sizeof (*(t))->key, STBDS_HM_PTR_TO_STRING), \
-      stbds_temp((t)-1))
-
-#define stbds_shgetp(t, k) \
-    ((void) stbds_shgeti(t,k), &(t)[stbds_temp((t)-1)])
-
-#define stbds_pshget(t, k) \
-    ((void) stbds_pshgeti(t,k), (t)[stbds_temp((t)-1)])
-
-#define stbds_shdel(t,k) \
-    (((t) = stbds_hmdel_key_wrapper((t),sizeof *(t), (void*) (k), sizeof (t)->key, STBDS_OFFSETOF((t),key), STBDS_HM_STRING)),(t)?stbds_temp((t)-1):0)
-#define stbds_pshdel(t,k) \
-    (((t) = stbds_hmdel_key_wrapper((t),sizeof *(t), (void*) (k), sizeof (*(t))->key, STBDS_OFFSETOF(*(t),key), STBDS_HM_PTR_TO_STRING)),(t)?stbds_temp((t)-1):0)
-
-#define stbds_sh_new_arena(t)  \
-    ((t) = stbds_shmode_func_wrapper(t, sizeof *(t), STBDS_SH_ARENA))
-#define stbds_sh_new_strdup(t) \
-    ((t) = stbds_shmode_func_wrapper(t, sizeof *(t), STBDS_SH_STRDUP))
-
-#define stbds_shdefault(t, v)  stbds_hmdefault(t,v)
-#define stbds_shdefaults(t, s) stbds_hmdefaults(t,s)
-
-#define stbds_shfree       stbds_hmfree
-#define stbds_shlenu       stbds_hmlenu
-
-#define stbds_shgets(t, k) (*stbds_shgetp(t,k))
-#define stbds_shget(t, k)  (stbds_shgetp(t,k)->value)
-#define stbds_shgetp_null(t,k)  (stbds_shgeti(t,k) == -1 ? NULL : &(t)[stbds_temp((t)-1)])
-#define stbds_shlen        stbds_hmlen
-
-typedef struct
-{
-  size_t      length;
-  size_t      capacity;
-  void      * hash_table;
-  ptrdiff_t   temp;
-} stbds_array_header;
-
-typedef struct stbds_string_block
-{
-  struct stbds_string_block *next;
-  char storage[8];
-} stbds_string_block;
-
-struct stbds_string_arena
-{
-  stbds_string_block *storage;
-  size_t remaining;
-  unsigned char block;
-  unsigned char mode;  // this isn't used by the string arena itself
-};
-
-#define STBDS_HM_BINARY         0
-#define STBDS_HM_STRING         1
-
-enum
-{
-   STBDS_SH_NONE,
-   STBDS_SH_DEFAULT,
-   STBDS_SH_STRDUP,
-   STBDS_SH_ARENA
-};
-
-#ifdef __cplusplus
-// in C we use implicit assignment from these void*-returning functions to T*.
-// in C++ these templates make the same code work
-template<class T> static T * stbds_arrgrowf_wrapper(T *a, size_t elemsize, size_t addlen, size_t min_cap) {
-  return (T*)stbds_arrgrowf((void *)a, elemsize, addlen, min_cap);
-}
-template<class T> static T * stbds_hmget_key_wrapper(T *a, size_t elemsize, void *key, size_t keysize, int mode) {
-  return (T*)stbds_hmget_key((void*)a, elemsize, key, keysize, mode);
-}
-template<class T> static T * stbds_hmget_key_ts_wrapper(T *a, size_t elemsize, void *key, size_t keysize, ptrdiff_t *temp, int mode) {
-  return (T*)stbds_hmget_key_ts((void*)a, elemsize, key, keysize, temp, mode);
-}
-template<class T> static T * stbds_hmput_default_wrapper(T *a, size_t elemsize) {
-  return (T*)stbds_hmput_default((void *)a, elemsize);
-}
-template<class T> static T * stbds_hmput_key_wrapper(T *a, size_t elemsize, void *key, size_t keysize, int mode) {
-  return (T*)stbds_hmput_key((void*)a, elemsize, key, keysize, mode);
-}
-template<class T> static T * stbds_hmdel_key_wrapper(T *a, size_t elemsize, void *key, size_t keysize, size_t keyoffset, int mode){
-  return (T*)stbds_hmdel_key((void*)a, elemsize, key, keysize, keyoffset, mode);
-}
-template<class T> static T * stbds_shmode_func_wrapper(T *, size_t elemsize, int mode) {
-  return (T*)stbds_shmode_func(elemsize, mode);
-}
-#else
-#define stbds_arrgrowf_wrapper            stbds_arrgrowf
-#define stbds_hmget_key_wrapper           stbds_hmget_key
-#define stbds_hmget_key_ts_wrapper        stbds_hmget_key_ts
-#define stbds_hmput_default_wrapper       stbds_hmput_default
-#define stbds_hmput_key_wrapper           stbds_hmput_key
-#define stbds_hmdel_key_wrapper           stbds_hmdel_key
-#define stbds_shmode_func_wrapper(t,e,m)  stbds_shmode_func(e,m)
-#endif
-
-#endif // INCLUDE_STB_DS_H
-
-// stb_sprintf.h:
-
-// stb_sprintf - v1.10 - public domain snprintf() implementation
-// originally by Jeff Roberts / RAD Game Tools, 2015/10/20
-// http://github.com/nothings/stb
-//
-// allowed types:  sc uidBboXx p AaGgEef n
-// lengths      :  hh h ll j z t I64 I32 I
-//
-// Contributors:
-//    Fabian "ryg" Giesen (reformatting)
-//    github:aganm (attribute format)
-//
-// Contributors (bugfixes):
-//    github:d26435
-//    github:trex78
-//    github:account-login
-//    Jari Komppa (SI suffixes)
-//    Rohit Nirmal
-//    Marcin Wojdyr
-//    Leonard Ritter
-//    Stefano Zanotti
-//    Adam Allison
-//    Arvid Gerstmann
-//    Markus Kolb
-//
-// LICENSE:
-//
-//   See end of file for license information.
-
-#ifndef STB_SPRINTF_H_INCLUDE
-#define STB_SPRINTF_H_INCLUDE
-
-/*
-Single file sprintf replacement.
-
-Originally written by Jeff Roberts at RAD Game Tools - 2015/10/20.
-Hereby placed in public domain.
-
-This is a full sprintf replacement that supports everything that
-the C runtime sprintfs support, including float/double, 64-bit integers,
-hex floats, field parameters (%*.*d stuff), length reads backs, etc.
-
-Why would you need this if sprintf already exists?  Well, first off,
-it's *much* faster (see below). It's also much smaller than the CRT
-versions code-space-wise. We've also added some simple improvements
-that are super handy (commas in thousands, callbacks at buffer full,
-for example). Finally, the format strings for MSVC and GCC differ
-for 64-bit integers (among other small things), so this lets you use
-the same format strings in cross platform code.
-
-It uses the standard single file trick of being both the header file
-and the source itself. If you just include it normally, you just get
-the header file function definitions. To get the code, you include
-it from a C or C++ file and define STB_SPRINTF_IMPLEMENTATION first.
-
-It only uses va_args macros from the C runtime to do it's work. It
-does cast doubles to S64s and shifts and divides U64s, which does
-drag in CRT code on most platforms.
-
-It compiles to roughly 8K with float support, and 4K without.
-As a comparison, when using MSVC static libs, calling sprintf drags
-in 16K.
-
-API:
-====
-int stbsp_sprintf( char * buf, char const * fmt, ... )
-int stbsp_snprintf( char * buf, int count, char const * fmt, ... )
-  Convert an arg list into a buffer.  stbsp_snprintf always returns
-  a zero-terminated string (unlike regular snprintf).
-
-int stbsp_vsprintf( char * buf, char const * fmt, va_list va )
-int stbsp_vsnprintf( char * buf, int count, char const * fmt, va_list va )
-  Convert a va_list arg list into a buffer.  stbsp_vsnprintf always returns
-  a zero-terminated string (unlike regular snprintf).
-
-int stbsp_vsprintfcb( STBSP_SPRINTFCB * callback, void * user, char * buf, char const * fmt, va_list va )
-    typedef char * STBSP_SPRINTFCB( char const * buf, void * user, int len );
-  Convert into a buffer, calling back every STB_SPRINTF_MIN chars.
-  Your callback can then copy the chars out, print them or whatever.
-  This function is actually the workhorse for everything else.
-  The buffer you pass in must hold at least STB_SPRINTF_MIN characters.
-    // you return the next buffer to use or 0 to stop converting
-
-void stbsp_set_separators( char comma, char period )
-  Set the comma and period characters to use.
-
-FLOATS/DOUBLES:
-===============
-This code uses a internal float->ascii conversion method that uses
-doubles with error correction (double-doubles, for ~105 bits of
-precision).  This conversion is round-trip perfect - that is, an atof
-of the values output here will give you the bit-exact double back.
-
-One difference is that our insignificant digits will be different than
-with MSVC or GCC (but they don't match each other either).  We also
-don't attempt to find the minimum length matching float (pre-MSVC15
-doesn't either).
-
-If you don't need float or doubles at all, define STB_SPRINTF_NOFLOAT
-and you'll save 4K of code space.
-
-64-BIT INTS:
-============
-This library also supports 64-bit integers and you can use MSVC style or
-GCC style indicators (%I64d or %lld).  It supports the C99 specifiers
-for size_t and ptr_diff_t (%jd %zd) as well.
-
-EXTRAS:
-=======
-Like some GCCs, for integers and floats, you can use a ' (single quote)
-specifier and commas will be inserted on the thousands: "%'d" on 12345
-would print 12,345.
-
-For integers and floats, you can use a "$" specifier and the number
-will be converted to float and then divided to get kilo, mega, giga or
-tera and then printed, so "%$d" 1000 is "1.0 k", "%$.2d" 2536000 is
-"2.53 M", etc. For byte values, use two $:s, like "%$$d" to turn
-2536000 to "2.42 Mi". If you prefer JEDEC suffixes to SI ones, use three
-$:s: "%$$$d" -> "2.42 M". To remove the space between the number and the
-suffix, add "_" specifier: "%_$d" -> "2.53M".
-
-In addition to octal and hexadecimal conversions, you can print
-integers in binary: "%b" for 256 would print 100.
-
-PERFORMANCE vs MSVC 2008 32-/64-bit (GCC is even slower than MSVC):
-===================================================================
-"%d" across all 32-bit ints (4.8x/4.0x faster than 32-/64-bit MSVC)
-"%24d" across all 32-bit ints (4.5x/4.2x faster)
-"%x" across all 32-bit ints (4.5x/3.8x faster)
-"%08x" across all 32-bit ints (4.3x/3.8x faster)
-"%f" across e-10 to e+10 floats (7.3x/6.0x faster)
-"%e" across e-10 to e+10 floats (8.1x/6.0x faster)
-"%g" across e-10 to e+10 floats (10.0x/7.1x faster)
-"%f" for values near e-300 (7.9x/6.5x faster)
-"%f" for values near e+300 (10.0x/9.1x faster)
-"%e" for values near e-300 (10.1x/7.0x faster)
-"%e" for values near e+300 (9.2x/6.0x faster)
-"%.320f" for values near e-300 (12.6x/11.2x faster)
-"%a" for random values (8.6x/4.3x faster)
-"%I64d" for 64-bits with 32-bit values (4.8x/3.4x faster)
-"%I64d" for 64-bits > 32-bit values (4.9x/5.5x faster)
-"%s%s%s" for 64 char strings (7.1x/7.3x faster)
-"...512 char string..." ( 35.0x/32.5x faster!)
-*/
-
-#if defined(__clang__)
- #if defined(__has_feature) && defined(__has_attribute)
-  #if __has_feature(address_sanitizer)
-   #if __has_attribute(__no_sanitize__)
-    #define STBSP__ASAN __attribute__((__no_sanitize__("address")))
-   #elif __has_attribute(__no_sanitize_address__)
-    #define STBSP__ASAN __attribute__((__no_sanitize_address__))
-   #elif __has_attribute(__no_address_safety_analysis__)
-    #define STBSP__ASAN __attribute__((__no_address_safety_analysis__))
-   #endif
-  #endif
- #endif
-#elif defined(__GNUC__) && (__GNUC__ >= 5 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))
- #if defined(__SANITIZE_ADDRESS__) && __SANITIZE_ADDRESS__
-  #define STBSP__ASAN __attribute__((__no_sanitize_address__))
- #endif
-#endif
-
-#ifndef STBSP__ASAN
-#define STBSP__ASAN
-#endif
-
-#ifdef STB_SPRINTF_STATIC
-#define STBSP__PUBLICDEC static
-#define STBSP__PUBLICDEF static STBSP__ASAN
-#else
-#ifdef __cplusplus
-#define STBSP__PUBLICDEC extern "C"
-#define STBSP__PUBLICDEF extern "C" STBSP__ASAN
-#else
-#define STBSP__PUBLICDEC extern
-#define STBSP__PUBLICDEF STBSP__ASAN
-#endif
-#endif
-
-#if defined(__has_attribute)
- #if __has_attribute(format)
-   #define STBSP__ATTRIBUTE_FORMAT(fmt,va) __attribute__((format(printf,fmt,va)))
- #endif
-#endif
-
-#ifndef STBSP__ATTRIBUTE_FORMAT
-#define STBSP__ATTRIBUTE_FORMAT(fmt,va)
-#endif
-
-#ifdef _MSC_VER
-#define STBSP__NOTUSED(v)  (void)(v)
-#else
-#define STBSP__NOTUSED(v)  (void)sizeof(v)
-#endif
-
-#include <stdarg.h> // for va_arg(), va_list()
-#include <stddef.h> // size_t, ptrdiff_t
-
-#ifndef STB_SPRINTF_MIN
-#define STB_SPRINTF_MIN 512 // how many characters per callback
-#endif
-typedef char *STBSP_SPRINTFCB(const char *buf, void *user, int len);
-
-#ifndef STB_SPRINTF_DECORATE
-#define STB_SPRINTF_DECORATE(name) stbsp_##name // define this before including if you want to change the names
-#endif
-
-STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(vsprintf)(char *buf, char const *fmt, va_list va);
-STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(vsnprintf)(char *buf, int count, char const *fmt, va_list va);
-STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(sprintf)(char *buf, char const *fmt, ...) STBSP__ATTRIBUTE_FORMAT(2,3);
-STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(snprintf)(char *buf, int count, char const *fmt, ...) STBSP__ATTRIBUTE_FORMAT(3,4);
-
-STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(vsprintfcb)(STBSP_SPRINTFCB *callback, void *user, char *buf, char const *fmt, va_list va);
-STBSP__PUBLICDEC void STB_SPRINTF_DECORATE(set_separators)(char comma, char period);
-
-#endif // STB_SPRINTF_H_INCLUDE
-
-// ----------------------------------------------------------------
-
-// typedefs
-typedef unsigned int uint;
-typedef uint8_t  uint8;
-typedef uint16_t uint16;
-typedef uint32_t uint32;
-typedef uint64_t uint64;
-
-typedef int8_t  int8;
-typedef int16_t int16;
-typedef int32_t int32;
-typedef int64_t int64;
-
-typedef float  float32;
-typedef double float64;
-
-typedef wchar_t wchar;
-
-typedef const char*  cstr;
-typedef const wchar* wstr;
-
-#define ASSERT(condition) if (!(condition)) panic_assertion(#condition, stderr, __FILE__, __LINE__)
-
-#define ARRAY_LEN(arr) (sizeof(arr) / sizeof(arr[0]))
-
-#define STRUCT(name) typedef struct name name
-#define ENUM(name)   typedef enum name name
-
-// Struct pre-decls
-
-STRUCT(Arena);
-
-void panic_assertion(cstr msg, FILE* file, cstr filename, int line);
-
-#define TEMP_BUFF_SIZE (1024*4)
-static char tempbuff[TEMP_BUFF_SIZE];
-
-#define temp_sprintf(var, fmt, ...) \
-  stbsp_snprintf(tempbuff, TEMP_BUFF_SIZE, fmt, __VA_ARGS__);  \
-  (var) = tempbuff
-
-//
-// Winapi
-//
-
-#define WINAPI_ERROR_MSG_BUFF_SIZE 1024
-static char winapi_error_msg_buff[WINAPI_ERROR_MSG_BUFF_SIZE];
-#define WINAPI_OUTPUT_STR_BUFF_SIZE (16*1024)
-static CHAR_INFO winapi_output_str_buff[WINAPI_OUTPUT_STR_BUFF_SIZE];
-
-bool output_str(cstr text);
-bool output_strn(cstr text, size_t text_len);
-cstr winapi_get_last_error_str(void);
-cstr winapi_get_current_working_directory(Arena* arena);
-
-//
-// logging
-//
-
-// FLUSH_ON_LOG - define this macro to fflush() on every call to log() not defined by default.
-
-typedef enum {
-  LOG_INFO = 0,
-  LOG_ERROR,
-  LOG_WARNING,
-  LOG_COUNT,
-} Log_type;
-
-void log_file(Log_type type, FILE* file, cstr fmt, ...);
-#define log_f(type, fmt, ...)  log_file(type, stdout, fmt, __VA_ARGS__)
-#define log_info(fmt, ...)     log_f(LOG_INFO, fmt, __VA_ARGS__)
-#define log_error(fmt, ...)    log_f(LOG_ERROR, fmt, __VA_ARGS__)
-#define log_warning(fmt, ...)  log_f(LOG_WARNING, fmt, __VA_ARGS__)
-
-//
-// File
-//
-
-// reads entire file and gives back the string holding the contents. (caller must be responsible for freeing the string!)
-const char* slurp_file(const char* filename);
-
-//
-// ### Allocators ###
-//
-
-//
-// Arena
-//
-
-#define ARENA_BUFF_INITIAL_SIZE (1024*4)
-
-struct Arena {
-  void* buff;
-  uint64 buff_size;
-  void* ptr;
-};
-
-// pass size 0 to get ARENA_BUFF_INITIAL_SIZE
-Arena Arena_make(size_t size);
-void* Arena_alloc(Arena* a, size_t size);
-void Arena_reset(Arena* a);
-void Arena_free(Arena* a);
-
-#define Arena_alloc_str(a, fmt, ...)  Arena_alloc(&(a), sizeof(char)*stbsp_snprintf((a).ptr, (int)((a).buff_size - ((uint8*)(a).ptr - (uint8*)(a).buff)), (fmt), __VA_ARGS__)+1)
-#define Arena_alloc_wstr(a, fmt, ...) Arena_alloc(&a, sizeof(char)*wprintf(a.ptr, a.buff_size - ((uint8*)a.ptr - (uint8*)a.buff), (fmt), __VA_ARGS__)+1)
-
-//
-// String
-//
-
-bool cstr_to_wstr(Arena* warena, cstr str);
-
-//
-// String view
-//
-
-typedef struct {
-  cstr data;
-  size_t count;
-} String_view;
-
-#define SV_FMT "%.*s"
-#define SV_ARG(sv) (int)sv.count, sv.data
-
-#define SV(cstr) (String_view){.data = cstr, strlen(cstr)}
-
-void sv_print_dumb(String_view sv);
-String_view sv_from_cstr(const char* cstr); // Actually just use SV(cstr) macro...
-String_view sv_lpop(String_view* sv, uint32 n);
-String_view sv_lpop_until_predicate(String_view* sv, int(*predicate)(int));
-String_view sv_rpop_until_predicate(String_view* sv, int(*predicate)(int));
-String_view sv_lpop_until_char(String_view* sv, char ch);
-String_view sv_rpop_until_char(String_view* sv, char ch);
-void sv_lremove(String_view* sv, size_t n);
-void sv_rremove(String_view* sv, size_t n);
-void sv_lremove_until_char(String_view* sv, char ch);
-void sv_rremove_until_char(String_view* sv, char ch);
-void sv_lremove_until_char_after(String_view* sv, char ch);
-void sv_rremove_until_char_after(String_view* sv, char ch);
-void sv_ltrim(String_view* sv);
-void sv_rtrim(String_view* sv);
-void sv_trim(String_view* sv);
-char* sv_to_cstr(String_view sv);
-int32  sv_to_int(String_view sv);
-uint64 sv_to_uint64(String_view sv);
-uint8 sv_to_uint8_hex(String_view sv);
-void*  sv_to_ptr(String_view sv);
-float32 sv_to_float(String_view sv);
-bool sv_contains_char(String_view sv, char ch);
-bool sv_is_hex_numbers(String_view sv);
-bool sv_equals(String_view sv1, String_view sv2);
-
-//
-// Args
-//
-
-cstr shift_args(int* argc, char*** argv);
-
-#endif /* _STDLIB_H_ */
-
-//////////////////////////////////////////////////
-#ifdef COMMONLIB_IMPLEMENTATION
-#include <string.h>
-#include <errno.h>
-#include <stdlib.h>
-
-//
-// External libs --------------------------------------------------
-//
-
-// stb_ds.h:
-
-#define STB_DS_IMPLEMENTATION
-#ifdef STB_DS_IMPLEMENTATION
-#include <assert.h>
-#include <string.h>
-
-#ifndef STBDS_ASSERT
-#define STBDS_ASSERT_WAS_UNDEFINED
-#define STBDS_ASSERT(x)   ((void) 0)
-#endif
-
-#ifdef STBDS_STATISTICS
-#define STBDS_STATS(x)   x
-size_t stbds_array_grow;
-size_t stbds_hash_grow;
-size_t stbds_hash_shrink;
-size_t stbds_hash_rebuild;
-size_t stbds_hash_probes;
-size_t stbds_hash_alloc;
-size_t stbds_rehash_probes;
-size_t stbds_rehash_items;
-#else
-#define STBDS_STATS(x)
-#endif
-
-//
-// stbds_arr implementation
-//
-
-//int *prev_allocs[65536];
-//int num_prev;
-
-void *stbds_arrgrowf(void *a, size_t elemsize, size_t addlen, size_t min_cap)
-{
-  stbds_array_header temp={0}; // force debugging
-  void *b;
-  size_t min_len = stbds_arrlen(a) + addlen;
-  (void) sizeof(temp);
-
-  // compute the minimum capacity needed
-  if (min_len > min_cap)
-    min_cap = min_len;
-
-  if (min_cap <= stbds_arrcap(a))
-    return a;
-
-  // increase needed capacity to guarantee O(1) amortized
-  if (min_cap < 2 * stbds_arrcap(a))
-    min_cap = 2 * stbds_arrcap(a);
-  else if (min_cap < 4)
-    min_cap = 4;
-
-  //if (num_prev < 65536) if (a) prev_allocs[num_prev++] = (int *) ((char *) a+1);
-  //if (num_prev == 2201)
-  //  num_prev = num_prev;
-  b = STBDS_REALLOC(NULL, (a) ? stbds_header(a) : 0, elemsize * min_cap + sizeof(stbds_array_header));
-  //if (num_prev < 65536) prev_allocs[num_prev++] = (int *) (char *) b;
-  b = (char *) b + sizeof(stbds_array_header);
-  if (a == NULL) {
-    stbds_header(b)->length = 0;
-    stbds_header(b)->hash_table = 0;
-    stbds_header(b)->temp = 0;
-  } else {
-    STBDS_STATS(++stbds_array_grow);
-  }
-  stbds_header(b)->capacity = min_cap;
-
-  return b;
-}
-
-void stbds_arrfreef(void *a)
-{
-  STBDS_FREE(NULL, stbds_header(a));
-}
-
-//
-// stbds_hm hash table implementation
-//
-
-#ifdef STBDS_INTERNAL_SMALL_BUCKET
-#define STBDS_BUCKET_LENGTH      4
-#else
-#define STBDS_BUCKET_LENGTH      8
-#endif
-
-#define STBDS_BUCKET_SHIFT      (STBDS_BUCKET_LENGTH == 8 ? 3 : 2)
-#define STBDS_BUCKET_MASK       (STBDS_BUCKET_LENGTH-1)
-#define STBDS_CACHE_LINE_SIZE   64
-
-#define STBDS_ALIGN_FWD(n,a)   (((n) + (a) - 1) & ~((a)-1))
-
-typedef struct
-{
-   size_t    hash [STBDS_BUCKET_LENGTH];
-   ptrdiff_t index[STBDS_BUCKET_LENGTH];
-} stbds_hash_bucket; // in 32-bit, this is one 64-byte cache line; in 64-bit, each array is one 64-byte cache line
-
-typedef struct
-{
-  char * temp_key; // this MUST be the first field of the hash table
-  size_t slot_count;
-  size_t used_count;
-  size_t used_count_threshold;
-  size_t used_count_shrink_threshold;
-  size_t tombstone_count;
-  size_t tombstone_count_threshold;
-  size_t seed;
-  size_t slot_count_log2;
-  stbds_string_arena string;
-  stbds_hash_bucket *storage; // not a separate allocation, just 64-byte aligned storage after this struct
-} stbds_hash_index;
-
-#define STBDS_INDEX_EMPTY    -1
-#define STBDS_INDEX_DELETED  -2
-#define STBDS_INDEX_IN_USE(x)  ((x) >= 0)
-
-#define STBDS_HASH_EMPTY      0
-#define STBDS_HASH_DELETED    1
-
-static size_t stbds_hash_seed=0x31415926;
-
-void stbds_rand_seed(size_t seed)
-{
-  stbds_hash_seed = seed;
-}
-
-#define stbds_load_32_or_64(var, temp, v32, v64_hi, v64_lo)                                          \
-  temp = v64_lo ^ v32, temp <<= 16, temp <<= 16, temp >>= 16, temp >>= 16, /* discard if 32-bit */   \
-  var = v64_hi, var <<= 16, var <<= 16,                                    /* discard if 32-bit */   \
-  var ^= temp ^ v32
-
-#define STBDS_SIZE_T_BITS           ((sizeof (size_t)) * 8)
-
-static size_t stbds_probe_position(size_t hash, size_t slot_count, size_t slot_log2)
-{
-  size_t pos;
-  STBDS_NOTUSED(slot_log2);
-  pos = hash & (slot_count-1);
-  #ifdef STBDS_INTERNAL_BUCKET_START
-  pos &= ~STBDS_BUCKET_MASK;
-  #endif
-  return pos;
-}
-
-static size_t stbds_log2(size_t slot_count)
-{
-  size_t n=0;
-  while (slot_count > 1) {
-    slot_count >>= 1;
-    ++n;
-  }
-  return n;
-}
-
-static stbds_hash_index *stbds_make_hash_index(size_t slot_count, stbds_hash_index *ot)
-{
-  stbds_hash_index *t;
-  t = (stbds_hash_index *) STBDS_REALLOC(NULL,0,(slot_count >> STBDS_BUCKET_SHIFT) * sizeof(stbds_hash_bucket) + sizeof(stbds_hash_index) + STBDS_CACHE_LINE_SIZE-1);
-  t->storage = (stbds_hash_bucket *) STBDS_ALIGN_FWD((size_t) (t+1), STBDS_CACHE_LINE_SIZE);
-  t->slot_count = slot_count;
-  t->slot_count_log2 = stbds_log2(slot_count);
-  t->tombstone_count = 0;
-  t->used_count = 0;
-
-  #if 0 // A1
-  t->used_count_threshold        = slot_count*12/16; // if 12/16th of table is occupied, grow
-  t->tombstone_count_threshold   = slot_count* 2/16; // if tombstones are 2/16th of table, rebuild
-  t->used_count_shrink_threshold = slot_count* 4/16; // if table is only 4/16th full, shrink
-  #elif 1 // A2
-  //t->used_count_threshold        = slot_count*12/16; // if 12/16th of table is occupied, grow
-  //t->tombstone_count_threshold   = slot_count* 3/16; // if tombstones are 3/16th of table, rebuild
-  //t->used_count_shrink_threshold = slot_count* 4/16; // if table is only 4/16th full, shrink
-
-  // compute without overflowing
-  t->used_count_threshold        = slot_count - (slot_count>>2);
-  t->tombstone_count_threshold   = (slot_count>>3) + (slot_count>>4);
-  t->used_count_shrink_threshold = slot_count >> 2;
-
-  #elif 0 // B1
-  t->used_count_threshold        = slot_count*13/16; // if 13/16th of table is occupied, grow
-  t->tombstone_count_threshold   = slot_count* 2/16; // if tombstones are 2/16th of table, rebuild
-  t->used_count_shrink_threshold = slot_count* 5/16; // if table is only 5/16th full, shrink
-  #else // C1
-  t->used_count_threshold        = slot_count*14/16; // if 14/16th of table is occupied, grow
-  t->tombstone_count_threshold   = slot_count* 2/16; // if tombstones are 2/16th of table, rebuild
-  t->used_count_shrink_threshold = slot_count* 6/16; // if table is only 6/16th full, shrink
-  #endif
-  // Following statistics were measured on a Core i7-6700 @ 4.00Ghz, compiled with clang 7.0.1 -O2
-    // Note that the larger tables have high variance as they were run fewer times
-  //     A1            A2          B1           C1
-  //    0.10ms :     0.10ms :     0.10ms :     0.11ms :      2,000 inserts creating 2K table
-  //    0.96ms :     0.95ms :     0.97ms :     1.04ms :     20,000 inserts creating 20K table
-  //   14.48ms :    14.46ms :    10.63ms :    11.00ms :    200,000 inserts creating 200K table
-  //  195.74ms :   196.35ms :   203.69ms :   214.92ms :  2,000,000 inserts creating 2M table
-  // 2193.88ms :  2209.22ms :  2285.54ms :  2437.17ms : 20,000,000 inserts creating 20M table
-  //   65.27ms :    53.77ms :    65.33ms :    65.47ms : 500,000 inserts & deletes in 2K table
-  //   72.78ms :    62.45ms :    71.95ms :    72.85ms : 500,000 inserts & deletes in 20K table
-  //   89.47ms :    77.72ms :    96.49ms :    96.75ms : 500,000 inserts & deletes in 200K table
-  //   97.58ms :    98.14ms :    97.18ms :    97.53ms : 500,000 inserts & deletes in 2M table
-  //  118.61ms :   119.62ms :   120.16ms :   118.86ms : 500,000 inserts & deletes in 20M table
-  //  192.11ms :   194.39ms :   196.38ms :   195.73ms : 500,000 inserts & deletes in 200M table
-
-  if (slot_count <= STBDS_BUCKET_LENGTH)
-    t->used_count_shrink_threshold = 0;
-  // to avoid infinite loop, we need to guarantee that at least one slot is empty and will terminate probes
-  STBDS_ASSERT(t->used_count_threshold + t->tombstone_count_threshold < t->slot_count);
-  STBDS_STATS(++stbds_hash_alloc);
-  if (ot) {
-    t->string = ot->string;
-    // reuse old seed so we can reuse old hashes so below "copy out old data" doesn't do any hashing
-    t->seed = ot->seed;
-  } else {
-    size_t a,b,temp;
-    memset(&t->string, 0, sizeof(t->string));
-    t->seed = stbds_hash_seed;
-    // LCG
-    // in 32-bit, a =          2147001325   b =  715136305
-    // in 64-bit, a = 2862933555777941757   b = 3037000493
-    stbds_load_32_or_64(a,temp, 2147001325, 0x27bb2ee6, 0x87b0b0fd);
-    stbds_load_32_or_64(b,temp,  715136305,          0, 0xb504f32d);
-    stbds_hash_seed = stbds_hash_seed  * a + b;
-  }
-
-  {
-    size_t i,j;
-    for (i=0; i < slot_count >> STBDS_BUCKET_SHIFT; ++i) {
-      stbds_hash_bucket *b = &t->storage[i];
-      for (j=0; j < STBDS_BUCKET_LENGTH; ++j)
-        b->hash[j] = STBDS_HASH_EMPTY;
-      for (j=0; j < STBDS_BUCKET_LENGTH; ++j)
-        b->index[j] = STBDS_INDEX_EMPTY;
-    }
-  }
-
-  // copy out the old data, if any
-  if (ot) {
-    size_t i,j;
-    t->used_count = ot->used_count;
-    for (i=0; i < ot->slot_count >> STBDS_BUCKET_SHIFT; ++i) {
-      stbds_hash_bucket *ob = &ot->storage[i];
-      for (j=0; j < STBDS_BUCKET_LENGTH; ++j) {
-        if (STBDS_INDEX_IN_USE(ob->index[j])) {
-          size_t hash = ob->hash[j];
-          size_t pos = stbds_probe_position(hash, t->slot_count, t->slot_count_log2);
-          size_t step = STBDS_BUCKET_LENGTH;
-          STBDS_STATS(++stbds_rehash_items);
-          for (;;) {
-            size_t limit,z;
-            stbds_hash_bucket *bucket;
-            bucket = &t->storage[pos >> STBDS_BUCKET_SHIFT];
-            STBDS_STATS(++stbds_rehash_probes);
-
-            for (z=pos & STBDS_BUCKET_MASK; z < STBDS_BUCKET_LENGTH; ++z) {
-              if (bucket->hash[z] == 0) {
-                bucket->hash[z] = hash;
-                bucket->index[z] = ob->index[j];
-                goto done;
-              }
-            }
-
-            limit = pos & STBDS_BUCKET_MASK;
-            for (z = 0; z < limit; ++z) {
-              if (bucket->hash[z] == 0) {
-                bucket->hash[z] = hash;
-                bucket->index[z] = ob->index[j];
-                goto done;
-              }
-            }
-
-            pos += step;                  // quadratic probing
-            step += STBDS_BUCKET_LENGTH;
-            pos &= (t->slot_count-1);
-          }
-        }
-       done:
-        ;
-      }
-    }
-  }
-
-  return t;
-}
-
-#define STBDS_ROTATE_LEFT(val, n)   (((val) << (n)) | ((val) >> (STBDS_SIZE_T_BITS - (n))))
-#define STBDS_ROTATE_RIGHT(val, n)  (((val) >> (n)) | ((val) << (STBDS_SIZE_T_BITS - (n))))
-
-size_t stbds_hash_string(char *str, size_t seed)
-{
-  size_t hash = seed;
-  while (*str)
-     hash = STBDS_ROTATE_LEFT(hash, 9) + (unsigned char) *str++;
-
-  // Thomas Wang 64-to-32 bit mix function, hopefully also works in 32 bits
-  hash ^= seed;
-  hash = (~hash) + (hash << 18);
-  hash ^= hash ^ STBDS_ROTATE_RIGHT(hash,31);
-  hash = hash * 21;
-  hash ^= hash ^ STBDS_ROTATE_RIGHT(hash,11);
-  hash += (hash << 6);
-  hash ^= STBDS_ROTATE_RIGHT(hash,22);
-  return hash+seed;
-}
-
-#ifdef STBDS_SIPHASH_2_4
-#define STBDS_SIPHASH_C_ROUNDS 2
-#define STBDS_SIPHASH_D_ROUNDS 4
-typedef int STBDS_SIPHASH_2_4_can_only_be_used_in_64_bit_builds[sizeof(size_t) == 8 ? 1 : -1];
-#endif
-
-#ifndef STBDS_SIPHASH_C_ROUNDS
-#define STBDS_SIPHASH_C_ROUNDS 1
-#endif
-#ifndef STBDS_SIPHASH_D_ROUNDS
-#define STBDS_SIPHASH_D_ROUNDS 1
-#endif
-
-#ifdef _MSC_VER
-#pragma warning(push)
-#pragma warning(disable:4127) // conditional expression is constant, for do..while(0) and sizeof()==
-#endif
-
-static size_t stbds_siphash_bytes(void *p, size_t len, size_t seed)
-{
-  unsigned char *d = (unsigned char *) p;
-  size_t i,j;
-  size_t v0,v1,v2,v3, data;
-
-  // hash that works on 32- or 64-bit registers without knowing which we have
-  // (computes different results on 32-bit and 64-bit platform)
-  // derived from siphash, but on 32-bit platforms very different as it uses 4 32-bit state not 4 64-bit
-  v0 = ((((size_t) 0x736f6d65 << 16) << 16) + 0x70736575) ^  seed;
-  v1 = ((((size_t) 0x646f7261 << 16) << 16) + 0x6e646f6d) ^ ~seed;
-  v2 = ((((size_t) 0x6c796765 << 16) << 16) + 0x6e657261) ^  seed;
-  v3 = ((((size_t) 0x74656462 << 16) << 16) + 0x79746573) ^ ~seed;
-
-  #ifdef STBDS_TEST_SIPHASH_2_4
-  // hardcoded with key material in the siphash test vectors
-  v0 ^= 0x0706050403020100ull ^  seed;
-  v1 ^= 0x0f0e0d0c0b0a0908ull ^ ~seed;
-  v2 ^= 0x0706050403020100ull ^  seed;
-  v3 ^= 0x0f0e0d0c0b0a0908ull ^ ~seed;
-  #endif
-
-  #define STBDS_SIPROUND() \
-    do {                   \
-      v0 += v1; v1 = STBDS_ROTATE_LEFT(v1, 13);  v1 ^= v0; v0 = STBDS_ROTATE_LEFT(v0,STBDS_SIZE_T_BITS/2); \
-      v2 += v3; v3 = STBDS_ROTATE_LEFT(v3, 16);  v3 ^= v2;                                                 \
-      v2 += v1; v1 = STBDS_ROTATE_LEFT(v1, 17);  v1 ^= v2; v2 = STBDS_ROTATE_LEFT(v2,STBDS_SIZE_T_BITS/2); \
-      v0 += v3; v3 = STBDS_ROTATE_LEFT(v3, 21);  v3 ^= v0;                                                 \
-    } while (0)
-
-  for (i=0; i+sizeof(size_t) <= len; i += sizeof(size_t), d += sizeof(size_t)) {
-    data = d[0] | (d[1] << 8) | (d[2] << 16) | (d[3] << 24);
-    data |= (size_t) (d[4] | (d[5] << 8) | (d[6] << 16) | (d[7] << 24)) << 16 << 16; // discarded if size_t == 4
-
-    v3 ^= data;
-    for (j=0; j < STBDS_SIPHASH_C_ROUNDS; ++j)
-      STBDS_SIPROUND();
-    v0 ^= data;
-  }
-  data = len << (STBDS_SIZE_T_BITS-8);
-  switch (len - i) {
-    case 7: data |= ((size_t) d[6] << 24) << 24; // fall through
-    case 6: data |= ((size_t) d[5] << 20) << 20; // fall through
-    case 5: data |= ((size_t) d[4] << 16) << 16; // fall through
-    case 4: data |= (d[3] << 24); // fall through
-    case 3: data |= (d[2] << 16); // fall through
-    case 2: data |= (d[1] << 8); // fall through
-    case 1: data |= d[0]; // fall through
-    case 0: break;
-  }
-  v3 ^= data;
-  for (j=0; j < STBDS_SIPHASH_C_ROUNDS; ++j)
-    STBDS_SIPROUND();
-  v0 ^= data;
-  v2 ^= 0xff;
-  for (j=0; j < STBDS_SIPHASH_D_ROUNDS; ++j)
-    STBDS_SIPROUND();
-
-#ifdef STBDS_SIPHASH_2_4
-  return v0^v1^v2^v3;
-#else
-  return v1^v2^v3; // slightly stronger since v0^v3 in above cancels out final round operation? I tweeted at the authors of SipHash about this but they didn't reply
-#endif
-}
-
-size_t stbds_hash_bytes(void *p, size_t len, size_t seed)
-{
-#ifdef STBDS_SIPHASH_2_4
-  return stbds_siphash_bytes(p,len,seed);
-#else
-  unsigned char *d = (unsigned char *) p;
-
-  if (len == 4) {
-    unsigned int hash = d[0] | (d[1] << 8) | (d[2] << 16) | (d[3] << 24);
-    #if 0
-    // HASH32-A  Bob Jenkin's hash function w/o large constants
-    hash ^= seed;
-    hash -= (hash<<6);
-    hash ^= (hash>>17);
-    hash -= (hash<<9);
-    hash ^= seed;
-    hash ^= (hash<<4);
-    hash -= (hash<<3);
-    hash ^= (hash<<10);
-    hash ^= (hash>>15);
-    #elif 1
-    // HASH32-BB  Bob Jenkin's presumably-accidental version of Thomas Wang hash with rotates turned into shifts.
-    // Note that converting these back to rotates makes it run a lot slower, presumably due to collisions, so I'm
-    // not really sure what's going on.
-    hash ^= seed;
-    hash = (hash ^ 61) ^ (hash >> 16);
-    hash = hash + (hash << 3);
-    hash = hash ^ (hash >> 4);
-    hash = hash * 0x27d4eb2d;
-    hash ^= seed;
-    hash = hash ^ (hash >> 15);
-    #else  // HASH32-C   -  Murmur3
-    hash ^= seed;
-    hash *= 0xcc9e2d51;
-    hash = (hash << 17) | (hash >> 15);
-    hash *= 0x1b873593;
-    hash ^= seed;
-    hash = (hash << 19) | (hash >> 13);
-    hash = hash*5 + 0xe6546b64;
-    hash ^= hash >> 16;
-    hash *= 0x85ebca6b;
-    hash ^= seed;
-    hash ^= hash >> 13;
-    hash *= 0xc2b2ae35;
-    hash ^= hash >> 16;
-    #endif
-    // Following statistics were measured on a Core i7-6700 @ 4.00Ghz, compiled with clang 7.0.1 -O2
-    // Note that the larger tables have high variance as they were run fewer times
-    //  HASH32-A   //  HASH32-BB  //  HASH32-C
-    //    0.10ms   //    0.10ms   //    0.10ms :      2,000 inserts creating 2K table
-    //    0.96ms   //    0.95ms   //    0.99ms :     20,000 inserts creating 20K table
-    //   14.69ms   //   14.43ms   //   14.97ms :    200,000 inserts creating 200K table
-    //  199.99ms   //  195.36ms   //  202.05ms :  2,000,000 inserts creating 2M table
-    // 2234.84ms   // 2187.74ms   // 2240.38ms : 20,000,000 inserts creating 20M table
-    //   55.68ms   //   53.72ms   //   57.31ms : 500,000 inserts & deletes in 2K table
-    //   63.43ms   //   61.99ms   //   65.73ms : 500,000 inserts & deletes in 20K table
-    //   80.04ms   //   77.96ms   //   81.83ms : 500,000 inserts & deletes in 200K table
-    //  100.42ms   //   97.40ms   //  102.39ms : 500,000 inserts & deletes in 2M table
-    //  119.71ms   //  120.59ms   //  121.63ms : 500,000 inserts & deletes in 20M table
-    //  185.28ms   //  195.15ms   //  187.74ms : 500,000 inserts & deletes in 200M table
-    //   15.58ms   //   14.79ms   //   15.52ms : 200,000 inserts creating 200K table with varying key spacing
-
-    return (((size_t) hash << 16 << 16) | hash) ^ seed;
-  } else if (len == 8 && sizeof(size_t) == 8) {
-    size_t hash = d[0] | (d[1] << 8) | (d[2] << 16) | (d[3] << 24);
-    hash |= (size_t) (d[4] | (d[5] << 8) | (d[6] << 16) | (d[7] << 24)) << 16 << 16; // avoid warning if size_t == 4
-    hash ^= seed;
-    hash = (~hash) + (hash << 21);
-    hash ^= STBDS_ROTATE_RIGHT(hash,24);
-    hash *= 265;
-    hash ^= STBDS_ROTATE_RIGHT(hash,14);
-    hash ^= seed;
-    hash *= 21;
-    hash ^= STBDS_ROTATE_RIGHT(hash,28);
-    hash += (hash << 31);
-    hash = (~hash) + (hash << 18);
-    return hash;
-  } else {
-    return stbds_siphash_bytes(p,len,seed);
-  }
-#endif
-}
-#ifdef _MSC_VER
-#pragma warning(pop)
-#endif
-
-
-static int stbds_is_key_equal(void *a, size_t elemsize, void *key, size_t keysize, size_t keyoffset, int mode, size_t i)
-{
-  if (mode >= STBDS_HM_STRING)
-    return 0==strcmp((char *) key, * (char **) ((char *) a + elemsize*i + keyoffset));
-  else
-    return 0==memcmp(key, (char *) a + elemsize*i + keyoffset, keysize);
-}
-
-#define STBDS_HASH_TO_ARR(x,elemsize) ((char*) (x) - (elemsize))
-#define STBDS_ARR_TO_HASH(x,elemsize) ((char*) (x) + (elemsize))
-
-#define stbds_hash_table(a)  ((stbds_hash_index *) stbds_header(a)->hash_table)
-
-void stbds_hmfree_func(void *a, size_t elemsize)
-{
-  if (a == NULL) return;
-  if (stbds_hash_table(a) != NULL) {
-    if (stbds_hash_table(a)->string.mode == STBDS_SH_STRDUP) {
-      size_t i;
-      // skip 0th element, which is default
-      for (i=1; i < stbds_header(a)->length; ++i)
-        STBDS_FREE(NULL, *(char**) ((char *) a + elemsize*i));
-    }
-    stbds_strreset(&stbds_hash_table(a)->string);
-  }
-  STBDS_FREE(NULL, stbds_header(a)->hash_table);
-  STBDS_FREE(NULL, stbds_header(a));
-}
-
-static ptrdiff_t stbds_hm_find_slot(void *a, size_t elemsize, void *key, size_t keysize, size_t keyoffset, int mode)
-{
-  void *raw_a = STBDS_HASH_TO_ARR(a,elemsize);
-  stbds_hash_index *table = stbds_hash_table(raw_a);
-  size_t hash = mode >= STBDS_HM_STRING ? stbds_hash_string((char*)key,table->seed) : stbds_hash_bytes(key, keysize,table->seed);
-  size_t step = STBDS_BUCKET_LENGTH;
-  size_t limit,i;
-  size_t pos;
-  stbds_hash_bucket *bucket;
-
-  if (hash < 2) hash += 2; // stored hash values are forbidden from being 0, so we can detect empty slots
-
-  pos = stbds_probe_position(hash, table->slot_count, table->slot_count_log2);
-
-  for (;;) {
-    STBDS_STATS(++stbds_hash_probes);
-    bucket = &table->storage[pos >> STBDS_BUCKET_SHIFT];
-
-    // start searching from pos to end of bucket, this should help performance on small hash tables that fit in cache
-    for (i=pos & STBDS_BUCKET_MASK; i < STBDS_BUCKET_LENGTH; ++i) {
-      if (bucket->hash[i] == hash) {
-        if (stbds_is_key_equal(a, elemsize, key, keysize, keyoffset, mode, bucket->index[i])) {
-          return (pos & ~STBDS_BUCKET_MASK)+i;
-        }
-      } else if (bucket->hash[i] == STBDS_HASH_EMPTY) {
-        return -1;
-      }
-    }
-
-    // search from beginning of bucket to pos
-    limit = pos & STBDS_BUCKET_MASK;
-    for (i = 0; i < limit; ++i) {
-      if (bucket->hash[i] == hash) {
-        if (stbds_is_key_equal(a, elemsize, key, keysize, keyoffset, mode, bucket->index[i])) {
-          return (pos & ~STBDS_BUCKET_MASK)+i;
-        }
-      } else if (bucket->hash[i] == STBDS_HASH_EMPTY) {
-        return -1;
-      }
-    }
-
-    // quadratic probing
-    pos += step;
-    step += STBDS_BUCKET_LENGTH;
-    pos &= (table->slot_count-1);
-  }
-  /* NOTREACHED */
-}
-
-void * stbds_hmget_key_ts(void *a, size_t elemsize, void *key, size_t keysize, ptrdiff_t *temp, int mode)
-{
-  size_t keyoffset = 0;
-  if (a == NULL) {
-    // make it non-empty so we can return a temp
-    a = stbds_arrgrowf(0, elemsize, 0, 1);
-    stbds_header(a)->length += 1;
-    memset(a, 0, elemsize);
-    *temp = STBDS_INDEX_EMPTY;
-    // adjust a to point after the default element
-    return STBDS_ARR_TO_HASH(a,elemsize);
-  } else {
-    stbds_hash_index *table;
-    void *raw_a = STBDS_HASH_TO_ARR(a,elemsize);
-    // adjust a to point to the default element
-    table = (stbds_hash_index *) stbds_header(raw_a)->hash_table;
-    if (table == 0) {
-      *temp = -1;
-    } else {
-      ptrdiff_t slot = stbds_hm_find_slot(a, elemsize, key, keysize, keyoffset, mode);
-      if (slot < 0) {
-        *temp = STBDS_INDEX_EMPTY;
-      } else {
-        stbds_hash_bucket *b = &table->storage[slot >> STBDS_BUCKET_SHIFT];
-        *temp = b->index[slot & STBDS_BUCKET_MASK];
-      }
-    }
-    return a;
-  }
-}
-
-void * stbds_hmget_key(void *a, size_t elemsize, void *key, size_t keysize, int mode)
-{
-  ptrdiff_t temp;
-  void *p = stbds_hmget_key_ts(a, elemsize, key, keysize, &temp, mode);
-  stbds_temp(STBDS_HASH_TO_ARR(p,elemsize)) = temp;
-  return p;
-}
-
-void * stbds_hmput_default(void *a, size_t elemsize)
-{
-  // three cases:
-  //   a is NULL <- allocate
-  //   a has a hash table but no entries, because of shmode <- grow
-  //   a has entries <- do nothing
-  if (a == NULL || stbds_header(STBDS_HASH_TO_ARR(a,elemsize))->length == 0) {
-    a = stbds_arrgrowf(a ? STBDS_HASH_TO_ARR(a,elemsize) : NULL, elemsize, 0, 1);
-    stbds_header(a)->length += 1;
-    memset(a, 0, elemsize);
-    a=STBDS_ARR_TO_HASH(a,elemsize);
-  }
-  return a;
-}
-
-static char *stbds_strdup(char *str);
-
-void *stbds_hmput_key(void *a, size_t elemsize, void *key, size_t keysize, int mode)
-{
-  size_t keyoffset=0;
-  void *raw_a;
-  stbds_hash_index *table;
-
-  if (a == NULL) {
-    a = stbds_arrgrowf(0, elemsize, 0, 1);
-    memset(a, 0, elemsize);
-    stbds_header(a)->length += 1;
-    // adjust a to point AFTER the default element
-    a = STBDS_ARR_TO_HASH(a,elemsize);
-  }
-
-  // adjust a to point to the default element
-  raw_a = a;
-  a = STBDS_HASH_TO_ARR(a,elemsize);
-
-  table = (stbds_hash_index *) stbds_header(a)->hash_table;
-
-  if (table == NULL || table->used_count >= table->used_count_threshold) {
-    stbds_hash_index *nt;
-    size_t slot_count;
-
-    slot_count = (table == NULL) ? STBDS_BUCKET_LENGTH : table->slot_count*2;
-    nt = stbds_make_hash_index(slot_count, table);
-    if (table)
-      STBDS_FREE(NULL, table);
-    else
-      nt->string.mode = mode >= STBDS_HM_STRING ? STBDS_SH_DEFAULT : 0;
-    stbds_header(a)->hash_table = table = nt;
-    STBDS_STATS(++stbds_hash_grow);
-  }
-
-  // we iterate hash table explicitly because we want to track if we saw a tombstone
-  {
-    size_t hash = mode >= STBDS_HM_STRING ? stbds_hash_string((char*)key,table->seed) : stbds_hash_bytes(key, keysize,table->seed);
-    size_t step = STBDS_BUCKET_LENGTH;
-    size_t pos;
-    ptrdiff_t tombstone = -1;
-    stbds_hash_bucket *bucket;
-
-    // stored hash values are forbidden from being 0, so we can detect empty slots to early out quickly
-    if (hash < 2) hash += 2;
-
-    pos = stbds_probe_position(hash, table->slot_count, table->slot_count_log2);
-
-    for (;;) {
-      size_t limit, i;
-      STBDS_STATS(++stbds_hash_probes);
-      bucket = &table->storage[pos >> STBDS_BUCKET_SHIFT];
-
-      // start searching from pos to end of bucket
-      for (i=pos & STBDS_BUCKET_MASK; i < STBDS_BUCKET_LENGTH; ++i) {
-        if (bucket->hash[i] == hash) {
-          if (stbds_is_key_equal(raw_a, elemsize, key, keysize, keyoffset, mode, bucket->index[i])) {
-            stbds_temp(a) = bucket->index[i];
-            if (mode >= STBDS_HM_STRING)
-              stbds_temp_key(a) = * (char **) ((char *) raw_a + elemsize*bucket->index[i] + keyoffset);
-            return STBDS_ARR_TO_HASH(a,elemsize);
-          }
-        } else if (bucket->hash[i] == 0) {
-          pos = (pos & ~STBDS_BUCKET_MASK) + i;
-          goto found_empty_slot;
-        } else if (tombstone < 0) {
-          if (bucket->index[i] == STBDS_INDEX_DELETED)
-            tombstone = (ptrdiff_t) ((pos & ~STBDS_BUCKET_MASK) + i);
-        }
-      }
-
-      // search from beginning of bucket to pos
-      limit = pos & STBDS_BUCKET_MASK;
-      for (i = 0; i < limit; ++i) {
-        if (bucket->hash[i] == hash) {
-          if (stbds_is_key_equal(raw_a, elemsize, key, keysize, keyoffset, mode, bucket->index[i])) {
-            stbds_temp(a) = bucket->index[i];
-            return STBDS_ARR_TO_HASH(a,elemsize);
-          }
-        } else if (bucket->hash[i] == 0) {
-          pos = (pos & ~STBDS_BUCKET_MASK) + i;
-          goto found_empty_slot;
-        } else if (tombstone < 0) {
-          if (bucket->index[i] == STBDS_INDEX_DELETED)
-            tombstone = (ptrdiff_t) ((pos & ~STBDS_BUCKET_MASK) + i);
-        }
-      }
-
-      // quadratic probing
-      pos += step;
-      step += STBDS_BUCKET_LENGTH;
-      pos &= (table->slot_count-1);
-    }
-   found_empty_slot:
-    if (tombstone >= 0) {
-      pos = tombstone;
-      --table->tombstone_count;
-    }
-    ++table->used_count;
-
-    {
-      ptrdiff_t i = (ptrdiff_t) stbds_arrlen(a);
-      // we want to do stbds_arraddn(1), but we can't use the macros since we don't have something of the right type
-      if ((size_t) i+1 > stbds_arrcap(a))
-        *(void **) &a = stbds_arrgrowf(a, elemsize, 1, 0);
-      raw_a = STBDS_ARR_TO_HASH(a,elemsize);
-
-      STBDS_ASSERT((size_t) i+1 <= stbds_arrcap(a));
-      stbds_header(a)->length = i+1;
-      bucket = &table->storage[pos >> STBDS_BUCKET_SHIFT];
-      bucket->hash[pos & STBDS_BUCKET_MASK] = hash;
-      bucket->index[pos & STBDS_BUCKET_MASK] = i-1;
-      stbds_temp(a) = i-1;
-
-      switch (table->string.mode) {
-         case STBDS_SH_STRDUP:  stbds_temp_key(a) = *(char **) ((char *) a + elemsize*i) = stbds_strdup((char*) key); break;
-         case STBDS_SH_ARENA:   stbds_temp_key(a) = *(char **) ((char *) a + elemsize*i) = stbds_stralloc(&table->string, (char*)key); break;
-         case STBDS_SH_DEFAULT: stbds_temp_key(a) = *(char **) ((char *) a + elemsize*i) = (char *) key; break;
-         default:                memcpy((char *) a + elemsize*i, key, keysize); break;
-      }
-    }
-    return STBDS_ARR_TO_HASH(a,elemsize);
-  }
-}
-
-void * stbds_shmode_func(size_t elemsize, int mode)
-{
-  void *a = stbds_arrgrowf(0, elemsize, 0, 1);
-  stbds_hash_index *h;
-  memset(a, 0, elemsize);
-  stbds_header(a)->length = 1;
-  stbds_header(a)->hash_table = h = (stbds_hash_index *) stbds_make_hash_index(STBDS_BUCKET_LENGTH, NULL);
-  h->string.mode = (unsigned char) mode;
-  return STBDS_ARR_TO_HASH(a,elemsize);
-}
-
-void * stbds_hmdel_key(void *a, size_t elemsize, void *key, size_t keysize, size_t keyoffset, int mode)
-{
-  if (a == NULL) {
-    return 0;
-  } else {
-    stbds_hash_index *table;
-    void *raw_a = STBDS_HASH_TO_ARR(a,elemsize);
-    table = (stbds_hash_index *) stbds_header(raw_a)->hash_table;
-    stbds_temp(raw_a) = 0;
-    if (table == 0) {
-      return a;
-    } else {
-      ptrdiff_t slot;
-      slot = stbds_hm_find_slot(a, elemsize, key, keysize, keyoffset, mode);
-      if (slot < 0)
-        return a;
-      else {
-        stbds_hash_bucket *b = &table->storage[slot >> STBDS_BUCKET_SHIFT];
-        int i = slot & STBDS_BUCKET_MASK;
-        ptrdiff_t old_index = b->index[i];
-        ptrdiff_t final_index = (ptrdiff_t) stbds_arrlen(raw_a)-1-1; // minus one for the raw_a vs a, and minus one for 'last'
-        STBDS_ASSERT(slot < (ptrdiff_t) table->slot_count);
-        --table->used_count;
-        ++table->tombstone_count;
-        stbds_temp(raw_a) = 1;
-        STBDS_ASSERT(table->used_count >= 0);
-        //STBDS_ASSERT(table->tombstone_count < table->slot_count/4);
-        b->hash[i] = STBDS_HASH_DELETED;
-        b->index[i] = STBDS_INDEX_DELETED;
-
-        if (mode == STBDS_HM_STRING && table->string.mode == STBDS_SH_STRDUP)
-          STBDS_FREE(NULL, *(char**) ((char *) a+elemsize*old_index));
-
-        // if indices are the same, memcpy is a no-op, but back-pointer-fixup will fail, so skip
-        if (old_index != final_index) {
-          // swap delete
-          memmove((char*) a + elemsize*old_index, (char*) a + elemsize*final_index, elemsize);
-
-          // now find the slot for the last element
-          if (mode == STBDS_HM_STRING)
-            slot = stbds_hm_find_slot(a, elemsize, *(char**) ((char *) a+elemsize*old_index + keyoffset), keysize, keyoffset, mode);
-          else
-            slot = stbds_hm_find_slot(a, elemsize,  (char* ) a+elemsize*old_index + keyoffset, keysize, keyoffset, mode);
-          STBDS_ASSERT(slot >= 0);
-          b = &table->storage[slot >> STBDS_BUCKET_SHIFT];
-          i = slot & STBDS_BUCKET_MASK;
-          STBDS_ASSERT(b->index[i] == final_index);
-          b->index[i] = old_index;
-        }
-        stbds_header(raw_a)->length -= 1;
-
-        if (table->used_count < table->used_count_shrink_threshold && table->slot_count > STBDS_BUCKET_LENGTH) {
-          stbds_header(raw_a)->hash_table = stbds_make_hash_index(table->slot_count>>1, table);
-          STBDS_FREE(NULL, table);
-          STBDS_STATS(++stbds_hash_shrink);
-        } else if (table->tombstone_count > table->tombstone_count_threshold) {
-          stbds_header(raw_a)->hash_table = stbds_make_hash_index(table->slot_count   , table);
-          STBDS_FREE(NULL, table);
-          STBDS_STATS(++stbds_hash_rebuild);
-        }
-
-        return a;
-      }
-    }
-  }
-  /* NOTREACHED */
-}
-
-static char *stbds_strdup(char *str)
-{
-  // to keep replaceable allocator simple, we don't want to use strdup.
-  // rolling our own also avoids problem of strdup vs _strdup
-  size_t len = strlen(str)+1;
-  char *p = (char*) STBDS_REALLOC(NULL, 0, len);
-  memmove(p, str, len);
-  return p;
-}
-
-#ifndef STBDS_STRING_ARENA_BLOCKSIZE_MIN
-#define STBDS_STRING_ARENA_BLOCKSIZE_MIN  512u
-#endif
-#ifndef STBDS_STRING_ARENA_BLOCKSIZE_MAX
-#define STBDS_STRING_ARENA_BLOCKSIZE_MAX  (1u<<20)
-#endif
-
-char *stbds_stralloc(stbds_string_arena *a, char *str)
-{
-  char *p;
-  size_t len = strlen(str)+1;
-  if (len > a->remaining) {
-    // compute the next blocksize
-    size_t blocksize = a->block;
-
-    // size is 512, 512, 1024, 1024, 2048, 2048, 4096, 4096, etc., so that
-    // there are log(SIZE) allocations to free when we destroy the table
-    blocksize = (size_t) (STBDS_STRING_ARENA_BLOCKSIZE_MIN) << (blocksize>>1);
-
-    // if size is under 1M, advance to next blocktype
-    if (blocksize < (size_t)(STBDS_STRING_ARENA_BLOCKSIZE_MAX))
-      ++a->block;
-
-    if (len > blocksize) {
-      // if string is larger than blocksize, then just allocate the full size.
-      // note that we still advance string_block so block size will continue
-      // increasing, so e.g. if somebody only calls this with 1000-long strings,
-      // eventually the arena will start doubling and handling those as well
-      stbds_string_block *sb = (stbds_string_block *) STBDS_REALLOC(NULL, 0, sizeof(*sb)-8 + len);
-      memmove(sb->storage, str, len);
-      if (a->storage) {
-        // insert it after the first element, so that we don't waste the space there
-        sb->next = a->storage->next;
-        a->storage->next = sb;
-      } else {
-        sb->next = 0;
-        a->storage = sb;
-        a->remaining = 0; // this is redundant, but good for clarity
-      }
-      return sb->storage;
-    } else {
-      stbds_string_block *sb = (stbds_string_block *) STBDS_REALLOC(NULL, 0, sizeof(*sb)-8 + blocksize);
-      sb->next = a->storage;
-      a->storage = sb;
-      a->remaining = blocksize;
-    }
-  }
-
-  STBDS_ASSERT(len <= a->remaining);
-  p = a->storage->storage + a->remaining - len;
-  a->remaining -= len;
-  memmove(p, str, len);
-  return p;
-}
-
-void stbds_strreset(stbds_string_arena *a)
-{
-  stbds_string_block *x,*y;
-  x = a->storage;
-  while (x) {
-    y = x->next;
-    STBDS_FREE(NULL, x);
-    x = y;
-  }
-  memset(a, 0, sizeof(*a));
-}
-
-#endif
-
-//////////////////////////////////////////////////////////////////////////////
-//
-//   UNIT TESTS
-//
-
-#ifdef STBDS_UNIT_TESTS
-#include <stdio.h>
-#ifdef STBDS_ASSERT_WAS_UNDEFINED
-#undef STBDS_ASSERT
-#endif
-#ifndef STBDS_ASSERT
-#define STBDS_ASSERT assert
-#include <assert.h>
-#endif
-
-typedef struct { int key,b,c,d; } stbds_struct;
-typedef struct { int key[2],b,c,d; } stbds_struct2;
-
-static char buffer[256];
-char *strkey(int n)
-{
-#if defined(_WIN32) && defined(__STDC_WANT_SECURE_LIB__)
-   sprintf_s(buffer, sizeof(buffer), "test_%d", n);
-#else
-   sprintf(buffer, "test_%d", n);
-#endif
-   return buffer;
-}
-
-void stbds_unit_tests(void)
-{
-#if defined(_MSC_VER) && _MSC_VER <= 1200 && defined(__cplusplus)
-  // VC6 C++ doesn't like the template<> trick on unnamed structures, so do nothing!
-  STBDS_ASSERT(0);
-#else
-  const int testsize = 100000;
-  const int testsize2 = testsize/20;
-  int *arr=NULL;
-  struct { int   key;        int value; }  *intmap  = NULL;
-  struct { char *key;        int value; }  *strmap  = NULL, s;
-  struct { stbds_struct key; int value; }  *map     = NULL;
-  stbds_struct                             *map2    = NULL;
-  stbds_struct2                            *map3    = NULL;
-  stbds_string_arena                        sa      = { 0 };
-  int key3[2] = { 1,2 };
-  ptrdiff_t temp;
-
-  int i,j;
-
-  STBDS_ASSERT(arrlen(arr)==0);
-  for (i=0; i < 20000; i += 50) {
-    for (j=0; j < i; ++j)
-      arrpush(arr,j);
-    arrfree(arr);
-  }
-
-  for (i=0; i < 4; ++i) {
-    arrpush(arr,1); arrpush(arr,2); arrpush(arr,3); arrpush(arr,4);
-    arrdel(arr,i);
-    arrfree(arr);
-    arrpush(arr,1); arrpush(arr,2); arrpush(arr,3); arrpush(arr,4);
-    arrdelswap(arr,i);
-    arrfree(arr);
-  }
-
-  for (i=0; i < 5; ++i) {
-    arrpush(arr,1); arrpush(arr,2); arrpush(arr,3); arrpush(arr,4);
-    stbds_arrins(arr,i,5);
-    STBDS_ASSERT(arr[i] == 5);
-    if (i < 4)
-      STBDS_ASSERT(arr[4] == 4);
-    arrfree(arr);
-  }
-
-  i = 1;
-  STBDS_ASSERT(hmgeti(intmap,i) == -1);
-  hmdefault(intmap, -2);
-  STBDS_ASSERT(hmgeti(intmap, i) == -1);
-  STBDS_ASSERT(hmget (intmap, i) == -2);
-  for (i=0; i < testsize; i+=2)
-    hmput(intmap, i, i*5);
-  for (i=0; i < testsize; i+=1) {
-    if (i & 1) STBDS_ASSERT(hmget(intmap, i) == -2 );
-    else       STBDS_ASSERT(hmget(intmap, i) == i*5);
-    if (i & 1) STBDS_ASSERT(hmget_ts(intmap, i, temp) == -2 );
-    else       STBDS_ASSERT(hmget_ts(intmap, i, temp) == i*5);
-  }
-  for (i=0; i < testsize; i+=2)
-    hmput(intmap, i, i*3);
-  for (i=0; i < testsize; i+=1)
-    if (i & 1) STBDS_ASSERT(hmget(intmap, i) == -2 );
-    else       STBDS_ASSERT(hmget(intmap, i) == i*3);
-  for (i=2; i < testsize; i+=4)
-    hmdel(intmap, i); // delete half the entries
-  for (i=0; i < testsize; i+=1)
-    if (i & 3) STBDS_ASSERT(hmget(intmap, i) == -2 );
-    else       STBDS_ASSERT(hmget(intmap, i) == i*3);
-  for (i=0; i < testsize; i+=1)
-    hmdel(intmap, i); // delete the rest of the entries
-  for (i=0; i < testsize; i+=1)
-    STBDS_ASSERT(hmget(intmap, i) == -2 );
-  hmfree(intmap);
-  for (i=0; i < testsize; i+=2)
-    hmput(intmap, i, i*3);
-  hmfree(intmap);
-
-  #if defined(__clang__) || defined(__GNUC__)
-  #ifndef __cplusplus
-  intmap = NULL;
-  hmput(intmap, 15, 7);
-  hmput(intmap, 11, 3);
-  hmput(intmap,  9, 5);
-  STBDS_ASSERT(hmget(intmap, 9) == 5);
-  STBDS_ASSERT(hmget(intmap, 11) == 3);
-  STBDS_ASSERT(hmget(intmap, 15) == 7);
-  #endif
-  #endif
-
-  for (i=0; i < testsize; ++i)
-    stralloc(&sa, strkey(i));
-  strreset(&sa);
-
-  {
-    s.key = "a", s.value = 1;
-    shputs(strmap, s);
-    STBDS_ASSERT(*strmap[0].key == 'a');
-    STBDS_ASSERT(strmap[0].key == s.key);
-    STBDS_ASSERT(strmap[0].value == s.value);
-    shfree(strmap);
-  }
-
-  {
-    s.key = "a", s.value = 1;
-    sh_new_strdup(strmap);
-    shputs(strmap, s);
-    STBDS_ASSERT(*strmap[0].key == 'a');
-    STBDS_ASSERT(strmap[0].key != s.key);
-    STBDS_ASSERT(strmap[0].value == s.value);
-    shfree(strmap);
-  }
-
-  {
-    s.key = "a", s.value = 1;
-    sh_new_arena(strmap);
-    shputs(strmap, s);
-    STBDS_ASSERT(*strmap[0].key == 'a');
-    STBDS_ASSERT(strmap[0].key != s.key);
-    STBDS_ASSERT(strmap[0].value == s.value);
-    shfree(strmap);
-  }
-
-  for (j=0; j < 2; ++j) {
-    STBDS_ASSERT(shgeti(strmap,"foo") == -1);
-    if (j == 0)
-      sh_new_strdup(strmap);
-    else
-      sh_new_arena(strmap);
-    STBDS_ASSERT(shgeti(strmap,"foo") == -1);
-    shdefault(strmap, -2);
-    STBDS_ASSERT(shgeti(strmap,"foo") == -1);
-    for (i=0; i < testsize; i+=2)
-      shput(strmap, strkey(i), i*3);
-    for (i=0; i < testsize; i+=1)
-      if (i & 1) STBDS_ASSERT(shget(strmap, strkey(i)) == -2 );
-      else       STBDS_ASSERT(shget(strmap, strkey(i)) == i*3);
-    for (i=2; i < testsize; i+=4)
-      shdel(strmap, strkey(i)); // delete half the entries
-    for (i=0; i < testsize; i+=1)
-      if (i & 3) STBDS_ASSERT(shget(strmap, strkey(i)) == -2 );
-      else       STBDS_ASSERT(shget(strmap, strkey(i)) == i*3);
-    for (i=0; i < testsize; i+=1)
-      shdel(strmap, strkey(i)); // delete the rest of the entries
-    for (i=0; i < testsize; i+=1)
-      STBDS_ASSERT(shget(strmap, strkey(i)) == -2 );
-    shfree(strmap);
-  }
-
-  {
-    struct { char *key; char value; } *hash = NULL;
-    char name[4] = "jen";
-    shput(hash, "bob"   , 'h');
-    shput(hash, "sally" , 'e');
-    shput(hash, "fred"  , 'l');
-    shput(hash, "jen"   , 'x');
-    shput(hash, "doug"  , 'o');
-
-    shput(hash, name    , 'l');
-    shfree(hash);
-  }
-
-  for (i=0; i < testsize; i += 2) {
-    stbds_struct s = { i,i*2,i*3,i*4 };
-    hmput(map, s, i*5);
-  }
-
-  for (i=0; i < testsize; i += 1) {
-    stbds_struct s = { i,i*2,i*3  ,i*4 };
-    stbds_struct t = { i,i*2,i*3+1,i*4 };
-    if (i & 1) STBDS_ASSERT(hmget(map, s) == 0);
-    else       STBDS_ASSERT(hmget(map, s) == i*5);
-    if (i & 1) STBDS_ASSERT(hmget_ts(map, s, temp) == 0);
-    else       STBDS_ASSERT(hmget_ts(map, s, temp) == i*5);
-    //STBDS_ASSERT(hmget(map, t.key) == 0);
-  }
-
-  for (i=0; i < testsize; i += 2) {
-    stbds_struct s = { i,i*2,i*3,i*4 };
-    hmputs(map2, s);
-  }
-  hmfree(map);
-
-  for (i=0; i < testsize; i += 1) {
-    stbds_struct s = { i,i*2,i*3,i*4 };
-    stbds_struct t = { i,i*2,i*3+1,i*4 };
-    if (i & 1) STBDS_ASSERT(hmgets(map2, s.key).d == 0);
-    else       STBDS_ASSERT(hmgets(map2, s.key).d == i*4);
-    //STBDS_ASSERT(hmgetp(map2, t.key) == 0);
-  }
-  hmfree(map2);
-
-  for (i=0; i < testsize; i += 2) {
-    stbds_struct2 s = { { i,i*2 }, i*3,i*4, i*5 };
-    hmputs(map3, s);
-  }
-  for (i=0; i < testsize; i += 1) {
-    stbds_struct2 s = { { i,i*2}, i*3, i*4, i*5 };
-    stbds_struct2 t = { { i,i*2}, i*3+1, i*4, i*5 };
-    if (i & 1) STBDS_ASSERT(hmgets(map3, s.key).d == 0);
-    else       STBDS_ASSERT(hmgets(map3, s.key).d == i*5);
-    //STBDS_ASSERT(hmgetp(map3, t.key) == 0);
-  }
-#endif
-}
-#endif
-
-
-/*
-------------------------------------------------------------------------------
-This software is available under 2 licenses -- choose whichever you prefer.
-------------------------------------------------------------------------------
-ALTERNATIVE A - MIT License
-Copyright (c) 2019 Sean Barrett
-Permission is hereby granted, free of charge, to any person obtaining a copy of
-this software and associated documentation files (the "Software"), to deal in
-the Software without restriction, including without limitation the rights to
-use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
-of the Software, and to permit persons to whom the Software is furnished to do
-so, subject to the following conditions:
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-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 OR COPYRIGHT HOLDERS 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.
-------------------------------------------------------------------------------
-ALTERNATIVE B - Public Domain (www.unlicense.org)
-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.
-------------------------------------------------------------------------------
-*/
-
-// stb_sprintf.h:
-
-#define STB_SPRINTF_IMPLEMENTATION
-#ifdef STB_SPRINTF_IMPLEMENTATION
-
-#define stbsp__uint32 unsigned int
-#define stbsp__int32 signed int
-
-#ifdef _MSC_VER
-#define stbsp__uint64 unsigned __int64
-#define stbsp__int64 signed __int64
-#else
-#define stbsp__uint64 unsigned long long
-#define stbsp__int64 signed long long
-#endif
-#define stbsp__uint16 unsigned short
-
-#ifndef stbsp__uintptr
-#if defined(__ppc64__) || defined(__powerpc64__) || defined(__aarch64__) || defined(_M_X64) || defined(__x86_64__) || defined(__x86_64) || defined(__s390x__)
-#define stbsp__uintptr stbsp__uint64
-#else
-#define stbsp__uintptr stbsp__uint32
-#endif
-#endif
-
-#ifndef STB_SPRINTF_MSVC_MODE // used for MSVC2013 and earlier (MSVC2015 matches GCC)
-#if defined(_MSC_VER) && (_MSC_VER < 1900)
-#define STB_SPRINTF_MSVC_MODE
-#endif
-#endif
-
-#ifdef STB_SPRINTF_NOUNALIGNED // define this before inclusion to force stbsp_sprintf to always use aligned accesses
-#define STBSP__UNALIGNED(code)
-#else
-#define STBSP__UNALIGNED(code) code
-#endif
-
-#ifndef STB_SPRINTF_NOFLOAT
-// internal float utility functions
-static stbsp__int32 stbsp__real_to_str(char const **start, stbsp__uint32 *len, char *out, stbsp__int32 *decimal_pos, double value, stbsp__uint32 frac_digits);
-static stbsp__int32 stbsp__real_to_parts(stbsp__int64 *bits, stbsp__int32 *expo, double value);
-#define STBSP__SPECIAL 0x7000
-#endif
-
-static char stbsp__period = '.';
-static char stbsp__comma = ',';
-static struct
-{
-   short temp; // force next field to be 2-byte aligned
-   char pair[201];
-} stbsp__digitpair =
-{
-  0,
-   "00010203040506070809101112131415161718192021222324"
-   "25262728293031323334353637383940414243444546474849"
-   "50515253545556575859606162636465666768697071727374"
-   "75767778798081828384858687888990919293949596979899"
-};
-
-STBSP__PUBLICDEF void STB_SPRINTF_DECORATE(set_separators)(char pcomma, char pperiod)
-{
-   stbsp__period = pperiod;
-   stbsp__comma = pcomma;
-}
-
-#define STBSP__LEFTJUST 1
-#define STBSP__LEADINGPLUS 2
-#define STBSP__LEADINGSPACE 4
-#define STBSP__LEADING_0X 8
-#define STBSP__LEADINGZERO 16
-#define STBSP__INTMAX 32
-#define STBSP__TRIPLET_COMMA 64
-#define STBSP__NEGATIVE 128
-#define STBSP__METRIC_SUFFIX 256
-#define STBSP__HALFWIDTH 512
-#define STBSP__METRIC_NOSPACE 1024
-#define STBSP__METRIC_1024 2048
-#define STBSP__METRIC_JEDEC 4096
-
-static void stbsp__lead_sign(stbsp__uint32 fl, char *sign)
-{
-   sign[0] = 0;
-   if (fl & STBSP__NEGATIVE) {
-      sign[0] = 1;
-      sign[1] = '-';
-   } else if (fl & STBSP__LEADINGSPACE) {
-      sign[0] = 1;
-      sign[1] = ' ';
-   } else if (fl & STBSP__LEADINGPLUS) {
-      sign[0] = 1;
-      sign[1] = '+';
-   }
-}
-
-static STBSP__ASAN stbsp__uint32 stbsp__strlen_limited(char const *s, stbsp__uint32 limit)
-{
-   char const * sn = s;
-
-   // get up to 4-byte alignment
-   for (;;) {
-      if (((stbsp__uintptr)sn & 3) == 0)
-         break;
-
-      if (!limit || *sn == 0)
-         return (stbsp__uint32)(sn - s);
-
-      ++sn;
-      --limit;
-   }
-
-   // scan over 4 bytes at a time to find terminating 0
-   // this will intentionally scan up to 3 bytes past the end of buffers,
-   // but becase it works 4B aligned, it will never cross page boundaries
-   // (hence the STBSP__ASAN markup; the over-read here is intentional
-   // and harmless)
-   while (limit >= 4) {
-      stbsp__uint32 v = *(stbsp__uint32 *)sn;
-      // bit hack to find if there's a 0 byte in there
-      if ((v - 0x01010101) & (~v) & 0x80808080UL)
-         break;
-
-      sn += 4;
-      limit -= 4;
-   }
-
-   // handle the last few characters to find actual size
-   while (limit && *sn) {
-      ++sn;
-      --limit;
-   }
-
-   return (stbsp__uint32)(sn - s);
-}
-
-STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(vsprintfcb)(STBSP_SPRINTFCB *callback, void *user, char *buf, char const *fmt, va_list va)
-{
-   static char hex[] = "0123456789abcdefxp";
-   static char hexu[] = "0123456789ABCDEFXP";
-   char *bf;
-   char const *f;
-   int tlen = 0;
-
-   bf = buf;
-   f = fmt;
-   for (;;) {
-      stbsp__int32 fw, pr, tz;
-      stbsp__uint32 fl;
-
-      // macros for the callback buffer stuff
-      #define stbsp__chk_cb_bufL(bytes)                        \
-         {                                                     \
-            int len = (int)(bf - buf);                         \
-            if ((len + (bytes)) >= STB_SPRINTF_MIN) {          \
-               tlen += len;                                    \
-               if (0 == (bf = buf = callback(buf, user, len))) \
-                  goto done;                                   \
-            }                                                  \
-         }
-      #define stbsp__chk_cb_buf(bytes)    \
-         {                                \
-            if (callback) {               \
-               stbsp__chk_cb_bufL(bytes); \
-            }                             \
-         }
-      #define stbsp__flush_cb()                      \
-         {                                           \
-            stbsp__chk_cb_bufL(STB_SPRINTF_MIN - 1); \
-         } // flush if there is even one byte in the buffer
-      #define stbsp__cb_buf_clamp(cl, v)                \
-         cl = v;                                        \
-         if (callback) {                                \
-            int lg = STB_SPRINTF_MIN - (int)(bf - buf); \
-            if (cl > lg)                                \
-               cl = lg;                                 \
-         }
-
-      // fast copy everything up to the next % (or end of string)
-      for (;;) {
-         while (((stbsp__uintptr)f) & 3) {
-         schk1:
-            if (f[0] == '%')
-               goto scandd;
-         schk2:
-            if (f[0] == 0)
-               goto endfmt;
-            stbsp__chk_cb_buf(1);
-            *bf++ = f[0];
-            ++f;
-         }
-         for (;;) {
-            // Check if the next 4 bytes contain %(0x25) or end of string.
-            // Using the 'hasless' trick:
-            // https://graphics.stanford.edu/~seander/bithacks.html#HasLessInWord
-            stbsp__uint32 v, c;
-            v = *(stbsp__uint32 *)f;
-            c = (~v) & 0x80808080;
-            if (((v ^ 0x25252525) - 0x01010101) & c)
-               goto schk1;
-            if ((v - 0x01010101) & c)
-               goto schk2;
-            if (callback)
-               if ((STB_SPRINTF_MIN - (int)(bf - buf)) < 4)
-                  goto schk1;
-            #ifdef STB_SPRINTF_NOUNALIGNED
-                if(((stbsp__uintptr)bf) & 3) {
-                    bf[0] = f[0];
-                    bf[1] = f[1];
-                    bf[2] = f[2];
-                    bf[3] = f[3];
-                } else
-            #endif
-            {
-                *(stbsp__uint32 *)bf = v;
-            }
-            bf += 4;
-            f += 4;
-         }
-      }
-   scandd:
-
-      ++f;
-
-      // ok, we have a percent, read the modifiers first
-      fw = 0;
-      pr = -1;
-      fl = 0;
-      tz = 0;
-
-      // flags
-      for (;;) {
-         switch (f[0]) {
-         // if we have left justify
-         case '-':
-            fl |= STBSP__LEFTJUST;
-            ++f;
-            continue;
-         // if we have leading plus
-         case '+':
-            fl |= STBSP__LEADINGPLUS;
-            ++f;
-            continue;
-         // if we have leading space
-         case ' ':
-            fl |= STBSP__LEADINGSPACE;
-            ++f;
-            continue;
-         // if we have leading 0x
-         case '#':
-            fl |= STBSP__LEADING_0X;
-            ++f;
-            continue;
-         // if we have thousand commas
-         case '\'':
-            fl |= STBSP__TRIPLET_COMMA;
-            ++f;
-            continue;
-         // if we have kilo marker (none->kilo->kibi->jedec)
-         case '$':
-            if (fl & STBSP__METRIC_SUFFIX) {
-               if (fl & STBSP__METRIC_1024) {
-                  fl |= STBSP__METRIC_JEDEC;
-               } else {
-                  fl |= STBSP__METRIC_1024;
-               }
-            } else {
-               fl |= STBSP__METRIC_SUFFIX;
-            }
-            ++f;
-            continue;
-         // if we don't want space between metric suffix and number
-         case '_':
-            fl |= STBSP__METRIC_NOSPACE;
-            ++f;
-            continue;
-         // if we have leading zero
-         case '0':
-            fl |= STBSP__LEADINGZERO;
-            ++f;
-            goto flags_done;
-         default: goto flags_done;
-         }
-      }
-   flags_done:
-
-      // get the field width
-      if (f[0] == '*') {
-         fw = va_arg(va, stbsp__uint32);
-         ++f;
-      } else {
-         while ((f[0] >= '0') && (f[0] <= '9')) {
-            fw = fw * 10 + f[0] - '0';
-            f++;
-         }
-      }
-      // get the precision
-      if (f[0] == '.') {
-         ++f;
-         if (f[0] == '*') {
-            pr = va_arg(va, stbsp__uint32);
-            ++f;
-         } else {
-            pr = 0;
-            while ((f[0] >= '0') && (f[0] <= '9')) {
-               pr = pr * 10 + f[0] - '0';
-               f++;
-            }
-         }
-      }
-
-      // handle integer size overrides
-      switch (f[0]) {
-      // are we halfwidth?
-      case 'h':
-         fl |= STBSP__HALFWIDTH;
-         ++f;
-         if (f[0] == 'h')
-            ++f;  // QUARTERWIDTH
-         break;
-      // are we 64-bit (unix style)
-      case 'l':
-         fl |= ((sizeof(long) == 8) ? STBSP__INTMAX : 0);
-         ++f;
-         if (f[0] == 'l') {
-            fl |= STBSP__INTMAX;
-            ++f;
-         }
-         break;
-      // are we 64-bit on intmax? (c99)
-      case 'j':
-         fl |= (sizeof(size_t) == 8) ? STBSP__INTMAX : 0;
-         ++f;
-         break;
-      // are we 64-bit on size_t or ptrdiff_t? (c99)
-      case 'z':
-         fl |= (sizeof(ptrdiff_t) == 8) ? STBSP__INTMAX : 0;
-         ++f;
-         break;
-      case 't':
-         fl |= (sizeof(ptrdiff_t) == 8) ? STBSP__INTMAX : 0;
-         ++f;
-         break;
-      // are we 64-bit (msft style)
-      case 'I':
-         if ((f[1] == '6') && (f[2] == '4')) {
-            fl |= STBSP__INTMAX;
-            f += 3;
-         } else if ((f[1] == '3') && (f[2] == '2')) {
-            f += 3;
-         } else {
-            fl |= ((sizeof(void *) == 8) ? STBSP__INTMAX : 0);
-            ++f;
-         }
-         break;
-      default: break;
-      }
-
-      // handle each replacement
-      switch (f[0]) {
-         #define STBSP__NUMSZ 512 // big enough for e308 (with commas) or e-307
-         char num[STBSP__NUMSZ];
-         char lead[8];
-         char tail[8];
-         char *s;
-         char const *h;
-         stbsp__uint32 l, n, cs;
-         stbsp__uint64 n64;
-#ifndef STB_SPRINTF_NOFLOAT
-         double fv;
-#endif
-         stbsp__int32 dp;
-         char const *sn;
-
-      case 's':
-         // get the string
-         s = va_arg(va, char *);
-         if (s == 0)
-            s = (char *)"null";
-         // get the length, limited to desired precision
-         // always limit to ~0u chars since our counts are 32b
-         l = stbsp__strlen_limited(s, (pr >= 0) ? pr : ~0u);
-         lead[0] = 0;
-         tail[0] = 0;
-         pr = 0;
-         dp = 0;
-         cs = 0;
-         // copy the string in
-         goto scopy;
-
-      case 'c': // char
-         // get the character
-         s = num + STBSP__NUMSZ - 1;
-         *s = (char)va_arg(va, int);
-         l = 1;
-         lead[0] = 0;
-         tail[0] = 0;
-         pr = 0;
-         dp = 0;
-         cs = 0;
-         goto scopy;
-
-      case 'n': // weird write-bytes specifier
-      {
-         int *d = va_arg(va, int *);
-         *d = tlen + (int)(bf - buf);
-      } break;
-
-#ifdef STB_SPRINTF_NOFLOAT
-      case 'A':              // float
-      case 'a':              // hex float
-      case 'G':              // float
-      case 'g':              // float
-      case 'E':              // float
-      case 'e':              // float
-      case 'f':              // float
-         va_arg(va, double); // eat it
-         s = (char *)"No float";
-         l = 8;
-         lead[0] = 0;
-         tail[0] = 0;
-         pr = 0;
-         cs = 0;
-         STBSP__NOTUSED(dp);
-         goto scopy;
-#else
-      case 'A': // hex float
-      case 'a': // hex float
-         h = (f[0] == 'A') ? hexu : hex;
-         fv = va_arg(va, double);
-         if (pr == -1)
-            pr = 6; // default is 6
-         // read the double into a string
-         if (stbsp__real_to_parts((stbsp__int64 *)&n64, &dp, fv))
-            fl |= STBSP__NEGATIVE;
-
-         s = num + 64;
-
-         stbsp__lead_sign(fl, lead);
-
-         if (dp == -1023)
-            dp = (n64) ? -1022 : 0;
-         else
-            n64 |= (((stbsp__uint64)1) << 52);
-         n64 <<= (64 - 56);
-         if (pr < 15)
-            n64 += ((((stbsp__uint64)8) << 56) >> (pr * 4));
-// add leading chars
-
-#ifdef STB_SPRINTF_MSVC_MODE
-         *s++ = '0';
-         *s++ = 'x';
-#else
-         lead[1 + lead[0]] = '0';
-         lead[2 + lead[0]] = 'x';
-         lead[0] += 2;
-#endif
-         *s++ = h[(n64 >> 60) & 15];
-         n64 <<= 4;
-         if (pr)
-            *s++ = stbsp__period;
-         sn = s;
-
-         // print the bits
-         n = pr;
-         if (n > 13)
-            n = 13;
-         if (pr > (stbsp__int32)n)
-            tz = pr - n;
-         pr = 0;
-         while (n--) {
-            *s++ = h[(n64 >> 60) & 15];
-            n64 <<= 4;
-         }
-
-         // print the expo
-         tail[1] = h[17];
-         if (dp < 0) {
-            tail[2] = '-';
-            dp = -dp;
-         } else
-            tail[2] = '+';
-         n = (dp >= 1000) ? 6 : ((dp >= 100) ? 5 : ((dp >= 10) ? 4 : 3));
-         tail[0] = (char)n;
-         for (;;) {
-            tail[n] = '0' + dp % 10;
-            if (n <= 3)
-               break;
-            --n;
-            dp /= 10;
-         }
-
-         dp = (int)(s - sn);
-         l = (int)(s - (num + 64));
-         s = num + 64;
-         cs = 1 + (3 << 24);
-         goto scopy;
-
-      case 'G': // float
-      case 'g': // float
-         h = (f[0] == 'G') ? hexu : hex;
-         fv = va_arg(va, double);
-         if (pr == -1)
-            pr = 6;
-         else if (pr == 0)
-            pr = 1; // default is 6
-         // read the double into a string
-         if (stbsp__real_to_str(&sn, &l, num, &dp, fv, (pr - 1) | 0x80000000))
-            fl |= STBSP__NEGATIVE;
-
-         // clamp the precision and delete extra zeros after clamp
-         n = pr;
-         if (l > (stbsp__uint32)pr)
-            l = pr;
-         while ((l > 1) && (pr) && (sn[l - 1] == '0')) {
-            --pr;
-            --l;
-         }
-
-         // should we use %e
-         if ((dp <= -4) || (dp > (stbsp__int32)n)) {
-            if (pr > (stbsp__int32)l)
-               pr = l - 1;
-            else if (pr)
-               --pr; // when using %e, there is one digit before the decimal
-            goto doexpfromg;
-         }
-         // this is the insane action to get the pr to match %g semantics for %f
-         if (dp > 0) {
-            pr = (dp < (stbsp__int32)l) ? l - dp : 0;
-         } else {
-            pr = -dp + ((pr > (stbsp__int32)l) ? (stbsp__int32) l : pr);
-         }
-         goto dofloatfromg;
-
-      case 'E': // float
-      case 'e': // float
-         h = (f[0] == 'E') ? hexu : hex;
-         fv = va_arg(va, double);
-         if (pr == -1)
-            pr = 6; // default is 6
-         // read the double into a string
-         if (stbsp__real_to_str(&sn, &l, num, &dp, fv, pr | 0x80000000))
-            fl |= STBSP__NEGATIVE;
-      doexpfromg:
-         tail[0] = 0;
-         stbsp__lead_sign(fl, lead);
-         if (dp == STBSP__SPECIAL) {
-            s = (char *)sn;
-            cs = 0;
-            pr = 0;
-            goto scopy;
-         }
-         s = num + 64;
-         // handle leading chars
-         *s++ = sn[0];
-
-         if (pr)
-            *s++ = stbsp__period;
-
-         // handle after decimal
-         if ((l - 1) > (stbsp__uint32)pr)
-            l = pr + 1;
-         for (n = 1; n < l; n++)
-            *s++ = sn[n];
-         // trailing zeros
-         tz = pr - (l - 1);
-         pr = 0;
-         // dump expo
-         tail[1] = h[0xe];
-         dp -= 1;
-         if (dp < 0) {
-            tail[2] = '-';
-            dp = -dp;
-         } else
-            tail[2] = '+';
-#ifdef STB_SPRINTF_MSVC_MODE
-         n = 5;
-#else
-         n = (dp >= 100) ? 5 : 4;
-#endif
-         tail[0] = (char)n;
-         for (;;) {
-            tail[n] = '0' + dp % 10;
-            if (n <= 3)
-               break;
-            --n;
-            dp /= 10;
-         }
-         cs = 1 + (3 << 24); // how many tens
-         goto flt_lead;
-
-      case 'f': // float
-         fv = va_arg(va, double);
-      doafloat:
-         // do kilos
-         if (fl & STBSP__METRIC_SUFFIX) {
-            double divisor;
-            divisor = 1000.0f;
-            if (fl & STBSP__METRIC_1024)
-               divisor = 1024.0;
-            while (fl < 0x4000000) {
-               if ((fv < divisor) && (fv > -divisor))
-                  break;
-               fv /= divisor;
-               fl += 0x1000000;
-            }
-         }
-         if (pr == -1)
-            pr = 6; // default is 6
-         // read the double into a string
-         if (stbsp__real_to_str(&sn, &l, num, &dp, fv, pr))
-            fl |= STBSP__NEGATIVE;
-      dofloatfromg:
-         tail[0] = 0;
-         stbsp__lead_sign(fl, lead);
-         if (dp == STBSP__SPECIAL) {
-            s = (char *)sn;
-            cs = 0;
-            pr = 0;
-            goto scopy;
-         }
-         s = num + 64;
-
-         // handle the three decimal varieties
-         if (dp <= 0) {
-            stbsp__int32 i;
-            // handle 0.000*000xxxx
-            *s++ = '0';
-            if (pr)
-               *s++ = stbsp__period;
-            n = -dp;
-            if ((stbsp__int32)n > pr)
-               n = pr;
-            i = n;
-            while (i) {
-               if ((((stbsp__uintptr)s) & 3) == 0)
-                  break;
-               *s++ = '0';
-               --i;
-            }
-            while (i >= 4) {
-               *(stbsp__uint32 *)s = 0x30303030;
-               s += 4;
-               i -= 4;
-            }
-            while (i) {
-               *s++ = '0';
-               --i;
-            }
-            if ((stbsp__int32)(l + n) > pr)
-               l = pr - n;
-            i = l;
-            while (i) {
-               *s++ = *sn++;
-               --i;
-            }
-            tz = pr - (n + l);
-            cs = 1 + (3 << 24); // how many tens did we write (for commas below)
-         } else {
-            cs = (fl & STBSP__TRIPLET_COMMA) ? ((600 - (stbsp__uint32)dp) % 3) : 0;
-            if ((stbsp__uint32)dp >= l) {
-               // handle xxxx000*000.0
-               n = 0;
-               for (;;) {
-                  if ((fl & STBSP__TRIPLET_COMMA) && (++cs == 4)) {
-                     cs = 0;
-                     *s++ = stbsp__comma;
-                  } else {
-                     *s++ = sn[n];
-                     ++n;
-                     if (n >= l)
-                        break;
-                  }
-               }
-               if (n < (stbsp__uint32)dp) {
-                  n = dp - n;
-                  if ((fl & STBSP__TRIPLET_COMMA) == 0) {
-                     while (n) {
-                        if ((((stbsp__uintptr)s) & 3) == 0)
-                           break;
-                        *s++ = '0';
-                        --n;
-                     }
-                     while (n >= 4) {
-                        *(stbsp__uint32 *)s = 0x30303030;
-                        s += 4;
-                        n -= 4;
-                     }
-                  }
-                  while (n) {
-                     if ((fl & STBSP__TRIPLET_COMMA) && (++cs == 4)) {
-                        cs = 0;
-                        *s++ = stbsp__comma;
-                     } else {
-                        *s++ = '0';
-                        --n;
-                     }
-                  }
-               }
-               cs = (int)(s - (num + 64)) + (3 << 24); // cs is how many tens
-               if (pr) {
-                  *s++ = stbsp__period;
-                  tz = pr;
-               }
-            } else {
-               // handle xxxxx.xxxx000*000
-               n = 0;
-               for (;;) {
-                  if ((fl & STBSP__TRIPLET_COMMA) && (++cs == 4)) {
-                     cs = 0;
-                     *s++ = stbsp__comma;
-                  } else {
-                     *s++ = sn[n];
-                     ++n;
-                     if (n >= (stbsp__uint32)dp)
-                        break;
-                  }
-               }
-               cs = (int)(s - (num + 64)) + (3 << 24); // cs is how many tens
-               if (pr)
-                  *s++ = stbsp__period;
-               if ((l - dp) > (stbsp__uint32)pr)
-                  l = pr + dp;
-               while (n < l) {
-                  *s++ = sn[n];
-                  ++n;
-               }
-               tz = pr - (l - dp);
-            }
-         }
-         pr = 0;
-
-         // handle k,m,g,t
-         if (fl & STBSP__METRIC_SUFFIX) {
-            char idx;
-            idx = 1;
-            if (fl & STBSP__METRIC_NOSPACE)
-               idx = 0;
-            tail[0] = idx;
-            tail[1] = ' ';
-            {
-               if (fl >> 24) { // SI kilo is 'k', JEDEC and SI kibits are 'K'.
-                  if (fl & STBSP__METRIC_1024)
-                     tail[idx + 1] = "_KMGT"[fl >> 24];
-                  else
-                     tail[idx + 1] = "_kMGT"[fl >> 24];
-                  idx++;
-                  // If printing kibits and not in jedec, add the 'i'.
-                  if (fl & STBSP__METRIC_1024 && !(fl & STBSP__METRIC_JEDEC)) {
-                     tail[idx + 1] = 'i';
-                     idx++;
-                  }
-                  tail[0] = idx;
-               }
-            }
-         };
-
-      flt_lead:
-         // get the length that we copied
-         l = (stbsp__uint32)(s - (num + 64));
-         s = num + 64;
-         goto scopy;
-#endif
-
-      case 'B': // upper binary
-      case 'b': // lower binary
-         h = (f[0] == 'B') ? hexu : hex;
-         lead[0] = 0;
-         if (fl & STBSP__LEADING_0X) {
-            lead[0] = 2;
-            lead[1] = '0';
-            lead[2] = h[0xb];
-         }
-         l = (8 << 4) | (1 << 8);
-         goto radixnum;
-
-      case 'o': // octal
-         h = hexu;
-         lead[0] = 0;
-         if (fl & STBSP__LEADING_0X) {
-            lead[0] = 1;
-            lead[1] = '0';
-         }
-         l = (3 << 4) | (3 << 8);
-         goto radixnum;
-
-      case 'p': // pointer
-         fl |= (sizeof(void *) == 8) ? STBSP__INTMAX : 0;
-         pr = sizeof(void *) * 2;
-         fl &= ~STBSP__LEADINGZERO; // 'p' only prints the pointer with zeros
-                                    // fall through - to X
-
-      case 'X': // upper hex
-      case 'x': // lower hex
-         h = (f[0] == 'X') ? hexu : hex;
-         l = (4 << 4) | (4 << 8);
-         lead[0] = 0;
-         if (fl & STBSP__LEADING_0X) {
-            lead[0] = 2;
-            lead[1] = '0';
-            lead[2] = h[16];
-         }
-      radixnum:
-         // get the number
-         if (fl & STBSP__INTMAX)
-            n64 = va_arg(va, stbsp__uint64);
-         else
-            n64 = va_arg(va, stbsp__uint32);
-
-         s = num + STBSP__NUMSZ;
-         dp = 0;
-         // clear tail, and clear leading if value is zero
-         tail[0] = 0;
-         if (n64 == 0) {
-            lead[0] = 0;
-            if (pr == 0) {
-               l = 0;
-               cs = 0;
-               goto scopy;
-            }
-         }
-         // convert to string
-         for (;;) {
-            *--s = h[n64 & ((1 << (l >> 8)) - 1)];
-            n64 >>= (l >> 8);
-            if (!((n64) || ((stbsp__int32)((num + STBSP__NUMSZ) - s) < pr)))
-               break;
-            if (fl & STBSP__TRIPLET_COMMA) {
-               ++l;
-               if ((l & 15) == ((l >> 4) & 15)) {
-                  l &= ~15;
-                  *--s = stbsp__comma;
-               }
-            }
-         };
-         // get the tens and the comma pos
-         cs = (stbsp__uint32)((num + STBSP__NUMSZ) - s) + ((((l >> 4) & 15)) << 24);
-         // get the length that we copied
-         l = (stbsp__uint32)((num + STBSP__NUMSZ) - s);
-         // copy it
-         goto scopy;
-
-      case 'u': // unsigned
-      case 'i':
-      case 'd': // integer
-         // get the integer and abs it
-         if (fl & STBSP__INTMAX) {
-            stbsp__int64 i64 = va_arg(va, stbsp__int64);
-            n64 = (stbsp__uint64)i64;
-            if ((f[0] != 'u') && (i64 < 0)) {
-               n64 = (stbsp__uint64)-i64;
-               fl |= STBSP__NEGATIVE;
-            }
-         } else {
-            stbsp__int32 i = va_arg(va, stbsp__int32);
-            n64 = (stbsp__uint32)i;
-            if ((f[0] != 'u') && (i < 0)) {
-               n64 = (stbsp__uint32)-i;
-               fl |= STBSP__NEGATIVE;
-            }
-         }
-
-#ifndef STB_SPRINTF_NOFLOAT
-         if (fl & STBSP__METRIC_SUFFIX) {
-            if (n64 < 1024)
-               pr = 0;
-            else if (pr == -1)
-               pr = 1;
-            fv = (double)(stbsp__int64)n64;
-            goto doafloat;
-         }
-#endif
-
-         // convert to string
-         s = num + STBSP__NUMSZ;
-         l = 0;
-
-         for (;;) {
-            // do in 32-bit chunks (avoid lots of 64-bit divides even with constant denominators)
-            char *o = s - 8;
-            if (n64 >= 100000000) {
-               n = (stbsp__uint32)(n64 % 100000000);
-               n64 /= 100000000;
-            } else {
-               n = (stbsp__uint32)n64;
-               n64 = 0;
-            }
-            if ((fl & STBSP__TRIPLET_COMMA) == 0) {
-               do {
-                  s -= 2;
-                  *(stbsp__uint16 *)s = *(stbsp__uint16 *)&stbsp__digitpair.pair[(n % 100) * 2];
-                  n /= 100;
-               } while (n);
-            }
-            while (n) {
-               if ((fl & STBSP__TRIPLET_COMMA) && (l++ == 3)) {
-                  l = 0;
-                  *--s = stbsp__comma;
-                  --o;
-               } else {
-                  *--s = (char)(n % 10) + '0';
-                  n /= 10;
-               }
-            }
-            if (n64 == 0) {
-               if ((s[0] == '0') && (s != (num + STBSP__NUMSZ)))
-                  ++s;
-               break;
-            }
-            while (s != o)
-               if ((fl & STBSP__TRIPLET_COMMA) && (l++ == 3)) {
-                  l = 0;
-                  *--s = stbsp__comma;
-                  --o;
-               } else {
-                  *--s = '0';
-               }
-         }
-
-         tail[0] = 0;
-         stbsp__lead_sign(fl, lead);
-
-         // get the length that we copied
-         l = (stbsp__uint32)((num + STBSP__NUMSZ) - s);
-         if (l == 0) {
-            *--s = '0';
-            l = 1;
-         }
-         cs = l + (3 << 24);
-         if (pr < 0)
-            pr = 0;
-
-      scopy:
-         // get fw=leading/trailing space, pr=leading zeros
-         if (pr < (stbsp__int32)l)
-            pr = l;
-         n = pr + lead[0] + tail[0] + tz;
-         if (fw < (stbsp__int32)n)
-            fw = n;
-         fw -= n;
-         pr -= l;
-
-         // handle right justify and leading zeros
-         if ((fl & STBSP__LEFTJUST) == 0) {
-            if (fl & STBSP__LEADINGZERO) // if leading zeros, everything is in pr
-            {
-               pr = (fw > pr) ? fw : pr;
-               fw = 0;
-            } else {
-               fl &= ~STBSP__TRIPLET_COMMA; // if no leading zeros, then no commas
-            }
-         }
-
-         // copy the spaces and/or zeros
-         if (fw + pr) {
-            stbsp__int32 i;
-            stbsp__uint32 c;
-
-            // copy leading spaces (or when doing %8.4d stuff)
-            if ((fl & STBSP__LEFTJUST) == 0)
-               while (fw > 0) {
-                  stbsp__cb_buf_clamp(i, fw);
-                  fw -= i;
-                  while (i) {
-                     if ((((stbsp__uintptr)bf) & 3) == 0)
-                        break;
-                     *bf++ = ' ';
-                     --i;
-                  }
-                  while (i >= 4) {
-                     *(stbsp__uint32 *)bf = 0x20202020;
-                     bf += 4;
-                     i -= 4;
-                  }
-                  while (i) {
-                     *bf++ = ' ';
-                     --i;
-                  }
-                  stbsp__chk_cb_buf(1);
-               }
-
-            // copy leader
-            sn = lead + 1;
-            while (lead[0]) {
-               stbsp__cb_buf_clamp(i, lead[0]);
-               lead[0] -= (char)i;
-               while (i) {
-                  *bf++ = *sn++;
-                  --i;
-               }
-               stbsp__chk_cb_buf(1);
-            }
-
-            // copy leading zeros
-            c = cs >> 24;
-            cs &= 0xffffff;
-            cs = (fl & STBSP__TRIPLET_COMMA) ? ((stbsp__uint32)(c - ((pr + cs) % (c + 1)))) : 0;
-            while (pr > 0) {
-               stbsp__cb_buf_clamp(i, pr);
-               pr -= i;
-               if ((fl & STBSP__TRIPLET_COMMA) == 0) {
-                  while (i) {
-                     if ((((stbsp__uintptr)bf) & 3) == 0)
-                        break;
-                     *bf++ = '0';
-                     --i;
-                  }
-                  while (i >= 4) {
-                     *(stbsp__uint32 *)bf = 0x30303030;
-                     bf += 4;
-                     i -= 4;
-                  }
-               }
-               while (i) {
-                  if ((fl & STBSP__TRIPLET_COMMA) && (cs++ == c)) {
-                     cs = 0;
-                     *bf++ = stbsp__comma;
-                  } else
-                     *bf++ = '0';
-                  --i;
-               }
-               stbsp__chk_cb_buf(1);
-            }
-         }
-
-         // copy leader if there is still one
-         sn = lead + 1;
-         while (lead[0]) {
-            stbsp__int32 i;
-            stbsp__cb_buf_clamp(i, lead[0]);
-            lead[0] -= (char)i;
-            while (i) {
-               *bf++ = *sn++;
-               --i;
-            }
-            stbsp__chk_cb_buf(1);
-         }
-
-         // copy the string
-         n = l;
-         while (n) {
-            stbsp__int32 i;
-            stbsp__cb_buf_clamp(i, n);
-            n -= i;
-            STBSP__UNALIGNED(while (i >= 4) {
-               *(stbsp__uint32 volatile *)bf = *(stbsp__uint32 volatile *)s;
-               bf += 4;
-               s += 4;
-               i -= 4;
-            })
-            while (i) {
-               *bf++ = *s++;
-               --i;
-            }
-            stbsp__chk_cb_buf(1);
-         }
-
-         // copy trailing zeros
-         while (tz) {
-            stbsp__int32 i;
-            stbsp__cb_buf_clamp(i, tz);
-            tz -= i;
-            while (i) {
-               if ((((stbsp__uintptr)bf) & 3) == 0)
-                  break;
-               *bf++ = '0';
-               --i;
-            }
-            while (i >= 4) {
-               *(stbsp__uint32 *)bf = 0x30303030;
-               bf += 4;
-               i -= 4;
-            }
-            while (i) {
-               *bf++ = '0';
-               --i;
-            }
-            stbsp__chk_cb_buf(1);
-         }
-
-         // copy tail if there is one
-         sn = tail + 1;
-         while (tail[0]) {
-            stbsp__int32 i;
-            stbsp__cb_buf_clamp(i, tail[0]);
-            tail[0] -= (char)i;
-            while (i) {
-               *bf++ = *sn++;
-               --i;
-            }
-            stbsp__chk_cb_buf(1);
-         }
-
-         // handle the left justify
-         if (fl & STBSP__LEFTJUST)
-            if (fw > 0) {
-               while (fw) {
-                  stbsp__int32 i;
-                  stbsp__cb_buf_clamp(i, fw);
-                  fw -= i;
-                  while (i) {
-                     if ((((stbsp__uintptr)bf) & 3) == 0)
-                        break;
-                     *bf++ = ' ';
-                     --i;
-                  }
-                  while (i >= 4) {
-                     *(stbsp__uint32 *)bf = 0x20202020;
-                     bf += 4;
-                     i -= 4;
-                  }
-                  while (i--)
-                     *bf++ = ' ';
-                  stbsp__chk_cb_buf(1);
-               }
-            }
-         break;
-
-      default: // unknown, just copy code
-         s = num + STBSP__NUMSZ - 1;
-         *s = f[0];
-         l = 1;
-         fw = fl = 0;
-         lead[0] = 0;
-         tail[0] = 0;
-         pr = 0;
-         dp = 0;
-         cs = 0;
-         goto scopy;
-      }
-      ++f;
-   }
-endfmt:
-
-   if (!callback)
-      *bf = 0;
-   else
-      stbsp__flush_cb();
-
-done:
-   return tlen + (int)(bf - buf);
-}
-
-// cleanup
-#undef STBSP__LEFTJUST
-#undef STBSP__LEADINGPLUS
-#undef STBSP__LEADINGSPACE
-#undef STBSP__LEADING_0X
-#undef STBSP__LEADINGZERO
-#undef STBSP__INTMAX
-#undef STBSP__TRIPLET_COMMA
-#undef STBSP__NEGATIVE
-#undef STBSP__METRIC_SUFFIX
-#undef STBSP__NUMSZ
-#undef stbsp__chk_cb_bufL
-#undef stbsp__chk_cb_buf
-#undef stbsp__flush_cb
-#undef stbsp__cb_buf_clamp
-
-// ============================================================================
-//   wrapper functions
-
-STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(sprintf)(char *buf, char const *fmt, ...)
-{
-   int result;
-   va_list va;
-   va_start(va, fmt);
-   result = STB_SPRINTF_DECORATE(vsprintfcb)(0, 0, buf, fmt, va);
-   va_end(va);
-   return result;
-}
-
-typedef struct stbsp__context {
-   char *buf;
-   int count;
-   int length;
-   char tmp[STB_SPRINTF_MIN];
-} stbsp__context;
-
-static char *stbsp__clamp_callback(const char *buf, void *user, int len)
-{
-   stbsp__context *c = (stbsp__context *)user;
-   c->length += len;
-
-   if (len > c->count)
-      len = c->count;
-
-   if (len) {
-      if (buf != c->buf) {
-         const char *s, *se;
-         char *d;
-         d = c->buf;
-         s = buf;
-         se = buf + len;
-         do {
-            *d++ = *s++;
-         } while (s < se);
-      }
-      c->buf += len;
-      c->count -= len;
-   }
-
-   if (c->count <= 0)
-      return c->tmp;
-   return (c->count >= STB_SPRINTF_MIN) ? c->buf : c->tmp; // go direct into buffer if you can
-}
-
-static char * stbsp__count_clamp_callback( const char * buf, void * user, int len )
-{
-   stbsp__context * c = (stbsp__context*)user;
-   (void) sizeof(buf);
-
-   c->length += len;
-   return c->tmp; // go direct into buffer if you can
-}
-
-STBSP__PUBLICDEF int STB_SPRINTF_DECORATE( vsnprintf )( char * buf, int count, char const * fmt, va_list va )
-{
-   stbsp__context c;
-
-   if ( (count == 0) && !buf )
-   {
-      c.length = 0;
-
-      STB_SPRINTF_DECORATE( vsprintfcb )( stbsp__count_clamp_callback, &c, c.tmp, fmt, va );
-   }
-   else
-   {
-      int l;
-
-      c.buf = buf;
-      c.count = count;
-      c.length = 0;
-
-      STB_SPRINTF_DECORATE( vsprintfcb )( stbsp__clamp_callback, &c, stbsp__clamp_callback(0,&c,0), fmt, va );
-
-      // zero-terminate
-      l = (int)( c.buf - buf );
-      if ( l >= count ) // should never be greater, only equal (or less) than count
-         l = count - 1;
-      buf[l] = 0;
-   }
-
-   return c.length;
-}
-
-STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(snprintf)(char *buf, int count, char const *fmt, ...)
-{
-   int result;
-   va_list va;
-   va_start(va, fmt);
-
-   result = STB_SPRINTF_DECORATE(vsnprintf)(buf, count, fmt, va);
-   va_end(va);
-
-   return result;
-}
-
-STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(vsprintf)(char *buf, char const *fmt, va_list va)
-{
-   return STB_SPRINTF_DECORATE(vsprintfcb)(0, 0, buf, fmt, va);
-}
-
-// =======================================================================
-//   low level float utility functions
-
-#ifndef STB_SPRINTF_NOFLOAT
-
-// copies d to bits w/ strict aliasing (this compiles to nothing on /Ox)
-#define STBSP__COPYFP(dest, src)                   \
-   {                                               \
-      int cn;                                      \
-      for (cn = 0; cn < 8; cn++)                   \
-         ((char *)&dest)[cn] = ((char *)&src)[cn]; \
-   }
-
-// get float info
-static stbsp__int32 stbsp__real_to_parts(stbsp__int64 *bits, stbsp__int32 *expo, double value)
-{
-   double d;
-   stbsp__int64 b = 0;
-
-   // load value and round at the frac_digits
-   d = value;
-
-   STBSP__COPYFP(b, d);
-
-   *bits = b & ((((stbsp__uint64)1) << 52) - 1);
-   *expo = (stbsp__int32)(((b >> 52) & 2047) - 1023);
-
-   return (stbsp__int32)((stbsp__uint64) b >> 63);
-}
-
-static double const stbsp__bot[23] = {
-   1e+000, 1e+001, 1e+002, 1e+003, 1e+004, 1e+005, 1e+006, 1e+007, 1e+008, 1e+009, 1e+010, 1e+011,
-   1e+012, 1e+013, 1e+014, 1e+015, 1e+016, 1e+017, 1e+018, 1e+019, 1e+020, 1e+021, 1e+022
-};
-static double const stbsp__negbot[22] = {
-   1e-001, 1e-002, 1e-003, 1e-004, 1e-005, 1e-006, 1e-007, 1e-008, 1e-009, 1e-010, 1e-011,
-   1e-012, 1e-013, 1e-014, 1e-015, 1e-016, 1e-017, 1e-018, 1e-019, 1e-020, 1e-021, 1e-022
-};
-static double const stbsp__negboterr[22] = {
-   -5.551115123125783e-018,  -2.0816681711721684e-019, -2.0816681711721686e-020, -4.7921736023859299e-021, -8.1803053914031305e-022, 4.5251888174113741e-023,
-   4.5251888174113739e-024,  -2.0922560830128471e-025, -6.2281591457779853e-026, -3.6432197315497743e-027, 6.0503030718060191e-028,  2.0113352370744385e-029,
-   -3.0373745563400371e-030, 1.1806906454401013e-032,  -7.7705399876661076e-032, 2.0902213275965398e-033,  -7.1542424054621921e-034, -7.1542424054621926e-035,
-   2.4754073164739869e-036,  5.4846728545790429e-037,  9.2462547772103625e-038,  -4.8596774326570872e-039
-};
-static double const stbsp__top[13] = {
-   1e+023, 1e+046, 1e+069, 1e+092, 1e+115, 1e+138, 1e+161, 1e+184, 1e+207, 1e+230, 1e+253, 1e+276, 1e+299
-};
-static double const stbsp__negtop[13] = {
-   1e-023, 1e-046, 1e-069, 1e-092, 1e-115, 1e-138, 1e-161, 1e-184, 1e-207, 1e-230, 1e-253, 1e-276, 1e-299
-};
-static double const stbsp__toperr[13] = {
-   8388608,
-   6.8601809640529717e+028,
-   -7.253143638152921e+052,
-   -4.3377296974619174e+075,
-   -1.5559416129466825e+098,
-   -3.2841562489204913e+121,
-   -3.7745893248228135e+144,
-   -1.7356668416969134e+167,
-   -3.8893577551088374e+190,
-   -9.9566444326005119e+213,
-   6.3641293062232429e+236,
-   -5.2069140800249813e+259,
-   -5.2504760255204387e+282
-};
-static double const stbsp__negtoperr[13] = {
-   3.9565301985100693e-040,  -2.299904345391321e-063,  3.6506201437945798e-086,  1.1875228833981544e-109,
-   -5.0644902316928607e-132, -6.7156837247865426e-155, -2.812077463003139e-178,  -5.7778912386589953e-201,
-   7.4997100559334532e-224,  -4.6439668915134491e-247, -6.3691100762962136e-270, -9.436808465446358e-293,
-   8.0970921678014997e-317
-};
-
-#if defined(_MSC_VER) && (_MSC_VER <= 1200)
-static stbsp__uint64 const stbsp__powten[20] = {
-   1,
-   10,
-   100,
-   1000,
-   10000,
-   100000,
-   1000000,
-   10000000,
-   100000000,
-   1000000000,
-   10000000000,
-   100000000000,
-   1000000000000,
-   10000000000000,
-   100000000000000,
-   1000000000000000,
-   10000000000000000,
-   100000000000000000,
-   1000000000000000000,
-   10000000000000000000U
-};
-#define stbsp__tento19th ((stbsp__uint64)1000000000000000000)
-#else
-static stbsp__uint64 const stbsp__powten[20] = {
-   1,
-   10,
-   100,
-   1000,
-   10000,
-   100000,
-   1000000,
-   10000000,
-   100000000,
-   1000000000,
-   10000000000ULL,
-   100000000000ULL,
-   1000000000000ULL,
-   10000000000000ULL,
-   100000000000000ULL,
-   1000000000000000ULL,
-   10000000000000000ULL,
-   100000000000000000ULL,
-   1000000000000000000ULL,
-   10000000000000000000ULL
-};
-#define stbsp__tento19th (1000000000000000000ULL)
-#endif
-
-#define stbsp__ddmulthi(oh, ol, xh, yh)                            \
-   {                                                               \
-      double ahi = 0, alo, bhi = 0, blo;                           \
-      stbsp__int64 bt;                                             \
-      oh = xh * yh;                                                \
-      STBSP__COPYFP(bt, xh);                                       \
-      bt &= ((~(stbsp__uint64)0) << 27);                           \
-      STBSP__COPYFP(ahi, bt);                                      \
-      alo = xh - ahi;                                              \
-      STBSP__COPYFP(bt, yh);                                       \
-      bt &= ((~(stbsp__uint64)0) << 27);                           \
-      STBSP__COPYFP(bhi, bt);                                      \
-      blo = yh - bhi;                                              \
-      ol = ((ahi * bhi - oh) + ahi * blo + alo * bhi) + alo * blo; \
-   }
-
-#define stbsp__ddtoS64(ob, xh, xl)          \
-   {                                        \
-      double ahi = 0, alo, vh, t;           \
-      ob = (stbsp__int64)xh;                \
-      vh = (double)ob;                      \
-      ahi = (xh - vh);                      \
-      t = (ahi - xh);                       \
-      alo = (xh - (ahi - t)) - (vh + t);    \
-      ob += (stbsp__int64)(ahi + alo + xl); \
-   }
-
-#define stbsp__ddrenorm(oh, ol) \
-   {                            \
-      double s;                 \
-      s = oh + ol;              \
-      ol = ol - (s - oh);       \
-      oh = s;                   \
-   }
-
-#define stbsp__ddmultlo(oh, ol, xh, xl, yh, yl) ol = ol + (xh * yl + xl * yh);
-
-#define stbsp__ddmultlos(oh, ol, xh, yl) ol = ol + (xh * yl);
-
-static void stbsp__raise_to_power10(double *ohi, double *olo, double d, stbsp__int32 power) // power can be -323 to +350
-{
-   double ph, pl;
-   if ((power >= 0) && (power <= 22)) {
-      stbsp__ddmulthi(ph, pl, d, stbsp__bot[power]);
-   } else {
-      stbsp__int32 e, et, eb;
-      double p2h, p2l;
-
-      e = power;
-      if (power < 0)
-         e = -e;
-      et = (e * 0x2c9) >> 14; /* %23 */
-      if (et > 13)
-         et = 13;
-      eb = e - (et * 23);
-
-      ph = d;
-      pl = 0.0;
-      if (power < 0) {
-         if (eb) {
-            --eb;
-            stbsp__ddmulthi(ph, pl, d, stbsp__negbot[eb]);
-            stbsp__ddmultlos(ph, pl, d, stbsp__negboterr[eb]);
-         }
-         if (et) {
-            stbsp__ddrenorm(ph, pl);
-            --et;
-            stbsp__ddmulthi(p2h, p2l, ph, stbsp__negtop[et]);
-            stbsp__ddmultlo(p2h, p2l, ph, pl, stbsp__negtop[et], stbsp__negtoperr[et]);
-            ph = p2h;
-            pl = p2l;
-         }
-      } else {
-         if (eb) {
-            e = eb;
-            if (eb > 22)
-               eb = 22;
-            e -= eb;
-            stbsp__ddmulthi(ph, pl, d, stbsp__bot[eb]);
-            if (e) {
-               stbsp__ddrenorm(ph, pl);
-               stbsp__ddmulthi(p2h, p2l, ph, stbsp__bot[e]);
-               stbsp__ddmultlos(p2h, p2l, stbsp__bot[e], pl);
-               ph = p2h;
-               pl = p2l;
-            }
-         }
-         if (et) {
-            stbsp__ddrenorm(ph, pl);
-            --et;
-            stbsp__ddmulthi(p2h, p2l, ph, stbsp__top[et]);
-            stbsp__ddmultlo(p2h, p2l, ph, pl, stbsp__top[et], stbsp__toperr[et]);
-            ph = p2h;
-            pl = p2l;
-         }
-      }
-   }
-   stbsp__ddrenorm(ph, pl);
-   *ohi = ph;
-   *olo = pl;
-}
-
-// given a float value, returns the significant bits in bits, and the position of the
-//   decimal point in decimal_pos.  +/-INF and NAN are specified by special values
-//   returned in the decimal_pos parameter.
-// frac_digits is absolute normally, but if you want from first significant digits (got %g and %e), or in 0x80000000
-static stbsp__int32 stbsp__real_to_str(char const **start, stbsp__uint32 *len, char *out, stbsp__int32 *decimal_pos, double value, stbsp__uint32 frac_digits)
-{
-   double d;
-   stbsp__int64 bits = 0;
-   stbsp__int32 expo, e, ng, tens;
-
-   d = value;
-   STBSP__COPYFP(bits, d);
-   expo = (stbsp__int32)((bits >> 52) & 2047);
-   ng = (stbsp__int32)((stbsp__uint64) bits >> 63);
-   if (ng)
-      d = -d;
-
-   if (expo == 2047) // is nan or inf?
-   {
-      *start = (bits & ((((stbsp__uint64)1) << 52) - 1)) ? "NaN" : "Inf";
-      *decimal_pos = STBSP__SPECIAL;
-      *len = 3;
-      return ng;
-   }
-
-   if (expo == 0) // is zero or denormal
-   {
-      if (((stbsp__uint64) bits << 1) == 0) // do zero
-      {
-         *decimal_pos = 1;
-         *start = out;
-         out[0] = '0';
-         *len = 1;
-         return ng;
-      }
-      // find the right expo for denormals
-      {
-         stbsp__int64 v = ((stbsp__uint64)1) << 51;
-         while ((bits & v) == 0) {
-            --expo;
-            v >>= 1;
-         }
-      }
-   }
-
-   // find the decimal exponent as well as the decimal bits of the value
-   {
-      double ph, pl;
-
-      // log10 estimate - very specifically tweaked to hit or undershoot by no more than 1 of log10 of all expos 1..2046
-      tens = expo - 1023;
-      tens = (tens < 0) ? ((tens * 617) / 2048) : (((tens * 1233) / 4096) + 1);
-
-      // move the significant bits into position and stick them into an int
-      stbsp__raise_to_power10(&ph, &pl, d, 18 - tens);
-
-      // get full as much precision from double-double as possible
-      stbsp__ddtoS64(bits, ph, pl);
-
-      // check if we undershot
-      if (((stbsp__uint64)bits) >= stbsp__tento19th)
-         ++tens;
-   }
-
-   // now do the rounding in integer land
-   frac_digits = (frac_digits & 0x80000000) ? ((frac_digits & 0x7ffffff) + 1) : (tens + frac_digits);
-   if ((frac_digits < 24)) {
-      stbsp__uint32 dg = 1;
-      if ((stbsp__uint64)bits >= stbsp__powten[9])
-         dg = 10;
-      while ((stbsp__uint64)bits >= stbsp__powten[dg]) {
-         ++dg;
-         if (dg == 20)
-            goto noround;
-      }
-      if (frac_digits < dg) {
-         stbsp__uint64 r;
-         // add 0.5 at the right position and round
-         e = dg - frac_digits;
-         if ((stbsp__uint32)e >= 24)
-            goto noround;
-         r = stbsp__powten[e];
-         bits = bits + (r / 2);
-         if ((stbsp__uint64)bits >= stbsp__powten[dg])
-            ++tens;
-         bits /= r;
-      }
-   noround:;
-   }
-
-   // kill long trailing runs of zeros
-   if (bits) {
-      stbsp__uint32 n;
-      for (;;) {
-         if (bits <= 0xffffffff)
-            break;
-         if (bits % 1000)
-            goto donez;
-         bits /= 1000;
-      }
-      n = (stbsp__uint32)bits;
-      while ((n % 1000) == 0)
-         n /= 1000;
-      bits = n;
-   donez:;
-   }
-
-   // convert to string
-   out += 64;
-   e = 0;
-   for (;;) {
-      stbsp__uint32 n;
-      char *o = out - 8;
-      // do the conversion in chunks of U32s (avoid most 64-bit divides, worth it, constant denomiators be damned)
-      if (bits >= 100000000) {
-         n = (stbsp__uint32)(bits % 100000000);
-         bits /= 100000000;
-      } else {
-         n = (stbsp__uint32)bits;
-         bits = 0;
-      }
-      while (n) {
-         out -= 2;
-         *(stbsp__uint16 *)out = *(stbsp__uint16 *)&stbsp__digitpair.pair[(n % 100) * 2];
-         n /= 100;
-         e += 2;
-      }
-      if (bits == 0) {
-         if ((e) && (out[0] == '0')) {
-            ++out;
-            --e;
-         }
-         break;
-      }
-      while (out != o) {
-         *--out = '0';
-         ++e;
-      }
-   }
-
-   *decimal_pos = tens;
-   *start = out;
-   *len = e;
-   return ng;
-}
-
-#undef stbsp__ddmulthi
-#undef stbsp__ddrenorm
-#undef stbsp__ddmultlo
-#undef stbsp__ddmultlos
-#undef STBSP__SPECIAL
-#undef STBSP__COPYFP
-
-#endif // STB_SPRINTF_NOFLOAT
-
-// clean up
-#undef stbsp__uint16
-#undef stbsp__uint32
-#undef stbsp__int32
-#undef stbsp__uint64
-#undef stbsp__int64
-#undef STBSP__UNALIGNED
-
-#endif // STB_SPRINTF_IMPLEMENTATION
-
-/*
-------------------------------------------------------------------------------
-This software is available under 2 licenses -- choose whichever you prefer.
-------------------------------------------------------------------------------
-ALTERNATIVE A - MIT License
-Copyright (c) 2017 Sean Barrett
-Permission is hereby granted, free of charge, to any person obtaining a copy of
-this software and associated documentation files (the "Software"), to deal in
-the Software without restriction, including without limitation the rights to
-use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
-of the Software, and to permit persons to whom the Software is furnished to do
-so, subject to the following conditions:
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-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 OR COPYRIGHT HOLDERS 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.
-------------------------------------------------------------------------------
-ALTERNATIVE B - Public Domain (www.unlicense.org)
-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.
-------------------------------------------------------------------------------
-*/
-
-// ----------------------------------------------------------------------------------------------------
-
-// My things implementation:
-
-// Winapi
-cstr winapi_get_last_error_str(void) {
-  DWORD error_code = GetLastError();
-  if (!FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, error_code, 0, winapi_error_msg_buff, WINAPI_ERROR_MSG_BUFF_SIZE, NULL)){
-    log_f(LOG_ERROR, "FormatMessage() failed with code: %d", GetLastError());
-    return NULL;
-  }
-  return winapi_error_msg_buff;
-}
-
-cstr winapi_get_current_working_directory(Arena* arena) {
-  if (arena->buff_size < MAX_PATH) {
-    log_error("Arena size should be at least of size MAX_PATH(%u)", MAX_PATH);
-    return NULL;
-  }
-
-  size_t n = GetCurrentDirectoryA((DWORD)arena->buff_size, arena->buff);
-  if (n == 0) {
-    log_error("%s -> %s", __func__, winapi_get_last_error_str());
-    return NULL;
-  }
-  return arena->buff;
-}
-
-bool output_str(cstr text) {
-  return output_strn(text, strlen(text));
-}
-
-bool output_strn(cstr text, size_t text_len) {
-  SECURITY_ATTRIBUTES sa = {0};
-  sa.nLength = sizeof(sa);
-  sa.bInheritHandle = TRUE;
-
-  HANDLE console = CreateFileA("CONOUT$",
-                              GENERIC_READ|GENERIC_WRITE,
-                              FILE_SHARE_WRITE,
-                              &sa,
-                              OPEN_EXISTING,
-                              0,
-                              NULL);
-
-  if (console == INVALID_HANDLE_VALUE) {
-    log_f(LOG_INFO, "Could not get a handle to the active console screen buffer: %s", winapi_get_last_error_str());
-    return 1;
-  }
-
-  if (text_len == 0) return false;
-
-  ASSERT(text_len < WINAPI_OUTPUT_STR_BUFF_SIZE);
-  CHAR_INFO* buff = winapi_output_str_buff;
-
-  CONSOLE_SCREEN_BUFFER_INFO csbi = {0};
-
-  if (!GetConsoleScreenBufferInfo(console, &csbi)) {
-    log_f(LOG_ERROR, "Could not get console screen buffer info: %s", winapi_get_last_error_str());
-    return false;
-  }
-
-  COORD buff_size = {0, 1};
-  COORD new_cursor = {
-    .X = csbi.dwCursorPosition.X,
-    .Y = csbi.dwCursorPosition.Y
-  };
-
-  bool overflown = false;
-
-  char* next_text = NULL;
-
-  for (size_t i = 0; i < text_len; ++i) {
-    if (text[i] == '\n') {
-      overflown = true;
-      next_text = (char*)text+buff_size.X+1;
-      text_len = (text+text_len)-text;
-      break;
-    } else {
-      buff[i].Char.AsciiChar = text[i];
-      buff_size.X++;
-      new_cursor.X++;
-      if (new_cursor.X >= csbi.dwMaximumWindowSize.X) {
-       overflown = true;
-       next_text = (char*)text+buff_size.X;
-       text_len = (text+text_len)-text;
-       break;
-      }
-    }
-
-    buff[i].Attributes = FOREGROUND_RED|FOREGROUND_INTENSITY;
-  }
-
-  COORD write_coord = {0, 0};
-  SMALL_RECT rect = {
-    .Left   = csbi.dwCursorPosition.X,
-    .Top    = csbi.dwCursorPosition.Y,
-    .Right  = new_cursor.X,
-    .Bottom = new_cursor.Y,
-  };
-
-  if(!WriteConsoleOutput(console,
-                        buff,
-                        buff_size,
-                        write_coord,
-                        &rect)) {
-    log_f(LOG_ERROR, "[1] Could not write to console output: [%d] %s", GetLastError(), winapi_get_last_error_str());
-    return false;
-  }
-
-  if (!overflown) {
-    // advance cursor
-    if (!SetConsoleCursorPosition(console, new_cursor)) {
-      log_f(LOG_ERROR, "[1] Could not set new cursor pos: %s", winapi_get_last_error_str());
-      return false;
-    }
-  }
-
-  if (overflown) {
-    new_cursor.X = 0;
-    new_cursor.Y++;
-    // advance cursor
-    if (!SetConsoleCursorPosition(console, new_cursor)) {
-      log_f(LOG_ERROR, "[2] Could not set new cursor pos: %s", winapi_get_last_error_str());
-      return false;
-    }
-    output_strn(next_text, text_len);
-  }
-
-  return true;
-}
-
-void panic_assertion(cstr msg, FILE* file, cstr filename, int line) {
-  fprintf(file, "%s:%d:0: ASSERTION FAILED: %s\n", filename, line, msg);
-  exit(1);
-}
-
-void log_file(Log_type type, FILE* file, cstr fmt, ...) {
-  va_list args;
-  va_start(args, fmt);
-
-  SYSTEMTIME sys_time = {0};
-  GetLocalTime(&sys_time);
-
-  fprintf(file, "[%02d:%02d:%02d] ", sys_time.wHour, sys_time.wMinute, sys_time.wSecond);
-
-  switch (type){
-  case LOG_INFO: fprintf(file, "INFO: "); break;
-  case LOG_ERROR: fprintf(file, "ERROR: "); break;
-  case LOG_WARNING: fprintf(file, "WARNING: "); break;
-  default: ASSERT(0 && "Unreachable");
-  }
-
-  while (*fmt != '\0'){
-    if (*fmt == '%'){
-      fmt++;
-      switch (*fmt){
-      case 's': {
-       const char* str = va_arg(args, const char*);
-       fprintf(file, "%s", str);
-      } break;
-      case 'i':
-      case 'd': {
-        int i = va_arg(args, int);
-       fprintf(file, "%d", i);
-      } break;
-      case 'o': {
-       int i = va_arg(args, int);
-       fprintf(file, "%o", i);
-      } break;
-      case 'u': {
-       unsigned int i = va_arg(args, unsigned int);
-       fprintf(file, "%u", i);
-      } break;
-      case 'f':
-      case 'F': {
-       double i = va_arg(args, double);
-       fprintf(file, "%f", i);
-      } break;
-      case 'p': {
-        void* i = va_arg(args, void*);
-       fprintf(file, "%p", i);
-      } break;
-      case '%': {
-       fprintf(file, "%%");
-      }
-      case 'c':{
-        char i = va_arg(args, char);
-       fprintf(file, "%c", i);
-      }
-      }
-    } else {
-      fputc(*fmt, file);
-    }
-    fmt++;
-  }
-
-  fputc('\n', file);
-#ifdef FLUSH_ON_LOG
-  fflush(file);
-#endif
-  va_end(args);
-}
-
-// simple and dirty way to have defering in C (not recommended to use!)
-#define defer(ret_val) \
-  result = ret_val;\
-  goto defer
-
-const char* slurp_file(const char* filename) {
-  FILE* f = fopen(filename, "rb");
-  char* result = NULL;
-
-#define TMPBUFF_SIZE (1024)
-  char tmpbuff[TMPBUFF_SIZE] = {0}; // for strerror_s()
-
-  if (f == NULL){
-    log_f(LOG_ERROR, "slurp_file::fopen(\"%s\", \"rb\") -> %s\n", filename, strerror_s(tmpbuff, TMPBUFF_SIZE, errno));
-    defer(NULL);
-  }
-
-  if (fseek(f, 0, SEEK_END) < 0) {
-    log_f(LOG_ERROR, "slurp_file::fseek(f, 0, SEEK_END) -> %s\n", filename, strerror_s(tmpbuff, TMPBUFF_SIZE, errno));
-    defer(NULL);
-  }
-
-  size_t fsize = ftell(f);
-
-  if (fsize == -1){
-    log_f(LOG_ERROR, "slurp_file::ftell(f) -> %s\n", filename, strerror_s(tmpbuff, TMPBUFF_SIZE, errno));
-    defer(NULL);
-  }
-
-  result = malloc(sizeof(char)*(fsize+1));
-
-  if (result == NULL){
-    log_f(LOG_ERROR, "slurp_file::malloc(%zu) -> %s\n", sizeof(char)*fsize, strerror_s(tmpbuff, TMPBUFF_SIZE, errno));
-    defer(NULL);
-  }
-
-  if (fseek(f, 0, SEEK_SET) < 0) {
-    log_f(LOG_ERROR, "slurp_file::fseek(f, 0, SEEK_SET) -> %s\n", filename, strerror_s(tmpbuff, TMPBUFF_SIZE, errno));
-    defer(NULL);
-  }
-
-  if (fread((char*)result, sizeof(char), fsize, f) != fsize){
-    log_f(LOG_ERROR, "slurp_file::fread(result, %d, 1, f) -> %s\n", fsize, strerror_s(tmpbuff, TMPBUFF_SIZE, errno));
-    defer(NULL);
-  }
-
-  // set null-terminator
-  result[fsize] = '\0';
-
- defer:
-  if (f) fclose(f);
-  return result;
-}
-
-
-//
-// ### Allocators ###
-//
-
-// Arena
-
-Arena Arena_make(size_t size) {
-  Arena res = {0};
-  res.buff_size = size == 0 ? ARENA_BUFF_INITIAL_SIZE : size;
-  res.buff = malloc(res.buff_size);
-  res.ptr = res.buff;
-
-  ASSERT(res.buff);
-
-  return res;
-}
-
-void* Arena_alloc(Arena* a, size_t size) {
-  ASSERT(a->buff);
-
-  void* res = a->ptr;
-  a->ptr = (uint8*)a->ptr + size;
-
-  // TODO: realloc buff with greater buff_size
-  size_t diff = (size_t)((uint8*)a->ptr - (uint8*)a->buff);
-  if (diff > a->buff_size) {
-    log_info("Arena resized from %u to %u", a->buff_size, a->buff_size*2);
-    a->buff_size *= 2;
-    a->buff = realloc(a->buff, a->buff_size);
-    a->ptr = (uint8*)a->buff + diff;
-    res = a->ptr;
-    a->ptr = (uint8*)a->ptr + size;
-  }
-  /* ASSERT((size_t)((uint8*)a->ptr - (uint8*)a->buff) <= a->buff_size); */
-
-  return res;
-}
-
-void Arena_reset(Arena* a) {
-  a->ptr = a->buff;
-}
-
-void Arena_free(Arena* a) {
-  free(a->buff);
-}
-
-//
-// String
-//
-
-bool cstr_to_wstr(Arena* warena, cstr str) {
-  if (warena->buff_size <= 0) {
-    log_error("Arena size is 0!!!");
-    return false;
-  }
-
-  size_t count = strlen(str)+1;
-  size_t ret = mbstowcs((wchar*)warena->buff,
-                 str,
-                 count);
-
-  if (ret == -1) {
-    log_error("Encountered an invalid multibyte character!");
-    return false;
-  } else if (ret == count) {
-    log_error("The wide-character string isn't null-terminated!");
-    return false;
-  }
-  return true;
-}
-
-//
-// String view
-//
-
-void sv_print_dumb(String_view sv){
-  for (size_t i = 0; i < (size_t)sv.count; ++i){
-    putc(*(sv.data+i), stdout);
-  }
-}
-
-String_view sv_from_cstr(const char* cstr){
-  return (String_view){
-    .data = cstr,
-    .count = strlen(cstr),
-  };
-}
-
-String_view sv_lpop(String_view* sv, uint32 n) {
-  String_view res = {0};
-  if (sv->count < n) return res;
-  res.data = sv->data;
-  res.count = n;
-
-  sv->data += n;
-  sv->count -= n;
-
-  return res;
-}
-
-String_view sv_lpop_until_predicate(String_view* sv, int(*predicate)(int)){
-  const char* old_sv_data = sv->data;
-  while (sv->count > 0 && !predicate(*sv->data)){
-    sv->data++;
-    sv->count--;
-  }
-
-  return (String_view){
-    .data = sv->data - (sv->data - old_sv_data),
-    .count = (sv->data - old_sv_data),
-  };
-}
-
-String_view sv_rpop_until_predicate(String_view* sv, int(*predicate)(int)){
-  size_t old_sv_count = sv->count;
-  while (sv->count > 0 && !predicate(*(sv->data+sv->count-1))){
-    sv->count--;
-  }
-
-  return (String_view){
-    .data = sv->data + sv->count,
-    .count = old_sv_count - sv->count,
-  };
-}
-
-String_view sv_lpop_until_char(String_view* sv, char ch){
-  const char* old_sv_data = sv->data;
-  while (sv->count > 0 && *sv->data != ch){
-    sv->data++;
-    sv->count--;
-  }
-
-  return (String_view){
-    .data = sv->data - (sv->data - old_sv_data),
-    .count = (sv->data - old_sv_data),
-  };
-}
-
-String_view sv_rpop_until_char(String_view* sv, char ch){
-  size_t old_sv_count = sv->count;
-  while (sv->count > 0 && *(sv->data+sv->count-1) != ch){
-    sv->count--;
-  }
-
-  return (String_view){
-    .data = sv->data + sv->count,
-    .count = old_sv_count - sv->count,
-  };
-}
-
-void sv_lremove(String_view* sv, size_t n){
-  if (n > sv->count) n = sv->count;
-
-  sv->data += n;
-  sv->count -= n;
-}
-
-void sv_rremove(String_view* sv, size_t n){
-  if (n > sv->count)
-    sv->count = 0;
-  else
-    sv->count -= n;
-}
-
-void sv_lremove_until_char(String_view* sv, char ch){
-  while (sv->count > 0 && *sv->data != ch){
-    sv->data++;
-    sv->count--;
-  }
-}
-
-void sv_rremove_until_char(String_view* sv, char ch){
-  while (sv->count > 0 && *(sv->data+sv->count-1) != ch){
-    sv->count--;
-  }
-}
-
-void sv_lremove_until_char_after(String_view* sv, char ch){
-  while (sv->count > 0 && *(sv->data-1) != ch){
-    sv->data++;
-    sv->count--;
-  }
-}
-
-void sv_rremove_until_char_after(String_view* sv, char ch){
-  while (sv->count > 0 && *(sv->data+sv->count) != ch){
-    sv->count--;
-  }
-}
-
-void sv_ltrim(String_view* sv){
-  while (sv->count > 0 && isspace(*sv->data)){
-    sv->data++;
-    sv->count--;
-  }
-}
-
-void sv_rtrim(String_view* sv){
-  while (sv->count > 0 && isspace(*(sv->data+sv->count-1))){
-    sv->count--;
-  }
-}
-
-void sv_trim(String_view* sv){
-  sv_ltrim(sv);
-  sv_rtrim(sv);
-}
-
-char* sv_to_cstr(String_view sv){
-  char* res = (char*)calloc(1, sizeof(char)*sv.count);
-  memcpy(res, sv.data, sv.count);
-  return res;
-}
-
-// TODO: check for failure of conversion. returns 0/0.0 on failure, so just check if the str contains zero.
-int32 sv_to_int(String_view sv) {
-  char* str = sv_to_cstr(sv);
-  int32 res = atoi(str);
-  free(str);
-  return res;
-}
-
-uint64 sv_to_uint64(String_view sv) {
-  char* str = sv_to_cstr(sv);
-  uint64 res = (uint64)atoll(str);
-  free(str);
-  return res;
-}
-
-uint8 sv_to_uint8_hex(String_view sv) {
-  char* str = sv_to_cstr(sv);
-  char* end = str + sv.count;
-  uint8 res = (uint8)strtol(str, &end, 16);
-  free(str);
-  return res;
-}
-
-float32 sv_to_float(String_view sv) {
-  char* str = sv_to_cstr(sv);
-  float32 res = (float32)atof(str);
-  free(str);
-  return res;
-}
-
-void*  sv_to_ptr(String_view sv) {
-  char* str = sv_to_cstr(sv);
-  char* end = NULL;
-  void* res = (void*)strtoull(str, &end, 16);
-  free(str);
-  return res;
-}
-
-bool sv_contains_char(String_view sv, char ch){
-  for (size_t i = 0; i < sv.count; ++i){
-    if (sv.data[i] == ch) return true;
-  }
-  return false;
-}
-
-bool sv_is_hex_numbers(String_view sv) {
-  char hex[] = {
-    '0', '1', '2', '3', '4', '5', '6', '7',
-    '8', '9', '0', 'a', 'b', 'c', 'd', 'e', 'f',
-    'A', 'B', 'C', 'D', 'E', 'F'
-  };
-  bool found = false;
-  for (size_t i = 0; i < sv.count; ++i) {
-    char c = sv.data[i];
-    for (size_t j = 0; j < ARRAY_LEN(hex); ++j) {
-      if (hex[j] == c) {
-       found = true;
-      }
-    }
-  }
-
-  return found;
-}
-
-bool sv_equals(String_view sv1, String_view sv2) {
-  if (sv1.count != sv2.count) return false;
-  for (size_t i = 0; i < sv1.count; ++i) {
-    if (sv1.data[i] != sv2.data[i]) {
-      return false;
-    }
-  }
-
-  return true;
-}
-
-
-//
-// Args
-//
-
-cstr shift_args(int* argc, char*** argv) {
-  if (*argc <= 0) return NULL;
-
-  cstr res = *(argv)[0];
-  *argv = (*argv) + 1;
-  *argc = (*argc) - 1;
-  return res;
-}
-
-#endif
+#ifndef _STDLIB_H_\r
+#define _STDLIB_H_\r
+#include <stdio.h>\r
+#include <stdbool.h>\r
+#include <stdarg.h>\r
+#include <stdint.h>\r
+#include <stdlib.h>\r
+#include <string.h>\r
+#include <ctype.h>\r
+#ifdef _WIN32\r
+#include <windows.h>\r
+#endif\r
+#include <locale.h>\r
+\r
+//\r
+// External libs --------------------------------------------------\r
+//\r
+\r
+// >> stb_ds.h >>\r
+\r
+/* stb_ds.h - v0.67 - public domain data structures - Sean Barrett 2019\r
+\r
+   This is a single-header-file library that provides easy-to-use\r
+   dynamic arrays and hash tables for C (also works in C++).\r
+\r
+   For a gentle introduction:\r
+      http://nothings.org/stb_ds\r
+\r
+   To use this library, do this in *one* C or C++ file:\r
+      #define STB_DS_IMPLEMENTATION\r
+      #include "stb_ds.h"\r
+\r
+TABLE OF CONTENTS\r
+\r
+  Table of Contents\r
+  Compile-time options\r
+  License\r
+  Documentation\r
+  Notes\r
+  Notes - Dynamic arrays\r
+  Notes - Hash maps\r
+  Credits\r
+\r
+COMPILE-TIME OPTIONS\r
+\r
+  #define STBDS_NO_SHORT_NAMES\r
+\r
+     This flag needs to be set globally.\r
+\r
+     By default stb_ds exposes shorter function names that are not qualified\r
+     with the "stbds_" prefix. If these names conflict with the names in your\r
+     code, define this flag.\r
+\r
+  #define STBDS_SIPHASH_2_4\r
+\r
+     This flag only needs to be set in the file containing #define STB_DS_IMPLEMENTATION.\r
+\r
+     By default stb_ds.h hashes using a weaker variant of SipHash and a custom hash for\r
+     4- and 8-byte keys. On 64-bit platforms, you can define the above flag to force\r
+     stb_ds.h to use specification-compliant SipHash-2-4 for all keys. Doing so makes\r
+     hash table insertion about 20% slower on 4- and 8-byte keys, 5% slower on\r
+     64-byte keys, and 10% slower on 256-byte keys on my test computer.\r
+\r
+  #define STBDS_REALLOC(context,ptr,size) better_realloc\r
+  #define STBDS_FREE(context,ptr)         better_free\r
+\r
+     These defines only need to be set in the file containing #define STB_DS_IMPLEMENTATION.\r
+\r
+     By default stb_ds uses stdlib realloc() and free() for memory management. You can\r
+     substitute your own functions instead by defining these symbols. You must either\r
+     define both, or neither. Note that at the moment, 'context' will always be NULL.\r
+     @TODO add an array/hash initialization function that takes a memory context pointer.\r
+\r
+  #define STBDS_UNIT_TESTS\r
+\r
+     Defines a function stbds_unit_tests() that checks the functioning of the data structures.\r
+\r
+  Note that on older versions of gcc (e.g. 5.x.x) you may need to build with '-std=c++0x'\r
+     (or equivalentally '-std=c++11') when using anonymous structures as seen on the web\r
+     page or in STBDS_UNIT_TESTS.\r
+\r
+LICENSE\r
+\r
+  Placed in the public domain and also MIT licensed.\r
+  See end of file for detailed license information.\r
+\r
+DOCUMENTATION\r
+\r
+  Dynamic Arrays\r
+\r
+    Non-function interface:\r
+\r
+      Declare an empty dynamic array of type T\r
+        T* foo = NULL;\r
+\r
+      Access the i'th item of a dynamic array 'foo' of type T, T* foo:\r
+        foo[i]\r
+\r
+    Functions (actually macros)\r
+\r
+      arrfree:\r
+        void arrfree(T*);\r
+          Frees the array.\r
+\r
+      arrlen:\r
+        ptrdiff_t arrlen(T*);\r
+          Returns the number of elements in the array.\r
+\r
+      arrlenu:\r
+        size_t arrlenu(T*);\r
+          Returns the number of elements in the array as an unsigned type.\r
+\r
+      arrpop:\r
+        T arrpop(T* a)\r
+          Removes the final element of the array and returns it.\r
+\r
+      arrput:\r
+        T arrput(T* a, T b);\r
+          Appends the item b to the end of array a. Returns b.\r
+\r
+      arrins:\r
+        T arrins(T* a, int p, T b);\r
+          Inserts the item b into the middle of array a, into a[p],\r
+          moving the rest of the array over. Returns b.\r
+\r
+      arrinsn:\r
+        void arrinsn(T* a, int p, int n);\r
+          Inserts n uninitialized items into array a starting at a[p],\r
+          moving the rest of the array over.\r
+\r
+      arraddnptr:\r
+        T* arraddnptr(T* a, int n)\r
+          Appends n uninitialized items onto array at the end.\r
+          Returns a pointer to the first uninitialized item added.\r
+\r
+      arraddnindex:\r
+        size_t arraddnindex(T* a, int n)\r
+          Appends n uninitialized items onto array at the end.\r
+          Returns the index of the first uninitialized item added.\r
+\r
+      arrdel:\r
+        void arrdel(T* a, int p);\r
+          Deletes the element at a[p], moving the rest of the array over.\r
+\r
+      arrdeln:\r
+        void arrdeln(T* a, int p, int n);\r
+          Deletes n elements starting at a[p], moving the rest of the array over.\r
+\r
+      arrdelswap:\r
+        void arrdelswap(T* a, int p);\r
+          Deletes the element at a[p], replacing it with the element from\r
+          the end of the array. O(1) performance.\r
+\r
+      arrsetlen:\r
+        void arrsetlen(T* a, int n);\r
+          Changes the length of the array to n. Allocates uninitialized\r
+          slots at the end if necessary.\r
+\r
+      arrsetcap:\r
+        size_t arrsetcap(T* a, int n);\r
+          Sets the length of allocated storage to at least n. It will not\r
+          change the length of the array.\r
+\r
+      arrcap:\r
+        size_t arrcap(T* a);\r
+          Returns the number of total elements the array can contain without\r
+          needing to be reallocated.\r
+\r
+  Hash maps & String hash maps\r
+\r
+    Given T is a structure type: struct { TK key; TV value; }. Note that some\r
+    functions do not require TV value and can have other fields. For string\r
+    hash maps, TK must be 'char *'.\r
+\r
+    Special interface:\r
+\r
+      stbds_rand_seed:\r
+        void stbds_rand_seed(size_t seed);\r
+          For security against adversarially chosen data, you should seed the\r
+          library with a strong random number. Or at least seed it with time().\r
+\r
+      stbds_hash_string:\r
+        size_t stbds_hash_string(char *str, size_t seed);\r
+          Returns a hash value for a string.\r
+\r
+      stbds_hash_bytes:\r
+        size_t stbds_hash_bytes(void *p, size_t len, size_t seed);\r
+          These functions hash an arbitrary number of bytes. The function\r
+          uses a custom hash for 4- and 8-byte data, and a weakened version\r
+          of SipHash for everything else. On 64-bit platforms you can get\r
+          specification-compliant SipHash-2-4 on all data by defining\r
+          STBDS_SIPHASH_2_4, at a significant cost in speed.\r
+\r
+    Non-function interface:\r
+\r
+      Declare an empty hash map of type T\r
+        T* foo = NULL;\r
+\r
+      Access the i'th entry in a hash table T* foo:\r
+        foo[i]\r
+\r
+    Function interface (actually macros):\r
+\r
+      hmfree\r
+      shfree\r
+        void hmfree(T*);\r
+        void shfree(T*);\r
+          Frees the hashmap and sets the pointer to NULL.\r
+\r
+      hmlen\r
+      shlen\r
+        ptrdiff_t hmlen(T*)\r
+        ptrdiff_t shlen(T*)\r
+          Returns the number of elements in the hashmap.\r
+\r
+      hmlenu\r
+      shlenu\r
+        size_t hmlenu(T*)\r
+        size_t shlenu(T*)\r
+          Returns the number of elements in the hashmap.\r
+\r
+      hmgeti\r
+      shgeti\r
+      hmgeti_ts\r
+        ptrdiff_t hmgeti(T*, TK key)\r
+        ptrdiff_t shgeti(T*, char* key)\r
+        ptrdiff_t hmgeti_ts(T*, TK key, ptrdiff_t tempvar)\r
+          Returns the index in the hashmap which has the key 'key', or -1\r
+          if the key is not present.\r
+\r
+      hmget\r
+      hmget_ts\r
+      shget\r
+        TV hmget(T*, TK key)\r
+        TV shget(T*, char* key)\r
+        TV hmget_ts(T*, TK key, ptrdiff_t tempvar)\r
+          Returns the value corresponding to 'key' in the hashmap.\r
+          The structure must have a 'value' field\r
+\r
+      hmgets\r
+      shgets\r
+        T hmgets(T*, TK key)\r
+        T shgets(T*, char* key)\r
+          Returns the structure corresponding to 'key' in the hashmap.\r
+\r
+      hmgetp\r
+      shgetp\r
+      hmgetp_ts\r
+      hmgetp_null\r
+      shgetp_null\r
+        T* hmgetp(T*, TK key)\r
+        T* shgetp(T*, char* key)\r
+        T* hmgetp_ts(T*, TK key, ptrdiff_t tempvar)\r
+        T* hmgetp_null(T*, TK key)\r
+        T* shgetp_null(T*, char *key)\r
+          Returns a pointer to the structure corresponding to 'key' in\r
+          the hashmap. Functions ending in "_null" return NULL if the key\r
+          is not present in the hashmap; the others return a pointer to a\r
+          structure holding the default value (but not the searched-for key).\r
+\r
+      hmdefault\r
+      shdefault\r
+        TV hmdefault(T*, TV value)\r
+        TV shdefault(T*, TV value)\r
+          Sets the default value for the hashmap, the value which will be\r
+          returned by hmget/shget if the key is not present.\r
+\r
+      hmdefaults\r
+      shdefaults\r
+        TV hmdefaults(T*, T item)\r
+        TV shdefaults(T*, T item)\r
+          Sets the default struct for the hashmap, the contents which will be\r
+          returned by hmgets/shgets if the key is not present.\r
+\r
+      hmput\r
+      shput\r
+        TV hmput(T*, TK key, TV value)\r
+        TV shput(T*, char* key, TV value)\r
+          Inserts a <key,value> pair into the hashmap. If the key is already\r
+          present in the hashmap, updates its value.\r
+\r
+      hmputs\r
+      shputs\r
+        T hmputs(T*, T item)\r
+        T shputs(T*, T item)\r
+          Inserts a struct with T.key into the hashmap. If the struct is already\r
+          present in the hashmap, updates it.\r
+\r
+      hmdel\r
+      shdel\r
+        int hmdel(T*, TK key)\r
+        int shdel(T*, char* key)\r
+          If 'key' is in the hashmap, deletes its entry and returns 1.\r
+          Otherwise returns 0.\r
+\r
+    Function interface (actually macros) for strings only:\r
+\r
+      sh_new_strdup\r
+        void sh_new_strdup(T*);\r
+          Overwrites the existing pointer with a newly allocated\r
+          string hashmap which will automatically allocate and free\r
+          each string key using realloc/free\r
+\r
+      sh_new_arena\r
+        void sh_new_arena(T*);\r
+          Overwrites the existing pointer with a newly allocated\r
+          string hashmap which will automatically allocate each string\r
+          key to a string arena. Every string key ever used by this\r
+          hash table remains in the arena until the arena is freed.\r
+          Additionally, any key which is deleted and reinserted will\r
+          be allocated multiple times in the string arena.\r
+\r
+NOTES\r
+\r
+  * These data structures are realloc'd when they grow, and the macro\r
+    "functions" write to the provided pointer. This means: (a) the pointer\r
+    must be an lvalue, and (b) the pointer to the data structure is not\r
+    stable, and you must maintain it the same as you would a realloc'd\r
+    pointer. For example, if you pass a pointer to a dynamic array to a\r
+    function which updates it, the function must return back the new\r
+    pointer to the caller. This is the price of trying to do this in C.\r
+\r
+  * The following are the only functions that are thread-safe on a single data\r
+    structure, i.e. can be run in multiple threads simultaneously on the same\r
+    data structure\r
+        hmlen        shlen\r
+        hmlenu       shlenu\r
+        hmget_ts     shget_ts\r
+        hmgeti_ts    shgeti_ts\r
+        hmgets_ts    shgets_ts\r
+\r
+  * You iterate over the contents of a dynamic array and a hashmap in exactly\r
+    the same way, using arrlen/hmlen/shlen:\r
+\r
+      for (i=0; i < arrlen(foo); ++i)\r
+         ... foo[i] ...\r
+\r
+  * All operations except arrins/arrdel are O(1) amortized, but individual\r
+    operations can be slow, so these data structures may not be suitable\r
+    for real time use. Dynamic arrays double in capacity as needed, so\r
+    elements are copied an average of once. Hash tables double/halve\r
+    their size as needed, with appropriate hysteresis to maintain O(1)\r
+    performance.\r
+\r
+NOTES - DYNAMIC ARRAY\r
+\r
+  * If you know how long a dynamic array is going to be in advance, you can avoid\r
+    extra memory allocations by using arrsetlen to allocate it to that length in\r
+    advance and use foo[n] while filling it out, or arrsetcap to allocate the memory\r
+    for that length and use arrput/arrpush as normal.\r
+\r
+  * Unlike some other versions of the dynamic array, this version should\r
+    be safe to use with strict-aliasing optimizations.\r
+\r
+NOTES - HASH MAP\r
+\r
+  * For compilers other than GCC and clang (e.g. Visual Studio), for hmput/hmget/hmdel\r
+    and variants, the key must be an lvalue (so the macro can take the address of it).\r
+    Extensions are used that eliminate this requirement if you're using C99 and later\r
+    in GCC or clang, or if you're using C++ in GCC. But note that this can make your\r
+    code less portable.\r
+\r
+  * To test for presence of a key in a hashmap, just do 'hmgeti(foo,key) >= 0'.\r
+\r
+  * The iteration order of your data in the hashmap is determined solely by the\r
+    order of insertions and deletions. In particular, if you never delete, new\r
+    keys are always added at the end of the array. This will be consistent\r
+    across all platforms and versions of the library. However, you should not\r
+    attempt to serialize the internal hash table, as the hash is not consistent\r
+    between different platforms, and may change with future versions of the library.\r
+\r
+  * Use sh_new_arena() for string hashmaps that you never delete from. Initialize\r
+    with NULL if you're managing the memory for your strings, or your strings are\r
+    never freed (at least until the hashmap is freed). Otherwise, use sh_new_strdup().\r
+    @TODO: make an arena variant that garbage collects the strings with a trivial\r
+    copy collector into a new arena whenever the table shrinks / rebuilds. Since\r
+    current arena recommendation is to only use arena if it never deletes, then\r
+    this can just replace current arena implementation.\r
+\r
+  * If adversarial input is a serious concern and you're on a 64-bit platform,\r
+    enable STBDS_SIPHASH_2_4 (see the 'Compile-time options' section), and pass\r
+    a strong random number to stbds_rand_seed.\r
+\r
+  * The default value for the hash table is stored in foo[-1], so if you\r
+    use code like 'hmget(T,k)->value = 5' you can accidentally overwrite\r
+    the value stored by hmdefault if 'k' is not present.\r
+\r
+CREDITS\r
+\r
+  Sean Barrett -- library, idea for dynamic array API/implementation\r
+  Per Vognsen  -- idea for hash table API/implementation\r
+  Rafael Sachetto -- arrpop()\r
+  github:HeroicKatora -- arraddn() reworking\r
+\r
+  Bugfixes:\r
+    Andy Durdin\r
+    Shane Liesegang\r
+    Vinh Truong\r
+    Andreas Molzer\r
+    github:hashitaku\r
+    github:srdjanstipic\r
+    Macoy Madson\r
+    Andreas Vennstrom\r
+    Tobias Mansfield-Williams\r
+*/\r
+\r
+#ifdef STBDS_UNIT_TESTS\r
+#define _CRT_SECURE_NO_WARNINGS\r
+#endif\r
+\r
+#ifndef INCLUDE_STB_DS_H\r
+#define INCLUDE_STB_DS_H\r
+\r
+#include <stddef.h>\r
+#include <string.h>\r
+\r
+#ifndef STBDS_NO_SHORT_NAMES\r
+#define arrlen      stbds_arrlen\r
+#define arrlenu     stbds_arrlenu\r
+#define arrput      stbds_arrput\r
+#define arrpush     stbds_arrput\r
+#define arrpop      stbds_arrpop\r
+#define arrfree     stbds_arrfree\r
+#define arraddn     stbds_arraddn // deprecated, use one of the following instead:\r
+#define arraddnptr  stbds_arraddnptr\r
+#define arraddnindex stbds_arraddnindex\r
+#define arrsetlen   stbds_arrsetlen\r
+#define arrlast     stbds_arrlast\r
+#define arrins      stbds_arrins\r
+#define arrinsn     stbds_arrinsn\r
+#define arrdel      stbds_arrdel\r
+#define arrdeln     stbds_arrdeln\r
+#define arrdelswap  stbds_arrdelswap\r
+#define arrcap      stbds_arrcap\r
+#define arrsetcap   stbds_arrsetcap\r
+\r
+#define hmput       stbds_hmput\r
+#define hmputs      stbds_hmputs\r
+#define hmget       stbds_hmget\r
+#define hmget_ts    stbds_hmget_ts\r
+#define hmgets      stbds_hmgets\r
+#define hmgetp      stbds_hmgetp\r
+#define hmgetp_ts   stbds_hmgetp_ts\r
+#define hmgetp_null stbds_hmgetp_null\r
+#define hmgeti      stbds_hmgeti\r
+#define hmgeti_ts   stbds_hmgeti_ts\r
+#define hmdel       stbds_hmdel\r
+#define hmlen       stbds_hmlen\r
+#define hmlenu      stbds_hmlenu\r
+#define hmfree      stbds_hmfree\r
+#define hmdefault   stbds_hmdefault\r
+#define hmdefaults  stbds_hmdefaults\r
+\r
+#define shput       stbds_shput\r
+#define shputi      stbds_shputi\r
+#define shputs      stbds_shputs\r
+#define shget       stbds_shget\r
+#define shgeti      stbds_shgeti\r
+#define shgets      stbds_shgets\r
+#define shgetp      stbds_shgetp\r
+#define shgetp_null stbds_shgetp_null\r
+#define shdel       stbds_shdel\r
+#define shlen       stbds_shlen\r
+#define shlenu      stbds_shlenu\r
+#define shfree      stbds_shfree\r
+#define shdefault   stbds_shdefault\r
+#define shdefaults  stbds_shdefaults\r
+#define sh_new_arena  stbds_sh_new_arena\r
+#define sh_new_strdup stbds_sh_new_strdup\r
+\r
+#define stralloc    stbds_stralloc\r
+#define strreset    stbds_strreset\r
+#endif\r
+\r
+#if defined(STBDS_REALLOC) && !defined(STBDS_FREE) || !defined(STBDS_REALLOC) && defined(STBDS_FREE)\r
+#error "You must define both STBDS_REALLOC and STBDS_FREE, or neither."\r
+#endif\r
+#if !defined(STBDS_REALLOC) && !defined(STBDS_FREE)\r
+#include <stdlib.h>\r
+#define STBDS_REALLOC(c,p,s) realloc(p,s)\r
+#define STBDS_FREE(c,p)      free(p)\r
+#endif\r
+\r
+#ifdef _MSC_VER\r
+#define STBDS_NOTUSED(v)  (void)(v)\r
+#else\r
+#define STBDS_NOTUSED(v)  (void)sizeof(v)\r
+#endif\r
+\r
+#ifdef __cplusplus\r
+extern "C" {\r
+#endif\r
+\r
+// for security against attackers, seed the library with a random number, at least time() but stronger is better\r
+extern void stbds_rand_seed(size_t seed);\r
+\r
+// these are the hash functions used internally if you want to test them or use them for other purposes\r
+extern size_t stbds_hash_bytes(void *p, size_t len, size_t seed);\r
+extern size_t stbds_hash_string(char *str, size_t seed);\r
+\r
+// this is a simple string arena allocator, initialize with e.g. 'stbds_string_arena my_arena={0}'.\r
+typedef struct stbds_string_arena stbds_string_arena;\r
+extern char * stbds_stralloc(stbds_string_arena *a, char *str);\r
+extern void   stbds_strreset(stbds_string_arena *a);\r
+\r
+// have to #define STBDS_UNIT_TESTS to call this\r
+extern void stbds_unit_tests(void);\r
+\r
+///////////////\r
+//\r
+// Everything below here is implementation details\r
+//\r
+\r
+extern void * stbds_arrgrowf(void *a, size_t elemsize, size_t addlen, size_t min_cap);\r
+extern void   stbds_arrfreef(void *a);\r
+extern void   stbds_hmfree_func(void *p, size_t elemsize);\r
+extern void * stbds_hmget_key(void *a, size_t elemsize, void *key, size_t keysize, int mode);\r
+extern void * stbds_hmget_key_ts(void *a, size_t elemsize, void *key, size_t keysize, ptrdiff_t *temp, int mode);\r
+extern void * stbds_hmput_default(void *a, size_t elemsize);\r
+extern void * stbds_hmput_key(void *a, size_t elemsize, void *key, size_t keysize, int mode);\r
+extern void * stbds_hmdel_key(void *a, size_t elemsize, void *key, size_t keysize, size_t keyoffset, int mode);\r
+extern void * stbds_shmode_func(size_t elemsize, int mode);\r
+\r
+#ifdef __cplusplus\r
+}\r
+#endif\r
+\r
+#if defined(__GNUC__) || defined(__clang__)\r
+#define STBDS_HAS_TYPEOF\r
+#ifdef __cplusplus\r
+//#define STBDS_HAS_LITERAL_ARRAY  // this is currently broken for clang\r
+#endif\r
+#endif\r
+\r
+#if !defined(__cplusplus)\r
+#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L\r
+#define STBDS_HAS_LITERAL_ARRAY\r
+#endif\r
+#endif\r
+\r
+// this macro takes the address of the argument, but on gcc/clang can accept rvalues\r
+#if defined(STBDS_HAS_LITERAL_ARRAY) && defined(STBDS_HAS_TYPEOF)\r
+  #if __clang__\r
+  #define STBDS_ADDRESSOF(typevar, value)     ((__typeof__(typevar)[1]){value}) // literal array decays to pointer to value\r
+  #else\r
+  #define STBDS_ADDRESSOF(typevar, value)     ((typeof(typevar)[1]){value}) // literal array decays to pointer to value\r
+  #endif\r
+#else\r
+#define STBDS_ADDRESSOF(typevar, value)     &(value)\r
+#endif\r
+\r
+#define STBDS_OFFSETOF(var,field)           ((char *) &(var)->field - (char *) (var))\r
+\r
+#define stbds_header(t)  ((stbds_array_header *) (t) - 1)\r
+#define stbds_temp(t)    stbds_header(t)->temp\r
+#define stbds_temp_key(t) (*(char **) stbds_header(t)->hash_table)\r
+\r
+#define stbds_arrsetcap(a,n)   (stbds_arrgrow(a,0,n))\r
+#define stbds_arrsetlen(a,n)   ((stbds_arrcap(a) < (size_t) (n) ? stbds_arrsetcap((a),(size_t)(n)),0 : 0), (a) ? stbds_header(a)->length = (size_t) (n) : 0)\r
+#define stbds_arrcap(a)        ((a) ? stbds_header(a)->capacity : 0)\r
+#define stbds_arrlen(a)        ((a) ? (ptrdiff_t) stbds_header(a)->length : 0)\r
+#define stbds_arrlenu(a)       ((a) ?             stbds_header(a)->length : 0)\r
+#define stbds_arrput(a,v)      (stbds_arrmaybegrow(a,1), (a)[stbds_header(a)->length++] = (v))\r
+#define stbds_arrpush          stbds_arrput  // synonym\r
+#define stbds_arrpop(a)        (stbds_header(a)->length--, (a)[stbds_header(a)->length])\r
+#define stbds_arraddn(a,n)     ((void)(stbds_arraddnindex(a, n)))    // deprecated, use one of the following instead:\r
+#define stbds_arraddnptr(a,n)  (stbds_arrmaybegrow(a,n), (n) ? (stbds_header(a)->length += (n), &(a)[stbds_header(a)->length-(n)]) : (a))\r
+#define stbds_arraddnindex(a,n)(stbds_arrmaybegrow(a,n), (n) ? (stbds_header(a)->length += (n), stbds_header(a)->length-(n)) : stbds_arrlen(a))\r
+#define stbds_arraddnoff       stbds_arraddnindex\r
+#define stbds_arrlast(a)       ((a)[stbds_header(a)->length-1])\r
+#define stbds_arrfree(a)       ((void) ((a) ? STBDS_FREE(NULL,stbds_header(a)) : (void)0), (a)=NULL)\r
+#define stbds_arrdel(a,i)      stbds_arrdeln(a,i,1)\r
+#define stbds_arrdeln(a,i,n)   (memmove(&(a)[i], &(a)[(i)+(n)], sizeof *(a) * (stbds_header(a)->length-(n)-(i))), stbds_header(a)->length -= (n))\r
+#define stbds_arrdelswap(a,i)  ((a)[i] = stbds_arrlast(a), stbds_header(a)->length -= 1)\r
+#define stbds_arrinsn(a,i,n)   (stbds_arraddn((a),(n)), memmove(&(a)[(i)+(n)], &(a)[i], sizeof *(a) * (stbds_header(a)->length-(n)-(i))))\r
+#define stbds_arrins(a,i,v)    (stbds_arrinsn((a),(i),1), (a)[i]=(v))\r
+\r
+#define stbds_arrmaybegrow(a,n)  ((!(a) || stbds_header(a)->length + (n) > stbds_header(a)->capacity) \\r
+                                  ? (stbds_arrgrow(a,n,0),0) : 0)\r
+\r
+#define stbds_arrgrow(a,b,c)   ((a) = stbds_arrgrowf_wrapper((a), sizeof *(a), (b), (c)))\r
+\r
+#define stbds_hmput(t, k, v) \\r
+    ((t) = stbds_hmput_key_wrapper((t), sizeof *(t), (void*) STBDS_ADDRESSOF((t)->key, (k)), sizeof (t)->key, 0),   \\r
+     (t)[stbds_temp((t)-1)].key = (k),    \\r
+     (t)[stbds_temp((t)-1)].value = (v))\r
+\r
+#define stbds_hmputs(t, s) \\r
+    ((t) = stbds_hmput_key_wrapper((t), sizeof *(t), &(s).key, sizeof (s).key, STBDS_HM_BINARY), \\r
+     (t)[stbds_temp((t)-1)] = (s))\r
+\r
+#define stbds_hmgeti(t,k) \\r
+    ((t) = stbds_hmget_key_wrapper((t), sizeof *(t), (void*) STBDS_ADDRESSOF((t)->key, (k)), sizeof (t)->key, STBDS_HM_BINARY), \\r
+      stbds_temp((t)-1))\r
+\r
+#define stbds_hmgeti_ts(t,k,temp) \\r
+    ((t) = stbds_hmget_key_ts_wrapper((t), sizeof *(t), (void*) STBDS_ADDRESSOF((t)->key, (k)), sizeof (t)->key, &(temp), STBDS_HM_BINARY), \\r
+      (temp))\r
+\r
+#define stbds_hmgetp(t, k) \\r
+    ((void) stbds_hmgeti(t,k), &(t)[stbds_temp((t)-1)])\r
+\r
+#define stbds_hmgetp_ts(t, k, temp) \\r
+    ((void) stbds_hmgeti_ts(t,k,temp), &(t)[temp])\r
+\r
+#define stbds_hmdel(t,k) \\r
+    (((t) = stbds_hmdel_key_wrapper((t),sizeof *(t), (void*) STBDS_ADDRESSOF((t)->key, (k)), sizeof (t)->key, STBDS_OFFSETOF((t),key), STBDS_HM_BINARY)),(t)?stbds_temp((t)-1):0)\r
+\r
+#define stbds_hmdefault(t, v) \\r
+    ((t) = stbds_hmput_default_wrapper((t), sizeof *(t)), (t)[-1].value = (v))\r
+\r
+#define stbds_hmdefaults(t, s) \\r
+    ((t) = stbds_hmput_default_wrapper((t), sizeof *(t)), (t)[-1] = (s))\r
+\r
+#define stbds_hmfree(p)        \\r
+    ((void) ((p) != NULL ? stbds_hmfree_func((p)-1,sizeof*(p)),0 : 0),(p)=NULL)\r
+\r
+#define stbds_hmgets(t, k)    (*stbds_hmgetp(t,k))\r
+#define stbds_hmget(t, k)     (stbds_hmgetp(t,k)->value)\r
+#define stbds_hmget_ts(t, k, temp)  (stbds_hmgetp_ts(t,k,temp)->value)\r
+#define stbds_hmlen(t)        ((t) ? (ptrdiff_t) stbds_header((t)-1)->length-1 : 0)\r
+#define stbds_hmlenu(t)       ((t) ?             stbds_header((t)-1)->length-1 : 0)\r
+#define stbds_hmgetp_null(t,k)  (stbds_hmgeti(t,k) == -1 ? NULL : &(t)[stbds_temp((t)-1)])\r
+\r
+#define stbds_shput(t, k, v) \\r
+    ((t) = stbds_hmput_key_wrapper((t), sizeof *(t), (void*) (k), sizeof (t)->key, STBDS_HM_STRING),   \\r
+     (t)[stbds_temp((t)-1)].value = (v))\r
+\r
+#define stbds_shputi(t, k, v) \\r
+    ((t) = stbds_hmput_key_wrapper((t), sizeof *(t), (void*) (k), sizeof (t)->key, STBDS_HM_STRING),   \\r
+     (t)[stbds_temp((t)-1)].value = (v), stbds_temp((t)-1))\r
+\r
+#define stbds_shputs(t, s) \\r
+    ((t) = stbds_hmput_key_wrapper((t), sizeof *(t), (void*) (s).key, sizeof (s).key, STBDS_HM_STRING), \\r
+     (t)[stbds_temp((t)-1)] = (s), \\r
+     (t)[stbds_temp((t)-1)].key = stbds_temp_key((t)-1)) // above line overwrites whole structure, so must rewrite key here if it was allocated internally\r
+\r
+#define stbds_pshput(t, p) \\r
+    ((t) = stbds_hmput_key_wrapper((t), sizeof *(t), (void*) (p)->key, sizeof (p)->key, STBDS_HM_PTR_TO_STRING), \\r
+     (t)[stbds_temp((t)-1)] = (p))\r
+\r
+#define stbds_shgeti(t,k) \\r
+     ((t) = stbds_hmget_key_wrapper((t), sizeof *(t), (void*) (k), sizeof (t)->key, STBDS_HM_STRING), \\r
+      stbds_temp((t)-1))\r
+\r
+#define stbds_pshgeti(t,k) \\r
+     ((t) = stbds_hmget_key_wrapper((t), sizeof *(t), (void*) (k), sizeof (*(t))->key, STBDS_HM_PTR_TO_STRING), \\r
+      stbds_temp((t)-1))\r
+\r
+#define stbds_shgetp(t, k) \\r
+    ((void) stbds_shgeti(t,k), &(t)[stbds_temp((t)-1)])\r
+\r
+#define stbds_pshget(t, k) \\r
+    ((void) stbds_pshgeti(t,k), (t)[stbds_temp((t)-1)])\r
+\r
+#define stbds_shdel(t,k) \\r
+    (((t) = stbds_hmdel_key_wrapper((t),sizeof *(t), (void*) (k), sizeof (t)->key, STBDS_OFFSETOF((t),key), STBDS_HM_STRING)),(t)?stbds_temp((t)-1):0)\r
+#define stbds_pshdel(t,k) \\r
+    (((t) = stbds_hmdel_key_wrapper((t),sizeof *(t), (void*) (k), sizeof (*(t))->key, STBDS_OFFSETOF(*(t),key), STBDS_HM_PTR_TO_STRING)),(t)?stbds_temp((t)-1):0)\r
+\r
+#define stbds_sh_new_arena(t)  \\r
+    ((t) = stbds_shmode_func_wrapper(t, sizeof *(t), STBDS_SH_ARENA))\r
+#define stbds_sh_new_strdup(t) \\r
+    ((t) = stbds_shmode_func_wrapper(t, sizeof *(t), STBDS_SH_STRDUP))\r
+\r
+#define stbds_shdefault(t, v)  stbds_hmdefault(t,v)\r
+#define stbds_shdefaults(t, s) stbds_hmdefaults(t,s)\r
+\r
+#define stbds_shfree       stbds_hmfree\r
+#define stbds_shlenu       stbds_hmlenu\r
+\r
+#define stbds_shgets(t, k) (*stbds_shgetp(t,k))\r
+#define stbds_shget(t, k)  (stbds_shgetp(t,k)->value)\r
+#define stbds_shgetp_null(t,k)  (stbds_shgeti(t,k) == -1 ? NULL : &(t)[stbds_temp((t)-1)])\r
+#define stbds_shlen        stbds_hmlen\r
+\r
+typedef struct\r
+{\r
+  size_t      length;\r
+  size_t      capacity;\r
+  void      * hash_table;\r
+  ptrdiff_t   temp;\r
+} stbds_array_header;\r
+\r
+typedef struct stbds_string_block\r
+{\r
+  struct stbds_string_block *next;\r
+  char storage[8];\r
+} stbds_string_block;\r
+\r
+struct stbds_string_arena\r
+{\r
+  stbds_string_block *storage;\r
+  size_t remaining;\r
+  unsigned char block;\r
+  unsigned char mode;  // this isn't used by the string arena itself\r
+};\r
+\r
+#define STBDS_HM_BINARY         0\r
+#define STBDS_HM_STRING         1\r
+\r
+enum\r
+{\r
+   STBDS_SH_NONE,\r
+   STBDS_SH_DEFAULT,\r
+   STBDS_SH_STRDUP,\r
+   STBDS_SH_ARENA\r
+};\r
+\r
+#ifdef __cplusplus\r
+// in C we use implicit assignment from these void*-returning functions to T*.\r
+// in C++ these templates make the same code work\r
+template<class T> static T * stbds_arrgrowf_wrapper(T *a, size_t elemsize, size_t addlen, size_t min_cap) {\r
+  return (T*)stbds_arrgrowf((void *)a, elemsize, addlen, min_cap);\r
+}\r
+template<class T> static T * stbds_hmget_key_wrapper(T *a, size_t elemsize, void *key, size_t keysize, int mode) {\r
+  return (T*)stbds_hmget_key((void*)a, elemsize, key, keysize, mode);\r
+}\r
+template<class T> static T * stbds_hmget_key_ts_wrapper(T *a, size_t elemsize, void *key, size_t keysize, ptrdiff_t *temp, int mode) {\r
+  return (T*)stbds_hmget_key_ts((void*)a, elemsize, key, keysize, temp, mode);\r
+}\r
+template<class T> static T * stbds_hmput_default_wrapper(T *a, size_t elemsize) {\r
+  return (T*)stbds_hmput_default((void *)a, elemsize);\r
+}\r
+template<class T> static T * stbds_hmput_key_wrapper(T *a, size_t elemsize, void *key, size_t keysize, int mode) {\r
+  return (T*)stbds_hmput_key((void*)a, elemsize, key, keysize, mode);\r
+}\r
+template<class T> static T * stbds_hmdel_key_wrapper(T *a, size_t elemsize, void *key, size_t keysize, size_t keyoffset, int mode){\r
+  return (T*)stbds_hmdel_key((void*)a, elemsize, key, keysize, keyoffset, mode);\r
+}\r
+template<class T> static T * stbds_shmode_func_wrapper(T *, size_t elemsize, int mode) {\r
+  return (T*)stbds_shmode_func(elemsize, mode);\r
+}\r
+#else\r
+#define stbds_arrgrowf_wrapper            stbds_arrgrowf\r
+#define stbds_hmget_key_wrapper           stbds_hmget_key\r
+#define stbds_hmget_key_ts_wrapper        stbds_hmget_key_ts\r
+#define stbds_hmput_default_wrapper       stbds_hmput_default\r
+#define stbds_hmput_key_wrapper           stbds_hmput_key\r
+#define stbds_hmdel_key_wrapper           stbds_hmdel_key\r
+#define stbds_shmode_func_wrapper(t,e,m)  stbds_shmode_func(e,m)\r
+#endif\r
+\r
+#endif // INCLUDE_STB_DS_H\r
+// << fold <<\r
+\r
+// >> stb_sprintf.h >>\r
+\r
+// stb_sprintf - v1.10 - public domain snprintf() implementation\r
+// originally by Jeff Roberts / RAD Game Tools, 2015/10/20\r
+// http://github.com/nothings/stb\r
+//\r
+// allowed types:  sc uidBboXx p AaGgEef n\r
+// lengths      :  hh h ll j z t I64 I32 I\r
+//\r
+// Contributors:\r
+//    Fabian "ryg" Giesen (reformatting)\r
+//    github:aganm (attribute format)\r
+//\r
+// Contributors (bugfixes):\r
+//    github:d26435\r
+//    github:trex78\r
+//    github:account-login\r
+//    Jari Komppa (SI suffixes)\r
+//    Rohit Nirmal\r
+//    Marcin Wojdyr\r
+//    Leonard Ritter\r
+//    Stefano Zanotti\r
+//    Adam Allison\r
+//    Arvid Gerstmann\r
+//    Markus Kolb\r
+//\r
+// LICENSE:\r
+//\r
+//   See end of file for license information.\r
+\r
+#ifndef STB_SPRINTF_H_INCLUDE\r
+#define STB_SPRINTF_H_INCLUDE\r
+\r
+/*\r
+Single file sprintf replacement.\r
+\r
+Originally written by Jeff Roberts at RAD Game Tools - 2015/10/20.\r
+Hereby placed in public domain.\r
+\r
+This is a full sprintf replacement that supports everything that\r
+the C runtime sprintfs support, including float/double, 64-bit integers,\r
+hex floats, field parameters (%*.*d stuff), length reads backs, etc.\r
+\r
+Why would you need this if sprintf already exists?  Well, first off,\r
+it's *much* faster (see below). It's also much smaller than the CRT\r
+versions code-space-wise. We've also added some simple improvements\r
+that are super handy (commas in thousands, callbacks at buffer full,\r
+for example). Finally, the format strings for MSVC and GCC differ\r
+for 64-bit integers (among other small things), so this lets you use\r
+the same format strings in cross platform code.\r
+\r
+It uses the standard single file trick of being both the header file\r
+and the source itself. If you just include it normally, you just get\r
+the header file function definitions. To get the code, you include\r
+it from a C or C++ file and define STB_SPRINTF_IMPLEMENTATION first.\r
+\r
+It only uses va_args macros from the C runtime to do it's work. It\r
+does cast doubles to S64s and shifts and divides U64s, which does\r
+drag in CRT code on most platforms.\r
+\r
+It compiles to roughly 8K with float support, and 4K without.\r
+As a comparison, when using MSVC static libs, calling sprintf drags\r
+in 16K.\r
+\r
+API:\r
+====\r
+int stbsp_sprintf( char * buf, char const * fmt, ... )\r
+int stbsp_snprintf( char * buf, int count, char const * fmt, ... )\r
+  Convert an arg list into a buffer.  stbsp_snprintf always returns\r
+  a zero-terminated string (unlike regular snprintf).\r
+\r
+int stbsp_vsprintf( char * buf, char const * fmt, va_list va )\r
+int stbsp_vsnprintf( char * buf, int count, char const * fmt, va_list va )\r
+  Convert a va_list arg list into a buffer.  stbsp_vsnprintf always returns\r
+  a zero-terminated string (unlike regular snprintf).\r
+\r
+int stbsp_vsprintfcb( STBSP_SPRINTFCB * callback, void * user, char * buf, char const * fmt, va_list va )\r
+    typedef char * STBSP_SPRINTFCB( char const * buf, void * user, int len );\r
+  Convert into a buffer, calling back every STB_SPRINTF_MIN chars.\r
+  Your callback can then copy the chars out, print them or whatever.\r
+  This function is actually the workhorse for everything else.\r
+  The buffer you pass in must hold at least STB_SPRINTF_MIN characters.\r
+    // you return the next buffer to use or 0 to stop converting\r
+\r
+void stbsp_set_separators( char comma, char period )\r
+  Set the comma and period characters to use.\r
+\r
+FLOATS/DOUBLES:\r
+===============\r
+This code uses a internal float->ascii conversion method that uses\r
+doubles with error correction (double-doubles, for ~105 bits of\r
+precision).  This conversion is round-trip perfect - that is, an atof\r
+of the values output here will give you the bit-exact double back.\r
+\r
+One difference is that our insignificant digits will be different than\r
+with MSVC or GCC (but they don't match each other either).  We also\r
+don't attempt to find the minimum length matching float (pre-MSVC15\r
+doesn't either).\r
+\r
+If you don't need float or doubles at all, define STB_SPRINTF_NOFLOAT\r
+and you'll save 4K of code space.\r
+\r
+64-BIT INTS:\r
+============\r
+This library also supports 64-bit integers and you can use MSVC style or\r
+GCC style indicators (%I64d or %lld).  It supports the C99 specifiers\r
+for size_t and ptr_diff_t (%jd %zd) as well.\r
+\r
+EXTRAS:\r
+=======\r
+Like some GCCs, for integers and floats, you can use a ' (single quote)\r
+specifier and commas will be inserted on the thousands: "%'d" on 12345\r
+would print 12,345.\r
+\r
+For integers and floats, you can use a "$" specifier and the number\r
+will be converted to float and then divided to get kilo, mega, giga or\r
+tera and then printed, so "%$d" 1000 is "1.0 k", "%$.2d" 2536000 is\r
+"2.53 M", etc. For byte values, use two $:s, like "%$$d" to turn\r
+2536000 to "2.42 Mi". If you prefer JEDEC suffixes to SI ones, use three\r
+$:s: "%$$$d" -> "2.42 M". To remove the space between the number and the\r
+suffix, add "_" specifier: "%_$d" -> "2.53M".\r
+\r
+In addition to octal and hexadecimal conversions, you can print\r
+integers in binary: "%b" for 256 would print 100.\r
+\r
+PERFORMANCE vs MSVC 2008 32-/64-bit (GCC is even slower than MSVC):\r
+===================================================================\r
+"%d" across all 32-bit ints (4.8x/4.0x faster than 32-/64-bit MSVC)\r
+"%24d" across all 32-bit ints (4.5x/4.2x faster)\r
+"%x" across all 32-bit ints (4.5x/3.8x faster)\r
+"%08x" across all 32-bit ints (4.3x/3.8x faster)\r
+"%f" across e-10 to e+10 floats (7.3x/6.0x faster)\r
+"%e" across e-10 to e+10 floats (8.1x/6.0x faster)\r
+"%g" across e-10 to e+10 floats (10.0x/7.1x faster)\r
+"%f" for values near e-300 (7.9x/6.5x faster)\r
+"%f" for values near e+300 (10.0x/9.1x faster)\r
+"%e" for values near e-300 (10.1x/7.0x faster)\r
+"%e" for values near e+300 (9.2x/6.0x faster)\r
+"%.320f" for values near e-300 (12.6x/11.2x faster)\r
+"%a" for random values (8.6x/4.3x faster)\r
+"%I64d" for 64-bits with 32-bit values (4.8x/3.4x faster)\r
+"%I64d" for 64-bits > 32-bit values (4.9x/5.5x faster)\r
+"%s%s%s" for 64 char strings (7.1x/7.3x faster)\r
+"...512 char string..." ( 35.0x/32.5x faster!)\r
+*/\r
+\r
+#if defined(__clang__)\r
+ #if defined(__has_feature) && defined(__has_attribute)\r
+  #if __has_feature(address_sanitizer)\r
+   #if __has_attribute(__no_sanitize__)\r
+    #define STBSP__ASAN __attribute__((__no_sanitize__("address")))\r
+   #elif __has_attribute(__no_sanitize_address__)\r
+    #define STBSP__ASAN __attribute__((__no_sanitize_address__))\r
+   #elif __has_attribute(__no_address_safety_analysis__)\r
+    #define STBSP__ASAN __attribute__((__no_address_safety_analysis__))\r
+   #endif\r
+  #endif\r
+ #endif\r
+#elif defined(__GNUC__) && (__GNUC__ >= 5 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))\r
+ #if defined(__SANITIZE_ADDRESS__) && __SANITIZE_ADDRESS__\r
+  #define STBSP__ASAN __attribute__((__no_sanitize_address__))\r
+ #endif\r
+#endif\r
+\r
+#ifndef STBSP__ASAN\r
+#define STBSP__ASAN\r
+#endif\r
+\r
+#ifdef STB_SPRINTF_STATIC\r
+#define STBSP__PUBLICDEC static\r
+#define STBSP__PUBLICDEF static STBSP__ASAN\r
+#else\r
+#ifdef __cplusplus\r
+#define STBSP__PUBLICDEC extern "C"\r
+#define STBSP__PUBLICDEF extern "C" STBSP__ASAN\r
+#else\r
+#define STBSP__PUBLICDEC extern\r
+#define STBSP__PUBLICDEF STBSP__ASAN\r
+#endif\r
+#endif\r
+\r
+#if defined(__has_attribute)\r
+ #if __has_attribute(format)\r
+   #define STBSP__ATTRIBUTE_FORMAT(fmt,va) __attribute__((format(printf,fmt,va)))\r
+ #endif\r
+#endif\r
+\r
+#ifndef STBSP__ATTRIBUTE_FORMAT\r
+#define STBSP__ATTRIBUTE_FORMAT(fmt,va)\r
+#endif\r
+\r
+#ifdef _MSC_VER\r
+#define STBSP__NOTUSED(v)  (void)(v)\r
+#else\r
+#define STBSP__NOTUSED(v)  (void)sizeof(v)\r
+#endif\r
+\r
+#include <stdarg.h> // for va_arg(), va_list()\r
+#include <stddef.h> // size_t, ptrdiff_t\r
+\r
+#ifndef STB_SPRINTF_MIN\r
+#define STB_SPRINTF_MIN 512 // how many characters per callback\r
+#endif\r
+typedef char *STBSP_SPRINTFCB(const char *buf, void *user, int len);\r
+\r
+#ifndef STB_SPRINTF_DECORATE\r
+#define STB_SPRINTF_DECORATE(name) stbsp_##name // define this before including if you want to change the names\r
+#endif\r
+\r
+STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(vsprintf)(char *buf, char const *fmt, va_list va);\r
+STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(vsnprintf)(char *buf, int count, char const *fmt, va_list va);\r
+STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(sprintf)(char *buf, char const *fmt, ...) STBSP__ATTRIBUTE_FORMAT(2,3);\r
+STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(snprintf)(char *buf, int count, char const *fmt, ...) STBSP__ATTRIBUTE_FORMAT(3,4);\r
+\r
+STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(vsprintfcb)(STBSP_SPRINTFCB *callback, void *user, char *buf, char const *fmt, va_list va);\r
+STBSP__PUBLICDEC void STB_SPRINTF_DECORATE(set_separators)(char comma, char period);\r
+\r
+#endif // STB_SPRINTF_H_INCLUDE\r
+// << fold <<\r
+\r
+// ----------------------------------------------------------------\r
+\r
+// typedefs\r
+typedef unsigned int uint;\r
+typedef uint8_t  uint8;\r
+typedef uint16_t uint16;\r
+typedef uint32_t uint32;\r
+typedef uint64_t uint64;\r
+\r
+typedef int8_t  int8;\r
+typedef int16_t int16;\r
+typedef int32_t int32;\r
+typedef int64_t int64;\r
+\r
+typedef float  float32;\r
+typedef double float64;\r
+\r
+typedef wchar_t wchar;\r
+\r
+typedef const char*  cstr;\r
+typedef const wchar* wstr;\r
+\r
+#define ASSERT(condition) if (!(condition)) panic_assertion(#condition, stderr, __FILE__, __LINE__)\r
+\r
+#define ARRAY_LEN(arr) (sizeof(arr) / sizeof(arr[0]))\r
+\r
+#define STRUCT(name) typedef struct name name\r
+#define ENUM(name)   typedef enum name name\r
+#define DYNAMIC_ARRAY(type, name) type* name = NULL\r
+\r
+// Struct pre-decls\r
+\r
+STRUCT(Arena);\r
+\r
+void panic_assertion(cstr msg, FILE* file, cstr filename, int line);\r
+\r
+//\r
+// OS\r
+//\r
+\r
+void os_get_timedate(Arena* a);\r
+\r
+//\r
+// Winapi\r
+//\r
+#ifdef _WIN32\r
+#define WINAPI_ERROR_MSG_BUFF_SIZE 1024\r
+static char winapi_error_msg_buff[WINAPI_ERROR_MSG_BUFF_SIZE];\r
+#define WINAPI_OUTPUT_STR_BUFF_SIZE (16*1024)\r
+static CHAR_INFO winapi_output_str_buff[WINAPI_OUTPUT_STR_BUFF_SIZE];\r
+\r
+bool output_str(cstr text);\r
+bool output_strn(cstr text, size_t text_len);\r
+cstr winapi_get_last_error_str(void);\r
+cstr winapi_get_current_working_directory(Arena* arena);\r
+#endif\r
+//\r
+// logging\r
+//\r
+\r
+// FLUSH_ON_LOG - define this macro to fflush() on every call to log() not defined by default.\r
+\r
+typedef enum {\r
+  C_LOG_INFO = 0,\r
+  C_LOG_ERROR,\r
+  C_LOG_WARNING,\r
+  C_LOG_COUNT,\r
+} Log_type;\r
+\r
+void log_file(Log_type type, FILE* file, cstr fmt, ...);\r
+#define log_f(type, fmt, ...)  log_file(type, stdout, fmt, ##__VA_ARGS__)\r
+#define log_info(fmt, ...)     log_f(C_LOG_INFO, fmt, ##__VA_ARGS__)\r
+#define log_error(fmt, ...)    log_f(C_LOG_ERROR, fmt, ##__VA_ARGS__)\r
+#define log_warning(fmt, ...)  log_f(C_LOG_WARNING, fmt, ##__VA_ARGS__)\r
+\r
+//\r
+// File\r
+//\r
+\r
+// reads entire file and gives back the string holding the contents. (caller must be responsible for freeing the string!)\r
+const char* slurp_file(const char* filename);\r
+\r
+//\r
+// ### Allocators ###\r
+//\r
+\r
+//\r
+// Arena\r
+//\r
+\r
+#define ARENA_BUFF_INITIAL_SIZE (1024*4)\r
+\r
+struct Arena {\r
+  void* buff;\r
+  uint64 buff_size;\r
+  void* ptr;\r
+};\r
+\r
+// pass size 0 to get ARENA_BUFF_INITIAL_SIZE\r
+Arena Arena_make(size_t size);\r
+void* Arena_alloc(Arena* a, size_t size);\r
+void Arena_reset(Arena* a);\r
+void Arena_free(Arena* a);\r
+\r
+#define Arena_alloc_str(a, fmt, ...)  Arena_alloc(&(a), sizeof(char)*stbsp_snprintf((a).ptr, (int)((a).buff_size - ((uint8*)(a).ptr - (uint8*)(a).buff)), (fmt), __VA_ARGS__)+1)\r
+#define Arena_alloc_wstr(a, fmt, ...) Arena_alloc(&a, sizeof(char)*wprintf(a.ptr, a.buff_size - ((uint8*)a.ptr - (uint8*)a.buff), (fmt), __VA_ARGS__)+1)\r
+\r
+//\r
+// String\r
+//\r
+\r
+bool cstr_to_wstr(Arena* warena, cstr str);\r
+\r
+//\r
+// String view\r
+//\r
+\r
+typedef struct {\r
+  cstr data;\r
+  size_t count;\r
+} String_view;\r
+\r
+#define SV_FMT "%.*s"\r
+#define SV_ARG(sv) (int)sv.count, sv.data\r
+\r
+#define SV(cstr) (String_view){.data = cstr, strlen(cstr)}\r
+\r
+void sv_print_dumb(String_view sv);\r
+String_view sv_from_cstr(const char* cstr); // Actually just use SV(cstr) macro...\r
+String_view sv_lpop(String_view* sv, uint32 n);\r
+String_view sv_lpop_until_predicate(String_view* sv, int(*predicate)(int));\r
+String_view sv_rpop_until_predicate(String_view* sv, int(*predicate)(int));\r
+String_view sv_lpop_until_char(String_view* sv, char ch);\r
+String_view sv_rpop_until_char(String_view* sv, char ch);\r
+void sv_lremove(String_view* sv, size_t n);\r
+void sv_rremove(String_view* sv, size_t n);\r
+void sv_lremove_until_char(String_view* sv, char ch);\r
+void sv_rremove_until_char(String_view* sv, char ch);\r
+void sv_lremove_until_char_after(String_view* sv, char ch);\r
+void sv_rremove_until_char_after(String_view* sv, char ch);\r
+void sv_ltrim(String_view* sv);\r
+void sv_rtrim(String_view* sv);\r
+void sv_trim(String_view* sv);\r
+char* sv_to_cstr(String_view sv);\r
+int32  sv_to_int(String_view sv);\r
+uint64 sv_to_uint64(String_view sv);\r
+uint8 sv_to_uint8_hex(String_view sv);\r
+void*  sv_to_ptr(String_view sv);\r
+float32 sv_to_float(String_view sv);\r
+bool sv_contains_char(String_view sv, char ch);\r
+bool sv_is_hex_numbers(String_view sv);\r
+bool sv_equals(String_view sv1, String_view sv2);\r
+\r
+//\r
+// Args\r
+//\r
+\r
+cstr shift_args(int* argc, char*** argv);\r
+\r
+#endif /* _STDLIB_H_ */\r
+\r
+//////////////////////////////////////////////////\r
+#ifdef COMMONLIB_IMPLEMENTATION\r
+#include <string.h>\r
+#include <errno.h>\r
+#include <stdlib.h>\r
+\r
+//\r
+// External libs --------------------------------------------------\r
+//\r
+\r
+// >> stb_ds.h >>\r
+\r
+#define STB_DS_IMPLEMENTATION\r
+#ifdef STB_DS_IMPLEMENTATION\r
+#include <assert.h>\r
+#include <string.h>\r
+\r
+#ifndef STBDS_ASSERT\r
+#define STBDS_ASSERT_WAS_UNDEFINED\r
+#define STBDS_ASSERT(x)   ((void) 0)\r
+#endif\r
+\r
+#ifdef STBDS_STATISTICS\r
+#define STBDS_STATS(x)   x\r
+size_t stbds_array_grow;\r
+size_t stbds_hash_grow;\r
+size_t stbds_hash_shrink;\r
+size_t stbds_hash_rebuild;\r
+size_t stbds_hash_probes;\r
+size_t stbds_hash_alloc;\r
+size_t stbds_rehash_probes;\r
+size_t stbds_rehash_items;\r
+#else\r
+#define STBDS_STATS(x)\r
+#endif\r
+\r
+//\r
+// stbds_arr implementation\r
+//\r
+\r
+//int *prev_allocs[65536];\r
+//int num_prev;\r
+\r
+void *stbds_arrgrowf(void *a, size_t elemsize, size_t addlen, size_t min_cap)\r
+{\r
+  stbds_array_header temp={0}; // force debugging\r
+  void *b;\r
+  size_t min_len = stbds_arrlen(a) + addlen;\r
+  (void) sizeof(temp);\r
+\r
+  // compute the minimum capacity needed\r
+  if (min_len > min_cap)\r
+    min_cap = min_len;\r
+\r
+  if (min_cap <= stbds_arrcap(a))\r
+    return a;\r
+\r
+  // increase needed capacity to guarantee O(1) amortized\r
+  if (min_cap < 2 * stbds_arrcap(a))\r
+    min_cap = 2 * stbds_arrcap(a);\r
+  else if (min_cap < 4)\r
+    min_cap = 4;\r
+\r
+  //if (num_prev < 65536) if (a) prev_allocs[num_prev++] = (int *) ((char *) a+1);\r
+  //if (num_prev == 2201)\r
+  //  num_prev = num_prev;\r
+  b = STBDS_REALLOC(NULL, (a) ? stbds_header(a) : 0, elemsize * min_cap + sizeof(stbds_array_header));\r
+  //if (num_prev < 65536) prev_allocs[num_prev++] = (int *) (char *) b;\r
+  b = (char *) b + sizeof(stbds_array_header);\r
+  if (a == NULL) {\r
+    stbds_header(b)->length = 0;\r
+    stbds_header(b)->hash_table = 0;\r
+    stbds_header(b)->temp = 0;\r
+  } else {\r
+    STBDS_STATS(++stbds_array_grow);\r
+  }\r
+  stbds_header(b)->capacity = min_cap;\r
+\r
+  return b;\r
+}\r
+\r
+void stbds_arrfreef(void *a)\r
+{\r
+  STBDS_FREE(NULL, stbds_header(a));\r
+}\r
+\r
+//\r
+// stbds_hm hash table implementation\r
+//\r
+\r
+#ifdef STBDS_INTERNAL_SMALL_BUCKET\r
+#define STBDS_BUCKET_LENGTH      4\r
+#else\r
+#define STBDS_BUCKET_LENGTH      8\r
+#endif\r
+\r
+#define STBDS_BUCKET_SHIFT      (STBDS_BUCKET_LENGTH == 8 ? 3 : 2)\r
+#define STBDS_BUCKET_MASK       (STBDS_BUCKET_LENGTH-1)\r
+#define STBDS_CACHE_LINE_SIZE   64\r
+\r
+#define STBDS_ALIGN_FWD(n,a)   (((n) + (a) - 1) & ~((a)-1))\r
+\r
+typedef struct\r
+{\r
+   size_t    hash [STBDS_BUCKET_LENGTH];\r
+   ptrdiff_t index[STBDS_BUCKET_LENGTH];\r
+} stbds_hash_bucket; // in 32-bit, this is one 64-byte cache line; in 64-bit, each array is one 64-byte cache line\r
+\r
+typedef struct\r
+{\r
+  char * temp_key; // this MUST be the first field of the hash table\r
+  size_t slot_count;\r
+  size_t used_count;\r
+  size_t used_count_threshold;\r
+  size_t used_count_shrink_threshold;\r
+  size_t tombstone_count;\r
+  size_t tombstone_count_threshold;\r
+  size_t seed;\r
+  size_t slot_count_log2;\r
+  stbds_string_arena string;\r
+  stbds_hash_bucket *storage; // not a separate allocation, just 64-byte aligned storage after this struct\r
+} stbds_hash_index;\r
+\r
+#define STBDS_INDEX_EMPTY    -1\r
+#define STBDS_INDEX_DELETED  -2\r
+#define STBDS_INDEX_IN_USE(x)  ((x) >= 0)\r
+\r
+#define STBDS_HASH_EMPTY      0\r
+#define STBDS_HASH_DELETED    1\r
+\r
+static size_t stbds_hash_seed=0x31415926;\r
+\r
+void stbds_rand_seed(size_t seed)\r
+{\r
+  stbds_hash_seed = seed;\r
+}\r
+\r
+#define stbds_load_32_or_64(var, temp, v32, v64_hi, v64_lo)                                          \\r
+  temp = v64_lo ^ v32, temp <<= 16, temp <<= 16, temp >>= 16, temp >>= 16, /* discard if 32-bit */   \\r
+  var = v64_hi, var <<= 16, var <<= 16,                                    /* discard if 32-bit */   \\r
+  var ^= temp ^ v32\r
+\r
+#define STBDS_SIZE_T_BITS           ((sizeof (size_t)) * 8)\r
+\r
+static size_t stbds_probe_position(size_t hash, size_t slot_count, size_t slot_log2)\r
+{\r
+  size_t pos;\r
+  STBDS_NOTUSED(slot_log2);\r
+  pos = hash & (slot_count-1);\r
+  #ifdef STBDS_INTERNAL_BUCKET_START\r
+  pos &= ~STBDS_BUCKET_MASK;\r
+  #endif\r
+  return pos;\r
+}\r
+\r
+static size_t stbds_log2(size_t slot_count)\r
+{\r
+  size_t n=0;\r
+  while (slot_count > 1) {\r
+    slot_count >>= 1;\r
+    ++n;\r
+  }\r
+  return n;\r
+}\r
+\r
+static stbds_hash_index *stbds_make_hash_index(size_t slot_count, stbds_hash_index *ot)\r
+{\r
+  stbds_hash_index *t;\r
+  t = (stbds_hash_index *) STBDS_REALLOC(NULL,0,(slot_count >> STBDS_BUCKET_SHIFT) * sizeof(stbds_hash_bucket) + sizeof(stbds_hash_index) + STBDS_CACHE_LINE_SIZE-1);\r
+  t->storage = (stbds_hash_bucket *) STBDS_ALIGN_FWD((size_t) (t+1), STBDS_CACHE_LINE_SIZE);\r
+  t->slot_count = slot_count;\r
+  t->slot_count_log2 = stbds_log2(slot_count);\r
+  t->tombstone_count = 0;\r
+  t->used_count = 0;\r
+\r
+  #if 0 // A1\r
+  t->used_count_threshold        = slot_count*12/16; // if 12/16th of table is occupied, grow\r
+  t->tombstone_count_threshold   = slot_count* 2/16; // if tombstones are 2/16th of table, rebuild\r
+  t->used_count_shrink_threshold = slot_count* 4/16; // if table is only 4/16th full, shrink\r
+  #elif 1 // A2\r
+  //t->used_count_threshold        = slot_count*12/16; // if 12/16th of table is occupied, grow\r
+  //t->tombstone_count_threshold   = slot_count* 3/16; // if tombstones are 3/16th of table, rebuild\r
+  //t->used_count_shrink_threshold = slot_count* 4/16; // if table is only 4/16th full, shrink\r
+\r
+  // compute without overflowing\r
+  t->used_count_threshold        = slot_count - (slot_count>>2);\r
+  t->tombstone_count_threshold   = (slot_count>>3) + (slot_count>>4);\r
+  t->used_count_shrink_threshold = slot_count >> 2;\r
+\r
+  #elif 0 // B1\r
+  t->used_count_threshold        = slot_count*13/16; // if 13/16th of table is occupied, grow\r
+  t->tombstone_count_threshold   = slot_count* 2/16; // if tombstones are 2/16th of table, rebuild\r
+  t->used_count_shrink_threshold = slot_count* 5/16; // if table is only 5/16th full, shrink\r
+  #else // C1\r
+  t->used_count_threshold        = slot_count*14/16; // if 14/16th of table is occupied, grow\r
+  t->tombstone_count_threshold   = slot_count* 2/16; // if tombstones are 2/16th of table, rebuild\r
+  t->used_count_shrink_threshold = slot_count* 6/16; // if table is only 6/16th full, shrink\r
+  #endif\r
+  // Following statistics were measured on a Core i7-6700 @ 4.00Ghz, compiled with clang 7.0.1 -O2\r
+    // Note that the larger tables have high variance as they were run fewer times\r
+  //     A1            A2          B1           C1\r
+  //    0.10ms :     0.10ms :     0.10ms :     0.11ms :      2,000 inserts creating 2K table\r
+  //    0.96ms :     0.95ms :     0.97ms :     1.04ms :     20,000 inserts creating 20K table\r
+  //   14.48ms :    14.46ms :    10.63ms :    11.00ms :    200,000 inserts creating 200K table\r
+  //  195.74ms :   196.35ms :   203.69ms :   214.92ms :  2,000,000 inserts creating 2M table\r
+  // 2193.88ms :  2209.22ms :  2285.54ms :  2437.17ms : 20,000,000 inserts creating 20M table\r
+  //   65.27ms :    53.77ms :    65.33ms :    65.47ms : 500,000 inserts & deletes in 2K table\r
+  //   72.78ms :    62.45ms :    71.95ms :    72.85ms : 500,000 inserts & deletes in 20K table\r
+  //   89.47ms :    77.72ms :    96.49ms :    96.75ms : 500,000 inserts & deletes in 200K table\r
+  //   97.58ms :    98.14ms :    97.18ms :    97.53ms : 500,000 inserts & deletes in 2M table\r
+  //  118.61ms :   119.62ms :   120.16ms :   118.86ms : 500,000 inserts & deletes in 20M table\r
+  //  192.11ms :   194.39ms :   196.38ms :   195.73ms : 500,000 inserts & deletes in 200M table\r
+\r
+  if (slot_count <= STBDS_BUCKET_LENGTH)\r
+    t->used_count_shrink_threshold = 0;\r
+  // to avoid infinite loop, we need to guarantee that at least one slot is empty and will terminate probes\r
+  STBDS_ASSERT(t->used_count_threshold + t->tombstone_count_threshold < t->slot_count);\r
+  STBDS_STATS(++stbds_hash_alloc);\r
+  if (ot) {\r
+    t->string = ot->string;\r
+    // reuse old seed so we can reuse old hashes so below "copy out old data" doesn't do any hashing\r
+    t->seed = ot->seed;\r
+  } else {\r
+    size_t a,b,temp;\r
+    memset(&t->string, 0, sizeof(t->string));\r
+    t->seed = stbds_hash_seed;\r
+    // LCG\r
+    // in 32-bit, a =          2147001325   b =  715136305\r
+    // in 64-bit, a = 2862933555777941757   b = 3037000493\r
+    stbds_load_32_or_64(a,temp, 2147001325, 0x27bb2ee6, 0x87b0b0fd);\r
+    stbds_load_32_or_64(b,temp,  715136305,          0, 0xb504f32d);\r
+    stbds_hash_seed = stbds_hash_seed  * a + b;\r
+  }\r
+\r
+  {\r
+    size_t i,j;\r
+    for (i=0; i < slot_count >> STBDS_BUCKET_SHIFT; ++i) {\r
+      stbds_hash_bucket *b = &t->storage[i];\r
+      for (j=0; j < STBDS_BUCKET_LENGTH; ++j)\r
+        b->hash[j] = STBDS_HASH_EMPTY;\r
+      for (j=0; j < STBDS_BUCKET_LENGTH; ++j)\r
+        b->index[j] = STBDS_INDEX_EMPTY;\r
+    }\r
+  }\r
+\r
+  // copy out the old data, if any\r
+  if (ot) {\r
+    size_t i,j;\r
+    t->used_count = ot->used_count;\r
+    for (i=0; i < ot->slot_count >> STBDS_BUCKET_SHIFT; ++i) {\r
+      stbds_hash_bucket *ob = &ot->storage[i];\r
+      for (j=0; j < STBDS_BUCKET_LENGTH; ++j) {\r
+        if (STBDS_INDEX_IN_USE(ob->index[j])) {\r
+          size_t hash = ob->hash[j];\r
+          size_t pos = stbds_probe_position(hash, t->slot_count, t->slot_count_log2);\r
+          size_t step = STBDS_BUCKET_LENGTH;\r
+          STBDS_STATS(++stbds_rehash_items);\r
+          for (;;) {\r
+            size_t limit,z;\r
+            stbds_hash_bucket *bucket;\r
+            bucket = &t->storage[pos >> STBDS_BUCKET_SHIFT];\r
+            STBDS_STATS(++stbds_rehash_probes);\r
+\r
+            for (z=pos & STBDS_BUCKET_MASK; z < STBDS_BUCKET_LENGTH; ++z) {\r
+              if (bucket->hash[z] == 0) {\r
+                bucket->hash[z] = hash;\r
+                bucket->index[z] = ob->index[j];\r
+                goto done;\r
+              }\r
+            }\r
+\r
+            limit = pos & STBDS_BUCKET_MASK;\r
+            for (z = 0; z < limit; ++z) {\r
+              if (bucket->hash[z] == 0) {\r
+                bucket->hash[z] = hash;\r
+                bucket->index[z] = ob->index[j];\r
+                goto done;\r
+              }\r
+            }\r
+\r
+            pos += step;                  // quadratic probing\r
+            step += STBDS_BUCKET_LENGTH;\r
+            pos &= (t->slot_count-1);\r
+          }\r
+        }\r
+       done:\r
+        ;\r
+      }\r
+    }\r
+  }\r
+\r
+  return t;\r
+}\r
+\r
+#define STBDS_ROTATE_LEFT(val, n)   (((val) << (n)) | ((val) >> (STBDS_SIZE_T_BITS - (n))))\r
+#define STBDS_ROTATE_RIGHT(val, n)  (((val) >> (n)) | ((val) << (STBDS_SIZE_T_BITS - (n))))\r
+\r
+size_t stbds_hash_string(char *str, size_t seed)\r
+{\r
+  size_t hash = seed;\r
+  while (*str)\r
+     hash = STBDS_ROTATE_LEFT(hash, 9) + (unsigned char) *str++;\r
+\r
+  // Thomas Wang 64-to-32 bit mix function, hopefully also works in 32 bits\r
+  hash ^= seed;\r
+  hash = (~hash) + (hash << 18);\r
+  hash ^= hash ^ STBDS_ROTATE_RIGHT(hash,31);\r
+  hash = hash * 21;\r
+  hash ^= hash ^ STBDS_ROTATE_RIGHT(hash,11);\r
+  hash += (hash << 6);\r
+  hash ^= STBDS_ROTATE_RIGHT(hash,22);\r
+  return hash+seed;\r
+}\r
+\r
+#ifdef STBDS_SIPHASH_2_4\r
+#define STBDS_SIPHASH_C_ROUNDS 2\r
+#define STBDS_SIPHASH_D_ROUNDS 4\r
+typedef int STBDS_SIPHASH_2_4_can_only_be_used_in_64_bit_builds[sizeof(size_t) == 8 ? 1 : -1];\r
+#endif\r
+\r
+#ifndef STBDS_SIPHASH_C_ROUNDS\r
+#define STBDS_SIPHASH_C_ROUNDS 1\r
+#endif\r
+#ifndef STBDS_SIPHASH_D_ROUNDS\r
+#define STBDS_SIPHASH_D_ROUNDS 1\r
+#endif\r
+\r
+#ifdef _MSC_VER\r
+#pragma warning(push)\r
+#pragma warning(disable:4127) // conditional expression is constant, for do..while(0) and sizeof()==\r
+#endif\r
+\r
+static size_t stbds_siphash_bytes(void *p, size_t len, size_t seed)\r
+{\r
+  unsigned char *d = (unsigned char *) p;\r
+  size_t i,j;\r
+  size_t v0,v1,v2,v3, data;\r
+\r
+  // hash that works on 32- or 64-bit registers without knowing which we have\r
+  // (computes different results on 32-bit and 64-bit platform)\r
+  // derived from siphash, but on 32-bit platforms very different as it uses 4 32-bit state not 4 64-bit\r
+  v0 = ((((size_t) 0x736f6d65 << 16) << 16) + 0x70736575) ^  seed;\r
+  v1 = ((((size_t) 0x646f7261 << 16) << 16) + 0x6e646f6d) ^ ~seed;\r
+  v2 = ((((size_t) 0x6c796765 << 16) << 16) + 0x6e657261) ^  seed;\r
+  v3 = ((((size_t) 0x74656462 << 16) << 16) + 0x79746573) ^ ~seed;\r
+\r
+  #ifdef STBDS_TEST_SIPHASH_2_4\r
+  // hardcoded with key material in the siphash test vectors\r
+  v0 ^= 0x0706050403020100ull ^  seed;\r
+  v1 ^= 0x0f0e0d0c0b0a0908ull ^ ~seed;\r
+  v2 ^= 0x0706050403020100ull ^  seed;\r
+  v3 ^= 0x0f0e0d0c0b0a0908ull ^ ~seed;\r
+  #endif\r
+\r
+  #define STBDS_SIPROUND() \\r
+    do {                   \\r
+      v0 += v1; v1 = STBDS_ROTATE_LEFT(v1, 13);  v1 ^= v0; v0 = STBDS_ROTATE_LEFT(v0,STBDS_SIZE_T_BITS/2); \\r
+      v2 += v3; v3 = STBDS_ROTATE_LEFT(v3, 16);  v3 ^= v2;                                                 \\r
+      v2 += v1; v1 = STBDS_ROTATE_LEFT(v1, 17);  v1 ^= v2; v2 = STBDS_ROTATE_LEFT(v2,STBDS_SIZE_T_BITS/2); \\r
+      v0 += v3; v3 = STBDS_ROTATE_LEFT(v3, 21);  v3 ^= v0;                                                 \\r
+    } while (0)\r
+\r
+  for (i=0; i+sizeof(size_t) <= len; i += sizeof(size_t), d += sizeof(size_t)) {\r
+    data = d[0] | (d[1] << 8) | (d[2] << 16) | (d[3] << 24);\r
+    data |= (size_t) (d[4] | (d[5] << 8) | (d[6] << 16) | (d[7] << 24)) << 16 << 16; // discarded if size_t == 4\r
+\r
+    v3 ^= data;\r
+    for (j=0; j < STBDS_SIPHASH_C_ROUNDS; ++j)\r
+      STBDS_SIPROUND();\r
+    v0 ^= data;\r
+  }\r
+  data = len << (STBDS_SIZE_T_BITS-8);\r
+  switch (len - i) {\r
+    case 7: data |= ((size_t) d[6] << 24) << 24; // fall through\r
+    case 6: data |= ((size_t) d[5] << 20) << 20; // fall through\r
+    case 5: data |= ((size_t) d[4] << 16) << 16; // fall through\r
+    case 4: data |= (d[3] << 24); // fall through\r
+    case 3: data |= (d[2] << 16); // fall through\r
+    case 2: data |= (d[1] << 8); // fall through\r
+    case 1: data |= d[0]; // fall through\r
+    case 0: break;\r
+  }\r
+  v3 ^= data;\r
+  for (j=0; j < STBDS_SIPHASH_C_ROUNDS; ++j)\r
+    STBDS_SIPROUND();\r
+  v0 ^= data;\r
+  v2 ^= 0xff;\r
+  for (j=0; j < STBDS_SIPHASH_D_ROUNDS; ++j)\r
+    STBDS_SIPROUND();\r
+\r
+#ifdef STBDS_SIPHASH_2_4\r
+  return v0^v1^v2^v3;\r
+#else\r
+  return v1^v2^v3; // slightly stronger since v0^v3 in above cancels out final round operation? I tweeted at the authors of SipHash about this but they didn't reply\r
+#endif\r
+}\r
+\r
+size_t stbds_hash_bytes(void *p, size_t len, size_t seed)\r
+{\r
+#ifdef STBDS_SIPHASH_2_4\r
+  return stbds_siphash_bytes(p,len,seed);\r
+#else\r
+  unsigned char *d = (unsigned char *) p;\r
+\r
+  if (len == 4) {\r
+    unsigned int hash = d[0] | (d[1] << 8) | (d[2] << 16) | (d[3] << 24);\r
+    #if 0\r
+    // HASH32-A  Bob Jenkin's hash function w/o large constants\r
+    hash ^= seed;\r
+    hash -= (hash<<6);\r
+    hash ^= (hash>>17);\r
+    hash -= (hash<<9);\r
+    hash ^= seed;\r
+    hash ^= (hash<<4);\r
+    hash -= (hash<<3);\r
+    hash ^= (hash<<10);\r
+    hash ^= (hash>>15);\r
+    #elif 1\r
+    // HASH32-BB  Bob Jenkin's presumably-accidental version of Thomas Wang hash with rotates turned into shifts.\r
+    // Note that converting these back to rotates makes it run a lot slower, presumably due to collisions, so I'm\r
+    // not really sure what's going on.\r
+    hash ^= seed;\r
+    hash = (hash ^ 61) ^ (hash >> 16);\r
+    hash = hash + (hash << 3);\r
+    hash = hash ^ (hash >> 4);\r
+    hash = hash * 0x27d4eb2d;\r
+    hash ^= seed;\r
+    hash = hash ^ (hash >> 15);\r
+    #else  // HASH32-C   -  Murmur3\r
+    hash ^= seed;\r
+    hash *= 0xcc9e2d51;\r
+    hash = (hash << 17) | (hash >> 15);\r
+    hash *= 0x1b873593;\r
+    hash ^= seed;\r
+    hash = (hash << 19) | (hash >> 13);\r
+    hash = hash*5 + 0xe6546b64;\r
+    hash ^= hash >> 16;\r
+    hash *= 0x85ebca6b;\r
+    hash ^= seed;\r
+    hash ^= hash >> 13;\r
+    hash *= 0xc2b2ae35;\r
+    hash ^= hash >> 16;\r
+    #endif\r
+    // Following statistics were measured on a Core i7-6700 @ 4.00Ghz, compiled with clang 7.0.1 -O2\r
+    // Note that the larger tables have high variance as they were run fewer times\r
+    //  HASH32-A   //  HASH32-BB  //  HASH32-C\r
+    //    0.10ms   //    0.10ms   //    0.10ms :      2,000 inserts creating 2K table\r
+    //    0.96ms   //    0.95ms   //    0.99ms :     20,000 inserts creating 20K table\r
+    //   14.69ms   //   14.43ms   //   14.97ms :    200,000 inserts creating 200K table\r
+    //  199.99ms   //  195.36ms   //  202.05ms :  2,000,000 inserts creating 2M table\r
+    // 2234.84ms   // 2187.74ms   // 2240.38ms : 20,000,000 inserts creating 20M table\r
+    //   55.68ms   //   53.72ms   //   57.31ms : 500,000 inserts & deletes in 2K table\r
+    //   63.43ms   //   61.99ms   //   65.73ms : 500,000 inserts & deletes in 20K table\r
+    //   80.04ms   //   77.96ms   //   81.83ms : 500,000 inserts & deletes in 200K table\r
+    //  100.42ms   //   97.40ms   //  102.39ms : 500,000 inserts & deletes in 2M table\r
+    //  119.71ms   //  120.59ms   //  121.63ms : 500,000 inserts & deletes in 20M table\r
+    //  185.28ms   //  195.15ms   //  187.74ms : 500,000 inserts & deletes in 200M table\r
+    //   15.58ms   //   14.79ms   //   15.52ms : 200,000 inserts creating 200K table with varying key spacing\r
+\r
+    return (((size_t) hash << 16 << 16) | hash) ^ seed;\r
+  } else if (len == 8 && sizeof(size_t) == 8) {\r
+    size_t hash = d[0] | (d[1] << 8) | (d[2] << 16) | (d[3] << 24);\r
+    hash |= (size_t) (d[4] | (d[5] << 8) | (d[6] << 16) | (d[7] << 24)) << 16 << 16; // avoid warning if size_t == 4\r
+    hash ^= seed;\r
+    hash = (~hash) + (hash << 21);\r
+    hash ^= STBDS_ROTATE_RIGHT(hash,24);\r
+    hash *= 265;\r
+    hash ^= STBDS_ROTATE_RIGHT(hash,14);\r
+    hash ^= seed;\r
+    hash *= 21;\r
+    hash ^= STBDS_ROTATE_RIGHT(hash,28);\r
+    hash += (hash << 31);\r
+    hash = (~hash) + (hash << 18);\r
+    return hash;\r
+  } else {\r
+    return stbds_siphash_bytes(p,len,seed);\r
+  }\r
+#endif\r
+}\r
+#ifdef _MSC_VER\r
+#pragma warning(pop)\r
+#endif\r
+\r
+\r
+static int stbds_is_key_equal(void *a, size_t elemsize, void *key, size_t keysize, size_t keyoffset, int mode, size_t i)\r
+{\r
+  if (mode >= STBDS_HM_STRING)\r
+    return 0==strcmp((char *) key, * (char **) ((char *) a + elemsize*i + keyoffset));\r
+  else\r
+    return 0==memcmp(key, (char *) a + elemsize*i + keyoffset, keysize);\r
+}\r
+\r
+#define STBDS_HASH_TO_ARR(x,elemsize) ((char*) (x) - (elemsize))\r
+#define STBDS_ARR_TO_HASH(x,elemsize) ((char*) (x) + (elemsize))\r
+\r
+#define stbds_hash_table(a)  ((stbds_hash_index *) stbds_header(a)->hash_table)\r
+\r
+void stbds_hmfree_func(void *a, size_t elemsize)\r
+{\r
+  if (a == NULL) return;\r
+  if (stbds_hash_table(a) != NULL) {\r
+    if (stbds_hash_table(a)->string.mode == STBDS_SH_STRDUP) {\r
+      size_t i;\r
+      // skip 0th element, which is default\r
+      for (i=1; i < stbds_header(a)->length; ++i)\r
+        STBDS_FREE(NULL, *(char**) ((char *) a + elemsize*i));\r
+    }\r
+    stbds_strreset(&stbds_hash_table(a)->string);\r
+  }\r
+  STBDS_FREE(NULL, stbds_header(a)->hash_table);\r
+  STBDS_FREE(NULL, stbds_header(a));\r
+}\r
+\r
+static ptrdiff_t stbds_hm_find_slot(void *a, size_t elemsize, void *key, size_t keysize, size_t keyoffset, int mode)\r
+{\r
+  void *raw_a = STBDS_HASH_TO_ARR(a,elemsize);\r
+  stbds_hash_index *table = stbds_hash_table(raw_a);\r
+  size_t hash = mode >= STBDS_HM_STRING ? stbds_hash_string((char*)key,table->seed) : stbds_hash_bytes(key, keysize,table->seed);\r
+  size_t step = STBDS_BUCKET_LENGTH;\r
+  size_t limit,i;\r
+  size_t pos;\r
+  stbds_hash_bucket *bucket;\r
+\r
+  if (hash < 2) hash += 2; // stored hash values are forbidden from being 0, so we can detect empty slots\r
+\r
+  pos = stbds_probe_position(hash, table->slot_count, table->slot_count_log2);\r
+\r
+  for (;;) {\r
+    STBDS_STATS(++stbds_hash_probes);\r
+    bucket = &table->storage[pos >> STBDS_BUCKET_SHIFT];\r
+\r
+    // start searching from pos to end of bucket, this should help performance on small hash tables that fit in cache\r
+    for (i=pos & STBDS_BUCKET_MASK; i < STBDS_BUCKET_LENGTH; ++i) {\r
+      if (bucket->hash[i] == hash) {\r
+        if (stbds_is_key_equal(a, elemsize, key, keysize, keyoffset, mode, bucket->index[i])) {\r
+          return (pos & ~STBDS_BUCKET_MASK)+i;\r
+        }\r
+      } else if (bucket->hash[i] == STBDS_HASH_EMPTY) {\r
+        return -1;\r
+      }\r
+    }\r
+\r
+    // search from beginning of bucket to pos\r
+    limit = pos & STBDS_BUCKET_MASK;\r
+    for (i = 0; i < limit; ++i) {\r
+      if (bucket->hash[i] == hash) {\r
+        if (stbds_is_key_equal(a, elemsize, key, keysize, keyoffset, mode, bucket->index[i])) {\r
+          return (pos & ~STBDS_BUCKET_MASK)+i;\r
+        }\r
+      } else if (bucket->hash[i] == STBDS_HASH_EMPTY) {\r
+        return -1;\r
+      }\r
+    }\r
+\r
+    // quadratic probing\r
+    pos += step;\r
+    step += STBDS_BUCKET_LENGTH;\r
+    pos &= (table->slot_count-1);\r
+  }\r
+  /* NOTREACHED */\r
+}\r
+\r
+void * stbds_hmget_key_ts(void *a, size_t elemsize, void *key, size_t keysize, ptrdiff_t *temp, int mode)\r
+{\r
+  size_t keyoffset = 0;\r
+  if (a == NULL) {\r
+    // make it non-empty so we can return a temp\r
+    a = stbds_arrgrowf(0, elemsize, 0, 1);\r
+    stbds_header(a)->length += 1;\r
+    memset(a, 0, elemsize);\r
+    *temp = STBDS_INDEX_EMPTY;\r
+    // adjust a to point after the default element\r
+    return STBDS_ARR_TO_HASH(a,elemsize);\r
+  } else {\r
+    stbds_hash_index *table;\r
+    void *raw_a = STBDS_HASH_TO_ARR(a,elemsize);\r
+    // adjust a to point to the default element\r
+    table = (stbds_hash_index *) stbds_header(raw_a)->hash_table;\r
+    if (table == 0) {\r
+      *temp = -1;\r
+    } else {\r
+      ptrdiff_t slot = stbds_hm_find_slot(a, elemsize, key, keysize, keyoffset, mode);\r
+      if (slot < 0) {\r
+        *temp = STBDS_INDEX_EMPTY;\r
+      } else {\r
+        stbds_hash_bucket *b = &table->storage[slot >> STBDS_BUCKET_SHIFT];\r
+        *temp = b->index[slot & STBDS_BUCKET_MASK];\r
+      }\r
+    }\r
+    return a;\r
+  }\r
+}\r
+\r
+void * stbds_hmget_key(void *a, size_t elemsize, void *key, size_t keysize, int mode)\r
+{\r
+  ptrdiff_t temp;\r
+  void *p = stbds_hmget_key_ts(a, elemsize, key, keysize, &temp, mode);\r
+  stbds_temp(STBDS_HASH_TO_ARR(p,elemsize)) = temp;\r
+  return p;\r
+}\r
+\r
+void * stbds_hmput_default(void *a, size_t elemsize)\r
+{\r
+  // three cases:\r
+  //   a is NULL <- allocate\r
+  //   a has a hash table but no entries, because of shmode <- grow\r
+  //   a has entries <- do nothing\r
+  if (a == NULL || stbds_header(STBDS_HASH_TO_ARR(a,elemsize))->length == 0) {\r
+    a = stbds_arrgrowf(a ? STBDS_HASH_TO_ARR(a,elemsize) : NULL, elemsize, 0, 1);\r
+    stbds_header(a)->length += 1;\r
+    memset(a, 0, elemsize);\r
+    a=STBDS_ARR_TO_HASH(a,elemsize);\r
+  }\r
+  return a;\r
+}\r
+\r
+static char *stbds_strdup(char *str);\r
+\r
+void *stbds_hmput_key(void *a, size_t elemsize, void *key, size_t keysize, int mode)\r
+{\r
+  size_t keyoffset=0;\r
+  void *raw_a;\r
+  stbds_hash_index *table;\r
+\r
+  if (a == NULL) {\r
+    a = stbds_arrgrowf(0, elemsize, 0, 1);\r
+    memset(a, 0, elemsize);\r
+    stbds_header(a)->length += 1;\r
+    // adjust a to point AFTER the default element\r
+    a = STBDS_ARR_TO_HASH(a,elemsize);\r
+  }\r
+\r
+  // adjust a to point to the default element\r
+  raw_a = a;\r
+  a = STBDS_HASH_TO_ARR(a,elemsize);\r
+\r
+  table = (stbds_hash_index *) stbds_header(a)->hash_table;\r
+\r
+  if (table == NULL || table->used_count >= table->used_count_threshold) {\r
+    stbds_hash_index *nt;\r
+    size_t slot_count;\r
+\r
+    slot_count = (table == NULL) ? STBDS_BUCKET_LENGTH : table->slot_count*2;\r
+    nt = stbds_make_hash_index(slot_count, table);\r
+    if (table)\r
+      STBDS_FREE(NULL, table);\r
+    else\r
+      nt->string.mode = mode >= STBDS_HM_STRING ? STBDS_SH_DEFAULT : 0;\r
+    stbds_header(a)->hash_table = table = nt;\r
+    STBDS_STATS(++stbds_hash_grow);\r
+  }\r
+\r
+  // we iterate hash table explicitly because we want to track if we saw a tombstone\r
+  {\r
+    size_t hash = mode >= STBDS_HM_STRING ? stbds_hash_string((char*)key,table->seed) : stbds_hash_bytes(key, keysize,table->seed);\r
+    size_t step = STBDS_BUCKET_LENGTH;\r
+    size_t pos;\r
+    ptrdiff_t tombstone = -1;\r
+    stbds_hash_bucket *bucket;\r
+\r
+    // stored hash values are forbidden from being 0, so we can detect empty slots to early out quickly\r
+    if (hash < 2) hash += 2;\r
+\r
+    pos = stbds_probe_position(hash, table->slot_count, table->slot_count_log2);\r
+\r
+    for (;;) {\r
+      size_t limit, i;\r
+      STBDS_STATS(++stbds_hash_probes);\r
+      bucket = &table->storage[pos >> STBDS_BUCKET_SHIFT];\r
+\r
+      // start searching from pos to end of bucket\r
+      for (i=pos & STBDS_BUCKET_MASK; i < STBDS_BUCKET_LENGTH; ++i) {\r
+        if (bucket->hash[i] == hash) {\r
+          if (stbds_is_key_equal(raw_a, elemsize, key, keysize, keyoffset, mode, bucket->index[i])) {\r
+            stbds_temp(a) = bucket->index[i];\r
+            if (mode >= STBDS_HM_STRING)\r
+              stbds_temp_key(a) = * (char **) ((char *) raw_a + elemsize*bucket->index[i] + keyoffset);\r
+            return STBDS_ARR_TO_HASH(a,elemsize);\r
+          }\r
+        } else if (bucket->hash[i] == 0) {\r
+          pos = (pos & ~STBDS_BUCKET_MASK) + i;\r
+          goto found_empty_slot;\r
+        } else if (tombstone < 0) {\r
+          if (bucket->index[i] == STBDS_INDEX_DELETED)\r
+            tombstone = (ptrdiff_t) ((pos & ~STBDS_BUCKET_MASK) + i);\r
+        }\r
+      }\r
+\r
+      // search from beginning of bucket to pos\r
+      limit = pos & STBDS_BUCKET_MASK;\r
+      for (i = 0; i < limit; ++i) {\r
+        if (bucket->hash[i] == hash) {\r
+          if (stbds_is_key_equal(raw_a, elemsize, key, keysize, keyoffset, mode, bucket->index[i])) {\r
+            stbds_temp(a) = bucket->index[i];\r
+            return STBDS_ARR_TO_HASH(a,elemsize);\r
+          }\r
+        } else if (bucket->hash[i] == 0) {\r
+          pos = (pos & ~STBDS_BUCKET_MASK) + i;\r
+          goto found_empty_slot;\r
+        } else if (tombstone < 0) {\r
+          if (bucket->index[i] == STBDS_INDEX_DELETED)\r
+            tombstone = (ptrdiff_t) ((pos & ~STBDS_BUCKET_MASK) + i);\r
+        }\r
+      }\r
+\r
+      // quadratic probing\r
+      pos += step;\r
+      step += STBDS_BUCKET_LENGTH;\r
+      pos &= (table->slot_count-1);\r
+    }\r
+   found_empty_slot:\r
+    if (tombstone >= 0) {\r
+      pos = tombstone;\r
+      --table->tombstone_count;\r
+    }\r
+    ++table->used_count;\r
+\r
+    {\r
+      ptrdiff_t i = (ptrdiff_t) stbds_arrlen(a);\r
+      // we want to do stbds_arraddn(1), but we can't use the macros since we don't have something of the right type\r
+      if ((size_t) i+1 > stbds_arrcap(a))\r
+        *(void **) &a = stbds_arrgrowf(a, elemsize, 1, 0);\r
+      raw_a = STBDS_ARR_TO_HASH(a,elemsize);\r
+\r
+      STBDS_ASSERT((size_t) i+1 <= stbds_arrcap(a));\r
+      stbds_header(a)->length = i+1;\r
+      bucket = &table->storage[pos >> STBDS_BUCKET_SHIFT];\r
+      bucket->hash[pos & STBDS_BUCKET_MASK] = hash;\r
+      bucket->index[pos & STBDS_BUCKET_MASK] = i-1;\r
+      stbds_temp(a) = i-1;\r
+\r
+      switch (table->string.mode) {\r
+         case STBDS_SH_STRDUP:  stbds_temp_key(a) = *(char **) ((char *) a + elemsize*i) = stbds_strdup((char*) key); break;\r
+         case STBDS_SH_ARENA:   stbds_temp_key(a) = *(char **) ((char *) a + elemsize*i) = stbds_stralloc(&table->string, (char*)key); break;\r
+         case STBDS_SH_DEFAULT: stbds_temp_key(a) = *(char **) ((char *) a + elemsize*i) = (char *) key; break;\r
+         default:                memcpy((char *) a + elemsize*i, key, keysize); break;\r
+      }\r
+    }\r
+    return STBDS_ARR_TO_HASH(a,elemsize);\r
+  }\r
+}\r
+\r
+void * stbds_shmode_func(size_t elemsize, int mode)\r
+{\r
+  void *a = stbds_arrgrowf(0, elemsize, 0, 1);\r
+  stbds_hash_index *h;\r
+  memset(a, 0, elemsize);\r
+  stbds_header(a)->length = 1;\r
+  stbds_header(a)->hash_table = h = (stbds_hash_index *) stbds_make_hash_index(STBDS_BUCKET_LENGTH, NULL);\r
+  h->string.mode = (unsigned char) mode;\r
+  return STBDS_ARR_TO_HASH(a,elemsize);\r
+}\r
+\r
+void * stbds_hmdel_key(void *a, size_t elemsize, void *key, size_t keysize, size_t keyoffset, int mode)\r
+{\r
+  if (a == NULL) {\r
+    return 0;\r
+  } else {\r
+    stbds_hash_index *table;\r
+    void *raw_a = STBDS_HASH_TO_ARR(a,elemsize);\r
+    table = (stbds_hash_index *) stbds_header(raw_a)->hash_table;\r
+    stbds_temp(raw_a) = 0;\r
+    if (table == 0) {\r
+      return a;\r
+    } else {\r
+      ptrdiff_t slot;\r
+      slot = stbds_hm_find_slot(a, elemsize, key, keysize, keyoffset, mode);\r
+      if (slot < 0)\r
+        return a;\r
+      else {\r
+        stbds_hash_bucket *b = &table->storage[slot >> STBDS_BUCKET_SHIFT];\r
+        int i = slot & STBDS_BUCKET_MASK;\r
+        ptrdiff_t old_index = b->index[i];\r
+        ptrdiff_t final_index = (ptrdiff_t) stbds_arrlen(raw_a)-1-1; // minus one for the raw_a vs a, and minus one for 'last'\r
+        STBDS_ASSERT(slot < (ptrdiff_t) table->slot_count);\r
+        --table->used_count;\r
+        ++table->tombstone_count;\r
+        stbds_temp(raw_a) = 1;\r
+        STBDS_ASSERT(table->used_count >= 0);\r
+        //STBDS_ASSERT(table->tombstone_count < table->slot_count/4);\r
+        b->hash[i] = STBDS_HASH_DELETED;\r
+        b->index[i] = STBDS_INDEX_DELETED;\r
+\r
+        if (mode == STBDS_HM_STRING && table->string.mode == STBDS_SH_STRDUP)\r
+          STBDS_FREE(NULL, *(char**) ((char *) a+elemsize*old_index));\r
+\r
+        // if indices are the same, memcpy is a no-op, but back-pointer-fixup will fail, so skip\r
+        if (old_index != final_index) {\r
+          // swap delete\r
+          memmove((char*) a + elemsize*old_index, (char*) a + elemsize*final_index, elemsize);\r
+\r
+          // now find the slot for the last element\r
+          if (mode == STBDS_HM_STRING)\r
+            slot = stbds_hm_find_slot(a, elemsize, *(char**) ((char *) a+elemsize*old_index + keyoffset), keysize, keyoffset, mode);\r
+          else\r
+            slot = stbds_hm_find_slot(a, elemsize,  (char* ) a+elemsize*old_index + keyoffset, keysize, keyoffset, mode);\r
+          STBDS_ASSERT(slot >= 0);\r
+          b = &table->storage[slot >> STBDS_BUCKET_SHIFT];\r
+          i = slot & STBDS_BUCKET_MASK;\r
+          STBDS_ASSERT(b->index[i] == final_index);\r
+          b->index[i] = old_index;\r
+        }\r
+        stbds_header(raw_a)->length -= 1;\r
+\r
+        if (table->used_count < table->used_count_shrink_threshold && table->slot_count > STBDS_BUCKET_LENGTH) {\r
+          stbds_header(raw_a)->hash_table = stbds_make_hash_index(table->slot_count>>1, table);\r
+          STBDS_FREE(NULL, table);\r
+          STBDS_STATS(++stbds_hash_shrink);\r
+        } else if (table->tombstone_count > table->tombstone_count_threshold) {\r
+          stbds_header(raw_a)->hash_table = stbds_make_hash_index(table->slot_count   , table);\r
+          STBDS_FREE(NULL, table);\r
+          STBDS_STATS(++stbds_hash_rebuild);\r
+        }\r
+\r
+        return a;\r
+      }\r
+    }\r
+  }\r
+  /* NOTREACHED */\r
+}\r
+\r
+static char *stbds_strdup(char *str)\r
+{\r
+  // to keep replaceable allocator simple, we don't want to use strdup.\r
+  // rolling our own also avoids problem of strdup vs _strdup\r
+  size_t len = strlen(str)+1;\r
+  char *p = (char*) STBDS_REALLOC(NULL, 0, len);\r
+  memmove(p, str, len);\r
+  return p;\r
+}\r
+\r
+#ifndef STBDS_STRING_ARENA_BLOCKSIZE_MIN\r
+#define STBDS_STRING_ARENA_BLOCKSIZE_MIN  512u\r
+#endif\r
+#ifndef STBDS_STRING_ARENA_BLOCKSIZE_MAX\r
+#define STBDS_STRING_ARENA_BLOCKSIZE_MAX  (1u<<20)\r
+#endif\r
+\r
+char *stbds_stralloc(stbds_string_arena *a, char *str)\r
+{\r
+  char *p;\r
+  size_t len = strlen(str)+1;\r
+  if (len > a->remaining) {\r
+    // compute the next blocksize\r
+    size_t blocksize = a->block;\r
+\r
+    // size is 512, 512, 1024, 1024, 2048, 2048, 4096, 4096, etc., so that\r
+    // there are log(SIZE) allocations to free when we destroy the table\r
+    blocksize = (size_t) (STBDS_STRING_ARENA_BLOCKSIZE_MIN) << (blocksize>>1);\r
+\r
+    // if size is under 1M, advance to next blocktype\r
+    if (blocksize < (size_t)(STBDS_STRING_ARENA_BLOCKSIZE_MAX))\r
+      ++a->block;\r
+\r
+    if (len > blocksize) {\r
+      // if string is larger than blocksize, then just allocate the full size.\r
+      // note that we still advance string_block so block size will continue\r
+      // increasing, so e.g. if somebody only calls this with 1000-long strings,\r
+      // eventually the arena will start doubling and handling those as well\r
+      stbds_string_block *sb = (stbds_string_block *) STBDS_REALLOC(NULL, 0, sizeof(*sb)-8 + len);\r
+      memmove(sb->storage, str, len);\r
+      if (a->storage) {\r
+        // insert it after the first element, so that we don't waste the space there\r
+        sb->next = a->storage->next;\r
+        a->storage->next = sb;\r
+      } else {\r
+        sb->next = 0;\r
+        a->storage = sb;\r
+        a->remaining = 0; // this is redundant, but good for clarity\r
+      }\r
+      return sb->storage;\r
+    } else {\r
+      stbds_string_block *sb = (stbds_string_block *) STBDS_REALLOC(NULL, 0, sizeof(*sb)-8 + blocksize);\r
+      sb->next = a->storage;\r
+      a->storage = sb;\r
+      a->remaining = blocksize;\r
+    }\r
+  }\r
+\r
+  STBDS_ASSERT(len <= a->remaining);\r
+  p = a->storage->storage + a->remaining - len;\r
+  a->remaining -= len;\r
+  memmove(p, str, len);\r
+  return p;\r
+}\r
+\r
+void stbds_strreset(stbds_string_arena *a)\r
+{\r
+  stbds_string_block *x,*y;\r
+  x = a->storage;\r
+  while (x) {\r
+    y = x->next;\r
+    STBDS_FREE(NULL, x);\r
+    x = y;\r
+  }\r
+  memset(a, 0, sizeof(*a));\r
+}\r
+\r
+#endif\r
+\r
+//////////////////////////////////////////////////////////////////////////////\r
+//\r
+//   UNIT TESTS\r
+//\r
+\r
+#ifdef STBDS_UNIT_TESTS\r
+#include <stdio.h>\r
+#ifdef STBDS_ASSERT_WAS_UNDEFINED\r
+#undef STBDS_ASSERT\r
+#endif\r
+#ifndef STBDS_ASSERT\r
+#define STBDS_ASSERT assert\r
+#include <assert.h>\r
+#endif\r
+\r
+typedef struct { int key,b,c,d; } stbds_struct;\r
+typedef struct { int key[2],b,c,d; } stbds_struct2;\r
+\r
+static char buffer[256];\r
+char *strkey(int n)\r
+{\r
+#if defined(_WIN32) && defined(__STDC_WANT_SECURE_LIB__)\r
+   sprintf_s(buffer, sizeof(buffer), "test_%d", n);\r
+#else\r
+   sprintf(buffer, "test_%d", n);\r
+#endif\r
+   return buffer;\r
+}\r
+\r
+void stbds_unit_tests(void)\r
+{\r
+#if defined(_MSC_VER) && _MSC_VER <= 1200 && defined(__cplusplus)\r
+  // VC6 C++ doesn't like the template<> trick on unnamed structures, so do nothing!\r
+  STBDS_ASSERT(0);\r
+#else\r
+  const int testsize = 100000;\r
+  const int testsize2 = testsize/20;\r
+  int *arr=NULL;\r
+  struct { int   key;        int value; }  *intmap  = NULL;\r
+  struct { char *key;        int value; }  *strmap  = NULL, s;\r
+  struct { stbds_struct key; int value; }  *map     = NULL;\r
+  stbds_struct                             *map2    = NULL;\r
+  stbds_struct2                            *map3    = NULL;\r
+  stbds_string_arena                        sa      = { 0 };\r
+  int key3[2] = { 1,2 };\r
+  ptrdiff_t temp;\r
+\r
+  int i,j;\r
+\r
+  STBDS_ASSERT(arrlen(arr)==0);\r
+  for (i=0; i < 20000; i += 50) {\r
+    for (j=0; j < i; ++j)\r
+      arrpush(arr,j);\r
+    arrfree(arr);\r
+  }\r
+\r
+  for (i=0; i < 4; ++i) {\r
+    arrpush(arr,1); arrpush(arr,2); arrpush(arr,3); arrpush(arr,4);\r
+    arrdel(arr,i);\r
+    arrfree(arr);\r
+    arrpush(arr,1); arrpush(arr,2); arrpush(arr,3); arrpush(arr,4);\r
+    arrdelswap(arr,i);\r
+    arrfree(arr);\r
+  }\r
+\r
+  for (i=0; i < 5; ++i) {\r
+    arrpush(arr,1); arrpush(arr,2); arrpush(arr,3); arrpush(arr,4);\r
+    stbds_arrins(arr,i,5);\r
+    STBDS_ASSERT(arr[i] == 5);\r
+    if (i < 4)\r
+      STBDS_ASSERT(arr[4] == 4);\r
+    arrfree(arr);\r
+  }\r
+\r
+  i = 1;\r
+  STBDS_ASSERT(hmgeti(intmap,i) == -1);\r
+  hmdefault(intmap, -2);\r
+  STBDS_ASSERT(hmgeti(intmap, i) == -1);\r
+  STBDS_ASSERT(hmget (intmap, i) == -2);\r
+  for (i=0; i < testsize; i+=2)\r
+    hmput(intmap, i, i*5);\r
+  for (i=0; i < testsize; i+=1) {\r
+    if (i & 1) STBDS_ASSERT(hmget(intmap, i) == -2 );\r
+    else       STBDS_ASSERT(hmget(intmap, i) == i*5);\r
+    if (i & 1) STBDS_ASSERT(hmget_ts(intmap, i, temp) == -2 );\r
+    else       STBDS_ASSERT(hmget_ts(intmap, i, temp) == i*5);\r
+  }\r
+  for (i=0; i < testsize; i+=2)\r
+    hmput(intmap, i, i*3);\r
+  for (i=0; i < testsize; i+=1)\r
+    if (i & 1) STBDS_ASSERT(hmget(intmap, i) == -2 );\r
+    else       STBDS_ASSERT(hmget(intmap, i) == i*3);\r
+  for (i=2; i < testsize; i+=4)\r
+    hmdel(intmap, i); // delete half the entries\r
+  for (i=0; i < testsize; i+=1)\r
+    if (i & 3) STBDS_ASSERT(hmget(intmap, i) == -2 );\r
+    else       STBDS_ASSERT(hmget(intmap, i) == i*3);\r
+  for (i=0; i < testsize; i+=1)\r
+    hmdel(intmap, i); // delete the rest of the entries\r
+  for (i=0; i < testsize; i+=1)\r
+    STBDS_ASSERT(hmget(intmap, i) == -2 );\r
+  hmfree(intmap);\r
+  for (i=0; i < testsize; i+=2)\r
+    hmput(intmap, i, i*3);\r
+  hmfree(intmap);\r
+\r
+  #if defined(__clang__) || defined(__GNUC__)\r
+  #ifndef __cplusplus\r
+  intmap = NULL;\r
+  hmput(intmap, 15, 7);\r
+  hmput(intmap, 11, 3);\r
+  hmput(intmap,  9, 5);\r
+  STBDS_ASSERT(hmget(intmap, 9) == 5);\r
+  STBDS_ASSERT(hmget(intmap, 11) == 3);\r
+  STBDS_ASSERT(hmget(intmap, 15) == 7);\r
+  #endif\r
+  #endif\r
+\r
+  for (i=0; i < testsize; ++i)\r
+    stralloc(&sa, strkey(i));\r
+  strreset(&sa);\r
+\r
+  {\r
+    s.key = "a", s.value = 1;\r
+    shputs(strmap, s);\r
+    STBDS_ASSERT(*strmap[0].key == 'a');\r
+    STBDS_ASSERT(strmap[0].key == s.key);\r
+    STBDS_ASSERT(strmap[0].value == s.value);\r
+    shfree(strmap);\r
+  }\r
+\r
+  {\r
+    s.key = "a", s.value = 1;\r
+    sh_new_strdup(strmap);\r
+    shputs(strmap, s);\r
+    STBDS_ASSERT(*strmap[0].key == 'a');\r
+    STBDS_ASSERT(strmap[0].key != s.key);\r
+    STBDS_ASSERT(strmap[0].value == s.value);\r
+    shfree(strmap);\r
+  }\r
+\r
+  {\r
+    s.key = "a", s.value = 1;\r
+    sh_new_arena(strmap);\r
+    shputs(strmap, s);\r
+    STBDS_ASSERT(*strmap[0].key == 'a');\r
+    STBDS_ASSERT(strmap[0].key != s.key);\r
+    STBDS_ASSERT(strmap[0].value == s.value);\r
+    shfree(strmap);\r
+  }\r
+\r
+  for (j=0; j < 2; ++j) {\r
+    STBDS_ASSERT(shgeti(strmap,"foo") == -1);\r
+    if (j == 0)\r
+      sh_new_strdup(strmap);\r
+    else\r
+      sh_new_arena(strmap);\r
+    STBDS_ASSERT(shgeti(strmap,"foo") == -1);\r
+    shdefault(strmap, -2);\r
+    STBDS_ASSERT(shgeti(strmap,"foo") == -1);\r
+    for (i=0; i < testsize; i+=2)\r
+      shput(strmap, strkey(i), i*3);\r
+    for (i=0; i < testsize; i+=1)\r
+      if (i & 1) STBDS_ASSERT(shget(strmap, strkey(i)) == -2 );\r
+      else       STBDS_ASSERT(shget(strmap, strkey(i)) == i*3);\r
+    for (i=2; i < testsize; i+=4)\r
+      shdel(strmap, strkey(i)); // delete half the entries\r
+    for (i=0; i < testsize; i+=1)\r
+      if (i & 3) STBDS_ASSERT(shget(strmap, strkey(i)) == -2 );\r
+      else       STBDS_ASSERT(shget(strmap, strkey(i)) == i*3);\r
+    for (i=0; i < testsize; i+=1)\r
+      shdel(strmap, strkey(i)); // delete the rest of the entries\r
+    for (i=0; i < testsize; i+=1)\r
+      STBDS_ASSERT(shget(strmap, strkey(i)) == -2 );\r
+    shfree(strmap);\r
+  }\r
+\r
+  {\r
+    struct { char *key; char value; } *hash = NULL;\r
+    char name[4] = "jen";\r
+    shput(hash, "bob"   , 'h');\r
+    shput(hash, "sally" , 'e');\r
+    shput(hash, "fred"  , 'l');\r
+    shput(hash, "jen"   , 'x');\r
+    shput(hash, "doug"  , 'o');\r
+\r
+    shput(hash, name    , 'l');\r
+    shfree(hash);\r
+  }\r
+\r
+  for (i=0; i < testsize; i += 2) {\r
+    stbds_struct s = { i,i*2,i*3,i*4 };\r
+    hmput(map, s, i*5);\r
+  }\r
+\r
+  for (i=0; i < testsize; i += 1) {\r
+    stbds_struct s = { i,i*2,i*3  ,i*4 };\r
+    stbds_struct t = { i,i*2,i*3+1,i*4 };\r
+    if (i & 1) STBDS_ASSERT(hmget(map, s) == 0);\r
+    else       STBDS_ASSERT(hmget(map, s) == i*5);\r
+    if (i & 1) STBDS_ASSERT(hmget_ts(map, s, temp) == 0);\r
+    else       STBDS_ASSERT(hmget_ts(map, s, temp) == i*5);\r
+    //STBDS_ASSERT(hmget(map, t.key) == 0);\r
+  }\r
+\r
+  for (i=0; i < testsize; i += 2) {\r
+    stbds_struct s = { i,i*2,i*3,i*4 };\r
+    hmputs(map2, s);\r
+  }\r
+  hmfree(map);\r
+\r
+  for (i=0; i < testsize; i += 1) {\r
+    stbds_struct s = { i,i*2,i*3,i*4 };\r
+    stbds_struct t = { i,i*2,i*3+1,i*4 };\r
+    if (i & 1) STBDS_ASSERT(hmgets(map2, s.key).d == 0);\r
+    else       STBDS_ASSERT(hmgets(map2, s.key).d == i*4);\r
+    //STBDS_ASSERT(hmgetp(map2, t.key) == 0);\r
+  }\r
+  hmfree(map2);\r
+\r
+  for (i=0; i < testsize; i += 2) {\r
+    stbds_struct2 s = { { i,i*2 }, i*3,i*4, i*5 };\r
+    hmputs(map3, s);\r
+  }\r
+  for (i=0; i < testsize; i += 1) {\r
+    stbds_struct2 s = { { i,i*2}, i*3, i*4, i*5 };\r
+    stbds_struct2 t = { { i,i*2}, i*3+1, i*4, i*5 };\r
+    if (i & 1) STBDS_ASSERT(hmgets(map3, s.key).d == 0);\r
+    else       STBDS_ASSERT(hmgets(map3, s.key).d == i*5);\r
+    //STBDS_ASSERT(hmgetp(map3, t.key) == 0);\r
+  }\r
+#endif\r
+}\r
+#endif\r
+\r
+\r
+/*\r
+------------------------------------------------------------------------------\r
+This software is available under 2 licenses -- choose whichever you prefer.\r
+------------------------------------------------------------------------------\r
+ALTERNATIVE A - MIT License\r
+Copyright (c) 2019 Sean Barrett\r
+Permission is hereby granted, free of charge, to any person obtaining a copy of\r
+this software and associated documentation files (the "Software"), to deal in\r
+the Software without restriction, including without limitation the rights to\r
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies\r
+of the Software, and to permit persons to whom the Software is furnished to do\r
+so, subject to the following conditions:\r
+The above copyright notice and this permission notice shall be included in all\r
+copies or substantial portions of the Software.\r
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\r
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\r
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\r
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
+SOFTWARE.\r
+------------------------------------------------------------------------------\r
+ALTERNATIVE B - Public Domain (www.unlicense.org)\r
+This is free and unencumbered software released into the public domain.\r
+Anyone is free to copy, modify, publish, use, compile, sell, or distribute this\r
+software, either in source code form or as a compiled binary, for any purpose,\r
+commercial or non-commercial, and by any means.\r
+In jurisdictions that recognize copyright laws, the author or authors of this\r
+software dedicate any and all copyright interest in the software to the public\r
+domain. We make this dedication for the benefit of the public at large and to\r
+the detriment of our heirs and successors. We intend this dedication to be an\r
+overt act of relinquishment in perpetuity of all present and future rights to\r
+this software under copyright law.\r
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\r
+AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
+ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\r
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r
+------------------------------------------------------------------------------\r
+*/\r
+// << fold <<\r
+\r
+// >> stb_sprintf.h >>\r
+\r
+#define STB_SPRINTF_IMPLEMENTATION\r
+#ifdef STB_SPRINTF_IMPLEMENTATION\r
+\r
+#define stbsp__uint32 unsigned int\r
+#define stbsp__int32 signed int\r
+\r
+#ifdef _MSC_VER\r
+#define stbsp__uint64 unsigned __int64\r
+#define stbsp__int64 signed __int64\r
+#else\r
+#define stbsp__uint64 unsigned long long\r
+#define stbsp__int64 signed long long\r
+#endif\r
+#define stbsp__uint16 unsigned short\r
+\r
+#ifndef stbsp__uintptr\r
+#if defined(__ppc64__) || defined(__powerpc64__) || defined(__aarch64__) || defined(_M_X64) || defined(__x86_64__) || defined(__x86_64) || defined(__s390x__)\r
+#define stbsp__uintptr stbsp__uint64\r
+#else\r
+#define stbsp__uintptr stbsp__uint32\r
+#endif\r
+#endif\r
+\r
+#ifndef STB_SPRINTF_MSVC_MODE // used for MSVC2013 and earlier (MSVC2015 matches GCC)\r
+#if defined(_MSC_VER) && (_MSC_VER < 1900)\r
+#define STB_SPRINTF_MSVC_MODE\r
+#endif\r
+#endif\r
+\r
+#ifdef STB_SPRINTF_NOUNALIGNED // define this before inclusion to force stbsp_sprintf to always use aligned accesses\r
+#define STBSP__UNALIGNED(code)\r
+#else\r
+#define STBSP__UNALIGNED(code) code\r
+#endif\r
+\r
+#ifndef STB_SPRINTF_NOFLOAT\r
+// internal float utility functions\r
+static stbsp__int32 stbsp__real_to_str(char const **start, stbsp__uint32 *len, char *out, stbsp__int32 *decimal_pos, double value, stbsp__uint32 frac_digits);\r
+static stbsp__int32 stbsp__real_to_parts(stbsp__int64 *bits, stbsp__int32 *expo, double value);\r
+#define STBSP__SPECIAL 0x7000\r
+#endif\r
+\r
+static char stbsp__period = '.';\r
+static char stbsp__comma = ',';\r
+static struct\r
+{\r
+   short temp; // force next field to be 2-byte aligned\r
+   char pair[201];\r
+} stbsp__digitpair =\r
+{\r
+  0,\r
+   "00010203040506070809101112131415161718192021222324"\r
+   "25262728293031323334353637383940414243444546474849"\r
+   "50515253545556575859606162636465666768697071727374"\r
+   "75767778798081828384858687888990919293949596979899"\r
+};\r
+\r
+STBSP__PUBLICDEF void STB_SPRINTF_DECORATE(set_separators)(char pcomma, char pperiod)\r
+{\r
+   stbsp__period = pperiod;\r
+   stbsp__comma = pcomma;\r
+}\r
+\r
+#define STBSP__LEFTJUST 1\r
+#define STBSP__LEADINGPLUS 2\r
+#define STBSP__LEADINGSPACE 4\r
+#define STBSP__LEADING_0X 8\r
+#define STBSP__LEADINGZERO 16\r
+#define STBSP__INTMAX 32\r
+#define STBSP__TRIPLET_COMMA 64\r
+#define STBSP__NEGATIVE 128\r
+#define STBSP__METRIC_SUFFIX 256\r
+#define STBSP__HALFWIDTH 512\r
+#define STBSP__METRIC_NOSPACE 1024\r
+#define STBSP__METRIC_1024 2048\r
+#define STBSP__METRIC_JEDEC 4096\r
+\r
+static void stbsp__lead_sign(stbsp__uint32 fl, char *sign)\r
+{\r
+   sign[0] = 0;\r
+   if (fl & STBSP__NEGATIVE) {\r
+      sign[0] = 1;\r
+      sign[1] = '-';\r
+   } else if (fl & STBSP__LEADINGSPACE) {\r
+      sign[0] = 1;\r
+      sign[1] = ' ';\r
+   } else if (fl & STBSP__LEADINGPLUS) {\r
+      sign[0] = 1;\r
+      sign[1] = '+';\r
+   }\r
+}\r
+\r
+static STBSP__ASAN stbsp__uint32 stbsp__strlen_limited(char const *s, stbsp__uint32 limit)\r
+{\r
+   char const * sn = s;\r
+\r
+   // get up to 4-byte alignment\r
+   for (;;) {\r
+      if (((stbsp__uintptr)sn & 3) == 0)\r
+         break;\r
+\r
+      if (!limit || *sn == 0)\r
+         return (stbsp__uint32)(sn - s);\r
+\r
+      ++sn;\r
+      --limit;\r
+   }\r
+\r
+   // scan over 4 bytes at a time to find terminating 0\r
+   // this will intentionally scan up to 3 bytes past the end of buffers,\r
+   // but becase it works 4B aligned, it will never cross page boundaries\r
+   // (hence the STBSP__ASAN markup; the over-read here is intentional\r
+   // and harmless)\r
+   while (limit >= 4) {\r
+      stbsp__uint32 v = *(stbsp__uint32 *)sn;\r
+      // bit hack to find if there's a 0 byte in there\r
+      if ((v - 0x01010101) & (~v) & 0x80808080UL)\r
+         break;\r
+\r
+      sn += 4;\r
+      limit -= 4;\r
+   }\r
+\r
+   // handle the last few characters to find actual size\r
+   while (limit && *sn) {\r
+      ++sn;\r
+      --limit;\r
+   }\r
+\r
+   return (stbsp__uint32)(sn - s);\r
+}\r
+\r
+STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(vsprintfcb)(STBSP_SPRINTFCB *callback, void *user, char *buf, char const *fmt, va_list va)\r
+{\r
+   static char hex[] = "0123456789abcdefxp";\r
+   static char hexu[] = "0123456789ABCDEFXP";\r
+   char *bf;\r
+   char const *f;\r
+   int tlen = 0;\r
+\r
+   bf = buf;\r
+   f = fmt;\r
+   for (;;) {\r
+      stbsp__int32 fw, pr, tz;\r
+      stbsp__uint32 fl;\r
+\r
+      // macros for the callback buffer stuff\r
+      #define stbsp__chk_cb_bufL(bytes)                        \\r
+         {                                                     \\r
+            int len = (int)(bf - buf);                         \\r
+            if ((len + (bytes)) >= STB_SPRINTF_MIN) {          \\r
+               tlen += len;                                    \\r
+               if (0 == (bf = buf = callback(buf, user, len))) \\r
+                  goto done;                                   \\r
+            }                                                  \\r
+         }\r
+      #define stbsp__chk_cb_buf(bytes)    \\r
+         {                                \\r
+            if (callback) {               \\r
+               stbsp__chk_cb_bufL(bytes); \\r
+            }                             \\r
+         }\r
+      #define stbsp__flush_cb()                      \\r
+         {                                           \\r
+            stbsp__chk_cb_bufL(STB_SPRINTF_MIN - 1); \\r
+         } // flush if there is even one byte in the buffer\r
+      #define stbsp__cb_buf_clamp(cl, v)                \\r
+         cl = v;                                        \\r
+         if (callback) {                                \\r
+            int lg = STB_SPRINTF_MIN - (int)(bf - buf); \\r
+            if (cl > lg)                                \\r
+               cl = lg;                                 \\r
+         }\r
+\r
+      // fast copy everything up to the next % (or end of string)\r
+      for (;;) {\r
+         while (((stbsp__uintptr)f) & 3) {\r
+         schk1:\r
+            if (f[0] == '%')\r
+               goto scandd;\r
+         schk2:\r
+            if (f[0] == 0)\r
+               goto endfmt;\r
+            stbsp__chk_cb_buf(1);\r
+            *bf++ = f[0];\r
+            ++f;\r
+         }\r
+         for (;;) {\r
+            // Check if the next 4 bytes contain %(0x25) or end of string.\r
+            // Using the 'hasless' trick:\r
+            // https://graphics.stanford.edu/~seander/bithacks.html#HasLessInWord\r
+            stbsp__uint32 v, c;\r
+            v = *(stbsp__uint32 *)f;\r
+            c = (~v) & 0x80808080;\r
+            if (((v ^ 0x25252525) - 0x01010101) & c)\r
+               goto schk1;\r
+            if ((v - 0x01010101) & c)\r
+               goto schk2;\r
+            if (callback)\r
+               if ((STB_SPRINTF_MIN - (int)(bf - buf)) < 4)\r
+                  goto schk1;\r
+            #ifdef STB_SPRINTF_NOUNALIGNED\r
+                if(((stbsp__uintptr)bf) & 3) {\r
+                    bf[0] = f[0];\r
+                    bf[1] = f[1];\r
+                    bf[2] = f[2];\r
+                    bf[3] = f[3];\r
+                } else\r
+            #endif\r
+            {\r
+                *(stbsp__uint32 *)bf = v;\r
+            }\r
+            bf += 4;\r
+            f += 4;\r
+         }\r
+      }\r
+   scandd:\r
+\r
+      ++f;\r
+\r
+      // ok, we have a percent, read the modifiers first\r
+      fw = 0;\r
+      pr = -1;\r
+      fl = 0;\r
+      tz = 0;\r
+\r
+      // flags\r
+      for (;;) {\r
+         switch (f[0]) {\r
+         // if we have left justify\r
+         case '-':\r
+            fl |= STBSP__LEFTJUST;\r
+            ++f;\r
+            continue;\r
+         // if we have leading plus\r
+         case '+':\r
+            fl |= STBSP__LEADINGPLUS;\r
+            ++f;\r
+            continue;\r
+         // if we have leading space\r
+         case ' ':\r
+            fl |= STBSP__LEADINGSPACE;\r
+            ++f;\r
+            continue;\r
+         // if we have leading 0x\r
+         case '#':\r
+            fl |= STBSP__LEADING_0X;\r
+            ++f;\r
+            continue;\r
+         // if we have thousand commas\r
+         case '\'':\r
+            fl |= STBSP__TRIPLET_COMMA;\r
+            ++f;\r
+            continue;\r
+         // if we have kilo marker (none->kilo->kibi->jedec)\r
+         case '$':\r
+            if (fl & STBSP__METRIC_SUFFIX) {\r
+               if (fl & STBSP__METRIC_1024) {\r
+                  fl |= STBSP__METRIC_JEDEC;\r
+               } else {\r
+                  fl |= STBSP__METRIC_1024;\r
+               }\r
+            } else {\r
+               fl |= STBSP__METRIC_SUFFIX;\r
+            }\r
+            ++f;\r
+            continue;\r
+         // if we don't want space between metric suffix and number\r
+         case '_':\r
+            fl |= STBSP__METRIC_NOSPACE;\r
+            ++f;\r
+            continue;\r
+         // if we have leading zero\r
+         case '0':\r
+            fl |= STBSP__LEADINGZERO;\r
+            ++f;\r
+            goto flags_done;\r
+         default: goto flags_done;\r
+         }\r
+      }\r
+   flags_done:\r
+\r
+      // get the field width\r
+      if (f[0] == '*') {\r
+         fw = va_arg(va, stbsp__uint32);\r
+         ++f;\r
+      } else {\r
+         while ((f[0] >= '0') && (f[0] <= '9')) {\r
+            fw = fw * 10 + f[0] - '0';\r
+            f++;\r
+         }\r
+      }\r
+      // get the precision\r
+      if (f[0] == '.') {\r
+         ++f;\r
+         if (f[0] == '*') {\r
+            pr = va_arg(va, stbsp__uint32);\r
+            ++f;\r
+         } else {\r
+            pr = 0;\r
+            while ((f[0] >= '0') && (f[0] <= '9')) {\r
+               pr = pr * 10 + f[0] - '0';\r
+               f++;\r
+            }\r
+         }\r
+      }\r
+\r
+      // handle integer size overrides\r
+      switch (f[0]) {\r
+      // are we halfwidth?\r
+      case 'h':\r
+         fl |= STBSP__HALFWIDTH;\r
+         ++f;\r
+         if (f[0] == 'h')\r
+            ++f;  // QUARTERWIDTH\r
+         break;\r
+      // are we 64-bit (unix style)\r
+      case 'l':\r
+         fl |= ((sizeof(long) == 8) ? STBSP__INTMAX : 0);\r
+         ++f;\r
+         if (f[0] == 'l') {\r
+            fl |= STBSP__INTMAX;\r
+            ++f;\r
+         }\r
+         break;\r
+      // are we 64-bit on intmax? (c99)\r
+      case 'j':\r
+         fl |= (sizeof(size_t) == 8) ? STBSP__INTMAX : 0;\r
+         ++f;\r
+         break;\r
+      // are we 64-bit on size_t or ptrdiff_t? (c99)\r
+      case 'z':\r
+         fl |= (sizeof(ptrdiff_t) == 8) ? STBSP__INTMAX : 0;\r
+         ++f;\r
+         break;\r
+      case 't':\r
+         fl |= (sizeof(ptrdiff_t) == 8) ? STBSP__INTMAX : 0;\r
+         ++f;\r
+         break;\r
+      // are we 64-bit (msft style)\r
+      case 'I':\r
+         if ((f[1] == '6') && (f[2] == '4')) {\r
+            fl |= STBSP__INTMAX;\r
+            f += 3;\r
+         } else if ((f[1] == '3') && (f[2] == '2')) {\r
+            f += 3;\r
+         } else {\r
+            fl |= ((sizeof(void *) == 8) ? STBSP__INTMAX : 0);\r
+            ++f;\r
+         }\r
+         break;\r
+      default: break;\r
+      }\r
+\r
+      // handle each replacement\r
+      switch (f[0]) {\r
+         #define STBSP__NUMSZ 512 // big enough for e308 (with commas) or e-307\r
+         char num[STBSP__NUMSZ];\r
+         char lead[8];\r
+         char tail[8];\r
+         char *s;\r
+         char const *h;\r
+         stbsp__uint32 l, n, cs;\r
+         stbsp__uint64 n64;\r
+#ifndef STB_SPRINTF_NOFLOAT\r
+         double fv;\r
+#endif\r
+         stbsp__int32 dp;\r
+         char const *sn;\r
+\r
+      case 's':\r
+         // get the string\r
+         s = va_arg(va, char *);\r
+         if (s == 0)\r
+            s = (char *)"null";\r
+         // get the length, limited to desired precision\r
+         // always limit to ~0u chars since our counts are 32b\r
+         l = stbsp__strlen_limited(s, (pr >= 0) ? (unsigned int)pr : ~0u);\r
+         lead[0] = 0;\r
+         tail[0] = 0;\r
+         pr = 0;\r
+         dp = 0;\r
+         cs = 0;\r
+         // copy the string in\r
+         goto scopy;\r
+\r
+      case 'c': // char\r
+         // get the character\r
+         s = num + STBSP__NUMSZ - 1;\r
+         *s = (char)va_arg(va, int);\r
+         l = 1;\r
+         lead[0] = 0;\r
+         tail[0] = 0;\r
+         pr = 0;\r
+         dp = 0;\r
+         cs = 0;\r
+         goto scopy;\r
+\r
+      case 'n': // weird write-bytes specifier\r
+      {\r
+         int *d = va_arg(va, int *);\r
+         *d = tlen + (int)(bf - buf);\r
+      } break;\r
+\r
+#ifdef STB_SPRINTF_NOFLOAT\r
+      case 'A':              // float\r
+      case 'a':              // hex float\r
+      case 'G':              // float\r
+      case 'g':              // float\r
+      case 'E':              // float\r
+      case 'e':              // float\r
+      case 'f':              // float\r
+         va_arg(va, double); // eat it\r
+         s = (char *)"No float";\r
+         l = 8;\r
+         lead[0] = 0;\r
+         tail[0] = 0;\r
+         pr = 0;\r
+         cs = 0;\r
+         STBSP__NOTUSED(dp);\r
+         goto scopy;\r
+#else\r
+      case 'A': // hex float\r
+      case 'a': // hex float\r
+         h = (f[0] == 'A') ? hexu : hex;\r
+         fv = va_arg(va, double);\r
+         if (pr == -1)\r
+            pr = 6; // default is 6\r
+         // read the double into a string\r
+         if (stbsp__real_to_parts((stbsp__int64 *)&n64, &dp, fv))\r
+            fl |= STBSP__NEGATIVE;\r
+\r
+         s = num + 64;\r
+\r
+         stbsp__lead_sign(fl, lead);\r
+\r
+         if (dp == -1023)\r
+            dp = (n64) ? -1022 : 0;\r
+         else\r
+            n64 |= (((stbsp__uint64)1) << 52);\r
+         n64 <<= (64 - 56);\r
+         if (pr < 15)\r
+            n64 += ((((stbsp__uint64)8) << 56) >> (pr * 4));\r
+// add leading chars\r
+\r
+#ifdef STB_SPRINTF_MSVC_MODE\r
+         *s++ = '0';\r
+         *s++ = 'x';\r
+#else\r
+         lead[1 + lead[0]] = '0';\r
+         lead[2 + lead[0]] = 'x';\r
+         lead[0] += 2;\r
+#endif\r
+         *s++ = h[(n64 >> 60) & 15];\r
+         n64 <<= 4;\r
+         if (pr)\r
+            *s++ = stbsp__period;\r
+         sn = s;\r
+\r
+         // print the bits\r
+         n = pr;\r
+         if (n > 13)\r
+            n = 13;\r
+         if (pr > (stbsp__int32)n)\r
+            tz = pr - n;\r
+         pr = 0;\r
+         while (n--) {\r
+            *s++ = h[(n64 >> 60) & 15];\r
+            n64 <<= 4;\r
+         }\r
+\r
+         // print the expo\r
+         tail[1] = h[17];\r
+         if (dp < 0) {\r
+            tail[2] = '-';\r
+            dp = -dp;\r
+         } else\r
+            tail[2] = '+';\r
+         n = (dp >= 1000) ? 6 : ((dp >= 100) ? 5 : ((dp >= 10) ? 4 : 3));\r
+         tail[0] = (char)n;\r
+         for (;;) {\r
+            tail[n] = '0' + dp % 10;\r
+            if (n <= 3)\r
+               break;\r
+            --n;\r
+            dp /= 10;\r
+         }\r
+\r
+         dp = (int)(s - sn);\r
+         l = (int)(s - (num + 64));\r
+         s = num + 64;\r
+         cs = 1 + (3 << 24);\r
+         goto scopy;\r
+\r
+      case 'G': // float\r
+      case 'g': // float\r
+         h = (f[0] == 'G') ? hexu : hex;\r
+         fv = va_arg(va, double);\r
+         if (pr == -1)\r
+            pr = 6;\r
+         else if (pr == 0)\r
+            pr = 1; // default is 6\r
+         // read the double into a string\r
+         if (stbsp__real_to_str(&sn, &l, num, &dp, fv, (pr - 1) | 0x80000000))\r
+            fl |= STBSP__NEGATIVE;\r
+\r
+         // clamp the precision and delete extra zeros after clamp\r
+         n = pr;\r
+         if (l > (stbsp__uint32)pr)\r
+            l = pr;\r
+         while ((l > 1) && (pr) && (sn[l - 1] == '0')) {\r
+            --pr;\r
+            --l;\r
+         }\r
+\r
+         // should we use %e\r
+         if ((dp <= -4) || (dp > (stbsp__int32)n)) {\r
+            if (pr > (stbsp__int32)l)\r
+               pr = l - 1;\r
+            else if (pr)\r
+               --pr; // when using %e, there is one digit before the decimal\r
+            goto doexpfromg;\r
+         }\r
+         // this is the insane action to get the pr to match %g semantics for %f\r
+         if (dp > 0) {\r
+            pr = (dp < (stbsp__int32)l) ? l - dp : 0;\r
+         } else {\r
+            pr = -dp + ((pr > (stbsp__int32)l) ? (stbsp__int32) l : pr);\r
+         }\r
+         goto dofloatfromg;\r
+\r
+      case 'E': // float\r
+      case 'e': // float\r
+         h = (f[0] == 'E') ? hexu : hex;\r
+         fv = va_arg(va, double);\r
+         if (pr == -1)\r
+            pr = 6; // default is 6\r
+         // read the double into a string\r
+         if (stbsp__real_to_str(&sn, &l, num, &dp, fv, pr | 0x80000000))\r
+            fl |= STBSP__NEGATIVE;\r
+      doexpfromg:\r
+         tail[0] = 0;\r
+         stbsp__lead_sign(fl, lead);\r
+         if (dp == STBSP__SPECIAL) {\r
+            s = (char *)sn;\r
+            cs = 0;\r
+            pr = 0;\r
+            goto scopy;\r
+         }\r
+         s = num + 64;\r
+         // handle leading chars\r
+         *s++ = sn[0];\r
+\r
+         if (pr)\r
+            *s++ = stbsp__period;\r
+\r
+         // handle after decimal\r
+         if ((l - 1) > (stbsp__uint32)pr)\r
+            l = pr + 1;\r
+         for (n = 1; n < l; n++)\r
+            *s++ = sn[n];\r
+         // trailing zeros\r
+         tz = pr - (l - 1);\r
+         pr = 0;\r
+         // dump expo\r
+         tail[1] = h[0xe];\r
+         dp -= 1;\r
+         if (dp < 0) {\r
+            tail[2] = '-';\r
+            dp = -dp;\r
+         } else\r
+            tail[2] = '+';\r
+#ifdef STB_SPRINTF_MSVC_MODE\r
+         n = 5;\r
+#else\r
+         n = (dp >= 100) ? 5 : 4;\r
+#endif\r
+         tail[0] = (char)n;\r
+         for (;;) {\r
+            tail[n] = '0' + dp % 10;\r
+            if (n <= 3)\r
+               break;\r
+            --n;\r
+            dp /= 10;\r
+         }\r
+         cs = 1 + (3 << 24); // how many tens\r
+         goto flt_lead;\r
+\r
+      case 'f': // float\r
+         fv = va_arg(va, double);\r
+      doafloat:\r
+         // do kilos\r
+         if (fl & STBSP__METRIC_SUFFIX) {\r
+            double divisor;\r
+            divisor = 1000.0f;\r
+            if (fl & STBSP__METRIC_1024)\r
+               divisor = 1024.0;\r
+            while (fl < 0x4000000) {\r
+               if ((fv < divisor) && (fv > -divisor))\r
+                  break;\r
+               fv /= divisor;\r
+               fl += 0x1000000;\r
+            }\r
+         }\r
+         if (pr == -1)\r
+            pr = 6; // default is 6\r
+         // read the double into a string\r
+         if (stbsp__real_to_str(&sn, &l, num, &dp, fv, pr))\r
+            fl |= STBSP__NEGATIVE;\r
+      dofloatfromg:\r
+         tail[0] = 0;\r
+         stbsp__lead_sign(fl, lead);\r
+         if (dp == STBSP__SPECIAL) {\r
+            s = (char *)sn;\r
+            cs = 0;\r
+            pr = 0;\r
+            goto scopy;\r
+         }\r
+         s = num + 64;\r
+\r
+         // handle the three decimal varieties\r
+         if (dp <= 0) {\r
+            stbsp__int32 i;\r
+            // handle 0.000*000xxxx\r
+            *s++ = '0';\r
+            if (pr)\r
+               *s++ = stbsp__period;\r
+            n = -dp;\r
+            if ((stbsp__int32)n > pr)\r
+               n = pr;\r
+            i = n;\r
+            while (i) {\r
+               if ((((stbsp__uintptr)s) & 3) == 0)\r
+                  break;\r
+               *s++ = '0';\r
+               --i;\r
+            }\r
+            while (i >= 4) {\r
+               *(stbsp__uint32 *)s = 0x30303030;\r
+               s += 4;\r
+               i -= 4;\r
+            }\r
+            while (i) {\r
+               *s++ = '0';\r
+               --i;\r
+            }\r
+            if ((stbsp__int32)(l + n) > pr)\r
+               l = pr - n;\r
+            i = l;\r
+            while (i) {\r
+               *s++ = *sn++;\r
+               --i;\r
+            }\r
+            tz = pr - (n + l);\r
+            cs = 1 + (3 << 24); // how many tens did we write (for commas below)\r
+         } else {\r
+            cs = (fl & STBSP__TRIPLET_COMMA) ? ((600 - (stbsp__uint32)dp) % 3) : 0;\r
+            if ((stbsp__uint32)dp >= l) {\r
+               // handle xxxx000*000.0\r
+               n = 0;\r
+               for (;;) {\r
+                  if ((fl & STBSP__TRIPLET_COMMA) && (++cs == 4)) {\r
+                     cs = 0;\r
+                     *s++ = stbsp__comma;\r
+                  } else {\r
+                     *s++ = sn[n];\r
+                     ++n;\r
+                     if (n >= l)\r
+                        break;\r
+                  }\r
+               }\r
+               if (n < (stbsp__uint32)dp) {\r
+                  n = dp - n;\r
+                  if ((fl & STBSP__TRIPLET_COMMA) == 0) {\r
+                     while (n) {\r
+                        if ((((stbsp__uintptr)s) & 3) == 0)\r
+                           break;\r
+                        *s++ = '0';\r
+                        --n;\r
+                     }\r
+                     while (n >= 4) {\r
+                        *(stbsp__uint32 *)s = 0x30303030;\r
+                        s += 4;\r
+                        n -= 4;\r
+                     }\r
+                  }\r
+                  while (n) {\r
+                     if ((fl & STBSP__TRIPLET_COMMA) && (++cs == 4)) {\r
+                        cs = 0;\r
+                        *s++ = stbsp__comma;\r
+                     } else {\r
+                        *s++ = '0';\r
+                        --n;\r
+                     }\r
+                  }\r
+               }\r
+               cs = (int)(s - (num + 64)) + (3 << 24); // cs is how many tens\r
+               if (pr) {\r
+                  *s++ = stbsp__period;\r
+                  tz = pr;\r
+               }\r
+            } else {\r
+               // handle xxxxx.xxxx000*000\r
+               n = 0;\r
+               for (;;) {\r
+                  if ((fl & STBSP__TRIPLET_COMMA) && (++cs == 4)) {\r
+                     cs = 0;\r
+                     *s++ = stbsp__comma;\r
+                  } else {\r
+                     *s++ = sn[n];\r
+                     ++n;\r
+                     if (n >= (stbsp__uint32)dp)\r
+                        break;\r
+                  }\r
+               }\r
+               cs = (int)(s - (num + 64)) + (3 << 24); // cs is how many tens\r
+               if (pr)\r
+                  *s++ = stbsp__period;\r
+               if ((l - dp) > (stbsp__uint32)pr)\r
+                  l = pr + dp;\r
+               while (n < l) {\r
+                  *s++ = sn[n];\r
+                  ++n;\r
+               }\r
+               tz = pr - (l - dp);\r
+            }\r
+         }\r
+         pr = 0;\r
+\r
+         // handle k,m,g,t\r
+         if (fl & STBSP__METRIC_SUFFIX) {\r
+            char idx;\r
+            idx = 1;\r
+            if (fl & STBSP__METRIC_NOSPACE)\r
+               idx = 0;\r
+            tail[0] = idx;\r
+            tail[1] = ' ';\r
+            {\r
+               if (fl >> 24) { // SI kilo is 'k', JEDEC and SI kibits are 'K'.\r
+                  if (fl & STBSP__METRIC_1024)\r
+                     tail[idx + 1] = "_KMGT"[fl >> 24];\r
+                  else\r
+                     tail[idx + 1] = "_kMGT"[fl >> 24];\r
+                  idx++;\r
+                  // If printing kibits and not in jedec, add the 'i'.\r
+                  if (fl & STBSP__METRIC_1024 && !(fl & STBSP__METRIC_JEDEC)) {\r
+                     tail[idx + 1] = 'i';\r
+                     idx++;\r
+                  }\r
+                  tail[0] = idx;\r
+               }\r
+            }\r
+         };\r
+\r
+      flt_lead:\r
+         // get the length that we copied\r
+         l = (stbsp__uint32)(s - (num + 64));\r
+         s = num + 64;\r
+         goto scopy;\r
+#endif\r
+\r
+      case 'B': // upper binary\r
+      case 'b': // lower binary\r
+         h = (f[0] == 'B') ? hexu : hex;\r
+         lead[0] = 0;\r
+         if (fl & STBSP__LEADING_0X) {\r
+            lead[0] = 2;\r
+            lead[1] = '0';\r
+            lead[2] = h[0xb];\r
+         }\r
+         l = (8 << 4) | (1 << 8);\r
+         goto radixnum;\r
+\r
+      case 'o': // octal\r
+         h = hexu;\r
+         lead[0] = 0;\r
+         if (fl & STBSP__LEADING_0X) {\r
+            lead[0] = 1;\r
+            lead[1] = '0';\r
+         }\r
+         l = (3 << 4) | (3 << 8);\r
+         goto radixnum;\r
+\r
+      case 'p': // pointer\r
+         fl |= (sizeof(void *) == 8) ? STBSP__INTMAX : 0;\r
+         pr = sizeof(void *) * 2;\r
+         fl &= ~STBSP__LEADINGZERO; // 'p' only prints the pointer with zeros\r
+                                    // fall through - to X\r
+\r
+      case 'X': // upper hex\r
+      case 'x': // lower hex\r
+         h = (f[0] == 'X') ? hexu : hex;\r
+         l = (4 << 4) | (4 << 8);\r
+         lead[0] = 0;\r
+         if (fl & STBSP__LEADING_0X) {\r
+            lead[0] = 2;\r
+            lead[1] = '0';\r
+            lead[2] = h[16];\r
+         }\r
+      radixnum:\r
+         // get the number\r
+         if (fl & STBSP__INTMAX)\r
+            n64 = va_arg(va, stbsp__uint64);\r
+         else\r
+            n64 = va_arg(va, stbsp__uint32);\r
+\r
+         s = num + STBSP__NUMSZ;\r
+         dp = 0;\r
+         // clear tail, and clear leading if value is zero\r
+         tail[0] = 0;\r
+         if (n64 == 0) {\r
+            lead[0] = 0;\r
+            if (pr == 0) {\r
+               l = 0;\r
+               cs = 0;\r
+               goto scopy;\r
+            }\r
+         }\r
+         // convert to string\r
+         for (;;) {\r
+            *--s = h[n64 & ((1 << (l >> 8)) - 1)];\r
+            n64 >>= (l >> 8);\r
+            if (!((n64) || ((stbsp__int32)((num + STBSP__NUMSZ) - s) < pr)))\r
+               break;\r
+            if (fl & STBSP__TRIPLET_COMMA) {\r
+               ++l;\r
+               if ((l & 15) == ((l >> 4) & 15)) {\r
+                  l &= ~15;\r
+                  *--s = stbsp__comma;\r
+               }\r
+            }\r
+         };\r
+         // get the tens and the comma pos\r
+         cs = (stbsp__uint32)((num + STBSP__NUMSZ) - s) + ((((l >> 4) & 15)) << 24);\r
+         // get the length that we copied\r
+         l = (stbsp__uint32)((num + STBSP__NUMSZ) - s);\r
+         // copy it\r
+         goto scopy;\r
+\r
+      case 'u': // unsigned\r
+      case 'i':\r
+      case 'd': // integer\r
+         // get the integer and abs it\r
+         if (fl & STBSP__INTMAX) {\r
+            stbsp__int64 i64 = va_arg(va, stbsp__int64);\r
+            n64 = (stbsp__uint64)i64;\r
+            if ((f[0] != 'u') && (i64 < 0)) {\r
+               n64 = (stbsp__uint64)-i64;\r
+               fl |= STBSP__NEGATIVE;\r
+            }\r
+         } else {\r
+            stbsp__int32 i = va_arg(va, stbsp__int32);\r
+            n64 = (stbsp__uint32)i;\r
+            if ((f[0] != 'u') && (i < 0)) {\r
+               n64 = (stbsp__uint32)-i;\r
+               fl |= STBSP__NEGATIVE;\r
+            }\r
+         }\r
+\r
+#ifndef STB_SPRINTF_NOFLOAT\r
+         if (fl & STBSP__METRIC_SUFFIX) {\r
+            if (n64 < 1024)\r
+               pr = 0;\r
+            else if (pr == -1)\r
+               pr = 1;\r
+            fv = (double)(stbsp__int64)n64;\r
+            goto doafloat;\r
+         }\r
+#endif\r
+\r
+         // convert to string\r
+         s = num + STBSP__NUMSZ;\r
+         l = 0;\r
+\r
+         for (;;) {\r
+            // do in 32-bit chunks (avoid lots of 64-bit divides even with constant denominators)\r
+            char *o = s - 8;\r
+            if (n64 >= 100000000) {\r
+               n = (stbsp__uint32)(n64 % 100000000);\r
+               n64 /= 100000000;\r
+            } else {\r
+               n = (stbsp__uint32)n64;\r
+               n64 = 0;\r
+            }\r
+            if ((fl & STBSP__TRIPLET_COMMA) == 0) {\r
+               do {\r
+                  s -= 2;\r
+                  *(stbsp__uint16 *)s = *(stbsp__uint16 *)&stbsp__digitpair.pair[(n % 100) * 2];\r
+                  n /= 100;\r
+               } while (n);\r
+            }\r
+            while (n) {\r
+               if ((fl & STBSP__TRIPLET_COMMA) && (l++ == 3)) {\r
+                  l = 0;\r
+                  *--s = stbsp__comma;\r
+                  --o;\r
+               } else {\r
+                  *--s = (char)(n % 10) + '0';\r
+                  n /= 10;\r
+               }\r
+            }\r
+            if (n64 == 0) {\r
+               if ((s[0] == '0') && (s != (num + STBSP__NUMSZ)))\r
+                  ++s;\r
+               break;\r
+            }\r
+            while (s != o)\r
+               if ((fl & STBSP__TRIPLET_COMMA) && (l++ == 3)) {\r
+                  l = 0;\r
+                  *--s = stbsp__comma;\r
+                  --o;\r
+               } else {\r
+                  *--s = '0';\r
+               }\r
+         }\r
+\r
+         tail[0] = 0;\r
+         stbsp__lead_sign(fl, lead);\r
+\r
+         // get the length that we copied\r
+         l = (stbsp__uint32)((num + STBSP__NUMSZ) - s);\r
+         if (l == 0) {\r
+            *--s = '0';\r
+            l = 1;\r
+         }\r
+         cs = l + (3 << 24);\r
+         if (pr < 0)\r
+            pr = 0;\r
+\r
+      scopy:\r
+         // get fw=leading/trailing space, pr=leading zeros\r
+         if (pr < (stbsp__int32)l)\r
+            pr = l;\r
+         n = pr + lead[0] + tail[0] + tz;\r
+         if (fw < (stbsp__int32)n)\r
+            fw = n;\r
+         fw -= n;\r
+         pr -= l;\r
+\r
+         // handle right justify and leading zeros\r
+         if ((fl & STBSP__LEFTJUST) == 0) {\r
+            if (fl & STBSP__LEADINGZERO) // if leading zeros, everything is in pr\r
+            {\r
+               pr = (fw > pr) ? fw : pr;\r
+               fw = 0;\r
+            } else {\r
+               fl &= ~STBSP__TRIPLET_COMMA; // if no leading zeros, then no commas\r
+            }\r
+         }\r
+\r
+         // copy the spaces and/or zeros\r
+         if (fw + pr) {\r
+            stbsp__int32 i;\r
+            stbsp__uint32 c;\r
+\r
+            // copy leading spaces (or when doing %8.4d stuff)\r
+            if ((fl & STBSP__LEFTJUST) == 0)\r
+               while (fw > 0) {\r
+                  stbsp__cb_buf_clamp(i, fw);\r
+                  fw -= i;\r
+                  while (i) {\r
+                     if ((((stbsp__uintptr)bf) & 3) == 0)\r
+                        break;\r
+                     *bf++ = ' ';\r
+                     --i;\r
+                  }\r
+                  while (i >= 4) {\r
+                     *(stbsp__uint32 *)bf = 0x20202020;\r
+                     bf += 4;\r
+                     i -= 4;\r
+                  }\r
+                  while (i) {\r
+                     *bf++ = ' ';\r
+                     --i;\r
+                  }\r
+                  stbsp__chk_cb_buf(1);\r
+               }\r
+\r
+            // copy leader\r
+            sn = lead + 1;\r
+            while (lead[0]) {\r
+               stbsp__cb_buf_clamp(i, lead[0]);\r
+               lead[0] -= (char)i;\r
+               while (i) {\r
+                  *bf++ = *sn++;\r
+                  --i;\r
+               }\r
+               stbsp__chk_cb_buf(1);\r
+            }\r
+\r
+            // copy leading zeros\r
+            c = cs >> 24;\r
+            cs &= 0xffffff;\r
+            cs = (fl & STBSP__TRIPLET_COMMA) ? ((stbsp__uint32)(c - ((pr + cs) % (c + 1)))) : 0;\r
+            while (pr > 0) {\r
+               stbsp__cb_buf_clamp(i, pr);\r
+               pr -= i;\r
+               if ((fl & STBSP__TRIPLET_COMMA) == 0) {\r
+                  while (i) {\r
+                     if ((((stbsp__uintptr)bf) & 3) == 0)\r
+                        break;\r
+                     *bf++ = '0';\r
+                     --i;\r
+                  }\r
+                  while (i >= 4) {\r
+                     *(stbsp__uint32 *)bf = 0x30303030;\r
+                     bf += 4;\r
+                     i -= 4;\r
+                  }\r
+               }\r
+               while (i) {\r
+                  if ((fl & STBSP__TRIPLET_COMMA) && (cs++ == c)) {\r
+                     cs = 0;\r
+                     *bf++ = stbsp__comma;\r
+                  } else\r
+                     *bf++ = '0';\r
+                  --i;\r
+               }\r
+               stbsp__chk_cb_buf(1);\r
+            }\r
+         }\r
+\r
+         // copy leader if there is still one\r
+         sn = lead + 1;\r
+         while (lead[0]) {\r
+            stbsp__int32 i;\r
+            stbsp__cb_buf_clamp(i, lead[0]);\r
+            lead[0] -= (char)i;\r
+            while (i) {\r
+               *bf++ = *sn++;\r
+               --i;\r
+            }\r
+            stbsp__chk_cb_buf(1);\r
+         }\r
+\r
+         // copy the string\r
+         n = l;\r
+         while (n) {\r
+            stbsp__int32 i;\r
+            stbsp__cb_buf_clamp(i, n);\r
+            n -= i;\r
+            STBSP__UNALIGNED(while (i >= 4) {\r
+               *(stbsp__uint32 volatile *)bf = *(stbsp__uint32 volatile *)s;\r
+               bf += 4;\r
+               s += 4;\r
+               i -= 4;\r
+            })\r
+            while (i) {\r
+               *bf++ = *s++;\r
+               --i;\r
+            }\r
+            stbsp__chk_cb_buf(1);\r
+         }\r
+\r
+         // copy trailing zeros\r
+         while (tz) {\r
+            stbsp__int32 i;\r
+            stbsp__cb_buf_clamp(i, tz);\r
+            tz -= i;\r
+            while (i) {\r
+               if ((((stbsp__uintptr)bf) & 3) == 0)\r
+                  break;\r
+               *bf++ = '0';\r
+               --i;\r
+            }\r
+            while (i >= 4) {\r
+               *(stbsp__uint32 *)bf = 0x30303030;\r
+               bf += 4;\r
+               i -= 4;\r
+            }\r
+            while (i) {\r
+               *bf++ = '0';\r
+               --i;\r
+            }\r
+            stbsp__chk_cb_buf(1);\r
+         }\r
+\r
+         // copy tail if there is one\r
+         sn = tail + 1;\r
+         while (tail[0]) {\r
+            stbsp__int32 i;\r
+            stbsp__cb_buf_clamp(i, tail[0]);\r
+            tail[0] -= (char)i;\r
+            while (i) {\r
+               *bf++ = *sn++;\r
+               --i;\r
+            }\r
+            stbsp__chk_cb_buf(1);\r
+         }\r
+\r
+         // handle the left justify\r
+         if (fl & STBSP__LEFTJUST)\r
+            if (fw > 0) {\r
+               while (fw) {\r
+                  stbsp__int32 i;\r
+                  stbsp__cb_buf_clamp(i, fw);\r
+                  fw -= i;\r
+                  while (i) {\r
+                     if ((((stbsp__uintptr)bf) & 3) == 0)\r
+                        break;\r
+                     *bf++ = ' ';\r
+                     --i;\r
+                  }\r
+                  while (i >= 4) {\r
+                     *(stbsp__uint32 *)bf = 0x20202020;\r
+                     bf += 4;\r
+                     i -= 4;\r
+                  }\r
+                  while (i--)\r
+                     *bf++ = ' ';\r
+                  stbsp__chk_cb_buf(1);\r
+               }\r
+            }\r
+         break;\r
+\r
+      default: // unknown, just copy code\r
+         s = num + STBSP__NUMSZ - 1;\r
+         *s = f[0];\r
+         l = 1;\r
+         fw = fl = 0;\r
+         lead[0] = 0;\r
+         tail[0] = 0;\r
+         pr = 0;\r
+         dp = 0;\r
+         cs = 0;\r
+         goto scopy;\r
+      }\r
+      ++f;\r
+   }\r
+endfmt:\r
+\r
+   if (!callback)\r
+      *bf = 0;\r
+   else\r
+      stbsp__flush_cb();\r
+\r
+done:\r
+   return tlen + (int)(bf - buf);\r
+}\r
+\r
+// cleanup\r
+#undef STBSP__LEFTJUST\r
+#undef STBSP__LEADINGPLUS\r
+#undef STBSP__LEADINGSPACE\r
+#undef STBSP__LEADING_0X\r
+#undef STBSP__LEADINGZERO\r
+#undef STBSP__INTMAX\r
+#undef STBSP__TRIPLET_COMMA\r
+#undef STBSP__NEGATIVE\r
+#undef STBSP__METRIC_SUFFIX\r
+#undef STBSP__NUMSZ\r
+#undef stbsp__chk_cb_bufL\r
+#undef stbsp__chk_cb_buf\r
+#undef stbsp__flush_cb\r
+#undef stbsp__cb_buf_clamp\r
+\r
+// ============================================================================\r
+//   wrapper functions\r
+\r
+STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(sprintf)(char *buf, char const *fmt, ...)\r
+{\r
+   int result;\r
+   va_list va;\r
+   va_start(va, fmt);\r
+   result = STB_SPRINTF_DECORATE(vsprintfcb)(0, 0, buf, fmt, va);\r
+   va_end(va);\r
+   return result;\r
+}\r
+\r
+typedef struct stbsp__context {\r
+   char *buf;\r
+   int count;\r
+   int length;\r
+   char tmp[STB_SPRINTF_MIN];\r
+} stbsp__context;\r
+\r
+static char *stbsp__clamp_callback(const char *buf, void *user, int len)\r
+{\r
+   stbsp__context *c = (stbsp__context *)user;\r
+   c->length += len;\r
+\r
+   if (len > c->count)\r
+      len = c->count;\r
+\r
+   if (len) {\r
+      if (buf != c->buf) {\r
+         const char *s, *se;\r
+         char *d;\r
+         d = c->buf;\r
+         s = buf;\r
+         se = buf + len;\r
+         do {\r
+            *d++ = *s++;\r
+         } while (s < se);\r
+      }\r
+      c->buf += len;\r
+      c->count -= len;\r
+   }\r
+\r
+   if (c->count <= 0)\r
+      return c->tmp;\r
+   return (c->count >= STB_SPRINTF_MIN) ? c->buf : c->tmp; // go direct into buffer if you can\r
+}\r
+\r
+static char * stbsp__count_clamp_callback( const char * buf, void * user, int len )\r
+{\r
+   stbsp__context * c = (stbsp__context*)user;\r
+   (void) sizeof(buf);\r
+\r
+   c->length += len;\r
+   return c->tmp; // go direct into buffer if you can\r
+}\r
+\r
+STBSP__PUBLICDEF int STB_SPRINTF_DECORATE( vsnprintf )( char * buf, int count, char const * fmt, va_list va )\r
+{\r
+   stbsp__context c;\r
+\r
+   if ( (count == 0) && !buf )\r
+   {\r
+      c.length = 0;\r
+\r
+      STB_SPRINTF_DECORATE( vsprintfcb )( stbsp__count_clamp_callback, &c, c.tmp, fmt, va );\r
+   }\r
+   else\r
+   {\r
+      int l;\r
+\r
+      c.buf = buf;\r
+      c.count = count;\r
+      c.length = 0;\r
+\r
+      STB_SPRINTF_DECORATE( vsprintfcb )( stbsp__clamp_callback, &c, stbsp__clamp_callback(0,&c,0), fmt, va );\r
+\r
+      // zero-terminate\r
+      l = (int)( c.buf - buf );\r
+      if ( l >= count ) // should never be greater, only equal (or less) than count\r
+         l = count - 1;\r
+      buf[l] = 0;\r
+   }\r
+\r
+   return c.length;\r
+}\r
+\r
+STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(snprintf)(char *buf, int count, char const *fmt, ...)\r
+{\r
+   int result;\r
+   va_list va;\r
+   va_start(va, fmt);\r
+\r
+   result = STB_SPRINTF_DECORATE(vsnprintf)(buf, count, fmt, va);\r
+   va_end(va);\r
+\r
+   return result;\r
+}\r
+\r
+STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(vsprintf)(char *buf, char const *fmt, va_list va)\r
+{\r
+   return STB_SPRINTF_DECORATE(vsprintfcb)(0, 0, buf, fmt, va);\r
+}\r
+\r
+// =======================================================================\r
+//   low level float utility functions\r
+\r
+#ifndef STB_SPRINTF_NOFLOAT\r
+\r
+// copies d to bits w/ strict aliasing (this compiles to nothing on /Ox)\r
+#define STBSP__COPYFP(dest, src)                   \\r
+   {                                               \\r
+      int cn;                                      \\r
+      for (cn = 0; cn < 8; cn++)                   \\r
+         ((char *)&dest)[cn] = ((char *)&src)[cn]; \\r
+   }\r
+\r
+// get float info\r
+static stbsp__int32 stbsp__real_to_parts(stbsp__int64 *bits, stbsp__int32 *expo, double value)\r
+{\r
+   double d;\r
+   stbsp__int64 b = 0;\r
+\r
+   // load value and round at the frac_digits\r
+   d = value;\r
+\r
+   STBSP__COPYFP(b, d);\r
+\r
+   *bits = b & ((((stbsp__uint64)1) << 52) - 1);\r
+   *expo = (stbsp__int32)(((b >> 52) & 2047) - 1023);\r
+\r
+   return (stbsp__int32)((stbsp__uint64) b >> 63);\r
+}\r
+\r
+static double const stbsp__bot[23] = {\r
+   1e+000, 1e+001, 1e+002, 1e+003, 1e+004, 1e+005, 1e+006, 1e+007, 1e+008, 1e+009, 1e+010, 1e+011,\r
+   1e+012, 1e+013, 1e+014, 1e+015, 1e+016, 1e+017, 1e+018, 1e+019, 1e+020, 1e+021, 1e+022\r
+};\r
+static double const stbsp__negbot[22] = {\r
+   1e-001, 1e-002, 1e-003, 1e-004, 1e-005, 1e-006, 1e-007, 1e-008, 1e-009, 1e-010, 1e-011,\r
+   1e-012, 1e-013, 1e-014, 1e-015, 1e-016, 1e-017, 1e-018, 1e-019, 1e-020, 1e-021, 1e-022\r
+};\r
+static double const stbsp__negboterr[22] = {\r
+   -5.551115123125783e-018,  -2.0816681711721684e-019, -2.0816681711721686e-020, -4.7921736023859299e-021, -8.1803053914031305e-022, 4.5251888174113741e-023,\r
+   4.5251888174113739e-024,  -2.0922560830128471e-025, -6.2281591457779853e-026, -3.6432197315497743e-027, 6.0503030718060191e-028,  2.0113352370744385e-029,\r
+   -3.0373745563400371e-030, 1.1806906454401013e-032,  -7.7705399876661076e-032, 2.0902213275965398e-033,  -7.1542424054621921e-034, -7.1542424054621926e-035,\r
+   2.4754073164739869e-036,  5.4846728545790429e-037,  9.2462547772103625e-038,  -4.8596774326570872e-039\r
+};\r
+static double const stbsp__top[13] = {\r
+   1e+023, 1e+046, 1e+069, 1e+092, 1e+115, 1e+138, 1e+161, 1e+184, 1e+207, 1e+230, 1e+253, 1e+276, 1e+299\r
+};\r
+static double const stbsp__negtop[13] = {\r
+   1e-023, 1e-046, 1e-069, 1e-092, 1e-115, 1e-138, 1e-161, 1e-184, 1e-207, 1e-230, 1e-253, 1e-276, 1e-299\r
+};\r
+static double const stbsp__toperr[13] = {\r
+   8388608,\r
+   6.8601809640529717e+028,\r
+   -7.253143638152921e+052,\r
+   -4.3377296974619174e+075,\r
+   -1.5559416129466825e+098,\r
+   -3.2841562489204913e+121,\r
+   -3.7745893248228135e+144,\r
+   -1.7356668416969134e+167,\r
+   -3.8893577551088374e+190,\r
+   -9.9566444326005119e+213,\r
+   6.3641293062232429e+236,\r
+   -5.2069140800249813e+259,\r
+   -5.2504760255204387e+282\r
+};\r
+static double const stbsp__negtoperr[13] = {\r
+   3.9565301985100693e-040,  -2.299904345391321e-063,  3.6506201437945798e-086,  1.1875228833981544e-109,\r
+   -5.0644902316928607e-132, -6.7156837247865426e-155, -2.812077463003139e-178,  -5.7778912386589953e-201,\r
+   7.4997100559334532e-224,  -4.6439668915134491e-247, -6.3691100762962136e-270, -9.436808465446358e-293,\r
+   8.0970921678014997e-317\r
+};\r
+\r
+#if defined(_MSC_VER) && (_MSC_VER <= 1200)\r
+static stbsp__uint64 const stbsp__powten[20] = {\r
+   1,\r
+   10,\r
+   100,\r
+   1000,\r
+   10000,\r
+   100000,\r
+   1000000,\r
+   10000000,\r
+   100000000,\r
+   1000000000,\r
+   10000000000,\r
+   100000000000,\r
+   1000000000000,\r
+   10000000000000,\r
+   100000000000000,\r
+   1000000000000000,\r
+   10000000000000000,\r
+   100000000000000000,\r
+   1000000000000000000,\r
+   10000000000000000000U\r
+};\r
+#define stbsp__tento19th ((stbsp__uint64)1000000000000000000)\r
+#else\r
+static stbsp__uint64 const stbsp__powten[20] = {\r
+   1,\r
+   10,\r
+   100,\r
+   1000,\r
+   10000,\r
+   100000,\r
+   1000000,\r
+   10000000,\r
+   100000000,\r
+   1000000000,\r
+   10000000000ULL,\r
+   100000000000ULL,\r
+   1000000000000ULL,\r
+   10000000000000ULL,\r
+   100000000000000ULL,\r
+   1000000000000000ULL,\r
+   10000000000000000ULL,\r
+   100000000000000000ULL,\r
+   1000000000000000000ULL,\r
+   10000000000000000000ULL\r
+};\r
+#define stbsp__tento19th (1000000000000000000ULL)\r
+#endif\r
+\r
+#define stbsp__ddmulthi(oh, ol, xh, yh)                            \\r
+   {                                                               \\r
+      double ahi = 0, alo, bhi = 0, blo;                           \\r
+      stbsp__int64 bt;                                             \\r
+      oh = xh * yh;                                                \\r
+      STBSP__COPYFP(bt, xh);                                       \\r
+      bt &= ((~(stbsp__uint64)0) << 27);                           \\r
+      STBSP__COPYFP(ahi, bt);                                      \\r
+      alo = xh - ahi;                                              \\r
+      STBSP__COPYFP(bt, yh);                                       \\r
+      bt &= ((~(stbsp__uint64)0) << 27);                           \\r
+      STBSP__COPYFP(bhi, bt);                                      \\r
+      blo = yh - bhi;                                              \\r
+      ol = ((ahi * bhi - oh) + ahi * blo + alo * bhi) + alo * blo; \\r
+   }\r
+\r
+#define stbsp__ddtoS64(ob, xh, xl)          \\r
+   {                                        \\r
+      double ahi = 0, alo, vh, t;           \\r
+      ob = (stbsp__int64)xh;                \\r
+      vh = (double)ob;                      \\r
+      ahi = (xh - vh);                      \\r
+      t = (ahi - xh);                       \\r
+      alo = (xh - (ahi - t)) - (vh + t);    \\r
+      ob += (stbsp__int64)(ahi + alo + xl); \\r
+   }\r
+\r
+#define stbsp__ddrenorm(oh, ol) \\r
+   {                            \\r
+      double s;                 \\r
+      s = oh + ol;              \\r
+      ol = ol - (s - oh);       \\r
+      oh = s;                   \\r
+   }\r
+\r
+#define stbsp__ddmultlo(oh, ol, xh, xl, yh, yl) ol = ol + (xh * yl + xl * yh);\r
+\r
+#define stbsp__ddmultlos(oh, ol, xh, yl) ol = ol + (xh * yl);\r
+\r
+static void stbsp__raise_to_power10(double *ohi, double *olo, double d, stbsp__int32 power) // power can be -323 to +350\r
+{\r
+   double ph, pl;\r
+   if ((power >= 0) && (power <= 22)) {\r
+      stbsp__ddmulthi(ph, pl, d, stbsp__bot[power]);\r
+   } else {\r
+      stbsp__int32 e, et, eb;\r
+      double p2h, p2l;\r
+\r
+      e = power;\r
+      if (power < 0)\r
+         e = -e;\r
+      et = (e * 0x2c9) >> 14; /* %23 */\r
+      if (et > 13)\r
+         et = 13;\r
+      eb = e - (et * 23);\r
+\r
+      ph = d;\r
+      pl = 0.0;\r
+      if (power < 0) {\r
+         if (eb) {\r
+            --eb;\r
+            stbsp__ddmulthi(ph, pl, d, stbsp__negbot[eb]);\r
+            stbsp__ddmultlos(ph, pl, d, stbsp__negboterr[eb]);\r
+         }\r
+         if (et) {\r
+            stbsp__ddrenorm(ph, pl);\r
+            --et;\r
+            stbsp__ddmulthi(p2h, p2l, ph, stbsp__negtop[et]);\r
+            stbsp__ddmultlo(p2h, p2l, ph, pl, stbsp__negtop[et], stbsp__negtoperr[et]);\r
+            ph = p2h;\r
+            pl = p2l;\r
+         }\r
+      } else {\r
+         if (eb) {\r
+            e = eb;\r
+            if (eb > 22)\r
+               eb = 22;\r
+            e -= eb;\r
+            stbsp__ddmulthi(ph, pl, d, stbsp__bot[eb]);\r
+            if (e) {\r
+               stbsp__ddrenorm(ph, pl);\r
+               stbsp__ddmulthi(p2h, p2l, ph, stbsp__bot[e]);\r
+               stbsp__ddmultlos(p2h, p2l, stbsp__bot[e], pl);\r
+               ph = p2h;\r
+               pl = p2l;\r
+            }\r
+         }\r
+         if (et) {\r
+            stbsp__ddrenorm(ph, pl);\r
+            --et;\r
+            stbsp__ddmulthi(p2h, p2l, ph, stbsp__top[et]);\r
+            stbsp__ddmultlo(p2h, p2l, ph, pl, stbsp__top[et], stbsp__toperr[et]);\r
+            ph = p2h;\r
+            pl = p2l;\r
+         }\r
+      }\r
+   }\r
+   stbsp__ddrenorm(ph, pl);\r
+   *ohi = ph;\r
+   *olo = pl;\r
+}\r
+\r
+// given a float value, returns the significant bits in bits, and the position of the\r
+//   decimal point in decimal_pos.  +/-INF and NAN are specified by special values\r
+//   returned in the decimal_pos parameter.\r
+// frac_digits is absolute normally, but if you want from first significant digits (got %g and %e), or in 0x80000000\r
+static stbsp__int32 stbsp__real_to_str(char const **start, stbsp__uint32 *len, char *out, stbsp__int32 *decimal_pos, double value, stbsp__uint32 frac_digits)\r
+{\r
+   double d;\r
+   stbsp__int64 bits = 0;\r
+   stbsp__int32 expo, e, ng, tens;\r
+\r
+   d = value;\r
+   STBSP__COPYFP(bits, d);\r
+   expo = (stbsp__int32)((bits >> 52) & 2047);\r
+   ng = (stbsp__int32)((stbsp__uint64) bits >> 63);\r
+   if (ng)\r
+      d = -d;\r
+\r
+   if (expo == 2047) // is nan or inf?\r
+   {\r
+      *start = (bits & ((((stbsp__uint64)1) << 52) - 1)) ? "NaN" : "Inf";\r
+      *decimal_pos = STBSP__SPECIAL;\r
+      *len = 3;\r
+      return ng;\r
+   }\r
+\r
+   if (expo == 0) // is zero or denormal\r
+   {\r
+      if (((stbsp__uint64) bits << 1) == 0) // do zero\r
+      {\r
+         *decimal_pos = 1;\r
+         *start = out;\r
+         out[0] = '0';\r
+         *len = 1;\r
+         return ng;\r
+      }\r
+      // find the right expo for denormals\r
+      {\r
+         stbsp__int64 v = ((stbsp__uint64)1) << 51;\r
+         while ((bits & v) == 0) {\r
+            --expo;\r
+            v >>= 1;\r
+         }\r
+      }\r
+   }\r
+\r
+   // find the decimal exponent as well as the decimal bits of the value\r
+   {\r
+      double ph, pl;\r
+\r
+      // log10 estimate - very specifically tweaked to hit or undershoot by no more than 1 of log10 of all expos 1..2046\r
+      tens = expo - 1023;\r
+      tens = (tens < 0) ? ((tens * 617) / 2048) : (((tens * 1233) / 4096) + 1);\r
+\r
+      // move the significant bits into position and stick them into an int\r
+      stbsp__raise_to_power10(&ph, &pl, d, 18 - tens);\r
+\r
+      // get full as much precision from double-double as possible\r
+      stbsp__ddtoS64(bits, ph, pl);\r
+\r
+      // check if we undershot\r
+      if (((stbsp__uint64)bits) >= stbsp__tento19th)\r
+         ++tens;\r
+   }\r
+\r
+   // now do the rounding in integer land\r
+   frac_digits = (frac_digits & 0x80000000) ? ((frac_digits & 0x7ffffff) + 1) : (tens + frac_digits);\r
+   if ((frac_digits < 24)) {\r
+      stbsp__uint32 dg = 1;\r
+      if ((stbsp__uint64)bits >= stbsp__powten[9])\r
+         dg = 10;\r
+      while ((stbsp__uint64)bits >= stbsp__powten[dg]) {\r
+         ++dg;\r
+         if (dg == 20)\r
+            goto noround;\r
+      }\r
+      if (frac_digits < dg) {\r
+         stbsp__uint64 r;\r
+         // add 0.5 at the right position and round\r
+         e = dg - frac_digits;\r
+         if ((stbsp__uint32)e >= 24)\r
+            goto noround;\r
+         r = stbsp__powten[e];\r
+         bits = bits + (r / 2);\r
+         if ((stbsp__uint64)bits >= stbsp__powten[dg])\r
+            ++tens;\r
+         bits /= r;\r
+      }\r
+   noround:;\r
+   }\r
+\r
+   // kill long trailing runs of zeros\r
+   if (bits) {\r
+      stbsp__uint32 n;\r
+      for (;;) {\r
+         if (bits <= 0xffffffff)\r
+            break;\r
+         if (bits % 1000)\r
+            goto donez;\r
+         bits /= 1000;\r
+      }\r
+      n = (stbsp__uint32)bits;\r
+      while ((n % 1000) == 0)\r
+         n /= 1000;\r
+      bits = n;\r
+   donez:;\r
+   }\r
+\r
+   // convert to string\r
+   out += 64;\r
+   e = 0;\r
+   for (;;) {\r
+      stbsp__uint32 n;\r
+      char *o = out - 8;\r
+      // do the conversion in chunks of U32s (avoid most 64-bit divides, worth it, constant denomiators be damned)\r
+      if (bits >= 100000000) {\r
+         n = (stbsp__uint32)(bits % 100000000);\r
+         bits /= 100000000;\r
+      } else {\r
+         n = (stbsp__uint32)bits;\r
+         bits = 0;\r
+      }\r
+      while (n) {\r
+         out -= 2;\r
+         *(stbsp__uint16 *)out = *(stbsp__uint16 *)&stbsp__digitpair.pair[(n % 100) * 2];\r
+         n /= 100;\r
+         e += 2;\r
+      }\r
+      if (bits == 0) {\r
+         if ((e) && (out[0] == '0')) {\r
+            ++out;\r
+            --e;\r
+         }\r
+         break;\r
+      }\r
+      while (out != o) {\r
+         *--out = '0';\r
+         ++e;\r
+      }\r
+   }\r
+\r
+   *decimal_pos = tens;\r
+   *start = out;\r
+   *len = e;\r
+   return ng;\r
+}\r
+\r
+#undef stbsp__ddmulthi\r
+#undef stbsp__ddrenorm\r
+#undef stbsp__ddmultlo\r
+#undef stbsp__ddmultlos\r
+#undef STBSP__SPECIAL\r
+#undef STBSP__COPYFP\r
+\r
+#endif // STB_SPRINTF_NOFLOAT\r
+\r
+// clean up\r
+#undef stbsp__uint16\r
+#undef stbsp__uint32\r
+#undef stbsp__int32\r
+#undef stbsp__uint64\r
+#undef stbsp__int64\r
+#undef STBSP__UNALIGNED\r
+\r
+#endif // STB_SPRINTF_IMPLEMENTATION\r
+\r
+/*\r
+------------------------------------------------------------------------------\r
+This software is available under 2 licenses -- choose whichever you prefer.\r
+------------------------------------------------------------------------------\r
+ALTERNATIVE A - MIT License\r
+Copyright (c) 2017 Sean Barrett\r
+Permission is hereby granted, free of charge, to any person obtaining a copy of\r
+this software and associated documentation files (the "Software"), to deal in\r
+the Software without restriction, including without limitation the rights to\r
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies\r
+of the Software, and to permit persons to whom the Software is furnished to do\r
+so, subject to the following conditions:\r
+The above copyright notice and this permission notice shall be included in all\r
+copies or substantial portions of the Software.\r
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\r
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\r
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\r
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
+SOFTWARE.\r
+------------------------------------------------------------------------------\r
+ALTERNATIVE B - Public Domain (www.unlicense.org)\r
+This is free and unencumbered software released into the public domain.\r
+Anyone is free to copy, modify, publish, use, compile, sell, or distribute this\r
+software, either in source code form or as a compiled binary, for any purpose,\r
+commercial or non-commercial, and by any means.\r
+In jurisdictions that recognize copyright laws, the author or authors of this\r
+software dedicate any and all copyright interest in the software to the public\r
+domain. We make this dedication for the benefit of the public at large and to\r
+the detriment of our heirs and successors. We intend this dedication to be an\r
+overt act of relinquishment in perpetuity of all present and future rights to\r
+this software under copyright law.\r
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\r
+AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
+ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\r
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r
+------------------------------------------------------------------------------\r
+*/\r
+// << fold <<\r
+\r
+// ----------------------------------------------------------------------------------------------------\r
+\r
+// My things implementation:\r
+\r
+//\r
+// OS\r
+//\r
+\r
+#ifdef _WIN32\r
+void os_get_timedate(Arena* a) {\r
+    (void)a;\r
+}\r
+#endif // _WIN32\r
+#ifdef linux\r
+void os_get_timedate(Arena* a) {\r
+    (void)a;\r
+}\r
+#endif // linux\r
+\r
+\r
+#ifdef _WIN32\r
+// Winapi\r
+cstr winapi_get_last_error_str(void) {\r
+  DWORD error_code = GetLastError();\r
+  if (!FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, error_code, 0, winapi_error_msg_buff, WINAPI_ERROR_MSG_BUFF_SIZE, NULL)){\r
+    log_f(C_LOG_ERROR, "FormatMessage() failed with code: %d", GetLastError());\r
+    return NULL;\r
+  }\r
+  return winapi_error_msg_buff;\r
+}\r
+\r
+cstr winapi_get_current_working_directory(Arena* arena) {\r
+  if (arena->buff_size < MAX_PATH) {\r
+    log_error("Arena size should be at least of size MAX_PATH(%u)", MAX_PATH);\r
+    return NULL;\r
+  }\r
+\r
+  size_t n = GetCurrentDirectoryA((DWORD)arena->buff_size, arena->buff);\r
+  if (n == 0) {\r
+    log_error("%s -> %s", __func__, winapi_get_last_error_str());\r
+    return NULL;\r
+  }\r
+  return arena->buff;\r
+}\r
+\r
+bool output_str(cstr text) {\r
+  return output_strn(text, strlen(text));\r
+}\r
+\r
+bool output_strn(cstr text, size_t text_len) {\r
+  SECURITY_ATTRIBUTES sa = {0};\r
+  sa.nLength = sizeof(sa);\r
+  sa.bInheritHandle = TRUE;\r
+\r
+  HANDLE console = CreateFileA("CONOUT$",\r
+                              GENERIC_READ|GENERIC_WRITE,\r
+                              FILE_SHARE_WRITE,\r
+                              &sa,\r
+                              OPEN_EXISTING,\r
+                              0,\r
+                              NULL);\r
+\r
+  if (console == INVALID_HANDLE_VALUE) {\r
+    log_f(C_LOG_INFO, "Could not get a handle to the active console screen buffer: %s", winapi_get_last_error_str());\r
+    return 1;\r
+  }\r
+\r
+  if (text_len == 0) return false;\r
+\r
+  ASSERT(text_len < WINAPI_OUTPUT_STR_BUFF_SIZE);\r
+  CHAR_INFO* buff = winapi_output_str_buff;\r
+\r
+  CONSOLE_SCREEN_BUFFER_INFO csbi = {0};\r
+\r
+  if (!GetConsoleScreenBufferInfo(console, &csbi)) {\r
+    log_f(C_LOG_ERROR, "Could not get console screen buffer info: %s", winapi_get_last_error_str());\r
+    return false;\r
+  }\r
+\r
+  COORD buff_size = {0, 1};\r
+  COORD new_cursor = {\r
+    .X = csbi.dwCursorPosition.X,\r
+    .Y = csbi.dwCursorPosition.Y\r
+  };\r
+\r
+  bool overflown = false;\r
+\r
+  char* next_text = NULL;\r
+\r
+  for (size_t i = 0; i < text_len; ++i) {\r
+    if (text[i] == '\n') {\r
+      overflown = true;\r
+      next_text = (char*)text+buff_size.X+1;\r
+      text_len = (text+text_len)-text;\r
+      break;\r
+    } else {\r
+      buff[i].Char.AsciiChar = text[i];\r
+      buff_size.X++;\r
+      new_cursor.X++;\r
+      if (new_cursor.X >= csbi.dwMaximumWindowSize.X) {\r
+       overflown = true;\r
+       next_text = (char*)text+buff_size.X;\r
+       text_len = (text+text_len)-text;\r
+       break;\r
+      }\r
+    }\r
+\r
+    buff[i].Attributes = FOREGROUND_RED|FOREGROUND_INTENSITY;\r
+  }\r
+\r
+  COORD write_coord = {0, 0};\r
+  SMALL_RECT rect = {\r
+    .Left   = csbi.dwCursorPosition.X,\r
+    .Top    = csbi.dwCursorPosition.Y,\r
+    .Right  = new_cursor.X,\r
+    .Bottom = new_cursor.Y,\r
+  };\r
+\r
+  if(!WriteConsoleOutput(console,\r
+                        buff,\r
+                        buff_size,\r
+                        write_coord,\r
+                        &rect)) {\r
+    log_f(C_LOG_ERROR, "[1] Could not write to console output: [%d] %s", GetLastError(), winapi_get_last_error_str());\r
+    return false;\r
+  }\r
+\r
+  if (!overflown) {\r
+    // advance cursor\r
+    if (!SetConsoleCursorPosition(console, new_cursor)) {\r
+      log_f(C_LOG_ERROR, "[1] Could not set new cursor pos: %s", winapi_get_last_error_str());\r
+      return false;\r
+    }\r
+  }\r
+\r
+  if (overflown) {\r
+    new_cursor.X = 0;\r
+    new_cursor.Y++;\r
+    // advance cursor\r
+    if (!SetConsoleCursorPosition(console, new_cursor)) {\r
+      log_f(C_LOG_ERROR, "[2] Could not set new cursor pos: %s", winapi_get_last_error_str());\r
+      return false;\r
+    }\r
+    output_strn(next_text, text_len);\r
+  }\r
+\r
+  return true;\r
+}\r
+\r
+#endif // _WIN32\r
+\r
+void panic_assertion(cstr msg, FILE* file, cstr filename, int line) {\r
+  fprintf(file, "%s:%d:0: ASSERTION FAILED: %s\n", filename, line, msg);\r
+  exit(1);\r
+}\r
+\r
+void log_file(Log_type type, FILE* file, cstr fmt, ...) {\r
+  va_list args;\r
+  va_start(args, fmt);\r
+\r
+  // SYSTEMTIME sys_time = {0};\r
+  // GetLocalTime(&sys_time);\r
+\r
+  // fprintf(file, "[%02d:%02d:%02d] ", sys_time.wHour, sys_time.wMinute, sys_time.wSecond);\r
+\r
+  switch (type){\r
+  case C_LOG_INFO: fprintf(file, "INFO: "); break;\r
+  case C_LOG_ERROR: fprintf(file, "ERROR: "); break;\r
+  case C_LOG_WARNING: fprintf(file, "WARNING: "); break;\r
+  default: ASSERT(0 && "Unreachable");\r
+  }\r
+\r
+  while (*fmt != '\0'){\r
+char_jmp:\r
+    if (*fmt == '%'){\r
+      fmt++;\r
+/* fmt_jmp: */\r
+      switch (*fmt){\r
+      case 's': {\r
+        const char* str = va_arg(args, const char*);\r
+        fprintf(file, "%s", str);\r
+      } break;\r
+      case 'i':\r
+      case 'd': {\r
+        int i = va_arg(args, int);\r
+        fprintf(file, "%d", i);\r
+      } break;\r
+      case 'o': {\r
+        int i = va_arg(args, int);\r
+        fprintf(file, "%o", i);\r
+      } break;\r
+      case 'u': {\r
+        unsigned int i = va_arg(args, unsigned int);\r
+        fprintf(file, "%u", i);\r
+      } break;\r
+      case 'f':\r
+      case 'F': {\r
+        double i = va_arg(args, double);\r
+        fprintf(file, "%f", i);\r
+      } break;\r
+      case 'p': {\r
+        void* i = va_arg(args, void*);\r
+        fprintf(file, "%p", i);\r
+      } break;\r
+      case '%': {\r
+        fprintf(file, "%%");\r
+      } break;\r
+      case 'c': {\r
+        int i = va_arg(args, int);\r
+        fprintf(file, "%c", i);\r
+      } break;\r
+      case '.': {\r
+        fmt++;\r
+        if (fmt[0] == '*' &&\r
+            fmt[1] == 's') {\r
+            fmt += 1;\r
+            int count = va_arg(args, int);\r
+            char* str = va_arg(args, char*);\r
+            while (count-- > 0) {\r
+                fputc(*str++, file);\r
+            }\r
+        } else {\r
+            fputc('%', file);\r
+            fputc('.', file);\r
+            goto char_jmp;\r
+        }\r
+      } break;\r
+      default: {\r
+        fputc(*fmt, file);\r
+      } break;\r
+      }\r
+    } else {\r
+      fputc(*fmt, file);\r
+    }\r
+    if (*fmt == '\0') { break; };\r
+    fmt++;\r
+  }\r
+\r
+  fputc('\n', file);\r
+#ifdef FLUSH_ON_LOG\r
+  fflush(file);\r
+#endif\r
+  va_end(args);\r
+}\r
+\r
+// simple and dirty way to have defering in C (not recommended to use!)\r
+#define defer(ret_val) \\r
+  result = ret_val;\\r
+  goto defer\r
+\r
+const char* slurp_file(const char* filename) {\r
+  FILE* f = fopen(filename, "rb");\r
+  char* result = NULL;\r
+\r
+  if (f == NULL){\r
+    log_f(C_LOG_ERROR, "slurp_file::fopen(\"%s\", \"rb\") -> %s\n", filename, strerror(errno));\r
+    defer(NULL);\r
+  }\r
+\r
+  if (fseek(f, 0, SEEK_END) < 0) {\r
+    log_f(C_LOG_ERROR, "slurp_file::fseek(f, 0, SEEK_END) -> %s\n", filename, strerror(errno));\r
+    defer(NULL);\r
+  }\r
+\r
+  size_t fsize = ftell(f);\r
+\r
+  if (fsize == (size_t)-1){\r
+    log_f(C_LOG_ERROR, "slurp_file::ftell(f) -> %s\n", filename, strerror(errno));\r
+    defer(NULL);\r
+  }\r
+\r
+  result = malloc(sizeof(char)*(fsize+1));\r
+\r
+  if (result == NULL){\r
+    log_f(C_LOG_ERROR, "slurp_file::malloc(%zu) -> %s\n", sizeof(char)*fsize, strerror(errno));\r
+    defer(NULL);\r
+  }\r
+\r
+  if (fseek(f, 0, SEEK_SET) < 0) {\r
+    log_f(C_LOG_ERROR, "slurp_file::fseek(f, 0, SEEK_SET) -> %s\n", filename, strerror(errno));\r
+    defer(NULL);\r
+  }\r
+\r
+  if (fread((char*)result, sizeof(char), fsize, f) != fsize){\r
+    log_f(C_LOG_ERROR, "slurp_file::fread(result, %d, 1, f) -> %s\n", fsize, strerror(errno));\r
+    defer(NULL);\r
+  }\r
+\r
+  // set null-terminator\r
+  result[fsize] = '\0';\r
+\r
+ defer:\r
+  if (f) fclose(f);\r
+  return result;\r
+}\r
+\r
+\r
+//\r
+// ### Allocators ###\r
+//\r
+\r
+// Arena\r
+\r
+Arena Arena_make(size_t size) {\r
+  Arena res = {0};\r
+  res.buff_size = size == 0 ? ARENA_BUFF_INITIAL_SIZE : size;\r
+  res.buff = malloc(res.buff_size);\r
+  res.ptr = res.buff;\r
+\r
+  ASSERT(res.buff);\r
+\r
+  return res;\r
+}\r
+\r
+void* Arena_alloc(Arena* a, size_t size) {\r
+  ASSERT(a->buff);\r
+\r
+  void* res = a->ptr;\r
+  a->ptr = (uint8*)a->ptr + size;\r
+\r
+  // TODO: realloc buff with greater buff_size\r
+  size_t diff = (size_t)((uint8*)a->ptr - (uint8*)a->buff);\r
+  if (diff > a->buff_size) {\r
+    log_info("Arena resized from %u to %u", a->buff_size, a->buff_size*2);\r
+    a->buff_size *= 2;\r
+    a->buff = realloc(a->buff, a->buff_size);\r
+    a->ptr = (uint8*)a->buff + diff;\r
+    res = a->ptr;\r
+    a->ptr = (uint8*)a->ptr + size;\r
+  }\r
+  /* ASSERT((size_t)((uint8*)a->ptr - (uint8*)a->buff) <= a->buff_size); */\r
+\r
+  return res;\r
+}\r
+\r
+void Arena_reset(Arena* a) {\r
+  a->ptr = a->buff;\r
+}\r
+\r
+void Arena_free(Arena* a) {\r
+  free(a->buff);\r
+}\r
+\r
+//\r
+// String\r
+//\r
+\r
+bool cstr_to_wstr(Arena* warena, cstr str) {\r
+  if (warena->buff_size <= 0) {\r
+    log_error("Arena size is 0!!!");\r
+    return false;\r
+  }\r
+\r
+  size_t count = strlen(str)+1;\r
+  size_t ret = mbstowcs((wchar*)warena->buff,\r
+                 str,\r
+                 count);\r
+\r
+  if (ret == (uint)-1) {\r
+    log_error("Encountered an invalid multibyte character!");\r
+    return false;\r
+  } else if (ret == count) {\r
+    log_error("The wide-character string isn't null-terminated!");\r
+    return false;\r
+  }\r
+  return true;\r
+}\r
+\r
+//\r
+// String view\r
+//\r
+\r
+void sv_print_dumb(String_view sv){\r
+  for (size_t i = 0; i < (size_t)sv.count; ++i){\r
+    putc(*(sv.data+i), stdout);\r
+  }\r
+}\r
+\r
+String_view sv_from_cstr(const char* cstr){\r
+  return (String_view){\r
+    .data = cstr,\r
+    .count = strlen(cstr),\r
+  };\r
+}\r
+\r
+String_view sv_lpop(String_view* sv, uint32 n) {\r
+  String_view res = {0};\r
+  if (sv->count < n) return res;\r
+  res.data = sv->data;\r
+  res.count = n;\r
+\r
+  sv->data += n;\r
+  sv->count -= n;\r
+\r
+  return res;\r
+}\r
+\r
+String_view sv_lpop_until_predicate(String_view* sv, int(*predicate)(int)){\r
+  const char* old_sv_data = sv->data;\r
+  while (sv->count > 0 && !predicate(*sv->data)){\r
+    sv->data++;\r
+    sv->count--;\r
+  }\r
+\r
+  return (String_view){\r
+    .data = sv->data - (sv->data - old_sv_data),\r
+    .count = (sv->data - old_sv_data),\r
+  };\r
+}\r
+\r
+String_view sv_rpop_until_predicate(String_view* sv, int(*predicate)(int)){\r
+  size_t old_sv_count = sv->count;\r
+  while (sv->count > 0 && !predicate(*(sv->data+sv->count-1))){\r
+    sv->count--;\r
+  }\r
+\r
+  return (String_view){\r
+    .data = sv->data + sv->count,\r
+    .count = old_sv_count - sv->count,\r
+  };\r
+}\r
+\r
+String_view sv_lpop_until_char(String_view* sv, char ch){\r
+  const char* old_sv_data = sv->data;\r
+  while (sv->count > 0 && *sv->data != ch){\r
+    sv->data++;\r
+    sv->count--;\r
+  }\r
+\r
+  return (String_view){\r
+    .data = sv->data - (sv->data - old_sv_data),\r
+    .count = (sv->data - old_sv_data),\r
+  };\r
+}\r
+\r
+String_view sv_rpop_until_char(String_view* sv, char ch){\r
+  size_t old_sv_count = sv->count;\r
+  while (sv->count > 0 && *(sv->data+sv->count-1) != ch){\r
+    sv->count--;\r
+  }\r
+\r
+  return (String_view){\r
+    .data = sv->data + sv->count,\r
+    .count = old_sv_count - sv->count,\r
+  };\r
+}\r
+\r
+void sv_lremove(String_view* sv, size_t n){\r
+  if (n > sv->count) n = sv->count;\r
+\r
+  sv->data += n;\r
+  sv->count -= n;\r
+}\r
+\r
+void sv_rremove(String_view* sv, size_t n){\r
+  if (n > sv->count)\r
+    sv->count = 0;\r
+  else\r
+    sv->count -= n;\r
+}\r
+\r
+void sv_lremove_until_char(String_view* sv, char ch){\r
+  while (sv->count > 0 && *sv->data != ch){\r
+    sv->data++;\r
+    sv->count--;\r
+  }\r
+}\r
+\r
+void sv_rremove_until_char(String_view* sv, char ch){\r
+  while (sv->count > 0 && *(sv->data+sv->count-1) != ch){\r
+    sv->count--;\r
+  }\r
+}\r
+\r
+void sv_lremove_until_char_after(String_view* sv, char ch){\r
+  while (sv->count > 0 && *(sv->data-1) != ch){\r
+    sv->data++;\r
+    sv->count--;\r
+  }\r
+}\r
+\r
+void sv_rremove_until_char_after(String_view* sv, char ch){\r
+  while (sv->count > 0 && *(sv->data+sv->count) != ch){\r
+    sv->count--;\r
+  }\r
+}\r
+\r
+void sv_ltrim(String_view* sv){\r
+  while (sv->count > 0 && isspace(*sv->data)){\r
+    sv->data++;\r
+    sv->count--;\r
+  }\r
+}\r
+\r
+void sv_rtrim(String_view* sv){\r
+  while (sv->count > 0 && isspace(*(sv->data+sv->count-1))){\r
+    sv->count--;\r
+  }\r
+}\r
+\r
+void sv_trim(String_view* sv){\r
+  sv_ltrim(sv);\r
+  sv_rtrim(sv);\r
+}\r
+\r
+char* sv_to_cstr(String_view sv){\r
+  char* res = (char*)calloc(1, sizeof(char)*sv.count);\r
+  memcpy(res, sv.data, sv.count);\r
+  return res;\r
+}\r
+\r
+// TODO: check for failure of conversion. returns 0/0.0 on failure, so just check if the str contains zero.\r
+int32 sv_to_int(String_view sv) {\r
+  char* str = sv_to_cstr(sv);\r
+  int32 res = atoi(str);\r
+  free(str);\r
+  return res;\r
+}\r
+\r
+uint64 sv_to_uint64(String_view sv) {\r
+  char* str = sv_to_cstr(sv);\r
+  uint64 res = (uint64)atoll(str);\r
+  free(str);\r
+  return res;\r
+}\r
+\r
+uint8 sv_to_uint8_hex(String_view sv) {\r
+  char* str = sv_to_cstr(sv);\r
+  char* end = str + sv.count;\r
+  uint8 res = (uint8)strtol(str, &end, 16);\r
+  free(str);\r
+  return res;\r
+}\r
+\r
+float32 sv_to_float(String_view sv) {\r
+  char* str = sv_to_cstr(sv);\r
+  float32 res = (float32)atof(str);\r
+  free(str);\r
+  return res;\r
+}\r
+\r
+void*  sv_to_ptr(String_view sv) {\r
+  char* str = sv_to_cstr(sv);\r
+  char* end = NULL;\r
+  void* res = (void*)strtoull(str, &end, 16);\r
+  free(str);\r
+  return res;\r
+}\r
+\r
+bool sv_contains_char(String_view sv, char ch){\r
+  for (size_t i = 0; i < sv.count; ++i){\r
+    if (sv.data[i] == ch) return true;\r
+  }\r
+  return false;\r
+}\r
+\r
+bool sv_is_hex_numbers(String_view sv) {\r
+  char hex[] = {\r
+    '0', '1', '2', '3', '4', '5', '6', '7',\r
+    '8', '9', '0', 'a', 'b', 'c', 'd', 'e', 'f',\r
+    'A', 'B', 'C', 'D', 'E', 'F'\r
+  };\r
+  bool found = false;\r
+  for (size_t i = 0; i < sv.count; ++i) {\r
+    char c = sv.data[i];\r
+    for (size_t j = 0; j < ARRAY_LEN(hex); ++j) {\r
+      if (hex[j] == c) {\r
+       found = true;\r
+      }\r
+    }\r
+  }\r
+\r
+  return found;\r
+}\r
+\r
+bool sv_equals(String_view sv1, String_view sv2) {\r
+  if (sv1.count != sv2.count) return false;\r
+  for (size_t i = 0; i < sv1.count; ++i) {\r
+    if (sv1.data[i] != sv2.data[i]) {\r
+      return false;\r
+    }\r
+  }\r
+\r
+  return true;\r
+}\r
+\r
+\r
+//\r
+// Args\r
+//\r
+\r
+cstr shift_args(int* argc, char*** argv) {\r
+  if (*argc <= 0) return NULL;\r
+\r
+  cstr res = *(argv)[0];\r
+  *argv = (*argv) + 1;\r
+  *argc = (*argc) - 1;\r
+  return res;\r
+}\r
+\r
+#endif\r