/* * 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 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** ppBTree); virtual ~BTreeLookup(); void Destroy() { HDelete< BTreeLookup >(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 HRESULT BTreeLookup::Create(bool fKeyIsWStr, OUT BTreeLookup** ppBTree) { *ppBTree = NULL; // Instantiate BTreeLookup* pbt = HNew< BTreeLookup >(); if (!pbt) return E_OUTOFMEMORY; pbt->Initialize(fKeyIsWStr); *ppBTree = pbt; return S_OK; } template void BTreeLookup::Initialize(bool fKeyIsWStr) { _uListSize = 0; _pList = NULL; _fKeyIsWStr = fKeyIsWStr; } template BTreeLookup::~BTreeLookup() { if (_pList) HFree(_pList); } template D* BTreeLookup::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 HRESULT BTreeLookup::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 HRESULT BTreeLookup::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 void BTreeLookup::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 void BTreeLookup::Enum(PBTENUMCALLBACK pfnCallback) { if (_pList) { for (UINT i = 0; i < _uListSize; i++) pfnCallback(_pList[i].pKey, _pList[i].tData); } } template int __cdecl BTreeLookup::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 int __cdecl BTreeLookup::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