diff --git a/src/util/table.c b/src/util/table.c index 1ce8dfe11..848ed7182 100644 --- a/src/util/table.c +++ b/src/util/table.c @@ -1,9 +1,31 @@ #include "table.h" +#include "util/hash.h" + #define LIST_INITIAL_SIZE 8 +#define TABLE_INITIAL_SIZE 8 + +#define TABLE_COMPARATOR(LIST, INDEX) LIST->list[(INDEX)].key == key +#define HASH_TABLE_COMPARATOR(LIST, INDEX) LIST->list[(INDEX)].key == hash && strncmp(LIST->list[(INDEX)].stringKey, key, LIST->list[(INDEX)].keylen) == 0 + +#define TABLE_LOOKUP_START(COMPARATOR, LIST, KEY) \ + uint32_t entry = (KEY) & (table->tableSize - 1); \ + LIST = &table->table[entry]; \ + size_t i; \ + for (i = 0; i < LIST->nEntries; ++i) { \ + if (COMPARATOR(LIST, i)) { \ + struct TableTuple* lookupResult = &LIST->list[i]; \ + UNUSED(lookupResult); + +#define TABLE_LOOKUP_END \ + break; \ + } \ + } struct TableTuple { uint32_t key; + char* stringKey; + size_t keylen; void* value; }; @@ -13,13 +35,40 @@ struct TableList { size_t listSize; }; -void TableInit(struct Table* table, size_t initialSize) { +static struct TableList* _resizeAsNeeded(struct Table* table, struct TableList* list, uint32_t key) { + UNUSED(table); + UNUSED(key); + // TODO: Expand table if needed + if (list->nEntries + 1 == list->listSize) { + list->listSize *= 2; + list->list = realloc(list->list, list->listSize * sizeof(struct TableTuple)); + } + return list; +} + +static void _removeItemFromList(struct Table* table, struct TableList* list, size_t item) { + --list->nEntries; + free(list->list[item].stringKey); + if (table->deinitializer) { + table->deinitializer(list->list[item].value); + } + if (item != list->nEntries) { + list->list[item] = list->list[list->nEntries]; + } +} + +void TableInit(struct Table* table, size_t initialSize, void (deinitializer(void*))) { + if (initialSize < 2 || (initialSize & (initialSize - 1))) { + initialSize = TABLE_INITIAL_SIZE; + } table->tableSize = initialSize; table->table = calloc(table->tableSize, sizeof(struct TableList)); + table->deinitializer = deinitializer; size_t i; for (i = 0; i < table->tableSize; ++i) { table->table[i].listSize = LIST_INITIAL_SIZE; + table->table[i].nEntries = 0; table->table[i].list = calloc(LIST_INITIAL_SIZE, sizeof(struct TableTuple)); } } @@ -27,34 +76,139 @@ void TableInit(struct Table* table, size_t initialSize) { void TableDeinit(struct Table* table) { size_t i; for (i = 0; i < table->tableSize; ++i) { - // TODO: Don't leak entries - free(table->table[i].list); + struct TableList* list = &table->table[i]; + size_t j; + for (j = 0; j < list->nEntries; ++j) { + free(list->list[j].stringKey); + if (table->deinitializer) { + table->deinitializer(list->list[j].value); + } + } + free(list->list); } free(table->table); table->table = 0; table->tableSize = 0; } -void* TableLookup(struct Table* table, uint32_t key) { - uint32_t entry = key & (table->tableSize - 1); - struct TableList* list = &table->table[entry]; - size_t i; - for (i = 0; i < list->nEntries; ++i) { - if (list->list[i].key == key) { - return list->list[i].value; - } - } +void* TableLookup(const struct Table* table, uint32_t key) { + const struct TableList* list; + TABLE_LOOKUP_START(TABLE_COMPARATOR, list, key) { + return lookupResult->value; + } TABLE_LOOKUP_END; return 0; } void TableInsert(struct Table* table, uint32_t key, void* value) { - uint32_t entry = key & (table->tableSize - 1); - struct TableList* list = &table->table[entry]; - if (list->nEntries + 1 == list->listSize) { - list->listSize *= 2; - list->list = realloc(list->list, list->listSize * sizeof(struct TableTuple)); - } + struct TableList* list; + TABLE_LOOKUP_START(TABLE_COMPARATOR, list, key) { + if (value != lookupResult->value) { + table->deinitializer(lookupResult->value); + lookupResult->value = value; + } + return; + } TABLE_LOOKUP_END; + list = _resizeAsNeeded(table, list, key); list->list[list->nEntries].key = key; + list->list[list->nEntries].stringKey = 0; list->list[list->nEntries].value = value; ++list->nEntries; } + +void TableRemove(struct Table* table, uint32_t key) { + struct TableList* list; + TABLE_LOOKUP_START(TABLE_COMPARATOR, list, key) { + _removeItemFromList(table, list, i); // TODO: Move i out of the macro + } TABLE_LOOKUP_END; +} + +void TableClear(struct Table* table) { + size_t i; + for (i = 0; i < table->tableSize; ++i) { + struct TableList* list = &table->table[i]; + if (table->deinitializer) { + size_t j; + for (j = 0; j < list->nEntries; ++j) { + table->deinitializer(list->list[j].value); + } + } + free(list->list); + list->listSize = LIST_INITIAL_SIZE; + list->nEntries = 0; + list->list = calloc(LIST_INITIAL_SIZE, sizeof(struct TableTuple)); + } +} + +void TableEnumerate(const struct Table* table, void (handler(void* value, void* user)), void* user) { + size_t i; + for (i = 0; i < table->tableSize; ++i) { + const struct TableList* list = &table->table[i]; + size_t j; + for (j = 0; j < list->nEntries; ++j) { + handler(list->list[j].value, user); + } + } +} + +void* HashTableLookup(const struct Table* table, const char* key) { + uint32_t hash = hash32(key, strlen(key), 0); + const struct TableList* list; + TABLE_LOOKUP_START(HASH_TABLE_COMPARATOR, list, hash) { + return lookupResult->value; + } TABLE_LOOKUP_END; + return 0; +} + +void HashTableInsert(struct Table* table, const char* key, void* value) { + uint32_t hash = hash32(key, strlen(key), 0); + struct TableList* list; + TABLE_LOOKUP_START(HASH_TABLE_COMPARATOR, list, hash) { + if (value != lookupResult->value) { + table->deinitializer(lookupResult->value); + lookupResult->value = value; + } + return; + } TABLE_LOOKUP_END; + list->list[list->nEntries].key = hash; + list->list[list->nEntries].stringKey = strdup(key); + list->list[list->nEntries].keylen = strlen(key); + list->list[list->nEntries].value = value; + ++list->nEntries; +} + +void HashTableRemove(struct Table* table, const char* key) { + uint32_t hash = hash32(key, strlen(key), 0); + struct TableList* list; + TABLE_LOOKUP_START(HASH_TABLE_COMPARATOR, list, hash) { + _removeItemFromList(table, list, i); // TODO: Move i out of the macro + } TABLE_LOOKUP_END; +} + +void HashTableClear(struct Table* table) { + size_t i; + for (i = 0; i < table->tableSize; ++i) { + struct TableList* list = &table->table[i]; + size_t j; + for (j = 0; j < list->nEntries; ++j) { + if (table->deinitializer) { + table->deinitializer(list->list[j].value); + } + free(list->list[j].stringKey); + } + free(list->list); + list->listSize = LIST_INITIAL_SIZE; + list->nEntries = 0; + list->list = calloc(LIST_INITIAL_SIZE, sizeof(struct TableTuple)); + } +} + +void HashTableEnumerate(const struct Table* table, void (handler(const char* key, void* value, void* user)), void* user) { + size_t i; + for (i = 0; i < table->tableSize; ++i) { + const struct TableList* list = &table->table[i]; + size_t j; + for (j = 0; j < list->nEntries; ++j) { + handler(list->list[j].stringKey, list->list[j].value, user); + } + } +} diff --git a/src/util/table.h b/src/util/table.h index d769efdea..3b6b85f97 100644 --- a/src/util/table.h +++ b/src/util/table.h @@ -8,12 +8,34 @@ struct TableList; struct Table { struct TableList* table; size_t tableSize; + void (*deinitializer)(void*); }; -void TableInit(struct Table*, size_t initialSize); +void TableInit(struct Table*, size_t initialSize, void (deinitializer(void*))); void TableDeinit(struct Table*); -void* TableLookup(struct Table*, uint32_t key); +void* TableLookup(const struct Table*, uint32_t key); void TableInsert(struct Table*, uint32_t key, void* value); +void TableRemove(struct Table*, uint32_t key); +void TableClear(struct Table*); + +void TableEnumerate(const struct Table*, void (handler(void* value, void* user)), void* user); + +static inline void HashTableInit(struct Table* table, size_t initialSize, void (deinitializer(void*))) { + TableInit(table, initialSize, deinitializer); +} + +static inline void HashTableDeinit(struct Table* table) { + TableDeinit(table); +} + +void* HashTableLookup(const struct Table*, const char* key); +void HashTableInsert(struct Table*, const char* key, void* value); + +void HashTableRemove(struct Table*, const char* key); +void HashTableClear(struct Table*); + +void HashTableEnumerate(const struct Table*, void (handler(const char* key, void* value, void* user)), void* user); + #endif