Polygon Crucher SDK - Documentation
Documentation
Loading...
Searching...
No Matches
Hash.h
1//! @file HashTable.h
2//! @brief Implements a template hash table
3//!
4// Etype: must have zero-parameter and copy constructor,
5// operator= and operator!=
6// unsigned int Hash( const Etype & Element, int TableSize )
7// must be defined
8// CONSTRUCTION: with (a) no initializer;
9// Copy constructon of CHashTable objects is DISALLOWED
10// Deep copy is supported
11//
12// ******************PUBLIC OPERATIONS*********************
13// int Insert( Etype X ) --> Insert X
14// int Remove( Etype X ) --> Remove X
15// Etype Find( Etype X ) --> Return item that matches X
16// int IsFound( Etype X ) --> Return 1 if X would be found
17// int IsEmpty( ) --> Return 1 if clear; else return 0
18// void Clear( ) --> Remove all items
19// ******************ERRORS********************************
20// Predefined exception is propogated if new fails
21
22#ifndef __CHASHTABLE_H
23#define __CHASHTABLE_H
24
25BEGIN_MOOTOOLS_NAMESPACE
26
27 typedef unsigned int HashPos;
28
29 #define HashEnd (unsigned int)(-1)
30 #define MinimalHashSize 5 // Must be prime number
31 #define DefaultHashSize 23 // Must be prime number
32
33 #ifdef MOOTOOLS_PRIVATE_DEBUG
34 #define MOOTOOLS_HASH_ADVANCED_DEBUG
35
36 #ifdef MOOTOOLS_HASH_ADVANCED_DEBUG
37 typedef struct HashStats
38 {
40 unsigned int rehash;
42 unsigned int rehashCollision;
43 unsigned int insertCollision;
44
45 HashStats()
46 {
49 }
50 } HashStats;
51
52 typedef enum
53 {
54 HASH_DBG_INIT = 1,
58
59 DLL_TOOLSFUNCTION unsigned int GetDefaultHashSize();
60 DLL_TOOLSFUNCTION void DebugCheckHashStats(HASH_DEBUG_INFO info);
61 DLL_TOOLSFUNCTION void EnableHashStats(bool enable);
62 DLL_TOOLSFUNCTION void ReportHashStats(LPCTSTR txt, unsigned int activeSize, unsigned int currentSize, unsigned int arraySize, HashStats& stats);
63
64 #undef DefaultHashSize
65 #define DefaultHashSize GetDefaultHashSize()
66 #endif
67 #endif
68
69 // Hash computation
70#ifdef _DEBUG
71 DLL_TOOLSFUNCTION bool IsPrime(unsigned int N);
72#endif
73
74 DLL_TOOLSFUNCTION unsigned int NextPrime(unsigned int N);
75
76 // std lib hash method
77 inline unsigned int HashBuffer(const char *values, longuint count)
78 {
79 const unsigned int _FNV_offset_basis = 2166136261U;
80 const unsigned int _FNV_prime = 16777619U;
81
82 unsigned int _Val = _FNV_offset_basis;
83
84 longuint i = count;
85 while (i--)
86 { // fold in another byte
87 _Val ^= (unsigned int)values[i];
89 }
90
91 return (_Val);
92 }
93
94 inline unsigned int HashBuffer(const wchar_t *values, longuint count)
95 {
96 const unsigned int _FNV_offset_basis = 2166136261U;
97 const unsigned int _FNV_prime = 16777619U;
98
99 unsigned int _Val = _FNV_offset_basis;
100
101 longuint i = count;
102 while (i--)
103 { // fold in another byte
104 _Val ^= (unsigned int)values[i];
105 _Val *= _FNV_prime;
106 }
107
108 return (_Val);
109 }
110
111 template<class TYPE> inline longuint Hash64DefaultType(TYPE key)
112 {
113 key = (~key) + (key << 21); // key = (key << 21) - key - 1;
114 key = key ^ (key >> 24);
115 key = (key + (key << 3)) + (key << 8); // key * 265
116 key = key ^ (key >> 14);
117 key = (key + (key << 2)) + (key << 4); // key * 21
118 key = key ^ (key >> 28);
119 key = key + (key << 31);
120 return (longuint)key;
121 }
122
123 template<class TYPE> inline unsigned int HashDefaultType(TYPE key)
124 {
125 key = (~key) + (key << 18);
126 key = key ^ (key >> 31);
127 key = key * 21;
128 key = key ^ (key >> 11);
129 key = key + (key << 6);
130 key = key ^ (key >> 22);
131 return (unsigned int)(key);
132 }
133
134 template<class TYPE> inline unsigned int HashMixValue(TYPE value)
135 {
136 value += ~(value << 15);
137 value ^= (value >> 10);
138 value += (value << 3);
139 value ^= (value >> 6);
140 value += ~(value << 11);
141 value ^= (value >> 16);
142 return value;
143 }
144
145 template<class TYPE> class CHashMethods
146 {
147 public:
148 static inline bool HashCompare(const TYPE& a, const TYPE& b);
149 static inline unsigned int HashValue(const TYPE& a);
150 };
151
152 template <class TYPE> class CHashMethods<TYPE *>
153 {
154 public:
155 static inline bool HashCompare(const TYPE *a, const TYPE *b)
156 {
157 return a == b;
158 }
159
160 static inline unsigned int HashValue(const TYPE *ptr)
161 {
162 return HashDefaultType((longuint)(ptr));
163 }
164 };
165
166 template <> class CHashMethods<unsigned int>
167 {
168 public:
169 static inline bool HashCompare(unsigned int a, unsigned int b)
170 {
171 return a == b;
172 }
173
174 static inline unsigned int HashValue(unsigned int value)
175 {
176 return value << 4;
177 }
178 };
179
180 template <> class CHashMethods<int> : public CHashMethods<unsigned int> {};
181 template <> class CHashMethods<unsigned char> : public CHashMethods<unsigned int> {};
182 template <> class CHashMethods<char > : public CHashMethods<unsigned int> {};
183 template <> class CHashMethods<wchar_t > : public CHashMethods<unsigned int> {};
184
185#if defined(__LINUX__) && (__WORDSIZE != 32)
186 template <> class CHashMethods<unsigned long long>
187 {
188 public:
189 static inline bool HashCompare(unsigned long long a, unsigned long long b)
190 {
191 return a == b;
192 }
193
194 // cf. Integer Hash Function. Thomas Wang, Jan 1997 - last update Mar 2007 Version 3.1
195 // https://gist.github.com/badboy/6267743
196 static inline unsigned int HashValue(unsigned long long key)
197 {
198 return HashDefaultType(key);
199 }
200 };
201
202 template <> class CHashMethods<long long>
203 {
204 public:
205 static inline bool HashCompare(long long a, long long b)
206 {
207 return a == b;
208 }
209
210 // cf. Integer Hash Function. Thomas Wang, Jan 1997 - last update Mar 2007 Version 3.1
211 // https://gist.github.com/badboy/6267743
212 static inline unsigned int HashValue(long long key)
213 {
214 return HashDefaultType(key);
215 }
216 };
217#endif
218
219 template <> class CHashMethods<longuint>
220 {
221 public:
222 static inline bool HashCompare(longuint a, longuint b)
223 {
224 return a == b;
225 }
226
227 // cf. Integer Hash Function. Thomas Wang, Jan 1997 - last update Mar 2007 Version 3.1
228 // https://gist.github.com/badboy/6267743
229 static inline unsigned int HashValue(longuint key)
230 {
231 return HashDefaultType(key);
232 }
233 };
234
235 template <> class CHashMethods<longint>
236 {
237 public:
238 static inline bool HashCompare(longint a, longint b)
239 {
240 return a == b;
241 }
242
243 static inline unsigned int HashValue(longint key)
244 {
245 return HashDefaultType(key);
246 }
247 };
248
249#ifdef __WINDOWS__
250 template <> class CHashMethods<unsigned long>
251 {
252 public:
253 static inline bool HashCompare(unsigned long a, unsigned long b)
254 {
255 return a == b;
256 }
257
258 static inline unsigned int HashValue(unsigned long value)
259 {
260 return value << 4;
261 }
262 };
263#endif
264
265 template <> class CHashMethods<float>
266 {
267 public:
268 static inline bool HashCompare(float a, float b)
269 {
270 return a == b;
271 }
272
273 static inline unsigned int HashValue(float value)
274 {
275 // On IEEE-float machines, minus zero and zero have different bit patterns
276 // but should compare as equal. We must ensure that they have the same
277 // hash value, which is most reliably done this way:
278 if (value == 0.0f)
279 return 0;
280
281 return *((unsigned int *)&value);
282 }
283 };
284
285 template <> class CHashMethods<double>
286 {
287 public:
288 static inline bool HashCompare(double a, double b)
289 {
290 return a == b;
291 }
292
293 static inline unsigned int HashValue(double value)
294 {
295 // On IEEE-float machines, minus zero and zero have different bit patterns
296 // but should compare as equal. We must ensure that they have the same
297 // hash value, which is most reliably done this way:
298 if (value == 0.0)
299 return 0;
300
301 return *((unsigned int *)&value);
302 }
303 };
304
305 template <> class CHashMethods<CXStringW>
306 {
307 public:
308 static inline unsigned int HashValue(const CXStringW &Key)
309 {
310 return HashBuffer(Key, Key.GetLength());
311 }
312
313 static inline bool HashCompare(const CXStringW& a, const CXStringW& b)
314 {
315 return a == b;
316 }
317 };
318
319 template <> class CHashMethods<CXStringA>
320 {
321 public:
322 static inline unsigned int HashValue(const CXStringA &Key)
323 {
324 return HashBuffer(Key, Key.GetLength());
325 }
326
327 static inline bool HashCompare(const CXStringA& a, const CXStringA& b)
328 {
329 return a == b;
330 }
331 };
332
333 //! @class CHashTable
334 //! @brief CHashTable is a template class that implements an hash table.
335 //! @details CHashMethods must be provided to compare entries and generate hash value for the template EType
336 template<class Etype, typename HashFunctions = CHashMethods<Etype> > class CHashTable // Keep space between > > for Linux C98
337 {
338 public:
339
340 enum KindOfEntry {
341 Active, Empty, Deleted
342 };
343
344 CHashTable(unsigned int defaultSize = DefaultHashSize);
345 CHashTable(const CHashTable& Rhs);
346 virtual ~CHashTable();
347
348 const CHashTable & operator=(const CHashTable & Rhs);
349 unsigned int CopyTo(CXArray<Etype>& dstArray) const;
350 unsigned int InitFrom(const CXArray<Etype>& dstArray, bool clear = true);
351
352 void InitHashTable(unsigned int elementsNumber);
353 unsigned int GetHashTableSize() const;
354 bool Minimize();
355
356 bool Compare(const CHashTable& Rhs) const; // return true if same
357 void Merge(const CHashTable& Rhs);
358 void Remove(const CHashTable& Rhs);
359 int Insert(const Etype & X);
360 void InsertAndReplace(const Etype & X);
361 int Remove(const Etype & X, bool enableResize = false); // Return 1 if found, 0 if not. enableResize must be false if removal is done in a GetFirst/GetNext loop
362 int FindAndUpdate(Etype & X); // Return item in table
363 int IsFound(const Etype & X) const; // Return 1 if found
364 int IsEmpty() const;
365 void Free();
366 void Clear();
367 unsigned int GetCount() const; // Return the element number
368 HashPos GetFirst() const;
369 void GetNext(HashPos& pos, Etype& element) const; // Get the next element. This return a copy and can be slower than GetNextPtr(HashPos& pos) which return a pointer on the element.
370// void GetNext(HashPos& pos, Etype *&element) const; // Get the next element pointer
371 Etype& GetNext(HashPos& pos) const; // Return the next element. This return a copy and can be slower than GetNextPtr(HashPos& pos) which returns a pointer on the element.
372 Etype *GetNextPtr(HashPos& pos) const; // Return a pointer on the next element.
373
374 private:
375 HashPos FindActiveElement(HashPos pos) const;
376
377 // 05/2018 : Remove HashEntry(const Etype & E, KindOfEntry i = Empty) : Element(E), Info(i) constructor
378 // 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)
379 // Additionally using this method make hash slower.
380 struct HashEntry
381 {
382 Etype Element; // The item
383 unsigned char Info; // Active, empty, or deleted
384 HashEntry() : Info(CHashTable<Etype, HashFunctions>::Empty) {}
385 };
386
387 unsigned int DefaultSize;
388 unsigned int ArraySize; // The size of this array
389 unsigned int CurrentSize; // The number of non-empty items (Active or Deleted)
390 unsigned int ActiveSize; // The number of active items (hash Active items)
391 HashEntry *Array; // The array of elements
392
393 // Some internal routines
394 void AllocateArray();
395 bool ReHash(bool minimize);
396 unsigned int FindPos(const Etype & X) const;
397
398#ifdef _DEBUG
399 mutable bool checkResize; // Check that we not perform resize in GetFirst/GetNext
400#endif
401
402#ifdef MOOTOOLS_HASH_ADVANCED_DEBUG
403 mutable HashStats stats;
404 unsigned int CheckCount() const; // Return the element number
405#endif
406 };
407
408 // Friends functions
409 template<class Etype, typename HashFunctions> bool IsSameHash(const CHashTable<Etype, HashFunctions> & Rhs1, const CHashTable<Etype, HashFunctions> & Rhs2);
410
411 // Define function here to allow CHashTable without specialisation
412 // Allocate the hash table array
413 template<class Etype, typename HashFunctions> void CHashTable<Etype, HashFunctions>::AllocateArray()
414 {
415 #ifdef MOOTOOLS_HASH_ADVANCED_DEBUG
417 if (ArraySize > DefaultSize && stats.checkAllocAboveDefaultSize)
418 {
419 stats.checkAllocAboveDefaultSize = false;
421 }
422 #endif
423
424 Array = xNewArray(HashEntry, ArraySize);
425 }
426
427 // CHashTable constructor
428 template<class Etype, typename HashFunctions>
430 {
431 Array = NULL;
432 ArraySize = 0;
433 ActiveSize = CurrentSize = 0;
434 InitHashTable(defaultSize);
435
436 #ifdef _DEBUG
437 checkResize = false;
438 #endif
439
440 #ifdef MOOTOOLS_HASH_ADVANCED_DEBUG
442 #endif
443
444 }
445
446 template<class Etype, typename HashFunctions>
448 {
449 Array = NULL;
450 ArraySize = 0;
451 ActiveSize = CurrentSize = 0;
452
453#ifdef _DEBUG
454 checkResize = false;
455#endif
456
457#ifdef MOOTOOLS_HASH_ADVANCED_DEBUG
459#endif
460
461 *this = Rhs;
462 }
463
464 // CHashTable constructor
465 template<class Etype, typename HashFunctions>
467 {
468 xDeleteArray(Array, ArraySize);
469
470 #ifdef MOOTOOLS_HASH_ADVANCED_DEBUG
471 Array = NULL; // Check that array is not accessed after deletion
472 ReportHashStats(_T("CHashTable::~CHashTable"), ActiveSize, CurrentSize, ArraySize, stats);
473 #endif
474 }
475 template<class Etype, typename HashFunctions>
477 {
478 if (Array != NULL) // Already allocated
479 {
480 elementsNumber = (elementsNumber+1)*2; // 06/19: +1 avoid some rehash when we specify exactly the number of the element of the map
481 elementsNumber = NextPrime(elementsNumber);
482 elementsNumber = (elementsNumber < MinimalHashSize) ? MinimalHashSize : elementsNumber; // If InitHashTable is called, elementNumber could be the real element number. Don't waste memory with extra space
483
484 if (DefaultSize == elementsNumber)
485 return;
486 }
487 else
488 {
489 if (elementsNumber != DefaultHashSize)
490 {
491 elementsNumber = NextPrime(elementsNumber);
492 elementsNumber = (elementsNumber < MinimalHashSize) ? MinimalHashSize : elementsNumber; // If InitHashTable is called, elementNumber could be the real element number. Don't waste memory with extra space
493 }
494 }
495
496 XASSERT(ActiveSize == 0 && CurrentSize == 0);
497
498 xDeleteArray(Array, ArraySize);
499
500 DefaultSize = elementsNumber;
501 ArraySize = elementsNumber;
502 AllocateArray();
503 ActiveSize = CurrentSize = 0;
504 }
505
506 template<class Etype, typename HashFunctions>
508 {
509 return ArraySize;
510 }
511
512 template<class Etype, typename HashFunctions> void CHashTable<Etype, HashFunctions>::Free()
513 {
514 #ifdef MOOTOOLS_HASH_ADVANCED_DEBUG
515 ReportHashStats(_T("CHashTable::Free"), ActiveSize, CurrentSize, ArraySize, stats);
516 #endif
517
518 xDeleteArray(Array, ArraySize);
519
520 ArraySize = DefaultSize = DefaultHashSize;
521 ActiveSize = CurrentSize = 0;
522 AllocateArray();
523 }
524
525 // Clear the hash table
526 template<class Etype, typename HashFunctions> void CHashTable<Etype, HashFunctions>::Clear()
527 {
528 #ifdef MOOTOOLS_HASH_ADVANCED_DEBUG
529 ReportHashStats(_T("CHashTable::Clear"), ActiveSize, CurrentSize, ArraySize, stats);
530 #endif
531
532 if (ActiveSize == 0 && CurrentSize == 0)
533 return;
534
535 ActiveSize = CurrentSize = 0;
536 for (unsigned int i = 0; i < ArraySize; i++)
537 Array[i].Info = Empty;
538 }
539
540 // Find an element and update it. If HashCompare is complete and the element inserted is the same than the X we provide
541 // In such case, IsFound is faster and should be used
542 template<class Etype, typename HashFunctions> int CHashTable<Etype, HashFunctions>::FindAndUpdate(Etype& X)
543 {
544 unsigned int CurrentPos = FindPos(X);
545
546 if (Array[CurrentPos].Info == Active)
547 {
548 X = Array[CurrentPos].Element;
549 return true;
550 }
551
552 return false;
553 }
554
555 // Return true if hash could be reduced, false otherwise
556 template<class Etype, typename HashFunctions> bool CHashTable<Etype, HashFunctions>::Minimize()
557 {
558 #ifdef MOOTOOLS_HASH_ADVANCED_DEBUG
559 ReportHashStats(_T("CHashTable::Minimize"), ActiveSize, CurrentSize, ArraySize, stats);
560 #endif
561
562 return ReHash(true); // ReHash does the job only if it is needed
563 }
564
565 // Routine to resolve collisions and locate
566 // cell that must contain X if X is found
567 // Note:
568 // 04/07/2013 changes: the hash could returns a deleted element, to reuse it
569 // As the caller checks if the element is active this have no incidence.
570 //
571 // Before the hash calls HashCompare for every element, even deleted
572 // 10/10/17: minor optimization
573 template<class Etype, typename HashFunctions>
574 inline unsigned int CHashTable<Etype, HashFunctions>::FindPos(const Etype & X) const
575 {
576 unsigned int i = 0; // The number of failed probes
577 unsigned int CurrentPos = HashFunctions::HashValue(X) % ArraySize;
578 unsigned int DeletedPos = (-1);
579
580 while (Array[CurrentPos].Info != Empty)
581 {
582 if (Array[CurrentPos].Info == Active)
583 {
584 if (HashFunctions::HashCompare(Array[CurrentPos].Element, X))
585 return CurrentPos;
586 }
587 else if (DeletedPos == (-1))
588 {
589 XASSERT(Array[CurrentPos].Info == Deleted);
591 }
592
593 CurrentPos += 2*++i - 1; // Compute ith probe
594 XASSERT(CurrentPos < ArraySize || ((CurrentPos - ArraySize) < ArraySize));
595 if (CurrentPos >= ArraySize) // Implement the mod
596 CurrentPos -= ArraySize;
597 }
598
599 #ifdef MOOTOOLS_HASH_ADVANCED_DEBUG
600 if (i > stats.insertCollisionCount)
601 stats.insertCollisionCount = i;
602 if (i > stats.rehashCollissionCount)
603 stats.rehashCollissionCount = i;
604 #endif
605
606 // Return the first deleted entry if we found one
607 // If no active element was found
608 XASSERT(Array[CurrentPos].Info == Empty);
609 return DeletedPos != (-1) ? DeletedPos : CurrentPos;
610 }
611
612 // Test if X is in the hash table
613 template<class Etype, typename HashFunctions> int CHashTable<Etype, HashFunctions>::IsFound(const Etype & X) const
614 {
615 unsigned int CurrentPos = FindPos(X);
616 return (Array[CurrentPos].Info == Active);
617 }
618
619 // Remove X from hash table
620 // Return true if X was removed; false otherwise
621
622 template<class Etype, typename HashFunctions> int CHashTable<Etype, HashFunctions>::Remove(const Etype & X, bool enableResize)
623 {
624 unsigned int CurrentPos = FindPos(X);
625 if (Array[CurrentPos].Info != Active)
626 return 0;
627
628 XASSERT(ActiveSize >= 0);
629 Array[CurrentPos].Info = Deleted; // Do not tag as empty, otherwise this makes holes in findpos
630 ActiveSize--;
631
632 // Should not resize in the GetFirst/GetNext loop
633 if (enableResize && (ActiveSize > DefaultHashSize)) // Avoid constant rehash for small bucket. Rehashing is done on insertion / minimize
634 ReHash(false);
635
636 return 1;
637 }
638
639 template<class Etype, typename HashFunctions> void CHashTable<Etype, HashFunctions>::Remove(const CHashTable<Etype, HashFunctions>& hash)
640 {
641 HashPos pos = hash.GetFirst();
642
643 while (pos != HashEnd)
644 {
645 const Etype& value = hash.GetNext(pos);
646 Remove(value);
647 }
648 }
649
650 template<class Etype, typename HashFunctions> bool CHashTable<Etype, HashFunctions>::Compare(const CHashTable<Etype, HashFunctions>& hash) const
651 {
652 if (GetCount() != hash.GetCount())
653 return false;
654
655 HashPos pos = hash.GetFirst();
656 const Etype* value;
657 while (pos != HashEnd)
658 {
659 value = hash.GetNextPtr(pos);
660 if (!IsFound(*value))
661 return false;
662 }
663
664 return true;
665 }
666
667 template<class Etype, typename HashFunctions> void CHashTable<Etype, HashFunctions>::Merge(const CHashTable<Etype, HashFunctions>& hash)
668 {
669 HashPos pos = hash.GetFirst();
670 Etype *value;
671
672 while (pos != HashEnd)
673 {
674 value = hash.GetNextPtr(pos);
675 Insert(*value);
676 }
677 }
678
679 // Insert X into hash table
680 // Update X if it exists, which make sense only when a class defined an operator == which might return true
681 // for 2 differents contents...
682
683 template<class Etype, typename HashFunctions> void CHashTable<Etype, HashFunctions>::InsertAndReplace(const Etype & X)
684 {
685 unsigned int CurrentPos = FindPos(X);
686
687 if (Array[CurrentPos].Info == Active)
688 {
689 Array[CurrentPos].Element = X;
690 return;
691 }
692
693 // Does not exists, insert it...
694 Insert(X);
695 return;
696 }
697
698 // 06/2019: modify rehash method to make it automatically compute the size
699 //
700 // 01/07/2013: use 6*ActiveSize instead 2*ArraySize
701 // note 2*ActiveSize is an array that as the minimal free space (see condition above)
702 // 2*2*ActiveSize is a correct size, which 2 time more space than needed
703 // 6*ActiveSize leaves enough space to avoid constant rehash, when adding and removing entries many times
704 // 6*ActiveSize < 2*ArraySize avoids to allocate array too big (when hash is used to only adds entries)
705 //
706 // The hash array size should be sized accordingly to the real size element number
707 // not the currentSize element number which is made of active and deleted values
708 // The ReHash methods removes deleted entries...
709 // The previous forms generates very large array for the pairs array in Polygon Cruncher
710 // as it spends its time to add and removes pairs.
711 template<class Etype, typename HashFunctions> bool CHashTable<Etype, HashFunctions>::ReHash(bool minimize)
712 {
713#ifdef _DEBUG
714 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
715#endif
716
717 // 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
718 unsigned int newSize = 0;
719 if (minimize)
720 {
721 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
722 newSize = NextPrime(newSize);
723 if (newSize == ArraySize)
724 {
725 MOOTOOLS_PRIVATE_ASSERT(CurrentSize < ArraySize/2);
726 return false;
727 }
728 }
729 else
730 {
731 if (CurrentSize < ArraySize/2) // Array is large enough. Rehash call with no reason
732 return false;
733
734 float ratio = (ActiveSize / (float)CurrentSize);
735 if (ratio <= 0.65) // Array as t least 50% of empty element and 33% of deleted elements. Insert/Remove sequence called
736 {
737 newSize = 2*(ActiveSize+1); // This lead to downsize the array
738 // newSize is the exact size for containing ActiveSize elements
739 // newSize < 0.65*ArraySize and ActiveSize < 0.65*CurrentSize as CurrentSize < 2*ArraySize
740 //
741 // 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
742 newSize = (unsigned int)(newSize/0.65); // Maximum value will be around current ArraySize.
743 if (newSize < DefaultHashSize)
744 newSize = DefaultHashSize;
745 }
746 else // Array is too small
747 newSize = ArraySize < 251 ? 4*ArraySize : 2*ArraySize; // Faster grow for small array
748
749 newSize = NextPrime(newSize);
750 // Must rehash as CurrentSize >= ArraySize/2!
751 }
752
753 // REHASHING CODE
754 // Save old table
755 HashEntry *OldArray = Array;
756 unsigned int OldArraySize = ArraySize;
757
758 ArraySize = newSize;
759 ActiveSize = CurrentSize = 0;
760 AllocateArray();
761
762 #ifdef MOOTOOLS_HASH_ADVANCED_DEBUG
763 stats.rehashCollissionCount = 0;
764 #endif
765
766 // Copy table over
767 for (unsigned int i = 0; i < OldArraySize; i++)
768 {
769 if (OldArray[i].Info == Active)
770 {
771 Insert(OldArray[i].Element);
772 }
773 }
774
775 #ifdef MOOTOOLS_HASH_ADVANCED_DEBUG
776 stats.rehash++;
777
778 if (stats.rehashCollissionCount > stats.rehashCollision)
779 stats.rehashCollision = stats.rehashCollissionCount;
780 #endif
781
782 // Recycle OldArray
783 xDeleteArray(OldArray, OldArraySize);
784
785 return true;
786 }
787
788 template<class Etype, typename HashFunctions> int CHashTable<Etype, HashFunctions>::Insert(const Etype & X)
789 {
790 #ifdef MOOTOOLS_HASH_ADVANCED_DEBUG
791 stats.insertCollisionCount = 0;
792 #endif
793
794 unsigned int CurrentPos = FindPos(X);
795
796 // Don't insert duplicates
797 if (Array[CurrentPos].Info == Active)
798 return 0;
799 else if (Array[CurrentPos].Info == Empty) // Can be Deleted
800 CurrentSize++; // It is the number of non empty item
801
802 ActiveSize++;
803
804 // 05/2018: Cf note in HashEntry
805 // Insert X as active
806 Array[CurrentPos].Element = X;
807 Array[CurrentPos].Info = Active;
808
809 #ifdef MOOTOOLS_HASH_ADVANCED_DEBUG
810 if (stats.insertCollisionCount > stats.insertCollision)
811 stats.insertCollision = stats.insertCollisionCount;
812 #endif
813
814 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
815 return 1;
816
817 #ifdef MOOTOOLS_HASH_ADVANCED_DEBUG
818 CheckCount();
819 #endif
820
821 ReHash(false);
822 return 1;
823 }
824
825 // Copy of the hash table to an array
826 template<class Etype, typename HashFunctions>
828 {
829 dstArray.SetSize(GetCount());
830
831 unsigned int i = 0;
832 HashPos pos = GetFirst();
833 while (pos != HashEnd)
834 dstArray[i++] = GetNext(pos);
835
836 XASSERT(i == GetCount());
837 return i;
838 }
839
840 template<class Etype, typename HashFunctions>
842 {
843 if (clear)
844 Clear();
845
846 unsigned int i, size = dstArray.GetSize();
847 for (i=0;i<size; i++)
848 Insert(dstArray[i]);
849
850 return GetCount();;
851 }
852
853 // Deep copy of the hash table
855 {
856 if (this != &Rhs)
857 {
858 if (ArraySize != Rhs.ArraySize)
859 {
860 xDeleteArray(Array, ArraySize);
861 ArraySize = Rhs.ArraySize;
862 AllocateArray();
863 }
864
865 for (unsigned int i = 0; i < ArraySize; i++)
866 {
867 Array[i].Info = Rhs.Array[i].Info;
868 if (Array[i].Info == Active)
869 Array[i].Element = Rhs.Array[i].Element;
870 }
871
872 DefaultSize = Rhs.DefaultSize;
873 CurrentSize = Rhs.CurrentSize;
874 ActiveSize = Rhs.ActiveSize;
875 }
876
877 return *this;
878 }
879
880 template<class Etype, typename HashFunctions> int CHashTable<Etype, HashFunctions>::IsEmpty() const
881 {
882 return (ActiveSize == 0);
883 }
884
885 // Manu add
886 template<class Etype, typename HashFunctions>
887 inline unsigned int CHashTable<Etype, HashFunctions>::GetCount() const
888 {
889 return ActiveSize;
890 }
891
892 #ifdef MOOTOOLS_HASH_ADVANCED_DEBUG
893 template<class Etype, typename HashFunctions> unsigned int CHashTable<Etype, HashFunctions>::CheckCount() const
894 {
895 if (CurrentSize == 0)
896 return 0;
897
898 unsigned int size1 = 0;
899 unsigned int size2 = 0;
900
901 // Copy table over
902 for (unsigned int i = 0; i < ArraySize; i++)
903 {
904 if (Array[i].Info == Active)
905 size1++;
906 if (Array[i].Info != Empty)
907 size2++;
908 }
909
910 XASSERT(ActiveSize == size1);
911 XASSERT(CurrentSize == size2);
912
913 return size1;
914 }
915 #endif
916
917 // Find and active element from pos to end of the array
918 template<class Etype, typename HashFunctions> HashPos CHashTable<Etype, HashFunctions>::FindActiveElement(HashPos pos) const
919 {
920 for (unsigned int i = pos; i < ArraySize; i++)
921 {
922 if (Array[i].Info == Active)
923 return i;
924 }
925
926 return HashEnd;
927 }
928
929 template<class Etype, typename HashFunctions> HashPos CHashTable<Etype, HashFunctions>::GetFirst() const
930 {
931 #ifdef _DEBUG
932 checkResize = false;
933 #endif
934
935 return FindActiveElement(0);
936 }
937
938 template<class Etype, typename HashFunctions> void CHashTable<Etype, HashFunctions>::GetNext(HashPos& pos, Etype& element) const
939 {
940 XASSERT(pos < ArraySize);
941 XASSERT(Array[pos].Info == Active);
942 XASSERT(checkResize == false); // We must Rehash during a GetFirst/GetNext operation
943
944 // Update the current element and find the next one
945 element = Array[pos].Element;
946 pos = FindActiveElement(pos + 1);
947 }
948
949 //template<class Etype, typename HashFunctions> void CHashTable<Etype, HashFunctions>::GetNext(HashPos& pos, Etype *&element) const
950 //{
951 // XASSERT(pos < ArraySize);
952 // XASSERT(Array[pos].Info == Active);
953 // XASSERT(checkResize == false); // We must Rehash during a GetFirst/GetNext operation
954
955 // // Update the current element and find the next one
956 // element = &Array[pos].Element;
957 // pos = FindActiveElement(pos + 1);
958 //}
959
960 template<class Etype, typename HashFunctions> Etype& CHashTable<Etype, HashFunctions>::GetNext(HashPos& pos) const
961 {
962 XASSERT(pos < ArraySize);
963 XASSERT(Array[pos].Info == Active);
964 XASSERT(checkResize == false); // We must Rehash during a GetFirst/GetNext operation
965
966 // Update the current element and find the next one
967 HashPos prevpos = pos;
968 pos = FindActiveElement(pos + 1);
969
970 return Array[prevpos].Element;
971 }
972
973 template<class Etype, typename HashFunctions> Etype *CHashTable<Etype, HashFunctions>::GetNextPtr(HashPos& pos) const
974 {
975 XASSERT(pos < ArraySize);
976 XASSERT(Array[pos].Info == Active);
977 XASSERT(checkResize == false); // We must Rehash during a GetFirst/GetNext operation
978
979 // Update the current element and find the next one
980 HashPos prevpos = pos;
981 pos = FindActiveElement(pos + 1);
982
983 return &Array[prevpos].Element;
984 }
985
986 template<class Etype, typename HashFunctions>
988 {
989 if (Rhs1.GetCount() != Rhs2.GetCount())
990 return false;
991
992 Etype *key;
993 HashPos pos = Rhs1.GetFirst();
994 while (pos != HashEnd)
995 {
996 key = Rhs1.GetNextPtr(pos);
997 if (!Rhs2.IsFound(*key))
998 return false;
999 }
1000
1001 return true;
1002 }
1003
1004 END_MOOTOOLS_NAMESPACE
1005
1006#endif // __CHASHTABLE_H
The class defines an x, y, z 3D point which can use int, float or double.
Definition 3DPoint.h:27
Definition Hash.h:146
CHashTable is a template class that implements an hash table.
Definition Hash.h:337
int GetLength() const
returns the length of the buffer in characters (can be greater than strlen, if set by GetBufferSetLen...
Definition XString.h:749