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.

692 lines
16 KiB

  1. /*===================================================================
  2. Microsoft Denali
  3. Microsoft Confidential.
  4. Copyright 1997 Microsoft Corporation. All Rights Reserved.
  5. File: idhash.cpp
  6. Owner: DmitryR
  7. Source file for the new hashing stuff
  8. ===================================================================*/
  9. #include "denpre.h"
  10. #pragma hdrstop
  11. #include "idhash.h"
  12. #include "memcls.h"
  13. #include "memchk.h"
  14. /*===================================================================
  15. C P t r A r r a y
  16. ===================================================================*/
  17. HRESULT CPtrArray::Insert
  18. (
  19. int iPos,
  20. void *pv
  21. )
  22. {
  23. if (!m_rgpvPtrs) // empty?
  24. {
  25. m_rgpvPtrs = (void **)malloc(m_dwInc * sizeof(void *));
  26. if (!m_rgpvPtrs)
  27. return E_OUTOFMEMORY;
  28. m_dwSize = m_dwInc;
  29. m_cPtrs = 0;
  30. }
  31. else if (m_cPtrs == m_dwSize) // full?
  32. {
  33. void **pNewPtrs = (void **)realloc
  34. (
  35. m_rgpvPtrs,
  36. (m_dwSize + m_dwInc) * sizeof(void *)
  37. );
  38. if (!pNewPtrs)
  39. return E_OUTOFMEMORY;
  40. m_rgpvPtrs = pNewPtrs;
  41. m_dwSize += m_dwInc;
  42. }
  43. if (iPos < 0)
  44. iPos = 0;
  45. if ((DWORD)iPos >= m_cPtrs) // append?
  46. {
  47. m_rgpvPtrs[m_cPtrs++] = pv;
  48. return S_OK;
  49. }
  50. memmove
  51. (
  52. &m_rgpvPtrs[iPos+1],
  53. &m_rgpvPtrs[iPos],
  54. (m_cPtrs-iPos) * sizeof(void *)
  55. );
  56. m_rgpvPtrs[iPos] = pv;
  57. m_cPtrs++;
  58. return S_OK;
  59. }
  60. HRESULT CPtrArray::Find
  61. (
  62. void *pv,
  63. int *piPos
  64. )
  65. const
  66. {
  67. Assert(piPos);
  68. for (DWORD i = 0; i < m_cPtrs; i++)
  69. {
  70. if (m_rgpvPtrs[i] == pv)
  71. {
  72. *piPos = i;
  73. return S_OK;
  74. }
  75. }
  76. // not found
  77. *piPos = -1;
  78. return S_FALSE;
  79. }
  80. HRESULT CPtrArray::Remove
  81. (
  82. void *pv
  83. )
  84. {
  85. HRESULT hr = S_FALSE;
  86. for (DWORD i = 0; i < m_cPtrs; i++)
  87. {
  88. if (m_rgpvPtrs[i] == pv)
  89. hr = Remove(i);
  90. }
  91. return hr;
  92. }
  93. HRESULT CPtrArray::Remove
  94. (
  95. int iPos
  96. )
  97. {
  98. Assert(iPos >= 0 && (DWORD)iPos < m_cPtrs);
  99. Assert(m_rgpvPtrs);
  100. // remove the element
  101. DWORD dwMoveSize = (m_cPtrs - iPos - 1) * sizeof(void *);
  102. if (dwMoveSize)
  103. memmove(&m_rgpvPtrs[iPos], &m_rgpvPtrs[iPos+1], dwMoveSize);
  104. m_cPtrs--;
  105. if (m_dwSize > 4*m_dwInc && m_dwSize > 8*m_cPtrs)
  106. {
  107. // collapse to 1/4 if size > 4 x increment and less < 1/8 full
  108. void **pNewPtrs = (void **)realloc
  109. (
  110. m_rgpvPtrs,
  111. (m_dwSize / 4) * sizeof(void *)
  112. );
  113. if (pNewPtrs)
  114. {
  115. m_rgpvPtrs = pNewPtrs;
  116. m_dwSize /= 4;
  117. }
  118. }
  119. return S_OK;
  120. }
  121. HRESULT CPtrArray::Clear()
  122. {
  123. if (m_rgpvPtrs)
  124. free(m_rgpvPtrs);
  125. m_dwSize = 0;
  126. m_rgpvPtrs = NULL;
  127. m_cPtrs = 0;
  128. return S_OK;
  129. }
  130. /*===================================================================
  131. C I d H a s h U n i t
  132. ===================================================================*/
  133. // Everything is inline for this structure. See the header file.
  134. /*===================================================================
  135. C I d H a s h A r r a y
  136. ===================================================================*/
  137. /*===================================================================
  138. For some hardcoded element counts (that relate to session hash),
  139. ACACHE is used for allocations
  140. This is wrapped in the two functions below.
  141. ===================================================================*/
  142. ACACHE_FSA_EXTERN(MemBlock128)
  143. ACACHE_FSA_EXTERN(MemBlock256)
  144. static inline void *AcacheAllocIdHashArray(DWORD cElems)
  145. {
  146. /* removed because in 64bit land it doesn't work because of padding
  147. void *pvMem;
  148. if (cElems == 13) { pvMem = ACACHE_FSA_ALLOC(MemBlock128); }
  149. else if (cElems == 31) { pvMem = ACACHE_FSA_ALLOC(MemBlock256); }
  150. else { pvMem = malloc(2*sizeof(USHORT) + cElems*sizeof(CIdHashElem)); }
  151. */
  152. return malloc(offsetof(CIdHashArray, m_rgElems) + cElems*sizeof(CIdHashElem));
  153. }
  154. static inline void AcacheFreeIdHashArray(CIdHashArray *pArray)
  155. {
  156. /* removed because in 64bit land it doesn't work because of padding
  157. if (pArray->m_cElems == 13) { ACACHE_FSA_FREE(MemBlock128, pArray); }
  158. else if (pArray->m_cElems == 31) { ACACHE_FSA_FREE(MemBlock256, pArray); }
  159. else { free(pArray); }
  160. */
  161. free(pArray);
  162. }
  163. /*===================================================================
  164. CIdHashArray::Alloc
  165. Static method.
  166. Allocates CIdHashArray as a memory block containing var length array.
  167. Parameters:
  168. cElems # of elements in the array
  169. Returns:
  170. Newly created CIdHashArray
  171. ===================================================================*/
  172. CIdHashArray *CIdHashArray::Alloc
  173. (
  174. DWORD cElems
  175. )
  176. {
  177. CIdHashArray *pArray = (CIdHashArray *)AcacheAllocIdHashArray(cElems);
  178. if (!pArray)
  179. return NULL;
  180. pArray->m_cElems = (USHORT)cElems;
  181. pArray->m_cNotNulls = 0;
  182. memset(&(pArray->m_rgElems[0]), 0, cElems * sizeof(CIdHashElem));
  183. return pArray;
  184. }
  185. /*===================================================================
  186. CIdHashArray::Alloc
  187. Static method.
  188. Frees allocated CIdHashArray as a memory block.
  189. Also frees any sub-arrays.
  190. Parameters:
  191. pArray CIdHashArray object to be freed
  192. Returns:
  193. ===================================================================*/
  194. void CIdHashArray::Free
  195. (
  196. CIdHashArray *pArray
  197. )
  198. {
  199. if (pArray->m_cNotNulls > 0)
  200. {
  201. for (DWORD i = 0; i < pArray->m_cElems; i++)
  202. {
  203. if (pArray->m_rgElems[i].FIsArray())
  204. Free(pArray->m_rgElems[i].PArray());
  205. }
  206. }
  207. AcacheFreeIdHashArray(pArray);
  208. }
  209. /*===================================================================
  210. CIdHashArray::Find
  211. Searches this array and any sub-arrays for an objects with the
  212. given id.
  213. Parameters:
  214. dwId id to look for
  215. ppvObj object found (if any)
  216. Returns:
  217. S_OK = found, S_FALSE = not found
  218. ===================================================================*/
  219. HRESULT CIdHashArray::Find
  220. (
  221. DWORD_PTR dwId,
  222. void **ppvObj
  223. )
  224. const
  225. {
  226. DWORD i = (DWORD)(dwId % m_cElems);
  227. if (m_rgElems[i].DWId() == dwId)
  228. {
  229. if (ppvObj)
  230. *ppvObj = m_rgElems[i].PObject();
  231. return S_OK;
  232. }
  233. if (m_rgElems[i].FIsArray())
  234. return m_rgElems[i].PArray()->Find(dwId, ppvObj);
  235. // Not found
  236. if (ppvObj)
  237. *ppvObj = NULL;
  238. return S_FALSE;
  239. }
  240. /*===================================================================
  241. CIdHashArray::Add
  242. Adds an object to this (or sub-) array by Id.
  243. Creates new sub-arrays as needed.
  244. Parameters:
  245. dwId object id
  246. pvObj object to add
  247. rgusSizes array of sizes (used when creating sub-arrays)
  248. Returns:
  249. HRESULT (S_OK = added)
  250. ===================================================================*/
  251. HRESULT CIdHashArray::Add
  252. (
  253. DWORD_PTR dwId,
  254. void *pvObj,
  255. USHORT *rgusSizes
  256. )
  257. {
  258. DWORD i = (DWORD)(dwId % m_cElems);
  259. if (m_rgElems[i].FIsEmpty()) {
  260. m_rgElems[i].SetToObject(dwId, pvObj);
  261. m_cNotNulls++;
  262. return S_OK;
  263. }
  264. // Advance array of sizes one level deeper
  265. if (rgusSizes[0]) // not at the end
  266. ++rgusSizes;
  267. if (m_rgElems[i].FIsObject()) {
  268. // this array logic can't handle adding the same ID twice. It will
  269. // loop endlessly. So, if an attempt is made to add the same
  270. // ID a second, return an error
  271. if (m_rgElems[i].DWId() == dwId) {
  272. return E_INVALIDARG;
  273. }
  274. // Old object already taken the slot - need to create new array
  275. // the size of first for three levels is predefined
  276. // increment by 1 thereafter
  277. CIdHashArray *pArray = Alloc (rgusSizes[0] ? rgusSizes[0] : m_cElems+1);
  278. if (!pArray)
  279. return E_OUTOFMEMORY;
  280. // Push the old object down into the array
  281. HRESULT hr = pArray->Add(m_rgElems[i].DWId(),
  282. m_rgElems[i].PObject(),
  283. rgusSizes);
  284. if (FAILED(hr))
  285. return hr;
  286. // Put array into slot
  287. m_rgElems[i].SetToArray(pArray);
  288. }
  289. Assert(m_rgElems[i].FIsArray());
  290. return m_rgElems[i].PArray()->Add(dwId, pvObj, rgusSizes);
  291. }
  292. /*===================================================================
  293. CIdHashArray::Remove
  294. Removes an object by id from this (or sub-) array.
  295. Removes empty sub-arrays.
  296. Parameters:
  297. dwId object id
  298. ppvObj object removed (out, optional)
  299. Returns:
  300. HRESULT (S_OK = removed, S_FALSE = not found)
  301. ===================================================================*/
  302. HRESULT CIdHashArray::Remove
  303. (
  304. DWORD_PTR dwId,
  305. void **ppvObj
  306. )
  307. {
  308. DWORD i = (DWORD)(dwId % m_cElems);
  309. if (m_rgElems[i].DWId() == dwId)
  310. {
  311. if (ppvObj)
  312. *ppvObj = m_rgElems[i].PObject();
  313. m_rgElems[i].SetToEmpty();
  314. m_cNotNulls--;
  315. return S_OK;
  316. }
  317. if (m_rgElems[i].FIsArray())
  318. {
  319. HRESULT hr = m_rgElems[i].PArray()->Remove(dwId, ppvObj);
  320. if (hr == S_OK && m_rgElems[i].PArray()->m_cNotNulls == 0)
  321. {
  322. Free(m_rgElems[i].PArray());
  323. m_rgElems[i].SetToEmpty();
  324. }
  325. return hr;
  326. }
  327. // Not found
  328. if (ppvObj)
  329. *ppvObj = NULL;
  330. return S_FALSE;
  331. }
  332. /*===================================================================
  333. CIdHashArray::Iterate
  334. Calls a supplied callback for each object in the array and sub-arrays.
  335. Parameters:
  336. pfnCB callback
  337. pvArg1, pvArg2 args to path to the callback
  338. Returns:
  339. IteratorCallbackCode what to do next?
  340. ===================================================================*/
  341. IteratorCallbackCode CIdHashArray::Iterate
  342. (
  343. PFNIDHASHCB pfnCB,
  344. void *pvArg1,
  345. void *pvArg2
  346. )
  347. {
  348. IteratorCallbackCode rc = iccContinue;
  349. for (DWORD i = 0; i < m_cElems; i++)
  350. {
  351. if (m_rgElems[i].FIsObject())
  352. {
  353. rc = (*pfnCB)(m_rgElems[i].PObject(), pvArg1, pvArg2);
  354. // remove if requested
  355. if (rc & (iccRemoveAndContinue|iccRemoveAndStop))
  356. {
  357. m_rgElems[i].SetToEmpty();
  358. m_cNotNulls--;
  359. }
  360. }
  361. else if (m_rgElems[i].FIsArray())
  362. {
  363. rc = m_rgElems[i].PArray()->Iterate(pfnCB, pvArg1, pvArg2);
  364. // remove sub-array if empty
  365. if (m_rgElems[i].PArray()->m_cNotNulls == 0)
  366. {
  367. Free(m_rgElems[i].PArray());
  368. m_rgElems[i].SetToEmpty();
  369. }
  370. }
  371. else
  372. {
  373. continue;
  374. }
  375. // stop if requested
  376. if (rc & (iccStop|iccRemoveAndStop))
  377. {
  378. rc = iccStop;
  379. break;
  380. }
  381. }
  382. return rc;
  383. }
  384. #ifdef DBG
  385. /*===================================================================
  386. CIdHashTable::Dump
  387. Dump hash table to a file (for debugging).
  388. Parameters:
  389. szFile file name where to dump
  390. Returns:
  391. ===================================================================*/
  392. void CIdHashArray::DumpStats
  393. (
  394. FILE *f,
  395. int nVerbose,
  396. DWORD iLevel,
  397. DWORD &cElems,
  398. DWORD &cSlots,
  399. DWORD &cArrays,
  400. DWORD &cDepth
  401. )
  402. const
  403. {
  404. if (nVerbose > 0)
  405. {
  406. for (DWORD t = 0; t < iLevel; t++) fprintf(f, "\t");
  407. fprintf(f, "Array (level=%d addr=%p) %d slots, %d not null:\n",
  408. iLevel, this, m_cElems, m_cNotNulls);
  409. }
  410. cSlots += m_cElems;
  411. cArrays++;
  412. if (iLevel > cDepth)
  413. cDepth = iLevel;
  414. for (DWORD i = 0; i < m_cElems; i++)
  415. {
  416. if (nVerbose > 1)
  417. {
  418. for (DWORD t = 0; t < iLevel; t++) fprintf(f, "\t");
  419. fprintf(f, "%[%08x:%p@%04d] ", m_rgElems[i].m_dw, m_rgElems[i].m_pv, i);
  420. }
  421. if (m_rgElems[i].FIsEmpty())
  422. {
  423. if (nVerbose > 1)
  424. fprintf(f, "NULL\n");
  425. }
  426. else if (m_rgElems[i].FIsObject())
  427. {
  428. if (nVerbose > 1)
  429. fprintf(f, "Object\n");
  430. cElems++;
  431. }
  432. else if (m_rgElems[i].FIsArray())
  433. {
  434. if (nVerbose > 1)
  435. fprintf(f, "Array:\n");
  436. m_rgElems[i].PArray()->DumpStats(f, nVerbose, iLevel+1,
  437. cElems, cSlots, cArrays, cDepth);
  438. }
  439. else
  440. {
  441. if (nVerbose > 1)
  442. fprintf(f, "BAD\n");
  443. }
  444. }
  445. }
  446. #endif
  447. /*===================================================================
  448. C I d H a s h T a b l e
  449. ===================================================================*/
  450. /*===================================================================
  451. CIdHashTable::Init
  452. Initialize id hash table. Does not allocate anything.
  453. Parameters:
  454. usSize1 size of the first level array
  455. usSize2 size of the 2nd level arrays (optional)
  456. usSize3 size of the 3rd level arrays (optional)
  457. Returns:
  458. S_OK
  459. ===================================================================*/
  460. HRESULT CIdHashTable::Init
  461. (
  462. USHORT usSize1,
  463. USHORT usSize2,
  464. USHORT usSize3
  465. )
  466. {
  467. Assert(!FInited());
  468. Assert(usSize1);
  469. m_rgusSizes[0] = usSize1; // size of first level array
  470. m_rgusSizes[1] = usSize2 ? usSize2 : 7;
  471. m_rgusSizes[2] = usSize3 ? usSize3 : 11;
  472. m_rgusSizes[3] = 0; // last one stays 0 to indicate
  473. // the end of predefined sizes
  474. m_pArray = NULL;
  475. return S_OK;
  476. }
  477. /*===================================================================
  478. CIdHashTable::UnInit
  479. Uninitialize id hash table. Frees all arrays.
  480. Parameters:
  481. Returns:
  482. S_OK
  483. ===================================================================*/
  484. HRESULT CIdHashTable::UnInit()
  485. {
  486. if (!FInited())
  487. {
  488. Assert(!m_pArray);
  489. return S_OK;
  490. }
  491. if (m_pArray)
  492. CIdHashArray::Free(m_pArray);
  493. m_pArray = NULL;
  494. m_rgusSizes[0] = 0;
  495. return S_OK;
  496. }
  497. #ifdef DBG
  498. /*===================================================================
  499. CIdHashTable::AssertValid
  500. Validates id hash table.
  501. Parameters:
  502. Returns:
  503. ===================================================================*/
  504. void CIdHashTable::AssertValid() const
  505. {
  506. Assert(FInited());
  507. }
  508. /*===================================================================
  509. CIdHashTable::Dump
  510. Dump hash table to a file (for debugging).
  511. Parameters:
  512. szFile file name where to dump
  513. Returns:
  514. ===================================================================*/
  515. void CIdHashTable::Dump
  516. (
  517. const char *szFile
  518. )
  519. const
  520. {
  521. Assert(FInited());
  522. Assert(szFile);
  523. FILE *f = fopen(szFile, "a");
  524. if (!f)
  525. return;
  526. fprintf(f, "ID Hash Table Dump:\n");
  527. DWORD cElems = 0;
  528. DWORD cSlots = 0;
  529. DWORD cArrays = 0;
  530. DWORD cDepth = 0;
  531. if (m_pArray)
  532. m_pArray->DumpStats(f, 1, 1, cElems, cSlots, cArrays, cDepth);
  533. fprintf(f, "Total %d Objects in %d Slots, %d Arrays, %d Max Depth\n\n",
  534. cElems, cSlots, cArrays, cDepth);
  535. fclose(f);
  536. }
  537. #endif
  538. /*===================================================================
  539. C H a s h L o c k
  540. ===================================================================*/
  541. /*===================================================================
  542. CHashLock::Init
  543. Initialize the critical section.
  544. Parameters:
  545. Returns:
  546. S_OK
  547. ===================================================================*/
  548. HRESULT CHashLock::Init()
  549. {
  550. Assert(!m_fInited);
  551. HRESULT hr;
  552. ErrInitCriticalSection(&m_csLock, hr);
  553. if (FAILED(hr))
  554. return hr;
  555. m_fInited = TRUE;
  556. return S_OK;
  557. }
  558. /*===================================================================
  559. CHashLock::UnInit
  560. Uninitialize the critical section.
  561. Parameters:
  562. Returns:
  563. S_OK
  564. ===================================================================*/
  565. HRESULT CHashLock::UnInit()
  566. {
  567. if (m_fInited)
  568. {
  569. DeleteCriticalSection(&m_csLock);
  570. m_fInited = FALSE;
  571. }
  572. return S_OK;
  573. }