Polygon Crucher SDK - Documentation
Documentation
Loading...
Searching...
No Matches
HashMap.h
Go to the documentation of this file.
1//! @file HashMap.h
2//! @brief Associates key to a single value through an hash table.
3//!
4// Associate a key to a single value
5//
6// KEY: must have zero-parameter and copy constructor,
7// operator= and operator!=
8// unsigned int Hash(const KEY & Key, int TableSize)
9// must be defined
10// CONSTRUCTION: with (a) no initializer;
11// Copy constructon of CHashMap objects is DISALLOWED
12// Deep copy is supported
13//
14// ******************PUBLIC OPERATIONS*********************
15// int Insert(KEY X) --> Insert X
16// int Remove(KEY X) --> Remove X
17// KEY Find(KEY X) --> Return item that matches X
18// int IsFound(KEY X) --> Return 1 if X would be found
19// int IsEmpty() --> Return 1 if empty; else return 0
20// void Clear() --> Remove all items
21// ******************ERRORS********************************
22// Predefined exception is propogated if new fails
23
24#ifndef __CHASHMAP_H
25#define __CHASHMAP_H
26
27#include "Hash.h"
28
29BEGIN_MOOTOOLS_NAMESPACE
30
31
32 #if defined(__WINDOWS__) && (_MSC_VER <= 1500)
33 // Const qualifier warning on VS2008 for ARG_KEY
34 #pragma warning(push)
35 #pragma warning(disable : 4181)
36 #endif
37
38 //! @class CHashMap
39 //! @brief CHashMap is a template class that associates key to a single value through an hash table.
40 template<class KEY, class ARG_KEY, class VALUE, typename HashFunctions = CHashMethods<KEY> > // Keep space between > > for Linux C98
42 {
43 public:
44 enum KindOfEntry {
45 Active, Empty, Deleted
46 };
47
48 CHashMap(unsigned int defaultSize = DefaultHashSize);
49 CHashMap(const CHashMap& Rhs);
50
51 virtual ~CHashMap()
52 {
53 #ifdef MOOTOOLS_PRIVATE_DEBUG
54 if (IsTraceEnabled())
55 MOOTOOLS_PRIVATE_TRACE(traceStruct, 1, "HashMap ratio %f\n", ActiveSize/(float)ArraySize);
56 #endif
57 xDeleteArray(Array, ArraySize);
58 }
59
60 const CHashMap & operator=(const CHashMap & Rhs);
61
62 int Insert2(const ARG_KEY K, const VALUE V); // Same has insert except that value is not a reference (we can pass an int for example)
63 int Insert(const ARG_KEY K, const VALUE& V);
64 int Remove(const ARG_KEY K, bool enableResize = false); // Return 1 if found, 0 if not. enableResize must be false if removal is done in a GetFirst/GetNext loop
65 int Find(const ARG_KEY K, VALUE& V) const; // Return item in table. This return a copy and can be slower than the version below which get a pointer
66 int Find(const ARG_KEY K, VALUE *&V) const; // Return item in table
67 VALUE *Find(const ARG_KEY K) const; // Return item ptr in table, NULL if not found
68 int IsFound(const ARG_KEY K) const; // Return 1 if found
69 int IsEmpty() const;
70 void Free();
71 void Clear();
72 unsigned int GetCount() const; // Return the element number
73 HashPos GetFirst() const;
74 void GetNext(HashPos& pos , KEY& element, VALUE& value) const;
75 void GetNext(HashPos& pos, KEY& element, VALUE *& value) const;
76 KEY& GetNext(HashPos& pos , VALUE& value) const;
77 void GetNext(HashPos& pos , KEY *element, VALUE *value) const; // Return next item in map with corresponding value. This returns a copy and can be slower than the version below which get a pointer
78 KEY *GetNext(HashPos& pos , VALUE *&value) const;
79 void InitHashTable(unsigned int elementsNumber);
80 unsigned int GetHashTableSize() const;
81 bool Minimize();
82
83 void Merge(const CHashMap& Rhs);
84 void Remove(const CHashMap& Rhs);
85
86 #ifdef MOOTOOLS_PRIVATE_DEBUG
87 bool IsTraceEnabled() const { return traceEnabled; }
88 void EnableTrace(bool show) { traceEnabled = show; }
89 #endif
90
91 private:
92
93 #ifdef _DEBUG
94 mutable bool checkResize; // Check that we not perform resize in GetFirst/GetNext
95 #endif
96
97 #ifdef MOOTOOLS_PRIVATE_DEBUG
98 unsigned int CheckCount() const; // Return the element number
99
100 mutable bool traceEnabled;
101 mutable bool checkRehash; // Do not re-rehash
102 #endif
103
104 HashPos FindActiveElement(HashPos pos) const;
105
106 // 05/2018 : Remove HashEntry(const Etype & E, KindOfEntry i = Empty) : Element(E), Info(i) constructor
107 // It can be potentially dangerous if Etype is a class, as Element(E) requires a Etype::Etype(const EType& E) constructor which might be missing (in such case, the compile uses Etype::Etype() without warning, probably because HashEntry is a struct)
108 // Additionally using this method make hash slower.
109 struct HashEntry
110 {
111 KEY Key; // The key
112 VALUE Value;
113 KindOfEntry Info; // Active, empty, or deleted
114
115 inline HashEntry() : Info(Empty) {}
116 };
117
118 unsigned int DefaultSize;
119 unsigned int ArraySize; // The size of this array
120 unsigned int CurrentSize; // The number of non-empty items
121 unsigned int ActiveSize; // The number of active items
122 HashEntry *Array; // The array of elements
123
124 // Some internal routines
125 void AllocateArray();
126 bool ReHash(bool minimize);
127 unsigned int FindPos(const ARG_KEY K) const;
128 };
129
130 // Friends functions
131 template<class KEY, class ARG_KEY, class VALUE, typename HashFunctions> bool IsSameHashMap(const CHashMap<KEY, ARG_KEY, VALUE, HashFunctions> & Rhs1, const CHashMap<KEY, ARG_KEY, VALUE, HashFunctions> & Rhs2);
132
133 template<class KEY, class ARG_KEY, class VALUE, typename HashFunctions>
135 {
136 return ArraySize;
137 }
138
139 // Allocate the hash table array
140 template<class KEY, class ARG_KEY, class VALUE, typename HashFunctions>
142 {
143 Array = xNewArray(HashEntry, ArraySize);
144 }
145
146 // CHashMap constructor
147
148 template<class KEY, class ARG_KEY, class VALUE, typename HashFunctions>
150 {
151 #ifdef MOOTOOLS_PRIVATE_DEBUG
152 traceEnabled = false;
153 checkRehash = false;
154 #endif
155
156 Array = NULL;
157 ArraySize = 0;
158 ActiveSize = CurrentSize = 0;
159 InitHashTable(defaultSize);
160
161 #ifdef _DEBUG
162 checkResize = false;
163 #endif
164 }
165
166
167 template<class KEY, class ARG_KEY, class VALUE, typename HashFunctions>
169 {
170#ifdef MOOTOOLS_PRIVATE_DEBUG
171 traceEnabled = false;
172#endif
173
174#ifdef _DEBUG
175 checkResize = false;
176#endif
177
178 DefaultSize = Rhs.DefaultSize;
179 ArraySize = 0;
180 Array = NULL;
181 ActiveSize = CurrentSize = 0;
182
183 *this = Rhs;
184 }
185
186 // Set a default starting size
187 template<class KEY, class ARG_KEY, class VALUE, typename HashFunctions>
189 {
190 if (Array != NULL) // Already allocated
191 {
192 elementsNumber = (elementsNumber+1)*2; // 06/19: +1 avoid some rehash when we specify exactly the number of the element of the map
193 elementsNumber = NextPrime(elementsNumber);
194 elementsNumber = (elementsNumber < MinimalHashSize) ? MinimalHashSize : elementsNumber; // If InitHashTable is called, elementNumber could be the real element number. Don't waste memory with extra space
195
196 if (DefaultSize == elementsNumber)
197 return;
198 }
199 else // Constructor only
200 {
201 if (elementsNumber != DefaultHashSize)
202 {
203 elementsNumber = NextPrime(elementsNumber);
204 elementsNumber = (elementsNumber < MinimalHashSize) ? MinimalHashSize : elementsNumber; // If InitHashTable is called, elementNumber could be the real element number. Don't waste memory with extra space
205 }
206 }
207
208 XASSERT(ActiveSize == 0 && CurrentSize == 0);
209 xDeleteArray(Array, ArraySize);
210
211 DefaultSize = elementsNumber;
212 ArraySize = elementsNumber;
213 AllocateArray();
214 ActiveSize = CurrentSize = 0;
215 }
216
217
218 // Set back the hash table to its minimal size
219 template<class KEY, class ARG_KEY, class VALUE, typename HashFunctions>
221 {
222 #ifdef MOOTOOLS_PRIVATE_DEBUG
223 if (IsTraceEnabled())
224 MOOTOOLS_PRIVATE_TRACE(traceStruct, 1, "HashMap ratio %f\n", ActiveSize/(float)ArraySize);
225 #endif
226
227 xDeleteArray(Array, ArraySize);
228
229 ArraySize = DefaultSize = DefaultHashSize;
230 ActiveSize = CurrentSize = 0;
231 AllocateArray();
232 }
233
234 // Clear the hash table, but keep memory allocated
235 template<class KEY, class ARG_KEY, class VALUE, typename HashFunctions>
237 {
238 #ifdef MOOTOOLS_PRIVATE_DEBUG
239 if (IsTraceEnabled())
240 MOOTOOLS_PRIVATE_TRACE(traceStruct, 1, "HashMap ratio %f\n", ActiveSize/(float)ArraySize);
241 #endif
242
243 ActiveSize = CurrentSize = 0;
244 for (unsigned int i = 0; i < ArraySize; i++)
245 Array[i].Info = Empty;
246 }
247
248 // Return item in hash table that matches X
249 template<class KEY, class ARG_KEY, class VALUE, typename HashFunctions>
251 {
252 unsigned int CurrentPos = FindPos(X);
253 if (Array[CurrentPos].Info == Active)
254 {
255 V = Array[CurrentPos].Value;
256 return true;
257 }
258
259 return false;
260 }
261
262 // Return item in hash table that matches X
263 template<class KEY, class ARG_KEY, class VALUE, typename HashFunctions>
265 {
266 unsigned int CurrentPos = FindPos(X);
267 if (Array[CurrentPos].Info == Active)
268 {
269 V = &Array[CurrentPos].Value;
270 return true;
271 }
272
273 return false;
274 }
275
276 // Return item ptr in hash table that matches X, allowing direct modification
277 template<class KEY, class ARG_KEY, class VALUE, typename HashFunctions>
279 {
280 unsigned int CurrentPos = FindPos(X);
281 if (Array[CurrentPos].Info == Active)
282 return &Array[CurrentPos].Value;
283
284 return NULL;
285 }
286
287 // Return true if hash could be reduced, false otherwise
288 template<class KEY, class ARG_KEY, class VALUE, typename HashFunctions>
290 {
291 return ReHash(true); // ReHash does the job only if it is needed
292 }
293
294 template<class KEY, class ARG_KEY, class VALUE, typename HashFunctions>
296 {
297 HashPos pos = hash.GetFirst();
298 VALUE *value;
299 while (pos != HashEnd)
300 {
301 KEY *key = hash.GetNext(pos, value);
302 Remove(*key);
303 }
304 }
305
306 template<class KEY, class ARG_KEY, class VALUE, typename HashFunctions>
308 {
309 HashPos pos = hash.GetFirst();
310 VALUE *value;
311 while (pos != HashEnd)
312 {
313 KEY *key = hash.GetNext(pos, value);
314 Insert(*key, *value);
315 }
316 }
317
318 // Routine to resolve collisions and locate
319 // cell that must contain X if X is found
320 // 23/01/14: update FindPos. Cf. CHashTable::FindPos note
321 // 10/10/17: minor optimization
322 template<class KEY, class ARG_KEY, class VALUE, typename HashFunctions>
323 inline unsigned int CHashMap<KEY, ARG_KEY, VALUE, HashFunctions>::FindPos(const ARG_KEY X) const
324 {
325 unsigned int i = 0; // The number of failed probes
326 unsigned int CurrentPos = HashFunctions::HashValue(X) % ArraySize;
327 unsigned int DeletedPos = (-1);
328
329 while (Array[CurrentPos].Info != Empty)
330 {
331 if (Array[CurrentPos].Info == Active)
332 {
333 if (HashFunctions::HashCompare(Array[CurrentPos].Key, X))
334 return CurrentPos;
335 }
336 else if (DeletedPos == (-1))
337 {
338 XASSERT(Array[CurrentPos].Info == Deleted);
340 }
341
342 CurrentPos += 2*++i - 1; // Compute ith probe
343 XASSERT(CurrentPos < ArraySize || ((CurrentPos - ArraySize) < ArraySize));
344 if (CurrentPos >= ArraySize) // Implement the mod
345 CurrentPos -= ArraySize;
346 }
347
348 // Return the first deleted entry if we found one
349 // If no active element was found
350 XASSERT(Array[CurrentPos].Info == Empty);
351 return DeletedPos != (-1) ? DeletedPos : CurrentPos;
352 }
353
354 // Test if X is in the hash table
355 template<class KEY, class ARG_KEY, class VALUE, typename HashFunctions>
357 {
358 unsigned int CurrentPos = FindPos(X);
359 return (Array[CurrentPos].Info == Active);
360 }
361
362 // Remove X from hash table
363 // Return true if X was removed; false otherwise
364
365 template<class KEY, class ARG_KEY, class VALUE, typename HashFunctions>
366 int
368 {
369 unsigned int CurrentPos = FindPos(X);
370 if (Array[CurrentPos].Info != Active)
371 return 0;
372
373 XASSERT(ActiveSize >= 0);
374 Array[CurrentPos].Info = Deleted;
375 ActiveSize--;
376
377 // Should not resize in the GetFirst/GetNext loop
378 if (enableResize && (ActiveSize > DefaultHashSize)) // Avoid constant rehash for small bucket. Rehashing is done on insertion / minimize
379 ReHash(false);
380
381 return 1;
382 }
383
384 template<class KEY, class ARG_KEY, class VALUE, typename HashFunctions>
385 int
387 {
388 VALUE tmp = V;
389 return Insert(X, tmp);
390 }
391
392
393 template<class KEY, class ARG_KEY, class VALUE, typename HashFunctions>
395 {
396#ifdef _DEBUG
397 checkResize = true; // Check we are not in a GetFirst/GetNext loop. Rehashing is not supported when traversing the hash. Remove operation can be performed (which enableResize = false), but insertion is forbidden
398#endif
399
400 MOOTOOLS_PRIVATE_ASSERT(!checkRehash);
401
402 // ReHash can be called for 2 different purposes : removing deleted elements when Insert-Remove occurs or minimize the size of the hash once it has been built
403 unsigned int newSize = 0;
404 if (minimize)
405 {
406 newSize = 2*(ActiveSize+1) < MinimalHashSize ? MinimalHashSize : 2*(ActiveSize+1); // +1 avoid some rehash, if NextPrime = 2*ActiveSize+1. Use MinimalHashSize, if we have few elements, don't waste space
407 newSize = NextPrime(newSize);
408 if (newSize == ArraySize)
409 {
410 MOOTOOLS_PRIVATE_ASSERT(CurrentSize < ArraySize/2);
411 return false;
412 }
413 }
414 else
415 {
416 if (CurrentSize < ArraySize/2) // Array is large enough. Rehash call with no reason
417 return false;
418
419 float ratio = (ActiveSize / (float)CurrentSize);
420 if (ratio <= 0.65) // Array as t least 50% of empty element and 33% of deleted elements. Insert/Remove sequence called
421 {
422 newSize = 2*(ActiveSize+1); // This lead to downsize the array
423 // newSize is the exact size for containing ActiveSize elements
424 // newSize < 0.65*ArraySize and ActiveSize < 0.65*CurrentSize as CurrentSize < 2*ArraySize
425 //
426 // As we probably insert other element, give space to avoid a new immediate rehash after the next insert, but do not exceed ArraySize to lead to downsize
427 newSize = (unsigned int)(newSize/0.65); // Maximum value will be around current ArraySize.
428 if (newSize < DefaultHashSize)
429 newSize = DefaultHashSize;
430 }
431 else // Array is too small
432 newSize = ArraySize < 251 ? 4*ArraySize : 2*ArraySize; // Faster grow for small array
433
434 newSize = NextPrime(newSize);
435 // Must rehash as CurrentSize >= ArraySize/2!
436 }
437
438 // REHASHING CODE
439 // Save old table
440 HashEntry *OldArray = Array;
441 unsigned int OldArraySize = ArraySize;
442
443 ArraySize = newSize;
444 ActiveSize = CurrentSize = 0;
445 AllocateArray();
446
447 #ifdef MOOTOOLS_PRIVATE_DEBUG
448 checkRehash = true;
449 #endif
450
451 // Copy table over
452 for (unsigned int i = 0; i < OldArraySize; i++)
453 {
454 if (OldArray[i].Info == Active)
455 {
456 Insert(OldArray[i].Key, OldArray[i].Value);
457 }
458 }
459
460 #ifdef MOOTOOLS_PRIVATE_DEBUG
461 checkRehash = false;
462 #endif
463
464 // Recycle OldArray
465 xDeleteArray(OldArray, OldArraySize);
466
467 return true;
468 }
469
470 // Insert X into hash table
471 // Return false if Xis a duplicate; true otherwise
472 // Rehash automatically as needed
473 template<class KEY, class ARG_KEY, class VALUE, typename HashFunctions>
475 {
476 unsigned int CurrentPos = FindPos(X);
477
478 // Don't insert duplicates
479 if (Array[CurrentPos].Info == Active)
480 {
481 // Update the value because (useful on classes where operator == might return true
482 // for 2 differents contents...)
483 Array[CurrentPos].Value = V;
484 return 0;
485 }
486 else if (Array[CurrentPos].Info == Empty) // Can be Deleted
487 CurrentSize++; // It is the number of non empty item
488
489 ActiveSize++;
490
491 // 05/2018: Cf note in HashEntry
492 // Insert X as active
493 Array[CurrentPos].Key = X;
494 Array[CurrentPos].Value = V;
495 Array[CurrentPos].Info = Active;
496
497 if (CurrentSize < ArraySize/2) // 06/19: Must rehash when CurrentSize = ArraySize/2 (ie when if ArraySize == 21, must rehash with CurrentSize = 10 (not 11!) or ith probe MIGHT fails in FindPos in next insertion
498 return 1;
499
500#ifdef MOOTOOLS_PRIVATE_DEBUG
501 CheckCount();
502#endif
503
504 ReHash(false);
505 return 1;
506 }
507
508 // Deep copy of the hash table
509 template<class KEY, class ARG_KEY, class VALUE, typename HashFunctions>
512 {
513 if (this != &Rhs)
514 {
515 if (ArraySize != Rhs.ArraySize)
516 {
517 xDeleteArray(Array, ArraySize);
518 ArraySize = Rhs.ArraySize;
519 AllocateArray();
520 }
521
522 for (unsigned int i = 0; i < ArraySize; i++)
523 {
524 Array[i].Info = Rhs.Array[i].Info;
525 if (Array[i].Info == Active)
526 {
527 Array[i].Key = Rhs.Array[i].Key;
528 Array[i].Value = Rhs.Array[i].Value;
529 }
530 }
531
532 DefaultSize = Rhs.DefaultSize;
533 CurrentSize = Rhs.CurrentSize;
534 ActiveSize = Rhs.ActiveSize;
535 }
536
537 return *this;
538 }
539
540 template<class KEY, class ARG_KEY, class VALUE, typename HashFunctions>
542 {
543 return (ActiveSize == 0);
544 }
545
546 // Manu add
547 template<class KEY, class ARG_KEY, class VALUE, typename HashFunctions>
549 {
550 return ActiveSize;
551 }
552
553 #ifdef MOOTOOLS_PRIVATE_DEBUG
554 template<class KEY, class ARG_KEY, class VALUE, typename HashFunctions>
556 {
557 if (CurrentSize == 0)
558 return 0;
559
560 unsigned int size1 = 0;
561 unsigned int size2 = 0;
562
563 // Copy table over
564 for (unsigned int i = 0; i < ArraySize; i++)
565 {
566 if (Array[i].Info == Active)
567 size1++;
568 if (Array[i].Info != Empty)
569 size2++;
570 }
571
572 XASSERT(ActiveSize == size1);
573 XASSERT(CurrentSize == size2);
574
575 return size1;
576 }
577 #endif
578
579 // Find and active element from pos to end of the array
580 template<class KEY, class ARG_KEY, class VALUE, typename HashFunctions>
582 {
583 for (unsigned int i = pos; i < ArraySize; i++)
584 {
585 if (Array[i].Info == Active)
586 return i;
587 }
588
589 return HashEnd;
590 }
591
592 template<class KEY, class ARG_KEY, class VALUE, typename HashFunctions>
594 {
595 #ifdef _DEBUG
596 checkResize = false;
597 #endif
598
599 return FindActiveElement(0);
600 }
601
602 template<class KEY, class ARG_KEY, class VALUE, typename HashFunctions>
603 void CHashMap<KEY, ARG_KEY, VALUE, HashFunctions>::GetNext(HashPos& pos, KEY& key, VALUE& value) const
604 {
605 XASSERT(pos < ArraySize);
606 XASSERT(Array[pos].Info == Active);
607 XASSERT(checkResize == false); // We must Rehash during a GetFirst/GetNext operation
608
609 // Update the current element and find the next one
610 key = Array[pos].Key;
611 value = Array[pos].Value;
612 pos = FindActiveElement(pos + 1);
613 }
614
615 template<class KEY, class ARG_KEY, class VALUE, typename HashFunctions>
616 void CHashMap<KEY, ARG_KEY, VALUE, HashFunctions>::GetNext(HashPos& pos, KEY& key, VALUE *&value) const
617 {
618 XASSERT(pos < ArraySize);
619 XASSERT(Array[pos].Info == Active);
620 XASSERT(checkResize == false); // We must Rehash during a GetFirst/GetNext operation
621
622 // Update the current element and find the next one
623 key = Array[pos].Key;
624 value = &Array[pos].Value;
625 pos = FindActiveElement(pos + 1);
626 }
627
628 template<class KEY, class ARG_KEY, class VALUE, typename HashFunctions>
629 void CHashMap<KEY, ARG_KEY, VALUE, HashFunctions>::GetNext(HashPos& pos, KEY *key, VALUE *value) const
630 {
631 XASSERT(pos < ArraySize);
632 XASSERT(Array[pos].Info == Active);
633 XASSERT(checkResize == false); // We must Rehash during a GetFirst/GetNext operation
634
635 // Update the current element and find the next one
636 key = &Array[pos].Key;
637 value = &Array[pos].Value;
638 pos = FindActiveElement(pos + 1);
639 }
640
641 template<class KEY, class ARG_KEY, class VALUE, typename HashFunctions>
643 {
644 XASSERT(pos < ArraySize);
645 XASSERT(Array[pos].Info == Active);
646 XASSERT(checkResize == false); // We must Rehash during a GetFirst/GetNext operation
647
648 // Update the current element and find the next one
649 HashPos prevpos = pos;
650 pos = FindActiveElement(pos + 1);
651
652 value = &Array[prevpos].Value;
653 return &Array[prevpos].Key;
654 }
655
656 template<class KEY, class ARG_KEY, class VALUE, typename HashFunctions>
658 {
659 XASSERT(pos < ArraySize);
660 XASSERT(Array[pos].Info == Active);
661 XASSERT(checkResize == false); // We must Rehash during a GetFirst/GetNext operation
662
663 // Update the current element and find the next one
664 HashPos prevpos = pos;
665 pos = FindActiveElement(pos + 1);
666
667 value = Array[prevpos].Value;
668 return Array[prevpos].Key;
669 }
670
671 class DLL_TOOLSFUNCTION CIntMap : public CHashMap<int, int, int>
672 {
673 };
674
675 #if defined(__WINDOWS__) && (_MSC_VER <= 1500)
676 #pragma warning(pop)
677 #endif
678
679 template<class KEY, class ARG_KEY, class VALUE, typename HashFunctions>
681 {
682 if (Rhs1.GetCount() != Rhs2.GetCount())
683 return false;
684
685 KEY *key1;
687 HashPos pos = Rhs1.GetFirst();
688 while (pos != HashEnd)
689 {
690 key1 = Rhs1.GetNext(pos, value1);
691 value2 = Rhs2.Find(*key1);
692 if (!value2)
693 return false;
694
695 if (*value1 != *value2)
696 return false;
697 }
698
699 return true;
700 }
701END_MOOTOOLS_NAMESPACE
702
703#endif // __CHASHMAP_H
The class defines an x, y, z 3D point which can use int, float or double.
Definition 3DPoint.h:27
CHashMap is a template class that associates key to a single value through an hash table.
Definition HashMap.h:42
Definition HashMap.h:672