mirror of https://github.com/mgba-emu/mgba.git
Util: Flesh out Table and add HashTable
This commit is contained in:
parent
a77d6e202d
commit
e66bcfb818
188
src/util/table.c
188
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue