1692 lines
		
	
	
		
			59 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
		
		
			
		
	
	
			1692 lines
		
	
	
		
			59 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| 
								 | 
							
								/* stb_ds.h - v0.5 - public domain data structures - Sean Barrett 2019
							 | 
						||
| 
								 | 
							
								  
							 | 
						||
| 
								 | 
							
								   This is a single-header-file library that provides easy-to-use
							 | 
						||
| 
								 | 
							
								   dynamic arrays and hash tables for C (also works in C++).
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								   For a gentle introduction:
							 | 
						||
| 
								 | 
							
								      http://nothings.org/stb_ds
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								   To use this library, do this in *one* C or C++ file:
							 | 
						||
| 
								 | 
							
								      #define STB_DS_IMPLEMENTATION
							 | 
						||
| 
								 | 
							
								      #include "stb_ds.h"
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								TABLE OF CONTENTS
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  Table of Contents
							 | 
						||
| 
								 | 
							
								  Compile-time options
							 | 
						||
| 
								 | 
							
								  License
							 | 
						||
| 
								 | 
							
								  Documentation
							 | 
						||
| 
								 | 
							
								  Notes
							 | 
						||
| 
								 | 
							
								  Notes - Dynamic arrays
							 | 
						||
| 
								 | 
							
								  Notes - Hash maps
							 | 
						||
| 
								 | 
							
								  Credits
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								COMPILE-TIME OPTIONS
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  #define STBDS_NO_SHORT_NAMES
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								     This flag needs to be set globally.
							 | 
						||
| 
								 | 
							
								       
							 | 
						||
| 
								 | 
							
								     By default stb_ds exposes shorter function names that are not qualified
							 | 
						||
| 
								 | 
							
								     with the "stbds_" prefix. If these names conflict with the names in your
							 | 
						||
| 
								 | 
							
								     code, define this flag.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  #define STBDS_SIPHASH_2_4
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								     This flag only needs to be set in the file containing #define STB_DS_IMPLEMENTATION.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								     By default stb_ds.h hashes using a weaker variant of SipHash and a custom hash for
							 | 
						||
| 
								 | 
							
								     4- and 8-byte keys. On 64-bit platforms, you can define the above flag to force
							 | 
						||
| 
								 | 
							
								     stb_ds.h to use specification-compliant SipHash-2-4 for all keys. Doing so makes
							 | 
						||
| 
								 | 
							
								     hash table insertion about 20% slower on 4- and 8-byte keys, 5% slower on
							 | 
						||
| 
								 | 
							
								     64-byte keys, and 10% slower on 256-byte keys on my test computer.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  #define STBDS_REALLOC(context,ptr,size) better_realloc
							 | 
						||
| 
								 | 
							
								  #define STBDS_FREE(context,ptr)         better_free
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								     These defines only need to be set in the file containing #define STB_DS_IMPLEMENTATION.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								     By default stb_ds uses stdlib realloc() and free() for memory management. You can
							 | 
						||
| 
								 | 
							
								     substitute your own functions instead by defining these symbols. You must either
							 | 
						||
| 
								 | 
							
								     define both, or neither. Note that at the moment, 'context' will always be NULL.
							 | 
						||
| 
								 | 
							
								     @TODO add an array/hash initialization function that takes a memory context pointer.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								LICENSE
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  Placed in the public domain and also MIT licensed.
							 | 
						||
| 
								 | 
							
								  See end of file for detailed license information.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								DOCUMENTATION
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  Dynamic Arrays
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    Non-function interface:
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      Declare an empty dynamic array of type T
							 | 
						||
| 
								 | 
							
								        T* foo = NULL;    
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      Access the i'th item of a dynamic array 'foo' of type T, T* foo:
							 | 
						||
| 
								 | 
							
								        foo[i]
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    Functions (actually macros)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      arrfree:
							 | 
						||
| 
								 | 
							
								        void arrfree(T*);
							 | 
						||
| 
								 | 
							
								          Frees the array.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      arrlen:
							 | 
						||
| 
								 | 
							
								        ptrdiff_t arrlen(T*);
							 | 
						||
| 
								 | 
							
								          Returns the number of elements in the array.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      arrlenu:
							 | 
						||
| 
								 | 
							
								        size_t arrlenu(T*);
							 | 
						||
| 
								 | 
							
								          Returns the number of elements in the array as an unsigned type.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      arrpop:
							 | 
						||
| 
								 | 
							
								        T arrpop(T* a)
							 | 
						||
| 
								 | 
							
								          Removes the final element of the array and returns it.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      arrput:
							 | 
						||
| 
								 | 
							
								        T arrput(T* a, T b);
							 | 
						||
| 
								 | 
							
								          Appends the item b to the end of array a. Returns b.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      arrins:
							 | 
						||
| 
								 | 
							
								        T arrins(T* a, int p, T b);
							 | 
						||
| 
								 | 
							
								          Inserts the item b into the middle of array a, into a[p],
							 | 
						||
| 
								 | 
							
								          moving the rest of the array over. Returns b.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      arrinsn:
							 | 
						||
| 
								 | 
							
								        void arrins(T* a, int p, int n);
							 | 
						||
| 
								 | 
							
								          Inserts n uninitialized items into array a starting at a[p],
							 | 
						||
| 
								 | 
							
								          moving the rest of the array over.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      arrdel:
							 | 
						||
| 
								 | 
							
								        void arrdel(T* a, int p);
							 | 
						||
| 
								 | 
							
								          Deletes the element at a[p], moving the rest of the array over.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      arrdeln:
							 | 
						||
| 
								 | 
							
								        void arrdel(T* a, int p, int n);
							 | 
						||
| 
								 | 
							
								          Deletes n elements starting at a[p], moving the rest of the array over.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      arrdelswap:
							 | 
						||
| 
								 | 
							
								        void arrdelswap(T* a, int p);
							 | 
						||
| 
								 | 
							
								          Deletes the element at a[p], replacing it with the element from
							 | 
						||
| 
								 | 
							
								          the end of the array. O(1) performance.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      arrsetlen:
							 | 
						||
| 
								 | 
							
								        void arrsetlen(T* a, int n);
							 | 
						||
| 
								 | 
							
								          Changes the length of the array to n. Allocates uninitialized
							 | 
						||
| 
								 | 
							
								          slots at the end if necessary.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      arrsetcap:
							 | 
						||
| 
								 | 
							
								        size_t arrsetcap(T* a, int n);
							 | 
						||
| 
								 | 
							
								          Sets the length of allocated storage to at least n. It will not
							 | 
						||
| 
								 | 
							
								          change the length of the array.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      arrcap:
							 | 
						||
| 
								 | 
							
								        size_t arrcap(T* a);
							 | 
						||
| 
								 | 
							
								          Returns the number of total elements the array can contain without
							 | 
						||
| 
								 | 
							
								          needing to be reallocated.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  Hash maps & String hash maps
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    Given T is a structure type: struct { TK key; TV value; }. Note that some
							 | 
						||
| 
								 | 
							
								    functions do not require TV value and can have other fields. For string
							 | 
						||
| 
								 | 
							
								    hash maps, TK must be 'char *'.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    Special interface:
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      stbds_rand_seed:
							 | 
						||
| 
								 | 
							
								        void stbds_rand_seed(size_t seed);
							 | 
						||
| 
								 | 
							
								          For security against adversarially chosen data, you should seed the
							 | 
						||
| 
								 | 
							
								          library with a strong random number. Or at least seed it with time().
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      stbds_hash_string:
							 | 
						||
| 
								 | 
							
								        size_t stbds_hash_string(char *str, size_t seed);
							 | 
						||
| 
								 | 
							
								          Returns a hash value for a string.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      stbds_hash_bytes:
							 | 
						||
| 
								 | 
							
								        size_t stbds_hash_bytes(void *p, size_t len, size_t seed);
							 | 
						||
| 
								 | 
							
								          These functions hash an arbitrary number of bytes. The function
							 | 
						||
| 
								 | 
							
								          uses a custom hash for 4- and 8-byte data, and a weakened version
							 | 
						||
| 
								 | 
							
								          of SipHash for everything else. On 64-bit platforms you can get
							 | 
						||
| 
								 | 
							
								          specification-compliant SipHash-2-4 on all data by defining
							 | 
						||
| 
								 | 
							
								          STBDS_SIPHASH_2_4, at a significant cost in speed.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    Non-function interface:
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      Declare an empty hash map of type T
							 | 
						||
| 
								 | 
							
								        T* foo = NULL;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      Access the i'th entry in a hash table T* foo:
							 | 
						||
| 
								 | 
							
								        foo[i]
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    Function interface (actually macros):
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      hmfree
							 | 
						||
| 
								 | 
							
								      shfree
							 | 
						||
| 
								 | 
							
								        void hmfree(T*);
							 | 
						||
| 
								 | 
							
								        void shfree(T*);
							 | 
						||
| 
								 | 
							
								          Frees the hashmap and sets the pointer to NULL.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      hmlen
							 | 
						||
| 
								 | 
							
								      shlen
							 | 
						||
| 
								 | 
							
								        ptrdiff_t hmlen(T*)
							 | 
						||
| 
								 | 
							
								        ptrdiff_t shlen(T*)
							 | 
						||
| 
								 | 
							
								          Returns the number of elements in the hashmap.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      hmlenu
							 | 
						||
| 
								 | 
							
								      shlenu
							 | 
						||
| 
								 | 
							
								        size_t hmlenu(T*)
							 | 
						||
| 
								 | 
							
								        size_t shlenu(T*)
							 | 
						||
| 
								 | 
							
								          Returns the number of elements in the hashmap.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      hmgeti
							 | 
						||
| 
								 | 
							
								      shgeti
							 | 
						||
| 
								 | 
							
								        ptrdiff_t hmgeti(T*, TK key)
							 | 
						||
| 
								 | 
							
								        ptrdiff_t shgeti(T*, char* key)
							 | 
						||
| 
								 | 
							
								          Returns the index in the hashmap which has the key 'key', or -1
							 | 
						||
| 
								 | 
							
								          if the key is not present.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      hmget
							 | 
						||
| 
								 | 
							
								      shget
							 | 
						||
| 
								 | 
							
								        TV hmget(T*, TK key)
							 | 
						||
| 
								 | 
							
								        TV shget(T*, char* key)
							 | 
						||
| 
								 | 
							
								          Returns the value corresponding to 'key' in the hashmap.
							 | 
						||
| 
								 | 
							
								          The structure must have a 'value' field
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      hmgets
							 | 
						||
| 
								 | 
							
								      shgets
							 | 
						||
| 
								 | 
							
								        T hmgets(T*, TK key)
							 | 
						||
| 
								 | 
							
								        T shgets(T*, char* key)
							 | 
						||
| 
								 | 
							
								          Returns the structure corresponding to 'key' in the hashmap.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      hmdefault
							 | 
						||
| 
								 | 
							
								      shdefault
							 | 
						||
| 
								 | 
							
								        TV hmdefault(T*, TV value)
							 | 
						||
| 
								 | 
							
								        TV shdefault(T*, TV value)
							 | 
						||
| 
								 | 
							
								          Sets the default value for the hashmap, the value which will be
							 | 
						||
| 
								 | 
							
								          returned by hmget/shget if the key is not present.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      hmdefaults
							 | 
						||
| 
								 | 
							
								      shdefaults
							 | 
						||
| 
								 | 
							
								        TV hmdefaults(T*, T item)
							 | 
						||
| 
								 | 
							
								        TV shdefaults(T*, T item)
							 | 
						||
| 
								 | 
							
								          Sets the default struct for the hashmap, the contents which will be
							 | 
						||
| 
								 | 
							
								          returned by hmgets/shgets if the key is not present.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      hmput
							 | 
						||
| 
								 | 
							
								      shput
							 | 
						||
| 
								 | 
							
								        TV hmput(T*, TK key, TV value)
							 | 
						||
| 
								 | 
							
								        TV shput(T*, char* key, TV value)
							 | 
						||
| 
								 | 
							
								          Inserts a <key,value> pair into the hashmap. If the key is already
							 | 
						||
| 
								 | 
							
								          present in the hashmap, updates its value.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      hmputs
							 | 
						||
| 
								 | 
							
								      shputs
							 | 
						||
| 
								 | 
							
								        T hmputs(T*, T item)
							 | 
						||
| 
								 | 
							
								        T shputs(T*, T item)
							 | 
						||
| 
								 | 
							
								          Inserts a struct with T.key and T.value into the hashmap. If the struct is already
							 | 
						||
| 
								 | 
							
								          present in the hashmap, updates it.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      hmdel
							 | 
						||
| 
								 | 
							
								      shdel
							 | 
						||
| 
								 | 
							
								        int hmdel(T*, TK key)
							 | 
						||
| 
								 | 
							
								        int shdel(T*, char* key)
							 | 
						||
| 
								 | 
							
								          If 'key' is in the hashmap, deletes its entry and returns 1.
							 | 
						||
| 
								 | 
							
								          Otherwise returns 0.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    Function interface (actually macros) for strings only:
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      sh_new_strdup
							 | 
						||
| 
								 | 
							
								        void sh_new_strdup(T*);
							 | 
						||
| 
								 | 
							
								          Overwrites the existing pointer with a newly allocated
							 | 
						||
| 
								 | 
							
								          string hashmap which will automatically allocate and free
							 | 
						||
| 
								 | 
							
								          each string key using realloc/free
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      sh_new_arena
							 | 
						||
| 
								 | 
							
								        void sh_new_arena(T*);
							 | 
						||
| 
								 | 
							
								          Overwrites the existing pointer with a newly allocated
							 | 
						||
| 
								 | 
							
								          string hashmap which will automatically allocate each string
							 | 
						||
| 
								 | 
							
								          key to a string arena. Every string key ever used by this
							 | 
						||
| 
								 | 
							
								          hash table remains in the arena until the arena is freed.
							 | 
						||
| 
								 | 
							
								          Additionally, any key which is deleted and reinserted will
							 | 
						||
| 
								 | 
							
								          be allocated multiple times in the string arena.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								NOTES
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  * These data structures are realloc'd when they grow, and the macro "functions"
							 | 
						||
| 
								 | 
							
								    write to the provided pointer. This means: (a) the pointer must be an lvalue,
							 | 
						||
| 
								 | 
							
								    and (b) the pointer to the data structure is not stable, and you must maintain
							 | 
						||
| 
								 | 
							
								    it the same as you would a realloc'd pointer. For example, if you pass a pointer
							 | 
						||
| 
								 | 
							
								    to a dynamic array to a function which updates it, the function must return
							 | 
						||
| 
								 | 
							
								    back the new pointer to the caller. This is the price of trying to do this in C.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  * You iterate over the contents of a dynamic array and a hashmap in exactly
							 | 
						||
| 
								 | 
							
								    the same way, using arrlen/hmlen/shlen:
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      for (i=0; i < arrlen(foo); ++i)
							 | 
						||
| 
								 | 
							
								         ... foo[i] ...
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  * All operations except arrins/arrdel are O(1) amortized, but individual
							 | 
						||
| 
								 | 
							
								    operations can be slow, so these data structures may not be suitable
							 | 
						||
| 
								 | 
							
								    for real time use. Dynamic arrays double in capacity as needed, so
							 | 
						||
| 
								 | 
							
								    elements are copied an average of once. Hash tables double/halve
							 | 
						||
| 
								 | 
							
								    their size as needed, with appropriate hysteresis to maintain O(1)
							 | 
						||
| 
								 | 
							
								    performance.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								NOTES - DYNAMIC ARRAY
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  * If you know how long a dynamic array is going to be in advance, you can avoid
							 | 
						||
| 
								 | 
							
								    extra memory allocations by using arrsetlen to allocate it to that length in
							 | 
						||
| 
								 | 
							
								    advance and use foo[n] while filling it out, or arrsetcap to allocate the memory
							 | 
						||
| 
								 | 
							
								    for that length and use arrput/arrpush as normal.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  * Unlike some other versions of the dynamic array, this version should
							 | 
						||
| 
								 | 
							
								    be safe to use with strict-aliasing optimizations.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								NOTES - HASH MAP
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  * For compilers other than GCC and clang (e.g. Visual Studio), for hmput/hmget/hmdel
							 | 
						||
| 
								 | 
							
								    and variants, the key must be an lvalue (so the macro can take the address of it).
							 | 
						||
| 
								 | 
							
								    Extensions are used that eliminate this requirement if you're using C99 and later
							 | 
						||
| 
								 | 
							
								    in GCC or clang, or if you're using C++ in GCC.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  * To test for presence of a key in a hashmap, just do 'hmgeti(foo,key) >= 0'.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  * The iteration order of your data in the hashmap is determined solely by the
							 | 
						||
| 
								 | 
							
								    order of insertions and deletions. In particular, if you never delete, new
							 | 
						||
| 
								 | 
							
								    keys are always added at the end of the array. This will be consistent
							 | 
						||
| 
								 | 
							
								    across all platforms and versions of the library. However, you should not
							 | 
						||
| 
								 | 
							
								    attempt to serialize the internal hash table, as the hash is not consistent
							 | 
						||
| 
								 | 
							
								    between different platforms, and may change with future versions of the library.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  * Use sh_new_arena() for string hashmaps that you never delete from. Initialize
							 | 
						||
| 
								 | 
							
								    with NULL if you're managing the memory for your strings, or your strings are
							 | 
						||
| 
								 | 
							
								    never freed (at least until the hashmap is freed). Otherwise, use sh_new_strdup().
							 | 
						||
| 
								 | 
							
								    @TODO: make an arena variant that garbage collects the strings with a trivial
							 | 
						||
| 
								 | 
							
								    copy collector into a new arena whenever the table shrinks / rebuilds. Since
							 | 
						||
| 
								 | 
							
								    current arena recommendation is to only use arena if it never deletes, then
							 | 
						||
| 
								 | 
							
								    this can just replace current arena implementation.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  * If adversarial input is a serious concern and you're on a 64-bit platform,
							 | 
						||
| 
								 | 
							
								    enable STBDS_SIPHASH_2_4 (see the 'Compile-time options' section), and pass
							 | 
						||
| 
								 | 
							
								    a strong random number to stbds_rand_seed.
							 | 
						||
| 
								 | 
							
								     
							 | 
						||
| 
								 | 
							
								  * The default value for the hash table is stored in foo[-1], so if you
							 | 
						||
| 
								 | 
							
								    use code like 'hmget(T,k)->value = 5' you can overwrite the value
							 | 
						||
| 
								 | 
							
								    stored by hmdefault if 'k' is not present.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								CREDITS
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  Sean Barrett -- library, idea for dynamic array API/implementation
							 | 
						||
| 
								 | 
							
								  Per Vognsen  -- idea for hash table API/implementation
							 | 
						||
| 
								 | 
							
								  Rafael Sachetto -- arrpop()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  Bugfixes:
							 | 
						||
| 
								 | 
							
								    Vinh Truong
							 | 
						||
| 
								 | 
							
								    Andy Durdin
							 | 
						||
| 
								 | 
							
								*/
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#ifdef STBDS_UNIT_TESTS
							 | 
						||
