/* * hashtable.c * * Created on: 24.10.2017 * Author: julian */ #include "strhash.h" #include "hashtable.h" #include #include #include #include #include int key_compare(entry_key_t one, entry_key_t two) { return strcmp(one, two) == 0; } entry_hash_t key_hash(entry_key_t key) { return strhash_str(key) + 1; // make hash always nonzero } void hashtable_init(struct hashtable * table) { memset(table, 0, sizeof(table)); } hashtable_iterator_t hashtable_end(struct hashtable * table) { return table->data + table->len; } hashtable_iterator_t hashtable_next(struct hashtable * table, hashtable_iterator_t current) { if (table == NULL) { return NULL; } if (current == NULL) { current = table->data; } else { // we want the NEXT pointer :) current++; } while(current != hashtable_end(table)) { if (current->hash != 0) break; current++; } return current; } hashtable_iterator_t hashtable_get_hash(struct hashtable * table, entry_hash_t hash) { if (!table->len) { return hashtable_end(table); } size_t index = hash % table->len; if (table->data[index].hash == 0) { return hashtable_end(table); } return table->data + index; } hashtable_iterator_t hashtable_get(struct hashtable * table, entry_key_t key) { entry_hash_t hash = key_hash(key); return hashtable_get_hash(table, hash); } void hashtable_clear(struct hashtable * table) { if (table->dealloc_data) { hashtable_iterator_t it = hashtable_next(table, NULL); for(;it != hashtable_end(table); it = hashtable_next(table, it)){ table->dealloc_data(it->data); } } free(table->data); table->data = NULL; table->count = table->len = 0; } int hashtable_resize(struct hashtable * table, size_t newsize) { if (table == NULL) { return 0; } if (newsize == 0) { // will fail, correct to 1 newsize = 1; } struct hashtable temp = *table; table->data = calloc(newsize, sizeof(struct entry)); if (table->data == NULL) { table->data = temp.data; errno = ENOMEM; return -1; } // try to reinsert old data table->len = newsize; for (size_t i = 0; i < temp.len; i++) { if (temp.data[i].hash && hashtable_add(table, temp.data[i]) == -1) { // abort mission, restore old table free(table->data); *table = temp; return -2; } } // delete old table free(temp.data); return 0; } int hashtable_add(struct hashtable * table, struct entry entry) { if (table == NULL) { return 0; } if (table->len == 0) { // initial alloc int err = hashtable_resize(table, 1); if (err < 0) { return err; } return hashtable_add(table, entry); } // try to insert into table size_t index = entry.hash % table->len; if (table->data->hash && !key_compare(table->data[index].key, entry.key)) { // key collision // make table bigger if (hashtable_resize(table, table->len*2) != 0) { return -1; } return hashtable_add(table, entry); } // insert new entry table->data[index] = entry; table->count++; return 0; } struct entry hashtable_make_entry(entry_key_t key, void * data) { return (struct entry){ .data = data, .key = key, .hash = key_hash(key), }; } int hashtable_remove(struct hashtable * table, hashtable_iterator_t it) { if (!it || it->hash == 0) { return 0; } if (table->dealloc_data) { table->dealloc_data(it->data); } it->data = NULL; it->hash = 0; table->count--; return 1; }