adding bh.h and stb_ds.h
authorBrendan Hansen <brendan.f.hansen@gmail.com>
Fri, 15 Apr 2022 00:33:17 +0000 (19:33 -0500)
committerBrendan Hansen <brendan.f.hansen@gmail.com>
Fri, 15 Apr 2022 00:33:17 +0000 (19:33 -0500)
build.sh
include/bh.h [new file with mode: 0644]
include/stb_ds.h [new file with mode: 0644]
src/cli.c
src/embedder.c

index 20502cca91b555e2f70ae16500da717248802f70..c868ee5f3ae24d4acc879e42d00592c9fd5f6969 100755 (executable)
--- a/build.sh
+++ b/build.sh
@@ -1,16 +1,17 @@
 #!/bin/sh
 
 CC="gcc"
+WARNINGS='-Wimplicit -Wmisleading-indentation -Wparentheses -Wsequence-point -Wreturn-type -Wshift-negative-value -Wunused-but-set-parameter -Wunused-but-set-variable -Wunused-function -Wunused-label -Wmaybe-uninitialized -Wsign-compare -Wstrict-overflow -Wduplicated-branches -Wduplicated-cond -Wtrigraphs -Waddress -Wlogical-op'
 FLAGS="-g3"
 LIBS=
-INCLUDES=
+INCLUDES="-I include"
 TARGET="libonyx_embedder.so"
 C_FILES="src/embedder.c"
 
-$CC $FLAGS $INCLUDES -o $TARGET -c $C_FILES $LIBS
+$CC $FLAGS $INCLUDES -o $TARGET -c $C_FILES $LIBS $WARNINGS
 
-C_FILES=src/cli.c
+C_FILES="src/cli.c"
 TARGET=onyx-debug
 LIBS="-L$(pwd) -lonyx_embedder"
 
-$CC $FLAGS $INCLUDES -o $TARGET $C_FILES $LIBS
\ No newline at end of file
+$CC $FLAGS $INCLUDES -o $TARGET $C_FILES $LIBS $WARNINGS
\ No newline at end of file
diff --git a/include/bh.h b/include/bh.h
new file mode 100644 (file)
index 0000000..b187b96
--- /dev/null
@@ -0,0 +1,2743 @@
+#ifndef BH_H
+#define BH_H
+
+// NOTE: For lseek64
+#define _LARGEFILE64_SOURCE
+
+#if defined(_WIN32) || defined(_WIN64)
+    #define _BH_WINDOWS 1
+#endif
+
+#if defined(__unix__)
+    #define _BH_LINUX 1
+#endif
+
+#include <sys/stat.h>
+#include <malloc.h>
+#include <time.h>
+
+#ifdef _BH_LINUX
+    #include <errno.h>
+    #include <fcntl.h>
+    #include <unistd.h>
+    #include <dirent.h>
+#endif
+
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h> // TODO: Replace with needed functions
+#include <stdint.h>
+#include <assert.h>
+#include <stdio.h>
+
+#if defined(_MSC_VER) && !defined(_WINDOWS_)
+    #include "small_windows.h"
+#endif
+
+//-------------------------------------------------------------------------------------
+// Better types
+//-------------------------------------------------------------------------------------
+typedef uint8_t u8;
+typedef uint16_t u16;
+typedef uint32_t u32;
+typedef uint64_t u64;
+typedef int8_t i8;
+typedef int16_t i16;
+typedef int32_t i32;
+typedef int64_t i64;
+typedef int64_t isize;
+typedef i32 b32;
+typedef void* ptr;
+typedef float f32;
+typedef double f64;
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+//-------------------------------------------------------------------------------------
+// Better character functions
+//-------------------------------------------------------------------------------------
+b32 char_is_alpha(const char a);
+b32 char_is_num(const char a);
+b32 char_is_alphanum(const char a);
+char charset_contains(const char* charset, char ch);
+b32 char_is_whitespace(const char a);
+b32 char_in_range(const char lo, const char hi, const char a);
+i64 chars_match(char* ptr1, char* ptr2);
+
+
+
+
+
+//-------------------------------------------------------------------------------------
+// Better math functions
+//-------------------------------------------------------------------------------------
+#define bh_max(a, b)         ((a) > (b) ? (a) : (b))
+#define bh_min(a, b)         ((a) < (b) ? (a) : (b))
+#define bh_clamp(v, a, b)    (bh_min((b), bh_max((a), (v))))
+#define bh_abs(x)            ((x) < 0 ? -(x) : (x))
+#define size_of(x)           (isize) sizeof(x)
+
+static inline u64 log2_dumb(u64 n) {
+    switch (n) {
+    case 1 << 0:  return 0;
+    case 1 << 1:  return 1;
+    case 1 << 2:  return 2;
+    case 1 << 3:  return 3;
+    case 1 << 4:  return 4;
+    case 1 << 5:  return 5;
+    case 1 << 6:  return 6;
+    case 1 << 7:  return 7;
+    case 1 << 8:  return 8;
+    case 1 << 9:  return 9;
+    case 1 << 10: return 10;
+    case 1 << 11: return 11;
+    case 1 << 12: return 12;
+    case 1 << 13: return 13;
+    case 1 << 14: return 14;
+    case 1 << 15: return 15;
+    case 1 << 16: return 16;
+    case 1 << 17: return 17;
+    case 1 << 18: return 18;
+    case 1 << 19: return 19;
+    case 1 << 20: return 20;
+    case 1 << 21: return 21;
+    case 1 << 22: return 22;
+    case 1 << 23: return 23;
+    case 1 << 24: return 24;
+    case 1 << 25: return 25;
+    case 1 << 26: return 26;
+    case 1 << 27: return 27;
+    case 1 << 28: return 28;
+    case 1 << 29: return 29;
+    case 1 << 30: return 30;
+    case 1 << 31: return 31;
+
+    default:   return 0;
+    }
+}
+
+
+
+static inline const char* bh_num_suffix(u64 i) {
+    if (i == 11 || i == 12 || i == 13) return "th";
+
+    switch (i % 10) {
+        case 0: return "th";
+        case 1: return "st";
+        case 2: return "nd";
+        case 3: return "rd";
+        case 4: return "th";
+        case 5: return "th";
+        case 6: return "th";
+        case 7: return "th";
+        case 8: return "th";
+        case 9: return "th";
+
+        default: return "";
+    }
+}
+
+
+
+
+//-------------------------------------------------------------------------------------
+// Conversion functions
+//-------------------------------------------------------------------------------------
+
+// Converts an unsigned integer to the unsigned LEB128 format
+u8* uint_to_uleb128(u64 n, i32* output_length);
+u8* int_to_leb128(i64 n, i32* output_length);
+u8* float_to_ieee754(f32 f, b32 reverse);
+u8* double_to_ieee754(f64 f, b32 reverse);
+
+u64 uleb128_to_uint(u8* bytes, i32 *byte_walker);
+
+
+
+//-------------------------------------------------------------------------------------
+// Helpful macros
+//-------------------------------------------------------------------------------------
+#define bh_offset_of(Type, elem)        ((isize)&(((Type)*) 0)->elem)
+#define bh_align_of(Type)                bh_offset_of(struct { char c; Type member; }, member)
+#define bh_swap(Type, a, b)                do { Type tmp = (a); (a) = (b); (b) = tmp; } while(0)
+
+#define bh_align(x, a) if ((x) % (a) != 0) (x) += (a) - ((x) % (a));
+
+#define bh_pointer_add(ptr, amm)        ((void *)((u8 *) ptr + amm))
+#define BH_BIT(x)                        (1 << (x))
+#define BH_MASK_SET(var, set, mask)     ((set) ? ((var) |= (mask)) : ((var) &= ~(mask)))
+
+#define fori(var, lo, hi)                 for (i64 var = (lo); var < (hi); var++)
+#define forir(var, hi, lo)                for (i64 var = (hi); var >= (lo); var--)
+#define forll(T, var, start, step)        for (T* var = (start); var != NULL; var = (T *) var->step)
+
+#if defined(BH_DEBUG) && !defined(_BH_WINDOWS)
+    #define DEBUG_HERE                        __asm("int $3")
+#else
+    #define DEBUG_HERE
+#endif
+
+
+
+
+
+//-------------------------------------------------------------------------------------
+// Custom allocators
+//-------------------------------------------------------------------------------------
+
+typedef enum bh_allocator_actions {
+    bh_allocator_action_alloc,
+    bh_allocator_action_free,
+    bh_allocator_action_resize,
+} bh_allocator_actions;
+
+#define BH_ALLOCATOR_PROC(name) \
+ptr name(ptr data, bh_allocator_actions action, \
+         isize size, isize alignment, \
+         void* prev_memory, \
+         u64 flags)
+
+typedef BH_ALLOCATOR_PROC(bh__allocator_proc); // NOTE: so bh__allocator_proc can be used instead of that type
+
+typedef struct bh_allocator {
+    bh__allocator_proc* proc; // Procedure that can handle bh_allocator_actions
+    ptr                    data; // Pointer to the other data for the allocator
+} bh_allocator;
+
+typedef enum bh_allocator_flags {
+    bh_allocator_flag_clear = 1    // Sets all memory to be 0
+} bh_allocator_flags;
+
+ptr bh_alloc(bh_allocator a, isize size);
+ptr bh_alloc_aligned(bh_allocator a, isize size, isize alignment);
+ptr bh_resize(bh_allocator a, ptr data, isize new_size);
+ptr bh_resize_aligned(bh_allocator a, ptr data, isize new_size, isize alignment);
+void bh_free(bh_allocator a, ptr data);
+
+#define bh_alloc_item(allocator_, T)                (T *) bh_alloc(allocator_, sizeof(T))
+#define bh_alloc_array(allocator_, T, n)            (T *) bh_alloc(allocator_, sizeof(T) * (n))
+
+// NOTE: This should get optimized out since alignment should be a power of two
+#define bh__align(x, alignment)                        ((((x) / alignment) + 1) * alignment)
+
+
+
+
+// HEAP ALLOCATOR
+// Essentially a wrapper for malloc, free and realloc
+bh_allocator bh_heap_allocator(void);
+BH_ALLOCATOR_PROC(bh_heap_allocator_proc);
+
+
+
+
+
+// ARENA ALLOCATOR
+typedef struct bh_arena {
+    bh_allocator backing;
+    ptr first_arena, current_arena;
+    isize size, arena_size; // in bytes
+} bh_arena;
+
+typedef struct bh__arena_internal {
+    ptr next_arena;
+    void* data; // Not actually a pointer, just used for the offset
+} bh__arena_internal;
+
+void bh_arena_init(bh_arena* alloc, bh_allocator backing, isize arena_size);
+void bh_arena_free(bh_arena* alloc);
+bh_allocator bh_arena_allocator(bh_arena* alloc);
+BH_ALLOCATOR_PROC(bh_arena_allocator_proc);
+
+
+
+
+
+
+// SCRATCH ALLOCATOR
+typedef struct bh_scratch {
+    bh_allocator backing;
+    ptr memory, end, curr;
+} bh_scratch;
+
+void bh_scratch_init(bh_scratch* scratch, bh_allocator backing, isize scratch_size);
+void bh_scratch_free(bh_scratch* scratch);
+bh_allocator bh_scratch_allocator(bh_scratch* scratch);
+BH_ALLOCATOR_PROC(bh_scratch_allocator_proc);
+
+
+
+
+
+
+
+//-------------------------------------------------------------------------------------
+// Allocator based string functions
+//-------------------------------------------------------------------------------------
+
+b32 bh_str_starts_with(char* str, char* start);
+b32 bh_str_ends_with(char* str, char* end);
+char* bh_strdup(bh_allocator a, char* str);
+
+
+
+
+
+
+
+
+
+
+
+
+
+//-------------------------------------------------------------------------------------
+// Better files
+//-------------------------------------------------------------------------------------
+#ifndef BH_NO_FILE
+
+typedef enum bh_file_error {
+    BH_FILE_ERROR_NONE,
+    BH_FILE_ERROR_INVALID,
+    BH_FILE_ERROR_BAD_FD,
+} bh_file_error;
+
+typedef enum bh_file_mode {
+    BH_FILE_MODE_READ = 1 << 0,
+    BH_FILE_MODE_WRITE = 1 << 1,
+    BH_FILE_MODE_APPEND = 1 << 2,
+    BH_FILE_MODE_RW = 1 << 3,
+
+    BH_FILE_MODE_MODES = BH_FILE_MODE_READ | BH_FILE_MODE_WRITE | BH_FILE_MODE_APPEND | BH_FILE_MODE_RW
+} bh_file_mode;
+
+typedef enum bh_file_whence {
+    BH_FILE_WHENCE_BEGIN = SEEK_SET,
+    BH_FILE_WHENCE_CURRENT = SEEK_CUR,
+    BH_FILE_WHENCE_END = SEEK_END,
+} bh_file_whence;
+
+#ifdef _BH_WINDOWS
+    typedef HANDLE bh_file_descriptor;
+#else
+    typedef int bh_file_descriptor;
+#endif
+
+typedef struct bh_file {
+    bh_file_descriptor fd;
+    char const* filename;
+} bh_file;
+
+typedef enum bh_file_standard {
+    BH_FILE_STANDARD_INPUT,
+    BH_FILE_STANDARD_OUTPUT,
+    BH_FILE_STANDARD_ERROR
+} bh_file_standard;
+
+typedef struct bh_file_contents {
+    bh_allocator allocator;
+    const char *filename;
+    isize length;
+    void* data;
+} bh_file_contents;
+
+bh_file_error bh_file_get_standard(bh_file* file, bh_file_standard stand);
+
+bh_file_error bh_file_create(bh_file* file, char const* filename);
+bh_file_error bh_file_open(bh_file* file, char const* filename);
+bh_file_error bh_file_open_mode(bh_file* file, bh_file_mode mode, const char* filename);
+bh_file_error bh_file_new(bh_file* file, bh_file_descriptor fd, const char* filename);
+b32 bh_file_read_at(bh_file* file, i64 offset, void* buffer, isize buff_size, isize* bytes_read);
+b32 bh_file_write_at(bh_file* file, i64 offset, void const* buffer, isize buff_size, isize* bytes_wrote);
+i64 bh_file_seek(bh_file* file, i64 offset, bh_file_whence whence);
+i64 bh_file_seek_to(bh_file* file, i64 offset);
+i64 bh_file_seek_to_end(bh_file* file);
+i64 bh_file_skip(bh_file* file, i64 bytes);
+i64 bh_file_tell(bh_file* file);
+bh_file_error bh_file_close(bh_file* file);
+i32 bh_file_read(bh_file* file, void* buffer, isize buff_size);
+i32 bh_file_write(bh_file* file, void* buffer, isize buff_size);
+void bh_file_flush(bh_file* file);
+i64 bh_file_size(bh_file* file);
+b32 bh_file_exists(char const* filename);
+char* bh_path_get_full_name(char const* filename, bh_allocator a);
+char* bh_path_get_parent(char const* filename, bh_allocator a);
+char* bh_path_convert_separators(char* path);
+
+// This function returns a volatile pointer. Do not store it without copying!
+// `included_folders` is bh_arr(const char *).
+char* bh_lookup_file(char* filename, char* relative_to, char *suffix, b32 add_suffix, const char ** included_folders, b32 search_included_folders);
+
+#define bh_file_read_contents(allocator_, x) _Generic((x), \
+    bh_file*: bh_file_read_contents_bh_file, \
+    const char*: bh_file_read_contents_direct, \
+    char*: bh_file_read_contents_direct)((allocator_), x)
+
+bh_file_contents bh_file_read_contents_bh_file(bh_allocator alloc, bh_file* file);
+bh_file_contents bh_file_read_contents_direct(bh_allocator alloc, const char* filename);
+i32 bh_file_contents_free(bh_file_contents* contents);
+
+
+#ifdef _BH_WINDOWS
+    typedef struct Windows_Directory_Opened {
+        HANDLE hndl;
+        WIN32_FIND_DATAA found_file;
+    } Windows_Directory_Opened;
+
+    typedef Windows_Directory_Opened *bh_dir;
+#else
+    typedef DIR *bh_dir;
+#endif
+
+typedef enum bh_dirent_type {
+    BH_DIRENT_UNKNOWN,
+    BH_DIRENT_BLOCK,
+    BH_DIRENT_CHAR,
+    BH_DIRENT_DIRECTORY,
+    BH_DIRENT_FILE,
+    BH_DIRENT_SYMLINK,
+    BH_DIRENT_OTHER,
+} bh_dirent_type;
+
+typedef struct bh_dirent {
+    bh_dirent_type type;
+    u32 id;
+    u32 name_length;
+    char name[256];
+} bh_dirent;
+
+bh_dir bh_dir_open(char* path);
+b32    bh_dir_read(bh_dir dir, bh_dirent* out);
+void   bh_dir_close(bh_dir dir);
+
+#endif
+
+
+
+
+
+
+
+
+
+
+//-------------------------------------------------------------------------------------
+// Alternate printing
+//-------------------------------------------------------------------------------------
+// Barebones implementation of printf. Does not support all format options
+// Currently supports:
+//         %c - chars
+//        %_(u)d - ints where _ is:
+//            nothing - decimal
+//            o - octal
+//            x - hexadecimal
+//        %_(u)l - longs where _ is:
+//            nothing - decimal
+//            o - octal
+//            x - hexadecimal
+//        %f - floating points
+//        %s - null terminated strings
+//        %p - pointers
+//        %% - literal %
+
+typedef struct bh__print_format {
+    u32 base;
+} bh__print_format;
+
+isize bh_printf(char const *fmt, ...);
+isize bh_printf_va(char const *fmt, va_list va);
+isize bh_printf_err(char const *fmt, ...);
+isize bh_printf_err_va(char const *fmt, va_list va);
+isize bh_fprintf(bh_file* f, char const *fmt, ...);
+isize bh_fprintf_va(bh_file* f, char const *fmt, va_list va);
+char* bh_bprintf(char const *fmt, ...);
+char* bh_bprintf_va(char const *fmt, va_list va);
+char* bh_aprintf(bh_allocator alloc, const char* fmt, ...);
+char* bh_aprintf_va(bh_allocator alloc, const char* fmt, va_list va);
+isize bh_snprintf(char *str, isize n, char const *fmt, ...);
+isize bh_snprintf_va(char *str, isize n, char const *fmt, va_list va);
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+//-------------------------------------------------------------------------------------
+// Flexible buffer
+//-------------------------------------------------------------------------------------
+
+typedef struct bh_buffer {
+    bh_allocator allocator;
+    i32 length, capacity;
+    u8* data;
+} bh_buffer;
+
+#ifndef BH_BUFFER_GROW_FORMULA
+#define BH_BUFFER_GROW_FORMULA(x)            ((x) > 0 ? ((x) << 1) : 16)
+#endif
+
+void bh_buffer_init(bh_buffer* buffer, bh_allocator alloc, i32 length);
+void bh_buffer_free(bh_buffer* buffer);
+void bh_buffer_grow(bh_buffer* buffer, i32 length);
+void bh_buffer_append(bh_buffer* buffer, const void * data, i32 length);
+void bh_buffer_concat(bh_buffer* buffer, bh_buffer other);
+void bh_buffer_write_byte(bh_buffer* buffer, u8 byte);
+void bh_buffer_write_u32(bh_buffer* buffer, u32 i);
+void bh_buffer_write_u64(bh_buffer* buffer, u64 i);
+void bh_buffer_align(bh_buffer* buffer, u32 alignment);
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+//-------------------------------------------------------------------------------------
+// Better dynamically-sized arrays
+//-------------------------------------------------------------------------------------
+#ifndef BH_NO_ARRAY
+
+typedef struct bh__arr {
+    bh_allocator allocator;
+    i32 length, capacity;
+} bh__arr;
+
+#ifndef BH_ARR_GROW_FORMULA
+#define BH_ARR_GROW_FORMULA(x)       ((x) > 0 ? ((x) << 1) : 4)
+#endif
+
+#define bh_arr(T)                    T*
+#define bh__arrhead(arr)             (((bh__arr *)(arr)) - 1)
+
+#define bh_arr_allocator(arr)        (arr ? bh__arrhead(arr)->allocator : bh_heap_allocator())
+#define bh_arr_length(arr)           (arr ? bh__arrhead(arr)->length : 0)
+#define bh_arr_capacity(arr)         (arr ? bh__arrhead(arr)->capacity : 0)
+#define bh_arr_size(arr)             (arr ? bh__arrhead(arr)->capacity * sizeof(*(arr)) : 0)
+#define bh_arr_valid(arr, i)         (arr ? (i32)(i) < bh__arrhead(arr)->length : 0)
+
+#define bh_arr_pop(arr)              ((arr)[--bh__arrhead(arr)->length])
+#define bh_arr_last(arr)             ((arr)[bh__arrhead(arr)->length - 1])
+#define bh_arr_end(arr, i)           ((i) >= &(arr)[bh_arr_length(arr)])
+#define bh_arr_start(arr, i)         ((i) < (arr))
+
+#define bh_arr_new(allocator_, arr, cap)    (bh__arr_grow((allocator_), (void**) &(arr), sizeof(*(arr)), cap))
+#define bh_arr_free(arr)                    (bh__arr_free((void**) &(arr)))
+#define bh_arr_copy(allocator_, arr)        (bh__arr_copy((allocator_), (arr), sizeof(*(arr))))
+
+#define bh_arr_grow(arr, cap)          (bh__arr_grow(bh_arr_allocator(arr), (void **) &(arr), sizeof(*(arr)), cap))
+#define bh_arr_shrink(arr, cap)        (bh__arr_shrink((void **) &(arr), sizeof(*(arr)), cap))
+#define bh_arr_set_length(arr, n)      (bh__arrhead(arr)->length = n)
+
+#define bh_arr_insertn(arr, i, n)      (bh__arr_insertn((void **) &(arr), sizeof(*(arr)), i, n))
+
+#define bh_arr_insert_end(arr, n)      ( \
+    bh__arr_grow(bh_arr_allocator(arr), (void **) &(arr), sizeof(*(arr)), bh_arr_length(arr) + n), \
+    bh__arrhead(arr)->length += n)
+
+#define bh_arr_push(arr, value)       ( \
+    bh_arr_length(arr) + 1 > bh_arr_capacity(arr) ? bh__arr_grow(bh_arr_allocator(arr), (void **) &(arr), sizeof(*(arr)), bh_arr_length(arr) + 1) : 0, \
+    arr[bh__arrhead(arr)->length++] = value)
+
+#define bh_arr_set_at(arr, n, value) ( \
+    bh__arr_grow(bh_arr_allocator(arr), (void **) &(arr), sizeof(*(arr)), (n) + 1), \
+    bh_arr_set_length((arr), bh_max(bh_arr_length(arr), (i32) (n) + 1)), \
+    arr[n] = value)
+
+#define bh_arr_is_empty(arr)          (arr ? bh__arrhead(arr)->length == 0 : 1)
+#define bh_arr_clear(arr)             (arr ? (bh__arrhead(arr)->length = 0) : 0)
+
+#define bh_arr_deleten(arr, i, n)     (bh__arr_deleten((void **) &(arr), sizeof(*(arr)), i, n))
+#define bh_arr_fastdelete(arr, i)     (arr[i] = arr[--bh__arrhead(arr)->length])
+
+#define bh_arr_each(T, var, arr)      for (T* var = (arr); !bh_arr_end((arr), var); var++)
+#define bh_arr_rev_each(T, var, arr)  for (T* var = &bh_arr_last((arr)); !bh_arr_start((arr), var); var--)
+
+#define bh_arr_zero(arr) memset(arr, 0, bh_arr_length(arr) * sizeof(*(arr)));
+
+b32 bh__arr_grow(bh_allocator alloc, void** arr, i32 elemsize, i32 cap);
+b32 bh__arr_shrink(void** arr, i32 elemsize, i32 cap);
+b32 bh__arr_free(void **arr);
+void* bh__arr_copy(bh_allocator alloc, void *arr, i32 elemsize);
+void bh__arr_insertn(void **arr, i32 elemsize, i32 index, i32 numelems);
+void bh__arr_deleten(void **arr, i32 elemsize, i32 index, i32 numelems);
+
+#endif
+
+
+
+
+
+
+
+
+
+
+
+
+//-------------------------------------------------------------------------------------
+// STRING HASH TABLE FUNCTIONS
+//-------------------------------------------------------------------------------------
+#ifndef BH_NO_TABLE
+
+#ifdef BH_DEFINE
+u64 bh__table_hash_function(const char* str, i32 len, i32 mod) {
+    u64 hash = 5381;
+    i32 c, l = 0;
+    if (len == 0) len = ((u32) 1 << 31) - 1;
+
+    while ((c = *str++) && l++ < len) {
+        hash = (hash << 5) + hash + c;
+    }
+
+    return hash % mod;
+}
+#endif
+
+typedef struct bh_table_iterator {
+    ptr *tab, *endtab;
+    i32 elemsize, arrlen;
+    ptr entry;
+} bh_table_iterator;
+
+typedef struct bh__table {
+    bh_allocator allocator;
+    u64 table_size; // NOTE: u64 since padding will make it 8-bytes no matter what
+    ptr arrs[];
+} bh__table;
+
+#define bh_table(T)        T*
+
+#ifdef BH_TABLE_SIZE_SAFE
+    #define bh_table_init(allocator_, tab, hs)    bh__table_init(allocator_, (bh__table **)&(tab), hs)
+    #define bh_table_free(tab)                    bh__table_free((bh__table **)&(tab))
+    #define bh_table_put(T, tab, key, value)      (assert(sizeof(T) == sizeof(*(tab))), (*((T *) bh__table_put((bh__table *) tab, sizeof(T), key)) = (T) value))
+    #define bh_table_has(T, tab, key)             (assert(sizeof(T) == sizeof(*(tab))), (bh__table_has((bh__table *) tab, sizeof(T), key)))
+    #define bh_table_get(T, tab, key)             (assert(sizeof(T) == sizeof(*(tab))), (*((T *) bh__table_get((bh__table *) tab, sizeof(T), key))))
+    #define bh_table_delete(T, tab, key)          (assert(sizeof(T) == sizeof(*(tab))), bh__table_delete((bh__table *) tab, sizeof(T), key))
+    #define bh_table_clear(tab)                   (bh__table_clear((bh__table *) tab))
+
+    #define bh_table_iter_setup(T, tab)           (assert(sizeof(T) == sizeof(*(tab))), bh__table_iter_setup((bh__table *) tab, sizeof(T)))
+    #define bh_table_iter_key(it)                 ((char *)(bh_pointer_add(it.entry, it.elemsize + sizeof(u16))))
+    #define bh_table_iter_value(T, it)            (*(T *)it.entry)
+#else
+    #define bh_table_init(allocator_, tab, hs)    bh__table_init(allocator_, (bh__table **)&(tab), hs)
+    #define bh_table_free(tab)                    bh__table_free((bh__table **)&(tab))
+    #define bh_table_put(T, tab, key, value)      (*((T *) bh__table_put((bh__table *) tab, sizeof(T), key)) = value)
+    #define bh_table_has(T, tab, key)             (bh__table_has((bh__table *) tab, sizeof(T), key))
+    #define bh_table_get(T, tab, key)             (*((T *) bh__table_get((bh__table *) tab, sizeof(T), key)))
+    #define bh_table_delete(T, tab, key)          (bh__table_delete((bh__table *) tab, sizeof(T), key))
+    #define bh_table_clear(tab)                   (bh__table_clear((bh__table *) tab))
+
+    #define bh_table_iter_setup(T, tab)           (bh__table_iter_setup((bh__table *) tab, sizeof(T)))
+    #define bh_table_iter_key(it)                 ((char *)(bh_pointer_add(it.entry, it.elemsize + sizeof(u16))))
+    #define bh_table_iter_value(T, it)            (*(T *)it.entry)
+#endif
+
+#define bh_table_each_start(T, table) { \
+    bh_table_iterator it = bh_table_iter_setup(T, (table)); \
+    while (bh_table_iter_next(&it)) { \
+        const char* key = bh_table_iter_key(it); \
+        T value = bh_table_iter_value(T, it);
+#define bh_table_each_end            } }
+
+b32 bh__table_init(bh_allocator allocator, bh__table **table, i32 table_size);
+b32 bh__table_free(bh__table **table);
+ptr bh__table_put(bh__table *table, i32 elemsize, char *key);
+b32 bh__table_has(bh__table *table, i32 elemsize, char *key);
+ptr bh__table_get(bh__table *table, i32 elemsize, char *key);
+void bh__table_delete(bh__table *table, i32 elemsize, char *key);
+void bh__table_clear(bh__table *table);
+bh_table_iterator bh__table_iter_setup(bh__table *table, i32 elemsize);
+b32 bh_table_iter_next(bh_table_iterator* it);
+
+#endif
+// Using stb_ds for tables now because they are better in every single way.
+#define Table(T) struct { char *key; T value; } *
+
+
+
+
+
+
+
+
+//-------------------------------------------------------------------------------
+// IMAP (integer to integer map)
+//-------------------------------------------------------------------------------
+#ifndef BH_NO_IMAP
+
+typedef u64 bh_imap_entry_t;
+
+typedef struct bh__imap_lookup_result {
+    i64 hash_index;
+    i64 entry_prev;
+    i64 entry_index;
+} bh__imap_lookup_result;
+
+typedef struct bh__imap_entry {
+    bh_imap_entry_t key, value;
+    i64 next;
+} bh__imap_entry;
+
+typedef struct bh_imap {
+    bh_allocator allocator;
+
+    bh_arr(i64)            hashes;
+    bh_arr(bh__imap_entry) entries;
+} bh_imap;
+
+
+void bh_imap_init(bh_imap* imap, bh_allocator alloc, i32 hash_count);
+void bh_imap_free(bh_imap* imap);
+void bh_imap_put(bh_imap* imap, bh_imap_entry_t key, bh_imap_entry_t value);
+b32 bh_imap_has(bh_imap* imap, bh_imap_entry_t key);
+bh_imap_entry_t bh_imap_get(bh_imap* imap, bh_imap_entry_t key);
+void bh_imap_delete(bh_imap* imap, bh_imap_entry_t key);
+void bh_imap_clear(bh_imap* imap);
+
+#ifdef BH_DEFINE
+#endif // BH_DEFINE
+
+
+#endif
+
+
+
+
+
+
+
+
+
+
+
+// MANAGED HEAP ALLOCATOR
+typedef struct bh_managed_heap {
+    bh_imap ptrs;
+} bh_managed_heap;
+
+void bh_managed_heap_init(bh_managed_heap* mh);
+void bh_managed_heap_free(bh_managed_heap* mh);
+bh_allocator bh_managed_heap_allocator(bh_managed_heap* mh);
+BH_ALLOCATOR_PROC(bh_managed_heap_allocator_proc);
+
+
+
+
+
+
+
+
+
+
+
+//-------------------------------------------------------------------------------
+// OTHER COMMON DATA STRUCTURES
+//-------------------------------------------------------------------------------
+#ifndef BH_NO_DATASTRUCTURES
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+#endif // BH_NO_DATASTRUCTURES
+
+
+
+
+
+//------------------------------------------------------------------------------
+// TIME / DURATION
+//------------------------------------------------------------------------------
+u64 bh_time_curr();
+u64 bh_time_duration(u64 old);
+
+
+
+
+
+
+
+
+
+
+
+#ifdef BH_DEFINE
+
+#undef BH_DEFINE
+//-------------------------------------------------------------------------------------
+// IMPLEMENTATIONS
+//-------------------------------------------------------------------------------------
+
+//-------------------------------------------------------------------------------------
+// CHAR FUNCTIONS
+//-------------------------------------------------------------------------------------
+
+b32 char_is_alpha(const char a) {
+    return ('a' <= a && a <= 'z') || ('A' <= a && a <= 'Z');
+}
+
+char charset_contains(const char* charset, char ch) {
+    while (*charset) {
+        if (*charset == ch) return ch;
+        charset++;
+    }
+
+    return 0;
+}
+
+b32 char_is_num(const char a) {
+    return ('0' <= a && a <= '9');
+}
+
+b32 char_is_alphanum(const char a) {
+    return char_is_alpha(a) || char_is_num(a);
+}
+
+b32 char_is_whitespace(const char a) {
+    return charset_contains(" \t\r\n", a);
+}
+
+b32 char_in_range(const char lo, const char hi, const char a) {
+    return lo <= a && a <= hi;
+}
+
+i64 chars_match(char* ptr1, char* ptr2) {
+    i64 len = 0;
+    while (*ptr2 != '\0' && *ptr1 == *ptr2) ptr1++, ptr2++, len++;
+    return *ptr2 == '\0' ? len : 0;
+}
+
+
+
+
+
+
+
+
+//-------------------------------------------------------------------------------------
+// CUSTOM ALLOCATORS IMPLEMENTATION
+//-------------------------------------------------------------------------------------
+ptr bh_alloc(bh_allocator a, isize size) {
+    return bh_alloc_aligned(a, size, 16);
+}
+
+ptr bh_alloc_aligned(bh_allocator a, isize size, isize alignment) {
+    return a.proc(a.data, bh_allocator_action_alloc, size, alignment, NULL,  0);
+}
+
+ptr bh_resize(bh_allocator a, ptr data, isize new_size) {
+    return bh_resize_aligned(a, data, new_size, 16);
+}
+
+ptr bh_resize_aligned(bh_allocator a, ptr data, isize new_size, isize alignment) {
+    return a.proc(a.data, bh_allocator_action_resize, new_size, alignment, data, 0);
+}
+
+void bh_free(bh_allocator a, ptr data) {
+    if (data != NULL) a.proc(a.data, bh_allocator_action_free, 0, 0, data, 0);
+}
+
+
+
+// HEAP ALLOCATOR IMPLEMENTATION
+bh_allocator bh_heap_allocator(void) {
+    return (bh_allocator) {
+        .proc = bh_heap_allocator_proc,
+        .data = NULL
+    };
+}
+
+BH_ALLOCATOR_PROC(bh_heap_allocator_proc) {
+    ptr retval = NULL;
+
+    switch (action) {
+    case bh_allocator_action_alloc: {
+#if defined(_BH_WINDOWS)
+        retval = _aligned_malloc(size, alignment);
+#elif defined(_BH_LINUX)
+        i32 success = posix_memalign(&retval, alignment, size);
+#endif
+        if (flags & bh_allocator_flag_clear && retval != NULL) {
+            memset(retval, 0, size);
+        }
+    } break;
+
+    case bh_allocator_action_resize: {
+        // TODO: Maybe replace with better custom function
+#if defined(_BH_WINDOWS)
+        retval = _aligned_realloc(prev_memory, size, alignment);
+#elif defined(_BH_LINUX)
+        retval = realloc(prev_memory, size);
+#endif
+    } break;
+
+    case bh_allocator_action_free: {
+#if defined(_BH_WINDOWS)
+        _aligned_free(prev_memory);
+#elif defined(_BH_LINUX)
+        free(prev_memory);
+#endif
+    } break;
+    }
+
+    return retval;
+}
+
+
+
+
+
+
+// MANAGED HEAP ALLOCATOR IMPLEMENTATION
+void bh_managed_heap_init(bh_managed_heap* mh) {
+    bh_imap_init(&mh->ptrs, bh_heap_allocator(), 512);
+}
+
+void bh_managed_heap_free(bh_managed_heap* mh) {
+    bh_arr_each(bh__imap_entry, p, mh->ptrs.entries) {
+#if defined(_BH_WINDOWS)
+        _aligned_free((void *) p->key);
+#elif defined(_BH_LINUX)
+        free((void *) p->key);
+#endif
+    }
+
+    bh_imap_free(&mh->ptrs);
+}
+
+bh_allocator bh_managed_heap_allocator(bh_managed_heap* mh) {
+    return (bh_allocator) {
+        .proc = bh_managed_heap_allocator_proc,
+        .data = mh
+    };
+}
+
+BH_ALLOCATOR_PROC(bh_managed_heap_allocator_proc) {
+    bh_managed_heap* mh = (bh_managed_heap *) data;
+    ptr retval = NULL;
+
+    switch (action) {
+    case bh_allocator_action_alloc: {
+#if defined(_BH_WINDOWS)
+        retval = _aligned_malloc(size, alignment);
+#elif defined(_BH_LINUX)
+        i32 success = posix_memalign(&retval, alignment, size);
+#endif
+
+        if (flags & bh_allocator_flag_clear && retval != NULL) {
+            memset(retval, 0, size);
+        }
+
+        if (retval != NULL)
+            bh_imap_put(&mh->ptrs, (u64) retval, 1);
+    } break;
+
+    case bh_allocator_action_resize: {
+        bh_imap_delete(&mh->ptrs, (u64) prev_memory);
+#if defined(_BH_WINDOWS)
+        retval = _aligned_realloc(prev_memory, size, alignment);
+#elif defined(_BH_LINUX)
+        retval = realloc(prev_memory, size);
+#endif
+        bh_imap_put(&mh->ptrs, (u64) retval, 1);
+    } break;
+
+    case bh_allocator_action_free: {
+        bh_imap_delete(&mh->ptrs, (u64) prev_memory);
+#if defined(_BH_WINDOWS)
+        _aligned_free(prev_memory);
+#elif defined(_BH_LINUX)
+        free(prev_memory);
+#endif
+    } break;
+    }
+
+    return retval;
+}
+
+
+
+
+
+
+
+
+// ARENA ALLOCATOR IMPLEMENTATION
+void bh_arena_init(bh_arena* alloc, bh_allocator backing, isize arena_size) {
+    arena_size = bh_max(arena_size, size_of(ptr));
+    ptr data = bh_alloc(backing, arena_size);
+
+    alloc->backing = backing;
+    alloc->arena_size = arena_size;
+    alloc->size = sizeof(ptr);
+    alloc->first_arena = data;
+    alloc->current_arena = data;
+
+    ((bh__arena_internal *)(alloc->first_arena))->next_arena = NULL;
+}
+
+void bh_arena_free(bh_arena* alloc) {
+    bh__arena_internal *walker = (bh__arena_internal *) alloc->first_arena;
+    bh__arena_internal *trailer = walker;
+    while (walker != NULL) {
+        walker = walker->next_arena;
+        bh_free(alloc->backing, trailer);
+        trailer = walker;
+    }
+
+    alloc->first_arena = NULL;
+    alloc->current_arena = NULL;
+    alloc->arena_size = 0;
+    alloc->size = 0;
+}
+
+bh_allocator bh_arena_allocator(bh_arena* alloc) {
+    return (bh_allocator) {
+        .proc = bh_arena_allocator_proc,
+        .data = alloc,
+    };
+}
+
+BH_ALLOCATOR_PROC(bh_arena_allocator_proc) {
+    bh_arena* alloc_arena = (bh_arena*) data;
+
+    ptr retval = NULL;
+
+    switch (action) {
+    case bh_allocator_action_alloc: {
+        bh_align(size, alignment);
+        bh_align(alloc_arena->size, alignment);
+
+        if (size > alloc_arena->arena_size - size_of(ptr)) {
+            // Size too large for the arena
+            return NULL;
+        }
+
+        if (alloc_arena->size + size >= alloc_arena->arena_size) {
+            bh__arena_internal* new_arena = (bh__arena_internal *) bh_alloc(alloc_arena->backing, alloc_arena->arena_size);
+
+            if (new_arena == NULL) {
+                bh_printf_err("Arena Allocator: couldn't allocate new arena");
+                return NULL;
+            }
+
+            new_arena->next_arena = NULL;
+            ((bh__arena_internal *)(alloc_arena->current_arena))->next_arena = new_arena;
+            alloc_arena->current_arena = new_arena;
+            alloc_arena->size = sizeof(ptr);
+        }
+
+        retval = bh_pointer_add(alloc_arena->current_arena, alloc_arena->size);
+        alloc_arena->size += size;
+    } break;
+
+    case bh_allocator_action_resize: {
+        // Do nothing since this is a fixed allocator
+    } break;
+
+    case bh_allocator_action_free: {
+        // Do nothing since this allocator isn't made for freeing memory
+    } break;
+    }
+
+    return retval;
+}
+
+
+
+
+// SCRATCH ALLOCATOR IMPLEMENTATION
+void bh_scratch_init(bh_scratch* scratch, bh_allocator backing, isize scratch_size) {
+    ptr memory = bh_alloc(backing, scratch_size);
+
+    scratch->backing = backing;
+    scratch->memory = memory;
+    scratch->curr = memory;
+    scratch->end = bh_pointer_add(memory, scratch_size);
+}
+
+void bh_scratch_free(bh_scratch* scratch) {
+    bh_free(scratch->backing, scratch->memory);
+
+    scratch->memory = NULL;
+    scratch->curr = NULL;
+    scratch->end = NULL;
+}
+
+bh_allocator bh_scratch_allocator(bh_scratch* scratch) {
+    return (bh_allocator) {
+        .proc = bh_scratch_allocator_proc,
+        .data = scratch,
+    };
+}
+
+BH_ALLOCATOR_PROC(bh_scratch_allocator_proc) {
+    bh_scratch* scratch = (bh_scratch*) data;
+    ptr retval = NULL;
+
+    switch (action) {
+    case bh_allocator_action_alloc: {
+        if (size > ((u8 *) scratch->end) - ((u8 *) scratch->memory)) {
+            return NULL;
+        }
+
+        retval = scratch->curr;
+        scratch->curr = bh_pointer_add(scratch->curr, size);
+
+        if (scratch->curr >= scratch->end) {
+            scratch->curr = scratch->memory;
+            retval = scratch->curr;
+        }
+    } break;
+
+    case bh_allocator_action_free: break;
+
+    case bh_allocator_action_resize: {
+        if (size > ((u8 *) scratch->end) - ((u8 *) scratch->memory)) {
+            return NULL;
+        }
+
+        retval = scratch->curr;
+        scratch->curr = bh_pointer_add(scratch->curr, size);
+
+        if (scratch->curr >= scratch->end) {
+            scratch->curr = scratch->memory;
+            retval = scratch->curr;
+        }
+
+        // HACK!!!!!: Using size instead of some kind of "old size"
+        memcpy(retval, prev_memory, size);
+    } break;
+    }
+
+    return retval;
+}
+
+
+
+
+//-------------------------------------------------------------------------------------
+// CONVERSION FUNCTIONS IMPLEMENTATION
+//-------------------------------------------------------------------------------------
+u8* uint_to_uleb128(u64 n, i32* output_length) {
+    static u8 buffer[16];
+
+    *output_length = 0;
+    u8* output = buffer;
+    u8 byte;
+    do {
+        byte = n & 0x7f;
+        n >>= 7;
+        if (n != 0) byte |= (1 << 7);
+        *output++ = byte;
+        (*output_length)++;
+    } while (n != 0);
+
+    return buffer;
+}
+
+
+// Converts a signed integer to the signed LEB128 format
+u8* int_to_leb128(i64 n, i32* output_length) {
+    static u8 buffer[16];
+
+    *output_length = 0;
+    u8* output = buffer;
+    b32 more = 1;
+
+    i32 size = 64;
+
+    u8 byte;
+    do {
+        byte = n & 0x7f;
+        n >>= 7;
+
+        more = !(((n == 0) && (byte & 0x40) == 0) || ((n == -1) && (byte & 0x40) != 0));
+        if (more)
+            byte |= 0x80;
+        *output++ = byte;
+        (*output_length)++;
+    } while (more);
+
+    return buffer;
+}
+
+// NOTE: This assumes the underlying implementation of float on the host
+// system is already IEEE-754. This is safe to assume in most cases.
+u8* float_to_ieee754(f32 f, b32 reverse) {
+    static u8 buffer[4];
+
+    u8* fmem = (u8*) &f;
+    if (reverse) {
+        buffer[0] = fmem[3];
+        buffer[1] = fmem[2];
+        buffer[2] = fmem[1];
+        buffer[3] = fmem[0];
+    } else {
+        buffer[0] = fmem[0];
+        buffer[1] = fmem[1];
+        buffer[2] = fmem[2];
+        buffer[3] = fmem[3];
+    }
+
+    return buffer;
+}
+
+u8* double_to_ieee754(f64 f, b32 reverse) {
+    static u8 buffer[8];
+
+    u8* fmem = (u8*) &f;
+    if (reverse) {
+        buffer[0] = fmem[7];
+        buffer[1] = fmem[6];
+        buffer[2] = fmem[5];
+        buffer[3] = fmem[4];
+        buffer[4] = fmem[3];
+        buffer[5] = fmem[2];
+        buffer[6] = fmem[1];
+        buffer[7] = fmem[0];
+    } else {
+        buffer[0] = fmem[0];
+        buffer[1] = fmem[1];
+        buffer[2] = fmem[2];
+        buffer[3] = fmem[3];
+        buffer[4] = fmem[4];
+        buffer[5] = fmem[5];
+        buffer[6] = fmem[6];
+        buffer[7] = fmem[7];
+    }
+
+    return buffer;
+}
+
+u64 uleb128_to_uint(u8* bytes, i32 *byte_count) {
+    u64 res = 0;
+    u64 shift = 0;
+
+    while (1) {
+        u8 byte = bytes[(*byte_count)++];
+        res |= (byte & 0x7f) << shift;
+        if ((byte & 0x80) == 0) break;
+        shift += 7;
+    }
+    return res;
+}
+
+
+
+//-------------------------------------------------------------------------------------
+// STRING IMPLEMENTATION
+//-------------------------------------------------------------------------------------
+b32 bh_str_starts_with(char* str, char* start) {
+    char* s = str;
+    char* p = start;
+
+    while (*s != '\0' && *p != '\0' && *s == *p) s++, p++;
+
+    return *p == '\0';
+}
+
+b32 bh_str_ends_with(char* str, char* end) {
+    i32 slen = strlen(str);
+    i32 elen = strlen(end);
+
+    char* s = str + slen - 1;
+    char* e = end + elen - 1;
+
+    while (*e == *s && e != end && s != str) e--, s--;
+
+    return *e == *s;
+}
+
+char* bh_strdup(bh_allocator a, char* str) {
+    u32 len = strlen(str);
+    char* buf = bh_alloc(a, len + 1);
+
+    char* t = buf;
+    while ((*t++ = *str++));
+    return buf;
+}
+
+
+
+
+
+//-------------------------------------------------------------------------------------
+// FILE IMPLEMENTATION
+//-------------------------------------------------------------------------------------
+#ifndef BH_NO_FILE
+
+static b32 bh__file_seek_wrapper(bh_file_descriptor fd, i64 offset, bh_file_whence whence, i64* new_offset);
+
+bh_file_error bh_file_get_standard(bh_file* file, bh_file_standard stand) {
+    const char* filename = NULL;
+
+#if defined(_BH_WINDOWS)
+    bh_file_descriptor sd_fd;
+
+    switch (stand) {
+    case BH_FILE_STANDARD_INPUT:
+        sd_fd = GetStdHandle(STD_INPUT_HANDLE);
+        filename = "stdin";
+        break;
+    case BH_FILE_STANDARD_OUTPUT:
+        sd_fd = GetStdHandle(STD_OUTPUT_HANDLE);
+        filename = "stdout";
+        break;
+    case BH_FILE_STANDARD_ERROR:
+        sd_fd = GetStdHandle(STD_ERROR_HANDLE);
+        filename = "stderr";
+        break;
+    default:
+        return BH_FILE_ERROR_BAD_FD;
+    }
+    file->fd = sd_fd;
+
+#elif defined(_BH_LINUX)
+    i32 sd_fd = -1;
+
+    switch (stand) {
+    case BH_FILE_STANDARD_INPUT:
+        sd_fd = STDIN_FILENO;
+        filename = "stdin"; // These are constants in the data section so everything should be okay
+        break;
+    case BH_FILE_STANDARD_OUTPUT:
+        sd_fd = STDOUT_FILENO;
+        filename = "stdout";
+        break;
+    case BH_FILE_STANDARD_ERROR:
+        sd_fd = STDERR_FILENO;
+        filename = "stderr";
+        break;
+    default:
+        return BH_FILE_ERROR_BAD_FD;
+    }
+
+    file->fd = sd_fd;
+#endif
+
+
+    file->filename = filename;
+    return BH_FILE_ERROR_NONE;
+}
+
+bh_file_error bh_file_create(bh_file* file, const char* filename) {
+    // Need to do this to avoid compiler complaining about types
+    bh_file_mode write_rw = (bh_file_mode) (BH_FILE_MODE_WRITE | BH_FILE_MODE_RW);
+    return bh_file_open_mode(file, write_rw, filename);
+}
+
+bh_file_error bh_file_open(bh_file* file, const char* filename) {
+    return bh_file_open_mode(file, BH_FILE_MODE_READ, filename);
+}
+
+bh_file_error bh_file_open_mode(bh_file* file, bh_file_mode mode, const char* filename) {
+#if defined(_BH_WINDOWS)
+    DWORD desired_access;
+    DWORD creation_disposition;
+
+    switch (mode & BH_FILE_MODE_MODES) {
+        case BH_FILE_MODE_READ:
+            desired_access = GENERIC_READ;
+            creation_disposition = OPEN_EXISTING;
+            break;
+        case BH_FILE_MODE_WRITE:
+            desired_access = GENERIC_WRITE;
+            creation_disposition = CREATE_ALWAYS;
+            break;
+        case BH_FILE_MODE_APPEND:
+            desired_access = GENERIC_WRITE;
+            creation_disposition = OPEN_ALWAYS;
+            break;
+        case BH_FILE_MODE_READ | BH_FILE_MODE_RW:
+            desired_access = GENERIC_READ | GENERIC_WRITE;
+            creation_disposition = OPEN_EXISTING;
+            break;
+        case BH_FILE_MODE_WRITE | BH_FILE_MODE_RW:
+            desired_access = GENERIC_READ | GENERIC_WRITE;
+            creation_disposition = CREATE_ALWAYS;
+            break;
+        case BH_FILE_MODE_APPEND | BH_FILE_MODE_RW:
+            desired_access = GENERIC_READ | GENERIC_WRITE;
+            creation_disposition = OPEN_ALWAYS;
+            break;
+        default:
+            return BH_FILE_ERROR_INVALID;
+    }
+
+
+    file->fd = CreateFileA(filename,
+                    desired_access,
+                    FILE_SHARE_READ,
+                    NULL,
+                    creation_disposition,
+                    FILE_ATTRIBUTE_NORMAL,
+                    NULL);
+
+    if (file->fd == INVALID_HANDLE_VALUE) {
+        return BH_FILE_ERROR_INVALID;
+    }
+
+    file->filename = filename;
+    return BH_FILE_ERROR_NONE;
+
+#elif defined(_BH_LINUX)
+    i32 os_mode = 0;
+
+    switch (mode & BH_FILE_MODE_MODES) {
+    case BH_FILE_MODE_READ:                       os_mode = O_RDONLY; break;
+    case BH_FILE_MODE_WRITE:                      os_mode = O_WRONLY | O_CREAT | O_TRUNC; break;
+    case BH_FILE_MODE_APPEND:                     os_mode = O_RDONLY | O_APPEND | O_CREAT; break;
+    case BH_FILE_MODE_READ   | BH_FILE_MODE_RW:   os_mode = O_RDWR; break;
+    case BH_FILE_MODE_WRITE  | BH_FILE_MODE_RW:   os_mode = O_RDWR | O_CREAT | O_TRUNC; break;
+    case BH_FILE_MODE_APPEND | BH_FILE_MODE_RW:   os_mode = O_RDWR | O_APPEND | O_CREAT; break;
+    //default: // TODO Handle errors
+    }
+
+    file->fd = open(filename, os_mode,
+        S_IRUSR | S_IWUSR | S_IROTH | S_IWOTH | S_IRGRP | S_IWGRP //+rw-rw-rw-
+    );
+    if (file->fd < 0) {
+        return BH_FILE_ERROR_INVALID;
+    }
+
+    // TODO: Set this using some allocator
+    file->filename = filename;
+
+    return BH_FILE_ERROR_NONE;
+#endif
+}
+
+bh_file_error bh_file_new(bh_file* file, bh_file_descriptor fd, const char* filename) {
+    file->filename = filename; // This may be unsafe
+    file->fd = fd;
+    return BH_FILE_ERROR_NONE;
+}
+
+b32 bh_file_read_at(bh_file* file, i64 offset, void* buffer, isize buff_size, isize* bytes_read) {
+#if defined(_BH_WINDOWS)
+    bh_file_seek_to(file, offset);
+    BOOL res = ReadFile(file->fd, buffer, buff_size, (i32 *) bytes_read, NULL);
+    if (res) return 1;
+    else     return 0;
+
+#elif defined(_BH_LINUX)
+    if (file->fd == 0) {
+        isize res = read(file->fd, buffer, buff_size);
+        if (res < 0) return 0;
+        if (bytes_read) *bytes_read = res;
+        return 1;
+    }
+
+    isize res = pread(file->fd, buffer, buff_size, offset);
+    if (res < 0) return 0;
+    if (bytes_read) *bytes_read = res;
+    return 1;
+#endif
+}
+
+b32 bh_file_write_at(bh_file* file, i64 offset, void const* buffer, isize buff_size, isize* bytes_wrote) {
+    isize res;
+    i64 current_offset = 0;
+    bh__file_seek_wrapper(file->fd, 0, BH_FILE_WHENCE_CURRENT, &current_offset);
+
+#if defined(_BH_WINDOWS)
+    if (current_offset != offset) bh_file_seek_to(file, offset);
+    res = (isize) WriteFile(file->fd, buffer, buff_size, (i32 *) bytes_wrote, NULL);
+    return res;
+
+#elif defined(_BH_LINUX)
+    if (current_offset == offset || file->fd == 1 || file->fd == 2) {
+        // Standard in and out do like pwrite()
+        res = write(file->fd, buffer, buff_size);
+    } else {
+        res = pwrite(file->fd, buffer, buff_size, offset);
+    }
+    if (res < 0) return 0;
+    if (bytes_wrote) *bytes_wrote = res;
+
+    return 1;
+#endif
+}
+
+static b32 bh__file_seek_wrapper(bh_file_descriptor fd, i64 offset, bh_file_whence whence, i64* new_offset) {
+#if defined(_BH_WINDOWS)
+    LARGE_INTEGER new_file_pointer;
+    LARGE_INTEGER dest;
+    dest.QuadPart = offset;
+
+    BOOL res = SetFilePointerEx(fd, dest, &new_file_pointer, whence);
+    *new_offset = new_file_pointer.QuadPart;
+
+    return res;
+
+#elif defined(_BH_LINUX)
+    i64 res = lseek64(fd, offset, whence);
+    if (res < 0) return 0;
+    if (new_offset) *new_offset = res;
+    return 1;
+#endif
+}
+
+// Returns new offset
+i64 bh_file_seek(bh_file* file, i64 offset, bh_file_whence whence) {
+    i64 new_offset = -1;
+    bh__file_seek_wrapper(file->fd, offset, whence, &new_offset);
+    return new_offset;
+}
+
+i64 bh_file_seek_to(bh_file* file, i64 offset) {
+    i64 new_offset = -1;
+    bh__file_seek_wrapper(file->fd, offset, BH_FILE_WHENCE_BEGIN, &new_offset);
+    return new_offset;
+}
+
+i64 bh_file_seek_to_end(bh_file* file) {
+    i64 new_offset = -1;
+    bh__file_seek_wrapper(file->fd, 0, BH_FILE_WHENCE_END, &new_offset);
+    return new_offset;
+}
+
+i64 bh_file_skip(bh_file* file, i64 bytes) {
+    i64 new_offset = 0;
+    bh__file_seek_wrapper(file->fd, bytes, BH_FILE_WHENCE_CURRENT, &new_offset);
+    return new_offset;
+}
+
+i64 bh_file_tell(bh_file* file) {
+    i64 new_offset = 0;
+    bh__file_seek_wrapper(file->fd, 0, BH_FILE_WHENCE_CURRENT, &new_offset);
+    return new_offset;
+}
+
+bh_file_error bh_file_close(bh_file* file) {
+    bh_file_error err = BH_FILE_ERROR_NONE;
+
+#if defined(_BH_WINDOWS)
+    BOOL success = CloseHandle(file->fd);
+    if (!success) err = BH_FILE_ERROR_INVALID;
+
+    return err;
+
+#elif defined(_BH_LINUX)
+    i32 res = close(file->fd);
+    if (res < 0)
+        err = BH_FILE_ERROR_INVALID;
+
+    return err;
+#endif
+}
+
+b32 bh_file_read(bh_file* file, void* buffer, isize buff_size) {
+    return bh_file_read_at(file, bh_file_tell(file), buffer, buff_size, NULL);
+}
+
+b32 bh_file_write(bh_file* file, void* buffer, isize buff_size) {
+    return bh_file_write_at(file, bh_file_tell(file), buffer, buff_size, NULL);
+}
+
+void bh_file_flush(bh_file* file) {
+    #ifdef _BH_LINUX
+    fdatasync(file->fd);
+    #endif
+}
+
+i64 bh_file_size(bh_file* file) {
+    i64 size = 0;
+    i64 prev = bh_file_tell(file);
+    bh_file_seek_to_end(file);
+    size = bh_file_tell(file);
+    bh_file_seek_to(file, prev);
+    return size;
+}
+
+bh_file_contents bh_file_read_contents_bh_file(bh_allocator alloc, bh_file* file) {
+    bh_file_contents fc = {
+        .allocator = alloc,
+        .filename  = bh_strdup(alloc, (char *) file->filename),
+        .length = 0, .data = NULL
+    };
+
+    isize size = bh_file_size(file);
+    if (size <= 0) return fc;
+
+    fc.data = bh_alloc(alloc, size + 1);
+    fc.length = size;
+    bh_file_read_at(file, 0, fc.data, fc.length, NULL);
+    ((u8*) fc.data)[fc.length] = '\0';
+
+    return fc;
+}
+
+bh_file_contents bh_file_read_contents_direct(bh_allocator alloc, const char* filename) {
+    bh_file file;
+    bh_file_open(&file, filename);
+    bh_file_contents fc = bh_file_read_contents(alloc, &file);
+    bh_file_close(&file);
+    return fc;
+}
+
+b32 bh_file_contents_free(bh_file_contents* contents) {
+    bh_free(contents->allocator, contents->data);
+    contents->length = 0;
+    return 1;
+}
+
+b32 bh_file_exists(char const* filename) {
+    struct stat s;
+    return stat(filename, &s) != -1;
+}
+
+char* bh_path_get_full_name(char const* filename, bh_allocator a) {
+#if defined(_BH_WINDOWS)
+    char buffer[4096];
+    GetFullPathNameA(filename, 4096, buffer, NULL);
+
+    i32 len = strlen(buffer);
+    char* result = bh_alloc_array(a, char, len + 1);
+    memmove(result, buffer, len);
+    result[len] = 0;
+
+    return result;
+
+#elif defined(_BH_LINUX)
+    char* p = realpath(filename, NULL);    
+
+    // Check if the file did not exists.
+    // :Cleanup should this return NULL?
+    if (p == NULL) return (char *) filename;
+    
+    i32 len = strlen(p);
+    char* result = bh_alloc_array(a, char, len + 1);
+    memmove(result, p, len);
+    result[len] = 0;
+
+    free(p);
+
+    return result;
+#endif
+}
+
+// NOTE: This assumes the filename is the full path, not relative to anything else.
+#if defined(_BH_LINUX)
+    #define DIR_SEPARATOR '/'
+#elif defined(_BH_WINDOWS)
+    #define DIR_SEPARATOR '\\'
+#endif
+char* bh_path_get_parent(char const* filename, bh_allocator a) {
+
+    char* result = bh_strdup(a, (char *) filename);
+    char* end = result + strlen(result);
+    while (*end != DIR_SEPARATOR && end != result) *end-- = '\0';
+
+    return result;
+}
+
+// This function returns a volatile pointer. Do not store it without copying!
+char* bh_lookup_file(char* filename, char* relative_to, char *suffix, b32 add_suffix, bh_arr(const char *) included_folders, b32 search_included_folders) {
+    assert(relative_to != NULL);
+
+    static char path[512];
+    fori (i, 0, 512) path[i] = 0;
+
+    static char fn[256];
+    fori (i, 0, 256) fn[i] = 0;
+
+    if (!bh_str_ends_with(filename, suffix) && add_suffix) {
+        bh_snprintf(fn, 256, "%s%s", filename, suffix);
+    } else {
+        bh_snprintf(fn, 256, "%s", filename);
+    }
+
+    fori (i, 0, 256) if (fn[i] == '/') fn[i] = DIR_SEPARATOR;
+
+    if (bh_str_starts_with(filename, "./")) {
+        if (relative_to[strlen(relative_to) - 1] != DIR_SEPARATOR)
+            bh_snprintf(path, 512, "%s%c%s", relative_to, DIR_SEPARATOR, fn + 2);
+        else
+            bh_snprintf(path, 512, "%s%s", relative_to, fn + 2);
+
+        if (bh_file_exists(path)) return bh_path_get_full_name(path, bh_heap_allocator());
+
+        return fn;
+    }
+
+    if (search_included_folders) {
+        bh_arr_each(const char *, folder, included_folders) {
+            if ((*folder)[strlen(*folder) - 1] != DIR_SEPARATOR)
+                bh_snprintf(path, 512, "%s%c%s", *folder, DIR_SEPARATOR, fn);
+            else
+                bh_snprintf(path, 512, "%s%s", *folder, fn);
+
+            if (bh_file_exists(path)) return bh_path_get_full_name(path, bh_heap_allocator());
+        }
+    }
+
+    return fn;
+}
+
+//
+// Modifies the path in-place.
+char* bh_path_convert_separators(char* path) {
+#if defined(_BH_LINUX)
+    #define DIR_SEPARATOR '/'
+    #define OTHER_SEPARATOR '\\'
+#elif defined(_BH_WINDOWS)
+    #define DIR_SEPARATOR '\\'
+    #define OTHER_SEPARATOR '/'
+#endif
+
+    fori (i, 0, (i64) strlen(path)) {
+        if (path[i] == OTHER_SEPARATOR) {
+            path[i] = DIR_SEPARATOR;
+        }
+    }
+
+    return path;
+}
+
+
+bh_dir bh_dir_open(char* path) {
+#ifdef _BH_WINDOWS
+    char new_path[512] = { 0 };
+    strncpy(new_path, path, 511);
+    bh_path_convert_separators(new_path);
+    strncat(new_path, "\\*.*", 511);
+
+    Windows_Directory_Opened* dir = malloc(sizeof(Windows_Directory_Opened));
+    dir->hndl = FindFirstFileA(new_path, &dir->found_file);
+    if (dir->hndl == INVALID_HANDLE_VALUE) {
+        return NULL;
+    }
+
+    return dir;
+#endif
+
+#ifdef _BH_LINUX
+    DIR* dir = opendir(path);
+    return dir;
+#endif
+}
+
+b32 bh_dir_read(bh_dir dir, bh_dirent* out) {
+
+#ifdef _BH_WINDOWS
+    do {
+        BOOL success = FindNextFileA(dir->hndl, &dir->found_file);
+        if (!success) return 0;
+    } while (!strcmp(dir->found_file.cFileName, ".") || !strcmp(dir->found_file.cFileName, ".."));
+
+    if (out == NULL) return 1;
+
+    out->type = (dir->found_file.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
+        ? BH_DIRENT_DIRECTORY : BH_DIRENT_FILE;
+    out->id = 0;
+    out->name_length = strlen(dir->found_file.cFileName);
+    strncpy(out->name, dir->found_file.cFileName, 256);
+
+    return 1;
+#endif
+
+#ifdef _BH_LINUX
+    struct dirent *ent;
+    while (1) {
+        ent = readdir(dir);
+        if (ent == NULL) return 0;
+
+        // Skip the current directory and parent directory
+        if (strcmp(ent->d_name, ".") && strcmp(ent->d_name, "..")) break;
+    }
+
+    bh_dirent_type type = 0;
+    switch (ent->d_type) {
+        case DT_UNKNOWN: break;
+        case DT_BLK: type = BH_DIRENT_BLOCK; break;
+        case DT_CHR: type = BH_DIRENT_CHAR; break;
+        case DT_DIR: type = BH_DIRENT_DIRECTORY; break;
+        case DT_LNK: type = BH_DIRENT_SYMLINK; break;
+        case DT_REG: type = BH_DIRENT_FILE; break;
+        default: type = BH_DIRENT_OTHER; break;
+    }
+
+    if (out == NULL) return 1;
+
+    out->type = type;
+    out->id = (u32) ent->d_ino;
+    out->name_length = strlen(ent->d_name);
+    strncpy(out->name, ent->d_name, 256);
+
+    return 1;
+#endif
+}
+
+void bh_dir_close(bh_dir dir) {
+#ifdef _BH_WINDOWS
+    if (dir == NULL) return;
+
+    FindClose(dir->hndl);
+    free(dir);
+#endif
+
+#ifdef _BH_LINUX
+    if (dir == NULL) return;
+    closedir(dir);
+#endif
+}
+
+#undef DIR_SEPARATOR
+
+#endif // ifndef BH_NO_FILE
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+//-------------------------------------------------------------------------------------
+// ALTERNATE PRINTF IMPLEMENTATION
+//-------------------------------------------------------------------------------------
+isize bh_printf(char const *fmt, ...) {
+    isize res;
+    va_list va;
+    va_start(va, fmt);
+    res = bh_printf_va(fmt, va);
+    va_end(va);
+    return res;
+}
+
+isize bh_printf_va(char const *fmt, va_list va) {
+    bh_file file;
+    bh_file_get_standard(&file, BH_FILE_STANDARD_OUTPUT);
+    return bh_fprintf_va(&file, fmt, va);
+}
+
+isize bh_printf_err(char const *fmt, ...) {
+    isize res;
+    va_list va;
+    va_start(va, fmt);
+    res = bh_printf_err_va(fmt, va);
+    va_end(va);
+    return res;
+}
+
+isize bh_printf_err_va(char const *fmt, va_list va) {
+    bh_file file;
+    bh_file_get_standard(&file, BH_FILE_STANDARD_ERROR);
+    return bh_fprintf_va(&file, fmt, va);
+}
+
+isize bh_fprintf(bh_file* f, char const *fmt, ...) {
+    isize res;
+    va_list va;
+    va_start(va, fmt);
+    res = bh_fprintf_va(f, fmt, va);
+    va_end(va);
+    return res;
+}
+
+isize bh_fprintf_va(bh_file* f, char const *fmt, va_list va) {
+    static char buffer[4096];
+    isize len = bh_snprintf_va(buffer, sizeof(buffer), fmt, va);
+    bh_file_write(f, buffer, len - 1);
+    return len;
+}
+
+char* bh_bprintf(char const *fmt, ...) {
+    char* res;
+    va_list va;
+    va_start(va, fmt);
+    res = bh_bprintf_va(fmt, va);
+    va_end(va);
+    return res;
+}
+
+char* bh_bprintf_va(char const *fmt, va_list va) {
+    static char buffer[4096];
+    isize len = bh_snprintf_va(buffer, sizeof(buffer), fmt, va);
+    buffer[len - 1] = 0;
+    return buffer;
+}
+
+char* bh_aprintf(bh_allocator alloc, const char* fmt, ...) {
+    char* res;
+    va_list va;
+    va_start(va, fmt);
+    res = bh_aprintf_va(alloc, fmt, va);
+    va_end(va);
+    return res;
+}
+
+char* bh_aprintf_va(bh_allocator alloc, const char* fmt, va_list va) {
+    static char buffer[4096];
+    isize len = bh_snprintf_va(buffer, sizeof(buffer), fmt, va);
+    char* res = bh_alloc(alloc, len);
+    memcpy(res, buffer, len);
+    res[len - 1] = 0;
+    return res;
+}
+
+isize bh_snprintf(char *str, isize n, char const *fmt, ...) {
+    isize res;
+    va_list va;
+    va_start(va, fmt);
+    res = bh_snprintf_va(str, n, fmt, va);
+    va_end(va);
+    return res;
+}
+
+isize bh__print_string(char* dest, isize n, char* src) {
+    isize len = 0;
+    while (n-- && (*dest++ = *src++)) len++;
+    return len;
+}
+
+isize bh__printu64(char* str, isize n, bh__print_format format, u64 value) {
+    char buf[128];
+    buf[127] = 0;
+    char* walker = buf + 127;
+    u32 base = format.base ? format.base : 10, tmp;
+
+    while (value > 0) {
+        tmp = value % base;
+        if (tmp > 9) {
+            switch (tmp) {
+            case 10: tmp = 'a'; break;
+            case 11: tmp = 'b'; break;
+            case 12: tmp = 'c'; break;
+            case 13: tmp = 'd'; break;
+            case 14: tmp = 'e'; break;
+            case 15: tmp = 'f'; break;
+            }
+        } else {
+            tmp += '0';
+        }
+
+        *--walker = tmp;
+        value /= base;
+    }
+
+    if (format.base == 16) {
+        *--walker = 'x';
+        *--walker = '0';
+    }
+
+    return bh__print_string(str, n, walker);
+}
+
+isize bh__printi64(char* str, isize n, bh__print_format format, i64 value) {
+    char buf[128];
+    buf[127] = 0;
+    char* walker = buf + 127;
+    u32 base = format.base ? format.base : 10, tmp;
+
+    b32 negative = value < 0 ? 1 : 0;
+    if (negative) value = -value;
+
+    if (value == 0) {
+        *--walker = '0';
+    } else {
+        while (value > 0) {
+            tmp = value % base;
+            if (tmp > 9) {
+                switch (tmp) {
+                case 10: tmp = 'a'; break;
+                case 11: tmp = 'b'; break;
+                case 12: tmp = 'c'; break;
+                case 13: tmp = 'd'; break;
+                case 14: tmp = 'e'; break;
+                case 15: tmp = 'f'; break;
+                }
+            } else {
+                tmp += '0';
+            }
+
+            *--walker = tmp;
+            value /= base;
+        }
+    }
+
+    if (negative) {
+        *--walker = '-';
+    }
+
+    if (format.base == 16) {
+        *--walker = 'x';
+        *--walker = '0';
+    }
+
+    return bh__print_string(str, n, walker);
+}
+
+// TODO: This implementation is VERY VERY BAD AND WRONG. Fix it.
+isize bh__printf64(char* str, isize n, f64 value) {
+    fori (i, 0, 6) value *= 10.0;
+    i64 v = (i64) value;
+
+    isize l1 = bh__printi64(str, n, ((bh__print_format) { .base = 10 }), v / 1000000);
+    str += l1;
+    n -= l1;
+
+    *str = '.';
+    str += 1;
+    n -= 1;
+
+    isize l2 = bh__printi64(str, n, ((bh__print_format) { .base = 10 }), bh_abs(v) % 1000000);
+
+    return l1 + l2 + 1;
+}
+
+// TODO: This is very hacked together but for now it will work.
+isize bh_snprintf_va(char *str, isize n, char const *fmt, va_list va) {
+    char const *text_start = str;
+    isize res;
+
+    while (*fmt) {
+        bh__print_format format = { 0 };
+        isize len = 0;
+
+        while (*fmt && *fmt != '%') {
+            *(str++) = *(fmt++);
+        }
+
+        if (!*fmt) goto end_of_format;
+
+        fmt++;
+
+        switch (*fmt++) {
+        case 'o': format.base = 8; break;
+        case 'x': format.base = 16; break;
+        default: fmt--;
+        }
+
+        switch (*fmt) {
+        case 'c': {
+            char c = (char) va_arg(va, int);
+            *(str++) = c;
+        } break;
+
+        case 'd': {
+            i64 value = (i64) va_arg(va, int);
+            len = bh__printi64(str, n, format, value);
+        } break;
+
+        case 'l': {
+            i64 value = (i64) va_arg(va, long);
+            len = bh__printi64(str, n, format, value);
+        } break;
+
+        case 'p': {
+            u64 value = (u64) va_arg(va, ptr);
+            format.base = 16;
+            len = bh__printu64(str, n, format, value);
+        } break;
+
+        case 's': {
+            char* s = va_arg(va, char *);
+            len = bh__print_string(str, n, s);
+        } break;
+
+        case 'b': { // String with a length (not null terminated)
+            char* s = va_arg(va, char *);
+            i32 l = va_arg(va, int);
+            len = bh__print_string(str, bh_min(l, n), s);
+        } break;
+
+        case 'f': {
+            f64 f = va_arg(va, f64);
+            len = bh__printf64(str, n, f);
+        } break;
+
+        default: fmt--;
+        }
+
+        fmt++;
+
+end_of_format:
+        str += len;
+        n -= len;
+    }
+
+    return str - text_start + 1;
+}
+
+
+
+
+
+//-------------------------------------------------------------------------------------
+// FLEXIBLE BUFFER IMPLEMENTATION
+//-------------------------------------------------------------------------------------
+#ifndef BH_NO_BUFFER
+
+void bh_buffer_init(bh_buffer* buffer, bh_allocator alloc, i32 init_size) {
+    buffer->allocator = alloc;
+    buffer->length = 0;
+    buffer->capacity = init_size;
+    buffer->data = bh_alloc(alloc, init_size);
+}
+
+void bh_buffer_free(bh_buffer* buffer) {
+    bh_free(buffer->allocator, buffer->data);
+    buffer->length = 0;
+    buffer->capacity = 0;
+}
+
+void bh_buffer_grow(bh_buffer* buffer, i32 length) {
+    if (buffer == NULL) return;
+
+    if (buffer->capacity >= length) {
+        // NOTE: Already have enough room
+        return;
+    }
+
+    i32 newcap = buffer->capacity;
+    while (newcap < length) newcap = BH_BUFFER_GROW_FORMULA(newcap);
+
+    ptr new_data = bh_resize(buffer->allocator, buffer->data, newcap);
+    if (new_data == NULL) return;
+
+    buffer->capacity = newcap;
+    buffer->data = new_data;
+}
+
+void bh_buffer_append(bh_buffer* buffer, const void * data, i32 length) {
+    if (buffer == NULL) return;
+
+    if (buffer->length + length > buffer->capacity) {
+        bh_buffer_grow(buffer, buffer->length + length);
+    }
+
+    memcpy(buffer->data + buffer->length, data, length);
+    buffer->length += length;
+}
+
+void bh_buffer_concat(bh_buffer* buffer, bh_buffer other) {
+    bh_buffer_append(buffer, other.data, other.length);
+}
+
+void bh_buffer_write_byte(bh_buffer* buffer, u8 byte) {
+    bh_buffer_grow(buffer, buffer->length + 1);
+    buffer->data[buffer->length++] = byte;
+}
+
+void bh_buffer_write_u32(bh_buffer* buffer, u32 i) {
+    bh_buffer_grow(buffer, buffer->length + 4);
+    *((u32 *) bh_pointer_add(buffer->data, buffer->length)) = i;
+    buffer->length += 4;
+}
+
+void bh_buffer_write_u64(bh_buffer* buffer, u64 i) {
+    bh_buffer_grow(buffer, buffer->length + 8);
+    *((u64 *) bh_pointer_add(buffer->data, buffer->length)) = i;
+    buffer->length += 8;
+}
+
+void bh_buffer_align(bh_buffer* buffer, u32 alignment) {
+    if (buffer->length % alignment != 0) {
+        u32 difference = alignment - (buffer->length % alignment);
+        buffer->length += difference;
+
+        bh_buffer_grow(buffer, buffer->length);
+    }
+}
+
+
+#endif
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+//-------------------------------------------------------------------------------------
+// ARRAY IMPLEMENTATION
+//-------------------------------------------------------------------------------------
+#ifndef BH_NO_ARRAY
+
+b32 bh__arr_grow(bh_allocator alloc, void** arr, i32 elemsize, i32 cap) {
+    bh__arr* arrptr;
+
+    if (*arr == NULL) {
+        if (cap == 0 && elemsize == 0) return 1;
+
+        arrptr = (bh__arr *) bh_alloc(alloc, sizeof(*arrptr) + elemsize * cap);
+        if (arrptr == NULL) return 0;
+
+        arrptr->allocator = alloc;
+        arrptr->capacity = cap;
+        arrptr->length = 0;
+
+    } else {
+        arrptr = bh__arrhead(*arr);
+
+        if (arrptr->capacity < cap) {
+            void* p;
+            i32 newcap = arrptr->capacity;
+            while (newcap < cap) newcap = BH_ARR_GROW_FORMULA(newcap);
+
+            p = bh_resize(arrptr->allocator, arrptr, sizeof(*arrptr) + elemsize * newcap);
+
+            if (p) {
+                arrptr = (bh__arr *) p;
+                arrptr->capacity = newcap;
+            } else {
+                return 0;
+            }
+        }
+    }
+
+    *arr = arrptr + 1;
+    return 1;
+}
+
+b32 bh__arr_shrink(void** arr, i32 elemsize, i32 cap) {
+    if (*arr == NULL) return 0;
+
+    bh__arr* arrptr = bh__arrhead(*arr);
+    cap = bh_max(cap, arrptr->length);
+
+    if (arrptr->capacity > cap) {
+        void* p = bh_resize(arrptr->allocator, arrptr, sizeof(*arrptr) + elemsize * cap);
+
+        if (p) {
+            arrptr = (bh__arr *) p;
+            arrptr->capacity = cap;
+        } else {
+            return 0;
+        }
+    }
+
+    *arr = arrptr + 1;
+    return 1;
+}
+
+b32 bh__arr_free(void **arr) {
+    if (*arr == NULL) return 0;
+
+    bh__arr* arrptr = bh__arrhead(*arr);
+    bh_free(arrptr->allocator, arrptr);
+    *arr = NULL;
+    return 1;
+}
+
+void* bh__arr_copy(bh_allocator alloc, void *arr, i32 elemsize) {
+    bh__arr* arrptr = bh__arrhead(arr);
+
+    const i32 cap = arrptr->length;
+
+    void* newarr = NULL;
+    bh__arr_grow(alloc, &newarr, elemsize, cap);
+    bh__arrhead(newarr)->length = cap;
+    bh__arrhead(newarr)->capacity = cap;
+    memcpy(newarr, arr, elemsize * arrptr->length);
+
+    return newarr;
+}
+
+void bh__arr_deleten(void **arr, i32 elemsize, i32 index, i32 numelems) {
+    bh__arr* arrptr = bh__arrhead(*arr);
+
+    if (index >= arrptr->length) return; // Can't delete past the end of the array
+    if (numelems <= 0) return; // Can't delete nothing
+
+    memmove(
+        (char *)(*arr) + elemsize * index,                    // Target
+        (char *)(*arr) + elemsize * (index + numelems),        // Source
+        elemsize * (arrptr->length - (index + numelems)));    // Length
+    arrptr->length -= numelems;
+}
+
+void bh__arr_insertn(void **arr, i32 elemsize, i32 index, i32 numelems) {
+    if (numelems) {
+        if (*arr == NULL) {
+            bh__arr_grow(bh_arr_allocator(arr), arr, elemsize, numelems); // Making a new array
+            return;
+        }
+
+        bh__arr* arrptr = bh__arrhead(*arr);
+        if (!bh__arr_grow(bh_arr_allocator(arr), arr, elemsize, arrptr->length + numelems)) return; // Fail case
+        arrptr = bh__arrhead(*arr);
+        memmove(
+            (char *)(*arr) + elemsize * (index + numelems),
+            (char *)(*arr) + elemsize * index,
+            elemsize * (arrptr->length - index));
+        arrptr->length += numelems;
+    }
+}
+
+#endif // ifndef BH_NO_ARRAY
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+//-------------------------------------------------------------------------------------
+// TABLE IMPLEMENTATION
+//-------------------------------------------------------------------------------------
+#ifndef BH_NO_TABLE
+
+b32 bh__table_init(bh_allocator allocator, bh__table **table, i32 table_size) {
+    *table = bh_alloc(allocator, sizeof(bh__table) + sizeof(ptr) * table_size);
+    if (*table == NULL) return 0;
+
+    (*table)->allocator = allocator;
+    (*table)->table_size = table_size;
+
+    for (i32 i = 0; i < table_size; i++) {
+        (*table)->arrs[i] = NULL;
+    }
+
+    return 1;
+}
+
+b32 bh__table_free(bh__table **table) {
+    if (*table == NULL) return 0;
+
+    for (u64 i = 0; i < (*table)->table_size; i++) {
+        if ((*table)->arrs[i] != NULL) {
+            bh_arr_free((*table)->arrs[i]);
+        }
+    }
+
+    bh_free((*table)->allocator, *table);
+    *table = NULL;
+    return 1;
+}
+
+// Assumes NULL terminated string for key
+ptr bh__table_put(bh__table *table, i32 elemsize, char *key) {
+    elemsize += (elemsize & 1);
+
+    u64 index = bh__table_hash_function(key, 0, table->table_size);
+    u8 arr_was_new = 0;
+
+    ptr arrptr = table->arrs[index];
+    if (arrptr == NULL) {
+        arr_was_new = 1;
+        goto add_new_element;
+    }
+    u64 len = *(u64 *) arrptr;
+    arrptr = bh_pointer_add(arrptr, sizeof(u64));
+
+    u16 key_length = 0;
+    while (len--) {
+        arrptr = bh_pointer_add(arrptr, elemsize);
+        key_length = *(u16 *) arrptr;
+        arrptr = bh_pointer_add(arrptr, sizeof(u16));
+        if (strncmp(key, (char *) arrptr, key_length) == 0) goto found_matching;
+        arrptr = bh_pointer_add(arrptr, key_length);
+    }
+
+add_new_element:
+    arrptr = table->arrs[index];
+    i32 byte_len = bh_arr_length(arrptr);
+    if (byte_len == 0) byte_len = sizeof(u64);
+    key_length = strlen(key) + 1;
+
+    // NOTE: Align to 16 bytes
+    if ((key_length + 2) % 16 != 0) {
+        key_length = ((((key_length + 2) >> 4) + 1) << 4) - 2;
+    }
+
+    bh__arr_grow(table->allocator, &arrptr, 1, byte_len + elemsize + sizeof(u16) + key_length);
+    bh__arrhead(arrptr)->length = byte_len + elemsize + sizeof(u16) + key_length;
+    table->arrs[index] = arrptr;
+
+    if (arr_was_new) {
+        *(u64 *) arrptr = 1;
+    } else {
+        (*(u64 *) arrptr)++;
+    }
+
+    arrptr = bh_pointer_add(arrptr, byte_len + elemsize);
+    *(u16 *) arrptr = key_length;
+    arrptr = bh_pointer_add(arrptr, sizeof(u16));
+    strncpy(arrptr, key, key_length);
+
+found_matching:
+    return bh_pointer_add(arrptr, -(sizeof(u16) + elemsize));
+}
+
+b32 bh__table_has(bh__table *table, i32 elemsize, char *key) {
+    elemsize += (elemsize & 1);
+
+    u64 index = bh__table_hash_function(key, 0, table->table_size);
+
+    ptr arrptr = table->arrs[index];
+    if (arrptr == NULL) return 0;
+
+    u64 len = *(u64 *) arrptr;
+    arrptr = bh_pointer_add(arrptr, sizeof(u64));
+
+    u16 key_length = 0;
+    while (len--) {
+        arrptr = bh_pointer_add(arrptr, elemsize);
+        key_length = *(u16 *) arrptr;
+        arrptr = bh_pointer_add(arrptr, sizeof(u16));
+        if (strncmp(key, (char *) arrptr, key_length) == 0) return 1;
+        arrptr = bh_pointer_add(arrptr, key_length);
+    }
+
+    return 0;
+}
+
+ptr bh__table_get(bh__table *table, i32 elemsize, char *key) {
+    elemsize += (elemsize & 1);
+
+    u64 index = bh__table_hash_function(key, 0, table->table_size);
+
+    ptr arrptr = table->arrs[index];
+    if (arrptr == NULL) return 0;
+
+    u64 len = *(u64 *) arrptr;
+    arrptr = bh_pointer_add(arrptr, sizeof(u64));
+
+    u16 key_length = 0;
+    while (len--) {
+        arrptr = bh_pointer_add(arrptr, elemsize);
+        key_length = *(u16 *) arrptr;
+        arrptr = bh_pointer_add(arrptr, sizeof(u16));
+        if (strncmp(key, (char *) arrptr, key_length) == 0) {
+            return bh_pointer_add(arrptr, -(sizeof(u16) + elemsize));
+        }
+        arrptr = bh_pointer_add(arrptr, key_length);
+    }
+
+    return NULL;
+}
+
+void bh__table_delete(bh__table *table, i32 elemsize, char *key) {
+    elemsize += (elemsize & 1);
+
+    u64 index = bh__table_hash_function(key, 0, table->table_size);
+
+    ptr arrptr = table->arrs[index], walker;
+    if (arrptr == NULL) return; // Didn't exist
+    walker = arrptr;
+
+    i32 byte_offset = 8;
+    i32 delete_len = 0;
+
+    u64 len = *(u64 *) walker;
+    walker = bh_pointer_add(walker, sizeof(u64));
+
+    u16 key_length = 0;
+    while (len--) {
+        walker = bh_pointer_add(walker, elemsize);
+        key_length = *(u16 *) walker;
+        walker = bh_pointer_add(walker, sizeof(u16));
+        if (strncmp(key, (char *) walker, key_length) == 0) {
+            delete_len = elemsize + sizeof(u16) + key_length;
+            goto found_matching;
+        }
+        walker = bh_pointer_add(walker, key_length);
+        byte_offset += elemsize + sizeof(u16) + key_length;
+    }
+
+    // NOTE: Already didn't exist
+    return;
+
+found_matching:
+    bh__arr_deleten((void **) &arrptr, 1, byte_offset, delete_len);
+    table->arrs[index] = arrptr;
+    (*(u64 *) arrptr)--;
+}
+
+void bh__table_clear(bh__table *table) {
+    for (u64 i = 0; i < table->table_size; i++) {
+        if (table->arrs[i] != NULL) {
+            // NOTE: Set length property to 0
+            *((u64 *) table->arrs[i]) = 0;
+            bh_arr_set_length(table->arrs[i], 0);
+        }
+    }
+}
+
+bh_table_iterator bh__table_iter_setup(bh__table *table, i32 elemsize) {
+    elemsize += (elemsize & 1);
+
+    bh_table_iterator it = {
+        .tab = table->arrs,
+        .endtab = table->arrs + table->table_size,
+        .elemsize = elemsize,
+        .entry = NULL
+    };
+    return it;
+}
+
+b32 bh_table_iter_next(bh_table_iterator* it) {
+    if (it->tab == NULL) return 0;
+
+    if (it->entry != NULL) {
+        it->arrlen--;
+        if (it->arrlen <= 0) {
+            it->tab++;
+            goto step_to_next;
+        }
+
+        it->entry = bh_pointer_add(it->entry, it->elemsize);
+        it->entry = bh_pointer_add(it->entry, sizeof(u16) + (*(u16 *) it->entry));
+        return 1;
+    }
+
+step_to_next:
+    // Step forward to find next valid
+    while (it->tab != it->endtab && *it->tab == NULL) {
+        it->tab++;
+    }
+
+    if (it->tab == it->endtab) return 0;
+
+    it->entry = *it->tab;
+    it->arrlen = *(u64 *) it->entry;
+    it->entry = bh_pointer_add(it->entry, sizeof(u64));
+    if (it->arrlen <= 0) {
+        it->tab++;
+        goto step_to_next;
+    }
+    return 1;
+}
+
+#endif // ifndef BH_NO_HASHTABLE
+
+
+
+//-------------------------------------------------------------------------------------
+// IMAP IMPLEMENTATION
+//-------------------------------------------------------------------------------------
+#ifndef BH_NO_IMAP
+void bh_imap_init(bh_imap* imap, bh_allocator alloc, i32 hash_count) {
+    imap->allocator = alloc;
+
+    imap->hashes  = NULL;
+    imap->entries = NULL;
+
+    bh_arr_new(alloc, imap->hashes, hash_count);
+    bh_arr_new(alloc, imap->entries, 4);
+
+    fori(count, 0, hash_count) bh_arr_push(imap->hashes, -1);
+}
+
+void bh_imap_free(bh_imap* imap) {
+    bh_arr_free(imap->hashes);
+    bh_arr_free(imap->entries);
+
+    imap->hashes = NULL;
+    imap->entries = NULL;
+}
+
+bh__imap_lookup_result bh__imap_lookup(bh_imap* imap, bh_imap_entry_t key) {
+    bh__imap_lookup_result lr = { -1, -1, -1 };
+
+    u64 hash = 0xcbf29ce484222325ull ^ key;
+    u64 n = bh_arr_capacity(imap->hashes);
+
+    lr.hash_index  = hash % n;
+    lr.entry_index = imap->hashes[lr.hash_index];
+    while (lr.entry_index >= 0) {
+        if (imap->entries[lr.entry_index].key == key) {
+            return lr;
+        }
+
+        lr.entry_prev  = lr.entry_index;
+        lr.entry_index = imap->entries[lr.entry_index].next;
+    }
+
+    return lr;
+}
+
+void bh_imap_put(bh_imap* imap, bh_imap_entry_t key, bh_imap_entry_t value) {
+    bh__imap_lookup_result lr = bh__imap_lookup(imap, key);
+
+    if (lr.entry_index >= 0) {
+        imap->entries[lr.entry_index].value = value;
+        return;
+    }
+
+    bh__imap_entry entry;
+    entry.key = key;
+    entry.value = value;
+    entry.next = imap->hashes[lr.hash_index];
+    bh_arr_push(imap->entries, entry);
+
+    imap->hashes[lr.hash_index] = bh_arr_length(imap->entries) - 1;
+}
+
+b32 bh_imap_has(bh_imap* imap, bh_imap_entry_t key) {
+    bh__imap_lookup_result lr = bh__imap_lookup(imap, key);
+    return lr.entry_index >= 0;
+}
+
+bh_imap_entry_t bh_imap_get(bh_imap* imap, bh_imap_entry_t key) {
+    bh__imap_lookup_result lr = bh__imap_lookup(imap, key);
+    if (lr.entry_index >= 0) {
+        return imap->entries[lr.entry_index].value;
+    } else {
+        return 0;
+    }
+}
+
+void bh_imap_delete(bh_imap* imap, bh_imap_entry_t key) {
+    bh__imap_lookup_result lr = bh__imap_lookup(imap, key);
+    if (lr.entry_index < 0) return;
+
+    if (lr.entry_prev < 0) {
+        imap->hashes[lr.hash_index] = imap->entries[lr.entry_index].next;
+    } else {
+        imap->entries[lr.entry_prev].next = imap->entries[lr.entry_index].next;
+    }
+
+    // If it's that last thing in the array, just pop off the end
+    if (lr.entry_index == bh_arr_length(imap->entries) - 1) {
+        bh_arr_pop(imap->entries);
+        return;
+    }
+
+    bh_arr_fastdelete(imap->entries, lr.entry_index);
+    bh__imap_lookup_result last = bh__imap_lookup(imap, imap->entries[lr.entry_index].key);
+    if (last.entry_prev >= 0) {
+        imap->entries[last.entry_prev].next = lr.entry_index;
+    } else {
+        imap->hashes[last.hash_index] = lr.entry_index;
+    }
+}
+
+void bh_imap_clear(bh_imap* imap) {
+    // NOTE: Does not clear out an of the data that was in the map
+    bh_arr_each(i64, hash, imap->hashes) *hash = -1;
+    bh_arr_set_length(imap->entries, 0);
+}
+
+#endif // ifndef BH_NO_IMAP
+
+
+
+
+
+u64 bh_time_curr() {
+#if defined(_BH_WINDOWS)
+    LARGE_INTEGER result;
+    QueryPerformanceCounter(&result);
+    return (u64) result.QuadPart;
+
+#elif defined(_BH_LINUX)
+    struct timespec spec;
+    clock_gettime(CLOCK_REALTIME, &spec);
+
+    time_t sec = spec.tv_sec;
+    u64 ms  = spec.tv_nsec / 1000000;
+    if (ms > 999) {
+        sec++;
+        ms = 0;
+    }
+
+    return sec * 1000 + ms;
+#endif
+}
+
+u64 bh_time_duration(u64 old) {
+#if defined(_BH_WINDOWS)
+    u64 curr = bh_time_curr();
+    u64 duration = curr - old;
+    
+    LARGE_INTEGER freq;
+    QueryPerformanceFrequency(&freq);
+    duration *= 1000;
+    duration /= freq.QuadPart;
+    return duration;
+
+#elif defined(_BH_LINUX)
+    u64 curr = bh_time_curr();
+    return curr - old;
+#endif
+}
+
+#endif // ifdef BH_DEFINE
+
+#endif // ifndef BH_H
diff --git a/include/stb_ds.h b/include/stb_ds.h
new file mode 100644 (file)
index 0000000..e84c82d
--- /dev/null
@@ -0,0 +1,1895 @@
+/* stb_ds.h - v0.67 - 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++).
+
+   For a gentle introduction:
+      http://nothings.org/stb_ds
+
+   To use this library, do this in *one* C or C++ file:
+      #define STB_DS_IMPLEMENTATION
+      #include "stb_ds.h"
+
+TABLE OF CONTENTS
+
+  Table of Contents
+  Compile-time options
+  License
+  Documentation
+  Notes
+  Notes - Dynamic arrays
+  Notes - Hash maps
+  Credits
+
+COMPILE-TIME OPTIONS
+
+  #define STBDS_NO_SHORT_NAMES
+
+     This flag needs to be set globally.
+
+     By default stb_ds exposes shorter function names that are not qualified
+     with the "stbds_" prefix. If these names conflict with the names in your
+     code, define this flag.
+
+  #define STBDS_SIPHASH_2_4
+
+     This flag only needs to be set in the file containing #define STB_DS_IMPLEMENTATION.
+
+     By default stb_ds.h hashes using a weaker variant of SipHash and a custom hash for
+     4- and 8-byte keys. On 64-bit platforms, you can define the above flag to force
+     stb_ds.h to use specification-compliant SipHash-2-4 for all keys. Doing so makes
+     hash table insertion about 20% slower on 4- and 8-byte keys, 5% slower on
+     64-byte keys, and 10% slower on 256-byte keys on my test computer.
+
+  #define STBDS_REALLOC(context,ptr,size) better_realloc
+  #define STBDS_FREE(context,ptr)         better_free
+
+     These defines only need to be set in the file containing #define STB_DS_IMPLEMENTATION.
+
+     By default stb_ds uses stdlib realloc() and free() for memory management. You can
+     substitute your own functions instead by defining these symbols. You must either
+     define both, or neither. Note that at the moment, 'context' will always be NULL.
+     @TODO add an array/hash initialization function that takes a memory context pointer.
+
+  #define STBDS_UNIT_TESTS
+
+     Defines a function stbds_unit_tests() that checks the functioning of the data structures.
+
+  Note that on older versions of gcc (e.g. 5.x.x) you may need to build with '-std=c++0x'
+     (or equivalentally '-std=c++11') when using anonymous structures as seen on the web
+     page or in STBDS_UNIT_TESTS.
+
+LICENSE
+
+  Placed in the public domain and also MIT licensed.
+  See end of file for detailed license information.
+
+DOCUMENTATION
+
+  Dynamic Arrays
+
+    Non-function interface:
+
+      Declare an empty dynamic array of type T
+        T* foo = NULL;
+
+      Access the i'th item of a dynamic array 'foo' of type T, T* foo:
+        foo[i]
+
+    Functions (actually macros)
+
+      arrfree:
+        void arrfree(T*);
+          Frees the array.
+
+      arrlen:
+        ptrdiff_t arrlen(T*);
+          Returns the number of elements in the array.
+
+      arrlenu:
+        size_t arrlenu(T*);
+          Returns the number of elements in the array as an unsigned type.
+
+      arrpop:
+        T arrpop(T* a)
+          Removes the final element of the array and returns it.
+
+      arrput:
+        T arrput(T* a, T b);
+          Appends the item b to the end of array a. Returns b.
+
+      arrins:
+        T arrins(T* a, int p, T b);
+          Inserts the item b into the middle of array a, into a[p],
+          moving the rest of the array over. Returns b.
+
+      arrinsn:
+        void arrinsn(T* a, int p, int n);
+          Inserts n uninitialized items into array a starting at a[p],
+          moving the rest of the array over.
+
+      arraddnptr:
+        T* arraddnptr(T* a, int n)
+          Appends n uninitialized items onto array at the end.
+          Returns a pointer to the first uninitialized item added.
+
+      arraddnindex:
+        size_t arraddnindex(T* a, int n)
+          Appends n uninitialized items onto array at the end.
+          Returns the index of 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.
+
+      arrdeln:
+        void arrdeln(T* a, int p, int n);
+          Deletes n elements starting at a[p], moving the rest of the array over.
+
+      arrdelswap:
+        void arrdelswap(T* a, int p);
+          Deletes the element at a[p], replacing it with the element from
+          the end of the array. O(1) performance.
+
+      arrsetlen:
+        void arrsetlen(T* a, int n);
+          Changes the length of the array to n. Allocates uninitialized
+          slots at the end if necessary.
+
+      arrsetcap:
+        size_t arrsetcap(T* a, int n);
+          Sets the length of allocated storage to at least n. It will not
+          change the length of the array.
+
+      arrcap:
+        size_t arrcap(T* a);
+          Returns the number of total elements the array can contain without
+          needing to be reallocated.
+
+  Hash maps & String hash maps
+
+    Given T is a structure type: struct { TK key; TV value; }. Note that some
+    functions do not require TV value and can have other fields. For string
+    hash maps, TK must be 'char *'.
+
+    Special interface:
+
+      stbds_rand_seed:
+        void stbds_rand_seed(size_t seed);
+          For security against adversarially chosen data, you should seed the
+          library with a strong random number. Or at least seed it with time().
+
+      stbds_hash_string:
+        size_t stbds_hash_string(char *str, size_t seed);
+          Returns a hash value for a string.
+
+      stbds_hash_bytes:
+        size_t stbds_hash_bytes(void *p, size_t len, size_t seed);
+          These functions hash an arbitrary number of bytes. The function
+          uses a custom hash for 4- and 8-byte data, and a weakened version
+          of SipHash for everything else. On 64-bit platforms you can get
+          specification-compliant SipHash-2-4 on all data by defining
+          STBDS_SIPHASH_2_4, at a significant cost in speed.
+
+    Non-function interface:
+
+      Declare an empty hash map of type T
+        T* foo = NULL;
+
+      Access the i'th entry in a hash table T* foo:
+        foo[i]
+
+    Function interface (actually macros):
+
+      hmfree
+      shfree
+        void hmfree(T*);
+        void shfree(T*);
+          Frees the hashmap and sets the pointer to NULL.
+
+      hmlen
+      shlen
+        ptrdiff_t hmlen(T*)
+        ptrdiff_t shlen(T*)
+          Returns the number of elements in the hashmap.
+
+      hmlenu
+      shlenu
+        size_t hmlenu(T*)
+        size_t shlenu(T*)
+          Returns the number of elements in the hashmap.
+
+      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
+
+      hmgets
+      shgets
+        T hmgets(T*, TK key)
+        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)
+        TV shdefault(T*, TV value)
+          Sets the default value for the hashmap, the value which will be
+          returned by hmget/shget if the key is not present.
+
+      hmdefaults
+      shdefaults
+        TV hmdefaults(T*, T item)
+        TV shdefaults(T*, T item)
+          Sets the default struct for the hashmap, the contents which will be
+          returned by hmgets/shgets if the key is not present.
+
+      hmput
+      shput
+        TV hmput(T*, TK key, TV value)
+        TV shput(T*, char* key, TV value)
+          Inserts a <key,value> pair into the hashmap. If the key is already
+          present in the hashmap, updates its value.
+
+      hmputs
+      shputs
+        T hmputs(T*, T item)
+        T shputs(T*, T item)
+          Inserts a struct with T.key into the hashmap. If the struct is already
+          present in the hashmap, updates it.
+
+      hmdel
+      shdel
+        int hmdel(T*, TK key)
+        int shdel(T*, char* key)
+          If 'key' is in the hashmap, deletes its entry and returns 1.
+          Otherwise returns 0.
+
+    Function interface (actually macros) for strings only:
+
+      sh_new_strdup
+        void sh_new_strdup(T*);
+          Overwrites the existing pointer with a newly allocated
+          string hashmap which will automatically allocate and free
+          each string key using realloc/free
+
+      sh_new_arena
+        void sh_new_arena(T*);
+          Overwrites the existing pointer with a newly allocated
+          string hashmap which will automatically allocate each string
+          key to a string arena. Every string key ever used by this
+          hash table remains in the arena until the arena is freed.
+          Additionally, any key which is deleted and reinserted will
+          be allocated multiple times in the string arena.
+
+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.
+
+  * 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:
+
+      for (i=0; i < arrlen(foo); ++i)
+         ... foo[i] ...
+
+  * All operations except arrins/arrdel are O(1) amortized, but individual
+    operations can be slow, so these data structures may not be suitable
+    for real time use. Dynamic arrays double in capacity as needed, so
+    elements are copied an average of once. Hash tables double/halve
+    their size as needed, with appropriate hysteresis to maintain O(1)
+    performance.
+
+NOTES - DYNAMIC ARRAY
+
+  * If you know how long a dynamic array is going to be in advance, you can avoid
+    extra memory allocations by using arrsetlen to allocate it to that length in
+    advance and use foo[n] while filling it out, or arrsetcap to allocate the memory
+    for that length and use arrput/arrpush as normal.
+
+  * Unlike some other versions of the dynamic array, this version should
+    be safe to use with strict-aliasing optimizations.
+
+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. 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'.
+
+  * 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
+    keys are always added at the end of the array. This will be consistent
+    across all platforms and versions of the library. However, you should not
+    attempt to serialize the internal hash table, as the hash is not consistent
+    between different platforms, and may change with future versions of the library.
+
+  * Use sh_new_arena() for string hashmaps that you never delete from. Initialize
+    with NULL if you're managing the memory for your strings, or your strings are
+    never freed (at least until the hashmap is freed). Otherwise, use sh_new_strdup().
+    @TODO: make an arena variant that garbage collects the strings with a trivial
+    copy collector into a new arena whenever the table shrinks / rebuilds. Since
+    current arena recommendation is to only use arena if it never deletes, then
+    this can just replace current arena implementation.
+
+  * If adversarial input is a serious concern and you're on a 64-bit platform,
+    enable STBDS_SIPHASH_2_4 (see the 'Compile-time options' section), and pass
+    a strong random number to stbds_rand_seed.
+
+  * The default value for the hash table is stored in foo[-1], so if you
+    use code like 'hmget(T,k)->value = 5' you can accidentally overwrite
+    the value stored by hmdefault if 'k' is not present.
+
+CREDITS
+
+  Sean Barrett -- library, idea for dynamic array API/implementation
+  Per Vognsen  -- idea for hash table API/implementation
+  Rafael Sachetto -- arrpop()
+  github:HeroicKatora -- arraddn() reworking
+
+  Bugfixes:
+    Andy Durdin
+    Shane Liesegang
+    Vinh Truong
+    Andreas Molzer
+    github:hashitaku
+    github:srdjanstipic
+    Macoy Madson
+    Andreas Vennstrom
+    Tobias Mansfield-Williams
+*/
+
+#ifdef STBDS_UNIT_TESTS
+#define _CRT_SECURE_NO_WARNINGS
+#endif
+
+#ifndef INCLUDE_STB_DS_H
+#define INCLUDE_STB_DS_H
+
+#include <stddef.h>
+#include <string.h>
+
+#ifndef STBDS_NO_SHORT_NAMES
+#define arrlen      stbds_arrlen
+#define arrlenu     stbds_arrlenu
+#define arrput      stbds_arrput
+#define arrpush     stbds_arrput
+#define arrpop      stbds_arrpop
+#define arrfree     stbds_arrfree
+#define arraddn     stbds_arraddn // deprecated, use one of the following instead:
+#define arraddnptr  stbds_arraddnptr
+#define arraddnindex stbds_arraddnindex
+#define arrsetlen   stbds_arrsetlen
+#define arrlast     stbds_arrlast
+#define arrins      stbds_arrins
+#define arrinsn     stbds_arrinsn
+#define arrdel      stbds_arrdel
+#define arrdeln     stbds_arrdeln
+#define arrdelswap  stbds_arrdelswap
+#define arrcap      stbds_arrcap
+#define arrsetcap   stbds_arrsetcap
+
+#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
+#define hmfree      stbds_hmfree
+#define hmdefault   stbds_hmdefault
+#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 shgetp_null stbds_shgetp_null
+#define shdel       stbds_shdel
+#define shlen       stbds_shlen
+#define shlenu      stbds_shlenu
+#define shfree      stbds_shfree
+#define shdefault   stbds_shdefault
+#define shdefaults  stbds_shdefaults
+#define sh_new_arena  stbds_sh_new_arena
+#define sh_new_strdup stbds_sh_new_strdup
+
+#define stralloc    stbds_stralloc
+#define strreset    stbds_strreset
+#endif
+
+#if defined(STBDS_REALLOC) && !defined(STBDS_FREE) || !defined(STBDS_REALLOC) && defined(STBDS_FREE)
+#error "You must define both STBDS_REALLOC and STBDS_FREE, or neither."
+#endif
+#if !defined(STBDS_REALLOC) && !defined(STBDS_FREE)
+#include <stdlib.h>
+#define STBDS_REALLOC(c,p,s) realloc(p,s)
+#define STBDS_FREE(c,p)      free(p)
+#endif
+
+#ifdef _MSC_VER
+#define STBDS_NOTUSED(v)  (void)(v)
+#else
+#define STBDS_NOTUSED(v)  (void)sizeof(v)
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// for security against attackers, seed the library with a random number, at least time() but stronger is better
+extern void stbds_rand_seed(size_t seed);
+
+// these are the hash functions used internally if you want to test them or use them for other purposes
+extern size_t stbds_hash_bytes(void *p, size_t len, size_t seed);
+extern size_t stbds_hash_string(char *str, size_t seed);
+
+// this is a simple string arena allocator, initialize with e.g. 'stbds_string_arena my_arena={0}'.
+typedef struct stbds_string_arena stbds_string_arena;
+extern char * stbds_stralloc(stbds_string_arena *a, char *str);
+extern void   stbds_strreset(stbds_string_arena *a);
+
+// have to #define STBDS_UNIT_TESTS to call this
+extern void stbds_unit_tests(void);
+
+///////////////
+//
+// Everything below here is implementation details
+//
+
+extern void * stbds_arrgrowf(void *a, size_t elemsize, size_t addlen, size_t min_cap);
+extern void   stbds_arrfreef(void *a);
+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);
+extern void * stbds_shmode_func(size_t elemsize, int mode);
+
+#ifdef __cplusplus
+}
+#endif
+
+#if defined(__GNUC__) || defined(__clang__)
+#define STBDS_HAS_TYPEOF
+#ifdef __cplusplus
+//#define STBDS_HAS_LITERAL_ARRAY  // this is currently broken for clang
+#endif
+#endif
+
+#if !defined(__cplusplus)
+#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
+#define STBDS_HAS_LITERAL_ARRAY
+#endif
+#endif
+
+// this macro takes the address of the argument, but on gcc/clang can accept rvalues
+#if defined(STBDS_HAS_LITERAL_ARRAY) && defined(STBDS_HAS_TYPEOF)
+  #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)[1]){value}) // literal array decays to pointer to value
+  #endif
+#else
+#define STBDS_ADDRESSOF(typevar, value)     &(value)
+#endif
+
+#define STBDS_OFFSETOF(var,field)           ((char *) &(var)->field - (char *) (var))
+
+#define stbds_header(t)  ((stbds_array_header *) (t) - 1)
+#define stbds_temp(t)    stbds_header(t)->temp
+#define stbds_temp_key(t) (*(char **) stbds_header(t)->hash_table)
+
+#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)     ((void)(stbds_arraddnindex(a, n)))    // deprecated, use one of the following instead:
+#define stbds_arraddnptr(a,n)  (stbds_arrmaybegrow(a,n), (n) ? (stbds_header(a)->length += (n), &(a)[stbds_header(a)->length-(n)]) : (a))
+#define stbds_arraddnindex(a,n)(stbds_arrmaybegrow(a,n), (n) ? (stbds_header(a)->length += (n), stbds_header(a)->length-(n)) : stbds_arrlen(a))
+#define stbds_arraddnoff       stbds_arraddnindex
+#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)
+#define stbds_arrdeln(a,i,n)   (memmove(&(a)[i], &(a)[(i)+(n)], sizeof *(a) * (stbds_header(a)->length-(n)-(i))), stbds_header(a)->length -= (n))
+#define stbds_arrdelswap(a,i)  ((a)[i] = stbds_arrlast(a), stbds_header(a)->length -= 1)
+#define stbds_arrinsn(a,i,n)   (stbds_arraddn((a),(n)), memmove(&(a)[(i)+(n)], &(a)[i], sizeof *(a) * (stbds_header(a)->length-(n)-(i))))
+#define stbds_arrins(a,i,v)    (stbds_arrinsn((a),(i),1), (a)[i]=(v))
+
+#define stbds_arrmaybegrow(a,n)  ((!(a) || stbds_header(a)->length + (n) > stbds_header(a)->capacity) \
+                                  ? (stbds_arrgrow(a,n,0),0) : 0)
+
+#define stbds_arrgrow(a,b,c)   ((a) = stbds_arrgrowf_wrapper((a), sizeof *(a), (b), (c)))
+
+#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)].value = (v))
+
+#define stbds_hmputs(t, s) \
+    ((t) = stbds_hmput_key_wrapper((t), sizeof *(t), &(s).key, sizeof (s).key, STBDS_HM_BINARY), \
+     (t)[stbds_temp((t)-1)] = (s))
+
+#define stbds_hmgeti(t,k) \
+    ((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)
+
+#define stbds_hmdefault(t, v) \
+    ((t) = stbds_hmput_default_wrapper((t), sizeof *(t)), (t)[-1].value = (v))
+
+#define stbds_hmdefaults(t, s) \
+    ((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)),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_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), \
+     (t)[stbds_temp((t)-1)].key = stbds_temp_key((t)-1)) // above line overwrites whole structure, so must rewrite key here if it was allocated internally
+
+#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_shdefaults(t, s) stbds_hmdefaults(t,s)
+
+#define stbds_shfree       stbds_hmfree
+#define stbds_shlenu       stbds_hmlenu
+
+#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
+{
+  size_t      length;
+  size_t      capacity;
+  void      * hash_table;
+  ptrdiff_t   temp;
+} stbds_array_header;
+
+typedef struct stbds_string_block
+{
+  struct stbds_string_block *next;
+  char storage[8];
+} stbds_string_block;
+
+struct stbds_string_arena
+{
+  stbds_string_block *storage;
+  size_t remaining;
+  unsigned char block;
+  unsigned char mode;  // this isn't used by the string arena itself
+};
+
+#define STBDS_HM_BINARY         0
+#define STBDS_HM_STRING         1
+
+enum
+{
+   STBDS_SH_NONE,
+   STBDS_SH_DEFAULT,
+   STBDS_SH_STRDUP,
+   STBDS_SH_ARENA
+};
+
+#ifdef __cplusplus
+// in C we use implicit assignment from these void*-returning functions to T*.
+// in C++ these templates make the same code work
+template<class T> static T * stbds_arrgrowf_wrapper(T *a, size_t elemsize, size_t addlen, size_t min_cap) {
+  return (T*)stbds_arrgrowf((void *)a, elemsize, addlen, min_cap);
+}
+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_ts((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);
+}
+template<class T> static T * stbds_hmput_key_wrapper(T *a, size_t elemsize, void *key, size_t keysize, int mode) {
+  return (T*)stbds_hmput_key((void*)a, elemsize, key, keysize, mode);
+}
+template<class T> static T * stbds_hmdel_key_wrapper(T *a, size_t elemsize, void *key, size_t keysize, size_t keyoffset, int mode){
+  return (T*)stbds_hmdel_key((void*)a, elemsize, key, keysize, keyoffset, mode);
+}
+template<class T> static T * stbds_shmode_func_wrapper(T *, size_t elemsize, int mode) {
+  return (T*)stbds_shmode_func(elemsize, mode);
+}
+#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
+#define stbds_shmode_func_wrapper(t,e,m)  stbds_shmode_func(e,m)
+#endif
+
+#endif // INCLUDE_STB_DS_H
+
+
+//////////////////////////////////////////////////////////////////////////////
+//
+//   IMPLEMENTATION
+//
+
+#ifdef STB_DS_IMPLEMENTATION
+#include <assert.h>
+#include <string.h>
+
+#ifndef STBDS_ASSERT
+#define STBDS_ASSERT_WAS_UNDEFINED
+#define STBDS_ASSERT(x)   ((void) 0)
+#endif
+
+#ifdef STBDS_STATISTICS
+#define STBDS_STATS(x)   x
+size_t stbds_array_grow;
+size_t stbds_hash_grow;
+size_t stbds_hash_shrink;
+size_t stbds_hash_rebuild;
+size_t stbds_hash_probes;
+size_t stbds_hash_alloc;
+size_t stbds_rehash_probes;
+size_t stbds_rehash_items;
+#else
+#define STBDS_STATS(x)
+#endif
+
+//
+// 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)
+{
+  stbds_array_header temp={0}; // force debugging
+  void *b;
+  size_t min_len = stbds_arrlen(a) + addlen;
+  (void) sizeof(temp);
+
+  // compute the minimum capacity needed
+  if (min_len > min_cap)
+    min_cap = min_len;
+
+  if (min_cap <= stbds_arrcap(a))
+    return a;
+
+  // increase needed capacity to guarantee O(1) amortized
+  if (min_cap < 2 * stbds_arrcap(a))
+    min_cap = 2 * stbds_arrcap(a);
+  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;
+    stbds_header(b)->hash_table = 0;
+    stbds_header(b)->temp = 0;
+  } else {
+    STBDS_STATS(++stbds_array_grow);
+  }
+  stbds_header(b)->capacity = min_cap;
+
+  return b;
+}
+
+void stbds_arrfreef(void *a)
+{
+  STBDS_FREE(NULL, stbds_header(a));
+}
+
+//
+// stbds_hm hash table implementation
+//
+
+#ifdef STBDS_INTERNAL_SMALL_BUCKET
+#define STBDS_BUCKET_LENGTH      4
+#else
+#define STBDS_BUCKET_LENGTH      8
+#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))
+
+typedef struct
+{
+   size_t    hash [STBDS_BUCKET_LENGTH];
+   ptrdiff_t index[STBDS_BUCKET_LENGTH];
+} stbds_hash_bucket; // in 32-bit, this is one 64-byte cache line; in 64-bit, each array is one 64-byte cache line
+
+typedef struct
+{
+  char * temp_key; // this MUST be the first field of the hash table
+  size_t slot_count;
+  size_t used_count;
+  size_t used_count_threshold;
+  size_t used_count_shrink_threshold;
+  size_t tombstone_count;
+  size_t tombstone_count_threshold;
+  size_t seed;
+  size_t slot_count_log2;
+  stbds_string_arena string;
+  stbds_hash_bucket *storage; // not a separate allocation, just 64-byte aligned storage after this struct
+} stbds_hash_index;
+
+#define STBDS_INDEX_EMPTY    -1
+#define STBDS_INDEX_DELETED  -2
+#define STBDS_INDEX_IN_USE(x)  ((x) >= 0)
+
+#define STBDS_HASH_EMPTY      0
+#define STBDS_HASH_DELETED    1
+
+static size_t stbds_hash_seed=0x31415926;
+
+void stbds_rand_seed(size_t seed)
+{
+  stbds_hash_seed = seed;
+}
+
+#define stbds_load_32_or_64(var, temp, v32, v64_hi, v64_lo)                                          \
+  temp = v64_lo ^ v32, temp <<= 16, temp <<= 16, temp >>= 16, temp >>= 16, /* discard if 32-bit */   \
+  var = v64_hi, var <<= 16, var <<= 16,                                    /* discard if 32-bit */   \
+  var ^= temp ^ v32
+
+#define STBDS_SIZE_T_BITS           ((sizeof (size_t)) * 8)
+
+static size_t stbds_probe_position(size_t hash, size_t slot_count, size_t slot_log2)
+{
+  size_t pos;
+  STBDS_NOTUSED(slot_log2);
+  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)
+{
+  size_t n=0;
+  while (slot_count > 1) {
+    slot_count >>= 1;
+    ++n;
+  }
+  return n;
+}
+
+static stbds_hash_index *stbds_make_hash_index(size_t slot_count, stbds_hash_index *ot)
+{
+  stbds_hash_index *t;
+  t = (stbds_hash_index *) STBDS_REALLOC(NULL,0,(slot_count >> STBDS_BUCKET_SHIFT) * sizeof(stbds_hash_bucket) + sizeof(stbds_hash_index) + STBDS_CACHE_LINE_SIZE-1);
+  t->storage = (stbds_hash_bucket *) STBDS_ALIGN_FWD((size_t) (t+1), STBDS_CACHE_LINE_SIZE);
+  t->slot_count = slot_count;
+  t->slot_count_log2 = stbds_log2(slot_count);
+  t->tombstone_count = 0;
+  t->used_count = 0;
+
+  #if 0 // A1
+  t->used_count_threshold        = slot_count*12/16; // if 12/16th of table is occupied, grow
+  t->tombstone_count_threshold   = slot_count* 2/16; // if tombstones are 2/16th of table, rebuild
+  t->used_count_shrink_threshold = slot_count* 4/16; // if table is only 4/16th full, shrink
+  #elif 1 // A2
+  //t->used_count_threshold        = slot_count*12/16; // if 12/16th of table is occupied, grow
+  //t->tombstone_count_threshold   = slot_count* 3/16; // if tombstones are 3/16th of table, rebuild
+  //t->used_count_shrink_threshold = slot_count* 4/16; // if table is only 4/16th full, shrink
+
+  // compute without overflowing
+  t->used_count_threshold        = slot_count - (slot_count>>2);
+  t->tombstone_count_threshold   = (slot_count>>3) + (slot_count>>4);
+  t->used_count_shrink_threshold = slot_count >> 2;
+
+  #elif 0 // B1
+  t->used_count_threshold        = slot_count*13/16; // if 13/16th of table is occupied, grow
+  t->tombstone_count_threshold   = slot_count* 2/16; // if tombstones are 2/16th of table, rebuild
+  t->used_count_shrink_threshold = slot_count* 5/16; // if table is only 5/16th full, shrink
+  #else // C1
+  t->used_count_threshold        = slot_count*14/16; // if 14/16th of table is occupied, grow
+  t->tombstone_count_threshold   = slot_count* 2/16; // if tombstones are 2/16th of table, rebuild
+  t->used_count_shrink_threshold = slot_count* 6/16; // if table is only 6/16th full, shrink
+  #endif
+  // Following statistics were measured on a Core i7-6700 @ 4.00Ghz, compiled with clang 7.0.1 -O2
+    // Note that the larger tables have high variance as they were run fewer times
+  //     A1            A2          B1           C1
+  //    0.10ms :     0.10ms :     0.10ms :     0.11ms :      2,000 inserts creating 2K table
+  //    0.96ms :     0.95ms :     0.97ms :     1.04ms :     20,000 inserts creating 20K table
+  //   14.48ms :    14.46ms :    10.63ms :    11.00ms :    200,000 inserts creating 200K table
+  //  195.74ms :   196.35ms :   203.69ms :   214.92ms :  2,000,000 inserts creating 2M table
+  // 2193.88ms :  2209.22ms :  2285.54ms :  2437.17ms : 20,000,000 inserts creating 20M table
+  //   65.27ms :    53.77ms :    65.33ms :    65.47ms : 500,000 inserts & deletes in 2K table
+  //   72.78ms :    62.45ms :    71.95ms :    72.85ms : 500,000 inserts & deletes in 20K table
+  //   89.47ms :    77.72ms :    96.49ms :    96.75ms : 500,000 inserts & deletes in 200K table
+  //   97.58ms :    98.14ms :    97.18ms :    97.53ms : 500,000 inserts & deletes in 2M table
+  //  118.61ms :   119.62ms :   120.16ms :   118.86ms : 500,000 inserts & deletes in 20M table
+  //  192.11ms :   194.39ms :   196.38ms :   195.73ms : 500,000 inserts & deletes in 200M table
+
+  if (slot_count <= STBDS_BUCKET_LENGTH)
+    t->used_count_shrink_threshold = 0;
+  // to avoid infinite loop, we need to guarantee that at least one slot is empty and will terminate probes
+  STBDS_ASSERT(t->used_count_threshold + t->tombstone_count_threshold < t->slot_count);
+  STBDS_STATS(++stbds_hash_alloc);
+  if (ot) {
+    t->string = ot->string;
+    // reuse old seed so we can reuse old hashes so below "copy out old data" doesn't do any hashing
+    t->seed = ot->seed;
+  } else {
+    size_t a,b,temp;
+    memset(&t->string, 0, sizeof(t->string));
+    t->seed = stbds_hash_seed;
+    // LCG
+    // in 32-bit, a =          2147001325   b =  715136305
+    // in 64-bit, a = 2862933555777941757   b = 3037000493
+    stbds_load_32_or_64(a,temp, 2147001325, 0x27bb2ee6, 0x87b0b0fd);
+    stbds_load_32_or_64(b,temp,  715136305,          0, 0xb504f32d);
+    stbds_hash_seed = stbds_hash_seed  * a + b;
+  }
+
+  {
+    size_t i,j;
+    for (i=0; i < slot_count >> STBDS_BUCKET_SHIFT; ++i) {
+      stbds_hash_bucket *b = &t->storage[i];
+      for (j=0; j < STBDS_BUCKET_LENGTH; ++j)
+        b->hash[j] = STBDS_HASH_EMPTY;
+      for (j=0; j < STBDS_BUCKET_LENGTH; ++j)
+        b->index[j] = STBDS_INDEX_EMPTY;
+    }
+  }
+
+  // copy out the old data, if any
+  if (ot) {
+    size_t i,j;
+    t->used_count = ot->used_count;
+    for (i=0; i < ot->slot_count >> STBDS_BUCKET_SHIFT; ++i) {
+      stbds_hash_bucket *ob = &ot->storage[i];
+      for (j=0; j < STBDS_BUCKET_LENGTH; ++j) {
+        if (STBDS_INDEX_IN_USE(ob->index[j])) {
+          size_t hash = ob->hash[j];
+          size_t pos = stbds_probe_position(hash, t->slot_count, t->slot_count_log2);
+          size_t step = STBDS_BUCKET_LENGTH;
+          STBDS_STATS(++stbds_rehash_items);
+          for (;;) {
+            size_t limit,z;
+            stbds_hash_bucket *bucket;
+            bucket = &t->storage[pos >> STBDS_BUCKET_SHIFT];
+            STBDS_STATS(++stbds_rehash_probes);
+
+            for (z=pos & STBDS_BUCKET_MASK; z < STBDS_BUCKET_LENGTH; ++z) {
+              if (bucket->hash[z] == 0) {
+                bucket->hash[z] = hash;
+                bucket->index[z] = ob->index[j];
+                goto done;
+              }
+            }
+
+            limit = pos & STBDS_BUCKET_MASK;
+            for (z = 0; z < limit; ++z) {
+              if (bucket->hash[z] == 0) {
+                bucket->hash[z] = hash;
+                bucket->index[z] = ob->index[j];
+                goto done;
+              }
+            }
+
+            pos += step;                  // quadratic probing
+            step += STBDS_BUCKET_LENGTH;
+            pos &= (t->slot_count-1);
+          }
+        }
+       done:
+        ;
+      }
+    }
+  }
+
+  return t;
+}
+
+#define STBDS_ROTATE_LEFT(val, n)   (((val) << (n)) | ((val) >> (STBDS_SIZE_T_BITS - (n))))
+#define STBDS_ROTATE_RIGHT(val, n)  (((val) >> (n)) | ((val) << (STBDS_SIZE_T_BITS - (n))))
+
+size_t stbds_hash_string(char *str, size_t seed)
+{
+  size_t hash = seed;
+  while (*str)
+     hash = STBDS_ROTATE_LEFT(hash, 9) + (unsigned char) *str++;
+
+  // Thomas Wang 64-to-32 bit mix function, hopefully also works in 32 bits
+  hash ^= seed;
+  hash = (~hash) + (hash << 18);
+  hash ^= hash ^ STBDS_ROTATE_RIGHT(hash,31);
+  hash = hash * 21;
+  hash ^= hash ^ STBDS_ROTATE_RIGHT(hash,11);
+  hash += (hash << 6);
+  hash ^= STBDS_ROTATE_RIGHT(hash,22);
+  return hash+seed;
+}
+
+#ifdef STBDS_SIPHASH_2_4
+#define STBDS_SIPHASH_C_ROUNDS 2
+#define STBDS_SIPHASH_D_ROUNDS 4
+typedef int STBDS_SIPHASH_2_4_can_only_be_used_in_64_bit_builds[sizeof(size_t) == 8 ? 1 : -1];
+#endif
+
+#ifndef STBDS_SIPHASH_C_ROUNDS
+#define STBDS_SIPHASH_C_ROUNDS 1
+#endif
+#ifndef STBDS_SIPHASH_D_ROUNDS
+#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;
+  size_t i,j;
+  size_t v0,v1,v2,v3, data;
+
+  // hash that works on 32- or 64-bit registers without knowing which we have
+  // (computes different results on 32-bit and 64-bit platform)
+  // derived from siphash, but on 32-bit platforms very different as it uses 4 32-bit state not 4 64-bit
+  v0 = ((((size_t) 0x736f6d65 << 16) << 16) + 0x70736575) ^  seed;
+  v1 = ((((size_t) 0x646f7261 << 16) << 16) + 0x6e646f6d) ^ ~seed;
+  v2 = ((((size_t) 0x6c796765 << 16) << 16) + 0x6e657261) ^  seed;
+  v3 = ((((size_t) 0x74656462 << 16) << 16) + 0x79746573) ^ ~seed;
+
+  #ifdef STBDS_TEST_SIPHASH_2_4
+  // hardcoded with key material in the siphash test vectors
+  v0 ^= 0x0706050403020100ull ^  seed;
+  v1 ^= 0x0f0e0d0c0b0a0908ull ^ ~seed;
+  v2 ^= 0x0706050403020100ull ^  seed;
+  v3 ^= 0x0f0e0d0c0b0a0908ull ^ ~seed;
+  #endif
+
+  #define STBDS_SIPROUND() \
+    do {                   \
+      v0 += v1; v1 = STBDS_ROTATE_LEFT(v1, 13);  v1 ^= v0; v0 = STBDS_ROTATE_LEFT(v0,STBDS_SIZE_T_BITS/2); \
+      v2 += v3; v3 = STBDS_ROTATE_LEFT(v3, 16);  v3 ^= v2;                                                 \
+      v2 += v1; v1 = STBDS_ROTATE_LEFT(v1, 17);  v1 ^= v2; v2 = STBDS_ROTATE_LEFT(v2,STBDS_SIZE_T_BITS/2); \
+      v0 += v3; v3 = STBDS_ROTATE_LEFT(v3, 21);  v3 ^= v0;                                                 \
+    } while (0)
+
+  for (i=0; i+sizeof(size_t) <= len; i += sizeof(size_t), d += sizeof(size_t)) {
+    data = d[0] | (d[1] << 8) | (d[2] << 16) | (d[3] << 24);
+    data |= (size_t) (d[4] | (d[5] << 8) | (d[6] << 16) | (d[7] << 24)) << 16 << 16; // discarded if size_t == 4
+
+    v3 ^= data;
+    for (j=0; j < STBDS_SIPHASH_C_ROUNDS; ++j)
+      STBDS_SIPROUND();
+    v0 ^= data;
+  }
+  data = len << (STBDS_SIZE_T_BITS-8);
+  switch (len - i) {
+    case 7: data |= ((size_t) d[6] << 24) << 24; // fall through
+    case 6: data |= ((size_t) d[5] << 20) << 20; // fall through
+    case 5: data |= ((size_t) d[4] << 16) << 16; // fall through
+    case 4: data |= (d[3] << 24); // fall through
+    case 3: data |= (d[2] << 16); // fall through
+    case 2: data |= (d[1] << 8); // fall through
+    case 1: data |= d[0]; // fall through
+    case 0: break;
+  }
+  v3 ^= data;
+  for (j=0; j < STBDS_SIPHASH_C_ROUNDS; ++j)
+    STBDS_SIPROUND();
+  v0 ^= data;
+  v2 ^= 0xff;
+  for (j=0; j < STBDS_SIPHASH_D_ROUNDS; ++j)
+    STBDS_SIPROUND();
+
+#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? I tweeted at the authors of SipHash about this but they didn't reply
+#endif
+}
+
+size_t stbds_hash_bytes(void *p, size_t len, size_t seed)
+{
+#ifdef STBDS_SIPHASH_2_4
+  return stbds_siphash_bytes(p,len,seed);
+#else
+  unsigned char *d = (unsigned char *) p;
+
+  if (len == 4) {
+    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;
+    hash -= (hash<<6);
+    hash ^= (hash>>17);
+    hash -= (hash<<9);
+    hash ^= seed;
+    hash ^= (hash<<4);
+    hash -= (hash<<3);
+    hash ^= (hash<<10);
+    hash ^= (hash>>15);
+    #elif 1
+    // 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;
+    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 ^= hash >> 16;
+    hash *= 0x85ebca6b;
+    hash ^= seed;
+    hash ^= hash >> 13;
+    hash *= 0xc2b2ae35;
+    hash ^= hash >> 16;
+    #endif
+    // Following statistics were measured on a Core i7-6700 @ 4.00Ghz, compiled with clang 7.0.1 -O2
+    // Note that the larger tables have high variance as they were run fewer times
+    //  HASH32-A   //  HASH32-BB  //  HASH32-C
+    //    0.10ms   //    0.10ms   //    0.10ms :      2,000 inserts creating 2K table
+    //    0.96ms   //    0.95ms   //    0.99ms :     20,000 inserts creating 20K table
+    //   14.69ms   //   14.43ms   //   14.97ms :    200,000 inserts creating 200K table
+    //  199.99ms   //  195.36ms   //  202.05ms :  2,000,000 inserts creating 2M table
+    // 2234.84ms   // 2187.74ms   // 2240.38ms : 20,000,000 inserts creating 20M table
+    //   55.68ms   //   53.72ms   //   57.31ms : 500,000 inserts & deletes in 2K table
+    //   63.43ms   //   61.99ms   //   65.73ms : 500,000 inserts & deletes in 20K table
+    //   80.04ms   //   77.96ms   //   81.83ms : 500,000 inserts & deletes in 200K table
+    //  100.42ms   //   97.40ms   //  102.39ms : 500,000 inserts & deletes in 2M table
+    //  119.71ms   //  120.59ms   //  121.63ms : 500,000 inserts & deletes in 20M table
+    //  185.28ms   //  195.15ms   //  187.74ms : 500,000 inserts & deletes in 200M table
+    //   15.58ms   //   14.79ms   //   15.52ms : 200,000 inserts creating 200K table with varying key spacing
+
+    return (((size_t) hash << 16 << 16) | hash) ^ 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;
+    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;
+  } else {
+    return stbds_siphash_bytes(p,len,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, size_t keyoffset, int mode, size_t i)
+{
+  if (mode >= STBDS_HM_STRING)
+    return 0==strcmp((char *) key, * (char **) ((char *) a + elemsize*i + keyoffset));
+  else
+    return 0==memcmp(key, (char *) a + elemsize*i + keyoffset, keysize);
+}
+
+#define STBDS_HASH_TO_ARR(x,elemsize) ((char*) (x) - (elemsize))
+#define STBDS_ARR_TO_HASH(x,elemsize) ((char*) (x) + (elemsize))
+
+#define stbds_hash_table(a)  ((stbds_hash_index *) stbds_header(a)->hash_table)
+
+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);
+  }
+  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, size_t keyoffset, int mode)
+{
+  void *raw_a = STBDS_HASH_TO_ARR(a,elemsize);
+  stbds_hash_index *table = stbds_hash_table(raw_a);
+  size_t hash = mode >= STBDS_HM_STRING ? stbds_hash_string((char*)key,table->seed) : stbds_hash_bytes(key, keysize,table->seed);
+  size_t step = STBDS_BUCKET_LENGTH;
+  size_t limit,i;
+  size_t pos;
+  stbds_hash_bucket *bucket;
+
+  if (hash < 2) hash += 2; // stored hash values are forbidden from being 0, so we can detect empty slots
+
+  pos = stbds_probe_position(hash, table->slot_count, table->slot_count_log2);
+
+  for (;;) {
+    STBDS_STATS(++stbds_hash_probes);
+    bucket = &table->storage[pos >> STBDS_BUCKET_SHIFT];
+
+    // 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, keyoffset, mode, bucket->index[i])) {
+          return (pos & ~STBDS_BUCKET_MASK)+i;
+        }
+      } else if (bucket->hash[i] == STBDS_HASH_EMPTY) {
+        return -1;
+      }
+    }
+
+    // search from beginning of bucket to pos
+    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, keyoffset, mode, bucket->index[i])) {
+          return (pos & ~STBDS_BUCKET_MASK)+i;
+        }
+      } else if (bucket->hash[i] == STBDS_HASH_EMPTY) {
+        return -1;
+      }
+    }
+
+    // quadratic probing
+    pos += step;
+    step += STBDS_BUCKET_LENGTH;
+    pos &= (table->slot_count-1);
+  }
+  /* NOTREACHED */
+}
+
+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);
+    *temp = STBDS_INDEX_EMPTY;
+    // adjust a to point after the default element
+    return STBDS_ARR_TO_HASH(a,elemsize);
+  } else {
+    stbds_hash_index *table;
+    void *raw_a = STBDS_HASH_TO_ARR(a,elemsize);
+    // adjust a to point to the default element
+    table = (stbds_hash_index *) stbds_header(raw_a)->hash_table;
+    if (table == 0) {
+      *temp = -1;
+    } else {
+      ptrdiff_t slot = stbds_hm_find_slot(a, elemsize, key, keysize, keyoffset, mode);
+      if (slot < 0) {
+        *temp = STBDS_INDEX_EMPTY;
+      } else {
+        stbds_hash_bucket *b = &table->storage[slot >> STBDS_BUCKET_SHIFT];
+        *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:
+  //   a is NULL <- allocate
+  //   a has a hash table but no entries, because of shmode <- grow
+  //   a has entries <- do nothing
+  if (a == NULL || stbds_header(STBDS_HASH_TO_ARR(a,elemsize))->length == 0) {
+    a = stbds_arrgrowf(a ? STBDS_HASH_TO_ARR(a,elemsize) : NULL, elemsize, 0, 1);
+    stbds_header(a)->length += 1;
+    memset(a, 0, elemsize);
+    a=STBDS_ARR_TO_HASH(a,elemsize);
+  }
+  return a;
+}
+
+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;
+
+  if (a == NULL) {
+    a = stbds_arrgrowf(0, elemsize, 0, 1);
+    memset(a, 0, elemsize);
+    stbds_header(a)->length += 1;
+    // adjust a to point AFTER the default element
+    a = STBDS_ARR_TO_HASH(a,elemsize);
+  }
+
+  // adjust a to point to the default element
+  raw_a = a;
+  a = STBDS_HASH_TO_ARR(a,elemsize);
+
+  table = (stbds_hash_index *) stbds_header(a)->hash_table;
+
+  if (table == NULL || table->used_count >= table->used_count_threshold) {
+    stbds_hash_index *nt;
+    size_t slot_count;
+
+    slot_count = (table == NULL) ? STBDS_BUCKET_LENGTH : table->slot_count*2;
+    nt = stbds_make_hash_index(slot_count, 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);
+  }
+
+  // we iterate hash table explicitly because we want to track if we saw a tombstone
+  {
+    size_t hash = mode >= STBDS_HM_STRING ? stbds_hash_string((char*)key,table->seed) : stbds_hash_bytes(key, keysize,table->seed);
+    size_t step = STBDS_BUCKET_LENGTH;
+    size_t pos;
+    ptrdiff_t tombstone = -1;
+    stbds_hash_bucket *bucket;
+
+    // stored hash values are forbidden from being 0, so we can detect empty slots to early out quickly
+    if (hash < 2) hash += 2;
+
+    pos = stbds_probe_position(hash, table->slot_count, table->slot_count_log2);
+
+    for (;;) {
+      size_t limit, i;
+      STBDS_STATS(++stbds_hash_probes);
+      bucket = &table->storage[pos >> STBDS_BUCKET_SHIFT];
+
+      // 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, keyoffset, mode, bucket->index[i])) {
+            stbds_temp(a) = bucket->index[i];
+            if (mode >= STBDS_HM_STRING)
+              stbds_temp_key(a) = * (char **) ((char *) raw_a + elemsize*bucket->index[i] + keyoffset);
+            return STBDS_ARR_TO_HASH(a,elemsize);
+          }
+        } else if (bucket->hash[i] == 0) {
+          pos = (pos & ~STBDS_BUCKET_MASK) + i;
+          goto found_empty_slot;
+        } else if (tombstone < 0) {
+          if (bucket->index[i] == STBDS_INDEX_DELETED)
+            tombstone = (ptrdiff_t) ((pos & ~STBDS_BUCKET_MASK) + i);
+        }
+      }
+
+      // search from beginning of bucket to pos
+      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, keyoffset, mode, bucket->index[i])) {
+            stbds_temp(a) = bucket->index[i];
+            return STBDS_ARR_TO_HASH(a,elemsize);
+          }
+        } else if (bucket->hash[i] == 0) {
+          pos = (pos & ~STBDS_BUCKET_MASK) + i;
+          goto found_empty_slot;
+        } else if (tombstone < 0) {
+          if (bucket->index[i] == STBDS_INDEX_DELETED)
+            tombstone = (ptrdiff_t) ((pos & ~STBDS_BUCKET_MASK) + i);
+        }
+      }
+
+      // quadratic probing
+      pos += step;
+      step += STBDS_BUCKET_LENGTH;
+      pos &= (table->slot_count-1);
+    }
+   found_empty_slot:
+    if (tombstone >= 0) {
+      pos = tombstone;
+      --table->tombstone_count;
+    }
+    ++table->used_count;
+
+    {
+      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
+      if ((size_t) i+1 > stbds_arrcap(a))
+        *(void **) &a = stbds_arrgrowf(a, elemsize, 1, 0);
+      raw_a = STBDS_ARR_TO_HASH(a,elemsize);
+
+      STBDS_ASSERT((size_t) i+1 <= stbds_arrcap(a));
+      stbds_header(a)->length = i+1;
+      bucket = &table->storage[pos >> STBDS_BUCKET_SHIFT];
+      bucket->hash[pos & STBDS_BUCKET_MASK] = hash;
+      bucket->index[pos & STBDS_BUCKET_MASK] = i-1;
+      stbds_temp(a) = i-1;
+
+      switch (table->string.mode) {
+         case STBDS_SH_STRDUP:  stbds_temp_key(a) = *(char **) ((char *) a + elemsize*i) = stbds_strdup((char*) key); break;
+         case STBDS_SH_ARENA:   stbds_temp_key(a) = *(char **) ((char *) a + elemsize*i) = stbds_stralloc(&table->string, (char*)key); break;
+         case STBDS_SH_DEFAULT: stbds_temp_key(a) = *(char **) ((char *) a + elemsize*i) = (char *) key; break;
+         default:                memcpy((char *) a + elemsize*i, key, keysize); break;
+      }
+    }
+    return STBDS_ARR_TO_HASH(a,elemsize);
+  }
+}
+
+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 = (unsigned char) mode;
+  return STBDS_ARR_TO_HASH(a,elemsize);
+}
+
+void * stbds_hmdel_key(void *a, size_t elemsize, void *key, size_t keysize, size_t keyoffset, int mode)
+{
+  if (a == NULL) {
+    return 0;
+  } else {
+    stbds_hash_index *table;
+    void *raw_a = STBDS_HASH_TO_ARR(a,elemsize);
+    table = (stbds_hash_index *) stbds_header(raw_a)->hash_table;
+    stbds_temp(raw_a) = 0;
+    if (table == 0) {
+      return a;
+    } else {
+      ptrdiff_t slot;
+      slot = stbds_hm_find_slot(a, elemsize, key, keysize, keyoffset, mode);
+      if (slot < 0)
+        return a;
+      else {
+        stbds_hash_bucket *b = &table->storage[slot >> STBDS_BUCKET_SHIFT];
+        int i = slot & STBDS_BUCKET_MASK;
+        ptrdiff_t old_index = b->index[i];
+        ptrdiff_t final_index = (ptrdiff_t) stbds_arrlen(raw_a)-1-1; // minus one for the raw_a vs a, and minus one for 'last'
+        STBDS_ASSERT(slot < (ptrdiff_t) table->slot_count);
+        --table->used_count;
+        ++table->tombstone_count;
+        stbds_temp(raw_a) = 1;
+        STBDS_ASSERT(table->used_count >= 0);
+        //STBDS_ASSERT(table->tombstone_count < table->slot_count/4);
+        b->hash[i] = STBDS_HASH_DELETED;
+        b->index[i] = STBDS_INDEX_DELETED;
+
+        if (mode == STBDS_HM_STRING && table->string.mode == STBDS_SH_STRDUP)
+          STBDS_FREE(NULL, *(char**) ((char *) a+elemsize*old_index));
+
+        // if indices are the same, memcpy is a no-op, but back-pointer-fixup will fail, so skip
+        if (old_index != final_index) {
+          // swap delete
+          memmove((char*) a + elemsize*old_index, (char*) a + elemsize*final_index, elemsize);
+
+          // 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, keyoffset, mode);
+          else
+            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;
+          STBDS_ASSERT(b->index[i] == final_index);
+          b->index[i] = old_index;
+        }
+        stbds_header(raw_a)->length -= 1;
+
+        if (table->used_count < table->used_count_shrink_threshold && table->slot_count > STBDS_BUCKET_LENGTH) {
+          stbds_header(raw_a)->hash_table = stbds_make_hash_index(table->slot_count>>1, table);
+          STBDS_FREE(NULL, table);
+          STBDS_STATS(++stbds_hash_shrink);
+        } else if (table->tombstone_count > table->tombstone_count_threshold) {
+          stbds_header(raw_a)->hash_table = stbds_make_hash_index(table->slot_count   , table);
+          STBDS_FREE(NULL, table);
+          STBDS_STATS(++stbds_hash_rebuild);
+        }
+
+        return a;
+      }
+    }
+  }
+  /* NOTREACHED */
+}
+
+static char *stbds_strdup(char *str)
+{
+  // to keep replaceable allocator simple, we don't want to use strdup.
+  // rolling our own also avoids problem of strdup vs _strdup
+  size_t len = strlen(str)+1;
+  char *p = (char*) STBDS_REALLOC(NULL, 0, len);
+  memmove(p, str, len);
+  return p;
+}
+
+#ifndef STBDS_STRING_ARENA_BLOCKSIZE_MIN
+#define STBDS_STRING_ARENA_BLOCKSIZE_MIN  512u
+#endif
+#ifndef STBDS_STRING_ARENA_BLOCKSIZE_MAX
+#define STBDS_STRING_ARENA_BLOCKSIZE_MAX  (1u<<20)
+#endif
+
+char *stbds_stralloc(stbds_string_arena *a, char *str)
+{
+  char *p;
+  size_t len = strlen(str)+1;
+  if (len > a->remaining) {
+    // compute the next blocksize
+    size_t blocksize = a->block;
+
+    // size is 512, 512, 1024, 1024, 2048, 2048, 4096, 4096, etc., so that
+    // there are log(SIZE) allocations to free when we destroy the table
+    blocksize = (size_t) (STBDS_STRING_ARENA_BLOCKSIZE_MIN) << (blocksize>>1);
+
+    // if size is under 1M, advance to next blocktype
+    if (blocksize < (size_t)(STBDS_STRING_ARENA_BLOCKSIZE_MAX))
+      ++a->block;
+
+    if (len > blocksize) {
+      // if string is larger than blocksize, then just allocate the full size.
+      // note that we still advance string_block so block size will continue
+      // increasing, so e.g. if somebody only calls this with 1000-long strings,
+      // eventually the arena will start doubling and handling those as well
+      stbds_string_block *sb = (stbds_string_block *) STBDS_REALLOC(NULL, 0, sizeof(*sb)-8 + len);
+      memmove(sb->storage, str, len);
+      if (a->storage) {
+        // insert it after the first element, so that we don't waste the space there
+        sb->next = a->storage->next;
+        a->storage->next = sb;
+      } else {
+        sb->next = 0;
+        a->storage = sb;
+        a->remaining = 0; // this is redundant, but good for clarity
+      }
+      return sb->storage;
+    } else {
+      stbds_string_block *sb = (stbds_string_block *) STBDS_REALLOC(NULL, 0, sizeof(*sb)-8 + blocksize);
+      sb->next = a->storage;
+      a->storage = sb;
+      a->remaining = blocksize;
+    }
+  }
+
+  STBDS_ASSERT(len <= a->remaining);
+  p = a->storage->storage + a->remaining - len;
+  a->remaining -= len;
+  memmove(p, str, len);
+  return p;
+}
+
+void stbds_strreset(stbds_string_arena *a)
+{
+  stbds_string_block *x,*y;
+  x = a->storage;
+  while (x) {
+    y = x->next;
+    STBDS_FREE(NULL, x);
+    x = y;
+  }
+  memset(a, 0, sizeof(*a));
+}
+
+#endif
+
+//////////////////////////////////////////////////////////////////////////////
+//
+//   UNIT TESTS
+//
+
+#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>
+#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)
+{
+#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;
+}
+
+void stbds_unit_tests(void)
+{
+#if defined(_MSC_VER) && _MSC_VER <= 1200 && defined(__cplusplus)
+  // VC6 C++ doesn't like the template<> trick on unnamed structures, so do nothing!
+  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, s;
+  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;
+
+  STBDS_ASSERT(arrlen(arr)==0);
+  for (i=0; i < 20000; i += 50) {
+    for (j=0; j < i; ++j)
+      arrpush(arr,j);
+    arrfree(arr);
+  }
+
+  for (i=0; i < 4; ++i) {
+    arrpush(arr,1); arrpush(arr,2); arrpush(arr,3); arrpush(arr,4);
+    arrdel(arr,i);
+    arrfree(arr);
+    arrpush(arr,1); arrpush(arr,2); arrpush(arr,3); arrpush(arr,4);
+    arrdelswap(arr,i);
+    arrfree(arr);
+  }
+
+  for (i=0; i < 5; ++i) {
+    arrpush(arr,1); arrpush(arr,2); arrpush(arr,3); arrpush(arr,4);
+    stbds_arrins(arr,i,5);
+    STBDS_ASSERT(arr[i] == 5);
+    if (i < 4)
+      STBDS_ASSERT(arr[4] == 4);
+    arrfree(arr);
+  }
+
+  i = 1;
+  STBDS_ASSERT(hmgeti(intmap,i) == -1);
+  hmdefault(intmap, -2);
+  STBDS_ASSERT(hmgeti(intmap, i) == -1);
+  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) {
+    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)
+    if (i & 1) STBDS_ASSERT(hmget(intmap, i) == -2 );
+    else       STBDS_ASSERT(hmget(intmap, i) == i*3);
+  for (i=2; i < testsize; i+=4)
+    hmdel(intmap, i); // delete half the entries
+  for (i=0; i < testsize; i+=1)
+    if (i & 3) STBDS_ASSERT(hmget(intmap, i) == -2 );
+    else       STBDS_ASSERT(hmget(intmap, i) == i*3);
+  for (i=0; i < testsize; i+=1)
+    hmdel(intmap, i); // delete the rest of the entries
+  for (i=0; i < testsize; i+=1)
+    STBDS_ASSERT(hmget(intmap, i) == -2 );
+  hmfree(intmap);
+  for (i=0; i < testsize; i+=2)
+    hmput(intmap, i, i*3);
+  hmfree(intmap);
+
+  #if defined(__clang__) || defined(__GNUC__)
+  #ifndef __cplusplus
+  intmap = NULL;
+  hmput(intmap, 15, 7);
+  hmput(intmap, 11, 3);
+  hmput(intmap,  9, 5);
+  STBDS_ASSERT(hmget(intmap, 9) == 5);
+  STBDS_ASSERT(hmget(intmap, 11) == 3);
+  STBDS_ASSERT(hmget(intmap, 15) == 7);
+  #endif
+  #endif
+
+  for (i=0; i < testsize; ++i)
+    stralloc(&sa, strkey(i));
+  strreset(&sa);
+
+  {
+    s.key = "a", s.value = 1;
+    shputs(strmap, s);
+    STBDS_ASSERT(*strmap[0].key == 'a');
+    STBDS_ASSERT(strmap[0].key == s.key);
+    STBDS_ASSERT(strmap[0].value == s.value);
+    shfree(strmap);
+  }
+
+  {
+    s.key = "a", s.value = 1;
+    sh_new_strdup(strmap);
+    shputs(strmap, s);
+    STBDS_ASSERT(*strmap[0].key == 'a');
+    STBDS_ASSERT(strmap[0].key != s.key);
+    STBDS_ASSERT(strmap[0].value == s.value);
+    shfree(strmap);
+  }
+
+  {
+    s.key = "a", s.value = 1;
+    sh_new_arena(strmap);
+    shputs(strmap, s);
+    STBDS_ASSERT(*strmap[0].key == 'a');
+    STBDS_ASSERT(strmap[0].key != s.key);
+    STBDS_ASSERT(strmap[0].value == s.value);
+    shfree(strmap);
+  }
+
+  for (j=0; j < 2; ++j) {
+    STBDS_ASSERT(shgeti(strmap,"foo") == -1);
+    if (j == 0)
+      sh_new_strdup(strmap);
+    else
+      sh_new_arena(strmap);
+    STBDS_ASSERT(shgeti(strmap,"foo") == -1);
+    shdefault(strmap, -2);
+    STBDS_ASSERT(shgeti(strmap,"foo") == -1);
+    for (i=0; i < testsize; i+=2)
+      shput(strmap, strkey(i), i*3);
+    for (i=0; i < testsize; i+=1)
+      if (i & 1) STBDS_ASSERT(shget(strmap, strkey(i)) == -2 );
+      else       STBDS_ASSERT(shget(strmap, strkey(i)) == i*3);
+    for (i=2; i < testsize; i+=4)
+      shdel(strmap, strkey(i)); // delete half the entries
+    for (i=0; i < testsize; i+=1)
+      if (i & 3) STBDS_ASSERT(shget(strmap, strkey(i)) == -2 );
+      else       STBDS_ASSERT(shget(strmap, strkey(i)) == i*3);
+    for (i=0; i < testsize; i+=1)
+      shdel(strmap, strkey(i)); // delete the rest of the entries
+    for (i=0; i < testsize; i+=1)
+      STBDS_ASSERT(shget(strmap, strkey(i)) == -2 );
+    shfree(strmap);
+  }
+
+  {
+    struct { char *key; char value; } *hash = NULL;
+    char name[4] = "jen";
+    shput(hash, "bob"   , 'h');
+    shput(hash, "sally" , 'e');
+    shput(hash, "fred"  , 'l');
+    shput(hash, "jen"   , 'x');
+    shput(hash, "doug"  , 'o');
+
+    shput(hash, name    , 'l');
+    shfree(hash);
+  }
+
+  for (i=0; i < testsize; i += 2) {
+    stbds_struct s = { i,i*2,i*3,i*4 };
+    hmput(map, s, i*5);
+  }
+
+  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+1,i*4 };
+    if (i & 1) STBDS_ASSERT(hmget(map, s) == 0);
+    else       STBDS_ASSERT(hmget(map, s) == i*5);
+    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) {
+    stbds_struct s = { i,i*2,i*3,i*4 };
+    hmputs(map2, s);
+  }
+  hmfree(map);
+
+  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+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(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
+
+
+/*
+------------------------------------------------------------------------------
+This software is available under 2 licenses -- choose whichever you prefer.
+------------------------------------------------------------------------------
+ALTERNATIVE A - MIT License
+Copyright (c) 2019 Sean Barrett
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to do
+so, subject to the following conditions:
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+------------------------------------------------------------------------------
+ALTERNATIVE B - Public Domain (www.unlicense.org)
+This is free and unencumbered software released into the public domain.
+Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
+software, either in source code form or as a compiled binary, for any purpose,
+commercial or non-commercial, and by any means.
+In jurisdictions that recognize copyright laws, the author or authors of this
+software dedicate any and all copyright interest in the software to the public
+domain. We make this dedication for the benefit of the public at large and to
+the detriment of our heirs and successors. We intend this dedication to be an
+overt act of relinquishment in perpetuity of all present and future rights to
+this software under copyright law.
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+------------------------------------------------------------------------------
+*/
index ec664212dc39153a441ed167c092fc5dc0fff906..f8800678e5fde7482e9ad93117698731642b73b0 100644 (file)
--- a/src/cli.c
+++ b/src/cli.c
@@ -1,7 +1,5 @@
-#include "stdio.h"
-
 int main(int argc, char *argv[]) {
-    int embed_test(void);
+    int embed_test();
     embed_test();
 
     return 0;
index e43ccff762e8b89b0bb4c8b5c149757a0dbc40e0..1751d7b9cb7f7fade0c11d0ef0bc5b6fd0cddfc5 100644 (file)
@@ -1,6 +1,11 @@
-#include "stdio.h"
+#define BH_DEFINE
+#define BH_NO_TABLE
+#define STB_DS_IMPLEMENTATION
+#define BH_STATIC
+#include "bh.h"
+#include "stb_ds.h"
 
-int embed_test(void) {
+i32 embed_test() {
     printf("Build works.\n");
     return 0;
 }
\ No newline at end of file