Source code of Windows XP (NT5)
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.

753 lines
17 KiB

  1. /*---------------------------------------------------------------------------
  2. File: VarMap.cpp
  3. Comments: This class implements a hash table which contains the keys stored in the varset,
  4. along with their values.
  5. CaseSensitive property - The case of each key is preserved as it was when
  6. the key was first added to the map. The hash function is not case sensitive,
  7. so the CaseSensitive property can be toggled on and off without rehashing the data.
  8. Optional indexing to allow for fast enumeration in alphabetical order by key.
  9. This will add overhead to insert operations. Without indexing, the contents of
  10. the map can be enumerated, but they will be in arbitrary order.
  11. Stream I/O functions for persistance.
  12. (c) Copyright 1995-1998, Mission Critical Software, Inc., All Rights Reserved
  13. Proprietary and confidential to Mission Critical Software, Inc.
  14. REVISION LOG ENTRY
  15. Revision By: Christy Boles
  16. Revised on 11/19/98 18:31:57
  17. ---------------------------------------------------------------------------
  18. */
  19. #include "stdafx.h"
  20. #include <afx.h>
  21. #include <afxplex_.h>
  22. #ifdef _DEBUG
  23. #define new DEBUG_NEW
  24. #undef THIS_FILE
  25. static char THIS_FILE[] = __FILE__;
  26. #endif
  27. #include "VarMap.h"
  28. #ifdef STRIPPED_VARSET
  29. #include "NoMcs.h"
  30. #else
  31. #pragma warning (push,3)
  32. #include "McString.h"
  33. #include "McLog.h"
  34. #pragma warning (pop)
  35. using namespace McString;
  36. #endif
  37. static inline void FreeString(CString* pOldData)
  38. {
  39. pOldData->~CString();
  40. }
  41. const UINT HashSizes[] = { 17, 251, 1049, 10753, 100417, 1299673 , 0 };
  42. CMapStringToVar::CMapStringToVar(BOOL isCaseSensitive, BOOL isIndexed, BOOL allowRehash,int nBlockSize)
  43. {
  44. ASSERT(nBlockSize > 0);
  45. m_pHashTable = NULL;
  46. m_nHashTableSize = HashSizes[0]; // default size
  47. m_nCount = 0;
  48. m_pFreeList = NULL;
  49. m_pBlocks = NULL;
  50. m_nBlockSize = nBlockSize;
  51. m_CaseSensitive = isCaseSensitive;
  52. m_Indexed = isIndexed;
  53. m_AllowRehash = allowRehash;
  54. }
  55. inline UINT CMapStringToVar::HashKey(LPCTSTR key) const
  56. {
  57. UINT nHash = 0;
  58. while (*key)
  59. {
  60. nHash = (nHash<<5) + nHash + toupper(*key++);
  61. }
  62. return nHash;
  63. }
  64. void CMapStringToVar::InitHashTable(
  65. UINT nHashSize, BOOL bAllocNow)
  66. //
  67. // Used to force allocation of a hash table or to override the default
  68. // hash table size (which is fairly small)
  69. {
  70. ASSERT_VALID(this);
  71. ASSERT(m_nCount == 0);
  72. ASSERT(nHashSize > 0);
  73. if (m_pHashTable != NULL)
  74. {
  75. // free hash table
  76. delete[] m_pHashTable;
  77. m_pHashTable = NULL;
  78. }
  79. if (bAllocNow)
  80. {
  81. m_pHashTable = new CHashItem* [nHashSize];
  82. if (!m_pHashTable)
  83. return;
  84. memset(m_pHashTable, 0, sizeof(CHashItem*) * nHashSize);
  85. }
  86. m_nHashTableSize = nHashSize;
  87. }
  88. void CMapStringToVar::ResizeTable()
  89. {
  90. // get the new size
  91. UINT nHashSize = 0;
  92. // find the current hash size in the array
  93. for ( int i = 0 ; HashSizes[i] <= m_nHashTableSize ; i++ )
  94. {
  95. if ( HashSizes[i] == m_nHashTableSize )
  96. {
  97. nHashSize = HashSizes[i+1];
  98. break;
  99. }
  100. }
  101. if ( nHashSize )
  102. {
  103. MC_LOGIF(VARSET_LOGLEVEL_INTERNAL,"Increasing hash size to "<< makeStr(nHashSize) );
  104. CHashItem ** oldHashTable = m_pHashTable;
  105. m_pHashTable = new CHashItem* [nHashSize];
  106. if (!m_pHashTable)
  107. return;
  108. memset(m_pHashTable,0, sizeof(CHashItem*) * nHashSize );
  109. // Rehash the existing items into the new table
  110. for ( UINT bucket = 0 ; bucket < m_nHashTableSize ; bucket++ )
  111. {
  112. CHashItem* pAssoc;
  113. CHashItem* pNext;
  114. for (pAssoc = oldHashTable[bucket]; pAssoc != NULL; pAssoc = pNext)
  115. {
  116. pNext = pAssoc->pNext;
  117. // Re-hash, and insert into new table
  118. pAssoc->nHashValue = HashKey(pAssoc->key) % nHashSize;
  119. pAssoc->pNext = m_pHashTable[pAssoc->nHashValue];
  120. m_pHashTable[pAssoc->nHashValue] = pAssoc;
  121. }
  122. }
  123. // cleanup the old table
  124. delete [] oldHashTable;
  125. m_nHashTableSize = nHashSize;
  126. }
  127. else
  128. {
  129. MC_LOG("Table size is "<< makeStr(m_nHashTableSize) << ". Larger hash size not found, disabling rehashing.");
  130. m_AllowRehash = FALSE;
  131. }
  132. }
  133. void CMapStringToVar::RemoveAll()
  134. {
  135. if ( m_Indexed )
  136. {
  137. m_Index.RemoveAll();
  138. }
  139. if (m_pHashTable != NULL)
  140. {
  141. // remove and destroy each element
  142. for (UINT nHash = 0; nHash < m_nHashTableSize; nHash++)
  143. {
  144. CHashItem* pAssoc;
  145. for (pAssoc = m_pHashTable[nHash]; pAssoc != NULL;
  146. pAssoc = pAssoc->pNext)
  147. {
  148. FreeString(&pAssoc->key);
  149. }
  150. }
  151. // free hash table
  152. delete [] m_pHashTable;
  153. m_pHashTable = NULL;
  154. }
  155. m_nCount = 0;
  156. m_pFreeList = NULL;
  157. m_pBlocks->FreeDataChain();
  158. m_pBlocks = NULL;
  159. }
  160. CMapStringToVar::~CMapStringToVar()
  161. {
  162. RemoveAll();
  163. ASSERT(m_nCount == 0);
  164. }
  165. /////////////////////////////////////////////////////////////////////////////
  166. // Assoc helpers
  167. CHashItem*
  168. CMapStringToVar::NewAssoc()
  169. {
  170. if (m_pFreeList == NULL)
  171. {
  172. // add another block
  173. CPlex* newBlock = CPlex::Create(m_pBlocks, m_nBlockSize,
  174. sizeof(CHashItem));
  175. // chain them into free list
  176. CHashItem* pAssoc = (CHashItem*) newBlock->data();
  177. // free in reverse order to make it easier to debug
  178. pAssoc += m_nBlockSize - 1;
  179. for (int i = m_nBlockSize-1; i >= 0; i--, pAssoc--)
  180. {
  181. pAssoc->pNext = m_pFreeList;
  182. m_pFreeList = pAssoc;
  183. }
  184. }
  185. ASSERT(m_pFreeList != NULL); // we must have something
  186. CHashItem* pAssoc = m_pFreeList;
  187. m_pFreeList = m_pFreeList->pNext;
  188. m_nCount++;
  189. ASSERT(m_nCount > 0); // make sure we don't overflow
  190. memcpy(&pAssoc->key, &afxEmptyString, sizeof(CString));
  191. pAssoc->value = 0;
  192. return pAssoc;
  193. }
  194. void CMapStringToVar::FreeAssoc(CHashItem* pAssoc)
  195. {
  196. FreeString(&pAssoc->key); // free up string data
  197. pAssoc->pNext = m_pFreeList;
  198. m_pFreeList = pAssoc;
  199. m_nCount--;
  200. MCSASSERT(m_nCount >= 0); // make sure we don't underflow
  201. // if no more elements, cleanup completely
  202. if (m_nCount == 0)
  203. RemoveAll();
  204. }
  205. CHashItem*
  206. CMapStringToVar::GetAssocAt(LPCTSTR key, UINT& nHash) const
  207. // find association (or return NULL)
  208. {
  209. nHash = HashKey(key) % m_nHashTableSize;
  210. if (m_pHashTable == NULL)
  211. return NULL;
  212. // see if it exists
  213. CHashItem* pAssoc;
  214. for (pAssoc = m_pHashTable[nHash]; pAssoc != NULL; pAssoc = pAssoc->pNext)
  215. {
  216. if ( m_CaseSensitive )
  217. {
  218. if (pAssoc->key == key)
  219. return pAssoc;
  220. }
  221. else
  222. {
  223. if ( ! pAssoc->key.CompareNoCase(key) )
  224. return pAssoc;
  225. }
  226. }
  227. return NULL;
  228. }
  229. /////////////////////////////////////////////////////////////////////////////
  230. BOOL CMapStringToVar::Lookup(LPCTSTR key, CVarData*& rValue) const
  231. {
  232. ASSERT_VALID(this);
  233. UINT nHash;
  234. CHashItem* pAssoc = GetAssocAt(key, nHash);
  235. if (pAssoc == NULL)
  236. return FALSE; // not in map
  237. rValue = pAssoc->value;
  238. return TRUE;
  239. }
  240. BOOL CMapStringToVar::LookupKey(LPCTSTR key, LPCTSTR& rKey) const
  241. {
  242. ASSERT_VALID(this);
  243. UINT nHash;
  244. CHashItem* pAssoc = GetAssocAt(key, nHash);
  245. if (pAssoc == NULL)
  246. return FALSE; // not in map
  247. rKey = pAssoc->key;
  248. return TRUE;
  249. }
  250. CVarData*& CMapStringToVar::operator[](LPCTSTR key)
  251. {
  252. ASSERT_VALID(this);
  253. UINT nHash;
  254. CHashItem* pAssoc;
  255. // Grow the hash table, if necessary
  256. if ( m_AllowRehash && ( m_nCount > 2 * m_nHashTableSize ) )
  257. {
  258. ResizeTable();
  259. }
  260. if ((pAssoc = GetAssocAt(key, nHash)) == NULL)
  261. {
  262. if (m_pHashTable == NULL)
  263. InitHashTable(m_nHashTableSize);
  264. // it doesn't exist, add a new Association
  265. pAssoc = NewAssoc();
  266. pAssoc->nHashValue = nHash;
  267. pAssoc->key = key;
  268. // put into hash table
  269. pAssoc->pNext = m_pHashTable[nHash];
  270. m_pHashTable[nHash] = pAssoc;
  271. if ( m_Indexed )
  272. {
  273. pAssoc->pIndex = m_Index.Insert(pAssoc);
  274. }
  275. else
  276. {
  277. pAssoc->pIndex = NULL;
  278. }
  279. }
  280. return pAssoc->value; // return new reference
  281. }
  282. void CMapStringToVar::SetIndexed(BOOL val)
  283. {
  284. POSITION pos = GetStartPosition();
  285. CString key;
  286. CVarData * value;
  287. if ( ! m_Indexed && val )
  288. {
  289. BuildIndex();
  290. }
  291. m_Indexed = val;
  292. // recursively update children
  293. while ( pos )
  294. {
  295. GetNextAssoc(pos,key,value);
  296. if ( value )
  297. {
  298. value->SetIndexed(val);
  299. }
  300. }
  301. }
  302. void CMapStringToVar::BuildIndex()
  303. {
  304. // delete any old entries
  305. m_Index.RemoveAll();
  306. CHashItem * pAssoc;
  307. POSITION pos = GetStartPosition();
  308. CString key;
  309. CVarData * value;
  310. UINT hash;
  311. while ( pos )
  312. {
  313. GetNextAssoc(pos,key,value);
  314. pAssoc = GetAssocAt(key,hash);
  315. if ( pAssoc )
  316. {
  317. pAssoc->pIndex = m_Index.Insert(pAssoc);
  318. if ( value->HasChildren() )
  319. {
  320. value->GetChildren()->SetIndexed(TRUE);
  321. }
  322. }
  323. }
  324. }
  325. BOOL CMapStringToVar::RemoveKey(LPCTSTR key)
  326. // remove key - return TRUE if removed
  327. {
  328. ASSERT_VALID(this);
  329. if (m_pHashTable == NULL)
  330. return FALSE; // nothing in the table
  331. CHashItem** ppAssocPrev;
  332. ppAssocPrev = &m_pHashTable[HashKey(key) % m_nHashTableSize];
  333. CHashItem* pAssoc;
  334. for (pAssoc = *ppAssocPrev; pAssoc != NULL; pAssoc = pAssoc->pNext)
  335. {
  336. if ( (m_CaseSensitive && (pAssoc->key == key) || !m_CaseSensitive && pAssoc->key.CompareNoCase(key) ) )
  337. {
  338. // remove it
  339. *ppAssocPrev = pAssoc->pNext; // remove from list
  340. FreeAssoc(pAssoc);
  341. return TRUE;
  342. }
  343. ppAssocPrev = &pAssoc->pNext;
  344. }
  345. return FALSE; // not found
  346. }
  347. /////////////////////////////////////////////////////////////////////////////
  348. // Iterating
  349. void CMapStringToVar::GetNextAssoc(POSITION& rNextPosition,
  350. CString& rKey, CVarData*& rValue) const
  351. {
  352. ASSERT_VALID(this);
  353. ASSERT(m_pHashTable != NULL); // never call on empty map
  354. CHashItem* pAssocRet = (CHashItem*)rNextPosition;
  355. ASSERT(pAssocRet != NULL);
  356. if (pAssocRet == (CHashItem*) BEFORE_START_POSITION)
  357. {
  358. // find the first association
  359. for (UINT nBucket = 0; nBucket < m_nHashTableSize; nBucket++)
  360. if ((pAssocRet = m_pHashTable[nBucket]) != NULL)
  361. break;
  362. ASSERT(pAssocRet != NULL); // must find something
  363. }
  364. // find next association
  365. ASSERT(AfxIsValidAddress(pAssocRet, sizeof(CHashItem)));
  366. CHashItem* pAssocNext;
  367. if ((pAssocNext = pAssocRet->pNext) == NULL)
  368. {
  369. // go to next bucket
  370. for (UINT nBucket = pAssocRet->nHashValue + 1;
  371. nBucket < m_nHashTableSize; nBucket++)
  372. if ((pAssocNext = m_pHashTable[nBucket]) != NULL)
  373. break;
  374. }
  375. rNextPosition = (POSITION) pAssocNext;
  376. // fill in return data
  377. rKey = pAssocRet->key;
  378. rValue = pAssocRet->value;
  379. }
  380. /////////////////////////////////////////////////////////////////////////////
  381. // Serialization
  382. void CMapStringToVar::Serialize(CArchive& ar)
  383. {
  384. ASSERT_VALID(this);
  385. CObject::Serialize(ar);
  386. if (ar.IsStoring())
  387. {
  388. ar.WriteCount(m_nCount);
  389. if (m_nCount == 0)
  390. return; // nothing more to do
  391. ASSERT(m_pHashTable != NULL);
  392. for (UINT nHash = 0; nHash < m_nHashTableSize; nHash++)
  393. {
  394. CHashItem* pAssoc;
  395. for (pAssoc = m_pHashTable[nHash]; pAssoc != NULL;
  396. pAssoc = pAssoc->pNext)
  397. {
  398. // ar << pAssoc->key;
  399. // ar << pAssoc->value;
  400. }
  401. }
  402. }
  403. else
  404. {
  405. // DWORD nNewCount = ar.ReadCount();
  406. // CString newKey;
  407. // CVarData* newValue;
  408. // while (nNewCount--)
  409. // {
  410. // ar >> newKey;
  411. // ar >> newValue;
  412. // SetAt(newKey, newValue);
  413. // }
  414. }
  415. }
  416. void CMapStringToVar::McLogInternalDiagnostics(CString keyName)
  417. {
  418. MC_LOGBLOCK("HashTable");
  419. MC_LOG(" " << String(keyName) << "Count="<<makeStr(m_nCount) << " Case Sensitive="<< (m_CaseSensitive?"TRUE":"FALSE") << " Indexed="<<(m_Indexed?"TRUE":"FALSE") );
  420. MC_LOG("TableSize="<<makeStr(m_nHashTableSize));
  421. for ( UINT i = 0 ; i < m_nHashTableSize ; i++ )
  422. {
  423. CHashItem * pAssoc;
  424. MC_LOG("Bucket " << makeStr(i));
  425. for ( pAssoc = m_pHashTable[i] ; pAssoc != NULL ; pAssoc=pAssoc->pNext)
  426. {
  427. if ( pAssoc->value )
  428. {
  429. CString subKey;
  430. subKey = keyName;
  431. if ( ! subKey.IsEmpty() )
  432. {
  433. subKey += _T(".");
  434. }
  435. subKey += pAssoc->key;
  436. pAssoc->value->McLogInternalDiagnostics(subKey);
  437. }
  438. if ( keyName.IsEmpty() )
  439. {
  440. MC_LOG(" Address="<< makeStr(pAssoc,L"0x%lx") << " Key="<< String(pAssoc->key));
  441. }
  442. else
  443. {
  444. MC_LOG(" Address="<< makeStr(pAssoc,L"0x%lx") << " Key="<< String(keyName) << "."<< String(pAssoc->key));
  445. }
  446. MC_LOG(" ValueAddress=" << makeStr(pAssoc->value,L"0x%lx") << " IndexAddress="<<makeStr(pAssoc->pIndex,L"0x%lx"));
  447. }
  448. }
  449. if ( m_Indexed )
  450. {
  451. m_Index.McLogInternalDiagnostics(keyName);
  452. }
  453. }
  454. HRESULT CMapStringToVar::WriteToStream(LPSTREAM pS)
  455. {
  456. HRESULT hr;
  457. ULONG result;
  458. CComBSTR str;
  459. do {
  460. hr = pS->Write(&m_nCount,(sizeof m_nCount),&result);
  461. if ( FAILED(hr) )
  462. break;
  463. if ( m_nCount )
  464. {
  465. for ( UINT nHash = 0 ; nHash < m_nHashTableSize ; nHash++ )
  466. {
  467. CHashItem * pAssoc;
  468. for ( pAssoc = m_pHashTable[nHash]; pAssoc != NULL ; pAssoc=pAssoc->pNext)
  469. {
  470. // write the key
  471. str = pAssoc->key;
  472. hr = str.WriteToStream(pS);
  473. if ( FAILED(hr) )
  474. break;
  475. // then the value
  476. hr = pAssoc->value->WriteToStream(pS);
  477. if ( FAILED(hr) )
  478. break;
  479. }
  480. if ( FAILED(hr) )
  481. break;
  482. }
  483. }
  484. }while ( FALSE );
  485. return hr;
  486. }
  487. HRESULT CMapStringToVar::ReadFromStream(LPSTREAM pS)
  488. {
  489. HRESULT hr;
  490. ULONG result;
  491. CComBSTR str;
  492. int count;
  493. do {
  494. hr = pS->Read(&count,(sizeof count),&result);
  495. if ( FAILED(hr) )
  496. break;
  497. if ( count )
  498. {
  499. // Find the closest hash table size to our count
  500. UINT nHashSize = HashSizes[0];
  501. for ( int size = 0 ; HashSizes[size] != 0 && nHashSize < (UINT)count ; size++ )
  502. {
  503. nHashSize = HashSizes[size];
  504. }
  505. InitHashTable(nHashSize);
  506. for ( int i = 0 ; i < count ; i++ )
  507. {
  508. CString key;
  509. CVarData * pObj = new CVarData;
  510. if (!pObj)
  511. return HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY);
  512. pObj->SetCaseSensitive(m_CaseSensitive);
  513. pObj->SetIndexed(m_Indexed);
  514. hr = str.ReadFromStream(pS);
  515. if ( FAILED(hr) )
  516. break;
  517. key = str;
  518. str.Empty();
  519. hr = pObj->ReadFromStream(pS);
  520. if ( FAILED(hr) )
  521. break;
  522. SetAt(key,pObj);
  523. }
  524. }
  525. }while ( FALSE );
  526. return hr;
  527. }
  528. DWORD
  529. CMapStringToVar::CalculateStreamedLength()
  530. {
  531. DWORD len = (sizeof m_nCount);
  532. if ( m_nCount )
  533. {
  534. for ( UINT nHash = 0 ; nHash < m_nHashTableSize ; nHash++ )
  535. {
  536. CHashItem * pAssoc;
  537. for ( pAssoc = m_pHashTable[nHash]; pAssoc != NULL ; pAssoc=pAssoc->pNext)
  538. {
  539. // add the length of the string
  540. len += (sizeof TCHAR)*(pAssoc->key.GetLength() + 2);
  541. // and the value
  542. if ( pAssoc->value)
  543. {
  544. len += pAssoc->value->CalculateStreamedLength();
  545. }
  546. }
  547. }
  548. }
  549. return len;
  550. }
  551. long
  552. CMapStringToVar::CountItems()
  553. {
  554. long count = 0;
  555. if ( m_nCount )
  556. {
  557. for ( UINT nHash = 0 ; nHash < m_nHashTableSize ; nHash++ )
  558. {
  559. CHashItem * pAssoc;
  560. for ( pAssoc = m_pHashTable[nHash]; pAssoc != NULL ; pAssoc=pAssoc->pNext)
  561. {
  562. // add the length of the string
  563. count += pAssoc->value->CountItems();
  564. }
  565. }
  566. }
  567. return count;
  568. }
  569. /////////////////////////////////////////////////////////////////////////////
  570. // Diagnostics
  571. #ifdef _DEBUG
  572. void CMapStringToVar::Dump(CDumpContext& dc) const
  573. {
  574. CObject::Dump(dc);
  575. dc << "with " << m_nCount << " elements";
  576. if (dc.GetDepth() > 0)
  577. {
  578. // Dump in format "[key] -> value"
  579. CString key;
  580. CVarData* val;
  581. POSITION pos = GetStartPosition();
  582. while (pos != NULL)
  583. {
  584. GetNextAssoc(pos, key, val);
  585. dc << "\n\t[" << key << "] = " << val;
  586. }
  587. }
  588. dc << "\n";
  589. }
  590. void CMapStringToVar::AssertValid() const
  591. {
  592. CObject::AssertValid();
  593. if ( m_Indexed )
  594. {
  595. //m_Index.AssertValid(m_nCount);
  596. }
  597. ASSERT(m_nHashTableSize > 0);
  598. ASSERT(m_nCount == 0 || m_pHashTable != NULL);
  599. // non-empty map should have hash table
  600. }
  601. #endif //_DEBUG
  602. #ifdef AFX_INIT_SEG
  603. #pragma code_seg(AFX_INIT_SEG)
  604. #endif
  605. IMPLEMENT_SERIAL(CMapStringToVar, CObject, 0)
  606. // BEGIN - STUFF FROM PLEX.CPP
  607. /////////////////////////////////////////////////////////////////////////////
  608. // CPlex
  609. CPlex* PASCAL CPlex::Create(CPlex*& pHead, UINT nMax, UINT cbElement)
  610. {
  611. ASSERT(nMax > 0 && cbElement > 0);
  612. CPlex* p = (CPlex*) new BYTE[sizeof(CPlex) + nMax * cbElement];
  613. if (!p)
  614. return NULL;
  615. // may throw exception
  616. p->pNext = pHead;
  617. pHead = p; // change head (adds in reverse order for simplicity)
  618. return p;
  619. }
  620. void CPlex::FreeDataChain() // free this one and links
  621. {
  622. CPlex* p = this;
  623. while (p != NULL)
  624. {
  625. BYTE* bytes = (BYTE*) p;
  626. CPlex* pNext = p->pNext;
  627. delete[] bytes;
  628. p = pNext;
  629. }
  630. }
  631. // END - STUFF FROM PLEX.CPP