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.

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