Plasma Engine  2.0
Loading...
Searching...
No Matches
HashTable_inl.h
1
3#ifndef plInvalidIndex
4# define plInvalidIndex 0xFFFFFFFF
5#endif
6
7// ***** Const Iterator *****
8
9template <typename K, typename V, typename H>
11 : m_pHashTable(&hashTable)
12{
13}
14
15template <typename K, typename V, typename H>
17{
18 if (m_pHashTable->IsEmpty())
19 {
20 m_uiCurrentIndex = m_pHashTable->m_uiCapacity;
21 return;
22 }
23 while (!m_pHashTable->IsValidEntry(m_uiCurrentIndex))
24 {
25 ++m_uiCurrentIndex;
26 }
27}
28
29template <typename K, typename V, typename H>
31{
32 m_uiCurrentCount = m_pHashTable->m_uiCount;
33 m_uiCurrentIndex = m_pHashTable->m_uiCapacity;
34}
35
36
37template <typename K, typename V, typename H>
39{
40 return m_uiCurrentCount < m_pHashTable->m_uiCount;
41}
42
43template <typename K, typename V, typename H>
45{
46 return m_uiCurrentIndex == rhs.m_uiCurrentIndex && m_pHashTable->m_pEntries == rhs.m_pHashTable->m_pEntries;
47}
48
49template <typename K, typename V, typename H>
50PL_ALWAYS_INLINE const K& plHashTableBaseConstIterator<K, V, H>::Key() const
51{
52 return m_pHashTable->m_pEntries[m_uiCurrentIndex].key;
53}
54
55template <typename K, typename V, typename H>
56PL_ALWAYS_INLINE const V& plHashTableBaseConstIterator<K, V, H>::Value() const
57{
58 return m_pHashTable->m_pEntries[m_uiCurrentIndex].value;
59}
60
61template <typename K, typename V, typename H>
63{
64 // if we already iterated over the amount of valid elements that the hash-table stores, early out
65 if (m_uiCurrentCount >= m_pHashTable->m_uiCount)
66 return;
67
68 // increase the counter of how many elements we have seen
69 ++m_uiCurrentCount;
70 // increase the index of the element to look at
71 ++m_uiCurrentIndex;
72
73 // check that we don't leave the valid range of element indices
74 while (m_uiCurrentIndex < m_pHashTable->m_uiCapacity)
75 {
76 if (m_pHashTable->IsValidEntry(m_uiCurrentIndex))
77 return;
78
79 ++m_uiCurrentIndex;
80 }
81
82 // if we fell through this loop, we reached the end of all elements in the container
83 // set the m_uiCurrentCount to maximum, to enable early-out in the future and to make 'IsValid' return 'false'
84 m_uiCurrentCount = m_pHashTable->m_uiCount;
85}
86
87template <typename K, typename V, typename H>
89{
90 Next();
91}
92
93#if PL_ENABLED(PL_USE_CPP20_OPERATORS)
94// These functions are used for structured bindings.
95// They describe how many elements can be accessed in the binding and which type they are.
96namespace std
97{
98 template <typename K, typename V, typename H>
99 struct tuple_size<plHashTableBaseConstIterator<K, V, H>> : integral_constant<size_t, 2>
100 {
101 };
102
103 template <typename K, typename V, typename H>
104 struct tuple_element<0, plHashTableBaseConstIterator<K, V, H>>
105 {
106 using type = const K&;
107 };
108
109 template <typename K, typename V, typename H>
110 struct tuple_element<1, plHashTableBaseConstIterator<K, V, H>>
111 {
112 using type = const V&;
113 };
114} // namespace std
115#endif
116
117// ***** Iterator *****
118
119template <typename K, typename V, typename H>
121 : plHashTableBaseConstIterator<K, V, H>(hashTable)
122{
123}
124
125template <typename K, typename V, typename H>
127 : plHashTableBaseConstIterator<K, V, H>(*rhs.m_pHashTable)
128{
129 this->m_uiCurrentIndex = rhs.m_uiCurrentIndex;
130 this->m_uiCurrentCount = rhs.m_uiCurrentCount;
131}
132
133template <typename K, typename V, typename H>
134PL_ALWAYS_INLINE void plHashTableBaseIterator<K, V, H>::operator=(const plHashTableBaseIterator& rhs) // [tested]
135{
136 this->m_pHashTable = rhs.m_pHashTable;
137 this->m_uiCurrentIndex = rhs.m_uiCurrentIndex;
138 this->m_uiCurrentCount = rhs.m_uiCurrentCount;
139}
140
141template <typename K, typename V, typename H>
143{
144 return this->m_pHashTable->m_pEntries[this->m_uiCurrentIndex].value;
145}
146
147template <typename K, typename V, typename H>
149{
150 return this->m_pHashTable->m_pEntries[this->m_uiCurrentIndex].value;
151}
152
153
154#if PL_ENABLED(PL_USE_CPP20_OPERATORS)
155// These functions are used for structured bindings.
156// They describe how many elements can be accessed in the binding and which type they are.
157namespace std
158{
159 template <typename K, typename V, typename H>
160 struct tuple_size<plHashTableBaseIterator<K, V, H>> : integral_constant<size_t, 2>
161 {
162 };
163
164 template <typename K, typename V, typename H>
165 struct tuple_element<0, plHashTableBaseIterator<K, V, H>>
166 {
167 using type = const K&;
168 };
169
170 template <typename K, typename V, typename H>
171 struct tuple_element<1, plHashTableBaseIterator<K, V, H>>
172 {
173 using type = V&;
174 };
175} // namespace std
176#endif
177
178// ***** plHashTableBase *****
179
180template <typename K, typename V, typename H>
182{
183 m_pEntries = nullptr;
184 m_pEntryFlags = nullptr;
185 m_uiCount = 0;
186 m_uiCapacity = 0;
187 m_pAllocator = pAllocator;
189
190template <typename K, typename V, typename H>
192{
193 m_pEntries = nullptr;
194 m_pEntryFlags = nullptr;
195 m_uiCount = 0;
196 m_uiCapacity = 0;
197 m_pAllocator = pAllocator;
198
199 *this = other;
201
202template <typename K, typename V, typename H>
204{
205 m_pEntries = nullptr;
206 m_pEntryFlags = nullptr;
207 m_uiCount = 0;
208 m_uiCapacity = 0;
209 m_pAllocator = pAllocator;
210
211 *this = std::move(other);
212}
213
214template <typename K, typename V, typename H>
216{
217 Clear();
218 PL_DELETE_RAW_BUFFER(m_pAllocator, m_pEntries);
219 PL_DELETE_RAW_BUFFER(m_pAllocator, m_pEntryFlags);
220 m_uiCapacity = 0;
221}
222
223template <typename K, typename V, typename H>
225{
226 Clear();
227 Reserve(rhs.GetCount());
228
229 plUInt32 uiCopied = 0;
230 for (plUInt32 i = 0; uiCopied < rhs.GetCount(); ++i)
231 {
232 if (rhs.IsValidEntry(i))
233 {
234 Insert(rhs.m_pEntries[i].key, rhs.m_pEntries[i].value);
235 ++uiCopied;
236 }
237 }
238}
240template <typename K, typename V, typename H>
242{
243 // Clear any existing data (calls destructors if necessary)
244 Clear();
245
246 if (m_pAllocator != rhs.m_pAllocator)
247 {
248 Reserve(rhs.m_uiCapacity);
249
250 plUInt32 uiCopied = 0;
251 for (plUInt32 i = 0; uiCopied < rhs.GetCount(); ++i)
252 {
253 if (rhs.IsValidEntry(i))
254 {
255 Insert(std::move(rhs.m_pEntries[i].key), std::move(rhs.m_pEntries[i].value));
256 ++uiCopied;
257 }
258 }
259
260 rhs.Clear();
261 }
262 else
264 PL_DELETE_RAW_BUFFER(m_pAllocator, m_pEntries);
265 PL_DELETE_RAW_BUFFER(m_pAllocator, m_pEntryFlags);
267 // Move all data over.
268 m_pEntries = rhs.m_pEntries;
269 m_pEntryFlags = rhs.m_pEntryFlags;
270 m_uiCount = rhs.m_uiCount;
271 m_uiCapacity = rhs.m_uiCapacity;
273 // Temp copy forgets all its state.
274 rhs.m_pEntries = nullptr;
275 rhs.m_pEntryFlags = nullptr;
276 rhs.m_uiCount = 0;
277 rhs.m_uiCapacity = 0;
279}
280
281template <typename K, typename V, typename H>
283{
284 if (m_uiCount != rhs.m_uiCount)
285 return false;
286
287 plUInt32 uiCompared = 0;
288 for (plUInt32 i = 0; uiCompared < m_uiCount; ++i)
289 {
290 if (IsValidEntry(i))
291 {
292 const V* pRhsValue = nullptr;
293 if (!rhs.TryGetValue(m_pEntries[i].key, pRhsValue))
294 return false;
295
296 if (m_pEntries[i].value != *pRhsValue)
297 return false;
298
299 ++uiCompared;
300 }
301 }
302
303 return true;
304}
305
306template <typename K, typename V, typename H>
307void plHashTableBase<K, V, H>::Reserve(plUInt32 uiCapacity)
308{
309 const plUInt64 uiCap64 = static_cast<plUInt64>(uiCapacity);
310 plUInt64 uiNewCapacity64 = uiCap64 + (uiCap64 * 2 / 3); // ensure a maximum load of 60%
311
312 uiNewCapacity64 = plMath::Min<plUInt64>(uiNewCapacity64, 0x80000000llu); // the largest power-of-two in 32 bit
313
314 plUInt32 uiNewCapacity32 = static_cast<plUInt32>(uiNewCapacity64 & 0xFFFFFFFF);
315 PL_ASSERT_DEBUG(uiCapacity <= uiNewCapacity32, "plHashSet/Map do not support more than 2 billion entries.");
316
317 if (m_uiCapacity >= uiNewCapacity32)
318 return;
319
320 uiNewCapacity32 = plMath::Max<plUInt32>(plMath::PowerOfTwo_Ceil(uiNewCapacity32), CAPACITY_ALIGNMENT);
321 SetCapacity(uiNewCapacity32);
322}
323
324template <typename K, typename V, typename H>
326{
327 if (IsEmpty())
328 {
329 // completely deallocate all data, if the table is empty.
330 PL_DELETE_RAW_BUFFER(m_pAllocator, m_pEntries);
331 PL_DELETE_RAW_BUFFER(m_pAllocator, m_pEntryFlags);
332 m_uiCapacity = 0;
333 }
334 else
335 {
336 const plUInt32 uiNewCapacity = plMath::PowerOfTwo_Ceil(m_uiCount + (CAPACITY_ALIGNMENT - 1)) & ~(CAPACITY_ALIGNMENT - 1);
337 if (m_uiCapacity != uiNewCapacity)
338 SetCapacity(uiNewCapacity);
339 }
340}
341
342template <typename K, typename V, typename H>
343PL_ALWAYS_INLINE plUInt32 plHashTableBase<K, V, H>::GetCount() const
344{
345 return m_uiCount;
346}
347
348template <typename K, typename V, typename H>
349PL_ALWAYS_INLINE bool plHashTableBase<K, V, H>::IsEmpty() const
350{
351 return m_uiCount == 0;
352}
353
354template <typename K, typename V, typename H>
356{
357 for (plUInt32 i = 0; i < m_uiCapacity; ++i)
358 {
359 if (IsValidEntry(i))
360 {
361 plMemoryUtils::Destruct(&m_pEntries[i].key, 1);
362 plMemoryUtils::Destruct(&m_pEntries[i].value, 1);
363 }
364 }
365
366 plMemoryUtils::ZeroFill(m_pEntryFlags, GetFlagsCapacity());
367 m_uiCount = 0;
368}
369
370template <typename K, typename V, typename H>
371template <typename CompatibleKeyType, typename CompatibleValueType>
372bool plHashTableBase<K, V, H>::Insert(CompatibleKeyType&& key, CompatibleValueType&& value, V* out_pOldValue /*= nullptr*/)
373{
374 Reserve(m_uiCount + 1);
375
376 plUInt32 uiIndex = H::Hash(key) & (m_uiCapacity - 1);
377 plUInt32 uiDeletedIndex = plInvalidIndex;
378
379 plUInt32 uiCounter = 0;
380 while (!IsFreeEntry(uiIndex) && uiCounter < m_uiCapacity)
381 {
382 if (IsDeletedEntry(uiIndex))
383 {
384 if (uiDeletedIndex == plInvalidIndex)
385 uiDeletedIndex = uiIndex;
386 }
387 else if (H::Equal(m_pEntries[uiIndex].key, key))
388 {
389 if (out_pOldValue != nullptr)
390 *out_pOldValue = std::move(m_pEntries[uiIndex].value);
391
392 m_pEntries[uiIndex].value = std::forward<CompatibleValueType>(value); // Either move or copy assignment.
393 return true;
394 }
395 ++uiIndex;
396 if (uiIndex == m_uiCapacity)
397 uiIndex = 0;
398
399 ++uiCounter;
400 }
401
402 // new entry
403 uiIndex = uiDeletedIndex != plInvalidIndex ? uiDeletedIndex : uiIndex;
404
405 // Both constructions might either be a move or a copy.
406 plMemoryUtils::CopyOrMoveConstruct(&m_pEntries[uiIndex].key, std::forward<CompatibleKeyType>(key));
407 plMemoryUtils::CopyOrMoveConstruct(&m_pEntries[uiIndex].value, std::forward<CompatibleValueType>(value));
408
409 MarkEntryAsValid(uiIndex);
410 ++m_uiCount;
411
412 return false;
413}
414
415template <typename K, typename V, typename H>
416template <typename CompatibleKeyType>
417bool plHashTableBase<K, V, H>::Remove(const CompatibleKeyType& key, V* out_pOldValue /*= nullptr*/)
418{
419 plUInt32 uiIndex = FindEntry(key);
420 if (uiIndex != plInvalidIndex)
421 {
422 if (out_pOldValue != nullptr)
423 *out_pOldValue = std::move(m_pEntries[uiIndex].value);
424
425 RemoveInternal(uiIndex);
426 return true;
427 }
428
429 return false;
430}
431
432template <typename K, typename V, typename H>
434{
435 PL_ASSERT_DEBUG(pos.m_pHashTable == this, "Iterator from wrong hashtable");
436 Iterator it = pos;
437 plUInt32 uiIndex = pos.m_uiCurrentIndex;
438 ++it;
439 --it.m_uiCurrentCount;
440 RemoveInternal(uiIndex);
441 return it;
442}
443
444template <typename K, typename V, typename H>
445void plHashTableBase<K, V, H>::RemoveInternal(plUInt32 uiIndex)
446{
447 plMemoryUtils::Destruct(&m_pEntries[uiIndex].key, 1);
448 plMemoryUtils::Destruct(&m_pEntries[uiIndex].value, 1);
449
450 plUInt32 uiNextIndex = uiIndex + 1;
451 if (uiNextIndex == m_uiCapacity)
452 uiNextIndex = 0;
453
454 // if the next entry is free we are at the end of a chain and
455 // can immediately mark this entry as free as well
456 if (IsFreeEntry(uiNextIndex))
457 {
458 MarkEntryAsFree(uiIndex);
459
460 // run backwards and free all deleted entries in this chain
461 plUInt32 uiPrevIndex = (uiIndex != 0) ? uiIndex : m_uiCapacity;
462 --uiPrevIndex;
463
464 while (IsDeletedEntry(uiPrevIndex))
465 {
466 MarkEntryAsFree(uiPrevIndex);
467
468 if (uiPrevIndex == 0)
469 uiPrevIndex = m_uiCapacity;
470 --uiPrevIndex;
471 }
472 }
473 else
474 {
475 MarkEntryAsDeleted(uiIndex);
476 }
477
478 --m_uiCount;
479}
480
481template <typename K, typename V, typename H>
482template <typename CompatibleKeyType>
483inline bool plHashTableBase<K, V, H>::TryGetValue(const CompatibleKeyType& key, V& out_value) const
484{
485 plUInt32 uiIndex = FindEntry(key);
486 if (uiIndex != plInvalidIndex)
487 {
488 PL_ASSERT_DEBUG(m_pEntries != nullptr, "No entries present"); // To fix static analysis
489 out_value = m_pEntries[uiIndex].value;
490 return true;
491 }
492
493 return false;
494}
495
496template <typename K, typename V, typename H>
497template <typename CompatibleKeyType>
498inline bool plHashTableBase<K, V, H>::TryGetValue(const CompatibleKeyType& key, const V*& out_pValue) const
499{
500 plUInt32 uiIndex = FindEntry(key);
501 if (uiIndex != plInvalidIndex)
502 {
503 out_pValue = &m_pEntries[uiIndex].value;
504 PL_ANALYSIS_ASSUME(out_pValue != nullptr);
505 return true;
506 }
507
508 return false;
509}
510
511template <typename K, typename V, typename H>
512template <typename CompatibleKeyType>
513inline bool plHashTableBase<K, V, H>::TryGetValue(const CompatibleKeyType& key, V*& out_pValue) const
514{
515 plUInt32 uiIndex = FindEntry(key);
516 if (uiIndex != plInvalidIndex)
517 {
518 out_pValue = &m_pEntries[uiIndex].value;
519 PL_ANALYSIS_ASSUME(out_pValue != nullptr);
520 return true;
521 }
522
523 return false;
524}
525
526template <typename K, typename V, typename H>
527template <typename CompatibleKeyType>
528inline typename plHashTableBase<K, V, H>::ConstIterator plHashTableBase<K, V, H>::Find(const CompatibleKeyType& key) const
529{
530 plUInt32 uiIndex = FindEntry(key);
531 if (uiIndex == plInvalidIndex)
532 {
533 return GetEndIterator();
534 }
535
536 ConstIterator it(*this);
537 it.m_uiCurrentIndex = uiIndex;
538 it.m_uiCurrentCount = 0; // we do not know the 'count' (which is used as an optimization), so we just use 0
539
540 return it;
541}
542
543template <typename K, typename V, typename H>
544template <typename CompatibleKeyType>
545inline typename plHashTableBase<K, V, H>::Iterator plHashTableBase<K, V, H>::Find(const CompatibleKeyType& key)
546{
547 plUInt32 uiIndex = FindEntry(key);
548 if (uiIndex == plInvalidIndex)
549 {
550 return GetEndIterator();
551 }
552
553 Iterator it(*this);
554 it.m_uiCurrentIndex = uiIndex;
555 it.m_uiCurrentCount = 0; // we do not know the 'count' (which is used as an optimization), so we just use 0
556 return it;
557}
558
559
560template <typename K, typename V, typename H>
561template <typename CompatibleKeyType>
562inline const V* plHashTableBase<K, V, H>::GetValue(const CompatibleKeyType& key) const
563{
564 plUInt32 uiIndex = FindEntry(key);
565 return (uiIndex != plInvalidIndex) ? &m_pEntries[uiIndex].value : nullptr;
566}
567
568template <typename K, typename V, typename H>
569template <typename CompatibleKeyType>
570inline V* plHashTableBase<K, V, H>::GetValue(const CompatibleKeyType& key)
571{
572 plUInt32 uiIndex = FindEntry(key);
573 return (uiIndex != plInvalidIndex) ? &m_pEntries[uiIndex].value : nullptr;
574}
575
576template <typename K, typename V, typename H>
578{
579 return FindOrAdd(key, nullptr);
580}
581
582template <typename K, typename V, typename H>
583V& plHashTableBase<K, V, H>::FindOrAdd(const K& key, bool* out_pExisted)
584{
585 const plUInt32 uiHash = H::Hash(key);
586 plUInt32 uiIndex = FindEntry(uiHash, key);
587
588 if (out_pExisted)
589 {
590 *out_pExisted = uiIndex != plInvalidIndex;
591 }
592
593 if (uiIndex == plInvalidIndex)
594 {
595 Reserve(m_uiCount + 1);
596
597 // search for suitable insertion index again, table might have been resized
598 uiIndex = uiHash & (m_uiCapacity - 1);
599 while (IsValidEntry(uiIndex))
600 {
601 ++uiIndex;
602 if (uiIndex == m_uiCapacity)
603 uiIndex = 0;
604 }
605
606 // new entry
607 plMemoryUtils::CopyConstruct(&m_pEntries[uiIndex].key, key, 1);
608 plMemoryUtils::Construct<ConstructAll>(&m_pEntries[uiIndex].value, 1);
609 MarkEntryAsValid(uiIndex);
610 ++m_uiCount;
611 }
612
613 PL_ASSERT_DEBUG(m_pEntries != nullptr, "Entries should be present");
614 return m_pEntries[uiIndex].value;
615}
616
617template <typename K, typename V, typename H>
618template <typename CompatibleKeyType>
619PL_FORCE_INLINE bool plHashTableBase<K, V, H>::Contains(const CompatibleKeyType& key) const
620{
621 return FindEntry(key) != plInvalidIndex;
622}
623
624template <typename K, typename V, typename H>
626{
627 Iterator iterator(*this);
628 iterator.SetToBegin();
629 return iterator;
630}
631
632template <typename K, typename V, typename H>
634{
635 Iterator iterator(*this);
636 iterator.SetToEnd();
637 return iterator;
638}
639
640template <typename K, typename V, typename H>
642{
643 ConstIterator iterator(*this);
644 iterator.SetToBegin();
645 return iterator;
646}
647
648template <typename K, typename V, typename H>
650{
651 ConstIterator iterator(*this);
652 iterator.SetToEnd();
653 return iterator;
654}
655
656template <typename K, typename V, typename H>
658{
659 return m_pAllocator;
660}
661
662template <typename K, typename V, typename H>
664{
665 return ((plUInt64)m_uiCapacity * sizeof(Entry)) + (sizeof(plUInt32) * (plUInt64)GetFlagsCapacity());
666}
667
668// private methods
669template <typename K, typename V, typename H>
670void plHashTableBase<K, V, H>::SetCapacity(plUInt32 uiCapacity)
671{
672 PL_ASSERT_DEBUG(plMath::IsPowerOf2(uiCapacity), "uiCapacity must be a power of two to avoid modulo during lookup.");
673 const plUInt32 uiOldCapacity = m_uiCapacity;
674 m_uiCapacity = uiCapacity;
675
676 Entry* pOldEntries = m_pEntries;
677 plUInt32* pOldEntryFlags = m_pEntryFlags;
678
679 m_pEntries = PL_NEW_RAW_BUFFER(m_pAllocator, Entry, m_uiCapacity);
680 m_pEntryFlags = PL_NEW_RAW_BUFFER(m_pAllocator, plUInt32, GetFlagsCapacity());
681 plMemoryUtils::ZeroFill(m_pEntryFlags, GetFlagsCapacity());
682
683 m_uiCount = 0;
684 for (plUInt32 i = 0; i < uiOldCapacity; ++i)
685 {
686 if (GetFlags(pOldEntryFlags, i) == VALID_ENTRY)
687 {
688 PL_VERIFY(!Insert(std::move(pOldEntries[i].key), std::move(pOldEntries[i].value)), "Implementation error");
689
690 plMemoryUtils::Destruct(&pOldEntries[i].key, 1);
691 plMemoryUtils::Destruct(&pOldEntries[i].value, 1);
692 }
693 }
694
695 PL_DELETE_RAW_BUFFER(m_pAllocator, pOldEntries);
696 PL_DELETE_RAW_BUFFER(m_pAllocator, pOldEntryFlags);
697}
698
699template <typename K, typename V, typename H>
700template <typename CompatibleKeyType>
701PL_ALWAYS_INLINE plUInt32 plHashTableBase<K, V, H>::FindEntry(const CompatibleKeyType& key) const
702{
703 return FindEntry(H::Hash(key), key);
704}
705
706template <typename K, typename V, typename H>
707template <typename CompatibleKeyType>
708inline plUInt32 plHashTableBase<K, V, H>::FindEntry(plUInt32 uiHash, const CompatibleKeyType& key) const
709{
710 if (m_uiCapacity > 0)
711 {
712 plUInt32 uiIndex = uiHash & (m_uiCapacity - 1);
713 plUInt32 uiCounter = 0;
714 while (!IsFreeEntry(uiIndex) && uiCounter < m_uiCapacity)
715 {
716 if (IsValidEntry(uiIndex) && H::Equal(m_pEntries[uiIndex].key, key))
717 return uiIndex;
718
719 ++uiIndex;
720 if (uiIndex == m_uiCapacity)
721 uiIndex = 0;
722
723 ++uiCounter;
724 }
725 }
726 // not found
727 return plInvalidIndex;
728}
729
730#define PL_HASHTABLE_USE_BITFLAGS PL_ON
731
732template <typename K, typename V, typename H>
733PL_FORCE_INLINE plUInt32 plHashTableBase<K, V, H>::GetFlagsCapacity() const
734{
735#if PL_ENABLED(PL_HASHTABLE_USE_BITFLAGS)
736 return (m_uiCapacity + 15) / 16;
737#else
738 return m_uiCapacity;
739#endif
740}
741
742template <typename K, typename V, typename H>
743PL_ALWAYS_INLINE plUInt32 plHashTableBase<K, V, H>::GetFlags(plUInt32* pFlags, plUInt32 uiEntryIndex) const
744{
745#if PL_ENABLED(PL_HASHTABLE_USE_BITFLAGS)
746 const plUInt32 uiIndex = uiEntryIndex / 16;
747 const plUInt32 uiSubIndex = (uiEntryIndex & 15) * 2;
748 return (pFlags[uiIndex] >> uiSubIndex) & FLAGS_MASK;
749#else
750 return pFlags[uiEntryIndex] & FLAGS_MASK;
751#endif
752}
753
754template <typename K, typename V, typename H>
755void plHashTableBase<K, V, H>::SetFlags(plUInt32 uiEntryIndex, plUInt32 uiFlags)
756{
757#if PL_ENABLED(PL_HASHTABLE_USE_BITFLAGS)
758 const plUInt32 uiIndex = uiEntryIndex / 16;
759 const plUInt32 uiSubIndex = (uiEntryIndex & 15) * 2;
760 PL_ASSERT_DEBUG(uiIndex < GetFlagsCapacity(), "Out of bounds access");
761 m_pEntryFlags[uiIndex] &= ~(FLAGS_MASK << uiSubIndex);
762 m_pEntryFlags[uiIndex] |= (uiFlags << uiSubIndex);
763#else
764 PL_ASSERT_DEBUG(uiEntryIndex < GetFlagsCapacity(), "Out of bounds access");
765 m_pEntryFlags[uiEntryIndex] = uiFlags;
766#endif
767}
768
769template <typename K, typename V, typename H>
770PL_FORCE_INLINE bool plHashTableBase<K, V, H>::IsFreeEntry(plUInt32 uiEntryIndex) const
771{
772 return GetFlags(m_pEntryFlags, uiEntryIndex) == FREE_ENTRY;
773}
774
775template <typename K, typename V, typename H>
776PL_FORCE_INLINE bool plHashTableBase<K, V, H>::IsValidEntry(plUInt32 uiEntryIndex) const
777{
778 return GetFlags(m_pEntryFlags, uiEntryIndex) == VALID_ENTRY;
779}
780
781template <typename K, typename V, typename H>
782PL_FORCE_INLINE bool plHashTableBase<K, V, H>::IsDeletedEntry(plUInt32 uiEntryIndex) const
783{
784 return GetFlags(m_pEntryFlags, uiEntryIndex) == DELETED_ENTRY;
785}
786
787template <typename K, typename V, typename H>
788PL_FORCE_INLINE void plHashTableBase<K, V, H>::MarkEntryAsFree(plUInt32 uiEntryIndex)
789{
790 SetFlags(uiEntryIndex, FREE_ENTRY);
791}
792
793template <typename K, typename V, typename H>
794PL_FORCE_INLINE void plHashTableBase<K, V, H>::MarkEntryAsValid(plUInt32 uiEntryIndex)
795{
796 SetFlags(uiEntryIndex, VALID_ENTRY);
797}
798
799template <typename K, typename V, typename H>
800PL_FORCE_INLINE void plHashTableBase<K, V, H>::MarkEntryAsDeleted(plUInt32 uiEntryIndex)
801{
802 SetFlags(uiEntryIndex, DELETED_ENTRY);
803}
804
805
806template <typename K, typename V, typename H, typename A>
808 : plHashTableBase<K, V, H>(A::GetAllocator())
809{
810}
811
812template <typename K, typename V, typename H, typename A>
814 : plHashTableBase<K, V, H>(pAllocator)
815{
816}
817
818template <typename K, typename V, typename H, typename A>
820 : plHashTableBase<K, V, H>(other, A::GetAllocator())
821{
822}
823
824template <typename K, typename V, typename H, typename A>
826 : plHashTableBase<K, V, H>(other, A::GetAllocator())
827{
828}
829
830template <typename K, typename V, typename H, typename A>
832 : plHashTableBase<K, V, H>(std::move(other), other.GetAllocator())
833{
834}
835
836template <typename K, typename V, typename H, typename A>
838 : plHashTableBase<K, V, H>(std::move(other), other.GetAllocator())
839{
840}
841
842template <typename K, typename V, typename H, typename A>
844{
846}
847
848template <typename K, typename V, typename H, typename A>
850{
852}
853
854template <typename K, typename V, typename H, typename A>
856{
858}
859
860template <typename K, typename V, typename H, typename A>
862{
864}
865
866template <typename KeyType, typename ValueType, typename Hasher>
868{
869 plMath::Swap(this->m_pEntries, other.m_pEntries);
870 plMath::Swap(this->m_pEntryFlags, other.m_pEntryFlags);
871 plMath::Swap(this->m_uiCount, other.m_uiCount);
872 plMath::Swap(this->m_uiCapacity, other.m_uiCapacity);
873 plMath::Swap(this->m_pAllocator, other.m_pAllocator);
874}
Base class for all memory allocators.
Definition Allocator.h:23
Implementation of a hashtable which stores key/value pairs.
Definition HashTable.h:157
bool operator==(const plHashTableBase< KeyType, ValueType, Hasher > &rhs) const
Compares this table to another table.
Definition HashTable_inl.h:282
ValueType & FindOrAdd(const KeyType &key, bool *out_pExisted=nullptr)
Returns the value stored at the given key. If none exists, one is created. bExisted indicates whether...
Definition HashTable_inl.h:583
Iterator GetIterator()
Returns an Iterator to the very first element.
Definition HashTable_inl.h:625
plAllocator * GetAllocator() const
Returns the allocator that is used by this instance.
Definition HashTable_inl.h:657
bool Contains(const CompatibleKeyType &key) const
Returns if an entry with given key exists in the table.
plUInt64 GetHeapMemoryUsage() const
Returns the amount of bytes that are currently allocated on the heap.
Definition HashTable_inl.h:663
void operator=(const plHashTableBase< KeyType, ValueType, Hasher > &rhs)
Copies the data from another hashtable into this one.
plUInt32 GetCount() const
Returns the number of active entries in the table.
Definition HashTable_inl.h:343
bool TryGetValue(const CompatibleKeyType &key, ValueType &out_value) const
Returns whether an entry with the given key was found and if found writes out the corresponding value...
ValueType & operator[](const KeyType &key)
Returns the value to the given key if found or creates a new entry with the given key and a default c...
Definition HashTable_inl.h:577
const ValueType * GetValue(const CompatibleKeyType &key) const
Returns a pointer to the value of the entry with the given key if found, otherwise returns nullptr.
ConstIterator Find(const CompatibleKeyType &key) const
Searches for key, returns a plHashTableBaseConstIterator to it or an invalid iterator,...
Definition HashTable_inl.h:528
~plHashTableBase()
Destructor.
Definition HashTable_inl.h:215
Iterator GetEndIterator()
Returns an Iterator to the first element that is not part of the hash-table. Needed to support range ...
Definition HashTable_inl.h:633
void Compact()
Tries to compact the hashtable to avoid wasting memory.
Definition HashTable_inl.h:325
bool IsEmpty() const
Returns true, if the hashtable does not contain any elements.
Definition HashTable_inl.h:349
bool Insert(CompatibleKeyType &&key, CompatibleValueType &&value, ValueType *out_pOldValue=nullptr)
Inserts the key value pair or replaces value if an entry with the given key already exists.
void Clear()
Clears the table.
Definition HashTable_inl.h:355
void Reserve(plUInt32 uiCapacity)
Expands the hashtable by over-allocating the internal storage so that the load factor is lower or equ...
Definition HashTable_inl.h:307
bool Remove(const CompatibleKeyType &key, ValueType *out_pOldValue=nullptr)
Removes the entry with the given key. Returns whether an entry was removed and optionally writes out ...
void Swap(plHashTableBase< KeyType, ValueType, Hasher > &other)
Swaps this map with the other one.
Definition HashTable_inl.h:867
plHashTableBase(plAllocator *pAllocator)
Creates an empty hashtable. Does not allocate any data yet.
Definition HashTable_inl.h:181
Definition HashTable.h:333
static void CopyConstruct(Destination *pDestination, const Source &copy, size_t uiCount=1)
Constructs uiCount objects of type T in a raw buffer at pDestination, by creating uiCount copies of c...
static void CopyOrMoveConstruct(Destination *pDestination, Source &&source)
This function will either move call MoveConstruct or CopyConstruct for a single element source,...
static void Construct(T *pDestination, size_t uiCount=1)
Constructs uiCount objects of type T in a raw buffer at pDestination.
static void ZeroFill(T *pDestination, size_t uiCount=1)
Zeros every byte in the provided memory buffer.
static void Destruct(T *pDestination, size_t uiCount=1)
Destructs uiCount objects of type T at pDestination.
constexpr PL_ALWAYS_INLINE T Min(T f1, T f2)
Returns the smaller value, f1 or f2.
Definition Math_inl.h:27
PL_ALWAYS_INLINE void Swap(T &ref_f1, T &ref_f2)
Swaps the values in the two variables f1 and f2.
Definition Math_inl.h:224
constexpr PL_ALWAYS_INLINE T Max(T f1, T f2)
Returns the greater value, f1 or f2.
Definition Math_inl.h:39
PL_FOUNDATION_DLL plUInt32 PowerOfTwo_Ceil(plUInt32 value)
Returns the next power-of-two that is >= value.
Definition Math.cpp:53
constexpr PL_FORCE_INLINE bool IsPowerOf2(plInt32 value)
Returns true, if there exists some x with 2^x == value.
Definition Math_inl.h:260
Const iterator.
Definition HashTable.h:13
void operator++()
Shorthand for 'Next'.
Definition HashTable_inl.h:88
void Next()
Advances the iterator to the next element in the map. The iterator will not be valid anymore,...
Definition HashTable_inl.h:62
const ValueType & Value() const
Returns the 'value' of the element that this iterator points to.
Definition HashTable_inl.h:56
const KeyType & Key() const
Returns the 'key' of the element that this iterator points to.
Definition HashTable_inl.h:50
bool IsValid() const
Checks whether this iterator points to a valid element.
Definition HashTable_inl.h:38
bool operator==(const plHashTableBaseConstIterator &rhs) const
Checks whether the two iterators point to the same element.
Definition HashTable_inl.h:44
Iterator with write access.
Definition HashTable.h:84
PL_ALWAYS_INLINE plHashTableBaseIterator(const plHashTableBaseIterator &rhs)
Creates a new iterator from another.
PL_ALWAYS_INLINE void operator=(const plHashTableBaseIterator &rhs)
Assigns one iterator no another.
Definition HashTable_inl.h:134
PL_FORCE_INLINE ValueType & Value()
Returns the 'value' of the element that this iterator points to.
Definition HashTable_inl.h:142