Minor changes to hash table code

git-svn-id: https://svn.eduke32.com/eduke32@8372 1a8010ca-5511-0410-912e-c29ae57300e0

# Conflicts:
#	source/build/include/hash.h
This commit is contained in:
terminx 2019-12-07 23:50:20 +00:00 committed by Christoph Oelckers
parent 15b76f2041
commit 0af13dcde0
2 changed files with 69 additions and 134 deletions

View file

@ -5,12 +5,13 @@
#define hash_h_ #define hash_h_
// Hash functions // Hash functions
#define DJB_MAGIC 5381u
typedef struct hashitem_ // size is 12/24 bytes. typedef struct hashitem // size is 12/24 bytes.
{ {
char *string; char *string;
intptr_t key; intptr_t key;
struct hashitem_ *next; struct hashitem *next;
} hashitem_t; } hashitem_t;
typedef struct typedef struct
@ -22,8 +23,8 @@ typedef struct
// djb3 algorithm // djb3 algorithm
static inline uint32_t hash_getcode(const char *s) static inline uint32_t hash_getcode(const char *s)
{ {
uint32_t h = 5381; uint32_t h = DJB_MAGIC;
int32_t ch; char ch;
while ((ch = Btolower(*s++)) != '\0') while ((ch = Btolower(*s++)) != '\0')
h = ((h << 5) + h) ^ ch; h = ((h << 5) + h) ^ ch;
@ -32,49 +33,53 @@ static inline uint32_t hash_getcode(const char *s)
} }
void hash_init(hashtable_t *t); void hash_init(hashtable_t *t);
void hash_loop(hashtable_t *t, void(*func)(const char *, intptr_t)); void hash_loop(hashtable_t *t, void (*func)(const char *, intptr_t));
void hash_free(hashtable_t *t); void hash_free(hashtable_t *t);
intptr_t hash_findcase(hashtable_t const * t, char const * s);
intptr_t hash_find(hashtable_t const * t, char const * s);
void hash_add(hashtable_t *t, const char *s, intptr_t key, int32_t replace); void hash_add(hashtable_t *t, const char *s, intptr_t key, int32_t replace);
void hash_delete(hashtable_t *t, const char *s); void hash_delete(hashtable_t *t, const char *s);
intptr_t hash_findcase(hashtable_t const *t, char const *s);
intptr_t hash_find(hashtable_t const *t, char const *s);
// Hash functions // Hash functions
// modified for raw binary keys and one big allocation, and maximum find() performance // modified for raw binary keys and one big allocation, and maximum find() performance
typedef struct inthashitem_ typedef struct inthashitem
{ {
intptr_t key; intptr_t key;
intptr_t value; intptr_t value;
struct inthashitem_ *collision; // use NULL to signify empty and pointer identity to signify end of linked list struct inthashitem *collision; // use NULL to signify empty and pointer identity to signify end of linked list
} inthashitem_t; } inthashitem_t;
typedef struct typedef struct
{ {
inthashitem_t * items; inthashitem_t *items;
uint32_t count; uint32_t count;
} inthashtable_t; } inthashtable_t;
// djb3 algorithm // djb3 algorithm
static inline uint32_t inthash_getcode(intptr_t key) static inline uint32_t inthash_getcode(intptr_t key)
{ {
uint32_t h = 5381; uint32_t h = DJB_MAGIC;
for (uint8_t const * keybuf = (uint8_t *) &key, *const keybuf_end = keybuf + sizeof(intptr_t); keybuf < keybuf_end; ++keybuf) for (auto keybuf = (uint8_t const *) &key, keybuf_end = keybuf + sizeof(key); keybuf < keybuf_end; ++keybuf)
h = ((h << 5) + h) ^ (uint32_t) *keybuf; h = ((h << 5) + h) ^ (uint32_t) *keybuf;
return h; return h;
} }
void inthash_init(inthashtable_t *t); void inthash_init(inthashtable_t *t);
void inthash_loop(inthashtable_t const *t, void(*func)(intptr_t, intptr_t)); void inthash_loop(inthashtable_t const *t, void (*func)(intptr_t, intptr_t));
void inthash_free(inthashtable_t *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_add(inthashtable_t *t, intptr_t key, intptr_t value, int32_t replace);
void inthash_delete(inthashtable_t *t, intptr_t key); void inthash_delete(inthashtable_t *t, intptr_t key);
intptr_t inthash_find(inthashtable_t const *t, intptr_t key);
// keep the load factor below 0.75 and make sure the size is odd // keep the load factor below 0.75 and make sure the size is odd
// ideally we would find the next largest prime number // ideally we would find the next largest prime number
#define INTHASH_SIZE(size) ((size * 4u / 3u) | 1u) #define INTHASH_SIZE(size) ((size * 4u / 3u) | 1u)
#endif #endif
#endif // hash_h_

View file

@ -6,69 +6,63 @@
void hash_init(hashtable_t *t) void hash_init(hashtable_t *t)
{ {
hash_free(t); hash_free(t);
t->items=(hashitem_t **) Xcalloc(1, t->size * sizeof(hashitem_t)); t->items = (hashitem_t **) Xaligned_calloc(16, t->size, sizeof(hashitem_t));
} }
void hash_loop(hashtable_t *t, void(*func)(const char *, intptr_t)) void hash_loop(hashtable_t *t, void(*func)(const char *, intptr_t))
{ {
if (t->items == NULL) if (t->items == nullptr)
return; return;
for (bssize_t i=0; i < t->size; i++) for (native_t i=0; i < t->size; i++)
for (hashitem_t *item=t->items[i]; item != NULL; item = item->next) for (auto item = t->items[i]; item != nullptr; item = item->next)
func(item->string, item->key); func(item->string, item->key);
} }
void hash_free(hashtable_t *t) void hash_free(hashtable_t *t)
{ {
if (t == NULL || t->items == NULL) if (t->items == nullptr)
return; return;
int remaining = t->size - 1; int remaining = t->size - 1;
do do
{ {
hashitem_t *cur = t->items[remaining]; auto cur = t->items[remaining];
while (cur) while (cur)
{ {
hashitem_t * const tmp = cur; auto tmp = cur;
cur = cur->next; cur = cur->next;
Xfree(tmp->string); Xfree(tmp->string);
Xfree(tmp); Xaligned_free(tmp);
} }
} while (--remaining >= 0); } while (--remaining >= 0);
DO_FREE_AND_NULL(t->items); ALIGNED_FREE_AND_NULL(t->items);
} }
void hash_add(hashtable_t *t, const char *s, intptr_t key, int32_t replace) void hash_add(hashtable_t *t, const char *s, intptr_t key, int32_t replace)
{ {
#ifdef DEBUGGINGAIDS #ifdef DEBUGGINGAIDS
Bassert(t->items != NULL); Bassert(t->items != nullptr);
#else
if (EDUKE32_PREDICT_FALSE(t->items == NULL))
{
initprintf("hash_add(): table not initialized!\n");
return;
}
#endif #endif
uint32_t const code = hash_getcode(s) % t->size;
uint32_t code = hash_getcode(s) % t->size; auto cur = t->items[code];
hashitem_t *cur = t->items[code];
if (!cur) if (!cur)
{ {
cur = (hashitem_t *) Xcalloc(1, sizeof(hashitem_t)); cur = (hashitem_t *) Xaligned_alloc(16, sizeof(hashitem_t));
cur->string = Xstrdup(s); cur->string = Xstrdup(s);
cur->key = key; cur->key = key;
cur->next = NULL; cur->next = nullptr;
t->items[code] = cur; t->items[code] = cur;
return; return;
} }
hashitem_t *prev = NULL; hashitem_t *prev = nullptr;
do do
{ {
@ -80,10 +74,11 @@ void hash_add(hashtable_t *t, const char *s, intptr_t key, int32_t replace)
prev = cur; prev = cur;
} while ((cur = cur->next)); } while ((cur = cur->next));
cur = (hashitem_t *) Xcalloc(1, sizeof(hashitem_t)); cur = (hashitem_t *) Xaligned_alloc(16, sizeof(hashitem_t));
cur->string = Xstrdup(s); cur->string = Xstrdup(s);
cur->key = key; cur->key = key;
cur->next = NULL; cur->next = nullptr;
prev->next = cur; prev->next = cur;
} }
@ -91,22 +86,15 @@ void hash_add(hashtable_t *t, const char *s, intptr_t key, int32_t replace)
void hash_delete(hashtable_t *t, const char *s) void hash_delete(hashtable_t *t, const char *s)
{ {
#ifdef DEBUGGINGAIDS #ifdef DEBUGGINGAIDS
Bassert(t->items != NULL); Bassert(t->items != nullptr);
#else
if (t->items == NULL)
{
initprintf("hash_delete(): table not initialized!\n");
return;
}
#endif #endif
uint32_t const code = hash_getcode(s) % t->size;
uint32_t code = hash_getcode(s) % t->size; auto cur = t->items[code];
hashitem_t *cur = t->items[code];
if (!cur) if (!cur)
return; return;
hashitem_t *prev = NULL; hashitem_t *prev = nullptr;
do do
{ {
@ -119,7 +107,7 @@ void hash_delete(hashtable_t *t, const char *s)
else else
prev->next = cur->next; prev->next = cur->next;
Xfree(cur); Xaligned_free(cur);
break; break;
} }
@ -130,16 +118,9 @@ void hash_delete(hashtable_t *t, const char *s)
intptr_t hash_find(const hashtable_t * const t, char const * const s) intptr_t hash_find(const hashtable_t * const t, char const * const s)
{ {
#ifdef DEBUGGINGAIDS #ifdef DEBUGGINGAIDS
Bassert(t->items != NULL); Bassert(t->items != nullptr);
#else
if (t->items == NULL)
{
initprintf("hash_find(): table not initialized!\n");
return -1;
}
#endif #endif
auto cur = t->items[hash_getcode(s) % t->size];
hashitem_t *cur = t->items[hash_getcode(s) % t->size];
if (!cur) if (!cur)
return -1; return -1;
@ -155,16 +136,9 @@ intptr_t hash_find(const hashtable_t * const t, char const * const s)
intptr_t hash_findcase(const hashtable_t * const t, char const * const s) intptr_t hash_findcase(const hashtable_t * const t, char const * const s)
{ {
#ifdef DEBUGGINGAIDS #ifdef DEBUGGINGAIDS
Bassert(t->items != NULL); Bassert(t->items != nullptr);
#else
if (t->items == NULL)
{
initprintf("hash_findcase(): table not initialized!\n");
return -1;
}
#endif #endif
auto cur = t->items[hash_getcode(s) % t->size];
hashitem_t *cur = t->items[hash_getcode(s) % t->size];
if (!cur) if (!cur)
return -1; return -1;
@ -178,60 +152,36 @@ intptr_t hash_findcase(const hashtable_t * const t, char const * const s)
} }
void inthash_free(inthashtable_t *t) { ALIGNED_FREE_AND_NULL(t->items); }
void inthash_init(inthashtable_t *t) void inthash_init(inthashtable_t *t)
{ {
if (EDUKE32_PREDICT_FALSE(!t->count))
{
initputs("inthash_add(): count is zero!\n");
return;
}
inthash_free(t); inthash_free(t);
t->items = (inthashitem_t *) Xaligned_calloc(16, t->count, sizeof(inthashitem_t));
t->items = (inthashitem_t *) Xcalloc(t->count, sizeof(inthashitem_t));
} }
void inthash_loop(inthashtable_t const *t, void(*func)(intptr_t, intptr_t)) void inthash_loop(inthashtable_t const *t, void(*func)(intptr_t, intptr_t))
{ {
#ifdef DEBUGGINGAIDS if (t->items == nullptr)
Bassert(t->items != NULL);
#else
if (EDUKE32_PREDICT_FALSE(t->items == NULL))
{
initputs("inthash_loop(): table not initialized!\n");
return; return;
}
#endif
for (inthashitem_t const * item = t->items, *const items_end = t->items + t->count; item < items_end; ++item) for (auto *item = t->items, *const items_end = t->items + t->count; item < items_end; ++item)
func(item->key, item->value); func(item->key, item->value);
} }
void inthash_free(inthashtable_t *t)
{
DO_FREE_AND_NULL(t->items);
}
void inthash_add(inthashtable_t *t, intptr_t key, intptr_t value, int32_t replace) void inthash_add(inthashtable_t *t, intptr_t key, intptr_t value, int32_t replace)
{ {
#ifdef DEBUGGINGAIDS #ifdef DEBUGGINGAIDS
Bassert(t->items != NULL); Bassert(t->items != nullptr);
#else
if (EDUKE32_PREDICT_FALSE(t->items == NULL))
{
initputs("inthash_add(): table not initialized!\n");
return;
}
#endif #endif
auto seeker = t->items + inthash_getcode(key) % t->count;
inthashitem_t * seeker = t->items + inthash_getcode(key) % t->count; if (seeker->collision == nullptr)
if (seeker->collision == NULL)
{ {
seeker->key = key; seeker->key = key;
seeker->value = value; seeker->value = value;
seeker->collision = seeker; seeker->collision = seeker;
return; return;
} }
@ -254,17 +204,14 @@ void inthash_add(inthashtable_t *t, intptr_t key, intptr_t value, int32_t replac
} }
} }
inthashitem_t *tail = seeker; auto tail = seeker;
do do
tail = t->items + (tail - t->items + 1) % t->count; tail = t->items + (tail - t->items + 1) % t->count;
while (tail->collision != NULL && tail != seeker); while (tail->collision != nullptr && tail != seeker);
if (EDUKE32_PREDICT_FALSE(tail == seeker)) if (EDUKE32_PREDICT_FALSE(tail == seeker))
{ fatal_exit("inthash_add(): table full!\n");
initputs("inthash_add(): table full!\n");
return;
}
tail->key = key; tail->key = key;
tail->value = value; tail->value = value;
@ -275,35 +222,25 @@ void inthash_add(inthashtable_t *t, intptr_t key, intptr_t value, int32_t replac
void inthash_delete(inthashtable_t *t, intptr_t key) void inthash_delete(inthashtable_t *t, intptr_t key)
{ {
#ifdef DEBUGGINGAIDS #ifdef DEBUGGINGAIDS
Bassert(t->items != NULL); Bassert(t->items != nullptr);
#else
if (EDUKE32_PREDICT_FALSE(t->items == NULL))
{
initputs("inthash_delete(): table not initialized!\n");
return;
}
#endif #endif
auto seeker = t->items + inthash_getcode(key) % t->count;
inthashitem_t * seeker = t->items + inthash_getcode(key) % t->count; if (seeker->collision == nullptr || seeker->key == key)
if (seeker->collision == NULL)
return;
if (seeker->key == key)
{ {
seeker->collision = NULL; seeker->collision = nullptr;
return; return;
} }
while (seeker != seeker->collision) while (seeker != seeker->collision)
{ {
inthashitem_t * const prev = seeker; auto prev = seeker;
seeker = seeker->collision; seeker = seeker->collision;
if (seeker->key == key) if (seeker->key == key)
{ {
prev->collision = seeker == seeker->collision ? prev : seeker->collision; prev->collision = seeker == seeker->collision ? prev : seeker->collision;
seeker->collision = NULL; seeker->collision = nullptr;
return; return;
} }
} }
@ -312,18 +249,11 @@ void inthash_delete(inthashtable_t *t, intptr_t key)
intptr_t inthash_find(inthashtable_t const *t, intptr_t key) intptr_t inthash_find(inthashtable_t const *t, intptr_t key)
{ {
#ifdef DEBUGGINGAIDS #ifdef DEBUGGINGAIDS
Bassert(t->items != NULL); Bassert(t->items != nullptr);
#else
if (EDUKE32_PREDICT_FALSE(t->items == NULL))
{
initputs("inthash_find(): table not initialized!\n");
return -1;
}
#endif #endif
auto seeker = t->items + inthash_getcode(key) % t->count;
inthashitem_t const * seeker = t->items + inthash_getcode(key) % t->count; if (seeker->collision == nullptr)
if (seeker->collision == NULL)
return -1; return -1;
if (seeker->key == key) if (seeker->key == key)