|
|
|
@ -1,4 +1,5 @@ |
|
|
|
|
/* stb_ds.h - v0.62 - public domain data structures - Sean Barrett 2019
|
|
|
|
|
#include <stdio.h> |
|
|
|
|
/* stb_ds.h - v0.62b - 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++). |
|
|
|
@ -108,6 +109,11 @@ DOCUMENTATION |
|
|
|
|
Inserts n uninitialized items into array a starting at a[p], |
|
|
|
|
moving the rest of the array over. |
|
|
|
|
|
|
|
|
|
arraddn: |
|
|
|
|
T* arraddn(T* a, int n) |
|
|
|
|
Appends n uninitialized items onto array at the end. |
|
|
|
|
Returns a pointer to the first uninitialized item added. |
|
|
|
|
|
|
|
|
|
arrdel: |
|
|
|
|
void arrdel(T* a, int p); |
|
|
|
|
Deletes the element at a[p], moving the rest of the array over. |
|
|
|
@ -191,15 +197,19 @@ DOCUMENTATION |
|
|
|
|
|
|
|
|
|
hmgeti |
|
|
|
|
shgeti |
|
|
|
|
hmgeti_ts |
|
|
|
|
ptrdiff_t hmgeti(T*, TK key) |
|
|
|
|
ptrdiff_t shgeti(T*, char* key) |
|
|
|
|
ptrdiff_t hmgeti_ts(T*, TK key, ptrdiff_t tempvar) |
|
|
|
|
Returns the index in the hashmap which has the key 'key', or -1 |
|
|
|
|
if the key is not present. |
|
|
|
|
|
|
|
|
|
hmget |
|
|
|
|
hmget_ts |
|
|
|
|
shget |
|
|
|
|
TV hmget(T*, TK key) |
|
|
|
|
TV shget(T*, char* key) |
|
|
|
|
TV hmget_ts(T*, TK key, ptrdiff_t tempvar) |
|
|
|
|
Returns the value corresponding to 'key' in the hashmap. |
|
|
|
|
The structure must have a 'value' field |
|
|
|
|
|
|
|
|
@ -209,6 +219,21 @@ DOCUMENTATION |
|
|
|
|
T shgets(T*, char* key) |
|
|
|
|
Returns the structure corresponding to 'key' in the hashmap. |
|
|
|
|
|
|
|
|
|
hmgetp |
|
|
|
|
shgetp |
|
|
|
|
hmgetp_ts |
|
|
|
|
hmgetp_null |
|
|
|
|
shgetp_null |
|
|
|
|
T* hmgetp(T*, TK key) |
|
|
|
|
T* shgetp(T*, char* key) |
|
|
|
|
T* hmgetp_ts(T*, TK key, ptrdiff_t tempvar) |
|
|
|
|
T* hmgetp_null(T*, TK key) |
|
|
|
|
T* shgetp_null(T*, char *key)
|
|
|
|
|
Returns a pointer to the structure corresponding to 'key' in |
|
|
|
|
the hashmap. Functions ending in "_null" return NULL if the key |
|
|
|
|
is not present in the hashmap; the others return a pointer to a |
|
|
|
|
structure holding the default value (but not the searched-for key). |
|
|
|
|
|
|
|
|
|
hmdefault |
|
|
|
|
shdefault |
|
|
|
|
TV hmdefault(T*, TV value) |
|
|
|
@ -234,7 +259,7 @@ DOCUMENTATION |
|
|
|
|
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 |
|
|
|
|
Inserts a struct with T.key into the hashmap. If the struct is already |
|
|
|
|
present in the hashmap, updates it. |
|
|
|
|
|
|
|
|
|
hmdel |
|
|
|
@ -263,12 +288,22 @@ DOCUMENTATION |
|
|
|
|
|
|
|
|
|
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. |
|
|
|
|
* These data structures are realloc'd when they grow, and the macro |
|
|
|
|
"functions" write to the provided pointer. This means: (a) the pointer |
|
|
|
|
must be an lvalue, and (b) the pointer to the data structure is not |
|
|
|
|
stable, and you must maintain it the same as you would a realloc'd |
|
|
|
|
pointer. For example, if you pass a pointer to a dynamic array to a |
|
|
|
|
function which updates it, the function must return back the new |
|
|
|
|
pointer to the caller. This is the price of trying to do this in C. |
|
|
|
|
|
|
|
|
|
* The following are the only functions that are thread-safe on a single data |
|
|
|
|
structure, i.e. can be run in multiple threads simultaneously on the same |
|
|
|
|
data structure |
|
|
|
|
hmlen shlen |
|
|
|
|
hmlenu shlenu |
|
|
|
|
hmget_ts shget_ts |
|
|
|
|
hmgeti_ts shgeti_ts |
|
|
|
|
hmgets_ts shgets_ts |
|
|
|
|
|
|
|
|
|
* You iterate over the contents of a dynamic array and a hashmap in exactly |
|
|
|
|
the same way, using arrlen/hmlen/shlen: |
|
|
|
@ -298,7 +333,8 @@ 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. |
|
|
|
|
in GCC or clang, or if you're using C++ in GCC. But note that this can make your |
|
|
|
|
code less portable. |
|
|
|
|
|
|
|
|
|
* To test for presence of a key in a hashmap, just do 'hmgeti(foo,key) >= 0'. |
|
|
|
|
|
|
|
|
@ -368,9 +404,13 @@ CREDITS |
|
|
|
|
#define hmput stbds_hmput |
|
|
|
|
#define hmputs stbds_hmputs |
|
|
|
|
#define hmget stbds_hmget |
|
|
|
|
#define hmget_ts stbds_hmget_ts |
|
|
|
|
#define hmgets stbds_hmgets |
|
|
|
|
#define hmgetp stbds_hmgetp |
|
|
|
|
#define hmgetp_ts stbds_hmgetp_ts |
|
|
|
|
#define hmgetp_null stbds_hmgetp_null |
|
|
|
|
#define hmgeti stbds_hmgeti |
|
|
|
|
#define hmgeti_ts stbds_hmgeti_ts |
|
|
|
|
#define hmdel stbds_hmdel |
|
|
|
|
#define hmlen stbds_hmlen |
|
|
|
|
#define hmlenu stbds_hmlenu |
|
|
|
@ -379,11 +419,13 @@ CREDITS |
|
|
|
|
#define hmdefaults stbds_hmdefaults |
|
|
|
|
|
|
|
|
|
#define shput stbds_shput |
|
|
|
|
#define shputi stbds_shputi |
|
|
|
|
#define shputs stbds_shputs |
|
|
|
|
#define shget stbds_shget |
|
|
|
|
#define shgeti stbds_shgeti |
|
|
|
|
#define shgets stbds_shgets |
|
|
|
|
#define shgetp stbds_shgetp |
|
|
|
|
#define shgeti stbds_shgeti |
|
|
|
|
#define shgetp_null stbds_shgetp_null |
|
|
|
|
#define shdel stbds_shdel |
|
|
|
|
#define shlen stbds_shlen |
|
|
|
|
#define shlenu stbds_shlenu |
|
|
|
@ -431,8 +473,9 @@ extern void stbds_unit_tests(void); |
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
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_hmfree_func(void *p, size_t elemsize); |
|
|
|
|
extern void * stbds_hmget_key(void *a, size_t elemsize, void *key, size_t keysize, int mode); |
|
|
|
|
extern void * stbds_hmget_key_ts(void *a, size_t elemsize, void *key, size_t keysize, ptrdiff_t *temp, int mode); |
|
|
|
|
extern void * stbds_hmput_default(void *a, size_t elemsize); |
|
|
|
|
extern void * stbds_hmput_key(void *a, size_t elemsize, void *key, size_t keysize, int mode); |
|
|
|
|
extern void * stbds_hmdel_key(void *a, size_t elemsize, void *key, size_t keysize, size_t keyoffset, int mode); |
|
|
|
@ -471,15 +514,15 @@ extern void * stbds_shmode_func(size_t elemsize, int mode); |
|
|
|
|
#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_arrsetcap(a,n) (stbds_arrgrow(a,0,n)) |
|
|
|
|
#define stbds_arrsetlen(a,n) ((stbds_arrcap(a) < (size_t) (n) ? stbds_arrsetcap((a),(size_t)(n)),0 : 0), (a) ? stbds_header(a)->length = (size_t) (n) : 0) |
|
|
|
|
#define stbds_arrcap(a) ((a) ? stbds_header(a)->capacity : 0) |
|
|
|
|
#define stbds_arrlen(a) ((a) ? (ptrdiff_t) stbds_header(a)->length : 0) |
|
|
|
|
#define stbds_arrlenu(a) ((a) ? stbds_header(a)->length : 0) |
|
|
|
|
#define stbds_arrput(a,v) (stbds_arrmaybegrow(a,1), (a)[stbds_header(a)->length++] = (v)) |
|
|
|
|
#define stbds_arrpush stbds_arrput // synonym
|
|
|
|
|
#define stbds_arrpop(a) (stbds_header(a)->length--, (a)[stbds_header(a)->length]) |
|
|
|
|
#define stbds_arraddn(a,n) (stbds_arrmaybegrow(a,n), stbds_header(a)->length += (n)) |
|
|
|
|
#define stbds_arraddn(a,n) (stbds_arrmaybegrow(a,n), stbds_header(a)->length += (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) |
|
|
|
@ -495,7 +538,7 @@ extern void * stbds_shmode_func(size_t elemsize, int mode); |
|
|
|
|
|
|
|
|
|
#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)].key = (k), \
|
|
|
|
|
(t)[stbds_temp((t)-1)].value = (v)) |
|
|
|
|
|
|
|
|
|
#define stbds_hmputs(t, s) \ |
|
|
|
@ -506,9 +549,16 @@ extern void * stbds_shmode_func(size_t elemsize, int mode); |
|
|
|
|
((t) = stbds_hmget_key_wrapper((t), sizeof *(t), (void*) STBDS_ADDRESSOF((t)->key, (k)), sizeof (t)->key, STBDS_HM_BINARY), \
|
|
|
|
|
stbds_temp((t)-1)) |
|
|
|
|
|
|
|
|
|
#define stbds_hmgeti_ts(t,k,temp) \ |
|
|
|
|
((t) = stbds_hmget_key_ts_wrapper((t), sizeof *(t), (void*) STBDS_ADDRESSOF((t)->key, (k)), sizeof (t)->key, &(temp), STBDS_HM_BINARY), \
|
|
|
|
|
(temp)) |
|
|
|
|
|
|
|
|
|
#define stbds_hmgetp(t, k) \ |
|
|
|
|
((void) stbds_hmgeti(t,k), &(t)[stbds_temp((t)-1)]) |
|
|
|
|
|
|
|
|
|
#define stbds_hmgetp_ts(t, k, temp) \ |
|
|
|
|
((void) stbds_hmgeti_ts(t,k,temp), &(t)[temp]) |
|
|
|
|
|
|
|
|
|
#define stbds_hmdel(t,k) \ |
|
|
|
|
(((t) = stbds_hmdel_key_wrapper((t),sizeof *(t), (void*) STBDS_ADDRESSOF((t)->key, (k)), sizeof (t)->key, STBDS_OFFSETOF((t),key), STBDS_HM_BINARY)),(t)?stbds_temp((t)-1):0) |
|
|
|
|
|
|
|
|
@ -519,37 +569,56 @@ extern void * stbds_shmode_func(size_t elemsize, int mode); |
|
|
|
|
((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) |
|
|
|
|
((void) ((p) != NULL ? stbds_hmfree_func((p)-1,sizeof*(p)),0 : 0),(p)=NULL) |
|
|
|
|
|
|
|
|
|
#define stbds_hmgets(t, k) (*stbds_hmgetp(t,k)) |
|
|
|
|
#define stbds_hmget(t, k) (stbds_hmgetp(t,k)->value) |
|
|
|
|
#define stbds_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_hmgets(t, k) (*stbds_hmgetp(t,k)) |
|
|
|
|
#define stbds_hmget(t, k) (stbds_hmgetp(t,k)->value) |
|
|
|
|
#define stbds_hmget_ts(t, k, temp) (stbds_hmgetp_ts(t,k,temp)->value) |
|
|
|
|
#define stbds_hmlen(t) ((t) ? (ptrdiff_t) stbds_header((t)-1)->length-1 : 0) |
|
|
|
|
#define stbds_hmlenu(t) ((t) ? stbds_header((t)-1)->length-1 : 0) |
|
|
|
|
#define stbds_hmgetp_null(t,k) (stbds_hmgeti(t,k) == -1 ? NULL : &(t)[stbds_temp(t)-1]) |
|
|
|
|
|
|
|
|
|
#define stbds_shput(t, k, v) \ |
|
|
|
|
((t) = stbds_hmput_key_wrapper((t), sizeof *(t), (void*) (k), sizeof (t)->key, STBDS_HM_STRING), \
|
|
|
|
|
(t)[stbds_temp((t)-1)].value = (v)) |
|
|
|
|
|
|
|
|
|
#define stbds_shputi(t, k, v) \ |
|
|
|
|
((t) = stbds_hmput_key_wrapper((t), sizeof *(t), (void*) (k), sizeof (t)->key, STBDS_HM_STRING), \
|
|
|
|
|
(t)[stbds_temp((t)-1)].value = (v), stbds_temp((t)-1)) |
|
|
|
|
|
|
|
|
|
#define stbds_shputs(t, s) \ |
|
|
|
|
((t) = stbds_hmput_key_wrapper((t), sizeof *(t), (void*) (s).key, sizeof (s).key, STBDS_HM_STRING), \
|
|
|
|
|
(t)[stbds_temp((t)-1)] = (s)) |
|
|
|
|
|
|
|
|
|
#define stbds_pshput(t, p) \ |
|
|
|
|
((t) = stbds_hmput_key_wrapper((t), sizeof *(t), (void*) (p)->key, sizeof (p)->key, STBDS_HM_PTR_TO_STRING), \
|
|
|
|
|
(t)[stbds_temp((t)-1)] = (p)) |
|
|
|
|
|
|
|
|
|
#define stbds_shgeti(t,k) \ |
|
|
|
|
((t) = stbds_hmget_key_wrapper((t), sizeof *(t), (void*) (k), sizeof (t)->key, STBDS_HM_STRING), \
|
|
|
|
|
stbds_temp((t)-1)) |
|
|
|
|
|
|
|
|
|
#define stbds_pshgeti(t,k) \ |
|
|
|
|
((t) = stbds_hmget_key_wrapper((t), sizeof *(t), (void*) (k), sizeof (*(t))->key, STBDS_HM_PTR_TO_STRING), \
|
|
|
|
|
stbds_temp((t)-1)) |
|
|
|
|
|
|
|
|
|
#define stbds_shgetp(t, k) \ |
|
|
|
|
((void) stbds_shgeti(t,k), &(t)[stbds_temp((t)-1)]) |
|
|
|
|
|
|
|
|
|
#define stbds_pshget(t, k) \ |
|
|
|
|
((void) stbds_pshgeti(t,k), (t)[stbds_temp((t)-1)]) |
|
|
|
|
|
|
|
|
|
#define stbds_shdel(t,k) \ |
|
|
|
|
(((t) = stbds_hmdel_key_wrapper((t),sizeof *(t), (void*) (k), sizeof (t)->key, STBDS_OFFSETOF((t),key), STBDS_HM_STRING)),(t)?stbds_temp((t)-1):0) |
|
|
|
|
#define stbds_pshdel(t,k) \ |
|
|
|
|
(((t) = stbds_hmdel_key_wrapper((t),sizeof *(t), (void*) (k), sizeof (*(t))->key, STBDS_OFFSETOF(*(t),key), STBDS_HM_PTR_TO_STRING)),(t)?stbds_temp((t)-1):0) |
|
|
|
|
|
|
|
|
|
#define stbds_sh_new_arena(t) \ |
|
|
|
|
((t) = stbds_shmode_func_wrapper(t, sizeof *(t), STBDS_SH_ARENA)) |
|
|
|
|
#define stbds_sh_new_strdup(t) \ |
|
|
|
|
((t) = stbds_shmode_func_wrapper(t, sizeof *(t), STBDS_SH_STRDUP)) |
|
|
|
|
|
|
|
|
|
#define stbds_shdefault(t, v) stbds_hmdefault(t,v) |
|
|
|
|
#define stbds_shdefault(t, v) stbds_hmdefault(t,v) |
|
|
|
|
#define stbds_shdefaults(t, s) stbds_hmdefaults(t,s) |
|
|
|
|
|
|
|
|
|
#define stbds_shfree stbds_hmfree |
|
|
|
@ -557,6 +626,7 @@ extern void * stbds_shmode_func(size_t elemsize, int mode); |
|
|
|
|
|
|
|
|
|
#define stbds_shgets(t, k) (*stbds_shgetp(t,k)) |
|
|
|
|
#define stbds_shget(t, k) (stbds_shgetp(t,k)->value) |
|
|
|
|
#define stbds_shgetp_null(t,k) (stbds_shgeti(t,k) == -1 ? NULL : &(t)[stbds_temp(t)-1]) |
|
|
|
|
#define stbds_shlen stbds_hmlen |
|
|
|
|
|
|
|
|
|
typedef struct |
|
|
|
@ -581,12 +651,13 @@ struct stbds_string_arena |
|
|
|
|
unsigned char mode; // this isn't used by the string arena itself
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
#define STBDS_HM_BINARY 0 |
|
|
|
|
#define STBDS_HM_STRING 1 |
|
|
|
|
#define STBDS_HM_BINARY 0 |
|
|
|
|
#define STBDS_HM_STRING 1 |
|
|
|
|
|
|
|
|
|
enum |
|
|
|
|
{ |
|
|
|
|
STBDS_SH_NONE, |
|
|
|
|
STBDS_SH_DEFAULT, |
|
|
|
|
STBDS_SH_STRDUP, |
|
|
|
|
STBDS_SH_ARENA |
|
|
|
|
}; |
|
|
|
@ -600,6 +671,9 @@ template<class T> static T * stbds_arrgrowf_wrapper(T *a, size_t elemsize, size_ |
|
|
|
|
template<class T> static T * stbds_hmget_key_wrapper(T *a, size_t elemsize, void *key, size_t keysize, int mode) { |
|
|
|
|
return (T*)stbds_hmget_key((void*)a, elemsize, key, keysize, mode); |
|
|
|
|
} |
|
|
|
|
template<class T> static T * stbds_hmget_key_ts_wrapper(T *a, size_t elemsize, void *key, size_t keysize, ptrdiff_t *temp, int mode) { |
|
|
|
|
return (T*)stbds_hmget_key((void*)a, elemsize, key, keysize, temp, mode); |
|
|
|
|
} |
|
|
|
|
template<class T> static T * stbds_hmput_default_wrapper(T *a, size_t elemsize) { |
|
|
|
|
return (T*)stbds_hmput_default((void *)a, elemsize); |
|
|
|
|
} |
|
|
|
@ -615,6 +689,7 @@ template<class T> static T * stbds_shmode_func_wrapper(T *, size_t elemsize, int |
|
|
|
|
#else |
|
|
|
|
#define stbds_arrgrowf_wrapper stbds_arrgrowf |
|
|
|
|
#define stbds_hmget_key_wrapper stbds_hmget_key |
|
|
|
|
#define stbds_hmget_key_ts_wrapper stbds_hmget_key_ts |
|
|
|
|
#define stbds_hmput_default_wrapper stbds_hmput_default |
|
|
|
|
#define stbds_hmput_key_wrapper stbds_hmput_key |
|
|
|
|
#define stbds_hmdel_key_wrapper stbds_hmdel_key |
|
|
|
@ -656,6 +731,9 @@ size_t stbds_rehash_items; |
|
|
|
|
// stbds_arr implementation
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
//int *prev_allocs[65536];
|
|
|
|
|
//int num_prev;
|
|
|
|
|
|
|
|
|
|
void *stbds_arrgrowf(void *a, size_t elemsize, size_t addlen, size_t min_cap) |
|
|
|
|
{ |
|
|
|
|
void *b; |
|
|
|
@ -674,7 +752,11 @@ void *stbds_arrgrowf(void *a, size_t elemsize, size_t addlen, size_t min_cap) |
|
|
|
|
else if (min_cap < 4) |
|
|
|
|
min_cap = 4; |
|
|
|
|
|
|
|
|
|
//if (num_prev < 65536) if (a) prev_allocs[num_prev++] = (int *) ((char *) a+1);
|
|
|
|
|
//if (num_prev == 2201)
|
|
|
|
|
// num_prev = num_prev;
|
|
|
|
|
b = STBDS_REALLOC(NULL, (a) ? stbds_header(a) : 0, elemsize * min_cap + sizeof(stbds_array_header)); |
|
|
|
|
//if (num_prev < 65536) prev_allocs[num_prev++] = (int *) (char *) b;
|
|
|
|
|
b = (char *) b + sizeof(stbds_array_header); |
|
|
|
|
if (a == NULL) { |
|
|
|
|
stbds_header(b)->length = 0; |
|
|
|
@ -683,6 +765,7 @@ void *stbds_arrgrowf(void *a, size_t elemsize, size_t addlen, size_t min_cap) |
|
|
|
|
STBDS_STATS(++stbds_array_grow); |
|
|
|
|
} |
|
|
|
|
stbds_header(b)->capacity = min_cap; |
|
|
|
|
|
|
|
|
|
return b; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -925,6 +1008,11 @@ typedef int STBDS_SIPHASH_2_4_can_only_be_used_in_64_bit_builds[sizeof(size_t) = |
|
|
|
|
#define STBDS_SIPHASH_D_ROUNDS 1 |
|
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
#ifdef _MSC_VER |
|
|
|
|
#pragma warning(push) |
|
|
|
|
#pragma warning(disable:4127) // conditional expression is constant, for do..while(0) and sizeof()==
|
|
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
static size_t stbds_siphash_bytes(void *p, size_t len, size_t seed) |
|
|
|
|
{ |
|
|
|
|
unsigned char *d = (unsigned char *) p; |
|
|
|
@ -982,6 +1070,7 @@ static size_t stbds_siphash_bytes(void *p, size_t len, size_t seed) |
|
|
|
|
v2 ^= 0xff; |
|
|
|
|
for (j=0; j < STBDS_SIPHASH_D_ROUNDS; ++j) |
|
|
|
|
STBDS_SIPROUND(); |
|
|
|
|
|
|
|
|
|
#ifdef STBDS_SIPHASH_2_4 |
|
|
|
|
return v0^v1^v2^v3; |
|
|
|
|
#else |
|
|
|
@ -1071,8 +1160,12 @@ size_t stbds_hash_bytes(void *p, size_t len, size_t seed) |
|
|
|
|
} |
|
|
|
|
#endif |
|
|
|
|
} |
|
|
|
|
#ifdef _MSC_VER |
|
|
|
|
#pragma warning(pop) |
|
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static int stbds_is_key_equal(void *a, size_t elemsize, void *key, size_t keysize, int mode, size_t i) |
|
|
|
|
static int stbds_is_key_equal(void *a, size_t elemsize, void *key, size_t keysize, size_t keyoffset, int mode, size_t i) |
|
|
|
|
{ |
|
|
|
|
if (mode >= STBDS_HM_STRING) |
|
|
|
|
return 0==strcmp((char *) key, * (char **) ((char *) a + elemsize*i)); |
|
|
|
@ -1085,23 +1178,23 @@ static int stbds_is_key_equal(void *a, size_t elemsize, void *key, size_t keysiz |
|
|
|
|
|
|
|
|
|
#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) |
|
|
|
|
void stbds_hmfree_func(void *a, size_t elemsize) |
|
|
|
|
{ |
|
|
|
|
if (a == NULL) return; |
|
|
|
|
if (stbds_hash_table(a) != NULL) { |
|
|
|
|
if (stbds_hash_table(a)->string.mode == STBDS_SH_STRDUP) { |
|
|
|
|
size_t i; |
|
|
|
|
// skip 0th element, which is default
|
|
|
|
|
for (i=1; i < stbds_header(a)->length; ++i) |
|
|
|
|
STBDS_FREE(NULL, *(char**) ((char *) a + elemsize*i)); |
|
|
|
|
} |
|
|
|
|
stbds_strreset(&stbds_hash_table(a)->string); |
|
|
|
|
} |
|
|
|
|
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) |
|
|
|
|
static ptrdiff_t stbds_hm_find_slot(void *a, size_t elemsize, void *key, size_t keysize, size_t keyoffset, int mode) |
|
|
|
|
{ |
|
|
|
|
void *raw_a = STBDS_HASH_TO_ARR(a,elemsize); |
|
|
|
|
stbds_hash_index *table = stbds_hash_table(raw_a); |
|
|
|
@ -1122,7 +1215,7 @@ static ptrdiff_t stbds_hm_find_slot(void *a, size_t elemsize, void *key, size_t |
|
|
|
|
// 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])) { |
|
|
|
|
if (stbds_is_key_equal(a, elemsize, key, keysize, keyoffset, mode, bucket->index[i])) { |
|
|
|
|
return (pos & ~STBDS_BUCKET_MASK)+i; |
|
|
|
|
} |
|
|
|
|
} else if (bucket->hash[i] == STBDS_HASH_EMPTY) { |
|
|
|
@ -1134,7 +1227,7 @@ static ptrdiff_t stbds_hm_find_slot(void *a, size_t elemsize, void *key, size_t |
|
|
|
|
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])) { |
|
|
|
|
if (stbds_is_key_equal(a, elemsize, key, keysize, keyoffset, mode, bucket->index[i])) { |
|
|
|
|
return (pos & ~STBDS_BUCKET_MASK)+i; |
|
|
|
|
} |
|
|
|
|
} else if (bucket->hash[i] == STBDS_HASH_EMPTY) { |
|
|
|
@ -1148,17 +1241,17 @@ static ptrdiff_t stbds_hm_find_slot(void *a, size_t elemsize, void *key, size_t |
|
|
|
|
pos &= (table->slot_count-1); |
|
|
|
|
} |
|
|
|
|
/* NOTREACHED */ |
|
|
|
|
return -1; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void * stbds_hmget_key(void *a, size_t elemsize, void *key, size_t keysize, int mode) |
|
|
|
|
void * stbds_hmget_key_ts(void *a, size_t elemsize, void *key, size_t keysize, ptrdiff_t *temp, int mode) |
|
|
|
|
{ |
|
|
|
|
size_t keyoffset = 0; |
|
|
|
|
if (a == NULL) { |
|
|
|
|
// make it non-empty so we can return a temp
|
|
|
|
|
a = stbds_arrgrowf(0, elemsize, 0, 1); |
|
|
|
|
stbds_header(a)->length += 1; |
|
|
|
|
memset(a, 0, elemsize); |
|
|
|
|
stbds_temp(a) = STBDS_INDEX_EMPTY; |
|
|
|
|
*temp = STBDS_INDEX_EMPTY; |
|
|
|
|
// adjust a to point after the default element
|
|
|
|
|
return STBDS_ARR_TO_HASH(a,elemsize); |
|
|
|
|
} else { |
|
|
|
@ -1167,20 +1260,28 @@ void * stbds_hmget_key(void *a, size_t elemsize, void *key, size_t keysize, int |
|
|
|
|
// 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; |
|
|
|
|
*temp = -1; |
|
|
|
|
} else { |
|
|
|
|
ptrdiff_t slot = stbds_hm_find_slot(a, elemsize, key, keysize, mode); |
|
|
|
|
ptrdiff_t slot = stbds_hm_find_slot(a, elemsize, key, keysize, keyoffset, mode); |
|
|
|
|
if (slot < 0) { |
|
|
|
|
stbds_temp(raw_a) = STBDS_INDEX_EMPTY; |
|
|
|
|
*temp = STBDS_INDEX_EMPTY; |
|
|
|
|
} else { |
|
|
|
|
stbds_hash_bucket *b = &table->storage[slot >> STBDS_BUCKET_SHIFT]; |
|
|
|
|
stbds_temp(raw_a) = b->index[slot & STBDS_BUCKET_MASK]; |
|
|
|
|
*temp = b->index[slot & STBDS_BUCKET_MASK]; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
return a; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void * stbds_hmget_key(void *a, size_t elemsize, void *key, size_t keysize, int mode) |
|
|
|
|
{ |
|
|
|
|
ptrdiff_t temp; |
|
|
|
|
void *p = stbds_hmget_key_ts(a, elemsize, key, keysize, &temp, mode); |
|
|
|
|
stbds_temp(STBDS_HASH_TO_ARR(p,elemsize)) = temp; |
|
|
|
|
return p; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void * stbds_hmput_default(void *a, size_t elemsize) |
|
|
|
|
{ |
|
|
|
|
// three cases:
|
|
|
|
@ -1200,6 +1301,7 @@ static char *stbds_strdup(char *str); |
|
|
|
|
|
|
|
|
|
void *stbds_hmput_key(void *a, size_t elemsize, void *key, size_t keysize, int mode) |
|
|
|
|
{ |
|
|
|
|
size_t keyoffset=0; |
|
|
|
|
void *raw_a; |
|
|
|
|
stbds_hash_index *table; |
|
|
|
|
|
|
|
|
@ -1223,9 +1325,10 @@ void *stbds_hmput_key(void *a, size_t elemsize, void *key, size_t keysize, int m |
|
|
|
|
|
|
|
|
|
slot_count = (table == NULL) ? STBDS_BUCKET_LENGTH : table->slot_count*2; |
|
|
|
|
nt = stbds_make_hash_index(slot_count, table); |
|
|
|
|
if (table) { |
|
|
|
|
if (table) |
|
|
|
|
STBDS_FREE(NULL, table); |
|
|
|
|
} |
|
|
|
|
else |
|
|
|
|
nt->string.mode = mode >= STBDS_HM_STRING ? STBDS_SH_DEFAULT : 0; |
|
|
|
|
stbds_header(a)->hash_table = table = nt; |
|
|
|
|
STBDS_STATS(++stbds_hash_grow); |
|
|
|
|
} |
|
|
|
@ -1251,7 +1354,7 @@ void *stbds_hmput_key(void *a, size_t elemsize, void *key, size_t keysize, int m |
|
|
|
|
// 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])) { |
|
|
|
|
if (stbds_is_key_equal(raw_a, elemsize, key, keysize, keyoffset, mode, bucket->index[i])) { |
|
|
|
|
stbds_temp(a) = bucket->index[i]; |
|
|
|
|
return STBDS_ARR_TO_HASH(a,elemsize); |
|
|
|
|
} |
|
|
|
@ -1268,7 +1371,7 @@ void *stbds_hmput_key(void *a, size_t elemsize, void *key, size_t keysize, int m |
|
|
|
|
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])) { |
|
|
|
|
if (stbds_is_key_equal(raw_a, elemsize, key, keysize, keyoffset, mode, bucket->index[i])) { |
|
|
|
|
stbds_temp(a) = bucket->index[i]; |
|
|
|
|
return STBDS_ARR_TO_HASH(a,elemsize); |
|
|
|
|
} |
|
|
|
@ -1295,7 +1398,7 @@ void *stbds_hmput_key(void *a, size_t elemsize, void *key, size_t keysize, int m |
|
|
|
|
|
|
|
|
|
{ |
|
|
|
|
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
|
|
|
|
|
// 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); |
|
|
|
@ -1308,9 +1411,10 @@ void *stbds_hmput_key(void *a, size_t elemsize, void *key, size_t keysize, int m |
|
|
|
|
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; |
|
|
|
|
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; |
|
|
|
|
case STBDS_SH_DEFAULT: *(char **) ((char *) a + elemsize*i) = (char *) key; break; |
|
|
|
|
default: memcpy((char *) a + elemsize*i, key, keysize); break; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
return STBDS_ARR_TO_HASH(a,elemsize); |
|
|
|
@ -1324,7 +1428,7 @@ void * stbds_shmode_func(size_t elemsize, int mode) |
|
|
|
|
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; |
|
|
|
|
h->string.mode = (unsigned char) mode; |
|
|
|
|
return STBDS_ARR_TO_HASH(a,elemsize); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -1341,7 +1445,7 @@ void * stbds_hmdel_key(void *a, size_t elemsize, void *key, size_t keysize, size |
|
|
|
|
return a; |
|
|
|
|
} else { |
|
|
|
|
ptrdiff_t slot; |
|
|
|
|
slot = stbds_hm_find_slot(a, elemsize, key, keysize, mode); |
|
|
|
|
slot = stbds_hm_find_slot(a, elemsize, key, keysize, keyoffset, mode); |
|
|
|
|
if (slot < 0) |
|
|
|
|
return a; |
|
|
|
|
else { |
|
|
|
@ -1368,9 +1472,9 @@ void * stbds_hmdel_key(void *a, size_t elemsize, void *key, size_t keysize, size |
|
|
|
|
|
|
|
|
|
// 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); |
|
|
|
|
slot = stbds_hm_find_slot(a, elemsize, *(char**) ((char *) a+elemsize*old_index + keyoffset), keysize, keyoffset, mode); |
|
|
|
|
else |
|
|
|
|
slot = stbds_hm_find_slot(a, elemsize, (char* ) a+elemsize*old_index + keyoffset, keysize, mode); |
|
|
|
|
slot = stbds_hm_find_slot(a, elemsize, (char* ) a+elemsize*old_index + keyoffset, keysize, keyoffset, mode); |
|
|
|
|
STBDS_ASSERT(slot >= 0); |
|
|
|
|
b = &table->storage[slot >> STBDS_BUCKET_SHIFT]; |
|
|
|
|
i = slot & STBDS_BUCKET_MASK; |
|
|
|
@ -1394,7 +1498,6 @@ void * stbds_hmdel_key(void *a, size_t elemsize, void *key, size_t keysize, size |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
/* NOTREACHED */ |
|
|
|
|
return 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static char *stbds_strdup(char *str) |
|
|
|
@ -1408,10 +1511,10 @@ static char *stbds_strdup(char *str) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#ifndef STBDS_STRING_ARENA_BLOCKSIZE_MIN |
|
|
|
|
#define STBDS_STRING_ARENA_BLOCKSIZE_MIN 512 |
|
|
|
|
#define STBDS_STRING_ARENA_BLOCKSIZE_MIN 512u |
|
|
|
|
#endif |
|
|
|
|
#ifndef STBDS_STRING_ARENA_BLOCKSIZE_MAX |
|
|
|
|
#define STBDS_STRING_ARENA_BLOCKSIZE_MAX 1<<20 |
|
|
|
|
#define STBDS_STRING_ARENA_BLOCKSIZE_MAX (1u<<20) |
|
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
char *stbds_stralloc(stbds_string_arena *a, char *str) |
|
|
|
@ -1492,6 +1595,7 @@ void stbds_strreset(stbds_string_arena *a) |
|
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
typedef struct { int key,b,c,d; } stbds_struct; |
|
|
|
|
typedef struct { int key[2],b,c,d; } stbds_struct2; |
|
|
|
|
|
|
|
|
|
static char buffer[256]; |
|
|
|
|
char *strkey(int n) |
|
|
|
@ -1511,12 +1615,16 @@ void stbds_unit_tests(void) |
|
|
|
|
STBDS_ASSERT(0); |
|
|
|
|
#else |
|
|
|
|
const int testsize = 100000; |
|
|
|
|
const int testsize2 = testsize/20; |
|
|
|
|
int *arr=NULL; |
|
|
|
|
struct { int key; int value; } *intmap = NULL; |
|
|
|
|
struct { char *key; int value; } *strmap = NULL; |
|
|
|
|
struct { stbds_struct key; int value; } *map = NULL; |
|
|
|
|
stbds_struct *map2 = NULL; |
|
|
|
|
stbds_string_arena sa = { 0 }; |
|
|
|
|
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_struct2 *map3 = NULL; |
|
|
|
|
stbds_string_arena sa = { 0 }; |
|
|
|
|
int key3[2] = { 1,2 }; |
|
|
|
|
ptrdiff_t temp; |
|
|
|
|
|
|
|
|
|
int i,j; |
|
|
|
|
|
|
|
|
@ -1552,9 +1660,12 @@ void stbds_unit_tests(void) |
|
|
|
|
STBDS_ASSERT(hmget (intmap, i) == -2); |
|
|
|
|
for (i=0; i < testsize; i+=2) |
|
|
|
|
hmput(intmap, i, i*5); |
|
|
|
|
for (i=0; i < testsize; i+=1) |
|
|
|
|
for (i=0; i < testsize; i+=1) { |
|
|
|
|
if (i & 1) STBDS_ASSERT(hmget(intmap, i) == -2 ); |
|
|
|
|
else STBDS_ASSERT(hmget(intmap, i) == i*5); |
|
|
|
|
if (i & 1) STBDS_ASSERT(hmget_ts(intmap, i, temp) == -2 ); |
|
|
|
|
else STBDS_ASSERT(hmget_ts(intmap, i, temp) == i*5); |
|
|
|
|
} |
|
|
|
|
for (i=0; i < testsize; i+=2) |
|
|
|
|
hmput(intmap, i, i*3); |
|
|
|
|
for (i=0; i < testsize; i+=1) |
|
|
|
@ -1639,7 +1750,9 @@ void stbds_unit_tests(void) |
|
|
|
|
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); |
|
|
|
|
if (i & 1) STBDS_ASSERT(hmget_ts(map, s, temp) == 0); |
|
|
|
|
else STBDS_ASSERT(hmget_ts(map, s, temp) == i*5); |
|
|
|
|
//STBDS_ASSERT(hmget(map, t.key) == 0);
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
for (i=0; i < testsize; i += 2) { |
|
|
|
@ -1653,9 +1766,21 @@ void stbds_unit_tests(void) |
|
|
|
|
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); |
|
|
|
|
//STBDS_ASSERT(hmgetp(map2, t.key) == 0);
|
|
|
|
|
} |
|
|
|
|
hmfree(map2); |
|
|
|
|
|
|
|
|
|
for (i=0; i < testsize; i += 2) { |
|
|
|
|
stbds_struct2 s = { { i,i*2 }, i*3,i*4, i*5 }; |
|
|
|
|
hmputs(map3, s); |
|
|
|
|
} |
|
|
|
|
for (i=0; i < testsize; i += 1) { |
|
|
|
|
stbds_struct2 s = { { i,i*2}, i*3, i*4, i*5 }; |
|
|
|
|
stbds_struct2 t = { { i,i*2}, i*3+1, i*4, i*5 }; |
|
|
|
|
if (i & 1) STBDS_ASSERT(hmgets(map3, s.key).d == 0); |
|
|
|
|
else STBDS_ASSERT(hmgets(map3, s.key).d == i*5); |
|
|
|
|
//STBDS_ASSERT(hmgetp(map3, t.key) == 0);
|
|
|
|
|
} |
|
|
|
|
#endif |
|
|
|
|
} |
|
|
|
|
#endif |
|
|
|
|