diff --git a/hashtable.c b/hashtable.c index f544b5f..8300604 100644 --- a/hashtable.c +++ b/hashtable.c @@ -49,11 +49,21 @@ unsigned int hashtable_find_slot(hashtable* t, char* key) { assert(key != NULL && key != HASHTABLE_TOMBSTONE); int index = hashtable_hash(key) % t->capacity; - while (t->body[index].key != NULL && - (t->body[index].key == HASHTABLE_TOMBSTONE || strcmp(t->body[index].key, key) != 0)) { + int first_tombstone = -1; + for (unsigned int i = 0; i < t->capacity; i++) { + if (t->body[index].key == NULL) { + return (first_tombstone != -1) ? first_tombstone : index; + } + if (t->body[index].key == HASHTABLE_TOMBSTONE) { + if (first_tombstone == -1) first_tombstone = index; + } else if (strcmp(t->body[index].key, key) == 0) { + return index; + } index = (index + 1) % t->capacity; } - return index; + /* All slots probed without finding key or NULL; reuse a tombstone. */ + assert(first_tombstone != -1); + return first_tombstone; } /** @@ -62,7 +72,7 @@ unsigned int hashtable_find_slot(hashtable* t, char* key) void* hashtable_get(hashtable* t, char* key) { int index = hashtable_find_slot(t, key); - if (t->body[index].key != NULL) { + if (t->body[index].key != NULL && t->body[index].key != HASHTABLE_TOMBSTONE) { return t->body[index].value; } else { return NULL; @@ -75,7 +85,7 @@ void* hashtable_get(hashtable* t, char* key) void hashtable_set(hashtable* t, char* key, void* value) { int index = hashtable_find_slot(t, key); - if (t->body[index].key != NULL) { + if (t->body[index].key != NULL && t->body[index].key != HASHTABLE_TOMBSTONE) { /* Entry exists; update it. */ t->body[index].value = value; } else { @@ -97,7 +107,7 @@ void hashtable_set(hashtable* t, char* key, void* value) void hashtable_remove(hashtable* t, char* key) { int index = hashtable_find_slot(t, key); - if (t->body[index].key != NULL) { + if (t->body[index].key != NULL && t->body[index].key != HASHTABLE_TOMBSTONE) { t->body[index].key = HASHTABLE_TOMBSTONE; t->body[index].value = NULL; t->size--; diff --git a/tests.c b/tests.c index 4521a62..886b3ac 100644 --- a/tests.c +++ b/tests.c @@ -1,5 +1,7 @@ #include #include +#include +#include #include "arraylist.h" #include "hashtable.h" /** @@ -209,5 +211,35 @@ int main() hashtable_destroy(t3); + /* + * Test: inserting into a table full of tombstones should not infinite loop. + * Keys "ab", "ba", "cd", "dc", "ef" all hash to slot 0 with capacity 4. + */ + signal(SIGALRM, SIG_DFL); + alarm(2); + + hashtable* t4 = hashtable_create(); + hashtable_set(t4, "ab", a); // slot 0 + hashtable_set(t4, "ba", b); // slot 1 + hashtable_set(t4, "cd", c); // slot 2 + + hashtable_remove(t4, "ab"); // tombstone at 0 + hashtable_remove(t4, "ba"); // tombstone at 1 + hashtable_remove(t4, "cd"); // tombstone at 2 + + // "dc" hashes to slot 0, probes past tombstones, lands on slot 3 (NULL) + hashtable_set(t4, "dc", d); + + // "ef" hashes to slot 0: all 4 slots are non-NULL (3 tombstones + "dc") + // Bug: hashtable_find_slot loops forever here + hashtable_set(t4, "ef", e); + + assert(t4->size == 2); + assert(hashtable_get(t4, "dc") == d); + assert(hashtable_get(t4, "ef") == e); + + alarm(0); + hashtable_destroy(t4); + printf("All tests completed.\n"); }