mirror of
https://github.com/beefytech/Beef.git
synced 2025-06-08 11:38:21 +02:00
Improved hotswapping with extension modules
This commit is contained in:
parent
769036584a
commit
fd4fd43ce3
19 changed files with 836 additions and 232 deletions
|
@ -2184,6 +2184,7 @@ copy /y "$(OutDir)$(TargetName).lib" "$(SolutionDir)\BeefLibs\Beefy2D\dist\"</Co
|
||||||
<ClInclude Include="util\MappedFile.h" />
|
<ClInclude Include="util\MappedFile.h" />
|
||||||
<ClInclude Include="util\MathUtils.h" />
|
<ClInclude Include="util\MathUtils.h" />
|
||||||
<ClInclude Include="util\Matrix4.h" />
|
<ClInclude Include="util\Matrix4.h" />
|
||||||
|
<ClInclude Include="util\MultiDictionary.h" />
|
||||||
<ClInclude Include="util\MultiHashSet.h" />
|
<ClInclude Include="util\MultiHashSet.h" />
|
||||||
<ClInclude Include="util\PerfTimer.h" />
|
<ClInclude Include="util\PerfTimer.h" />
|
||||||
<ClInclude Include="util\Point.h" />
|
<ClInclude Include="util\Point.h" />
|
||||||
|
|
|
@ -1132,6 +1132,9 @@
|
||||||
<ClInclude Include="third_party\putty\wildcard.h">
|
<ClInclude Include="third_party\putty\wildcard.h">
|
||||||
<Filter>src\third_party\putty</Filter>
|
<Filter>src\third_party\putty</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClInclude Include="util\MultiDictionary.h">
|
||||||
|
<Filter>src\util</Filter>
|
||||||
|
</ClInclude>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<CustomBuild Include="third_party\libffi\i686-pc-cygwin\src\x86\win32.asm">
|
<CustomBuild Include="third_party\libffi\i686-pc-cygwin\src\x86\win32.asm">
|
||||||
|
|
|
@ -4,16 +4,16 @@
|
||||||
|
|
||||||
NS_BF_BEGIN;
|
NS_BF_BEGIN;
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
class AllocatorCLib
|
class AllocatorCLib
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
template <typename T>
|
||||||
T* allocate(intptr count)
|
T* allocate(intptr count)
|
||||||
{
|
{
|
||||||
return (T*)malloc(sizeof(T) * count);
|
return (T*)malloc(sizeof(T) * count);
|
||||||
}
|
}
|
||||||
|
|
||||||
void deallocate(T* ptr)
|
void deallocate(void* ptr)
|
||||||
{
|
{
|
||||||
free(ptr);
|
free(ptr);
|
||||||
}
|
}
|
||||||
|
@ -27,9 +27,14 @@ public:
|
||||||
{
|
{
|
||||||
free(ptr);
|
free(ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool deallocateAll()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T, typename TAlloc = AllocatorCLib<T> >
|
template <typename T, typename TAlloc = AllocatorCLib >
|
||||||
class ArrayBase : public TAlloc
|
class ArrayBase : public TAlloc
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -445,7 +450,7 @@ protected:
|
||||||
|
|
||||||
void SetBufferSize(intptr newSize)
|
void SetBufferSize(intptr newSize)
|
||||||
{
|
{
|
||||||
T* newVals = TAlloc::allocate(newSize);
|
T* newVals = TAlloc::allocate<T>(newSize);
|
||||||
if (this->mVals != NULL)
|
if (this->mVals != NULL)
|
||||||
{
|
{
|
||||||
if (this->mSize > 0)
|
if (this->mSize > 0)
|
||||||
|
@ -625,7 +630,7 @@ public:
|
||||||
{
|
{
|
||||||
intptr newSize = this->mAllocSize + this->mAllocSize / 2 + 1;
|
intptr newSize = this->mAllocSize + this->mAllocSize / 2 + 1;
|
||||||
|
|
||||||
T* newVals = TAlloc::allocate(newSize);
|
T* newVals = TAlloc::allocate<T>(newSize);
|
||||||
if (this->mVals != NULL)
|
if (this->mVals != NULL)
|
||||||
{
|
{
|
||||||
if (idx > 0) // Copy left of idx
|
if (idx > 0) // Copy left of idx
|
||||||
|
@ -653,7 +658,7 @@ public:
|
||||||
{
|
{
|
||||||
intptr newSize = BF_MAX(this->mSize + size, this->mAllocSize + this->mAllocSize / 2 + 1);
|
intptr newSize = BF_MAX(this->mSize + size, this->mAllocSize + this->mAllocSize / 2 + 1);
|
||||||
|
|
||||||
T* newVals = TAlloc::allocate(newSize);
|
T* newVals = TAlloc::allocate<T>(newSize);
|
||||||
if (this->mVals != NULL)
|
if (this->mVals != NULL)
|
||||||
{
|
{
|
||||||
if (idx > 0) // Copy left of idx
|
if (idx > 0) // Copy left of idx
|
||||||
|
@ -682,7 +687,7 @@ public:
|
||||||
{
|
{
|
||||||
intptr newSize = BF_MAX(this->mSize + count, this->mAllocSize + this->mAllocSize / 2 + 1);
|
intptr newSize = BF_MAX(this->mSize + count, this->mAllocSize + this->mAllocSize / 2 + 1);
|
||||||
|
|
||||||
T* newVals = TAlloc::allocate(newSize);
|
T* newVals = TAlloc::allocate<T>(newSize);
|
||||||
if (this->mVals != NULL)
|
if (this->mVals != NULL)
|
||||||
{
|
{
|
||||||
if (idx > 0) // Copy left of idx
|
if (idx > 0) // Copy left of idx
|
||||||
|
@ -764,7 +769,7 @@ public:
|
||||||
protected:
|
protected:
|
||||||
void SetBufferSize(intptr newSize)
|
void SetBufferSize(intptr newSize)
|
||||||
{
|
{
|
||||||
T* newVals = TAlloc::allocate(newSize);
|
T* newVals = TAlloc::allocate<T>(newSize);
|
||||||
if (this->mVals != NULL)
|
if (this->mVals != NULL)
|
||||||
{
|
{
|
||||||
if (this->mSize > 0)
|
if (this->mSize > 0)
|
||||||
|
@ -928,7 +933,7 @@ public:
|
||||||
{
|
{
|
||||||
intptr newSize = this->mAllocSize + this->mAllocSize / 2 + 1;
|
intptr newSize = this->mAllocSize + this->mAllocSize / 2 + 1;
|
||||||
|
|
||||||
T* newVals = TAlloc::allocate(newSize);
|
T* newVals = TAlloc::allocate<T>(newSize);
|
||||||
if (this->mVals != NULL)
|
if (this->mVals != NULL)
|
||||||
{
|
{
|
||||||
if (idx > 0) // Copy left of idx
|
if (idx > 0) // Copy left of idx
|
||||||
|
@ -956,7 +961,7 @@ public:
|
||||||
{
|
{
|
||||||
intptr newSize = BF_MAX(this->mSize + size, this->mAllocSize + this->mAllocSize / 2 + 1);
|
intptr newSize = BF_MAX(this->mSize + size, this->mAllocSize + this->mAllocSize / 2 + 1);
|
||||||
|
|
||||||
T* newVals = TAlloc::allocate(newSize);
|
T* newVals = TAlloc::allocate<T>(newSize);
|
||||||
if (this->mVals != NULL)
|
if (this->mVals != NULL)
|
||||||
{
|
{
|
||||||
if (idx > 0) // Copy left of idx
|
if (idx > 0) // Copy left of idx
|
||||||
|
@ -984,7 +989,7 @@ public:
|
||||||
{
|
{
|
||||||
intptr newSize = BF_MAX(this->mSize + size, this->mAllocSize + this->mAllocSize / 2 + 1);
|
intptr newSize = BF_MAX(this->mSize + size, this->mAllocSize + this->mAllocSize / 2 + 1);
|
||||||
|
|
||||||
T* newVals = TAlloc::allocate(newSize);
|
T* newVals = TAlloc::allocate<T>(newSize);
|
||||||
if (this->mVals != NULL)
|
if (this->mVals != NULL)
|
||||||
{
|
{
|
||||||
if (idx > 0) // Copy left of idx
|
if (idx > 0) // Copy left of idx
|
||||||
|
@ -1102,7 +1107,7 @@ public:
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T, typename TAlloc = AllocatorCLib<T> >
|
template <typename T, typename TAlloc = AllocatorCLib >
|
||||||
class Array : public ArrayImpl<T, TAlloc, std::is_pod<T>::value>
|
class Array : public ArrayImpl<T, TAlloc, std::is_pod<T>::value>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
|
@ -255,23 +255,24 @@ class BumpAllocator : public BumpAllocatorT<0x2000>
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T, int ALLOC_SIZE = 0x2000>
|
template <int ALLOC_SIZE = 0x2000>
|
||||||
class AllocatorBump
|
class AllocatorBumpT
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
BumpAllocatorT<ALLOC_SIZE>* mAlloc;
|
BumpAllocatorT<ALLOC_SIZE>* mAlloc;
|
||||||
|
|
||||||
AllocatorBump()
|
AllocatorBumpT()
|
||||||
{
|
{
|
||||||
mAlloc = NULL;
|
mAlloc = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
T* allocate(intptr count)
|
T* allocate(intptr count)
|
||||||
{
|
{
|
||||||
return (T*)mAlloc->AllocBytes((int)(sizeof(T) * count), alignof(T));
|
return (T*)mAlloc->AllocBytes((int)(sizeof(T) * count), alignof(T));
|
||||||
}
|
}
|
||||||
|
|
||||||
void deallocate(T* ptr)
|
void deallocate(void* ptr)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -286,26 +287,9 @@ public:
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <int ALLOC_SIZE = 0x2000>
|
class AllocatorBump : public AllocatorBumpT<0x2000>
|
||||||
class RawAllocatorBump
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
BumpAllocatorT<ALLOC_SIZE>* mAlloc;
|
|
||||||
|
|
||||||
RawAllocatorBump()
|
|
||||||
{
|
|
||||||
mAlloc = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
void* rawAllocate(intptr size)
|
|
||||||
{
|
|
||||||
return mAlloc->AllocBytes(size, 16);
|
|
||||||
}
|
|
||||||
|
|
||||||
void rawDeallocate(void* ptr)
|
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
NS_BF_END
|
NS_BF_END
|
||||||
|
|
|
@ -8,7 +8,7 @@ NS_BF_BEGIN;
|
||||||
#define DEQUE_IDX(i) (this->mVals[((i) + this->mOffset) % this->mAllocSize])
|
#define DEQUE_IDX(i) (this->mVals[((i) + this->mOffset) % this->mAllocSize])
|
||||||
#define DEQUE_IDX_ON(arr, i) ((arr).mVals[((i) + (arr).mOffset) % (arr).mAllocSize])
|
#define DEQUE_IDX_ON(arr, i) ((arr).mVals[((i) + (arr).mOffset) % (arr).mAllocSize])
|
||||||
|
|
||||||
template <typename T, typename TAlloc = AllocatorCLib<T> >
|
template <typename T, typename TAlloc = AllocatorCLib >
|
||||||
class DequeBase : public TAlloc
|
class DequeBase : public TAlloc
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -370,7 +370,7 @@ protected:
|
||||||
|
|
||||||
void Grow(intptr newSize)
|
void Grow(intptr newSize)
|
||||||
{
|
{
|
||||||
T* newVals = TAlloc::allocate(newSize);
|
T* newVals = TAlloc::allocate<T>(newSize);
|
||||||
if (this->mVals != NULL)
|
if (this->mVals != NULL)
|
||||||
{
|
{
|
||||||
if (this->mSize > 0)
|
if (this->mSize > 0)
|
||||||
|
@ -707,7 +707,7 @@ class DequeImpl<T, TAlloc, true> : public DequeBase<T, TAlloc>
|
||||||
protected:
|
protected:
|
||||||
void Grow(intptr newSize)
|
void Grow(intptr newSize)
|
||||||
{
|
{
|
||||||
T* newVals = TAlloc::allocate(newSize);
|
T* newVals = TAlloc::allocate<T>(newSize);
|
||||||
if (this->mVals != NULL)
|
if (this->mVals != NULL)
|
||||||
{
|
{
|
||||||
if (this->mSize > 0)
|
if (this->mSize > 0)
|
||||||
|
@ -1043,7 +1043,7 @@ public:
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T, typename TAlloc = AllocatorCLib<T> >
|
template <typename T, typename TAlloc = AllocatorCLib >
|
||||||
class Deque : public DequeImpl<T, TAlloc, std::is_pod<T>::value>
|
class Deque : public DequeImpl<T, TAlloc, std::is_pod<T>::value>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
|
@ -9,7 +9,7 @@ NS_BF_BEGIN;
|
||||||
|
|
||||||
#ifdef NEW_DICTIONAY
|
#ifdef NEW_DICTIONAY
|
||||||
|
|
||||||
template <typename TKey, typename TValue, typename TAlloc = AllocatorCLib<TKey> >
|
template <typename TKey, typename TValue, typename TAlloc = AllocatorCLib >
|
||||||
class Dictionary : public TAlloc
|
class Dictionary : public TAlloc
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
|
|
||||||
NS_BF_BEGIN;
|
NS_BF_BEGIN;
|
||||||
|
|
||||||
template <typename TKey, typename TAlloc = AllocatorCLib<TKey> >
|
template <typename TKey, typename TAlloc = AllocatorCLib >
|
||||||
class HashSet : public TAlloc
|
class HashSet : public TAlloc
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
523
BeefySysLib/util/MultiDictionary.h
Normal file
523
BeefySysLib/util/MultiDictionary.h
Normal file
|
@ -0,0 +1,523 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "../Common.h"
|
||||||
|
#include "Array.h"
|
||||||
|
|
||||||
|
NS_BF_BEGIN;
|
||||||
|
|
||||||
|
struct MultiDictionaryFuncs : AllocatorCLib
|
||||||
|
{
|
||||||
|
template <typename T>
|
||||||
|
size_t GetHash(const T& value)
|
||||||
|
{
|
||||||
|
return BeefHash<T>()(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
bool Matches(const T& lhs, const T& rhs)
|
||||||
|
{
|
||||||
|
return lhs == rhs;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename TKey, typename TValue, typename TFuncs = MultiDictionaryFuncs>
|
||||||
|
class MultiDictionary : public TFuncs
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
struct Entry
|
||||||
|
{
|
||||||
|
TKey mKey;
|
||||||
|
TValue mValue;
|
||||||
|
int mNext;
|
||||||
|
int mHashCode;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct EntryRef
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
MultiDictionary* mSet;
|
||||||
|
int mIndex;
|
||||||
|
|
||||||
|
public:
|
||||||
|
EntryRef()
|
||||||
|
{
|
||||||
|
mSet = NULL;
|
||||||
|
mIndex = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
EntryRef(MultiDictionary* set, int index)
|
||||||
|
{
|
||||||
|
mSet = set;
|
||||||
|
mIndex = index;
|
||||||
|
}
|
||||||
|
|
||||||
|
Entry* operator*()
|
||||||
|
{
|
||||||
|
return &this->mSet->mEntries[this->mIndex];
|
||||||
|
}
|
||||||
|
|
||||||
|
Entry* operator->()
|
||||||
|
{
|
||||||
|
return &this->mSet->mEntries[this->mIndex];
|
||||||
|
}
|
||||||
|
|
||||||
|
operator bool() const
|
||||||
|
{
|
||||||
|
return this->mIndex != -1;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Iterator
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
MultiDictionary* mSet;
|
||||||
|
int mCurEntry;
|
||||||
|
int mCurBucket;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Iterator(MultiDictionary* set)
|
||||||
|
{
|
||||||
|
this->mSet = set;
|
||||||
|
this->mCurBucket = 0;
|
||||||
|
this->mCurEntry = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
Iterator& operator++()
|
||||||
|
{
|
||||||
|
if (this->mCurEntry != -1)
|
||||||
|
{
|
||||||
|
this->mCurEntry = this->mSet->mEntries[this->mCurEntry].mNext;
|
||||||
|
if (this->mCurEntry != -1)
|
||||||
|
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 != -1)
|
||||||
|
return *this;
|
||||||
|
this->mCurBucket++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return *this; // At end
|
||||||
|
}
|
||||||
|
|
||||||
|
TKey GetKey()
|
||||||
|
{
|
||||||
|
return this->mSet->mEntries[this->mCurEntry].mKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
TValue GetValue()
|
||||||
|
{
|
||||||
|
return this->mSet->mEntries[this->mCurEntry].mValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator!=(const Iterator& itr) const
|
||||||
|
{
|
||||||
|
return ((itr.mCurEntry != this->mCurEntry) || (itr.mCurBucket != this->mCurBucket));
|
||||||
|
}
|
||||||
|
|
||||||
|
operator bool() const
|
||||||
|
{
|
||||||
|
return this->mCurEntry != -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MoveToNextHashMatch()
|
||||||
|
{
|
||||||
|
int wantHash = this->mSet->mEntries[this->mCurEntry].mHashCode;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
this->mCurEntry = this->mSet->mEntries[this->mCurEntry].mNext;
|
||||||
|
} while ((this->mCurEntry != -1) && (this->mSet->mEntries[this->mCurEntry].mHashCode != wantHash));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
int GetPrimeish(int min)
|
||||||
|
{
|
||||||
|
// This is a minimal effort to help address-aligned dataa
|
||||||
|
return (min | 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
int ExpandSize(int oldSize)
|
||||||
|
{
|
||||||
|
int 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 ResizeEntries()
|
||||||
|
{
|
||||||
|
ResizeEntries(ExpandSize(mCount));
|
||||||
|
}
|
||||||
|
|
||||||
|
void ResizeEntries(int newSize)
|
||||||
|
{
|
||||||
|
BF_ASSERT(newSize >= mAllocSize);
|
||||||
|
Entry* newEntries = TFuncs::allocate<Entry>(newSize);
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
TFuncs::deallocate(mEntries);
|
||||||
|
|
||||||
|
mEntries = newEntries;
|
||||||
|
mAllocSize = (int)newSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FreeIdx(int entryIdx)
|
||||||
|
{
|
||||||
|
this->mEntries[entryIdx].mNext = this->mFreeList;
|
||||||
|
this->mFreeList = entryIdx;
|
||||||
|
this->mFreeCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
int AllocEntry()
|
||||||
|
{
|
||||||
|
int index;
|
||||||
|
if (this->mFreeCount > 0)
|
||||||
|
{
|
||||||
|
index = this->mFreeList;
|
||||||
|
this->mFreeList = this->mEntries[index].mNext;
|
||||||
|
this->mFreeCount--;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (this->mCount == this->mAllocSize)
|
||||||
|
ResizeEntries();
|
||||||
|
index = mCount;
|
||||||
|
this->mCount++;
|
||||||
|
}
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
int* mHashHeads;
|
||||||
|
int mAllocSize;
|
||||||
|
Entry* mEntries;
|
||||||
|
int mFreeList;
|
||||||
|
int mFreeCount;
|
||||||
|
|
||||||
|
static const int cDefaultHashSize = 17;
|
||||||
|
int mHashSize;
|
||||||
|
int mCount;
|
||||||
|
|
||||||
|
MultiDictionary()
|
||||||
|
{
|
||||||
|
this->mHashHeads = NULL;
|
||||||
|
this->mHashSize = cDefaultHashSize;
|
||||||
|
this->mEntries = NULL;
|
||||||
|
this->mAllocSize = 0;
|
||||||
|
this->mCount = 0;
|
||||||
|
this->mFreeList = -1;
|
||||||
|
this->mFreeCount = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
~MultiDictionary()
|
||||||
|
{
|
||||||
|
this->Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void EnsureFreeCount(int wantFreeCount)
|
||||||
|
{
|
||||||
|
int freeCount = mFreeCount + (mAllocSize - mCount);
|
||||||
|
if (freeCount >= wantFreeCount)
|
||||||
|
return;
|
||||||
|
ResizeEntries(BF_MAX(ExpandSize(mCount), mAllocSize + wantFreeCount - freeCount));
|
||||||
|
}
|
||||||
|
|
||||||
|
int GetCount() const
|
||||||
|
{
|
||||||
|
return mCount - mFreeCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
int size() const
|
||||||
|
{
|
||||||
|
return mCount - mFreeCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
EntryRef AddRaw(int hash)
|
||||||
|
{
|
||||||
|
if (this->mHashHeads == NULL)
|
||||||
|
{
|
||||||
|
this->mHashHeads = TFuncs::allocate<int>(mHashSize);
|
||||||
|
memset(this->mHashHeads, -1, sizeof(int) * mHashSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
int index = AllocEntry();
|
||||||
|
int hashIdx = (hash & 0x7FFFFFFF) % this->mHashSize;
|
||||||
|
int headEntry = this->mHashHeads[hashIdx];
|
||||||
|
|
||||||
|
Entry* newEntry = &mEntries[index];
|
||||||
|
newEntry->mValue = T();
|
||||||
|
newEntry->mNext = headEntry;
|
||||||
|
newEntry->mHashCode = hash;
|
||||||
|
|
||||||
|
mHashHeads[hashIdx] = index;
|
||||||
|
|
||||||
|
return EntryRef(this, index);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Add(TKey key, TValue value)
|
||||||
|
{
|
||||||
|
if (this->mHashHeads == NULL)
|
||||||
|
{
|
||||||
|
this->mHashHeads = TFuncs::allocate<int>(mHashSize);
|
||||||
|
memset(this->mHashHeads, -1, sizeof(int) * mHashSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
int index = AllocEntry();
|
||||||
|
int hash = TFuncs::GetHash(key);
|
||||||
|
int hashIdx = (hash & 0x7FFFFFFF) % this->mHashSize;
|
||||||
|
int headEntry = this->mHashHeads[hashIdx];
|
||||||
|
|
||||||
|
Entry* newEntry = &mEntries[index];
|
||||||
|
newEntry->mKey = key;
|
||||||
|
newEntry->mValue = value;
|
||||||
|
newEntry->mNext = headEntry;
|
||||||
|
newEntry->mHashCode = hash;
|
||||||
|
|
||||||
|
mHashHeads[hashIdx] = index;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AddAfter(TKey key, TValue value, Entry* afterEntry)
|
||||||
|
{
|
||||||
|
int hash = TFuncs::GetHash(key);
|
||||||
|
int hashIdx = (hash & 0x7FFFFFFF) % this->mHashSize;
|
||||||
|
BF_ASSERT(hash == afterEntry->mHashCode);
|
||||||
|
|
||||||
|
int index = AllocEntry();
|
||||||
|
Entry* newEntry = &mEntries[index];
|
||||||
|
newEntry->mKey = key;
|
||||||
|
newEntry->mValue = value;
|
||||||
|
newEntry->mNext = afterEntry->mNext;
|
||||||
|
newEntry->mHashCode = hash;
|
||||||
|
|
||||||
|
afterEntry->mNext = index;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Rehash(int newHashSize)
|
||||||
|
{
|
||||||
|
auto newHashHeads = TFuncs::allocate<int>(newHashSize);
|
||||||
|
memset(newHashHeads, -1, sizeof(int) * newHashSize);
|
||||||
|
|
||||||
|
if (mHashHeads != NULL)
|
||||||
|
{
|
||||||
|
SizedArray<int, 1024> entryList;
|
||||||
|
for (int hashIdx = 0; hashIdx < mHashSize; hashIdx++)
|
||||||
|
{
|
||||||
|
int checkEntryIdx = mHashHeads[hashIdx];
|
||||||
|
if (checkEntryIdx != -1)
|
||||||
|
{
|
||||||
|
// We want to keep elements with equal hashes in their insert order so we need to
|
||||||
|
// iterate through the linked list in reverse
|
||||||
|
entryList.Clear();
|
||||||
|
|
||||||
|
while (checkEntryIdx != -1)
|
||||||
|
{
|
||||||
|
entryList.Add(checkEntryIdx);
|
||||||
|
checkEntryIdx = mEntries[checkEntryIdx].mNext;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = (int)entryList.mSize - 1; i >= 0; i--)
|
||||||
|
{
|
||||||
|
int checkEntryIdx = entryList[i];
|
||||||
|
auto checkEntry = &mEntries[checkEntryIdx];
|
||||||
|
int newHashIdx = (checkEntry->mHashCode & 0x7FFFFFFF) % newHashSize;
|
||||||
|
checkEntry->mNext = newHashHeads[newHashIdx];
|
||||||
|
newHashHeads[newHashIdx] = checkEntryIdx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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, TKey* outKey, TValue* outValue)
|
||||||
|
{
|
||||||
|
if (mHashHeads == NULL)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
this->CheckRehash();
|
||||||
|
|
||||||
|
int hash = TFuncs::GetHash(key);
|
||||||
|
int hashIdx = (hash & 0x7FFFFFFF) % this->mHashSize;
|
||||||
|
int checkEntryIdx = this->mHashHeads[hashIdx];
|
||||||
|
while (checkEntryIdx != -1)
|
||||||
|
{
|
||||||
|
Entry* checkEntry = &mEntries[checkEntryIdx];
|
||||||
|
if ((checkEntry->mHashCode == hash) && (TFuncs::Matches(key, checkEntry->mKey)))
|
||||||
|
{
|
||||||
|
if (outKey != NULL)
|
||||||
|
*outKey = checkEntry->mKey;
|
||||||
|
if (outValue != NULL)
|
||||||
|
*outValue = checkEntry->mValue;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
checkEntryIdx = 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;
|
||||||
|
int checkEntryIdx = this->mHashHeads[hashIdx];
|
||||||
|
while (checkEntryIdx != -1)
|
||||||
|
{
|
||||||
|
auto checkEntry = &this->mEntries[checkEntryIdx];
|
||||||
|
if ((checkEntry->mHashCode == hash) && (TFuncs::Matches(key, checkEntry->mKey)))
|
||||||
|
{
|
||||||
|
Iterator itr(this);
|
||||||
|
itr.mCurEntry = checkEntryIdx;
|
||||||
|
itr.mCurBucket = hashIdx;
|
||||||
|
return itr;
|
||||||
|
}
|
||||||
|
checkEntryIdx = 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;
|
||||||
|
|
||||||
|
int* srcCheckEntryPtr = &this->mHashHeads[hashIdx];
|
||||||
|
int checkEntryIdx = *srcCheckEntryPtr;
|
||||||
|
while (checkEntryIdx != -1)
|
||||||
|
{
|
||||||
|
auto checkEntry = &mEntries[checkEntryIdx];
|
||||||
|
if ((checkEntry->mHashCode == hash) && (TFuncs::Matches(key, checkEntry->mKey)))
|
||||||
|
{
|
||||||
|
*srcCheckEntryPtr = checkEntry->mNext;
|
||||||
|
FreeIdx(checkEntryIdx);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
srcCheckEntryPtr = &checkEntry->mNext;
|
||||||
|
checkEntryIdx = checkEntry->mNext;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Iterator Erase(const Iterator& itr)
|
||||||
|
{
|
||||||
|
Iterator next = itr;
|
||||||
|
++next;
|
||||||
|
|
||||||
|
bool found = false;
|
||||||
|
|
||||||
|
auto entryIdx = itr.mCurEntry;
|
||||||
|
auto entry = &mEntries[entryIdx];
|
||||||
|
int hashIdx = (entry->mHashCode & 0x7FFFFFFF) % this->mHashSize;
|
||||||
|
|
||||||
|
int* srcCheckEntryPtr = &this->mHashHeads[hashIdx];
|
||||||
|
int checkEntryIdx = *srcCheckEntryPtr;
|
||||||
|
while (checkEntryIdx != -1)
|
||||||
|
{
|
||||||
|
auto checkEntry = &mEntries[checkEntryIdx];
|
||||||
|
if (checkEntryIdx == itr.mCurEntry)
|
||||||
|
{
|
||||||
|
*srcCheckEntryPtr = checkEntry->mNext;
|
||||||
|
found = true;
|
||||||
|
}
|
||||||
|
srcCheckEntryPtr = &checkEntry->mNext;
|
||||||
|
checkEntryIdx = checkEntry->mNext;
|
||||||
|
}
|
||||||
|
|
||||||
|
BF_ASSERT(found);
|
||||||
|
FreeIdx(entryIdx);
|
||||||
|
|
||||||
|
return next;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Clear()
|
||||||
|
{
|
||||||
|
if (!TFuncs::deallocateAll())
|
||||||
|
{
|
||||||
|
auto itr = begin();
|
||||||
|
auto endItr = end();
|
||||||
|
while (itr != endItr)
|
||||||
|
{
|
||||||
|
auto entry = itr.mCurEntry;
|
||||||
|
++itr;
|
||||||
|
FreeIdx(entry);
|
||||||
|
}
|
||||||
|
TFuncs::deallocate(this->mHashHeads);
|
||||||
|
TFuncs::deallocate(this->mEntries);
|
||||||
|
}
|
||||||
|
|
||||||
|
this->mHashSize = cDefaultHashSize;
|
||||||
|
this->mHashHeads = NULL;
|
||||||
|
this->mEntries = NULL;
|
||||||
|
this->mCount = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Iterator begin()
|
||||||
|
{
|
||||||
|
return ++Iterator(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
Iterator end()
|
||||||
|
{
|
||||||
|
Iterator itr(this);
|
||||||
|
itr.mCurBucket = this->mHashSize;
|
||||||
|
return itr;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
NS_BF_END;
|
|
@ -30,7 +30,7 @@ struct MultiHashSetFuncs
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T, typename TFuncs = AllocatorCLib<T> >
|
template <typename T, typename TFuncs = AllocatorCLib >
|
||||||
class MultiHashSet : public TFuncs
|
class MultiHashSet : public TFuncs
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -180,7 +180,7 @@ protected:
|
||||||
void ResizeEntries(int newSize)
|
void ResizeEntries(int newSize)
|
||||||
{
|
{
|
||||||
BF_ASSERT(newSize >= mAllocSize);
|
BF_ASSERT(newSize >= mAllocSize);
|
||||||
Entry* newEntries = (Entry*)TFuncs::Allocate(sizeof(Entry) * newSize, alignof(Entry));
|
Entry* newEntries = TFuncs::allocate<Entry>(newSize);
|
||||||
|
|
||||||
for (int i = 0; i < mCount; i++)
|
for (int i = 0; i < mCount; i++)
|
||||||
{
|
{
|
||||||
|
@ -195,7 +195,7 @@ protected:
|
||||||
newEntries[i].mHashCode = -1;
|
newEntries[i].mHashCode = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
TFuncs::Deallocate(mEntries);
|
TFuncs::deallocate(mEntries);
|
||||||
|
|
||||||
mEntries = newEntries;
|
mEntries = newEntries;
|
||||||
mAllocSize = (int)newSize;
|
mAllocSize = (int)newSize;
|
||||||
|
@ -276,7 +276,7 @@ public:
|
||||||
{
|
{
|
||||||
if (this->mHashHeads == NULL)
|
if (this->mHashHeads == NULL)
|
||||||
{
|
{
|
||||||
this->mHashHeads = (int*)TFuncs::Allocate(sizeof(int) * mHashSize, alignof(int));
|
this->mHashHeads = TFuncs::allocate<int>(mHashSize);
|
||||||
memset(this->mHashHeads, -1, sizeof(int) * mHashSize);
|
memset(this->mHashHeads, -1, sizeof(int) * mHashSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -298,7 +298,7 @@ public:
|
||||||
{
|
{
|
||||||
if (this->mHashHeads == NULL)
|
if (this->mHashHeads == NULL)
|
||||||
{
|
{
|
||||||
this->mHashHeads = (int*)TFuncs::Allocate(sizeof(int) * mHashSize, alignof(int));
|
this->mHashHeads = TFuncs::allocate<int>(mHashSize);
|
||||||
memset(this->mHashHeads, -1, sizeof(int) * mHashSize);
|
memset(this->mHashHeads, -1, sizeof(int) * mHashSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -332,7 +332,7 @@ public:
|
||||||
|
|
||||||
void Rehash(int newHashSize)
|
void Rehash(int newHashSize)
|
||||||
{
|
{
|
||||||
auto newHashHeads = (int*)TFuncs::Allocate(sizeof(int) * newHashSize, alignof(int));
|
auto newHashHeads = TFuncs::allocate<int>(newHashSize);
|
||||||
memset(newHashHeads, -1, sizeof(int) * newHashSize);
|
memset(newHashHeads, -1, sizeof(int) * newHashSize);
|
||||||
|
|
||||||
if (mHashHeads != NULL)
|
if (mHashHeads != NULL)
|
||||||
|
@ -364,7 +364,7 @@ public:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TFuncs::Deallocate(mHashHeads);
|
TFuncs::deallocate(mHashHeads);
|
||||||
}
|
}
|
||||||
mHashHeads = newHashHeads;
|
mHashHeads = newHashHeads;
|
||||||
mHashSize = newHashSize;
|
mHashSize = newHashSize;
|
||||||
|
@ -492,7 +492,7 @@ public:
|
||||||
|
|
||||||
void Clear()
|
void Clear()
|
||||||
{
|
{
|
||||||
if (!TFuncs::DeallocateAll())
|
if (!TFuncs::deallocateAll())
|
||||||
{
|
{
|
||||||
auto itr = begin();
|
auto itr = begin();
|
||||||
auto endItr = end();
|
auto endItr = end();
|
||||||
|
@ -502,8 +502,8 @@ public:
|
||||||
++itr;
|
++itr;
|
||||||
FreeIdx(entry);
|
FreeIdx(entry);
|
||||||
}
|
}
|
||||||
TFuncs::Deallocate(this->mHashHeads);
|
TFuncs::deallocate(this->mHashHeads);
|
||||||
TFuncs::Deallocate(this->mEntries);
|
TFuncs::deallocate(this->mEntries);
|
||||||
}
|
}
|
||||||
|
|
||||||
this->mHashSize = cDefaultHashSize;
|
this->mHashSize = cDefaultHashSize;
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
NS_BF_BEGIN;
|
NS_BF_BEGIN;
|
||||||
|
|
||||||
template <typename T, typename TAlloc = AllocatorCLib<T> >
|
template <typename T, typename TAlloc = AllocatorCLib >
|
||||||
class SizedArrayBase : protected TAlloc
|
class SizedArrayBase : protected TAlloc
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -378,7 +378,7 @@ protected:
|
||||||
|
|
||||||
void Grow(intptr newSize)
|
void Grow(intptr newSize)
|
||||||
{
|
{
|
||||||
T* newVals = TAlloc::allocate(newSize);
|
T* newVals = TAlloc::allocate<T>(newSize);
|
||||||
if (this->mVals != NULL)
|
if (this->mVals != NULL)
|
||||||
{
|
{
|
||||||
if (this->mSize > 0)
|
if (this->mSize > 0)
|
||||||
|
@ -533,7 +533,7 @@ public:
|
||||||
{
|
{
|
||||||
intptr newSize = this->mAllocSize + this->mAllocSize / 2 + 1;
|
intptr newSize = this->mAllocSize + this->mAllocSize / 2 + 1;
|
||||||
|
|
||||||
T* newVals = TAlloc::allocate(newSize);
|
T* newVals = TAlloc::allocate<T>(newSize);
|
||||||
if (this->mVals != NULL)
|
if (this->mVals != NULL)
|
||||||
{
|
{
|
||||||
if (idx > 0) // Copy left of idx
|
if (idx > 0) // Copy left of idx
|
||||||
|
@ -562,7 +562,7 @@ public:
|
||||||
{
|
{
|
||||||
intptr newSize = BF_MAX(this->mSize + size, this->mAllocSize + this->mAllocSize / 2 + 1);
|
intptr newSize = BF_MAX(this->mSize + size, this->mAllocSize + this->mAllocSize / 2 + 1);
|
||||||
|
|
||||||
T* newVals = TAlloc::allocate(newSize);
|
T* newVals = TAlloc::allocate<T>(newSize);
|
||||||
if (this->mVals != NULL)
|
if (this->mVals != NULL)
|
||||||
{
|
{
|
||||||
if (idx > 0) // Copy left of idx
|
if (idx > 0) // Copy left of idx
|
||||||
|
@ -638,7 +638,7 @@ public:
|
||||||
protected:
|
protected:
|
||||||
void Grow(intptr newSize)
|
void Grow(intptr newSize)
|
||||||
{
|
{
|
||||||
T* newVals = TAlloc::allocate(newSize);
|
T* newVals = TAlloc::allocate<T>(newSize);
|
||||||
if (this->mVals != NULL)
|
if (this->mVals != NULL)
|
||||||
{
|
{
|
||||||
if (this->mSize > 0)
|
if (this->mSize > 0)
|
||||||
|
@ -761,7 +761,7 @@ public:
|
||||||
{
|
{
|
||||||
intptr newSize = this->mAllocSize + this->mAllocSize / 2 + 1;
|
intptr newSize = this->mAllocSize + this->mAllocSize / 2 + 1;
|
||||||
|
|
||||||
T* newVals = TAlloc::allocate(newSize);
|
T* newVals = TAlloc::allocate<T>(newSize);
|
||||||
if (this->mVals != NULL)
|
if (this->mVals != NULL)
|
||||||
{
|
{
|
||||||
if (idx > 0) // Copy left of idx
|
if (idx > 0) // Copy left of idx
|
||||||
|
@ -790,7 +790,7 @@ public:
|
||||||
{
|
{
|
||||||
intptr newSize = BF_MAX(this->mSize + size, this->mAllocSize + this->mAllocSize / 2 + 1);
|
intptr newSize = BF_MAX(this->mSize + size, this->mAllocSize + this->mAllocSize / 2 + 1);
|
||||||
|
|
||||||
T* newVals = TAlloc::allocate(newSize);
|
T* newVals = TAlloc::allocate<T>(newSize);
|
||||||
if (this->mVals != NULL)
|
if (this->mVals != NULL)
|
||||||
{
|
{
|
||||||
if (idx > 0) // Copy left of idx
|
if (idx > 0) // Copy left of idx
|
||||||
|
@ -854,14 +854,14 @@ public:
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T, typename TAlloc = AllocatorCLib<T> >
|
template <typename T, typename TAlloc = AllocatorCLib >
|
||||||
class SizedArrayImpl : public SizedArrayBaseT<T, TAlloc, std::is_pod<T>::value>
|
class SizedArrayImpl : public SizedArrayBaseT<T, TAlloc, std::is_pod<T>::value>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
typedef SizedArrayBaseT<T, TAlloc, std::is_pod<T>::value> _Base;
|
typedef SizedArrayBaseT<T, TAlloc, std::is_pod<T>::value> _Base;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T, int TInternalSize, typename TAlloc = AllocatorCLib<T> >
|
template <typename T, int TInternalSize, typename TAlloc = AllocatorCLib >
|
||||||
class SizedArray : public SizedArrayImpl<T, TAlloc>
|
class SizedArray : public SizedArrayImpl<T, TAlloc>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
|
@ -1063,8 +1063,8 @@ public:
|
||||||
BfIRBlock mIRHeadBlock;
|
BfIRBlock mIRHeadBlock;
|
||||||
BfIRBlock mIRInitBlock;
|
BfIRBlock mIRInitBlock;
|
||||||
BfIRBlock mIREntryBlock;
|
BfIRBlock mIREntryBlock;
|
||||||
Array<BfLocalVariable*, AllocatorBump<BfLocalVariable*> > mLocals;
|
Array<BfLocalVariable*, AllocatorBump> mLocals;
|
||||||
HashSet<BfLocalVarEntry, AllocatorBump<BfLocalVariable*> > mLocalVarSet;
|
HashSet<BfLocalVarEntry, AllocatorBump> mLocalVarSet;
|
||||||
Array<BfLocalMethod*> mLocalMethods;
|
Array<BfLocalMethod*> mLocalMethods;
|
||||||
Dictionary<String, BfLocalMethod*> mLocalMethodMap;
|
Dictionary<String, BfLocalMethod*> mLocalMethodMap;
|
||||||
Dictionary<String, BfLocalMethod*> mLocalMethodCache; // So any lambda 'capturing' and 'processing' stages use the same local method
|
Dictionary<String, BfLocalMethod*> mLocalMethodCache; // So any lambda 'capturing' and 'processing' stages use the same local method
|
||||||
|
|
|
@ -2690,11 +2690,7 @@ public:
|
||||||
void ReportMemory(MemReporter* memReporter);
|
void ReportMemory(MemReporter* memReporter);
|
||||||
};
|
};
|
||||||
|
|
||||||
class BfResolvedTypeSetFuncs : public MultiHashSetFuncs
|
class BfResolvedTypeSet : public MultiHashSet<BfType*, AllocatorCLib>
|
||||||
{
|
|
||||||
};
|
|
||||||
|
|
||||||
class BfResolvedTypeSet : public MultiHashSet<BfType*, BfResolvedTypeSetFuncs>
|
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
enum BfHashFlags
|
enum BfHashFlags
|
||||||
|
@ -2749,7 +2745,7 @@ public:
|
||||||
BfTypeDef* ResolveToTypeDef(BfTypeReference* typeReference, BfType** outType = NULL);
|
BfTypeDef* ResolveToTypeDef(BfTypeReference* typeReference, BfType** outType = NULL);
|
||||||
};
|
};
|
||||||
|
|
||||||
class Iterator : public MultiHashSet<BfType*, BfResolvedTypeSetFuncs>::Iterator
|
class Iterator : public MultiHashSet<BfType*, AllocatorCLib>::Iterator
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Iterator(MultiHashSet* set) : MultiHashSet::Iterator(set)
|
Iterator(MultiHashSet* set) : MultiHashSet::Iterator(set)
|
||||||
|
|
|
@ -1254,7 +1254,7 @@ public:
|
||||||
bool HasCustomAttributes();
|
bool HasCustomAttributes();
|
||||||
};
|
};
|
||||||
|
|
||||||
struct BfTypeDefMapFuncs : public MultiHashSetFuncs
|
struct BfTypeDefMapFuncs : public AllocatorCLib
|
||||||
{
|
{
|
||||||
int GetHash(BfTypeDef* typeDef)
|
int GetHash(BfTypeDef* typeDef)
|
||||||
{
|
{
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
#include "Compiler/BfDemangler.h"
|
#include "Compiler/BfDemangler.h"
|
||||||
#include "BeefySysLib/util/Hash.h"
|
#include "BeefySysLib/util/Hash.h"
|
||||||
#include "BeefySysLib/util/BeefPerf.h"
|
#include "BeefySysLib/util/BeefPerf.h"
|
||||||
|
#include "BeefySysLib/util/MultiDictionary.h"
|
||||||
#include "DbgSymSrv.h"
|
#include "DbgSymSrv.h"
|
||||||
#include "MiniDumpDebugger.h"
|
#include "MiniDumpDebugger.h"
|
||||||
|
|
||||||
|
@ -5173,7 +5174,6 @@ void DbgModule::ParseHotTargetSections(DataStream* stream, addr_target* resolved
|
||||||
stream->Read(&coffReloc, sizeof(COFFRelocation));
|
stream->Read(&coffReloc, sizeof(COFFRelocation));
|
||||||
|
|
||||||
PE_SymInfo* symInfo = (PE_SymInfo*)&mSymbolData[coffReloc.mSymbolTableIndex * 18];
|
PE_SymInfo* symInfo = (PE_SymInfo*)&mSymbolData[coffReloc.mSymbolTableIndex * 18];
|
||||||
//const char* symName = mSymbolData[coffReloc.mSymbolTableIndex];
|
|
||||||
|
|
||||||
bool isStaticSymbol = symInfo->mStorageClass == COFF_SYM_CLASS_STATIC;
|
bool isStaticSymbol = symInfo->mStorageClass == COFF_SYM_CLASS_STATIC;
|
||||||
|
|
||||||
|
@ -5313,27 +5313,20 @@ void DbgModule::CommitHotTargetSections()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DbgModule::HotReplaceType(DbgType* newType)
|
bool DbgModule::HasHotReplacedMethods(DbgType* type)
|
||||||
|
{
|
||||||
|
for (auto newMethod : type->mMethodList)
|
||||||
|
{
|
||||||
|
if ((newMethod->mBlock.mLowPC >= mImageBase) && (newMethod->mBlock.mHighPC <= mImageBase + mImageSize))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DbgModule::HotReplaceMethods(DbgType* newType, DbgType* primaryType)
|
||||||
{
|
{
|
||||||
auto linkedModule = GetLinkedModule();
|
auto linkedModule = GetLinkedModule();
|
||||||
|
|
||||||
newType->PopulateType();
|
|
||||||
DbgType* primaryType = linkedModule->GetPrimaryType(newType);
|
|
||||||
if (primaryType == newType)
|
|
||||||
{
|
|
||||||
// There was no previous type
|
|
||||||
BF_ASSERT(primaryType->mHotNewType == NULL);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (primaryType->mHotNewType != newType)
|
|
||||||
{
|
|
||||||
// We have already pulled in the new data from a previous new type
|
|
||||||
BF_ASSERT(primaryType->mHotNewType == NULL);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
primaryType->mHotNewType = NULL;
|
|
||||||
|
|
||||||
primaryType->PopulateType();
|
primaryType->PopulateType();
|
||||||
linkedModule->ParseGlobalsData();
|
linkedModule->ParseGlobalsData();
|
||||||
linkedModule->ParseSymbolData();
|
linkedModule->ParseSymbolData();
|
||||||
|
@ -5354,13 +5347,15 @@ void DbgModule::HotReplaceType(DbgType* newType)
|
||||||
|
|
||||||
// Now actually remove the linedata from the defining module
|
// Now actually remove the linedata from the defining module
|
||||||
HashSet<DbgSrcFile*> checkedFiles;
|
HashSet<DbgSrcFile*> checkedFiles;
|
||||||
for (auto method : primaryType->mMethodList)
|
//for (auto method : primaryType->mMethodList)
|
||||||
|
|
||||||
|
auto _RemoveLineInfo = [&](DbgSubprogram* method)
|
||||||
{
|
{
|
||||||
//method->mWasModuleHotReplaced = true;
|
//method->mWasModuleHotReplaced = true;
|
||||||
method->mHotReplaceKind = DbgSubprogram::HotReplaceKind_Orphaned; // May be temporarily orphaned
|
method->mHotReplaceKind = DbgSubprogram::HotReplaceKind_Orphaned; // May be temporarily orphaned
|
||||||
|
|
||||||
if (method->mLineInfo == NULL)
|
if (method->mLineInfo == NULL)
|
||||||
continue;
|
return;
|
||||||
|
|
||||||
//FIXME: Hot replacing lines
|
//FIXME: Hot replacing lines
|
||||||
DbgSrcFile* lastSrcFile = NULL;
|
DbgSrcFile* lastSrcFile = NULL;
|
||||||
|
@ -5394,51 +5389,52 @@ void DbgModule::HotReplaceType(DbgType* newType)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
//DbgType* primaryType = newType->GetPrimaryType();
|
//DbgType* primaryType = newType->GetPrimaryType();
|
||||||
|
|
||||||
|
MultiDictionary<StringView, DbgSubprogram*> oldProgramMap;
|
||||||
|
auto _AddToHotReplacedMethodList = [&](DbgSubprogram* oldMethod)
|
||||||
|
{
|
||||||
|
int hotIdx = oldMethod->mCompileUnit->mDbgModule->mHotIdx;
|
||||||
|
if ((hotIdx > 0) && (hotIdx < mDebugTarget->mVDataHotIdx))
|
||||||
|
{
|
||||||
|
// Too old
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
oldMethod->PopulateSubprogram();
|
||||||
|
if (oldMethod->mBlock.IsEmpty())
|
||||||
|
return;
|
||||||
|
auto symInfo = mDebugTarget->mSymbolMap.Get(oldMethod->mBlock.mLowPC);
|
||||||
|
if (symInfo != NULL)
|
||||||
|
{
|
||||||
|
oldProgramMap.Add(StringView(symInfo->mName), oldMethod);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (newType != primaryType)
|
||||||
|
{
|
||||||
// We need to keep a persistent list of hot replaced methods so we can set hot jumps
|
// We need to keep a persistent list of hot replaced methods so we can set hot jumps
|
||||||
// in old methods that may still be on the callstack. These entries get removed when
|
// in old methods that may still be on the callstack. These entries get removed when
|
||||||
// we unload unused hot files in
|
// we unload unused hot files in
|
||||||
while (!primaryType->mMethodList.IsEmpty())
|
while (!primaryType->mMethodList.IsEmpty())
|
||||||
{
|
{
|
||||||
auto method = primaryType->mMethodList.PopFront();
|
auto method = primaryType->mMethodList.PopFront();
|
||||||
|
|
||||||
method->PopulateSubprogram();
|
method->PopulateSubprogram();
|
||||||
|
|
||||||
primaryType->mHotReplacedMethodList.PushFront(method);
|
primaryType->mHotReplacedMethodList.PushFront(method);
|
||||||
mHotPrimaryTypes.Add(primaryType);
|
mHotPrimaryTypes.Add(primaryType);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Dictionary<StringView, DbgSubprogram*> oldProgramMap;
|
|
||||||
for (auto oldMethod : primaryType->mHotReplacedMethodList)
|
for (auto oldMethod : primaryType->mHotReplacedMethodList)
|
||||||
{
|
{
|
||||||
oldMethod->PopulateSubprogram();
|
_AddToHotReplacedMethodList(oldMethod);
|
||||||
if (oldMethod->mBlock.IsEmpty())
|
|
||||||
continue;
|
|
||||||
auto symInfo = mDebugTarget->mSymbolMap.Get(oldMethod->mBlock.mLowPC);
|
|
||||||
if (symInfo != NULL)
|
|
||||||
{
|
|
||||||
oldProgramMap.TryAdd(symInfo->mName, oldMethod);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool setHotJumpFailed = false;
|
bool setHotJumpFailed = false;
|
||||||
while (!newType->mMethodList.IsEmpty())
|
|
||||||
{
|
|
||||||
DbgSubprogram* newMethod = newType->mMethodList.PopFront();
|
|
||||||
if (!newMethod->mBlock.IsEmpty())
|
|
||||||
{
|
|
||||||
BfLogDbg("Hot added new method %p %s Address:%p\n", newMethod, newMethod->mName, newMethod->mBlock.mLowPC);
|
|
||||||
|
|
||||||
newMethod->PopulateSubprogram();
|
auto _HotJump = [&](DbgSubprogram* oldMethod, DbgSubprogram* newMethod)
|
||||||
|
|
||||||
auto symInfo = mDebugTarget->mSymbolMap.Get(newMethod->mBlock.mLowPC);
|
|
||||||
if (symInfo != NULL)
|
|
||||||
{
|
|
||||||
DbgSubprogram* oldMethod = NULL;
|
|
||||||
if (oldProgramMap.TryGetValue(symInfo->mName, &oldMethod))
|
|
||||||
{
|
{
|
||||||
bool doHotJump = false;
|
bool doHotJump = false;
|
||||||
|
|
||||||
|
@ -5520,17 +5516,95 @@ void DbgModule::HotReplaceType(DbgType* newType)
|
||||||
{
|
{
|
||||||
if (!setHotJumpFailed)
|
if (!setHotJumpFailed)
|
||||||
{
|
{
|
||||||
if (!mDebugger->SetHotJump(oldMethod, newMethod->mBlock.mLowPC, (int)(newMethod->mBlock.mHighPC - newMethod->mBlock.mLowPC)))
|
if (mDebugger->SetHotJump(oldMethod, newMethod->mBlock.mLowPC, (int)(newMethod->mBlock.mHighPC - newMethod->mBlock.mLowPC)))
|
||||||
|
{
|
||||||
|
_RemoveLineInfo(oldMethod);
|
||||||
|
}
|
||||||
|
else
|
||||||
setHotJumpFailed = true;
|
setHotJumpFailed = true;
|
||||||
}
|
}
|
||||||
oldMethod->mHotReplaceKind = DbgSubprogram::HotReplaceKind_Replaced;
|
oldMethod->mHotReplaceKind = DbgSubprogram::HotReplaceKind_Replaced;
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
auto _ReplaceMethod = [&](DbgSubprogram* newMethod)
|
||||||
|
{
|
||||||
|
if (!newMethod->mBlock.IsEmpty())
|
||||||
|
{
|
||||||
|
BfLogDbg("Hot added new method %p %s Address:%p\n", newMethod, newMethod->mName, newMethod->mBlock.mLowPC);
|
||||||
|
|
||||||
|
newMethod->PopulateSubprogram();
|
||||||
|
|
||||||
|
auto symInfo = mDebugTarget->mSymbolMap.Get(newMethod->mBlock.mLowPC);
|
||||||
|
if (symInfo != NULL)
|
||||||
|
{
|
||||||
|
DbgSubprogram* oldMethod = NULL;
|
||||||
|
for (auto itr = oldProgramMap.TryGet(StringView(symInfo->mName)); itr != oldProgramMap.end(); ++itr)
|
||||||
|
{
|
||||||
|
auto oldMethod = itr.GetValue();
|
||||||
|
_HotJump(oldMethod, newMethod);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (newType == primaryType)
|
||||||
|
{
|
||||||
|
// In-place method swapping
|
||||||
|
|
||||||
|
auto newMethod = newType->mMethodList.front();
|
||||||
|
while (newMethod != NULL)
|
||||||
|
{
|
||||||
|
if ((newMethod->mBlock.mLowPC >= mImageBase) && (newMethod->mBlock.mHighPC <= mImageBase + mImageSize))
|
||||||
|
{
|
||||||
|
// Our object file contains this function
|
||||||
|
_ReplaceMethod(newMethod);
|
||||||
|
auto nextMethod = newMethod->mNext;
|
||||||
|
newType->mMethodList.Remove(newMethod);
|
||||||
|
primaryType->mHotReplacedMethodList.PushFront(newMethod);
|
||||||
|
newMethod = nextMethod;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_AddToHotReplacedMethodList(newMethod);
|
||||||
|
}
|
||||||
|
newMethod = newMethod->mNext;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
while (!newType->mMethodList.IsEmpty())
|
||||||
|
{
|
||||||
|
DbgSubprogram* newMethod = newType->mMethodList.PopFront();
|
||||||
|
_ReplaceMethod(newMethod);
|
||||||
newMethod->mParentType = primaryType;
|
newMethod->mParentType = primaryType;
|
||||||
primaryType->mMethodList.PushBack(newMethod);
|
primaryType->mMethodList.PushBack(newMethod);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DbgModule::HotReplaceType(DbgType* newType)
|
||||||
|
{
|
||||||
|
auto linkedModule = GetLinkedModule();
|
||||||
|
|
||||||
|
newType->PopulateType();
|
||||||
|
DbgType* primaryType = linkedModule->GetPrimaryType(newType);
|
||||||
|
if (primaryType == newType)
|
||||||
|
{
|
||||||
|
// There was no previous type
|
||||||
|
BF_ASSERT(primaryType->mHotNewType == NULL);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (primaryType->mHotNewType != newType)
|
||||||
|
{
|
||||||
|
// We have already pulled in the new data from a previous new type
|
||||||
|
BF_ASSERT(primaryType->mHotNewType == NULL);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
primaryType->mHotNewType = NULL;
|
||||||
|
|
||||||
|
HotReplaceMethods(newType, primaryType);
|
||||||
|
|
||||||
//mDebugTarget->mSymbolMap.Get()
|
//mDebugTarget->mSymbolMap.Get()
|
||||||
|
|
||||||
|
@ -6506,6 +6580,10 @@ bool DbgModule::ReadCOFF(DataStream* stream, DbgModuleKind moduleKind)
|
||||||
{
|
{
|
||||||
auto dbgType = *itr;
|
auto dbgType = *itr;
|
||||||
auto primaryType = dbgType->GetPrimaryType();
|
auto primaryType = dbgType->GetPrimaryType();
|
||||||
|
|
||||||
|
if ((primaryType->mHotNewType == NULL) && (HasHotReplacedMethods(primaryType)) && (dbgType == primaryType))
|
||||||
|
HotReplaceMethods(dbgType, primaryType);
|
||||||
|
|
||||||
if (primaryType != dbgType)
|
if (primaryType != dbgType)
|
||||||
{
|
{
|
||||||
mHotPrimaryTypes.Remove(itr);
|
mHotPrimaryTypes.Remove(itr);
|
||||||
|
|
|
@ -665,8 +665,8 @@ public:
|
||||||
class SubprogramRecord
|
class SubprogramRecord
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Array<DbgLineInfoCtx, AllocatorBump<DbgLineInfoCtx> > mContexts;
|
Array<DbgLineInfoCtx, AllocatorBump> mContexts;
|
||||||
Array<DbgLineData, AllocatorBump<DbgLineData> > mLines;
|
Array<DbgLineData, AllocatorBump> mLines;
|
||||||
int mCurContext;
|
int mCurContext;
|
||||||
bool mHasInlinees;
|
bool mHasInlinees;
|
||||||
};
|
};
|
||||||
|
@ -1273,6 +1273,8 @@ public:
|
||||||
void DoReloc(DbgHotTargetSection* hotTargetSection, COFFRelocation& coffReloc, addr_target resolveSymbolAddr, PE_SymInfo* symInfo);
|
void DoReloc(DbgHotTargetSection* hotTargetSection, COFFRelocation& coffReloc, addr_target resolveSymbolAddr, PE_SymInfo* symInfo);
|
||||||
void ParseHotTargetSections(DataStream* stream, addr_target* resovledSymbolAddrs);
|
void ParseHotTargetSections(DataStream* stream, addr_target* resovledSymbolAddrs);
|
||||||
void CommitHotTargetSections();
|
void CommitHotTargetSections();
|
||||||
|
bool HasHotReplacedMethods(DbgType* type);
|
||||||
|
void HotReplaceMethods(DbgType* newType, DbgType* primaryType);
|
||||||
void HotReplaceType(DbgType* newType);
|
void HotReplaceType(DbgType* newType);
|
||||||
void ProcessHotSwapVariables();
|
void ProcessHotSwapVariables();
|
||||||
virtual bool LoadPDB(const String& pdbPath, uint8 wantGuid[16], int32 wantAge) { return false; }
|
virtual bool LoadPDB(const String& pdbPath, uint8 wantGuid[16], int32 wantAge) { return false; }
|
||||||
|
|
|
@ -33,6 +33,7 @@ DebugTarget::DebugTarget(WinDebugger* debugger)
|
||||||
mHotHeapAddr = 0;
|
mHotHeapAddr = 0;
|
||||||
mHotHeapReserveSize = 0;
|
mHotHeapReserveSize = 0;
|
||||||
mLastHotHeapCleanIdx = 0;
|
mLastHotHeapCleanIdx = 0;
|
||||||
|
mVDataHotIdx = -1;
|
||||||
mIsEmpty = false;
|
mIsEmpty = false;
|
||||||
mWasLocallyBuilt = false;
|
mWasLocallyBuilt = false;
|
||||||
mCurModuleId = 0;
|
mCurModuleId = 0;
|
||||||
|
@ -2489,15 +2490,17 @@ DbgBreakKind DebugTarget::GetDbgBreakKind(addr_target address, CPURegisters* reg
|
||||||
return DbgBreakKind_None;
|
return DbgBreakKind_None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define ADDR_ROUGH(address) ((address) & ~0x3FFF)
|
||||||
|
|
||||||
DbgModule* DebugTarget::FindDbgModuleForAddress(addr_target address)
|
DbgModule* DebugTarget::FindDbgModuleForAddress(addr_target address)
|
||||||
{
|
{
|
||||||
addr_target checkAddr = address & ~0xFFFF;
|
addr_target checkAddr = ADDR_ROUGH(address);
|
||||||
FindDbgModuleCacheEntry* valuePtr = NULL;
|
FindDbgModuleCacheEntry* valuePtr = NULL;
|
||||||
if (mFindDbgModuleCache.TryAdd(checkAddr, NULL, &valuePtr))
|
if (mFindDbgModuleCache.TryAdd(checkAddr, NULL, &valuePtr))
|
||||||
{
|
{
|
||||||
for (auto dwarf : mDbgModules)
|
for (auto dwarf : mDbgModules)
|
||||||
{
|
{
|
||||||
if ((address >= dwarf->mImageBase) && (address < dwarf->mImageBase + dwarf->mImageSize))
|
if ((checkAddr >= ADDR_ROUGH(dwarf->mImageBase)) && (checkAddr <= ADDR_ROUGH(dwarf->mImageBase + dwarf->mImageSize)))
|
||||||
{
|
{
|
||||||
if (valuePtr->mFirst == NULL)
|
if (valuePtr->mFirst == NULL)
|
||||||
{
|
{
|
||||||
|
|
|
@ -49,6 +49,7 @@ public:
|
||||||
addr_target mHotHeapAddr;
|
addr_target mHotHeapAddr;
|
||||||
int64 mHotHeapReserveSize;
|
int64 mHotHeapReserveSize;
|
||||||
int mLastHotHeapCleanIdx;
|
int mLastHotHeapCleanIdx;
|
||||||
|
int mVDataHotIdx;
|
||||||
String mTargetPath;
|
String mTargetPath;
|
||||||
DbgModule* mLaunchBinary;
|
DbgModule* mLaunchBinary;
|
||||||
DbgModule* mTargetBinary;
|
DbgModule* mTargetBinary;
|
||||||
|
|
|
@ -20,6 +20,6 @@
|
||||||
<LocalDebuggerEnvironment>_NO_DEBUG_HEAP=1</LocalDebuggerEnvironment>
|
<LocalDebuggerEnvironment>_NO_DEBUG_HEAP=1</LocalDebuggerEnvironment>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<ShowAllFiles>true</ShowAllFiles>
|
<ShowAllFiles>false</ShowAllFiles>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
</Project>
|
</Project>
|
|
@ -1151,9 +1151,14 @@ void WinDebugger::HotLoad(const Array<String>& objectFiles, int hotIdx)
|
||||||
|
|
||||||
int startingModuleIdx = (int)mDebugTarget->mDbgModules.size();
|
int startingModuleIdx = (int)mDebugTarget->mDbgModules.size();
|
||||||
|
|
||||||
|
bool hasHotVData = false;
|
||||||
|
|
||||||
bool failed = false;
|
bool failed = false;
|
||||||
for (auto fileName : objectFiles)
|
for (auto fileName : objectFiles)
|
||||||
{
|
{
|
||||||
|
if ((fileName.IndexOf("/vdata.") != -1) || (fileName.IndexOf("\\vdata.") != -1))
|
||||||
|
hasHotVData = true;
|
||||||
|
|
||||||
BfLogDbg("WinDebugger::HotLoad: %s\n", fileName.c_str());
|
BfLogDbg("WinDebugger::HotLoad: %s\n", fileName.c_str());
|
||||||
DbgModule* newBinary = mDebugTarget->HotLoad(fileName, hotIdx);
|
DbgModule* newBinary = mDebugTarget->HotLoad(fileName, hotIdx);
|
||||||
if ((newBinary != NULL) && (newBinary->mFailed))
|
if ((newBinary != NULL) && (newBinary->mFailed))
|
||||||
|
@ -1186,6 +1191,9 @@ void WinDebugger::HotLoad(const Array<String>& objectFiles, int hotIdx)
|
||||||
|
|
||||||
mDebugTarget->RehupSrcFiles();
|
mDebugTarget->RehupSrcFiles();
|
||||||
|
|
||||||
|
if (hasHotVData)
|
||||||
|
mDebugTarget->mVDataHotIdx = hotIdx;
|
||||||
|
|
||||||
for (int breakIdx = 0; breakIdx < (int)mBreakpoints.size(); breakIdx++)
|
for (int breakIdx = 0; breakIdx < (int)mBreakpoints.size(); breakIdx++)
|
||||||
{
|
{
|
||||||
auto breakpoint = mBreakpoints[breakIdx];
|
auto breakpoint = mBreakpoints[breakIdx];
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue