184 lines
4.6 KiB
C++
184 lines
4.6 KiB
C++
#include "hash_table.h"
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <math.h>
|
|
|
|
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--;
|
|
}
|