RavEngine
Loading...
Searching...
No Matches
PxHashInternals.h
1// Redistribution and use in source and binary forms, with or without
2// modification, are permitted provided that the following conditions
3// are met:
4// * Redistributions of source code must retain the above copyright
5// notice, this list of conditions and the following disclaimer.
6// * Redistributions in binary form must reproduce the above copyright
7// notice, this list of conditions and the following disclaimer in the
8// documentation and/or other materials provided with the distribution.
9// * Neither the name of NVIDIA CORPORATION nor the names of its
10// contributors may be used to endorse or promote products derived
11// from this software without specific prior written permission.
12//
13// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
14// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
17// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24//
25// Copyright (c) 2008-2022 NVIDIA Corporation. All rights reserved.
26// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
27// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.
28
29#ifndef PX_HASH_INTERNALS_H
30#define PX_HASH_INTERNALS_H
31
32#include "foundation/PxAllocator.h"
33#include "foundation/PxBitUtils.h"
34#include "foundation/PxMathIntrinsics.h"
35#include "foundation/PxBasicTemplates.h"
36#include "foundation/PxHash.h"
37
38#if PX_VC
39#pragma warning(push)
40#pragma warning(disable : 4127) // conditional expression is constant
41#endif
42#if !PX_DOXYGEN
43namespace physx
44{
45#endif
46template <class Entry, class Key, class HashFn, class GetKey, class PxAllocator, bool compacting>
47class PxHashBase : private PxAllocator
48{
49 void init(uint32_t initialTableSize, float loadFactor)
50 {
51 mBuffer = NULL;
52 mEntries = NULL;
53 mEntriesNext = NULL;
54 mHash = NULL;
55 mEntriesCapacity = 0;
56 mHashSize = 0;
57 mLoadFactor = loadFactor;
58 mFreeList = uint32_t(EOL);
59 mTimestamp = 0;
60 mEntriesCount = 0;
61
62 if(initialTableSize)
63 reserveInternal(initialTableSize);
64 }
65
66 public:
67 typedef Entry EntryType;
68
69 PxHashBase(uint32_t initialTableSize = 64, float loadFactor = 0.75f) : PxAllocator("hashBase")
70 {
71 init(initialTableSize, loadFactor);
72 }
73
74 PxHashBase(uint32_t initialTableSize, float loadFactor, const PxAllocator& alloc) : PxAllocator(alloc)
75 {
76 init(initialTableSize, loadFactor);
77 }
78
79 PxHashBase(const PxAllocator& alloc) : PxAllocator(alloc)
80 {
81 init(64, 0.75f);
82 }
83
85 {
86 destroy(); // No need to clear()
87
88 if(mBuffer)
89 PxAllocator::deallocate(mBuffer);
90 }
91
92 static const uint32_t EOL = 0xffffffff;
93
94 PX_INLINE Entry* create(const Key& k, bool& exists)
95 {
96 uint32_t h = 0;
97 if(mHashSize)
98 {
99 h = hash(k);
100 uint32_t index = mHash[h];
101 while(index != EOL && !HashFn().equal(GetKey()(mEntries[index]), k))
102 index = mEntriesNext[index];
103 exists = index != EOL;
104 if(exists)
105 return mEntries + index;
106 }
107 else
108 exists = false;
109
110 if(freeListEmpty())
111 {
112 grow();
113 h = hash(k);
114 }
115
116 uint32_t entryIndex = freeListGetNext();
117
118 mEntriesNext[entryIndex] = mHash[h];
119 mHash[h] = entryIndex;
120
121 mEntriesCount++;
122 mTimestamp++;
123
124 return mEntries + entryIndex;
125 }
126
127 PX_INLINE const Entry* find(const Key& k) const
128 {
129 if(!mEntriesCount)
130 return NULL;
131
132 const uint32_t h = hash(k);
133 uint32_t index = mHash[h];
134 while(index != EOL && !HashFn().equal(GetKey()(mEntries[index]), k))
135 index = mEntriesNext[index];
136 return index != EOL ? mEntries + index : NULL;
137 }
138
139 PX_INLINE bool erase(const Key& k, Entry& e)
140 {
141 if(!mEntriesCount)
142 return false;
143
144 const uint32_t h = hash(k);
145 uint32_t* ptr = mHash + h;
146 while(*ptr != EOL && !HashFn().equal(GetKey()(mEntries[*ptr]), k))
147 ptr = mEntriesNext + *ptr;
148
149 if(*ptr == EOL)
150 return false;
151
152 PX_PLACEMENT_NEW(&e, Entry)(mEntries[*ptr]);
153
154 return eraseInternal(ptr);
155 }
156
157 PX_INLINE bool erase(const Key& k)
158 {
159 if(!mEntriesCount)
160 return false;
161
162 const uint32_t h = hash(k);
163 uint32_t* ptr = mHash + h;
164 while(*ptr != EOL && !HashFn().equal(GetKey()(mEntries[*ptr]), k))
165 ptr = mEntriesNext + *ptr;
166
167 if(*ptr == EOL)
168 return false;
169
170 return eraseInternal(ptr);
171 }
172
173 PX_INLINE uint32_t size() const
174 {
175 return mEntriesCount;
176 }
177
178 PX_INLINE uint32_t capacity() const
179 {
180 return mHashSize;
181 }
182
183 void clear()
184 {
185 if(!mHashSize || mEntriesCount == 0)
186 return;
187
188 destroy();
189
190 intrinsics::memSet(mHash, EOL, mHashSize * sizeof(uint32_t));
191
192 const uint32_t sizeMinus1 = mEntriesCapacity - 1;
193 for(uint32_t i = 0; i < sizeMinus1; i++)
194 {
195 PxPrefetchLine(mEntriesNext + i, 128);
196 mEntriesNext[i] = i + 1;
197 }
198 mEntriesNext[mEntriesCapacity - 1] = uint32_t(EOL);
199 mFreeList = 0;
200 mEntriesCount = 0;
201 }
202
203 void reserve(uint32_t size)
204 {
205 if(size > mHashSize)
206 reserveInternal(size);
207 }
208
209 PX_INLINE const Entry* getEntries() const
210 {
211 return mEntries;
212 }
213
214 PX_INLINE Entry* insertUnique(const Key& k)
215 {
216 PX_ASSERT(find(k) == NULL);
217 uint32_t h = hash(k);
218
219 uint32_t entryIndex = freeListGetNext();
220
221 mEntriesNext[entryIndex] = mHash[h];
222 mHash[h] = entryIndex;
223
224 mEntriesCount++;
225 mTimestamp++;
226
227 return mEntries + entryIndex;
228 }
229
230 private:
231 void destroy()
232 {
233 for(uint32_t i = 0; i < mHashSize; i++)
234 {
235 for(uint32_t j = mHash[i]; j != EOL; j = mEntriesNext[j])
236 mEntries[j].~Entry();
237 }
238 }
239
240 template <typename HK, typename GK, class A, bool comp>
242
243 // free list management - if we're coalescing, then we use mFreeList to hold
244 // the top of the free list and it should always be equal to size(). Otherwise,
245 // we build a free list in the next() pointers.
246
247 PX_INLINE void freeListAdd(uint32_t index)
248 {
249 if(compacting)
250 {
251 mFreeList--;
252 PX_ASSERT(mFreeList == mEntriesCount);
253 }
254 else
255 {
256 mEntriesNext[index] = mFreeList;
257 mFreeList = index;
258 }
259 }
260
261 PX_INLINE void freeListAdd(uint32_t start, uint32_t end)
262 {
263 if(!compacting)
264 {
265 for(uint32_t i = start; i < end - 1; i++) // add the new entries to the free list
266 mEntriesNext[i] = i + 1;
267
268 // link in old free list
269 mEntriesNext[end - 1] = mFreeList;
270 PX_ASSERT(mFreeList != end - 1);
271 mFreeList = start;
272 }
273 else if(mFreeList == EOL) // don't reset the free ptr for the compacting hash unless it's empty
274 mFreeList = start;
275 }
276
277 PX_INLINE uint32_t freeListGetNext()
278 {
279 PX_ASSERT(!freeListEmpty());
280 if(compacting)
281 {
282 PX_ASSERT(mFreeList == mEntriesCount);
283 return mFreeList++;
284 }
285 else
286 {
287 uint32_t entryIndex = mFreeList;
288 mFreeList = mEntriesNext[mFreeList];
289 return entryIndex;
290 }
291 }
292
293 PX_INLINE bool freeListEmpty() const
294 {
295 if(compacting)
296 return mEntriesCount == mEntriesCapacity;
297 else
298 return mFreeList == EOL;
299 }
300
301 PX_INLINE void replaceWithLast(uint32_t index)
302 {
303 PX_PLACEMENT_NEW(mEntries + index, Entry)(mEntries[mEntriesCount]);
304 mEntries[mEntriesCount].~Entry();
305 mEntriesNext[index] = mEntriesNext[mEntriesCount];
306
307 uint32_t h = hash(GetKey()(mEntries[index]));
308 uint32_t* ptr;
309 for(ptr = mHash + h; *ptr != mEntriesCount; ptr = mEntriesNext + *ptr)
310 PX_ASSERT(*ptr != EOL);
311 *ptr = index;
312 }
313
314 PX_INLINE uint32_t hash(const Key& k, uint32_t hashSize) const
315 {
316 return HashFn()(k) & (hashSize - 1);
317 }
318
319 PX_INLINE uint32_t hash(const Key& k) const
320 {
321 return hash(k, mHashSize);
322 }
323
324 PX_INLINE bool eraseInternal(uint32_t* ptr)
325 {
326 const uint32_t index = *ptr;
327
328 *ptr = mEntriesNext[index];
329
330 mEntries[index].~Entry();
331
332 mEntriesCount--;
333 mTimestamp++;
334
335 if (compacting && index != mEntriesCount)
336 replaceWithLast(index);
337
338 freeListAdd(index);
339 return true;
340 }
341
342 PX_NOINLINE void reserveInternal(uint32_t size)
343 {
344 if(!PxIsPowerOfTwo(size))
345 size = PxNextPowerOfTwo(size);
346
347 PX_ASSERT(!(size & (size - 1)));
348
349 // decide whether iteration can be done on the entries directly
350 bool resizeCompact = compacting || freeListEmpty();
351
352 // define new table sizes
353 uint32_t oldEntriesCapacity = mEntriesCapacity;
354 uint32_t newEntriesCapacity = uint32_t(float(size) * mLoadFactor);
355 uint32_t newHashSize = size;
356
357 // allocate new common buffer and setup pointers to new tables
358 uint8_t* newBuffer;
359 uint32_t* newHash;
360 uint32_t* newEntriesNext;
361 Entry* newEntries;
362 {
363 uint32_t newHashByteOffset = 0;
364 uint32_t newEntriesNextBytesOffset = newHashByteOffset + newHashSize * sizeof(uint32_t);
365 uint32_t newEntriesByteOffset = newEntriesNextBytesOffset + newEntriesCapacity * sizeof(uint32_t);
366 newEntriesByteOffset += (16 - (newEntriesByteOffset & 15)) & 15;
367 uint32_t newBufferByteSize = newEntriesByteOffset + newEntriesCapacity * sizeof(Entry);
368
369 newBuffer = reinterpret_cast<uint8_t*>(PxAllocator::allocate(newBufferByteSize, __FILE__, __LINE__));
370 PX_ASSERT(newBuffer);
371
372 newHash = reinterpret_cast<uint32_t*>(newBuffer + newHashByteOffset);
373 newEntriesNext = reinterpret_cast<uint32_t*>(newBuffer + newEntriesNextBytesOffset);
374 newEntries = reinterpret_cast<Entry*>(newBuffer + newEntriesByteOffset);
375 }
376
377 // initialize new hash table
378 intrinsics::memSet(newHash, uint32_t(EOL), newHashSize * sizeof(uint32_t));
379
380 // iterate over old entries, re-hash and create new entries
381 if(resizeCompact)
382 {
383 // check that old free list is empty - we don't need to copy the next entries
384 PX_ASSERT(compacting || mFreeList == EOL);
385
386 for(uint32_t index = 0; index < mEntriesCount; ++index)
387 {
388 uint32_t h = hash(GetKey()(mEntries[index]), newHashSize);
389 newEntriesNext[index] = newHash[h];
390 newHash[h] = index;
391
392 PX_PLACEMENT_NEW(newEntries + index, Entry)(mEntries[index]);
393 mEntries[index].~Entry();
394 }
395 }
396 else
397 {
398 // copy old free list, only required for non compact resizing
399 intrinsics::memCopy(newEntriesNext, mEntriesNext, mEntriesCapacity * sizeof(uint32_t));
400
401 for(uint32_t bucket = 0; bucket < mHashSize; bucket++)
402 {
403 uint32_t index = mHash[bucket];
404 while(index != EOL)
405 {
406 uint32_t h = hash(GetKey()(mEntries[index]), newHashSize);
407 newEntriesNext[index] = newHash[h];
408 PX_ASSERT(index != newHash[h]);
409
410 newHash[h] = index;
411
412 PX_PLACEMENT_NEW(newEntries + index, Entry)(mEntries[index]);
413 mEntries[index].~Entry();
414
415 index = mEntriesNext[index];
416 }
417 }
418 }
419
420 // swap buffer and pointers
421 PxAllocator::deallocate(mBuffer);
422 mBuffer = newBuffer;
423 mHash = newHash;
424 mHashSize = newHashSize;
425 mEntriesNext = newEntriesNext;
426 mEntries = newEntries;
427 mEntriesCapacity = newEntriesCapacity;
428
429 freeListAdd(oldEntriesCapacity, newEntriesCapacity);
430 }
431
432 void grow()
433 {
434 PX_ASSERT((mFreeList == EOL) || (compacting && (mEntriesCount == mEntriesCapacity)));
435
436 uint32_t size = mHashSize == 0 ? 16 : mHashSize * 2;
437 reserve(size);
438 }
439
440 uint8_t* mBuffer;
441 Entry* mEntries;
442 uint32_t* mEntriesNext; // same size as mEntries
443 uint32_t* mHash;
444 uint32_t mEntriesCapacity;
445 uint32_t mHashSize;
446 float mLoadFactor;
447 uint32_t mFreeList;
448 uint32_t mTimestamp;
449 uint32_t mEntriesCount; // number of entries
450
451 public:
452 class Iter
453 {
454 public:
455 PX_INLINE Iter(PxHashBase& b) : mBucket(0), mEntry(uint32_t(b.EOL)), mTimestamp(b.mTimestamp), mBase(b)
456 {
457 if(mBase.mEntriesCapacity > 0)
458 {
459 mEntry = mBase.mHash[0];
460 skip();
461 }
462 }
463
464 PX_INLINE void check() const
465 {
466 PX_ASSERT(mTimestamp == mBase.mTimestamp);
467 }
468 PX_INLINE const Entry& operator*() const
469 {
470 check();
471 return mBase.mEntries[mEntry];
472 }
473 PX_INLINE Entry& operator*()
474 {
475 check();
476 return mBase.mEntries[mEntry];
477 }
478 PX_INLINE const Entry* operator->() const
479 {
480 check();
481 return mBase.mEntries + mEntry;
482 }
483 PX_INLINE Entry* operator->()
484 {
485 check();
486 return mBase.mEntries + mEntry;
487 }
488 PX_INLINE Iter operator++()
489 {
490 check();
491 advance();
492 return *this;
493 }
494 PX_INLINE Iter operator++(int)
495 {
496 check();
497 Iter i = *this;
498 advance();
499 return i;
500 }
501 PX_INLINE bool done() const
502 {
503 check();
504 return mEntry == mBase.EOL;
505 }
506
507 private:
508 PX_INLINE void advance()
509 {
510 mEntry = mBase.mEntriesNext[mEntry];
511 skip();
512 }
513 PX_INLINE void skip()
514 {
515 while(mEntry == mBase.EOL)
516 {
517 if(++mBucket == mBase.mHashSize)
518 break;
519 mEntry = mBase.mHash[mBucket];
520 }
521 }
522
523 Iter& operator=(const Iter&);
524
525 uint32_t mBucket;
526 uint32_t mEntry;
527 uint32_t mTimestamp;
528 PxHashBase& mBase;
529 };
530
535 {
536 public:
538 {
539 reset();
540 }
541
542 PX_INLINE Entry* eraseCurrentGetNext(bool eraseCurrent)
543 {
544 if(eraseCurrent && mCurrentEntryIndexPtr)
545 {
546 mBase.eraseInternal(mCurrentEntryIndexPtr);
547 // if next was valid return the same ptr, if next was EOL search new hash entry
548 if(*mCurrentEntryIndexPtr != mBase.EOL)
549 return mBase.mEntries + *mCurrentEntryIndexPtr;
550 else
551 return traverseHashEntries();
552 }
553
554 // traverse mHash to find next entry
555 if(mCurrentEntryIndexPtr == NULL)
556 return traverseHashEntries();
557
558 const uint32_t index = *mCurrentEntryIndexPtr;
559 if(mBase.mEntriesNext[index] == mBase.EOL)
560 {
561 return traverseHashEntries();
562 }
563 else
564 {
565 mCurrentEntryIndexPtr = mBase.mEntriesNext + index;
566 return mBase.mEntries + *mCurrentEntryIndexPtr;
567 }
568 }
569
570 PX_INLINE void reset()
571 {
572 mCurrentHashIndex = 0;
573 mCurrentEntryIndexPtr = NULL;
574 }
575
576 private:
577 PX_INLINE Entry* traverseHashEntries()
578 {
579 mCurrentEntryIndexPtr = NULL;
580 while (mCurrentEntryIndexPtr == NULL && mCurrentHashIndex < mBase.mHashSize)
581 {
582 if (mBase.mHash[mCurrentHashIndex] != mBase.EOL)
583 {
584 mCurrentEntryIndexPtr = mBase.mHash + mCurrentHashIndex;
585 mCurrentHashIndex++;
586 return mBase.mEntries + *mCurrentEntryIndexPtr;
587 }
588 else
589 {
590 mCurrentHashIndex++;
591 }
592 }
593 return NULL;
594 }
595
596 PxEraseIterator& operator=(const PxEraseIterator&);
597 private:
598 uint32_t* mCurrentEntryIndexPtr;
599 uint32_t mCurrentHashIndex;
600 PxHashBase& mBase;
601 };
602};
603
604template <class Entry, class Key, class HashFn, class GetKey, class PxAllocator, bool compacting>
605template <typename HK, typename GK, class A, bool comp>
606PX_NOINLINE void
608{
609 reserve(other.mEntriesCount);
610
611 for(uint32_t i = 0; i < other.mEntriesCount; i++)
612 {
613 for(uint32_t j = other.mHash[i]; j != EOL; j = other.mEntriesNext[j])
614 {
615 const Entry& otherEntry = other.mEntries[j];
616
617 bool exists;
618 Entry* newEntry = create(GK()(otherEntry), exists);
619 PX_ASSERT(!exists);
620
621 PX_PLACEMENT_NEW(newEntry, Entry)(otherEntry);
622 }
623 }
624}
625
626template <class Key, class HashFn, class PxAllocator = typename PxAllocatorTraits<Key>::Type, bool Coalesced = false>
628{
629 PX_NOCOPY(PxHashSetBase)
630 public:
631 struct GetKey
632 {
633 PX_INLINE const Key& operator()(const Key& e)
634 {
635 return e;
636 }
637 };
638
640 typedef typename BaseMap::Iter Iterator;
641
642 PxHashSetBase(uint32_t initialTableSize, float loadFactor, const PxAllocator& alloc)
643 : mBase(initialTableSize, loadFactor, alloc)
644 {
645 }
646
647 PxHashSetBase(const PxAllocator& alloc) : mBase(64, 0.75f, alloc)
648 {
649 }
650
651 PxHashSetBase(uint32_t initialTableSize = 64, float loadFactor = 0.75f) : mBase(initialTableSize, loadFactor)
652 {
653 }
654
655 bool insert(const Key& k)
656 {
657 bool exists;
658 Key* e = mBase.create(k, exists);
659 if(!exists)
660 PX_PLACEMENT_NEW(e, Key)(k);
661 return !exists;
662 }
663
664 PX_INLINE bool contains(const Key& k) const
665 {
666 return mBase.find(k) != 0;
667 }
668 PX_INLINE bool erase(const Key& k)
669 {
670 return mBase.erase(k);
671 }
672 PX_INLINE uint32_t size() const
673 {
674 return mBase.size();
675 }
676 PX_INLINE uint32_t capacity() const
677 {
678 return mBase.capacity();
679 }
680 PX_INLINE void reserve(uint32_t size)
681 {
682 mBase.reserve(size);
683 }
684 PX_INLINE void clear()
685 {
686 mBase.clear();
687 }
688
689 protected:
690 BaseMap mBase;
691};
692
693template <class Key, class Value, class HashFn, class PxAllocator = typename PxAllocatorTraits<PxPair<const Key, Value> >::Type>
695{
696 PX_NOCOPY(PxHashMapBase)
697 public:
699
700 struct GetKey
701 {
702 PX_INLINE const Key& operator()(const Entry& e)
703 {
704 return e.first;
705 }
706 };
707
709 typedef typename BaseMap::Iter Iterator;
711
712 PxHashMapBase(uint32_t initialTableSize, float loadFactor, const PxAllocator& alloc)
713 : mBase(initialTableSize, loadFactor, alloc)
714 {
715 }
716
717 PxHashMapBase(const PxAllocator& alloc) : mBase(64, 0.75f, alloc)
718 {
719 }
720
721 PxHashMapBase(uint32_t initialTableSize = 64, float loadFactor = 0.75f) : mBase(initialTableSize, loadFactor)
722 {
723 }
724
725 bool insert(const Key /*&*/ k, const Value /*&*/ v)
726 {
727 bool exists;
728 Entry* e = mBase.create(k, exists);
729 if(!exists)
730 PX_PLACEMENT_NEW(e, Entry)(k, v);
731 return !exists;
732 }
733
734 Value& operator[](const Key& k)
735 {
736 bool exists;
737 Entry* e = mBase.create(k, exists);
738 if(!exists)
739 PX_PLACEMENT_NEW(e, Entry)(k, Value());
740
741 return e->second;
742 }
743
744 PX_INLINE const Entry* find(const Key& k) const
745 {
746 return mBase.find(k);
747 }
748 PX_INLINE bool erase(const Key& k)
749 {
750 return mBase.erase(k);
751 }
752 PX_INLINE bool erase(const Key& k, Entry& e)
753 {
754 return mBase.erase(k, e);
755 }
756 PX_INLINE uint32_t size() const
757 {
758 return mBase.size();
759 }
760 PX_INLINE uint32_t capacity() const
761 {
762 return mBase.capacity();
763 }
764 PX_INLINE Iterator getIterator()
765 {
766 return Iterator(mBase);
767 }
768 PX_INLINE EraseIterator getEraseIterator()
769 {
770 return EraseIterator(mBase);
771 }
772 PX_INLINE void reserve(uint32_t size)
773 {
774 mBase.reserve(size);
775 }
776 PX_INLINE void clear()
777 {
778 mBase.clear();
779 }
780
781 protected:
782 BaseMap mBase;
783};
784#if !PX_DOXYGEN
785} // namespace physx
786#endif
787
788#if PX_VC
789#pragma warning(pop)
790#endif
791#endif
792
Definition PxAllocator.h:97
Definition PxHashInternals.h:453
Definition PxHashInternals.h:535
Definition PxHashInternals.h:48
Definition PxHashInternals.h:695
Definition PxHashInternals.h:628
Definition PxBasicTemplates.h:67
#define PX_NOINLINE
Definition PxPreprocessor.h:346
#define PX_INLINE
Definition PxPreprocessor.h:320
uint32 uint32_t
Definition fwd.hpp:131
Sorts an array of objects in ascending order, assuming that the predicate implements the < operator:
Definition PxBoxController.h:39
PX_FORCE_INLINE void PxPrefetchLine(const void *ptr, uint32_t offset=0)
Definition PxUnixIntrinsics.h:83
Definition PxHashInternals.h:701
Definition PxHashInternals.h:632