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