mirror of
https://github.com/beefytech/Beef.git
synced 2025-06-08 11:38:21 +02:00
336 lines
No EOL
6.4 KiB
C++
336 lines
No EOL
6.4 KiB
C++
#pragma once
|
|
|
|
#include "../Common.h"
|
|
#include "Array.h"
|
|
|
|
NS_BF_BEGIN;
|
|
|
|
struct MultiHashSetFuncs
|
|
{
|
|
void* Allocate(intptr size, intptr align)
|
|
{
|
|
return malloc(size);
|
|
}
|
|
|
|
void* AllocateZero(intptr size, intptr align)
|
|
{
|
|
void* mem = malloc(size);
|
|
memset(mem, 0, size);
|
|
return mem;
|
|
}
|
|
|
|
void Deallocate(void* ptr)
|
|
{
|
|
free(ptr);
|
|
}
|
|
|
|
bool DeallocateAll()
|
|
{
|
|
return false;
|
|
}
|
|
};
|
|
|
|
template <typename T, typename TFuncs = AllocatorCLib<T> >
|
|
class MultiHashSet : public TFuncs
|
|
{
|
|
public:
|
|
struct Entry
|
|
{
|
|
T mValue;
|
|
Entry* mNext;
|
|
int mHash;
|
|
};
|
|
|
|
struct Iterator
|
|
{
|
|
public:
|
|
MultiHashSet* mSet;
|
|
Entry* mCurEntry;
|
|
int mCurBucket;
|
|
|
|
public:
|
|
Iterator(MultiHashSet* set)
|
|
{
|
|
this->mSet = set;
|
|
this->mCurBucket = 0;
|
|
this->mCurEntry = NULL;
|
|
}
|
|
|
|
Iterator& operator++()
|
|
{
|
|
if (this->mCurEntry != NULL)
|
|
{
|
|
this->mCurEntry = this->mCurEntry->mNext;
|
|
if (this->mCurEntry != NULL)
|
|
return *this;
|
|
this->mCurBucket++;
|
|
}
|
|
|
|
if (mSet->mHashHeads == NULL)
|
|
{
|
|
this->mCurBucket = this->mSet->mHashSize;
|
|
return *this; // At end
|
|
}
|
|
|
|
while (this->mCurBucket < mSet->mHashSize)
|
|
{
|
|
this->mCurEntry = this->mSet->mHashHeads[mCurBucket];
|
|
if (this->mCurEntry != NULL)
|
|
return *this;
|
|
this->mCurBucket++;
|
|
}
|
|
|
|
return *this; // At end
|
|
}
|
|
|
|
bool operator!=(const Iterator& itr) const
|
|
{
|
|
return ((itr.mCurEntry != this->mCurEntry) || (itr.mCurBucket != this->mCurBucket));
|
|
}
|
|
|
|
T operator*()
|
|
{
|
|
return this->mCurEntry->mValue;
|
|
}
|
|
|
|
operator bool() const
|
|
{
|
|
return this->mCurEntry != NULL;
|
|
}
|
|
|
|
void MoveToNextHashMatch()
|
|
{
|
|
int wantHash = this->mCurEntry->mHash;
|
|
do
|
|
{
|
|
this->mCurEntry = this->mCurEntry->mNext;
|
|
}
|
|
while ((this->mCurEntry != NULL) && (this->mCurEntry->mHash != wantHash));
|
|
}
|
|
};
|
|
|
|
public:
|
|
Entry** mHashHeads;
|
|
static const int cDefaultHashSize = 17;
|
|
int mHashSize;
|
|
int mCount;
|
|
|
|
MultiHashSet()
|
|
{
|
|
this->mHashHeads = NULL;
|
|
this->mHashSize = cDefaultHashSize;
|
|
this->mCount = 0;
|
|
}
|
|
|
|
~MultiHashSet()
|
|
{
|
|
this->Clear();
|
|
}
|
|
|
|
void Add(T value)
|
|
{
|
|
if (this->mHashHeads == NULL)
|
|
this->mHashHeads = (Entry**)TFuncs::AllocateZero(sizeof(Entry*) * mHashSize, alignof(Entry*));
|
|
|
|
int hash = TFuncs::GetHash(value);
|
|
int hashIdx = (hash & 0x7FFFFFFF) % this->mHashSize;
|
|
Entry* headEntry = this->mHashHeads[hashIdx];
|
|
|
|
Entry* newEntry = (Entry*)TFuncs::Allocate(sizeof(Entry), alignof(Entry));
|
|
newEntry->mValue = value;
|
|
newEntry->mNext = headEntry;
|
|
newEntry->mHash = hash;
|
|
mCount++;
|
|
|
|
mHashHeads[hashIdx] = newEntry;
|
|
}
|
|
|
|
void AddAfter(T value, Entry* afterEntry)
|
|
{
|
|
int hash = TFuncs::GetHash(value);
|
|
int hashIdx = (hash & 0x7FFFFFFF) % this->mHashSize;
|
|
BF_ASSERT(hash == afterEntry->mHash);
|
|
|
|
Entry* newEntry = (Entry*)TFuncs::Allocate(sizeof(Entry), alignof(Entry));
|
|
newEntry->mValue = value;
|
|
newEntry->mNext = afterEntry->mNext;
|
|
newEntry->mHash = hash;
|
|
mCount++;
|
|
|
|
afterEntry->mNext = newEntry;
|
|
}
|
|
|
|
void Rehash(int newHashSize)
|
|
{
|
|
auto newHashHeads = (Entry**)TFuncs::AllocateZero(sizeof(Entry*) * newHashSize, alignof(Entry*));
|
|
|
|
for (int hashIdx = 0; hashIdx < mHashSize; hashIdx++)
|
|
{
|
|
Entry* checkEntry = mHashHeads[hashIdx];
|
|
while (checkEntry != NULL)
|
|
{
|
|
auto nextEntry = checkEntry->mNext;
|
|
int newHashIdx = (checkEntry->mHash & 0x7FFFFFFF) % newHashSize;
|
|
checkEntry->mNext = newHashHeads[newHashIdx];
|
|
newHashHeads[newHashIdx] = checkEntry;
|
|
|
|
checkEntry = nextEntry;
|
|
}
|
|
}
|
|
|
|
TFuncs::Deallocate(mHashHeads);
|
|
mHashHeads = newHashHeads;
|
|
mHashSize = newHashSize;
|
|
}
|
|
|
|
void CheckRehash()
|
|
{
|
|
// Make the lookup load reasonable
|
|
if (mHashSize < mCount)
|
|
{
|
|
this->Rehash(BF_MAX(mCount, (int)(mHashSize * 1.5f)) | 1);
|
|
}
|
|
}
|
|
|
|
template <typename TKey>
|
|
bool TryGet(const TKey& key, T* val)
|
|
{
|
|
if (mHashHeads == NULL)
|
|
return false;
|
|
|
|
this->CheckRehash();
|
|
|
|
int hash = TFuncs::GetHash(key);
|
|
int hashIdx = (hash & 0x7FFFFFFF) % this->mHashSize;
|
|
Entry* checkEntry = this->mHashHeads[hashIdx];
|
|
while (checkEntry != NULL)
|
|
{
|
|
if ((checkEntry->mHash == hash) && (TFuncs::Matches(key, checkEntry->mValue)))
|
|
{
|
|
if (val != NULL)
|
|
*val = checkEntry->mValue;
|
|
return true;
|
|
}
|
|
checkEntry = checkEntry->mNext;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
template <typename TKey>
|
|
Iterator TryGet(const TKey& key)
|
|
{
|
|
if (mHashHeads == NULL)
|
|
return end();
|
|
|
|
this->CheckRehash();
|
|
|
|
int hash = TFuncs::GetHash(key);
|
|
int hashIdx = (hash & 0x7FFFFFFF) % this->mHashSize;
|
|
Entry* checkEntry = this->mHashHeads[hashIdx];
|
|
while (checkEntry != NULL)
|
|
{
|
|
if ((checkEntry->mHash == hash) && (TFuncs::Matches(key, checkEntry->mValue)))
|
|
{
|
|
Iterator itr(this);
|
|
itr.mCurEntry = checkEntry;
|
|
itr.mCurBucket = hashIdx;
|
|
return itr;
|
|
}
|
|
checkEntry = checkEntry->mNext;
|
|
}
|
|
|
|
return end();
|
|
}
|
|
|
|
template <typename TKey>
|
|
bool Remove(const TKey& key)
|
|
{
|
|
if (mHashHeads == NULL)
|
|
return false;
|
|
|
|
this->CheckRehash();
|
|
|
|
int hash = TFuncs::GetHash(key);
|
|
int hashIdx = (hash & 0x7FFFFFFF) % this->mHashSize;
|
|
|
|
Entry** srcCheckEntryPtr = &this->mHashHeads[hashIdx];
|
|
Entry* checkEntry = *srcCheckEntryPtr;
|
|
while (checkEntry != NULL)
|
|
{
|
|
if ((checkEntry->mHash == hash) && (TFuncs::Matches(key, checkEntry->mValue)))
|
|
{
|
|
this->mCount--;
|
|
*srcCheckEntryPtr = checkEntry->mNext;
|
|
TFuncs::Deallocate(checkEntry);
|
|
return true;
|
|
}
|
|
srcCheckEntryPtr = &checkEntry->mNext;
|
|
checkEntry = checkEntry->mNext;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
Iterator Erase(const Iterator& itr)
|
|
{
|
|
Iterator next = itr;
|
|
++next;
|
|
|
|
bool found = false;
|
|
|
|
auto entry = itr.mCurEntry;
|
|
int hashIdx = (entry->mHash & 0x7FFFFFFF) % this->mHashSize;
|
|
|
|
Entry** srcCheckEntryPtr = &this->mHashHeads[hashIdx];
|
|
Entry* checkEntry = *srcCheckEntryPtr;
|
|
while (checkEntry != NULL)
|
|
{
|
|
if (checkEntry == itr.mCurEntry)
|
|
{
|
|
this->mCount--;
|
|
*srcCheckEntryPtr = checkEntry->mNext;
|
|
found = true;
|
|
}
|
|
srcCheckEntryPtr = &checkEntry->mNext;
|
|
checkEntry = checkEntry->mNext;
|
|
}
|
|
|
|
BF_ASSERT(found);
|
|
TFuncs::Deallocate(entry);
|
|
|
|
return next;
|
|
}
|
|
|
|
void Clear()
|
|
{
|
|
if (!TFuncs::DeallocateAll())
|
|
{
|
|
auto itr = begin();
|
|
auto endItr = end();
|
|
while (itr != endItr)
|
|
{
|
|
auto entry = itr.mCurEntry;
|
|
++itr;
|
|
TFuncs::Deallocate(entry);
|
|
}
|
|
TFuncs::Deallocate(this->mHashHeads);
|
|
}
|
|
|
|
this->mHashSize = cDefaultHashSize;
|
|
this->mHashHeads = NULL;
|
|
this->mCount = 0;
|
|
}
|
|
|
|
Iterator begin()
|
|
{
|
|
return ++Iterator(this);
|
|
}
|
|
|
|
Iterator end()
|
|
{
|
|
Iterator itr(this);
|
|
itr.mCurBucket = this->mHashSize;
|
|
return itr;
|
|
}
|
|
};
|
|
|
|
NS_BF_END; |