1
0
Fork 0
mirror of https://github.com/beefytech/Beef.git synced 2025-06-08 11:38:21 +02:00
Beef/BeefySysLib/util/Dictionary.h

737 lines
15 KiB
C
Raw Normal View History

2019-08-23 11:56:54 -07:00
#pragma once
#include "../Common.h"
#include <unordered_map>
#define NEW_DICTIONAY
NS_BF_BEGIN;
#ifdef NEW_DICTIONAY
template <typename TKey, typename TValue>
class Dictionary
{
public:
typedef int int_cosize;
typedef TKey key_type;
typedef TValue value_type;
struct EntryPair
{
public:
TKey mKey; // Key of entry
TValue mValue; // Value of entry
};
struct RawEntryPair
{
public:
typename std::aligned_storage<sizeof(TKey), alignof(TKey)>::type mKey;
typename std::aligned_storage<sizeof(TValue), alignof(TValue)>::type mValue;
};
public:
struct Entry : public RawEntryPair
{
int_cosize mNext; // Index of next entry, -1 if last
int32 mHashCode; // Lower 31 bits of hash code, -1 if unused
};
public:
int_cosize* mBuckets;
Entry* mEntries;
int_cosize mAllocSize;
int_cosize mCount;
int_cosize mFreeList;
int_cosize mFreeCount;
public:
struct iterator
{
public:
typedef std::bidirectional_iterator_tag iterator_category;
//typedef T value_type;
//typedef intptr difference_type;
//typedef T* pointer;
//typedef T& reference;
public:
Dictionary* mDictionary;
int_cosize mIdx;
int_cosize mCurrentIdx;
protected:
void MoveNext()
{
while ((uintptr)mIdx < (uintptr)mDictionary->mCount)
{
if (mDictionary->mEntries[mIdx].mHashCode >= 0)
{
mCurrentIdx = mIdx;
mIdx++;
return;
}
mIdx++;
}
mIdx = mDictionary->mCount + 1;
mCurrentIdx = -1;
}
public:
iterator()
{
mDictionary = NULL;
mIdx = 0;
mCurrentIdx = -1;
}
iterator(Dictionary* dict, int idx)
{
mDictionary = dict;
mIdx = idx;
mCurrentIdx = idx;
}
iterator(Dictionary* dict, int idx, int currentIdx)
{
mDictionary = dict;
mIdx = idx;
mCurrentIdx = currentIdx;
}
iterator& operator++()
{
MoveNext();
return *this;
}
iterator operator++(int)
{
auto prevVal = *this;
MoveNext();
return prevVal;
}
2020-03-14 16:27:58 -07:00
bool NextWithSameKey(const TKey& key)
2019-08-23 11:56:54 -07:00
{
2020-03-14 16:27:58 -07:00
int_cosize hashCode = (int_cosize)BeefHash<TKey>()(key) & 0x7FFFFFFF;
2019-08-23 11:56:54 -07:00
2020-03-15 06:07:21 -07:00
while (mCurrentIdx >= 0)
{
mCurrentIdx = mDictionary->mEntries[mCurrentIdx].mNext;
if (mCurrentIdx < 0)
2020-03-14 16:27:58 -07:00
break;
if ((mDictionary->mEntries[mCurrentIdx].mHashCode == hashCode) &&
2020-03-15 06:07:21 -07:00
((*(TKey*)&mDictionary->mEntries[mCurrentIdx].mKey) == key))
{
mIdx = mCurrentIdx;
2020-03-14 16:27:58 -07:00
return true;
2020-03-15 06:07:21 -07:00
}
2020-03-14 16:27:58 -07:00
}
2019-08-23 11:56:54 -07:00
2020-03-14 16:27:58 -07:00
mIdx = mDictionary->mCount + 1;
mCurrentIdx = -1;
return false;
}
2019-08-23 11:56:54 -07:00
bool operator!=(const iterator& itr) const
{
return (itr.mDictionary != mDictionary) || (itr.mIdx != mIdx);
}
bool operator==(const iterator& itr) const
{
return (itr.mDictionary == mDictionary) && (itr.mIdx == mIdx);
}
EntryPair& operator*()
{
return *(EntryPair*)&mDictionary->mEntries[mCurrentIdx];
}
EntryPair* operator->()
{
return (EntryPair*)&mDictionary->mEntries[mCurrentIdx];
}
};
private:
int_cosize GetPrimeish(int_cosize min)
{
// This is a minimal effort to help address-aligned dataa
return (min | 1);
}
int_cosize ExpandSize(int_cosize oldSize)
{
int_cosize newSize = 2 * oldSize;
// Allow the hashtables to grow to maximum possible size (~2G elements) before encoutering capacity overflow.
// Note that this check works even when mAllocSize overflowed thanks to the (uint) cast
/*if ((uint)newSize > MaxPrimeArrayLength && MaxPrimeArrayLength > oldSize)
{
Contract.Assert( MaxPrimeArrayLength == GetPrime(MaxPrimeArrayLength), "Invalid MaxPrimeArrayLength");
return MaxPrimeArrayLength;
}*/
return GetPrimeish(newSize);
}
void Resize()
{
Resize(ExpandSize(mCount), false);
}
void Resize(intptr newSize, bool forceNewHashCodes)
{
BF_ASSERT(newSize >= mAllocSize);
Entry* newEntries;
int_cosize* newBuckets;
AllocData(newSize, newEntries, newBuckets);
2019-08-23 11:56:54 -07:00
for (int_cosize i = 0; i < newSize; i++)
newBuckets[i] = -1;
2019-08-23 11:56:54 -07:00
for (int i = 0; i < mCount; i++)
{
auto& newEntry = newEntries[i];
auto& oldEntry = mEntries[i];
newEntry.mHashCode = oldEntry.mHashCode;
newEntry.mNext = oldEntry.mNext;
new (&newEntry.mKey) TKey(std::move(*(TKey*)&oldEntry.mKey));
new (&newEntry.mValue) TValue(std::move(*(TValue*)&oldEntry.mValue));
}
for (int i = mCount; i < newSize; i++)
{
newEntries[i].mHashCode = -1;
}
if (forceNewHashCodes)
{
for (int_cosize i = 0; i < mCount; i++)
{
if (newEntries[i].mHashCode != -1)
{
newEntries[i].mHashCode = (int_cosize)BeefHash<TKey>()(*(TKey*)&newEntries[i].mKey) & 0x7FFFFFFF;
}
}
}
for (int_cosize i = 0; i < mCount; i++)
{
if (newEntries[i].mHashCode >= 0)
{
int_cosize bucket = (int_cosize)(newEntries[i].mHashCode % newSize);
newEntries[i].mNext = newBuckets[bucket];
newBuckets[bucket] = i;
}
}
DeleteData();
2019-08-23 11:56:54 -07:00
mBuckets = newBuckets;
mEntries = newEntries;
mAllocSize = (int_cosize)newSize;
}
int FindEntry(const TKey& key)
{
if (mBuckets != NULL)
{
int_cosize hashCode = (int_cosize)BeefHash<TKey>()(key) & 0x7FFFFFFF;
for (int_cosize i = mBuckets[hashCode % mAllocSize]; i >= 0; i = mEntries[i].mNext)
{
if (mEntries[i].mHashCode == hashCode && (*(TKey*)&mEntries[i].mKey == key)) return i;
}
}
return -1;
}
template <typename TAltKey>
int FindEntryWith(const TAltKey& key)
{
if (mBuckets != NULL)
{
int_cosize hashCode = (int_cosize)BeefHash<TAltKey>()(key) & 0x7FFFFFFF;
for (int_cosize i = mBuckets[hashCode % mAllocSize]; i >= 0; i = mEntries[i].mNext)
{
if (mEntries[i].mHashCode == hashCode && (*(TKey*)&mEntries[i].mKey == key)) return i;
}
}
return -1;
}
void Initialize(intptr capacity)
{
int_cosize size = GetPrimeish((int_cosize)capacity);
AllocData(size, mEntries, mBuckets);
2019-08-23 11:56:54 -07:00
mAllocSize = size;
for (int_cosize i = 0; i < (int_cosize)mAllocSize; i++) mBuckets[i] = -1;
2019-08-23 11:56:54 -07:00
mFreeList = -1;
}
2020-03-14 16:27:58 -07:00
bool Insert(const TKey& key, bool forceAdd, TKey** keyPtr, TValue** valuePtr)
2019-08-23 11:56:54 -07:00
{
if (mBuckets == NULL) Initialize(0);
int_cosize hashCode = (int_cosize)BeefHash<TKey>()(key) & 0x7FFFFFFF;
int_cosize targetBucket = hashCode % (int_cosize)mAllocSize;
2020-03-14 16:27:58 -07:00
if (!forceAdd)
2019-08-23 11:56:54 -07:00
{
2020-03-14 16:27:58 -07:00
for (int_cosize i = mBuckets[targetBucket]; i >= 0; i = mEntries[i].mNext)
2019-08-23 11:56:54 -07:00
{
2020-03-14 16:27:58 -07:00
if ((mEntries[i].mHashCode == hashCode) && (*(TKey*)&mEntries[i].mKey == key))
2019-08-23 11:56:54 -07:00
{
2020-03-14 16:27:58 -07:00
if (keyPtr != NULL)
*keyPtr = (TKey*)&mEntries[i].mKey;
if (valuePtr != NULL)
*valuePtr = (TValue*)&mEntries[i].mValue;
return false;
2019-08-23 11:56:54 -07:00
}
}
}
2020-03-14 16:27:58 -07:00
2019-08-23 11:56:54 -07:00
int_cosize index;
if (mFreeCount > 0)
{
index = mFreeList;
mFreeList = mEntries[index].mNext;
mFreeCount--;
}
else
{
if (mCount == mAllocSize)
{
Resize();
targetBucket = hashCode % (int_cosize)mAllocSize;
}
index = mCount;
mCount++;
}
mEntries[index].mHashCode = hashCode;
mEntries[index].mNext = mBuckets[targetBucket];
new (&mEntries[index].mKey) TKey(key);
mBuckets[targetBucket] = index;
2020-03-14 16:27:58 -07:00
2019-08-23 11:56:54 -07:00
if (keyPtr != NULL)
*keyPtr = (TKey*)&mEntries[index].mKey;
if (valuePtr != NULL)
*valuePtr = (TValue*)&mEntries[index].mValue;
return true;
}
void RemoveIdx(int_cosize bucket, int_cosize i, int_cosize last)
{
if (last < 0)
{
mBuckets[bucket] = mEntries[i].mNext;
}
else
{
mEntries[last].mNext = mEntries[i].mNext;
}
mEntries[i].mHashCode = -1;
mEntries[i].mNext = mFreeList;
((TKey*)&mEntries[i].mKey)->~TKey();
((TValue*)&mEntries[i].mValue)->~TValue();
mFreeList = i;
mFreeCount++;
}
public:
Dictionary()
{
mBuckets = NULL;
mEntries = NULL;
mAllocSize = 0;
mCount = 0;
mFreeList = 0;
mFreeCount = 0;
}
Dictionary(const Dictionary& val)
{
mAllocSize = val.mAllocSize;
mCount = val.mCount;
if (mAllocSize == 0)
{
mBuckets = NULL;
mEntries = NULL;
}
else
{
AllocData(mAllocSize, mEntries, mBuckets);
2019-08-23 11:56:54 -07:00
for (int_cosize i = 0; i < mAllocSize; i++)
mBuckets[i] = val.mBuckets[i];
for (int i = 0; i < mCount; i++)
{
auto& newEntry = mEntries[i];
auto& oldEntry = val.mEntries[i];
newEntry.mHashCode = oldEntry.mHashCode;
newEntry.mNext = oldEntry.mNext;
new (&newEntry.mKey) TKey(*(TKey*)&oldEntry.mKey);
new (&newEntry.mValue) TValue(*(TValue*)&oldEntry.mValue);
}
for (int i = mCount; i < mAllocSize; i++)
{
mEntries[i].mHashCode = -1;
}
}
mFreeCount = val.mFreeCount;
mFreeList = val.mFreeList;
}
Dictionary(Dictionary&& val)
{
mBuckets = val.mBuckets;
mEntries = val.mEntries;
2022-04-28 15:01:59 -07:00
mAllocSize = val.mAllocSize;
mCount = val.mCount;
2019-08-23 11:56:54 -07:00
mFreeList = val.mFreeList;
2022-04-28 15:01:59 -07:00
mFreeCount = val.mFreeCount;
val.mBuckets = NULL;
val.mEntries = NULL;
val.mAllocSize = 0;
val.mCount = 0;
val.mFreeList = 0;
val.mFreeCount = 0;
2019-08-23 11:56:54 -07:00
}
~Dictionary()
2020-05-14 14:56:25 -07:00
{
DeleteData();
}
void AllocData(intptr size, Entry*& outEntries, int_cosize*& outBuckets)
{
uint8* data = new uint8[size * (sizeof(Entry) + sizeof(int_cosize))];
outEntries = (Entry*)data;
outBuckets = (int_cosize*)(data + size * sizeof(Entry));
}
void DeleteData()
2019-08-23 11:56:54 -07:00
{
if (!std::is_pod<TKey>::value)
{
for (int_cosize i = 0; i < mCount; i++)
{
if (mEntries[i].mHashCode != -1)
((TKey*)&mEntries[i].mKey)->~TKey();
}
}
if (!std::is_pod<TValue>::value)
{
for (int_cosize i = 0; i < mCount; i++)
{
if (mEntries[i].mHashCode != -1)
((TValue*)&mEntries[i].mValue)->~TValue();
}
}
2022-03-18 18:06:14 -07:00
delete [] mEntries;
2019-08-23 11:56:54 -07:00
}
Dictionary& operator=(const Dictionary& rhs)
{
Clear();
for (auto& kv : rhs)
2020-03-14 16:27:58 -07:00
ForceAdd(kv.mKey, kv.mValue);
2019-08-23 11:56:54 -07:00
return *this;
}
intptr GetCount() const
{
return mCount - mFreeCount;
}
intptr size() const
{
return mCount - mFreeCount;
}
bool IsEmpty() const
{
return mCount - mFreeCount == 0;
}
void Reserve(intptr size)
{
if (size > mAllocSize)
Resize(size, false);
}
TValue& operator[](const TKey& key)
{
TValue* valuePtr;
if (Insert(key, false, NULL, &valuePtr))
{
new (valuePtr) TValue();
}
return *valuePtr;
}
const TValue& operator[](const TKey& key) const
{
int_cosize i = (int_cosize)FindEntry(key);
if (i >= 0)
return mEntries[i].mValue;
BF_FATAL("Key not found");
return TValue();
}
2020-03-14 16:27:58 -07:00
void ForceAdd(const TKey& key, const TValue& value)
{
TValue* valuePtr = NULL;
Insert(key, true, NULL, &valuePtr);
new (valuePtr) TValue(value);
}
2019-08-23 11:56:54 -07:00
bool TryAdd(const TKey& key, const TValue& value)
{
TValue* valuePtr;
if (!Insert(key, false, NULL, &valuePtr))
return false;
new (valuePtr) TValue(value);
return true;
}
bool TryAdd(const TKey& key, TKey** keyPtr, TValue** valuePtr)
{
if (!Insert(key, false, keyPtr, valuePtr))
return false;
new (*valuePtr) TValue();
return true;
}
// template <typename TAltKey>
// bool TryAddWith(const TAltKey& key, TKey** keyPtr, TValue** valuePtr)
// {
// if (!Insert(key, false, keyPtr, valuePtr))
// return false;
// new (*valuePtr) TValue();
// return true;
// }
2019-08-23 11:56:54 -07:00
// Returns uninitialized valuePtr - must use placement new
bool TryAddRaw(const TKey& key, TKey** keyPtr, TValue** valuePtr)
{
return Insert(key, false, keyPtr, valuePtr);
}
bool TryGetValue(const TKey& key, TValue** valuePtr)
{
int_cosize i = (int_cosize)FindEntry(key);
if (i >= 0)
{
*valuePtr = (TValue*)&mEntries[i].mValue;
return true;
}
return false;
}
template <typename TAltKey>
bool TryGetValueWith(const TAltKey& key, TValue** valuePtr)
{
int_cosize i = (int_cosize)FindEntryWith(key);
if (i >= 0)
{
*valuePtr = (TValue*)&mEntries[i].mValue;
return true;
}
return false;
}
bool TryGetValue(const TKey& key, TValue* valuePtr)
{
int_cosize i = (int_cosize)FindEntry(key);
if (i >= 0)
{
*valuePtr = *(TValue*)&mEntries[i].mValue;
return true;
}
return false;
}
iterator Find(const TKey& key)
{
int_cosize i = (int_cosize)FindEntry(key);
iterator itr;
if (i >= 0)
{
return iterator((Dictionary*)this, i);
}
else
return iterator((Dictionary*)this, mCount + 1, -1);
}
bool Remove(const TKey& key)
{
if (mBuckets != NULL)
{
int_cosize hashCode = (int_cosize)BeefHash<TKey>()(key) & 0x7FFFFFFF;
int_cosize bucket = hashCode % (int_cosize)mAllocSize;
int_cosize last = -1;
for (int_cosize i = mBuckets[bucket]; i >= 0; last = i, i = mEntries[i].mNext)
{
if ((mEntries[i].mHashCode == hashCode) && (*(TKey*)&mEntries[i].mKey == key))
{
RemoveIdx(bucket, i, last);
return true;
}
}
}
return false;
}
bool Remove(const TKey& key, TValue* valuePtr)
{
if (mBuckets != NULL)
{
int_cosize hashCode = (int_cosize)BeefHash<TKey>()(key) & 0x7FFFFFFF;
int_cosize bucket = hashCode % (int_cosize)mAllocSize;
int_cosize last = -1;
for (int_cosize i = mBuckets[bucket]; i >= 0; last = i, i = mEntries[i].mNext)
{
if ((mEntries[i].mHashCode == hashCode) && (*(TKey*)&mEntries[i].mKey == key))
{
*valuePtr = *(TValue*)&mEntries[i].mValue;
RemoveIdx(bucket, i, last);
return true;
}
}
}
return false;
}
2020-03-14 16:27:58 -07:00
bool Remove(const TKey& key, const TValue& value)
{
if (mBuckets != NULL)
{
int_cosize hashCode = (int_cosize)BeefHash<TKey>()(key) & 0x7FFFFFFF;
int_cosize bucket = hashCode % (int_cosize)mAllocSize;
int_cosize last = -1;
for (int_cosize i = mBuckets[bucket]; i >= 0; last = i, i = mEntries[i].mNext)
{
if ((mEntries[i].mHashCode == hashCode) && (*(TKey*)&mEntries[i].mKey == key) &&
(*(TValue*)&mEntries[i].mValue == value))
{
RemoveIdx(bucket, i, last);
return true;
}
}
}
return false;
}
2019-08-23 11:56:54 -07:00
iterator Remove(const iterator& itr)
{
iterator nextItr = itr;
++nextItr;
auto& entry = mEntries[itr.mCurrentIdx];
BF_ASSERT(entry.mHashCode >= 0);
// We have to iterate through to mCurrentIdx so we can get 'last'
int_cosize hashCode = entry.mHashCode;
int_cosize bucket = hashCode % (int_cosize)mAllocSize;
int_cosize last = -1;
for (int_cosize i = mBuckets[bucket]; i >= 0; last = i, i = mEntries[i].mNext)
{
if ((mEntries[i].mHashCode == hashCode) && (i == itr.mCurrentIdx))
{
RemoveIdx(bucket, i, last);
return nextItr;
}
}
BF_FATAL("not found");
return nextItr;
}
void Clear()
{
if (mCount > 0)
{
for (int_cosize i = 0; i < mAllocSize; i++) mBuckets[i] = -1;
if (!std::is_pod<TKey>::value)
{
for (int_cosize i = 0; i < mCount; i++)
{
if (mEntries[i].mHashCode != -1)
((TKey*)&mEntries[i].mKey)->~TKey();
}
}
if (!std::is_pod<TValue>::value)
{
for (int_cosize i = 0; i < mCount; i++)
{
if (mEntries[i].mHashCode != -1)
((TValue*)&mEntries[i].mValue)->~TValue();
}
}
for (int_cosize i = 0; i < mCount; i++)
{
mEntries[i].mHashCode = -1;
}
mFreeList = -1;
mCount = 0;
mFreeCount = 0;
}
}
bool ContainsKey(const TKey& key)
{
return FindEntry(key) >= 0;
2020-03-14 16:27:58 -07:00
}
2019-08-23 11:56:54 -07:00
iterator begin() const
{
iterator itr((Dictionary*)this, 0);
++itr;
return itr;
}
iterator end() const
{
return iterator((Dictionary*)this, mCount + 1, -1);
}
};
#else
template <typename TKey, typename TValue>
class Dictionary : public std::unordered_map<TKey, TValue>
{
public:
public:
TValue* GetValuePtr(const TKey& key)
{
auto itr = find(key);
if (itr == end())
return NULL;
return &itr->second;
}
bool ContainsKey(const TKey& key)
{
return find(key) != end();
}
};
#endif
NS_BF_END;