Allow unordered iteration over hash table

This commit is contained in:
Doyle T 2018-07-24 00:05:43 +10:00
parent c876f2af0b
commit fc11e9ac0e
2 changed files with 109 additions and 26 deletions

View File

@ -6,22 +6,50 @@ void DqnVHashTable_Test()
int x;
};
using Height = u32;
Block block = {};
DqnVHashTable<Height, Block> table = {};
table.Set(12, block);
{
Block block = {};
Block *getResult = table.Get(12);
Block *operatorResult = table[12];
DQN_ASSERT(operatorResult == getResult);
DQN_ASSERT(operatorResult != nullptr);
Log(Status::Ok, "Set and get element using key");
DqnVHashTable<Height, Block> table = {};
DQN_DEFER(table.Free());
table.Set(12, block);
table.Erase(12);
getResult = table.Get(12);
operatorResult = table[12];
Block *getResult = table.Get(12);
Block *operatorResult = table[12];
DQN_ASSERT(operatorResult == getResult);
DQN_ASSERT(operatorResult != nullptr);
Log(Status::Ok, "Set and get element using key");
DQN_ASSERT(operatorResult == getResult);
DQN_ASSERT(operatorResult == nullptr);
Log(Status::Ok, "Erase element using key");
table.Erase(12);
getResult = table.Get(12);
operatorResult = table[12];
DQN_ASSERT(operatorResult == getResult);
DQN_ASSERT(operatorResult == nullptr);
Log(Status::Ok, "Erase element using key");
}
{
Block blocks[] = {{0}, {1}, {2}, {3}, {4}};
bool blockSeen[DQN_ARRAY_COUNT(blocks)] = {};
DqnVHashTable<Height, Block> table = {};
DQN_DEFER(table.Free());
table.Set(1, blocks[0]);
table.Set(2, blocks[1]);
table.Set(3, blocks[2]);
table.Set(4, blocks[3]);
table.Set(5, blocks[4]);
isize blocksSeen = 0;
for (Block const &block : table)
{
DQN_ASSERT(blockSeen[block.x] == false);
blockSeen[block.x] = true;
blocksSeen++;
}
DQN_ASSERT(blocksSeen == DQN_ARRAY_COUNT(blockSeen));
}
}

79
dqn.h
View File

