353 lines
8.9 KiB
C
353 lines
8.9 KiB
C
|
/*
|
||
|
* BTreeLookup
|
||
|
*/
|
||
|
|
||
|
/*
|
||
|
* Stores data and associated key and uses a binary search for quick lookup
|
||
|
* Used if gets are much more frequent than gets
|
||
|
*
|
||
|
* Keys are compared as pointers. If fKeyIsWStr is true, Keys are dereferenced
|
||
|
* as WCHAR* and compared
|
||
|
*/
|
||
|
|
||
|
#ifndef DUI_BASE_BTREELOOKUP_H_INCLUDED
|
||
|
#define DUI_BASE_BTREELOOKUP_H_INCLUDED
|
||
|
|
||
|
#pragma once
|
||
|
|
||
|
namespace DirectUI
|
||
|
{
|
||
|
|
||
|
template <typename D> class BTreeLookup
|
||
|
{
|
||
|
typedef struct
|
||
|
{
|
||
|
void* pKey;
|
||
|
D tData;
|
||
|
} ENTRY, *PENTRY;
|
||
|
|
||
|
typedef void (*PBTENUMCALLBACK)(void* pKey, D tData);
|
||
|
|
||
|
public:
|
||
|
static HRESULT Create(bool fKeyIsWStr, OUT BTreeLookup<D>** ppBTree);
|
||
|
virtual ~BTreeLookup();
|
||
|
|
||
|
void Destroy() { HDelete< BTreeLookup<D> >(this); }
|
||
|
|
||
|
D* GetItem(void* pKey); // Pointer to Value (NULL if doesn't exist, internal copy returned)
|
||
|
HRESULT SetItem(void* pKey, D* ptData); // Setup Key/Value map, creates new is doesn't exist (via indirection)
|
||
|
HRESULT SetItem(void* pKey, D tData); // Setup Key/Value map, creates new is doesn't exist
|
||
|
void Remove(void* pKey); // Removes Key/Value map, ok if Key doesn't exist
|
||
|
void Enum(PBTENUMCALLBACK pfnCallback); // Callback with every item in map
|
||
|
|
||
|
static int __cdecl ENTRYCompare(const void* pA, const void* pB);
|
||
|
static int __cdecl WStrENTRYCompare(const void* pA, const void* pB);
|
||
|
|
||
|
BTreeLookup() { }
|
||
|
void Initialize(bool fKeyIsWStr);
|
||
|
|
||
|
private:
|
||
|
UINT _uListSize;
|
||
|
PENTRY _pList;
|
||
|
bool _fKeyIsWStr;
|
||
|
};
|
||
|
|
||
|
template <typename D> HRESULT BTreeLookup<D>::Create(bool fKeyIsWStr, OUT BTreeLookup<D>** ppBTree)
|
||
|
{
|
||
|
*ppBTree = NULL;
|
||
|
|
||
|
// Instantiate
|
||
|
BTreeLookup<D>* pbt = HNew< BTreeLookup<D> >();
|
||
|
if (!pbt)
|
||
|
return E_OUTOFMEMORY;
|
||
|
|
||
|
pbt->Initialize(fKeyIsWStr);
|
||
|
|
||
|
*ppBTree = pbt;
|
||
|
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
template <typename D> void BTreeLookup<D>::Initialize(bool fKeyIsWStr)
|
||
|
{
|
||
|
_uListSize = 0;
|
||
|
_pList = NULL;
|
||
|
_fKeyIsWStr = fKeyIsWStr;
|
||
|
}
|
||
|
|
||
|
template <typename D> BTreeLookup<D>::~BTreeLookup()
|
||
|
{
|
||
|
if (_pList)
|
||
|
HFree(_pList);
|
||
|
}
|
||
|
|
||
|
template <typename D> D* BTreeLookup<D>::GetItem(void* pKey)
|
||
|
{
|
||
|
DUIAssert(_fKeyIsWStr ? pKey != NULL : true, "pKey may not be NULL");
|
||
|
|
||
|
//PENTRY pEntry = NULL;
|
||
|
|
||
|
if (_pList)
|
||
|
{
|
||
|
//ENTRY eKey = { pKey }; // Create ENTRY key, populate key field
|
||
|
//pEntry = (PENTRY)bsearch(&eKey, _pList, _uListSize, sizeof(ENTRY), ENTRYCompare);
|
||
|
|
||
|
PENTRY pEntry;
|
||
|
int uPv;
|
||
|
int uLo = 0;
|
||
|
int uHi = _uListSize - 1;
|
||
|
while (uLo <= uHi)
|
||
|
{
|
||
|
uPv = (uHi + uLo) / 2;
|
||
|
|
||
|
pEntry = _pList + uPv;
|
||
|
|
||
|
// Locate
|
||
|
if (!_fKeyIsWStr)
|
||
|
{
|
||
|
// Key is numeric
|
||
|
if ((UINT_PTR)pKey == (UINT_PTR)pEntry->pKey)
|
||
|
return &(pEntry->tData);
|
||
|
|
||
|
if ((UINT_PTR)pKey < (UINT_PTR)pEntry->pKey)
|
||
|
uHi = uPv - 1;
|
||
|
else
|
||
|
uLo = uPv + 1;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Key is pointer to a wide string
|
||
|
int cmp = _wcsicmp((LPCWSTR)pKey, (LPCWSTR)pEntry->pKey);
|
||
|
|
||
|
if (!cmp)
|
||
|
return &(pEntry->tData);
|
||
|
|
||
|
if (cmp < 0)
|
||
|
uHi = uPv - 1;
|
||
|
else
|
||
|
uLo = uPv + 1;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//return pEntry ? &(pEntry->tData) : NULL;
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
template <typename D> HRESULT BTreeLookup<D>::SetItem(void* pKey, D tData)
|
||
|
{
|
||
|
D* pData = GetItem(pKey); // Find current entry (if exits)
|
||
|
|
||
|
if (pData)
|
||
|
{
|
||
|
// Entry found and have pointer to data of entry
|
||
|
*pData = tData;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Entry not found, allocate room for new entry, store, and sort
|
||
|
|
||
|
// New size
|
||
|
UINT uNewSize = _uListSize + 1;
|
||
|
|
||
|
if (_pList)
|
||
|
{
|
||
|
DUIAssert(uNewSize > 1, "Tracked list size and actual size differ");
|
||
|
|
||
|
PENTRY pNewList = (PENTRY)HReAlloc(_pList, sizeof(ENTRY) * uNewSize);
|
||
|
if (!pNewList)
|
||
|
return E_OUTOFMEMORY;
|
||
|
|
||
|
_pList = pNewList;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
DUIAssert(uNewSize == 1, "Tracked list size and actual list size differ");
|
||
|
|
||
|
_pList = (PENTRY)HAlloc(sizeof(ENTRY));
|
||
|
if (!_pList)
|
||
|
return E_OUTOFMEMORY;
|
||
|
}
|
||
|
|
||
|
// Update size
|
||
|
_uListSize = uNewSize;
|
||
|
|
||
|
// Store
|
||
|
_pList[_uListSize - 1].pKey = pKey;
|
||
|
_pList[_uListSize - 1].tData = tData;
|
||
|
|
||
|
// Sort
|
||
|
qsort(_pList, _uListSize, sizeof(ENTRY), !_fKeyIsWStr ? ENTRYCompare : WStrENTRYCompare);
|
||
|
}
|
||
|
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
template <typename D> HRESULT BTreeLookup<D>::SetItem(void* pKey, D* ptData)
|
||
|
{
|
||
|
D* pData = GetItem(pKey); // Find current entry (if exits)
|
||
|
|
||
|
if (pData)
|
||
|
{
|
||
|
// Entry found and have pointer to data of entry
|
||
|
*pData = *ptData;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Entry not found, allocate room for new entry, store, and sort
|
||
|
|
||
|
// New size
|
||
|
UINT uNewSize = _uListSize + 1;
|
||
|
|
||
|
if (_pList)
|
||
|
{
|
||
|
DUIAssert(uNewSize > 1, "Tracked list size and actual list size differ");
|
||
|
|
||
|
PENTRY pNewList = (PENTRY)HReAlloc(_pList, sizeof(ENTRY) * uNewSize);
|
||
|
if (!pNewList)
|
||
|
return E_OUTOFMEMORY;
|
||
|
|
||
|
_pList = pNewList;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
DUIAssert(uNewSize == 1, "Tracked list size and actual list size differ");
|
||
|
|
||
|
_pList = (PENTRY)HAlloc(sizeof(ENTRY));
|
||
|
if (!_pList)
|
||
|
return E_OUTOFMEMORY;
|
||
|
}
|
||
|
|
||
|
// Update size
|
||
|
_uListSize = uNewSize;
|
||
|
|
||
|
// Store
|
||
|
_pList[_uListSize - 1].pKey = pKey;
|
||
|
_pList[_uListSize - 1].tData = *ptData;
|
||
|
|
||
|
// Sort
|
||
|
qsort(_pList, _uListSize, sizeof(ENTRY), !_fKeyIsWStr ? ENTRYCompare : WStrENTRYCompare);
|
||
|
}
|
||
|
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
// Returns success even if key isn't found
|
||
|
template <typename D> void BTreeLookup<D>::Remove(void* pKey)
|
||
|
{
|
||
|
// Validate parameters
|
||
|
DUIAssert(_fKeyIsWStr ? pKey != NULL : true, "Invalid parameter: pKey == NULL");
|
||
|
|
||
|
if (_pList)
|
||
|
{
|
||
|
// Search for ENTRY with key
|
||
|
//ENTRY eKey = { pKey };
|
||
|
//PENTRY pEntry = (PENTRY)bsearch(&eKey, _pList, _uListSize, sizeof(ENTRY), ENTRYCompare);
|
||
|
|
||
|
PENTRY pEntry = NULL;
|
||
|
int uPv;
|
||
|
int uLo = 0;
|
||
|
int uHi = _uListSize - 1;
|
||
|
while (uLo <= uHi)
|
||
|
{
|
||
|
uPv = (uHi + uLo) / 2;
|
||
|
|
||
|
pEntry = _pList + uPv;
|
||
|
|
||
|
// Locate
|
||
|
if (!_fKeyIsWStr)
|
||
|
{
|
||
|
// Key is numeric
|
||
|
if ((UINT_PTR)pKey == (UINT_PTR)pEntry->pKey)
|
||
|
break;
|
||
|
|
||
|
if ((UINT_PTR)pKey < (UINT_PTR)pEntry->pKey)
|
||
|
uHi = uPv - 1;
|
||
|
else
|
||
|
uLo = uPv + 1;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Key is pointer to a wide string
|
||
|
int cmp = _wcsicmp((LPCWSTR)pKey, (LPCWSTR)pEntry->pKey);
|
||
|
|
||
|
if (!cmp)
|
||
|
break;
|
||
|
|
||
|
if (cmp < 0)
|
||
|
uHi = uPv - 1;
|
||
|
else
|
||
|
uLo = uPv + 1;
|
||
|
}
|
||
|
|
||
|
pEntry = NULL;
|
||
|
}
|
||
|
|
||
|
if (pEntry)
|
||
|
{
|
||
|
UINT uIndex = (UINT)(((UINT_PTR)pEntry - (UINT_PTR)_pList) / sizeof(ENTRY));
|
||
|
|
||
|
DUIAssert(uIndex < _uListSize, "Index out of bounds");
|
||
|
|
||
|
// ENTRY found, move all entries after this entry down
|
||
|
MoveMemory(pEntry, pEntry + 1, (_uListSize - uIndex - 1) * sizeof(ENTRY));
|
||
|
|
||
|
// One less entry
|
||
|
UINT uNewSize = _uListSize - 1;
|
||
|
|
||
|
// Trim allocation
|
||
|
if (uNewSize == 0)
|
||
|
{
|
||
|
HFree(_pList);
|
||
|
_pList = NULL;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
PENTRY pNewList = (PENTRY)HReAlloc(_pList, uNewSize * sizeof(ENTRY));
|
||
|
|
||
|
// List is becoming smaller, if re-allocation failed, keep previous and continue
|
||
|
if (pNewList)
|
||
|
_pList = pNewList;
|
||
|
}
|
||
|
|
||
|
// Update size
|
||
|
_uListSize = uNewSize;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
template <typename D> void BTreeLookup<D>::Enum(PBTENUMCALLBACK pfnCallback)
|
||
|
{
|
||
|
if (_pList)
|
||
|
{
|
||
|
for (UINT i = 0; i < _uListSize; i++)
|
||
|
pfnCallback(_pList[i].pKey, _pList[i].tData);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
template <typename D> int __cdecl BTreeLookup<D>::ENTRYCompare(const void* pA, const void* pB)
|
||
|
{
|
||
|
PENTRY pEA = (PENTRY)pA;
|
||
|
PENTRY pEB = (PENTRY)pB;
|
||
|
|
||
|
if ((UINT_PTR)pEA->pKey == (UINT_PTR)pEB->pKey)
|
||
|
return 0;
|
||
|
else if ((UINT_PTR)pEA->pKey < (UINT_PTR)pEB->pKey)
|
||
|
return -1;
|
||
|
else
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
template <typename D> int __cdecl BTreeLookup<D>::WStrENTRYCompare(const void* pA, const void* pB)
|
||
|
{
|
||
|
PENTRY pEA = (PENTRY)pA;
|
||
|
PENTRY pEB = (PENTRY)pB;
|
||
|
|
||
|
// Ignore case
|
||
|
return _wcsicmp((LPCWSTR)pEA->pKey, (LPCWSTR)pEB->pKey);
|
||
|
}
|
||
|
|
||
|
} // namespace DirectUI
|
||
|
|
||
|
#endif // DUI_BASE_BTREELOOKUP_H_INCLUDED
|