#include "hash_table.h" #include #include #include static Hash_Item_t HT_DELETED_ITEM = {NULL, NULL}; static Hash_Item_t* ht_new_item(const char* k, const char* v) { Hash_Item_t* i = (Hash_Item_t*)malloc(sizeof(Hash_Item_t)); i->key = strdup(k); i->value = strdup(v); return i; } static int ht_hash(const char* s, const int a, const int m) { long hash = 0; const int len_s = strlen(s); for (int i = 0; i < len_s; i++) { hash += (long)pow(a, len_s - (i+1)) * s[i]; hash = hash % m; } return (int)hash; } static int ht_get_hash( const char* s, const int num_buckets, const int attempt ) { const int hash_a = ht_hash(s, HT_PRIME_1, num_buckets); const int hash_b = ht_hash(s, HT_PRIME_2, num_buckets); return (hash_a + (attempt * (hash_b + 1))) % num_buckets; } static void ht_del_item(Hash_Item_t* i) { free(i->key); free(i->value); free(i); } void ht_del_hash_table(Hash_Table_t* ht) { for (int i = 0; i < ht->size; i++) { Hash_Item_t* item = ht->items[i]; if (item != NULL) { ht_del_item(item); } } free(ht->items); free(ht); } int is_prime(const int x) { if (x < 2) { return -1; } if (x < 4) { return 1; } if ((x % 2) == 0) { return 0; } for (int i = 3; i <= floor(sqrt((double) x)); i += 2) { if ((x % i) == 0) { return 0; } } return 1; } int next_prime(int x) { while (is_prime(x) != 1) { x++; } return x; } static Hash_Table_t* ht_new_sized(const int base_size) { Hash_Table_t* ht = (Hash_Table_t*)malloc(sizeof(Hash_Table_t)); ht->base_size = base_size; ht->size = next_prime(ht->base_size); ht->count = 0; ht->items = (Hash_Item_t**) calloc((size_t)ht->size, sizeof(Hash_Item_t*)); return ht; } Hash_Table_t* ht_new() { return ht_new_sized(HT_INITIAL_BASE_SIZE); } static void ht_resize(Hash_Table_t* ht, const int base_size) { if (base_size < HT_INITIAL_BASE_SIZE) { return; } Hash_Table_t* new_ht = ht_new_sized(base_size); for (int i = 0; i < ht->size; i++) { Hash_Item_t* item = ht->items[i]; if (item != NULL && item != &HT_DELETED_ITEM) { ht_insert(new_ht, item->key, item->value); } } ht->base_size = new_ht->base_size; ht->count = new_ht->count; // To delete new_ht, we give it ht's size and items const int tmp_size = ht->size; ht->size = new_ht->size; new_ht->size = tmp_size; Hash_Item_t** tmp_items = ht->items; ht->items = new_ht->items; new_ht->items = tmp_items; ht_del_hash_table(new_ht); } static void ht_resize_up(Hash_Table_t* ht) { const int new_size = ht->base_size * 2; ht_resize(ht, new_size); } static void ht_resize_down(Hash_Table_t* ht) { const int new_size = ht->base_size / 2; ht_resize(ht, new_size); } void ht_insert(Hash_Table_t* ht, const char* key, const char* value) { const int load = ht->count * 100 / ht->size; if (load > 70) { ht_resize_up(ht); } Hash_Item_t* item = ht_new_item(key, value); int index = ht_get_hash(item->key, ht->size, 0); Hash_Item_t* cur_item = ht->items[index]; int i = 1; while (cur_item != NULL) { if (cur_item != &HT_DELETED_ITEM) { if (strcmp(cur_item->key, key) == 0) { ht_del_item(cur_item); ht->items[index] = item; return; } } index = ht_get_hash(item->key, ht->size, i); cur_item = ht->items[index]; i++; } ht->items[index] = item; ht->count++; } char* ht_search(Hash_Table_t* ht, const char* key) { int index = ht_get_hash(key, ht->size, 0); Hash_Item_t* item = ht->items[index]; int i = 1; while (item != NULL) { if (item != &HT_DELETED_ITEM) { if (strcmp(item->key, key) == 0) { return item->value; } } index = ht_get_hash(key, ht->size, i); item = ht->items[index]; i++; } return NULL; } void ht_delete(Hash_Table_t* ht, const char* key) { const int load = ht->count * 100 / ht->size; if (load < 10) { ht_resize_down(ht); } int index = ht_get_hash(key, ht->size, 0); Hash_Item_t* item = ht->items[index]; int i = 1; while (item != NULL) { if (item != &HT_DELETED_ITEM) { if (strcmp(item->key, key) == 0) { ht_del_item(item); ht->items[index] = &HT_DELETED_ITEM; } } index = ht_get_hash(key, ht->size, i); item = ht->items[index]; i++; } ht->count--; }