#pragma once #include "idlib/containers/hashindex.h" #include #include #include #include #include template struct idHashTableKeyOps { static std::uint32_t Hash(const keyType& key) { return static_cast(std::hash()(key)); } static bool Equal(const keyType& stored, const keyType& key) { return stored == key; } }; template<> struct idHashTableKeyOps { static std::uint32_t Hash(const char* key) { std::uint32_t hash = 2166136261u; if (key != nullptr) while (*key != '\0') { hash = (hash ^ static_cast(*key++)) * 16777619u; } return hash; } static bool Equal(const idStr& stored, const char* key) { return idStr::Cmp(stored.c_str(), key) == 0; } }; template class idHashTableT { public: using nodeType = idHashNodeT; nodeType** heads; int tableSize; int numEntries; int tableSizeMask; explicit idHashTableT(const int requestedSize = 256) : heads(nullptr), tableSize(NextPowerOfTwo(requestedSize)), numEntries(0), tableSizeMask(tableSize - 1) { heads = new (std::nothrow) nodeType*[tableSize]; if (heads != nullptr) std::fill(heads, heads + tableSize, nullptr); } ~idHashTableT() { Clear(); delete[] heads; } idHashTableT(const idHashTableT&) = delete; idHashTableT& operator=(const idHashTableT&) = delete; int Num() const { return numEntries; } void Clear() { if (heads == nullptr) return; for (int bucket = 0; bucket < tableSize; ++bucket) { nodeType* node = heads[bucket]; while (node != nullptr) { nodeType* next = node->next; delete node; node = next; } heads[bucket] = nullptr; } numEntries = 0; } bool Get(const keyType& key, valueType** value) { nodeType* node = FindNode(key); if (value != nullptr) *value = node == nullptr ? nullptr : &node->value; return node != nullptr; } bool Get(const keyType& key, const valueType** value) const { const nodeType* node = FindNode(key); if (value != nullptr) *value = node == nullptr ? nullptr : &node->value; return node != nullptr; } valueType& Set(const keyType& key, const valueType& value) { nodeType* node = FindNode(key); if (node == nullptr) { const int bucket = Bucket(key); node = new nodeType(); node->key = key; node->next = heads[bucket]; heads[bucket] = node; ++numEntries; } node->value = value; return node->value; } valueType* GetIndex(int index) { if (index < 0 || index >= numEntries) return nullptr; for (int bucket = 0; bucket < tableSize; ++bucket) for (nodeType* node = heads[bucket]; node != nullptr; node = node->next) if (index-- == 0) return &node->value; return nullptr; } private: static int NextPowerOfTwo(int value) { int result = 1; while (result < (value > 0 ? value : 1)) result <<= 1; return result; } int Bucket(const keyType& key) const { return static_cast(idHashTableKeyOps::Hash(key)) & tableSizeMask; } nodeType* FindNode(const keyType& key) { if (heads == nullptr) return nullptr; for (nodeType* node = heads[Bucket(key)]; node != nullptr; node = node->next) if (idHashTableKeyOps::Equal(node->key, key)) return node; return nullptr; } const nodeType* FindNode(const keyType& key) const { return const_cast(this)->FindNode(key); } }; template class idHashTable : public idHashTableT { public: using idHashTableT::idHashTableT; }; #if defined(_WIN32) && !defined(_WIN64) static_assert(sizeof(idHashTableT) == 16, "Recovered idHashTableT ABI changed"); #endif