| 
								 | 
							
								#define _CRT_SECURE_NO_WARNINGS
							 | 
						||
| 
								 | 
							
								#endif
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#ifndef INCLUDE_STB_DS_H
							 | 
						||
| 
								 | 
							
								#define INCLUDE_STB_DS_H
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#include <stddef.h>
							 | 
						||
| 
								 | 
							
								#include <string.h>
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#ifndef STBDS_NO_SHORT_NAMES
							 | 
						||
| 
								 | 
							
								#define arrlen      stbds_arrlen
							 | 
						||
| 
								 | 
							
								#define arrlenu     stbds_arrlenu
							 | 
						||
| 
								 | 
							
								#define arrput      stbds_arrput
							 | 
						||
| 
								 | 
							
								#define arrpush     stbds_arrput
							 | 
						||
| 
								 | 
							
								#define arrpop      stbds_arrpop
							 | 
						||
| 
								 | 
							
								#define arrfree     stbds_arrfree
							 | 
						||
| 
								 | 
							
								#define arraddn     stbds_arraddn
							 | 
						||
| 
								 | 
							
								#define arrsetlen   stbds_arrsetlen
							 | 
						||
| 
								 | 
							
								#define arrlast     stbds_arrlast
							 | 
						||
| 
								 | 
							
								#define arrins      stbds_arrins
							 | 
						||
| 
								 | 
							
								#define arrinsn     stbds_arrinsn
							 | 
						||
| 
								 | 
							
								#define arrdel      stbds_arrdel
							 | 
						||
| 
								 | 
							
								#define arrdeln     stbds_arrdeln
							 | 
						||
| 
								 | 
							
								#define arrdelswap  stbds_arrdelswap
							 | 
						||
| 
								 | 
							
								#define arrcap      stbds_arrcap
							 | 
						||
| 
								 | 
							
								#define arrsetcap   stbds_arrsetcap
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#define hmput       stbds_hmput
							 | 
						||
| 
								 | 
							
								#define hmputs      stbds_hmputs
							 | 
						||
| 
								 | 
							
								#define hmget       stbds_hmget
							 | 
						||
| 
								 | 
							
								#define hmgets      stbds_hmgets
							 | 
						||
| 
								 | 
							
								#define hmgetp      stbds_hmgetp
							 | 
						||
| 
								 | 
							
								#define hmgeti      stbds_hmgeti
							 | 
						||
| 
								 | 
							
								#define hmdel       stbds_hmdel
							 | 
						||
| 
								 | 
							
								#define hmlen       stbds_hmlen
							 | 
						||
| 
								 | 
							
								#define hmlenu      stbds_hmlenu
							 | 
						||
| 
								 | 
							
								#define hmfree      stbds_hmfree
							 | 
						||
| 
								 | 
							
								#define hmdefault   stbds_hmdefault
							 | 
						||
| 
								 | 
							
								#define hmdefaults  stbds_hmdefaults
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#define shput       stbds_shput
							 | 
						||
| 
								 | 
							
								#define shputs      stbds_shputs
							 | 
						||
| 
								 | 
							
								#define shget       stbds_shget
							 | 
						||
| 
								 | 
							
								#define shgets      stbds_shgets
							 | 
						||
| 
								 | 
							
								#define shgetp      stbds_shgetp
							 | 
						||
| 
								 | 
							
								#define shgeti      stbds_shgeti
							 | 
						||
| 
								 | 
							
								#define shdel       stbds_shdel
							 | 
						||
| 
								 | 
							
								#define shlen       stbds_shlen
							 | 
						||
| 
								 | 
							
								#define shlenu      stbds_shlenu
							 | 
						||
| 
								 | 
							
								#define shfree      stbds_shfree
							 | 
						||
| 
								 | 
							
								#define shdefault   stbds_shdefault
							 | 
						||
| 
								 | 
							
								#define shdefaults  stbds_shdefaults
							 | 
						||
| 
								 | 
							
								#define sh_new_arena  stbds_sh_new_arena
							 | 
						||
| 
								 | 
							
								#define sh_new_strdup stbds_sh_new_strdup
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#define stralloc    stbds_stralloc
							 | 
						||
| 
								 | 
							
								#define strreset    stbds_strreset
							 | 
						||
| 
								 | 
							
								#endif      
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#if defined(STBDS_REALLOC) && !defined(STBDS_FREE) || !defined(STBDS_REALLOC) && defined(STBDS_FREE)
							 | 
						||
| 
								 | 
							
								#error "You must define both STBDS_REALLOC and STBDS_FREE, or neither."
							 | 
						||
| 
								 | 
							
								#endif
							 | 
						||
| 
								 | 
							
								#if !defined(STBDS_REALLOC) && !defined(STBDS_FREE)
							 | 
						||
| 
								 | 
							
								#include <stdlib.h>
							 | 
						||
| 
								 | 
							
								#define STBDS_REALLOC(c,p,s) realloc(p,s)
							 | 
						||
| 
								 | 
							
								#define STBDS_FREE(c,p)      free(p)
							 | 
						||
| 
								 | 
							
								#endif
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#ifdef __cplusplus
							 | 
						||
| 
								 | 
							
								extern "C" {
							 | 
						||
| 
								 | 
							
								#endif
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// for security against attackers, seed the library with a random number, at least time() but stronger is better
							 | 
						||
| 
								 | 
							
								extern void stbds_rand_seed(size_t seed);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// these are the hash functions used internally if you want to test them or use them for other purposes
							 | 
						||
| 
								 | 
							
								extern size_t stbds_hash_bytes(void *p, size_t len, size_t seed);
							 | 
						||
| 
								 | 
							
								extern size_t stbds_hash_string(char *str, size_t seed);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// this is a simple string arena allocator, initialize with e.g. 'stbds_string_arena my_arena={0}'.
							 | 
						||
| 
								 | 
							
								typedef struct stbds_string_arena stbds_string_arena;
							 | 
						||
| 
								 | 
							
								extern char * stbds_stralloc(stbds_string_arena *a, char *str);
							 | 
						||
| 
								 | 
							
								extern void   stbds_strreset(stbds_string_arena *a);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// have to #define STBDS_UNIT_TESTS to call this
							 | 
						||
| 
								 | 
							
								extern void stbds_unit_tests(void);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								///////////////
							 | 
						||
| 
								 | 
							
								//
							 | 
						||
| 
								 | 
							
								// Everything below here is implementation details
							 | 
						||
| 
								 | 
							
								//
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								extern void * stbds_arrgrowf(void *a, size_t elemsize, size_t addlen, size_t min_cap);
							 | 
						||
| 
								 | 
							
								extern void   stbds_hmfree_func(void *p, size_t elemsize, size_t keyoff);
							 | 
						||
| 
								 | 
							
								extern void * stbds_hmget_key(void *a, size_t elemsize, void *key, size_t keysize, int mode);
							 | 
						||
| 
								 | 
							
								extern void * stbds_hmput_default(void *a, size_t elemsize);
							 | 
						||
| 
								 | 
							
								extern void * stbds_hmput_key(void *a, size_t elemsize, void *key, size_t keysize, int mode);
							 | 
						||
| 
								 | 
							
								extern void * stbds_hmdel_key(void *a, size_t elemsize, void *key, size_t keysize, size_t keyoffset, int mode);
							 | 
						||
| 
								 | 
							
								extern void * stbds_shmode_func(size_t elemsize, int mode);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#ifdef __cplusplus
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								#endif
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#if defined(__GNUC__) || defined(__clang__)
							 | 
						||
| 
								 | 
							
								#define STBDS_HAS_TYPEOF
							 | 
						||
| 
								 | 
							
								#ifdef __cplusplus
							 | 
						||
| 
								 | 
							
								//#define STBDS_HAS_LITERAL_ARRAY  // this is currently broken for clang
							 | 
						||
| 
								 | 
							
								#endif
							 | 
						||
| 
								 | 
							
								#endif
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#if !defined(__cplusplus)
							 | 
						||
| 
								 | 
							
								#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
							 | 
						||
| 
								 | 
							
								#define STBDS_HAS_LITERAL_ARRAY
							 | 
						||
| 
								 | 
							
								#endif
							 | 
						||
| 
								 | 
							
								#endif
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// this macro takes the address of the argument, but on gcc/clang can accept rvalues
							 | 
						||
| 
								 | 
							
								#if defined(STBDS_HAS_LITERAL_ARRAY) && defined(STBDS_HAS_TYPEOF)
							 | 
						||
| 
								 | 
							
								  #if __clang__
							 | 
						||
| 
								 | 
							
								  #define STBDS_ADDRESSOF(typevar, value)     ((__typeof__(typevar)[1]){value}) // literal array decays to pointer to value
							 | 
						||
| 
								 | 
							
								  #else
							 | 
						||
| 
								 | 
							
								  #define STBDS_ADDRESSOF(typevar, value)     ((typeof(typevar)[1]){value}) // literal array decays to pointer to value
							 | 
						||
| 
								 | 
							
								  #endif
							 | 
						||
| 
								 | 
							
								#else
							 | 
						||
| 
								 | 
							
								#define STBDS_ADDRESSOF(typevar, value)     &(value)
							 | 
						||
| 
								 | 
							
								#endif
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#define STBDS_OFFSETOF(var,field)           ((char *) &(var)->field - (char *) (var))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#define stbds_header(t)  ((stbds_array_header *) (t) - 1)
							 | 
						||
| 
								 | 
							
								#define stbds_temp(t)    stbds_header(t)->temp
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#define stbds_arrsetcap(a,n) (stbds_arrgrow(a,0,n))
							 | 
						||
| 
								 | 
							
								#define stbds_arrsetlen(a,n) ((stbds_arrcap(a) < n ? stbds_arrsetcap(a,n),0 : 0), (a) ? stbds_header(a)->length = (n) : 0)
							 | 
						||
| 
								 | 
							
								#define stbds_arrcap(a)       ((a) ? stbds_header(a)->capacity : 0)
							 | 
						||
| 
								 | 
							
								#define stbds_arrlen(a)       ((a) ? (ptrdiff_t) stbds_header(a)->length : 0)
							 | 
						||
| 
								 | 
							
								#define stbds_arrlenu(a)      ((a) ?             stbds_header(a)->length : 0)
							 | 
						||
| 
								 | 
							
								#define stbds_arrput(a,v)     (stbds_arrmaybegrow(a,1), (a)[stbds_header(a)->length++] = (v))
							 | 
						||
| 
								 | 
							
								#define stbds_arrpush         stbds_arrput  // synonym
							 | 
						||
| 
								 | 
							
								#define stbds_arrpop(a)       (stbds_header(a)->length--, (a)[stbds_header(a)->length])
							 | 
						||
| 
								 | 
							
								#define stbds_arraddn(a,n)    (stbds_arrmaybegrow(a,n), stbds_header(a)->length += (n))
							 | 
						||
| 
								 | 
							
								#define stbds_arrlast(a)      ((a)[stbds_header(a)->length-1])
							 | 
						||
| 
								 | 
							
								#define stbds_arrfree(a)      ((void) ((a) ? STBDS_FREE(NULL,stbds_header(a)) : (void)0), (a)=NULL)
							 | 
						||
| 
								 | 
							
								#define stbds_arrdel(a,i)     stbds_arrdeln(a,i,1)
							 | 
						||
| 
								 | 
							
								#define stbds_arrdeln(a,i,n)  (memmove(&(a)[i], &(a)[(i)+(n)], sizeof *(a) * (stbds_header(a)->length-(n)-(i))), stbds_header(a)->length -= (n))
							 | 
						||
| 
								 | 
							
								#define stbds_arrdelswap(a,i) ((a)[i] = stbds_arrlast(a), stbds_header(a)->length -= 1)
							 | 
						||
| 
								 | 
							
								#define stbds_arrinsn(a,i,n)  (stbds_arraddn((a),(n)), memmove(&(a)[(i)+(n)], &(a)[i], sizeof *(a) * (stbds_header(a)->length-(n)-(i))))
							 | 
						||
| 
								 | 
							
								#define stbds_arrins(a,i,v)   (stbds_arrinsn((a),(i),1), (a)[i]=(v))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#define stbds_arrmaybegrow(a,n)  ((!(a) || stbds_header(a)->length + (n) > stbds_header(a)->capacity) \
							 | 
						||
| 
								 | 
							
								                                  ? (stbds_arrgrow(a,n,0),0) : 0)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#define stbds_arrgrow(a,b,c)   ((a) = stbds_arrgrowf_wrapper((a), sizeof *(a), (b), (c)))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#define stbds_hmput(t, k, v) \
							 | 
						||
