Browse Source

Merge branch 'master' into working

Sean Barrett 6 years ago
parent
commit
d940053a01

File diff suppressed because it is too large
+ 221 - 150
stb.h


+ 5 - 4
stb_c_lexer.h

@@ -514,7 +514,7 @@ static int stb__clex_parse_string(stb_lexer *lexer, char *p, int type)
    }
    *out = 0;
    lexer->string = lexer->string_storage;
-   lexer->string_len = out - lexer->string_storage;
+   lexer->string_len = (int) (out - lexer->string_storage);
    return stb__clex_token(lexer, type, start, p);
 }
 
@@ -824,7 +824,7 @@ int stb_c_lexer_get_token(stb_lexer *lexer)
 #endif // STB_C_LEXER_IMPLEMENTATION
 
 #ifdef STB_C_LEXER_SELF_TEST
-
+#define _CRT_SECURE_NO_WARNINGS
 #include <stdio.h>
 #include <stdlib.h>
 
@@ -884,11 +884,12 @@ void dummy(void)
 {
    double some_floats[] = {
       1.0501, -10.4e12, 5E+10,
-#if 0   // not support in C++ or C-pre-99, so don't try to compile it
+#if 0   // not supported in C++ or C-pre-99, so don't try to compile it, but let our parser test it
       0x1.0p+24, 0xff.FP-8, 0x1p-23,
 #endif
       4.
    };
+   (void) sizeof(some_floats);
 
    printf("test %d",1); // https://github.com/nothings/stb/issues/13
 }
