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.

868 lines
20 KiB

  1. //+---------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. // Copyright (C) Microsoft Corporation, 1992 - 2000.
  5. //
  6. // File: twidhash.hxx
  7. //
  8. // Contents: WORKID Hash Table (Closed) implementation for mapping
  9. // WORKIDs to a 32bit value (row offset in the table).
  10. // Classes:
  11. //
  12. // Functions:
  13. //
  14. // History: 11-21-94 srikants Created
  15. // 2-16-95 srikants Modified to use templates
  16. //
  17. // Notes : The "WorkId" itself will be used as a hash value as it is
  18. // supposed to be unique for this implementation.
  19. //
  20. //----------------------------------------------------------------------------
  21. #pragma once
  22. //
  23. // Bit 32 of an WORKID is not set for any valid wid. It is used by OFS
  24. // to indicate a deleted WORKID. We will use that fact to mark deleted
  25. // entries in the hash table.
  26. //
  27. const widDeleted = 0x80000000; // Used for marking "deleted" entries
  28. #include <pshpack4.h>
  29. class CWidHashEntry
  30. {
  31. public:
  32. CWidHashEntry( WORKID wid = widInvalid ) : _wid(wid) { }
  33. void SetWorkId( WORKID wid ) { _wid = wid; }
  34. WORKID WorkId() const
  35. {
  36. return _wid;
  37. }
  38. ULONG HashValue() const { return _wid; }
  39. BOOL IsDeleted() const
  40. {
  41. return widDeleted == _wid;
  42. }
  43. BOOL IsFree() const
  44. {
  45. return (widInvalid == _wid) || (widDeleted == _wid);
  46. }
  47. BOOL IsValid() const
  48. {
  49. return widInvalid != _wid;
  50. }
  51. protected:
  52. WORKID _wid; // WorkId being hashed (hash Key)
  53. };
  54. class CWidValueHashEntry : public CWidHashEntry
  55. {
  56. public:
  57. CWidValueHashEntry( WORKID wid=widInvalid, ULONG value = 0 )
  58. : CWidHashEntry( wid ), _value(value)
  59. {
  60. }
  61. void SetValue( ULONG value )
  62. {
  63. _value = value;
  64. }
  65. void Get( WORKID & wid, ULONG & value ) const
  66. {
  67. wid = _wid;
  68. value = _value;
  69. }
  70. ULONG Value() const
  71. {
  72. return _value;
  73. }
  74. private:
  75. ULONG _value; // The value for the BookMark (hash Value)
  76. };
  77. class CDirWidHashEntry: public CWidHashEntry
  78. {
  79. public:
  80. CDirWidHashEntry ( WORKID wid=widInvalid, BOOL fDeep=FALSE, BOOL fInScope=FALSE )
  81. : CWidHashEntry( wid ), _fDeep(fDeep), _fInScope(fInScope)
  82. {
  83. }
  84. BOOL fInScope()const
  85. {
  86. return _fInScope;
  87. }
  88. BOOL fDeep ()const
  89. {
  90. return _fDeep;
  91. }
  92. private:
  93. BOOL _fInScope;
  94. BOOL _fDeep;
  95. };
  96. #include <poppack.h>
  97. const TWID_INIT_HASH_SIZE = 31;
  98. const MEASURE_OF_SLACK = 3;
  99. template <class T> class TWidHashTable
  100. {
  101. public:
  102. TWidHashTable( ULONG cHashInit = TWID_INIT_HASH_SIZE,
  103. BOOL fReHash = TRUE );
  104. ~TWidHashTable();
  105. BOOL IsWorkIdPresent( WORKID wid )
  106. {
  107. ULONG iTable;
  108. return _LookUp( wid, iTable );
  109. }
  110. ULONG AddEntry( const T & entry );
  111. T & FindOrAdd( WORKID wid );
  112. void ReplaceOrAddEntry( const T & entry );
  113. BOOL DeleteWorkId( WORKID wid );
  114. void DeleteItem( T & item );
  115. BOOL LookUpWorkId( T & entry );
  116. void DeleteAllEntries();
  117. const T & GetEntry( ULONG i )
  118. {
  119. Win4Assert( i < _size );
  120. return _pTable[i];
  121. }
  122. ULONG Size() const { return _size; }
  123. ULONG Count() const { return _count; }
  124. private:
  125. BOOL _IsFull() const
  126. {
  127. return (_count + _count/MEASURE_OF_SLACK) >= _size;
  128. }
  129. BOOL _LookUp( WORKID wid, ULONG & riTable );
  130. ULONG _GrowSize(ULONG count) const;
  131. void _GrowAndReHash();
  132. BOOL _IsSizeValid( ULONG size, ULONG count ) const;
  133. T * _AllocAndInit(ULONG size ) const;
  134. static void _InitTable( T * pTable, ULONG size );
  135. ULONG _size;
  136. ULONG _count;
  137. T * _pTable;
  138. //
  139. // Number of deleted entries. These can be re-used for adding new
  140. // entries.
  141. //
  142. ULONG _cDeleted;
  143. BOOL _fReHash;
  144. #if DBG==1
  145. //
  146. // For keeping track of usage statistics.
  147. //
  148. unsigned _cMaxChainLen;
  149. unsigned _cTotalSearches;
  150. LONGLONG _cTotalLength;
  151. #endif // DBG==1
  152. };
  153. //+---------------------------------------------------------------------------
  154. //
  155. // Function: CWorkIdHashTable :: ~ctor
  156. //
  157. // Synopsis: Constructs the hash table. It allocates a table of size
  158. // cHashInit and initializes it to be "empty".
  159. //
  160. // Arguments: [cHashInit] - Number of buckets to allocate. A minimum of
  161. // TWID_INIT_HASH_SIZE will be allocated.
  162. //
  163. // History: 1-09-95 srikants Created
  164. //
  165. // Notes:
  166. //
  167. //----------------------------------------------------------------------------
  168. template<class T> TWidHashTable<T>::TWidHashTable(
  169. ULONG cHashInit,
  170. BOOL fReHash ) : _fReHash( fReHash )
  171. {
  172. Win4Assert( 0 != cHashInit );
  173. #if DBG==1
  174. _cMaxChainLen = 0;
  175. _cTotalSearches = 0;
  176. _cTotalLength = 0;
  177. #endif // DBG==1
  178. _count = 0;
  179. _size = max( cHashInit, TWID_INIT_HASH_SIZE ) ;
  180. _pTable = _AllocAndInit(_size);
  181. _cDeleted = 0;
  182. Win4Assert( !_IsFull() );
  183. }
  184. template<class T> TWidHashTable<T>::~TWidHashTable()
  185. {
  186. if ( 0 != _size )
  187. {
  188. Win4Assert( 0 != _pTable );
  189. delete [] ((BYTE *)_pTable);
  190. }
  191. }
  192. template<class T> T & TWidHashTable<T>::FindOrAdd( WORKID wid )
  193. {
  194. ULONG iItem;
  195. if ( _LookUp( wid, iItem ) )
  196. return _pTable[ iItem ];
  197. T entry;
  198. entry.SetWorkId( wid );
  199. return _pTable[ AddEntry( entry ) ];
  200. }
  201. //+---------------------------------------------------------------------------
  202. //
  203. // Function: _LookUp
  204. //
  205. // Synopsis: Looks up for the specified wid in the hash table.
  206. //
  207. // Arguments: [wid] - WorkId to look up.
  208. // [riTable] - (output) Will contain the index in the hash
  209. // table of the entry if found.
  210. //
  211. // Returns: TRUE if found; FALSE o/w
  212. //
  213. // History: 1-09-95 srikants Created
  214. //
  215. // Notes:
  216. //
  217. //----------------------------------------------------------------------------
  218. template<class T> BOOL TWidHashTable<T>::_LookUp( WORKID wid, ULONG &riTable )
  219. {
  220. Win4Assert( 0 != _size );
  221. Win4Assert( widInvalid != wid );
  222. Win4Assert( !_IsFull() );
  223. ULONG cur = wid % _size;
  224. ULONG start = cur;
  225. ULONG delta = cur;
  226. #if DBG==1
  227. ULONG cSearchLen = 1;
  228. #endif // DBG==1
  229. BOOL fFound = FALSE;
  230. while ( !fFound && (widInvalid != _pTable[cur].WorkId()) )
  231. {
  232. if ( wid == _pTable[cur].WorkId() )
  233. {
  234. riTable = cur;
  235. fFound = TRUE;
  236. }
  237. else
  238. {
  239. cur = (cur + delta) % _size;
  240. if ( cur == start ) // wrapped around
  241. {
  242. if ( 1 != delta )
  243. {
  244. delta = 1;
  245. cur = (cur + 1) % _size;
  246. }
  247. else
  248. {
  249. break;
  250. }
  251. }
  252. #if DBG==1
  253. cSearchLen++;
  254. #endif // DBG==1
  255. }
  256. }
  257. #if DBG==1
  258. _cTotalSearches++;
  259. _cTotalLength += cSearchLen;
  260. if (cSearchLen > _cMaxChainLen)
  261. _cMaxChainLen = cSearchLen;
  262. #endif // DBG==1
  263. return fFound;
  264. }
  265. //+---------------------------------------------------------------------------
  266. //
  267. // Function: LookUpWorkId
  268. //
  269. // Synopsis: Looks up the specified work id in the hash table.
  270. //
  271. // Arguments: [wid] - WorkId to look up
  272. // [value] - (output) Value associated with the wid (if lookup
  273. // is successful).
  274. //
  275. // Returns: TRUE if found; FALSE o/w
  276. //
  277. // History: 1-09-95 srikants Created
  278. //
  279. // Notes:
  280. //
  281. //----------------------------------------------------------------------------
  282. template<class T> BOOL TWidHashTable<T>::LookUpWorkId( T & entry )
  283. {
  284. const WORKID wid = entry.WorkId();
  285. Win4Assert( widInvalid != wid );
  286. ULONG iTable;
  287. BOOL fFound = _LookUp( wid, iTable );
  288. if ( fFound )
  289. {
  290. Win4Assert( iTable < _size );
  291. entry = _pTable[iTable];
  292. }
  293. return fFound;
  294. }
  295. //+---------------------------------------------------------------------------
  296. //
  297. // Function: DeleteWorkId
  298. //
  299. // Synopsis: Deletes the entry associated with the specified workid from
  300. // the hash table.
  301. //
  302. // Arguments: [wid] - WorkId to delete.
  303. //
  304. // Returns: TRUE if the given workid was present in the hash table.
  305. // FALSE o/w
  306. //
  307. // History: 1-09-95 srikants Created
  308. //
  309. // Notes: The entry deleted will be identified as "deleted" and will
  310. // be later available for "reuse".
  311. //
  312. //----------------------------------------------------------------------------
  313. template<class T> BOOL TWidHashTable<T>::DeleteWorkId( WORKID wid )
  314. {
  315. Win4Assert( widInvalid != wid );
  316. ULONG iTable;
  317. BOOL fFound = _LookUp( wid, iTable );
  318. if ( fFound )
  319. {
  320. Win4Assert( iTable < _size );
  321. Win4Assert( wid == _pTable[iTable].WorkId() );
  322. Win4Assert( _count > 0 );
  323. _pTable[iTable].SetWorkId( widDeleted );
  324. _count--;
  325. _cDeleted++;
  326. }
  327. return fFound;
  328. }
  329. //+---------------------------------------------------------------------------
  330. //
  331. // Function: DeleteItem
  332. //
  333. // Synopsis: Deletes an entry in the hash table.
  334. //
  335. // Arguments: [item] - reference to the item to be deleted.
  336. //
  337. // History: 28-Feb-96 dlee Created
  338. //
  339. // Notes: The entry deleted will be identified as "deleted" and will
  340. // be later available for "reuse" if there are other items
  341. // in the hash table. Otherwise it is marked as widInvalid.
  342. //
  343. //----------------------------------------------------------------------------
  344. template<class T> void TWidHashTable<T>::DeleteItem( T & item )
  345. {
  346. Win4Assert( widInvalid != item.WorkId() );
  347. Win4Assert( widDeleted != item.WorkId() );
  348. Win4Assert( _count > 0 );
  349. _count--;
  350. // If there aren't any items in the hash table, don't worry about
  351. // breaking up a hash chain, especially since finding a wid in an
  352. // array of widDeleted items is a linear search...
  353. if ( 0 == _count )
  354. item.SetWorkId( widInvalid );
  355. else
  356. {
  357. item.SetWorkId( widDeleted );
  358. _cDeleted++;
  359. }
  360. }
  361. //+---------------------------------------------------------------------------
  362. //
  363. // Function: AddWorkId
  364. //
  365. // Synopsis: Adds the specified workid to the hash table. If one is
  366. // already present, its value will be modified.
  367. //
  368. // Arguments: [wid] - WorkId to be added.
  369. // [value] - Value associated with the workid.
  370. //
  371. // History: 1-09-95 srikants Created
  372. //
  373. // Notes: If the table gets "full" as a result of adding the
  374. // workid, the table will be re-sized and entries re-hashed.
  375. //
  376. //----------------------------------------------------------------------------
  377. template<class T> ULONG TWidHashTable<T>::AddEntry( const T & entry )
  378. {
  379. const WORKID wid = entry.WorkId();
  380. Win4Assert( 0 != _size );
  381. Win4Assert( widInvalid != wid );
  382. Win4Assert( widDeleted != wid );
  383. Win4Assert( !_IsFull() );
  384. ULONG cur = wid % _size;
  385. ULONG start = cur;
  386. //
  387. // Initialize use a delta which is not 1 to prevent data from getting
  388. // clumped in one place.
  389. //
  390. ULONG delta = cur;
  391. #if DBG==1
  392. ULONG cSearchLen = 1;
  393. #endif // DBG==1
  394. while ( !_pTable[cur].IsFree() )
  395. {
  396. if ( wid == _pTable[cur].WorkId() )
  397. {
  398. // just replacing the value of the workid.
  399. _pTable[cur] = entry;
  400. return cur;
  401. }
  402. cur = (cur + delta) % _size;
  403. if ( cur == start ) // wrapped around
  404. {
  405. if ( 1 == delta )
  406. {
  407. if ( _fReHash )
  408. {
  409. Win4Assert( delta != 1 );
  410. }
  411. else
  412. {
  413. // If no rehashing is turned on, give out any old record
  414. // and pump out a warning. Clients expect this behavior!
  415. ciDebugOut(( DEB_IWARN,
  416. "TWidHashTable::AddEntry handing out duplicate" ));
  417. return cur;
  418. }
  419. }
  420. delta = 1;
  421. cur = (cur + 1) % _size;
  422. }
  423. #if DBG==1
  424. cSearchLen++;
  425. #endif // DBG==1
  426. }
  427. #if DBG==1
  428. _cTotalSearches++;
  429. _cTotalLength += cSearchLen;
  430. if (cSearchLen > _cMaxChainLen)
  431. _cMaxChainLen = cSearchLen;
  432. #endif DBG==1
  433. #if DBG==1
  434. ULONG iTable;
  435. Win4Assert( !_LookUp( wid, iTable ) );
  436. #endif // DBG
  437. if ( widDeleted == _pTable[cur].WorkId() )
  438. {
  439. Win4Assert( _cDeleted > 0 );
  440. _cDeleted--;
  441. }
  442. _pTable[cur] = entry;
  443. _count++;
  444. if ( _fReHash && _IsFull() )
  445. {
  446. _GrowAndReHash();
  447. }
  448. return cur;
  449. }
  450. //+---------------------------------------------------------------------------
  451. //
  452. // Function: ReplaceOrAddWorkId
  453. //
  454. // Synopsis: Replaces the value associated with the given workid if one
  455. // exists; creates a new one if the given wid is not in the
  456. // hash table.
  457. //
  458. // Arguments: [wid] - Workid to be replaced/added
  459. // [value] - Value associated with the workid.
  460. //
  461. // History: 1-09-95 srikants Created
  462. //
  463. // Notes:
  464. //
  465. //----------------------------------------------------------------------------
  466. template<class T> void TWidHashTable<T>::ReplaceOrAddEntry( const T & entry )
  467. {
  468. ULONG iTable = _size;
  469. BOOL fFound = FALSE;
  470. const WORKID wid = entry.WorkId();
  471. if ( _cDeleted )
  472. {
  473. //
  474. // If there are "deleted" entries present, we must first do a
  475. // look up to see if an entry is already present. AddWorkId
  476. // will use the first free entry available and may not be able
  477. // to detect this workid.
  478. //
  479. fFound = _LookUp( wid, iTable );
  480. }
  481. if ( fFound )
  482. {
  483. Win4Assert( iTable < _size );
  484. Win4Assert( _pTable[iTable].WorkId() == wid );
  485. _pTable[iTable] = entry;
  486. }
  487. else
  488. {
  489. AddEntry( entry );
  490. }
  491. }
  492. //+---------------------------------------------------------------------------
  493. //
  494. // Function: DeleteAllEntries
  495. //
  496. // Synopsis: Deletes all the entries in the hash table (efficiently).
  497. //
  498. // History: 11-22-94 srikants Created
  499. //
  500. // Notes:
  501. //
  502. //----------------------------------------------------------------------------
  503. template<class T> void TWidHashTable<T>::DeleteAllEntries()
  504. {
  505. #if DBG==1
  506. _cMaxChainLen = 0;
  507. _cTotalSearches = 0;
  508. _cTotalLength = 0;
  509. #endif // DBG==1
  510. _InitTable( _pTable, _size );
  511. _count = 0;
  512. _cDeleted = 0;
  513. }
  514. //+---------------------------------------------------------------------------
  515. //
  516. // Function: _IsSizeValid
  517. //
  518. // Synopsis: Determines if the specified size and count are in agreement
  519. // or not.
  520. //
  521. // Arguments: [size] - The total size of the hash table.
  522. // [count] - Number of entries used in the hash table.
  523. //
  524. // Returns: TRUE if okay; FALSE o/w
  525. //
  526. // History: 1-09-95 srikants Created
  527. //
  528. // Notes:
  529. //
  530. //----------------------------------------------------------------------------
  531. template<class T> BOOL TWidHashTable<T>::_IsSizeValid ( ULONG size, ULONG count ) const
  532. {
  533. if (size == 0 && count == 0)
  534. return(TRUE);
  535. // is it a power of 2 plus 1?
  536. for (ULONG i = 1; i < size; i *= 2 )
  537. continue;
  538. if (i/2 + 1 != size)
  539. return(FALSE);
  540. // is there enough slack?
  541. if (count + count/MEASURE_OF_SLACK > size)
  542. return(FALSE);
  543. return(TRUE);
  544. }
  545. //+---------------------------------------------------------------------------
  546. //
  547. // Function: _GrowSize
  548. //
  549. // Synopsis: For a given valid hash table entries, this routine figures
  550. // out the next valid size (close approximation to a prime).
  551. //
  552. // Arguments: [count] - Number of valid entries in the hash table.
  553. //
  554. // Returns: The size of the hash table for the given number of valid
  555. // entries.
  556. //
  557. // History: 1-09-95 srikants Created
  558. //
  559. // Notes:
  560. //
  561. //----------------------------------------------------------------------------
  562. template<class T> ULONG TWidHashTable<T>::_GrowSize ( ULONG count ) const
  563. {
  564. ULONG size = (count + count/MEASURE_OF_SLACK + 1);
  565. // make it power of two + 1
  566. // a good approximation of a prime
  567. for ( unsigned sizeInit = 1; sizeInit < size; sizeInit *= 2 )
  568. continue;
  569. return(sizeInit + 1);
  570. }
  571. //+---------------------------------------------------------------------------
  572. //
  573. // Function: _AllocAndInit
  574. //
  575. // Synopsis: Helper function to allocate a buffer for the specified number
  576. // of entries and initialize the buffer with "unused" entries.
  577. //
  578. // Arguments: [size] - Number of entries for the hash table.
  579. //
  580. // Returns: Pointer to the buffer.
  581. //
  582. // History: 1-09-95 srikants Created
  583. //
  584. // Notes:
  585. //
  586. //----------------------------------------------------------------------------
  587. template<class T> T * TWidHashTable<T>::_AllocAndInit( ULONG size ) const
  588. {
  589. ULONG cb = sizeof(T)*size;
  590. //
  591. // Note that we are not using the new CWorkIdHashEntry [size] because
  592. // that will call the constructor on all the entries and we have an
  593. // easier way of initializing the array
  594. //
  595. T * pTable = (T *) new BYTE [cb];
  596. _InitTable( pTable, size );
  597. return pTable;
  598. }
  599. //+---------------------------------------------------------------------------
  600. //
  601. // Function: _InitTable
  602. //
  603. // Synopsis: Helper function to initialize the hash table array.
  604. //
  605. // Arguments: [pTable] - Pointer to the table.
  606. // [size] - Size (in entries) of the table.
  607. //
  608. // History: 1-09-95 srikants Created
  609. //
  610. // Notes:
  611. //
  612. //----------------------------------------------------------------------------
  613. template<class T> void TWidHashTable<T>::_InitTable(
  614. T * pTable,
  615. ULONG size )
  616. {
  617. ULONG cb = sizeof(T)*size;
  618. RtlFillMemory( pTable, cb, 0xFF );
  619. }
  620. //+---------------------------------------------------------------------------
  621. //
  622. // Function: _GrowAndReHash
  623. //
  624. // Synopsis: Grows the current hash table and re-hashes all the entries
  625. // in the new table.
  626. //
  627. // History: 1-09-95 srikants Created
  628. //
  629. // Notes:
  630. //
  631. //----------------------------------------------------------------------------
  632. template<class T> void TWidHashTable<T>::_GrowAndReHash()
  633. {
  634. ULONG sizeNew = _GrowSize( _count );
  635. Win4Assert( sizeNew != _size );
  636. T * pNewTable = _AllocAndInit( sizeNew );
  637. ULONG sizeOld = _size;
  638. ULONG countOld = _count;
  639. T * pOldTable = _pTable;
  640. //
  641. // Add the entries from the current table to the new table.
  642. //
  643. _size = sizeNew;
  644. _count = 0;
  645. _pTable = pNewTable;
  646. _cDeleted = 0;
  647. for ( ULONG i = 0; i < sizeOld; i++ )
  648. {
  649. if ( !pOldTable[i].IsFree() )
  650. {
  651. Win4Assert( pOldTable[i].WorkId() != widInvalid );
  652. AddEntry( pOldTable[i] );
  653. }
  654. }
  655. Win4Assert( _count == countOld );
  656. delete [] ((BYTE *) pOldTable);
  657. }
  658. //+---------------------------------------------------------------------------
  659. //
  660. // Class: TWidHashIter
  661. //
  662. // Purpose: Iterator over the TWidHashTable
  663. //
  664. // History: 3-10-95 srikants Created
  665. //
  666. // Notes:
  667. //
  668. //----------------------------------------------------------------------------
  669. template <class T> class TWidHashIter
  670. {
  671. public:
  672. TWidHashIter( TWidHashTable<T> & hashTable )
  673. : _hashTable(hashTable)
  674. {
  675. for ( _curr = 0 ; !AtEnd() && _hashTable.GetEntry(_curr).IsFree();
  676. _curr++ )
  677. {
  678. }
  679. }
  680. const T & Get()
  681. {
  682. return _hashTable.GetEntry(_curr);
  683. }
  684. BOOL AtEnd() const
  685. {
  686. Win4Assert( _curr <= _hashTable.Size() );
  687. return _curr == _hashTable.Size();
  688. }
  689. void Reset()
  690. {
  691. _curr = 0;
  692. }
  693. void Next()
  694. {
  695. Win4Assert( !AtEnd() );
  696. for ( _curr++ ; !AtEnd() && _hashTable.GetEntry(_curr).IsFree();
  697. _curr++ )
  698. {
  699. }
  700. }
  701. private:
  702. TWidHashTable<T> & _hashTable;
  703. ULONG _curr;
  704. };