From de6630a03d9369c0b7972047b4b6711693a6513f Mon Sep 17 00:00:00 2001 From: Daniel Chappuis Date: Fri, 28 Aug 2020 23:10:19 +0200 Subject: [PATCH] Refactoring and optimization of List and Set containers --- include/reactphysics3d/containers/Map.h | 733 ++++++++---------- include/reactphysics3d/containers/Set.h | 685 ++++++++-------- include/reactphysics3d/engine/PhysicsCommon.h | 34 +- .../mathematics/mathematics_functions.h | 19 +- src/engine/PhysicsCommon.cpp | 174 ++++- src/mathematics/mathematics_functions.cpp | 24 - test/main.cpp | 2 +- test/tests/containers/TestList.h | 2 +- .../mathematics/TestMathematicsFunctions.h | 28 + 9 files changed, 844 insertions(+), 857 deletions(-) diff --git a/include/reactphysics3d/containers/Map.h b/include/reactphysics3d/containers/Map.h index a486f529..6bb4df00 100755 --- a/include/reactphysics3d/containers/Map.h +++ b/include/reactphysics3d/containers/Map.h @@ -47,202 +47,59 @@ class Map { private: - /// An entry of the map - struct Entry { - - size_t hashCode; // Hash code of the entry - int next; // Index of the next entry - Pair* keyValue; // Pointer to the pair with key and value - - /// Constructor - Entry() { - next = -1; - keyValue = nullptr; - } - - /// Constructor - Entry(size_t hashcode, int nextEntry) { - hashCode = hashcode; - next = nextEntry; - keyValue = nullptr; - } - - /// Copy-constructor - Entry(const Entry& entry) { - hashCode = entry.hashCode; - next = entry.next; - keyValue = entry.keyValue; - } - - /// Destructor - ~Entry() { - - } - - }; - // -------------------- Constants -------------------- // - /// Number of prime numbers in array - static constexpr int NB_PRIMES = 70; + /// Default load factor + static constexpr float DEFAULT_LOAD_FACTOR = 0.75; - /// Array of prime numbers for the size of the map - static const int PRIMES[NB_PRIMES]; - - /// Largest prime number - static int LARGEST_PRIME; + /// Invalid index in the array + static constexpr uint32 INVALID_INDEX = 0xffffffff; // -------------------- Attributes -------------------- // - /// Current number of used entries in the map - int mNbUsedEntries; + /// Total number of allocated entries + uint32 mNbAllocatedEntries; - /// Number of free entries among the used ones - int mNbFreeEntries; + /// Number of items in the set + uint32 mNbEntries; - /// Current capacity of the map - int mCapacity; + /// Number of buckets and size of the hash table (nbEntries = loadFactor * mHashSize) + uint32 mHashSize ; /// Array with all the buckets - int* mBuckets; + uint32* mBuckets; /// Array with all the entries - Entry* mEntries; + Pair* mEntries; + + /// For each entry, index of the next entry at the same bucket + uint32* mNextEntries; /// Memory allocator MemoryAllocator& mAllocator; /// Index to the fist free entry - int mFreeIndex; + uint32 mFreeIndex; // -------------------- Methods -------------------- // - /// Initialize the map - void initialize(int capacity) { - - // Compute the next larger prime size - mCapacity = getPrimeSize(capacity); - assert(mCapacity >= 0); - - // Allocate memory for the buckets - mBuckets = static_cast(mAllocator.allocate(mCapacity * sizeof(int))); - - // Allocate memory for the entries - mEntries = static_cast(mAllocator.allocate(mCapacity * sizeof(Entry))); - - // Initialize the buckets and entries - for (int i=0; i= 0); - } - - /// Expand the capacity of the map - void expand(int newCapacity) { - - assert(newCapacity > mCapacity); - assert(isPrimeNumber(newCapacity)); - - // Allocate memory for the buckets - int* newBuckets = static_cast(mAllocator.allocate(newCapacity * sizeof(int))); - - // Allocate memory for the entries - Entry* newEntries = static_cast(mAllocator.allocate(newCapacity * sizeof(Entry))); - - // Initialize the new buckets - for (int i=0; i 0) { - - // Copy the old entries to the new allocated memory location - std::uninitialized_copy(mEntries, mEntries + mNbUsedEntries, newEntries); - - // Destruct the old entries in the previous location - for (int i=0; i < mNbUsedEntries; i++) { - mEntries[i].~Entry(); - } - } - - // Construct the new entries - for (int i=mNbUsedEntries; i(&newEntries[i])) Entry(); - } - - // For each used entry - for (int i=0; i= 0); - } - /// Return the index of the entry with a given key or -1 if there is no entry with this key int findEntry(const K& key) const { - if (mCapacity > 0) { + if (mHashSize > 0) { const size_t hashCode = Hash()(key); - int bucket = hashCode % mCapacity; + const uint32 bucket = hashCode & (mHashSize - 1); auto keyEqual = KeyEqual(); - for (int i = mBuckets[bucket]; i >= 0; i = mEntries[i].next) { - if (mEntries[i].hashCode == hashCode && keyEqual(mEntries[i].keyValue->first, key)) { + for (uint32 i = mBuckets[bucket]; i != INVALID_INDEX; i = mNextEntries[i]) { + if (Hash()(mEntries[i].first) == hashCode && keyEqual(mEntries[i].first, key)) { return i; } } } - return -1; - } - - /// Return the prime number that is larger or equal to the number in parameter - /// for the size of the map - static int getPrimeSize(int number) { - - // Check if the next larger prime number is in the precomputed array of primes - for (int i = 0; i < NB_PRIMES; i++) { - if (PRIMES[i] >= number) return PRIMES[i]; - } - - // Manually compute the next larger prime number - for (int i = (number | 1); i < std::numeric_limits::max(); i+=2) { - - if (isPrimeNumber(i)) { - return i; - } - } - - return number; + return INVALID_INDEX; } public: @@ -255,36 +112,36 @@ class Map { private: - /// Array of entries - const Entry* mEntries; + /// Pointer to the map + const Map* mMap; - /// Capacity of the map - int mCapacity; - - /// Number of used entries in the map - int mNbUsedEntries; + /// Index of the current bucket + uint32 mCurrentBucketIndex; /// Index of the current entry - int mCurrentEntry; + uint32 mCurrentEntryIndex; /// Advance the iterator void advance() { - // If we are trying to move past the end - assert(mCurrentEntry < mNbUsedEntries); + assert(mCurrentBucketIndex < mMap->mHashSize); + assert(mCurrentEntryIndex < mMap->mNbAllocatedEntries); - for (mCurrentEntry += 1; mCurrentEntry < mNbUsedEntries; mCurrentEntry++) { - - // If the entry is not empty - if (mEntries[mCurrentEntry].keyValue != nullptr) { - - // We have found the next non empty entry - return; - } + // Try the next entry + if (mMap->mNextEntries[mCurrentEntryIndex] != INVALID_INDEX) { + mCurrentEntryIndex = mMap->mNextEntries[mCurrentEntryIndex]; + return; } - // We have not find a non empty entry, we return an iterator to the end - mCurrentEntry = mCapacity; + // Try to move to the next bucket + mCurrentEntryIndex = 0; + mCurrentBucketIndex++; + while(mCurrentBucketIndex < mMap->mHashSize && mMap->mBuckets[mCurrentBucketIndex] == INVALID_INDEX) { + mCurrentBucketIndex++; + } + if (mCurrentBucketIndex < mMap->mHashSize) { + mCurrentEntryIndex = mMap->mBuckets[mCurrentBucketIndex]; + } } public: @@ -300,29 +157,29 @@ class Map { Iterator() = default; /// Constructor - Iterator(const Entry* entries, int capacity, int nbUsedEntries, int currentEntry) - :mEntries(entries), mCapacity(capacity), mNbUsedEntries(nbUsedEntries), mCurrentEntry(currentEntry) { + Iterator(const Map* map, uint32 bucketIndex, uint32 entryIndex) + :mMap(map), mCurrentBucketIndex(bucketIndex), mCurrentEntryIndex(entryIndex) { } /// Copy constructor Iterator(const Iterator& it) - :mEntries(it.mEntries), mCapacity(it.mCapacity), mNbUsedEntries(it.mNbUsedEntries), mCurrentEntry(it.mCurrentEntry) { + :mMap(it.mMap), mCurrentBucketIndex(it.mCurrentBucketIndex), mCurrentEntryIndex(it.mCurrentEntryIndex) { } /// Deferencable reference operator*() const { - assert(mCurrentEntry >= 0 && mCurrentEntry < mNbUsedEntries); - assert(mEntries[mCurrentEntry].keyValue != nullptr); - return *(mEntries[mCurrentEntry].keyValue); + assert(mCurrentEntryIndex < mMap->mNbAllocatedEntries); + assert(mCurrentEntryIndex != INVALID_INDEX); + return mMap->mEntries[mCurrentEntryIndex]; } /// Deferencable pointer operator->() const { - assert(mCurrentEntry >= 0 && mCurrentEntry < mNbUsedEntries); - assert(mEntries[mCurrentEntry].keyValue != nullptr); - return mEntries[mCurrentEntry].keyValue; + assert(mCurrentEntryIndex < mMap->mNbAllocatedEntries); + assert(mCurrentEntryIndex != INVALID_INDEX); + return &(mMap->mEntries[mCurrentEntryIndex]); } /// Post increment (it++) @@ -332,7 +189,7 @@ class Map { } /// Pre increment (++it) - Iterator operator++(int number) { + Iterator operator++(int) { Iterator tmp = *this; advance(); return tmp; @@ -340,69 +197,73 @@ class Map { /// Equality operator (it == end()) bool operator==(const Iterator& iterator) const { - return mCurrentEntry == iterator.mCurrentEntry && mEntries == iterator.mEntries; + return mCurrentBucketIndex == iterator.mCurrentBucketIndex && mCurrentEntryIndex == iterator.mCurrentEntryIndex && mMap == iterator.mMap; } /// Inequality operator (it != end()) bool operator!=(const Iterator& iterator) const { return !(*this == iterator); } + + /// Overloaded assignment operator + Iterator& operator=(const Iterator& it) { + + // Check for self assignment + if (this != &it) { + + mMap = it.mMap; + mCurrentBucketIndex = it.mCurrentBucketIndex; + mCurrentEntryIndex = it.mCurrentEntryIndex; + } + + return *this; + } }; // -------------------- Methods -------------------- // /// Constructor - Map(MemoryAllocator& allocator, size_t capacity = 0) - : mNbUsedEntries(0), mNbFreeEntries(0), mCapacity(0), mBuckets(nullptr), - mEntries(nullptr), mAllocator(allocator), mFreeIndex(-1) { - - // If the largest prime has not been computed yet - if (LARGEST_PRIME == -1) { - - // Compute the largest prime number (largest map capacity) - LARGEST_PRIME = getPrimeSize(PRIMES[NB_PRIMES - 1] + 2); - } + Map(MemoryAllocator& allocator, uint32 capacity = 0) + : mNbAllocatedEntries(0), mNbEntries(0), mHashSize(0), mBuckets(nullptr), + mEntries(nullptr), mNextEntries(nullptr), mAllocator(allocator), mFreeIndex(INVALID_INDEX) { if (capacity > 0) { - initialize(capacity); + reserve(capacity); } } /// Copy constructor Map(const Map& map) - :mNbUsedEntries(map.mNbUsedEntries), mNbFreeEntries(map.mNbFreeEntries), mCapacity(map.mCapacity), - mBuckets(nullptr), mEntries(nullptr), mAllocator(map.mAllocator), mFreeIndex(map.mFreeIndex) { + :mNbAllocatedEntries(map.mNbAllocatedEntries), mNbEntries(map.mNbEntries), mHashSize(map.mHashSize), + mBuckets(nullptr), mEntries(nullptr), mNextEntries(nullptr), mAllocator(map.mAllocator), mFreeIndex(map.mFreeIndex) { - assert(capacity() >= 0); + // Allocate memory for the buckets + mBuckets = static_cast(mAllocator.allocate(mHashSize * sizeof(uint32))); - if (mCapacity > 0) { + // Allocate memory for the entries + mEntries = static_cast*>(mAllocator.allocate(mNbAllocatedEntries * sizeof(Pair))); + mNextEntries = static_cast(mAllocator.allocate(mNbAllocatedEntries * sizeof(uint32))); - // Allocate memory for the buckets - mBuckets = static_cast(mAllocator.allocate(mCapacity * sizeof(int))); + // Copy the buckets array + std::memcpy(mBuckets, map.mBuckets, mHashSize * sizeof(uint32)); - // Allocate memory for the entries - mEntries = static_cast(mAllocator.allocate(mCapacity * sizeof(Entry))); + // Copy the next entries indices + std::memcpy(mNextEntries, map.mNextEntries, mNbAllocatedEntries * sizeof(uint32)); - // Copy the buckets - std::uninitialized_copy(map.mBuckets, map.mBuckets + mCapacity, mBuckets); + // Copy the entries + for (uint32 i=0; i(map.mEntries[entryIndex]); - if (map.mEntries[i].keyValue != nullptr) { - mEntries[i].keyValue = static_cast*>(mAllocator.allocate(sizeof(Pair))); - new (mEntries[i].keyValue) Pair(*(map.mEntries[i].keyValue)); - } + entryIndex = mNextEntries[entryIndex]; } - } - - assert(size() >= 0); - assert((*this) == map); } /// Destructor @@ -412,95 +273,162 @@ class Map { } /// Allocate memory for a given number of elements - void reserve(int capacity) { + void reserve(uint32 capacity) { - if (capacity <= mCapacity) return; + if (capacity <= mHashSize) return; - if (capacity > LARGEST_PRIME && LARGEST_PRIME > mCapacity) { - capacity = LARGEST_PRIME; - } - else { - capacity = getPrimeSize(capacity); - } + if (capacity < 16) capacity = 16; - expand(capacity); + // Make sure we have a power of two size + if (!isPowerOfTwo(capacity)) { + capacity = nextPowerOfTwo32Bits(capacity); + } + + assert(capacity < INVALID_INDEX); + + assert(capacity > mHashSize); + + // Allocate memory for the buckets + uint32* newBuckets = static_cast(mAllocator.allocate(capacity * sizeof(uint32))); + + // Allocate memory for the entries + const uint32 nbAllocatedEntries = capacity * DEFAULT_LOAD_FACTOR; + assert(nbAllocatedEntries > 0); + Pair* newEntries = static_cast*>(mAllocator.allocate(nbAllocatedEntries * sizeof(Pair))); + uint32* newNextEntries = static_cast(mAllocator.allocate(nbAllocatedEntries * sizeof(uint32))); + + assert(newEntries != nullptr); + assert(newNextEntries != nullptr); + + // Initialize the new buckets + for (uint32 i=0; i 0) { + + assert(mNextEntries != nullptr); + + // Copy the free nodes indices in the nextEntries array + std::memcpy(newNextEntries, mNextEntries, mNbAllocatedEntries * sizeof(uint32)); + } + + // Recompute the buckets (hash) with the new hash size + for (uint32 i=0; i(mEntries[entryIndex]); + mEntries[entryIndex].~Pair(); + + entryIndex = mNextEntries[entryIndex]; + } + } + + if (mNbAllocatedEntries > 0) { + + // Release previously allocated memory + mAllocator.release(mBuckets, mHashSize * sizeof(uint32)); + mAllocator.release(mEntries, mNbAllocatedEntries * sizeof(Pair)); + mAllocator.release(mNextEntries, mNbAllocatedEntries * sizeof(uint32)); + } + + // Add the new entries to the free list + for (uint32 i=mNbAllocatedEntries; i < nbAllocatedEntries-1; i++) { + newNextEntries[i] = i + 1; + } + newNextEntries[nbAllocatedEntries - 1] = mFreeIndex; + mFreeIndex = mNbAllocatedEntries; + + mHashSize = capacity; + mNbAllocatedEntries = nbAllocatedEntries; + mBuckets = newBuckets; + mEntries = newEntries; + mNextEntries = newNextEntries; + + assert(mFreeIndex != INVALID_INDEX); } /// Return true if the map contains an item with the given key bool containsKey(const K& key) const { - return findEntry(key) != -1; + return findEntry(key) != INVALID_INDEX; } /// Add an element into the map - void add(const Pair& keyValue, bool insertIfAlreadyPresent = false) { + /// Returns true if the item has been inserted and false otherwise. + bool add(const Pair& keyValue, bool insertIfAlreadyPresent = false) { - if (mCapacity == 0) { - initialize(0); - } + uint32 bucket; - // Compute the hash code of the key + // Compute the hash code of the value const size_t hashCode = Hash()(keyValue.first); - // Compute the corresponding bucket index - int bucket = hashCode % mCapacity; + if (mHashSize > 0) { - auto keyEqual = KeyEqual(); + // Compute the corresponding bucket index + bucket = hashCode & (mHashSize - 1); - // Check if the item is already in the map - for (int i = mBuckets[bucket]; i >= 0; i = mEntries[i].next) { + auto keyEqual = KeyEqual(); - // If there is already an item with the same key in the map - if (mEntries[i].hashCode == hashCode && keyEqual(mEntries[i].keyValue->first, keyValue.first)) { + // Check if the item is already in the set + for (uint32 i = mBuckets[bucket]; i != INVALID_INDEX; i = mNextEntries[i]) { - if (insertIfAlreadyPresent) { + // If there is already an item with the same value in the set + if (Hash()(mEntries[i].first) == hashCode && keyEqual(mEntries[i].first, keyValue.first)) { - // Destruct the previous key/value - mEntries[i].keyValue->~Pair(); + if (insertIfAlreadyPresent) { - // Copy construct the new key/value - new (mEntries[i].keyValue) Pair(keyValue); + // Destruct the previous key/value + mEntries[i].~Pair(); - return; - } - else { - throw std::runtime_error("The key and value pair already exists in the map"); + // Copy construct the new key/value + new (mEntries + i) Pair(keyValue); + + return true; + } + else { + assert(false); + throw std::runtime_error("The key and value pair already exists in the map"); + } } } } size_t entryIndex; - // If there are free entries to use - if (mNbFreeEntries > 0) { - assert(mFreeIndex >= 0); - entryIndex = mFreeIndex; - mFreeIndex = mEntries[entryIndex].next; - mNbFreeEntries--; - } - else { + // If there are no more free entries to use + if (mFreeIndex == INVALID_INDEX) { - // If we need to allocator more entries - if (mNbUsedEntries == mCapacity) { + // Allocate more memory + reserve(mHashSize == 0 ? 16 : mHashSize * 2); - // Allocate more memory - reserve(mCapacity * 2); - - // Recompute the bucket index - bucket = hashCode % mCapacity; - } - - entryIndex = mNbUsedEntries; - mNbUsedEntries++; + // Recompute the bucket index + bucket = hashCode & (mHashSize - 1); } - assert(size() >= 0); - assert(mEntries[entryIndex].keyValue == nullptr); - mEntries[entryIndex].hashCode = hashCode; - mEntries[entryIndex].next = mBuckets[bucket]; - mEntries[entryIndex].keyValue = static_cast*>(mAllocator.allocate(sizeof(Pair))); - assert(mEntries[entryIndex].keyValue != nullptr); - new (mEntries[entryIndex].keyValue) Pair(keyValue); + assert(mNbEntries < mNbAllocatedEntries); + assert(mFreeIndex != INVALID_INDEX); + + // Get the next free entry + entryIndex = mFreeIndex; + mFreeIndex = mNextEntries[entryIndex]; + + mNbEntries++; + + mNextEntries[entryIndex] = mBuckets[bucket]; + new (mEntries + entryIndex) Pair(keyValue); mBuckets[bucket] = entryIndex; + + return true; } /// Remove the element pointed by some iterator @@ -517,110 +445,102 @@ class Map { /// the one that has been removed Iterator remove(const K& key) { - if (mCapacity > 0) { + if (mHashSize > 0) { const size_t hashcode = Hash()(key); - int bucket = hashcode % mCapacity; - int last = -1; auto keyEqual = KeyEqual(); + const uint32 bucket = hashcode & (mHashSize - 1); + uint32 last = INVALID_INDEX; + for (uint32 i = mBuckets[bucket]; i != INVALID_INDEX; last = i, i = mNextEntries[i]) { - for (int i = mBuckets[bucket]; i >= 0; last = i, i = mEntries[i].next) { + // If we have found the item + if (Hash()(mEntries[i].first) == hashcode && keyEqual(mEntries[i].first, key)) { - if (mEntries[i].hashCode == hashcode && keyEqual(mEntries[i].keyValue->first, key)) { - - if (last < 0 ) { - mBuckets[bucket] = mEntries[i].next; + if (last == INVALID_INDEX) { + mBuckets[bucket] = mNextEntries[i]; } else { - mEntries[last].next = mEntries[i].next; + mNextEntries[last] = mNextEntries[i]; } - // Release memory for the key/value pair if any - if (mEntries[i].keyValue != nullptr) { - mEntries[i].keyValue->~Pair(); - mAllocator.release(mEntries[i].keyValue, sizeof(Pair)); - mEntries[i].keyValue = nullptr; - } - assert(mEntries[i].keyValue == nullptr); - mEntries[i].next = mFreeIndex; + uint32 nextEntryIndex = mNextEntries[i]; + uint32 nextBucketIndex = bucket; + + mEntries[i].~Pair(); + mNextEntries[i] = mFreeIndex; mFreeIndex = i; - mNbFreeEntries++; + mNbEntries--; - // Find the next entry to return the iterator - for (i += 1; i < mNbUsedEntries; i++) { - - // If the entry is not empty - if (mEntries[i].keyValue != nullptr) { - - // We have found the next non empty entry - return Iterator(mEntries, mCapacity, mNbUsedEntries, i); + // Find the next entry to return an iterator + if (nextEntryIndex == INVALID_INDEX) { + nextEntryIndex = 0; + nextBucketIndex++; + while(nextBucketIndex < mHashSize && mBuckets[nextBucketIndex] == INVALID_INDEX) { + nextBucketIndex++; + } + if (nextBucketIndex < mHashSize) { + nextEntryIndex = mBuckets[nextBucketIndex]; } } - return end(); + // We have found the next non empty entry + return Iterator(this, nextBucketIndex, nextEntryIndex); } } } - assert(size() >= 0); - - // Return the end iterator return end(); } /// Clear the map void clear(bool releaseMemory = false) { - if (mNbUsedEntries > 0) { + for (uint32 i=0; i~Pair(); - mAllocator.release(mEntries[i].keyValue, sizeof(Pair)); - mEntries[i].keyValue = nullptr; - } + // Destroy the entry + mEntries[entryIndex].~Pair(); + + uint32 nextEntryIndex = mNextEntries[entryIndex]; + + // Add entry to the free list + mNextEntries[entryIndex] = mFreeIndex; + mFreeIndex = entryIndex; + + entryIndex = nextEntryIndex; } - mFreeIndex = -1; - mNbUsedEntries = 0; - mNbFreeEntries = 0; - - assert(size() >= 0); + mBuckets[i] = INVALID_INDEX; } - // If elements have been allocated - if (releaseMemory && mCapacity > 0) { + if (releaseMemory && mNbAllocatedEntries > 0) { - // Destroy the entries - for (int i=0; i < mCapacity; i++) { - mEntries[i].~Entry(); - } + // Release previously allocated memory + mAllocator.release(mBuckets, mHashSize * sizeof(uint32)); + mAllocator.release(mEntries, mNbAllocatedEntries * sizeof(Pair)); + mAllocator.release(mNextEntries, mNbAllocatedEntries * sizeof(uint32)); - // Release memory - mAllocator.release(mBuckets, mCapacity * sizeof(int)); - mAllocator.release(mEntries, mCapacity * sizeof(Entry)); - - mCapacity = 0; mBuckets = nullptr; mEntries = nullptr; + mNextEntries = nullptr; + + mNbAllocatedEntries = 0; + mHashSize = 0; } - assert(size() == 0); - } + mNbEntries = 0; + } /// Return the number of elements in the map - int size() const { - assert(mNbUsedEntries - mNbFreeEntries >= 0); - return mNbUsedEntries - mNbFreeEntries; + uint32 size() const { + return mNbEntries; } /// Return the capacity of the map - int capacity() const { - return mCapacity; + uint32 capacity() const { + return mHashSize; } /// Try to find an item of the map given a key. @@ -628,68 +548,54 @@ class Map { /// an iterator pointing to the end if not found Iterator find(const K& key) const { - int bucket; - int entry = -1; + uint32 bucket; + uint32 entry = INVALID_INDEX; - if (mCapacity > 0) { + if (mHashSize > 0) { const size_t hashCode = Hash()(key); - bucket = hashCode % mCapacity; - + bucket = hashCode & (mHashSize - 1); auto keyEqual = KeyEqual(); - for (int i = mBuckets[bucket]; i >= 0; i = mEntries[i].next) { - if (mEntries[i].hashCode == hashCode && keyEqual(mEntries[i].keyValue->first, key)) { + for (uint32 i = mBuckets[bucket]; i != INVALID_INDEX; i = mNextEntries[i]) { + if (Hash()(mEntries[i].first) == hashCode && keyEqual(mEntries[i].first, key)) { entry = i; break; } } } - if (entry == -1) { + if (entry == INVALID_INDEX) { return end(); } - assert(mEntries[entry].keyValue != nullptr); - - return Iterator(mEntries, mCapacity, mNbUsedEntries, entry); + return Iterator(this, bucket, entry); } /// Overloaded index operator V& operator[](const K& key) { - int entry = -1; + const uint32 entry = findEntry(key); - if (mCapacity > 0) { - entry = findEntry(key); - } - - if (entry == -1) { + if (entry == INVALID_INDEX) { assert(false); throw std::runtime_error("No item with given key has been found in the map"); } - assert(mEntries[entry].keyValue != nullptr); - - return mEntries[entry].keyValue->second; + return mEntries[entry].second; } /// Overloaded index operator const V& operator[](const K& key) const { - int entry = -1; + const uint32 entry = findEntry(key); - if (mCapacity > 0) { - entry = findEntry(key); - } - - if (entry == -1) { + if (entry == INVALID_INDEX) { + assert(false); throw std::runtime_error("No item with given key has been found in the map"); } - assert(mEntries[entry].keyValue != nullptr); - - return mEntries[entry].keyValue->second; + return mEntries[entry].second; } /// Overloaded equality operator @@ -729,40 +635,38 @@ class Map { // Clear the map clear(true); - if (map.mCapacity > 0) { + mNbAllocatedEntries = map.mNbAllocatedEntries; + mNbEntries = map.mNbEntries; + mHashSize = map.mHashSize; + mFreeIndex = map.mFreeIndex; - // Compute the next larger prime size - mCapacity = getPrimeSize(map.mCapacity); - assert(mCapacity >= 0); + // Allocate memory for the buckets + mBuckets = static_cast(mAllocator.allocate(mHashSize * sizeof(uint32))); - // Allocate memory for the buckets - mBuckets = static_cast(mAllocator.allocate(mCapacity * sizeof(int))); + // Allocate memory for the entries + mEntries = static_cast*>(mAllocator.allocate(mNbAllocatedEntries * sizeof(Pair))); + mNextEntries = static_cast(mAllocator.allocate(mNbAllocatedEntries * sizeof(uint32))); - // Allocate memory for the entries - mEntries = static_cast(mAllocator.allocate(mCapacity * sizeof(Entry))); + // Copy the buckets array + std::memcpy(mBuckets, map.mBuckets, mHashSize * sizeof(uint32)); - // Copy the buckets - std::uninitialized_copy(map.mBuckets, map.mBuckets + mCapacity, mBuckets); + // Copy the next entries indices + std::memcpy(mNextEntries, map.mNextEntries, mNbAllocatedEntries * sizeof(uint32)); - // Copy the entries - for (int i=0; i < mCapacity; i++) { + // Copy the entries + for (uint32 i=0; i*>(mAllocator.allocate(sizeof(Pair))); - new (mEntries[i].keyValue) Pair(*(map.mEntries[i].keyValue)); - } + // Copy the entry to the new location and destroy the previous one + new (mEntries + entryIndex) Pair(map.mEntries[entryIndex]); + + entryIndex = mNextEntries[entryIndex]; } - - mNbUsedEntries = map.mNbUsedEntries; - mNbFreeEntries = map.mNbFreeEntries; - mFreeIndex = map.mFreeIndex; } } - assert(size() >= 0); - return *this; } @@ -777,33 +681,28 @@ class Map { } // Find the first used entry - int entry; - for (entry=0; entry < mNbUsedEntries; entry++) { - if (mEntries[entry].keyValue != nullptr) { - return Iterator(mEntries, mCapacity, mNbUsedEntries, entry); - } + uint32 bucketIndex = 0; + while (mBuckets[bucketIndex] == INVALID_INDEX) { + + bucketIndex++; } - assert(false); - return end(); + assert(bucketIndex < mHashSize); + assert(mBuckets[bucketIndex] != INVALID_INDEX); + + return Iterator(this, bucketIndex, mBuckets[bucketIndex]); } /// Return a end iterator Iterator end() const { - return Iterator(mEntries, mCapacity, mNbUsedEntries, mCapacity); + return Iterator(this, mHashSize, 0); } + + // ---------- Friendship ---------- // + + friend class Iterator; }; -template -const int Map::PRIMES[NB_PRIMES] = {3, 7, 11, 17, 23, 29, 37, 47, 59, 71, 89, 107, 131, 163, 197, 239, 293, 353, 431, 521, 631, 761, 919, - 1103, 1327, 1597, 1931, 2333, 2801, 3371, 4049, 4861, 5839, 7013, 8419, 10103, 12143, 14591, - 17519, 21023, 25229, 30293, 36353, 43627, 52361, 62851, 75431, 90523, 108631, 130363, 156437, - 187751, 225307, 270371, 324449, 389357, 467237, 560689, 672827, 807403, 968897, 1162687, 1395263, - 1674319, 2009191, 2411033, 2893249, 3471899, 4166287, 4999559}; - -template -int Map::LARGEST_PRIME = -1; - } #endif diff --git a/include/reactphysics3d/containers/Set.h b/include/reactphysics3d/containers/Set.h index 17088fe3..f54c7a69 100755 --- a/include/reactphysics3d/containers/Set.h +++ b/include/reactphysics3d/containers/Set.h @@ -47,197 +47,59 @@ class Set { private: - /// An entry of the set - struct Entry { - - size_t hashCode; // Hash code of the entry - int next; // Index of the next entry - V* value; // Pointer to the value stored in the entry - - /// Constructor - Entry() { - next = -1; - value = nullptr; - } - - /// Constructor - Entry(size_t hashcode, int nextEntry) { - hashCode = hashcode; - next = nextEntry; - value = nullptr; - } - - /// Copy-constructor - Entry(const Entry& entry) { - hashCode = entry.hashCode; - next = entry.next; - value = entry.value; - } - - /// Destructor - ~Entry() { - - } - - }; - // -------------------- Constants -------------------- // - /// Number of prime numbers in array - static constexpr int NB_PRIMES = 70; + /// Default load factor + static constexpr float DEFAULT_LOAD_FACTOR = 0.75; - /// Array of prime numbers for the size of the set - static const int PRIMES[NB_PRIMES]; - - /// Largest prime number - static int LARGEST_PRIME; + /// Invalid index in the array + static constexpr uint32 INVALID_INDEX = 0xffffffff; // -------------------- Attributes -------------------- // - /// Current number of used entries in the set - int mNbUsedEntries; + /// Total number of allocated entries + uint32 mNbAllocatedEntries; - /// Number of free entries among the used ones - int mNbFreeEntries; + /// Number of items in the set + uint32 mNbEntries; - /// Current capacity of the set - int mCapacity; + /// Number of buckets and size of the hash table (nbEntries = loadFactor * mHashSize) + uint32 mHashSize ; /// Array with all the buckets - int* mBuckets; + uint32* mBuckets; - /// Array with all the entries - Entry* mEntries; + /// Array with all the entries (nbEntries = loadFactor * mHashSize) + V* mEntries; + + /// For each entry, index of the next entry at the same bucket + uint32* mNextEntries; /// Memory allocator MemoryAllocator& mAllocator; /// Index to the fist free entry - int mFreeIndex; + uint32 mFreeIndex; // -------------------- Methods -------------------- // - /// Initialize the set - void initialize(int capacity) { - - // Compute the next larger prime size - mCapacity = getPrimeSize(capacity); - - // Allocate memory for the buckets - mBuckets = static_cast(mAllocator.allocate(mCapacity * sizeof(int))); - - // Allocate memory for the entries - mEntries = static_cast(mAllocator.allocate(mCapacity * sizeof(Entry))); - - // Initialize the buckets and entries - for (int i=0; i mCapacity); - assert(isPrimeNumber(newCapacity)); - - // Allocate memory for the buckets - int* newBuckets = static_cast(mAllocator.allocate(newCapacity * sizeof(int))); - - // Allocate memory for the entries - Entry* newEntries = static_cast(mAllocator.allocate(newCapacity * sizeof(Entry))); - - // Initialize the new buckets - for (int i=0; i 0) { - - // Copy the old entries to the new allocated memory location - std::uninitialized_copy(mEntries, mEntries + mNbUsedEntries, newEntries); - - // Destruct the old entries at previous location - for (int i=0; i(&newEntries[i])) Entry(); - } - - // For each used entry - for (int i=0; i 0) { + if (mHashSize > 0) { - size_t hashCode = Hash()(value); - int bucket = hashCode % mCapacity; - auto keyEqual = KeyEqual(); + const size_t hashCode = Hash()(value); + const uint32 bucket = hashCode & (mHashSize - 1); + auto keyEqual = KeyEqual(); - for (int i = mBuckets[bucket]; i >= 0; i = mEntries[i].next) { - if (mEntries[i].hashCode == hashCode && keyEqual(*mEntries[i].value, value)) { + for (uint32 i = mBuckets[bucket]; i != INVALID_INDEX; i = mNextEntries[i]) { + if (Hash()(mEntries[i]) == hashCode && keyEqual(mEntries[i], value)) { return i; } } } - return -1; - } - - /// Return the prime number that is larger or equal to the number in parameter - /// for the size of the set - static int getPrimeSize(int number) { - - // Check if the next larger prime number is in the precomputed array of primes - for (int i = 0; i < NB_PRIMES; i++) { - if (PRIMES[i] >= number) return PRIMES[i]; - } - - // Manually compute the next larger prime number - for (int i = (number | 1); i < std::numeric_limits::max(); i+=2) { - - if (isPrimeNumber(i)) { - return i; - } - } - - return number; + return INVALID_INDEX; } public: @@ -250,36 +112,36 @@ class Set { private: - /// Array of entries - const Entry* mEntries; + /// Pointer to the set + const Set* mSet; - /// Capacity of the map - int mCapacity; - - /// Number of used entries in the map - int mNbUsedEntries; + /// Index of the current bucket + uint32 mCurrentBucketIndex; /// Index of the current entry - int mCurrentEntry; + uint32 mCurrentEntryIndex; /// Advance the iterator void advance() { - // If we are trying to move past the end - assert(mCurrentEntry < mNbUsedEntries); + assert(mCurrentBucketIndex < mSet->mHashSize); + assert(mCurrentEntryIndex < mSet->mNbAllocatedEntries); - for (mCurrentEntry += 1; mCurrentEntry < mNbUsedEntries; mCurrentEntry++) { - - // If the entry is not empty - if (mEntries[mCurrentEntry].value != nullptr) { - - // We have found the next non empty entry - return; - } + // Try the next entry + if (mSet->mNextEntries[mCurrentEntryIndex] != INVALID_INDEX) { + mCurrentEntryIndex = mSet->mNextEntries[mCurrentEntryIndex]; + return; } - // We have not find a non empty entry, we return an iterator to the end - mCurrentEntry = mCapacity; + // Try to move to the next bucket + mCurrentEntryIndex = 0; + mCurrentBucketIndex++; + while(mCurrentBucketIndex < mSet->mHashSize && mSet->mBuckets[mCurrentBucketIndex] == INVALID_INDEX) { + mCurrentBucketIndex++; + } + if (mCurrentBucketIndex < mSet->mHashSize) { + mCurrentEntryIndex = mSet->mBuckets[mCurrentBucketIndex]; + } } public: @@ -295,29 +157,29 @@ class Set { Iterator() = default; /// Constructor - Iterator(const Entry* entries, int capacity, int nbUsedEntries, int currentEntry) - :mEntries(entries), mCapacity(capacity), mNbUsedEntries(nbUsedEntries), mCurrentEntry(currentEntry) { + Iterator(const Set* set, uint32 bucketIndex, uint32 entryIndex) + :mSet(set), mCurrentBucketIndex(bucketIndex), mCurrentEntryIndex(entryIndex) { } /// Copy constructor Iterator(const Iterator& it) - :mEntries(it.mEntries), mCapacity(it.mCapacity), mNbUsedEntries(it.mNbUsedEntries), mCurrentEntry(it.mCurrentEntry) { + :mSet(it.mSet), mCurrentBucketIndex(it.mCurrentBucketIndex), mCurrentEntryIndex(it.mCurrentEntryIndex) { } /// Deferencable reference operator*() const { - assert(mCurrentEntry >= 0 && mCurrentEntry < mNbUsedEntries); - assert(mEntries[mCurrentEntry].value != nullptr); - return *(mEntries[mCurrentEntry].value); + assert(mCurrentEntryIndex < mSet->mNbAllocatedEntries); + assert(mCurrentEntryIndex != INVALID_INDEX); + return mSet->mEntries[mCurrentEntryIndex]; } /// Deferencable pointer operator->() const { - assert(mCurrentEntry >= 0 && mCurrentEntry < mNbUsedEntries); - assert(mEntries[mCurrentEntry].value != nullptr); - return mEntries[mCurrentEntry].value; + assert(mCurrentEntryIndex < mSet->mNbAllocatedEntries); + assert(mCurrentEntryIndex != INVALID_INDEX); + return &(mSet->mEntries[mCurrentEntryIndex]); } /// Post increment (it++) @@ -335,61 +197,71 @@ class Set { /// Equality operator (it == end()) bool operator==(const Iterator& iterator) const { - return mCurrentEntry == iterator.mCurrentEntry && mEntries == iterator.mEntries; + return mCurrentBucketIndex == iterator.mCurrentBucketIndex && mCurrentEntryIndex == iterator.mCurrentEntryIndex && mSet == iterator.mSet; } /// Inequality operator (it != end()) bool operator!=(const Iterator& iterator) const { return !(*this == iterator); } + + /// Overloaded assignment operator + Iterator& operator=(const Iterator& it) { + + // Check for self assignment + if (this != &it) { + + mSet = it.mSet; + mCurrentBucketIndex = it.mCurrentBucketIndex; + mCurrentEntryIndex = it.mCurrentEntryIndex; + } + + return *this; + } }; // -------------------- Methods -------------------- // /// Constructor - Set(MemoryAllocator& allocator, size_t capacity = 0) - : mNbUsedEntries(0), mNbFreeEntries(0), mCapacity(0), mBuckets(nullptr), - mEntries(nullptr), mAllocator(allocator), mFreeIndex(-1) { - - // If the largest prime has not been computed yet - if (LARGEST_PRIME == -1) { - - // Compute the largest prime number (largest map capacity) - LARGEST_PRIME = getPrimeSize(PRIMES[NB_PRIMES - 1] + 2); - } + Set(MemoryAllocator& allocator, uint32 capacity = 0) + : mNbAllocatedEntries(0), mNbEntries(0), mHashSize(0), mBuckets(nullptr), + mEntries(nullptr), mNextEntries(nullptr), mAllocator(allocator), mFreeIndex(INVALID_INDEX) { if (capacity > 0) { - initialize(capacity); + reserve(capacity); } } /// Copy constructor Set(const Set& set) - :mNbUsedEntries(set.mNbUsedEntries), mNbFreeEntries(set.mNbFreeEntries), mCapacity(set.mCapacity), - mBuckets(nullptr), mEntries(nullptr), mAllocator(set.mAllocator), mFreeIndex(set.mFreeIndex) { + :mNbAllocatedEntries(set.mNbAllocatedEntries), mNbEntries(set.mNbEntries), mHashSize(set.mHashSize), + mBuckets(nullptr), mEntries(nullptr), mNextEntries(nullptr), mAllocator(set.mAllocator), mFreeIndex(set.mFreeIndex) { - if (mCapacity > 0) { + // Allocate memory for the buckets + mBuckets = static_cast(mAllocator.allocate(mHashSize * sizeof(uint32))); - // Allocate memory for the buckets - mBuckets = static_cast(mAllocator.allocate(mCapacity * sizeof(int))); + // Allocate memory for the entries + mEntries = static_cast(mAllocator.allocate(mNbAllocatedEntries * sizeof(V))); + mNextEntries = static_cast(mAllocator.allocate(mNbAllocatedEntries * sizeof(uint32))); - // Allocate memory for the entries - mEntries = static_cast(mAllocator.allocate(mCapacity * sizeof(Entry))); + // Copy the buckets array + std::memcpy(mBuckets, set.mBuckets, mHashSize * sizeof(uint32)); - // Copy the buckets - std::uninitialized_copy(set.mBuckets, set.mBuckets + mCapacity, mBuckets); + // Copy the next entries indices + std::memcpy(mNextEntries, set.mNextEntries, mNbAllocatedEntries * sizeof(uint32)); - // Copy the entries - for (int i=0; i < mCapacity; i++) { + // Copy the entries + for (uint32 i=0; i(mAllocator.allocate(sizeof(V))); - new (mEntries[i].value) V(*(set.mEntries[i].value)); - } + // Copy the entry to the new location and destroy the previous one + new (mEntries + entryIndex) V(set.mEntries[entryIndex]); + + entryIndex = mNextEntries[entryIndex]; } } } @@ -401,82 +273,146 @@ class Set { } /// Allocate memory for a given number of elements - void reserve(int capacity) { + void reserve(uint32 capacity) { - if (capacity <= mCapacity) return; + if (capacity <= mHashSize) return; - if (capacity > LARGEST_PRIME && LARGEST_PRIME > mCapacity) { - capacity = LARGEST_PRIME; - } - else { - capacity = getPrimeSize(capacity); - } + if (capacity < 16) capacity = 16; - expand(capacity); + // Make sure we have a power of two size + if (!isPowerOfTwo(capacity)) { + capacity = nextPowerOfTwo32Bits(capacity); + } + + assert(capacity < INVALID_INDEX); + + assert(capacity > mHashSize); + + // Allocate memory for the buckets + uint32* newBuckets = static_cast(mAllocator.allocate(capacity * sizeof(uint32))); + + // Allocate memory for the entries + const uint32 nbAllocatedEntries = capacity * DEFAULT_LOAD_FACTOR; + assert(nbAllocatedEntries > 0); + V* newEntries = static_cast(mAllocator.allocate(nbAllocatedEntries * sizeof(V))); + uint32* newNextEntries = static_cast(mAllocator.allocate(nbAllocatedEntries * sizeof(uint32))); + + assert(newEntries != nullptr); + assert(newNextEntries != nullptr); + + // Initialize the new buckets + for (uint32 i=0; i 0) { + + assert(mNextEntries != nullptr); + + // Copy the free nodes indices in the nextEntries array + std::memcpy(newNextEntries, mNextEntries, mNbAllocatedEntries * sizeof(uint32)); + } + + // Recompute the buckets (hash) with the new hash size + for (uint32 i=0; i 0) { + + // Release previously allocated memory + mAllocator.release(mBuckets, mHashSize * sizeof(uint32)); + mAllocator.release(mEntries, mNbAllocatedEntries * sizeof(V)); + mAllocator.release(mNextEntries, mNbAllocatedEntries * sizeof(uint32)); + } + + // Add the new entries to the free list + for (uint32 i=mNbAllocatedEntries; i < nbAllocatedEntries-1; i++) { + newNextEntries[i] = i + 1; + } + newNextEntries[nbAllocatedEntries - 1] = mFreeIndex; + mFreeIndex = mNbAllocatedEntries; + + mHashSize = capacity; + mNbAllocatedEntries = nbAllocatedEntries; + mBuckets = newBuckets; + mEntries = newEntries; + mNextEntries = newNextEntries; + + assert(mFreeIndex != INVALID_INDEX); } /// Return true if the set contains a given value bool contains(const V& value) const { - return findEntry(value) != -1; + return findEntry(value) != INVALID_INDEX; } /// Add a value into the set. /// Returns true if the item has been inserted and false otherwise. bool add(const V& value) { - if (mCapacity == 0) { - initialize(0); - } + uint32 bucket; // Compute the hash code of the value - size_t hashCode = Hash()(value); + const size_t hashCode = Hash()(value); - // Compute the corresponding bucket index - int bucket = hashCode % mCapacity; + if (mHashSize > 0) { - auto keyEqual = KeyEqual(); + // Compute the corresponding bucket index + bucket = hashCode & (mHashSize - 1); - // Check if the item is already in the set - for (int i = mBuckets[bucket]; i >= 0; i = mEntries[i].next) { + auto keyEqual = KeyEqual(); - // If there is already an item with the same value in the set - if (mEntries[i].hashCode == hashCode && keyEqual(*mEntries[i].value, value)) { + // Check if the item is already in the set + for (uint32 i = mBuckets[bucket]; i != INVALID_INDEX; i = mNextEntries[i]) { - return false; + // If there is already an item with the same value in the set + if (Hash()(mEntries[i]) == hashCode && keyEqual(mEntries[i], value)) { + + return false; + } } } size_t entryIndex; - // If there are free entries to use - if (mNbFreeEntries > 0) { - assert(mFreeIndex >= 0); - entryIndex = mFreeIndex; - mFreeIndex = mEntries[entryIndex].next; - mNbFreeEntries--; - } - else { + // If there are no more free entries to use + if (mFreeIndex == INVALID_INDEX) { - // If we need to allocator more entries - if (mNbUsedEntries == mCapacity) { + // Allocate more memory + reserve(mHashSize == 0 ? 16 : mHashSize * 2); - // Allocate more memory - reserve(mCapacity * 2); - - // Recompute the bucket index - bucket = hashCode % mCapacity; - } - - entryIndex = mNbUsedEntries; - mNbUsedEntries++; + // Recompute the bucket index + bucket = hashCode & (mHashSize - 1); } - assert(mEntries[entryIndex].value == nullptr); - mEntries[entryIndex].hashCode = hashCode; - mEntries[entryIndex].next = mBuckets[bucket]; - mEntries[entryIndex].value = static_cast(mAllocator.allocate(sizeof(V))); - assert(mEntries[entryIndex].value != nullptr); - new (mEntries[entryIndex].value) V(value); + assert(mNbEntries < mNbAllocatedEntries); + assert(mFreeIndex != INVALID_INDEX); + + // Get the next free entry + entryIndex = mFreeIndex; + mFreeIndex = mNextEntries[entryIndex]; + + mNbEntries++; + + mNextEntries[entryIndex] = mBuckets[bucket]; + new (mEntries + entryIndex) V(value); mBuckets[bucket] = entryIndex; return true; @@ -495,46 +431,46 @@ class Set { /// element after the one that has been removed Iterator remove(const V& value) { - if (mCapacity > 0) { + if (mHashSize > 0) { - size_t hashcode = Hash()(value); - auto keyEqual = KeyEqual(); - int bucket = hashcode % mCapacity; - int last = -1; - for (int i = mBuckets[bucket]; i >= 0; last = i, i = mEntries[i].next) { + const size_t hashcode = Hash()(value); + auto keyEqual = KeyEqual(); + const uint32 bucket = hashcode & (mHashSize - 1); + uint32 last = INVALID_INDEX; + for (uint32 i = mBuckets[bucket]; i != INVALID_INDEX; last = i, i = mNextEntries[i]) { - if (mEntries[i].hashCode == hashcode && keyEqual(*mEntries[i].value, value)) { + // If we have found the item + if (Hash()(mEntries[i]) == hashcode && keyEqual(mEntries[i], value)) { - if (last < 0 ) { - mBuckets[bucket] = mEntries[i].next; + if (last == INVALID_INDEX) { + mBuckets[bucket] = mNextEntries[i]; } else { - mEntries[last].next = mEntries[i].next; + mNextEntries[last] = mNextEntries[i]; } - // Release memory for the value if any - if (mEntries[i].value != nullptr) { - mEntries[i].value->~V(); - mAllocator.release(mEntries[i].value, sizeof(V)); - mEntries[i].value = nullptr; - } - assert(mEntries[i].value == nullptr); - mEntries[i].next = mFreeIndex; + uint32 nextEntryIndex = mNextEntries[i]; + uint32 nextBucketIndex = bucket; + + mEntries[i].~V(); + mNextEntries[i] = mFreeIndex; mFreeIndex = i; - mNbFreeEntries++; + mNbEntries--; - // Find the next valid entry to return an iterator - for (i += 1; i < mNbUsedEntries; i++) { - - // If the entry is not empty - if (mEntries[i].value != nullptr) { - - // We have found the next non empty entry - return Iterator(mEntries, mCapacity, mNbUsedEntries, i); + // Find the next entry to return an iterator + if (nextEntryIndex == INVALID_INDEX) { + nextEntryIndex = 0; + nextBucketIndex++; + while(nextBucketIndex < mHashSize && mBuckets[nextBucketIndex] == INVALID_INDEX) { + nextBucketIndex++; + } + if (nextBucketIndex < mHashSize) { + nextEntryIndex = mBuckets[nextBucketIndex]; } } - return end(); + // We have found the next non empty entry + return Iterator(this, nextBucketIndex, nextEntryIndex); } } } @@ -547,10 +483,8 @@ class Set { List list(listAllocator); - for (int i=0; i < mCapacity; i++) { - if (mEntries[i].value != nullptr) { - list.add(*(mEntries[i].value)); - } + for (auto it = begin(); it != end(); ++it) { + list.add(*it); } return list; @@ -559,50 +493,52 @@ class Set { /// Clear the set void clear(bool releaseMemory = false) { - if (mNbUsedEntries > 0) { + for (uint32 i=0; i~V(); - mAllocator.release(mEntries[i].value, sizeof(V)); - mEntries[i].value = nullptr; - } + uint32 entryIndex = mBuckets[i]; + while(entryIndex != INVALID_INDEX) { + + // Destroy the entry + mEntries[entryIndex].~V(); + + uint32 nextEntryIndex = mNextEntries[entryIndex]; + + // Add entry to the free list + mNextEntries[entryIndex] = mFreeIndex; + mFreeIndex = entryIndex; + + entryIndex = nextEntryIndex; } - mFreeIndex = -1; - mNbUsedEntries = 0; - mNbFreeEntries = 0; + mBuckets[i] = INVALID_INDEX; } - // If elements have been allocated - if (releaseMemory && mCapacity > 0) { + if (releaseMemory && mNbAllocatedEntries > 0) { - // Destroy the entries - for (int i=0; i < mCapacity; i++) { - mEntries[i].~Entry(); - } + // Release previously allocated memory + mAllocator.release(mBuckets, mHashSize * sizeof(uint32)); + mAllocator.release(mEntries, mNbAllocatedEntries * sizeof(V)); + mAllocator.release(mNextEntries, mNbAllocatedEntries * sizeof(uint32)); - mAllocator.release(mBuckets, mCapacity * sizeof(int)); - mAllocator.release(mEntries, mCapacity * sizeof(Entry)); - - mCapacity = 0; mBuckets = nullptr; mEntries = nullptr; + mNextEntries = nullptr; + + mNbAllocatedEntries = 0; + mHashSize = 0; } - assert(size() == 0); + mNbEntries = 0; } /// Return the number of elements in the set - int size() const { - return mNbUsedEntries - mNbFreeEntries; + uint32 size() const { + return mNbEntries; } /// Return the capacity of the set - int capacity() const { - return mCapacity; + uint32 capacity() const { + return mHashSize; } /// Try to find an item of the set given a key. @@ -610,30 +546,28 @@ class Set { /// an iterator pointing to the end if not found Iterator find(const V& value) const { - int bucket; - int entry = -1; + uint32 bucket; + uint32 entry = INVALID_INDEX; - if (mCapacity > 0) { + if (mHashSize > 0) { - size_t hashCode = Hash()(value); - bucket = hashCode % mCapacity; - auto keyEqual = KeyEqual(); + const size_t hashCode = Hash()(value); + bucket = hashCode & (mHashSize - 1); + auto keyEqual = KeyEqual(); - for (int i = mBuckets[bucket]; i >= 0; i = mEntries[i].next) { - if (mEntries[i].hashCode == hashCode && keyEqual(*(mEntries[i].value), value)) { + for (uint32 i = mBuckets[bucket]; i != INVALID_INDEX; i = mNextEntries[i]) { + if (Hash()(mEntries[i]) == hashCode && keyEqual(mEntries[i], value)) { entry = i; break; } } } - if (entry == -1) { + if (entry == INVALID_INDEX) { return end(); } - assert(mEntries[entry].value != nullptr); - - return Iterator(mEntries, mCapacity, mNbUsedEntries, entry); + return Iterator(this, bucket, entry); } /// Overloaded equality operator @@ -665,34 +599,35 @@ class Set { // Clear the set clear(true); - if (set.mCapacity > 0) { + mNbAllocatedEntries = set.mNbAllocatedEntries; + mNbEntries = set.mNbEntries; + mHashSize = set.mHashSize; + mFreeIndex = set.mFreeIndex; - // Compute the next larger prime size - mCapacity = getPrimeSize(set.mCapacity); + // Allocate memory for the buckets + mBuckets = static_cast(mAllocator.allocate(mHashSize * sizeof(uint32))); - // Allocate memory for the buckets - mBuckets = static_cast(mAllocator.allocate(mCapacity * sizeof(int))); + // Allocate memory for the entries + mEntries = static_cast(mAllocator.allocate(mNbAllocatedEntries * sizeof(V))); + mNextEntries = static_cast(mAllocator.allocate(mNbAllocatedEntries * sizeof(uint32))); - // Allocate memory for the entries - mEntries = static_cast(mAllocator.allocate(mCapacity * sizeof(Entry))); + // Copy the buckets array + std::memcpy(mBuckets, set.mBuckets, mHashSize * sizeof(uint32)); - // Copy the buckets - std::uninitialized_copy(set.mBuckets, set.mBuckets + mCapacity, mBuckets); + // Copy the next entries indices + std::memcpy(mNextEntries, set.mNextEntries, mNbAllocatedEntries * sizeof(uint32)); - // Copy the entries - for (int i=0; i < mCapacity; i++) { + // Copy the entries + for (uint32 i=0; i(mAllocator.allocate(sizeof(V))); - new (mEntries[i].value) V(*(set.mEntries[i].value)); - } + // Copy the entry to the new location and destroy the previous one + new (mEntries + entryIndex) V(set.mEntries[entryIndex]); + + entryIndex = mNextEntries[entryIndex]; } - - mNbUsedEntries = set.mNbUsedEntries; - mNbFreeEntries = set.mNbFreeEntries; - mFreeIndex = set.mFreeIndex; } } @@ -702,7 +637,7 @@ class Set { /// Return a begin iterator Iterator begin() const { - // If the map is empty + // If the set is empty if (size() == 0) { // Return an iterator to the end @@ -710,33 +645,29 @@ class Set { } // Find the first used entry - int entry; - for (entry=0; entry < mNbUsedEntries; entry++) { - if (mEntries[entry].value != nullptr) { - return Iterator(mEntries, mCapacity, mNbUsedEntries, entry); - } + uint32 bucketIndex = 0; + while (mBuckets[bucketIndex] == INVALID_INDEX) { + + bucketIndex++; } - assert(false); - return end(); + assert(bucketIndex < mHashSize); + assert(mBuckets[bucketIndex] != INVALID_INDEX); + + return Iterator(this, bucketIndex, mBuckets[bucketIndex]); } /// Return a end iterator Iterator end() const { - return Iterator(mEntries, mCapacity, mNbUsedEntries, mCapacity); + + return Iterator(this, mHashSize, 0); } + + // ---------- Friendship ---------- // + + friend class Iterator; }; -template -const int Set::PRIMES[NB_PRIMES] = {3, 7, 11, 17, 23, 29, 37, 47, 59, 71, 89, 107, 131, 163, 197, 239, 293, 353, 431, 521, 631, 761, 919, - 1103, 1327, 1597, 1931, 2333, 2801, 3371, 4049, 4861, 5839, 7013, 8419, 10103, 12143, 14591, - 17519, 21023, 25229, 30293, 36353, 43627, 52361, 62851, 75431, 90523, 108631, 130363, 156437, - 187751, 225307, 270371, 324449, 389357, 467237, 560689, 672827, 807403, 968897, 1162687, 1395263, - 1674319, 2009191, 2411033, 2893249, 3471899, 4166287, 4999559}; - -template -int Set::LARGEST_PRIME = -1; - } #endif diff --git a/include/reactphysics3d/engine/PhysicsCommon.h b/include/reactphysics3d/engine/PhysicsCommon.h index 939c8b39..f2e827c8 100644 --- a/include/reactphysics3d/engine/PhysicsCommon.h +++ b/include/reactphysics3d/engine/PhysicsCommon.h @@ -97,6 +97,36 @@ class PhysicsCommon { /// Destroy and release everything that has been allocated void release(); + /// Delete an instance of PhysicsWorld + void deletePhysicsWorld(PhysicsWorld* world); + + /// Delete a sphere collision shape + void deleteSphereShape(SphereShape* sphereShape); + + /// Delete a box collision shape + void deleteBoxShape(BoxShape* boxShape); + + /// Delete a capsule collision shape + void deleteCapsuleShape(CapsuleShape* capsuleShape); + + /// Delete a convex mesh shape + void deleteConvexMeshShape(ConvexMeshShape* convexMeshShape); + + /// Delete a height-field shape + void deleteHeightFieldShape(HeightFieldShape* heightFieldShape); + + /// Delete a concave mesh shape + void deleteConcaveMeshShape(ConcaveMeshShape* concaveMeshShape); + + /// Delete a polyhedron mesh + void deletePolyhedronMesh(PolyhedronMesh* polyhedronMesh); + + /// Delete a triangle mesh + void deleteTriangleMesh(TriangleMesh* triangleMesh); + + /// Delete a default logger + void deleteDefaultLogger(DefaultLogger* logger); + // If profiling is enabled #ifdef IS_RP3D_PROFILING_ENABLED @@ -106,6 +136,9 @@ class PhysicsCommon { /// Destroy a profiler void destroyProfiler(Profiler* profiler); + /// Delete a profiler + void deleteProfiler(Profiler* profiler); + #endif public : @@ -186,7 +219,6 @@ class PhysicsCommon { /// Set the logger static void setLogger(Logger* logger); - }; // Return the current logger diff --git a/include/reactphysics3d/mathematics/mathematics_functions.h b/include/reactphysics3d/mathematics/mathematics_functions.h index cd98140e..cf0f6880 100755 --- a/include/reactphysics3d/mathematics/mathematics_functions.h +++ b/include/reactphysics3d/mathematics/mathematics_functions.h @@ -126,8 +126,23 @@ Vector3 projectPointOntoPlane(const Vector3& point, const Vector3& planeNormal, /// Return the distance between a point and a plane (the plane normal must be normalized) decimal computePointToPlaneDistance(const Vector3& point, const Vector3& planeNormal, const Vector3& planePoint); -/// Return true if the given number is prime -bool isPrimeNumber(int number); +/// Return true if a number is a power of two +RP3D_FORCE_INLINE bool isPowerOfTwo(uint32 number) { + return number != 0 && !(number & (number -1)); +} + +/// Return the next power of two larger than the number in parameter +RP3D_FORCE_INLINE uint32 nextPowerOfTwo32Bits(uint32 number) { + number--; + number |= number >> 1; + number |= number >> 2; + number |= number >> 4; + number |= number >> 8; + number |= number >> 16; + number++; + number += (number == 0); + return number; +} /// Return an unique integer from two integer numbers (pairing function) /// Here we assume that the two parameter numbers are sorted such that diff --git a/src/engine/PhysicsCommon.cpp b/src/engine/PhysicsCommon.cpp index 3147c702..08ef4248 100644 --- a/src/engine/PhysicsCommon.cpp +++ b/src/engine/PhysicsCommon.cpp @@ -58,53 +58,63 @@ void PhysicsCommon::release() { // Destroy the physics worlds for (auto it = mPhysicsWorlds.begin(); it != mPhysicsWorlds.end(); ++it) { - destroyPhysicsWorld(*it); + deletePhysicsWorld(*it); } + mPhysicsWorlds.clear(); // Destroy the sphere shapes for (auto it = mSphereShapes.begin(); it != mSphereShapes.end(); ++it) { - destroySphereShape(*it); + deleteSphereShape(*it); } + mSphereShapes.clear(); // Destroy the box shapes for (auto it = mBoxShapes.begin(); it != mBoxShapes.end(); ++it) { - destroyBoxShape(*it); + deleteBoxShape(*it); } + mBoxShapes.clear(); // Destroy the capsule shapes for (auto it = mCapsuleShapes.begin(); it != mCapsuleShapes.end(); ++it) { - destroyCapsuleShape(*it); + deleteCapsuleShape(*it); } + mCapsuleShapes.clear(); // Destroy the convex mesh shapes for (auto it = mConvexMeshShapes.begin(); it != mConvexMeshShapes.end(); ++it) { - destroyConvexMeshShape(*it); + deleteConvexMeshShape(*it); } + mConvexMeshShapes.clear(); // Destroy the heigh-field shapes for (auto it = mHeightFieldShapes.begin(); it != mHeightFieldShapes.end(); ++it) { - destroyHeightFieldShape(*it); + deleteHeightFieldShape(*it); } + mHeightFieldShapes.clear(); // Destroy the concave mesh shapes for (auto it = mConcaveMeshShapes.begin(); it != mConcaveMeshShapes.end(); ++it) { - destroyConcaveMeshShape(*it); + deleteConcaveMeshShape(*it); } + mConcaveMeshShapes.clear(); // Destroy the polyhedron mesh for (auto it = mPolyhedronMeshes.begin(); it != mPolyhedronMeshes.end(); ++it) { - destroyPolyhedronMesh(*it); + deletePolyhedronMesh(*it); } + mPolyhedronMeshes.clear(); // Destroy the triangle mesh for (auto it = mTriangleMeshes.begin(); it != mTriangleMeshes.end(); ++it) { - destroyTriangleMesh(*it); + deleteTriangleMesh(*it); } + mTriangleMeshes.clear(); // Destroy the default loggers for (auto it = mDefaultLoggers.begin(); it != mDefaultLoggers.end(); ++it) { - destroyDefaultLogger(*it); + deleteDefaultLogger(*it); } + mDefaultLoggers.clear(); // If profiling is enabled #ifdef IS_RP3D_PROFILING_ENABLED @@ -112,8 +122,9 @@ void PhysicsCommon::release() { // Destroy the profilers for (auto it = mProfilers.begin(); it != mProfilers.end(); ++it) { - destroyProfiler(*it); + deleteProfiler(*it); } + mProfilers.clear(); #endif @@ -151,13 +162,22 @@ PhysicsWorld* PhysicsCommon::createPhysicsWorld(const PhysicsWorld::WorldSetting */ void PhysicsCommon::destroyPhysicsWorld(PhysicsWorld* world) { + deletePhysicsWorld(world); + + mPhysicsWorlds.remove(world); +} + +// Delete an instance of PhysicsWorld +/** + * @param world A pointer to the physics world to destroy + */ +void PhysicsCommon::deletePhysicsWorld(PhysicsWorld* world) { + // Call the destructor of the world world->~PhysicsWorld(); // Release allocated memory mMemoryManager.release(MemoryManager::AllocationType::Heap, world, sizeof(PhysicsWorld)); - - mPhysicsWorlds.remove(world); } // Create and return a sphere collision shape @@ -185,6 +205,17 @@ SphereShape* PhysicsCommon::createSphereShape(const decimal radius) { */ void PhysicsCommon::destroySphereShape(SphereShape* sphereShape) { + deleteSphereShape(sphereShape); + + mSphereShapes.remove(sphereShape); +} + +// Delete a sphere collision shape +/** + * @param sphereShape A pointer to the sphere collision shape to destroy + */ +void PhysicsCommon::deleteSphereShape(SphereShape* sphereShape) { + // If the shape is still part of some colliders if (sphereShape->mColliders.size() > 0) { @@ -197,8 +228,6 @@ void PhysicsCommon::destroySphereShape(SphereShape* sphereShape) { // Release allocated memory mMemoryManager.release(MemoryManager::AllocationType::Pool, sphereShape, sizeof(SphereShape)); - - mSphereShapes.remove(sphereShape); } // Create and return a box collision shape @@ -226,6 +255,17 @@ BoxShape* PhysicsCommon::createBoxShape(const Vector3& halfExtents) { */ void PhysicsCommon::destroyBoxShape(BoxShape* boxShape) { + deleteBoxShape(boxShape); + + mBoxShapes.remove(boxShape); +} + +// Delete a box collision shape +/** + * @param boxShape A pointer to the box shape to destroy + */ +void PhysicsCommon::deleteBoxShape(BoxShape* boxShape) { + // If the shape is still part of some colliders if (boxShape->mColliders.size() > 0) { @@ -238,8 +278,6 @@ void PhysicsCommon::destroyBoxShape(BoxShape* boxShape) { // Release allocated memory mMemoryManager.release(MemoryManager::AllocationType::Pool, boxShape, sizeof(BoxShape)); - - mBoxShapes.remove(boxShape); } // Create and return a capsule shape @@ -275,6 +313,17 @@ CapsuleShape* PhysicsCommon::createCapsuleShape(decimal radius, decimal height) */ void PhysicsCommon::destroyCapsuleShape(CapsuleShape* capsuleShape) { + deleteCapsuleShape(capsuleShape); + + mCapsuleShapes.remove(capsuleShape); +} + +// Delete a capsule collision shape +/** + * @param capsuleShape A pointer to the capsule shape to destroy + */ +void PhysicsCommon::deleteCapsuleShape(CapsuleShape* capsuleShape) { + // If the shape is still part of some colliders if (capsuleShape->mColliders.size() > 0) { @@ -287,8 +336,6 @@ void PhysicsCommon::destroyCapsuleShape(CapsuleShape* capsuleShape) { // Release allocated memory mMemoryManager.release(MemoryManager::AllocationType::Pool, capsuleShape, sizeof(CapsuleShape)); - - mCapsuleShapes.remove(capsuleShape); } // Create and return a convex mesh shape @@ -312,6 +359,17 @@ ConvexMeshShape* PhysicsCommon::createConvexMeshShape(PolyhedronMesh* polyhedron */ void PhysicsCommon::destroyConvexMeshShape(ConvexMeshShape* convexMeshShape) { + deleteConvexMeshShape(convexMeshShape); + + mConvexMeshShapes.remove(convexMeshShape); +} + +// Delete a convex mesh shape +/** + * @param convexMeshShape A pointer to the convex mesh shape to destroy + */ +void PhysicsCommon::deleteConvexMeshShape(ConvexMeshShape* convexMeshShape) { + // If the shape is still part of some colliders if (convexMeshShape->mColliders.size() > 0) { @@ -324,8 +382,6 @@ void PhysicsCommon::destroyConvexMeshShape(ConvexMeshShape* convexMeshShape) { // Release allocated memory mMemoryManager.release(MemoryManager::AllocationType::Pool, convexMeshShape, sizeof(ConvexMeshShape)); - - mConvexMeshShapes.remove(convexMeshShape); } // Create and return a height-field shape @@ -358,6 +414,17 @@ HeightFieldShape* PhysicsCommon::createHeightFieldShape(int nbGridColumns, int n */ void PhysicsCommon::destroyHeightFieldShape(HeightFieldShape* heightFieldShape) { + deleteHeightFieldShape(heightFieldShape); + + mHeightFieldShapes.remove(heightFieldShape); +} + +// Delete a height-field shape +/** + * @param heightFieldShape A pointer to the height field shape to destroy + */ +void PhysicsCommon::deleteHeightFieldShape(HeightFieldShape* heightFieldShape) { + // If the shape is still part of some colliders if (heightFieldShape->mColliders.size() > 0) { @@ -370,8 +437,6 @@ void PhysicsCommon::destroyHeightFieldShape(HeightFieldShape* heightFieldShape) // Release allocated memory mMemoryManager.release(MemoryManager::AllocationType::Pool, heightFieldShape, sizeof(HeightFieldShape)); - - mHeightFieldShapes.remove(heightFieldShape); } // Create and return a concave mesh shape @@ -395,6 +460,17 @@ ConcaveMeshShape* PhysicsCommon::createConcaveMeshShape(TriangleMesh* triangleMe */ void PhysicsCommon::destroyConcaveMeshShape(ConcaveMeshShape* concaveMeshShape) { + deleteConcaveMeshShape(concaveMeshShape); + + mConcaveMeshShapes.remove(concaveMeshShape); +} + +// Delete a concave mesh shape +/** + * @param concaveMeshShape A pointer to the concave mesh shape to destroy + */ +void PhysicsCommon::deleteConcaveMeshShape(ConcaveMeshShape* concaveMeshShape) { + // If the shape is still part of some colliders if (concaveMeshShape->mColliders.size() > 0) { @@ -407,8 +483,6 @@ void PhysicsCommon::destroyConcaveMeshShape(ConcaveMeshShape* concaveMeshShape) // Release allocated memory mMemoryManager.release(MemoryManager::AllocationType::Pool, concaveMeshShape, sizeof(ConcaveMeshShape)); - - mConcaveMeshShapes.remove(concaveMeshShape); } // Create a polyhedron mesh @@ -431,13 +505,22 @@ PolyhedronMesh* PhysicsCommon::createPolyhedronMesh(PolygonVertexArray* polygonV */ void PhysicsCommon::destroyPolyhedronMesh(PolyhedronMesh* polyhedronMesh) { + deletePolyhedronMesh(polyhedronMesh); + + mPolyhedronMeshes.remove(polyhedronMesh); +} + +// Delete a polyhedron mesh +/** + * @param polyhedronMesh A pointer to the polyhedron mesh to destroy + */ +void PhysicsCommon::deletePolyhedronMesh(PolyhedronMesh* polyhedronMesh) { + // Call the destructor of the shape polyhedronMesh->~PolyhedronMesh(); // Release allocated memory mMemoryManager.release(MemoryManager::AllocationType::Pool, polyhedronMesh, sizeof(PolyhedronMesh)); - - mPolyhedronMeshes.remove(polyhedronMesh); } // Create a triangle mesh @@ -459,13 +542,22 @@ TriangleMesh* PhysicsCommon::createTriangleMesh() { */ void PhysicsCommon::destroyTriangleMesh(TriangleMesh* triangleMesh) { + deleteTriangleMesh(triangleMesh); + + mTriangleMeshes.remove(triangleMesh); +} + +// Delete a triangle mesh +/** + * @param A pointer to the triangle mesh to destroy + */ +void PhysicsCommon::deleteTriangleMesh(TriangleMesh* triangleMesh) { + // Call the destructor of the shape triangleMesh->~TriangleMesh(); // Release allocated memory mMemoryManager.release(MemoryManager::AllocationType::Pool, triangleMesh, sizeof(TriangleMesh)); - - mTriangleMeshes.remove(triangleMesh); } // Create and return a new logger @@ -487,13 +579,22 @@ DefaultLogger* PhysicsCommon::createDefaultLogger() { */ void PhysicsCommon::destroyDefaultLogger(DefaultLogger* logger) { + deleteDefaultLogger(logger); + + mDefaultLoggers.remove(logger); +} + +// Delete a logger +/** + * @param A pointer to the default logger to destroy + */ +void PhysicsCommon::deleteDefaultLogger(DefaultLogger* logger) { + // Call the destructor of the logger logger->~DefaultLogger(); // Release allocated memory mMemoryManager.release(MemoryManager::AllocationType::Pool, logger, sizeof(DefaultLogger)); - - mDefaultLoggers.remove(logger); } // If profiling is enabled @@ -514,13 +615,18 @@ Profiler* PhysicsCommon::createProfiler() { // Destroy a profiler void PhysicsCommon::destroyProfiler(Profiler* profiler) { + deleteProfiler(profiler); + + mProfilers.remove(profiler); +} + +// Delete a profiler +void PhysicsCommon::deleteProfiler(Profiler* profiler) { + // Call the destructor of the profiler profiler->~Profiler(); // Release allocated memory mMemoryManager.release(MemoryManager::AllocationType::Pool, profiler, sizeof(Profiler)); - - mProfilers.remove(profiler); } - #endif diff --git a/src/mathematics/mathematics_functions.cpp b/src/mathematics/mathematics_functions.cpp index 5438c980..daf021b0 100755 --- a/src/mathematics/mathematics_functions.cpp +++ b/src/mathematics/mathematics_functions.cpp @@ -379,27 +379,3 @@ Vector3 reactphysics3d::projectPointOntoPlane(const Vector3& point, const Vector decimal reactphysics3d::computePointToPlaneDistance(const Vector3& point, const Vector3& planeNormal, const Vector3& planePoint) { return planeNormal.dot(point - planePoint); } - -// Return true if the given number is prime -bool reactphysics3d::isPrimeNumber(int number) { - - // If it's a odd number - if ((number & 1) != 0) { - - int limit = static_cast(std::sqrt(number)); - - for (int divisor = 3; divisor <= limit; divisor += 2) { - - // If we have found a divisor - if ((number % divisor) == 0) { - - // It is not a prime number - return false; - } - } - - return true; - } - - return number == 2; -} diff --git a/test/main.cpp b/test/main.cpp index 6a965039..3d2ba550 100644 --- a/test/main.cpp +++ b/test/main.cpp @@ -53,9 +53,9 @@ int main() { // ---------- Containers tests ---------- // + testSuite.addTest(new TestSet("Set")); testSuite.addTest(new TestList("List")); testSuite.addTest(new TestMap("Map")); - testSuite.addTest(new TestSet("Set")); testSuite.addTest(new TestDeque("Deque")); testSuite.addTest(new TestStack("Stack")); diff --git a/test/tests/containers/TestList.h b/test/tests/containers/TestList.h index 5190e341..10c500aa 100644 --- a/test/tests/containers/TestList.h +++ b/test/tests/containers/TestList.h @@ -94,7 +94,7 @@ class TestList : public Test { rp3d_test(list4.size() == 0); List list5(list3); - rp3d_test(list5.capacity() == list3.size()); + rp3d_test(list5.capacity() == list3.capacity()); rp3d_test(list5.size() == list3.size()); for (uint i=0; i #include +#include /// Reactphysics3D namespace namespace reactphysics3d { @@ -267,6 +268,33 @@ class TestMathematicsFunctions : public Test { rp3d_test(approxEqual(clipPolygonVertices[3].y, 4, 0.000001)); rp3d_test(approxEqual(clipPolygonVertices[3].z, 0, 0.000001)); + // Test isPowerOfTwo() + rp3d_test(!isPowerOfTwo(0)); + rp3d_test(!isPowerOfTwo(3)); + rp3d_test(!isPowerOfTwo(144)); + rp3d_test(!isPowerOfTwo(13)); + rp3d_test(!isPowerOfTwo(18)); + rp3d_test(!isPowerOfTwo(1000)); + + rp3d_test(isPowerOfTwo(1)); + rp3d_test(isPowerOfTwo(2)); + rp3d_test(isPowerOfTwo(4)); + rp3d_test(isPowerOfTwo(8)); + rp3d_test(isPowerOfTwo(256)); + rp3d_test(isPowerOfTwo(1024)); + rp3d_test(isPowerOfTwo(2048)); + + // Test nextPowerOfTwo32Bits() + rp3d_test(nextPowerOfTwo32Bits(0) == 1); + rp3d_test(nextPowerOfTwo32Bits(1) == 1); + rp3d_test(nextPowerOfTwo32Bits(2) == 2); + rp3d_test(nextPowerOfTwo32Bits(3) == 4); + rp3d_test(nextPowerOfTwo32Bits(5) == 8); + rp3d_test(nextPowerOfTwo32Bits(6) == 8); + rp3d_test(nextPowerOfTwo32Bits(7) == 8); + rp3d_test(nextPowerOfTwo32Bits(1000) == 1024); + rp3d_test(nextPowerOfTwo32Bits(129) == 256); + rp3d_test(nextPowerOfTwo32Bits(260) == 512); } };