diff --git a/headers/Containers/Hashmap.hpp b/headers/Containers/Hashmap.hpp index a9e1248..01413f9 100644 --- a/headers/Containers/Hashmap.hpp +++ b/headers/Containers/Hashmap.hpp @@ -15,32 +15,32 @@ #include namespace Tourmaline::Containers { -template +template class Hashmap { public: - Hashmap() { storage.resize(baseBucketCount); } + Hashmap() { storage.resize(minimumBucketCount); } ~Hashmap() { Clear(); } Value &Insert(Key key, Value value) { std::size_t keyHash = std::hash{}(key), keyHashPosition = keyHash % storage.size(); + if (currentLoadFactor >= loadFactor && currentlyRehashing == false) { + rehash(); + } // Empty bucket if (storage[keyHashPosition] == nullptr) { storage[keyHashPosition] = new std::vector; - storage[keyHashPosition]->emplace_back(key, std::move(value), keyHash); - ++count; - return storage[keyHashPosition]->back().value; + } else { + // Throws + Systems::Logging::Log("Trying to inserting same key twice! Throwing...", + "Hashmap", Systems::Logging::LogLevel::Error, + Has(key)); } - // Throws - Systems::Logging::Log("Trying to inserting same key twice! Throwing...", - "Hashmap", Systems::Logging::LogLevel::Error, - Has(key)); - storage[keyHashPosition]->emplace_back(key, std::move(value), keyHash); - ++count; + currentLoadFactor = (++count) / static_cast(bucketCount); return storage[keyHashPosition]->back().value; } @@ -56,7 +56,11 @@ public: [keyHash, &key](const hashStorage &hash) { return hash.hash == keyHash && hash.key == key; }); - --count; + + currentLoadFactor = (--count) / static_cast(bucketCount); + if (currentLoadFactor <= minimizeFactor) { + rehash(); + } } [[nodiscard("Unnecessary call of Has function")]] @@ -116,6 +120,54 @@ public: } private: + bool rehash(std::size_t goalSize = 0) { + // Minimum + goalSize = goalSize == 0 ? count : goalSize; + float wouldBeLoadFactor = goalSize / static_cast(bucketCount); + if (wouldBeLoadFactor < loadFactor && wouldBeLoadFactor > minimizeFactor) + [[unlikely]] { + return false; // No rehashing is required + } + + // Putting it closer to minimizeFactor + std::size_t goalBucketCount = + goalSize / ((loadFactor + minimizeFactor) / 2.5); // Magic number + if (goalBucketCount < minimumBucketCount) [[unlikely]] { + goalBucketCount = minimumBucketCount; + } + + // No need to reallocate + if (goalBucketCount == bucketCount) { + return false; + } + + currentlyRehashing = true; + std::vector oldStorage = std::move(storage); + storage = std::vector(); + storage.resize(goalBucketCount); + + // Repopulate and cleanup + for (bucket *entry : oldStorage) { + if (entry == nullptr) { + continue; + } + + for (const hashStorage &hash : *entry) { + Insert(hash.key, hash.value); + } + + entry->clear(); + delete entry; + } + + // It's necessary to write these again due to insert above + currentLoadFactor = goalSize / static_cast(goalBucketCount); + bucketCount = goalBucketCount; + count = goalSize; + currentlyRehashing = false; + return true; + } + struct hashStorage { Key key; Value value; @@ -124,7 +176,9 @@ private: using bucket = std::vector; std::vector storage; - std::size_t count = 0; + std::size_t count = 0, bucketCount = minimumBucketCount; + float currentLoadFactor = 0; + bool currentlyRehashing = false; // Lock for Insert in rehash }; } // namespace Tourmaline::Containers #endif