Refactoring and optimization of List and Set containers

This commit is contained in:
Daniel Chappuis 2020-08-28 23:10:19 +02:00
parent a1e0e0aa94
commit de6630a03d
9 changed files with 844 additions and 857 deletions

File diff suppressed because it is too large Load Diff

View File

@ -47,197 +47,59 @@ class Set {
private: 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 -------------------- // // -------------------- Constants -------------------- //
/// Number of prime numbers in array /// Default load factor
static constexpr int NB_PRIMES = 70; static constexpr float DEFAULT_LOAD_FACTOR = 0.75;
/// Array of prime numbers for the size of the set /// Invalid index in the array
static const int PRIMES[NB_PRIMES]; static constexpr uint32 INVALID_INDEX = 0xffffffff;
/// Largest prime number
static int LARGEST_PRIME;
// -------------------- Attributes -------------------- // // -------------------- Attributes -------------------- //
/// Current number of used entries in the set /// Total number of allocated entries
int mNbUsedEntries; uint32 mNbAllocatedEntries;
/// Number of free entries among the used ones /// Number of items in the set
int mNbFreeEntries; uint32 mNbEntries;
/// Current capacity of the set /// Number of buckets and size of the hash table (nbEntries = loadFactor * mHashSize)
int mCapacity; uint32 mHashSize ;
/// Array with all the buckets /// Array with all the buckets
int* mBuckets; uint32* mBuckets;
/// Array with all the entries /// Array with all the entries (nbEntries = loadFactor * mHashSize)
Entry* mEntries; V* mEntries;
/// For each entry, index of the next entry at the same bucket
uint32* mNextEntries;
/// Memory allocator /// Memory allocator
MemoryAllocator& mAllocator; MemoryAllocator& mAllocator;
/// Index to the fist free entry /// Index to the fist free entry
int mFreeIndex; uint32 mFreeIndex;
// -------------------- Methods -------------------- // // -------------------- 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<int*>(mAllocator.allocate(mCapacity * sizeof(int)));
// Allocate memory for the entries
mEntries = static_cast<Entry*>(mAllocator.allocate(mCapacity * sizeof(Entry)));
// Initialize the buckets and entries
for (int i=0; i<mCapacity; i++) {
mBuckets[i] = -1;
// Construct the entry
new (&mEntries[i]) Entry();
}
mNbUsedEntries = 0;
mNbFreeEntries = 0;
mFreeIndex = -1;
}
/// Expand the capacity of the set
void expand(int newCapacity) {
assert(newCapacity > mCapacity);
assert(isPrimeNumber(newCapacity));
// Allocate memory for the buckets
int* newBuckets = static_cast<int*>(mAllocator.allocate(newCapacity * sizeof(int)));
// Allocate memory for the entries
Entry* newEntries = static_cast<Entry*>(mAllocator.allocate(newCapacity * sizeof(Entry)));
// Initialize the new buckets
for (int i=0; i<newCapacity; i++) {
newBuckets[i] = -1;
}
if (mNbUsedEntries > 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<mNbUsedEntries; i++) {
mEntries[i].~Entry();
}
}
// Construct the new entries
for (int i=mNbUsedEntries; i<newCapacity; i++) {
// Construct the entry
new (static_cast<void*>(&newEntries[i])) Entry();
}
// For each used entry
for (int i=0; i<mNbUsedEntries; i++) {
// If the entry is not free
if (newEntries[i].value != nullptr) {
// Get the corresponding bucket
int bucket = newEntries[i].hashCode % newCapacity;
newEntries[i].next = newBuckets[bucket];
newBuckets[bucket] = i;
}
}
// Release previously allocated memory
mAllocator.release(mBuckets, mCapacity * sizeof(int));
mAllocator.release(mEntries, mCapacity * sizeof(Entry));
mCapacity = newCapacity;
mBuckets = newBuckets;
mEntries = newEntries;
}
/// Return the index of the entry with a given value or -1 if there is no entry with this value /// Return the index of the entry with a given value or -1 if there is no entry with this value
int findEntry(const V& value) const { uint32 findEntry(const V& value) const {
if (mCapacity > 0) { if (mHashSize > 0) {
size_t hashCode = Hash()(value); const size_t hashCode = Hash()(value);
int bucket = hashCode % mCapacity; const uint32 bucket = hashCode & (mHashSize - 1);
auto keyEqual = KeyEqual(); auto keyEqual = KeyEqual();
for (int i = mBuckets[bucket]; i >= 0; i = mEntries[i].next) { for (uint32 i = mBuckets[bucket]; i != INVALID_INDEX; i = mNextEntries[i]) {
if (mEntries[i].hashCode == hashCode && keyEqual(*mEntries[i].value, value)) { if (Hash()(mEntries[i]) == hashCode && keyEqual(mEntries[i], value)) {
return i; return i;
} }
} }
} }
return -1; return INVALID_INDEX;
}
/// 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<int>::max(); i+=2) {
if (isPrimeNumber(i)) {
return i;
}
}
return number;
} }
public: public:
@ -250,36 +112,36 @@ class Set {
private: private:
/// Array of entries /// Pointer to the set
const Entry* mEntries; const Set* mSet;
/// Capacity of the map /// Index of the current bucket
int mCapacity; uint32 mCurrentBucketIndex;
/// Number of used entries in the map
int mNbUsedEntries;
/// Index of the current entry /// Index of the current entry
int mCurrentEntry; uint32 mCurrentEntryIndex;
/// Advance the iterator /// Advance the iterator
void advance() { void advance() {
// If we are trying to move past the end assert(mCurrentBucketIndex < mSet->mHashSize);
assert(mCurrentEntry < mNbUsedEntries); assert(mCurrentEntryIndex < mSet->mNbAllocatedEntries);
for (mCurrentEntry += 1; mCurrentEntry < mNbUsedEntries; mCurrentEntry++) { // Try the next entry
if (mSet->mNextEntries[mCurrentEntryIndex] != INVALID_INDEX) {
// If the entry is not empty mCurrentEntryIndex = mSet->mNextEntries[mCurrentEntryIndex];
if (mEntries[mCurrentEntry].value != nullptr) { return;
// We have found the next non empty entry
return;
}
} }
// We have not find a non empty entry, we return an iterator to the end // Try to move to the next bucket
mCurrentEntry = mCapacity; mCurrentEntryIndex = 0;
mCurrentBucketIndex++;
while(mCurrentBucketIndex < mSet->mHashSize && mSet->mBuckets[mCurrentBucketIndex] == INVALID_INDEX) {
mCurrentBucketIndex++;
}
if (mCurrentBucketIndex < mSet->mHashSize) {
mCurrentEntryIndex = mSet->mBuckets[mCurrentBucketIndex];
}
} }
public: public:
@ -295,29 +157,29 @@ class Set {
Iterator() = default; Iterator() = default;
/// Constructor /// Constructor
Iterator(const Entry* entries, int capacity, int nbUsedEntries, int currentEntry) Iterator(const Set* set, uint32 bucketIndex, uint32 entryIndex)
:mEntries(entries), mCapacity(capacity), mNbUsedEntries(nbUsedEntries), mCurrentEntry(currentEntry) { :mSet(set), mCurrentBucketIndex(bucketIndex), mCurrentEntryIndex(entryIndex) {
} }
/// Copy constructor /// Copy constructor
Iterator(const Iterator& it) 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 /// Deferencable
reference operator*() const { reference operator*() const {
assert(mCurrentEntry >= 0 && mCurrentEntry < mNbUsedEntries); assert(mCurrentEntryIndex < mSet->mNbAllocatedEntries);
assert(mEntries[mCurrentEntry].value != nullptr); assert(mCurrentEntryIndex != INVALID_INDEX);
return *(mEntries[mCurrentEntry].value); return mSet->mEntries[mCurrentEntryIndex];
} }
/// Deferencable /// Deferencable
pointer operator->() const { pointer operator->() const {
assert(mCurrentEntry >= 0 && mCurrentEntry < mNbUsedEntries); assert(mCurrentEntryIndex < mSet->mNbAllocatedEntries);
assert(mEntries[mCurrentEntry].value != nullptr); assert(mCurrentEntryIndex != INVALID_INDEX);
return mEntries[mCurrentEntry].value; return &(mSet->mEntries[mCurrentEntryIndex]);
} }
/// Post increment (it++) /// Post increment (it++)
@ -335,61 +197,71 @@ class Set {
/// Equality operator (it == end()) /// Equality operator (it == end())
bool operator==(const Iterator& iterator) const { 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()) /// Inequality operator (it != end())
bool operator!=(const Iterator& iterator) const { bool operator!=(const Iterator& iterator) const {
return !(*this == iterator); 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 -------------------- // // -------------------- Methods -------------------- //
/// Constructor /// Constructor
Set(MemoryAllocator& allocator, size_t capacity = 0) Set(MemoryAllocator& allocator, uint32 capacity = 0)
: mNbUsedEntries(0), mNbFreeEntries(0), mCapacity(0), mBuckets(nullptr), : mNbAllocatedEntries(0), mNbEntries(0), mHashSize(0), mBuckets(nullptr),
mEntries(nullptr), mAllocator(allocator), mFreeIndex(-1) { mEntries(nullptr), mNextEntries(nullptr), mAllocator(allocator), mFreeIndex(INVALID_INDEX) {
// 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);
}
if (capacity > 0) { if (capacity > 0) {
initialize(capacity); reserve(capacity);
} }
} }
/// Copy constructor /// Copy constructor
Set(const Set<V, Hash, KeyEqual>& set) Set(const Set<V, Hash, KeyEqual>& set)
:mNbUsedEntries(set.mNbUsedEntries), mNbFreeEntries(set.mNbFreeEntries), mCapacity(set.mCapacity), :mNbAllocatedEntries(set.mNbAllocatedEntries), mNbEntries(set.mNbEntries), mHashSize(set.mHashSize),
mBuckets(nullptr), mEntries(nullptr), mAllocator(set.mAllocator), mFreeIndex(set.mFreeIndex) { mBuckets(nullptr), mEntries(nullptr), mNextEntries(nullptr), mAllocator(set.mAllocator), mFreeIndex(set.mFreeIndex) {
if (mCapacity > 0) { // Allocate memory for the buckets
mBuckets = static_cast<uint32*>(mAllocator.allocate(mHashSize * sizeof(uint32)));
// Allocate memory for the buckets // Allocate memory for the entries
mBuckets = static_cast<int*>(mAllocator.allocate(mCapacity * sizeof(int))); mEntries = static_cast<V*>(mAllocator.allocate(mNbAllocatedEntries * sizeof(V)));
mNextEntries = static_cast<uint32*>(mAllocator.allocate(mNbAllocatedEntries * sizeof(uint32)));
// Allocate memory for the entries // Copy the buckets array
mEntries = static_cast<Entry*>(mAllocator.allocate(mCapacity * sizeof(Entry))); std::memcpy(mBuckets, set.mBuckets, mHashSize * sizeof(uint32));
// Copy the buckets // Copy the next entries indices
std::uninitialized_copy(set.mBuckets, set.mBuckets + mCapacity, mBuckets); std::memcpy(mNextEntries, set.mNextEntries, mNbAllocatedEntries * sizeof(uint32));
// Copy the entries // Copy the entries
for (int i=0; i < mCapacity; i++) { for (uint32 i=0; i<mHashSize; i++) {
new (&mEntries[i]) Entry(set.mEntries[i].hashCode, set.mEntries[i].next); uint32 entryIndex = mBuckets[i];
while(entryIndex != INVALID_INDEX) {
if (set.mEntries[i].value != nullptr) { // Copy the entry to the new location and destroy the previous one
mEntries[i].value = static_cast<V*>(mAllocator.allocate(sizeof(V))); new (mEntries + entryIndex) V(set.mEntries[entryIndex]);
new (mEntries[i].value) V(*(set.mEntries[i].value));
} entryIndex = mNextEntries[entryIndex];
} }
} }
} }
@ -401,82 +273,146 @@ class Set {
} }
/// Allocate memory for a given number of elements /// 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) { if (capacity < 16) capacity = 16;
capacity = LARGEST_PRIME;
}
else {
capacity = getPrimeSize(capacity);
}
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<uint32*>(mAllocator.allocate(capacity * sizeof(uint32)));
// Allocate memory for the entries
const uint32 nbAllocatedEntries = capacity * DEFAULT_LOAD_FACTOR;
assert(nbAllocatedEntries > 0);
V* newEntries = static_cast<V*>(mAllocator.allocate(nbAllocatedEntries * sizeof(V)));
uint32* newNextEntries = static_cast<uint32*>(mAllocator.allocate(nbAllocatedEntries * sizeof(uint32)));
assert(newEntries != nullptr);
assert(newNextEntries != nullptr);
// Initialize the new buckets
for (uint32 i=0; i<capacity; i++) {
newBuckets[i] = INVALID_INDEX;
}
if (mNbAllocatedEntries > 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<mHashSize; i++) {
uint32 entryIndex = mBuckets[i];
while(entryIndex != INVALID_INDEX) {
// Get the corresponding bucket
const size_t hashCode = Hash()(mEntries[entryIndex]);
const uint32 bucketIndex = hashCode & (capacity - 1);
newNextEntries[entryIndex] = newBuckets[bucketIndex];
newBuckets[bucketIndex] = entryIndex;
// Copy the entry to the new location and destroy the previous one
new (newEntries + entryIndex) V(mEntries[entryIndex]);
mEntries[entryIndex].~V();
entryIndex = mNextEntries[entryIndex];
}
}
if (mNbAllocatedEntries > 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 /// Return true if the set contains a given value
bool contains(const V& value) const { bool contains(const V& value) const {
return findEntry(value) != -1; return findEntry(value) != INVALID_INDEX;
} }
/// Add a value into the set. /// Add a value into the set.
/// Returns true if the item has been inserted and false otherwise. /// Returns true if the item has been inserted and false otherwise.
bool add(const V& value) { bool add(const V& value) {
if (mCapacity == 0) { uint32 bucket;
initialize(0);
}
// Compute the hash code of the value // Compute the hash code of the value
size_t hashCode = Hash()(value); const size_t hashCode = Hash()(value);
// Compute the corresponding bucket index if (mHashSize > 0) {
int bucket = hashCode % mCapacity;
auto keyEqual = KeyEqual(); // Compute the corresponding bucket index
bucket = hashCode & (mHashSize - 1);
// Check if the item is already in the set auto keyEqual = KeyEqual();
for (int i = mBuckets[bucket]; i >= 0; i = mEntries[i].next) {
// If there is already an item with the same value in the set // Check if the item is already in the set
if (mEntries[i].hashCode == hashCode && keyEqual(*mEntries[i].value, value)) { 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; size_t entryIndex;
// If there are free entries to use // If there are no more free entries to use
if (mNbFreeEntries > 0) { if (mFreeIndex == INVALID_INDEX) {
assert(mFreeIndex >= 0);
entryIndex = mFreeIndex;
mFreeIndex = mEntries[entryIndex].next;
mNbFreeEntries--;
}
else {
// If we need to allocator more entries // Allocate more memory
if (mNbUsedEntries == mCapacity) { reserve(mHashSize == 0 ? 16 : mHashSize * 2);
// Allocate more memory // Recompute the bucket index
reserve(mCapacity * 2); bucket = hashCode & (mHashSize - 1);
// Recompute the bucket index
bucket = hashCode % mCapacity;
}
entryIndex = mNbUsedEntries;
mNbUsedEntries++;
} }
assert(mEntries[entryIndex].value == nullptr); assert(mNbEntries < mNbAllocatedEntries);
mEntries[entryIndex].hashCode = hashCode; assert(mFreeIndex != INVALID_INDEX);
mEntries[entryIndex].next = mBuckets[bucket];
mEntries[entryIndex].value = static_cast<V*>(mAllocator.allocate(sizeof(V))); // Get the next free entry
assert(mEntries[entryIndex].value != nullptr); entryIndex = mFreeIndex;
new (mEntries[entryIndex].value) V(value); mFreeIndex = mNextEntries[entryIndex];
mNbEntries++;
mNextEntries[entryIndex] = mBuckets[bucket];
new (mEntries + entryIndex) V(value);
mBuckets[bucket] = entryIndex; mBuckets[bucket] = entryIndex;
return true; return true;
@ -495,46 +431,46 @@ class Set {
/// element after the one that has been removed /// element after the one that has been removed
Iterator remove(const V& value) { Iterator remove(const V& value) {
if (mCapacity > 0) { if (mHashSize > 0) {
size_t hashcode = Hash()(value); const size_t hashcode = Hash()(value);
auto keyEqual = KeyEqual(); auto keyEqual = KeyEqual();
int bucket = hashcode % mCapacity; const uint32 bucket = hashcode & (mHashSize - 1);
int last = -1; uint32 last = INVALID_INDEX;
for (int i = mBuckets[bucket]; i >= 0; last = i, i = mEntries[i].next) { 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 ) { if (last == INVALID_INDEX) {
mBuckets[bucket] = mEntries[i].next; mBuckets[bucket] = mNextEntries[i];
} }
else { else {
mEntries[last].next = mEntries[i].next; mNextEntries[last] = mNextEntries[i];
} }
// Release memory for the value if any uint32 nextEntryIndex = mNextEntries[i];
if (mEntries[i].value != nullptr) { uint32 nextBucketIndex = bucket;
mEntries[i].value->~V();
mAllocator.release(mEntries[i].value, sizeof(V)); mEntries[i].~V();
mEntries[i].value = nullptr; mNextEntries[i] = mFreeIndex;
}
assert(mEntries[i].value == nullptr);
mEntries[i].next = mFreeIndex;
mFreeIndex = i; mFreeIndex = i;
mNbFreeEntries++; mNbEntries--;
// Find the next valid entry to return an iterator // Find the next entry to return an iterator
for (i += 1; i < mNbUsedEntries; i++) { if (nextEntryIndex == INVALID_INDEX) {
nextEntryIndex = 0;
// If the entry is not empty nextBucketIndex++;
if (mEntries[i].value != nullptr) { while(nextBucketIndex < mHashSize && mBuckets[nextBucketIndex] == INVALID_INDEX) {
nextBucketIndex++;
// We have found the next non empty entry }
return Iterator(mEntries, mCapacity, mNbUsedEntries, i); 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<V> list(listAllocator); List<V> list(listAllocator);
for (int i=0; i < mCapacity; i++) { for (auto it = begin(); it != end(); ++it) {
if (mEntries[i].value != nullptr) { list.add(*it);
list.add(*(mEntries[i].value));
}
} }
return list; return list;
@ -559,50 +493,52 @@ class Set {
/// Clear the set /// Clear the set
void clear(bool releaseMemory = false) { void clear(bool releaseMemory = false) {
if (mNbUsedEntries > 0) { for (uint32 i=0; i<mHashSize; i++) {
for (int i=0; i < mCapacity; i++) { uint32 entryIndex = mBuckets[i];
mBuckets[i] = -1; while(entryIndex != INVALID_INDEX) {
mEntries[i].next = -1;
if (mEntries[i].value != nullptr) { // Destroy the entry
mEntries[i].value->~V(); mEntries[entryIndex].~V();
mAllocator.release(mEntries[i].value, sizeof(V));
mEntries[i].value = nullptr; uint32 nextEntryIndex = mNextEntries[entryIndex];
}
// Add entry to the free list
mNextEntries[entryIndex] = mFreeIndex;
mFreeIndex = entryIndex;
entryIndex = nextEntryIndex;
} }
mFreeIndex = -1; mBuckets[i] = INVALID_INDEX;
mNbUsedEntries = 0;
mNbFreeEntries = 0;
} }
// If elements have been allocated if (releaseMemory && mNbAllocatedEntries > 0) {
if (releaseMemory && mCapacity > 0) {
// Destroy the entries // Release previously allocated memory
for (int i=0; i < mCapacity; i++) { mAllocator.release(mBuckets, mHashSize * sizeof(uint32));
mEntries[i].~Entry(); 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; mBuckets = nullptr;
mEntries = nullptr; mEntries = nullptr;
mNextEntries = nullptr;
mNbAllocatedEntries = 0;
mHashSize = 0;
} }
assert(size() == 0); mNbEntries = 0;
} }
/// Return the number of elements in the set /// Return the number of elements in the set
int size() const { uint32 size() const {
return mNbUsedEntries - mNbFreeEntries; return mNbEntries;
} }
/// Return the capacity of the set /// Return the capacity of the set
int capacity() const { uint32 capacity() const {
return mCapacity; return mHashSize;
} }
/// Try to find an item of the set given a key. /// 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 /// an iterator pointing to the end if not found
Iterator find(const V& value) const { Iterator find(const V& value) const {
int bucket; uint32 bucket;
int entry = -1; uint32 entry = INVALID_INDEX;
if (mCapacity > 0) { if (mHashSize > 0) {
size_t hashCode = Hash()(value); const size_t hashCode = Hash()(value);
bucket = hashCode % mCapacity; bucket = hashCode & (mHashSize - 1);
auto keyEqual = KeyEqual(); auto keyEqual = KeyEqual();
for (int i = mBuckets[bucket]; i >= 0; i = mEntries[i].next) { for (uint32 i = mBuckets[bucket]; i != INVALID_INDEX; i = mNextEntries[i]) {
if (mEntries[i].hashCode == hashCode && keyEqual(*(mEntries[i].value), value)) { if (Hash()(mEntries[i]) == hashCode && keyEqual(mEntries[i], value)) {
entry = i; entry = i;
break; break;
} }
} }
} }
if (entry == -1) { if (entry == INVALID_INDEX) {
return end(); return end();
} }
assert(mEntries[entry].value != nullptr); return Iterator(this, bucket, entry);
return Iterator(mEntries, mCapacity, mNbUsedEntries, entry);
} }
/// Overloaded equality operator /// Overloaded equality operator
@ -665,34 +599,35 @@ class Set {
// Clear the set // Clear the set
clear(true); clear(true);
if (set.mCapacity > 0) { mNbAllocatedEntries = set.mNbAllocatedEntries;
mNbEntries = set.mNbEntries;
mHashSize = set.mHashSize;
mFreeIndex = set.mFreeIndex;
// Compute the next larger prime size // Allocate memory for the buckets
mCapacity = getPrimeSize(set.mCapacity); mBuckets = static_cast<uint32*>(mAllocator.allocate(mHashSize * sizeof(uint32)));
// Allocate memory for the buckets // Allocate memory for the entries
mBuckets = static_cast<int*>(mAllocator.allocate(mCapacity * sizeof(int))); mEntries = static_cast<V*>(mAllocator.allocate(mNbAllocatedEntries * sizeof(V)));
mNextEntries = static_cast<uint32*>(mAllocator.allocate(mNbAllocatedEntries * sizeof(uint32)));
// Allocate memory for the entries // Copy the buckets array
mEntries = static_cast<Entry*>(mAllocator.allocate(mCapacity * sizeof(Entry))); std::memcpy(mBuckets, set.mBuckets, mHashSize * sizeof(uint32));
// Copy the buckets // Copy the next entries indices
std::uninitialized_copy(set.mBuckets, set.mBuckets + mCapacity, mBuckets); std::memcpy(mNextEntries, set.mNextEntries, mNbAllocatedEntries * sizeof(uint32));
// Copy the entries // Copy the entries
for (int i=0; i < mCapacity; i++) { for (uint32 i=0; i<mHashSize; i++) {
new (&mEntries[i]) Entry(set.mEntries[i].hashCode, set.mEntries[i].next); uint32 entryIndex = mBuckets[i];
while(entryIndex != INVALID_INDEX) {
if (set.mEntries[i].value != nullptr) { // Copy the entry to the new location and destroy the previous one
mEntries[i].value = static_cast<V*>(mAllocator.allocate(sizeof(V))); new (mEntries + entryIndex) V(set.mEntries[entryIndex]);
new (mEntries[i].value) V(*(set.mEntries[i].value));
} entryIndex = mNextEntries[entryIndex];
} }
mNbUsedEntries = set.mNbUsedEntries;
mNbFreeEntries = set.mNbFreeEntries;
mFreeIndex = set.mFreeIndex;
} }
} }
@ -702,7 +637,7 @@ class Set {
/// Return a begin iterator /// Return a begin iterator
Iterator begin() const { Iterator begin() const {
// If the map is empty // If the set is empty
if (size() == 0) { if (size() == 0) {
// Return an iterator to the end // Return an iterator to the end
@ -710,33 +645,29 @@ class Set {
} }
// Find the first used entry // Find the first used entry
int entry; uint32 bucketIndex = 0;
for (entry=0; entry < mNbUsedEntries; entry++) { while (mBuckets[bucketIndex] == INVALID_INDEX) {
if (mEntries[entry].value != nullptr) {
return Iterator(mEntries, mCapacity, mNbUsedEntries, entry); bucketIndex++;
}
} }
assert(false); assert(bucketIndex < mHashSize);
return end(); assert(mBuckets[bucketIndex] != INVALID_INDEX);
return Iterator(this, bucketIndex, mBuckets[bucketIndex]);
} }
/// Return a end iterator /// Return a end iterator
Iterator end() const { Iterator end() const {
return Iterator(mEntries, mCapacity, mNbUsedEntries, mCapacity);
return Iterator(this, mHashSize, 0);
} }
// ---------- Friendship ---------- //
friend class Iterator;
}; };
template<typename V, class Hash, class KeyEqual>
const int Set<V, Hash, KeyEqual>::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<typename V, class Hash, class KeyEqual>
int Set<V, Hash, KeyEqual>::LARGEST_PRIME = -1;
} }
#endif #endif

View File

@ -97,6 +97,36 @@ class PhysicsCommon {
/// Destroy and release everything that has been allocated /// Destroy and release everything that has been allocated
void release(); 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 // If profiling is enabled
#ifdef IS_RP3D_PROFILING_ENABLED #ifdef IS_RP3D_PROFILING_ENABLED
@ -106,6 +136,9 @@ class PhysicsCommon {
/// Destroy a profiler /// Destroy a profiler
void destroyProfiler(Profiler* profiler); void destroyProfiler(Profiler* profiler);
/// Delete a profiler
void deleteProfiler(Profiler* profiler);
#endif #endif
public : public :
@ -186,7 +219,6 @@ class PhysicsCommon {
/// Set the logger /// Set the logger
static void setLogger(Logger* logger); static void setLogger(Logger* logger);
}; };
// Return the current logger // Return the current logger

View File

@ -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) /// 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); decimal computePointToPlaneDistance(const Vector3& point, const Vector3& planeNormal, const Vector3& planePoint);
/// Return true if the given number is prime /// Return true if a number is a power of two
bool isPrimeNumber(int number); 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) /// Return an unique integer from two integer numbers (pairing function)
/// Here we assume that the two parameter numbers are sorted such that /// Here we assume that the two parameter numbers are sorted such that

View File

@ -58,53 +58,63 @@ void PhysicsCommon::release() {
// Destroy the physics worlds // Destroy the physics worlds
for (auto it = mPhysicsWorlds.begin(); it != mPhysicsWorlds.end(); ++it) { for (auto it = mPhysicsWorlds.begin(); it != mPhysicsWorlds.end(); ++it) {
destroyPhysicsWorld(*it); deletePhysicsWorld(*it);
} }
mPhysicsWorlds.clear();
// Destroy the sphere shapes // Destroy the sphere shapes
for (auto it = mSphereShapes.begin(); it != mSphereShapes.end(); ++it) { for (auto it = mSphereShapes.begin(); it != mSphereShapes.end(); ++it) {
destroySphereShape(*it); deleteSphereShape(*it);
} }
mSphereShapes.clear();
// Destroy the box shapes // Destroy the box shapes
for (auto it = mBoxShapes.begin(); it != mBoxShapes.end(); ++it) { for (auto it = mBoxShapes.begin(); it != mBoxShapes.end(); ++it) {
destroyBoxShape(*it); deleteBoxShape(*it);
} }
mBoxShapes.clear();
// Destroy the capsule shapes // Destroy the capsule shapes
for (auto it = mCapsuleShapes.begin(); it != mCapsuleShapes.end(); ++it) { for (auto it = mCapsuleShapes.begin(); it != mCapsuleShapes.end(); ++it) {
destroyCapsuleShape(*it); deleteCapsuleShape(*it);
} }
mCapsuleShapes.clear();
// Destroy the convex mesh shapes // Destroy the convex mesh shapes
for (auto it = mConvexMeshShapes.begin(); it != mConvexMeshShapes.end(); ++it) { for (auto it = mConvexMeshShapes.begin(); it != mConvexMeshShapes.end(); ++it) {
destroyConvexMeshShape(*it); deleteConvexMeshShape(*it);
} }
mConvexMeshShapes.clear();
// Destroy the heigh-field shapes // Destroy the heigh-field shapes
for (auto it = mHeightFieldShapes.begin(); it != mHeightFieldShapes.end(); ++it) { for (auto it = mHeightFieldShapes.begin(); it != mHeightFieldShapes.end(); ++it) {
destroyHeightFieldShape(*it); deleteHeightFieldShape(*it);
} }
mHeightFieldShapes.clear();
// Destroy the concave mesh shapes // Destroy the concave mesh shapes
for (auto it = mConcaveMeshShapes.begin(); it != mConcaveMeshShapes.end(); ++it) { for (auto it = mConcaveMeshShapes.begin(); it != mConcaveMeshShapes.end(); ++it) {
destroyConcaveMeshShape(*it); deleteConcaveMeshShape(*it);
} }
mConcaveMeshShapes.clear();
// Destroy the polyhedron mesh // Destroy the polyhedron mesh
for (auto it = mPolyhedronMeshes.begin(); it != mPolyhedronMeshes.end(); ++it) { for (auto it = mPolyhedronMeshes.begin(); it != mPolyhedronMeshes.end(); ++it) {
destroyPolyhedronMesh(*it); deletePolyhedronMesh(*it);
} }
mPolyhedronMeshes.clear();
// Destroy the triangle mesh // Destroy the triangle mesh
for (auto it = mTriangleMeshes.begin(); it != mTriangleMeshes.end(); ++it) { for (auto it = mTriangleMeshes.begin(); it != mTriangleMeshes.end(); ++it) {
destroyTriangleMesh(*it); deleteTriangleMesh(*it);
} }
mTriangleMeshes.clear();
// Destroy the default loggers // Destroy the default loggers
for (auto it = mDefaultLoggers.begin(); it != mDefaultLoggers.end(); ++it) { for (auto it = mDefaultLoggers.begin(); it != mDefaultLoggers.end(); ++it) {
destroyDefaultLogger(*it); deleteDefaultLogger(*it);
} }
mDefaultLoggers.clear();
// If profiling is enabled // If profiling is enabled
#ifdef IS_RP3D_PROFILING_ENABLED #ifdef IS_RP3D_PROFILING_ENABLED
@ -112,8 +122,9 @@ void PhysicsCommon::release() {
// Destroy the profilers // Destroy the profilers
for (auto it = mProfilers.begin(); it != mProfilers.end(); ++it) { for (auto it = mProfilers.begin(); it != mProfilers.end(); ++it) {
destroyProfiler(*it); deleteProfiler(*it);
} }
mProfilers.clear();
#endif #endif
@ -151,13 +162,22 @@ PhysicsWorld* PhysicsCommon::createPhysicsWorld(const PhysicsWorld::WorldSetting
*/ */
void PhysicsCommon::destroyPhysicsWorld(PhysicsWorld* world) { 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 // Call the destructor of the world
world->~PhysicsWorld(); world->~PhysicsWorld();
// Release allocated memory // Release allocated memory
mMemoryManager.release(MemoryManager::AllocationType::Heap, world, sizeof(PhysicsWorld)); mMemoryManager.release(MemoryManager::AllocationType::Heap, world, sizeof(PhysicsWorld));
mPhysicsWorlds.remove(world);
} }
// Create and return a sphere collision shape // Create and return a sphere collision shape
@ -185,6 +205,17 @@ SphereShape* PhysicsCommon::createSphereShape(const decimal radius) {
*/ */
void PhysicsCommon::destroySphereShape(SphereShape* sphereShape) { 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 the shape is still part of some colliders
if (sphereShape->mColliders.size() > 0) { if (sphereShape->mColliders.size() > 0) {
@ -197,8 +228,6 @@ void PhysicsCommon::destroySphereShape(SphereShape* sphereShape) {
// Release allocated memory // Release allocated memory
mMemoryManager.release(MemoryManager::AllocationType::Pool, sphereShape, sizeof(SphereShape)); mMemoryManager.release(MemoryManager::AllocationType::Pool, sphereShape, sizeof(SphereShape));
mSphereShapes.remove(sphereShape);
} }
// Create and return a box collision shape // Create and return a box collision shape
@ -226,6 +255,17 @@ BoxShape* PhysicsCommon::createBoxShape(const Vector3& halfExtents) {
*/ */
void PhysicsCommon::destroyBoxShape(BoxShape* boxShape) { 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 the shape is still part of some colliders
if (boxShape->mColliders.size() > 0) { if (boxShape->mColliders.size() > 0) {
@ -238,8 +278,6 @@ void PhysicsCommon::destroyBoxShape(BoxShape* boxShape) {
// Release allocated memory // Release allocated memory
mMemoryManager.release(MemoryManager::AllocationType::Pool, boxShape, sizeof(BoxShape)); mMemoryManager.release(MemoryManager::AllocationType::Pool, boxShape, sizeof(BoxShape));
mBoxShapes.remove(boxShape);
} }
// Create and return a capsule shape // Create and return a capsule shape
@ -275,6 +313,17 @@ CapsuleShape* PhysicsCommon::createCapsuleShape(decimal radius, decimal height)
*/ */
void PhysicsCommon::destroyCapsuleShape(CapsuleShape* capsuleShape) { 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 the shape is still part of some colliders
if (capsuleShape->mColliders.size() > 0) { if (capsuleShape->mColliders.size() > 0) {
@ -287,8 +336,6 @@ void PhysicsCommon::destroyCapsuleShape(CapsuleShape* capsuleShape) {
// Release allocated memory // Release allocated memory
mMemoryManager.release(MemoryManager::AllocationType::Pool, capsuleShape, sizeof(CapsuleShape)); mMemoryManager.release(MemoryManager::AllocationType::Pool, capsuleShape, sizeof(CapsuleShape));
mCapsuleShapes.remove(capsuleShape);
} }
// Create and return a convex mesh shape // Create and return a convex mesh shape
@ -312,6 +359,17 @@ ConvexMeshShape* PhysicsCommon::createConvexMeshShape(PolyhedronMesh* polyhedron
*/ */
void PhysicsCommon::destroyConvexMeshShape(ConvexMeshShape* convexMeshShape) { 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 the shape is still part of some colliders
if (convexMeshShape->mColliders.size() > 0) { if (convexMeshShape->mColliders.size() > 0) {
@ -324,8 +382,6 @@ void PhysicsCommon::destroyConvexMeshShape(ConvexMeshShape* convexMeshShape) {
// Release allocated memory // Release allocated memory
mMemoryManager.release(MemoryManager::AllocationType::Pool, convexMeshShape, sizeof(ConvexMeshShape)); mMemoryManager.release(MemoryManager::AllocationType::Pool, convexMeshShape, sizeof(ConvexMeshShape));
mConvexMeshShapes.remove(convexMeshShape);
} }
// Create and return a height-field shape // Create and return a height-field shape
@ -358,6 +414,17 @@ HeightFieldShape* PhysicsCommon::createHeightFieldShape(int nbGridColumns, int n
*/ */
void PhysicsCommon::destroyHeightFieldShape(HeightFieldShape* heightFieldShape) { 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 the shape is still part of some colliders
if (heightFieldShape->mColliders.size() > 0) { if (heightFieldShape->mColliders.size() > 0) {
@ -370,8 +437,6 @@ void PhysicsCommon::destroyHeightFieldShape(HeightFieldShape* heightFieldShape)
// Release allocated memory // Release allocated memory
mMemoryManager.release(MemoryManager::AllocationType::Pool, heightFieldShape, sizeof(HeightFieldShape)); mMemoryManager.release(MemoryManager::AllocationType::Pool, heightFieldShape, sizeof(HeightFieldShape));
mHeightFieldShapes.remove(heightFieldShape);
} }
// Create and return a concave mesh shape // Create and return a concave mesh shape
@ -395,6 +460,17 @@ ConcaveMeshShape* PhysicsCommon::createConcaveMeshShape(TriangleMesh* triangleMe
*/ */
void PhysicsCommon::destroyConcaveMeshShape(ConcaveMeshShape* concaveMeshShape) { 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 the shape is still part of some colliders
if (concaveMeshShape->mColliders.size() > 0) { if (concaveMeshShape->mColliders.size() > 0) {
@ -407,8 +483,6 @@ void PhysicsCommon::destroyConcaveMeshShape(ConcaveMeshShape* concaveMeshShape)
// Release allocated memory // Release allocated memory
mMemoryManager.release(MemoryManager::AllocationType::Pool, concaveMeshShape, sizeof(ConcaveMeshShape)); mMemoryManager.release(MemoryManager::AllocationType::Pool, concaveMeshShape, sizeof(ConcaveMeshShape));
mConcaveMeshShapes.remove(concaveMeshShape);
} }
// Create a polyhedron mesh // Create a polyhedron mesh
@ -431,13 +505,22 @@ PolyhedronMesh* PhysicsCommon::createPolyhedronMesh(PolygonVertexArray* polygonV
*/ */
void PhysicsCommon::destroyPolyhedronMesh(PolyhedronMesh* polyhedronMesh) { 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 // Call the destructor of the shape
polyhedronMesh->~PolyhedronMesh(); polyhedronMesh->~PolyhedronMesh();
// Release allocated memory // Release allocated memory
mMemoryManager.release(MemoryManager::AllocationType::Pool, polyhedronMesh, sizeof(PolyhedronMesh)); mMemoryManager.release(MemoryManager::AllocationType::Pool, polyhedronMesh, sizeof(PolyhedronMesh));
mPolyhedronMeshes.remove(polyhedronMesh);
} }
// Create a triangle mesh // Create a triangle mesh
@ -459,13 +542,22 @@ TriangleMesh* PhysicsCommon::createTriangleMesh() {
*/ */
void PhysicsCommon::destroyTriangleMesh(TriangleMesh* triangleMesh) { 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 // Call the destructor of the shape
triangleMesh->~TriangleMesh(); triangleMesh->~TriangleMesh();
// Release allocated memory // Release allocated memory
mMemoryManager.release(MemoryManager::AllocationType::Pool, triangleMesh, sizeof(TriangleMesh)); mMemoryManager.release(MemoryManager::AllocationType::Pool, triangleMesh, sizeof(TriangleMesh));
mTriangleMeshes.remove(triangleMesh);
} }
// Create and return a new logger // Create and return a new logger
@ -487,13 +579,22 @@ DefaultLogger* PhysicsCommon::createDefaultLogger() {
*/ */
void PhysicsCommon::destroyDefaultLogger(DefaultLogger* logger) { 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 // Call the destructor of the logger
logger->~DefaultLogger(); logger->~DefaultLogger();
// Release allocated memory // Release allocated memory
mMemoryManager.release(MemoryManager::AllocationType::Pool, logger, sizeof(DefaultLogger)); mMemoryManager.release(MemoryManager::AllocationType::Pool, logger, sizeof(DefaultLogger));
mDefaultLoggers.remove(logger);
} }
// If profiling is enabled // If profiling is enabled
@ -514,13 +615,18 @@ Profiler* PhysicsCommon::createProfiler() {
// Destroy a profiler // Destroy a profiler
void PhysicsCommon::destroyProfiler(Profiler* 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 // Call the destructor of the profiler
profiler->~Profiler(); profiler->~Profiler();
// Release allocated memory // Release allocated memory
mMemoryManager.release(MemoryManager::AllocationType::Pool, profiler, sizeof(Profiler)); mMemoryManager.release(MemoryManager::AllocationType::Pool, profiler, sizeof(Profiler));
mProfilers.remove(profiler);
} }
#endif #endif

View File

@ -379,27 +379,3 @@ Vector3 reactphysics3d::projectPointOntoPlane(const Vector3& point, const Vector
decimal reactphysics3d::computePointToPlaneDistance(const Vector3& point, const Vector3& planeNormal, const Vector3& planePoint) { decimal reactphysics3d::computePointToPlaneDistance(const Vector3& point, const Vector3& planeNormal, const Vector3& planePoint) {
return planeNormal.dot(point - 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<int>(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;
}

View File

@ -53,9 +53,9 @@ int main() {
// ---------- Containers tests ---------- // // ---------- Containers tests ---------- //
testSuite.addTest(new TestSet("Set"));
testSuite.addTest(new TestList("List")); testSuite.addTest(new TestList("List"));
testSuite.addTest(new TestMap("Map")); testSuite.addTest(new TestMap("Map"));
testSuite.addTest(new TestSet("Set"));
testSuite.addTest(new TestDeque("Deque")); testSuite.addTest(new TestDeque("Deque"));
testSuite.addTest(new TestStack("Stack")); testSuite.addTest(new TestStack("Stack"));

View File

@ -94,7 +94,7 @@ class TestList : public Test {
rp3d_test(list4.size() == 0); rp3d_test(list4.size() == 0);
List<int> list5(list3); List<int> list5(list3);
rp3d_test(list5.capacity() == list3.size()); rp3d_test(list5.capacity() == list3.capacity());
rp3d_test(list5.size() == list3.size()); rp3d_test(list5.size() == list3.size());
for (uint i=0; i<list3.size(); i++) { for (uint i=0; i<list3.size(); i++) {
rp3d_test(list5[i] == list3[i]); rp3d_test(list5[i] == list3[i]);

View File

@ -29,6 +29,7 @@
// Libraries // Libraries
#include <reactphysics3d/containers/List.h> #include <reactphysics3d/containers/List.h>
#include <reactphysics3d/memory/DefaultAllocator.h> #include <reactphysics3d/memory/DefaultAllocator.h>
#include <reactphysics3d/mathematics/mathematics.h>
/// Reactphysics3D namespace /// Reactphysics3D namespace
namespace reactphysics3d { namespace reactphysics3d {
@ -267,6 +268,33 @@ class TestMathematicsFunctions : public Test {
rp3d_test(approxEqual(clipPolygonVertices[3].y, 4, 0.000001)); rp3d_test(approxEqual(clipPolygonVertices[3].y, 4, 0.000001));
rp3d_test(approxEqual(clipPolygonVertices[3].z, 0, 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);
} }
}; };