| 
								 | 
							
								    ((t) = stbds_hmput_key_wrapper((t), sizeof *(t), (void*) STBDS_ADDRESSOF((t)->key, (k)), sizeof (t)->key, 0),   \
							 | 
						||
| 
								 | 
							
								     (t)[stbds_temp((t)-1)].key = (k), \
							 | 
						||
| 
								 | 
							
								     (t)[stbds_temp((t)-1)].value = (v))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#define stbds_hmputs(t, s) \
							 | 
						||
| 
								 | 
							
								    ((t) = stbds_hmput_key_wrapper((t), sizeof *(t), &(s).key, sizeof (s).key, STBDS_HM_BINARY), \
							 | 
						||
| 
								 | 
							
								     (t)[stbds_temp((t)-1)] = (s))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#define stbds_hmgeti(t,k) \
							 | 
						||
| 
								 | 
							
								    ((t) = stbds_hmget_key_wrapper((t), sizeof *(t), (void*) STBDS_ADDRESSOF((t)->key, (k)), sizeof (t)->key, STBDS_HM_BINARY), \
							 | 
						||
| 
								 | 
							
								      stbds_temp((t)-1))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#define stbds_hmgetp(t, k) \
							 | 
						||
| 
								 | 
							
								    ((void) stbds_hmgeti(t,k), &(t)[stbds_temp((t)-1)])
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#define stbds_hmdel(t,k) \
							 | 
						||
| 
								 | 
							
								    (((t) = stbds_hmdel_key_wrapper((t),sizeof *(t), (void*) STBDS_ADDRESSOF((t)->key, (k)), sizeof (t)->key, STBDS_OFFSETOF((t),key), STBDS_HM_BINARY)),(t)?stbds_temp((t)-1):0)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#define stbds_hmdefault(t, v) \
							 | 
						||
| 
								 | 
							
								    ((t) = stbds_hmput_default_wrapper((t), sizeof *(t)), (t)[-1].value = (v))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#define stbds_hmdefaults(t, s) \
							 | 
						||
| 
								 | 
							
								    ((t) = stbds_hmput_default_wrapper((t), sizeof *(t)), (t)[-1] = (s))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#define stbds_hmfree(p)        \
							 | 
						||
| 
								 | 
							
								    ((void) ((p) != NULL ? stbds_hmfree_func((p)-1,sizeof*(p),STBDS_OFFSETOF((p),key)),0 : 0),(p)=NULL)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#define stbds_hmgets(t, k) (*stbds_hmgetp(t,k))
							 | 
						||
| 
								 | 
							
								#define stbds_hmget(t, k)  (stbds_hmgetp(t,k)->value)
							 | 
						||
| 
								 | 
							
								#define stbds_hmlen(t)     ((t) ? (ptrdiff_t) stbds_header((t)-1)->length-1 : 0)
							 | 
						||
| 
								 | 
							
								#define stbds_hmlenu(t)    ((t) ?             stbds_header((t)-1)->length-1 : 0)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#define stbds_shput(t, k, v) \
							 | 
						||
| 
								 | 
							
								    ((t) = stbds_hmput_key_wrapper((t), sizeof *(t), (void*) (k), sizeof (t)->key, STBDS_HM_STRING),   \
							 | 
						||
| 
								 | 
							
								     (t)[stbds_temp(t-1)].value = (v))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#define stbds_shputs(t, s) \
							 | 
						||
| 
								 | 
							
								    ((t) = stbds_hmput_key_wrapper((t), sizeof *(t), (void*) (s).key, sizeof (s).key, STBDS_HM_STRING), \
							 | 
						||
| 
								 | 
							
								     (t)[stbds_temp(t-1)] = (s))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#define stbds_shgeti(t,k) \
							 | 
						||
| 
								 | 
							
								     ((t) = stbds_hmget_key_wrapper((t), sizeof *(t), (void*) (k), sizeof (t)->key, STBDS_HM_STRING), \
							 | 
						||
| 
								 | 
							
								      stbds_temp(t))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#define stbds_shgetp(t, k) \
							 | 
						||
| 
								 | 
							
								    ((void) stbds_shgeti(t,k), &(t)[stbds_temp(t-1)])
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#define stbds_shdel(t,k) \
							 | 
						||
| 
								 | 
							
								    (((t) = stbds_hmdel_key_wrapper((t),sizeof *(t), (void*) (k), sizeof (t)->key, STBDS_OFFSETOF((t),key), STBDS_HM_STRING)),(t)?stbds_temp((t)-1):0)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#define stbds_sh_new_arena(t)  \
							 | 
						||
| 
								 | 
							
								    ((t) = stbds_shmode_func_wrapper(t, sizeof *(t), STBDS_SH_ARENA))
							 | 
						||
| 
								 | 
							
								#define stbds_sh_new_strdup(t) \
							 | 
						||
| 
								 | 
							
								    ((t) = stbds_shmode_func_wrapper(t, sizeof *(t), STBDS_SH_STRDUP))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#define stbds_shdefault(t, v) stbds_hmdefault(t,v)
							 | 
						||
| 
								 | 
							
								#define stbds_shdefaults(t, s) stbds_hmdefaults(t,s)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#define stbds_shfree       stbds_hmfree
							 | 
						||
| 
								 | 
							
								#define stbds_shlenu       stbds_hmlenu
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#define stbds_shgets(t, k) (*stbds_shgetp(t,k))
							 | 
						||
| 
								 | 
							
								#define stbds_shget(t, k)  (stbds_shgetp(t,k)->value)
							 | 
						||
| 
								 | 
							
								#define stbds_shlen        stbds_hmlen
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								typedef struct
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								  size_t      length;
							 | 
						||
| 
								 | 
							
								  size_t      capacity;
							 | 
						||
| 
								 | 
							
								  void      * hash_table;
							 | 
						||
| 
								 | 
							
								  ptrdiff_t   temp;
							 | 
						||
| 
								 | 
							
								} stbds_array_header;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								typedef struct stbds_string_block
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								  struct stbds_string_block *next;
							 | 
						||
| 
								 | 
							
								  char storage[8];
							 | 
						||
| 
								 | 
							
								} stbds_string_block;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								struct stbds_string_arena
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								  stbds_string_block *storage;
							 | 
						||
| 
								 | 
							
								  size_t remaining;
							 | 
						||
| 
								 | 
							
								  unsigned char block;
							 | 
						||
| 
								 | 
							
								  unsigned char mode;  // this isn't used by the string arena itself
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								enum
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								   STBDS_HM_BINARY,
							 | 
						||
| 
								 | 
							
								   STBDS_HM_STRING,
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								enum
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								   STBDS_SH_NONE,
							 | 
						||
| 
								 | 
							
								   STBDS_SH_STRDUP,
							 | 
						||
| 
								 | 
							
								   STBDS_SH_ARENA
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#ifdef __cplusplus
							 | 
						||
| 
								 | 
							
								// in C we use implicit assignment from these void*-returning functions to T*.
							 | 
						||
| 
								 | 
							
								// in C++ these templates make the same code work
							 | 
						||
