/******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * * Copyright (c) 2010-2018 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * * In no event will the authors be held liable for any damages arising from the * * use of this software. * * * * Permission is granted to anyone to use this software for any purpose, * * including commercial applications, and to alter it and redistribute it * * freely, subject to the following restrictions: * * * * 1. The origin of this software must not be misrepresented; you must not claim * * that you wrote the original software. If you use this software in a * * product, an acknowledgment in the product documentation would be * * appreciated but is not required. * * * * 2. Altered source versions must be plainly marked as such, and must not be * * misrepresented as being the original software. * * * * 3. This notice may not be removed or altered from any source distribution. * * * ********************************************************************************/ #ifndef REACTPHYSICS3D_MAP_H #define REACTPHYSICS3D_MAP_H // Libraries #include "memory/MemoryAllocator.h" #include "mathematics/mathematics_functions.h" #include "containers/Pair.h" #include #include #include #include namespace reactphysics3d { // Class Map /** * This class represents a simple generic associative map. This map is * implemented with a hash table. */ template 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; /// Array of prime numbers for the size of the map static const int PRIMES[NB_PRIMES]; /// Largest prime number static int LARGEST_PRIME; // -------------------- Attributes -------------------- // /// Current number of used entries in the map int mNbUsedEntries; /// Number of free entries among the used ones int mNbFreeEntries; /// Current capacity of the map int mCapacity; /// Array with all the buckets int* mBuckets; /// Array with all the entries Entry* mEntries; /// Memory allocator MemoryAllocator& mAllocator; /// Index to the fist free entry int mFreeIndex; // -------------------- Methods -------------------- // /// Initialize the map 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 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) { size_t hashCode = std::hash()(key); int bucket = hashCode % mCapacity; for (int i = mBuckets[bucket]; i >= 0; i = mEntries[i].next) { if (mEntries[i].hashCode == hashCode && mEntries[i].keyValue->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; } /// Clear and reset the map void reset() { // If elements have been allocated if (mCapacity > 0) { // Clear the map clear(); // Destroy the entries for (int i=0; i < mCapacity; i++) { mEntries[i].~Entry(); } mAllocator.release(mBuckets, mCapacity * sizeof(int)); mAllocator.release(mEntries, mCapacity * sizeof(Entry)); mNbUsedEntries = 0; mNbFreeEntries = 0; mCapacity = 0; mBuckets = nullptr; mEntries = nullptr; mFreeIndex = -1; } } public: /// Class Iterator /** * This class represents an iterator for the Map */ class Iterator { private: /// Array of entries const Entry* mEntries; /// Capacity of the map int mCapacity; /// Number of used entries in the map int mNbUsedEntries; /// Index of the current entry int mCurrentEntry; /// Advance the iterator void advance() { // If we are trying to move past the end assert(mCurrentEntry < mNbUsedEntries); 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; } } // We have not find a non empty entry, we return an iterator to the end mCurrentEntry = mCapacity; } public: // Iterator traits using value_type = Pair; using difference_type = std::ptrdiff_t; using pointer = Pair*; using reference = Pair&; using iterator_category = std::forward_iterator_tag; /// Constructor Iterator() = default; /// Constructor Iterator(const Entry* entries, int capacity, int nbUsedEntries, int currentEntry) :mEntries(entries), mCapacity(capacity), mNbUsedEntries(nbUsedEntries), mCurrentEntry(currentEntry) { } /// Copy constructor Iterator(const Iterator& it) :mEntries(it.mEntries), mCapacity(it.mCapacity), mNbUsedEntries(it.mNbUsedEntries), mCurrentEntry(it.mCurrentEntry) { } /// Deferencable reference operator*() const { assert(mCurrentEntry >= 0 && mCurrentEntry < mNbUsedEntries); assert(mEntries[mCurrentEntry].keyValue != nullptr); return *(mEntries[mCurrentEntry].keyValue); } /// Deferencable pointer operator->() const { assert(mCurrentEntry >= 0 && mCurrentEntry < mNbUsedEntries); assert(mEntries[mCurrentEntry].keyValue != nullptr); return mEntries[mCurrentEntry].keyValue; } /// Post increment (it++) Iterator& operator++() { advance(); return *this; } /// Pre increment (++it) Iterator operator++(int number) { Iterator tmp = *this; advance(); return tmp; } /// Equality operator (it == end()) bool operator==(const Iterator& iterator) const { return mCurrentEntry == iterator.mCurrentEntry && mEntries == iterator.mEntries; } /// Inequality operator (it != end()) bool operator!=(const Iterator& iterator) const { return !(*this == iterator); } }; // -------------------- 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); } if (capacity > 0) { initialize(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) { if (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))); // Copy the buckets std::uninitialized_copy(map.mBuckets, map.mBuckets + mCapacity, mBuckets); // Copy the entries for (int i=0; i < mCapacity; i++) { new (&mEntries[i]) Entry(map.mEntries[i].hashCode, map.mEntries[i].next); if (map.mEntries[i].keyValue != nullptr) { mEntries[i].keyValue = static_cast*>(mAllocator.allocate(sizeof(Pair))); new (mEntries[i].keyValue) Pair(*(map.mEntries[i].keyValue)); } } } } /// Destructor ~Map() { reset(); } /// Allocate memory for a given number of elements void reserve(int capacity) { if (capacity <= mCapacity) return; if (capacity > LARGEST_PRIME && LARGEST_PRIME > mCapacity) { capacity = LARGEST_PRIME; } else { capacity = getPrimeSize(capacity); } expand(capacity); } /// Return true if the map contains an item with the given key bool containsKey(const K& key) const { return findEntry(key) != -1; } /// Add an element into the map void add(const Pair& keyValue, bool insertIfAlreadyPresent = false) { if (mCapacity == 0) { initialize(0); } // Compute the hash code of the key size_t hashCode = std::hash()(keyValue.first); // Compute the corresponding bucket index int bucket = hashCode % mCapacity; // Check if the item is already in the map for (int i = mBuckets[bucket]; i >= 0; i = mEntries[i].next) { // If there is already an item with the same key in the map if (mEntries[i].hashCode == hashCode && mEntries[i].keyValue->first == keyValue.first) { if (insertIfAlreadyPresent) { // Destruct the previous key/value mEntries[i].keyValue->~Pair(); // Copy construct the new key/value new (mEntries[i].keyValue) Pair(keyValue); return; } else { 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 we need to allocator more entries if (mNbUsedEntries == mCapacity) { // Allocate more memory reserve(mCapacity * 2); // Recompute the bucket index bucket = hashCode % mCapacity; } entryIndex = mNbUsedEntries; mNbUsedEntries++; } 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); mBuckets[bucket] = entryIndex; } /// Remove the element pointed by some iterator /// This method returns an iterator pointing to the element after /// the one that has been removed Iterator remove(const Iterator& it) { const K& key = it->first; return remove(key); } /// Remove the element from the map with a given key /// This method returns an iterator pointing to the element after /// the one that has been removed Iterator remove(const K& key) { if (mCapacity > 0) { size_t hashcode = std::hash()(key); int bucket = hashcode % mCapacity; int last = -1; for (int i = mBuckets[bucket]; i >= 0; last = i, i = mEntries[i].next) { if (mEntries[i].hashCode == hashcode && mEntries[i].keyValue->first == key) { if (last < 0 ) { mBuckets[bucket] = mEntries[i].next; } else { mEntries[last].next = mEntries[i].next; } // 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; mFreeIndex = i; mNbFreeEntries++; // 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); } } return end(); } } } // Return the end iterator return end(); } /// Clear the map void clear() { if (mNbUsedEntries > 0) { for (int i=0; i < mCapacity; i++) { mBuckets[i] = -1; mEntries[i].next = -1; if (mEntries[i].keyValue != nullptr) { mEntries[i].keyValue->~Pair(); mAllocator.release(mEntries[i].keyValue, sizeof(Pair)); mEntries[i].keyValue = nullptr; } } mFreeIndex = -1; mNbUsedEntries = 0; mNbFreeEntries = 0; } assert(size() == 0); } /// Return the number of elements in the map int size() const { return mNbUsedEntries - mNbFreeEntries; } /// Return the capacity of the map int capacity() const { return mCapacity; } /// Try to find an item of the map given a key. /// The method returns an iterator to the found item or /// an iterator pointing to the end if not found Iterator find(const K& key) const { int bucket; int entry = -1; if (mCapacity > 0) { size_t hashCode = std::hash()(key); bucket = hashCode % mCapacity; for (int i = mBuckets[bucket]; i >= 0; i = mEntries[i].next) { if (mEntries[i].hashCode == hashCode && mEntries[i].keyValue->first == key) { entry = i; break; } } } if (entry == -1) { return end(); } assert(mEntries[entry].keyValue != nullptr); return Iterator(mEntries, mCapacity, mNbUsedEntries, entry); } /// Overloaded index operator V& operator[](const K& key) { int entry = -1; if (mCapacity > 0) { entry = findEntry(key); } if (entry == -1) { 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; } /// Overloaded index operator const V& operator[](const K& key) const { int entry = -1; if (mCapacity > 0) { entry = findEntry(key); } if (entry == -1) { 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; } /// Overloaded equality operator bool operator==(const Map& map) const { if (size() != map.size()) return false; for (auto it = begin(); it != end(); ++it) { auto it2 = map.find(it->first); if (it2 == map.end() || it2->second != it->second) { return false; } } for (auto it = map.begin(); it != map.end(); ++it) { auto it2 = find(it->first); if (it2 == end() || it2->second != it->second) { return false; } } return true; } /// Overloaded not equal operator bool operator!=(const Map& map) const { return !((*this) == map); } /// Overloaded assignment operator Map& operator=(const Map& map) { // Check for self assignment if (this != &map) { // Reset the map reset(); if (map.mCapacity > 0) { // Compute the next larger prime size mCapacity = getPrimeSize(map.mCapacity); // 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))); // Copy the buckets std::uninitialized_copy(map.mBuckets, map.mBuckets + mCapacity, mBuckets); // Copy the entries for (int i=0; i < mCapacity; i++) { new (&mEntries[i]) Entry(map.mEntries[i].hashCode, map.mEntries[i].next); if (map.mEntries[i].keyValue != nullptr) { mEntries[i].keyValue = static_cast*>(mAllocator.allocate(sizeof(Pair))); new (mEntries[i].keyValue) Pair(*(map.mEntries[i].keyValue)); } } mNbUsedEntries = map.mNbUsedEntries; mNbFreeEntries = map.mNbFreeEntries; mFreeIndex = map.mFreeIndex; } } return *this; } /// Return a begin iterator Iterator begin() const { // If the map is empty if (size() == 0) { // Return an iterator to the end return end(); } // Find the first used entry int entry; for (entry=0; entry < mNbUsedEntries; entry++) { if (mEntries[entry].keyValue != nullptr) { return Iterator(mEntries, mCapacity, mNbUsedEntries, entry); } } assert(false); return end(); } /// Return a end iterator Iterator end() const { return Iterator(mEntries, mCapacity, mNbUsedEntries, mCapacity); } }; 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