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:
/// 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<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
int findEntry(const V& value) const {
uint32 findEntry(const V& value) const {
if (mCapacity > 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<int>::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<V, Hash, KeyEqual>& 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<uint32*>(mAllocator.allocate(mHashSize * sizeof(uint32)));
// Allocate memory for the buckets
mBuckets = static_cast<int*>(mAllocator.allocate(mCapacity * sizeof(int)));
// Allocate memory for the entries
mEntries = static_cast<V*>(mAllocator.allocate(mNbAllocatedEntries * sizeof(V)));
mNextEntries = static_cast<uint32*>(mAllocator.allocate(mNbAllocatedEntries * sizeof(uint32)));
// Allocate memory for the entries
mEntries = static_cast<Entry*>(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<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) {
mEntries[i].value = static_cast<V*>(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<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
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<V*>(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<V> 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<mHashSize; i++) {
for (int i=0; i < mCapacity; i++) {
mBuckets[i] = -1;
mEntries[i].next = -1;
if (mEntries[i].value != nullptr) {
mEntries[i].value->~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<uint32*>(mAllocator.allocate(mHashSize * sizeof(uint32)));
// Allocate memory for the buckets
mBuckets = static_cast<int*>(mAllocator.allocate(mCapacity * sizeof(int)));
// Allocate memory for the entries
mEntries = static_cast<V*>(mAllocator.allocate(mNbAllocatedEntries * sizeof(V)));
mNextEntries = static_cast<uint32*>(mAllocator.allocate(mNbAllocatedEntries * sizeof(uint32)));
// Allocate memory for the entries
mEntries = static_cast<Entry*>(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<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) {
mEntries[i].value = static_cast<V*>(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<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

View File

@ -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

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)
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

View File

@ -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

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) {
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 ---------- //
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"));

View File

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

View File

@ -29,6 +29,7 @@
// Libraries
#include <reactphysics3d/containers/List.h>
#include <reactphysics3d/memory/DefaultAllocator.h>
#include <reactphysics3d/mathematics/mathematics.h>
/// 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);
}
};