1 //========= Copyright Valve Corporation, All rights reserved. =================//
2 //
3 // Purpose: index-based hash map container
4 //			Use FOR_EACH_HASHMAP to iterate through CUtlHashMap.
5 //
6 //=============================================================================//
7 
8 #ifndef UTLHASHMAP_H
9 #define UTLHASHMAP_H
10 #pragma once
11 
12 #include <tier0/dbg.h>
13 #include "utlvector.h"
14 
15 #define FOR_EACH_HASHMAP( mapName, iteratorName ) \
16 	for ( int iteratorName = 0; iteratorName < (mapName).MaxElement(); ++iteratorName ) if ( !(mapName).IsValidIndex( iteratorName ) ) continue; else
17 
18 //-----------------------------------------------------------------------------
19 //
20 // Purpose:	An associative container.  Similar to std::unordered_map,
21 // but without STL's rather wacky interface.  Also, each item is not a separate
22 // allocation, so insertion of items can cause existing items to move in memory.
23 //
24 // This differs from the one in Steam by not having any default hash or equality
25 // class.  We will use std::hash and std::equal_to insetad of our own hand-rolled
26 // versions, which I suspect do not add any value (any more at least).  Valve's
27 // CDefEquals unfortunately is not exactly the same as std::equal_to in the way
28 // it handles pointers, so let's require the few uses of hashmaps in use
29 // to be explicit in the equality operation.
30 //
31 //-----------------------------------------------------------------------------
32 template <typename K, typename T, typename L, typename H >
33 class CUtlHashMap
34 {
35 protected:
36 	enum ReplaceExisting
37 	{
38 		False = 0,
39 		True = 1,
40 	};
41 
42 public:
43 	using KeyType_t= K;
44 	using ElemType_t = T;
45 	using IndexType_t = int;
46 	using EqualityFunc_t = L;
47 	using HashFunc_t = H;
48 	static constexpr IndexType_t kInvalidIndex = -1;
49 
CUtlHashMap()50 	CUtlHashMap()
51 	{
52 		m_cElements = 0;
53 		m_nMaxElement = 0;
54 		m_nNeedRehashStart = 0;
55 		m_nNeedRehashEnd = 0;
56 		m_nMinBucketMask = 1;
57 		m_iNodeFreeListHead = kInvalidIndex;
58 	}
59 
CUtlHashMap(int cElementsExpected)60 	CUtlHashMap( int cElementsExpected )
61 	{
62 		m_cElements = 0;
63 		m_nMaxElement = 0;
64 		m_nNeedRehashStart = 0;
65 		m_nNeedRehashEnd = 0;
66 		m_nMinBucketMask = 1;
67 		m_iNodeFreeListHead = kInvalidIndex;
68 		EnsureCapacity( cElementsExpected );
69 	}
70 
~CUtlHashMap()71 	~CUtlHashMap()
72 	{
73 		Purge();
74 	}
75 
CopyFullHashMap(CUtlHashMap<K,T,L,H> & target)76 	void CopyFullHashMap( CUtlHashMap< K, T, L, H > &target ) const
77 	{
78 		target.RemoveAll();
79 		FOR_EACH_HASHMAP( *this, i )
80 		{
81 			target.Insert( this->Key( i ), this->Element( i ) );
82 		}
83 	}
84 
85 	// gets particular elements
Element(IndexType_t i)86 	ElemType_t &		Element( IndexType_t i )			{ return m_memNodes.Element( i ).m_elem; }
Element(IndexType_t i)87 	const ElemType_t &	Element( IndexType_t i ) const		{ return m_memNodes.Element( i ).m_elem; }
88 	ElemType_t &		operator[]( IndexType_t i )			{ return m_memNodes.Element( i ).m_elem; }
89 	const ElemType_t &	operator[]( IndexType_t i ) const	{ return m_memNodes.Element( i ).m_elem; }
Key(IndexType_t i)90 	const KeyType_t &	Key( IndexType_t i ) const			{ return m_memNodes.Element( i ).m_key; }
91 
92 	// Num elements
Count()93 	IndexType_t Count() const								{ return m_cElements; }
94 
95 	// Max "size" of the vector
MaxElement()96 	IndexType_t  MaxElement() const							{ return m_nMaxElement; }
97 
98 	/// Checks if a node is valid and in the map.
99 	/// NOTE: Do not use this function on the result of Find().  That is overkill
100 	/// and slower.  Instead, compare the returned index against InvalidIndex().
101 	/// (Or better use, use one of the methods sue as FindGetPtr() or HasElement()
102 	/// that makes it unnecessary to deal with indices at all.
IsValidIndex(IndexType_t i)103 	bool  IsValidIndex( IndexType_t i ) const				{ return /* i >= 0 && i < m_nMaxElement */ (unsigned)i < (unsigned)m_nMaxElement && m_memNodes[i].m_iNextNode >= -1; }
104 
105 	// Invalid index
InvalidIndex()106 	static constexpr IndexType_t InvalidIndex()						{ return -1; }
107 
108 	// Insert method
Insert(const KeyType_t & key)109 	IndexType_t  Insert( const KeyType_t &key )								{ return FindOrInsert_Internal( key, ReplaceExisting::True ); }
Insert(KeyType_t && key)110 	IndexType_t  Insert( KeyType_t &&key )									{ return FindOrInsert_Internal( std::move(key), ReplaceExisting::True ); }
111 
112 	// Insert or replace the existing if found (no dupes)
Insert(const KeyType_t & key,const ElemType_t & insert)113 	IndexType_t  Insert( const KeyType_t &key, const ElemType_t &insert )	{ return FindOrInsert_Internal( key, insert, ReplaceExisting::True ); }
Insert(const KeyType_t & key,ElemType_t && insert)114 	IndexType_t  Insert( const KeyType_t &key, ElemType_t &&insert )		{ return FindOrInsert_Internal( key, std::move(insert), ReplaceExisting::True ); }
Insert(KeyType_t && key,const ElemType_t & insert)115 	IndexType_t  Insert( KeyType_t &&key, const ElemType_t &insert )		{ return FindOrInsert_Internal( std::move(key), insert, ReplaceExisting::True ); }
Insert(KeyType_t && key,ElemType_t && insert)116 	IndexType_t  Insert( KeyType_t &&key, ElemType_t &&insert )				{ return FindOrInsert_Internal( std::move(key), std::move(insert), ReplaceExisting::True ); }
117 
118 	// Insert or replace the existing if found (no dupes)
InsertOrReplace(const KeyType_t & key,const ElemType_t & insert)119 	IndexType_t  InsertOrReplace( const KeyType_t &key, const ElemType_t &insert )	{ return FindOrInsert_Internal( key, insert, ReplaceExisting::True ); }
InsertOrReplace(const KeyType_t & key,ElemType_t && insert)120 	IndexType_t  InsertOrReplace( const KeyType_t &key, ElemType_t &&insert )		{ return FindOrInsert_Internal( key, std::move(insert), ReplaceExisting::True ); }
InsertOrReplace(KeyType_t && key,const ElemType_t & insert)121 	IndexType_t  InsertOrReplace( KeyType_t &&key, const ElemType_t &insert )		{ return FindOrInsert_Internal( std::move(key), insert, ReplaceExisting::True ); }
InsertOrReplace(KeyType_t && key,ElemType_t && insert)122 	IndexType_t  InsertOrReplace( KeyType_t &&key, ElemType_t &&insert )			{ return FindOrInsert_Internal( std::move(key), std::move(insert), ReplaceExisting::True ); }
123 
124 	// Insert ALWAYS, possibly creating a dupe
InsertWithDupes(const KeyType_t & key,const ElemType_t & insert)125 	IndexType_t  InsertWithDupes( const KeyType_t &key, const ElemType_t &insert )	{ return InsertWithDupes_Internal( key, insert ); }
InsertWithDupes(const KeyType_t & key,ElemType_t && insert)126 	IndexType_t  InsertWithDupes( const KeyType_t &key, ElemType_t &&insert )		{ return InsertWithDupes_Internal( key, std::move(insert) ); }
InsertWithDupes(KeyType_t && key,const ElemType_t & insert)127 	IndexType_t  InsertWithDupes( KeyType_t &&key, const ElemType_t &insert )		{ return InsertWithDupes_Internal( std::move(key), insert ); }
InsertWithDupes(KeyType_t && key,ElemType_t && insert)128 	IndexType_t  InsertWithDupes( KeyType_t &&key, ElemType_t &&insert )			{ return InsertWithDupes_Internal( std::move(key), std::move(insert) ); }
129 
130 	// Find-or-insert method, one-arg - can insert default-constructed element
131 	// when there is no available copy constructor or assignment operator
FindOrInsert(const KeyType_t & key)132 	IndexType_t  FindOrInsert( const KeyType_t &key )						{ return FindOrInsert_Internal( key, ReplaceExisting::False ); }
FindOrInsert(KeyType_t && key)133 	IndexType_t  FindOrInsert( KeyType_t &&key )							{ return FindOrInsert_Internal( std::move(key), ReplaceExisting::False ); }
134 
135 	// Find-or-insert method, two-arg - can insert an element when there is no
136 	// copy constructor for the type (but does require assignment operator)
FindOrInsert(const KeyType_t & key,const ElemType_t & insert)137 	IndexType_t  FindOrInsert( const KeyType_t &key, const ElemType_t &insert )	{ return FindOrInsert_Internal( key, insert, ReplaceExisting::False ); }
FindOrInsert(const KeyType_t & key,ElemType_t && insert)138 	IndexType_t  FindOrInsert( const KeyType_t &key, ElemType_t &&insert )		{ return FindOrInsert_Internal( key, std::move(insert), ReplaceExisting::False ); }
FindOrInsert(KeyType_t && key,const ElemType_t & insert)139 	IndexType_t  FindOrInsert( KeyType_t &&key, const ElemType_t &insert )		{ return FindOrInsert_Internal( std::move(key), insert, ReplaceExisting::False ); }
FindOrInsert(KeyType_t && key,ElemType_t && insert)140 	IndexType_t  FindOrInsert( KeyType_t &&key, ElemType_t &&insert )			{ return FindOrInsert_Internal( std::move(key), std::move(insert), ReplaceExisting::False ); }
141 
142 	// Find key, insert with default value if not found.  Returns pointer to the
143 	// element
FindOrInsertGetPtr(const KeyType_t & key)144 	ElemType_t *FindOrInsertGetPtr( const KeyType_t &key )
145 	{
146 		IndexType_t i = FindOrInsert(key);
147 		return &m_memNodes.Element( i ).m_elem;
148 	}
FindOrInsertGetPtr(KeyType_t && key)149 	ElemType_t *FindOrInsertGetPtr( KeyType_t &&key )
150 	{
151 		IndexType_t i = FindOrInsert( std::move( key ) );
152 		return &m_memNodes.Element( i ).m_elem;
153 	}
154 
155 	// Finds an element.  Returns index of element, or InvalidIndex() if not found
156 	IndexType_t  Find( const KeyType_t &key ) const;
157 
158 	// Finds an element, returns pointer to element or NULL if not found
FindGetPtr(const KeyType_t & key)159 	ElemType_t *FindGetPtr( const KeyType_t &key )
160 	{
161 		IndexType_t i = Find(key);
162 		return i == kInvalidIndex ? nullptr : &m_memNodes.Element( i ).m_elem;
163 	}
FindGetPtr(const KeyType_t & key)164 	const ElemType_t *FindGetPtr( const KeyType_t &key ) const
165 	{
166 		IndexType_t i = Find(key);
167 		return i == kInvalidIndex ? nullptr : &m_memNodes.Element( i ).m_elem;
168 	}
169 
170 	/// Returns true if the specified *key* (not the "element"!!!) can be found
171 	/// This name is definitely unfortunate, but remains because of compatibility
172 	/// with other containers and also other Valve codebases.
HasElement(const KeyType_t & key)173 	bool HasElement( const KeyType_t &key ) const { return Find( key ) != kInvalidIndex; }
174 
175 	// Finds an exact key/value match, even with duplicate keys. Requires operator== for ElemType_t.
176 	IndexType_t  FindExact( const KeyType_t &key, const ElemType_t &elem ) const;
177 
178 	// Find next element with same key
179 	IndexType_t  NextSameKey( IndexType_t i ) const;
180 
181 	void EnsureCapacity( int num );
182 
183 	//
184 	// DANGER DANGER
185 	// This doesn't really work if you pass a temporary to defaultValue!!!
186 	//
FindElement(const KeyType_t & key,const ElemType_t & defaultValue)187 	const ElemType_t &FindElement( const KeyType_t &key, const ElemType_t &defaultValue ) const
188 	{
189 		IndexType_t i = Find( key );
190 		if ( i == kInvalidIndex )
191 			return defaultValue;
192 		return Element( i );
193 	}
194 
195 	void RemoveAt( IndexType_t i );
Remove(const KeyType_t & key)196 	bool Remove( const KeyType_t &key )
197 	{
198 		int iMap = Find( key );
199 		if ( iMap != kInvalidIndex )
200 		{
201 			RemoveAt( iMap );
202 			return true;
203 		}
204 		return false;
205 	}
206 	void RemoveAll();
207 	void Purge();
208 
209 	// call delete on each element (as a pointer) and then purge
PurgeAndDeleteElements()210 	void PurgeAndDeleteElements()
211 	{
212 		FOR_EACH_HASHMAP( *this, i )
213 			delete this->Element(i);
214 		Purge();
215 	}
216 
217 	void Swap( CUtlHashMap< K, T, L, H > &that );
218 
219 protected:
220 	template < typename pf_key >
221 	IndexType_t  FindOrInsert_Internal( pf_key &&key, ReplaceExisting bReplace );
222 
223 	template < typename pf_key, typename pf_elem >
224 	IndexType_t  FindOrInsert_Internal( pf_key &&key, pf_elem &&insert, ReplaceExisting bReplace );
225 
226 	template < typename pf_key, typename pf_elem >
227 	IndexType_t  InsertWithDupes_Internal( pf_key &&key, pf_elem &&insert );
228 
229 	template < typename KeyType_universal_ref >
230 	IndexType_t InsertUnconstructed( KeyType_universal_ref &&key, IndexType_t *pExistingIndex, bool bAllowDupes );
231 
FreeNodeIDToIndex(IndexType_t i)232 	inline IndexType_t FreeNodeIDToIndex( IndexType_t i ) const	{ return (0-i)-3; }
FreeNodeIndexToID(IndexType_t i)233 	inline IndexType_t FreeNodeIndexToID( IndexType_t i ) const	{ return (-3)-i; }
234 
235 	int FindInBucket( int iBucket, const KeyType_t &key ) const;
236 	int AllocNode();
237 	void RehashNodesInBucket( int iBucket );
238 	void LinkNodeIntoBucket( int iBucket, int iNewNode );
239 	bool RemoveNodeFromBucket( int iBucket, int iNodeToRemove );
240 	void IncrementalRehash();
241 
242 	struct HashBucket_t
243 	{
244 		IndexType_t m_iNode;
245 	};
246 	CUtlVector<HashBucket_t> m_vecHashBuckets;
247 
248 	struct Node_t
249 	{
250 		KeyType_t m_key;
251 		ElemType_t m_elem;
252 		int m_iNextNode;
253 	};
254 
255 	CUtlMemory<Node_t> m_memNodes;
256 	IndexType_t m_iNodeFreeListHead;
257 
258 	IndexType_t m_cElements;
259 	IndexType_t m_nMaxElement;
260 	IndexType_t m_nNeedRehashStart, m_nNeedRehashEnd; // Range of buckets that need to be rehashed
261 	IndexType_t m_nMinBucketMask; // Mask at the time we last finished completed rehashing.  So no need to check hash buckets based on mask smaller than this.
262 	EqualityFunc_t m_EqualityFunc;
263 	HashFunc_t m_HashFunc;
264 
265 public:
266 	//
267 	// Range-based for loop iteration over the map.  You can iterate
268 	// over the keys, the values ("elements"), or both ("items").
269 	// This naming style comes from Python.
270 	//
271 	// Examples:
272 	//
273 	// CUtlHashMap<uint64,CUtlString> map;
274 	//
275 	// Iterate over the keys.  Your loop variable will receive
276 	// const KeyType_t &.  (You can use an ordinary KeyType_t
277 	// variable for small KeyType_t.)
278 	//
279 	//   for ( uint64 k: map.IterKeys() ) { ... }
280 	//
281 	// Iterate over the values ("elements").  Your loop variable will receive
282 	// [const] ElemType_t &.  (You can use an ordinary ElemType_t
283 	// variable for small ElemType_t if you don't need to modify the element.
284 	//   for ( CUtlString &v: map.IterValues() )
285 	//
286 	// Iterate over the "items" (key/value pairs) in the map.  Your
287 	// loop variable will receive an an ItemRef or MutableItemRef.  This is
288 	// a small proxy object that is very fast to copy, so you
289 	// will usually use plain "auto" (not auto&).  (Like std::map iterators,
290 	// using the actual type would be really messy and verbose, since it's a
291 	// template type, hence using auto.)
292 	//
293 	//   for ( auto item: map.IterItems() )
294 	//   {
295 	//       int i = item.Index();
296 	//       uint64 k = item.Key();
297 	//       CUtlString &v = item.Value();
298 	//   }
299 
300 	// A reference to a key/value pair in a map
301 	class ItemRef
302 	{
303 	protected:
304 		Node_t &m_node;
305 		const int m_idx;
306 	public:
ItemRef(const CUtlHashMap<K,T,L,H> & map,int idx)307 		inline ItemRef( const CUtlHashMap< K, T, L, H > &map, int idx ) : m_node( const_cast< Node_t &>( map.m_memNodes[idx] ) ), m_idx(idx) {}
308 		ItemRef( const ItemRef &x ) = default;
Index()309 		inline int Index() const { return m_idx; }
Key()310 		inline const KeyType_t &Key() const { return m_node.m_key; }
Element()311 		inline const ElemType_t &Element() const { return m_node.m_elem; }
312 	};
313 	struct MutableItemRef : ItemRef
314 	{
MutableItemRefMutableItemRef315 		inline MutableItemRef( CUtlHashMap< K, T, L, H > &map, int idx ) : ItemRef( map, idx ) {}
316 		MutableItemRef( const MutableItemRef &x ) = default;
317 		using ItemRef::Element; // const reference
ElementMutableItemRef318 		inline ElemType_t &Element() const { return this->m_node.m_elem; } // non-const reference
319 	};
320 
321 	// Base class iterator
322 	class Iterator
323 	{
324 	protected:
325 		CUtlHashMap<K, T, L, H > &m_map;
326 		int m_idx;
327 	public:
Iterator(const CUtlHashMap<K,T,L,H> & map,int idx)328 		inline Iterator( const CUtlHashMap< K, T, L, H > &map, int idx ) : m_map( const_cast< CUtlHashMap< K, T, L, H > &>( map ) ), m_idx(idx) {}
329 		Iterator( const Iterator &x ) = default;
330 		inline bool operator==( const Iterator &x ) const { return &m_map == &x.m_map && m_idx == x.m_idx; } // Comparing the map reference is probably not necessary in 99% of cases, but needed to be correct
331 		inline bool operator!=( const Iterator &x ) const { return &m_map != &x.m_map || m_idx != x.m_idx; }
332 		inline void operator++()
333 		{
334 			if ( m_idx == kInvalidIndex )
335 				return;
336 			do
337 			{
338 				++m_idx;
339 				if ( m_idx >= m_map.m_nMaxElement )
340 				{
341 					m_idx = kInvalidIndex;
342 					break;
343 				}
344 			} while ( m_map.m_memNodes[m_idx].m_iNextNode < -1 );
345 		}
346 	};
347 	struct MutableIterator : Iterator
348 	{
349 		inline MutableIterator( const MutableIterator &x ) = default;
MutableIteratorMutableIterator350 		inline MutableIterator( CUtlHashMap< K, T, L, H > &map, int idx ) : Iterator( map, idx ) {}
351 	};
352 	struct KeyIterator : Iterator
353 	{
354 		using Iterator::Iterator;
355 		inline const KeyType_t &operator*() { return this->m_map.m_memNodes[this->m_idx].m_key; }
356 	};
357 	struct ConstValueIterator : Iterator
358 	{
359 		using Iterator::Iterator;
360 		inline const ElemType_t &operator*() { return this->m_map.m_memNodes[this->m_idx].m_elem; }
361 	};
362 	struct MutableValueIterator : MutableIterator
363 	{
364 		using MutableIterator::MutableIterator;
365 		inline ElemType_t &operator*() { return this->m_map.m_memNodes[this->m_idx].m_elem; }
366 	};
367 	struct ConstItemIterator : Iterator
368 	{
369 		using Iterator::Iterator;
370 		inline ItemRef operator*() { return ItemRef( this->m_map, this->m_idx ); }
371 	};
372 	struct MutableItemIterator : public MutableIterator
373 	{
374 		using MutableIterator::MutableIterator;
375 		inline MutableItemRef operator*() { return MutableItemRef( this->m_map, this->m_idx ); }
376 	};
377 
378 	// Internal type used by the IterXxx functions.  You normally won't use
379 	// this directly, because it will be consumed by the machinery of the
380 	// range-based for loop.
381 	template <typename TIterator>
382 	class Range
383 	{
384 		CUtlHashMap< K, T, L, H > &m_map;
385 	public:
Range(const CUtlHashMap<K,T,L,H> & map)386 		Range( const CUtlHashMap< K, T, L, H > &map ) : m_map( const_cast< CUtlHashMap< K, T, L, H > &>( map ) ) {}
begin()387 		TIterator begin() const
388 		{
389 			int idx;
390 			if ( m_map.m_cElements <= 0 )
391 			{
392 				idx = kInvalidIndex;
393 			}
394 			else
395 			{
396 				idx = 0;
397 				while ( m_map.m_memNodes[idx].m_iNextNode < -1 )
398 				{
399 					++idx;
400 					Assert( idx < m_map.m_nMaxElement ); // Or else how is m_map.m_cElements > 0?
401 				}
402 			}
403 			return TIterator( m_map, idx );
404 		}
end()405 		TIterator end() const { return TIterator( m_map, kInvalidIndex ); }
406 	};
407 
408 	/// Iterate over the keys.  You will receive a reference to the key/
IterKeys()409 	Range<KeyIterator> IterKeys() const { return Range<KeyIterator>(*this); }
410 
411 	/// Iterate over the values ("elements").  You will receive a reference to the value.
IterValues()412 	Range<ConstValueIterator> IterValues() const { return Range<ConstValueIterator>(*this); }
IterValues()413 	Range<MutableValueIterator> IterValues() { return Range<MutableValueIterator>(*this); }
414 
415 	/// Iterate over the "items" (key/value pairs).  You will receive a small reference
416 	/// object that is cheap to copy.
IterItems()417 	Range<ConstItemIterator> IterItems() const { return Range<ConstItemIterator>(*this); }
IterItems()418 	Range<MutableItemIterator> IterItems() { return Range<MutableItemIterator>(*this); }
419 
420 };
421 
422 
423 //-----------------------------------------------------------------------------
424 // Purpose: inserts and constructs a key into the map.
425 // Element member is left unconstructed (to be copy constructed or default-constructed by a wrapper function)
426 // Supports both copy and move constructors for KeyType_t via universal refs and type deduction
427 //-----------------------------------------------------------------------------
428 template <typename K, typename T, typename L, typename H>
429 template <typename KeyType_universal_ref>
InsertUnconstructed(KeyType_universal_ref && key,int * piNodeExistingIfDupe,bool bAllowDupes)430 inline int CUtlHashMap<K,T,L,H>::InsertUnconstructed( KeyType_universal_ref &&key, int *piNodeExistingIfDupe, bool bAllowDupes )
431 {
432 	// make sure we have room in the hash table
433 	if ( m_cElements >= m_vecHashBuckets.Count() )
434 		EnsureCapacity( MAX( 16, m_vecHashBuckets.Count() * 2 ) );
435 	if ( m_cElements >= m_memNodes.Count() )
436 		m_memNodes.Grow( m_memNodes.Count() * 2 );
437 
438 	// Do a bit of cleanup, if table is not already clean.  If statement here
439 	// avoids the function call in the (hopefully common!) case that the table
440 	// is already clean
441 	if ( m_nNeedRehashStart < m_nNeedRehashEnd )
442 		IncrementalRehash();
443 
444 	// hash the item
445 	int hash = (int)m_HashFunc( key );
446 
447 	// Make sure any buckets that might contain duplicates have been rehashed, so that we only need
448 	// to check one bucket below.  Also, we have the invariant that all duplicates (if they are allowed)
449 	// are in the same bucket.  This rehashing might not actually be necessary, because we might have
450 	// already done it.  But it's probably not worth keeping track of.  1.) The number of back probes
451 	// in normal usage is at most 1.  2.) If hashing is reasonably effective, then the number of items
452 	// in each bucket should be small.
453 	int nBucketMaskMigrate = ( m_vecHashBuckets.Count() >> 1 ) - 1;
454 	while ( nBucketMaskMigrate >= m_nMinBucketMask )
455 	{
456 		int iBucketMigrate = hash & nBucketMaskMigrate;
457 		if ( iBucketMigrate < m_nNeedRehashStart )
458 			break;
459 		RehashNodesInBucket( iBucketMigrate );
460 		nBucketMaskMigrate >>= 1;
461 	}
462 
463 	int iBucket = hash & ( m_vecHashBuckets.Count()-1 );
464 
465 	// return existing node without insert, if duplicates are not permitted
466 	if ( !bAllowDupes )
467 	{
468 		// look in the bucket to see if we have a conflict
469 		IndexType_t iNode = FindInBucket( iBucket, key );
470 		if ( iNode != kInvalidIndex )
471 		{
472 			if ( piNodeExistingIfDupe )
473 			{
474 				*piNodeExistingIfDupe = iNode;
475 			}
476 			return kInvalidIndex;
477 		}
478 	}
479 
480 	// make an item
481 	int iNewNode = AllocNode();
482 	m_memNodes[iNewNode].m_iNextNode = kInvalidIndex;
483 	Construct( &m_memNodes[iNewNode].m_key, std::forward<KeyType_universal_ref>( key ) );
484 	// Note: m_elem remains intentionally unconstructed here
485 	// Note: key may have been moved depending on which constructor was called.
486 
487 	// link ourselves in
488 	//	::OutputDebugStr( CFmtStr( "insert %d into bucket %d\n", key, iBucket ).Access() );
489 	LinkNodeIntoBucket( iBucket, iNewNode );
490 
491     // Initialized to placate the compiler's uninitialized value checking.
492     if ( piNodeExistingIfDupe )
493     {
494         *piNodeExistingIfDupe = kInvalidIndex;
495     }
496 
497 	// return the new node
498 	return iNewNode;
499 }
500 
501 
502 //-----------------------------------------------------------------------------
503 // Purpose: inserts a default item into the map, no change if key already exists
504 //-----------------------------------------------------------------------------
505 template <typename K, typename T, typename L, typename H>
506 template < typename pf_key >
FindOrInsert_Internal(pf_key && key,ReplaceExisting bReplace)507 inline int CUtlHashMap<K,T,L,H>::FindOrInsert_Internal( pf_key &&key, ReplaceExisting bReplace )
508 {
509 	int iNodeExisting;
510 	int iNodeInserted = InsertUnconstructed( std::forward<pf_key>( key ), &iNodeExisting, false /*no duplicates allowed*/ );
511 	// If replacing, stomp the existing one
512 	if ( bReplace && iNodeExisting != kInvalidIndex )
513 	{
514 		Destruct( &m_memNodes[ iNodeExisting ].m_elem );
515 		ValueInitializeConstruct( &m_memNodes[ iNodeExisting ].m_elem );
516 	}
517 	else if ( iNodeInserted != kInvalidIndex )
518 	{
519 		ValueInitializeConstruct( &m_memNodes[ iNodeInserted ].m_elem );
520 		return iNodeInserted;
521 	}
522 	return iNodeExisting;
523 }
524 
525 
526 //-----------------------------------------------------------------------------
527 // Purpose: inserts an item into the map, no change if key already exists
528 //-----------------------------------------------------------------------------
529 template <typename K, typename T, typename L, typename H>
530 template < typename pf_key, typename pf_elem >
FindOrInsert_Internal(pf_key && key,pf_elem && elem,ReplaceExisting bReplace)531 inline int CUtlHashMap<K,T,L,H>::FindOrInsert_Internal( pf_key &&key, pf_elem &&elem, ReplaceExisting bReplace )
532 {
533 	int iNodeExisting;
534 	int iNodeInserted = InsertUnconstructed( std::forward<pf_key>( key ), &iNodeExisting, false /*no duplicates allowed*/ );
535 	// If replacing, stomp the existing one
536 	if ( bReplace && iNodeExisting != kInvalidIndex )
537 	{
538 		Destruct( &m_memNodes[ iNodeExisting ].m_elem );
539 		Construct( &m_memNodes[ iNodeExisting ].m_elem, std::forward<pf_elem>( elem ) );
540 	}
541 	else if ( iNodeInserted != kInvalidIndex )
542 	{
543 		Construct( &m_memNodes[ iNodeInserted ].m_elem, std::forward<pf_elem>( elem ) );
544 		return iNodeInserted;
545 	}
546 	return iNodeExisting;
547 }
548 
549 //-----------------------------------------------------------------------------
550 // Purpose: inserts element no matter what, even if key already exists
551 //-----------------------------------------------------------------------------
552 template <typename K, typename T, typename L, typename H>
553 template < typename pf_key, typename pf_elem >
InsertWithDupes_Internal(pf_key && key,pf_elem && insert)554 inline int CUtlHashMap<K,T,L,H>::InsertWithDupes_Internal( pf_key &&key, pf_elem &&insert )
555 {
556 	int iNodeInserted = InsertUnconstructed( std::forward<pf_key>( key ), NULL, true /*duplicates allowed!*/ ); // copies key
557 	if ( iNodeInserted != kInvalidIndex )
558 	{
559 		Construct( &m_memNodes[ iNodeInserted ].m_elem, std::forward<pf_elem>( insert ) );
560 	}
561 	return iNodeInserted;
562 }
563 
564 
565 //-----------------------------------------------------------------------------
566 // Purpose: grows the map to fit the specified amount
567 //-----------------------------------------------------------------------------
568 template <typename K, typename T, typename L, typename H>
EnsureCapacity(int amount)569 inline void CUtlHashMap<K,T,L,H>::EnsureCapacity( int amount )
570 {
571 	m_memNodes.EnsureCapacity( amount );
572 	// ::OutputDebugStr( CFmtStr( "grown m_memNodes from %d to %d\n", m_cElements, m_memNodes.Count() ).Access() );
573 
574 	if ( amount <= m_vecHashBuckets.Count() )
575 		return;
576 	int cBucketsNeeded = MAX( 16, m_vecHashBuckets.Count() );
577 	while ( cBucketsNeeded < amount )
578 		cBucketsNeeded <<= 1;
579 	DbgAssert( ( cBucketsNeeded & (cBucketsNeeded-1) ) == 0 ); // It's a power of 2
580 
581 	// ::OutputDebugStr( CFmtStr( "grown m_vecHashBuckets from %d to %d\n", m_vecHashBuckets.Count(), cBucketsNeeded ).Access() );
582 
583 	// grow the hash buckets
584 	int grow = cBucketsNeeded - m_vecHashBuckets.Count();
585 	int iFirst = m_vecHashBuckets.AddMultipleToTail( grow );
586 	// clear all the new data to invalid bits
587 	memset( &m_vecHashBuckets[iFirst], 0xFFFFFFFF, grow*sizeof(m_vecHashBuckets[iFirst]) );
588 	DbgAssert( m_vecHashBuckets.Count() == cBucketsNeeded );
589 
590 	// Mark appropriate range for rehashing
591 	if ( m_cElements > 0 )
592 	{
593 		// we'll have to rehash, all the buckets that existed before growth
594 		m_nNeedRehashStart = 0;
595 		m_nNeedRehashEnd = iFirst;
596 	}
597 	else
598 	{
599 		// no elements - no rehashing!
600 		m_nNeedRehashStart = m_vecHashBuckets.Count();
601 		m_nNeedRehashEnd = m_nNeedRehashStart;
602 		m_nMinBucketMask = m_nNeedRehashStart-1;
603 	}
604 }
605 
606 
607 //-----------------------------------------------------------------------------
608 // Purpose: gets a new node, from the free list if possible
609 //-----------------------------------------------------------------------------
610 template <typename K, typename T, typename L, typename H>
AllocNode()611 inline int CUtlHashMap<K,T,L,H>::AllocNode()
612 {
613 	// if we're out of free elements, get the max
614 	if ( m_cElements == m_nMaxElement )
615 	{
616 		m_cElements++;
617 		return m_nMaxElement++;
618 	}
619 
620 	// pull from the free list
621 	DbgAssert( m_iNodeFreeListHead != kInvalidIndex );
622 	int iNewNode = m_iNodeFreeListHead;
623 	m_iNodeFreeListHead = FreeNodeIDToIndex( m_memNodes[iNewNode].m_iNextNode );
624 	m_cElements++;
625 	return iNewNode;
626 }
627 
628 
629 //-----------------------------------------------------------------------------
630 // Purpose: takes a bucket of nodes and re-hashes them into a more optimal bucket
631 //-----------------------------------------------------------------------------
632 template <typename K, typename T, typename L, typename H>
RehashNodesInBucket(int iBucketSrc)633 inline void CUtlHashMap<K,T,L,H>::RehashNodesInBucket( int iBucketSrc )
634 {
635 
636 	// walk the list of items, re-hashing them
637 	IndexType_t *pLink = &m_vecHashBuckets[iBucketSrc].m_iNode;
638 	for (;;)
639 	{
640 		IndexType_t iNode = *pLink;
641 		if ( iNode == kInvalidIndex )
642 			break;
643 		Node_t &node = m_memNodes[iNode];
644 		DbgAssert( node.m_iNextNode != iNode );
645 
646 		// work out where the node should go
647 		int hash = (int)m_HashFunc( node.m_key );
648 		int iBucketDest = hash & (m_vecHashBuckets.Count()-1);
649 
650 		// if the hash bucket has changed, move it
651 		if ( iBucketDest != iBucketSrc )
652 		{
653 			//	::OutputDebugStr( CFmtStr( "moved key %d from bucket %d to %d\n", key, iBucketSrc, iBucketDest ).Access() );
654 
655 			// remove from this bucket list
656 			*pLink = node.m_iNextNode;
657 
658 			// link into new bucket list
659 			LinkNodeIntoBucket( iBucketDest, iNode );
660 		}
661 		else
662 		{
663 			pLink = &node.m_iNextNode;
664 		}
665 	}
666 }
667 
668 
669 //-----------------------------------------------------------------------------
670 // Purpose: searches for an item by key, returning the index handle
671 //-----------------------------------------------------------------------------
672 template <typename K, typename T, typename L, typename H>
Find(const KeyType_t & key)673 inline int CUtlHashMap<K,T,L,H>::Find( const KeyType_t &key ) const
674 {
675 	if ( m_cElements == 0 )
676 		return kInvalidIndex;
677 
678 	// Rehash incrementally.  Note that this is really a "const"
679 	// function, since it is only shuffling around the buckets.  The
680 	// items do not move in memory, and their index does not change.
681 	// So this can be called during iteration, etc.  The buckets are
682 	// invisible to the app code.
683 	//
684 	// It's a better tradeoff to make this function slightly slower
685 	// until we get rehashed, to make sure that we do eventually get
686 	// rehashed, even if we have stopped Inserting.  This minimizes
687 	// the number of back probes that need to be made.
688 	//
689 	// NOTE: This means that you cannot call the "read-only" Find()
690 	// function from different threads at the same time!
691 	if ( m_nNeedRehashStart < m_nNeedRehashEnd )
692 		(const_cast<CUtlHashMap<K,T,L,H> *>( this ))->IncrementalRehash();
693 
694 	// hash the item
695 	int hash = (int)m_HashFunc( key );
696 
697 	// find the bucket
698 	int cBucketsMask = m_vecHashBuckets.Count()-1;
699 	int iBucket = hash & cBucketsMask;
700 	do
701 	{
702 
703 		// Look in the bucket for the item
704 		int iNode = FindInBucket( iBucket, key );
705 		if ( iNode != kInvalidIndex )
706 			return iNode;
707 
708 		// Not found.  Might be in an older bucket.
709 		cBucketsMask >>= 1;
710 		if ( cBucketsMask < m_nMinBucketMask )
711 			break;
712 		iBucket = hash & cBucketsMask;
713 	} while ( iBucket >= m_nNeedRehashStart );
714 
715 	return kInvalidIndex;
716 }
717 
718 
719 //-----------------------------------------------------------------------------
720 // Purpose: searches for an item by key and element equality, returning the index handle
721 //-----------------------------------------------------------------------------
722 template <typename K, typename T, typename L, typename H>
FindExact(const KeyType_t & key,const ElemType_t & elem)723 inline int CUtlHashMap<K, T, L, H>::FindExact( const KeyType_t &key, const ElemType_t &elem ) const
724 {
725 	int iNode = Find( key );
726 	while ( iNode != kInvalidIndex )
727 	{
728 		if ( elem == m_memNodes[iNode].m_elem )
729 			return iNode;
730 		iNode = NextSameKey( iNode );
731 	}
732 	return kInvalidIndex;
733 }
734 
735 
736 //-----------------------------------------------------------------------------
737 // Purpose: find the next element with the same key, if insertwithdupes was used
738 //-----------------------------------------------------------------------------
739 template <typename K, typename T, typename L, typename H>
NextSameKey(IndexType_t i)740 inline int CUtlHashMap<K, T, L, H>::NextSameKey( IndexType_t i ) const
741 {
742 	if ( m_memNodes.IsIdxValid( i ) )
743 	{
744 		const KeyType_t &key = m_memNodes[i].m_key;
745 		IndexType_t iNode = m_memNodes[i].m_iNextNode;
746 		while ( iNode != kInvalidIndex )
747 		{
748 			DbgAssert( iNode < m_nMaxElement );
749 
750 			// equality check
751 			if ( m_EqualityFunc( key, m_memNodes[iNode].m_key ) )
752 				return iNode;
753 
754 			iNode = m_memNodes[iNode].m_iNextNode;
755 		}
756 	}
757 	return kInvalidIndex;
758 }
759 
760 
761 //-----------------------------------------------------------------------------
762 // Purpose: searches for an item by key, returning the index handle
763 //-----------------------------------------------------------------------------
764 template <typename K, typename T, typename L, typename H>
FindInBucket(int iBucket,const KeyType_t & key)765 inline int CUtlHashMap<K,T,L,H>::FindInBucket( int iBucket, const KeyType_t &key ) const
766 {
767 	IndexType_t iNode = m_vecHashBuckets[iBucket].m_iNode;
768 	while ( iNode != kInvalidIndex )
769 	{
770 		DbgAssert( iNode < m_nMaxElement );
771 
772 		// equality check
773 		const Node_t &node = m_memNodes[iNode];
774 		if ( m_EqualityFunc( key, node.m_key ) )
775 			return iNode;
776 
777 		iNode = node.m_iNextNode;
778 	}
779 
780 	return kInvalidIndex;
781 }
782 
783 
784 //-----------------------------------------------------------------------------
785 // Purpose: links a node into a bucket
786 //-----------------------------------------------------------------------------
787 template <typename K, typename T, typename L, typename H>
LinkNodeIntoBucket(int iBucket,int iNewNode)788 inline void CUtlHashMap<K,T,L,H>::LinkNodeIntoBucket( int iBucket, int iNewNode )
789 {
790 	// add into the start of the bucket's list
791 	m_memNodes[iNewNode].m_iNextNode = m_vecHashBuckets[iBucket].m_iNode;
792 	m_vecHashBuckets[iBucket].m_iNode = iNewNode;
793 }
794 
795 
796 //-----------------------------------------------------------------------------
797 // Purpose: removes a single item from the map
798 //-----------------------------------------------------------------------------
799 template <typename K, typename T, typename L, typename H>
RemoveAt(IndexType_t i)800 inline void CUtlHashMap<K,T,L,H>::RemoveAt( IndexType_t i )
801 {
802 	if ( !IsValidIndex( i ) )
803 	{
804 		Assert( false );
805 		return;
806 	}
807 
808 	// Rehash incrementally
809 	if ( m_nNeedRehashStart < m_nNeedRehashEnd )
810 		IncrementalRehash();
811 
812 	// unfortunately, we have to re-hash to find which bucket we're in
813 	int hash = (int)m_HashFunc( m_memNodes[i].m_key );
814 	int nBucketMask = m_vecHashBuckets.Count()-1;
815 	if ( RemoveNodeFromBucket( hash & nBucketMask, i ) )
816 		return;
817 
818 	// wasn't found; look in older buckets
819 	for (;;)
820 	{
821 		nBucketMask >>= 1;
822 		if ( nBucketMask < m_nMinBucketMask )
823 			break;
824 		int iBucket = hash & nBucketMask;
825 		if ( iBucket < m_nNeedRehashStart )
826 			break;
827 		if ( RemoveNodeFromBucket( iBucket, i ) )
828 			return;
829 	}
830 
831 	// never found, container is busted
832 	Assert( false );
833 }
834 
835 
836 //-----------------------------------------------------------------------------
837 // Purpose: removes a node from the bucket, return true if it was found
838 //-----------------------------------------------------------------------------
839 template <typename K, typename T, typename L, typename H>
RemoveNodeFromBucket(IndexType_t iBucket,int iNodeToRemove)840 inline bool CUtlHashMap<K,T,L,H>::RemoveNodeFromBucket( IndexType_t iBucket, int iNodeToRemove )
841 {
842 	// walk the list of items
843 	IndexType_t *pLink = &m_vecHashBuckets[iBucket].m_iNode;
844 	for (;;)
845 	{
846 		IndexType_t iNode = *pLink;
847 		if ( iNode == kInvalidIndex )
848 			break;
849 		Node_t &node = m_memNodes[iNode];
850 		DbgAssert( node.m_iNextNode != iNode );
851 
852 		if ( iNodeToRemove == iNode )
853 		{
854 			// found it, remove
855 			*pLink = node.m_iNextNode;
856 			Destruct( &node.m_key );
857 			Destruct( &node.m_elem );
858 
859 			// link into free list
860 			node.m_iNextNode = FreeNodeIndexToID( m_iNodeFreeListHead );
861 			m_iNodeFreeListHead = iNode;
862 			m_cElements--;
863 			if ( m_cElements == 0 )
864 			{
865 				// No items left in container, so no rehashing necessary
866 				m_nNeedRehashStart = m_vecHashBuckets.Count();
867 				m_nNeedRehashEnd = m_nNeedRehashStart;
868 				m_nMinBucketMask = m_vecHashBuckets.Count()-1;
869 			}
870 			return true;
871 		}
872 
873 		pLink = &node.m_iNextNode;
874 	}
875 
876 	return false;
877 }
878 
879 
880 //-----------------------------------------------------------------------------
881 // Purpose: removes all items from the hash map
882 //-----------------------------------------------------------------------------
883 template <typename K, typename T, typename L, typename H>
RemoveAll()884 inline void CUtlHashMap<K,T,L,H>::RemoveAll()
885 {
886 	if ( m_cElements > 0 )
887 	{
888 		FOR_EACH_HASHMAP( *this, i )
889 		{
890 			Node_t &node = m_memNodes[i];
891 			Destruct( &node.m_key );
892 			Destruct( &node.m_elem );
893 		}
894 
895 		m_cElements = 0;
896 		m_nMaxElement = 0;
897 		m_iNodeFreeListHead = kInvalidIndex;
898 		m_nNeedRehashStart = m_vecHashBuckets.Count();
899 		m_nNeedRehashEnd = m_nNeedRehashStart;
900 		DbgAssert( m_vecHashBuckets.Count() >= 2 );
901 		m_nMinBucketMask = m_vecHashBuckets.Count()-1;
902 		memset( m_vecHashBuckets.Base(), 0xFF, m_vecHashBuckets.Count() * sizeof(HashBucket_t) );
903 	}
904 }
905 
906 
907 //-----------------------------------------------------------------------------
908 // Purpose: removes all items from the hash map and frees all memory
909 //-----------------------------------------------------------------------------
910 template <typename K, typename T, typename L, typename H>
Purge()911 inline void CUtlHashMap<K,T,L,H>::Purge()
912 {
913 	if ( m_cElements > 0 )
914 	{
915 		FOR_EACH_HASHMAP( *this, i )
916 		{
917 			Node_t &node = m_memNodes[i];
918 			Destruct( &node.m_key );
919 			Destruct( &node.m_elem );
920 		}
921 	}
922 
923 	m_cElements = 0;
924 	m_nMaxElement = 0;
925 	m_iNodeFreeListHead = kInvalidIndex;
926 	m_nNeedRehashStart = 0;
927 	m_nNeedRehashEnd = 0;
928 	m_nMinBucketMask = 1;
929 	m_vecHashBuckets.Purge();
930 	m_memNodes.Purge();
931 }
932 
933 
934 //-----------------------------------------------------------------------------
935 // Purpose: rehashes buckets
936 //-----------------------------------------------------------------------------
937 template <typename K, typename T, typename L, typename H>
IncrementalRehash()938 inline void CUtlHashMap<K,T,L,H>::IncrementalRehash()
939 {
940 	// Each call site should check this, to avoid the function call in the
941 	// common case where the table is already clean.
942 	DbgAssert( m_nNeedRehashStart < m_nNeedRehashEnd );
943 
944 	do
945 	{
946 		int iBucketSrc = m_nNeedRehashStart;
947 		++m_nNeedRehashStart;
948 
949 		// Bucket empty?
950 		if ( m_vecHashBuckets[iBucketSrc].m_iNode != kInvalidIndex )
951 		{
952 			RehashNodesInBucket( iBucketSrc );
953 
954 			// only actively do one - don't want to do it too fast since we may be on a rapid growth path
955 			if ( m_nNeedRehashStart < m_nNeedRehashEnd )
956 				return;
957 			break;
958 		}
959 	} while ( m_nNeedRehashStart < m_nNeedRehashEnd );
960 
961 	// We're done; don't need any bits anymore
962 	DbgAssert( m_vecHashBuckets.Count() >= 2 );
963 	m_nNeedRehashStart = m_vecHashBuckets.Count();
964 	m_nNeedRehashEnd = m_nNeedRehashStart;
965 	m_nMinBucketMask = m_vecHashBuckets.Count()-1;
966 }
967 
968 
969 //-----------------------------------------------------------------------------
970 // Purpose: swaps with another hash map
971 //-----------------------------------------------------------------------------
972 template <typename K, typename T, typename L, typename H>
Swap(CUtlHashMap<K,T,L,H> & that)973 inline void CUtlHashMap<K,T,L,H>::Swap( CUtlHashMap<K,T,L,H> &that )
974 {
975 	m_vecHashBuckets.Swap( that.m_vecHashBuckets );
976 	m_memNodes.Swap( that.m_memNodes );
977 	SWAP( m_iNodeFreeListHead, that.m_iNodeFreeListHead );
978 	SWAP( m_cElements, that.m_cElements );
979 	SWAP( m_nMaxElement, that.m_nMaxElement );
980 	SWAP( m_nNeedRehashStart, that.m_nNeedRehashStart );
981 	SWAP( m_nNeedRehashEnd, that.m_nNeedRehashEnd );
982 	SWAP( m_nMinBucketMask, that.m_nMinBucketMask );
983 }
984 
985 #endif // UTLHASHMAP_H
986