From 2e968b90bf882cccb9a3450e903c233d475c1dd2 Mon Sep 17 00:00:00 2001 From: hendricks266 Date: Fri, 8 Jan 2016 01:33:46 +0000 Subject: [PATCH] Add inthashtable_t, mapping intptr_t to intptr_t. git-svn-id: https://svn.eduke32.com/eduke32@5532 1a8010ca-5511-0410-912e-c29ae57300e0 --- polymer/eduke32/build/include/build.h | 31 ++++- polymer/eduke32/build/src/engine.c | 158 ++++++++++++++++++++++++++ 2 files changed, 187 insertions(+), 2 deletions(-) diff --git a/polymer/eduke32/build/include/build.h b/polymer/eduke32/build/include/build.h index 948cc1307..8945853ef 100644 --- a/polymer/eduke32/build/include/build.h +++ b/polymer/eduke32/build/include/build.h @@ -1407,11 +1407,11 @@ int32_t loadoldboard(const char *filename, char fromwhere, vec3_t *dapos, int16_ // Hash functions -typedef struct _hashitem // size is 12/24 bytes. +typedef struct hashitem_ // size is 12/24 bytes. { char *string; intptr_t key; - struct _hashitem *next; + struct hashitem_ *next; } hashitem_t; typedef struct @@ -1428,6 +1428,33 @@ intptr_t hash_find(const hashtable_t *t, const char *s); void hash_add(hashtable_t *t, const char *s, intptr_t key, int32_t replace); void hash_delete(hashtable_t *t, const char *s); + +// Hash functions +// modified for raw binary keys and one big allocation, and maximum find() performance + +typedef struct inthashitem_ +{ + intptr_t key; + intptr_t value; + struct inthashitem_ *collision; // use NULL to signify empty and pointer identity to signify end of linked list +} inthashitem_t; + +typedef struct +{ + inthashitem_t * items; + uint32_t count; +} inthashtable_t; + +void inthash_init(inthashtable_t *t); +void inthash_loop(inthashtable_t const *t, void (*func)(intptr_t, intptr_t)); +void inthash_free(inthashtable_t *t); +intptr_t inthash_find(inthashtable_t const *t, intptr_t key); +void inthash_add(inthashtable_t *t, intptr_t key, intptr_t value, int32_t replace); +void inthash_delete(inthashtable_t *t, intptr_t key); +// keep the load factor below 0.75 and make sure the size is odd +// ideally we would find the next largest prime number +#define INTHASH_SIZE(size) ((size * 4u / 3u) | 1u) + #ifdef POLYMER # include "polymer.h" #else diff --git a/polymer/eduke32/build/src/engine.c b/polymer/eduke32/build/src/engine.c index 2a6e96592..93899973c 100644 --- a/polymer/eduke32/build/src/engine.c +++ b/polymer/eduke32/build/src/engine.c @@ -18539,6 +18539,164 @@ intptr_t hash_findcase(const hashtable_t *t, const char *s) return -1; } + +void inthash_init(inthashtable_t *t) +{ + if (EDUKE32_PREDICT_FALSE(!t->count)) + { + initputs("inthash_add(): count is zero!\n"); + return; + } + + inthash_free(t); + + t->items = (inthashitem_t *)Xcalloc(t->count, sizeof(inthashitem_t)); +} + +void inthash_loop(inthashtable_t const *t, void (*func)(intptr_t, intptr_t)) +{ + if (EDUKE32_PREDICT_FALSE(t->items == NULL)) + { + initputs("inthash_loop(): table not initialized!\n"); + return; + } + + for (inthashitem_t const * item = t->items, * const items_end = t->items + t->count; item < items_end; ++item) + func(item->key, item->value); +} + +void inthash_free(inthashtable_t *t) +{ + DO_FREE_AND_NULL(t->items); +} + +// djb3 algorithm +static inline uint32_t inthash_getcode(intptr_t key) +{ + uint32_t h = 5381; + + for (uint8_t const * keybuf = (uint8_t *)&key, * const keybuf_end = keybuf + sizeof(intptr_t); keybuf < keybuf_end; ++keybuf) + h = ((h << 5) + h) ^ (uint32_t)*keybuf; + + return h; +} + +void inthash_add(inthashtable_t *t, intptr_t key, intptr_t value, int32_t replace) +{ + if (EDUKE32_PREDICT_FALSE(t->items == NULL)) + { + initputs("inthash_add(): table not initialized!\n"); + return; + } + + inthashitem_t * seeker = t->items + inthash_getcode(key) % t->count; + + if (seeker->collision == NULL) + { + seeker->key = key; + seeker->value = value; + seeker->collision = seeker; + + return; + } + + if (seeker->key == key) + { + if (replace) + seeker->value = value; + return; + } + + while (seeker != seeker->collision) + { + seeker = seeker->collision; + + if (seeker->key == key) + { + if (replace) + seeker->value = value; + return; + } + } + + inthashitem_t *tail = seeker; + + do + tail = t->items + (tail - t->items + 1) % t->count; + while (tail->collision != NULL && tail != seeker); + + if (EDUKE32_PREDICT_FALSE(tail == seeker)) + { + initputs("inthash_add(): table full!\n"); + return; + } + + tail->key = key; + tail->value = value; + tail->collision = seeker->collision = tail; +} + +// delete at most once +void inthash_delete(inthashtable_t *t, intptr_t key) +{ + if (EDUKE32_PREDICT_FALSE(t->items == NULL)) + { + initputs("inthash_delete(): table not initialized!\n"); + return; + } + + inthashitem_t * seeker = t->items + inthash_getcode(key) % t->count; + + if (seeker->collision == NULL) + return; + + if (seeker->key == key) + { + seeker->collision = NULL; + return; + } + + while (seeker != seeker->collision) + { + inthashitem_t * const prev = seeker; + seeker = seeker->collision; + + if (seeker->key == key) + { + prev->collision = seeker == seeker->collision ? prev : seeker->collision; + seeker->collision = NULL; + return; + } + } +} + +intptr_t inthash_find(inthashtable_t const *t, intptr_t key) +{ + if (EDUKE32_PREDICT_FALSE(t->items == NULL)) + { + initputs("inthash_find(): table not initialized!\n"); + return -1; + } + + inthashitem_t const * seeker = t->items + inthash_getcode(key) % t->count; + + if (seeker->collision == NULL) + return -1; + + if (seeker->key == key) + return seeker->value; + + while (seeker != seeker->collision) + { + seeker = seeker->collision; + + if (seeker->key == key) + return seeker->value; + } + + return -1; +} + /* * vim:ts=8: */