| 
								 | 
							
								template<class T> static T * stbds_arrgrowf_wrapper(T *a, size_t elemsize, size_t addlen, size_t min_cap) {
							 | 
						||
| 
								 | 
							
								  return (T*)stbds_arrgrowf((void *)a, elemsize, addlen, min_cap);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								template<class T> static T * stbds_hmget_key_wrapper(T *a, size_t elemsize, void *key, size_t keysize, int mode) {
							 | 
						||
| 
								 | 
							
								  return (T*)stbds_hmget_key((void*)a, elemsize, key, keysize, mode);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								template<class T> static T * stbds_hmput_default_wrapper(T *a, size_t elemsize) {
							 | 
						||
| 
								 | 
							
								  return (T*)stbds_hmput_default((void *)a, elemsize);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								template<class T> static T * stbds_hmput_key_wrapper(T *a, size_t elemsize, void *key, size_t keysize, int mode) {
							 | 
						||
| 
								 | 
							
								  return (T*)stbds_hmput_key((void*)a, elemsize, key, keysize, mode);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								template<class T> static T * stbds_hmdel_key_wrapper(T *a, size_t elemsize, void *key, size_t keysize, size_t keyoffset, int mode){
							 | 
						||
| 
								 | 
							
								  return (T*)stbds_hmdel_key((void*)a, elemsize, key, keysize, keyoffset, mode);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								template<class T> static T * stbds_shmode_func_wrapper(T *, size_t elemsize, int mode) {
							 | 
						||
| 
								 | 
							
								  return (T*)stbds_shmode_func(elemsize, mode);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								#else
							 | 
						||
| 
								 | 
							
								#define stbds_arrgrowf_wrapper            stbds_arrgrowf
							 | 
						||
| 
								 | 
							
								#define stbds_hmget_key_wrapper           stbds_hmget_key
							 | 
						||
| 
								 | 
							
								#define stbds_hmput_default_wrapper       stbds_hmput_default
							 | 
						||
| 
								 | 
							
								#define stbds_hmput_key_wrapper           stbds_hmput_key
							 | 
						||
| 
								 | 
							
								#define stbds_hmdel_key_wrapper           stbds_hmdel_key
							 | 
						||
| 
								 | 
							
								#define stbds_shmode_func_wrapper(t,e,m)  stbds_shmode_func(e,m)
							 | 
						||
| 
								 | 
							
								#endif
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#endif // INCLUDE_STB_DS_H
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								//////////////////////////////////////////////////////////////////////////////
							 | 
						||
| 
								 | 
							
								//
							 | 
						||
| 
								 | 
							
								//   IMPLEMENTATION
							 | 
						||
| 
								 | 
							
								//
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#ifdef STB_DS_IMPLEMENTATION
							 | 
						||
| 
								 | 
							
								#include <assert.h>
							 | 
						||
| 
								 | 
							
								#include <string.h>
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#ifndef STBDS_ASSERT
							 | 
						||
| 
								 | 
							
								#define STBDS_ASSERT_WAS_UNDEFINED
							 | 
						||
| 
								 | 
							
								#define STBDS_ASSERT(x)   ((void) 0)
							 | 
						||
| 
								 | 
							
								#endif
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#ifdef STBDS_STATISTICS
							 | 
						||
| 
								 | 
							
								#define STBDS_STATS(x)   x
							 | 
						||
| 
								 | 
							
								size_t stbds_array_grow;
							 | 
						||
| 
								 | 
							
								size_t stbds_hash_grow;
							 | 
						||
| 
								 | 
							
								size_t stbds_hash_shrink;
							 | 
						||
| 
								 | 
							
								size_t stbds_hash_rebuild;
							 | 
						||
| 
								 | 
							
								size_t stbds_hash_probes;
							 | 
						||
| 
								 | 
							
								size_t stbds_hash_alloc;
							 | 
						||
| 
								 | 
							
								size_t stbds_rehash_probes;
							 | 
						||
| 
								 | 
							
								size_t stbds_rehash_items;
							 | 
						||
| 
								 | 
							
								#else
							 | 
						||
| 
								 | 
							
								#define STBDS_STATS(x)
							 | 
						||
| 
								 | 
							
								#endif
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								//
							 | 
						||
| 
								 | 
							
								// stbds_arr implementation
							 | 
						||
| 
								 | 
							
								//
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								void *stbds_arrgrowf(void *a, size_t elemsize, size_t addlen, size_t min_cap)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								  void *b;
							 | 
						||
| 
								 | 
							
								  size_t min_len = stbds_arrlen(a) + addlen;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  // compute the minimum capacity needed
							 | 
						||
| 
								 | 
							
								  if (min_len > min_cap)
							 | 
						||
| 
								 | 
							
								    min_cap = min_len;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (min_cap <= stbds_arrcap(a))
							 | 
						||
| 
								 | 
							
								    return a;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  // increase needed capacity to guarantee O(1) amortized
							 | 
						||
| 
								 | 
							
								  if (min_cap < 2 * stbds_arrcap(a))
							 | 
						||
| 
								 | 
							
								    min_cap = 2 * stbds_arrcap(a);
							 | 
						||
| 
								 | 
							
								  else if (min_cap < 4)
							 | 
						||
| 
								 | 
							
								    min_cap = 4;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  b = STBDS_REALLOC(NULL, (a) ? stbds_header(a) : 0, elemsize * min_cap + sizeof(stbds_array_header));
							 | 
						||
| 
								 | 
							
								  b = (char *) b + sizeof(stbds_array_header);
							 | 
						||
| 
								 | 
							
								  if (a == NULL) {
							 | 
						||
| 
								 | 
							
								    stbds_header(b)->length = 0;
							 | 
						||
| 
								 | 
							
								    stbds_header(b)->hash_table = 0;
							 | 
						||
| 
								 | 
							
								  } else {
							 | 
						||
| 
								 | 
							
								    STBDS_STATS(++stbds_array_grow);
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  stbds_header(b)->capacity = min_cap;
							 | 
						||
| 
								 | 
							
								  return b;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								//
							 | 
						||
| 
								 | 
							
								// stbds_hm hash table implementation
							 | 
						||
| 
								 | 
							
								//
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#ifdef STBDS_INTERNAL_SMALL_BUCKET
							 | 
						||
| 
								 | 
							
								#define STBDS_BUCKET_LENGTH      4
							 | 
						||
| 
								 | 
							
								#else
							 | 
						||
| 
								 | 
							
								#define STBDS_BUCKET_LENGTH      8
							 | 
						||
| 
								 | 
							
								#endif
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#define STBDS_BUCKET_SHIFT      (STBDS_BUCKET_LENGTH == 8 ? 3 : 2)
							 | 
						||
| 
								 | 
							
								#define STBDS_BUCKET_MASK       (STBDS_BUCKET_LENGTH-1)
							 | 
						||
| 
								 | 
							
								#define STBDS_CACHE_LINE_SIZE   64
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#define STBDS_ALIGN_FWD(n,a)   (((n) + (a) - 1) & ~((a)-1))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								typedef struct
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								   size_t    hash [STBDS_BUCKET_LENGTH];
							 | 
						||
| 
								 | 
							
								   ptrdiff_t index[STBDS_BUCKET_LENGTH];
							 | 
						||
| 
								 | 
							
								} stbds_hash_bucket; // in 32-bit, this is one 64-byte cache line; in 64-bit, each array is one 64-byte cache line
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								typedef struct
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								  size_t slot_count;
							 | 
						||
| 
								 | 
							
								  size_t used_count;
							 | 
						||
| 
								 | 
							
								  size_t used_count_threshold;
							 | 
						||
| 
								 | 
							
								  size_t used_count_shrink_threshold;
							 | 
						||
| 
								 | 
							
								  size_t tombstone_count;
							 | 
						||
| 
								 | 
							
								  size_t tombstone_count_threshold;
							 | 
						||
| 
								 | 
							
								  size_t seed;
							 | 
						||
| 
								 | 
							
								  size_t slot_count_log2;
							 | 
						||
| 
								 | 
							
								  stbds_string_arena string;
							 | 
						||
| 
								 | 
							
								  stbds_hash_bucket *storage; // not a separate allocation, just 64-byte aligned storage after this struct
							 | 
						||
| 
								 | 
							
								} stbds_hash_index;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#define STBDS_INDEX_EMPTY    -1
							 | 
						||
| 
								 | 
							
								#define STBDS_INDEX_DELETED  -2
							 | 
						||
| 
								 | 
							
								#define STBDS_INDEX_IN_USE(x)  ((x) >= 0)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#define STBDS_HASH_EMPTY      0
							 | 
						||
| 
								 | 
							
								#define STBDS_HASH_DELETED    1
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static size_t stbds_hash_seed=0x31415926;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								void stbds_rand_seed(size_t seed)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								  stbds_hash_seed = seed;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#define stbds_load_32_or_64(var, temp, v32, v64_hi, v64_lo)                                          \
							 | 
						||
| 
								 | 
							
								  temp = v64_lo ^ v32, temp <<= 16, temp <<= 16, temp >>= 16, temp >>= 16, /* discard if 32-bit */   \
							 | 
						||
| 
								 | 
							
								  var = v64_hi, var <<= 16, var <<= 16,                                    /* discard if 32-bit */   \
							 | 
						||
| 
								 | 
							
								  var ^= temp ^ v32
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#define STBDS_SIZE_T_BITS           ((sizeof (size_t)) * 8)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static size_t stbds_probe_position(size_t hash, size_t slot_count, size_t slot_log2)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								  size_t pos;
							 | 
						||
| 
								 | 
							
								  pos = hash & (slot_count-1);
							 | 
						||
| 
								 | 
							
								  #ifdef STBDS_INTERNAL_BUCKET_START
							 | 
						||
| 
								 | 
							
								  pos &= ~STBDS_BUCKET_MASK;
							 | 
						||
| 
								 | 
							
								  #endif
							 | 
						||
| 
								 | 
							
								  return pos;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static size_t stbds_log2(size_t slot_count)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								  size_t n=0;
							 | 
						||
| 
								 | 
							
								  while (slot_count > 1) {
							 | 
						||
| 
								 | 
							
								    slot_count >>= 1;
							 | 
						||
| 
								 | 
							
								    ++n;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  return n;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static stbds_hash_index *stbds_make_hash_index(size_t slot_count, stbds_hash_index *ot)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								  stbds_hash_index *t;
							 | 
						||
| 
								 | 
							
								  t = (stbds_hash_index *) STBDS_REALLOC(NULL,0,(slot_count >> STBDS_BUCKET_SHIFT) * sizeof(stbds_hash_bucket) + sizeof(stbds_hash_index) + STBDS_CACHE_LINE_SIZE-1);
							 | 
						||
| 
								 | 
							
								  t->storage = (stbds_hash_bucket *) STBDS_ALIGN_FWD((size_t) (t+1), STBDS_CACHE_LINE_SIZE);
							 | 
						||
| 
								 | 
							
								  t->slot_count = slot_count;
							 | 
						||
| 
								 | 
							
								  t->slot_count_log2 = stbds_log2(slot_count);
							 | 
						||
| 
								 | 
							
								  t->tombstone_count = 0;
							 | 
						||
| 
								 | 
							
								  t->used_count = 0;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  #if 0 // A1
							 | 
						||
| 
								 | 
							
								  t->used_count_threshold        = slot_count*12/16; // if 12/16th of table is occupied, grow
							 | 
						||
| 
								 | 
							
								  t->tombstone_count_threshold   = slot_count* 2/16; // if tombstones are 2/16th of table, rebuild
							 | 
						||
| 
								 | 
							
								  t->used_count_shrink_threshold = slot_count* 4/16; // if table is only 4/16th full, shrink
							 | 
						||
| 
								 | 
							
								  #elif 1 // A2
							 | 
						||
| 
								 | 
							
								  //t->used_count_threshold        = slot_count*12/16; // if 12/16th of table is occupied, grow
							 | 
						||
| 
								 | 
							
								  //t->tombstone_count_threshold   = slot_count* 3/16; // if tombstones are 3/16th of table, rebuild
							 | 
						||
| 
								 | 
							
								  //t->used_count_shrink_threshold = slot_count* 4/16; // if table is only 4/16th full, shrink
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  // compute without overflowing
							 | 
						||
| 
								 | 
							
								  t->used_count_threshold        = slot_count - (slot_count>>2);
							 | 
						||
| 
								 | 
							
								  t->tombstone_count_threshold   = (slot_count>>3) + (slot_count>>4);
							 | 
						||
| 
								 | 
							
								  t->used_count_shrink_threshold = slot_count >> 2;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  #elif 0 // B1
							 | 
						||
| 
								 | 
							
								  t->used_count_threshold        = slot_count*13/16; // if 13/16th of table is occupied, grow
							 | 
						||
| 
								 | 
							
								  t->tombstone_count_threshold   = slot_count* 2/16; // if tombstones are 2/16th of table, rebuild
							 | 
						||
| 
								 | 
							
								  t->used_count_shrink_threshold = slot_count* 5/16; // if table is only 5/16th full, shrink
							 | 
						||
| 
								 | 
							
								  #else // C1
							 | 
						||
| 
								 | 
							
								  t->used_count_threshold        = slot_count*14/16; // if 14/16th of table is occupied, grow
							 | 
						||
| 
								 | 
							
								  t->tombstone_count_threshold   = slot_count* 2/16; // if tombstones are 2/16th of table, rebuild
							 | 
						||
| 
								 | 
							
								  t->used_count_shrink_threshold = slot_count* 6/16; // if table is only 6/16th full, shrink
							 | 
						||
| 
								 | 
							
								  #endif
							 | 
						||
| 
								 | 
							
								  // Following statistics were measured on a Core i7-6700 @ 4.00Ghz, compiled with clang 7.0.1 -O2
							 | 
						||
| 
								 | 
							
								    // Note that the larger tables have high variance as they were run fewer times
							 | 
						||
| 
								 | 
							
								  //     A1            A2          B1           C1
							 | 
						||
| 
								 | 
							
								  //    0.10ms :     0.10ms :     0.10ms :     0.11ms :      2,000 inserts creating 2K table   
							 | 
						||
| 
								 | 
							
								  //    0.96ms :     0.95ms :     0.97ms :     1.04ms :     20,000 inserts creating 20K table  
							 | 
						||
| 
								 | 
							
								  //   14.48ms :    14.46ms :    10.63ms :    11.00ms :    200,000 inserts creating 200K table 
							 | 
						||
| 
								 | 
							
								  //  195.74ms :   196.35ms :   203.69ms :   214.92ms :  2,000,000 inserts creating 2M table   
							 | 
						||
| 
								 | 
							
								  // 2193.88ms :  2209.22ms :  2285.54ms :  2437.17ms : 20,000,000 inserts creating 20M table  
							 | 
						||
| 
								 | 
							
								  //   65.27ms :    53.77ms :    65.33ms :    65.47ms : 500,000 inserts & deletes in 2K table  
							 | 
						||
| 
								 | 
							
								  //   72.78ms :    62.45ms :    71.95ms :    72.85ms : 500,000 inserts & deletes in 20K table 
							 | 
						||
| 
								 | 
							
								  //   89.47ms :    77.72ms :    96.49ms :    96.75ms : 500,000 inserts & deletes in 200K table
							 | 
						||
| 
								 | 
							
								  //   97.58ms :    98.14ms :    97.18ms :    97.53ms : 500,000 inserts & deletes in 2M table  
							 | 
						||
| 
								 | 
							
								  //  118.61ms :   119.62ms :   120.16ms :   118.86ms : 500,000 inserts & deletes in 20M table 
							 | 
						||
| 
								 | 
							
								  //  192.11ms :   194.39ms :   196.38ms :   195.73ms : 500,000 inserts & deletes in 200M table
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (slot_count <= STBDS_BUCKET_LENGTH)
							 | 
						||
| 
								 | 
							
								    t->used_count_shrink_threshold = 0;
							 | 
						||
| 
								 | 
							
								  // to avoid infinite loop, we need to guarantee that at least one slot is empty and will terminate probes
							 | 
						||
| 
								 | 
							
								  STBDS_ASSERT(t->used_count_threshold + t->tombstone_count_threshold < t->slot_count);
							 | 
						||
| 
								 | 
							
								  STBDS_STATS(++stbds_hash_alloc);
							 | 
						||
| 
								 | 
							
								  if (ot) {
							 | 
						||
| 
								 | 
							
								    t->string = ot->string;
							 | 
						||
| 
								 | 
							
								    // reuse old seed so we can reuse old hashes so below "copy out old data" doesn't do any hashing
							 | 
						||
| 
								 | 
							
								    t->seed = ot->seed;
							 | 
						||
| 
								 | 
							
								  } else {
							 | 
						||
| 
								 | 
							
								    size_t a,b,temp;
							 | 
						||
| 
								 | 
							
								    memset(&t->string, 0, sizeof(t->string));
							 | 
						||
| 
								 | 
							
								    t->seed = stbds_hash_seed;
							 | 
						||
| 
								 | 
							
								    // LCG
							 | 
						||
| 
								 | 
							
								    // in 32-bit, a =          2147001325   b =  715136305
							 | 
						||
| 
								 | 
							
								    // in 64-bit, a = 2862933555777941757   b = 3037000493
							 | 
						||
| 
								 | 
							
								    stbds_load_32_or_64(a,temp, 2147001325, 0x27bb2ee6, 0x87b0b0fd);
							 | 
						||
| 
								 | 
							
								    stbds_load_32_or_64(b,temp,  715136305,          0, 0xb504f32d);
							 | 
						||
| 
								 | 
							
								    stbds_hash_seed = stbds_hash_seed  * a + b;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  {
							 | 
						||
| 
								 | 
							
								    size_t i,j;
							 | 
						||
| 
								 | 
							
								    for (i=0; i < slot_count >> STBDS_BUCKET_SHIFT; ++i) {
							 | 
						||
| 
								 | 
							
								      stbds_hash_bucket *b = &t->storage[i];
							 | 
						||
| 
								 | 
							
								      for (j=0; j < STBDS_BUCKET_LENGTH; ++j)
							 | 
						||
| 
								 | 
							
								        b->hash[j] = STBDS_HASH_EMPTY;
							 | 
						||
| 
								 | 
							
								      for (j=0; j < STBDS_BUCKET_LENGTH; ++j)
							 | 
						||
| 
								 | 
							
								        b->index[j] = STBDS_INDEX_EMPTY;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  // copy out the old data, if any
							 | 
						||
| 
								 | 
							
								  if (ot) {
							 | 
						||
| 
								 | 
							
								    size_t i,j;
							 | 
						||
| 
								 | 
							
								    t->used_count = ot->used_count;
							 | 
						||
| 
								 | 
							
								    for (i=0; i < ot->slot_count >> STBDS_BUCKET_SHIFT; ++i) {
							 | 
						||
| 
								 | 
							
								      stbds_hash_bucket *ob = &ot->storage[i];
							 | 
						||
| 
								 | 
							
								      for (j=0; j < STBDS_BUCKET_LENGTH; ++j) {
							 | 
						||
| 
								 | 
							
								        if (STBDS_INDEX_IN_USE(ob->index[j])) {
							 | 
						||
| 
								 | 
							
								          size_t hash = ob->hash[j];
							 | 
						||
| 
								 | 
							
								          size_t pos = stbds_probe_position(hash, t->slot_count, t->slot_count_log2);
							 | 
						||
| 
								 | 
							
								          size_t step = STBDS_BUCKET_LENGTH;
							 | 
						||
| 
								 | 
							
								          STBDS_STATS(++stbds_rehash_items);
							 | 
						||
| 
								 | 
							
								          for (;;) {
							 | 
						||
| 
								 | 
							
								            size_t limit,z;
							 | 
						||
| 
								 | 
							
								            stbds_hash_bucket *bucket;
							 | 
						||
| 
								 | 
							
								            bucket = &t->storage[pos >> STBDS_BUCKET_SHIFT];
							 | 
						||
| 
								 | 
							
								            STBDS_STATS(++stbds_rehash_probes);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            for (z=pos & STBDS_BUCKET_MASK; z < STBDS_BUCKET_LENGTH; ++z) {
							 | 
						||
| 
								 | 
							
								              if (bucket->hash[z] == 0) {
							 | 
						||
| 
								 | 
							
								                bucket->hash[z] = hash;
							 | 
						||
| 
								 | 
							
								                bucket->index[z] = ob->index[j];
							 | 
						||
| 
								 | 
							
								                goto done;
							 | 
						||
| 
								 | 
							
								              }
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            limit = pos & STBDS_BUCKET_MASK;
							 | 
						||
| 
								 | 
							
								            for (z = 0; z < limit; ++z) {
							 | 
						||
| 
								 | 
							
								              if (bucket->hash[z] == 0) {
							 | 
						||
| 
								 | 
							
								                bucket->hash[z] = hash;
							 | 
						||
| 
								 | 
							
								                bucket->index[z] = ob->index[j];
							 | 
						||
| 
								 | 
							
								                goto done;
							 | 
						||
| 
								 | 
							
								              }
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            pos += step;                  // quadratic probing
							 | 
						||
| 
								 | 
							
								            step += STBDS_BUCKET_LENGTH;
							 | 
						||
| 
								 | 
							
								            pos &= (t->slot_count-1);
							 | 
						||
| 
								 | 
							
								          }
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								       done:
							 | 
						||
| 
								 | 
							
								        ;
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  return t;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#define STBDS_ROTATE_LEFT(val, n)   (((val) << (n)) | ((val) >> (STBDS_SIZE_T_BITS - (n))))
							 | 
						||
| 
								 | 
							
								#define STBDS_ROTATE_RIGHT(val, n)  (((val) >> (n)) | ((val) << (STBDS_SIZE_T_BITS - (n))))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								size_t stbds_hash_string(char *str, size_t seed)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								  size_t hash = seed;
							 | 
						||
| 
								 | 
							
								  while (*str)
							 | 
						||
| 
								 | 
							
								     hash = STBDS_ROTATE_LEFT(hash, 9) + (unsigned char) *str++;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  // Thomas Wang 64-to-32 bit mix function, hopefully also works in 32 bits
							 | 
						||
| 
								 | 
							
								  hash ^= seed;
							 | 
						||
| 
								 | 
							
								  hash = (~hash) + (hash << 18);
							 | 
						||
| 
								 | 
							
								  hash ^= hash ^ STBDS_ROTATE_RIGHT(hash,31);
							 | 
						||
| 
								 | 
							
								  hash = hash * 21;
							 | 
						||
| 
								 | 
							
								  hash ^= hash ^ STBDS_ROTATE_RIGHT(hash,11);
							 | 
						||
| 
								 | 
							
								  hash += (hash << 6);
							 | 
						||
| 
								 | 
							
								  hash ^= STBDS_ROTATE_RIGHT(hash,22);
							 | 
						||
| 
								 | 
							
								  return hash+seed;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#ifdef STBDS_SIPHASH_2_4
							 | 
						||
| 
								 | 
							
								#define STBDS_SIPHASH_C_ROUNDS 2
							 | 
						||
| 
								 | 
							
								#define STBDS_SIPHASH_D_ROUNDS 4
							 | 
						||
| 
								 | 
							
								typedef int STBDS_SIPHASH_2_4_can_only_be_used_in_64_bit_builds[sizeof(size_t) == 8 ? 1 : -1];
							 | 
						||
| 
								 | 
							
								#endif
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#ifndef STBDS_SIPHASH_C_ROUNDS
							 | 
						||
| 
								 | 
							
								#define STBDS_SIPHASH_C_ROUNDS 1
							 | 
						||
| 
								 | 
							
								#endif
							 | 
						||
| 
								 | 
							
								#ifndef STBDS_SIPHASH_D_ROUNDS
							 | 
						||
| 
								 | 
							
								#define STBDS_SIPHASH_D_ROUNDS 1
							 | 
						||
| 
								 | 
							
								#endif
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static size_t stbds_siphash_bytes(void *p, size_t len, size_t seed)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								  unsigned char *d = (unsigned char *) p;
							 | 
						||
| 
								 | 
							
								  size_t i,j;
							 | 
						||
| 
								 | 
							
								  size_t v0,v1,v2,v3, data;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  // hash that works on 32- or 64-bit registers without knowing which we have
							 | 
						||
| 
								 | 
							
								  // (computes different results on 32-bit and 64-bit platform)
							 | 
						||
| 
								 | 
							
								  // derived from siphash, but on 32-bit platforms very different as it uses 4 32-bit state not 4 64-bit
							 | 
						||
| 
								 | 
							
								  v0 = ((((size_t) 0x736f6d65 << 16) << 16) + 0x70736575) ^  seed;
							 | 
						||
| 
								 | 
							
								  v1 = ((((size_t) 0x646f7261 << 16) << 16) + 0x6e646f6d) ^ ~seed;
							 | 
						||
| 
								 | 
							
								  v2 = ((((size_t) 0x6c796765 << 16) << 16) + 0x6e657261) ^  seed; 
							 | 
						||
| 
								 | 
							
								  v3 = ((((size_t) 0x74656462 << 16) << 16) + 0x79746573) ^ ~seed;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  #ifdef STBDS_TEST_SIPHASH_2_4
							 | 
						||
| 
								 | 
							
								  // hardcoded with key material in the siphash test vectors
							 | 
						||
| 
								 | 
							
								  v0 ^= 0x0706050403020100ull ^  seed;
							 | 
						||
| 
								 | 
							
								  v1 ^= 0x0f0e0d0c0b0a0908ull ^ ~seed;
							 | 
						||
| 
								 | 
							
								  v2 ^= 0x0706050403020100ull ^  seed;
							 | 
						||
| 
								 | 
							
								  v3 ^= 0x0f0e0d0c0b0a0908ull ^ ~seed;
							 | 
						||
| 
								 | 
							
								  #endif
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  #define STBDS_SIPROUND() \
							 | 
						||
| 
								 | 
							
								    do {                   \
							 | 
						||
| 
								 | 
							
								      v0 += v1; v1 = STBDS_ROTATE_LEFT(v1, 13);  v1 ^= v0; v0 = STBDS_ROTATE_LEFT(v0,STBDS_SIZE_T_BITS/2); \
							 | 
						||
| 
								 | 
							
								      v2 += v3; v3 = STBDS_ROTATE_LEFT(v3, 16);  v3 ^= v2;                                                 \
							 | 
						||
| 
								 | 
							
								      v2 += v1; v1 = STBDS_ROTATE_LEFT(v1, 17);  v1 ^= v2; v2 = STBDS_ROTATE_LEFT(v2,STBDS_SIZE_T_BITS/2); \
							 | 
						||
| 
								 | 
							
								      v0 += v3; v3 = STBDS_ROTATE_LEFT(v3, 21);  v3 ^= v0;                                                 \
							 | 
						||
| 
								 | 
							
								    } while (0)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  for (i=0; i+sizeof(size_t) <= len; i += sizeof(size_t), d += sizeof(size_t)) {
							 | 
						||
| 
								 | 
							
								    data = d[0] | (d[1] << 8) | (d[2] << 16) | (d[3] << 24);
							 | 
						||
| 
								 | 
							
								    data |= (size_t) (d[4] | (d[5] << 8) | (d[6] << 16) | (d[7] << 24)) << 16 << 16; // discarded if size_t == 4
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    v3 ^= data;
							 | 
						||
| 
								 | 
							
								    for (j=0; j < STBDS_SIPHASH_C_ROUNDS; ++j)
							 | 
						||
| 
								 | 
							
								      STBDS_SIPROUND();
							 | 
						||
| 
								 | 
							
								    v0 ^= data;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  data = len << (STBDS_SIZE_T_BITS-8);
							 | 
						||
| 
								 | 
							
								  switch (len - i) {
							 | 
						||
| 
								 | 
							
								    case 7: data |= ((size_t) d[6] << 24) << 24;
							 | 
						||
| 
								 | 
							
								    case 6: data |= ((size_t) d[5] << 20) << 20;
							 | 
						||
| 
								 | 
							
								    case 5: data |= ((size_t) d[4] << 16) << 16;
							 | 
						||
| 
								 | 
							
								    case 4: data |= (d[3] << 24);
							 | 
						||
| 
								 | 
							
								    case 3: data |= (d[2] << 16);
							 | 
						||
| 
								 | 
							
								    case 2: data |= (d[1] << 8);
							 | 
						||
| 
								 | 
							
								    case 1: data |= d[0];
							 | 
						||
| 
								 | 
							
								    case 0: break;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  v3 ^= data;
							 | 
						||
| 
								 | 
							
								  for (j=0; j < STBDS_SIPHASH_C_ROUNDS; ++j)
							 | 
						||
| 
								 | 
							
								    STBDS_SIPROUND();
							 | 
						||
| 
								 | 
							
								  v0 ^= data;
							 | 
						||
| 
								 | 
							
								  v2 ^= 0xff;
							 | 
						||
| 
								 | 
							
								  for (j=0; j < STBDS_SIPHASH_D_ROUNDS; ++j)
							 | 
						||
| 
								 | 
							
								    STBDS_SIPROUND();
							 | 
						||
| 
								 | 
							
								#ifdef STBDS_SIPHASH_2_4
							 | 
						||
| 
								 | 
							
								  return v0^v1^v2^v3;
							 | 
						||
| 
								 | 
							
								#else
							 | 
						||
| 
								 | 
							
								  return v1^v2^v3; // slightly stronger since v0^v3 in above cancels out final round operation? I tweeted at the authors of SipHash about this but they didn't reply
							 | 
						||
| 
								 | 
							
								#endif
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								size_t stbds_hash_bytes(void *p, size_t len, size_t seed)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								#ifdef STBDS_SIPHASH_2_4
							 | 
						||
| 
								 | 
							
								  return stbds_siphash_bytes(p,len,seed);
							 | 
						||
| 
								 | 
							
								#else
							 | 
						||
| 
								 | 
							
								  unsigned char *d = (unsigned char *) p;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (len == 4) {
							 | 
						||
| 
								 | 
							
								    unsigned int hash = d[0] | (d[1] << 8) | (d[2] << 16) | (d[3] << 24);
							 | 
						||
| 
								 | 
							
								    #if 0
							 | 
						||
| 
								 | 
							
								    // HASH32-A  Bob Jenkin's hash function w/o large constants
							 | 
						||
| 
								 | 
							
								    hash ^= seed;
							 | 
						||
| 
								 | 
							
								    hash -= (hash<<6);
							 | 
						||
| 
								 | 
							
								    hash ^= (hash>>17);
							 | 
						||
| 
								 | 
							
								    hash -= (hash<<9);
							 | 
						||
| 
								 | 
							
								    hash ^= seed;
							 | 
						||
| 
								 | 
							
								    hash ^= (hash<<4);
							 | 
						||
| 
								 | 
							
								    hash -= (hash<<3);
							 | 
						||
| 
								 | 
							
								    hash ^= (hash<<10);
							 | 
						||
| 
								 | 
							
								    hash ^= (hash>>15);
							 | 
						||
| 
								 | 
							
								    #elif 1
							 | 
						||
| 
								 | 
							
								    // HASH32-BB  Bob Jenkin's presumably-accidental version of Thomas Wang hash with rotates turned into shifts.
							 | 
						||
| 
								 | 
							
								    // Note that converting these back to rotates makes it run a lot slower, presumably due to collisions, so I'm
							 | 
						||
| 
								 | 
							
								    // not really sure what's going on.
							 | 
						||
| 
								 | 
							
								    hash ^= seed;
							 | 
						||
| 
								 | 
							
								    hash = (hash ^ 61) ^ (hash >> 16);
							 | 
						||
| 
								 | 
							
								    hash = hash + (hash << 3);
							 | 
						||
| 
								 | 
							
								    hash = hash ^ (hash >> 4);
							 | 
						||
| 
								 | 
							
								    hash = hash * 0x27d4eb2d;
							 | 
						||
| 
								 | 
							
								    hash ^= seed;
							 | 
						||
| 
								 | 
							
								    hash = hash ^ (hash >> 15);
							 | 
						||
| 
								 | 
							
								    #else  // HASH32-C   -  Murmur3
							 | 
						||
| 
								 | 
							
								    hash ^= seed;
							 | 
						||
| 
								 | 
							
								    hash *= 0xcc9e2d51;
							 | 
						||
| 
								 | 
							
								    hash = (hash << 17) | (hash >> 15);
							 | 
						||
| 
								 | 
							
								    hash *= 0x1b873593;
							 | 
						||
| 
								 | 
							
								    hash ^= seed;
							 | 
						||
| 
								 | 
							
								    hash = (hash << 19) | (hash >> 13);
							 | 
						||
| 
								 | 
							
								    hash = hash*5 + 0xe6546b64;
							 | 
						||
| 
								 | 
							
								    hash ^= hash >> 16;
							 | 
						||
| 
								 | 
							
								    hash *= 0x85ebca6b;
							 | 
						||
| 
								 | 
							
								    hash ^= seed;
							 | 
						||
| 
								 | 
							
								    hash ^= hash >> 13;
							 | 
						||
| 
								 | 
							
								    hash *= 0xc2b2ae35;
							 | 
						||
| 
								 | 
							
								    hash ^= hash >> 16;
							 | 
						||
| 
								 | 
							
								    #endif
							 | 
						||
| 
								 | 
							
								    // Following statistics were measured on a Core i7-6700 @ 4.00Ghz, compiled with clang 7.0.1 -O2
							 | 
						||
| 
								 | 
							
								    // Note that the larger tables have high variance as they were run fewer times
							 | 
						||
| 
								 | 
							
								    //  HASH32-A   //  HASH32-BB  //  HASH32-C
							 | 
						||
| 
								 | 
							
								    //    0.10ms   //    0.10ms   //    0.10ms :      2,000 inserts creating 2K table   
							 | 
						||
| 
								 | 
							
								    //    0.96ms   //    0.95ms   //    0.99ms :     20,000 inserts creating 20K table  
							 | 
						||
| 
								 | 
							
								    //   14.69ms   //   14.43ms   //   14.97ms :    200,000 inserts creating 200K table 
							 | 
						||
| 
								 | 
							
								    //  199.99ms   //  195.36ms   //  202.05ms :  2,000,000 inserts creating 2M table   
							 | 
						||
| 
								 | 
							
								    // 2234.84ms   // 2187.74ms   // 2240.38ms : 20,000,000 inserts creating 20M table  
							 | 
						||
| 
								 | 
							
								    //   55.68ms   //   53.72ms   //   57.31ms : 500,000 inserts & deletes in 2K table  
							 | 
						||
| 
								 | 
							
								    //   63.43ms   //   61.99ms   //   65.73ms : 500,000 inserts & deletes in 20K table 
							 | 
						||
| 
								 | 
							
								    //   80.04ms   //   77.96ms   //   81.83ms : 500,000 inserts & deletes in 200K table
							 | 
						||
| 
								 | 
							
								    //  100.42ms   //   97.40ms   //  102.39ms : 500,000 inserts & deletes in 2M table  
							 | 
						||
| 
								 | 
							
								    //  119.71ms   //  120.59ms   //  121.63ms : 500,000 inserts & deletes in 20M table 
							 | 
						||
| 
								 | 
							
								    //  185.28ms   //  195.15ms   //  187.74ms : 500,000 inserts & deletes in 200M table
							 | 
						||
| 
								 | 
							
								    //   15.58ms   //   14.79ms   //   15.52ms : 200,000 inserts creating 200K table with varying key spacing
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    return (((size_t) hash << 16 << 16) | hash) ^ seed;
							 | 
						||
| 
								 | 
							
								  } else if (len == 8 && sizeof(size_t) == 8) {
							 | 
						||
| 
								 | 
							
								    size_t hash = d[0] | (d[1] << 8) | (d[2] << 16) | (d[3] << 24);
							 | 
						||
| 
								 | 
							
								    hash |= (size_t) (d[4] | (d[5] << 8) | (d[6] << 16) | (d[7] << 24)) << 16 << 16; // avoid warning if size_t == 4
							 | 
						||
| 
								 | 
							
								    hash ^= seed;
							 | 
						||
| 
								 | 
							
								    hash = (~hash) + (hash << 21);
							 | 
						||
| 
								 | 
							
								    hash ^= STBDS_ROTATE_RIGHT(hash,24);
							 | 
						||
| 
								 | 
							
								    hash *= 265;
							 | 
						||
| 
								 | 
							
								    hash ^= STBDS_ROTATE_RIGHT(hash,14);
							 | 
						||
| 
								 | 
							
								    hash ^= seed;
							 | 
						||
| 
								 | 
							
								    hash *= 21;
							 | 
						||
| 
								 | 
							
								    hash ^= STBDS_ROTATE_RIGHT(hash,28);
							 | 
						||
| 
								 | 
							
								    hash += (hash << 31);
							 | 
						||
| 
								 | 
							
								    hash = (~hash) + (hash << 18);
							 | 
						||
| 
								 | 
							
								    return hash;
							 | 
						||
| 
								 | 
							
								  } else {
							 | 
						||
| 
								 | 
							
								    return stbds_siphash_bytes(p,len,seed);
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								#endif
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static int stbds_is_key_equal(void *a, size_t elemsize, void *key, size_t keysize, int mode, size_t i)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								  if (mode >= STBDS_HM_STRING)
							 | 
						||
| 
								 | 
							
								    return 0==strcmp((char *) key, * (char **) ((char *) a + elemsize*i));
							 | 
						||
| 
								 | 
							
								  else
							 | 
						||
| 
								 | 
							
								    return 0==memcmp(key, (char *) a + elemsize*i, keysize);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#define STBDS_HASH_TO_ARR(x,elemsize) ((char*) (x) - (elemsize))
							 | 
						||
| 
								 | 
							
								#define STBDS_ARR_TO_HASH(x,elemsize) ((char*) (x) + (elemsize))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#define stbds_hash_table(a)  ((stbds_hash_index *) stbds_header(a)->hash_table)
							 | 
						||
| 
								 | 
							
								 
							 | 
						||
| 
								 | 
							
								void stbds_hmfree_func(void *a, size_t elemsize, size_t keyoff)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								  if (a == NULL) return;
							 | 
						||
| 
								 | 
							
								  if (stbds_hash_table(a) != NULL) {
							 | 
						||
| 
								 | 
							
								     if (stbds_hash_table(a)->string.mode == STBDS_SH_STRDUP) {
							 | 
						||
| 
								 | 
							
								       size_t i;
							 | 
						||
| 
								 | 
							
								       // skip 0th element, which is default
							 | 
						||
| 
								 | 
							
								       for (i=1; i < stbds_header(a)->length; ++i)
							 | 
						||
| 
								 | 
							
								         STBDS_FREE(NULL, *(char**) ((char *) a + elemsize*i));
							 | 
						||
| 
								 | 
							
								     }
							 | 
						||
| 
								 | 
							
								     stbds_strreset(&stbds_hash_table(a)->string);
							 | 
						||
| 
								 | 
							
								   }
							 | 
						||
| 
								 | 
							
								  STBDS_FREE(NULL, stbds_header(a)->hash_table);
							 | 
						||
| 
								 | 
							
								  STBDS_FREE(NULL, stbds_header(a));
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static ptrdiff_t stbds_hm_find_slot(void *a, size_t elemsize, void *key, size_t keysize, int mode)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								  void *raw_a = STBDS_HASH_TO_ARR(a,elemsize);
							 | 
						||
| 
								 | 
							
								  stbds_hash_index *table = stbds_hash_table(raw_a);
							 | 
						||
| 
								 | 
							
								  size_t hash = mode >= STBDS_HM_STRING ? stbds_hash_string((char*)key,table->seed) : stbds_hash_bytes(key, keysize,table->seed);
							 | 
						||
| 
								 | 
							
								  size_t step = STBDS_BUCKET_LENGTH;
							 | 
						||
| 
								 | 
							
								  size_t limit,i;
							 | 
						||
| 
								 | 
							
								  size_t pos;
							 | 
						||
| 
								 | 
							
								  stbds_hash_bucket *bucket;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (hash < 2) hash += 2; // stored hash values are forbidden from being 0, so we can detect empty slots
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  pos = stbds_probe_position(hash, table->slot_count, table->slot_count_log2);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  for (;;) {
							 | 
						||
| 
								 | 
							
								    STBDS_STATS(++stbds_hash_probes);
							 | 
						||
| 
								 | 
							
								    bucket = &table->storage[pos >> STBDS_BUCKET_SHIFT];
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // start searching from pos to end of bucket, this should help performance on small hash tables that fit in cache
							 | 
						||
| 
								 | 
							
								    for (i=pos & STBDS_BUCKET_MASK; i < STBDS_BUCKET_LENGTH; ++i) {
							 | 
						||
| 
								 | 
							
								      if (bucket->hash[i] == hash) {
							 | 
						||
| 
								 | 
							
								        if (stbds_is_key_equal(a, elemsize, key, keysize, mode, bucket->index[i])) {
							 | 
						||
| 
								 | 
							
								          return (pos & ~STBDS_BUCKET_MASK)+i;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								      } else if (bucket->hash[i] == STBDS_HASH_EMPTY) {
							 | 
						||
| 
								 | 
							
								        return -1;
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // search from beginning of bucket to pos
							 | 
						||
| 
								 | 
							
								    limit = pos & STBDS_BUCKET_MASK;
							 | 
						||
| 
								 | 
							
								    for (i = 0; i < limit; ++i) {
							 | 
						||
| 
								 | 
							
								      if (bucket->hash[i] == hash) {
							 | 
						||
| 
								 | 
							
								        if (stbds_is_key_equal(a, elemsize, key, keysize, mode, bucket->index[i])) {
							 | 
						||
| 
								 | 
							
								          return (pos & ~STBDS_BUCKET_MASK)+i;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								      } else if (bucket->hash[i] == STBDS_HASH_EMPTY) {
							 | 
						||
| 
								 | 
							
								        return -1;
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // quadratic probing
							 | 
						||
| 
								 | 
							
								    pos += step;
							 | 
						||
| 
								 | 
							
								    step += STBDS_BUCKET_LENGTH;
							 | 
						||
| 
								 | 
							
								    pos &= (table->slot_count-1);
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  /* NOTREACHED */
							 | 
						||
| 
								 | 
							
								  return -1;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								void * stbds_hmget_key(void *a, size_t elemsize, void *key, size_t keysize, int mode)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								  if (a == NULL) {
							 | 
						||
| 
								 | 
							
								    // make it non-empty so we can return a temp
							 | 
						||
| 
								 | 
							
								    a = stbds_arrgrowf(0, elemsize, 0, 1);
							 | 
						||
| 
								 | 
							
								    stbds_header(a)->length += 1;
							 | 
						||
| 
								 | 
							
								    memset(a, 0, elemsize);
							 | 
						||
| 
								 | 
							
								    stbds_temp(a) = STBDS_INDEX_EMPTY;
							 | 
						||
| 
								 | 
							
								    // adjust a to point after the default element
							 | 
						||
| 
								 | 
							
								    return STBDS_ARR_TO_HASH(a,elemsize);
							 | 
						||
| 
								 | 
							
								  } else {
							 | 
						||
| 
								 | 
							
								    stbds_hash_index *table;
							 | 
						||
| 
								 | 
							
								    void *raw_a = STBDS_HASH_TO_ARR(a,elemsize);
							 | 
						||
| 
								 | 
							
								    // adjust a to point to the default element
							 | 
						||
| 
								 | 
							
								    table = (stbds_hash_index *) stbds_header(raw_a)->hash_table;
							 | 
						||
| 
								 | 
							
								    if (table == 0) {
							 | 
						||
| 
								 | 
							
								      stbds_temp(raw_a) = -1;
							 | 
						||
| 
								 | 
							
								    } else {
							 | 
						||
| 
								 | 
							
								      ptrdiff_t slot = stbds_hm_find_slot(a, elemsize, key, keysize, mode);
							 | 
						||
| 
								 | 
							
								      if (slot < 0) {
							 | 
						||
| 
								 | 
							
								        stbds_temp(raw_a) = STBDS_INDEX_EMPTY;
							 | 
						||
| 
								 | 
							
								      } else {
							 | 
						||
| 
								 | 
							
								        stbds_hash_bucket *b = &table->storage[slot >> STBDS_BUCKET_SHIFT];
							 | 
						||
| 
								 | 
							
								        stbds_temp(raw_a) = b->index[slot & STBDS_BUCKET_MASK];
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    return a;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								void * stbds_hmput_default(void *a, size_t elemsize)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								  // three cases:
							 | 
						||
| 
								 | 
							
								  //   a is NULL <- allocate
							 | 
						||
| 
								 | 
							
								  //   a has a hash table but no entries, because of shmode <- grow
							 | 
						||
| 
								 | 
							
								  //   a has entries <- do nothing
							 | 
						||
| 
								 | 
							
								  if (a == NULL || stbds_header(STBDS_HASH_TO_ARR(a,elemsize))->length == 0) {
							 | 
						||
| 
								 | 
							
								    a = stbds_arrgrowf(a ? STBDS_HASH_TO_ARR(a,elemsize) : NULL, elemsize, 0, 1);
							 | 
						||
| 
								 | 
							
								    stbds_header(a)->length += 1;
							 | 
						||
| 
								 | 
							
								    memset(a, 0, elemsize);
							 | 
						||
| 
								 | 
							
								    a=STBDS_ARR_TO_HASH(a,elemsize);
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  return a;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static char *stbds_strdup(char *str);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								void *stbds_hmput_key(void *a, size_t elemsize, void *key, size_t keysize, int mode)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								  void *raw_a;
							 | 
						||
| 
								 | 
							
								  stbds_hash_index *table;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (a == NULL) {
							 | 
						||
| 
								 | 
							
								    a = stbds_arrgrowf(0, elemsize, 0, 1);
							 | 
						||
| 
								 | 
							
								    memset(a, 0, elemsize);
							 | 
						||
| 
								 | 
							
								    stbds_header(a)->length += 1;
							 | 
						||
| 
								 | 
							
								    // adjust a to point AFTER the default element
							 | 
						||
| 
								 | 
							
								    a = STBDS_ARR_TO_HASH(a,elemsize);
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  // adjust a to point to the default element
							 | 
						||
| 
								 | 
							
								  raw_a = a;
							 | 
						||
| 
								 | 
							
								  a = STBDS_HASH_TO_ARR(a,elemsize);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  table = (stbds_hash_index *) stbds_header(a)->hash_table;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (table == NULL || table->used_count >= table->used_count_threshold) {
							 | 
						||
| 
								 | 
							
								    stbds_hash_index *nt;
							 | 
						||
| 
								 | 
							
								    size_t slot_count;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    slot_count = (table == NULL) ? STBDS_BUCKET_LENGTH : table->slot_count*2;
							 | 
						||
| 
								 | 
							
								    nt = stbds_make_hash_index(slot_count, table);
							 | 
						||
| 
								 | 
							
								    if (table) {
							 | 
						||
| 
								 | 
							
								      STBDS_FREE(NULL, table);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    stbds_header(a)->hash_table = table = nt;
							 | 
						||
| 
								 | 
							
								    STBDS_STATS(++stbds_hash_grow);
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  // we iterate hash table explicitly because we want to track if we saw a tombstone
							 | 
						||
| 
								 | 
							
								  {
							 | 
						||
| 
								 | 
							
								    size_t hash = mode >= STBDS_HM_STRING ? stbds_hash_string((char*)key,table->seed) : stbds_hash_bytes(key, keysize,table->seed);
							 | 
						||
| 
								 | 
							
								    size_t step = STBDS_BUCKET_LENGTH;
							 | 
						||
| 
								 | 
							
								    size_t limit,i;
							 | 
						||
| 
								 | 
							
								    size_t pos;
							 | 
						||
| 
								 | 
							
								    ptrdiff_t tombstone = -1;
							 | 
						||
| 
								 | 
							
								    stbds_hash_bucket *bucket;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // stored hash values are forbidden from being 0, so we can detect empty slots to early out quickly
							 | 
						||
| 
								 | 
							
								    if (hash < 2) hash += 2;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    pos = stbds_probe_position(hash, table->slot_count, table->slot_count_log2);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    for (;;) {
							 | 
						||
| 
								 | 
							
								      STBDS_STATS(++stbds_hash_probes);
							 | 
						||
| 
								 | 
							
								      bucket = &table->storage[pos >> STBDS_BUCKET_SHIFT];
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      // start searching from pos to end of bucket
							 | 
						||
| 
								 | 
							
								      for (i=pos & STBDS_BUCKET_MASK; i < STBDS_BUCKET_LENGTH; ++i) {
							 | 
						||
| 
								 | 
							
								        if (bucket->hash[i] == hash) {
							 | 
						||
| 
								 | 
							
								          if (stbds_is_key_equal(raw_a, elemsize, key, keysize, mode, bucket->index[i])) {
							 | 
						||
| 
								 | 
							
								            stbds_temp(a) = bucket->index[i];
							 | 
						||
| 
								 | 
							
								            return STBDS_ARR_TO_HASH(a,elemsize);
							 | 
						||
| 
								 | 
							
								          }
							 | 
						||
| 
								 | 
							
								        } else if (bucket->hash[i] == 0) {
							 | 
						||
| 
								 | 
							
								          pos = (pos & ~STBDS_BUCKET_MASK) + i;
							 | 
						||
| 
								 | 
							
								          goto found_empty_slot;
							 | 
						||
| 
								 | 
							
								        } else if (tombstone < 0) {
							 | 
						||
| 
								 | 
							
								          if (bucket->index[i] == STBDS_INDEX_DELETED)
							 | 
						||
| 
								 | 
							
								            tombstone = (ptrdiff_t) ((pos & ~STBDS_BUCKET_MASK) + i);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      // search from beginning of bucket to pos
							 | 
						||
| 
								 | 
							
								      limit = pos & STBDS_BUCKET_MASK;
							 | 
						||
| 
								 | 
							
								      for (i = 0; i < limit; ++i) {
							 | 
						||
| 
								 | 
							
								        if (bucket->hash[i] == hash) {
							 | 
						||
| 
								 | 
							
								          if (stbds_is_key_equal(raw_a, elemsize, key, keysize, mode, bucket->index[i])) {
							 | 
						||
| 
								 | 
							
								            stbds_temp(a) = bucket->index[i];
							 | 
						||
| 
								 | 
							
								            return STBDS_ARR_TO_HASH(a,elemsize);
							 | 
						||
| 
								 | 
							
								          }
							 | 
						||
| 
								 | 
							
								        } else if (bucket->hash[i] == 0) {
							 | 
						||
| 
								 | 
							
								          pos = (pos & ~STBDS_BUCKET_MASK) + i;
							 | 
						||
| 
								 | 
							
								          goto found_empty_slot;
							 | 
						||
| 
								 | 
							
								        } else if (tombstone < 0) {
							 | 
						||
| 
								 | 
							
								          if (bucket->index[i] == STBDS_INDEX_DELETED)
							 | 
						||
| 
								 | 
							
								            tombstone = (ptrdiff_t) ((pos & ~STBDS_BUCKET_MASK) + i);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      // quadratic probing
							 | 
						||
| 
								 | 
							
								      pos += step;
							 | 
						||
| 
								 | 
							
								      step += STBDS_BUCKET_LENGTH;
							 | 
						||
| 
								 | 
							
								      pos &= (table->slot_count-1);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								   found_empty_slot:
							 | 
						||
| 
								 | 
							
								    if (tombstone >= 0) {
							 | 
						||
| 
								 | 
							
								      pos = tombstone;
							 | 
						||
| 
								 | 
							
								      --table->tombstone_count;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    ++table->used_count;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								      ptrdiff_t i = (ptrdiff_t) stbds_arrlen(a);
							 | 
						||
| 
								 | 
							
								    // we want to do stbds_arraddn(1), but we can't use the macros since we don't have something of the right type
							 | 
						||
| 
								 | 
							
								      if ((size_t) i+1 > stbds_arrcap(a))
							 | 
						||
| 
								 | 
							
								        *(void **) &a = stbds_arrgrowf(a, elemsize, 1, 0);
							 | 
						||
| 
								 | 
							
								      raw_a = STBDS_ARR_TO_HASH(a,elemsize);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      STBDS_ASSERT((size_t) i+1 <= stbds_arrcap(a));
							 | 
						||
| 
								 | 
							
								      stbds_header(a)->length = i+1;
							 | 
						||
| 
								 | 
							
								      bucket = &table->storage[pos >> STBDS_BUCKET_SHIFT];
							 | 
						||
| 
								 | 
							
								      bucket->hash[pos & STBDS_BUCKET_MASK] = hash;
							 | 
						||
| 
								 | 
							
								      bucket->index[pos & STBDS_BUCKET_MASK] = i-1;
							 | 
						||
| 
								 | 
							
								      stbds_temp(a) = i-1;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      switch (table->string.mode) {
							 | 
						||
| 
								 | 
							
								         case STBDS_SH_STRDUP: *(char **) ((char *) a + elemsize*i) = stbds_strdup((char*) key); break;
							 | 
						||
| 
								 | 
							
								         case STBDS_SH_ARENA:  *(char **) ((char *) a + elemsize*i) = stbds_stralloc(&table->string, (char*)key); break;
							 | 
						||
| 
								 | 
							
								         default:              *(char **) ((char *) a + elemsize*i) = (char *) key; break;
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    return STBDS_ARR_TO_HASH(a,elemsize);
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								void * stbds_shmode_func(size_t elemsize, int mode)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								  void *a = stbds_arrgrowf(0, elemsize, 0, 1);
							 | 
						||
| 
								 | 
							
								  stbds_hash_index *h;
							 | 
						||
| 
								 | 
							
								  memset(a, 0, elemsize);
							 | 
						||
| 
								 | 
							
								  stbds_header(a)->length = 1;
							 | 
						||
| 
								 | 
							
								  stbds_header(a)->hash_table = h = (stbds_hash_index *) stbds_make_hash_index(STBDS_BUCKET_LENGTH, NULL);
							 | 
						||
| 
								 | 
							
								  h->string.mode = mode;
							 | 
						||
| 
								 | 
							
								  return STBDS_ARR_TO_HASH(a,elemsize);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								void * stbds_hmdel_key(void *a, size_t elemsize, void *key, size_t keysize, size_t keyoffset, int mode)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								  if (a == NULL) {
							 | 
						||
| 
								 | 
							
								    return 0;
							 | 
						||
| 
								 | 
							
								  } else {
							 | 
						||
| 
								 | 
							
								    stbds_hash_index *table;
							 | 
						||
| 
								 | 
							
								    void *raw_a = STBDS_HASH_TO_ARR(a,elemsize);
							 | 
						||
| 
								 | 
							
								    table = (stbds_hash_index *) stbds_header(raw_a)->hash_table;
							 | 
						||
| 
								 | 
							
								    stbds_temp(raw_a) = 0;
							 | 
						||
| 
								 | 
							
								    if (table == 0) {
							 | 
						||
| 
								 | 
							
								      return a;
							 | 
						||
| 
								 | 
							
								    } else {
							 | 
						||
| 
								 | 
							
								      ptrdiff_t slot;
							 | 
						||
| 
								 | 
							
								      slot = stbds_hm_find_slot(a, elemsize, key, keysize, mode);
							 | 
						||
| 
								 | 
							
								      if (slot < 0)
							 | 
						||
| 
								 | 
							
								        return a;
							 | 
						||
| 
								 | 
							
								      else {
							 | 
						||
| 
								 | 
							
								        stbds_hash_bucket *b = &table->storage[slot >> STBDS_BUCKET_SHIFT];
							 | 
						||
| 
								 | 
							
								        int i = slot & STBDS_BUCKET_MASK;
							 | 
						||
| 
								 | 
							
								        ptrdiff_t old_index = b->index[i];
							 | 
						||
| 
								 | 
							
								        ptrdiff_t final_index = (ptrdiff_t) stbds_arrlen(raw_a)-1-1; // minus one for the raw_a vs a, and minus one for 'last'
							 | 
						||
| 
								 | 
							
								        STBDS_ASSERT(slot < (ptrdiff_t) table->slot_count);
							 | 
						||
| 
								 | 
							
								        --table->used_count;
							 | 
						||
| 
								 | 
							
								        ++table->tombstone_count;
							 | 
						||
| 
								 | 
							
								        stbds_temp(raw_a) = 1;
							 | 
						||
| 
								 | 
							
								        STBDS_ASSERT(table->used_count >= 0);
							 | 
						||
| 
								 | 
							
								        //STBDS_ASSERT(table->tombstone_count < table->slot_count/4);
							 | 
						||
| 
								 | 
							
								        b->hash[i] = STBDS_HASH_DELETED;
							 | 
						||
| 
								 | 
							
								        b->index[i] = STBDS_INDEX_DELETED;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if (mode == STBDS_HM_STRING && table->string.mode == STBDS_SH_STRDUP)
							 | 
						||
| 
								 | 
							
								          STBDS_FREE(NULL, *(char**) ((char *) a+elemsize*old_index));
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        // if indices are the same, memcpy is a no-op, but back-pointer-fixup will fail, so skip
							 | 
						||
| 
								 | 
							
								        if (old_index != final_index) {
							 | 
						||
| 
								 | 
							
								          // swap delete 
							 | 
						||
| 
								 | 
							
								          memmove((char*) a + elemsize*old_index, (char*) a + elemsize*final_index, elemsize);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								          // now find the slot for the last element
							 | 
						||
| 
								 | 
							
								          if (mode == STBDS_HM_STRING)
							 | 
						||
| 
								 | 
							
								            slot = stbds_hm_find_slot(a, elemsize, *(char**) ((char *) a+elemsize*old_index + keyoffset), keysize, mode);
							 | 
						||
| 
								 | 
							
								          else
							 | 
						||
| 
								 | 
							
								            slot = stbds_hm_find_slot(a, elemsize,  (char* ) a+elemsize*old_index + keyoffset, keysize, mode);
							 | 
						||
| 
								 | 
							
								          STBDS_ASSERT(slot >= 0);
							 | 
						||
| 
								 | 
							
								          b = &table->storage[slot >> STBDS_BUCKET_SHIFT];
							 | 
						||
| 
								 | 
							
								          i = slot & STBDS_BUCKET_MASK;
							 | 
						||
| 
								 | 
							
								          STBDS_ASSERT(b->index[i] == final_index);
							 | 
						||
| 
								 | 
							
								          b->index[i] = old_index;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        stbds_header(raw_a)->length -= 1;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if (table->used_count < table->used_count_shrink_threshold && table->slot_count > STBDS_BUCKET_LENGTH) {
							 | 
						||
| 
								 | 
							
								          stbds_header(raw_a)->hash_table = stbds_make_hash_index(table->slot_count>>1, table);
							 | 
						||
| 
								 | 
							
								          STBDS_FREE(NULL, table);
							 | 
						||
| 
								 | 
							
								          STBDS_STATS(++stbds_hash_shrink);
							 | 
						||
| 
								 | 
							
								        } else if (table->tombstone_count > table->tombstone_count_threshold) {
							 | 
						||
| 
								 | 
							
								          stbds_header(raw_a)->hash_table = stbds_make_hash_index(table->slot_count   , table);
							 | 
						||
| 
								 | 
							
								          STBDS_FREE(NULL, table);
							 | 
						||
| 
								 | 
							
								          STBDS_STATS(++stbds_hash_rebuild);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        return a;
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  /* NOTREACHED */
							 | 
						||
| 
								 | 
							
								  return 0;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static char *stbds_strdup(char *str)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								  // to keep replaceable allocator simple, we don't want to use strdup.
							 | 
						||
| 
								 | 
							
								  // rolling our own also avoids problem of strdup vs _strdup
							 | 
						||
| 
								 | 
							
								  size_t len = strlen(str)+1;
							 | 
						||
| 
								 | 
							
								  char *p = (char*) STBDS_REALLOC(NULL, 0, len);
							 | 
						||
| 
								 | 
							
								  memmove(p, str, len);
							 | 
						||
| 
								 | 
							
								  return p;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#ifndef STBDS_STRING_ARENA_BLOCKSIZE_MIN
							 | 
						||
| 
								 | 
							
								#define STBDS_STRING_ARENA_BLOCKSIZE_MIN  512
							 | 
						||
| 
								 | 
							
								#endif
							 | 
						||
| 
								 | 
							
								#ifndef STBDS_STRING_ARENA_BLOCKSIZE_MAX
							 | 
						||
| 
								 | 
							
								#define STBDS_STRING_ARENA_BLOCKSIZE_MAX  1<<20
							 | 
						||
| 
								 | 
							
								#endif
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								char *stbds_stralloc(stbds_string_arena *a, char *str)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								  char *p;
							 | 
						||
| 
								 | 
							
								  size_t len = strlen(str)+1;
							 | 
						||
| 
								 | 
							
								  if (len > a->remaining) {
							 | 
						||
| 
								 | 
							
								    // compute the next blocksize
							 | 
						||
| 
								 | 
							
								    size_t blocksize = a->block;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // size is 512, 512, 1024, 1024, 2048, 2048, 4096, 4096, etc., so that
							 | 
						||
| 
								 | 
							
								    // there are log(SIZE) allocations to free when we destroy the table
							 | 
						||
| 
								 | 
							
								    blocksize = (size_t) (STBDS_STRING_ARENA_BLOCKSIZE_MIN) << (blocksize>>1);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // if size is under 1M, advance to next blocktype
							 | 
						||
| 
								 | 
							
								    if (blocksize < (size_t)(STBDS_STRING_ARENA_BLOCKSIZE_MAX))
							 | 
						||
| 
								 | 
							
								      ++a->block;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (len > blocksize) {
							 | 
						||
| 
								 | 
							
								      // if string is larger than blocksize, then just allocate the full size.
							 | 
						||
| 
								 | 
							
								      // note that we still advance string_block so block size will continue
							 | 
						||
| 
								 | 
							
								      // increasing, so e.g. if somebody only calls this with 1000-long strings,
							 | 
						||
| 
								 | 
							
								      // eventually the arena will start doubling and handling those as well
							 | 
						||
| 
								 | 
							
								      stbds_string_block *sb = (stbds_string_block *) STBDS_REALLOC(NULL, 0, sizeof(*sb)-8 + len);
							 | 
						||
| 
								 | 
							
								      memmove(sb->storage, str, len);
							 | 
						||
| 
								 | 
							
								      if (a->storage) {
							 | 
						||
| 
								 | 
							
								        // insert it after the first element, so that we don't waste the space there
							 | 
						||
| 
								 | 
							
								        sb->next = a->storage->next;
							 | 
						||
| 
								 | 
							
								        a->storage->next = sb;
							 | 
						||
| 
								 | 
							
								      } else {
							 | 
						||
| 
								 | 
							
								        sb->next = 0;
							 | 
						||
| 
								 | 
							
								        a->storage = sb;
							 | 
						||
| 
								 | 
							
								        a->remaining = 0; // this is redundant, but good for clarity
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								      return sb->storage;
							 | 
						||
| 
								 | 
							
								    } else {
							 | 
						||
| 
								 | 
							
								      stbds_string_block *sb = (stbds_string_block *) STBDS_REALLOC(NULL, 0, sizeof(*sb)-8 + blocksize);
							 | 
						||
| 
								 | 
							
								      sb->next = a->storage;
							 | 
						||
| 
								 | 
							
								      a->storage = sb;
							 | 
						||
| 
								 | 
							
								      a->remaining = blocksize;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  STBDS_ASSERT(len <= a->remaining);
							 | 
						||
| 
								 | 
							
								  p = a->storage->storage + a->remaining - len;
							 | 
						||
| 
								 | 
							
								  a->remaining -= len;
							 | 
						||
| 
								 | 
							
								  memmove(p, str, len);
							 | 
						||
| 
								 | 
							
								  return p;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								void stbds_strreset(stbds_string_arena *a)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								  stbds_string_block *x,*y;
							 | 
						||
| 
								 | 
							
								  x = a->storage;
							 | 
						||
| 
								 | 
							
								  while (x) {
							 | 
						||
| 
								 | 
							
								    y = x->next;
							 | 
						||
| 
								 | 
							
								    STBDS_FREE(NULL, x);
							 | 
						||
| 
								 | 
							
								    x = y;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  memset(a, 0, sizeof(*a));
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#endif
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								//////////////////////////////////////////////////////////////////////////////
							 | 
						||
| 
								 | 
							
								//
							 | 
						||
| 
								 | 
							
								//   UNIT TESTS
							 | 
						||
| 
								 | 
							
								//
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#ifdef STBDS_UNIT_TESTS
							 | 
						||
| 
								 | 
							
								#include <stdio.h>
							 | 
						||
| 
								 | 
							
								#ifdef STBDS_ASSERT_WAS_UNDEFINED
							 | 
						||
| 
								 | 
							
								#undef STBDS_ASSERT
							 | 
						||
| 
								 | 
							
								#endif
							 | 
						||
| 
								 | 
							
								#ifndef STBDS_ASSERT
							 | 
						||
| 
								 | 
							
								#define STBDS_ASSERT assert
							 | 
						||
| 
								 | 
							
								#include <assert.h>
							 | 
						||
| 
								 | 
							
								#endif
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								typedef struct { int key,b,c,d; } stbds_struct;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static char buffer[256];
							 | 
						||
| 
								 | 
							
								char *strkey(int n)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								#if defined(_WIN32) && defined(__STDC_WANT_SECURE_LIB__)
							 | 
						||
| 
								 | 
							
								   sprintf_s(buffer, sizeof(buffer), "test_%d", n);
							 | 
						||
| 
								 | 
							
								#else
							 | 
						||
| 
								 | 
							
								   sprintf(buffer, "test_%d", n);
							 | 
						||
| 
								 | 
							
								#endif
							 | 
						||
| 
								 | 
							
								   return buffer;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								void stbds_unit_tests(void)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								#if defined(_MSC_VER) && _MSC_VER <= 1200 && defined(__cplusplus)
							 | 
						||
| 
								 | 
							
								  // VC6 C++ doesn't like the template<> trick on unnamed structures, so do nothing!
							 | 
						||
| 
								 | 
							
								  STBDS_ASSERT(0);
							 | 
						||
| 
								 | 
							
								#else
							 | 
						||
| 
								 | 
							
								  const int testsize = 100000;
							 | 
						||
| 
								 | 
							
								  int *arr=NULL;
							 | 
						||
| 
								 | 
							
								  struct { int   key;        int value; } *intmap = NULL;
							 | 
						||
| 
								 | 
							
								  struct { char *key;        int value; } *strmap = NULL;
							 | 
						||
| 
								 | 
							
								  struct { stbds_struct key; int value; } *map    = NULL;
							 | 
						||
| 
								 | 
							
								  stbds_struct                            *map2   = NULL;
							 | 
						||
| 
								 | 
							
								  stbds_string_arena                       sa     = { 0 };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  int i,j;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  for (i=0; i < 20000; i += 50) {
							 | 
						||
| 
								 | 
							
								    for (j=0; j < i; ++j)
							 | 
						||
| 
								 | 
							
								      arrpush(arr,j);
							 | 
						||
| 
								 | 
							
								    arrfree(arr);
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  for (i=0; i < 4; ++i) {
							 | 
						||
| 
								 | 
							
								    arrpush(arr,1); arrpush(arr,2); arrpush(arr,3); arrpush(arr,4);
							 | 
						||
| 
								 | 
							
								    arrdel(arr,i);
							 | 
						||
| 
								 | 
							
								    arrfree(arr);
							 | 
						||
| 
								 | 
							
								    arrpush(arr,1); arrpush(arr,2); arrpush(arr,3); arrpush(arr,4);
							 | 
						||
| 
								 | 
							
								    arrdelswap(arr,i);
							 | 
						||
| 
								 | 
							
								    arrfree(arr);
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  for (i=0; i < 5; ++i) {
							 | 
						||
| 
								 | 
							
								    arrpush(arr,1); arrpush(arr,2); arrpush(arr,3); arrpush(arr,4);
							 | 
						||
| 
								 | 
							
								    stbds_arrins(arr,i,5);
							 | 
						||
| 
								 | 
							
								    STBDS_ASSERT(arr[i] == 5);
							 | 
						||
| 
								 | 
							
								    if (i < 4)
							 | 
						||
| 
								 | 
							
								      STBDS_ASSERT(arr[4] == 4);
							 | 
						||
| 
								 | 
							
								    arrfree(arr);
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  hmdefault(intmap, -1);
							 | 
						||
| 
								 | 
							
								  i=1; STBDS_ASSERT(hmget(intmap, i) == -1);
							 | 
						||
| 
								 | 
							
								  for (i=0; i < testsize; i+=2)
							 | 
						||
| 
								 | 
							
								    hmput(intmap, i, i*5);
							 | 
						||
| 
								 | 
							
								  for (i=0; i < testsize; i+=1)
							 | 
						||
| 
								 | 
							
								    if (i & 1) STBDS_ASSERT(hmget(intmap, i) == -1 );
							 | 
						||
| 
								 | 
							
								    else       STBDS_ASSERT(hmget(intmap, i) == i*5);
							 | 
						||
| 
								 | 
							
								  for (i=0; i < testsize; i+=2)
							 | 
						||
| 
								 | 
							
								    hmput(intmap, i, i*3);
							 | 
						||
| 
								 | 
							
								  for (i=0; i < testsize; i+=1)
							 | 
						||
| 
								 | 
							
								    if (i & 1) STBDS_ASSERT(hmget(intmap, i) == -1 );
							 | 
						||
| 
								 | 
							
								    else       STBDS_ASSERT(hmget(intmap, i) == i*3);
							 | 
						||
| 
								 | 
							
								  for (i=2; i < testsize; i+=4)
							 | 
						||
| 
								 | 
							
								    hmdel(intmap, i); // delete half the entries
							 | 
						||
| 
								 | 
							
								  for (i=0; i < testsize; i+=1)
							 | 
						||
| 
								 | 
							
								    if (i & 3) STBDS_ASSERT(hmget(intmap, i) == -1 );
							 | 
						||
| 
								 | 
							
								    else       STBDS_ASSERT(hmget(intmap, i) == i*3);
							 | 
						||
| 
								 | 
							
								  for (i=0; i < testsize; i+=1)
							 | 
						||
| 
								 | 
							
								    hmdel(intmap, i); // delete the rest of the entries    
							 | 
						||
| 
								 | 
							
								  for (i=0; i < testsize; i+=1)
							 | 
						||
| 
								 | 
							
								    STBDS_ASSERT(hmget(intmap, i) == -1 );
							 | 
						||
| 
								 | 
							
								  hmfree(intmap);
							 | 
						||
| 
								 | 
							
								  for (i=0; i < testsize; i+=2)
							 | 
						||
| 
								 | 
							
								    hmput(intmap, i, i*3);
							 | 
						||
| 
								 | 
							
								  hmfree(intmap);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  #if defined(__clang__) || defined(__GNUC__)
							 | 
						||
| 
								 | 
							
								  #ifndef __cplusplus
							 | 
						||
| 
								 | 
							
								  intmap = NULL;
							 | 
						||
| 
								 | 
							
								  hmput(intmap, 15, 7);
							 | 
						||
| 
								 | 
							
								  hmput(intmap, 11, 3);
							 | 
						||
| 
								 | 
							
								  hmput(intmap,  9, 5);
							 | 
						||
| 
								 | 
							
								  STBDS_ASSERT(hmget(intmap, 9) == 5);
							 | 
						||
| 
								 | 
							
								  STBDS_ASSERT(hmget(intmap, 11) == 3);
							 | 
						||
| 
								 | 
							
								  STBDS_ASSERT(hmget(intmap, 15) == 7);
							 | 
						||
| 
								 | 
							
								  #endif
							 | 
						||
| 
								 | 
							
								  #endif
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  for (i=0; i < testsize; ++i)
							 | 
						||
| 
								 | 
							
								    stralloc(&sa, strkey(i));
							 | 
						||
| 
								 | 
							
								  strreset(&sa);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  for (j=0; j < 2; ++j) {
							 | 
						||
| 
								 | 
							
								    if (j == 0)
							 | 
						||
| 
								 | 
							
								      sh_new_strdup(strmap);
							 | 
						||
| 
								 | 
							
								    else
							 | 
						||
| 
								 | 
							
								      sh_new_arena(strmap);
							 | 
						||
| 
								 | 
							
								    shdefault(strmap, -1);
							 | 
						||
| 
								 | 
							
								    for (i=0; i < testsize; i+=2)
							 | 
						||
| 
								 | 
							
								      shput(strmap, strkey(i), i*3);
							 | 
						||
| 
								 | 
							
								    for (i=0; i < testsize; i+=1)
							 | 
						||
| 
								 | 
							
								      if (i & 1) STBDS_ASSERT(shget(strmap, strkey(i)) == -1 );
							 | 
						||
| 
								 | 
							
								      else       STBDS_ASSERT(shget(strmap, strkey(i)) == i*3);
							 | 
						||
| 
								 | 
							
								    for (i=2; i < testsize; i+=4)
							 | 
						||
| 
								 | 
							
								      shdel(strmap, strkey(i)); // delete half the entries
							 | 
						||
| 
								 | 
							
								    for (i=0; i < testsize; i+=1)
							 | 
						||
| 
								 | 
							
								      if (i & 3) STBDS_ASSERT(shget(strmap, strkey(i)) == -1 );
							 | 
						||
| 
								 | 
							
								      else       STBDS_ASSERT(shget(strmap, strkey(i)) == i*3);
							 | 
						||
| 
								 | 
							
								    for (i=0; i < testsize; i+=1)
							 | 
						||
| 
								 | 
							
								      shdel(strmap, strkey(i)); // delete the rest of the entries    
							 | 
						||
| 
								 | 
							
								    for (i=0; i < testsize; i+=1)
							 | 
						||
| 
								 | 
							
								      STBDS_ASSERT(shget(strmap, strkey(i)) == -1 );
							 | 
						||
| 
								 | 
							
								    shfree(strmap);
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  {
							 | 
						||
| 
								 | 
							
								    struct { char *key; char value; } *hash = NULL;
							 | 
						||
| 
								 | 
							
								    char name[4] = "jen";
							 | 
						||
| 
								 | 
							
								    shput(hash, "bob"   , 'h');
							 | 
						||
| 
								 | 
							
								    shput(hash, "sally" , 'e');
							 | 
						||
| 
								 | 
							
								    shput(hash, "fred"  , 'l');
							 | 
						||
| 
								 | 
							
								    shput(hash, "jen"   , 'x');
							 | 
						||
| 
								 | 
							
								    shput(hash, "doug"  , 'o');
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    shput(hash, name    , 'l');
							 | 
						||
| 
								 | 
							
								    shfree(hash);
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  for (i=0; i < testsize; i += 2) {
							 | 
						||
| 
								 | 
							
								    stbds_struct s = { i,i*2,i*3,i*4 };
							 | 
						||
| 
								 | 
							
								    hmput(map, s, i*5);
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  for (i=0; i < testsize; i += 1) {
							 | 
						||
| 
								 | 
							
								    stbds_struct s = { i,i*2,i*3  ,i*4 };
							 | 
						||
| 
								 | 
							
								    stbds_struct t = { i,i*2,i*3+1,i*4 };
							 | 
						||
| 
								 | 
							
								    if (i & 1) STBDS_ASSERT(hmget(map, s) == 0);
							 | 
						||
| 
								 | 
							
								    else       STBDS_ASSERT(hmget(map, s) == i*5);
							 | 
						||
| 
								 | 
							
								    STBDS_ASSERT(hmget(map, t) == 0);
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  for (i=0; i < testsize; i += 2) {
							 | 
						||
| 
								 | 
							
								    stbds_struct s = { i,i*2,i*3,i*4 };
							 | 
						||
| 
								 | 
							
								    hmputs(map2, s);
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  hmfree(map);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  for (i=0; i < testsize; i += 1) {
							 | 
						||
| 
								 | 
							
								    stbds_struct s = { i,i*2,i*3,i*4 };
							 | 
						||
| 
								 | 
							
								    stbds_struct t = { i,i*2,i*3+1,i*4 };
							 | 
						||
| 
								 | 
							
								    if (i & 1) STBDS_ASSERT(hmgets(map2, s.key).d == 0);
							 | 
						||
| 
								 | 
							
								    else       STBDS_ASSERT(hmgets(map2, s.key).d == i*4);
							 | 
						||
| 
								 | 
							
								    STBDS_ASSERT(hmget(map, t) == 0);
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  hmfree(map2);
							 | 
						||
| 
								 | 
							
								#endif
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								#endif
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/*
							 | 
						||
| 
								 | 
							
								------------------------------------------------------------------------------
							 | 
						||
| 
								 | 
							
								This software is available under 2 licenses -- choose whichever you prefer.
							 | 
						||
| 
								 | 
							
								------------------------------------------------------------------------------
							 | 
						||
| 
								 | 
							
								ALTERNATIVE A - MIT License
							 | 
						||
| 
								 | 
							
								Copyright (c) 2019 Sean Barrett
							 | 
						||
| 
								 | 
							
								Permission is hereby granted, free of charge, to any person obtaining a copy of
							 | 
						||
| 
								 | 
							
								this software and associated documentation files (the "Software"), to deal in
							 | 
						||
| 
								 | 
							
								the Software without restriction, including without limitation the rights to
							 | 
						||
| 
								 | 
							
								use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
							 | 
						||
| 
								 | 
							
								of the Software, and to permit persons to whom the Software is furnished to do
							 | 
						||
| 
								 | 
							
								so, subject to the following conditions:
							 | 
						||
| 
								 | 
							
								The above copyright notice and this permission notice shall be included in all
							 | 
						||
| 
								 | 
							
								copies or substantial portions of the Software.
							 | 
						||
| 
								 | 
							
								THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
							 | 
						||
| 
								 | 
							
								IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
							 | 
						||
| 
								 | 
							
								FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
							 | 
						||
| 
								 | 
							
								AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
							 | 
						||
| 
								 | 
							
								LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
							 | 
						||
| 
								 | 
							
								OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
							 | 
						||
| 
								 | 
							
								SOFTWARE.
							 | 
						||
| 
								 | 
							
								------------------------------------------------------------------------------
							 | 
						||
| 
								 | 
							
								ALTERNATIVE B - Public Domain (www.unlicense.org)
							 | 
						||
| 
								 | 
							
								This is free and unencumbered software released into the public domain.
							 | 
						||
| 
								 | 
							
								Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
							 | 
						||
| 
								 | 
							
								software, either in source code form or as a compiled binary, for any purpose,
							 | 
						||
| 
								 | 
							
								commercial or non-commercial, and by any means.
							 | 
						||
| 
								 | 
							
								In jurisdictions that recognize copyright laws, the author or authors of this
							 | 
						||
| 
								 | 
							
								software dedicate any and all copyright interest in the software to the public
							 | 
						||
| 
								 | 
							
								domain. We make this dedication for the benefit of the public at large and to
							 | 
						||
| 
								 | 
							
								the detriment of our heirs and successors. We intend this dedication to be an
							 | 
						||
| 
								 | 
							
								overt act of relinquishment in perpetuity of all present and future rights to
							 | 
						||
| 
								 | 
							
								this software under copyright law.
							 | 
						||
| 
								 | 
							
								THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
							 | 
						||
| 
								 | 
							
								IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
							 | 
						||
| 
								 | 
							
								FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
							 | 
						||
| 
								 | 
							
								AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
							 | 
						||
| 
								 | 
							
								ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
							 | 
						||
| 
								 | 
							
								WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
							 | 
						||
| 
								 | 
							
								------------------------------------------------------------------------------
							 | 
						||
| 
								 | 
							
								*/
							 |