#include #include #include "sdbm.h" #include "hashtable.h" bool streq(char* str1, size_t len1, char* str2, size_t len2) { if (len1 == 0 && len2 == 0) { return !strcmp(str1, str2); } else if (len1 == len2) { return !memcmp(str1, str2, len1); } else if (len1 == 0) { return !memcmp(str1, str2, len2); } else if (len2 == 0) { return !memcmp(str1, str2, len1); } return true; } bool hashtable_recreate(hashtable* table) { size_t oldcapacity = table->capacity; hashtable_item* olditems = table->items; hashtable_item* newitems = calloc(16, sizeof(hashtable_item)); if (newitems == NULL) return false; hashtable temptable; temptable.capacity = 16; temptable.filled = 0; temptable.items = newitems; for (size_t i = 0; i < oldcapacity; i++) { if (olditems[i].name == NULL || olditems[i].name == (void*)1) continue; size_t_optional index = hashtable_insert(&temptable, olditems[i].name, olditems[i].size); if (!index.exists) { hashtable_item* array = temptable.items; for (size_t i = 0; i < temptable.capacity; i++) free(array[i].name); free(newitems); return false; } newitems[index.sizet].data = olditems[i].data; } table->capacity = temptable.capacity; table->filled = temptable.filled; table->items = newitems; return true; } hashtable* hashtable_create(void) { hashtable* table = malloc(sizeof(hashtable)); if (table == NULL) return NULL; table->capacity = 16; table->items = calloc(16, sizeof(hashtable_item)); return table; } size_t_optional hashtable_key(hashtable* table, char* name, size_t size) { size_t index; if (size) index = sdbm_clean(name, size) & (table->capacity - 1); else index = sdbm(name) & (table->capacity - 1); hashtable_item* array = table->items; while (1) { if (array[index].name == NULL) return (size_t_optional) { false, 0 }; if (array[index].name != (void*)(1)) if (streq(name, size, array[index].name, array[index].size)) return (size_t_optional) { true, index }; index = (index + 1) & (table->capacity - 1); } } size_t_optional hashtable_insert(hashtable* table, char* name, size_t size) { size_t index; if (size) index = sdbm_clean(name, size) & (table->capacity - 1); else index = sdbm(name) & (table->capacity - 1); hashtable_item* array = table->items; if ((table->capacity >> 2) * 3 < table->filled) { hashtable_recreate(table); } while (1) { if (array[index].name == NULL || array[index].name == (void*)1) { array[index].name = malloc(strlen(name) + sizeof(name)); array[index].size = size; if (size) memcpy(array[index].name, name, size); else strcpy(array[index].name, name); table->filled++; return (size_t_optional) {true, index}; } else if (streq(array[index].name, array[index].size, name, size)) return (size_t_optional) {true, index}; else index = (index + 1) & (table->capacity - 1); } } void hashtable_destroy(hashtable* table) { hashtable_item* array = table->items; for (size_t i = 0; i < table->capacity; i++) free(array[i].name); free(table); } void* hashtable_get(hashtable* table, char* name, size_t size) { size_t_optional index = hashtable_key(table, name, size); if (!index.exists) return NULL; return table->items[index.sizet].data; } void hashtable_set(hashtable* table, char* name, size_t size, void* data) { size_t_optional index = hashtable_insert(table, name, size); if (index.exists) table->items[index.sizet].data = data; } bool hashtable_delete(hashtable* table, char* name, size_t size) { size_t_optional index = hashtable_key(table, name, size); if (index.exists) { table->items[index.sizet].name = (void*)1; table->filled--; return true; } else return false; } void hashtable_forall(hashtable* table, void (*callback)(char*, size_t, void*, void*), void* userdata) { size_t capacity = table->capacity; hashtable_item* items = table->items; for (size_t i = 0; i < capacity; i++) callback(items[i].name, items[i].size, items[i].data, userdata); }