stb_ds.h: thread-safe functions; pointer-returning functions; change return value of arraddn

This commit is contained in:
Sean Barrett 2020-02-01 04:18:23 -08:00
parent aa482fc4a1
commit d693c6103a

257
stb_ds.h
View File

@ -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 This is a single-header-file library that provides easy-to-use
dynamic arrays and hash tables for C (also works in C++). 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], Inserts n uninitialized items into array a starting at a[p],
moving the rest of the array over. 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: arrdel:
void arrdel(T* a, int p); void arrdel(T* a, int p);
Deletes the element at a[p], moving the rest of the array over. Deletes the element at a[p], moving the rest of the array over.
@ -191,15 +197,19 @@ DOCUMENTATION
hmgeti hmgeti
shgeti shgeti
hmgeti_ts
ptrdiff_t hmgeti(T*, TK key) ptrdiff_t hmgeti(T*, TK key)
ptrdiff_t shgeti(T*, char* 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 Returns the index in the hashmap which has the key 'key', or -1
if the key is not present. if the key is not present.
hmget hmget
hmget_ts
shget shget
TV hmget(T*, TK key) TV hmget(T*, TK key)
TV shget(T*, char* key) TV shget(T*, char* key)
TV hmget_ts(T*, TK key, ptrdiff_t tempvar)
Returns the value corresponding to 'key' in the hashmap. Returns the value corresponding to 'key' in the hashmap.
The structure must have a 'value' field The structure must have a 'value' field
@ -209,6 +219,21 @@ DOCUMENTATION
T shgets(T*, char* key) T shgets(T*, char* key)
Returns the structure corresponding to 'key' in the hashmap. 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 hmdefault
shdefault shdefault
TV hmdefault(T*, TV value) TV hmdefault(T*, TV value)
@ -234,7 +259,7 @@ DOCUMENTATION
shputs shputs
T hmputs(T*, T item) T hmputs(T*, T item)
T shputs(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. present in the hashmap, updates it.
hmdel hmdel
@ -263,12 +288,22 @@ DOCUMENTATION
NOTES NOTES
* These data structures are realloc'd when they grow, and the macro "functions" * These data structures are realloc'd when they grow, and the macro
write to the provided pointer. This means: (a) the pointer must be an lvalue, "functions" write to the provided pointer. This means: (a) the pointer
and (b) the pointer to the data structure is not stable, and you must maintain must be an lvalue, and (b) the pointer to the data structure is not
it the same as you would a realloc'd pointer. For example, if you pass a pointer stable, and you must maintain it the same as you would a realloc'd
to a dynamic array to a function which updates it, the function must return pointer. For example, if you pass a pointer to a dynamic array to a
back the new pointer to the caller. This is the price of trying to do this in C. 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 * You iterate over the contents of a dynamic array and a hashmap in exactly
the same way, using arrlen/hmlen/shlen: 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 * 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). 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 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'. * 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 hmput stbds_hmput
#define hmputs stbds_hmputs #define hmputs stbds_hmputs
#define hmget stbds_hmget #define hmget stbds_hmget
#define hmget_ts stbds_hmget_ts
#define hmgets stbds_hmgets #define hmgets stbds_hmgets
#define hmgetp stbds_hmgetp #define hmgetp stbds_hmgetp
#define hmgetp_ts stbds_hmgetp_ts
#define hmgetp_null stbds_hmgetp_null
#define hmgeti stbds_hmgeti #define hmgeti stbds_hmgeti
#define hmgeti_ts stbds_hmgeti_ts
#define hmdel stbds_hmdel #define hmdel stbds_hmdel
#define hmlen stbds_hmlen #define hmlen stbds_hmlen
#define hmlenu stbds_hmlenu #define hmlenu stbds_hmlenu
@ -379,11 +419,13 @@ CREDITS
#define hmdefaults stbds_hmdefaults #define hmdefaults stbds_hmdefaults
#define shput stbds_shput #define shput stbds_shput
#define shputi stbds_shputi
#define shputs stbds_shputs #define shputs stbds_shputs
#define shget stbds_shget #define shget stbds_shget
#define shgeti stbds_shgeti
#define shgets stbds_shgets #define shgets stbds_shgets
#define shgetp stbds_shgetp #define shgetp stbds_shgetp
#define shgeti stbds_shgeti #define shgetp_null stbds_shgetp_null
#define shdel stbds_shdel #define shdel stbds_shdel
#define shlen stbds_shlen #define shlen stbds_shlen
#define shlenu stbds_shlenu #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_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(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_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_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_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_header(t) ((stbds_array_header *) (t) - 1)
#define stbds_temp(t) stbds_header(t)->temp #define stbds_temp(t) stbds_header(t)->temp
#define stbds_arrsetcap(a,n) (stbds_arrgrow(a,0,n)) #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_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_arrcap(a) ((a) ? stbds_header(a)->capacity : 0)
#define stbds_arrlen(a) ((a) ? (ptrdiff_t) stbds_header(a)->length : 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_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_arrput(a,v) (stbds_arrmaybegrow(a,1), (a)[stbds_header(a)->length++] = (v))
#define stbds_arrpush stbds_arrput // synonym #define stbds_arrpush stbds_arrput // synonym
#define stbds_arrpop(a) (stbds_header(a)->length--, (a)[stbds_header(a)->length]) #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_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_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_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) \ #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_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)) (t)[stbds_temp((t)-1)].value = (v))
#define stbds_hmputs(t, s) \ #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), \ ((t) = stbds_hmget_key_wrapper((t), sizeof *(t), (void*) STBDS_ADDRESSOF((t)->key, (k)), sizeof (t)->key, STBDS_HM_BINARY), \
stbds_temp((t)-1)) 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) \ #define stbds_hmgetp(t, k) \
((void) stbds_hmgeti(t,k), &(t)[stbds_temp((t)-1)]) ((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) \ #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) (((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)) ((t) = stbds_hmput_default_wrapper((t), sizeof *(t)), (t)[-1] = (s))
#define stbds_hmfree(p) \ #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_hmgets(t, k) (*stbds_hmgetp(t,k))
#define stbds_hmget(t, k) (stbds_hmgetp(t,k)->value) #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_hmget_ts(t, k, temp) (stbds_hmgetp_ts(t,k,temp)->value)
#define stbds_hmlenu(t) ((t) ? stbds_header((t)-1)->length-1 : 0) #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) \ #define stbds_shput(t, k, v) \
((t) = stbds_hmput_key_wrapper((t), sizeof *(t), (void*) (k), sizeof (t)->key, STBDS_HM_STRING), \ ((t) = stbds_hmput_key_wrapper((t), sizeof *(t), (void*) (k), sizeof (t)->key, STBDS_HM_STRING), \
(t)[stbds_temp((t)-1)].value = (v)) (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) \ #define stbds_shputs(t, s) \
((t) = stbds_hmput_key_wrapper((t), sizeof *(t), (void*) (s).key, sizeof (s).key, STBDS_HM_STRING), \ ((t) = stbds_hmput_key_wrapper((t), sizeof *(t), (void*) (s).key, sizeof (s).key, STBDS_HM_STRING), \
(t)[stbds_temp((t)-1)] = (s)) (t)[stbds_temp((t)-1)] = (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) \ #define stbds_shgeti(t,k) \
((t) = stbds_hmget_key_wrapper((t), sizeof *(t), (void*) (k), sizeof (t)->key, STBDS_HM_STRING), \ ((t) = stbds_hmget_key_wrapper((t), sizeof *(t), (void*) (k), sizeof (t)->key, STBDS_HM_STRING), \
stbds_temp((t)-1)) 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) \ #define stbds_shgetp(t, k) \
((void) stbds_shgeti(t,k), &(t)[stbds_temp((t)-1)]) ((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) \ #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) (((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) \ #define stbds_sh_new_arena(t) \
((t) = stbds_shmode_func_wrapper(t, sizeof *(t), STBDS_SH_ARENA)) ((t) = stbds_shmode_func_wrapper(t, sizeof *(t), STBDS_SH_ARENA))
#define stbds_sh_new_strdup(t) \ #define stbds_sh_new_strdup(t) \
((t) = stbds_shmode_func_wrapper(t, sizeof *(t), STBDS_SH_STRDUP)) ((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_shdefaults(t, s) stbds_hmdefaults(t,s)
#define stbds_shfree stbds_hmfree #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_shgets(t, k) (*stbds_shgetp(t,k))
#define stbds_shget(t, k) (stbds_shgetp(t,k)->value) #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 #define stbds_shlen stbds_hmlen
typedef struct typedef struct
@ -581,12 +651,13 @@ struct stbds_string_arena
unsigned char mode; // this isn't used by the string arena itself unsigned char mode; // this isn't used by the string arena itself
}; };
#define STBDS_HM_BINARY 0 #define STBDS_HM_BINARY 0
#define STBDS_HM_STRING 1 #define STBDS_HM_STRING 1
enum enum
{ {
STBDS_SH_NONE, STBDS_SH_NONE,
STBDS_SH_DEFAULT,
STBDS_SH_STRDUP, STBDS_SH_STRDUP,
STBDS_SH_ARENA 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) { 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); 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) { template<class T> static T * stbds_hmput_default_wrapper(T *a, size_t elemsize) {
return (T*)stbds_hmput_default((void *)a, 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 #else
#define stbds_arrgrowf_wrapper stbds_arrgrowf #define stbds_arrgrowf_wrapper stbds_arrgrowf
#define stbds_hmget_key_wrapper stbds_hmget_key #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_default_wrapper stbds_hmput_default
#define stbds_hmput_key_wrapper stbds_hmput_key #define stbds_hmput_key_wrapper stbds_hmput_key
#define stbds_hmdel_key_wrapper stbds_hmdel_key #define stbds_hmdel_key_wrapper stbds_hmdel_key
@ -656,6 +731,9 @@ size_t stbds_rehash_items;
// stbds_arr implementation // 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 *stbds_arrgrowf(void *a, size_t elemsize, size_t addlen, size_t min_cap)
{ {
void *b; 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) else if (min_cap < 4)
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)); 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); b = (char *) b + sizeof(stbds_array_header);
if (a == NULL) { if (a == NULL) {
stbds_header(b)->length = 0; 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_STATS(++stbds_array_grow);
} }
stbds_header(b)->capacity = min_cap; stbds_header(b)->capacity = min_cap;
return b; 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 #define STBDS_SIPHASH_D_ROUNDS 1
#endif #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) static size_t stbds_siphash_bytes(void *p, size_t len, size_t seed)
{ {
unsigned char *d = (unsigned char *) p; 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; v2 ^= 0xff;
for (j=0; j < STBDS_SIPHASH_D_ROUNDS; ++j) for (j=0; j < STBDS_SIPHASH_D_ROUNDS; ++j)
STBDS_SIPROUND(); STBDS_SIPROUND();
#ifdef STBDS_SIPHASH_2_4 #ifdef STBDS_SIPHASH_2_4
return v0^v1^v2^v3; return v0^v1^v2^v3;
#else #else
@ -1071,8 +1160,12 @@ size_t stbds_hash_bytes(void *p, size_t len, size_t seed)
} }
#endif #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) if (mode >= STBDS_HM_STRING)
return 0==strcmp((char *) key, * (char **) ((char *) a + elemsize*i)); 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) #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 (a == NULL) return;
if (stbds_hash_table(a) != NULL) { if (stbds_hash_table(a) != NULL) {
if (stbds_hash_table(a)->string.mode == STBDS_SH_STRDUP) { if (stbds_hash_table(a)->string.mode == STBDS_SH_STRDUP) {
size_t i; size_t i;
// skip 0th element, which is default // skip 0th element, which is default
for (i=1; i < stbds_header(a)->length; ++i) for (i=1; i < stbds_header(a)->length; ++i)
STBDS_FREE(NULL, *(char**) ((char *) a + elemsize*i)); STBDS_FREE(NULL, *(char**) ((char *) a + elemsize*i));
} }
stbds_strreset(&stbds_hash_table(a)->string); stbds_strreset(&stbds_hash_table(a)->string);
} }
STBDS_FREE(NULL, stbds_header(a)->hash_table); STBDS_FREE(NULL, stbds_header(a)->hash_table);
STBDS_FREE(NULL, stbds_header(a)); 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); void *raw_a = STBDS_HASH_TO_ARR(a,elemsize);
stbds_hash_index *table = stbds_hash_table(raw_a); 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 // 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) { for (i=pos & STBDS_BUCKET_MASK; i < STBDS_BUCKET_LENGTH; ++i) {
if (bucket->hash[i] == hash) { 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; return (pos & ~STBDS_BUCKET_MASK)+i;
} }
} else if (bucket->hash[i] == STBDS_HASH_EMPTY) { } 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; limit = pos & STBDS_BUCKET_MASK;
for (i = 0; i < limit; ++i) { for (i = 0; i < limit; ++i) {
if (bucket->hash[i] == hash) { 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; return (pos & ~STBDS_BUCKET_MASK)+i;
} }
} else if (bucket->hash[i] == STBDS_HASH_EMPTY) { } 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); pos &= (table->slot_count-1);
} }
/* NOTREACHED */ /* 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) { if (a == NULL) {
// make it non-empty so we can return a temp // make it non-empty so we can return a temp
a = stbds_arrgrowf(0, elemsize, 0, 1); a = stbds_arrgrowf(0, elemsize, 0, 1);
stbds_header(a)->length += 1; stbds_header(a)->length += 1;
memset(a, 0, elemsize); memset(a, 0, elemsize);
stbds_temp(a) = STBDS_INDEX_EMPTY; *temp = STBDS_INDEX_EMPTY;
// adjust a to point after the default element // adjust a to point after the default element
return STBDS_ARR_TO_HASH(a,elemsize); return STBDS_ARR_TO_HASH(a,elemsize);
} else { } 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 // adjust a to point to the default element
table = (stbds_hash_index *) stbds_header(raw_a)->hash_table; table = (stbds_hash_index *) stbds_header(raw_a)->hash_table;
if (table == 0) { if (table == 0) {
stbds_temp(raw_a) = -1; *temp = -1;
} else { } 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) { if (slot < 0) {
stbds_temp(raw_a) = STBDS_INDEX_EMPTY; *temp = STBDS_INDEX_EMPTY;
} else { } else {
stbds_hash_bucket *b = &table->storage[slot >> STBDS_BUCKET_SHIFT]; 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; 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) void * stbds_hmput_default(void *a, size_t elemsize)
{ {
// three cases: // 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) void *stbds_hmput_key(void *a, size_t elemsize, void *key, size_t keysize, int mode)
{ {
size_t keyoffset=0;
void *raw_a; void *raw_a;
stbds_hash_index *table; 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; slot_count = (table == NULL) ? STBDS_BUCKET_LENGTH : table->slot_count*2;
nt = stbds_make_hash_index(slot_count, table); nt = stbds_make_hash_index(slot_count, table);
if (table) { if (table)
STBDS_FREE(NULL, 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_header(a)->hash_table = table = nt;
STBDS_STATS(++stbds_hash_grow); 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 // start searching from pos to end of bucket
for (i=pos & STBDS_BUCKET_MASK; i < STBDS_BUCKET_LENGTH; ++i) { for (i=pos & STBDS_BUCKET_MASK; i < STBDS_BUCKET_LENGTH; ++i) {
if (bucket->hash[i] == hash) { 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]; stbds_temp(a) = bucket->index[i];
return STBDS_ARR_TO_HASH(a,elemsize); 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; limit = pos & STBDS_BUCKET_MASK;
for (i = 0; i < limit; ++i) { for (i = 0; i < limit; ++i) {
if (bucket->hash[i] == hash) { 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]; stbds_temp(a) = bucket->index[i];
return STBDS_ARR_TO_HASH(a,elemsize); 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); 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)) if ((size_t) i+1 > stbds_arrcap(a))
*(void **) &a = stbds_arrgrowf(a, elemsize, 1, 0); *(void **) &a = stbds_arrgrowf(a, elemsize, 1, 0);
raw_a = STBDS_ARR_TO_HASH(a,elemsize); 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; stbds_temp(a) = i-1;
switch (table->string.mode) { switch (table->string.mode) {
case STBDS_SH_STRDUP: *(char **) ((char *) a + elemsize*i) = stbds_strdup((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_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_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); return STBDS_ARR_TO_HASH(a,elemsize);
@ -1324,7 +1428,7 @@ void * stbds_shmode_func(size_t elemsize, int mode)
memset(a, 0, elemsize); memset(a, 0, elemsize);
stbds_header(a)->length = 1; stbds_header(a)->length = 1;
stbds_header(a)->hash_table = h = (stbds_hash_index *) stbds_make_hash_index(STBDS_BUCKET_LENGTH, NULL); 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); 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; return a;
} else { } else {
ptrdiff_t slot; 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) if (slot < 0)
return a; return a;
else { 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 // now find the slot for the last element
if (mode == STBDS_HM_STRING) 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 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); STBDS_ASSERT(slot >= 0);
b = &table->storage[slot >> STBDS_BUCKET_SHIFT]; b = &table->storage[slot >> STBDS_BUCKET_SHIFT];
i = slot & STBDS_BUCKET_MASK; 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 */ /* NOTREACHED */
return 0;
} }
static char *stbds_strdup(char *str) static char *stbds_strdup(char *str)
@ -1408,10 +1511,10 @@ static char *stbds_strdup(char *str)
} }
#ifndef STBDS_STRING_ARENA_BLOCKSIZE_MIN #ifndef STBDS_STRING_ARENA_BLOCKSIZE_MIN
#define STBDS_STRING_ARENA_BLOCKSIZE_MIN 512 #define STBDS_STRING_ARENA_BLOCKSIZE_MIN 512u
#endif #endif
#ifndef STBDS_STRING_ARENA_BLOCKSIZE_MAX #ifndef STBDS_STRING_ARENA_BLOCKSIZE_MAX
#define STBDS_STRING_ARENA_BLOCKSIZE_MAX 1<<20 #define STBDS_STRING_ARENA_BLOCKSIZE_MAX (1u<<20)
#endif #endif
char *stbds_stralloc(stbds_string_arena *a, char *str) char *stbds_stralloc(stbds_string_arena *a, char *str)
@ -1492,6 +1595,7 @@ void stbds_strreset(stbds_string_arena *a)
#endif #endif
typedef struct { int key,b,c,d; } stbds_struct; typedef struct { int key,b,c,d; } stbds_struct;
typedef struct { int key[2],b,c,d; } stbds_struct2;
static char buffer[256]; static char buffer[256];
char *strkey(int n) char *strkey(int n)
@ -1511,12 +1615,16 @@ void stbds_unit_tests(void)
STBDS_ASSERT(0); STBDS_ASSERT(0);
#else #else
const int testsize = 100000; const int testsize = 100000;
const int testsize2 = testsize/20;
int *arr=NULL; int *arr=NULL;
struct { int key; int value; } *intmap = NULL; struct { int key; int value; } *intmap = NULL;
struct { char *key; int value; } *strmap = NULL; struct { char *key; int value; } *strmap = NULL;
struct { stbds_struct key; int value; } *map = NULL; struct { stbds_struct key; int value; } *map = NULL;
stbds_struct *map2 = NULL; stbds_struct *map2 = NULL;
stbds_string_arena sa = { 0 }; stbds_struct2 *map3 = NULL;
stbds_string_arena sa = { 0 };
int key3[2] = { 1,2 };
ptrdiff_t temp;
int i,j; int i,j;
@ -1552,9 +1660,12 @@ void stbds_unit_tests(void)
STBDS_ASSERT(hmget (intmap, i) == -2); STBDS_ASSERT(hmget (intmap, i) == -2);
for (i=0; i < testsize; i+=2) for (i=0; i < testsize; i+=2)
hmput(intmap, i, i*5); 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 ); if (i & 1) STBDS_ASSERT(hmget(intmap, i) == -2 );
else STBDS_ASSERT(hmget(intmap, i) == i*5); 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) for (i=0; i < testsize; i+=2)
hmput(intmap, i, i*3); hmput(intmap, i, i*3);
for (i=0; i < testsize; i+=1) 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 }; stbds_struct t = { i,i*2,i*3+1,i*4 };
if (i & 1) STBDS_ASSERT(hmget(map, s) == 0); if (i & 1) STBDS_ASSERT(hmget(map, s) == 0);
else STBDS_ASSERT(hmget(map, s) == i*5); 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) { 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 }; stbds_struct t = { i,i*2,i*3+1,i*4 };
if (i & 1) STBDS_ASSERT(hmgets(map2, s.key).d == 0); if (i & 1) STBDS_ASSERT(hmgets(map2, s.key).d == 0);
else STBDS_ASSERT(hmgets(map2, s.key).d == i*4); 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); 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
} }
#endif #endif