From fc11e9ac0eba8fbdfd01e193bdab72ddb6e4880f Mon Sep 17 00:00:00 2001 From: Doyle T Date: Tue, 24 Jul 2018 00:05:43 +1000 Subject: [PATCH] Allow unordered iteration over hash table --- DqnVHashTable.cpp | 56 ++++++++++++++++++++++++--------- dqn.h | 79 ++++++++++++++++++++++++++++++++++++++++------- 2 files changed, 109 insertions(+), 26 deletions(-) diff --git a/DqnVHashTable.cpp b/DqnVHashTable.cpp index 72d2314..8b5713e 100644 --- a/DqnVHashTable.cpp +++ b/DqnVHashTable.cpp @@ -6,22 +6,50 @@ void DqnVHashTable_Test() int x; }; using Height = u32; - Block block = {}; - DqnVHashTable 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 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 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)); + } } diff --git a/dqn.h b/dqn.h index 1508f41..22bd5d0 100644 --- a/dqn.h +++ b/dqn.h @@ -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(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(DqnOS_VAlloc(size * sizeof(*this->buckets))); + this->indexesOfUsedBuckets = static_cast (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(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 +struct DqnCatalog +{ + struct Entry + { + u64 timeLastModified; + T *data; + }; + + DqnVHashTable, T> assetTable; + void Update(); +}; + // XPlatform > #DqnFile API // ================================================================================================= struct DqnFile