4
Fork 0
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

1320 lines
24 KiB

#pragma once
#include "skse64_common/Utilities.h"
#include "skse64/GameAPI.h"
class TESForm;
// 08
struct BSIntrusiveRefCounted
{
public:
volatile UInt32 m_refCount; // 00
UInt32 unk04; // 04
};
// 04
template <typename T>
class BSTSmartPointer
{
public:
// refcounted
T * ptr;
};
//// 04
//struct BSNonReentrantSpinLock
//{
//public:
// volatile UInt32 uiLock; // 00
//};
//
// 08
class SimpleLock
{
enum
{
kFastSpinThreshold = 10000
};
volatile SInt32 threadID; // 00
volatile UInt32 lockCount; // 04
public:
SimpleLock() : threadID(0), lockCount(0) {}
void Lock(UInt32 pauseAttempts = 0);
void Release(void);
};
STATIC_ASSERT(sizeof(SimpleLock) == 0x8);
class SimpleLocker
{
public:
SimpleLocker(SimpleLock * dataHolder) { m_lock = dataHolder; m_lock->Lock(); }
~SimpleLocker() { m_lock->Release(); }
protected:
SimpleLock * m_lock;
};
// 08
class BSReadWriteLock
{
enum
{
kFastSpinThreshold = 10000,
kLockWrite = 0x80000000,
kLockCountMask = 0xFFFFFFF
};
volatile SInt32 threadID; // 00
volatile UInt32 lockValue; // 04
public:
BSReadWriteLock() : threadID(0), lockValue(0) {}
//void LockForRead();
//void LockForWrite();
MEMBER_FN_PREFIX(BSReadWriteLock);
DEFINE_MEMBER_FN(LockForRead, void, 0x00C2CD50);
DEFINE_MEMBER_FN(LockForWrite, void, 0x00C2CDD0);
DEFINE_MEMBER_FN(UnlockRead, void, 0x00C2D010);
DEFINE_MEMBER_FN(UnlockWrite, void, 0x00C2D020);
DEFINE_MEMBER_FN(LockForReadAndWrite, void, 0x00C2CED0);
DEFINE_MEMBER_FN(TryLockForWrite, bool, 0x00C2CFC0);
};
STATIC_ASSERT(sizeof(BSReadWriteLock) == 0x8);
class BSReadLocker
{
public:
BSReadLocker(BSReadWriteLock * lock) { m_lock = lock; CALL_MEMBER_FN(m_lock, LockForRead)(); }
~BSReadLocker() { CALL_MEMBER_FN(m_lock, UnlockRead)(); }
protected:
BSReadWriteLock * m_lock;
};
class BSWriteLocker
{
public:
BSWriteLocker(BSReadWriteLock * lock) { m_lock = lock; CALL_MEMBER_FN(m_lock, LockForWrite)(); }
~BSWriteLocker() { CALL_MEMBER_FN(m_lock, UnlockWrite)(); }
protected:
BSReadWriteLock * m_lock;
};
class BSReadAndWriteLocker
{
public:
BSReadAndWriteLocker(BSReadWriteLock * lock) { m_lock = lock; CALL_MEMBER_FN(m_lock, LockForReadAndWrite)(); }
~BSReadAndWriteLocker() { CALL_MEMBER_FN(m_lock, UnlockWrite)(); }
protected:
BSReadWriteLock * m_lock;
};
// 80808
class StringCache
{
public:
struct Ref
{
const char * data;
MEMBER_FN_PREFIX(Ref);
DEFINE_MEMBER_FN(ctor, Ref *, 0x00C4F780, const char * buf);
// 31D79EFB15D5E4B34BD32D03A46EAFF65C28ACFB+CB
DEFINE_MEMBER_FN(ctor_ref, Ref *, 0x00C4F830, const Ref & rhs);
DEFINE_MEMBER_FN(Set, Ref *, 0x00C4F910, const char * buf);
// 31D79EFB15D5E4B34BD32D03A46EAFF65C28ACFB+41
DEFINE_MEMBER_FN(Set_ref, Ref *, 0x00C4F9D0, const Ref & rhs);
// 46F6DC561A3C9677037E58B55951C58A08E41C47+4A
DEFINE_MEMBER_FN(Release, void, 0x00C50B50);
Ref();
Ref(const char * buf);
void Release() { CALL_MEMBER_FN(this, Release)(); }
bool operator==(const Ref& lhs) const { return data == lhs.data; }
bool operator<(const Ref& lhs) const { return data < lhs.data; }
const char * c_str() const { return operator const char *(); }
const char * Get() const { return c_str(); }
operator const char *() const { return data ? data : ""; }
};
// 10
struct Lock
{
UInt32 unk00; // 00 - set to 80000000 when locked
UInt32 pad04; // 04
UInt64 pad08; // 08
};
void * lut[0x10000]; // 00000
Lock lock[0x80]; // 80000
UInt8 isInit; // 80800
};
typedef StringCache::Ref BSFixedString;
//class BSAutoFixedString : public BSFixedString
//{
//public:
// BSAutoFixedString() : BSFixedString() { }
// BSAutoFixedString(const char * buf) : BSFixedString(buf) { }
//
// ~BSAutoFixedString()
// {
// Release();
// }
//};
// 10
class BSString
{
public:
BSString() :m_data(NULL), m_dataLen(0), m_bufLen(0) { }
~BSString();
const char * Get(void) const;
MEMBER_FN_PREFIX(BSString);
DEFINE_MEMBER_FN(Set, bool, 0x00103D50, const char * str, UInt32 len); // len default 0
private:
char * m_data; // 00
UInt16 m_dataLen; // 08
UInt16 m_bufLen; // 0A
UInt32 pad0C; // 0C
};
// Container types
// 18
template <class T, int nGrow = 10, int nShrink = 10>
class tArray
{
public:
T* entries; // 00
UInt32 capacity; // 08
UInt32 pad0C; // 0C
UInt32 count; // 10
UInt32 pad14; // 14
tArray() : entries(NULL), capacity(0), count(0), pad0C(0), pad14(0) { }
T& operator[](UInt64 index)
{
return entries[index];
}
void Clear()
{
Heap_Free(entries);
entries = NULL;
capacity = 0;
count = 0;
}
bool Allocate(UInt32 numEntries)
{
entries = (T *)Heap_Allocate(sizeof(T) * numEntries);
if(!entries) return false;
for(UInt32 i = 0; i < numEntries; i++)
new (&entries[i]) T;
capacity = numEntries;
count = numEntries;
return true;
}
bool CopyFrom(const tArray<T> * rhs)
{
if (rhs->count == 0) return false;
if (!rhs->entries) return false;
if (entries)
Clear();
if (!Allocate(rhs->count)) return false;
memcpy(entries, rhs->entries, sizeof(T) * count);
return true;
}
bool Resize(UInt32 numEntries)
{
if(numEntries == capacity)
return false;
if(!entries) {
Allocate(numEntries);
return true;
}
if(numEntries < capacity) {
// Delete the truncated entries
for(UInt32 i = numEntries; i < capacity; i++)
delete &entries[i];
}
T * newBlock = (T *)Heap_Allocate(sizeof(T) * numEntries); // Create a new block
memmove_s(newBlock, sizeof(T) * numEntries, entries, sizeof(T) * numEntries); // Move the old memory to the new block
if(numEntries > capacity) { // Fill in new remaining entries
for(UInt32 i = capacity; i < numEntries; i++)
new (&entries[i]) T;
}
Heap_Free(entries); // Free the old block
entries = newBlock; // Assign the new block
capacity = numEntries; // Capacity is now the number of total entries in the block
count = min(capacity, count); // Count stays the same, or is truncated to capacity
return true;
}
bool Push(const T & entry)
{
if(!entries || count + 1 > capacity) {
if(!Grow(nGrow))
return false;
}
new (&entries[count]) T(entry);
count++;
return true;
};
bool Insert(UInt32 index, const T & entry)
{
if(!entries || index < count)
return false;
entries[index] = entry;
return true;
};
bool Remove(UInt32 index)
{
if(!entries || index >= count)
return false;
// This might not be right for pointer types...
(&entries[index])->~T();
if(index + 1 < count) {
UInt32 remaining = count - index;
memmove_s(&entries[index + 1], sizeof(T) * remaining, &entries[index], sizeof(T) * remaining); // Move the rest up
}
count--;
if(capacity > count + nShrink)
Shrink();
return true;
}
bool Shrink()
{
if(!entries || count == capacity) return false;
try {
UInt32 newSize = count;
T * oldArray = entries;
T * newArray = (T *)Heap_Allocate(sizeof(T) * newSize); // Allocate new block
memmove_s(newArray, sizeof(T) * newSize, entries, sizeof(T) * newSize); // Move the old block
entries = newArray;
capacity = count;
Heap_Free(oldArray); // Free the old block
return true;
}
catch(...) {
return false;
}
return false;
}
bool Grow(UInt32 numEntries)
{
if(!entries) {
entries = (T *)Heap_Allocate(sizeof(T) * numEntries);
count = 0;
capacity = numEntries;
return true;
}
try {
UInt32 oldSize = capacity;
UInt32 newSize = oldSize + numEntries;
T * oldArray = entries;
T * newArray = (T *)Heap_Allocate(sizeof(T) * newSize); // Allocate new block
if(oldArray)
memmove_s(newArray, sizeof(T) * newSize, entries, sizeof(T) * capacity); // Move the old block
entries = newArray;
capacity = newSize;
if(oldArray)
Heap_Free(oldArray); // Free the old block
for(UInt32 i = oldSize; i < newSize; i++) // Allocate the rest of the free blocks
new (&entries[i]) T;
return true;
}
catch(...) {
return false;
}
return false;
}
bool GetNthItem(UInt64 index, T& pT) const
{
if (index < count) {
pT = entries[index];
return true;
}
return false;
}
SInt64 GetItemIndex(T & pFind) const
{
for (UInt64 n = 0; n < count; n++) {
T& pT = entries[n];
if (pT == pFind)
return n;
}
return -1;
}
DEFINE_STATIC_HEAP(Heap_Allocate, Heap_Free)
};
//template<class T>
//class tMutexArray : public tArray<T>
//{
//public:
// SimpleLock lock; // 18
//};
typedef tArray<UInt64> UnkArray;
typedef tArray<TESForm*> UnkFormArray;
// Returns if/where the element was found, otherwise indexOut can be used as insert position
template <typename T>
bool GetSortIndex(tArray<T> & arr, T & elem, SInt32 & indexOut)
{
UInt32 count = arr.count;
if (count == 0)
{
indexOut = 0;
return false;
}
SInt32 leftIdx = 0;
SInt32 rightIdx = count - 1;
while (true)
{
UInt32 pivotIdx = leftIdx + ((rightIdx - leftIdx) / 2);
T & p = arr[pivotIdx];
if (elem == p)
{
indexOut = pivotIdx;
return true;
}
else if (elem > p)
{
leftIdx = pivotIdx + 1;
}
else
{
rightIdx = pivotIdx - 1;
}
if (leftIdx > rightIdx)
{
indexOut = leftIdx;
return false;
}
}
}
enum {
eListCount = -3,
eListEnd = -2,
eListInvalid = -1,
};
// 10
template <class T>
class tList
{
enum {
eListCount = -3,
eListEnd = -2,
eListInvalid = -1,
};
struct _Node
{
T * item; // 00
_Node* next; // 04
T * Item() const { return item; }
_Node* Next() const { return next; }
T * Remove(_Node * pred)
{
T * pRemoved = item;
_Node * pNext = next;
// become the next entry and return my item
if (pNext)
{
item = pNext->item;
next = pNext->next;
Heap_Free(pNext);
}
// tail?
else
{
item = NULL;
next = NULL;
// has precedessor, so tail != head
if (pred)
{
pred->next = NULL;
Heap_Free(this);
}
}
return pRemoved;
}
};
_Node m_listHead; // 00
private:
_Node * Head() const { return const_cast<_Node*>(&m_listHead); }
_Node * Tail() const
{
_Node * node = const_cast<_Node*>(&m_listHead);
if (node)
while (node->next) node = node->next;
return node;
}
template <class Op>
UInt32 FreeNodes(Op &compareOp)
{
UInt32 numFreed = 0;
_Node * pPred = NULL;
_Node * pCur = Head();
while (pCur)
{
if (pCur->next)
{
if (compareOp.Accept(pCur->item))
{
pCur->Remove(pPred);
numFreed++;
}
else
{
pPred = pCur;
pCur = pCur->next;
}
}
// List Tail?
else
{
if (compareOp.Accept(pCur->item))
{
pCur->Remove(pPred);
numFreed++;
}
break;
}
}
return numFreed;
}
struct NodePos
{
NodePos(): node(NULL), index(eListInvalid) {}
_Node* node;
SInt32 index;
};
NodePos GetNthNode(SInt32 index) const
{
NodePos pos;
SInt32 n = 0;
_Node* pCur = Head();
while (pCur && pCur->Item())
{
if (n == index) break;
if (eListEnd == index && !pCur->Next()) break;
pCur = pCur->Next();
++n;
}
pos.node = pCur;
pos.index = n;
return pos;
}
public:
// Allocate list as a single empty node
static tList<T> * Create(void)
{
tList<T> * p = (tList<T> *)Heap_Allocate(sizeof(tList<T>));
ASSERT(p);
p->m_listHead.item = NULL;
p->m_listHead.next = NULL;
return p;
}
void Delete(void)
{
RemoveAll();
Heap_Free(&m_listHead);
}
class Iterator
{
_Node * m_cur;
public:
Iterator() : m_cur(NULL) {}
Iterator(_Node* node) : m_cur(node) { }
Iterator operator++() { if (!End()) m_cur = m_cur->Next(); return *this;}
bool End() { return m_cur == NULL; }
const T * operator->() { return (m_cur) ? m_cur->Item() : NULL; }
const T * operator*() { return (m_cur) ? m_cur->Item() : NULL; }
const Iterator& operator=(const Iterator& rhs) {
m_cur = rhs.m_cur;
return *this;
}
T * Get() { return (m_cur) ? m_cur->Item() : NULL; }
};
const Iterator Begin() const { return Iterator(Head()); }
void Insert(T * item)
{
// add new node if we aren't empty
if (m_listHead.item)
{
// copy head in to new node
_Node * node = (_Node *)Heap_Allocate(sizeof(_Node));
ASSERT(node);
node->item = m_listHead.item;
node->next = m_listHead.next;
m_listHead.next = node;
}
m_listHead.item = item;
}
void Push(T * item)
{
_Node * tail = Tail();
// add new node if we aren't empty
if (tail->item)
{
_Node * node = (_Node *)Heap_Allocate(sizeof(_Node));
ASSERT(node);
tail->next = node;
node->item = item;
node->next = NULL;
}
else
{
tail->item = item;
}
}
T * AddFront(void)
{
T * item = (T *)Heap_Allocate(sizeof(T));
if(!item)
return NULL;
new (item) T;
Insert(item);
return item;
}
T * AddBack(void)
{
T * item = (T *)Heap_Allocate(sizeof(T));
if(!item)
return NULL;
new (item) T;
Push(item);
return item;
}
void Append(Iterator source)
{
while (!source.End())
{
Push(source.Get());
++source;
}
}
UInt32 Count() const
{
NodePos pos = GetNthNode(eListCount);
return (pos.index > 0) ? pos.index : 0;
};
T * GetNthItem(SInt32 n) const
{
NodePos pos = GetNthNode(n);
return (pos.index == n && pos.node) ? pos.node->Item() : NULL;
}
T * GetLastItem() const
{
NodePos pos = GetNthNode(eListEnd);
return pos.node->Item();
}
SInt32 AddAt(T * item, SInt32 index)
{
if (!m_listHead.item) {
m_listHead.item = item;
return 0;
}
NodePos pos = GetNthNode(index);
_Node* pTargetNode = pos.node;
_Node* newNode = (_Node*)Heap_Allocate(sizeof(_Node));
if (newNode && pTargetNode) {
if (index == eListEnd) {
pTargetNode->next = newNode;
newNode->item = item;
newNode->next = NULL;
} else {
newNode->item = pTargetNode->item;
newNode->next = pTargetNode->next;
pTargetNode->item = item;
pTargetNode->next = newNode;
}
return pos.index;
}
return eListInvalid;
}
template <class Op>
void Visit(Op& op, _Node* prev = NULL) const {
const _Node* pCur = (prev) ? prev->next : Head();
bool bContinue = true;
while (pCur && bContinue) {
bContinue = op.Accept(pCur->Item());
if (bContinue) {
pCur = pCur->next;
}
}
}
template <class Op>
T * Find(Op& op) const
{
const _Node* pCur = Head();
bool bFound = false;
while (pCur && !bFound)
{
if (!pCur->Item())
pCur = pCur->Next();
else
{
bFound = op.Accept(pCur->Item());
if (!bFound)
pCur = pCur->Next();
}
}
return (bFound && pCur) ? pCur->Item() : NULL;
}
template <class Op>
Iterator Find(Op& op, Iterator prev) const
{
Iterator curIt = (prev.End()) ? Begin() : ++prev;
bool bFound = false;
while(!curIt.End() && !bFound) {
const T * pCur = *curIt;
if (pCur) {
bFound = op.Accept(pCur);
}
if (!bFound) {
++curIt;
}
}
return curIt;
}
const _Node* FindString(char* str, Iterator prev) const
{
return Find(StringFinder_CI(str), prev);
}
template <class Op>
UInt32 CountIf(Op& op) const
{
UInt32 count = 0;
const _Node* pCur = Head();
while (pCur)
{
if (pCur->Item() && op.Accept(pCur->Item()))
count++;
pCur = pCur->Next();
}
return count;
}
class AcceptAll {
public:
bool Accept(T * item) {
return true;
}
};
void RemoveAll()
{
FreeNodes(AcceptAll());
}
T * RemoveNth(SInt32 n)
{
Item* pRemoved = NULL;
if (n == 0) {
pRemoved = m_listHead.RemoveMe();
} else if (n > 0) {
NodePos nodePos = GetNthNode(n);
if (nodePos.node && nodePos.index == n) {
pRemoved = nodePos.node->RemoveMe();
}
}
return pRemoved;
};
T * ReplaceNth(SInt32 n, T* item)
{
Item* pReplaced = NULL;
NodePos nodePos = GetNthNode(n);
if (nodePos.node && nodePos.index == n) {
pReplaced = nodePos.node->item;
nodePos.node->item = item;
}
return pReplaced;
}
template <class Op>
UInt32 RemoveIf(Op& op)
{
return FreeNodes(op);
}
template <class Op>
SInt32 GetIndexOf(Op& op)
{
SInt32 idx = 0;
const _Node* pCur = Head();
while (pCur && pCur->Item() && !op.Accept(pCur->Item()))
{
idx++;
pCur = pCur->Next();
}
if (pCur && pCur->Item())
return idx;
else
return -1;
}
class AcceptEqual {
public:
T * item;
AcceptEqual(T * a_item) : item(a_item) {}
bool Accept(T * a_item) {
return *item == *a_item;
}
};
bool Contains(T * item) const
{
return Find(AcceptEqual(item)) != NULL;
}
void Dump(void)
{
_MESSAGE("tList:");
_MESSAGE("> count: %d", Count());
const _Node* pCur = Head();
UInt32 i = 0;
while (pCur)
{
_MESSAGE("* %d :", i);
//_MESSAGE("\t\titem: %08X", pCur->item);
if (pCur->item)
_MESSAGE("\t\t*item: %d", *pCur->item);
_MESSAGE("\t\tnext: %08X", pCur->next);
i++;
pCur = pCur->next;
}
}
};
//
//STATIC_ASSERT(sizeof(tList<void *>) == 0x10);
//
// 30
template <typename Item, typename Key = Item>
class tHashSet
{
class _Entry
{
public:
Item item;
_Entry * next;
_Entry() : next(NULL) {}
bool IsFree() const { return next == NULL; }
void SetFree() { next = NULL; }
void Dump(void)
{
item.Dump();
_MESSAGE("\t\tnext: %08X", next);
}
};
// When creating a new tHashSet, init sentinel pointer with address of this entry
static _Entry sentinel;
void * unk00; // 000
UInt32 unk_000; // 008
UInt32 m_size; // 00C
UInt32 m_freeCount; // 010
UInt32 m_freeOffset; // 014
_Entry * m_eolPtr; // 018
UInt64 unk_018; // 020
_Entry * m_entries; // 028
_Entry * GetEntry(UInt32 hash) const
{
return (_Entry*) (((uintptr_t) m_entries) + sizeof(_Entry) * (hash & (m_size - 1)));
}
_Entry * GetEntryAt(UInt32 index) const
{
return (_Entry*) (((uintptr_t) m_entries) + sizeof(_Entry) * index);
}
_Entry * NextFreeEntry(void)
{
_Entry * result = NULL;
if (m_freeCount == 0)
return NULL;
do
{
m_freeOffset = (m_size - 1) & (m_freeOffset - 1);
_Entry * entry = GetEntryAt(m_freeOffset);
if (entry->IsFree())
result = entry;
}
while (!result);
m_freeCount--;
return result;
}
enum InsertResult
{
kInsert_Duplicate = -1,
kInsert_OutOfSpace = 0,
kInsert_Success = 1
};
InsertResult Insert(Item * item)
{
if (! m_entries)
return kInsert_OutOfSpace;
Key k = (Key)*item;
_Entry * targetEntry = GetEntry(Item::GetHash(&k));
// Case 1: Target entry is free
if (targetEntry->IsFree())
{
targetEntry->item = *item;
targetEntry->next = m_eolPtr;
--m_freeCount;
return kInsert_Success;
}
// -- Target entry is already in use
// Case 2: Item already included
_Entry * p = targetEntry;
do
{
if (p->item == *item)
return kInsert_Duplicate;
p = p->next;
}
while (p != m_eolPtr);
// -- Either hash collision or bucket overlap
_Entry * freeEntry = NextFreeEntry();
// No more space?
if (!freeEntry)
return kInsert_OutOfSpace;
// Original position of the entry that currently uses the target position
k = (Key)targetEntry->item;
p = GetEntry(Item::GetHash(&k));
// Case 3a: Hash collision - insert new entry between target entry and successor
if (targetEntry == p)
{
freeEntry->item = *item;
freeEntry->next = targetEntry->next;
targetEntry->next = freeEntry;
return kInsert_Success;
}
// Case 3b: Bucket overlap
else
{
while (p->next != targetEntry)
p = p->next;
freeEntry->item = targetEntry->item;
freeEntry->next = targetEntry->next;
p->next = freeEntry;
targetEntry->item = *item;
targetEntry->next = m_eolPtr;
return kInsert_Success;
}
}
bool CopyEntry(_Entry * sourceEntry)
{
if (! m_entries)
return false;
Key k = (Key)sourceEntry->item;
_Entry * targetEntry = GetEntry(Item::GetHash(&k));
// Case 1: Target location is unused
if (!targetEntry->next)
{
targetEntry->item = sourceEntry->item;
targetEntry->next = m_eolPtr;
--m_freeCount;
return true;
}
// Target location is in use. Either hash collision or bucket overlap.
_Entry * freeEntry = NextFreeEntry();
k = (Key)targetEntry->item;
_Entry * p = GetEntry(Item::GetHash(&k));
// Case 2a: Hash collision - insert new entry between target entry and successor
if (targetEntry == p)
{
freeEntry->item = sourceEntry->item;
freeEntry->next = targetEntry->next;
targetEntry->next = freeEntry;
return true;
}
// Case 2b: Bucket overlap - forward until hash collision is found, then insert
while (p->next != targetEntry)
p = p->next;
// Source entry takes position of target entry - not completely understood yet
freeEntry->item = targetEntry->item;
freeEntry->next = targetEntry->next;
p->next = freeEntry;
targetEntry->item = sourceEntry->item;
targetEntry->next = m_eolPtr;
return true;
}
void Grow(void)
{
UInt32 oldSize = m_size;
UInt32 newSize = oldSize ? 2*oldSize : 8;
_Entry * oldEntries = m_entries;
_Entry * newEntries = (_Entry*)Heap_Allocate(newSize * sizeof(_Entry));
ASSERT(newEntries);
m_entries = newEntries;
m_size = m_freeCount = m_freeOffset = newSize;
// Initialize new table data (clear next pointers)
_Entry * p = newEntries;
for (UInt32 i = 0; i < newSize; i++, p++)
p->SetFree();
// Copy old entries, free old table data
if (oldEntries)
{
_Entry * p = oldEntries;
for (UInt32 i = 0; i < oldSize; i++, p++)
if (p->next)
CopyEntry(p);
Heap_Free(oldEntries);
}
}
public:
tHashSet() : m_size(0), m_freeCount(0), m_freeOffset(0), m_entries(NULL), m_eolPtr(&sentinel) { }
UInt32 Size() const { return m_size; }
UInt32 FreeCount() const { return m_freeCount; }
UInt32 FillCount() const { return m_size - m_freeCount; }
Item * Find(Key * key) const
{
if (!m_entries)
return NULL;
_Entry * entry = GetEntry(Item::GetHash(key));
if (! entry->next)
return NULL;
while (!(entry->item == *key))
{
entry = entry->next;
if (entry == m_eolPtr)
return NULL;
}
return &entry->item;
}
bool Add(Item * item)
{
InsertResult result;
for (result = Insert(item); result == kInsert_OutOfSpace; result = Insert(item))
Grow();
return result == kInsert_Success;
}
bool Remove(Key * key)
{
if ( !m_entries)
return false;
_Entry * entry = GetEntry(Item::GetHash(key));
if (! entry->next)
return NULL;
_Entry * prevEntry = NULL;
while (! (entry->item == *key))
{
prevEntry = entry;
entry = entry->next;
if (entry == m_eolPtr)
return false;
}
// Remove tail?
_Entry * nextEntry = entry->next;
if (nextEntry == m_eolPtr)
{
if (prevEntry)
prevEntry->next = m_eolPtr;
entry->next = NULL;
}
else
{
entry->item = nextEntry->item;
entry->next = nextEntry->next;
nextEntry->next = NULL;
}
++m_freeCount;
return true;
}
void Clear(void)
{
if (m_entries)
{
_Entry * p = m_entries;
for (UInt32 i = 0; i < m_size; i++, p++)
p->next = NULL;
}
else
{
m_size = 0;
}
m_freeCount = m_freeOffset = m_size;
}
template <typename T>
void ForEach(T& functor)
{
if (!m_entries)
return;
if(m_size == m_freeCount) // The whole thing is free
return;
_Entry * cur = m_entries;
_Entry * end = GetEntryAt(m_size); // one index beyond the entries data to check if we reached that point
if (cur == end)
return;
if (cur->IsFree())
{
// Forward to first non-free entry
do cur++;
while (cur != end && cur->IsFree());
}
do
{
if (! functor(&cur->item))
return;
// Forward to next non-free entry
do cur++;
while (cur != end && cur->IsFree());
} while (cur != end);
}
void Dump(void)
{
_MESSAGE("tHashSet:");
_MESSAGE("> size: %d", Size());
_MESSAGE("> free: %d", FreeCount());
_MESSAGE("> filled: %d", FillCount());
if (m_entries)
{
_Entry * p = m_entries;
for (UInt32 i = 0; i < m_size; i++, p++) {
_MESSAGE("* %d %s:", i, p->IsFree()?"(free)" : "");
if (!p->IsFree())
p->Dump();
}
}
}
DEFINE_STATIC_HEAP(Heap_Allocate, Heap_Free)
};
template <typename Key, typename Item>
typename tHashSet<Key,Item>::_Entry tHashSet<Key,Item>::sentinel = tHashSet<Key,Item>::_Entry();
// Don't know if this really is a native type or if sometimes locks are just placed before data structures.
template <typename T>
class SafeDataHolder
{
protected:
mutable SimpleLock m_lock;
public:
T m_data;
void Lock(void) { m_lock.Lock(); }
void Release(void) { m_lock.Release(); }
template<typename T> friend class SafeDataLocker;
};
// 18
// Some generic type but I haven't figured it out yet
class UpdateRegistrationHolder
{
// 018
class Registration
{
public:
volatile SInt32 refCount; // 00
bool runOnce; // 04
UInt8 pad05; // 05
UInt16 pad06; // 06
UInt32 schedTime; // 08
UInt32 interval; // 0C
UInt64 handle; // 10
void Release(void)
{
if (InterlockedDecrement(&refCount) == 0) Heap_Free(this);
}
};
tArray<Registration*> m_regs; // 00
void Order(UInt32 index);
public:
bool Remove(UInt64 & handle);
};