From a6097575bb0d37c6678fb7be245d4e8744548b8c Mon Sep 17 00:00:00 2001 From: Brendan Hansen Date: Fri, 15 May 2020 14:20:35 -0500 Subject: [PATCH] Added custom allocator capability to hashtables --- bh.h | 117 +++++++++++++++++++++++++++++------------------------- onyx | Bin 50520 -> 50624 bytes onyx.c | 2 +- onyxlex.h | 2 +- 4 files changed, 64 insertions(+), 57 deletions(-) diff --git a/bh.h b/bh.h index b2e44081..1febb5f1 100644 --- a/bh.h +++ b/bh.h @@ -450,39 +450,44 @@ typedef struct bh_hash_iterator { bh__hash_entry* entry; } bh_hash_iterator; +typedef struct bh__hash { + bh_allocator allocator; + ptr arrs[BH__HASH_MODULUS]; +} bh__hash; + #define bh_hash(T) T* #ifdef BH_HASH_SIZE_SAFE - #define bh_hash_init(tab) bh__hash_init((ptr **) &(tab)) - #define bh_hash_free(tab) bh__hash_free((ptr **) &(tab)) - #define bh_hash_put(T, tab, key, value) (assert(sizeof(T) == sizeof(*(tab))), (*((T *) bh__hash_put((ptr *) tab, sizeof(T), key)) = (T) value)) - #define bh_hash_has(T, tab, key) (assert(sizeof(T) == sizeof(*(tab))), (bh__hash_has((ptr *) tab, sizeof(T), key))) - #define bh_hash_get(T, tab, key) (assert(sizeof(T) == sizeof(*(tab))), (*((T *) bh__hash_get((ptr *) tab, sizeof(T), key)))) - #define bh_hash_delete(T, tab, key) (assert(sizeof(T) == sizeof(*(tab))), bh__hash_delete((ptr *) tab, sizeof(T), key)) - - #define bh_hash_iter_setup(T, tab) (bh__hash_iter_setup((ptr *) tab, sizeof(T))) + #define bh_hash_init(allocator_, tab) bh__hash_init(allocator_, (bh__hash **)&(tab)) + #define bh_hash_free(tab) bh__hash_free((bh__hash **)&(tab)) + #define bh_hash_put(T, tab, key, value) (assert(sizeof(T) == sizeof(*(tab))), (*((T *) bh__hash_put((bh__hash *) tab, sizeof(T), key)) = (T) value)) + #define bh_hash_has(T, tab, key) (assert(sizeof(T) == sizeof(*(tab))), (bh__hash_has((bh__hash *) tab, sizeof(T), key))) + #define bh_hash_get(T, tab, key) (assert(sizeof(T) == sizeof(*(tab))), (*((T *) bh__hash_get((bh__hash *) tab, sizeof(T), key)))) + #define bh_hash_delete(T, tab, key) (assert(sizeof(T) == sizeof(*(tab))), bh__hash_delete((bh__hash *) tab, sizeof(T), key)) + + #define bh_hash_iter_setup(T, tab) (assert(sizeof(T) == sizeof(*(tab))), bh__hash_iter_setup((bh__hash *) tab, sizeof(T))) #define bh_hash_iter_key(it) (it.entry->key) - #define bh_hash_iter_value(T, it) (*(T *)&(it.entry->value)) + #define bh_hash_iter_value(T, it) (assert(sizeof(T) == it.elemsize), *(T *)&(it.entry->value)) #else - #define bh_hash_init(tab) bh__hash_init((ptr **) &(tab)) - #define bh_hash_free(tab) bh__hash_free((ptr **) &(tab)) - #define bh_hash_put(T, tab, key, value) (*((T *) bh__hash_put((ptr *) tab, sizeof(T), key)) = value) - #define bh_hash_has(T, tab, key) (bh__hash_has((ptr *) tab, sizeof(T), key)) - #define bh_hash_get(T, tab, key) (*((T *) bh__hash_get((ptr *) tab, sizeof(T), key))) - #define bh_hash_delete(T, tab, key) (bh__hash_delete((ptr *) tab, sizeof(T), key)) - - #define bh_hash_iter_setup(T, tab) (assert(sizeof(T) == sizeof(*(tab))), bh__hash_iter_setup((ptr *) tab, sizeof(T))) + #define bh_hash_init(allocator_, tab) bh__hash_init(allocator_, (bh__hash **)&(tab)) + #define bh_hash_free(tab) bh__hash_free((bh__hash **)&(tab)) + #define bh_hash_put(T, tab, key, value) (*((T *) bh__hash_put((bh__hash *) tab, sizeof(T), key)) = value) + #define bh_hash_has(T, tab, key) (bh__hash_has((bh__hash *) tab, sizeof(T), key)) + #define bh_hash_get(T, tab, key) (*((T *) bh__hash_get((bh__hash *) tab, sizeof(T), key))) + #define bh_hash_delete(T, tab, key) (bh__hash_delete((bh__hash *) tab, sizeof(T), key)) + + #define bh_hash_iter_setup(T, tab) (bh__hash_iter_setup((bh__hash *) tab, sizeof(T))) #define bh_hash_iter_key(it) (it.entry->key) - #define bh_hash_iter_value(T, it) (assert(sizeof(T) == it.elemsize), *(T *)&(it.entry->value)) + #define bh_hash_iter_value(T, it) (*(T *)&(it.entry->value)) #endif -b32 bh__hash_init(ptr **table); -b32 bh__hash_free(ptr **table); -ptr bh__hash_put(ptr *table, i32 elemsize, char *key); -b32 bh__hash_has(ptr *table, i32 elemsize, char *key); -ptr bh__hash_get(ptr *table, i32 elemsize, char *key); -void bh__hash_delete(ptr *table, i32 elemsize, char *key); -bh_hash_iterator bh__hash_iter_setup(ptr *table, i32 elemsize); +b32 bh__hash_init(bh_allocator allocator, bh__hash **table); +b32 bh__hash_free(bh__hash **table); +ptr bh__hash_put(bh__hash *table, i32 elemsize, char *key); +b32 bh__hash_has(bh__hash *table, i32 elemsize, char *key); +ptr bh__hash_get(bh__hash *table, i32 elemsize, char *key); +void bh__hash_delete(bh__hash *table, i32 elemsize, char *key); +bh_hash_iterator bh__hash_iter_setup(bh__hash *table, i32 elemsize); b32 bh_hash_iter_next(bh_hash_iterator* it); #endif @@ -667,7 +672,7 @@ BH_ALLOCATOR_PROC(bh_alloc_nofree_allocator_proc) { } break; case bh_allocator_action_resize: { - // Need to think about this one + // Do nothing since this is a fixed allocator } break; case bh_allocator_action_free: { @@ -1180,60 +1185,62 @@ void bh__arr_insertn(void **arr, i32 elemsize, i32 index, i32 numelems) { //------------------------------------------------------------------------------------- #ifndef BH_NO_HASHTABLE -b32 bh__hash_init(ptr **table) { - *table = malloc(sizeof(ptr) * BH__HASH_MODULUS); +b32 bh__hash_init(bh_allocator allocator, bh__hash **table) { + *table = bh_alloc(allocator, sizeof(bh__hash)); if (*table == NULL) return 0; + (*table)->allocator = allocator; + for (i32 i = 0; i < BH__HASH_MODULUS; i++) { - (*table)[i] = NULL; + (*table)->arrs[i] = NULL; } return 1; } -b32 bh__hash_free(ptr **table) { +b32 bh__hash_free(bh__hash **table) { for (i32 i = 0; i < BH__HASH_MODULUS; i++) { - if ((*table)[i] != NULL) { - bh_arr_free(*((*table) + i)); + if ((*table)->arrs[i] != NULL) { + bh_arr_free((*table)->arrs[i]); } } - free(*table); + bh_free((*table)->allocator, *table); *table = NULL; } // Assumes NULL terminated string for key -ptr bh__hash_put(ptr *table, i32 elemsize, char *key) { +ptr bh__hash_put(bh__hash *table, i32 elemsize, char *key) { u64 index = bh__hash_function(key, 0); elemsize += BH__HASH_STORED_KEY_SIZE; - ptr arrptr = table[index]; + ptr arrptr = table->arrs[index]; i32 len = bh_arr_length(arrptr); while (len--) { if (strncmp(key, (char *) arrptr, BH__HASH_STORED_KEY_SIZE) == 0) goto found_matching; - arrptr = (ptr)((char *) arrptr + elemsize); + arrptr = bh_pointer_add(arrptr, elemsize); } // Didn't find it in the array, make a new one - arrptr = table[index]; + arrptr = table->arrs[index]; len = bh_arr_length(arrptr); - bh__arr_grow(bh_arr_allocator(arrptr), &arrptr, elemsize, len + 1); + bh__arr_grow(table->allocator, &arrptr, elemsize, len + 1); bh__arrhead(arrptr)->length++; - table[index] = arrptr; + table->arrs[index] = arrptr; - arrptr = (ptr)(((char *) arrptr) + elemsize * len); + arrptr = bh_pointer_add(arrptr, elemsize * len); strncpy(arrptr, key, BH__HASH_STORED_KEY_SIZE); found_matching: - return (ptr)(((char *) arrptr) + BH__HASH_STORED_KEY_SIZE); + return bh_pointer_add(arrptr, BH__HASH_STORED_KEY_SIZE); } -b32 bh__hash_has(ptr *table, i32 elemsize, char *key) { +b32 bh__hash_has(bh__hash *table, i32 elemsize, char *key) { u64 index = bh__hash_function(key, 0); - ptr arrptr = table[index]; + ptr arrptr = table->arrs[index]; if (arrptr == NULL) return 0; i32 len = bh_arr_length(arrptr); @@ -1241,16 +1248,16 @@ b32 bh__hash_has(ptr *table, i32 elemsize, char *key) { while (len--) { if (strncmp(key, (char *) arrptr, BH__HASH_STORED_KEY_SIZE) == 0) return 1; - arrptr = (ptr)((char *) arrptr + stride); + arrptr = bh_pointer_add(arrptr, stride); } return 0; } -ptr bh__hash_get(ptr *table, i32 elemsize, char *key) { +ptr bh__hash_get(bh__hash *table, i32 elemsize, char *key) { u64 index = bh__hash_function(key, 0); - ptr arrptr = table[index]; + ptr arrptr = table->arrs[index]; i32 len = bh_arr_length(arrptr); assert(arrptr != NULL); @@ -1258,19 +1265,19 @@ ptr bh__hash_get(ptr *table, i32 elemsize, char *key) { while (len--) { if (strncmp(key, (char *) arrptr, BH__HASH_STORED_KEY_SIZE) == 0) { - return (ptr)((char *) arrptr + BH__HASH_STORED_KEY_SIZE); + return bh_pointer_add(arrptr, BH__HASH_STORED_KEY_SIZE); } - arrptr = (ptr)((char *) arrptr + stride); + return bh_pointer_add(arrptr, stride); } return 0; } -void bh__hash_delete(ptr *table, i32 elemsize, char *key) { +void bh__hash_delete(bh__hash *table, i32 elemsize, char *key) { u64 index = bh__hash_function(key, 0); - ptr arrptr = table[index]; + ptr arrptr = table->arrs[index]; i32 len = bh_arr_length(arrptr); if (arrptr == NULL) return; // Didn't exist @@ -1278,7 +1285,7 @@ void bh__hash_delete(ptr *table, i32 elemsize, char *key) { i32 i = 0; while (len && strncmp(key, (char *) arrptr, BH__HASH_STORED_KEY_SIZE) != 0) { - arrptr = (ptr)((char *) arrptr + stride); + arrptr = bh_pointer_add(arrptr, stride); i++, len--; } @@ -1287,10 +1294,10 @@ void bh__hash_delete(ptr *table, i32 elemsize, char *key) { bh__arr_deleten((void **) &arrptr, elemsize, i, 1); } -bh_hash_iterator bh__hash_iter_setup(ptr *table, i32 elemsize) { +bh_hash_iterator bh__hash_iter_setup(bh__hash *table, i32 elemsize) { bh_hash_iterator it = { - .tab = table, - .endtab = table + BH__HASH_MODULUS, + .tab = table->arrs, + .endtab = table->arrs + BH__HASH_MODULUS, .elemsize = elemsize, .entry = NULL }; @@ -1307,7 +1314,7 @@ b32 bh_hash_iter_next(bh_hash_iterator* it) { goto step_to_next; } - it->entry = (bh__hash_entry *)((char *)(it->entry) + BH__HASH_STORED_KEY_SIZE + it->elemsize); + it->entry = (bh__hash_entry *)bh_pointer_add(it->entry, BH__HASH_STORED_KEY_SIZE + it->elemsize); return 1; } diff --git a/onyx b/onyx index 57bbd0546305f1b2277e9eb07e1b08de762d515f..7191babc8a9c7e41196ef1c6f726eaaa76abdca1 100755 GIT binary patch delta 16115 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