715 lines
25 KiB
C++
Executable File
715 lines
25 KiB
C++
Executable File
/********************************************************************************
|
|
* ReactPhysics3D physics library, http://www.reactphysics3d.com *
|
|
* Copyright (c) 2010-2020 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 <reactphysics3d/memory/MemoryAllocator.h>
|
|
#include <reactphysics3d/mathematics/mathematics_functions.h>
|
|
#include <reactphysics3d/containers/Pair.h>
|
|
#include <cstring>
|
|
#include <stdexcept>
|
|
#include <functional>
|
|
#include <limits>
|
|
|
|
namespace reactphysics3d {
|
|
|
|
// Class Map
|
|
/**
|
|
* This class represents a simple generic associative map. This map is
|
|
* implemented with a hash table.
|
|
*/
|
|
template<typename K, typename V, class Hash = std::hash<K>, class KeyEqual = std::equal_to<K>>
|
|
class Map {
|
|
|
|
private:
|
|
|
|
// -------------------- Constants -------------------- //
|
|
|
|
/// Default load factor
|
|
static constexpr float DEFAULT_LOAD_FACTOR = 0.75;
|
|
|
|
/// Invalid index in the array
|
|
static constexpr uint32 INVALID_INDEX = 0xffffffff;
|
|
|
|
// -------------------- Attributes -------------------- //
|
|
|
|
/// Total number of allocated entries
|
|
uint32 mNbAllocatedEntries;
|
|
|
|
/// Number of items in the set
|
|
uint32 mNbEntries;
|
|
|
|
/// Number of buckets and size of the hash table (nbEntries = loadFactor * mHashSize)
|
|
uint32 mHashSize ;
|
|
|
|
/// Array with all the buckets
|
|
uint32* mBuckets;
|
|
|
|
/// Array with all the entries
|
|
Pair<K, 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
|
|
uint32 mFreeIndex;
|
|
|
|
// -------------------- Methods -------------------- //
|
|
|
|
/// Return the index of the entry with a given key or INVALID_INDEX if there is no entry with this key
|
|
uint32 findEntry(const K& key) const {
|
|
|
|
if (mHashSize > 0) {
|
|
|
|
const size_t hashCode = Hash()(key);
|
|
const uint32 bucket = hashCode & (mHashSize - 1);
|
|
auto keyEqual = KeyEqual();
|
|
|
|
for (uint32 i = mBuckets[bucket]; i != INVALID_INDEX; i = mNextEntries[i]) {
|
|
if (Hash()(mEntries[i].first) == hashCode && keyEqual(mEntries[i].first, key)) {
|
|
return i;
|
|
}
|
|
}
|
|
}
|
|
|
|
return INVALID_INDEX;
|
|
}
|
|
|
|
public:
|
|
|
|
/// Class Iterator
|
|
/**
|
|
* This class represents an iterator for the Map
|
|
*/
|
|
class Iterator {
|
|
|
|
private:
|
|
|
|
/// Pointer to the map
|
|
const Map* mMap;
|
|
|
|
/// Index of the current bucket
|
|
uint32 mCurrentBucketIndex;
|
|
|
|
/// Index of the current entry
|
|
uint32 mCurrentEntryIndex;
|
|
|
|
/// Advance the iterator
|
|
void advance() {
|
|
|
|
assert(mCurrentBucketIndex < mMap->mHashSize);
|
|
assert(mCurrentEntryIndex < mMap->mNbAllocatedEntries);
|
|
|
|
// Try the next entry
|
|
if (mMap->mNextEntries[mCurrentEntryIndex] != INVALID_INDEX) {
|
|
mCurrentEntryIndex = mMap->mNextEntries[mCurrentEntryIndex];
|
|
return;
|
|
}
|
|
|
|
// Try to move to the next bucket
|
|
mCurrentEntryIndex = 0;
|
|
mCurrentBucketIndex++;
|
|
while(mCurrentBucketIndex < mMap->mHashSize && mMap->mBuckets[mCurrentBucketIndex] == INVALID_INDEX) {
|
|
mCurrentBucketIndex++;
|
|
}
|
|
if (mCurrentBucketIndex < mMap->mHashSize) {
|
|
mCurrentEntryIndex = mMap->mBuckets[mCurrentBucketIndex];
|
|
}
|
|
}
|
|
|
|
public:
|
|
|
|
// Iterator traits
|
|
using value_type = Pair<K,V>;
|
|
using difference_type = std::ptrdiff_t;
|
|
using pointer = Pair<K, V>*;
|
|
using reference = Pair<K,V>&;
|
|
using iterator_category = std::forward_iterator_tag;
|
|
|
|
/// Constructor
|
|
Iterator() = default;
|
|
|
|
/// Constructor
|
|
Iterator(const Map* map, uint32 bucketIndex, uint32 entryIndex)
|
|
:mMap(map), mCurrentBucketIndex(bucketIndex), mCurrentEntryIndex(entryIndex) {
|
|
|
|
}
|
|
|
|
/// Copy constructor
|
|
Iterator(const Iterator& it)
|
|
:mMap(it.mMap), mCurrentBucketIndex(it.mCurrentBucketIndex), mCurrentEntryIndex(it.mCurrentEntryIndex) {
|
|
|
|
}
|
|
|
|
/// Deferencable
|
|
reference operator*() const {
|
|
assert(mCurrentEntryIndex < mMap->mNbAllocatedEntries);
|
|
assert(mCurrentEntryIndex != INVALID_INDEX);
|
|
return mMap->mEntries[mCurrentEntryIndex];
|
|
}
|
|
|
|
/// Deferencable
|
|
pointer operator->() const {
|
|
assert(mCurrentEntryIndex < mMap->mNbAllocatedEntries);
|
|
assert(mCurrentEntryIndex != INVALID_INDEX);
|
|
return &(mMap->mEntries[mCurrentEntryIndex]);
|
|
}
|
|
|
|
/// Post increment (it++)
|
|
Iterator& operator++() {
|
|
advance();
|
|
return *this;
|
|
}
|
|
|
|
/// Pre increment (++it)
|
|
Iterator operator++(int) {
|
|
Iterator tmp = *this;
|
|
advance();
|
|
return tmp;
|
|
}
|
|
|
|
/// Equality operator (it == end())
|
|
bool operator==(const Iterator& iterator) const {
|
|
return mCurrentBucketIndex == iterator.mCurrentBucketIndex && mCurrentEntryIndex == iterator.mCurrentEntryIndex && mMap == iterator.mMap;
|
|
}
|
|
|
|
/// Inequality operator (it != end())
|
|
bool operator!=(const Iterator& iterator) const {
|
|
return !(*this == iterator);
|
|
}
|
|
|
|
/// Overloaded assignment operator
|
|
Iterator& operator=(const Iterator& it) {
|
|
|
|
// Check for self assignment
|
|
if (this != &it) {
|
|
|
|
mMap = it.mMap;
|
|
mCurrentBucketIndex = it.mCurrentBucketIndex;
|
|
mCurrentEntryIndex = it.mCurrentEntryIndex;
|
|
}
|
|
|
|
return *this;
|
|
}
|
|
};
|
|
|
|
|
|
// -------------------- Methods -------------------- //
|
|
|
|
/// Constructor
|
|
Map(MemoryAllocator& allocator, uint32 capacity = 0)
|
|
: mNbAllocatedEntries(0), mNbEntries(0), mHashSize(0), mBuckets(nullptr),
|
|
mEntries(nullptr), mNextEntries(nullptr), mAllocator(allocator), mFreeIndex(INVALID_INDEX) {
|
|
|
|
if (capacity > 0) {
|
|
|
|
reserve(capacity);
|
|
}
|
|
}
|
|
|
|
/// Copy constructor
|
|
Map(const Map<K, V>& map)
|
|
:mNbAllocatedEntries(map.mNbAllocatedEntries), mNbEntries(map.mNbEntries), mHashSize(map.mHashSize),
|
|
mBuckets(nullptr), mEntries(nullptr), mNextEntries(nullptr), mAllocator(map.mAllocator), mFreeIndex(map.mFreeIndex) {
|
|
|
|
if (mHashSize > 0) {
|
|
|
|
// Allocate memory for the buckets
|
|
mBuckets = static_cast<uint32*>(mAllocator.allocate(mHashSize * sizeof(uint32)));
|
|
|
|
// Allocate memory for the entries
|
|
mEntries = static_cast<Pair<K, V>*>(mAllocator.allocate(mNbAllocatedEntries * sizeof(Pair<K, V>)));
|
|
mNextEntries = static_cast<uint32*>(mAllocator.allocate(mNbAllocatedEntries * sizeof(uint32)));
|
|
|
|
// Copy the buckets array
|
|
std::memcpy(mBuckets, map.mBuckets, mHashSize * sizeof(uint32));
|
|
|
|
// Copy the next entries indices
|
|
std::memcpy(mNextEntries, map.mNextEntries, mNbAllocatedEntries * sizeof(uint32));
|
|
|
|
// Copy the entries
|
|
for (uint32 i=0; i<mHashSize; i++) {
|
|
|
|
uint32 entryIndex = mBuckets[i];
|
|
while(entryIndex != INVALID_INDEX) {
|
|
|
|
// Copy the entry to the new location and destroy the previous one
|
|
new (mEntries + entryIndex) Pair<K,V>(map.mEntries[entryIndex]);
|
|
|
|
entryIndex = mNextEntries[entryIndex];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Destructor
|
|
~Map() {
|
|
|
|
clear(true);
|
|
}
|
|
|
|
/// Allocate memory for a given number of elements
|
|
void reserve(uint32 capacity) {
|
|
|
|
if (capacity <= mHashSize) return;
|
|
|
|
if (capacity < 16) capacity = 16;
|
|
|
|
// 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);
|
|
Pair<K, V>* newEntries = static_cast<Pair<K, V>*>(mAllocator.allocate(nbAllocatedEntries * sizeof(Pair<K, 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].first);
|
|
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) Pair<K,V>(mEntries[entryIndex]);
|
|
mEntries[entryIndex].~Pair<K,V>();
|
|
|
|
entryIndex = mNextEntries[entryIndex];
|
|
}
|
|
}
|
|
|
|
if (mNbAllocatedEntries > 0) {
|
|
|
|
// Release previously allocated memory
|
|
mAllocator.release(mBuckets, mHashSize * sizeof(uint32));
|
|
mAllocator.release(mEntries, mNbAllocatedEntries * sizeof(Pair<K, 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 map contains an item with the given key
|
|
bool containsKey(const K& key) const {
|
|
return findEntry(key) != INVALID_INDEX;
|
|
}
|
|
|
|
/// Add an element into the map
|
|
/// Returns true if the item has been inserted and false otherwise.
|
|
bool add(const Pair<K,V>& keyValue, bool insertIfAlreadyPresent = false) {
|
|
|
|
uint32 bucket;
|
|
|
|
// Compute the hash code of the value
|
|
const size_t hashCode = Hash()(keyValue.first);
|
|
|
|
if (mHashSize > 0) {
|
|
|
|
// Compute the corresponding bucket index
|
|
bucket = hashCode & (mHashSize - 1);
|
|
|
|
auto keyEqual = KeyEqual();
|
|
|
|
// Check if the item is already in the set
|
|
for (uint32 i = mBuckets[bucket]; i != INVALID_INDEX; i = mNextEntries[i]) {
|
|
|
|
// If there is already an item with the same value in the set
|
|
if (Hash()(mEntries[i].first) == hashCode && keyEqual(mEntries[i].first, keyValue.first)) {
|
|
|
|
if (insertIfAlreadyPresent) {
|
|
|
|
// Destruct the previous key/value
|
|
mEntries[i].~Pair<K, V>();
|
|
|
|
// Copy construct the new key/value
|
|
new (mEntries + i) Pair<K,V>(keyValue);
|
|
|
|
return true;
|
|
}
|
|
else {
|
|
assert(false);
|
|
throw std::runtime_error("The key and value pair already exists in the map");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
size_t entryIndex;
|
|
|
|
// If there are no more free entries to use
|
|
if (mFreeIndex == INVALID_INDEX) {
|
|
|
|
// Allocate more memory
|
|
reserve(mHashSize == 0 ? 16 : mHashSize * 2);
|
|
|
|
// Recompute the bucket index
|
|
bucket = hashCode & (mHashSize - 1);
|
|
}
|
|
|
|
assert(mNbEntries < mNbAllocatedEntries);
|
|
assert(mFreeIndex != INVALID_INDEX);
|
|
|
|
// Get the next free entry
|
|
entryIndex = mFreeIndex;
|
|
mFreeIndex = mNextEntries[entryIndex];
|
|
|
|
mNbEntries++;
|
|
|
|
mNextEntries[entryIndex] = mBuckets[bucket];
|
|
new (mEntries + entryIndex) Pair<K, V>(keyValue);
|
|
mBuckets[bucket] = entryIndex;
|
|
|
|
return true;
|
|
}
|
|
|
|
/// 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 (mHashSize > 0) {
|
|
|
|
const size_t hashcode = Hash()(key);
|
|
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 we have found the item
|
|
if (Hash()(mEntries[i].first) == hashcode && keyEqual(mEntries[i].first, key)) {
|
|
|
|
if (last == INVALID_INDEX) {
|
|
mBuckets[bucket] = mNextEntries[i];
|
|
}
|
|
else {
|
|
mNextEntries[last] = mNextEntries[i];
|
|
}
|
|
|
|
uint32 nextEntryIndex = mNextEntries[i];
|
|
uint32 nextBucketIndex = bucket;
|
|
|
|
mEntries[i].~Pair<K,V>();
|
|
mNextEntries[i] = mFreeIndex;
|
|
mFreeIndex = i;
|
|
mNbEntries--;
|
|
|
|
// 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];
|
|
}
|
|
}
|
|
|
|
// We have found the next non empty entry
|
|
return Iterator(this, nextBucketIndex, nextEntryIndex);
|
|
}
|
|
}
|
|
}
|
|
|
|
return end();
|
|
}
|
|
|
|
/// Clear the map
|
|
void clear(bool releaseMemory = false) {
|
|
|
|
for (uint32 i=0; i<mHashSize; i++) {
|
|
|
|
uint32 entryIndex = mBuckets[i];
|
|
while(entryIndex != INVALID_INDEX) {
|
|
|
|
// Destroy the entry
|
|
mEntries[entryIndex].~Pair<K,V>();
|
|
|
|
uint32 nextEntryIndex = mNextEntries[entryIndex];
|
|
|
|
// Add entry to the free list
|
|
mNextEntries[entryIndex] = mFreeIndex;
|
|
mFreeIndex = entryIndex;
|
|
|
|
entryIndex = nextEntryIndex;
|
|
}
|
|
|
|
mBuckets[i] = INVALID_INDEX;
|
|
}
|
|
|
|
if (releaseMemory && mNbAllocatedEntries > 0) {
|
|
|
|
// Release previously allocated memory
|
|
mAllocator.release(mBuckets, mHashSize * sizeof(uint32));
|
|
mAllocator.release(mEntries, mNbAllocatedEntries * sizeof(Pair<K, V>));
|
|
mAllocator.release(mNextEntries, mNbAllocatedEntries * sizeof(uint32));
|
|
|
|
mBuckets = nullptr;
|
|
mEntries = nullptr;
|
|
mNextEntries = nullptr;
|
|
|
|
mNbAllocatedEntries = 0;
|
|
mHashSize = 0;
|
|
}
|
|
|
|
mNbEntries = 0;
|
|
}
|
|
|
|
/// Return the number of elements in the map
|
|
uint32 size() const {
|
|
return mNbEntries;
|
|
}
|
|
|
|
/// Return the capacity of the map
|
|
uint32 capacity() const {
|
|
return mHashSize;
|
|
}
|
|
|
|
/// 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 {
|
|
|
|
uint32 bucket;
|
|
uint32 entry = INVALID_INDEX;
|
|
|
|
if (mHashSize > 0) {
|
|
|
|
const size_t hashCode = Hash()(key);
|
|
bucket = hashCode & (mHashSize - 1);
|
|
auto keyEqual = KeyEqual();
|
|
|
|
for (uint32 i = mBuckets[bucket]; i != INVALID_INDEX; i = mNextEntries[i]) {
|
|
if (Hash()(mEntries[i].first) == hashCode && keyEqual(mEntries[i].first, key)) {
|
|
entry = i;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (entry == INVALID_INDEX) {
|
|
return end();
|
|
}
|
|
|
|
return Iterator(this, bucket, entry);
|
|
}
|
|
|
|
/// Overloaded index operator
|
|
V& operator[](const K& key) {
|
|
|
|
const uint32 entry = findEntry(key);
|
|
|
|
if (entry == INVALID_INDEX) {
|
|
assert(false);
|
|
throw std::runtime_error("No item with given key has been found in the map");
|
|
}
|
|
|
|
return mEntries[entry].second;
|
|
}
|
|
|
|
/// Overloaded index operator
|
|
const V& operator[](const K& key) const {
|
|
|
|
const uint32 entry = findEntry(key);
|
|
|
|
if (entry == INVALID_INDEX) {
|
|
assert(false);
|
|
throw std::runtime_error("No item with given key has been found in the map");
|
|
}
|
|
|
|
return mEntries[entry].second;
|
|
}
|
|
|
|
/// Overloaded equality operator
|
|
bool operator==(const Map<K,V>& 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<K,V>& map) const {
|
|
|
|
return !((*this) == map);
|
|
}
|
|
|
|
/// Overloaded assignment operator
|
|
Map<K,V>& operator=(const Map<K, V>& map) {
|
|
|
|
// Check for self assignment
|
|
if (this != &map) {
|
|
|
|
// Clear the map
|
|
clear(true);
|
|
|
|
mNbAllocatedEntries = map.mNbAllocatedEntries;
|
|
mNbEntries = map.mNbEntries;
|
|
mHashSize = map.mHashSize;
|
|
mFreeIndex = map.mFreeIndex;
|
|
|
|
if (mHashSize > 0) {
|
|
|
|
// Allocate memory for the buckets
|
|
mBuckets = static_cast<uint32*>(mAllocator.allocate(mHashSize * sizeof(uint32)));
|
|
|
|
// Allocate memory for the entries
|
|
mEntries = static_cast<Pair<K,V>*>(mAllocator.allocate(mNbAllocatedEntries * sizeof(Pair<K,V>)));
|
|
mNextEntries = static_cast<uint32*>(mAllocator.allocate(mNbAllocatedEntries * sizeof(uint32)));
|
|
|
|
// Copy the buckets array
|
|
std::memcpy(mBuckets, map.mBuckets, mHashSize * sizeof(uint32));
|
|
|
|
// Copy the next entries indices
|
|
std::memcpy(mNextEntries, map.mNextEntries, mNbAllocatedEntries * sizeof(uint32));
|
|
|
|
// Copy the entries
|
|
for (uint32 i=0; i<mHashSize; i++) {
|
|
|
|
uint32 entryIndex = mBuckets[i];
|
|
while(entryIndex != INVALID_INDEX) {
|
|
|
|
// Copy the entry to the new location and destroy the previous one
|
|
new (mEntries + entryIndex) Pair<K,V>(map.mEntries[entryIndex]);
|
|
|
|
entryIndex = mNextEntries[entryIndex];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
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
|
|
uint32 bucketIndex = 0;
|
|
while (mBuckets[bucketIndex] == INVALID_INDEX) {
|
|
|
|
bucketIndex++;
|
|
}
|
|
|
|
assert(bucketIndex < mHashSize);
|
|
assert(mBuckets[bucketIndex] != INVALID_INDEX);
|
|
|
|
return Iterator(this, bucketIndex, mBuckets[bucketIndex]);
|
|
}
|
|
|
|
/// Return a end iterator
|
|
Iterator end() const {
|
|
return Iterator(this, mHashSize, 0);
|
|
}
|
|
|
|
// ---------- Friendship ---------- //
|
|
|
|
friend class Iterator;
|
|
};
|
|
|
|
}
|
|
|
|
#endif
|