186 lines
3.4 KiB
C
186 lines
3.4 KiB
C
/*
|
|
* hashtable.c
|
|
*
|
|
* Created on: 24.10.2017
|
|
* Author: julian
|
|
*/
|
|
|
|
#include "strhash.h"
|
|
#include "hashtable.h"
|
|
|
|
#include <stddef.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include <errno.h>
|
|
#include <assert.h>
|
|
|
|
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;
|
|
}
|
|
|
|
|