#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;
static char tempbuff[TEMP_BUFF_SIZE];
#define temp_sprintf(var, fmt, ...) \
- snprintf(tempbuff, TEMP_BUFF_SIZE, fmt, __VA_ARGS__); \
+ stbsp_snprintf(tempbuff, TEMP_BUFF_SIZE, fmt, __VA_ARGS__); \
(var) = tempbuff
//
void Arena_reset(Arena* a);
void Arena_free(Arena* a);
-#define Arena_alloc_str(a, fmt, ...) Arena_alloc(&(a), sizeof(char)*snprintf((a).ptr, (a).buff_size - ((uint8*)(a).ptr - (uint8*)(a).buff), (fmt), __VA_ARGS__)+1)
+#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)
//
#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();