mgba/src/util/table.c

221 lines
6.2 KiB
C

/* Copyright (c) 2013-2014 Jeffrey Pfau
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "table.h"
#include "util/hash.h"
#include "util/string.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;
};
struct TableList {
struct TableTuple* list;
size_t nEntries;
size_t listSize;
};
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));
}
}
void TableDeinit(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) {
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(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) {
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(uint32_t 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].key, 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);
}
}
}