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 { return uint32_t(key % 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>
235 hashArray(rhs.hashArray),
236 bulkDirty(rhs.bulkDirty),
237 inBulkAdd(rhs.inBulkAdd),
238 size(rhs.size)
239{
240 // empty
241}
242
243//------------------------------------------------------------------------------
246template<class KEYTYPE, class VALUETYPE, int TABLE_SIZE, int STACK_SIZE>
248 hashArray(std::move(rhs.hashArray)),
249 bulkDirty(rhs.bulkDirty),
250 inBulkAdd(rhs.inBulkAdd),
251 size(rhs.size)
252{
253 rhs.size = 0;
254}
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->size = rhs.size;
283 rhs.size = 0;
284 }
285}
286
287//------------------------------------------------------------------------------
290template<class KEYTYPE, class VALUETYPE, int TABLE_SIZE, int STACK_SIZE>
291VALUETYPE&
293{
294 // get hash code from key, trim to capacity
295 n_assert(!this->inBulkAdd);
296 uint32_t hashIndex = GetHashCode<KEYTYPE>(key);
297 const StackArray<KeyValuePair<KEYTYPE, VALUETYPE>, STACK_SIZE>& hashElements = this->hashArray[hashIndex];
298 int numHashElements = hashElements.Size();
299 #if NEBULA_BOUNDSCHECKS
300 n_assert(0 != numHashElements); // element with key doesn't exist
301 #endif
302 if (1 == numHashElements)
303 {
304 // no hash collisions, just return the only existing element
305 return hashElements[0].Value();
306 }
307 else
308 {
309 // here's a hash collision, find the right key
310 // with a binary search
311 IndexT hashElementIndex = hashElements.template BinarySearchIndex<KEYTYPE>(key);
312 #if NEBULA_BOUNDSCHECKS
313 n_assert(InvalidIndex != hashElementIndex);
314 #endif
315 return hashElements[hashElementIndex].Value();
316 }
317}
318
319//------------------------------------------------------------------------------
322template<class KEYTYPE, class VALUETYPE, int TABLE_SIZE, int STACK_SIZE>
323SizeT
325{
326 return this->size;
327}
328
329//------------------------------------------------------------------------------
332template<class KEYTYPE, class VALUETYPE, int TABLE_SIZE, int STACK_SIZE>
333SizeT
335{
336 return this->hashArray.Size();
337}
338
339//------------------------------------------------------------------------------
342template<class KEYTYPE, class VALUETYPE, int TABLE_SIZE, int STACK_SIZE>
343void
345{
346 IndexT i;
347 SizeT num = this->hashArray.Size();
348 for (i = 0; i < num; i++)
349 {
350 this->hashArray[i].Clear();
351 }
352 this->size = 0;
353}
354
355//------------------------------------------------------------------------------
358template<class KEYTYPE, class VALUETYPE, int TABLE_SIZE, int STACK_SIZE>
359void
361{
362 IndexT i;
363 SizeT num = this->hashArray.Size();
364 for (i = 0; i < num; i++)
365 {
366 this->hashArray[i].Reset();
367 }
368 this->size = 0;
369}
370
371//------------------------------------------------------------------------------
374template<class KEYTYPE, class VALUETYPE, int TABLE_SIZE, int STACK_SIZE>
375bool
377{
378 return (0 == this->size);
379}
380
381//------------------------------------------------------------------------------
384template<class KEYTYPE, class VALUETYPE, int TABLE_SIZE, int STACK_SIZE>
385void
387{
388 n_assert(!this->inBulkAdd);
389 this->inBulkAdd = true;
390 this->bulkDirty.Fill(false);
391}
392
393//------------------------------------------------------------------------------
396template<class KEYTYPE, class VALUETYPE, int TABLE_SIZE, int STACK_SIZE>
397inline const bool
399{
400 return this->inBulkAdd;
401}
402
403//------------------------------------------------------------------------------
406template<class KEYTYPE, class VALUETYPE, int TABLE_SIZE, int STACK_SIZE>
407IndexT
409{
410#if NEBULA_BOUNDSCHECKS
411 n_assert(!this->Contains(kvp.Key()));
412#endif
413 uint32_t hashIndex = GetHashCode<KEYTYPE>(kvp.Key());
414 IndexT ret;
415 if (this->inBulkAdd)
416 {
417 ret = this->hashArray[hashIndex].Size();
418 this->hashArray[hashIndex].Append(kvp);
419 this->bulkDirty[hashIndex] = true;
420 }
421 else
422 ret = this->hashArray[hashIndex].InsertSorted(kvp);
423
424 this->size++;
425 return ret;
426}
427
428//------------------------------------------------------------------------------
431template<class KEYTYPE, class VALUETYPE, int TABLE_SIZE, int STACK_SIZE>
432IndexT
433HashTable<KEYTYPE, VALUETYPE, TABLE_SIZE, STACK_SIZE>::Add(const KEYTYPE& key, const VALUETYPE& value)
434{
435 KeyValuePair<KEYTYPE, VALUETYPE> kvp(key, value);
436 return this->Add(kvp);
437}
438
439//------------------------------------------------------------------------------
442template<class KEYTYPE, class VALUETYPE, int TABLE_SIZE, int STACK_SIZE>
443VALUETYPE&
445{
446 // get hash code from key, trim to capacity
447 uint32_t hashIndex = GetHashCode<KEYTYPE>(key);
448 IndexT elementIndex = InvalidIndex;
449 StackArray<KeyValuePair<KEYTYPE, VALUETYPE>, STACK_SIZE>& hashElements = this->hashArray[hashIndex];
450 if (hashElements.Size() == 0)
451 {
452 elementIndex = this->Add(key, VALUETYPE());
453 }
454 else
455 {
456 // binary search requires the array to be sorted, which it isn't if we're in bulk add mode
457 if (this->inBulkAdd)
458 elementIndex = hashElements.template FindIndex<KEYTYPE>(key);
459 else
460 elementIndex = hashElements.template BinarySearchIndex<KEYTYPE>(key);
461
462 if (elementIndex == InvalidIndex)
463 {
464 elementIndex = this->Add(key, VALUETYPE());
465 }
466 }
467 return hashElements[elementIndex].Value();
468}
469
470//------------------------------------------------------------------------------
473template<class KEYTYPE, class VALUETYPE, int TABLE_SIZE, int STACK_SIZE>
474void
476{
477 n_assert(this->inBulkAdd);
478 IndexT i;
479 for (i = 0; i < this->bulkDirty.Size(); i++)
480 {
481 if (this->bulkDirty[i])
482 {
483 this->hashArray[i].Sort();
484 this->bulkDirty[i] = false;
485 }
486 }
487 this->inBulkAdd = false;
488}
489
490//------------------------------------------------------------------------------
493template<class KEYTYPE, class VALUETYPE, int TABLE_SIZE, int STACK_SIZE>
494void
496{
497 n_assert(this->hashArray.Size() == rhs.hashArray.Size());
498 IndexT i;
499 for (i = 0; i < rhs.hashArray.Size(); i++)
500 {
501 IndexT j;
502 for (j = 0; j < rhs.hashArray[i].Size(); j++)
503 this->Add(rhs.hashArray[i][j]);
504 }
505}
506
507//------------------------------------------------------------------------------
510template<class KEYTYPE, class VALUETYPE, int TABLE_SIZE, int STACK_SIZE>
511void
513{
514 #if NEBULA_BOUNDSCHECKS
515 n_assert(!this->inBulkAdd);
516 n_assert(this->size > 0);
517 #endif
518 uint32_t hashIndex = GetHashCode<KEYTYPE>(key);
519 StackArray<KeyValuePair<KEYTYPE, VALUETYPE>, STACK_SIZE>& hashElements = this->hashArray[hashIndex];
520 IndexT hashElementIndex = hashElements.template BinarySearchIndex<KEYTYPE>(key);
521 #if NEBULA_BOUNDSCHECKS
522 n_assert(InvalidIndex != hashElementIndex); // key doesn't exist
523 #endif
524 hashElements.EraseIndex(hashElementIndex);
525 this->size--;
526}
527
528//------------------------------------------------------------------------------
531template<class KEYTYPE, class VALUETYPE, int TABLE_SIZE, int STACK_SIZE>
532void
534{
535#if NEBULA_BOUNDSCHECKS
536 n_assert(this->size > 0);
537#endif
538 uint32_t hashIndex = GetHashCode<KEYTYPE>(key);
539 StackArray<KeyValuePair<KEYTYPE, VALUETYPE>, STACK_SIZE>& hashElements = this->hashArray[hashIndex];
540#if NEBULA_BOUNDSCHECKS
541 n_assert(InvalidIndex != index); // key doesn't exist
542#endif
543 hashElements.EraseIndex(index);
544 this->size--;
545}
546
547//------------------------------------------------------------------------------
550template<class KEYTYPE, class VALUETYPE, int TABLE_SIZE, int STACK_SIZE>
551bool
553{
554 if (this->size > 0)
555 {
556 uint32_t hashIndex = GetHashCode<KEYTYPE>(key);
557 StackArray<KeyValuePair<KEYTYPE, VALUETYPE>, STACK_SIZE>& hashElements = this->hashArray[hashIndex];
558 if (hashElements.Size() == 0) return false;
559 else
560 {
561 IndexT hashElementIndex;
562 if (this->inBulkAdd)
563 hashElementIndex = hashElements.template FindIndex<KEYTYPE>(key);
564 else
565 hashElementIndex = hashElements.template BinarySearchIndex<KEYTYPE>(key);
566 return (InvalidIndex != hashElementIndex);
567 }
568 }
569 else
570 {
571 return false;
572 }
573}
574
575//------------------------------------------------------------------------------
578template<class KEYTYPE, class VALUETYPE, int TABLE_SIZE, int STACK_SIZE>
579IndexT
581{
582 uint32_t hashIndex = GetHashCode<KEYTYPE>(key);
583 StackArray<KeyValuePair<KEYTYPE, VALUETYPE>, STACK_SIZE>& hashElements = this->hashArray[hashIndex];
584 IndexT hashElementIndex;
585 if (this->inBulkAdd)
586 hashElementIndex = hashElements.template FindIndex<KEYTYPE>(key);
587 else
588 hashElementIndex = hashElements.template BinarySearchIndex<KEYTYPE>(key);
589 return hashElementIndex;
590}
591
592//------------------------------------------------------------------------------
595template<class KEYTYPE, class VALUETYPE, int TABLE_SIZE, int STACK_SIZE>
596VALUETYPE&
598{
599 uint32_t hashIndex = GetHashCode<KEYTYPE>(key);
600 StackArray<KeyValuePair<KEYTYPE, VALUETYPE>, STACK_SIZE>& hashElements = this->hashArray[hashIndex];
601 return hashElements[i].Value();
602}
603
604//------------------------------------------------------------------------------
607template<class KEYTYPE, class VALUETYPE, int TABLE_SIZE, int STACK_SIZE>
610{
612 int i;
613 int num = this->hashArray.Size();
614 for (i = 0; i < num; i++)
615 {
616 if (this->hashArray[i].Size() > 0)
617 {
618 result.AppendArray(this->hashArray[i]);
619 }
620 }
621 return result;
622}
623
624//------------------------------------------------------------------------------
627template<class KEYTYPE, class VALUETYPE, int TABLE_SIZE, int STACK_SIZE>
630{
632
633 // always increment bucket index
634 this->bucketIndex++;
635 if (arr[this->hashIndex].Size() > this->bucketIndex)
636 {
637 this->val = &arr[this->hashIndex][this->bucketIndex].Value();
638 this->key = &arr[this->hashIndex][this->bucketIndex].Key();
639 }
640 else
641 {
642 // go through remainding hash slots and update iterator if an array is empty
643 IndexT i;
644 for (i = this->hashIndex + 1; i < arr.Size(); i++)
645 {
646 if (arr[i].Size() > 0)
647 {
648 this->hashIndex = i;
649 this->bucketIndex = 0;
650 this->val = &arr[i][this->bucketIndex].Value();
651 this->key = &arr[i][this->bucketIndex].Key();
652 break;
653 }
654 }
655
656 // no bucket found, set to 'end'
657 if (i == arr.Size())
658 {
659 this->hashIndex = arr.Size();
660 this->bucketIndex = 0;
661 this->val = nullptr;
662 this->key = nullptr;
663 }
664 }
665 return *this;
666}
667
668//------------------------------------------------------------------------------
671template<class KEYTYPE, class VALUETYPE, int TABLE_SIZE, int STACK_SIZE>
672const bool
674{
675 return this->key == rhs.key;
676}
677
678//------------------------------------------------------------------------------
681template<class KEYTYPE, class VALUETYPE, int TABLE_SIZE, int STACK_SIZE>
682const bool
684{
685 return this->key != rhs.key;
686}
687} // namespace Util
688//------------------------------------------------------------------------------
Nebula's dynamic array class.
Definition array.h:60
void Append(const TYPE &first, const ELEM_TYPE &... elements)
Append multiple elements to the end of the array.
Definition array.h:1334
void AppendArray(const Array< TYPE, SMALL_VECTOR_SIZE > &rhs)
append the contents of an array to this array
Definition array.h:768
void EraseIndex(IndexT index)
erase element at index, keep sorting intact
Definition array.h:1036
const SizeT Size() const
get number of elements in array
Definition array.h:880
Implements a fixed size one-dimensional array.
Definition fixedarray.h:20
const SizeT Size() const
get number of elements
Definition fixedarray.h:532
Definition hashtable.h:98
IndexT bucketIndex
Definition hashtable.h:115
Iterator & operator++(int)
progress to next item in the hash table
Definition hashtable.h:629
const bool operator!=(const Iterator &rhs) const
check if iterator is identical
Definition hashtable.h:683
const bool operator==(const Iterator &rhs) const
check if iterator is identical
Definition hashtable.h:673
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
Organizes key/value pairs by a hash code.
Definition hashtable.h:42
StackArray< KeyValuePair< KEYTYPE, VALUETYPE >, STACK_SIZE > Content() const
return array of all key/value pairs in the table (slow)
Definition hashtable.h:609
void EndBulkAdd()
end bulk adding, which sorts all affected tables
Definition hashtable.h:475
IndexT FindIndex(const KEYTYPE &key) const
find index in bucket
Definition hashtable.h:580
HashTable(HashTable< KEYTYPE, VALUETYPE, TABLE_SIZE, STACK_SIZE > &&rhs)
move constructor
Definition hashtable.h:247
VALUETYPE & Emplace(const KEYTYPE &key)
adds element only if it doesn't exist, and return reference to it
Definition hashtable.h:444
void Reset()
reset the hashtable arrays to 0 size, but don't run destructor
Definition hashtable.h:360
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:597
VALUETYPE & operator[](const KEYTYPE &key) const
read/write [] operator, assertion if key not found
Definition hashtable.h:292
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:408
HashTable()
default constructor
Definition hashtable.h:221
SizeT Size() const
return current number of values in the hashtable
Definition hashtable.h:324
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:376
HashTable(const HashTable< KEYTYPE, VALUETYPE, TABLE_SIZE, STACK_SIZE > &rhs)
copy constructor
Definition hashtable.h:234
void Clear()
clear the hashtable
Definition hashtable.h:344
void Erase(const KEYTYPE &key)
erase an entry
Definition hashtable.h:512
SizeT Capacity() const
return fixed-size capacity of the hash table
Definition hashtable.h:334
void BeginBulkAdd()
begin bulk adding to the hashtable, which will amortize the cost of sorting the hash buckets upon end
Definition hashtable.h:386
void Merge(const HashTable< KEYTYPE, VALUETYPE, TABLE_SIZE, STACK_SIZE > &rhs)
merge two dictionaries
Definition hashtable.h:495
bool Contains(const KEYTYPE &key) const
return true if key exists in the array
Definition hashtable.h:552
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:533
Iterator Begin()
get iterator to first element
Definition hashtable.h:178
const bool IsBulkAdd() const
returns true if currently bulk adding
Definition hashtable.h:398
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
void operator=(HashTable< KEYTYPE, VALUETYPE, TABLE_SIZE, STACK_SIZE > &&rhs)
move assignment operator
Definition hashtable.h:276
IndexT Add(const KEYTYPE &key, const VALUETYPE &value)
add a key and associated value
Definition hashtable.h:433
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 pinned array is an array which manages its own virtual memory.
Definition String.cs:6
Array< TYPE, STACK_SIZE > StackArray
Definition array.h:1881
Definition half.h:491
Nebula's scalar datatype.
static const int InvalidIndex
Definition types.h:54
int SizeT
Definition types.h:49
int IndexT
Definition types.h:48