Leaked source code of windows server 2003
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.

763 lines
20 KiB

  1. /*==========================================================================
  2. *
  3. * Copyright (C) 1999 Microsoft Corporation. All Rights Reserved.
  4. *
  5. * File: ClassHash.h
  6. * Content: Hash table that takes a class as a key. The key class MUST support
  7. * two member functions:
  8. * 'HashFunction' will perform a hash down to a specified number of bits.
  9. * 'CompareFunction' will perform a comparrisson of two items of that class.
  10. *
  11. * Note: This class requires an FPM to operate.
  12. *
  13. * THIS CLASS IS NOT THREAD SAFE!!
  14. *
  15. * History:
  16. * Date By Reason
  17. * ==== == ======
  18. * 11/15/98 jwo Created it (map).
  19. * 04/19/99 jtk Rewrote without using STL (map)
  20. * 08/03/99 jtk Derived from ClassMap.h
  21. ***************************************************************************/
  22. #ifndef __CLASS_HASH_H__
  23. #define __CLASS_HASH_H__
  24. #include "dndbg.h"
  25. #include "classbilink.h"
  26. #undef DPF_SUBCOMP
  27. #define DPF_SUBCOMP DN_SUBCOMP_VOICE
  28. //**********************************************************************
  29. // Constant definitions
  30. //**********************************************************************
  31. //**********************************************************************
  32. // Macro definitions
  33. //**********************************************************************
  34. //**********************************************************************
  35. // Structure definitions
  36. //**********************************************************************
  37. //**********************************************************************
  38. // Variable prototypes
  39. //**********************************************************************
  40. //**********************************************************************
  41. // Function prototypes
  42. //**********************************************************************
  43. //**********************************************************************
  44. // Class definitions
  45. //**********************************************************************
  46. //
  47. // Template class for entry in map.
  48. //
  49. template<class T, class S>
  50. class CClassHashEntry
  51. {
  52. public:
  53. CClassHashEntry(){};
  54. ~CClassHashEntry(){};
  55. //
  56. // internals, put the linkage at the end to make sure the FPM doesn't
  57. // wail on it!
  58. //
  59. PVOID m_FPMPlaceHolder;
  60. S m_Key;
  61. T *m_pItem;
  62. CBilink m_Linkage;
  63. //
  64. // linkage functions
  65. //
  66. #undef DPF_MODNAME
  67. #define DPF_MODNAME "CClassHashEntry::EntryFromBilink"
  68. static CClassHashEntry *EntryFromBilink( CBilink *const pLinkage )
  69. {
  70. DBG_CASSERT( sizeof( void* ) == sizeof( INT_PTR ) );
  71. return reinterpret_cast<CClassHashEntry*>( &reinterpret_cast<BYTE*>( pLinkage )[ -OFFSETOF( CClassHashEntry, m_Linkage ) ] );
  72. }
  73. void AddToList( CBilink &Linkage )
  74. {
  75. m_Linkage.InsertAfter( &Linkage );
  76. }
  77. void RemoveFromList( void )
  78. {
  79. m_Linkage.RemoveFromList();
  80. }
  81. //
  82. // pool management functions
  83. //
  84. #undef DPF_MODNAME
  85. #define DPF_MODNAME "CClassHashEntry::InitAlloc"
  86. static BOOL InitAlloc( void *pItem, void* pvContext )
  87. {
  88. CClassHashEntry<T,S> *pThisObject;
  89. DNASSERT( pItem != NULL );
  90. pThisObject = static_cast<CClassHashEntry<T,S>*>( pItem );
  91. pThisObject->m_pItem = NULL;
  92. pThisObject->m_Linkage.Initialize();
  93. return TRUE;
  94. }
  95. #undef DPF_MODNAME
  96. #define DPF_MODNAME "CClassHashEntry::Init"
  97. static void Init( void *pItem, void* pvContext )
  98. {
  99. CClassHashEntry<T,S> *pThisObject;
  100. DNASSERT( pItem != NULL );
  101. pThisObject = static_cast<CClassHashEntry<T,S>*>( pItem );
  102. DNASSERT( pThisObject->m_pItem == NULL );
  103. DNASSERT( pThisObject->m_Linkage.IsEmpty() != FALSE );
  104. }
  105. #undef DPF_MODNAME
  106. #define DPF_MODNAME "CClassHashEntry::Release"
  107. static void Release( void *pItem )
  108. {
  109. CClassHashEntry<T,S> *pThisObject;
  110. DNASSERT( pItem != NULL );
  111. pThisObject = static_cast<CClassHashEntry<T,S>*>( pItem );
  112. pThisObject->m_pItem = NULL;
  113. DNASSERT( pThisObject->m_Linkage.IsEmpty() != FALSE );
  114. }
  115. #undef DPF_MODNAME
  116. #define DPF_MODNAME "CClassHashEntry::Dealloc"
  117. static void Dealloc( void *pItem )
  118. {
  119. CClassHashEntry<T,S> *pThisObject;
  120. DNASSERT( pItem != NULL );
  121. pThisObject = static_cast<CClassHashEntry<T,S>*>( pItem );
  122. DNASSERT( pThisObject->m_pItem == NULL );
  123. DNASSERT( pThisObject->m_Linkage.IsEmpty() != FALSE );
  124. }
  125. protected:
  126. private:
  127. //
  128. // make copy constructor and assignment operator private and unimplemented
  129. // to prevent illegal copies from being made
  130. //
  131. CClassHashEntry( const CClassHashEntry & );
  132. CClassHashEntry& operator=( const CClassHashEntry & );
  133. };
  134. //
  135. // template class for the map
  136. //
  137. template<class T, class S>
  138. class CClassHash
  139. {
  140. public:
  141. CClassHash();
  142. ~CClassHash();
  143. BOOL Initialize( const INT_PTR iBitDepth, const INT_PTR iGrowBits );
  144. void Deinitialize( void );
  145. BOOL Insert( const S Key, T *const pItem );
  146. void Remove( const S Key );
  147. BOOL RemoveLastEntry( T **const ppItem );
  148. BOOL Find( const S Key, T **const ppItem );
  149. BOOL IsEmpty( void ) { return ( m_iEntriesInUse == 0 ); }
  150. INT_PTR m_iHashBitDepth; // number of bits used for hash entry
  151. INT_PTR m_iGrowBits; // number of bits to grow has by
  152. CBilink *m_pHashEntries; // list of hash entries
  153. INT_PTR m_iAllocatedEntries; // count of allocated entries in index/item list
  154. INT_PTR m_iEntriesInUse; // count of entries in use
  155. CFixedPool m_EntryPool; // pool of entries
  156. private:
  157. BOOL m_fInitialized;
  158. BOOL LocalFind( const S Key, CBilink **const ppLink );
  159. void Grow( void );
  160. void InitializeHashEntries( const UINT_PTR uEntryCount ) const;
  161. //
  162. // make copy constructor and assignment operator private and unimplemented
  163. // to prevent illegal copies from being made
  164. //
  165. CClassHash( const CClassHash & );
  166. CClassHash& operator=( const CClassHash & );
  167. };
  168. //**********************************************************************
  169. // Class function definitions
  170. //**********************************************************************
  171. //**********************************************************************
  172. // ------------------------------
  173. // CClassHash::CClassHash - constructor
  174. //
  175. // Entry: Nothing
  176. //
  177. // Exit: Nothing
  178. // ------------------------------
  179. #undef DPF_MODNAME
  180. #define DPF_MODNAME "CClassHash::CClassHash"
  181. template<class T, class S>
  182. CClassHash< T, S >::CClassHash(): m_iHashBitDepth( 0 ),m_iGrowBits( 0 ),
  183. m_pHashEntries( NULL ),m_iAllocatedEntries( 0 ),m_iEntriesInUse( 0 )
  184. {
  185. //
  186. // clear internals
  187. //
  188. m_fInitialized = FALSE;
  189. memset( &m_EntryPool, 0x00, sizeof( m_EntryPool ) );
  190. }
  191. //**********************************************************************
  192. //**********************************************************************
  193. // ------------------------------
  194. // CClassHash::~CClassHash - destructor
  195. //
  196. // Entry: Nothing
  197. //
  198. // Exit: Nothing
  199. // ------------------------------
  200. #undef DPF_MODNAME
  201. #define DPF_MODNAME "CClassHash::~CClassHash"
  202. template<class T, class S>
  203. CClassHash< T, S >::~CClassHash()
  204. {
  205. DNASSERT( m_iHashBitDepth == 0 );
  206. DNASSERT( m_iGrowBits == 0 );
  207. DNASSERT( m_pHashEntries == NULL );
  208. DNASSERT( m_iAllocatedEntries == 0 );
  209. DNASSERT( m_iEntriesInUse == 0 );
  210. DNASSERT( m_fInitialized == FALSE );
  211. }
  212. //**********************************************************************
  213. //**********************************************************************
  214. // ------------------------------
  215. // CClassHash::Initialize - initialize hash table
  216. //
  217. // Entry: Pointer to key
  218. // Pointer to 'key' associated with this item
  219. // Pointer to item to add
  220. //
  221. // Exit: Boolean indicating success
  222. // TRUE = success
  223. // FALSE = failure
  224. // ------------------------------
  225. #undef DPF_MODNAME
  226. #define DPF_MODNAME "CClassHash::Initialize"
  227. template<class T, class S>
  228. BOOL CClassHash< T, S >::Initialize( const INT_PTR iBitDepth, const INT_PTR iGrowBits )
  229. {
  230. BOOL fReturn;
  231. DNASSERT( iBitDepth != 0 );
  232. //
  233. // initialize
  234. //
  235. fReturn = TRUE;
  236. DNASSERT( m_pHashEntries == NULL );
  237. m_pHashEntries = static_cast<CBilink*>( DNMalloc( sizeof( *m_pHashEntries ) * ( 1 << iBitDepth ) ) );
  238. if ( m_pHashEntries == NULL )
  239. {
  240. fReturn = FALSE;
  241. DPFX(DPFPREP, 0, "Unable to allocate memory for hash table!" );
  242. goto Exit;
  243. }
  244. m_iAllocatedEntries = 1 << iBitDepth;
  245. InitializeHashEntries( m_iAllocatedEntries );
  246. if (!m_EntryPool.Initialize(sizeof( CClassHashEntry<T,S> ), // size of pool entry
  247. CClassHashEntry<T,S>::InitAlloc, // function for allocating item
  248. CClassHashEntry<T,S>::Init, // function for getting item from pool
  249. CClassHashEntry<T,S>::Release, // function for releasing item
  250. CClassHashEntry<T,S>::Dealloc // function for deallocating item
  251. ))
  252. {
  253. fReturn = FALSE;
  254. DPFX(DPFPREP, 0, "Unable to allocate memory for entry pool!" );
  255. DNFree(m_pHashEntries);
  256. m_pHashEntries = NULL;
  257. goto Exit;
  258. }
  259. m_iHashBitDepth = iBitDepth;
  260. m_iGrowBits = iGrowBits;
  261. m_fInitialized = TRUE;
  262. Exit:
  263. return fReturn;
  264. }
  265. //**********************************************************************
  266. //**********************************************************************
  267. // ------------------------------
  268. // CClassHash::Deinitialize - deinitialize hash table
  269. //
  270. // Entry: Nothing
  271. //
  272. // Exit: Nothing
  273. // ------------------------------
  274. #undef DPF_MODNAME
  275. #define DPF_MODNAME "CClassHash::Deinitialize"
  276. template<class T, class S>
  277. void CClassHash< T, S >::Deinitialize( void )
  278. {
  279. if( !m_fInitialized )
  280. return;
  281. DNASSERT( m_iEntriesInUse == 0 );
  282. DNASSERT( m_pHashEntries != NULL );
  283. DNFree( m_pHashEntries );
  284. m_pHashEntries = NULL;
  285. m_EntryPool.DeInitialize();
  286. m_fInitialized = FALSE;
  287. m_iHashBitDepth = 0;
  288. m_iGrowBits = 0;
  289. m_iAllocatedEntries = 0;
  290. }
  291. //**********************************************************************
  292. //**********************************************************************
  293. // ------------------------------
  294. // CClassHash::Insert - add item to map
  295. //
  296. // Entry: Pointer to 'key' associated with this item
  297. // Pointer to item to add
  298. //
  299. // Exit: Boolean indicating success:
  300. // TRUE = success
  301. // FALSE = failure
  302. // ------------------------------
  303. #undef DPF_MODNAME
  304. #define DPF_MODNAME "CClassHash::Insert"
  305. template<class T, class S>
  306. BOOL CClassHash< T, S >::Insert( const S Key, T *const pItem )
  307. {
  308. BOOL fReturn;
  309. BOOL fFound;
  310. CBilink *pLink;
  311. CClassHashEntry< T, S > *pNewEntry;
  312. DNASSERT( pItem != NULL );
  313. DNASSERT( m_fInitialized != FALSE );
  314. //
  315. // initialize
  316. //
  317. fReturn = TRUE;
  318. pNewEntry = NULL;
  319. //
  320. // grow the map if applicable
  321. //
  322. if ( m_iEntriesInUse >= ( m_iAllocatedEntries / 2 ) )
  323. {
  324. Grow();
  325. }
  326. //
  327. // get a new table entry before trying the lookup
  328. //
  329. pNewEntry = static_cast<CClassHashEntry<T,S>*>( m_EntryPool.Get() );
  330. if ( pNewEntry == NULL )
  331. {
  332. fReturn = FALSE;
  333. DPFX(DPFPREP, 0, "Problem allocating new hash table entry on Insert!" );
  334. goto Exit;
  335. }
  336. //
  337. // scan for this item in the list, since we're only supposed to have
  338. // unique items in the list, ASSERT if a duplicate is found
  339. //
  340. fFound = LocalFind( Key, &pLink );
  341. DNASSERT( pLink != NULL );
  342. DNASSERT( fFound == FALSE );
  343. //
  344. // officially add entry to the hash table
  345. //
  346. m_iEntriesInUse++;
  347. pNewEntry->m_Key = Key;
  348. pNewEntry->m_pItem = pItem;
  349. DNASSERT( pLink != NULL );
  350. pNewEntry->AddToList( *pLink );
  351. DNASSERT( fReturn == TRUE );
  352. Exit:
  353. return fReturn;
  354. }
  355. //**********************************************************************
  356. //**********************************************************************
  357. // ------------------------------
  358. // CClassHash::Remove - remove item from map
  359. //
  360. // Entry: Reference to 'key' used to look up this item
  361. //
  362. // Exit: Nothing
  363. // ------------------------------
  364. #undef DPF_MODNAME
  365. #define DPF_MODNAME "CClassHash::Remove"
  366. template<class T, class S>
  367. void CClassHash< T, S >::Remove( const S Key )
  368. {
  369. CBilink *pLink;
  370. DNASSERT( m_fInitialized != FALSE );
  371. if ( LocalFind( Key, &pLink ) != FALSE )
  372. {
  373. CClassHashEntry< T, S > *pEntry;
  374. DNASSERT( pLink != NULL );
  375. pEntry = CClassHashEntry< T, S >::EntryFromBilink( pLink );
  376. pEntry->RemoveFromList();
  377. m_EntryPool.Release( pEntry );
  378. DNASSERT( m_iEntriesInUse != 0 );
  379. m_iEntriesInUse--;
  380. }
  381. }
  382. //**********************************************************************
  383. //**********************************************************************
  384. // ------------------------------
  385. // CClassHash::RemoveLastEntry - remove last item from map
  386. //
  387. // Entry: Pointer to pointer to 'key'
  388. // Pointer to pointer to item data
  389. //
  390. // Exit: Boolean indicating success
  391. // TRUE = item was removed
  392. // FALSE = item was not removed (map empty)
  393. // ------------------------------
  394. #undef DPF_MODNAME
  395. #define DPF_MODNAME "CClassHash::RemoveLastEntry"
  396. template<class T, class S>
  397. BOOL CClassHash< T, S >::RemoveLastEntry( T **const ppItem )
  398. {
  399. BOOL fReturn;
  400. DNASSERT( ppItem != NULL );
  401. //
  402. // initialize
  403. //
  404. DNASSERT( m_fInitialized != FALSE );
  405. fReturn = FALSE;
  406. if ( m_iEntriesInUse != 0 )
  407. {
  408. INT_PTR iIndex;
  409. DNASSERT( m_pHashEntries != NULL );
  410. iIndex = m_iAllocatedEntries;
  411. while ( iIndex > 0 )
  412. {
  413. iIndex--;
  414. if ( m_pHashEntries[ iIndex ].IsEmpty() == FALSE )
  415. {
  416. CClassHashEntry<T,S> *pEntry;
  417. pEntry = CClassHashEntry< T, S >::EntryFromBilink( m_pHashEntries[ iIndex ].GetNext() );
  418. pEntry->RemoveFromList();
  419. *ppItem = pEntry->m_pItem;
  420. m_EntryPool.Release( pEntry );
  421. m_iEntriesInUse--;
  422. fReturn = TRUE;
  423. goto Exit;
  424. }
  425. }
  426. }
  427. Exit:
  428. return fReturn;
  429. }
  430. //**********************************************************************
  431. //**********************************************************************
  432. // ------------------------------
  433. // CClassHash::Find - find item in map
  434. //
  435. // Entry: Reference of 'key' used to look up this item
  436. // Pointer to pointer to be filled in with data
  437. //
  438. // Exit: Boolean indicating success
  439. // TRUE = item found
  440. // FALSE = item not found
  441. // ------------------------------
  442. #undef DPF_MODNAME
  443. #define DPF_MODNAME "CClassHash::Find"
  444. template<class T, class S>
  445. BOOL CClassHash< T, S >::Find( const S Key, T **const ppItem )
  446. {
  447. BOOL fReturn;
  448. CBilink *pLinkage;
  449. DNASSERT( m_fInitialized != FALSE );
  450. //
  451. // initialize
  452. //
  453. fReturn = FALSE;
  454. pLinkage = NULL;
  455. if ( LocalFind( Key, &pLinkage ) != FALSE )
  456. {
  457. CClassHashEntry<T,S> *pEntry;
  458. pEntry = CClassHashEntry< T, S >::EntryFromBilink( pLinkage );
  459. *ppItem = pEntry->m_pItem;
  460. fReturn = TRUE;
  461. }
  462. return fReturn;
  463. }
  464. //**********************************************************************
  465. //**********************************************************************
  466. // ------------------------------
  467. // CClassHash::LocalFind - find an entry in a hash table, or find out where to insert.
  468. //
  469. // Entry: Refernce of 'key' to look for
  470. // Pointer to pointer to linkage of find or insert
  471. //
  472. // Exit: Boolean indicating whether the item was found
  473. // TRUE = found
  474. // FALSE = not found
  475. // ------------------------------
  476. #undef DPF_MODNAME
  477. #define DPF_MODNAME "CClassHash::LocalFind"
  478. template<class T, class S>
  479. BOOL CClassHash< T, S >::LocalFind( const S Key, CBilink **const ppLinkage )
  480. {
  481. BOOL fFound;
  482. DWORD_PTR HashResult;
  483. CBilink *pTemp;
  484. DNASSERT( m_fInitialized != FALSE );
  485. HashResult = ClassHash_Hash( Key, m_iHashBitDepth );
  486. DNASSERT( HashResult < ( 1 << m_iHashBitDepth ) );
  487. // DNASSERT( HashResult >= 0 ); -- REMOVED by MiNara
  488. fFound = FALSE;
  489. pTemp = &m_pHashEntries[ HashResult ];
  490. while ( pTemp->GetNext() != &m_pHashEntries[ HashResult ] )
  491. {
  492. const CClassHashEntry< T, S > *pEntry;
  493. pEntry = CClassHashEntry< T, S >::EntryFromBilink( pTemp->GetNext() );
  494. if ( Key == pEntry->m_Key )
  495. {
  496. fFound = TRUE;
  497. *ppLinkage = pTemp->GetNext();
  498. goto Exit;
  499. }
  500. else
  501. {
  502. pTemp = pTemp->GetNext();
  503. }
  504. }
  505. //
  506. // entry was not found, return pointer to linkage to insert after if a new
  507. // entry is being added to the table
  508. //
  509. *ppLinkage = pTemp;
  510. Exit:
  511. return fFound;
  512. }
  513. //**********************************************************************
  514. //**********************************************************************
  515. // ------------------------------
  516. // CClassHash::Grow - grow hash table to next larger size
  517. //
  518. // Entry: Nothing
  519. //
  520. // Exit: Nothing
  521. // ------------------------------
  522. #undef DPF_MODNAME
  523. #define DPF_MODNAME "CClassHash::Grow"
  524. template<class T, class S>
  525. void CClassHash< T, S >::Grow( void )
  526. {
  527. CBilink *pTemp;
  528. INT_PTR iNewEntryBitCount;
  529. DNASSERT( m_fInitialized != FALSE );
  530. //
  531. // We're more than 50% full, find a new has table size that will accomodate
  532. // all of the current entries, and keep a pointer to the old data
  533. // in case the memory allocation fails.
  534. //
  535. pTemp = m_pHashEntries;
  536. iNewEntryBitCount = m_iHashBitDepth;
  537. do
  538. {
  539. iNewEntryBitCount += m_iGrowBits;
  540. } while ( m_iEntriesInUse >= ( ( 1 << iNewEntryBitCount ) / 2 ) );
  541. //
  542. // assert that we don't consume half of the machine's address space!
  543. //
  544. DNASSERT( iNewEntryBitCount <= ( sizeof( UINT_PTR ) * 8 / 2 ) );
  545. m_pHashEntries = static_cast<CBilink*>( DNMalloc( sizeof( *pTemp ) * ( 1 << iNewEntryBitCount ) ) );
  546. if ( m_pHashEntries == NULL )
  547. {
  548. //
  549. // Allocation failed, restore the old data pointer and insert the item
  550. // into the hash table. This will probably result in adding to a bucket.
  551. //
  552. m_pHashEntries = pTemp;
  553. DPFX(DPFPREP, 0, "Warning: Failed to grow hash table when 50% full!" );
  554. }
  555. else
  556. {
  557. INT_PTR iOldHashSize;
  558. INT_PTR iOldEntryCount;
  559. //
  560. // we have more memory, reorient the hash table and re-add all of
  561. // the old items
  562. //
  563. InitializeHashEntries( 1 << iNewEntryBitCount );
  564. iOldEntryCount = m_iEntriesInUse;
  565. iOldHashSize = 1 << m_iHashBitDepth;
  566. m_iHashBitDepth = iNewEntryBitCount;
  567. iOldEntryCount = m_iEntriesInUse;
  568. m_iAllocatedEntries = 1 << iNewEntryBitCount;
  569. m_iEntriesInUse = 0;
  570. DNASSERT( iOldHashSize > 0 );
  571. while ( iOldHashSize > 0 )
  572. {
  573. iOldHashSize--;
  574. while ( pTemp[ iOldHashSize ].GetNext() != &pTemp[ iOldHashSize ] )
  575. {
  576. BOOL fTempReturn;
  577. S Key;
  578. T* pItem;
  579. CClassHashEntry<T,S> *pTempEntry;
  580. pTempEntry = CClassHashEntry< T, S >::EntryFromBilink( pTemp[ iOldHashSize ].GetNext() );
  581. pTempEntry->RemoveFromList();
  582. Key = pTempEntry->m_Key;
  583. pItem = pTempEntry->m_pItem;
  584. m_EntryPool.Release( pTempEntry );
  585. //
  586. // Since we're returning the current hash table entry to the pool
  587. // it will be immediately reused in the new table. We should never
  588. // have a problem adding to the new list.
  589. //
  590. fTempReturn = Insert( Key, pItem );
  591. DNASSERT( fTempReturn != FALSE );
  592. iOldEntryCount--;
  593. }
  594. }
  595. DNASSERT( iOldEntryCount == 0 );
  596. DNFree( pTemp );
  597. pTemp = NULL;
  598. }
  599. }
  600. //**********************************************************************
  601. //**********************************************************************
  602. // ------------------------------
  603. // CClassHash::InitializeHashEntries - initialize all of the entries in the hash table
  604. //
  605. // Entry: Count of entries to initialize.
  606. //
  607. // Exit: Nothing
  608. // ------------------------------
  609. #undef DPF_MODNAME
  610. #define DPF_MODNAME "CClassHash::InitializeHashEntries"
  611. template<class T, class S>
  612. void CClassHash< T, S >::InitializeHashEntries( const UINT_PTR uEntryCount ) const
  613. {
  614. UINT_PTR uLocalEntryCount;
  615. DNASSERT( m_pHashEntries != NULL );
  616. uLocalEntryCount = uEntryCount;
  617. while ( uLocalEntryCount != 0 )
  618. {
  619. uLocalEntryCount--;
  620. m_pHashEntries[ uLocalEntryCount ].Initialize();
  621. }
  622. }
  623. //**********************************************************************
  624. #undef DPF_SUBCOMP
  625. #endif // __CLASS_HASH_H__