|
|
1.1 ! root 1: ///////////////////////////////////////////////////////////////////////////// ! 2: // class CMapStringTo - a mapping from CStrings to 'VALUE's. ! 3: // ! 4: // This is a part of the Microsoft Foundation Classes C++ library. ! 5: // Copyright (C) 1992 Microsoft Corporation ! 6: // All rights reserved. ! 7: // ! 8: // This source code is only intended as a supplement to the ! 9: // Microsoft Foundation Classes Reference and Microsoft ! 10: // QuickHelp documentation provided with the library. ! 11: // See these sources for detailed information regarding the ! 12: // Microsoft Foundation Classes product. ! 13: ///////////////////////////////////////////////////////////////////////////// ! 14: ! 15: //$DECLARE_TEMPLATE ! 16: ! 17: ///////////////////////////////////////////////////////////////////////////// ! 18: ! 19: template<class VALUE, class ARG_VALUE, int IS_SERIAL, int HAS_CREATE> ! 20: class CMapStringTo<VALUE, ARG_VALUE, IS_SERIAL, HAS_CREATE> : public CObject ! 21: { ! 22: #if IS_SERIAL ! 23: DECLARE_SERIAL(CMapStringTo) ! 24: #else ! 25: DECLARE_DYNAMIC(CMapStringTo) ! 26: #endif //!IS_SERIAL ! 27: protected: ! 28: // Association ! 29: struct CAssoc ! 30: { ! 31: CAssoc* pNext; ! 32: UINT nHashValue; // needed for efficient iteration ! 33: CString key; ! 34: VALUE value; ! 35: }; ! 36: public: ! 37: ! 38: // Construction ! 39: CMapStringTo(int nBlockSize=10); ! 40: ! 41: // Attributes ! 42: // number of elements ! 43: int GetCount() const ! 44: { return m_nCount; } ! 45: BOOL IsEmpty() const ! 46: { return m_nCount == 0; } ! 47: // Lookup ! 48: BOOL Lookup(const char* key, VALUE& rValue) const; ! 49: ! 50: // Operations ! 51: // Lookup and add if not there ! 52: VALUE& operator[](const char* key); ! 53: ! 54: // add a new (key, value) pair ! 55: void SetAt(const char* key, ARG_VALUE newValue) ! 56: { (*this)[key] = newValue; } ! 57: ! 58: // removing existing (key, ?) pair ! 59: BOOL RemoveKey(const char* key); ! 60: void RemoveAll(); ! 61: ! 62: // iterating all (key, value) pairs ! 63: POSITION GetStartPosition() const ! 64: { return (m_nCount == 0) ? NULL : BEFORE_START_POSITION; } ! 65: void GetNextAssoc(POSITION& rNextPosition, CString& rKey, VALUE& rValue) const; ! 66: ! 67: // advanced features for derived classes ! 68: UINT GetHashTableSize() const ! 69: { return m_nHashTableSize; } ! 70: void InitHashTable(UINT hashSize); ! 71: ! 72: // Overridables: special non-virtual (see map implementation for details) ! 73: // Routine used to user-provided hash keys ! 74: UINT HashKey(const char* key) const; ! 75: ! 76: // Implementation ! 77: protected: ! 78: CAssoc** m_pHashTable; ! 79: UINT m_nHashTableSize; ! 80: int m_nCount; ! 81: CAssoc* m_pFreeList; ! 82: struct CPlex* m_pBlocks; ! 83: int m_nBlockSize; ! 84: ! 85: CAssoc* NewAssoc(); ! 86: void FreeAssoc(CAssoc*); ! 87: CAssoc* GetAssocAt(const char*, UINT&) const; ! 88: ! 89: public: ! 90: ~CMapStringTo(); ! 91: #if IS_SERIAL ! 92: void Serialize(CArchive&); ! 93: #endif //IS_SERIAL ! 94: #ifdef _DEBUG ! 95: void Dump(CDumpContext&) const; ! 96: void AssertValid() const; ! 97: #endif ! 98: }; ! 99: ! 100: //$IMPLEMENT_TEMPLATE ! 101: ///////////////////////////////////////////////////////////////////////////// ! 102: // ! 103: // Implementation of Map from CString to VALUE ! 104: // ! 105: ///////////////////////////////////////////////////////////////////////////// ! 106: ! 107: #include "afxcoll.h" ! 108: #pragma hdrstop ! 109: ! 110: #include "plex.h" ! 111: ! 112: #ifdef AFX_COLL_SEG ! 113: #pragma code_seg(AFX_COLL_SEG) ! 114: #endif ! 115: ! 116: #if IS_SERIAL ! 117: IMPLEMENT_SERIAL(CMapStringTo, CObject, 0); ! 118: #else ! 119: IMPLEMENT_DYNAMIC(CMapStringTo, CObject); ! 120: #endif //!IS_SERIAL ! 121: ! 122: #ifdef _DEBUG ! 123: #undef THIS_FILE ! 124: static char BASED_CODE THIS_FILE[] = __FILE__; ! 125: #endif ! 126: ! 127: #if HAS_CREATE ! 128: #include "elements.h" // used for special creation ! 129: #endif ! 130: ! 131: #define new DEBUG_NEW ! 132: ! 133: extern const CString NEAR afxEmptyString; // for creating empty key strings ! 134: ! 135: ///////////////////////////////////////////////////////////////////////////// ! 136: ! 137: template<class VALUE, class ARG_VALUE, int IS_SERIAL, int HAS_CREATE> ! 138: CMapStringTo<VALUE, ARG_VALUE, IS_SERIAL, HAS_CREATE>::CMapStringTo(int nBlockSize) ! 139: { ! 140: ASSERT(nBlockSize > 0); ! 141: ! 142: m_pHashTable = NULL; ! 143: m_nHashTableSize = 17; // default size ! 144: m_nCount = 0; ! 145: m_pFreeList = NULL; ! 146: m_pBlocks = NULL; ! 147: m_nBlockSize = nBlockSize; ! 148: } ! 149: ! 150: template<class VALUE, class ARG_VALUE, int IS_SERIAL, int HAS_CREATE> ! 151: inline UINT CMapStringTo<VALUE, ARG_VALUE, IS_SERIAL, HAS_CREATE>::HashKey(const char* key) const ! 152: { ! 153: register UINT nHash = 0; ! 154: while (*key) ! 155: nHash = (nHash<<5) + nHash + *key++; ! 156: return nHash; ! 157: } ! 158: ! 159: template<class VALUE, class ARG_VALUE, int IS_SERIAL, int HAS_CREATE> ! 160: void CMapStringTo<VALUE, ARG_VALUE, IS_SERIAL, HAS_CREATE>::InitHashTable(UINT nHashSize) ! 161: // ! 162: // Used to force allocation of a hash table or to override the default ! 163: // hash table size of (which is fairly small) ! 164: { ! 165: ASSERT_VALID(this); ! 166: ASSERT(m_nCount == 0); ! 167: ASSERT(nHashSize > 0); ! 168: ! 169: // if had a hash table - get rid of it ! 170: if (m_pHashTable != NULL) ! 171: delete m_pHashTable; ! 172: m_pHashTable = NULL; ! 173: ! 174: m_pHashTable = new CAssoc* [nHashSize]; ! 175: memset(m_pHashTable, 0, sizeof(CAssoc*) * nHashSize); ! 176: m_nHashTableSize = nHashSize; ! 177: } ! 178: ! 179: template<class VALUE, class ARG_VALUE, int IS_SERIAL, int HAS_CREATE> ! 180: void CMapStringTo<VALUE, ARG_VALUE, IS_SERIAL, HAS_CREATE>::RemoveAll() ! 181: { ! 182: ASSERT_VALID(this); ! 183: ! 184: if (m_pHashTable != NULL) ! 185: { ! 186: // destroy elements ! 187: for (UINT nHash = 0; nHash < m_nHashTableSize; nHash++) ! 188: { ! 189: register CAssoc* pAssoc; ! 190: for (pAssoc = m_pHashTable[nHash]; pAssoc != NULL; ! 191: pAssoc = pAssoc->pNext) ! 192: { ! 193: pAssoc->key.Empty(); // free up string data ! 194: #if HAS_CREATE ! 195: DestructElement(&pAssoc->value); ! 196: #endif ! 197: } ! 198: } ! 199: ! 200: // free hash table ! 201: delete m_pHashTable; ! 202: m_pHashTable = NULL; ! 203: } ! 204: ! 205: m_nCount = 0; ! 206: m_pFreeList = NULL; ! 207: m_pBlocks->FreeDataChain(); ! 208: m_pBlocks = NULL; ! 209: } ! 210: ! 211: template<class VALUE, class ARG_VALUE, int IS_SERIAL, int HAS_CREATE> ! 212: CMapStringTo<VALUE, ARG_VALUE, IS_SERIAL, HAS_CREATE>::~CMapStringTo() ! 213: { ! 214: RemoveAll(); ! 215: ASSERT(m_nCount == 0); ! 216: } ! 217: ! 218: ///////////////////////////////////////////////////////////////////////////// ! 219: // Assoc helpers ! 220: // same as CList implementation except we store CAssoc's not CNode's ! 221: // and CAssoc's are singly linked all the time ! 222: ! 223: template<class VALUE, class ARG_VALUE, int IS_SERIAL, int HAS_CREATE> ! 224: CMapStringTo<VALUE, ARG_VALUE, IS_SERIAL, HAS_CREATE>::CAssoc* CMapStringTo::NewAssoc() ! 225: { ! 226: if (m_pFreeList == NULL) ! 227: { ! 228: // add another block ! 229: CPlex* newBlock = CPlex::Create(m_pBlocks, m_nBlockSize, ! 230: sizeof(CMapStringTo::CAssoc)); ! 231: // chain them into free list ! 232: register CMapStringTo::CAssoc* pAssoc = ! 233: (CMapStringTo::CAssoc*) newBlock->data(); ! 234: // free in reverse order to make it easier to debug ! 235: pAssoc += m_nBlockSize - 1; ! 236: for (int i = m_nBlockSize-1; i >= 0; i--, pAssoc--) ! 237: { ! 238: pAssoc->pNext = m_pFreeList; ! 239: m_pFreeList = pAssoc; ! 240: } ! 241: } ! 242: ASSERT(m_pFreeList != NULL); // we must have something ! 243: ! 244: CMapStringTo::CAssoc* pAssoc = m_pFreeList; ! 245: m_pFreeList = m_pFreeList->pNext; ! 246: m_nCount++; ! 247: ASSERT(m_nCount > 0); // make sure we don't overflow ! 248: memcpy(&pAssoc->key, &afxEmptyString, sizeof(CString)); ! 249: #if HAS_CREATE ! 250: ConstructElement(&pAssoc->value); ! 251: #else ! 252: memset(&pAssoc->value, 0, sizeof(VALUE)); ! 253: #endif ! 254: return pAssoc; ! 255: } ! 256: ! 257: template<class VALUE, class ARG_VALUE, int IS_SERIAL, int HAS_CREATE> ! 258: void CMapStringTo<VALUE, ARG_VALUE, IS_SERIAL, HAS_CREATE>::FreeAssoc(CMapStringTo::CAssoc* pAssoc) ! 259: { ! 260: pAssoc->key.Empty(); // free up string data ! 261: #if HAS_CREATE ! 262: DestructElement(&pAssoc->value); ! 263: #endif ! 264: pAssoc->pNext = m_pFreeList; ! 265: m_pFreeList = pAssoc; ! 266: m_nCount--; ! 267: ASSERT(m_nCount >= 0); // make sure we don't underflow ! 268: } ! 269: ! 270: template<class VALUE, class ARG_VALUE, int IS_SERIAL, int HAS_CREATE> ! 271: CMapStringTo<VALUE, ARG_VALUE, IS_SERIAL, HAS_CREATE>::CAssoc* ! 272: CMapStringTo<VALUE, ARG_VALUE, IS_SERIAL, HAS_CREATE>::GetAssocAt(const char* key, UINT& nHash) const ! 273: // find association (or return NULL) ! 274: { ! 275: nHash = HashKey(key) % m_nHashTableSize; ! 276: ! 277: if (m_pHashTable == NULL) ! 278: return NULL; ! 279: ! 280: // see if it exists ! 281: register CAssoc* pAssoc; ! 282: for (pAssoc = m_pHashTable[nHash]; pAssoc != NULL; pAssoc = pAssoc->pNext) ! 283: { ! 284: if (pAssoc->key == key) ! 285: return pAssoc; ! 286: } ! 287: return NULL; ! 288: } ! 289: ! 290: ///////////////////////////////////////////////////////////////////////////// ! 291: ! 292: template<class VALUE, class ARG_VALUE, int IS_SERIAL, int HAS_CREATE> ! 293: BOOL CMapStringTo<VALUE, ARG_VALUE, IS_SERIAL, HAS_CREATE>::Lookup(const char* key, VALUE& rValue) const ! 294: { ! 295: ASSERT_VALID(this); ! 296: ! 297: UINT nHash; ! 298: register CAssoc* pAssoc = GetAssocAt(key, nHash); ! 299: if (pAssoc == NULL) ! 300: return FALSE; // not in map ! 301: ! 302: rValue = pAssoc->value; ! 303: return TRUE; ! 304: } ! 305: ! 306: template<class VALUE, class ARG_VALUE, int IS_SERIAL, int HAS_CREATE> ! 307: VALUE& CMapStringTo<VALUE, ARG_VALUE, IS_SERIAL, HAS_CREATE>::operator[](const char* key) ! 308: { ! 309: ASSERT_VALID(this); ! 310: ! 311: UINT nHash; ! 312: register CAssoc* pAssoc; ! 313: if ((pAssoc = GetAssocAt(key, nHash)) == NULL) ! 314: { ! 315: if (m_pHashTable == NULL) ! 316: InitHashTable(m_nHashTableSize); ! 317: ! 318: // it doesn't exist, add a new Association ! 319: pAssoc = NewAssoc(); ! 320: pAssoc->nHashValue = nHash; ! 321: pAssoc->key = key; ! 322: // 'pAssoc->value' is a constructed object, nothing more ! 323: ! 324: // put into hash table ! 325: pAssoc->pNext = m_pHashTable[nHash]; ! 326: m_pHashTable[nHash] = pAssoc; ! 327: } ! 328: return pAssoc->value; // return new reference ! 329: } ! 330: ! 331: ! 332: template<class VALUE, class ARG_VALUE, int IS_SERIAL, int HAS_CREATE> ! 333: BOOL CMapStringTo<VALUE, ARG_VALUE, IS_SERIAL, HAS_CREATE>::RemoveKey(const char* key) ! 334: // remove key - return TRUE if removed ! 335: { ! 336: ASSERT_VALID(this); ! 337: ! 338: if (m_pHashTable == NULL) ! 339: return FALSE; // nothing in the table ! 340: ! 341: CAssoc** ppAssocPrev; ! 342: ppAssocPrev = &m_pHashTable[HashKey(key) % m_nHashTableSize]; ! 343: ! 344: CAssoc* pAssoc; ! 345: for (pAssoc = *ppAssocPrev; pAssoc != NULL; pAssoc = pAssoc->pNext) ! 346: { ! 347: if (pAssoc->key == key) ! 348: { ! 349: // remove it ! 350: *ppAssocPrev = pAssoc->pNext; // remove from list ! 351: FreeAssoc(pAssoc); ! 352: return TRUE; ! 353: } ! 354: ppAssocPrev = &pAssoc->pNext; ! 355: } ! 356: return FALSE; // not found ! 357: } ! 358: ! 359: ! 360: ///////////////////////////////////////////////////////////////////////////// ! 361: // Iterating ! 362: ! 363: template<class VALUE, class ARG_VALUE, int IS_SERIAL, int HAS_CREATE> ! 364: void CMapStringTo<VALUE, ARG_VALUE, IS_SERIAL, HAS_CREATE>::GetNextAssoc(POSITION& rNextPosition, ! 365: CString& rKey, VALUE& rValue) const ! 366: { ! 367: ASSERT_VALID(this); ! 368: ASSERT(m_pHashTable != NULL); // never call on empty map ! 369: ! 370: register CAssoc* pAssocRet = (CAssoc*)rNextPosition; ! 371: ASSERT(pAssocRet != NULL); ! 372: ! 373: if (pAssocRet == (CAssoc*) BEFORE_START_POSITION) ! 374: { ! 375: // find the first association ! 376: for (UINT nBucket = 0; nBucket < m_nHashTableSize; nBucket++) ! 377: if ((pAssocRet = m_pHashTable[nBucket]) != NULL) ! 378: break; ! 379: ASSERT(pAssocRet != NULL); // must find something ! 380: } ! 381: ! 382: // find next association ! 383: register CAssoc* pAssocNext; ! 384: if ((pAssocNext = pAssocRet->pNext) == NULL) ! 385: { ! 386: // go to next bucket ! 387: for (UINT nBucket = pAssocRet->nHashValue + 1; ! 388: nBucket < m_nHashTableSize; nBucket++) ! 389: if ((pAssocNext = m_pHashTable[nBucket]) != NULL) ! 390: break; ! 391: } ! 392: ! 393: rNextPosition = (POSITION) pAssocNext; ! 394: ! 395: // fill in return data ! 396: rKey = pAssocRet->key; ! 397: rValue = pAssocRet->value; ! 398: } ! 399: ! 400: ///////////////////////////////////////////////////////////////////////////// ! 401: // Serialization ! 402: ! 403: #if IS_SERIAL ! 404: template<class VALUE, class ARG_VALUE, int IS_SERIAL, int HAS_CREATE> ! 405: void CMapStringTo<VALUE, ARG_VALUE, IS_SERIAL, HAS_CREATE>::Serialize(CArchive& ar) ! 406: { ! 407: ASSERT_VALID(this); ! 408: ! 409: CObject::Serialize(ar); ! 410: ! 411: if (ar.IsStoring()) ! 412: { ! 413: ar << (WORD) m_nCount; ! 414: if (m_nCount == 0) ! 415: return; // nothing more to do ! 416: ! 417: ASSERT(m_pHashTable != NULL); ! 418: for (UINT nHash = 0; nHash < m_nHashTableSize; nHash++) ! 419: { ! 420: register CAssoc* pAssoc; ! 421: for (pAssoc = m_pHashTable[nHash]; pAssoc != NULL; ! 422: pAssoc = pAssoc->pNext) ! 423: { ! 424: ar << pAssoc->key; ! 425: ar << pAssoc->value; ! 426: } ! 427: } ! 428: } ! 429: else ! 430: { ! 431: WORD wNewCount; ! 432: ar >> wNewCount; ! 433: while (wNewCount--) ! 434: { ! 435: CString newKey; ! 436: VALUE newValue; ! 437: ar >> newKey; ! 438: ar >> newValue; ! 439: SetAt(newKey, newValue); ! 440: } ! 441: } ! 442: } ! 443: #endif //IS_SERIAL ! 444: ! 445: ///////////////////////////////////////////////////////////////////////////// ! 446: // Diagnostics ! 447: ! 448: #ifdef _DEBUG ! 449: ! 450: template<class VALUE, class ARG_VALUE, int IS_SERIAL, int HAS_CREATE> ! 451: void CMapStringTo<VALUE, ARG_VALUE, IS_SERIAL, HAS_CREATE>::Dump(CDumpContext& dc) const ! 452: { ! 453: ASSERT_VALID(this); ! 454: ! 455: #define MAKESTRING(x) #x ! 456: dc << "A " MAKESTRING(CMapStringTo) " with " << m_nCount << " elements\n"; ! 457: #undef MAKESTRING ! 458: if (dc.GetDepth() > 0) ! 459: { ! 460: // Dump in format "[key] -> value" ! 461: dc << "\n"; ! 462: POSITION pos = GetStartPosition(); ! 463: CString key; ! 464: VALUE val; ! 465: while (pos != NULL) ! 466: { ! 467: GetNextAssoc(pos, key, val); ! 468: dc << "\n\t[" << key << "] = " << val; ! 469: } ! 470: } ! 471: } ! 472: ! 473: template<class VALUE, class ARG_VALUE, int IS_SERIAL, int HAS_CREATE> ! 474: void CMapStringTo<VALUE, ARG_VALUE, IS_SERIAL, HAS_CREATE>::AssertValid() const ! 475: { ! 476: CObject::AssertValid(); ! 477: ! 478: ASSERT(m_nHashTableSize > 0); ! 479: ASSERT(m_nCount == 0 || m_pHashTable != NULL); ! 480: // non-empty map should have hash table ! 481: } ! 482: #endif //_DEBUG ! 483: ! 484: /////////////////////////////////////////////////////////////////////////////
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.