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
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