ovr_sdk

view LibOVR/Src/Kernel/OVR_Hash.h @ 0:1b39a1b46319

initial 0.4.4
author John Tsiombikas <nuclear@member.fsf.org>
date Wed, 14 Jan 2015 06:51:16 +0200
parents
children
line source
1 /************************************************************************************
3 PublicHeader: None
4 Filename : OVR_Hash.h
5 Content : Template hash-table/set implementation
6 Created : September 19, 2012
7 Notes :
9 Copyright : Copyright 2014 Oculus VR, LLC All Rights reserved.
11 Licensed under the Oculus VR Rift SDK License Version 3.2 (the "License");
12 you may not use the Oculus VR Rift SDK except in compliance with the License,
13 which is provided at the time of installation or download, or which
14 otherwise accompanies this software in either electronic or hard copy form.
16 You may obtain a copy of the License at
18 http://www.oculusvr.com/licenses/LICENSE-3.2
20 Unless required by applicable law or agreed to in writing, the Oculus VR SDK
21 distributed under the License is distributed on an "AS IS" BASIS,
22 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
23 See the License for the specific language governing permissions and
24 limitations under the License.
26 ************************************************************************************/
28 #ifndef OVR_Hash_h
29 #define OVR_Hash_h
31 #include "OVR_ContainerAllocator.h"
32 #include "OVR_Alg.h"
34 // 'new' operator is redefined/used in this file.
35 #undef new
37 namespace OVR {
39 //-----------------------------------------------------------------------------------
40 // ***** Hash Table Implementation
42 // HastSet and Hash.
43 //
44 // Hash table, linear probing, internal chaining. One interesting/nice thing
45 // about this implementation is that the table itself is a flat chunk of memory
46 // containing no pointers, only relative indices. If the key and value types
47 // of the Hash contain no pointers, then the Hash can be serialized using raw IO.
48 //
49 // Never shrinks, unless you explicitly Clear() it. Expands on
50 // demand, though. For best results, if you know roughly how big your
51 // table will be, default it to that size when you create it.
52 //
53 // Key usability feature:
54 //
55 // 1. Allows node hash values to either be cached or not.
56 //
57 // 2. Allows for alternative keys with methods such as GetAlt(). Handy
58 // if you need to search nodes by their components; no need to create
59 // temporary nodes.
60 //
63 // *** Hash functors:
64 //
65 // IdentityHash - use when the key is already a good hash
66 // HFixedSizeHash - general hash based on object's in-memory representation.
69 // Hash is just the input value; can use this for integer-indexed hash tables.
70 template<class C>
71 class IdentityHash
72 {
73 public:
74 size_t operator()(const C& data) const
75 { return (size_t) data; }
76 };
78 // Computes a hash of an object's representation.
79 template<class C>
80 class FixedSizeHash
81 {
82 public:
83 // Alternative: "sdbm" hash function, suggested at same web page
84 // above, http::/www.cs.yorku.ca/~oz/hash.html
85 // This is somewhat slower then Bernstein, but it works way better than the above
86 // hash function for hashing large numbers of 32-bit ints.
87 static OVR_FORCE_INLINE size_t SDBM_Hash(const void* data_in, size_t size, size_t seed = 5381)
88 {
89 const uint8_t* data = (const uint8_t*) data_in;
90 size_t h = seed;
91 while (size-- > 0)
92 {
93 #ifndef __clang_analyzer__ // It mistakenly thinks data is garbage.
94 h = (h << 16) + (h << 6) - h + (size_t)data[size];
95 #endif
96 }
97 return h;
98 }
100 size_t operator()(const C& data) const
101 {
102 const unsigned char* p = (const unsigned char*) &data;
103 const size_t size = sizeof(C);
105 return SDBM_Hash(p, size);
106 }
107 };
111 // *** HashsetEntry Entry types.
113 // Compact hash table Entry type that re-computes hash keys during hash traversal.
114 // Good to use if the hash function is cheap or the hash value is already cached in C.
115 template<class C, class HashF>
116 class HashsetEntry
117 {
118 public:
119 // Internal chaining for collisions.
120 intptr_t NextInChain;
121 C Value;
123 HashsetEntry()
124 : NextInChain(-2) { }
125 HashsetEntry(const HashsetEntry& e)
126 : NextInChain(e.NextInChain), Value(e.Value) { }
127 HashsetEntry(const C& key, intptr_t next)
128 : NextInChain(next), Value(key) { }
130 bool IsEmpty() const { return NextInChain == -2; }
131 bool IsEndOfChain() const { return NextInChain == -1; }
133 // Cached hash value access - can be optimized bu storing hash locally.
134 // Mask value only needs to be used if SetCachedHash is not implemented.
135 size_t GetCachedHash(size_t maskValue) const { return HashF()(Value) & maskValue; }
136 void SetCachedHash(size_t) {}
138 void Clear()
139 {
140 Value.~C(); // placement delete
141 NextInChain = -2;
142 }
143 // Free is only used from dtor of hash; Clear is used during regular operations:
144 // assignment, hash reallocations, value reassignments, so on.
145 void Free() { Clear(); }
146 };
148 // Hash table Entry type that caches the Entry hash value for nodes, so that it
149 // does not need to be re-computed during access.
150 template<class C, class HashF>
151 class HashsetCachedEntry
152 {
153 public:
154 // Internal chaining for collisions.
155 intptr_t NextInChain;
156 size_t HashValue;
157 C Value;
159 HashsetCachedEntry()
160 : NextInChain(-2) { }
161 HashsetCachedEntry(const HashsetCachedEntry& e)
162 : NextInChain(e.NextInChain), HashValue(e.HashValue), Value(e.Value) { }
163 HashsetCachedEntry(const C& key, intptr_t next)
164 : NextInChain(next), Value(key) { }
166 bool IsEmpty() const { return NextInChain == -2; }
167 bool IsEndOfChain() const { return NextInChain == -1; }
169 // Cached hash value access - can be optimized bu storing hash locally.
170 // Mask value only needs to be used if SetCachedHash is not implemented.
171 size_t GetCachedHash(size_t maskValue) const { OVR_UNUSED(maskValue); return HashValue; }
172 void SetCachedHash(size_t hashValue) { HashValue = hashValue; }
174 void Clear()
175 {
176 Value.~C();
177 NextInChain = -2;
178 }
179 // Free is only used from dtor of hash; Clear is used during regular operations:
180 // assignment, hash reallocations, value reassignments, so on.
181 void Free() { Clear(); }
182 };
185 //-----------------------------------------------------------------------------------
186 // *** HashSet implementation - relies on either cached or regular entries.
187 //
188 // Use: Entry = HashsetCachedEntry<C, HashF> if hashes are expensive to
189 // compute and thus need caching in entries.
190 // Entry = HashsetEntry<C, HashF> if hashes are already externally cached.
191 //
192 template<class C, class HashF = FixedSizeHash<C>,
193 class AltHashF = HashF,
194 class Allocator = ContainerAllocator<C>,
195 class Entry = HashsetCachedEntry<C, HashF> >
196 class HashSetBase
197 {
198 enum { HashMinSize = 8 };
200 public:
201 OVR_MEMORY_REDEFINE_NEW(HashSetBase)
203 typedef HashSetBase<C, HashF, AltHashF, Allocator, Entry> SelfType;
205 HashSetBase() : pTable(NULL) { }
206 HashSetBase(int sizeHint) : pTable(NULL) { SetCapacity(this, sizeHint); }
207 HashSetBase(const SelfType& src) : pTable(NULL) { Assign(this, src); }
209 ~HashSetBase()
210 {
211 if (pTable)
212 {
213 // Delete the entries.
214 for (size_t i = 0, n = pTable->SizeMask; i <= n; i++)
215 {
216 Entry* e = &E(i);
217 if (!e->IsEmpty())
218 e->Free();
219 }
221 Allocator::Free(pTable);
222 pTable = NULL;
223 }
224 }
227 void Assign(const SelfType& src)
228 {
229 Clear();
230 if (src.IsEmpty() == false)
231 {
232 SetCapacity(src.GetSize());
234 for (ConstIterator it = src.Begin(); it != src.End(); ++it)
235 {
236 Add(*it);
237 }
238 }
239 }
242 // Remove all entries from the HashSet table.
243 void Clear()
244 {
245 if (pTable)
246 {
247 // Delete the entries.
248 for (size_t i = 0, n = pTable->SizeMask; i <= n; i++)
249 {
250 Entry* e = &E(i);
251 if (!e->IsEmpty())
252 e->Clear();
253 }
255 Allocator::Free(pTable);
256 pTable = NULL;
257 }
258 }
260 // Returns true if the HashSet is empty.
261 bool IsEmpty() const
262 {
263 return pTable == NULL || pTable->EntryCount == 0;
264 }
267 // Set a new or existing value under the key, to the value.
268 // Pass a different class of 'key' so that assignment reference object
269 // can be passed instead of the actual object.
270 template<class CRef>
271 void Set(const CRef& key)
272 {
273 size_t hashValue = HashF()(key);
274 intptr_t index = (intptr_t)-1;
276 if (pTable != NULL)
277 index = findIndexCore(key, hashValue & pTable->SizeMask);
279 if (index >= 0)
280 {
281 E(index).Value = key;
282 }
283 else
284 {
285 // Entry under key doesn't exist.
286 add(key, hashValue);
287 }
288 }
290 template<class CRef>
291 inline void Add(const CRef& key)
292 {
293 size_t hashValue = HashF()(key);
294 add(key, hashValue);
295 }
297 // Remove by alternative key.
298 template<class K>
299 void RemoveAlt(const K& key)
300 {
301 if (pTable == NULL)
302 return;
304 size_t hashValue = AltHashF()(key);
305 intptr_t index = hashValue & pTable->SizeMask;
307 Entry* e = &E(index);
309 // If empty node or occupied by collider, we have nothing to remove.
310 if (e->IsEmpty() || (e->GetCachedHash(pTable->SizeMask) != (size_t)index))
311 return;
313 // Save index
314 intptr_t naturalIndex = index;
315 intptr_t prevIndex = -1;
317 while ((e->GetCachedHash(pTable->SizeMask) != (size_t)naturalIndex) || !(e->Value == key))
318 {
319 // Keep looking through the chain.
320 prevIndex = index;
321 index = e->NextInChain;
322 if (index == -1)
323 return; // End of chain, item not found
324 e = &E(index);
325 }
327 // Found it - our item is at index
328 if (naturalIndex == index)
329 {
330 // If we have a follower, move it to us
331 if (!e->IsEndOfChain())
332 {
333 Entry* enext = &E(e->NextInChain);
334 e->Clear();
335 new (e) Entry(*enext);
336 // Point us to the follower's cell that will be cleared
337 e = enext;
338 }
339 }
340 else
341 {
342 // We are not at natural index, so deal with the prev items next index
343 E(prevIndex).NextInChain = e->NextInChain;
344 }
346 // Clear us, of the follower cell that was moved.
347 e->Clear();
348 pTable->EntryCount --;
349 // Should we check the size to condense hash? ...
350 }
352 // Remove by main key.
353 template<class CRef>
354 void Remove(const CRef& key)
355 {
356 RemoveAlt(key);
357 }
359 // Retrieve the pointer to a value under the given key.
360 // - If there's no value under the key, then return NULL.
361 // - If there is a value, return the pointer.
362 template<class K>
363 C* Get(const K& key)
364 {
365 intptr_t index = findIndex(key);
366 if (index >= 0)
367 return &E(index).Value;
368 return 0;
369 }
371 template<class K>
372 const C* Get(const K& key) const
373 {
374 intptr_t index = findIndex(key);
375 if (index >= 0)
376 return &E(index).Value;
377 return 0;
378 }
380 // Alternative key versions of Get. Used by Hash.
381 template<class K>
382 const C* GetAlt(const K& key) const
383 {
384 intptr_t index = findIndexAlt(key);
385 if (index >= 0)
386 return &E(index).Value;
387 return 0;
388 }
390 template<class K>
391 C* GetAlt(const K& key)
392 {
393 intptr_t index = findIndexAlt(key);
394 if (index >= 0)
395 return &E(index).Value;
396 return 0;
397 }
399 template<class K>
400 bool GetAlt(const K& key, C* pval) const
401 {
402 intptr_t index = findIndexAlt(key);
403 if (index >= 0)
404 {
405 if (pval)
406 *pval = E(index).Value;
407 return true;
408 }
409 return false;
410 }
413 size_t GetSize() const
414 {
415 return pTable == NULL ? 0 : (size_t)pTable->EntryCount;
416 }
417 int GetSizeI() const { return (int)GetSize(); }
420 // Resize the HashSet table to fit one more Entry. Often this
421 // doesn't involve any action.
422 void CheckExpand()
423 {
424 if (pTable == NULL)
425 {
426 // Initial creation of table. Make a minimum-sized table.
427 setRawCapacity(HashMinSize);
428 }
429 else if (pTable->EntryCount * 5 > (pTable->SizeMask + 1) * 4)
430 {
431 // pTable is more than 5/4 ths full. Expand.
432 setRawCapacity((pTable->SizeMask + 1) * 2);
433 }
434 }
436 // Hint the bucket count to >= n.
437 void Resize(size_t n)
438 {
439 // Not really sure what this means in relation to
440 // STLport's hash_map... they say they "increase the
441 // bucket count to at least n" -- but does that mean
442 // their real capacity after Resize(n) is more like
443 // n*2 (since they do linked-list chaining within
444 // buckets?).
445 SetCapacity(n);
446 }
448 // Size the HashSet so that it can comfortably contain the given
449 // number of elements. If the HashSet already contains more
450 // elements than newSize, then this may be a no-op.
451 void SetCapacity(size_t newSize)
452 {
453 size_t newRawSize = (newSize * 5) / 4;
454 if (newRawSize <= GetSize())
455 return;
456 setRawCapacity(newRawSize);
457 }
459 // Disable inappropriate 'operator ->' warning on MSVC6.
460 #ifdef OVR_CC_MSVC
461 #if (OVR_CC_MSVC < 1300)
462 # pragma warning(disable : 4284)
463 #endif
464 #endif
466 // Iterator API, like STL.
467 struct ConstIterator
468 {
469 const C& operator * () const
470 {
471 OVR_ASSERT(Index >= 0 && Index <= (intptr_t)pHash->pTable->SizeMask);
472 return pHash->E(Index).Value;
473 }
475 const C* operator -> () const
476 {
477 OVR_ASSERT(Index >= 0 && Index <= (intptr_t)pHash->pTable->SizeMask);
478 return &pHash->E(Index).Value;
479 }
481 void operator ++ ()
482 {
483 // Find next non-empty Entry.
484 if (Index <= (intptr_t)pHash->pTable->SizeMask)
485 {
486 Index++;
487 while ((size_t)Index <= pHash->pTable->SizeMask &&
488 pHash->E(Index).IsEmpty())
489 {
490 Index++;
491 }
492 }
493 }
495 bool operator == (const ConstIterator& it) const
496 {
497 if (IsEnd() && it.IsEnd())
498 {
499 return true;
500 }
501 else
502 {
503 return (pHash == it.pHash) && (Index == it.Index);
504 }
505 }
507 bool operator != (const ConstIterator& it) const
508 {
509 return ! (*this == it);
510 }
513 bool IsEnd() const
514 {
515 return (pHash == NULL) ||
516 (pHash->pTable == NULL) ||
517 (Index > (intptr_t)pHash->pTable->SizeMask);
518 }
520 ConstIterator()
521 : pHash(NULL), Index(0)
522 { }
524 public:
525 // Constructor was intentionally made public to allow create
526 // iterator with arbitrary index.
527 ConstIterator(const SelfType* h, intptr_t index)
528 : pHash(h), Index(index)
529 { }
531 const SelfType* GetContainer() const
532 {
533 return pHash;
534 }
535 intptr_t GetIndex() const
536 {
537 return Index;
538 }
540 protected:
541 friend class HashSetBase<C, HashF, AltHashF, Allocator, Entry>;
543 const SelfType* pHash;
544 intptr_t Index;
545 };
547 friend struct ConstIterator;
550 // Non-const Iterator; Get most of it from ConstIterator.
551 struct Iterator : public ConstIterator
552 {
553 // Allow non-const access to entries.
554 C& operator*() const
555 {
556 OVR_ASSERT((ConstIterator::pHash) && ConstIterator::pHash->pTable && (ConstIterator::Index >= 0) && (ConstIterator::Index <= (intptr_t)ConstIterator::pHash->pTable->SizeMask));
557 return const_cast<SelfType*>(ConstIterator::pHash)->E(ConstIterator::Index).Value;
558 }
560 C* operator->() const
561 {
562 return &(operator*());
563 }
565 Iterator()
566 : ConstIterator(NULL, 0)
567 { }
569 // Removes current element from Hash
570 void Remove()
571 {
572 RemoveAlt(operator*());
573 }
575 template <class K>
576 void RemoveAlt(const K& key)
577 {
578 SelfType* phash = const_cast<SelfType*>(ConstIterator::pHash);
579 //Entry* ee = &phash->E(ConstIterator::Index);
580 //const C& key = ee->Value;
582 size_t hashValue = AltHashF()(key);
583 intptr_t index = hashValue & phash->pTable->SizeMask;
585 Entry* e = &phash->E(index);
587 // If empty node or occupied by collider, we have nothing to remove.
588 if (e->IsEmpty() || (e->GetCachedHash(phash->pTable->SizeMask) != (size_t)index))
589 return;
591 // Save index
592 intptr_t naturalIndex = index;
593 intptr_t prevIndex = -1;
595 while ((e->GetCachedHash(phash->pTable->SizeMask) != (size_t)naturalIndex) || !(e->Value == key))
596 {
597 // Keep looking through the chain.
598 prevIndex = index;
599 index = e->NextInChain;
600 if (index == -1)
601 return; // End of chain, item not found
602 e = &phash->E(index);
603 }
605 if (index == (intptr_t)ConstIterator::Index)
606 {
607 // Found it - our item is at index
608 if (naturalIndex == index)
609 {
610 // If we have a follower, move it to us
611 if (!e->IsEndOfChain())
612 {
613 Entry* enext = &phash->E(e->NextInChain);
614 e->Clear();
615 new (e) Entry(*enext);
616 // Point us to the follower's cell that will be cleared
617 e = enext;
618 --ConstIterator::Index;
619 }
620 }
621 else
622 {
623 // We are not at natural index, so deal with the prev items next index
624 phash->E(prevIndex).NextInChain = e->NextInChain;
625 }
627 // Clear us, of the follower cell that was moved.
628 e->Clear();
629 phash->pTable->EntryCount --;
630 }
631 else
632 OVR_ASSERT(0); //?
633 }
635 private:
636 friend class HashSetBase<C, HashF, AltHashF, Allocator, Entry>;
638 Iterator(SelfType* h, intptr_t i0)
639 : ConstIterator(h, i0)
640 { }
641 };
643 friend struct Iterator;
645 Iterator Begin()
646 {
647 if (pTable == 0)
648 return Iterator(NULL, 0);
650 // Scan till we hit the First valid Entry.
651 size_t i0 = 0;
652 while (i0 <= pTable->SizeMask && E(i0).IsEmpty())
653 {
654 i0++;
655 }
656 return Iterator(this, i0);
657 }
658 Iterator End() { return Iterator(NULL, 0); }
660 ConstIterator Begin() const { return const_cast<SelfType*>(this)->Begin(); }
661 ConstIterator End() const { return const_cast<SelfType*>(this)->End(); }
663 template<class K>
664 Iterator Find(const K& key)
665 {
666 intptr_t index = findIndex(key);
667 if (index >= 0)
668 return Iterator(this, index);
669 return Iterator(NULL, 0);
670 }
672 template<class K>
673 Iterator FindAlt(const K& key)
674 {
675 intptr_t index = findIndexAlt(key);
676 if (index >= 0)
677 return Iterator(this, index);
678 return Iterator(NULL, 0);
679 }
681 template<class K>
682 ConstIterator Find(const K& key) const { return const_cast<SelfType*>(this)->Find(key); }
684 template<class K>
685 ConstIterator FindAlt(const K& key) const { return const_cast<SelfType*>(this)->FindAlt(key); }
687 private:
688 // Find the index of the matching Entry. If no match, then return -1.
689 template<class K>
690 intptr_t findIndex(const K& key) const
691 {
692 if (pTable == NULL)
693 return -1;
694 size_t hashValue = HashF()(key) & pTable->SizeMask;
695 return findIndexCore(key, hashValue);
696 }
698 template<class K>
699 intptr_t findIndexAlt(const K& key) const
700 {
701 if (pTable == NULL)
702 return -1;
703 size_t hashValue = AltHashF()(key) & pTable->SizeMask;
704 return findIndexCore(key, hashValue);
705 }
707 // Find the index of the matching Entry. If no match, then return -1.
708 template<class K>
709 intptr_t findIndexCore(const K& key, size_t hashValue) const
710 {
711 // Table must exist.
712 OVR_ASSERT(pTable != 0);
713 // Hash key must be 'and-ed' by the caller.
714 OVR_ASSERT((hashValue & ~pTable->SizeMask) == 0);
716 size_t index = hashValue;
717 const Entry* e = &E(index);
719 // If empty or occupied by a collider, not found.
720 if (e->IsEmpty() || (e->GetCachedHash(pTable->SizeMask) != index))
721 return -1;
723 while(1)
724 {
725 OVR_ASSERT(e->GetCachedHash(pTable->SizeMask) == hashValue);
727 if (e->GetCachedHash(pTable->SizeMask) == hashValue && e->Value == key)
728 {
729 // Found it.
730 return index;
731 }
732 // Values can not be equal at this point.
733 // That would mean that the hash key for the same value differs.
734 OVR_ASSERT(!(e->Value == key));
736 // Keep looking through the chain.
737 index = e->NextInChain;
738 if (index == (size_t)-1)
739 break; // end of chain
741 e = &E(index);
742 OVR_ASSERT(!e->IsEmpty());
743 }
744 return -1;
745 }
748 // Add a new value to the HashSet table, under the specified key.
749 template<class CRef>
750 void add(const CRef& key, size_t hashValue)
751 {
752 CheckExpand();
753 hashValue &= pTable->SizeMask;
755 pTable->EntryCount++;
757 intptr_t index = hashValue;
758 Entry* naturalEntry = &(E(index));
760 if (naturalEntry->IsEmpty())
761 {
762 // Put the new Entry in.
763 new (naturalEntry) Entry(key, -1);
764 }
765 else
766 {
767 // Find a blank spot.
768 intptr_t blankIndex = index;
769 do {
770 blankIndex = (blankIndex + 1) & pTable->SizeMask;
771 } while(!E(blankIndex).IsEmpty());
773 Entry* blankEntry = &E(blankIndex);
775 if (naturalEntry->GetCachedHash(pTable->SizeMask) == (size_t)index)
776 {
777 // Collision. Link into this chain.
779 // Move existing list head.
780 new (blankEntry) Entry(*naturalEntry); // placement new, copy ctor
782 // Put the new info in the natural Entry.
783 naturalEntry->Value = key;
784 naturalEntry->NextInChain = blankIndex;
785 }
786 else
787 {
788 // Existing Entry does not naturally
789 // belong in this slot. Existing
790 // Entry must be moved.
792 // Find natural location of collided element (i.e. root of chain)
793 intptr_t collidedIndex = naturalEntry->GetCachedHash(pTable->SizeMask);
794 OVR_ASSERT(collidedIndex >= 0 && collidedIndex <= (intptr_t)pTable->SizeMask);
795 for (;;)
796 {
797 Entry* e = &E(collidedIndex);
798 if (e->NextInChain == index)
799 {
800 // Here's where we need to splice.
801 new (blankEntry) Entry(*naturalEntry);
802 e->NextInChain = blankIndex;
803 break;
804 }
805 collidedIndex = e->NextInChain;
806 OVR_ASSERT(collidedIndex >= 0 && collidedIndex <= (intptr_t)pTable->SizeMask);
807 }
809 // Put the new data in the natural Entry.
810 naturalEntry->Value = key;
811 naturalEntry->NextInChain = -1;
812 }
813 }
815 // Record hash value: has effect only if cached node is used.
816 naturalEntry->SetCachedHash(hashValue);
817 }
819 // Index access helpers.
820 Entry& E(size_t index)
821 {
822 // Must have pTable and access needs to be within bounds.
823 OVR_ASSERT(index <= pTable->SizeMask);
824 return *(((Entry*) (pTable + 1)) + index);
825 }
826 const Entry& E(size_t index) const
827 {
828 OVR_ASSERT(index <= pTable->SizeMask);
829 return *(((Entry*) (pTable + 1)) + index);
830 }
833 // Resize the HashSet table to the given size (Rehash the
834 // contents of the current table). The arg is the number of
835 // HashSet table entries, not the number of elements we should
836 // actually contain (which will be less than this).
837 void setRawCapacity(size_t newSize)
838 {
839 if (newSize == 0)
840 {
841 // Special case.
842 Clear();
843 return;
844 }
846 // Minimum size; don't incur rehashing cost when expanding
847 // very small tables. Not that we perform this check before
848 // 'log2f' call to avoid fp exception with newSize == 1.
849 if (newSize < HashMinSize)
850 newSize = HashMinSize;
851 else
852 {
853 // Force newSize to be a power of two.
854 int bits = Alg::UpperBit(newSize-1) + 1; // Chop( Log2f((float)(newSize-1)) + 1);
855 OVR_ASSERT((size_t(1) << bits) >= newSize);
856 newSize = size_t(1) << bits;
857 }
859 SelfType newHash;
860 newHash.pTable = (TableType*)
861 Allocator::Alloc(
862 sizeof(TableType) + sizeof(Entry) * newSize);
863 // Need to do something on alloc failure!
864 OVR_ASSERT(newHash.pTable);
866 newHash.pTable->EntryCount = 0;
867 newHash.pTable->SizeMask = newSize - 1;
868 size_t i, n;
870 // Mark all entries as empty.
871 for (i = 0; i < newSize; i++)
872 newHash.E(i).NextInChain = -2;
874 // Copy stuff to newHash
875 if (pTable)
876 {
877 for (i = 0, n = pTable->SizeMask; i <= n; i++)
878 {
879 Entry* e = &E(i);
880 if (e->IsEmpty() == false)
881 {
882 // Insert old Entry into new HashSet.
883 newHash.Add(e->Value);
884 // placement delete of old element
885 e->Clear();
886 }
887 }
889 // Delete our old data buffer.
890 Allocator::Free(pTable);
891 }
893 // Steal newHash's data.
894 pTable = newHash.pTable;
895 newHash.pTable = NULL;
896 }
898 struct TableType
899 {
900 size_t EntryCount;
901 size_t SizeMask;
902 // Entry array follows this structure
903 // in memory.
904 };
905 TableType* pTable;
906 };
910 //-----------------------------------------------------------------------------------
911 template<class C, class HashF = FixedSizeHash<C>,
912 class AltHashF = HashF,
913 class Allocator = ContainerAllocator<C>,
914 class Entry = HashsetCachedEntry<C, HashF> >
915 class HashSet : public HashSetBase<C, HashF, AltHashF, Allocator, Entry>
916 {
917 public:
918 typedef HashSetBase<C, HashF, AltHashF, Allocator, Entry> BaseType;
919 typedef HashSet<C, HashF, AltHashF, Allocator, Entry> SelfType;
921 HashSet() { }
922 HashSet(int sizeHint) : BaseType(sizeHint) { }
923 HashSet(const SelfType& src) : BaseType(src) { }
924 ~HashSet() { }
926 void operator = (const SelfType& src) { BaseType::Assign(src); }
928 // Set a new or existing value under the key, to the value.
929 // Pass a different class of 'key' so that assignment reference object
930 // can be passed instead of the actual object.
931 template<class CRef>
932 void Set(const CRef& key)
933 {
934 BaseType::Set(key);
935 }
937 template<class CRef>
938 inline void Add(const CRef& key)
939 {
940 BaseType::Add(key);
941 }
943 // Hint the bucket count to >= n.
944 void Resize(size_t n)
945 {
946 BaseType::SetCapacity(n);
947 }
949 // Size the HashSet so that it can comfortably contain the given
950 // number of elements. If the HashSet already contains more
951 // elements than newSize, then this may be a no-op.
952 void SetCapacity(size_t newSize)
953 {
954 BaseType::SetCapacity(newSize);
955 }
957 };
959 // HashSet with uncached hash code; declared for convenience.
960 template<class C, class HashF = FixedSizeHash<C>,
961 class AltHashF = HashF,
962 class Allocator = ContainerAllocator<C> >
963 class HashSetUncached : public HashSet<C, HashF, AltHashF, Allocator, HashsetEntry<C, HashF> >
964 {
965 public:
967 typedef HashSetUncached<C, HashF, AltHashF, Allocator> SelfType;
968 typedef HashSet<C, HashF, AltHashF, Allocator, HashsetEntry<C, HashF> > BaseType;
970 // Delegated constructors.
971 HashSetUncached() { }
972 HashSetUncached(int sizeHint) : BaseType(sizeHint) { }
973 HashSetUncached(const SelfType& src) : BaseType(src) { }
974 ~HashSetUncached() { }
976 void operator = (const SelfType& src)
977 {
978 BaseType::operator = (src);
979 }
980 };
983 //-----------------------------------------------------------------------------------
984 // ***** Hash hash table implementation
986 // Node for Hash - necessary so that Hash can delegate its implementation
987 // to HashSet.
988 template<class C, class U, class HashF>
989 struct HashNode
990 {
991 typedef HashNode<C, U, HashF> SelfType;
992 typedef C FirstType;
993 typedef U SecondType;
995 C First;
996 U Second;
998 // NodeRef is used to allow passing of elements into HashSet
999 // without using a temporary object.
1000 struct NodeRef
1002 const C* pFirst;
1003 const U* pSecond;
1005 NodeRef(const C& f, const U& s) : pFirst(&f), pSecond(&s) { }
1006 NodeRef(const NodeRef& src) : pFirst(src.pFirst), pSecond(src.pSecond) { }
1008 // Enable computation of ghash_node_hashf.
1009 inline size_t GetHash() const { return HashF()(*pFirst); }
1010 // Necessary conversion to allow HashNode::operator == to work.
1011 operator const C& () const { return *pFirst; }
1012 };
1014 // Note: No default constructor is necessary.
1015 HashNode(const HashNode& src) : First(src.First), Second(src.Second) { }
1016 HashNode(const NodeRef& src) : First(*src.pFirst), Second(*src.pSecond) { }
1017 void operator = (const NodeRef& src) { First = *src.pFirst; Second = *src.pSecond; }
1019 template<class K>
1020 bool operator == (const K& src) const { return (First == src); }
1022 template<class K>
1023 static size_t CalcHash(const K& data) { return HashF()(data); }
1024 inline size_t GetHash() const { return HashF()(First); }
1026 // Hash functors used with this node. A separate functor is used for alternative
1027 // key lookup so that it does not need to access the '.First' element.
1028 struct NodeHashF
1030 template<class K>
1031 size_t operator()(const K& data) const { return data.GetHash(); }
1032 };
1033 struct NodeAltHashF
1035 template<class K>
1036 size_t operator()(const K& data) const { return HashNode<C,U,HashF>::CalcHash(data); }
1037 };
1038 };
1042 // **** Extra hashset_entry types to allow NodeRef construction.
1044 // The big difference between the below types and the ones used in hash_set is that
1045 // these allow initializing the node with 'typename C::NodeRef& keyRef', which
1046 // is critical to avoid temporary node allocation on stack when using placement new.
1048 // Compact hash table Entry type that re-computes hash keys during hash traversal.
1049 // Good to use if the hash function is cheap or the hash value is already cached in C.
1050 template<class C, class HashF>
1051 class HashsetNodeEntry
1053 public:
1054 // Internal chaining for collisions.
1055 intptr_t NextInChain;
1056 C Value;
1058 HashsetNodeEntry()
1059 : NextInChain(-2) { }
1060 HashsetNodeEntry(const HashsetNodeEntry& e)
1061 : NextInChain(e.NextInChain), Value(e.Value) { }
1062 HashsetNodeEntry(const C& key, intptr_t next)
1063 : NextInChain(next), Value(key) { }
1064 HashsetNodeEntry(const typename C::NodeRef& keyRef, intptr_t next)
1065 : NextInChain(next), Value(keyRef) { }
1067 bool IsEmpty() const { return NextInChain == -2; }
1068 bool IsEndOfChain() const { return NextInChain == -1; }
1069 size_t GetCachedHash(size_t maskValue) const { return HashF()(Value) & maskValue; }
1070 void SetCachedHash(size_t hashValue) { OVR_UNUSED(hashValue); }
1072 void Clear()
1074 Value.~C(); // placement delete
1075 NextInChain = -2;
1077 // Free is only used from dtor of hash; Clear is used during regular operations:
1078 // assignment, hash reallocations, value reassignments, so on.
1079 void Free() { Clear(); }
1080 };
1082 // Hash table Entry type that caches the Entry hash value for nodes, so that it
1083 // does not need to be re-computed during access.
1084 template<class C, class HashF>
1085 class HashsetCachedNodeEntry
1087 public:
1088 // Internal chaining for collisions.
1089 intptr_t NextInChain;
1090 size_t HashValue;
1091 C Value;
1093 HashsetCachedNodeEntry()
1094 : NextInChain(-2) { }
1095 HashsetCachedNodeEntry(const HashsetCachedNodeEntry& e)
1096 : NextInChain(e.NextInChain), HashValue(e.HashValue), Value(e.Value) { }
1097 HashsetCachedNodeEntry(const C& key, intptr_t next)
1098 : NextInChain(next), Value(key) { }
1099 HashsetCachedNodeEntry(const typename C::NodeRef& keyRef, intptr_t next)
1100 : NextInChain(next), Value(keyRef) { }
1102 bool IsEmpty() const { return NextInChain == -2; }
1103 bool IsEndOfChain() const { return NextInChain == -1; }
1104 size_t GetCachedHash(size_t maskValue) const { OVR_UNUSED(maskValue); return HashValue; }
1105 void SetCachedHash(size_t hashValue) { HashValue = hashValue; }
1107 void Clear()
1109 Value.~C();
1110 NextInChain = -2;
1112 // Free is only used from dtor of hash; Clear is used during regular operations:
1113 // assignment, hash reallocations, value reassignments, so on.
1114 void Free() { Clear(); }
1115 };
1118 //-----------------------------------------------------------------------------------
1119 template<class C, class U,
1120 class HashF = FixedSizeHash<C>,
1121 class Allocator = ContainerAllocator<C>,
1122 class HashNode = OVR::HashNode<C,U,HashF>,
1123 class Entry = HashsetCachedNodeEntry<HashNode, typename HashNode::NodeHashF>,
1124 class Container = HashSet<HashNode, typename HashNode::NodeHashF,
1125 typename HashNode::NodeAltHashF, Allocator,
1126 Entry> >
1127 class Hash
1129 public:
1130 OVR_MEMORY_REDEFINE_NEW(Hash)
1132 // Types used for hash_set.
1133 typedef U ValueType;
1134 typedef Hash<C, U, HashF, Allocator, HashNode, Entry, Container> SelfType;
1136 // Actual hash table itself, implemented as hash_set.
1137 Container mHash;
1139 public:
1140 Hash() { }
1141 Hash(int sizeHint) : mHash(sizeHint) { }
1142 Hash(const SelfType& src) : mHash(src.mHash) { }
1143 ~Hash() { }
1145 void operator = (const SelfType& src) { mHash = src.mHash; }
1147 // Remove all entries from the Hash table.
1148 inline void Clear() { mHash.Clear(); }
1149 // Returns true if the Hash is empty.
1150 inline bool IsEmpty() const { return mHash.IsEmpty(); }
1152 // Access (set).
1153 inline void Set(const C& key, const U& value)
1155 typename HashNode::NodeRef e(key, value);
1156 mHash.Set(e);
1158 inline void Add(const C& key, const U& value)
1160 typename HashNode::NodeRef e(key, value);
1161 mHash.Add(e);
1164 // Removes an element by clearing its Entry.
1165 inline void Remove(const C& key)
1167 mHash.RemoveAlt(key);
1169 template<class K>
1170 inline void RemoveAlt(const K& key)
1172 mHash.RemoveAlt(key);
1175 // Retrieve the value under the given key.
1176 // - If there's no value under the key, then return false and leave *pvalue alone.
1177 // - If there is a value, return true, and Set *Pvalue to the Entry's value.
1178 // - If value == NULL, return true or false according to the presence of the key.
1179 bool Get(const C& key, U* pvalue) const
1181 const HashNode* p = mHash.GetAlt(key);
1182 if (p)
1184 if (pvalue)
1185 *pvalue = p->Second;
1186 return true;
1188 return false;
1191 template<class K>
1192 bool GetAlt(const K& key, U* pvalue) const
1194 const HashNode* p = mHash.GetAlt(key);
1195 if (p)
1197 if (pvalue)
1198 *pvalue = p->Second;
1199 return true;
1201 return false;
1204 // Retrieve the pointer to a value under the given key.
1205 // - If there's no value under the key, then return NULL.
1206 // - If there is a value, return the pointer.
1207 inline U* Get(const C& key)
1209 HashNode* p = mHash.GetAlt(key);
1210 return p ? &p->Second : 0;
1212 inline const U* Get(const C& key) const
1214 const HashNode* p = mHash.GetAlt(key);
1215 return p ? &p->Second : 0;
1218 template<class K>
1219 inline U* GetAlt(const K& key)
1221 HashNode* p = mHash.GetAlt(key);
1222 return p ? &p->Second : 0;
1224 template<class K>
1225 inline const U* GetAlt(const K& key) const
1227 const HashNode* p = mHash.GetAlt(key);
1228 return p ? &p->Second : 0;
1231 // Sizing methods - delegate to Hash.
1232 inline size_t GetSize() const { return mHash.GetSize(); }
1233 inline int GetSizeI() const { return (int)GetSize(); }
1234 inline void Resize(size_t n) { mHash.Resize(n); }
1235 inline void SetCapacity(size_t newSize) { mHash.SetCapacity(newSize); }
1237 // Iterator API, like STL.
1238 typedef typename Container::ConstIterator ConstIterator;
1239 typedef typename Container::Iterator Iterator;
1241 inline Iterator Begin() { return mHash.Begin(); }
1242 inline Iterator End() { return mHash.End(); }
1243 inline ConstIterator Begin() const { return mHash.Begin(); }
1244 inline ConstIterator End() const { return mHash.End(); }
1246 Iterator Find(const C& key) { return mHash.FindAlt(key); }
1247 ConstIterator Find(const C& key) const { return mHash.FindAlt(key); }
1249 template<class K>
1250 Iterator FindAlt(const K& key) { return mHash.FindAlt(key); }
1251 template<class K>
1252 ConstIterator FindAlt(const K& key) const { return mHash.FindAlt(key); }
1253 };
1257 // Hash with uncached hash code; declared for convenience.
1258 template<class C, class U, class HashF = FixedSizeHash<C>, class Allocator = ContainerAllocator<C> >
1259 class HashUncached
1260 : public Hash<C, U, HashF, Allocator, HashNode<C,U,HashF>,
1261 HashsetNodeEntry<HashNode<C,U,HashF>, typename HashNode<C,U,HashF>::NodeHashF> >
1263 public:
1264 typedef HashUncached<C, U, HashF, Allocator> SelfType;
1265 typedef Hash<C, U, HashF, Allocator, HashNode<C,U,HashF>,
1266 HashsetNodeEntry<HashNode<C,U,HashF>,
1267 typename HashNode<C,U,HashF>::NodeHashF> > BaseType;
1269 // Delegated constructors.
1270 HashUncached() { }
1271 HashUncached(int sizeHint) : BaseType(sizeHint) { }
1272 HashUncached(const SelfType& src) : BaseType(src) { }
1273 ~HashUncached() { }
1274 void operator = (const SelfType& src) { BaseType::operator = (src); }
1275 };
1279 // And identity hash in which keys serve as hash value. Can be uncached,
1280 // since hash computation is assumed cheap.
1281 template<class C, class U, class Allocator = ContainerAllocator<C>, class HashF = IdentityHash<C> >
1282 class HashIdentity
1283 : public HashUncached<C, U, HashF, Allocator>
1285 public:
1286 typedef HashIdentity<C, U, Allocator, HashF> SelfType;
1287 typedef HashUncached<C, U, HashF, Allocator> BaseType;
1289 // Delegated constructors.
1290 HashIdentity() { }
1291 HashIdentity(int sizeHint) : BaseType(sizeHint) { }
1292 HashIdentity(const SelfType& src) : BaseType(src) { }
1293 ~HashIdentity() { }
1294 void operator = (const SelfType& src) { BaseType::operator = (src); }
1295 };
1298 } // OVR
1301 #ifdef OVR_DEFINE_NEW
1302 #define new OVR_DEFINE_NEW
1303 #endif
1305 #endif