Util: Add Table iterators

This commit is contained in:
Vicki Pfau 2022-05-15 01:17:53 -07:00
parent 68c57df1f6
commit 4c1d44692c
4 changed files with 301 additions and 0 deletions

View File

@ -29,6 +29,11 @@ struct Table {
struct TableFunctions fn;
};
struct TableIterator {
size_t bucket;
size_t entry;
};
void TableInit(struct Table*, size_t initialSize, void (*deinitializer)(void*));
void TableDeinit(struct Table*);
@ -41,6 +46,12 @@ void TableClear(struct Table*);
void TableEnumerate(const struct Table*, void (*handler)(uint32_t key, void* value, void* user), void* user);
size_t TableSize(const struct Table*);
bool TableIteratorStart(const struct Table*, struct TableIterator*);
bool TableIteratorNext(const struct Table*, struct TableIterator*);
uint32_t TableIteratorGetKey(const struct Table*, const struct TableIterator*);
void* TableIteratorGetValue(const struct Table*, const struct TableIterator*);
bool TableIteratorLookup(const struct Table*, struct TableIterator*, uint32_t key);
void HashTableInit(struct Table* table, size_t initialSize, void (*deinitializer)(void*));
void HashTableInitCustom(struct Table* table, size_t initialSize, const struct TableFunctions* funcs);
void HashTableDeinit(struct Table* table);
@ -66,6 +77,17 @@ const char* HashTableSearchData(const struct Table* table, const void* value, si
const char* HashTableSearchString(const struct Table* table, const char* value);
size_t HashTableSize(const struct Table*);
bool HashTableIteratorStart(const struct Table*, struct TableIterator*);
bool HashTableIteratorNext(const struct Table*, struct TableIterator*);
const char* HashTableIteratorGetKey(const struct Table*, const struct TableIterator*);
const void* HashTableIteratorGetBinaryKey(const struct Table*, const struct TableIterator*);
size_t HashTableIteratorGetBinaryKeyLen(const struct Table*, const struct TableIterator*);
void* HashTableIteratorGetCustomKey(const struct Table*, const struct TableIterator*);
void* HashTableIteratorGetValue(const struct Table*, const struct TableIterator*);
bool HashTableIteratorLookup(const struct Table*, struct TableIterator*, const char* key);
bool HashTableIteratorLookupBinary(const struct Table*, struct TableIterator*, const void* key, size_t keylen);
bool HashTableIteratorLookupCustom(const struct Table*, struct TableIterator*, void* key);
CXX_GUARD_END
#endif

View File

@ -30,6 +30,7 @@ set(GUI_FILES
set(TEST_FILES
test/string-parser.c
test/string-utf8.c
test/table.c
test/text-codec.c
test/vfs.c)

View File

@ -217,6 +217,52 @@ size_t TableSize(const struct Table* table) {
return table->size;
}
bool TableIteratorStart(const struct Table* table, struct TableIterator* iter) {
iter->entry = 0;
for (iter->bucket = 0; iter->bucket < table->tableSize; ++iter->bucket) {
if (table->table[iter->bucket].nEntries) {
break;
}
}
return iter->bucket < table->tableSize;
}
bool TableIteratorNext(const struct Table* table, struct TableIterator* iter) {
if (iter->entry + 1 < table->table[iter->bucket].nEntries) {
++iter->entry;
return true;
}
if (iter->bucket + 1 < table->tableSize) {
iter->entry = 0;
for (++iter->bucket; iter->bucket < table->tableSize; ++iter->bucket) {
if (table->table[iter->bucket].nEntries) {
break;
}
}
return iter->bucket < table->tableSize;
}
return false;
}
uint32_t TableIteratorGetKey(const struct Table* table, const struct TableIterator* iter) {
return table->table[iter->bucket].list[iter->entry].key;
}
void* TableIteratorGetValue(const struct Table* table, const struct TableIterator* iter) {
return table->table[iter->bucket].list[iter->entry].value;
}
bool TableIteratorLookup(const struct Table* table, struct TableIterator* iter, uint32_t key) {
uint32_t bucket = key & (table->tableSize - 1);
const struct TableList* list = &table->table[bucket];
TABLE_LOOKUP_START(TABLE_COMPARATOR, list) {
iter->bucket = bucket;
iter->entry = i;
return true;
} TABLE_LOOKUP_END;
return false;
}
void HashTableInit(struct Table* table, size_t initialSize, void (*deinitializer)(void*)) {
TableInit(table, initialSize, deinitializer);
table->seed = 1;
@ -531,3 +577,77 @@ const char* HashTableSearchString(const struct Table* table, const char* value)
size_t HashTableSize(const struct Table* table) {
return table->size;
}
bool HashTableIteratorStart(const struct Table* table, struct TableIterator* iter) {
return TableIteratorStart(table, iter);
}
bool HashTableIteratorNext(const struct Table* table, struct TableIterator* iter) {
return TableIteratorNext(table, iter);
}
const char* HashTableIteratorGetKey(const struct Table* table, const struct TableIterator* iter) {
return table->table[iter->bucket].list[iter->entry].stringKey;
}
const void* HashTableIteratorGetBinaryKey(const struct Table* table, const struct TableIterator* iter) {
return table->table[iter->bucket].list[iter->entry].stringKey;
}
size_t HashTableIteratorGetBinaryKeyLen(const struct Table* table, const struct TableIterator* iter) {
return table->table[iter->bucket].list[iter->entry].keylen;
}
void* HashTableIteratorGetCustomKey(const struct Table* table, const struct TableIterator* iter) {
return (char*) table->table[iter->bucket].list[iter->entry].stringKey;
}
void* HashTableIteratorGetValue(const struct Table* table, const struct TableIterator* iter) {
return TableIteratorGetValue(table, iter);
}
bool HashTableIteratorLookup(const struct Table* table, struct TableIterator* iter, const char* key) {
uint32_t hash;
if (table->fn.hash) {
hash = table->fn.hash(key, strlen(key), table->seed);
} else {
hash = hash32(key, strlen(key), table->seed);
}
uint32_t bucket = hash & (table->tableSize - 1);
const struct TableList* list = &table->table[bucket];
TABLE_LOOKUP_START(HASH_TABLE_STRNCMP_COMPARATOR, list) {
iter->bucket = bucket;
iter->entry = i;
return true;
} TABLE_LOOKUP_END;
return false;
}
bool HashTableIteratorLookupBinary(const struct Table* table, struct TableIterator* iter, const void* key, size_t keylen) {
uint32_t hash;
if (table->fn.hash) {
hash = table->fn.hash(key, keylen, table->seed);
} else {
hash = hash32(key, keylen, table->seed);
}
uint32_t bucket = hash & (table->tableSize - 1);
const struct TableList* list = &table->table[bucket];
TABLE_LOOKUP_START(HASH_TABLE_MEMCMP_COMPARATOR, list) {
iter->bucket = bucket;
iter->entry = i;
return true;
} TABLE_LOOKUP_END;
return false;
}
bool HashTableIteratorLookupCustom(const struct Table* table, struct TableIterator* iter, void* key) {
uint32_t hash = table->fn.hash(key, 0, table->seed);
uint32_t bucket = hash & (table->tableSize - 1);
const struct TableList* list = &table->table[bucket];
TABLE_LOOKUP_START(HASH_TABLE_CUSTOM_COMPARATOR, list) {
iter->bucket = bucket;
iter->entry = i;
return true;
} TABLE_LOOKUP_END;
return false;
}

158
src/util/test/table.c Normal file
View File

@ -0,0 +1,158 @@
/* Copyright (c) 2013-2022 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 "util/test/suite.h"
#include <mgba-util/table.h>
M_TEST_DEFINE(basic) {
struct Table table;
TableInit(&table, 0, NULL);
size_t i;
for (i = 0; i < 5000; ++i) {
TableInsert(&table, i, (void*) i);
}
for (i = 0; i < 5000; ++i) {
assert_int_equal(i, (size_t) TableLookup(&table, i));
}
TableDeinit(&table);
}
M_TEST_DEFINE(iterator) {
struct Table table;
struct TableIterator iter;
TableInit(&table, 0, NULL);
assert_false(TableIteratorStart(&table, &iter));
size_t i;
for (i = 0; i < 32; ++i) {
TableInsert(&table, i, (void*) i);
}
assert_true(TableIteratorStart(&table, &iter));
uint32_t mask = 0;
while (true) {
assert_int_equal(TableIteratorGetKey(&table, &iter), (uintptr_t) TableIteratorGetValue(&table, &iter));
mask ^= 1 << TableIteratorGetKey(&table, &iter);
if (!TableIteratorNext(&table, &iter)) {
break;
}
}
assert_int_equal(mask, 0xFFFFFFFFU);
TableDeinit(&table);
}
M_TEST_DEFINE(iteratorLookup) {
struct Table table;
struct TableIterator iter;
TableInit(&table, 0, NULL);
size_t i;
for (i = 0; i < 500; ++i) {
TableInsert(&table, (i * 0x5DEECE66D) >> 16, (void*) i);
}
for (i = 0; i < 500; ++i) {
assert_true(TableIteratorLookup(&table, &iter, (i * 0x5DEECE66D) >> 16));
assert_int_equal(TableIteratorGetKey(&table, &iter), (i * 0x5DEECE66D) >> 16);
assert_int_equal((uintptr_t) TableIteratorGetValue(&table, &iter), i);
}
for (i = 1000; i < 1200; ++i) {
assert_false(TableIteratorLookup(&table, &iter, (i * 0x5DEECE66D) >> 16));
}
TableDeinit(&table);
}
M_TEST_DEFINE(hash) {
struct Table table;
HashTableInit(&table, 0, NULL);
size_t i;
for (i = 0; i < 5000; ++i) {
char buffer[16];
snprintf(buffer, sizeof(buffer), "%"PRIz"i", i);
HashTableInsert(&table, buffer, (void*) i);
}
for (i = 0; i < 5000; ++i) {
char buffer[16];
snprintf(buffer, sizeof(buffer), "%"PRIz"i", i);
assert_int_equal(i, (size_t) HashTableLookup(&table, buffer));
}
HashTableDeinit(&table);
}
M_TEST_DEFINE(hashIterator) {
struct Table table;
struct TableIterator iter;
char buf[18];
HashTableInit(&table, 0, NULL);
assert_false(HashTableIteratorStart(&table, &iter));
size_t i;
for (i = 0; i < 32; ++i) {
snprintf(buf, sizeof(buf), "%zu", i);
HashTableInsert(&table, buf, (void*) i);
}
assert_true(TableIteratorStart(&table, &iter));
uint32_t mask = 0;
while (true) {
assert_int_equal(atoi(HashTableIteratorGetKey(&table, &iter)), (uintptr_t) HashTableIteratorGetValue(&table, &iter));
mask ^= 1 << atoi(HashTableIteratorGetKey(&table, &iter));
if (!HashTableIteratorNext(&table, &iter)) {
break;
}
}
assert_int_equal(mask, 0xFFFFFFFFU);
HashTableDeinit(&table);
}
M_TEST_DEFINE(hashIteratorLookup) {
struct Table table;
struct TableIterator iter;
char buf[18];
HashTableInit(&table, 0, NULL);
size_t i;
for (i = 0; i < 500; ++i) {
snprintf(buf, sizeof(buf), "%zu", (i * 0x5DEECE66D) >> 4);
HashTableInsert(&table, buf, (void*) i);
}
for (i = 0; i < 500; ++i) {
snprintf(buf, sizeof(buf), "%zu", (i * 0x5DEECE66D) >> 4);
assert_true(HashTableIteratorLookup(&table, &iter, buf));
assert_string_equal(HashTableIteratorGetKey(&table, &iter), buf);
assert_int_equal((uintptr_t) HashTableIteratorGetValue(&table, &iter), i);
}
for (i = 1000; i < 1200; ++i) {
snprintf(buf, sizeof(buf), "%zu", (i * 0x5DEECE66D) >> 4);
assert_false(HashTableIteratorLookup(&table, &iter, buf));
}
HashTableDeinit(&table);
}
M_TEST_SUITE_DEFINE(Table,
cmocka_unit_test(basic),
cmocka_unit_test(iterator),
cmocka_unit_test(iteratorLookup),
cmocka_unit_test(hash),
cmocka_unit_test(hashIterator),
cmocka_unit_test(hashIteratorLookup),
)