Nebula
Loading...
Searching...
No Matches
hashtable.h
Go to the documentation of this file.
1#pragma once
2//------------------------------------------------------------------------------
32#include "util/fixedarray.h"
33#include "util/array.h"
34#include "util/keyvaluepair.h"
35#include "math/scalar.h"
36#include <type_traits>
37
38//------------------------------------------------------------------------------
39namespace Util
40{
41template<class KEYTYPE, class VALUETYPE, int TABLE_SIZE = 128, int STACK_SIZE = 1> class HashTable
42{
43public:
55 VALUETYPE& operator[](const KEYTYPE& key) const;
57 SizeT Size() const;
59 SizeT Capacity() const;
61 void Clear();
63 void Reset();
65 bool IsEmpty() const;
69 const bool IsBulkAdd() const;
73 IndexT Add(const KEYTYPE& key, const VALUETYPE& value);
75 VALUETYPE& Emplace(const KEYTYPE& key);
77 void EndBulkAdd();
81 void Erase(const KEYTYPE& key);
83 void EraseIndex(const KEYTYPE& key, IndexT i);
85 bool Contains(const KEYTYPE& key) const;
87 IndexT FindIndex(const KEYTYPE& key) const;
89 VALUETYPE& ValueAtIndex(const KEYTYPE& key, IndexT i) const;
96
98 {
99 public:
100
102 Iterator& operator++(int);
104 const bool operator==(const Iterator& rhs) const;
106 const bool operator!=(const Iterator& rhs) const;
107
109 VALUETYPE* val;
110 KEYTYPE const* key;
111 private:
112 friend class HashTable<KEYTYPE, VALUETYPE, TABLE_SIZE, STACK_SIZE>;
114 uint32_t hashIndex;
116 };
117
119 Iterator Begin();
121 Iterator End();
122private:
124 int size;
125
128
130 template <typename HASHKEY> const uint32_t GetHashCode(const typename std::enable_if<std::is_integral<HASHKEY>::value, HASHKEY>::type& key) const { using UNSIGNED = typename std::make_unsigned<HASHKEY>::type; return uint32_t(UNSIGNED(key) % UNSIGNED(this->hashArray.Size())); };
132 template <typename HASHKEY> const uint32_t GetHashCode(const HASHKEY& key) const { return key.HashCode() % this->hashArray.Size(); };
134 template <typename HASHKEY> const uint32_t GetHashCode(const typename std::enable_if<std::is_pointer<HASHKEY>::value, HASHKEY>::type& key) const { return key->HashCode() % this->hashArray.Size(); }
135};
136
137//------------------------------------------------------------------------------
140template<class KEYTYPE, class VALUETYPE, int TABLE_SIZE, int STACK_SIZE>
143{
145 for (int i = 0; i < this->hashArray.Size(); i++)
146 {
147 for (int j = 0; j < this->hashArray[i].Size();j++)
148 {
149 keys.Append(this->hashArray[i][j].Key());
150 }
151 }
152 return keys;
153}
154
155//------------------------------------------------------------------------------
158template<class KEYTYPE, class VALUETYPE, int TABLE_SIZE, int STACK_SIZE>
161{
163 for (int i = 0; i < this->hashArray.Size(); i++)
164 {
165 for (int j = 0; j < this->hashArray[i].Size(); j++)
166 {
167 vals.Append(this->hashArray[i][j].Value());
168 }
169 }
170 return vals;
171}
172
173//------------------------------------------------------------------------------
176template<class KEYTYPE, class VALUETYPE, int TABLE_SIZE, int STACK_SIZE>
179{
180 Iterator ret;
181 ret.hashIndex = this->hashArray.Size();
182 ret.bucketIndex = 0;
183 ret.val = nullptr;
184 ret.key = nullptr;
185 ret.arr = &this->hashArray;
186 IndexT i;
187 for (i = 0; i < this->hashArray.Size(); i++)
188 {
189 if (this->hashArray[i].Size() != 0)
190 {
191 ret.hashIndex = i;
192 ret.bucketIndex = 0;
193 ret.val = &this->hashArray[i].Front().Value();
194 ret.key = &this->hashArray[i].Front().Key();
195 break;
196 }
197 }
198 return ret;
199}
200
201//------------------------------------------------------------------------------
204template<class KEYTYPE, class VALUETYPE, int TABLE_SIZE, int STACK_SIZE>
207{
208 Iterator ret;
209 ret.hashIndex = this->hashArray.Size();
210 ret.bucketIndex = 0;
211 ret.val = nullptr;
212 ret.key = nullptr;
213 ret.arr = &this->hashArray;
214 return ret;
215}
216
217//------------------------------------------------------------------------------
220template<class KEYTYPE, class VALUETYPE, int TABLE_SIZE, int STACK_SIZE>
222 hashArray(TABLE_SIZE),
223 bulkDirty(TABLE_SIZE),
224 inBulkAdd(false),
225 size(0)
226{
227 // empty
228}
229
230//------------------------------------------------------------------------------
233template<class KEYTYPE, class VALUETYPE, int TABLE_SIZE, int STACK_SIZE>
242
243//------------------------------------------------------------------------------
246template<class KEYTYPE, class VALUETYPE, int TABLE_SIZE, int STACK_SIZE>
255
256//------------------------------------------------------------------------------
259template<class KEYTYPE, class VALUETYPE, int TABLE_SIZE, int STACK_SIZE>
260void
262{
263 if (this != &rhs)
264 {
265 this->hashArray = rhs.hashArray;
266 this->bulkDirty = rhs.bulkDirty;
267 this->size = rhs.size;
268 }
269}
270
271//------------------------------------------------------------------------------
274template<class KEYTYPE, class VALUETYPE, int TABLE_SIZE, int STACK_SIZE>
275void
277{
278 if (this != &rhs)
279 {
280 this->hashArray = std::move(rhs.hashArray);
281 this->bulkDirty = rhs.bulkDirty;
282 this->inBulkAdd = rhs.inBulkAdd;
283 this->size = rhs.size;
284 rhs.inBulkAdd = false;
285 rhs.size = 0;
286 }
287}
288
289//------------------------------------------------------------------------------
292template<class KEYTYPE, class VALUETYPE, int TABLE_SIZE, int STACK_SIZE>
293VALUETYPE&
295{
296 // get hash code from key, trim to capacity
297 n_assert(!this->inBulkAdd);
298 uint32_t hashIndex = GetHashCode<KEYTYPE>(key);
299 const StackArray<KeyValuePair<KEYTYPE, VALUETYPE>, STACK_SIZE>& hashElements = this->hashArray[hashIndex];
300 int numHashElements = hashElements.Size();
301 #if NEBULA_BOUNDSCHECKS
302 n_assert(0 != numHashElements); // element with key doesn't exist
303 #endif
304 IndexT hashElementIndex = InvalidIndex;
305 if (numHashElements > 0)
306 {
307 // here's a hash collision, find the right key
308 // with a binary search
309 hashElementIndex = hashElements.template BinarySearchIndex<KEYTYPE>(key);
310 }
311 #if NEBULA_BOUNDSCHECKS
312 n_assert(InvalidIndex != hashElementIndex);
313 #endif
314 return hashElements[hashElementIndex].Value();
315}
316
317//------------------------------------------------------------------------------
320template<class KEYTYPE, class VALUETYPE, int TABLE_SIZE, int STACK_SIZE>
321SizeT
326
327//------------------------------------------------------------------------------
330template<class KEYTYPE, class VALUETYPE, int TABLE_SIZE, int STACK_SIZE>
331SizeT
336
337//------------------------------------------------------------------------------
340template<class KEYTYPE, class VALUETYPE, int TABLE_SIZE, int STACK_SIZE>
341void
343{
344 IndexT i;
345 SizeT num = this->hashArray.Size();
346 for (i = 0; i < num; i++)
347 {
348 this->hashArray[i].Clear();
349 }
350 this->size = 0;
351}
352
353//------------------------------------------------------------------------------
356template<class KEYTYPE, class VALUETYPE, int TABLE_SIZE, int STACK_SIZE>
357void
359{
360 IndexT i;
361 SizeT num = this->hashArray.Size();
362 for (i = 0; i < num; i++)
363 {
364 this->hashArray[i].Reset();
365 }
366 this->size = 0;
367}
368
369//------------------------------------------------------------------------------
372template<class KEYTYPE, class VALUETYPE, int TABLE_SIZE, int STACK_SIZE>
373bool
378
379//------------------------------------------------------------------------------
382template<class KEYTYPE, class VALUETYPE, int TABLE_SIZE, int STACK_SIZE>
383void
385{
386 n_assert(!this->inBulkAdd);
387 this->inBulkAdd = true;
388 this->bulkDirty.Fill(false);
389}
390
391//------------------------------------------------------------------------------
394template<class KEYTYPE, class VALUETYPE, int TABLE_SIZE, int STACK_SIZE>
395inline const bool
400
401//------------------------------------------------------------------------------
404template<class KEYTYPE, class VALUETYPE, int TABLE_SIZE, int STACK_SIZE>
405IndexT
407{
408#if NEBULA_BOUNDSCHECKS
409 n_assert(!this->Contains(kvp.Key()));
410#endif
411 uint32_t hashIndex = GetHashCode<KEYTYPE>(kvp.Key());
412 IndexT ret;
413 if (this->inBulkAdd)
414 {
415 ret = this->hashArray[hashIndex].Size();
416 this->hashArray[hashIndex].Append(kvp);
417 this->bulkDirty[hashIndex] = true;
418 }
419 else
420 ret = this->hashArray[hashIndex].InsertSorted(kvp);
421
422 this->size++;
423 return ret;
424}
425
426//------------------------------------------------------------------------------
429template<class KEYTYPE, class VALUETYPE, int TABLE_SIZE, int STACK_SIZE>
430IndexT
431HashTable<KEYTYPE, VALUETYPE, TABLE_SIZE, STACK_SIZE>::Add(const KEYTYPE& key, const VALUETYPE& value)
432{
433 KeyValuePair<KEYTYPE, VALUETYPE> kvp(key, value);
434 return this->Add(kvp);
435}
436
437//------------------------------------------------------------------------------
440template<class KEYTYPE, class VALUETYPE, int TABLE_SIZE, int STACK_SIZE>
441VALUETYPE&
443{
444 // get hash code from key, trim to capacity
445 uint32_t hashIndex = GetHashCode<KEYTYPE>(key);
446 IndexT elementIndex = InvalidIndex;
447 StackArray<KeyValuePair<KEYTYPE, VALUETYPE>, STACK_SIZE>& hashElements = this->hashArray[hashIndex];
448 if (hashElements.Size() == 0)
449 {
450 elementIndex = this->Add(key, VALUETYPE());
451 }
452 else
453 {
454 // binary search requires the array to be sorted, which it isn't if we're in bulk add mode
455 if (this->inBulkAdd)
456 elementIndex = hashElements.template FindIndex<KEYTYPE>(key);
457 else
458 elementIndex = hashElements.template BinarySearchIndex<KEYTYPE>(key);
459
460 if (elementIndex == InvalidIndex)
461 {
462 elementIndex = this->Add(key, VALUETYPE());
463 }
464 }
465 return hashElements[elementIndex].Value();
466}
467
468//------------------------------------------------------------------------------
471template<class KEYTYPE, class VALUETYPE, int TABLE_SIZE, int STACK_SIZE>
472void
474{
475 n_assert(this->inBulkAdd);
476 IndexT i;
477 for (i = 0; i < this->bulkDirty.Size(); i++)
478 {
479 if (this->bulkDirty[i])
480 {
481 this->hashArray[i].Sort();
482 this->bulkDirty[i] = false;
483 }
484 }
485 this->inBulkAdd = false;
486}
487
488//------------------------------------------------------------------------------
491template<class KEYTYPE, class VALUETYPE, int TABLE_SIZE, int STACK_SIZE>
492void
494{
495 n_assert(this->hashArray.Size() == rhs.hashArray.Size());
496 IndexT i;
497 for (i = 0; i < rhs.hashArray.Size(); i++)
498 {
499 IndexT j;
500 for (j = 0; j < rhs.hashArray[i].Size(); j++)
501 this->Add(rhs.hashArray[i][j]);
502 }
503}
504
505//------------------------------------------------------------------------------
508template<class KEYTYPE, class VALUETYPE, int TABLE_SIZE, int STACK_SIZE>
509void
511{
512 #if NEBULA_BOUNDSCHECKS
513 n_assert(!this->inBulkAdd);
514 n_assert(this->size > 0);
515 #endif
516 uint32_t hashIndex = GetHashCode<KEYTYPE>(key);
517 StackArray<KeyValuePair<KEYTYPE, VALUETYPE>, STACK_SIZE>& hashElements = this->hashArray[hashIndex];
518 IndexT hashElementIndex = hashElements.template BinarySearchIndex<KEYTYPE>(key);
519 #if NEBULA_BOUNDSCHECKS
520 n_assert(InvalidIndex != hashElementIndex); // key doesn't exist
521 #endif
522 hashElements.EraseIndex(hashElementIndex);
523 this->size--;
524}
525
526//------------------------------------------------------------------------------
529template<class KEYTYPE, class VALUETYPE, int TABLE_SIZE, int STACK_SIZE>
530void
532{
533#if NEBULA_BOUNDSCHECKS
534 n_assert(this->size > 0);
535#endif
536 uint32_t hashIndex = GetHashCode<KEYTYPE>(key);
537 StackArray<KeyValuePair<KEYTYPE, VALUETYPE>, STACK_SIZE>& hashElements = this->hashArray[hashIndex];
538#if NEBULA_BOUNDSCHECKS
539 n_assert(InvalidIndex != index); // key doesn't exist
540#endif
541 hashElements.EraseIndex(index);
542 this->size--;
543}
544
545//------------------------------------------------------------------------------
548template<class KEYTYPE, class VALUETYPE, int TABLE_SIZE, int STACK_SIZE>
549bool
551{
552 if (this->size > 0)
553 {
554 uint32_t hashIndex = GetHashCode<KEYTYPE>(key);
555 StackArray<KeyValuePair<KEYTYPE, VALUETYPE>, STACK_SIZE>& hashElements = this->hashArray[hashIndex];
556 if (hashElements.Size() == 0) return false;
557 else
558 {
559 IndexT hashElementIndex;
560 if (this->inBulkAdd)
561 hashElementIndex = hashElements.template FindIndex<KEYTYPE>(key);
562 else
563 hashElementIndex = hashElements.template BinarySearchIndex<KEYTYPE>(key);
564 return (InvalidIndex != hashElementIndex);
565 }
566 }
567 else
568 {
569 return false;
570 }
571}
572
573//------------------------------------------------------------------------------
576template<class KEYTYPE, class VALUETYPE, int TABLE_SIZE, int STACK_SIZE>
577IndexT
579{
580 uint32_t hashIndex = GetHashCode<KEYTYPE>(key);
581 StackArray<KeyValuePair<KEYTYPE, VALUETYPE>, STACK_SIZE>& hashElements = this->hashArray[hashIndex];
582 IndexT hashElementIndex;
583 if (this->inBulkAdd)
584 hashElementIndex = hashElements.template FindIndex<KEYTYPE>(key);
585 else
586 hashElementIndex = hashElements.template BinarySearchIndex<KEYTYPE>(key);
587 return hashElementIndex;
588}
589
590//------------------------------------------------------------------------------
593template<class KEYTYPE, class VALUETYPE, int TABLE_SIZE, int STACK_SIZE>
594VALUETYPE&
596{
597 uint32_t hashIndex = GetHashCode<KEYTYPE>(key);
598 StackArray<KeyValuePair<KEYTYPE, VALUETYPE>, STACK_SIZE>& hashElements = this->hashArray[hashIndex];
599 return hashElements[i].Value();
600}
601
602//------------------------------------------------------------------------------
605template<class KEYTYPE, class VALUETYPE, int TABLE_SIZE, int STACK_SIZE>
608{
610 int i;
611 int num = this->hashArray.Size();
612 for (i = 0; i < num; i++)
613 {
614 if (this->hashArray[i].Size() > 0)
615 {
616 result.AppendArray(this->hashArray[i]);
617 }
618 }
619 return result;
620}
621
622//------------------------------------------------------------------------------
625template<class KEYTYPE, class VALUETYPE, int TABLE_SIZE, int STACK_SIZE>
628{
629 const FixedArray<StackArray<KeyValuePair<KEYTYPE, VALUETYPE>, STACK_SIZE> >& arr = *this->arr;
630
631 if (this->hashIndex >= arr.Size())
632 {
633 // already at end, do nothing
634 return *this;
635 }
636 // always increment bucket index
637 this->bucketIndex++;
638 if (arr[this->hashIndex].Size() > this->bucketIndex)
639 {
640 this->val = &arr[this->hashIndex][this->bucketIndex].Value();
641 this->key = &arr[this->hashIndex][this->bucketIndex].Key();
642 }
643 else
644 {
645 // go through remainding hash slots and update iterator if an array is empty
646 IndexT i;
647 for (i = this->hashIndex + 1; i < arr.Size(); i++)
648 {
649 if (arr[i].Size() > 0)
650 {
651 this->hashIndex = i;
652 this->bucketIndex = 0;
653 this->val = &arr[i][this->bucketIndex].Value();
654 this->key = &arr[i][this->bucketIndex].Key();
655 break;
656 }
657 }
658
659 // no bucket found, set to 'end'
660 if (i == arr.Size())
661 {
662 this->hashIndex = arr.Size();
663 this->bucketIndex = 0;
664 this->val = nullptr;
665 this->key = nullptr;
666 }
667 }
668 return *this;
669}
670
671//------------------------------------------------------------------------------
674template<class KEYTYPE, class VALUETYPE, int TABLE_SIZE, int STACK_SIZE>
675const bool
677{
678 return (this->arr == rhs.arr) && (this->hashIndex == rhs.hashIndex) && (this->bucketIndex == rhs.bucketIndex);
679}
680
681//------------------------------------------------------------------------------
684template<class KEYTYPE, class VALUETYPE, int TABLE_SIZE, int STACK_SIZE>
685const bool
687{
688 return !(*this == rhs);
689}
690} // namespace Util
691//------------------------------------------------------------------------------
void Append(const TYPE &first, const ELEM_TYPE &... elements)
Append multiple elements to the end of the array.
Definition array.h:1392
void AppendArray(const Array< TYPE, SMALL_VECTOR_SIZE > &rhs)
append the contents of an array to this array
Definition array.h:801
void EraseIndex(IndexT index)
erase element at index, keep sorting intact
Definition array.h:1069
const SizeT Size() const
get number of elements in array
Definition array.h:913
Implements a fixed size one-dimensional array.
Definition fixedarray.h:20
const SizeT Size() const
get number of elements
Definition fixedarray.h:538
Definition hashtable.h:98
IndexT bucketIndex
Definition hashtable.h:115
Iterator & operator++(int)
progress to next item in the hash table
Definition hashtable.h:627
const bool operator!=(const Iterator &rhs) const
check if iterator is identical
Definition hashtable.h:686
const bool operator==(const Iterator &rhs) const
check if iterator is identical
Definition hashtable.h:676
KEYTYPE const * key
Definition hashtable.h:110
uint32_t hashIndex
Definition hashtable.h:114
FixedArray< StackArray< KeyValuePair< KEYTYPE, VALUETYPE >, STACK_SIZE > > * arr
Definition hashtable.h:113
VALUETYPE * val
the current value
Definition hashtable.h:109
StackArray< KeyValuePair< KEYTYPE, VALUETYPE >, STACK_SIZE > Content() const
return array of all key/value pairs in the table (slow)
Definition hashtable.h:607
void EndBulkAdd()
end bulk adding, which sorts all affected tables
Definition hashtable.h:473
IndexT FindIndex(const KEYTYPE &key) const
find index in bucket
Definition hashtable.h:578
VALUETYPE & Emplace(const KEYTYPE &key)
adds element only if it doesn't exist, and return reference to it
Definition hashtable.h:442
void Reset()
reset the hashtable arrays to 0 size, but don't run destructor
Definition hashtable.h:358
void operator=(const HashTable< KEYTYPE, VALUETYPE, TABLE_SIZE, STACK_SIZE > &rhs)
assignment operator
Definition hashtable.h:261
FixedArray< bool > bulkDirty
Definition hashtable.h:126
const uint32_t GetHashCode(const typename std::enable_if< std::is_integral< HASHKEY >::value, HASHKEY >::type &key) const
if type is integral, just use that value directly
Definition hashtable.h:130
VALUETYPE & ValueAtIndex(const KEYTYPE &key, IndexT i) const
get value from key and bucket
Definition hashtable.h:595
VALUETYPE & operator[](const KEYTYPE &key) const
read/write [] operator, assertion if key not found
Definition hashtable.h:294
StackArray< KEYTYPE, STACK_SIZE > KeysAsArray() const
get all keys as an Util::Array (slow)
Definition hashtable.h:142
int size
Definition hashtable.h:124
IndexT Add(const KeyValuePair< KEYTYPE, VALUETYPE > &kvp)
add a key/value pair object to the hash table, returns index within hash array where item is stored
Definition hashtable.h:406
HashTable()
default constructor
Definition hashtable.h:221
SizeT Size() const
return current number of values in the hashtable
Definition hashtable.h:322
const uint32_t GetHashCode(const HASHKEY &key) const
if not, call the function on HashCode on HASHKEY
Definition hashtable.h:132
bool IsEmpty() const
return true if empty
Definition hashtable.h:374
void Clear()
clear the hashtable
Definition hashtable.h:342
void Erase(const KEYTYPE &key)
erase an entry
Definition hashtable.h:510
SizeT Capacity() const
return fixed-size capacity of the hash table
Definition hashtable.h:332
void BeginBulkAdd()
begin bulk adding to the hashtable, which will amortize the cost of sorting the hash buckets upon end
Definition hashtable.h:384
void Merge(const HashTable< KEYTYPE, VALUETYPE, TABLE_SIZE, STACK_SIZE > &rhs)
merge two dictionaries
Definition hashtable.h:493
bool Contains(const KEYTYPE &key) const
return true if key exists in the array
Definition hashtable.h:550
StackArray< VALUETYPE, STACK_SIZE > ValuesAsArray() const
get all keys as an Util::Array (slow)
Definition hashtable.h:160
bool inBulkAdd
Definition hashtable.h:127
void EraseIndex(const KEYTYPE &key, IndexT i)
erase an entry with known index
Definition hashtable.h:531
Iterator Begin()
get iterator to first element
Definition hashtable.h:178
const bool IsBulkAdd() const
returns true if currently bulk adding
Definition hashtable.h:396
const uint32_t GetHashCode(const typename std::enable_if< std::is_pointer< HASHKEY >::value, HASHKEY >::type &key) const
if type is pointer, convert using questionable method
Definition hashtable.h:134
Iterator End()
get iterator to last element
Definition hashtable.h:206
FixedArray< StackArray< KeyValuePair< KEYTYPE, VALUETYPE >, STACK_SIZE > > hashArray
Definition hashtable.h:123
Key/Value pair objects are used by most assiociative container classes, like Dictionary or HashTable.
Definition keyvaluepair.h:19
const KEYTYPE & Key() const
read access to key
Definition keyvaluepair.h:282
#define n_assert(exp)
Definition debug.h:50
A quad tree designed to return regions of free 2D space.
Definition String.cs:6
Array< TYPE, STACK_SIZE > StackArray
Definition array.h:2001
Definition half.h:491
Nebula's scalar datatype.
static const int InvalidIndex
Definition types.h:47
int SizeT
Definition types.h:42
int IndexT
Definition types.h:41