Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 16 additions & 6 deletions hashtable.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

/**
Expand All @@ -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;
Expand All @@ -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 {
Expand All @@ -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--;
Expand Down
32 changes: 32 additions & 0 deletions tests.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#include <stdio.h>
#include <assert.h>
#include <signal.h>
#include <unistd.h>
#include "arraylist.h"
#include "hashtable.h"
/**
Expand Down Expand Up @@ -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");
}