@@ -897,7 +898,7 @@ int main(int argc, char **argv)
 {
    FILE *f = fopen("stb_c_lexer.h","rb");
    char *text = (char *) malloc(1 << 20);
-   int len = f ? fread(text, 1, 1<<20, f) : -1;
+   int len = f ? (int) fread(text, 1, 1<<20, f) : -1;
    stb_lexer lex;
    if (len < 0) {
       fprintf(stderr, "Error opening file\n");

+ 6 - 3
stb_connected_components.h

@@ -709,7 +709,7 @@ static void stbcc__remove_clump_connection(stbcc_grid *g, int x1, int y1, int x2
 
 static void stbcc__add_connections_to_adjacent_cluster(stbcc_grid *g, int cx, int cy, int dx, int dy)
 {
-   unsigned char connected[STBCC__MAX_EDGE_CLUMPS_PER_CLUSTER][STBCC__MAX_EDGE_CLUMPS_PER_CLUSTER/8] = { 0 };
+   unsigned char connected[STBCC__MAX_EDGE_CLUMPS_PER_CLUSTER][STBCC__MAX_EDGE_CLUMPS_PER_CLUSTER/8] = { { 0 } };
    int x = cx * STBCC__CLUSTER_SIZE_X;
    int y = cy * STBCC__CLUSTER_SIZE_Y;
    int step_x, step_y=0, i, j, k, n;
@@ -751,6 +751,7 @@ static void stbcc__add_connections_to_adjacent_cluster(stbcc_grid *g, int cx, in
       n = STBCC__CLUSTER_SIZE_X;
    } else {
       assert(0);
+      return;
    }
 
    for (k=0; k < n; ++k) {
@@ -772,7 +773,7 @@ static void stbcc__add_connections_to_adjacent_cluster(stbcc_grid *g, int cx, in
 
 static void stbcc__remove_connections_to_adjacent_cluster(stbcc_grid *g, int cx, int cy, int dx, int dy)
 {
-   unsigned char disconnected[STBCC__MAX_EDGE_CLUMPS_PER_CLUSTER][STBCC__MAX_EDGE_CLUMPS_PER_CLUSTER/8] = { 0 };
+   unsigned char disconnected[STBCC__MAX_EDGE_CLUMPS_PER_CLUSTER][STBCC__MAX_EDGE_CLUMPS_PER_CLUSTER/8] = { { 0 } };
    int x = cx * STBCC__CLUSTER_SIZE_X;
    int y = cy * STBCC__CLUSTER_SIZE_Y;
    int step_x, step_y=0, i, j, k, n;
@@ -811,6 +812,7 @@ static void stbcc__remove_connections_to_adjacent_cluster(stbcc_grid *g, int cx,
       n = STBCC__CLUSTER_SIZE_X;
    } else {
       assert(0);
+      return;
    }
 
    for (k=0; k < n; ++k) {
@@ -954,11 +956,12 @@ static void stbcc__build_clumps_for_cluster(stbcc_grid *g, int cx, int cy)
    for (j=1; j < STBCC__CLUSTER_SIZE_Y-1; ++j) {
       for (i=1; i < STBCC__CLUSTER_SIZE_X-1; ++i) {
          stbcc__tinypoint p = cbi.parent[j][i];
-         if (p.x == i && p.y == j)
+         if (p.x == i && p.y == j) {
             if (STBCC__MAP_OPEN(g,x+i,y+j))
                cbi.label[j][i] = label++;
             else
                cbi.label[j][i] = STBCC__NULL_CLUMPID;
+         }
       }
    }
 

+ 43 - 20
stb_ds.h

@@ -275,10 +275,10 @@ 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).
-    For GCC and clang, extensions are used that eliminate this requirement if you're
-    using C99 and later or using C++.
+    Extensions are used that eliminate this requirement if you're using C99 and later
+    in GCC or clang, or if you're using C++ in GCC.
 
-  * To test for presence of a key in a hashmap, just do 'hmget(foo,key) >= 0'.
+  * To test for presence of a key in a hashmap, just do 'hmgeti(foo,key) >= 0'.
 
   * The iteration order of your data in the hashmap is determined solely by the
     order of insertions and deletions. In particular, if you never delete, new
@@ -309,6 +309,10 @@ CREDITS
   Per Vognsen  -- idea for hash table API/implementation
 */
 
+#ifdef STBDS_UNIT_TESTS
+#define _CRT_SECURE_NO_WARNINGS
+#endif
+
 #ifndef INCLUDE_STB_DS_H
 #define INCLUDE_STB_DS_H
 
@@ -419,7 +423,7 @@ extern void * stbds_shmode_func(size_t elemsize, int mode);
   #if __clang__
   #define STBDS_ADDRESSOF(typevar, value)     ((__typeof__(typevar)[1]){value}) // literal array decays to pointer to value
   #else
-  #define STBDS_ADDRESSOF(typevar, value)     ((typeof(typevar)[]){value}) // literal array decays to pointer to value
+  #define STBDS_ADDRESSOF(typevar, value)     ((typeof(typevar)[1]){value}) // literal array decays to pointer to value
   #endif
 #else
 #define STBDS_ADDRESSOF(typevar, value)     &(value)
@@ -596,6 +600,7 @@ template<class T> static T * stbds_shmode_func_wrapper(T *, size_t elemsize, int
 #include <string.h>
 
 #ifndef STBDS_ASSERT
+#define STBDS_ASSERT_WAS_UNDEFINED
 #define STBDS_ASSERT(x)   ((void) 0)
 #endif
 
@@ -651,10 +656,15 @@ void *stbds_arrgrowf(void *a, size_t elemsize, size_t addlen, size_t min_cap)
 // stbds_hm hash table implementation
 //
 
-#define STBDS_CACHE_LINE_SIZE   64
+#ifdef STBDS_INTERNAL_SMALL_BUCKET
+#define STBDS_BUCKET_LENGTH      4
+#else
 #define STBDS_BUCKET_LENGTH      8
-#define STBDS_BUCKET_SHIFT       3
+#endif
+
+#define STBDS_BUCKET_SHIFT      (STBDS_BUCKET_LENGTH == 8 ? 3 : 2)
 #define STBDS_BUCKET_MASK       (STBDS_BUCKET_LENGTH-1)
+#define STBDS_CACHE_LINE_SIZE   64
 
 #define STBDS_ALIGN_FWD(n,a)   (((n) + (a) - 1) & ~((a)-1))
 
@@ -701,13 +711,12 @@ void stbds_rand_seed(size_t seed)
 
 static size_t stbds_probe_position(size_t hash, size_t slot_count, size_t slot_log2)
 {
-  #if 1
-  size_t pos = (hash >> (STBDS_SIZE_T_BITS-slot_log2));
-  STBDS_ASSERT(pos < slot_count);
-  return pos;
-  #else
-  return hash & (slot_count-1);
+  size_t pos;
+  pos = hash & (slot_count-1);
+  #ifdef STBDS_INTERNAL_BUCKET_START
+  pos &= ~STBDS_BUCKET_MASK;
   #endif
+  return pos;
 }
 
 static size_t stbds_log2(size_t slot_count)
@@ -815,7 +824,6 @@ static stbds_hash_index *stbds_make_hash_index(size_t slot_count, stbds_hash_ind
           for (;;) {
             size_t limit,z;
             stbds_hash_bucket *bucket;
-            pos &= (t->slot_count-1);
             bucket = &t->storage[pos >> STBDS_BUCKET_SHIFT];
             STBDS_STATS(++stbds_rehash_probes);
 
@@ -838,6 +846,7 @@ static stbds_hash_index *stbds_make_hash_index(size_t slot_count, stbds_hash_ind
 
             pos += step;                  // quadratic probing
             step += STBDS_BUCKET_LENGTH;
+            pos &= (t->slot_count-1);
           }
         }
        done:
@@ -942,7 +951,7 @@ static size_t stbds_siphash_bytes(void *p, size_t len, size_t seed)
 #ifdef STBDS_SIPHASH_2_4
   return v0^v1^v2^v3;
 #else
-  return v1^v2^v3; // slightly stronger since v0^v3 in above cancels out final round operation
+  return v1^v2^v3; // slightly stronger since v0^v3 in above cancels out final round operation? I tweeted at the authors of SipHash about this but they didn't reply
 #endif
 }
 
@@ -957,10 +966,11 @@ size_t stbds_hash_bytes(void *p, size_t len, size_t seed)
     unsigned int hash = d[0] | (d[1] << 8) | (d[2] << 16) | (d[3] << 24);
     #if 0
     // HASH32-A  Bob Jenkin's hash function w/o large constants
-    hash ^= seed ^ len;
+    hash ^= seed;
     hash -= (hash<<6);
     hash ^= (hash>>17);
     hash -= (hash<<9);
+    hash ^= seed;
     hash ^= (hash<<4);
     hash -= (hash<<3);
     hash ^= (hash<<10);
@@ -969,22 +979,24 @@ size_t stbds_hash_bytes(void *p, size_t len, size_t seed)
     // HASH32-BB  Bob Jenkin's presumably-accidental version of Thomas Wang hash with rotates turned into shifts.
     // Note that converting these back to rotates makes it run a lot slower, presumably due to collisions, so I'm
     // not really sure what's going on.
-    hash ^= seed ^ len;
+    hash ^= seed;
     hash = (hash ^ 61) ^ (hash >> 16);
     hash = hash + (hash << 3);
     hash = hash ^ (hash >> 4);
     hash = hash * 0x27d4eb2d;
+    hash ^= seed;
     hash = hash ^ (hash >> 15);
     #else  // HASH32-C   -  Murmur3
+    hash ^= seed;
     hash *= 0xcc9e2d51;
     hash = (hash << 17) | (hash >> 15);
     hash *= 0x1b873593;
     hash ^= seed;
     hash = (hash << 19) | (hash >> 13);
     hash = hash*5 + 0xe6546b64;
-    hash ^= len;
     hash ^= hash >> 16;
     hash *= 0x85ebca6b;
+    hash ^= seed;
     hash ^= hash >> 13;
     hash *= 0xc2b2ae35;
     hash ^= hash >> 16;
@@ -1009,16 +1021,17 @@ size_t stbds_hash_bytes(void *p, size_t len, size_t seed)
   } else if (len == 8 && sizeof(size_t) == 8) {
     size_t hash = d[0] | (d[1] << 8) | (d[2] << 16) | (d[3] << 24);
     hash |= (size_t) (d[4] | (d[5] << 8) | (d[6] << 16) | (d[7] << 24)) << 16 << 16; // avoid warning if size_t == 4
-    hash ^= seed ^ len;
+    hash ^= seed;
     hash = (~hash) + (hash << 21);
     hash ^= STBDS_ROTATE_RIGHT(hash,24);
     hash *= 265;
     hash ^= STBDS_ROTATE_RIGHT(hash,14);
+    hash ^= seed;
     hash *= 21;
     hash ^= STBDS_ROTATE_RIGHT(hash,28);
     hash += (hash << 31);
     hash = (~hash) + (hash << 18);
-    return hash^seed;
+    return hash;
   } else {
     return stbds_siphash_bytes(p,len,seed);
   }
@@ -1275,6 +1288,8 @@ void * stbds_shmode_func(size_t elemsize, int mode)
 {
   void *a = stbds_arrgrowf(0, elemsize, 0, 1);
   stbds_hash_index *h;
+  memset(a, 0, elemsize);
+  stbds_header(a)->length = 1;
   stbds_header(a)->hash_table = h = (stbds_hash_index *) stbds_make_hash_index(STBDS_BUCKET_LENGTH, NULL);
   h->string.mode = mode;
   return STBDS_ARR_TO_HASH(a,elemsize);
@@ -1435,6 +1450,9 @@ void stbds_strreset(stbds_string_arena *a)
 
 #ifdef STBDS_UNIT_TESTS
 #include <stdio.h>
+#ifdef STBDS_ASSERT_WAS_UNDEFINED
+#undef STBDS_ASSERT
+#endif
 #ifndef STBDS_ASSERT
 #define STBDS_ASSERT assert
 #include <assert.h>
@@ -1445,7 +1463,11 @@ typedef struct { int key,b,c,d; } stbds_struct;
 static char buffer[256];
 char *strkey(int n)
 {
+#if defined(_WIN32) && defined(__STDC_WANT_SECURE_LIB__)
+   sprintf_s(buffer, sizeof(buffer), "test_%d", n);
+#else
    sprintf(buffer, "test_%d", n);
+#endif
    return buffer;
 }
 
@@ -1588,9 +1610,10 @@ void stbds_unit_tests(void)
 
   for (i=0; i < testsize; i += 1) {
     stbds_struct s = { i,i*2,i*3,i*4 };
-    stbds_struct t = { i,i*2,i*3,i*4 };
+    stbds_struct t = { i,i*2,i*3+1,i*4 };
     if (i & 1) STBDS_ASSERT(hmgets(map2, s.key).d == 0);
     else       STBDS_ASSERT(hmgets(map2, s.key).d == i*4);
+    STBDS_ASSERT(hmget(map, t) == 0);
   }
   hmfree(map2);
 #endif

+ 7 - 7
stb_herringbone_wang_tile.h

@@ -166,7 +166,7 @@ int main(int argc, char **argv)
 typedef struct stbhw_tileset stbhw_tileset;
 
 // returns description of last error produced by any function (not thread-safe)
-STBHW_EXTERN char *stbhw_get_last_error(void);
+STBHW_EXTERN const char *stbhw_get_last_error(void);
 
 // build a tileset from an image that conforms to a template created by this
 // library. (you allocate storage for stbhw_tileset and function fills it out;
@@ -345,10 +345,10 @@ static signed char c_color[STB_HBWANG_MAX_Y+6][STB_HBWANG_MAX_X+6];
 static signed char v_color[STB_HBWANG_MAX_Y+6][STB_HBWANG_MAX_X+5];
 static signed char h_color[STB_HBWANG_MAX_Y+5][STB_HBWANG_MAX_X+6];
 
-static char *stbhw_error;
-STBHW_EXTERN char *stbhw_get_last_error(void)
+static const char *stbhw_error;
+STBHW_EXTERN const char *stbhw_get_last_error(void)
 {
-   char *temp = stbhw_error;
+   const char *temp = stbhw_error;
    stbhw_error = 0;
    return temp;
 }
@@ -717,7 +717,7 @@ STBHW_EXTERN int stbhw_generate_image(stbhw_tileset *ts, int **weighting, unsign
       // to avoid really obvious repetition (which happens easily with extreme weights)
       for (j=0; j < ymax-3; ++j) {
          for (i=0; i < xmax-3; ++i) {
-            int p = (i-j+1) & 3; // corner type
+            //int p = (i-j+1) & 3; // corner type   // unused, not sure what the intent was so commenting it out
             STB_HBWANG_ASSERT(i+3 < STB_HBWANG_MAX_X+6);
             STB_HBWANG_ASSERT(j+3 < STB_HBWANG_MAX_Y+6);
             if (stbhw__match(i,j) && stbhw__match(i,j+1) && stbhw__match(i,j+2)
@@ -747,7 +747,7 @@ STBHW_EXTERN int stbhw_generate_image(stbhw_tileset *ts, int **weighting, unsign
          } else {
             i = phase-4;
          }
-         for (i;; i += 4) {
+         for (;; i += 4) {
             int xpos = i * sidelen;
             if (xpos >= w)
                break;
@@ -798,7 +798,7 @@ STBHW_EXTERN int stbhw_generate_image(stbhw_tileset *ts, int **weighting, unsign
          } else {
             i = phase-4;
          }
-         for (i;; i += 4) {
+         for (;; i += 4) {
             int xpos = i * sidelen;
             if (xpos >= w)
                break;

+ 3 - 1
stb_image.h

@@ -339,11 +339,13 @@ typedef unsigned short stbi_us;
 extern "C" {
 #endif
 
+#ifndef STBIDEF
 #ifdef STB_IMAGE_STATIC
 #define STBIDEF static
 #else
 #define STBIDEF extern
 #endif
+#endif
 
 //////////////////////////////////////////////////////////////////////////////
 //
@@ -1182,7 +1184,7 @@ STBI_EXTERN __declspec(dllimport) int __stdcall WideCharToMultiByte(unsigned int
 #if defined(_MSC_VER) && defined(STBI_WINDOWS_UTF8)
 STBIDEF int stbi_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input)
 {
-	return WideCharToMultiByte(65001 /* UTF8 */, 0, input, -1, buffer, bufferlen, NULL, NULL);
+	return WideCharToMultiByte(65001 /* UTF8 */, 0, input, -1, buffer, (int) bufferlen, NULL, NULL);
 }
 #endif
 

+ 4 - 2
stb_image_resize.h

@@ -193,6 +193,7 @@ typedef uint16_t stbir_uint16;
 typedef uint32_t stbir_uint32;
 #endif
 
+#ifndef STBIRDEF
 #ifdef STB_IMAGE_RESIZE_STATIC
 #define STBIRDEF static
 #else
@@ -202,7 +203,7 @@ typedef uint32_t stbir_uint32;
 #define STBIRDEF extern
 #endif
 #endif
-
+#endif
 
 //////////////////////////////////////////////////////////////////////////////
 //
@@ -2324,8 +2325,9 @@ static int stbir__resize_allocated(stbir__info *info,
     if (alpha_channel < 0)
         flags |= STBIR_FLAG_ALPHA_USES_COLORSPACE | STBIR_FLAG_ALPHA_PREMULTIPLIED;
 
-    if (!(flags&STBIR_FLAG_ALPHA_USES_COLORSPACE) || !(flags&STBIR_FLAG_ALPHA_PREMULTIPLIED))
+    if (!(flags&STBIR_FLAG_ALPHA_USES_COLORSPACE) || !(flags&STBIR_FLAG_ALPHA_PREMULTIPLIED)) {
         STBIR_ASSERT(alpha_channel >= 0 && alpha_channel < info->channels);
+    }
 
     if (alpha_channel >= info->channels)
         return 0;

+ 3 - 3
stb_image_write.h

@@ -299,7 +299,7 @@ STBIW_EXTERN __declspec(dllimport) int __stdcall WideCharToMultiByte(unsigned in
 
 STBIWDEF int stbiw_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input)
 {
-	return WideCharToMultiByte(65001 /* UTF8 */, 0, input, -1, buffer, bufferlen, NULL, NULL);
+	return WideCharToMultiByte(65001 /* UTF8 */, 0, input, -1, buffer, (int) bufferlen, NULL, NULL);
 }
 #endif
 
@@ -737,8 +737,8 @@ static int stbi_write_hdr_core(stbi__write_context *s, int x, int y, int comp, f
       char header[] = "#?RADIANCE\n# Written by stb_image_write.h\nFORMAT=32-bit_rle_rgbe\n";
       s->func(s->context, header, sizeof(header)-1);
 
-#ifdef STBI_MSC_SECURE_CRT
-      len = sprintf_s(buffer, "EXPOSURE=          1.0000000000000\n\n-Y %d +X %d\n", y, x);
+#ifdef __STDC_WANT_SECURE_LIB__
+      len = sprintf_s(buffer, sizeof(buffer), "EXPOSURE=          1.0000000000000\n\n-Y %d +X %d\n", y, x);
 #else
       len = sprintf(buffer, "EXPOSURE=          1.0000000000000\n\n-Y %d +X %d\n", y, x);
 #endif

+ 28 - 46
stb_tilemap_editor.h

@@ -971,7 +971,7 @@ struct stbte_tilemap
     short *undo_buffer;
 };
 
-static char *default_category = "[unassigned]";
+static char *default_category = (char*) "[unassigned]";
 
 static void stbte__init_gui(void)
 {
@@ -1172,7 +1172,9 @@ void stbte_set_property(stbte_tilemap *tm, int x, int y, int n, float val)
    tm->props[y][x][n] = val;
 }
 
+#ifdef STBTE_ALLOW_LINK
 static void stbte__set_link(stbte_tilemap *tm, int src_x, int src_y, int dest_x, int dest_y, int undo_mode);
+#endif
 
 enum
 {
@@ -1254,7 +1256,7 @@ static int stbte__strequal(char *p, char *q)
 
 static void stbte__compute_tileinfo(stbte_tilemap *tm)
 {
-   int i,j,n=0;
+   int i,j;
 
    tm->num_categories=0;
 
@@ -1649,6 +1651,7 @@ static void stbte__draw_rect(int x0, int y0, int x1, int y1, unsigned int color)
    STBTE_DRAW_RECT(x0,y0,x1,y1, color);
 }
 
+#ifdef STBTE_ALLOW_LINK
 static void stbte__draw_line(int x0, int y0, int x1, int y1, unsigned int color)
 {
    int temp;
@@ -1662,6 +1665,7 @@ static void stbte__draw_link(int x0, int y0, int x1, int y1, unsigned int color)
    stbte__draw_line(x0,y0,x0,y1, color);
    stbte__draw_line(x0,y1,x1,y1, color);
 }
+#endif
 
 static void stbte__draw_frame(int x0, int y0, int x1, int y1, unsigned int color)
 {
@@ -1671,12 +1675,6 @@ static void stbte__draw_frame(int x0, int y0, int x1, int y1, unsigned int color
    stbte__draw_rect(x0,y0+1,x0+1,y1,color);
 }
 
-static void stbte__draw_halfframe(int x0, int y0, int x1, int y1, unsigned int color)
-{
-   stbte__draw_rect(x0,y0,x1,y0+1,color);
-   stbte__draw_rect(x0,y0+1,x0+1,y1,color);
-}
-
 static int stbte__get_char_width(int ch)
 {
    return stbte__fontdata[ch-16];
@@ -1817,15 +1815,13 @@ static void stbte__draw_textbox(int x0, int y0, int x1, int y1, char *text, int
    stbte__draw_text(x0+xoff,y0+yoff, text, x1-x0-xoff-1, stbte__color_table[colormode][STBTE__text][colorindex]);
 }
 
-static int stbte__button(int colormode, char *label, int x, int y, int textoff, int width, int id, int toggled, int disabled)
+static int stbte__button(int colormode, const char *label, int x, int y, int textoff, int width, int id, int toggled, int disabled)
 {
    int x0=x,y0=y, x1=x+width,y1=y+STBTE__BUTTON_HEIGHT;
    int s = STBTE__BUTTON_INTERNAL_SPACING;
 
-   int over = !disabled && stbte__hittest(x0,y0,x1,y1,id);
-      
    if (stbte__ui.event == STBTE__paint)
-      stbte__draw_textbox(x0,y0,x1,y1, label,s+textoff,s, colormode, STBTE__INDEX_FOR_ID(id,disabled,toggled));
+      stbte__draw_textbox(x0,y0,x1,y1, (char*) label,s+textoff,s, colormode, STBTE__INDEX_FOR_ID(id,disabled,toggled));
    if (disabled)
       return 0;
    return (stbte__button_core(id) == 1);
@@ -1836,8 +1832,6 @@ static int stbte__button_icon(int colormode, char ch, int x, int y, int width, i
    int x0=x,y0=y, x1=x+width,y1=y+STBTE__BUTTON_HEIGHT;
    int s = STBTE__BUTTON_INTERNAL_SPACING;
 
-   int over = stbte__hittest(x0,y0,x1,y1,id);
-      
    if (stbte__ui.event == STBTE__paint) {
       char label[2] = { ch, 0 };
       int pad = (9 - stbte__get_char_width(ch))/2;
@@ -1851,7 +1845,6 @@ static int stbte__button_icon(int colormode, char ch, int x, int y, int width, i
 static int stbte__minibutton(int colormode, int x, int y, int ch, int id)
 {
    int x0 = x, y0 = y, x1 = x+8, y1 = y+7;
-   int over = stbte__hittest(x0,y0,x1,y1,id);
    if (stbte__ui.event == STBTE__paint) {
       char str[2] = { (char)ch, 0 };
       stbte__draw_textbox(x0,y0,x1,y1, str,1,0,colormode, STBTE__INDEX_FOR_ID(id,0,0));
@@ -1862,7 +1855,6 @@ static int stbte__minibutton(int colormode, int x, int y, int ch, int id)
 static int stbte__layerbutton(int x, int y, int ch, int id, int toggled, int disabled, int colormode)
 {
    int x0 = x, y0 = y, x1 = x+10, y1 = y+11;
-   int over = !disabled && stbte__hittest(x0,y0,x1,y1,id);
    if (stbte__ui.event == STBTE__paint) {
       char str[2] = { (char)ch, 0 };
       int off = (9-stbte__get_char_width(ch))/2;
@@ -1876,7 +1868,6 @@ static int stbte__layerbutton(int x, int y, int ch, int id, int toggled, int dis
 static int stbte__microbutton(int x, int y, int size, int id, int colormode)
 {
    int x0 = x, y0 = y, x1 = x+size, y1 = y+size;
-   int over = stbte__hittest(x0,y0,x1,y1,id);
    if (stbte__ui.event == STBTE__paint) {
       stbte__draw_box(x0,y0,x1,y1, colormode, STBTE__INDEX_FOR_ID(id,0,0));
    }
@@ -1886,7 +1877,6 @@ static int stbte__microbutton(int x, int y, int size, int id, int colormode)
 static int stbte__microbutton_dragger(int x, int y, int size, int id, int *pos)
 {
    int x0 = x, y0 = y, x1 = x+size, y1 = y+size;
-   int over = stbte__hittest(x0,y0,x1,y1,id);
    switch (stbte__ui.event) {
       case STBTE__paint:
          stbte__draw_box(x0,y0,x1,y1, STBTE__cexpander, STBTE__INDEX_FOR_ID(id,0,0));
@@ -1912,15 +1902,13 @@ static int stbte__microbutton_dragger(int x, int y, int size, int id, int *pos)
    return 0;
 }
 
-static int stbte__category_button(char *label, int x, int y, int width, int id, int toggled)
+static int stbte__category_button(const char *label, int x, int y, int width, int id, int toggled)
 {
    int x0=x,y0=y, x1=x+width,y1=y+STBTE__BUTTON_HEIGHT;
    int s = STBTE__BUTTON_INTERNAL_SPACING;
-
-   int over = stbte__hittest(x0,y0,x1,y1,id);
       
    if (stbte__ui.event == STBTE__paint)
-      stbte__draw_textbox(x0,y0,x1,y1, label, s,s, STBTE__ccategory_button, STBTE__INDEX_FOR_ID(id,0,toggled));
+      stbte__draw_textbox(x0,y0,x1,y1, (char*) label, s,s, STBTE__ccategory_button, STBTE__INDEX_FOR_ID(id,0,toggled));
 
    return (stbte__button_core(id) == 1);
 }
@@ -1938,7 +1926,6 @@ static int stbte__slider(int x0, int w, int y, int range, int *value, int id)
 {
    int x1 = x0+w;
    int pos = *value * w / (range+1);
-   int over = stbte__hittest(x0,y-2,x1,y+3,id);
    int event_mouse_move = STBTE__change;
    switch (stbte__ui.event) {
       case STBTE__paint:
@@ -1969,15 +1956,22 @@ static int stbte__slider(int x0, int w, int y, int range, int *value, int id)
    return STBTE__none;
 }
 
-static int stbte__float_control(int x0, int y0, int w, float minv, float maxv, float scale, char *fmt, float *value, int colormode, int id)
+#if defined(_WIN32) && defined(__STDC_WANT_SECURE_LIB__)
+   #define stbte__sprintf      sprintf_s
+   #define stbte__sizeof(s)    , sizeof(s)
+#else
+   #define stbte__sprintf      sprintf
+   #define stbte__sizeof(s)    
+#endif
+
+static int stbte__float_control(int x0, int y0, int w, float minv, float maxv, float scale, const char *fmt, float *value, int colormode, int id)
 {
    int x1 = x0+w;
    int y1 = y0+11;
-   int over = stbte__hittest(x0,y0,x1,y1,id);
    switch (stbte__ui.event) {
       case STBTE__paint: {
          char text[32];
-         sprintf(text, fmt ? fmt : "%6.2f", *value);
+         stbte__sprintf(text stbte__sizeof(text), fmt ? fmt : "%6.2f", *value);
          stbte__draw_textbox(x0,y0,x1,y1, text, 1,2, colormode, STBTE__INDEX_FOR_ID(id,0,0));
          break;
       }
@@ -2283,7 +2277,6 @@ static void stbte__alert(const char *msg)
 
 static void stbte__brush_predict(stbte_tilemap *tm, short result[])
 {
-   int layer_to_paint = tm->cur_layer;
    stbte__tileinfo *ti;
    int i;
 
@@ -2322,7 +2315,6 @@ static void stbte__brush_predict(stbte_tilemap *tm, short result[])
 
 static void stbte__brush(stbte_tilemap *tm, int x, int y)
 {
-   int layer_to_paint = tm->cur_layer;
    stbte__tileinfo *ti;
 
    // find lowest legit layer to paint it on, and put it there
@@ -2634,7 +2626,6 @@ static void stbte__clear_stack(stbte_tilemap *tm, short result[])
 static void stbte__fillrect(stbte_tilemap *tm, int x0, int y0, int x1, int y1, int fill)
 {
    int i,j;
-   int x=x0,y=y0;
 
    stbte__begin_undo(tm);
    if (x0 > x1) i=x0,x0=x1,x1=i;
@@ -2734,6 +2725,7 @@ static int stbte__in_rect(int x, int y, int x0, int y0, int w, int h)
    return x >= x0 && x < x0+w && y >= y0 && y < y0+h;
 }
 
+#ifdef STBTE_ALLOW_LINK
 static int stbte__in_src_rect(int x, int y)
 {
    return stbte__in_rect(x,y, stbte__ui.copy_src_x, stbte__ui.copy_src_y, stbte__ui.copy_width, stbte__ui.copy_height);
@@ -2743,6 +2735,7 @@ static int stbte__in_dest_rect(int x, int y, int destx, int desty)
 {
    return stbte__in_rect(x,y, destx, desty, stbte__ui.copy_width, stbte__ui.copy_height);
 }
+#endif
 
 static void stbte__paste(stbte_tilemap *tm, int mapx, int mapy)
 {
@@ -2934,9 +2927,6 @@ static void stbte__tile_paint(stbte_tilemap *tm, int sx, int sy, int mapx, int m
 {
    int i;
    int id = STBTE__IDMAP(mapx,mapy);
-   int x0=sx, y0=sy;
-   int x1=sx+tm->spacing_x, y1=sy+tm->spacing_y;
-   int over = stbte__hittest(x0,y0,x1,y1, id);
    short *data = tm->data[mapy][mapx];
    short temp[STBTE_MAX_LAYERS];
 
@@ -3366,7 +3356,7 @@ static int stbte__info_value(const char *label, int x, int y, int val, int digit
    if (stbte__ui.event == STBTE__paint) {
       int off = 9-stbte__get_char_width(label[0]);
       char text[16];
-      sprintf(text, label, digits, val);
+      stbte__sprintf(text stbte__sizeof(text), label, digits, val);
       stbte__draw_text_core(x+off,y, text, 999, STBTE__TEXTCOLOR(STBTE__cpanel),1);
    }
    if (id) {
@@ -3414,7 +3404,7 @@ static void stbte__info(stbte_tilemap *tm, int x0, int y0, int w, int h)
 
 static void stbte__layers(stbte_tilemap *tm, int x0, int y0, int w, int h)
 {
-   static char *propmodes[3] = {
+   static const char *propmodes[3] = {
       "default", "always", "never"
    };
    int num_rows;
@@ -3449,7 +3439,7 @@ static void stbte__layers(stbte_tilemap *tm, int x0, int y0, int w, int h)
       int disabled = (tm->solo_layer >= 0 && tm->solo_layer != i);
       if (i-tm->layer_scroll >= 0 && i-tm->layer_scroll < num_rows) {
          if (str == NULL)
-            sprintf(str=text, "%2d", i+1);
+            stbte__sprintf(str=text stbte__sizeof(text), "%2d", i+1);
          if (stbte__button(STBTE__clayer_button, str, x0,y,(i+1<10)*2,xoff-2, STBTE__ID(STBTE__layer,i), tm->cur_layer==i,0))
             tm->cur_layer = (tm->cur_layer == i ? -1 : i);
          if (stbte__layerbutton(x0+xoff +  0,y+1,'H',STBTE__ID(STBTE__hide,i), tm->layerinfo[i].hidden,disabled,STBTE__clayer_hide))
@@ -3501,10 +3491,7 @@ static void stbte__categories(stbte_tilemap *tm, int x0, int y0, int w, int h)
 
 static void stbte__tile_in_palette(stbte_tilemap *tm, int x, int y, int slot)
 {
-   stbte__tileinfo *t = &tm->tiles[slot];
-   int x0=x, y0=y, x1 = x+tm->palette_spacing_x - 1, y1 = y+tm->palette_spacing_y;
    int id = STBTE__ID(STBTE__palette, slot);
-   int over = stbte__hittest(x0,y0,x1,y1, id);
    switch (stbte__ui.event) {
       case STBTE__paint:
          stbte__draw_rect(x,y,x+tm->palette_spacing_x-1,y+tm->palette_spacing_x-1, STBTE_COLOR_TILEPALETTE_BACKGROUND);
@@ -3561,15 +3548,10 @@ static void stbte__palette_of_tiles(stbte_tilemap *tm, int x0, int y0, int w, in
    stbte__scrollbar(x1-4, y0+6, y1-2, &tm->palette_scroll, 0, num_total_rows, num_vis_rows, STBTE__ID(STBTE__scrollbar_id, STBTE__palette));
 }
 
-static float stbte__linear_remap(float n, float x0, float x1, float y0, float y1)
-{
-   return (n-x0)/(x1-x0)*(y1-y0) + y0;
-}
-
 static float stbte__saved;
 static void stbte__props_panel(stbte_tilemap *tm, int x0, int y0, int w, int h)
 {
-   int x1 = x0+w, y1 = y0+h;
+   int x1 = x0+w;
    int i;
    int y = y0 + 5, x = x0+2;
    int slider_width = 60;
@@ -3585,8 +3567,8 @@ static void stbte__props_panel(stbte_tilemap *tm, int x0, int y0, int w, int h)
    for (i=0; i < STBTE_MAX_PROPERTIES; ++i) {
       unsigned int n = STBTE_PROP_TYPE(i, data, p);
       if (n) {
-         char *s = STBTE_PROP_NAME(i, data, p);
-         if (s == NULL) s = "";
+         char *s = (char*) STBTE_PROP_NAME(i, data, p);
+         if (s == NULL) s = (char*) "";
          switch (n & 3) {
             case STBTE_PROP_bool: {
                int flag = (int) p[i];

+ 7 - 1
stb_vorbis.c

@@ -4990,7 +4990,13 @@ stb_vorbis * stb_vorbis_open_file(FILE *file, int close_on_free, int *error, con
 
 stb_vorbis * stb_vorbis_open_filename(const char *filename, int *error, const stb_vorbis_alloc *alloc)
 {
-   FILE *f = fopen(filename, "rb");
+   FILE *f;
+#if defined(_WIN32) && defined(__STDC_WANT_SECURE_LIB__)
+   if (0 != fopen_s(&f, filename, "rb"))
+      f = NULL;
+#else
+   f = fopen(filename, "rb");
+#endif
    if (f) 
       return stb_vorbis_open_file(f, TRUE, error, alloc);
    if (error) *error = VORBIS_file_open_failure;

+ 2 - 2
stb_voxel_render.h

@@ -3552,7 +3552,7 @@ void stbvox_set_buffer(stbvox_mesh_maker *mm, int mesh, int slot, void *buffer,
    stbvox_bring_up_to_date(mm);
    mm->output_buffer[mesh][slot] = (char *) buffer;
    mm->output_cur   [mesh][slot] = (char *) buffer;
-   mm->output_len   [mesh][slot] = len;
+   mm->output_len   [mesh][slot] = (int) len;
    mm->output_end   [mesh][slot] = (char *) buffer + len;
    for (i=0; i < STBVOX_MAX_MESH_SLOTS; ++i) {
       if (mm->output_buffer[mesh][i]) {
@@ -3568,7 +3568,7 @@ void stbvox_set_default_mesh(stbvox_mesh_maker *mm, int mesh)
 
 int stbvox_get_quad_count(stbvox_mesh_maker *mm, int mesh)
 {
-   return (mm->output_cur[mesh][0] - mm->output_buffer[mesh][0]) / mm->output_size[mesh][0];
+   return (int) ((mm->output_cur[mesh][0] - mm->output_buffer[mesh][0]) / mm->output_size[mesh][0]);
 }
 
 stbvox_input_description *stbvox_get_input_description(stbvox_mesh_maker *mm)

+ 1 - 1
tests/image_test.c

@@ -113,7 +113,7 @@ int main(int argc, char **argv)
             printf("FAILED 4\n");
       }
    } else {
-      int i, nope=0;
+      int i;
       #ifdef PNGSUITE_PRIMARY
       char **files = stb_readdir_files("pngsuite/primary");
       #else

+ 3 - 0
tests/image_write_test.c

@@ -1,3 +1,6 @@
+#ifdef __clang__
+#define STBIWDEF static inline
+#endif
 #define STB_IMAGE_WRITE_STATIC
 #define STB_IMAGE_WRITE_IMPLEMENTATION
 #include "stb_image_write.h"

+ 7 - 3
tests/resample_test.cpp

@@ -1,3 +1,4 @@
+#define _CRT_SECURE_NO_WARNINGS
 #include <stdlib.h>
 #include <stdio.h>
 
@@ -56,8 +57,11 @@ void stbir_progress(float p)
 	STBIR_ASSERT(p >= 0 && p <= 1);
 }
 
-#define STBIR_PROGRESS_REPORT stbir_progress
+#ifdef __clang__
+#define STBIRDEF static inline
+#endif
 
+#define STBIR_PROGRESS_REPORT stbir_progress
 #define STB_IMAGE_RESIZE_IMPLEMENTATION
 #define STB_IMAGE_RESIZE_STATIC
 #include "stb_image_resize.h"
@@ -127,7 +131,7 @@ inline float mtfrand()
 	return (float)(mtrand() % ninenine)/ninenine;
 }
 
-static void resizer(int argc, char **argv)
+void resizer(int argc, char **argv)
 {
 	unsigned char* input_pixels;
 	unsigned char* output_pixels;
@@ -144,7 +148,7 @@ static void resizer(int argc, char **argv)
 	exit(0);
 }
 
-static void performance(int argc, char **argv)
+void performance(int argc, char **argv)
 {
 	unsigned char* input_pixels;
 	unsigned char* output_pixels;

+ 32 - 30
tests/stb.c

@@ -2,6 +2,7 @@
  * Unit tests for "stb.h"
  */
 
+#define _CRT_SECURE_NO_WARNINGS
 //#include <windows.h>
 #include <stdio.h>
 #include <string.h>
@@ -110,8 +111,8 @@ void test_classes(void)
     while ((psize % s) > (psize >> 3)) {
       psize += kPageSize;
     }
-    class_to_pages[cl] = psize >> kPageShift;
-    wasted_pages += psize;
+    class_to_pages[cl] = (int) (psize >> kPageShift);
+    wasted_pages += (int) psize;
   }
 
   printf("TCMalloc can waste as much as %d memory on one-shot allocations\n", wasted_pages);
@@ -356,10 +357,10 @@ void test_threads2(void)
 
 char tc[] = "testing compression test quick test voila woohoo what the hell";
 
-char storage1[1 << 23];
-int test_compression(char *buffer, int length)
+unsigned char storage1[1 << 23];
+int test_compression(unsigned char *buffer, int length)
 {
-   char *storage2;
+   unsigned char *storage2;
    int c_len = stb_compress(storage1, buffer, length);
    int dc_len;
    printf("Compressed %d to %d\n", length, c_len);
@@ -421,7 +422,7 @@ stb_uint stb_adler32_old(stb_uint adler32, stb_uchar *buffer, stb_uint buflen)
    return (s2 << 16) + s1;
 }
 
-static int sample_test[3][5] =
+int sample_test[3][5] =
 {
    { 1,2,3,4,5 },
    { 6,7,8,9,10, },
@@ -1579,10 +1580,10 @@ struct
    char *digest;
 } sha1_tests[] =
 {
-   24,
+{   24,
 "616263",
 "a9993e364706816aba3e25717850c26c9cd0d89d",
-
+},{
    1304,
 "ec29561244ede706b6eb30a1c371d74450a105c3f9735f7fa9fe38cf67f304a5736a106e"
 "92e17139a6813b1c81a4f3d3fb9546ab4296fa9f722826c066869edacd73b25480351858"
@@ -1590,7 +1591,7 @@ struct
 "e31cb413ae29810fd794cad5dfaf29ec43cb38d198fe4ae1da2359780221405bd6712a53"
 "05da4b1b737fce7cd21c0eb7728d08235a9011",
 "970111c4e77bcc88cc20459c02b69b4aa8f58217",
-
+},{
    2096,
 "5fc2c3f6a7e79dc94be526e5166a238899d54927ce470018fbfd668fd9dd97cbf64e2c91"
 "584d01da63be3cc9fdff8adfefc3ac728e1e335b9cdc87f069172e323d094b47fa1e652a"
@@ -1601,7 +1602,7 @@ struct
 "e485050e4bbb6235574fc0102be8f7a306d6e8de6ba6becf80f37415b57f9898a5824e77"
 "414197422be3d36a6080",
    "0423dc76a8791107d14e13f5265b343f24cc0f19",
-
+},{
    2888,
 "0f865f46a8f3aed2da18482aa09a8f390dc9da07d51d1bd10fe0bf5f3928d5927d08733d"
 "32075535a6d1c8ac1b2dc6ba0f2f633dc1af68e3f0fa3d85e6c60cb7b56c239dc1519a00"
@@ -1615,7 +1616,7 @@ struct
 "6afbb27dbbd300477d70c371e7b8963812f5ed4fb784fb2f3bd1d3afe883cdd47ef32bea"
 "ea",
    "6692a71d73e00f27df976bc56df4970650d90e45",
-
+},{
    3680,
 "4893f1c763625f2c6ce53aacf28026f14b3cd8687e1a1d3b60a81e80fcd1e2b038f9145a"
 "b64a0718f948f7c3c9ac92e3d86fb669a5257da1a18c776291653688338210a3242120f1"
@@ -1631,7 +1632,7 @@ struct
 "73c30b39920e726fe861b72483a3f886269ab7a8eefe952f35d25c4eb7f443f4f3f26e43"
 "d51fb54591e6a6dad25fcdf5142033084e5624bdd51435e77dea86b8",
    "dc5859dd5163c4354d5d577b855fa98e37f04384",
-
+},{
    4472,
 "cf494c18a4e17bf03910631471bca5ba7edea8b9a63381e3463517961749848eb03abefd"
 "4ce676dece3740860255f57c261a558aa9c7f11432f549a9e4ce31d8e17c79450ce2ccfc"
@@ -1650,7 +1651,7 @@ struct
 "317746270599424b9248791a0780449c1eabbb9459cc1e588bfd74df9b1b711c85c09d8a"
 "a171b309281947e8f4b6ac438753158f4f36fa",
    "4c17926feb6e87f5bca7890d8a5cde744f231dab",
-
+},{
    5264,
 "8236153781bd2f1b81ffe0def1beb46f5a70191142926651503f1b3bb1016acdb9e7f7ac"
 "ced8dd168226f118ff664a01a8800116fd023587bfba52a2558393476f5fc69ce9c65001"
@@ -1672,7 +1673,7 @@ struct
 "5cf62845c2009980a4dfa69fbc7e5a0b1bb20a5958ca967aec68eb31dd8fccca9afcd30a"
 "26bab26279f1bf6724ff",
    "11863b483809ef88413ca9b0084ac4a5390640af",
-
+},{
    6056,
 "31ec3c3636618c7141441294fde7e72366a407fa7ec6a64a41a7c8dfda150ca417fac868"
 "1b3c5be253e3bff3ab7a5e2c01b72790d95ee09b5362be835b4d33bd20e307c3c702aa15"
@@ -1697,7 +1698,7 @@ struct
 "386719b28a09a8e5934722202beb3429899b016dfeb972fee487cdd8d18f8a681042624f"
 "51",
    "f43937922444421042f76756fbed0338b354516f",
-
+},{
    6848,
 "21b9a9686ec200456c414f2e6963e2d59e8b57e654eced3d4b57fe565b51c9045c697566"
 "44c953178f0a64a6e44d1b46f58763c6a71ce4c373b0821c0b3927a64159c32125ec916b"
@@ -1724,7 +1725,7 @@ struct
 "aac4130293f6f62ae6d50c0d0c3b9876f0728923a94843785966a27555dd3ce68602e7d9"
 "0f7c7c552f9bda4969ec2dc3e30a70620db6300e822a93e633ab9a7a",
    "5d4d18b24b877092188a44be4f2e80ab1d41e795",
-
+},{
    7640,
 "1c87f48f4409c3682e2cf34c63286dd52701b6c14e08669851a6dc8fa15530ad3bef692c"
 "7d2bf02238644561069df19bdec3bccae5311fce877afc58c7628d08d32d9bd2dc1df0a6"
@@ -1754,7 +1755,7 @@ struct
 "e147c723aca999015230d22c917730b935e902092f83e0a8e6db9a75d2626e0346e67e40"
 "8d5b815439dab8ccb8ea23f828fff6916c4047",
    "32e0f5d40ceec1fbe45ddd151c76c0b3fef1c938",
-
+},{
    8432,
 "084f04f8d44b333dca539ad2f45f1d94065fbb1d86d2ccf32f9486fe98f7c64011160ec0"
 "cd66c9c7478ed74fde7945b9c2a95cbe14cedea849978cc2d0c8eb0df48d4834030dfac2"
@@ -1787,7 +1788,7 @@ struct
 "b5200b7892554b59532ac63af3bdef590b57bd5df4fbf38d2b3fa540fa5bf89455802963"
 "036bd173fe3967ed1b7d",
    "ee976e4ad3cad933b283649eff9ffdb41fcccb18",
-
+},{
    9224,
 "bd8320703d0cac96a96aeefa3abf0f757456bf42b3e56f62070fc03e412d3b8f4e4e427b"
 "c47c4600bb423b96de6b4910c20bc5c476c45feb5b429d4b35088813836fa5060ceb26db"
@@ -1823,7 +1824,7 @@ struct
 "42a756a3e22123cbf38c429373c6a8663130c24b24b2690b000013960b1c46a32d1d5397"
 "47",
    "2df09b10933afedfcd3f2532dfd29e7cb6213859",
-
+},{
    10016,
 "7a94978bec7f5034b12c96b86498068db28cd2726b676f54d81d8d7350804cc106bead8a"
 "252b465a1f413b1c41e5697f8cece49ec0dea4dfb9fa7b1bfe7a4a00981875b420d094bb"
@@ -1861,7 +1862,7 @@ struct
 "5740090c1b165ecae7dec0b341d60a88f46d7ad8624aac231a90c93fad61fcfbbea12503"
 "59fcd203862a6b0f1d71ac43db6c58a6b60c2c546edc12dd658998e8",
    "f32e70862a16e3e8b199e9d81a9949d66f812cad",
-
+},{
    10808,
 "88dd7f273acbe799219c23184782ac0b07bade2bc46b4f8adbd25ed3d59c0fd3e2931638"
 "837d31998641bbb7374c7f03d533ca60439ac4290054ff7659cc519bdda3dff2129a7bdb"
@@ -1902,7 +1903,7 @@ struct
 "1fd561274392ee6ee1a14424d5c134a69bcb4333079400f03615952fc4c99bf03f5733a8"
 "dc71524269fc5c648371f5f3098314d9d10258",
    "08632c75676571a5db5971f5d99cb8de6bf1792a",
-
+},{
    11600,
 "85d43615942fcaa449329fd1fe9efb17545eb252cac752228f1e9d90955a3cf4e72cb116"
 "3c3d8e93ccb7e4826206ff58b3e05009ee82ab70943db3f18a32925d6d5aed1525c91673"
@@ -1946,7 +1947,7 @@ struct
 "b16dcde529248a477628067d13d0cb3bf51776f4d39fb3fbc5f669e91019323e40360e4b"
 "78b6584f077bf9e03b66",
    "ab7213f6becb980d40dc89fbda0ca39f225a2d33",
-
+},{
    12392,
 "7ae3ca60b3a96be914d24980fb5652eb68451fed5fa47abe8e771db8301fbd5331e64753"
 "93d96a4010d6551701e5c23f7ecb33bec7dd7bade21381e9865d410c383a139cb4863082"
@@ -1993,7 +1994,7 @@ struct
 "09d1af296eb3121d782650e7d038063bab5fa854aac77de5ffebeb53d263f521e3fc02ac"
 "70",
    "b0e15d39025f5263e3efa255c1868d4a37041382",
-
+},{
    13184,
 "fa922061131282d91217a9ff07463843ae34ff7f8c28b6d93b23f1ea031d5020aa92f660"
 "8c3d3df0ee24a8958fd41af880ee454e36e26438defb2de8f09c018607c967d2f0e8b80a"
@@ -2042,7 +2043,7 @@ struct
 "b409cc322354672a21ea383e870d047551a3af71aaf2f44f49a859cf001e61b592dd036f"
 "c6625bf7b91ea0fb78c1563cceb8c4345bf4a9fbe6ee2b6bf5e81083",
    "8b6d59106f04300cb57c7c961945cd77f3536b0a",
-
+},{
    13976,
 "162cca41155de90f6e4b76a34261be6666ef65bdb92b5831b47604ce42e0c6c8d2eda265"
 "ab9a3716809bf2e745e7831a41768d0f6349a268d9ac6e6adfb832a5d51b75d7951cf60e"
@@ -2094,7 +2095,7 @@ struct
 "7635eed03558ac673d17280769b2368056276d5d72f5dbc75525f8a7558bd90b544aa6cb"
 "dd964e6c70be79441969bfdf471f17a2dc0c92",
    "6144c4786145852e2a01b20604c369d1b9721019",
-
+},{
    14768,
 "c9bed88d93806b89c2d028866842e6542ab88c895228c96c1f9f05125f8697c7402538b0"
 "6465b7ae33daef847500f73d20c598c86e4804e633e1c4466e61f3ed1e9baadc5723bbed"
@@ -2149,6 +2150,7 @@ struct
 "f5e1f03c2280e71c6e1ae21312d4ff163eee16ebb1fdee8e887bb0d453829b4e6ed5fa70"
 "8f2053f29b81e277be46",
    "a757ead499a6ec3d8ab9814f839117354ae563c8"
+}
 };
 
 void test_sha1(void)
@@ -2257,14 +2259,14 @@ void do_compressor(int argc,char**argv)
 {
    char *p;
    size_t slen;
-   int len;
+   unsigned int len;
 
    int window;
    if (argc == 2) {
       p = stb_file(argv[1], &slen);
-      len = (int) slen;
+      len = (unsigned int) slen;
       if (p) {
-         int dlen, clen = stb_compress_tofile("data/dummy.bin", p, len);
+         unsigned int dlen, clen = stb_compress_tofile("data/dummy.bin", p, len);
          char *q = stb_decompress_fromfile("data/dummy.bin", &dlen);
 
          if (len != dlen) {
@@ -2398,7 +2400,7 @@ done:
 
 
 
-
+#if 0 // parser generator
 //////////////////////////////////////////////////////////////////////////
 //
 //   stb_parser
@@ -2946,7 +2948,7 @@ void test_parser_generator(void)
    parser_destroy(p);
 }
 #endif
-
+#endif // parser generator
 
 #if 0
 // stb_threadtest.c

+ 7 - 7
tests/stb_cpp.cpp

@@ -11,7 +11,7 @@
 //#include "stb_file.h"
 
 int count;
-void c(int truth, char *error)
+void c(int truth, const char *error)
 {
    if (!truth) {
       fprintf(stderr, "Test failed: %s\n", error);
@@ -19,7 +19,7 @@ void c(int truth, char *error)
    }
 }
 
-char *expects(stb_matcher *m, char *s, int result, int len, char *str)
+char *expects(stb_matcher *m, char *s, int result, int len, const char *str)
 {
    int res2,len2=0;
    res2 = stb_lex(m, s, &len2);
@@ -31,7 +31,7 @@ void test_lex(void)
 {
    stb_matcher *m = stb_lex_matcher();
    //         tok_en5 .3 20.1 20. .20 .1
-   char *s = "tok_en5.3 20.1 20. .20.1";
+   char *s = (char*) "tok_en5.3 20.1 20. .20.1";
 
    stb_lex_item(m, "[a-zA-Z_][a-zA-Z0-9_]*", 1   );
    stb_lex_item(m, "[0-9]*\\.?[0-9]*"      , 2   );
@@ -53,22 +53,22 @@ void test_lex(void)
 int main(int argc, char **argv)
 {
    char *p;
-   p = "abcdefghijklmnopqrstuvwxyz";
+   p = (char*) "abcdefghijklmnopqrstuvwxyz";
    c(stb_ischar('c', p), "stb_ischar 1");
    c(stb_ischar('x', p), "stb_ischar 2");
    c(!stb_ischar('#', p), "stb_ischar 3");
    c(!stb_ischar('X', p), "stb_ischar 4");
-   p = "0123456789";
+   p = (char*) "0123456789";
    c(!stb_ischar('c', p), "stb_ischar 5");
    c(!stb_ischar('x', p), "stb_ischar 6");
    c(!stb_ischar('#', p), "stb_ischar 7");
    c(!stb_ischar('X', p), "stb_ischar 8");
-   p = "#####";
+   p = (char*) "#####";
    c(!stb_ischar('c', p), "stb_ischar a");
    c(!stb_ischar('x', p), "stb_ischar b");
    c(stb_ischar('#', p), "stb_ischar c");
    c(!stb_ischar('X', p), "stb_ischar d");
-   p = "xXyY";
+   p = (char*) "xXyY";
    c(!stb_ischar('c', p), "stb_ischar e");
    c(stb_ischar('x', p), "stb_ischar f");
    c(!stb_ischar('#', p), "stb_ischar g");

+ 12 - 0
tests/stb_static.c

@@ -0,0 +1,12 @@
+#define STBI_WINDOWS_UTF8
+#define STB_IMAGE_STATIC
+#define STB_IMAGE_IMPLEMENTATION
+#include "stb_image.h"
+
+#define STB_IMAGE_WRITE_STATIC
+#define STB_IMAGE_WRITE_IMPLEMENTATION
+//#include "stb_image_write.h"
+
+#define STBTT_STATIC
+#define STB_TRUETYPE_IMPLEMENTATION
+#include "stb_truetype.h"

+ 7 - 1
tests/stretchy_buffer_test.c

@@ -1 +1,7 @@
-#include "stretchy_buffer.h"
+#include "stretchy_buffer.h"
+
+void test_sb(void)
+{
+   char *x = NULL;
+   sb_push(x, 'x');
+}

+ 37 - 0
tests/test.sbm

@@ -0,0 +1,37 @@
+[compilers]
+VS2015 for x64, vcvars_2015_x64
+VC6           , vcvars_vc6
+VS2008 for x86, vcvars_2008_x86
+VS2013 for x86, vcvars_2013_x86
+VS2015 for x86, vcvars_2015_x86
+clang-cl for x64, vcvars_2015_x64, clang-cl
+clang-cl for x86, vcvars_2015_x86, clang32-cl
+#these batch files don't work on my machine
+#VS2008 for x64, vcvars_2008_x64
+#VS2013 for x64, vcvars_2013_x64
+
+[args]
+-I .. -W3 -WX -D_DEBUG
+
+[link]
+advapi32.lib
+
+[projects]
+c_lexer_test.c
+image_test.c image_write_test.c
+test_cpp_compilation.cpp stb_cpp.cpp ../stb_vorbis.c
+resample_test.cpp 
+-DTT_TEST test_c_compilation.c test_truetype.c 
+main.c stb.c 
+main.c stretchy_buffer_test.c
+main.c test_c_compilation.c
+main.c test_c_lexer.c
+main.c test_dxt.c
+main.c test_easyfont.c
+main.c test_image.c
+main.c test_image_write.c
+main.c test_perlin.c
+main.c test_sprintf.c
+main.c test_vorbis.c ../stb_vorbis.c
+main.c test_voxel.c
+main.c textedit_sample.c

+ 23 - 4
tests/test_cpp_compilation.cpp

@@ -3,6 +3,7 @@
 #include "stb_sprintf.h"
 
 #define STB_IMAGE_WRITE_STATIC
+#define STBIWDEF static inline
 
 #define STB_IMAGE_WRITE_IMPLEMENTATION
 #define STB_TRUETYPE_IMPLEMENTATION
@@ -57,9 +58,18 @@ void my_free(void *) { }
 #define STB_IMAGE_RESIZE_IMPLEMENTATION
 #include "stb_image_resize.h"
 
-#include "stretchy_buffer.h"
+//#include "stretchy_buffer.h"  // deprecating
 
 
+// avoid unused-function complaints
+void dummy2(void)
+{
+   stb_easy_font_spacing(1.0);
+   stb_easy_font_print(0,0,NULL,NULL,NULL,0);
+   stb_easy_font_width(NULL);
+   stb_easy_font_height(NULL);
+}
+
 
 ////////////////////////////////////////////////////////////
 //
@@ -113,7 +123,7 @@ int insert_chars(STB_TEXTEDIT_STRING *str, int pos, STB_TEXTEDIT_CHARTYPE *newte
 
 // define all the #defines needed 
 
-#define KEYDOWN_BIT                    0x80000000
+#define KEYDOWN_BIT                    0x40000000
 
 #define STB_TEXTEDIT_STRINGLEN(tc)     ((tc)->stringlen)
 #define STB_TEXTEDIT_LAYOUTROW         layout_func
@@ -125,8 +135,8 @@ int insert_chars(STB_TEXTEDIT_STRING *str, int pos, STB_TEXTEDIT_CHARTYPE *newte
 #define STB_TEXTEDIT_DELETECHARS       delete_chars
 #define STB_TEXTEDIT_INSERTCHARS       insert_chars
 
-#define STB_TEXTEDIT_K_SHIFT           0x40000000
-#define STB_TEXTEDIT_K_CONTROL         0x20000000
+#define STB_TEXTEDIT_K_SHIFT           0x20000000
+#define STB_TEXTEDIT_K_CONTROL         0x10000000
 #define STB_TEXTEDIT_K_LEFT            (KEYDOWN_BIT | 1) // actually use VK_LEFT, SDLK_LEFT, etc
 #define STB_TEXTEDIT_K_RIGHT           (KEYDOWN_BIT | 2) // VK_RIGHT
 #define STB_TEXTEDIT_K_UP              (KEYDOWN_BIT | 3) // VK_UP
@@ -149,3 +159,12 @@ int insert_chars(STB_TEXTEDIT_STRING *str, int pos, STB_TEXTEDIT_CHARTYPE *newte
 #include "stb_textedit.h"
 
 
+void dummy3(void)
+{
+  stb_textedit_click(0,0,0,0);
+  stb_textedit_drag(0,0,0,0);
+  stb_textedit_cut(0,0);
+  stb_textedit_key(0,0,0);
+  stb_textedit_initialize_state(0,0);
+  stb_textedit_paste(0,0,0,0);
+}

+ 641 - 29
tests/test_ds.c

@@ -1,7 +1,25 @@
+#ifdef DS_PERF
+#define _CRT_SECURE_NO_WARNINGS
+#define _CRT_NONSTDC_NO_DEPRECATE
+#define _CRT_NON_CONFORMING_SWPRINTFS
+
+
+//#define STBDS_INTERNAL_SMALL_BUCKET    // make 64-bit bucket fit both keys and hash bits
+//#define STBDS_SIPHASH_2_4     // performance test 1_3 against 2_4
+//#define STBDS_INTERNAL_BUCKET_START    // don't bother offseting differently within bucket for different hash values
+//#define STBDS_FLUSH_CACHE  (1u<<20) // do this much memory traffic to flush the cache between some benchmarking measurements
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#define STB_DEFINE
+#define STB_NO_REGISTRY
+#include "../stb.h"
 #include <stdio.h>
+#endif
 
 #ifdef DS_TEST
 #define STBDS_UNIT_TESTS
+#define STBDS_SMALL_BUCKET
 #endif
 
 #ifdef DS_STATS
@@ -13,7 +31,6 @@
 #include <assert.h>
 #endif
 
-//#define STBDS_SIPHASH_2_4
 #define STB_DS_IMPLEMENTATION
 #include "../stb_ds.h"
 
@@ -141,30 +158,27 @@ int main(int arg, char **argv)
 }
 #endif
 
-#ifdef DS_PERF
-#define WIN32_LEAN_AND_MEAN
-#include <windows.h>
-#define STB_DEFINE
-#define STB_NO_REGISTRY
-//#include "../stb.h"
 
+#ifdef DS_PERF
+//char *strdup(const char *foo) { return 0; }
+//int stricmp(const char *a, const char *b) { return 0; }
+//int strnicmp(const char *a, const char *b, size_t n) { return 0; }
 
-size_t t0, sum, mn,mx,count;
+unsigned __int64 t0, xsum, mn,mx,count;
 void begin(void)
 {
-  size_t t0;
   LARGE_INTEGER m;
   QueryPerformanceCounter(&m);
   t0 = m.QuadPart;
-  sum = 0;
+  xsum = 0;
   count = 0;
   mx = 0;
-  mn = ~(size_t) 0;
+  mn = ~(unsigned __int64) 0;
 }
 
 void measure(void)
 {
-  size_t t1, t;
+  unsigned __int64 t1, t;
   LARGE_INTEGER m;
   QueryPerformanceCounter(&m);
   t1 = m.QuadPart;
@@ -173,40 +187,42 @@ void measure(void)
     printf("ALERT: QueryPerformanceCounter was unordered!\n");
   if (t < mn) mn = t;
   if (t > mx) mx = t;
-  sum += t;
+  xsum += t;
   ++count;
   t0 = t1;
 }
 
 void dont_measure(void)
 {
-  size_t t1, t;
   LARGE_INTEGER m;
   QueryPerformanceCounter(&m);
   t0 = m.QuadPart;
 }
 
 double timer;
-void end(void)
+double end(void)
 {
   LARGE_INTEGER m;
   QueryPerformanceFrequency(&m);
 
   if (count > 3) {
     // discard the highest and lowest
-    sum -= mn;
-    sum -= mx;
+    xsum -= mn;
+    xsum -= mx;
     count -= 2;
   }
-  timer = (double) (sum) / count / m.QuadPart * 1000;
+  timer = (double) (xsum) / count / m.QuadPart * 1000;
+  return timer;
 }
 
 void build(int a, int b, int count, int step)
 {
   struct { int key,value; } *map=NULL;
-  int i,j,n,k;
-  for (i=0; i < a; ++i)
-    hmput(map,i*step,i+1);
+  int i,n;
+  for (i=0; i < a; ++i) {
+    n = i*step;
+    hmput(map,n,i+1);
+  }
   measure();
   churn_inserts = i;
   hmfree(map);
@@ -217,8 +233,7 @@ void build(int a, int b, int count, int step)
 void build_stb(int a, int b, int count, int step)
 {
   stb_idict *d = stb_idict_new_size(8);
-  struct { int key,value; } *map=NULL;
-  int i,j,n,k;
+  int i;
   for (i=0; i < a; ++i)
     stb_idict_add(d, i*step, i+1);
   measure();
@@ -226,8 +241,68 @@ void build_stb(int a, int b, int count, int step)
   stb_idict_destroy(d);
   dont_measure();
 }
+
+void multibuild_stb(int a, int b, int count, int step, int tables)
+{
+  stb_idict *d[50000];
+  int i,q;
+  for (q=0; q < tables; ++q)
+    d[q] = stb_idict_new_size(8);
+  dont_measure();
+  for (i=0; i < a; ++i)
+    for (q=0; q < tables; ++q)
+      stb_idict_add(d[q], i*step+q*771, i+1);
+  measure();
+  churn_inserts = i;
+  for (q=0; q < tables; ++q)
+    stb_idict_destroy(d[q]);
+  dont_measure();
+}
+
+int multisearch_stb(int a, int start, int end, int step, int tables)
+{
+  stb_idict *d[50000];
+  int i,q,total=0,v;
+  for (q=0; q < tables; ++q)
+    d[q] = stb_idict_new_size(8);
+  for (q=0; q < tables; ++q)
+    for (i=0; i < a; ++i)
+      stb_idict_add(d[q], i*step+q*771, i+1);
+  dont_measure();
+  for (i=start; i < end; ++i)
+    for (q=0; q < tables; ++q)
+      if (stb_idict_get_flag(d[q], i*step+q*771, &v))
+        total += v;
+  measure();
+  churn_inserts = i;
+  for (q=0; q < tables; ++q)
+    stb_idict_destroy(d[q]);
+  dont_measure();
+  return total;
+}
 #endif
 
+int multisearch(int a, int start, int end, int step, int tables)
+{
+  struct { int key,value; } *hash[50000];
+  int i,q,total=0;
+  for (q=0; q < tables; ++q)
+    hash[q] = NULL;
+  for (q=0; q < tables; ++q)
+    for (i=0; i < a; ++i)
+      hmput(hash[q], i*step+q*771, i+1);
+  dont_measure();
+  for (i=start; i < end; ++i)
+    for (q=0; q < tables; ++q)
+      total += hmget(hash[q], i*step+q*771);
+  measure();
+  churn_inserts = i;
+  for (q=0; q < tables; ++q)
+    hmfree(hash[q]);
+  dont_measure();
+  return total;
+}
+
 void churn_skip(unsigned int a, unsigned int b, int count)
 {
   struct { unsigned int key,value; } *map=NULL;
@@ -343,12 +418,526 @@ void churn8(int a, int b, int count, int include_startup)
   dont_measure();
 }
 
+void multichurn4(int a, int b, int count, int include_startup, int tables)
+{
+  struct { int key,value; } *map[50000];
+  int i,j,n,k,q;
+  for (q=0; q < tables; ++q)
+    map[q] = NULL;
+  dont_measure();
+
+  for (i=0; i < a; ++i)
+    for (q=0; q < tables; ++q)
+      hmput(map[q],i,i+1);
+  if (!include_startup)
+    dont_measure();
+  for (n=0; n < count; ++n) {
+    for (j=a; j < b; ++j,++i) {
+      for (q=0; q < tables; ++q)
+        hmput(map[q],i,i+1);
+    }
+    assert(hmlen(map[0]) == b);
+    for (j=a; j < b; ++j) {
+      k=i-j-1;
+      for (q=0; q < tables; ++q)
+        k = hmdel(map[q],k);
+      assert(k != 0);
+    }
+    assert(hmlen(map[0]) == a);
+  }
+  measure();
+  for (q=0; q < tables; ++q)
+    hmfree(map[q]);
+  churn_inserts = i * tables;
+  churn_deletes = (b-a) * n * tables;
+  dont_measure();
+}
+
+struct {
+  unsigned __int64 start;
+  unsigned __int64 end;
+  int    table_size;
+} mstats[32][4000];
+
+const int first_step = 64;
+const int last_step = 384-48; // 32M
+
+void measure_build4(int step_log2)
+{
+  double length;
+  int i,j,k=0;
+  int step = 1 << step_log2;
+  unsigned __int64 t0,t1;
+  struct { int key,value; } *map=NULL;
+  double rdtsc_scale;
+  begin();
+  t0 = __rdtsc();
+
+  mstats[0][0].start = __rdtsc();
+  for (i=0; i < 256; ++i) {
+    hmput(map,k,k+1);
+    k += step;
+  }
+  mstats[0][first_step-1].end = __rdtsc();
+  mstats[0][first_step-1].table_size = k >> step_log2;
+  for (j=first_step; j < last_step; ++j) {
+    for (i=0; i < (1<<(j>>4)); ++i) {
+      hmput(map, k,k+1);
+      k += step;
+    }
+    mstats[0][j].end = __rdtsc();
+    mstats[0][j].table_size = k >> step_log2;
+  }
+  t1 = __rdtsc();
+  measure();
+  hmfree(map);
+  length = end();
+  rdtsc_scale = length / (t1-t0) * 1000;
+
+  for (j=1; j < last_step; ++j)
+    mstats[0][j].start = mstats[0][0].start;
+  for (j=first_step-1; j < last_step; ++j) {
+    printf("%12.4f,%12d,%12d,0,0,0\n", (mstats[0][j].end - mstats[0][j].start) * rdtsc_scale, mstats[0][j].table_size, mstats[0][j].table_size);
+  }
+}
+
+#ifdef STBDS_FLUSH_CACHE
+static int cache_index;
+char dummy[8][STBDS_FLUSH_CACHE];
+
+int flush_cache(void)
+{
+  memmove(dummy[cache_index],dummy[cache_index]+1, sizeof(dummy[cache_index])-1);
+  cache_index = (cache_index+1)%8;
+  return dummy[cache_index][0];
+}
+#else
+int flush_cache(void) { return 0; }
+#endif
+
+int measure_average_lookup4(int step_log2)
+{
+  int total;
+  double length;
+  int i,j,k=0,q;
+  int step = 1 << step_log2;
+  unsigned __int64 t0,t1;
+  struct { int key,value; } *map=NULL;
+  double rdtsc_scale;
+  begin();
+  t0 = __rdtsc();
+
+  for (i=0; i < 128; ++i) {
+    hmput(map,k,k+1);
+    k += step;
+  }
+  for (j=first_step; j <= last_step; ++j) {
+    total += flush_cache();
+    mstats[0][j].start = __rdtsc();
+    for (q=i=0; i < 50000; ++i) {
+       total += hmget(map, q); // hit
+       if (++q == k) q = 0;
+    }
+    mstats[0][j].end = __rdtsc();
+    mstats[0][j].table_size = k;
+    total += flush_cache();
+    mstats[1][j].start = __rdtsc();
+    for (i=0; i < 50000; ++i) {
+       total += hmget(map, i+k); // miss
+    }
+    mstats[1][j].end = __rdtsc();
+    mstats[1][j].table_size = k;
+
+    // expand table
+    for (i=0; i < (1<<(j>>4)); ++i) {
+      hmput(map, k,k+1);
+      k += step;
+    }
+  }
+
+  t1 = __rdtsc();
+  measure();
+  hmfree(map);
+  length = end();
+  rdtsc_scale = length / (t1-t0) * 1000;
+
+  for (j=first_step; j <= last_step; ++j) {
+    // time,table_size,numins,numhit,nummiss,numperflush
+    printf("%12.4f,%12d,0,50000,0,0\n", (mstats[0][j].end - mstats[0][j].start) * rdtsc_scale, mstats[0][j].table_size);
+  }
+  for (j=first_step; j <= last_step; ++j) {
+    printf("%12.4f,%12d,0,0,50000,0\n", (mstats[1][j].end - mstats[1][j].start) * rdtsc_scale, mstats[1][j].table_size);
+  }
+  return total;
+}
+
+int measure_worst_lookup4_a(int step_log2)
+{
+  int total;
+  double length;
+  int i,j,k=0,q,worst_q,n,z,attempts;
+  int step = 1 << step_log2;
+  unsigned __int64 t0,t1;
+  unsigned __int64 m0,m1,worst;
+  struct { int key,value; } *map=NULL;
+  double rdtsc_scale;
+  begin();
+  t0 = __rdtsc();
+
+  memset(mstats, 0, sizeof(mstats));
+  for (j=first_step; j <= last_step; ++j)
+    mstats[0][j].end = mstats[1][j].end = ~(unsigned __int64) 0;
+
+  for(attempts=0; attempts < 2; ++attempts) {
+    k = 0;
+    stbds_rand_seed(0); // force us to get the same table every time
+    for (i=0; i < 128; ++i) {
+      hmput(map,k,k+1);
+      k += step;
+    }
+    for (j=first_step; j <= last_step; ++j) {
+      unsigned __int64 times[32];
+
+      // find the worst hit time
+      for (z=0; z < 2; ++z) { // try the bisectioning measurement 4 times
+        worst = 0;
+        for (n=0; n < 10; ++n) { // test 400 keys total
+          // find the worst time to hit 20 keys
+          q=0;
+          worst_q = 0;
+          total += flush_cache();
+          m0 = __rdtsc();
+          for (i=0; i < 20; ++i) {
+            total += hmget(map, q); // hit
+            if (++q == k) q = 0;
+          }
+          m1 = __rdtsc();
+          // for each n, check if this is the worst lookup we've seen
+          if (m1 - m0 > worst) {
+            worst = m1-m0;
+            worst_q = q - i;
+            if (worst_q < 0) q += k;
+          }
+        }
+        // after 400 keys, take the worst 20 keys, and try each one
+        worst = 0;
+        q = worst_q;
+        for (i=0; i < 20; ++i) {
+          total += flush_cache();
+          m0 = __rdtsc();
+          total += hmget(map, q); // hit
+          m1 = __rdtsc();
+          if (m1 - m0 > worst)
+            worst = m1-m0;
+          if (++q == k) q = 0;
+        }
+        times[z] = worst;
+      }
+      // find the worst time in the bunch
+      worst = 0;
+      for (i=0; i < z; ++i)
+        if (times[i] > worst)
+          worst = times[i];
+      // take the best of 'attempts', to discard outliers
+      if (worst < mstats[0][j].end)
+        mstats[0][j].end = worst;
+      mstats[0][j].start = 0;
+      mstats[0][j].table_size = k >> step_log2;
+
+      // find the worst miss time
+      for (z=0; z < 8; ++z) { // try the bisectioning measurement 8 times
+        worst = 0;
+        for (n=0; n < 20; ++n) { // test 400 keys total
+          // find the worst time to hit 20 keys
+          q=k;
+          worst_q = 0;
+          total += flush_cache();
+          m0 = __rdtsc();
+          for (i=0; i < 20; ++i) {
+            total += hmget(map, q); // hit
+          }
+          m1 = __rdtsc();
+          // for each n, check if this is the worst lookup we've seen
+          if (m1 - m0 > worst) {
+            worst = m1-m0;
+            worst_q = q - i;
+          }
+        }
+        // after 400 keys, take the worst 20 keys, and try each one
+        worst = 0;
+        q = worst_q;
+        for (i=0; i < 20; ++i) {
+          total += flush_cache();
+          m0 = __rdtsc();
+          total += hmget(map, q); // hit
+          m1 = __rdtsc();
+          if (m1 - m0 > worst)
+            worst = m1-m0;
+        }
+        times[z] = worst;
+      }
+      // find the worst time in the bunch
+      worst = 0;
+      for (i=0; i < z; ++i)
+        if (times[i] > worst)
+          worst = times[i];
+      if (worst < mstats[1][j].end)
+        mstats[1][j].end = worst;
+      mstats[1][j].start = 0;
+      mstats[1][j].table_size = k >> step_log2;
+
+      // expand table
+      for (i=0; i < (1<<(j>>4)); ++i) {
+        hmput(map, k,k+1);
+        k += step;
+      }
+    }
+    hmfree(map);
+  }
+
+  t1 = __rdtsc();
+  measure();
+  length = end();
+  rdtsc_scale = length / (t1-t0) * 1000;
+
+  for (j=first_step; j <= last_step; ++j) {
+    printf("%12.4f,%12d,0,1,0,1\n", (mstats[0][j].end - mstats[0][j].start) * rdtsc_scale, mstats[0][j].table_size);
+  }
+  for (j=first_step; j <= last_step; ++j) {
+    printf("%12.4f,%12d,0,0,1,1\n", (mstats[1][j].end - mstats[1][j].start) * rdtsc_scale, mstats[1][j].table_size);
+  }
+  return total;
+}
+
+int measure_worst_lookup4_b(int step_log2)
+{
+  int total;
+  double length;
+  int i,j,k=0,q,worst_q,n,z,attempts;
+  int step = 1 << step_log2;
+  unsigned __int64 t0,t1;
+  unsigned __int64 m0,m1,worst;
+  struct { int key,value; } *map=NULL;
+  double rdtsc_scale;
+  begin();
+  t0 = __rdtsc();
+
+  memset(mstats, 0, sizeof(mstats));
+  for (j=first_step; j <= last_step; ++j)
+    mstats[0][j].end = mstats[1][j].end = ~(unsigned __int64) 0;
+
+  k = 0;
+  stbds_rand_seed(0); // force us to get the same table every time
+  for (i=0; i < 128; ++i) {
+    hmput(map,k,k+1);
+    k += step;
+  }
+  for (j=first_step; j <= last_step; ++j) {
+    unsigned __int64 times[32];
+
+    // find the worst hit time
+    for (z=0; z < 8; ++z) { // try this 8 times
+      worst = 0;
+      q=0;
+      for (i=0; i < 5000; ++i) {
+        total += hmget(map, q);
+        m0 = __rdtsc();
+        total += hmget(map, q);
+        m1 = __rdtsc();
+        if (m1 - m0 > worst) {
+          worst = m1-m0;
+          worst_q = q - i;
+        }
+        if (++q == k) q = 0;
+      }
+      // now retry with the worst one, but find the shortest time for it
+      worst = ~(unsigned __int64) 0;
+      for (i=0; i < 4; ++i) {
+        total += flush_cache();
+        m0 = __rdtsc();
+        total += hmget(map,worst_q);
+        m1 = __rdtsc();
+        if (m1-m0 < worst)
+          worst = m1-m0;
+      }
+      times[z] = worst;
+    }
+
+    // find the worst of those
+    worst = 0;
+    for (i=0; i < z; ++i)
+      if (times[i] > worst)
+        worst = times[i];
+    mstats[0][j].start = 0;
+    mstats[0][j].end = worst;
+    mstats[0][j].table_size = k;
+
+    // find the worst miss time
+    for (z=0; z < 8; ++z) { // try this 8 times
+      worst = 0;
+      q=k;
+      for (i=0; i < 5000; ++i) {
+        total += hmget(map, q);
+        m0 = __rdtsc();
+        total += hmget(map, q);
+        m1 = __rdtsc();
+        if (m1 - m0 > worst) {
+          worst = m1-m0;
+          worst_q = q - i;
+        }
+        //printf("%6llu ", m1-m0);
+      }
+      // now retry with the worst one, but find the shortest time for it
+      worst = ~(unsigned __int64) 0;
+      for (i=0; i < 4; ++i) {
+        total += flush_cache();
+        m0 = __rdtsc();
+        total += hmget(map,worst_q);
+        m1 = __rdtsc();
+        if (m1-m0 < worst)
+          worst = m1-m0;
+      }
+      times[z] = worst;
+    }
+
+    // find the worst of those
+    worst = 0;
+    for (i=0; i < z; ++i)
+      if (times[i] > worst)
+        worst = times[i];
+    mstats[1][j].start = 0;
+    mstats[1][j].end = worst;
+    mstats[1][j].table_size = k;
+
+    // expand table
+    for (i=0; i < (1<<(j>>4)); ++i) {
+      hmput(map, k,k+1);
+      k += step;
+    }
+  }
+  hmfree(map);
+
+  t1 = __rdtsc();
+  measure();
+  length = end();
+  rdtsc_scale = length / (t1-t0) * 1000;
+
+  for (j=first_step+1; j <= last_step; ++j) {
+    printf("%12.4f,%12d,0,1,0,1\n", (mstats[0][j].end - mstats[0][j].start) * rdtsc_scale, mstats[0][j].table_size);
+  }
+  for (j=first_step+1; j <= last_step; ++j) {
+    printf("%12.4f,%12d,0,0,1,1\n", (mstats[1][j].end - mstats[1][j].start) * rdtsc_scale, mstats[1][j].table_size);
+  }
+  return total;
+}
+
+int measure_uncached_lookup4(int step_log2)
+{
+  int total;
+  double length;
+  int i,j,k=0,q;
+  int step = 1 << step_log2;
+  unsigned __int64 t0,t1;
+  struct { int key,value; } *map=NULL;
+  double rdtsc_scale;
+  begin();
+  t0 = __rdtsc();
+
+  map = NULL;
+  for (i=0; i < 128; ++i) {
+    hmput(map,k,k+1);
+    k += step;
+  }
+  for (j=first_step; j <= last_step; ++j) {
+    mstats[0][j].start = __rdtsc();
+    mstats[0][j].end = 0;
+    for (q=i=0; i < 512; ++i) {
+      if ((i & 3) == 0) {
+        mstats[0][j].end += __rdtsc();
+        total += flush_cache();
+        mstats[0][j].start += __rdtsc();
+      }
+      total += hmget(map, q); // hit
+      if (++q == k) q = 0;
+    }
+    mstats[0][j].end += __rdtsc();
+    mstats[0][j].table_size = k;
+    total += flush_cache();
+    mstats[1][j].end = 0;
+    mstats[1][j].start = __rdtsc();
+    for (i=0; i < 512; ++i) {
+      if ((i & 3) == 0) {
+        mstats[1][j].end += __rdtsc();
+        total += flush_cache();
+        mstats[1][j].start += __rdtsc();
+      }
+      total += hmget(map, i+k); // miss
+    }
+    mstats[1][j].end += __rdtsc();
+    mstats[1][j].table_size = k;
+
+    // expand table
+    for (i=0; i < (1<<(j>>4)); ++i) {
+      hmput(map, k,k+1);
+      k += step;
+    }
+  }
+  hmfree(map);
+
+  t1 = __rdtsc();
+  measure();
+  length = end();
+  rdtsc_scale = length / (t1-t0) * 1000;
+
+  for (j=first_step; j <= last_step; ++j) {
+    printf("%12.4f,%12d,0,512,0,4\n", (mstats[0][j].end - mstats[0][j].start) * rdtsc_scale, mstats[0][j].table_size);
+  }
+  for (j=first_step; j <= last_step; ++j) {
+    printf("%12.4f,%12d,0,0,512,4\n", (mstats[1][j].end - mstats[1][j].start) * rdtsc_scale, mstats[1][j].table_size);
+  }
+  return total;
+}
+
+
+
 
 int main(int arg, char **argv)
 {
   int n,s,w;
   double worst = 0;
 
+  printf("# size_t=%d,", (int) sizeof(size_t));
+
+// number of cache-lines
+#ifdef STBDS_SMALL_BUCKET
+  printf("cacheline=%d,", 1);
+#else
+  printf("cacheline=%d,", sizeof(size_t)==8 ? 2 : 1);
+#endif
+#ifdef STBDS_FLUSH_CACHE
+  printf("%d,", (int) stbds_log2(STBDS_FLUSH_CACHE));
+#else
+  printf("0,");
+#endif
+#ifdef STBDS_BUCKET_START    // don't bother offseting differently within bucket for different hash values
+  printf("STBDS_BUCKET_START,");
+#else
+  printf(",");
+#endif
+#ifdef STBDS_SIPHASH_2_4
+  printf("STBDS_SIPHASH_2_4,");
+#else
+  printf(",");
+#endif
+  printf("\n");
+
+  measure_worst_lookup4_b(0);
+  //measure_worst_lookup4_a(0);
+  measure_average_lookup4(0);
+  measure_uncached_lookup4(0);
+  measure_build4(0);
+  return 0;
+
 #if 0
   begin(); for (n=0; n < 2000; ++n) { build_stb(2000,0,0,1);          } end(); printf("  // %7.2fms :      2,000 inserts creating 2K table\n", timer);
   begin(); for (n=0; n <  500; ++n) { build_stb(20000,0,0,1);         } end(); printf("  // %7.2fms :     20,000 inserts creating 20K table\n", timer);
@@ -357,12 +946,6 @@ int main(int arg, char **argv)
   begin(); for (n=0; n <    5; ++n) { build_stb(20000000,0,0,1);      } end(); printf("  // %7.2fms : 20,000,000 inserts creating 20M table\n", timer);
 #endif
 
-  begin(); for (n=0; n < 2000; ++n) { churn8(2000,0,0,1);          } end(); printf("  // %7.2fms :      2,000 inserts creating 2K table w/ 8-byte key\n", timer);
-  begin(); for (n=0; n <  500; ++n) { churn8(20000,0,0,1);         } end(); printf("  // %7.2fms :     20,000 inserts creating 20K table w/ 8-byte key\n", timer);
-  begin(); for (n=0; n <  100; ++n) { churn8(200000,0,0,1);        } end(); printf("  // %7.2fms :    200,000 inserts creating 200K table w/ 8-byte key\n", timer);
-  begin(); for (n=0; n <   10; ++n) { churn8(2000000,0,0,1);       } end(); printf("  // %7.2fms :  2,000,000 inserts creating 2M table w/ 8-byte key\n", timer);
-  begin(); for (n=0; n <    5; ++n) { churn8(20000000,0,0,1);      } end(); printf("  // %7.2fms : 20,000,000 inserts creating 20M table w/ 8-byte key\n", timer);
-
 #if 0
   begin(); for (n=0; n < 2000; ++n) { churn32(2000,0,0,1);          } end(); printf("  // %7.2fms :      2,000 inserts creating 2K table w/ 32-byte key\n", timer);
   begin(); for (n=0; n <  500; ++n) { churn32(20000,0,0,1);         } end(); printf("  // %7.2fms :     20,000 inserts creating 20K table w/ 32-byte key\n", timer);
@@ -377,12 +960,41 @@ int main(int arg, char **argv)
   begin(); for (n=0; n <    5; ++n) { churn256(20000000,0,0,1);      } end(); printf("  // %7.2fms : 20,000,000 inserts creating 20M table w/ 256-byte key\n", timer);
 #endif
 
+  begin(); for (n=0; n <   20; ++n) { multisearch_stb(2000,0,2000,1,1000);    } end(); printf("  // %7.2fms : 2,000,000 hits on 1,000   2K table w/ 4-byte key\n", timer);
+  begin(); for (n=0; n <   10; ++n) { multisearch_stb(20000,0,2000,1,1000);   } end(); printf("  // %7.2fms : 2,000,000 hits on 1,000  20K table w/ 4-byte key\n", timer);
+  begin(); for (n=0; n <    6; ++n) { multisearch_stb(200000,0,2000,1,1000);  } end(); printf("  // %7.2fms : 2,000,000 hits on 1,000 200K table w/ 4-byte key\n", timer);
+  begin(); for (n=0; n <    2; ++n) { multisearch_stb(2000000,0,20000,1,100); } end(); printf("  // %7.2fms : 2,000,000 hits on   100   2M table w/ 4-byte key\n", timer);
+
+  begin(); for (n=0; n <   20; ++n) { multisearch    (2000,0,2000,1,1000);    } end(); printf("  // %7.2fms : 2,000,000 hits on 1,000   2K table w/ 4-byte key\n", timer);
+  begin(); for (n=0; n <   10; ++n) { multisearch    (20000,0,2000,1,1000);   } end(); printf("  // %7.2fms : 2,000,000 hits on 1,000  20K table w/ 4-byte key\n", timer);
+  begin(); for (n=0; n <    6; ++n) { multisearch    (200000,0,2000,1,1000);  } end(); printf("  // %7.2fms : 2,000,000 hits on 1,000 200K table w/ 4-byte key\n", timer);
+  begin(); for (n=0; n <    2; ++n) { multisearch    (2000000,0,20000,1,100); } end(); printf("  // %7.2fms : 2,000,000 hits on   100   2M table w/ 4-byte key\n", timer);
+
+
+#if 1
+  begin(); for (n=0; n <    2; ++n) { multibuild_stb(2000,0,0,1,10000); } end(); printf("  // %7.2fms : 20,000,000 inserts creating 10,000   2K table w/ 4-byte key\n", timer);
+  begin(); for (n=0; n <    2; ++n) { multibuild_stb(20000,0,0,1,1000); } end(); printf("  // %7.2fms : 20,000,000 inserts creating  1,000  20K table w/ 4-byte key\n", timer);
+  begin(); for (n=0; n <    2; ++n) { multibuild_stb(200000,0,0,1,100); } end(); printf("  // %7.2fms : 20,000,000 inserts creating    100 200K table w/ 4-byte key\n", timer);
+  begin(); for (n=0; n <    2; ++n) { multibuild_stb(2000000,0,0,1,10); } end(); printf("  // %7.2fms : 20,000,000 inserts creating     10   2M table w/ 4-byte key\n", timer);
+
+  begin(); for (n=0; n <    2; ++n) { multichurn4(2000,0,0,1,10000); } end(); printf("  // %7.2fms : 20,000,000 inserts creating 10,000   2K table w/ 4-byte key\n", timer);
+  begin(); for (n=0; n <    2; ++n) { multichurn4(20000,0,0,1,1000); } end(); printf("  // %7.2fms : 20,000,000 inserts creating  1,000  20K table w/ 4-byte key\n", timer);
+  begin(); for (n=0; n <    2; ++n) { multichurn4(200000,0,0,1,100); } end(); printf("  // %7.2fms : 20,000,000 inserts creating    100 200K table w/ 4-byte key\n", timer);
+  begin(); for (n=0; n <    2; ++n) { multichurn4(2000000,0,0,1,10); } end(); printf("  // %7.2fms : 20,000,000 inserts creating     10   2M table w/ 4-byte key\n", timer);
+#endif
+
   begin(); for (n=0; n < 2000; ++n) { build(2000,0,0,1);          } end(); printf("  // %7.2fms :      2,000 inserts creating 2K table w/ 4-byte key\n", timer);
   begin(); for (n=0; n <  500; ++n) { build(20000,0,0,1);         } end(); printf("  // %7.2fms :     20,000 inserts creating 20K table w/ 4-byte key\n", timer);
   begin(); for (n=0; n <  100; ++n) { build(200000,0,0,1);        } end(); printf("  // %7.2fms :    200,000 inserts creating 200K table w/ 4-byte key\n", timer);
   begin(); for (n=0; n <   10; ++n) { build(2000000,0,0,1);       } end(); printf("  // %7.2fms :  2,000,000 inserts creating 2M table w/ 4-byte key\n", timer);
   begin(); for (n=0; n <    5; ++n) { build(20000000,0,0,1);      } end(); printf("  // %7.2fms : 20,000,000 inserts creating 20M table w/ 4-byte key\n", timer);
 
+  begin(); for (n=0; n < 2000; ++n) { churn8(2000,0,0,1);          } end(); printf("  // %7.2fms :      2,000 inserts creating 2K table w/ 8-byte key\n", timer);
+  begin(); for (n=0; n <  500; ++n) { churn8(20000,0,0,1);         } end(); printf("  // %7.2fms :     20,000 inserts creating 20K table w/ 8-byte key\n", timer);
+  begin(); for (n=0; n <  100; ++n) { churn8(200000,0,0,1);        } end(); printf("  // %7.2fms :    200,000 inserts creating 200K table w/ 8-byte key\n", timer);
+  begin(); for (n=0; n <   10; ++n) { churn8(2000000,0,0,1);       } end(); printf("  // %7.2fms :  2,000,000 inserts creating 2M table w/ 8-byte key\n", timer);
+  begin(); for (n=0; n <    5; ++n) { churn8(20000000,0,0,1);      } end(); printf("  // %7.2fms : 20,000,000 inserts creating 20M table w/ 8-byte key\n", timer);
+
   begin(); for (n=0; n <   60; ++n) { churn_skip(2000,2100,5000);            } end(); printf("  // %7.2fms : 500,000 inserts & deletes in 2K table\n", timer);
   begin(); for (n=0; n <   30; ++n) { churn_skip(20000,21000,500);           } end(); printf("  // %7.2fms : 500,000 inserts & deletes in 20K table\n", timer);
   begin(); for (n=0; n <   15; ++n) { churn_skip(200000,201000,500);         } end(); printf("  // %7.2fms : 500,000 inserts & deletes in 200K table\n", timer);

+ 418 - 0
tests/test_ds_cpp.cpp

@@ -0,0 +1,418 @@
+#include <stdio.h>
+
+#ifdef DS_TEST
+#define STBDS_UNIT_TESTS
+#endif
+
+#ifdef DS_STATS
+#define STBDS_STATISTICS
+#endif
+
+#ifndef DS_PERF
+#define STBDS_ASSERT assert
+#include <assert.h>
+#endif
+
+//#define STBDS_SIPHASH_2_4
+#define STB_DS_IMPLEMENTATION
+#include "../stb_ds.h"
+
+size_t churn_inserts, churn_deletes;
+
+void churn(int a, int b, int count)
+{
+  struct { int key,value; } *map=NULL;
+  int i,j,n,k;
+  for (i=0; i < a; ++i)
+    hmput(map,i,i+1);
+  for (n=0; n < count; ++n) {
+    for (j=a; j < b; ++j,++i) {
+      hmput(map,i,i+1);
+    }
+    assert(hmlen(map) == b);
+    for (j=a; j < b; ++j) {
+      k=i-j-1;
+      k = hmdel(map,k);
+      assert(k != 0);
+    }
+    assert(hmlen(map) == a);
+  }
+  hmfree(map);
+  churn_inserts = i;
+  churn_deletes = (b-a) * n;
+}
+
+#ifdef DS_TEST
+#include <stdio.h>
+int main(int argc, char **argv)
+{
+  stbds_unit_tests();
+  churn(0,100,1);
+  churn(3,7,50000);
+  churn(3,15,50000);
+  churn(16, 48, 25000);
+  churn(10, 15, 25000);
+  churn(200,500, 5000);
+  churn(2000,5000, 500);
+  churn(20000,50000, 50);
+  printf("Ok!");
+  return 0;
+}
+#endif
+
+#ifdef DS_STATS
+#define MAX(a,b) ((a) > (b) ? (a) : (b))
+size_t max_hit_probes, max_miss_probes, total_put_probes, total_miss_probes, churn_misses;
+void churn_stats(int a, int b, int count)
+{
+  struct { int key,value; } *map=NULL;
+  int i,j,n,k;
+  churn_misses = 0;
+  for (i=0; i < a; ++i) {
+    hmput(map,i,i+1);
+    max_hit_probes = MAX(max_hit_probes, stbds_hash_probes);
+    total_put_probes += stbds_hash_probes;
+    stbds_hash_probes = 0;
+  }
+    
+  for (n=0; n < count; ++n) {
+    for (j=a; j < b; ++j,++i) {
+      hmput(map,i,i+1);
+      max_hit_probes = MAX(max_hit_probes, stbds_hash_probes);
+      total_put_probes += stbds_hash_probes;
+      stbds_hash_probes = 0;
+    }
+    for (j=0; j < (b-a)*10; ++j) {
+      k=i+j;
+      (void) hmgeti(map,k); // miss
+      max_miss_probes = MAX(max_miss_probes, stbds_hash_probes);
+      total_miss_probes += stbds_hash_probes;
+      stbds_hash_probes = 0;
+      ++churn_misses;
+    }
+    assert(hmlen(map) == b);
+    for (j=a; j < b; ++j) {
+      k=i-j-1;
+      k = hmdel(map,k);
+      stbds_hash_probes = 0;
+      assert(k);
+    }
+    assert(hmlen(map) == a);
+  }
+  hmfree(map);
+  churn_inserts = i;
+  churn_deletes = (b-a) * n;
+}
+
+void reset_stats(void)
+{
+  stbds_array_grow=0, 
+  stbds_hash_grow=0;
+  stbds_hash_shrink=0;
+  stbds_hash_rebuild=0;
+  stbds_hash_probes=0;
+  stbds_hash_alloc=0;
+  stbds_rehash_probes=0;
+  stbds_rehash_items=0;
+  max_hit_probes = 0;
+  max_miss_probes = 0;
+  total_put_probes = 0;
+  total_miss_probes = 0;
+}
+
+void print_churn_probe_stats(char *str)
+{
+  printf("Probes: %3d max hit, %3d max miss, %4.2f avg hit, %4.2f avg miss: %s\n",
+    (int) max_hit_probes, (int) max_miss_probes, (float) total_put_probes / churn_inserts, (float) total_miss_probes / churn_misses, str);
+  reset_stats();
+}
+
+int main(int arg, char **argv)
+{
+  churn_stats(0,500000,1); print_churn_probe_stats("Inserting 500000 items");
+  churn_stats(0,500000,1); print_churn_probe_stats("Inserting 500000 items");
+  churn_stats(0,500000,1); print_churn_probe_stats("Inserting 500000 items");
+  churn_stats(0,500000,1); print_churn_probe_stats("Inserting 500000 items");
+  churn_stats(49000,50000,500); print_churn_probe_stats("Deleting/Inserting 500000 items");
+  churn_stats(49000,50000,500); print_churn_probe_stats("Deleting/Inserting 500000 items");
+  churn_stats(49000,50000,500); print_churn_probe_stats("Deleting/Inserting 500000 items");
+  churn_stats(49000,50000,500); print_churn_probe_stats("Deleting/Inserting 500000 items");
+  return 0;
+}
+#endif
+
+#ifdef DS_PERF
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#define STB_DEFINE
+#define STB_NO_REGISTRY
+//#include "../stb.h"
+
+
+size_t t0, sum, mn,mx,count;
+void begin(void)
+{
+  size_t t0;
+  LARGE_INTEGER m;
+  QueryPerformanceCounter(&m);
+  t0 = m.QuadPart;
+  sum = 0;
+  count = 0;
+  mx = 0;
+  mn = ~(size_t) 0;
+}
+
+void measure(void)
+{
+  size_t t1, t;
+  LARGE_INTEGER m;
+  QueryPerformanceCounter(&m);
+  t1 = m.QuadPart;
+  t = t1-t0;
+  if (t1 < t0)
+    printf("ALERT: QueryPerformanceCounter was unordered!\n");
+  if (t < mn) mn = t;
+  if (t > mx) mx = t;
+  sum += t;
+  ++count;
+  t0 = t1;
+}
+
+void dont_measure(void)
+{
+  size_t t1, t;
+  LARGE_INTEGER m;
+  QueryPerformanceCounter(&m);
+  t0 = m.QuadPart;
+}
+
+double timer;
+void end(void)
+{
+  LARGE_INTEGER m;
+  QueryPerformanceFrequency(&m);
+
+  if (count > 3) {
+    // discard the highest and lowest
+    sum -= mn;
+    sum -= mx;
+    count -= 2;
+  }
+  timer = (double) (sum) / count / m.QuadPart * 1000;
+}
+
+void build(int a, int b, int count, int step)
+{
+  struct { int key,value; } *map=NULL;
+  int i,j,n,k;
+  for (i=0; i < a; ++i)
+    hmput(map,i*step,i+1);
+  measure();
+  churn_inserts = i;
+  hmfree(map);
+  dont_measure();
+}
+
+#ifdef STB__INCLUDE_STB_H
+void build_stb(int a, int b, int count, int step)
+{
+  stb_idict *d = stb_idict_new_size(8);
+  struct { int key,value; } *map=NULL;
+  int i,j,n,k;
+  for (i=0; i < a; ++i)
+    stb_idict_add(d, i*step, i+1);
+  measure();
+  churn_inserts = i;
+  stb_idict_destroy(d);
+  dont_measure();
+}
+#endif
+
+void churn_skip(unsigned int a, unsigned int b, int count)
+{
+  struct { unsigned int key,value; } *map=NULL;
+  unsigned int i,j,n,k;
+  for (i=0; i < a; ++i)
+    hmput(map,i,i+1);
+  dont_measure();
+  for (n=0; n < count; ++n) {
+    for (j=a; j < b; ++j,++i) {
+      hmput(map,i,i+1);
+    }
+    assert(hmlen(map) == b);
+    for (j=a; j < b; ++j) {
+      k=i-j-1;
+      k = hmdel(map,k);
+      assert(k != 0);
+    }
+    assert(hmlen(map) == a);
+  }
+  measure();
+  churn_inserts = i;
+  churn_deletes = (b-a) * n;
+  hmfree(map);
+  dont_measure();
+}
+
+typedef struct { int n[8]; } str32;
+void churn32(int a, int b, int count, int include_startup)
+{
+  struct { str32 key; int value; } *map=NULL;
+  int i,j,n;
+  str32 key = { 0 };
+  for (i=0; i < a; ++i) {
+    key.n[0] = i;
+    hmput(map,key,i+1);
+  }
+  if (!include_startup)
+    dont_measure();
+  for (n=0; n < count; ++n) {
+    for (j=a; j < b; ++j,++i) {
+      key.n[0] = i;
+      hmput(map,key,i+1);
+    }
+    assert(hmlen(map) == b);
+    for (j=a; j < b; ++j) {
+      key.n[0] = i-j-1;
+      hmdel(map,key);
+    }
+    assert(hmlen(map) == a);
+  }
+  measure();
+  hmfree(map);
+  churn_inserts = i;
+  churn_deletes = (b-a) * n;
+  dont_measure();
+}
+
+typedef struct { int n[32]; } str256;
+void churn256(int a, int b, int count, int include_startup)
+{
+  struct { str256 key; int value; } *map=NULL;
+  int i,j,n;
+  str256 key = { 0 };
+  for (i=0; i < a; ++i) {
+    key.n[0] = i;
+    hmput(map,key,i+1);
+  }
+  if (!include_startup)
+    dont_measure();
+  for (n=0; n < count; ++n) {
+    for (j=a; j < b; ++j,++i) {
+      key.n[0] = i;
+      hmput(map,key,i+1);
+    }
+    assert(hmlen(map) == b);
+    for (j=a; j < b; ++j) {
+      key.n[0] = i-j-1;
+      hmdel(map,key);
+    }
+    assert(hmlen(map) == a);
+  }
+  measure();
+  hmfree(map);
+  churn_inserts = i;
+  churn_deletes = (b-a) * n;
+  dont_measure();
+}
+
+void churn8(int a, int b, int count, int include_startup)
+{
+  struct { size_t key,value; } *map=NULL;
+  int i,j,n,k;
+  for (i=0; i < a; ++i)
+    hmput(map,i,i+1);
+  if (!include_startup)
+    dont_measure();
+  for (n=0; n < count; ++n) {
+    for (j=a; j < b; ++j,++i) {
+      hmput(map,i,i+1);
+    }
+    assert(hmlen(map) == b);
+    for (j=a; j < b; ++j) {
+      k=i-j-1;
+      k = hmdel(map,k);
+      assert(k != 0);
+    }
+    assert(hmlen(map) == a);
+  }
+  measure();
+  hmfree(map);
+  churn_inserts = i;
+  churn_deletes = (b-a) * n;
+  dont_measure();
+}
+
+
+int main(int arg, char **argv)
+{
+  int n,s,w;
+  double worst = 0;
+
+#if 0
+  begin(); for (n=0; n < 2000; ++n) { build_stb(2000,0,0,1);          } end(); printf("  // %7.2fms :      2,000 inserts creating 2K table\n", timer);
+  begin(); for (n=0; n <  500; ++n) { build_stb(20000,0,0,1);         } end(); printf("  // %7.2fms :     20,000 inserts creating 20K table\n", timer);
+  begin(); for (n=0; n <  100; ++n) { build_stb(200000,0,0,1);        } end(); printf("  // %7.2fms :    200,000 inserts creating 200K table\n", timer);
+  begin(); for (n=0; n <   10; ++n) { build_stb(2000000,0,0,1);       } end(); printf("  // %7.2fms :  2,000,000 inserts creating 2M table\n", timer);
+  begin(); for (n=0; n <    5; ++n) { build_stb(20000000,0,0,1);      } end(); printf("  // %7.2fms : 20,000,000 inserts creating 20M table\n", timer);
+#endif
+
+  begin(); for (n=0; n < 2000; ++n) { churn8(2000,0,0,1);          } end(); printf("  // %7.2fms :      2,000 inserts creating 2K table w/ 8-byte key\n", timer);
+  begin(); for (n=0; n <  500; ++n) { churn8(20000,0,0,1);         } end(); printf("  // %7.2fms :     20,000 inserts creating 20K table w/ 8-byte key\n", timer);
+  begin(); for (n=0; n <  100; ++n) { churn8(200000,0,0,1);        } end(); printf("  // %7.2fms :    200,000 inserts creating 200K table w/ 8-byte key\n", timer);
+  begin(); for (n=0; n <   10; ++n) { churn8(2000000,0,0,1);       } end(); printf("  // %7.2fms :  2,000,000 inserts creating 2M table w/ 8-byte key\n", timer);
+  begin(); for (n=0; n <    5; ++n) { churn8(20000000,0,0,1);      } end(); printf("  // %7.2fms : 20,000,000 inserts creating 20M table w/ 8-byte key\n", timer);
+
+#if 0
+  begin(); for (n=0; n < 2000; ++n) { churn32(2000,0,0,1);          } end(); printf("  // %7.2fms :      2,000 inserts creating 2K table w/ 32-byte key\n", timer);
+  begin(); for (n=0; n <  500; ++n) { churn32(20000,0,0,1);         } end(); printf("  // %7.2fms :     20,000 inserts creating 20K table w/ 32-byte key\n", timer);
+  begin(); for (n=0; n <  100; ++n) { churn32(200000,0,0,1);        } end(); printf("  // %7.2fms :    200,000 inserts creating 200K table w/ 32-byte key\n", timer);
+  begin(); for (n=0; n <   10; ++n) { churn32(2000000,0,0,1);       } end(); printf("  // %7.2fms :  2,000,000 inserts creating 2M table w/ 32-byte key\n", timer);
+  begin(); for (n=0; n <    5; ++n) { churn32(20000000,0,0,1);      } end(); printf("  // %7.2fms : 20,000,000 inserts creating 20M table w/ 32-byte key\n", timer);
+
+  begin(); for (n=0; n < 2000; ++n) { churn256(2000,0,0,1);          } end(); printf("  // %7.2fms :      2,000 inserts creating 2K table w/ 256-byte key\n", timer);
+  begin(); for (n=0; n <  500; ++n) { churn256(20000,0,0,1);         } end(); printf("  // %7.2fms :     20,000 inserts creating 20K table w/ 256-byte key\n", timer);
+  begin(); for (n=0; n <  100; ++n) { churn256(200000,0,0,1);        } end(); printf("  // %7.2fms :    200,000 inserts creating 200K table w/ 256-byte key\n", timer);
+  begin(); for (n=0; n <   10; ++n) { churn256(2000000,0,0,1);       } end(); printf("  // %7.2fms :  2,000,000 inserts creating 2M table w/ 256-byte key\n", timer);
+  begin(); for (n=0; n <    5; ++n) { churn256(20000000,0,0,1);      } end(); printf("  // %7.2fms : 20,000,000 inserts creating 20M table w/ 256-byte key\n", timer);
+#endif
+
+  begin(); for (n=0; n < 2000; ++n) { build(2000,0,0,1);          } end(); printf("  // %7.2fms :      2,000 inserts creating 2K table w/ 4-byte key\n", timer);
+  begin(); for (n=0; n <  500; ++n) { build(20000,0,0,1);         } end(); printf("  // %7.2fms :     20,000 inserts creating 20K table w/ 4-byte key\n", timer);
+  begin(); for (n=0; n <  100; ++n) { build(200000,0,0,1);        } end(); printf("  // %7.2fms :    200,000 inserts creating 200K table w/ 4-byte key\n", timer);
+  begin(); for (n=0; n <   10; ++n) { build(2000000,0,0,1);       } end(); printf("  // %7.2fms :  2,000,000 inserts creating 2M table w/ 4-byte key\n", timer);
+  begin(); for (n=0; n <    5; ++n) { build(20000000,0,0,1);      } end(); printf("  // %7.2fms : 20,000,000 inserts creating 20M table w/ 4-byte key\n", timer);
+
+  begin(); for (n=0; n <   60; ++n) { churn_skip(2000,2100,5000);            } end(); printf("  // %7.2fms : 500,000 inserts & deletes in 2K table\n", timer);
+  begin(); for (n=0; n <   30; ++n) { churn_skip(20000,21000,500);           } end(); printf("  // %7.2fms : 500,000 inserts & deletes in 20K table\n", timer);
+  begin(); for (n=0; n <   15; ++n) { churn_skip(200000,201000,500);         } end(); printf("  // %7.2fms : 500,000 inserts & deletes in 200K table\n", timer);
+  begin(); for (n=0; n <    8; ++n) { churn_skip(2000000,2001000,500);       } end(); printf("  // %7.2fms : 500,000 inserts & deletes in 2M table\n", timer);
+  begin(); for (n=0; n <    5; ++n) { churn_skip(20000000,20001000,500);     } end(); printf("  // %7.2fms : 500,000 inserts & deletes in 20M table\n", timer);
+  begin(); for (n=0; n <    1; ++n) { churn_skip(200000000u,200001000u,500); } end(); printf("  // %7.2fms : 500,000 inserts & deletes in 200M table\n", timer);
+  // even though the above measures a roughly fixed amount of work, we still have to build the table n times, hence the fewer measurements each time
+
+  begin(); for (n=0; n <   60; ++n) { churn_skip(1000,3000,250);             } end(); printf("  // %7.2fms :    500,000 inserts & deletes in 2K table\n", timer);
+  begin(); for (n=0; n <   15; ++n) { churn_skip(10000,30000,25);            } end(); printf("  // %7.2fms :    500,000 inserts & deletes in 20K table\n", timer);
+  begin(); for (n=0; n <    7; ++n) { churn_skip(100000,300000,10);          } end(); printf("  // %7.2fms :  2,000,000 inserts & deletes in 200K table\n", timer);
+  begin(); for (n=0; n <    2; ++n) { churn_skip(1000000,3000000,10);        } end(); printf("  // %7.2fms : 20,000,000 inserts & deletes in 2M table\n", timer);
+
+  // search for bad intervals.. in practice this just seems to measure execution variance
+  for (s = 2; s < 64; ++s) {
+    begin(); for (n=0; n < 50; ++n) { build(200000,0,0,s); } end();
+    if (timer > worst) {
+      worst = timer;
+      w = s;
+    }
+  }
+  for (; s <= 1024; s *= 2) {
+    begin(); for (n=0; n < 50; ++n) { build(200000,0,0,s); } end();
+    if (timer > worst) {
+      worst = timer;
+      w = s;
+    }
+  }
+  printf("  // %7.2fms(%d)   : Worst time from inserting 200,000 items with spacing %d.\n", worst, w, w);
+
+  return 0;
+}
+#endif

+ 9 - 0
tests/test_easyfont.c

@@ -1 +1,10 @@
 #include "stb_easy_font.h"
+
+void ef_dummy(void)
+{
+   // suppress unsused-function warning
+   stb_easy_font_spacing(0);
+   stb_easy_font_print(0,0,0,0,0,0);
+   stb_easy_font_width(0);
+   stb_easy_font_height(0);
+}

+ 4 - 0
tests/test_image.c

@@ -1,3 +1,7 @@
+#ifdef __clang__
+#define STBIDEF static inline
+#endif
+
 #define STB_IMAGE_STATIC
 #define STB_IMAGE_IMPLEMENTATION
 #include "stb_image.h"

+ 4 - 0
tests/test_image_write.c

@@ -1,3 +1,7 @@
+#ifdef __clang__
+#define STBIWDEF static inline
+#endif
+
 #define STB_IMAGE_WRITE_IMPLEMENTATION
 #define STB_IMAGE_WRITE_STATIC
 #include "stb_image_write.h"

+ 0 - 4
tests/test_vorbis.c

@@ -1,7 +1,3 @@
-#define STB_IMAGE_STATIC
-#define STB_IMAGE_IMPLEMENTATION
-#include "stb_image.h"
-
 #define STB_VORBIS_HEADER_ONLY
 #include "stb_vorbis.c"
 #include "stb.h"

+ 10 - 0
tests/textedit_sample.c

@@ -82,3 +82,13 @@ int insert_chars(STB_TEXTEDIT_STRING *str, int pos, STB_TEXTEDIT_CHARTYPE *newte
 
 #define STB_TEXTEDIT_IMPLEMENTATION
 #include "stb_textedit.h"
+
+void dummy3(void)
+{
+  stb_textedit_click(0,0,0,0);
+  stb_textedit_drag(0,0,0,0);
+  stb_textedit_cut(0,0);
+  stb_textedit_key(0,0,0);
+  stb_textedit_initialize_state(0,0);
+  stb_textedit_paste(0,0,0,0);
+}

Some files were not shown because too many files changed in this diff