@ -2681,21 +2681,50 @@ DQN_VHASH_TABLE_TEMPLATE struct DqnVHashTable
Bucket *buckets;
isize numBuckets;
isize bucketsUsed;
isize *indexesOfUsedBuckets;
isize indexInIndexesOfUsedBuckets;
DqnVHashTable() = default;
DqnVHashTable(isize size) { LazyInit(size); }
~DqnVHashTable() { if (buckets) DqnOS_VFree(buckets, sizeof(buckets) * numBuckets); }
DqnVHashTable() = default;
DqnVHashTable(isize size) { LazyInit(size); }
void LazyInit(isize size) { *this = {}; numBuckets = size; buckets = static_cast<Bucket *>(DqnOS_VAlloc(numBuckets * sizeof(*buckets))); DQN_ASSERT(buckets); }
void Free () { if (buckets) DqnOS_VFree(buckets, sizeof(buckets) * numBuckets); *this = {}; }
void LazyInit(isize size);
void Erase (Key const &key);
Item *Set (Key const &key, Item const &item);
Item *Get (Key const &key);
Item *operator[](Key const &key) { return Get(key); }
Item *operator[](Key const &key) { return Get(key); }
struct Iterator
{
DqnVHashTable *table;
isize indexInIndexesOfUsedBuckets;
isize indexInBucket;
Iterator(DqnVHashTable *table_, isize indexInIndexesOfUsedBuckets_ = 0, isize indexInBucket_ = 0) : table(table_), indexInIndexesOfUsedBuckets(indexInIndexesOfUsedBuckets_), indexInBucket(indexInBucket_) {}
Bucket *GetCurrBucket() const { return (table->buckets + table->indexesOfUsedBuckets[indexInIndexesOfUsedBuckets]); }
Entry *GetCurrEntry() const { return GetCurrBucket()->entries + indexInBucket; }
Item *GetCurrItem () const { return &(GetCurrEntry()->item); }
bool operator!=(Iterator const &other) const { return !Equals(GetCurrEntry()->key, other.GetCurrEntry()->key); }
Item &operator* () const { return *GetCurrItem(); }
Iterator operator++() { if (++indexInBucket >= GetCurrBucket()->entryIndex) { indexInBucket = 0; indexInIndexesOfUsedBuckets++; } return *this; }
};
Iterator begin() { return Iterator(this); }
Iterator end() { isize lastBucketIndex = indexesOfUsedBuckets[indexInIndexesOfUsedBuckets - 1]; isize lastIndexInBucket = DQN_MAX((buckets + lastBucketIndex)->entryIndex - 1, 0); return Iterator(this, DQN_MAX(lastBucketIndex, 0), DQN_MAX(lastIndexInBucket, 0)); }
};
DQN_VHASH_TABLE_TEMPLATE void DQN_VHASH_TABLE_DECL::LazyInit(isize size)
{
*this = {};
this->numBuckets = size;
this->buckets = static_cast<Bucket *>(DqnOS_VAlloc(size * sizeof(*this->buckets)));
this->indexesOfUsedBuckets = static_cast<isize *> (DqnOS_VAlloc(size * sizeof(*this->indexesOfUsedBuckets)));
DQN_ASSERT(this->buckets && this->indexesOfUsedBuckets);
}
DQN_VHASH_TABLE_TEMPLATE Item *DQN_VHASH_TABLE_DECL::Set(Key const &key, Item const &item)
{
if (!buckets) LazyInit(1024);
@ -2713,8 +2742,10 @@ DQN_VHASH_TABLE_TEMPLATE Item *DQN_VHASH_TABLE_DECL::Set(Key const &key, Item co
if (!entry)
{
DQN_ALWAYS_ASSERTM(bucket->entryIndex < DQN_ARRAY_COUNT(bucket->entries), "More than %zu collisions in hash table, increase the size of the table or buckets", DQN_ARRAY_COUNT(bucket->entries));
this->bucketsUsed = (bucket->entryIndex == 0) ? this->bucketsUsed + 1 : this->bucketsUsed;
entry = bucket->entries + bucket->entryIndex++;
if (bucket->entryIndex == 0)
this->indexesOfUsedBuckets[this->indexInIndexesOfUsedBuckets++] = index;
entry = bucket->entries + bucket->entryIndex++;
}
// TODO(doyle): A maybe case. We're using virtual memory, so you should
@ -2722,7 +2753,7 @@ DQN_VHASH_TABLE_TEMPLATE Item *DQN_VHASH_TABLE_DECL::Set(Key const &key, Item co
// day we care about resizing the table but at the cost of a lot more code
// complexity.
isize const threshold = static_cast<isize>(0.75f * this->numBuckets);
DQN_ALWAYS_ASSERTM(this->bucketsUsed < threshold, "%zu >= %zu", this->bucketsUsed, threshold);
DQN_ALWAYS_ASSERTM(this->indexInIndexesOfUsedBuckets < threshold, "%zu >= %zu", this->indexInIndexesOfUsedBuckets, threshold);
entry->key = key;
entry->item = item;
@ -2759,7 +2790,7 @@ DQN_VHASH_TABLE_TEMPLATE void DQN_VHASH_TABLE_DECL::Erase(Key const &key)
isize index = Hash(this->numBuckets, key);
Bucket *bucket = this->buckets + index;
for (isize i = 0; i < bucket->entryIndex; ++i)
DQN_FOR_EACH(i, bucket->entryIndex)
{
Entry *check = bucket->entries + i;
if (Equals(check->key, key))
@ -2768,15 +2799,39 @@ DQN_VHASH_TABLE_TEMPLATE void DQN_VHASH_TABLE_DECL::Erase(Key const &key)
bucket->entries[j] = bucket->entries[j + 1];
if (--bucket->entryIndex == 0)
--this->bucketsUsed;
{
DQN_FOR_EACH(bucketIndex, this->indexInIndexesOfUsedBuckets)
{
if (this->indexesOfUsedBuckets[bucketIndex] == index)
{
indexesOfUsedBuckets[bucketIndex] =
indexesOfUsedBuckets[--this->indexInIndexesOfUsedBuckets];
}
}
}
DQN_ASSERT(this->bucketsUsed >= 0);
DQN_ASSERT(this->indexInIndexesOfUsedBuckets >= 0);
DQN_ASSERT(bucket->entryIndex >= 0);
return;
}
}
}
// XPlatform > #DqnCatalog API
// =================================================================================================
template <typename T>
struct DqnCatalog
{
struct Entry
{
u64 timeLastModified;
T *data;
};
DqnVHashTable<DqnSlice<const char>, T> assetTable;
void Update();
};
// XPlatform > #DqnFile API
// =================================================================================================
struct DqnFile