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.
452 lines
11 KiB
452 lines
11 KiB
/*
|
|
* Value map
|
|
*/
|
|
|
|
#ifndef DUI_BASE_VALUEMAP_H_INCLUDED
|
|
#define DUI_BASE_VALUEMAP_H_INCLUDED
|
|
|
|
#pragma once
|
|
|
|
namespace DirectUI
|
|
{
|
|
|
|
//-------------------------------------------------------------------------
|
|
//
|
|
// ValueMap
|
|
//
|
|
// Stores Key/Value pairs
|
|
//
|
|
// Compile DEBUG for DUIAsserts, see public class declarations for API
|
|
//
|
|
// Keys and Values are stored natively and the type of each can be chosen
|
|
// at compile time. For example (Key is an int, Value is a string pointer,
|
|
// and the map will have 5 buckets):
|
|
//
|
|
// ValueMap<int,LPWSTR>* pvm;
|
|
// ValueMap<int,LPWSTR>::Create(5, &pvm);
|
|
// pvm->SetItem(1150, L"One thousand one hundred and fifty");
|
|
// LPWSTR psz;
|
|
// pvm->GetItem(1150, &psz);
|
|
// DUITrace("%s\n", psz);
|
|
//
|
|
// The Key type must support the following operations:
|
|
// Assignment (=)
|
|
// Int cast for finding bucket (int)
|
|
// Equality (==)
|
|
//
|
|
// The Value type must support the following operation:
|
|
// Assignment (=)
|
|
//
|
|
// Given the above, a key can be created based on a string where the
|
|
// correct mapping occurs even though the instance of the string is different.
|
|
//
|
|
// class StringKey
|
|
// {
|
|
// public:
|
|
// StringKey(LPWSTR);
|
|
// operator =(LPWSTR);
|
|
// BOOL operator ==(StringKey);
|
|
// operator INT_PTR();
|
|
//
|
|
// private:
|
|
// LPWSTR pszStr;
|
|
// };
|
|
//
|
|
// StringKey::StringKey(LPWSTR pstr)
|
|
// {
|
|
// pszStr = pstr;
|
|
// }
|
|
//
|
|
// StringKey::operator =(LPWSTR pstr)
|
|
// {
|
|
// pszStr = pstr;
|
|
// }
|
|
//
|
|
// BOOL StringKey::operator ==(StringKey st)
|
|
// {
|
|
// return wcscmp(pszStr, st.pszStr) == 0;
|
|
// }
|
|
//
|
|
// StringKey::operator INT_PTR() // Create hash code from string
|
|
// {
|
|
// int dHash = 0;
|
|
// LPWSTR pstr = pszStr;
|
|
// WCHAR c;
|
|
//
|
|
// while (*pstr)
|
|
// {
|
|
// c = *pstr++;
|
|
// dHash += (c << 1) + (c >> 1) + c;
|
|
// }
|
|
//
|
|
// return dHash;
|
|
// }
|
|
//
|
|
// It's usage would be:
|
|
//
|
|
// ValueMap<StringKey, int> v(11);
|
|
//
|
|
// v.SetItem(L"My favorite number", 4);
|
|
// v.SetItem(L"Your favorite number", 8);
|
|
//
|
|
// Trace1(L"Mine : %d\n", *v.GetItem(L"My favorite number"); // 4
|
|
// Trace1(L"Yours: %d\n", *v.GetItem(L"Your favorite number"); // 8
|
|
//
|
|
// v.SetItem(L"My favorite number", 5150);
|
|
//
|
|
// Trace1(L"Mine : %d\n", *v.GetItem(L"My favorite number"); // 5150
|
|
//
|
|
// v.Remove(L"Your favorite number";
|
|
//
|
|
// DUIAssert(!v.ContainsKey(L"Your favorite number"), "Error!"); // Mapping is removed
|
|
//
|
|
//-------------------------------------------------------------------------
|
|
|
|
template <typename K, typename D> class ValueMap
|
|
{
|
|
typedef struct _ENTRY
|
|
{
|
|
bool fInUse;
|
|
K tKey;
|
|
D tData;
|
|
struct _ENTRY* peNext;
|
|
|
|
} ENTRY, *PENTRY;
|
|
|
|
typedef void (*VMENUMCALLBACK)(K tKey, D tData);
|
|
|
|
public: // API
|
|
static HRESULT Create(UINT uBuckets, OUT ValueMap<K,D>** ppMap);
|
|
virtual ~ValueMap();
|
|
void Destroy() { HDelete< ValueMap<K,D> >(this); }
|
|
|
|
D* GetItem(K, bool); // Pointer to Value (NULL if doesn't exist, internal copy returned)
|
|
HRESULT SetItem(K, D*, bool); // Setup Key/Value map, creates new is doesn't exist (via indirection)
|
|
HRESULT SetItem(K, D, bool); // Setup Key/Value map, creates new is doesn't exist
|
|
void Remove(K, bool, bool); // Removes Key/Value map, ok if Key doesn't exist
|
|
void Enum(VMENUMCALLBACK pfnCallback); // Callback with every item in map
|
|
bool IsEmpty(); // True if no entries
|
|
K* GetFirstKey(); // Returns pointer to first key found in table
|
|
HRESULT GetDistribution(WCHAR**); // Returns a null terminated string describing table distribution (must HFree)
|
|
|
|
ValueMap() { }
|
|
HRESULT Initialize(UINT uBuckets);
|
|
|
|
private:
|
|
UINT _uBuckets;
|
|
PENTRY* _ppBuckets;
|
|
};
|
|
|
|
template <typename K, typename D> HRESULT ValueMap<K,D>::Create(UINT uBuckets, OUT ValueMap<K,D>** ppMap)
|
|
{
|
|
DUIAssert(uBuckets > 0, "Must create at least one bucket in ValueMap");
|
|
|
|
*ppMap = NULL;
|
|
|
|
// Instantiate
|
|
ValueMap<K,D>* pvm = HNew< ValueMap<K,D> >();
|
|
if (!pvm)
|
|
return E_OUTOFMEMORY;
|
|
|
|
HRESULT hr = pvm->Initialize(uBuckets);
|
|
if (FAILED(hr))
|
|
{
|
|
pvm->Destroy();
|
|
return hr;
|
|
}
|
|
|
|
*ppMap = pvm;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
template <typename K, typename D> HRESULT ValueMap<K,D>::Initialize(UINT uBuckets)
|
|
{
|
|
_uBuckets = uBuckets;
|
|
_ppBuckets = (PENTRY*)HAllocAndZero(sizeof(PENTRY) * _uBuckets);
|
|
|
|
if (!_ppBuckets)
|
|
{
|
|
// Object isn't created if buckets cannot be allocated
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
template <typename K, typename D> ValueMap<K,D>::~ValueMap()
|
|
{
|
|
PENTRY pe;
|
|
PENTRY peNext;
|
|
|
|
for (UINT i = 0; i < _uBuckets; i++)
|
|
{
|
|
pe = _ppBuckets[i];
|
|
while (pe != NULL)
|
|
{
|
|
peNext = pe->peNext;
|
|
HFree(pe);
|
|
|
|
pe = peNext;
|
|
}
|
|
}
|
|
|
|
HFree(_ppBuckets);
|
|
}
|
|
|
|
template <typename K, typename D> void ValueMap<K,D>::Enum(VMENUMCALLBACK pfnCallback)
|
|
{
|
|
PENTRY pe;
|
|
for (UINT i = 0; i < _uBuckets; i++)
|
|
{
|
|
pe = _ppBuckets[i];
|
|
while (pe)
|
|
{
|
|
if (pe->fInUse)
|
|
pfnCallback(pe->tKey, pe->tData);
|
|
|
|
pe = pe->peNext;
|
|
}
|
|
}
|
|
}
|
|
|
|
template <typename K, typename D> K* ValueMap<K,D>::GetFirstKey()
|
|
{
|
|
PENTRY pe;
|
|
for (UINT i = 0; i < _uBuckets; i++)
|
|
{
|
|
pe = _ppBuckets[i];
|
|
while (pe)
|
|
{
|
|
if (pe->fInUse)
|
|
return &pe->tKey;
|
|
|
|
pe = pe->peNext;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
template <typename K, typename D> D* ValueMap<K,D>::GetItem(K tKey, bool fKeyIsPtr)
|
|
{
|
|
// Pointer based keys are shifted for better distribution
|
|
|
|
// Search for items in buckets
|
|
|
|
PENTRY pe = _ppBuckets[(UINT)(((fKeyIsPtr) ? (int)tKey >> 2 : (int)tKey) % _uBuckets)];
|
|
|
|
while (pe && !(pe->fInUse && (pe->tKey == tKey)))
|
|
{
|
|
pe = pe->peNext;
|
|
}
|
|
|
|
return (pe) ? &pe->tData : NULL;
|
|
}
|
|
|
|
// Stores the value of tData (via indirection)
|
|
template <typename K, typename D> HRESULT ValueMap<K,D>::SetItem(K tKey, D* pData, bool fKeyIsPtr)
|
|
{
|
|
// Pointer based keys are shifted for better distribution
|
|
|
|
PENTRY pe;
|
|
PENTRY pUnused = NULL;
|
|
UINT uBucket = (UINT)(((fKeyIsPtr) ? (int)tKey >> 2 : (int)tKey) % _uBuckets);
|
|
|
|
// Search for items in buckets
|
|
pe = _ppBuckets[uBucket];
|
|
|
|
while (pe && !(pe->fInUse && (pe->tKey == tKey)))
|
|
{
|
|
if (!pe->fInUse)
|
|
{
|
|
pUnused = pe;
|
|
}
|
|
|
|
pe = pe->peNext;
|
|
}
|
|
|
|
if (pe)
|
|
{
|
|
// Item found
|
|
pe->tData = *pData;
|
|
}
|
|
else
|
|
{
|
|
// Reuse or create new item
|
|
if (pUnused)
|
|
{
|
|
pUnused->fInUse = true;
|
|
pUnused->tKey = tKey;
|
|
pUnused->tData = *pData;
|
|
}
|
|
else
|
|
{
|
|
pe = (PENTRY)HAlloc(sizeof(ENTRY));
|
|
if (!pe)
|
|
return E_OUTOFMEMORY;
|
|
|
|
pe->fInUse = true;
|
|
pe->tKey = tKey;
|
|
pe->tData = *pData;
|
|
pe->peNext = _ppBuckets[uBucket];
|
|
|
|
_ppBuckets[uBucket] = pe;
|
|
}
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
// Stores the value of tData
|
|
template <typename K, typename D> HRESULT ValueMap<K,D>::SetItem(K tKey, D tData, bool fKeyIsPtr)
|
|
{
|
|
// Pointer based keys are shifted for better distribution
|
|
|
|
PENTRY pe;
|
|
PENTRY pUnused = NULL;
|
|
UINT uBucket = (UINT)(((fKeyIsPtr) ? (UINT_PTR)tKey >> 2 : (INT_PTR)tKey) % _uBuckets);
|
|
|
|
// Search for items in buckets
|
|
pe = _ppBuckets[uBucket];
|
|
|
|
while (pe && !(pe->fInUse && (pe->tKey == tKey)))
|
|
{
|
|
if (!pe->fInUse)
|
|
{
|
|
pUnused = pe;
|
|
}
|
|
|
|
pe = pe->peNext;
|
|
}
|
|
|
|
if (pe)
|
|
{
|
|
// Item found
|
|
pe->tData = tData;
|
|
}
|
|
else
|
|
{
|
|
// Reuse or create new item
|
|
if (pUnused)
|
|
{
|
|
pUnused->fInUse = true;
|
|
pUnused->tKey = tKey;
|
|
pUnused->tData = tData;
|
|
}
|
|
else
|
|
{
|
|
pe = (PENTRY)HAlloc(sizeof(ENTRY));
|
|
if (!pe)
|
|
return E_OUTOFMEMORY;
|
|
|
|
pe->fInUse = true;
|
|
pe->tKey = tKey;
|
|
pe->tData = tData;
|
|
pe->peNext = _ppBuckets[uBucket];
|
|
|
|
_ppBuckets[uBucket] = pe;
|
|
}
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
template <typename K, typename D> void ValueMap<K,D>::Remove(K tKey, bool fFree, bool fKeyIsPtr)
|
|
{
|
|
// Pointer based keys are shifted for better distribution
|
|
|
|
PENTRY pe;
|
|
PENTRY pePrev = NULL;
|
|
UINT uBucket = (UINT)(((fKeyIsPtr) ? (UINT_PTR)tKey >> 2 : (INT_PTR)tKey) % _uBuckets);
|
|
|
|
// Search for items in buckets
|
|
pe = _ppBuckets[uBucket];
|
|
|
|
while (pe && !(pe->fInUse && (pe->tKey == tKey)))
|
|
{
|
|
pePrev = pe; // Keep the previous item
|
|
pe = pe->peNext;
|
|
}
|
|
|
|
if (pe)
|
|
{
|
|
if (fFree)
|
|
{
|
|
if (pePrev != NULL)
|
|
{
|
|
pePrev->peNext = pe->peNext;
|
|
}
|
|
else
|
|
{
|
|
_ppBuckets[uBucket] = pe->peNext;
|
|
}
|
|
|
|
HFree(pe);
|
|
}
|
|
else
|
|
{
|
|
pe->fInUse = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
template <typename K, typename D> bool ValueMap<K,D>::IsEmpty()
|
|
{
|
|
PENTRY pe;
|
|
for (UINT i = 0; i < _uBuckets; i++)
|
|
{
|
|
pe = _ppBuckets[i];
|
|
while (pe != NULL)
|
|
{
|
|
if (pe->fInUse)
|
|
return false;
|
|
|
|
pe = pe->peNext;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
template <typename K, typename D> HRESULT ValueMap<K,D>::GetDistribution(OUT WCHAR** ppszDist)
|
|
{
|
|
*ppszDist = NULL;
|
|
|
|
LPWSTR pszOut = (LPWSTR)HAlloc((256 + _uBuckets * 24) * sizeof(WCHAR));
|
|
if (!pszOut)
|
|
return E_OUTOFMEMORY
|
|
|
|
WCHAR pszBuf[151];
|
|
|
|
swprintf(pszOut, L"Buckets for %x (Slots InUse/Total): %d - ", this, _uBuckets);
|
|
|
|
PENTRY pe;
|
|
UINT cInUse;
|
|
UINT cCount;
|
|
for (UINT i = 0; i < _uBuckets; i++)
|
|
{
|
|
pe = _ppBuckets[i];
|
|
|
|
cInUse = 0;
|
|
cCount = 0;
|
|
|
|
while (pe)
|
|
{
|
|
cCount++;
|
|
if (pe->fInUse)
|
|
cInUse++;
|
|
|
|
pe = pe->peNext;
|
|
}
|
|
|
|
swprintf(pszBuf, L"(B%d): %d/%d ", i, cInUse, cCount);
|
|
wcscat(pszOut, pszBuf);
|
|
}
|
|
|
|
return pszOut;
|
|
}
|
|
|
|
} // namespace DirectUI
|
|
|
|
#endif // DUI_BASE_VALUEMAP_H_INCLUDED
|