// implementation of a hashtable #include #include #include #include "hashtable.h" // entry in the hashtable struct entry { const char *key; void *value; struct entry *next; }; struct htobj { int size; struct entry **table; }; struct item { struct entry *prev, *found; int index; }; static unsigned int hash(const char *key) { unsigned int hash = 0; while (*key) { hash = (hash << 5) - hash + *key++; } return hash; } hashtable hashtable_new(int size) { // Allocate memory for the hashtable hashtable h = malloc(sizeof(struct htobj)); // Set the size of the hashtable h->size = size; // Allocate memory for the entry pointers h->table = calloc(size, sizeof(struct entry *)); // Return the newly created hashtable return h; } struct item hashtable_lookup_item(hashtable h, const char *key) { struct item result = { .found = NULL, .index = -1, }; int index = hash(key) % h->size; struct entry *e = h->table[index]; struct entry *prev = NULL; while (e != NULL) { if (strcmp(e->key, key) == 0) { result.index = index; result.found = e; result.prev = prev; return result; } prev = e; e = e->next; } return result; // empty in this case } void *hashtable_set(hashtable h, const char *key, void *value) { struct item item = hashtable_lookup_item(h, key); // not found - insert new if (item.found == NULL) { int index = hash(key) % h->size; // Create a new entry struct entry *e = malloc(sizeof(struct entry)); e->key = strdup(key); // key is the copy e->value = value; e->next = NULL; // Insert the entry at the beginning of the linked list e->next = h->table[index]; h->table[index] = e; return NULL; } // found replace the value void *old_value = item.found->value; item.found-> value = value; return old_value; } void *hashtable_lookup(hashtable h, const char *key) { struct item item = hashtable_lookup_item(h, key); if (item.found != NULL) { return item.found->value; } return NULL; } void *hashtable_remove(hashtable h, const char *key) { struct item item = hashtable_lookup_item(h, key); if (item.found == NULL) { return NULL; } // found, so detete it if (item.prev != NULL) { item.prev->next = item.found->next; } else { h->table[item.index] = item.found->next; } // free unused memory void *value = item.found->value; // remember value free((void*) item.found->key); // free key free((void*) item.found); // free entry return value; } void hashtable_destroy_bucket(hashtable h, int i) { struct entry *e = h->table[i]; while (e != NULL) { free((void*)e->key); e = e->next; free(e); } } void hashtable_destroy(hashtable h) { if (h==NULL) return; for (int i = 0; i < h->size; i++) { hashtable_destroy_bucket(h, i); } free (h->table); free(h); } ht_iterator hashtable_iter(hashtable h) { ht_iterator iter = { .h = h, .index = -1, .entry = NULL, }; return iter; } bool ht_iter_next(ht_iterator *iter) { if (iter == NULL) { return false; } hashtable h = iter->h; while(true) { if (iter->index >= h->size) { return false; } if (iter->entry == NULL) { iter->index++; iter->entry = h->table[iter->index]; } else { iter->entry = iter->entry->next; } if (iter->entry != NULL) return true; } } const char *ht_iter_key(ht_iterator *iter) { if (iter == NULL || iter->entry == NULL) return NULL; return iter->entry->key; } void *ht_iter_value(ht_iterator *iter) { if (iter == NULL || iter->entry == NULL) return NULL; return iter->entry->value; }