Przeglądaj źródła

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

Sean Barrett 5 lat temu
rodzic
commit
d693c6103a
1 zmienionych plików z 191 dodań i 66 usunięć
  1. 191 66
      stb_ds.h

+ 191 - 66
stb_ds.h

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