Counter Strike : Global Offensive Source Code
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.

997 lines
41 KiB

  1. //========= Copyright � 2011, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: a fast growable hashtable with stored hashes, L2-friendly behavior.
  4. // Useful as a string dictionary or a low-overhead set/map for small POD types.
  5. //
  6. // Usage notes:
  7. // - handles are NOT STABLE across element removal! use RemoveAndAdvance()
  8. // if you are removing elements while iterating through the hashtable.
  9. // Use CUtlStableHashtable if you need stable handles (less efficient).
  10. // - handles are also NOT STABLE across element insertion. The handle
  11. // resulting from the insertion of an element may not retreive the
  12. // same (or any!) element after further insertions. Again, use
  13. // CUtlStableHashtable if you need stable handles
  14. // - Insert() first searches for an existing match and returns it if found
  15. // - a value type of "empty_t" can be used to eliminate value storage and
  16. // switch Element() to return const Key references instead of values
  17. // - an extra user flag bit is accessible via Get/SetUserFlag()
  18. // - hash function pointer / functor is exposed via GetHashRef()
  19. // - comparison function pointer / functor is exposed via GetEqualRef()
  20. // - if your value type cannot be copy-constructed, use key-only Insert()
  21. // to default-initialize the value and then manipulate it afterwards.
  22. // - The reason that UtlHashtable permutes itself and invalidates
  23. // iterators is to make it faster in the case where you are not
  24. // tracking iterators. If you use it as a set or a map ("is this
  25. // value a member?") as opposed to a long-term container, then you
  26. // probably don't need stable iterators. Hashtable tries to place
  27. // newly inserted data in the primary hash slot, making an
  28. // assumption that if you inserted it recently, you're more likely
  29. // to access it than if you inserted something a long time
  30. // ago. It's effectively trying to minimize cache misses for hot
  31. // data if you add and remove a lot.
  32. // If you don't care too much about cache misses, UtlStableHashtable
  33. // is what you're looking for
  34. //
  35. // Implementation notes:
  36. // - overall hash table load is kept between .25 and .75
  37. // - items which would map to the same ideal slot are chained together
  38. // - chained items are stored sequentially in adjacent free spaces
  39. // - "root" entries are prioritized over chained entries; if a
  40. // slot is not occupied by an item in its root position, the table
  41. // is guaranteed to contain no keys which would hash to that slot.
  42. // - new items go at the head of the chain (ie, in their root slot)
  43. // and evict / "bump" any chained entries which occupy that slot
  44. // - chain-following skips over unused holes and continues examining
  45. // table entries until a chain entry with FLAG_LAST is encountered
  46. //
  47. // CUtlHashtable< uint32 > setOfIntegers;
  48. // CUtlHashtable< const char* > setOfStringPointers;
  49. // CUtlHashtable< int, CUtlVector<blah_t> > mapFromIntsToArrays;
  50. //
  51. // $NoKeywords: $
  52. //
  53. // A closed-form (open addressing) hashtable with linear sequential probing.
  54. //=============================================================================//
  55. #ifndef UTLHASHTABLE_H
  56. #define UTLHASHTABLE_H
  57. #pragma once
  58. #include "utlcommon.h"
  59. #include "utlmemory.h"
  60. #include "mathlib/mathlib.h"
  61. #include "utllinkedlist.h"
  62. //-----------------------------------------------------------------------------
  63. // Henry Goffin (henryg) was here. Questions? Bugs? Go slap him around a bit.
  64. //-----------------------------------------------------------------------------
  65. typedef unsigned int UtlHashHandle_t;
  66. #define FOR_EACH_HASHTABLE( table, iter ) \
  67. for ( UtlHashHandle_t iter = (table).FirstHandle(); iter != (table).InvalidHandle(); iter = (table).NextHandle( iter ) )
  68. // CUtlHashtableEntry selects between 16 and 32 bit storage backing
  69. // for flags_and_hash depending on the size of the stored types.
  70. template < typename KeyT, typename ValueT = empty_t >
  71. class CUtlHashtableEntry
  72. {
  73. public:
  74. typedef CUtlKeyValuePair< KeyT, ValueT > KVPair;
  75. enum { INT16_STORAGE = ( sizeof( KVPair ) <= 2 ) };
  76. typedef typename CTypeSelect< INT16_STORAGE, int16, int32 >::type storage_t;
  77. enum
  78. {
  79. FLAG_FREE = INT16_STORAGE ? 0x8000 : 0x80000000, // must be high bit for IsValid and IdealIndex to work
  80. FLAG_LAST = INT16_STORAGE ? 0x4000 : 0x40000000,
  81. MASK_HASH = INT16_STORAGE ? 0x3FFF : 0x3FFFFFFF
  82. };
  83. storage_t flags_and_hash;
  84. storage_t data[ ( sizeof(KVPair) + sizeof(storage_t) - 1 ) / sizeof(storage_t) ];
  85. bool IsValid() const { return flags_and_hash >= 0; }
  86. void MarkInvalid() { int32 flag = FLAG_FREE; flags_and_hash = (storage_t)flag; }
  87. const KVPair *Raw() const { return reinterpret_cast< const KVPair * >( &data[0] ); }
  88. const KVPair *operator->() const { Assert( IsValid() ); return reinterpret_cast< const KVPair * >( &data[0] ); }
  89. KVPair *Raw() { return reinterpret_cast< KVPair * >( &data[0] ); }
  90. KVPair *operator->() { Assert( IsValid() ); return reinterpret_cast< KVPair * >( &data[0] ); }
  91. // Returns the ideal index of the data in this slot, or all bits set if invalid
  92. uint32 FORCEINLINE IdealIndex( uint32 slotmask ) const { return IdealIndex( flags_and_hash, slotmask ) | ( (int32)flags_and_hash >> 31 ); }
  93. // Use template tricks to fully define only one function that takes either 16 or 32 bits
  94. // and performs different logic without using "if ( INT16_STORAGE )", because GCC and MSVC
  95. // sometimes have trouble removing the constant branch, which is dumb... but whatever.
  96. // 16-bit hashes are simply too narrow for large hashtables; more mask bits than hash bits!
  97. // So we duplicate the hash bits. (Note: h *= MASK_HASH+2 is the same as h += h<<HASH_BITS)
  98. typedef typename CTypeSelect< INT16_STORAGE, int16, undefined_t >::type uint32_if16BitStorage;
  99. typedef typename CTypeSelect< INT16_STORAGE, undefined_t, int32 >::type uint32_if32BitStorage;
  100. static FORCEINLINE uint32 IdealIndex( uint32_if16BitStorage h, uint32 m ) { h &= MASK_HASH; h *= MASK_HASH + 2; return h & m; }
  101. static FORCEINLINE uint32 IdealIndex( uint32_if32BitStorage h, uint32 m ) { return h & m; }
  102. // More efficient than memcpy for the small types that are stored in a hashtable
  103. void MoveDataFrom( CUtlHashtableEntry &src )
  104. {
  105. storage_t * RESTRICT srcData = &src.data[0];
  106. for ( int i = 0; i < ARRAYSIZE( data ); ++i ) { data[i] = srcData[i]; }
  107. }
  108. };
  109. template <typename KeyT, typename ValueT = empty_t, typename KeyHashT = DefaultHashFunctor<KeyT>, typename KeyIsEqualT = DefaultEqualFunctor<KeyT>, typename AlternateKeyT = typename ArgumentTypeInfo<KeyT>::Alt_t >
  110. class CUtlHashtable
  111. {
  112. public:
  113. typedef UtlHashHandle_t handle_t;
  114. protected:
  115. typedef CUtlKeyValuePair<KeyT, ValueT> KVPair;
  116. typedef typename ArgumentTypeInfo<KeyT>::Arg_t KeyArg_t;
  117. typedef typename ArgumentTypeInfo<ValueT>::Arg_t ValueArg_t;
  118. typedef typename ArgumentTypeInfo<AlternateKeyT>::Arg_t KeyAlt_t;
  119. typedef CUtlHashtableEntry< KeyT, ValueT > entry_t;
  120. enum { FLAG_FREE = entry_t::FLAG_FREE };
  121. enum { FLAG_LAST = entry_t::FLAG_LAST };
  122. enum { MASK_HASH = entry_t::MASK_HASH };
  123. CUtlMemory< entry_t > m_table;
  124. int m_nUsed;
  125. int m_nMinSize;
  126. bool m_bSizeLocked;
  127. KeyIsEqualT m_eq;
  128. KeyHashT m_hash;
  129. // Allocate an empty table and then re-insert all existing entries.
  130. void DoRealloc( int size );
  131. // Move an existing entry to a free slot, leaving a hole behind
  132. void BumpEntry( unsigned int idx );
  133. // Insert an unconstructed KVPair at the primary slot
  134. int DoInsertUnconstructed( unsigned int h, bool allowGrow );
  135. // Implementation for Insert functions, constructs a KVPair
  136. // with either a default-construted or copy-constructed value
  137. template <typename KeyParamT> handle_t DoInsert( KeyParamT k, unsigned int h, bool* pDidInsert );
  138. template <typename KeyParamT> handle_t DoInsert( KeyParamT k, typename ArgumentTypeInfo<ValueT>::Arg_t v, unsigned int h, bool* pDidInsert );
  139. template <typename KeyParamT> handle_t DoInsertNoCheck( KeyParamT k, typename ArgumentTypeInfo<ValueT>::Arg_t v, unsigned int h );
  140. // Key lookup. Can also return previous-in-chain if result is chained.
  141. template <typename KeyParamT> handle_t DoLookup( KeyParamT x, unsigned int h, handle_t *pPreviousInChain ) const;
  142. // Remove single element by key + hash. Returns the index of the new hole
  143. // that was created. Returns InvalidHandle() if element was not found.
  144. template <typename KeyParamT> int DoRemove( KeyParamT x, unsigned int h );
  145. // Friend CUtlStableHashtable so that it can call our Do* functions directly
  146. template < typename K, typename V, typename S, typename H, typename E, typename A > friend class CUtlStableHashtable;
  147. public:
  148. explicit CUtlHashtable( int minimumSize = 32 )
  149. : m_nUsed(0), m_nMinSize(MAX(8, minimumSize)), m_bSizeLocked(false), m_eq(), m_hash() { }
  150. CUtlHashtable( int minimumSize, const KeyHashT &hash, KeyIsEqualT const &eq = KeyIsEqualT() )
  151. : m_nUsed(0), m_nMinSize(MAX(8, minimumSize)), m_bSizeLocked(false), m_eq(eq), m_hash(hash) { }
  152. CUtlHashtable( entry_t* pMemory, unsigned int nCount, const KeyHashT &hash = KeyHashT(), KeyIsEqualT const &eq = KeyIsEqualT() )
  153. : m_nUsed(0), m_nMinSize(8), m_bSizeLocked(false), m_eq(eq), m_hash(hash) { SetExternalBuffer( pMemory, nCount ); }
  154. ~CUtlHashtable() { RemoveAll(); }
  155. CUtlHashtable &operator=( CUtlHashtable const &src );
  156. // Set external memory
  157. void SetExternalBuffer( byte* pRawBuffer, unsigned int nBytes, bool bAssumeOwnership = false, bool bGrowable = false );
  158. void SetExternalBuffer( entry_t* pBuffer, unsigned int nSize, bool bAssumeOwnership = false, bool bGrowable = false );
  159. // Functor/function-pointer access
  160. KeyHashT& GetHashRef() { return m_hash; }
  161. KeyIsEqualT& GetEqualRef() { return m_eq; }
  162. KeyHashT const &GetHashRef() const { return m_hash; }
  163. KeyIsEqualT const &GetEqualRef() const { return m_eq; }
  164. // Handle validation
  165. bool IsValidHandle( handle_t idx ) const { return (unsigned)idx < (unsigned)m_table.Count() && m_table[idx].IsValid(); }
  166. static handle_t InvalidHandle() { return (handle_t) -1; }
  167. // Iteration functions
  168. handle_t FirstHandle() const { return NextHandle( (handle_t) -1 ); }
  169. handle_t NextHandle( handle_t start ) const;
  170. // Returns the number of unique keys in the table
  171. int Count() const { return m_nUsed; }
  172. // Key lookup, returns InvalidHandle() if not found
  173. handle_t Find( KeyArg_t k ) const { return DoLookup<KeyArg_t>( k, m_hash(k), NULL ); }
  174. handle_t Find( KeyArg_t k, unsigned int hash) const { Assert( hash == m_hash(k) ); return DoLookup<KeyArg_t>( k, hash, NULL ); }
  175. // Alternate-type key lookup, returns InvalidHandle() if not found
  176. handle_t Find( KeyAlt_t k ) const { return DoLookup<KeyAlt_t>( k, m_hash(k), NULL ); }
  177. handle_t Find( KeyAlt_t k, unsigned int hash) const { Assert( hash == m_hash(k) ); return DoLookup<KeyAlt_t>( k, hash, NULL ); }
  178. // True if the key is in the table
  179. bool HasElement( KeyArg_t k ) const { return InvalidHandle() != Find( k ); }
  180. bool HasElement( KeyAlt_t k ) const { return InvalidHandle() != Find( k ); }
  181. // Key insertion or lookup, always returns a valid handle
  182. // Using a different prototype for InsertIfNotFound since it could be confused with Insert if the ValueArg_t is a bool*
  183. handle_t Insert( KeyArg_t k ) { return DoInsert<KeyArg_t>( k, m_hash(k), nullptr ); }
  184. handle_t InsertIfNotFound( KeyArg_t k, bool* pDidInsert ) { return DoInsert<KeyArg_t>( k, m_hash( k ), pDidInsert ); }
  185. handle_t Insert( KeyArg_t k, ValueArg_t v, bool *pDidInsert = nullptr ) { return DoInsert<KeyArg_t>( k, v, m_hash(k), pDidInsert ); }
  186. handle_t Insert( KeyArg_t k, ValueArg_t v, unsigned int hash, bool *pDidInsert = nullptr ) { Assert( hash == m_hash(k) ); return DoInsert<KeyArg_t>( k, v, hash, pDidInsert ); }
  187. // Alternate-type key insertion or lookup, always returns a valid handle
  188. // Using a different prototype for InsertIfNotFound since it could be confused with Insert if the ValueArg_t is a bool*
  189. handle_t Insert( KeyAlt_t k ) { return DoInsert<KeyAlt_t>( k, m_hash(k), nullptr ); }
  190. handle_t InsertIfNotFound( KeyAlt_t k, bool* pDidInsert ) { return DoInsert<KeyAlt_t>( k, m_hash( k ), pDidInsert ); }
  191. handle_t Insert( KeyAlt_t k, ValueArg_t v, bool *pDidInsert = NULL ) { return DoInsert<KeyAlt_t>( k, v, m_hash(k), pDidInsert ); }
  192. handle_t Insert( KeyAlt_t k, ValueArg_t v, unsigned int hash, bool *pDidInsert = NULL ) { Assert( hash == m_hash(k) ); return DoInsert<KeyAlt_t>( k, v, hash, pDidInsert ); }
  193. // Key removal, returns false if not found
  194. bool Remove( KeyArg_t k ) { return DoRemove<KeyArg_t>( k, m_hash(k) ) >= 0; }
  195. bool Remove( KeyArg_t k, unsigned int hash ) { Assert( hash == m_hash(k) ); return DoRemove<KeyArg_t>( k, hash ) >= 0; }
  196. // Alternate-type key removal, returns false if not found
  197. bool Remove( KeyAlt_t k ) { return DoRemove<KeyAlt_t>( k, m_hash(k) ) >= 0; }
  198. bool Remove( KeyAlt_t k, unsigned int hash ) { Assert( hash == m_hash(k) ); return DoRemove<KeyAlt_t>( k, hash ) >= 0; }
  199. // Remove while iterating, returns the next handle for forward iteration
  200. // Note: aside from this, ALL handles are invalid if an element is removed
  201. handle_t RemoveAndAdvance( handle_t idx );
  202. // Remove by handle, convenient when you look up a handle and do something with it before removing the element
  203. void RemoveByHandle( handle_t idx );
  204. // Nuke contents
  205. void RemoveAll();
  206. // Nuke and release memory.
  207. void Purge() { RemoveAll(); m_table.Purge(); }
  208. // Reserve table capacity up front to avoid reallocation during insertions
  209. void Reserve( int expected ) { if ( expected > m_nUsed ) DoRealloc( expected * 4 / 3 ); }
  210. // Shrink to best-fit size, re-insert keys for optimal lookup
  211. void Compact( bool bMinimal ) { DoRealloc( bMinimal ? m_nUsed : ( m_nUsed * 4 / 3 ) ); }
  212. // Access functions. Note: if ValueT is empty_t, all functions return const keys.
  213. typedef typename KVPair::ValueReturn_t Element_t;
  214. KeyT const &Key( handle_t idx ) const { return m_table[idx]->m_key; }
  215. Element_t const &Element( handle_t idx ) const { return m_table[idx]->GetValue(); }
  216. Element_t &Element(handle_t idx) { return m_table[idx]->GetValue(); }
  217. Element_t const &operator[]( handle_t idx ) const { return m_table[idx]->GetValue(); }
  218. Element_t &operator[]( handle_t idx ) { return m_table[idx]->GetValue(); }
  219. void ReplaceKey( handle_t idx, KeyArg_t k ) { Assert( m_eq( m_table[idx]->m_key, k ) && m_hash( k ) == m_hash( m_table[idx]->m_key ) ); m_table[idx]->m_key = k; }
  220. void ReplaceKey( handle_t idx, KeyAlt_t k ) { Assert( m_eq( m_table[idx]->m_key, k ) && m_hash( k ) == m_hash( m_table[idx]->m_key ) ); m_table[idx]->m_key = k; }
  221. Element_t const &Get( KeyArg_t k, Element_t const &defaultValue ) const { handle_t h = Find( k ); if ( h != InvalidHandle() ) return Element( h ); return defaultValue; }
  222. Element_t const &Get( KeyAlt_t k, Element_t const &defaultValue ) const { handle_t h = Find( k ); if ( h != InvalidHandle() ) return Element( h ); return defaultValue; }
  223. Element_t const *GetPtr( KeyArg_t k ) const { handle_t h = Find(k); if ( h != InvalidHandle() ) return &Element( h ); return NULL; }
  224. Element_t const *GetPtr( KeyAlt_t k ) const { handle_t h = Find(k); if ( h != InvalidHandle() ) return &Element( h ); return NULL; }
  225. Element_t *GetPtr( KeyArg_t k ) { handle_t h = Find( k ); if ( h != InvalidHandle() ) return &Element( h ); return NULL; }
  226. Element_t *GetPtr( KeyAlt_t k ) { handle_t h = Find( k ); if ( h != InvalidHandle() ) return &Element( h ); return NULL; }
  227. // Swap memory and contents with another identical hashtable
  228. // (NOTE: if using function pointers or functors with state,
  229. // it is up to the caller to ensure that they are compatible!)
  230. void Swap( CUtlHashtable &other ) { m_table.Swap(other.m_table); ::V_swap(m_nUsed, other.m_nUsed); }
  231. // GetMemoryUsage returns all memory held by this class
  232. // and its held classes. It does not include sizeof(*this).
  233. size_t GetMemoryUsage() const
  234. {
  235. return m_table.AllocSize();
  236. }
  237. size_t GetReserveCount( )const
  238. {
  239. return m_table.Count();
  240. }
  241. #if _DEBUG
  242. // Validate the integrity of the hashtable
  243. void DbgCheckIntegrity() const;
  244. #endif
  245. private:
  246. CUtlHashtable(const CUtlHashtable& copyConstructorIsNotImplemented);
  247. };
  248. // Set external memory (raw byte buffer, best-fit)
  249. template <typename KeyT, typename ValueT, typename KeyHashT, typename KeyIsEqualT, typename AltKeyT>
  250. void CUtlHashtable<KeyT, ValueT, KeyHashT, KeyIsEqualT, AltKeyT>::SetExternalBuffer( byte* pRawBuffer, unsigned int nBytes, bool bAssumeOwnership, bool bGrowable )
  251. {
  252. AssertDbg( ((uintptr_t)pRawBuffer % VALIGNOF(int)) == 0 );
  253. uint32 bestSize = LargestPowerOfTwoLessThanOrEqual( nBytes / sizeof(entry_t) );
  254. Assert( bestSize != 0 && bestSize*sizeof(entry_t) <= nBytes );
  255. return SetExternalBuffer( (entry_t*) pRawBuffer, bestSize, bAssumeOwnership, bGrowable );
  256. }
  257. // Set external memory (typechecked, must be power of two)
  258. template <typename KeyT, typename ValueT, typename KeyHashT, typename KeyIsEqualT, typename AltKeyT>
  259. void CUtlHashtable<KeyT, ValueT, KeyHashT, KeyIsEqualT, AltKeyT>::SetExternalBuffer( entry_t* pBuffer, unsigned int nSize, bool bAssumeOwnership, bool bGrowable )
  260. {
  261. Assert( IsPowerOfTwo(nSize) );
  262. Assert( m_nUsed == 0 );
  263. for ( uint i = 0; i < nSize; ++i )
  264. pBuffer[i].MarkInvalid();
  265. if ( bAssumeOwnership )
  266. m_table.AssumeMemory( pBuffer, nSize );
  267. else
  268. m_table.SetExternalBuffer( pBuffer, nSize );
  269. m_bSizeLocked = !bGrowable;
  270. }
  271. // Allocate an empty table and then re-insert all existing entries.
  272. template <typename KeyT, typename ValueT, typename KeyHashT, typename KeyIsEqualT, typename AltKeyT>
  273. void CUtlHashtable<KeyT, ValueT, KeyHashT, KeyIsEqualT, AltKeyT>::DoRealloc( int size )
  274. {
  275. Assert( !m_bSizeLocked );
  276. size = SmallestPowerOfTwoGreaterOrEqual( MAX( m_nMinSize, size ) );
  277. Assert( size > 0 && (uint)size <= entry_t::IdealIndex( ~0, 0x1FFFFFFF ) ); // reasonable power of 2
  278. Assert( size > m_nUsed );
  279. CUtlMemory<entry_t> oldTable;
  280. oldTable.Swap( m_table );
  281. entry_t * RESTRICT const pOldBase = oldTable.Base();
  282. m_table.EnsureCapacity( size );
  283. entry_t * const pNewBase = m_table.Base();
  284. for ( int i = 0; i < size; ++i )
  285. pNewBase[i].MarkInvalid();
  286. int nLeftToMove = m_nUsed;
  287. m_nUsed = 0;
  288. for ( int i = oldTable.Count() - 1; i >= 0; --i )
  289. {
  290. if ( pOldBase[i].IsValid() )
  291. {
  292. int newIdx = DoInsertUnconstructed( pOldBase[i].flags_and_hash, false );
  293. pNewBase[newIdx].MoveDataFrom( pOldBase[i] );
  294. if ( --nLeftToMove == 0 )
  295. break;
  296. }
  297. }
  298. Assert( nLeftToMove == 0 );
  299. }
  300. // Move an existing entry to a free slot, leaving a hole behind
  301. template <typename KeyT, typename ValueT, typename KeyHashT, typename KeyIsEqualT, typename AltKeyT>
  302. void CUtlHashtable<KeyT, ValueT, KeyHashT, KeyIsEqualT, AltKeyT>::BumpEntry( unsigned int idx )
  303. {
  304. Assert( m_table[idx].IsValid() );
  305. Assert( m_nUsed < m_table.Count() );
  306. entry_t* table = m_table.Base();
  307. unsigned int slotmask = m_table.Count()-1;
  308. unsigned int new_flags_and_hash = table[idx].flags_and_hash & (FLAG_LAST | MASK_HASH);
  309. unsigned int chainid = entry_t::IdealIndex( new_flags_and_hash, slotmask );
  310. // Look for empty slots scanning forward, stripping FLAG_LAST as we go.
  311. // Note: this potentially strips FLAG_LAST from table[idx] if we pass it
  312. int newIdx = chainid; // start at ideal slot
  313. for ( ; ; newIdx = (newIdx + 1) & slotmask )
  314. {
  315. if ( table[newIdx].IdealIndex( slotmask ) == chainid )
  316. {
  317. if ( table[newIdx].flags_and_hash & FLAG_LAST )
  318. {
  319. table[newIdx].flags_and_hash &= ~FLAG_LAST;
  320. new_flags_and_hash |= FLAG_LAST;
  321. }
  322. continue;
  323. }
  324. if ( table[newIdx].IsValid() )
  325. {
  326. continue;
  327. }
  328. break;
  329. }
  330. // Did we pick something closer to the ideal slot, leaving behind a
  331. // FLAG_LAST bit on the current slot because we didn't scan past it?
  332. if ( table[idx].flags_and_hash & FLAG_LAST )
  333. {
  334. #ifdef _DEBUG
  335. Assert( new_flags_and_hash & FLAG_LAST );
  336. // Verify logic: we must have moved to an earlier slot, right?
  337. uint offset = ((uint)idx - chainid + slotmask + 1) & slotmask;
  338. uint newOffset = ((uint)newIdx - chainid + slotmask + 1) & slotmask;
  339. Assert( newOffset < offset );
  340. #endif
  341. // Scan backwards from old to new location, depositing FLAG_LAST on
  342. // the first match we find. (+slotmask) is the same as (-1) without
  343. // having to make anyone think about two's complement shenanigans.
  344. int scan = (idx + slotmask) & slotmask;
  345. while ( scan != newIdx )
  346. {
  347. if ( table[scan].IdealIndex( slotmask ) == chainid )
  348. {
  349. table[scan].flags_and_hash |= FLAG_LAST;
  350. new_flags_and_hash &= ~FLAG_LAST;
  351. break;
  352. }
  353. scan = (scan + slotmask) & slotmask;
  354. }
  355. }
  356. // Move entry to the free slot we found, leaving a hole at idx
  357. table[newIdx].flags_and_hash = new_flags_and_hash;
  358. table[newIdx].MoveDataFrom( table[idx] );
  359. table[idx].MarkInvalid();
  360. }
  361. // Insert a value at the root position for that value's hash chain.
  362. template <typename KeyT, typename ValueT, typename KeyHashT, typename KeyIsEqualT, typename AltKeyT>
  363. int CUtlHashtable<KeyT, ValueT, KeyHashT, KeyIsEqualT, AltKeyT>::DoInsertUnconstructed( unsigned int h, bool allowGrow )
  364. {
  365. if ( allowGrow && !m_bSizeLocked )
  366. {
  367. // Keep the load factor between .25 and .75
  368. int newSize = m_nUsed + 1;
  369. if ( ( newSize*4 < m_table.Count() && m_table.Count() > m_nMinSize*2 ) || newSize*4 > m_table.Count()*3 )
  370. {
  371. DoRealloc( newSize * 4 / 3 );
  372. }
  373. }
  374. Assert( m_nUsed < m_table.Count() );
  375. ++m_nUsed;
  376. entry_t* table = m_table.Base();
  377. unsigned int slotmask = m_table.Count()-1;
  378. unsigned int new_flags_and_hash = FLAG_LAST | (h & MASK_HASH);
  379. unsigned int idx = entry_t::IdealIndex( h, slotmask );
  380. if ( table[idx].IdealIndex( slotmask ) == idx )
  381. {
  382. // There is already an entry in this chain.
  383. new_flags_and_hash &= ~FLAG_LAST;
  384. BumpEntry(idx);
  385. }
  386. else if ( table[idx].IsValid() )
  387. {
  388. // Somebody else is living in our ideal index but does not belong
  389. // to our entry chain; move it out of the way, start a new chain.
  390. BumpEntry(idx);
  391. }
  392. table[idx].flags_and_hash = new_flags_and_hash;
  393. return idx;
  394. }
  395. // Key lookup. Can also return previous-in-chain if result is a chained slot.
  396. template <typename KeyT, typename ValueT, typename KeyHashT, typename KeyIsEqualT, typename AltKeyT>
  397. template <typename KeyParamT>
  398. UtlHashHandle_t CUtlHashtable<KeyT, ValueT, KeyHashT, KeyIsEqualT, AltKeyT>::DoLookup( KeyParamT x, unsigned int h, handle_t *pPreviousInChain ) const
  399. {
  400. if ( m_nUsed == 0 )
  401. {
  402. // Empty table.
  403. return (handle_t) -1;
  404. }
  405. const entry_t* table = m_table.Base();
  406. unsigned int slotmask = m_table.Count()-1;
  407. Assert( m_table.Count() > 0 && (slotmask & m_table.Count()) == 0 );
  408. unsigned int chainid = entry_t::IdealIndex( h, slotmask );
  409. unsigned int idx = chainid;
  410. if ( table[idx].IdealIndex( slotmask ) != chainid )
  411. {
  412. // Nothing in root position? No match.
  413. return (handle_t) -1;
  414. }
  415. // Linear scan until found or end of chain
  416. handle_t lastIdx = (handle_t) -1;
  417. while (1)
  418. {
  419. // Only examine this slot if it is valid and belongs to our hash chain
  420. if ( table[idx].IdealIndex( slotmask ) == chainid )
  421. {
  422. // Test the full-width hash to avoid unnecessary calls to m_eq()
  423. if ( ((table[idx].flags_and_hash ^ h) & MASK_HASH) == 0 && m_eq( table[idx]->m_key, x ) )
  424. {
  425. // Found match!
  426. if (pPreviousInChain)
  427. *pPreviousInChain = lastIdx;
  428. return (handle_t) idx;
  429. }
  430. if ( table[idx].flags_and_hash & FLAG_LAST )
  431. {
  432. // End of chain. No match.
  433. return (handle_t) -1;
  434. }
  435. lastIdx = (handle_t) idx;
  436. }
  437. idx = (idx + 1) & slotmask;
  438. }
  439. }
  440. // Key insertion, or return index of existing key if found
  441. template <typename KeyT, typename ValueT, typename KeyHashT, typename KeyIsEqualT, typename AltKeyT>
  442. template <typename KeyParamT>
  443. UtlHashHandle_t CUtlHashtable<KeyT, ValueT, KeyHashT, KeyIsEqualT, AltKeyT>::DoInsert( KeyParamT k, unsigned int h, bool* pDidInsert )
  444. {
  445. handle_t idx = DoLookup<KeyParamT>( k, h, NULL );
  446. bool bShouldInsert = ( idx == (handle_t)-1 );
  447. if ( pDidInsert )
  448. {
  449. *pDidInsert = bShouldInsert;
  450. }
  451. if ( bShouldInsert )
  452. {
  453. idx = (handle_t) DoInsertUnconstructed( h, true );
  454. Construct( m_table[ idx ].Raw(), k );
  455. }
  456. return idx;
  457. }
  458. // Key insertion, or return index of existing key if found
  459. template <typename KeyT, typename ValueT, typename KeyHashT, typename KeyIsEqualT, typename AltKeyT>
  460. template <typename KeyParamT>
  461. UtlHashHandle_t CUtlHashtable<KeyT, ValueT, KeyHashT, KeyIsEqualT, AltKeyT>::DoInsert( KeyParamT k, typename ArgumentTypeInfo<ValueT>::Arg_t v, unsigned int h, bool *pDidInsert )
  462. {
  463. handle_t idx = DoLookup<KeyParamT>( k, h, NULL );
  464. if ( idx == (handle_t) -1 )
  465. {
  466. idx = (handle_t) DoInsertUnconstructed( h, true );
  467. Construct( m_table[ idx ].Raw(), k, v );
  468. if ( pDidInsert ) *pDidInsert = true;
  469. }
  470. else
  471. {
  472. if ( pDidInsert ) *pDidInsert = false;
  473. }
  474. return idx;
  475. }
  476. // Key insertion
  477. template <typename KeyT, typename ValueT, typename KeyHashT, typename KeyIsEqualT, typename AltKeyT>
  478. template <typename KeyParamT>
  479. UtlHashHandle_t CUtlHashtable<KeyT, ValueT, KeyHashT, KeyIsEqualT, AltKeyT>::DoInsertNoCheck( KeyParamT k, typename ArgumentTypeInfo<ValueT>::Arg_t v, unsigned int h )
  480. {
  481. Assert( DoLookup<KeyParamT>( k, h, NULL ) == (handle_t) -1 );
  482. handle_t idx = (handle_t) DoInsertUnconstructed( h, true );
  483. Construct( m_table[ idx ].Raw(), k, v );
  484. return idx;
  485. }
  486. // Remove single element by key + hash. Returns the location of the new empty hole.
  487. template <typename KeyT, typename ValueT, typename KeyHashT, typename KeyIsEqualT, typename AltKeyT>
  488. template <typename KeyParamT>
  489. int CUtlHashtable<KeyT, ValueT, KeyHashT, KeyIsEqualT, AltKeyT>::DoRemove( KeyParamT x, unsigned int h )
  490. {
  491. unsigned int slotmask = m_table.Count()-1;
  492. handle_t previous = (handle_t) -1;
  493. int idx = (int) DoLookup<KeyParamT>( x, h, &previous );
  494. if (idx == -1)
  495. {
  496. return -1;
  497. }
  498. enum { FAKEFLAG_ROOT = 1 };
  499. int nLastAndRootFlags = m_table[idx].flags_and_hash & FLAG_LAST;
  500. nLastAndRootFlags |= ( (uint)idx == m_table[idx].IdealIndex( slotmask ) );
  501. // Remove from table
  502. m_table[idx].MarkInvalid();
  503. Destruct( m_table[idx].Raw() );
  504. --m_nUsed;
  505. if ( nLastAndRootFlags == FLAG_LAST ) // last only, not root
  506. {
  507. // This was the end of the chain - mark previous as last.
  508. // (This isn't the root, so there must be a previous.)
  509. Assert( previous != (handle_t) -1 );
  510. m_table[previous].flags_and_hash |= FLAG_LAST;
  511. }
  512. if ( nLastAndRootFlags == FAKEFLAG_ROOT ) // root only, not last
  513. {
  514. // If we are removing the root and there is more to the chain,
  515. // scan to find the next chain entry and move it to the root.
  516. unsigned int chainid = entry_t::IdealIndex( h, slotmask );
  517. unsigned int nextIdx = idx;
  518. while (1)
  519. {
  520. nextIdx = (nextIdx + 1) & slotmask;
  521. if ( m_table[nextIdx].IdealIndex( slotmask ) == chainid )
  522. {
  523. break;
  524. }
  525. }
  526. Assert( !(m_table[nextIdx].flags_and_hash & FLAG_FREE) );
  527. // Leave a hole where the next entry in the chain was.
  528. m_table[idx].flags_and_hash = m_table[nextIdx].flags_and_hash;
  529. m_table[idx].MoveDataFrom( m_table[nextIdx] );
  530. m_table[nextIdx].MarkInvalid();
  531. return nextIdx;
  532. }
  533. // The hole is still where the element used to be.
  534. return idx;
  535. }
  536. // Assignment operator. It's up to the user to make sure that the hash and equality functors match.
  537. template <typename K, typename V, typename H, typename E, typename A>
  538. CUtlHashtable<K,V,H,E,A> &CUtlHashtable<K,V,H,E,A>::operator=( CUtlHashtable<K,V,H,E,A> const &src )
  539. {
  540. if ( &src != this )
  541. {
  542. Assert( !m_bSizeLocked || m_table.Count() >= src.m_nUsed );
  543. if ( !m_bSizeLocked )
  544. {
  545. Purge();
  546. Reserve(src.m_nUsed);
  547. }
  548. else
  549. {
  550. RemoveAll();
  551. }
  552. const entry_t * srcTable = src.m_table.Base();
  553. for ( int i = src.m_table.Count() - 1; i >= 0; --i )
  554. {
  555. if ( srcTable[i].IsValid() )
  556. {
  557. // If this assert trips, double-check that both hashtables
  558. // have the same hash function pointers or hash functor state!
  559. Assert( m_hash(srcTable[i]->m_key) == src.m_hash(srcTable[i]->m_key) );
  560. int newIdx = DoInsertUnconstructed( srcTable[i].flags_and_hash , false );
  561. Construct( m_table[newIdx].Raw(), *srcTable[i].Raw() ); // copy construct KVPair
  562. }
  563. }
  564. }
  565. return *this;
  566. }
  567. // Remove and return the next valid iterator for a forward iteration.
  568. template <typename KeyT, typename ValueT, typename KeyHashT, typename KeyIsEqualT, typename AltKeyT>
  569. UtlHashHandle_t CUtlHashtable<KeyT, ValueT, KeyHashT, KeyIsEqualT, AltKeyT>::RemoveAndAdvance( UtlHashHandle_t idx )
  570. {
  571. Assert( IsValidHandle( idx ) );
  572. // TODO optimize, implement DoRemoveAt that does not need to re-evaluate equality in DoLookup
  573. int hole = DoRemove< KeyArg_t >( m_table[idx]->m_key, m_table[idx].flags_and_hash & MASK_HASH );
  574. // DoRemove returns the index of the element that it moved to fill the hole, if any.
  575. if ( hole <= (int) idx )
  576. {
  577. // Didn't fill, or filled from a previously seen element.
  578. return NextHandle( idx );
  579. }
  580. else
  581. {
  582. // Do not advance; slot has a new un-iterated value.
  583. Assert( IsValidHandle(idx) );
  584. return idx;
  585. }
  586. }
  587. // Remove and return the next valid iterator for a forward iteration.
  588. template <typename KeyT, typename ValueT, typename KeyHashT, typename KeyIsEqualT, typename AltKeyT>
  589. void CUtlHashtable<KeyT, ValueT, KeyHashT, KeyIsEqualT, AltKeyT>::RemoveByHandle( UtlHashHandle_t idx )
  590. {
  591. AssertDbg( IsValidHandle( idx ) );
  592. // Copied from RemoveAndAdvance(): TODO optimize, implement DoRemoveAt that does not need to re-evaluate equality in DoLookup
  593. DoRemove< KeyArg_t >( m_table[idx]->m_key, m_table[idx].flags_and_hash & MASK_HASH );
  594. }
  595. // Burn it with fire.
  596. template <typename KeyT, typename ValueT, typename KeyHashT, typename KeyIsEqualT, typename AltKeyT>
  597. void CUtlHashtable<KeyT, ValueT, KeyHashT, KeyIsEqualT, AltKeyT>::RemoveAll()
  598. {
  599. int used = m_nUsed;
  600. if ( used != 0 )
  601. {
  602. entry_t* table = m_table.Base();
  603. for ( int i = m_table.Count() - 1; i >= 0; --i )
  604. {
  605. if ( table[i].IsValid() )
  606. {
  607. table[i].MarkInvalid();
  608. Destruct( table[i].Raw() );
  609. if ( --used == 0 )
  610. break;
  611. }
  612. }
  613. m_nUsed = 0;
  614. }
  615. }
  616. template <typename KeyT, typename ValueT, typename KeyHashT, typename KeyIsEqualT, typename AltKeyT>
  617. UtlHashHandle_t CUtlHashtable<KeyT, ValueT, KeyHashT, KeyIsEqualT, AltKeyT>::NextHandle( handle_t start ) const
  618. {
  619. const entry_t *table = m_table.Base();
  620. for ( int i = (int)start + 1; i < m_table.Count(); ++i )
  621. {
  622. if ( table[i].IsValid() )
  623. return (handle_t) i;
  624. }
  625. return (handle_t) -1;
  626. }
  627. #if _DEBUG
  628. template <typename KeyT, typename ValueT, typename KeyHashT, typename KeyIsEqualT, typename AltKeyT>
  629. void CUtlHashtable<KeyT, ValueT, KeyHashT, KeyIsEqualT, AltKeyT>::DbgCheckIntegrity() const
  630. {
  631. // Stress test the hash table as a test of both container functionality
  632. // and also the validity of the user's Hash and Equal function objects.
  633. // NOTE: will fail if function objects require any sort of state!
  634. CUtlHashtable clone;
  635. unsigned int bytes = sizeof(entry_t)*max(16,m_table.Count());
  636. byte* tempbuf = (byte*) malloc(bytes);
  637. clone.SetExternalBuffer( tempbuf, bytes, false, false );
  638. clone = *this;
  639. int count = 0, roots = 0, ends = 0;
  640. int slotmask = m_table.Count() - 1;
  641. for (int i = 0; i < m_table.Count(); ++i)
  642. {
  643. if (!(m_table[i].flags_and_hash & FLAG_FREE)) ++count;
  644. if (m_table[i].IdealIndex(slotmask) == (uint)i) ++roots;
  645. if (m_table[i].flags_and_hash & FLAG_LAST) ++ends;
  646. if (m_table[i].IsValid())
  647. {
  648. Assert( Find(m_table[i]->m_key) == (handle_t)i );
  649. Verify( clone.Remove(m_table[i]->m_key) );
  650. }
  651. else
  652. {
  653. Assert( m_table[i].flags_and_hash == FLAG_FREE );
  654. }
  655. }
  656. Assert( count == Count() && count >= roots && roots == ends );
  657. Assert( clone.Count() == 0 );
  658. clone.Purge();
  659. free(tempbuf);
  660. }
  661. #endif
  662. //-----------------------------------------------------------------------
  663. // CUtlStableHashtable
  664. //-----------------------------------------------------------------------
  665. // Stable hashtables are less memory and cache efficient, but can be
  666. // iterated quickly and their element handles are completely stable.
  667. // Implemented as a hashtable which only stores indices, and a separate
  668. // CUtlLinkedList data table which contains key-value pairs; this may
  669. // change to a more efficient structure in the future if space becomes
  670. // critical. I have some ideas about that but not the time to implement
  671. // at the moment. -henryg
  672. // Note: RemoveAndAdvance is slower than in CUtlHashtable because the
  673. // key needs to be re-hashed under the current implementation.
  674. template <typename KeyT, typename ValueT = empty_t, typename KeyHashT = DefaultHashFunctor<KeyT>, typename KeyIsEqualT = DefaultEqualFunctor<KeyT>, typename IndexStorageT = uint16, typename AlternateKeyT = typename ArgumentTypeInfo<KeyT>::Alt_t >
  675. class CUtlStableHashtable
  676. {
  677. public:
  678. typedef typename ArgumentTypeInfo<KeyT>::Arg_t KeyArg_t;
  679. typedef typename ArgumentTypeInfo<ValueT>::Arg_t ValueArg_t;
  680. typedef typename ArgumentTypeInfo<AlternateKeyT>::Arg_t KeyAlt_t;
  681. typedef typename CTypeSelect< sizeof( IndexStorageT ) == 2, uint16, uint32 >::type IndexStorage_t;
  682. protected:
  683. COMPILE_TIME_ASSERT( sizeof( IndexStorage_t ) == sizeof( IndexStorageT ) );
  684. typedef CUtlKeyValuePair< KeyT, ValueT > KVPair;
  685. struct HashProxy;
  686. struct EqualProxy;
  687. struct IndirectIndex;
  688. typedef CUtlHashtable< IndirectIndex, empty_t, HashProxy, EqualProxy, AlternateKeyT > Hashtable_t;
  689. typedef CUtlLinkedList< KVPair, IndexStorage_t > LinkedList_t;
  690. template <typename KeyArgumentT> bool DoRemove( KeyArgumentT k );
  691. template <typename KeyArgumentT> UtlHashHandle_t DoFind( KeyArgumentT k ) const;
  692. template <typename KeyArgumentT> UtlHashHandle_t DoInsert( KeyArgumentT k );
  693. template <typename KeyArgumentT, typename ValueArgumentT> UtlHashHandle_t DoInsert( KeyArgumentT k, ValueArgumentT v );
  694. public:
  695. KeyHashT &GetHashRef() { return m_table.GetHashRef().m_hash; }
  696. KeyIsEqualT &GetEqualRef() { return m_table.GetEqualRef().m_eq; }
  697. KeyHashT const &GetHashRef() const { return m_table.GetHashRef().m_hash; }
  698. KeyIsEqualT const &GetEqualRef() const { return m_table.GetEqualRef().m_eq; }
  699. UtlHashHandle_t Insert( KeyArg_t k ) { return DoInsert<KeyArg_t>( k ); }
  700. UtlHashHandle_t Insert( KeyAlt_t k ) { return DoInsert<KeyAlt_t>( k ); }
  701. UtlHashHandle_t Insert( KeyArg_t k, ValueArg_t v ) { return DoInsert<KeyArg_t, ValueArg_t>( k, v ); }
  702. UtlHashHandle_t Insert( KeyAlt_t k, ValueArg_t v ) { return DoInsert<KeyAlt_t, ValueArg_t>( k, v ); }
  703. UtlHashHandle_t Find( KeyArg_t k ) const { return DoFind<KeyArg_t>( k ); }
  704. UtlHashHandle_t Find( KeyAlt_t k ) const { return DoFind<KeyAlt_t>( k ); }
  705. bool Remove( KeyArg_t k ) { return DoRemove<KeyArg_t>( k ); }
  706. bool Remove( KeyAlt_t k ) { return DoRemove<KeyAlt_t>( k ); }
  707. void RemoveAll() { m_table.RemoveAll(); m_data.RemoveAll(); }
  708. void Purge() { m_table.Purge(); m_data.Purge(); }
  709. int Count() const { return m_table.Count(); }
  710. typedef typename KVPair::ValueReturn_t Element_t;
  711. KeyT const &Key( UtlHashHandle_t idx ) const { return m_data[idx].m_key; }
  712. Element_t const &Element( UtlHashHandle_t idx ) const { return m_data[idx].GetValue(); }
  713. Element_t &Element( UtlHashHandle_t idx ) { return m_data[idx].GetValue(); }
  714. Element_t const &operator[]( UtlHashHandle_t idx ) const { return m_data[idx].GetValue(); }
  715. Element_t &operator[]( UtlHashHandle_t idx ) { return m_data[idx].GetValue(); }
  716. void ReplaceKey( UtlHashHandle_t idx, KeyArg_t k ) { Assert( GetEqualRef()( m_data[idx].m_key, k ) && GetHashRef()( k ) == GetHashRef()( m_data[idx].m_key ) ); m_data[idx].m_key = k; }
  717. void ReplaceKey( UtlHashHandle_t idx, KeyAlt_t k ) { Assert( GetEqualRef()( m_data[idx].m_key, k ) && GetHashRef()( k ) == GetHashRef()( m_data[idx].m_key ) ); m_data[idx].m_key = k; }
  718. Element_t const &Get( KeyArg_t k, Element_t const &defaultValue ) const { UtlHashHandle_t h = Find( k ); if ( h != InvalidHandle() ) return Element( h ); return defaultValue; }
  719. Element_t const &Get( KeyAlt_t k, Element_t const &defaultValue ) const { UtlHashHandle_t h = Find( k ); if ( h != InvalidHandle() ) return Element( h ); return defaultValue; }
  720. Element_t const *GetPtr( KeyArg_t k ) const { UtlHashHandle_t h = Find(k); if ( h != InvalidHandle() ) return &Element( h ); return NULL; }
  721. Element_t const *GetPtr( KeyAlt_t k ) const { UtlHashHandle_t h = Find(k); if ( h != InvalidHandle() ) return &Element( h ); return NULL; }
  722. Element_t *GetPtr( KeyArg_t k ) { UtlHashHandle_t h = Find( k ); if ( h != InvalidHandle() ) return &Element( h ); return NULL; }
  723. Element_t *GetPtr( KeyAlt_t k ) { UtlHashHandle_t h = Find( k ); if ( h != InvalidHandle() ) return &Element( h ); return NULL; }
  724. UtlHashHandle_t FirstHandle() const { return ExtendInvalidHandle( m_data.Head() ); }
  725. UtlHashHandle_t NextHandle( UtlHashHandle_t h ) const { return ExtendInvalidHandle( m_data.Next( h ) ); }
  726. bool IsValidHandle( UtlHashHandle_t h ) const { return m_data.IsValidIndex( h ); }
  727. UtlHashHandle_t InvalidHandle() const { return (UtlHashHandle_t)-1; }
  728. UtlHashHandle_t RemoveAndAdvance( UtlHashHandle_t h )
  729. {
  730. Assert( m_data.IsValidIndex( h ) );
  731. m_table.Remove( IndirectIndex( h ) );
  732. IndexStorage_t next = m_data.Next( h );
  733. m_data.Remove( h );
  734. return ExtendInvalidHandle(next);
  735. }
  736. void Compact( bool bMinimal ) { m_table.Compact( bMinimal ); /*m_data.Compact();*/ }
  737. void Swap( CUtlStableHashtable &other )
  738. {
  739. m_table.Swap(other.m_table);
  740. // XXX swapping CUtlLinkedList by block memory swap, ugh
  741. char buf[ sizeof(m_data) ];
  742. memcpy( buf, &m_data, sizeof(m_data) );
  743. memcpy( &m_data, &other.m_data, sizeof(m_data) );
  744. memcpy( &other.m_data, buf, sizeof(m_data) );
  745. }
  746. protected:
  747. // Perform extension of 0xFFFF to 0xFFFFFFFF if necessary. Note: ( a < CONSTANT ) ? 0 : -1 is usually branchless
  748. static UtlHashHandle_t ExtendInvalidHandle( uint32 x ) { return x; }
  749. static UtlHashHandle_t ExtendInvalidHandle( uint16 x ) { uint32 a = x; return a | ( ( a < 0xFFFFu ) ? 0 : -1 ); }
  750. struct IndirectIndex
  751. {
  752. explicit IndirectIndex(IndexStorage_t i) : m_index(i) { }
  753. IndexStorage_t m_index;
  754. };
  755. struct HashProxy
  756. {
  757. KeyHashT m_hash;
  758. unsigned int operator()( IndirectIndex idx ) const
  759. {
  760. const ptrdiff_t tableoffset = (uintptr_t)(&((Hashtable_t*)1024)->GetHashRef()) - 1024;
  761. const ptrdiff_t owneroffset = offsetof(CUtlStableHashtable, m_table) + tableoffset;
  762. CUtlStableHashtable* pOwner = (CUtlStableHashtable*)((uintptr_t)this - owneroffset);
  763. return m_hash( pOwner->m_data[ idx.m_index ].m_key );
  764. }
  765. unsigned int operator()( KeyArg_t k ) const { return m_hash( k ); }
  766. unsigned int operator()( KeyAlt_t k ) const { return m_hash( k ); }
  767. };
  768. struct EqualProxy
  769. {
  770. KeyIsEqualT m_eq;
  771. unsigned int operator()( IndirectIndex lhs, IndirectIndex rhs ) const
  772. {
  773. return lhs.m_index == rhs.m_index;
  774. }
  775. unsigned int operator()( IndirectIndex lhs, KeyArg_t rhs ) const
  776. {
  777. const ptrdiff_t tableoffset = (uintptr_t)(&((Hashtable_t*)1024)->GetEqualRef()) - 1024;
  778. const ptrdiff_t owneroffset = offsetof(CUtlStableHashtable, m_table) + tableoffset;
  779. CUtlStableHashtable* pOwner = (CUtlStableHashtable*)((uintptr_t)this - owneroffset);
  780. return m_eq( pOwner->m_data[ lhs.m_index ].m_key, rhs );
  781. }
  782. unsigned int operator()( IndirectIndex lhs, KeyAlt_t rhs ) const
  783. {
  784. const ptrdiff_t tableoffset = (uintptr_t)(&((Hashtable_t*)1024)->GetEqualRef()) - 1024;
  785. const ptrdiff_t owneroffset = offsetof(CUtlStableHashtable, m_table) + tableoffset;
  786. CUtlStableHashtable* pOwner = (CUtlStableHashtable*)((uintptr_t)this - owneroffset);
  787. return m_eq( pOwner->m_data[ lhs.m_index ].m_key, rhs );
  788. }
  789. };
  790. class CCustomLinkedList : public LinkedList_t
  791. {
  792. public:
  793. int AddToTailUnconstructed()
  794. {
  795. IndexStorage_t newNode = this->AllocInternal();
  796. if ( newNode != this->InvalidIndex() )
  797. this->LinkToTail( newNode );
  798. return newNode;
  799. }
  800. };
  801. Hashtable_t m_table;
  802. CCustomLinkedList m_data;
  803. };
  804. template <typename K, typename V, typename H, typename E, typename S, typename A>
  805. template <typename KeyArgumentT>
  806. inline bool CUtlStableHashtable<K,V,H,E,S,A>::DoRemove( KeyArgumentT k )
  807. {
  808. unsigned int hash = m_table.GetHashRef()( k );
  809. UtlHashHandle_t h = m_table.template DoLookup<KeyArgumentT>( k, hash, NULL );
  810. if ( h == m_table.InvalidHandle() )
  811. return false;
  812. int idx = m_table[ h ].m_index;
  813. m_table.template DoRemove<IndirectIndex>( IndirectIndex( idx ), hash );
  814. m_data.Remove( idx );
  815. return true;
  816. }
  817. template <typename K, typename V, typename H, typename E, typename S, typename A>
  818. template <typename KeyArgumentT>
  819. inline UtlHashHandle_t CUtlStableHashtable<K,V,H,E,S,A>::DoFind( KeyArgumentT k ) const
  820. {
  821. unsigned int hash = m_table.GetHashRef()( k );
  822. UtlHashHandle_t h = m_table.template DoLookup<KeyArgumentT>( k, hash, NULL );
  823. if ( h != m_table.InvalidHandle() )
  824. return m_table[ h ].m_index;
  825. return (UtlHashHandle_t) -1;
  826. }
  827. template <typename K, typename V, typename H, typename E, typename S, typename A>
  828. template <typename KeyArgumentT>
  829. inline UtlHashHandle_t CUtlStableHashtable<K,V,H,E,S,A>::DoInsert( KeyArgumentT k )
  830. {
  831. unsigned int hash = m_table.GetHashRef()( k );
  832. UtlHashHandle_t h = m_table.template DoLookup<KeyArgumentT>( k, hash, NULL );
  833. if ( h != m_table.InvalidHandle() )
  834. return m_table[ h ].m_index;
  835. int idx = m_data.AddToTailUnconstructed();
  836. Construct( &m_data[idx], k );
  837. m_table.template DoInsertNoCheck<IndirectIndex>( IndirectIndex( idx ), empty_t(), hash );
  838. return idx;
  839. }
  840. template <typename K, typename V, typename H, typename E, typename S, typename A>
  841. template <typename KeyArgumentT, typename ValueArgumentT>
  842. inline UtlHashHandle_t CUtlStableHashtable<K,V,H,E,S,A>::DoInsert( KeyArgumentT k, ValueArgumentT v )
  843. {
  844. unsigned int hash = m_table.GetHashRef()( k );
  845. UtlHashHandle_t h = m_table.template DoLookup<KeyArgumentT>( k, hash, NULL );
  846. if ( h != m_table.InvalidHandle() )
  847. return m_table[ h ].m_index;
  848. int idx = m_data.AddToTailUnconstructed();
  849. Construct( &m_data[idx], k, v );
  850. m_table.template DoInsertNoCheck<IndirectIndex>( IndirectIndex( idx ), empty_t(), hash );
  851. return idx;
  852. }
  853. #endif // UTLHASHTABLE_H