mirror of https://github.com/mgba-emu/mgba.git
Util: Add Table iterators
This commit is contained in:
parent
68c57df1f6
commit
4c1d44692c
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
120
src/util/table.c
120
src/util/table.c
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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),
|
||||
)
|
Loading…
Reference in New Issue