Plasma Engine  2.0
Loading...
Searching...
No Matches
HashSet_inl.h
1
3#ifndef plInvalidIndex
4# define plInvalidIndex 0xFFFFFFFF
5#endif
6
7// ***** Const Iterator *****
8
9template <typename K, typename H>
11 : m_pHashSet(&hashSet)
12{
13}
14
15template <typename K, typename H>
17{
18 if (m_pHashSet->IsEmpty())
19 {
20 m_uiCurrentIndex = m_pHashSet->m_uiCapacity;
21 return;
22 }
23 while (!m_pHashSet->IsValidEntry(m_uiCurrentIndex))
24 {
25 ++m_uiCurrentIndex;
26 }
27}
28
29template <typename K, typename H>
31{
32 m_uiCurrentCount = m_pHashSet->m_uiCount;
33 m_uiCurrentIndex = m_pHashSet->m_uiCapacity;
35
36template <typename K, typename H>
38{
39 return m_uiCurrentCount < m_pHashSet->m_uiCount;
41
42template <typename K, typename H>
44{
45 return m_uiCurrentIndex == rhs.m_uiCurrentIndex && m_pHashSet->m_pEntries == rhs.m_pHashSet->m_pEntries;
46}
47
48template <typename K, typename H>
49PL_FORCE_INLINE const K& plHashSetBase<K, H>::ConstIterator::Key() const
50{
51 return m_pHashSet->m_pEntries[m_uiCurrentIndex];
52}
53
54template <typename K, typename H>
56{
57 ++m_uiCurrentCount;
58 if (m_uiCurrentCount == m_pHashSet->m_uiCount)
59 {
60 m_uiCurrentIndex = m_pHashSet->m_uiCapacity;
61 return;
62 }
63
64 for (++m_uiCurrentIndex; m_uiCurrentIndex < m_pHashSet->m_uiCapacity; ++m_uiCurrentIndex)
65 {
66 if (m_pHashSet->IsValidEntry(m_uiCurrentIndex))
67 {
68 return;
69 }
70 }
71 SetToEnd();
72}
73
74template <typename K, typename H>
76{
77 Next();
79
80
81// ***** plHashSetBase *****
82
83template <typename K, typename H>
85{
86 m_pEntries = nullptr;
87 m_pEntryFlags = nullptr;
88 m_uiCount = 0;
89 m_uiCapacity = 0;
90 m_pAllocator = pAllocator;
91}
93template <typename K, typename H>
96 m_pEntries = nullptr;
97 m_pEntryFlags = nullptr;
98 m_uiCount = 0;
99 m_uiCapacity = 0;
100 m_pAllocator = pAllocator;
101
102 *this = other;
103}
104
105template <typename K, typename H>
107{
108 m_pEntries = nullptr;
109 m_pEntryFlags = nullptr;
110 m_uiCount = 0;
111 m_uiCapacity = 0;
112 m_pAllocator = pAllocator;
113
114 *this = std::move(other);
115}
117template <typename K, typename H>
120 Clear();
121 PL_DELETE_RAW_BUFFER(m_pAllocator, m_pEntries);
122 PL_DELETE_RAW_BUFFER(m_pAllocator, m_pEntryFlags);
123 m_uiCapacity = 0;
124}
126template <typename K, typename H>
129 Clear();
130 Reserve(rhs.GetCount());
131
132 plUInt32 uiCopied = 0;
133 for (plUInt32 i = 0; uiCopied < rhs.GetCount(); ++i)
134 {
135 if (rhs.IsValidEntry(i))
136 {
137 Insert(rhs.m_pEntries[i]);
138 ++uiCopied;
139 }
140 }
142
143template <typename K, typename H>
145{
146 // Clear any existing data (calls destructors if necessary)
147 Clear();
148
149 if (m_pAllocator != rhs.m_pAllocator)
150 {
151 Reserve(rhs.m_uiCapacity);
152
153 plUInt32 uiCopied = 0;
154 for (plUInt32 i = 0; uiCopied < rhs.GetCount(); ++i)
155 {
156 if (rhs.IsValidEntry(i))
157 {
158 Insert(std::move(rhs.m_pEntries[i]));
159 ++uiCopied;
160 }
161 }
162
163 rhs.Clear();
164 }
165 else
166 {
167 PL_DELETE_RAW_BUFFER(m_pAllocator, m_pEntries);
168 PL_DELETE_RAW_BUFFER(m_pAllocator, m_pEntryFlags);
169
170 // Move all data over.
171 m_pEntries = rhs.m_pEntries;
172 m_pEntryFlags = rhs.m_pEntryFlags;
173 m_uiCount = rhs.m_uiCount;
174 m_uiCapacity = rhs.m_uiCapacity;
175
176 // Temp copy forgets all its state.
177 rhs.m_pEntries = nullptr;
178 rhs.m_pEntryFlags = nullptr;
179 rhs.m_uiCount = 0;
180 rhs.m_uiCapacity = 0;
181 }
182}
183
184template <typename K, typename H>
186{
187 if (m_uiCount != rhs.m_uiCount)
188 return false;
189
190 plUInt32 uiCompared = 0;
191 for (plUInt32 i = 0; uiCompared < m_uiCount; ++i)
192 {
193 if (IsValidEntry(i))
194 {
195 if (!rhs.Contains(m_pEntries[i]))
196 return false;
197
198 ++uiCompared;
199 }
200 }
201
202 return true;
203}
204
205template <typename K, typename H>
206void plHashSetBase<K, H>::Reserve(plUInt32 uiCapacity)
207{
208 const plUInt64 uiCap64 = static_cast<plUInt64>(uiCapacity);
209 plUInt64 uiNewCapacity64 = uiCap64 + (uiCap64 * 2 / 3); // ensure a maximum load of 60%
210
211 uiNewCapacity64 = plMath::Min<plUInt64>(uiNewCapacity64, 0x80000000llu); // the largest power-of-two in 32 bit
212
213 plUInt32 uiNewCapacity32 = static_cast<plUInt32>(uiNewCapacity64 & 0xFFFFFFFF);
214 PL_ASSERT_DEBUG(uiCapacity <= uiNewCapacity32, "plHashSet/Map do not support more than 2 billion entries.");
215
216 if (m_uiCapacity >= uiNewCapacity32)
217 return;
218
219 uiNewCapacity32 = plMath::Max<plUInt32>(plMath::PowerOfTwo_Ceil(uiNewCapacity32), CAPACITY_ALIGNMENT);
220 SetCapacity(uiNewCapacity32);
221}
222
223template <typename K, typename H>
225{
226 if (IsEmpty())
227 {
228 // completely deallocate all data, if the table is empty.
229 PL_DELETE_RAW_BUFFER(m_pAllocator, m_pEntries);
230 PL_DELETE_RAW_BUFFER(m_pAllocator, m_pEntryFlags);
231 m_uiCapacity = 0;
232 }
233 else
234 {
235 const plUInt32 uiNewCapacity = (m_uiCount + (CAPACITY_ALIGNMENT - 1)) & ~(CAPACITY_ALIGNMENT - 1);
236 if (m_uiCapacity != uiNewCapacity)
237 SetCapacity(uiNewCapacity);
238 }
239}
240
241template <typename K, typename H>
242PL_ALWAYS_INLINE plUInt32 plHashSetBase<K, H>::GetCount() const
243{
244 return m_uiCount;
245}
246
247template <typename K, typename H>
248PL_ALWAYS_INLINE bool plHashSetBase<K, H>::IsEmpty() const
249{
250 return m_uiCount == 0;
251}
252
253template <typename K, typename H>
255{
256 for (plUInt32 i = 0; i < m_uiCapacity; ++i)
257 {
258 if (IsValidEntry(i))
259 {
260 plMemoryUtils::Destruct(&m_pEntries[i], 1);
261 }
262 }
263
264 plMemoryUtils::ZeroFill(m_pEntryFlags, GetFlagsCapacity());
265 m_uiCount = 0;
266}
267
268template <typename K, typename H>
269template <typename CompatibleKeyType>
270bool plHashSetBase<K, H>::Insert(CompatibleKeyType&& key)
271{
272 Reserve(m_uiCount + 1);
273
274 plUInt32 uiIndex = H::Hash(key) & (m_uiCapacity - 1);
275 plUInt32 uiDeletedIndex = plInvalidIndex;
276
277 plUInt32 uiCounter = 0;
278 while (!IsFreeEntry(uiIndex) && uiCounter < m_uiCapacity)
279 {
280 if (IsDeletedEntry(uiIndex))
281 {
282 if (uiDeletedIndex == plInvalidIndex)
283 uiDeletedIndex = uiIndex;
284 }
285 else if (H::Equal(m_pEntries[uiIndex], key))
286 {
287 return true;
288 }
289 ++uiIndex;
290 if (uiIndex == m_uiCapacity)
291 uiIndex = 0;
292
293 ++uiCounter;
294 }
295
296 // new entry
297 uiIndex = uiDeletedIndex != plInvalidIndex ? uiDeletedIndex : uiIndex;
298
299 // Constructions might either be a move or a copy.
300 plMemoryUtils::CopyOrMoveConstruct(&m_pEntries[uiIndex], std::forward<CompatibleKeyType>(key));
301
302 MarkEntryAsValid(uiIndex);
303 ++m_uiCount;
304
305 return false;
306}
307
308template <typename K, typename H>
309template <typename CompatibleKeyType>
310bool plHashSetBase<K, H>::Remove(const CompatibleKeyType& key)
311{
312 plUInt32 uiIndex = FindEntry(key);
313 if (uiIndex != plInvalidIndex)
314 {
315 RemoveInternal(uiIndex);
316 return true;
317 }
318
319 return false;
320}
321
322template <typename K, typename H>
324{
325 ConstIterator it = pos;
326 plUInt32 uiIndex = pos.m_uiCurrentIndex;
327 ++it;
328 --it.m_uiCurrentCount;
329 RemoveInternal(uiIndex);
330 return it;
331}
332
333template <typename K, typename H>
334void plHashSetBase<K, H>::RemoveInternal(plUInt32 uiIndex)
335{
336 plMemoryUtils::Destruct(&m_pEntries[uiIndex], 1);
337
338 plUInt32 uiNextIndex = uiIndex + 1;
339 if (uiNextIndex == m_uiCapacity)
340 uiNextIndex = 0;
341
342 // if the next entry is free we are at the end of a chain and
343 // can immediately mark this entry as free as well
344 if (IsFreeEntry(uiNextIndex))
345 {
346 MarkEntryAsFree(uiIndex);
347
348 // run backwards and free all deleted entries in this chain
349 plUInt32 uiPrevIndex = (uiIndex != 0) ? uiIndex : m_uiCapacity;
350 --uiPrevIndex;
351
352 while (IsDeletedEntry(uiPrevIndex))
353 {
354 MarkEntryAsFree(uiPrevIndex);
355
356 if (uiPrevIndex == 0)
357 uiPrevIndex = m_uiCapacity;
358 --uiPrevIndex;
359 }
360 }
361 else
362 {
363 MarkEntryAsDeleted(uiIndex);
364 }
365
366 --m_uiCount;
367}
368
369template <typename K, typename H>
370template <typename CompatibleKeyType>
371PL_FORCE_INLINE bool plHashSetBase<K, H>::Contains(const CompatibleKeyType& key) const
372{
373 return FindEntry(key) != plInvalidIndex;
374}
375
376template <typename K, typename H>
377template <typename CompatibleKeyType>
378PL_FORCE_INLINE typename plHashSetBase<K, H>::ConstIterator plHashSetBase<K, H>::Find(const CompatibleKeyType& key) const
379{
380 plUInt32 uiIndex = FindEntry(key);
381 if (uiIndex == plInvalidIndex)
382 {
383 return GetEndIterator();
384 }
385
386 ConstIterator it(*this);
387 it.m_uiCurrentIndex = uiIndex;
388 it.m_uiCurrentCount = 0; // we do not know the 'count' (which is used as an optimization), so we just use 0
389
390 return it;
391}
392
393template <typename K, typename H>
395{
396 for (const K& key : operand)
397 {
398 if (!Contains(key))
399 return false;
400 }
401
402 return true;
403}
404
405template <typename K, typename H>
407{
408 Reserve(GetCount() + operand.GetCount());
409 for (const auto& key : operand)
410 {
411 Insert(key);
412 }
413}
414
415template <typename K, typename H>
417{
418 for (const auto& key : operand)
419 {
420 Remove(key);
421 }
422}
423
424template <typename K, typename H>
426{
427 for (auto it = GetIterator(); it.IsValid();)
428 {
429 if (!operand.Contains(it.Key()))
430 it = Remove(it);
431 else
432 ++it;
433 }
434}
435
436template <typename K, typename H>
438{
439 ConstIterator iterator(*this);
440 iterator.SetToBegin();
441 return iterator;
442}
443
444template <typename K, typename H>
446{
447 ConstIterator iterator(*this);
448 iterator.SetToEnd();
449 return iterator;
450}
451
452template <typename K, typename H>
454{
455 return m_pAllocator;
456}
457
458template <typename K, typename H>
460{
461 return ((plUInt64)m_uiCapacity * sizeof(K)) + (sizeof(plUInt32) * (plUInt64)GetFlagsCapacity());
462}
463
464// private methods
465template <typename K, typename H>
466void plHashSetBase<K, H>::SetCapacity(plUInt32 uiCapacity)
467{
468 PL_ASSERT_DEBUG(plMath::IsPowerOf2(uiCapacity), "uiCapacity must be a power of two to avoid modulo during lookup.");
469 const plUInt32 uiOldCapacity = m_uiCapacity;
470 m_uiCapacity = uiCapacity;
471
472 K* pOldEntries = m_pEntries;
473 plUInt32* pOldEntryFlags = m_pEntryFlags;
474
475 m_pEntries = PL_NEW_RAW_BUFFER(m_pAllocator, K, m_uiCapacity);
476 m_pEntryFlags = PL_NEW_RAW_BUFFER(m_pAllocator, plUInt32, GetFlagsCapacity());
477 plMemoryUtils::ZeroFill(m_pEntryFlags, GetFlagsCapacity());
478
479 m_uiCount = 0;
480 for (plUInt32 i = 0; i < uiOldCapacity; ++i)
481 {
482 if (GetFlags(pOldEntryFlags, i) == VALID_ENTRY)
483 {
484 PL_VERIFY(!Insert(std::move(pOldEntries[i])), "Implementation error");
485
486 plMemoryUtils::Destruct(&pOldEntries[i], 1);
487 }
488 }
489
490 PL_DELETE_RAW_BUFFER(m_pAllocator, pOldEntries);
491 PL_DELETE_RAW_BUFFER(m_pAllocator, pOldEntryFlags);
492}
493
494template <typename K, typename H>
495template <typename CompatibleKeyType>
496PL_FORCE_INLINE plUInt32 plHashSetBase<K, H>::FindEntry(const CompatibleKeyType& key) const
497{
498 return FindEntry(H::Hash(key), key);
499}
500
501template <typename K, typename H>
502template <typename CompatibleKeyType>
503inline plUInt32 plHashSetBase<K, H>::FindEntry(plUInt32 uiHash, const CompatibleKeyType& key) const
504{
505 if (m_uiCapacity > 0)
506 {
507 plUInt32 uiIndex = uiHash & (m_uiCapacity - 1);
508 plUInt32 uiCounter = 0;
509 while (!IsFreeEntry(uiIndex) && uiCounter < m_uiCapacity)
510 {
511 if (IsValidEntry(uiIndex) && H::Equal(m_pEntries[uiIndex], key))
512 return uiIndex;
513
514 ++uiIndex;
515 if (uiIndex == m_uiCapacity)
516 uiIndex = 0;
517
518 ++uiCounter;
519 }
520 }
521 // not found
522 return plInvalidIndex;
523}
524
525#define PL_HASHSET_USE_BITFLAGS PL_ON
526
527template <typename K, typename H>
528PL_FORCE_INLINE plUInt32 plHashSetBase<K, H>::GetFlagsCapacity() const
529{
530#if PL_ENABLED(PL_HASHSET_USE_BITFLAGS)
531 return (m_uiCapacity + 15) / 16;
532#else
533 return m_uiCapacity;
534#endif
535}
536
537template <typename K, typename H>
538plUInt32 plHashSetBase<K, H>::GetFlags(plUInt32* pFlags, plUInt32 uiEntryIndex) const
539{
540#if PL_ENABLED(PL_HASHSET_USE_BITFLAGS)
541 const plUInt32 uiIndex = uiEntryIndex / 16;
542 const plUInt32 uiSubIndex = (uiEntryIndex & 15) * 2;
543 return (pFlags[uiIndex] >> uiSubIndex) & FLAGS_MASK;
544#else
545 return pFlags[uiEntryIndex] & FLAGS_MASK;
546#endif
547}
548
549template <typename K, typename H>
550void plHashSetBase<K, H>::SetFlags(plUInt32 uiEntryIndex, plUInt32 uiFlags)
551{
552#if PL_ENABLED(PL_HASHSET_USE_BITFLAGS)
553 const plUInt32 uiIndex = uiEntryIndex / 16;
554 const plUInt32 uiSubIndex = (uiEntryIndex & 15) * 2;
555 PL_ASSERT_DEBUG(uiIndex < GetFlagsCapacity(), "Out of bounds access");
556 m_pEntryFlags[uiIndex] &= ~(FLAGS_MASK << uiSubIndex);
557 m_pEntryFlags[uiIndex] |= (uiFlags << uiSubIndex);
558#else
559 PL_ASSERT_DEBUG(uiEntryIndex < GetFlagsCapacity(), "Out of bounds access");
560 m_pEntryFlags[uiEntryIndex] = uiFlags;
561#endif
562}
563
564template <typename K, typename H>
565PL_FORCE_INLINE bool plHashSetBase<K, H>::IsFreeEntry(plUInt32 uiEntryIndex) const
566{
567 return GetFlags(m_pEntryFlags, uiEntryIndex) == FREE_ENTRY;
568}
569
570template <typename K, typename H>
571PL_FORCE_INLINE bool plHashSetBase<K, H>::IsValidEntry(plUInt32 uiEntryIndex) const
572{
573 PL_ASSERT_DEBUG(uiEntryIndex < m_uiCapacity, "Out of bounds access");
574 return GetFlags(m_pEntryFlags, uiEntryIndex) == VALID_ENTRY;
575}
576
577template <typename K, typename H>
578PL_FORCE_INLINE bool plHashSetBase<K, H>::IsDeletedEntry(plUInt32 uiEntryIndex) const
579{
580 return GetFlags(m_pEntryFlags, uiEntryIndex) == DELETED_ENTRY;
581}
582
583template <typename K, typename H>
584PL_FORCE_INLINE void plHashSetBase<K, H>::MarkEntryAsFree(plUInt32 uiEntryIndex)
585{
586 SetFlags(uiEntryIndex, FREE_ENTRY);
587}
588
589template <typename K, typename H>
590PL_FORCE_INLINE void plHashSetBase<K, H>::MarkEntryAsValid(plUInt32 uiEntryIndex)
591{
592 SetFlags(uiEntryIndex, VALID_ENTRY);
593}
594
595template <typename K, typename H>
596PL_FORCE_INLINE void plHashSetBase<K, H>::MarkEntryAsDeleted(plUInt32 uiEntryIndex)
597{
598 SetFlags(uiEntryIndex, DELETED_ENTRY);
599}
600
601
602template <typename K, typename H, typename A>
604 : plHashSetBase<K, H>(A::GetAllocator())
605{
606}
607
608template <typename K, typename H, typename A>
610 : plHashSetBase<K, H>(pAllocator)
611{
612}
613
614template <typename K, typename H, typename A>
616 : plHashSetBase<K, H>(other, A::GetAllocator())
617{
618}
619
620template <typename K, typename H, typename A>
622 : plHashSetBase<K, H>(other, A::GetAllocator())
623{
624}
625
626template <typename K, typename H, typename A>
628 : plHashSetBase<K, H>(std::move(other), other.GetAllocator())
629{
630}
631
632template <typename K, typename H, typename A>
634 : plHashSetBase<K, H>(std::move(other), other.GetAllocator())
635{
636}
637
638template <typename K, typename H, typename A>
640{
642}
643
644template <typename K, typename H, typename A>
646{
648}
649
650template <typename K, typename H, typename A>
652{
653 plHashSetBase<K, H>::operator=(std::move(rhs));
654}
655
656template <typename K, typename H, typename A>
658{
659 plHashSetBase<K, H>::operator=(std::move(rhs));
660}
661
662template <typename KeyType, typename Hasher>
664{
665 plMath::Swap(this->m_pEntries, other.m_pEntries);
666 plMath::Swap(this->m_pEntryFlags, other.m_pEntryFlags);
667 plMath::Swap(this->m_uiCount, other.m_uiCount);
668 plMath::Swap(this->m_uiCapacity, other.m_uiCapacity);
669 plMath::Swap(this->m_pAllocator, other.m_pAllocator);
670}
Base class for all memory allocators.
Definition Allocator.h:23
Const iterator.
Definition HashSet.h:23
bool IsValid() const
Checks whether this iterator points to a valid element.
Definition HashSet_inl.h:37
void operator++()
Shorthand for 'Next'.
Definition HashSet_inl.h:75
void Next()
Advances the iterator to the next element in the map. The iterator will not be valid anymore,...
Definition HashSet_inl.h:55
const KeyType & Key() const
Returns the 'key' of the element that this iterator points to.
Definition HashSet_inl.h:49
bool operator==(const typename plHashSetBase< KeyType, Hasher >::ConstIterator &rhs) const
Checks whether the two iterators point to the same element.
Definition HashSet_inl.h:43
Implementation of a hashset.
Definition HashSet.h:19
bool operator==(const plHashSetBase< KeyType, Hasher > &rhs) const
Compares this table to another table.
Definition HashSet_inl.h:185
bool Contains(const CompatibleKeyType &key) const
Returns if an entry with given key exists in the table.
void Clear()
Clears the table.
Definition HashSet_inl.h:254
bool Insert(CompatibleKeyType &&key)
Inserts the key. Returns whether the key was already existing.
Definition HashSet_inl.h:270
void Intersection(const plHashSetBase< KeyType, Hasher > &operand)
Makes this set the intersection of itself and the operand.
Definition HashSet_inl.h:425
plAllocator * GetAllocator() const
Returns the allocator that is used by this instance.
Definition HashSet_inl.h:453
void Compact()
Tries to compact the hashset to avoid wasting memory.
Definition HashSet_inl.h:224
void Union(const plHashSetBase< KeyType, Hasher > &operand)
Makes this set the union of itself and the operand.
Definition HashSet_inl.h:406
~plHashSetBase()
Destructor.
Definition HashSet_inl.h:118
bool IsEmpty() const
Returns true, if the hashset does not contain any elements.
Definition HashSet_inl.h:248
ConstIterator Find(const CompatibleKeyType &key) const
Searches for key, returns a ConstIterator to it or an invalid iterator, if no such key is found....
bool ContainsSet(const plHashSetBase< KeyType, Hasher > &operand) const
Checks whether all keys of the given set are in the container.
Definition HashSet_inl.h:394
plHashSetBase(plAllocator *pAllocator)
Creates an empty hashset. Does not allocate any data yet.
Definition HashSet_inl.h:84
plUInt32 GetCount() const
Returns the number of active entries in the table.
Definition HashSet_inl.h:242
void Swap(plHashSetBase< KeyType, Hasher > &other)
Swaps this map with the other one.
Definition HashSet_inl.h:663
void Reserve(plUInt32 uiCapacity)
Expands the hashset by over-allocating the internal storage so that the load factor is lower or equal...
Definition HashSet_inl.h:206
void Difference(const plHashSetBase< KeyType, Hasher > &operand)
Makes this set the difference of itself and the operand, i.e. subtracts operand.
Definition HashSet_inl.h:416
plUInt64 GetHeapMemoryUsage() const
Returns the amount of bytes that are currently allocated on the heap.
Definition HashSet_inl.h:459
ConstIterator GetIterator() const
Returns a constant Iterator to the very first element.
Definition HashSet_inl.h:437
void operator=(const plHashSetBase< KeyType, Hasher > &rhs)
Copies the data from another hashset into this one.
ConstIterator GetEndIterator() const
Returns a constant Iterator to the first element that is not part of the hashset. Needed to implement...
Definition HashSet_inl.h:445
bool Remove(const CompatibleKeyType &key)
Removes the entry with the given key. Returns if an entry was removed.
Definition HashSet_inl.h:310
Definition HashSet.h:191
static void CopyOrMoveConstruct(Destination *pDestination, Source &&source)
This function will either move call MoveConstruct or CopyConstruct for a single element source,...
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