#include "hashtable.h" char *dup_string(const char *text) { if (text == nullptr) return nullptr; size_t len = strlen(text) + 1; char *out = malloc(len); if (out == nullptr) return nullptr; memcpy(out, text, len); return out; } struct Node *create_node(const char *text) { if (text == nullptr) return nullptr; struct Node *n = malloc(sizeof(struct Node)); if (n == nullptr) { fprintf(stderr, "Error on allocation..\n"); return nullptr; } n->text = dup_string(text); if (n->text == nullptr) { fprintf(stderr, "Could not allocate text buffer\n"); free(n); return nullptr; } n->next = n->prev = nullptr; n->value = nullptr; n->cleanup = nullptr; n->bytes = 0; return n; } struct Node *insert_node(struct Node **root, const char *text) { if (root == nullptr || text == nullptr) return nullptr; struct Node *n = create_node(text); if (n == nullptr) return nullptr; n->next = *root; if (*root != nullptr) { (*root)->prev = n; } *root = n; return n; } void print_node(const struct Node *root) { if (root == nullptr) return; const struct Node *temp = root; while (temp != nullptr) { printf("\t[%s]\n", temp->text); temp = temp->next; } } int node_cmp(const char *a, const char *b) { return strcmp(a, b); } void sort_node(struct Node *root) { if (root == nullptr) return; bool swapped; struct Node *ptr_1 = nullptr, *l_ptr = nullptr; do { swapped = false; ptr_1 = root; while (ptr_1->next != l_ptr) { if (node_cmp(ptr_1->text, ptr_1->next->text) > 0) { void *temp_data = ptr_1->value; ptr_1->value = ptr_1->next->value; ptr_1->next->value = temp_data; char *temp_sz = ptr_1->text; ptr_1->text = ptr_1->next->text; ptr_1->next->text = temp_sz; void (*temp_cleanup)(void *) = ptr_1->cleanup; ptr_1->cleanup = ptr_1->next->cleanup; ptr_1->next->cleanup = temp_cleanup; size_t temp_bytes = ptr_1->bytes; ptr_1->bytes = ptr_1->next->bytes; ptr_1->next->bytes = temp_bytes; swapped = true; } ptr_1 = ptr_1->next; } l_ptr = ptr_1; } while (swapped); } void release_node(struct Node *root) { while (root != nullptr) { struct Node *n = root->next; free(root->text); if (root->cleanup != nullptr && root->value != nullptr) root->cleanup(root->value); free(root); root = n; } } struct Node *find_node(struct Node *root, const char *text) { if (root == nullptr || text == nullptr) return nullptr; struct Node *n = root; while (n != nullptr) { if (strcmp(n->text, text) == 0) return n; n = n->next; } return nullptr; } size_t hash(const char *key, size_t bucket_size) { if (key == nullptr || bucket_size == 0) return 0; size_t h = 1469598103934665603ull; while (*key != '\0') { h ^= (unsigned char)*key; h *= 1099511628211ull; ++key; } return h % bucket_size; } struct Node *hash_insert(struct HashTable *table, const char *text) { if (table == nullptr || text == nullptr || table->buckets == nullptr || table->bucket_size == 0) return nullptr; size_t key = hash(text, table->bucket_size); struct Node *existing = find_node(table->buckets[key], text); if (existing != nullptr) return existing; return insert_node(&table->buckets[key], text); } struct Node *hash_lookup(const struct HashTable *table, const char *text) { if (table == nullptr || text == nullptr || table->buckets == nullptr || table->bucket_size == 0) return nullptr; size_t key = hash(text, table->bucket_size); return find_node(table->buckets[key], text); } bool hash_init(struct HashTable *table, size_t bucket_size) { if (table == nullptr || bucket_size == 0) return false; memset(table, 0, sizeof(*table)); table->buckets = calloc(bucket_size, sizeof(*table->buckets)); if (table->buckets == nullptr) return false; table->bucket_size = bucket_size; return true; } void hash_cleanup(struct HashTable *table) { if (table == nullptr || table->buckets == nullptr || table->bucket_size == 0) return; for (size_t i = 0; i < table->bucket_size; ++i) { release_node(table->buckets[i]); table->buckets[i] = nullptr; } free(table->buckets); table->buckets = nullptr; table->bucket_size = 0; } void hash_print(const struct HashTable *table) { if (table == nullptr || table->buckets == nullptr || table->bucket_size == 0) return; printf("Keys: {\n"); for (size_t i = 0; i < table->bucket_size; ++i) { print_node(table->buckets[i]); } printf("}\n"); } void cleanup_ptr(void *ptr) { if (ptr != nullptr) { free(ptr); } } struct Node *hash_set(struct HashTable *table, const char *text, void *value, size_t bytes, void (*cleanup)(void *)) { if (table == nullptr || text == nullptr || table->buckets == nullptr || table->bucket_size == 0) return nullptr; struct Node *n = hash_insert(table, text); if (n == nullptr) { printf("Error on insert into Hash Table.\n"); return nullptr; } if (n->cleanup != nullptr && n->value != nullptr) { n->cleanup(n->value); } n->value = value; n->cleanup = cleanup; n->bytes = bytes; return n; } void hash_remove(struct HashTable *table, const char *text) { if (table == nullptr || text == nullptr || table->buckets == nullptr || table->bucket_size == 0) return; size_t key = hash(text, table->bucket_size); struct Node *n = table->buckets[key]; while (n != nullptr) { if (strcmp(n->text, text) == 0) { if (n->prev) n->prev->next = n->next; else table->buckets[key] = n->next; if (n->next) n->next->prev = n->prev; free(n->text); if (n->cleanup && n->value) n->cleanup(n->value); free(n); return; } n = n->next; } } enum HASH_VALUE_RETURN hash_set_value(struct HashTable *table, const char *key, bool (*hash_setvalue)(struct Node *n), void (*cleanup)(void *)) { if (table == nullptr || table->buckets == nullptr || table->bucket_size == 0 || key == nullptr || hash_setvalue == nullptr) return VALUE_ERROR; struct Node *n = hash_lookup(table, key); if (n == nullptr) { struct Node *inserted = hash_insert(table, key); if (inserted != nullptr) { inserted->cleanup = cleanup; if (hash_setvalue(inserted)) return VALUE_NEW; } } else { n->cleanup = cleanup; if (hash_setvalue(n)) return VALUE_UPDATE; } return VALUE_ERROR; } struct Node *hash_flat_list(const struct HashTable *table) { if (table == nullptr || table->buckets == nullptr || table->bucket_size == 0) return nullptr; struct Node *flat = nullptr; for (size_t i = 0; i < table->bucket_size; ++i) { struct Node *root = table->buckets[i]; for (struct Node *n = root; n != nullptr; n = n->next) { struct Node *new_node = insert_node(&flat, n->text); if (new_node == nullptr) { fprintf(stderr, "Error, out of memory.\n"); release_node(flat); return nullptr; } } } return flat; } size_t hash_count(struct HashTable *table) { size_t count = 0; for (size_t i = 0; i < table->bucket_size; ++i) { struct Node *root = table->buckets[i]; while (root != nullptr) { count++; root = root->next; } } return count; } bool hash_merge(struct HashTable *to, const struct HashTable *from) { if (to == nullptr || from == nullptr || to->buckets == nullptr || from->buckets == nullptr || to->bucket_size == 0 || from->bucket_size == 0) return false; for (size_t i = 0; i < from->bucket_size; ++i) { struct Node *root = from->buckets[i]; while (root != nullptr) { const char *key = root->text; struct Node *n = hash_lookup(to, key); if (n == nullptr) { struct Node *ivalue = hash_insert(to, key); if (ivalue == nullptr) return false; if (root->bytes == 0 || root->value == nullptr) { root = root->next; continue; } ivalue->value = malloc(root->bytes); if (ivalue->value == nullptr) return false; memcpy(ivalue->value, root->value, root->bytes); ivalue->bytes = root->bytes; ivalue->cleanup = root->cleanup; } root = root->next; } } return true; } bool hash_clone_merge(struct HashTable *global, const struct HashTable *file_table) { if (global == nullptr || file_table == nullptr || global->buckets == nullptr || file_table->buckets == nullptr || global->bucket_size == 0 || file_table->bucket_size == 0) return false; struct HashTable temp; if (!hash_init(&temp, global->bucket_size)) return false; if (!hash_merge(&temp, global)) { hash_cleanup(&temp); return false; } if (!hash_merge(&temp, file_table)) { hash_cleanup(&temp); return false; } hash_cleanup(global); *global = temp; return true; }