Util: Flesh out Table and add HashTable

This commit is contained in:
Jeffrey Pfau 2014-11-01 03:02:10 -07:00
parent a77d6e202d
commit e66bcfb818
2 changed files with 196 additions and 20 deletions

View File

@ -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);
}
}
}

View File

@ -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